您的位置:首页 > 编程语言 > Lua

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

2015-07-12 11:07 555 查看
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。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: