glua_ls 1.0.27

Language server for Garry's Mod Lua (GLua).
Documentation
buffer: |
  字符串缓冲区库允许对类字符串数据进行高性能操作。
  
  与 Lua 字符串(常量)不同,字符串缓冲区是可变的 8 位(二进制透明)字符序列。数据可以存储、格式化并编码到字符串缓冲区中,之后再转换、提取或解码。
  
  便捷的字符串缓冲区 API 简化了常见的字符串处理任务,否则这些任务通常需要创建大量中间字符串。字符串缓冲区通过消除冗余的内存拷贝、对象创建、字符串驻留以及垃圾回收开销来提升性能;与 FFI 库结合时,还能实现零拷贝操作。
  
  字符串缓冲区库还包含一个用于 Lua 对象的高性能序列化器。
  
  ## 流式序列化
  
  在某些场景下,希望对大型数据集进行分片序列化(也称 streaming)。该序列化格式可安全拼接并支持流式:可以把多个编码简单地追加到一个缓冲区,之后再分别解码:
  
  ```lua
  local buf = buffer.new()
  buf:encode(obj1)
  buf:encode(obj2)
  local copy1 = buf:decode()
  local copy2 = buf:decode()
  ```
  
  下面展示如何迭代一个流:
  
  ```lua
  while #buf ~= 0 do
    local obj = buf:decode()
    -- 对 obj 做些处理。
  end
  ```
  
  由于该序列化格式不会在编码前附加长度信息,网络应用可能还需要同时传输长度。
  
  该序列化格式主要供 LuaJIT 应用内部使用。序列化数据向上兼容,并可在所有支持的 LuaJIT 平台间移植。
  
  它是 8 位二进制格式,非人类可读,可能包含嵌入的零字节;并且会原样存储嵌入的 Lua 字符串对象(同样是 8 位透明)。编码后的数据可以安全拼接以用于流式处理,并可在之后按“顶层对象”逐个解码。
  
  该编码相对紧凑,但优化目标是最大性能而非最小空间占用;它也能很好地被常见的按字节数据压缩算法压缩。
  
  尽管本文档给出了说明,但该格式明确不打算作为跨语言结构化数据交换的“公共标准”(如 JSON 或 MessagePack);请不要这样使用。
  
  规范以无上下文文法给出,以顶层对象为起点。备选项以 `|` 分隔,`*` 表示重复。分组是隐式的,或用 `{…}` 表示。终结符要么是以字节编码的十六进制数,要么带有 `.format` 后缀。
  
  ```
  object    → nil | false | true
  ```

buf: |
  缓冲区对象是由垃圾回收管理的 Lua 对象。通过 `buffer.new()` 创建后,它可以(也应该)在多个操作中复用。当最后一个对缓冲区对象的引用消失后,它最终会由垃圾回收器释放,包括其分配的缓冲区空间。
  
  缓冲区以 FIFO(先进先出)数据结构方式工作:数据可以被追加(写入)到缓冲区末尾,也可以从缓冲区头部被消费(读取);这些操作可以任意混合。
  
  用于保存字符的缓冲区空间由系统自动管理——按需增长,并会回收已消费的空间。如需更细粒度的控制,可使用 `buffer.new(size)` 与 `buf:free()`。
  
  单个缓冲区的最大大小与 Lua 字符串的最大大小相同,略低于 2GB。对于超大数据规模,字符串和缓冲区都不是合适的数据结构——应使用 FFI 库直接映射内存或文件,直到操作系统的虚拟内存限制。

string.buffer.data: |
  要写入缓冲区的数据:字符串、数字,或带有 `__tostring` 元方法的对象 `obj`。

buf.put: |
  向缓冲区追加一个字符串 `str`、数字 `num`,或任何带有 `__tostring` 元方法的对象 `obj`。多个参数会按给定顺序依次追加。
  
  将一个缓冲区追加到另一个缓冲区是可行的,并会在内部进行短路处理,但仍然会发生一次拷贝;更好的做法是合并写入,尽量使用同一个缓冲区完成写入。

buf.putf: |
  将格式化后的参数追加到缓冲区。格式字符串支持与 `string.format()` 相同的选项。

buf.putcdata: |
  将 FFI cdata 对象指向的内存中 `len` 个字节追加到缓冲区。该对象需要可转换为(常量)指针。

buf.set: |
  该方法允许将一个字符串或 FFI cdata 对象以“零拷贝”的方式作为缓冲区来消费。它会在缓冲区中保存对传入字符串 `str` 或 FFI cdata 对象的引用,并释放原本为缓冲区分配的任何空间。
  
  调用该方法后,缓冲区的行为等同于调用 `buf:free():put(str)` 或 `buf:free():put(cdata, len)`,但只要缓冲区只被消费(读取),数据仅被引用而不会被复制。
  
  如果之后对缓冲区进行写入,则会把被引用的数据拷贝到内部缓冲区,并移除对象引用(写时复制语义)。
  
  保存的引用会作为垃圾回收器的锚点,确保原本传入的字符串或 FFI cdata 对象保持存活。

buf.reset: |
  重置(清空)缓冲区。已分配的缓冲区空间不会被释放,并可被复用。

buf.free: |
  释放缓冲区对象所占用的缓冲区空间。对象本身仍然存在,处于空状态并可被复用。
  
  注意:通常不需要使用该方法。缓冲区对象被回收时,垃圾回收器会自动释放其缓冲区空间。只有在你需要立即释放关联内存时才应使用此方法。

buf.reserve: |
  `reserve` 方法会在缓冲区中至少预留 `size` 字节的写入空间,并返回一个指向该空间的 `uint8_t *` FFI cdata 指针 `ptr`。
  
  可用长度(字节)会通过 `len` 返回:它至少为 `size`,但为了便于高效扩容,可能会更大。你可以利用这部分额外空间,也可以忽略 `len`,只使用 `size` 字节。
  
  该方法与 `buf:commit()` 配合,可对 C 风格的 read API 实现零拷贝写入:
  
  ```lua
  local MIN_SIZE = 65536
  repeat
    local ptr, len = buf:reserve(MIN_SIZE)
    local n = C.read(fd, ptr, len)
    if n == 0 then break end -- EOF.
    if n < 0 then error("read error") end
    buf:commit(n)
  until false
  ```
  
  预留的写入空间不会被初始化。在调用 `commit` 之前,至少需要把实际使用的 `used` 字节写入该空间。如果没有向缓冲区添加任何内容(例如发生错误),则无需调用 `commit`。

buf.reserve.return.1: |
  指向该空间的 `uint8_t *` FFI cdata 指针。

buf.reserve.return.2: |
  可用长度(字节)。

buf.commit: |
  将先前 `reserve` 返回的写入空间中实际使用的 `used` 字节追加到缓冲区数据中。

buf.skip: |
  从缓冲区中跳过(消费)`len` 个字节,最多到当前缓冲区数据长度为止。

buf.get: |
  消费缓冲区数据并返回一个或多个字符串。多个参数会按给定顺序依次消费缓冲区数据。
  
  - 不带参数调用时,消费整个缓冲区数据。
  - 以数字参数调用时,最多消费 `len` 字节。
  - 参数为 `nil` 时,会消费剩余的缓冲区空间(这通常只应作为最后一个参数使用)。
  
  注意:当长度为 0 或没有剩余缓冲区数据时,返回的是空字符串而不是 `nil`。

buf.tostring: |
  从缓冲区数据创建一个字符串,但不会消费数据;缓冲区保持不变。
  
  缓冲区对象还定义了 `__tostring` 元方法。这意味着缓冲区可以传给全局 `tostring()` 函数,以及许多可以用它代替字符串的函数。在诸如 `io.write()` 之类的函数中,一些关键的内部用法会被短路处理,以避免创建中间字符串对象。

buf.ref: |
  返回一个指向缓冲区数据的 `uint8_t *` FFI cdata 指针 `ptr`,并通过 `len` 返回缓冲区数据的字节长度。
  
  返回的指针可直接传给需要“缓冲区 + 长度”的 C 函数。你也可以对缓冲区数据进行逐字节读取(`local x = ptr[i]`)或写入(`ptr[i] = 0x40`)。
  
  结合 `buf:skip()` 方法,可以对 C 风格的 write API 实现零拷贝写入:
  
  ```lua
  repeat
    local ptr, len = buf:ref()
    if len == 0 then break end
    local n = C.write(fd, ptr, len)
    if n < 0 then error("write error") end
    buf:skip(n)
  until n >= len
  ```
  
  与 Lua 字符串不同,缓冲区数据不会隐式以 0 结尾。把 `ptr` 传给期望以 0 结尾字符串的 C 函数并不安全。如果你不使用 `len`,那很可能是在做错事。

buf.ref.return.1: |
  指向缓冲区数据的 `uint8_t *` FFI cdata 指针。

buf.ref.return.2: |
  缓冲区数据的字节长度。

buf.encode: |
  将 Lua 对象序列化(编码)到缓冲区。
  
  尝试序列化不支持的对象类型、循环引用或嵌套很深的表时,该函数可能会抛出错误。

buf.decode: |
  从缓冲区反序列化(解码)一个对象。
  
  返回的对象可以是任意受支持的 Lua 类型——甚至可以是 `nil`。
  
  当输入的数据格式错误或编码数据不完整时,该函数可能会抛出错误。
  
  解码后会把未消费的剩余数据保留在缓冲区中。
  
  如果尝试反序列化 FFI 类型,而 FFI 库未内建或尚未加载,则会抛出错误。

buffer.encode: |
  序列化(编码)Lua 对象 `obj`。
  
  尝试序列化不支持的对象类型、循环引用或嵌套很深的表时,该函数可能会抛出错误。

buffer.decode: |
  将字符串反序列化(解码)为 Lua 对象。
  
  返回的对象可以是任意受支持的 Lua 类型——甚至可以是 `nil`。
  
  当输入的数据格式错误或编码数据不完整时会抛出错误。
  当解码单个顶层对象后仍有剩余数据时也会抛出错误。
  
  如果尝试反序列化 FFI 类型,而 FFI 库未内建或尚未加载,则会抛出错误。

buffer.new: |
  创建一个新的缓冲区对象。
  
  可选参数 `size` 用于确保最小的初始缓冲区大小。当你预先知道所需缓冲区大小时,这严格来说只是一次优化;无论如何,缓冲区空间都会按需增长。
  
  可选的表参数 `options` 用于设置各种序列化选项。

string.buffer.serialization.opts: |
  序列化选项
  
  传给 `buffer.new()` 的 `options` 表可以包含以下成员(全部可选):
  
  - `dict`:一个 Lua 表,保存一组在你要序列化的对象中经常作为表键出现的字符串字典。序列化时这些键会被紧凑地编码为索引。选择合适的字典可以节省空间并提升序列化性能。
  - `metatable`:一个 Lua 表,保存一组用于你要序列化的表对象的元表字典。
  
  `dict` 需要是字符串数组,`metatable` 需要是表数组;两者都从索引 1 开始且不能有空洞(中间不能出现 nil)。这些表会被锚定在缓冲区对象中,并在内部被改造为双向索引(不要自己这么做,只需传入普通数组)。在把它们传给 `buffer.new()` 之后,这些表不得再被修改。
  
  编码器与解码器使用的 `dict` 与 `metatable` 必须一致。把最常见的条目放在前面,并在末尾扩展以保证向后兼容——这样旧的编码仍然能被读取。你也可以将某些索引设为 false 来显式放弃向后兼容;解码使用了这些索引的旧编码时会抛出错误。
  
  编码时,如果某个元表不在 `metatable` 字典中,则会被忽略;解码会返回一个元表为 nil 的表。
  
  注意:解析并准备 `options` 表的开销相对较高。建议只创建一次缓冲区对象并在多次使用中复用。避免混用编码与解码缓冲区,因为 `buf:set()` 会释放已分配的缓冲区空间:
  
  ```lua
  local options = {
    dict = { "commonly", "used", "string", "keys" },
  }
  local buf_enc = buffer.new(options)
  local buf_dec = buffer.new(options)
  
  local function encode(obj)
    return buf_enc:reset():encode(obj):get()
  end
  
  local function decode(str)
    return buf_dec:set(str):decode()
  end
  ```