Lua5.2.3源码阅读(1)-TValue,TString

Lua是一个弱类型语言,类型可以在使用的时候变化。对应在源码中,TVable就充当了这个角色,lua中的所有数据类型都可以放在这个结构中。TValue是实现Lua数据类型的主要结构,不仅在脚本中使用了TValue,其他的一些数据结构也依赖于它。首先看看Lua的整个数据类型(lua.h)

#define LUA_TNONE       (-1)
#define LUA_TNIL        0
#define LUA_TBOOLEAN        1
#define LUA_TLIGHTUSERDATA  2
#define LUA_TNUMBER     3
#define LUA_TSTRING     4
#define LUA_TTABLE      5
#define LUA_TFUNCTION       6
#define LUA_TUSERDATA       7 //(自定义的用户数据结构,有Light和Heavy两种,后者由Lua来分配管理,用GC)
#define LUA_TTHREAD     8   //(线程,CoRoutine)
#define LUA_NUMTAGS     9

从这里可以看出lua中有9中数据类型,其中有8种是和脚本中对应的,现在来看看TValue这个结构。

#define TValuefields    Value value_; int tt_  ①
...
#undef TValuefields
...
/* little endian */
#define TValuefields  \
    union { struct { Value v__; int tt__; } i; double d__; } u ②
...
/* big endian */
#define TValuefields  \
    union { struct { int tt__; Value v__; } i; double d__; } u ③
...
struct lua_TValue {
  TValuefields;
};
...
typedef struct lua_TValue TValue;

在使用的这台x86机器上,使用的是定义②。因为#undef TValuefields,定义①在后面失效。定义②中用到了 Value,在来看看Value这个结构。

typedef union Value Value;
...
union Value {
  GCObject *gc;    /* collectable objects 可以gc的数据类型,*/
  void *p;         /* light userdata 轻量用户数据*/
  int b;           /* booleans */
  lua_CFunction f; /* light C functions */
  numfield         /* numbers暂时无用 */
};

Value中包含了数据结构GCObject和三种数据类型,先看看这里GCObject这个结构

union GCObject {
  GCheader gch;       /* common header */
  union TString ts;  //string类型
  union Udata u;         //用户数据
  union Closure cl;  //闭包
  struct Table h;    //表
  struct Proto p;    //函数字节码结构
  struct UpVal uv;   //闭包数据
  struct lua_State th;  /* thread 线程(协同)*/
};

其中GCheader如下

typedef struct GCheader {
CommonHeader;
} GCheader;

monHeader是一个宏,定义如下。

#define CommonHeader    GCObject *next; lu_byte tt; lu_byte marked 

*next将所有回收的对象连成一个链表,tt表示了对象的类型,marked表示当前的回收状态。通过tt可以确定当前需要访问那个类型的值,通过marked可以确定该对象是否可以被回收。

struct lua_TValue 
{
  union 
  {
    struct 
    { 
       union Value 
       {
         union GCObject {
           GCObject *next; 
           lu_byte tt; 
           lu_byte marked 
           union TString ts;  //string类型
           union Udata u;       //用户数据
           union Closure cl;    //闭包
           struct Table h;     //表
           struct Proto p;     //函数字节码结构
           struct UpVal uv;  //闭包数据
           struct lua_State th;  /* thread 线程(协同)*/
         } *gc;
         void *p;         /* light userdata 轻量用户数据*/
         int b;           /* booleans */
         lua_CFunction f;/* light C functions */
         numfield         /* numbers暂时无用 */
       } v__;
       int tt__;   /*数据类型*/
     } i; 
     double d__; 
  } u
};

其中tt__是数据类型,如果数据类型是NUM,那么值直接存在d__中,如果是其他类型,就放在value中。Value中有GCObject,这是一个可回收的结构。CommonHeader头中包含了回收对象链,数据类型tt,和标记状态marked。GCObject 采用了union的结构,可以表示不同的值,而这些值,都有CommonHeader作为头部。这样的处理方式,使得在访问GCObject头部时,设定头部值后,里面的所有联合体访问的都是这个头部。
Lua语句

下面来看看TString这个结构,其定义如下

typedef union TString {
  L_Umaxalign dummy;  /* ensures maximum alignment for strings 用于最大字节对齐,这里起占位作用*/
  struct {
    CommonHeader;  //用于gc处理的头
    lu_byte extra;  /* reserved words for short strings; "has hash" for longs 字符串是不是保留字符串*/
    unsigned int hash; //记录字符串对应的hash值
    size_t len;  /* number of characters in string 字符串长度*/
  } tsv;
} TString;

可以看出STring是个union,第一个成员主要是占用,用于字节对其。第二个成员是string主体,前面依然是CommonHeader,即STring也是可以gc的对象。在lua中,分为长字符串和段字符串。长度小于40的是短字符串,大于40的是长字符串(在luaconf.h中的LUAI_MAXSHORTLEN中定义)。短字符串存放在global_State->strt中,长字符串放在gc链上。下面是创建字符串用到的函数

TString *luaS_newlstr (lua_State *L, const char *str, size_t l) {
  if (l <= LUAI_MAXSHORTLEN)  /* short string? */
    return internshrstr(L, str, l);
  else {
    if (l + 1 > (MAX_SIZET - sizeof(TString))/sizeof(char))
      luaM_toobig(L);
    return createstrobj(L, str, l, LUA_TLNGSTR, G(L)->seed, NULL);
  }
}

对于短字符串,在创建的时候,首先计算str的hash值。计算时会获得一个随机种子,这个种子就是global_State->seed。然后通过LUAI_HASHLIMIT控制步长,每一个步长范围内取字符串中的一个字符,和上次hash的结果相加,得到新的hash结果。计算出hash后就开始找是否存在这个字符串,方法是遍历global_State->strt->hash,global_State->strt结构定义如下。短字符串表申请内存的大小和实际使用大小由后两个字段表示。

typedef struct stringtable {
  GCObject **hash;  //保存所有的字符串
  lu_int32 nuse;  /* number of elements 已装元素的个数*/
  int size;  //当前hash桶的大小
} stringtable;

这里写图片描述
GCObject是一个数组,数组中每个元素是一个STring同义词链,每新建的一个元素其hash值通过lmod方法得在数组中的位置,然后挂在在位置的list链上。这种方类似于用链地址表法解决同义词冲突,方便查找和删除。
遍历global_State->strt->hash数组时,首先是比较hash,相同再比较len(字符串长度),相同在判断原始str是否相同。通过两个&&关系起到了过滤的作用,可以加快判断的速度。如果找到有相同的字符串,那就直接返回找到的STting,没有找到,就需要创建新的段字符串,通过函数newshrstr实现。先创建一个LUA_TSHRSTR结构,然后存放到global_State->strt->hash中。

对于长字符串,创建的是LUA_TLNGSTR类型结构。会直接createstrobj创建一个gc对象,挂到gc链上。长字符串一般是使用的文本,在新建的时候不计算hash值,也不保证唯一性,使用extra字段来表示是否计算hash。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值