【lua学习】4.表
- 1 概述
- 2 數據結構
- 2.1.表Table
- 2.2 鍵TKey
- 2.3 節點(鍵值對)Node
- 3 操作算法
- 3.1 查找
- 3.1.1 通用查找luaH_get
- 3.1.2 根據字符串查找 luaH_getstr
- 3.1.3 根據整數查找 luaH_getnum
- 3.2 新增元素/修改元素/刪除元素 luaH_set系列
- 3.2.1 根據key獲取或創建一個value: luaH_set
- 3.2.2 根據數字獲取或創建一個value: luaH_setnum
- 3.2.3 根據數字獲取或創建一個value: luaH_setstr
- 3.3 新建表 luaH_new
- 3.4 迭代表 luaH_next
- 3.5 取長度 luaH_getn
1 概述
- lua的表有數組部分和哈希部分
- 數組部分的索引從1開始
2 數據結構
2.1.表Table
(lobject.h) Table
typedef struct Table {CommonHeader;//表示表中提供了哪些元方法,起初為1,當查找后,若有此元方法,則該元方法對應的flag為0,若無,則對應flag為1,下次查找時發現該位為1,說明該表無此元方法,則不查表了,節約性能。lu_byte flags;//散列表大小的以2為底的對數,因為散列部分規定大小就是2的冪,所以這樣存lu_byte lsizenode;//該表的元表struct Table* metatable;//指向數組部分的指針TValue* array;//指向散列桶數組起始位置的指針Node* node;//指向散列桶數組最后位置的指針Node* lastfree;//GC相關的鏈表,todo以后再說GCObject* gclist;//數組部分的大小int sizearray; } Table;2.2 鍵TKey
(lobject.h) TKey
typedef union TKey {struct {TValuefields;//#define TValuefields Value value; int ttstruct Node* next;//指向下一個Node} nk;TValue tvk; } TKey;2.3 節點(鍵值對)Node
(lobject.h) Node
typedef struct Node {TValue i_val;//值TKey i_key;//鍵 } Node;3 操作算法
3.1 查找
3.1.1 通用查找luaH_get
(ltable.c) luaH_get
const TValue* luaH_get(Table* t, const TValue* key) {switch (ttype(key)){case LUA_TNIL:{//#define luaO_nilobject (&luaO_nilobject_)//LUAI_DATA const TValue luaO_nilobject_;//const TValue luaO_nilobject_ = {{NULL}, LUA_TNIL}; //公共nilreturn luaO_nilobject;}case LUA_TSTRING:{//#define rawtsvalue(o) check_exp(ttisstring(o), &(o)->value.gc->ts)return luaH_getstr(t, rawtsvalue(key));}case LUA_TNUMBER:{//#define nvalue(o) check_exp(ttisnumber(o), (o)->value.n)lua_Number n = nvalue(key);int k;//#define lua_number2int(i,d) __asm fld d __asm fistp ilua_number2int(k, n);//若n是整數,才觸發 根據整數查找//#define luai_numeq(a,b) ((a)==(b))if (luai_numeq(cast_num(k), n)){return luaH_getnum(t, k);}}default:{//找到key對應鍵值對所在的首地址(由于首地址會有沖突,所以不一定所有的鍵值對都在首地址,那些無法放在首地址的鍵值對,會作為身在首地址的鍵值對的后繼節點,所以需要next遍歷查找)Node* n = mainposition(t, key);//mainposition見下文do{//若在鏈表中找到了和key相同地鍵,則返回該鍵值對的值//#define key2tval(n) (&(n)->i_key.tvk)if (luaO_rawequalObj(key2tval(n), key))//luaO_rawequalObj,見棧章節{return gval(n);//#define gval(n) (&(n)->i_val)}//沒找到,繼續找下一個鍵值對n = gnext(n);//#define gnext(n) ((n)->i_key.nk.next)} while(n);//沒找到舊返回nilreturn luaO_nilobject;}} }(luaconf.h) lua_number2int
//就是利用匯編指令 快速地 把浮點數轉為整數 //關于浮點運算指令,f指float,代表浮點運算指令前綴 //fld: ld指load的意思,把一個浮點數加載進浮點運算器,入棧 //fistp: i指int是整數的意思,st指store是存儲到內存的意思,p指pop是出棧的意思。連起來就是 浮點運算器把棧頂出棧,以整數的形式存到內存中 #define lua_number2int(i,d) __asm fld d __asm fistp i(ltable.c) mainposition根據key獲取哈希部分鍵值對的首地址
static Node* mainposition(const Table* t, const TValue* key) {switch (ttype(key)){case LUA_TNUMBER:{return hashnum(t, nvalue(key));}case LUA_TSTRING:{//#define gnode(t,i) (&(t)->node[i]) //獲取Table的哈希部分的第i個地址//#define lmod(s,size) (check_exp((size&(size-1))==0, (cast(int, (s) & ((size)-1)))))//#define twoto(x) (1<<(x))//#define sizenode(t) (twoto((t)->lsizenode)) //獲取表的哈希部分大小//#define hashpow2(t,n) (gnode(t, lmod((n), sizenode(t)))) //根據 n 對表的哈希大小求余 來獲取鍵值對的地址//#define hashstr(t,str) hashpow2(t, (str)->tsv.hash)return hashstr(t, rawtsvalue(key));}case LUA_TBOOLEAN:{//#define hashboolean(t,p) hashpow2(t, p) //根據p的值 對表的哈希大小求余 來獲取鍵值對的地址return hashboolean(t, bvalue(key));//#define bvalue(o) check_exp(ttisboolean(o), (o)->value.b)}case LUA_TLIGHTUSERDATA:{//#define hashmod(t,n) (gnode(t, ((n) % ((sizenode(t)-1)|1)))) //根據n求余來獲取表的鍵值對地址//#define IntPoint(p) ((unsigned int)(lu_mem)(p))//#define hashpointer(t,p) hashmod(t, IntPoint(p)) //根據對象地址來求余 來獲取鍵值對的地址return hashpointer(t, pvalue(key));//#define pvalue(o) check_exp(ttislightuserdata(o), (o)->value.p)}default:{return hashpointer(t, gcvalue(key));//#define gcvalue(o) check_exp(iscollectable(o), (o)->value.gc)}} }(ltable.c) hashnum根據數字獲取表的哈希部分鍵值對的地址
static Node* hashnum(const Table* t, lua_Number n) {//若n為0,則返回哈希部分的0號地址if (luai_numeq(n, 0)){return gnode(t, 0);}//#define numints cast_int(sizeof(lua_Number)/sizeof(int)) 獲取lua_Number大小是int大小的多少倍unsigned int a[numints];memcpy(a, &n, sizeof(a));//已知luaNumber是int的2倍,就是把luaNumber的前4字節和后4字節加起來for (int i = 1; i < numints; i++){a[0] += a[i];}return hashmod(t, a[0]); }3.1.2 根據字符串查找 luaH_getstr
(ltable.c) luaH_getstr
const TValue* luaH_getstr(Table* t, TString* key) {//獲取hash部分索引為 (key->tsv.hash) % (1 << t->lnodesize) 的鍵值對地址Node* n = hashstr(t, key);//沿著鏈條一直往下找do{//若找到了鍵為string且和key相等的鍵值對,則返回鍵值對的值if (ttisstring(gkey(n)) && rawtsvalue(gkey(n)) == key){return gval(n);}n = gnext(n);} while(n);//沒找到就返回nilreturn luaO_nilobject; }3.1.3 根據整數查找 luaH_getnum
(ltable.c) luaH_getnum
const TValue* luaH_getnum(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];}//否則就根據數字在哈希部分找鍵值對地址lua_Number nk = cast_num(key);Node* n = hashnum(t, nk);//沿著鏈表往下找,直到找到符合條件的為止do{if (ttisnumber(gkey(n) && luai_numeq(gkey(n), nk))){return gval(n);}n = gnext(n);} while(n);//未找到,返回nilreturn luaO_nilobject; }3.2 新增元素/修改元素/刪除元素 luaH_set系列
新增,修改,刪除元素行為其實都是一樣的
例如
luaH_set系列方法做的事情不是set,但是它返回一個TValue*,供外部去設置其字段。
3.2.1 根據key獲取或創建一個value: luaH_set
(ltable.c) luaH_set
//雖然名字叫set,其實根本不叫set,個人覺得更合適的命名應該叫做 luaH_get_or_create,應為返回的是TValue類型的 value 的地址 TValue* luaH_set(lua_State* L, Table* t, const TValue* key) {//根據key獲取value的地址const TValue* p = luaH_get(t, key);//將table的方法標記位全部置為0,todo后面再說t->flags = 0;//若找到的不是nil,則直接返回找到的value的地址if (p != luaO_nilobject){return cast(TValue*, p);}//如果key是nil,則報錯if (ttisnil(key)){luaG_runerror(L, "table index is nil");//luaG_runerror,見異常章節return;}//若key是數字,但為NaN,則報錯//#define luai_numisnan(a) (!luai_numeq((a), (a))) //不等于自身的數字就是NaNif (ttisnumber(key) && luai_numisnan(nvalue(key))){luaG_runerror(L, "table index is NaN");}return newkey(L, t, key); }(ltable.c) newkey
//根據key創建一個新的value,返回value所在的地址 static TValue* newkey(lua_State* L, Table* t, const TValue* key) {//找到key對應的hash部分的索引。lua的hash部分采用閉散列的結構,每個桶的鏈表的每個元素,實際上是占據其它桶的位置的。//舉個例子,有幾號人去坐火車。//1號的票是A座,發現A空著,則坐下。那么票為A的鏈表為[A:1];//2號的票也是A,詢問1號,由于1號先來,所以只好灰溜溜地找一個空位B,坐下。那么索引為A的鏈表為[A:1, B:2];//3號的票是B,發現B被2號占了,詢問2號,2號只好起身再去找了另一個空位C坐下,B空出來了,于是3坐下。那么票為A的座位鏈表更新為[A:1, C:2];//4號的票是A,發現A被1占了,詢問1,4只好起身找空位D坐下。那么票為A的座位鏈表更新為[A:1, D:4, C:2];//5號的票是C,發現C被2占了,詢問2,2只好起身找到空位E坐下,C空出來了,于是5坐下。那么票為A的座位鏈表更新為[A:1, D:4, E:2];(雖然是單鏈表,但是2可以通過票找到A的位置,所以要找2在的位置的前驅節點并不難)Node* mp = mainposition(t, key);//若key的目標位置已經被占,或者目標位置是dummynode,也就是說key的目標位置都是無效的,只能找一個空閑的位置/*#define dummynode (&dummynode_)static const Node dummynode_ = {{{NULL}, LUA_TNIL},{{{NULL}, LUA_TNIL, NULL}}};*/if (!ttisnil(gval(mp)) || mp == dummynode){//找一個空閑的位置Node* freepos = getfreepos(t);//若沒有空閑位置了,則需要rehash以擴容if (n == NULL){rehash(L, t, key);return luaH_set(L, t, key);}Node* othern = mainposition(t, key2tval(mp));//若在mp位置的節點的mainposition不是mp位置,則mp需要被空出來,該節點內容被移到空位上if (othern != mp){//找到mp的前驅節點while(gnext(othern) != mp){othern = gnext(othern);}//前驅節點指向空閑位置,因為占mp位置的元素要被移到這個空閑位置gnext(othern) = freepos;*freepos = *mp;//mp位置空出來gnext(mp) = NULL;setnilvalue(gval(mp));}//若在mp位置的節點的mainposition就是mp位置,則新節點需要被移到空閑的位置, 且使這個空閑節點為mp的后繼節點else{gnext(freepos) = gnext(mp);gnext(mp) = freepos;mp = freepos;}}//最終mp更新為新節點的位置gkey(mp)->value = key->value;gkey(mp)->tt = key->tt;luaC_barriert(L, t, key);//luaC_barriert,見GC章節lua_assert(!ttisnil(gval(mp)));return gval(mp); }(ltable.c) getfreepos 獲取table的hash部分的一個空閑的節點位置
static Node* getfreepos(Table* t) {//注意t->lastfree是指向最后一個空閑位置的下一個位置while (t->lastfree > t->node){t->lastfree--;if (ttisnil(gkey(t->lastfree))) {return t->lasfree;}}return NULL; }(ltable.c) rehash 重新調整表的結構
static void rehash(lua_State* L, Table* t, const TValue* extra_key) {//table的array部分最大長度為2^MAXBITS, nums[i]表示key在( 2^(i-1), 2^i ] 區間的key的個數int nums[MAXBITS + 1] = {0};//獲取在array部分的key數量int keynum_in_array = numusearray(t, nums);//numusearray見下文//正整數key數量int keynum_positive_int = keynum_in_array;//獲取hash部分的key數量,更新正整數key數量int keynum_in_hash = numusehash(t, nums, &keynum_positive_int);//numusehash見下文//根據extra_key判斷是否正整數key數量是否+1keynum_positive_int += countint(extra_key, nums);//key總數=array部分key數量 + hash部分key數量 + extra_key數量(1)int key_num_total = keynum_in_array + keynum_in_hash + 1;//根據正整數key數量,計算array部分的新大小, 以及array中key新數量int keynum_positive_int2 = keynum_positive_int;keynum_in_array = computesizes(nums, &keynum_positive_int2);//computesizes見下文int array_size = keynum_positive_int2;keynum_in_hash = key_num_total - keynum_in_array;//hash的大小就等于hash部分key數量int hash_size = keynum_in_hash;//重新設置table的大小resize(L, t, array_size, hash_size); }(ltable.c) numusearray 統計table的array部分的key個數 以及 記錄key分布
static int numusearray(const Table* t, int* nums) {int keynum_total = 0;int key = 1;for (int section_idx=0, section_upper_limit=1; section_idx <= MAXBITS; section_idx++, section_upper_limit*=2){int keynum_this_section = 0;int lim = section_upper_limit;if (lim > t->sizearray){lim = t->sizearray;if (key > lim){break;}}for (; key <= lim; key++){for (!ttisnil(&t->array[key - 1])){keynum_this_section++;}}nums[section_idx] += keynum_this_section;keynum_total += keynum_this_section;}return keynum_total; }(ltable.c) numusehash 計算table中hash部分的key數量,若有正整數,則更新傳入的正整數key數量
static int numusehash(const Table* t, int* nums, int* keynum_positive_int) {//hash部分key數量int keynum_in_hash = 0;//hash部分的正整數key數量int keynum_positive_int_in_hash = 0;//獲取hash桶的數量int hashbucket_num = sizenode(t);//從后往前遍歷int i = hashbucket_num;while(i){i--;Node* hashbucket = t->node + i;if (!ttisnil(gval(n))){keynum_positive_int_in_hash += countint(key2tval(n), nums);//countint見下文keynum_in_hash++;}}*keynum_positive_int += keynum_positive_int_in_hash;return keynum_in_hash; }(ltable.c) countint 計算一個key是否是正整數,并更新正整數索引分布
static int countint(const TValue* key, int* nums) {int k = arrayindex(key);if (0 < k && k < MAXASIZE){//#define ceillog2(x) (luaO_log2((x) - 1) + 1) //獲取x的log2向上取整的值nums[ceillog2(k)]++;return 1;}return 0; }(ltable.c) arrayindex 根據key返回整數值
static int arrayindex(const TValue* key) {//key是整數,則返回整數if (ttisnumber(key)){int k;lua_number2int(k, n);if (luai_numeq(cast_num(k), n)){return k;}}//若key不是整數,則返回-1return -1; }(lobject.c) luaO_log2
//求x的log2向下取整值,用的是查表優化的方式,避免了多次重復計算log2值 int luaO_log2 (unsigned int x) {static const lu_byte log_2[256] = {0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8};int l = -1;while (x >= 256) { l += 8; x >>= 8; }return l + log_2[x]; }(ltable.c) computesizes
static int computesizes(int nums[], int* keynum_positive_int) {//正整數key計數器int counter_keynum_positive_int = 0;//array最優大小int array_size = 0;//array的key數量int keynum_in_array = 0;//選定array新的大小的原則:在[1, array_size]范圍,key的數量>array_size/2,也就是array要滿足空間利用率>50%for (int section_idx = 0, section_upperlimit = 1; section_upperlimit/2 < *keynum_positive_int; i++, section_upperlimit *= 2){if (nums[i] > 0){//正整數key計數器累加counter_keynum_positive_int += nums[i];//當滿足空間利用率>50%時,更新array的最優大小以及key數量if (section_upperlimit/2 < counter_keynum_positive_int){array_size = section_upperlimit;keynum_in_array = counter_keynum_positive_int;}}//若已經遍歷完了所有的key,則退出循環if (counter_keynum_positive_int == *keynum_positive_int){break;}}//記錄最佳的數組大小*keynum_positive_int = array_size;//返回數組return keynum_in_array; }(ltable.c) resize
//重新設置table的大小 static void resize(lua_State* L, Table* t, int array_size, int hash_size) {int old_array_size = t->sizearray;int old_hash_size = twoto(t->lsizenode);Node* old_hash = t->node;//若要的array大小大于原大小,則array擴容if (array_size > old_array_size){setarrayvector(L, t, array_size);}//重新分配hash部分內存setnodevector(L, t, hash_size);//若要的array大小小于原大小,則array縮容,且重新分配多出的keyif (array_size < old_array_size){t->sizearray = array_size;for (int i = array_size; i < old_array_size; i++){//array部分多出的key就要重新設置位置啦, 會被分配到hash部分哦if (!ttisnil(t->array + i)){setobjt2t(L, luaH_setnum(L, t, i+1), t->array + i);}}//array部分縮容luaM_reallocvector(L, t->array, old_array_size, array_size, TValue);}//把舊的hash部分的所有元素移到新的hash部分for (int i = old_hash_size - 1; i >= 0; i--){Node* node = old_hash + i;//將舊的hash上的節點設置到新的hash上if (!ttisnil(gval(node))){setobjt2t(L, luaH_set(L, t, key2tval(node)), gval(node));}//若舊的hash不是指向dummynode,則釋放掉if (old_hash != dummynode){luaM_freearray(L, old_hash, old_hash_size, Node);}} }(ltable.c) setarrayvector
//重新分配table的array部分的內存 static void setarrayvector(lua_State* L, Table* t, int size) {//重新分配array內存luaM_rellocvector(L, t->array, t->sizearray, size, TValue);//對于[sizearray, size)區間,設置值為nil;對于[1, sizearray)區間,暫且不管for (int i = t->sizearray; i < size; i++){setnilvalue(t->array + i);}//更新table的array大小t->arraysize = size; }(ltable.h) setnodevector
//重新分配table的hash部分的內存 static void setnodevector(lua_State* L, Table* t, int size) {//hash部分大小的log2值int lszie = 0;//若hash大小要設為0,則讓hash指向dummynode, 不會讓其真正為0if (size==0){t->node = cast(Node*, dummynode);lsize = 0;}else{lsize = ceillog(size);//若hash部分過大,則報錯if (lsize > MAXBITS){luaG_runerror(L, "table overflow");return;}size = twoto(lsize);//為hash重新分配內存,注意,老的t->node在外面已經記錄了,所以不用擔心t->node = luaM_newvector(L, size, Node);//遍歷hash部分每個桶,設置key和value都為nil,且設置后繼節點為空for (int i = 0; i < size; i++){Node* n = gnode(t, i);gnext(n) = NULL;setnilvalue(gkey(n));setnilvalue(gval(n));}}t->lsizenode = cast_byte(lsize);t->lastfree = gnode(t, size);//指向最后一個空閑位置的下一個位置 }3.2.2 根據數字獲取或創建一個value: luaH_setnum
(ltable.c) luaH_setnum
TValue* luaH_setnum(lua_State* L, Table* t, int key) {const TValue* p = luaH_getnum(t, key);if (p!=luaO_nilobject){return cast(TValue*, p);}//構造一個數字類型的臨時的keyTValue k;setnvalue(&k, cast_num(key));return newkey(L, t, &k); }3.2.3 根據數字獲取或創建一個value: luaH_setstr
(ltable.c) luaH_setstr
TValue* luaH_setstr(lua_State* L, Table* t, TString* key) {const TValue* p = luaH_getstr(t, key);if (p != luaO_nilobject){return cast(TValue*, p);}TValue k;setsvalue(L, &k, key);return newkey(L, t, &k); }3.3 新建表 luaH_new
(ltable.c) luaH_new
Table* luaH_new(lua_State* L, int array_size, int hash_size) {//#define luaM_new(L,t) cast(t *, luaM_malloc(L, sizeof(t))) //分配一個長度為類型t的堆內存Table* t = luaM_new(L, Table);//鏈接到gc鏈表上luaC_link(L, obj2gco(t), LUA_TTABLE);//luaC_link,見GC章節t->metatable = NULL;t->flags = cast_byte(~0);t->array = NULL;t->sizearray = 0;t->lsizenode = 0;t->node = cast(Node*, dummynode);//分配array部分內存setarrayvector(L, t, array_size);//分配hash部分內存setnodevector(L, t, hash_size);return t; }3.4 迭代表 luaH_next
- lua table迭代不是通過迭代器,而是通過key
- 先在array部分查找數據,若找到,返回key的下一個數據
- 否則在hash部分查找數據,若找到,則返回key的下一個數據
(ltable.c) luaH_next
//根據key找下一個鍵值對,找到則返回1,沒找到則返回0 int luaH_next(lua_State* L, Table* t, StkId key) {for i = findindex(L,t,key);//先找array部分for (i++; i < t->sizearray; i++){if (!ttisnil(t->array + i)){setnvalue(key, cast_num(i+1));//將key設置為i+1(因為lua索引比c索引大1)setobj2s(L, key+1, t->array+i);//將value設置為array+i處的內容(因為value在lua棧上的為止比key大1)return 1;}}//再找hash部分int size_hash = sizenode(t);for (i -= t->sizearray; i < size_hash ; i++){Node* n = gnode(t, i);if (!ttisnil(gval(n))){setobj2s(L, key, key2tval(n));setobj2s(L, key + 1, gval(n));return 1;}}//沒找到則返回0return 0; }(ltable.c) findindex
//根據key找到索引值,若在[0,size_array)范圍則表示在數組部分,若在[size_array,size_array+size_hash)范圍則表示在hash部分 static int findindex(lua_State* L, Table* t, StkId key) {if (ttisnil(key)){return -1;}//根據key獲取數組索引int i = arrayindex(key);if (0 < i && i <= t->size){return i - 1;}//找到key在hash部分的mainposition,沿著鏈表往下找,直到找到其key=key的節點為止Node* n = mainposition(t, key);do{//#define LAST_TAG LUA_TTHREAD//#define LUA_TPROTO (LAST_TAG+1)//#define LUA_TUPVAL (LAST_TAG+2)//#define LUA_TDEADKEY (LAST_TAG+3) //類型為死亡的key,見GC章節if(luaO_rawequalObj(key2tval(n), key)|| (ttype(gkey(n)) == LUA_TDEADKEY && iscollectable(key) && gcvalue(gkey(n)) == gcvalue(key))){i = cast_int(n - gnode(t, 0));return i + t->size_array;}n = gnext(n);} while(n)//若沒找到,則報錯 非法的keyluaG_runerror(L, "invalid key to " LUA_QL("next"));return 0; }3.5 取長度 luaH_getn
(ltable.c) luaH_getn
int luaH_getn(Table* t) {//若array部分>0且最后一個元素是nil,則使用二分法找一個位置n, n處不為nil,n+1處為nilif (t->sizearray > 0 && ttisnil(t->array + upperbound - 1)){unsigned int lowerbound = 0;unsigned int upperbound = t->sizearray;while (upperbound - lowerbound > 1){unsigned int m = (upperbound + lowerbound)/2;if (ttisnil(t->array + m - 1)){upperbound = m;}else{lowerbound = m;}}return lowerbound;}//若array部分最后一個元素不是nil 且 hash部分只有一個dummynode,則返回array部分大小if (t->node == dummynode){return t->sizearray;}//若array部分最后一個元素不是nil 且 hash部分 不指向dummynode,則使用unbond_searchreturn unbound_search(t, t->sizearray); }(ltable.c) unbound_search
static int unbound_search(Table* t, unsigned int upperbound) {unsigned int lowerbound = upperbound;upperbound++;//j每次*2, 直到j處為nil; 當然,若找的過程中j越界了,則從索引1開始找,找到一個位置n,處為不為nil,n-1處不為nilwhile (!ttisnil(luaH_getnum(t, upperbound))){lowerbound = upperbound;upperbound *= 2;//upperbound 越界后,則從1開始重新找if (upperbound > cast(unsigned int, MAX_INT)){upperbound = 1;while (!ttisnil(luaH_getnum(t, upperbound))){upperbound++;}return upperbound - 1;}}//既然到了upperbound處為nil,則用二分法找位置upperbound,upperbound處為nil,upperbound-1(即lowerbound)處不為nilwhile (upperbound - lowerbound > 1){unsigned int m = (lowerbound + upperbound)/2;if (ttisnil(luaH_getnum(t, m))){upperbound = m;}else{lowerbound = m;}}return lowerbound; }總結
以上是生活随笔為你收集整理的【lua学习】4.表的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux查看内存型号命令(linux查
- 下一篇: 【lua学习】7.环境