From db04956b20ba4324a0cd4d391a2d2e3e2a358831 Mon Sep 17 00:00:00 2001 From: Alexandre Felipe Date: Sun, 15 Mar 2026 00:00:00 +0000 Subject: [PATCH 1/5] Custom simplehash empty value detection Changes the empty value identification in simplehash allowing custom values to be used. The default continues to use the status field. For types where the key value already has an "invalid" value, the macros SH_ENTRY_EMPTY, SH_MAKE_EMPTY and SH_MAKE_IN_USE can be overridden to elliminate the need for a separate status field. --- src/include/lib/simplehash.h | 59 +++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 21 deletions(-) diff --git a/src/include/lib/simplehash.h b/src/include/lib/simplehash.h index 848719232a..3c03a7e9c9 100644 --- a/src/include/lib/simplehash.h +++ b/src/include/lib/simplehash.h @@ -287,6 +287,20 @@ SH_SCOPE void SH_STAT(SH_TYPE * tb); #define SH_COMPARE_KEYS(tb, ahash, akey, b) (SH_EQUAL(tb, b->SH_KEY, akey)) #endif +/* + * Macros to check/set entry status. Users can override these to avoid + * needing a separate status field if their key type has an "invalid" value. + */ +#ifndef SH_ENTRY_EMPTY +#define SH_ENTRY_EMPTY(entry) ((entry)->status == SH_STATUS_EMPTY) +#endif +#ifndef SH_MAKE_EMPTY +#define SH_MAKE_EMPTY(entry) ((entry)->status = SH_STATUS_EMPTY) +#endif +#ifndef SH_MAKE_IN_USE +#define SH_MAKE_IN_USE(entry) ((entry)->status = SH_STATUS_IN_USE) +#endif + /* * Wrap the following definitions in include guards, to avoid multiple * definition errors if this header is included more than once. The rest of @@ -544,7 +558,7 @@ SH_GROW(SH_TYPE * tb, uint64 newsize) uint32 hash; uint32 optimal; - if (oldentry->status != SH_STATUS_IN_USE) + if (SH_ENTRY_EMPTY(oldentry)) { startelem = i; break; @@ -566,7 +580,7 @@ SH_GROW(SH_TYPE * tb, uint64 newsize) { SH_ELEMENT_TYPE *oldentry = &olddata[copyelem]; - if (oldentry->status == SH_STATUS_IN_USE) + if (!SH_ENTRY_EMPTY(oldentry)) { uint32 hash; uint32 startelem2; @@ -582,7 +596,7 @@ SH_GROW(SH_TYPE * tb, uint64 newsize) { newentry = &newdata[curelem]; - if (newentry->status == SH_STATUS_EMPTY) + if (SH_ENTRY_EMPTY(newentry)) { break; } @@ -653,14 +667,14 @@ restart: SH_ELEMENT_TYPE *entry = &data[curelem]; /* any empty bucket can directly be used */ - if (entry->status == SH_STATUS_EMPTY) + if (SH_ENTRY_EMPTY(entry)) { tb->members++; entry->SH_KEY = key; #ifdef SH_STORE_HASH SH_GET_HASH(tb, entry) = hash; #endif - entry->status = SH_STATUS_IN_USE; + SH_MAKE_IN_USE(entry); *found = false; return entry; } @@ -675,7 +689,7 @@ restart: if (SH_COMPARE_KEYS(tb, hash, key, entry)) { - Assert(entry->status == SH_STATUS_IN_USE); + Assert(!SH_ENTRY_EMPTY(entry)); *found = true; return entry; } @@ -699,7 +713,7 @@ restart: emptyelem = SH_NEXT(tb, emptyelem, startelem); emptyentry = &data[emptyelem]; - if (emptyentry->status == SH_STATUS_EMPTY) + if (SH_ENTRY_EMPTY(emptyentry)) { lastentry = emptyentry; break; @@ -748,7 +762,7 @@ restart: #ifdef SH_STORE_HASH SH_GET_HASH(tb, entry) = hash; #endif - entry->status = SH_STATUS_IN_USE; + SH_MAKE_IN_USE(entry); *found = false; return entry; } @@ -810,12 +824,12 @@ SH_LOOKUP_HASH_INTERNAL(SH_TYPE * tb, SH_KEY_TYPE key, uint32 hash) { SH_ELEMENT_TYPE *entry = &tb->data[curelem]; - if (entry->status == SH_STATUS_EMPTY) + if (SH_ENTRY_EMPTY(entry)) { return NULL; } - Assert(entry->status == SH_STATUS_IN_USE); + Assert(!SH_ENTRY_EMPTY(entry)); if (SH_COMPARE_KEYS(tb, hash, key, entry)) return entry; @@ -868,10 +882,10 @@ SH_DELETE(SH_TYPE * tb, SH_KEY_TYPE key) { SH_ELEMENT_TYPE *entry = &tb->data[curelem]; - if (entry->status == SH_STATUS_EMPTY) + if (SH_ENTRY_EMPTY(entry)) return false; - if (entry->status == SH_STATUS_IN_USE && + if (!SH_ENTRY_EMPTY(entry) && SH_COMPARE_KEYS(tb, hash, key, entry)) { SH_ELEMENT_TYPE *lastentry = entry; @@ -894,9 +908,9 @@ SH_DELETE(SH_TYPE * tb, SH_KEY_TYPE key) curelem = SH_NEXT(tb, curelem, startelem); curentry = &tb->data[curelem]; - if (curentry->status != SH_STATUS_IN_USE) + if (SH_ENTRY_EMPTY(curentry)) { - lastentry->status = SH_STATUS_EMPTY; + SH_MAKE_EMPTY(lastentry); break; } @@ -906,7 +920,7 @@ SH_DELETE(SH_TYPE * tb, SH_KEY_TYPE key) /* current is at optimal position, done */ if (curoptimal == curelem) { - lastentry->status = SH_STATUS_EMPTY; + SH_MAKE_EMPTY(lastentry); break; } @@ -957,9 +971,9 @@ SH_DELETE_ITEM(SH_TYPE * tb, SH_ELEMENT_TYPE * entry) curelem = SH_NEXT(tb, curelem, startelem); curentry = &tb->data[curelem]; - if (curentry->status != SH_STATUS_IN_USE) + if (SH_ENTRY_EMPTY(curentry)) { - lastentry->status = SH_STATUS_EMPTY; + SH_MAKE_EMPTY(lastentry); break; } @@ -969,7 +983,7 @@ SH_DELETE_ITEM(SH_TYPE * tb, SH_ELEMENT_TYPE * entry) /* current is at optimal position, done */ if (curoptimal == curelem) { - lastentry->status = SH_STATUS_EMPTY; + SH_MAKE_EMPTY(lastentry); break; } @@ -997,7 +1011,7 @@ SH_START_ITERATE(SH_TYPE * tb, SH_ITERATOR * iter) { SH_ELEMENT_TYPE *entry = &tb->data[i]; - if (entry->status != SH_STATUS_IN_USE) + if (SH_ENTRY_EMPTY(entry)) { startelem = i; break; @@ -1063,7 +1077,7 @@ SH_ITERATE(SH_TYPE * tb, SH_ITERATOR * iter) if ((iter->cur & tb->sizemask) == (iter->end & tb->sizemask)) iter->done = true; - if (elem->status == SH_STATUS_IN_USE) + if (!SH_ENTRY_EMPTY(elem)) { return elem; } @@ -1140,7 +1154,7 @@ SH_STAT(SH_TYPE * tb) elem = &tb->data[i]; - if (elem->status != SH_STATUS_IN_USE) + if (SH_ENTRY_EMPTY(elem)) continue; hash = SH_ENTRY_HASH(tb, elem); @@ -1205,6 +1219,9 @@ SH_STAT(SH_TYPE * tb) #undef SH_STORE_HASH #undef SH_USE_NONDEFAULT_ALLOCATOR #undef SH_EQUAL +#undef SH_ENTRY_EMPTY +#undef SH_MAKE_EMPTY +#undef SH_MAKE_IN_USE /* undefine locally declared macros */ #undef SH_MAKE_PREFIX -- 2.34.1