1
0
mirror of https://github.com/janet-lang/janet synced 2024-06-27 23:53:16 +00:00
janet/dict.c
2017-02-09 15:02:59 -05:00

153 lines
4.6 KiB
C

#include "dict.h"
#include "value.h"
#include "gc.h"
/* Create a new dictionary */
Dictionary * DictNew(GC * gc, uint32_t capacity) {
Dictionary * dict = GCAlloc(gc, sizeof(Dictionary));
DictBucket ** buckets = GCZalloc(gc, capacity * sizeof(DictBucket *));
dict->buckets = buckets;
dict->capacity = capacity;
dict->count = 0;
return dict;
}
/* Resize the dictionary table. */
static void DictReHash(GC * gc, Dictionary * dict, uint32_t size) {
DictBucket ** newBuckets = GCZalloc(gc, size * sizeof(DictBucket *));
uint32_t i, count;
for (i = 0, count = dict->capacity; i < count; ++i) {
DictBucket * bucket = dict->buckets[i];
while (bucket) {
uint32_t index;
DictBucket * next = bucket->next;
index = ValueHash(&bucket->key) % size;
bucket->next = newBuckets[index];
newBuckets[index] = bucket;
bucket = next;
}
}
dict->buckets = newBuckets;
dict->capacity = size;
}
/* Find the bucket that contains the given key */
static DictBucket * DictFind(Dictionary * dict, Value * key) {
uint32_t index = ValueHash(key) % dict->capacity;
DictBucket * bucket = dict->buckets[index];
while (bucket) {
if (ValueEqual(&bucket->key, key))
return bucket;
bucket = bucket->next;
}
return (DictBucket *)0;
}
/* Get a value out of the dictionary */
Value DictGet(Dictionary * dict, Value * key) {
DictBucket * bucket = DictFind(dict, key);
if (bucket) {
return bucket->value;
} else {
Value nil;
nil.type = TYPE_NIL;
return nil;
}
}
/* Remove an entry from the dictionary */
Value DictRemove(GC * gc, Dictionary * dict, Value * key) {
DictBucket * bucket, * previous;
uint32_t index = ValueHash(key) % dict->capacity;
bucket = dict->buckets[index];
previous = (DictBucket *)0;
while (bucket) {
if (ValueEqual(&bucket->key, key)) {
if (previous) {
previous->next = bucket->next;
} else {
dict->buckets[index] = bucket->next;
}
if (dict->count < dict->capacity / 4) {
DictReHash(gc, dict, dict->capacity / 2);
}
--dict->count;
return bucket->value;
}
previous = bucket;
bucket = bucket->next;
}
/* Return nil if we found nothing */
{
Value nil;
nil.type = TYPE_NIL;
return nil;
}
}
/* Put a value into the dictionary. Returns 1 if successful, 0 if out of memory.
* The VM pointer is needed for memory allocation. */
void DictPut(GC * gc, Dictionary * dict, Value * key, Value * value) {
DictBucket * bucket, * previous;
uint32_t index = ValueHash(key) % dict->capacity;
if (key->type == TYPE_NIL) return;
/* Do a removal if value is nil */
if (value->type == TYPE_NIL) {
bucket = dict->buckets[index];
previous = (DictBucket *)0;
while (bucket) {
if (ValueEqual(&bucket->key, key)) {
if (previous) {
previous->next = bucket->next;
} else {
dict->buckets[index] = bucket->next;
}
if (dict->count < dict->capacity / 4) {
DictReHash(gc, dict, dict->capacity / 2);
}
--dict->count;
return;
}
previous = bucket;
bucket = bucket->next;
}
} else {
bucket = DictFind(dict, key);
if (bucket) {
bucket->value = *value;
} else {
if (dict->count >= 2 * dict->capacity) {
DictReHash(gc, dict, 2 * dict->capacity);
}
bucket = GCAlloc(gc, sizeof(DictBucket));
bucket->next = dict->buckets[index];
bucket->value = *value;
bucket->key = *key;
dict->buckets[index] = bucket;
++dict->count;
}
}
}
/* Begin iteration through a dictionary */
void DictIterate(Dictionary * dict, DictionaryIterator * iterator) {
iterator->index = 0;
iterator->dict = dict;
iterator->bucket = dict->buckets[0];
}
/* Provides a mechanism for iterating through a table. */
int DictIterateNext(DictionaryIterator * iterator, DictBucket ** bucket) {
Dictionary * dict = iterator->dict;
for (;;) {
if (iterator->bucket) {
*bucket = iterator->bucket;
iterator->bucket = iterator->bucket->next;
return 1;
}
if (++iterator->index >= dict->capacity) break;
iterator->bucket = dict->buckets[iterator->index];
}
return 0;
}