diff --git a/lapi.c b/lapi.c index 767fa55e..34b335fd 100644 --- a/lapi.c +++ b/lapi.c @@ -637,50 +637,44 @@ LUA_API int lua_pushthread (lua_State *L) { l_sinline int auxgetstr (lua_State *L, const TValue *t, const char *k) { - const TValue *slot; + int hres; TString *str = luaS_new(L, k); - if (luaV_fastget(L, t, str, slot, luaH_getstr)) { - setobj2s(L, L->top.p, slot); + luaV_fastget(t, str, s2v(L->top.p), luaH_getstr, hres); + if (hres == HOK) { api_incr_top(L); } else { setsvalue2s(L, L->top.p, str); api_incr_top(L); - luaV_finishget(L, t, s2v(L->top.p - 1), L->top.p - 1, slot); + luaV_finishget(L, t, s2v(L->top.p - 1), L->top.p - 1, hres); } lua_unlock(L); return ttype(s2v(L->top.p - 1)); } -/* -** Get the global table in the registry. Since all predefined -** indices in the registry were inserted right when the registry -** was created and never removed, they must always be in the array -** part of the registry. -*/ -#define getGtable(L) \ - (&hvalue(&G(L)->l_registry)->array[LUA_RIDX_GLOBALS - 1]) +static void getGlobalTable (lua_State *L, TValue *gt) { + Table *registry = hvalue(&G(L)->l_registry); + luaH_getint(registry, LUA_RIDX_GLOBALS, gt); +} LUA_API int lua_getglobal (lua_State *L, const char *name) { - const TValue *G; + TValue gt; lua_lock(L); - G = getGtable(L); - return auxgetstr(L, G, name); + getGlobalTable(L, >); + return auxgetstr(L, >, name); } LUA_API int lua_gettable (lua_State *L, int idx) { - const TValue *slot; + int hres; TValue *t; lua_lock(L); t = index2value(L, idx); - if (luaV_fastget(L, t, s2v(L->top.p - 1), slot, luaH_get)) { - setobj2s(L, L->top.p - 1, slot); - } - else - luaV_finishget(L, t, s2v(L->top.p - 1), L->top.p - 1, slot); + luaV_fastget(t, s2v(L->top.p - 1), s2v(L->top.p - 1), luaH_get, hres); + if (hres != HOK) + luaV_finishget(L, t, s2v(L->top.p - 1), L->top.p - 1, hres); lua_unlock(L); return ttype(s2v(L->top.p - 1)); } @@ -694,16 +688,14 @@ LUA_API int lua_getfield (lua_State *L, int idx, const char *k) { LUA_API int lua_geti (lua_State *L, int idx, lua_Integer n) { TValue *t; - const TValue *slot; + int hres; lua_lock(L); t = index2value(L, idx); - if (luaV_fastgeti(L, t, n, slot)) { - setobj2s(L, L->top.p, slot); - } - else { - TValue aux; - setivalue(&aux, n); - luaV_finishget(L, t, &aux, L->top.p, slot); + luaV_fastgeti(t, n, s2v(L->top.p), hres); + if (hres != HOK) { + TValue key; + setivalue(&key, n); + luaV_finishget(L, t, &key, L->top.p, hres); } api_incr_top(L); lua_unlock(L); @@ -711,11 +703,9 @@ LUA_API int lua_geti (lua_State *L, int idx, lua_Integer n) { } -l_sinline int finishrawget (lua_State *L, const TValue *val) { - if (isempty(val)) /* avoid copying empty items to the stack */ +l_sinline int finishrawget (lua_State *L, int hres) { + if (hres != HOK) /* avoid copying empty items to the stack */ setnilvalue(s2v(L->top.p)); - else - setobj2s(L, L->top.p, val); api_incr_top(L); lua_unlock(L); return ttype(s2v(L->top.p - 1)); @@ -731,13 +721,13 @@ static Table *gettable (lua_State *L, int idx) { LUA_API int lua_rawget (lua_State *L, int idx) { Table *t; - const TValue *val; + int hres; lua_lock(L); api_checknelems(L, 1); t = gettable(L, idx); - val = luaH_get(t, s2v(L->top.p - 1)); + hres = luaH_get(t, s2v(L->top.p - 1), s2v(L->top.p - 1)); L->top.p--; /* remove key */ - return finishrawget(L, val); + return finishrawget(L, hres); } @@ -745,7 +735,7 @@ LUA_API int lua_rawgeti (lua_State *L, int idx, lua_Integer n) { Table *t; lua_lock(L); t = gettable(L, idx); - return finishrawget(L, luaH_getint(t, n)); + return finishrawget(L, luaH_getint(t, n, s2v(L->top.p))); } @@ -755,7 +745,7 @@ LUA_API int lua_rawgetp (lua_State *L, int idx, const void *p) { lua_lock(L); t = gettable(L, idx); setpvalue(&k, cast_voidp(p)); - return finishrawget(L, luaH_get(t, &k)); + return finishrawget(L, luaH_get(t, &k, s2v(L->top.p))); } @@ -827,17 +817,18 @@ LUA_API int lua_getiuservalue (lua_State *L, int idx, int n) { ** t[k] = value at the top of the stack (where 'k' is a string) */ static void auxsetstr (lua_State *L, const TValue *t, const char *k) { - const TValue *slot; + int hres; TString *str = luaS_new(L, k); api_checknelems(L, 1); - if (luaV_fastget(L, t, str, slot, luaH_getstr)) { - luaV_finishfastset(L, t, slot, s2v(L->top.p - 1)); + luaV_fastset(t, str, s2v(L->top.p - 1), hres, luaH_psetstr); + if (hres == HOK) { + luaV_finishfastset(L, t, s2v(L->top.p - 1)); L->top.p--; /* pop value */ } else { setsvalue2s(L, L->top.p, str); /* push 'str' (to make it a TValue) */ api_incr_top(L); - luaV_finishset(L, t, s2v(L->top.p - 1), s2v(L->top.p - 2), slot); + luaV_finishset(L, t, s2v(L->top.p - 1), s2v(L->top.p - 2), hres); L->top.p -= 2; /* pop value and key */ } lua_unlock(L); /* lock done by caller */ @@ -845,24 +836,25 @@ static void auxsetstr (lua_State *L, const TValue *t, const char *k) { LUA_API void lua_setglobal (lua_State *L, const char *name) { - const TValue *G; + TValue gt; lua_lock(L); /* unlock done in 'auxsetstr' */ - G = getGtable(L); - auxsetstr(L, G, name); + getGlobalTable(L, >); + auxsetstr(L, >, name); } LUA_API void lua_settable (lua_State *L, int idx) { TValue *t; - const TValue *slot; + int hres; lua_lock(L); api_checknelems(L, 2); t = index2value(L, idx); - if (luaV_fastget(L, t, s2v(L->top.p - 2), slot, luaH_get)) { - luaV_finishfastset(L, t, slot, s2v(L->top.p - 1)); + luaV_fastset(t, s2v(L->top.p - 2), s2v(L->top.p - 1), hres, luaH_pset); + if (hres == HOK) { + luaV_finishfastset(L, t, s2v(L->top.p - 1)); } else - luaV_finishset(L, t, s2v(L->top.p - 2), s2v(L->top.p - 1), slot); + luaV_finishset(L, t, s2v(L->top.p - 2), s2v(L->top.p - 1), hres); L->top.p -= 2; /* pop index and value */ lua_unlock(L); } @@ -876,17 +868,18 @@ LUA_API void lua_setfield (lua_State *L, int idx, const char *k) { LUA_API void lua_seti (lua_State *L, int idx, lua_Integer n) { TValue *t; - const TValue *slot; + int hres; lua_lock(L); api_checknelems(L, 1); t = index2value(L, idx); - if (luaV_fastgeti(L, t, n, slot)) { - luaV_finishfastset(L, t, slot, s2v(L->top.p - 1)); + luaV_fastseti(t, n, s2v(L->top.p - 1), hres); + if (hres == HOK) { + luaV_finishfastset(L, t, s2v(L->top.p - 1)); } else { - TValue aux; - setivalue(&aux, n); - luaV_finishset(L, t, &aux, s2v(L->top.p - 1), slot); + TValue temp; + setivalue(&temp, n); + luaV_finishset(L, t, &temp, s2v(L->top.p - 1), hres); } L->top.p--; /* pop value */ lua_unlock(L); @@ -1096,10 +1089,11 @@ LUA_API int lua_load (lua_State *L, lua_Reader reader, void *data, LClosure *f = clLvalue(s2v(L->top.p - 1)); /* get new function */ if (f->nupvalues >= 1) { /* does it have an upvalue? */ /* get global table from registry */ - const TValue *gt = getGtable(L); + TValue gt; + getGlobalTable(L, >); /* set global table as 1st upvalue of 'f' (may be LUA_ENV) */ - setobj(L, f->upvals[0]->v.p, gt); - luaC_barrier(L, f->upvals[0], gt); + setobj(L, f->upvals[0]->v.p, >); + luaC_barrier(L, f->upvals[0], >); } } lua_unlock(L); diff --git a/lcode.c b/lcode.c index 914d8cd0..0d888822 100644 --- a/lcode.c +++ b/lcode.c @@ -544,10 +544,10 @@ static int addk (FuncState *fs, TValue *key, TValue *v) { TValue val; lua_State *L = fs->ls->L; Proto *f = fs->f; - const TValue *idx = luaH_get(fs->ls->h, key); /* query scanner table */ + int aux = luaH_get(fs->ls->h, key, &val); /* query scanner table */ int k, oldsize; - if (ttisinteger(idx)) { /* is there an index there? */ - k = cast_int(ivalue(idx)); + if (aux == HOK && ttisinteger(&val)) { /* is there an index there? */ + k = cast_int(ivalue(&val)); /* correct value? (warning: must distinguish floats from integers!) */ if (k < fs->nk && ttypetag(&f->k[k]) == ttypetag(v) && luaV_rawequalobj(&f->k[k], v)) @@ -559,7 +559,7 @@ static int addk (FuncState *fs, TValue *key, TValue *v) { /* numerical value does not need GC barrier; table has no metatable, so it does not need to invalidate cache */ setivalue(&val, k); - luaH_finishset(L, fs->ls->h, key, idx, &val); + luaH_set(L, fs->ls->h, key, &val); luaM_growvector(L, f->k, k, f->sizek, TValue, MAXARG_Ax, "constants"); while (oldsize < f->sizek) setnilvalue(&f->k[oldsize++]); setobj(L, &f->k[k], v); diff --git a/lgc.c b/lgc.c index 81682454..e4f8e396 100644 --- a/lgc.c +++ b/lgc.c @@ -73,6 +73,13 @@ #define gcvalueN(o) (iscollectable(o) ? gcvalue(o) : NULL) +/* +** Access to collectable objects in array part of tables +*/ +#define gcvalarr(t,i) \ + ((*getArrTag(t,i) & BIT_ISCOLLECTABLE) ? getArrVal(t,i)->gc : NULL) + + #define markvalue(g,o) { checkliveness(g->mainthread,o); \ if (valiswhite(o)) reallymarkobject(g,gcvalue(o)); } @@ -477,9 +484,10 @@ static int traverseephemeron (global_State *g, Table *h, int inv) { unsigned int nsize = sizenode(h); /* traverse array part */ for (i = 0; i < asize; i++) { - if (valiswhite(&h->array[i])) { + GCObject *o = gcvalarr(h, i); + if (o != NULL && iswhite(o)) { marked = 1; - reallymarkobject(g, gcvalue(&h->array[i])); + reallymarkobject(g, o); } } /* traverse hash part; if 'inv', traverse descending @@ -515,8 +523,11 @@ static void traversestrongtable (global_State *g, Table *h) { Node *n, *limit = gnodelast(h); unsigned int i; unsigned int asize = luaH_realasize(h); - for (i = 0; i < asize; i++) /* traverse array part */ - markvalue(g, &h->array[i]); + for (i = 0; i < asize; i++) { /* traverse array part */ + GCObject *o = gcvalarr(h, i); + if (o != NULL && iswhite(o)) + reallymarkobject(g, o); + } for (n = gnode(h, 0); n < limit; n++) { /* traverse hash part */ if (isempty(gval(n))) /* entry is empty? */ clearkey(n); /* clear its key */ @@ -741,9 +752,9 @@ static l_obj clearbyvalues (global_State *g, GCObject *l, GCObject *f) { unsigned int i; unsigned int asize = luaH_realasize(h); for (i = 0; i < asize; i++) { - TValue *o = &h->array[i]; - if (iscleared(g, gcvalueN(o))) /* value was collected? */ - setempty(o); /* remove entry */ + GCObject *o = gcvalarr(h, i); + if (iscleared(g, o)) /* value was collected? */ + *getArrTag(h, i) = LUA_VEMPTY; /* remove entry */ } for (n = gnode(h, 0); n < limit; n++) { if (iscleared(g, gcvalueN(gval(n)))) /* unmarked value? */ diff --git a/llex.c b/llex.c index 5fc39a5c..9f20d3c8 100644 --- a/llex.c +++ b/llex.c @@ -134,13 +134,13 @@ l_noret luaX_syntaxerror (LexState *ls, const char *msg) { TString *luaX_newstring (LexState *ls, const char *str, size_t l) { lua_State *L = ls->L; TString *ts = luaS_newlstr(L, str, l); /* create new string */ - const TValue *o = luaH_getstr(ls->h, ts); - if (!ttisnil(o)) /* string already present? */ - ts = keystrval(nodefromval(o)); /* get saved copy */ - else { /* not in use yet */ + TString *oldts = luaH_getstrkey(ls->h, ts); + if (oldts != NULL) /* string already present? */ + return oldts; /* use it */ + else { /* create a new entry */ TValue *stv = s2v(L->top.p++); /* reserve stack space for string */ setsvalue(L, stv, ts); /* temporarily anchor the string */ - luaH_finishset(L, ls->h, stv, o, stv); /* t[string] = string */ + luaH_set(L, ls->h, stv, stv); /* t[string] = string */ /* table is not a metatable, so it does not need to invalidate cache */ luaC_checkGC(L); L->top.p--; /* remove string from stack */ diff --git a/lobject.h b/lobject.h index 209a87fc..c6c43647 100644 --- a/lobject.h +++ b/lobject.h @@ -192,6 +192,8 @@ typedef union { /* macro to test for (any kind of) nil */ #define ttisnil(v) checktype((v), LUA_TNIL) +#define tagisempty(tag) (novariant(tag) == LUA_TNIL) + /* macro to test for a standard nil */ #define ttisstrictnil(o) checktag((o), LUA_VNIL) @@ -749,12 +751,15 @@ typedef union Node { #define setnorealasize(t) ((t)->flags |= BITRAS) +typedef union ArrayCell ArrayCell; + + typedef struct Table { CommonHeader; lu_byte flags; /* 1<
l_registry, registry);
luaH_resize(L, registry, LUA_RIDX_LAST, 0);
/* registry[LUA_RIDX_MAINTHREAD] = L */
- setthvalue(L, ®istry->array[LUA_RIDX_MAINTHREAD - 1], L);
+ setthvalue(L, &aux, L);
+ luaH_setint(L, registry, LUA_RIDX_MAINTHREAD, &aux);
/* registry[LUA_RIDX_GLOBALS] = new table (table of globals) */
- sethvalue(L, ®istry->array[LUA_RIDX_GLOBALS - 1], luaH_new(L));
+ sethvalue(L, &aux, luaH_new(L));
+ luaH_setint(L, registry, LUA_RIDX_GLOBALS, &aux);
}
diff --git a/ltable.c b/ltable.c
index 103478fb..c9f66b3c 100644
--- a/ltable.c
+++ b/ltable.c
@@ -371,9 +371,10 @@ int luaH_next (lua_State *L, Table *t, StkId key) {
unsigned int asize = luaH_realasize(t);
unsigned int i = findindex(L, t, s2v(key), asize); /* find original key */
for (; i < asize; i++) { /* try first array part */
- if (!isempty(&t->array[i])) { /* a non-empty entry? */
+ int tag = *getArrTag(t, i);
+ if (!tagisempty(tag)) { /* a non-empty entry? */
setivalue(s2v(key), i + 1);
- setobj2s(L, key + 1, &t->array[i]);
+ farr2val(t, i + 1, tag, s2v(key + 1));
return 1;
}
}
@@ -402,6 +403,41 @@ static void freehash (lua_State *L, Table *t) {
}
+/*
+** Check whether an integer key is in the array part. If 'alimit' is
+** not the real size of the array, the key still can be in the array
+** part. In this case, do the "Xmilia trick" to check whether 'key-1'
+** is smaller than the real size.
+** The trick works as follow: let 'p' be an integer such that
+** '2^(p+1) >= alimit > 2^p', or '2^(p+1) > alimit-1 >= 2^p'.
+** That is, 2^(p+1) is the real size of the array, and 'p' is the highest
+** bit on in 'alimit-1'. What we have to check becomes 'key-1 < 2^(p+1)'.
+** We compute '(key-1) & ~(alimit-1)', which we call 'res'; it will
+** have the 'p' bit cleared. If the key is outside the array, that is,
+** 'key-1 >= 2^(p+1)', then 'res' will have some 1-bit higher than 'p',
+** therefore it will be larger or equal to 'alimit', and the check
+** will fail. If 'key-1 < 2^(p+1)', then 'res' has no 1-bit higher than
+** 'p', and as the bit 'p' itself was cleared, 'res' will be smaller
+** than 2^p, therefore smaller than 'alimit', and the check succeeds.
+** As special cases, when 'alimit' is 0 the condition is trivially false,
+** and when 'alimit' is 1 the condition simplifies to 'key-1 < alimit'.
+** If key is 0 or negative, 'res' will have its higher bit on, so that
+** if cannot be smaller than alimit.
+*/
+static int keyinarray (Table *t, lua_Integer key) {
+ lua_Unsigned alimit = t->alimit;
+ if (l_castS2U(key) - 1u < alimit) /* 'key' in [1, t->alimit]? */
+ return 1;
+ else if (!isrealasize(t) && /* key still may be in the array part? */
+ (((l_castS2U(key) - 1u) & ~(alimit - 1u)) < alimit)) {
+ t->alimit = cast_uint(key); /* probably '#t' is here now */
+ return 1;
+ }
+ else
+ return 0;
+}
+
+
/*
** {=============================================================
** Rehash
@@ -449,6 +485,12 @@ static int countint (lua_Integer key, unsigned int *nums) {
}
+l_sinline int arraykeyisempty (const Table *t, lua_Integer key) {
+ int tag = *getArrTag(t, key - 1);
+ return tagisempty(tag);
+}
+
+
/*
** Count keys in array part of table 't': Fill 'nums[i]' with
** number of keys that will go into corresponding slice and return
@@ -471,7 +513,7 @@ static unsigned int numusearray (const Table *t, unsigned int *nums) {
}
/* count elements in range (2^(lg - 1), 2^lg] */
for (; i <= lim; i++) {
- if (!isempty(&t->array[i-1]))
+ if (!arraykeyisempty(t, i))
lc++;
}
nums[lg] += lc;
@@ -498,6 +540,33 @@ static int numusehash (const Table *t, unsigned int *nums, unsigned int *pna) {
}
+/*
+** Convert an "abstract size" (number of values in an array) to
+** "concrete size" (number of cell elements in the array). Cells
+** do not need to be full; we only must make sure it has the values
+** needed and its 'tag' element. So, we compute the concrete tag index
+** and the concrete value index of the last element, get their maximum
+** and adds 1.
+*/
+static unsigned int concretesize (unsigned int size) {
+ if (size == 0) return 0;
+ else {
+ unsigned int ts = TagIndex(size - 1);
+ unsigned int vs = ValueIndex(size - 1);
+ return ((ts >= vs) ? ts : vs) + 1;
+ }
+}
+
+
+static ArrayCell *resizearray (lua_State *L , Table *t,
+ unsigned int oldasize,
+ unsigned int newasize) {
+ oldasize = concretesize(oldasize);
+ newasize = concretesize(newasize);
+ return luaM_reallocvector(L, t->array, oldasize, newasize, ArrayCell);
+}
+
+
/*
** Creates an array for the hash part of a table with the given
** size, or reuses the dummy node if size is zero.
@@ -593,7 +662,7 @@ void luaH_resize (lua_State *L, Table *t, unsigned int newasize,
unsigned int i;
Table newt; /* to keep the new hash part */
unsigned int oldasize = setlimittosize(t);
- TValue *newarray;
+ ArrayCell *newarray;
/* create new hash part with appropriate size into 'newt' */
newt.flags = 0;
setnodevector(L, &newt, nhsize);
@@ -602,14 +671,18 @@ void luaH_resize (lua_State *L, Table *t, unsigned int newasize,
exchangehashpart(t, &newt); /* and new hash */
/* re-insert into the new hash the elements from vanishing slice */
for (i = newasize; i < oldasize; i++) {
- if (!isempty(&t->array[i]))
- luaH_setint(L, t, i + 1, &t->array[i]);
+ int tag = *getArrTag(t, i);
+ if (!tagisempty(tag)) { /* a non-empty entry? */
+ TValue aux;
+ farr2val(t, i + 1, tag, &aux);
+ luaH_setint(L, t, i + 1, &aux);
+ }
}
t->alimit = oldasize; /* restore current size... */
exchangehashpart(t, &newt); /* and hash (in case of errors) */
}
/* allocate new array */
- newarray = luaM_reallocvector(L, t->array, oldasize, newasize, TValue);
+ newarray = resizearray(L, t, oldasize, newasize);
if (l_unlikely(newarray == NULL && newasize > 0)) { /* allocation failed? */
freehash(L, &newt); /* release new hash part */
luaM_error(L); /* raise error (with array unchanged) */
@@ -619,7 +692,7 @@ void luaH_resize (lua_State *L, Table *t, unsigned int newasize,
t->array = newarray; /* set new array part */
t->alimit = newasize;
for (i = oldasize; i < newasize; i++) /* clear new slice of the array */
- setempty(&t->array[i]);
+ *getArrTag(t, i) = LUA_VEMPTY;
/* re-insert elements from old hash part into new parts */
reinsert(L, &newt, t); /* 'newt' now has the old hash */
freehash(L, &newt); /* free old hash part */
@@ -675,8 +748,9 @@ Table *luaH_new (lua_State *L) {
void luaH_free (lua_State *L, Table *t) {
+ unsigned ps = concretesize(luaH_realasize(t));
freehash(L, t);
- luaM_freearray(L, t->array, luaH_realasize(t));
+ luaM_freearray(L, t->array, ps);
luaM_free(L, t);
}
@@ -770,57 +844,57 @@ static void luaH_newkey (lua_State *L, Table *t, const TValue *key,
}
-/*
-** Search function for integers. If integer is inside 'alimit', get it
-** directly from the array part. Otherwise, if 'alimit' is not
-** the real size of the array, the key still can be in the array part.
-** In this case, do the "Xmilia trick" to check whether 'key-1' is
-** smaller than the real size.
-** The trick works as follow: let 'p' be an integer such that
-** '2^(p+1) >= alimit > 2^p', or '2^(p+1) > alimit-1 >= 2^p'.
-** That is, 2^(p+1) is the real size of the array, and 'p' is the highest
-** bit on in 'alimit-1'. What we have to check becomes 'key-1 < 2^(p+1)'.
-** We compute '(key-1) & ~(alimit-1)', which we call 'res'; it will
-** have the 'p' bit cleared. If the key is outside the array, that is,
-** 'key-1 >= 2^(p+1)', then 'res' will have some bit on higher than 'p',
-** therefore it will be larger or equal to 'alimit', and the check
-** will fail. If 'key-1 < 2^(p+1)', then 'res' has no bit on higher than
-** 'p', and as the bit 'p' itself was cleared, 'res' will be smaller
-** than 2^p, therefore smaller than 'alimit', and the check succeeds.
-** As special cases, when 'alimit' is 0 the condition is trivially false,
-** and when 'alimit' is 1 the condition simplifies to 'key-1 < alimit'.
-** If key is 0 or negative, 'res' will have its higher bit on, so that
-** if cannot be smaller than alimit.
-*/
-const TValue *luaH_getint (Table *t, lua_Integer key) {
- lua_Unsigned alimit = t->alimit;
- if (l_castS2U(key) - 1u < alimit) /* 'key' in [1, t->alimit]? */
- return &t->array[key - 1];
- else if (!isrealasize(t) && /* key still may be in the array part? */
- (((l_castS2U(key) - 1u) & ~(alimit - 1u)) < alimit)) {
- t->alimit = cast_uint(key); /* probably '#t' is here now */
- return &t->array[key - 1];
- }
- else { /* key is not in the array part; check the hash */
- Node *n = hashint(t, key);
- for (;;) { /* check whether 'key' is somewhere in the chain */
- if (keyisinteger(n) && keyival(n) == key)
- return gval(n); /* that's it */
- else {
- int nx = gnext(n);
- if (nx == 0) break;
- n += nx;
- }
+static const TValue *getintfromhash (Table *t, lua_Integer key) {
+ Node *n = hashint(t, key);
+ lua_assert(l_castS2U(key) - 1u >= luaH_realasize(t));
+ for (;;) { /* check whether 'key' is somewhere in the chain */
+ if (keyisinteger(n) && keyival(n) == key)
+ return gval(n); /* that's it */
+ else {
+ int nx = gnext(n);
+ if (nx == 0) break;
+ n += nx;
}
- return &absentkey;
}
+ return &absentkey;
+}
+
+
+static int hashkeyisempty (Table *t, lua_Integer key) {
+ const TValue *val = getintfromhash(t, key);
+ return isempty(val);
+}
+
+
+static int finishnodeget (const TValue *val, TValue *res) {
+ if (!ttisnil(val)) {
+ setobj(((lua_State*)NULL), res, val);
+ return HOK; /* success */
+ }
+ else
+ return HNOTFOUND; /* could not get value */
+}
+
+
+int luaH_getint (Table *t, lua_Integer key, TValue *res) {
+ if (keyinarray(t, key)) {
+ int tag = *getArrTag(t, key - 1);
+ if (!tagisempty(tag)) {
+ farr2val(t, key, tag, res);
+ return HOK; /* success */
+ }
+ else
+ return ~cast_int(key); /* empty slot in the array part */
+ }
+ else
+ return finishnodeget(getintfromhash(t, key), res);
}
/*
** search function for short strings
*/
-const TValue *luaH_getshortstr (Table *t, TString *key) {
+const TValue *luaH_Hgetshortstr (Table *t, TString *key) {
Node *n = hashstr(t, key);
lua_assert(key->tt == LUA_VSHRSTR);
for (;;) { /* check whether 'key' is somewhere in the chain */
@@ -836,9 +910,14 @@ const TValue *luaH_getshortstr (Table *t, TString *key) {
}
-const TValue *luaH_getstr (Table *t, TString *key) {
+int luaH_getshortstr (Table *t, TString *key, TValue *res) {
+ return finishnodeget(luaH_Hgetshortstr(t, key), res);
+}
+
+
+static const TValue *Hgetstr (Table *t, TString *key) {
if (key->tt == LUA_VSHRSTR)
- return luaH_getshortstr(t, key);
+ return luaH_Hgetshortstr(t, key);
else { /* for long strings, use generic case */
TValue ko;
setsvalue(cast(lua_State *, NULL), &ko, key);
@@ -847,38 +926,121 @@ const TValue *luaH_getstr (Table *t, TString *key) {
}
+int luaH_getstr (Table *t, TString *key, TValue *res) {
+ return finishnodeget(Hgetstr(t, key), res);
+}
+
+
+TString *luaH_getstrkey (Table *t, TString *key) {
+ const TValue *o = Hgetstr(t, key);
+ if (!isabstkey(o)) /* string already present? */
+ return keystrval(nodefromval(o)); /* get saved copy */
+ else
+ return NULL;
+}
+
+
/*
** main search function
*/
-const TValue *luaH_get (Table *t, const TValue *key) {
+int luaH_get (Table *t, const TValue *key, TValue *res) {
+ const TValue *slot;
switch (ttypetag(key)) {
- case LUA_VSHRSTR: return luaH_getshortstr(t, tsvalue(key));
- case LUA_VNUMINT: return luaH_getint(t, ivalue(key));
- case LUA_VNIL: return &absentkey;
+ case LUA_VSHRSTR:
+ slot = luaH_Hgetshortstr(t, tsvalue(key));
+ break;
+ case LUA_VNUMINT:
+ return luaH_getint(t, ivalue(key), res);
+ case LUA_VNIL:
+ slot = &absentkey;
+ break;
case LUA_VNUMFLT: {
lua_Integer k;
if (luaV_flttointeger(fltvalue(key), &k, F2Ieq)) /* integral index? */
- return luaH_getint(t, k); /* use specialized version */
+ return luaH_getint(t, k, res); /* use specialized version */
/* else... */
} /* FALLTHROUGH */
default:
- return getgeneric(t, key, 0);
+ slot = getgeneric(t, key, 0);
+ break;
}
+ return finishnodeget(slot, res);
}
+static int finishnodeset (Table *t, const TValue *slot, TValue *val) {
+ if (!ttisnil(slot)) {
+ setobj(((lua_State*)NULL), cast(TValue*, slot), val);
+ return HOK; /* success */
+ }
+ else if (isabstkey(slot))
+ return HNOTFOUND; /* no slot with that key */
+ else return (cast(Node*, slot) - t->node) + HFIRSTNODE; /* node encoded */
+}
+
+
+int luaH_psetint (Table *t, lua_Integer key, TValue *val) {
+ if (keyinarray(t, key)) {
+ lu_byte *tag = getArrTag(t, key - 1);
+ if (!tagisempty(*tag)) {
+ fval2arr(t, key, tag, val);
+ return HOK; /* success */
+ }
+ else
+ return ~cast_int(key); /* empty slot in the array part */
+ }
+ else
+ return finishnodeset(t, getintfromhash(t, key), val);
+}
+
+
+int luaH_psetshortstr (Table *t, TString *key, TValue *val) {
+ return finishnodeset(t, luaH_Hgetshortstr(t, key), val);
+}
+
+
+int luaH_psetstr (Table *t, TString *key, TValue *val) {
+ return finishnodeset(t, Hgetstr(t, key), val);
+}
+
+
+int luaH_pset (Table *t, const TValue *key, TValue *val) {
+ switch (ttypetag(key)) {
+ case LUA_VSHRSTR: return luaH_psetshortstr(t, tsvalue(key), val);
+ case LUA_VNUMINT: return luaH_psetint(t, ivalue(key), val);
+ case LUA_VNIL: return HNOTFOUND;
+ case LUA_VNUMFLT: {
+ lua_Integer k;
+ if (luaV_flttointeger(fltvalue(key), &k, F2Ieq)) /* integral index? */
+ return luaH_psetint(t, k, val); /* use specialized version */
+ /* else... */
+ } /* FALLTHROUGH */
+ default:
+ return finishnodeset(t, getgeneric(t, key, 0), val);
+ }
+}
+
/*
** Finish a raw "set table" operation, where 'slot' is where the value
** should have been (the result of a previous "get table").
** Beware: when using this function you probably need to check a GC
** barrier and invalidate the TM cache.
*/
+
+
void luaH_finishset (lua_State *L, Table *t, const TValue *key,
- const TValue *slot, TValue *value) {
- if (isabstkey(slot))
+ TValue *value, int hres) {
+ lua_assert(hres != HOK);
+ if (hres == HNOTFOUND) {
luaH_newkey(L, t, key, value);
- else
- setobj2t(L, cast(TValue *, slot), value);
+ }
+ else if (hres > 0) { /* regular Node? */
+ setobj2t(L, gval(gnode(t, hres - HFIRSTNODE)), value);
+ }
+ else { /* array entry */
+ hres = ~hres; /* real index */
+ obj2arr(t, hres, value);
+ }
}
@@ -887,20 +1049,19 @@ void luaH_finishset (lua_State *L, Table *t, const TValue *key,
** barrier and invalidate the TM cache.
*/
void luaH_set (lua_State *L, Table *t, const TValue *key, TValue *value) {
- const TValue *slot = luaH_get(t, key);
- luaH_finishset(L, t, key, slot, value);
+ int hres = luaH_pset(t, key, value);
+ if (hres != HOK)
+ luaH_finishset(L, t, key, value, hres);
}
void luaH_setint (lua_State *L, Table *t, lua_Integer key, TValue *value) {
- const TValue *p = luaH_getint(t, key);
- if (isabstkey(p)) {
+ int hres = luaH_psetint(t, key, value);
+ if (hres != HOK) {
TValue k;
setivalue(&k, key);
- luaH_newkey(L, t, &k, value);
+ luaH_finishset(L, t, &k, value, hres);
}
- else
- setobj2t(L, cast(TValue *, p), value);
}
@@ -926,27 +1087,26 @@ static lua_Unsigned hash_search (Table *t, lua_Unsigned j) {
j *= 2;
else {
j = LUA_MAXINTEGER;
- if (isempty(luaH_getint(t, j))) /* t[j] not present? */
+ if (hashkeyisempty(t, j)) /* t[j] not present? */
break; /* 'j' now is an absent index */
else /* weird case */
return j; /* well, max integer is a boundary... */
}
- } while (!isempty(luaH_getint(t, j))); /* repeat until an absent t[j] */
+ } while (!hashkeyisempty(t, j)); /* repeat until an absent t[j] */
/* i < j && t[i] present && t[j] absent */
while (j - i > 1u) { /* do a binary search between them */
lua_Unsigned m = (i + j) / 2;
- if (isempty(luaH_getint(t, m))) j = m;
+ if (hashkeyisempty(t, m)) j = m;
else i = m;
}
return i;
}
-static unsigned int binsearch (const TValue *array, unsigned int i,
- unsigned int j) {
+static unsigned int binsearch (Table *array, unsigned int i, unsigned int j) {
while (j - i > 1u) { /* binary search */
unsigned int m = (i + j) / 2;
- if (isempty(&array[m - 1])) j = m;
+ if (arraykeyisempty(array, m)) j = m;
else i = m;
}
return i;
@@ -987,9 +1147,9 @@ static unsigned int binsearch (const TValue *array, unsigned int i,
*/
lua_Unsigned luaH_getn (Table *t) {
unsigned int limit = t->alimit;
- if (limit > 0 && isempty(&t->array[limit - 1])) { /* (1)? */
+ if (limit > 0 && arraykeyisempty(t, limit)) { /* (1)? */
/* there must be a boundary before 'limit' */
- if (limit >= 2 && !isempty(&t->array[limit - 2])) {
+ if (limit >= 2 && !arraykeyisempty(t, limit - 1)) {
/* 'limit - 1' is a boundary; can it be a new limit? */
if (ispow2realasize(t) && !ispow2(limit - 1)) {
t->alimit = limit - 1;
@@ -998,7 +1158,7 @@ lua_Unsigned luaH_getn (Table *t) {
return limit - 1;
}
else { /* must search for a boundary in [0, limit] */
- unsigned int boundary = binsearch(t->array, 0, limit);
+ unsigned int boundary = binsearch(t, 0, limit);
/* can this boundary represent the real size of the array? */
if (ispow2realasize(t) && boundary > luaH_realasize(t) / 2) {
t->alimit = boundary; /* use it as the new limit */
@@ -1010,14 +1170,14 @@ lua_Unsigned luaH_getn (Table *t) {
/* 'limit' is zero or present in table */
if (!limitequalsasize(t)) { /* (2)? */
/* 'limit' > 0 and array has more elements after 'limit' */
- if (isempty(&t->array[limit])) /* 'limit + 1' is empty? */
+ if (arraykeyisempty(t, limit + 1)) /* 'limit + 1' is empty? */
return limit; /* this is the boundary */
/* else, try last element in the array */
limit = luaH_realasize(t);
- if (isempty(&t->array[limit - 1])) { /* empty? */
+ if (arraykeyisempty(t, limit)) { /* empty? */
/* there must be a boundary in the array after old limit,
and it must be a valid new limit */
- unsigned int boundary = binsearch(t->array, t->alimit, limit);
+ unsigned int boundary = binsearch(t, t->alimit, limit);
t->alimit = boundary;
return boundary;
}
@@ -1025,8 +1185,8 @@ lua_Unsigned luaH_getn (Table *t) {
}
/* (3) 'limit' is the last element and either is zero or present in table */
lua_assert(limit == luaH_realasize(t) &&
- (limit == 0 || !isempty(&t->array[limit - 1])));
- if (isdummy(t) || isempty(luaH_getint(t, cast(lua_Integer, limit + 1))))
+ (limit == 0 || !arraykeyisempty(t, limit)));
+ if (isdummy(t) || hashkeyisempty(t, cast(lua_Integer, limit + 1)))
return limit; /* 'limit + 1' is absent */
else /* 'limit + 1' is also present */
return hash_search(t, limit);
diff --git a/ltable.h b/ltable.h
index 30cfc8be..210ae7ac 100644
--- a/ltable.h
+++ b/ltable.h
@@ -45,16 +45,105 @@
#define nodefromval(v) cast(Node *, (v))
-LUAI_FUNC const TValue *luaH_getint (Table *t, lua_Integer key);
+/* results from get/pset */
+#define HOK 0
+#define HNOTFOUND 1
+#define HNOTATABLE 2
+#define HFIRSTNODE 3
+
+/*
+** Besides these values, pset (pre-set) operations may also return an
+** encoding of where the value should go (usually called 'hres'). That
+** means that there is a slot with that key but with no value. (pset
+** cannot set that value because there might be a metamethod.) If the
+** slot is in the hash part, the encoding is (HFIRSTNODE + hash index);
+** if the slot is in the array part, the encoding is (~array index).
+*/
+
+
+/*
+** The array part of a table is represented by an array of cells.
+** Each cell is composed of (NM + 1) elements, and each element has the
+** type 'ArrayCell'. In each cell, only one element has the variant
+** 'tag', while the other NM elements have the variant 'value'. The
+** array in the 'tag' element holds the tags of the other elements in
+** that cell.
+*/
+#define NM ((unsigned int)sizeof(Value))
+
+union ArrayCell {
+ unsigned char tag[NM];
+ Value value;
+};
+
+
+/*
+** 'NMTag' defines which cell element has the tags; that could be any
+** value between 0 (tags come before all values) and NM (tags come after
+** all values).
+*/
+#define NMTag 0
+
+
+/*
+** Computes the concrete index that holds the tag of abstract index 'i'
+*/
+#define TagIndex(i) (((i)/NM * (NM + 1u)) + NMTag)
+
+/*
+** Computes the concrete index that holds the value of abstract index 'i'
+*/
+#define ValueIndex(i) ((i) + (((i) + (NM - NMTag))/NM))
+
+
+/* Computes the address of the tag for the abstract index 'k' */
+#define getArrTag(t,k) (&(t)->array[TagIndex(k)].tag[(k)%NM])
+
+/* Computes the address of the value for the abstract index 'k' */
+#define getArrVal(t,k) (&(t)->array[ValueIndex(k)].value)
+
+
+/*
+** Move TValues to/from arrays, using Lua indices
+*/
+#define arr2obj(h,k,val) \
+ ((val)->tt_ = *getArrTag(h,(k)-1u), (val)->value_ = *getArrVal(h,(k)-1u))
+
+#define obj2arr(h,k,val) \
+ (*getArrTag(h,(k)-1u) = (val)->tt_, *getArrVal(h,(k)-1u) = (val)->value_)
+
+
+/*
+** Often, we need to check the tag of a value before moving it. These
+** macros also move TValues to/from arrays, but receive the precomputed
+** tag value or address as an extra argument.
+*/
+#define farr2val(h,k,tag,res) \
+ ((res)->tt_ = tag, (res)->value_ = *getArrVal(h,(k)-1u))
+
+#define fval2arr(h,k,tag,val) \
+ (*tag = (val)->tt_, *getArrVal(h,(k)-1u) = (val)->value_)
+
+
+LUAI_FUNC int luaH_getshortstr (Table *t, TString *key, TValue *res);
+LUAI_FUNC int luaH_getstr (Table *t, TString *key, TValue *res);
+LUAI_FUNC int luaH_get (Table *t, const TValue *key, TValue *res);
+LUAI_FUNC int luaH_getint (Table *t, lua_Integer key, TValue *res);
+
+LUAI_FUNC TString *luaH_getstrkey (Table *t, TString *key);
+
+LUAI_FUNC int luaH_psetint (Table *t, lua_Integer key, TValue *val);
+LUAI_FUNC int luaH_psetshortstr (Table *t, TString *key, TValue *val);
+LUAI_FUNC int luaH_psetstr (Table *t, TString *key, TValue *val);
+LUAI_FUNC int luaH_pset (Table *t, const TValue *key, TValue *val);
+
LUAI_FUNC void luaH_setint (lua_State *L, Table *t, lua_Integer key,
TValue *value);
-LUAI_FUNC const TValue *luaH_getshortstr (Table *t, TString *key);
-LUAI_FUNC const TValue *luaH_getstr (Table *t, TString *key);
-LUAI_FUNC const TValue *luaH_get (Table *t, const TValue *key);
+LUAI_FUNC const TValue *luaH_Hgetshortstr (Table *t, TString *key);
LUAI_FUNC void luaH_set (lua_State *L, Table *t, const TValue *key,
TValue *value);
LUAI_FUNC void luaH_finishset (lua_State *L, Table *t, const TValue *key,
- const TValue *slot, TValue *value);
+ TValue *value, int aux);
LUAI_FUNC Table *luaH_new (lua_State *L);
LUAI_FUNC void luaH_resize (lua_State *L, Table *t, unsigned int nasize,
unsigned int nhsize);
diff --git a/ltests.c b/ltests.c
index 15d1a564..6f556dc9 100644
--- a/ltests.c
+++ b/ltests.c
@@ -362,8 +362,11 @@ static void checktable (global_State *g, Table *h) {
Node *n, *limit = gnode(h, sizenode(h));
GCObject *hgc = obj2gco(h);
checkobjrefN(g, hgc, h->metatable);
- for (i = 0; i < asize; i++)
- checkvalref(g, hgc, &h->array[i]);
+ for (i = 0; i < asize; i++) {
+ TValue aux;
+ arr2obj(h, i + 1, &aux);
+ checkvalref(g, hgc, &aux);
+ }
for (n = gnode(h, 0); n < limit; n++) {
if (!isempty(gval(n))) {
TValue k;
@@ -1004,7 +1007,8 @@ static int table_query (lua_State *L) {
}
else if ((unsigned int)i < asize) {
lua_pushinteger(L, i);
- pushobject(L, &t->array[i]);
+ arr2obj(t, i + 1, s2v(L->top.p));
+ api_incr_top(L);
lua_pushnil(L);
}
else if ((i -= asize) < sizenode(t)) {
diff --git a/ltm.c b/ltm.c
index 8e0d2222..c943bc7b 100644
--- a/ltm.c
+++ b/ltm.c
@@ -58,7 +58,7 @@ void luaT_init (lua_State *L) {
** tag methods
*/
const TValue *luaT_gettm (Table *events, TMS event, TString *ename) {
- const TValue *tm = luaH_getshortstr(events, ename);
+ const TValue *tm = luaH_Hgetshortstr(events, ename);
lua_assert(event <= TM_EQ);
if (notm(tm)) { /* no tag method? */
events->flags |= cast_byte(1u<