diff options
author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2017-05-16 16:07:08 -0300 |
---|---|---|
committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2017-05-16 16:07:08 -0300 |
commit | 3d879fbc5df8076aa02bc0cec48e10b6a9ef0665 (patch) | |
tree | 9e155bfa58c159afdb82d43eb7e6fa159c8e0fa4 | |
parent | 6d95de83c644da5f720cf468c38df9782f1c890d (diff) | |
download | lua-3d879fbc5df8076aa02bc0cec48e10b6a9ef0665.tar.gz lua-3d879fbc5df8076aa02bc0cec48e10b6a9ef0665.tar.bz2 lua-3d879fbc5df8076aa02bc0cec48e10b6a9ef0665.zip |
reimplementation of 'luaH_getn', trying to handle numeric limits
properly.
-rw-r--r-- | ltable.c | 77 |
1 files changed, 44 insertions, 33 deletions
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | ** $Id: ltable.c,v 2.118 2016/11/07 12:38:35 roberto Exp roberto $ | 2 | ** $Id: ltable.c,v 2.119 2017/05/09 14:39:46 roberto Exp roberto $ |
3 | ** Lua tables (hash) | 3 | ** Lua tables (hash) |
4 | ** See Copyright Notice in lua.h | 4 | ** See Copyright Notice in lua.h |
5 | */ | 5 | */ |
@@ -224,12 +224,10 @@ static unsigned int computesizes (unsigned int nums[], unsigned int *pna) { | |||
224 | unsigned int optimal = 0; /* optimal size for array part */ | 224 | unsigned int optimal = 0; /* optimal size for array part */ |
225 | /* loop while keys can fill more than half of total size */ | 225 | /* loop while keys can fill more than half of total size */ |
226 | for (i = 0, twotoi = 1; *pna > twotoi / 2; i++, twotoi *= 2) { | 226 | for (i = 0, twotoi = 1; *pna > twotoi / 2; i++, twotoi *= 2) { |
227 | if (nums[i] > 0) { | 227 | a += nums[i]; |
228 | a += nums[i]; | 228 | if (a > twotoi/2) { /* more than half elements present? */ |
229 | if (a > twotoi/2) { /* more than half elements present? */ | 229 | optimal = twotoi; /* optimal size (till now) */ |
230 | optimal = twotoi; /* optimal size (till now) */ | 230 | na = a; /* all elements up to 'optimal' will go to array part */ |
231 | na = a; /* all elements up to 'optimal' will go to array part */ | ||
232 | } | ||
233 | } | 231 | } |
234 | } | 232 | } |
235 | lua_assert((optimal == 0 || optimal / 2 < na) && na <= optimal); | 233 | lua_assert((optimal == 0 || optimal / 2 < na) && na <= optimal); |
@@ -610,23 +608,31 @@ void luaH_setint (lua_State *L, Table *t, lua_Integer key, TValue *value) { | |||
610 | } | 608 | } |
611 | 609 | ||
612 | 610 | ||
613 | static int unbound_search (Table *t, unsigned int j) { | 611 | /* |
614 | unsigned int i = j; /* i is zero or a present index */ | 612 | ** Try to find a boundary in the hash part of table 't'. From the |
615 | j++; | 613 | ** caller, we know that 'i' is zero or present. We need to find an |
616 | /* find 'i' and 'j' such that i is present and j is not */ | 614 | ** upper bound (an absent index larger than 'i') to do a binary search |
617 | while (!ttisnil(luaH_getint(t, j))) { | 615 | ** for a boundary. We try 'max', a number larger than the total number |
618 | i = j; | 616 | ** of keys in the table. (Given the size of the array elements, 'max' |
619 | if (j > cast(unsigned int, MAX_INT)/2) { /* overflow? */ | 617 | ** computation cannot overflow a 'size_t'.) If 'max' does not fit in a |
620 | /* table was built with bad purposes: resort to linear search */ | 618 | ** lua_Integer or it is present in the table, we try LUA_MAXINTEGER. If |
621 | i = 1; | 619 | ** LUA_MAXINTEGER is present, it is a boundary, so we are done. Otherwise, |
622 | while (!ttisnil(luaH_getint(t, i))) i++; | 620 | ** we are left with a 'j' that is within the size of lua_Integers and |
623 | return i - 1; | 621 | ** absent, so can do the binary search. |
624 | } | 622 | */ |
625 | j *= 2; | 623 | static lua_Unsigned hash_search (Table *t, lua_Unsigned i) { |
624 | lua_Unsigned j; | ||
625 | size_t max = (cast(size_t, i) + sizenode(t) + 10) * 2; | ||
626 | if (max <= l_castS2U(LUA_MAXINTEGER) && ttisnil(luaH_getint(t, max))) | ||
627 | j = max; | ||
628 | else { | ||
629 | j = LUA_MAXINTEGER; | ||
630 | if (!ttisnil(luaH_getint(t, j))) /* weird case? */ | ||
631 | return j; /* well, that is a boundary... */ | ||
626 | } | 632 | } |
627 | /* now do a binary search between them */ | 633 | /* now, 'i' is zero or present and 'j' is absent */ |
628 | while (j - i > 1) { | 634 | while (j - i > 1u) { /* do a binary search between them */ |
629 | unsigned int m = (i+j)/2; | 635 | size_t m = (i + j) / 2; |
630 | if (ttisnil(luaH_getint(t, m))) j = m; | 636 | if (ttisnil(luaH_getint(t, m))) j = m; |
631 | else i = m; | 637 | else i = m; |
632 | } | 638 | } |
@@ -635,25 +641,30 @@ static int unbound_search (Table *t, unsigned int j) { | |||
635 | 641 | ||
636 | 642 | ||
637 | /* | 643 | /* |
638 | ** Try to find a boundary in table 't'. A 'boundary' is an integer index | 644 | ** Try to find a boundary in table 't'. (A 'boundary' is an integer index |
639 | ** such that t[i] is non-nil and t[i+1] is nil (and 0 if t[1] is nil). | 645 | ** such that t[i] is non-nil and t[i+1] is nil (or 0 if t[1] is nil).) |
646 | ** First, try the array part: if there is an array part and its last | ||
647 | ** element is nil, there must be a boundary there; a binary search | ||
648 | ** finds that boundary. Otherwise, if the hash part is empty or does not | ||
649 | ** contain 'j + 1', 'j' is a boundary. Othersize, call 'hash_search' | ||
650 | ** to find a boundary in the hash part. | ||
640 | */ | 651 | */ |
641 | int luaH_getn (Table *t) { | 652 | lua_Unsigned luaH_getn (Table *t) { |
642 | unsigned int j = t->sizearray; | 653 | unsigned int j = t->sizearray; |
643 | if (j > 0 && ttisnil(&t->array[j - 1])) { | 654 | if (j > 0 && ttisnil(&t->array[j - 1])) { |
644 | /* there is a boundary in the array part: (binary) search for it */ | ||
645 | unsigned int i = 0; | 655 | unsigned int i = 0; |
646 | while (j - i > 1) { | 656 | while (j - i > 1u) { /* binary search */ |
647 | unsigned int m = (i+j)/2; | 657 | unsigned int m = (i + j) / 2; |
648 | if (ttisnil(&t->array[m - 1])) j = m; | 658 | if (ttisnil(&t->array[m - 1])) j = m; |
649 | else i = m; | 659 | else i = m; |
650 | } | 660 | } |
651 | return i; | 661 | return i; |
652 | } | 662 | } |
653 | /* else must find a boundary in hash part */ | 663 | /* 'j' is zero or present in table */ |
654 | else if (isdummy(t)) /* hash part is empty? */ | 664 | else if (isdummy(t) || ttisnil(luaH_getint(t, l_castU2S(j + 1)))) |
655 | return j; /* that is easy... */ | 665 | return j; /* 'j + 1' is absent... */ |
656 | else return unbound_search(t, j); | 666 | else |
667 | return hash_search(t, j); | ||
657 | } | 668 | } |
658 | 669 | ||
659 | 670 | ||