lua-users home
lua-l archive

late binding functions to save ram on embedded systems

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


I was looking for ways to free up ram on embedded systems using lua and ran across e-lua
specifically the LuaTinyRam patches I looked through the source and replicated it on my side 
to see what kind of ram savings it brought around 10-15k on this specific application pretty good!
Anyways, having hand applied the patches I realized how much of a maintenance nightmare 
these changes would add, So I came up with something I think might work well and would like
to get some opinions.
My Idea is to use a form of late binding basically instead of luaL_register adding tables instead 
it would store the address of the table and add an __index and __pairs method to the meta table
this is lua 5.1 so I also had to change the pairs function a bit
[lauxlib.c]
------------------------------------------------------------------------------------
static int lookup_latebind_func(lua_State *L)
{
 const luaL_Reg *l;
 /* name is top of stack */
 const char * name = lua_tostring (L, -1);
 luaL_argcheck(L, lua_istable(L, -2), -2, "table expected");
 lua_getmetatable (L, -2);
 
 /* lookup our virtual function(s) */
 for(int i = lua_objlen(L, -1); i > 0; i--) {
 lua_rawgeti (L, -1, i);
 l = (const luaL_Reg *) lua_touserdata (L, -1);
 lua_pop(L, 1);
 if(!l)
 break;
 for (; l->name; l++) {
 if(!name || strcmp(name, l->name) == 0) {
 /* if we use a function it gets added to the base table*/
 lua_pushcclosure(L, l->func, 0);
 if(name) {
 lua_pushvalue(L, -1); /* dupe closure */
 lua_setfield (L, -5, l->name);
 /* returns the closure */
 return 1;
 }
 else
 lua_setfield (L, -4, l->name);
 }
 }
 }
 lua_pop(L, 2); /* base table is top of stack */
 return 0;
}
static int latebind_func_pairs(lua_State *L)
{
 const luaL_Reg *l;
 /* base table is top of stack */
 luaL_argcheck(L, lua_istable(L, -1), -1, "table expected");
 lua_getmetatable (L, -1);
 lua_getglobal(L, "pairs"); /* function to be called / returned */
 /* clone the base table */
 lua_newtable(L);
 lua_pushnil(L);
 while(lua_next(L, -5) != 0) { 
 lua_pushvalue(L, -2); /* dupe key */
 lua_insert(L, -2);
 lua_settable(L, -4);
 }
 /* add our dynamic functions */
 for(int i = lua_objlen(L, -3); i > 0; i--) {
 lua_rawgeti (L, -3, i);
 l = (const luaL_Reg *) lua_touserdata (L, -1);
 lua_pop(L, 1);
 if(!l)
 break;
 for (; l->name; l++)
 { 
 lua_pushcclosure(L, l->func, 0);
 lua_setfield (L, -2, l->name);
 }
 }
 lua_call(L, 1, 3);
 return 3;
}
LUALIB_API void (luaL_register_late) (lua_State *L, const char *libname,
 const luaL_Reg *l) {
 if(!libname)
 {
 /* if there is no libname register normally */
 luaI_openlib(L, libname, l, 0);
 return;
 }
 
 static const struct luaL_reg virt_lib [] =
 {
 {"__latebind", lookup_latebind_func},
 {NULL, NULL}
 };
 luaI_openlib(L, libname, virt_lib, 0);
 if(!lua_getmetatable(L, -1))
 lua_createtable(L, 5, 2); //-2
 lua_pushcfunction(L, latebind_func_pairs);
 lua_setfield(L, -2, "__pairs");
 lua_pushcfunction(L, lookup_latebind_func);
 lua_setfield(L, -2, "__index");
 lua_pushinteger(L, lua_objlen(L, -1) + 1);
 lua_pushlightuserdata (L, (void *) l);
 lua_settable(L, -3);
 
 lua_setmetatable(L, -2);
}
[lbaselib.c]
-------------------------------------------------------------------------------------
static int luaB_pairs (lua_State *L) {
 if (!luaL_getmetafield(L, 1, "__pairs")) { /* no metamethod? */
 luaL_checktype(L, 1, LUA_TTABLE);
 lua_pushvalue(L, lua_upvalueindex(1)); /* return generator, */
 lua_pushvalue(L, 1); /* state, */
 lua_pushnil(L); /* and initial value */
 }
 else {
 lua_pushvalue(L, 1); /* argument 'self' to metamethod */
 lua_call(L, 1, 3); /* get 3 values from metamethod */
 }
 
 return 3;
}
By adding the __pairs metamethod it allows us to still lookup functions within the tables
when functions are called within a latebound table the __index metamethod adds them
to the table and returns the closure this makes it faster after the first call to a function
The __latebind key allows a way to see which tables are latebound and if desired
bind all the functions by passing nil as the name __latebind(nil, t)

AltStyle によって変換されたページ (->オリジナル) /