mirror of
https://github.com/janet-lang/janet
synced 2024-12-27 00:40:26 +00:00
More work on open hashing implementation of objects.
Add metatable support for callable objects.
This commit is contained in:
parent
9c94bfab4d
commit
1effd9e740
@ -3,8 +3,8 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
/* Flag for immutability in an otherwise mutable datastructure */
|
/* Max search depth for classes. */
|
||||||
#define GST_IMMUTABLE 1
|
#define GST_MAX_SEARCH_DEPTH 128
|
||||||
|
|
||||||
/* Verious types */
|
/* Verious types */
|
||||||
typedef enum GstType {
|
typedef enum GstType {
|
||||||
@ -89,7 +89,6 @@ struct GstArray {
|
|||||||
uint32_t count;
|
uint32_t count;
|
||||||
uint32_t capacity;
|
uint32_t capacity;
|
||||||
GstValue *data;
|
GstValue *data;
|
||||||
uint32_t flags;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* A bytebuffer type. Used as a mutable string or string builder. */
|
/* A bytebuffer type. Used as a mutable string or string builder. */
|
||||||
@ -97,7 +96,6 @@ struct GstBuffer {
|
|||||||
uint32_t count;
|
uint32_t count;
|
||||||
uint32_t capacity;
|
uint32_t capacity;
|
||||||
uint8_t *data;
|
uint8_t *data;
|
||||||
uint32_t flags;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* The main Gst type, an obect. Objects are just hashtables with some meta
|
/* The main Gst type, an obect. Objects are just hashtables with some meta
|
||||||
@ -106,7 +104,6 @@ struct GstObject {
|
|||||||
uint32_t count;
|
uint32_t count;
|
||||||
uint32_t capacity;
|
uint32_t capacity;
|
||||||
GstBucket **buckets;
|
GstBucket **buckets;
|
||||||
uint32_t flags;
|
|
||||||
GstObject *meta;
|
GstObject *meta;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -168,6 +165,8 @@ struct Gst {
|
|||||||
/* Return state */
|
/* Return state */
|
||||||
const char *crash;
|
const char *crash;
|
||||||
GstValue ret; /* Returned value from gst_start. Also holds errors. */
|
GstValue ret; /* Returned value from gst_start. Also holds errors. */
|
||||||
|
/* Temporary array for use in function dispatch */
|
||||||
|
GstValue tempArray[GST_MAX_SEARCH_DEPTH];
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Bytecode */
|
/* Bytecode */
|
||||||
|
350
dict.c
350
dict.c
@ -1,130 +1,264 @@
|
|||||||
#include "dict.h"
|
#include "dict.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "value.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 */
|
/* Initialize a dictionary */
|
||||||
GstDict *gst_dict_init(GstDict *dict, uint32_t capacity) {
|
GstDict *gst_dict(Gst *vm, uint32_t capacity) {
|
||||||
GstDictBucket *buckets = gst_raw_calloc(1, sizeof(GstDictBucket) * capacity);
|
GstDict *dict = gst_alloc(vm, sizeof(GstDict));
|
||||||
if (data == NULL)
|
GstValue *data = gst_zalloc(vm, sizeof(GstValue) * 2 * capacity);
|
||||||
return NULL;
|
dict->data = data;
|
||||||
dict->buckets = buckets;
|
|
||||||
dict->capacity = capacity;
|
dict->capacity = capacity;
|
||||||
dict->count = 0;
|
dict->count = 0;
|
||||||
|
dict->flags = (capacity < GST_OBJECT_BAG_THRESHOLD) ? GST_OBJECT_FLAG_ISBAG : 0;
|
||||||
return dict;
|
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 */
|
/* Get item from dictionary */
|
||||||
int gst_dict_get(GstDict *dict, GstValue key, GstValue *value) {
|
GstValue gst_dict_get(GstDict *dict, GstValue key) {
|
||||||
GstDictBucket *bucket;
|
GstValue *bucket *notused;
|
||||||
int found = gst_dict_find(dict, key, &bucket);
|
if (dict->flags & GST_OBJECT_FLAG_ISBAG) {
|
||||||
if (found)
|
bucket = gst_object_bag_find(dict, key);
|
||||||
*value = bucket->value;
|
} else {
|
||||||
return found;
|
bucket = hash_findkv(obj->data, obj->capacity, key, ¬used);
|
||||||
|
}
|
||||||
|
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 */
|
/* Add item to dictionary */
|
||||||
GstDict *gst_dict_put(GstDict *dict, GstValue key, GstValue value) {
|
void gst_dict_put(Gst *vm, GstDict *obj, GstValue key, GstValue value) {
|
||||||
/* Check if we need to increase capacity. The load factor is low
|
if (obj->flags & GST_OBJECT_FLAG_ISBAG) {
|
||||||
* because we are using linear probing */
|
if (obj->count > GST_OBJECT_BAG_THRESHOLD) {
|
||||||
uint32_t index, i;
|
/* Change to hashtable */
|
||||||
uint32_t newCap = dict->count * 2 + 1;
|
obj->flags |= GST_OBJECT_FLAG_ISBAG;
|
||||||
GstBucket *buckets;
|
gst_object_rehash(vm, obj, 4 * obj->count);
|
||||||
if (newCap > dict->capacity) {
|
goto put_hash;
|
||||||
dict = gst_dict_rehash(dict, newCap);
|
|
||||||
if (!dict) return dict;
|
|
||||||
}
|
}
|
||||||
index = gst_hash(key) % dict->capacity;
|
gst_object_bag_put(vm, obj, key, value);
|
||||||
buckets = dict->buckets;
|
} else {
|
||||||
for (i = index; i < dict->capacity; ++i) {
|
GstValue *bucket, *out;
|
||||||
if ((buckets[i].flags & GST_DICT_FLAGS_TOMBSTONE) ||
|
put_hash:
|
||||||
!(buckets[i].flags & GST_DICT_FLAGS_OCCUPIED))
|
bucket = hash_findkv(obj->data, obj->capacity, key, &out);
|
||||||
continue;
|
if (bucket != NULL) {
|
||||||
dict->buckets[i].key = key;
|
bucket[1] = value;
|
||||||
dict->buckets[i].value = value;
|
} else {
|
||||||
dict->buckets[i].flags &= GST_DICT_FLAGS_OCCUPIED;
|
/* Check for resize */
|
||||||
dict->count++;
|
if (obj->count + 1 > obj->capacity) {
|
||||||
return dict;
|
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;
|
||||||
}
|
}
|
||||||
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 */
|
/* Remove item from dictionary */
|
||||||
int gst_dict_remove(GstDict *dict, GstValue key) {
|
void gst_dict_remove(GstDict *obj, GstValue key) {
|
||||||
GstDictBucket *bucket;
|
if (obj->flags & GST_OBJECT_FLAG_ISBAG) {
|
||||||
int found = gst_dict_find(dict, key, &bucket);
|
gst_object_bag_remove(obj, key);
|
||||||
if (found) {
|
} else {
|
||||||
bucket->flags |= GST_DICT_FLAGS_TOMBSTONE;
|
GstValue *bucket, *out;
|
||||||
dict->count--;
|
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
35
dict.h
@ -3,39 +3,36 @@
|
|||||||
|
|
||||||
#include "datatypes.h"
|
#include "datatypes.h"
|
||||||
|
|
||||||
#define GST_DICT_FLAG_OCCUPIED 1
|
/* Indicates object is implement as unsorted array of keypairs */
|
||||||
#define GST_DICT_FLAG_TOMBSTONE 2
|
#define GST_OBJECT_FLAG_ISBAG (1 << 31)
|
||||||
|
|
||||||
typedef struct GstDictBucket GstDictBucket;
|
/* Indicates object is immutable */
|
||||||
struct GstDictBucket {
|
#define GST_OBJECT_IMMUTABLE (1 << 30)
|
||||||
GstValue key;
|
|
||||||
GstValue value;
|
/* Count at which the object goes from a linear search to a hash table */
|
||||||
uint8_t flags;
|
#define GST_OBJECT_BAG_THRESHOLD 8
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct GstDict GstDict;
|
typedef struct GstDict GstDict;
|
||||||
struct GstDict {
|
struct GstDict {
|
||||||
uint32_t capacity;
|
uint32_t capacity;
|
||||||
uint32_t count;
|
uint32_t count;
|
||||||
GstDictBucket *buckets;
|
uint32_t flags;
|
||||||
|
GstValue *data;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Initialize a dictionary */
|
/* Initialize a dictionary */
|
||||||
GstDict *gst_dict_init(GstDict *dict, uint32_t capacity);
|
GstDict *gst_dict(Gst *vm, uint32_t capacity);
|
||||||
|
|
||||||
/* Deinitialize a dictionary */
|
|
||||||
GstDict *gst_dict_free(GstDict *dict);
|
|
||||||
|
|
||||||
/* Rehash a dictionary */
|
|
||||||
GstDict *gst_dict_rehash(GstDict *dict, uint32_t newCapacity);
|
|
||||||
|
|
||||||
/* Get item from dictionary */
|
/* 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 */
|
/* 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 */
|
/* Remove item from dictionary */
|
||||||
int gst_dict_remove(GstDict *dict, GstValue key);
|
void gst_dict_remove(GstDict *dict, GstValue key);
|
||||||
|
|
||||||
#endif // dict_h_INCLUDED
|
#endif // dict_h_INCLUDED
|
||||||
|
9
ds.c
9
ds.c
@ -14,7 +14,6 @@ GstBuffer *gst_buffer(Gst *vm, uint32_t capacity) {
|
|||||||
buffer->data = data;
|
buffer->data = data;
|
||||||
buffer->count = 0;
|
buffer->count = 0;
|
||||||
buffer->capacity = capacity;
|
buffer->capacity = capacity;
|
||||||
buffer->flags = 0;
|
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,7 +75,6 @@ GstArray *gst_array(Gst * vm, uint32_t capacity) {
|
|||||||
array->data = data;
|
array->data = data;
|
||||||
array->count = 0;
|
array->count = 0;
|
||||||
array->capacity = capacity;
|
array->capacity = capacity;
|
||||||
array->flags = 0;
|
|
||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,7 +179,7 @@ GstObject* gst_object(Gst *vm, uint32_t capacity) {
|
|||||||
o->buckets = buckets;
|
o->buckets = buckets;
|
||||||
o->capacity = capacity;
|
o->capacity = capacity;
|
||||||
o->count = 0;
|
o->count = 0;
|
||||||
o->flags = 0;
|
o->meta = NULL;
|
||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,9 +228,8 @@ GstValue gst_object_get(GstObject *o, GstValue key) {
|
|||||||
|
|
||||||
/* Get a value of the object with a cstring key */
|
/* Get a value of the object with a cstring key */
|
||||||
GstValue gst_object_get_cstring(GstObject *obj, const char *key) {
|
GstValue gst_object_get_cstring(GstObject *obj, const char *key) {
|
||||||
const char *end = key;
|
uint32_t len;
|
||||||
while (*end++);
|
for (len = 0; key[len]; ++len);
|
||||||
uint32_t len = end - key;
|
|
||||||
uint32_t hash = gst_cstring_calchash((uint8_t *)key, len);
|
uint32_t hash = gst_cstring_calchash((uint8_t *)key, len);
|
||||||
uint32_t index = hash % obj->capacity;
|
uint32_t index = hash % obj->capacity;
|
||||||
GstBucket *bucket = obj->buckets[index];
|
GstBucket *bucket = obj->buckets[index];
|
||||||
|
6
gc.c
6
gc.c
@ -149,6 +149,12 @@ void gst_mark(Gst *vm, GstValue *x) {
|
|||||||
bucket = bucket->next;
|
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;
|
break;
|
||||||
|
|
||||||
|
11
thread.c
Normal file
11
thread.c
Normal 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) {
|
||||||
|
|
||||||
|
}
|
8
value.c
8
value.c
@ -444,16 +444,12 @@ const char *gst_set(Gst *vm, GstValue ds, GstValue key, GstValue value) {
|
|||||||
int32_t index;
|
int32_t index;
|
||||||
switch (ds.type) {
|
switch (ds.type) {
|
||||||
case GST_ARRAY:
|
case GST_ARRAY:
|
||||||
if (ds.data.array->flags & GST_IMMUTABLE)
|
|
||||||
return "cannot set immutable value";
|
|
||||||
if (key.type != GST_NUMBER) return "expected numeric key";
|
if (key.type != GST_NUMBER) return "expected numeric key";
|
||||||
index = to_index(key.data.number, ds.data.array->count);
|
index = to_index(key.data.number, ds.data.array->count);
|
||||||
if (index == -1) return "invalid array access";
|
if (index == -1) return "invalid array access";
|
||||||
ds.data.array->data[index] = value;
|
ds.data.array->data[index] = value;
|
||||||
break;
|
break;
|
||||||
case GST_BYTEBUFFER:
|
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 (key.type != GST_NUMBER) return "expected numeric key";
|
||||||
if (value.type != GST_NUMBER) return "expected numeric value";
|
if (value.type != GST_NUMBER) return "expected numeric value";
|
||||||
index = to_index(key.data.number, ds.data.buffer->count);
|
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);
|
ds.data.buffer->data[index] = to_byte(value.data.number);
|
||||||
break;
|
break;
|
||||||
case GST_OBJECT:
|
case GST_OBJECT:
|
||||||
if (ds.data.object->flags & GST_IMMUTABLE)
|
if (ds.data.object->meta != NULL) {
|
||||||
return "cannot set immutable value";
|
}
|
||||||
gst_object_put(vm, ds.data.object, key, value);
|
gst_object_put(vm, ds.data.object, key, value);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
74
vm.c
74
vm.c
@ -382,27 +382,53 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) {
|
|||||||
case GST_OP_PSH: /* Push stack frame */
|
case GST_OP_PSH: /* Push stack frame */
|
||||||
{
|
{
|
||||||
GstValue *nextStack;
|
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 */
|
/* Get arguments to op */
|
||||||
temp = stack[pc[1]];
|
temp = stack[pc[1]];
|
||||||
arity = pc[2];
|
arity = pc[2];
|
||||||
|
|
||||||
/* Get the size of next stack frame */
|
/* Get the size of next stack frame */
|
||||||
if (temp.type == GST_FUNCTION) {
|
prefixCount = 0;
|
||||||
|
recur:
|
||||||
|
switch(temp.type) {
|
||||||
|
default: gst_error(vm, GST_EXPECTED_FUNCTION);
|
||||||
|
case GST_FUNCTION:
|
||||||
|
{
|
||||||
GstFunction *fn = temp.data.function;
|
GstFunction *fn = temp.data.function;
|
||||||
locals = fn->def->locals;
|
locals = fn->def->locals;
|
||||||
varArgs = fn->def->flags & GST_FUNCDEF_FLAG_VARARG;
|
varArgs = fn->def->flags & GST_FUNCDEF_FLAG_VARARG;
|
||||||
expectedArity = fn->def->arity;
|
argSlots = fn->def->arity;
|
||||||
if (arity > expectedArity)
|
if (arity + prefixCount > argSlots) {
|
||||||
normalArity = expectedArity;
|
fullArgSlots = argSlots;
|
||||||
else
|
tupleCount = arity + prefixCount - fullArgSlots;
|
||||||
normalArity = arity;
|
|
||||||
} else if (temp.type == GST_CFUNCTION) {
|
|
||||||
locals = normalArity = expectedArity = arity;
|
|
||||||
varArgs = 0;
|
|
||||||
} else {
|
} else {
|
||||||
gst_error(vm, GST_EXPECTED_FUNCTION);
|
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 */
|
/* Get next frame size */
|
||||||
@ -427,11 +453,15 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) {
|
|||||||
gst_frame_callee(nextStack) = temp;
|
gst_frame_callee(nextStack) = temp;
|
||||||
gst_frame_errjmp(nextStack) = NULL;
|
gst_frame_errjmp(nextStack) = NULL;
|
||||||
|
|
||||||
/* Write arguments to new stack */
|
/* Write prefix args to stack */
|
||||||
for (i = 0; i < normalArity; ++i)
|
for (i = 0; i < prefixCount; ++i)
|
||||||
nextStack[i] = stack[pc[3 + i]];
|
nextStack[i] = vm->tempArray[i];
|
||||||
|
|
||||||
/* Clear stack */
|
/* Write arguments to new stack */
|
||||||
|
for (; i < fullArgSlots; ++i)
|
||||||
|
nextStack[i] = stack[pc[3 + i - prefixCount]];
|
||||||
|
|
||||||
|
/* Clear rest of stack */
|
||||||
for (; i < locals; ++i)
|
for (; i < locals; ++i)
|
||||||
nextStack[i].type = GST_NIL;
|
nextStack[i].type = GST_NIL;
|
||||||
|
|
||||||
@ -439,11 +469,14 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) {
|
|||||||
if (varArgs) {
|
if (varArgs) {
|
||||||
GstValue *tuple;
|
GstValue *tuple;
|
||||||
uint32_t j;
|
uint32_t j;
|
||||||
tuple = gst_tuple(vm, arity - expectedArity);
|
tuple = gst_tuple(vm, tupleCount);
|
||||||
for (j = expectedArity; j < arity; ++j)
|
for (j = argSlots; j < arity; ++j)
|
||||||
tuple[j - expectedArity] = stack[pc[3 + j]];
|
if (j < prefixCount)
|
||||||
nextStack[expectedArity].type = GST_TUPLE;
|
tuple[j - argSlots] = vm->tempArray[j - prefixCount];
|
||||||
nextStack[expectedArity].data.tuple = tuple;
|
else
|
||||||
|
tuple[j - argSlots] = stack[pc[3 + j - prefixCount]];
|
||||||
|
nextStack[argSlots].type = GST_TUPLE;
|
||||||
|
nextStack[argSlots].data.tuple = tuple;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Increment pc */
|
/* Increment pc */
|
||||||
@ -486,6 +519,7 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) {
|
|||||||
} else if (v1.type == GST_CFUNCTION) {
|
} else if (v1.type == GST_CFUNCTION) {
|
||||||
int status;
|
int status;
|
||||||
GST_STATE_WRITE();
|
GST_STATE_WRITE();
|
||||||
|
vm->ret.type = GST_NIL;
|
||||||
status = v1.data.cfunction(vm);
|
status = v1.data.cfunction(vm);
|
||||||
GST_STATE_SYNC();
|
GST_STATE_SYNC();
|
||||||
if (status == GST_RETURN_OK)
|
if (status == GST_RETURN_OK)
|
||||||
|
Loading…
Reference in New Issue
Block a user