I'm in the middle of writing a small application that needs to read some complex binary messages in LuaJit.
I've been using the bit module and string.rep a lot. However, it's all very cumbersome. I'm new to using LuaJit and think there might be a much easier way using FFI.
In C I can declare a structure like this:
struct mystruct
{
uint32_t field1;
char field2[6];
uin64_t field3;
short field4;
} __attribute__(packed);
In reading LuaJit's FFI it seems you can declare
ffi.cdef[[
#pragma pack(1)
struct mystruct
{
uint32_t field1;
char field2[6];
uin64_t field3;
short field4;
};
]]
I can then create a mystruct and access the fields like this:
local ms = ffi.new("mystruct")
ms.field1 = 32;
// ... etc
But, how do I convert this back into a lua string?
I tried this, but it didn't seem to do what I wanted.
local s = tostring(ms)
and this:
local s = ffi.string(ms)
produces the following error "bad argument #1 to 'string' (cannot convert 'struct mystruct' to 'const char *')"
So I tried a cast:
local s = ffi.string(ffi.cast("char*", ms))
No error, but it looks wrong on the wire.
You have to explicitly specify the length when using ffi.string
with a non-string-like parameter:
str = ffi.string(ptr [,len])
Creates an interned Lua string from the data pointed to by ptr.
If the optional argument len is missing, ptr is converted to a "char *" and the data is assumed to be zero-terminated. The length of the string is computed with strlen().
When running the following code, I get the expected (little endian) result:
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
Update: I know this is not a part of the original question, but I just learned this trick, and in order to be complete, here is a way to convert Lua string back to 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