diff --git a/luprex/eris-master/src/lapi.c b/luprex/eris-master/src/lapi.c index 5c7794bb..f4abd7fc 100644 --- a/luprex/eris-master/src/lapi.c +++ b/luprex/eris-master/src/lapi.c @@ -1141,7 +1141,6 @@ LUA_API int lua_error (lua_State *L) { return 0; /* to avoid warnings */ } - LUA_API int lua_next (lua_State *L, int idx) { StkId t; int more; @@ -1158,6 +1157,29 @@ LUA_API int lua_next (lua_State *L, int idx) { return more; } +LUA_API int lua_nkeys (lua_State *L, int idx) { + StkId t; + lua_lock(L); + t = index2addr(L, idx); + api_check(L, ttistable(t), "table expected"); + int n = luaH_nkeys(hvalue(t)); + lua_unlock(L); + return n; +} + +LUA_API int lua_nthkey (lua_State *L, int idx, int n) { + StkId t; + lua_lock(L); + t = index2addr(L, idx); + api_check(L, ttistable(t), "table expected"); + api_incr_top(L); + api_incr_top(L); + int res = luaH_nthkey(L, hvalue(t), n, L->top - 2); + if (res == 0) + L->top -= 2; + lua_unlock(L); + return res; +} LUA_API void lua_concat (lua_State *L, int n) { lua_lock(L); diff --git a/luprex/eris-master/src/lbaselib.c b/luprex/eris-master/src/lbaselib.c index cfebd58e..7c890bc0 100644 --- a/luprex/eris-master/src/lbaselib.c +++ b/luprex/eris-master/src/lbaselib.c @@ -215,6 +215,26 @@ static int pairsmeta (lua_State *L, const char *method, int iszero, } +static int luaB_nkeys (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + int n = lua_nkeys(L, 1); + lua_pop(L, 1); + lua_pushinteger(L, n); + return 1; +} + + +static int luaB_nthkey (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + int n = luaL_checkint(L, 2); + if (lua_nthkey(L, 1, n)) { + return 2; + } else { + return 0; + } +} + + static int luaB_next (lua_State *L) { luaL_checktype(L, 1, LUA_TTABLE); lua_settop(L, 2); /* create a 2nd argument if there isn't one */ @@ -232,6 +252,15 @@ static int luaB_pairs (lua_State *L) { } +static int luaB_rawpairs (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); /* argument must be a table */ + lua_pushcfunction(L, luaB_next); /* will return generator, */ + lua_pushvalue(L, 1); /* table */ + lua_pushnil(L); + return 3; +} + + static int ipairsaux (lua_State *L) { int i = luaL_checkint(L, 2); luaL_checktype(L, 1, LUA_TTABLE); @@ -432,7 +461,10 @@ static const luaL_Reg base_funcs[] = { {"loadstring", luaB_load}, #endif {"next", luaB_next}, + {"nkeys", luaB_nkeys}, + {"nthkey", luaB_nthkey}, {"pairs", luaB_pairs}, + {"rawpairs", luaB_rawpairs}, {"pcall", luaB_pcall}, {"print", luaB_print}, {"rawequal", luaB_rawequal}, diff --git a/luprex/eris-master/src/lgc.c b/luprex/eris-master/src/lgc.c index 553fd177..f4335db7 100644 --- a/luprex/eris-master/src/lgc.c +++ b/luprex/eris-master/src/lgc.c @@ -383,9 +383,9 @@ static int traverseephemeron (global_State *g, Table *h) { int i; /* traverse array part (numeric keys are 'strong') */ for (i = 0; i < h->sizearray; i++) { - if (valiswhite(&h->array[i])) { + if (valiswhite(&h->array[i].i_val)) { marked = 1; - reallymarkobject(g, gcvalue(&h->array[i])); + reallymarkobject(g, gcvalue(&h->array[i].i_val)); } } /* traverse hash part */ @@ -417,7 +417,7 @@ static void traversestrongtable (global_State *g, Table *h) { Node *n, *limit = gnodelast(h); int i; for (i = 0; i < h->sizearray; i++) /* traverse array part */ - markvalue(g, &h->array[i]); + markvalue(g, &h->array[i].i_val); for (n = gnode(h, 0); n < limit; n++) { /* traverse hash part */ checkdeadkey(n); if (ttisnil(gval(n))) /* entry is empty? */ @@ -646,7 +646,7 @@ static void clearvalues (global_State *g, GCObject *l, GCObject *f) { Node *n, *limit = gnodelast(h); int i; for (i = 0; i < h->sizearray; i++) { - TValue *o = &h->array[i]; + TValue *o = &h->array[i].i_val; if (iscleared(g, o)) /* value was collected? */ setnilvalue(o); /* remove value */ } diff --git a/luprex/eris-master/src/lobject.h b/luprex/eris-master/src/lobject.h index bc0bb69f..1d1db9dd 100644 --- a/luprex/eris-master/src/lobject.h +++ b/luprex/eris-master/src/lobject.h @@ -542,29 +542,29 @@ typedef union Closure { ** Tables */ -typedef union TKey { - struct { - TValuefields; - struct Node *next; /* for chaining */ - } nk; - TValue tvk; -} TKey; - +typedef struct ANode { + TValue i_val; + int i_sequence; +} ANode; typedef struct Node { - TValue i_val; - TKey i_key; + ANode anode; + TValue i_key; + struct Node *i_next; } Node; - typedef struct Table { CommonHeader; lu_byte flags; /* 1<

+#include #define ltable_c #define LUA_CORE @@ -69,10 +70,61 @@ #define isdummy(n) ((n) == dummynode) static const Node dummynode_ = { - {NILCONSTANT}, /* value */ - {{NILCONSTANT, NULL}} /* key */ + {{NILCONSTANT}, 0}, /* anode: value, sequence */ + {NILCONSTANT}, /* key */ + NULL /* next */ }; +static int truesizenode(Table *t) { + return isdummy(t->node) ? 0 : sizenode(t); +} + +/* given a valid anode pointer, return its index */ +static int anodeindex(Table *t, ANode *anode) { + int aoffs = anode - t->array; + if (aoffs >= 0 && aoffs < t->sizearray && (t->array+aoffs==anode)) { + return aoffs; + } + Node *node = cast(Node *, anode); + return (node - t->node) + t->sizearray; +} + +/* given a valid anode index, return its pointer */ +static ANode *nthanode(Table *t, int n) { + if (n < t->sizearray) { + return t->array + n; + } else { + return &t->node[n - t->sizearray].anode; + } +} + +static void checksequence(Table *t) { + assert(t->nnkeys >= 0); + int nodesize = truesizenode(t); + int totalanodes = t->sizearray + nodesize; + for (int i = 0; i < t->nnkeys; i++) { + int anodeindex = t->sequence[i]; + assert((anodeindex >= 0) && (anodeindex < totalanodes)); + ANode *n = nthanode(t, anodeindex); + assert(n->i_sequence == i); + } + for (int i = 0; i < t->sizearray; i++) { + int seqno = t->array[i].i_sequence; + if (seqno != -1) { + assert((seqno >= 0) && (seqno < t->nnkeys)); + assert(t->sequence[seqno] == i); + } + } + for (int i = 0; i < nodesize; i++) { + Node *n = t->node + i; + int seqno = gseq(n); + if (seqno != -1) { + assert((seqno >= 0) && (seqno < t->nnkeys)); + assert(t->sequence[seqno] == i + t->sizearray); + } + } +} + /* ** hash for lua_Numbers @@ -135,57 +187,86 @@ static int arrayindex (const TValue *key) { return -1; /* `key' did not match some condition */ } - -/* -** returns the index of a `key' for table traversals. First goes all -** elements in the array part, then elements in the hash part. The -** beginning of a traversal is signaled by -1. -*/ -static int findindex (lua_State *L, Table *t, StkId key) { - int i; - if (ttisnil(key)) return -1; /* first iteration */ - i = arrayindex(key); - if (0 < i && i <= t->sizearray) /* is `key' inside array part? */ - return i-1; /* yes; that's the index (corrected to C) */ - else { - Node *n = mainposition(t, key); - for (;;) { /* check whether `key' is somewhere in the chain */ - /* key may be dead already, but it is ok to use it in `next' */ - if (luaV_rawequalobj(gkey(n), key) || - (ttisdeadkey(gkey(n)) && iscollectable(key) && - deadvalue(gkey(n)) == gcvalue(key))) { - i = cast_int(n - gnode(t, 0)); /* key index in hash table */ - /* hash elements are numbered after array ones */ - return i + t->sizearray; - } - else n = gnext(n); - if (n == NULL) - luaG_runerror(L, "invalid key to " LUA_QL("next")); /* key not found */ - } - } +int luaH_nkeys (Table *t) { + return t->nnkeys; } +int luaH_nthkey (lua_State *L, Table *t, int n, StkId pair) { + n -= 1; /* convert to C indexing */ + if ((n < 0) || (n >= t->nnkeys)) { + setnilvalue(pair+0); + setnilvalue(pair+1); + return 0; + } + int index = t->sequence[n]; + if (index < t->sizearray) { + setnvalue(pair + 0, index + 1); + setobj2s(L, pair + 1, &t->array[index].i_val); + return 1; + } else { + index -= t->sizearray; + Node *n = t->node + index; + setobj2s(L, pair + 0, gkey(n)); + setobj2s(L, pair + 1, gval(n)); + return 1; + } +} + +int successorindex (lua_State *L, Table *t, StkId key) { + int i, seqno; + if (ttisnil(key)) { + return 0; + } + i = arrayindex(key); + if (0 < i && i <= t->sizearray) { + seqno = t->array[i-1].i_sequence + 1; + if (seqno == 0) { + if (t->array + (i - 1) == t->lastdeleted) { + return t->lastdelseq; + } + luaG_runerror(L, "next: no such key in table"); + } + return seqno; + } + Node *n = mainposition(t, key); + for (;;) { /* check whether `key' is somewhere in the chain */ + /* key may be dead already, but it is ok to use it in `next' */ + if (luaV_rawequalobj(gkey(n), key) || + (ttisdeadkey(gkey(n)) && iscollectable(key) && + deadvalue(gkey(n)) == gcvalue(key))) { + seqno = gseq(n) + 1; + if (seqno == 0) { + if (ganode(n) == t->lastdeleted) { + return t->lastdelseq; + } + luaG_runerror(L, "next: no such key in table"); + } + return seqno; + } + else n = gnext(n); + if (n == NULL) + luaG_runerror(L, "invalid key to " LUA_QL("next")); /* key not found */ + } +} int luaH_next (lua_State *L, Table *t, StkId key) { - int i = findindex(L, t, key); /* find original element */ - for (i++; i < t->sizearray; i++) { /* try first array part */ - if (!ttisnil(&t->array[i])) { /* a non-nil value? */ - setnvalue(key, cast_num(i+1)); - setobj2s(L, key+1, &t->array[i]); - return 1; - } + int seqno = successorindex(L, t, key); + if (seqno >= t->nnkeys) { + return 0; } - for (i -= t->sizearray; i < sizenode(t); i++) { /* then hash part */ - if (!ttisnil(gval(gnode(t, i)))) { /* a non-nil value? */ - setobj2s(L, key, gkey(gnode(t, i))); - setobj2s(L, key+1, gval(gnode(t, i))); - return 1; - } + int i = t->sequence[seqno]; + if (i < t->sizearray) { + setnvalue(key + 0, i+1); + setobj2s(L, key + 1, &t->array[i].i_val); + return 1; + } else { + Node *n = t->node + (i - t->sizearray); + setobj2s(L, key + 0, gkey(n)); + setobj2s(L, key + 1, gval(n)); + return 1; } - return 0; /* no more elements */ } - /* ** {============================================================= ** Rehash @@ -241,7 +322,7 @@ static int numusearray (const Table *t, int *nums) { } /* count elements in range (2^(lg-1), 2^lg] */ for (; i <= lim; i++) { - if (!ttisnil(&t->array[i-1])) + if (!ttisnil(&t->array[i-1].i_val)) lc++; } nums[lg] += lc; @@ -269,9 +350,11 @@ static int numusehash (const Table *t, int *nums, int *pnasize) { static void setarrayvector (lua_State *L, Table *t, int size) { int i; - luaM_reallocvector(L, t->array, t->sizearray, size, TValue); - for (i=t->sizearray; iarray[i]); + luaM_reallocvector(L, t->array, t->sizearray, size, ANode); + for (i=t->sizearray; iarray[i].i_val); + t->array[i].i_sequence = -1; + } t->sizearray = size; } @@ -285,13 +368,15 @@ static void setnodevector (lua_State *L, Table *t, int size) { else { int i; lsize = luaO_ceillog2(size); + if (lsize == 0) lsize = 1; if (lsize > MAXBITS) - luaG_runerror(L, "table overflow"); + luaG_runerror(L, "table overflow"); size = twoto(lsize); t->node = luaM_newvector(L, size, Node); for (i=0; ilastfree = gnode(t, size); /* all positions are free */ } +static TValue *luaH_newkey(lua_State *L, Table *t, const TValue *key); + +static void storeintanode (lua_State *L, Table *t, int key, ANode *anode) { + const TValue *p = luaH_getint(t, key); + ANode *cell; + if (p != luaO_nilobject) { + cell = cast(ANode *, p); + } else { + TValue nk; + setnvalue(&nk, key); + cell = cast(ANode *, luaH_newkey(L, t, &nk)); + } + setobj2t(L, &cell->i_val, &anode->i_val); + cell->i_sequence = anode->i_sequence; +} + +static void storeanode (lua_State *L, Table *t, const TValue *key, ANode *anode) { + const TValue *p = luaH_get(t, key); + ANode *cell; + if (p != luaO_nilobject) { + cell = cast(ANode *, p); + } else { + cell = cast(ANode *, luaH_newkey(L, t, key)); + } + setobj2t(L, &cell->i_val, &anode->i_val); + cell->i_sequence = anode->i_sequence; +} + void luaH_resize (lua_State *L, Table *t, int nasize, int nhsize) { int i; int oldasize = t->sizearray; - int oldhsize = t->lsizenode; + int oldhsize = truesizenode(t); Node *nold = t->node; /* save old hash ... */ if (nasize > oldasize) /* array part must grow? */ setarrayvector(L, t, nasize); /* create new hash part with appropriate size */ setnodevector(L, t, nhsize); - if (nasize < oldasize) { /* array part must shrink? */ + nhsize = truesizenode(t); + /* resize the sequence array */ + int oldseqlen = oldasize + oldhsize; + int newseqlen = nasize + nhsize; + if (newseqlen < t->nnkeys) + luaG_runerror(L, "table sequence vector not large enough?"); + luaM_reallocvector(L, t->sequence, oldseqlen, newseqlen, int); + /* possibly shrink the array part */ + if (nasize < oldasize) { t->sizearray = nasize; /* re-insert elements from vanishing slice */ for (i=nasize; iarray[i])) - luaH_setint(L, t, i + 1, &t->array[i]); + if (!ttisnil(&t->array[i].i_val)) + storeintanode(L, t, i + 1, &t->array[i]); } /* shrink array */ - luaM_reallocvector(L, t->array, oldasize, nasize, TValue); + luaM_reallocvector(L, t->array, oldasize, nasize, ANode); } /* re-insert elements from hash part */ - for (i = twoto(oldhsize) - 1; i >= 0; i--) { + for (i = oldhsize - 1; i >= 0; i--) { Node *old = nold+i; if (!ttisnil(gval(old))) { /* doesn't need barrier/invalidate cache, as entry was already present in the table */ - luaH_setvalue(L, t, gkey(old), gval(old)); + storeanode(L, t, gkey(old), ganode(old)); } } + /* delete the old node vector */ if (!isdummy(nold)) - luaM_freearray(L, nold, cast(size_t, twoto(oldhsize))); /* free old array */ + luaM_freearray(L, nold, cast(size_t, oldhsize)); + /* recalculate the entire sequence vector */ + int total = 0; + for (i = nasize - 1; i >= 0; i--) { + ANode *anode = &t->array[i]; + if (anode->i_sequence >= 0) { + assert(anode->i_sequence < t->nnkeys); + t->sequence[anode->i_sequence] = i; + total += 1; + } + } + for (i = nhsize - 1; i >= 0; i--) { + ANode *anode = &t->node[i].anode; + if (anode->i_sequence >= 0) { + assert(anode->i_sequence < t->nnkeys); + t->sequence[anode->i_sequence] = i + t->sizearray; + total += 1; + } + } + t->nnkeys = total; } void luaH_resizearray (lua_State *L, Table *t, int nasize) { - int nsize = isdummy(t->node) ? 0 : sizenode(t); - luaH_resize(L, t, nasize, nsize); + luaH_resize(L, t, nasize, truesizenode(t)); } @@ -371,7 +511,13 @@ Table *luaH_new (lua_State *L) { t->flags = cast_byte(~0); t->array = NULL; t->sizearray = 0; - setnodevector(L, t, 0); + t->node = cast(Node *, dummynode); + t->lsizenode = 0; + t->lastfree = gnode(t, 0); + t->sequence = 0; + t->nnkeys = 0; + t->lastdeleted = NULL; + t->lastdelseq = -1; return t; } @@ -380,6 +526,8 @@ void luaH_free (lua_State *L, Table *t) { if (!isdummy(t->node)) luaM_freearray(L, t->node, cast(size_t, sizenode(t))); luaM_freearray(L, t->array, t->sizearray); + int seqlen = t->sizearray + truesizenode(t); + luaM_freearray(L, t->sequence, seqlen); luaM_free(L, t); } @@ -428,6 +576,9 @@ static TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key) { *n = *mp; /* copy colliding node into free pos. (mp->next also goes) */ gnext(mp) = NULL; /* now `mp' is free */ setnilvalue(gval(mp)); + gseq(mp) = -1; + int seqno = gseq(n); + t->sequence[seqno] = (n - t->node) + t->sizearray; } else { /* colliding node is in its own main position */ /* new node will go into free position */ @@ -449,7 +600,7 @@ static TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key) { const TValue *luaH_getint (Table *t, int key) { /* (1 <= key && key <= t->sizearray) */ if (cast(unsigned int, key-1) < cast(unsigned int, t->sizearray)) - return &t->array[key-1]; + return &t->array[key-1].i_val; else { lua_Number nk = cast_num(key); Node *n = hashnum(t, nk); @@ -505,19 +656,71 @@ const TValue *luaH_get (Table *t, const TValue *key) { } } +static void addsequence(Table *t, ANode *anode) { + assert(anode->i_sequence == -1); + int index = anodeindex(t, anode); + int totalanodes = t->sizearray + truesizenode(t); + assert((index >= 0) && (index < totalanodes)); + anode->i_sequence = t->nnkeys++; + assert((anode->i_sequence >= 0) && (anode->i_sequence < totalanodes)); + t->sequence[anode->i_sequence] = index; +} + +static void delsequence(Table *t, ANode *anode) { + assert((anode->i_sequence >= 0) && (anode->i_sequence < t->nnkeys)); + /* remove the last item from the sequence, and get a pointer to its anode */ + int totalanodes = t->sizearray + truesizenode(t); + assert((t->nnkeys > 0) && (t->nnkeys <= totalanodes)); + int lastanodeindex = t->sequence[t->nnkeys - 1]; + assert((lastanodeindex >= 0) && (lastanodeindex < totalanodes)); + ANode *lastanode = nthanode(t, lastanodeindex); + /* move the last anode to the freed slot */ + int freeslot = anode->i_sequence; + t->sequence[freeslot] = lastanodeindex; + t->nnkeys -= 1; + lastanode->i_sequence = freeslot; + /* set the last deleted variables */ + t->lastdeleted = anode; + t->lastdelseq = anode->i_sequence; + /* clear out the anode */ + anode->i_sequence = -1; +} /* ** beware: when using this function you probably need to check a GC ** barrier and invalidate the TM cache. */ void luaH_setupdate (lua_State *L, Table *t, const TValue *key, TValue *value, const TValue *getres) { - TValue *cell; - if (getres != luaO_nilobject) { - cell = cast(TValue *, getres); - } else { - cell = luaH_newkey(L, t, key); + t->lastdeleted = NULL; + t->lastdelseq = -1; + if (!ttisnil(getres)) { + if (ttisnil(value)) { + /* replacing a non-nil value with nil */ + TValue *cell = cast(TValue *, getres); + delsequence(t, cast(ANode *, cell)); + setobj2t(L, cell, value); + checksequence(t); + } else { + /* replacing a non-nil value with a different non-nil value */ + TValue *cell = cast(TValue *, getres); + setobj2t(L, cell, value); + checksequence(t); + } + } else if (getres == luaO_nilobject) { + if (!ttisnil(value)) { + /* creating a new key with a non-nil value */ + TValue *cell = luaH_newkey(L, t, key); + addsequence(t, cast(ANode *, cell)); + setobj2t(L, cell, value); + checksequence(t); + } + } else if (!ttisnil(value)) { + /* replacing a nil value with a non-nil value */ + TValue *cell = cast(TValue *, getres); + addsequence(t, cast(ANode *, cell)); + setobj2t(L, cell, value); + checksequence(t); } - setobj2t(L, cell, value); } void luaH_setvalue (lua_State *L, Table *t, const TValue *key, TValue *value) { @@ -525,19 +728,11 @@ void luaH_setvalue (lua_State *L, Table *t, const TValue *key, TValue *value) { } void luaH_setint (lua_State *L, Table *t, int key, TValue *value) { - const TValue *p = luaH_getint(t, key); - TValue *cell; - if (p != luaO_nilobject) - cell = cast(TValue *, p); - else { - TValue k; - setnvalue(&k, cast_num(key)); - cell = luaH_newkey(L, t, &k); - } - setobj2t(L, cell, value); + TValue kv; + setnvalue(&kv, key); + luaH_setupdate(L, t, &kv, value, luaH_getint(t, key)); } - /* ** Try to find a boundary in table `t'. A `boundary' is an integer index ** such that t[i] is non-nil and t[i+1] is nil (and 0 if t[1] is nil). diff --git a/luprex/eris-master/src/ltable.h b/luprex/eris-master/src/ltable.h index 4969cd35..0341b841 100644 --- a/luprex/eris-master/src/ltable.h +++ b/luprex/eris-master/src/ltable.h @@ -11,15 +11,17 @@ #define gnode(t,i) (&(t)->node[i]) -#define gkey(n) (&(n)->i_key.tvk) -#define gval(n) (&(n)->i_val) -#define gnext(n) ((n)->i_key.nk.next) +#define gkey(n) (&(n)->i_key) +#define gval(n) (&(n)->anode.i_val) +#define ganode(n) (&(n)->anode) +#define gseq(n) ((n)->anode.i_sequence) +#define gnext(n) ((n)->i_next) #define invalidateTMcache(t) ((t)->flags = 0) /* returns the key, given the value of a table entry */ #define keyfromval(v) \ - (gkey(cast(Node *, cast(char *, (v)) - offsetof(Node, i_val)))) + (gkey(cast(Node *, cast(char *, (v)) - offsetof(Node, anode.i_val)))) LUAI_FUNC const TValue *luaH_getint (Table *t, int key); @@ -32,6 +34,8 @@ LUAI_FUNC Table *luaH_new (lua_State *L); LUAI_FUNC void luaH_resize (lua_State *L, Table *t, int nasize, int nhsize); LUAI_FUNC void luaH_resizearray (lua_State *L, Table *t, int nasize); LUAI_FUNC void luaH_free (lua_State *L, Table *t); +LUAI_FUNC int luaH_nkeys (Table *t); +LUAI_FUNC int luaH_nthkey (lua_State *L, Table *t, int n, StkId pair); LUAI_FUNC int luaH_next (lua_State *L, Table *t, StkId key); LUAI_FUNC int luaH_getn (Table *t); diff --git a/luprex/eris-master/src/lua.h b/luprex/eris-master/src/lua.h index 26ca1a99..a7f54051 100644 --- a/luprex/eris-master/src/lua.h +++ b/luprex/eris-master/src/lua.h @@ -303,6 +303,7 @@ LUA_API int (lua_gc) (lua_State *L, int what, int data); LUA_API int (lua_error) (lua_State *L); LUA_API int (lua_next) (lua_State *L, int idx); +LUA_API int (lua_altnext) (lua_State *L, int idx); LUA_API void (lua_concat) (lua_State *L, int n); LUA_API void (lua_len) (lua_State *L, int idx); @@ -310,7 +311,8 @@ LUA_API void (lua_len) (lua_State *L, int idx); LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud); LUA_API void (lua_setallocf) (lua_State *L, lua_Alloc f, void *ud); - +LUA_API int (lua_nkeys) (lua_State *L, int idx); +LUA_API int (lua_nthkey) (lua_State *L, int idx, int n); /* ** ===============================================================