aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbfredl <bjorn.linse@gmail.com>2023-08-25 23:00:29 +0200
committerbfredl <bjorn.linse@gmail.com>2023-09-10 13:09:44 +0200
commit87cde88c41d003988e7d5dbc4ddb26687d24923d (patch)
treefc48cd928eea258c9df8145d4007a5a667453b9c
parente99a3fd25daeb52f609c72cb7e96b83bd0610e9e (diff)
downloadrneovim-87cde88c41d003988e7d5dbc4ddb26687d24923d.tar.gz
rneovim-87cde88c41d003988e7d5dbc4ddb26687d24923d.tar.bz2
rneovim-87cde88c41d003988e7d5dbc4ddb26687d24923d.zip
refactor(memfile): change mf_trans and mf_hash from ad-hoc hashtable to Map
Memfile used a private implementation of an open hash table with intrusive collision chains, but there is no reason to assume the standard khash_t based Map won't work just fine. Yes, we are taking full ownership and maintenance over memline and memfile. No one is going to maintain it for us. Trust the plan.
-rw-r--r--src/nvim/map.c12
-rw-r--r--src/nvim/map.h4
-rw-r--r--src/nvim/memfile.c277
-rw-r--r--src/nvim/memfile_defs.h66
-rw-r--r--src/nvim/memline.c19
5 files changed, 71 insertions, 307 deletions
diff --git a/src/nvim/map.c b/src/nvim/map.c
index e2c6443245..33a7759a1b 100644
--- a/src/nvim/map.c
+++ b/src/nvim/map.c
@@ -25,6 +25,8 @@
#define equal_uint32_t equal_simple
#define hash_int(x) hash_uint32_t((uint32_t)(x))
#define equal_int equal_simple
+#define hash_int64_t(key) hash_uint64_t((uint64_t)key)
+#define equal_int64_t equal_simple
#if defined(ARCH_64)
# define hash_ptr_t(key) hash_uint64_t((uint64_t)(key))
@@ -182,6 +184,16 @@ void mh_clear(MapHash *h)
#undef VAL_NAME
#undef KEY_NAME
+#define KEY_NAME(x) x##int64_t
+#include "nvim/map_key_impl.c.h"
+#define VAL_NAME(x) quasiquote(x, ptr_t)
+#include "nvim/map_value_impl.c.h"
+#undef VAL_NAME
+#define VAL_NAME(x) quasiquote(x, int64_t)
+#include "nvim/map_value_impl.c.h"
+#undef VAL_NAME
+#undef KEY_NAME
+
#define KEY_NAME(x) x##HlEntry
#include "nvim/map_key_impl.c.h"
#define VAL_NAME(x) quasiquote(x, int)
diff --git a/src/nvim/map.h b/src/nvim/map.h
index a84d533262..bfba014736 100644
--- a/src/nvim/map.h
+++ b/src/nvim/map.h
@@ -27,6 +27,7 @@ static const ptr_t value_init_ptr_t = NULL;
static const ssize_t value_init_ssize_t = -1;
static const uint32_t value_init_uint32_t = 0;
static const uint64_t value_init_uint64_t = 0;
+static const int64_t value_init_int64_t = 0;
static const String value_init_String = STRING_INIT;
static const ColorItem value_init_ColorItem = COLOR_ITEM_INITIALIZER;
@@ -123,6 +124,7 @@ KEY_DECLS(int)
KEY_DECLS(cstr_t)
KEY_DECLS(ptr_t)
KEY_DECLS(uint64_t)
+KEY_DECLS(int64_t)
KEY_DECLS(uint32_t)
KEY_DECLS(String)
KEY_DECLS(HlEntry)
@@ -137,6 +139,8 @@ MAP_DECLS(uint32_t, ptr_t)
MAP_DECLS(uint64_t, ptr_t)
MAP_DECLS(uint64_t, ssize_t)
MAP_DECLS(uint64_t, uint64_t)
+MAP_DECLS(int64_t, int64_t)
+MAP_DECLS(int64_t, ptr_t)
MAP_DECLS(uint32_t, uint32_t)
MAP_DECLS(HlEntry, int)
MAP_DECLS(String, int)
diff --git a/src/nvim/memfile.c b/src/nvim/memfile.c
index 333ff75f7b..b1aab0690c 100644
--- a/src/nvim/memfile.c
+++ b/src/nvim/memfile.c
@@ -101,11 +101,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 = MF_DIRTY_NO;
- mf_hash_init(&mfp->mf_hash);
- mf_hash_init(&mfp->mf_trans);
+ 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.
@@ -182,15 +180,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);
}
@@ -271,8 +269,7 @@ 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 = 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!
@@ -294,7 +291,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;
@@ -317,13 +314,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;
}
@@ -356,8 +351,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--;
@@ -399,7 +393,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
@@ -424,7 +419,7 @@ 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.
@@ -447,61 +442,15 @@ 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 = MF_DIRTY_YES;
}
-/// 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;
- }
-}
-
/// Release as many blocks as possible.
///
/// Used in case of out of memory
@@ -520,17 +469,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++;
}
}
}
@@ -558,7 +508,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;
}
@@ -568,7 +518,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;
}
@@ -637,7 +587,7 @@ static int mf_write(memfile_T *mfp, bhdr_T *hp)
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;
}
@@ -690,8 +640,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.
@@ -714,15 +662,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;
}
@@ -733,20 +679,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;
}
@@ -823,152 +765,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;
-}
diff --git a/src/nvim/memfile_defs.h b/src/nvim/memfile_defs.h
index 917dd6a905..bf9bb208a4 100644
--- a/src/nvim/memfile_defs.h
+++ b/src/nvim/memfile_defs.h
@@ -5,6 +5,7 @@
#include <stdint.h>
#include <stdlib.h>
+#include "nvim/map.h"
#include "nvim/pos.h"
#include "nvim/types.h"
@@ -15,57 +16,20 @@
/// with negative numbers are currently in memory only.
typedef int64_t blocknr_T;
-/// A hash item.
-///
-/// Items' keys are block numbers.
-/// Items in the same bucket are organized into a doubly-linked list.
-///
-/// Therefore, items can be arbitrary data structures beginning with pointers
-/// for the list and and a block number key.
-typedef struct mf_hashitem {
- struct mf_hashitem *mhi_next;
- struct mf_hashitem *mhi_prev;
- blocknr_T mhi_key;
-} mf_hashitem_T;
-
-/// Initial size for a hashtable.
-#define MHT_INIT_SIZE 64
-
-/// A chained hashtable with block numbers as keys and arbitrary data structures
-/// as items.
-///
-/// This is an intrusive data structure: we require that items begin with
-/// mf_hashitem_T which contains the key and linked list pointers. List of items
-/// in each bucket is doubly-linked.
-typedef struct mf_hashtab {
- size_t mht_mask; ///< mask used to mod hash value to array index
- ///< (nr of items in array is 'mht_mask + 1')
- size_t mht_count; ///< number of items inserted
- mf_hashitem_T **mht_buckets; ///< points to the array of buckets (can be
- ///< mht_small_buckets or a newly allocated array
- ///< when mht_small_buckets becomes too small)
- mf_hashitem_T *mht_small_buckets[MHT_INIT_SIZE]; ///< initial buckets
-} mf_hashtab_T;
-
/// A block header.
///
/// There is a block header for each previously used block in the memfile.
///
/// The block may be linked in the used list OR in the free list.
-/// The used blocks are also kept in hash lists.
///
/// The used list is a doubly linked list, most recently used block first.
/// The blocks in the used list have a block of memory allocated.
-/// The hash lists are used to quickly find a block in the used list.
/// The free list is a single linked list, not sorted.
/// The blocks in the free list have no block of memory allocated and
/// the contents of the block in the file (if any) is irrelevant.
typedef struct bhdr {
- mf_hashitem_T bh_hashitem; ///< header for hash table and key
-#define bh_bnum bh_hashitem.mhi_key ///< block number, part of bh_hashitem
+ blocknr_T bh_bnum; ///< key used in hash table
- struct bhdr *bh_next; ///< next block header in free or used list
- struct bhdr *bh_prev; ///< previous block header in used list
void *bh_data; ///< pointer to memory (for used block)
unsigned bh_page_count; ///< number of pages in this block
@@ -74,18 +38,6 @@ typedef struct bhdr {
unsigned bh_flags; ///< BH_DIRTY or BH_LOCKED
} bhdr_T;
-/// A block number translation list item.
-///
-/// When a block with a negative number is flushed to the file, it gets
-/// a positive number. Because the reference to the block is still the negative
-/// number, we remember the translation to the new positive number in the
-/// double linked trans lists. The structure is the same as the hash lists.
-typedef struct mf_blocknr_trans_item {
- mf_hashitem_T nt_hashitem; ///< header for hash table and key
-#define nt_old_bnum nt_hashitem.mhi_key ///< old, negative, number
- blocknr_T nt_new_bnum; ///< new, positive, number
-} mf_blocknr_trans_item_T;
-
typedef enum {
MF_DIRTY_NO = 0, ///< no dirty blocks
MF_DIRTY_YES, ///< there are dirty blocks
@@ -98,10 +50,16 @@ typedef struct memfile {
char *mf_ffname; ///< idem, full path
int mf_fd; ///< file descriptor
bhdr_T *mf_free_first; ///< first block header in free list
- bhdr_T *mf_used_first; ///< mru block header in used list
- bhdr_T *mf_used_last; ///< lru block header in used list
- mf_hashtab_T mf_hash; ///< hash lists
- mf_hashtab_T mf_trans; ///< trans lists
+
+ /// The used blocks are kept in mf_hash.
+ /// mf_hash are used to quickly find a block in the used list.
+ PMap(int64_t) mf_hash;
+
+ /// When a block with a negative number is flushed to the file, it gets
+ /// a positive number. Because the reference to the block is still the negative
+ /// number, we remember the translation to the new positive number.
+ Map(int64_t, int64_t) mf_trans;
+
blocknr_T mf_blocknr_max; ///< highest positive block number + 1
blocknr_T mf_blocknr_min; ///< lowest negative block number - 1
blocknr_T mf_neg_count; ///< number of negative blocks numbers
diff --git a/src/nvim/memline.c b/src/nvim/memline.c
index ff5f621611..ecc52a4da8 100644
--- a/src/nvim/memline.c
+++ b/src/nvim/memline.c
@@ -3684,22 +3684,19 @@ static long char_to_long(const char *s_in)
/// - 'fileencoding'
void ml_setflags(buf_T *buf)
{
- bhdr_T *hp;
ZERO_BL *b0p;
if (!buf->b_ml.ml_mfp) {
return;
}
- for (hp = buf->b_ml.ml_mfp->mf_used_last; hp != NULL; hp = hp->bh_prev) {
- if (hp->bh_bnum == 0) {
- b0p = hp->bh_data;
- b0p->b0_dirty = buf->b_changed ? B0_DIRTY : 0;
- b0p->b0_flags = (char)((b0p->b0_flags & ~B0_FF_MASK) | (uint8_t)(get_fileformat(buf) + 1));
- add_b0_fenc(b0p, buf);
- hp->bh_flags |= BH_DIRTY;
- mf_sync(buf->b_ml.ml_mfp, MFS_ZERO);
- break;
- }
+ bhdr_T *hp = pmap_get(int64_t)(&buf->b_ml.ml_mfp->mf_hash, 0);
+ if (hp) {
+ b0p = hp->bh_data;
+ b0p->b0_dirty = buf->b_changed ? B0_DIRTY : 0;
+ b0p->b0_flags = (char)((b0p->b0_flags & ~B0_FF_MASK) | (uint8_t)(get_fileformat(buf) + 1));
+ add_b0_fenc(b0p, buf);
+ hp->bh_flags |= BH_DIRTY;
+ mf_sync(buf->b_ml.ml_mfp, MFS_ZERO);
}
}