lua table 的rehash

先看rehash实现:

static void rehash (lua_State *L, Table *t, const TValue *ek) {
  int nasize, na;
  int nums[MAXBITS+1];  /* nums[i] = number of keys with 2^(i-1) < k <= 2^i */
  int i;
  int totaluse;
  for (i=0; i<=MAXBITS; i++) nums[i] = 0;  /* reset counts */
  nasize = numusearray(t, nums);  /* count keys in array part */
  totaluse = nasize;  /* all those keys are integer keys */
  totaluse += numusehash(t, nums, &nasize);  /* count keys in hash part */
  /* count extra key */
  nasize += countint(ek, nums);
  totaluse++;
  /* compute new size for array part */
  na = computesizes(nums, &nasize);
  /* resize the table to new computed sizes */
  luaH_resize(L, t, nasize, totaluse - na);
}

nums[MAXIBITS+1]:先把table中的数组部分和hash部分的所有key为number类形的按区域2^n的分布分别

统计起来,比如:nums[1]=1,num2[4] = 12,表示key<=2^1时和key<=2^4&&key>2^3时的key有多少个.

再看numusearray函数,从函数名得知,此函数就是统计所有key为number的存在于数组部分有多少个:

static int numusearray (const Table *t, int *nums) {
  int lg;
  int ttlg;  /* 2^lg */
  int ause = 0;  /* summation of `nums' */
  int i = 1;  /* count to traverse all array keys */
  for (lg=0, ttlg=1; lg<=MAXBITS; lg++, ttlg*=2) {  /* for each slice */
    int lc = 0;  /* counter */
    int lim = ttlg;
    if (lim > t->sizearray) {
      lim = t->sizearray;  /* adjust upper limit */
      if (i > lim)
        break;  /* no more elements to count */
    }
    /* count elements in range (2^(lg-1), 2^lg] */
    for (; i <= lim; i++) {
      if (!ttisnil(&t->array[i-1]))
        lc++;
    }
    nums[lg] += lc;
    ause += lc;
  }
  return ause;
}

numarray统计数组部分区域key的数量。

lim就是2^lg =ttlg,如果lim大于数组大小,而且i大于lim,就不用统计,因为i是之前从0开始的一直统计完的计数。

ause表示数组部分key为number的有多少个,其实也就是数组部分有效的key有多少个。

static int numusehash (const Table *t, int *nums, int *pnasize) {
  int totaluse = 0;  /* total number of elements */
  int ause = 0;  /* summation of `nums' */
  int i = sizenode(t);
  while (i--) {
    Node *n = &t->node[i];
    if (!ttisnil(gval(n))) {
      ause += countint(gkey(n), nums);
      totaluse++;
    }
  }
  *pnasize += ause;
  return totaluse;
}
numusehash 就是统计 hash 部分的 key number 类型的 ( 不包括浮点形的,从 countint arrayindex 可以得知 ):

static int countint (const TValue *key, int *nums) {
  int k = arrayindex(key);
  if (0 < k && k <= MAXASIZE) {  /* is `key' an appropriate array index? */
    nums[luaO_ceillog2(k)]++;  /* count as such */
    return 1;
  }
  else
    return 0;
}

static int arrayindex (const TValue *key) {
  if (ttisnumber(key)) {
    lua_Number n = nvalue(key);
    int k;
    lua_number2int(k, n);
    if (luai_numeq(cast_num(k), n))
      return k;
  }
  return -1;  /* `key' did not match some condition */
}

从arrayindex的函数名可以得知,此函数就是统计key能否进入array部分,并不是所有number

做key都可以进入array部分,浮点形就不行,于是有了luai_numeq(cast_num(k), n)的判断.

arrayindex返回的是key的int类型.

然后在countint 函数的luaO_ceillog2(k),求得k的2的幂是多少,然后放进统计nums.

 

再回来看rehash函数

先统计array部分的key,nasize从名字得知numberarray size,就是数组部分的key为number有多少个。

其实是等于localsize = 0; for k,v in pairs(nums) do size = size + v end(lua代码,方便表达).

nasize = size的。

再统计hash部分,再统计ek,ek就是rehash之前要插入的value。

好,先看一下到computesizes之前几个临时变量的情况。

nasize是数组部分和hash部分和将要插入的ek的key为number的数量,totaluse是数组,hash和

ek有效的数量,totaluse>=nasize的.

static int computesizes (int nums[], int *narray) {
  int i;
  int twotoi;  /* 2^i */
  int a = 0;  /* number of elements smaller than 2^i */
  int na = 0;  /* number of elements to go to array part */
  int n = 0;  /* optimal size for array part */
  for (i = 0, twotoi = 1; twotoi/2 < *narray; i++, twotoi *= 2) {
    if (nums[i] > 0) {
      a += nums[i];
      if (a > twotoi/2) {  /* more than half elements present? */
        n = twotoi;  /* optimal size (till now) */
        na = a;  /* all elements smaller than n will go to array part */
      }
    }
    if (a == *narray) break;  /* all elements already counted */
  }
  *narray = n;
  lua_assert(*narray/2 <= na && na <= *narray);
  return na;
}

此函数是根据之前nums区域的数据去求知rehash之后的数组大小。从前往后把nums的区域数据相加,看看每个2^n之中的有多少个key为number形的。

如果数量大于2^n /2= 2^n-1的话,则把rehash后的数组长度确定起来,就是n,然后数

组长度为2^n,然后能进入到数组部分的数量为na.如果a == *narry表示已经统计完。上面

lua伪代码有说,把所有nums的数量加起来就是narray,也就是之前所说的nasize.

computsize的统计方法不知道是根据啥数学原理还是经验值统计出来,就不得而知了,反正是挺有道理的。

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 */
    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,
    size_t, twoto(oldhsize))); /* free old array */
}

再看luaH_resize函数,nasize就是新数组的长度,totaluse-na,就是数组,hash,ek以number为

key的总数量-目前能进入到新数组的数量,也就是进入到hash的部分。此函数不多解释,从里面的函数

名就得知函数意途。







  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值