128 lines
3.7 KiB
C
128 lines
3.7 KiB
C
#include <stdio.h>
|
|
#include <string.h>
|
|
#include "kuroko.h"
|
|
#include "object.h"
|
|
#include "value.h"
|
|
#include "memory.h"
|
|
#include "table.h"
|
|
|
|
#define TABLE_MAX_LOAD 0.75
|
|
|
|
void krk_initTable(KrkTable * table) {
|
|
table->count = 0;
|
|
table->capacity = 0;
|
|
table->entries = NULL;
|
|
}
|
|
|
|
void krk_freeTable(KrkTable * table) {
|
|
FREE_ARRAY(KrkTableEntry, table->entries, table->capacity);
|
|
krk_initTable(table);
|
|
}
|
|
|
|
static uint32_t hashValue(KrkValue value) {
|
|
if (IS_STRING(value)) return (AS_STRING(value))->hash;
|
|
if (IS_INTEGER(value)) return (uint32_t)(AS_INTEGER(value));
|
|
if (IS_FLOATING(value)) return (uint32_t)(AS_FLOATING(value) * 1000); /* arbitrary; what's a good way to hash floats? */
|
|
if (IS_BOOLEAN(value)) return (uint32_t)(AS_BOOLEAN(value));
|
|
if (IS_NONE(value)) return 0;
|
|
|
|
/* Warn? */
|
|
return 0;
|
|
}
|
|
|
|
static KrkTableEntry * findEntry(KrkTableEntry * entries, size_t capacity, KrkValue key) {
|
|
uint32_t index = hashValue(key) % capacity;
|
|
KrkTableEntry * tombstone = NULL;
|
|
for (;;) {
|
|
KrkTableEntry * entry = &entries[index];
|
|
if (entry->key.type == VAL_NONE) {
|
|
if (IS_NONE(entry->value)) {
|
|
return tombstone != NULL ? tombstone : entry;
|
|
} else {
|
|
if (tombstone == NULL) tombstone = entry;
|
|
}
|
|
} else if (krk_valuesEqual(entry->key, key)) {
|
|
return entry;
|
|
}
|
|
index = (index + 1) % capacity;
|
|
}
|
|
}
|
|
|
|
static void adjustCapacity(KrkTable * table, size_t capacity) {
|
|
KrkTableEntry * entries = ALLOCATE(KrkTableEntry, capacity);
|
|
for (size_t i = 0; i < capacity; ++i) {
|
|
entries[i].key = NONE_VAL();
|
|
entries[i].value = NONE_VAL();
|
|
}
|
|
|
|
table->count = 0;
|
|
for (size_t i = 0; i < table->capacity; ++i) {
|
|
KrkTableEntry * entry = &table->entries[i];
|
|
if (entry->key.type == VAL_NONE) continue;
|
|
KrkTableEntry * dest = findEntry(entries, capacity, entry->key);
|
|
dest->key = entry->key;
|
|
dest->value = entry->value;
|
|
table->count++;
|
|
}
|
|
|
|
FREE_ARRAY(KrkTableEntry, table->entries, table->capacity);
|
|
table->entries = entries;
|
|
table->capacity = capacity;
|
|
}
|
|
|
|
int krk_tableSet(KrkTable * table, KrkValue key, KrkValue value) {
|
|
if (table->count + 1 > table->capacity * TABLE_MAX_LOAD) {
|
|
size_t capacity = GROW_CAPACITY(table->capacity);
|
|
adjustCapacity(table, capacity);
|
|
}
|
|
KrkTableEntry * entry = findEntry(table->entries, table->capacity, key);
|
|
int isNewKey = entry->key.type == VAL_NONE;
|
|
if (isNewKey && IS_NONE(entry->value)) table->count++;
|
|
entry->key = key;
|
|
entry->value = value;
|
|
return isNewKey;
|
|
}
|
|
|
|
void krk_tableAddAll(KrkTable * from, KrkTable * to) {
|
|
for (size_t i = 0; i < from->capacity; ++i) {
|
|
KrkTableEntry * entry = &from->entries[i];
|
|
if (entry->key.type != VAL_NONE) {
|
|
krk_tableSet(to, entry->key, entry->value);
|
|
}
|
|
}
|
|
}
|
|
|
|
int krk_tableGet(KrkTable * table, KrkValue key, KrkValue * value) {
|
|
if (table->count == 0) return 0;
|
|
KrkTableEntry * entry = findEntry(table->entries, table->capacity, key);
|
|
if (entry->key.type == VAL_NONE) return 0;
|
|
*value = entry->value;
|
|
return 1;
|
|
}
|
|
|
|
int krk_tableDelete(KrkTable * table, KrkValue key) {
|
|
if (table->count == 0) return 0;
|
|
KrkTableEntry * entry = findEntry(table->entries, table->capacity, key);
|
|
if (entry->key.type == VAL_NONE) return 0;
|
|
entry->key = NONE_VAL();
|
|
entry->value = BOOLEAN_VAL(1);
|
|
return 1;
|
|
}
|
|
|
|
KrkString * krk_tableFindString(KrkTable * table, const char * chars, size_t length, uint32_t hash) {
|
|
if (table->count == 0) return NULL;
|
|
|
|
uint32_t index = hash % table->capacity;
|
|
for (;;) {
|
|
KrkTableEntry * entry = &table->entries[index];
|
|
if (entry->key.type == VAL_NONE) {
|
|
if (IS_NONE(entry->value)) return NULL;
|
|
} else if (AS_STRING(entry->key)->length == length &&
|
|
AS_STRING(entry->key)->hash == hash &&
|
|
memcmp(AS_STRING(entry->key)->chars, chars, length) == 0) {
|
|
return AS_STRING(entry->key);
|
|
}
|
|
index = (index + 1) % table->capacity;
|
|
}
|
|
}
|