46#if defined(HMAP_DEBUG)
47#define HMAP_LOG(...) DBG("HMAP", __VA_ARGS__)
73 return (
void *)(((uintptr_t)(ptr)) | (uintptr_t)1);
77#if !defined(SERENE_TESTS)
95 if (a ==
nullptr || b ==
nullptr || (a->
len != b->
len)) {
99 if (a->
len == 0 && b->
len == 0) {
117 while (
n !=
nullptr) {
119 if (existing_kv->
hash == hash && (
int)ctl->
cmp(ctx, existing_kv->
k, k)) {
159 return (node->
datamap & bitpos) != 0;
164 return (node->
nodemap & bitpos) != 0;
196 assert(ctx !=
nullptr);
200 n->entries =
nullptr;
223 for (
size_t i = 0; i < count; ++i) {
224 new_entries[i] = entries[i];
273 size_t idx1 = (bit1 < bit2) ? 0 : 1;
274 size_t idx2 = 1 - idx1;
300 void *v, uint32_t shift,
bool *out_added) {
306 HMAP_LOG(
"Recursive insert with: (bit %u) (shift %u) (has_data %s) (has_child %s)", bit, shift,
307 has_data ?
"yes" :
"no", has_child ?
"yes" :
"no");
313 if (!has_data && !has_child) {
329 for (
size_t dst = 0; dst <= count; ++dst) {
330 if (dst == insert_idx) {
331 new_node->
entries[dst] = new_kv;
346 void *ptr = node->
entries[idx];
351 HMAP_LOG(
"Case B: Hit a collision node");
364 new_head->
next = head;
365 new_head->
kv = new_kv;
374 *out_added = ((!key_exist) != 0);
382 if (existing_kv->
hash == hash && (
int)ctl->
cmp(ctx, existing_kv->
k, k)) {
383 HMAP_LOG(
"Case B: The key already exist");
391 new_node->
entries[idx] = new_kv;
405 HMAP_LOG(
"Case B: Partial collision at maximum depth");
408 c1->
kv = existing_kv;
426 HMAP_LOG(
"Case B: Partial collision");
439 new_node->
datamap = new_datamap;
440 new_node->
nodemap = new_nodemap;
445 for (
size_t i = 0; i < count; ++i) {
449 new_node->
entries[idx] = sub_node;
461 bool child_added =
false;
466 if (new_child == child) {
477 new_node->
entries[idx] = new_child;
480 *out_added = child_added;
486 void *default_value) {
492 return default_value;
496 if ((
n->kv->hash == hash) && (
int)ctl->
cmp(ctx,
n->kv->k, k)) {
501 return default_value;
506 uint32_t shift,
void *default_value) {
508 HMAP_LOG(
"Looking up in for haha %x at shift %u", hash, shift);
510 if (node ==
nullptr) {
511 return default_value;
532 return default_value;
543 return default_value;
569 HMAP_LOG(
"Trying to insert a key with the hash: %x", hash);
571 if (hmap->
root ==
nullptr) {
573 HMAP_LOG(
"First insertion on an empty map");
578 new_map.
len = hmap->
len + 1;
581 HMAP_LOG(
"Find the correct node to insert the key");
586 new_map.
root = new_root;
588 HMAP_LOG(
"The key was added successfully");
589 new_map.
len = hmap->
len + 1;
591 HMAP_LOG(
"The key existed, just updated the value");
604 if (hmap->
root ==
nullptr) {
605 return default_value;
614 hmap_t map = {.len = 0, .root =
nullptr};
623 void *default_value) {
#define ALLOCN(ctx, T, N)
srn_hash_t srn_hash(const srn_engine_t *engine, const void *data, size_t len)
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.
static hmap_node_t * hmap_merge_two_kv(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 hmap_node_t * create_empty_node(srn_context_t *ctx)
hmap_t hmap_empty(srn_context_t *ctx)
Create, initialize and return a new hashmap.
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 hmap_data_t * hmap_copy_entries(srn_context_t *ctx, hmap_data_t *entries, size_t count)
Copy entries array into a new one, returning pointer.
static void * hmap_lookup_in_node(const hmap_control_t *ctl, srn_context_t *ctx, const hmap_node_t *node, hmap_hash_t hash, const hmap_key_t *k, uint32_t shift, void *default_value)
void * hmap_lookup(srn_context_t *ctx, 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.
static bool is_collision_node(void *p)
Return true if the pointer's LSB is 1 indicating a collision.
static bool is_key_in_collision_node(const hmap_control_t *ctl, srn_context_t *ctx, hmap_collision_t *head, hmap_hash_t hash, hmap_key_t *k)
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 hmap_kv_entry_t * hmap_new_kv_entry(srn_context_t *ctx, hmap_hash_t hash, hmap_key_t *k, void *v)
Abstract away the kv entry allocation.
static size_t hmap_popcount(hmap_bitmap_t bm)
hmap_hash_t hmap_hash(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...
hmap_t hmap_insert(srn_context_t *ctx, 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.
static hmap_hash_t hmap_hash_internal(srn_context_t *ctx, const hmap_key_t *k)
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.
void * hmap_lookup_ctl(const hmap_control_t *ctl, srn_context_t *ctx, 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 ...
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_in_collision(const hmap_control_t *ctl, srn_context_t *ctx, hmap_collision_t *cnode, hmap_hash_t hash, const hmap_key_t *k, void *default_value)
static bool hmap_at_max_depth(uint32_t shift)
static bool hmap_internal_cmp(srn_context_t *ctx, const hmap_key_t *a, const hmap_key_t *b)
static hmap_node_t * hmap_insert_node(const hmap_control_t *ctl, srn_context_t *ctx, 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 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 hmap_node_t * hmap_singleton_node(srn_context_t *ctx, hmap_hash_t hash, uint32_t shift, hmap_kv_entry_t *kv)
static hmap_bitmap_t hmap_bitpos(hmap_hash_t hash, uint8_t shift)
Bit position corresponding to this fragment.
hmap_t hmap_insert_ctl(const hmap_control_t *ctl, srn_context_t *ctx, 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 ...
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_hash_t(* hash)(srn_context_t *ctx, const hmap_key_t *k)
bool(* cmp)(srn_context_t *ctx, const hmap_key_t *a, const hmap_key_t *b)
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):
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)
static uint32_t srn_popcnt32(uint32_t x)