mirror of
https://github.com/janet-lang/janet
synced 2025-01-10 23:50:26 +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 #####
|
||||
###################################
|
||||
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)
|
||||
GST_CORE_OBJECTS=$(patsubst %.c,%.o,$(GST_CORE_SOURCES))
|
||||
$(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 */
|
||||
static SpecialFormHelper get_special(GstValue *form) {
|
||||
uint8_t *name;
|
||||
const uint8_t *name;
|
||||
if (gst_tuple_length(form) < 1 || form[0].type != GST_SYMBOL)
|
||||
return NULL;
|
||||
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 */
|
||||
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;
|
||||
if (newSize > buffer->capacity) {
|
||||
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 */
|
||||
uint8_t *gst_buffer_to_string(Gst *vm, GstBuffer *buffer) {
|
||||
return gst_load_cstring_rawlen(vm, (char *) buffer->data, buffer->count);
|
||||
const uint8_t *gst_buffer_to_string(Gst *vm, GstBuffer *buffer) {
|
||||
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 */
|
||||
GstValue gst_object_remove(Gst * vm, GstObject *o, GstValue key) {
|
||||
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) {
|
||||
GstValue x;
|
||||
GstNumber number;
|
||||
uint8_t * data = buf->data;
|
||||
uint8_t * back = data + buf->count;
|
||||
uint8_t *data = buf->data;
|
||||
uint8_t *back = data + buf->count;
|
||||
if (read_number(data, back, &number, 0)) {
|
||||
x.type = GST_NUMBER;
|
||||
x.data.number = number;
|
||||
@ -453,7 +453,7 @@ int gst_parse_cstring(GstParser *p, const char *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;
|
||||
p->status = GST_PARSER_PENDING;
|
||||
for (i = 0; i < gst_string_length(string); ++i) {
|
||||
|
@ -34,7 +34,7 @@
|
||||
static const char UEB[] = "unexpected end of buffer";
|
||||
|
||||
/* Read 4 bytes as an unsigned integer */
|
||||
static uint32_t bytes2u32(uint8_t *bytes) {
|
||||
static uint32_t bytes2u32(const uint8_t *bytes) {
|
||||
union {
|
||||
uint8_t bytes[4];
|
||||
uint32_t u32;
|
||||
@ -44,7 +44,7 @@ static uint32_t bytes2u32(uint8_t *bytes) {
|
||||
}
|
||||
|
||||
/* Read 2 bytes as unsigned short */
|
||||
static uint16_t bytes2u16(uint8_t *bytes) {
|
||||
static uint16_t bytes2u16(const uint8_t *bytes) {
|
||||
union {
|
||||
uint8_t bytes[2];
|
||||
uint16_t u16;
|
||||
@ -54,7 +54,7 @@ static uint16_t bytes2u16(uint8_t *bytes) {
|
||||
}
|
||||
|
||||
/* Read 8 bytes as a double */
|
||||
static uint32_t bytes2dbl(uint8_t *bytes) {
|
||||
static uint32_t bytes2dbl(const uint8_t *bytes) {
|
||||
union {
|
||||
uint8_t bytes[8];
|
||||
double dbl;
|
||||
@ -69,16 +69,16 @@ static uint32_t bytes2dbl(uint8_t *bytes) {
|
||||
* passed by reference. */
|
||||
static const char *gst_deserialize_impl(
|
||||
Gst *vm,
|
||||
uint8_t *data,
|
||||
uint8_t *end,
|
||||
uint8_t **newData,
|
||||
const uint8_t *data,
|
||||
const uint8_t *end,
|
||||
const uint8_t **newData,
|
||||
GstArray *visited,
|
||||
GstValue *out) {
|
||||
|
||||
GstValue ret;
|
||||
ret.type = GST_NIL;
|
||||
GstValue *buffer;
|
||||
uint8_t *bytebuf;
|
||||
const uint8_t *bytebuf;
|
||||
uint32_t length, i;
|
||||
const char *err;
|
||||
|
||||
@ -135,12 +135,7 @@ static const char *gst_deserialize_impl(
|
||||
ret.type = data[-1] == 205 ? GST_STRING : GST_SYMBOL;
|
||||
read_u32(length);
|
||||
deser_datacheck(length);
|
||||
ret.data.string = gst_alloc(vm, 2 * sizeof(uint32_t) + length + 1);
|
||||
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;
|
||||
ret.data.string = gst_string_loadbuffer(vm, data, length);
|
||||
data += length;
|
||||
gst_array_push(vm, visited, ret);
|
||||
break;
|
||||
@ -151,7 +146,7 @@ static const char *gst_deserialize_impl(
|
||||
deser_datacheck(length);
|
||||
ret.data.buffer = gst_alloc(vm, sizeof(GstBuffer));
|
||||
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->capacity = length;
|
||||
data += length;
|
||||
@ -391,10 +386,10 @@ static const char *gst_deserialize_impl(
|
||||
/* Load a value from data */
|
||||
const char *gst_deserialize(
|
||||
Gst *vm,
|
||||
uint8_t *data,
|
||||
const uint8_t *data,
|
||||
uint32_t len,
|
||||
GstValue *out,
|
||||
uint8_t *nextData) {
|
||||
const uint8_t *nextData) {
|
||||
GstValue ret;
|
||||
const char *err;
|
||||
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);
|
||||
for (j = 0; j < count; ++j) {
|
||||
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);
|
||||
for (i = 0; i < len; ++i)
|
||||
fputc(string[i], stdout);
|
||||
@ -46,7 +46,7 @@ int gst_stl_make_buffer(Gst *vm) {
|
||||
buf.data.buffer = gst_buffer(vm, 10);
|
||||
count = gst_count_args(vm);
|
||||
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_c_return(vm, buf);
|
||||
@ -55,7 +55,7 @@ int gst_stl_make_buffer(Gst *vm) {
|
||||
/* To string */
|
||||
int gst_stl_tostring(Gst *vm) {
|
||||
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.data.string = string;
|
||||
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) {
|
||||
GstValue ret;
|
||||
double number;
|
||||
uint8_t *str = gst_to_string(vm, gst_arg(vm, 0));
|
||||
uint8_t *end = str + gst_string_length(str);
|
||||
const uint8_t *str = gst_to_string(vm, gst_arg(vm, 0));
|
||||
const uint8_t *end = str + gst_string_length(str);
|
||||
if (read_number(str, end, &number, 0)) {
|
||||
ret.type = GST_NUMBER;
|
||||
ret.data.number = number;
|
||||
@ -161,7 +161,7 @@ int gst_stl_parse_number(Gst *vm) {
|
||||
|
||||
/* Parse a source string into an AST */
|
||||
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;
|
||||
/* init state */
|
||||
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_deinit(Gst *vm);
|
||||
uint8_t *gst_stringcache_get(Gst *vm, uint8_t *str);
|
||||
void gst_stringcache_remove(Gst *vm, uint8_t *str);
|
||||
void gst_stringcache_remove(Gst *vm, const uint8_t *str);
|
||||
|
||||
#endif
|
||||
|
260
core/strings.c
260
core/strings.c
@ -1,35 +1,28 @@
|
||||
#include <gst/gst.h>
|
||||
#include "stringcache.h"
|
||||
|
||||
uint8_t *gst_load_cstring_rawlen(Gst *vm, const char *string, uint32_t len) {
|
||||
uint8_t *data = gst_alloc(vm, len + 1 + 2 * sizeof(uint32_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);
|
||||
}
|
||||
/* Dud pointer to serve as deletion marker */
|
||||
static uint8_t *deleted = (uint8_t *) "";
|
||||
|
||||
/* 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_load_cstring_rawlen(vm, string, strlen(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_load_cstring_rawlen(vm, string, strlen(string));
|
||||
return ret;
|
||||
/* 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) */
|
||||
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;
|
||||
uint32_t hash = 5381;
|
||||
while (str < end)
|
||||
@ -37,31 +30,202 @@ uint32_t gst_cstring_calchash(const uint8_t *str, uint32_t len) {
|
||||
return hash;
|
||||
}
|
||||
|
||||
/* GST string version */
|
||||
uint32_t gst_string_calchash(const uint8_t *str) {
|
||||
return gst_cstring_calchash(str, gst_string_length(str));
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* Check if two strings are equal. Does not check the string cache. */
|
||||
int gst_string_equal(const uint8_t *lhs, const uint8_t *rhs) {
|
||||
uint32_t hash_l, hash_r, len, i;
|
||||
if (lhs == rhs)
|
||||
return 1;
|
||||
/* Check lengths */
|
||||
len = gst_string_length(lhs);
|
||||
if (len != gst_string_length(rhs)) return 0;
|
||||
/* Check hashes */
|
||||
hash_l = gst_string_hash(lhs);
|
||||
hash_r = gst_string_hash(rhs);
|
||||
if (!hash_l)
|
||||
hash_l = gst_string_hash(lhs) = gst_string_calchash(lhs);
|
||||
if (!hash_r)
|
||||
hash_r = gst_string_hash(rhs) = gst_string_calchash(rhs);
|
||||
if (hash_l != hash_r) return 0;
|
||||
for (i = 0; i < len; ++i)
|
||||
if (lhs[i] != rhs[i])
|
||||
return 0;
|
||||
return 1;
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
|
@ -105,12 +105,12 @@ GstValue *gst_thread_expand_callable(Gst *vm, GstThread *thread, GstValue callee
|
||||
meta = callee.data.object->meta;
|
||||
if (meta == NULL) return NULL;
|
||||
gst_thread_push(vm, thread, callee);
|
||||
callee = gst_object_get_cstring(meta, "call");
|
||||
callee = gst_object_get(meta, gst_load_cstring(vm, "call"));
|
||||
continue;
|
||||
case GST_USERDATA:
|
||||
meta = ((GstUserdataHeader *)callee.data.pointer - 1)->meta;
|
||||
gst_thread_push(vm, thread, callee);
|
||||
callee = gst_object_get_cstring(meta, "call");
|
||||
callee = gst_object_get(meta, gst_load_cstring(vm, "call"));
|
||||
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);
|
||||
}
|
||||
|
||||
static uint8_t * number_to_string(Gst *vm, GstNumber x) {
|
||||
static const uint32_t SIZE = 20;
|
||||
uint8_t *data = gst_alloc(vm, SIZE + 1 + 2 * sizeof(uint32_t));
|
||||
data += 2 * sizeof(uint32_t);
|
||||
/* Temporary buffer size */
|
||||
#define GST_BUFSIZE 36
|
||||
|
||||
static const uint8_t *number_to_string(Gst *vm, GstNumber x) {
|
||||
uint8_t buf[GST_BUFSIZE];
|
||||
/* TODO - not depend on stdio */
|
||||
snprintf((char *) data, SIZE, "%.21g", x);
|
||||
gst_string_hash(data) = 0;
|
||||
gst_string_length(data) = strlen((char *) data);
|
||||
return data;
|
||||
int count = snprintf((char *) buf, GST_BUFSIZE, "%.21g", x);
|
||||
return gst_string_loadbuffer(vm, buf, (uint32_t) count);
|
||||
}
|
||||
|
||||
static const char *HEX_CHARACTERS = "0123456789abcdef";
|
||||
#define HEX(i) (((uint8_t *) HEX_CHARACTERS)[(i)])
|
||||
|
||||
/* Returns a string description for a pointer */
|
||||
static uint8_t *string_description(Gst *vm, const char *title, uint32_t titlelen, void *pointer) {
|
||||
uint32_t len = 5 + titlelen + sizeof(void *) * 2;
|
||||
/* Returns a string description for a pointer. Max titlelen is GST_BUFSIZE
|
||||
* - 5 - 2 * sizeof(void *). */
|
||||
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;
|
||||
uint8_t *data = gst_alloc(vm, len + 1 + 2 * sizeof(uint32_t));
|
||||
uint8_t *c;
|
||||
union {
|
||||
uint8_t bytes[sizeof(void *)];
|
||||
void *p;
|
||||
} buf;
|
||||
buf.p = pointer;
|
||||
data += 2 * sizeof(uint32_t);
|
||||
c = data;
|
||||
} pbuf;
|
||||
|
||||
pbuf.p = pointer;
|
||||
*c++ = '<';
|
||||
for (i = 0; i < titlelen; ++i) {
|
||||
for (i = 0; title[i]; ++i)
|
||||
*c++ = ((uint8_t *)title) [i];
|
||||
}
|
||||
*c++ = ' ';
|
||||
*c++ = '0';
|
||||
*c++ = 'x';
|
||||
for (i = sizeof(void *); i > 0; --i) {
|
||||
uint8_t byte = buf.bytes[i - 1];
|
||||
uint8_t byte = pbuf.bytes[i - 1];
|
||||
if (!byte) continue;
|
||||
*c++ = HEX(byte >> 4);
|
||||
*c++ = HEX(byte & 0xF);
|
||||
}
|
||||
*c++ = '>';
|
||||
gst_string_hash(data) = 0;
|
||||
gst_string_length(data) = c - data;
|
||||
*c = 0;
|
||||
return data;
|
||||
return gst_string_loadbuffer(vm, buf, c - buf);
|
||||
}
|
||||
|
||||
#undef GST_BUFSIZE
|
||||
|
||||
/* 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) {
|
||||
case GST_NIL:
|
||||
return gst_load_cstring_rawlen(vm, "nil", 3);
|
||||
return gst_cstring_to_string(vm, "nil");
|
||||
case GST_BOOLEAN:
|
||||
if (x.data.boolean) {
|
||||
return gst_load_cstring_rawlen(vm, "true", 4);
|
||||
return gst_cstring_to_string(vm, "true");
|
||||
} else {
|
||||
return gst_load_cstring_rawlen(vm, "false", 5);
|
||||
return gst_cstring_to_string(vm, "false");
|
||||
}
|
||||
case GST_NUMBER:
|
||||
return number_to_string(vm, x.data.number);
|
||||
case GST_ARRAY:
|
||||
return string_description(vm, "array", 5, x.data.pointer);
|
||||
return string_description(vm, "array", x.data.pointer);
|
||||
case GST_TUPLE:
|
||||
return string_description(vm, "tuple", 5, x.data.pointer);
|
||||
return string_description(vm, "tuple", x.data.pointer);
|
||||
case GST_OBJECT:
|
||||
return string_description(vm, "object", 6, x.data.pointer);
|
||||
return string_description(vm, "object", x.data.pointer);
|
||||
case GST_STRING:
|
||||
return x.data.string;
|
||||
case GST_SYMBOL:
|
||||
return string_description(vm, "symbol", 6, x.data.pointer);
|
||||
return string_description(vm, "symbol", x.data.pointer);
|
||||
case GST_BYTEBUFFER:
|
||||
return string_description(vm, "buffer", 6, x.data.pointer);
|
||||
return string_description(vm, "buffer", x.data.pointer);
|
||||
case GST_CFUNCTION:
|
||||
return string_description(vm, "cfunction", 9, x.data.pointer);
|
||||
return string_description(vm, "cfunction", x.data.pointer);
|
||||
case GST_FUNCTION:
|
||||
return string_description(vm, "function", 8, x.data.pointer);
|
||||
return string_description(vm, "function", x.data.pointer);
|
||||
case GST_THREAD:
|
||||
return string_description(vm, "thread", 6, x.data.pointer);
|
||||
return string_description(vm, "thread", x.data.pointer);
|
||||
case GST_USERDATA:
|
||||
return string_description(vm, "userdata", 8, x.data.pointer);
|
||||
return string_description(vm, "userdata", x.data.pointer);
|
||||
case GST_FUNCENV:
|
||||
return string_description(vm, "funcenv", 7, x.data.pointer);
|
||||
return string_description(vm, "funcenv", x.data.pointer);
|
||||
case GST_FUNCDEF:
|
||||
return string_description(vm, "funcdef", 7, x.data.pointer);
|
||||
return string_description(vm, "funcdef", x.data.pointer);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@ -173,10 +169,7 @@ uint32_t gst_hash(GstValue x) {
|
||||
/* String hashes */
|
||||
case GST_STRING:
|
||||
case GST_SYMBOL:
|
||||
/* Assume 0 is not hashed. */
|
||||
hash = gst_string_hash(x.data.string);
|
||||
if (!hash)
|
||||
hash = gst_string_hash(x.data.string) = gst_string_calchash(x.data.string);
|
||||
break;
|
||||
case GST_TUPLE:
|
||||
if (gst_tuple_hash(x.data.tuple))
|
||||
@ -418,8 +411,9 @@ int gst_length(Gst *vm, GstValue x, GstValue *len) {
|
||||
case GST_OBJECT:
|
||||
/* TODO - Check for class override */
|
||||
if (x.data.object->meta != NULL) {
|
||||
GstValue check = gst_object_get_cstring(
|
||||
x.data.object->meta, "length");
|
||||
GstValue check = gst_object_get(
|
||||
x.data.object->meta,
|
||||
gst_load_cstring(vm, "length"));
|
||||
if (check.type != GST_NIL) {
|
||||
int status = gst_call(vm, check, 1, &x);
|
||||
if (status == GST_RETURN_OK)
|
||||
|
Loading…
Reference in New Issue
Block a user