【lua学习】3.字符串

【lua学习】3.字符串

  • Lua字符串的概况
  • 字符串实现
    • 字符串结构TString
    • 全局字符串表stringtable
    • 新建字符串luaS_newlstr (先查表,再决定创建与否)
    • 新建字符串 newlstr
    • 重新设置全局字符串的大小 luaS_resize
    • 全局字符串表的缩容
    • 保留字是如何不被回收的

Lua字符串的概况

  • Lua虚拟机中存在一个散列桶结构的全局字符串表来存放所有字符串
  • 关于比较字符串:先比较hash,再比较长度,再逐字符比较。节省时间
  • 同一个字符串在Lua虚拟机中仅有一个副本。节省空间
  • 一旦创建则无法变更
  • 变量存放的仅是字符串的引用

字符串实现

字符串结构TString

(lobject.h) TString

typedef union TString {L_Umaxalign dummy;//保证最大对其//见下文struct {CommonHeader;lu_byte reserved;//当>0时,其值-1表示保留字列表中的索引//见下文unsigned int hash;//字符串散列值,根据字符串长度和部分字符计算而来的值,见下文size_t len;//字符串长度} tsv;
} TString;

(llimits.h) L_Umaxalign

typedef LUAI_USER_ALIGNMENT_T L_Umaxalign;//LUAI_USER_ALIGNMENT_T见下文

(luaconf.h) LUAI_USER_ALIGNMENT_T

//看此定义,为8字节对齐
#define LUAI_USER_ALIGNMENT_T union { double u; void* s; long l; }

全局字符串表stringtable

(lstate.h) stringtable

typedef struct stringtable {GCObject** hash;//开散列结构(和lua table的闭散列结构是有区别的),指向一个数组,每个元素是桶(GCObject*类型),桶管理GCObject链表lu_int32 nuse;//存储的字符串数量int size;//全局字符串表的最大容量(hash桶的最大数量)
} stringtable;

新建字符串luaS_newlstr (先查表,再决定创建与否)

(lstring.c) luaS_newlstr

TString* luaS_newlstr(lua_State* L, const char *str, size_t len)
{//初始h值就是字符串的长度unsigned int h = cast(unsigned int, len);//cast就是强制转型,见下文//获得计算hash值的跨度,如果字符串很长,若逐位计算肯定非常消耗性能size_t step = (len>>5) + 1;//从最后一个字符开始,计算h值,跟后续计算的值执行异或,进而得到最终的h值for (size_t l1 = len; l1 >= step; l1 -= step){h ^= ((h<<5)+(h>>2)+cast(unsigned char, str[l1-1]));}//h值对全局字符串表的最大桶数量求余,得到桶的索引unsigned int bucket_index = lmod(h, G(L)->strt.size);//lmod见下文,G见下文//遍历该桶管理的链表,查找有没有相等的字符串for (GCObject* gco = G(L)->strt.hash[bucket_index];gco != NULL;gco = gco->gch.next){TString* ts = rawgco2ts(gco);//rawgco2ts见下文if (ts->tsv.len == len//先比较长度&& (memcmp(str, getstr(ts), len) == 0))//再逐位比较。getstr见下文{if (isdead(G(L), gco))//若要被GC,则把标记标为另一种白色,防止被GC。isdead见下文{changewhite(gco);}//找到了,就不需要新建了,直接返回即可return ts;}}//新建字符串return newlstr(L, str, len, h);
}

(llimits.h) cast宏

#define cast(t, exp) ((t)(exp))

(lobject.h) lmod宏

//针对size为2次幂的 优化的 取模算法
#define lmod(s,size) \(check_exp((size&(size-1))==0, (cast(int, (s) & ((size)-1)))))

(lstate.h) G宏

#define G(L) (L->l_G)

(lstate.h) rawgco2ts宏

//根据GCObject*获取TString*
#define rawgco2ts(gco) check_exp((gco)->gch.tt == LUA_TSTRING, &((gco)->ts))

(lobject.h) getstr宏

//根据TString* 获取 字符串的首地址,注意:字符串首地址并不在TString内部,而在TString对象最后一个字节的下一个字节,这也解释了为何TString一定要对齐,就是为了提高CPU读取性能
#define getstr(ts) cast(const char *, (ts) + 1)

(lgc.h) isdead宏

//判断是否在当前GC阶段被判定为需要回收,todo以后讨论
#define isdead(g, gco) ((gco)->gch.marked & otherwhite(g) & WHITEBITS)

(lgc.h) changewhite宏

//改变GCObject的当前白色标记,todo以后讨论
#define changewhite(gco) ((gco)->gch.marked ^= WHITEBITS)

新建字符串 newlstr

(lstring.c) newlstr

static TString* newlstr(lua_State* L, const char* str, size_t len, unsigned int h)
{//若字符串太长,则luaM_toobig。luaM_toobig见下文if (len + 1 > (MAX_SIZE - sizeof(TString)/sizeof(char)){luaM_toobig(L);}//为TString对象分配连续的空间,这个空间首部是TString结构,后面紧接着是字符串实际内容TString* ts = cast(TString*, luaM_malloc(L, (len + 1)*sizeof(char) + sizeof(TString)));ts.tsv.len = len;ts.tsv.hash = h;ts.tsv.marked = luaC_white(G(L));ts.tsv.reserved = 0;//获取字符串内容的首地址pstrchar* pstr = (char*)(ts + 1);//拷贝str到pstrmemcpy(pstr , str, len * sizeof(char));//字符串最后一个字符当然是'\0'pstr[len] = '\0';//获取全局字符串表stringtable* tb = &G(L)->strt;//计算桶索引h = lmod(h, tb->size);//用头插法将字符串插入桶中ts->tsv.next = tb->hash[h];tb->hash[h] = obj2gco(ts);//obj2gco见下文//全局字符串表的字符串数量+1tb->nuse++;//若字符串总数 超过了 全局字符串表的最大桶数 且 最大桶数 <= MAX_INT/2,则对全局字符串表扩容if (tb->nuse > cast(lu_int32, tb->size) && tb->size <= MAX_INT/2){luaS_resize(L, tb->size*2);//luaS_resize,重新分配全局字符串表的大小,见下文}
}

(llimits.h) MAX_SIZET宏

#define MAX_SIZET ((size_t)(~(size_t)0)-2)

(lmem.c) luaM_toobig报告要分配的内存过大

void* luaM_toobig(lua_State* L)
{//luaG_runerror, todo后面讨论luaG_runerror(L, "memory allocation error: blobk too big");return NULL;
}

(lmem.h) luaM_malloc宏

//请求分配needbytes字节内存,luaM_realloc_见下文
#define luaM_malloc(L, bytes_to_allocate) luaM_realloc_(L, NULL, 0, (bytes_to_allocate))

(lmem.c) luaM_realloc_分配内存

void* luaM_relloc_(lua_State* L, void* address_to_free, size_t bytes_to_free, size_t bytes_to_allocate)
{lua_assert((bytes_to_free==0)==(address_to_free==NULL));//#llimits.h中define lua_assert(c) ((void)0) 什么也不做,所以忽略global_State* g = G(L);//调用全局表的内存分配函数void* address_to_allocate = (*g->frealloc)(g->ud, address_to_free, bytes_to_free, bytes_to_allocate);//若分配失败且需要的字节数>0,抛出内存分配错误if (address_to_allocate == NULL && bytes_to_allocate> 0){//luaD_throw,todo后面讨论luaD_throw(L, LUA_ERRMEM);//lua.h中#define LUA_ERRMEM	4}lua_assert((bytes_to_allocate==0)==(address_to_allocate==NULL));//分配的内存总字节数 发生变化g->totalbytes += bytes_to_allocate - bytes_to_free;return address_to_allocate;
}

(lgc.h) luaC_white获取当前GC白色

//获取当前gc的白色,todo后面讨论
#define luaC_white(g) cast(lu_byte, (g)->currentwhite & WHITEBITS)

(lstate.h) obj2gco宏

//将对象指针强制转为GCObject*
#define obj2gco(v) (cast(GCObject *, (v)))

(llimits.h) MAX_INT宏

#define MAX_INT (INT_MAX-2)

重新设置全局字符串的大小 luaS_resize

(lstring.c) luaS_resize

void luaS_resize(lua_State* L, int newsize)
{//若GC正处于扫描字符串阶段,则不处理。GCSweepingstring见下文if (G(L)->gcstate == GCSweepingstring){return;}//新分配hash结构GCObject** newhash = luaM_newvector(L, newsize, GCObject*);//初始化每个桶为空指针for (int i = 0; i < newsize; i++){newhash[i] = NULL;}//获取全局字符串表指针stringtable* tb = &G(L)->strt;//遍历每个桶,遍历每个桶管理的链表,全部夺舍到新的hash结构中for (int i = 0; i < tb.size; i+){GCObject* p = tb->hash[i];//遍历桶管理的链表while(p){//以next指向下一个元素GCObject* next = p->gch.next;//根据的hash计算新的 hashunsigned int oldh = gco2ts(p)->hash;int newh = lmod(oldh, newsize);lua_assert(cast_int(oldh%newsize)==lmod(oldh,newsize))//用头插法将元素加入桶管理的链表p->gch.next = newhash[newh];newhash[newh] = p;//p设为next,以便循环的下一轮p = next;}}//释放旧的hash结构luaM_freearray(L, tb->hash, tb_size, TString*);//更新全局字符串表tb->size = newsize;tb->hash = newhash;
}

(lgc.h) GCSsweepstring宏

//gc的几个阶段,todo后面再说
#define GCSpause	0
#define GCSpropagate	1
#define GCSsweepstring	2
#define GCSsweep	3
#define GCSfinalize	4

(lmem.h) luaM_newvector宏

//分配count个类型为datatype的连续内存空间,获得的数据强制转型为datatype*
#define luaM_newvector(L,count_to_allocate,datatype) \cast(datatype*, luaM_reallocv(L, NULL, 0, count_to_allocate, sizeof(datatype)))

(lmem.h) luaM_reallocv宏

//分配count个singlebytes大小的连续内存空间,若空间足够则分配,否则报错
#define luaM_reallocv(L,address_to_free,count_to_free,count_to_allocate,singlebytes) \((cast(size_t, (count)+1) <= MAX_SIZET/(singlebytes)) ? luaM_realloc_(L, (address_to_free), (count_to_free)*(singlebytes), (count_to_allocate)*(singlebytes)) : \luaM_toobig(L))

(lmem.h) luaM_freearray宏

//释放address_to_free处的count_to_free个datatype类型的连续内存空间
#define luaM_freearray(L, address_to_free, count_to_free, datatype)   luaM_reallocv(L, (address_to_free), count_to_free, 0, sizeof(datatype))

全局字符串表的缩容

  • 缩容的时机:垃圾回收的GCSweep阶段
  • 缩容的原则:全局字符串表的字符串总数<桶的最大数量 且 桶的最大数量>MINSTRTABSIZE*2 (llimits.h中#define MINSTRTABSIZE 32)

(lgc.c) 看checkSize

static void checkSize(lua_State* L)
{global_State* g = G(L);//当全局字符串表的字符串总数小于桶最大数量的四分之一  且  桶的最大数量大于MINSTRTABSIZE*2, 则缩容if (g-<strt.nuse < cast(lu_int32, g->strt.size/4)&& g->strt.size > MINSTRTABSIZE*2){luaS_resize(L, g->strt.size/2);}//...无关内容省略
}

(lgc.c) 看singlestep

//一次单步GC,todo后面再说
static l_mem singlestep(lua_State* L)
{global_State* g = G(L);switch(g->gcstate){//...无关内容省略case GCSweep:{lu_mem old = g->totalbytes;g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX);if (*g->sweepgc == NULL){//包含有全局字符串表缩容的操作checkSizes(L);g->gcstate = GCSfinalize;}lua_assert(old >= g->totalbytes);g->estimate -= old - g->totalbytes;return GCSWEEPMAX*GCSWEEPCOST;}//...无关内容省略}//...无关内容省略
}

保留字是如何不被回收的

  • 不被回收的原则:被luaS_fix操作,其tsv.marked被改成了FIXEDBIT

(llex.c) luaX_init

void luaX_init(lua_State* L)
{for (int i=0; i<NUM_RESERVED; i++)//NUM_RESERVED见下文{//尝试新建每个保留字字符串TString* ts = luaS_new(L, luaX_tokens[i]);//luaS_new luaX_tokens 见下文//标记不会被GC,修改ts->tsv.marked为FIXEDBITluaS_fix(ts);//luaS_fix见下文lua_assert(strlen(luaX_tokens[i]) + 1 <= TOKEN_LEN);//TOKEN_LEN见下文//记录在保留字数组的索引+1值ts->tsv.reserved = cast_byte(i + 1);}
}

(llex.h) NUM_RESERVED宏

//表示多少个保留字
//TK_WHILE FIRST_RESERVED 见下文
#define NUM_RESERVED (cast(int, TK_WHILE-FIRST_RESERVED+1))

(llex.h) RESERVED 枚举

enum RESERVED {/* terminal symbols denoted by reserved words */TK_AND = FIRST_RESERVED, TK_BREAK,TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FALSE, TK_FOR, TK_FUNCTION,TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, TK_REPEAT,TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE,/* other terminal symbols */TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE, TK_NUMBER,TK_NAME, TK_STRING, TK_EOS
};

(lstring.h) luaS_new宏

//尝试新建一个字符串
#define luaS_new(L, s)	(luaS_newlstr(L, s, strlen(s)))

(llex.c) luaX_tokens

//终结符数组
const char *const luaX_tokens [] = {"and", "break", "do", "else", "elseif","end", "false", "for", "function", "if","in", "local", "nil", "not", "or", "repeat","return", "then", "true", "until", "while","..", "...", "==", ">=", "<=", "~=","<number>", "<name>", "<string>", "<eof>",NULL
};

(lstring.h) luaS_fix宏

//设置字符串不会被GC
//l_setbit FIXEDBIT见下文
#define luaS_fix(s)	l_setbit((s)->tsv.marked, FIXEDBIT)

(lgc.h) l_setbit宏

//x与m求或
#define setbits(x,m) ((x) |= (m))
//求2的b-1次方,也就是第b位为1,其余为0
#define bitmask(b)	(1<<(b))
//将b1和b2位设为1,其余为0
#define bit2mask(b1,b2)	(bitmask(b1) | bitmask(b2))
//将x的第b位置为1
#define l_setbit(x,b)	setbits(x, bitmask(b))

(lgc.h) FIXEDBIT宏

//todo后面再说
#define WHITE0BIT	0
#define WHITE1BIT	1
#define BLACKBIT	2
#define FINALIZEDBIT	3
#define KEYWEAKBIT	3
#define VALUEWEAKBIT	4
#define FIXEDBIT	5
#define SFIXEDBIT	6
#define WHITEBITS	bit2mask(WHITE0BIT, WHITE1BIT)

(llex.h) TOKEN_LEN宏

//保留字选function为最长
#define TOKEN_LEN	(sizeof("function")/sizeof(char))

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

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

相关文章

【lua学习】4.表

1 概述2 数据结构2.1.表Table2.2 键TKey2.3 节点&#xff08;键值对&#xff09;Node3 操作算法3.1 查找3.1.1 通用查找luaH_get3.1.2 根据字符串查找 luaH_getstr3.1.3 根据整数查找 luaH_getnum3.2 新增元素/修改元素/删除元素 luaH_set系列3.2.1 根据key获取或创建一个value…

批量提取文件创建时间_批量采集新浪微博用户内容

有时我们需要把某些用户的微博数据全部采集下来用作分析&#xff0c;每条信息复制的工作量是非常低效的&#xff0c;必须要借助工具。今天给大家介绍一款采集软件&#xff1a;微风采集器。打开软件&#xff0c;选择模板&#xff0c;下拉框选&#xff1a;批量提取指定用户微博内…

C++异常的规则

点击蓝字关注我们异常是指存在于程序运行时的异常行为&#xff0c;这些行为超出了函数正常功能的范围&#xff0c;当程序的某部分检测到一个无法处理的问题时&#xff0c;就需要用到异常处理。1. C语言中传统的处理错误方式终止程序&#xff1a;如assert&#xff0c;当发生错误…

java中集合选取怎么选_集合中的可选

java中集合选取怎么选有时有人认为Optional类型值得在集合中使用。 据称&#xff0c;它解决了以下问题&#xff1a; HashMap在没有键映射以及值null映射到键的情况下返回null 。 如果使用Map<Optional<Something>>则可以清楚地区分缺少的映射和缺少的值。 这样一来…

【lua学习】7.环境

1 最重要的两个数据结构1.1 lua_State(Lua虚拟机/Lua协程)1.2 global_State(Lua全局状态)2 环境相关的变量2.1 Global表2.1.1 Global表在lua_State结构中2.1.2 Global表在 f_luaopen 时被初始化2.2 env表2.2.1 env表在Closure结构中2.2.2 查找一个全局变量<>在当前函数的…

异质性查询需要为连线设定_振奋人心!华东理工大学开发新型的荧光染料,为细胞成像奠定基础...

结合并激活荧光染料的适体荧光RNA(FR)已用于对丰富的细胞RNA种类进行成像。然而&#xff0c;诸如低亮度和具有不同光谱特性的染料/适体组合的有限可用性的局限性&#xff0c;限制了这些工具在活的哺乳动物细胞和体内的使用。最近&#xff0c;华东理工大学朱麟勇及杨弋共同通讯在…

C++ STL详解(1)

点击蓝字关注我们概述STL 是“Standard Template Library”的缩写&#xff0c;中文译为“标准模板库”。STL 是 C 标准库的一部分&#xff0c;不用单独安装。C 对模板&#xff08;Template&#xff09;支持得很好&#xff0c;STL 就是借助模板把常用的数据结构及其算法都实现了…

jooq 配置oracle_jOOQ配置

jooq 配置oracle本文是我们学院课程“ jOOQ –类型安全数据库查询”的一部分 。 在SQL和特定关系数据库很重要的Java应用程序中&#xff0c;jOOQ是一个不错的选择。 当JPA / Hibernate抽象过多而JDBC过于抽象时&#xff0c;这是一种替代方法。 它显示了一种现代的领域特定语言…

各种说明方法的答题格式_高中化学:选择题答题方法与知识点总结,让你轻松秒杀各种难题...

选择题是化学考试中被广泛采用的一种题型。它具有知识容量大&#xff0c;覆盖面广&#xff0c;构思新颖、灵活巧妙&#xff0c;考试的客观性强&#xff0c;答题简单&#xff0c;评分容易、客观准确等优点。 选择题按考查形式可分为三种类型&#xff0c;即&#xff1a;常规型选择…

C++ STL详解(2)

点击蓝字关注我们来源自网络&#xff0c;侵删刷题时常用的STLstring之前写过一篇 string 的简介但是不是特别全面&#xff0c;这里再补充说明一下。size()返回字符串中字符的数量#include<iostream> #include<string>using namespace std;int main() {string str …

【lua学习】5.栈和lua_State

1 背景2 栈&#xff08;寄存器数组&#xff09;&#xff0c;虚拟机&#xff0c;全局状态机2.1 栈定义在lua_State结构体中2.2 global_State 全局状态机2.3 lua_newstate 主虚拟机和全局状态机的创建2.4 lua_close 关闭虚拟机3 栈上的地址3.1 假索引3.2 根据数字索引获取栈上的地…

python中反向切片用法_使用Python中的切片[:0:-1]反转列表

举个例子a [1, 2, 3, 4, 4, 5, 6, 9]如果你试着用正指数分割它newa a[1:5]这将导致newa [2, 3, 4, 4]这是因为&#xff0c;在上面这种情况下&#xff0c;切片是这样发生的&#xff0c;[包含&#xff1a;排他的]&#xff0c;包括第一个索引&#xff0c;切片从这个索引开始&am…

斐波那契数列的四种实现方式(C语言)

点击蓝字关注我们来源自网络&#xff0c;侵删斐波那契数列是一组第一位和第二位为1&#xff0c;从第三位开始&#xff0c;后一位是前两位和的一组递增数列&#xff0c;像这样的&#xff1a;0、1、1、2、3、5、8、13、21、34、55......今天&#xff0c;我们用四种方式来进行实现…

【lua学习】6.函数,闭包,错误处理

1 数据结构和宏1.1 Closure 闭包1.2 Proto 函数原型1.3 UpVal 外部局部变量(upvalue)1.4 LocVar 局部变量信息1.5 SParser 语法分析所需要的结构1.6 Zio 读写流对象1.7 Mbuffer 缓冲对象1.8 lua_Debug 调试信息1.9 CallInfo 函数调用信息1.10 lua_longjmp 跳转信息1.11 虚拟机状…

cdi 2.7.5_看一下CDI 2.0 EDR1

cdi 2.7.5CDI是最近对Java EE最好的补充之一。 该观点在用户和集成商之间广泛共享。 当前版本的CDI 1.2于2014年4月发布。现在&#xff0c;在2015年中期&#xff0c;我们将面对CDI 2.0规范的早期草案。 CDI 2.0将在Java 8和更高版本上运行。 最终版本计划于2016年发布&#xf…

linux make命令_第一章 1.3Linux下安装Redis

1.3.2 Linux下安装Redis第一步: 去官网下载安装包 ,传送门第二步: 上传到Linux服务器,解压redis的安装包tar -zxvf redis-6.0.8.tar.gz这里我已经解压好了,并且移动到了redis目录下第三步: 安装基本环境yum -y insatll gcc-c这里注意一个问题,Centos下安装的gcc默认版本为4.8.5…

C语言strcpy函数的使用

点击蓝字关注我们strcpy简单使用&#xff1a; #include <stdio.h> #include <string.h>struct Student {int sid;char name[20];int age;} st; //此处分号不可省略int main(void) {struct Student st {1000,"zhangsan",20};printf("%d %s %d\n&…

jooq_使用jOOQ DSL

jooq本文是我们学院课程“ jOOQ –类型安全数据库查询”的一部分 。 在SQL和特定关系数据库很重要的Java应用程序中&#xff0c;jOOQ是一个不错的选择。 当JPA / Hibernate抽象过多而JDBC过于抽象时&#xff0c;这是一种替代方法。 它显示了一种现代的领域特定语言如何可以极大…

什么镜头最适合拍风景_为什么您的风景摄影套件中应始终装有远摄镜头

当您考虑风景摄影镜头时&#xff0c;许多摄影师只考虑广角镜头。14-24mm f / 2.8或24-70mm f / 2.8等经典镜头是用于风景的流行光学元件。它们是绝佳的选择但是&#xff0c;也值得在风景摄影套件中使用长焦镜头。为什么要使用长镜头进行风景摄影&#xff1f;您在网上或浏览Inst…

C++ STL详解(3)

点击蓝字关注我们简介set 是 关联容器 的一种&#xff0c;是排序好的集合&#xff08;元素已经进行了排序&#xff09;。set 和 multiset 类似&#xff0c;它和 multiset 的差别在于 set 中不能有重复的元素。multiset 的成员函数 set 中也都有。使用 set 必须包含 #include<…