📄 memfile.c
字号:
if (nr < 0 || nr >= mfp->mf_infile_count) /* can't be in the file */
return NULL;
/* could check here if the block is in the free list */
/*
* Check if we need to flush an existing block.
* If so, use that block.
* If not, allocate a new block.
*/
hp = mf_release(mfp, page_count);
if (hp == NULL && (hp = mf_alloc_bhdr(mfp, page_count)) == NULL)
return NULL;
hp->bh_bnum = nr;
hp->bh_flags = 0;
hp->bh_page_count = page_count;
if (mf_read(mfp, hp) == FAIL) /* cannot read the block! */
{
mf_free_bhdr(hp);
return NULL;
}
}
else
{
mf_rem_used(mfp, hp); /* remove from list, insert in front below */
mf_rem_hash(mfp, hp);
}
hp->bh_flags |= BH_LOCKED;
mf_ins_used(mfp, hp); /* put in front of used list */
mf_ins_hash(mfp, hp); /* put in front of hash list */
return hp;
}
/*
* release the block *hp
*
* dirty: Block must be written to file later
* infile: Block should be in file (needed for recovery)
*
* no return value, function cannot fail
*/
void
mf_put(mfp, hp, dirty, infile)
MEMFILE *mfp;
BHDR *hp;
int dirty;
int infile;
{
int flags;
flags = hp->bh_flags;
if ((flags & BH_LOCKED) == 0)
EMSG("block was not locked");
flags &= ~BH_LOCKED;
if (dirty)
{
flags |= BH_DIRTY;
mfp->mf_dirty = TRUE;
}
hp->bh_flags = flags;
if (infile)
mf_trans_add(mfp, hp); /* may translate negative in positive nr */
}
/*
* block *hp is no longer in used, may put it in the free list of memfile *mfp
*/
void
mf_free(mfp, hp)
MEMFILE *mfp;
BHDR *hp;
{
vim_free(hp->bh_data); /* free the memory */
mf_rem_hash(mfp, hp); /* get *hp out of the hash list */
mf_rem_used(mfp, hp); /* get *hp out of the used list */
if (hp->bh_bnum < 0)
{
vim_free(hp); /* don't want negative numbers in free list */
mfp->mf_neg_count--;
}
else
mf_ins_free(mfp, hp); /* put *hp in the free list */
}
/*
* Sync the memory file *mfp to disk.
* Flags:
* MFS_ALL If not given, blocks with negative numbers are not synced,
* even when they are dirty!
* MFS_STOP Stop syncing when a character becomes available, but sync at
* least one block.
* MFS_FLUSH Make sure buffers are flushed to disk, so they will survive a
* system crash.
* MFS_ZERO Only write block 0.
*
* Return FAIL for failure, OK otherwise
*/
int
mf_sync(mfp, flags)
MEMFILE *mfp;
int flags;
{
int status;
BHDR *hp;
#ifdef SYNC_DUP_CLOSE
int fd;
#endif
if (mfp->mf_fd < 0) /* there is no file, nothing to do */
{
mfp->mf_dirty = FALSE;
return FAIL;
}
/*
* sync from last to first (may reduce the probability of an inconsistent
* file) If a write fails, it is very likely caused by a full filesystem.
* Then we only try to write blocks within the existing file. If that also
* fails then we give up.
*/
status = OK;
for (hp = mfp->mf_used_last; hp != NULL; hp = hp->bh_prev)
if (((flags & MFS_ALL) || hp->bh_bnum >= 0)
&& (hp->bh_flags & BH_DIRTY)
&& (status == OK || (hp->bh_bnum >= 0
&& hp->bh_bnum < mfp->mf_infile_count)))
{
if ((flags & MFS_ZERO) && hp->bh_bnum != 0)
continue;
if (mf_write(mfp, hp) == FAIL)
{
if (status == FAIL) /* double error: quit syncing */
break;
status = FAIL;
}
/* Stop when char available now */
if ((flags & MFS_STOP) && ui_char_avail())
break;
}
/*
* If the whole list is flushed, the memfile is not dirty anymore.
* In case of an error this flag is also set, to avoid trying all the time.
*/
if (hp == NULL || status == FAIL)
mfp->mf_dirty = FALSE;
if ((flags & MFS_FLUSH) && *p_sws != NUL)
{
#if defined(UNIX)
# ifdef HAVE_FSYNC
/*
* most Unixes have the very useful fsync() function, just what we need.
* However, with OS/2 and EMX it is also available, but there are
* reports of bad problems with it (a bug in HPFS.IFS).
* So we disable use of it here in case someone tries to be smart
* and changes os_os2_cfg.h... (even though there is no __EMX__ test
* in the #if, as __EMX__ does not have sync(); we hope for a timely
* sync from the system itself).
*/
# if defined(__EMX__)
error "Dont use fsync with EMX! Read emxdoc.doc or emxfix01.doc for info."
# endif
if (STRCMP(p_sws, "fsync") == 0)
{
if (fsync(mfp->mf_fd))
status = FAIL;
}
else
# endif
# ifdef __OPENNT
fflush(NULL); /* OpenNT is strictly POSIX (Benzinger) */
# else
sync();
# endif
#endif
#ifdef VMS
if (STRCMP(p_sws, "fsync") == 0)
{
if (fsync(mfp->mf_fd))
status = FAIL;
}
#endif
#ifdef MSDOS
if (_dos_commit(mfp->mf_fd))
status = FAIL;
#else
# ifdef SYNC_DUP_CLOSE
/*
* Win32 is a bit more work: Duplicate the file handle and close it.
* This should flush the file to disk.
*/
if ((fd = dup(mfp->mf_fd)) >= 0)
close(fd);
# endif
#endif
#ifdef AMIGA
/*
* Flush() only exists for AmigaDos 2.0.
* For 1.3 it should be done with close() + open(), but then the risk
* is that the open() may fail and lose the file....
*/
# ifndef NO_ARP
if (dos2)
# endif
# ifdef SASC
{
struct UFB *fp = chkufb(mfp->mf_fd);
if (fp != NULL)
Flush(fp->ufbfh);
}
# else
# ifdef _DCC
{
BPTR fh = (BPTR)fdtofh(mfp->mf_fd);
if (fh != 0)
Flush(fh);
}
# else /* assume Manx */
Flush(_devtab[mfp->mf_fd].fd);
# endif
# endif
#endif /* AMIGA */
}
return status;
}
/*
* insert block *hp in front of hashlist of memfile *mfp
*/
static void
mf_ins_hash(mfp, hp)
MEMFILE *mfp;
BHDR *hp;
{
BHDR *hhp;
int hash;
hash = MEMHASH(hp->bh_bnum);
hhp = mfp->mf_hash[hash];
hp->bh_hash_next = hhp;
hp->bh_hash_prev = NULL;
if (hhp != NULL)
hhp->bh_hash_prev = hp;
mfp->mf_hash[hash] = hp;
}
/*
* remove block *hp from hashlist of memfile list *mfp
*/
static void
mf_rem_hash(mfp, hp)
MEMFILE *mfp;
BHDR *hp;
{
if (hp->bh_hash_prev == NULL)
mfp->mf_hash[MEMHASH(hp->bh_bnum)] = hp->bh_hash_next;
else
hp->bh_hash_prev->bh_hash_next = hp->bh_hash_next;
if (hp->bh_hash_next)
hp->bh_hash_next->bh_hash_prev = hp->bh_hash_prev;
}
/*
* look in hash lists of memfile *mfp for block header with number 'nr'
*/
static BHDR *
mf_find_hash(mfp, nr)
MEMFILE *mfp;
blocknr_t nr;
{
BHDR *hp;
for (hp = mfp->mf_hash[MEMHASH(nr)]; hp != NULL; hp = hp->bh_hash_next)
if (hp->bh_bnum == nr)
break;
return hp;
}
/*
* insert block *hp in front of used list of memfile *mfp
*/
static void
mf_ins_used(mfp, hp)
MEMFILE *mfp;
BHDR *hp;
{
hp->bh_next = mfp->mf_used_first;
mfp->mf_used_first = hp;
hp->bh_prev = NULL;
if (hp->bh_next == NULL) /* list was empty, adjust last pointer */
mfp->mf_used_last = hp;
else
hp->bh_next->bh_prev = hp;
mfp->mf_used_count += hp->bh_page_count;
total_mem_used += hp->bh_page_count * mfp->mf_page_size;
}
/*
* remove block *hp from used list of memfile *mfp
*/
static void
mf_rem_used(mfp, hp)
MEMFILE *mfp;
BHDR *hp;
{
if (hp->bh_next == NULL) /* last block in used list */
mfp->mf_used_last = hp->bh_prev;
else
hp->bh_next->bh_prev = hp->bh_prev;
if (hp->bh_prev == NULL) /* first block in used list */
mfp->mf_used_first = hp->bh_next;
else
hp->bh_prev->bh_next = hp->bh_next;
mfp->mf_used_count -= hp->bh_page_count;
total_mem_used -= hp->bh_page_count * mfp->mf_page_size;
}
/*
* Release the least recently used block from the used list if the number
* of used memory blocks gets to big.
*
* Return the block header to the caller, including the memory block, so
* it can be re-used. Make sure the page_count is right.
*/
static BHDR *
mf_release(mfp, page_count)
MEMFILE *mfp;
int page_count;
{
BHDR *hp;
int need_release;
BUF *buf;
/* don't release while in mf_close_file() */
if (dont_release)
return NULL;
/*
* Need to release a block if the number of blocks for this memfile is
* higher than the maximum or total memory used is over 'maxmemtot'
*/
need_release = ((mfp->mf_used_count >= mfp->mf_used_count_max)
|| (total_mem_used >> 10) >= (long_u)p_mmt);
/*
* Try to create a swap file if the amount of memory used is getting too
* high.
*/
if (mfp->mf_fd < 0 && need_release && p_uc)
{
/* find for which buffer this memfile is */
for (buf = firstbuf; buf != NULL; buf = buf->b_next)
if (buf->b_ml.ml_mfp == mfp)
break;
if (buf != NULL && buf->b_may_swap)
ml_open_file(buf);
}
/*
* don't release a block if
* there is no file for this memfile
* or
* the number of blocks for this memfile is lower than the maximum
* and
* total memory used is not up to 'maxmemtot'
*/
if (mfp->mf_fd < 0 || !need_release)
return NULL;
for (hp = mfp->mf_used_last; hp != NULL; hp = hp->bh_prev)
if (!(hp->bh_flags & BH_LOCKED))
break;
if (hp == NULL) /* not a single one that can be released */
return NULL;
/*
* If the block is dirty, write it.
* If the write fails we don't free it.
*/
if ((hp->bh_flags & BH_DIRTY) && mf_write(mfp, hp) == FAIL)
return NULL;
mf_rem_used(mfp, hp);
mf_rem_hash(mfp, hp);
/*
* If a BHDR is returned, make sure that the page_count of bh_data is right
*/
if (hp->bh_page_count != page_count)
{
vim_free(hp->bh_data);
if ((hp->bh_data = alloc(mfp->mf_page_size * page_count)) == NULL)
{
vim_free(hp);
return NULL;
}
hp->bh_page_count = page_count;
}
return hp;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -