mirror of
https://github.com/lua/lua
synced 2025-03-25 23:22:51 +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:
parent
477ca2fe8c
commit
9243c414d9
30
lapi.c
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
|
||||
** 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) {
|
||||
TValue *t;
|
||||
const TValue *val;
|
||||
lua_lock(L);
|
||||
t = index2value(L, idx);
|
||||
api_check(L, ttistable(t), "table expected");
|
||||
setobj2s(L, L->top - 1, luaH_get(hvalue(t), s2v(L->top - 1)));
|
||||
lua_unlock(L);
|
||||
return ttnov(s2v(L->top - 1));
|
||||
val = luaH_get(hvalue(t), s2v(L->top - 1));
|
||||
L->top--; /* remove key */
|
||||
return finishrawget(L, val);
|
||||
}
|
||||
|
||||
|
||||
@ -673,10 +685,7 @@ LUA_API int lua_rawgeti (lua_State *L, int idx, lua_Integer n) {
|
||||
lua_lock(L);
|
||||
t = index2value(L, idx);
|
||||
api_check(L, ttistable(t), "table expected");
|
||||
setobj2s(L, L->top, luaH_getint(hvalue(t), n));
|
||||
api_incr_top(L);
|
||||
lua_unlock(L);
|
||||
return ttnov(s2v(L->top - 1));
|
||||
return finishrawget(L, luaH_getint(hvalue(t), n));
|
||||
}
|
||||
|
||||
|
||||
@ -687,10 +696,7 @@ LUA_API int lua_rawgetp (lua_State *L, int idx, const void *p) {
|
||||
t = index2value(L, idx);
|
||||
api_check(L, ttistable(t), "table expected");
|
||||
setpvalue(&k, cast_voidp(p));
|
||||
setobj2s(L, L->top, luaH_get(hvalue(t), &k));
|
||||
api_incr_top(L);
|
||||
lua_unlock(L);
|
||||
return ttnov(s2v(L->top - 1));
|
||||
return finishrawget(L, luaH_get(hvalue(t), &k));
|
||||
}
|
||||
|
||||
|
||||
|
58
lgc.c
58
lgc.c
@ -145,12 +145,13 @@ static GCObject **getgclist (GCObject *o) {
|
||||
** 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
|
||||
** collection of the key, but keeps its entry in the table (its removal
|
||||
** could break a chain). Other places never manipulate dead keys,
|
||||
** because its associated nil value is enough to signal that the entry
|
||||
** is logically empty.
|
||||
** could break a chain). The main feature of a dead key is that it must
|
||||
** be different from any other value, to do not disturb searches.
|
||||
** 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) {
|
||||
lua_assert(ttisnil(gval(n)));
|
||||
static void clearkey (Node *n) {
|
||||
lua_assert(isempty(gval(n)));
|
||||
if (keyiswhite(n))
|
||||
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) */
|
||||
int hasclears = (h->sizearray > 0);
|
||||
for (n = gnode(h, 0); n < limit; n++) { /* traverse hash part */
|
||||
if (ttisnil(gval(n))) /* entry is empty? */
|
||||
removeentry(n); /* remove it */
|
||||
if (isempty(gval(n))) /* entry is empty? */
|
||||
clearkey(n); /* clear its key */
|
||||
else {
|
||||
lua_assert(!keyisnil(n));
|
||||
markkey(g, n);
|
||||
@ -428,8 +429,8 @@ static int traverseephemeron (global_State *g, Table *h) {
|
||||
}
|
||||
/* traverse hash part */
|
||||
for (n = gnode(h, 0); n < limit; n++) {
|
||||
if (ttisnil(gval(n))) /* entry is empty? */
|
||||
removeentry(n); /* remove it */
|
||||
if (isempty(gval(n))) /* entry is empty? */
|
||||
clearkey(n); /* clear its key */
|
||||
else if (iscleared(g, gckeyN(n))) { /* key is not marked (yet)? */
|
||||
hasclears = 1; /* table must be cleared */
|
||||
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 */
|
||||
markvalue(g, &h->array[i]);
|
||||
for (n = gnode(h, 0); n < limit; n++) { /* traverse hash part */
|
||||
if (ttisnil(gval(n))) /* entry is empty? */
|
||||
removeentry(n); /* remove it */
|
||||
if (isempty(gval(n))) /* entry is empty? */
|
||||
clearkey(n); /* clear its key */
|
||||
else {
|
||||
lua_assert(!keyisnil(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'
|
||||
*/
|
||||
static void clearkeys (global_State *g, GCObject *l) {
|
||||
static void clearbykeys (global_State *g, GCObject *l) {
|
||||
for (; l; l = gco2t(l)->gclist) {
|
||||
Table *h = gco2t(l);
|
||||
Node *n, *limit = gnodelast(h);
|
||||
Node *limit = gnodelast(h);
|
||||
Node *n;
|
||||
for (n = gnode(h, 0); n < limit; n++) {
|
||||
if (!ttisnil(gval(n)) && (iscleared(g, gckeyN(n)))) /* unmarked key? */
|
||||
setnilvalue(gval(n)); /* clear value */
|
||||
if (ttisnil(gval(n))) /* is entry empty? */
|
||||
removeentry(n); /* remove it from table */
|
||||
if (isempty(gval(n))) /* is entry empty? */
|
||||
clearkey(n); /* clear its key */
|
||||
else if (iscleared(g, gckeyN(n))) /* unmarked key? */
|
||||
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
|
||||
** 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) {
|
||||
Table *h = gco2t(l);
|
||||
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++) {
|
||||
TValue *o = &h->array[i];
|
||||
if (iscleared(g, gcvalueN(o))) /* value was collected? */
|
||||
setnilvalue(o); /* remove value */
|
||||
setempty(o); /* remove entry */
|
||||
}
|
||||
for (n = gnode(h, 0); n < limit; n++) {
|
||||
if (iscleared(g, gcvalueN(gval(n)))) /* unmarked value? */
|
||||
setnilvalue(gval(n)); /* clear value */
|
||||
if (ttisnil(gval(n))) /* is entry empty? */
|
||||
removeentry(n); /* remove it from table */
|
||||
setempty(gval(n)); /* remove entry */
|
||||
if (isempty(gval(n))) /* is entry empty? */
|
||||
clearkey(n); /* clear its key */
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1372,8 +1374,8 @@ static lu_mem atomic (lua_State *L) {
|
||||
convergeephemerons(g);
|
||||
/* at this point, all strongly accessible objects are marked. */
|
||||
/* Clear values from weak tables, before checking finalizers */
|
||||
clearvalues(g, g->weak, NULL);
|
||||
clearvalues(g, g->allweak, NULL);
|
||||
clearbyvalues(g, g->weak, NULL);
|
||||
clearbyvalues(g, g->allweak, NULL);
|
||||
origweak = g->weak; origall = g->allweak;
|
||||
separatetobefnz(g, 0); /* separate objects to be finalized */
|
||||
work += markbeingfnz(g); /* mark objects that will be finalized */
|
||||
@ -1381,11 +1383,11 @@ static lu_mem atomic (lua_State *L) {
|
||||
convergeephemerons(g);
|
||||
/* at this point, all resurrected objects are marked. */
|
||||
/* remove dead objects from weak tables */
|
||||
clearkeys(g, g->ephemeron); /* clear keys from all ephemeron tables */
|
||||
clearkeys(g, g->allweak); /* clear keys from all 'allweak' tables */
|
||||
clearbykeys(g, g->ephemeron); /* clear keys from all ephemeron tables */
|
||||
clearbykeys(g, g->allweak); /* clear keys from all 'allweak' tables */
|
||||
/* clear values from resurrected weak tables */
|
||||
clearvalues(g, g->weak, origweak);
|
||||
clearvalues(g, g->allweak, origall);
|
||||
clearbyvalues(g, g->weak, origweak);
|
||||
clearbyvalues(g, g->allweak, origall);
|
||||
luaS_clearcache(g);
|
||||
clearprotolist(g);
|
||||
g->currentwhite = cast_byte(otherwhite(g)); /* flip current white */
|
||||
|
4
llex.c
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
|
||||
** 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 */
|
||||
setsvalue2s(L, L->top++, ts); /* temporarily anchor it in stack */
|
||||
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;
|
||||
table is not a metatable, so it does not need to invalidate cache */
|
||||
setbvalue(o, 1); /* t[string] = true */
|
||||
|
30
lobject.h
30
lobject.h
@ -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
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
@ -100,7 +100,7 @@ typedef struct TValue {
|
||||
#define setobj(L,obj1,obj2) \
|
||||
{ TValue *io1=(obj1); const TValue *io2=(obj2); \
|
||||
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
|
||||
@ -148,6 +148,30 @@ typedef StackValue *StkId; /* index to stack elements */
|
||||
/* (address of) a fixed nil value */
|
||||
#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
|
||||
** 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
|
||||
** set, so these values are considered not collectable and are different
|
||||
** from any valid value.
|
||||
|
63
ltable.c
63
ltable.c
@ -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)
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
@ -88,11 +88,14 @@
|
||||
#define dummynode (&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 */
|
||||
};
|
||||
|
||||
|
||||
LUAI_DDEF const TValue luaH_emptyobject_ = {EMPTYCONSTANT};
|
||||
|
||||
|
||||
/*
|
||||
** Hash for floating-point numbers.
|
||||
** The main computation should be just
|
||||
@ -200,7 +203,7 @@ static const TValue *getgeneric (Table *t, const TValue *key) {
|
||||
else {
|
||||
int nx = gnext(n);
|
||||
if (nx == 0)
|
||||
return luaO_nilobject; /* not found */
|
||||
return luaH_emptyobject; /* not found */
|
||||
n += nx;
|
||||
}
|
||||
}
|
||||
@ -232,7 +235,7 @@ static unsigned int findindex (lua_State *L, Table *t, TValue *key) {
|
||||
return i; /* yes; that's the index */
|
||||
else {
|
||||
const TValue *n = getgeneric(t, key);
|
||||
if (n == luaO_nilobject)
|
||||
if (n == luaH_emptyobject)
|
||||
luaG_runerror(L, "invalid key to 'next'"); /* key not found */
|
||||
i = cast_int(nodefromval(n) - gnode(t, 0)); /* key index in hash table */
|
||||
/* 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) {
|
||||
unsigned int i = findindex(L, t, s2v(key)); /* find original element */
|
||||
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);
|
||||
setobj2s(L, key + 1, &t->array[i]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
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);
|
||||
getnodekey(L, s2v(key), 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] */
|
||||
for (; i <= lim; i++) {
|
||||
if (!ttisnil(&t->array[i-1]))
|
||||
if (!isempty(&t->array[i-1]))
|
||||
lc++;
|
||||
}
|
||||
nums[lg] += lc;
|
||||
@ -352,7 +355,7 @@ static int numusehash (const Table *t, unsigned int *nums, unsigned int *pna) {
|
||||
int i = sizenode(t);
|
||||
while (i--) {
|
||||
Node *n = &t->node[i];
|
||||
if (!ttisnil(gval(n))) {
|
||||
if (!isempty(gval(n))) {
|
||||
if (keyisinteger(n))
|
||||
ause += countint(keyival(n), nums);
|
||||
totaluse++;
|
||||
@ -387,7 +390,7 @@ static void setnodevector (lua_State *L, Table *t, unsigned int size) {
|
||||
Node *n = gnode(t, i);
|
||||
gnext(n) = 0;
|
||||
setnilkey(n);
|
||||
setnilvalue(gval(n));
|
||||
setempty(gval(n));
|
||||
}
|
||||
t->lsizenode = cast_byte(lsize);
|
||||
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);
|
||||
for (j = 0; j < size; j++) {
|
||||
Node *old = gnode(ot, j);
|
||||
if (!ttisnil(gval(old))) {
|
||||
if (!isempty(gval(old))) {
|
||||
/* doesn't need barrier/invalidate cache, as entry was
|
||||
already present in the table */
|
||||
TValue k;
|
||||
@ -456,7 +459,7 @@ 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 (!ttisnil(&t->array[i]))
|
||||
if (!isempty(&t->array[i]))
|
||||
luaH_setint(L, t, i + 1, &t->array[i]);
|
||||
}
|
||||
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->sizearray = newasize;
|
||||
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 */
|
||||
reinsert(L, &newt, t); /* 'newt' now has the old hash */
|
||||
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");
|
||||
}
|
||||
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 *f = getfreepos(t); /* get 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(mp) = 0; /* now 'mp' is free */
|
||||
}
|
||||
setnilvalue(gval(mp));
|
||||
setempty(gval(mp));
|
||||
}
|
||||
else { /* colliding node is in its own main 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);
|
||||
luaC_barrierback(L, obj2gco(t), key);
|
||||
lua_assert(ttisnil(gval(mp)));
|
||||
lua_assert(isempty(gval(mp)));
|
||||
return gval(mp);
|
||||
}
|
||||
|
||||
@ -625,7 +628,7 @@ const TValue *luaH_getint (Table *t, lua_Integer key) {
|
||||
n += nx;
|
||||
}
|
||||
}
|
||||
return luaO_nilobject;
|
||||
return luaH_emptyobject;
|
||||
}
|
||||
}
|
||||
|
||||
@ -642,7 +645,7 @@ const TValue *luaH_getshortstr (Table *t, TString *key) {
|
||||
else {
|
||||
int nx = gnext(n);
|
||||
if (nx == 0)
|
||||
return luaO_nilobject; /* not found */
|
||||
return luaH_emptyobject; /* not found */
|
||||
n += nx;
|
||||
}
|
||||
}
|
||||
@ -667,7 +670,7 @@ const TValue *luaH_get (Table *t, const TValue *key) {
|
||||
switch (ttype(key)) {
|
||||
case LUA_TSHRSTR: return luaH_getshortstr(t, tsvalue(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: {
|
||||
lua_Integer k;
|
||||
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) {
|
||||
const TValue *p = luaH_get(t, key);
|
||||
if (p != luaO_nilobject)
|
||||
if (p != luaH_emptyobject)
|
||||
return cast(TValue *, p);
|
||||
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) {
|
||||
const TValue *p = luaH_getint(t, key);
|
||||
TValue *cell;
|
||||
if (p != luaO_nilobject)
|
||||
if (p != luaH_emptyobject)
|
||||
cell = cast(TValue *, p);
|
||||
else {
|
||||
TValue k;
|
||||
@ -728,16 +731,16 @@ static lua_Unsigned hash_search (Table *t, lua_Unsigned j) {
|
||||
j *= 2;
|
||||
else {
|
||||
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 */
|
||||
else /* weird case */
|
||||
return j; /* well, max integer is a boundary... */
|
||||
}
|
||||
} while (!ttisnil(luaH_getint(t, j))); /* repeat until t[j] == nil */
|
||||
/* i < j && t[i] !≃ nil && t[j] == nil */
|
||||
} while (!isempty(luaH_getint(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 (ttisnil(luaH_getint(t, m))) j = m;
|
||||
if (isempty(luaH_getint(t, m))) j = m;
|
||||
else i = m;
|
||||
}
|
||||
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
|
||||
** such that t[i] is non-nil and t[i+1] is nil, plus 0 if t[1] is nil
|
||||
** and 'maxinteger' if t[maxinteger] is not 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 present.)
|
||||
** 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
|
||||
** contain 'j + 1', 'j' is a boundary. Otherwize, call 'hash_search'
|
||||
** to find a boundary in the hash part.
|
||||
*/
|
||||
lua_Unsigned luaH_getn (Table *t) {
|
||||
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;
|
||||
while (j - i > 1u) { /* binary search */
|
||||
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;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
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... */
|
||||
else /* 'j + 1' is also present */
|
||||
return hash_search(t, j);
|
||||
|
7
ltable.h
7
ltable.h
@ -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)
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
@ -21,6 +21,8 @@
|
||||
/* true when 't' is using 'dummynode' as its hash part */
|
||||
#define isdummy(t) ((t)->lastfree == NULL)
|
||||
|
||||
#define luaH_emptyobject (&luaH_emptyobject_)
|
||||
|
||||
|
||||
/* allocated size for hash nodes */
|
||||
#define allocsizenode(t) (isdummy(t) ? 0 : sizenode(t))
|
||||
@ -30,6 +32,9 @@
|
||||
#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 void luaH_setint (lua_State *L, Table *t, lua_Integer key,
|
||||
TValue *value);
|
||||
|
6
ltests.c
6
ltests.c
@ -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
|
||||
** 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++)
|
||||
checkvalref(g, hgc, &h->array[i]);
|
||||
for (n = gnode(h, 0); n < limit; n++) {
|
||||
if (!ttisnil(gval(n))) {
|
||||
if (!isempty(gval(n))) {
|
||||
TValue k;
|
||||
getnodekey(g->mainthread, &k, n);
|
||||
lua_assert(!keyisnil(n));
|
||||
@ -842,7 +842,7 @@ static int table_query (lua_State *L) {
|
||||
else if ((i -= t->sizearray) < sizenode(t)) {
|
||||
TValue k;
|
||||
getnodekey(L, &k, gnode(t, i));
|
||||
if (!ttisnil(gval(gnode(t, i))) ||
|
||||
if (!isempty(gval(gnode(t, i))) ||
|
||||
ttisnil(&k) ||
|
||||
ttisnumber(&k)) {
|
||||
pushobject(L, &k);
|
||||
|
8
ltm.c
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
|
||||
** 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 *tm = luaH_getshortstr(events, ename);
|
||||
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 */
|
||||
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,
|
||||
StkId res, TMS event) {
|
||||
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 */
|
||||
if (ttisnil(tm)) return 0;
|
||||
if (notm(tm)) return 0;
|
||||
luaT_callTMres(L, tm, p1, p2, res);
|
||||
return 1;
|
||||
}
|
||||
|
8
ltm.h
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
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
@ -45,6 +45,12 @@ typedef enum {
|
||||
} 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 : \
|
||||
((et)->flags & (1u<<(e))) ? NULL : luaT_gettm(et, e, (g)->tmname[e]))
|
||||
|
21
lvm.c
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
|
||||
** 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]'.
|
||||
** 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,
|
||||
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? */
|
||||
lua_assert(!ttistable(t));
|
||||
tm = luaT_gettmbyobj(L, t, TM_INDEX);
|
||||
if (ttisnil(tm))
|
||||
if (notm(tm))
|
||||
luaG_typeerror(L, t, "index"); /* no metamethod */
|
||||
/* else will try the metamethod */
|
||||
}
|
||||
else { /* 't' is a table */
|
||||
lua_assert(ttisnil(slot));
|
||||
lua_assert(isempty(slot));
|
||||
tm = fasttm(L, hvalue(t)->metatable, TM_INDEX); /* table's metamethod */
|
||||
if (tm == NULL) { /* no metamethod? */
|
||||
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'.
|
||||
** 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
|
||||
** entry. (The value at 'slot' must be nil, otherwise 'luaV_fastget'
|
||||
** to the entry 't[key]', or to 'luaH_emptyobject' if there is no such
|
||||
** entry. (The value at 'slot' must be empty, otherwise 'luaV_fastget'
|
||||
** would have done the job.)
|
||||
*/
|
||||
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 */
|
||||
if (slot != NULL) { /* is 't' a 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 */
|
||||
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 */
|
||||
/* no metamethod and (now) there is an entry with given key */
|
||||
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 { /* 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");
|
||||
}
|
||||
/* try the metamethod */
|
||||
@ -586,7 +587,7 @@ void luaV_objlen (lua_State *L, StkId ra, const TValue *rb) {
|
||||
}
|
||||
default: { /* try metamethod */
|
||||
tm = luaT_gettmbyobj(L, rb, TM_LEN);
|
||||
if (ttisnil(tm)) /* no metamethod? */
|
||||
if (notm(tm)) /* no metamethod? */
|
||||
luaG_typeerror(L, rb, "get length of");
|
||||
break;
|
||||
}
|
||||
|
10
lvm.h
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
|
||||
** 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).
|
||||
** 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.
|
||||
*/
|
||||
#define luaV_fastget(L,t,k,slot,f) \
|
||||
(!ttistable(t) \
|
||||
? (slot = NULL, 0) /* not a table; 'slot' is NULL and result is 0 */ \
|
||||
: (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 = (l_castS2U(k) - 1u < hvalue(t)->sizearray) \
|
||||
? &hvalue(t)->array[k - 1] : luaH_getint(hvalue(t), k), \
|
||||
!ttisnil(slot))) /* result not nil? */
|
||||
!isempty(slot))) /* result not empty? */
|
||||
|
||||
|
||||
/*
|
||||
|
Loading…
x
Reference in New Issue
Block a user