Lua_Trace 1. Lua数据结构
<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);
#define hvalue(o) check_exp(ttistable(o), gco2t(val_(o).gc))
#define val_(o) ((o)->value_)
#define gco2t(o) check_exp((o)->tt == LUA_TTABLE, &((cast_u(o))->h))
#define cast_u(o) cast(union GCUnion *, (o))
有任何错误请指正,后续我将尽可能的再补充详细