mirror of
https://github.com/janet-lang/janet
synced 2024-11-28 02:59:54 +00:00
153 lines
4.6 KiB
C
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;
|
|
}
|