mirror of
https://github.com/Ed94/raddebugger.git
synced 2026-08-14 09:18:09 +00:00
hash map
This commit is contained in:
@@ -528,3 +528,381 @@ str8_from_key_value_pairs(Arena *arena, KeyValuePair *v, U64 count)
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
internal U64
|
||||
hash_map_hasher(String8 string)
|
||||
{
|
||||
return u64_hash_from_str8(string);
|
||||
}
|
||||
|
||||
internal HashMapNode **
|
||||
hash_map_search_(HashMap *hm, U64 hash, HashMapKey key, HashMapKeyMatchFunc *match_func, B32 *found_out)
|
||||
{
|
||||
HashMapNode **n = &hm->root;
|
||||
HashMapNode **last_tombstone = 0;
|
||||
|
||||
*found_out = 0;
|
||||
|
||||
for (U64 h = hash; *n != 0; h <<= 2) {
|
||||
if ((*n)->is_live) {
|
||||
if (match_func(&(*n)->v.key, &key)) {
|
||||
*found_out = 1;
|
||||
return n;
|
||||
}
|
||||
} else {
|
||||
last_tombstone = n;
|
||||
}
|
||||
n = &(*n)->children[h >> 62];
|
||||
}
|
||||
|
||||
if (last_tombstone != 0) {
|
||||
hm->tombstone_count -= 1;
|
||||
return last_tombstone;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
internal HashMapNode *
|
||||
hash_map_search(HashMap *hm, U64 hash, HashMapKey key, HashMapKeyMatchFunc *match_func)
|
||||
{
|
||||
B32 found = 0;
|
||||
HashMapNode **n = hash_map_search_(hm, hash, key, match_func, &found);
|
||||
return found ? *n : 0;
|
||||
}
|
||||
|
||||
internal HashMapNode *
|
||||
hash_map_push(Arena *arena, HashMap *hm, U64 hash, HashMapKeyValue key_value, HashMapKeyMatchFunc *match_func)
|
||||
{
|
||||
B32 found = 0;
|
||||
HashMapNode **n = hash_map_search_(hm, hash, key_value.key, match_func, &found);
|
||||
if (found) { return *n; }
|
||||
|
||||
if (*n == 0) {
|
||||
*n = hm->free_list;
|
||||
if (hm->free_list) { SLLStackPop(hm->free_list); }
|
||||
else { *n = push_array(arena, HashMapNode, 1); }
|
||||
}
|
||||
|
||||
(*n)->v = key_value;
|
||||
(*n)->is_live = 1;
|
||||
|
||||
hm->count += 1;
|
||||
return *n;
|
||||
}
|
||||
|
||||
internal B32
|
||||
hash_map_purge_item(HashMap *hm, U64 hash, HashMapKey key, HashMapKeyMatchFunc *match_func)
|
||||
{
|
||||
B32 found = 0;
|
||||
HashMapNode **n = hash_map_search_(hm, hash, key, match_func, &found);
|
||||
if (found) {
|
||||
(*n)->is_live = 0;
|
||||
hm->count -= 1;
|
||||
hm->tombstone_count += 1;
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
internal void
|
||||
hash_map_purge(HashMap *hm)
|
||||
{
|
||||
Temp scratch = scratch_begin(0,0);
|
||||
|
||||
typedef struct Stack {
|
||||
struct Stack *next;
|
||||
HashMapNode *node;
|
||||
U32 next_child_idx;
|
||||
} Stack;
|
||||
|
||||
Stack *free_list = 0;
|
||||
Stack *stack = 0;
|
||||
|
||||
if (hm->count > 0) {
|
||||
stack = push_array(scratch.arena, Stack, 1);
|
||||
stack->node = hm->root;
|
||||
}
|
||||
|
||||
while (stack) {
|
||||
// descend
|
||||
while (stack->next_child_idx < ArrayCount(stack->node->children)) {
|
||||
HashMapNode *child = stack->node->children[stack->next_child_idx];
|
||||
stack->next_child_idx += 1;
|
||||
|
||||
if (child) {
|
||||
Stack *f = free_list;
|
||||
if (f) { SLLStackPop(free_list); }
|
||||
else { f = push_array(scratch.arena, Stack, 1); }
|
||||
|
||||
f->node = child;
|
||||
SLLStackPush(stack, f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ascend
|
||||
if (stack->next_child_idx >= ArrayCount(stack->node->children)) {
|
||||
SLLStackPush(hm->free_list, stack->node);
|
||||
SLLStackPop(stack);
|
||||
}
|
||||
}
|
||||
|
||||
scratch_end(scratch);
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
force_inline HASH_MAP_KEY_MATCH(hash_map_match_u32) { return a->key_u32 == b->key_u32; }
|
||||
force_inline HASH_MAP_KEY_MATCH(hash_map_match_u64) { return a->key_u64 == b->key_u64; }
|
||||
force_inline HASH_MAP_KEY_MATCH(hash_map_match_raw) { return a->key_raw == b->key_raw; }
|
||||
force_inline HASH_MAP_KEY_MATCH(hash_map_match_string) { return str8_match(a->key_string, b->key_string, 0); }
|
||||
force_inline HASH_MAP_KEY_MATCH(hash_map_match_path) { return str8_match(a->key_string, b->key_string, StringMatchFlag_CaseInsensitive|StringMatchFlag_SlashInsensitive); }
|
||||
|
||||
internal U64
|
||||
hash_map_hash_from_path(String8 path)
|
||||
{
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
|
||||
String8 path_canon = path;
|
||||
path_canon = lower_from_str8(scratch.arena, path_canon);
|
||||
path_canon = path_convert_slashes(scratch.arena, path_canon, PathStyle_UnixAbsolute);
|
||||
|
||||
U64 hash = hash_map_hasher(path_canon);
|
||||
|
||||
scratch_end(scratch);
|
||||
return hash;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
internal HashMapNode *
|
||||
hash_map_push_string_string(Arena *arena, HashMap *hm, String8 key, String8 value)
|
||||
{
|
||||
return hash_map_push(arena, hm, hash_map_hasher(key), (HashMapKeyValue){ .key = { .key_string = key }, .value = { .value_string = value } }, hash_map_match_string);
|
||||
}
|
||||
|
||||
internal HashMapNode *
|
||||
hash_map_push_string_raw(Arena *arena, HashMap *hm, String8 key, void *value)
|
||||
{
|
||||
return hash_map_push(arena, hm, hash_map_hasher(key), (HashMapKeyValue){ .key = { .key_string = key }, .value = { .value_raw = value } }, hash_map_match_string);
|
||||
}
|
||||
|
||||
internal HashMapNode *
|
||||
hash_map_push_string_u64(Arena *arena, HashMap *hm, String8 key, U64 value)
|
||||
{
|
||||
return hash_map_push(arena, hm, hash_map_hasher(key), (HashMapKeyValue){ .key = { .key_string = key }, .value = { .value_u64 = value } }, hash_map_match_string);
|
||||
}
|
||||
|
||||
internal HashMapNode *
|
||||
hash_map_push_u32_raw(Arena *arena, HashMap *hm, U32 key, void *value)
|
||||
{
|
||||
return hash_map_push(arena, hm, hash_map_hasher(str8_struct(&key)), (HashMapKeyValue){ .key = { .key_u32 = key }, .value = { .value_raw = value } }, hash_map_match_u32);
|
||||
}
|
||||
|
||||
internal HashMapNode *
|
||||
hash_map_push_u32_string(Arena *arena, HashMap *hm, U32 key, String8 value)
|
||||
{
|
||||
return hash_map_push(arena, hm, hash_map_hasher(str8_struct(&key)), (HashMapKeyValue){ .key = { .key_u32 = key }, .value = { .value_string = value } }, hash_map_match_u32);
|
||||
}
|
||||
|
||||
internal HashMapNode *
|
||||
hash_map_push_u64_raw(Arena *arena, HashMap *hm, U64 key, void *value)
|
||||
{
|
||||
return hash_map_push(arena, hm, hash_map_hasher(str8_struct(&key)), (HashMapKeyValue){ .key = { .key_u64 = key }, .value = { .value_raw = value } }, hash_map_match_u64);
|
||||
}
|
||||
|
||||
internal HashMapNode *
|
||||
hash_map_push_u64_string(Arena *arena, HashMap *hm, U64 key, String8 value)
|
||||
{
|
||||
return hash_map_push(arena, hm, hash_map_hasher(str8_struct(&key)), (HashMapKeyValue){ .key = { .key_u64 = key }, .value = { .value_string = value } }, hash_map_match_u64);
|
||||
}
|
||||
|
||||
internal HashMapNode *
|
||||
hash_map_push_u64_u64(Arena *arena, HashMap *hm, U64 key, U64 value)
|
||||
{
|
||||
return hash_map_push(arena, hm, hash_map_hasher(str8_struct(&key)), (HashMapKeyValue){ .key = { .key_u64 = key }, .value = { .value_u64 = value } }, hash_map_match_u64);
|
||||
}
|
||||
|
||||
internal HashMapNode *
|
||||
hash_map_push_path_u64(Arena *arena, HashMap *hm, String8 path, U64 value)
|
||||
{
|
||||
return hash_map_push(arena, hm, hash_map_hasher(path), (HashMapKeyValue){ .key = { .key_string = path }, .value = { .value_u64 = value } }, hash_map_match_path);
|
||||
}
|
||||
|
||||
internal HashMapNode *
|
||||
hash_map_push_path_string(Arena *arena, HashMap *hm, String8 path, String8 value)
|
||||
{
|
||||
return hash_map_push(arena, hm, hash_map_hasher(path), (HashMapKeyValue){ .key = { .key_string = path }, .value = { .value_string = value } }, hash_map_match_path);
|
||||
}
|
||||
|
||||
internal HashMapNode *
|
||||
hash_map_push_path_raw(Arena *arena, HashMap *hm, String8 path, void *value)
|
||||
{
|
||||
return hash_map_push(arena, hm, hash_map_hash_from_path(path), (HashMapKeyValue){ .key = { .key_string = path }, .value = { .value_raw = value } }, hash_map_match_path);
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
internal void *
|
||||
hash_map_search_string_raw(HashMap *hm, String8 key)
|
||||
{
|
||||
HashMapNode *n = hash_map_search(hm, hash_map_hasher(key), (HashMapKey){ .key_string = key }, hash_map_match_string);
|
||||
return n ? n->v.value.value_raw : 0;
|
||||
}
|
||||
|
||||
internal U32 *
|
||||
hash_map_search_string_u32(HashMap *hm, String8 key)
|
||||
{
|
||||
HashMapNode *n = hash_map_search(hm, hash_map_hasher(key), (HashMapKey){ .key_string = key }, hash_map_match_string);
|
||||
return n ? &n->v.value.value_u32 : 0;
|
||||
}
|
||||
|
||||
internal U64 *
|
||||
hash_map_search_string_u64(HashMap *hm, String8 key)
|
||||
{
|
||||
HashMapNode *n = hash_map_search(hm, hash_map_hasher(key), (HashMapKey){ .key_string = key }, hash_map_match_string);
|
||||
return n ? &n->v.value.value_u64 : 0;
|
||||
}
|
||||
|
||||
internal void *
|
||||
hash_map_search_path_raw(HashMap *hm, String8 key)
|
||||
{
|
||||
HashMapNode *n = hash_map_search(hm, hash_map_hash_from_path(key), (HashMapKey){ .key_string = key }, hash_map_match_path);
|
||||
return n ? n->v.value.value_raw : 0;
|
||||
}
|
||||
|
||||
internal void *
|
||||
hash_map_search_u64_raw(HashMap *hm, U64 key)
|
||||
{
|
||||
HashMapNode *n = hash_map_search(hm, hash_map_hasher(str8_struct(&key)), (HashMapKey){ .key_u64 = key }, hash_map_match_u64);
|
||||
return n ? n->v.value.value_raw : 0;
|
||||
}
|
||||
|
||||
internal U64 *
|
||||
hash_map_search_u64_u64(HashMap *hm, U64 key)
|
||||
{
|
||||
HashMapNode *n = hash_map_search(hm, hash_map_hasher(str8_struct(&key)), (HashMapKey){ .key_u64 = key }, hash_map_match_u64);
|
||||
return n ? &n->v.value.value_u64 : 0;
|
||||
}
|
||||
|
||||
internal void *
|
||||
hash_map_search_raw_raw(HashMap *hm, void *key)
|
||||
{
|
||||
HashMapNode *n = hash_map_search(hm, hash_map_hasher(str8_struct(&key)), (HashMapKey){ .key_raw = key }, hash_map_match_raw);
|
||||
return n ? n->v.value.value_raw : 0;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
internal B32 hash_map_purge_u32(HashMap *hm, U32 key) { return hash_map_purge_item(hm, hash_map_hasher(str8_struct(&key)), (HashMapKey){ .key_u32 = key }, hash_map_match_u32); }
|
||||
internal B32 hash_map_purge_u64(HashMap *hm, U64 key) { return hash_map_purge_item(hm, hash_map_hasher(str8_struct(&key)), (HashMapKey){ .key_u64 = key }, hash_map_match_u64); }
|
||||
internal B32 hash_map_purge_string(HashMap *hm, String8 key) { return hash_map_purge_item(hm, hash_map_hasher(key), (HashMapKey){ .key_string = key }, hash_map_match_string); }
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
#define HASH_MAP_EXTRACT_FUNC(name) void name(HashMapKeyValue *kv, void *buffer)
|
||||
typedef HASH_MAP_EXTRACT_FUNC(HashMapExtractFunc);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
void *keys;
|
||||
void *values;
|
||||
} HashMapExtract;
|
||||
|
||||
internal HashMapExtract
|
||||
hash_map_extract(Arena *arena,
|
||||
HashMap *hm,
|
||||
HashMapExtractFunc *key_func, U64 key_size,
|
||||
HashMapExtractFunc *value_func, U64 value_size)
|
||||
{
|
||||
Temp scratch = scratch_begin(&arena, 1);
|
||||
|
||||
U64 key_offset = 0;
|
||||
U64 value_offset = 0;
|
||||
U8 *key_buffer = 0;
|
||||
U8 *value_buffer = 0;
|
||||
if (key_func && key_size) { key_buffer = push_array_no_zero(arena, U8, key_size * hm->count); }
|
||||
if (value_func && value_size) { value_buffer = push_array_no_zero(arena, U8, value_size * hm->count); }
|
||||
|
||||
typedef struct Stack {
|
||||
struct Stack *next;
|
||||
HashMapNode *node;
|
||||
U32 next_child_idx;
|
||||
} Stack;
|
||||
|
||||
Stack *free_list = 0;
|
||||
Stack *stack = 0;
|
||||
|
||||
if (hm->count > 0) {
|
||||
stack = push_array(scratch.arena, Stack, 1);
|
||||
stack->node = hm->root;
|
||||
}
|
||||
|
||||
while (stack) {
|
||||
// descend
|
||||
while (stack->next_child_idx < ArrayCount(stack->node->children)) {
|
||||
HashMapNode *child = stack->node->children[stack->next_child_idx];
|
||||
stack->next_child_idx += 1;
|
||||
|
||||
if (child) {
|
||||
Stack *f = free_list;
|
||||
if (f) { SLLStackPop(free_list); }
|
||||
else { f = push_array(scratch.arena, Stack, 1); }
|
||||
|
||||
f->node = child;
|
||||
SLLStackPush(stack, f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ascend
|
||||
if (stack->next_child_idx >= ArrayCount(stack->node->children)) {
|
||||
|
||||
if (stack->node->is_live) {
|
||||
if (key_func) {
|
||||
key_func(&stack->node->v, key_buffer + key_offset);
|
||||
key_offset += key_size;
|
||||
}
|
||||
if (value_func) {
|
||||
value_func(&stack->node->v, value_buffer + value_offset);
|
||||
value_offset += value_size;
|
||||
}
|
||||
}
|
||||
|
||||
SLLStackPop(stack);
|
||||
}
|
||||
}
|
||||
|
||||
scratch_end(scratch);
|
||||
return (HashMapExtract){ .keys = key_buffer, .values = value_buffer };
|
||||
}
|
||||
|
||||
// { keys, value }
|
||||
force_inline HASH_MAP_EXTRACT_FUNC(hash_map_extract_key_value) { MemoryCopy(buffer, kv, sizeof(*kv)); }
|
||||
internal HashMapKeyValue * key_value_from_hash_map ( Arena *arena, HashMap *hm) { return hash_map_extract(arena, hm, hash_map_extract_key_value, sizeof(HashMapKeyValue), 0, 0).keys; }
|
||||
|
||||
// keys
|
||||
force_inline HASH_MAP_EXTRACT_FUNC(hash_map_extract_key_u32) { MemoryCopy(buffer, &kv->key.key_u32, sizeof(kv->key.key_u32)); }
|
||||
force_inline HASH_MAP_EXTRACT_FUNC(hash_map_extract_key_u64) { MemoryCopy(buffer, &kv->key.key_u64, sizeof(kv->key.key_u64)); }
|
||||
force_inline HASH_MAP_EXTRACT_FUNC(hash_map_extract_key_string) { MemoryCopy(buffer, &kv->key.key_string, sizeof(kv->key.key_string)); }
|
||||
internal U32 * keys_from_hash_map_u32 ( Arena *arena, HashMap *hm) { return hash_map_extract(arena, hm, hash_map_extract_key_u32, sizeof(U32), 0, 0).keys; }
|
||||
internal U64 * keys_from_hash_map_u64 ( Arena *arena, HashMap *hm) { return hash_map_extract(arena, hm, hash_map_extract_key_u64, sizeof(U64), 0, 0).keys; }
|
||||
internal String8 * keys_from_hash_map_string( Arena *arena, HashMap *hm) { return hash_map_extract(arena, hm, hash_map_extract_key_string, sizeof(String8), 0, 0).keys; }
|
||||
internal void * keys_from_hash_map_raw ( Arena *arena, HashMap *hm) { return hash_map_extract(arena, hm, hash_map_extract_key_string, sizeof(void *), 0, 0).keys; }
|
||||
|
||||
// values
|
||||
force_inline HASH_MAP_EXTRACT_FUNC(hash_map_extract_value_u64) { MemoryCopy(buffer, &kv->value.value_u64, sizeof(kv->value.value_u64)); }
|
||||
internal U64 * values_from_hash_map_u64(Arena *arena, HashMap *hm) { return hash_map_extract(arena, hm, 0, 0, hash_map_extract_value_u64, sizeof(U64)).values; }
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
force_inline int hash_map_key_is_before_u32(void *a, void *b) { return ((HashMapKeyValue*)a)->key.key_u32 < ((HashMapKeyValue*)b)->key.key_u32; }
|
||||
force_inline int hash_map_key_is_before_u64(void *a, void *b) { return ((HashMapKeyValue*)a)->key.key_u64 < ((HashMapKeyValue*)b)->key.key_u64; }
|
||||
force_inline int hash_map_key_is_before_string(void *a, void *b) { return str8_is_before_case_sensitive(a, b); }
|
||||
|
||||
internal void sort_hash_map_key_value_u32(HashMapKeyValue *pairs, U64 count) { radsort(pairs, count, hash_map_key_is_before_u32); }
|
||||
internal void sort_hash_map_key_value_u64(HashMapKeyValue *pairs, U64 count) { radsort(pairs, count, hash_map_key_is_before_u64); }
|
||||
internal void sort_hash_map_key_value_string(HashMapKeyValue *pairs, U64 count) { radsort(pairs, count, hash_map_key_is_before_string); }
|
||||
|
||||
|
||||
+86
-3
@@ -92,11 +92,10 @@ internal B32 hash_table_purge_string(HashTable *ht, String8 key);
|
||||
|
||||
internal U32 * keys_from_hash_table_u32 (Arena *arena, HashTable *ht);
|
||||
internal U64 * keys_from_hash_table_u64 (Arena *arena, HashTable *ht);
|
||||
internal String8 keys_from_hash_table_str8 (Arena *arena, HashTable *ht);
|
||||
internal KeyValuePair * key_value_pairs_from_hash_table(Arena *arena, HashTable *ht);
|
||||
internal void * keys_from_hash_table_raw (Arena *arena, HashTable *ht);
|
||||
|
||||
internal U64 * values_from_hash_table_u64(Arena *arena, HashTable *ht);
|
||||
internal String8 keys_from_hash_table_str8 (Arena *arena, HashTable *ht);
|
||||
internal KeyValuePair * key_value_pairs_from_hash_table(Arena *arena, HashTable *ht);
|
||||
|
||||
internal void sort_key_value_pairs_as_u32 (KeyValuePair *pairs, U64 count);
|
||||
internal void sort_key_value_pairs_as_u64 (KeyValuePair *pairs, U64 count);
|
||||
@@ -109,3 +108,87 @@ internal String8 ** str8_from_key_value_pairs(Arena *arena, KeyValuePair *v, U64
|
||||
internal U64Array remove_duplicates_u64_array(Arena *arena, U64Array arr);
|
||||
internal String8List remove_duplicates_str8_list(Arena *arena, String8List list);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
typedef union
|
||||
{
|
||||
String8 key_string;
|
||||
void *key_raw;
|
||||
U32 key_u32;
|
||||
U64 key_u64;
|
||||
} HashMapKey;
|
||||
|
||||
typedef union
|
||||
{
|
||||
String8 value_string;
|
||||
void *value_raw;
|
||||
U32 value_u32;
|
||||
U64 value_u64;
|
||||
} HashMapValue;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
HashMapKey key;
|
||||
HashMapValue value;
|
||||
} HashMapKeyValue;
|
||||
|
||||
typedef union HashMapNode HashMapNode;
|
||||
union HashMapNode
|
||||
{
|
||||
struct {
|
||||
HashMapNode *children[4];
|
||||
HashMapKeyValue v;
|
||||
B32 is_live;
|
||||
};
|
||||
HashMapNode *next;
|
||||
};
|
||||
|
||||
typedef struct HashMap
|
||||
{
|
||||
HashMapNode *root;
|
||||
U64 count;
|
||||
U64 tombstone_count;
|
||||
HashMapNode *free_list;
|
||||
} HashMap;
|
||||
|
||||
#define HASH_MAP_KEY_MATCH(name) B32 name(HashMapKey *a, HashMapKey *b)
|
||||
typedef HASH_MAP_KEY_MATCH(HashMapKeyMatchFunc);
|
||||
|
||||
internal U64 hash_map_hasher(String8 string);
|
||||
|
||||
internal HashMapNode * hash_map_search(HashMap *hm, U64 hash, HashMapKey key, HashMapKeyMatchFunc *match_func);
|
||||
internal HashMapNode * hash_map_push(Arena *arena, HashMap *hm, U64 hash, HashMapKeyValue key_value, HashMapKeyMatchFunc *match_func);
|
||||
internal B32 hash_map_purge_item(HashMap *hm, U64 hash, HashMapKey key, HashMapKeyMatchFunc *match_func);
|
||||
internal void hash_map_purge(HashMap *hm);
|
||||
|
||||
internal HashMapNode * hash_map_push_string_string(Arena *arena, HashMap *hm, String8 key, String8 value);
|
||||
internal HashMapNode * hash_map_push_string_raw (Arena *arena, HashMap *hm, String8 key, void *value);
|
||||
internal HashMapNode * hash_map_push_string_u64 (Arena *arena, HashMap *hm, String8 key, U64 value);
|
||||
internal HashMapNode * hash_map_push_u32_raw (Arena *arena, HashMap *hm, U32 key, void *value);
|
||||
internal HashMapNode * hash_map_push_u32_string (Arena *arena, HashMap *hm, U32 key, String8 value);
|
||||
internal HashMapNode * hash_map_push_u64_raw (Arena *arena, HashMap *hm, U64 key, void *value);
|
||||
internal HashMapNode * hash_map_push_u64_string (Arena *arena, HashMap *hm, U64 key, String8 value);
|
||||
internal HashMapNode * hash_map_push_u64_u64 (Arena *arena, HashMap *hm, U64 key, U64 value);
|
||||
internal HashMapNode * hash_map_push_path_u64 (Arena *arena, HashMap *hm, String8 path, U64 value);
|
||||
internal HashMapNode * hash_map_push_path_string (Arena *arena, HashMap *hm, String8 path, String8 value);
|
||||
internal HashMapNode * hash_map_push_path_raw (Arena *arena, HashMap *hm, String8 path, void *value);
|
||||
|
||||
internal void * hash_map_search_string_raw(HashMap *hm, String8 key);
|
||||
internal U32 * hash_map_search_string_u32(HashMap *hm, String8 key);
|
||||
internal U64 * hash_map_search_string_u64(HashMap *hm, String8 key);
|
||||
internal void * hash_map_search_path_raw (HashMap *hm, String8 key);
|
||||
internal void * hash_map_search_u64_raw (HashMap *hm, U64 key);
|
||||
internal U64 * hash_map_search_u64_u64 (HashMap *hm, U64 key);
|
||||
internal void * hash_map_search_raw_raw (HashMap *hm, void *key);
|
||||
|
||||
internal HashMapKeyValue * key_value_from_hash_map (Arena *arena, HashMap *hm);
|
||||
internal U32 * keys_from_hash_map_u32 (Arena *arena, HashMap *hm);
|
||||
internal U64 * keys_from_hash_map_u64 (Arena *arena, HashMap *hm);
|
||||
internal String8 * keys_from_hash_map_string(Arena *arena, HashMap *hm);
|
||||
internal void * keys_from_hash_map_raw (Arena *arena, HashMap *hm);
|
||||
internal U64 * values_from_hash_map_u64 (Arena *arena, HashMap *hm);
|
||||
|
||||
internal void sort_hash_map_key_value_u32 (HashMapKeyValue *pairs, U64 count);
|
||||
internal void sort_hash_map_key_value_u64 (HashMapKeyValue *pairs, U64 count);
|
||||
internal void sort_hash_map_key_value_string(HashMapKeyValue *pairs, U64 count);
|
||||
|
||||
|
||||
@@ -176,4 +176,169 @@ TEST(match_wildcard)
|
||||
T_Ok(str8_match_wildcard(str8_lit("a"), str8_lit("?*?"), 0) == 0);
|
||||
}
|
||||
|
||||
TEST(hash_map)
|
||||
{
|
||||
{
|
||||
HashMap hm = {0};
|
||||
|
||||
U64 test_count = 256;
|
||||
for EachIndex(i, test_count) { hash_map_push_u64_u64(arena, &hm, i, i*2); }
|
||||
|
||||
// test tombstone reuse
|
||||
for (U64 i = 10; i < test_count; ++i) {
|
||||
HashMapNode *test_node = hash_map_search(&hm, hash_map_hasher(str8_struct(&i)), (HashMapKey){ .key_u64 = i }, hash_map_match_u64);
|
||||
T_Ok(hash_map_purge_u64(&hm, i) == 1);
|
||||
T_Ok(hash_map_push_u64_u64(arena, &hm, i, 10) == test_node);
|
||||
}
|
||||
|
||||
// delete some items and after search them
|
||||
for (U64 i = 0; i < 10; ++i) {
|
||||
T_Ok(hash_map_purge_u64(&hm, i));
|
||||
}
|
||||
T_Ok(hm.tombstone_count == 10);
|
||||
for (U64 i = 0; i < 10; ++i) {
|
||||
T_Ok(hash_map_search_u64_raw(&hm, i) == 0);
|
||||
}
|
||||
|
||||
// extract pairs and test sorting
|
||||
HashMapKeyValue *pairs = key_value_from_hash_map(arena, &hm);
|
||||
sort_hash_map_key_value_u64(pairs, hm.count);
|
||||
for (U64 i = 1; i < hm.count; ++i) {
|
||||
T_Ok(pairs[i-1].key.key_u64 < pairs[i].key.key_u64);
|
||||
}
|
||||
|
||||
// did purge put all the nodes on free list?
|
||||
hash_map_purge(&hm);
|
||||
U64 free_list_count = 0;
|
||||
for (HashMapNode *n = hm.free_list; n != 0; n = n->next) { free_list_count += 1; }
|
||||
T_Ok(free_list_count == test_count);
|
||||
}
|
||||
|
||||
// test search through tombstones
|
||||
{
|
||||
HashMap hm = {0};
|
||||
|
||||
U64 hash = 12345;
|
||||
|
||||
hash_map_push(arena, &hm, hash, (HashMapKeyValue){ .key = { .key_u64 = 1 }, .value = { .value_u64 = 10 } }, hash_map_match_u64);
|
||||
hash_map_push(arena, &hm, hash, (HashMapKeyValue){ .key = { .key_u64 = 2 }, .value = { .value_u64 = 20 } }, hash_map_match_u64);
|
||||
hash_map_push(arena, &hm, hash, (HashMapKeyValue){ .key = { .key_u64 = 3 }, .value = { .value_u64 = 30 } }, hash_map_match_u64);
|
||||
|
||||
T_Ok(hash_map_purge_u64(&hm, 1));
|
||||
|
||||
T_Ok(hash_map_search(&hm, hash, (HashMapKey){ .key_u64 = 2 }, hash_map_match_u64)->v.value.value_u64 == 20);
|
||||
T_Ok(hash_map_search(&hm, hash, (HashMapKey){ .key_u64 = 3 }, hash_map_match_u64)->v.value.value_u64 == 30);
|
||||
}
|
||||
|
||||
// interleaved purges
|
||||
{
|
||||
HashMap hm = {0};
|
||||
for EachIndex(round, 100) {
|
||||
for EachIndex(i, 256) { hash_map_push_u64_u64(arena, &hm, i, round); }
|
||||
for (U64 i = 0; i < 256; i += 2) { hash_map_purge_u64(&hm, i); }
|
||||
for (U64 i = 0; i < 256; i += 2) { hash_map_push_u64_u64(arena, &hm, i, round + 1); }
|
||||
|
||||
T_Ok(hm.count == 256);
|
||||
T_Ok(hm.tombstone_count == 0);
|
||||
}
|
||||
|
||||
for EachIndex(i, 100) {
|
||||
T_Ok(hash_map_purge_u64(&hm, 123123123 + i) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
// empty hash map test
|
||||
{
|
||||
HashMap hm = {0};
|
||||
|
||||
T_Ok(hash_map_search_u64_raw(&hm, 123) == 0);
|
||||
T_Ok(hash_map_purge_u64(&hm, 123) == 0);
|
||||
|
||||
HashMapKeyValue *pairs = key_value_from_hash_map(arena, &hm);
|
||||
T_Ok(pairs == 0 || hm.count == 0);
|
||||
|
||||
hash_map_purge(&hm);
|
||||
T_Ok(hm.count == 0);
|
||||
T_Ok(hm.tombstone_count == 0);
|
||||
}
|
||||
|
||||
// heavy collision
|
||||
{
|
||||
HashMap hm = {0};
|
||||
|
||||
U64 hash = 12345;
|
||||
U64 count = 1024;
|
||||
|
||||
for EachIndex(i, count) {
|
||||
hash_map_push(arena, &hm, hash, (HashMapKeyValue){ .key = { .key_u64 = i }, .value = { .value_u64 = i*3 } }, hash_map_match_u64);
|
||||
}
|
||||
|
||||
for EachIndex(i, count) {
|
||||
T_Ok(hash_map_search(&hm, hash, (HashMapKey){ .key_u64 = i }, hash_map_match_u64)->v.value.value_u64 == i*3);
|
||||
}
|
||||
}
|
||||
|
||||
// duplicate key test
|
||||
{
|
||||
HashMap hm = {0};
|
||||
|
||||
U64 a = 1;
|
||||
U64 b = 2;
|
||||
|
||||
HashMapNode *n0 = hash_map_push_u64_u64(arena, &hm, 1, a);
|
||||
HashMapNode *n1 = hash_map_push_u64_u64(arena, &hm, 1, b);
|
||||
|
||||
T_Ok(hm.count == 1);
|
||||
T_Ok(n0 == n1);
|
||||
U64 test = *hash_map_search_u64_u64(&hm, 1);
|
||||
T_Ok(test != b);
|
||||
}
|
||||
|
||||
// test paths
|
||||
{
|
||||
HashMap hm = {0};
|
||||
|
||||
U64 foo = 0;
|
||||
U64 bar = 1;
|
||||
|
||||
hash_map_push_path_raw(arena, &hm, str8_lit("c:/DEVEL/test"), &foo);
|
||||
hash_map_push_path_raw(arena, &hm, str8_lit("c:\\DEVEL\\test"), &foo);
|
||||
hash_map_push_path_raw(arena, &hm, str8_lit("c:/devel\\test"), &foo);
|
||||
|
||||
hash_map_push_path_raw(arena, &hm, str8_lit("/mnt/devel"), &bar);
|
||||
hash_map_push_path_raw(arena, &hm, str8_lit("/MNT/devel"), &bar);
|
||||
|
||||
T_Ok(hm.count == 2);
|
||||
|
||||
T_Ok(hash_map_search_path_raw(&hm, str8_lit("c:/devel/test")) == &foo);
|
||||
T_Ok(hash_map_search_path_raw(&hm, str8_lit("c:\\devel\\TEST")) == &foo);
|
||||
T_Ok(hash_map_search_path_raw(&hm, str8_lit("/mnt/devel")) == &bar);
|
||||
T_Ok(hash_map_search_path_raw(&hm, str8_lit("/MNT/DEVEL")) == &bar);
|
||||
}
|
||||
|
||||
// test strings
|
||||
{
|
||||
HashMap hm = {0};
|
||||
|
||||
U64 foo = 0;
|
||||
U64 bar = 1;
|
||||
|
||||
hash_map_push_string_raw(arena, &hm, str8_lit("c:/DEVEL/test"), &foo);
|
||||
hash_map_push_string_raw(arena, &hm, str8_lit("c:\\DEVEL\\test"), &foo);
|
||||
hash_map_push_string_raw(arena, &hm, str8_lit("c:/devel\\test"), &foo);
|
||||
|
||||
hash_map_push_string_raw(arena, &hm, str8_lit("/mnt/devel"), &bar);
|
||||
hash_map_push_string_raw(arena, &hm, str8_lit("/MNT/devel"), &bar);
|
||||
|
||||
T_Ok(hm.count == 5);
|
||||
|
||||
T_Ok(hash_map_search_string_raw(&hm, str8_lit("c:/DEVEL/test")) == &foo);
|
||||
T_Ok(hash_map_search_string_raw(&hm, str8_lit("c:\\DEVEL\\test")) == &foo);
|
||||
T_Ok(hash_map_search_string_raw(&hm, str8_lit("c:/devel\\test")) == &foo);
|
||||
|
||||
T_Ok(hash_map_search_string_raw(&hm, str8_lit("/mnt/devel")) == &bar);
|
||||
T_Ok(hash_map_search_string_raw(&hm, str8_lit("/MNT/devel")) == &bar);
|
||||
}
|
||||
}
|
||||
|
||||
#undef T_Group
|
||||
|
||||
Reference in New Issue
Block a user