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: 11579 $
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: 11579 $"
14  
15 if not AceLibrary then error(vmajor .. " requires AceLibrary.") end
16 if not AceLibrary:IsNewVersion(vmajor, vminor) then return end
17  
18 local lib = {}
19  
20 local table_setn
21 do
22 local version = GetBuildInfo()
23 if string.find(version, "^2%.") then
24 -- 2.0.0
25 table_setn = function() end
26 else
27 table_setn = table.setn
28 end
29 end
30  
31 -- Activate a new instance of this library
32 local function activate(self, oldLib, oldDeactivate)
33 if oldLib then -- if upgrading
34 self.var, self.k = oldLib.var, oldLib.k
35 else
36 self.k = { -- Constants go here
37 maxcache = 10, -- I think this is a good number, I'll change it later if necessary
38 }
39 self.var = { -- "Local" variables go here
40 cache = {},
41 secondarycache = {},
42 }
43  
44 -- This makes the secondary cache table a weak table, any values in it will be reclaimed
45 -- during a GC if there are no other references to them
46 setmetatable(self.var.secondarycache, {__mode = "v"})
47 end
48 if not self.var.tablechecks then
49 self.var.tablechecks = {}
50 setmetatable(self.var.tablechecks, {__mode = "kv"})
51 for i,v in ipairs(self.var.cache) do self.var.tablechecks[v] = true end
52 for i,v in ipairs(self.var.secondarycache) do self.var.tablechecks[v] = true end
53 end
54 if oldDeactivate then oldDeactivate(oldLib) end
55 end
56  
57  
58 -- Removes an empty table from the cache and returns it
59 -- or generates a new table if none available
60 function lib:GetTable()
61 if self.var.disabled then return {} end
62  
63 if table.getn(self.var.cache) > 0 then
64 for i in pairs(self.var.cache) do
65 local t = table.remove(self.var.cache, i)
66 self.var.tablechecks[t] = nil
67 if next(t) then -- Table has been modified, someone holds a ref still, discard it
68 error("Someone is modifying tables reclaimed by Compost!")
69 self:IncDec("numdiscarded", 1)
70 else -- It's clean, we think... return it.
71 self:IncDec("totn", -1)
72 self:IncDec("numrecycled", 1)
73 return t
74 end
75 end
76 end
77  
78 if next(self.var.secondarycache) then
79 for i in pairs(self.var.secondarycache) do
80 local t = table.remove(self.var.secondarycache, i)
81 self.var.tablechecks[t] = nil
82 if next(t) then -- Table has been modified, someone holds a ref still, discard it
83 error("Someone is modifying tables reclaimed by Compost!")
84 self:IncDec("numdiscarded", 1)
85 else -- It's clean, we think... return it.
86 self:IncDec("totn", -1)
87 self:IncDec("numrecycled", 1)
88 return t
89 end
90 end
91 end
92  
93 self:IncDec("numnew", 1)
94 return {}
95 end
96  
97  
98 -- Returns a table, populated with any variables passed
99 -- basically: return {a1, a2, ... a20}
100 function lib:Acquire(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
101 local t = self:GetTable()
102 return self:Populate(t,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
103 end
104  
105  
106 -- Acquires a table and fills it with values, hash style
107 -- basically: return {k1 = v1, k2 = v2, ... k10 = v10}
108 function lib:AcquireHash(k1,v1,k2,v2,k3,v3,k4,v4,k5,v5,k6,v6,k7,v7,k8,v8,k9,v9,k10,v10)
109 local t = self:GetTable()
110 return self:PopulateHash(t,k1,v1,k2,v2,k3,v3,k4,v4,k5,v5,k6,v6,k7,v7,k8,v8,k9,v9,k10,v10)
111 end
112  
113  
114 -- Erases the table passed, fills it with the args passed, and returns it
115 -- Essentially the same as doing Reclaim then Acquire, except the same table is reused
116 function lib:Recycle(t,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
117 t = self:Erase(t)
118 return self:Populate(t,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
119 end
120  
121  
122 -- Erases the table passed, fills it with the args passed, and returns it
123 -- Essentially the same as doing Reclaim then AcquireHash, except the same table is reused
124 function lib:RecycleHash(t,k1,v1,k2,v2,k3,v3,k4,v4,k5,v5,k6,v6,k7,v7,k8,v8,k9,v9,k10,v10)
125 t = self:Erase(t)
126 return self:PopulateHash(t,k1,v1,k2,v2,k3,v3,k4,v4,k5,v5,k6,v6,k7,v7,k8,v8,k9,v9,k10,v10)
127 end
128  
129  
130 -- Returns a table to the cache
131 -- All tables referenced inside the passed table will be reclaimed also
132 -- If a depth is passed, Reclaim will call itsself recursivly
133 -- to reclaim all tables contained in t to the depth specified
134 function lib:Reclaim(t, depth)
135 if type(t) ~= "table" or self.var.disabled then return end
136 self:assert(not self.var.tablechecks[t], "Cannot reclaim a table twice")
137  
138 if not self:ItemsInSecondaryCache() then self.var.totn = table.getn(self.var.cache) end
139  
140 if depth and depth > 0 then
141 for i in pairs(t) do
142 if type(t[i]) == "table" then self:Reclaim(t[i], depth - 1) end
143 end
144 end
145 self:Erase(t)
146 if self.k.maxcache and table.getn(self.var.cache) >= self.k.maxcache then
147 table.insert(self.var.secondarycache, t)
148 else
149 table.insert(self.var.cache, t)
150 end
151 self:IncDec("numreclaim", 1)
152 self:IncDec("totn", 1)
153 self.var.maxn = math.max(self.var.maxn or 0, self.var.totn)
154 self.var.tablechecks[t] = true
155 end
156  
157  
158 -- Reclaims multiple tables, can take 10 recursive sets or 20 non-recursives,
159 -- or any combination of the two. Pass args in the following manner:
160 -- table1, depth1, tabl2, depth2, table3, table4, table5, depth5, ...
161 function lib:ReclaimMulti(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
162 if not a1 then return end
163 if type(a2) == "number" then
164 self:Reclaim(a1, a2)
165 self:ReclaimMulti(a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
166 else
167 self:Reclaim(a1)
168 self:ReclaimMulti(a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
169 end
170 end
171  
172  
173 -- Erases the table passed, nothing more nothing less :)
174 -- Tables referenced inside the passed table are NOT erased
175 function lib:Erase(t)
176 if type(t) ~= "table" then return end
177 if self.var.disabled then return {} end
178 local mem = gcinfo()
179 setmetatable(t, nil)
180 for i in pairs(t) do
181 t[i] = nil
182 end
183 t.reset = 1
184 t.reset = nil
185 table_setn(t, 0)
186 self:IncDec("memfreed", math.abs(gcinfo() - mem))
187 self:IncDec("numerased", 1)
188 return t
189 end
190  
191  
192 -- Fills the table passed with the args passed
193 function lib:Populate(t,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
194 if not t then return end
195 if a1 ~= nil then table.insert(t, a1) end
196 if a2 ~= nil then table.insert(t, a2) end
197 if a3 ~= nil then table.insert(t, a3) end
198 if a4 ~= nil then table.insert(t, a4) end
199 if a5 ~= nil then table.insert(t, a5) end
200 if a6 ~= nil then table.insert(t, a6) end
201 if a7 ~= nil then table.insert(t, a7) end
202 if a8 ~= nil then table.insert(t, a8) end
203 if a9 ~= nil then table.insert(t, a9) end
204 if a10 ~= nil then table.insert(t, a10) end
205 if a11 ~= nil then table.insert(t, a11) end
206 if a12 ~= nil then table.insert(t, a12) end
207 if a13 ~= nil then table.insert(t, a13) end
208 if a14 ~= nil then table.insert(t, a14) end
209 if a15 ~= nil then table.insert(t, a15) end
210 if a16 ~= nil then table.insert(t, a16) end
211 if a17 ~= nil then table.insert(t, a17) end
212 if a18 ~= nil then table.insert(t, a18) end
213 if a19 ~= nil then table.insert(t, a19) end
214 if a20 ~= nil then table.insert(t, a20) end
215 return t
216 end
217  
218  
219 -- Same as Populate, but takes 10 key-value pairs instead
220 function lib:PopulateHash(t,k1,v1,k2,v2,k3,v3,k4,v4,k5,v5,k6,v6,k7,v7,k8,v8,k9,v9,k10,v10)
221 if not t then return end
222 if k1 ~= nil then t[k1] = v1 end
223 if k2 ~= nil then t[k2] = v2 end
224 if k3 ~= nil then t[k3] = v3 end
225 if k4 ~= nil then t[k4] = v4 end
226 if k5 ~= nil then t[k5] = v5 end
227 if k6 ~= nil then t[k6] = v6 end
228 if k7 ~= nil then t[k7] = v7 end
229 if k8 ~= nil then t[k8] = v8 end
230 if k9 ~= nil then t[k9] = v9 end
231 if k10 ~= nil then t[k10] = v10 end
232 return t
233 end
234  
235  
236 function lib:IncDec(variable, diff)
237 self.var[variable] = (self.var[variable] or 0) + diff
238 end
239  
240  
241 function lib:ItemsInSecondaryCache()
242 for i in pairs(self.var.secondarycache) do return true end
243 end
244  
245  
246 function lib:GetSecondaryCacheSize()
247 local n = 0
248 for i in pairs(self.var.secondarycache) do n = n + 1 end
249 return n
250 end
251  
252  
253 -- Prints out statistics on table recycling
254 -- /script CompostLib:GetInstance("compost-1"):Stats()
255 function lib:Stats()
256 if self.var.disabled then ChatFrame1:AddMessage("CompostLib is disabled!")
257 else ChatFrame1:AddMessage(
258 string.format(
259 "|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",
260 self.var.numnew or 0,
261 self.var.numrecycled or 0,
262 table.getn(self.var.cache),
263 self:GetSecondaryCacheSize(),
264 self.var.maxn or 0,
265 (self.var.numerased or 0) - (self.var.numreclaim or 0),
266 (self.var.memfreed or 0) + 32/1024*(self.var.numrecycled or 0),
267 (self.var.numreclaim or 0) - (self.var.numrecycled or 0) - table.getn(self.var.cache)))
268 end
269 end
270  
271 setmetatable(lib, { __call = lib.Acquire })
272  
273 --------------------------------
274 -- Load this bitch! --
275 --------------------------------
276 AceLibrary:Register(lib, vmajor, vminor, activate)
277 lib = nil