lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


Thanks a lot, doing the memory operations on the userdata completely
in lua side is a much better approach.
That way I could allocate a bunch of generic sized userdatas in
advance, so that the regular plugin call doesn't ruin the jit at later
times.

2011/2/28 Robert G. Jakabosky <bobby@sharedrealm.com>:
> On Sunday 27, CrazyButcher wrote:
>> I try to box the content of a cdata in my own userdata via a classic
>> lua plugin function, to which I can hand the metatable for the
>> userdata.
>>
>> lua_topointer -> gives pointer to cdata obj, not the pointer it stores
>> lua_touserdata -> fails on cdata
>>
>> lua_tonumber -> works but it feels weird
>>
>> I create the number in the lua code through
>> tonumber(ffi.cast("uintptr_t",cdata))
>>
>> and on C side
>> const void orig = (const void*)(uintptr_t)lua_tonumber(...)
>
> I do not think this would be 64bit safe.  It might work most of the time if
> all your 64bit pointer only use the lower 48bits.
>
> For the FFI-based bindings generated by LuaNativeObjects [1], I exported
> (private to the FFI bindings code) a C function which takes two arguments size
> & a metatable and returns a new userdata object.  Then in the FFI bindings
> code the userdata object is cast to a structure pointer.
>
> /* c code. */
> static int nobj_udata_new_ffi(lua_State *L) {
>        size_t size = luaL_checkinteger(L, 1);
>        void *ud;
>        luaL_checktype(L, 2, LUA_TTABLE);
>        lua_settop(L, 2);
>        /* create userdata. */
>        ud = lua_newuserdata(L, size);
>        lua_replace(L, 1);
>        /* set userdata's metatable. */
>        lua_setmetatable(L, 1);
>        return 1;
> }
>
> -- lua side FFI code
> ffi.cdef[[
> typedef struct obj_udata {
>  void *obj;
>  int flags;
> } obj_udata;
> ]]
>
> -- create new userdata wrapped cdata object.
> function new_cdata_obj(cdata, size)
>  local meta = {} -- new metatable or use some existing metatable
>  if not size then
>    local udata = nobj_udata_new_ffi(ffi.sizeof('obj_udata'), meta)
>    local obj_data = ffi.cast('obj_udata *')
>    obj_data.obj = cdata -- cdata pointer.
>    obj_data.flags = 0
>  else
>    local udata = nobj_udata_new_ffi(size, meta)
>    local obj_data = ffi.cast('void *')
>    ffi.copy(obj_data, cdata, size)
>  end
>  -- return userdata
>  return udata, meta
> end
>
> Also note that the object's methods will receive the userdata object as the
> 'self' argument and it will need to be cast back to a cdata value and you
> should check the metatable on the userdata to make sure it is the right type
> (someone could pass any other userdata object can cause bad things to happen).
> If you don't do type checking, then your bindings can't be used in a sandbox
> safely.
>
> For performance of the metatable type checking, I use a weak table per object
> type that maps each userdata value to the wrapped cdata value.
>
> local function obj_udata_luacheck_internal(obj, type_mt)
>  -- need the debug version of getmetatable, since the metatable might be
>  -- hidden (i.e. if '__metatable' is set)
>  local obj_mt = debug.getmetatable(obj)
>  if obj_mt == type_mt then
>    -- convert userdata to cdata.
>    local ud = ffi.cast('obj_data *',obj)
>    return ud.obj
>  end
>  error("(expected `" .. type_mt['.name'] .. "`, got " .. type(obj) .. ")", 2)
> end
>
> -- '${object_name}' is replaced with the cdata C typename.
> local ${object_name}_objects[udata] = setmetatable({}, { __mode = "k" })
> local function obj_type_${object_name}_check(udata)
>  local c_obj = ${object_name}_objects[udata]
>  if c_obj == nil then
>    -- cdata object not in cache
>    c_obj = obj_udata_luacheck(udata, ${object_name}_mt)
>    c_obj = ffi.cast("${object_name} *", c_obj) -- cast from 'void *'
>    ${object_name}_objects[udata] = c_obj -- cache object
>  end
>  return c_obj
> end
>
> 1. https://github.com/Neopallium/LuaNativeObjects
>
> --
> Robert G. Jakabosky
>
>