当前位置:首页 > 技术 > 正文内容

Lua字符串机制详解

访客 技术 2026年7月16日 1

3.1 核心特性

Lua语言采用不可变字符串模型,当创建新字符串时会生成独立副本,原有数据保持不变。

3.2 内部实现原理

// luaconf.h:595
// 实现8字节对齐机制
typedef union { double u; void *s; long l; } LUAI_USER_ALIGNMENT_T;

// limits.h:47
typedef LUAI_USER_ALIGNMENT_T L_Umaxalign;

// lobject.h:199
typedef union TString {
  L_Umaxalign dummy;  // 确保最大对齐方式
  struct {
    CommonHeader;
    unsigned char isReserved;  // 标识保留字标记
    unsigned int hashValue;
    size_t length;
  } tsv;
} TString;

// lstate.h:68
typedef struct global_State {
  stringtable stringTable;  /* 字符串哈希表 */
  // 其他字段...
} global_State;

// lstate.h:38
typedef struct stringtable {
  GCObject **hashTable;  // 哈希桶数组,采用链式存储结构
  lu_int32 elementCount;  // 元素数量
  int capacity;
} stringtable;

// 动态调整哈希表容量:
void resizeStringTable(lua_State *L, int newCapacity) {
  GCObject **newTable;
  stringtable *table = &G(L)->stringTable;
  
  // 避免GC遍历期间调整
  if (G(L)->gcState == GCSsweepstring) return;
  
  newTable = luaM_newvector(L, newCapacity, GCObject *);
  memset(newTable, 0, newCapacity * sizeof(GCObject*));
  
  // 重哈希操作
  for (int i=0; i<table->capacity; i++) {
    GCObject *current = table->hashTable[i];
    while (current) {
      GCObject *next = current->gch.next;
      unsigned int hash = gco2ts(current)->hashValue;
      int newIndex = lmod(hash, newCapacity);
      current->gch.next = newTable[newIndex];
      newTable[newIndex] = current;
      current = next;
    }
  }
  
  // 释放旧表资源
  luaM_freearray(L, table->hashTable, table->capacity, TString *);
  table->capacity = newCapacity;
  table->hashTable = newTable;
}

系统中存在两个关键调用场景:

// lgc.c:431
void checkTableSizes(lua_State *L) {
  global_State *g = G(L);
  
  // 检查字符串哈希表容量
  if (g->stringTable.elementCount < (g->stringTable.capacity / 4) &&
      g->stringTable.capacity > MINSTRTABSIZE * 2)
    resizeStringTable(L, g->stringTable.capacity / 2);
    
  // 检查缓冲区容量
  if (luaZ_sizebuffer(&g->buff) > LUA_MINBUFFER * 2) {
    size_t newSize = luaZ_sizebuffer(&g->buff) / 2;
    luaZ_resizebuffer(L, &g->buff, newSize);
  }
}

// lstring.c:75
TString *createString(lua_State *L, const char *input, size_t length) {
  GCObject *node;
  unsigned int hashSeed = (unsigned int)length;
  size_t stepSize = (length >> 5) + 1;
  
  // 计算哈希值
  for (size_t i=length; i>=stepSize; i-=stepSize)
    hashSeed ^= ((hashSeed << 5) + (hashSeed >> 2) + (unsigned char)input[i-1]);
  
  // 查找已存在字符串
  for (node = G(L)->stringTable.hashTable[lmod(hashSeed, G(L)->stringTable.capacity)];
       node != NULL;
       node = node->gch.next) {
    TString *existing = rawgco2ts(node);
    if (existing->tsv.length == length && memcmp(input, getstr(existing), length) == 0) {
      if (isdead(G(L), node)) changewhite(node);
      return existing;
    }
  }
  
  // 创建新字符串
  return allocateString(L, input, length, hashSeed);
}

// lstring.c:50
static TString *allocateString(lua_State *L, const char *input, size_t length, unsigned int hash) {
  TString *newString;
  stringtable *table = &G(L)->stringTable;
  
  // 检查内存限制
  if (length + 1 > (MAX_SIZET - sizeof(TString))/sizeof(char))
    luaL_error(L, "memory allocation failed");
  
  // 分配内存
  newString = cast(TString *, luaM_malloc(L, (length+1)*sizeof(char)+sizeof(TString)));
  newString->tsv.length = length;
  newString->tsv.hashValue = hash;
  newString->tsv.marked = luaC_white(G(L));
  newString->tsv.tt = LUA_TSTRING;
  newString->tsv.isReserved = 0;
  
  // 复制字符串内容
  memcpy(newString+1, input, length);
  ((char *)(newString+1))[length] = '\0';
  
  // 插入哈希表
  hash = lmod(hash, table->capacity);
  newString->tsv.next = table->hashTable[hash];
  table->hashTable[hash] = obj2gco(newString);
  table->elementCount++;
  
  // 触发扩容
  if (table->elementCount > (lu_int32)table->capacity && table->capacity <= MAX_INT/2)
    resizeStringTable(L, table->capacity * 2);
  
  return newString;
}

保留字标记字段说明:

// llex.c:37
const char *const reservedKeywords[] = {
    "and", "break", "do", "else", "elseif",
    "end", "false", "for", "function", "if",
    "in", "local", "nil", "not", "or", "repeat",
    "return", "then", "true", "until", "while",
    "..", "...", "==", ">=", "<=", "~=",
    "<number>", "<name>", "<string>", "<eof>",
    NULL
};

// llex.h:24
enum ReservedTokens {
  TK_AND = FIRST_RESERVED, TK_BREAK,
  TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FALSE, TK_FOR, TK_FUNCTION,
  TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, TK_REPEAT,
  TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE,
  TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE, TK_NUMBER,
  TK_NAME, TK_STRING, TK_EOS
};

// lstring.h:20
#define createString(L, s)    createStringImpl(L, s, strlen(s))

// lgc.h:60
#define FIXED_BIT  5

// lstring.h:24
#define markAsReserved(s) l_setbit((s)->tsv.marked, FIXED_BIT)

// llex.h:36
#define RESERVED_COUNT    (cast(int, TK_WHILE-FIRST_RESERVED+1))

// llex.c:64
void initializeKeywords(lua_State *L) {
  int index;
  for (index=0; index<RESERVED_COUNT; index++) {
    TString *keyword = createString(L, reservedKeywords[index]);
    markAsReserved(keyword);
    lua_assert(strlen(reservedKeywords[index])+1 <= TOKEN_LEN);
    keyword->tsv.isReserved = cast_byte(index+1);
  }
}

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。