Initial commit
[yaffs-website] / node_modules / lru-cache / lib / lru-cache.js
1 ;(function () { // closure for web browsers
2
3 if (typeof module === 'object' && module.exports) {
4   module.exports = LRUCache
5 } else {
6   // just set the global for non-node platforms.
7   this.LRUCache = LRUCache
8 }
9
10 function hOP (obj, key) {
11   return Object.prototype.hasOwnProperty.call(obj, key)
12 }
13
14 function naiveLength () { return 1 }
15
16 var didTypeWarning = false
17 function typeCheckKey(key) {
18   if (!didTypeWarning && typeof key !== 'string' && typeof key !== 'number') {
19     didTypeWarning = true
20     console.error(new TypeError("LRU: key must be a string or number. Almost certainly a bug! " + typeof key).stack)
21   }
22 }
23
24 function LRUCache (options) {
25   if (!(this instanceof LRUCache))
26     return new LRUCache(options)
27
28   if (typeof options === 'number')
29     options = { max: options }
30
31   if (!options)
32     options = {}
33
34   this._max = options.max
35   // Kind of weird to have a default max of Infinity, but oh well.
36   if (!this._max || !(typeof this._max === "number") || this._max <= 0 )
37     this._max = Infinity
38
39   this._lengthCalculator = options.length || naiveLength
40   if (typeof this._lengthCalculator !== "function")
41     this._lengthCalculator = naiveLength
42
43   this._allowStale = options.stale || false
44   this._maxAge = options.maxAge || null
45   this._dispose = options.dispose
46   this.reset()
47 }
48
49 // resize the cache when the max changes.
50 Object.defineProperty(LRUCache.prototype, "max",
51   { set : function (mL) {
52       if (!mL || !(typeof mL === "number") || mL <= 0 ) mL = Infinity
53       this._max = mL
54       if (this._length > this._max) trim(this)
55     }
56   , get : function () { return this._max }
57   , enumerable : true
58   })
59
60 // resize the cache when the lengthCalculator changes.
61 Object.defineProperty(LRUCache.prototype, "lengthCalculator",
62   { set : function (lC) {
63       if (typeof lC !== "function") {
64         this._lengthCalculator = naiveLength
65         this._length = this._itemCount
66         for (var key in this._cache) {
67           this._cache[key].length = 1
68         }
69       } else {
70         this._lengthCalculator = lC
71         this._length = 0
72         for (var key in this._cache) {
73           this._cache[key].length = this._lengthCalculator(this._cache[key].value)
74           this._length += this._cache[key].length
75         }
76       }
77
78       if (this._length > this._max) trim(this)
79     }
80   , get : function () { return this._lengthCalculator }
81   , enumerable : true
82   })
83
84 Object.defineProperty(LRUCache.prototype, "length",
85   { get : function () { return this._length }
86   , enumerable : true
87   })
88
89
90 Object.defineProperty(LRUCache.prototype, "itemCount",
91   { get : function () { return this._itemCount }
92   , enumerable : true
93   })
94
95 LRUCache.prototype.forEach = function (fn, thisp) {
96   thisp = thisp || this
97   var i = 0
98   var itemCount = this._itemCount
99
100   for (var k = this._mru - 1; k >= 0 && i < itemCount; k--) if (this._lruList[k]) {
101     i++
102     var hit = this._lruList[k]
103     if (isStale(this, hit)) {
104       del(this, hit)
105       if (!this._allowStale) hit = undefined
106     }
107     if (hit) {
108       fn.call(thisp, hit.value, hit.key, this)
109     }
110   }
111 }
112
113 LRUCache.prototype.keys = function () {
114   var keys = new Array(this._itemCount)
115   var i = 0
116   for (var k = this._mru - 1; k >= 0 && i < this._itemCount; k--) if (this._lruList[k]) {
117     var hit = this._lruList[k]
118     keys[i++] = hit.key
119   }
120   return keys
121 }
122
123 LRUCache.prototype.values = function () {
124   var values = new Array(this._itemCount)
125   var i = 0
126   for (var k = this._mru - 1; k >= 0 && i < this._itemCount; k--) if (this._lruList[k]) {
127     var hit = this._lruList[k]
128     values[i++] = hit.value
129   }
130   return values
131 }
132
133 LRUCache.prototype.reset = function () {
134   if (this._dispose && this._cache) {
135     for (var k in this._cache) {
136       this._dispose(k, this._cache[k].value)
137     }
138   }
139
140   this._cache = Object.create(null) // hash of items by key
141   this._lruList = Object.create(null) // list of items in order of use recency
142   this._mru = 0 // most recently used
143   this._lru = 0 // least recently used
144   this._length = 0 // number of items in the list
145   this._itemCount = 0
146 }
147
148 LRUCache.prototype.dump = function () {
149   var arr = []
150   var i = 0
151
152   for (var k = this._mru - 1; k >= 0 && i < this._itemCount; k--) if (this._lruList[k]) {
153     var hit = this._lruList[k]
154     if (!isStale(this, hit)) {
155       //Do not store staled hits
156       ++i
157       arr.push({
158         k: hit.key,
159         v: hit.value,
160         e: hit.now + (hit.maxAge || 0)
161       });
162     }
163   }
164   //arr has the most read first
165   return arr
166 }
167
168 LRUCache.prototype.dumpLru = function () {
169   return this._lruList
170 }
171
172 LRUCache.prototype.set = function (key, value, maxAge) {
173   maxAge = maxAge || this._maxAge
174   typeCheckKey(key)
175
176   var now = maxAge ? Date.now() : 0
177   var len = this._lengthCalculator(value)
178
179   if (hOP(this._cache, key)) {
180     if (len > this._max) {
181       del(this, this._cache[key])
182       return false
183     }
184     // dispose of the old one before overwriting
185     if (this._dispose)
186       this._dispose(key, this._cache[key].value)
187
188     this._cache[key].now = now
189     this._cache[key].maxAge = maxAge
190     this._cache[key].value = value
191     this._length += (len - this._cache[key].length)
192     this._cache[key].length = len
193     this.get(key)
194
195     if (this._length > this._max)
196       trim(this)
197
198     return true
199   }
200
201   var hit = new Entry(key, value, this._mru++, len, now, maxAge)
202
203   // oversized objects fall out of cache automatically.
204   if (hit.length > this._max) {
205     if (this._dispose) this._dispose(key, value)
206     return false
207   }
208
209   this._length += hit.length
210   this._lruList[hit.lu] = this._cache[key] = hit
211   this._itemCount ++
212
213   if (this._length > this._max)
214     trim(this)
215
216   return true
217 }
218
219 LRUCache.prototype.has = function (key) {
220   typeCheckKey(key)
221   if (!hOP(this._cache, key)) return false
222   var hit = this._cache[key]
223   if (isStale(this, hit)) {
224     return false
225   }
226   return true
227 }
228
229 LRUCache.prototype.get = function (key) {
230   typeCheckKey(key)
231   return get(this, key, true)
232 }
233
234 LRUCache.prototype.peek = function (key) {
235   typeCheckKey(key)
236   return get(this, key, false)
237 }
238
239 LRUCache.prototype.pop = function () {
240   var hit = this._lruList[this._lru]
241   del(this, hit)
242   return hit || null
243 }
244
245 LRUCache.prototype.del = function (key) {
246   typeCheckKey(key)
247   del(this, this._cache[key])
248 }
249
250 LRUCache.prototype.load = function (arr) {
251   //reset the cache
252   this.reset();
253
254   var now = Date.now()
255   //A previous serialized cache has the most recent items first
256   for (var l = arr.length - 1; l >= 0; l-- ) {
257     var hit = arr[l]
258     typeCheckKey(hit.k)
259     var expiresAt = hit.e || 0
260     if (expiresAt === 0) {
261       //the item was created without expiration in a non aged cache
262       this.set(hit.k, hit.v)
263     } else {
264       var maxAge = expiresAt - now
265       //dont add already expired items
266       if (maxAge > 0) this.set(hit.k, hit.v, maxAge)
267     }
268   }
269 }
270
271 function get (self, key, doUse) {
272   typeCheckKey(key)
273   var hit = self._cache[key]
274   if (hit) {
275     if (isStale(self, hit)) {
276       del(self, hit)
277       if (!self._allowStale) hit = undefined
278     } else {
279       if (doUse) use(self, hit)
280     }
281     if (hit) hit = hit.value
282   }
283   return hit
284 }
285
286 function isStale(self, hit) {
287   if (!hit || (!hit.maxAge && !self._maxAge)) return false
288   var stale = false;
289   var diff = Date.now() - hit.now
290   if (hit.maxAge) {
291     stale = diff > hit.maxAge
292   } else {
293     stale = self._maxAge && (diff > self._maxAge)
294   }
295   return stale;
296 }
297
298 function use (self, hit) {
299   shiftLU(self, hit)
300   hit.lu = self._mru ++
301   self._lruList[hit.lu] = hit
302 }
303
304 function trim (self) {
305   while (self._lru < self._mru && self._length > self._max)
306     del(self, self._lruList[self._lru])
307 }
308
309 function shiftLU (self, hit) {
310   delete self._lruList[ hit.lu ]
311   while (self._lru < self._mru && !self._lruList[self._lru]) self._lru ++
312 }
313
314 function del (self, hit) {
315   if (hit) {
316     if (self._dispose) self._dispose(hit.key, hit.value)
317     self._length -= hit.length
318     self._itemCount --
319     delete self._cache[ hit.key ]
320     shiftLU(self, hit)
321   }
322 }
323
324 // classy, since V8 prefers predictable objects.
325 function Entry (key, value, lu, length, now, maxAge) {
326   this.key = key
327   this.value = value
328   this.lu = lu
329   this.length = length
330   this.now = now
331   if (maxAge) this.maxAge = maxAge
332 }
333
334 })()