标签:
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结构内存增长时的管理方式。
标签:
原文地址:http://www.cnblogs.com/zerozero/p/4215616.html