Tasmota/lib/libesp32/Berry/src/be_string.c
2021-04-12 19:53:35 +02:00

298 lines
8.3 KiB
C

/********************************************************************
** Copyright (c) 2018-2020 Guan Wenliang
** This file is part of the Berry default interpreter.
** skiars@qq.com, https://github.com/Skiars/berry
** See Copyright Notice in the LICENSE file or at
** https://github.com/Skiars/berry/blob/master/LICENSE
********************************************************************/
#include "be_string.h"
#include "be_vm.h"
#include "be_mem.h"
#include "be_constobj.h"
#include <string.h>
#define next(_s) cast(void*, cast(bstring*, (_s)->next))
#define sstr(_s) cast(char*, cast(bsstring*, _s) + 1)
#define lstr(_s) cast(char*, cast(blstring*, _s) + 1)
#define cstr(_s) (cast(bcstring*, _s)->s)
#define be_define_const_str(_name, _s, _hash, _extra, _len, _next) \
BERRY_LOCAL const bcstring be_const_str_##_name = { \
.next = (bgcobject *)_next, \
.type = BE_STRING, \
.marked = GC_CONST, \
.extra = _extra, \
.slen = _len, \
.hash = _hash, \
.s = _s \
}
/* const string table */
struct bconststrtab {
const bstring* const *table;
int count; /* string count */
int size;
};
#if BE_USE_PRECOMPILED_OBJECT
#include "../generate/be_const_strtab_def.h"
#endif
int be_eqstr(bstring *s1, bstring *s2)
{
int slen;
if (s1 == s2) { /* short string or the same string */
return 1;
}
slen = s1->slen;
/* discard different lengths */
if (slen != s2->slen) {
return 0;
}
/* long string */
if (slen == 255) { /* s2->slen is also 255 */
blstring *ls1 = cast(blstring*, s1);
blstring *ls2 = cast(blstring*, s2);
return ls1->llen == ls2->llen && !strcmp(lstr(ls1), lstr(ls2));
}
// TODO one is long const and the other is long string
/* const short strings */
if (gc_isconst(s1) || gc_isconst(s2)) { /* one of the two string is short const */
if (cast(bcstring*, s1)->hash && cast(bcstring*, s2)->hash) {
return 0; /* if they both have a hash, then we know they are different */
}
return !strcmp(str(s1), str(s2));
}
return 0;
}
static void resize(bvm *vm, int size)
{
int i;
struct bstringtable *tab = &vm->strtab;
if (size > tab->size) {
tab->table = be_realloc(vm, tab->table,
tab->size * sizeof(bstring*), size * sizeof(bstring*));
for (i = tab->size; i < size; ++i) {
tab->table[i] = NULL;
}
}
for (i = 0; i < tab->size; ++i) { /* rehash */
bstring *p = tab->table[i];
tab->table[i] = NULL;
while (p) { /* for each node in the list */
bstring *hnext = next(p);
uint32_t hash = be_strhash(p) & (size - 1);
p->next = cast(void*, tab->table[hash]);
tab->table[hash] = p;
p = hnext;
}
}
if (size < tab->size) {
for (i = size; i < tab->size; ++i) {
tab->table[i] = NULL;
}
tab->table = be_realloc(vm, tab->table,
tab->size * sizeof(bstring*), size * sizeof(bstring*));
}
tab->size = size;
}
static void free_sstring(bvm *vm, bstring *str)
{
be_free(vm, str, sizeof(bsstring) + str->slen + 1);
}
/* FNV-1a Hash */
static uint32_t str_hash(const char *str, size_t len)
{
uint32_t hash = 2166136261u;
be_assert(str || len);
while (len--) {
hash = (hash ^ (unsigned char)*str++) * 16777619u;
}
return hash;
}
void be_string_init(bvm *vm)
{
resize(vm, 8);
#if !BE_USE_PRECOMPILED_OBJECT
/* the destructor name deinit needs to exist all the time, to ensure
* that it does not need to be created when the heap is exhausted. */
be_gc_fix(vm, cast(bgcobject*, str_literal(vm, "deinit")));
#endif
/* be_const_str_deinit --> for precompiled */
}
void be_string_deleteall(bvm *vm)
{
int i;
struct bstringtable *tab = &vm->strtab;
for (i = 0; i < tab->size; ++i) {
bstring *node = tab->table[i];
while (node) {
bstring *next = next(node);
free_sstring(vm, node);
node = next;
}
}
be_free(vm, tab->table, tab->size * sizeof(bstring*));
}
static bstring* createstrobj(bvm *vm, size_t len, int islong)
{
size_t size = (islong ? sizeof(blstring)
: sizeof(bsstring)) + len + 1;
bgcobject *gco = be_gc_newstr(vm, size, islong);
bstring *s = cast_str(gco);
if (s) {
s->slen = islong ? 255 : (bbyte)len;
char *str = cast(char *, islong ? lstr(s) : sstr(s));
str[len] = '\0';
}
return s;
}
#if BE_USE_PRECOMPILED_OBJECT
static bstring* find_conststr(const char *str, size_t len)
{
const struct bconststrtab *tab = &m_const_string_table;
uint32_t hash = str_hash(str, len);
bcstring *s = (bcstring*)tab->table[hash % tab->size];
for (; s != NULL; s = next(s)) {
if (len == s->slen && !strncmp(str, s->s, len)) {
return (bstring*)s;
}
}
return NULL;
}
#endif
static bstring* newshortstr(bvm *vm, const char *str, size_t len)
{
bstring *s;
int size = vm->strtab.size;
uint32_t hash = str_hash(str, len);
bstring **list = vm->strtab.table + (hash & (size - 1));
for (s = *list; s != NULL; s = next(s)) {
if (len == s->slen && !strncmp(str, sstr(s), len)) {
return s;
}
}
s = createstrobj(vm, len, 0);
if (s) {
memcpy(cast(char *, sstr(s)), str, len);
s->extra = 0;
s->next = cast(void*, *list);
#if BE_USE_STR_HASH_CACHE
cast(bsstring*, s)->hash = hash;
#endif
*list = s;
vm->strtab.count++;
if (vm->strtab.count > size << 2) {
resize(vm, size << 1);
}
}
return s;
}
bstring* be_newlongstr(bvm *vm, const char *str, size_t len)
{
bstring *s;
blstring *ls;
s = createstrobj(vm, len, 1);
ls = cast(blstring*, s);
s->extra = 0;
ls->llen = cast_int(len);
if (str) { /* if the argument 'str' is NULL, we just allocate space */
memcpy(cast(char *, lstr(s)), str, len);
}
return s;
}
bstring* be_newstr(bvm *vm, const char *str)
{
return be_newstrn(vm, str, strlen(str));
}
bstring *be_newstrn(bvm *vm, const char *str, size_t len)
{
if (len <= SHORT_STR_MAX_LEN) {
#if BE_USE_PRECOMPILED_OBJECT
bstring *s = find_conststr(str, len);
return s ? s : newshortstr(vm, str, len);
#else
return newshortstr(vm, str, len);
#endif
}
return be_newlongstr(vm, str, len); /* long string */
}
void be_gcstrtab(bvm *vm)
{
struct bstringtable *tab = &vm->strtab;
int size = tab->size, i;
for (i = 0; i < size; ++i) {
bstring **list = tab->table + i;
bstring *prev = NULL, *node, *next;
for (node = *list; node; node = next) {
next = next(node);
if (!gc_isfixed(node) && gc_iswhite(node)) {
free_sstring(vm, node);
tab->count--;
if (prev) { /* link list */
prev->next = cast(void*, next);
} else {
*list = next;
}
} else {
prev = node;
gc_setwhite(node);
}
}
}
if (tab->count < size >> 2 && size > 8) {
resize(vm, size >> 1);
}
}
uint32_t be_strhash(const bstring *s)
{
if (gc_isconst(s)) {
bcstring* cs = cast(bcstring*, s);
if (cs->hash) { /* if hash is null we need to compute it */
return cs->hash;
} else {
return str_hash(cstr(s), str_len(s));
}
}
#if BE_USE_STR_HASH_CACHE
if (s->slen != 255) {
return cast(bsstring*, s)->hash;
}
#endif
return str_hash(str(s), str_len(s));
}
const char* be_str2cstr(const bstring *s)
{
be_assert(cast_str(s) != NULL);
if (gc_isconst(s)) {
return cstr(s);
}
if (s->slen == 255) {
return lstr(s);
}
return sstr(s);
}
void be_str_setextra(bstring *s, int extra)
{
if (!gc_isconst(s)) {
s->extra = cast(bbyte, extra);
}
}