vanilla-wow-addons – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1  
2 --[[
3 Name: Compost-2.0
4 Revision: $Rev: 17406 $
5 Author: Tekkub Stoutwrithe (tekkub@gmail.com)
6 Website: http://wiki.wowace.com/index.php/CompostLib
7 Documentation: http://wiki.wowace.com/index.php/Compost-2.0_API_Documentation
8 SVN: svn://svn.wowace.com/root/trunk/CompostLib/Compost-2.0
9 Description: Recycle tables to reduce garbage generation
10 Dependencies: AceLibrary
11 ]]
12  
13 local vmajor, vminor = "Compost-2.0", "$Revision: 17406 $"
14  
15 if not AceLibrary then error(vmajor .. " requires AceLibrary.") end
16 if not AceLibrary:IsNewVersion(vmajor, vminor) then return end
17  
18 local lua51 = loadstring("return function(...) return ... end") and true or false
19 local lib = {}
20  
21  
22 -- Activate a new instance of this library
23 local function activate(self, oldLib, oldDeactivate)
24 if oldLib then -- if upgrading
25 self.var, self.k = oldLib.var, oldLib.k
26 else
27 self.k = { -- Constants go here
28 maxcache = 10, -- I think this is a good number, I'll change it later if necessary
29 }
30 self.var = { -- "Local" variables go here
31 cache = {},
32 secondarycache = {},
33 }
34  
35 -- This makes the secondary cache table a weak table, any values in it will be reclaimed
36 -- during a GC if there are no other references to them
37 setmetatable(self.var.secondarycache, {__mode = "v"})
38 end
39 if not self.var.tablechecks then
40 self.var.tablechecks = {}
41 setmetatable(self.var.tablechecks, {__mode = "kv"})
42 for i,v in ipairs(self.var.cache) do self.var.tablechecks[v] = true end
43 for i,v in ipairs(self.var.secondarycache) do self.var.tablechecks[v] = true end
44 end
45 if oldDeactivate then oldDeactivate(oldLib) end
46 end
47  
48  
49 -- Removes an empty table from the cache and returns it
50 -- or generates a new table if none available
51 function lib:GetTable()
52 if lua51 or self.var.disabled then return {} end
53  
54 if table.getn(self.var.cache) > 0 then
55 for i in pairs(self.var.cache) do
56 local t = table.remove(self.var.cache, i)
57 self.var.tablechecks[t] = nil
58 if next(t) then -- Table has been modified, someone holds a ref still, discard it
59 error("Someone is modifying tables reclaimed by Compost!")
60 self:IncDec("numdiscarded", 1)
61 else -- It's clean, we think... return it.
62 self:IncDec("totn", -1)
63 self:IncDec("numrecycled", 1)
64 return t
65 end
66 end
67 end
68  
69 if next(self.var.secondarycache) then
70 for i in pairs(self.var.secondarycache) do
71 local t = table.remove(self.var.secondarycache, i)
72 self.var.tablechecks[t] = nil
73 if next(t) then -- Table has been modified, someone holds a ref still, discard it
74 error("Someone is modifying tables reclaimed by Compost!")
75 self:IncDec("numdiscarded", 1)
76 else -- It's clean, we think... return it.
77 self:IncDec("totn", -1)
78 self:IncDec("numrecycled", 1)
79 return t
80 end
81 end
82 end
83  
84 self:IncDec("numnew", 1)
85 return {}
86 end
87  
88  
89 -- Returns a table, populated with any variables passed
90 -- basically: return {a1, a2, ... a20}
91 if lua51 then
92 --[[
93 function lib:Acquire(...)
94 return self:Populate({}, ...)
95 end
96 ]]
97 lib.Acquire = loadstring([[return function(self, ...)
98 return self:Populate({}, ...)
99 end]])()
100 else
101 function lib:Acquire(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
102 local t = self:GetTable()
103 return self:Populate(t,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
104 end
105 end
106  
107  
108 -- Acquires a table and fills it with values, hash style
109 -- basically: return {k1 = v1, k2 = v2, ... k10 = v10}
110 if lua51 then
111 --[[
112 function lib:AcquireHash(...)
113 return self:PopulateHash({}, ...)
114 end
115 ]]
116 lib.AcquireHash = loadstring([[return function(self, ...)
117 return self:PopulateHash({}, ...)
118 end]])()
119 else
120 function lib:AcquireHash(k1,v1,k2,v2,k3,v3,k4,v4,k5,v5,k6,v6,k7,v7,k8,v8,k9,v9,k10,v10)
121 local t = self:GetTable()
122 return self:PopulateHash(t,k1,v1,k2,v2,k3,v3,k4,v4,k5,v5,k6,v6,k7,v7,k8,v8,k9,v9,k10,v10)
123 end
124 end
125  
126  
127 -- Erases the table passed, fills it with the args passed, and returns it
128 -- Essentially the same as doing Reclaim then Acquire, except the same table is reused
129 if lua51 then
130 --[[
131 function lib:Recycle(t, ...)
132 t = self:Erase(t)
133 return self:Populate(t, ...)
134 end
135 ]]
136 lib.Recycle = loadstring([[return function(self, t, ...)
137 t = self:Erase(t)
138 return self:Populate(t, ...)
139 end]])()
140 else
141 function lib:Recycle(t,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
142 t = self:Erase(t)
143 return self:Populate(t,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
144 end
145 end
146  
147  
148 -- Erases the table passed, fills it with the args passed, and returns it
149 -- Essentially the same as doing Reclaim then AcquireHash, except the same table is reused
150 if lua51 then
151 --[[
152 function lib:RecycleHash(t, ...)
153 t = self:Erase(t)
154 return self:PopulateHash(t, ...)
155 end
156 ]]
157 lib.RecycleHash = loadstring([[return function(self, t, ...)
158 t = self:Erase(t)
159 return self:PopulateHash(t, ...)
160 end]])()
161 else
162 function lib:RecycleHash(t,k1,v1,k2,v2,k3,v3,k4,v4,k5,v5,k6,v6,k7,v7,k8,v8,k9,v9,k10,v10)
163 t = self:Erase(t)
164 return self:PopulateHash(t,k1,v1,k2,v2,k3,v3,k4,v4,k5,v5,k6,v6,k7,v7,k8,v8,k9,v9,k10,v10)
165 end
166 end
167  
168 -- Returns a table to the cache
169 -- All tables referenced inside the passed table will be reclaimed also
170 -- If a depth is passed, Reclaim will call itsself recursivly
171 -- to reclaim all tables contained in t to the depth specified
172 if lua51 then
173 function lib:Reclaim() end
174 else
175 function lib:Reclaim(t, depth)
176 if type(t) ~= "table" or self.var.disabled then return end
177 self:assert(not self.var.tablechecks[t], "Cannot reclaim a table twice")
178  
179 if not self:ItemsInSecondaryCache() then self.var.totn = table.getn(self.var.cache) end
180  
181 if depth and depth > 0 then
182 for i in pairs(t) do
183 if type(t[i]) == "table" then self:Reclaim(t[i], depth - 1) end
184 end
185 end
186 self:Erase(t)
187 if self.k.maxcache and table.getn(self.var.cache) >= self.k.maxcache then
188 table.insert(self.var.secondarycache, t)
189 else
190 table.insert(self.var.cache, t)
191 end
192 self:IncDec("numreclaim", 1)
193 self:IncDec("totn", 1)
194 self.var.maxn = math.max(self.var.maxn or 0, self.var.totn)
195 self.var.tablechecks[t] = true
196 end
197 end
198  
199 -- Reclaims multiple tables, can take 10 recursive sets or 20 non-recursives,
200 -- or any combination of the two. Pass args in the following manner:
201 -- table1, depth1, tabl2, depth2, table3, table4, table5, depth5, ...
202 if lua51 then
203 function lib:ReclaimMulti() end
204 else
205 function lib:ReclaimMulti(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
206 if not a1 then return end
207 if type(a2) == "number" then
208 self:Reclaim(a1, a2)
209 self:ReclaimMulti(a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
210 else
211 self:Reclaim(a1)
212 self:ReclaimMulti(a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
213 end
214 end
215 end
216  
217 -- Erases the table passed, nothing more nothing less :)
218 -- Tables referenced inside the passed table are NOT erased
219 if lua51 then
220 function lib:Erase() return {} end
221 else
222 function lib:Erase(t)
223 if type(t) ~= "table" then return end
224 if self.var.disabled then return {} end
225 local mem = gcinfo()
226 setmetatable(t, nil)
227 for i in pairs(t) do
228 t[i] = nil
229 end
230 t.reset = 1
231 t.reset = nil
232 table.setn(t, 0)
233 self:IncDec("memfreed", math.abs(gcinfo() - mem))
234 self:IncDec("numerased", 1)
235 return t
236 end
237 end
238  
239 -- Fills the table passed with the args passed
240 if lua51 then
241 --[[
242 function lib:Populate(t, a, ...)
243 if not t then return
244 elseif a ~= nil then
245 table.insert(t, a)
246 return self:Populate(t, ...)
247 else return t end
248 end
249 ]]
250 lib.Populate = loadstring([[return function(self, t, a, ...)
251 if not t then return
252 elseif a ~= nil then
253 table.insert(t, a)
254 return self:Populate(t, ...)
255 else return t end
256 end]])()
257 else
258 function lib:Populate(t,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
259 if not t then return end
260 if a1 ~= nil then table.insert(t, a1) end
261 if a2 ~= nil then table.insert(t, a2) end
262 if a3 ~= nil then table.insert(t, a3) end
263 if a4 ~= nil then table.insert(t, a4) end
264 if a5 ~= nil then table.insert(t, a5) end
265 if a6 ~= nil then table.insert(t, a6) end
266 if a7 ~= nil then table.insert(t, a7) end
267 if a8 ~= nil then table.insert(t, a8) end
268 if a9 ~= nil then table.insert(t, a9) end
269 if a10 ~= nil then table.insert(t, a10) end
270 if a11 ~= nil then table.insert(t, a11) end
271 if a12 ~= nil then table.insert(t, a12) end
272 if a13 ~= nil then table.insert(t, a13) end
273 if a14 ~= nil then table.insert(t, a14) end
274 if a15 ~= nil then table.insert(t, a15) end
275 if a16 ~= nil then table.insert(t, a16) end
276 if a17 ~= nil then table.insert(t, a17) end
277 if a18 ~= nil then table.insert(t, a18) end
278 if a19 ~= nil then table.insert(t, a19) end
279 if a20 ~= nil then table.insert(t, a20) end
280 return t
281 end
282 end
283  
284 -- Same as Populate, but takes 10 key-value pairs instead
285 if lua51 then
286 --[[
287 function lib:PopulateHash(t, k, v, ...)
288 if not t then return
289 elseif k ~= nil then
290 t[k] = v
291 return self:PopulateHash(t, ...)
292 else return t end
293 end
294 ]]
295 lib.PopulateHash = loadstring([[return function(self, t, k, v, ...)
296 if not t then return
297 elseif k ~= nil then
298 t[k] = v
299 return self:PopulateHash(t, ...)
300 else return t end
301 end]])()
302 else
303 function lib:PopulateHash(t,k1,v1,k2,v2,k3,v3,k4,v4,k5,v5,k6,v6,k7,v7,k8,v8,k9,v9,k10,v10)
304 if not t then return end
305 if k1 ~= nil then t[k1] = v1 end
306 if k2 ~= nil then t[k2] = v2 end
307 if k3 ~= nil then t[k3] = v3 end
308 if k4 ~= nil then t[k4] = v4 end
309 if k5 ~= nil then t[k5] = v5 end
310 if k6 ~= nil then t[k6] = v6 end
311 if k7 ~= nil then t[k7] = v7 end
312 if k8 ~= nil then t[k8] = v8 end
313 if k9 ~= nil then t[k9] = v9 end
314 if k10 ~= nil then t[k10] = v10 end
315 return t
316 end
317 end
318  
319 function lib:IncDec(variable, diff)
320 self.var[variable] = (self.var[variable] or 0) + diff
321 end
322  
323  
324 function lib:ItemsInSecondaryCache()
325 for i in pairs(self.var.secondarycache) do return true end
326 end
327  
328  
329 function lib:GetSecondaryCacheSize()
330 local n = 0
331 for i in pairs(self.var.secondarycache) do n = n + 1 end
332 return n
333 end
334  
335  
336 -- Prints out statistics on table recycling
337 -- /script CompostLib:GetInstance("compost-1"):Stats()
338 function lib:Stats()
339 if self.var.disabled then ChatFrame1:AddMessage("CompostLib is disabled!")
340 else ChatFrame1:AddMessage(
341 string.format(
342 "|cff00ff00New: %d|r | |cffffff00Recycled: %d|r | |cff00ffffMain: %d|r | |cffff0000Secondary: %d|r | |cffff8800Max %d|r | |cff888888Erases: %d|r | |cffff00ffMem Saved: %d KiB|r | |cffff0088Lost to GC: %d",
343 self.var.numnew or 0,
344 self.var.numrecycled or 0,
345 table.getn(self.var.cache),
346 self:GetSecondaryCacheSize(),
347 self.var.maxn or 0,
348 (self.var.numerased or 0) - (self.var.numreclaim or 0),
349 (self.var.memfreed or 0) + 32/1024*(self.var.numrecycled or 0),
350 (self.var.numreclaim or 0) - (self.var.numrecycled or 0) - table.getn(self.var.cache)))
351 end
352 end
353  
354 setmetatable(lib, { __call = lib.Acquire })
355  
356 --------------------------------
357 -- Load this bitch! --
358 --------------------------------
359 AceLibrary:Register(lib, vmajor, vminor, activate)
360 lib = nil