#include "hash.h" #include "env.h" #include "pebblisp.h" #include "object.h" #include #include size_t addStripped(struct ObjectTable* table, char* name, struct StrippedObject object); Object deStrip(struct StrippedObject object) { // Garbage .forward data may be safe here, but since usage of that data is // currently in flux, it should take the more cautious route for now. return (Object) { .type = object.type, .string = object.data, .forward = NULL, }; } struct ObjectTable buildTable(size_t capacity) { struct ObjectTable table = { .count = 0, .capacity = capacity, .elements = capacity ? malloc(sizeof(struct EnvElement) * capacity) : NULL, }; for (int i = 0; i < table.capacity; i++) { table.elements[i].symbol = NULL; } return table; } void deleteTable(struct ObjectTable* table) { for (int i = 0; i < table->capacity; i++) { if (table->elements[i].symbol) { free(table->elements[i].symbol); Object deStripped = deStrip(table->elements[i].object); cleanObject(&deStripped); } } free(table->elements); } #ifndef HASHLESS_ENV static size_t hash(const char* str, struct ObjectTable* table) { size_t hash = 5381; char c; while ((c = *str++)) { hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ } return hash; } void extendTable(struct ObjectTable* table) { if (table->capacity >= (table->count + 1) * 2) { return; } struct ObjectTable newTable = buildTable(table->capacity ? table->capacity * 2 : 4); for (int i = 0; i < table->capacity; i++) { if (table->elements[i].symbol) { addStripped(&newTable, table->elements[i].symbol, table->elements[i].object); } } free(table->elements); *table = newTable; } struct StrippedObject* getFromTable(struct ObjectTable* table, const char* name) { if (table->capacity == 0) { return NULL; } size_t h = hash(name, table) % table->capacity; while (table->elements[h].symbol) { if (strcmp(name, table->elements[h].symbol) == 0) { return &table->elements[h].object; } h = (h + 1) % table->capacity; } return NULL; } #else void extendTable(struct ObjectTable* table) { if (table->count == (table->capacity - 1)) { struct EnvElement* oldElements = table->elements; size_t oldCapacity = table->capacity; table->capacity *= 2; table->elements = malloc(sizeof(struct EnvElement) * table->capacity); for (int i = 0; i < oldCapacity; i++) { table->elements[i] = oldElements[i]; } for (size_t i = oldCapacity; i < table->capacity; i++) { table->elements[i].symbol = NULL; } free(oldElements); } } struct StrippedObject* getFromTable(struct ObjectTable* table, const char* name) { for (size_t i = 0; i < table->count; i++) { if (strcmp(name, table->elements[i].symbol) == 0) { return &table->elements[i].object; } } return NULL; } static size_t hash(unused const char* str, struct ObjectTable* table) { return table->count; } #endif size_t addStripped(struct ObjectTable* table, char* name, struct StrippedObject object) { extendTable(table); size_t initial = hash(name, table); size_t h = initial % table->capacity; while (table->elements[h].symbol && strcmp(table->elements[h].symbol, name) != 0) { h = (h + 1) % table->capacity; } if (table->elements[h].symbol) { Object previous = deStrip(table->elements[h].object); cleanObject(&previous); free(table->elements[h].symbol); } table->elements[h].symbol = name; table->elements[h].object = object; table->count += 1; return initial; } /// /// \param table /// \param name Should be a new allocation /// \param object Should already be cloned size_t addToTable(struct ObjectTable* table, char* name, Object object) { return addStripped(table, name, (struct StrippedObject) { .data = object.data, .type = object.type, }); } #ifdef STANDALONE int isHash(const Object test) { return test.type == TYPE_HASH_TABLE; } Object buildHashTable(Object* params, int length, struct Environment* env) { long capacity = 16; if (length > 0 && params[0].type == TYPE_NUMBER) { capacity = params[0].number; } Object table = newObject(TYPE_HASH_TABLE); table.table = malloc(sizeof(struct ObjectTableObject)); table.table->table = buildTable(capacity); table.table->refs = 1; return table; } Object addToHashTable(Object* params, int length, struct Environment* env) { checkTypes(addToHashTable) Object name = params[1]; Object add = params[2]; struct ObjectTable* table = ¶ms[0].table->table; addToTable(table, strdup(name.string), cloneObject(add)); return numberObject(0); } Object getFromHashTable(Object* params, int length, struct Environment* env) { checkTypes(getFromHashTable) struct ObjectTable* table = ¶ms[0].table->table; struct StrippedObject* fetched = getFromTable(table, params[1].string); if (fetched) { return cloneObject(deStrip(*fetched)); } throw(DID_NOT_FIND_SYMBOL, "Hash table does not contain %s", params[1].string); } #endif