From c99ec2d834950115b0ae381fcbc890acee098177 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Mon, 4 Feb 2013 17:29:46 +0100 Subject: [PATCH 1/8] Add ol.structs.LRUCache --- src/ol/structs/lrucache.js | 271 ++++++++++++++++++++++++++++++++++ test/spec/ol/lrucache.test.js | 162 ++++++++++++++++++++ 2 files changed, 433 insertions(+) create mode 100644 src/ol/structs/lrucache.js create mode 100644 test/spec/ol/lrucache.test.js diff --git a/src/ol/structs/lrucache.js b/src/ol/structs/lrucache.js new file mode 100644 index 0000000000..863859bdba --- /dev/null +++ b/src/ol/structs/lrucache.js @@ -0,0 +1,271 @@ +goog.provide('ol.structs.LRUCache'); + +goog.require('goog.asserts'); +goog.require('goog.object'); + + + +/** + * @constructor + * Implements a Least-Recently-Used cache where the keys do not conflict with + * Object's properties (e.g. 'hasOwnProperty' is not allowed as a key). Expiring + * items from the cache is the responsibility of the user. + */ +ol.structs.LRUCache = function() { + + /** + * @private + * @type {number} + */ + this.count_ = 0; + + /** + * @private + * @type {Object.} + */ + this.entries_ = {}; + + /** + * @private + * @type {?ol.structs.LRUCacheEntry_} + */ + this.oldest_ = null; + + /** + * @private + * @type {?ol.structs.LRUCacheEntry_} + */ + this.newest_ = null; + +}; + + +/** + * FIXME empty description for jsdoc + */ +ol.structs.LRUCache.prototype.assertValid = function() { + if (this.count_ === 0) { + goog.asserts.assert(goog.object.isEmpty(this.entries_)); + goog.asserts.assert(goog.isNull(this.oldest_)); + goog.asserts.assert(goog.isNull(this.newest_)); + } else { + goog.asserts.assert(goog.object.getCount(this.entries_) == this.count_); + goog.asserts.assert(!goog.isNull(this.oldest_)); + goog.asserts.assert(goog.isNull(this.oldest_.older)); + goog.asserts.assert(!goog.isNull(this.newest_)); + goog.asserts.assert(goog.isNull(this.newest_.newer)); + var i, entry; + var older = null; + i = 0; + for (entry = this.oldest_; !goog.isNull(entry); entry = entry.newer) { + goog.asserts.assert(entry.older === older); + older = entry; + ++i; + } + goog.asserts.assert(i == this.count_); + var newer = null; + i = 0; + for (entry = this.newest_; !goog.isNull(entry); entry = entry.older) { + goog.asserts.assert(entry.newer === newer); + newer = entry; + ++i; + } + goog.asserts.assert(i == this.count_); + } +}; + + +/** + * FIXME empty description for jsdoc + */ +ol.structs.LRUCache.prototype.clear = function() { + this.count_ = 0; + this.entries_ = {}; + this.oldest_ = null; + this.newest_ = null; +}; + + +/** + * @param {string} key Key. + * @return {boolean} Contains key. + */ +ol.structs.LRUCache.prototype.containsKey = function(key) { + return key in this.entries_; +}; + + +/** + * @param {Function} f Function. + * @param {Object=} opt_obj Object. + */ +ol.structs.LRUCache.prototype.forEach = function(f, opt_obj) { + var entry = this.oldest_; + while (!goog.isNull(entry)) { + f.call(opt_obj, entry.value, entry.key, this); + entry = entry.newer; + } +}; + + +/** + * @param {string} key Key. + * @return {*} Value. + */ +ol.structs.LRUCache.prototype.get = function(key) { + var entry = this.entries_[key]; + goog.asserts.assert(goog.isDef(entry)); + if (entry === this.newest_) { + return entry.value; + } else if (entry === this.oldest_) { + this.oldest_ = this.oldest_.newer; + this.oldest_.older = null; + } else { + entry.newer.older = entry.older; + entry.older.newer = entry.newer; + } + entry.newer = null; + entry.older = this.newest_; + this.newest_.newer = entry; + this.newest_ = entry; + return entry.value; +}; + + +/** + * @return {number} Count. + */ +ol.structs.LRUCache.prototype.getCount = function() { + return this.count_; +}; + + +/** + * @return {Array.} Keys. + */ +ol.structs.LRUCache.prototype.getKeys = function() { + var keys = new Array(this.count_); + var i = 0; + var entry; + for (entry = this.newest_; !goog.isNull(entry); entry = entry.older) { + keys[i++] = entry.key; + } + goog.asserts.assert(i == this.count_); + return keys; +}; + + +/** + * @return {string} Last key. + */ +ol.structs.LRUCache.prototype.getLastKey = function() { + goog.asserts.assert(!goog.isNull(this.newest_)); + return this.newest_.key; +}; + + +/** + * @return {Array} Values. + */ +ol.structs.LRUCache.prototype.getValues = function() { + var values = new Array(this.count_); + var i = 0; + var entry; + for (entry = this.newest_; !goog.isNull(entry); entry = entry.older) { + values[i++] = entry.value; + } + goog.asserts.assert(i == this.count_); + return values; +}; + + +/** + * @return {*} Last value. + */ +ol.structs.LRUCache.prototype.peekLast = function() { + goog.asserts.assert(!goog.isNull(this.oldest_)); + return this.oldest_.value; +}; + + +/** + * @return {string} Last key. + */ +ol.structs.LRUCache.prototype.peekLastKey = function() { + goog.asserts.assert(!goog.isNull(this.oldest_)); + return this.oldest_.key; +}; + + +/** + * @return {*} value Value. + */ +ol.structs.LRUCache.prototype.pop = function() { + goog.asserts.assert(!goog.isNull(this.oldest_)); + goog.asserts.assert(!goog.isNull(this.newest_)); + var entry = this.oldest_; + goog.asserts.assert(entry.key in this.entries_); + delete this.entries_[entry.key]; + if (!goog.isNull(entry.newer)) { + entry.newer.older = null; + } + this.oldest_ = entry.newer; + if (goog.isNull(this.oldest_)) { + this.newest_ = null; + } + --this.count_; + return entry.value; +}; + + +/** + * @param {string} key Key. + * @param {*} value Value. + */ +ol.structs.LRUCache.prototype.set = function(key, value) { + goog.asserts.assert(!(key in {})); + goog.asserts.assert(!(key in this.entries_)); + var entry = new ol.structs.LRUCacheEntry_(key, value, null, this.newest_); + if (goog.isNull(this.newest_)) { + this.oldest_ = entry; + } else { + this.newest_.newer = entry; + } + this.newest_ = entry; + this.entries_[key] = entry; + ++this.count_; +}; + + + +/** + * @constructor + * @private + * @param {string} key Key. + * @param {*} value Value. + * @param {ol.structs.LRUCacheEntry_} newer Newer. + * @param {ol.structs.LRUCacheEntry_} older Older. + */ +ol.structs.LRUCacheEntry_ = function(key, value, newer, older) { + + /** + * @type {string} + */ + this.key = key; + + /** + * @type {*} + */ + this.value = value; + + /** + * @type {ol.structs.LRUCacheEntry_} + */ + this.newer = newer; + + /** + * @type {ol.structs.LRUCacheEntry_} + */ + this.older = older; + +}; diff --git a/test/spec/ol/lrucache.test.js b/test/spec/ol/lrucache.test.js new file mode 100644 index 0000000000..7ceb8a0115 --- /dev/null +++ b/test/spec/ol/lrucache.test.js @@ -0,0 +1,162 @@ +goog.provide('ol.test.LRUCache'); + + +describe('ol.structs.LRUCache', function() { + + var lruCache; + + function fillLRUCache(lruCache) { + lruCache.set('a', 0); + lruCache.set('b', 1); + lruCache.set('c', 2); + lruCache.set('d', 3); + } + + beforeEach(function() { + lruCache = new ol.structs.LRUCache(); + }); + + describe('empty cache', function() { + it('has size zero', function() { + expect(lruCache.getCount()).toEqual(0); + }); + it('has no keys', function() { + expect(lruCache.getKeys()).toEqual([]); + }); + it('has no values', function() { + expect(lruCache.getValues()).toEqual([]); + }); + }); + + describe('populating', function() { + it('returns the correct size', function() { + fillLRUCache(lruCache); + expect(lruCache.getCount()).toEqual(4); + }); + it('contains the correct keys in the correct order', function() { + fillLRUCache(lruCache); + expect(lruCache.getKeys()).toEqual(['d', 'c', 'b', 'a']); + }); + it('contains the correct values in the correct order', function() { + fillLRUCache(lruCache); + expect(lruCache.getValues()).toEqual([3, 2, 1, 0]); + }); + it('reports which keys are contained', function() { + fillLRUCache(lruCache); + expect(lruCache.containsKey('a')).toBeTruthy(); + expect(lruCache.containsKey('b')).toBeTruthy(); + expect(lruCache.containsKey('c')).toBeTruthy(); + expect(lruCache.containsKey('d')).toBeTruthy(); + expect(lruCache.containsKey('e')).toBeFalsy(); + }); + }); + + describe('getting the oldest key', function() { + it('moves the key to newest position', function() { + fillLRUCache(lruCache); + lruCache.get('a'); + expect(lruCache.getCount()).toEqual(4); + expect(lruCache.getKeys()).toEqual(['a', 'd', 'c', 'b']); + expect(lruCache.getValues()).toEqual([0, 3, 2, 1]); + }); + }); + + describe('getting a key in the middle', function() { + it('moves the key to newest position', function() { + fillLRUCache(lruCache); + lruCache.get('b'); + expect(lruCache.getCount()).toEqual(4); + expect(lruCache.getKeys()).toEqual(['b', 'd', 'c', 'a']); + expect(lruCache.getValues()).toEqual([1, 3, 2, 0]); + }); + }); + + describe('getting the newest key', function() { + it('maintains the key to newest position', function() { + fillLRUCache(lruCache); + lruCache.get('d'); + expect(lruCache.getCount()).toEqual(4); + expect(lruCache.getKeys()).toEqual(['d', 'c', 'b', 'a']); + expect(lruCache.getValues()).toEqual([3, 2, 1, 0]); + }); + }); + + describe('setting a new value', function() { + it('adds it as the newest value', function() { + fillLRUCache(lruCache); + lruCache.set('e', 4); + expect(lruCache.getKeys()).toEqual(['e', 'd', 'c', 'b', 'a']); + expect(lruCache.getValues()).toEqual([4, 3, 2, 1, 0]); + }); + }); + + describe('setting an existing value', function() { + it('raises an exception', function() { + fillLRUCache(lruCache); + expect(function() { + lruCache.set('a', 0); + }).toThrow(); + }); + }); + + describe('setting a disallowed key', function() { + it('raises an exception', function() { + expect(function() { + lruCache.set('hasOwnProperty', 0); + }).toThrow(); + }); + }); + + describe('popping a value', function() { + it('returns the least-recent-used value', function() { + fillLRUCache(lruCache); + expect(lruCache.pop()).toEqual(0); + expect(lruCache.getCount()).toEqual(3); + expect(lruCache.containsKey('a')).toBeFalsy(); + expect(lruCache.pop()).toEqual(1); + expect(lruCache.getCount()).toEqual(2); + expect(lruCache.containsKey('b')).toBeFalsy(); + expect(lruCache.pop()).toEqual(2); + expect(lruCache.getCount()).toEqual(1); + expect(lruCache.containsKey('c')).toBeFalsy(); + expect(lruCache.pop()).toEqual(3); + expect(lruCache.getCount()).toEqual(0); + expect(lruCache.containsKey('d')).toBeFalsy(); + }); + }); + + describe('peeking at the last value', function() { + it('returns the last key', function() { + fillLRUCache(lruCache); + expect(lruCache.peekLast()).toEqual(0); + }); + it('throws an exception when the cache is empty', function() { + expect(function() { + lruCache.peekLast(); + }).toThrow(); + }); + }); + + describe('peeking at the last key', function() { + it('returns the last key', function() { + fillLRUCache(lruCache); + expect(lruCache.peekLastKey()).toEqual('a'); + }); + it('throws an exception when the cache is empty', function() { + expect(function() { + lruCache.peekLastKey(); + }).toThrow(); + }); + }); + + describe('clearing the cache', function() { + it('clears the cache', function() { + fillLRUCache(lruCache); + lruCache.clear(); + expect(lruCache.getCount()).toEqual(0); + expect(lruCache.getKeys()).toEqual([]); + expect(lruCache.getValues()).toEqual([]); + }); + }); + +}); From 06eb3040afdd792a752a75289900ccea2a9dd614 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Mon, 4 Feb 2013 17:30:10 +0100 Subject: [PATCH 2/8] Port ol.TileCache to ol.structs.LRUCache --- src/ol/tilecache.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ol/tilecache.js b/src/ol/tilecache.js index 2972766682..0fd289c2b9 100644 --- a/src/ol/tilecache.js +++ b/src/ol/tilecache.js @@ -2,7 +2,7 @@ goog.provide('ol.TileCache'); goog.require('ol.Tile'); goog.require('ol.TileRange'); -goog.require('ol.structs.LinkedMap'); +goog.require('ol.structs.LRUCache'); /** @@ -14,12 +14,12 @@ ol.DEFAULT_TILE_CACHE_HIGH_WATER_MARK = 512; /** * @constructor - * @extends {ol.structs.LinkedMap} + * @extends {ol.structs.LRUCache} * @param {number=} opt_highWaterMark High water mark. */ ol.TileCache = function(opt_highWaterMark) { - goog.base(this, undefined, true); + goog.base(this); /** * @private @@ -29,7 +29,7 @@ ol.TileCache = function(opt_highWaterMark) { opt_highWaterMark : ol.DEFAULT_TILE_CACHE_HIGH_WATER_MARK; }; -goog.inherits(ol.TileCache, ol.structs.LinkedMap); +goog.inherits(ol.TileCache, ol.structs.LRUCache); /** From 1f460975bb3106898cca101faab3cc9b5b7ee873 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Mon, 4 Feb 2013 17:30:49 +0100 Subject: [PATCH 3/8] Port WebGL texture cache to ol.structs.LRUCache --- src/ol/renderer/webgl/webglmaprenderer.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ol/renderer/webgl/webglmaprenderer.js b/src/ol/renderer/webgl/webglmaprenderer.js index 6f754ba7c0..0af57fa846 100644 --- a/src/ol/renderer/webgl/webglmaprenderer.js +++ b/src/ol/renderer/webgl/webglmaprenderer.js @@ -19,7 +19,7 @@ goog.require('ol.renderer.Map'); goog.require('ol.renderer.webgl.FragmentShader'); goog.require('ol.renderer.webgl.TileLayer'); goog.require('ol.renderer.webgl.VertexShader'); -goog.require('ol.structs.LinkedMap'); +goog.require('ol.structs.LRUCache'); goog.require('ol.webgl'); goog.require('ol.webgl.WebGLContextEventType'); @@ -183,9 +183,9 @@ ol.renderer.webgl.Map = function(container, map) { /** * @private - * @type {ol.structs.LinkedMap} + * @type {ol.structs.LRUCache} */ - this.textureCache_ = new ol.structs.LinkedMap(undefined, true); + this.textureCache_ = new ol.structs.LRUCache(); /** * @private From c34fe519c04e94ddd6ceeaa7cdebc8c9502fb457 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Mon, 4 Feb 2013 17:31:02 +0100 Subject: [PATCH 4/8] Remove ol.structs.LinkedMap --- src/ol/structs/linkedmap.js | 486 ------------------------------------ 1 file changed, 486 deletions(-) delete mode 100644 src/ol/structs/linkedmap.js diff --git a/src/ol/structs/linkedmap.js b/src/ol/structs/linkedmap.js deleted file mode 100644 index 63971748c5..0000000000 --- a/src/ol/structs/linkedmap.js +++ /dev/null @@ -1,486 +0,0 @@ -// Copyright 2007 The Closure Library Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS-IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// FIXME replace this with goog.structs.LinkedMap when goog.structs.LinkedMap -// adds peekLastKey() - -/** - * @fileoverview A LinkedMap data structure that is accessed using key/value - * pairs like an ordinary Map, but which guarantees a consistent iteration - * order over its entries. The iteration order is either insertion order (the - * default) or ordered from most recent to least recent use. By setting a fixed - * size, the LRU version of the LinkedMap makes an effective object cache. This - * data structure is similar to Java's LinkedHashMap. - * - * @author brenneman@google.com (Shawn Brenneman) - */ - - -goog.provide('ol.structs.LinkedMap'); - -goog.require('goog.structs.Map'); - - - -/** - * Class for a LinkedMap datastructure, which combines O(1) map access for - * key/value pairs with a linked list for a consistent iteration order. Sample - * usage: - * - *
- * var m = new LinkedMap();
- * m.set('param1', 'A');
- * m.set('param2', 'B');
- * m.set('param3', 'C');
- * alert(m.getKeys()); // param1, param2, param3
- *
- * var c = new LinkedMap(5, true);
- * for (var i = 0; i < 10; i++) {
- *   c.set('entry' + i, false);
- * }
- * alert(c.getKeys()); // entry9, entry8, entry7, entry6, entry5
- *
- * c.set('entry5', true);
- * c.set('entry1', false);
- * alert(c.getKeys()); // entry1, entry5, entry9, entry8, entry7
- * 
- * - * @param {number=} opt_maxCount The maximum number of objects to store in the - * LinkedMap. If unspecified or 0, there is no maximum. - * @param {boolean=} opt_cache When set, the LinkedMap stores items in order - * from most recently used to least recently used, instead of insertion - * order. - * @constructor - */ -ol.structs.LinkedMap = function(opt_maxCount, opt_cache) { - /** - * The maximum number of entries to allow, or null if there is no limit. - * @type {?number} - * @private - */ - this.maxCount_ = opt_maxCount || null; - - /** - * @type {boolean} - * @private - */ - this.cache_ = !!opt_cache; - - this.map_ = new goog.structs.Map(); - - this.head_ = new ol.structs.LinkedMap.Node_('', undefined); - this.head_.next = this.head_.prev = this.head_; -}; - - -/** - * Finds a node and updates it to be the most recently used. - * @param {string} key The key of the node. - * @return {ol.structs.LinkedMap.Node_} The node or null if not found. - * @private - */ -ol.structs.LinkedMap.prototype.findAndMoveToTop_ = function(key) { - var node = /** @type {ol.structs.LinkedMap.Node_} */ (this.map_.get(key)); - if (node) { - if (this.cache_) { - node.remove(); - this.insert_(node); - } - } - return node; -}; - - -/** - * Retrieves the value for a given key. If this is a caching LinkedMap, the - * entry will become the most recently used. - * @param {string} key The key to retrieve the value for. - * @param {*=} opt_val A default value that will be returned if the key is - * not found, defaults to undefined. - * @return {*} The retrieved value. - */ -ol.structs.LinkedMap.prototype.get = function(key, opt_val) { - var node = this.findAndMoveToTop_(key); - return node ? node.value : opt_val; -}; - - -/** - * Retrieves the value for a given key without updating the entry to be the - * most recently used. - * @param {string} key The key to retrieve the value for. - * @param {*=} opt_val A default value that will be returned if the key is - * not found. - * @return {*} The retrieved value. - */ -ol.structs.LinkedMap.prototype.peekValue = function(key, opt_val) { - var node = this.map_.get(key); - return node ? node.value : opt_val; -}; - - -/** - * Sets a value for a given key. If this is a caching LinkedMap, this entry - * will become the most recently used. - * @param {string} key The key to retrieve the value for. - * @param {*} value A default value that will be returned if the key is - * not found. - */ -ol.structs.LinkedMap.prototype.set = function(key, value) { - var node = this.findAndMoveToTop_(key); - if (node) { - node.value = value; - } else { - node = new ol.structs.LinkedMap.Node_(key, value); - this.map_.set(key, node); - this.insert_(node); - } -}; - - -/** - * Returns the value of the first node without making any modifications. - * @return {*} The value of the first node or undefined if the map is empty. - */ -ol.structs.LinkedMap.prototype.peek = function() { - return this.head_.next.value; -}; - - -/** - * Returns the value of the last node without making any modifications. - * @return {*} The value of the last node or undefined if the map is empty. - */ -ol.structs.LinkedMap.prototype.peekLast = function() { - return this.head_.prev.value; -}; - - -/** - * Returns the key of the last node without making any modifications. - * @return {string|undefined} The key of the last node or undefined if the map - * is empty. - */ -ol.structs.LinkedMap.prototype.peekLastKey = function() { - return this.head_.prev.key; -}; - - -/** - * Removes the first node from the list and returns its value. - * @return {*} The value of the popped node, or undefined if the map was empty. - */ -ol.structs.LinkedMap.prototype.shift = function() { - return this.popNode_(this.head_.next); -}; - - -/** - * Removes the last node from the list and returns its value. - * @return {*} The value of the popped node, or undefined if the map was empty. - */ -ol.structs.LinkedMap.prototype.pop = function() { - return this.popNode_(this.head_.prev); -}; - - -/** - * Removes a value from the LinkedMap based on its key. - * @param {string} key The key to remove. - * @return {boolean} True if the entry was removed, false if the key was not - * found. - */ -ol.structs.LinkedMap.prototype.remove = function(key) { - var node = /** @type {ol.structs.LinkedMap.Node_} */ (this.map_.get(key)); - if (node) { - this.removeNode(node); - return true; - } - return false; -}; - - -/** - * Removes a node from the {@code LinkedMap}. It can be overridden to do - * further cleanup such as disposing of the node value. - * @param {!ol.structs.LinkedMap.Node_} node The node to remove. - * @protected - */ -ol.structs.LinkedMap.prototype.removeNode = function(node) { - node.remove(); - this.map_.remove(node.key); -}; - - -/** - * @return {number} The number of items currently in the LinkedMap. - */ -ol.structs.LinkedMap.prototype.getCount = function() { - return this.map_.getCount(); -}; - - -/** - * @return {boolean} True if the cache is empty, false if it contains any items. - */ -ol.structs.LinkedMap.prototype.isEmpty = function() { - return this.map_.isEmpty(); -}; - - -/** - * Sets the maximum number of entries allowed in this object, truncating any - * excess objects if necessary. - * @param {number} maxCount The new maximum number of entries to allow. - */ -ol.structs.LinkedMap.prototype.setMaxCount = function(maxCount) { - this.maxCount_ = maxCount || null; - if (this.maxCount_ != null) { - this.truncate_(this.maxCount_); - } -}; - - -/** - * @return {!Array.} The list of the keys in the appropriate order for - * this LinkedMap. - */ -ol.structs.LinkedMap.prototype.getKeys = function() { - return this.map(function(val, key) { - return key; - }); -}; - - -/** - * @return {!Array} The list of the values in the appropriate order for - * this LinkedMap. - */ -ol.structs.LinkedMap.prototype.getValues = function() { - return this.map(function(val, key) { - return val; - }); -}; - - -/** - * Tests whether a provided value is currently in the LinkedMap. This does not - * affect item ordering in cache-style LinkedMaps. - * @param {Object} value The value to check for. - * @return {boolean} Whether the value is in the LinkedMap. - */ -ol.structs.LinkedMap.prototype.contains = function(value) { - return this.some(function(el) { - return el == value; - }); -}; - - -/** - * Tests whether a provided key is currently in the LinkedMap. This does not - * affect item ordering in cache-style LinkedMaps. - * @param {string} key The key to check for. - * @return {boolean} Whether the key is in the LinkedMap. - */ -ol.structs.LinkedMap.prototype.containsKey = function(key) { - return this.map_.containsKey(key); -}; - - -/** - * Removes all entries in this object. - */ -ol.structs.LinkedMap.prototype.clear = function() { - this.truncate_(0); -}; - - -/** - * Calls a function on each item in the LinkedMap. - * - * @see goog.structs.forEach - * @param {Function} f The function to call for each item. The function takes - * three arguments: the value, the key, and the LinkedMap. - * @param {Object=} opt_obj The object context to use as "this" for the - * function. - */ -ol.structs.LinkedMap.prototype.forEach = function(f, opt_obj) { - for (var n = this.head_.next; n != this.head_; n = n.next) { - f.call(opt_obj, n.value, n.key, this); - } -}; - - -/** - * Calls a function on each item in the LinkedMap and returns the results of - * those calls in an array. - * - * @see goog.structs.map - * @param {!Function} f The function to call for each item. The function takes - * three arguments: the value, the key, and the LinkedMap. - * @param {Object=} opt_obj The object context to use as "this" for the - * function. - * @return {!Array} The results of the function calls for each item in the - * LinkedMap. - */ -ol.structs.LinkedMap.prototype.map = function(f, opt_obj) { - var rv = []; - for (var n = this.head_.next; n != this.head_; n = n.next) { - rv.push(f.call(opt_obj, n.value, n.key, this)); - } - return rv; -}; - - -/** - * Calls a function on each item in the LinkedMap and returns true if any of - * those function calls returns a true-like value. - * - * @see goog.structs.some - * @param {Function} f The function to call for each item. The function takes - * three arguments: the value, the key, and the LinkedMap, and returns a - * boolean. - * @param {Object=} opt_obj The object context to use as "this" for the - * function. - * @return {boolean} Whether f evaluates to true for at least one item in the - * LinkedMap. - */ -ol.structs.LinkedMap.prototype.some = function(f, opt_obj) { - for (var n = this.head_.next; n != this.head_; n = n.next) { - if (f.call(opt_obj, n.value, n.key, this)) { - return true; - } - } - return false; -}; - - -/** - * Calls a function on each item in the LinkedMap and returns true only if every - * function call returns a true-like value. - * - * @see goog.structs.some - * @param {Function} f The function to call for each item. The function takes - * three arguments: the value, the key, and the Cache, and returns a - * boolean. - * @param {Object=} opt_obj The object context to use as "this" for the - * function. - * @return {boolean} Whether f evaluates to true for every item in the Cache. - */ -ol.structs.LinkedMap.prototype.every = function(f, opt_obj) { - for (var n = this.head_.next; n != this.head_; n = n.next) { - if (!f.call(opt_obj, n.value, n.key, this)) { - return false; - } - } - return true; -}; - - -/** - * Appends a node to the list. LinkedMap in cache mode adds new nodes to - * the head of the list, otherwise they are appended to the tail. If there is a - * maximum size, the list will be truncated if necessary. - * - * @param {ol.structs.LinkedMap.Node_} node The item to insert. - * @private - */ -ol.structs.LinkedMap.prototype.insert_ = function(node) { - if (this.cache_) { - node.next = this.head_.next; - node.prev = this.head_; - - this.head_.next = node; - node.next.prev = node; - } else { - node.prev = this.head_.prev; - node.next = this.head_; - - this.head_.prev = node; - node.prev.next = node; - } - - if (this.maxCount_ != null) { - this.truncate_(this.maxCount_); - } -}; - - -/** - * Removes elements from the LinkedMap if the given count has been exceeded. - * In cache mode removes nodes from the tail of the list. Otherwise removes - * nodes from the head. - * @param {number} count Number of elements to keep. - * @private - */ -ol.structs.LinkedMap.prototype.truncate_ = function(count) { - for (var i = this.map_.getCount(); i > count; i--) { - this.removeNode(this.cache_ ? this.head_.prev : this.head_.next); - } -}; - - -/** - * Removes the node from the LinkedMap if it is not the head, and returns - * the node's value. - * @param {!ol.structs.LinkedMap.Node_} node The item to remove. - * @return {*} The value of the popped node. - * @private - */ -ol.structs.LinkedMap.prototype.popNode_ = function(node) { - if (this.head_ != node) { - this.removeNode(node); - } - return node.value; -}; - - - -/** - * Internal class for a doubly-linked list node containing a key/value pair. - * @param {string} key The key. - * @param {*} value The value. - * @constructor - * @private - */ -ol.structs.LinkedMap.Node_ = function(key, value) { - this.key = key; - this.value = value; -}; - - -/** - * The next node in the list. - * @type {!ol.structs.LinkedMap.Node_} - */ -ol.structs.LinkedMap.Node_.prototype.next; - - -/** - * The previous node in the list. - * @type {!ol.structs.LinkedMap.Node_} - */ -ol.structs.LinkedMap.Node_.prototype.prev; - - -/** - * Causes this node to remove itself from the list. - */ -ol.structs.LinkedMap.Node_.prototype.remove = function() { - this.prev.next = this.next; - this.next.prev = this.prev; - - delete this.prev; - delete this.next; -}; From 0c48fbd1888114175f73d37846744c8a8a421125 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Tue, 5 Feb 2013 10:50:18 -0700 Subject: [PATCH 5/8] Check sooner and only assert if duration hasn't passed --- test/spec/ol/map.test.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test/spec/ol/map.test.js b/test/spec/ol/map.test.js index ea42734d47..e131be7255 100644 --- a/test/spec/ol/map.test.js +++ b/test/spec/ol/map.test.js @@ -211,14 +211,16 @@ describe('ol.Map', function() { // confirm that the center is somewhere between origin and destination // after a short delay - waits(100); + waits(goog.async.AnimationDelay.TIMEOUT); runs(function() { expect(o.callback).toHaveBeenCalled(); var loc = map.getView().getCenter(); expect(loc.x).not.toEqual(origin.x); expect(loc.y).not.toEqual(origin.y); - expect(loc.x).not.toEqual(destination.x); - expect(loc.y).not.toEqual(destination.y); + if (new Date().getTime() - start < duration) { + expect(loc.x).not.toEqual(destination.x); + expect(loc.y).not.toEqual(destination.y); + } }); // confirm that the map has reached the destination after the duration From c37837ba34a406d113dad42d4af3232f1c45b3a9 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Wed, 6 Feb 2013 08:42:43 +0100 Subject: [PATCH 6/8] Use an object literal rather than a class for LRUCache entries --- src/ol/structs/lrucache.js | 42 ++++++++++---------------------------- 1 file changed, 11 insertions(+), 31 deletions(-) diff --git a/src/ol/structs/lrucache.js b/src/ol/structs/lrucache.js index 863859bdba..909dbcb2c1 100644 --- a/src/ol/structs/lrucache.js +++ b/src/ol/structs/lrucache.js @@ -225,7 +225,12 @@ ol.structs.LRUCache.prototype.pop = function() { ol.structs.LRUCache.prototype.set = function(key, value) { goog.asserts.assert(!(key in {})); goog.asserts.assert(!(key in this.entries_)); - var entry = new ol.structs.LRUCacheEntry_(key, value, null, this.newest_); + var entry = { + key: key, + newer: null, + older: this.newest_, + value: value + }; if (goog.isNull(this.newest_)) { this.oldest_ = entry; } else { @@ -237,35 +242,10 @@ ol.structs.LRUCache.prototype.set = function(key, value) { }; - /** - * @constructor - * @private - * @param {string} key Key. - * @param {*} value Value. - * @param {ol.structs.LRUCacheEntry_} newer Newer. - * @param {ol.structs.LRUCacheEntry_} older Older. + * @typedef {{key: string, + * newer: ol.structs.LRUCacheEntry_, + * older: ol.structs.LRUCacheEntry_, + * value: *}} */ -ol.structs.LRUCacheEntry_ = function(key, value, newer, older) { - - /** - * @type {string} - */ - this.key = key; - - /** - * @type {*} - */ - this.value = value; - - /** - * @type {ol.structs.LRUCacheEntry_} - */ - this.newer = newer; - - /** - * @type {ol.structs.LRUCacheEntry_} - */ - this.older = older; - -}; +ol.structs.LRUCacheEntry_; From b1df0cac65f62a547c471f9fa24e4aa05a17a669 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Wed, 6 Feb 2013 09:09:04 +0100 Subject: [PATCH 7/8] Rename object properties so compiler can rename them --- src/ol/structs/lrucache.js | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/ol/structs/lrucache.js b/src/ol/structs/lrucache.js index 909dbcb2c1..a67df961b8 100644 --- a/src/ol/structs/lrucache.js +++ b/src/ol/structs/lrucache.js @@ -102,7 +102,7 @@ ol.structs.LRUCache.prototype.containsKey = function(key) { ol.structs.LRUCache.prototype.forEach = function(f, opt_obj) { var entry = this.oldest_; while (!goog.isNull(entry)) { - f.call(opt_obj, entry.value, entry.key, this); + f.call(opt_obj, entry.value_, entry.key_, this); entry = entry.newer; } }; @@ -116,7 +116,7 @@ ol.structs.LRUCache.prototype.get = function(key) { var entry = this.entries_[key]; goog.asserts.assert(goog.isDef(entry)); if (entry === this.newest_) { - return entry.value; + return entry.value_; } else if (entry === this.oldest_) { this.oldest_ = this.oldest_.newer; this.oldest_.older = null; @@ -128,7 +128,7 @@ ol.structs.LRUCache.prototype.get = function(key) { entry.older = this.newest_; this.newest_.newer = entry; this.newest_ = entry; - return entry.value; + return entry.value_; }; @@ -148,7 +148,7 @@ ol.structs.LRUCache.prototype.getKeys = function() { var i = 0; var entry; for (entry = this.newest_; !goog.isNull(entry); entry = entry.older) { - keys[i++] = entry.key; + keys[i++] = entry.key_; } goog.asserts.assert(i == this.count_); return keys; @@ -160,7 +160,7 @@ ol.structs.LRUCache.prototype.getKeys = function() { */ ol.structs.LRUCache.prototype.getLastKey = function() { goog.asserts.assert(!goog.isNull(this.newest_)); - return this.newest_.key; + return this.newest_.key_; }; @@ -172,7 +172,7 @@ ol.structs.LRUCache.prototype.getValues = function() { var i = 0; var entry; for (entry = this.newest_; !goog.isNull(entry); entry = entry.older) { - values[i++] = entry.value; + values[i++] = entry.value_; } goog.asserts.assert(i == this.count_); return values; @@ -184,7 +184,7 @@ ol.structs.LRUCache.prototype.getValues = function() { */ ol.structs.LRUCache.prototype.peekLast = function() { goog.asserts.assert(!goog.isNull(this.oldest_)); - return this.oldest_.value; + return this.oldest_.value_; }; @@ -193,7 +193,7 @@ ol.structs.LRUCache.prototype.peekLast = function() { */ ol.structs.LRUCache.prototype.peekLastKey = function() { goog.asserts.assert(!goog.isNull(this.oldest_)); - return this.oldest_.key; + return this.oldest_.key_; }; @@ -204,8 +204,8 @@ ol.structs.LRUCache.prototype.pop = function() { goog.asserts.assert(!goog.isNull(this.oldest_)); goog.asserts.assert(!goog.isNull(this.newest_)); var entry = this.oldest_; - goog.asserts.assert(entry.key in this.entries_); - delete this.entries_[entry.key]; + goog.asserts.assert(entry.key_ in this.entries_); + delete this.entries_[entry.key_]; if (!goog.isNull(entry.newer)) { entry.newer.older = null; } @@ -214,7 +214,7 @@ ol.structs.LRUCache.prototype.pop = function() { this.newest_ = null; } --this.count_; - return entry.value; + return entry.value_; }; @@ -226,10 +226,10 @@ ol.structs.LRUCache.prototype.set = function(key, value) { goog.asserts.assert(!(key in {})); goog.asserts.assert(!(key in this.entries_)); var entry = { - key: key, + key_: key, newer: null, older: this.newest_, - value: value + value_: value }; if (goog.isNull(this.newest_)) { this.oldest_ = entry; @@ -243,9 +243,9 @@ ol.structs.LRUCache.prototype.set = function(key, value) { /** - * @typedef {{key: string, + * @typedef {{key_: string, * newer: ol.structs.LRUCacheEntry_, * older: ol.structs.LRUCacheEntry_, - * value: *}} + * value_: *}} */ ol.structs.LRUCacheEntry_; From 3692918ed7cbeab7255b2f009fff9e5b844d2778 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Wed, 6 Feb 2013 12:50:34 +0100 Subject: [PATCH 8/8] Remove unnecessary undercores, thanks @fredj --- src/ol/structs/lrucache.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ol/structs/lrucache.js b/src/ol/structs/lrucache.js index a67df961b8..ecf53521a6 100644 --- a/src/ol/structs/lrucache.js +++ b/src/ol/structs/lrucache.js @@ -21,19 +21,19 @@ ol.structs.LRUCache = function() { /** * @private - * @type {Object.} + * @type {Object.} */ this.entries_ = {}; /** * @private - * @type {?ol.structs.LRUCacheEntry_} + * @type {?ol.structs.LRUCacheEntry} */ this.oldest_ = null; /** * @private - * @type {?ol.structs.LRUCacheEntry_} + * @type {?ol.structs.LRUCacheEntry} */ this.newest_ = null; @@ -244,8 +244,8 @@ ol.structs.LRUCache.prototype.set = function(key, value) { /** * @typedef {{key_: string, - * newer: ol.structs.LRUCacheEntry_, - * older: ol.structs.LRUCacheEntry_, + * newer: ol.structs.LRUCacheEntry, + * older: ol.structs.LRUCacheEntry, * value_: *}} */ -ol.structs.LRUCacheEntry_; +ol.structs.LRUCacheEntry;