-
Notifications
You must be signed in to change notification settings - Fork 1
/
event.lua
100 lines (81 loc) · 2.19 KB
/
event.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
--- dokidoki.event
--- ==============
using 'pl'
local event = class()
function event:_init()
self._count = 0 -- number of handlers
self._length = 0 -- length of sparse array
self._indices = {} -- handler->index
self._handlers = {} -- list of handlers
self._running = false
end
function event:__call(...)
self._running = true
local handlers = self._handlers
for i = 1, self._length do
local f = handlers[i]
if f ~= nil then
f(...)
end
end
self._running = false
-- we never trigger filters while in the event call loop, so try one now
self:_filter_if_appropriate()
end
function event:add_handler(f)
assert(f ~= nil, 'tried to add a nil handler')
assert(self._indices[f] == nil, 'two of the same handler added to one event')
self._length = self._length + 1
self._count = self._count + 1
self._handlers[self._length] = f
self._indices[f] = self._length
end
function event:remove_handler(f)
local handlers = self._handlers
local indices = self._indices
assert(f ~= nil)
assert(indices[f] ~= nil, 'removing handler that isn\'t on this event')
handlers[indices[f]] = nil
indices[f] = nil
self._count = self._count - 1
self:_filter_if_appropriate()
end
local function filter_out_nils_in_place(t, length)
local src = 1
while src <= length and t[src] ~= nil do
-- [1..src) were already compacted
-- [src..length] left to check
src = src + 1
end
-- [1..src) were already compacted
-- [src..length] left to compact
local dest = src
while src <= length do
-- [1..dest) compacted
-- [src..length] to compact
if t[src] ~= nil then
t[dest] = t[src]
dest = dest + 1
end
src = src + 1
end
-- [1, dest) compacted
-- [dest, length] garbage to nil out
while dest <= length do
t[dest] = nil
dest = dest + 1
end
end
function event:_filter_if_appropriate()
if not self._running and self._count < self._length/2 then
local handlers = self._handlers
local indices = self._indices
filter_out_nils_in_place(handlers, self._length)
self._length = self._count
for i = 1, #handlers do
indices[handlers[i]] = i
end
assert(self._length == #handlers)
end
end
return event