Lua中table类型的源码实现

  1、概述
    table是lua中唯一的表示数据结构的工具。它可以用于实现数据容器、函数环境(Env)、元表(metatable)、模块(module)和注册表(registery)等其他各种用途。因此了解table的实现是非常有必要的,根据《Lua中数据类型的源码实现》中知道,在Lua中,table是由结构体体Table来实现的。下面将以Lua 5.2.1的源码来看table的实现。
  2、实现原理
    在Lua5.0以后,table是以一种混合型数据结构来实现的,它包含一个哈希表部分和一个数组部分,比如下面操作的构造table:
local t = {100}
t[2] = 200
table.insert(t, 300)
t.x = 9.2
print(t[1], t[2], t[3], t.x)

其可能的实现方式如下图:


注意数组部分不需要保存键值,只要在底层实现时才考虑这种区别,其他情况,即使对虚拟机来说,访问表项也是由底层自动统一的,因为使用的时候无须考虑这种差别。在往表中插入数值时,表会根据key-value的值和表当前的数据内容自动动态地使用这个两个部分:数据部分试图保存所有key值介于1和某个上限n之间的值,非整数key和超过数据范围n的整数key对应的值将存入哈希表部分。
对于数组部分,要求的数组的大小同时满足:1到n之间至少一半的空间被利用(避免像稀疏数组一样浪费空间);并且n/2+1到n之间的空间至少有一个空间被利用(避免n/2个空间就能容纳所有数据时申请了n个空间浪费)。
对于哈希部分,解决冲突的方式是用开放寻址法(open addressing),即所有的元素都存在哈希表中,使用这种方法往往可以让Hash表内数据更紧凑,有更高效的空间利用率,并且在用这个方法时还做了改进,下面将通过源代码具体分析。
  3、源码实现
   首先来看对应的数据结构Table,其源码如下(lobject.h):

105 #define TValuefields    Value value_; int tt_ 
544 /*
545 ** Tables 
546 */
547   
548 typedef union TKey {
549   struct { 
550     TValuefields;
551     struct Node *next;  /* for chaining */
552   } nk;
553   TValue tvk;
554 } TKey;
555 
556 
557 typedef struct Node {
558   TValue i_val;
559   TKey i_key;
560 } Node;
561 
562 
563 typedef struct Table {
564   CommonHeader;
565   lu_byte flags;  /* 1<<p means tagmethod(p) is not present */
566   lu_byte lsizenode;  /* log2 of size of `node' array */
567   struct Table *metatable;
568   TValue *array;  /* array part */
569   Node *node;
570   Node *lastfree;  /* any free position is before this position */
571   GCObject *gclist;
572   int sizearray;  /* size of `array' array */
573 } Table;
  Table结构的头CommonHeader与TString中是一样的,用于GC,实质上所有GC类型的头上相同的,都包含这个宏。
   成员TValue *array就是Table的数组部分,TValue表示Lua数据类型通用实现,再前面文章已经分析过。成员int sizearray指明了这个数组的大小。
   成员Node *node就是Table的哈希表部分,其大小保存在成员lu_byte lsizenode中,注意保存的是哈希表大小的幂次,而不是实质大小。比如哈希表的大小为2^n,则lsizenode中保存的值是n,同时也说明哈希表的长度只能是2的幂次。
   结构体Node中包含两个成员i_key和i_val,很显然分别表示key、value,其中value的数据类型就是通用的Lua数据类型TValue;key的数据类型是联合体,除了通常存储数据外,key还有一个作用是保存Node中next指针,也就是说key除了能保存TValue的数据结构外,还多了一个next指针,这个next指针就是用作同一个hash值下冲突时的链表指针。成员Node *lastfree就是链表的最后一个空元素。
   成员struct Table *metatable是元表的指针,每个table的元表也是一个table。lu_byte flags用于元表元方法的一些优化手段,一共有8位用于标记是否没有某个元方法,初始值都是有的。成员GCObject *gclist用于GC。下面是创建一个表的接口,代码如下(ltable.c):

368 Table *luaH_new (lua_State *L) {
369   Table *t = &luaC_newobj(L, LUA_TTABLE, sizeof(Table), NULL, 0)->h;
370   t->metatable = NULL;
371   t->flags = cast_byte(~0);
372   t->arr
  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值