Lua源码阅读笔记 -string字符串

TString数据结构

// lobject.h

/*
** String headers for string table
*/
typedef union TString {
  L_Umaxalign dummy;  /* ensures maximum alignment for strings */
  struct {
    CommonHeader;
    lu_byte reserved;
    unsigned int hash;
    size_t len;
  } tsv;
} TString;

上面是lua中字符串的数据结构。可以看到,TString中并没有出现显式的char*变量,而是存储了哈希值和长度。
所以,这里面没有存储字符吗?我们来看一下TString的创建函数。


创建一个TString

先贴一下对应的源码:

// lstring.c

TString *luaS_newlstr (lua_State *L, const char *str, size_t l) {
  GCObject *o;
  unsigned int h = cast(unsigned int, l);  /* seed */
  size_t step = (l>>5)+1;  /* if string is too long, don't hash all its chars */
  size_t l1;
  for (l1=l; l1>=step; l1-=step)  /* compute hash */
    h = h ^ ((h<<5)+(h>>2)+cast(unsigned char, str[l1-1]));
  for (o = G(L)->strt.hash[lmod(h, G(L)->strt.size)];
       o != NULL;
       o = o->gch.next) {
    TString *ts = rawgco2ts(o);
    if (ts->tsv.len == l && (memcmp(str, getstr(ts), l) == 0)) {
      /* string may be dead */
      if (isdead(G(L), o)) changewhite(o);
      return ts;
    }
  }
  return newlstr(L, str, l, h);  /* not found */
}

函数传入字符串str和长度l,首先计算字符串的哈希值h,然后在全局的字符串表中查找,比较字符串长度和内存,如果找到则直接返回已经创建好的字符串,否则新建。

看一下全局字符串表strt的定义:

// lstate.h

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

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

strt定义在全局状态中,hash是一个保存了所有TString的哈希表。遍历这个表可以找到lua中所有的字符串。
hash的查找过程中有一个条件:if (ts->tsv.len == l && (memcmp(str, getstr(ts), l) == 0)),这里先比较了长度,然后又比较了字符串内存,来看一下getstr

// lobject.h

#define getstr(ts)    cast(const char *, (ts) + 1)

这里把str与已创建字符串ts后面的内存进行比较,所以我们可以猜测,TString中也保存了字符串,就在紧邻的内存区域中。

进一步来看一下新建字符串的函数TString *newlstr (lua_State *L, const char *str, size_t l, unsigned int h)

// lstring.c

static TString *newlstr (lua_State *L, const char *str, size_t l,
                                       unsigned int h) {
  TString *ts;
  stringtable *tb;
  if (l+1 > (MAX_SIZET - sizeof(TString))/sizeof(char))
    luaM_toobig(L);
  ts = cast(TString *, luaM_malloc(L, (l+1)*sizeof(char)+sizeof(TString)));
  ts->tsv.len = l;
  ts->tsv.hash = h;
  ts->tsv.marked = luaC_white(G(L));
  ts->tsv.tt = LUA_TSTRING;
  ts->tsv.reserved = 0;
  memcpy(ts+1, str, l*sizeof(char));
  ((char *)(ts+1))[l] = '\0';  /* ending 0 */
  tb = &G(L)->strt;
  h = lmod(h, tb->size);
  ts->tsv.next = tb->hash[h];  /* chain new entry */
  tb->hash[h] = obj2gco(ts);
  tb->nuse++;
  if (tb->nuse > cast(lu_int32, tb->size) && tb->size <= MAX_INT/2)
    luaS_resize(L, tb->size*2);  /* too crowded */
  return ts;
}

字符串表中没有找到时,就会进入这个函数新建字符串。
分配内存时,除了TString的空间,还多分配了l+1字节,用来存储传入的字符串,这里也验证了之前的猜想。TString新建之后,根据字符串哈希值再次计算新的哈希值h,然后插入全局字符串表strt中。可以看到,strt的哈希表与table的哈希表不同:table的哈希表采用的是开放寻址法,而strt的哈希表则用的链地址法。
如果插入后发现全局字符串表太大,就会调用void luaS_resize (lua_State *L, int newsize)重新给哈希表分配内存,空间为之前的两倍,并且把旧哈希表中的数据插入到新哈希表中,然后释放掉之前的内存。


https://blog.csdn.net/fujia_jzyl/article/details/88394343
原创不易,转载请注明出处,谢谢!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值