Skip to content

cdata serializer with reflect #11

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
sonoro1234 opened this issue Dec 23, 2024 · 2 comments
Open

cdata serializer with reflect #11

sonoro1234 opened this issue Dec 23, 2024 · 2 comments

Comments

@sonoro1234
Copy link

I have implemented a cdata serializer with reflect that I wish to share. It recreates the table of init values used in ffi.new
I can be used as:

local str = cdataser(cdata)
print(str)
print(loadstring("local ffi = require'ffi';"..str))
print(loadstring("local ffi = require'ffi';return "..str)())

It needs the necessary cdefs already present in LuaJIT to work but it seems no problem as they were already present when saving.
It can be used as a plug in a more general serializer for tables.

this is the cdataser.lua code

local ffi = require "ffi"
local reflect = require "reflect"

local function tb2st(t)
    if type(t)~= "table" then
        return tostring(t)
    else
        local str = {"{"}
        for i,v in ipairs(t) do
            table.insert(str,tb2st(v))
            table.insert(str, ",")
        end
        if #str > 1 then table.remove(str) end
        table.insert(str, "}")
        return table.concat(str)
    end
end

local function valor(base,ind,name)
    --print("valor",tb2st(ind),name)
    local field = base
    for i,vv in ipairs(ind) do field = field[vv] end
    field = field[name]
    return field
end
local function valorarray(base,ind,name,nels)
    local field = valor(base,ind,name)
    local t = {}
    for i=0,nels-1 do t[i+1] = field[i] end
    return t
end
local function mergetab(t,t2,ti,ti2)
    if ti2.what == "union" then
        t2 = ti.transparent and t2[1] or {t2[1]}
        table.insert(t,t2)
    else --struct
        if ti.transparent then
            for i,v in ipairs(t2) do table.insert(t,v) end
        else
            table.insert(t,t2)
        end
    end
end
local function cd2tab(cd,ct,ind,nelems)
  local t = {}
  ind = ind or {}
  for ti in ct:members() do
    if ti.what == "struct" or ti.what == "union" then
        if not ti.transparent then table.insert(ind,ti.name) end
        local t2 = cd2tab(cd,ti,ind,nelems)
        if not ti.transparent then table.remove(ind) end
        mergetab(t,t2,ti,ti)
    else
        assert(ti.what == "field")
        if ti.type.what == "struct" or ti.type.what == "union" then
            if not ti.transparent then table.insert(ind,ti.name) end
            local t2 = cd2tab(cd,ti.type,ind,nelems)
            if not ti.transparent then table.remove(ind) end
            mergetab(t,t2,ti,ti.type)
        elseif ti.type.what == "array" then
            local nels = ti.type.vla and nelems or (ti.type.size/ti.type.element_type.size)
            local t2 = valorarray(cd,ind,ti.name,nels)
            table.insert(t,t2)
        else
            table.insert(t,valor(cd,ind,ti.name))
        end
    end
  end
  return ct.what=="union" and {t[1]} or t
end

local function valorarray1(cd,nels,ti)
    local tk = ti.element_type.what
    local t = {}
    for i=0,nels-1 do
        local val = cd[i]
        if tk == "struct" or tk == "union" then 
            val = cd2tab(cd[i],ti.element_type,{})
        end
        t[i+1] = val 
    end
    return t
end
local function genstr(tystr,vals,nelem)
    if nelem then
        return table.concat{"ffi.new(\"",tystr,"\",",nelem,",",tb2st(vals),")"}
    else
        return table.concat{"ffi.new(\"",tystr,"\",",tb2st(vals),")"}
    end
end
local function cdataser(cd)
    local ty = ffi.typeof(cd)
    local len = ffi.sizeof(cd)
    local s0 = ffi.sizeof(ty,0)
    local s1 = ffi.sizeof(ty,1)
    local tystr = tostring(ty):sub(7, -2)
    local is_vla, nelem
    if s0 ~= s1 then
        is_vla = true
        local el_size = s1 - s0
        nelem = (len - s0)/el_size
    end
    local ti = reflect.typeof(ty)
    if ti.what == "struct" or ti.what == "union" then
        local t = cd2tab(cd,ti,{},nelem)
        return genstr(tystr,t,nelem)
    elseif ti.what == "array" then
        assert(ti.vla == is_vla)
        local nels = is_vla and nelem or ti.size/ti.element_type.size
        local vals = valorarray1(cd,nels,ti)
        return genstr(tystr,vals,nelem)
    else
        return genstr(tystr,tonumber(cd))
    end
end

return cdataser

feedback welcome

@sonoro1234
Copy link
Author

ptr, ref, bitfields and anonymous struct (union) having a typedef now added.
For finding typedef of an anonymous struct it would be necessary to have access to refct_from_id

@sonoro1234
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant