Lua - lstring

本文介绍了Lua中字符串lstring的实现,包括数据结构和API。Lua字符串分为短字符串和长字符串,短字符串通过hash复用。数据结构包含CommonHeader、shrlen、hash等字段,API包括字符串比较、hash计算、字符串创建等功能。文章详细讨论了字符串的内存管理及hash碰撞处理策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

lstring

lstring实现了Lua对于字符串的管理.

字符串对象的结构体声明在lobject中. Lua中的字符串可以是C语言的'\0'结尾的连续字符数组, 也可以单纯是一块连续内存的内容, 包含任何字节内容.

#define LUA_TSTRING     4
/* Variant tags for strings */
#define LUA_TSHRSTR (LUA_TSTRING | (0 << 4))  /* short strings */
#define LUA_TLNGSTR (LUA_TSTRING | (1 << 4))  /* long strings */

在Lua中, 字符串根据长度LUAI_MAXSHORTLEN( 默认40) 区分长短字符串, 分为LUA_TLNGSTRLUA_TSHRSTR. 对短字符串进行hash后, 保存起来, 进行复用.


数据结构

/*
** Header for string value; string bytes follow the end of this structure
** (aligned according to 'UTString'; see next).
*/
typedef struct TString {
  CommonHeader;
  lu_byte extra;  /* reserved words for short strings; "has hash" for longs */
  lu_byte shrlen;  /* length for short strings */
  unsigned int hash;
  union {
    size_t lnglen;  /* length for long strings */
    struct TString *hnext;  /* linked list for hash table */
  } u;
} TString;
  • CommonHeader
  • extra: luaX_init中使用到这个, 用来表示是关键字类别.
  • shrlen: 短字符串的长度
  • hash: hash值
  • u.lnglen: 长字符串的长度
  • u.hnext: 如果出现hash碰撞, 则将碰撞内容链接到链表内
/*
** Ensures that address after this type is always fully aligned.
*/
typedef union UTString {
  L_Umaxalign dummy;  /* ensures maximum alignment for strings */
  TString tsv;
} UTString;

TString进行内存对齐. L_Umaxalign默认是一个union, 内容和部分union Value一致.


lua_Stateglobal_State

lua_State的l_G中引用了这个lua_State所属的global_State. 在global_State中有几个被用到的域

  TString *strcache[STRCACHE_SIZE][1];  /* cache for strings in API */

strcache是长字符串的hash表, 默认长字符串使用地址对strcache的大小取余来获取存取地址. 可以通过luaS_clearcache来清空.

都是以'\0'结尾的字符串.

  TString *memerrmsg;  /* memory-error message */

memerrmsg是在luaS_init的时候没设置成MEMERRMSG("not enough memory"). 在luaS_init中调用了luaC_fix将这个字符串强制标记为不可被GC回收. 也是所有短字符串的默认值. rehash后

  stringtable strt;  /* hash table for strings */

strt保存hash过的短字符串. 用luaS_remove从中删除字符串对象,


API

  • luaS_eqlngstr比较长字符串, assert同类型, 先看是否指向同一对象, 在比较长度是否相等, 最后利用字符串长度, 用memcmp比较内存内容.

  • luaS_hash计算字符串的hash值. 对于长度小于2^LUAI_HASHLIMIT的字符串, 每字节都参加计算hash(LUAI_HASHLIMIT默认为5). 长度大于32的字符串, 从末尾开始, 每(l >> LUAI_HASHLIMIT) + 1参加计算hash值.

  • 如果长度一样, 但是参加计算的字符也是一样的, 就会增加hash碰撞

  • luaS_resize拓展L->l_G->strt并将拓展部分置NULL. 如果newsize小于strt的长度, 那么就会截取.

  • 所以必须保证, 截取的时候, 截取位置之后没有hash.

  • luaS_clearcache: 如果g->strcache[i][0]white, 则将里面的指向g->memerrmsg

  • 为什么不置为NULL, 而要置为一个不可回收的字符串呢?

  • luaS_init利用luaS_resize初始化L->l_G->strtMINSTRTABSIZE(128)个元素. 将g->memerrmsg置为MEMERRMSG( "not enough memory") , 并使其不可被GC回收. 初始化g->strcache的每个元素指向g->memerrmsg.

  • createstrobj创建字符串对象. 调用到了luaC_newobj, 等看到GC的时候再返回看这个函数.

  • luaS_removeTStringL->l_G->strt中移除, 这个TString需要已经计算出hash值.

  • internshrstr检查短字符串是否在strt内, 如果存在, 则复用, 否则检查是否需要rehash, 最后, 创建新的短字符串对象, 并放入strt中.

  • luaS_newlstr创建字符串对象. 对短字符串进行internshrstr, 对长字符串, 字节创建对象. 长字符串对象的hash是他的长度, 末尾会添加'\0'

  • luaS_new创建或复用'\0'结尾的字符串, 首先在L->l_G->strcache内用地址作为hash键值查找, 看是否有内容相同的'\0'结尾的字符串. 有则复用, 没有则利用luaS_newlstr创建长字符串对象.

  • luaS_newudata和创建userdata相关

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值