首页 > 代码库 > lua.5.2.3源码阅读(04):Table结构基本操作

lua.5.2.3源码阅读(04):Table结构基本操作

table(lobject.h)的结构定义

 1 // TKey结构是一个链表结构,用来存储hash相同 2 // 的所有key,value对结构。 3 typedef union TKey { 4   struct { 5     TValuefields;       // key值 6     struct Node *next;  // 指向像一个相同hash值的key值; 7   } nk; 8   TValue tvk;           //尾部的key值; 9 } TKey;10 11 // (key,value)对结构,通过key计算获得的相同hash的Node保存在一个链表中;12 typedef struct Node {13   TValue i_val;14   TKey i_key;15 } Node;16 17 18 typedef struct Table {19   CommonHeader;20   lu_byte flags;  21   lu_byte lsizenode;   // Node数量 = 2^lsizenode 个Node结构22   struct Table *metatable;23   TValue *array;       // 数组方式存储的Node结构24   Node *node;          // Node数组,数组的索引通过Key计算hash获取25   Node *lastfree;      // 指向Node数组中的最后一个空闲Node,初始化为Node数组的最后一个值26   GCObject *gclist;27   int sizearray;       // 数组方式储存的Node结构数量,即数组大小28 } Table;

 

通过上述结构:

a、Table作为数组使用是,获取较好的性能;

b、避免采用数组时,太多的空间浪费;

c、不过,通过后续的分析,还是将Table采用单一结构来使用(数组或者Map)可以获取更好的访问性能;

 

接下来分析Table的一些函数:

1、Table的创建和销毁;

2、Table的读取和写入;

3、Table的内存数组和Hash表的动态管理;

 

Table的创建和销毁;

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;}void luaH_free (lua_State *L, Table *t) {  if (!isdummy(t->node))    luaM_freearray(L, t->node, cast(size_t, sizenode(t)));  luaM_freearray(L, t->array, t->sizearray);  luaM_free(L, t);}

a、luaC_newobj函数生成一个大小为Table的内存块,同TString的方式一样,在申请内存的过程中,构造GCObject结构,

并且把Table挂在到lua_State中的全局gc object列表中,用于垃圾回收;

b、Table的删除也很简单:首先释放Hash表(实际上也是连续内存)、然后释放数组、最后释放Table结构指针指向的内存;

c、luaH_free函数本身不负责从gc链表中删除;

 

Table的读取和写入:

LUAI_FUNC const TValue *luaH_getint (Table *t, int key);
LUAI_FUNC void luaH_setint (lua_State *L, Table *t, int key, TValue *value);
LUAI_FUNC const TValue *luaH_getstr (Table *t, TString *key);
LUAI_FUNC const TValue *luaH_get (Table *t, const TValue *key);
LUAI_FUNC TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key);
LUAI_FUNC TValue *luaH_set (lua_State *L, Table *t, const TValue *key);

首先看int的读取和写入:

 1 /* 2 ** search function for integers 3 */ 4 const TValue *luaH_getint (Table *t, int key) { 5   /* (1 <= key && key <= t->sizearray) */ 6   if (cast(unsigned int, key-1) < cast(unsigned int, t->sizearray)) 7     return &t->array[key-1]; 8   else { 9     lua_Number nk = cast_num(key);10     Node *n = hashnum(t, nk);11     do {  /* check whether `key‘ is somewhere in the chain */12       if (ttisnumber(gkey(n)) && luai_numeq(nvalue(gkey(n)), nk))13         return gval(n);  /* that‘s it */14       else n = gnext(n);15     } while (n);16     return luaO_nilobject;17   }18 }19 20 void luaH_setint (lua_State *L, Table *t, int key, TValue *value) {21   const TValue *p = luaH_getint(t, key);22   TValue *cell;23   if (p != luaO_nilobject)24     cell = cast(TValue *, p);25   else {26     TValue k;27     setnvalue(&k, cast_num(key));28     cell = luaH_newkey(L, t, &k);29   }30   setobj2t(L, cell, value);31 }

读取过程:

a、判断key的值是否小于Table中sizearray数组大小;

b、如果小于数组大小,直接返回数组key所在的数组值;

c、如果大于数组大小,通过整型hashnum函数计算索引,并获取Node结构;

d、如果Node为空,则为没有该key对应的TValue;

e、如果Node非空,则从Node链表中遍历,查找和Key相同的Node,并返回TValue;

写入过程:

a、首先查找Table中是否包含key的元素,通过读取函数获取;

b、如果包括,则直接返回key对应的value,并设置int;

c、如果不包括,则新生成key,并把TValue返回,设置int值;

d、实际上在生成key的过程中,可能会内部调整内存结构,后续分析;

 

通过Table的读取或写入:

 1 const TValue *luaH_get (Table *t, const TValue *key) { 2   switch (ttype(key)) { 3     case LUA_TSHRSTR: return luaH_getstr(t, rawtsvalue(key)); 4     case LUA_TNIL: return luaO_nilobject; 5     case LUA_TNUMBER: { 6       int k; 7       lua_Number n = nvalue(key); 8       lua_number2int(k, n); 9       if (luai_numeq(cast_num(k), n)) /* index is int? */10         return luaH_getint(t, k);  /* use specialized version */11       /* else go through */12     }13     default: {14       Node *n = mainposition(t, key);15       do {  /* check whether `key‘ is somewhere in the chain */16         if (luaV_rawequalobj(gkey(n), key))17           return gval(n);  /* that‘s it */18         else n = gnext(n);19       } while (n);20       return luaO_nilobject;21     }22   }23 }24 25 TValue *luaH_set (lua_State *L, Table *t, const TValue *key) {26   const TValue *p = luaH_get(t, key);27   if (p != luaO_nilobject)28     return cast(TValue *, p);29   else return luaH_newkey(L, t, key);30 }31 32 static Node *mainposition (const Table *t, const TValue *key) {33   switch (ttype(key)) {34     case LUA_TNUMBER:35       return hashnum(t, nvalue(key));36     case LUA_TLNGSTR: {37       TString *s = rawtsvalue(key);38       if (s->tsv.extra == 0) {  /* no hash? */39         s->tsv.hash = luaS_hash(getstr(s), s->tsv.len, s->tsv.hash);40         s->tsv.extra = 1;  /* now it has its hash */41       }42       return hashstr(t, rawtsvalue(key));43     }44     case LUA_TSHRSTR:45       return hashstr(t, rawtsvalue(key));46     case LUA_TBOOLEAN:47       return hashboolean(t, bvalue(key));48     case LUA_TLIGHTUSERDATA:49       return hashpointer(t, pvalue(key));50     case LUA_TLCF:51       return hashpointer(t, fvalue(key));52     default:53       return hashpointer(t, gcvalue(key));54   }55 }

 

a、读取函数和上述的int的主要区别就是区分不同的类型采用不同的hash的方式(注意mainposition函数);

b、指针直接将地址采用int方式hash;

c、字符串的访问直接采用hash查找,实际上只有计算hash的时候较耗时,后续访问比c\c++中更快;

d、写入函数基本上和int一模一样;

 

下一篇详细说明一下Table结构内存增长时的管理方式。

lua.5.2.3源码阅读(04):Table结构基本操作