mirror of
				https://github.com/janet-lang/janet
				synced 2025-10-30 23:23:07 +00:00 
			
		
		
		
	Add struct type.
This commit is contained in:
		
							
								
								
									
										7
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										7
									
								
								Makefile
									
									
									
									
									
								
							| @@ -7,6 +7,7 @@ CFLAGS=-std=c99 -Wall -Wextra -Wpedantic -g -I./include | |||||||
| PREFIX=/usr/local | PREFIX=/usr/local | ||||||
| GST_TARGET=client/gst | GST_TARGET=client/gst | ||||||
| GST_CORELIB=core/libgst.a | 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) | GST_HEADERS=$(addprefix include/gst/, gst.h stl.h compile.h disasm.h parse.h) | ||||||
|  |  | ||||||
| all: $(GST_TARGET) | all: $(GST_TARGET) | ||||||
| @@ -15,7 +16,7 @@ all: $(GST_TARGET) | |||||||
| ##### The core vm and runtime ##### | ##### The core vm and runtime ##### | ||||||
| ################################### | ################################### | ||||||
| GST_CORE_SOURCES=$(addprefix core/,\ | 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) | 				 value.c vm.c ds.c gc.c thread.c serialize.c capi.c) | ||||||
| GST_CORE_OBJECTS=$(patsubst %.c,%.o,$(GST_CORE_SOURCES)) | GST_CORE_OBJECTS=$(patsubst %.c,%.o,$(GST_CORE_SOURCES)) | ||||||
| $(GST_CORELIB): $(GST_CORE_OBJECTS) $(GST_HEADERS) | $(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_SOURCES=client/main.c | ||||||
| GST_CLIENT_OBJECTS=$(patsubst %.c,%.o,$(GST_CLIENT_SOURCES)) | 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) | 	$(CC) $(CFLAGS) -o $(GST_TARGET) $(GST_CLIENT_OBJECTS) $(GST_CORELIB) | ||||||
|  |  | ||||||
| # Compile all .c to .o | # Compile all .c to .o | ||||||
| %.o : %.c $(GST_HEADERS) | %.o : %.c $(GST_HEADERS) $(GST_INTERNAL_HEADERS) | ||||||
| 	$(CC) $(CFLAGS) -o $@ -c $< | 	$(CC) $(CFLAGS) -o $@ -c $< | ||||||
|  |  | ||||||
| run: $(GST_TARGET) | run: $(GST_TARGET) | ||||||
|   | |||||||
| @@ -59,7 +59,7 @@ void debug_repl(FILE *in, FILE *out) { | |||||||
|         gst_compiler(&c, &vm); |         gst_compiler(&c, &vm); | ||||||
|         func.type = GST_NIL; |         func.type = GST_NIL; | ||||||
|         gst_compiler_usemodule(&c, "std"); |         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.type = GST_FUNCTION; | ||||||
|         func.data.function = gst_compiler_compile(&c, p.value); |         func.data.function = gst_compiler_compile(&c, p.value); | ||||||
|  |  | ||||||
| @@ -88,7 +88,7 @@ void debug_repl(FILE *in, FILE *out) { | |||||||
|             continue; |             continue; | ||||||
|         } else if (out) { |         } else if (out) { | ||||||
|             fprintf(out, "%s\n", (char *)gst_to_string(&vm, vm.ret)); |             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 *gst_c_module(Gst *vm, const GstModuleItem *mod) { | ||||||
|     GstObject *module = gst_object(vm, 10); |     GstObject *module = gst_object(vm, 10); | ||||||
|     while (mod->name != NULL) { |     while (mod->name != NULL) { | ||||||
|         GstValue key = gst_load_cstring(vm, mod->name); |         GstValue key = gst_string_cv(vm, mod->name); | ||||||
|         GstValue val; |         GstValue val; | ||||||
|         val.type = GST_CFUNCTION; |         val.type = GST_CFUNCTION; | ||||||
|         val.data.cfunction = mod->data; |         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); |         vm->rootenv = gst_object(vm, 10); | ||||||
|     modv.type = GST_OBJECT; |     modv.type = GST_OBJECT; | ||||||
|     modv.data.object = mod; |     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 | /* Compile series of expressions. This compiles the meat of | ||||||
|  * function definitions and the inside of do forms. */ |  * 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; |     GstScope *scope = c->tail; | ||||||
|     FormOptions subOpts = form_options_default(); |     FormOptions subOpts = form_options_default(); | ||||||
|     uint32_t current = startIndex; |     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 */ | /* 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; |     GstScope *scope = c->tail; | ||||||
|     GstBuffer *buffer = c->buffer; |     GstBuffer *buffer = c->buffer; | ||||||
|     uint32_t current = 1; |     uint32_t current = 1; | ||||||
| @@ -595,7 +595,7 @@ static Slot compile_function(GstCompiler *c, FormOptions opts, GstValue *form) { | |||||||
| } | } | ||||||
|  |  | ||||||
| /* Branching special */ | /* 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; |     GstScope *scope = c->tail; | ||||||
|     GstBuffer *buffer = c->buffer; |     GstBuffer *buffer = c->buffer; | ||||||
|     FormOptions condOpts = opts; |     FormOptions condOpts = opts; | ||||||
| @@ -666,7 +666,7 @@ static Slot compile_if(GstCompiler *c, FormOptions opts, GstValue *form) { | |||||||
| } | } | ||||||
|  |  | ||||||
| /* Try catch special */ | /* 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; |     GstScope *scope = c->tail; | ||||||
|     GstBuffer *buffer = c->buffer; |     GstBuffer *buffer = c->buffer; | ||||||
|     Slot body; |     Slot body; | ||||||
| @@ -730,7 +730,7 @@ static Slot compile_try(GstCompiler *c, FormOptions opts, GstValue *form) { | |||||||
| } | } | ||||||
|  |  | ||||||
| /* While special */ | /* 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; |     Slot cond; | ||||||
|     uint32_t countAtStart = c->buffer->count; |     uint32_t countAtStart = c->buffer->count; | ||||||
|     uint32_t countAtJumpDelta; |     uint32_t countAtJumpDelta; | ||||||
| @@ -769,7 +769,7 @@ static Slot compile_while(GstCompiler *c, FormOptions opts, GstValue *form) { | |||||||
| } | } | ||||||
|  |  | ||||||
| /* Do special */ | /* 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; |     Slot ret; | ||||||
|     compiler_push_scope(c, 1); |     compiler_push_scope(c, 1); | ||||||
|     ret = compile_block(c, opts, form, 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. */ | /* 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; |     GstScope *scope = c->tail; | ||||||
|     GstBuffer *buffer = c->buffer; |     GstBuffer *buffer = c->buffer; | ||||||
|     Slot ret; |     Slot ret; | ||||||
| @@ -801,17 +801,17 @@ static Slot compile_quote(GstCompiler *c, FormOptions opts, GstValue *form) { | |||||||
| } | } | ||||||
|  |  | ||||||
| /* Assignment special */ | /* 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) |     if (gst_tuple_length(form) != 3) | ||||||
|         c_error(c, "assignment expects 2 arguments"); |         c_error(c, "assignment expects 2 arguments"); | ||||||
|     return compile_assign(c, opts, form[1], form[2]); |     return compile_assign(c, opts, form[1], form[2]); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Define a function type for Special Form helpers */ | /* 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 */ | /* Dispatch to a special form */ | ||||||
| static SpecialFormHelper get_special(GstValue *form) { | static SpecialFormHelper get_special(const GstValue *form) { | ||||||
|     const uint8_t *name; |     const uint8_t *name; | ||||||
|     if (gst_tuple_length(form) < 1 || form[0].type != GST_STRING) |     if (gst_tuple_length(form) < 1 || form[0].type != GST_STRING) | ||||||
|         return NULL; |         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. */ | /* 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; |     GstScope *scope = c->tail; | ||||||
|     GstBuffer *buffer = c->buffer; |     GstBuffer *buffer = c->buffer; | ||||||
|     SpecialFormHelper helper; |     SpecialFormHelper helper; | ||||||
| @@ -1026,7 +1026,7 @@ void gst_compiler(GstCompiler *c, Gst *vm) { | |||||||
|  |  | ||||||
| /* Add a global variable */ | /* Add a global variable */ | ||||||
| void gst_compiler_global(GstCompiler *c, const char *name, GstValue x) { | 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); |     compiler_declare_symbol(c, c->tail, sym); | ||||||
|     gst_array_push(c->vm, c->env, x);                 |     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 */ | /* Use a module that was loaded into the vm */ | ||||||
| void gst_compiler_usemodule(GstCompiler *c, const char *modulename) { | 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) { |     if (mod.type == GST_OBJECT) { | ||||||
|         gst_compiler_globals(c, mod.data.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 */ | /* Convert the buffer to a string */ | ||||||
| const uint8_t *gst_buffer_to_string(Gst *vm, GstBuffer *buffer) { | 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 */ | /* 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 <gst/gst.h> | ||||||
| #include "stringcache.h" | #include "cache.h" | ||||||
|  |  | ||||||
| /* The metadata header associated with an allocated block of memory */ | /* The metadata header associated with an allocated block of memory */ | ||||||
| #define gc_header(mem) ((GCMemoryHeader *)(mem) - 1) | #define gc_header(mem) ((GCMemoryHeader *)(mem) - 1) | ||||||
| @@ -102,6 +102,16 @@ void gst_mark(Gst *vm, GstValueUnion x, GstType type) { | |||||||
|             } |             } | ||||||
|             break; |             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: |         case GST_THREAD: | ||||||
|             if (gc_header(x.thread)->color != vm->black) { |             if (gc_header(x.thread)->color != vm->black) { | ||||||
|                 GstThread *thread = x.thread; |                 GstThread *thread = x.thread; | ||||||
| @@ -185,9 +195,13 @@ void gst_sweep(Gst *vm) { | |||||||
|             } else { |             } else { | ||||||
|                 vm->blocks = next; |                 vm->blocks = next; | ||||||
|             } |             } | ||||||
|             /* Remove from string cache */ |             if (current->tags) { | ||||||
|             if (current->tags & GST_MEMTAG_STRING) { |                 if (current->tags & GST_MEMTAG_STRING) | ||||||
|                 gst_stringcache_remove(vm, (uint8_t *)(current + 1) + 2 * sizeof(uint32_t)); |                     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); |             gst_raw_free(current); | ||||||
|         } else { |         } 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 */ |     /* Load a quote form to get the string literal */ | ||||||
|     GstValue tuplev; |     GstValue tuplev; | ||||||
|     GstValue *tuple; |     GstValue *tuple; | ||||||
|     tuple = gst_tuple(p->vm, 2); |     tuple = gst_tuple_begin(p->vm, 2); | ||||||
|     tuplev.type = GST_TUPLE; |     tuple[0] = gst_string_cv(p->vm, "quote"); | ||||||
|     tuplev.data.tuple = tuple; |  | ||||||
|     tuple[0] = gst_load_cstring(p->vm, "quote"); |  | ||||||
|     tuple[1] = x; |     tuple[1] = x; | ||||||
|  |     tuplev.type = GST_TUPLE; | ||||||
|  |     tuplev.data.tuple = gst_tuple_end(p->vm, tuple); | ||||||
|     return tuplev; |     return tuplev; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -376,9 +376,11 @@ static int form_state(GstParser *p, uint8_t c) { | |||||||
|             x.type = GST_ARRAY; |             x.type = GST_ARRAY; | ||||||
|             x.data.array = array; |             x.data.array = array; | ||||||
|         } else if (c == ')') { |         } 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.type = GST_TUPLE; | ||||||
|             x.data.tuple = gst_tuple(p->vm, array->count); |             x.data.tuple = gst_tuple_end(p->vm, tup); | ||||||
|             gst_memcpy(x.data.tuple, array->data, array->count * sizeof(GstValue)); |  | ||||||
|         } else { /* c == '{' */ |         } else { /* c == '{' */ | ||||||
|             uint32_t i; |             uint32_t i; | ||||||
|             if (array->count % 2 != 0) { |             if (array->count % 2 != 0) { | ||||||
|   | |||||||
| @@ -78,7 +78,6 @@ static const char *gst_deserialize_impl( | |||||||
|     GstValue ret; |     GstValue ret; | ||||||
|     ret.type = GST_NIL; |     ret.type = GST_NIL; | ||||||
|     GstValue *buffer; |     GstValue *buffer; | ||||||
|     const uint8_t *bytebuf; |  | ||||||
|     uint32_t length, i; |     uint32_t length, i; | ||||||
|     const char *err; |     const char *err; | ||||||
|  |  | ||||||
| @@ -137,11 +136,27 @@ static const char *gst_deserialize_impl( | |||||||
|             ret.type = GST_STRING; |             ret.type = GST_STRING; | ||||||
|             read_u32(length); |             read_u32(length); | ||||||
|             deser_datacheck(length); |             deser_datacheck(length); | ||||||
|             ret.data.string = gst_string_loadbuffer(vm, data, length); |             ret.data.string = gst_string_b(vm, data, length); | ||||||
|             data += length; |             data += length; | ||||||
|             gst_array_push(vm, visited, ret); |             gst_array_push(vm, visited, ret); | ||||||
|             break; |             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 */ |         case 207: /* Buffer */ | ||||||
|             ret.type = GST_BYTEBUFFER; |             ret.type = GST_BYTEBUFFER; | ||||||
|             read_u32(length); |             read_u32(length); | ||||||
| @@ -172,14 +187,12 @@ static const char *gst_deserialize_impl( | |||||||
|         case 209: /* Tuple */ |         case 209: /* Tuple */ | ||||||
|             ret.type = GST_TUPLE; |             ret.type = GST_TUPLE; | ||||||
|             read_u32(length); |             read_u32(length); | ||||||
|             bytebuf = gst_alloc(vm, length * sizeof(GstValue) + 2 * sizeof(uint32_t)); |             buffer = gst_tuple_begin(vm, length); | ||||||
|             buffer = (GstValue *)(bytebuf + 2 * sizeof(uint32_t)); |  | ||||||
|             for (i = 0; i < length; ++i) |             for (i = 0; i < length; ++i) | ||||||
|                 if ((err = gst_deserialize_impl(vm, data, end, &data, visited, buffer + i))) |                 if ((err = gst_deserialize_impl(vm, data, end, &data, visited, buffer + i))) | ||||||
|                     return err; |                     return err; | ||||||
|             gst_tuple_hash(buffer) = 0; |             ret.type = GST_TUPLE; | ||||||
|             gst_tuple_length(buffer) = length; |             ret.data.tuple = gst_tuple_end(vm, buffer); | ||||||
|             ret.data.tuple = buffer; |  | ||||||
|             gst_array_push(vm, visited, ret); |             gst_array_push(vm, visited, ret); | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										31
									
								
								core/stl.c
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								core/stl.c
									
									
									
									
									
								
							| @@ -197,7 +197,7 @@ int gst_stl_type(Gst *vm) { | |||||||
|         typestr = "funcdef"; |         typestr = "funcdef"; | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
|     gst_c_return(vm, gst_load_cstring(vm, typestr)); |     gst_c_return(vm, gst_string_cv(vm, typestr)); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Create array */ | /* Create array */ | ||||||
| @@ -219,12 +219,12 @@ int gst_stl_tuple(Gst *vm) { | |||||||
|     uint32_t i; |     uint32_t i; | ||||||
|     uint32_t count = gst_count_args(vm); |     uint32_t count = gst_count_args(vm); | ||||||
|     GstValue ret; |     GstValue ret; | ||||||
|     GstValue *tuple= gst_tuple(vm, count); |     GstValue *tuple= gst_tuple_begin(vm, count); | ||||||
|     for (i = 0; i < count; ++i) { |     for (i = 0; i < count; ++i) { | ||||||
|         tuple[i] = gst_arg(vm, i); |         tuple[i] = gst_arg(vm, i); | ||||||
|     } |     } | ||||||
|     ret.type = GST_TUPLE; |     ret.type = GST_TUPLE; | ||||||
|     ret.data.tuple = tuple; |     ret.data.tuple = gst_tuple_end(vm, tuple); | ||||||
|     gst_c_return(vm, ret); |     gst_c_return(vm, ret); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -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); |     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)); | ||||||
|     } |     } | ||||||
| @@ -246,6 +246,24 @@ int gst_stl_object(Gst *vm) { | |||||||
|     gst_c_return(vm, ret); |     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 */ | /* Create a buffer */ | ||||||
| int gst_stl_buffer(Gst *vm) { | int gst_stl_buffer(Gst *vm) { | ||||||
|     uint32_t i, count; |     uint32_t i, count; | ||||||
| @@ -308,7 +326,6 @@ int gst_stl_rawget(Gst *vm) { | |||||||
|  |  | ||||||
| /* Associative rawset */ | /* Associative rawset */ | ||||||
| int gst_stl_rawset(Gst *vm) { | int gst_stl_rawset(Gst *vm) { | ||||||
|     GstValue ret; |  | ||||||
|     uint32_t count; |     uint32_t count; | ||||||
|     const char *err; |     const char *err; | ||||||
|     count = gst_count_args(vm); |     count = gst_count_args(vm); | ||||||
| @@ -319,8 +336,7 @@ int gst_stl_rawset(Gst *vm) { | |||||||
|     if (err != NULL) { |     if (err != NULL) { | ||||||
|         gst_c_throwc(vm, err); |         gst_c_throwc(vm, err); | ||||||
|     } else { |     } else { | ||||||
|         ret.type = GST_NIL; |         gst_c_return(vm, gst_arg(vm, 0)); | ||||||
|         gst_c_return(vm, ret); |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -408,6 +424,7 @@ static const GstModuleItem const std_module[] = { | |||||||
|     {"array", gst_stl_array}, |     {"array", gst_stl_array}, | ||||||
|     {"tuple", gst_stl_tuple}, |     {"tuple", gst_stl_tuple}, | ||||||
|     {"object", gst_stl_object}, |     {"object", gst_stl_object}, | ||||||
|  |     {"struct", gst_stl_struct}, | ||||||
|     {"buffer", gst_stl_buffer}, |     {"buffer", gst_stl_buffer}, | ||||||
|     {"strcat", gst_stl_strcat}, |     {"strcat", gst_stl_strcat}, | ||||||
|     {"print", gst_stl_print}, |     {"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 <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 */ | /* Compares two strings */ | ||||||
| int gst_string_compare(const uint8_t *lhs, const uint8_t *rhs) { | 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]) { |         if (lhs[i] == rhs[i]) { | ||||||
|             continue; |             continue; | ||||||
|         } else if (lhs[i] < rhs[i]) { |         } else if (lhs[i] < rhs[i]) { | ||||||
|             return -1; /* x is less then y */ |             return -1; /* x is less than y */ | ||||||
|         } else { |         } else { | ||||||
|             return 1; /* y is less than x */ |             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); |         gst_thread_pushnil(vm, thread, n - size + 1); | ||||||
|         stack = thread->data + thread->count; |         stack = thread->data + thread->count; | ||||||
|         stack[n].type = GST_TUPLE; |         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 { |     } else { | ||||||
|         uint32_t i; |         uint32_t i; | ||||||
|         GstValue *tuple = gst_tuple(vm, size - n); |         GstValue *tuple = gst_tuple_begin(vm, size - n); | ||||||
|         for (i = n; i < size; ++i) |         for (i = n; i < size; ++i) | ||||||
|             tuple[i - n] = stack[i]; |             tuple[i - n] = stack[i]; | ||||||
|         stack[n].type = GST_TUPLE; |         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; |         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]; |     uint8_t buf[GST_BUFSIZE]; | ||||||
|     /* TODO - not depend on stdio */ |     /* TODO - not depend on stdio */ | ||||||
|     int count = snprintf((char *) buf, GST_BUFSIZE, "%.21g", x); |     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"; | 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++ = HEX(byte & 0xF); | ||||||
|     } |     } | ||||||
|     *c++ = '>'; |     *c++ = '>'; | ||||||
|     return gst_string_loadbuffer(vm, buf, c - buf); |     return gst_string_b(vm, buf, c - buf); | ||||||
| } | } | ||||||
|  |  | ||||||
| #undef GST_BUFSIZE | #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. */ | /* Returns a string pointer or NULL if could not allocate memory. */ | ||||||
| const uint8_t *gst_to_string(Gst *vm, GstValue x) { | const uint8_t *gst_to_string(Gst *vm, GstValue x) { | ||||||
|     switch (x.type) { |     switch (x.type) { | ||||||
|         case GST_NIL: |     case GST_NIL: | ||||||
|             return gst_cstring_to_string(vm, "nil"); |         return gst_string_c(vm, "nil"); | ||||||
|         case GST_BOOLEAN: |     case GST_BOOLEAN: | ||||||
|             if (x.data.boolean) { |         if (x.data.boolean) { | ||||||
|                 return gst_cstring_to_string(vm, "true"); |             return gst_string_c(vm, "true"); | ||||||
|             } else { |         } else { | ||||||
|                 return gst_cstring_to_string(vm, "false"); |             return gst_string_c(vm, "false"); | ||||||
|             } |         } | ||||||
|         case GST_NUMBER: |     case GST_NUMBER: | ||||||
|             return number_to_string(vm, x.data.number); |         return number_to_string(vm, x.data.number); | ||||||
|         case GST_ARRAY: |     case GST_ARRAY: | ||||||
|             return string_description(vm, "array", x.data.pointer); |         return string_description(vm, "array", x.data.pointer); | ||||||
|         case GST_TUPLE: |     case GST_TUPLE: | ||||||
|             return string_description(vm, "tuple", x.data.pointer); |         return string_description(vm, "tuple", x.data.pointer); | ||||||
|         case GST_OBJECT: |     case GST_STRUCT: | ||||||
|             return string_description(vm, "object", x.data.pointer); |         return string_description(vm, "struct", x.data.pointer); | ||||||
|         case GST_STRING: |     case GST_OBJECT: | ||||||
|             return x.data.string; |         return string_description(vm, "object", x.data.pointer); | ||||||
|         case GST_BYTEBUFFER: |     case GST_STRING: | ||||||
|             return string_description(vm, "buffer", x.data.pointer); |         return x.data.string; | ||||||
|         case GST_CFUNCTION: |     case GST_BYTEBUFFER: | ||||||
|             return string_description(vm, "cfunction", x.data.pointer); |         return string_description(vm, "buffer", x.data.pointer); | ||||||
|         case GST_FUNCTION: |     case GST_CFUNCTION: | ||||||
|             return string_description(vm, "function", x.data.pointer); |         return string_description(vm, "cfunction", x.data.pointer); | ||||||
|         case GST_THREAD: |     case GST_FUNCTION: | ||||||
|             return string_description(vm, "thread", x.data.pointer); |         return string_description(vm, "function", x.data.pointer); | ||||||
|         case GST_USERDATA: |     case GST_THREAD: | ||||||
|             return string_description(vm, "userdata", x.data.pointer); |         return string_description(vm, "thread", x.data.pointer); | ||||||
|         case GST_FUNCENV: |     case GST_USERDATA: | ||||||
|             return string_description(vm, "funcenv", x.data.pointer); |         return string_description(vm, "userdata", x.data.pointer); | ||||||
|         case GST_FUNCDEF: |     case GST_FUNCENV: | ||||||
|             return string_description(vm, "funcdef", x.data.pointer); |         return string_description(vm, "funcenv", x.data.pointer); | ||||||
|  |     case GST_FUNCDEF: | ||||||
|  |         return string_description(vm, "funcdef", x.data.pointer); | ||||||
|     } |     } | ||||||
|     return NULL; |     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. */ | /* Check if two values are equal. This is strict equality with no conversion. */ | ||||||
| int gst_equals(GstValue x, GstValue y) { | int gst_equals(GstValue x, GstValue y) { | ||||||
|     int result = 0; |     int result = 0; | ||||||
| @@ -105,40 +97,19 @@ int gst_equals(GstValue x, GstValue y) { | |||||||
|         result = 0; |         result = 0; | ||||||
|     } else { |     } else { | ||||||
|         switch (x.type) { |         switch (x.type) { | ||||||
|             case GST_NIL: |         case GST_NIL: | ||||||
|                 result = 1; |             result = 1; | ||||||
|                 break; |             break; | ||||||
|             case GST_BOOLEAN: |         case GST_BOOLEAN: | ||||||
|                 result = (x.data.boolean == y.data.boolean); |             result = (x.data.boolean == y.data.boolean); | ||||||
|                 break; |             break; | ||||||
|             case GST_NUMBER: |         case GST_NUMBER: | ||||||
|                 result = (x.data.number == y.data.number); |             result = (x.data.number == y.data.number); | ||||||
|                 break; |             break; | ||||||
|             case GST_TUPLE: |         default: | ||||||
|                 if (x.data.tuple == y.data.tuple) { |             /* compare pointers */ | ||||||
|                     result = 1; |             result = (x.data.pointer == y.data.pointer); | ||||||
|                     break; |             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; |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     return result; |     return result; | ||||||
| @@ -148,43 +119,43 @@ int gst_equals(GstValue x, GstValue y) { | |||||||
| uint32_t gst_hash(GstValue x) { | uint32_t gst_hash(GstValue x) { | ||||||
|     uint32_t hash = 0; |     uint32_t hash = 0; | ||||||
|     switch (x.type) { |     switch (x.type) { | ||||||
|         case GST_NIL: |     case GST_NIL: | ||||||
|             hash = 0; |         hash = 0; | ||||||
|             break; |         break; | ||||||
|         case GST_BOOLEAN: |     case GST_BOOLEAN: | ||||||
|             hash = x.data.boolean; |         hash = x.data.boolean; | ||||||
|             break; |         break; | ||||||
|         case GST_NUMBER: |     case GST_NUMBER: | ||||||
|             { |         { | ||||||
|                 union { |             union { | ||||||
|                     uint32_t hash; |                 uint32_t hash; | ||||||
|                     GstNumber number; |                 GstNumber number; | ||||||
|                 } u; |             } u; | ||||||
|                 u.number = x.data.number; |             u.number = x.data.number; | ||||||
|                 hash = u.hash; |             hash = u.hash; | ||||||
|             } |         } | ||||||
|             break; |         break; | ||||||
|             /* String hashes */ |         /* String hashes */ | ||||||
|         case GST_STRING: |     case GST_STRING: | ||||||
|             hash = gst_string_hash(x.data.string); |         hash = gst_string_hash(x.data.string); | ||||||
|             break; |         break; | ||||||
|         case GST_TUPLE: |     case GST_TUPLE: | ||||||
|             if (gst_tuple_hash(x.data.tuple)) |         hash = gst_tuple_hash(x.data.tuple); | ||||||
|                 hash = gst_tuple_hash(x.data.tuple); |         break; | ||||||
|             else |     case GST_STRUCT: | ||||||
|                 hash = gst_tuple_hash(x.data.tuple) = tuple_calchash(x.data.tuple); |         hash = gst_struct_hash(x.data.st); | ||||||
|             break; |         break; | ||||||
|         default: |     default: | ||||||
|             /* Cast the pointer */ |         /* Cast the pointer */ | ||||||
|             { |         { | ||||||
|                 union { |             union { | ||||||
|                     void * pointer; |                 void * pointer; | ||||||
|                     uint32_t hash; |                 uint32_t hash; | ||||||
|                 } u; |             } u; | ||||||
|                 u.pointer = x.data.pointer; |             u.pointer = x.data.pointer; | ||||||
|                 hash = u.hash; |             hash = u.hash; | ||||||
|             } |         } | ||||||
|             break; |         break; | ||||||
|     } |     } | ||||||
|     return hash; |     return hash; | ||||||
| } | } | ||||||
| @@ -304,6 +275,9 @@ const char *gst_get(GstValue ds, GstValue key, GstValue *out) { | |||||||
|         ret.type = GST_NUMBER; |         ret.type = GST_NUMBER; | ||||||
|         ret.data.number = ds.data.string[index]; |         ret.data.number = ds.data.string[index]; | ||||||
|         break; |         break; | ||||||
|  |     case GST_STRUCT: | ||||||
|  |         ret = gst_struct_get(ds.data.st, key); | ||||||
|  |         break; | ||||||
|     case GST_OBJECT: |     case GST_OBJECT: | ||||||
|         ret = gst_object_get(ds.data.object, key); |         ret = gst_object_get(ds.data.object, key); | ||||||
|         break; |         break; | ||||||
| @@ -346,7 +320,7 @@ int gst_length(Gst *vm, GstValue x, GstValue *len) { | |||||||
|     uint32_t length; |     uint32_t length; | ||||||
|     switch (x.type) { |     switch (x.type) { | ||||||
|         default: |         default: | ||||||
|             vm->ret = gst_load_cstring(vm, "cannot get length"); |             vm->ret = gst_string_cv(vm, "cannot get length"); | ||||||
|             return GST_RETURN_ERROR; |             return GST_RETURN_ERROR; | ||||||
|         case GST_STRING: |         case GST_STRING: | ||||||
|             length = gst_string_length(x.data.string); |             length = gst_string_length(x.data.string); | ||||||
| @@ -360,6 +334,9 @@ int gst_length(Gst *vm, GstValue x, GstValue *len) { | |||||||
|         case GST_TUPLE: |         case GST_TUPLE: | ||||||
|             length = gst_tuple_length(x.data.tuple); |             length = gst_tuple_length(x.data.tuple); | ||||||
|             break; |             break; | ||||||
|  |         case GST_STRUCT: | ||||||
|  |             length = gst_struct_length(x.data.st); | ||||||
|  |             break; | ||||||
|         case GST_OBJECT: |         case GST_OBJECT: | ||||||
|             length = x.data.object->count; |             length = x.data.object->count; | ||||||
|             break; |             break; | ||||||
|   | |||||||
							
								
								
									
										29
									
								
								core/vm.c
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								core/vm.c
									
									
									
									
									
								
							| @@ -1,5 +1,4 @@ | |||||||
| #include <gst/gst.h> | #include <gst/gst.h> | ||||||
| #include "stringcache.h" |  | ||||||
|  |  | ||||||
| /* Macros for errors in the vm */ | /* Macros for errors in the vm */ | ||||||
|  |  | ||||||
| @@ -7,7 +6,7 @@ | |||||||
| #define gst_exit(vm, r) return ((vm)->ret = (r), GST_RETURN_OK) | #define gst_exit(vm, r) return ((vm)->ret = (r), GST_RETURN_OK) | ||||||
|  |  | ||||||
| /* Bail from the VM with an error string. */ | /* 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. */ | /* Crash. Not catchable, unlike error. */ | ||||||
| #define gst_crash(vm, e) return ((vm)->crash = (e), GST_RETURN_CRASH) | #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; |                 offset = isTCall ? 3 : 4; | ||||||
|                 arity = pc[offset - 1]; |                 arity = pc[offset - 1]; | ||||||
|                 /* Push new frame */ |                 /* 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); |                 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); |                 oldStack = stack - GST_FRAME_SIZE - gst_frame_prevsize(stack); | ||||||
|                 /* Write arguments */ |                 /* Write arguments */ | ||||||
|                 size = gst_frame_size(stack); |                 size = gst_frame_size(stack); | ||||||
| @@ -389,11 +389,11 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) { | |||||||
|             { |             { | ||||||
|                 uint32_t i; |                 uint32_t i; | ||||||
|                 uint32_t len = pc[2]; |                 uint32_t len = pc[2]; | ||||||
|                 GstValue *tuple = gst_tuple(vm, len); |                 GstValue *tuple = gst_tuple_begin(vm, len); | ||||||
|                 for (i = 0; i < len; ++i) |                 for (i = 0; i < len; ++i) | ||||||
|                     tuple[i] = stack[pc[3 + i]]; |                     tuple[i] = stack[pc[3 + i]]; | ||||||
|                 temp.type = GST_TUPLE; |                 temp.type = GST_TUPLE; | ||||||
|                 temp.data.tuple = tuple; |                 temp.data.tuple = gst_tuple_end(vm, tuple); | ||||||
|                 stack[pc[1]] = temp; |                 stack[pc[1]] = temp; | ||||||
|                 pc += 3 + len; |                 pc += 3 + len; | ||||||
|             } |             } | ||||||
| @@ -504,10 +504,16 @@ void gst_init(Gst *vm) { | |||||||
|     vm->black = 0; |     vm->black = 0; | ||||||
|     /* Add thread */ |     /* Add thread */ | ||||||
|     vm->thread = NULL; |     vm->thread = NULL; | ||||||
|     /* Set up string cache */ |  | ||||||
|     gst_stringcache_init(vm, 128); |  | ||||||
|     /* Set up global env */ |     /* Set up global env */ | ||||||
|     vm->rootenv = NULL; |     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 */ | /* Clear all memory associated with the VM */ | ||||||
| @@ -516,5 +522,12 @@ void gst_deinit(Gst *vm) { | |||||||
|     vm->thread = NULL; |     vm->thread = NULL; | ||||||
|     vm->rootenv = NULL; |     vm->rootenv = NULL; | ||||||
|     vm->ret.type = GST_NIL; |     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 */ | /* Struct utils */ | ||||||
| #define gst_struct_raw(t) ((uint32_t *)(t) - 2) | #define gst_struct_raw(t) ((uint32_t *)(t) - 2) | ||||||
| #define gst_struct_length(t) (gst_struct_raw(t)[0]) | #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]) | #define gst_struct_hash(t) (gst_struct_raw(t)[1]) | ||||||
|  |  | ||||||
| /* Memcpy for moving memory */ | /* Memcpy for moving memory */ | ||||||
| @@ -78,7 +78,7 @@ | |||||||
| #define gst_c_throw(vm, e) do { (vm)->ret = (e); return GST_RETURN_ERROR; } while (0) | #define gst_c_throw(vm, e) do { (vm)->ret = (e); return GST_RETURN_ERROR; } while (0) | ||||||
|  |  | ||||||
| /* Throw c string error from a c function */ | /* 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 */ | /* Assert from a c function */ | ||||||
| #define gst_c_assert(vm, cond, e) do {if (cond) gst_c_throw((vm), (e)); } while (0) | #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_STRING, | ||||||
|     GST_ARRAY, |     GST_ARRAY, | ||||||
|     GST_TUPLE, |     GST_TUPLE, | ||||||
|  |     GST_STRUCT, | ||||||
|     GST_THREAD, |     GST_THREAD, | ||||||
|     GST_BYTEBUFFER, |     GST_BYTEBUFFER, | ||||||
|     GST_FUNCTION, |     GST_FUNCTION, | ||||||
| @@ -153,12 +154,12 @@ union GstValueUnion { | |||||||
|     GstBuffer *buffer; |     GstBuffer *buffer; | ||||||
|     GstObject *object; |     GstObject *object; | ||||||
|     GstThread *thread; |     GstThread *thread; | ||||||
|     GstValue *tuple; |     const GstValue *tuple; | ||||||
|     GstCFunction cfunction; |     GstCFunction cfunction; | ||||||
|     GstFunction *function; |     GstFunction *function; | ||||||
|     GstFuncEnv *env; |     GstFuncEnv *env; | ||||||
|     GstFuncDef *def; |     GstFuncDef *def; | ||||||
|     GstValue *st; |     const GstValue *st; | ||||||
|     const uint8_t *string; |     const uint8_t *string; | ||||||
|     const char *cstring; /* Alias for ease of use from c */ |     const char *cstring; /* Alias for ease of use from c */ | ||||||
|     /* Indirectly used union members */ |     /* Indirectly used union members */ | ||||||
| @@ -264,11 +265,14 @@ struct Gst { | |||||||
|     uint32_t memoryInterval; |     uint32_t memoryInterval; | ||||||
|     uint32_t nextCollection; |     uint32_t nextCollection; | ||||||
|     uint32_t black : 1; |     uint32_t black : 1; | ||||||
|     /* String cache */ |     /* Immutable value cache */ | ||||||
|     const uint8_t **strings; |     GstValue *cache; | ||||||
|     uint32_t stringsCapacity; |     uint32_t cache_capacity; | ||||||
|     uint32_t stringsCount; |     uint32_t cache_count; | ||||||
|     uint32_t stringsDeleted; |     uint32_t cache_deleted; | ||||||
|  |     /* Scratch memory */ | ||||||
|  |     char *scratch; | ||||||
|  |     uint32_t scratch_len; | ||||||
|     /* Thread */ |     /* Thread */ | ||||||
|     GstThread *thread; |     GstThread *thread; | ||||||
|     /* A GC root */ |     /* 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); | void gst_array_push(Gst *vm, GstArray *array, GstValue x); | ||||||
| GstValue gst_array_pop(GstArray *array); | GstValue gst_array_pop(GstArray *array); | ||||||
|  |  | ||||||
|  | /****/ | ||||||
|  | /* Userdata functions */ | ||||||
|  | /****/ | ||||||
|  |  | ||||||
|  | void *gst_userdata(Gst *vm, uint32_t size, GstObject *meta); | ||||||
|  |  | ||||||
| /****/ | /****/ | ||||||
| /* Tuple functions */ | /* 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 */ | /* String functions */ | ||||||
| @@ -357,16 +368,19 @@ GstValue *gst_tuple(Gst *vm, uint32_t length); | |||||||
|  |  | ||||||
| uint8_t *gst_string_begin(Gst *vm, uint32_t len); | 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_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_string_b(Gst *vm, const uint8_t *buf, uint32_t len); | ||||||
| const uint8_t *gst_cstring_to_string(Gst *vm, const char *cstring); | const uint8_t *gst_string_c(Gst *vm, const char *cstring); | ||||||
| GstValue gst_load_cstring(Gst *vm, const char *string); | GstValue gst_string_cv(Gst *vm, const char *string); | ||||||
| int gst_string_compare(const uint8_t *lhs, const uint8_t *rhs); | 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 */ | /* Object functions */ | ||||||
| @@ -453,6 +467,8 @@ const char *gst_serialize(Gst *vm, GstBuffer *buffer, GstValue x); | |||||||
| /****/ | /****/ | ||||||
|  |  | ||||||
| #define GST_MEMTAG_STRING 4 | #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_value(Gst *vm, GstValue x); | ||||||
| void gst_mark(Gst *vm, GstValueUnion x, GstType type); | void gst_mark(Gst *vm, GstValueUnion x, GstType type); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Calvin Rose
					Calvin Rose