Skip to content

feat(encode): support LuaJIT int64_t/uint64_t cdata in qjson.encode #107

@membphis

Description

@membphis

背景

本 issue 从 #106 拆出。#106 明确将 int64/uint64 cdata 编码(原 D02~D05)划为不覆盖项,单独在此跟踪。

OpenResty / LuaJIT 生态中 64 位整数 cdata 非常普遍:

  • 数据库驱动(如 lua-resty-mysql)返回的 BIGINT 字段;
  • snowflake / 雪花算法等大整数 ID;
  • 纳秒时间戳、大计数器。

LuaJIT 的 Lua number 是 double,超过 2^53 即丢精度,因此业界用 int64_t / uint64_t
cdata 承载大整数。无损处理大整数也是 qjson 写在 CLAUDE.md 里的定位卖点之一。

当前缺口(实测): qjson.encode 对任意 cdata 一律拒绝
qjson.encode: unsupported value type: cdata)。这使得「decode 侧用 doc:get_i64()
取到无损 int64 cdata -> 放进 table -> 再 encode 出去」这条闭环断裂。lua-cjson 同样拒绝一切
cdata(Cannot serialise cdata: type not supported),因此这是 qjson 在 cjson 之上的
差异化扩展,不破坏任何 cjson 兼容场景。

目标行为

ID 输入 当前 目标输出
D02 encode(9007199254740993LL) 拒绝 9007199254740993
D03 encode(18446744073709551615ULL) 拒绝 18446744073709551615
D04 encode({x=9007199254740993LL}) 拒绝 {"x":9007199254740993}
D05 encode({x=18446744073709551615ULL}) 拒绝 {"x":18446744073709551615}

输出为裸十进制整数(合法 JSON number)。

实现要点(基于实测)

  • 检测ffi.istype("int64_t", v) / ffi.istype("uint64_t", v)。实测二者可区分有/无符号,
    且对 double 等其它 cdata 返回 false:
    • istype("int64_t", 9007199254740993LL) -> true
    • istype("uint64_t", 18446744073709551615ULL) -> true
    • istype("int64_t", 18446744073709551615ULL) -> false
    • istype("int64_t", ffi.new("double",1.5)) -> false
  • 格式化不能用 string.format("%lld" / "%llu") —— LuaJIT 不支持 %l,实测报
    invalid option '%l' to 'format'。改用 tostring(v) 再剥掉类型后缀:
    tostring(v):gsub("[UuLl]+$", "")。实测:
    • tostring(9007199254740993LL) -> "9007199254740993LL" -> 去后缀 -> 9007199254740993
    • tostring(18446744073709551615ULL) -> "18446744073709551615ULL" -> 去后缀 -> 18446744073709551615
  • 接入点:encode 主体(lua/qjson/table.luaencode 分发的 type(v)=="cdata" 分支);
    object / array 递归值同样适用(覆盖 D04/D05)。
  • 其它所有 cdata(double、struct、pointer 等)继续拒绝,保持 spec: align qjson.encode Lua input behavior with lua-cjson #106 的共享拒绝语义。

注意点 / 边界

  • 适用范围:服务于「用户持有 / 显式构造 int64 cdata」的场景(doc:get_i64、DB 驱动返回值、
    ffi.newLL/ULL 字面量)。改变 decode 的标量路径 —— 经普通字段读取的数值仍走
    get_f64 返回 double(>2^53 丢精度),本 issue 不涉及 decode 侧。
  • uint64 与 JSON 安全整数uint64_t 可超过 2^53 甚至 int64 上限。输出语法是合法 JSON,
    但下游解析器(JS JSON.parse、甚至 qjson 自身 decode 成 f64)可能丢精度。这是消费方问题;
    qjson 作为产出方如实无损输出。
  • 与 cjson 的关系:cjson 拒绝一切 cdata,这是 qjson 有意的扩展差异(非回归对齐)。

测试覆盖

  • D02~D05 成功并输出期望整数。
  • 边界值:int64 max 9223372036854775807LL、int64 min -9223372036854775808LL
    uint64 max 18446744073709551615ULL0LL / 0ULL
  • 嵌套:数组中的 int64(如 {1LL, 2LL})、对象/数组混合。
  • 仍拒绝:double cdata、struct/pointer 等其它 cdata —— 断言报错。
  • 与 lua-cjson 对比:cjson 拒绝、qjson 成功,记录有意差异。

验收标准

  • D02~D05 编码成功,输出无损十进制整数。
  • 非 int64/uint64 的 cdata 仍被拒绝。
  • 不影响 spec: align qjson.encode Lua input behavior with lua-cjson #106 定义的其它 encode 行为,现有 lazy encode / mutation / depth / property 测试继续通过。
  • Lua 测试可通过现有 Makefile 测试路径运行。

关联

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions