diff options
| author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2023-10-27 16:32:49 -0300 |
|---|---|---|
| committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2023-10-27 16:32:49 -0300 |
| commit | b8b709b6d40c5c18d9b8ef33bb50afc55f048ab8 (patch) | |
| tree | 9b7ddf7ce2ee3e5f04f1f6b8a1ffae91156e7536 | |
| parent | 819bd51d87b799fdee029754c660dc9a5587ef57 (diff) | |
| download | lua-b8b709b6d40c5c18d9b8ef33bb50afc55f048ab8.tar.gz lua-b8b709b6d40c5c18d9b8ef33bb50afc55f048ab8.tar.bz2 lua-b8b709b6d40c5c18d9b8ef33bb50afc55f048ab8.zip | |
Avoid direct accesses to the array part of a table
| -rw-r--r-- | ltable.c | 128 |
1 files changed, 79 insertions, 49 deletions
| @@ -719,16 +719,38 @@ void luaH_newkey (lua_State *L, Table *t, const TValue *key, TValue *value) { | |||
| 719 | } | 719 | } |
| 720 | 720 | ||
| 721 | 721 | ||
| 722 | static const TValue *getintfromarray (Table *t, lua_Integer key) { | 722 | /* |
| 723 | if (l_castS2U(key) - 1u < t->alimit) /* 'key' in [1, t->alimit]? */ | 723 | ** Check whether key is in the array part. If 'alimit' is not the real |
| 724 | return &t->array[key - 1]; | 724 | ** size of the array, the key still can be in the array part. In this |
| 725 | else if (!limitequalsasize(t) && /* key still may be in the array part? */ | 725 | ** case, do the "Xmilia trick" to check whether 'key-1' is smaller than |
| 726 | (l_castS2U(key) == t->alimit + 1 || | 726 | ** the real size. |
| 727 | l_castS2U(key) - 1u < luaH_realasize(t))) { | 727 | ** The trick works as follow: let 'p' be an integer such that |
| 728 | ** '2^(p+1) >= alimit > 2^p', or '2^(p+1) > alimit-1 >= 2^p'. | ||
| 729 | ** That is, 2^(p+1) is the real size of the array, and 'p' is the highest | ||
| 730 | ** bit on in 'alimit-1'. What we have to check becomes 'key-1 < 2^(p+1)'. | ||
| 731 | ** We compute '(key-1) & ~(alimit-1)', which we call 'res'; it will | ||
| 732 | ** have the 'p' bit cleared. If the key is outside the array, that is, | ||
| 733 | ** 'key-1 >= 2^(p+1)', then 'res' will have some 1-bit higher than 'p', | ||
| 734 | ** therefore it will be larger or equal to 'alimit', and the check | ||
| 735 | ** will fail. If 'key-1 < 2^(p+1)', then 'res' has no 1-bit higher than | ||
| 736 | ** 'p', and as the bit 'p' itself was cleared, 'res' will be smaller | ||
| 737 | ** than 2^p, therefore smaller than 'alimit', and the check succeeds. | ||
| 738 | ** As special cases, when 'alimit' is 0 the condition is trivially false, | ||
| 739 | ** and when 'alimit' is 1 the condition simplifies to 'key-1 < alimit'. | ||
| 740 | ** If key is 0 or negative, 'res' will have its higher bit on, so that | ||
| 741 | ** if cannot be smaller than alimit. | ||
| 742 | */ | ||
| 743 | static int keyinarray (Table *t, lua_Integer key) { | ||
| 744 | lua_Unsigned alimit = t->alimit; | ||
| 745 | if (l_castS2U(key) - 1u < alimit) /* 'key' in [1, t->alimit]? */ | ||
| 746 | return 1; | ||
| 747 | else if (!isrealasize(t) && /* key still may be in the array part? */ | ||
| 748 | (((l_castS2U(key) - 1u) & ~(alimit - 1u)) < alimit)) { | ||
| 728 | t->alimit = cast_uint(key); /* probably '#t' is here now */ | 749 | t->alimit = cast_uint(key); /* probably '#t' is here now */ |
| 729 | return &t->array[key - 1]; | 750 | return 1; |
| 730 | } | 751 | } |
| 731 | else return NULL; /* key is not in the array part */ | 752 | else |
| 753 | return 0; | ||
| 732 | } | 754 | } |
| 733 | 755 | ||
| 734 | 756 | ||
| @@ -748,20 +770,16 @@ static const TValue *getintfromhash (Table *t, lua_Integer key) { | |||
| 748 | } | 770 | } |
| 749 | 771 | ||
| 750 | 772 | ||
| 751 | /* | 773 | l_sinline int arraykeyisempty (Table *t, lua_Integer key) { |
| 752 | ** Search function for integers. If integer is inside 'alimit', get it | 774 | int tag = *getArrTag(t, key); |
| 753 | ** directly from the array part. Otherwise, if 'alimit' is not equal to | 775 | return tagisempty(tag); |
| 754 | ** the real size of the array, key still can be in the array part. In | 776 | } |
| 755 | ** this case, try to avoid a call to 'luaH_realasize' when key is just | 777 | |
| 756 | ** one more than the limit (so that it can be incremented without | 778 | |
| 757 | ** changing the real size of the array). | 779 | static int hashkeyisempty (Table *t, lua_Integer key) { |
| 758 | */ | 780 | const TValue *val = getintfromhash(t, key); |
| 759 | static const TValue *Hgetint (Table *t, lua_Integer key) { | 781 | lua_assert(!keyinarray(t, key)); |
| 760 | const TValue *slot = getintfromarray(t, key); | 782 | return isempty(val); |
| 761 | if (slot != NULL) | ||
| 762 | return slot; | ||
| 763 | else | ||
| 764 | return getintfromhash(t, key); | ||
| 765 | } | 783 | } |
| 766 | 784 | ||
| 767 | 785 | ||
| @@ -776,7 +794,17 @@ static int finishnodeget (const TValue *val, TValue *res) { | |||
| 776 | 794 | ||
| 777 | 795 | ||
| 778 | int luaH_getint (Table *t, lua_Integer key, TValue *res) { | 796 | int luaH_getint (Table *t, lua_Integer key, TValue *res) { |
| 779 | return finishnodeget(Hgetint(t, key), res); | 797 | if (keyinarray(t, key)) { |
| 798 | int tag = *getArrTag(t, key); | ||
| 799 | if (!tagisempty(tag)) { | ||
| 800 | arr2val(t, key, tag, res); | ||
| 801 | return HOK; /* success */ | ||
| 802 | } | ||
| 803 | else | ||
| 804 | return ~cast_int(key); /* empty slot in the array part */ | ||
| 805 | } | ||
| 806 | else | ||
| 807 | return finishnodeget(getintfromhash(t, key), res); | ||
| 780 | } | 808 | } |
| 781 | 809 | ||
| 782 | 810 | ||
| @@ -832,25 +860,28 @@ TString *luaH_getstrkey (Table *t, TString *key) { | |||
| 832 | /* | 860 | /* |
| 833 | ** main search function | 861 | ** main search function |
| 834 | */ | 862 | */ |
| 835 | static const TValue *Hget (Table *t, const TValue *key) { | 863 | int luaH_get (Table *t, const TValue *key, TValue *res) { |
| 864 | const TValue *slot; | ||
| 836 | switch (ttypetag(key)) { | 865 | switch (ttypetag(key)) { |
| 837 | case LUA_VSHRSTR: return luaH_Hgetshortstr(t, tsvalue(key)); | 866 | case LUA_VSHRSTR: |
| 838 | case LUA_VNUMINT: return Hgetint(t, ivalue(key)); | 867 | slot = luaH_Hgetshortstr(t, tsvalue(key)); |
| 839 | case LUA_VNIL: return &absentkey; | 868 | break; |
| 869 | case LUA_VNUMINT: | ||
| 870 | return luaH_getint(t, ivalue(key), res); | ||
| 871 | case LUA_VNIL: | ||
| 872 | slot = &absentkey; | ||
| 873 | break; | ||
| 840 | case LUA_VNUMFLT: { | 874 | case LUA_VNUMFLT: { |
| 841 | lua_Integer k; | 875 | lua_Integer k; |
| 842 | if (luaV_flttointeger(fltvalue(key), &k, F2Ieq)) /* integral index? */ | 876 | if (luaV_flttointeger(fltvalue(key), &k, F2Ieq)) /* integral index? */ |
| 843 | return Hgetint(t, k); /* use specialized version */ | 877 | return luaH_getint(t, k, res); /* use specialized version */ |
| 844 | /* else... */ | 878 | /* else... */ |
| 845 | } /* FALLTHROUGH */ | 879 | } /* FALLTHROUGH */ |
| 846 | default: | 880 | default: |
| 847 | return getgeneric(t, key, 0); | 881 | slot = getgeneric(t, key, 0); |
| 882 | break; | ||
| 848 | } | 883 | } |
| 849 | } | 884 | return finishnodeget(slot, res); |
| 850 | |||
| 851 | |||
| 852 | int luaH_get (Table *t, const TValue *key, TValue *res) { | ||
| 853 | return finishnodeget(Hget(t, key), res); | ||
| 854 | } | 885 | } |
| 855 | 886 | ||
| 856 | 887 | ||
| @@ -866,10 +897,10 @@ static int finishnodeset (Table *t, const TValue *slot, TValue *val) { | |||
| 866 | 897 | ||
| 867 | 898 | ||
| 868 | int luaH_psetint (Table *t, lua_Integer key, TValue *val) { | 899 | int luaH_psetint (Table *t, lua_Integer key, TValue *val) { |
| 869 | const TValue *slot = getintfromarray(t, key); | 900 | if (keyinarray(t, key)) { |
| 870 | if (slot != NULL) { | 901 | lu_byte *tag = getArrTag(t, key); |
| 871 | if (!ttisnil(slot)) { | 902 | if (!tagisempty(*tag)) { |
| 872 | setobj(((lua_State*)NULL), cast(TValue*, slot), val); | 903 | val2arr(t, key, tag, val); |
| 873 | return HOK; /* success */ | 904 | return HOK; /* success */ |
| 874 | } | 905 | } |
| 875 | else | 906 | else |
| @@ -973,27 +1004,26 @@ static lua_Unsigned hash_search (Table *t, lua_Unsigned j) { | |||
| 973 | j *= 2; | 1004 | j *= 2; |
| 974 | else { | 1005 | else { |
| 975 | j = LUA_MAXINTEGER; | 1006 | j = LUA_MAXINTEGER; |
| 976 | if (isempty(Hgetint(t, j))) /* t[j] not present? */ | 1007 | if (hashkeyisempty(t, j)) /* t[j] not present? */ |
| 977 | break; /* 'j' now is an absent index */ | 1008 | break; /* 'j' now is an absent index */ |
| 978 | else /* weird case */ | 1009 | else /* weird case */ |
| 979 | return j; /* well, max integer is a boundary... */ | 1010 | return j; /* well, max integer is a boundary... */ |
| 980 | } | 1011 | } |
| 981 | } while (!isempty(Hgetint(t, j))); /* repeat until an absent t[j] */ | 1012 | } while (!hashkeyisempty(t, j)); /* repeat until an absent t[j] */ |
| 982 | /* i < j && t[i] present && t[j] absent */ | 1013 | /* i < j && t[i] present && t[j] absent */ |
| 983 | while (j - i > 1u) { /* do a binary search between them */ | 1014 | while (j - i > 1u) { /* do a binary search between them */ |
| 984 | lua_Unsigned m = (i + j) / 2; | 1015 | lua_Unsigned m = (i + j) / 2; |
| 985 | if (isempty(Hgetint(t, m))) j = m; | 1016 | if (hashkeyisempty(t, m)) j = m; |
| 986 | else i = m; | 1017 | else i = m; |
| 987 | } | 1018 | } |
| 988 | return i; | 1019 | return i; |
| 989 | } | 1020 | } |
| 990 | 1021 | ||
| 991 | 1022 | ||
| 992 | static unsigned int binsearch (const TValue *array, unsigned int i, | 1023 | static unsigned int binsearch (Table *array, unsigned int i, unsigned int j) { |
| 993 | unsigned int j) { | ||
| 994 | while (j - i > 1u) { /* binary search */ | 1024 | while (j - i > 1u) { /* binary search */ |
| 995 | unsigned int m = (i + j) / 2; | 1025 | unsigned int m = (i + j) / 2; |
| 996 | if (isempty(&array[m - 1])) j = m; | 1026 | if (arraykeyisempty(array, m)) j = m; |
| 997 | else i = m; | 1027 | else i = m; |
| 998 | } | 1028 | } |
| 999 | return i; | 1029 | return i; |
| @@ -1034,9 +1064,9 @@ static unsigned int binsearch (const TValue *array, unsigned int i, | |||
| 1034 | */ | 1064 | */ |
| 1035 | lua_Unsigned luaH_getn (Table *t) { | 1065 | lua_Unsigned luaH_getn (Table *t) { |
| 1036 | unsigned int limit = t->alimit; | 1066 | unsigned int limit = t->alimit; |
| 1037 | if (limit > 0 && isempty(&t->array[limit - 1])) { /* (1)? */ | 1067 | if (limit > 0 && arraykeyisempty(t, limit)) { /* (1)? */ |
| 1038 | /* there must be a boundary before 'limit' */ | 1068 | /* there must be a boundary before 'limit' */ |
| 1039 | if (limit >= 2 && !isempty(&t->array[limit - 2])) { | 1069 | if (limit >= 2 && !arraykeyisempty(t, limit - 1)) { |
| 1040 | /* 'limit - 1' is a boundary; can it be a new limit? */ | 1070 | /* 'limit - 1' is a boundary; can it be a new limit? */ |
| 1041 | if (ispow2realasize(t) && !ispow2(limit - 1)) { | 1071 | if (ispow2realasize(t) && !ispow2(limit - 1)) { |
| 1042 | t->alimit = limit - 1; | 1072 | t->alimit = limit - 1; |
| @@ -1045,7 +1075,7 @@ lua_Unsigned luaH_getn (Table *t) { | |||
| 1045 | return limit - 1; | 1075 | return limit - 1; |
| 1046 | } | 1076 | } |
| 1047 | else { /* must search for a boundary in [0, limit] */ | 1077 | else { /* must search for a boundary in [0, limit] */ |
| 1048 | unsigned int boundary = binsearch(t->array, 0, limit); | 1078 | unsigned int boundary = binsearch(t, 0, limit); |
| 1049 | /* can this boundary represent the real size of the array? */ | 1079 | /* can this boundary represent the real size of the array? */ |
| 1050 | if (ispow2realasize(t) && boundary > luaH_realasize(t) / 2) { | 1080 | if (ispow2realasize(t) && boundary > luaH_realasize(t) / 2) { |
| 1051 | t->alimit = boundary; /* use it as the new limit */ | 1081 | t->alimit = boundary; /* use it as the new limit */ |
| @@ -1064,7 +1094,7 @@ lua_Unsigned luaH_getn (Table *t) { | |||
| 1064 | if (isempty(&t->array[limit - 1])) { /* empty? */ | 1094 | if (isempty(&t->array[limit - 1])) { /* empty? */ |
| 1065 | /* there must be a boundary in the array after old limit, | 1095 | /* there must be a boundary in the array after old limit, |
| 1066 | and it must be a valid new limit */ | 1096 | and it must be a valid new limit */ |
| 1067 | unsigned int boundary = binsearch(t->array, t->alimit, limit); | 1097 | unsigned int boundary = binsearch(t, t->alimit, limit); |
| 1068 | t->alimit = boundary; | 1098 | t->alimit = boundary; |
| 1069 | return boundary; | 1099 | return boundary; |
| 1070 | } | 1100 | } |
| @@ -1073,7 +1103,7 @@ lua_Unsigned luaH_getn (Table *t) { | |||
| 1073 | /* (3) 'limit' is the last element and either is zero or present in table */ | 1103 | /* (3) 'limit' is the last element and either is zero or present in table */ |
| 1074 | lua_assert(limit == luaH_realasize(t) && | 1104 | lua_assert(limit == luaH_realasize(t) && |
| 1075 | (limit == 0 || !isempty(&t->array[limit - 1]))); | 1105 | (limit == 0 || !isempty(&t->array[limit - 1]))); |
| 1076 | if (isdummy(t) || isempty(Hgetint(t, cast(lua_Integer, limit + 1)))) | 1106 | if (isdummy(t) || hashkeyisempty(t, cast(lua_Integer, limit + 1))) |
| 1077 | return limit; /* 'limit + 1' is absent */ | 1107 | return limit; /* 'limit + 1' is absent */ |
| 1078 | else /* 'limit + 1' is also present */ | 1108 | else /* 'limit + 1' is also present */ |
| 1079 | return hash_search(t, limit); | 1109 | return hash_search(t, limit); |
