in hash nodes, keys are stored in separate pieces to avoid wasting

space with alignments
This commit is contained in:
Roberto Ierusalimschy 2017-06-09 13:48:44 -03:00
parent 4bb30f461b
commit b6f87491af
6 changed files with 202 additions and 111 deletions

42
lgc.c
View File

@ -1,5 +1,5 @@
/* /*
** $Id: lgc.c,v 2.229 2017/05/26 19:14:29 roberto Exp roberto $ ** $Id: lgc.c,v 2.230 2017/06/01 19:16:34 roberto Exp roberto $
** Garbage Collector ** Garbage Collector
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -73,7 +73,9 @@
#define valiswhite(x) (iscollectable(x) && iswhite(gcvalue(x))) #define valiswhite(x) (iscollectable(x) && iswhite(gcvalue(x)))
#define checkdeadkey(n) lua_assert(!ttisdeadkey(gkey(n)) || ttisnil(gval(n))) #define keyiswhite(n) (keyiscollectable(n) && iswhite(gckey(n)))
#define checkdeadkey(n) lua_assert(!keyisdead(n) || ttisnil(gval(n)))
#define checkconsistency(obj) \ #define checkconsistency(obj) \
@ -83,6 +85,8 @@
#define markvalue(g,o) { checkconsistency(o); \ #define markvalue(g,o) { checkconsistency(o); \
if (valiswhite(o)) reallymarkobject(g,gcvalue(o)); } if (valiswhite(o)) reallymarkobject(g,gcvalue(o)); }
#define markkey(g, n) { if keyiswhite(n) reallymarkobject(g,gckey(n)); }
#define markobject(g,t) { if (iswhite(t)) reallymarkobject(g, obj2gco(t)); } #define markobject(g,t) { if (iswhite(t)) reallymarkobject(g, obj2gco(t)); }
/* /*
@ -125,8 +129,8 @@ static lu_mem atomic (lua_State *L);
*/ */
static void removeentry (Node *n) { static void removeentry (Node *n) {
lua_assert(ttisnil(gval(n))); lua_assert(ttisnil(gval(n)));
if (valiswhite(gkey(n))) if (keyiswhite(n))
setdeadvalue(wgkey(n)); /* unused and unmarked key; remove it */ setdeadkey(n); /* unused and unmarked key; remove it */
} }
@ -137,13 +141,13 @@ static void removeentry (Node *n) {
** other objects: if really collected, cannot keep them; for objects ** other objects: if really collected, cannot keep them; for objects
** being finalized, keep them in keys, but not in values ** being finalized, keep them in keys, but not in values
*/ */
static int iscleared (global_State *g, const TValue *o) { static int iscleared (global_State *g, const GCObject *o) {
if (!iscollectable(o)) return 0; if (o == NULL) return 0; /* non-collectable value */
else if (ttisstring(o)) { else if (novariant(o->tt) == LUA_TSTRING) {
markobject(g, tsvalue(o)); /* strings are 'values', so are never weak */ markobject(g, o); /* strings are 'values', so are never weak */
return 0; return 0;
} }
else return iswhite(gcvalue(o)); else return iswhite(o);
} }
@ -391,9 +395,9 @@ static void traverseweakvalue (global_State *g, Table *h) {
if (ttisnil(gval(n))) /* entry is empty? */ if (ttisnil(gval(n))) /* entry is empty? */
removeentry(n); /* remove it */ removeentry(n); /* remove it */
else { else {
lua_assert(!ttisnil(gkey(n))); lua_assert(!keyisnil(n));
markvalue(g, gkey(n)); /* mark key */ markkey(g, n);
if (!hasclears && iscleared(g, gval(n))) /* is there a white value? */ if (!hasclears && iscleared(g, gcvalueN(gval(n)))) /* a white value? */
hasclears = 1; /* table will have to be cleared */ hasclears = 1; /* table will have to be cleared */
} }
} }
@ -433,7 +437,7 @@ static int traverseephemeron (global_State *g, Table *h) {
checkdeadkey(n); checkdeadkey(n);
if (ttisnil(gval(n))) /* entry is empty? */ if (ttisnil(gval(n))) /* entry is empty? */
removeentry(n); /* remove it */ removeentry(n); /* remove it */
else if (iscleared(g, gkey(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? */
hasww = 1; /* white-white entry */ hasww = 1; /* white-white entry */
@ -468,9 +472,9 @@ static void traversestrongtable (global_State *g, Table *h) {
if (ttisnil(gval(n))) /* entry is empty? */ if (ttisnil(gval(n))) /* entry is empty? */
removeentry(n); /* remove it */ removeentry(n); /* remove it */
else { else {
lua_assert(!ttisnil(gkey(n))); lua_assert(!keyisnil(n));
markvalue(g, gkey(n)); /* mark key */ markkey(g, n);
markvalue(g, gval(n)); /* mark value */ markvalue(g, gval(n));
} }
} }
if (g->gckind == KGC_GEN) { if (g->gckind == KGC_GEN) {
@ -691,7 +695,7 @@ static void clearkeys (global_State *g, GCObject *l) {
Table *h = gco2t(l); Table *h = gco2t(l);
Node *n, *limit = gnodelast(h); Node *n, *limit = gnodelast(h);
for (n = gnode(h, 0); n < limit; n++) { for (n = gnode(h, 0); n < limit; n++) {
if (!ttisnil(gval(n)) && (iscleared(g, gkey(n)))) { if (!ttisnil(gval(n)) && (iscleared(g, gckeyN(n)))) {
setnilvalue(gval(n)); /* remove value ... */ setnilvalue(gval(n)); /* remove value ... */
removeentry(n); /* and remove entry from table */ removeentry(n); /* and remove entry from table */
} }
@ -711,11 +715,11 @@ static void clearvalues (global_State *g, GCObject *l, GCObject *f) {
unsigned int i; unsigned int i;
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, o)) /* value was collected? */ if (iscleared(g, gcvalueN(o))) /* value was collected? */
setnilvalue(o); /* remove value */ setnilvalue(o); /* remove value */
} }
for (n = gnode(h, 0); n < limit; n++) { for (n = gnode(h, 0); n < limit; n++) {
if (iscleared(g, gval(n))) { if (iscleared(g, gcvalueN(gval(n)))) {
setnilvalue(gval(n)); /* remove value ... */ setnilvalue(gval(n)); /* remove value ... */
removeentry(n); /* and remove entry from table */ removeentry(n); /* and remove entry from table */
} }

6
llex.c
View File

@ -1,5 +1,5 @@
/* /*
** $Id: llex.c,v 2.95 2015/11/19 19:16:22 roberto Exp roberto $ ** $Id: llex.c,v 2.96 2016/05/02 14:02:12 roberto Exp roberto $
** Lexical Analyzer ** Lexical Analyzer
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -132,12 +132,12 @@ TString *luaX_newstring (LexState *ls, const char *str, size_t l) {
o = luaH_set(L, ls->h, L->top - 1); o = luaH_set(L, ls->h, L->top - 1);
if (ttisnil(o)) { /* not in use yet? */ if (ttisnil(o)) { /* not in use yet? */
/* boolean value does not need GC barrier; /* boolean value does not need GC barrier;
table has no 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 */
luaC_checkGC(L); luaC_checkGC(L);
} }
else { /* string already present */ else { /* string already present */
ts = tsvalue(keyfromval(o)); /* re-use value previously stored */ ts = keystrval(nodefromval(o)); /* re-use value previously stored */
} }
L->top--; /* remove string from stack */ L->top--; /* remove string from stack */
return ts; return ts;

View File

@ -1,5 +1,5 @@
/* /*
** $Id: lobject.h,v 2.120 2017/04/30 20:43:26 roberto Exp roberto $ ** $Id: lobject.h,v 2.121 2017/06/01 20:24:05 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
*/ */
@ -122,6 +122,7 @@ typedef struct lua_TValue {
#define val_(o) ((o)->value_) #define val_(o) ((o)->value_)
#define valraw(o) (&val_(o))
/* raw type tag of a TValue */ /* raw type tag of a TValue */
@ -131,7 +132,8 @@ typedef struct lua_TValue {
#define novariant(x) ((x) & 0x0F) #define novariant(x) ((x) & 0x0F)
/* type tag of a TValue (bits 0-3 for tags + variant bits 4-5) */ /* type tag of a TValue (bits 0-3 for tags + variant bits 4-5) */
#define ttype(o) (rttype(o) & 0x3F) #define ttyperaw(t) ((t) & 0x3F)
#define ttype(o) ttyperaw(rttype(o))
/* type tag of a TValue with no variants (bits 0-3) */ /* type tag of a TValue with no variants (bits 0-3) */
#define ttnov(o) (novariant(rttype(o))) #define ttnov(o) (novariant(rttype(o)))
@ -157,7 +159,19 @@ typedef struct lua_TValue {
#define ttislcf(o) checktag((o), LUA_TLCF) #define ttislcf(o) checktag((o), LUA_TLCF)
#define ttisfulluserdata(o) checktag((o), ctb(LUA_TUSERDATA)) #define ttisfulluserdata(o) checktag((o), ctb(LUA_TUSERDATA))
#define ttisthread(o) checktag((o), ctb(LUA_TTHREAD)) #define ttisthread(o) checktag((o), ctb(LUA_TTHREAD))
#define ttisdeadkey(o) checktag((o), LUA_TDEADKEY)
/*
** Macros to access unstructured values (may come both from
** 'TValue's and table keys)
*/
#define ivalueraw(v) ((v).i)
#define fltvalueraw(v) ((v).n)
#define gcvalueraw(v) ((v).gc)
#define pvalueraw(v) ((v).p)
#define tsvalueraw(v) (gco2ts((v).gc))
#define fvalueraw(v) ((v).f)
#define bvalueraw(v) ((v).b)
/* Macros to access values */ /* Macros to access values */
@ -176,8 +190,6 @@ typedef struct lua_TValue {
#define hvalue(o) check_exp(ttistable(o), gco2t(val_(o).gc)) #define hvalue(o) check_exp(ttistable(o), gco2t(val_(o).gc))
#define bvalue(o) check_exp(ttisboolean(o), val_(o).b) #define bvalue(o) check_exp(ttisboolean(o), val_(o).b)
#define thvalue(o) check_exp(ttisthread(o), gco2th(val_(o).gc)) #define thvalue(o) check_exp(ttisthread(o), gco2th(val_(o).gc))
/* a dead value may get the 'gc' field, but cannot access its contents */
#define deadvalue(o) check_exp(ttisdeadkey(o), cast(void *, val_(o).gc))
#define l_isfalse(o) (ttisnil(o) || (ttisboolean(o) && bvalue(o) == 0)) #define l_isfalse(o) (ttisnil(o) || (ttisboolean(o) && bvalue(o) == 0))
@ -185,6 +197,12 @@ typedef struct lua_TValue {
#define iscollectable(o) (rttype(o) & BIT_ISCOLLECTABLE) #define iscollectable(o) (rttype(o) & BIT_ISCOLLECTABLE)
/*
** Protected access to objects in values
*/
#define gcvalueN(o) (iscollectable(o) ? gcvalue(o) : NULL)
/* Macros for internal tests */ /* Macros for internal tests */
#define righttt(obj) (ttype(obj) == gcvalue(obj)->tt) #define righttt(obj) (ttype(obj) == gcvalue(obj)->tt)
@ -253,8 +271,6 @@ typedef struct lua_TValue {
val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_TTABLE)); \ val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_TTABLE)); \
checkliveness(L,io); } checkliveness(L,io); }
#define setdeadvalue(obj) settt_(obj, LUA_TDEADKEY)
#define setobj(L,obj1,obj2) \ #define setobj(L,obj1,obj2) \
@ -485,26 +501,37 @@ typedef union Closure {
** Tables ** Tables
*/ */
typedef union TKey {
struct { /*
TValuefields; ** Nodes for Hash tables. A pack of two TValue's (key-value pairs)
int next; /* for chaining (offset for next node) */ ** plus a 'next' field to link colliding entries. The distribuition
} nk; ** of the key's fields ('key_tt' and 'key_val') not forming a proper
TValue tvk; ** 'TValue' allows for a smaller size for 'Node' both in 4-byte
} TKey; ** and 8-byte alignments.
*/
typedef union Node {
struct NodeKey {
TValuefields; /* fields for value */
lu_byte key_tt; /* key type */
int next; /* for chaining */
Value key_val; /* key value */
} u;
TValue i_val; /* direct access to node's value as a proper 'TValue' */
} Node;
/* copy a value into a key without messing up field 'next' */ /* copy a value into a key */
#define setnodekey(L,key,obj) \ #define setnodekey(L,node,obj) \
{ TKey *k_=(key); const TValue *io_=(obj); \ { Node *n_=(node); const TValue *io_=(obj); \
k_->nk.value_ = io_->value_; k_->nk.tt_ = io_->tt_; \ n_->u.key_val = io_->value_; n_->u.key_tt = io_->tt_; \
(void)L; checkliveness(L,io_); } (void)L; checkliveness(L,io_); }
typedef struct Node { /* copy a value from a key */
TValue i_val; #define getnodekey(L,obj,node) \
TKey i_key; { TValue *io_=(obj); const Node *n_=(node); \
} Node; io_->value_ = n_->u.key_val; io_->tt_ = n_->u.key_tt; \
(void)L; checkliveness(L,io_); }
typedef struct Table { typedef struct Table {
@ -520,6 +547,33 @@ typedef struct Table {
} Table; } Table;
/*
** Macros to manipulate keys inserted in nodes
*/
#define keytt(node) ((node)->u.key_tt)
#define keyval(node) ((node)->u.key_val)
#define keyisnil(node) (keytt(node) == LUA_TNIL)
#define keyisinteger(node) (keytt(node) == LUA_TNUMINT)
#define keyival(node) (keyval(node).i)
#define keyisshrstr(node) (keytt(node) == ctb(LUA_TSHRSTR))
#define keystrval(node) (gco2ts(keyval(node).gc))
#define keyisdead(node) (keytt(node) == LUA_TDEADKEY)
#define setnilkey(node) (keytt(node) = LUA_TNIL)
#define setdeadkey(node) (keytt(node) = LUA_TDEADKEY)
/* a dead value may get the 'gc' field, but cannot access its contents */
#define deadkey(n) \
check_exp(keytt(n) == LUA_TDEADKEY, cast(void *, keyval(n).gc))
#define keyiscollectable(n) (keytt(n) & BIT_ISCOLLECTABLE)
#define gckey(n) (keyval(n).gc)
#define gckeyN(n) (keyiscollectable(n) ? gckey(n) : NULL)
/* /*
** 'module' operation for hashing (size is always a power of 2) ** 'module' operation for hashing (size is always a power of 2)

133
ltable.c
View File

@ -1,5 +1,5 @@
/* /*
** $Id: ltable.c,v 2.121 2017/05/19 12:47:00 roberto Exp roberto $ ** $Id: ltable.c,v 2.122 2017/05/19 12:57:10 roberto Exp roberto $
** Lua tables (hash) ** Lua tables (hash)
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -75,8 +75,8 @@
#define dummynode (&dummynode_) #define dummynode (&dummynode_)
static const Node dummynode_ = { static const Node dummynode_ = {
{NILCONSTANT}, /* value */ {{NULL}, LUA_TNIL, /* value's value and type */
{{NILCONSTANT, 0}} /* key */ LUA_TNIL, 0, {NULL}} /* key type, next, and key value */
}; };
@ -111,43 +111,79 @@ static int l_hashfloat (lua_Number n) {
/* /*
** returns the 'main' position of an element in a table (that is, the index ** returns the 'main' position of an element in a table (that is,
** of its hash value) ** the index of its hash value). The key comes broken (tag in 'ktt'
** and value in 'vkl') so that we can call it on keys inserted into
** nodes.
*/ */
static Node *mainposition (const Table *t, const TValue *key) { static Node *mainposition (const Table *t, int ktt, const Value *kvl) {
switch (ttype(key)) { switch (ttyperaw(ktt)) {
case LUA_TNUMINT: case LUA_TNUMINT:
return hashint(t, ivalue(key)); return hashint(t, ivalueraw(*kvl));
case LUA_TNUMFLT: case LUA_TNUMFLT:
return hashmod(t, l_hashfloat(fltvalue(key))); return hashmod(t, l_hashfloat(fltvalueraw(*kvl)));
case LUA_TSHRSTR: case LUA_TSHRSTR:
return hashstr(t, tsvalue(key)); return hashstr(t, tsvalueraw(*kvl));
case LUA_TLNGSTR: case LUA_TLNGSTR:
return hashpow2(t, luaS_hashlongstr(tsvalue(key))); return hashpow2(t, luaS_hashlongstr(tsvalueraw(*kvl)));
case LUA_TBOOLEAN: case LUA_TBOOLEAN:
return hashboolean(t, bvalue(key)); return hashboolean(t, bvalueraw(*kvl));
case LUA_TLIGHTUSERDATA: case LUA_TLIGHTUSERDATA:
return hashpointer(t, pvalue(key)); return hashpointer(t, pvalueraw(*kvl));
case LUA_TLCF: case LUA_TLCF:
return hashpointer(t, fvalue(key)); return hashpointer(t, fvalueraw(*kvl));
default: default:
lua_assert(!ttisdeadkey(key)); return hashpointer(t, gcvalueraw(*kvl));
return hashpointer(t, gcvalue(key)); }
}
static Node *mainpositionTV (const Table *t, const TValue *key) {
return mainposition(t, rttype(key), valraw(key));
}
/*
** Check whether key 'k1' is equal to the key in node 'n2'.
** This equality is raw, so there are no metamethods. Floats
** with integer values have been normalized, so integers cannot
** be equal to floats. It is assumed that 'eqshrstr' is simply
** pointer equality, so that short strings are handled in the
** default case.
*/
static int equalkey (const TValue *k1, const Node *n2) {
if (rttype(k1) != keytt(n2)) /* not the same variants? */
return 0; /* cannot be same key */
switch (ttype(k1)) {
case LUA_TNIL:
return 1;
case LUA_TNUMINT:
return (ivalue(k1) == keyival(n2));
case LUA_TNUMFLT:
return luai_numeq(fltvalue(k1), fltvalueraw(keyval(n2)));
case LUA_TBOOLEAN:
return bvalue(k1) == bvalueraw(keyval(n2));
case LUA_TLIGHTUSERDATA:
return pvalue(k1) == pvalueraw(keyval(n2));
case LUA_TLCF:
return fvalue(k1) == fvalueraw(keyval(n2));
case LUA_TLNGSTR:
return luaS_eqlngstr(tsvalue(k1), keystrval(n2));
default:
return gcvalue(k1) == gcvalueraw(keyval(n2));
} }
} }
/* /*
** returns the index for 'key' if 'key' is an appropriate key to live in ** returns the index for 'k' if 'k' is an appropriate key to live in
** the array part of the table, 0 otherwise. ** the array part of a table, 0 otherwise.
*/ */
static unsigned int arrayindex (const TValue *key) { static unsigned int arrayindex (lua_Integer k) {
if (ttisinteger(key)) { if (0 < k && l_castS2U(k) <= MAXASIZE)
lua_Integer k = ivalue(key); return cast(unsigned int, k); /* 'key' is an appropriate array index */
if (0 < k && (lua_Unsigned)k <= MAXASIZE) else
return cast(unsigned int, k); /* 'key' is an appropriate array index */ return 0;
}
return 0; /* 'key' did not match some condition */
} }
@ -159,17 +195,17 @@ static unsigned int arrayindex (const TValue *key) {
static unsigned int findindex (lua_State *L, Table *t, StkId key) { static unsigned int findindex (lua_State *L, Table *t, StkId key) {
unsigned int i; unsigned int i;
if (ttisnil(key)) return 0; /* first iteration */ if (ttisnil(key)) return 0; /* first iteration */
i = arrayindex(key); i = ttisinteger(key) ? arrayindex(ivalue(key)) : 0;
if (i != 0 && i <= t->sizearray) /* is 'key' inside array part? */ if (i != 0 && i <= t->sizearray) /* is 'key' inside array part? */
return i; /* yes; that's the index */ return i; /* yes; that's the index */
else { else {
int nx; int nx;
Node *n = mainposition(t, key); Node *n = mainpositionTV(t, key);
for (;;) { /* check whether 'key' is somewhere in the chain */ for (;;) { /* check whether 'key' is somewhere in the chain */
/* key may be dead already, but it is ok to use it in 'next' */ /* key may be dead already, but it is ok to use it in 'next' */
if (luaV_rawequalobj(gkey(n), key) || if (equalkey(key, n) ||
(ttisdeadkey(gkey(n)) && iscollectable(key) && (keyisdead(n) && iscollectable(key) &&
deadvalue(gkey(n)) == gcvalue(key))) { deadkey(n) == gcvalue(key))) {
i = cast_int(n - gnode(t, 0)); /* key index in hash table */ i = cast_int(n - gnode(t, 0)); /* key index in hash table */
/* hash elements are numbered after array ones */ /* hash elements are numbered after array ones */
return (i + 1) + t->sizearray; return (i + 1) + t->sizearray;
@ -194,8 +230,9 @@ int luaH_next (lua_State *L, Table *t, StkId key) {
} }
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 (!ttisnil(gval(gnode(t, i)))) { /* a non-nil value? */
setobj2s(L, key, gkey(gnode(t, i))); Node *n = gnode(t, i);
setobj2s(L, key+1, gval(gnode(t, i))); getnodekey(L, key, n);
setobj2s(L, key + 1, gval(n));
return 1; return 1;
} }
} }
@ -239,7 +276,7 @@ static unsigned int computesizes (unsigned int nums[], unsigned int *pna) {
} }
static int countint (const TValue *key, unsigned int *nums) { static int countint (lua_Integer key, unsigned int *nums) {
unsigned int k = arrayindex(key); unsigned int k = arrayindex(key);
if (k != 0) { /* is 'key' an appropriate array index? */ if (k != 0) { /* is 'key' an appropriate array index? */
nums[luaO_ceillog2(k)]++; /* count as such */ nums[luaO_ceillog2(k)]++; /* count as such */
@ -288,7 +325,8 @@ static int numusehash (const Table *t, unsigned int *nums, unsigned int *pna) {
while (i--) { while (i--) {
Node *n = &t->node[i]; Node *n = &t->node[i];
if (!ttisnil(gval(n))) { if (!ttisnil(gval(n))) {
ause += countint(gkey(n), nums); if (keyisinteger(n))
ause += countint(keyival(n), nums);
totaluse++; totaluse++;
} }
} }
@ -322,7 +360,7 @@ static void setnodevector (lua_State *L, Table *t, unsigned int size) {
for (i = 0; i < (int)size; i++) { for (i = 0; i < (int)size; i++) {
Node *n = gnode(t, i); Node *n = gnode(t, i);
gnext(n) = 0; gnext(n) = 0;
setnilvalue(wgkey(n)); setnilkey(n);
setnilvalue(gval(n)); setnilvalue(gval(n));
} }
t->lsizenode = cast_byte(lsize); t->lsizenode = cast_byte(lsize);
@ -358,7 +396,8 @@ void luaH_resize (lua_State *L, Table *t, unsigned int nasize,
if (!ttisnil(gval(old))) { if (!ttisnil(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 */
setobjt2t(L, luaH_set(L, t, gkey(old)), gval(old)); TValue k; getnodekey(L, &k, old);
setobjt2t(L, luaH_set(L, t, &k), gval(old));
} }
} }
if (oldhsize > 0) /* not the dummy node? */ if (oldhsize > 0) /* not the dummy node? */
@ -385,7 +424,8 @@ static void rehash (lua_State *L, Table *t, const TValue *ek) {
totaluse = na; /* all those keys are integer keys */ totaluse = na; /* all those keys are integer keys */
totaluse += numusehash(t, nums, &na); /* count keys in hash part */ totaluse += numusehash(t, nums, &na); /* count keys in hash part */
/* count extra key */ /* count extra key */
na += countint(ek, nums); if (ttisinteger(ek))
na += countint(ivalue(ek), nums);
totaluse++; totaluse++;
/* compute new size for array part */ /* compute new size for array part */
asize = computesizes(nums, &na); asize = computesizes(nums, &na);
@ -424,7 +464,7 @@ static Node *getfreepos (Table *t) {
if (!isdummy(t)) { if (!isdummy(t)) {
while (t->lastfree > t->node) { while (t->lastfree > t->node) {
t->lastfree--; t->lastfree--;
if (ttisnil(gkey(t->lastfree))) if (keyisnil(t->lastfree))
return t->lastfree; return t->lastfree;
} }
} }
@ -453,7 +493,7 @@ TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key) {
else if (luai_numisnan(fltvalue(key))) else if (luai_numisnan(fltvalue(key)))
luaG_runerror(L, "table index is NaN"); luaG_runerror(L, "table index is NaN");
} }
mp = mainposition(t, key); mp = mainpositionTV(t, key);
if (!ttisnil(gval(mp)) || isdummy(t)) { /* main position is taken? */ if (!ttisnil(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 */
@ -463,7 +503,7 @@ TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key) {
return luaH_set(L, t, key); /* insert key into grown table */ return luaH_set(L, t, key); /* insert key into grown table */
} }
lua_assert(!isdummy(t)); lua_assert(!isdummy(t));
othern = mainposition(t, gkey(mp)); othern = mainposition(t, keytt(mp), &keyval(mp));
if (othern != mp) { /* is colliding node out of its main position? */ if (othern != mp) { /* is colliding node out of its main position? */
/* yes; move colliding node into free position */ /* yes; move colliding node into free position */
while (othern + gnext(othern) != mp) /* find previous */ while (othern + gnext(othern) != mp) /* find previous */
@ -485,7 +525,7 @@ TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key) {
mp = f; mp = f;
} }
} }
setnodekey(L, &mp->i_key, key); setnodekey(L, mp, key);
luaC_barrierback(L, t, key); luaC_barrierback(L, t, key);
lua_assert(ttisnil(gval(mp))); lua_assert(ttisnil(gval(mp)));
return gval(mp); return gval(mp);
@ -502,7 +542,7 @@ const TValue *luaH_getint (Table *t, lua_Integer key) {
else { else {
Node *n = hashint(t, key); Node *n = hashint(t, key);
for (;;) { /* check whether 'key' is somewhere in the chain */ for (;;) { /* check whether 'key' is somewhere in the chain */
if (ttisinteger(gkey(n)) && ivalue(gkey(n)) == key) if (keyisinteger(n) && keyival(n) == key)
return gval(n); /* that's it */ return gval(n); /* that's it */
else { else {
int nx = gnext(n); int nx = gnext(n);
@ -522,8 +562,7 @@ const TValue *luaH_getshortstr (Table *t, TString *key) {
Node *n = hashstr(t, key); Node *n = hashstr(t, key);
lua_assert(key->tt == LUA_TSHRSTR); lua_assert(key->tt == LUA_TSHRSTR);
for (;;) { /* check whether 'key' is somewhere in the chain */ for (;;) { /* check whether 'key' is somewhere in the chain */
const TValue *k = gkey(n); if (keyisshrstr(n) && eqshrstr(keystrval(n), key))
if (ttisshrstring(k) && eqshrstr(tsvalue(k), key))
return gval(n); /* that's it */ return gval(n); /* that's it */
else { else {
int nx = gnext(n); int nx = gnext(n);
@ -540,9 +579,9 @@ const TValue *luaH_getshortstr (Table *t, TString *key) {
** which may be in array part, nor for floats with integral values.) ** which may be in array part, nor for floats with integral values.)
*/ */
static const TValue *getgeneric (Table *t, const TValue *key) { static const TValue *getgeneric (Table *t, const TValue *key) {
Node *n = mainposition(t, key); Node *n = mainpositionTV(t, key);
for (;;) { /* check whether 'key' is somewhere in the chain */ for (;;) { /* check whether 'key' is somewhere in the chain */
if (luaV_rawequalobj(gkey(n), key)) if (equalkey(key, n))
return gval(n); /* that's it */ return gval(n); /* that's it */
else { else {
int nx = gnext(n); int nx = gnext(n);
@ -683,7 +722,7 @@ lua_Unsigned luaH_getn (Table *t) {
#if defined(LUA_DEBUG) #if defined(LUA_DEBUG)
Node *luaH_mainposition (const Table *t, const TValue *key) { Node *luaH_mainposition (const Table *t, const TValue *key) {
return mainposition(t, key); return mainpositionTV(t, key);
} }
int luaH_isdummy (const Table *t) { return isdummy(t); } int luaH_isdummy (const Table *t) { return isdummy(t); }

View File

@ -1,5 +1,5 @@
/* /*
** $Id: ltable.h,v 2.23 2016/12/22 13:08:50 roberto Exp roberto $ ** $Id: ltable.h,v 2.24 2017/05/19 12:48:15 roberto Exp roberto $
** Lua tables (hash) ** Lua tables (hash)
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -12,18 +12,9 @@
#define gnode(t,i) (&(t)->node[i]) #define gnode(t,i) (&(t)->node[i])
#define gval(n) (&(n)->i_val) #define gval(n) (&(n)->i_val)
#define gnext(n) ((n)->i_key.nk.next) #define gnext(n) ((n)->u.next)
/* 'const' to avoid wrong writings that can mess up field 'next' */
#define gkey(n) cast(const TValue*, (&(n)->i_key.tvk))
/*
** writable version of 'gkey'; allows updates to individual fields,
** but not to the whole (which has incompatible type)
*/
#define wgkey(n) (&(n)->i_key.nk)
#define invalidateTMcache(t) ((t)->flags = 0) #define invalidateTMcache(t) ((t)->flags = 0)
@ -35,9 +26,8 @@
#define allocsizenode(t) (isdummy(t) ? 0 : sizenode(t)) #define allocsizenode(t) (isdummy(t) ? 0 : sizenode(t))
/* returns the key, given the value of a table entry */ /* returns the Node, given the value of a table entry */
#define keyfromval(v) \ #define nodefromval(v) cast(Node *, (v))
(gkey(cast(Node *, cast(char *, (v)) - offsetof(Node, i_val))))
LUAI_FUNC const TValue *luaH_getint (Table *t, lua_Integer key); LUAI_FUNC const TValue *luaH_getint (Table *t, lua_Integer key);

View File

@ -1,5 +1,5 @@
/* /*
** $Id: ltests.c,v 2.217 2017/05/04 13:32:01 roberto Exp $ ** $Id: ltests.c,v 2.218 2017/05/31 18:54:58 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
*/ */
@ -253,8 +253,10 @@ static void checktable (global_State *g, Table *h) {
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 (!ttisnil(gval(n))) {
lua_assert(!ttisnil(gkey(n))); TValue k;
checkvalref(g, hgc, gkey(n)); getnodekey(g->mainthread, &k, n);
lua_assert(!keyisnil(n));
checkvalref(g, hgc, &k);
checkvalref(g, hgc, gval(n)); checkvalref(g, hgc, gval(n));
} }
} }
@ -802,10 +804,12 @@ static int table_query (lua_State *L) {
lua_pushnil(L); lua_pushnil(L);
} }
else if ((i -= t->sizearray) < sizenode(t)) { else if ((i -= t->sizearray) < sizenode(t)) {
TValue k;
getnodekey(L, &k, gnode(t, i));
if (!ttisnil(gval(gnode(t, i))) || if (!ttisnil(gval(gnode(t, i))) ||
ttisnil(gkey(gnode(t, i))) || ttisnil(&k) ||
ttisnumber(gkey(gnode(t, i)))) { ttisnumber(&k)) {
pushobject(L, gkey(gnode(t, i))); pushobject(L, &k);
} }
else else
lua_pushliteral(L, "<undef>"); lua_pushliteral(L, "<undef>");