• lua数据类型


    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。


         
  • 相关阅读:
    < java.util >-- Set接口
    Codeforces 627 A. XOR Equation (数学)
    Codeforces 161 B. Discounts (贪心)
    Codeforces 161 D. Distance in Tree (树dp)
    HDU 5534 Partial Tree (完全背包变形)
    HDU 5927 Auxiliary Set (dfs)
    Codeforces 27E. Number With The Given Amount Of Divisors (暴力)
    lght oj 1257
    Codeforces 219D. Choosing Capital for Treeland (树dp)
    Codeforces 479E. Riding in a Lift (dp + 前缀和优化)
  • 原文地址:https://www.cnblogs.com/persistentsnail/p/3294846.html
Copyright © 2020-2023  润新知