1
0
mirror of https://github.com/janet-lang/janet synced 2024-12-25 07:50:27 +00:00

More work on open hashing implementation of objects.

Add metatable support for callable objects.
This commit is contained in:
Calvin Rose 2017-03-11 17:04:59 -05:00
parent 9c94bfab4d
commit 1effd9e740
8 changed files with 344 additions and 170 deletions

View File

@ -3,8 +3,8 @@
#include <stdint.h>
/* Flag for immutability in an otherwise mutable datastructure */
#define GST_IMMUTABLE 1
/* Max search depth for classes. */
#define GST_MAX_SEARCH_DEPTH 128
/* Verious types */
typedef enum GstType {
@ -89,7 +89,6 @@ struct GstArray {
uint32_t count;
uint32_t capacity;
GstValue *data;
uint32_t flags;
};
/* A bytebuffer type. Used as a mutable string or string builder. */
@ -97,7 +96,6 @@ struct GstBuffer {
uint32_t count;
uint32_t capacity;
uint8_t *data;
uint32_t flags;
};
/* The main Gst type, an obect. Objects are just hashtables with some meta
@ -106,7 +104,6 @@ struct GstObject {
uint32_t count;
uint32_t capacity;
GstBucket **buckets;
uint32_t flags;
GstObject *meta;
};
@ -168,6 +165,8 @@ struct Gst {
/* Return state */
const char *crash;
GstValue ret; /* Returned value from gst_start. Also holds errors. */
/* Temporary array for use in function dispatch */
GstValue tempArray[GST_MAX_SEARCH_DEPTH];
};
/* Bytecode */

354
dict.c
View File

@ -1,130 +1,264 @@
#include "dict.h"
#include "util.h"
#include "value.h"
#include "vm.h"
/****/
/* Bag implementation */
/****/
/* Find a kv pair in a bag */
static GstValue *gst_object_bag_find(GstDict *obj, GstValue key) {
GstValue *start = obj->data;
GstValue *end = obj->data + obj->count * 2;
while (start < end) {
if (gst_equals(*start, key))
return start;
start += 2;
}
return NULL;
}
/* Check for string equality */
static int str_equal_value(GstValue v, const char *str, uint32_t len, uint32_t hash) {
uint32_t i;
if (v.type != GST_STRING) return 0;
if (gst_string_length(str) != len) return 0;
if (!gst_string_hash(str))
gst_string_hash(str) = gst_string_calchash((uint8_t *)str);
if (gst_string_hash(str) != hash) return 0;
for (i = 0; i < len; ++i)
if (str[1] != v.data.string[i]) return 0;
return 1;
}
/* Find key value pair with c string key */
static GstValue *gst_object_bag_findcstring(GstDict *obj, const char *key) {
uint32_t len, hash;
for (len = 0; key[len]; ++len);
hash = gst_cstring_calchash((uint8_t *)key, len);
GstValue *start = obj->data;
GstValue *end = obj->data + obj->count * 2;
while (start < end) {
if (start->type == GST_STRING) {
uint8_t *str = start->data.string;
if (gst_string_length(str) == len) {
if (!gst_string_hash(str))
gst_string_hash(str) = gst_string_calchash(str);
if (gst_string_hash(str) == hash) {
return start
}
}
}
start += 2;
}
return NULL;
}
/* Remove a key from a bag */
static void gst_object_bag_remove(GstDict *obj, GstValue key) {
GstValue *kv = gst_object_bag_find(obj, key);
if (kv != NULL) {
GstValue *lastKv = obj->data + --obj->count * 2;
kv[0] = lastKv[0];
kv[1] = lastKv[1];
}
}
/* Add a key to a bag */
static void gst_object_bag_put(Gst *vm, GstDict *obj, GstValue key, GstValue value) {
GstValue *kv = gst_object_bag_find(obj, key);
if (kv != NULL) {
/* Replace value */
kv[1] = value;
} else {
/* Check for need to resize */
if (obj->count + 1 > obj->capacity) {
uint32_t newCap = 2 * obj->count + 2;
GstValue *newData = gst_alloc(vm, sizeof(GstValue) * 2 * newCap);
gst_memcpy(newData, obj->data, obj->count * 2 * sizeof(GstValue));
obj->data = newData;
obj->capacity = newCap;
}
/* Push to end */
kv = obj->data + obj->count * 2;
kv[0] = key;
kv[1] = value;
++obj->count;
}
}
/****/
/* Hashtable implementaion */
/****/
/* Add a key value pair to a given array. Returns if key successfully added. */
static void hash_putkv(GstValue *data, uint32_t cap, GstValue key, GstValue value) {
GstValue *end = data + 2 * cap;
GstValue *start = data + (gst_hash(key) % cap) * 2;
GstValue *bucket;
/* Check second half of array */
for (bucket = start; bucket < end; bucket += 2) {
if (bucket[0].type == GST_NIL) {
bucket[0] = key;
bucket[1] = value;
return;
}
}
/* Check first half of array */
for (bucket = data; bucket < start; bucket += 2) {
if (bucket[0].type == GST_NIL) {
bucket[0] = key;
bucket[1] = value;
return;
}
}
/* Should never reach here - data would be full */
}
/* Find a bucket in the hastable */
static GstValue *hash_findkv(GstValue *data, uint32_t cap, GstValue key, GstValue **out) {
GstValue *end = data + 2 * cap;
GstValue *start = data + (gst_hash(key) % cap) * 2;
GstValue *bucket;
/* Check second half of array */
for (bucket = start; bucket < end; bucket += 2)
if (bucket[0].type == GST_NIL)
if (bucket[1].type == GST_BOOLEAN) /* Check if just marked deleted */
continue;
else {
*out = bucket;
return NULL;
}
else if (gst_equals(bucket[0], key))
return bucket;
/* Check first half of array */
for (bucket = data; bucket < start; bucket += 2)
if (bucket[0].type == GST_NIL)
if (bucket[1].type == GST_BOOLEAN) /* Check if just marked deleted */
continue;
else {
*out = bucket;
return NULL;
}
else if (gst_equals(bucket[0], key))
return bucket;
/* Should never reach here - data would be full */
*out = bucket;
return NULL;
}
/* Resize internal hashtable. Also works if currently a bag. */
static void gst_object_rehash(Gst *vm, GstDict *obj, uint32_t capacity) {
GstValue *toData, *fromBucket, *toBucket, *toStart *fromEnd, *toEnd;
toData = gst_alloc(vm, capacity * 2 * sizeof(GstValue));
toEnd = toData + 2 * capacity;
fromBucket = obj->data;
fromEnd = fromBucket + obj->count * 2;
for (; fromBucket < fromEnd; fromBucket += 2) {
if (fromBucket[0].type == GST_NIL) continue;
toStart = toData + (gst_hash(fromBucket[0]) % capacity) * 2;
/* Check second half of array */
for (toBucket = toStart; toBucket < toEnd; toBucket += 2) {
if (toBucket[0].type == GST_NIL) {
toBucket[0] = fromBucket[0];
toBucket[1] = fromBucket[1];
goto finish_put;
}
}
/* Check first half of array */
for (toBucket = toData; toBucket < toStart; toBucket += 2) {
if (toBucket[0].type == GST_NIL) {
toBucket[0] = fromBucket[0];
toBucket[1] = fromBucket[1];
goto finish_put;
}
}
/* Error if we got here - backing array to small. */
;
/* Continue. */
finish_put: continue;
}
obj->capacity = capacity;
obj->data = toData;
}
/****/
/* Interface */
/****/
/* Initialize a dictionary */
GstDict *gst_dict_init(GstDict *dict, uint32_t capacity) {
GstDictBucket *buckets = gst_raw_calloc(1, sizeof(GstDictBucket) * capacity);
if (data == NULL)
return NULL;
dict->buckets = buckets;
GstDict *gst_dict(Gst *vm, uint32_t capacity) {
GstDict *dict = gst_alloc(vm, sizeof(GstDict));
GstValue *data = gst_zalloc(vm, sizeof(GstValue) * 2 * capacity);
dict->data = data;
dict->capacity = capacity;
dict->count = 0;
dict->flags = (capacity < GST_OBJECT_BAG_THRESHOLD) ? GST_OBJECT_FLAG_ISBAG : 0;
return dict;
}
/* Deinitialize a dictionary */
GstDict *gst_dict_free(GstDict *dict) {
gst_raw_free(dict->buckets);
}
/* Rehash a dictionary */
GstDict *gst_dict_rehash(GstDict *dict, uint32_t newCapacity) {
GstDictBucket *newBuckets = gst_raw_calloc(1, sizeof(GstDictBucket) * newCapacity);
GstDictBucket *buckets = dict->buckets;
uint32_t i, j;
if (newBuckets == NULL)
return NULL;
for (i = 0; i < dict->capacity; ++i) {
int index;
if (!(buckets[i].flags & GST_DICT_FLAG_OCCUPIED)) continue;
if (buckets[i].flags & GST_DICT_FLAG_TOMBSTONE) continue;
index = gst_hash(buckets[i].key) % newCapacity;
for (j = index; j < dict->capacity; ++j) {
if (newBuckets[j].flags & GST_DICT_FLAG_OCCUPIED) continue;
newBuckets[j] = buckets[i];
goto done;
}
for (j = 0; j < index; ++j) {
if (newBuckets[j].flags & GST_DICT_FLAG_OCCUPIED) continue;
newBuckets[j] = buckets[i];
goto done;
}
/* Error - could not rehash a bucket - this should never happen */
gst_raw_free(newBuckets);
return NULL;
/* Successfully rehashed bucket */
done:
}
dict->capacity = newCapacity;
return dict;
}
/* Find a bucket with a given key */
static int gst_dict_find(GstDict *dict, GstValue key, GstDictBucket **out) {
uint32_t index, i;
GstDictBucket *buckets = dict->buckets;
index = gst_hash(key) % dict->capacity;
for (i = index; i < dict->capacity; ++i) {
if (buckets[i].flags & GST_DICT_FLAGS_TOMBSTONE) continue;
if (!(buckets[i].flags & GST_DICT_FLAGS_OCCUPIED)) continue;
if (!gst_equals(key, buckets[i].key)) continue;
*out = buckets + i;
return 1;
}
for (i = 0; i < index; ++i) {
if (buckets[i].flags & GST_DICT_FLAGS_TOMBSTONE) continue;
if (!(buckets[i].flags & GST_DICT_FLAGS_OCCUPIED)) continue;
if (!gst_equals(key, buckets[i].key)) continue;
*out = buckets + i;
return 1;
}
return 0;
}
/* Get item from dictionary */
int gst_dict_get(GstDict *dict, GstValue key, GstValue *value) {
GstDictBucket *bucket;
int found = gst_dict_find(dict, key, &bucket);
if (found)
*value = bucket->value;
return found;
GstValue gst_dict_get(GstDict *dict, GstValue key) {
GstValue *bucket *notused;
if (dict->flags & GST_OBJECT_FLAG_ISBAG) {
bucket = gst_object_bag_find(dict, key);
} else {
bucket = hash_findkv(obj->data, obj->capacity, key, &notused);
}
if (bucket != NULL) {
return bucket[1];
} else {
GstValue ret;
ret.type = GST_NIL;
return ret;
}
}
/* Get item with c string key */
GstValue gst_dict_get_cstring(GstDict *dict, const char *key);
/* Add item to dictionary */
GstDict *gst_dict_put(GstDict *dict, GstValue key, GstValue value) {
/* Check if we need to increase capacity. The load factor is low
* because we are using linear probing */
uint32_t index, i;
uint32_t newCap = dict->count * 2 + 1;
GstBucket *buckets;
if (newCap > dict->capacity) {
dict = gst_dict_rehash(dict, newCap);
if (!dict) return dict;
void gst_dict_put(Gst *vm, GstDict *obj, GstValue key, GstValue value) {
if (obj->flags & GST_OBJECT_FLAG_ISBAG) {
if (obj->count > GST_OBJECT_BAG_THRESHOLD) {
/* Change to hashtable */
obj->flags |= GST_OBJECT_FLAG_ISBAG;
gst_object_rehash(vm, obj, 4 * obj->count);
goto put_hash;
}
gst_object_bag_put(vm, obj, key, value);
} else {
GstValue *bucket, *out;
put_hash:
bucket = hash_findkv(obj->data, obj->capacity, key, &out);
if (bucket != NULL) {
bucket[1] = value;
} else {
/* Check for resize */
if (obj->count + 1 > obj->capacity) {
gst_object_rehash(vm, obj, 2 * (obj->count + 1));
bucket = hash_findkv(obj->data, obj->capacity, key, &out);
}
out[0] = key;
out[1] = value;
++obj->count;
}
}
index = gst_hash(key) % dict->capacity;
buckets = dict->buckets;
for (i = index; i < dict->capacity; ++i) {
if ((buckets[i].flags & GST_DICT_FLAGS_TOMBSTONE) ||
!(buckets[i].flags & GST_DICT_FLAGS_OCCUPIED))
continue;
dict->buckets[i].key = key;
dict->buckets[i].value = value;
dict->buckets[i].flags &= GST_DICT_FLAGS_OCCUPIED;
dict->count++;
return dict;
}
for (i = 0; i < index; ++i) {
if ((buckets[i].flags & GST_DICT_FLAGS_TOMBSTONE) ||
!(buckets[i].flags & GST_DICT_FLAGS_OCCUPIED))
continue;
dict->buckets[i].key = key;
dict->buckets[i].value = value;
dict->buckets[i].flags &= GST_DICT_FLAGS_OCCUPIED;
dict->count++;
return dict;
}
/* Error - should never get here */
return NULL;
}
/* Remove item from dictionary */
int gst_dict_remove(GstDict *dict, GstValue key) {
GstDictBucket *bucket;
int found = gst_dict_find(dict, key, &bucket);
if (found) {
bucket->flags |= GST_DICT_FLAGS_TOMBSTONE;
dict->count--;
void gst_dict_remove(GstDict *obj, GstValue key) {
if (obj->flags & GST_OBJECT_FLAG_ISBAG) {
gst_object_bag_remove(obj, key);
} else {
GstValue *bucket, *out;
bucket = hash_findkv(obj->data, obj->capacity, key, &out);
if (bucket != NULL) {
--obj->count;
bucket[0].type = GST_NIL;
bucket[1].type = GST_BOOLEAN;
}
}
return found;
}

35
dict.h
View File

@ -3,39 +3,36 @@
#include "datatypes.h"
#define GST_DICT_FLAG_OCCUPIED 1
#define GST_DICT_FLAG_TOMBSTONE 2
/* Indicates object is implement as unsorted array of keypairs */
#define GST_OBJECT_FLAG_ISBAG (1 << 31)
typedef struct GstDictBucket GstDictBucket;
struct GstDictBucket {
GstValue key;
GstValue value;
uint8_t flags;
};
/* Indicates object is immutable */
#define GST_OBJECT_IMMUTABLE (1 << 30)
/* Count at which the object goes from a linear search to a hash table */
#define GST_OBJECT_BAG_THRESHOLD 8
typedef struct GstDict GstDict;
struct GstDict {
uint32_t capacity;
uint32_t count;
GstDictBucket *buckets;
uint32_t flags;
GstValue *data;
};
/* Initialize a dictionary */
GstDict *gst_dict_init(GstDict *dict, uint32_t capacity);
/* Deinitialize a dictionary */
GstDict *gst_dict_free(GstDict *dict);
/* Rehash a dictionary */
GstDict *gst_dict_rehash(GstDict *dict, uint32_t newCapacity);
GstDict *gst_dict(Gst *vm, uint32_t capacity);
/* Get item from dictionary */
int gst_dict_get(GstDict *dict, GstValue key, GstValue *value);
GstValue gst_dict_get(GstDict *dict, GstValue key);
/* Get c string from object */
GstValue gst_dict_get_cstring(GstDict *dict, const char *key);
/* Add item to dictionary */
GstDict *gst_dict_put(GstDict *dict, GstValue key, GstValue value);
void gst_dict_put(Gst *vm, GstDict *dict, GstValue key, GstValue value);
/* Remove item from dictionary */
int gst_dict_remove(GstDict *dict, GstValue key);
void gst_dict_remove(GstDict *dict, GstValue key);
#endif // dict_h_INCLUDED

9
ds.c
View File

@ -14,7 +14,6 @@ GstBuffer *gst_buffer(Gst *vm, uint32_t capacity) {
buffer->data = data;
buffer->count = 0;
buffer->capacity = capacity;
buffer->flags = 0;
return buffer;
}
@ -76,7 +75,6 @@ GstArray *gst_array(Gst * vm, uint32_t capacity) {
array->data = data;
array->count = 0;
array->capacity = capacity;
array->flags = 0;
return array;
}
@ -181,7 +179,7 @@ GstObject* gst_object(Gst *vm, uint32_t capacity) {
o->buckets = buckets;
o->capacity = capacity;
o->count = 0;
o->flags = 0;
o->meta = NULL;
return o;
}
@ -230,9 +228,8 @@ GstValue gst_object_get(GstObject *o, GstValue key) {
/* Get a value of the object with a cstring key */
GstValue gst_object_get_cstring(GstObject *obj, const char *key) {
const char *end = key;
while (*end++);
uint32_t len = end - key;
uint32_t len;
for (len = 0; key[len]; ++len);
uint32_t hash = gst_cstring_calchash((uint8_t *)key, len);
uint32_t index = hash % obj->capacity;
GstBucket *bucket = obj->buckets[index];

6
gc.c
View File

@ -149,6 +149,12 @@ void gst_mark(Gst *vm, GstValue *x) {
bucket = bucket->next;
}
}
if (x->data.object->meta != NULL) {
GstValue temp;
temp.type = GST_OBJECT;
temp.data.object = x->data.object->meta;
gst_mark(vm, &temp);
}
}
break;

11
thread.c Normal file
View File

@ -0,0 +1,11 @@
#include "datatypes.h"
/* Push Value to thread */
void gst_thread_push(Gst *vm, GstThread *thread, GstValue x) {
}
/* Push stack frame to thread */
void gst_thread_pushframe(Gst *vm, GstThread *thread, GstValue callee) {
}

View File

@ -444,16 +444,12 @@ const char *gst_set(Gst *vm, GstValue ds, GstValue key, GstValue value) {
int32_t index;
switch (ds.type) {
case GST_ARRAY:
if (ds.data.array->flags & GST_IMMUTABLE)
return "cannot set immutable value";
if (key.type != GST_NUMBER) return "expected numeric key";
index = to_index(key.data.number, ds.data.array->count);
if (index == -1) return "invalid array access";
ds.data.array->data[index] = value;
break;
case GST_BYTEBUFFER:
if (ds.data.buffer->flags & GST_IMMUTABLE)
return "cannot set immutable value";
if (key.type != GST_NUMBER) return "expected numeric key";
if (value.type != GST_NUMBER) return "expected numeric value";
index = to_index(key.data.number, ds.data.buffer->count);
@ -461,8 +457,8 @@ const char *gst_set(Gst *vm, GstValue ds, GstValue key, GstValue value) {
ds.data.buffer->data[index] = to_byte(value.data.number);
break;
case GST_OBJECT:
if (ds.data.object->flags & GST_IMMUTABLE)
return "cannot set immutable value";
if (ds.data.object->meta != NULL) {
}
gst_object_put(vm, ds.data.object, key, value);
break;
default:

82
vm.c
View File

@ -67,7 +67,7 @@ static void gst_load(Gst *vm, GstValue callee) {
#define GST_STATE_WRITE() do { \
*vm->thread = thread; \
} while (0)
/* Start running the VM from where it left off. Continue running
* until the stack size is smaller than minStackSize. */
static int gst_continue_size(Gst *vm, uint32_t stackBase) {
@ -382,27 +382,53 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) {
case GST_OP_PSH: /* Push stack frame */
{
GstValue *nextStack;
uint32_t expectedArity, normalArity, arity, varArgs, i, locals, nextCount;
uint32_t argSlots, fullArgSlots, arity, prefixCount, varArgs, tupleCount, i, locals, nextCount;
/* Get arguments to op */
temp = stack[pc[1]];
arity = pc[2];
/* Get the size of next stack frame */
if (temp.type == GST_FUNCTION) {
GstFunction *fn = temp.data.function;
locals = fn->def->locals;
varArgs = fn->def->flags & GST_FUNCDEF_FLAG_VARARG;
expectedArity = fn->def->arity;
if (arity > expectedArity)
normalArity = expectedArity;
else
normalArity = arity;
} else if (temp.type == GST_CFUNCTION) {
locals = normalArity = expectedArity = arity;
varArgs = 0;
} else {
gst_error(vm, GST_EXPECTED_FUNCTION);
prefixCount = 0;
recur:
switch(temp.type) {
default: gst_error(vm, GST_EXPECTED_FUNCTION);
case GST_FUNCTION:
{
GstFunction *fn = temp.data.function;
locals = fn->def->locals;
varArgs = fn->def->flags & GST_FUNCDEF_FLAG_VARARG;
argSlots = fn->def->arity;
if (arity + prefixCount > argSlots) {
fullArgSlots = argSlots;
tupleCount = arity + prefixCount - fullArgSlots;
} else {
fullArgSlots = arity + prefixCount;
tupleCount = 0;
}
break;
}
case GST_CFUNCTION:
{
locals = argSlots = fullArgSlots = arity + prefixCount;
varArgs = tupleCount = 0;
break;
}
case GST_OBJECT:
{
GstObject *meta = temp.data.object->meta;
if (meta == NULL) gst_error(vm, GST_EXPECTED_FUNCTION);
vm->tempArray[prefixCount++] = temp;
temp = gst_object_get_cstring(meta, "call");
goto recur;
}
case GST_USERDATA:
{
GstObject *meta = ((GstUserdataHeader *)temp.data.pointer - 1)->meta;
vm->tempArray[prefixCount++] = temp;
temp = gst_object_get_cstring(meta, "call");
goto recur;
}
}
/* Get next frame size */
@ -426,12 +452,16 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) {
gst_frame_env(nextStack) = NULL;
gst_frame_callee(nextStack) = temp;
gst_frame_errjmp(nextStack) = NULL;
/* Write prefix args to stack */
for (i = 0; i < prefixCount; ++i)
nextStack[i] = vm->tempArray[i];
/* Write arguments to new stack */
for (i = 0; i < normalArity; ++i)
nextStack[i] = stack[pc[3 + i]];
for (; i < fullArgSlots; ++i)
nextStack[i] = stack[pc[3 + i - prefixCount]];
/* Clear stack */
/* Clear rest of stack */
for (; i < locals; ++i)
nextStack[i].type = GST_NIL;
@ -439,11 +469,14 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) {
if (varArgs) {
GstValue *tuple;
uint32_t j;
tuple = gst_tuple(vm, arity - expectedArity);
for (j = expectedArity; j < arity; ++j)
tuple[j - expectedArity] = stack[pc[3 + j]];
nextStack[expectedArity].type = GST_TUPLE;
nextStack[expectedArity].data.tuple = tuple;
tuple = gst_tuple(vm, tupleCount);
for (j = argSlots; j < arity; ++j)
if (j < prefixCount)
tuple[j - argSlots] = vm->tempArray[j - prefixCount];
else
tuple[j - argSlots] = stack[pc[3 + j - prefixCount]];
nextStack[argSlots].type = GST_TUPLE;
nextStack[argSlots].data.tuple = tuple;
}
/* Increment pc */
@ -486,6 +519,7 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) {
} else if (v1.type == GST_CFUNCTION) {
int status;
GST_STATE_WRITE();
vm->ret.type = GST_NIL;
status = v1.data.cfunction(vm);
GST_STATE_SYNC();
if (status == GST_RETURN_OK)