mirror of
https://github.com/janet-lang/janet
synced 2025-01-25 14:46:52 +00:00
More work on cleaning up string implementation.
This commit is contained in:
parent
841ee3696d
commit
6365a007b6
2
Makefile
2
Makefile
@ -15,7 +15,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 stringcache.c\
|
compile.c disasm.c parse.c stl.c strings.c \
|
||||||
value.c vm.c ds.c gc.c thread.c serialize.c)
|
value.c vm.c ds.c gc.c thread.c serialize.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)
|
||||||
|
@ -1010,7 +1010,7 @@ typedef Slot (*SpecialFormHelper) (GstCompiler *c, FormOptions opts, GstValue *f
|
|||||||
|
|
||||||
/* Dispatch to a special form */
|
/* Dispatch to a special form */
|
||||||
static SpecialFormHelper get_special(GstValue *form) {
|
static SpecialFormHelper get_special(GstValue *form) {
|
||||||
uint8_t *name;
|
const uint8_t *name;
|
||||||
if (gst_tuple_length(form) < 1 || form[0].type != GST_SYMBOL)
|
if (gst_tuple_length(form) < 1 || form[0].type != GST_SYMBOL)
|
||||||
return NULL;
|
return NULL;
|
||||||
name = form[0].data.string;
|
name = form[0].data.string;
|
||||||
|
39
core/ds.c
39
core/ds.c
@ -42,7 +42,7 @@ void gst_buffer_push(Gst *vm, GstBuffer * buffer, uint8_t c) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Push multiple bytes into the buffer */
|
/* Push multiple bytes into the buffer */
|
||||||
void gst_buffer_append(Gst *vm, GstBuffer *buffer, uint8_t *string, uint32_t length) {
|
void gst_buffer_append(Gst *vm, GstBuffer *buffer, const uint8_t *string, uint32_t length) {
|
||||||
uint32_t newSize = buffer->count + length;
|
uint32_t newSize = buffer->count + length;
|
||||||
if (newSize > buffer->capacity) {
|
if (newSize > buffer->capacity) {
|
||||||
gst_buffer_ensure(vm, buffer, 2 * newSize);
|
gst_buffer_ensure(vm, buffer, 2 * newSize);
|
||||||
@ -52,8 +52,8 @@ void gst_buffer_append(Gst *vm, GstBuffer *buffer, uint8_t *string, uint32_t len
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Convert the buffer to a string */
|
/* Convert the buffer to a string */
|
||||||
uint8_t *gst_buffer_to_string(Gst *vm, GstBuffer *buffer) {
|
const uint8_t *gst_buffer_to_string(Gst *vm, GstBuffer *buffer) {
|
||||||
return gst_load_cstring_rawlen(vm, (char *) buffer->data, buffer->count);
|
return gst_string_loadbuffer(vm, buffer->data, buffer->count);
|
||||||
}
|
}
|
||||||
|
|
||||||
/****/
|
/****/
|
||||||
@ -218,39 +218,6 @@ GstValue gst_object_get(GstObject *o, GstValue key) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get a value of the object with a cstring key */
|
|
||||||
GstValue gst_object_get_cstring(GstObject *obj, const char *key) {
|
|
||||||
uint32_t len;
|
|
||||||
for (len = 0; key[len]; ++len);
|
|
||||||
uint32_t hash = gst_cstring_calchash((uint8_t *)key, len);
|
|
||||||
uint32_t index = hash % obj->capacity;
|
|
||||||
GstBucket *bucket = obj->buckets[index];
|
|
||||||
while (bucket) {
|
|
||||||
if (bucket->key.type == GST_STRING) {
|
|
||||||
uint8_t *s = bucket->key.data.string;
|
|
||||||
if (gst_string_length(s) == len) {
|
|
||||||
if (!gst_string_hash(s))
|
|
||||||
gst_string_hash(s) = gst_string_calchash(s);
|
|
||||||
if (gst_string_hash(s) == hash) {
|
|
||||||
uint32_t i;
|
|
||||||
for (i = 0; i < len; ++i)
|
|
||||||
if (s[i] != key[i])
|
|
||||||
goto notequal;
|
|
||||||
return bucket->value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
notequal:
|
|
||||||
bucket = bucket->next;
|
|
||||||
}
|
|
||||||
/* Return nil */
|
|
||||||
{
|
|
||||||
GstValue ret;
|
|
||||||
ret.type = GST_NIL;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Remove an entry from the dictionary */
|
/* Remove an entry from the dictionary */
|
||||||
GstValue gst_object_remove(Gst * vm, GstObject *o, GstValue key) {
|
GstValue gst_object_remove(Gst * vm, GstObject *o, GstValue key) {
|
||||||
GstBucket *bucket, *previous;
|
GstBucket *bucket, *previous;
|
||||||
|
@ -219,8 +219,8 @@ static int check_str_const(const char *ref, const uint8_t *start, const uint8_t
|
|||||||
static GstValue build_token(GstParser *p, GstBuffer *buf) {
|
static GstValue build_token(GstParser *p, GstBuffer *buf) {
|
||||||
GstValue x;
|
GstValue x;
|
||||||
GstNumber number;
|
GstNumber number;
|
||||||
uint8_t * data = buf->data;
|
uint8_t *data = buf->data;
|
||||||
uint8_t * back = data + buf->count;
|
uint8_t *back = data + buf->count;
|
||||||
if (read_number(data, back, &number, 0)) {
|
if (read_number(data, back, &number, 0)) {
|
||||||
x.type = GST_NUMBER;
|
x.type = GST_NUMBER;
|
||||||
x.data.number = number;
|
x.data.number = number;
|
||||||
@ -453,7 +453,7 @@ int gst_parse_cstring(GstParser *p, const char *string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Parse a gst string */
|
/* Parse a gst string */
|
||||||
int gst_parse_string(GstParser *p, uint8_t *string) {
|
int gst_parse_string(GstParser *p, const uint8_t *string) {
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
p->status = GST_PARSER_PENDING;
|
p->status = GST_PARSER_PENDING;
|
||||||
for (i = 0; i < gst_string_length(string); ++i) {
|
for (i = 0; i < gst_string_length(string); ++i) {
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
static const char UEB[] = "unexpected end of buffer";
|
static const char UEB[] = "unexpected end of buffer";
|
||||||
|
|
||||||
/* Read 4 bytes as an unsigned integer */
|
/* Read 4 bytes as an unsigned integer */
|
||||||
static uint32_t bytes2u32(uint8_t *bytes) {
|
static uint32_t bytes2u32(const uint8_t *bytes) {
|
||||||
union {
|
union {
|
||||||
uint8_t bytes[4];
|
uint8_t bytes[4];
|
||||||
uint32_t u32;
|
uint32_t u32;
|
||||||
@ -44,7 +44,7 @@ static uint32_t bytes2u32(uint8_t *bytes) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Read 2 bytes as unsigned short */
|
/* Read 2 bytes as unsigned short */
|
||||||
static uint16_t bytes2u16(uint8_t *bytes) {
|
static uint16_t bytes2u16(const uint8_t *bytes) {
|
||||||
union {
|
union {
|
||||||
uint8_t bytes[2];
|
uint8_t bytes[2];
|
||||||
uint16_t u16;
|
uint16_t u16;
|
||||||
@ -54,7 +54,7 @@ static uint16_t bytes2u16(uint8_t *bytes) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Read 8 bytes as a double */
|
/* Read 8 bytes as a double */
|
||||||
static uint32_t bytes2dbl(uint8_t *bytes) {
|
static uint32_t bytes2dbl(const uint8_t *bytes) {
|
||||||
union {
|
union {
|
||||||
uint8_t bytes[8];
|
uint8_t bytes[8];
|
||||||
double dbl;
|
double dbl;
|
||||||
@ -69,16 +69,16 @@ static uint32_t bytes2dbl(uint8_t *bytes) {
|
|||||||
* passed by reference. */
|
* passed by reference. */
|
||||||
static const char *gst_deserialize_impl(
|
static const char *gst_deserialize_impl(
|
||||||
Gst *vm,
|
Gst *vm,
|
||||||
uint8_t *data,
|
const uint8_t *data,
|
||||||
uint8_t *end,
|
const uint8_t *end,
|
||||||
uint8_t **newData,
|
const uint8_t **newData,
|
||||||
GstArray *visited,
|
GstArray *visited,
|
||||||
GstValue *out) {
|
GstValue *out) {
|
||||||
|
|
||||||
GstValue ret;
|
GstValue ret;
|
||||||
ret.type = GST_NIL;
|
ret.type = GST_NIL;
|
||||||
GstValue *buffer;
|
GstValue *buffer;
|
||||||
uint8_t *bytebuf;
|
const uint8_t *bytebuf;
|
||||||
uint32_t length, i;
|
uint32_t length, i;
|
||||||
const char *err;
|
const char *err;
|
||||||
|
|
||||||
@ -135,12 +135,7 @@ static const char *gst_deserialize_impl(
|
|||||||
ret.type = data[-1] == 205 ? GST_STRING : GST_SYMBOL;
|
ret.type = data[-1] == 205 ? GST_STRING : GST_SYMBOL;
|
||||||
read_u32(length);
|
read_u32(length);
|
||||||
deser_datacheck(length);
|
deser_datacheck(length);
|
||||||
ret.data.string = gst_alloc(vm, 2 * sizeof(uint32_t) + length + 1);
|
ret.data.string = gst_string_loadbuffer(vm, data, length);
|
||||||
ret.data.string += 2 * sizeof(uint32_t);
|
|
||||||
gst_string_length(ret.data.string) = length;
|
|
||||||
gst_string_hash(ret.data.string) = 0;
|
|
||||||
gst_memcpy(ret.data.string, data, length);
|
|
||||||
ret.data.string[length] = 0;
|
|
||||||
data += length;
|
data += length;
|
||||||
gst_array_push(vm, visited, ret);
|
gst_array_push(vm, visited, ret);
|
||||||
break;
|
break;
|
||||||
@ -151,7 +146,7 @@ static const char *gst_deserialize_impl(
|
|||||||
deser_datacheck(length);
|
deser_datacheck(length);
|
||||||
ret.data.buffer = gst_alloc(vm, sizeof(GstBuffer));
|
ret.data.buffer = gst_alloc(vm, sizeof(GstBuffer));
|
||||||
ret.data.buffer->data = gst_alloc(vm, length);
|
ret.data.buffer->data = gst_alloc(vm, length);
|
||||||
gst_memcpy(ret.data.string, data, length);
|
gst_memcpy(ret.data.buffer->data, data, length);
|
||||||
ret.data.buffer->count = length;
|
ret.data.buffer->count = length;
|
||||||
ret.data.buffer->capacity = length;
|
ret.data.buffer->capacity = length;
|
||||||
data += length;
|
data += length;
|
||||||
@ -391,10 +386,10 @@ static const char *gst_deserialize_impl(
|
|||||||
/* Load a value from data */
|
/* Load a value from data */
|
||||||
const char *gst_deserialize(
|
const char *gst_deserialize(
|
||||||
Gst *vm,
|
Gst *vm,
|
||||||
uint8_t *data,
|
const uint8_t *data,
|
||||||
uint32_t len,
|
uint32_t len,
|
||||||
GstValue *out,
|
GstValue *out,
|
||||||
uint8_t *nextData) {
|
const uint8_t *nextData) {
|
||||||
GstValue ret;
|
GstValue ret;
|
||||||
const char *err;
|
const char *err;
|
||||||
GstArray *visited = gst_array(vm, 10);
|
GstArray *visited = gst_array(vm, 10);
|
||||||
|
12
core/stl.c
12
core/stl.c
@ -13,7 +13,7 @@ int gst_stl_print(Gst *vm) {
|
|||||||
count = gst_count_args(vm);
|
count = gst_count_args(vm);
|
||||||
for (j = 0; j < count; ++j) {
|
for (j = 0; j < count; ++j) {
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
uint8_t *string = gst_to_string(vm, gst_arg(vm, j));
|
const uint8_t *string = gst_to_string(vm, gst_arg(vm, j));
|
||||||
uint32_t len = gst_string_length(string);
|
uint32_t len = gst_string_length(string);
|
||||||
for (i = 0; i < len; ++i)
|
for (i = 0; i < len; ++i)
|
||||||
fputc(string[i], stdout);
|
fputc(string[i], stdout);
|
||||||
@ -46,7 +46,7 @@ int gst_stl_make_buffer(Gst *vm) {
|
|||||||
buf.data.buffer = gst_buffer(vm, 10);
|
buf.data.buffer = gst_buffer(vm, 10);
|
||||||
count = gst_count_args(vm);
|
count = gst_count_args(vm);
|
||||||
for (i = 0; i < count; ++i) {
|
for (i = 0; i < count; ++i) {
|
||||||
uint8_t *string = gst_to_string(vm, gst_arg(vm, i));
|
const uint8_t *string = gst_to_string(vm, gst_arg(vm, i));
|
||||||
gst_buffer_append(vm, buf.data.buffer, string, gst_string_length(string));
|
gst_buffer_append(vm, buf.data.buffer, string, gst_string_length(string));
|
||||||
}
|
}
|
||||||
gst_c_return(vm, buf);
|
gst_c_return(vm, buf);
|
||||||
@ -55,7 +55,7 @@ int gst_stl_make_buffer(Gst *vm) {
|
|||||||
/* To string */
|
/* To string */
|
||||||
int gst_stl_tostring(Gst *vm) {
|
int gst_stl_tostring(Gst *vm) {
|
||||||
GstValue ret;
|
GstValue ret;
|
||||||
uint8_t *string = gst_to_string(vm, gst_arg(vm, 0));
|
const uint8_t *string = gst_to_string(vm, gst_arg(vm, 0));
|
||||||
ret.type = GST_STRING;
|
ret.type = GST_STRING;
|
||||||
ret.data.string = string;
|
ret.data.string = string;
|
||||||
gst_c_return(vm, ret);
|
gst_c_return(vm, ret);
|
||||||
@ -147,8 +147,8 @@ static int read_number(const uint8_t *string, const uint8_t *end, double *ret, i
|
|||||||
int gst_stl_parse_number(Gst *vm) {
|
int gst_stl_parse_number(Gst *vm) {
|
||||||
GstValue ret;
|
GstValue ret;
|
||||||
double number;
|
double number;
|
||||||
uint8_t *str = gst_to_string(vm, gst_arg(vm, 0));
|
const uint8_t *str = gst_to_string(vm, gst_arg(vm, 0));
|
||||||
uint8_t *end = str + gst_string_length(str);
|
const uint8_t *end = str + gst_string_length(str);
|
||||||
if (read_number(str, end, &number, 0)) {
|
if (read_number(str, end, &number, 0)) {
|
||||||
ret.type = GST_NUMBER;
|
ret.type = GST_NUMBER;
|
||||||
ret.data.number = number;
|
ret.data.number = number;
|
||||||
@ -161,7 +161,7 @@ int gst_stl_parse_number(Gst *vm) {
|
|||||||
|
|
||||||
/* Parse a source string into an AST */
|
/* Parse a source string into an AST */
|
||||||
int gst_stl_parse(Gst *vm) {
|
int gst_stl_parse(Gst *vm) {
|
||||||
uint8_t *source = gst_to_string(vm, gst_arg(vm, 0));
|
const uint8_t *source = gst_to_string(vm, gst_arg(vm, 0));
|
||||||
GstParser p;
|
GstParser p;
|
||||||
/* init state */
|
/* init state */
|
||||||
gst_parser(&p, vm);
|
gst_parser(&p, vm);
|
||||||
|
@ -1,120 +0,0 @@
|
|||||||
#include <gst/gst.h>
|
|
||||||
#include "stringcache.h"
|
|
||||||
|
|
||||||
/* Dud pointer to serve as deletion marker */
|
|
||||||
static uint8_t *deleted = (uint8_t *) "DELETED";
|
|
||||||
|
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Find a string in the hashtable. Returns null if
|
|
||||||
* not found. */
|
|
||||||
static uint8_t **gst_stringcache_find(Gst *vm, uint8_t *str, int *success) {
|
|
||||||
uint32_t bounds[4];
|
|
||||||
uint32_t i, j, index, hash;
|
|
||||||
uint8_t **firstEmpty = NULL;
|
|
||||||
hash = gst_string_hash(str);
|
|
||||||
if (!hash) {
|
|
||||||
hash = gst_string_hash(str) = gst_string_calchash(str);
|
|
||||||
}
|
|
||||||
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) {
|
|
||||||
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_string_equal(testStr, str)) {
|
|
||||||
/* 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;
|
|
||||||
uint8_t **oldCache = vm->strings;
|
|
||||||
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->stringsCount = 0;
|
|
||||||
vm->stringsDeleted = 0;
|
|
||||||
/* Add all of the old strings back */
|
|
||||||
for (i = 0; i < oldCapacity; ++i) {
|
|
||||||
uint8_t *str = oldCache[i];
|
|
||||||
if (str != NULL && str != deleted)
|
|
||||||
gst_stringcache_get(vm, str);
|
|
||||||
}
|
|
||||||
/* Free the old cache */
|
|
||||||
gst_raw_free(oldCache);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get a string from the string cache */
|
|
||||||
uint8_t *gst_stringcache_get(Gst *vm, uint8_t *str) {
|
|
||||||
int status = 0;
|
|
||||||
uint8_t **bucket = gst_stringcache_find(vm, str, &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, &status);
|
|
||||||
}
|
|
||||||
vm->stringsCount++;
|
|
||||||
*bucket = str;
|
|
||||||
/* Mark the memory as string memory */
|
|
||||||
gst_mem_tag(gst_string_raw(str), GST_MEMTAG_STRING);
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Remove a string from the cache */
|
|
||||||
void gst_stringcache_remove(Gst *vm, uint8_t *str) {
|
|
||||||
int status = 0;
|
|
||||||
uint8_t **bucket = gst_stringcache_find(vm, str, &status);
|
|
||||||
if (status) {
|
|
||||||
vm->stringsCount--;
|
|
||||||
vm->stringsDeleted++;
|
|
||||||
*bucket = deleted;
|
|
||||||
}
|
|
||||||
}
|
|
@ -9,7 +9,6 @@
|
|||||||
|
|
||||||
void gst_stringcache_init(Gst *vm, uint32_t capacity);
|
void gst_stringcache_init(Gst *vm, uint32_t capacity);
|
||||||
void gst_stringcache_deinit(Gst *vm);
|
void gst_stringcache_deinit(Gst *vm);
|
||||||
uint8_t *gst_stringcache_get(Gst *vm, uint8_t *str);
|
void gst_stringcache_remove(Gst *vm, const uint8_t *str);
|
||||||
void gst_stringcache_remove(Gst *vm, uint8_t *str);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
260
core/strings.c
260
core/strings.c
@ -1,35 +1,28 @@
|
|||||||
#include <gst/gst.h>
|
#include <gst/gst.h>
|
||||||
#include "stringcache.h"
|
#include "stringcache.h"
|
||||||
|
|
||||||
uint8_t *gst_load_cstring_rawlen(Gst *vm, const char *string, uint32_t len) {
|
/* Dud pointer to serve as deletion marker */
|
||||||
uint8_t *data = gst_alloc(vm, len + 1 + 2 * sizeof(uint32_t));
|
static uint8_t *deleted = (uint8_t *) "";
|
||||||
data += 2 * sizeof(uint32_t);
|
|
||||||
gst_string_hash(data) = 0;
|
|
||||||
gst_string_length(data) = len;
|
|
||||||
gst_memcpy(data, string, len);
|
|
||||||
data[len] = 0;
|
|
||||||
/* Check string cache */
|
|
||||||
return gst_stringcache_get(vm, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Load a c string into a GST string */
|
/* Check if string and cstring are equal */
|
||||||
GstValue gst_load_cstring(Gst *vm, const char *string) {
|
/* To check if strings are equal externally, one can
|
||||||
GstValue ret;
|
* just use == */
|
||||||
ret.type = GST_STRING;
|
static int gst_cstring_equal(const uint8_t *lhs, const uint8_t *rhs, uint32_t rlen, uint32_t rhash) {
|
||||||
ret.data.string = gst_load_cstring_rawlen(vm, string, strlen(string));
|
uint32_t lhash, len, i;
|
||||||
return ret;
|
/* Check lengths */
|
||||||
}
|
len = gst_string_length(lhs);
|
||||||
|
if (len != rlen) return 0;
|
||||||
/* Load a c string into a GST symbol */
|
/* Check hashes */
|
||||||
GstValue gst_load_csymbol(Gst *vm, const char *string) {
|
lhash = gst_string_hash(lhs);
|
||||||
GstValue ret;
|
if (lhash != rhash) return 0;
|
||||||
ret.type = GST_SYMBOL;
|
for (i = 0; i < len; ++i)
|
||||||
ret.data.string = gst_load_cstring_rawlen(vm, string, strlen(string));
|
if (lhs[i] != rhs[i])
|
||||||
return ret;
|
return 0;
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Simple hash function (djb2) */
|
/* Simple hash function (djb2) */
|
||||||
uint32_t gst_cstring_calchash(const uint8_t *str, uint32_t len) {
|
static uint32_t gst_string_calchash(const uint8_t *str, uint32_t len) {
|
||||||
const uint8_t *end = str + len;
|
const uint8_t *end = str + len;
|
||||||
uint32_t hash = 5381;
|
uint32_t hash = 5381;
|
||||||
while (str < end)
|
while (str < end)
|
||||||
@ -37,31 +30,202 @@ uint32_t gst_cstring_calchash(const uint8_t *str, uint32_t len) {
|
|||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* GST string version */
|
/* Find a string in the hashtable. Returns null if
|
||||||
uint32_t gst_string_calchash(const uint8_t *str) {
|
* not found. */
|
||||||
return gst_cstring_calchash(str, gst_string_length(str));
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check if two strings are equal. Does not check the string cache. */
|
/* Resize the hashtable. */
|
||||||
int gst_string_equal(const uint8_t *lhs, const uint8_t *rhs) {
|
static void gst_stringcache_resize(Gst *vm, uint32_t newCapacity) {
|
||||||
uint32_t hash_l, hash_r, len, i;
|
uint32_t i, oldCapacity;
|
||||||
if (lhs == rhs)
|
const uint8_t **oldCache = vm->strings;
|
||||||
return 1;
|
const uint8_t **newCache = gst_raw_calloc(1, newCapacity * sizeof(uint8_t *));
|
||||||
/* Check lengths */
|
if (newCache == NULL)
|
||||||
len = gst_string_length(lhs);
|
GST_OUT_OF_MEMORY;
|
||||||
if (len != gst_string_length(rhs)) return 0;
|
oldCapacity = vm->stringsCapacity;
|
||||||
/* Check hashes */
|
vm->strings = newCache;
|
||||||
hash_l = gst_string_hash(lhs);
|
vm->stringsCapacity = newCapacity;
|
||||||
hash_r = gst_string_hash(rhs);
|
vm->stringsDeleted = 0;
|
||||||
if (!hash_l)
|
/* Add all of the old strings back */
|
||||||
hash_l = gst_string_hash(lhs) = gst_string_calchash(lhs);
|
for (i = 0; i < oldCapacity; ++i) {
|
||||||
if (!hash_r)
|
int status;
|
||||||
hash_r = gst_string_hash(rhs) = gst_string_calchash(rhs);
|
const uint8_t **bucket;
|
||||||
if (hash_l != hash_r) return 0;
|
const uint8_t *str = oldCache[i];
|
||||||
for (i = 0; i < len; ++i)
|
if (str != NULL && str != deleted) {
|
||||||
if (lhs[i] != rhs[i])
|
bucket = gst_stringcache_find(vm, str,
|
||||||
return 0;
|
gst_string_length(str),
|
||||||
return 1;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Load a c string into a GST symbol */
|
||||||
|
GstValue gst_load_csymbol(Gst *vm, const char *string) {
|
||||||
|
GstValue ret;
|
||||||
|
ret.type = GST_SYMBOL;
|
||||||
|
ret.data.string = gst_cstring_to_string(vm, string);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Compares two strings */
|
/* Compares two strings */
|
||||||
|
@ -105,12 +105,12 @@ GstValue *gst_thread_expand_callable(Gst *vm, GstThread *thread, GstValue callee
|
|||||||
meta = callee.data.object->meta;
|
meta = callee.data.object->meta;
|
||||||
if (meta == NULL) return NULL;
|
if (meta == NULL) return NULL;
|
||||||
gst_thread_push(vm, thread, callee);
|
gst_thread_push(vm, thread, callee);
|
||||||
callee = gst_object_get_cstring(meta, "call");
|
callee = gst_object_get(meta, gst_load_cstring(vm, "call"));
|
||||||
continue;
|
continue;
|
||||||
case GST_USERDATA:
|
case GST_USERDATA:
|
||||||
meta = ((GstUserdataHeader *)callee.data.pointer - 1)->meta;
|
meta = ((GstUserdataHeader *)callee.data.pointer - 1)->meta;
|
||||||
gst_thread_push(vm, thread, callee);
|
gst_thread_push(vm, thread, callee);
|
||||||
callee = gst_object_get_cstring(meta, "call");
|
callee = gst_object_get(meta, gst_load_cstring(vm, "call"));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
82
core/value.c
82
core/value.c
@ -6,90 +6,86 @@ int gst_truthy(GstValue v) {
|
|||||||
return v.type != GST_NIL && !(v.type == GST_BOOLEAN && !v.data.boolean);
|
return v.type != GST_NIL && !(v.type == GST_BOOLEAN && !v.data.boolean);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t * number_to_string(Gst *vm, GstNumber x) {
|
/* Temporary buffer size */
|
||||||
static const uint32_t SIZE = 20;
|
#define GST_BUFSIZE 36
|
||||||
uint8_t *data = gst_alloc(vm, SIZE + 1 + 2 * sizeof(uint32_t));
|
|
||||||
data += 2 * sizeof(uint32_t);
|
static const uint8_t *number_to_string(Gst *vm, GstNumber x) {
|
||||||
|
uint8_t buf[GST_BUFSIZE];
|
||||||
/* TODO - not depend on stdio */
|
/* TODO - not depend on stdio */
|
||||||
snprintf((char *) data, SIZE, "%.21g", x);
|
int count = snprintf((char *) buf, GST_BUFSIZE, "%.21g", x);
|
||||||
gst_string_hash(data) = 0;
|
return gst_string_loadbuffer(vm, buf, (uint32_t) count);
|
||||||
gst_string_length(data) = strlen((char *) data);
|
|
||||||
return data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *HEX_CHARACTERS = "0123456789abcdef";
|
static const char *HEX_CHARACTERS = "0123456789abcdef";
|
||||||
#define HEX(i) (((uint8_t *) HEX_CHARACTERS)[(i)])
|
#define HEX(i) (((uint8_t *) HEX_CHARACTERS)[(i)])
|
||||||
|
|
||||||
/* Returns a string description for a pointer */
|
/* Returns a string description for a pointer. Max titlelen is GST_BUFSIZE
|
||||||
static uint8_t *string_description(Gst *vm, const char *title, uint32_t titlelen, void *pointer) {
|
* - 5 - 2 * sizeof(void *). */
|
||||||
uint32_t len = 5 + titlelen + sizeof(void *) * 2;
|
static const uint8_t *string_description(Gst *vm, const char *title, void *pointer) {
|
||||||
|
uint8_t buf[GST_BUFSIZE];
|
||||||
|
uint8_t *c = buf;
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
uint8_t *data = gst_alloc(vm, len + 1 + 2 * sizeof(uint32_t));
|
|
||||||
uint8_t *c;
|
|
||||||
union {
|
union {
|
||||||
uint8_t bytes[sizeof(void *)];
|
uint8_t bytes[sizeof(void *)];
|
||||||
void *p;
|
void *p;
|
||||||
} buf;
|
} pbuf;
|
||||||
buf.p = pointer;
|
|
||||||
data += 2 * sizeof(uint32_t);
|
pbuf.p = pointer;
|
||||||
c = data;
|
|
||||||
*c++ = '<';
|
*c++ = '<';
|
||||||
for (i = 0; i < titlelen; ++i) {
|
for (i = 0; title[i]; ++i)
|
||||||
*c++ = ((uint8_t *)title) [i];
|
*c++ = ((uint8_t *)title) [i];
|
||||||
}
|
|
||||||
*c++ = ' ';
|
*c++ = ' ';
|
||||||
*c++ = '0';
|
*c++ = '0';
|
||||||
*c++ = 'x';
|
*c++ = 'x';
|
||||||
for (i = sizeof(void *); i > 0; --i) {
|
for (i = sizeof(void *); i > 0; --i) {
|
||||||
uint8_t byte = buf.bytes[i - 1];
|
uint8_t byte = pbuf.bytes[i - 1];
|
||||||
if (!byte) continue;
|
if (!byte) continue;
|
||||||
*c++ = HEX(byte >> 4);
|
*c++ = HEX(byte >> 4);
|
||||||
*c++ = HEX(byte & 0xF);
|
*c++ = HEX(byte & 0xF);
|
||||||
}
|
}
|
||||||
*c++ = '>';
|
*c++ = '>';
|
||||||
gst_string_hash(data) = 0;
|
return gst_string_loadbuffer(vm, buf, c - buf);
|
||||||
gst_string_length(data) = c - data;
|
|
||||||
*c = 0;
|
|
||||||
return data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#undef GST_BUFSIZE
|
||||||
|
|
||||||
/* Returns a string pointer or NULL if could not allocate memory. */
|
/* Returns a string pointer or NULL if could not allocate memory. */
|
||||||
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_load_cstring_rawlen(vm, "nil", 3);
|
return gst_cstring_to_string(vm, "nil");
|
||||||
case GST_BOOLEAN:
|
case GST_BOOLEAN:
|
||||||
if (x.data.boolean) {
|
if (x.data.boolean) {
|
||||||
return gst_load_cstring_rawlen(vm, "true", 4);
|
return gst_cstring_to_string(vm, "true");
|
||||||
} else {
|
} else {
|
||||||
return gst_load_cstring_rawlen(vm, "false", 5);
|
return gst_cstring_to_string(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", 5, x.data.pointer);
|
return string_description(vm, "array", x.data.pointer);
|
||||||
case GST_TUPLE:
|
case GST_TUPLE:
|
||||||
return string_description(vm, "tuple", 5, x.data.pointer);
|
return string_description(vm, "tuple", x.data.pointer);
|
||||||
case GST_OBJECT:
|
case GST_OBJECT:
|
||||||
return string_description(vm, "object", 6, x.data.pointer);
|
return string_description(vm, "object", x.data.pointer);
|
||||||
case GST_STRING:
|
case GST_STRING:
|
||||||
return x.data.string;
|
return x.data.string;
|
||||||
case GST_SYMBOL:
|
case GST_SYMBOL:
|
||||||
return string_description(vm, "symbol", 6, x.data.pointer);
|
return string_description(vm, "symbol", x.data.pointer);
|
||||||
case GST_BYTEBUFFER:
|
case GST_BYTEBUFFER:
|
||||||
return string_description(vm, "buffer", 6, x.data.pointer);
|
return string_description(vm, "buffer", x.data.pointer);
|
||||||
case GST_CFUNCTION:
|
case GST_CFUNCTION:
|
||||||
return string_description(vm, "cfunction", 9, x.data.pointer);
|
return string_description(vm, "cfunction", x.data.pointer);
|
||||||
case GST_FUNCTION:
|
case GST_FUNCTION:
|
||||||
return string_description(vm, "function", 8, x.data.pointer);
|
return string_description(vm, "function", x.data.pointer);
|
||||||
case GST_THREAD:
|
case GST_THREAD:
|
||||||
return string_description(vm, "thread", 6, x.data.pointer);
|
return string_description(vm, "thread", x.data.pointer);
|
||||||
case GST_USERDATA:
|
case GST_USERDATA:
|
||||||
return string_description(vm, "userdata", 8, x.data.pointer);
|
return string_description(vm, "userdata", x.data.pointer);
|
||||||
case GST_FUNCENV:
|
case GST_FUNCENV:
|
||||||
return string_description(vm, "funcenv", 7, x.data.pointer);
|
return string_description(vm, "funcenv", x.data.pointer);
|
||||||
case GST_FUNCDEF:
|
case GST_FUNCDEF:
|
||||||
return string_description(vm, "funcdef", 7, x.data.pointer);
|
return string_description(vm, "funcdef", x.data.pointer);
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -173,10 +169,7 @@ uint32_t gst_hash(GstValue x) {
|
|||||||
/* String hashes */
|
/* String hashes */
|
||||||
case GST_STRING:
|
case GST_STRING:
|
||||||
case GST_SYMBOL:
|
case GST_SYMBOL:
|
||||||
/* Assume 0 is not hashed. */
|
|
||||||
hash = gst_string_hash(x.data.string);
|
hash = gst_string_hash(x.data.string);
|
||||||
if (!hash)
|
|
||||||
hash = gst_string_hash(x.data.string) = gst_string_calchash(x.data.string);
|
|
||||||
break;
|
break;
|
||||||
case GST_TUPLE:
|
case GST_TUPLE:
|
||||||
if (gst_tuple_hash(x.data.tuple))
|
if (gst_tuple_hash(x.data.tuple))
|
||||||
@ -418,8 +411,9 @@ int gst_length(Gst *vm, GstValue x, GstValue *len) {
|
|||||||
case GST_OBJECT:
|
case GST_OBJECT:
|
||||||
/* TODO - Check for class override */
|
/* TODO - Check for class override */
|
||||||
if (x.data.object->meta != NULL) {
|
if (x.data.object->meta != NULL) {
|
||||||
GstValue check = gst_object_get_cstring(
|
GstValue check = gst_object_get(
|
||||||
x.data.object->meta, "length");
|
x.data.object->meta,
|
||||||
|
gst_load_cstring(vm, "length"));
|
||||||
if (check.type != GST_NIL) {
|
if (check.type != GST_NIL) {
|
||||||
int status = gst_call(vm, check, 1, &x);
|
int status = gst_call(vm, check, 1, &x);
|
||||||
if (status == GST_RETURN_OK)
|
if (status == GST_RETURN_OK)
|
||||||
|
Loading…
Reference in New Issue
Block a user