diff options
author | Josh Rahm <joshuarahm@gmail.com> | 2023-11-30 20:35:25 +0000 |
---|---|---|
committer | Josh Rahm <joshuarahm@gmail.com> | 2023-11-30 20:35:25 +0000 |
commit | 1b7b916b7631ddf73c38e3a0070d64e4636cb2f3 (patch) | |
tree | cd08258054db80bb9a11b1061bb091c70b76926a /src/nvim/memfile.c | |
parent | eaa89c11d0f8aefbb512de769c6c82f61a8baca3 (diff) | |
parent | 4a8bf24ac690004aedf5540fa440e788459e5e34 (diff) | |
download | rneovim-1b7b916b7631ddf73c38e3a0070d64e4636cb2f3.tar.gz rneovim-1b7b916b7631ddf73c38e3a0070d64e4636cb2f3.tar.bz2 rneovim-1b7b916b7631ddf73c38e3a0070d64e4636cb2f3.zip |
Merge remote-tracking branch 'upstream/master' into aucmd_textputpostaucmd_textputpost
Diffstat (limited to 'src/nvim/memfile.c')
-rw-r--r-- | src/nvim/memfile.c | 341 |
1 files changed, 69 insertions, 272 deletions
diff --git a/src/nvim/memfile.c b/src/nvim/memfile.c index 46be9ccea5..d989600d45 100644 --- a/src/nvim/memfile.c +++ b/src/nvim/memfile.c @@ -1,6 +1,3 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check -// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - /// An abstraction to handle blocks of memory which can be stored in a file. /// This is the implementation of a sort of virtual memory. /// @@ -45,24 +42,25 @@ #include <stdbool.h> #include <stdio.h> #include <string.h> +#include <sys/stat.h> -#include "nvim/assert.h" +#include "nvim/assert_defs.h" #include "nvim/buffer_defs.h" #include "nvim/fileio.h" #include "nvim/gettext.h" #include "nvim/globals.h" -#include "nvim/macros.h" +#include "nvim/map_defs.h" #include "nvim/memfile.h" #include "nvim/memfile_defs.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/os/fs_defs.h" +#include "nvim/os/fs.h" #include "nvim/os/input.h" #include "nvim/os/os.h" #include "nvim/path.h" -#include "nvim/pos.h" -#include "nvim/vim.h" +#include "nvim/pos_defs.h" +#include "nvim/vim_defs.h" #define MEMFILE_PAGE_SIZE 4096 /// default page size @@ -70,6 +68,8 @@ # include "memfile.c.generated.h" #endif +static const char e_block_was_not_locked[] = N_("E293: Block was not locked"); + /// Open a new or existing memory block file. /// /// @param fname Name of file to use. @@ -99,11 +99,9 @@ memfile_T *mf_open(char *fname, int flags) } mfp->mf_free_first = NULL; // free list is empty - mfp->mf_used_first = NULL; // used list is empty - mfp->mf_used_last = NULL; - mfp->mf_dirty = false; - mf_hash_init(&mfp->mf_hash); - mf_hash_init(&mfp->mf_trans); + mfp->mf_dirty = MF_DIRTY_NO; + mfp->mf_hash = (PMap(int64_t)) MAP_INIT; + mfp->mf_trans = (Map(int64_t, int64_t)) MAP_INIT; mfp->mf_page_size = MEMFILE_PAGE_SIZE; // Try to set the page size equal to device's block size. Speeds up I/O a lot. @@ -124,7 +122,7 @@ memfile_T *mf_open(char *fname, int flags) // must be rounded up. if (mfp->mf_fd < 0 || (flags & (O_TRUNC|O_EXCL)) - || (size = vim_lseek(mfp->mf_fd, 0L, SEEK_END)) <= 0) { + || (size = vim_lseek(mfp->mf_fd, 0, SEEK_END)) <= 0) { // no file or empty file mfp->mf_blocknr_max = 0; } else { @@ -157,7 +155,7 @@ memfile_T *mf_open(char *fname, int flags) int mf_open_file(memfile_T *mfp, char *fname) { if (mf_do_open(mfp, fname, O_RDWR | O_CREAT | O_EXCL)) { - mfp->mf_dirty = true; + mfp->mf_dirty = MF_DIRTY_YES; return OK; } @@ -180,15 +178,15 @@ void mf_close(memfile_T *mfp, bool del_file) } // free entries in used list - for (bhdr_T *hp = mfp->mf_used_first, *nextp; hp != NULL; hp = nextp) { - nextp = hp->bh_next; + bhdr_T *hp; + map_foreach_value(&mfp->mf_hash, hp, { mf_free_bhdr(hp); - } + }) while (mfp->mf_free_first != NULL) { // free entries in free list xfree(mf_rem_free(mfp)); } - mf_hash_free(&mfp->mf_hash); - mf_hash_free_all(&mfp->mf_trans); // free hashtable and its items + map_destroy(int64_t, &mfp->mf_hash); + map_destroy(int64_t, &mfp->mf_trans); // free hashtable and its items mf_free_fnames(mfp); xfree(mfp); } @@ -206,7 +204,7 @@ void mf_close_file(buf_T *buf, bool getlines) if (getlines) { // get all blocks in memory by accessing all lines (clumsy!) for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) { - (void)ml_get_buf(buf, lnum, false); + (void)ml_get_buf(buf, lnum); } } @@ -267,10 +265,9 @@ bhdr_T *mf_new(memfile_T *mfp, bool negative, unsigned page_count) } } hp->bh_flags = BH_LOCKED | BH_DIRTY; // new block is always dirty - mfp->mf_dirty = true; + mfp->mf_dirty = MF_DIRTY_YES; hp->bh_page_count = page_count; - mf_ins_used(mfp, hp); - mf_ins_hash(mfp, hp); + pmap_put(int64_t)(&mfp->mf_hash, hp->bh_bnum, hp); // Init the data to all zero, to avoid reading uninitialized data. // This also avoids that the passwd file ends up in the swap file! @@ -292,7 +289,7 @@ bhdr_T *mf_get(memfile_T *mfp, blocknr_T nr, unsigned page_count) } // see if it is in the cache - bhdr_T *hp = mf_find_hash(mfp, nr); + bhdr_T *hp = pmap_get(int64_t)(&mfp->mf_hash, nr); if (hp == NULL) { // not in the hash list if (nr < 0 || nr >= mfp->mf_infile_count) { // can't be in the file return NULL; @@ -300,7 +297,12 @@ bhdr_T *mf_get(memfile_T *mfp, blocknr_T nr, unsigned page_count) // could check here if the block is in the free list - hp = mf_alloc_bhdr(mfp, page_count); + if (page_count > 0) { + hp = mf_alloc_bhdr(mfp, page_count); + } + if (hp == NULL) { + return NULL; + } hp->bh_bnum = nr; hp->bh_flags = 0; @@ -310,13 +312,11 @@ bhdr_T *mf_get(memfile_T *mfp, blocknr_T nr, unsigned page_count) return NULL; } } else { - mf_rem_used(mfp, hp); // remove from list, insert in front below - mf_rem_hash(mfp, hp); + pmap_del(int64_t)(&mfp->mf_hash, hp->bh_bnum, NULL); } 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 + pmap_put(int64_t)(&mfp->mf_hash, hp->bh_bnum, hp); // put in front of hash table return hp; } @@ -330,12 +330,14 @@ void mf_put(memfile_T *mfp, bhdr_T *hp, bool dirty, bool infile) unsigned flags = hp->bh_flags; if ((flags & BH_LOCKED) == 0) { - iemsg(_("E293: block was not locked")); + iemsg(_(e_block_was_not_locked)); } flags &= ~BH_LOCKED; if (dirty) { flags |= BH_DIRTY; - mfp->mf_dirty = true; + if (mfp->mf_dirty != MF_DIRTY_YES_NOSYNC) { + mfp->mf_dirty = MF_DIRTY_YES; + } } hp->bh_flags = flags; if (infile) { @@ -347,8 +349,7 @@ void mf_put(memfile_T *mfp, bhdr_T *hp, bool dirty, bool infile) void mf_free(memfile_T *mfp, bhdr_T *hp) { xfree(hp->bh_data); // free data - mf_rem_hash(mfp, hp); // get *hp out of the hash list - mf_rem_used(mfp, hp); // get *hp out of the used list + pmap_del(int64_t)(&mfp->mf_hash, hp->bh_bnum, NULL); // get *hp out of the hash table if (hp->bh_bnum < 0) { xfree(hp); // don't want negative numbers in free list mfp->mf_neg_count--; @@ -375,8 +376,9 @@ int mf_sync(memfile_T *mfp, int flags) { int got_int_save = got_int; - if (mfp->mf_fd < 0) { // there is no file, nothing to do - mfp->mf_dirty = false; + if (mfp->mf_fd < 0) { + // there is no file, nothing to do + mfp->mf_dirty = MF_DIRTY_NO; return FAIL; } @@ -389,7 +391,8 @@ int mf_sync(memfile_T *mfp, int flags) // fails then we give up. int status = OK; bhdr_T *hp; - for (hp = mfp->mf_used_last; hp != NULL; hp = hp->bh_prev) { + // note, "last" block is typically earlier in the hash list + map_foreach_value(&mfp->mf_hash, hp, { if (((flags & MFS_ALL) || hp->bh_bnum >= 0) && (hp->bh_flags & BH_DIRTY) && (status == OK || (hp->bh_bnum >= 0 @@ -414,12 +417,12 @@ int mf_sync(memfile_T *mfp, int flags) break; } } - } + }) // If the whole list is flushed, the memfile is not dirty anymore. // In case of an error, dirty flag is also set, to avoid trying all the time. if (hp == NULL || status == FAIL) { - mfp->mf_dirty = false; + mfp->mf_dirty = MF_DIRTY_NO; } if (flags & MFS_FLUSH) { @@ -437,59 +440,13 @@ int mf_sync(memfile_T *mfp, int flags) /// These are blocks that need to be written to a newly created swapfile. void mf_set_dirty(memfile_T *mfp) { - for (bhdr_T *hp = mfp->mf_used_last; hp != NULL; hp = hp->bh_prev) { + bhdr_T *hp; + map_foreach_value(&mfp->mf_hash, hp, { if (hp->bh_bnum > 0) { hp->bh_flags |= BH_DIRTY; } - } - mfp->mf_dirty = true; -} - -/// Insert block in front of memfile's hash list. -static void mf_ins_hash(memfile_T *mfp, bhdr_T *hp) -{ - mf_hash_add_item(&mfp->mf_hash, (mf_hashitem_T *)hp); -} - -/// Remove block from memfile's hash list. -static void mf_rem_hash(memfile_T *mfp, bhdr_T *hp) -{ - mf_hash_rem_item(&mfp->mf_hash, (mf_hashitem_T *)hp); -} - -/// Lookup block with number "nr" in memfile's hash list. -static bhdr_T *mf_find_hash(memfile_T *mfp, blocknr_T nr) -{ - return (bhdr_T *)mf_hash_find(&mfp->mf_hash, nr); -} - -/// Insert block at the front of memfile's used list. -static void mf_ins_used(memfile_T *mfp, bhdr_T *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; - } -} - -/// Remove block from memfile's used list. -static void mf_rem_used(memfile_T *mfp, bhdr_T *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_dirty = MF_DIRTY_YES; } /// Release as many blocks as possible. @@ -510,17 +467,18 @@ bool mf_release_all(void) // Flush as many blocks as possible, only if there is a swapfile. if (mfp->mf_fd >= 0) { - for (bhdr_T *hp = mfp->mf_used_last; hp != NULL;) { + for (int i = 0; i < (int)map_size(&mfp->mf_hash);) { + bhdr_T *hp = mfp->mf_hash.values[i]; if (!(hp->bh_flags & BH_LOCKED) && (!(hp->bh_flags & BH_DIRTY) || mf_write(mfp, hp) != FAIL)) { - mf_rem_used(mfp, hp); - mf_rem_hash(mfp, hp); + pmap_del(int64_t)(&mfp->mf_hash, hp->bh_bnum, NULL); mf_free_bhdr(hp); - hp = mfp->mf_used_last; // restart, list was changed retval = true; + // Rerun with the same value of i. another item will have taken + // its place (or it was the last) } else { - hp = hp->bh_prev; + i++; } } } @@ -548,7 +506,7 @@ static void mf_free_bhdr(bhdr_T *hp) /// Insert a block in the free list. static void mf_ins_free(memfile_T *mfp, bhdr_T *hp) { - hp->bh_next = mfp->mf_free_first; + hp->bh_data = mfp->mf_free_first; mfp->mf_free_first = hp; } @@ -558,7 +516,7 @@ static void mf_ins_free(memfile_T *mfp, bhdr_T *hp) static bhdr_T *mf_rem_free(memfile_T *mfp) { bhdr_T *hp = mfp->mf_free_first; - mfp->mf_free_first = hp->bh_next; + mfp->mf_free_first = hp->bh_data; return hp; } @@ -602,12 +560,8 @@ static int mf_read(memfile_T *mfp, bhdr_T *hp) /// - Write error in swap file. static int mf_write(memfile_T *mfp, bhdr_T *hp) { - off_T offset; // offset in the file - blocknr_T nr; // block nr which is being written bhdr_T *hp2; - unsigned page_size; // number of bytes in a page unsigned page_count; // number of pages written - unsigned size; // number of bytes written if (mfp->mf_fd < 0) { // there is no file, can't write return FAIL; @@ -619,23 +573,23 @@ static int mf_write(memfile_T *mfp, bhdr_T *hp) } } - page_size = mfp->mf_page_size; + unsigned page_size = mfp->mf_page_size; // number of bytes in a page /// We don't want gaps in the file. Write the blocks in front of *hp /// to extend the file. /// If block 'mf_infile_count' is not in the hash list, it has been /// freed. Fill the space in the file with data from the current block. - for (;;) { - nr = hp->bh_bnum; + while (true) { + blocknr_T nr = hp->bh_bnum; // block nr which is being written if (nr > mfp->mf_infile_count) { // beyond end of file nr = mfp->mf_infile_count; - hp2 = mf_find_hash(mfp, nr); // NULL caught below + hp2 = pmap_get(int64_t)(&mfp->mf_hash, nr); // NULL caught below } else { hp2 = hp; } // TODO(elmart): Check (page_size * nr) within off_T bounds. - offset = (off_T)(page_size * nr); + off_T offset = (off_T)(page_size * nr); // offset in the file if (vim_lseek(mfp->mf_fd, offset, SEEK_SET) != offset) { PERROR(_("E296: Seek error in swap file write")); return FAIL; @@ -645,7 +599,7 @@ static int mf_write(memfile_T *mfp, bhdr_T *hp) } else { page_count = hp2->bh_page_count; } - size = page_size * page_count; + unsigned size = page_size * page_count; // number of bytes written void *data = (hp2 == NULL) ? hp->bh_data : hp2->bh_data; if ((unsigned)write_eintr(mfp->mf_fd, data, size) != size) { /// Avoid repeating the error message, this mostly happens when the @@ -682,8 +636,6 @@ static int mf_trans_add(memfile_T *mfp, bhdr_T *hp) return OK; } - mf_blocknr_trans_item_T *np = xmalloc(sizeof(mf_blocknr_trans_item_T)); - // Get a new number for the block. // If the first item in the free list has sufficient pages, use its number. // Otherwise use mf_blocknr_max. @@ -706,15 +658,13 @@ static int mf_trans_add(memfile_T *mfp, bhdr_T *hp) mfp->mf_blocknr_max += page_count; } - np->nt_old_bnum = hp->bh_bnum; // adjust number - np->nt_new_bnum = new_bnum; - - mf_rem_hash(mfp, hp); // remove from old hash list + blocknr_T old_bnum = hp->bh_bnum; // adjust number + pmap_del(int64_t)(&mfp->mf_hash, hp->bh_bnum, NULL); hp->bh_bnum = new_bnum; - mf_ins_hash(mfp, hp); // insert in new hash list + pmap_put(int64_t)(&mfp->mf_hash, new_bnum, hp); // Insert "np" into "mf_trans" hashtable with key "np->nt_old_bnum". - mf_hash_add_item(&mfp->mf_trans, (mf_hashitem_T *)np); + map_put(int64_t, int64_t)(&mfp->mf_trans, old_bnum, new_bnum); return OK; } @@ -725,20 +675,16 @@ static int mf_trans_add(memfile_T *mfp, bhdr_T *hp) /// The old number When not found. blocknr_T mf_trans_del(memfile_T *mfp, blocknr_T old_nr) { - mf_blocknr_trans_item_T *np = - (mf_blocknr_trans_item_T *)mf_hash_find(&mfp->mf_trans, old_nr); - - if (np == NULL) { // not found + blocknr_T *num = map_ref(int64_t, int64_t)(&mfp->mf_trans, old_nr, false); + if (num == NULL) { // not found return old_nr; } mfp->mf_neg_count--; - blocknr_T new_bnum = np->nt_new_bnum; + blocknr_T new_bnum = *num; // remove entry from the trans list - mf_hash_rem_item(&mfp->mf_trans, (mf_hashitem_T *)np); - - xfree(np); + map_del(int64_t, int64_t)(&mfp->mf_trans, old_nr, NULL); return new_bnum; } @@ -802,7 +748,7 @@ static bool mf_do_open(memfile_T *mfp, char *fname, int flags) emsg(_("E300: Swap file already exists (symlink attack?)")); } else { // try to open the file - mfp->mf_fd = MCH_OPEN_RW((char *)mfp->mf_fname, flags | O_NOFOLLOW); + mfp->mf_fd = os_open(mfp->mf_fname, flags | O_NOFOLLOW, S_IREAD | S_IWRITE); } // If the file cannot be opened, use memory only @@ -815,152 +761,3 @@ static bool mf_do_open(memfile_T *mfp, char *fname, int flags) return true; } - -// -// Implementation of mf_hashtab_T. -// - -/// The number of buckets in the hashtable is increased by a factor of -/// MHT_GROWTH_FACTOR when the average number of items per bucket -/// exceeds 2 ^ MHT_LOG_LOAD_FACTOR. -enum { - MHT_LOG_LOAD_FACTOR = 6, - MHT_GROWTH_FACTOR = 2, // must be a power of two -}; - -/// Initialize an empty hash table. -static void mf_hash_init(mf_hashtab_T *mht) -{ - CLEAR_POINTER(mht); - mht->mht_buckets = mht->mht_small_buckets; - mht->mht_mask = MHT_INIT_SIZE - 1; -} - -/// Free the array of a hash table. Does not free the items it contains! -/// The hash table must not be used again without another mf_hash_init() call. -static void mf_hash_free(mf_hashtab_T *mht) -{ - if (mht->mht_buckets != mht->mht_small_buckets) { - xfree(mht->mht_buckets); - } -} - -/// Free the array of a hash table and all the items it contains. -static void mf_hash_free_all(mf_hashtab_T *mht) -{ - for (size_t idx = 0; idx <= mht->mht_mask; idx++) { - mf_hashitem_T *next; - for (mf_hashitem_T *mhi = mht->mht_buckets[idx]; mhi != NULL; mhi = next) { - next = mhi->mhi_next; - xfree(mhi); - } - } - - mf_hash_free(mht); -} - -/// Find by key. -/// -/// @return A pointer to a mf_hashitem_T or NULL if the item was not found. -static mf_hashitem_T *mf_hash_find(mf_hashtab_T *mht, blocknr_T key) -{ - mf_hashitem_T *mhi = mht->mht_buckets[(size_t)key & mht->mht_mask]; - while (mhi != NULL && mhi->mhi_key != key) { - mhi = mhi->mhi_next; - } - return mhi; -} - -/// Add item to hashtable. Item must not be NULL. -static void mf_hash_add_item(mf_hashtab_T *mht, mf_hashitem_T *mhi) -{ - size_t idx = (size_t)mhi->mhi_key & mht->mht_mask; - mhi->mhi_next = mht->mht_buckets[idx]; - mhi->mhi_prev = NULL; - if (mhi->mhi_next != NULL) { - mhi->mhi_next->mhi_prev = mhi; - } - mht->mht_buckets[idx] = mhi; - - mht->mht_count++; - - /// Grow hashtable when we have more thank 2^MHT_LOG_LOAD_FACTOR - /// items per bucket on average. - if ((mht->mht_count >> MHT_LOG_LOAD_FACTOR) > mht->mht_mask) { - mf_hash_grow(mht); - } -} - -/// Remove item from hashtable. Item must be non NULL and within hashtable. -static void mf_hash_rem_item(mf_hashtab_T *mht, mf_hashitem_T *mhi) -{ - if (mhi->mhi_prev == NULL) { - mht->mht_buckets[(size_t)mhi->mhi_key & mht->mht_mask] = - mhi->mhi_next; - } else { - mhi->mhi_prev->mhi_next = mhi->mhi_next; - } - - if (mhi->mhi_next != NULL) { - mhi->mhi_next->mhi_prev = mhi->mhi_prev; - } - - mht->mht_count--; - - // We could shrink the table here, but it typically takes little memory, - // so why bother? -} - -/// Increase number of buckets in the hashtable by MHT_GROWTH_FACTOR and -/// rehash items. -static void mf_hash_grow(mf_hashtab_T *mht) -{ - size_t size = (mht->mht_mask + 1) * MHT_GROWTH_FACTOR * sizeof(void *); - mf_hashitem_T **buckets = xcalloc(1, size); - - int shift = 0; - while ((mht->mht_mask >> shift) != 0) { - shift++; - } - - for (size_t i = 0; i <= mht->mht_mask; i++) { - /// Traverse the items in the i-th original bucket and move them into - /// MHT_GROWTH_FACTOR new buckets, preserving their relative order - /// within each new bucket. Preserving the order is important because - /// mf_get() tries to keep most recently used items at the front of - /// each bucket. - /// - /// Here we strongly rely on the fact that hashes are computed modulo - /// a power of two. - - mf_hashitem_T *tails[MHT_GROWTH_FACTOR]; - CLEAR_FIELD(tails); - - for (mf_hashitem_T *mhi = mht->mht_buckets[i]; - mhi != NULL; mhi = mhi->mhi_next) { - size_t j = (mhi->mhi_key >> shift) & (MHT_GROWTH_FACTOR - 1); - if (tails[j] == NULL) { - buckets[i + (j << shift)] = mhi; - tails[j] = mhi; - mhi->mhi_prev = NULL; - } else { - tails[j]->mhi_next = mhi; - mhi->mhi_prev = tails[j]; - tails[j] = mhi; - } - } - - for (size_t j = 0; j < MHT_GROWTH_FACTOR; j++) { - if (tails[j] != NULL) { - tails[j]->mhi_next = NULL; - } - } - } - - if (mht->mht_buckets != mht->mht_small_buckets) { - xfree(mht->mht_buckets); - } - - mht->mht_buckets = buckets; - mht->mht_mask = (mht->mht_mask + 1) * MHT_GROWTH_FACTOR - 1; -} |