当前位置 博文首页 > TTc 's share:Lua 5.3 源码分析(五)字符串 TString

    TTc 's share:Lua 5.3 源码分析(五)字符串 TString

    作者:[db:作者] 时间:2021-07-19 13:30

    # Lua 5.3 源码分析 (五) 字符串 TString

    typedef struct TString {
      CommonHeader;
      lu_byte extra;  /* reserved words for short strings; "has hash" for longs */
      unsigned int hash;
      size_t len;  /* number of characters in string */
      struct TString *hnext;  /* linked list for hash table */
    } TString;
    
    typedef union UTString {
      L_Umaxalign dummy;  /* ensures maximum alignment for strings */
      TString tsv;
    } UTS
    

    TSTRING 类型有 短字符串与长字符串之分。LUA-TSHRSTR(短字符串 ( 0x100 | 0x0 = 0x4 = 4)) ;LUA-TLNGSTR(长字符串0x100 | 0x10000 = 0x10100 = 20); 根据字符串的长度(luaconf.h中的LUAI-MAXSHORTLEN,默认为40)的不同来区别。
    1. 其中 extra 字段 在短字符串中 如果 extra >0,则表示这是一个系统保留的关键字,extra的值直接对应着词法分析时的一个token值,这样可以加速词法分析的速度,同时也保证不被GC 回收; 对于长字符串,一般很少做索引或者比较,所以长字符串直接链接到allgc 链表上 做GC 对象来处理。Lua不会对新创建的长字符串对象计算哈希值,也不保证长字符串对象的唯一性。当长字符串需要被用来当作索引时,会为其计算一次哈希值,并使用extra来记录是否已经为其计算了哈希值。
    2. hash字段则是用存储在全局字符串池里的哈希值;
    3. len表示长度,lua的字符串 不同于 C 字符串 (并不以0结尾),所以需要记录长度信息;
    4. hnext是用来把全局TString串起来,整个链表就是字符串池;

    对于短字符串,在实际使用中一般用来作为索引或者需要进行字符串比较。不同于其他的对象,Lua并不是将其连接到全局的allgc对象链表上,而是将其放到全局状态global_State中的字符串表中进行管理。

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

    这个字符串表是一个stringtable类型的全局唯一的哈希表。当需要创建一个短字符串对象时,会首先在这个表中查找已有对象。所有的短字符串都是全局唯一的,不会存在两个相同的短字符串对象。如果需要比较两个短字符串是否相等,只需要看他们指向的是否是同一个TString对象就可以了,速度非常快。

    当一个字符串被放入到字符串表 stringtable 的时候,需要先检查一下表中是否已经存在相同的字符。如果没有则创建一个新的。

    这个哈希表是开散列实现(又称为链地址法)。当碰到哈希值相同的字符串,只需要链到同一个哈希位的链表上。

    Lua 的GC 是分步完成的,这里需要检查 stringtable 表中的字符串是否为 死掉的字符串。 向 stringtable 中添加新字符串在任何步骤之间都可能发生。 有可能在标记完字符串后发现有些字符串没有任何引用,但在下一个步骤中又产生了相同的字符串导致这个字符串激活。

    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)];
      for (ts = *list; ts != NULL; ts = ts->hnext) {
    if (l == ts->len &&
    (memcmp(str, getstr(ts), l * sizeof(char)) == 0)) {
      /* found! */
      if (isdead(g, ts))  /* dead (but not collected yet)? */
    changewhite(ts);  /* resurrect it */
      return ts;
    }
      }
      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, str, l, LUA_TSHRSTR, h);
      ts->hnext = *list;
      *list = ts;
      g->strt.nuse++;
      return ts;
    }
    

    当stringtable->nuse 域 超过了预定容量(stringtable->size 域)时,说明hash 冲突必然发生。 需要调用 luaS_resize 函数将 stringtable 的哈希链表数组扩大,重新排列所有字符串的位置。

    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 */
    TString *p = tb->hash[i];
    tb->hash[i] = NULL;
    while (p) {  /* for each node in the list */
      TString *hnext = p->hnext;  /* save next */
      unsigned int h = lmod(p->hash, newsize);  /* new position */
      p->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;
    }
    

    字符串比较操作

    对于 LUA_TSHRSTR
    define eqshrstr(a,b)    check_exp((a)->tt == LUA_TSHRSTR, (a) == (b))
    
    对于长字符串比较,先比较字符串长度。当长度相同的时候,需要逐个字节比较。
    int luaS_eqlngstr (TString *a, TString *b) {
      size_t len = a->len;
      lua_assert(a->tt == LUA_TLNGSTR && b->tt == LUA_TLNGSTR);
      return (a == b) ||  /* same instance or... */
    ((len == b->len) &&  /* equal length and ... */
     (memcmp(getstr(a), getstr(b), len) == 0));  /* equal contents */
    }
    
    cs
    下一篇:没有了