如何将CDATA结构转换为Lua字符串?

我正在编写一个小程序,需要使用LuaJit读取一些复杂的二进制消息。

我一直在大量使用位模块和string.rep,但是这一切都非常麻烦。我刚接触LuaJit,并认为使用FFI可能有更简单的方法。

在C中,我可以声明一个这样的结构:

struct mystruct
{
  uint32_t field1;
  char     field2[6];
  uin64_t  field3;
  short    field4;
} __attribute__(packed);

在阅读LuaJit的FFI时,似乎可以声明如下:

ffi.cdef[[
    #pragma pack(1)
    struct mystruct
    {
      uint32_t field1;
      char     field2[6];
      uin64_t  field3;
      short    field4;
    };
]]

然后我可以创建一个mystruct,并像这样访问字段:

local ms = ffi.new("mystruct")
ms.field1 = 32;
// ... etc

但我如何将其转换回Lua字符串?

我尝试过这个,但似乎没有得到我想要的结果。

local s = tostring(ms)

以及这个:

local s = ffi.string(ms)

产生以下错误 “bad argument #1 to 'string' (cannot convert 'struct mystruct' to 'const char *')”

所以我尝试了一个转换:

local s = ffi.string(ffi.cast("char*", ms))

没有错误,但是在传输过程中似乎有问题。

点赞
用户221509
用户221509

使用ffi.string与非字符串参数时,必须显式指定长度:

str = ffi.string(ptr [,len])

从指针ptr指向的数据创建Lua字符串。

如果省略可选参数len,将把ptr转换为“char *”,并假定数据以零结尾。字符串的长度使用strlen()计算。

当运行以下代码时,会得到预期的(小端)结果:

ffi = require 'ffi'
ffi.cdef[[
    typedef unsigned long uint32_t;
    typedef unsigned long long uint64_t;
    #pragma pack(1)
    struct mystruct
    {
      uint32_t field1;
      char     field2[6];
      uint64_t  field3;
      short    field4;
    };
]]

function string.tohex(str)
    return (str:gsub('.', function (c)
        return string.format('%02X', string.byte(c))
    end))
end

ms = ffi.new('struct mystruct', 1, {2, 3, 4, 5, 6, 7}, 8, 9)
s = ffi.string(ms, ffi.sizeof(ms)) -- 指定字节序列的长度
print(s:tohex()) --> 0100000002030405060708000000000000000900

更新:我知道这不是原问题的一部分,但我刚学到这个技巧,为了完整起见,这是一种将Lua字符串转换回FFI cdata的方法:

    data = ffi.new('struct mystruct')   -- 创建一个新的cdata
    ffi.copy(data, s, ffi.sizeof(data)) -- 用Lua字符串's'中的数据填充它
    print(data.field1, data.field4)     --> 1   9
2012-09-10 07:32:43
用户9472195
用户9472195

上一个 存在错误。 根据当前的 luaffi 实现,你可以将它转换为 void*。使用 ffi.string(ffi.cast("void*",ms),ffi.sizeof(ms)) 完成。

2019-03-07 14:14:17