Merge pull request #24875 from bfredl/memfilemap

refactor(memfile): change mf_trans and mf_hash from ad-hoc hashtable to Map
This commit is contained in:
bfredl 2023-09-10 19:10:29 +02:00 committed by GitHub
commit a03e00a353
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 73 additions and 321 deletions

View File

@ -1402,7 +1402,7 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
while ((fd = os_open(wfname, fflags, mode)) < 0) {
// A forced write will try to create a new file if the old one
// is still readonly. This may also happen when the directory
// is read-only. In that case the mch_remove() will fail.
// is read-only. In that case the os_remove() will fail.
if (err.msg == NULL) {
#ifdef UNIX
FileInfo file_info;

View File

@ -82,15 +82,6 @@
#define READBIN "rb"
#define APPENDBIN "ab"
// mch_open_rw(): invoke os_open() with third argument for user R/W.
#if defined(UNIX) // open in rw------- mode
# define MCH_OPEN_RW(n, f) os_open((n), (f), (mode_t)0600)
#elif defined(MSWIN)
# define MCH_OPEN_RW(n, f) os_open((n), (f), S_IREAD | S_IWRITE)
#else
# define MCH_OPEN_RW(n, f) os_open((n), (f), 0)
#endif
#define REPLACE_NORMAL(s) (((s)& REPLACE_FLAG) && !((s)& VREPLACE_FLAG))
// MB_PTR_ADV(): advance a pointer to the next character, taking care of

View File

@ -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)

View File

@ -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)

View File

@ -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;
}
@ -810,7 +752,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(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
@ -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;
}

View File

@ -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

View File

@ -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);
}
}

View File

@ -618,8 +618,6 @@ static void free_menu(vimmenu_T **menup)
{
vimmenu_T *menu = *menup;
// Don't change *menup until after calling gui_mch_destroy_menu(). The
// MacOS code needs the original structure to properly delete the menu.
*menup = menu->next;
xfree(menu->name);
xfree(menu->dname);

View File

@ -5171,7 +5171,6 @@ static void win_free(win_T *wp, tabpage_T *tp)
alist_unlink(wp->w_alist);
// Don't execute autocommands while the window is halfway being deleted.
// gui_mch_destroy_scrollbar() may trigger a FocusGained event.
block_autocmds();
clear_winopt(&wp->w_onebuf_opt);