diff --git a/src/checker.cpp b/src/checker.cpp index 125893d11..643673afe 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1135,8 +1135,7 @@ isize type_info_index(CheckerInfo *info, Type *type, bool error_on_failure) { mutex_lock(&info->type_info_mutex); isize entry_index = -1; - HashKey key = hash_type(type); - isize *found_entry_index = map_get(&info->type_info_map, key); + isize *found_entry_index = map_get(&info->type_info_map, type); if (found_entry_index) { entry_index = *found_entry_index; } @@ -1145,11 +1144,10 @@ isize type_info_index(CheckerInfo *info, Type *type, bool error_on_failure) { // TODO(bill): This is O(n) and can be very slow for_array(i, info->type_info_map.entries){ auto *e = &info->type_info_map.entries[i]; - Type *prev_type = cast(Type *)cast(uintptr)e->key.key; - if (are_types_identical(prev_type, type)) { + if (are_types_identical(e->key, type)) { entry_index = e->value; // NOTE(bill): Add it to the search map - map_set(&info->type_info_map, key, entry_index); + map_set(&info->type_info_map, type, entry_index); break; } } @@ -1484,7 +1482,7 @@ void add_type_info_type_internal(CheckerContext *c, Type *t) { add_type_info_dependency(c->decl, t); - auto found = map_get(&c->info->type_info_map, hash_type(t)); + auto found = map_get(&c->info->type_info_map, t); if (found != nullptr) { // Types have already been added return; @@ -1494,8 +1492,7 @@ void add_type_info_type_internal(CheckerContext *c, Type *t) { isize ti_index = -1; for_array(i, c->info->type_info_map.entries) { auto *e = &c->info->type_info_map.entries[i]; - Type *prev_type = cast(Type *)cast(uintptr)e->key.key; - if (are_types_identical(t, prev_type)) { + if (are_types_identical(t, e->key)) { // Duplicate entry ti_index = e->value; prev = true; @@ -1508,7 +1505,7 @@ void add_type_info_type_internal(CheckerContext *c, Type *t) { ti_index = c->info->type_info_types.count; array_add(&c->info->type_info_types, t); } - map_set(&c->checker->info.type_info_map, hash_type(t), ti_index); + map_set(&c->checker->info.type_info_map, t, ti_index); if (prev) { // NOTE(bill): If a previous one exists already, no need to continue diff --git a/src/checker.hpp b/src/checker.hpp index c65d766ba..e7f152eb2 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -321,7 +321,7 @@ struct CheckerInfo { BlockingMutex type_info_mutex; // NOT recursive Array type_info_types; - Map type_info_map; // Key: Type * + PtrMap type_info_map; BlockingMutex foreign_mutex; // NOT recursive StringMap foreigns; diff --git a/src/common.cpp b/src/common.cpp index 7af7026b9..5d7c87b6d 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -277,6 +277,7 @@ gb_global bool global_module_path_set = false; #include "string_map.cpp" #include "map.cpp" +#include "ptr_map.cpp" #include "ptr_set.cpp" #include "string_set.cpp" #include "priority_queue.cpp" diff --git a/src/ptr_map.cpp b/src/ptr_map.cpp new file mode 100644 index 000000000..af3cf86f9 --- /dev/null +++ b/src/ptr_map.cpp @@ -0,0 +1,318 @@ +#define PTR_MAP_ENABLE_MULTI_MAP 1 + + +template +struct PtrMapEntry { + static_assert(sizeof(K) == sizeof(void *), "Key size must be pointer size"); + + u32 hash; + MapIndex next; + K key; + V value; +}; + +template +struct PtrMap { + Slice hashes; + Array > entries; +}; + + +template +u32 ptr_map_hash_key(K key) { + return gb_fnv32a(&key, gb_size_of(key)); +} + +template void map_init (PtrMap *h, gbAllocator a, isize capacity = 16); +template void map_destroy (PtrMap *h); +template V * map_get (PtrMap *h, K key); +template void map_set (PtrMap *h, K key, V const &value); +template void map_remove (PtrMap *h, K key); +template void map_clear (PtrMap *h); +template void map_grow (PtrMap *h); +template void map_rehash (PtrMap *h, isize new_count); +template void map_reserve (PtrMap *h, isize cap); + +#if PTR_MAP_ENABLE_MULTI_MAP +// Mutlivalued map procedure +template PtrMapEntry * multi_map_find_first(PtrMap *h, K key); +template PtrMapEntry * multi_map_find_next (PtrMap *h, PtrMapEntry *e); + +template isize multi_map_count (PtrMap *h, K key); +template void multi_map_get_all (PtrMap *h, K key, V *items); +template void multi_map_insert (PtrMap *h, K key, V const &value); +template void multi_map_remove (PtrMap *h, K key, PtrMapEntry *e); +template void multi_map_remove_all(PtrMap *h, K key); +#endif + +template +gb_inline void map_init(PtrMap *h, gbAllocator a, isize capacity) { + capacity = next_pow2_isize(capacity); + slice_init(&h->hashes, a, capacity); + array_init(&h->entries, a, 0, capacity); + for (isize i = 0; i < capacity; i++) { + h->hashes.data[i] = MAP_SENTINEL; + } +} + +template +gb_inline void map_destroy(PtrMap *h) { + slice_free(&h->hashes, h->entries.allocator); + array_free(&h->entries); +} + +template +gb_internal MapIndex map__add_entry(PtrMap *h, K key) { + PtrMapEntry e = {}; + e.hash = ptr_map_hash_key(key); + e.key = key; + e.next = MAP_SENTINEL; + array_add(&h->entries, e); + return cast(MapIndex)(h->entries.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.data[fr.hash_index]; + while (fr.entry_index != MAP_SENTINEL) { + if (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 MapFindResult map__find_from_entry(PtrMap *h, PtrMapEntry *e) { + MapFindResult fr = {MAP_SENTINEL, MAP_SENTINEL, MAP_SENTINEL}; + if (h->hashes.count == 0) { + return fr; + } + fr.hash_index = cast(MapIndex)(e->hash & (h->hashes.count-1)); + fr.entry_index = h->hashes.data[fr.hash_index]; + while (fr.entry_index != MAP_SENTINEL) { + 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 map__full(PtrMap *h) { + return 0.75f * h->hashes.count <= h->entries.count; +} + +template +gb_inline void map_grow(PtrMap *h) { + isize new_count = gb_max(h->hashes.count<<1, 16); + map_rehash(h, new_count); +} + +template +void map_reset_entries(PtrMap *h) { + for (isize i = 0; i < h->hashes.count; i++) { + h->hashes.data[i] = MAP_SENTINEL; + } + for (isize i = 0; i < h->entries.count; i++) { + MapFindResult fr; + PtrMapEntry *e = &h->entries.data[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 +void map_reserve(PtrMap *h, isize cap) { + array_reserve(&h->entries, cap); + if (h->entries.count*2 < h->hashes.count) { + return; + } + slice_resize(&h->hashes, h->entries.allocator, cap*2); + map_reset_entries(h); +} + + +template +void map_rehash(PtrMap *h, isize new_count) { + map_reserve(h, new_count); +} + +template +V *map_get(PtrMap *h, K key) { + isize index = map__find(h, key).entry_index; + if (index != MAP_SENTINEL) { + return &h->entries.data[index].value; + } + return nullptr; +} + +template +V &map_must_get(PtrMap *h, K key) { + isize index = map__find(h, key).entry_index; + GB_ASSERT(index != MAP_SENTINEL); + return h->entries.data[index].value; +} + +template +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.data[fr.entry_prev].next = index; + } else { + h->hashes.data[fr.hash_index] = index; + } + } + h->entries.data[index].value = value; + + if (map__full(h)) { + map_grow(h); + } +} + + +template +void map__erase(PtrMap *h, MapFindResult const &fr) { + MapFindResult last; + if (fr.entry_prev == MAP_SENTINEL) { + 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]; + array_pop(&h->entries); + + last = map__find(h, h->entries.data[fr.entry_index].key); + if (last.entry_prev != MAP_SENTINEL) { + h->entries.data[last.entry_prev].next = fr.entry_index; + } else { + h->hashes.data[last.hash_index] = fr.entry_index; + } +} + +template +void map_remove(PtrMap *h, HashKey const &key) { + MapFindResult fr = map__find(h, key); + if (fr.entry_index != MAP_SENTINEL) { + map__erase(h, fr); + } +} + +template +gb_inline void map_clear(PtrMap *h) { + array_clear(&h->entries); + for (isize i = 0; i < h->hashes.count; i++) { + h->hashes.data[i] = MAP_SENTINEL; + } +} + + +#if PTR_MAP_ENABLE_MULTI_MAP +template +PtrMapEntry *multi_map_find_first(PtrMap *h, HashKey const &key) { + isize i = map__find(h, key).entry_index; + if (i == MAP_SENTINEL) { + return nullptr; + } + return &h->entries.data[i]; +} + +template +PtrMapEntry *multi_map_find_next(PtrMap *h, PtrMapEntry *e) { + isize i = e->next; + while (i != MAP_SENTINEL) { + if (hash_key_equal(h->entries.data[i].key, e->key)) { + return &h->entries.data[i]; + } + i = h->entries.data[i].next; + } + return nullptr; +} + +template +isize multi_map_count(PtrMap *h, HashKey const &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 +void multi_map_get_all(PtrMap *h, K key, V *items) { + isize 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 +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.data[fr.hash_index] = i; + } else { + h->entries.data[fr.entry_prev].next = i; + } + h->entries.data[i].next = fr.entry_index; + h->entries.data[i].value = value; + // Grow if needed + if (map__full(h)) { + map_grow(h); + } +} + +template +void multi_map_remove(PtrMap *h, HashKey const &key, PtrMapEntry *e) { + MapFindResult fr = map__find_from_entry(h, e); + if (fr.entry_index != MAP_SENTINEL) { + map__erase(h, fr); + } +} + +template +void multi_map_remove_all(PtrMap *h, HashKey const &key) { + while (map_get(h, key) != nullptr) { + map_remove(h, key); + } +} +#endif