|
| 1 | +/* src/runtime/table.c */ |
| 2 | +#include <stdlib.h> |
| 3 | +#include <string.h> |
| 4 | + |
| 5 | +#include "../include/memory.h" |
| 6 | +#include "../include/object.h" |
| 7 | +#include "../include/table.h" |
| 8 | +#include "../include/value.h" |
| 9 | + |
| 10 | +#define TABLE_MAX_LOAD 0.75 |
| 11 | + |
| 12 | +void initTable(Table* table) { |
| 13 | + table->count = 0; |
| 14 | + table->capacity = 0; |
| 15 | + table->entries = NULL; |
| 16 | +} |
| 17 | + |
| 18 | +void freeTable(Table* table) { |
| 19 | + FREE_ARRAY(Entry, table->entries, table->capacity); |
| 20 | + initTable(table); |
| 21 | +} |
| 22 | + |
| 23 | +static Entry* findEntry(Entry* entries, int capacity, ObjString* key) { |
| 24 | + uint32_t index = key->hash % capacity; |
| 25 | + Entry* tombstone = NULL; |
| 26 | + |
| 27 | + for (;;) { |
| 28 | + Entry* entry = &entries[index]; |
| 29 | + if (entry->key == NULL) { |
| 30 | + if (IS_NIL(entry->value)) { |
| 31 | + return tombstone != NULL ? tombstone : entry; |
| 32 | + } else { |
| 33 | + if (tombstone == NULL) tombstone = entry; |
| 34 | + } |
| 35 | + } else if (entry->key == key) { |
| 36 | + return entry; |
| 37 | + } |
| 38 | + |
| 39 | + index = (index + 1) % capacity; |
| 40 | + } |
| 41 | +} |
| 42 | + |
| 43 | +static void adjustCapacity(Table* table, int capacity) { |
| 44 | + Entry* entries = ALLOCATE(Entry, capacity); |
| 45 | + for (int i = 0; i < capacity; i++) { |
| 46 | + entries[i].key = NULL; |
| 47 | + entries[i].value = NIL_VAL; |
| 48 | + } |
| 49 | + |
| 50 | + table->count = 0; |
| 51 | + for (int i = 0; i < table->capacity; i++) { |
| 52 | + Entry* entry = &table->entries[i]; |
| 53 | + if (entry->key == NULL) continue; |
| 54 | + |
| 55 | + Entry* dest = findEntry(entries, capacity, entry->key); |
| 56 | + dest->key = entry->key; |
| 57 | + dest->value = entry->value; |
| 58 | + table->count++; |
| 59 | + } |
| 60 | + |
| 61 | + FREE_ARRAY(Entry, table->entries, table->capacity); |
| 62 | + table->entries = entries; |
| 63 | + table->capacity = capacity; |
| 64 | +} |
| 65 | + |
| 66 | +bool tableGet(Table* table, ObjString* key, Value* value) { |
| 67 | + if (table->count == 0) return false; |
| 68 | + |
| 69 | + Entry* entry = findEntry(table->entries, table->capacity, key); |
| 70 | + if (entry->key == NULL) return false; |
| 71 | + |
| 72 | + *value = entry->value; |
| 73 | + return true; |
| 74 | +} |
| 75 | + |
| 76 | +bool tableSet(Table* table, ObjString* key, Value value) { |
| 77 | + if (table->count + 1 > table->capacity * TABLE_MAX_LOAD) { |
| 78 | + int capacity = GROW_CAPACITY(table->capacity); |
| 79 | + adjustCapacity(table, capacity); |
| 80 | + } |
| 81 | + |
| 82 | + Entry* entry = findEntry(table->entries, table->capacity, key); |
| 83 | + bool isNewKey = entry->key == NULL; |
| 84 | + if (isNewKey && IS_NIL(entry->value)) table->count++; |
| 85 | + |
| 86 | + entry->key = key; |
| 87 | + entry->value = value; |
| 88 | + return isNewKey; |
| 89 | +} |
| 90 | + |
| 91 | +bool tableDelete(Table* table, ObjString* key) { |
| 92 | + if (table->count == 0) return false; |
| 93 | + |
| 94 | + Entry* entry = findEntry(table->entries, table->capacity, key); |
| 95 | + if (entry->key == NULL) return false; |
| 96 | + |
| 97 | + entry->key = NULL; |
| 98 | + entry->value = BOOL_VAL(true); |
| 99 | + return true; |
| 100 | +} |
| 101 | + |
| 102 | +void tableAddAll(Table* from, Table* to) { |
| 103 | + for (int i = 0; i < from->capacity; i++) { |
| 104 | + Entry* entry = &from->entries[i]; |
| 105 | + if (entry->key != NULL) { |
| 106 | + tableSet(to, entry->key, entry->value); |
| 107 | + } |
| 108 | + } |
| 109 | +} |
| 110 | + |
| 111 | +ObjString* tableFindString(Table* table, const char* chars, int length, uint32_t hash) { |
| 112 | + if (table->count == 0) return NULL; |
| 113 | + |
| 114 | + uint32_t index = hash % table->capacity; |
| 115 | + for (;;) { |
| 116 | + Entry* entry = &table->entries[index]; |
| 117 | + if (entry->key == NULL) { |
| 118 | + if (IS_NIL(entry->value)) return NULL; |
| 119 | + } else if (entry->key->length == length && |
| 120 | + entry->key->hash == hash && |
| 121 | + memcmp(entry->key->chars, chars, length) == 0) { |
| 122 | + return entry->key; |
| 123 | + } |
| 124 | + |
| 125 | + index = (index + 1) % table->capacity; |
| 126 | + } |
| 127 | +} |
| 128 | + |
0 commit comments