lua的字符串和Table类型实现

字符串

实现在lstring.c中。
字符串类型TString定义如下:

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

字符串内容紧随其后,以\0结尾,用宏getstr来获取字符串指针:

#define getstr(ts)	(const char *)((ts) + 1)

len是字符串的长度,不包括结尾的\0。CommonHeader用于GC。hash是由字符串内容计算出来的哈希值。dummy可能是为了让字符串指针内存对齐,提高访问速度。

lua的字符串内容不可修改,相同字符串会合并存储在一个全局字符串表中,字符串表定义如下:

typedef struct stringtable {GCObject **hash;lu_int32 nuse; /* number of elements */int size;
} stringtable;

这是一个开散列的哈希表实现。一个字符串被放入字符串表的时候,先检查一下表中有没有相同的字符串。如果有,则复用己有的字符串;没有则创建一个新的。碰到哈希值相同的字符串,简单的串在同一个哈希位的链表上即可。

创建一个lua字符串的函数如下:

TString *luaS_newlstr (lua_State *L, const char *str, size_t l) {GCObject *o;unsigned int h = cast(unsigned int, l); /* seed */size_t step = (l>>5)+1; /* if string is too long, don't hash all its chars */size_t l1;for (l1=l; l1>=step; l1-=step) /* compute hash */h = h ^ ((h<<5)+(h>>2)+cast(unsigned char, str[l1-1]));for (o = G(L)->strt.hash[lmod(h, G(L)->strt.size)];o != NULL;o = o->gch.next) {TString *ts = rawgco2ts(o);if (ts->tsv.len == l && (memcmp(str, getstr(ts), l) == 0)) {/* string may be dead */if (isdead(G(L), o)) changewhite(o);return ts;}}return newlstr(L, str, l, h); /* not found */
}

Real-World Impact of Hash DoS in Lua 指出该哈希计算方法面临Dos攻击的风险,攻击者可以轻易构造出上千万拥有相同哈希值的不同字符串,以此数十倍的降低lua从外部压入字符串进入内部字符串表的效率。
Lua 5.2.1为了解决这个问题,把长字符串独立出来,长字符串不再通过哈希内部化进入全局字符串表。同时使用了一个随机种子用于哈希值的计算,使攻击者无法轻易构造出拥有相同哈希值的不同字符串。

userdata在储存形式上和字符串相同,可以看成是拥有独立元表,不被内部化处理,也不需要追加\0的字符串。在实现上,只是对象结构从TString换成了UData,所以实现代码也被放在lstring.c中,其api也以luaS开头。

Table

实现在ltable.c中。
Table类型定义如下:

typedef struct Table {CommonHeader;lu_byte flags; /* 1<<p means tagmethod(p) is not present */lu_byte lsizenode; /* log2 of size of `node' array */struct Table *metatable;TValue *array; /* array part */Node *node;Node *lastfree; /* any free position is before this position */GCObject *gclist;int sizearray; /* size of `array' array */
} Table;

table的储存分为数组部分和哈希表部分。
数组部分在TValue *array,数组大小是sizearray。
哈希表以闭散列方式实现,节点类型:

typedef struct Node {TValue i_val;TKey i_key;
} Node;

TKey相比TValue多了一个struct Node *next成员,用以链接冲突的节点。
非dummynode哈希表的大小为2^lsizenode。
为了减少空表的维护成本,定义了一个不可改写的全局空哈希表dummynode,当以大小0初始化或resize哈希表时,node域指向这个dummy节点,此时lsizenode也为0。
云风在 一个链接 lua 引起的 bug , 事不过三 中描述了一个dummynode的问题,用NULL来替换dummynode或许可以更好的解决这个问题。

所以每个table结构,最多会由三块连续内存构成。一个Table结构,一块存放了连续整数索引的数组,和一块大小为2的整数次幂的哈希表。
lastfree最初指向node末尾,每当需要一个空闲节点时,就从lastfree倒着查找,如果lastfree到达node起始位置,说明空间已满,需要扩容了。

是不是所有key是正整数的节点一定在数组部分?
不是的,刚创建的空表数组部分长度为0,哈希表也为空,插入新元素时,如果key是数字但不在[1,sizearray]范围内,会往哈希表里插,如果发现哈希表没有空闲节点,便会执行rehash(),rehash()会依据一定规则(computesizes()函数,调整数组部分的大小使其利用率不小于50%),重新设置数组和哈希表的大小。

往哈希表中插入节点的流程可直接参考newkey()的注释:

/*
** inserts a new key into a hash table; first, check whether key's main
** position is free. If not, check whether colliding node is in its main
** position or not: if it is not, move colliding node to an empty place and
** put new key in its main position; otherwise (colliding node is in its main
** position), new key goes to an empty position.
*/
static TValue *newkey (lua_State *L, Table *t, const TValue *key);

table的长度定义只对序列表有效,所以在实现的时候,仅需要遍历数组部分。只有当数组部分填满时才需要进一步的去检索哈希表。

元表

实现在ltm.c中。
Lua实现复杂数据结构大量依赖给table附加一个元表(metatable)来实现,故而table本身的一大作用就是作为元表存在。但并非所有元表都提供了所有元方法,对于不存在的元方法查询就是一个浪费了。
在上面代码中,可以看到每个Table结构中都有一个fags域,它记录了那些元方法不存在,查询元方法时用于缓存查询结果:

const TValue *luaT_gettm (Table *events, TMS event, TString *ename) {const TValue *tm = luaH_getstr(events, ename);lua_assert(event <= TM_EQ);if (ttisnil(tm)) { /* no tag method? */events->flags |= cast_byte(1u<<event); /* cache this fact */return NULL;}else return tm;
}

lua仅对table的元表做了这个优化,而没有理会其它类型的元表的元方法查询。这大概是因为只有table容易缺失一些诸如__index这样的元方法,而使用table的默认行为。当lua代码把这些操作作用于其它类型如userdata时,它没有table那样的默认行为,故对应的元方法通常存在。

参考

Lua 源码欣赏 云风
lua 5.1.4 源码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/819965.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【安装部署】Apache SeaTunnel 和 Web快速安装详解

版本说明 由于作者目前接触当前最新版本为2.3.4 但是官方提供的web版本未1.0.0&#xff0c;不兼容2.3.4&#xff0c;因此这里仍然使用2.3.3版本。 可以自定义兼容处理&#xff0c;官方提供了文档&#xff1a;https://mp.weixin.qq.com/s/Al1VmBoOKu2P02sBOTB6DQ 因为大部分用…

引领智能互联时代,紫光展锐赋能百业创新发展

随着5G技术的快速发展&#xff0c;各行各业对通信技术的需求也在不断升级。紫光展锐持续深耕5G垂直行业&#xff0c;不断推进5G标准演进&#xff0c;从R15到R16&#xff0c;再到R17&#xff0c;展锐携手生态合作伙伴&#xff0c;不断推出创新性解决方案&#xff0c;在5G RedCap…

【Unity】RPG小游戏创建游戏中的交互

RPG小游戏创建游戏中的交互 创建可交互的物体的公共的父类&#xff08;Interactable&#xff09;InteractableObject 类NPCObject 类PickableObject 类 创建可交互的物体的公共的父类&#xff08;Interactable&#xff09; InteractableObject 类 using System.Collections; u…

【攻防世界】warmup

[HCTF 2018]WarmUp全网最详细解释_[hctf 2018]warmup的解-CSDN博客 php://filter 读取源码&#xff08;文件&#xff09; php://input 执行php代码&#xff0c;需要post请求提交数据 Content-Type为image/jpeg text. 绕过后缀的有文件格式有php,php3,php4,php5,pht…

【Unity 实用工具篇】 | UIEffect 实现一系列UGUI特效,灰度、负片、像素化特效

前言 【Unity 实用工具篇】 | UIEffect 实现一系列UGUI特效&#xff0c;灰度、负片、像素化特效一、UGUI特效插件&#xff1a;UIEffect1.1 介绍1.2 效果展示1.3 使用说明及下载 二、组件属性面板三、代码操作组件四、组件常用方法示例4.1 使用灰度特效做头像(关卡)选择 总结 前…

03-JAVA设计模式-迭代器模式

迭代器模式 什么是迭代器模式 迭代器模式&#xff08;demo1.Iterator Pattern&#xff09;是Java中一种常用的设计模式&#xff0c;它提供了一种顺序访问一个聚合对象中各个元素&#xff0c;而又不需要暴露该对象的内部表示的方法。迭代器模式将遍历逻辑从聚合对象中分离出来…

IP地址归属地与旅游业应用

在当今数字化时代&#xff0c;IP地址归属地已成为许多行业的重要工具&#xff0c;其中包括旅游业。IP地址归属地是指将特定IP地址与其地理位置相关联的过程。在旅游业中&#xff0c;利用IP地址归属地可以提供多种应用&#xff0c;从客户定位到个性化推广&#xff0c;以及旅游数…

树--排序二叉树的删除

一、二叉排序树的删除 二叉排序树的删除情况比较复杂&#xff0c;有以下三种情况需要考虑。 删除叶子节点 &#xff08;比如&#xff1a;2,5,9,10&#xff09;删除只有一个子树的节点&#xff08;比如&#xff1a;1&#xff09;删除有两个子树的节点 &#xff08;比如&#x…

YAPI第一次创建项目

黑马程序员JavaWeb开发教程 文章目录 1、添加项目2、添加分类3、添加接口 1、添加项目 2、添加分类 3、添加接口

数据结构:线性表————单链表专题

&#x1f308;个人主页&#xff1a;小新_- &#x1f388;个人座右铭&#xff1a;“成功者不是从不失败的人&#xff0c;而是从不放弃的人&#xff01;”&#x1f388; &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd; &#x1f3c6;所属专栏&#xff1…

STM32的GPIO端口的八种模式解析

目录 STM32的GPIO端口的八种模式解析 一、上拉输入模式 二、下拉输入模式 三、浮空输入模式 四、模拟输入模式 五、推挽输出模式 六、开漏输出模式 七、复用推挽输出模式 八、复用开漏输出模式 STM32的GPIO端口的八种模式解析 在学习STM32的过程中&#xff0c;GPIO端口…

最全面的多语言同城送餐app开发流程解析

在当今数字化时代&#xff0c;随着移动互联网的普及和人们生活水平的提高&#xff0c;多语言同城送餐app开发成为各大企业争相布局的热门领域。本文将从专家的视角出发&#xff0c;为您详细解析最全面的多语言同城送餐app开发流程&#xff0c;助您在激烈的竞争中脱颖而出。 多…

使用colab进行yolov5小demo练习

输入一张动物的图片进行目标检测和分类 !pip install yolov5 import torch from PIL import Image from torchvision import transforms from yolov5.models.experimental import attempt_load from yolov5.utils.general import non_max_suppression# 加载YOLOv5模型 device …

婴儿专用洗衣机哪个牌子比较好?四款品质婴儿洗衣机暖心安利

科技让我们的生活变得方便了许多&#xff0c;比如&#xff0c;自从有了婴儿洗衣机之后&#xff0c;有些人就改变了宝宝衣物必须要手洗的想法&#xff0c;许多研究也证明&#xff0c;单靠手洗是无法将宝宝衣物彻底清洗干净的&#xff0c;一台专门的洗衣机就可以减轻我们的负担&a…

【MySQL笔记】InnoDB的插入缓存+非聚簇索引插入的离散性理解

文章目录 为什么需要插入缓存Insert Buffer非聚簇索引插入的离散性 Insert Buffer查看Insert Buffer信息 Insert Buffer的问题Change Buffer总结Reference 为什么需要插入缓存Insert Buffer 磁盘中的主键索引由于天然自增&#xff0c;无须磁盘的随机 I/O&#xff0c;只需不断追…

聚观早报 | 2024款蔚来ET7将发布;魏建军直播测试长城汽车

聚观早报每日整理最值得关注的行业重点事件&#xff0c;帮助大家及时了解最新行业动态&#xff0c;每日读报&#xff0c;就读聚观365资讯简报。 整理丨Cutie 4月16日消息 2024款蔚来ET7将发布 魏建军直播测试长城汽车 Redmi Turbo 3开启销售 华为P系列正式升级为华为Pura…

数据采集1688官方API商品数据采集1688官方商品详情数据返回|电商API接口数据采集

随着全球经济一体化和电子商务的快速发展&#xff0c;网络购物的需求日益增加。不断涌现的电商企业使得行业的竞争情况愈演愈烈。在这种情况下&#xff0c;企业不仅要加大经营力度&#xff0c;还要在自己的基础设施和技术上持续投入&#xff0c;才能更好的适应市场和消费习惯。…

开春运动正当时,谨防这些运动损伤,别让“健身”变“伤身”

春季运动正当时 运动损伤需警惕 科学运动 快乐加倍 天气回升&#xff0c;春暖清明 户外运动爱好者纷纷复出 但要注意的是 春季是运动受伤的高发季 因为经过寒冷冬季的“运动低潮期”&#xff0c;身体各器官的季节活跃性较低&#xff0c;运动方式和强度不合适很容易导致身体…

C++(运算符重载+赋值拷贝函数+日期类的书写)

目录 运算符重载运算赋值重载和运算赋重载前置和后置<,<,>,>,,!运算符重载日期类的实现<<流插入和>>流提取的运算符重载总结 运算符重载 C为了增强代码的可读性引入了运算符重载&#xff0c;运算符重载是具有特殊函数名的函数&#xff0c;也具有其 返回…

(最新)华为 2024 届实习招聘-硬件通⽤/单板开发——第十一套和十二套

&#xff08;最新&#xff09;华为 2024 届实习招聘-硬件通⽤/单板开发——第十一套和十二套 部分题目分享&#xff0c;完整版带答案(有答案和解析&#xff0c;答案非官方&#xff0c;未仔细校正&#xff0c;仅供参考&#xff09;&#xff08;共十套&#xff09;获取&#xff…