摘要:[Lua_Trace] 1. Lua數據結構 - TValue
欲深入了解Lua,我認為先從數據結構開始是必要的,
本文將由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))
有任何錯誤請指正,後續我將盡可能的再補充詳細