lua之table数据结构分析(一)

先看看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存储的是哈希表中空闲元素的起始查找位置,初始化为哈希表的表尾。

线性数组array

初始化操作:
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的范围内时,直接返回对应索引处的值

修改:
先获取对应索引处的值,然后修改

哈希表部分node

初始化:

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));

你可能感兴趣的:(table,源码分析,lua,数据结构分析)