45#if defined(HMAP_DEBUG)
46# define HMAP_LOG(...) DBG("HMAP", __VA_ARGS__)
72 return (
void *)(((uintptr_t)(ptr)) | (uintptr_t)1);
76#if !defined(SERENE_TESTS)
94 if (a ==
nullptr || b ==
nullptr || (a->
len != b->
len)) {
98 if (a->
len == 0 && b->
len == 0) {
113 assert(ctl !=
nullptr);
114 assert(hmap !=
nullptr);
115 assert(hmap->
map_ctx !=
nullptr);
118 while (
n !=
nullptr) {
120 if (existing_kv->
hash == hash && (
int)ctl->
cmp(hmap, existing_kv->
k, k)) {
160 return (node->
datamap & bitpos) != 0;
165 return (node->
nodemap & bitpos) != 0;
188 assert(k !=
nullptr);
197 assert(ctx !=
nullptr);
201 n->entries =
nullptr;
226 for (
size_t i = 0; i < count; ++i) {
227 new_entries[i] = entries[i];
236 assert(kv1 !=
nullptr);
237 assert(kv2 !=
nullptr);
255 size_t idx1 = (bit1 < bit2) ? 0 : 1;
256 size_t idx2 = 1 - idx1;
306 hmap_key_t *k,
void *v, uint32_t shift,
bool *out_added
316 "Recursive insert with: (bit %u) (shift %u) (has_data %s) (has_child %s)", bit, shift,
317 has_data ?
"yes" :
"no", has_child ?
"yes" :
"no"
324 if (!has_data && !has_child) {
340 for (
size_t dst = 0; dst <= count; ++dst) {
341 if (dst == insert_idx) {
342 new_node->
entries[dst] = new_kv;
357 void *ptr = node->
entries[idx];
362 HMAP_LOG(
"Case B: Hit a collision node");
381 bool match =
n->kv->hash == hash && (int)ctl->
cmp(hmap,
n->kv->k, k);
382 copy->
kv = match ? new_kv :
n->kv;
383 copy->
next =
nullptr;
390 new_head->
next = head;
391 new_head->
kv = new_kv;
401 *out_added = ((!key_exist) != 0);
409 if (existing_kv->
hash == hash && (
int)ctl->
cmp(hmap, existing_kv->
k, k)) {
410 HMAP_LOG(
"Case B: The key already exist");
418 new_node->
entries[idx] = new_kv;
432 HMAP_LOG(
"Case B: Partial collision at maximum depth");
435 c1->
kv = existing_kv;
453 HMAP_LOG(
"Case B: Partial collision");
466 new_node->
datamap = new_datamap;
467 new_node->
nodemap = new_nodemap;
472 for (
size_t i = 0; i < count; ++i) {
476 new_node->
entries[idx] = sub_node;
488 bool child_added =
false;
493 if (new_child == child) {
504 new_node->
entries[idx] = new_child;
507 *out_added = child_added;
515 assert(ctl !=
nullptr);
516 assert(hmap !=
nullptr);
517 assert(hmap->
map_ctx !=
nullptr);
523 return default_value;
527 if ((
n->kv->hash == hash) && (
int)ctl->
cmp(hmap,
n->kv->k, k)) {
532 return default_value;
537 const hmap_key_t *k, uint32_t shift,
void *default_value
539 assert(ctl !=
nullptr);
540 assert(hmap !=
nullptr);
541 assert(hmap->
map_ctx !=
nullptr);
543 HMAP_LOG(
"Looking up in for haha %x at shift %u", hash, shift);
545 if (node ==
nullptr) {
546 return default_value;
561 if (
kv->
hash == hash && (
int)ctl->
cmp(hmap,
kv->
k, k)) {
567 return default_value;
578 return default_value;
610 HMAP_LOG(
"Trying to insert a key with the hash: %x", hash);
612 if (hmap->
root ==
nullptr) {
614 HMAP_LOG(
"First insertion on an empty map");
619 new_map.
len = hmap->
len + 1;
622 HMAP_LOG(
"Find the correct node to insert the key");
627 new_map.
root = new_root;
629 HMAP_LOG(
"The key was added successfully");
630 new_map.
len = hmap->
len + 1;
632 HMAP_LOG(
"The key existed, just updated the value");
647 if (hmap->
root ==
nullptr) {
648 return default_value;
657 hmap_t map = {.len = 0, .root =
nullptr, .map_ctx = ctx};
#define ALLOCN(ctx, T, N)
srn_hash_t srn_hash(const srn_engine_t *engine, const void *data, size_t len)
static hmap_node_t * hmap_merge_two_kv(const srn_context_t *ctx, hmap_kv_entry_t *kv1, hmap_kv_entry_t *kv2, uint32_t shift)
Merge two distinct kv entries into a sub-trie.
static void * tag_ptr(void *ptr)
static bool hmap_has_data_at(const hmap_node_t *node, hmap_bitmap_t bitpos)
Is this bitpos occupied by a data (kv) entry?
static bool hmap_key_equal(const hmap_key_t *a, const hmap_key_t *b)
Compare key a with key b.
static uint32_t hmap_hash_fragment(hmap_hash_t hash, uint8_t shift)
Extract a HMAP_MAK-bit chunk from the hash at the given shift.
hmap_t hmap_insert(const hmap_t *hmap, hmap_key_t *k, void *v)
Insert the given key k with the value v in the given hash hmap and return the new map.
hmap_hash_t hmap_hash(const srn_context_t *ctx, const void *data, size_t len)
This is a simple hack to mock the hash function during tests to force a high collision hashing functi...
static void * hmap_get_entry_at(const hmap_node_t *node, hmap_bitmap_t bitpos)
Make sure the result with is_collision_node and case the result to either an entry or a collision nod...
static bool hmap_has_node_at(const hmap_node_t *node, hmap_bitmap_t bitpos)
Is this bitpos occupied by a child node?
static bool is_collision_node(void *p)
Return true if the pointer's LSB is 1 indicating a collision.
static hmap_kv_entry_t * hmap_new_kv_entry(const srn_context_t *ctx, hmap_hash_t hash, hmap_key_t *k, void *v)
Abstract away the kv entry allocation.
static bool hmap_internal_cmp(const hmap_t *hmap, const hmap_key_t *a, const hmap_key_t *b)
static hmap_node_t * create_empty_node(const srn_context_t *ctx)
static hmap_collision_t * untag_ptr(void *ptr)
Since for non collision nodes the LSB is 0 then we don't need to untag it.
static size_t hmap_popcount(hmap_bitmap_t bm)
static hmap_node_t * hmap_singleton_node(const srn_context_t *ctx, hmap_hash_t hash, uint32_t shift, hmap_kv_entry_t *kv)
static void * hmap_lookup_in_collision(const hmap_control_t *ctl, const hmap_t *hmap, hmap_collision_t *cnode, hmap_hash_t hash, const hmap_key_t *k, void *default_value)
hmap_t hmap_empty(const srn_context_t *ctx)
Create, initialize and return a new hashmap pinned to ctx.
static hmap_node_t * hmap_get_child_at(const hmap_node_t *node, hmap_bitmap_t bitpos)
Fetch the child node pointer at a given bitpos; we must ensure nodemap has this bit.
static size_t hmap_number_of_entries(const hmap_node_t *node)
Number of entries (data + node) in a node regardless of a data pointer or a node pointer.
hmap_key_t * hmap_make_key(srn_context_t *ctx, void *data, size_t len)
Create a new key out of the given data, with the given len.
void * hmap_lookup_ctl(const hmap_control_t *ctl, const hmap_t *hmap, const hmap_key_t *k, void *default_value)
Just like the hmap_lookup function but, it receives a hmap_control_t to customize the comparison and ...
static hmap_data_t * hmap_copy_entries(const srn_context_t *ctx, hmap_data_t *entries, size_t count)
Copy entries array into a new one, returning pointer.
hmap_t hmap_insert_ctl(const hmap_control_t *ctl, const hmap_t *hmap, hmap_key_t *k, void *v)
Just like the hmap_insert function but, it receives a hmap_control_t to customize the comparison and ...
static bool is_key_in_collision_node(const hmap_control_t *ctl, const hmap_t *hmap, hmap_collision_t *head, hmap_hash_t hash, hmap_key_t *k)
static hmap_node_t * hmap_insert_node(const hmap_control_t *ctl, const hmap_t *hmap, const hmap_node_t *node, hmap_hash_t hash, hmap_key_t *k, void *v, uint32_t shift, bool *out_added)
Recursively insert a new node containing the k and v.
static bool hmap_at_max_depth(uint32_t shift)
static hmap_hash_t hmap_hash_internal(const hmap_t *hmap, const hmap_key_t *k)
static size_t hmap_entry_index(const hmap_node_t *node, hmap_bitmap_t bitpos)
Index in the packed entries array for a given bitpos (either datamap or nodemap).
static void * hmap_lookup_in_node(const hmap_control_t *ctl, const hmap_t *hmap, const hmap_node_t *node, hmap_hash_t hash, const hmap_key_t *k, uint32_t shift, void *default_value)
static hmap_bitmap_t hmap_bitpos(hmap_hash_t hash, uint8_t shift)
Bit position corresponding to this fragment.
hmap_key_t * hmap_copy_key(const hmap_t *hmap, const hmap_key_t *k)
The default copy_key hook.
void * hmap_lookup(const hmap_t *hmap, const hmap_key_t *k, void *default_value)
Lookup the given k in the given hmap and return the value if it's been found.
hmap_control_t hmap_default_control
This is an implementation of Compressed Hash-Array Mapped Prefix-tree, which is a bit-partitioned,...
#define HMAP_HASH_SIZE
By default we use 32bit hashes.
HMAP_BITMAP_TYPE hmap_bitmap_t
HMAP_HASH_TYPE hmap_hash_t
A simple alist to manage collision nodes.
struct hmap_collision_t * next
If we ever want to modify some of these behaviours for a new instance of hashmap, we should use this ...
hmap_key_t *(* copy_key)(const hmap_t *hmap, const hmap_key_t *k)
Clone k into the map's map_ctx before the map retains it, so the map never holds a pointer into the i...
bool(* cmp)(const hmap_t *hmap, const hmap_key_t *a, const hmap_key_t *b)
hmap_hash_t(* hash)(const hmap_t *hmap, const hmap_key_t *k)
Note: For key equality we use the memcpy function.
void * data
len 0 -> data == nullptr
hmap_data_t * entries
This is really a (void **) which each entry in the array will be either (mutually exclusive):
const srn_context_t * map_ctx
The context that owns every allocation the map retains.
size_t len
Number of key/value pairs in the map.
srn_engine_t * engine
Long term state of the compiler.
#define PANIC_IF_NULL(ptr)
#define PANIC_IF(cond, msg)
char * srn_copy_bytes(const srn_context_t *ctx, const char *src, size_t len, size_t alignment)
static uint32_t srn_popcnt32(uint32_t x)