lua.5.2.3源码阅读(06):Table内存的申请
rehash是根据当前表格中的数据重新计算一个比较函数的:数组大小和hash表大小,重新申请空间,并重新插入原有数据:
1 static void rehash (lua_State *L, Table *t, const TValue *ek) { 2 int nasize, na; 3 int nums[MAXBITS+1]; /* nums[i] = number of keys with 2^(i-1) < k <= 2^i */ 4 int i; 5 int totaluse; 6 for (i=0; i<=MAXBITS; i++) nums[i] = 0; /* reset counts */ 7 nasize = numusearray(t, nums); /* count keys in array part */ 8 totaluse = nasize; /* all those keys are integer keys */ 9 totaluse += numusehash(t, nums, &nasize); /* count keys in hash part */ 10 /* count extra key */ 11 nasize += countint(ek, nums); 12 totaluse++; 13 /* compute new size for array part */ 14 na = computesizes(nums, &nasize); 15 /* resize the table to new computed sizes */ 16 luaH_resize(L, t, nasize, totaluse - na); 17 }
1、nums[i] = number of keys with 2^(i-1) < k <= 2^i;
2、numusearray计算数组中有效值个数nasize,以及有效值在每个区间的分布情况;
3、numusehash统计hash表中有效值的个数,并且统计hash表中key为整数的个数,合并到nasize中;
4、统计插入当前的值是不是整数值;
5、根据整数key的个数,以及在各个区间的分布,计算合适的数组大小;
6、重新分配空间;
统计数组有效值的numusearray:
1 // nums是一个30的数组,每个数字用来记录 2 // 2^i到2^(i+1)之间数字的数字个数,数组部分 3 // 把 t->sizearray数组内的所有数据,按照2^i到2^(i+1) 4 // 分类统计,返回总共使用的个数; 5 static int numusearray (const Table *t, int *nums) { 6 int lg; 7 int ttlg; /* 2^lg */ 8 int ause = 0; /* summation of `nums' */ 9 int i = 1; /* count to traverse all array keys */ 10 for (lg=0, ttlg=1; lg<=MAXBITS; lg++, ttlg*=2) { /* for each slice */ 11 int lc = 0; /* counter */ 12 int lim = ttlg; 13 if (lim > t->sizearray) { 14 lim = t->sizearray; /* adjust upper limit */ 15 if (i > lim) 16 break; /* no more elements to count */ 17 } 18 /* count elements in range (2^(lg-1), 2^lg] */ 19 for (; i <= lim; i++) { 20 if (!ttisnil(&t->array[i-1])) 21 lc++; 22 } 23 nums[lg] += lc; 24 ause += lc; 25 } 26 return ause; 27 }
numusehash统计hash表格中有效数的情况,以及key为整数的个数,累加到数组有效个数中,countint检查是不是整数:
1 // 统计Node的使用情况,以及key对应到2^i到2^(i+1)之间数字的数字个数 2 // 并统计hash表中key为整数的个数,可以用来保存到数组中 3 static int numusehash(const Table *t, int *nums, int *pnasize) { 4 int totaluse = 0; /* total number of elements */ 5 int ause = 0; /* summation of `nums' */ 6 int i = sizenode(t); 7 while (i--) { 8 Node *n = &t->node[i]; 9 if (!ttisnil(gval(n))) { 10 // 根据key是否是数值整型,如果是,统计所在的区间 11 ause += countint(gkey(n), nums); 12 totaluse++; 13 } 14 } 15 *pnasize += ause; 16 return totaluse; 17 }
重新计算的过程:computesizes,结果必须保证空间一半以上是被利用的:
lua_assert(*narray/2 <= na && na <= *narray);
1 // 根据每个区域的数量,计算array和node的数量 2 static int computesizes (int nums[], int *narray) { 3 int i; 4 int twotoi; /* 2^i */ 5 int a = 0; /* number of elements smaller than 2^i */ 6 int na = 0; /* number of elements to go to array part */ 7 int n = 0; /* optimal size for array part */ 8 for (i = 0, twotoi = 1; twotoi/2 < *narray; i++, twotoi *= 2) { 9 if (nums[i] > 0) { 10 a += nums[i]; 11 if (a > twotoi/2) { /* more than half elements present? */ 12 n = twotoi; /* optimal size (till now) */ 13 na = a; /* all elements smaller than n will go to array part */ 14 } 15 } 16 if (a == *narray) break; /* all elements already counted */ 17 } 18 *narray = n; 19 lua_assert(*narray/2 <= na && na <= *narray); 20 return na; 21 }
luaH_resize用来重新根据两个数组大小申请空间,并且把旧的hash表中的元素重新插入新的hash数组中,解释都在注释中:
1 void luaH_resize(lua_State *L, Table *t, int nasize, int nhsize) { 2 int i; 3 int oldasize = t->sizearray; 4 int oldhsize = t->lsizenode; 5 Node *nold = t->node; /* save old hash ... */ 6 7 // 这里表明数组是不会缩小的,只会增加; 8 if (nasize > oldasize) /* array part must grow? */ 9 setarrayvector(L, t, nasize); 10 11 // hash表格会调整大小,增大或者缩小; 12 /* create new hash part with appropriate size */ 13 setnodevector(L, t, nhsize); 14 15 // 如果缩小,吧缩小部分的有效值重新hash一次; 16 if (nasize < oldasize) { /* array part must shrink? */ 17 t->sizearray = nasize; 18 /* re-insert elements from vanishing slice */ 19 for (i = nasize; i < oldasize; i++) { 20 if (!ttisnil(&t->array[i])) 21 luaH_setint(L, t, i + 1, &t->array[i]); 22 } 23 /* shrink array */ 24 luaM_reallocvector(L, t->array, oldasize, nasize, TValue); 25 } 26 27 // 如果是增大,重新hash一次,避免冲突结点过多 28 /* re-insert elements from hash part */ 29 for (i = twoto(oldhsize) - 1; i >= 0; i--) { 30 Node *old = nold + i; 31 if (!ttisnil(gval(old))) { 32 /* doesn't need barrier/invalidate cache, as entry was 33 already present in the table */ 34 setobjt2t(L, luaH_set(L, t, gkey(old)), gval(old)); 35 } 36 } 37 38 if (!isdummy(nold)) 39 luaM_freearray(L, nold, cast(size_t, twoto(oldhsize))); /* free old array */ 40 }