Lua_Trace 1. Lua数据结构

2.3k 词
                    <p>摘要:[Lua_Trace] 1. Lua数据结构 - TValue</p>
                <br />
                <p>
欲深入了解Lua,我认为先从数据结构开始是必要的,</p>

本文将由Lua基础数据结构(TValue)讲起, 进而衍生至TString, Table ⋯ 等。

此源码分析的版本为 : Lua 5.3.1

1. TValue : 基础数据

Lua中所有的数据结构都由TValue衍伸,以OO的概念来看它就有点像是基底类(abstract)般的存在着,因此采C语言来模拟,就是定义出一个通用的结构体作为”父类”,然后子类的结构体中以这个父类作为结构体的第一个成员变量。


//lobject.h

typedef struct lua_TValue TValue;

struct lua_TValue {
TValuefields;
};

/*
Tagged Values. This is the basic representation of values in Lua,
an actual value plus a tag with its type.
*/
#define TValuefields Value value_; int tt_

tt_可再区分为 tt (low byte) & marked (hight byte),前者表示数据的类型(详细可见下表),后者则是GC回收用途的标记(marked)。

 

value_则是存放各种数据,事实上Lua将数据区分为两大类:

1. 原始类型 : 透过C语言表示的对应类型,例:void *p表示 light userdata、int 表示 boolean、double 表示 lua_Number‧‧‧等。

                    因此如为原始类型的数据,则根据其tt_类型将数据对应的置于Value union中的 p (void *), b (int), f(lua_CFunction), i (lua_Integer), n(lua_Number)中。

2. 可被GC回收类型 : 统一使用GCObject的指针表示。


//lobject.h

union Value {
GCObject gc; / collectable objects */
void p; / light userdata /
int b; /
booleans /
lua_CFunction f; /
light C functions /
lua_Integer i; /
integer numbers /
lua_Number n; /
float numbers */
};

GCObject的成员由 GCObject指针*next, 数据类型tt与回收标签marked组成, 其中tt与marked两者同TValue中的tt_定义,也是为了识别该数据的类型。

可被GC回收的数据类型都有个共同点就是其结构的第一个成员皆是CommonHeader,此手法同前面提到OO概念,在Lua源码中算是蛮常见的,因此CommonHeader可算是所有GC回收类型的父类。


/*
 Common type has only the common header
*/
struct GCObject {
  CommonHeader;
};

/*
Common Header for all collectable objects (in macro form, to be
included in other objects)
*/
#define CommonHeader GCObject *next; lu_byte tt; lu_byte marked

GCUnion : 专针对GC类型进行转型的数据结构


//lstate.h

/*
Union of all collectable objects (only for conversions)
/
union GCUnion {
GCObject gc; /
common header /
struct TString ts;
struct Udata u;
union Closure cl;
struct Table h;
struct Proto p;
struct lua_State th; /
thread */
};

一些会被GC回收的类型都集合在GCUnion中  (ex : TString, Udata, Closure, Table, Proto, lua_State)

相对的,GC回收的数据类型中它的第一项元素往往是CommonHeader (GCObject的共有定义,前面也才刚提到)

事实上GCUnion的主用功能是拿来转型之用,由于在Lua栈上的数据都视为TValue

故Lua运用一些定义(define)协助转型(ex : hvalue),而这过程中常透过GCUnion来取回数据的真实类型。

例如 :  Table -> TValue -> GCUnion - > Table

上述原Table类型的变量可能因为被存入栈中后被统一视为TValue,

因此如果需要正确的将TValue转型为Table则可透过 hvalue (如下程序) 协助,仔细追朔其中不难发现它最后都会透过GCUnion来达到正确的类型。


//lvm.c

Table *h = hvalue(t);

//lobject.h

#define hvalue(o) check_exp(ttistable(o), gco2t(val_(o).gc))

#define val_(o) ((o)->value_)

//lstate.h

#define gco2t(o) check_exp((o)->tt == LUA_TTABLE, &((cast_u(o))->h))

#define cast_u(o) cast(union GCUnion *, (o))

有任何错误请指正,后续我将尽可能的再补充详细