如何将cdata结构转换为lua字符串?

5
我正在编写一个小应用程序,需要在LuaJit中读取一些复杂的二进制消息。我一直在大量使用bit模块和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))

没有错误,但在传输过程中看起来不正确。


哎呀,应该是pack而不是packed。已修复。 - hookenz
1
“it looks wrong on the wire”是什么意思?你看到了什么?你期望/想要看到什么? - Michal Kottman
2个回答

14

当使用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)) -- specify how long the byte sequence is
print(s:tohex()) --> 0100000002030405060708000000000000000900

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

    data = ffi.new('struct mystruct')   -- create a new cdata
    ffi.copy(data, s, ffi.sizeof(data)) -- fill it with data from Lua string 's'
    print(data.field1, data.field4)     --> 1   9

1
本地变量st被转换为'mystruct*'结构体类型的指针,指向s。然后打印出st中的field4字段。 - Nick X

0

之前有一个错误。 根据当前的 luaffi 实现,您可以将其转换为 void*。使用 ffi.string(ffi.cast("void*",ms),ffi.sizeof(ms)) 来实现。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接