diff options
| -rw-r--r-- | src/nvim/shada.c | 34 | ||||
| -rw-r--r-- | test/functional/shada/merging_spec.lua | 54 | 
2 files changed, 69 insertions, 19 deletions
| diff --git a/src/nvim/shada.c b/src/nvim/shada.c index b98aa2f258..c069bd56ad 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -360,10 +360,9 @@ typedef struct {    HMLListEntry *first;    ///< First entry in the list (is not necessary start                            ///< of the array) or NULL.    HMLListEntry *last;     ///< Last entry in the list or NULL. -  HMLListEntry **free_entries;  ///< Free array entries. -  HMLListEntry *last_free_element;  ///< Last free array element. +  HMLListEntry *free_entry;  ///< Last free entry removed by hmll_remove. +  HMLListEntry *last_free_entry;  ///< Last unused element in entries array.    size_t size;            ///< Number of allocated entries. -  size_t free_entries_size;  ///< Number of non-NULL entries in free_entries.    size_t num_entries;     ///< Number of entries already used.    khash_t(hmll_entries) contained_entries;  ///< Hash mapping all history entry                                              ///< strings to corresponding entry @@ -552,13 +551,12 @@ static inline void hmll_init(HMLList *const hmll, const size_t size)      .entries = xcalloc(size, sizeof(hmll->entries[0])),      .first = NULL,      .last = NULL, -    .free_entries = NULL, +    .free_entry = NULL,      .size = size, -    .free_entries_size = 0,      .num_entries = 0,      .contained_entries = KHASH_EMPTY_TABLE(hmll_entries),    }; -  hmll->last_free_element = hmll->entries; +  hmll->last_free_entry = hmll->entries;  }  /// Iterate over HMLList in forward direction @@ -579,15 +577,11 @@ static inline void hmll_remove(HMLList *const hmll,                                 HMLListEntry *const hmll_entry)    FUNC_ATTR_NONNULL_ALL  { -  if (hmll->free_entries == NULL) { -    if (hmll_entry == hmll->last_free_element) { -      hmll->last_free_element--; -    } else { -      hmll->free_entries = xcalloc(hmll->size, sizeof(hmll->free_entries[0])); -      hmll->free_entries[hmll->free_entries_size++] = hmll_entry; -    } +  if (hmll_entry == hmll->last_free_entry - 1) { +    hmll->last_free_entry--;    } else { -    hmll->free_entries[hmll->free_entries_size++] = hmll_entry; +    assert(hmll->free_entry == NULL); +    hmll->free_entry = hmll_entry;    }    const khiter_t k = kh_get(hmll_entries, &hmll->contained_entries,                              hmll_entry->data.data.history_item.string); @@ -630,12 +624,15 @@ static inline void hmll_insert(HMLList *const hmll,      hmll_remove(hmll, hmll->first);    }    HMLListEntry *target_entry; -  if (hmll->free_entries == NULL) { -    assert((size_t) (hmll->last_free_element - hmll->entries) +  if (hmll->free_entry == NULL) { +    assert((size_t) (hmll->last_free_entry - hmll->entries)             == hmll->num_entries); -    target_entry = hmll->last_free_element++; +    target_entry = hmll->last_free_entry++;    } else { -    target_entry = hmll->free_entries[--hmll->free_entries_size]; +    assert((size_t) (hmll->last_free_entry - hmll->entries) - 1 +           == hmll->num_entries); +    target_entry = hmll->free_entry; +    hmll->free_entry = NULL;    }    target_entry->data = data;    target_entry->can_free_entry = can_free_entry; @@ -680,7 +677,6 @@ static inline void hmll_dealloc(HMLList *const hmll)  {    kh_dealloc(hmll_entries, &hmll->contained_entries);    xfree(hmll->entries); -  xfree(hmll->free_entries);  }  /// Wrapper for reading from file descriptors diff --git a/test/functional/shada/merging_spec.lua b/test/functional/shada/merging_spec.lua index 3daa5af7c2..fd0111d30d 100644 --- a/test/functional/shada/merging_spec.lua +++ b/test/functional/shada/merging_spec.lua @@ -175,6 +175,60 @@ describe('ShaDa history merging code', function()      end      eq(#items, found)    end) + +  it('correctly merges history items with duplicate mid entry when writing', +  function() +    -- Regression test: ShaDa code used to crash here. +    -- Conditions: +    -- 1. Entry which is duplicate to non-last entry. +    -- 2. At least one more non-duplicate entry. +    wshada('\004\000\009\147\000\196\002ab\196\001a' +           .. '\004\001\009\147\000\196\002ac\196\001a' +           .. '\004\002\009\147\000\196\002ad\196\001a' +           .. '\004\003\009\147\000\196\002ac\196\001a' +           .. '\004\004\009\147\000\196\002af\196\001a' +           .. '\004\005\009\147\000\196\002ae\196\001a' +           .. '\004\006\009\147\000\196\002ag\196\001a' +           .. '\004\007\009\147\000\196\002ah\196\001a' +           .. '\004\008\009\147\000\196\002ai\196\001a' +          ) +    eq(0, exc_exec('wshada ' .. shada_fname)) +    local items = {'ab', 'ad', 'ac', 'af', 'ae', 'ag', 'ah', 'ai'} +    local found = 0 +    for _, v in ipairs(read_shada_file(shada_fname)) do +      if v.type == 4 and v.value[1] == 0 then +        found = found + 1 +        eq(items[found], v.value[2]) +        eq('a', v.value[3]) +      end +    end +    eq(#items, found) +  end) + +  it('correctly merges history items with duplicate adj entry when writing', +  function() +    wshada('\004\000\009\147\000\196\002ab\196\001a' +           .. '\004\001\009\147\000\196\002ac\196\001a' +           .. '\004\002\009\147\000\196\002ad\196\001a' +           .. '\004\003\009\147\000\196\002ad\196\001a' +           .. '\004\004\009\147\000\196\002af\196\001a' +           .. '\004\005\009\147\000\196\002ae\196\001a' +           .. '\004\006\009\147\000\196\002ag\196\001a' +           .. '\004\007\009\147\000\196\002ah\196\001a' +           .. '\004\008\009\147\000\196\002ai\196\001a' +          ) +    eq(0, exc_exec('wshada ' .. shada_fname)) +    local items = {'ab', 'ac', 'ad', 'af', 'ae', 'ag', 'ah', 'ai'} +    local found = 0 +    for _, v in ipairs(read_shada_file(shada_fname)) do +      if v.type == 4 and v.value[1] == 0 then +        found = found + 1 +        eq(items[found], v.value[2]) +        eq('a', v.value[3]) +      end +    end +    eq(#items, found) +  end)  end)  describe('ShaDa search pattern support code', function() | 
