clox/table.c
2026-01-13 19:52:23 +00:00

173 lines
4 KiB
C

#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 = nullptr;
}
void freeTable(Table* table)
{
FREE_ARRAY(Entry, table->entries, table->capacity);
initTable(table);
}
static Entry* findEntry(Entry* entries, int capacity, ObjString* key)
{
auto index = key->hash & (capacity - 1);
Entry* tombstone = nullptr;
for (;;) {
Entry* entry = &entries[index];
if (entry->key == nullptr) {
if (IS_NIL(entry->value)) {
return tombstone != nullptr ? tombstone : entry;
} else {
if (tombstone == nullptr) {
tombstone = entry;
}
}
} else if (entry->key == key) {
return entry;
}
index = (index + 1) & (capacity - 1);
}
}
bool tableGet(Table* table, ObjString* key, Value* value)
{
if (table->count == 0) {
return false;
}
Entry* entry = findEntry(table->entries, table->capacity, key);
if (entry->key == nullptr) {
return false;
}
*value = entry->value;
return true;
}
static void adjustCapacity(Table* table, int capacity)
{
Entry* entries = ALLOCATE(Entry, capacity);
for (auto i = 0; i < capacity; i++) {
entries[i].key = nullptr;
entries[i].value = NIL_VAL;
}
table->count = 0;
for (auto i = 0; i < table->capacity; i++) {
Entry* entry = &table->entries[i];
if (entry->key == nullptr) {
continue;
}
Entry* dest = findEntry(entries, capacity, entry->key);
dest->key = entry->key;
dest->value = entry->value;
table->count++;
}
FREE_ARRAY(Entry, table->entries, table->capacity);
table->entries = entries;
table->capacity = capacity;
}
bool tableSet(Table* table, ObjString* key, Value value)
{
if (table->count + 1 > table->capacity * TABLE_MAX_LOAD) {
auto capacity = GROW_CAPACITY(table->capacity);
adjustCapacity(table, capacity);
}
Entry* entry = findEntry(table->entries, table->capacity, key);
auto isNewKey = entry->key == nullptr;
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;
}
Entry* entry = findEntry(table->entries, table->capacity, key);
if (entry->key == nullptr) {
return false;
}
entry->key = nullptr;
entry->value = BOOL_VAL(true);
return true;
}
void tableAddAll(Table* from, Table* to)
{
for (auto i = 0; i < from->capacity; i++) {
Entry* entry = &from->entries[i];
if (entry->key != nullptr) {
tableSet(to, entry->key, entry->value);
}
}
}
ObjString* tableFindString(Table* table, const char* chars, int length, uint32_t hash)
{
if (table->count == 0) {
return nullptr;
}
auto index = hash & (table->capacity - 1);
for (;;) {
Entry* entry = &table->entries[index];
if (entry->key == nullptr) {
if (IS_NIL(entry->value)) {
return nullptr;
}
} else if (entry->key->length == length && entry->key->hash == hash && memcmp(entry->key->chars, chars, length) == 0) {
return entry->key;
}
index = (index + 1) & (table->capacity - 1);
}
}
void tableRemoveWhite(Table* table)
{
for (auto i = 0; i < table->capacity; i++) {
Entry* entry = &table->entries[i];
if (entry->key != nullptr && !entry->key->obj.isMarked) {
tableDelete(table, entry->key);
}
}
}
void markTable(Table* table)
{
for (auto i = 0; i < table->capacity; i++) {
Entry* entry = &table->entries[i];
markObject((Obj*)entry->key);
markValue(entry->value);
}
}