【lua学习】3.字符串
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))))