Test and avoid MD_StringMap collisions.

This commit is contained in:
Miguel Lechon
2021-03-16 19:05:48 +01:00
parent 55943b79fc
commit 541a5443b6
3 changed files with 157 additions and 52 deletions
+1
View File
@@ -398,6 +398,7 @@ struct MD_MapSlot
{
MD_MapSlot *next;
MD_u64 hash;
void *key;
void *value;
};
+101 -52
View File
@@ -959,12 +959,109 @@ _MD_Map_Initialize(MD_Map *map)
}
}
/////////////////////////////////////////////
//~ NOTE(mal): MD_StringMap
MD_FUNCTION_IMPL MD_MapSlot *
_MD_Map_Lookup(MD_Map *map, MD_u64 hash)
MD_StringMap_Lookup(MD_StringMap *map, MD_String8 string) // NOTE(mal): Or MD_PtrFromString
{
_MD_Map_Initialize(map);
MD_MapSlot *slot = 0;
MD_u64 hash = MD_HashString(string);
MD_u64 index = hash % map->table_size;
for(MD_MapSlot *candidate = map->table[index]; candidate; candidate = candidate->next)
{
if(candidate->hash == hash && MD_StringMatch(*((MD_String8 *)candidate->key), string, 0))
{
slot = candidate;
break;
}
}
return slot;
}
MD_FUNCTION_IMPL MD_b32
MD_StringMap_Insert(MD_StringMap *map, MD_MapCollisionRule collision_rule, MD_String8 string, void *value)
{
_MD_Map_Initialize(map);
MD_MapSlot *slot = 0;
MD_u64 hash = MD_HashString(string);
MD_u64 index = hash % map->table_size;
for(MD_MapSlot *candidate = map->table[index]; candidate; candidate = candidate->next)
{
if(candidate->hash == hash && MD_StringMatch(*((MD_String8 *)candidate->key), string, 0))
{
slot = candidate;
break;
}
}
if(slot == 0 || (slot != 0 && collision_rule == MD_MapCollisionRule_Chain))
{
slot = _MD_PushArray(_MD_GetCtx(), MD_MapSlot, 1);
if(slot)
{
slot->next = 0;
if(map->table[index])
{
for(MD_MapSlot *old_slot = map->table[index]; old_slot; old_slot = old_slot->next)
{
if(old_slot->next == 0)
{
old_slot->next = slot;
break;
}
}
}
else
{
map->table[index] = slot;
}
}
}
if(slot)
{
slot->value = value;
slot->hash = hash;
MD_String8 *string_copy = _MD_PushArray(_MD_GetCtx(), MD_String8, 1);
*string_copy = MD_PushStringCopy(string);
slot->key = string_copy;
}
return !!slot;
}
// NOTE(mal): Original MD_NodeTable interface
#define MD_NodeTable_Lookup(map, string) MD_StringMap_Lookup(map, string)
#define MD_NodeTable_Insert(map, collision_rule, string, node) MD_StringMap_Insert(map, collision_rule, string, (void *) node)
/////////////////////////////////////////////
//~ NOTE(mal): MD_PtrMap
// NOTE(mal): Generic 64-bit hash function (https://nullprogram.com/blog/2018/07/31/)
// Reversible, so no collisions. Assumes all bits of the pointer matter.
MD_FUNCTION_IMPL MD_u64
MD_HashPointer(void *p)
{
MD_u64 h = (MD_u64)p;
h = (h ^ (h >> 30)) * UINT64_C(0xbf58476d1ce4e5b9);
h = (h ^ (h >> 27)) * UINT64_C(0x94d049bb133111eb);
h = h ^ (h >> 31);
return h;
}
MD_FUNCTION_IMPL MD_MapSlot *
MD_PtrMap_Lookup(MD_PtrMap *map, void *key) // NOTE(mal): Or MD_PtrFromPtr
{
_MD_Map_Initialize(map);
MD_MapSlot *slot = 0;
MD_u64 hash = MD_HashPointer(key);
MD_u64 index = hash % map->table_size;
for(MD_MapSlot *candidate = map->table[index]; candidate; candidate = candidate->next)
{
@@ -974,15 +1071,17 @@ _MD_Map_Lookup(MD_Map *map, MD_u64 hash)
break;
}
}
return slot;
}
MD_FUNCTION_IMPL MD_b32
_MD_Map_Insert(MD_StringMap *map, MD_MapCollisionRule collision_rule, MD_u64 hash, void *value)
MD_PtrMap_Insert(MD_PtrMap *map, MD_MapCollisionRule collision_rule, void *key, void *value)
{
_MD_Map_Initialize(map);
MD_MapSlot *slot = 0;
MD_u64 hash = MD_HashPointer(key);
MD_u64 index = hash % map->table_size;
for(MD_MapSlot *candidate = map->table[index]; candidate; candidate = candidate->next)
@@ -1027,56 +1126,6 @@ _MD_Map_Insert(MD_StringMap *map, MD_MapCollisionRule collision_rule, MD_u64 has
return !!slot;
}
/////////////////////////////////////////////
//~ NOTE(mal): MD_StringMap
MD_FUNCTION_IMPL MD_MapSlot *
MD_StringMap_Lookup(MD_StringMap *map, MD_String8 string) // NOTE(mal): Or MD_PtrFromString
{
MD_MapSlot *slot = _MD_Map_Lookup(map, MD_HashString(string));
return slot;
}
MD_FUNCTION_IMPL MD_b32
MD_StringMap_Insert(MD_StringMap *map, MD_MapCollisionRule collision_rule, MD_String8 string, void *value)
{
MD_b32 result = _MD_Map_Insert(map, collision_rule, MD_HashString(string), value);
return result;
}
// NOTE(mal): Original MD_NodeTable interface
#define MD_NodeTable_Lookup(map, string) MD_StringMap_Lookup(map, string)
#define MD_NodeTable_Insert(map, collision_rule, string, node) MD_StringMap_Insert(map, collision_rule, string, (void *) node)
/////////////////////////////////////////////
//~ NOTE(mal): MD_PtrMap
// NOTE(mal): Generic 64-bit hash function (https://nullprogram.com/blog/2018/07/31/)
// Assumes all bits of the pointer matter and that there's a b
MD_FUNCTION_IMPL MD_u64
MD_HashPointer(void *p)
{
MD_u64 h = (MD_u64)p;
h = (h ^ (h >> 30)) * UINT64_C(0xbf58476d1ce4e5b9);
h = (h ^ (h >> 27)) * UINT64_C(0x94d049bb133111eb);
h = h ^ (h >> 31);
return h;
}
MD_FUNCTION_IMPL MD_MapSlot *
MD_PtrMap_Lookup(MD_PtrMap *map, void *key) // NOTE(mal): Or MD_PtrFromPtr
{
MD_MapSlot *slot = _MD_Map_Lookup(map, MD_HashPointer(key));
return slot;
}
// TODO(mal): Remove collision_rule parameter and assume MD_MapCollisionRule_Overwrite?
MD_FUNCTION_IMPL MD_b32
MD_PtrMap_Insert(MD_PtrMap *map, MD_MapCollisionRule collision_rule, void *key, void *value)
{
MD_b32 result = _MD_Map_Insert(map, collision_rule, MD_HashPointer(key), value);
return result;
}
MD_FUNCTION_IMPL MD_b32
MD_TokenKindIsWhitespace(MD_TokenKind kind)
{
+55
View File
@@ -488,5 +488,60 @@ int main(void)
}
}
Test("Hash maps")
{
MD_String8 keys[] =
{
MD_S8Lit("\xed\x80\x73\x71\x78\xba\xff\xd6\x87\x83\xcd\x20\x28\xf7\x1c\xc1\x5f\xca\x98\x9c\x5a\xab\x0c\xae\x9a\x60\x57\x03\xeb\x1f\xde\x99"),
MD_S8Lit("\x4c\x80\xb7\x8b\xbf\x65\x5a\x4b\xc1\x2a\xc3\x5f\xe1\x66\xfb\x0d\x72\x83\x1c\x63\xba\xb5\x97\x02\x3f\x6a\xe0\x2a\x1b\x82\x07\x76"),
MD_S8Lit("\xd8\xfd\x11\x4b\x04\xdf\xe5\x20\x5b\xd6\x4f\x87\x00\x70\x6a\xc8\xde\xed\xc7\x79\xdb\x87\x24\x36\xa8\x7a\x31\x41\x00\x57\xbd\x8d"),
};
// NOTE(mal): True for MD_HashString is djb2
{
TestResult(MD_HashString(keys[0]) == 1 && MD_HashString(keys[1]) == 1 && MD_HashString(keys[2]) == 3);
}
{
MD_MapCollisionRule rules[] = { MD_MapCollisionRule_Chain, MD_MapCollisionRule_Overwrite };
for(int i_rule = 0; i_rule < MD_ArrayCount(rules); ++i_rule)
{
MD_StringMap map = {0};
MD_StringMap_Insert(&map, rules[i_rule], keys[0], (void *)0);
MD_StringMap_Insert(&map, rules[i_rule], keys[1], (void *)1);
MD_StringMap_Insert(&map, rules[i_rule], keys[2], (void *)2);
MD_MapSlot *slot0 = MD_StringMap_Lookup(&map, keys[0]);
MD_MapSlot *slot1 = MD_StringMap_Lookup(&map, keys[1]);
MD_MapSlot *slot2 = MD_StringMap_Lookup(&map, keys[2]);
TestResult(slot0 && slot0->value == (void *)0 &&
slot1 && slot1->value == (void *)1 &&
slot2 && slot2->value == (void *)2);
}
}
{
MD_MapCollisionRule rules[] = { MD_MapCollisionRule_Chain, MD_MapCollisionRule_Overwrite };
for(int i_rule = 0; i_rule < MD_ArrayCount(rules); ++i_rule)
{
MD_PtrMap map = {0};
MD_PtrMap_Insert(&map, rules[i_rule], (void *)0, (void *)0);
MD_PtrMap_Insert(&map, rules[i_rule], (void *)1, (void *)1);
MD_PtrMap_Insert(&map, rules[i_rule], (void *)2, (void *)2);
MD_MapSlot *slot0 = MD_PtrMap_Lookup(&map, (void *)0);
MD_MapSlot *slot1 = MD_PtrMap_Lookup(&map, (void *)1);
MD_MapSlot *slot2 = MD_PtrMap_Lookup(&map, (void *)2);
TestResult(slot0 && slot0->value == (void *) 0 &&
slot1 && slot1->value == (void *) 1 &&
slot2 && slot2->value == (void *) 2);
}
}
}
return 0;
}