【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系列

新增,修改,删除元素行为其实都是一样的
例如

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))) //不等于自身的数字就是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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/337371.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

批量提取文件创建时间_批量采集新浪微博用户内容

有时我们需要把某些用户的微博数据全部采集下来用作分析&#xff0c;每条信息复制的工作量是非常低效的&#xff0c;必须要借助工具。今天给大家介绍一款采集软件&#xff1a;微风采集器。打开软件&#xff0c;选择模板&#xff0c;下拉框选&#xff1a;批量提取指定用户微博内…

C++异常的规则

点击蓝字关注我们异常是指存在于程序运行时的异常行为&#xff0c;这些行为超出了函数正常功能的范围&#xff0c;当程序的某部分检测到一个无法处理的问题时&#xff0c;就需要用到异常处理。1. C语言中传统的处理错误方式终止程序&#xff1a;如assert&#xff0c;当发生错误…

java中集合选取怎么选_集合中的可选

java中集合选取怎么选有时有人认为Optional类型值得在集合中使用。 据称&#xff0c;它解决了以下问题&#xff1a; HashMap在没有键映射以及值null映射到键的情况下返回null 。 如果使用Map<Optional<Something>>则可以清楚地区分缺少的映射和缺少的值。 这样一来…

【lua学习】7.环境

1 最重要的两个数据结构1.1 lua_State(Lua虚拟机/Lua协程)1.2 global_State(Lua全局状态)2 环境相关的变量2.1 Global表2.1.1 Global表在lua_State结构中2.1.2 Global表在 f_luaopen 时被初始化2.2 env表2.2.1 env表在Closure结构中2.2.2 查找一个全局变量<>在当前函数的…

异质性查询需要为连线设定_振奋人心!华东理工大学开发新型的荧光染料,为细胞成像奠定基础...

结合并激活荧光染料的适体荧光RNA(FR)已用于对丰富的细胞RNA种类进行成像。然而&#xff0c;诸如低亮度和具有不同光谱特性的染料/适体组合的有限可用性的局限性&#xff0c;限制了这些工具在活的哺乳动物细胞和体内的使用。最近&#xff0c;华东理工大学朱麟勇及杨弋共同通讯在…

C++ STL详解(1)

点击蓝字关注我们概述STL 是“Standard Template Library”的缩写&#xff0c;中文译为“标准模板库”。STL 是 C 标准库的一部分&#xff0c;不用单独安装。C 对模板&#xff08;Template&#xff09;支持得很好&#xff0c;STL 就是借助模板把常用的数据结构及其算法都实现了…

jooq 配置oracle_jOOQ配置

jooq 配置oracle本文是我们学院课程“ jOOQ –类型安全数据库查询”的一部分 。 在SQL和特定关系数据库很重要的Java应用程序中&#xff0c;jOOQ是一个不错的选择。 当JPA / Hibernate抽象过多而JDBC过于抽象时&#xff0c;这是一种替代方法。 它显示了一种现代的领域特定语言…

各种说明方法的答题格式_高中化学:选择题答题方法与知识点总结,让你轻松秒杀各种难题...

选择题是化学考试中被广泛采用的一种题型。它具有知识容量大&#xff0c;覆盖面广&#xff0c;构思新颖、灵活巧妙&#xff0c;考试的客观性强&#xff0c;答题简单&#xff0c;评分容易、客观准确等优点。 选择题按考查形式可分为三种类型&#xff0c;即&#xff1a;常规型选择…

C++ STL详解(2)

点击蓝字关注我们来源自网络&#xff0c;侵删刷题时常用的STLstring之前写过一篇 string 的简介但是不是特别全面&#xff0c;这里再补充说明一下。size()返回字符串中字符的数量#include<iostream> #include<string>using namespace std;int main() {string str …

【lua学习】5.栈和lua_State

1 背景2 栈&#xff08;寄存器数组&#xff09;&#xff0c;虚拟机&#xff0c;全局状态机2.1 栈定义在lua_State结构体中2.2 global_State 全局状态机2.3 lua_newstate 主虚拟机和全局状态机的创建2.4 lua_close 关闭虚拟机3 栈上的地址3.1 假索引3.2 根据数字索引获取栈上的地…

python中反向切片用法_使用Python中的切片[:0:-1]反转列表

举个例子a [1, 2, 3, 4, 4, 5, 6, 9]如果你试着用正指数分割它newa a[1:5]这将导致newa [2, 3, 4, 4]这是因为&#xff0c;在上面这种情况下&#xff0c;切片是这样发生的&#xff0c;[包含&#xff1a;排他的]&#xff0c;包括第一个索引&#xff0c;切片从这个索引开始&am…

斐波那契数列的四种实现方式(C语言)

点击蓝字关注我们来源自网络&#xff0c;侵删斐波那契数列是一组第一位和第二位为1&#xff0c;从第三位开始&#xff0c;后一位是前两位和的一组递增数列&#xff0c;像这样的&#xff1a;0、1、1、2、3、5、8、13、21、34、55......今天&#xff0c;我们用四种方式来进行实现…

【lua学习】6.函数,闭包,错误处理

1 数据结构和宏1.1 Closure 闭包1.2 Proto 函数原型1.3 UpVal 外部局部变量(upvalue)1.4 LocVar 局部变量信息1.5 SParser 语法分析所需要的结构1.6 Zio 读写流对象1.7 Mbuffer 缓冲对象1.8 lua_Debug 调试信息1.9 CallInfo 函数调用信息1.10 lua_longjmp 跳转信息1.11 虚拟机状…

cdi 2.7.5_看一下CDI 2.0 EDR1

cdi 2.7.5CDI是最近对Java EE最好的补充之一。 该观点在用户和集成商之间广泛共享。 当前版本的CDI 1.2于2014年4月发布。现在&#xff0c;在2015年中期&#xff0c;我们将面对CDI 2.0规范的早期草案。 CDI 2.0将在Java 8和更高版本上运行。 最终版本计划于2016年发布&#xf…

linux make命令_第一章 1.3Linux下安装Redis

1.3.2 Linux下安装Redis第一步: 去官网下载安装包 ,传送门第二步: 上传到Linux服务器,解压redis的安装包tar -zxvf redis-6.0.8.tar.gz这里我已经解压好了,并且移动到了redis目录下第三步: 安装基本环境yum -y insatll gcc-c这里注意一个问题,Centos下安装的gcc默认版本为4.8.5…

C语言strcpy函数的使用

点击蓝字关注我们strcpy简单使用&#xff1a; #include <stdio.h> #include <string.h>struct Student {int sid;char name[20];int age;} st; //此处分号不可省略int main(void) {struct Student st {1000,"zhangsan",20};printf("%d %s %d\n&…

jooq_使用jOOQ DSL

jooq本文是我们学院课程“ jOOQ –类型安全数据库查询”的一部分 。 在SQL和特定关系数据库很重要的Java应用程序中&#xff0c;jOOQ是一个不错的选择。 当JPA / Hibernate抽象过多而JDBC过于抽象时&#xff0c;这是一种替代方法。 它显示了一种现代的领域特定语言如何可以极大…

什么镜头最适合拍风景_为什么您的风景摄影套件中应始终装有远摄镜头

当您考虑风景摄影镜头时&#xff0c;许多摄影师只考虑广角镜头。14-24mm f / 2.8或24-70mm f / 2.8等经典镜头是用于风景的流行光学元件。它们是绝佳的选择但是&#xff0c;也值得在风景摄影套件中使用长焦镜头。为什么要使用长镜头进行风景摄影&#xff1f;您在网上或浏览Inst…

C++ STL详解(3)

点击蓝字关注我们简介set 是 关联容器 的一种&#xff0c;是排序好的集合&#xff08;元素已经进行了排序&#xff09;。set 和 multiset 类似&#xff0c;它和 multiset 的差别在于 set 中不能有重复的元素。multiset 的成员函数 set 中也都有。使用 set 必须包含 #include<…

【lua学习】8.协程

1 数据结构和宏1.1 协程的状态码1.2 协程的执行状态码1.3 lua_State 协程结构体2 C API2.1 lua_newthread 新建一个协程&#xff0c;压栈&#xff0c;返回这个新协程2.2 luaE_freethread 释放一个协程L12.3 lua_status 获取协程的错误码2.4 lua_resume 唤醒一个协程&#xff0c…