我正在编写一个需要在LuaJit中读取一些复杂二进制消息的小应用程序.
我一直在使用bit模块和string.rep.然而,这一切都非常麻烦.我是使用LuaJit的新手,并认为使用FFI可能会有更简单的方法.
在CI中可以声明这样的结构:
struct mystruct
{
uint32_t field1;
char field2[6];
uin64_t field3;
short field4;
} __attribute__(packed);
Run Code Online (Sandbox Code Playgroud)
在阅读LuaJit的FFI时,你似乎可以宣布
ffi.cdef[[
#pragma pack(1)
struct mystruct
{
uint32_t field1;
char field2[6];
uin64_t field3;
short field4;
};
]]
Run Code Online (Sandbox Code Playgroud)
然后我可以创建一个mystruct并访问这样的字段:
local ms = ffi.new("mystruct")
ms.field1 = 32;
// ... etc
Run Code Online (Sandbox Code Playgroud)
但是,如何将其转换为lua字符串?
我尝试了这个,但它似乎没有做我想要的.
local s = tostring(ms)
Run Code Online (Sandbox Code Playgroud)
还有这个:
local s = ffi.string(ms)
Run Code Online (Sandbox Code Playgroud)
产生以下错误"错误的参数#1到'字符串'(不能将'struct mystruct'转换为'const char*')"
所以我试了一个演员:
local s = ffi.string(ffi.cast("char*", ms))
Run Code Online (Sandbox Code Playgroud)
没有错误,但它在电线上看起来不对.
Mic*_*man 12
使用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
Run Code Online (Sandbox Code Playgroud)
更新:我知道这不是原始问题的一部分,但我刚刚学会了这个技巧,为了完成,这里有一种方法将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
Run Code Online (Sandbox Code Playgroud)