mirror of
https://github.com/janet-lang/janet
synced 2024-11-28 02:59:54 +00:00
Add struct type.
This commit is contained in:
parent
20bb5a18f7
commit
e90b66af58
7
Makefile
7
Makefile
@ -7,6 +7,7 @@ CFLAGS=-std=c99 -Wall -Wextra -Wpedantic -g -I./include
|
||||
PREFIX=/usr/local
|
||||
GST_TARGET=client/gst
|
||||
GST_CORELIB=core/libgst.a
|
||||
GST_INTERNAL_HEADERS=$(addprefix core/, cache.h)
|
||||
GST_HEADERS=$(addprefix include/gst/, gst.h stl.h compile.h disasm.h parse.h)
|
||||
|
||||
all: $(GST_TARGET)
|
||||
@ -15,7 +16,7 @@ all: $(GST_TARGET)
|
||||
##### The core vm and runtime #####
|
||||
###################################
|
||||
GST_CORE_SOURCES=$(addprefix core/,\
|
||||
compile.c disasm.c parse.c stl.c strings.c \
|
||||
compile.c disasm.c parse.c stl.c strings.c ids.c \
|
||||
value.c vm.c ds.c gc.c thread.c serialize.c capi.c)
|
||||
GST_CORE_OBJECTS=$(patsubst %.c,%.o,$(GST_CORE_SOURCES))
|
||||
$(GST_CORELIB): $(GST_CORE_OBJECTS) $(GST_HEADERS)
|
||||
@ -26,11 +27,11 @@ $(GST_CORELIB): $(GST_CORE_OBJECTS) $(GST_HEADERS)
|
||||
##############################
|
||||
GST_CLIENT_SOURCES=client/main.c
|
||||
GST_CLIENT_OBJECTS=$(patsubst %.c,%.o,$(GST_CLIENT_SOURCES))
|
||||
$(GST_TARGET): $(GST_CLIENT_OBJECTS) $(GST_HEADERS) $(GST_CORELIB)
|
||||
$(GST_TARGET): $(GST_CLIENT_OBJECTS) $(GST_CORELIB)
|
||||
$(CC) $(CFLAGS) -o $(GST_TARGET) $(GST_CLIENT_OBJECTS) $(GST_CORELIB)
|
||||
|
||||
# Compile all .c to .o
|
||||
%.o : %.c $(GST_HEADERS)
|
||||
%.o : %.c $(GST_HEADERS) $(GST_INTERNAL_HEADERS)
|
||||
$(CC) $(CFLAGS) -o $@ -c $<
|
||||
|
||||
run: $(GST_TARGET)
|
||||
|
@ -59,7 +59,7 @@ void debug_repl(FILE *in, FILE *out) {
|
||||
gst_compiler(&c, &vm);
|
||||
func.type = GST_NIL;
|
||||
gst_compiler_usemodule(&c, "std");
|
||||
gst_compiler_global(&c, "ans", gst_object_get(vm.rootenv, gst_load_cstring(&vm, "ans")));
|
||||
gst_compiler_global(&c, "ans", gst_object_get(vm.rootenv, gst_string_cv(&vm, "ans")));
|
||||
func.type = GST_FUNCTION;
|
||||
func.data.function = gst_compiler_compile(&c, p.value);
|
||||
|
||||
@ -88,7 +88,7 @@ void debug_repl(FILE *in, FILE *out) {
|
||||
continue;
|
||||
} else if (out) {
|
||||
fprintf(out, "%s\n", (char *)gst_to_string(&vm, vm.ret));
|
||||
gst_object_put(&vm, vm.rootenv, gst_load_cstring(&vm, "ans"), vm.ret);
|
||||
gst_object_put(&vm, vm.rootenv, gst_string_cv(&vm, "ans"), vm.ret);
|
||||
}
|
||||
}
|
||||
|
||||
|
10
core/cache.h
Normal file
10
core/cache.h
Normal file
@ -0,0 +1,10 @@
|
||||
#ifndef CACHE_H_LVYZMBLR
|
||||
#define CACHE_H_LVYZMBLR
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
void gst_cache_remove_string(Gst *vm, char *strmem);
|
||||
void gst_cache_remove_tuple(Gst *vm, char *tuplemem);
|
||||
void gst_cache_remove_struct(Gst *vm, char *structmem);
|
||||
|
||||
#endif /* end of include guard: CACHE_H_LVYZMBLR */
|
@ -3,7 +3,7 @@
|
||||
GstObject *gst_c_module(Gst *vm, const GstModuleItem *mod) {
|
||||
GstObject *module = gst_object(vm, 10);
|
||||
while (mod->name != NULL) {
|
||||
GstValue key = gst_load_cstring(vm, mod->name);
|
||||
GstValue key = gst_string_cv(vm, mod->name);
|
||||
GstValue val;
|
||||
val.type = GST_CFUNCTION;
|
||||
val.data.cfunction = mod->data;
|
||||
@ -19,5 +19,5 @@ void gst_c_register(Gst *vm, const char *packagename, GstObject *mod) {
|
||||
vm->rootenv = gst_object(vm, 10);
|
||||
modv.type = GST_OBJECT;
|
||||
modv.data.object = mod;
|
||||
gst_object_put(vm, vm->rootenv, gst_load_cstring(vm, packagename), modv);
|
||||
gst_object_put(vm, vm->rootenv, gst_string_cv(vm, packagename), modv);
|
||||
}
|
||||
|
@ -488,7 +488,7 @@ static Slot compile_assign(GstCompiler *c, FormOptions opts, GstValue left, GstV
|
||||
|
||||
/* Compile series of expressions. This compiles the meat of
|
||||
* function definitions and the inside of do forms. */
|
||||
static Slot compile_block(GstCompiler *c, FormOptions opts, GstValue *form, uint32_t startIndex) {
|
||||
static Slot compile_block(GstCompiler *c, FormOptions opts, const GstValue *form, uint32_t startIndex) {
|
||||
GstScope *scope = c->tail;
|
||||
FormOptions subOpts = form_options_default();
|
||||
uint32_t current = startIndex;
|
||||
@ -542,7 +542,7 @@ static GstFuncDef *compiler_gen_funcdef(GstCompiler *c, uint32_t lastNBytes, uin
|
||||
}
|
||||
|
||||
/* Compile a function from a function literal source form */
|
||||
static Slot compile_function(GstCompiler *c, FormOptions opts, GstValue *form) {
|
||||
static Slot compile_function(GstCompiler *c, FormOptions opts, const GstValue *form) {
|
||||
GstScope *scope = c->tail;
|
||||
GstBuffer *buffer = c->buffer;
|
||||
uint32_t current = 1;
|
||||
@ -595,7 +595,7 @@ static Slot compile_function(GstCompiler *c, FormOptions opts, GstValue *form) {
|
||||
}
|
||||
|
||||
/* Branching special */
|
||||
static Slot compile_if(GstCompiler *c, FormOptions opts, GstValue *form) {
|
||||
static Slot compile_if(GstCompiler *c, FormOptions opts, const GstValue *form) {
|
||||
GstScope *scope = c->tail;
|
||||
GstBuffer *buffer = c->buffer;
|
||||
FormOptions condOpts = opts;
|
||||
@ -666,7 +666,7 @@ static Slot compile_if(GstCompiler *c, FormOptions opts, GstValue *form) {
|
||||
}
|
||||
|
||||
/* Try catch special */
|
||||
static Slot compile_try(GstCompiler *c, FormOptions opts, GstValue *form) {
|
||||
static Slot compile_try(GstCompiler *c, FormOptions opts, const GstValue *form) {
|
||||
GstScope *scope = c->tail;
|
||||
GstBuffer *buffer = c->buffer;
|
||||
Slot body;
|
||||
@ -730,7 +730,7 @@ static Slot compile_try(GstCompiler *c, FormOptions opts, GstValue *form) {
|
||||
}
|
||||
|
||||
/* While special */
|
||||
static Slot compile_while(GstCompiler *c, FormOptions opts, GstValue *form) {
|
||||
static Slot compile_while(GstCompiler *c, FormOptions opts, const GstValue *form) {
|
||||
Slot cond;
|
||||
uint32_t countAtStart = c->buffer->count;
|
||||
uint32_t countAtJumpDelta;
|
||||
@ -769,7 +769,7 @@ static Slot compile_while(GstCompiler *c, FormOptions opts, GstValue *form) {
|
||||
}
|
||||
|
||||
/* Do special */
|
||||
static Slot compile_do(GstCompiler *c, FormOptions opts, GstValue *form) {
|
||||
static Slot compile_do(GstCompiler *c, FormOptions opts, const GstValue *form) {
|
||||
Slot ret;
|
||||
compiler_push_scope(c, 1);
|
||||
ret = compile_block(c, opts, form, 1);
|
||||
@ -778,7 +778,7 @@ static Slot compile_do(GstCompiler *c, FormOptions opts, GstValue *form) {
|
||||
}
|
||||
|
||||
/* Quote special - returns its argument as is. */
|
||||
static Slot compile_quote(GstCompiler *c, FormOptions opts, GstValue *form) {
|
||||
static Slot compile_quote(GstCompiler *c, FormOptions opts, const GstValue *form) {
|
||||
GstScope *scope = c->tail;
|
||||
GstBuffer *buffer = c->buffer;
|
||||
Slot ret;
|
||||
@ -801,17 +801,17 @@ static Slot compile_quote(GstCompiler *c, FormOptions opts, GstValue *form) {
|
||||
}
|
||||
|
||||
/* Assignment special */
|
||||
static Slot compile_var(GstCompiler *c, FormOptions opts, GstValue *form) {
|
||||
static Slot compile_var(GstCompiler *c, FormOptions opts, const GstValue *form) {
|
||||
if (gst_tuple_length(form) != 3)
|
||||
c_error(c, "assignment expects 2 arguments");
|
||||
return compile_assign(c, opts, form[1], form[2]);
|
||||
}
|
||||
|
||||
/* Define a function type for Special Form helpers */
|
||||
typedef Slot (*SpecialFormHelper) (GstCompiler *c, FormOptions opts, GstValue *form);
|
||||
typedef Slot (*SpecialFormHelper) (GstCompiler *c, FormOptions opts, const GstValue *form);
|
||||
|
||||
/* Dispatch to a special form */
|
||||
static SpecialFormHelper get_special(GstValue *form) {
|
||||
static SpecialFormHelper get_special(const GstValue *form) {
|
||||
const uint8_t *name;
|
||||
if (gst_tuple_length(form) < 1 || form[0].type != GST_STRING)
|
||||
return NULL;
|
||||
@ -944,7 +944,7 @@ static Slot compile_object(GstCompiler *c, FormOptions opts, GstObject *obj) {
|
||||
}
|
||||
|
||||
/* Compile a form. Checks for special forms and macros. */
|
||||
static Slot compile_form(GstCompiler *c, FormOptions opts, GstValue *form) {
|
||||
static Slot compile_form(GstCompiler *c, FormOptions opts, const GstValue *form) {
|
||||
GstScope *scope = c->tail;
|
||||
GstBuffer *buffer = c->buffer;
|
||||
SpecialFormHelper helper;
|
||||
@ -1026,7 +1026,7 @@ void gst_compiler(GstCompiler *c, Gst *vm) {
|
||||
|
||||
/* Add a global variable */
|
||||
void gst_compiler_global(GstCompiler *c, const char *name, GstValue x) {
|
||||
GstValue sym = gst_load_cstring(c->vm, name);
|
||||
GstValue sym = gst_string_cv(c->vm, name);
|
||||
compiler_declare_symbol(c, c->tail, sym);
|
||||
gst_array_push(c->vm, c->env, x);
|
||||
}
|
||||
@ -1049,7 +1049,7 @@ void gst_compiler_globals(GstCompiler *c, GstObject *env) {
|
||||
|
||||
/* Use a module that was loaded into the vm */
|
||||
void gst_compiler_usemodule(GstCompiler *c, const char *modulename) {
|
||||
GstValue mod = gst_object_get(c->vm->rootenv, gst_load_cstring(c->vm, modulename));
|
||||
GstValue mod = gst_object_get(c->vm->rootenv, gst_string_cv(c->vm, modulename));
|
||||
if (mod.type == GST_OBJECT) {
|
||||
gst_compiler_globals(c, mod.data.object);
|
||||
}
|
||||
|
17
core/ds.c
17
core/ds.c
@ -53,7 +53,7 @@ void gst_buffer_append(Gst *vm, GstBuffer *buffer, const uint8_t *string, uint32
|
||||
|
||||
/* Convert the buffer to a string */
|
||||
const uint8_t *gst_buffer_to_string(Gst *vm, GstBuffer *buffer) {
|
||||
return gst_string_loadbuffer(vm, buffer->data, buffer->count);
|
||||
return gst_string_b(vm, buffer->data, buffer->count);
|
||||
}
|
||||
|
||||
/****/
|
||||
@ -132,20 +132,6 @@ GstValue gst_array_peek(GstArray *array) {
|
||||
}
|
||||
}
|
||||
|
||||
/****/
|
||||
/* Tuple functions */
|
||||
/****/
|
||||
|
||||
/* Create a new empty tuple of the given size. Expected to be
|
||||
* mutated immediately */
|
||||
GstValue *gst_tuple(Gst *vm, uint32_t length) {
|
||||
char *data = gst_alloc(vm, 2 * sizeof(uint32_t) + length * sizeof(GstValue));
|
||||
GstValue *tuple = (GstValue *)(data + (2 * sizeof(uint32_t)));
|
||||
gst_tuple_length(tuple) = length;
|
||||
gst_tuple_hash(tuple) = 0;
|
||||
return tuple;
|
||||
}
|
||||
|
||||
/****/
|
||||
/* Userdata functions */
|
||||
/****/
|
||||
@ -290,3 +276,4 @@ void gst_object_put(Gst *vm, GstObject *o, GstValue key, GstValue value) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
22
core/gc.c
22
core/gc.c
@ -1,5 +1,5 @@
|
||||
#include <gst/gst.h>
|
||||
#include "stringcache.h"
|
||||
#include "cache.h"
|
||||
|
||||
/* The metadata header associated with an allocated block of memory */
|
||||
#define gc_header(mem) ((GCMemoryHeader *)(mem) - 1)
|
||||
@ -102,6 +102,16 @@ void gst_mark(Gst *vm, GstValueUnion x, GstType type) {
|
||||
}
|
||||
break;
|
||||
|
||||
case GST_STRUCT:
|
||||
if (gc_header(gst_struct_raw(x.st))->color != vm->black) {
|
||||
uint32_t i, count;
|
||||
count = gst_struct_capacity(x.st);
|
||||
gc_header(gst_struct_raw(x.st))->color = vm->black;
|
||||
for (i = 0; i < count; ++i)
|
||||
gst_mark_value(vm, x.st[i]);
|
||||
}
|
||||
break;
|
||||
|
||||
case GST_THREAD:
|
||||
if (gc_header(x.thread)->color != vm->black) {
|
||||
GstThread *thread = x.thread;
|
||||
@ -185,9 +195,13 @@ void gst_sweep(Gst *vm) {
|
||||
} else {
|
||||
vm->blocks = next;
|
||||
}
|
||||
/* Remove from string cache */
|
||||
if (current->tags & GST_MEMTAG_STRING) {
|
||||
gst_stringcache_remove(vm, (uint8_t *)(current + 1) + 2 * sizeof(uint32_t));
|
||||
if (current->tags) {
|
||||
if (current->tags & GST_MEMTAG_STRING)
|
||||
gst_cache_remove_string(vm, (char *)(current + 1));
|
||||
if (current->tags & GST_MEMTAG_STRUCT)
|
||||
gst_cache_remove_struct(vm, (char *)(current + 1));
|
||||
if (current->tags & GST_MEMTAG_TUPLE)
|
||||
gst_cache_remove_tuple(vm, (char *)(current + 1));
|
||||
}
|
||||
gst_raw_free(current);
|
||||
} else {
|
||||
|
361
core/ids.c
Normal file
361
core/ids.c
Normal file
@ -0,0 +1,361 @@
|
||||
#include <gst/gst.h>
|
||||
|
||||
/****/
|
||||
/* Cache */
|
||||
/****/
|
||||
|
||||
/* Calculate hash for string */
|
||||
static uint32_t gst_string_calchash(const uint8_t *str, uint32_t len) {
|
||||
const uint8_t *end = str + len;
|
||||
uint32_t hash = 5381;
|
||||
while (str < end)
|
||||
hash = (hash << 5) + hash + *str++;
|
||||
return hash;
|
||||
}
|
||||
|
||||
/* Calculate hash for tuple (and struct) */
|
||||
static uint32_t gst_tuple_calchash(const GstValue *tuple, uint32_t len) {
|
||||
const GstValue *end = tuple + len;
|
||||
uint32_t hash = 5381;
|
||||
while (tuple < end)
|
||||
hash = (hash << 5) + hash + gst_hash(*tuple++);
|
||||
return hash;
|
||||
}
|
||||
|
||||
/* Check if two not necesarrily finalized immutable values
|
||||
* are equal. Does caching logic */
|
||||
static int gst_cache_equal(GstValue x, GstValue y) {
|
||||
uint32_t i, len;
|
||||
if (x.type != y.type) return 0;
|
||||
switch (x.type) {
|
||||
/* Don't bother implemeting equality checks for all types. We only care
|
||||
* about immutable data structures */
|
||||
default:
|
||||
return 0;
|
||||
case GST_STRING:
|
||||
if (gst_string_hash(x.data.string) != gst_string_hash(y.data.string)) return 0;
|
||||
if (gst_string_length(x.data.string) != gst_string_length(y.data.string)) return 0;
|
||||
len = gst_string_length(x.data.string);
|
||||
for (i = 0; i < len; ++i)
|
||||
if (x.data.string[i] != y.data.string[i])
|
||||
return 0;
|
||||
return 1;
|
||||
case GST_STRUCT:
|
||||
if (gst_struct_hash(x.data.st) != gst_struct_hash(y.data.st)) return 0;
|
||||
if (gst_struct_length(x.data.st) != gst_struct_length(y.data.st)) return 0;
|
||||
len = gst_struct_capacity(x.data.st);
|
||||
for (i = 0; i < len; ++i)
|
||||
if (!gst_equals(x.data.st[i], y.data.st[i]))
|
||||
return 0;
|
||||
return 1;
|
||||
case GST_TUPLE:
|
||||
if (gst_tuple_hash(x.data.tuple) != gst_tuple_hash(y.data.tuple)) return 0;
|
||||
if (gst_tuple_length(x.data.tuple) != gst_tuple_length(y.data.tuple)) return 0;
|
||||
len = gst_tuple_length(x.data.tuple);
|
||||
for (i = 0; i < len; ++i)
|
||||
if (!gst_equals(x.data.tuple[i], y.data.tuple[i]))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Find an item in the cache and return its location.
|
||||
* If the item is not found, return the location
|
||||
* where one would put it. */
|
||||
static GstValue *gst_cache_find(Gst *vm, GstValue key, int *success) {
|
||||
uint32_t bounds[4];
|
||||
uint32_t i, j, index;
|
||||
uint32_t hash = gst_hash(key);
|
||||
GstValue *firstEmpty = NULL;
|
||||
index = hash % vm->cache_capacity;
|
||||
bounds[0] = index;
|
||||
bounds[1] = vm->cache_capacity;
|
||||
bounds[2] = 0;
|
||||
bounds[3] = index;
|
||||
for (j = 0; j < 4; j += 2)
|
||||
for (i = bounds[j]; i < bounds[j+1]; ++i) {
|
||||
GstValue test = vm->cache[i];
|
||||
/* Check empty spots */
|
||||
if (test.type == GST_NIL) {
|
||||
if (firstEmpty == NULL)
|
||||
firstEmpty = vm->cache + i;
|
||||
goto notfound;
|
||||
}
|
||||
/* Check for marked deleted - use booleans as deleted */
|
||||
if (test.type == GST_BOOLEAN) {
|
||||
if (firstEmpty == NULL)
|
||||
firstEmpty = vm->cache + i;
|
||||
continue;
|
||||
}
|
||||
if (gst_cache_equal(test, key)) {
|
||||
/* Replace first deleted */
|
||||
*success = 1;
|
||||
if (firstEmpty != NULL) {
|
||||
*firstEmpty = test;
|
||||
vm->cache[i].type = GST_BOOLEAN;
|
||||
return firstEmpty;
|
||||
}
|
||||
return vm->cache + i;
|
||||
}
|
||||
}
|
||||
notfound:
|
||||
*success = 0;
|
||||
return firstEmpty;
|
||||
}
|
||||
|
||||
/* Resize the cache. */
|
||||
static void gst_cache_resize(Gst *vm, uint32_t newCapacity) {
|
||||
uint32_t i, oldCapacity;
|
||||
GstValue *oldCache = vm->cache;
|
||||
GstValue *newCache = gst_raw_calloc(1, newCapacity * sizeof(GstValue));
|
||||
if (newCache == NULL)
|
||||
GST_OUT_OF_MEMORY;
|
||||
oldCapacity = vm->cache_capacity;
|
||||
vm->cache = newCache;
|
||||
vm->cache_capacity = newCapacity;
|
||||
vm->cache_deleted = 0;
|
||||
/* Add all of the old strings back */
|
||||
for (i = 0; i < oldCapacity; ++i) {
|
||||
int status;
|
||||
GstValue *bucket;
|
||||
GstValue x = oldCache[i];
|
||||
if (x.type != GST_NIL && x.type != GST_BOOLEAN) {
|
||||
bucket = gst_cache_find(vm, x, &status);
|
||||
if (status || bucket == NULL) {
|
||||
/* there was a problem with the algorithm. */
|
||||
break;
|
||||
}
|
||||
*bucket = x;
|
||||
}
|
||||
}
|
||||
/* Free the old cache */
|
||||
gst_raw_free(oldCache);
|
||||
}
|
||||
|
||||
/* Add a value to the cache */
|
||||
static GstValue gst_cache_add(Gst *vm, GstValue x) {
|
||||
int status = 0;
|
||||
GstValue *bucket = gst_cache_find(vm, x, &status);
|
||||
if (!status) {
|
||||
if ((vm->cache_count + vm->cache_deleted) * 2 > vm->cache_capacity) {
|
||||
gst_cache_resize(vm, vm->cache_count * 4);
|
||||
bucket = gst_cache_find(vm, x, &status);
|
||||
}
|
||||
/* Mark the memory for the gc */
|
||||
switch (x.type) {
|
||||
default:
|
||||
break;
|
||||
case GST_STRING:
|
||||
gst_mem_tag(gst_string_raw(x.data.string), GST_MEMTAG_STRING);
|
||||
break;
|
||||
case GST_STRUCT:
|
||||
gst_mem_tag(gst_struct_raw(x.data.st), GST_MEMTAG_STRUCT);
|
||||
break;
|
||||
case GST_TUPLE:
|
||||
gst_mem_tag(gst_tuple_raw(x.data.tuple), GST_MEMTAG_TUPLE);
|
||||
break;
|
||||
}
|
||||
/* Add x to the cache */
|
||||
vm->cache_count++;
|
||||
*bucket = x;
|
||||
return x;
|
||||
} else {
|
||||
return *bucket;
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove a value from the cache */
|
||||
static void gst_cache_remove(Gst *vm, GstValue x) {
|
||||
int status = 0;
|
||||
GstValue *bucket = gst_cache_find(vm, x, &status);
|
||||
if (status) {
|
||||
vm->cache_count--;
|
||||
vm->cache_deleted++;
|
||||
bucket->type = GST_BOOLEAN;
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove a string from cache (called from gc) */
|
||||
void gst_cache_remove_string(Gst *vm, char *strmem) {
|
||||
GstValue x;
|
||||
x.type = GST_STRING;
|
||||
x.data.string = (const uint8_t *)(strmem + 2 * sizeof(uint32_t));
|
||||
gst_cache_remove(vm, x);
|
||||
}
|
||||
|
||||
/* Remove a tuple from cache (called from gc) */
|
||||
void gst_cache_remove_tuple(Gst *vm, char *tuplemem) {
|
||||
GstValue x;
|
||||
x.type = GST_TUPLE;
|
||||
x.data.tuple = (const GstValue *)(tuplemem + 2 * sizeof(uint32_t));
|
||||
gst_cache_remove(vm, x);
|
||||
}
|
||||
|
||||
/* Remove a struct from cache (called from gc) */
|
||||
void gst_cache_remove_struct(Gst *vm, char *structmem) {
|
||||
GstValue x;
|
||||
x.type = GST_STRUCT;
|
||||
x.data.st = (const GstValue *)(structmem + 2 * sizeof(uint32_t));
|
||||
gst_cache_remove(vm, x);
|
||||
}
|
||||
|
||||
/****/
|
||||
/* Struct Functions */
|
||||
/****/
|
||||
|
||||
/* Begin creation of a struct */
|
||||
GstValue *gst_struct_begin(Gst *vm, uint32_t count) {
|
||||
char *data = gst_alloc(vm, sizeof(uint32_t) * 2 + 4 * count * sizeof(GstValue));
|
||||
GstValue *st = (GstValue *) (data + 2 * sizeof(uint32_t));
|
||||
gst_struct_length(st) = count;
|
||||
return st;
|
||||
}
|
||||
|
||||
/* Put a kv pair into a struct that has not yet been fully constructed. */
|
||||
void gst_struct_put(GstValue *st, GstValue key, GstValue value) {
|
||||
uint32_t cap = gst_struct_capacity(st);
|
||||
uint32_t index = (gst_hash(key) % (cap / 2)) * 2;
|
||||
uint32_t i;
|
||||
for (i = index; i < cap; i += 2) {
|
||||
if (st[i + 1].type == GST_NIL) {
|
||||
st[i] = key;
|
||||
st[i + 1] = value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < index; i += 2) {
|
||||
if (st[i + 1].type == GST_NIL) {
|
||||
st[i] = key;
|
||||
st[i + 1] = value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* Should not get here if struct was initialized with proper size */
|
||||
}
|
||||
|
||||
/* Finish building a struct */
|
||||
const GstValue *gst_struct_end(Gst *vm, GstValue *st) {
|
||||
GstValue cached;
|
||||
GstValue check;
|
||||
gst_struct_hash(st) = gst_tuple_calchash(st, gst_struct_capacity(st));
|
||||
check.type = GST_STRUCT;
|
||||
check.data.st = (const GstValue *) st;
|
||||
cached = gst_cache_add(vm, check);
|
||||
return cached.data.st;
|
||||
}
|
||||
|
||||
/* Get an item from a struct */
|
||||
GstValue gst_struct_get(const GstValue *st, GstValue key) {
|
||||
GstValue ret;
|
||||
uint32_t cap = gst_struct_capacity(st);
|
||||
uint32_t index = (gst_hash(key) % (cap / 2)) * 2;
|
||||
uint32_t i;
|
||||
for (i = index; i < cap; i += 2) {
|
||||
if (st[i + 1].type == GST_NIL) {
|
||||
goto notfound;
|
||||
} else if (gst_equals(st[i], key)) {
|
||||
return st[i + 1];
|
||||
}
|
||||
}
|
||||
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;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****/
|
||||
/* Tuple functions */
|
||||
/****/
|
||||
|
||||
/* Create a new empty tuple of the given size. Expected to be
|
||||
* mutated immediately */
|
||||
GstValue *gst_tuple_begin(Gst *vm, uint32_t length) {
|
||||
char *data = gst_alloc(vm, 2 * sizeof(uint32_t) + length * sizeof(GstValue));
|
||||
GstValue *tuple = (GstValue *)(data + (2 * sizeof(uint32_t)));
|
||||
gst_tuple_length(tuple) = length;
|
||||
return tuple;
|
||||
}
|
||||
|
||||
/* Finish building a tuple */
|
||||
const GstValue *gst_tuple_end(Gst *vm, GstValue *tuple) {
|
||||
GstValue cached;
|
||||
GstValue check;
|
||||
gst_tuple_hash(tuple) = gst_tuple_calchash(tuple, gst_tuple_length(tuple));
|
||||
check.type = GST_TUPLE;
|
||||
check.data.tuple = (const GstValue *) tuple;
|
||||
cached = gst_cache_add(vm, check);
|
||||
return cached.data.tuple;
|
||||
}
|
||||
|
||||
/****/
|
||||
/* String Functions */
|
||||
/****/
|
||||
|
||||
/* Begin building a string */
|
||||
uint8_t *gst_string_begin(Gst *vm, uint32_t length) {
|
||||
char *data = gst_alloc(vm, 2 * sizeof(uint32_t) + length + 1);
|
||||
uint8_t *str = (uint8_t *) (data + 2 * sizeof(uint32_t));
|
||||
gst_string_length(str) = length;
|
||||
str[length] = 0;
|
||||
return str;
|
||||
}
|
||||
|
||||
/* Finish building a string */
|
||||
const uint8_t *gst_string_end(Gst *vm, uint8_t *str) {
|
||||
GstValue cached;
|
||||
GstValue check;
|
||||
gst_string_hash(str) = gst_string_calchash(str, gst_string_length(str));
|
||||
check.type = GST_STRING;
|
||||
check.data.string = (const uint8_t *) str;
|
||||
cached = gst_cache_add(vm, check);
|
||||
return cached.data.string;
|
||||
|
||||
}
|
||||
|
||||
/* Load a buffer as a string */
|
||||
const uint8_t *gst_string_b(Gst *vm, const uint8_t *buf, uint32_t len) {
|
||||
GstValue cached;
|
||||
GstValue check;
|
||||
uint32_t newbufsize = len + 2 * sizeof(uint32_t) + 1;
|
||||
uint8_t *str;
|
||||
/* Ensure enough scratch memory */
|
||||
if (vm->scratch_len < newbufsize) {
|
||||
vm->scratch = gst_alloc(vm, newbufsize);
|
||||
vm->scratch_len = newbufsize;
|
||||
}
|
||||
str = (uint8_t *)(vm->scratch + 2 * sizeof(uint32_t));
|
||||
gst_memcpy(str, buf, len);
|
||||
gst_string_length(str) = len;
|
||||
gst_string_hash(str) = gst_string_calchash(str, gst_string_length(str));
|
||||
str[len] = 0;
|
||||
check.type = GST_STRING;
|
||||
check.data.string = (const uint8_t *) str;
|
||||
cached = gst_cache_add(vm, check);
|
||||
if (cached.data.string == (const uint8_t *) str) {
|
||||
vm->scratch_len = 0;
|
||||
vm->scratch = NULL;
|
||||
}
|
||||
return cached.data.string;
|
||||
}
|
||||
|
||||
/* Load a c string */
|
||||
const uint8_t *gst_string_c(Gst *vm, const char *str) {
|
||||
uint32_t len = 0;
|
||||
while (str[len]) ++len;
|
||||
return gst_string_b(vm, (const uint8_t *)str, len);
|
||||
}
|
||||
|
||||
/* Load a c string and return it as a GstValue */
|
||||
GstValue gst_string_cv(Gst *vm, const char *str) {
|
||||
GstValue ret;
|
||||
const uint8_t *data = gst_string_c(vm, str);
|
||||
ret.type = GST_STRING;
|
||||
ret.data.string = data;
|
||||
return ret;
|
||||
}
|
14
core/parse.c
14
core/parse.c
@ -65,11 +65,11 @@ static GstValue quote(GstParser *p, GstValue x) {
|
||||
/* Load a quote form to get the string literal */
|
||||
GstValue tuplev;
|
||||
GstValue *tuple;
|
||||
tuple = gst_tuple(p->vm, 2);
|
||||
tuplev.type = GST_TUPLE;
|
||||
tuplev.data.tuple = tuple;
|
||||
tuple[0] = gst_load_cstring(p->vm, "quote");
|
||||
tuple = gst_tuple_begin(p->vm, 2);
|
||||
tuple[0] = gst_string_cv(p->vm, "quote");
|
||||
tuple[1] = x;
|
||||
tuplev.type = GST_TUPLE;
|
||||
tuplev.data.tuple = gst_tuple_end(p->vm, tuple);
|
||||
return tuplev;
|
||||
}
|
||||
|
||||
@ -376,9 +376,11 @@ static int form_state(GstParser *p, uint8_t c) {
|
||||
x.type = GST_ARRAY;
|
||||
x.data.array = array;
|
||||
} else if (c == ')') {
|
||||
GstValue *tup;
|
||||
tup = gst_tuple_begin(p->vm, array->count);
|
||||
gst_memcpy(tup, array->data, array->count * sizeof(GstValue));
|
||||
x.type = GST_TUPLE;
|
||||
x.data.tuple = gst_tuple(p->vm, array->count);
|
||||
gst_memcpy(x.data.tuple, array->data, array->count * sizeof(GstValue));
|
||||
x.data.tuple = gst_tuple_end(p->vm, tup);
|
||||
} else { /* c == '{' */
|
||||
uint32_t i;
|
||||
if (array->count % 2 != 0) {
|
||||
|
@ -78,7 +78,6 @@ static const char *gst_deserialize_impl(
|
||||
GstValue ret;
|
||||
ret.type = GST_NIL;
|
||||
GstValue *buffer;
|
||||
const uint8_t *bytebuf;
|
||||
uint32_t length, i;
|
||||
const char *err;
|
||||
|
||||
@ -137,11 +136,27 @@ static const char *gst_deserialize_impl(
|
||||
ret.type = GST_STRING;
|
||||
read_u32(length);
|
||||
deser_datacheck(length);
|
||||
ret.data.string = gst_string_loadbuffer(vm, data, length);
|
||||
ret.data.string = gst_string_b(vm, data, length);
|
||||
data += length;
|
||||
gst_array_push(vm, visited, ret);
|
||||
break;
|
||||
|
||||
case 206: /* Struct */
|
||||
ret.type = GST_STRUCT;
|
||||
read_u32(length);
|
||||
buffer = gst_struct_begin(vm, length);
|
||||
for (i = 0; i < length; ++i) {
|
||||
GstValue k, v;
|
||||
if ((err = gst_deserialize_impl(vm, data, end, &data, visited, &k)))
|
||||
return err;
|
||||
if ((err = gst_deserialize_impl(vm, data, end, &data, visited, &v)))
|
||||
return err;
|
||||
gst_struct_put(buffer, k, v);
|
||||
}
|
||||
ret.data.st = gst_struct_end(vm, buffer);
|
||||
gst_array_push(vm, visited, ret);
|
||||
break;
|
||||
|
||||
case 207: /* Buffer */
|
||||
ret.type = GST_BYTEBUFFER;
|
||||
read_u32(length);
|
||||
@ -172,14 +187,12 @@ static const char *gst_deserialize_impl(
|
||||
case 209: /* Tuple */
|
||||
ret.type = GST_TUPLE;
|
||||
read_u32(length);
|
||||
bytebuf = gst_alloc(vm, length * sizeof(GstValue) + 2 * sizeof(uint32_t));
|
||||
buffer = (GstValue *)(bytebuf + 2 * sizeof(uint32_t));
|
||||
buffer = gst_tuple_begin(vm, length);
|
||||
for (i = 0; i < length; ++i)
|
||||
if ((err = gst_deserialize_impl(vm, data, end, &data, visited, buffer + i)))
|
||||
return err;
|
||||
gst_tuple_hash(buffer) = 0;
|
||||
gst_tuple_length(buffer) = length;
|
||||
ret.data.tuple = buffer;
|
||||
ret.type = GST_TUPLE;
|
||||
ret.data.tuple = gst_tuple_end(vm, buffer);
|
||||
gst_array_push(vm, visited, ret);
|
||||
break;
|
||||
|
||||
|
31
core/stl.c
31
core/stl.c
@ -197,7 +197,7 @@ int gst_stl_type(Gst *vm) {
|
||||
typestr = "funcdef";
|
||||
break;
|
||||
}
|
||||
gst_c_return(vm, gst_load_cstring(vm, typestr));
|
||||
gst_c_return(vm, gst_string_cv(vm, typestr));
|
||||
}
|
||||
|
||||
/* Create array */
|
||||
@ -219,12 +219,12 @@ int gst_stl_tuple(Gst *vm) {
|
||||
uint32_t i;
|
||||
uint32_t count = gst_count_args(vm);
|
||||
GstValue ret;
|
||||
GstValue *tuple= gst_tuple(vm, count);
|
||||
GstValue *tuple= gst_tuple_begin(vm, count);
|
||||
for (i = 0; i < count; ++i) {
|
||||
tuple[i] = gst_arg(vm, i);
|
||||
}
|
||||
ret.type = GST_TUPLE;
|
||||
ret.data.tuple = tuple;
|
||||
ret.data.tuple = gst_tuple_end(vm, tuple);
|
||||
gst_c_return(vm, ret);
|
||||
}
|
||||
|
||||
@ -237,7 +237,7 @@ int gst_stl_object(Gst *vm) {
|
||||
if (count % 2 != 0) {
|
||||
gst_c_throwc(vm, "expected even number of arguments");
|
||||
}
|
||||
object = gst_object(vm, count);
|
||||
object = gst_object(vm, count / 2);
|
||||
for (i = 0; i < count; i += 2) {
|
||||
gst_object_put(vm, object, gst_arg(vm, i), gst_arg(vm, i + 1));
|
||||
}
|
||||
@ -246,6 +246,24 @@ int gst_stl_object(Gst *vm) {
|
||||
gst_c_return(vm, ret);
|
||||
}
|
||||
|
||||
/* Create struct */
|
||||
int gst_stl_struct(Gst *vm) {
|
||||
uint32_t i;
|
||||
uint32_t count = gst_count_args(vm);
|
||||
GstValue ret;
|
||||
GstValue *st;
|
||||
if (count % 2 != 0) {
|
||||
gst_c_throwc(vm, "expected even number of arguments");
|
||||
}
|
||||
st = gst_struct_begin(vm, count / 2);
|
||||
for (i = 0; i < count; i += 2) {
|
||||
gst_struct_put(st, gst_arg(vm, i), gst_arg(vm, i + 1));
|
||||
}
|
||||
ret.type = GST_STRUCT;
|
||||
ret.data.st = gst_struct_end(vm, st);
|
||||
gst_c_return(vm, ret);
|
||||
}
|
||||
|
||||
/* Create a buffer */
|
||||
int gst_stl_buffer(Gst *vm) {
|
||||
uint32_t i, count;
|
||||
@ -308,7 +326,6 @@ int gst_stl_rawget(Gst *vm) {
|
||||
|
||||
/* Associative rawset */
|
||||
int gst_stl_rawset(Gst *vm) {
|
||||
GstValue ret;
|
||||
uint32_t count;
|
||||
const char *err;
|
||||
count = gst_count_args(vm);
|
||||
@ -319,8 +336,7 @@ int gst_stl_rawset(Gst *vm) {
|
||||
if (err != NULL) {
|
||||
gst_c_throwc(vm, err);
|
||||
} else {
|
||||
ret.type = GST_NIL;
|
||||
gst_c_return(vm, ret);
|
||||
gst_c_return(vm, gst_arg(vm, 0));
|
||||
}
|
||||
}
|
||||
|
||||
@ -408,6 +424,7 @@ static const GstModuleItem const std_module[] = {
|
||||
{"array", gst_stl_array},
|
||||
{"tuple", gst_stl_tuple},
|
||||
{"object", gst_stl_object},
|
||||
{"struct", gst_stl_struct},
|
||||
{"buffer", gst_stl_buffer},
|
||||
{"strcat", gst_stl_strcat},
|
||||
{"print", gst_stl_print},
|
||||
|
@ -1,14 +0,0 @@
|
||||
#ifndef GST_STRINGCACHE_defined
|
||||
#define GST_STRINGCACHE_defined
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
/****/
|
||||
/* String Cache (move internal) */
|
||||
/****/
|
||||
|
||||
void gst_stringcache_init(Gst *vm, uint32_t capacity);
|
||||
void gst_stringcache_deinit(Gst *vm);
|
||||
void gst_stringcache_remove(Gst *vm, const uint8_t *str);
|
||||
|
||||
#endif
|
222
core/strings.c
222
core/strings.c
@ -1,224 +1,4 @@
|
||||
#include <gst/gst.h>
|
||||
#include "stringcache.h"
|
||||
|
||||
/* Dud pointer to serve as deletion marker */
|
||||
static uint8_t *deleted = (uint8_t *) "";
|
||||
|
||||
/* Check if string and cstring are equal */
|
||||
/* To check if strings are equal externally, one can
|
||||
* just use == */
|
||||
static int gst_cstring_equal(const uint8_t *lhs, const uint8_t *rhs, uint32_t rlen, uint32_t rhash) {
|
||||
uint32_t lhash, len, i;
|
||||
/* Check lengths */
|
||||
len = gst_string_length(lhs);
|
||||
if (len != rlen) return 0;
|
||||
/* Check hashes */
|
||||
lhash = gst_string_hash(lhs);
|
||||
if (lhash != rhash) return 0;
|
||||
for (i = 0; i < len; ++i)
|
||||
if (lhs[i] != rhs[i])
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Simple hash function (djb2) */
|
||||
static uint32_t gst_string_calchash(const uint8_t *str, uint32_t len) {
|
||||
const uint8_t *end = str + len;
|
||||
uint32_t hash = 5381;
|
||||
while (str < end)
|
||||
hash = (hash << 5) + hash + *str++;
|
||||
return hash;
|
||||
}
|
||||
|
||||
/* Find a string in the hashtable. Returns null if
|
||||
* not found. */
|
||||
static const uint8_t **gst_stringcache_find(
|
||||
Gst *vm,
|
||||
const uint8_t *str,
|
||||
uint32_t len,
|
||||
uint32_t hash,
|
||||
int *success) {
|
||||
uint32_t bounds[4];
|
||||
uint32_t i, j, index;
|
||||
const uint8_t **firstEmpty = NULL;
|
||||
index = hash % vm->stringsCapacity;
|
||||
bounds[0] = index;
|
||||
bounds[1] = vm->stringsCapacity;
|
||||
bounds[2] = 0;
|
||||
bounds[3] = index;
|
||||
for (j = 0; j < 4; j += 2)
|
||||
for (i = bounds[j]; i < bounds[j+1]; ++i) {
|
||||
const uint8_t *testStr = vm->strings[i];
|
||||
/* Check empty spots */
|
||||
if (testStr == NULL) {
|
||||
if (firstEmpty == NULL)
|
||||
firstEmpty = vm->strings + i;
|
||||
goto notfound;
|
||||
}
|
||||
if (testStr == deleted) {
|
||||
if (firstEmpty == NULL)
|
||||
firstEmpty = vm->strings + i;
|
||||
continue;
|
||||
}
|
||||
if (gst_cstring_equal(testStr, str, len, hash)) {
|
||||
/* Replace first deleted */
|
||||
*success = 1;
|
||||
if (firstEmpty != NULL) {
|
||||
*firstEmpty = testStr;
|
||||
vm->strings[i] = deleted;
|
||||
return firstEmpty;
|
||||
}
|
||||
return vm->strings + i;
|
||||
}
|
||||
}
|
||||
notfound:
|
||||
*success = 0;
|
||||
return firstEmpty;
|
||||
}
|
||||
|
||||
/* Resize the hashtable. */
|
||||
static void gst_stringcache_resize(Gst *vm, uint32_t newCapacity) {
|
||||
uint32_t i, oldCapacity;
|
||||
const uint8_t **oldCache = vm->strings;
|
||||
const uint8_t **newCache = gst_raw_calloc(1, newCapacity * sizeof(uint8_t *));
|
||||
if (newCache == NULL)
|
||||
GST_OUT_OF_MEMORY;
|
||||
oldCapacity = vm->stringsCapacity;
|
||||
vm->strings = newCache;
|
||||
vm->stringsCapacity = newCapacity;
|
||||
vm->stringsDeleted = 0;
|
||||
/* Add all of the old strings back */
|
||||
for (i = 0; i < oldCapacity; ++i) {
|
||||
int status;
|
||||
const uint8_t **bucket;
|
||||
const uint8_t *str = oldCache[i];
|
||||
if (str != NULL && str != deleted) {
|
||||
bucket = gst_stringcache_find(vm, str,
|
||||
gst_string_length(str),
|
||||
gst_string_hash(str), &status);
|
||||
if (status || bucket == NULL) {
|
||||
/* there was a problem with the algorithm. */
|
||||
break;
|
||||
}
|
||||
*bucket = str;
|
||||
}
|
||||
}
|
||||
/* Free the old cache */
|
||||
gst_raw_free(oldCache);
|
||||
}
|
||||
|
||||
/****/
|
||||
/* Internal API */
|
||||
/****/
|
||||
|
||||
/* Initialize the string cache for a vm */
|
||||
void gst_stringcache_init(Gst *vm, uint32_t capacity) {
|
||||
vm->strings = gst_raw_calloc(1, capacity * sizeof(uint8_t *));
|
||||
if (vm->strings == NULL)
|
||||
GST_OUT_OF_MEMORY;
|
||||
vm->stringsCapacity = capacity;
|
||||
vm->stringsCount = 0;
|
||||
vm->stringsDeleted = 0;
|
||||
}
|
||||
|
||||
/* Deinitialize the stringcache for a vm */
|
||||
void gst_stringcache_deinit(Gst *vm) {
|
||||
gst_raw_free(vm->strings);
|
||||
vm->stringsCapacity = 0;
|
||||
vm->stringsCount = 0;
|
||||
vm->stringsDeleted = 0;
|
||||
}
|
||||
|
||||
/* Remove a string from the cache */
|
||||
void gst_stringcache_remove(Gst *vm, const uint8_t *str) {
|
||||
int status = 0;
|
||||
const uint8_t **bucket = gst_stringcache_find(vm, str,
|
||||
gst_string_length(str),
|
||||
gst_string_hash(str),
|
||||
&status);
|
||||
if (status) {
|
||||
vm->stringsCount--;
|
||||
vm->stringsDeleted++;
|
||||
*bucket = deleted;
|
||||
}
|
||||
}
|
||||
|
||||
/****/
|
||||
/* Public C API */
|
||||
/****/
|
||||
|
||||
/* Begin creation of a string */
|
||||
uint8_t *gst_string_begin(Gst *vm, uint32_t len) {
|
||||
uint8_t *data = gst_alloc(vm, len + 1 + 2 * sizeof(uint32_t));
|
||||
data += 2 * sizeof(uint32_t);
|
||||
gst_string_length(data) = len;
|
||||
data[len] = 0;
|
||||
return data;
|
||||
}
|
||||
|
||||
/* Finish building the string. Calculates the hash and deduplicates it */
|
||||
const uint8_t *gst_string_end(Gst *vm, uint8_t *str) {
|
||||
int status = 0;
|
||||
const uint8_t **bucket;
|
||||
uint32_t hash, len;
|
||||
len = gst_string_length(str);
|
||||
hash = gst_string_hash(str) = gst_string_calchash(str, len);
|
||||
bucket = gst_stringcache_find(vm, str, len, hash, &status);
|
||||
if (status) {
|
||||
return *bucket;
|
||||
} else {
|
||||
if ((vm->stringsCount + vm->stringsDeleted) * 2 > vm->stringsCapacity) {
|
||||
gst_stringcache_resize(vm, vm->stringsCount * 4);
|
||||
bucket = gst_stringcache_find(vm, str, len, hash, &status);
|
||||
}
|
||||
/* Mark the memory as string memory */
|
||||
gst_mem_tag(gst_string_raw(str), GST_MEMTAG_STRING);
|
||||
vm->stringsCount++;
|
||||
*bucket = str;
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
/* Loads a constant buffer as a string into a gst vm */
|
||||
const uint8_t *gst_string_loadbuffer(Gst *vm, const uint8_t *buf, uint32_t len) {
|
||||
int status = 0;
|
||||
const uint8_t **bucket;
|
||||
uint32_t hash;
|
||||
hash = gst_string_calchash(buf, len);
|
||||
bucket = gst_stringcache_find(vm, buf, len, hash, &status);
|
||||
if (status) {
|
||||
return *bucket;
|
||||
} else {
|
||||
uint8_t *str;
|
||||
if ((vm->stringsCount + vm->stringsDeleted) * 2 > vm->stringsCapacity) {
|
||||
gst_stringcache_resize(vm, vm->stringsCount * 4);
|
||||
bucket = gst_stringcache_find(vm, buf, len, hash, &status);
|
||||
}
|
||||
vm->stringsCount++;
|
||||
str = gst_string_begin(vm, len);
|
||||
gst_memcpy(str, buf, len);
|
||||
gst_string_hash(str) = hash;
|
||||
/* Mark the memory as string memory */
|
||||
gst_mem_tag(gst_string_raw(str), GST_MEMTAG_STRING);
|
||||
*bucket = str;
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
/* Converts a c style string to a gst string */
|
||||
const uint8_t *gst_cstring_to_string(Gst *vm, const char *cstring) {
|
||||
uint32_t len = 0;
|
||||
while (cstring[len]) ++len;
|
||||
return gst_string_loadbuffer(vm, (const uint8_t *)cstring, len);
|
||||
}
|
||||
|
||||
/* Load a c string into a GST string */
|
||||
GstValue gst_load_cstring(Gst *vm, const char *string) {
|
||||
GstValue ret;
|
||||
ret.type = GST_STRING;
|
||||
ret.data.string = gst_cstring_to_string(vm, string);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Compares two strings */
|
||||
int gst_string_compare(const uint8_t *lhs, const uint8_t *rhs) {
|
||||
@ -230,7 +10,7 @@ int gst_string_compare(const uint8_t *lhs, const uint8_t *rhs) {
|
||||
if (lhs[i] == rhs[i]) {
|
||||
continue;
|
||||
} else if (lhs[i] < rhs[i]) {
|
||||
return -1; /* x is less then y */
|
||||
return -1; /* x is less than y */
|
||||
} else {
|
||||
return 1; /* y is less than x */
|
||||
}
|
||||
|
@ -68,14 +68,14 @@ void gst_thread_tuplepack(Gst *vm, GstThread *thread, uint32_t n) {
|
||||
gst_thread_pushnil(vm, thread, n - size + 1);
|
||||
stack = thread->data + thread->count;
|
||||
stack[n].type = GST_TUPLE;
|
||||
stack[n].data.tuple = gst_tuple(vm, 0);
|
||||
stack[n].data.tuple = gst_tuple_end(vm, gst_tuple_begin(vm, 0));
|
||||
} else {
|
||||
uint32_t i;
|
||||
GstValue *tuple = gst_tuple(vm, size - n);
|
||||
GstValue *tuple = gst_tuple_begin(vm, size - n);
|
||||
for (i = n; i < size; ++i)
|
||||
tuple[i - n] = stack[i];
|
||||
stack[n].type = GST_TUPLE;
|
||||
stack[n].data.tuple = tuple;
|
||||
stack[n].data.tuple = gst_tuple_end(vm, tuple);
|
||||
gst_frame_size(stack) = n + 1;
|
||||
}
|
||||
}
|
||||
|
209
core/value.c
209
core/value.c
@ -13,7 +13,7 @@ static const uint8_t *number_to_string(Gst *vm, GstNumber x) {
|
||||
uint8_t buf[GST_BUFSIZE];
|
||||
/* TODO - not depend on stdio */
|
||||
int count = snprintf((char *) buf, GST_BUFSIZE, "%.21g", x);
|
||||
return gst_string_loadbuffer(vm, buf, (uint32_t) count);
|
||||
return gst_string_b(vm, buf, (uint32_t) count);
|
||||
}
|
||||
|
||||
static const char *HEX_CHARACTERS = "0123456789abcdef";
|
||||
@ -44,7 +44,7 @@ static const uint8_t *string_description(Gst *vm, const char *title, void *point
|
||||
*c++ = HEX(byte & 0xF);
|
||||
}
|
||||
*c++ = '>';
|
||||
return gst_string_loadbuffer(vm, buf, c - buf);
|
||||
return gst_string_b(vm, buf, c - buf);
|
||||
}
|
||||
|
||||
#undef GST_BUFSIZE
|
||||
@ -52,52 +52,44 @@ static const uint8_t *string_description(Gst *vm, const char *title, void *point
|
||||
/* Returns a string pointer or NULL if could not allocate memory. */
|
||||
const uint8_t *gst_to_string(Gst *vm, GstValue x) {
|
||||
switch (x.type) {
|
||||
case GST_NIL:
|
||||
return gst_cstring_to_string(vm, "nil");
|
||||
case GST_BOOLEAN:
|
||||
if (x.data.boolean) {
|
||||
return gst_cstring_to_string(vm, "true");
|
||||
} else {
|
||||
return gst_cstring_to_string(vm, "false");
|
||||
}
|
||||
case GST_NUMBER:
|
||||
return number_to_string(vm, x.data.number);
|
||||
case GST_ARRAY:
|
||||
return string_description(vm, "array", x.data.pointer);
|
||||
case GST_TUPLE:
|
||||
return string_description(vm, "tuple", x.data.pointer);
|
||||
case GST_OBJECT:
|
||||
return string_description(vm, "object", x.data.pointer);
|
||||
case GST_STRING:
|
||||
return x.data.string;
|
||||
case GST_BYTEBUFFER:
|
||||
return string_description(vm, "buffer", x.data.pointer);
|
||||
case GST_CFUNCTION:
|
||||
return string_description(vm, "cfunction", x.data.pointer);
|
||||
case GST_FUNCTION:
|
||||
return string_description(vm, "function", x.data.pointer);
|
||||
case GST_THREAD:
|
||||
return string_description(vm, "thread", x.data.pointer);
|
||||
case GST_USERDATA:
|
||||
return string_description(vm, "userdata", x.data.pointer);
|
||||
case GST_FUNCENV:
|
||||
return string_description(vm, "funcenv", x.data.pointer);
|
||||
case GST_FUNCDEF:
|
||||
return string_description(vm, "funcdef", x.data.pointer);
|
||||
case GST_NIL:
|
||||
return gst_string_c(vm, "nil");
|
||||
case GST_BOOLEAN:
|
||||
if (x.data.boolean) {
|
||||
return gst_string_c(vm, "true");
|
||||
} else {
|
||||
return gst_string_c(vm, "false");
|
||||
}
|
||||
case GST_NUMBER:
|
||||
return number_to_string(vm, x.data.number);
|
||||
case GST_ARRAY:
|
||||
return string_description(vm, "array", x.data.pointer);
|
||||
case GST_TUPLE:
|
||||
return string_description(vm, "tuple", x.data.pointer);
|
||||
case GST_STRUCT:
|
||||
return string_description(vm, "struct", x.data.pointer);
|
||||
case GST_OBJECT:
|
||||
return string_description(vm, "object", x.data.pointer);
|
||||
case GST_STRING:
|
||||
return x.data.string;
|
||||
case GST_BYTEBUFFER:
|
||||
return string_description(vm, "buffer", x.data.pointer);
|
||||
case GST_CFUNCTION:
|
||||
return string_description(vm, "cfunction", x.data.pointer);
|
||||
case GST_FUNCTION:
|
||||
return string_description(vm, "function", x.data.pointer);
|
||||
case GST_THREAD:
|
||||
return string_description(vm, "thread", x.data.pointer);
|
||||
case GST_USERDATA:
|
||||
return string_description(vm, "userdata", x.data.pointer);
|
||||
case GST_FUNCENV:
|
||||
return string_description(vm, "funcenv", x.data.pointer);
|
||||
case GST_FUNCDEF:
|
||||
return string_description(vm, "funcdef", x.data.pointer);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Simple hash function to get tuple hash */
|
||||
static uint32_t tuple_calchash(GstValue *tuple) {
|
||||
uint32_t i;
|
||||
uint32_t count = gst_tuple_length(tuple);
|
||||
uint32_t hash = 5387;
|
||||
for (i = 0; i < count; ++i)
|
||||
hash = (hash << 5) + hash + gst_hash(tuple[i]);
|
||||
return hash;
|
||||
}
|
||||
|
||||
/* Check if two values are equal. This is strict equality with no conversion. */
|
||||
int gst_equals(GstValue x, GstValue y) {
|
||||
int result = 0;
|
||||
@ -105,40 +97,19 @@ int gst_equals(GstValue x, GstValue y) {
|
||||
result = 0;
|
||||
} else {
|
||||
switch (x.type) {
|
||||
case GST_NIL:
|
||||
result = 1;
|
||||
break;
|
||||
case GST_BOOLEAN:
|
||||
result = (x.data.boolean == y.data.boolean);
|
||||
break;
|
||||
case GST_NUMBER:
|
||||
result = (x.data.number == y.data.number);
|
||||
break;
|
||||
case GST_TUPLE:
|
||||
if (x.data.tuple == y.data.tuple) {
|
||||
result = 1;
|
||||
break;
|
||||
}
|
||||
if (gst_hash(x) != gst_hash(y) ||
|
||||
gst_tuple_length(x.data.string) != gst_tuple_length(y.data.string)) {
|
||||
result = 0;
|
||||
break;
|
||||
}
|
||||
result = 1;
|
||||
{
|
||||
uint32_t i;
|
||||
for (i = 0; i < gst_tuple_length(x.data.tuple); ++i) {
|
||||
if (!gst_equals(x.data.tuple[i], y.data.tuple[i])) {
|
||||
result = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* compare pointers */
|
||||
result = (x.data.pointer == y.data.pointer);
|
||||
break;
|
||||
case GST_NIL:
|
||||
result = 1;
|
||||
break;
|
||||
case GST_BOOLEAN:
|
||||
result = (x.data.boolean == y.data.boolean);
|
||||
break;
|
||||
case GST_NUMBER:
|
||||
result = (x.data.number == y.data.number);
|
||||
break;
|
||||
default:
|
||||
/* compare pointers */
|
||||
result = (x.data.pointer == y.data.pointer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@ -148,43 +119,43 @@ int gst_equals(GstValue x, GstValue y) {
|
||||
uint32_t gst_hash(GstValue x) {
|
||||
uint32_t hash = 0;
|
||||
switch (x.type) {
|
||||
case GST_NIL:
|
||||
hash = 0;
|
||||
break;
|
||||
case GST_BOOLEAN:
|
||||
hash = x.data.boolean;
|
||||
break;
|
||||
case GST_NUMBER:
|
||||
{
|
||||
union {
|
||||
uint32_t hash;
|
||||
GstNumber number;
|
||||
} u;
|
||||
u.number = x.data.number;
|
||||
hash = u.hash;
|
||||
}
|
||||
break;
|
||||
/* String hashes */
|
||||
case GST_STRING:
|
||||
hash = gst_string_hash(x.data.string);
|
||||
break;
|
||||
case GST_TUPLE:
|
||||
if (gst_tuple_hash(x.data.tuple))
|
||||
hash = gst_tuple_hash(x.data.tuple);
|
||||
else
|
||||
hash = gst_tuple_hash(x.data.tuple) = tuple_calchash(x.data.tuple);
|
||||
break;
|
||||
default:
|
||||
/* Cast the pointer */
|
||||
{
|
||||
union {
|
||||
void * pointer;
|
||||
uint32_t hash;
|
||||
} u;
|
||||
u.pointer = x.data.pointer;
|
||||
hash = u.hash;
|
||||
}
|
||||
break;
|
||||
case GST_NIL:
|
||||
hash = 0;
|
||||
break;
|
||||
case GST_BOOLEAN:
|
||||
hash = x.data.boolean;
|
||||
break;
|
||||
case GST_NUMBER:
|
||||
{
|
||||
union {
|
||||
uint32_t hash;
|
||||
GstNumber number;
|
||||
} u;
|
||||
u.number = x.data.number;
|
||||
hash = u.hash;
|
||||
}
|
||||
break;
|
||||
/* String hashes */
|
||||
case GST_STRING:
|
||||
hash = gst_string_hash(x.data.string);
|
||||
break;
|
||||
case GST_TUPLE:
|
||||
hash = gst_tuple_hash(x.data.tuple);
|
||||
break;
|
||||
case GST_STRUCT:
|
||||
hash = gst_struct_hash(x.data.st);
|
||||
break;
|
||||
default:
|
||||
/* Cast the pointer */
|
||||
{
|
||||
union {
|
||||
void * pointer;
|
||||
uint32_t hash;
|
||||
} u;
|
||||
u.pointer = x.data.pointer;
|
||||
hash = u.hash;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
@ -304,6 +275,9 @@ const char *gst_get(GstValue ds, GstValue key, GstValue *out) {
|
||||
ret.type = GST_NUMBER;
|
||||
ret.data.number = ds.data.string[index];
|
||||
break;
|
||||
case GST_STRUCT:
|
||||
ret = gst_struct_get(ds.data.st, key);
|
||||
break;
|
||||
case GST_OBJECT:
|
||||
ret = gst_object_get(ds.data.object, key);
|
||||
break;
|
||||
@ -346,7 +320,7 @@ int gst_length(Gst *vm, GstValue x, GstValue *len) {
|
||||
uint32_t length;
|
||||
switch (x.type) {
|
||||
default:
|
||||
vm->ret = gst_load_cstring(vm, "cannot get length");
|
||||
vm->ret = gst_string_cv(vm, "cannot get length");
|
||||
return GST_RETURN_ERROR;
|
||||
case GST_STRING:
|
||||
length = gst_string_length(x.data.string);
|
||||
@ -360,6 +334,9 @@ int gst_length(Gst *vm, GstValue x, GstValue *len) {
|
||||
case GST_TUPLE:
|
||||
length = gst_tuple_length(x.data.tuple);
|
||||
break;
|
||||
case GST_STRUCT:
|
||||
length = gst_struct_length(x.data.st);
|
||||
break;
|
||||
case GST_OBJECT:
|
||||
length = x.data.object->count;
|
||||
break;
|
||||
|
29
core/vm.c
29
core/vm.c
@ -1,5 +1,4 @@
|
||||
#include <gst/gst.h>
|
||||
#include "stringcache.h"
|
||||
|
||||
/* Macros for errors in the vm */
|
||||
|
||||
@ -7,7 +6,7 @@
|
||||
#define gst_exit(vm, r) return ((vm)->ret = (r), GST_RETURN_OK)
|
||||
|
||||
/* Bail from the VM with an error string. */
|
||||
#define gst_error(vm, e) do { (vm)->ret = gst_load_cstring((vm), (e)); goto vm_error; } while (0)
|
||||
#define gst_error(vm, e) do { (vm)->ret = gst_string_cv((vm), (e)); goto vm_error; } while (0)
|
||||
|
||||
/* Crash. Not catchable, unlike error. */
|
||||
#define gst_crash(vm, e) return ((vm)->crash = (e), GST_RETURN_CRASH)
|
||||
@ -229,8 +228,9 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) {
|
||||
offset = isTCall ? 3 : 4;
|
||||
arity = pc[offset - 1];
|
||||
/* Push new frame */
|
||||
if (temp.type != GST_FUNCTION && temp.type != GST_CFUNCTION)
|
||||
gst_error(vm, GST_EXPECTED_FUNCTION);
|
||||
stack = gst_thread_beginframe(vm, &thread, temp, arity);
|
||||
if (stack == NULL) gst_error(vm, "expected function");
|
||||
oldStack = stack - GST_FRAME_SIZE - gst_frame_prevsize(stack);
|
||||
/* Write arguments */
|
||||
size = gst_frame_size(stack);
|
||||
@ -389,11 +389,11 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) {
|
||||
{
|
||||
uint32_t i;
|
||||
uint32_t len = pc[2];
|
||||
GstValue *tuple = gst_tuple(vm, len);
|
||||
GstValue *tuple = gst_tuple_begin(vm, len);
|
||||
for (i = 0; i < len; ++i)
|
||||
tuple[i] = stack[pc[3 + i]];
|
||||
temp.type = GST_TUPLE;
|
||||
temp.data.tuple = tuple;
|
||||
temp.data.tuple = gst_tuple_end(vm, tuple);
|
||||
stack[pc[1]] = temp;
|
||||
pc += 3 + len;
|
||||
}
|
||||
@ -504,10 +504,16 @@ void gst_init(Gst *vm) {
|
||||
vm->black = 0;
|
||||
/* Add thread */
|
||||
vm->thread = NULL;
|
||||
/* Set up string cache */
|
||||
gst_stringcache_init(vm, 128);
|
||||
/* Set up global env */
|
||||
vm->rootenv = NULL;
|
||||
/* Set up scratch memory */
|
||||
vm->scratch = NULL;
|
||||
vm->scratch_len = 0;
|
||||
/* Set up the cache */
|
||||
vm->cache = gst_raw_calloc(1, 128 * sizeof(GstValue));
|
||||
vm->cache_capacity = vm->cache == NULL ? 0 : 128;
|
||||
vm->cache_count = 0;
|
||||
vm->cache_deleted = 0;
|
||||
}
|
||||
|
||||
/* Clear all memory associated with the VM */
|
||||
@ -516,5 +522,12 @@ void gst_deinit(Gst *vm) {
|
||||
vm->thread = NULL;
|
||||
vm->rootenv = NULL;
|
||||
vm->ret.type = GST_NIL;
|
||||
gst_stringcache_deinit(vm);
|
||||
vm->scratch = NULL;
|
||||
vm->scratch_len = 0;
|
||||
/* Deinit the cache */
|
||||
gst_raw_free(vm->cache);
|
||||
vm->cache = NULL;
|
||||
vm->cache_count = 0;
|
||||
vm->cache_capacity = 0;
|
||||
vm->cache_deleted = 0;
|
||||
}
|
||||
|
@ -16,7 +16,7 @@
|
||||
/* Struct utils */
|
||||
#define gst_struct_raw(t) ((uint32_t *)(t) - 2)
|
||||
#define gst_struct_length(t) (gst_struct_raw(t)[0])
|
||||
#define gst_struct_capacity(t) (gst_struct_length(t) * 3)
|
||||
#define gst_struct_capacity(t) (gst_struct_length(t) * 4)
|
||||
#define gst_struct_hash(t) (gst_struct_raw(t)[1])
|
||||
|
||||
/* Memcpy for moving memory */
|
||||
@ -78,7 +78,7 @@
|
||||
#define gst_c_throw(vm, e) do { (vm)->ret = (e); return GST_RETURN_ERROR; } while (0)
|
||||
|
||||
/* Throw c string error from a c function */
|
||||
#define gst_c_throwc(vm, e) gst_c_throw((vm), gst_load_cstring((vm), (e)))
|
||||
#define gst_c_throwc(vm, e) gst_c_throw((vm), gst_string_cv((vm), (e)))
|
||||
|
||||
/* Assert from a c function */
|
||||
#define gst_c_assert(vm, cond, e) do {if (cond) gst_c_throw((vm), (e)); } while (0)
|
||||
@ -101,6 +101,7 @@ typedef enum GstType {
|
||||
GST_STRING,
|
||||
GST_ARRAY,
|
||||
GST_TUPLE,
|
||||
GST_STRUCT,
|
||||
GST_THREAD,
|
||||
GST_BYTEBUFFER,
|
||||
GST_FUNCTION,
|
||||
@ -153,12 +154,12 @@ union GstValueUnion {
|
||||
GstBuffer *buffer;
|
||||
GstObject *object;
|
||||
GstThread *thread;
|
||||
GstValue *tuple;
|
||||
const GstValue *tuple;
|
||||
GstCFunction cfunction;
|
||||
GstFunction *function;
|
||||
GstFuncEnv *env;
|
||||
GstFuncDef *def;
|
||||
GstValue *st;
|
||||
const GstValue *st;
|
||||
const uint8_t *string;
|
||||
const char *cstring; /* Alias for ease of use from c */
|
||||
/* Indirectly used union members */
|
||||
@ -264,11 +265,14 @@ struct Gst {
|
||||
uint32_t memoryInterval;
|
||||
uint32_t nextCollection;
|
||||
uint32_t black : 1;
|
||||
/* String cache */
|
||||
const uint8_t **strings;
|
||||
uint32_t stringsCapacity;
|
||||
uint32_t stringsCount;
|
||||
uint32_t stringsDeleted;
|
||||
/* Immutable value cache */
|
||||
GstValue *cache;
|
||||
uint32_t cache_capacity;
|
||||
uint32_t cache_count;
|
||||
uint32_t cache_deleted;
|
||||
/* Scratch memory */
|
||||
char *scratch;
|
||||
uint32_t scratch_len;
|
||||
/* Thread */
|
||||
GstThread *thread;
|
||||
/* A GC root */
|
||||
@ -345,11 +349,18 @@ void gst_array_ensure(Gst *vm, GstArray *array, uint32_t capacity);
|
||||
void gst_array_push(Gst *vm, GstArray *array, GstValue x);
|
||||
GstValue gst_array_pop(GstArray *array);
|
||||
|
||||
/****/
|
||||
/* Userdata functions */
|
||||
/****/
|
||||
|
||||
void *gst_userdata(Gst *vm, uint32_t size, GstObject *meta);
|
||||
|
||||
/****/
|
||||
/* Tuple functions */
|
||||
/****/
|
||||
|
||||
GstValue *gst_tuple(Gst *vm, uint32_t length);
|
||||
GstValue *gst_tuple_begin(Gst *vm, uint32_t length);
|
||||
const GstValue *gst_tuple_end(Gst *vm, GstValue *tuple);
|
||||
|
||||
/****/
|
||||
/* String functions */
|
||||
@ -357,16 +368,19 @@ GstValue *gst_tuple(Gst *vm, uint32_t length);
|
||||
|
||||
uint8_t *gst_string_begin(Gst *vm, uint32_t len);
|
||||
const uint8_t *gst_string_end(Gst *vm, uint8_t *str);
|
||||
const uint8_t *gst_string_loadbuffer(Gst *vm, const uint8_t *buf, uint32_t len);
|
||||
const uint8_t *gst_cstring_to_string(Gst *vm, const char *cstring);
|
||||
GstValue gst_load_cstring(Gst *vm, const char *string);
|
||||
const uint8_t *gst_string_b(Gst *vm, const uint8_t *buf, uint32_t len);
|
||||
const uint8_t *gst_string_c(Gst *vm, const char *cstring);
|
||||
GstValue gst_string_cv(Gst *vm, const char *string);
|
||||
int gst_string_compare(const uint8_t *lhs, const uint8_t *rhs);
|
||||
|
||||
/****/
|
||||
/* Userdata functions */
|
||||
/* Struct functions */
|
||||
/****/
|
||||
|
||||
void *gst_userdata(Gst *vm, uint32_t size, GstObject *meta);
|
||||
GstValue *gst_struct_begin(Gst *vm, uint32_t count);
|
||||
void gst_struct_put(GstValue *st, GstValue key, GstValue value);
|
||||
const GstValue *gst_struct_end(Gst *vm, GstValue *st);
|
||||
GstValue gst_struct_get(const GstValue *st, GstValue key);
|
||||
|
||||
/****/
|
||||
/* Object functions */
|
||||
@ -453,6 +467,8 @@ const char *gst_serialize(Gst *vm, GstBuffer *buffer, GstValue x);
|
||||
/****/
|
||||
|
||||
#define GST_MEMTAG_STRING 4
|
||||
#define GST_MEMTAG_TUPLE 8
|
||||
#define GST_MEMTAG_STRUCT 16
|
||||
|
||||
void gst_mark_value(Gst *vm, GstValue x);
|
||||
void gst_mark(Gst *vm, GstValueUnion x, GstType type);
|
||||
|
Loading…
Reference in New Issue
Block a user