先看看lua的table数据结构:
typedef struct Table {
CommonHeader;
lu_byte flags; /* 1<<p means tagmethod(p) is not present */
lu_byte lsizenode; /* log2 of size of 'node' array */
unsigned int sizearray; /* size of 'array' array */
TValue *array; /* array part */
Node *node;
Node *lastfree; /* any free position is before this position */
struct Table *metatable;
GCObject *gclist;
} Table;
有两个跟数据存储有关的成员变量:线性存储的array(就是一个数组)和哈希存储的node(也是一个数组),分别对应table的线性存储部分和哈希表部分。lastfree存储的是哈希表中空闲元素的起始查找位置,初始化为哈希表的表尾。
初始化操作:
Table在创建时初始化array为Null
t->array = NULL;
t->sizearray = 0;
容量扩展:
static void setarrayvector (lua_State *L, Table *t, unsigned int size) {
unsigned int i;
luaM_reallocvector(L, t->array, t->sizearray, size, TValue);
for (i=t->sizearray; i<size; i++)
setnilvalue(&t->array[i]);
t->sizearray = size;
}
重新申请一个更大的数组,并把扩展的元素设置为LUA_TNIL
访问:
if (l_castS2U(key - 1) < t->sizearray)
return &t->array[key - 1];
当key的值在array的范围内时,直接返回对应索引处的值
修改:
先获取对应索引处的值,然后修改
初始化:
t->node = cast(Node *, dummynode);
/*dummynode是一个常量*/
static const Node dummynode_ = {
{NILCONSTANT}, /* value */
{{NILCONSTANT, 0}} /* key */
};
扩展:
t->node = luaM_newvector(L, size, Node);
for (i = 0; i < (int)size; i++) { Node *n = gnode(t, i); gnext(n) = 0; setnilvalue(wgkey(n)); setnilvalue(gval(n)); }
需要分两步做:
第一步,扩展空间大小
第二部,重新哈希元素的位置
访问:
分两步进行
第一步,确定哈希值,不同的key类型有不同的哈希值算法,具体可以看hashint、hashstr、hashboolean、hashpointer等函数的实现;
第二部,根据得到的哈希值在node中查找元素,查找到的元素叫做mainposition,这个不一定就是要查找的值,因为还有冲突元素的存在。哈希值相同的元素肯定在以mainposition为表头的链表上,遍历这个链表,如果找到则返回对应的值,如果找不到则返回luaO_nilobject
Node *n = mainposition(t, key);
for (;;) { /* check whether 'key' is somewhere in the chain */
if (luaV_rawequalobj(gkey(n), key))
return gval(n); /* that's it */
else {
int nx = gnext(n);
if (nx == 0) break;
n += nx;
}
};
return luaO_nilobject;
如果查找的key值不存在时,也就是找到的值为luaO_nilobject时,就通过luaH_newkey创建一个。
先约定几个特定名字:
f:哈希表中一个空闲位置
mp:待访问key值的mainposition
othern:mp处原有值的mainposition(如果存在的话)
创建算法:
先找到key值对应的mainposition:
mp = mainposition(t, key);
如果这个位置是空闲可用的,则直接占用这个mainposition
setnodekey(L, &mp->i_key, key);
如果已经被其它值占用,还要分两种情况考虑
首先申请一个可用的空闲位置f
static Node *getfreepos (Table *t) {
while (t->lastfree > t->node) {
t->lastfree--;
if (ttisnil(gkey(t->lastfree)))
return t->lastfree;
}
return NULL; /* could not find a free place */
}
就是从哈希表尾部向头部遍历,返回找到的第一个空闲位置,如果找不到就使用rehash函数重新整理table的线性存储区和哈希表存储区,rehash的时候有可能会扩展存储空间。
然后获取othern
othern = mainposition(t, gkey(mp));
情况一:冲突元素的mainposition一样,这说明它们应该在同一个链表上,把新key值存储到f中,把f插入到链表中:
if (gnext(mp) != 0)
gnext(f) = cast_int((mp + gnext(mp)) - f); /* chain new position */
else lua_assert(gnext(f) == 0);
gnext(mp) = cast_int(f - mp);
mp = f;
情况二:冲突元素的mainposition不一样,则把othern移动到f中(这牵扯到othern所在链表的操作)把mp中的值设置为key值
while (othern + gnext(othern) != mp) /* find previous */
othern += gnext(othern);
gnext(othern) = cast_int(f - othern); /* rechain to point to 'f' */
*f = *mp; /* copy colliding node into free pos. (mp->next also goes) */
if (gnext(mp) != 0) {
gnext(f) += cast_int(mp - f); /* correct 'next' */
gnext(mp) = 0; /* now 'mp' is free */
}
setnilvalue(gval(mp));