Lua源码阅读笔记 - table的长度

table数据结构

首先看一下lua中table的数据结构:

// lobject.h

/*
** Tables
*/

typedef union TKey {
   
  struct {
   
    TValuefields;
    struct Node *next;  /* for chaining */
  } nk;
  TValue tvk;
} TKey;


typedef struct Node {
   
  TValue i_val;
  TKey i_key;
} Node;


typedef struct Table {
   
  CommonHeader;
  lu_byte flags;  /* 1<

lu_byte lsizenode; /* log2 of size of `node' array */ struct Table *metatable; TValue *array; /* array part */ Node *node; Node *lastfree; /* any free position is before this position */ GCObject *gclist; int sizearray; /* size of `array' array */ } Table;

table由两部分组成:

  1. array是数组头,sizearray是数组长度。
  2. node是哈希表头,lsizenode是以2为底哈希表长度的对数,即 2lsizenode 为哈希表长度。

table的长度

执行如下lua代码:

tb1 = {1, 2, 3, 4, 5}
tb1[7] = 7
print("tb1 length: "..#tb1)
-- tb1 length: 5

tb2 = {1, 2, 3, 4, 5}
tb2[8] = 8
print("tb2 length: "..#tb2)
-- tb2 length: 8

tb3 = {1, 2, 3, 4, 5}
tb3[8] = 8
tb3[9] = 9
print("tb3 length: "..#tb3)
-- tb3 length: 9

tb4 = {1, 2, 3, 4, 5}
tb4[2] = nil
tb4[3] = nil
print("tb4 length: "..#tb4)
-- tb4 length: 5

tb1长度5,tb2长度8,tb3长度9,tb4长度5。
为什么会这样呢?可以找一下lua中#获取table长度的源代码。
在 lvm.c 文件中的函数void luaV_execute (lua_State *L, int nexeccalls)内,我们可以通过调试找到#对应的操作码OP_LEN,发现#获取table长度即是调用了int luaH_getn (Table *t)

// ltable.c

/*
** Try to find a boundary in table `t'. A `boundary' is an integer index
** such that t[i] is non-nil and t[i+1] is nil (and 0 if t[1] is nil).
*/
int luaH_getn (Table *t) {
   
  unsigned int j = t->sizearray;
  if (j > 0 && ttisnil(&t->array[j - 1])) {
   
    /* there is a boundary in the array part: (binary) search for it */
    unsigned int i = 0;
    while (j - i > 1) {
   
      unsigned int m = (i+j)/2;
      if (ttisnil(&t->array[m - 1])) j = m;
      else i = m;
    }
    return i;
  }
  /* else must find a boundary in hash part */
  else if (t->node == dummynode)  /* hash part is empty? */
    return j;  /* that is easy... */
  else return unbound_search(t, j);
}

函数中分三种情况:

  1. table长度大于0,且最后一个元素是nil
  2. table的哈希表为空
  3. 其他情况
  • 对于tb1sizearray为8,则数组部分是{1, 2, 3, 4, 5, nil, 7, nil}
    符合情况1,则二分查找Number和nil的分界,所以最终返回值是5。
  • 对于tb2sizearray也是8,数组部分是{1, 2, 3, 4, 5, nil, nil, 8},最后一个元素不为nil,哈希表部分为空。
    符合情况2,所以直接返回sizearray即8。
  • 对于tb3sizearray为8,索引9在哈希表部分,数组部分与tb2相同。
    符合情况3,调用int unbound_search (Table *t, unsigned int j)
// ltable.c

static int unbound_search (Table *t, unsigned int j) {
   
  unsigned int i = j;  /* i is zero or a present index */
  j++;
  /* find `i' and `j' such that i is present and j is not */
  while (!ttisnil(luaH_getnum(t, j))) {
   
    i = j;
    j *= 2;
    if (j > cast(unsigned int, MAX_INT)) {
     /* overflow? */
      /* table was built with bad purposes: resort to linear search */
      i = 1;
      while (!ttisnil(luaH_getnum(t, i))) i++;
      return i - 1;
    }
  }
  /* now do a binary search between them */
  while (j - i > 1) {
   

你可能感兴趣的:(Lua,Lua,table,rehash)