#define PTR_MAP_ENABLE_MULTI_MAP 1 typedef u32 MapIndex; enum { MAP_CACHE_LINE_SIZE_POW = 6, MAP_CACHE_LINE_SIZE = 1< struct PtrMapEntry { static_assert(sizeof(K) == sizeof(void *), "Key size must be pointer size"); K key; V value; MapIndex next; }; template struct PtrMap { MapIndex * hashes; usize hashes_count; PtrMapEntry *entries; u32 count; u32 entries_capacity; }; gb_internal gb_inline u32 ptr_map_hash_key(uintptr key) { u32 res; #if defined(GB_ARCH_64_BIT) key = (~key) + (key << 21); key = key ^ (key >> 24); key = (key + (key << 3)) + (key << 8); key = key ^ (key >> 14); key = (key + (key << 2)) + (key << 4); key = key ^ (key << 28); res = cast(u32)key; #elif defined(GB_ARCH_32_BIT) u32 state = (cast(u32)key) * 747796405u + 2891336453u; u32 word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u; res = (word >> 22u) ^ word; #endif return res; } gb_internal gb_inline u32 ptr_map_hash_key(void const *key) { return ptr_map_hash_key((uintptr)key); } template gb_internal void map_init (PtrMap *h, isize capacity = 16); template gb_internal void map_destroy (PtrMap *h); template gb_internal V * map_get (PtrMap *h, K key); template gb_internal void map_set (PtrMap *h, K key, V const &value); template gb_internal bool map_set_if_not_previously_exists(PtrMap *h, K key, V const &value); // returns true if it previously existed template gb_internal void map_remove (PtrMap *h, K key); template gb_internal void map_clear (PtrMap *h); template gb_internal void map_grow (PtrMap *h); template gb_internal void map_rehash (PtrMap *h, isize new_count); template gb_internal void map_reserve (PtrMap *h, isize cap); #if PTR_MAP_ENABLE_MULTI_MAP // Mutlivalued map procedure template gb_internal PtrMapEntry * multi_map_find_first(PtrMap *h, K key); template gb_internal PtrMapEntry * multi_map_find_next (PtrMap *h, PtrMapEntry *e); template gb_internal isize multi_map_count (PtrMap *h, K key); template gb_internal void multi_map_get_all (PtrMap *h, K key, V *items); template gb_internal void multi_map_insert (PtrMap *h, K key, V const &value); template gb_internal void multi_map_remove (PtrMap *h, K key, PtrMapEntry *e); template gb_internal void multi_map_remove_all(PtrMap *h, K key); #endif gb_internal gbAllocator map_allocator(void) { return heap_allocator(); } template gb_internal gb_inline void map_init(PtrMap *h, isize capacity) { capacity = next_pow2_isize(capacity); map_reserve(h, capacity); } template gb_internal gb_inline void map_destroy(PtrMap *h) { gbAllocator a = map_allocator(); gb_free(a, h->hashes); gb_free(a, h->entries); } template gb_internal void map__resize_hashes(PtrMap *h, usize count) { h->hashes_count = cast(u32)resize_array_raw(&h->hashes, map_allocator(), h->hashes_count, count, MAP_CACHE_LINE_SIZE); } template gb_internal void map__reserve_entries(PtrMap *h, usize capacity) { h->entries_capacity = cast(u32)resize_array_raw(&h->entries, map_allocator(), h->entries_capacity, capacity, MAP_CACHE_LINE_SIZE); } template gb_internal MapIndex map__add_entry(PtrMap *h, K key) { PtrMapEntry e = {}; e.key = key; e.next = MAP_SENTINEL; if (h->count+1 >= h->entries_capacity) { map__reserve_entries(h, gb_max(h->entries_capacity*2, 4)); } h->entries[h->count++] = e; return cast(MapIndex)(h->count-1); } template gb_internal MapFindResult map__find(PtrMap *h, K key) { MapFindResult fr = {MAP_SENTINEL, MAP_SENTINEL, MAP_SENTINEL}; if (h->hashes_count == 0) { return fr; } u32 hash = ptr_map_hash_key(key); fr.hash_index = cast(MapIndex)(hash & (h->hashes_count-1)); fr.entry_index = h->hashes[fr.hash_index]; while (fr.entry_index != MAP_SENTINEL) { auto *entry = &h->entries[fr.entry_index]; if (entry->key == key) { return fr; } fr.entry_prev = fr.entry_index; fr.entry_index = entry->next; } return fr; } template gb_internal MapFindResult map__find_from_entry(PtrMap *h, PtrMapEntry *e) { MapFindResult fr = {MAP_SENTINEL, MAP_SENTINEL, MAP_SENTINEL}; if (h->hashes_count == 0) { return fr; } u32 hash = ptr_map_hash_key(e->key); fr.hash_index = cast(MapIndex)(hash & (h->hashes_count-1)); fr.entry_index = h->hashes[fr.hash_index]; while (fr.entry_index != MAP_SENTINEL) { if (&h->entries[fr.entry_index] == e) { return fr; } fr.entry_prev = fr.entry_index; fr.entry_index = h->entries[fr.entry_index].next; } return fr; } template gb_internal b32 map__full(PtrMap *h) { return 0.75f * h->hashes_count <= h->count; } template gb_internal gb_inline void map_grow(PtrMap *h) { isize new_count = gb_max(h->hashes_count<<1, 16); map_rehash(h, new_count); } template gb_internal void map_reset_entries(PtrMap *h) { for (usize i = 0; i < h->hashes_count; i++) { h->hashes[i] = MAP_SENTINEL; } for (usize i = 0; i < h->count; i++) { MapFindResult fr; PtrMapEntry *e = &h->entries[i]; e->next = MAP_SENTINEL; fr = map__find_from_entry(h, e); if (fr.entry_prev == MAP_SENTINEL) { h->hashes[fr.hash_index] = cast(MapIndex)i; } else { h->entries[fr.entry_prev].next = cast(MapIndex)i; } } } template gb_internal void map_reserve(PtrMap *h, isize cap) { if (h->count*2 < h->hashes_count) { return; } map__reserve_entries(h, cap); map__resize_hashes(h, cap*2); map_reset_entries(h); } template gb_internal void map_rehash(PtrMap *h, isize new_count) { map_reserve(h, new_count); } template gb_internal V *map_get(PtrMap *h, K key) { MapIndex hash_index = MAP_SENTINEL; MapIndex entry_prev = MAP_SENTINEL; MapIndex entry_index = MAP_SENTINEL; if (h->hashes_count != 0) { u32 hash = ptr_map_hash_key(key); hash_index = cast(MapIndex)(hash & (h->hashes_count-1)); entry_index = h->hashes[hash_index]; while (entry_index != MAP_SENTINEL) { auto *entry = &h->entries[entry_index]; if (entry->key == key) { return &entry->value; } entry_prev = entry_index; entry_index = entry->next; } } return nullptr; } template gb_internal V *map_try_get(PtrMap *h, K key, MapFindResult *fr_) { MapFindResult fr = {MAP_SENTINEL, MAP_SENTINEL, MAP_SENTINEL}; if (h->hashes_count != 0) { u32 hash = ptr_map_hash_key(key); fr.hash_index = cast(MapIndex)(hash & (h->hashes_count-1)); fr.entry_index = h->hashes[fr.hash_index]; while (fr.entry_index != MAP_SENTINEL) { auto *entry = &h->entries[fr.entry_index]; if (entry->key == key) { return &entry->value; } fr.entry_prev = fr.entry_index; fr.entry_index = entry->next; } } if (h->hashes_count == 0 || map__full(h)) { map_grow(h); } if (fr_) *fr_ = fr; return nullptr; } template gb_internal void map_set_internal_from_try_get(PtrMap *h, K key, V const &value, MapFindResult const &fr) { MapIndex index = map__add_entry(h, key); if (fr.entry_prev != MAP_SENTINEL) { h->entries[fr.entry_prev].next = index; } else { h->hashes[fr.hash_index] = index; } h->entries[index].value = value; } template gb_internal V &map_must_get(PtrMap *h, K key) { V *ptr = map_get(h, key); GB_ASSERT(ptr != nullptr); return *ptr; } template gb_internal void map_set(PtrMap *h, K key, V const &value) { MapIndex index; MapFindResult fr; if (h->hashes_count == 0) { map_grow(h); } fr = map__find(h, key); if (fr.entry_index != MAP_SENTINEL) { index = fr.entry_index; } else { index = map__add_entry(h, key); if (fr.entry_prev != MAP_SENTINEL) { h->entries[fr.entry_prev].next = index; } else { h->hashes[fr.hash_index] = index; } } h->entries[index].value = value; if (map__full(h)) { map_grow(h); } } // returns true if it previously existed template gb_internal bool map_set_if_not_previously_exists(PtrMap *h, K key, V const &value) { MapIndex index; MapFindResult fr; if (h->hashes_count == 0) { map_grow(h); } fr = map__find(h, key); if (fr.entry_index != MAP_SENTINEL) { return true; } else { index = map__add_entry(h, key); if (fr.entry_prev != MAP_SENTINEL) { h->entries[fr.entry_prev].next = index; } else { h->hashes[fr.hash_index] = index; } } h->entries[index].value = value; if (map__full(h)) { map_grow(h); } return false; } template gb_internal void map__erase(PtrMap *h, MapFindResult const &fr) { MapFindResult last; if (fr.entry_prev == MAP_SENTINEL) { h->hashes[fr.hash_index] = h->entries[fr.entry_index].next; } else { h->entries[fr.entry_prev].next = h->entries[fr.entry_index].next; } if (fr.entry_index == h->count-1) { h->count--; return; } h->entries[fr.entry_index] = h->entries[h->count-1]; h->count--; last = map__find(h, h->entries[fr.entry_index].key); if (last.entry_prev != MAP_SENTINEL) { h->entries[last.entry_prev].next = fr.entry_index; } else { h->hashes[last.hash_index] = fr.entry_index; } } template gb_internal void map_remove(PtrMap *h, K key) { MapFindResult fr = map__find(h, key); if (fr.entry_index != MAP_SENTINEL) { map__erase(h, fr); } } template gb_internal gb_inline void map_clear(PtrMap *h) { h->count = 0; for (usize i = 0; i < h->hashes_count; i++) { h->hashes[i] = MAP_SENTINEL; } } #if PTR_MAP_ENABLE_MULTI_MAP template gb_internal PtrMapEntry *multi_map_find_first(PtrMap *h, K key) { MapIndex i = map__find(h, key).entry_index; if (i == MAP_SENTINEL) { return nullptr; } return &h->entries[i]; } template gb_internal PtrMapEntry *multi_map_find_next(PtrMap *h, PtrMapEntry *e) { MapIndex i = e->next; while (i != MAP_SENTINEL) { if (h->entries[i].key == e->key) { return &h->entries[i]; } i = h->entries[i].next; } return nullptr; } template gb_internal isize multi_map_count(PtrMap *h, K key) { isize count = 0; PtrMapEntry *e = multi_map_find_first(h, key); while (e != nullptr) { count++; e = multi_map_find_next(h, e); } return count; } template gb_internal void multi_map_get_all(PtrMap *h, K key, V *items) { usize i = 0; PtrMapEntry *e = multi_map_find_first(h, key); while (e != nullptr) { items[i++] = e->value; e = multi_map_find_next(h, e); } } template gb_internal void multi_map_insert(PtrMap *h, K key, V const &value) { MapFindResult fr; MapIndex i; if (h->hashes_count == 0) { map_grow(h); } // Make fr = map__find(h, key); i = map__add_entry(h, key); if (fr.entry_prev == MAP_SENTINEL) { h->hashes[fr.hash_index] = i; } else { h->entries[fr.entry_prev].next = i; } h->entries[i].next = fr.entry_index; h->entries[i].value = value; // Grow if needed if (map__full(h)) { map_grow(h); } } template gb_internal void multi_map_remove(PtrMap *h, K key, PtrMapEntry *e) { MapFindResult fr = map__find_from_entry(h, e); if (fr.entry_index != MAP_SENTINEL) { map__erase(h, fr); } } template gb_internal void multi_map_remove_all(PtrMap *h, K key) { while (map_get(h, key) != nullptr) { map_remove(h, key); } } #endif template gb_internal PtrMapEntry *begin(PtrMap &m) { return m.entries; } template gb_internal PtrMapEntry const *begin(PtrMap const &m) { return m.entries; } template gb_internal PtrMapEntry *end(PtrMap &m) { return m.entries + m.count; } template gb_internal PtrMapEntry const *end(PtrMap const &m) { return m.entries + m.count; }