lua数据类型

it2022-06-30  99

1.TValue      typedef struct      {           Value value;           int tt;      } TValue      lua所有类型,Value是union,      typedef union      {           // GCObject *gc; 可以gc的类型            GCheader gch;  // 头部            union TString ts; // 字符串            union Udata u;  // userdata            union Closure cl; // 闭包函数            struct Table h;  // 表            struct Proto p;   // 函数原型            struct UpVal uv;  // upvalue            struct lua_State th;  /* thread */           // 不可gc的类型           void *p;           lua_Number n;           int b;        } Value          GCHeader头部信息:           typedef struct           {                GCObject *next; lu_byte tt; lu_byte marked           }GCHeader; 2.字符串类型      typedef union TString {         L_Umaxalign dummy;  /* ensures maximum alignment for strings */         struct {                CommonHeader;                lu_byte reserved;                unsigned int hash;                size_t len;                   } tsv;            } TString;      这个CommonHeader就是GCheader,其实是同一块内存,这样写法,在GCObject和具体类型中都可以访问到头部。      lua里字符串内存布局:字符串类型表现形式是TString指针,其实是一个TString头部包含字符串描述信息,紧接着才是一个(TString->len)长度的字符串内容,并加一个'\0'字符。      lua里保存字符串的方式:global state有一个全局字符串表strt(stringtable类型),其实是一个哈希表,通过一种哈希算法(保存在TSreing->hash)计算出哈希值,保存到对应表项,对于冲突项使用链表解决(TString->tsv->next保存下一个相同哈希值的字符串)。      lua的这种机制,使得相同的字符串公用一块内存,节省了不少内存,同其他GC对象一样,字符串都是引用着存在。看下新创建一个字符串类型的过程: TString *luaS_newlstr (lua_State *L, const char *str, size_t l) { 76   GCObject *o; 77   unsigned int h = cast(unsigned int, l);  /* seed */ 78   size_t step = (l>>5)+1;  /* if string is too long, don't hash all its chars */ 79   size_t l1; 80   for (l1=l; l1>=step; l1-=step)  /* compute hash */ 81     h = h ^ ((h<<5)+(h>>2)+cast(unsigned char, str[l1-1])); 82   for (o = G(L)->strt.hash[lmod(h, G(L)->strt.size)]; 83        o != NULL; 84        o = o->gch.next) { 85     TString *ts = rawgco2ts(o); 86     if (ts->tsv.len == l && (memcmp(str, getstr(ts), l) == 0)) { 87       /* string may be dead */ 88       if (isdead(G(L), o)) changewhite(o); 89       return ts;       90     }          91   }             92   return newlstr(L, str, l, h);  /* not found */ 93 } 94  3.table类型      typedef struct Table {   CommonHeader;   lu_byte flags;  /* 1<<p means tagmethod(p) is not present */   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类型被分为两部分了,一部分是array一部分是hash table,这样可以对用户使用数组进行优化,lua_creatable(L, narray, nrec) API :narray代表数组部分大小,nrec代表hash表部分大小。      对于数组部分的实现没啥好说了,看下哈希表的算法。      哈希表数据结构: typedef union TKey {   struct {     TValuefields;     struct Node *next;  /* for chaining */   } nk;   TValue tvk; } TKey; typedef struct Node {   TValue i_val;   TKey i_key; } Node;      其实是一个Node数组,Node分为key,value字段,key其实也可以是任何value,TKey又使用了之前的技巧,nk,tvk是union的字段,公用同样的内存,这样既可TKey->tvk取值,又可TKey->nk->..取值,另外key保存了一个指向冲突表项的指针。      总而言之,这个哈希表是这样的:内存上只是一块Node的连续数组,插入时:依据key算出hash value,mod到表项,如果这一表项是空项,直接插入。如果冲突,先取一块新的空表项(根据Table的lastfree取),之后判断冲突表项的key hash后是否应该放在自己所在的位置,如果不应该放在这,把它挪到刚取出的空表项,腾出的表项放新key;如果确实是放在这,也就是与新key有同样的hash value,则把新key插入空表项,并设计好链接关系。看下代码吧:      static TValue *newkey (lua_State *L, Table *t, const TValue *key) {   Node *mp = mainposition(t, key);   if (!ttisnil(gval(mp)) || mp == dummynode) {     Node *othern;     Node *n = getfreepos(t);  /* get a free place */     if (n == NULL) {  /* cannot find a free place? */       rehash(L, t, key);  /* grow table */       return luaH_set(L, t, key);  /* re-insert key into grown table */     }     lua_assert(n != dummynode);     othern = mainposition(t, key2tval(mp));     if (othern != mp) {  /* is colliding node out of its main position? */       /* yes; move colliding node into free position */       while (gnext(othern) != mp) othern = gnext(othern);  /* find previous */       gnext(othern) = n;  /* redo the chain with `n' in place of `mp' */       *n = *mp;  /* copy colliding node into free pos. (mp->next also goes) */       gnext(mp) = NULL;  /* now `mp' is free */       setnilvalue(gval(mp));     }     else {  /* colliding node is in its own main position */       /* new node will go into free position */       gnext(n) = gnext(mp);  /* chain new position */       gnext(mp) = n;       mp = n;     }   }   gkey(mp)->value = key->value; gkey(mp)->tt = key->tt;   luaC_barriert(L, t, key);   lua_assert(ttisnil(gval(mp)));   return gval(mp); }            看下接口设计,每次插入时,luaH_set其实是返回了这个key对应的value指针,所以使用时类似这样  setobj2t(L, luaH_set(L, hvalue(t), L->top-2), L->top-1); 4.函数类型   typedef struct CClosure {   ClosureHeader;   lua_CFunction f;   TValue upvalue[1]; } CClosure; typedef struct LClosure {   ClosureHeader;   struct Proto *p;   UpVal *upvals[1]; } LClosure; typedef union Closure {   CClosure c;   LClosure l; } Closure; #define ClosureHeader \      CommonHeader; lu_byte isC; lu_byte nupvalues; GCObject *gclist; \      struct Table *env   分为c函数闭包和lua函数闭包,其实都是包含函数原型,upvalues,环境表这三个部分。关于c函数闭包实现比较简单,主要是通过接口lua_pushcclosure创建,函数实现中可以通过lua_getvalue访问upvalue。lua函数闭包和虚拟机相关,后面再描述。 5.upvalue upvalue不是单独的一个对外可用的类型,他是Closure的一部分,是为了引用在这个函数外部的局部变量而设计的。许多语言限制了这种特性,比如python用作用域限制访问外部的局部变量,pascal的函数类型不是第一类类型,即你必须保证外部的变量一直在栈中,以求引用时,内存仍然存在。c语言两种限制方式都存在。lua里面使用upvalue,以很少的代码实现了访问函数外部局部变量的特性。看定义: /* ** Upvalues */ typedef struct UpVal {   CommonHeader;   TValue *v;  /* points to stack or to its own value */   union {     TValue value;  /* the value (when closed) */     struct {  /* double linked list (when open) */       struct UpVal *prev;       struct UpVal *next;     } l;   } u; } UpVal; UpVal有两个状态open和closed:      open状态是指UpVal所引用的值还在外部函数栈上保存,这时候UpVal->v指向栈内存,UpVal->prev和UpVal->next则是用于把所有UpVal链接到一个链表里,链表保存在lua_State的openupval,这样如果有不同函数引用同一个变量,就可以直接使用这个UpVal,而不需要再创建一个新的。      closed状态是指UpVal所引用的值所在的栈已经被释放了,例如:      function add (x)           return function (y) return y + x end      end      add2 = add(2)      print(add2())      当执行add2()时add2引用的x所在的栈已经释放了,这时,lua会复制x到UpVal->l.value,并且UpVal->v指向自己的value。      

转载于:https://www.cnblogs.com/persistentsnail/archive/2013/04/02/3294846.html

相关资源:Lua判断数据类型的方法

最新回复(0)