-- gets a table from the stack, it will then be recycled.
functionpopCachedTable()
localt=tableCache
iftthen
tableCache=t.next
numChachedTables=numChachedTables-1
end
returnt
end
-- tries to push a table on the stack
-- only tables with <= 4 array entries are accepted as cached tables are only used for tasks with few arguments as we don't want to have big arrays wasting our precious memory space doing nothing...
-- also, the maximum number of cached tables is limited to 8 as DBM rarely has more than eight scheduled tasks with less than 4 arguments at the same time
-- this is just to re-use all the tables of the small tasks that are scheduled all the time (like the wipe detection)
-- note that the cache does not use weak references anywhere for performance reasons, so a cached table will never be deleted by the garbage collector
functionpushCachedTable(t)
ifnumChachedTables<8and#t<=4then
twipe(t)
t.next=tableCache
tableCache=t
numChachedTables=numChachedTables+1
end
end
end
-- priority queue (min-heap) that stores all scheduled tasks.
-- insert: O(log n)
-- deleteMin: O(log n)
-- getMin: O(1)
-- removeAllMatching: O(n)
localinsert,removeAllMatching,getMin,deleteMin
do
localheap={}
localfirstFree=1
-- gets the next task
functiongetMin()
returnheap[1]
end
-- restores the heap invariant by moving an item up
localfunctionsiftUp(n)
localparent=floor(n/2)
whilen>1andheap[parent].time>heap[n].timedo-- move the element up until the heap invariant is restored, meaning the element is at the top or the element's parent is <= the element
heap[n],heap[parent]=heap[parent],heap[n]-- swap the element with its parent
n=parent
parent=floor(n/2)
end
end
-- restores the heap invariant by moving an item down
localfunctionsiftDown(n)
localm-- position of the smaller child
while2*n<firstFreedo-- #children >= 1
-- swap the element with its smaller child
if2*n+1==firstFreethen-- n does not have a right child --> it only has a left child as #children >= 1
m=2*n-- left child
elseifheap[2*n].time<heap[2*n+1].timethen-- #children = 2 and left child < right child
m=2*n-- left child
else-- #children = 2 and right child is smaller than the left one
m=2*n+1-- right
end
ifheap[n].time<=heap[m].timethen-- n is <= its smallest child --> heap invariant restored
return
end
heap[n],heap[m]=heap[m],heap[n]
n=m
end
end
-- inserts a new element into the heap
functioninsert(ele)
heap[firstFree]=ele
siftUp(firstFree)
firstFree=firstFree+1
end
-- deletes the min element
functiondeleteMin()
localmin=heap[1]
firstFree=firstFree-1
heap[1]=heap[firstFree]
heap[firstFree]=nil
siftDown(1)
returnmin
end
-- removes multiple scheduled tasks from the heap
-- note that this function is comparatively slow by design as it has to check all tasks and allows partial matches
functionremoveAllMatching(f,mod,...)
-- remove all elements that match the signature, this destroyes the heap and leaves a normal array
localv,match
localfoundMatch=false
fori=#heap,1,-1do-- iterate backwards over the array to allow usage of table.remove
-- clean up sync spam timers and auto respond spam blockers
iftime>nextModSyncSpamUpdatethen
nextModSyncSpamUpdate=time+20
-- TODO: optimize this; using next(t, k) all the time on nearly empty hash tables is not a good idea...doesn't really matter here as modSyncSpam only very rarely contains more than 4 entries...
-- we now do this just every 20 seconds since the earlier assumption about modSyncSpam isn't true any longer
-- note that not removing entries at all would be just a small memory leak and not a problem (the sync functions themselves check the timestamp)