857 lines
35 KiB
C
Executable File
857 lines
35 KiB
C
Executable File
#if defined(GUF_DICT_IMPL_STATIC)
|
|
#define GUF_DICT_KWRDS static
|
|
#else
|
|
#define GUF_DICT_KWRDS
|
|
#endif
|
|
|
|
#ifndef GUF_DICT_H
|
|
#define GUF_DICT_H
|
|
#include "guf_common.h"
|
|
#include "guf_alloc.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
|
|
|
|
#ifndef GUF_DICT_KEY_T
|
|
#error "Undefined container template GUF_DICT_KEY_T"
|
|
#endif
|
|
|
|
#ifndef GUF_DICT_KEY_HASH
|
|
#error "Undefined container template GUF_DICT_KEY_HASH"
|
|
#endif
|
|
|
|
#if !defined(GUF_DICT_KEY_T_EQ) && !defined(GUF_DICT_KEY_T_IS_INTEGRAL_TYPE)
|
|
#error "Undefined container template GUF_DICT_KEY_T_EQ"
|
|
#endif
|
|
|
|
#ifndef GUF_DICT_VAL_T
|
|
#define GUF_DICT_IS_SET
|
|
#endif
|
|
|
|
#if defined(GUF_DICT_32_BIT)
|
|
#define GUF_DICT_KV_IDX_T uint32_t
|
|
#define GUF_DICT_KV_META_T guf_dict_kv_meta_32
|
|
#define GUF_DICT_KV_IDX_NULL UINT32_MAX
|
|
#elif defined(GUF_DICT_64_BIT)
|
|
#define GUF_DICT_KV_IDX_T uint64_t
|
|
#define GUF_DICT_KV_META_T guf_dict_kv_meta_64
|
|
#define GUF_DICT_KV_IDX_NULL UINT64_MAX
|
|
#else
|
|
#define GUF_DICT_KV_IDX_T guf_hash_size_t
|
|
#define GUF_DICT_KV_META_T guf_dict_kv_meta
|
|
#define GUF_DICT_KV_IDX_NULL GUF_HASH_MAX
|
|
#endif
|
|
|
|
#define GUF_DICT_KV_IDX_TOMBSTONE (GUF_DICT_KV_IDX_NULL - 1)
|
|
|
|
#if PTRDIFF_MAX <= SIZE_T_MAX
|
|
#define GUF_DICT_MAX_PTR PTRDIFF_MAX
|
|
#else
|
|
#define GUF_DICT_MAX_PTR SIZE_T_MAX
|
|
#endif
|
|
|
|
#if (GUF_DICT_KV_IDX_TOMBSTONE - 1) <= GUF_DICT_MAX_PTR
|
|
#define GUF_DICT_KV_IDX_T_MAX (GUF_DICT_KV_IDX_TOMBSTONE - 1)
|
|
#else
|
|
#define GUF_DICT_KV_IDX_T_MAX GUF_DICT_MAX_PTR
|
|
#endif
|
|
|
|
// #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
|
|
#define GUF_DICT_NAME GUF_CAT(dict_, GUF_CAT(GUF_DICT_KEY_T, GUF_CAT(_to_, GUF_DICT_VAL_T)))
|
|
#endif
|
|
#ifndef GUF_DICT_KV_NAME
|
|
#define GUF_DICT_KV_NAME GUF_CAT(GUF_DICT_NAME, _kv)
|
|
#endif
|
|
|
|
#define GUF_DICT_KV_DBUF GUF_CAT(GUF_DICT_KV_NAME, _dbuf)
|
|
|
|
// - GUF_T_COPY: cpy function with signature GUF_T *copy(GUF_T *dst, const GUF_T *src, void *ctx) (default: copy by value)
|
|
// - GUF_T_MOVE: move function with signature GUF_T *move(GUF_T *dst, GUF_T *src, void *ctx) (default: undefined)
|
|
// - GUF_T_FREE: free function with signature void free(GUF_T *a, void *ctx) (default: undefined)
|
|
// - GUF_T_EQ: equality function with signature bool eq(const GUF_T *a, const GUF_T *a) (default: undefined, or equality by value if GUF_T_IS_INTEGRAL_TYPE is defined)
|
|
|
|
#ifndef GUF_DICT_IMPL
|
|
|
|
typedef struct GUF_DICT_KV_NAME {
|
|
GUF_DICT_KEY_T key;
|
|
#ifdef GUF_DICT_VAL_T
|
|
GUF_DICT_VAL_T val;
|
|
#endif
|
|
} GUF_DICT_KV_NAME;
|
|
|
|
#define GUF_T GUF_DICT_KV_NAME
|
|
#define GUF_DBUF_NAME GUF_DICT_KV_DBUF
|
|
#define GUF_DBUF_ONLY_TYPES
|
|
#include "guf_dbuf.h"
|
|
|
|
typedef struct GUF_DICT_NAME {
|
|
GUF_DICT_KV_DBUF kv_elems; // The actual key-value elements (contiguous in memory)
|
|
GUF_DICT_KV_META_T *kv_indices; // Indices into the kv_elems dbuf.
|
|
ptrdiff_t kv_indices_cap, num_tombstones;
|
|
ptrdiff_t max_probelen; // Stores the worst-case probe-length (for performance measurement)
|
|
} GUF_DICT_NAME;
|
|
|
|
typedef GUF_CAT(GUF_DICT_KV_DBUF, _iter) GUF_CAT(GUF_DICT_NAME, _iter);
|
|
|
|
#endif
|
|
|
|
GUF_DICT_KWRDS GUF_DICT_NAME *GUF_CAT(GUF_DICT_NAME, _try_init)(GUF_DICT_NAME *ht, guf_allocator *alloc, guf_err *err);
|
|
GUF_DICT_KWRDS GUF_DICT_NAME *GUF_CAT(GUF_DICT_NAME, _init)(GUF_DICT_NAME *ht, guf_allocator *alloc);
|
|
|
|
GUF_DICT_KWRDS void GUF_CAT(GUF_DICT_NAME, _free)(GUF_DICT_NAME *ht, void *ctx);
|
|
|
|
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, _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_DICT_KWRDS void GUF_CAT(GUF_DICT_NAME, _try_insert_val_arg)(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, _insert_val_arg)(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_DICT_KWRDS bool GUF_CAT(GUF_DICT_NAME, _erase)(GUF_DICT_NAME *ht, const GUF_DICT_KEY_T *key);
|
|
GUF_DICT_KWRDS bool GUF_CAT(GUF_DICT_NAME, _erase_val_arg)(GUF_DICT_NAME *ht, GUF_DICT_KEY_T key);
|
|
|
|
#ifdef GUF_DICT_VAL_T
|
|
GUF_DICT_KWRDS GUF_DICT_VAL_T *GUF_CAT(GUF_DICT_NAME, _at)(GUF_DICT_NAME *ht, const GUF_DICT_KEY_T *key);
|
|
GUF_DICT_KWRDS GUF_DICT_VAL_T *GUF_CAT(GUF_DICT_NAME, _at_val_arg)(GUF_DICT_NAME *ht, GUF_DICT_KEY_T key);
|
|
#endif
|
|
|
|
GUF_DICT_KWRDS bool GUF_CAT(GUF_DICT_NAME, _contains)(GUF_DICT_NAME *ht, const GUF_DICT_KEY_T *key);
|
|
GUF_DICT_KWRDS bool GUF_CAT(GUF_DICT_NAME, _contains_val_arg)(GUF_DICT_NAME *ht, GUF_DICT_KEY_T key);
|
|
|
|
GUF_DICT_KWRDS ptrdiff_t GUF_CAT(GUF_DICT_NAME, _size)(const GUF_DICT_NAME *ht);
|
|
GUF_DICT_KWRDS double GUF_CAT(GUF_DICT_NAME, _load_factor)(const GUF_DICT_NAME *ht);
|
|
|
|
/* Iterator functions */
|
|
GUF_DICT_KWRDS GUF_CAT(GUF_DICT_NAME, _iter) GUF_CAT(GUF_DICT_NAME, _begin)(const GUF_DICT_NAME* ht);
|
|
GUF_DICT_KWRDS GUF_CAT(GUF_DICT_NAME, _iter) GUF_CAT(GUF_DICT_NAME, _end)(const GUF_DICT_NAME* ht);
|
|
GUF_DICT_KWRDS GUF_CAT(GUF_DICT_NAME, _iter) GUF_CAT(GUF_DICT_NAME, _rbegin)(const GUF_DICT_NAME* ht);
|
|
GUF_DICT_KWRDS GUF_CAT(GUF_DICT_NAME, _iter) GUF_CAT(GUF_DICT_NAME, _rend)(const GUF_DICT_NAME* ht);
|
|
GUF_DICT_KWRDS bool GUF_CAT(GUF_DICT_NAME, _iter_is_end)(const GUF_DICT_NAME* ht, GUF_CAT(GUF_DICT_NAME, _iter) it);
|
|
|
|
GUF_DICT_KWRDS GUF_CAT(GUF_DICT_NAME, _iter) GUF_CAT(GUF_DICT_NAME, _iter_next)(const GUF_DICT_NAME *ht, GUF_CAT(GUF_DICT_NAME, _iter) it, ptrdiff_t step);
|
|
GUF_DICT_KWRDS GUF_CAT(GUF_DICT_NAME, _iter) GUF_CAT(GUF_DICT_NAME, _iter_at_idx)(const GUF_DICT_NAME *ht, ptrdiff_t idx);
|
|
GUF_DICT_KWRDS GUF_CAT(GUF_DICT_NAME, _iter) GUF_CAT(GUF_DICT_NAME, _reverse_iter_at_idx)(const GUF_DICT_NAME *ht, ptrdiff_t idx);
|
|
GUF_DICT_KWRDS ptrdiff_t GUF_CAT(GUF_DICT_NAME, _iter_to_idx)(const GUF_DICT_NAME *ht, GUF_CAT(GUF_DICT_NAME, _iter) it);
|
|
|
|
#if defined(GUF_DICT_VAL_T) && (defined(GUF_DICT_VAL_T_EQ) || defined(GUF_DICT_VAL_T_IS_INTEGRAL_TYPE))
|
|
GUF_DICT_KWRDS GUF_CAT(GUF_DICT_NAME, _iter) GUF_CAT(GUF_DICT_NAME, _find_val)(GUF_DICT_NAME *ht, GUF_CAT(GUF_DICT_NAME, _iter) begin, GUF_CAT(GUF_DICT_NAME, _iter) end, const GUF_DICT_VAL_T *needle_val);
|
|
GUF_DICT_KWRDS GUF_CAT(GUF_DICT_NAME, _iter) GUF_CAT(GUF_DICT_NAME, _find_val_val_arg)(GUF_DICT_NAME *ht, GUF_CAT(GUF_DICT_NAME, _iter) begin, GUF_CAT(GUF_DICT_NAME, _iter) end, GUF_DICT_VAL_T needle_val);
|
|
#endif
|
|
#if defined(GUF_DICT_VAL_T)
|
|
GUF_DICT_KWRDS GUF_CAT(GUF_DICT_NAME, _iter) GUF_CAT(GUF_DICT_NAME, _find_val_if)(GUF_DICT_NAME *ht, GUF_CAT(GUF_DICT_NAME, _iter) begin, GUF_CAT(GUF_DICT_NAME, _iter) end, bool (*predicate)(const GUF_DICT_VAL_T *));
|
|
#endif
|
|
|
|
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, _valid)(const GUF_DICT_NAME *ht);
|
|
GUF_DICT_KWRDS bool GUF_CAT(GUF_DICT_NAME, _max_capacity)(void);
|
|
|
|
// #define GUF_DICT_IMPL /* DEBUGGGGGGGGG */
|
|
|
|
#if defined(GUF_DICT_IMPL) || defined(GUF_DICT_IMPL_STATIC)
|
|
|
|
#include "guf_assert.h"
|
|
#include "guf_math.h"
|
|
|
|
GUF_DICT_KWRDS bool GUF_CAT(GUF_DICT_NAME, _debug_valid_size)(const GUF_DICT_NAME *ht)
|
|
{
|
|
ptrdiff_t cnt = 0;
|
|
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) {
|
|
++cnt;
|
|
}
|
|
}
|
|
return cnt == ht->kv_elems.size;
|
|
}
|
|
|
|
static inline void GUF_CAT(GUF_DICT_KV_NAME, _free)(GUF_DICT_KV_NAME *kv, void *ctx)
|
|
{
|
|
(void)ctx;
|
|
#ifdef GUF_DICT_KEY_T_FREE
|
|
GUF_DICT_KEY_T_FREE(&kv->key, NULL);
|
|
#endif
|
|
#ifdef GUF_DICT_VAL_T_FREE
|
|
GUF_DICT_VAL_T_FREE(&kv->val, NULL);
|
|
#endif
|
|
#if !defined(GUF_DICT_KEY_T_FREE) && !defined(GUF_DICT_VAL_T_FREE)
|
|
(void)kv;
|
|
#endif
|
|
}
|
|
#define GUF_T GUF_DICT_KV_NAME
|
|
#define GUF_T_FREE GUF_CAT(GUF_DICT_KV_NAME, _free)
|
|
#define GUF_DBUF_NAME GUF_DICT_KV_DBUF
|
|
#define GUF_DBUF_WITHOUT_TYPES
|
|
#define GUF_DBUF_IMPL_STATIC
|
|
#include "guf_dbuf.h"
|
|
|
|
GUF_DICT_KWRDS double GUF_CAT(GUF_DICT_NAME, _load_factor)(const GUF_DICT_NAME *ht)
|
|
{
|
|
if (ht->kv_indices_cap == 0) {
|
|
return 1;
|
|
}
|
|
ptrdiff_t occupied_count = ht->kv_elems.size + ht->num_tombstones;
|
|
GUF_ASSERT(occupied_count <= ht->kv_indices_cap);
|
|
return (double)occupied_count / (double)ht->kv_indices_cap;
|
|
}
|
|
|
|
GUF_DICT_KWRDS GUF_DICT_NAME *GUF_CAT(GUF_DICT_NAME, _try_init)(GUF_DICT_NAME *ht, guf_allocator *alloc, guf_err *err)
|
|
{
|
|
if (!ht || !alloc) {
|
|
guf_err_set_or_panic(err, GUF_ERR_INVALID_ARG, GUF_ERR_MSG("in dict_try_init: ht or alloc NULL"));
|
|
return NULL;
|
|
}
|
|
|
|
ht->kv_elems = (GUF_DICT_KV_DBUF){0};
|
|
GUF_CAT(GUF_DICT_KV_DBUF, _try_init)(&ht->kv_elems, 0, alloc, err);
|
|
if (err != GUF_ERR_NONE) {
|
|
return NULL;
|
|
}
|
|
|
|
ht->kv_indices = NULL;
|
|
ht->kv_indices_cap = 0;
|
|
|
|
ht->num_tombstones = 0;
|
|
ht->max_probelen = 0;
|
|
return ht;
|
|
}
|
|
|
|
GUF_DICT_KWRDS GUF_DICT_NAME *GUF_CAT(GUF_DICT_NAME, _init)(GUF_DICT_NAME *ht, guf_allocator *alloc)
|
|
{
|
|
return GUF_CAT(GUF_DICT_NAME, _try_init)(ht, alloc, NULL);
|
|
}
|
|
|
|
GUF_DICT_KWRDS bool GUF_CAT(GUF_DICT_NAME, _valid)(const GUF_DICT_NAME *ht)
|
|
{
|
|
if (!ht) {
|
|
return false;
|
|
}
|
|
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 count_valid = ht->num_tombstones >= 0 && ht->kv_elems.size >= 0 && ((ht->kv_elems.size + ht->num_tombstones) <= ht->kv_indices_cap);
|
|
return kv_dbuf_valid && kv_meta_buf_valid && count_valid;
|
|
}
|
|
|
|
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 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);
|
|
}
|
|
|
|
|
|
GUF_DICT_KWRDS void GUF_CAT(GUF_DICT_NAME, _free)(GUF_DICT_NAME *ht, void *ctx)
|
|
{
|
|
(void)ctx;
|
|
GUF_ASSERT_RELEASE(GUF_CAT(GUF_DICT_NAME, _valid)(ht));
|
|
guf_allocator *allocator = ht->kv_elems.allocator;
|
|
|
|
if (ht->kv_indices) {
|
|
allocator->free(ht->kv_indices, ht->kv_indices_cap * sizeof(GUF_DICT_KV_META_T), allocator->ctx);
|
|
ht->kv_indices = NULL;
|
|
ht->kv_indices_cap = 0;
|
|
}
|
|
|
|
GUF_CAT(GUF_DICT_KV_DBUF, _free)(&ht->kv_elems, NULL);
|
|
|
|
ht->num_tombstones = 0;
|
|
ht->max_probelen = 0;
|
|
}
|
|
|
|
GUF_DICT_KWRDS ptrdiff_t GUF_CAT(GUF_DICT_NAME, _size)(const GUF_DICT_NAME *ht)
|
|
{
|
|
GUF_ASSERT_RELEASE(GUF_CAT(GUF_DICT_NAME, _valid)(ht));
|
|
return ht->kv_elems.size;
|
|
}
|
|
|
|
static inline size_t GUF_CAT(GUF_DICT_NAME, _probe_offset)(size_t probe_len)
|
|
{
|
|
GUF_ASSERT(probe_len > 0);
|
|
#ifdef GUF_DICT_PROBE_LINEAR
|
|
return probe_len; // 1, 2, 3, 4, 5, ...
|
|
#else
|
|
/*
|
|
Guaranteed to visit each index once for capacities which are powers of two.
|
|
cf. https://fgiesen.wordpress.com/2015/02/22/triangular-numbers-mod-2n/ (last-retrieved 2024-07-29)
|
|
*/
|
|
return probe_len * (probe_len + 1) / 2; // 1, 3, 6, 10, 15, 21 ... (starting from probe_len == 1)
|
|
#endif
|
|
}
|
|
|
|
static size_t GUF_CAT(GUF_DICT_NAME, _find_idx)(GUF_DICT_NAME *ht, const GUF_DICT_KEY_T *key, bool *key_exists)
|
|
{
|
|
if (ht->kv_indices_cap <= 0) {
|
|
*key_exists = false;
|
|
return SIZE_T_MAX;
|
|
}
|
|
const GUF_DICT_KV_IDX_T hash = GUF_DICT_KEY_HASH(key);
|
|
|
|
#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);
|
|
const size_t start_idx = idx;
|
|
size_t first_tombstone_idx = SIZE_T_MAX;
|
|
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.
|
|
do {
|
|
if (ht->kv_indices[idx].kv_idx == GUF_DICT_KV_IDX_NULL) { // 1.) Empty.
|
|
if (first_tombstone_idx != SIZE_T_MAX) {
|
|
idx = first_tombstone_idx;
|
|
}
|
|
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;
|
|
return idx;
|
|
} else if (ht->kv_indices[idx].kv_idx == GUF_DICT_KV_IDX_TOMBSTONE) { // 2.) Tombstone.
|
|
if (first_tombstone_idx == SIZE_T_MAX) {
|
|
first_tombstone_idx = idx;
|
|
}
|
|
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.
|
|
ht->max_probelen = GUF_MAX((ptrdiff_t)probe_len, ht->max_probelen);
|
|
*key_exists = true;
|
|
return idx;
|
|
} else { // 4.) Have to probe due to hash-collision/tombstone.
|
|
probe:
|
|
++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.
|
|
GUF_ASSERT((ptrdiff_t)probe_len <= (ht->kv_elems.size + ht->num_tombstones));
|
|
}
|
|
} while (idx != start_idx && probe_len < (size_t)ht->kv_indices_cap);
|
|
|
|
*key_exists = false;
|
|
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);
|
|
GUF_ASSERT(ht->kv_indices[first_tombstone_idx].kv_idx == GUF_DICT_KV_IDX_NULL);
|
|
return first_tombstone_idx;
|
|
} else { // Failed to find an idx.
|
|
return SIZE_T_MAX;
|
|
}
|
|
#undef GUF_MOD_CAP
|
|
}
|
|
|
|
|
|
static void GUF_CAT(GUF_DICT_NAME, _try_grow_if_necessary)(GUF_DICT_NAME *ht, guf_err *err)
|
|
{
|
|
GUF_ASSERT_RELEASE(GUF_CAT(GUF_DICT_NAME, _valid)(ht));
|
|
#ifdef GUF_DICT_PROBE_LINEAR
|
|
const double MAX_LOAD_FAC = 0.6;
|
|
#else
|
|
const double MAX_LOAD_FAC = 0.5;
|
|
#endif
|
|
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.
|
|
|
|
guf_allocator *allocator = ht->kv_elems.allocator;
|
|
|
|
if (ht->kv_indices_cap == 0) { // 1.a) Allocate initial kv-index-buffer.
|
|
GUF_DICT_KV_META_T *new_kv_indices = allocator->alloc(KV_META_START_CAP * sizeof(GUF_DICT_KV_META_T), allocator->ctx);
|
|
if (new_kv_indices == NULL) {
|
|
guf_err_set_or_panic(err, GUF_ERR_ALLOC_FAIL, GUF_ERR_MSG("in function dict_try_grow: Initial allocation failed"));
|
|
return;
|
|
}
|
|
ht->kv_indices = new_kv_indices;
|
|
ht->kv_indices_cap = KV_META_START_CAP;
|
|
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].key_hash = 0;
|
|
}
|
|
} 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);
|
|
const ptrdiff_t old_size_bytes = ht->kv_indices_cap * sizeof(GUF_DICT_KV_META_T);
|
|
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
|
|
GUF_ASSERT(MAX_SIZE_BYTES % sizeof(GUF_DICT_KV_META_T) == 0 && MAX_SIZE_BYTES <= PTRDIFF_MAX);
|
|
|
|
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)"));
|
|
return;
|
|
}
|
|
|
|
// 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);
|
|
if (new_kv_indices == NULL) {
|
|
guf_err_set_or_panic(err, GUF_ERR_ALLOC_FAIL, GUF_ERR_MSG("in function dict_try_insert: re-allocation failed"));
|
|
return;
|
|
}
|
|
allocator->free(ht->kv_indices, old_size_bytes, allocator->ctx);
|
|
ht->kv_indices = new_kv_indices;
|
|
const ptrdiff_t new_indices_cap = new_size_bytes / sizeof(GUF_DICT_KV_META_T); // TODO: check
|
|
GUF_ASSERT(ht->kv_indices_cap < new_indices_cap);
|
|
ht->kv_indices_cap = new_indices_cap;
|
|
ht->num_tombstones = 0;
|
|
// ht->max_probelen = 0;
|
|
|
|
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].key_hash = 0;
|
|
}
|
|
|
|
GUF_ASSERT(ht->kv_elems.size <= GUF_DICT_KV_IDX_T_MAX);
|
|
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);
|
|
GUF_ASSERT(kv);
|
|
bool key_exists = false;
|
|
const size_t new_idx = GUF_CAT(GUF_DICT_NAME, _find_idx)(ht, &kv->key, &key_exists);
|
|
GUF_ASSERT(!key_exists);
|
|
GUF_ASSERT(new_idx < SIZE_T_MAX && new_idx <= PTRDIFF_MAX);
|
|
ht->kv_indices[new_idx].kv_idx = (GUF_DICT_KV_IDX_T)kv_idx;
|
|
ht->kv_indices[new_idx].key_hash = GUF_DICT_KEY_HASH(&kv->key); // TODO: might be expensive...
|
|
}
|
|
}
|
|
|
|
guf_err_set_if_not_null(err, GUF_ERR_NONE);
|
|
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_ASSERT_RELEASE(GUF_CAT(GUF_DICT_NAME, _valid)(ht));
|
|
|
|
if (!key || !val) {
|
|
guf_err_set_or_panic(err, GUF_ERR_INVALID_ARG, GUF_ERR_MSG("in function dict_try_insert: key or val argument is NULL"));
|
|
return;
|
|
}
|
|
|
|
if (ht->kv_elems.size >= GUF_DICT_KV_IDX_T_MAX) {
|
|
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)"));
|
|
return;
|
|
}
|
|
|
|
// 1.) Grow kv-index-buffer if neccessary (or make the initial allocation.)
|
|
GUF_CAT(GUF_DICT_NAME, _try_grow_if_necessary)(ht, err);
|
|
if (err != NULL && *err != GUF_ERR_NONE) {
|
|
guf_err_set_or_panic(err, *err, GUF_ERR_MSG("in function dict_try_insert: try_grow failed."));
|
|
return;
|
|
}
|
|
GUF_ASSERT_RELEASE(ht->kv_indices_cap > ht->kv_elems.size);
|
|
|
|
// 2.) Insert new key-value pair.
|
|
bool key_exists = false;
|
|
size_t idx = GUF_CAT(GUF_DICT_NAME, _find_idx)(ht, key, &key_exists);
|
|
if (key_exists) {
|
|
guf_err_set_or_panic(err, GUF_ERR_ALREADY_EXISTS, GUF_ERR_MSG("in function dict_try_insert: Key already exists"));
|
|
return;
|
|
}
|
|
GUF_ASSERT_RELEASE(idx < (size_t)ht->kv_indices_cap);
|
|
|
|
if (ht->kv_indices[idx].kv_idx == GUF_DICT_KV_IDX_TOMBSTONE) {
|
|
ht->num_tombstones -= 1;
|
|
GUF_ASSERT_RELEASE(ht->num_tombstones >= 0);
|
|
}
|
|
|
|
GUF_ASSERT(ht->kv_elems.size <= GUF_DICT_KV_IDX_T_MAX);
|
|
ht->kv_indices[idx].key_hash = GUF_DICT_KEY_HASH(key);
|
|
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_res = NULL;
|
|
if (key_opt == GUF_CPY_DEEP) {
|
|
#ifdef GUF_DICT_KEY_T_COPY
|
|
key_cpy_res = GUF_DICT_KEY_T_COPY(&key_cpy, key);
|
|
#else
|
|
GUF_ASSERT_RELEASE(false);
|
|
#endif
|
|
} else if (key_opt == GUF_CPY_MOVE) {
|
|
#ifdef GUF_DICT_KEY_T_MOVE
|
|
key_cpy_res = GUF_DICT_KEY_T_MOVE(&key_cpy, key);
|
|
#else
|
|
GUF_ASSERT_RELEASE(false);
|
|
#endif
|
|
} else {
|
|
key_cpy = *key;
|
|
key_cpy_res = &key_cpy;
|
|
}
|
|
if (!key_cpy_res) {
|
|
guf_err_set_or_panic(err, GUF_ERR_ALLOC_FAIL, GUF_ERR_MSG("in function dict_try_insert: Failed to copy key"));
|
|
return;
|
|
}
|
|
|
|
GUF_DICT_VAL_T val_cpy;
|
|
GUF_DICT_VAL_T *val_cpy_res = NULL;
|
|
if (val_opt == GUF_CPY_DEEP) {
|
|
#ifdef GUF_DICT_VAL_T_COPY
|
|
val_cpy_res = GUF_DICT_KEY_T_COPY(&val_cpy, val);
|
|
#else
|
|
GUF_ASSERT_RELEASE(false);
|
|
#endif
|
|
} else if (val_opt == GUF_CPY_MOVE) {
|
|
#ifdef GUF_DICT_VAL_T_MOVE
|
|
val_cpy_res = GUF_DICT_KEY_T_MOVE(&val_cpy, val);
|
|
#else
|
|
GUF_ASSERT_RELEASE(false);
|
|
#endif
|
|
} else {
|
|
val_cpy = *val;
|
|
val_cpy_res = &val_cpy;
|
|
}
|
|
if (!val_cpy_res) {
|
|
guf_err_set_or_panic(err, GUF_ERR_ALLOC_FAIL, GUF_ERR_MSG("in function dict_try_insert: Failed to copy value"));
|
|
if (key_opt == GUF_CPY_DEEP || key_opt == GUF_CPY_MOVE) {
|
|
#ifdef GUF_DICT_KEY_T_FREE
|
|
GUF_DICT_KEY_T_FREE(key_cpy_res, NULL);
|
|
#endif
|
|
}
|
|
return;
|
|
}
|
|
|
|
GUF_DICT_KV_NAME kv = {.key = key_cpy, .val = val_cpy};
|
|
GUF_CAT(GUF_DICT_KV_DBUF, _try_push_val)(&ht->kv_elems, kv, err);
|
|
|
|
if (err && *err != GUF_ERR_NONE) { // Insertion failed.
|
|
GUF_ASSERT(*err != GUF_ERR_IDX_RANGE && *err != GUF_ERR_INVALID_ARG);
|
|
#ifdef GUF_DICT_KEY_T_FREE
|
|
GUF_DICT_KEY_T_FREE(&kv.key, NULL);
|
|
#endif
|
|
#ifdef GUF_DICT_VAL_T_FREE
|
|
GUF_DICT_VAL_T_FREE(&kv.val, NULL);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
GUF_DICT_KWRDS void GUF_CAT(GUF_DICT_NAME, _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_CAT(GUF_DICT_NAME, _try_insert)(ht, key, val, key_opt, val_opt, NULL);
|
|
}
|
|
|
|
GUF_DICT_KWRDS void GUF_CAT(GUF_DICT_NAME, _try_insert_val_arg)(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_CAT(GUF_DICT_NAME, _try_insert)(ht, &key, &val, key_opt, val_opt, err);
|
|
}
|
|
|
|
GUF_DICT_KWRDS void GUF_CAT(GUF_DICT_NAME, _insert_val_arg)(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_CAT(GUF_DICT_NAME, _insert)(ht, &key, &val, key_opt, val_opt);
|
|
}
|
|
|
|
|
|
#ifdef GUF_DICT_VAL_T
|
|
GUF_DICT_KWRDS GUF_DICT_VAL_T *GUF_CAT(GUF_DICT_NAME, _at)(GUF_DICT_NAME *ht, const GUF_DICT_KEY_T *key)
|
|
{
|
|
GUF_ASSERT_RELEASE(GUF_CAT(GUF_DICT_NAME, _valid)(ht));
|
|
if (!key) {
|
|
return NULL;
|
|
}
|
|
|
|
bool key_exists = false;
|
|
const size_t idx = GUF_CAT(GUF_DICT_NAME, _find_idx)(ht, key, &key_exists);
|
|
if (!key_exists) {
|
|
return NULL;
|
|
} else {
|
|
GUF_ASSERT(idx != SIZE_T_MAX);
|
|
GUF_ASSERT((ptrdiff_t)idx < ht->kv_indices_cap);
|
|
const size_t kv_idx = ht->kv_indices[idx].kv_idx;
|
|
GUF_ASSERT((ptrdiff_t)kv_idx < ht->kv_elems.size);
|
|
return &GUF_CAT(GUF_DICT_KV_DBUF, _at)(&ht->kv_elems, kv_idx)->val;
|
|
}
|
|
}
|
|
|
|
GUF_DICT_KWRDS GUF_DICT_VAL_T *GUF_CAT(GUF_DICT_NAME, _at_val_arg)(GUF_DICT_NAME *ht, GUF_DICT_KEY_T key)
|
|
{
|
|
return GUF_CAT(GUF_DICT_NAME, _at)(ht, &key);
|
|
}
|
|
|
|
#endif
|
|
|
|
GUF_DICT_KWRDS bool GUF_CAT(GUF_DICT_NAME, _contains)(GUF_DICT_NAME *ht, const GUF_DICT_KEY_T *key)
|
|
{
|
|
GUF_ASSERT_RELEASE(GUF_CAT(GUF_DICT_NAME, _valid)(ht));
|
|
if (!key) {
|
|
return false;
|
|
}
|
|
bool key_exists = false;
|
|
const size_t idx = GUF_CAT(GUF_DICT_NAME, _find_idx)(ht, key, &key_exists);
|
|
if (key_exists) {
|
|
GUF_ASSERT(idx != SIZE_T_MAX);
|
|
GUF_ASSERT(ht->kv_indices[idx].kv_idx != GUF_DICT_KV_IDX_TOMBSTONE);
|
|
GUF_ASSERT(ht->kv_indices[idx].kv_idx != GUF_DICT_KV_IDX_NULL);
|
|
}
|
|
(void)idx;
|
|
return key_exists;
|
|
}
|
|
|
|
GUF_DICT_KWRDS bool GUF_CAT(GUF_DICT_NAME, _contains_val_arg)(GUF_DICT_NAME *ht, GUF_DICT_KEY_T key)
|
|
{
|
|
return GUF_CAT(GUF_DICT_NAME, _contains)(ht, &key);
|
|
}
|
|
|
|
|
|
GUF_DICT_KWRDS bool GUF_CAT(GUF_DICT_NAME, _erase)(GUF_DICT_NAME *ht, const GUF_DICT_KEY_T *key)
|
|
{
|
|
GUF_ASSERT_RELEASE(GUF_CAT(GUF_DICT_NAME, _valid)(ht));
|
|
|
|
if (!key || ht->kv_elems.size == 0) {
|
|
return false;
|
|
}
|
|
|
|
bool key_exists = false;
|
|
const size_t idx = GUF_CAT(GUF_DICT_NAME, _find_idx)(ht, key, &key_exists);
|
|
if (!key_exists) {
|
|
return false;
|
|
}
|
|
GUF_ASSERT((ptrdiff_t)idx < ht->kv_indices_cap);
|
|
|
|
const size_t kv_idx = ht->kv_indices[idx].kv_idx;
|
|
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].key_hash = 0;
|
|
|
|
ht->num_tombstones += 1;
|
|
|
|
GUF_DICT_KV_NAME *kv = GUF_CAT(GUF_DICT_KV_DBUF, _at)(&ht->kv_elems, kv_idx);
|
|
GUF_ASSERT(kv);
|
|
|
|
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.
|
|
// 1.) Switch kv_elem.
|
|
GUF_DICT_KV_NAME *last_kv = GUF_CAT(GUF_DICT_KV_DBUF, _back)(&ht->kv_elems);
|
|
GUF_ASSERT(last_kv);
|
|
GUF_ASSERT(kv != last_kv);
|
|
*kv = *last_kv;
|
|
|
|
// GUF_ASSERT(!GUF_DICT_KEY_T_EQ(key, &last_kv->key));
|
|
|
|
// 2.) Update kv_index.
|
|
bool last_key_exists = false;
|
|
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_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(ht->kv_indices[last_idx].kv_idx != GUF_DICT_KV_IDX_TOMBSTONE && ht->kv_indices[last_idx].kv_idx != GUF_DICT_KV_IDX_NULL);
|
|
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;
|
|
|
|
GUF_ASSERT(ht->kv_elems.size >= 0);
|
|
GUF_ASSERT(ht->num_tombstones <= ht->kv_indices_cap);
|
|
|
|
// GUF_ASSERT(!GUF_CAT(GUF_DICT_NAME, _contains)(ht, key));
|
|
|
|
if (ht->kv_elems.size == 0 && ht->num_tombstones > 0) { // Optimisation: We can delete all tombstones here.
|
|
ptrdiff_t del_tombstone_cnt = 0;
|
|
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);
|
|
if (ht->kv_indices[i].kv_idx == GUF_DICT_KV_IDX_TOMBSTONE) {
|
|
ht->kv_indices[i].kv_idx = GUF_DICT_KV_IDX_NULL;
|
|
ht->kv_indices[i].key_hash = 0;
|
|
++del_tombstone_cnt;
|
|
} else {
|
|
GUF_ASSERT(ht->kv_indices[i].kv_idx == GUF_DICT_KV_IDX_NULL);
|
|
}
|
|
}
|
|
GUF_ASSERT(del_tombstone_cnt == ht->num_tombstones);
|
|
ht->num_tombstones = 0;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
GUF_DICT_KWRDS bool GUF_CAT(GUF_DICT_NAME, _erase_val_arg)(GUF_DICT_NAME *ht, GUF_DICT_KEY_T key)
|
|
{
|
|
return GUF_CAT(GUF_DICT_NAME, _erase)(ht, &key);
|
|
}
|
|
|
|
|
|
/* Iterator functions */
|
|
|
|
GUF_DICT_KWRDS GUF_CAT(GUF_DICT_NAME, _iter) GUF_CAT(GUF_DICT_NAME, _begin)(const GUF_DICT_NAME* ht)
|
|
{
|
|
GUF_ASSERT_RELEASE(GUF_CAT(GUF_DICT_NAME, _valid)(ht));
|
|
GUF_CAT(GUF_DICT_KV_DBUF, _iter) kv_it = GUF_CAT(GUF_DICT_KV_DBUF, _begin)(&ht->kv_elems);
|
|
return (GUF_CAT(GUF_DICT_NAME, _iter)){.ptr = kv_it.ptr, .base = kv_it.base};
|
|
}
|
|
|
|
GUF_DICT_KWRDS GUF_CAT(GUF_DICT_NAME, _iter) GUF_CAT(GUF_DICT_NAME, _end)(const GUF_DICT_NAME* ht)
|
|
{
|
|
GUF_ASSERT_RELEASE(GUF_CAT(GUF_DICT_NAME, _valid)(ht));
|
|
GUF_CAT(GUF_DICT_KV_DBUF, _iter) kv_it = GUF_CAT(GUF_DICT_KV_DBUF, _end)(&ht->kv_elems);
|
|
return (GUF_CAT(GUF_DICT_NAME, _iter)){.ptr = kv_it.ptr, .base = kv_it.base};
|
|
}
|
|
|
|
GUF_DICT_KWRDS GUF_CAT(GUF_DICT_NAME, _iter) GUF_CAT(GUF_DICT_NAME, _rbegin)(const GUF_DICT_NAME* ht)
|
|
{
|
|
GUF_ASSERT_RELEASE(GUF_CAT(GUF_DICT_NAME, _valid)(ht));
|
|
GUF_CAT(GUF_DICT_KV_DBUF, _iter) kv_it = GUF_CAT(GUF_DICT_KV_DBUF, _rbegin)(&ht->kv_elems);
|
|
return (GUF_CAT(GUF_DICT_NAME, _iter)){.ptr = kv_it.ptr, .base = kv_it.base};
|
|
}
|
|
|
|
GUF_DICT_KWRDS GUF_CAT(GUF_DICT_NAME, _iter) GUF_CAT(GUF_DICT_NAME, _rend)(const GUF_DICT_NAME* ht)
|
|
{
|
|
GUF_ASSERT_RELEASE(GUF_CAT(GUF_DICT_NAME, _valid)(ht));
|
|
GUF_CAT(GUF_DICT_KV_DBUF, _iter) kv_it = GUF_CAT(GUF_DICT_KV_DBUF, _rend)(&ht->kv_elems);
|
|
return (GUF_CAT(GUF_DICT_NAME, _iter)){.ptr = kv_it.ptr, .base = kv_it.base};
|
|
}
|
|
|
|
GUF_DICT_KWRDS bool GUF_CAT(GUF_DICT_NAME, _iter_is_end)(const GUF_DICT_NAME* ht, GUF_CAT(GUF_DICT_NAME, _iter) it)
|
|
{
|
|
const bool is_reverse_it = it.base != NULL;
|
|
const GUF_CAT(GUF_DICT_NAME, _iter) dbuf_end_it = is_reverse_it ? GUF_CAT(GUF_DICT_NAME, _rend)(ht) : GUF_CAT(GUF_DICT_NAME, _end)(ht);
|
|
return it.ptr == dbuf_end_it.ptr;
|
|
}
|
|
|
|
GUF_DICT_KWRDS GUF_CAT(GUF_DICT_NAME, _iter) GUF_CAT(GUF_DICT_NAME, _iter_next)(const GUF_DICT_NAME *ht, GUF_CAT(GUF_DICT_NAME, _iter) it, ptrdiff_t step)
|
|
{
|
|
GUF_CAT(GUF_DICT_KV_DBUF, _iter) kv_it = GUF_CAT(GUF_DICT_KV_DBUF, _iter_next)(&ht->kv_elems, it, step);
|
|
return (GUF_CAT(GUF_DICT_NAME, _iter)){.ptr = kv_it.ptr, .base = kv_it.base};
|
|
}
|
|
GUF_DICT_KWRDS GUF_CAT(GUF_DICT_NAME, _iter) GUF_CAT(GUF_DICT_NAME, _iter_at_idx)(const GUF_DICT_NAME *ht, ptrdiff_t idx)
|
|
{
|
|
GUF_CAT(GUF_DICT_KV_DBUF, _iter) kv_it = GUF_CAT(GUF_DICT_KV_DBUF, _iter_at_idx)(&ht->kv_elems, idx);
|
|
return (GUF_CAT(GUF_DICT_NAME, _iter)){.ptr = kv_it.ptr, .base = kv_it.base};
|
|
}
|
|
|
|
GUF_DICT_KWRDS GUF_CAT(GUF_DICT_NAME, _iter) GUF_CAT(GUF_DICT_NAME, _reverse_iter_at_idx)(const GUF_DICT_NAME *ht, ptrdiff_t idx)
|
|
{
|
|
GUF_CAT(GUF_DICT_KV_DBUF, _iter) kv_it = GUF_CAT(GUF_DICT_KV_DBUF, _reverse_iter_at_idx)(&ht->kv_elems, idx);
|
|
return (GUF_CAT(GUF_DICT_NAME, _iter)){.ptr = kv_it.ptr, .base = kv_it.base};
|
|
}
|
|
|
|
GUF_DICT_KWRDS ptrdiff_t GUF_CAT(GUF_DICT_NAME, _iter_to_idx)(const GUF_DICT_NAME *ht, GUF_CAT(GUF_DICT_NAME, _iter) it)
|
|
{
|
|
return GUF_CAT(GUF_DICT_KV_DBUF, _iter_to_idx)(&ht->kv_elems, it);
|
|
}
|
|
|
|
#if defined(GUF_DICT_VAL_T) && (defined(GUF_DICT_VAL_T_EQ) || defined(GUF_DICT_VAL_T_IS_INTEGRAL_TYPE))
|
|
GUF_DICT_KWRDS GUF_CAT(GUF_DICT_NAME, _iter) GUF_CAT(GUF_DICT_NAME, _find_val)(GUF_DICT_NAME *ht, GUF_CAT(GUF_DICT_NAME, _iter) begin, GUF_CAT(GUF_DICT_NAME, _iter) end, const GUF_DICT_VAL_T *needle)
|
|
{
|
|
GUF_ASSERT_RELEASE(GUF_CAT(GUF_DICT_NAME, _valid)(ht));
|
|
GUF_ASSERT_RELEASE(needle);
|
|
|
|
const bool is_reverse_it = begin.base != NULL;
|
|
GUF_ASSERT_RELEASE(is_reverse_it == (end.base != NULL)); // begin and end must be the same iterator type.
|
|
const GUF_CAT(GUF_DICT_NAME, _iter) dbuf_end_it = is_reverse_it ? GUF_CAT(GUF_DICT_NAME, _rend)(ht) : GUF_CAT(GUF_DICT_NAME, _end)(ht);
|
|
|
|
if (!ht->kv_elems.data || !ht->kv_elems.size || (!begin.ptr && !begin.base) || (!end.ptr && !end.base)) {
|
|
return dbuf_end_it;
|
|
}
|
|
if ((begin.ptr == dbuf_end_it.ptr) || (!is_reverse_it && begin.ptr >= end.ptr) || (is_reverse_it && begin.base <= end.base)) {
|
|
return dbuf_end_it;
|
|
}
|
|
|
|
for (GUF_CAT(GUF_DICT_NAME, _iter) it = begin; it.ptr != end.ptr && it.ptr != NULL; it = GUF_CAT(GUF_DICT_NAME, _iter_next)(ht, it, 1)) {
|
|
#ifdef GUF_DICT_VAL_T_EQ
|
|
if (GUF_DICT_VAL_T_EQ(&(it.ptr->val), needle)) {
|
|
return it;
|
|
}
|
|
#else
|
|
if (it.ptr->val == *needle) {
|
|
return it;
|
|
}
|
|
#endif
|
|
}
|
|
return dbuf_end_it;
|
|
}
|
|
|
|
GUF_DICT_KWRDS GUF_CAT(GUF_DICT_NAME, _iter) GUF_CAT(GUF_DICT_NAME, _find_val_val_arg)(GUF_DICT_NAME *ht, GUF_CAT(GUF_DICT_NAME, _iter) begin, GUF_CAT(GUF_DICT_NAME, _iter) end, GUF_DICT_VAL_T needle)
|
|
{
|
|
return GUF_CAT(GUF_DICT_NAME, _find_val)(ht, begin, end, &needle);
|
|
}
|
|
#endif
|
|
|
|
#if defined(GUF_DICT_VAL_T)
|
|
GUF_DICT_KWRDS GUF_CAT(GUF_DICT_NAME, _iter) GUF_CAT(GUF_DICT_NAME, _find_val_if)(GUF_DICT_NAME *ht, GUF_CAT(GUF_DICT_NAME, _iter) begin, GUF_CAT(GUF_DICT_NAME, _iter) end, bool (*predicate)(const GUF_DICT_VAL_T *))
|
|
{
|
|
GUF_ASSERT_RELEASE(GUF_CAT(GUF_DICT_NAME, _valid)(ht));
|
|
GUF_ASSERT_RELEASE(predicate);
|
|
|
|
const bool is_reverse_it = begin.base != NULL;
|
|
GUF_ASSERT_RELEASE(is_reverse_it == (end.base != NULL)); // begin and end must be the same iterator type.
|
|
const GUF_CAT(GUF_DICT_NAME, _iter) dbuf_end_it = is_reverse_it ? GUF_CAT(GUF_DICT_NAME, _rend)(ht) : GUF_CAT(GUF_DICT_NAME, _end)(ht);
|
|
|
|
if (!ht->kv_elems.data || !ht->kv_elems.size || (!begin.ptr && !begin.base) || (!end.ptr && !end.base)) {
|
|
return dbuf_end_it;
|
|
}
|
|
if ((begin.ptr == dbuf_end_it.ptr) || (!is_reverse_it && begin.ptr >= end.ptr) || (is_reverse_it && begin.base <= end.base)) {
|
|
return dbuf_end_it;
|
|
}
|
|
|
|
for (GUF_CAT(GUF_DICT_NAME, _iter) it = begin; it.ptr != end.ptr && it.ptr != NULL; it = GUF_CAT(GUF_DICT_NAME, _iter_next)(ht, it, 1)) {
|
|
if (predicate(&(it.ptr->val))) {
|
|
return it;
|
|
}
|
|
}
|
|
return dbuf_end_it;
|
|
}
|
|
#endif
|
|
|
|
#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_MAX_PTR
|
|
|
|
#undef GUF_DICT_NAME
|
|
#undef GUF_DICT_IS_SET
|
|
#undef GUF_DICT_PROBE_LINEAR
|
|
#undef GUF_DICT_PROBE_QUADRATIC
|
|
|
|
#undef GUF_DICT_KEY_T
|
|
#undef GUF_DICT_KEY_T_IS_INTEGRAL_TYPE
|
|
#undef GUF_DICT_KEY_T_EQ
|
|
#undef GUF_DICT_KEY_T_FREE
|
|
#undef GUF_DICT_KEY_T_CMP
|
|
#undef GUF_DICT_KEY_T_COPY
|
|
#undef GUF_DICT_KEY_T_MOVE
|
|
#undef GUF_DICT_KEY_HASH
|
|
|
|
#undef GUF_DICT_VAL_T
|
|
#undef GUF_DICT_VAL_T_IS_INTEGRAL_TYPE
|
|
#undef GUF_DICT_VAL_T_EQ
|
|
#undef GUF_DICT_VAL_T_FREE
|
|
#undef GUF_DICT_VAL_T_CMP
|
|
#undef GUF_DICT_VAL_T_COPY
|
|
#undef GUF_DICT_VAL_T_MOVE
|
|
|
|
#undef GUF_DICT_KEY_LOOKUP_T
|
|
#undef GUF_DICT_KEY_TO_LOOKUP_KEY_CONVERT
|
|
|
|
#undef GUF_DICT_KV_NAME
|
|
#undef GUF_DICT_KV_DBUF
|
|
|
|
#undef GUF_DICT_IMPL_STATIC
|
|
#undef GUF_DICT_IMPL
|
|
#undef GUF_DICT_KWRDS
|