// NOTE(bill): This util stuff is the same for every `Map` struct StringMapFindResult { isize hash_index; isize entry_prev; isize entry_index; }; struct StringHashKey { u64 hash; String string; }; StringHashKey string_hashing_proc(void const *data, isize len) { StringHashKey h = {}; h.hash = gb_fnv64a(data, len); h.string.text = (u8 *)data; h.string.len = len; return h; } gb_inline StringHashKey string_hash_string(String const &s) { return string_hashing_proc(s.text, s.len); } bool string_hash_key_equal(StringHashKey a, StringHashKey b) { if (a.hash == b.hash) { // NOTE(bill): If two string's hashes collide, compare the strings themselves return a.string == b.string; } return false; } bool operator==(StringHashKey a, StringHashKey b) { return string_hash_key_equal(a, b); } bool operator!=(StringHashKey a, StringHashKey b) { return !string_hash_key_equal(a, b); } template struct StringMapEntry { StringHashKey key; isize next; T value; }; template struct StringMap { Array hashes; Array > entries; }; template void string_map_init (StringMap *h, gbAllocator a, isize capacity = 16); template void string_map_destroy (StringMap *h); template T * string_map_get (StringMap *h, char const *key); template T * string_map_get (StringMap *h, String const &key); template T * string_map_get (StringMap *h, StringHashKey const &key); template T & string_map_must_get (StringMap *h, char const *key); template T & string_map_must_get (StringMap *h, String const &key); template T & string_map_must_get (StringMap *h, StringHashKey const &key); template void string_map_set (StringMap *h, StringHashKey const &key, T const &value); template void string_map_set (StringMap *h, String const &key, T const &value); template void string_map_set (StringMap *h, char const *key, T const &value); template void string_map_remove (StringMap *h, StringHashKey const &key); template void string_map_clear (StringMap *h); template void string_map_grow (StringMap *h); template void string_map_rehash (StringMap *h, isize new_count); template gb_inline void string_map_init(StringMap *h, gbAllocator a, isize capacity) { array_init(&h->hashes, a, capacity); array_init(&h->entries, a, 0, capacity); for (isize i = 0; i < capacity; i++) { h->hashes.data[i] = -1; } } template gb_inline void string_map_destroy(StringMap *h) { array_free(&h->entries); array_free(&h->hashes); } template gb_internal isize string_map__add_entry(StringMap *h, StringHashKey const &key) { StringMapEntry e = {}; e.key = key; e.next = -1; array_add(&h->entries, e); return h->entries.count-1; } template gb_internal StringMapFindResult string_map__find(StringMap *h, StringHashKey const &key) { StringMapFindResult fr = {-1, -1, -1}; if (h->hashes.count > 0) { fr.hash_index = key.hash % h->hashes.count; fr.entry_index = h->hashes.data[fr.hash_index]; while (fr.entry_index >= 0) { if (string_hash_key_equal(h->entries.data[fr.entry_index].key, key)) { return fr; } fr.entry_prev = fr.entry_index; fr.entry_index = h->entries.data[fr.entry_index].next; } } return fr; } template gb_internal StringMapFindResult string_map__find_from_entry(StringMap *h, StringMapEntry *e) { StringMapFindResult fr = {-1, -1, -1}; if (h->hashes.count > 0) { fr.hash_index = e->key.hash % h->hashes.count; fr.entry_index = h->hashes.data[fr.hash_index]; while (fr.entry_index >= 0) { if (&h->entries.data[fr.entry_index] == e) { return fr; } fr.entry_prev = fr.entry_index; fr.entry_index = h->entries.data[fr.entry_index].next; } } return fr; } template gb_internal b32 string_map__full(StringMap *h) { return 0.75f * h->hashes.count <= h->entries.count; } #define STRING_MAP_ARRAY_GROW_FORMULA(x) (4*(x) + 7) GB_STATIC_ASSERT(STRING_MAP_ARRAY_GROW_FORMULA(0) > 0); template gb_inline void string_map_grow(StringMap *h) { isize new_count = STRING_MAP_ARRAY_GROW_FORMULA(h->entries.count); string_map_rehash(h, new_count); } template void string_map_rehash(StringMap *h, isize new_count) { isize i, j; StringMap nh = {}; string_map_init(&nh, h->hashes.allocator, new_count); array_resize(&nh.hashes, new_count); array_reserve(&nh.entries, h->entries.count); for (i = 0; i < new_count; i++) { nh.hashes.data[i] = -1; } for (i = 0; i < h->entries.count; i++) { StringMapEntry *e = &h->entries.data[i]; StringMapFindResult fr; if (nh.hashes.count == 0) { string_map_grow(&nh); } fr = string_map__find(&nh, e->key); j = string_map__add_entry(&nh, e->key); if (fr.entry_prev < 0) { nh.hashes.data[fr.hash_index] = j; } else { nh.entries.data[fr.entry_prev].next = j; } nh.entries.data[j].next = fr.entry_index; nh.entries.data[j].value = e->value; if (string_map__full(&nh)) { string_map_grow(&nh); } } string_map_destroy(h); *h = nh; } template T *string_map_get(StringMap *h, StringHashKey const &key) { isize index = string_map__find(h, key).entry_index; if (index >= 0) { return &h->entries.data[index].value; } return nullptr; } template gb_inline T *string_map_get(StringMap *h, String const &key) { return string_map_get(h, string_hash_string(key)); } template gb_inline T *string_map_get(StringMap *h, char const *key) { return string_map_get(h, string_hash_string(make_string_c(key))); } template T &string_map_must_get(StringMap *h, StringHashKey const &key) { isize index = string_map__find(h, key).entry_index; GB_ASSERT(index >= 0); return h->entries.data[index].value; } template gb_inline T &string_map_must_get(StringMap *h, String const &key) { return string_map_must_get(h, string_hash_string(key)); } template gb_inline T &string_map_must_get(StringMap *h, char const *key) { return string_map_must_get(h, string_hash_string(make_string_c(key))); } template void string_map_set(StringMap *h, StringHashKey const &key, T const &value) { isize index; StringMapFindResult fr; if (h->hashes.count == 0) { string_map_grow(h); } fr = string_map__find(h, key); if (fr.entry_index >= 0) { index = fr.entry_index; } else { index = string_map__add_entry(h, key); if (fr.entry_prev >= 0) { h->entries.data[fr.entry_prev].next = index; } else { h->hashes.data[fr.hash_index] = index; } } h->entries.data[index].value = value; if (string_map__full(h)) { string_map_grow(h); } } template gb_inline void string_map_set(StringMap *h, String const &key, T const &value) { string_map_set(h, string_hash_string(key), value); } template gb_inline void string_map_set(StringMap *h, char const *key, T const &value) { string_map_set(h, string_hash_string(make_string_c(key)), value); } template void string_map__erase(StringMap *h, StringMapFindResult const &fr) { StringMapFindResult last; if (fr.entry_prev < 0) { h->hashes.data[fr.hash_index] = h->entries.data[fr.entry_index].next; } else { h->entries.data[fr.entry_prev].next = h->entries.data[fr.entry_index].next; } if (fr.entry_index == h->entries.count-1) { array_pop(&h->entries); return; } h->entries.data[fr.entry_index] = h->entries.data[h->entries.count-1]; last = string_map__find(h, h->entries.data[fr.entry_index].key); if (last.entry_prev >= 0) { h->entries.data[last.entry_prev].next = fr.entry_index; } else { h->hashes.data[last.hash_index] = fr.entry_index; } } template void string_map_remove(StringMap *h, StringHashKey const &key) { StringMapFindResult fr = string_map__find(h, key); if (fr.entry_index >= 0) { string_map__erase(h, fr); } } template gb_inline void string_map_clear(StringMap *h) { array_clear(&h->hashes); array_clear(&h->entries); }