1
0
mirror of https://github.com/lua/lua synced 2025-03-27 16:12:52 +03:00

first version of empty entries in tables

(so that, in the future, tables can contain regular nil entries)
This commit is contained in:
Roberto Ierusalimschy 2018-02-23 10:16:18 -03:00
parent 477ca2fe8c
commit 9243c414d9
11 changed files with 146 additions and 99 deletions

30
lapi.c

@ -1,5 +1,5 @@
/* /*
** $Id: lapi.c,v 2.283 2018/02/05 17:10:52 roberto Exp roberto $ ** $Id: lapi.c,v 2.285 2018/02/20 16:52:50 roberto Exp roberto $
** Lua API ** Lua API
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -657,14 +657,26 @@ LUA_API int lua_geti (lua_State *L, int idx, lua_Integer n) {
} }
static int finishrawget (lua_State *L, const TValue *val) {
if (isempty(val)) /* avoid copying empty items to the stack */
setnilvalue(s2v(L->top));
else
setobj2s(L, L->top, val);
api_incr_top(L);
lua_unlock(L);
return ttnov(s2v(L->top - 1));
}
LUA_API int lua_rawget (lua_State *L, int idx) { LUA_API int lua_rawget (lua_State *L, int idx) {
TValue *t; TValue *t;
const TValue *val;
lua_lock(L); lua_lock(L);
t = index2value(L, idx); t = index2value(L, idx);
api_check(L, ttistable(t), "table expected"); api_check(L, ttistable(t), "table expected");
setobj2s(L, L->top - 1, luaH_get(hvalue(t), s2v(L->top - 1))); val = luaH_get(hvalue(t), s2v(L->top - 1));
lua_unlock(L); L->top--; /* remove key */
return ttnov(s2v(L->top - 1)); return finishrawget(L, val);
} }
@ -673,10 +685,7 @@ LUA_API int lua_rawgeti (lua_State *L, int idx, lua_Integer n) {
lua_lock(L); lua_lock(L);
t = index2value(L, idx); t = index2value(L, idx);
api_check(L, ttistable(t), "table expected"); api_check(L, ttistable(t), "table expected");
setobj2s(L, L->top, luaH_getint(hvalue(t), n)); return finishrawget(L, luaH_getint(hvalue(t), n));
api_incr_top(L);
lua_unlock(L);
return ttnov(s2v(L->top - 1));
} }
@ -687,10 +696,7 @@ LUA_API int lua_rawgetp (lua_State *L, int idx, const void *p) {
t = index2value(L, idx); t = index2value(L, idx);
api_check(L, ttistable(t), "table expected"); api_check(L, ttistable(t), "table expected");
setpvalue(&k, cast_voidp(p)); setpvalue(&k, cast_voidp(p));
setobj2s(L, L->top, luaH_get(hvalue(t), &k)); return finishrawget(L, luaH_get(hvalue(t), &k));
api_incr_top(L);
lua_unlock(L);
return ttnov(s2v(L->top - 1));
} }

58
lgc.c

@ -145,12 +145,13 @@ static GCObject **getgclist (GCObject *o) {
** Clear keys for empty entries in tables. If entry is empty ** Clear keys for empty entries in tables. If entry is empty
** and its key is not marked, mark its entry as dead. This allows the ** and its key is not marked, mark its entry as dead. This allows the
** collection of the key, but keeps its entry in the table (its removal ** collection of the key, but keeps its entry in the table (its removal
** could break a chain). Other places never manipulate dead keys, ** could break a chain). The main feature of a dead key is that it must
** because its associated nil value is enough to signal that the entry ** be different from any other value, to do not disturb searches.
** is logically empty. ** Other places never manipulate dead keys, because its associated empty
** value is enough to signal that the entry is logically empty.
*/ */
static void removeentry (Node *n) { static void clearkey (Node *n) {
lua_assert(ttisnil(gval(n))); lua_assert(isempty(gval(n)));
if (keyiswhite(n)) if (keyiswhite(n))
setdeadkey(n); /* unused and unmarked key; remove it */ setdeadkey(n); /* unused and unmarked key; remove it */
} }
@ -386,8 +387,8 @@ static void traverseweakvalue (global_State *g, Table *h) {
worth traversing it now just to check) */ worth traversing it now just to check) */
int hasclears = (h->sizearray > 0); int hasclears = (h->sizearray > 0);
for (n = gnode(h, 0); n < limit; n++) { /* traverse hash part */ for (n = gnode(h, 0); n < limit; n++) { /* traverse hash part */
if (ttisnil(gval(n))) /* entry is empty? */ if (isempty(gval(n))) /* entry is empty? */
removeentry(n); /* remove it */ clearkey(n); /* clear its key */
else { else {
lua_assert(!keyisnil(n)); lua_assert(!keyisnil(n));
markkey(g, n); markkey(g, n);
@ -428,8 +429,8 @@ static int traverseephemeron (global_State *g, Table *h) {
} }
/* traverse hash part */ /* traverse hash part */
for (n = gnode(h, 0); n < limit; n++) { for (n = gnode(h, 0); n < limit; n++) {
if (ttisnil(gval(n))) /* entry is empty? */ if (isempty(gval(n))) /* entry is empty? */
removeentry(n); /* remove it */ clearkey(n); /* clear its key */
else if (iscleared(g, gckeyN(n))) { /* key is not marked (yet)? */ else if (iscleared(g, gckeyN(n))) { /* key is not marked (yet)? */
hasclears = 1; /* table must be cleared */ hasclears = 1; /* table must be cleared */
if (valiswhite(gval(n))) /* value not marked yet? */ if (valiswhite(gval(n))) /* value not marked yet? */
@ -461,8 +462,8 @@ static void traversestrongtable (global_State *g, Table *h) {
for (i = 0; i < h->sizearray; i++) /* traverse array part */ for (i = 0; i < h->sizearray; i++) /* traverse array part */
markvalue(g, &h->array[i]); markvalue(g, &h->array[i]);
for (n = gnode(h, 0); n < limit; n++) { /* traverse hash part */ for (n = gnode(h, 0); n < limit; n++) { /* traverse hash part */
if (ttisnil(gval(n))) /* entry is empty? */ if (isempty(gval(n))) /* entry is empty? */
removeentry(n); /* remove it */ clearkey(n); /* clear its key */
else { else {
lua_assert(!keyisnil(n)); lua_assert(!keyisnil(n));
markkey(g, n); markkey(g, n);
@ -681,15 +682,16 @@ static void clearprotolist (global_State *g) {
/* /*
** clear entries with unmarked keys from all weaktables in list 'l' ** clear entries with unmarked keys from all weaktables in list 'l'
*/ */
static void clearkeys (global_State *g, GCObject *l) { static void clearbykeys (global_State *g, GCObject *l) {
for (; l; l = gco2t(l)->gclist) { for (; l; l = gco2t(l)->gclist) {
Table *h = gco2t(l); Table *h = gco2t(l);
Node *n, *limit = gnodelast(h); Node *limit = gnodelast(h);
Node *n;
for (n = gnode(h, 0); n < limit; n++) { for (n = gnode(h, 0); n < limit; n++) {
if (!ttisnil(gval(n)) && (iscleared(g, gckeyN(n)))) /* unmarked key? */ if (isempty(gval(n))) /* is entry empty? */
setnilvalue(gval(n)); /* clear value */ clearkey(n); /* clear its key */
if (ttisnil(gval(n))) /* is entry empty? */ else if (iscleared(g, gckeyN(n))) /* unmarked key? */
removeentry(n); /* remove it from table */ setempty(gval(n)); /* remove entry */
} }
} }
} }
@ -699,7 +701,7 @@ static void clearkeys (global_State *g, GCObject *l) {
** clear entries with unmarked values from all weaktables in list 'l' up ** clear entries with unmarked values from all weaktables in list 'l' up
** to element 'f' ** to element 'f'
*/ */
static void clearvalues (global_State *g, GCObject *l, GCObject *f) { static void clearbyvalues (global_State *g, GCObject *l, GCObject *f) {
for (; l != f; l = gco2t(l)->gclist) { for (; l != f; l = gco2t(l)->gclist) {
Table *h = gco2t(l); Table *h = gco2t(l);
Node *n, *limit = gnodelast(h); Node *n, *limit = gnodelast(h);
@ -707,13 +709,13 @@ static void clearvalues (global_State *g, GCObject *l, GCObject *f) {
for (i = 0; i < h->sizearray; i++) { for (i = 0; i < h->sizearray; i++) {
TValue *o = &h->array[i]; TValue *o = &h->array[i];
if (iscleared(g, gcvalueN(o))) /* value was collected? */ if (iscleared(g, gcvalueN(o))) /* value was collected? */
setnilvalue(o); /* remove value */ setempty(o); /* remove entry */
} }
for (n = gnode(h, 0); n < limit; n++) { for (n = gnode(h, 0); n < limit; n++) {
if (iscleared(g, gcvalueN(gval(n)))) /* unmarked value? */ if (iscleared(g, gcvalueN(gval(n)))) /* unmarked value? */
setnilvalue(gval(n)); /* clear value */ setempty(gval(n)); /* remove entry */
if (ttisnil(gval(n))) /* is entry empty? */ if (isempty(gval(n))) /* is entry empty? */
removeentry(n); /* remove it from table */ clearkey(n); /* clear its key */
} }
} }
} }
@ -1372,8 +1374,8 @@ static lu_mem atomic (lua_State *L) {
convergeephemerons(g); convergeephemerons(g);
/* at this point, all strongly accessible objects are marked. */ /* at this point, all strongly accessible objects are marked. */
/* Clear values from weak tables, before checking finalizers */ /* Clear values from weak tables, before checking finalizers */
clearvalues(g, g->weak, NULL); clearbyvalues(g, g->weak, NULL);
clearvalues(g, g->allweak, NULL); clearbyvalues(g, g->allweak, NULL);
origweak = g->weak; origall = g->allweak; origweak = g->weak; origall = g->allweak;
separatetobefnz(g, 0); /* separate objects to be finalized */ separatetobefnz(g, 0); /* separate objects to be finalized */
work += markbeingfnz(g); /* mark objects that will be finalized */ work += markbeingfnz(g); /* mark objects that will be finalized */
@ -1381,11 +1383,11 @@ static lu_mem atomic (lua_State *L) {
convergeephemerons(g); convergeephemerons(g);
/* at this point, all resurrected objects are marked. */ /* at this point, all resurrected objects are marked. */
/* remove dead objects from weak tables */ /* remove dead objects from weak tables */
clearkeys(g, g->ephemeron); /* clear keys from all ephemeron tables */ clearbykeys(g, g->ephemeron); /* clear keys from all ephemeron tables */
clearkeys(g, g->allweak); /* clear keys from all 'allweak' tables */ clearbykeys(g, g->allweak); /* clear keys from all 'allweak' tables */
/* clear values from resurrected weak tables */ /* clear values from resurrected weak tables */
clearvalues(g, g->weak, origweak); clearbyvalues(g, g->weak, origweak);
clearvalues(g, g->allweak, origall); clearbyvalues(g, g->allweak, origall);
luaS_clearcache(g); luaS_clearcache(g);
clearprotolist(g); clearprotolist(g);
g->currentwhite = cast_byte(otherwhite(g)); /* flip current white */ g->currentwhite = cast_byte(otherwhite(g)); /* flip current white */

4
llex.c

@ -1,5 +1,5 @@
/* /*
** $Id: llex.c,v 2.98 2017/06/29 15:06:44 roberto Exp roberto $ ** $Id: llex.c,v 2.99 2018/01/28 15:13:26 roberto Exp roberto $
** Lexical Analyzer ** Lexical Analyzer
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -130,7 +130,7 @@ TString *luaX_newstring (LexState *ls, const char *str, size_t l) {
TString *ts = luaS_newlstr(L, str, l); /* create new string */ TString *ts = luaS_newlstr(L, str, l); /* create new string */
setsvalue2s(L, L->top++, ts); /* temporarily anchor it in stack */ setsvalue2s(L, L->top++, ts); /* temporarily anchor it in stack */
o = luaH_set(L, ls->h, s2v(L->top - 1)); o = luaH_set(L, ls->h, s2v(L->top - 1));
if (ttisnil(o)) { /* not in use yet? */ if (isempty(o)) { /* not in use yet? */
/* boolean value does not need GC barrier; /* boolean value does not need GC barrier;
table is not a metatable, so it does not need to invalidate cache */ table is not a metatable, so it does not need to invalidate cache */
setbvalue(o, 1); /* t[string] = true */ setbvalue(o, 1); /* t[string] = true */

@ -1,5 +1,5 @@
/* /*
** $Id: lobject.h,v 2.135 2018/02/21 16:28:12 roberto Exp roberto $ ** $Id: lobject.h,v 2.136 2018/02/22 17:28:10 roberto Exp roberto $
** Type definitions for Lua objects ** Type definitions for Lua objects
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -100,7 +100,7 @@ typedef struct TValue {
#define setobj(L,obj1,obj2) \ #define setobj(L,obj1,obj2) \
{ TValue *io1=(obj1); const TValue *io2=(obj2); \ { TValue *io1=(obj1); const TValue *io2=(obj2); \
io1->value_ = io2->value_; io1->tt_ = io2->tt_; \ io1->value_ = io2->value_; io1->tt_ = io2->tt_; \
(void)L; checkliveness(L,io1); } (void)L; checkliveness(L,io1); lua_assert(!isreallyempty(io1)); }
/* /*
** different types of assignments, according to destination ** different types of assignments, according to destination
@ -148,6 +148,30 @@ typedef StackValue *StkId; /* index to stack elements */
/* (address of) a fixed nil value */ /* (address of) a fixed nil value */
#define luaO_nilobject (&luaO_nilobject_) #define luaO_nilobject (&luaO_nilobject_)
/*
** Variant tag, used only in tables to signal an empty slot
** (which might be different from a slot containing nil)
*/
#define LUA_TEMPTY (LUA_TNIL | (1 << 4))
#define ttisnilorempty(v) checktype((v), LUA_TNIL)
/*
** By default, entries with any kind of nil are considered empty
*/
#define isempty(v) ttisnilorempty(v)
#define isreallyempty(v) checktag((v), LUA_TEMPTY)
/* macro defining an empty value */
#define EMPTYCONSTANT {NULL}, LUA_TEMPTY
/* mark an entry as empty */
#define setempty(v) settt_(v, LUA_TEMPTY)
/* }================================================================== */ /* }================================================================== */
@ -644,7 +668,7 @@ typedef struct Table {
/* /*
** Use a "nil table" to mark dead keys in a table. Those keys serve ** Use a "nil table" to mark dead keys in a table. Those keys serve
** only to keep space for removed entries, which may still be part of ** to keep space for removed entries, which may still be part of
** chains. Note that the 'keytt' does not have the BIT_ISCOLLECTABLE ** chains. Note that the 'keytt' does not have the BIT_ISCOLLECTABLE
** set, so these values are considered not collectable and are different ** set, so these values are considered not collectable and are different
** from any valid value. ** from any valid value.

@ -1,5 +1,5 @@
/* /*
** $Id: ltable.c,v 2.132 2018/02/19 20:06:56 roberto Exp roberto $ ** $Id: ltable.c,v 2.133 2018/02/21 12:54:26 roberto Exp roberto $
** Lua tables (hash) ** Lua tables (hash)
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -88,11 +88,14 @@
#define dummynode (&dummynode_) #define dummynode (&dummynode_)
static const Node dummynode_ = { static const Node dummynode_ = {
{{NULL}, LUA_TNIL, /* value's value and type */ {{NULL}, LUA_TEMPTY, /* value's value and type */
LUA_TNIL, 0, {NULL}} /* key type, next, and key value */ LUA_TNIL, 0, {NULL}} /* key type, next, and key value */
}; };
LUAI_DDEF const TValue luaH_emptyobject_ = {EMPTYCONSTANT};
/* /*
** Hash for floating-point numbers. ** Hash for floating-point numbers.
** The main computation should be just ** The main computation should be just
@ -200,7 +203,7 @@ static const TValue *getgeneric (Table *t, const TValue *key) {
else { else {
int nx = gnext(n); int nx = gnext(n);
if (nx == 0) if (nx == 0)
return luaO_nilobject; /* not found */ return luaH_emptyobject; /* not found */
n += nx; n += nx;
} }
} }
@ -232,7 +235,7 @@ static unsigned int findindex (lua_State *L, Table *t, TValue *key) {
return i; /* yes; that's the index */ return i; /* yes; that's the index */
else { else {
const TValue *n = getgeneric(t, key); const TValue *n = getgeneric(t, key);
if (n == luaO_nilobject) if (n == luaH_emptyobject)
luaG_runerror(L, "invalid key to 'next'"); /* key not found */ luaG_runerror(L, "invalid key to 'next'"); /* key not found */
i = cast_int(nodefromval(n) - gnode(t, 0)); /* key index in hash table */ i = cast_int(nodefromval(n) - gnode(t, 0)); /* key index in hash table */
/* hash elements are numbered after array ones */ /* hash elements are numbered after array ones */
@ -244,14 +247,14 @@ static unsigned int findindex (lua_State *L, Table *t, TValue *key) {
int luaH_next (lua_State *L, Table *t, StkId key) { int luaH_next (lua_State *L, Table *t, StkId key) {
unsigned int i = findindex(L, t, s2v(key)); /* find original element */ unsigned int i = findindex(L, t, s2v(key)); /* find original element */
for (; i < t->sizearray; i++) { /* try first array part */ for (; i < t->sizearray; i++) { /* try first array part */
if (!ttisnil(&t->array[i])) { /* a non-nil value? */ if (!isempty(&t->array[i])) { /* a non-empty entry? */
setivalue(s2v(key), i + 1); setivalue(s2v(key), i + 1);
setobj2s(L, key + 1, &t->array[i]); setobj2s(L, key + 1, &t->array[i]);
return 1; return 1;
} }
} }
for (i -= t->sizearray; cast_int(i) < sizenode(t); i++) { /* hash part */ for (i -= t->sizearray; cast_int(i) < sizenode(t); i++) { /* hash part */
if (!ttisnil(gval(gnode(t, i)))) { /* a non-nil value? */ if (!isempty(gval(gnode(t, i)))) { /* a non-empty entry? */
Node *n = gnode(t, i); Node *n = gnode(t, i);
getnodekey(L, s2v(key), n); getnodekey(L, s2v(key), n);
setobj2s(L, key + 1, gval(n)); setobj2s(L, key + 1, gval(n));
@ -336,7 +339,7 @@ static unsigned int numusearray (const Table *t, unsigned int *nums) {
} }
/* count elements in range (2^(lg - 1), 2^lg] */ /* count elements in range (2^(lg - 1), 2^lg] */
for (; i <= lim; i++) { for (; i <= lim; i++) {
if (!ttisnil(&t->array[i-1])) if (!isempty(&t->array[i-1]))
lc++; lc++;
} }
nums[lg] += lc; nums[lg] += lc;
@ -352,7 +355,7 @@ static int numusehash (const Table *t, unsigned int *nums, unsigned int *pna) {
int i = sizenode(t); int i = sizenode(t);
while (i--) { while (i--) {
Node *n = &t->node[i]; Node *n = &t->node[i];
if (!ttisnil(gval(n))) { if (!isempty(gval(n))) {
if (keyisinteger(n)) if (keyisinteger(n))
ause += countint(keyival(n), nums); ause += countint(keyival(n), nums);
totaluse++; totaluse++;
@ -387,7 +390,7 @@ static void setnodevector (lua_State *L, Table *t, unsigned int size) {
Node *n = gnode(t, i); Node *n = gnode(t, i);
gnext(n) = 0; gnext(n) = 0;
setnilkey(n); setnilkey(n);
setnilvalue(gval(n)); setempty(gval(n));
} }
t->lsizenode = cast_byte(lsize); t->lsizenode = cast_byte(lsize);
t->lastfree = gnode(t, size); /* all positions are free */ t->lastfree = gnode(t, size); /* all positions are free */
@ -403,7 +406,7 @@ static void reinsert (lua_State *L, Table *ot, Table *t) {
int size = sizenode(ot); int size = sizenode(ot);
for (j = 0; j < size; j++) { for (j = 0; j < size; j++) {
Node *old = gnode(ot, j); Node *old = gnode(ot, j);
if (!ttisnil(gval(old))) { if (!isempty(gval(old))) {
/* doesn't need barrier/invalidate cache, as entry was /* doesn't need barrier/invalidate cache, as entry was
already present in the table */ already present in the table */
TValue k; TValue k;
@ -456,7 +459,7 @@ void luaH_resize (lua_State *L, Table *t, unsigned int newasize,
exchangehashpart(t, &newt); /* and new hash */ exchangehashpart(t, &newt); /* and new hash */
/* re-insert into the new hash the elements from vanishing slice */ /* re-insert into the new hash the elements from vanishing slice */
for (i = newasize; i < oldasize; i++) { for (i = newasize; i < oldasize; i++) {
if (!ttisnil(&t->array[i])) if (!isempty(&t->array[i]))
luaH_setint(L, t, i + 1, &t->array[i]); luaH_setint(L, t, i + 1, &t->array[i]);
} }
t->sizearray = oldasize; /* restore current size... */ t->sizearray = oldasize; /* restore current size... */
@ -473,7 +476,7 @@ void luaH_resize (lua_State *L, Table *t, unsigned int newasize,
t->array = newarray; /* set new array part */ t->array = newarray; /* set new array part */
t->sizearray = newasize; t->sizearray = newasize;
for (i = oldasize; i < newasize; i++) /* clear new slice of the array */ for (i = oldasize; i < newasize; i++) /* clear new slice of the array */
setnilvalue(&t->array[i]); setempty(&t->array[i]);
/* re-insert elements from old hash part into new parts */ /* re-insert elements from old hash part into new parts */
reinsert(L, &newt, t); /* 'newt' now has the old hash */ reinsert(L, &newt, t); /* 'newt' now has the old hash */
freehash(L, &newt); /* free old hash part */ freehash(L, &newt); /* free old hash part */
@ -569,7 +572,7 @@ TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key) {
luaG_runerror(L, "table index is NaN"); luaG_runerror(L, "table index is NaN");
} }
mp = mainpositionTV(t, key); mp = mainpositionTV(t, key);
if (!ttisnil(gval(mp)) || isdummy(t)) { /* main position is taken? */ if (!isempty(gval(mp)) || isdummy(t)) { /* main position is taken? */
Node *othern; Node *othern;
Node *f = getfreepos(t); /* get a free place */ Node *f = getfreepos(t); /* get a free place */
if (f == NULL) { /* cannot find a free place? */ if (f == NULL) { /* cannot find a free place? */
@ -589,7 +592,7 @@ TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key) {
gnext(f) += cast_int(mp - f); /* correct 'next' */ gnext(f) += cast_int(mp - f); /* correct 'next' */
gnext(mp) = 0; /* now 'mp' is free */ gnext(mp) = 0; /* now 'mp' is free */
} }
setnilvalue(gval(mp)); setempty(gval(mp));
} }
else { /* colliding node is in its own main position */ else { /* colliding node is in its own main position */
/* new node will go into free position */ /* new node will go into free position */
@ -602,7 +605,7 @@ TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key) {
} }
setnodekey(L, mp, key); setnodekey(L, mp, key);
luaC_barrierback(L, obj2gco(t), key); luaC_barrierback(L, obj2gco(t), key);
lua_assert(ttisnil(gval(mp))); lua_assert(isempty(gval(mp)));
return gval(mp); return gval(mp);
} }
@ -625,7 +628,7 @@ const TValue *luaH_getint (Table *t, lua_Integer key) {
n += nx; n += nx;
} }
} }
return luaO_nilobject; return luaH_emptyobject;
} }
} }
@ -642,7 +645,7 @@ const TValue *luaH_getshortstr (Table *t, TString *key) {
else { else {
int nx = gnext(n); int nx = gnext(n);
if (nx == 0) if (nx == 0)
return luaO_nilobject; /* not found */ return luaH_emptyobject; /* not found */
n += nx; n += nx;
} }
} }
@ -667,7 +670,7 @@ const TValue *luaH_get (Table *t, const TValue *key) {
switch (ttype(key)) { switch (ttype(key)) {
case LUA_TSHRSTR: return luaH_getshortstr(t, tsvalue(key)); case LUA_TSHRSTR: return luaH_getshortstr(t, tsvalue(key));
case LUA_TNUMINT: return luaH_getint(t, ivalue(key)); case LUA_TNUMINT: return luaH_getint(t, ivalue(key));
case LUA_TNIL: return luaO_nilobject; case LUA_TNIL: return luaH_emptyobject;
case LUA_TNUMFLT: { case LUA_TNUMFLT: {
lua_Integer k; lua_Integer k;
if (luaV_flttointeger(fltvalue(key), &k, 0)) /* index is an integral? */ if (luaV_flttointeger(fltvalue(key), &k, 0)) /* index is an integral? */
@ -686,7 +689,7 @@ const TValue *luaH_get (Table *t, const TValue *key) {
*/ */
TValue *luaH_set (lua_State *L, Table *t, const TValue *key) { TValue *luaH_set (lua_State *L, Table *t, const TValue *key) {
const TValue *p = luaH_get(t, key); const TValue *p = luaH_get(t, key);
if (p != luaO_nilobject) if (p != luaH_emptyobject)
return cast(TValue *, p); return cast(TValue *, p);
else return luaH_newkey(L, t, key); else return luaH_newkey(L, t, key);
} }
@ -695,7 +698,7 @@ TValue *luaH_set (lua_State *L, Table *t, const TValue *key) {
void luaH_setint (lua_State *L, Table *t, lua_Integer key, TValue *value) { void luaH_setint (lua_State *L, Table *t, lua_Integer key, TValue *value) {
const TValue *p = luaH_getint(t, key); const TValue *p = luaH_getint(t, key);
TValue *cell; TValue *cell;
if (p != luaO_nilobject) if (p != luaH_emptyobject)
cell = cast(TValue *, p); cell = cast(TValue *, p);
else { else {
TValue k; TValue k;
@ -728,16 +731,16 @@ static lua_Unsigned hash_search (Table *t, lua_Unsigned j) {
j *= 2; j *= 2;
else { else {
j = LUA_MAXINTEGER; j = LUA_MAXINTEGER;
if (ttisnil(luaH_getint(t, j))) /* t[j] == nil? */ if (isempty(luaH_getint(t, j))) /* t[j] not present? */
break; /* 'j' now is an absent index */ break; /* 'j' now is an absent index */
else /* weird case */ else /* weird case */
return j; /* well, max integer is a boundary... */ return j; /* well, max integer is a boundary... */
} }
} while (!ttisnil(luaH_getint(t, j))); /* repeat until t[j] == nil */ } while (!isempty(luaH_getint(t, j))); /* repeat until an absent t[j] */
/* i < j && t[i] !≃ nil && t[j] == nil */ /* i < j && t[i] present && t[j] absent */
while (j - i > 1u) { /* do a binary search between them */ while (j - i > 1u) { /* do a binary search between them */
lua_Unsigned m = (i + j) / 2; lua_Unsigned m = (i + j) / 2;
if (ttisnil(luaH_getint(t, m))) j = m; if (isempty(luaH_getint(t, m))) j = m;
else i = m; else i = m;
} }
return i; return i;
@ -746,27 +749,27 @@ static lua_Unsigned hash_search (Table *t, lua_Unsigned j) {
/* /*
** Try to find a boundary in table 't'. (A 'boundary' is an integer index ** 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, plus 0 if t[1] is nil ** such that t[i] is present and t[i+1] is absent, or 0 if t[1] is absent
** and 'maxinteger' if t[maxinteger] is not nil.) ** and 'maxinteger' if t[maxinteger] is present.)
** First, try the array part: if there is an array part and its last ** First, try the array part: if there is an array part and its last
** element is nil, there must be a boundary there; a binary search ** element is absent, there must be a boundary there; a binary search
** finds that boundary. Otherwise, if the hash part is empty or does not ** finds that boundary. Otherwise, if the hash part is empty or does not
** contain 'j + 1', 'j' is a boundary. Otherwize, call 'hash_search' ** contain 'j + 1', 'j' is a boundary. Otherwize, call 'hash_search'
** to find a boundary in the hash part. ** to find a boundary in the hash part.
*/ */
lua_Unsigned luaH_getn (Table *t) { lua_Unsigned luaH_getn (Table *t) {
unsigned int j = t->sizearray; unsigned int j = t->sizearray;
if (j > 0 && ttisnil(&t->array[j - 1])) { if (j > 0 && isempty(&t->array[j - 1])) {
unsigned int i = 0; unsigned int i = 0;
while (j - i > 1u) { /* binary search */ while (j - i > 1u) { /* binary search */
unsigned int m = (i + j) / 2; unsigned int m = (i + j) / 2;
if (ttisnil(&t->array[m - 1])) j = m; if (isempty(&t->array[m - 1])) j = m;
else i = m; else i = m;
} }
return i; return i;
} }
else { /* 'j' is zero or present in table */ else { /* 'j' is zero or present in table */
if (isdummy(t) || ttisnil(luaH_getint(t, l_castU2S(j + 1)))) if (isdummy(t) || isempty(luaH_getint(t, l_castU2S(j + 1))))
return j; /* 'j + 1' is absent... */ return j; /* 'j + 1' is absent... */
else /* 'j + 1' is also present */ else /* 'j + 1' is also present */
return hash_search(t, j); return hash_search(t, j);

@ -1,5 +1,5 @@
/* /*
** $Id: ltable.h,v 2.24 2017/05/19 12:48:15 roberto Exp roberto $ ** $Id: ltable.h,v 2.25 2017/06/09 16:48:44 roberto Exp roberto $
** Lua tables (hash) ** Lua tables (hash)
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -21,6 +21,8 @@
/* true when 't' is using 'dummynode' as its hash part */ /* true when 't' is using 'dummynode' as its hash part */
#define isdummy(t) ((t)->lastfree == NULL) #define isdummy(t) ((t)->lastfree == NULL)
#define luaH_emptyobject (&luaH_emptyobject_)
/* allocated size for hash nodes */ /* allocated size for hash nodes */
#define allocsizenode(t) (isdummy(t) ? 0 : sizenode(t)) #define allocsizenode(t) (isdummy(t) ? 0 : sizenode(t))
@ -30,6 +32,9 @@
#define nodefromval(v) cast(Node *, (v)) #define nodefromval(v) cast(Node *, (v))
LUAI_DDEC const TValue luaH_emptyobject_;
LUAI_FUNC const TValue *luaH_getint (Table *t, lua_Integer key); LUAI_FUNC const TValue *luaH_getint (Table *t, lua_Integer key);
LUAI_FUNC void luaH_setint (lua_State *L, Table *t, lua_Integer key, LUAI_FUNC void luaH_setint (lua_State *L, Table *t, lua_Integer key,
TValue *value); TValue *value);

@ -1,5 +1,5 @@
/* /*
** $Id: ltests.c,v 2.240 2018/01/28 15:13:26 roberto Exp roberto $ ** $Id: ltests.c,v 2.241 2018/02/20 16:52:50 roberto Exp roberto $
** Internal Module for Debugging of the Lua Implementation ** Internal Module for Debugging of the Lua Implementation
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -254,7 +254,7 @@ static void checktable (global_State *g, Table *h) {
for (i = 0; i < h->sizearray; i++) for (i = 0; i < h->sizearray; i++)
checkvalref(g, hgc, &h->array[i]); checkvalref(g, hgc, &h->array[i]);
for (n = gnode(h, 0); n < limit; n++) { for (n = gnode(h, 0); n < limit; n++) {
if (!ttisnil(gval(n))) { if (!isempty(gval(n))) {
TValue k; TValue k;
getnodekey(g->mainthread, &k, n); getnodekey(g->mainthread, &k, n);
lua_assert(!keyisnil(n)); lua_assert(!keyisnil(n));
@ -842,7 +842,7 @@ static int table_query (lua_State *L) {
else if ((i -= t->sizearray) < sizenode(t)) { else if ((i -= t->sizearray) < sizenode(t)) {
TValue k; TValue k;
getnodekey(L, &k, gnode(t, i)); getnodekey(L, &k, gnode(t, i));
if (!ttisnil(gval(gnode(t, i))) || if (!isempty(gval(gnode(t, i))) ||
ttisnil(&k) || ttisnil(&k) ||
ttisnumber(&k)) { ttisnumber(&k)) {
pushobject(L, &k); pushobject(L, &k);

8
ltm.c

@ -1,5 +1,5 @@
/* /*
** $Id: ltm.c,v 2.62 2018/02/17 19:20:00 roberto Exp roberto $ ** $Id: ltm.c,v 2.63 2018/02/21 15:49:32 roberto Exp roberto $
** Tag methods ** Tag methods
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -60,7 +60,7 @@ void luaT_init (lua_State *L) {
const TValue *luaT_gettm (Table *events, TMS event, TString *ename) { const TValue *luaT_gettm (Table *events, TMS event, TString *ename) {
const TValue *tm = luaH_getshortstr(events, ename); const TValue *tm = luaH_getshortstr(events, ename);
lua_assert(event <= TM_EQ); lua_assert(event <= TM_EQ);
if (ttisnil(tm)) { /* no tag method? */ if (notm(tm)) { /* no tag method? */
events->flags |= cast_byte(1u<<event); /* cache this fact */ events->flags |= cast_byte(1u<<event); /* cache this fact */
return NULL; return NULL;
} }
@ -137,9 +137,9 @@ void luaT_callTMres (lua_State *L, const TValue *f, const TValue *p1,
static int callbinTM (lua_State *L, const TValue *p1, const TValue *p2, static int callbinTM (lua_State *L, const TValue *p1, const TValue *p2,
StkId res, TMS event) { StkId res, TMS event) {
const TValue *tm = luaT_gettmbyobj(L, p1, event); /* try first operand */ const TValue *tm = luaT_gettmbyobj(L, p1, event); /* try first operand */
if (ttisnil(tm)) if (notm(tm))
tm = luaT_gettmbyobj(L, p2, event); /* try second operand */ tm = luaT_gettmbyobj(L, p2, event); /* try second operand */
if (ttisnil(tm)) return 0; if (notm(tm)) return 0;
luaT_callTMres(L, tm, p1, p2, res); luaT_callTMres(L, tm, p1, p2, res);
return 1; return 1;
} }

8
ltm.h

@ -1,5 +1,5 @@
/* /*
** $Id: ltm.h,v 2.31 2018/02/09 15:16:06 roberto Exp roberto $ ** $Id: ltm.h,v 2.32 2018/02/17 19:20:00 roberto Exp roberto $
** Tag methods ** Tag methods
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -45,6 +45,12 @@ typedef enum {
} TMS; } TMS;
/*
** Test whether there is no tagmethod.
** (Because tagmethods use raw accesses, the result may be an "empty" nil.)
*/
#define notm(tm) ttisnilorempty(tm)
#define gfasttm(g,et,e) ((et) == NULL ? NULL : \ #define gfasttm(g,et,e) ((et) == NULL ? NULL : \
((et)->flags & (1u<<(e))) ? NULL : luaT_gettm(et, e, (g)->tmname[e])) ((et)->flags & (1u<<(e))) ? NULL : luaT_gettm(et, e, (g)->tmname[e]))

21
lvm.c

@ -1,5 +1,5 @@
/* /*
** $Id: lvm.c,v 2.345 2018/02/21 15:49:32 roberto Exp roberto $ ** $Id: lvm.c,v 2.346 2018/02/21 19:43:44 roberto Exp roberto $
** Lua virtual machine ** Lua virtual machine
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -168,7 +168,7 @@ static int forlimit (const TValue *obj, lua_Integer *p, lua_Integer step,
/* /*
** Finish the table access 'val = t[key]'. ** Finish the table access 'val = t[key]'.
** if 'slot' is NULL, 't' is not a table; otherwise, 'slot' points to ** if 'slot' is NULL, 't' is not a table; otherwise, 'slot' points to
** t[k] entry (which must be nil). ** t[k] entry (which must be empty).
*/ */
void luaV_finishget (lua_State *L, const TValue *t, TValue *key, StkId val, void luaV_finishget (lua_State *L, const TValue *t, TValue *key, StkId val,
const TValue *slot) { const TValue *slot) {
@ -178,12 +178,12 @@ void luaV_finishget (lua_State *L, const TValue *t, TValue *key, StkId val,
if (slot == NULL) { /* 't' is not a table? */ if (slot == NULL) { /* 't' is not a table? */
lua_assert(!ttistable(t)); lua_assert(!ttistable(t));
tm = luaT_gettmbyobj(L, t, TM_INDEX); tm = luaT_gettmbyobj(L, t, TM_INDEX);
if (ttisnil(tm)) if (notm(tm))
luaG_typeerror(L, t, "index"); /* no metamethod */ luaG_typeerror(L, t, "index"); /* no metamethod */
/* else will try the metamethod */ /* else will try the metamethod */
} }
else { /* 't' is a table */ else { /* 't' is a table */
lua_assert(ttisnil(slot)); lua_assert(isempty(slot));
tm = fasttm(L, hvalue(t)->metatable, TM_INDEX); /* table's metamethod */ tm = fasttm(L, hvalue(t)->metatable, TM_INDEX); /* table's metamethod */
if (tm == NULL) { /* no metamethod? */ if (tm == NULL) { /* no metamethod? */
setnilvalue(s2v(val)); /* result is nil */ setnilvalue(s2v(val)); /* result is nil */
@ -209,8 +209,8 @@ void luaV_finishget (lua_State *L, const TValue *t, TValue *key, StkId val,
/* /*
** Finish a table assignment 't[key] = val'. ** Finish a table assignment 't[key] = val'.
** If 'slot' is NULL, 't' is not a table. Otherwise, 'slot' points ** If 'slot' is NULL, 't' is not a table. Otherwise, 'slot' points
** to the entry 't[key]', or to 'luaO_nilobject' if there is no such ** to the entry 't[key]', or to 'luaH_emptyobject' if there is no such
** entry. (The value at 'slot' must be nil, otherwise 'luaV_fastget' ** entry. (The value at 'slot' must be empty, otherwise 'luaV_fastget'
** would have done the job.) ** would have done the job.)
*/ */
void luaV_finishset (lua_State *L, const TValue *t, TValue *key, void luaV_finishset (lua_State *L, const TValue *t, TValue *key,
@ -220,10 +220,10 @@ void luaV_finishset (lua_State *L, const TValue *t, TValue *key,
const TValue *tm; /* '__newindex' metamethod */ const TValue *tm; /* '__newindex' metamethod */
if (slot != NULL) { /* is 't' a table? */ if (slot != NULL) { /* is 't' a table? */
Table *h = hvalue(t); /* save 't' table */ Table *h = hvalue(t); /* save 't' table */
lua_assert(ttisnil(slot)); /* old value must be nil */ lua_assert(isempty(slot)); /* slot must be empty */
tm = fasttm(L, h->metatable, TM_NEWINDEX); /* get metamethod */ tm = fasttm(L, h->metatable, TM_NEWINDEX); /* get metamethod */
if (tm == NULL) { /* no metamethod? */ if (tm == NULL) { /* no metamethod? */
if (slot == luaO_nilobject) /* no previous entry? */ if (slot == luaH_emptyobject) /* no previous entry? */
slot = luaH_newkey(L, h, key); /* create one */ slot = luaH_newkey(L, h, key); /* create one */
/* no metamethod and (now) there is an entry with given key */ /* no metamethod and (now) there is an entry with given key */
setobj2t(L, cast(TValue *, slot), val); /* set its new value */ setobj2t(L, cast(TValue *, slot), val); /* set its new value */
@ -234,7 +234,8 @@ void luaV_finishset (lua_State *L, const TValue *t, TValue *key,
/* else will try the metamethod */ /* else will try the metamethod */
} }
else { /* not a table; check metamethod */ else { /* not a table; check metamethod */
if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_NEWINDEX))) tm = luaT_gettmbyobj(L, t, TM_NEWINDEX);
if (notm(tm))
luaG_typeerror(L, t, "index"); luaG_typeerror(L, t, "index");
} }
/* try the metamethod */ /* try the metamethod */
@ -586,7 +587,7 @@ void luaV_objlen (lua_State *L, StkId ra, const TValue *rb) {
} }
default: { /* try metamethod */ default: { /* try metamethod */
tm = luaT_gettmbyobj(L, rb, TM_LEN); tm = luaT_gettmbyobj(L, rb, TM_LEN);
if (ttisnil(tm)) /* no metamethod? */ if (notm(tm)) /* no metamethod? */
luaG_typeerror(L, rb, "get length of"); luaG_typeerror(L, rb, "get length of");
break; break;
} }

10
lvm.h

@ -1,5 +1,5 @@
/* /*
** $Id: lvm.h,v 2.49 2018/02/19 20:06:56 roberto Exp roberto $ ** $Id: lvm.h,v 2.50 2018/02/21 12:54:26 roberto Exp roberto $
** Lua virtual machine ** Lua virtual machine
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -64,17 +64,17 @@
/* /*
** fast track for 'gettable': if 't' is a table and 't[k]' is not nil, ** fast track for 'gettable': if 't' is a table and 't[k]' is present,
** return 1 with 'slot' pointing to 't[k]' (position of final result). ** return 1 with 'slot' pointing to 't[k]' (position of final result).
** Otherwise, return 0 (meaning it will have to check metamethod) ** Otherwise, return 0 (meaning it will have to check metamethod)
** with 'slot' pointing to a nil 't[k]' (if 't' is a table) or NULL ** with 'slot' pointing to an empty 't[k]' (if 't' is a table) or NULL
** (otherwise). 'f' is the raw get function to use. ** (otherwise). 'f' is the raw get function to use.
*/ */
#define luaV_fastget(L,t,k,slot,f) \ #define luaV_fastget(L,t,k,slot,f) \
(!ttistable(t) \ (!ttistable(t) \
? (slot = NULL, 0) /* not a table; 'slot' is NULL and result is 0 */ \ ? (slot = NULL, 0) /* not a table; 'slot' is NULL and result is 0 */ \
: (slot = f(hvalue(t), k), /* else, do raw access */ \ : (slot = f(hvalue(t), k), /* else, do raw access */ \
!ttisnil(slot))) /* result not nil? */ !isempty(slot))) /* result not empty? */
/* /*
@ -86,7 +86,7 @@
? (slot = NULL, 0) /* not a table; 'slot' is NULL and result is 0 */ \ ? (slot = NULL, 0) /* not a table; 'slot' is NULL and result is 0 */ \
: (slot = (l_castS2U(k) - 1u < hvalue(t)->sizearray) \ : (slot = (l_castS2U(k) - 1u < hvalue(t)->sizearray) \
? &hvalue(t)->array[k - 1] : luaH_getint(hvalue(t), k), \ ? &hvalue(t)->array[k - 1] : luaH_getint(hvalue(t), k), \
!ttisnil(slot))) /* result not nil? */ !isempty(slot))) /* result not empty? */
/* /*