Index: runtime/lib/compact_hash.dart |
diff --git a/runtime/lib/compact_hash.dart b/runtime/lib/compact_hash.dart |
index 22af0a23c2c7c48eaf7c5876fa841c9f9de4af0c..5bcb48e6fe5b41a7bc95d7ded1ee5dab007992fd 100644 |
--- a/runtime/lib/compact_hash.dart |
+++ b/runtime/lib/compact_hash.dart |
@@ -13,15 +13,13 @@ abstract class _HashFieldBase { |
// The hash pattern is based on hashCode, but is guaranteed to be non-zero. |
// The length of _index is always a power of two, and there is always at |
// least one unoccupied entry. |
+ // NOTE: When maps are deserialized, their _index and _hashMask is regenerated |
+ // lazily by _regenerateIndex. |
+ // TODO(koda): Consider also using null _index for tiny, linear-search tables. |
Uint32List _index = new Uint32List(_HashBase._INITIAL_INDEX_SIZE); |
- // Cached in-place mask for the hash pattern component. On 32-bit, the top |
- // bits are wasted to avoid Mint allocation. |
- // TODO(koda): Reclaim the bits by making the compiler treat hash patterns |
- // as unsigned words. |
- int _hashMask = internal.is64Bit ? |
- (1 << (32 - _HashBase._INITIAL_INDEX_BITS)) - 1 : |
- (1 << (30 - _HashBase._INITIAL_INDEX_BITS)) - 1; |
+ // Cached in-place mask for the hash pattern component. |
+ int _hashMask = _HashBase._indexSizeToHashMask(_HashBase._INITIAL_INDEX_SIZE); |
// Fixed-length list of keys (set) or key/value at even/odd indices (map). |
List _data = new List(_HashBase._INITIAL_INDEX_SIZE); |
@@ -67,7 +65,16 @@ abstract class _HashBase { |
// Unused and deleted entries are marked by 0 and 1, respectively. |
static const int _UNUSED_PAIR = 0; |
static const int _DELETED_PAIR = 1; |
- |
+ |
+ // On 32-bit, the top bits are wasted to avoid Mint allocation. |
+ // TODO(koda): Reclaim the bits by making the compiler treat hash patterns |
+ // as unsigned words. |
+ static int _indexSizeToHashMask(int indexSize) { |
+ int indexBits = indexSize.bitLength - 2; |
+ return internal.is64Bit ? (1 << (32 - indexBits)) - 1 : |
+ (1 << (30 - indexBits)) - 1; |
+ } |
+ |
static int _hashPattern(int fullHash, int hashMask, int size) { |
final int maskedHash = fullHash & hashMask; |
// TODO(koda): Consider keeping bit length and use left shift. |
@@ -132,7 +139,8 @@ class _LinkedHashMapMixin<K, V> { |
void clear() { |
if (!isEmpty) { |
- _init(_index.length, _hashMask); |
+ // Use _data.length, since _index might be null. |
+ _init(_data.length, _hashMask); |
} |
} |
@@ -155,6 +163,24 @@ class _LinkedHashMapMixin<K, V> { |
} |
} |
} |
+ |
+ int _getIndexLength() { |
+ return (_index == null) ? _regenerateIndex() : _index.length; |
+ } |
+ |
+ int _regenerateIndex() { |
+ assert(_index == null); |
+ _index = new Uint32List(_data.length); |
+ assert(_hashMask == 0); |
+ _hashMask = _HashBase._indexSizeToHashMask(_index.length); |
+ final int tmpUsed = _usedData; |
+ _usedData = 0; |
+ for (int i = 0; i < tmpUsed; i += 2) { |
+ // TODO(koda): Avoid redundant equality tests and stores into _data. |
+ this[_data[i]] = _data[i + 1]; |
+ } |
+ return _index.length; |
+ } |
void _insert(K key, V value, int hashPattern, int i) { |
if (_usedData == _data.length) { |
@@ -199,7 +225,7 @@ class _LinkedHashMapMixin<K, V> { |
} |
void operator[]=(K key, V value) { |
- final int size = _index.length; |
+ final int size = _getIndexLength(); |
final int sizeMask = size - 1; |
final int fullHash = _hashCode(key); |
final int hashPattern = _HashBase._hashPattern(fullHash, _hashMask, size); |
@@ -213,7 +239,7 @@ class _LinkedHashMapMixin<K, V> { |
} |
V putIfAbsent(K key, V ifAbsent()) { |
- final int size = _index.length; |
+ final int size = _getIndexLength(); |
final int sizeMask = size - 1; |
final int maxEntries = size >> 1; |
final int fullHash = _hashCode(key); |
@@ -236,7 +262,7 @@ class _LinkedHashMapMixin<K, V> { |
} |
V remove(Object key) { |
- final int size = _index.length; |
+ final int size = _getIndexLength(); |
final int sizeMask = size - 1; |
final int maxEntries = size >> 1; |
final int fullHash = _hashCode(key); |
@@ -266,7 +292,7 @@ class _LinkedHashMapMixin<K, V> { |
// If key is absent, return _data (which is never a value). |
Object _getValueOrData(Object key) { |
- final int size = _index.length; |
+ final int size = _getIndexLength(); |
final int sizeMask = size - 1; |
final int maxEntries = size >> 1; |
final int fullHash = _hashCode(key); |