Lua 源码分析之Table

一、创建一个table(lapi.c, line:666)

LUA_API void lua_createtable (lua_State *L, int narray, int nrec) {
  Table *t;
  lua_lock(L);
  luaC_checkGC(L);
  t = luaH_new(L);
  sethvalue(L, L->top, t);
  api_incr_top(L);
  if (narray > 0 || nrec > 0)
    luaH_resize(L, t, narray, nrec);
  lua_unlock(L);
}

1.luaH_new: 创建一个table(ltable.c, line:368)
    

Table *luaH_new (lua_State *L) {
      Table *t = &luaC_newobj(L, LUA_TTABLE, sizeof(Table), NULL, 0)->h;
      t->metatable = NULL;
      t->flags = cast_byte(~0);
      t->array = NULL;
      t->sizearray = 0;
      setnodevector(L, t, 0);
      return t;
    }


    初始化下面几个重要属性
    1.t->array = NULL; /*数组为空*/
    2.t->sizearray = 0; /*数组长度为0*/
    3.t->node = cast(Node *, dummynode);  /* use common `dummynode' */
    4.t->lsizenode = cast_byte(lsize); /*hash 节点链表长度为0 */
    5.t->lastfree = gnode(t, size);  /* all positions are free */
    

2.luaH_resize(ltable.c, line:304): 根据narray和nrec的长度来决定是否要resize
a.resize分两部分:array和hash部分
array部分:
如果需要扩大,则在最后部分增加空节点;
如果需要减少,把超出新的总量部分的不为空的节点插入到hash部分里,再减少数量。
hash部分:
把先保存一下旧的节点列表头指针,设置hash列表部分的大小后,重新把旧的节目插入到hash部分。

注意:
调用luaM_reallocvector时,实际调用的是void * frealloc (void *ud, void *ptr, size_t osize, size_t nsize)
frealloc是初始化是在lua_State *L = lua_newstate(l_alloc, NULL),l_alloc为frealloc最后调用分配的地方
1.会创建一个大小为nsize的数据块,而不管osize是什么。
2.如果nsize为0,则释放这个数据块
3.如果osize和nsize都为0,则什么都不做

  

 void luaH_resize (lua_State *L, Table *t, int nasize, int nhsize) {
      int i;
      int oldasize = t->sizearray;
      int oldhsize = t->lsizenode;
      Node *nold = t->node;  /* save old hash ... */
      if (nasize > oldasize)  /* array part must grow? */
        setarrayvector(L, t, nasize);
      /* create new hash part with appropriate size */
      setnodevector(L, t, nhsize);
      if (nasize < oldasize) {  /* array part must shrink? */
        t->sizearray = nasize;
        /* re-insert elements from vanishing slice */
        /* 如果数组为{1,2,3,nil,5,6},会把5和6设置到hash部分去*/
        for (i=nasize; i<oldasize; i++) {
          if (!ttisnil(&t->array[i]))
            luaH_setint(L, t, i + 1, &t->array[i]);
        }
        /* shrink array */
        luaM_reallocvector(L, t->array, oldasize, nasize, TValue);
      }
      /* re-insert elements from hash part */
      for (i = twoto(oldhsize) - 1; i >= 0; i--) {
        Node *old = nold+i;
        if (!ttisnil(gval(old))) {
          /* doesn't need barrier/invalidate cache, as entry was
             already present in the table */
          setobjt2t(L, luaH_set(L, t, gkey(old)), gval(old));
        }
      }
      if (!isdummy(nold))
        luaM_freearray(L, nold, cast(size_t, twoto(oldhsize))); /* free old array */
    }


    
    /* 重置array部分大小,变长部分的节点全部置为nil */
    

static void setarrayvector (lua_State *L, Table *t, int size) {
      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;
    }

    /* 重置hash部分大小,大小使用log底数来存,最后根据底数计算2的幂,设置大小和最后一个空的节点 */
    

static void setnodevector (lua_State *L, Table *t, int size) {
      int lsize;
      if (size == 0) {  /* no elements to hash part? */
        t->node = cast(Node *, dummynode);  /* use common `dummynode' */
        lsize = 0;
      }
      else {
        int i;
        lsize = luaO_ceillog2(size);
        if (lsize > MAXBITS)
          luaG_runerror(L, "table overflow");
        size = twoto(lsize);
        t->node = luaM_newvector(L, size, Node);
        for (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);  /* all positions are free */
    }


    
二、设置key和value(lapi.c, line:741)

LUA_API void lua_settable (lua_State *L, int idx) {
  StkId t;
  lua_lock(L);
  api_checknelems(L, 2);
  t = index2adr(L, idx);
  api_checkvalidindex(L, t);
  luaV_settable(L, t, L->top - 2, L->top - 1);
  L->top -= 2;  /* pop index and value */
  lua_unlock(L);
}

1.luaV_settable(lvm.c, line:141):根据key和value设置

void luaV_settable (lua_State *L, const TValue *t, TValue *key, StkId val) {
  int loop;
  TValue temp;
  for (loop = 0; loop < MAXTAGLOOP; loop++) {
    const TValue *tm;
    if (ttistable(t)) {  /* `t' is a table? */
      Table *h = hvalue(t);
      TValue *oldval = luaH_set(L, h, key); /* do a primitive set */
      if (!ttisnil(oldval) ||  /* result is no nil? */
          (tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL) { /* or no TM? */
        setobj2t(L, oldval, val);
        h->flags = 0;
        luaC_barriert(L, h, val);
        return;
      }
      /* else will try the tag method */
    }
    else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_NEWINDEX)))
      luaG_typeerror(L, t, "index");
    if (ttisfunction(tm)) {
      callTM(L, tm, t, key, val);
      return;
    }
    /* else repeat with `tm' */
    setobj(L, &temp, tm);  /* avoid pointing inside table (may rehash) */
    t = &temp;
  }
  luaG_runerror(L, "loop in settable");
}

2.luaH_set(ltable.c, line:510):设置前,先get,检查是否已经存在,存在即返回;如果不存在,则通过newkey创建一个新key并返回新的节点

TValue *luaH_set (lua_State *L, Table *t, const TValue *key) {
  const TValue *p = luaH_get(t, key);
  if (p != luaO_nilobject)
    return cast(TValue *, p);
  else return luaH_newkey(L, t, key);
}

三、根据key值获取内容luaH_get(ltable.c, line:481)

const TValue *luaH_get (Table *t, const TValue *key) {
  switch (ttype(key)) {
    case LUA_TNIL: return luaO_nilobject;
    case LUA_TSHRSTR: return luaH_getstr(t, rawtsvalue(key));
    case LUA_TNUMBER: {
      int k;
      lua_Number n = nvalue(key);
      lua_number2int(k, n);
      if (luai_numeq(cast_num(k), nvalue(key))) /* index is int? */
        return luaH_getint(t, k);  /* use specialized version */
      /* else go through */
    }
    default: {
      Node *n = mainposition(t, key);
      do {  /* check whether `key' is somewhere in the chain */
        if (luaV_rawequalobj(gkey(n), key))
          return gval(n);  /* that's it */
        else n = gnext(n);
      } while (n);
      return luaO_nilobject;
    }
  }
}

1.luaH_getstr(ltable.c, line:466)
当key为LUA_TSHRSTR时,调用luaH_getstr
对于hash部分,需要在node的链表里搜索是否存在;
如果不存在,看看能否在链表中是否有空的位置;
如果有返回;否则需要rehash再找合适位置存储;

/*
** search function for short strings
*/
const TValue *luaH_getstr (Table *t, TString *key) {
  Node *n = hashstr(t, key);
  lua_assert(key->tsv.tt == LUA_TSHRSTR);
  do {  /* check whether `key' is somewhere in the chain */
    if (ttisshrstring(gkey(n)) && eqshrstr(rawtsvalue(gkey(n)), key))
      return gval(n);  /* that's it */
    else n = gnext(n);
  } while (n);
  return luaO_nilobject;
}

2.luaH_getint(ltable.c, line:446)
当key为LUA_TNUMBER时,调用luaH_getint
如果key的值大于1并且小于数组部分长度,就在数组部分查找并返回
否则,就去hash部分查找

/*
** search function for integers
*/
const TValue *luaH_getint (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];
  else {
    lua_Number nk = cast_num(key);
    Node *n = hashnum(t, nk);
    do {  /* check whether `key' is somewhere in the chain */
      if (ttisnumber(gkey(n)) && luai_numeq(nvalue(gkey(n)), nk))
        return gval(n);  /* that's it */
      else n = gnext(n);
    } while (n);
    return luaO_nilobject;
  }
}

3.数组下标的hash值计算
先把n转换为double,把n的内存结构前后各半拆开做加法得到的就是为hash值

#define luai_hashnum(i,n)  \
  { volatile union luai_Cast u; u.l_d = (n) + 1.0;  /* avoid -0 */ \
    (i) = u.l_p[0]; (i) += u.l_p[1]; }  /* add double bits for his hash */
	
/*
** hash for lua_Numbers
*/
static Node *hashnum (const Table *t, lua_Number n) {
  int i;
  luai_hashnum(i, n);
  if (i < 0) {
    if (cast(unsigned int, i) == 0u - i)  /* use unsigned to avoid overflows */
      i = 0;  /* handle INT_MIN */
    i = -i;  /* must be a positive value */
  }
  return hashmod(t, i);
}

4.当key都不是LUA_TSHRSTR,LUA_TNIL,LUA_TNUMBER时,就调用mainposition获取它的地址

四、luaH_newkey(ltable.c, line:405),这个篇幅比较大,具体参考另一篇文章:Lua源码之Table - 细说Hash部分
创建一个新的key(对于新的key,会调用2次luaH_set:第一次是在luaV_settable,第二次是在newkey函数rehash后)

TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key) {
  Node *mp;
  if (ttisnil(key)) luaG_runerror(L, "table index is nil");
  else if (ttisnumber(key) && luai_numisnan(L, nvalue(key)))
    luaG_runerror(L, "table index is NaN");
  mp = mainposition(t, key);
  if (!ttisnil(gval(mp)) || isdummy(mp)) {  /* main position is taken? */
    Node *othern;
    Node *n = getfreepos(t);  /* get a free place */
    if (n == NULL) {  /* cannot find a free place? */
      rehash(L, t, key);  /* grow table */
      /* whatever called 'newkey' take care of TM cache and GC barrier */
      return luaH_set(L, t, key);  /* insert key into grown table */
    }
    lua_assert(!isdummy(n));
    othern = mainposition(t, gkey(mp));
    if (othern != mp) {  /* is colliding node out of its main position? */
      /* yes; move colliding node into free position */
      while (gnext(othern) != mp) othern = gnext(othern);  /* find previous */
      gnext(othern) = n;  /* redo the chain with `n' in place of `mp' */
      *n = *mp;  /* copy colliding node into free pos. (mp->next also goes) */
      gnext(mp) = NULL;  /* now `mp' is free */
      setnilvalue(gval(mp));
    }
    else {  /* colliding node is in its own main position */
      /* new node will go into free position */
      gnext(n) = gnext(mp);  /* chain new position */
      gnext(mp) = n;
      mp = n;
    }
  }
  setobj2t(L, gkey(mp), key);
  luaC_barrierback(L, obj2gco(t), key);
  lua_assert(ttisnil(gval(mp)));
  return gval(mp);
}
发布了70 篇原创文章 · 获赞 48 · 访问量 19万+

猜你喜欢

转载自blog.csdn.net/fwb330198372/article/details/83818978