From e38153eda9fbe5c7bd5cb9e4f4a2b579e0658927 Mon Sep 17 00:00:00 2001 From: "dgrowley@gmail.com" Date: Thu, 2 Jul 2020 19:07:34 +1200 Subject: [PATCH v12 2/5] Allow users of simplehash.h to perform direct deletions Previously simplehash.h only exposed a method to perform a hash table delete by the key. This required performing a hash table lookup in order to find the element which belongs to that key. Having the code this way made sense for the existing callers, but in an up-coming commit, a caller already has the element which it would like to delete, so can do so without performing a lookup. --- src/include/lib/simplehash.h | 62 +++++++++++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/src/include/lib/simplehash.h b/src/include/lib/simplehash.h index 395be1ca9a..da51781e98 100644 --- a/src/include/lib/simplehash.h +++ b/src/include/lib/simplehash.h @@ -110,6 +110,7 @@ #define SH_RESET SH_MAKE_NAME(reset) #define SH_INSERT SH_MAKE_NAME(insert) #define SH_INSERT_HASH SH_MAKE_NAME(insert_hash) +#define SH_DELETE_ITEM SH_MAKE_NAME(delete_item) #define SH_DELETE SH_MAKE_NAME(delete) #define SH_LOOKUP SH_MAKE_NAME(lookup) #define SH_LOOKUP_HASH SH_MAKE_NAME(lookup_hash) @@ -217,6 +218,9 @@ SH_SCOPE SH_ELEMENT_TYPE *SH_LOOKUP(SH_TYPE * tb, SH_KEY_TYPE key); SH_SCOPE SH_ELEMENT_TYPE *SH_LOOKUP_HASH(SH_TYPE * tb, SH_KEY_TYPE key, uint32 hash); +/* void _delete_item(_hash *tb, *entry) */ +SH_SCOPE void SH_DELETE_ITEM(SH_TYPE * tb, SH_ELEMENT_TYPE * entry); + /* bool _delete(_hash *tb, key) */ SH_SCOPE bool SH_DELETE(SH_TYPE * tb, SH_KEY_TYPE key); @@ -829,7 +833,7 @@ SH_LOOKUP_HASH(SH_TYPE * tb, SH_KEY_TYPE key, uint32 hash) } /* - * Delete entry from hash table. Returns whether to-be-deleted key was + * Delete entry from hash table by key. Returns whether to-be-deleted key was * present. */ SH_SCOPE bool @@ -900,6 +904,61 @@ SH_DELETE(SH_TYPE * tb, SH_KEY_TYPE key) } } +/* + * Delete entry from hash table by entry pointer + */ +SH_SCOPE void +SH_DELETE_ITEM(SH_TYPE * tb, SH_ELEMENT_TYPE * entry) +{ + SH_ELEMENT_TYPE *lastentry = entry; + uint32 hash = SH_ENTRY_HASH(tb, entry); + uint32 startelem = SH_INITIAL_BUCKET(tb, hash); + uint32 curelem; + + /* Calculate the index of 'entry' */ + curelem = entry - &tb->data[0]; + + tb->members--; + + /* + * Backward shift following elements till either an empty element or an + * element at its optimal position is encountered. + * + * While that sounds expensive, the average chain length is short, and + * deletions would otherwise require tombstones. + */ + while (true) + { + SH_ELEMENT_TYPE *curentry; + uint32 curhash; + uint32 curoptimal; + + curelem = SH_NEXT(tb, curelem, startelem); + curentry = &tb->data[curelem]; + + if (curentry->status != SH_STATUS_IN_USE) + { + lastentry->status = SH_STATUS_EMPTY; + break; + } + + curhash = SH_ENTRY_HASH(tb, curentry); + curoptimal = SH_INITIAL_BUCKET(tb, curhash); + + /* current is at optimal position, done */ + if (curoptimal == curelem) + { + lastentry->status = SH_STATUS_EMPTY; + break; + } + + /* shift */ + memcpy(lastentry, curentry, sizeof(SH_ELEMENT_TYPE)); + + lastentry = curentry; + } +} + /* * Initialize iterator. */ @@ -1102,6 +1161,7 @@ SH_STAT(SH_TYPE * tb) #undef SH_RESET #undef SH_INSERT #undef SH_INSERT_HASH +#undef SH_DELETE_ITEM #undef SH_DELETE #undef SH_LOOKUP #undef SH_LOOKUP_HASH -- 2.27.0