/** ****************************************************************************** * @file : table.c * @author : simon * @brief : None * @attention : None * @date : 2023/8/24 ****************************************************************************** */ #include #include #include "memory.h" #include "object.h" #include "table.h" #include "value.h" #define TABLE_MAX_LOAD 0.75 void initTable(Table *table) { table->count = 0; table->capacity = 0; table->entries = NULL; } void freeTable(Table *table) { FREE_ARRAY(Entry, table->entries, table->capacity); initTable(table); } /// 查找位置,hash 冲突使用 线性探测(Linear probing) 策略 /// \param entries /// \param capacity /// \param key /// \return 应该设置的位置 static Entry *findEntry(Entry *entries, int capacity, ObjString *key) { uint32_t index = key->hash % capacity; Entry *tombstone = NULL; for (;;) { Entry *entry = &entries[index]; if (entry->key == NULL) { if (IS_NIL(entry->value)) { // Empty entry. return tombstone != NULL ? tombstone : entry; } else { // We found a tombstone. if (tombstone == NULL) tombstone = entry; } } else if (entry->key == key) { // We found the key. return entry; } index = (index + 1) % capacity; } } /// Allocating and resizing /// \param table /// \param capacity void adjustCapacity(Table *table, int capacity) { Entry *entries = ALLOCATE(Entry, capacity); for (int i = 0; i < capacity; i++) { entries[i].key = NULL; entries[i].value = NIL_VAL; } // 原来的 entry 重新放置到新位置 table->count = 0; for (int i = 0; i < table->capacity; i++) { Entry *src = &table->entries[i]; if (src->key == NULL) continue; Entry *dest = findEntry(entries, capacity, src->key); dest->key = src->key; dest->value = src->value; table->count++; } FREE_ARRAY(Entry, table->entries, table->capacity); table->entries = entries; table->capacity = capacity; } bool tableGet(Table *table, ObjString *key, Value *value) { // the table is completely empty if (table->count == 0) return false; Entry *entry = findEntry(table->entries, table->capacity, key); if (entry->key == NULL) return false; *value = entry->value; return true; } bool tableSet(Table *table, ObjString *key, Value value) { // 容量少于75% 就开始扩容 if (table->count + 1 > table->capacity * TABLE_MAX_LOAD) { int capacity = GROW_CAPACITY(table->capacity); adjustCapacity(table, capacity); } Entry *entry = findEntry(table->entries, table->capacity, key); bool isNewKey = entry->key == NULL; if (isNewKey && IS_NIL(entry->value)) table->count++; entry->key = key; entry->value = value; return isNewKey; } bool tableDelete(Table *table, ObjString *key) { if (table->count == 0) return false; // Find the entry Entry *entry = findEntry(table->entries, table->capacity, key); if (entry->key == NULL) return false; // Place a tombstone in the entry. entry->key = NULL; entry->value = BOOL_VAL(true); return true; } void tableAddAll(Table *from, Table *to) { for (int i = 0; i < from->capacity; i++) { Entry *entry = &from->entries[i]; if (entry->key != NULL) { tableSet(to, entry->key, entry->value); } } } ObjString *tableFindString(Table *table, const char *chars, int length, uint32_t hash) { if (table->count == 0) return NULL; uint32_t index = hash % table->capacity; for (;;) { Entry *entry = &table->entries[index]; if (entry->key == NULL) { // Stop if we find an empty non-tombstone entry. if (IS_NIL(entry->value)) return NULL; } else if (entry->key->length == length && entry->key->hash == hash && memcmp(entry->key->chars, chars, length) == 0) { // We found it. return entry->key; } index = (index + 1) % table->capacity; } return NULL; }