403 lines
8.8 KiB
Lua
403 lines
8.8 KiB
Lua
local fmt = require "fmt";
|
|
|
|
local uint8_le_fmt = fmt.new "<! I1";
|
|
local uint16_le_fmt = fmt.new "<! I2";
|
|
local uint32_le_fmt = fmt.new "<! I4";
|
|
|
|
|
|
local parsers = {};
|
|
local cache = setmetatable({}, { __mode = "v" });
|
|
|
|
-- local indent = "";
|
|
|
|
local function alignof(i, n)
|
|
return math.ceil((i - 1) / n) * n + 1;
|
|
end
|
|
|
|
local function offset_reader(s, e)
|
|
if e - s + 1 < 256 then
|
|
return function (buf, i)
|
|
local res = uint8_le_fmt:unpack(buf, e - i + 1);
|
|
return res;
|
|
end, 1;
|
|
elseif e - s + 1 < 65536 then
|
|
return function (buf, i)
|
|
local res = uint16_le_fmt:unpack(buf, e - i * 2 + 1);
|
|
return res;
|
|
end, 2;
|
|
else
|
|
return function (buf, i)
|
|
local res = uint32_le_fmt:unpack(buf, e - i * 4 + 1);
|
|
return res;
|
|
end, 4;
|
|
end
|
|
end
|
|
|
|
local function construct_reader_no_cache(str, i)
|
|
local c = str:sub(i, i);
|
|
|
|
if c == "b" then
|
|
return parsers.bool(), i + 1;
|
|
elseif c == "y" then
|
|
return parsers.uint8(), i + 1;
|
|
elseif c == "n" then
|
|
return parsers.int16(), i + 1;
|
|
elseif c == "q" then
|
|
return parsers.uint16(), i + 1;
|
|
elseif c == "i" then
|
|
return parsers.int32(), i + 1;
|
|
elseif c == "u" then
|
|
return parsers.uint32(), i + 1;
|
|
elseif c == "x" then
|
|
return parsers.int64(), i + 1;
|
|
elseif c == "t" then
|
|
return parsers.uint64(), i + 1;
|
|
elseif c == "d" then
|
|
return parsers.float64(), i + 1;
|
|
elseif c == "s" or c == "o" or c == "g" then
|
|
return parsers.string(), i + 1;
|
|
elseif c == "r" then
|
|
return parsers.ref(), i + 1;
|
|
elseif c == "v" then
|
|
return parsers.variant(), i + 1;
|
|
elseif c == "m" then
|
|
local reader;
|
|
reader, i = construct_reader_no_cache(str, i + 1);
|
|
return parsers.maybe(reader), i;
|
|
elseif c == "a" or c == "l" then
|
|
i = i + 1;
|
|
local is_gen = c == "l";
|
|
|
|
if str:sub(i, i) == "{" then
|
|
i = i + 1;
|
|
|
|
local key, val;
|
|
key, i = construct_reader_no_cache(str, i);
|
|
val, i = construct_reader_no_cache(str, i);
|
|
|
|
if str:sub(i, i) ~= "}" then
|
|
error "malformed gvarian format";
|
|
end
|
|
i = i + 1;
|
|
|
|
return parsers.map(key, val), i;
|
|
elseif str:sub(i, i) == "y" then
|
|
i = i + 1;
|
|
return parsers.buffer(), i;
|
|
else
|
|
local reader;
|
|
reader, i = construct_reader_no_cache(str, i);
|
|
return parsers.array(reader, is_gen), i;
|
|
end
|
|
elseif c == "(" then
|
|
i = i + 1;
|
|
|
|
local tuple_parsers = {};
|
|
|
|
while str:sub(i, i) ~= ")" do
|
|
tuple_parsers[#tuple_parsers + 1], i = construct_reader_no_cache(str, i);
|
|
end
|
|
|
|
i = i + 1;
|
|
|
|
return parsers.tuple(table.unpack(tuple_parsers)), i;
|
|
elseif c == "{" then
|
|
i = i + 1;
|
|
|
|
local key, val;
|
|
key, i = construct_reader_no_cache(str, i);
|
|
val, i = construct_reader_no_cache(str, i);
|
|
|
|
if str:sub(i, i) ~= "}" then
|
|
error "malformed gvarian format";
|
|
end
|
|
i = i + 1;
|
|
|
|
return parsers.tuple(key, val), i;
|
|
else
|
|
error "malformed gvariant format";
|
|
end
|
|
end
|
|
|
|
local function construct_reader(str)
|
|
if not cache[str] then
|
|
cache[str] = { construct_reader_no_cache(str, 1) };
|
|
end
|
|
return cache[str][1], cache[str][2];
|
|
end
|
|
|
|
local function encoded_helper(c)
|
|
return ("%02x"):format(string.byte(c));
|
|
end
|
|
local function to_encoded(str)
|
|
return str:gsub(".", encoded_helper);
|
|
end
|
|
|
|
local function fmt_to_parser(format, fixed, align)
|
|
return function ()
|
|
return {
|
|
fixed = fixed, align = align,
|
|
read = function(buf, s)
|
|
return (format:unpack(buf, s));
|
|
end,
|
|
};
|
|
end
|
|
end
|
|
|
|
parsers.uint8 = fmt_to_parser(fmt.new ">! I1", 1, 1);
|
|
parsers.uint16 = fmt_to_parser(fmt.new ">! I2", 2, 2);
|
|
parsers.uint32 = fmt_to_parser(fmt.new ">! I4", 4, 4);
|
|
parsers.uint64 = fmt_to_parser(fmt.new ">! I8", 8, 8);
|
|
|
|
parsers.int8 = fmt_to_parser(fmt.new ">! i1", 1, 1);
|
|
parsers.int16 = fmt_to_parser(fmt.new ">! i2", 2, 2);
|
|
parsers.int32 = fmt_to_parser(fmt.new ">! i4", 4, 4);
|
|
parsers.int64 = fmt_to_parser(fmt.new ">! i8", 8, 8);
|
|
|
|
parsers.float32 = fmt_to_parser(fmt.new ">! f", 4, 4);
|
|
parsers.float64 = fmt_to_parser(fmt.new ">! d", 8, 8);
|
|
|
|
function parsers.string()
|
|
return {
|
|
fixed = nil, align = 1,
|
|
read = function(buf, s, e)
|
|
return buf:sub(s, e - 1);
|
|
end,
|
|
};
|
|
end
|
|
function parsers.ref()
|
|
return {
|
|
fixed = nil, align = 1,
|
|
read = function(buf, s, e)
|
|
return to_encoded(buf:sub(s, e));
|
|
end,
|
|
};
|
|
end
|
|
function parsers.buffer()
|
|
return {
|
|
fixed = nil, align = 1,
|
|
read = function(buf, s, e)
|
|
return buf:sub(s, e);
|
|
end,
|
|
};
|
|
end
|
|
|
|
function parsers.bool()
|
|
return {
|
|
fixed = 1, align = 1,
|
|
read = function(buf, s, e)
|
|
return buf:sub(s, e) == "\1";
|
|
end,
|
|
};
|
|
end
|
|
|
|
function parsers.maybe(val_parser)
|
|
if val_parser.fixed then
|
|
return {
|
|
fixed = nil, align = val_parser.align,
|
|
read = function(buf, s, e)
|
|
if s > e then
|
|
return nil;
|
|
else
|
|
return val_parser.read(buf, alignof(s, val_parser.align), e);
|
|
end
|
|
end
|
|
};
|
|
else
|
|
return {
|
|
fixed = nil, align = val_parser.align,
|
|
read = function(buf, s, e)
|
|
if s > e then
|
|
return nil;
|
|
else
|
|
return val_parser.read(buf, alignof(s, val_parser.align), e - 1);
|
|
end
|
|
end
|
|
};
|
|
end
|
|
end
|
|
|
|
function parsers.array(el_parser, as_iterable)
|
|
return {
|
|
fixed = nil, align = el_parser.align,
|
|
read = function(buf, s, e)
|
|
if s > e then
|
|
if as_iterable then
|
|
return function () return nil end
|
|
else
|
|
return {};
|
|
end
|
|
end
|
|
|
|
s = alignof(s, el_parser.align);
|
|
|
|
local read_offset, offset_size = offset_reader(s, e);
|
|
local n = ((e - s + 1) - read_offset(buf, 1)) / offset_size;
|
|
local curr_s = s;
|
|
|
|
-- print(indent, "ARR\t", s, e, n);
|
|
-- indent = indent .. "\t";
|
|
|
|
assert(not (n % 1 > 0), "offset calculation error");
|
|
assert(n > 0, "negative array length");
|
|
n = math.floor(n);
|
|
|
|
if as_iterable then
|
|
local i = 0;
|
|
|
|
return function ()
|
|
if i >= n then
|
|
-- indent = indent:sub(1, -2);
|
|
return nil;
|
|
end
|
|
i = i + 1;
|
|
|
|
local curr_e = s + read_offset(buf, n - i + 1) - 1;
|
|
curr_s = alignof(curr_s, el_parser.align);
|
|
|
|
-- print(indent, "ARR ELEMENT", i, curr_s, curr_e);
|
|
local res = el_parser.read(buf, curr_s, curr_e);
|
|
|
|
curr_s = curr_e + 1;
|
|
return res;
|
|
end
|
|
else
|
|
local res = {};
|
|
for i = 1, n do
|
|
local curr_e = s + read_offset(buf, n - i + 1) - 1;
|
|
curr_s = alignof(curr_s, el_parser.align);
|
|
|
|
-- print(indent, "ARR ELEMENT", i, curr_s, curr_e);
|
|
res[i] = el_parser.read(buf, curr_s, curr_e);
|
|
|
|
curr_s = curr_e + 1;
|
|
end
|
|
|
|
-- indent = indent:sub(1, -2);
|
|
return res;
|
|
end
|
|
end
|
|
};
|
|
end
|
|
|
|
function parsers.map(key, val)
|
|
local arr_parser = parsers.array(parsers.tuple(key, val), true);
|
|
|
|
return {
|
|
fixed = nil, align = arr_parser.align,
|
|
read = function (buf, s, e)
|
|
local res = {};
|
|
|
|
for el in arr_parser.read(buf, s, e) do
|
|
res[el[1]] = el[2];
|
|
end
|
|
|
|
return res;
|
|
end
|
|
}
|
|
end
|
|
|
|
function parsers.tuple(...)
|
|
local descriptors = { ... };
|
|
|
|
local align = 1;
|
|
--- @type integer | nil
|
|
local fixed = 0;
|
|
|
|
for i = 1, select("#", ...) do
|
|
assert(select(i, ...) ~= nil, "don't pass nil arguments!");
|
|
|
|
local parser = descriptors[i];
|
|
|
|
if align < parser.align then
|
|
align = parser.align;
|
|
end
|
|
|
|
if parser.fixed == nil then
|
|
fixed = nil;
|
|
elseif fixed ~= nil then
|
|
fixed = alignof(fixed, parser.align) + parser.fixed;
|
|
end
|
|
end
|
|
|
|
return {
|
|
fixed = fixed, align = align,
|
|
read = function (buff, s, e)
|
|
if s > e then return {} end
|
|
|
|
s = alignof(s, align);
|
|
-- print(indent, "TUPLE\t", s, e);
|
|
-- indent = indent .. "\t";
|
|
|
|
local read_offset, offset_size = offset_reader(s, e);
|
|
local res = {};
|
|
local curr_s = s;
|
|
|
|
local offset_i = 0;
|
|
|
|
for i = 1, #descriptors do
|
|
curr_s = alignof(curr_s, descriptors[i].align);
|
|
local curr_e;
|
|
|
|
if descriptors[i].fixed then
|
|
curr_e = curr_s + descriptors[i].fixed - 1;
|
|
-- print(indent, "TUPLE FIXEL", i, curr_s, curr_e);
|
|
elseif i == #descriptors then
|
|
curr_e = e - offset_size * offset_i;
|
|
-- print(indent, "TUPLE LASTEL", i, curr_s, curr_e);
|
|
else
|
|
offset_i = offset_i + 1;
|
|
curr_e = s + read_offset(buff, offset_i) - 1;
|
|
-- print(indent, "TUPLE DYNEL", i, curr_s, curr_e);
|
|
end
|
|
|
|
assert(curr_e - curr_s + 1 >= 0, "negative length segment");
|
|
|
|
res[i] = descriptors[i].read(buff, curr_s, curr_e);
|
|
curr_s = curr_e + 1;
|
|
end
|
|
|
|
-- indent = indent:sub(1, -2);
|
|
return res;
|
|
end
|
|
};
|
|
end
|
|
|
|
function parsers.variant()
|
|
return {
|
|
fixed = nil, align = 8,
|
|
read = function (buff, s, e)
|
|
local format = buff:sub(s, e):match "\0([^\0]+)$";
|
|
local parser = construct_reader(format);
|
|
-- print(indent, "VARIANT", format);
|
|
return parser.read(buff, alignof(s, parser.align), e - #format - 1);
|
|
end
|
|
};
|
|
end
|
|
|
|
return function (format)
|
|
local readers = {};
|
|
|
|
while #format > 0 do
|
|
local i;
|
|
readers[#readers + 1], i = construct_reader(format);
|
|
format = format:sub(i);
|
|
end
|
|
|
|
if #readers == 0 then
|
|
return function () end
|
|
elseif #readers == 1 then
|
|
local reader = readers[1];
|
|
|
|
return function (buff, s, e)
|
|
return reader.read(buff, s or 1, e or #buff);
|
|
end
|
|
else
|
|
local tuple_parser = parsers.tuple(table.unpack(readers));
|
|
|
|
return function (buff, s, e)
|
|
return table.unpack(tuple_parser.read(buff, s or 1, e or #buff));
|
|
end
|
|
end
|
|
end
|