lua.5.2.3源码阅读(05):Table表插入流程
通过luaH_new创建的Table开始实际上是一张空表,只是包含了Table本身的数据 结构。创建完以后需要添加元素,
添加到函数为:luaH_set,该函数在设置前会根据key的值去获取对应的Value,如果能找到,则直接设置,如果找
不到,则需要申请一个位置来存放(Key,Value)。
1 TValue *luaH_set (lua_State *L, Table *t, const TValue *key) { 2 const TValue *p = luaH_get(t, key); 3 if (p != luaO_nilobject) 4 return cast(TValue *, p); 5 else return luaH_newkey(L, t, key); 6 }
luaH_newkey函数是查找一个合适的位置用来存放(Key,value),实际上有可能会申请空间,也可能不会申请空间,
还有一种可能就是,统计表格中的整型key的情况,重新调整数组和hash表达数组大小,让元素更合适的存储。
1 /* 2 ** inserts a new key into a hash table; first, check whether key's main 3 ** position is free. If not, check whether colliding node is in its main 4 ** position or not: if it is not, move colliding node to an empty place and 5 ** put new key in its main position; otherwise (colliding node is in its main 6 ** position), new key goes to an empty position. 7 */ 8 TValue *luaH_newkey(lua_State *L, Table *t, const TValue *key) { 9 Node *mp; 10 if (ttisnil(key)) 11 luaG_runerror(L, "table index is nil"); 12 else if (ttisnumber(key) && luai_numisnan(L, nvalue(key))) 13 luaG_runerror(L, "table index is NaN");
上面的注释的大意:
1、根据key,计算对应的main position,所谓的main position是指作为数组访问时代位置索引,或者hash索引位置的表头。
2、如果main position未被占用,则直接写入对应的位置;
3、如果main position被占用,则检查该main position是不是冲突节点的main position:如果是则重新申请空间写入并
加入main position链表尾部;如果不是,则重新申请空间给冲突节点,main position则写入当前的值。
继续读luaH_newkey代码如下:
1 mp = mainposition(t, key); 2 if (!ttisnil(gval(mp)) || isdummy(mp)) { /* main position is taken? */ 3 Node *othern; 4 Node *n = getfreepos(t); /* get a free place */ 5 if (n == NULL) { /* cannot find a free place? */ 6 rehash(L, t, key); /* grow table */ 7 /* whatever called 'newkey' take care of TM cache and GC barrier */ 8 return luaH_set(L, t, key); /* insert key into grown table */ 9 } 10 11 static Node *getfreepos (Table *t) { 12 while (t->lastfree > t->node) { 13 t->lastfree--; 14 if (ttisnil(gkey(t->lastfree))) 15 return t->lastfree; 16 } 17 return NULL; /* could not find a free place */ 18 }
1、如果mainposition被占用,或者该table还是空的,则去查找空闲结点存储;
2、getfreepos函数是从最后的空闲结点中获取逐步查找;
3、如果发现找不到空闲结点时候,则通过rehash申请空间(后续分析),然后重新调用luaH_set;
继续读luaH_newkey代码如下:
1 othern = mainposition(t, gkey(mp)); 2 if (othern != mp) { /* is colliding node out of its main position? */ 3 /* yes; move colliding node into free position */ 4 while (gnext(othern) != mp) othern = gnext(othern); /* find previous */ 5 gnext(othern) = n; /* redo the chain with `n' in place of `mp' */ 6 *n = *mp; /* copy colliding node into free pos. (mp->next also goes) */ 7 gnext(mp) = NULL; /* now `mp' is free */ 8 setnilvalue(gval(mp)); 9 } 10 else { /* colliding node is in its own main position */ 11 /* new node will go into free position */ 12 gnext(n) = gnext(mp); /* chain new position */ 13 gnext(mp) = n; 14 mp = n; 15 }
1、主要是检查冲突的结点是不是在其main position;
2、如果不是在main position,则将刚申请的空闲空间用来保存冲突结点;
3、如果是在main position,则挂空闲结点用来保存当前值,并且挂在main position结点后。
最后的代码:
1 setobj2t(L, gkey(mp), key);
1、把刚申请来存储当前值的结点的key值设置为当前的key。