Lua 源码学习笔记(2)字符串

Lua 源码学习笔记(2)字符串

参考书籍:《Lua设计与实现》

作者书籍对应Github:https://github.com/lichuang/Lua-Source-Internal

lua string源码分析总结:https://blog.csdn.net/boyxiaolong/article/details/24104543

lua.5.2.3源码阅读(02):TString字符串对象:https://www.cnblogs.com/zerozero/p/4190300.html

  • Lua版本:5.3.5

概述

  • 字符串是被内化(internalization)的一种数据:存放字符串的变量,不是字符串的数据副本,而是整个字符串的引用。每创建一个字符串,都会先查内存有没有相同字符串数据,有的话直接引用,否则创建一份新的。

  • 一旦创建字符串,内容不可变。

  • 改变一个字符串变量不会影响原来的字符串数据:

    a = "1"			-- 变量a指向字符串"1"
    a = a.."2"		-- 变量a指向字符串"12",上面的字符串"1"留着等GC
    

在这里插入图片描述

  • Lua虚拟机使用一个散列桶管理字符串。

优点

  • 传统字符串:
    • 比较:逐位对比,复杂度和字符串长度线性相关
  • Lua字符串:
    • 比较:对比散列值即可。
    • 空间优化,相同字符串只有一个副本。

字符串实现

// lobject.h

/*
** 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;


/*
** Ensures that address after this type is always fully aligned.
*/
typedef union UTString {
  L_Umaxalign dummy;  /* ensures maximum alignment for strings */
  TString tsv;
} UTString;

  • L_Umaxalign:最大对齐量,这里是double,为了CPU读取数据时能更高性能。
// llimits.h

/* type to ensure maximum alignment */
#if defined(LUAI_USER_ALIGNMENT_T)
typedef LUAI_USER_ALIGNMENT_T L_Umaxalign;
#else
typedef union {
  lua_Number n;
  double u;
  void *s;
  lua_Integer i;
  long l;
} L_Umaxalign;
#endif

  • extra:标记为保留字符串,即不会GC阶段被回收,一直保留在系统,一般只有关键字这种。
// lstring.h
// 判断是否是保留字符串

/*
** test whether a string is a reserved word
*/
#define isreserved(s)	((s)->tt == LUA_TSHRSTR && (s)->extra > 0)

Hash

// lstate.h  管理字符串hash值的字符串表

/*
** 'global state', shared by all threads of this state
*/
typedef struct global_State {
  ...
  stringtable strt;  /* hash table for strings */
  ...
}


typedef struct stringtable {
  TString **hash;
  int nuse;  /* number of elements */
  int size;
} stringtable;

  • 创建一个TString的时候,首先根据散列算法算出散列值,这就是strt数组的索引值。如果这里已经有元素,则使用链表串接起来。
  • 当数据量非常大时,分配到每个桶上的数据也会非常多,这样一次查找也退化成了一次线性的查找过程。Lua 中也考虑了这种情况, 所以有一个重新散列( rehash )的过程,这就是当字符串数据非常多时, 会重新分配桶的数量,降低每个桶上分配到的数据数量,这个过程在函数luaS_resize中

创建字符串

// lstring.c

/*
** new string (with explicit length)
*/
TString *luaS_newlstr (lua_State *L, const char *str, size_t l) {
  if (l <= LUAI_MAXSHORTLEN)  /* short string? 见下面*/
    return internshrstr(L, str, l);
  else {
    TString *ts;
    if (l >= (MAX_SIZE - sizeof(TString))/sizeof(char))
      luaM_toobig(L);			// 太大,抛异常
    ts = luaS_createlngstrobj(L, l); 		//创建long字符串
    memcpy(getstr(ts), str, l * sizeof(char));
    return ts;
  }
}
  • 短字符串
// llimits.h

/*
** Maximum length for short strings, that is, strings that are
** internalized. (Cannot be smaller than reserved words or tags for
** metamethods, as these strings must be internalized;
** #("function") = 8, #("__newindex") = 10.)
*/
#if !defined(LUAI_MAXSHORTLEN)
#define LUAI_MAXSHORTLEN	40
#endif

/*
** checks whether short string exists and reuses it or creates a new one
*/
static TString *internshrstr (lua_State *L, const char *str, size_t l) {
  TString *ts;
  global_State *g = G(L);
  unsigned int h = luaS_hash(str, l, g->seed);
  TString **list = &g->strt.hash[lmod(h, g->strt.size)];
  lua_assert(str != NULL);  /* otherwise 'memcmp'/'memcpy' are undefined */
  for (ts = *list; ts != NULL; ts = ts->u.hnext) {
    if (l == ts->shrlen &&
        (memcmp(str, getstr(ts), l * sizeof(char)) == 0)) {
      /* found! */
      if (isdead(g, ts))  /* dead (but not collected yet)? */
        changewhite(ts);  /* resurrect it */
      return ts;
    }
  }
    // 字符串的数量大于桶数量,且桶数量小于 MAX_INT/2,进行翻倍
  if (g->strt.nuse >= g->strt.size && g->strt.size <= MAX_INT/2) {
    luaS_resize(L, g->strt.size * 2);			// 见下面
    list = &g->strt.hash[lmod(h, g->strt.size)];  /* recompute with new size */
  }
  ts = createstrobj(L, l, LUA_TSHRSTR, h);
  memcpy(getstr(ts), str, l * sizeof(char));
  ts->shrlen = cast_byte(l);
  ts->u.hnext = *list;
  *list = ts;
  g->strt.nuse++;
  return ts;
}

/*
** resizes the string table
*/
void luaS_resize (lua_State *L, int newsize) {
  int i;
  stringtable *tb = &G(L)->strt;			// 拿到全局的字符串表
  if (newsize > tb->size) {  /* grow table if needed ,内存不够,重新申请*/
    luaM_reallocvector(L, tb->hash, tb->size, newsize, TString *);
    for (i = tb->size; i < newsize; i++)
      tb->hash[i] = NULL;
  }
  for (i = 0; i < tb->size; i++) {  /* rehash  重新计算Hash */
    TString *p = tb->hash[i];
    tb->hash[i] = NULL;
    while (p) {  /* for each node in the list ,找到Hash链表最后一个为空的,塞进去*/
      TString *hnext = p->u.hnext;  /* save next */
      unsigned int h = lmod(p->hash, newsize);  /* new position */
      p->u.hnext = tb->hash[h];  /* chain it */
      tb->hash[h] = p;
      p = hnext;
    }
  }
  if (newsize < tb->size) {  /* shrink table if needed */
    /* vanishing slice should be empty */
    lua_assert(tb->hash[newsize] == NULL && tb->hash[tb->size - 1] == NULL);
    luaM_reallocvector(L, tb->hash, tb->size, newsize, TString *);
  }
  tb->size = newsize;
}


/*
** creates a new string object
*/
static TString *createstrobj (lua_State *L, size_t l, int tag, unsigned int h) {
  TString *ts;
  GCObject *o;
  size_t totalsize;  /* total size of TString object */
  totalsize = sizelstring(l);
  o = luaC_newobj(L, tag, totalsize);
  ts = gco2ts(o);
  ts->hash = h;
  ts->extra = 0;
  getstr(ts)[l] = '\0';  /* ending 0 */
  return ts;
}


TString *luaS_createlngstrobj (lua_State *L, size_t l) {
  TString *ts = createstrobj(L, l, LUA_TLNGSTR, G(L)->seed);
  ts->u.lnglen = l;
  return ts;
}

luaS_newlstr和luaS_new,实际两个函数只是参数不一样,是否包含’\0’结束符。

创建字符串的过程中,根据字符串的长度,进行不同的处理,长度小于LUAI_MAXSHORTLEN的字符串,

进行hash,重用放置在在一个hash表格中,对于长度大于LUAI_MAXSHORTLEN的则一定创建一个

TString对象,另外一个区别是,GC所挂在的位置不同:一个在global_State的字符串hash表中,另外

一个在global_State的所有GC列表中allgc。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值