diff options
| -rw-r--r-- | ltable.c | 27 | ||||
| -rw-r--r-- | ltests.c | 11 | ||||
| -rw-r--r-- | testes/nextvar.lua | 72 |
3 files changed, 81 insertions, 29 deletions
| @@ -471,12 +471,23 @@ typedef struct { | |||
| 471 | unsigned nums[MAXABITS + 1]; | 471 | unsigned nums[MAXABITS + 1]; |
| 472 | } Counters; | 472 | } Counters; |
| 473 | 473 | ||
| 474 | |||
| 475 | /* | ||
| 476 | ** Check whether it is worth to use 'na' array entries instead of 'nh' | ||
| 477 | ** hash nodes. (A hash node uses ~3 times more memory than an array | ||
| 478 | ** entry: Two values plus 'next' versus one value.) Evaluate with size_t | ||
| 479 | ** to avoid overflows. | ||
| 480 | */ | ||
| 481 | #define arrayXhash(na,nh) (cast_sizet(na) <= cast_sizet(nh) * 3) | ||
| 482 | |||
| 474 | /* | 483 | /* |
| 475 | ** Compute the optimal size for the array part of table 't'. | 484 | ** Compute the optimal size for the array part of table 't'. |
| 485 | ** This size maximizes the number of elements going to the array part | ||
| 486 | ** while satisfying the condition 'arrayXhash' with the use of memory if | ||
| 487 | ** all those elements went to the hash part. | ||
| 476 | ** 'ct->na' enters with the total number of array indices in the table | 488 | ** 'ct->na' enters with the total number of array indices in the table |
| 477 | ** and leaves with the number of keys that will go to the array part; | 489 | ** and leaves with the number of keys that will go to the array part; |
| 478 | ** return the optimal size. (The condition 'twotoi > 0' in the for loop | 490 | ** return the optimal size for the array part. |
| 479 | ** stops the loop if 'twotoi' overflows.) | ||
| 480 | */ | 491 | */ |
| 481 | static unsigned computesizes (Counters *ct) { | 492 | static unsigned computesizes (Counters *ct) { |
| 482 | int i; | 493 | int i; |
| @@ -484,17 +495,19 @@ static unsigned computesizes (Counters *ct) { | |||
| 484 | unsigned int a = 0; /* number of elements smaller than 2^i */ | 495 | unsigned int a = 0; /* number of elements smaller than 2^i */ |
| 485 | unsigned int na = 0; /* number of elements to go to array part */ | 496 | unsigned int na = 0; /* number of elements to go to array part */ |
| 486 | unsigned int optimal = 0; /* optimal size for array part */ | 497 | unsigned int optimal = 0; /* optimal size for array part */ |
| 487 | /* loop while keys can fill more than half of total size */ | 498 | /* traverse slices while 'twotoi' does not overflow and total of array |
| 499 | indices still can satisfy 'arrayXhash' against the array size */ | ||
| 488 | for (i = 0, twotoi = 1; | 500 | for (i = 0, twotoi = 1; |
| 489 | twotoi > 0 && ct->na > twotoi / 2; | 501 | twotoi > 0 && arrayXhash(twotoi, ct->na); |
| 490 | i++, twotoi *= 2) { | 502 | i++, twotoi *= 2) { |
| 491 | a += ct->nums[i]; | 503 | unsigned nums = ct->nums[i]; |
| 492 | if (a > twotoi/2) { /* more than half elements present? */ | 504 | a += nums; |
| 505 | if (nums > 0 && /* grows array only if it gets more elements... */ | ||
| 506 | arrayXhash(twotoi, a)) { /* ...while using "less memory" */ | ||
| 493 | optimal = twotoi; /* optimal size (till now) */ | 507 | optimal = twotoi; /* optimal size (till now) */ |
| 494 | na = a; /* all elements up to 'optimal' will go to array part */ | 508 | na = a; /* all elements up to 'optimal' will go to array part */ |
| 495 | } | 509 | } |
| 496 | } | 510 | } |
| 497 | lua_assert((optimal == 0 || optimal / 2 < na) && na <= optimal); | ||
| 498 | ct->na = na; | 511 | ct->na = na; |
| 499 | return optimal; | 512 | return optimal; |
| 500 | } | 513 | } |
| @@ -1043,7 +1043,10 @@ static int table_query (lua_State *L) { | |||
| 1043 | } | 1043 | } |
| 1044 | else if (cast_uint(i) < asize) { | 1044 | else if (cast_uint(i) < asize) { |
| 1045 | lua_pushinteger(L, i); | 1045 | lua_pushinteger(L, i); |
| 1046 | arr2obj(t, cast_uint(i), s2v(L->top.p)); | 1046 | if (!tagisempty(*getArrTag(t, i))) |
| 1047 | arr2obj(t, cast_uint(i), s2v(L->top.p)); | ||
| 1048 | else | ||
| 1049 | setnilvalue(s2v(L->top.p)); | ||
| 1047 | api_incr_top(L); | 1050 | api_incr_top(L); |
| 1048 | lua_pushnil(L); | 1051 | lua_pushnil(L); |
| 1049 | } | 1052 | } |
| @@ -1057,11 +1060,11 @@ static int table_query (lua_State *L) { | |||
| 1057 | } | 1060 | } |
| 1058 | else | 1061 | else |
| 1059 | lua_pushliteral(L, "<undef>"); | 1062 | lua_pushliteral(L, "<undef>"); |
| 1060 | pushobject(L, gval(gnode(t, i))); | 1063 | if (!isempty(gval(gnode(t, i)))) |
| 1061 | if (gnext(&t->node[i]) != 0) | 1064 | pushobject(L, gval(gnode(t, i))); |
| 1062 | lua_pushinteger(L, gnext(&t->node[i])); | ||
| 1063 | else | 1065 | else |
| 1064 | lua_pushnil(L); | 1066 | lua_pushnil(L); |
| 1067 | lua_pushinteger(L, gnext(&t->node[i])); | ||
| 1065 | } | 1068 | } |
| 1066 | return 3; | 1069 | return 3; |
| 1067 | } | 1070 | } |
diff --git a/testes/nextvar.lua b/testes/nextvar.lua index cee77f76..85c9ff6b 100644 --- a/testes/nextvar.lua +++ b/testes/nextvar.lua | |||
| @@ -9,6 +9,22 @@ local function checkerror (msg, f, ...) | |||
| 9 | end | 9 | end |
| 10 | 10 | ||
| 11 | 11 | ||
| 12 | |||
| 13 | ---------------------------------------------------------------- | ||
| 14 | local function printTable (t) | ||
| 15 | local a, h = T.querytab(t) | ||
| 16 | print("array:") | ||
| 17 | for i = 1, a do | ||
| 18 | print("", T.querytab(t, i - 1)) | ||
| 19 | end | ||
| 20 | print("hash:") | ||
| 21 | for i = 1, h do | ||
| 22 | print("", T.querytab(t, a + i - 1)) | ||
| 23 | end | ||
| 24 | end | ||
| 25 | ---------------------------------------------------------------- | ||
| 26 | |||
| 27 | |||
| 12 | local function check (t, na, nh) | 28 | local function check (t, na, nh) |
| 13 | if not T then return end | 29 | if not T then return end |
| 14 | local a, h = T.querytab(t) | 30 | local a, h = T.querytab(t) |
| @@ -106,9 +122,10 @@ else --[ | |||
| 106 | -- testing table sizes | 122 | -- testing table sizes |
| 107 | 123 | ||
| 108 | 124 | ||
| 109 | local function mp2 (n) -- minimum power of 2 >= n | 125 | -- minimum power of 2 (or zero) >= n |
| 126 | local function mp2 (n) | ||
| 110 | local mp = 2^math.ceil(math.log(n, 2)) | 127 | local mp = 2^math.ceil(math.log(n, 2)) |
| 111 | assert(n == 0 or (mp/2 < n and n <= mp)) | 128 | assert((mp == 0 or mp/2 < n) and n <= mp) |
| 112 | return mp | 129 | return mp |
| 113 | end | 130 | end |
| 114 | 131 | ||
| @@ -123,7 +140,7 @@ end | |||
| 123 | 140 | ||
| 124 | -- testing constructor sizes | 141 | -- testing constructor sizes |
| 125 | local sizes = {0, 1, 2, 3, 4, 5, 7, 8, 9, 15, 16, 17, | 142 | local sizes = {0, 1, 2, 3, 4, 5, 7, 8, 9, 15, 16, 17, |
| 126 | 30, 31, 32, 33, 34, 254, 255, 256, 500, 1000} | 143 | 30, 31, 32, 33, 34, 254, 255, 256, 257, 500, 1001} |
| 127 | 144 | ||
| 128 | for _, sa in ipairs(sizes) do -- 'sa' is size of the array part | 145 | for _, sa in ipairs(sizes) do -- 'sa' is size of the array part |
| 129 | local arr = {"return {"} | 146 | local arr = {"return {"} |
| @@ -167,8 +184,9 @@ end | |||
| 167 | 184 | ||
| 168 | -- testing tables dynamically built | 185 | -- testing tables dynamically built |
| 169 | local lim = 130 | 186 | local lim = 130 |
| 170 | local a = {}; a[2] = 1; check(a, 0, 1) | 187 | local a = {}; a[2] = 1; check(a, 2, 0) |
| 171 | a = {}; a[0] = 1; check(a, 0, 1); a[2] = 1; check(a, 0, 2) | 188 | a = {}; a[0] = 1; check(a, 0, 1); |
| 189 | a[2] = 1; check(a, 2, 1) | ||
| 172 | a = {}; a[0] = 1; a[1] = 1; check(a, 1, 1) | 190 | a = {}; a[0] = 1; a[1] = 1; check(a, 1, 1) |
| 173 | a = {} | 191 | a = {} |
| 174 | for i = 1,lim do | 192 | for i = 1,lim do |
| @@ -184,28 +202,46 @@ for i = 1,lim do | |||
| 184 | check(a, 0, mp2(i)) | 202 | check(a, 0, mp2(i)) |
| 185 | end | 203 | end |
| 186 | 204 | ||
| 187 | a = {} | ||
| 188 | for i=1,16 do a[i] = i end | ||
| 189 | check(a, 16, 0) | ||
| 190 | do | 205 | do |
| 206 | local a = {} | ||
| 207 | for i=1,16 do a[i] = i end | ||
| 208 | check(a, 16, 0) | ||
| 191 | for i=1,11 do a[i] = undef end | 209 | for i=1,11 do a[i] = undef end |
| 192 | for i=30,50 do a[i] = true; a[i] = undef end -- force a rehash (?) | 210 | check(a, 16, 0) |
| 193 | check(a, 0, 8) -- 5 elements in the table | 211 | a[30] = true -- force a rehash |
| 212 | a[30] = undef | ||
| 213 | check(a, 0, 8) -- 5 elements in the hash part: [12]-[16] | ||
| 194 | a[10] = 1 | 214 | a[10] = 1 |
| 195 | for i=30,50 do a[i] = true; a[i] = undef end -- force a rehash (?) | 215 | for i=30,50 do a[i] = true; a[i] = undef end -- force a rehash |
| 196 | check(a, 0, 8) -- only 6 elements in the table | 216 | check(a, 16, 1) |
| 197 | for i=1,14 do a[i] = true; a[i] = undef end | 217 | for i=1,14 do a[i] = true; a[i] = undef end |
| 198 | for i=18,50 do a[i] = true; a[i] = undef end -- force a rehash (?) | 218 | check(a, 16, 1) -- no rehash... |
| 199 | check(a, 0, 4) -- only 2 elements ([15] and [16]) | 219 | a[31] = true; a[32] = true -- force a rehash |
| 220 | check(a, 0, 4) -- [15], [16], [31], [32] | ||
| 200 | end | 221 | end |
| 201 | 222 | ||
| 202 | -- reverse filling | 223 | -- reverse filling |
| 203 | for i=1,lim do | 224 | do |
| 225 | local N = 2^10 | ||
| 204 | local a = {} | 226 | local a = {} |
| 205 | for i=i,1,-1 do a[i] = i end -- fill in reverse | 227 | for i = N, 1, -1 do a[i] = i end -- fill in reverse |
| 206 | check(a, mp2(i), 0) | 228 | check(a, mp2(N), 0) |
| 207 | end | 229 | end |
| 208 | 230 | ||
| 231 | |||
| 232 | do -- "almost sparse" arrays | ||
| 233 | -- create table with holes in 1/3 of its entries; all its | ||
| 234 | -- elements are always in the array part | ||
| 235 | local a = {} | ||
| 236 | for i = 1, 257 do | ||
| 237 | if i % 3 ~= 1 then | ||
| 238 | a[i] = true | ||
| 239 | check(a, mp2(i), 0) | ||
| 240 | end | ||
| 241 | end | ||
| 242 | end | ||
| 243 | |||
| 244 | |||
| 209 | -- size tests for vararg | 245 | -- size tests for vararg |
| 210 | lim = 35 | 246 | lim = 35 |
| 211 | local function foo (n, ...) | 247 | local function foo (n, ...) |
| @@ -840,7 +876,7 @@ do | |||
| 840 | co() -- start coroutine | 876 | co() -- start coroutine |
| 841 | co(1) -- continue after yield | 877 | co(1) -- continue after yield |
| 842 | assert(res[1] == 30 and res[2] == 20 and res[3] == 10 and #res == 3) | 878 | assert(res[1] == 30 and res[2] == 20 and res[3] == 10 and #res == 3) |
| 843 | 879 | ||
| 844 | end | 880 | end |
| 845 | 881 | ||
| 846 | print"OK" | 882 | print"OK" |
