Index: quiver/lib/src/collection/lru_map.dart |
diff --git a/quiver/lib/src/collection/lru_map.dart b/quiver/lib/src/collection/lru_map.dart |
deleted file mode 100644 |
index 13e094ac066e3229ffc6dac28cb9ccf25d6b0acc..0000000000000000000000000000000000000000 |
--- a/quiver/lib/src/collection/lru_map.dart |
+++ /dev/null |
@@ -1,303 +0,0 @@ |
-// Copyright 2014 Google Inc. 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. |
- |
-part of quiver.collection; |
- |
-/** |
- * An implementation of a [Map] which has a maximum size and uses a (Least |
- * Recently Used)[http://en.wikipedia.org/wiki/Cache_algorithms#LRU] algorithm |
- * to remove items from the [Map] when the [maximumSize] is reached and new |
- * items are added. |
- * |
- * It is safe to access the [keys] and [values] collections without affecting |
- * the "used" ordering - as well as using [forEach]. Other types of access, |
- * including bracket, and [putIfAbsent], promotes the key-value pair to the MRU |
- * position. |
- */ |
-abstract class LruMap<K, V> implements Map<K, V> { |
- /** |
- * Creates a [LruMap] instance with the default implementation. |
- */ |
- factory LruMap({int maximumSize}) = LinkedLruHashMap; |
- |
- /** |
- * Maximum size of the [Map]. If [length] exceeds this value at any time, |
- * n entries accessed the earliest are removed, where n is [length] - |
- * [maximumSize]. |
- */ |
- int maximumSize; |
-} |
- |
-/** |
- * Simple implementation of a linked-list entry that contains a [key] and |
- * [value]. |
- */ |
-class _LinkedEntry<K, V> { |
- K key; |
- V value; |
- |
- _LinkedEntry<K, V> next; |
- _LinkedEntry<K, V> previous; |
- |
- _LinkedEntry([this.key, this.value]); |
-} |
- |
-/** |
- * A linked hash-table based implementation of [LruMap]. |
- */ |
-class LinkedLruHashMap<K, V> implements LruMap<K, V> { |
- static const _DEFAULT_MAXIMUM_SIZE = 100; |
- |
- final Map<K, _LinkedEntry<K, V>> _entries; |
- |
- int _maximumSize; |
- |
- _LinkedEntry<K, V> _head; |
- _LinkedEntry<K, V> _tail; |
- |
- /** |
- * Create a new LinkedLruHashMap with a [maximumSize]. |
- */ |
- factory LinkedLruHashMap({int maximumSize}) => |
- new LinkedLruHashMap._fromMap(new HashMap<K, _LinkedEntry<K, V>>(), |
- maximumSize: maximumSize); |
- |
- LinkedLruHashMap._fromMap( |
- this._entries, { |
- int maximumSize}) |
- // This pattern is used instead of a default value because we want to |
- // be able to respect null values coming in from MapCache.lru. |
- : _maximumSize = firstNonNull(maximumSize, _DEFAULT_MAXIMUM_SIZE); |
- |
- /** |
- * Adds all key-value pairs of [other] to this map. |
- * |
- * The operation is equivalent to doing this[key] = value for each key and |
- * associated value in other. It iterates over other, which must therefore not |
- * change during the iteration. |
- * |
- * If a key of [other] is already in this map, its value is overwritten. If |
- * the number of unique keys is greater than [maximumSize] then the least |
- * recently use keys are evicted. For keys written to by [other], the least |
- * recently user order is determined by [other]'s iteration order. |
- */ |
- @override |
- void addAll(Map<K, V> other) => other.forEach((k, v) => this[k] = v); |
- |
- @override |
- void clear() { |
- _entries.clear(); |
- _head = _tail = null; |
- } |
- |
- @override |
- bool containsKey(K key) => _entries.containsKey(key); |
- |
- @override |
- bool containsValue(V value) => values.contains(value); |
- |
- /** |
- * Applies [action] to each key-value pair of the map in order of MRU to LRU. |
- * |
- * Calling `action` must not add or remove keys from the map. |
- */ |
- @override |
- void forEach(void action(K key, V value)) { |
- var head = _head; |
- while (head != null) { |
- action(head.key, head.value); |
- head = head.next; |
- } |
- } |
- |
- @override |
- int get length => _entries.length; |
- |
- @override |
- bool get isEmpty => _entries.isEmpty; |
- |
- @override |
- bool get isNotEmpty => _entries.isNotEmpty; |
- |
- /** |
- * Creates an [Iterable] around the entries of the map. |
- */ |
- Iterable<_LinkedEntry<K, V>> _iterable() { |
- return new GeneratingIterable<_LinkedEntry<K, V>>( |
- () => _head, (n) => n.next); |
- } |
- |
- /** |
- * The keys of [this] - in order of MRU to LRU. |
- * |
- * The returned iterable does *not* have efficient `length` or `contains`. |
- */ |
- @override |
- Iterable<K> get keys => _iterable().map((e) => e.key); |
- |
- /** |
- * The values of [this] - in order of MRU to LRU. |
- * |
- * The returned iterable does *not* have efficient `length` or `contains`. |
- */ |
- @override |
- Iterable<V> get values => _iterable().map((e) => e.value); |
- |
- @override |
- int get maximumSize => _maximumSize; |
- |
- @override |
- void set maximumSize(int maximumSize) { |
- if (maximumSize == null) throw new ArgumentError.notNull('maximumSize'); |
- while (length > maximumSize) { |
- _removeLru(); |
- } |
- _maximumSize = maximumSize; |
- } |
- |
- /** |
- * Look up the value associated with [key], or add a new value if it isn't |
- * there. The pair is promoted to the MRU position. |
- * |
- * Otherwise calls [ifAbsent] to get a new value, associates [key] to that |
- * [value], and then returns the new [value], with the key-value pair in the |
- * MRU position. If this causes [length] to exceed [maximumSize], then the |
- * LRU position is removed. |
- */ |
- @override |
- V putIfAbsent(K key, V ifAbsent()) { |
- final entry = _entries.putIfAbsent( |
- key, () => _createEntry(key, ifAbsent())); |
- if (length > maximumSize) { |
- _removeLru(); |
- } |
- _promoteEntry(entry); |
- return entry.value; |
- } |
- |
- /** |
- * Get the value for a [key] in the [Map]. The [key] will be promoted to the |
- * 'Most Recently Used' position. *NOTE*: Calling [] inside an iteration over |
- * keys/values is currently unsupported; use [keys] or [values] if you |
- * need information about entries without modifying their position. |
- */ |
- @override |
- V operator[](K key) { |
- final entry = _entries[key]; |
- if (entry != null) { |
- _promoteEntry(entry); |
- return entry.value; |
- } else { |
- return null; |
- } |
- } |
- |
- /** |
- * If [key] already exists, promotes it to the MRU position & assigns [value]. |
- * |
- * Otherwise, adds [key] and [value] to the MRU position. |
- * If [length] exceeds [maximumSize] while adding, removes the LRU position. |
- */ |
- @override |
- void operator []=(K key, V value) { |
- // Add this item to the MRU position. |
- _insertMru(_createEntry(key, value)); |
- |
- // Remove the LRU item if the size would be exceeded by adding this item. |
- if (length > maximumSize) { |
- assert(length == maximumSize + 1); |
- _removeLru(); |
- } |
- } |
- |
- @override |
- V remove(K key) { |
- final entry = _entries.remove(key); |
- if (entry != null) { |
- if (entry == _head) { |
- _head = _head.next; |
- } else if (entry == _tail) { |
- _tail.previous.next = null; |
- _tail = _tail.previous; |
- } else { |
- entry.previous.next = entry.next; |
- } |
- return entry.value; |
- } |
- return null; |
- } |
- |
- @override |
- String toString() => Maps.mapToString(this); |
- |
- /** |
- * Moves [entry] to the MRU position, shifting the linked list if necessary. |
- */ |
- void _promoteEntry(_LinkedEntry<K, V> entry) { |
- if (entry.previous != null) { |
- // If already existed in the map, link previous to next. |
- entry.previous.next = entry.next; |
- |
- // If this was the tail element, assign a new tail. |
- if (_tail == entry) { |
- _tail = entry.previous; |
- } |
- } |
- |
- // Replace head with this element. |
- if (_head != null) { |
- _head.previous = entry; |
- } |
- entry.previous = null; |
- entry.next = _head; |
- _head = entry; |
- |
- // Add a tail if this is the first element. |
- if (_tail == null) { |
- assert(length == 1); |
- _tail = _head; |
- } |
- } |
- |
- /** |
- * Creates and returns an entry from [key] and [value]. |
- */ |
- _LinkedEntry<K, V> _createEntry(K key, V value) { |
- return new _LinkedEntry<K, V>(key, value); |
- } |
- |
- /** |
- * If [entry] does not exist, inserts it into the backing map. |
- * If it does, replaces the existing [_LinkedEntry.value] with [entry.value]. |
- * Then, in either case, promotes [entry] to the MRU position. |
- */ |
- void _insertMru(_LinkedEntry<K, V> entry) { |
- // Insert a new entry if necessary (only 1 hash lookup in entire function). |
- // Otherwise, just updates the existing value. |
- final value = entry.value; |
- _promoteEntry(_entries.putIfAbsent(entry.key, () => entry)..value = value); |
- } |
- |
- /** |
- * Removes the LRU position, shifting the linked list if necessary. |
- */ |
- void _removeLru() { |
- // Remove the tail from the internal map. |
- _entries.remove(_tail.key); |
- |
- // Remove the tail element itself. |
- _tail = _tail.previous; |
- _tail.next = null; |
- } |
-} |