1
0
mirror of https://github.com/janet-lang/janet synced 2024-11-14 04:34:48 +00:00
janet/ds.c

301 lines
8.5 KiB
C

#include "ds.h"
#include "value.h"
#include "vm.h"
#include <string.h>
/****/
/* Buffer functions */
/****/
/* Create a new Buffer */
Buffer * BufferNew(VM * vm, uint32_t capacity) {
Buffer * buffer = VMAlloc(vm, sizeof(Buffer));
uint8_t * data = VMAlloc(vm, sizeof(uint8_t) * capacity);
buffer->data = data;
buffer->count = 0;
buffer->capacity = capacity;
buffer->flags = 0;
return buffer;
}
/* Ensure that the buffer has enough internal capacity */
void BufferEnsure(VM * vm, Buffer * buffer, uint32_t capacity) {
uint8_t * newData;
if (capacity <= buffer->capacity) return;
newData = VMAlloc(vm, capacity * sizeof(uint8_t));
memcpy(newData, buffer->data, buffer->count * sizeof(uint8_t));
buffer->data = newData;
buffer->capacity = capacity;
}
/* Get a byte from an index in the buffer */
int32_t BufferGet(Buffer * buffer, uint32_t index) {
if (index < buffer->count) {
return buffer->data[index];
} else {
return -1;
}
}
/* Push a byte into the buffer */
void BufferPush(VM * vm, Buffer * buffer, uint8_t c) {
if (buffer->count >= buffer->capacity) {
BufferEnsure(vm, buffer, 2 * buffer->count);
}
buffer->data[buffer->count++] = c;
}
/* Push multiple bytes into the buffer */
void BufferAppendData(VM * vm, Buffer * buffer, uint8_t * string, uint32_t length) {
uint32_t newSize = buffer->count + length;
if (newSize > buffer->capacity) {
BufferEnsure(vm, buffer, 2 * newSize);
}
memcpy(buffer->data + buffer->count, string, length);
buffer->count = newSize;
}
/* Convert the buffer to a string */
uint8_t * BufferToString(VM * vm, Buffer * buffer) {
uint8_t * data = VMAlloc(vm, buffer->count + 2 * sizeof(uint32_t));
data += 2 * sizeof(uint32_t);
VStringSize(data) = buffer->count;
VStringHash(data) = 0;
memcpy(data, buffer->data, buffer->count * sizeof(uint8_t));
return data;
}
/****/
/* Array functions */
/****/
/* Creates a new array */
Array * ArrayNew(VM * vm, uint32_t capacity) {
Array * array = VMAlloc(vm, sizeof(Array));
Value * data = VMAlloc(vm, capacity * sizeof(Value));
array->data = data;
array->count = 0;
array->capacity = capacity;
array->flags = 0;
return array;
}
/* Ensure the array has enough capacity for capacity elements */
void ArrayEnsure(VM * vm, Array * array, uint32_t capacity) {
Value * newData;
if (capacity <= array->capacity) return;
newData = VMAlloc(vm, capacity * sizeof(Value));
memcpy(newData, array->data, array->capacity * sizeof(Value));
array->data = newData;
array->capacity = capacity;
}
/* Get a value of an array with bounds checking. */
Value ArrayGet(Array * array, uint32_t index) {
if (index < array->count) {
return array->data[index];
} else {
Value v;
v.type = TYPE_NIL;
v.data.boolean = 0;
return v;
}
}
/* Try to set an index in the array. Return 1 if successful, 0
* on failiure */
int ArraySet(Array * array, uint32_t index, Value x) {
if (index < array->count) {
array->data[index] = x;
return 1;
} else {
return 0;
}
}
/* Add an item to the end of the array */
void ArrayPush(VM * vm, Array * array, Value x) {
if (array->count >= array->capacity) {
ArrayEnsure(vm, array, 2 * array->count);
}
array->data[array->count++] = x;
}
/* Remove the last item from the Array and return it */
Value ArrayPop(Array * array) {
if (array->count) {
return array->data[--array->count];
} else {
Value v;
v.type = TYPE_NIL;
v.data.boolean = 0;
return v;
}
}
/* Look at the last item in the Array */
Value ArrayPeek(Array * array) {
if (array->count) {
return array->data[array->count - 1];
} else {
Value v;
v.type = TYPE_NIL;
v.data.boolean = 0;
return v;
}
}
/****/
/* Dictionary functions */
/****/
/* Create a new dictionary */
Dictionary * DictNew(VM * vm, uint32_t capacity) {
Dictionary * dict = VMAlloc(vm, sizeof(Dictionary));
DictBucket ** buckets = VMZalloc(vm, capacity * sizeof(DictBucket *));
dict->buckets = buckets;
dict->capacity = capacity;
dict->count = 0;
dict->flags = 0;
return dict;
}
/* Resize the dictionary table. */
static void DictReHash(VM * vm, Dictionary * dict, uint32_t size) {
DictBucket ** newBuckets = VMZalloc(vm, 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(VM * vm, 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(vm, 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(VM * vm, 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(vm, 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(vm, dict, 2 * dict->capacity);
}
bucket = VMAlloc(vm, 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;
}