klisp

an open source interpreter for the Kernel Programming Language.
git clone http://git.hanabi.in/repos/klisp.git
Log | Files | Refs | README

commit 5c14a3ecf31143f514741fd9fce317fc21fde3cb
parent c3b0290830bfa29735f712d150571b07775042c8
Author: Andres Navarro <canavarro82@gmail.com>
Date:   Wed, 20 Apr 2011 03:00:02 -0300

Merged hashtables branch

Diffstat:
Msrc/Makefile | 20++++++++++++--------
Msrc/kapplicative.c | 2--
Msrc/kcontinuation.c | 2--
Msrc/kencapsulation.c | 2--
Msrc/kenvironment.c | 62++++++++++++++++++++++++++++++++++++++++++++++++++------------
Msrc/kenvironment.h | 8++++++++
Msrc/kerror.c | 12++++++------
Msrc/kgc.c | 244++++++++++++++++++++++++++++++++++++-------------------------------------------
Msrc/kgc.h | 6+++---
Msrc/kgcombiners.c | 3++-
Msrc/kgeqp.h | 22+++++++++++++++++++---
Msrc/kgequalp.c | 2+-
Msrc/kground.c | 8+++++++-
Msrc/kgstrings.c | 59+++++++++++++++++++++++++++++++++++++++++------------------
Msrc/kgstrings.h | 8+++++---
Msrc/klimits.h | 16++++++++++++++++
Msrc/klispconf.h | 2+-
Msrc/kmem.c | 27+++++++++++++++++++++++++++
Msrc/kmem.h | 21++++++++++++++++++++-
Msrc/kobject.c | 18++++++++++++++++++
Msrc/kobject.h | 147++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
Msrc/koperative.c | 2--
Msrc/kpair.c | 1-
Msrc/kpair.h | 5+++--
Msrc/kport.c | 2--
Msrc/kpromise.c | 2--
Msrc/kread.c | 15+++------------
Msrc/kstate.c | 30+++++++++++++++++++++++-------
Msrc/kstate.h | 10+++++++++-
Msrc/kstring.c | 162+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
Msrc/kstring.h | 67++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
Msrc/ksymbol.c | 73++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
Msrc/ksymbol.h | 3++-
Asrc/ktable.c | 641+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/ktable.h | 40++++++++++++++++++++++++++++++++++++++++
Msrc/ktoken.c | 8+++++---
Msrc/kwrite.c | 4++--
37 files changed, 1434 insertions(+), 322 deletions(-)

diff --git a/src/Makefile b/src/Makefile @@ -10,7 +10,7 @@ MYLIBS= CORE_O= kobject.o ktoken.o kpair.o kstring.o ksymbol.o kread.o \ kwrite.o kstate.o kmem.o kerror.o kauxlib.o kenvironment.o \ kcontinuation.o koperative.o kapplicative.o keval.o krepl.o \ - kencapsulation.o kpromise.o kport.o kinteger.o \ + kencapsulation.o kpromise.o kport.o kinteger.o ktable.o \ kground.o kghelpers.o kgbooleans.o kgeqp.o kgequalp.o \ kgsymbols.o kgcontrol.o kgpairs_lists.o kgpair_mut.o kgenvironments.o \ kgenv_mut.o kgcombiners.o kgcontinuations.o kgencapsulations.o \ @@ -41,7 +41,7 @@ clean: klisp.o: klisp.c klisp.h kobject.h kread.h kwrite.h klimits.h kstate.h kmem.h \ kerror.h kauxlib.h koperative.h kenvironment.h kcontinuation.h \ - kapplicative.h koperative.h keval.h krepl.h + kapplicative.h koperative.h keval.h krepl.h kground.h kobject.o: kobject.c kobject.h klimits.h klispconf.h ktoken.o: ktoken.c ktoken.h kobject.h kstate.h kpair.h kstring.h ksymbol.h \ kerror.h klisp.h kinteger.h @@ -57,14 +57,14 @@ kread.o: kread.c kread.h kobject.h ktoken.h kpair.h kstate.h kerror.h klisp.h \ kwrite.o: kwrite.c kwrite.h kobject.h kpair.h kstring.h kstate.h kerror.h \ klisp.h kport.h kinteger.h kstate.o: kstate.c kstate.h klisp.h kobject.h kmem.h kstring.h klisp.h \ - kground.h kenvironment.h kpair.h keval.h koperative.h kground.h \ + kenvironment.h kpair.h keval.h koperative.h kground.h \ krepl.h kcontinuation.h kapplicative.h kport.h ksymbol.h kport.h \ - kstring.h kinteger.h kgc.h + kstring.h kinteger.h kgc.h klimits.h kmem.o: kmem.c kmem.h klisp.h kerror.h klisp.h kstate.h kgc.h klispconf.h kerror.o: kerror.c kerror.h klisp.h kstate.h klisp.h kmem.h kstring.h kpair.h kauxlib.o: kauxlib.c kauxlib.h klisp.h kstate.h klisp.h kenvironment.o: kenvironment.c kenvironment.h kpair.h kobject.h kerror.h \ - kmem.h kstate.h klisp.h kgc.h + kmem.h kstate.h klisp.h kgc.h ktable.h kcontinuation.o: kcontinuation.c kcontinuation.h kmem.h kstate.h kobject.h \ klisp.h kgc.h koperative.o: koperative.c koperative.h kmem.h kstate.h kobject.h \ @@ -77,6 +77,8 @@ kpromise.o: kpromise.c kpromise.h kmem.h kstate.h kobject.h \ klisp.h kpair.h kgc.h kport.o: kport.c kport.h kmem.h kstate.h kobject.h klisp.h kerror.h kstring.h \ kgc.h +ktable.o: ktable.c ktable.h kobject.h kstate.h kmem.h klisp.h kgc.h \ + kapplicative.h kgeqp.h kstring.h keval.o: keval.c keval.h kcontinuation.h kenvironment.h kstate.h kobject.h \ kpair.h kerror.h klisp.h krepl.o: krepl.c krepl.h kcontinuation.h kstate.h kobject.h keval.h klisp.h \ @@ -93,7 +95,8 @@ kghelpers.o: kghelpers.c kghelpers.h kstate.h kstate.h klisp.h kpair.h \ kgbooleans.o: kgbooleans.c kgbooleans.c kghelpers.h kstate.h klisp.h \ kobject.h kerror.h kpair.h kcontinuation.h ksymbol.h kgeqp.o: kgeqp.c kgeqp.c kghelpers.h kstate.h klisp.h \ - kobject.h kerror.h kpair.h kcontinuation.h + kobject.h kerror.h kpair.h kcontinuation.h kapplicative.h \ + kinteger.h kgequalp.o: kgequalp.c kgequalp.c kghelpers.h kstate.h klisp.h \ kobject.h kerror.h kpair.h kcontinuation.h kgeqp.h kstring.h kgsymbols.o: kgsymbols.c kgsymbols.c kghelpers.h kstate.h klisp.h \ @@ -140,6 +143,7 @@ kgnumbers.o: kgnumbers.c kgnumbers.h kghelpers.h kstate.h klisp.h \ ksymbol.h kinteger.h kgstrings.o: kgstrings.c kgstrings.h kghelpers.h kstate.h klisp.h \ kobject.h kerror.h kapplicative.h koperative.h kcontinuation.h \ - ksymbol.h kgnumbers.h + kstring.h ksymbol.h kgnumbers.h imath.o: kobject.h kstate.h kmem.h kerror.h -kgc.o: kgc.c kgc.h kobject.h kmem.h kstate.h kport.h imath.h +kgc.o: kgc.c kgc.h kobject.h kmem.h kstate.h kport.h imath.h ktable.h \ + kstring.h diff --git a/src/kapplicative.c b/src/kapplicative.c @@ -19,8 +19,6 @@ TValue kwrap(klisp_State *K, TValue underlying) klispC_link(K, (GCObject *) new_app, K_TAPPLICATIVE, 0); /* applicative specific fields */ - new_app->name = KNIL; - new_app->si = KNIL; new_app->underlying = underlying; return gc2app(new_app); } diff --git a/src/kcontinuation.c b/src/kcontinuation.c @@ -25,8 +25,6 @@ TValue kmake_continuation(klisp_State *K, TValue parent, klisp_Cfunc fn, /* continuation specific fields */ new_cont->mark = KFALSE; - new_cont->name = KNIL; - new_cont->si = KNIL; new_cont->parent = parent; new_cont->fn = fn; new_cont->extra_size = xcount; diff --git a/src/kencapsulation.c b/src/kencapsulation.c @@ -20,8 +20,6 @@ TValue kmake_encapsulation(klisp_State *K, TValue key, TValue val) klispC_link(K, (GCObject *) new_enc, K_TENCAPSULATION, 0); /* encapsulation specific fields */ - new_enc->name = KNIL; - new_enc->si = KNIL; new_enc->key = key; new_enc->value = val; diff --git a/src/kenvironment.c b/src/kenvironment.c @@ -13,6 +13,7 @@ #include "kerror.h" #include "kstate.h" #include "kmem.h" +#include "ktable.h" #include "kgc.h" /* keyed dynamic vars */ @@ -84,11 +85,13 @@ TValue kmake_environment(klisp_State *K, TValue parents) /* ** Helper function for kadd_binding and kget_binding, +** Only for list environments, table environments are handled elsewhere ** returns KNIL or a pair with sym as car. */ TValue kfind_local_binding(klisp_State *K, TValue bindings, TValue sym) { - (void) K; + UNUSED(K); + while(!ttisnil(bindings)) { TValue first = kcar(bindings); TValue first_sym = kcar(first); @@ -109,13 +112,22 @@ TValue kfind_local_binding(klisp_State *K, TValue bindings, TValue sym) right now, but that could change */ void kadd_binding(klisp_State *K, TValue env, TValue sym, TValue val) { - TValue oldb = kfind_local_binding(K, kenv_bindings(K, env), sym); + klisp_assert(ttisenvironment(env)); + klisp_assert(ttissymbol(sym)); - if (ttisnil(oldb)) { - TValue new_pair = kcons(K, sym, val); - kenv_bindings(K, env) = kcons(K, new_pair, kenv_bindings(K, env)); + TValue bindings = kenv_bindings(K, env); + if (ttistable(bindings)) { + TValue *cell = klispH_setsym(K, tv2table(bindings), tv2sym(sym)); + *cell = val; } else { - kset_cdr(oldb, val); + TValue oldb = kfind_local_binding(K, bindings, sym); + + if (ttisnil(oldb)) { + TValue new_pair = kcons(K, sym, val); + kenv_bindings(K, env) = kcons(K, new_pair, bindings); + } else { + kset_cdr(oldb, val); + } } } @@ -134,12 +146,24 @@ inline bool try_get_binding(klisp_State *K, TValue env, TValue sym, if (ttisnil(obj)) { continue; } else if (ttisenvironment(obj)) { - TValue oldb = kfind_local_binding(K, kenv_bindings(K, obj), sym); - if (!ttisnil(oldb)) { - /* remember to leave the stack as it was */ - ks_sdiscardn(K, pushed); - *value = kcdr(oldb); - return true; + TValue bindings = kenv_bindings(K, obj); + if (ttistable(bindings)) { + const TValue *cell = klispH_getsym(tv2table(bindings), + tv2sym(sym)); + if (cell != &kfree) { + /* remember to leave the stack as it was */ + ks_sdiscardn(K, pushed); + *value = *cell; + return true; + } + } else { + TValue oldb = kfind_local_binding(K, bindings, sym); + if (!ttisnil(oldb)) { + /* remember to leave the stack as it was */ + ks_sdiscardn(K, pushed); + *value = kcdr(oldb); + return true; + } } TValue parents = kenv_parents(K, obj); ks_spush(K, parents); @@ -157,6 +181,8 @@ inline bool try_get_binding(klisp_State *K, TValue env, TValue sym, TValue kget_binding(klisp_State *K, TValue env, TValue sym) { + klisp_assert(ttisenvironment(env)); + klisp_assert(ttissymbol(sym)); TValue value; if (try_get_binding(K, env, sym, &value)) { return value; @@ -237,3 +263,15 @@ TValue kget_keyed_static_var(klisp_State *K, TValue env, TValue key) return KINERT; } } + +/* environments with hashtable bindings */ +/* TEMP: for now only for ground environment */ +TValue kmake_table_environment(klisp_State *K, TValue parents) +{ + TValue new_env = kmake_environment(K, parents); + krooted_tvs_push(K, new_env); + TValue new_table = klispH_new(K, 0, ENVTABSIZE, K_FLAG_WEAK_NOTHING); + tv2env(new_env)->bindings = new_table; + krooted_tvs_pop(K); + return new_env; +} diff --git a/src/kenvironment.h b/src/kenvironment.h @@ -22,4 +22,12 @@ TValue kmake_keyed_static_env(klisp_State *K, TValue parent, TValue key, TValue val); TValue kget_keyed_static_var(klisp_State *K, TValue env, TValue key); +/* environments with hashtable bindings */ +/* TEMP: for now only for ground environment + TODO: Should profile too see when it makes sense & should add code + to all operatives creating environments to see when it's appropiate + or should add code to add binding to at certain point move over to + hashtable */ +TValue kmake_table_environment(klisp_State *K, TValue parents); + #endif diff --git a/src/kerror.c b/src/kerror.c @@ -33,10 +33,9 @@ void clear_buffers(klisp_State *K) void klispE_throw(klisp_State *K, char *msg) { - TValue error_msg = kstring_new(K, msg, strlen(msg)); + TValue error_msg = kstring_new_b_imm(K, msg); /* TEMP */ clear_buffers(K); - kcall_cont(K, K->error_cont, error_msg); } @@ -46,15 +45,16 @@ void klispE_throw_extra(klisp_State *K, char *msg, char *extra_msg) { int32_t l1 = strlen(msg); int32_t l2 = strlen(extra_msg); - int32_t tl = l1+l2+1; + int32_t tl = l1+l2; - char *msg_buf = klispM_malloc(K, tl); + char *msg_buf = klispM_malloc(K, tl+1); strcpy(msg_buf, msg); strcpy(msg_buf+l1, extra_msg); + msg_buf[tl] = '\0'; /* if the mem allocator could throw errors, this could potentially leak msg_buf */ - TValue error_msg = kstring_new(K, msg_buf, tl); - klispM_freemem(K, msg_buf, tl); + TValue error_msg = kstring_new_bs_imm(K, msg_buf, tl); + klispM_freemem(K, msg_buf, tl+1); clear_buffers(K); diff --git a/src/kgc.c b/src/kgc.c @@ -17,10 +17,8 @@ #include "kmem.h" #include "kport.h" #include "imath.h" - -/* XXX */ -#include "kwrite.h" -/* XXX */ +#include "ktable.h" +#include "kstring.h" #define GCSTEPSIZE 1024u #define GCSWEEPMAX 40 @@ -68,14 +66,11 @@ #define setthreshold(g) (g->GCthreshold = (g->estimate/100) * g->gcpause) -/* klisp no need for it yet */ -#if 0 static void removeentry (Node *n) { - klisp_assert(ttisnil(gval(n))); - if (iscollectable(gkey(n))) - setttype(gkey(n), LUA_TDEADKEY); /* dead key; remove it */ + klisp_assert(ttisfree(gval(n))); + if (iscollectable(gkey(n)->this))/* dead key; remove it */ + gkey(n)->this = gc2deadkey(gcvalue(gkey(n)->this)); } -#endif static void reallymarkobject (klisp_State *K, GCObject *o) { @@ -108,6 +103,7 @@ static void reallymarkobject (klisp_State *K, GCObject *o) case K_TENCAPSULATION: case K_TPROMISE: case K_TPORT: + case K_TTABLE: o->gch.gclist = K->gray; K->gray = o; break; @@ -163,48 +159,44 @@ size_t klispC_separateudata (lua_State *L, int all) { return deadmem; } +#endif -static int traversetable (global_State *g, Table *h) { - int i; - int weakkey = 0; - int weakvalue = 0; - const TValue *mode; - if (h->metatable) - markobject(g, h->metatable); - mode = gfasttm(g, h->metatable, TM_MODE); - if (mode && ttisstring(mode)) { /* is there a weak mode? */ - weakkey = (strchr(svalue(mode), 'k') != NULL); - weakvalue = (strchr(svalue(mode), 'v') != NULL); - if (weakkey || weakvalue) { /* is really weak? */ - h->gct &= ~(KEYWEAK | VALUEWEAK); /* clear bits */ - h->gct |= cast(uint16_t, (weakkey << KEYWEAKBIT) | - (weakvalue << VALUEWEAKBIT)); - h->gclist = g->weak; /* must be cleared after GC, ... */ - g->weak = obj2gco(h); /* ... so put in the appropriate list */ - } +static int32_t traversetable (klisp_State *K, Table *h) { + int32_t i; + TValue tv = gc2table(h); + int32_t weakkey = ktable_has_weak_keys(tv)? 1 : 0; + int32_t weakvalue = ktable_has_weak_values(tv)? 1 : 0; + + if (weakkey || weakvalue) { /* is really weak? */ + h->gct &= ~(KEYWEAK | VALUEWEAK); /* clear bits */ + h->gct |= cast(uint16_t, (weakkey << KEYWEAKBIT) | + (weakvalue << VALUEWEAKBIT)); + h->gclist = K->weak; /* must be cleared after GC, ... */ + K->weak = obj2gco(h); /* ... so put in the appropriate list */ } if (weakkey && weakvalue) return 1; if (!weakvalue) { i = h->sizearray; while (i--) - markvalue(g, &h->array[i]); + markvalue(K, h->array[i]); } i = sizenode(h); while (i--) { Node *n = gnode(h, i); - klisp_assert(ttype(gkey(n)) != LUA_TDEADKEY || ttisnil(gval(n))); - if (ttisnil(gval(n))) + klisp_assert(ttype(gkey(n)->this) != K_TDEADKEY || + ttisfree(gval(n))); + if (ttisfree(gval(n))) removeentry(n); /* remove empty entries */ else { - klisp_assert(!ttisnil(gkey(n))); - if (!weakkey) markvalue(g, gkey(n)); - if (!weakvalue) markvalue(g, gval(n)); + klisp_assert(!ttisfree(gkey(n)->this)); + if (!weakkey) markvalue(K, gkey(n)->this); + if (!weakvalue) markvalue(K, gval(n)); } } return weakkey || weakvalue; } - +#if 0 /* ** All marks are conditional because a GC may happen while the ** prototype is still being created @@ -242,23 +234,12 @@ static int32_t propagatemark (klisp_State *K) { uint8_t type = o->gch.tt; switch (type) { -#if 0 /* klisp: keep around */ - case LUA_TTABLE: { - Table *h = gco2h(o); - K->gray = h->gclist; - if (traversetable(K, h)) /* table is weak? */ - black2gray(o); /* keep it gray */ - return sizeof(Table) + sizeof(TValue) * h->sizearray + - sizeof(Node) * sizenode(h); - } -#endif /* case K_TBIGINT: bigints are never gray */ case K_TPAIR: { Pair *p = cast(Pair *, o); markvalue(K, p->mark); markvalue(K, p->car); markvalue(K, p->cdr); - markvalue(K, p->si); return sizeof(Pair); } case K_TSYMBOL: { @@ -284,48 +265,43 @@ static int32_t propagatemark (klisp_State *K) { case K_TCONTINUATION: { Continuation *c = cast(Continuation *, o); markvalue(K, c->mark); - markvalue(K, c->name); - markvalue(K, c->si); markvalue(K, c->parent); markvaluearray(K, c->extra, c->extra_size); return sizeof(Continuation) + sizeof(TValue) * c->extra_size; } case K_TOPERATIVE: { Operative *op = cast(Operative *, o); - markvalue(K, op->name); - markvalue(K, op->si); markvaluearray(K, op->extra, op->extra_size); return sizeof(Operative) + sizeof(TValue) * op->extra_size; } case K_TAPPLICATIVE: { Applicative *a = cast(Applicative *, o); - markvalue(K, a->name); - markvalue(K, a->si); markvalue(K, a->underlying); return sizeof(Applicative); } case K_TENCAPSULATION: { Encapsulation *e = cast(Encapsulation *, o); - markvalue(K, e->name); - markvalue(K, e->si); markvalue(K, e->key); markvalue(K, e->value); return sizeof(Encapsulation); } case K_TPROMISE: { Promise *p = cast(Promise *, o); - markvalue(K, p->name); - markvalue(K, p->si); markvalue(K, p->node); return sizeof(Promise); } case K_TPORT: { Port *p = cast(Port *, o); - markvalue(K, p->name); - markvalue(K, p->si); markvalue(K, p->filename); return sizeof(Port); } + case K_TTABLE: { + Table *h = cast(Table *, o); + if (traversetable(K, h)) /* table is weak? */ + black2gray(o); /* keep it gray */ + return sizeof(Table) + sizeof(TValue) * h->sizearray + + sizeof(Node) * sizenode(h); + } default: fprintf(stderr, "Unknown GCObject type (in GC propagate): %d\n", type); @@ -340,7 +316,6 @@ static size_t propagateall (klisp_State *K) { return m; } -#if 0 /* klisp: keep around */ /* ** The next function tells whether a key or value can be cleared from ** a weak table. Non-collectable objects are never removed from weak @@ -348,14 +323,19 @@ static size_t propagateall (klisp_State *K) { ** other objects: if really collected, cannot keep them; for userdata ** being finalized, keep them in keys, but not in values */ -static int iscleared (const TValue *o, int iskey) { +static int32_t iscleared (TValue o, int iskey) { if (!iscollectable(o)) return 0; +#if 0 /* klisp: strings may be mutable... */ if (ttisstring(o)) { stringmark(rawtsvalue(o)); /* strings are `values', so are never weak */ return 0; } - return iswhite(gcvalue(o)) || - (ttisuserdata(o) && (!iskey && isfinalized(uvalue(o)))); +#endif + return iswhite(gcvalue(o)); + +/* klisp: keep around for later + || (ttisuserdata(o) && (!iskey && isfinalized(uvalue(o)))); +*/ } @@ -364,36 +344,34 @@ static int iscleared (const TValue *o, int iskey) { */ static void cleartable (GCObject *l) { while (l) { - Table *h = gco2h(l); - int i = h->sizearray; + Table *h = (Table *) (l); + int32_t i = h->sizearray; klisp_assert(testbit(h->gct, VALUEWEAKBIT) || testbit(h->gct, KEYWEAKBIT)); if (testbit(h->gct, VALUEWEAKBIT)) { while (i--) { TValue *o = &h->array[i]; - if (iscleared(o, 0)) /* value was collected? */ - setnilvalue(o); /* remove value */ + if (iscleared(*o, 0)) /* value was collected? */ + *o = KFREE; /* remove value */ } } i = sizenode(h); while (i--) { Node *n = gnode(h, i); - if (!ttisnil(gval(n)) && /* non-empty entry? */ + if (!ttisfree(gval(n)) && /* non-empty entry? */ (iscleared(key2tval(n), 1) || iscleared(gval(n), 0))) { - setnilvalue(gval(n)); /* remove value ... */ + gval(n) = KFREE; /* remove value ... */ removeentry(n); /* remove entry from table */ } } l = h->gclist; } } -#endif static void freeobj (klisp_State *K, GCObject *o) { - /* TODO use specific functions like in bigint & lua */ + /* TODO use specific functions like in bigint & table */ uint8_t type = o->gch.tt; switch (type) { - /* case LUA_TTABLE: luaH_free(L, gco2h(o)); break; */ case K_TBIGINT: { mp_int_free(K, (Bigint *)o); break; @@ -402,10 +380,15 @@ static void freeobj (klisp_State *K, GCObject *o) { klispM_free(K, (Pair *)o); break; case K_TSYMBOL: + /* symbols are in the string/symbol table */ /* The string will be freed before/after */ + K->strt.nuse--; klispM_free(K, (Symbol *)o); break; case K_TSTRING: + /* immutable strings are in the string/symbol table */ + if (kstring_immutablep(gc2str(o))) + K->strt.nuse--; klispM_freemem(K, o, sizeof(String)+o->str.size+1); break; case K_TENVIRONMENT: @@ -437,6 +420,9 @@ static void freeobj (klisp_State *K, GCObject *o) { kclose_port(K, gc2port(o)); klispM_free(K, (Port *)o); break; + case K_TTABLE: + klispH_free(K, (Table *)o); + break; default: /* shouldn't happen */ fprintf(stderr, "Unknown GCObject type (in GC free): %d\n", @@ -470,20 +456,19 @@ static GCObject **sweeplist (klisp_State *K, GCObject **p, uint32_t count) return p; } -#if 0 /* klisp: keep this around */ -static void checkSizes (lua_State *L) { - global_State *g = G(L); - /* check size of string hash */ - if (g->strt.nuse < cast(lu_int32, g->strt.size/4) && - g->strt.size > MINSTRTABSIZE*2) - luaS_resize(L, g->strt.size/2); /* table is too big */ +static void checkSizes (klisp_State *K) { + /* check size of string/symbol hash */ + if (K->strt.nuse < cast(uint32_t , K->strt.size/4) && + K->strt.size > MINSTRTABSIZE*2) + klispS_resize(K, K->strt.size/2); /* table is too big */ +#if 0 /* not used in klisp */ /* check size of buffer */ if (luaZ_sizebuffer(&g->buff) > LUA_MINBUFFER*2) { /* buffer too big? */ size_t newsize = luaZ_sizebuffer(&g->buff) / 2; luaZ_resizebuffer(L, &g->buff, newsize); } -} #endif +} #if 0 /* klisp: keep this around */ static void GCTM (lua_State *L) { @@ -531,26 +516,20 @@ void klispC_freeall (klisp_State *K) { K->currentwhite = WHITEBITS | bitmask(SFIXEDBIT); /* in klisp this may not be necessary */ sweepwholelist(K, &K->rootgc); + /* free all symbol/string lists */ + for (int32_t i = 0; i < K->strt.size; i++) + sweepwholelist(K, &K->strt.hash[i]); } -#if 0 /* klisp: keep this around */ -static void markmt (global_State *g) { - int i; - for (i=0; i<NUM_TAGS; i++) - if (g->mt[i]) markobject(g, g->mt[i]); -} -#endif - /* mark root set */ static void markroot (klisp_State *K) { K->gray = NULL; - K->grayagain = NULL; /* for now in klisp this isn't used */ - K->weak = NULL; /* for now in klisp this isn't used */ + K->grayagain = NULL; + K->weak = NULL; /* TEMP: this is quite awfull, think of other way to do this */ /* MAYBE: some of these could be FIXED */ - markvalue(K, K->symbol_table); markvalue(K, K->curr_cont); markvalue(K, K->next_obj); markvalue(K, K->next_value); @@ -586,7 +565,6 @@ static void markroot (klisp_State *K) { markvalue(K, K->dummy_pair1); markvalue(K, K->dummy_pair2); markvalue(K, K->dummy_pair3); -/* markmt(g); */ K->gcstate = GCSpropagate; } @@ -599,11 +577,7 @@ static void atomic (klisp_State *K) { /* remark weak tables */ K->gray = K->weak; K->weak = NULL; -#if 0 /* keep around */ - markmt(g); /* mark basic metatables (again) */ - propagateall(g); -#endif - /* klisp: for now in klisp this isn't used */ + /* remark gray again */ K->gray = K->grayagain; K->grayagain = NULL; @@ -614,10 +588,12 @@ static void atomic (klisp_State *K) { udsize = klispC_separateudata(L, 0); /* separate userdata to be finalized */ marktmu(g); /* mark `preserved' userdata */ udsize += propagateall(g); /* remark, to propagate `preserveness' */ - cleartable(g->weak); /* remove collected objects from weak tables */ #endif + cleartable(K->weak); /* remove collected objects from weak tables */ + /* flip current white */ K->currentwhite = cast(uint16_t, otherwhite(K)); + K->sweepstrgc = 0; K->sweepgc = &K->rootgc; K->gcstate = GCSsweepstring; K->estimate = K->totalbytes - udsize; /* first estimate */ @@ -639,16 +615,19 @@ static int32_t singlestep (klisp_State *K) { } } case GCSsweepstring: { - /* No need to do anything in klisp, we just kept it - to avoid eliminating a state in the GC */ - K->gcstate = GCSsweep; /* end sweep-string phase */ - return 0; + uint32_t old = K->totalbytes; + sweepwholelist(K, &K->strt.hash[K->sweepstrgc++]); + if (K->sweepstrgc >= K->strt.size) /* nothing more to sweep? */ + K->gcstate = GCSsweep; /* end sweep-string phase */ + klisp_assert(old >= K->totalbytes); + K->estimate -= old - K->totalbytes; + return GCSWEEPCOST; } case GCSsweep: { uint32_t old = K->totalbytes; K->sweepgc = sweeplist(K, K->sweepgc, GCSWEEPMAX); if (*K->sweepgc == NULL) { /* nothing more to sweep? */ - /* checkSizes(K); */ /* klisp: keep this around */ + checkSizes(K); K->gcstate = GCSfinalize; /* end sweep phase */ } klisp_assert(old >= K->totalbytes); @@ -693,8 +672,8 @@ void klispC_step (klisp_State *K) { if (K->gcstate != GCSpause) { if (K->gcdept < GCSTEPSIZE) { - /* - lim/g->gcstepmul;*/ K->GCthreshold = K->totalbytes + GCSTEPSIZE; + /* - lim/g->gcstepmul;*/ } else { K->gcdept -= GCSTEPSIZE; K->GCthreshold = K->totalbytes; @@ -707,25 +686,26 @@ void klispC_step (klisp_State *K) { void klispC_fullgc (klisp_State *K) { if (K->gcstate <= GCSpropagate) { - /* reset sweep marks to sweep all elements (returning them to white) */ - K->sweepgc = &K->rootgc; - /* reset other collector lists */ - K->gray = NULL; - K->grayagain = NULL; - K->weak = NULL; - K->gcstate = GCSsweepstring; - } - klisp_assert(K->gcstate != GCSpause && K->gcstate != GCSpropagate); - /* finish any pending sweep phase */ - while (K->gcstate != GCSfinalize) { - klisp_assert(K->gcstate == GCSsweepstring || K->gcstate == GCSsweep); - singlestep(K); - } - markroot(K); - while (K->gcstate != GCSpause) { - singlestep(K); - } - setthreshold(K); + /* reset sweep marks to sweep all elements (returning them to white) */ + K->sweepstrgc = 0; + K->sweepgc = &K->rootgc; + /* reset other collector lists */ + K->gray = NULL; + K->grayagain = NULL; + K->weak = NULL; + K->gcstate = GCSsweepstring; + } + klisp_assert(K->gcstate != GCSpause && K->gcstate != GCSpropagate); + /* finish any pending sweep phase */ + while (K->gcstate != GCSfinalize) { + klisp_assert(K->gcstate == GCSsweepstring || K->gcstate == GCSsweep); + singlestep(K); + } + markroot(K); + while (K->gcstate != GCSpause) { + singlestep(K); + } + setthreshold(K); } /* TODO: make all code using mutation to call these, @@ -737,7 +717,7 @@ guarded stack! */ void klispC_barrierf (klisp_State *K, GCObject *o, GCObject *v) { klisp_assert(isblack(o) && iswhite(v) && !isdead(K, v) && !isdead(K, o)); klisp_assert(K->gcstate != GCSfinalize && K->gcstate != GCSpause); -/* klisp_assert(ttype(&o->gch) != LUA_TTABLE); */ + klisp_assert(o->gch.tt != K_TTABLE); /* must keep invariant? */ if (K->gcstate == GCSpropagate) reallymarkobject(K, v); /* restore invariant */ @@ -745,24 +725,22 @@ void klispC_barrierf (klisp_State *K, GCObject *o, GCObject *v) { makewhite(K, o); /* mark as white just to avoid other barriers */ } -#if 0 /* keep around */ -void klispC_barrierback (lua_State *L, Table *t) { +void klispC_barrierback (klisp_State *K, Table *t) { GCObject *o = obj2gco(t); - klisp_assert(isblack(o) && !isdead(g, o)); - klisp_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause); + klisp_assert(isblack(o) && !isdead(K, o)); + klisp_assert(K->gcstate != GCSfinalize && K->gcstate != GCSpause); black2gray(o); /* make table gray (again) */ - t->gclist = g->grayagain; - g->grayagain = o; + t->gclist = K->grayagain; + K->grayagain = o; } -#endif -/* NOTE: flags is added for klisp */ -void klispC_link (klisp_State *K, GCObject *o, uint8_t tt, uint8_t flags) { +/* NOTE: kflags is added for klisp */ +void klispC_link (klisp_State *K, GCObject *o, uint8_t tt, uint8_t kflags) { o->gch.next = K->rootgc; K->rootgc = o; o->gch.gct = klispC_white(K); o->gch.tt = tt; - o->gch.flags = flags; + o->gch.kflags = kflags; /* NOTE that o->gch.gclist doesn't need to be setted */ } diff --git a/src/kgc.h b/src/kgc.h @@ -42,8 +42,8 @@ #define reset2bits(x,b1,b2) resetbits(x, (bit2mask(b1, b2))) #define test2bits(x,b1,b2) testbits(x, (bit2mask(b1, b2))) -/* NOTE: in klisp there is still no tables, userdata, threads, weak keys, - or finalization. Also the field is called gct instead of marked */ +/* NOTE: in klisp there is still no userdata, threads or finalization. + Also the field is called gct instead of marked */ /* ** Layout for bit use in `gct' field: @@ -110,6 +110,6 @@ void klispC_step (klisp_State *K); void klispC_fullgc (klisp_State *K); void klispC_link (klisp_State *K, GCObject *o, uint8_t tt, uint8_t flags); void klispC_barrierf (klisp_State *K, GCObject *o, GCObject *v); -/* void klispC_barrierback (klisp_State *K, Table *t); */ +void klispC_barrierback (klisp_State *K, Table *t); #endif diff --git a/src/kgcombiners.c b/src/kgcombiners.c @@ -81,7 +81,8 @@ void do_vau(klisp_State *K, TValue *xparams, TValue obj, TValue denv) /* TODO use name from operative */ match(K, "[user-operative]", env, ptree, obj); - kadd_binding(K, env, penv, denv); + if (!ttisignore(penv)) + kadd_binding(K, env, penv, denv); /* keep env in stack in case a cont has to be constructed */ diff --git a/src/kgeqp.h b/src/kgeqp.h @@ -15,6 +15,8 @@ #include "kstate.h" #include "kobject.h" +#include "kapplicative.h" /* for unwrap */ +#include "kinteger.h" /* for kbigint_eqp */ #include "klisp.h" #include "kghelpers.h" @@ -23,11 +25,25 @@ void eqp(klisp_State *K, TValue *xparams, TValue ptree, TValue denv); /* Helper (also used in equal?) */ -/* TEMP: for now this is the same as tv_equal, - later it will change with numbers and immutable objects */ +/* TEMP: this will change with immutable strings */ inline bool eq2p(klisp_State *K, TValue obj1, TValue obj2) { - return (tv_equal(obj1, obj2)); + bool res = (tv_equal(obj1, obj2)); + if (!res && (ttype(obj1) == ttype(obj2))) { + if (ttisapplicative(obj1)) { + while(ttisapplicative(obj1) && ttisapplicative(obj2)) { + obj1 = kunwrap(obj1); + obj2 = kunwrap(obj2); + } + res = (tv_equal(obj1, obj2)); + } else if (ttisbigint(obj1)) { + /* it's important to know that it can't be the case + that obj1 is bigint and obj is some other type and + (eq? obj1 obj2) */ + res = kbigint_eqp(obj1, obj2); + } + } + return res; } #endif diff --git a/src/kgequalp.c b/src/kgequalp.c @@ -181,7 +181,7 @@ bool equal2p(klisp_State *K, TValue obj1, TValue obj2) while(!ks_sisempty(K)) { obj2 = ks_spop(K); obj1 = ks_spop(K); - +/* REFACTOR these ifs: compare both types first, then switch on type */ if (!eq2p(K, obj1, obj2)) { if (ttispair(obj1) && ttispair(obj2)) { /* if they were already compaired, consider equal for now diff --git a/src/kground.c b/src/kground.c @@ -807,7 +807,13 @@ void kinit_ground_env(klisp_State *K) /* 13.2.8? string-copy */ add_applicative(K, ground_env, "string-copy", string_copy, 0); - /* 13.2.9? string-fill! */ + /* 13.2.9? string->immutable-string */ + add_applicative(K, ground_env, "string->immutable-string", + string_to_immutable_string, 0); + + /* TODO: add string-immutable? or general immutable? */ + + /* 13.2.10? string-fill! */ add_applicative(K, ground_env, "string-fill!", string_fillS, 0); /* diff --git a/src/kgstrings.c b/src/kgstrings.c @@ -49,7 +49,7 @@ void make_string(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) return; } - TValue new_str = kstring_new_sc(K, ivalue(tv_s), fill); + TValue new_str = kstring_new_sf(K, ivalue(tv_s), fill); kapply_cc(K, new_str); } @@ -102,6 +102,9 @@ void string_setS(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) /* TODO show index */ klispE_throw(K, "string-set!: index out of bounds"); return; + } else if (kstring_immutablep(str)) { + klispE_throw(K, "string-set!: immutable string"); + return; } int32_t i = ivalue(tv_i); @@ -130,7 +133,7 @@ inline TValue list_to_string_h(klisp_State *K, char *name, TValue ls) if (pairs == 0) { return K->empty_string; } else { - new_str = kstring_new_g(K, pairs); + new_str = kstring_new_s(K, pairs); char *buf = kstring_buf(new_str); TValue tail = ls; while(pairs--) { @@ -162,14 +165,9 @@ void string(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) /* Helpers for binary predicates */ /* XXX: this should probably be in file kstring.h */ -bool kstring_eqp(TValue str1, TValue str2) -{ - int32_t size = kstring_size(str1); - if (kstring_size(str2) != size) - return false; - else - return ((size == 0) || - memcmp(kstring_buf(str1), kstring_buf(str2), size) == 0); + +bool kstring_eqp(TValue str1, TValue str2) { + return tv_equal(str1, str2) || kstring_equalp(str1, str2); } bool kstring_ci_eqp(TValue str1, TValue str2) @@ -241,6 +239,7 @@ bool kstring_ci_gep(TValue str1, TValue str2) } /* 13.2.5? substring */ +/* TEMP: at least for now this always returns mutable strings */ void substring(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) { UNUSED(xparams); @@ -278,12 +277,13 @@ void substring(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) if (size == 0) { new_str = K->empty_string; } else { - new_str = kstring_new(K, kstring_buf(str)+start, size); + new_str = kstring_new_bs(K, kstring_buf(str)+start, size); } kapply_cc(K, new_str); } /* 13.2.6? string-append */ +/* TEMP: at least for now this always returns mutable strings */ /* TEMP: this does 3 passes over the list */ void string_append(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) @@ -314,7 +314,7 @@ void string_append(klisp_State *K, TValue *xparams, TValue ptree, if (size == 0) { new_str = K->empty_string; } else { - new_str = kstring_new_g(K, size); + new_str = kstring_new_s(K, size); char *buf = kstring_buf(new_str); /* loop again to copy the chars of each string */ tail = ptree; @@ -369,6 +369,7 @@ void list_to_string(klisp_State *K, TValue *xparams, TValue ptree, } /* 13.2.8? string-copy */ +/* TEMP: at least for now this always returns mutable strings */ void string_copy(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) { UNUSED(xparams); @@ -380,12 +381,29 @@ void string_copy(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) if (tv_equal(str, K->empty_string)) { new_str = str; } else { - new_str = kstring_new(K, kstring_buf(str), kstring_size(str)); + new_str = kstring_new_bs(K, kstring_buf(str), kstring_size(str)); } kapply_cc(K, new_str); } -/* 13.2.9? string-fill! */ +/* 13.2.9? string->immutable-string */ +void string_to_immutable_string(klisp_State *K, TValue *xparams, + TValue ptree, TValue denv) +{ + UNUSED(xparams); + UNUSED(denv); + bind_1tp(K, "string->immutable-string", ptree, "string", ttisstring, str); + + TValue res_str; + if (kstring_immutablep(str)) {/* this includes the empty list */ + res_str = str; + } else { + res_str = kstring_new_bs_imm(K, kstring_buf(str), kstring_size(str)); + } + kapply_cc(K, res_str); +} + +/* 13.2.10? string-fill! */ void string_fillS(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) { UNUSED(xparams); @@ -393,22 +411,26 @@ void string_fillS(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) bind_2tp(K, "string-fill!", ptree, "string", ttisstring, str, "char", ttischar, tv_ch); + if (kstring_immutablep(str)) { + klispE_throw(K, "string-fill!: immutable string"); + return; + } + memset(kstring_buf(str), chvalue(tv_ch), kstring_size(str)); kapply_cc(K, KINERT); } /* 13.3.1? symbol->string */ -/* TEMP: for now all strings are mutable, this returns a new object - each time */ +/* The strings in symbols are immutable so we can just return that */ void symbol_to_string(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) { UNUSED(xparams); UNUSED(denv); bind_1tp(K, "symbol->string", ptree, "symbol", ttissymbol, sym); - TValue new_str = kstring_new(K, ksymbol_buf(sym), ksymbol_size(sym)); - kapply_cc(K, new_str); + TValue str = ksymbol_str(sym); + kapply_cc(K, str); } /* 13.3.2? string->symbol */ @@ -421,6 +443,7 @@ void symbol_to_string(klisp_State *K, TValue *xparams, TValue ptree, because the report only says that read objects when written and read again must be equal? which happens here */ +/* If the string is mutable it is copied */ void string_to_symbol(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) { diff --git a/src/kgstrings.h b/src/kgstrings.h @@ -78,12 +78,14 @@ void string_to_list(klisp_State *K, TValue *xparams, TValue ptree, /* 13.2.8? string-copy */ void string_copy(klisp_State *K, TValue *xparams, TValue ptree, TValue denv); -/* 13.2.9? string-fill! */ +/* 13.2.9? string->immutable-string */ +void string_to_immutable_string(klisp_State *K, TValue *xparams, + TValue ptree, TValue denv); + +/* 13.2.10? string-fill! */ void string_fillS(klisp_State *K, TValue *xparams, TValue ptree, TValue denv); /* 13.3.1? symbol->string */ -/* TEMP: for now all strings are mutable, this returns a new object - each time */ void symbol_to_string(klisp_State *K, TValue *xparams, TValue ptree, TValue denv); diff --git a/src/klimits.h b/src/klimits.h @@ -42,4 +42,20 @@ #define cast(t, exp) ((t)(exp)) #endif +/* +** conversion of pointer to integer +** this is for hashing only; there is no problem if the integer +** cannot hold the whole pointer value +*/ +#define IntPoint(p) ((uint32_t)(p)) + +/* minimum size for the string table (must be power of 2) */ +#ifndef MINSTRTABSIZE +#define MINSTRTABSIZE 32 +#endif + +/* starting size for ground environment hashtable */ +/* at last count, there were about 200 bindings in ground env */ +#define ENVTABSIZE 512 + #endif diff --git a/src/klispconf.h b/src/klispconf.h @@ -16,7 +16,7 @@ /* temp defines till gc is stabilized */ #define KUSE_GC 1 /* Print msgs when starting and ending gc */ -/* #define KDEBUG_GC 1 */ +#define KDEBUG_GC 1 /* #define KTRACK_MARKS (true) diff --git a/src/kmem.c b/src/kmem.c @@ -20,6 +20,8 @@ #include "kerror.h" #include "kgc.h" +#define MINSIZEARRAY 4 + /* ** About the realloc function: ** void * frealloc (void *ud, void *ptr, size_t osize, size_t nsize); @@ -38,6 +40,31 @@ ** (any reallocation to an equal or smaller size cannot fail!) */ +void *klispM_growaux_ (klisp_State *K, void *block, int *size, size_t size_elems, + int32_t limit, const char *errormsg) { + void *newblock; + int32_t newsize; + if (*size >= limit/2) { /* cannot double it? */ + if (*size >= limit) /* cannot grow even a little? */ + klispE_throw(K, (char *) errormsg); /* XXX */ + newsize = limit; /* still have at least one free place */ + } + else { + newsize = (*size)*2; + if (newsize < MINSIZEARRAY) + newsize = MINSIZEARRAY; /* minimum size */ + } + newblock = klispM_reallocv(K, block, *size, newsize, size_elems); + *size = newsize; /* update only when everything else is OK */ + return newblock; +} + + +void *klispM_toobig (klisp_State *K) { + klispE_throw(K, "memory allocation error: block too big"); + return NULL; /* to avoid warnings */ +} + /* ** generic allocation routine. diff --git a/src/kmem.h b/src/kmem.h @@ -8,7 +8,7 @@ #define kmem_h /* -** SOURCE NOTE: This is from Lua, but greatly shortened +** SOURCE NOTE: This is from Lua */ #include <stddef.h> @@ -17,13 +17,32 @@ #define MEMERRMSG "not enough memory" +#define klispM_reallocv(L,b,on,n,e) \ + ((cast(size_t, (n)+1) <= SIZE_MAX/(e)) ? /* +1 to avoid warnings */ \ + klispM_realloc_(L, (b), (on)*(e), (n)*(e)) : \ + klispM_toobig(L)) + #define klispM_freemem(K, b, s) klispM_realloc_(K, (b), (s), 0) #define klispM_free(K, b) klispM_realloc_(K, (b), sizeof(*(b)), 0) +#define klispM_freearray(L, b, n, t) klispM_reallocv(L, (b), n, 0, sizeof(t)) #define klispM_malloc(K,t) klispM_realloc_(K, NULL, 0, (t)) #define klispM_new(K,t) cast(t *, klispM_malloc(K, sizeof(t))) +#define klispM_newvector(L,n,t) \ + cast(t *, klispM_reallocv(L, NULL, 0, n, sizeof(t))) + +#define klispM_growvector(L,v,nelems,size,t,limit,e) \ + if ((nelems)+1 > (size)) \ + ((v)=cast(t *, klispM_growaux_(L,v,&(size),sizeof(t),limit,e))) + +#define klispM_reallocvector(L, v,oldn,n,t) \ + ((v)=cast(t *, klispM_reallocv(L, v, oldn, n, sizeof(t)))) void *klispM_realloc_ (klisp_State *K, void *block, size_t oldsize, size_t size); +void *klispM_toobig (klisp_State *K); +void *klispM_growaux_ (klisp_State *K, void *block, int *size, + size_t size_elem, int limit, + const char *errormsg); #endif diff --git a/src/kobject.c b/src/kobject.c @@ -22,6 +22,7 @@ const TValue kepinf = KEPINF_; const TValue keminf = KEMINF_; const TValue kspace = KSPACE_; const TValue knewline = KNEWLINE_; +const TValue kfree = KFREE_; /* ** The name strings for all TValue types @@ -44,6 +45,8 @@ char *ktv_names[] = { [K_TEOF] = "eof", [K_TBOOLEAN] = "boolean", [K_TCHAR] = "char", + [K_TCHAR] = "free entry", + [K_TDEADKEY] = "dead key", [K_TUSER] = "user pointer", @@ -69,3 +72,18 @@ bool kis_output_port(TValue o) return ttisport(o) && kport_is_output(o); } +int32_t klispO_log2 (uint32_t x) { + static const uint8_t log_2[256] = { + 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 + }; + int32_t l = -1; + while (x >= 256) { l += 8; x >>= 8; } + return l + log_2[x]; +} diff --git a/src/kobject.h b/src/kobject.h @@ -44,11 +44,11 @@ typedef union GCObject GCObject; ** Common Header for all collectible objects (in macro form, to be ** included in other objects) */ -#define CommonHeader GCObject *next; uint8_t tt; uint8_t flags; \ +#define CommonHeader GCObject *next; uint8_t tt; uint8_t kflags; \ uint16_t gct; uint32_t padding; GCObject *gclist; /* NOTE: the gc flags are called marked in lua, but we reserve that them - for marks used in cycle traversal. The field flags is also missing + for marks used in cycle traversal. The field kflags is also missing from lua, they serve as compact bool fields for certain types */ /* @@ -147,6 +147,7 @@ typedef struct __attribute__ ((__packed__)) GCheader { #define K_TEOF 23 #define K_TBOOLEAN 24 #define K_TCHAR 25 +#define K_TFREE 26 /* this is used instead of lua nil in tables */ /* user pointer */ #define K_TUSER 29 @@ -160,6 +161,10 @@ typedef struct __attribute__ ((__packed__)) GCheader { #define K_TENCAPSULATION 37 #define K_TPROMISE 38 #define K_TPORT 39 +#define K_TTABLE 40 + +/* for tables */ +#define K_TDEADKEY 60 /* this is used to test for numbers, as returned by ttype */ #define K_LAST_NUMBER_TYPE K_TCOMPLEX @@ -187,6 +192,8 @@ typedef struct __attribute__ ((__packed__)) GCheader { #define K_TAG_EOF K_MAKE_VTAG(K_TEOF) #define K_TAG_BOOLEAN K_MAKE_VTAG(K_TBOOLEAN) #define K_TAG_CHAR K_MAKE_VTAG(K_TCHAR) +#define K_TAG_FREE K_MAKE_VTAG(K_TDEADKEY) +#define K_TAG_DEADKEY K_MAKE_VTAG(K_TDEADKEY) #define K_TAG_USER K_MAKE_VTAG(K_TUSER) @@ -202,6 +209,7 @@ typedef struct __attribute__ ((__packed__)) GCheader { #define K_TAG_ENCAPSULATION K_MAKE_VTAG(K_TENCAPSULATION) #define K_TAG_PROMISE K_MAKE_VTAG(K_TPROMISE) #define K_TAG_PORT K_MAKE_VTAG(K_TPORT) +#define K_TAG_TABLE K_MAKE_VTAG(K_TTABLE) /* @@ -221,7 +229,7 @@ typedef struct __attribute__ ((__packed__)) GCheader { /* Simple types (value in TValue struct) */ #define ttisfixint(o) (tbasetype_(o) == K_TAG_FIXINT) -#define ttisbigint(o) (tbasetype_(o) == K_TAG_FIXINT) +#define ttisbigint(o) (tbasetype_(o) == K_TAG_BIGINT) #define ttisinteger(o_) ({ int32_t t_ = tbasetype_(o_); \ t_ == K_TAG_FIXINT || t_ == K_TAG_BIGINT;}) #define ttisnumber(o) (ttype(o) <= K_LAST_NUMBER_TYPE); }) @@ -233,6 +241,7 @@ typedef struct __attribute__ ((__packed__)) GCheader { #define ttiseof(o) (tbasetype_(o) == K_TAG_EOF) #define ttisboolean(o) (tbasetype_(o) == K_TAG_BOOLEAN) #define ttischar(o) (tbasetype_(o) == K_TAG_CHAR) +#define ttisfree(o) (tbasetype_(o) == K_TAG_FREE) #define ttisdouble(o) ((ttag(o) & K_TAG_BASE_MASK) != K_TAG_TAGGED) /* Complex types (value in heap), @@ -254,6 +263,7 @@ typedef struct __attribute__ ((__packed__)) GCheader { #define ttisencapsulation(o) (tbasetype_(o) == K_TAG_ENCAPSULATION) #define ttispromise(o) (tbasetype_(o) == K_TAG_PROMISE) #define ttisport(o) (tbasetype_(o) == K_TAG_PORT) +#define ttistable(o) (tbasetype_(o) == K_TAG_TABLE) /* macros to easily check boolean values */ #define kis_true(o_) (tv_equal((o_), KTRUE)) @@ -316,20 +326,22 @@ typedef struct __attribute__ ((__packed__)) { TValue mark; /* for cycle/sharing aware algorithms */ TValue car; TValue cdr; - TValue si; /* source code info (either () or (filename line col) */ } Pair; typedef struct __attribute__ ((__packed__)) { CommonHeader; TValue mark; /* for cycle/sharing aware algorithms */ TValue str; /* could use String * here, but for now... */ + uint32_t hash; /* this is different from the str hash to + avoid having both the string and the symbol + from always falling in the same bucket */ } Symbol; typedef struct __attribute__ ((__packed__)) { CommonHeader; TValue mark; /* for cycle/sharing aware algorithms */ TValue parents; /* may be (), a list, or a single env */ - TValue bindings; /* TEMP: for now alist of (binding . value) */ + TValue bindings; /* alist of (binding . value) or table */ /* for keyed static vars */ TValue keyed_node; /* (key . value) pair or KNIL */ /* this is a different field from parents to jump over non keyed @@ -340,8 +352,6 @@ typedef struct __attribute__ ((__packed__)) { typedef struct __attribute__ ((__packed__)) { CommonHeader; TValue mark; /* for guarding continuation */ - TValue name; /* cont name/type */ - TValue si; /* source code info (either () or (filename line col) */ TValue parent; /* may be () for root continuation */ void *fn; /* the function that does the work */ int32_t extra_size; @@ -350,8 +360,6 @@ typedef struct __attribute__ ((__packed__)) { typedef struct __attribute__ ((__packed__)) { CommonHeader; - TValue name; - TValue si; /* source code info (either () or (filename line col) */ void *fn; /* the function that does the work */ int32_t extra_size; TValue extra[]; @@ -359,23 +367,17 @@ typedef struct __attribute__ ((__packed__)) { typedef struct __attribute__ ((__packed__)) { CommonHeader; - TValue name; - TValue si; /* source code info (either () or (filename line col) */ TValue underlying; /* underlying operative/applicative */ } Applicative; typedef struct __attribute__ ((__packed__)) { CommonHeader; - TValue name; - TValue si; /* source code info (either () or (filename line col) */ TValue key; /* unique pair identifying this type of encapsulation */ TValue value; /* encapsulated object */ } Encapsulation; typedef struct __attribute__ ((__packed__)) { CommonHeader; - TValue name; - TValue si; /* source code info (either () or (filename line col) */ TValue node; /* pair (exp . maybe-env) */ /* if maybe-env is nil, then the promise has determined exp, otherwise the promise should eval exp in maybe-env when forced @@ -384,15 +386,59 @@ typedef struct __attribute__ ((__packed__)) { sharing the pair */ } Promise; -/* input/output direction and open/close status are in flags */ +/* input/output direction and open/close status are in kflags */ typedef struct __attribute__ ((__packed__)) { CommonHeader; - TValue name; - TValue si; /* source code info (either () or (filename line col) */ TValue filename; FILE *file; } Port; +/* input/output direction and open/close status are in kflags */ + +/* +** Hashtables +*/ + +typedef union TKey { + struct { + TValue this; /* different from lua because of the tagging scheme */ + struct Node *next; /* for chaining */ + } nk; + TValue tvk; +} TKey; + +typedef struct Node { + TValue i_val; + TKey i_key; +} Node; + +typedef struct __attribute__ ((__packed__)) { + CommonHeader; + uint8_t lsizenode; /* log2 of size of `node' array */ + uint8_t t1padding; + uint16_t t2padding; /* to avoid disturbing the alignment */ + TValue *array; /* array part */ + Node *node; + Node *lastfree; /* any free position is before this position */ + int32_t sizearray; /* size of `array' array */ +} Table; + +/* The weak flags are in kflags */ + +/* +** `module' operation for hashing (size is always a power of 2) +*/ +#define lmod(s,size) \ + (check_exp((size&(size-1))==0, (cast(int32_t, (s) & ((size)-1))))) + + +#define twoto(x) (1<<(x)) +#define sizenode(t) (twoto((t)->lsizenode)) + +#define ceillog2(x) (klispO_log2((x)-1) + 1) + +int32_t klispO_log2 (uint32_t x); + /* ** RATIONALE: ** @@ -405,9 +451,12 @@ typedef struct __attribute__ ((__packed__)) { CommonHeader; TValue mark; /* for cycle/sharing aware algorithms */ uint32_t size; + uint32_t hash; /* only used for immutable strings */ char b[]; // buffer } String; +/* MAYBE: mark fields could be replaced by a hashtable or a bit + a hashtable */ + /* ** Common header for markable objects */ @@ -433,6 +482,7 @@ union GCObject { Encapsulation enc; Promise prom; Port port; + Table table; }; @@ -449,6 +499,7 @@ union GCObject { #define KEMINF_ {.tv = {.t = K_TAG_EINF, .v = { .i = -1 }}} #define KSPACE_ {.tv = {.t = K_TAG_CHAR, .v = { .ch = ' ' }}} #define KNEWLINE_ {.tv = {.t = K_TAG_CHAR, .v = { .ch = '\n' }}} +#define KFREE_ {.tv = {.t = K_TAG_FREE, .v = { .i = 0 }}} /* RATIONALE: the ones above can be used in initializers */ @@ -462,6 +513,7 @@ union GCObject { #define KEMINF ((TValue) KEMINF_) #define KSPACE ((TValue) KSPACE_) #define KNEWLINE ((TValue) KNEWLINE_) +#define KFREE ((TValue) KFREE_) /* The same constants as global const variables */ const TValue knil; @@ -474,6 +526,7 @@ const TValue kepinf; const TValue keminf; const TValue kspace; const TValue knewline; +const TValue kfree; /* Macros to create TValues of non-heap allocated types (for initializers) */ #define ch2tv_(ch_) {.tv = {.t = K_TAG_CHAR, .v = { .ch = (ch_) }}} @@ -504,6 +557,8 @@ const TValue knewline; #define gc2enc(o_) (gc2tv(K_TAG_ENCAPSULATION, o_)) #define gc2prom(o_) (gc2tv(K_TAG_PROMISE, o_)) #define gc2port(o_) (gc2tv(K_TAG_PORT, o_)) +#define gc2table(o_) (gc2tv(K_TAG_TABLE, o_)) +#define gc2deadkey(o_) (gc2tv(K_TAG_DEADKEY, o_)) /* Macro to convert a TValue into a specific heap allocated object */ #define tv2bigint(v_) ((Bigint *) gcvalue(v_)) @@ -517,6 +572,7 @@ const TValue knewline; #define tv2enc(v_) ((Encapsulation *) gcvalue(v_)) #define tv2prom(v_) ((Promise *) gcvalue(v_)) #define tv2port(v_) ((Port *) gcvalue(v_)) +#define tv2table(v_) ((Table *) gcvalue(v_)) #define tv2gch(v_) ((GCheader *) gcvalue(v_)) #define tv2mgch(v_) ((MGCheader *) gcvalue(v_)) @@ -568,48 +624,61 @@ int32_t kmark_count; #define kis_marked(p_) (!kis_unmarked(p_)) #define kis_unmarked(p_) (tv_equal(kget_mark(p_), KFALSE)) -/* Macros to access flags & type in GCHeader */ +/* Macros to access kflags & type in GCHeader */ +/* TODO: 1 should always be reserved for mutability flag */ #define gch_get_type(o_) (obj2gch(o_)->tt) -#define gch_get_flags(o_) (obj2gch(o_)->flags) -#define tv_get_flags(o_) (gch_get_flags(tv2gch(o_))) +#define gch_get_kflags(o_) (obj2gch(o_)->kflags) +#define tv_get_kflags(o_) (gch_get_kflags(tv2gch(o_))) -/* Flags for symbols */ +/* KFlags for symbols */ /* has external representation (identifiers) */ #define K_FLAG_EXT_REP 0x01 -#define khas_ext_rep(s_) ((tv_get_flags(s_) & K_FLAG_EXT_REP) != 0) +#define khas_ext_rep(s_) ((tv_get_kflags(s_) & K_FLAG_EXT_REP) != 0) -/* Flags for marking continuations */ +/* KFlags for marking continuations */ #define K_FLAG_OUTER 0x01 #define K_FLAG_INNER 0x02 #define K_FLAG_DYNAMIC 0x04 #define K_FLAG_BOOL_CHECK 0x08 /* evaluate c_ more than once */ -#define kset_inner_cont(c_) (tv_get_flags(c_) |= K_FLAG_INNER) -#define kset_outer_cont(c_) (tv_get_flags(c_) |= K_FLAG_OUTER) -#define kset_dyn_cont(c_) (tv_get_flags(c_) |= K_FLAG_DYNAMIC) -#define kset_bool_check_cont(c_) (tv_get_flags(c_) |= K_FLAG_BOOL_CHECK) +#define kset_inner_cont(c_) (tv_get_kflags(c_) |= K_FLAG_INNER) +#define kset_outer_cont(c_) (tv_get_kflags(c_) |= K_FLAG_OUTER) +#define kset_dyn_cont(c_) (tv_get_kflags(c_) |= K_FLAG_DYNAMIC) +#define kset_bool_check_cont(c_) (tv_get_kflags(c_) |= K_FLAG_BOOL_CHECK) -#define kis_inner_cont(c_) ((tv_get_flags(c_) & K_FLAG_INNER) != 0) -#define kis_outer_cont(c_) ((tv_get_flags(c_) & K_FLAG_OUTER) != 0) -#define kis_dyn_cont(c_) ((tv_get_flags(c_) & K_FLAG_DYNAMIC) != 0) -#define kis_bool_check_cont(c_) ((tv_get_flags(c_) & K_FLAG_BOOL_CHECK) != 0) +#define kis_inner_cont(c_) ((tv_get_kflags(c_) & K_FLAG_INNER) != 0) +#define kis_outer_cont(c_) ((tv_get_kflags(c_) & K_FLAG_OUTER) != 0) +#define kis_dyn_cont(c_) ((tv_get_kflags(c_) & K_FLAG_DYNAMIC) != 0) +#define kis_bool_check_cont(c_) ((tv_get_kflags(c_) & K_FLAG_BOOL_CHECK) != 0) +/* for now only used in pairs and strings */ #define K_FLAG_IMMUTABLE 0x01 -#define kis_mutable(o_) ((tv_get_flags(o_) & K_FLAG_IMMUTABLE) == 0) +#define kis_mutable(o_) ((tv_get_kflags(o_) & K_FLAG_IMMUTABLE) == 0) #define kis_immutable(o_) (!kis_mutable(o_)) #define K_FLAG_OUTPUT_PORT 0x01 #define K_FLAG_INPUT_PORT 0x02 #define K_FLAG_CLOSED_PORT 0x04 -#define kport_set_input(o_) (tv_get_flags(o_) |= K_FLAG_INPUT_PORT) -#define kport_set_output(o_) (tv_get_flags(o_) |= K_FLAG_INPUT_PORT) -#define kport_set_closed(o_) (tv_get_flags(o_) |= K_FLAG_CLOSED_PORT) +#define kport_set_input(o_) (tv_get_kflags(o_) |= K_FLAG_INPUT_PORT) +#define kport_set_output(o_) (tv_get_kflags(o_) |= K_FLAG_INPUT_PORT) +#define kport_set_closed(o_) (tv_get_kflags(o_) |= K_FLAG_CLOSED_PORT) + +#define kport_is_input(o_) ((tv_get_kflags(o_) & K_FLAG_INPUT_PORT) != 0) +#define kport_is_output(o_) ((tv_get_kflags(o_) & K_FLAG_OUTPUT_PORT) != 0) +#define kport_is_closed(o_) ((tv_get_kflags(o_) & K_FLAG_CLOSED_PORT) != 0) + +#define K_FLAG_WEAK_KEYS 0x01 +#define K_FLAG_WEAK_VALUES 0x02 +#define K_FLAG_WEAK_NOTHING 0x00 + +#define ktable_has_weak_keys(o_) \ + ((tv_get_kflags(o_) & K_FLAG_WEAK_KEYS) != 0) +#define ktable_has_weak_values(o_) \ + ((tv_get_kflags(o_) & K_FLAG_WEAK_VALUES) != 0) + -#define kport_is_input(o_) ((tv_get_flags(o_) & K_FLAG_INPUT_PORT) != 0) -#define kport_is_output(o_) ((tv_get_flags(o_) & K_FLAG_OUTPUT_PORT) != 0) -#define kport_is_closed(o_) ((tv_get_flags(o_) & K_FLAG_CLOSED_PORT) != 0) /* can't be inline because we also use pointers to them, (at least gcc doesn't bother to create them and the linker fails) */ diff --git a/src/koperative.c b/src/koperative.c @@ -24,8 +24,6 @@ TValue kmake_operative(klisp_State *K, klisp_Ofunc fn, int32_t xcount, ...) klispC_link(K, (GCObject *) new_op, K_TOPERATIVE, 0); /* operative specific fields */ - new_op->name = KNIL; - new_op->si = KNIL; new_op->fn = fn; new_op->extra_size = xcount; diff --git a/src/kpair.c b/src/kpair.c @@ -21,7 +21,6 @@ TValue kcons_g(klisp_State *K, bool m, TValue car, TValue cdr) klispC_link(K, (GCObject *) new_pair, K_TPAIR, (m? 0 : K_FLAG_IMMUTABLE)); /* pair specific fields */ - new_pair->si = KNIL; new_pair->mark = KFALSE; new_pair->car = car; new_pair->cdr = cdr; diff --git a/src/kpair.h b/src/kpair.h @@ -62,8 +62,9 @@ TValue klist_g(klisp_State *K, bool m, int32_t n, ...); #define klist(K_, n_, ...) (klist_g(K_, true, n_, __VA_ARGS__)) #define kimm_list(K_, n_, ...) (klist_g(K_, false, n_, __VA_ARGS__)) -#define kget_source_info(p_) (tv2pair(p_)->si) -#define kset_source_info(p_, si_) (kget_source_info(p_) = (si_)) +/* TODO use a source info table */ +#define kget_source_info(p_) (UNUSED(p_), KNIL) +#define kset_source_info(K_, p_, si_) (UNUSED(K_), UNUSED(p_), UNUSED(si_)) bool kpairp(TValue obj); diff --git a/src/kport.c b/src/kport.c @@ -48,8 +48,6 @@ TValue kmake_std_port(klisp_State *K, TValue filename, bool writep, writep? K_FLAG_OUTPUT_PORT : K_FLAG_INPUT_PORT); /* port specific fields */ - new_port->name = name; - new_port->si = si; new_port->filename = filename; new_port->file = file; diff --git a/src/kpromise.c b/src/kpromise.c @@ -20,8 +20,6 @@ TValue kmake_promise(klisp_State *K, TValue exp, TValue maybe_env) klispC_link(K, (GCObject *) new_prom, K_TPROMISE, 0); /* promise specific fields */ - new_prom->name = KNIL; - new_prom->si = KNIL; new_prom->node = KNIL; /* temp in case of GC */ krooted_tvs_push(K, gc2prom(new_prom)); new_prom->node = kcons(K, exp, maybe_env); diff --git a/src/kread.c b/src/kread.c @@ -4,15 +4,6 @@ ** See Copyright Notice in klisp.h */ - -/* -** TODO: -** -** - Read mutable/immutable objects (cons function should be a parameter) -** this is needed because some functions (like load) return immutable objs -** -*/ - #include <stdio.h> #include <stdlib.h> #include <assert.h> @@ -184,7 +175,7 @@ TValue kread_fsm(klisp_State *K) ** in np (later it will be replace by the source info ** of the car of the list */ - kset_source_info(np, ktok_get_source_info(K)); + kset_source_info(K, np, ktok_get_source_info(K)); /* update the shared def to point to the new list */ /* NOTE: this is necessary for self referencing lists */ @@ -400,7 +391,7 @@ TValue kread_fsm(klisp_State *K) /* GC: the way things are done here fp is rooted at all times */ TValue fp_old_si = kget_source_info(fp); - kset_source_info(fp, obj_si); + kset_source_info(K, fp, obj_si); kset_car(fp, obj); /* continue reading objects of list */ @@ -420,7 +411,7 @@ TValue kread_fsm(klisp_State *K) /* GC: np is rooted by push_data */ TValue np = kcons_g(K, K->read_mconsp, obj, KNIL); krooted_tvs_push(K, np); - kset_source_info(np, obj_si); + kset_source_info(K, np, obj_si); kset_cdr(get_data(K), np); /* replace last pair of the (still incomplete) read next obj */ pop_data(K); diff --git a/src/kstate.c b/src/kstate.c @@ -17,6 +17,7 @@ #include <setjmp.h> #include "klisp.h" +#include "klimits.h" #include "kstate.h" #include "kobject.h" #include "kstring.h" @@ -59,8 +60,6 @@ klisp_State *klisp_newstate (klisp_Alloc f, void *ud) { K = (klisp_State *) k; - K->symbol_table = KNIL; - /* TODO: create a continuation */ K->curr_cont = KNIL; K->next_obj = KINERT; @@ -94,6 +93,7 @@ klisp_State *klisp_newstate (klisp_Alloc f, void *ud) { /* GC */ K->currentwhite = bit2mask(WHITE0BIT, FIXEDBIT); K->gcstate = GCSpause; + K->sweepstrgc = 0; K->rootgc = NULL; K->sweepgc = &(K->rootgc); K->gray = NULL; @@ -124,9 +124,16 @@ klisp_State *klisp_newstate (klisp_Alloc f, void *ud) { K->dummy_pair3 = kcons(K, KINERT, KNIL); /* initialize strings */ + + /* initial size of string/symbol table */ + K->strt.size = 0; + K->strt.nuse = 0; + K->strt.hash = NULL; + klispS_resize(K, MINSTRTABSIZE); + /* Empty string */ - /* TODO: make it uncollectible */ - K->empty_string = kstring_new_empty(K); + /* MAYBE: fix it so we can remove empty_string from roots */ + K->empty_string = kstring_new_b_imm(K, ""); /* initialize tokenizer */ @@ -161,9 +168,9 @@ klisp_State *klisp_newstate (klisp_Alloc f, void *ud) { K->sbuf = (TValue *)s; /* the dynamic ports and the keys for the dynamic ports */ - TValue in_port = kmake_std_port(K, kstring_new_ns(K, "*STDIN*"), + TValue in_port = kmake_std_port(K, kstring_new_b_imm(K, "*STDIN*"), false, KNIL, KNIL, stdin); - TValue out_port = kmake_std_port(K, kstring_new_ns(K, "*STDOUT*"), + TValue out_port = kmake_std_port(K, kstring_new_b_imm(K, "*STDOUT*"), true, KNIL, KNIL, stdout); K->kd_in_port_key = kcons(K, KTRUE, in_port); K->kd_out_port_key = kcons(K, KTRUE, out_port); @@ -171,7 +178,10 @@ klisp_State *klisp_newstate (klisp_Alloc f, void *ud) { /* create the ground environment and the eval operative */ K->eval_op = kmake_operative(K, keval_ofn, 0); K->list_app = kmake_applicative(K, list, 0); - K->ground_env = kmake_empty_environment(K); + /* ground environment has a hashtable for bindings */ + K->ground_env = kmake_table_environment(K, KNIL); + + /* MAYBE: fix it so we can remove module_params_sym from roots */ K->module_params_sym = ksymbol_new(K, "module-parameters"); kinit_ground_env(K); @@ -495,6 +505,12 @@ void klisp_close (klisp_State *K) klispM_freemem(K, ks_sbuf(K), ks_ssize(K) * sizeof(TValue)); klispM_freemem(K, ks_tbuf(K), ks_tbsize(K)); + /* there should be no pending strings */ + klisp_assert(K->strt.nuse == 0); + + /* free string/symbol table */ + klispM_freearray(K, K->strt.hash, K->strt.size, GCObject *); + /* only remaining mem should be of the state struct */ klisp_assert(K->totalbytes == state_size()); diff --git a/src/kstate.h b/src/kstate.h @@ -34,12 +34,19 @@ typedef struct { int32_t saved_col; } ksource_info_t; +/* in klisp this has both the immutable strings & the symbols */ +typedef struct stringtable { + GCObject **hash; + uint32_t nuse; /* number of elements */ + int32_t size; +} stringtable; + #define GC_PROTECT_SIZE 32 /* NOTE: when adding TValues here, remember to add them to markroot in kgc.c!! */ struct klisp_State { - TValue symbol_table; + stringtable strt; /* hash table for immutable strings & symbols */ TValue curr_cont; /* @@ -71,6 +78,7 @@ struct klisp_State { uint16_t currentwhite; /* the one of the two whites that is in use in this collection cycle */ uint8_t gcstate; /* state of garbage collector */ + int32_t sweepstrgc; /* position of sweep in `strt' */ GCObject *rootgc; /* list of all collectable objects */ GCObject **sweepgc; /* position of sweep in `rootgc' */ GCObject *gray; /* list of gray objects */ diff --git a/src/kstring.c b/src/kstring.c @@ -4,6 +4,8 @@ ** See Copyright Notice in klisp.h */ +/* SOURCE NOTE: the string table & hashing code is from lua */ + #include <string.h> #include <stdbool.h> @@ -13,26 +15,142 @@ #include "kmem.h" #include "kgc.h" -/* TEMP: this is for initializing the above value, for now, from ktoken.h */ -TValue kstring_new_empty(klisp_State *K) +/* for immutable string/symbols table */ +void klispS_resize (klisp_State *K, int32_t newsize) +{ + GCObject **newhash; + stringtable *tb; + int32_t i; + if (K->gcstate == GCSsweepstring) + return; /* cannot resize during GC traverse */ + newhash = klispM_newvector(K, newsize, GCObject *); + tb = &K->strt; + for (i = 0; i < newsize; i++) newhash[i] = NULL; + /* rehash */ + for (i = 0; i < tb->size; i++) { + GCObject *p = tb->hash[i]; + while (p) { /* for each node in the list */ + /* imm string & symbols aren't chained with all other + objs, but with each other in strt */ + GCObject *next = p->gch.next; /* save next */ + + uint32_t h = 0; + + if (p->gch.tt == K_TSYMBOL) { + h = ((Symbol *) p)->hash; + } else if (p->gch.tt == K_TSTRING) { + h = ((String *) p)->hash; + } else { + klisp_assert(0); + } + + int32_t h1 = lmod(h, newsize); /* new position */ + klisp_assert((int32_t) (h%newsize) == lmod(h, newsize)); + p->gch.next = newhash[h1]; /* chain it */ + newhash[h1] = p; + p = next; + } + } + klispM_freearray(K, tb->hash, tb->size, GCObject *); + tb->size = newsize; + tb->hash = newhash; +} + +/* General constructor for strings */ +TValue kstring_new_bs_g(klisp_State *K, bool m, const char *buf, + uint32_t size) { + return m? kstring_new_bs(K, buf, size) : + kstring_new_bs_imm(K, buf, size); +} + +/* +** Constructors for immutable strings +*/ + +/* main constructor for immutable strings */ +TValue kstring_new_bs_imm(klisp_State *K, const char *buf, uint32_t size) +{ + /* first check to see if it's in the stringtable */ + GCObject *o; + uint32_t h = size; /* seed */ + size_t step = (size>>5)+1; /* if string is too long, don't hash all + its chars */ + size_t size1; + for (size1 = size; size1 >= step; size1 -= step) /* compute hash */ + h = h ^ ((h<<5)+(h>>2)+ ((unsigned char) buf[size1-1])); + + for (o = K->strt.hash[lmod(h, K->strt.size)]; + o != NULL; o = o->gch.next) { + String *ts = NULL; + if (o->gch.tt == K_TSTRING) { + ts = (String *) o; + } else if (o->gch.tt == K_TSYMBOL) { + continue; + } else { + klisp_assert(0); /* only symbols and immutable strings */ + } + if (ts->size == size && (memcmp(buf, ts->b, size) == 0)) { + /* string may be dead */ + if (isdead(K, o)) changewhite(o); + return gc2str(o); + } + } + + /* If it exits the loop, it means it wasn't found, hash is still in h */ + /* REFACTOR: move all of these to a new function */ String *new_str; - new_str = klispM_malloc(K, sizeof(String) + 1); + if (size+1 > (SIZE_MAX - sizeof(String))) + klispM_toobig(K); - /* header + gc_fields */ - klispC_link(K, (GCObject *) new_str, K_TSTRING, 0); + new_str = (String *) klispM_malloc(K, sizeof(String) + size + 1); + /* header + gc_fields */ + /* can't use klispC_link, because strings use the next pointer + differently */ + new_str->gct = klispC_white(K); + new_str->tt = K_TSTRING; + new_str->kflags = K_FLAG_IMMUTABLE; /* string specific fields */ + new_str->hash = h; new_str->mark = KFALSE; - new_str->size = 0; - new_str->b[0] = '\0'; + new_str->size = size; + if (size != 0) { + memcpy(new_str->b, buf, size); + } + new_str->b[size] = '\0'; /* final 0 for printing */ + + /* add to the string/symbol table (and link it) */ + stringtable *tb; + tb = &K->strt; + h = lmod(h, tb->size); + new_str->next = tb->hash[h]; /* chain new entry */ + tb->hash[h] = (GCObject *)(new_str); + tb->nuse++; + TValue ret_tv = gc2str(new_str); + if (tb->nuse > ((uint32_t) tb->size) && tb->size <= INT32_MAX / 2) { + krooted_tvs_push(K, ret_tv); /* save in case of gc */ + klispS_resize(K, tb->size*2); /* too crowded */ + krooted_tvs_pop(K); + } + + return ret_tv; +} - return gc2str(new_str); +/* with just buffer, no embedded '\0's */ +TValue kstring_new_b_imm(klisp_State *K, const char *buf) +{ + return (kstring_new_bs_imm(K, buf, strlen(buf))); } -/* TEMP: for now all strings are mutable */ -TValue kstring_new_g(klisp_State *K, uint32_t size) +/* +** Constructors for mutable strings +*/ + +/* main constructor for mutable strings */ +/* with just size */ +TValue kstring_new_s(klisp_State *K, uint32_t size) { String *new_str; @@ -46,6 +164,7 @@ TValue kstring_new_g(klisp_State *K, uint32_t size) klispC_link(K, (GCObject *) new_str, K_TSTRING, 0); /* string specific fields */ + new_str->hash = 0; /* unimportant for mutable strings */ new_str->mark = KFALSE; new_str->size = size; @@ -58,34 +177,39 @@ TValue kstring_new_g(klisp_State *K, uint32_t size) return gc2str(new_str); } -TValue kstring_new(klisp_State *K, const char *buf, uint32_t size) +/* with buffer & size */ +TValue kstring_new_bs(klisp_State *K, const char *buf, uint32_t size) { - TValue new_str = kstring_new_g(K, size); + TValue new_str = kstring_new_s(K, size); memcpy(kstring_buf(new_str), buf, size); return new_str; } -/* with no size, no embedded '\0's */ -TValue kstring_new_ns(klisp_State *K, const char *buf) +/* with buffer but no size, no embedded '\0's */ +TValue kstring_new_b(klisp_State *K, const char *buf) { - return (kstring_new(K, buf, strlen(buf))); + return (kstring_new_bs(K, buf, strlen(buf))); } -TValue kstring_new_sc(klisp_State *K, uint32_t size, char fill) +/* with size and fill char */ +TValue kstring_new_sf(klisp_State *K, uint32_t size, char fill) { - TValue new_str = kstring_new_g(K, size); + TValue new_str = kstring_new_s(K, size); memset(kstring_buf(new_str), fill, size); return new_str; } -/* both obj1 and obj2 should be strings! */ +/* both obj1 and obj2 should be strings */ bool kstring_equalp(TValue obj1, TValue obj2) { + klisp_assert(ttisstring(obj1) && ttisstring(obj2)); + String *str1 = tv2str(obj1); String *str2 = tv2str(obj2); if (str1->size == str2->size) { - return (memcmp(str1->b, str2->b, str1->size) == 0); + return (str1->size == 0) || + (memcmp(str1->b, str2->b, str1->size) == 0); } else { return false; } diff --git a/src/kstring.h b/src/kstring.h @@ -4,6 +4,8 @@ ** See Copyright Notice in klisp.h */ +/* SOURCE NOTE: the string table & hashing code is from lua */ + #ifndef kstring_h #define kstring_h @@ -12,21 +14,68 @@ #include "kobject.h" #include "kstate.h" -/* TEMP: for now all strings are mutable */ +/* for immutable string table */ +void klispS_resize (klisp_State *K, int32_t newsize); + +/* +** Constructors for immutable strings +*/ + +/* General constructor for strings */ +TValue kstring_new_bs_g(klisp_State *K, bool m, const char *buf, + uint32_t size); + +/* main immutable string constructor */ +/* with buffer & size */ +TValue kstring_new_bs_imm(klisp_State *K, const char *buf, uint32_t size); -TValue kstring_new_empty(klisp_State *K); -TValue kstring_new(klisp_State *K, const char *buf, uint32_t size); -/* with no size, no embedded '\0's */ -TValue kstring_new_ns(klisp_State *K, const char *buf); -TValue kstring_new_g(klisp_State *K, uint32_t size); -TValue kstring_new_sc(klisp_State *K, uint32_t size, char fill); +/* with just buffer, no embedded '\0's */ +TValue kstring_new_b_imm(klisp_State *K, const char *buf); +/* +** Constructors for mutable strings +*/ + +/* main mutable string constructor */ +/* with just size */ +TValue kstring_new_s(klisp_State *K, uint32_t size); +/* with buffer & size */ +TValue kstring_new_bs(klisp_State *K, const char *buf, uint32_t size); +/* with just buffer, no embedded '\0's */ +TValue kstring_new_b(klisp_State *K, const char *buf); +/* with size & fill char */ +TValue kstring_new_sf(klisp_State *K, uint32_t size, char fill); + +/* macros for mutable & immutable versions of the above */ +#if 0 +#define kstring_new_s(K_, size_) \ + kstring_new_s_g(K_, true, size_) +#define kstring_new_bs(K_, buf_, size_) \ + kstring_new_bs_g(K_, true, buf_, size_) +#define kstring_new_b(K_, buf_) \ + kstring_new_b_g(K_, true, buf_) +#define kstring_new_sf(K_, size_, fill_) \ + kstring_new_sf_g(K_, true, size_, fill_) + +#define kstring_new_s_imm(K_, size_) \ + kstring_new_s_g(K_, false, size_) +#define kstring_new_bs_imm(K_, buf_, size_) \ + kstring_new_bs_g(K_, false, buf_, size_) +#define kstring_new_b_imm(K_, buf_) \ + kstring_new_b_g(K_, false, buf_) +#define kstring_new_sf_imm(K_, size_, fill_) \ + kstring_new_sf_g(K_, false, size_, fill_) +#endif +/* some macros to access the parts of the string */ #define kstring_buf(tv_) (tv2str(tv_)->b) #define kstring_size(tv_) (tv2str(tv_)->size) -#define kstring_is_empty(tv_) (kstring_size(tv_) == 0) +#define kstring_emptyp(tv_) (kstring_size(tv_) == 0) +#define kstring_mutablep(tv_) (kis_mutable(tv_)) +#define kstring_immutablep(tv_) (kis_immutable(tv_)) -/* both obj1 and obj2 should be strings! */ +/* both obj1 and obj2 should be strings, this compares char by char + but differentiates immutable from mutable strings */ bool kstring_equalp(TValue obj1, TValue obj2); #endif diff --git a/src/ksymbol.c b/src/ksymbol.c @@ -18,42 +18,69 @@ TValue ksymbol_new_g(klisp_State *K, const char *buf, int32_t size, bool identifierp) { - /* TODO: replace symbol list with hashtable */ /* First look for it in the symbol table */ - TValue tbl = K->symbol_table; + GCObject *o; + uint32_t h = size; /* seed */ + size_t step = (size>>5)+1; /* if string is too long, don't hash all + its chars */ + size_t size1; + for (size1 = size; size1 >= step; size1 -= step) /* compute hash */ + h = h ^ ((h<<5)+(h>>2)+ ((unsigned char) buf[size1-1])); - while (!ttisnil(tbl)) { - TValue first = kcar(tbl); - /* NOTE: there are no embedded '\0's in identifiers but - they could be in other symbols */ - if (size == ksymbol_size(first) && - memcmp(buf, ksymbol_buf(first), size) == 0) { - return first; - } else - tbl = kcdr(tbl); - } + h = ~h; /* symbol hash should be different from string hash + otherwise symbols and their respective immutable string + would always fall in the same bucket */ - /* Didn't find it, alloc new string and save in symbol table */ - /* NOTE: there are no embedded '\0's in symbols */ - /* GC: root new_str */ - TValue new_str = kstring_new(K, buf, size); /* this copies the buf */ + for (o = K->strt.hash[lmod(h, K->strt.size)]; + o != NULL; o = o->gch.next) { + String *ts = NULL; + if (o->gch.tt == K_TSTRING) { + continue; + } else if (o->gch.tt == K_TSYMBOL) { + ts = tv2str(((Symbol *) o)->str); + } else { + klisp_assert(0); /* only symbols and immutable strings */ + } + if (ts->size == size && (memcmp(buf, ts->b, size) == 0)) { + /* symbol may be dead */ + if (isdead(K, o)) changewhite(o); + return gc2sym(o); + } + } + /* REFACTOR: move this to a new function */ + /* Didn't find it, alloc new immutable string and save in symbol table, + note that the hash value remained in h */ + TValue new_str = kstring_new_bs_imm(K, buf, size); krooted_tvs_push(K, new_str); Symbol *new_sym = klispM_new(K, Symbol); krooted_tvs_pop(K); /* header + gc_fields */ - klispC_link(K, (GCObject *) new_sym, K_TSYMBOL, - identifierp? K_FLAG_EXT_REP : 0); + /* can't use klispC_link, because strings use the next pointer + differently */ + new_sym->gct = klispC_white(K); + new_sym->tt = K_TSYMBOL; + new_sym->kflags = identifierp? K_FLAG_EXT_REP : 0; /* symbol specific fields */ new_sym->mark = KFALSE; new_sym->str = new_str; + new_sym->hash = h; - TValue new_symv = gc2sym(new_sym); - krooted_tvs_push(K, new_symv); - K->symbol_table = kcons(K, new_symv, K->symbol_table); - krooted_tvs_pop(K); - return new_symv; + /* add to the string/symbol table (and link it) */ + stringtable *tb; + tb = &K->strt; + h = lmod(h, tb->size); + new_sym->next = tb->hash[h]; /* chain new entry */ + tb->hash[h] = (GCObject *)(new_sym); + tb->nuse++; + TValue ret_tv = gc2sym(new_sym); + if (tb->nuse > ((uint32_t) tb->size) && tb->size <= INT32_MAX / 2) { + krooted_tvs_push(K, ret_tv); /* save in case of gc */ + klispS_resize(K, tb->size*2); /* too crowded */ + krooted_tvs_pop(K); + } + return ret_tv; } /* for indentifiers */ diff --git a/src/ksymbol.h b/src/ksymbol.h @@ -17,9 +17,10 @@ TValue ksymbol_new_i(klisp_State *K, const char *buf, int32_t size); /* For identifiers, simplified for unknown size */ TValue ksymbol_new(klisp_State *K, const char *buf); -/* For general strings */ +/* For general strings, copies str if not immutable */ TValue ksymbol_new_check_i(klisp_State *K, TValue str); +#define ksymbol_str(tv_) (tv2sym(tv_)->str) #define ksymbol_buf(tv_) (kstring_buf(tv2sym(tv_)->str)) #define ksymbol_size(tv_) (kstring_size(tv2sym(tv_)->str)) diff --git a/src/ktable.c b/src/ktable.c @@ -0,0 +1,641 @@ +/* +** ktable.c +** Kernel Hashtables +** See Copyright Notice in klisp.h +*/ + +/* +** SOURCE NOTE: This is almost textually from lua. +** Parts that don't apply, or don't apply yet to klisp are in comments. +** In klisp arrays are indexed from 0, (while in Lua they are indexed from +** one). So watch out for off by one errors! Andres Navarro +** To indicate a missing entry, klisp uses 'free' instead of 'nil'. +** 'free' is a special type that is unavailable to Kernel programs. +*/ + +/* +** Implementation of tables (aka arrays, objects, or hash tables). +** Tables keep its elements in two parts: an array part and a hash part. +** Non-negative integer keys are all candidates to be kept in the array +** part. The actual size of the array is the largest `n' such that at +** least half the slots between 0 and n are in use. +** Hash uses a mix of chained scatter table with Brent's variation. +** A main invariant of these tables is that, if an element is not +** in its main position (i.e. the `original' position that its hash gives +** to it), then the colliding element is in its own main position. +** Hence even when the load factor reaches 100%, performance remains good. +*/ + +#include <math.h> +#include <string.h> + +#include "klisp.h" +#include "kgc.h" +#include "kmem.h" +#include "kobject.h" +#include "kstate.h" +#include "ktable.h" +#include "kapplicative.h" +#include "kgeqp.h" +#include "kstring.h" + +/* +** max size of array part is 2^MAXBITS +*/ +#define MAXBITS 26 +#define MAXASIZE (1 << MAXBITS) + + +#define hashpow2(t,n) (gnode(t, lmod((n), sizenode(t)))) + +#define hashstr(t,str) hashpow2(t, (str)->hash) +#define hashsym(t,sym) hashpow2(t, (sym)->hash) +#define hashboolean(t,p) hashpow2(t, p? 1 : 0) + + +/* +** for some types, it is better to avoid modulus by power of 2, as +** they tend to have many 2 factors. +*/ +#define hashmod(t,n) (gnode(t, ((n) % ((sizenode(t)-1)|1)))) + + +#define hashpointer(t,p) hashmod(t, IntPoint(p)) + +#define dummynode (&dummynode_) + +static const Node dummynode_ = { + .i_val = KFREE_, + .i_key = { .nk = { .this = KFREE_, .next = NULL}} +}; + + +/* +** hash for klisp numbers +*/ +inline static Node *hashfixint (const Table *t, int32_t n) { + return hashmod(t, (uint32_t) n); +} + +/* XXX: this accesses the internal representation of bigints... + maybe it should be in kbigint.c. + This may also not be the best hashing for bigints, I just + made it up... +*/ +static Node *hashbigint (const Table *t, Bigint *b) { + uint32_t n = (b->sign == 0)? 0 : 1; + for (uint32_t i = 0; i < b->used; i++) + n += b->digits[i]; + + return hashmod(t, n); +} + +/* +** returns the `main' position of an element in a table (that is, the index +** of its hash value) +*/ +static Node *mainposition (const Table *t, TValue key) { + switch (ttype(key)) { + case K_TNIL: + case K_TIGNORE: + case K_TINERT: + case K_TEOF: + case K_TFIXINT: + case K_TEINF: /* infinites have -1 or 1 as ivalues */ + return hashfixint(t, ivalue(key)); + case K_TCHAR: + return hashfixint(t, chvalue(key)); + case K_TBIGINT: + return hashbigint(t, tv2bigint(key)); + case K_TBOOLEAN: + return hashboolean(t, bvalue(key)); + case K_TSTRING: + if (kstring_immutablep(key)) + return hashstr(t, tv2str(key)); + else /* mutable strings are eq iff they are the same object */ + return hashpointer(t, gcvalue(key)); + case K_TSYMBOL: + return hashsym(t, tv2sym(key)); + case K_TUSER: + return hashpointer(t, pvalue(key)); + case K_TAPPLICATIVE: + /* applicatives are eq if wrapping the same number of times the + same applicative, just in case make the hash of an applicative + the same as the hash of the operative is ultimately wraps */ + while(ttisapplicative(key)) { + key = kunwrap(key); + } + /* fall through */ + default: + return hashpointer(t, gcvalue(key)); + } +} + + +/* +** returns the index for `key' if `key' is an appropriate key to live in +** the array part of the table, -1 otherwise. +*/ +static int32_t arrayindex (const TValue key) { + return (ttisfixint(key) && ivalue(key) >= 0)? ivalue(key) : -1; +} + + +/* +** returns the index of a `key' for table traversals. First goes all +** elements in the array part, then elements in the hash part. The +** beginning of a traversal is signalled by -1. +*/ +static int32_t findindex (klisp_State *K, Table *t, TValue key) +{ + int32_t i; + if (ttisfree(key)) return -1; /* first iteration */ + i = arrayindex(key); + if (0 <= i && i < t->sizearray) /* is `key' inside array part? */ + return i; /* yes; that's the index */ + else { + Node *n = mainposition(t, key); + do { /* check whether `key' is somewhere in the chain */ + /* key may be dead already, but it is ok to use it in `next' */ +/* klisp: i'm not so sure about this... */ + if (eq2p(K, key2tval(n), key) || + (ttype(gkey(n)->this) == K_TDEADKEY && iscollectable(key) && + gcvalue(gkey(n)->this) == gcvalue(key))) { + i = (int32_t) (n - gnode(t, 0)); /* key index in hash table */ + /* hash elements are numbered after array ones */ + return i + t->sizearray; + } + else n = gnext(n); + } while (n); + klispE_throw(K, "invalid key to next"); /* key not found */ + return 0; /* to avoid warnings */ + } +} + +int32_t klispH_next (klisp_State *K, Table *t, TValue *key, TValue *data) +{ + int32_t i = findindex(K, t, *key); /* find original element */ + for (i++; i < t->sizearray; i++) { /* try first array part */ + if (!ttisfree(t->array[i])) { /* a non-nil value? */ + *key = i2tv(i); + *data = t->array[i]; + return 1; + } + } + for (i -= t->sizearray; i < sizenode(t); i++) { /* then hash part */ + if (!ttisfree(gval(gnode(t, i)))) { /* a non-nil value? */ + *key = key2tval(gnode(t, i)); + *data = gval(gnode(t, i)); + return 1; + } + } + return 0; /* no more elements */ +} + + +/* +** {============================================================= +** Rehash +** ============================================================== +*/ + + +static int32_t computesizes (int32_t nums[], int32_t *narray) +{ + int32_t i; + int32_t twotoi; /* 2^i */ + int32_t a = 0; /* number of elements smaller than 2^i */ + int32_t na = 0; /* number of elements to go to array part */ + int32_t n = 0; /* optimal size for array part */ + for (i = 0, twotoi = 1; twotoi/2 < *narray; i++, twotoi *= 2) { + if (nums[i] > 0) { + a += nums[i]; + if (a > twotoi/2) { /* more than half elements present? */ + n = twotoi; /* optimal size (till now) */ + na = a; /* all elements smaller than n will go to array part */ + } + } + if (a == *narray) break; /* all elements already counted */ + } + *narray = n; + klisp_assert(*narray/2 <= na && na <= *narray); + return na; +} + + +static int32_t countint (const TValue key, int32_t *nums) +{ + int32_t k = arrayindex(key); + if (0 < k && k <= MAXASIZE) { /* is `key' an appropriate array index? */ + nums[ceillog2(k)]++; /* count as such */ + return 1; + } + else + return 0; +} + + +static int32_t numusearray (const Table *t, int32_t *nums) +{ + int32_t lg; + int32_t ttlg; /* 2^lg */ + int32_t ause = 0; /* summation of `nums' */ + int32_t i = 1; /* count to traverse all array keys */ + for (lg=0, ttlg=1; lg<=MAXBITS; lg++, ttlg*=2) { /* for each slice */ + int32_t lc = 0; /* counter */ + int32_t lim = ttlg; + if (lim > t->sizearray) { + lim = t->sizearray; /* adjust upper limit */ + if (i > lim) + break; /* no more elements to count */ + } + /* count elements in range (2^(lg-1), 2^lg] */ + for (; i <= lim; i++) { + if (!ttisfree(t->array[i-1])) + lc++; + } + nums[lg] += lc; + ause += lc; + } + return ause; +} + + +static int32_t numusehash (const Table *t, int32_t *nums, int32_t *pnasize) +{ + int32_t totaluse = 0; /* total number of elements */ + int32_t ause = 0; /* summation of `nums' */ + int32_t i = sizenode(t); + while (i--) { + Node *n = &t->node[i]; + if (!ttisfree(gval(n))) { + ause += countint(key2tval(n), nums); + totaluse++; + } + } + *pnasize += ause; + return totaluse; +} + + +static void setarrayvector (klisp_State *K, Table *t, int32_t size) +{ + int32_t i; + klispM_reallocvector(K, t->array, t->sizearray, size, TValue); + for (i=t->sizearray; i<size; i++) + t->array[i] = KFREE; + t->sizearray = size; +} + + +static void setnodevector (klisp_State *K, Table *t, int32_t size) +{ + int32_t lsize; + if (size == 0) { /* no elements to hash part? */ + t->node = cast(Node *, dummynode); /* use common `dummynode' */ + lsize = 0; + } + else { + int32_t i; + lsize = ceillog2(size); + if (lsize > MAXBITS) + klispE_throw(K, "table overflow"); + size = twoto(lsize); + t->node = klispM_newvector(K, size, Node); + for (i=0; i<size; i++) { + Node *n = gnode(t, i); + gnext(n) = NULL; + gkey(n)->this = KFREE; + gval(n) = KFREE; + } + } + t->lsizenode = (uint8_t) (lsize); + t->lastfree = gnode(t, size); /* all positions are free */ +} + + +static void resize (klisp_State *K, Table *t, int32_t nasize, int32_t nhsize) +{ + int32_t i; + int32_t oldasize = t->sizearray; + int32_t oldhsize = t->lsizenode; + Node *nold = t->node; /* save old hash ... */ + if (nasize > oldasize) /* array part must grow? */ + setarrayvector(K, t, nasize); + /* create new hash part with appropriate size */ + setnodevector(K, t, nhsize); + if (nasize < oldasize) { /* array part must shrink? */ + t->sizearray = nasize; + /* re-insert elements from vanishing slice */ + for (i=nasize; i<oldasize; i++) { + if (!ttisfree(t->array[i])) { + TValue v = t->array[i]; + *klispH_setfixint(K, t, i) = v; + checkliveness(K, v); + } + } + /* shrink array */ + klispM_reallocvector(K, t->array, oldasize, nasize, TValue); + } + /* re-insert elements from hash part */ + for (i = twoto(oldhsize) - 1; i >= 0; i--) { + Node *old = nold+i; + if (!ttisfree(gval(old))) { + TValue v = gval(old); + *klispH_set(K, t, key2tval(old)) = v; + checkliveness(K, v); + } + } + if (nold != dummynode) + klispM_freearray(K, nold, twoto(oldhsize), Node); /* free old array */ +} + + +void klispH_resizearray (klisp_State *K, Table *t, int32_t nasize) +{ + int32_t nsize = (t->node == dummynode) ? 0 : sizenode(t); + resize(K, t, nasize, nsize); +} + + +static void rehash (klisp_State *K, Table *t, const TValue ek) { + int32_t nasize, na; + int32_t nums[MAXBITS+1]; /* nums[i] = number of keys between 2^(i-1) and 2^i */ + int32_t i; + int32_t totaluse; + for (i=0; i<=MAXBITS; i++) nums[i] = 0; /* reset counts */ + nasize = numusearray(t, nums); /* count keys in array part */ + totaluse = nasize; /* all those keys are integer keys */ + totaluse += numusehash(t, nums, &nasize); /* count keys in hash part */ + /* count extra key */ + nasize += countint(ek, nums); + totaluse++; + /* compute new size for array part */ + na = computesizes(nums, &nasize); + /* resize the table to new computed sizes */ + resize(K, t, nasize, totaluse - na); +} + + + +/* +** }============================================================= +*/ + +/* wflags should be either or both of K_FLAG_WEAK_KEYS or K_FLAG_WEAK VALUES */ +TValue klispH_new (klisp_State *K, int32_t narray, int32_t nhash, + int32_t wflags) +{ + klisp_assert((wflags & (K_FLAG_WEAK_KEYS | K_FLAG_WEAK_VALUES)) == + wflags); + Table *t = klispM_new(K, Table); + klispC_link(K, (GCObject *) t, K_TTABLE, wflags); + /* temporary values (kept only if some malloc fails) */ + t->array = NULL; + t->sizearray = 0; + t->lsizenode = 0; + t->node = cast(Node *, dummynode); + /* root in case gc is run while allocating array or nodes */ + TValue tv_t = gc2table(t); + krooted_tvs_push(K, tv_t); + + setarrayvector(K, t, narray); + setnodevector(K, t, nhash); + krooted_tvs_pop(K); + return tv_t; +} + + +void klispH_free (klisp_State *K, Table *t) +{ + if (t->node != dummynode) + klispM_freearray(K, t->node, sizenode(t), Node); + klispM_freearray(K, t->array, t->sizearray, TValue); + klispM_free(K, t); +} + + +static Node *getfreepos (Table *t) +{ + while (t->lastfree-- > t->node) { + if (ttisfree(gkey(t->lastfree)->this)) + return t->lastfree; + } + return NULL; /* could not find a free place */ +} + + +/* +** inserts a new key into a hash table; first, check whether key's main +** position is free. If not, check whether colliding node is in its main +** position or not: if it is not, move colliding node to an empty place and +** put new key in its main position; otherwise (colliding node is in its main +** position), new key goes to an empty position. +*/ +static TValue *newkey (klisp_State *K, Table *t, TValue key) +{ + Node *mp = mainposition(t, key); + if (!ttisfree(gval(mp)) || mp == dummynode) { + Node *othern; + Node *n = getfreepos(t); /* get a free place */ + if (n == NULL) { /* cannot find a free place? */ + rehash(K, t, key); /* grow table */ + return klispH_set(K, t, key); /* re-insert key into grown table */ + } + klisp_assert(n != dummynode); + othern = mainposition(t, key2tval(mp)); + if (othern != mp) { /* is colliding node out of its main position? */ + /* yes; move colliding node into free position */ + while (gnext(othern) != mp) othern = gnext(othern); /* find previous */ + gnext(othern) = n; /* redo the chain with `n' in place of `mp' */ + *n = *mp; /* copy colliding node into free pos. (mp->next also goes) */ + gnext(mp) = NULL; /* now `mp' is free */ + gval(mp) = KFREE; + } else { /* colliding node is in its own main position */ + /* new node will go into free position */ + gnext(n) = gnext(mp); /* chain new position */ + gnext(mp) = n; + mp = n; + } + } + gkey(mp)->this = key; + klispC_barriert(K, t, key); + klisp_assert(ttisfree(gval(mp))); + return &gval(mp); +} + + +/* +** search function for integers +*/ +const TValue *klispH_getfixint (Table *t, int32_t key) +{ + if (key >= 0 && key < t->sizearray) + return &t->array[key]; + else { + Node *n = hashfixint(t, key); + do { /* check whether `key' is somewhere in the chain */ + if (ttisfixint(gkey(n)->this) && ivalue(gkey(n)->this) == key) + return &gval(n); /* that's it */ + else n = gnext(n); + } while (n); + return &kfree; + } +} + + +/* +** search function for immutable strings +*/ +const TValue *klispH_getstr (Table *t, String *key) { + klisp_assert(kstring_immutablep(gc2str(key))); + Node *n = hashstr(t, key); + do { /* check whether `key' is somewhere in the chain */ + if (ttisstring(gkey(n)->this) && tv2str(gkey(n)->this) == key) + return &gval(n); /* that's it */ + else n = gnext(n); + } while (n); + return &kfree; +} + +/* +** search function for symbol +*/ +const TValue *klispH_getsym (Table *t, Symbol *key) { + Node *n = hashsym(t, key); + do { /* check whether `key' is somewhere in the chain */ + if (ttissymbol(gkey(n)->this) && tv2sym(gkey(n)->this) == key) + return &gval(n); /* that's it */ + else n = gnext(n); + } while (n); + return &kfree; +} + + +/* +** main search function +*/ +const TValue *klispH_get (Table *t, TValue key) +{ + switch (ttype(key)) { + case K_TFREE: return &kfree; + case K_TSYMBOL: return klispH_getsym(t, tv2sym(key)); + case K_TFIXINT: return klispH_getfixint(t, ivalue(key)); + case K_TSTRING: + if (kstring_immutablep(key)) + return klispH_getstr(t, tv2str(key)); + /* else fall through */ + default: { + Node *n = mainposition(t, key); + do { /* check whether `key' is somewhere in the chain */ + /* XXX: for some reason eq2p takes klisp_State but + doesn't use it */ + if (eq2p((klisp_State *)NULL, key2tval(n), key)) + return &gval(n); /* that's it */ + else n = gnext(n); + } while (n); + return &kfree; + } + } +} + + +TValue *klispH_set (klisp_State *K, Table *t, TValue key) +{ + const TValue *p = klispH_get(t, key); + if (p != &kfree) + return cast(TValue *, p); + else { + if (ttisfree(key)) + klispE_throw(K, "table index is free"); +/* + else if (ttisnumber(key) && luai_numisnan(nvalue(key))) + luaG_runerror(L, "table index is NaN"); +*/ + return newkey(K, t, key); + } +} + + +TValue *klispH_setfixint (klisp_State *K, Table *t, int32_t key) +{ + const TValue *p = klispH_getfixint(t, key); + if (p != &kfree) + return cast(TValue *, p); + else + return newkey(K, t, i2tv(key)); +} + + +TValue *klispH_setstr (klisp_State *K, Table *t, String *key) +{ + klisp_assert(kstring_immutablep(gc2str(key))); + const TValue *p = klispH_getstr(t, key); + if (p != &kfree) + return cast(TValue *, p); + else { + return newkey(K, t, gc2str(key)); + } +} + + +TValue *klispH_setsym (klisp_State *K, Table *t, Symbol *key) +{ + const TValue *p = klispH_getsym(t, key); + if (p != &kfree) + return cast(TValue *, p); + else { + return newkey(K, t, gc2sym(key)); + } +} + + +/* klisp: Untested, may have off by one errors, check before using */ +static int32_t unbound_search (Table *t, int32_t j) { + int32_t i = j; /* i -1 or a present index */ + j++; + /* find `i' and `j' such that i is present and j is not */ + while (!ttisfree(*klispH_getfixint(t, j))) { + i = j; + if (j <= (INT32_MAX - i) / 2) + j *= 2; + else { /* overflow? */ + /* table was built with bad purposes: resort to linear search */ + i = 0; + while (!ttisfree(*klispH_getfixint(t, i))) i++; + return i-1; + } + } + /* now do a binary search between them */ + while (j - i > 1) { + int32_t m = (i+j)/2; + if (ttisfree(*klispH_getfixint(t, m))) j = m; + else i = m; + } + return i; +} + + +/* +** Try to find a boundary in table `t'. A `boundary' is an integer index +** such that t[i] is non-nil and t[i+1] is nil (and 0 if t[1] is nil). +** klisp: in klisp that indexes are from zero, this returns -1 if t[0] is nil +** also klisp uses free instead of nil +*/ +int32_t klispH_getn (Table *t) { + int32_t j = t->sizearray - 1; + if (j >= 0 && ttisfree(t->array[j])) { + /* there is a boundary in the array part: (binary) search for it */ + int32_t i = -1; + while (j - i > 1) { + int32_t m = (i+j)/2; + if (ttisfree(t->array[m])) j = m; + else i = m; + } + return i; + } + /* else must find a boundary in hash part */ + else if (t->node == dummynode) /* hash part is empty? */ + return j; /* that is easy... */ + else return unbound_search(t, j); +} diff --git a/src/ktable.h b/src/ktable.h @@ -0,0 +1,40 @@ +/* +** ktable.h +** Kernel Hashtables +** See Copyright Notice in klisp.h +*/ + +/* +** SOURCE NOTE: This is almost textually from lua. +** Parts that don't apply, or don't apply yet to klisp are in comments. +*/ + +#ifndef ktable_h +#define ktable_h + +#include "kobject.h" +#include "kstate.h" + +#define gnode(t,i) (&(t)->node[i]) +#define gkey(n) (&(n)->i_key.nk) +#define gval(n) ((n)->i_val) +#define gnext(n) ((n)->i_key.nk.next) + +#define key2tval(n) ((n)->i_key.tvk) + +const TValue *klispH_getfixint (Table *t, int32_t key); +TValue *klispH_setfixint (klisp_State *K, Table *t, int32_t key); +const TValue *klispH_getstr (Table *t, String *key); +TValue *klispH_setstr (klisp_State *K, Table *t, String *key); +const TValue *klispH_getsym (Table *t, Symbol *key); +TValue *klispH_setsym (klisp_State *K, Table *t, Symbol *key); +const TValue *klispH_get (Table *t, TValue key); +TValue *klispH_set (klisp_State *K, Table *t, TValue key); +TValue klispH_new (klisp_State *K, int32_t narray, int32_t nhash, + int32_t wflags); +void klispH_resizearray (klisp_State *K, Table *t, int32_t nasize); +void klispH_free (klisp_State *K, Table *t); +int32_t klispH_next (klisp_State *K, Table *t, TValue *key, TValue *data); +int32_t klispH_getn (Table *t); + +#endif diff --git a/src/ktoken.c b/src/ktoken.c @@ -188,8 +188,7 @@ TValue ktok_get_source_info(klisp_State *K) { /* NOTE: the filename doesn't contains embedded '\0's */ TValue filename_str = - kstring_new(K, K->ktok_source_info.saved_filename, - strlen(K->ktok_source_info.saved_filename)); + kstring_new_b_imm(K, K->ktok_source_info.saved_filename); krooted_tvs_push(K, filename_str); /* TEMP: for now, lines and column names are fixints */ TValue res = kcons(K, i2tv(K->ktok_source_info.saved_line), @@ -521,7 +520,10 @@ TValue ktok_read_string(klisp_State *K) i++; } } - TValue new_str = kstring_new(K, ks_tbget_buffer(K), i); + /* TEMP: for now strings "read" are mutable but strings "loaded" are + not */ + TValue new_str = kstring_new_bs_g(K, K->read_mconsp, + ks_tbget_buffer(K), i); krooted_tvs_push(K, new_str); ks_tbclear(K); /* shouldn't cause gc, but still */ krooted_tvs_pop(K); diff --git a/src/kwrite.c b/src/kwrite.c @@ -44,7 +44,7 @@ void kw_print_bigint(klisp_State *K, TValue bigint) ((kbigint_negativep(bigint))? 1 : 0); krooted_tvs_push(K, bigint); - TValue buf_str = kstring_new_g(K, size); + TValue buf_str = kstring_new_s(K, size); krooted_tvs_push(K, buf_str); /* write backwards so we can use printf later */ @@ -338,7 +338,7 @@ void kwrite_fsm(klisp_State *K, TValue obj) break; } case K_TSTRING: { - if (kstring_is_empty(obj)) { + if (kstring_emptyp(obj)) { kw_printf(K, "\"\""); } else { TValue mark = kget_mark(obj);