template struct PtrSetEntry { T ptr; MapIndex next; }; template struct PtrSet { Slice hashes; Array> entries; }; template void ptr_set_init (PtrSet *s, gbAllocator a, isize capacity = 16); template void ptr_set_destroy(PtrSet *s); template T ptr_set_add (PtrSet *s, T ptr); template bool ptr_set_update (PtrSet *s, T ptr); // returns true if it previously existsed template bool ptr_set_exists (PtrSet *s, T ptr); template void ptr_set_remove (PtrSet *s, T ptr); template void ptr_set_clear (PtrSet *s); template void ptr_set_grow (PtrSet *s); template void ptr_set_rehash (PtrSet *s, isize new_count); template void ptr_set_reserve(PtrSet *h, isize cap); template void ptr_set_init(PtrSet *s, gbAllocator a, isize capacity) { if (capacity != 0) { capacity = next_pow2_isize(gb_max(16, capacity)); } slice_init(&s->hashes, a, capacity); array_init(&s->entries, a, 0, capacity); for (isize i = 0; i < capacity; i++) { s->hashes.data[i] = MAP_SENTINEL; } } template void ptr_set_destroy(PtrSet *s) { slice_free(&s->hashes, s->entries.allocator); array_free(&s->entries); } template gb_internal MapIndex ptr_set__add_entry(PtrSet *s, T ptr) { PtrSetEntry e = {}; e.ptr = ptr; e.next = MAP_SENTINEL; array_add(&s->entries, e); return cast(MapIndex)(s->entries.count-1); } template gb_internal MapFindResult ptr_set__find(PtrSet *s, T ptr) { MapFindResult fr = {MAP_SENTINEL, MAP_SENTINEL, MAP_SENTINEL}; if (s->hashes.count != 0) { u32 hash = ptr_map_hash_key(ptr); fr.hash_index = cast(MapIndex)(hash & (s->hashes.count-1)); fr.entry_index = s->hashes.data[fr.hash_index]; while (fr.entry_index != MAP_SENTINEL) { if (s->entries.data[fr.entry_index].ptr == ptr) { return fr; } fr.entry_prev = fr.entry_index; fr.entry_index = s->entries.data[fr.entry_index].next; } } return fr; } template gb_internal MapFindResult ptr_set__find_from_entry(PtrSet *s, PtrSetEntry *e) { MapFindResult fr = {MAP_SENTINEL, MAP_SENTINEL, MAP_SENTINEL}; if (s->hashes.count != 0) { u32 hash = ptr_map_hash_key(e->ptr); fr.hash_index = cast(MapIndex)(hash & (s->hashes.count-1)); fr.entry_index = s->hashes.data[fr.hash_index]; while (fr.entry_index != MAP_SENTINEL) { if (&s->entries.data[fr.entry_index] == e) { return fr; } fr.entry_prev = fr.entry_index; fr.entry_index = s->entries.data[fr.entry_index].next; } } return fr; } template gb_internal bool ptr_set__full(PtrSet *s) { return 0.75f * s->hashes.count <= s->entries.count; } template gb_inline void ptr_set_grow(PtrSet *s) { isize new_count = gb_max(s->hashes.count<<1, 16); ptr_set_rehash(s, new_count); } template void ptr_set_reset_entries(PtrSet *s) { for (isize i = 0; i < s->hashes.count; i++) { s->hashes.data[i] = MAP_SENTINEL; } for (isize i = 0; i < s->entries.count; i++) { MapFindResult fr; PtrSetEntry *e = &s->entries.data[i]; e->next = MAP_SENTINEL; fr = ptr_set__find_from_entry(s, e); if (fr.entry_prev == MAP_SENTINEL) { s->hashes[fr.hash_index] = cast(MapIndex)i; } else { s->entries[fr.entry_prev].next = cast(MapIndex)i; } } } template void ptr_set_reserve(PtrSet *s, isize cap) { array_reserve(&s->entries, cap); if (s->entries.count*2 < s->hashes.count) { return; } slice_resize(&s->hashes, s->entries.allocator, cap*2); ptr_set_reset_entries(s); } template void ptr_set_rehash(PtrSet *s, isize new_count) { ptr_set_reserve(s, new_count); } template gb_inline bool ptr_set_exists(PtrSet *s, T ptr) { isize index = ptr_set__find(s, ptr).entry_index; return index != MAP_SENTINEL; } template gb_inline isize ptr_entry_index(PtrSet *s, T ptr) { isize index = ptr_set__find(s, ptr).entry_index; if (index != MAP_SENTINEL) { return index; } return -1; } // Returns true if it already exists template T ptr_set_add(PtrSet *s, T ptr) { MapIndex index; MapFindResult fr; if (s->hashes.count == 0) { ptr_set_grow(s); } fr = ptr_set__find(s, ptr); if (fr.entry_index == MAP_SENTINEL) { index = ptr_set__add_entry(s, ptr); if (fr.entry_prev != MAP_SENTINEL) { s->entries.data[fr.entry_prev].next = index; } else { s->hashes.data[fr.hash_index] = index; } } if (ptr_set__full(s)) { ptr_set_grow(s); } return ptr; } template bool ptr_set_update(PtrSet *s, T ptr) { // returns true if it previously existsed bool exists = false; MapIndex index; MapFindResult fr; if (s->hashes.count == 0) { ptr_set_grow(s); } fr = ptr_set__find(s, ptr); if (fr.entry_index != MAP_SENTINEL) { exists = true; } else { index = ptr_set__add_entry(s, ptr); if (fr.entry_prev != MAP_SENTINEL) { s->entries.data[fr.entry_prev].next = index; } else { s->hashes.data[fr.hash_index] = index; } } if (ptr_set__full(s)) { ptr_set_grow(s); } return exists; } template void ptr_set__erase(PtrSet *s, MapFindResult fr) { MapFindResult last; if (fr.entry_prev == MAP_SENTINEL) { s->hashes.data[fr.hash_index] = s->entries.data[fr.entry_index].next; } else { s->entries.data[fr.entry_prev].next = s->entries.data[fr.entry_index].next; } if (cast(isize)fr.entry_index == s->entries.count-1) { array_pop(&s->entries); return; } s->entries.data[fr.entry_index] = s->entries.data[s->entries.count-1]; last = ptr_set__find(s, s->entries.data[fr.entry_index].ptr); if (last.entry_prev != MAP_SENTINEL) { s->entries.data[last.entry_prev].next = fr.entry_index; } else { s->hashes.data[last.hash_index] = fr.entry_index; } } template void ptr_set_remove(PtrSet *s, T ptr) { MapFindResult fr = ptr_set__find(s, ptr); if (fr.entry_index != MAP_SENTINEL) { ptr_set__erase(s, fr); } } template gb_inline void ptr_set_clear(PtrSet *s) { array_clear(&s->entries); for (isize i = 0; i < s->hashes.count; i++) { s->hashes.data[i] = MAP_SENTINEL; } }