Use single u32/u64 as GUF_DICT_KV_META_T instead of two
This commit is contained in:
parent
e535d39e3d
commit
6a7bd2bd97
249
src/guf_dict.h
249
src/guf_dict.h
@ -9,21 +9,6 @@
|
|||||||
#include "guf_common.h"
|
#include "guf_common.h"
|
||||||
#include "guf_alloc.h"
|
#include "guf_alloc.h"
|
||||||
#include "guf_hash.h"
|
#include "guf_hash.h"
|
||||||
|
|
||||||
typedef struct guf_dict_kv_meta_32 {
|
|
||||||
uint32_t kv_idx; // Index into the kv_elems dbuf.
|
|
||||||
uint32_t key_hash;
|
|
||||||
} guf_dict_kv_meta_32;
|
|
||||||
|
|
||||||
typedef struct guf_dict_kv_meta_64 {
|
|
||||||
uint64_t kv_idx; // Index into the kv_elems dbuf.
|
|
||||||
uint64_t key_hash;
|
|
||||||
} guf_dict_kv_meta_64;
|
|
||||||
|
|
||||||
typedef struct guf_dict_kv_meta {
|
|
||||||
guf_hash_size_t kv_idx; // Index into the kv_elems dbuf.
|
|
||||||
guf_hash_size_t key_hash;
|
|
||||||
} guf_dict_kv_meta;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef GUF_DICT_KEY_T
|
#ifndef GUF_DICT_KEY_T
|
||||||
@ -42,42 +27,62 @@
|
|||||||
#define GUF_DICT_IS_SET
|
#define GUF_DICT_IS_SET
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(GUF_DICT_32_BIT)
|
#if defined(GUF_DICT_32_BIT_HASH)
|
||||||
#define GUF_DICT_KV_IDX_T uint32_t
|
#define GUF_DICT_HASH_T uint32_t
|
||||||
#define GUF_DICT_KV_META_T guf_dict_kv_meta_32
|
#define GUF_DICT_HASH_T_MAX UINT32_MAX
|
||||||
#define GUF_DICT_KV_IDX_NULL UINT32_MAX
|
#elif defined(GUF_DICT_64_BIT_HASH)
|
||||||
#elif defined(GUF_DICT_64_BIT)
|
#define GUF_DICT_HASH_T uint64_t
|
||||||
#define GUF_DICT_KV_IDX_T uint64_t
|
#define GUF_DICT_HASH_T_MAX UINT64_MAX
|
||||||
#define GUF_DICT_KV_META_T guf_dict_kv_meta_64
|
|
||||||
#define GUF_DICT_KV_IDX_NULL UINT64_MAX
|
|
||||||
#else
|
#else
|
||||||
#define GUF_DICT_KV_IDX_T guf_hash_size_t
|
#define GUF_DICT_HASH_T guf_hash_size_t
|
||||||
#define GUF_DICT_KV_META_T guf_dict_kv_meta
|
#define GUF_DICT_HASH_T_MAX GUF_HASH_MAX
|
||||||
#define GUF_DICT_KV_IDX_NULL GUF_HASH_MAX
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define GUF_DICT_KV_IDX_TOMBSTONE (GUF_DICT_KV_IDX_NULL - 1)
|
#if defined (GUF_DICT_64_BIT_IDX)
|
||||||
|
#define GUF_DICT_KV_META_T uint64_t
|
||||||
|
/*
|
||||||
|
Store a 16-bit hash-fragment in the upper 16-bits of kv_meta
|
||||||
|
-> (2^48 - 1 is IDX_NULL, 2^48 - 2 is IDX_TOMBSTONE, 2^48 - 3 is the largest actual idx,
|
||||||
|
i.e. the max amount of actual kv_elems the dict could hold is 2^48 - 2 = 281,474,976,710,654).
|
||||||
|
*/
|
||||||
|
#define GUF_DICT_KV_META_HASHFRAG_MASK UINT64_C(0xffff000000000000)
|
||||||
|
#define GUF_DICT_KV_META_IDX_MASK (~UINT64_C(0xffff000000000000))
|
||||||
|
|
||||||
#if PTRDIFF_MAX <= SIZE_T_MAX
|
#if GUF_DICT_HASH_T_MAX == UINT64_MAX
|
||||||
#define GUF_DICT_MAX_PTR PTRDIFF_MAX
|
#define GUF_DICT_HASH_T_GET_HASHFRAG(HASH) ( (HASH) & GUF_DICT_KV_META_HASHFRAG_MASK )
|
||||||
|
#elif GUF_DICT_HASH_T_MAX == UINT32_MAX
|
||||||
|
#define GUF_DICT_HASH_T_GET_HASHFRAG(HASH) ( (((uint64_t)(HASH)) << 32) & GUF_DICT_HASH_T_HASHFRAG_MASK )
|
||||||
|
#else
|
||||||
|
#error "guf_dict: invalid hash size (should not happen)"
|
||||||
|
#endif
|
||||||
#else
|
#else
|
||||||
#define GUF_DICT_MAX_PTR SIZE_T_MAX
|
#define GUF_DICT_KV_META_T uint32_t
|
||||||
|
/*
|
||||||
|
Store a 7-bit hash-fragment in the upper 7-bits of kv_meta
|
||||||
|
-> (2^25 - 1 is IDX_NULL, 2^25 - 2 is IDX_TOMBSTONE, 2^25 - 3 is the largest actual idx,
|
||||||
|
i.e. the max amount of actual kv_elems the dict could hold is 2^25 - 2 = 33,554,430).
|
||||||
|
*/
|
||||||
|
#define GUF_DICT_KV_META_HASHFRAG_MASK UINT32_C(0xfe000000)
|
||||||
|
#define GUF_DICT_KV_META_IDX_MASK (~UINT32_C(0xfe000000))
|
||||||
|
|
||||||
|
#if GUF_DICT_HASH_T_MAX == UINT64_MAX
|
||||||
|
#define GUF_DICT_HASH_T_GET_HASHFRAG(HASH) ( ((uint32_t)((HASH) >> 32)) & GUF_DICT_KV_META_HASHFRAG_MASK )
|
||||||
|
#elif GUF_DICT_HASH_T_MAX == UINT32_MAX
|
||||||
|
#define GUF_DICT_HASH_T_GET_HASHFRAG(HASH) ( (HASH) & GUF_DICT_KV_META_HASHFRAG_MASK )
|
||||||
|
#else
|
||||||
|
#error "guf_dict: invalid hash size (should not happen)"
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if (GUF_DICT_KV_IDX_TOMBSTONE - 1) <= GUF_DICT_MAX_PTR
|
#define GUF_DICT_KV_META_IDX_NULL GUF_DICT_KV_META_IDX_MASK
|
||||||
#define GUF_DICT_KV_IDX_T_MAX (GUF_DICT_KV_IDX_TOMBSTONE - 1)
|
#define GUF_DICT_KV_META_IDX_TOMBSTONE (GUF_DICT_KV_META_IDX_NULL - 1)
|
||||||
#else
|
#define GUF_DICT_KV_META_IDX_MAX (GUF_DICT_KV_META_IDX_TOMBSTONE - 1)
|
||||||
#define GUF_DICT_KV_IDX_T_MAX GUF_DICT_MAX_PTR
|
|
||||||
#endif
|
#define GUF_DICT_META_GET_IDX(META) ( (META) & GUF_DICT_KV_META_IDX_MASK )
|
||||||
|
#define GUF_DICT_META_GET_HASHFRAG(META) ( (META) & GUF_DICT_KV_META_HASHFRAG_MASK )
|
||||||
|
#define GUF_DICT_META_IS_NULL(META) ( GUF_DICT_META_GET_IDX(META) == GUF_DICT_KV_META_IDX_NULL )
|
||||||
|
#define GUF_DICT_META_IS_TOMBSTONE(META) ( GUF_DICT_META_GET_IDX(META) == GUF_DICT_KV_META_IDX_TOMBSTONE )
|
||||||
|
|
||||||
// #ifndef GUF_DICT_KEY_LOOKUP_T
|
|
||||||
// #define GUF_DICT_KEY_LOOKUP_T GUF_DICT_KEY_T
|
|
||||||
// #else
|
|
||||||
// // GUF_DICT_KEY_LOOKUP_T convert(const GUF_DICT_KEY_T *key)
|
|
||||||
// #ifndef GUF_DICT_KEY_TO_LOOKUP_KEY_CONVERT
|
|
||||||
// #error "GUF_DICT_KEY_TO_LOOKUP_KEY_CONVis must be defined since GUF_DICT_KEY_LOOKUP_T is defined"
|
|
||||||
// #endif
|
|
||||||
// #endif
|
|
||||||
|
|
||||||
#ifndef GUF_DICT_NAME
|
#ifndef GUF_DICT_NAME
|
||||||
#define GUF_DICT_NAME GUF_CAT(dict_, GUF_CAT(GUF_DICT_KEY_T, GUF_CAT(_to_, GUF_DICT_VAL_T)))
|
#define GUF_DICT_NAME GUF_CAT(dict_, GUF_CAT(GUF_DICT_KEY_T, GUF_CAT(_to_, GUF_DICT_VAL_T)))
|
||||||
@ -170,15 +175,16 @@ GUF_DICT_KWRDS bool GUF_CAT(GUF_DICT_NAME, _max_capacity)(void);
|
|||||||
// #define GUF_DICT_IMPL /* DEBUGGGGGGGGG */
|
// #define GUF_DICT_IMPL /* DEBUGGGGGGGGG */
|
||||||
|
|
||||||
#if defined(GUF_DICT_IMPL) || defined(GUF_DICT_IMPL_STATIC)
|
#if defined(GUF_DICT_IMPL) || defined(GUF_DICT_IMPL_STATIC)
|
||||||
|
|
||||||
#include "guf_assert.h"
|
#include "guf_assert.h"
|
||||||
#include "guf_math.h"
|
#include "guf_math.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
GUF_DICT_KWRDS bool GUF_CAT(GUF_DICT_NAME, _debug_valid_size)(const GUF_DICT_NAME *ht)
|
GUF_DICT_KWRDS bool GUF_CAT(GUF_DICT_NAME, _debug_valid_size)(const GUF_DICT_NAME *ht)
|
||||||
{
|
{
|
||||||
ptrdiff_t cnt = 0;
|
ptrdiff_t cnt = 0;
|
||||||
for (ptrdiff_t i = 0; i < ht->kv_indices_cap; ++i) {
|
for (ptrdiff_t i = 0; i < ht->kv_indices_cap; ++i) {
|
||||||
if (ht->kv_indices[i].kv_idx != GUF_DICT_KV_IDX_NULL && ht->kv_indices[i].kv_idx != GUF_DICT_KV_IDX_TOMBSTONE) {
|
if (!GUF_DICT_META_IS_NULL(ht->kv_indices[i]) && !GUF_DICT_META_IS_TOMBSTONE(ht->kv_indices[i])) {
|
||||||
++cnt;
|
++cnt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -247,16 +253,25 @@ GUF_DICT_KWRDS bool GUF_CAT(GUF_DICT_NAME, _valid)(const GUF_DICT_NAME *ht)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bool kv_dbuf_valid = GUF_CAT(GUF_DICT_KV_DBUF, _valid)(&ht->kv_elems);
|
bool kv_dbuf_valid = GUF_CAT(GUF_DICT_KV_DBUF, _valid)(&ht->kv_elems);
|
||||||
bool kv_meta_buf_valid = (!ht->kv_indices && !ht->kv_indices_cap) || (ht->kv_indices && ht->kv_indices_cap);
|
bool kv_meta_buf_valid = (!ht->kv_indices && !ht->kv_indices_cap) || (ht->kv_indices && ht->kv_indices_cap && guf_is_pow2_size_t(ht->kv_indices_cap));
|
||||||
bool count_valid = ht->num_tombstones >= 0 && ht->kv_elems.size >= 0 && ((ht->kv_elems.size + ht->num_tombstones) <= ht->kv_indices_cap);
|
bool count_valid = ht->num_tombstones >= 0 && ht->kv_elems.size >= 0 && (size_t)ht->kv_elems.size <= GUF_DICT_KV_META_IDX_MAX && ((ht->kv_elems.size + ht->num_tombstones) <= ht->kv_indices_cap);
|
||||||
return kv_dbuf_valid && kv_meta_buf_valid && count_valid;
|
return kv_dbuf_valid && kv_meta_buf_valid && count_valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
GUF_DICT_KWRDS bool GUF_CAT(GUF_DICT_NAME, _max_capacity)(void)
|
GUF_DICT_KWRDS bool GUF_CAT(GUF_DICT_NAME, _max_capacity)(void)
|
||||||
{
|
{
|
||||||
const ptrdiff_t max_cap_kv_elems = GUF_ALLOC_MAX_CAPACITY(GUF_DICT_KV_NAME);
|
const size_t max_cap_kv_elems = GUF_MIN(GUF_DICT_KV_META_IDX_MAX + 1, GUF_ALLOC_MAX_CAPACITY(GUF_DICT_KV_NAME));
|
||||||
const ptrdiff_t max_cap_kv_indices = GUF_ALLOC_MAX_CAPACITY(GUF_DICT_KV_META_T);
|
|
||||||
return GUF_MIN(max_cap_kv_elems, max_cap_kv_indices);
|
const size_t max_cap_kv_indices = GUF_ALLOC_MAX_CAPACITY(GUF_DICT_KV_META_T);
|
||||||
|
// Find next power of two (capacities must be powers of two).
|
||||||
|
size_t pow2_cap = 1;
|
||||||
|
const size_t max_pow2 = SIZE_T_MAX & ~(SIZE_T_MAX >> 1);
|
||||||
|
while ((pow2_cap < max_pow2) && ((pow2_cap << 1) < max_cap_kv_indices) ) {
|
||||||
|
pow2_cap <<= 1;
|
||||||
|
}
|
||||||
|
GUF_ASSERT(guf_is_pow2_size_t(pow2_cap) && pow2_cap <= max_cap_kv_indices && pow2_cap > 1);
|
||||||
|
|
||||||
|
return GUF_MIN(max_cap_kv_elems, pow2_cap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -304,9 +319,10 @@ static size_t GUF_CAT(GUF_DICT_NAME, _find_idx)(GUF_DICT_NAME *ht, const GUF_DIC
|
|||||||
*key_exists = false;
|
*key_exists = false;
|
||||||
return SIZE_T_MAX;
|
return SIZE_T_MAX;
|
||||||
}
|
}
|
||||||
const GUF_DICT_KV_IDX_T hash = GUF_DICT_KEY_HASH(key);
|
const GUF_DICT_HASH_T hash = GUF_DICT_KEY_HASH(key);
|
||||||
|
const GUF_DICT_KV_META_T hash_frag = GUF_DICT_HASH_T_GET_HASHFRAG(hash);
|
||||||
|
|
||||||
#define GUF_MOD_CAP(a) ((size_t)(a) & (size_t)(ht->kv_indices_cap - 1)) // a % ht->kv_indices_cap (kv_indices_cap must be a power of two > 0)
|
#define GUF_MOD_CAP(A) ((size_t)(A) & (size_t)(ht->kv_indices_cap - 1)) /* A % ht->kv_indices_cap (kv_indices_cap must be a power of two > 0) */
|
||||||
|
|
||||||
size_t idx = GUF_MOD_CAP(hash);
|
size_t idx = GUF_MOD_CAP(hash);
|
||||||
const size_t start_idx = idx;
|
const size_t start_idx = idx;
|
||||||
@ -314,29 +330,28 @@ static size_t GUF_CAT(GUF_DICT_NAME, _find_idx)(GUF_DICT_NAME *ht, const GUF_DIC
|
|||||||
size_t probe_len = 0;
|
size_t probe_len = 0;
|
||||||
// size_t seen_occupied = 0; // This allows us to bail out early once we visited every non-null/non-tombstone kv_idx.
|
// size_t seen_occupied = 0; // This allows us to bail out early once we visited every non-null/non-tombstone kv_idx.
|
||||||
do {
|
do {
|
||||||
if (ht->kv_indices[idx].kv_idx == GUF_DICT_KV_IDX_NULL) { // 1.) Empty.
|
const GUF_DICT_KV_META_T kv_idx = GUF_DICT_META_GET_IDX(ht->kv_indices[idx]);
|
||||||
|
const GUF_DICT_KV_META_T kv_hashfrag = GUF_DICT_META_GET_HASHFRAG(ht->kv_indices[idx]);
|
||||||
|
|
||||||
|
if (kv_idx == GUF_DICT_KV_META_IDX_NULL) { // 1.) Empty.
|
||||||
if (first_tombstone_idx != SIZE_T_MAX) {
|
if (first_tombstone_idx != SIZE_T_MAX) {
|
||||||
idx = first_tombstone_idx;
|
idx = first_tombstone_idx;
|
||||||
}
|
}
|
||||||
ht->max_probelen = GUF_MAX((ptrdiff_t)probe_len, ht->max_probelen);
|
ht->max_probelen = GUF_MAX((ptrdiff_t)probe_len, ht->max_probelen);
|
||||||
GUF_ASSERT((ht->kv_indices[idx].kv_idx == GUF_DICT_KV_IDX_NULL) || (ht->kv_indices[idx].kv_idx == GUF_DICT_KV_IDX_TOMBSTONE));
|
|
||||||
*key_exists = false;
|
*key_exists = false;
|
||||||
return idx;
|
return idx;
|
||||||
} else if (ht->kv_indices[idx].kv_idx == GUF_DICT_KV_IDX_TOMBSTONE) { // 2.) Tombstone.
|
} else if (kv_idx == GUF_DICT_KV_META_IDX_TOMBSTONE) { // 2.) Tombstone.
|
||||||
if (first_tombstone_idx == SIZE_T_MAX) {
|
if (first_tombstone_idx == SIZE_T_MAX) {
|
||||||
first_tombstone_idx = idx;
|
first_tombstone_idx = idx;
|
||||||
}
|
}
|
||||||
goto probe;
|
goto probe;
|
||||||
} else if (hash == ht->kv_indices[idx].key_hash && GUF_DICT_KEY_T_EQ(key, &(GUF_CAT(GUF_DICT_KV_DBUF, _at)(&ht->kv_elems, ht->kv_indices[idx].kv_idx)->key))) { // 3.) Key already exists.
|
} else if (hash_frag == kv_hashfrag && GUF_DICT_KEY_T_EQ(key, &(GUF_CAT(GUF_DICT_KV_DBUF, _at)(&ht->kv_elems, kv_idx)->key))) { // 3.) Key already exists.
|
||||||
ht->max_probelen = GUF_MAX((ptrdiff_t)probe_len, ht->max_probelen);
|
ht->max_probelen = GUF_MAX((ptrdiff_t)probe_len, ht->max_probelen);
|
||||||
*key_exists = true;
|
*key_exists = true;
|
||||||
return idx;
|
return idx;
|
||||||
} else { // 4.) Have to probe due to hash-collision/tombstone.
|
} else { // 4.) Have to probe due to hash-collision/tombstone.
|
||||||
probe:
|
probe:
|
||||||
++probe_len;
|
++probe_len;
|
||||||
// if (ht->kv_indices[idx].kv_idx != GUF_DICT_KV_IDX_NULL && ht->kv_indices[idx].kv_idx != GUF_DICT_KV_IDX_TOMBSTONE) {
|
|
||||||
// ++seen_occupied; // && seen_occupied <= (size_t)ht->kv_elems.size
|
|
||||||
// }
|
|
||||||
idx = GUF_MOD_CAP(start_idx + GUF_CAT(GUF_DICT_NAME, _probe_offset)(probe_len)); // NOTE: Add probe_offset to start_idx and not to idx.
|
idx = GUF_MOD_CAP(start_idx + GUF_CAT(GUF_DICT_NAME, _probe_offset)(probe_len)); // NOTE: Add probe_offset to start_idx and not to idx.
|
||||||
GUF_ASSERT((ptrdiff_t)probe_len <= (ht->kv_elems.size + ht->num_tombstones));
|
GUF_ASSERT((ptrdiff_t)probe_len <= (ht->kv_elems.size + ht->num_tombstones));
|
||||||
}
|
}
|
||||||
@ -345,7 +360,7 @@ static size_t GUF_CAT(GUF_DICT_NAME, _find_idx)(GUF_DICT_NAME *ht, const GUF_DIC
|
|||||||
*key_exists = false;
|
*key_exists = false;
|
||||||
if (first_tombstone_idx != SIZE_T_MAX) { // Edge case: No empty slots, but found tombstone.
|
if (first_tombstone_idx != SIZE_T_MAX) { // Edge case: No empty slots, but found tombstone.
|
||||||
ht->max_probelen = GUF_MAX((ptrdiff_t)probe_len, ht->max_probelen);
|
ht->max_probelen = GUF_MAX((ptrdiff_t)probe_len, ht->max_probelen);
|
||||||
GUF_ASSERT(ht->kv_indices[first_tombstone_idx].kv_idx == GUF_DICT_KV_IDX_NULL);
|
GUF_ASSERT(GUF_DICT_META_GET_IDX(ht->kv_indices[first_tombstone_idx]) == GUF_DICT_KV_META_IDX_NULL);
|
||||||
return first_tombstone_idx;
|
return first_tombstone_idx;
|
||||||
} else { // Failed to find an idx.
|
} else { // Failed to find an idx.
|
||||||
return SIZE_T_MAX;
|
return SIZE_T_MAX;
|
||||||
@ -363,7 +378,7 @@ static void GUF_CAT(GUF_DICT_NAME, _try_grow_if_necessary)(GUF_DICT_NAME *ht, gu
|
|||||||
const double MAX_LOAD_FAC = 0.5;
|
const double MAX_LOAD_FAC = 0.5;
|
||||||
#endif
|
#endif
|
||||||
const ptrdiff_t KV_META_START_CAP = 32; // Must be a power of two > 0.
|
const ptrdiff_t KV_META_START_CAP = 32; // Must be a power of two > 0.
|
||||||
const ptrdiff_t KV_META_GROWTH_FAC = 2; // Must be a power of two > 1.
|
const ptrdiff_t KV_META_GROWTH_FAC = (ht->kv_indices_cap <= 128) ? 4 : 2; // Must be a power of two > 1.
|
||||||
|
|
||||||
guf_allocator *allocator = ht->kv_elems.allocator;
|
guf_allocator *allocator = ht->kv_elems.allocator;
|
||||||
|
|
||||||
@ -376,29 +391,24 @@ static void GUF_CAT(GUF_DICT_NAME, _try_grow_if_necessary)(GUF_DICT_NAME *ht, gu
|
|||||||
ht->kv_indices = new_kv_indices;
|
ht->kv_indices = new_kv_indices;
|
||||||
ht->kv_indices_cap = KV_META_START_CAP;
|
ht->kv_indices_cap = KV_META_START_CAP;
|
||||||
for (ptrdiff_t i = 0; i < ht->kv_indices_cap; ++i) {
|
for (ptrdiff_t i = 0; i < ht->kv_indices_cap; ++i) {
|
||||||
new_kv_indices[i].kv_idx = GUF_DICT_KV_IDX_NULL;
|
new_kv_indices[i] = GUF_DICT_KV_META_IDX_NULL;
|
||||||
new_kv_indices[i].key_hash = 0;
|
|
||||||
}
|
}
|
||||||
} else if (GUF_CAT(GUF_DICT_NAME, _load_factor)(ht) > MAX_LOAD_FAC) { // 1.b) Grow kv-index-buffer if necessary.
|
} else if (GUF_CAT(GUF_DICT_NAME, _load_factor)(ht) > MAX_LOAD_FAC) { // 1.b) Grow kv-index-buffer if necessary.
|
||||||
GUF_ASSERT(ht->kv_indices);
|
GUF_ASSERT(ht->kv_indices);
|
||||||
const ptrdiff_t old_size_bytes = ht->kv_indices_cap * sizeof(GUF_DICT_KV_META_T);
|
GUF_ASSERT((size_t)ht->kv_indices_cap <= GUF_ALLOC_MAX_CAPACITY(GUF_DICT_KV_META_T));
|
||||||
|
const ptrdiff_t old_size_bytes = (size_t)ht->kv_indices_cap * sizeof(GUF_DICT_KV_META_T);
|
||||||
ptrdiff_t new_size_bytes = 0;
|
ptrdiff_t new_size_bytes = 0;
|
||||||
|
|
||||||
const size_t MAX_SIZE_BYTES = (size_t)GUF_ALLOC_MAX_BYTES(GUF_DICT_KV_META_T); // TODO: check
|
const size_t MAX_SIZE_BYTES = (size_t)GUF_ALLOC_MAX_BYTES(GUF_DICT_KV_META_T);
|
||||||
GUF_ASSERT(MAX_SIZE_BYTES % sizeof(GUF_DICT_KV_META_T) == 0 && MAX_SIZE_BYTES <= PTRDIFF_MAX);
|
const size_t new_size_bytes_test = (size_t)old_size_bytes * (size_t)KV_META_GROWTH_FAC;
|
||||||
|
if (guf_mul_is_overflow_size_t(old_size_bytes, KV_META_GROWTH_FAC) || new_size_bytes_test > MAX_SIZE_BYTES) { // Handle overflow.
|
||||||
if (guf_mul_is_overflow_size_t(old_size_bytes, KV_META_GROWTH_FAC)) {
|
|
||||||
new_size_bytes = MAX_SIZE_BYTES;
|
|
||||||
} else {
|
|
||||||
const size_t mul = (size_t)old_size_bytes * (size_t)KV_META_GROWTH_FAC;
|
|
||||||
new_size_bytes = GUF_MIN(mul, MAX_SIZE_BYTES);
|
|
||||||
}
|
|
||||||
GUF_ASSERT(new_size_bytes % sizeof(GUF_DICT_KV_META_T) == 0);
|
|
||||||
|
|
||||||
if (new_size_bytes <= old_size_bytes) { // Handle overflow.
|
|
||||||
guf_err_set_or_panic(err, GUF_ERR_ALLOC_FAIL, GUF_ERR_MSG("in function dict_try_insert: New kv_indices_capacity would overflow)"));
|
guf_err_set_or_panic(err, GUF_ERR_ALLOC_FAIL, GUF_ERR_MSG("in function dict_try_insert: New kv_indices_capacity would overflow)"));
|
||||||
return;
|
return;
|
||||||
|
} else {
|
||||||
|
GUF_ASSERT(new_size_bytes_test <= PTRDIFF_MAX);
|
||||||
|
new_size_bytes = (ptrdiff_t)new_size_bytes_test;
|
||||||
}
|
}
|
||||||
|
GUF_ASSERT_RELEASE(new_size_bytes > old_size_bytes);
|
||||||
|
|
||||||
// TODO: Not sure if alloc and free is better here than realloc (since we do not copy ht->kv_indices anyway.)
|
// TODO: Not sure if alloc and free is better here than realloc (since we do not copy ht->kv_indices anyway.)
|
||||||
GUF_DICT_KV_META_T *new_kv_indices = allocator->alloc(new_size_bytes, allocator->ctx);
|
GUF_DICT_KV_META_T *new_kv_indices = allocator->alloc(new_size_bytes, allocator->ctx);
|
||||||
@ -408,27 +418,26 @@ static void GUF_CAT(GUF_DICT_NAME, _try_grow_if_necessary)(GUF_DICT_NAME *ht, gu
|
|||||||
}
|
}
|
||||||
allocator->free(ht->kv_indices, old_size_bytes, allocator->ctx);
|
allocator->free(ht->kv_indices, old_size_bytes, allocator->ctx);
|
||||||
ht->kv_indices = new_kv_indices;
|
ht->kv_indices = new_kv_indices;
|
||||||
const ptrdiff_t new_indices_cap = new_size_bytes / sizeof(GUF_DICT_KV_META_T); // TODO: check
|
ht->kv_indices_cap = ht->kv_indices_cap * KV_META_GROWTH_FAC;;
|
||||||
GUF_ASSERT(ht->kv_indices_cap < new_indices_cap);
|
GUF_ASSERT(guf_is_pow2_size_t(ht->kv_indices_cap));
|
||||||
ht->kv_indices_cap = new_indices_cap;
|
|
||||||
ht->num_tombstones = 0;
|
ht->num_tombstones = 0;
|
||||||
// ht->max_probelen = 0;
|
// ht->max_probelen = 0;
|
||||||
|
|
||||||
for (ptrdiff_t i = 0; i < ht->kv_indices_cap; ++i) {
|
for (ptrdiff_t i = 0; i < ht->kv_indices_cap; ++i) {
|
||||||
ht->kv_indices[i].kv_idx = GUF_DICT_KV_IDX_NULL;
|
ht->kv_indices[i] = GUF_DICT_KV_META_IDX_NULL;
|
||||||
ht->kv_indices[i].key_hash = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GUF_ASSERT(ht->kv_elems.size <= GUF_DICT_KV_IDX_T_MAX);
|
GUF_ASSERT((size_t)ht->kv_elems.size < GUF_DICT_KV_META_IDX_MAX);
|
||||||
for (ptrdiff_t kv_idx = 0; kv_idx < ht->kv_elems.size; ++kv_idx) { // Re-insert keys.
|
for (ptrdiff_t kv_idx = 0; kv_idx < ht->kv_elems.size; ++kv_idx) { // Re-insert keys.
|
||||||
const GUF_DICT_KV_NAME *kv = GUF_CAT(GUF_DICT_KV_DBUF, _at)(&ht->kv_elems, kv_idx);
|
const GUF_DICT_KV_NAME *kv = GUF_CAT(GUF_DICT_KV_DBUF, _at)(&ht->kv_elems, kv_idx);
|
||||||
GUF_ASSERT(kv);
|
GUF_ASSERT(kv);
|
||||||
bool key_exists = false;
|
bool key_exists = false;
|
||||||
const size_t new_idx = GUF_CAT(GUF_DICT_NAME, _find_idx)(ht, &kv->key, &key_exists);
|
const size_t new_idx = GUF_CAT(GUF_DICT_NAME, _find_idx)(ht, &kv->key, &key_exists);
|
||||||
GUF_ASSERT(!key_exists);
|
GUF_ASSERT(!key_exists);
|
||||||
GUF_ASSERT(new_idx < SIZE_T_MAX && new_idx <= PTRDIFF_MAX);
|
GUF_ASSERT(new_idx < SIZE_T_MAX && new_idx < (size_t)ht->kv_indices_cap);
|
||||||
ht->kv_indices[new_idx].kv_idx = (GUF_DICT_KV_IDX_T)kv_idx;
|
const GUF_DICT_HASH_T key_hash = GUF_DICT_KEY_HASH(&kv->key); // TODO: might be expensive...
|
||||||
ht->kv_indices[new_idx].key_hash = GUF_DICT_KEY_HASH(&kv->key); // TODO: might be expensive...
|
GUF_ASSERT((GUF_DICT_HASH_T_GET_HASHFRAG(key_hash) & (GUF_DICT_KV_META_T)kv_idx) == 0);
|
||||||
|
ht->kv_indices[new_idx] = GUF_DICT_HASH_T_GET_HASHFRAG(key_hash) | (GUF_DICT_KV_META_T)kv_idx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -436,7 +445,6 @@ static void GUF_CAT(GUF_DICT_NAME, _try_grow_if_necessary)(GUF_DICT_NAME *ht, gu
|
|||||||
GUF_ASSERT(GUF_CAT(GUF_DICT_NAME, _load_factor)(ht) <= MAX_LOAD_FAC);
|
GUF_ASSERT(GUF_CAT(GUF_DICT_NAME, _load_factor)(ht) <= MAX_LOAD_FAC);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
GUF_DICT_KWRDS void GUF_CAT(GUF_DICT_NAME, _try_insert)(GUF_DICT_NAME *ht, GUF_DICT_KEY_T *key, GUF_DICT_VAL_T *val, guf_cpy_opt key_opt, guf_cpy_opt val_opt, guf_err *err)
|
GUF_DICT_KWRDS void GUF_CAT(GUF_DICT_NAME, _try_insert)(GUF_DICT_NAME *ht, GUF_DICT_KEY_T *key, GUF_DICT_VAL_T *val, guf_cpy_opt key_opt, guf_cpy_opt val_opt, guf_err *err)
|
||||||
{
|
{
|
||||||
GUF_ASSERT_RELEASE(GUF_CAT(GUF_DICT_NAME, _valid)(ht));
|
GUF_ASSERT_RELEASE(GUF_CAT(GUF_DICT_NAME, _valid)(ht));
|
||||||
@ -446,8 +454,9 @@ GUF_DICT_KWRDS void GUF_CAT(GUF_DICT_NAME, _try_insert)(GUF_DICT_NAME *ht, GUF_D
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ht->kv_elems.size >= GUF_DICT_KV_IDX_T_MAX) {
|
if ((size_t)ht->kv_elems.size >= (GUF_DICT_KV_META_IDX_MAX + 1)) {
|
||||||
guf_err_set_or_panic(err, GUF_ERR_INT_OVERFLOW, GUF_ERR_MSG("in function dict_try_insert: dict has reached its max size (UINT64_MAX - 2 or UINT32_MAX - 2)"));
|
GUF_ASSERT(ht->kv_elems.size == (GUF_DICT_KV_META_IDX_MAX + 1));
|
||||||
|
guf_err_set_or_panic(err, GUF_ERR_INT_OVERFLOW, GUF_ERR_MSG("in function dict_try_insert: dict has reached its max size 33,554,430 (or 2^48 - 2 for GUF_DICT_64_BIT_IDX)"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -460,6 +469,7 @@ GUF_DICT_KWRDS void GUF_CAT(GUF_DICT_NAME, _try_insert)(GUF_DICT_NAME *ht, GUF_D
|
|||||||
GUF_ASSERT_RELEASE(ht->kv_indices_cap > ht->kv_elems.size);
|
GUF_ASSERT_RELEASE(ht->kv_indices_cap > ht->kv_elems.size);
|
||||||
|
|
||||||
// 2.) Insert new key-value pair.
|
// 2.) Insert new key-value pair.
|
||||||
|
const GUF_DICT_HASH_T key_hash = GUF_DICT_KEY_HASH(key);
|
||||||
bool key_exists = false;
|
bool key_exists = false;
|
||||||
size_t idx = GUF_CAT(GUF_DICT_NAME, _find_idx)(ht, key, &key_exists);
|
size_t idx = GUF_CAT(GUF_DICT_NAME, _find_idx)(ht, key, &key_exists);
|
||||||
if (key_exists) {
|
if (key_exists) {
|
||||||
@ -468,14 +478,13 @@ GUF_DICT_KWRDS void GUF_CAT(GUF_DICT_NAME, _try_insert)(GUF_DICT_NAME *ht, GUF_D
|
|||||||
}
|
}
|
||||||
GUF_ASSERT_RELEASE(idx < (size_t)ht->kv_indices_cap);
|
GUF_ASSERT_RELEASE(idx < (size_t)ht->kv_indices_cap);
|
||||||
|
|
||||||
if (ht->kv_indices[idx].kv_idx == GUF_DICT_KV_IDX_TOMBSTONE) {
|
if (GUF_DICT_META_IS_TOMBSTONE(ht->kv_indices[idx])) {
|
||||||
ht->num_tombstones -= 1;
|
ht->num_tombstones -= 1;
|
||||||
GUF_ASSERT_RELEASE(ht->num_tombstones >= 0);
|
GUF_ASSERT_RELEASE(ht->num_tombstones >= 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
GUF_ASSERT(ht->kv_elems.size <= GUF_DICT_KV_IDX_T_MAX);
|
GUF_ASSERT((GUF_DICT_HASH_T_GET_HASHFRAG(key_hash) & (GUF_DICT_KV_META_T)ht->kv_elems.size) == 0);
|
||||||
ht->kv_indices[idx].key_hash = GUF_DICT_KEY_HASH(key);
|
ht->kv_indices[idx] = GUF_DICT_HASH_T_GET_HASHFRAG(key_hash) | (GUF_DICT_KV_META_T)ht->kv_elems.size;
|
||||||
ht->kv_indices[idx].kv_idx = (GUF_DICT_KV_IDX_T)ht->kv_elems.size;
|
|
||||||
|
|
||||||
GUF_DICT_KEY_T key_cpy;
|
GUF_DICT_KEY_T key_cpy;
|
||||||
GUF_DICT_KEY_T *key_cpy_res = NULL;
|
GUF_DICT_KEY_T *key_cpy_res = NULL;
|
||||||
@ -565,7 +574,6 @@ GUF_DICT_KWRDS void GUF_CAT(GUF_DICT_NAME, _insert_val_arg)(GUF_DICT_NAME *ht, G
|
|||||||
if (!key) {
|
if (!key) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool key_exists = false;
|
bool key_exists = false;
|
||||||
const size_t idx = GUF_CAT(GUF_DICT_NAME, _find_idx)(ht, key, &key_exists);
|
const size_t idx = GUF_CAT(GUF_DICT_NAME, _find_idx)(ht, key, &key_exists);
|
||||||
if (!key_exists) {
|
if (!key_exists) {
|
||||||
@ -573,7 +581,7 @@ GUF_DICT_KWRDS void GUF_CAT(GUF_DICT_NAME, _insert_val_arg)(GUF_DICT_NAME *ht, G
|
|||||||
} else {
|
} else {
|
||||||
GUF_ASSERT(idx != SIZE_T_MAX);
|
GUF_ASSERT(idx != SIZE_T_MAX);
|
||||||
GUF_ASSERT((ptrdiff_t)idx < ht->kv_indices_cap);
|
GUF_ASSERT((ptrdiff_t)idx < ht->kv_indices_cap);
|
||||||
const size_t kv_idx = ht->kv_indices[idx].kv_idx;
|
const size_t kv_idx = GUF_DICT_META_GET_IDX(ht->kv_indices[idx]);
|
||||||
GUF_ASSERT((ptrdiff_t)kv_idx < ht->kv_elems.size);
|
GUF_ASSERT((ptrdiff_t)kv_idx < ht->kv_elems.size);
|
||||||
return &GUF_CAT(GUF_DICT_KV_DBUF, _at)(&ht->kv_elems, kv_idx)->val;
|
return &GUF_CAT(GUF_DICT_KV_DBUF, _at)(&ht->kv_elems, kv_idx)->val;
|
||||||
}
|
}
|
||||||
@ -596,8 +604,8 @@ GUF_DICT_KWRDS bool GUF_CAT(GUF_DICT_NAME, _contains)(GUF_DICT_NAME *ht, const G
|
|||||||
const size_t idx = GUF_CAT(GUF_DICT_NAME, _find_idx)(ht, key, &key_exists);
|
const size_t idx = GUF_CAT(GUF_DICT_NAME, _find_idx)(ht, key, &key_exists);
|
||||||
if (key_exists) {
|
if (key_exists) {
|
||||||
GUF_ASSERT(idx != SIZE_T_MAX);
|
GUF_ASSERT(idx != SIZE_T_MAX);
|
||||||
GUF_ASSERT(ht->kv_indices[idx].kv_idx != GUF_DICT_KV_IDX_TOMBSTONE);
|
GUF_ASSERT(!GUF_DICT_META_IS_TOMBSTONE(ht->kv_indices[idx]));
|
||||||
GUF_ASSERT(ht->kv_indices[idx].kv_idx != GUF_DICT_KV_IDX_NULL);
|
GUF_ASSERT(!GUF_DICT_META_IS_NULL(ht->kv_indices[idx]));
|
||||||
}
|
}
|
||||||
(void)idx;
|
(void)idx;
|
||||||
return key_exists;
|
return key_exists;
|
||||||
@ -622,14 +630,12 @@ GUF_DICT_KWRDS bool GUF_CAT(GUF_DICT_NAME, _erase)(GUF_DICT_NAME *ht, const GUF_
|
|||||||
if (!key_exists) {
|
if (!key_exists) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
GUF_ASSERT((ptrdiff_t)idx < ht->kv_indices_cap);
|
GUF_ASSERT(idx < SIZE_T_MAX && (ptrdiff_t)idx < ht->kv_indices_cap);
|
||||||
|
|
||||||
const size_t kv_idx = ht->kv_indices[idx].kv_idx;
|
const size_t kv_idx = (size_t)GUF_DICT_META_GET_IDX(ht->kv_indices[idx]);
|
||||||
GUF_ASSERT(kv_idx < (size_t)ht->kv_elems.size);
|
GUF_ASSERT(kv_idx < (size_t)ht->kv_elems.size);
|
||||||
|
|
||||||
ht->kv_indices[idx].kv_idx = GUF_DICT_KV_IDX_TOMBSTONE;
|
ht->kv_indices[idx] = GUF_DICT_KV_META_IDX_TOMBSTONE;
|
||||||
ht->kv_indices[idx].key_hash = 0;
|
|
||||||
|
|
||||||
ht->num_tombstones += 1;
|
ht->num_tombstones += 1;
|
||||||
|
|
||||||
GUF_DICT_KV_NAME *kv = GUF_CAT(GUF_DICT_KV_DBUF, _at)(&ht->kv_elems, kv_idx);
|
GUF_DICT_KV_NAME *kv = GUF_CAT(GUF_DICT_KV_DBUF, _at)(&ht->kv_elems, kv_idx);
|
||||||
@ -638,12 +644,12 @@ GUF_DICT_KWRDS bool GUF_CAT(GUF_DICT_NAME, _erase)(GUF_DICT_NAME *ht, const GUF_
|
|||||||
GUF_CAT(GUF_DICT_KV_NAME, _free)(kv, NULL);
|
GUF_CAT(GUF_DICT_KV_NAME, _free)(kv, NULL);
|
||||||
|
|
||||||
if (ht->kv_elems.size > 1 && (ptrdiff_t)kv_idx != ht->kv_elems.size - 1) { // Switch last kv-elem into the erased position and update its kv-index accordingly.
|
if (ht->kv_elems.size > 1 && (ptrdiff_t)kv_idx != ht->kv_elems.size - 1) { // Switch last kv-elem into the erased position and update its kv-index accordingly.
|
||||||
|
GUF_ASSERT(kv_idx <= GUF_DICT_KV_META_IDX_MAX);
|
||||||
// 1.) Switch kv_elem.
|
// 1.) Switch kv_elem.
|
||||||
GUF_DICT_KV_NAME *last_kv = GUF_CAT(GUF_DICT_KV_DBUF, _back)(&ht->kv_elems);
|
GUF_DICT_KV_NAME *last_kv = GUF_CAT(GUF_DICT_KV_DBUF, _back)(&ht->kv_elems);
|
||||||
GUF_ASSERT(last_kv);
|
GUF_ASSERT(last_kv);
|
||||||
GUF_ASSERT(kv != last_kv);
|
GUF_ASSERT(kv != last_kv);
|
||||||
*kv = *last_kv;
|
*kv = *last_kv;
|
||||||
|
|
||||||
// GUF_ASSERT(!GUF_DICT_KEY_T_EQ(key, &last_kv->key));
|
// GUF_ASSERT(!GUF_DICT_KEY_T_EQ(key, &last_kv->key));
|
||||||
|
|
||||||
// 2.) Update kv_index.
|
// 2.) Update kv_index.
|
||||||
@ -651,10 +657,8 @@ GUF_DICT_KWRDS bool GUF_CAT(GUF_DICT_NAME, _erase)(GUF_DICT_NAME *ht, const GUF_
|
|||||||
const size_t last_idx = GUF_CAT(GUF_DICT_NAME, _find_idx)(ht, &last_kv->key, &last_key_exists);
|
const size_t last_idx = GUF_CAT(GUF_DICT_NAME, _find_idx)(ht, &last_kv->key, &last_key_exists);
|
||||||
GUF_ASSERT(last_idx != idx);
|
GUF_ASSERT(last_idx != idx);
|
||||||
GUF_ASSERT(last_key_exists && (ptrdiff_t)last_idx < ht->kv_indices_cap);
|
GUF_ASSERT(last_key_exists && (ptrdiff_t)last_idx < ht->kv_indices_cap);
|
||||||
GUF_ASSERT((ptrdiff_t)ht->kv_indices[last_idx].kv_idx == ht->kv_elems.size - 1);
|
GUF_ASSERT(GUF_DICT_META_GET_IDX(ht->kv_indices[last_idx]) == (GUF_DICT_KV_META_T)(ht->kv_elems.size - 1));
|
||||||
GUF_ASSERT(ht->kv_indices[last_idx].kv_idx != GUF_DICT_KV_IDX_TOMBSTONE && ht->kv_indices[last_idx].kv_idx != GUF_DICT_KV_IDX_NULL);
|
ht->kv_indices[last_idx] = GUF_DICT_META_GET_HASHFRAG(ht->kv_indices[last_idx]) | (GUF_DICT_KV_META_T)kv_idx;
|
||||||
GUF_ASSERT(kv_idx <= GUF_DICT_KV_IDX_T_MAX);
|
|
||||||
ht->kv_indices[last_idx].kv_idx = (GUF_DICT_KV_IDX_T)kv_idx;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ht->kv_elems.size -= 1;
|
ht->kv_elems.size -= 1;
|
||||||
@ -667,13 +671,14 @@ GUF_DICT_KWRDS bool GUF_CAT(GUF_DICT_NAME, _erase)(GUF_DICT_NAME *ht, const GUF_
|
|||||||
if (ht->kv_elems.size == 0 && ht->num_tombstones > 0) { // Optimisation: We can delete all tombstones here.
|
if (ht->kv_elems.size == 0 && ht->num_tombstones > 0) { // Optimisation: We can delete all tombstones here.
|
||||||
ptrdiff_t del_tombstone_cnt = 0;
|
ptrdiff_t del_tombstone_cnt = 0;
|
||||||
for (ptrdiff_t i = 0; i < ht->kv_indices_cap && del_tombstone_cnt < ht->num_tombstones; ++i) {
|
for (ptrdiff_t i = 0; i < ht->kv_indices_cap && del_tombstone_cnt < ht->num_tombstones; ++i) {
|
||||||
GUF_ASSERT(ht->kv_indices[i].kv_idx == GUF_DICT_KV_IDX_TOMBSTONE || ht->kv_indices[i].kv_idx == GUF_DICT_KV_IDX_NULL);
|
const GUF_DICT_KV_META_T kv_del_idx = GUF_DICT_META_GET_IDX(ht->kv_indices[i]);
|
||||||
if (ht->kv_indices[i].kv_idx == GUF_DICT_KV_IDX_TOMBSTONE) {
|
GUF_ASSERT(GUF_DICT_META_GET_HASHFRAG(ht->kv_indices[i]) == 0);
|
||||||
ht->kv_indices[i].kv_idx = GUF_DICT_KV_IDX_NULL;
|
GUF_ASSERT(kv_del_idx == GUF_DICT_KV_META_IDX_TOMBSTONE || kv_del_idx == GUF_DICT_KV_META_IDX_NULL);
|
||||||
ht->kv_indices[i].key_hash = 0;
|
if (kv_del_idx == GUF_DICT_KV_META_IDX_TOMBSTONE) {
|
||||||
|
ht->kv_indices[i] = GUF_DICT_KV_META_IDX_NULL;
|
||||||
++del_tombstone_cnt;
|
++del_tombstone_cnt;
|
||||||
} else {
|
} else {
|
||||||
GUF_ASSERT(ht->kv_indices[i].kv_idx == GUF_DICT_KV_IDX_NULL);
|
GUF_ASSERT(kv_del_idx == GUF_DICT_KV_META_IDX_NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GUF_ASSERT(del_tombstone_cnt == ht->num_tombstones);
|
GUF_ASSERT(del_tombstone_cnt == ht->num_tombstones);
|
||||||
@ -813,15 +818,21 @@ GUF_DICT_KWRDS GUF_CAT(GUF_DICT_NAME, _iter) GUF_CAT(GUF_DICT_NAME, _find_val_if
|
|||||||
|
|
||||||
#endif /* end GUF_IMPL/GUF_IMPL_STATIC */
|
#endif /* end GUF_IMPL/GUF_IMPL_STATIC */
|
||||||
|
|
||||||
#undef GUF_DICT_KV_IDX_NULL
|
|
||||||
#undef GUF_DICT_KV_IDX_TOMBSTONE
|
|
||||||
#undef GUF_DICT_32_BIT
|
|
||||||
#undef GUF_DICT_64_BIT
|
|
||||||
|
|
||||||
#undef GUF_DICT_KV_IDX_T
|
|
||||||
#undef GUF_DICT_KV_IDX_T_MAX
|
|
||||||
#undef GUF_DICT_KV_META_T
|
#undef GUF_DICT_KV_META_T
|
||||||
#undef GUF_DICT_MAX_PTR
|
|
||||||
|
#undef GUF_DICT_32_BIT_HASH
|
||||||
|
#undef GUF_DICT_64_BIT_HASH
|
||||||
|
#undef GUF_DICT_64_BIT_IDX
|
||||||
|
#undef GUF_DICT_HASH_T_GET_HASHFRAG
|
||||||
|
#undef GUF_DICT_KV_META_HASHFRAG_MASK
|
||||||
|
#undef GUF_DICT_KV_META_IDX_MASK
|
||||||
|
#undef GUF_DICT_KV_META_IDX_NULL
|
||||||
|
#undef GUF_DICT_KV_META_IDX_TOMBSTONE
|
||||||
|
#undef GUF_DICT_KV_META_IDX_MAX
|
||||||
|
#undef GUF_DICT_META_GET_IDX
|
||||||
|
#undef GUF_DICT_META_GET_HASHFRAG
|
||||||
|
#undef GUF_DICT_META_IS_NULL
|
||||||
|
#undef GUF_DICT_META_IS_TOMBSTONE
|
||||||
|
|
||||||
#undef GUF_DICT_NAME
|
#undef GUF_DICT_NAME
|
||||||
#undef GUF_DICT_IS_SET
|
#undef GUF_DICT_IS_SET
|
||||||
|
|||||||
@ -141,6 +141,12 @@ static inline bool guf_size_calc_safe(ptrdiff_t count, ptrdiff_t sizeof_elem, pt
|
|||||||
return is_safe;
|
return is_safe;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// cf. https://graphics.stanford.edu/~seander/bithacks.html#DetermineIfPowerOf2 (last-retrieved 2025-03-19)
|
||||||
|
static inline bool guf_is_pow2_u8(uint8_t x) { return x && !(x & (x - 1)); }
|
||||||
|
static inline bool guf_is_pow2_u16(uint16_t x) { return x && !(x & (x - 1)); }
|
||||||
|
static inline bool guf_is_pow2_u32(uint32_t x) { return x && !(x & (x - 1)); }
|
||||||
|
static inline bool guf_is_pow2_u64(uint64_t x) { return x && !(x & (x - 1)); }
|
||||||
|
static inline bool guf_is_pow2_size_t(size_t x) { return x && !(x & (x - 1)); }
|
||||||
|
|
||||||
static bool guf_nearly_zero_f32(float x, float eps) {return fabsf(x) <= eps;}
|
static bool guf_nearly_zero_f32(float x, float eps) {return fabsf(x) <= eps;}
|
||||||
static bool guf_nearly_one_f32(float x, float eps) {return fabsf(x - 1) <= eps;}
|
static bool guf_nearly_one_f32(float x, float eps) {return fabsf(x - 1) <= eps;}
|
||||||
|
|||||||
@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
static inline guf_hash_size_t int32_hash(const int32_t *a)
|
static inline guf_hash_size_t int32_hash(const int32_t *a)
|
||||||
{
|
{
|
||||||
return guf_hash(a, sizeof(int32_t), GUF_HASH_INIT);
|
return guf_hash(a, sizeof(int32_t), GUF_HASH_INIT); // TODO: byte order...
|
||||||
}
|
}
|
||||||
static inline bool int32_eq(const int32_t *a, const int32_t *b)
|
static inline bool int32_eq(const int32_t *a, const int32_t *b)
|
||||||
{
|
{
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user