Chromium Code Reviews| Index: sdk/lib/internal/lru.dart |
| diff --git a/sdk/lib/internal/lru.dart b/sdk/lib/internal/lru.dart |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..66c5f138236f535057f5192c2162c4c46edf9e0f |
| --- /dev/null |
| +++ b/sdk/lib/internal/lru.dart |
| @@ -0,0 +1,108 @@ |
| +// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| +// for details. All rights reserved. Use of this source code is governed by a |
| +// BSD-style license that can be found in the LICENSE file. |
| + |
| +part of dart._internal; |
| + |
| +class LRUAssociation<K,V> { |
| + K key; |
| + V value; |
| + LRUAssociation previous; |
| + LRUAssociation next; |
|
Ivan Posva
2014/05/25 20:00:26
Empty lines between methods please.
|
| + void insertBetween(before, after) { |
| + after.previous = this; |
| + before.next = this; |
| + this.next = after; |
| + this.previous = before; |
| + } |
| + void remove() { |
| + var after = next; |
| + var before = previous; |
| + after.previous = before; |
| + before.next = after; |
| + } |
| +} |
| + |
| +/** |
| + * A map with a fixed capacity that evicts associations when capacity is reached |
| + * on a least-recently-used basis. Implemented as an open addressing hash table |
| + * with doubly-linked entries forming the LRU queue. |
| + */ |
| +class LRUMap<K,V> { |
| + final LRUAssociation<K,V> head; |
| + final List table; |
| + final int capacity; |
| + int size = 0; |
| + |
| + LRUMap(int capacity) |
| + : this.capacity = capacity |
| + , this.table = new List(capacity * 4 ~/ 3) |
| + , this.head = new LRUAssociation() { |
| + head.insertBetween(head, head); |
|
Ivan Posva
2014/05/25 20:00:26
Tow space indent. This way it looks like part of t
|
| + } |
| + |
| + int _scanFor(K key) { |
| + var start = key.hashCode % table.length; |
|
Ivan Posva
2014/05/25 20:00:26
How about rounding up the length to the next power
rmacnak
2014/05/30 21:26:05
Using a mask instead of modulus is a ~20% improvem
|
| + var index = start; |
| + do { |
| + var assoc = table[index]; |
| + if (assoc == null || assoc.key == key) { |
|
Ivan Posva
2014/05/25 20:00:26
I don't think this will work that well for capacit
rmacnak
2014/05/30 21:26:05
This scheme relies on their always being at least
|
| + return index; |
| + } |
| + index = (index + 1) % table.length; |
| + } while (index != start); |
| + // Should never happen because we start evicting associations before the |
| + // table is full. |
| + throw "LRUMap out of space"; |
| + } |
| + |
| + void _fixCollisionsAfter(start) { |
| + var assoc; |
| + var index = (start + 1) % table.length; |
| + while ((assoc = table[index]) != null) { |
| + var newIndex = _scanFor(assoc.key); |
| + table[newIndex] = assoc; |
| + table[index] = null; |
| + index = (index + 1) % table.length; |
| + } |
| + } |
| + |
| + operator []=(K key, V value) { |
| + int index = _scanFor(key); |
| + var assoc = table[index]; |
| + if (assoc != null) { |
| + // Existing key, replace value. |
| + assoc.value = value; |
| + assoc.remove(); |
| + assoc.insertBetween(head, head.next); |
| + } else { |
| + // New key. |
| + var newAssoc; |
| + if (size == capacity) { |
| + // Knock out the oldest association. |
| + var lru = head.previous; |
| + lru.remove(); |
| + index = _scanFor(lru.key); |
| + _fixCollisionsAfter(index); |
| + index = _scanFor(key); |
| + newAssoc = lru; // Recycle the association. |
| + } else { |
| + newAssoc = new LRUAssociation(); |
| + size++; |
| + } |
| + newAssoc.key = key; |
| + newAssoc.value = value; |
| + newAssoc.insertBetween(head, head.next); |
| + table[index] = newAssoc; |
| + } |
| + } |
| + |
| + V operator [](K key) { |
| + var index = _scanFor(key); |
| + var assoc = table[index]; |
| + if (assoc == null) return null; |
| + assoc.remove(); |
| + assoc.insertBetween(head, head.next); |
| + return assoc.value; |
| + } |
| +} |