1
0
mirror of https://github.com/janet-lang/janet synced 2024-11-28 11:09:54 +00:00

Change object implementaion to use open hashing. Currently

using simple linear probing.
This commit is contained in:
Calvin Rose 2017-04-16 09:39:41 -04:00
parent e90b66af58
commit f456de5fac
7 changed files with 158 additions and 153 deletions

View File

@ -918,21 +918,18 @@ static Slot compile_object(GstCompiler *c, FormOptions opts, GstObject *obj) {
GstScope *scope = c->tail; GstScope *scope = c->tail;
FormOptions subOpts = form_options_default(); FormOptions subOpts = form_options_default();
GstBuffer *buffer = c->buffer; GstBuffer *buffer = c->buffer;
GstBucket *bucket;
Slot ret; Slot ret;
SlotTracker tracker; SlotTracker tracker;
uint32_t i, cap; uint32_t i, cap;
cap = obj->capacity; cap = obj->capacity;
ret = compiler_get_target(c, opts); ret = compiler_get_target(c, opts);
tracker_init(c, &tracker); tracker_init(c, &tracker);
for (i = 0; i < cap; ++i) { for (i = 0; i < cap; i += 2) {
bucket = obj->buckets[i]; if (obj->data[i].type != GST_NIL) {
while (bucket != NULL) { Slot slot = compile_value(c, subOpts, obj->data[i]);
Slot slot = compile_value(c, subOpts, bucket->key);
compiler_tracker_push(c, &tracker, compiler_realize_slot(c, slot)); compiler_tracker_push(c, &tracker, compiler_realize_slot(c, slot));
slot = compile_value(c, subOpts, bucket->value); slot = compile_value(c, subOpts, obj->data[i + 1]);
compiler_tracker_push(c, &tracker, compiler_realize_slot(c, slot)); compiler_tracker_push(c, &tracker, compiler_realize_slot(c, slot));
bucket = bucket->next;
} }
} }
compiler_tracker_free(c, scope, &tracker); compiler_tracker_free(c, scope, &tracker);
@ -1034,15 +1031,10 @@ void gst_compiler_global(GstCompiler *c, const char *name, GstValue x) {
/* Add many global variables */ /* Add many global variables */
void gst_compiler_globals(GstCompiler *c, GstObject *env) { void gst_compiler_globals(GstCompiler *c, GstObject *env) {
uint32_t i; uint32_t i;
GstBucket *bucket; for (i = 0; i < env->capacity; i += 2) {
for (i = 0; i < env->capacity; ++i) { if (env->data[i].type == GST_STRING) {
bucket = env->buckets[i]; compiler_declare_symbol(c, c->tail, env->data[i]);
while (bucket) { gst_array_push(c->vm, c->env, env->data[i + 1]);
if (bucket->key.type == GST_STRING) {
compiler_declare_symbol(c, c->tail, bucket->key);
gst_array_push(c->vm, c->env, bucket->value);
}
bucket = bucket->next;
} }
} }
} }

165
core/ds.c
View File

@ -153,50 +153,60 @@ void *gst_userdata(Gst *vm, uint32_t size, GstObject *meta) {
/* Create a new dictionary */ /* Create a new dictionary */
GstObject* gst_object(Gst *vm, uint32_t capacity) { GstObject* gst_object(Gst *vm, uint32_t capacity) {
GstObject *o = gst_alloc(vm, sizeof(GstObject)); GstObject *o = gst_alloc(vm, sizeof(GstObject));
GstBucket **buckets = gst_zalloc(vm, capacity * sizeof(GstBucket *)); GstValue *data = gst_zalloc(vm, capacity * sizeof(GstValue));
o->buckets = buckets; o->data = data;
o->capacity = capacity; o->capacity = capacity;
o->count = 0; o->count = 0;
o->parent = NULL; o->parent = NULL;
o->deleted = 0;
return o; return o;
} }
/* Find the bucket that contains the given key. Will also return
* bucket where key should go if not in object. */
static GstValue *gst_object_find(GstObject *o, GstValue key) {
uint32_t index = (gst_hash(key) % (o->capacity / 2)) * 2;
uint32_t i, j;
uint32_t start[2], end[2];
start[0] = index; end[0] = o->capacity;
start[1] = 0; end[1] = index;
for (j = 0; j < 2; ++j)
for (i = start[j]; i < end[j]; i += 2) {
if (o->data[i].type == GST_NIL) {
if (o->data[i + 1].type == GST_NIL) {
/* Empty */
return o->data + i;
}
} else if (gst_equals(o->data[i], key)) {
return o->data + i;
}
}
return NULL;
}
/* Resize the dictionary table. */ /* Resize the dictionary table. */
static void gst_object_rehash(Gst *vm, GstObject *o, uint32_t size) { static void gst_object_rehash(Gst *vm, GstObject *o, uint32_t size) {
GstBucket **newBuckets = gst_zalloc(vm, size * sizeof(GstBucket *)); GstValue *olddata = o->data;
uint32_t i, count; GstValue *newdata = gst_zalloc(vm, size * sizeof(GstValue));
for (i = 0, count = o->capacity; i < count; ++i) { uint32_t i, oldcapacity;
GstBucket *bucket = o->buckets[i]; oldcapacity = o->capacity;
while (bucket) { o->data = newdata;
uint32_t index; o->capacity = size;
GstBucket *next = bucket->next; o->deleted = 0;
index = gst_hash(bucket->key) % size; for (i = 0; i < oldcapacity; i += 2) {
bucket->next = newBuckets[index]; if (olddata[i].type != GST_NIL) {
newBuckets[index] = bucket; GstValue *bucket = gst_object_find(o, olddata[i]);
bucket = next; bucket[0] = olddata[i];
bucket[1] = olddata[i + 1];
} }
} }
o->buckets = newBuckets;
o->capacity = size;
}
/* Find the bucket that contains the given key */
static GstBucket *gst_object_find(GstObject *o, GstValue key) {
uint32_t index = gst_hash(key) % o->capacity;
GstBucket *bucket = o->buckets[index];
while (bucket) {
if (gst_equals(bucket->key, key))
return bucket;
bucket = bucket->next;
}
return (GstBucket *)0;
} }
/* Get a value out of the object */ /* Get a value out of the object */
GstValue gst_object_get(GstObject *o, GstValue key) { GstValue gst_object_get(GstObject *o, GstValue key) {
GstBucket *bucket = gst_object_find(o, key); GstValue *bucket = gst_object_find(o, key);
if (bucket) { if (bucket && bucket[0].type != GST_NIL) {
return bucket->value; return bucket[1];
} else { } else {
GstValue nil; GstValue nil;
nil.type = GST_NIL; nil.type = GST_NIL;
@ -205,75 +215,60 @@ GstValue gst_object_get(GstObject *o, GstValue key) {
} }
/* Remove an entry from the dictionary */ /* Remove an entry from the dictionary */
GstValue gst_object_remove(Gst * vm, GstObject *o, GstValue key) { GstValue gst_object_remove(GstObject *o, GstValue key) {
GstBucket *bucket, *previous; GstValue *bucket = gst_object_find(o, key);
uint32_t index = gst_hash(key) % o->capacity; if (bucket && bucket[0].type != GST_NIL) {
bucket = o->buckets[index]; GstValue ret = bucket[1];
previous = (GstBucket *)0; o->count--;
while (bucket) { o->deleted++;
if (gst_equals(bucket->key, key)) { bucket[0].type = GST_NIL;
if (previous) { bucket[1].type = GST_BOOLEAN;
previous->next = bucket->next; return ret;
} else { } else {
o->buckets[index] = bucket->next;
}
if (o->count < o->capacity / 4) {
gst_object_rehash(vm, o, o->capacity / 2);
}
--o->count;
return bucket->value;
}
previous = bucket;
bucket = bucket->next;
}
/* Return nil if we found nothing */
{
GstValue nil; GstValue nil;
nil.type = GST_NIL; nil.type = GST_NIL;
return nil; return nil;
} }
} }
/* Put a value into the dictionary. */ /* Put a value into the object */
void gst_object_put(Gst *vm, GstObject *o, GstValue key, GstValue value) { void gst_object_put(Gst *vm, GstObject *o, GstValue key, GstValue value) {
GstBucket *bucket, *previous;
uint32_t index = gst_hash(key) % o->capacity;
if (key.type == GST_NIL) return; if (key.type == GST_NIL) return;
/* Do a removal if value is nil */
if (value.type == GST_NIL) { if (value.type == GST_NIL) {
bucket = o->buckets[index]; gst_object_remove(o, key);
previous = (GstBucket *)0;
while (bucket) {
if (gst_equals(bucket->key, key)) {
if (previous) {
previous->next = bucket->next;
} else {
o->buckets[index] = bucket->next;
}
if (o->count < o->capacity / 4) {
gst_object_rehash(vm, o, o->capacity / 2);
}
--o->count;
return;
}
previous = bucket;
bucket = bucket->next;
}
} else { } else {
bucket = gst_object_find(o, key); GstValue *bucket = gst_object_find(o, key);
if (bucket) { if (bucket && bucket[0].type != GST_NIL) {
bucket->value = value; bucket[1] = value;
} else { } else {
if (o->count >= 2 * o->capacity) { if (!bucket || 4 * (o->count + o->deleted) >= o->capacity) {
gst_object_rehash(vm, o, 2 * o->capacity); gst_object_rehash(vm, o, 4 * o->count + 6);
} }
bucket = gst_alloc(vm, sizeof(GstBucket)); bucket = gst_object_find(o, key);
bucket->next = o->buckets[index]; bucket[0] = key;
bucket->value = value; bucket[1] = value;
bucket->key = key;
o->buckets[index] = bucket;
++o->count; ++o->count;
} }
} }
} }
/* Find next key in an object. Returns nil if no next key. */
GstValue gst_object_next(GstObject *o, GstValue key) {
GstValue ret;
GstValue *bucket;
if (key.type == GST_NIL)
bucket = o->data - 2;
else
bucket = gst_object_find(o, key);
if (bucket && bucket[0].type != GST_NIL) {
GstValue *nextbucket, *end;
end = o->data + o->capacity;
for (nextbucket = bucket + 2; nextbucket < end; nextbucket += 2) {
if (nextbucket[0].type != GST_NIL)
return nextbucket[0];
}
}
ret.type = GST_NIL;
return ret;
}

View File

@ -142,16 +142,12 @@ void gst_mark(Gst *vm, GstValueUnion x, GstType type) {
case GST_OBJECT: case GST_OBJECT:
if (gc_header(x.object)->color != vm->black) { if (gc_header(x.object)->color != vm->black) {
uint32_t i; uint32_t i;
GstBucket *bucket;
gc_header(x.object)->color = vm->black; gc_header(x.object)->color = vm->black;
gc_header(x.object->buckets)->color = vm->black; gc_header(x.object->data)->color = vm->black;
for (i = 0; i < x.object->capacity; ++i) { for (i = 0; i < x.object->capacity; i += 2) {
bucket = x.object->buckets[i]; if (x.object->data[i].type != GST_NIL) {
while (bucket) { gst_mark_value(vm, x.object->data[i]);
gc_header(bucket)->color = vm->black; gst_mark_value(vm, x.object->data[i + 1]);
gst_mark_value(vm, bucket->key);
gst_mark_value(vm, bucket->value);
bucket = bucket->next;
} }
} }
if (x.object->parent != NULL) { if (x.object->parent != NULL) {

View File

@ -205,32 +205,40 @@ void gst_cache_remove_struct(Gst *vm, char *structmem) {
/* Begin creation of a struct */ /* Begin creation of a struct */
GstValue *gst_struct_begin(Gst *vm, uint32_t count) { GstValue *gst_struct_begin(Gst *vm, uint32_t count) {
char *data = gst_alloc(vm, sizeof(uint32_t) * 2 + 4 * count * sizeof(GstValue)); char *data = gst_zalloc(vm, sizeof(uint32_t) * 2 + 4 * count * sizeof(GstValue));
GstValue *st = (GstValue *) (data + 2 * sizeof(uint32_t)); GstValue *st = (GstValue *) (data + 2 * sizeof(uint32_t));
gst_struct_length(st) = count; gst_struct_length(st) = count;
return st; return st;
} }
/* Put a kv pair into a struct that has not yet been fully constructed. */ /* Find an item in a struct */
void gst_struct_put(GstValue *st, GstValue key, GstValue value) { static const GstValue *gst_struct_find(const GstValue *st, GstValue key) {
uint32_t cap = gst_struct_capacity(st); uint32_t cap = gst_struct_capacity(st);
uint32_t index = (gst_hash(key) % (cap / 2)) * 2; uint32_t index = (gst_hash(key) % (cap / 2)) * 2;
uint32_t i; uint32_t i;
for (i = index; i < cap; i += 2) { for (i = index; i < cap; i += 2) {
if (st[i + 1].type == GST_NIL) { if (st[i].type == GST_NIL || gst_equals(st[i], key)) {
st[i] = key; return st + i;
st[i + 1] = value;
return;
} }
} }
for (i = 0; i < index; i += 2) { for (i = 0; i < index; i += 2) {
if (st[i + 1].type == GST_NIL) { if (st[i].type == GST_NIL || gst_equals(st[i], key)) {
st[i] = key; return st + i;
st[i + 1] = value;
return;
} }
} }
/* Should not get here if struct was initialized with proper size */ return NULL;
}
/* Put a kv pair into a struct that has not yet been fully constructed.
* Behavior is undefined if too many keys are added, or if a key is added
* twice. Nil keys and values are ignored. */
void gst_struct_put(GstValue *st, GstValue key, GstValue value) {
GstValue *bucket;
if (key.type == GST_NIL || value.type == GST_NIL) return;
bucket = (GstValue *) gst_struct_find(st, key);
if (!bucket) return;
bucket[0] = key;
bucket[1] = value;
} }
/* Finish building a struct */ /* Finish building a struct */
@ -246,27 +254,35 @@ const GstValue *gst_struct_end(Gst *vm, GstValue *st) {
/* Get an item from a struct */ /* Get an item from a struct */
GstValue gst_struct_get(const GstValue *st, GstValue key) { GstValue gst_struct_get(const GstValue *st, GstValue key) {
GstValue *bucket = gst_struct_find(st, key);
if (!bucket || bucket[0].type == GST_NIL) {
GstValue ret;
ret.type = GST_NIL;
return ret;
} else {
return bucket[1];
}
}
/* Get the next key in a struct */
GstValue gst_struct_next(const GstValue *st, GstValue key) {
GstValue ret; GstValue ret;
uint32_t cap = gst_struct_capacity(st); const GstValue *bucket;
uint32_t index = (gst_hash(key) % (cap / 2)) * 2; if (key.type == GST_NIL)
uint32_t i; bucket = st - 2;
for (i = index; i < cap; i += 2) { else
if (st[i + 1].type == GST_NIL) { bucket = gst_struct_find(st, key);
goto notfound; if (bucket && bucket[0].type != GST_NIL) {
} else if (gst_equals(st[i], key)) { const GstValue *nextbucket, *end;
return st[i + 1]; end = st + gst_struct_capacity(st);
for (nextbucket = bucket + 2; nextbucket < end; nextbucket += 2) {
if (nextbucket[0].type != GST_NIL)
return nextbucket[0];
} }
} }
for (i = 0; i < index; i += 2) {
if (st[i + 1].type == GST_NIL) {
goto notfound;
} else if (gst_equals(st[i], key)) {
return st[i + 1];
}
}
notfound:
ret.type = GST_NIL; ret.type = GST_NIL;
return ret; return ret;
} }
/****/ /****/

View File

@ -237,7 +237,7 @@ int gst_stl_object(Gst *vm) {
if (count % 2 != 0) { if (count % 2 != 0) {
gst_c_throwc(vm, "expected even number of arguments"); gst_c_throwc(vm, "expected even number of arguments");
} }
object = gst_object(vm, count / 2); object = gst_object(vm, count * 2);
for (i = 0; i < count; i += 2) { for (i = 0; i < count; i += 2) {
gst_object_put(vm, object, gst_arg(vm, i), gst_arg(vm, i + 1)); gst_object_put(vm, object, gst_arg(vm, i), gst_arg(vm, i + 1));
} }
@ -255,7 +255,7 @@ int gst_stl_struct(Gst *vm) {
if (count % 2 != 0) { if (count % 2 != 0) {
gst_c_throwc(vm, "expected even number of arguments"); gst_c_throwc(vm, "expected even number of arguments");
} }
st = gst_struct_begin(vm, count / 2); st = gst_struct_begin(vm, count * 2);
for (i = 0; i < count; i += 2) { for (i = 0; i < count; i += 2) {
gst_struct_put(st, gst_arg(vm, i), gst_arg(vm, i + 1)); gst_struct_put(st, gst_arg(vm, i), gst_arg(vm, i + 1));
} }
@ -340,6 +340,19 @@ int gst_stl_rawset(Gst *vm) {
} }
} }
/* Get next key in struct or object */
int gst_stl_next(Gst *vm) {
GstValue ds = gst_arg(vm, 0);
GstValue key = gst_arg(vm, 1);
if (ds.type == GST_OBJECT) {
gst_c_return(vm, gst_object_next(ds.data.object, key));
} else if (ds.type == GST_STRUCT) {
gst_c_return(vm, gst_struct_next(ds.data.st, key));
} else {
gst_c_throwc(vm, "expected object or struct");
}
}
/* Print values for inspection */ /* Print values for inspection */
int gst_stl_print(Gst *vm) { int gst_stl_print(Gst *vm) {
uint32_t j, count; uint32_t j, count;

View File

@ -371,7 +371,7 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) {
{ {
uint32_t i = 3; uint32_t i = 3;
uint32_t kvs = pc[2]; uint32_t kvs = pc[2];
GstObject *o = gst_object(vm, kvs + 2); GstObject *o = gst_object(vm, 2 * kvs + 2);
kvs = kvs + 3; kvs = kvs + 3;
while (i < kvs) { while (i < kvs) {
v1 = stack[pc[i++]]; v1 = stack[pc[i++]];

View File

@ -134,9 +134,6 @@ typedef struct GstFuncDef GstFuncDef;
typedef struct GstFuncEnv GstFuncEnv; typedef struct GstFuncEnv GstFuncEnv;
typedef union GstValueUnion GstValueUnion; typedef union GstValueUnion GstValueUnion;
/* Definitely implementation details */
typedef struct GstBucket GstBucket;
/* API Types */ /* API Types */
typedef struct GstModuleItem GstModuleItem; typedef struct GstModuleItem GstModuleItem;
@ -208,7 +205,8 @@ struct GstBuffer {
struct GstObject { struct GstObject {
uint32_t count; uint32_t count;
uint32_t capacity; uint32_t capacity;
GstBucket **buckets; uint32_t deleted;
GstValue *data;
GstObject *parent; GstObject *parent;
}; };
@ -240,13 +238,6 @@ struct GstFunction {
GstFunction *parent; GstFunction *parent;
}; };
/* A hash table bucket in an object */
struct GstBucket {
GstValue key;
GstValue value;
GstBucket *next;
};
/* Contains information about userdata */ /* Contains information about userdata */
struct GstUserdataHeader { struct GstUserdataHeader {
uint32_t size; uint32_t size;
@ -381,6 +372,7 @@ GstValue *gst_struct_begin(Gst *vm, uint32_t count);
void gst_struct_put(GstValue *st, GstValue key, GstValue value); void gst_struct_put(GstValue *st, GstValue key, GstValue value);
const GstValue *gst_struct_end(Gst *vm, GstValue *st); const GstValue *gst_struct_end(Gst *vm, GstValue *st);
GstValue gst_struct_get(const GstValue *st, GstValue key); GstValue gst_struct_get(const GstValue *st, GstValue key);
GstValue gst_struct_next(const GstValue *st, GstValue key);
/****/ /****/
/* Object functions */ /* Object functions */
@ -388,8 +380,9 @@ GstValue gst_struct_get(const GstValue *st, GstValue key);
GstObject *gst_object(Gst *vm, uint32_t capacity); GstObject *gst_object(Gst *vm, uint32_t capacity);
GstValue gst_object_get(GstObject *obj, GstValue key); GstValue gst_object_get(GstObject *obj, GstValue key);
GstValue gst_object_remove(Gst *vm, GstObject *obj, GstValue key); GstValue gst_object_remove(GstObject *obj, GstValue key);
void gst_object_put(Gst *vm, GstObject *obj, GstValue key, GstValue value); void gst_object_put(Gst *vm, GstObject *obj, GstValue key, GstValue value);
GstValue gst_object_next(GstObject *o, GstValue key);
/****/ /****/
/* Threads */ /* Threads */