|
|
@@ -0,0 +1,146 @@
|
|
|
+/**
|
|
|
+ ******************************************************************************
|
|
|
+ * @file : table.c
|
|
|
+ * @author : simon
|
|
|
+ * @brief : None
|
|
|
+ * @attention : None
|
|
|
+ * @date : 2023/8/24
|
|
|
+ ******************************************************************************
|
|
|
+ */
|
|
|
+
|
|
|
+#include <stdlib.h>
|
|
|
+#include <string.h>
|
|
|
+
|
|
|
+#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;
|
|
|
+}
|