Lua Types: Common Structures

Lua 数据类型的通用表示结构

Lua 内部目前共存在 10 种数据类型,为了统一表示这些数据类型,Lua 内部实现了一套统一的数据结构,模拟了「基类」的效果。

从顶向下共涉及以下数据结构:

TValue——通用类型数据+类型

TValue 结构可用以表达所有 Lua 中的数据类型,其定义如下:

#define TValuefields	Value value; int tt

typedef struct lua_TValue {
  TValuefields;
} TValue;

包含了两个字段,分别用以存储特定类型的数据 value,以及类型信息 tttt 字段有一批关联的宏,用以判断各个具体类型,比如其中两个宏如下:

/* Macros to test type */
#define ttisnil(o)	(ttype(o) == LUA_TNIL)
#define ttisnumber(o)	(ttype(o) == LUA_TNUMBER)
// ...

可以看到 tt 的取值有 LUA_TNIL 等,他们的完整列表有:

  • #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
  • #define LUA_TTHREAD   8

这也就是上文所说「10 种」类型的由来。

需要 GC 的各个类型的 CommonHeader 中其实也有一个 tt 字段,但为了足以区分布尔、整数等非 GC 类型,在这里还是需要设立一个 tt 以区分开来。

Value——通用类型数据

相当于所有数据类型的基类,来看定义:

typedef union {
  GCObject *gc;
  void *p;
  lua_Number n;
  int b;
} Value;

Lua 的数据类型有两大类:纳入 GC 管理的,以及不需要 GC 的。他们分别对应着此联合体内的 gc 字段与其他三个字段。注意到有一个宏:

#define ttype(o)	((o)->tt)
#define iscollectable(o)	(ttype(o) >= LUA_TSTRING)

iscollectable 被用来判断对应数据类型是否需要 GC。也就是说,不需要 GC 的类型共有 TNONETNILTBOOLEANTLIGHTUSERDATATNUMBER。忽略前两个空值之后,他们刚好一一对应着 Value 联合体内剩下的三个字段:pTLIGHTUSERDATAnTNUMBERbTBOOLEAN

由于 Value 是一个联合体,以上四个字段不会同时使用。但是当使用其中任何一个字段时,都能正确表示对应的类型。(缺点是整个结构体的大小会被最大的部分给撑大,但只要创建数据结构时,不要直接创建 Value 即可,而应该针对具体的数据类型编写具体的创建逻辑)

GCObject——可GC的通用类型数据

union GCObject {
  GCheader gch;
  union TString ts;
  union Udata u;
  union Closure cl;
  struct Table h;
  struct Proto p;
  struct UpVal uv;
  struct lua_State th;  /* thread */
};

首先注意到的是,它仍然是一个联合体,所以仍然可以发挥类似「基类」的作用,表示所有字段对应的 Lua 数据类型。

除了 gch 之外,其他字段都能看出来对应的具体数据类型的影子。以 TString 为例,看看其结构(详细可参考我的这篇文章:Lua 字符串源码解析):

typedef union TString {
  L_Umaxalign dummy;  /* ensures maximum alignment for strings */
  struct {
    CommonHeader;
    lu_byte reserved;
    unsigned int hash;
    size_t len;
  } tsv;
} TString;

暂且忽略 dummy 字段,第二层首先是一个通用的 CommonHeader 头,其次就是 TString 自己的字段。来看看其定义:

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

值得一提的是,GCObject 的成员之一 GCHeader,其定义也仅仅包含了 CommonHeader

typedef struct GCheader {
  CommonHeader;
} GCheader;

除了 GCHeader 之外,其他字段对应的结构体都统一以 CommonHeader 开头。因此,当只需要 GC 信息时,可以借助 gch 字段获取(并忽略各个类型剩余的特异的字段);当需要整个类型的信息时,也可以通过对应的字段获取。


参考资料:

  • 《Lua设计与实现》2.2章节

题图来自 Photo by Michael on Unsplash

Lua 数据类型的通用表示结构》有2个想法

  1. Pingback引用通告: Lua 表源码解析 | chenxy.me

  2. Pingback引用通告: Lua 字符串源码解析 | chenxy.me

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据