diff --git a/matrix/eventable.lua b/matrix/eventable.lua index e273d95..a1a7e09 100644 --- a/matrix/eventable.lua +++ b/matrix/eventable.lua @@ -8,6 +8,7 @@ local _select, _pack, _unpack = select, table.pack, table.unpack or unpack if not _pack then + -- Provide a table.pack() implementation for Lua 5.1 and LuaJIT. _pack = function (...) local n = _select("#", ...) local r = { n = n } @@ -18,28 +19,15 @@ if not _pack then end end -local function _expack (args, ...) - local r = { n = args.n } - local n = _select("#", ...) - for i = 1, args.n do - r[i] = args[i] - end - for i = 1, n do - r[r.n + i] = _select(i, ...) - end - r.n = r.n + n - return _unpack(r) -end - local log = (function () local env_value = os.getenv("MATRIX_EVENTABLE_DEBUG_LOG") if env_value and #env_value > 0 and env_value ~= "0" then local out, _tostring = io.stderr, tostring return function (...) out:write("[eventable]") - local args = _pack(...) - for i = 1, args.n do - out:write(" " .. _tostring(args[i])) + local n = _select("#", ...) + for i = 1, n do + out:write(" " .. _tostring(_select(i, ...))) end out:write("\n") out:flush() @@ -49,100 +37,66 @@ local log = (function () end end)() -local function eventable_functions (...) - local event_map = {} - - local hook = function (event, handler) - log("hook:", event, handler) - if not handler then - event_map[event] = nil - else - if not event_map[event] then - event_map[event] = {} - end - local handlers = event_map[event] - handlers[#handlers + 1] = handler - end - end - - local unhook = function (event, handler) - log("unhook:", event, handler) - local old_handlers = event_map[event] - if old_handlers then - local handlers = {} - for i = 1, #old_handlers do - local h = old_handlers[i] - if h ~= handler then - handlers[#handlers + 1] = h - end - end - event_map[event] = handlers - end - end - - local fire - - local nargs = _select("#", ...) - if nargs == 0 then - -- Simplest version, no arguments. - fire = function (event, ...) - log("fire: " .. event .. ":", ...) - local handlers = event_map[event] - if handlers then - for i = 1, #handlers do - local ret = _pack(handlers[i](...)) - if ret.n > 0 then - return _unpack(ret) - end - end - end - end - elseif nargs == 1 then - -- Common single-argument case: optimized. - local arg = _select(1, ...) - fire = function (event, ...) - log("fire: " .. event .. ":", arg, ...) - local handlers = event_map[event] - if handlers then - for i = 1, #handlers do - local ret = _pack(handlers[i](arg, ...)) - if ret.n > 0 then - return _unpack(ret) - end - end - end - end +local function do_hook(event_map, event, handler) + log("hook:", event, handler) + if not handler then + event_map[event] = nil else - -- Generic multi-argument case. - local args = _pack(...) - fire = function (event, ...) - log("fire: " .. event .. ":", _expack(args, ...)) - local handlers = event_map[event] - if handlers then - for i = 1, #handlers do - local ret = _pack(handlers[i](_expack(args, ...))) - if ret.n > 0 then - return _unpack(ret) - end - end - end + if not event_map[event] then + event_map[event] = {} end + local handlers = event_map[event] + handlers[#handlers + 1] = handler end - - return fire, hook, unhook end -local function eventable_object(obj) - local fire, hook, unhook = eventable_functions() - function obj:fire(name, ...) - return fire(name, self, ...) +local function do_unhook(event_map, event, handler) + log("unhook:", event, handler) + local old_handlers = event_map[event] + if old_handlers then + local handlers = {} + for i = 1, #old_handlers do + local h = old_handlers[i] + if h ~= handler then + handlers[#handlers + 1] = h + end + end + event_map[event] = handlers + end +end + +local function do_fire(event_map, event, ...) + log("fire: " .. event .. ":", ...) + local handlers = event_map[event] + if handlers then + for i = 1, #handlers do + local ret = _pack(handlers[i](...)) + if ret.n > 0 then + return _unpack(ret) + end + end + end +end + +local function eventable_functions (event_map) + if not event_map then event_map = {} end + return function (e, ...) return do_fire(event_map, e, ...) end, + function (e, h) return do_hook(event_map, e, h) end, + function (e, h) return do_unhook(event_map, e, h) end +end + +local function eventable_object(obj, event_map) + if not obj then obj = {} end + if not event_map then event_map = {} end + function obj:fire(e, ...) + return do_fire(event_map, e, self, ...) end function obj:hook(...) - hook(...) + do_hook(event_map, ...) return self -- Allow chaining end function obj:unhook(...) - unhook(...) + do_unhook(event_map, ...) return self -- Allow chaining end return obj