Skip to content

Commit 0e646b5

Browse files
committed
feature: implemented the new table.nkeys Lua API.
This Lua builtin returns the number of elements in a Lua table, i.e., the number of non-nil array elements plus the number of non-nil key-value pairs in a single Lua table. It can be used like this: local nkeys = require "table.nkeys" print(nkeys({})) -- 0 print(nkeys({ "a", nil, "b" })) -- 2 print(nkeys({ dog = 3, cat = 4, bird = nil })) -- 2 print(nkeys({ "a", dog = 3, cat = 4 })) -- 3 This Lua API can be JIT compiled. Signed-off-by: Yichun Zhang (agentzh) <agentzh@gmail.com>
1 parent 5e4cf2b commit 0e646b5

File tree

7 files changed

+244
-0
lines changed

7 files changed

+244
-0
lines changed

README

+17
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,23 @@ the upstream LuaJIT (https://github.com/LuaJIT/LuaJIT), which are
6060
print(isarr{"dog" = 3}) -- false
6161
print(isarr{}) -- true
6262

63+
* feature: implemented the new table.nkeys Lua API.
64+
65+
This Lua builtin returns the number of elements in a Lua table, i.e.,
66+
the number of non-nil array elements plus the number of non-nil key-value
67+
pairs in a single Lua table.
68+
69+
It can be used like this:
70+
71+
local nkeys = require "table.nkeys"
72+
73+
print(nkeys({})) -- 0
74+
print(nkeys({ "a", nil, "b" })) -- 2
75+
print(nkeys({ dog = 3, cat = 4, bird = nil })) -- 2
76+
print(nkeys({ "a", dog = 3, cat = 4 })) -- 3
77+
78+
This Lua API can be JIT compiled.
79+
6380
* feature: implemented the new Lua and C API functions for thread exdata.
6481

6582
The Lua API can be used like below:

src/lib_table.c

+16
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,16 @@ LJLIB_NOREG LJLIB_CF(table_isarray) LJLIB_REC(.)
190190
return 1;
191191
}
192192

193+
LJLIB_NOREG LJLIB_CF(table_nkeys) LJLIB_REC(.)
194+
{
195+
GCtab *src = lj_lib_checktab(L, 1);
196+
197+
setintV(L->base, lj_tab_nkeys(src));
198+
L->top = L->base+1;
199+
200+
return 1;
201+
}
202+
193203
LJLIB_NOREG LJLIB_CF(table_isempty) LJLIB_REC(.)
194204
{
195205
GCtab *src = lj_lib_checktab(L, 1);
@@ -340,6 +350,11 @@ static int luaopen_table_clone(lua_State *L)
340350
return lj_lib_postreg(L, lj_cf_table_clone, FF_table_clone, "clone");
341351
}
342352

353+
static int luaopen_table_nkeys(lua_State *L)
354+
{
355+
return lj_lib_postreg(L, lj_cf_table_nkeys, FF_table_nkeys, "nkeys");
356+
}
357+
343358
static int luaopen_table_isarray(lua_State *L)
344359
{
345360
return lj_lib_postreg(L, lj_cf_table_isarray, FF_table_isarray, "isarray");
@@ -369,6 +384,7 @@ LUALIB_API int luaopen_table(lua_State *L)
369384
lj_lib_prereg(L, LUA_TABLIBNAME ".new", luaopen_table_new, tabV(L->top-1));
370385
lj_lib_prereg(L, LUA_TABLIBNAME ".clone", luaopen_table_clone, tabV(L->top-1));
371386
lj_lib_prereg(L, LUA_TABLIBNAME ".isarray", luaopen_table_isarray, tabV(L->top-1));
387+
lj_lib_prereg(L, LUA_TABLIBNAME ".nkeys", luaopen_table_nkeys, tabV(L->top-1));
372388
lj_lib_prereg(L, LUA_TABLIBNAME ".isempty", luaopen_table_isempty, tabV(L->top-1));
373389
lj_lib_prereg(L, LUA_TABLIBNAME ".clear", luaopen_table_clear, tabV(L->top-1));
374390
return 1;

src/lj_ffrecord.c

+8
Original file line numberDiff line numberDiff line change
@@ -1126,6 +1126,14 @@ static void LJ_FASTCALL recff_table_isarray(jit_State *J, RecordFFData *rd)
11261126
} /* else: Interpreter will throw. */
11271127
}
11281128

1129+
static void LJ_FASTCALL recff_table_nkeys(jit_State *J, RecordFFData *rd)
1130+
{
1131+
TRef src = J->base[0];
1132+
if (LJ_LIKELY(tref_istab(src))) {
1133+
J->base[0] = lj_ir_call(J, IRCALL_lj_tab_nkeys, src);
1134+
} /* else: Interpreter will throw. */
1135+
}
1136+
11291137
static void LJ_FASTCALL recff_table_isempty(jit_State *J, RecordFFData *rd)
11301138
{
11311139
TRef src = J->base[0];

src/lj_ircall.h

+1
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ typedef struct CCallInfo {
169169
_(ANY, lj_tab_len, 1, FL, INT, 0) \
170170
_(ANY, lj_tab_clone, 2, FS, TAB, CCI_L) \
171171
_(ANY, lj_tab_isarray, 1, FL, INT, 0) \
172+
_(ANY, lj_tab_nkeys, 1, FL, INT, 0) \
172173
_(ANY, lj_tab_isempty, 1, FL, INT, 0) \
173174
_(ANY, lj_gc_step_jit, 2, FS, NIL, CCI_L) \
174175
_(ANY, lj_gc_barrieruv, 2, FS, NIL, 0) \

src/lj_tab.c

+26
Original file line numberDiff line numberDiff line change
@@ -719,6 +719,32 @@ int LJ_FASTCALL lj_tab_isarray(const GCtab *src)
719719
return 1;
720720
}
721721

722+
MSize LJ_FASTCALL lj_tab_nkeys(const GCtab *t)
723+
{
724+
MSize narr = (MSize)t->asize;
725+
cTValue *e;
726+
Node *node;
727+
MSize i, cnt = 0;
728+
729+
e = tvref(t->array);
730+
for (i = 0; i < narr; i++)
731+
if (LJ_LIKELY(!tvisnil(&e[i])))
732+
cnt++;
733+
734+
if (t->hmask <= 0)
735+
return cnt;
736+
737+
node = noderef(t->node);
738+
for (i = 0; i <= (MSize)t->hmask; i++) {
739+
Node *n = &node[i];
740+
if (LJ_LIKELY(!tvisnil(&n->val))) {
741+
cnt++;
742+
}
743+
}
744+
745+
return cnt;
746+
}
747+
722748
int LJ_FASTCALL lj_tab_isempty(const GCtab *t)
723749
{
724750
MSize narr = (MSize)t->asize;

src/lj_tab.h

+1
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ LJ_FUNCA MSize LJ_FASTCALL lj_tab_len(GCtab *t);
7272

7373
LJ_FUNCA GCtab * LJ_FASTCALL lj_tab_clone(lua_State *L, const GCtab *src);
7474
LJ_FUNCA int LJ_FASTCALL lj_tab_isarray(const GCtab *src);
75+
LJ_FUNCA MSize LJ_FASTCALL lj_tab_nkeys(const GCtab *src);
7576
LJ_FUNCA int LJ_FASTCALL lj_tab_isempty(const GCtab *t);
7677

7778
#endif

t/nkeys.t

+175
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
# vim: set ss=4 ft= sw=4 et sts=4 ts=4:
2+
3+
use lib '.';
4+
use t::TestLJ;
5+
6+
plan tests => 3 * blocks();
7+
8+
run_tests();
9+
10+
__DATA__
11+
12+
=== TEST 1: hash table, interpreted
13+
--- lua
14+
jit.off()
15+
local new_tab = require "table.new"
16+
local assert = assert
17+
local nkeys = require "table.nkeys"
18+
print(nkeys(new_tab(0, 4)))
19+
print(nkeys({}))
20+
print(nkeys({ cats = 4 }))
21+
print(nkeys({ dogs = 3, cats = 4 }))
22+
print(nkeys({ dogs = nil, cats = 4 }))
23+
--- jv
24+
--- out
25+
0
26+
0
27+
1
28+
2
29+
1
30+
--- err
31+
32+
33+
34+
=== TEST 2: hash table, JIT
35+
--- lua
36+
jit.on()
37+
jit.opt.start("minstitch=100000", "hotloop=2")
38+
39+
local new_tab = require "table.new"
40+
local assert = assert
41+
local nkeys = require "table.nkeys"
42+
43+
local list = {
44+
new_tab(0, 4),
45+
{},
46+
{ cats = 4 },
47+
{ dogs = 3, cats = 4 },
48+
{ dogs = nil, cats = 4 },
49+
}
50+
51+
for i, t in ipairs(list) do
52+
local total = 0
53+
for i = 1, 10 do
54+
total = total + nkeys(t)
55+
end
56+
print(total)
57+
end
58+
--- jv
59+
--- out
60+
0
61+
0
62+
10
63+
20
64+
10
65+
--- err
66+
[TRACE 1 test.lua:18 loop]
67+
[TRACE 2 test.lua:16 -> 1]
68+
69+
70+
71+
=== TEST 3: pure arrays, interpreted
72+
--- lua
73+
jit.off()
74+
local new_tab = require "table.new"
75+
local assert = assert
76+
local nkeys = require "table.nkeys"
77+
print(nkeys(new_tab(5, 0)))
78+
print(nkeys({}))
79+
print(nkeys({ "cats" }))
80+
print(nkeys({ "dogs", 3, "cats", 4 }))
81+
print(nkeys({ "dogs", nil, "cats", 4 }))
82+
--- jv
83+
--- out
84+
0
85+
0
86+
1
87+
4
88+
3
89+
--- err
90+
91+
92+
93+
=== TEST 4: pure array, JIT
94+
--- lua
95+
jit.on()
96+
jit.opt.start("minstitch=100000", "hotloop=2")
97+
98+
local new_tab = require "table.new"
99+
local assert = assert
100+
local nkeys = require "table.nkeys"
101+
102+
local list = {
103+
new_tab(0, 4),
104+
{},
105+
{ 3 },
106+
{ "cats", 4 },
107+
{ "dogs", 3, "cats", 4 },
108+
{ "dogs", nil, "cats", 4 },
109+
}
110+
111+
for i, t in ipairs(list) do
112+
local total = 0
113+
for i = 1, 10 do
114+
total = total + nkeys(t)
115+
end
116+
print(total)
117+
end
118+
--- jv
119+
--- out
120+
0
121+
0
122+
10
123+
20
124+
40
125+
30
126+
--- err
127+
[TRACE 1 test.lua:19 loop]
128+
[TRACE 2 test.lua:17 -> 1]
129+
130+
131+
132+
=== TEST 5: mixing array and hash table, interpreted
133+
--- lua
134+
jit.off()
135+
local new_tab = require "table.new"
136+
local assert = assert
137+
local nkeys = require "table.nkeys"
138+
print(nkeys({ cats = 4, 5, 6 }))
139+
print(nkeys({ nil, "foo", dogs = 3, cats = 4 }))
140+
--- jv
141+
--- out
142+
3
143+
3
144+
--- err
145+
146+
147+
148+
=== TEST 6: mixing array & hash, JIT
149+
--- lua
150+
jit.on()
151+
jit.opt.start("minstitch=100000", "hotloop=2")
152+
153+
local new_tab = require "table.new"
154+
local assert = assert
155+
local nkeys = require "table.nkeys"
156+
157+
local list = {
158+
{ cats = 4, 5, 6 },
159+
{ nil, "foo", dogs = 3, cats = 4 },
160+
}
161+
162+
for i, t in ipairs(list) do
163+
local total = 0
164+
for i = 1, 10 do
165+
total = total + nkeys(t)
166+
end
167+
print(total)
168+
end
169+
--- jv
170+
--- out
171+
30
172+
30
173+
--- err
174+
[TRACE 1 test.lua:15 loop]
175+
[TRACE 2 test.lua:13 -> 1]

0 commit comments

Comments
 (0)