Avoid using multiple tables to fetch hook

String hooks still use a table to fetch the id, but the id indexes a C array.

Also I fixed a missing call in the MusicChange hook.
This commit is contained in:
James R 2020-12-10 05:39:53 -08:00
parent e0a307da15
commit 3f7c2ae0b0

View file

@ -52,17 +52,24 @@ static inline int in_bit_array (const UINT8 *array, const int n) {
return array[n >> 3] & (1 << (n & 7)); return array[n >> 3] & (1 << (n & 7));
} }
typedef struct {
int numHooks;
int *ids;
} hook_t;
typedef struct { typedef struct {
int numGeneric; int numGeneric;
int ref; int ref;
} stringhook_t; } stringhook_t;
static int hookRefs[Hook(MAX)]; static hook_t hookIds[Hook(MAX)];
static int mobjHookRefs[NUMMOBJTYPES][Mobj_Hook(MAX)]; static hook_t mobjHookIds[NUMMOBJTYPES][Mobj_Hook(MAX)];
// Lua tables are used to lookup string hook ids.
static stringhook_t stringHooks[String_Hook(MAX)]; static stringhook_t stringHooks[String_Hook(MAX)];
static int hookReg; // This will be indexed by hook id, the value of which fetches the registry.
static int * hookRefs;
// After a hook errors once, don't print the error again. // After a hook errors once, don't print the error again.
static UINT8 * hooksErrored; static UINT8 * hooksErrored;
@ -71,8 +78,8 @@ static boolean mobj_hook_available(int hook_type, mobjtype_t mobj_type)
{ {
return return
( (
mobjHookRefs [MT_NULL] [hook_type] > 0 || mobjHookIds [MT_NULL] [hook_type].numHooks > 0 ||
mobjHookRefs[mobj_type][hook_type] > 0 mobjHookIds[mobj_type][hook_type].numHooks > 0
); );
} }
@ -109,32 +116,10 @@ static void get_table(lua_State *L)
lua_remove(L, -2); lua_remove(L, -2);
} }
static void new_hook_table(lua_State *L, int *ref) static void add_hook_to_table(lua_State *L, int id, int n)
{ {
if (*ref > 0) lua_pushnumber(L, id);
lua_getref(L, *ref); lua_rawseti(L, -2, n);
else
{
lua_newtable(L);
lua_pushvalue(L, -1);
*ref = luaL_ref(L, LUA_REGISTRYINDEX);
}
}
static void add_hook(lua_State *L, int id)
{
lua_pushnumber(L, 1 + id);
lua_rawseti(L, -2, 1 + lua_objlen(L, -2));
}
static void add_mobj_hook(lua_State *L, int hook_type, int id)
{
mobjtype_t mobj_type = luaL_optnumber(L, 3, MT_NULL);
luaL_argcheck(L, mobj_type < NUMMOBJTYPES, 3, "invalid mobjtype_t");
new_hook_table(L, &mobjHookRefs[mobj_type][hook_type]);
add_hook(L, id);
} }
static void add_string_hook(lua_State *L, int type, int id) static void add_string_hook(lua_State *L, int type, int id)
@ -160,19 +145,39 @@ static void add_string_hook(lua_State *L, int type, int id)
break; break;
} }
new_hook_table(L, &hook->ref); if (hook->ref > 0)
lua_getref(L, hook->ref);
else
{
lua_newtable(L);
lua_pushvalue(L, -1);
hook->ref = luaL_ref(L, LUA_REGISTRYINDEX);
}
if (string) if (string)
{ {
lua_pushstring(L, string); lua_pushstring(L, string);
get_table(L); get_table(L);
add_hook(L, id); add_hook_to_table(L, id, 1 + lua_objlen(L, -1));
} }
else else
{ add_hook_to_table(L, id, ++hook->numGeneric);
lua_pushnumber(L, 1 + id); }
lua_rawseti(L, -2, ++hook->numGeneric);
} static void add_hook(hook_t *map, int id)
{
Z_Realloc(map->ids, (map->numHooks + 1) * sizeof *map->ids,
PU_STATIC, &map->ids);
map->ids[map->numHooks++] = id;
}
static void add_mobj_hook(lua_State *L, int hook_type, int id)
{
mobjtype_t mobj_type = luaL_optnumber(L, 3, MT_NULL);
luaL_argcheck(L, mobj_type < NUMMOBJTYPES, 3, "invalid mobjtype_t");
add_hook(&mobjHookIds[mobj_type][hook_type], id);
} }
// Takes hook, function, and additional arguments (mobj type to act on, etc.) // Takes hook, function, and additional arguments (mobj type to act on, etc.)
@ -200,8 +205,7 @@ static int lib_addHook(lua_State *L)
} }
else if (( type = hook_in_list(name, hookNames) ) < Hook(MAX)) else if (( type = hook_in_list(name, hookNames) ) < Hook(MAX))
{ {
new_hook_table(L, &hookRefs[type]); add_hook(&hookIds[type], nextid);
add_hook(L, nextid);
} }
else else
{ {
@ -216,17 +220,17 @@ static int lib_addHook(lua_State *L)
hooksErrored[nextid >> 3] = 0; hooksErrored[nextid >> 3] = 0;
} }
Z_Realloc(hookRefs, (nextid + 1) * sizeof *hookRefs, PU_STATIC, &hookRefs);
// set the hook function in the registry. // set the hook function in the registry.
lua_getref(L, hookReg);
lua_pushvalue(L, 2);/* the function */ lua_pushvalue(L, 2);/* the function */
lua_rawseti(L, -2, ++nextid); hookRefs[nextid++] = luaL_ref(L, LUA_REGISTRYINDEX);
return 0; return 0;
} }
int LUA_HookLib(lua_State *L) int LUA_HookLib(lua_State *L)
{ {
new_hook_table(L, &hookReg);
lua_register(L, "addHook", lib_addHook); lua_register(L, "addHook", lib_addHook);
return 0; return 0;
} }
@ -237,21 +241,19 @@ typedef void (*Hook_Callback)(Hook_State *);
struct Hook_State { struct Hook_State {
int status;/* return status to calling function */ int status;/* return status to calling function */
void * userdata; void * userdata;
int ref;/* ref for primary hook table */ int hook_type;
int hook_type;/* Hook or Hook(MAX) + Mobj_Hook */ mobjtype_t mobj_type;/* >0 if mobj hook */
mobjtype_t mobj_type;
const char * string;/* used to fetch table, ran first if set */ const char * string;/* used to fetch table, ran first if set */
int top;/* index of last argument passed to hook */ int top;/* index of last argument passed to hook */
int id;/* id to fetch function from registry */ int id;/* id to fetch ref */
int values;/* num arguments passed to hook */ int values;/* num arguments passed to hook */
int results;/* num values returned by hook */ int results;/* num values returned by hook */
Hook_Callback results_handler;/* callback when hook successfully returns */ Hook_Callback results_handler;/* callback when hook successfully returns */
}; };
enum { enum {
HINDEX = 1,/* hook registry */ EINDEX = 1,/* error handler */
EINDEX = 2,/* error handler */ SINDEX = 2,/* string itself is pushed in case of string hook */
SINDEX = 3,/* string itself is pushed in case of string hook */
}; };
static void push_error_handler(void) static void push_error_handler(void)
@ -268,7 +270,6 @@ static void push_string(void)
static boolean start_hook_stack(void) static boolean start_hook_stack(void)
{ {
lua_settop(gL, 0); lua_settop(gL, 0);
lua_getref(gL, hookReg);
push_error_handler(); push_error_handler();
return true; return true;
} }
@ -280,20 +281,12 @@ static boolean init_hook_type
int hook_type, int hook_type,
mobjtype_t mobj_type, mobjtype_t mobj_type,
const char * string, const char * string,
int ref int nonzero
){ ){
boolean ready;
hook->status = status; hook->status = status;
if (mobj_type > 0) if (nonzero)
ready = mobj_hook_available(hook_type, mobj_type);
else
ready = ref > 0;
if (ready)
{ {
hook->ref = ref;
hook->hook_type = hook_type; hook->hook_type = hook_type;
hook->mobj_type = mobj_type; hook->mobj_type = mobj_type;
hook->string = string; hook->string = string;
@ -311,7 +304,7 @@ static boolean prepare_hook
){ ){
return init_hook_type(hook, default_status, return init_hook_type(hook, default_status,
hook_type, 0, NULL, hook_type, 0, NULL,
hookRefs[hook_type]); hookIds[hook_type].numHooks);
} }
static boolean prepare_mobj_hook static boolean prepare_mobj_hook
@ -323,7 +316,7 @@ static boolean prepare_mobj_hook
){ ){
return init_hook_type(hook, default_status, return init_hook_type(hook, default_status,
hook_type, mobj_type, NULL, hook_type, mobj_type, NULL,
mobjHookRefs[mobj_type][hook_type]); mobj_hook_available(hook_type, mobj_type));
} }
static boolean prepare_string_hook static boolean prepare_string_hook
@ -357,16 +350,17 @@ static void init_hook_call
hook->results_handler = results_handler; hook->results_handler = results_handler;
} }
static void get_hook_table(Hook_State *hook) static void get_hook(Hook_State *hook, const int *ids, int n)
{ {
lua_getref(gL, hook->ref); hook->id = ids[n];
lua_getref(gL, hookRefs[hook->id]);
} }
static void get_hook(Hook_State *hook, int n) static void get_hook_from_table(Hook_State *hook, int n)
{ {
lua_rawgeti(gL, -1, n); lua_rawgeti(gL, -1, n);
hook->id = lua_tonumber(gL, -1) - 1; hook->id = lua_tonumber(gL, -1);
lua_rawget(gL, HINDEX); lua_getref(gL, hookRefs[hook->id]);
} }
static int call_single_hook_no_copy(Hook_State *hook) static int call_single_hook_no_copy(Hook_State *hook)
@ -409,7 +403,7 @@ static int call_hook_table_for(Hook_State *hook, int n)
for (k = 1; k <= n; ++k) for (k = 1; k <= n; ++k)
{ {
get_hook(hook, k); get_hook_from_table(hook, k);
call_single_hook(hook); call_single_hook(hook);
} }
@ -421,31 +415,29 @@ static int call_hook_table(Hook_State *hook)
return call_hook_table_for(hook, lua_objlen(gL, -1)); return call_hook_table_for(hook, lua_objlen(gL, -1));
} }
static int call_ref(Hook_State *hook, int ref) static int call_mapped(Hook_State *hook, const hook_t *map)
{ {
int calls; int k;
if (ref > 0) for (k = 0; k < map->numHooks; ++k)
{ {
lua_getref(gL, ref); get_hook(hook, map->ids, k);
calls = call_hook_table(hook); call_single_hook(hook);
return calls;
} }
else
return 0; return map->numHooks;
} }
static int call_string_hooks(Hook_State *hook) static int call_string_hooks(Hook_State *hook)
{ {
const int numGeneric = stringHooks[hook->hook_type].numGeneric; const stringhook_t *map = &stringHooks[hook->hook_type];
int calls = 0; int calls = 0;
get_hook_table(hook); lua_getref(gL, map->ref);
/* call generic string hooks first */ /* call generic string hooks first */
calls += call_hook_table_for(hook, numGeneric); calls += call_hook_table_for(hook, map->numGeneric);
push_string(); push_string();
lua_rawget(gL, -2); lua_rawget(gL, -2);
@ -454,10 +446,9 @@ static int call_string_hooks(Hook_State *hook)
return calls; return calls;
} }
static int call_generic_mobj_hooks(Hook_State *hook) static int call_mobj_type_hooks(Hook_State *hook, mobjtype_t mobj_type)
{ {
const int ref = mobjHookRefs[MT_NULL][hook->hook_type]; return call_mapped(hook, &mobjHookIds[mobj_type][hook->hook_type]);
return call_ref(hook, ref);
} }
static int call_hooks static int call_hooks
@ -475,16 +466,16 @@ static int call_hooks
{ {
calls += call_string_hooks(hook); calls += call_string_hooks(hook);
} }
else else if (hook->mobj_type > 0)
{ {
if (hook->mobj_type > 0) /* call generic mobj hooks first */
calls += call_generic_mobj_hooks(hook); calls += call_mobj_type_hooks(hook, MT_NULL);
calls += call_mobj_type_hooks(hook, hook->mobj_type);
calls += call_ref(hook, hook->ref); ps_lua_mobjhooks += calls;
if (hook->mobj_type > 0)
ps_lua_mobjhooks += calls;
} }
else
calls += call_mapped(hook, &hookIds[hook->hook_type]);
lua_settop(gL, 0); lua_settop(gL, 0);
@ -600,25 +591,24 @@ int LUA_HookTiccmd(player_t *player, ticcmd_t *cmd, int hook_type)
void LUA_HookThinkFrame(void) void LUA_HookThinkFrame(void)
{ {
const int type = Hook(ThinkFrame);
// variables used by perf stats // variables used by perf stats
int hook_index = 0; int hook_index = 0;
precise_t time_taken = 0; precise_t time_taken = 0;
Hook_State hook; Hook_State hook;
int n; const hook_t * map = &hookIds[type];
int k; int k;
if (prepare_hook(&hook, 0, Hook(ThinkFrame))) if (prepare_hook(&hook, 0, type))
{ {
init_hook_call(&hook, 0, 0, res_none); init_hook_call(&hook, 0, 0, res_none);
get_hook_table(&hook); for (k = 0; k < map->numHooks; ++k)
n = lua_objlen(gL, -1);
for (k = 1; k <= n; ++k)
{ {
get_hook(&hook, k); get_hook(&hook, map->ids, k);
if (cv_perfstats.value == 3) if (cv_perfstats.value == 3)
{ {
@ -839,19 +829,16 @@ int LUA_HookHurtMsg(player_t *player, mobj_t *inflictor, mobj_t *source, UINT8 d
void LUA_HookNetArchive(lua_CFunction archFunc) void LUA_HookNetArchive(lua_CFunction archFunc)
{ {
const int ref = hookRefs[Hook(NetVars)]; const hook_t * map = &hookIds[Hook(NetVars)];
Hook_State hook; Hook_State hook;
/* this is a remarkable case where the stack isn't reset */ /* this is a remarkable case where the stack isn't reset */
if (ref > 0) if (map->numHooks > 0)
{ {
// stack: tables // stack: tables
I_Assert(lua_gettop(gL) > 0); I_Assert(lua_gettop(gL) > 0);
I_Assert(lua_istable(gL, -1)); I_Assert(lua_istable(gL, -1));
push_error_handler(); push_error_handler();
lua_getref(gL, hookReg);
lua_insert(gL, HINDEX);
lua_insert(gL, EINDEX); lua_insert(gL, EINDEX);
// tables becomes an upvalue of archFunc // tables becomes an upvalue of archFunc
@ -860,11 +847,10 @@ void LUA_HookNetArchive(lua_CFunction archFunc)
// stack: tables, archFunc // stack: tables, archFunc
init_hook_call(&hook, 1, 0, res_none); init_hook_call(&hook, 1, 0, res_none);
call_ref(&hook, ref); call_mapped(&hook, map);
lua_pop(gL, 2); // pop hook table and archFunc lua_pop(gL, 2); // pop hook table and archFunc
lua_remove(gL, EINDEX); // pop error handler lua_remove(gL, EINDEX); // pop error handler
lua_remove(gL, HINDEX); // pop hook registry
// stack: tables // stack: tables
} }
} }
@ -1031,22 +1017,25 @@ static void res_musicchange(Hook_State *hook)
int LUA_HookMusicChange(const char *oldname, struct MusicChange *param) int LUA_HookMusicChange(const char *oldname, struct MusicChange *param)
{ {
Hook_State hook; const int type = Hook(MusicChange);
if (prepare_hook(&hook, false, Hook(MusicChange))) const hook_t * map = &hookIds[type];
{
int n;
int k;
Hook_State hook;
int k;
if (prepare_hook(&hook, false, type))
{
init_hook_call(&hook, 7, 6, res_musicchange); init_hook_call(&hook, 7, 6, res_musicchange);
hook.userdata = param; hook.userdata = param;
lua_pushstring(gL, oldname);/* the only constant value */ lua_pushstring(gL, oldname);/* the only constant value */
lua_pushstring(gL, param->newname);/* semi constant */ lua_pushstring(gL, param->newname);/* semi constant */
get_hook_table(&hook); for (k = 0; k <= map->numHooks; ++k)
n = lua_objlen(gL, -1); {
get_hook(&hook, map->ids, k);
for (k = 1; k <= n; ++k) {
lua_pushvalue(gL, -3); lua_pushvalue(gL, -3);
lua_pushvalue(gL, -3); lua_pushvalue(gL, -3);
lua_pushinteger(gL, *param->mflags); lua_pushinteger(gL, *param->mflags);
@ -1060,5 +1049,6 @@ int LUA_HookMusicChange(const char *oldname, struct MusicChange *param)
lua_settop(gL, 0); lua_settop(gL, 0);
} }
return hook.status; return hook.status;
} }