这是从lua-5.1.1中分离出来的table实现代码。
Last active
August 9, 2021 08:17
-
-
Save losophy/fe348e052e92695ea83df77f9aa0a185 to your computer and use it in GitHub Desktop.
lua-5.1.1中的table实现
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
** search function for integers | |
*/ | |
const TValue *luaH_getnum (Table *t, int key) { | |
/* (1 <= key && key <= t->sizearray) */ | |
if (cast(unsigned int, key-1) < cast(unsigned int, t->sizearray))//如果输入的Key是一个正整数,并且它的位大于0并且少等于或等于数组的大小,尝试在数组部分查找 | |
return &t->array[key-1]; | |
else {//如果不是,尝试在散列表部分查找,计算出该`Key`的散列值,根据此散列值访问`Node`数组得到散列桶所在的位置,遍历该散列桶下的所有链表元素,直到找到该`Key`为止。 | |
lua_Number nk = cast_num(key); | |
Node *n = hashnum(t, nk); | |
do { /* check whether `key' is somewhere in the chain */ | |
if (ttisnumber(gkey(n)) && luai_numeq(nvalue(gkey(n)), nk)) | |
return gval(n); /* that's it */ | |
else n = gnext(n); | |
} while (n); | |
return luaO_nilobject; | |
} | |
} | |
/* | |
** inserts a new key into a hash table; first, check whether key's main | |
** position is free. If not, check whether colliding node is in its main | |
** position or not: if it is not, move colliding node to an empty place and | |
** put new key in its main position; otherwise (colliding node is in its main | |
** position), new key goes to an empty position. | |
*/ | |
static TValue *newkey (lua_State *L, Table *t, const TValue *key) { | |
Node *mp = mainposition(t, key);//根据key来查找其所在散列桶的mainposition | |
if (!ttisnil(gval(mp)) || mp == dummynode) {//如果该mainposition上已经有其他数据了,需要重新分配空间给这个新的key,然后将这个新的Node串联到对应的散列桶上 | |
Node *othern; | |
Node *n = getfreepos(t); /* get a free place */ | |
if (n == NULL) { /* cannot find a free place? */ | |
rehash(L, t, key); /* grow table */ | |
return luaH_set(L, t, key); /* re-insert key into grown table */ | |
} | |
lua_assert(n != dummynode); | |
othern = mainposition(t, key2tval(mp)); | |
if (othern != mp) { /* is colliding node out of its main position? */ | |
/* yes; move colliding node into free position */ | |
while (gnext(othern) != mp) othern = gnext(othern); /* find previous */ | |
gnext(othern) = n; /* redo the chain with `n' in place of `mp' */ | |
*n = *mp; /* copy colliding node into free pos. (mp->next also goes) */ | |
gnext(mp) = NULL; /* now `mp' is free */ | |
setnilvalue(gval(mp)); | |
} | |
else { /* colliding node is in its own main position */ | |
/* new node will go into free position */ | |
gnext(n) = gnext(mp); /* chain new position */ | |
gnext(mp) = n; | |
mp = n; | |
} | |
} | |
gkey(mp)->value = key->value; gkey(mp)->tt = key->tt;//如果该Node的值为nil,那么直接将key赋值并且返回Node的TValue指针就可以了 | |
luaC_barriert(L, t, key); | |
lua_assert(ttisnil(gval(mp))); | |
return gval(mp); | |
} | |
static void rehash (lua_State *L, Table *t, const TValue *ek) { | |
int nasize, na; | |
int nums[MAXBITS+1]; //分配一个位图nums,将其中的所有位置0。这个位图的意义在于:nums数组中第 i个元素存放的是key在2(i-l)和i之间的元素数量。 | |
int i; | |
int totaluse; | |
for (i=0; i<=MAXBITS; i++) nums[i] = 0; | |
nasize = numusearray(t, nums); //遍历Lua表中的数组部分,计算其中的元素数量,更新对应的nums数组中的元素数量 | |
totaluse = nasize; /* all those keys are integer keys */ | |
totaluse += numusehash(t, nums, &nasize); //遍历lua表中的散列桶部分,因为其中也可能存放了正整数,需要根据这里的正整数数量更新对应的nums数组元素数量 | |
/* count extra key */ | |
nasize += countint(ek, nums); | |
totaluse++; | |
/* compute new size for array part */ | |
na = computesizes(nums, &nasize);//此时nums数组已经有了当前这个Table中所有正整数的分配统计,逐个遍历nums数组,获得其范围区间内所包含的整数数量大于50%的最大索引,作为重新散列之后的数组大小,超过这个范围的正整数,就分配到散列桶部分了 | |
/* resize the table to new computed sizes */ | |
resize(L, t, nasize, totaluse - na);//根据上面计算得到的调整后的数组和散列桶大小调整表 | |
} | |
static int computesizes (int nums[], int *narray) { | |
int i; | |
int twotoi; /* 2^i */ | |
int a = 0; /* number of elements smaller than 2^i */ | |
int na = 0; /* number of elements to go to array part */ | |
int n = 0; /* optimal size for array part */ | |
for (i = 0, twotoi = 1; twotoi/2 < *narray; i++, twotoi *= 2) { | |
if (nums[i] > 0) { | |
a += nums[i]; | |
if (a > twotoi/2) { /* more than half elements present? */ | |
n = twotoi; /* optimal size (till now) */ | |
na = a; /* all elements smaller than n will go to array part */ | |
} | |
} | |
if (a == *narray) break; /* all elements already counted */ | |
} | |
*narray = n; | |
lua_assert(*narray/2 <= na && na <= *narray); | |
return na; | |
} | |
int luaH_next (lua_State *L, Table *t, StkId key) { | |
int i = findindex(L, t, key); /* find original element */ | |
for (i++; i < t->sizearray; i++) { /* try first array part */ | |
if (!ttisnil(&t->array[i])) { /* a non-nil value? */ | |
setnvalue(key, cast_num(i+1)); | |
setobj2s(L, key+1, &t->array[i]); | |
return 1; | |
} | |
} | |
for (i -= t->sizearray; i < sizenode(t); i++) { /* then hash part */ | |
if (!ttisnil(gval(gnode(t, i)))) { /* a non-nil value? */ | |
setobj2s(L, key, key2tval(gnode(t, i))); | |
setobj2s(L, key+1, gval(gnode(t, i))); | |
return 1; | |
} | |
} | |
return 0; /* no more elements */ | |
} | |
/* | |
** Try to find a boundary in table `t'. A `boundary' is an integer index | |
** such that t[i] is non-nil and t[i+1] is nil (and 0 if t[1] is nil). | |
*/ | |
int luaH_getn (Table *t) { | |
unsigned int j = t->sizearray; | |
if (j > 0 && ttisnil(&t->array[j - 1])) { | |
/* there is a boundary in the array part: (binary) search for it */ | |
unsigned int i = 0; | |
while (j - i > 1) { | |
unsigned int m = (i+j)/2; | |
if (ttisnil(&t->array[m - 1])) j = m; | |
else i = m; | |
} | |
return i; | |
} | |
/* else must find a boundary in hash part */ | |
else if (t->node == dummynode) /* hash part is empty? */ | |
return j; /* that is easy... */ | |
else return unbound_search(t, j); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
** Union of all Lua values | |
*/ | |
typedef union { | |
GCObject *gc; | |
void *p; | |
lua_Number n; | |
int b; | |
} Value; | |
/* | |
** Tagged Values | |
*/ | |
#define TValuefields Value value; int tt | |
typedef union TKey { | |
struct { | |
TValuefields; | |
struct Node *next; /* for chaining */ | |
} nk; | |
TValue tvk; | |
} TKey; | |
typedef struct Node { | |
TValue i_val; | |
TKey i_key; | |
} Node; | |
typedef struct Table { | |
CommonHeader; | |
lu_byte flags; //这是一个byte类型的数据,用于表示这个表中提供了哪些元方法。最开始这个flags是空的,也就是0,当查找一次之后,如果该表中存在某个元方法,那么将该元方法对应的flag bit置为1,这样下一次查找时只需要比较这个bit就行了。每个元方法对应的bit定义在ltm. h文件中。 | |
lu_byte lsizenode; //该表中以2为底的散列表大小的对数值。同时由此可知,散列表部分的大小一定是2的幕,即如果散列桶数组要扩展的话,也是以每次在原大小基础上乘以2的形式扩展。 | |
struct Table *metatable;//存放该表的元表 | |
TValue *array; //指向数组部分的指针 | |
Node *node; //指向该表的散列桶数组起始位置的指针 | |
Node *lastfree; //指向该表散列桶数组的最后位置的指针 | |
GCObject *gclist; //GC相关的链表 | |
int sizearray; //数组部分的大小 | |
} Table; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment