1) Исправления в связи со сменой API MySQL
[openlib.git] / www / resources / lru / lru.js
diff --git a/www/resources/lru/lru.js b/www/resources/lru/lru.js
new file mode 100644 (file)
index 0000000..d014942
--- /dev/null
@@ -0,0 +1,249 @@
+/**\r
+ * A doubly linked list-based Least Recently Used (LRU) cache. Will keep most\r
+ * recently used items while discarding least recently used items when its limit\r
+ * is reached.\r
+ *\r
+ * Licensed under MIT. Copyright (c) 2010 Rasmus Andersson <http://hunch.se/>\r
+ * See README.md for details.\r
+ *\r
+ * Illustration of the design:\r
+ *\r
+ *       entry             entry             entry             entry\r
+ *       ______            ______            ______            ______\r
+ *      | head |.newer => |      |.newer => |      |.newer => | tail |\r
+ *      |  A   |          |  B   |          |  C   |          |  D   |\r
+ *      |______| <= older.|______| <= older.|______| <= older.|______|\r
+ *\r
+ *  removed  <--  <--  <--  <--  <--  <--  <--  <--  <--  <--  <--  added\r
+ */\r
+function LRUCache (limit) {\r
+  // Current size of the cache. (Read-only).\r
+  this.size = 0;\r
+  // Maximum number of items this cache can hold.\r
+  this.limit = limit;\r
+  this._keymap = {};\r
+}\r
+\r
+/**\r
+ * Put <value> into the cache associated with <key>. Returns the entry which was\r
+ * removed to make room for the new entry. Otherwise undefined is returned\r
+ * (i.e. if there was enough room already).\r
+ */\r
+LRUCache.prototype.put = function(key, value) {\r
+  var entry = {key:key, value:value};\r
+  // Note: No protection agains replacing, and thus orphan entries. By design.\r
+  this._keymap[key] = entry;\r
+  if (this.tail) {\r
+    // link previous tail to the new tail (entry)\r
+    this.tail.newer = entry;\r
+    entry.older = this.tail;\r
+  } else {\r
+    // we're first in -- yay\r
+    this.head = entry;\r
+  }\r
+  // add new entry to the end of the linked list -- it's now the freshest entry.\r
+  this.tail = entry;\r
+  if (this.size === this.limit) {\r
+    // we hit the limit -- remove the head\r
+    return this.shift();\r
+  } else {\r
+    // increase the size counter\r
+    this.size++;\r
+  }\r
+}\r
+\r
+/**\r
+ * Purge the least recently used (oldest) entry from the cache. Returns the\r
+ * removed entry or undefined if the cache was empty.\r
+ *\r
+ * If you need to perform any form of finalization of purged items, this is a\r
+ * good place to do it. Simply override/replace this function:\r
+ *\r
+ *   var c = new LRUCache(123);\r
+ *   c.shift = function() {\r
+ *     var entry = LRUCache.prototype.shift.call(this);\r
+ *     doSomethingWith(entry);\r
+ *     return entry;\r
+ *   }\r
+ */\r
+LRUCache.prototype.shift = function() {\r
+  // todo: handle special case when limit == 1\r
+  var entry = this.head;\r
+  if (entry) {\r
+    if (this.head.newer) {\r
+      this.head = this.head.newer;\r
+      this.head.older = undefined;\r
+    } else {\r
+      this.head = undefined;\r
+    }\r
+    // Remove last strong reference to <entry> and remove links from the purged\r
+    // entry being returned:\r
+    entry.newer = entry.older = undefined;\r
+    // delete is slow, but we need to do this to avoid uncontrollable growth:\r
+    delete this._keymap[entry.key];\r
+  }\r
+  return entry;\r
+}\r
+\r
+/**\r
+ * Get and register recent use of <key>. Returns the value associated with <key>\r
+ * or undefined if not in cache.\r
+ */\r
+LRUCache.prototype.get = function(key, returnEntry) {\r
+  // First, find our cache entry\r
+  var entry = this._keymap[key];\r
+  if (entry === undefined) return; // Not cached. Sorry.\r
+  // As <key> was found in the cache, register it as being requested recently\r
+  if (entry === this.tail) {\r
+    // Already the most recenlty used entry, so no need to update the list\r
+    return entry.value;\r
+  }\r
+  // HEAD--------------TAIL\r
+  //   <.older   .newer>\r
+  //  <--- add direction --\r
+  //   A  B  C  <D>  E\r
+  if (entry.newer) {\r
+    if (entry === this.head)\r
+      this.head = entry.newer;\r
+    entry.newer.older = entry.older; // C <-- E.\r
+  }\r
+  if (entry.older)\r
+    entry.older.newer = entry.newer; // C. --> E\r
+  entry.newer = undefined; // D --x\r
+  entry.older = this.tail; // D. --> E\r
+  if (this.tail)\r
+    this.tail.newer = entry; // E. <-- D\r
+  this.tail = entry;\r
+  return returnEntry ? entry : entry.value;\r
+}\r
+\r
+// ----------------------------------------------------------------------------\r
+// Following code is optional and can be removed without breaking the core\r
+// functionality.\r
+\r
+/**\r
+ * Check if <key> is in the cache without registering recent use. Feasible if\r
+ * you do not want to chage the state of the cache, but only "peek" at it.\r
+ * Returns the entry associated with <key> if found, or undefined if not found.\r
+ */\r
+LRUCache.prototype.find = function(key) {\r
+  return this._keymap[key];\r
+}\r
+\r
+/**\r
+ * Update the value of entry with <key>. Returns the old value, or undefined if\r
+ * entry was not in the cache.\r
+ */\r
+LRUCache.prototype.set = function(key, value) {\r
+  var oldvalue, entry = this.get(key, true);\r
+  if (entry) {\r
+    oldvalue = entry.value;\r
+    entry.value = value;\r
+  } else {\r
+    oldvalue = this.put(key, value);\r
+    if (oldvalue) oldvalue = oldvalue.value;\r
+  }\r
+  return oldvalue;\r
+}\r
+\r
+/**\r
+ * Remove entry <key> from cache and return its value. Returns undefined if not\r
+ * found.\r
+ */\r
+LRUCache.prototype.remove = function(key) {\r
+  var entry = this._keymap[key];\r
+  if (!entry) return;\r
+  delete this._keymap[entry.key]; // need to do delete unfortunately\r
+  if (entry.newer && entry.older) {\r
+    // relink the older entry with the newer entry\r
+    entry.older.newer = entry.newer;\r
+    entry.newer.older = entry.older;\r
+  } else if (entry.newer) {\r
+    // remove the link to us\r
+    entry.newer.older = undefined;\r
+    // link the newer entry to head\r
+    this.head = entry.newer;\r
+  } else if (entry.older) {\r
+    // remove the link to us\r
+    entry.older.newer = undefined;\r
+    // link the newer entry to head\r
+    this.tail = entry.older;\r
+  } else {// if(entry.older === undefined && entry.newer === undefined) {\r
+    this.head = this.tail = undefined;\r
+  }\r
+\r
+  this.size--;\r
+  return entry.value;\r
+}\r
+\r
+/** Removes all entries */\r
+LRUCache.prototype.removeAll = function() {\r
+  // This should be safe, as we never expose strong refrences to the outside\r
+  this.head = this.tail = undefined;\r
+  this.size = 0;\r
+  this._keymap = {};\r
+}\r
+\r
+/**\r
+ * Return an array containing all keys of entries stored in the cache object, in\r
+ * arbitrary order.\r
+ */\r
+if (typeof Object.keys === 'function') {\r
+  LRUCache.prototype.keys = function() { return Object.keys(this._keymap); }\r
+} else {\r
+  LRUCache.prototype.keys = function() {\r
+    var keys = [];\r
+    for (var k in this._keymap) keys.push(k);\r
+    return keys;\r
+  }\r
+}\r
+\r
+/**\r
+ * Call `fun` for each entry. Starting with the newest entry if `desc` is a true\r
+ * value, otherwise starts with the oldest (head) enrty and moves towards the\r
+ * tail.\r
+ *\r
+ * `fun` is called with 3 arguments in the context `context`:\r
+ *   `fun.call(context, Object key, Object value, LRUCache self)`\r
+ */\r
+LRUCache.prototype.forEach = function(fun, context, desc) {\r
+  if (context === true) { desc = true; context = undefined; }\r
+  else if (typeof context !== 'object') context = this;\r
+  if (desc) {\r
+    var entry = this.tail;\r
+    while (entry) {\r
+      fun.call(context, entry.key, entry.value, this);\r
+      entry = entry.older;\r
+    }\r
+  } else {\r
+    var entry = this.head;\r
+    while (entry) {\r
+      fun.call(context, entry.key, entry.value, this);\r
+      entry = entry.newer;\r
+    }\r
+  }\r
+}\r
+\r
+/** Returns a JSON (array) representation */\r
+LRUCache.prototype.toJSON = function() {\r
+  var s = [], entry = this.head;\r
+  while (entry) {\r
+    s.push({key:entry.key.toJSON(), value:entry.value.toJSON()});\r
+    entry = entry.newer;\r
+  }\r
+  return s;\r
+}\r
+\r
+/** Returns a String representation */\r
+LRUCache.prototype.toString = function() {\r
+  var s = '', entry = this.head;\r
+  while (entry) {\r
+    s += String(entry.key)+':'+entry.value;\r
+    if (entry = entry.newer)\r
+      s += ' < ';\r
+  }\r
+  return s;\r
+}\r
+\r
+// Export ourselves\r
+if (typeof this === 'object') this.LRUCache = LRUCache;\r