- 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 tt
struct 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}; //公共nil
return 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 i
lua_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);
//没找到旧返回nil
return 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);
//没找到就返回nil
return 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);
//未找到,返回nil
return luaO_nilobject;
}
3.2 新增元素/修改元素/删除元素 luaH_set系列
新增,修改,删除元素行为其实都是一样的
例如
local a={}
a.name=1 //新增元素
a.name=2 //修改元素
a.name=nil //删除元素
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))) //不等于自身的数字就是NaN
if (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数量是否+1
keynum_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不是整数,则返回-1
return -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缩容,且重新分配多出的key
if (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, 不会让其真正为0
if (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);
}
//构造一个数字类型的临时的key
TValue 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;
}
}
//没找到则返回0
return 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)
//若没找到,则报错 非法的key
luaG_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处为nil
if (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_search
return 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处不为nil
while (!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)处不为nil
while (upperbound - lowerbound > 1)
{
unsigned int m = (lowerbound + upperbound)/2;
if (ttisnil(luaH_getnum(t, m)))
{
upperbound = m;
}
else
{
lowerbound = m;
}
}
return lowerbound;
}