#include #ifndef SCC_INIT_HASHMAP_SIZE #define SCC_INIT_HASHMAP_SIZE (32) #endif void scc_hashtable_init(scc_hashtable_t *ht) { scc_vec_init(ht->entries); ht->count = 0; ht->tombstone_count = 0; Assert(ht->key_cmp != NULL && ht->hash_func != NULL); } static int next_power_of_two(int n) { n--; n |= n >> 1; n |= n >> 2; n |= n >> 4; n |= n >> 8; n |= n >> 16; return n + 1; } static scc_hashtable_entry_t *find_entry(scc_hashtable_t *ht, const void *key, u32 hash) { if (ht->entries.cap == 0) return NULL; u32 index = hash & (ht->entries.cap - 1); // 容量是2的幂 u32 probe = 0; scc_hashtable_entry_t *tombstone = NULL; while (1) { scc_hashtable_entry_t *entry = &scc_vec_at(ht->entries, index); if (entry->state == ENTRY_EMPTY) { return tombstone ? tombstone : entry; } if (entry->state == ENTRY_TOMBSTONE) { if (!tombstone) tombstone = entry; } else if (entry->hash == hash && ht->key_cmp(entry->key, key) == 0) { return entry; } // Liner finding index = (index + 1) & (ht->entries.cap - 1); probe++; if (probe >= ht->entries.cap) break; } LOG_ERROR("hashset_find: hash table is full"); return NULL; } static void adjust_capacity(scc_hashtable_t *ht, usize new_cap) { new_cap = next_power_of_two(new_cap); Assert(new_cap >= ht->entries.cap); SCC_VEC(scc_hashtable_entry_t) old_entries; old_entries.data = ht->entries.data; old_entries.cap = ht->entries.cap; // Not used size but for gdb python extention debug ht->entries.size = new_cap; ht->entries.cap = new_cap; ht->entries.data = scc_realloc(NULL, new_cap * sizeof(scc_hashtable_entry_t)); scc_memset(ht->entries.data, 0, new_cap * sizeof(scc_hashtable_entry_t)); // rehash the all of the old data for (usize i = 0; i < old_entries.cap; i++) { scc_hashtable_entry_t *entry = &scc_vec_at(old_entries, i); if (entry->state == ENTRY_ACTIVE) { scc_hashtable_entry_t *dest = find_entry(ht, entry->key, entry->hash); *dest = *entry; } } scc_vec_free(old_entries); ht->tombstone_count = 0; } void *scc_hashtable_set(scc_hashtable_t *ht, const void *key, void *value) { if (ht->count + ht->tombstone_count >= ht->entries.cap * 0.75) { int new_cap = ht->entries.cap < SCC_INIT_HASHMAP_SIZE ? SCC_INIT_HASHMAP_SIZE : ht->entries.cap * 2; adjust_capacity(ht, new_cap); } u32 hash = ht->hash_func(key); scc_hashtable_entry_t *entry = find_entry(ht, key, hash); void *old_value = NULL; if (entry->state == ENTRY_ACTIVE) { old_value = entry->value; } else { if (entry->state == ENTRY_TOMBSTONE) ht->tombstone_count--; ht->count++; } entry->key = key; entry->value = value; entry->hash = hash; entry->state = ENTRY_ACTIVE; return old_value; } void *scc_hashtable_get(scc_hashtable_t *ht, const void *key) { if (ht->entries.cap == 0) return NULL; u32 hash = ht->hash_func(key); scc_hashtable_entry_t *entry = find_entry(ht, key, hash); return (entry && entry->state == ENTRY_ACTIVE) ? entry->value : NULL; } void *scc_hashtable_del(scc_hashtable_t *ht, const void *key) { if (ht->entries.cap == 0) return NULL; u32 hash = ht->hash_func(key); scc_hashtable_entry_t *entry = find_entry(ht, key, hash); if (entry == NULL || entry->state != ENTRY_ACTIVE) return NULL; void *value = entry->value; entry->state = ENTRY_TOMBSTONE; ht->count--; ht->tombstone_count++; return value; } void scc_hashtable_drop(scc_hashtable_t *ht) { scc_vec_free(ht->entries); ht->count = 0; ht->tombstone_count = 0; } void scc_hashmap_foreach(scc_hashtable_t *ht, scc_hashtable_iter_fn iter_func, void *context) { for (usize i = 0; i < ht->entries.cap; i++) { scc_hashtable_entry_t *entry = &scc_vec_at(ht->entries, i); if (entry->state == ENTRY_ACTIVE) { if (!iter_func(entry->key, entry->value, context)) { break; // enable callback function terminal the iter } } } }