]>
Commit | Line | Data |
---|---|---|
6527f429 DM |
1 | /**\r |
2 | * @private\r | |
3 | * @class Ext.util.LruCache\r | |
4 | * @extend Ext.util.HashMap\r | |
5 | * A linked {@link Ext.util.HashMap HashMap} implementation which maintains most recently accessed\r | |
6 | * items at the end of the list, and purges the cache down to the most recently accessed {@link #maxSize} items\r | |
7 | * upon add.\r | |
8 | */\r | |
9 | Ext.define('Ext.util.LruCache', {\r | |
10 | extend: 'Ext.util.HashMap',\r | |
11 | \r | |
12 | config: {\r | |
13 | /** \r | |
14 | * @cfg {Number} maxSize The maximum size the cache is allowed to grow to before further additions cause\r | |
15 | * removal of the least recently used entry.\r | |
16 | */\r | |
17 | maxSize: null\r | |
18 | },\r | |
19 | \r | |
20 | /**\r | |
21 | * @inheritdoc\r | |
22 | */\r | |
23 | add: function(key, newValue) {\r | |
24 | var me = this,\r | |
25 | entry, last;\r | |
26 | \r | |
27 | me.removeAtKey(key);\r | |
28 | last = me.last;\r | |
29 | entry = {\r | |
30 | prev: last,\r | |
31 | next: null,\r | |
32 | key: key,\r | |
33 | value: newValue\r | |
34 | };\r | |
35 | \r | |
36 | \r | |
37 | if (last) {\r | |
38 | // If the list is not empty, update the last entry\r | |
39 | last.next = entry;\r | |
40 | } else {\r | |
41 | // List is empty\r | |
42 | me.first = entry;\r | |
43 | }\r | |
44 | me.last = entry;\r | |
45 | me.callParent([key, entry]);\r | |
46 | me.prune();\r | |
47 | return newValue;\r | |
48 | },\r | |
49 | \r | |
50 | /**\r | |
51 | * @private\r | |
52 | */\r | |
53 | insertBefore: function(key, newValue, sibling) {\r | |
54 | var me = this,\r | |
55 | existingKey,\r | |
56 | entry;\r | |
57 | \r | |
58 | // NOT an assignment.\r | |
59 | // If there is a following sibling\r | |
60 | if (sibling = this.map[this.findKey(sibling)]) {\r | |
61 | existingKey = me.findKey(newValue);\r | |
62 | \r | |
63 | // "new" value is in the list.\r | |
64 | if (existingKey) {\r | |
65 | me.unlinkEntry(entry = me.map[existingKey]);\r | |
66 | }\r | |
67 | // Genuinely new: create an entry for it.\r | |
68 | else {\r | |
69 | entry = {\r | |
70 | prev: sibling.prev,\r | |
71 | next: sibling,\r | |
72 | key: key,\r | |
73 | value: newValue\r | |
74 | };\r | |
75 | }\r | |
76 | \r | |
77 | if (sibling.prev) {\r | |
78 | entry.prev.next = entry;\r | |
79 | } else {\r | |
80 | me.first = entry;\r | |
81 | }\r | |
82 | entry.next = sibling;\r | |
83 | sibling.prev = entry;\r | |
84 | me.prune();\r | |
85 | return newValue;\r | |
86 | }\r | |
87 | // No following sibling, it's just an add.\r | |
88 | else {\r | |
89 | return me.add(key, newValue);\r | |
90 | }\r | |
91 | },\r | |
92 | \r | |
93 | /**\r | |
94 | * @inheritdoc\r | |
95 | */\r | |
96 | get: function(key) {\r | |
97 | var entry = this.map[key];\r | |
98 | if (entry) {\r | |
99 | \r | |
100 | // If it's not the end, move to end of list on get\r | |
101 | if (entry.next) {\r | |
102 | this.moveToEnd(entry);\r | |
103 | }\r | |
104 | return entry.value;\r | |
105 | }\r | |
106 | },\r | |
107 | \r | |
108 | /**\r | |
109 | * @private\r | |
110 | */\r | |
111 | removeAtKey: function(key) {\r | |
112 | this.unlinkEntry(this.map[key]);\r | |
113 | return this.callParent(arguments);\r | |
114 | },\r | |
115 | \r | |
116 | /**\r | |
117 | * @inheritdoc\r | |
118 | */\r | |
119 | clear: function(/* private */ initial) {\r | |
120 | this.first = this.last = null;\r | |
121 | return this.callParent([initial]);\r | |
122 | },\r | |
123 | \r | |
124 | /**\r | |
125 | * @private\r | |
126 | */\r | |
127 | unlinkEntry: function(entry) {\r | |
128 | // Stitch the list back up.\r | |
129 | if (entry) {\r | |
130 | if (entry.next) {\r | |
131 | entry.next.prev = entry.prev;\r | |
132 | } else {\r | |
133 | this.last = entry.prev;\r | |
134 | }\r | |
135 | if (entry.prev) {\r | |
136 | entry.prev.next = entry.next;\r | |
137 | } else {\r | |
138 | this.first = entry.next;\r | |
139 | }\r | |
140 | entry.prev = entry.next = null;\r | |
141 | }\r | |
142 | },\r | |
143 | \r | |
144 | /**\r | |
145 | * @private\r | |
146 | */\r | |
147 | moveToEnd: function(entry) {\r | |
148 | this.unlinkEntry(entry);\r | |
149 | \r | |
150 | // NOT an assignment.\r | |
151 | // If the list is not empty, update the last entry\r | |
152 | if (entry.prev = this.last) {\r | |
153 | this.last.next = entry;\r | |
154 | }\r | |
155 | // List is empty\r | |
156 | else {\r | |
157 | this.first = entry;\r | |
158 | }\r | |
159 | this.last = entry;\r | |
160 | },\r | |
161 | \r | |
162 | /**\r | |
163 | * @private\r | |
164 | */\r | |
165 | getArray: function(isKey) {\r | |
166 | var arr = [],\r | |
167 | entry = this.first;\r | |
168 | \r | |
169 | while (entry) {\r | |
170 | arr.push(isKey ? entry.key: entry.value);\r | |
171 | entry = entry.next;\r | |
172 | }\r | |
173 | return arr;\r | |
174 | },\r | |
175 | \r | |
176 | /**\r | |
177 | * Executes the specified function once for each item in the cache.\r | |
178 | * Returning false from the function will cease iteration.\r | |
179 | *\r | |
180 | * By default, iteration is from least recently used to most recent.\r | |
181 | *\r | |
182 | * The paramaters passed to the function are:\r | |
183 | * <div class="mdetail-params"><ul>\r | |
184 | * <li><b>key</b> : String<p class="sub-desc">The key of the item</p></li>\r | |
185 | * <li><b>value</b> : Number<p class="sub-desc">The value of the item</p></li>\r | |
186 | * <li><b>length</b> : Number<p class="sub-desc">The total number of items in the hash</p></li>\r | |
187 | * </ul></div>\r | |
188 | * @param {Function} fn The function to execute.\r | |
189 | * @param {Object} scope The scope (<code>this</code> reference) to execute in. Defaults to this LruCache.\r | |
190 | * @param {Boolean} [reverse=false] Pass <code>true</code> to iterate the list in reverse (most recent first) order.\r | |
191 | * @return {Ext.util.LruCache} this\r | |
192 | */\r | |
193 | each: function(fn, scope, reverse) {\r | |
194 | var me = this,\r | |
195 | entry = reverse ? me.last : me.first,\r | |
196 | length = me.length;\r | |
197 | \r | |
198 | scope = scope || me;\r | |
199 | while (entry) {\r | |
200 | if (fn.call(scope, entry.key, entry.value, length) === false) {\r | |
201 | break;\r | |
202 | }\r | |
203 | entry = reverse ? entry.prev : entry.next;\r | |
204 | }\r | |
205 | return me;\r | |
206 | },\r | |
207 | \r | |
208 | /**\r | |
209 | * @private\r | |
210 | */\r | |
211 | findKey: function(value) {\r | |
212 | var key,\r | |
213 | map = this.map;\r | |
214 | \r | |
215 | for (key in map) {\r | |
216 | // Attention. Differs from subclass in that this compares the value property\r | |
217 | // of the entry.\r | |
218 | if (map.hasOwnProperty(key) && map[key].value === value) {\r | |
219 | return key;\r | |
220 | }\r | |
221 | }\r | |
222 | return undefined;\r | |
223 | },\r | |
224 | \r | |
225 | /**\r | |
226 | * Performs a shallow copy on this haLruCachesh.\r | |
227 | * @return {Ext.util.HashMap} The new hash object.\r | |
228 | */\r | |
229 | clone: function() {\r | |
230 | var newCache = new this.self(this.initialConfig),\r | |
231 | map = this.map,\r | |
232 | key;\r | |
233 | \r | |
234 | newCache.suspendEvents();\r | |
235 | for (key in map) {\r | |
236 | if (map.hasOwnProperty(key)) {\r | |
237 | newCache.add(key, map[key].value);\r | |
238 | }\r | |
239 | }\r | |
240 | newCache.resumeEvents();\r | |
241 | return newCache;\r | |
242 | },\r | |
243 | \r | |
244 | /**\r | |
245 | * Purge the least recently used entries if the maxSize has been exceeded.\r | |
246 | */\r | |
247 | prune: function() {\r | |
248 | var me = this,\r | |
249 | max = me.getMaxSize(),\r | |
250 | purgeCount = max ? (me.length - max) : 0;\r | |
251 | \r | |
252 | if (purgeCount > 0) {\r | |
253 | for (; me.first && purgeCount; purgeCount--) {\r | |
254 | me.removeAtKey(me.first.key);\r | |
255 | }\r | |
256 | }\r | |
257 | },\r | |
258 | \r | |
259 | destroy: function() {\r | |
260 | this.first = this.last = null;\r | |
261 | this.callParent(); \r | |
262 | }\r | |
263 | \r | |
264 | /**\r | |
265 | * @method containsKey\r | |
266 | * @private\r | |
267 | */\r | |
268 | /**\r | |
269 | * @method contains\r | |
270 | * @private\r | |
271 | */\r | |
272 | /**\r | |
273 | * @method getKeys\r | |
274 | * @private\r | |
275 | */\r | |
276 | /**\r | |
277 | * @method getValues\r | |
278 | * @private\r | |
279 | */\r | |
280 | }); |