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.mfbz.cn/a/557555.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数仓建模—物理数据模型

文章目录 数仓建模—物理数据模型什么是物理数据模型物理数据模型示例如何构建物理数据模型物理数据模型与逻辑数据模型逻辑模型和物理模型之间有什么关系逻辑数据模型的好处物理数据模型的好处数仓建模—物理数据模型 前面我们讲了数据模型和逻辑数据模型,你可以参考前面的文…

【JAVA进阶篇教学】第四篇:JDK8中函数式接口

博主打算从0-1讲解下java进阶篇教学&#xff0c;今天教学第四篇&#xff1a;JDK8中函数式接口。 在 Java 8 中&#xff0c;函数式接口是指只包含一个抽象方法的接口。这种接口可以用作 Lambda 表达式的类型&#xff0c;从而使得函数式编程在 Java 中变得更加方便和灵活。下面…

【题解】NC398 腐烂的苹果(多源BFS)

https://www.nowcoder.com/practice/54ab9865ce7a45968b126d6968a77f34?tpId196&tqId40529&ru/exam/oj 从每个腐烂的苹果开始使用广度优先遍历&#xff08;bfs&#xff09; class Solution {int n, m;int dx[4] {0, 0, 1, -1};int dy[4] {1, -1, 0, 0};vector<v…

C++ STL 容器 deque

目录 1. deque 对象1.1 内部表示1.2 底层数据结构1.3 分段连续1.4 重新分配 2. deque 迭代器 本文测试环境 gcc 13.1 1. deque 对象 1.1 内部表示 deque 为我们提供了一个双端队列&#xff0c;即可以在队头进行 push、pop&#xff0c;可以在队尾进行 push、pop deque 容器擅…

电弧的产生机理

目录&#xff1a; 1、起弧机理 2、电弧特点 3、电弧放电特点 4、实际意义 1&#xff09;电力开关装置 2&#xff09;保险丝 1、起弧机理 电弧的本质是一种气体放电现象&#xff0c;可以理解为绝缘情况下产生的高强度瞬时电流。起弧效果如下图所示&#xff1a; 在电场的…

pyplot+pandas实现操作excel及画图

1、安装jupyter lab pip install jupyterlab # 启动 建议在指定的项目文件夹下 开启cmd窗口并执行 jupyter lab 启动后会自动打开浏览器访问 2、安装依赖 pip install matplotlib pip install xlrd pip install pandas 3、读取excel import pandas as pddf pd.read_excel(hi…

一文带你了解什么是国际短信

什么是国际短信&#xff1f; 国际短信&#xff0c;也叫国际短消息&#xff0c;是指中国大陆以外的国家或地区运营商与用户之间发送和接收短信息的业务&#xff0c;是一种实现国际间沟通交流与信息触达的便捷方式&#xff0c;可广泛应用于出海游戏、跨境电商、在线社交、实体零…

「探索C语言内存:动态内存管理解析」

&#x1f320;先赞后看&#xff0c;不足指正!&#x1f320; &#x1f388;这将对我有很大的帮助&#xff01;&#x1f388; &#x1f4dd;所属专栏&#xff1a;C语言知识 &#x1f4dd;阿哇旭的主页&#xff1a;Awas-Home page 目录 引言 1. 静态内存 2. 动态内存 2.1 动态内…

比特币上最有价值的BRC-20,你了解吗?

BRC20 是比特币网络上发行同质化Token 的实验性格式标准&#xff0c;由domodata 于2023 年3 月8 日基于 Ordinal 协议创建。 类似于以太坊的 ERC20 标准&#xff0c;它规定了以太坊上发行 Token 的名称、发行量、转账等功能&#xff0c;所有基于以太坊开发的 Token 合约都遵守这…

计算机视觉——DiffYOLO 改进YOLO与扩散模型的抗噪声目标检测

概述 物体检测技术在图像处理和计算机视觉中发挥着重要作用。其中&#xff0c;YOLO 系列等型号因其高性能和高效率而备受关注。然而&#xff0c;在现实生活中&#xff0c;并非所有数据都是高质量的。在低质量数据集中&#xff0c;更难准确检测物体。为了解决这个问题&#xff…

axios 请求中断和请求重试

请求中断​ 请求已经发出去了&#xff0c;如何取消掉这个已经发出去的请求&#xff1f; 微信扫码体验一下 &#xff08;说不定哪天你就用得上&#xff09; 用途&#xff1a; 比如取消正在下载中的文件点击不同的下拉框选项&#xff0c;向服务器发送新请求但携带不同的参数&…

解决系统报错:此应用无法在你的电脑上运行

在开发过程中不知从何时起&#xff0c;使用电脑时过程中不断的都显示“此应用无法在你的电脑上运行”&#xff0c;让人非常恶心&#xff0c;一直以为是系统误操作了什么或误安了软件 百度的答案就是让你找到报错的软件用兼容模式运行。而我连报错的软件都不知道&#xff0c;让人…

盲人盲杖:科技革新,助力视障人士独立出行

在我们的社会中&#xff0c;盲人朋友们以其坚韧的精神风貌&#xff0c;生动诠释着生活的多样与可能。然而&#xff0c;当我们聚焦于他们的日常出行&#xff0c;那些普通人视为寻常的街道、路口&#xff0c;却成为他们必须面对的严峻挑战。如何切实提升盲人盲杖的功能&#xff0…

怎么检查3d模型里的垃圾文件---模大狮模型网

在处理3D模型时&#xff0c;经常会遇到一些不必要的垃圾文件&#xff0c;它们可能占据硬盘空间&#xff0c;增加文件大小&#xff0c;甚至影响模型的性能和质量。因此&#xff0c;及时检查和清理这些垃圾文件对于优化工作流程和提高效率非常重要。在本文中&#xff0c;我们将介…

利用Python进行大规模数据处理【第173篇—数据处理】

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 利用Python进行大规模数据处理&#xff1a;Hadoop与Spark的对比 随着数据量的不断增长&…

CSS中position属性总结

CSS中position属性的总结 如果我的文章看不懂&#xff0c;不要犹豫&#xff0c;请直接看阮一峰大佬写的文章 https://www.ruanyifeng.com/blog/2019/11/css-position.html 1 干嘛用的 用来定位HTML元素位置的&#xff0c;通过top、bottom、right、left定位元素 分别有这些值&a…

【DM8】ODBC

官网下载ODBC https://www.unixodbc.org/ 上传到linux系统中 /mnt下 [rootstudy ~]#cd /mnt [rootstudy mnt]# tar -zxvf unixODBC-2.3.12.tar.gz [rootstudy mnt]# cd unixODBC-2.3.12/ [rootstudy unixODBC-2.3.12]# ./configure 注意&#xff1a;若是报以上错 则是gcc未安…

双向链表(带头双向循环链表)的实现

前言&#xff1a;前面实现的单向链表&#xff0c;全称是不带头单向不循环链表。这里实现带头双向不循环链表&#xff0c;比单向链表好实现一点。 目录 链表的分类 单向链表与双向链表的比较&#xff1a; 双向链表的节点的定义&#xff1a; 多文件实现&#xff1a; List.h来…

B007-二维数组方法

目录 二维数组一维数组回顾二维数组定义与创建二维数组的遍历二维数组堆栈图特殊的char数组 方法main方法认识自定义方法调用同类中方法调用不同类中方法方法的参数方法的返回值方法签名方法重载 二维数组 一维数组回顾 二维数组定义与创建 二维数组的遍历 /*** 二维数组:* …

230元的通配符证书是最便宜的吗

随着互联网的发展&#xff0c;越来越多的人认为需要保护用户在网站中传输的数据&#xff0c;因此各个数字证书颁发机构颁发各种数字证书来为网站传输信息进行加密。其中通配符SSL证书是比较受欢迎的一款域名数字证书&#xff0c;这款SSL证书可以用一张证书保护主域名以及主域名…
最新文章