vanilla-wow-addons – Blame information for rev 1
?pathlinks?
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 |