2019-06-02 14:58:15 -05:00
|
|
|
#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);
|
|
|
|
}
|
|
|
|
|
|
|
|
static Entry* findEntry(Entry* entries, int capacity, Value key) {
|
|
|
|
uint32_t index = hashValue(key) % capacity;
|
|
|
|
Entry* tombstone = NULL;
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
Entry* entry = &entries[index];
|
|
|
|
|
|
|
|
if (IS_EMPTY(entry->key)) {
|
|
|
|
if (IS_NIL(entry->value)) {
|
|
|
|
// Empty entry.
|
|
|
|
return tombstone != NULL ? tombstone : entry;
|
|
|
|
} else {
|
|
|
|
// We found a tombstone.
|
|
|
|
if (tombstone == NULL) tombstone = entry;
|
|
|
|
}
|
|
|
|
} else if (valuesEqual(key, entry->key)) {
|
|
|
|
// We found the key.
|
|
|
|
return entry;
|
|
|
|
}
|
|
|
|
|
|
|
|
index = (index + 1) % capacity;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void adjustCapacity(Table* table, int capacity) {
|
|
|
|
Entry* entries = ALLOCATE(Entry, capacity);
|
|
|
|
for (int i = 0; i < capacity; i++) {
|
|
|
|
entries[i].key = EMPTY_VAL;
|
|
|
|
entries[i].value = NIL_VAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
table->count = 0;
|
|
|
|
for (int i = 0; i < table->capacity; i++) {
|
|
|
|
Entry* entry = &table->entries[i];
|
|
|
|
if (IS_EMPTY(entry->key)) 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 tableGet(Table* table, Value key, Value* value) {
|
|
|
|
if (table->entries == NULL) return false;
|
|
|
|
|
|
|
|
Entry* entry = findEntry(table->entries, table->capacity, key);
|
|
|
|
if (IS_EMPTY(entry->key)) return false;
|
|
|
|
|
|
|
|
*value = entry->value;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool tableSet(Table* table, Value key, Value value) {
|
|
|
|
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 = IS_EMPTY(entry->key);
|
|
|
|
if (isNewKey && IS_NIL(entry->value)) table->count++;
|
|
|
|
|
|
|
|
entry->key = key;
|
|
|
|
entry->value = value;
|
|
|
|
return isNewKey;
|
|
|
|
}
|
|
|
|
|
2019-06-03 07:02:03 -05:00
|
|
|
bool tableDelete(Table* table, Value key) {
|
2019-06-02 14:58:15 -05:00
|
|
|
if (table->count == 0) return false;
|
|
|
|
|
|
|
|
// Find the entry.
|
|
|
|
Entry* entry = findEntry(table->entries, table->capacity, key);
|
|
|
|
if (IS_EMPTY(entry->key)) return false;
|
|
|
|
|
|
|
|
// Place a tombstone in the entry.
|
|
|
|
entry->key = EMPTY_VAL;
|
|
|
|
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 (!IS_EMPTY(entry->key)) {
|
|
|
|
tableSet(to, entry->key, entry->value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ObjString* tableFindString(Table* table, const char* chars, int length, uint32_t hash) {
|
|
|
|
// If the table is empty, we definitely won't find it.
|
|
|
|
if (table->entries == NULL) return NULL;
|
|
|
|
|
|
|
|
uint32_t index = hash % table->capacity;
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
Entry* entry = &table->entries[index];
|
|
|
|
|
|
|
|
if (IS_EMPTY(entry->key)) return NULL;
|
|
|
|
ObjString* string = AS_STRING(entry->key);
|
|
|
|
if (string->length == length && memcmp(string->chars, chars, length) == 0) {
|
|
|
|
// We found it.
|
|
|
|
return string;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try the next slot.
|
|
|
|
index = (index + 1) % table->capacity;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
2019-06-03 07:02:03 -05:00
|
|
|
|
|
|
|
static int tableLength(Table* table) {
|
|
|
|
int length = 0;
|
|
|
|
for (int i = 0; i < table->capacity; i++) {
|
|
|
|
Entry entry = table->entries[i];
|
|
|
|
if (!IS_EMPTY(entry.key)) length++;
|
|
|
|
}
|
|
|
|
return length;
|
|
|
|
}
|
|
|
|
|
|
|
|
Entry** getEntries(Table* table) {
|
|
|
|
int length = tableLength(table);
|
|
|
|
Entry** entries = malloc(sizeof(Entry*)*(length+1));
|
|
|
|
entries[length]=NULL;
|
|
|
|
int j=0;
|
|
|
|
for (int i=0; i < table->capacity; i++) {
|
|
|
|
Entry* entry = &table->entries[i];
|
|
|
|
if (!IS_EMPTY(entry->key)) {
|
|
|
|
entries[j]=entry;
|
|
|
|
j++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return entries;
|
|
|
|
}
|