本文主要源码分析基于Lua5.3.1.
之前已经做过Lua内部类型的分析。
在上文中,已经分析了LUA的数据类型抽象。
本文就来分析下Lua语言是如何利用这些抽象的数据类型来保存字符串类型。
lua中关于字符串的操作都放到了如下两个文件中:
- lstring.h
- lstring.c
lua中对于字符串类型的定义放在lobject.h中。
1.1 TString定义
先看下TString定义:
1 | C复制代码 |
从注释可以看出TString可以表示两种字符串:
- short string
- long string
1.2 创建TString对象
上篇文章中提到,对于GCObject
都会调用一个工厂函数:luaC_newobj (lua_State *L, int tt, size_t sz)
。
1 | C复制代码/* |
每次存放LUA字符串的变量,实际上存放的并不是一个真正的字符串数据, 而是一个字符串的引用。比如如下代码:
1 | lua复制代码a = "str1" |
其实a和b引用的是同一份数据。
在lua内部有一个大的hash表,里面存放了所有了字符串。
如下图所示:
这个结构在LUA虚拟机中叫做stringtable
。
定义如下:
1 | C复制代码typedef struct stringtable { |
这个结构保存在global_State
中。
下面看看LUA虚拟机已启动的时候,是怎么初始化的。
在LUA程序开始执行一个LUA脚本的时候,其实都会调用一个函数f_luaopen
.在这个函数中会调用luaS_init(L);
这个函数,这个函数其实就是对上文提到的hashtable
进行初始化。
1 | C复制代码/* |
看到一上来,就调用了一个叫做luaS_resize
的函数,从这个函数的名字就能看出来,这个函数两种场景会调用:
- 场景1:初始调用(我们现在代码看到的)
- 场景2:当hash桶太小,而元素太多,导致数据寻找渐渐的退化为链表搜索的时候;(这个调用时机我们待会看)
场景1:这个函数其实就是按照MINSTRTABSIZE
的大小来初始化hash桶。
下面来看看这个函数怎么实现的:
1 | C复制代码/* |
3.1 Lua中字符串的定义
在lua虚拟机里,字符串类型对象的数据结构做了封装和抽象,一般结构UTString
是这样的:
1 | C复制代码/* |
而一个TString
的定义如下:
1 | C复制代码/* |
字符的内容都保存在TString
的后面。最终使用\0
结尾。
大致结构如下:
3.2 Lua中字符串的创建
在lua虚拟机里面创建一个字符串调用的函数是luaS_newlstr
1 | C复制代码/* |
看出字符串分为两种,小于等于LUAI_MAXSHORTLEN
称为短字符串,否则称为长字符串。
对于短字符串来说,调用的是函数internshrstr
.看下实现:
1 | C复制代码/* |
如果当前hash桶中没有找到这个字符串,就需要创建一个新的。
如果是长字符串,LUA的处理逻辑其实是和短字符串处理差不多的,只是两个Obj的tag不同而已。
1 | C复制代码#include <stdio.h> |
本文转载自: 掘金