209 lines
5.3 KiB
C
209 lines
5.3 KiB
C
#include "hash.h"
|
|
#include "env.h"
|
|
#include "pebblisp.h"
|
|
#include "object.h"
|
|
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
|
|
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
|
|
|
|
size_t hash(const char* str, unused const 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
|
|
|
|
/**
|
|
* \return The hash value of the given name
|
|
*/
|
|
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;
|
|
}
|
|
|
|
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
|