diff options
| author | Igor Minar | 2011-02-16 20:04:39 -0500 | 
|---|---|---|
| committer | Igor Minar | 2011-11-30 11:03:42 -0500 | 
| commit | 497839f583ca3dd75583fb996bb764cbd6d7c4ac (patch) | |
| tree | c05ee9e7888da61b4e6c287d9e9f6d2fc457d8d0 /src/service/cacheFactory.js | |
| parent | 5487bdb3d1c905fb9453644f7e290c75dcee14c1 (diff) | |
| download | angular.js-497839f583ca3dd75583fb996bb764cbd6d7c4ac.tar.bz2 | |
feat($cacheFactory): add general purpose $cacheFactory service
Diffstat (limited to 'src/service/cacheFactory.js')
| -rw-r--r-- | src/service/cacheFactory.js | 152 | 
1 files changed, 152 insertions, 0 deletions
diff --git a/src/service/cacheFactory.js b/src/service/cacheFactory.js new file mode 100644 index 00000000..ccc69313 --- /dev/null +++ b/src/service/cacheFactory.js @@ -0,0 +1,152 @@ +/** + * @ngdoc object + * @name angular.module.ng.$cacheFactory + * + * @description + * Factory that constructs cache objects. + * + * + * @param {string} cacheId Name or id of the newly created cache. + * @param {object=} options Options object that specifies the cache behavior. Properties: + * + *   - `{number=}` `capacity` — turns the cache into LRU cache. + * + * @returns {object} Newly created cache object with the following set of methods: + * + * - `{string}` `id()` — Returns id or name of the cache. + * - `{number}` `size()` — Returns number of items currently in the cache + * - `{void}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache + * - `{(*}} `get({string} key) — Returns cached value for `key` or undefined for cache miss. + * - `{void}` `remove{string} key) — Removes a key-value pair from the cache. + * - `{void}` `removeAll() — Removes all cached values. + * + */ +function $CacheFactoryProvider() { + +  this.$get = function() { +    var caches = {}; + +    function cacheFactory(cacheId, options) { +      if (cacheId in caches) { +        throw Error('cacheId ' + cacheId + ' taken'); +      } + +      var size = 0, +          stats = extend({}, options, {id: cacheId}), +          data = {}, +          capacity = (options && options.capacity) || Number.MAX_VALUE, +          lruHash = {}, +          freshEnd = null, +          staleEnd = null; + +      return caches[cacheId] = { + +        put: function(key, value) { +          var lruEntry = lruHash[key] || (lruHash[key] = {key: key}); + +          refresh(lruEntry); + +          if (isUndefined(value)) return; +          if (!(key in data)) size++; +          data[key] = value; + +          if (size > capacity) { +            this.remove(staleEnd.key); +          } +        }, + + +        get: function(key) { +          var lruEntry = lruHash[key]; + +          if (!lruEntry) return; + +          refresh(lruEntry); + +          return data[key]; +        }, + + +        remove: function(key) { +          var lruEntry = lruHash[key]; + +          if (lruEntry == freshEnd) freshEnd = lruEntry.p; +          if (lruEntry == staleEnd) staleEnd = lruEntry.n; +          link(lruEntry.n,lruEntry.p); + +          delete lruHash[key]; +          delete data[key]; +          size--; +        }, + + +        removeAll: function() { +          data = {}; +          size = 0; +          lruHash = {}; +          freshEnd = staleEnd = null; +        }, + + +        destroy: function() { +          data = null; +          stats = null; +          lruHash = null; +          delete caches[cacheId]; +        }, + + +        info: function() { +          return extend({}, stats, {size: size}); +        } +      }; + + +      /** +       * makes the `entry` the freshEnd of the LRU linked list +       */ +      function refresh(entry) { +        if (entry != freshEnd) { +          if (!staleEnd) { +            staleEnd = entry; +          } else if (staleEnd == entry) { +            staleEnd = entry.n; +          } + +          link(entry.n, entry.p); +          link(entry, freshEnd); +          freshEnd = entry; +          freshEnd.n = null; +        } +      } + + +      /** +       * bidirectionally links two entries of the LRU linked list +       */ +      function link(nextEntry, prevEntry) { +        if (nextEntry != prevEntry) { +          if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify +          if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify +        } +      } +    } + + +    cacheFactory.info = function() { +      var info = {}; +      forEach(caches, function(cache, cacheId) { +        info[cacheId] = cache.info(); +      }); +      return info; +    }; + + +    cacheFactory.get = function(cacheId) { +      return caches[cacheId]; +    }; + + +    return cacheFactory; +  }; +}  | 
