Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 import 'dart:typed_data'; | 5 import 'dart:typed_data'; |
| 6 | 6 |
| 7 // Hash table with open addressing that separates the index from keys/values. | 7 // Hash table with open addressing that separates the index from keys/values. |
| 8 abstract class _HashBase { | 8 abstract class _HashBase { |
| 9 // Each occupied entry in _index is a fixed-size integer that encodes a pair: | 9 // Each occupied entry in _index is a fixed-size integer that encodes a pair: |
| 10 // [ hash pattern for key | index of entry in _data ] | 10 // [ hash pattern for key | index of entry in _data ] |
| 11 // The hash pattern is based on hashCode, but is guaranteed to be non-zero. | 11 // The hash pattern is based on hashCode, but is guaranteed to be non-zero. |
| 12 // The length of _index is always a power of two, and there is always at | 12 // The length of _index is always a power of two, and there is always at |
| 13 // least one unoccupied entry. | 13 // least one unoccupied entry. |
| 14 Uint32List _index; | 14 Uint32List _index; |
| 15 | 15 |
| 16 // The number of bits used for each component is determined by table size. | 16 // The number of bits used for each component is determined by table size. |
| 17 // The length of _index is twice the number of entries in _data, and both | 17 // The length of _index is twice the number of entries in _data, and both |
| 18 // are doubled when _data is full. Thus, _index will have a max load factor | 18 // are doubled when _data is full. Thus, _index will have a max load factor |
| 19 // of 1/2, which enables one more bit to be used for the hash. | 19 // of 1/2, which enables one more bit to be used for the hash. |
| 20 // TODO(koda): Consider growing _data by factor sqrt(2), twice as often. | 20 // TODO(koda): Consider growing _data by factor sqrt(2), twice as often. |
| 21 static const int _INITIAL_INDEX_BITS = 3; | 21 static const int _INITIAL_INDEX_BITS = 3; |
| 22 static const int _INITIAL_INDEX_SIZE = 1 << (_INITIAL_INDEX_BITS + 1); | 22 static const int _INITIAL_INDEX_SIZE = 1 << (_INITIAL_INDEX_BITS + 1); |
| 23 | 23 |
| 24 // Unused and deleted entries are marked by 0 and 1, respectively. | 24 // Unused and deleted entries are marked by 0 and 1, respectively. |
| 25 static const int _UNUSED_PAIR = 0; | 25 static const int _UNUSED_PAIR = 0; |
| 26 static const int _DELETED_PAIR = 1; | 26 static const int _DELETED_PAIR = 1; |
| 27 | 27 |
| 28 // Cached in-place mask for the hash pattern component. On 32-bit, the top | 28 // Cached in-place mask for the hash pattern component. On 32-bit, the top |
| 29 // bits are wasted to avoid Mint allocation. | 29 // bits are wasted to avoid Mint allocation. |
| 30 // TODO(koda): Reclaim the bits by making the compiler treat hash patterns | 30 // TODO(koda): Reclaim the bits by making the compiler treat hash patterns |
| 31 // as unsigned words. | 31 // as unsigned words. |
| 32 int _hashMask = int.is64Bit() ? | 32 int _hashMask = int.is64Bit() ? |
| 33 (1 << (32 - _INITIAL_INDEX_BITS)) - 1 : | 33 (1 << (32 - _INITIAL_INDEX_BITS)) - 1 : |
| 34 (1 << (30 - _INITIAL_INDEX_BITS)) - 1; | 34 (1 << (30 - _INITIAL_INDEX_BITS)) - 1; |
| 35 | 35 |
| 36 static int _hashPattern(int fullHash, int hashMask, int size) { | 36 static int _hashPattern(int fullHash, int hashMask, int size) { |
| 37 final int maskedHash = fullHash & hashMask; | 37 final int maskedHash = fullHash & hashMask; |
| 38 // TODO(koda): Consider keeping bit length and use left shift. | 38 // TODO(koda): Consider keeping bit length and use left shift. |
| 39 return (maskedHash == 0) ? (size >> 1) : maskedHash * (size >> 1); | 39 return (maskedHash == 0) ? (size >> 1) : maskedHash * (size >> 1); |
| 40 } | 40 } |
| 41 | 41 |
| 42 // Linear probing. | 42 // Linear probing. |
| 43 static int _firstProbe(int fullHash, int sizeMask) { | 43 static int _firstProbe(int fullHash, int sizeMask) { |
| 44 final int i = fullHash & sizeMask; | 44 final int i = fullHash & sizeMask; |
| 45 // Light, fast shuffle to mitigate bad hashCode (e.g., sequential). | 45 // Light, fast shuffle to mitigate bad hashCode (e.g., sequential). |
| 46 return ((i << 1) + i) & sizeMask; | 46 return ((i << 1) + i) & sizeMask; |
| 47 } | 47 } |
| 48 static int _nextProbe(int i, int sizeMask) => (i + 1) & sizeMask; | 48 static int _nextProbe(int i, int sizeMask) => (i + 1) & sizeMask; |
| 49 | 49 |
| 50 // Fixed-length list of keys (set) or key/value at even/odd indices (map). | 50 // Fixed-length list of keys (set) or key/value at even/odd indices (map). |
| 51 List _data; | 51 List _data; |
| 52 // Length of _data that is used (i.e., keys + values for a map). | 52 // Length of _data that is used (i.e., keys + values for a map). |
| 53 int _usedData = 0; | 53 int _usedData = 0; |
| 54 // Number of deleted keys. | 54 // Number of deleted keys. |
| 55 int _deletedKeys = 0; | 55 int _deletedKeys = 0; |
| 56 | 56 |
| 57 // A self-loop is used to mark a deleted key or value. | 57 // A self-loop is used to mark a deleted key or value. |
| 58 static bool _isDeleted(List data, Object keyOrValue) => | 58 static bool _isDeleted(List data, Object keyOrValue) => |
| 59 identical(keyOrValue, data); | 59 identical(keyOrValue, data); |
| 60 static void _setDeletedAt(List data, int d) { | 60 static void _setDeletedAt(List data, int d) { |
| 61 data[d] = data; | 61 data[d] = data; |
| 62 } | 62 } |
| 63 | 63 |
| 64 // Concurrent modification detection relies on this checksum monotonically | 64 // Concurrent modification detection relies on this checksum monotonically |
| 65 // increasing between reallocations of _data. | 65 // increasing between reallocations of _data. |
| 66 int get _checkSum => _usedData + _deletedKeys; | 66 int get _checkSum => _usedData + _deletedKeys; |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 82 // appended to _data. | 82 // appended to _data. |
| 83 class _CompactLinkedHashMap<K, V> | 83 class _CompactLinkedHashMap<K, V> |
| 84 extends MapBase<K, V> with _HashBase, _OperatorEqualsAndHashCode | 84 extends MapBase<K, V> with _HashBase, _OperatorEqualsAndHashCode |
| 85 implements LinkedHashMap<K, V> { | 85 implements LinkedHashMap<K, V> { |
| 86 | 86 |
| 87 _CompactLinkedHashMap() { | 87 _CompactLinkedHashMap() { |
| 88 assert(_HashBase._UNUSED_PAIR == 0); | 88 assert(_HashBase._UNUSED_PAIR == 0); |
| 89 _index = new Uint32List(_HashBase._INITIAL_INDEX_SIZE); | 89 _index = new Uint32List(_HashBase._INITIAL_INDEX_SIZE); |
| 90 _data = new List(_HashBase._INITIAL_INDEX_SIZE); | 90 _data = new List(_HashBase._INITIAL_INDEX_SIZE); |
| 91 } | 91 } |
| 92 | 92 |
| 93 int get length => (_usedData >> 1) - _deletedKeys; | 93 int get length => (_usedData >> 1) - _deletedKeys; |
| 94 bool get isEmpty => length == 0; | 94 bool get isEmpty => length == 0; |
| 95 bool get isNotEmpty => !isEmpty; | 95 bool get isNotEmpty => !isEmpty; |
| 96 | 96 |
| 97 void _rehash() { | 97 void _rehash() { |
| 98 if ((_deletedKeys << 1) > _usedData) { | 98 if ((_deletedKeys << 2) > _usedData) { |
|
koda
2015/03/24 16:27:57
Perhaps expressing this in terms of 'length' inste
kustermann
2015/03/24 16:44:01
Good point. Ivan wanted me to submit the CL as is
| |
| 99 // TODO(koda): Consider shrinking. | 99 // TODO(koda): Consider shrinking. |
| 100 // TODO(koda): Consider in-place compaction and more costly CME check. | 100 // TODO(koda): Consider in-place compaction and more costly CME check. |
| 101 _init(_index.length, _hashMask, _data, _usedData); | 101 _init(_index.length, _hashMask, _data, _usedData); |
| 102 } else { | 102 } else { |
| 103 // TODO(koda): Support 32->64 bit transition (and adjust _hashMask). | 103 // TODO(koda): Support 32->64 bit transition (and adjust _hashMask). |
| 104 _init(_index.length << 1, _hashMask >> 1, _data, _usedData); | 104 _init(_index.length << 1, _hashMask >> 1, _data, _usedData); |
| 105 } | 105 } |
| 106 } | 106 } |
| 107 | 107 |
| 108 void clear() { | 108 void clear() { |
| 109 if (!isEmpty) { | 109 if (!isEmpty) { |
| 110 _init(_index.length, _hashMask); | 110 _init(_index.length, _hashMask); |
| 111 } | 111 } |
| 112 } | 112 } |
| 113 | 113 |
| 114 // Allocate new _index and _data, and optionally copy existing contents. | 114 // Allocate new _index and _data, and optionally copy existing contents. |
| 115 void _init(int size, int hashMask, [List oldData, int oldUsed]) { | 115 void _init(int size, int hashMask, [List oldData, int oldUsed]) { |
| 116 assert(size & (size - 1) == 0); | 116 assert(size & (size - 1) == 0); |
| 117 assert(_HashBase._UNUSED_PAIR == 0); | 117 assert(_HashBase._UNUSED_PAIR == 0); |
| 118 _index = new Uint32List(size); | 118 _index = new Uint32List(size); |
| 119 _hashMask = hashMask; | 119 _hashMask = hashMask; |
| 120 _data = new List(size); | 120 _data = new List(size); |
| 121 _usedData = 0; | 121 _usedData = 0; |
| 122 _deletedKeys = 0; | 122 _deletedKeys = 0; |
| 123 if (oldData != null) { | 123 if (oldData != null) { |
| 124 for (int i = 0; i < oldUsed; i += 2) { | 124 for (int i = 0; i < oldUsed; i += 2) { |
| 125 var key = oldData[i]; | 125 var key = oldData[i]; |
| 126 if (!_HashBase._isDeleted(oldData, key)) { | 126 if (!_HashBase._isDeleted(oldData, key)) { |
| 127 // TODO(koda): While there are enough hash bits, avoid hashCode calls. | 127 // TODO(koda): While there are enough hash bits, avoid hashCode calls. |
| 128 this[key] = oldData[i + 1]; | 128 this[key] = oldData[i + 1]; |
| 129 } | 129 } |
| 130 } | 130 } |
| 131 } | 131 } |
| 132 } | 132 } |
| 133 | 133 |
| 134 void _insert(K key, V value, int hashPattern, int i) { | 134 void _insert(K key, V value, int hashPattern, int i) { |
| 135 if (_usedData == _data.length) { | 135 if (_usedData == _data.length) { |
| 136 _rehash(); | 136 _rehash(); |
| 137 this[key] = value; | 137 this[key] = value; |
| 138 } else { | 138 } else { |
| 139 assert(1 <= hashPattern && hashPattern < (1 << 32)); | 139 assert(1 <= hashPattern && hashPattern < (1 << 32)); |
| 140 final int index = _usedData >> 1; | 140 final int index = _usedData >> 1; |
| 141 assert((index & hashPattern) == 0); | 141 assert((index & hashPattern) == 0); |
| 142 _index[i] = hashPattern | index; | 142 _index[i] = hashPattern | index; |
| 143 _data[_usedData++] = key; | 143 _data[_usedData++] = key; |
| 144 _data[_usedData++] = value; | 144 _data[_usedData++] = value; |
| 145 } | 145 } |
| 146 } | 146 } |
| 147 | 147 |
| 148 // If key is present, returns the index of the value in _data, else returns | 148 // If key is present, returns the index of the value in _data, else returns |
| 149 // the negated insertion point in _index. | 149 // the negated insertion point in _index. |
| 150 int _findValueOrInsertPoint(K key, int fullHash, int hashPattern, int size) { | 150 int _findValueOrInsertPoint(K key, int fullHash, int hashPattern, int size) { |
| 151 final int sizeMask = size - 1; | 151 final int sizeMask = size - 1; |
| 152 final int maxEntries = size >> 1; | 152 final int maxEntries = size >> 1; |
| 153 int i = _HashBase._firstProbe(fullHash, sizeMask); | 153 int i = _HashBase._firstProbe(fullHash, sizeMask); |
| 154 int firstDeleted = -1; | 154 int firstDeleted = -1; |
| 155 int pair = _index[i]; | 155 int pair = _index[i]; |
| 156 while (pair != _HashBase._UNUSED_PAIR) { | 156 while (pair != _HashBase._UNUSED_PAIR) { |
| 157 if (pair == _HashBase._DELETED_PAIR) { | 157 if (pair == _HashBase._DELETED_PAIR) { |
| 158 if (firstDeleted < 0){ | 158 if (firstDeleted < 0){ |
| 159 firstDeleted = i; | 159 firstDeleted = i; |
| 160 } | 160 } |
| 161 } else { | 161 } else { |
| 162 final int entry = hashPattern ^ pair; | 162 final int entry = hashPattern ^ pair; |
| 163 if (entry < maxEntries) { | 163 if (entry < maxEntries) { |
| 164 final int d = entry << 1; | 164 final int d = entry << 1; |
| 165 if (_equals(key, _data[d])) { | 165 if (_equals(key, _data[d])) { |
| 166 return d + 1; | 166 return d + 1; |
| 167 } | 167 } |
| 168 } | 168 } |
| 169 } | 169 } |
| 170 i = _HashBase._nextProbe(i, sizeMask); | 170 i = _HashBase._nextProbe(i, sizeMask); |
| 171 pair = _index[i]; | 171 pair = _index[i]; |
| 172 } | 172 } |
| 173 return firstDeleted >= 0 ? -firstDeleted : -i; | 173 return firstDeleted >= 0 ? -firstDeleted : -i; |
| 174 } | 174 } |
| 175 | 175 |
| 176 void operator[]=(K key, V value) { | 176 void operator[]=(K key, V value) { |
| 177 final int size = _index.length; | 177 final int size = _index.length; |
| 178 final int sizeMask = size - 1; | 178 final int sizeMask = size - 1; |
| 179 final int fullHash = _hashCode(key); | 179 final int fullHash = _hashCode(key); |
| 180 final int hashPattern = _HashBase._hashPattern(fullHash, _hashMask, size); | 180 final int hashPattern = _HashBase._hashPattern(fullHash, _hashMask, size); |
| 181 final int d = _findValueOrInsertPoint(key, fullHash, hashPattern, size); | 181 final int d = _findValueOrInsertPoint(key, fullHash, hashPattern, size); |
| 182 if (d > 0) { | 182 if (d > 0) { |
| 183 _data[d] = value; | 183 _data[d] = value; |
| 184 } else { | 184 } else { |
| 185 final int i = -d; | 185 final int i = -d; |
| 186 _insert(key, value, hashPattern, i); | 186 _insert(key, value, hashPattern, i); |
| 187 } | 187 } |
| 188 } | 188 } |
| 189 | 189 |
| 190 V putIfAbsent(K key, V ifAbsent()) { | 190 V putIfAbsent(K key, V ifAbsent()) { |
| 191 final int size = _index.length; | 191 final int size = _index.length; |
| 192 final int sizeMask = size - 1; | 192 final int sizeMask = size - 1; |
| 193 final int maxEntries = size >> 1; | 193 final int maxEntries = size >> 1; |
| 194 final int fullHash = _hashCode(key); | 194 final int fullHash = _hashCode(key); |
| 195 final int hashPattern = _HashBase._hashPattern(fullHash, _hashMask, size); | 195 final int hashPattern = _HashBase._hashPattern(fullHash, _hashMask, size); |
| 196 final int d = _findValueOrInsertPoint(key, fullHash, hashPattern, size); | 196 final int d = _findValueOrInsertPoint(key, fullHash, hashPattern, size); |
| 197 if (d > 0) { | 197 if (d > 0) { |
| 198 return _data[d]; | 198 return _data[d]; |
| 199 } | 199 } |
| 200 // 'ifAbsent' is allowed to modify the map. | 200 // 'ifAbsent' is allowed to modify the map. |
| 201 List oldData = _data; | 201 List oldData = _data; |
| 202 int oldCheckSum = _checkSum; | 202 int oldCheckSum = _checkSum; |
| 203 V value = ifAbsent(); | 203 V value = ifAbsent(); |
| 204 if (_isModifiedSince(oldData, oldCheckSum)) { | 204 if (_isModifiedSince(oldData, oldCheckSum)) { |
| 205 this[key] = value; | 205 this[key] = value; |
| 206 } else { | 206 } else { |
| 207 final int i = -d; | 207 final int i = -d; |
| 208 _insert(key, value, hashPattern, i); | 208 _insert(key, value, hashPattern, i); |
| 209 } | 209 } |
| 210 return value; | 210 return value; |
| 211 } | 211 } |
| 212 | 212 |
| 213 V remove(Object key) { | 213 V remove(Object key) { |
| 214 final int size = _index.length; | 214 final int size = _index.length; |
| 215 final int sizeMask = size - 1; | 215 final int sizeMask = size - 1; |
| 216 final int maxEntries = size >> 1; | 216 final int maxEntries = size >> 1; |
| 217 final int fullHash = _hashCode(key); | 217 final int fullHash = _hashCode(key); |
| 218 final int hashPattern = _HashBase._hashPattern(fullHash, _hashMask, size); | 218 final int hashPattern = _HashBase._hashPattern(fullHash, _hashMask, size); |
| 219 int i = _HashBase._firstProbe(fullHash, sizeMask); | 219 int i = _HashBase._firstProbe(fullHash, sizeMask); |
| 220 int pair = _index[i]; | 220 int pair = _index[i]; |
| 221 while (pair != _HashBase._UNUSED_PAIR) { | 221 while (pair != _HashBase._UNUSED_PAIR) { |
| 222 if (pair != _HashBase._DELETED_PAIR) { | 222 if (pair != _HashBase._DELETED_PAIR) { |
| 223 final int entry = hashPattern ^ pair; | 223 final int entry = hashPattern ^ pair; |
| 224 if (entry < maxEntries) { | 224 if (entry < maxEntries) { |
| 225 final int d = entry << 1; | 225 final int d = entry << 1; |
| 226 if (_equals(key, _data[d])) { | 226 if (_equals(key, _data[d])) { |
| 227 _index[i] = _HashBase._DELETED_PAIR; | 227 _index[i] = _HashBase._DELETED_PAIR; |
| 228 _HashBase._setDeletedAt(_data, d); | 228 _HashBase._setDeletedAt(_data, d); |
| 229 V value = _data[d + 1]; | 229 V value = _data[d + 1]; |
| 230 _HashBase._setDeletedAt(_data, d + 1); | 230 _HashBase._setDeletedAt(_data, d + 1); |
| 231 ++_deletedKeys; | 231 ++_deletedKeys; |
| 232 return value; | 232 return value; |
| 233 } | 233 } |
| 234 } | 234 } |
| 235 } | 235 } |
| 236 i = _HashBase._nextProbe(i, sizeMask); | 236 i = _HashBase._nextProbe(i, sizeMask); |
| 237 pair = _index[i]; | 237 pair = _index[i]; |
| 238 } | 238 } |
| 239 return null; | 239 return null; |
| 240 } | 240 } |
| 241 | 241 |
| 242 // If key is absent, return _data (which is never a value). | 242 // If key is absent, return _data (which is never a value). |
| 243 Object _getValueOrData(Object key) { | 243 Object _getValueOrData(Object key) { |
| 244 final int size = _index.length; | 244 final int size = _index.length; |
| 245 final int sizeMask = size - 1; | 245 final int sizeMask = size - 1; |
| 246 final int maxEntries = size >> 1; | 246 final int maxEntries = size >> 1; |
| 247 final int fullHash = _hashCode(key); | 247 final int fullHash = _hashCode(key); |
| 248 final int hashPattern = _HashBase._hashPattern(fullHash, _hashMask, size); | 248 final int hashPattern = _HashBase._hashPattern(fullHash, _hashMask, size); |
| 249 int i = _HashBase._firstProbe(fullHash, sizeMask); | 249 int i = _HashBase._firstProbe(fullHash, sizeMask); |
| 250 int pair = _index[i]; | 250 int pair = _index[i]; |
| 251 while (pair != _HashBase._UNUSED_PAIR) { | 251 while (pair != _HashBase._UNUSED_PAIR) { |
| 252 if (pair != _HashBase._DELETED_PAIR) { | 252 if (pair != _HashBase._DELETED_PAIR) { |
| 253 final int entry = hashPattern ^ pair; | 253 final int entry = hashPattern ^ pair; |
| 254 if (entry < maxEntries) { | 254 if (entry < maxEntries) { |
| 255 final int d = entry << 1; | 255 final int d = entry << 1; |
| 256 if (_equals(key, _data[d])) { | 256 if (_equals(key, _data[d])) { |
| 257 return _data[d + 1]; | 257 return _data[d + 1]; |
| 258 } | 258 } |
| 259 } | 259 } |
| 260 } | 260 } |
| 261 i = _HashBase._nextProbe(i, sizeMask); | 261 i = _HashBase._nextProbe(i, sizeMask); |
| 262 pair = _index[i]; | 262 pair = _index[i]; |
| 263 } | 263 } |
| 264 return _data; | 264 return _data; |
| 265 } | 265 } |
| 266 | 266 |
| 267 bool containsKey(Object key) => !identical(_data, _getValueOrData(key)); | 267 bool containsKey(Object key) => !identical(_data, _getValueOrData(key)); |
| 268 | 268 |
| 269 V operator[](Object key) { | 269 V operator[](Object key) { |
| 270 var v = _getValueOrData(key); | 270 var v = _getValueOrData(key); |
| 271 return identical(_data, v) ? null : v; | 271 return identical(_data, v) ? null : v; |
| 272 } | 272 } |
| 273 | 273 |
| 274 bool containsValue(Object value) { | 274 bool containsValue(Object value) { |
| 275 for (var v in values) { | 275 for (var v in values) { |
| 276 // Spec. says this should always use "==", also for identity maps, etc. | 276 // Spec. says this should always use "==", also for identity maps, etc. |
| 277 if (v == value) { | 277 if (v == value) { |
| 278 return true; | 278 return true; |
| 279 } | 279 } |
| 280 } | 280 } |
| 281 return false; | 281 return false; |
| 282 } | 282 } |
| 283 | 283 |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 302 | 302 |
| 303 class _CompactLinkedCustomHashMap<K, V> | 303 class _CompactLinkedCustomHashMap<K, V> |
| 304 extends _CompactLinkedHashMap<K, V> { | 304 extends _CompactLinkedHashMap<K, V> { |
| 305 final _equality; | 305 final _equality; |
| 306 final _hasher; | 306 final _hasher; |
| 307 final _validKey; | 307 final _validKey; |
| 308 | 308 |
| 309 // TODO(koda): Ask gbracha why I cannot have fields _equals/_hashCode. | 309 // TODO(koda): Ask gbracha why I cannot have fields _equals/_hashCode. |
| 310 int _hashCode(e) => _hasher(e); | 310 int _hashCode(e) => _hasher(e); |
| 311 bool _equals(e1, e2) => _equality(e1, e2); | 311 bool _equals(e1, e2) => _equality(e1, e2); |
| 312 | 312 |
| 313 bool containsKey(Object o) => _validKey(o) ? super.containsKey(o) : false; | 313 bool containsKey(Object o) => _validKey(o) ? super.containsKey(o) : false; |
| 314 V operator[](Object o) => _validKey(o) ? super[o] : null; | 314 V operator[](Object o) => _validKey(o) ? super[o] : null; |
| 315 V remove(Object o) => _validKey(o) ? super.remove(o) : null; | 315 V remove(Object o) => _validKey(o) ? super.remove(o) : null; |
| 316 | 316 |
| 317 _CompactLinkedCustomHashMap(this._equality, this._hasher, validKey) | 317 _CompactLinkedCustomHashMap(this._equality, this._hasher, validKey) |
| 318 : _validKey = (validKey != null) ? validKey : new _TypeTest<K>().test; | 318 : _validKey = (validKey != null) ? validKey : new _TypeTest<K>().test; |
| 319 } | 319 } |
| 320 | 320 |
| 321 // Iterates through _data[_offset + _step], _data[_offset + 2*_step], ... | 321 // Iterates through _data[_offset + _step], _data[_offset + 2*_step], ... |
| 322 // and checks for concurrent modification. | 322 // and checks for concurrent modification. |
| 323 class _CompactIterable<E> extends IterableBase<E> { | 323 class _CompactIterable<E> extends IterableBase<E> { |
| 324 final _table; | 324 final _table; |
| 325 final List _data; | 325 final List _data; |
| 326 final int _len; | 326 final int _len; |
| 327 final int _offset; | 327 final int _offset; |
| 328 final int _step; | 328 final int _step; |
| 329 | 329 |
| 330 _CompactIterable(this._table, this._data, this._len, | 330 _CompactIterable(this._table, this._data, this._len, |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 380 | 380 |
| 381 int get length => _usedData - _deletedKeys; | 381 int get length => _usedData - _deletedKeys; |
| 382 | 382 |
| 383 void _rehash() { | 383 void _rehash() { |
| 384 if ((_deletedKeys << 1) > _usedData) { | 384 if ((_deletedKeys << 1) > _usedData) { |
| 385 _init(_index.length, _hashMask, _data, _usedData); | 385 _init(_index.length, _hashMask, _data, _usedData); |
| 386 } else { | 386 } else { |
| 387 _init(_index.length << 1, _hashMask >> 1, _data, _usedData); | 387 _init(_index.length << 1, _hashMask >> 1, _data, _usedData); |
| 388 } | 388 } |
| 389 } | 389 } |
| 390 | 390 |
| 391 void clear() { | 391 void clear() { |
| 392 if (!isEmpty) { | 392 if (!isEmpty) { |
| 393 _init(_index.length, _hashMask); | 393 _init(_index.length, _hashMask); |
| 394 } | 394 } |
| 395 } | 395 } |
| 396 | 396 |
| 397 void _init(int size, int hashMask, [List oldData, int oldUsed]) { | 397 void _init(int size, int hashMask, [List oldData, int oldUsed]) { |
| 398 _index = new Uint32List(size); | 398 _index = new Uint32List(size); |
| 399 _hashMask = hashMask; | 399 _hashMask = hashMask; |
| 400 _data = new List(size >> 1); | 400 _data = new List(size >> 1); |
| 401 _usedData = 0; | 401 _usedData = 0; |
| 402 _deletedKeys = 0; | 402 _deletedKeys = 0; |
| 403 if (oldData != null) { | 403 if (oldData != null) { |
| 404 for (int i = 0; i < oldUsed; i += 1) { | 404 for (int i = 0; i < oldUsed; i += 1) { |
| 405 var key = oldData[i]; | 405 var key = oldData[i]; |
| 406 if (!_HashBase._isDeleted(oldData, key)) { | 406 if (!_HashBase._isDeleted(oldData, key)) { |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 438 add(key); | 438 add(key); |
| 439 } else { | 439 } else { |
| 440 final int insertionPoint = (firstDeleted >= 0) ? firstDeleted : i; | 440 final int insertionPoint = (firstDeleted >= 0) ? firstDeleted : i; |
| 441 assert(1 <= hashPattern && hashPattern < (1 << 32)); | 441 assert(1 <= hashPattern && hashPattern < (1 << 32)); |
| 442 assert((hashPattern & _usedData) == 0); | 442 assert((hashPattern & _usedData) == 0); |
| 443 _index[insertionPoint] = hashPattern | _usedData; | 443 _index[insertionPoint] = hashPattern | _usedData; |
| 444 _data[_usedData++] = key; | 444 _data[_usedData++] = key; |
| 445 } | 445 } |
| 446 return true; | 446 return true; |
| 447 } | 447 } |
| 448 | 448 |
| 449 // If key is absent, return _data (which is never a value). | 449 // If key is absent, return _data (which is never a value). |
| 450 Object _getKeyOrData(Object key) { | 450 Object _getKeyOrData(Object key) { |
| 451 final int size = _index.length; | 451 final int size = _index.length; |
| 452 final int sizeMask = size - 1; | 452 final int sizeMask = size - 1; |
| 453 final int maxEntries = size >> 1; | 453 final int maxEntries = size >> 1; |
| 454 final int fullHash = _hashCode(key); | 454 final int fullHash = _hashCode(key); |
| 455 final int hashPattern = _HashBase._hashPattern(fullHash, _hashMask, size); | 455 final int hashPattern = _HashBase._hashPattern(fullHash, _hashMask, size); |
| 456 int i = _HashBase._firstProbe(fullHash, sizeMask); | 456 int i = _HashBase._firstProbe(fullHash, sizeMask); |
| 457 int pair = _index[i]; | 457 int pair = _index[i]; |
| 458 while (pair != _HashBase._UNUSED_PAIR) { | 458 while (pair != _HashBase._UNUSED_PAIR) { |
| 459 if (pair != _HashBase._DELETED_PAIR) { | 459 if (pair != _HashBase._DELETED_PAIR) { |
| 460 final int d = hashPattern ^ pair; | 460 final int d = hashPattern ^ pair; |
| 461 if (d < maxEntries && _equals(key, _data[d])) { | 461 if (d < maxEntries && _equals(key, _data[d])) { |
| 462 return _data[d]; // Note: Must return the existing key. | 462 return _data[d]; // Note: Must return the existing key. |
| 463 } | 463 } |
| 464 } | 464 } |
| 465 i = _HashBase._nextProbe(i, sizeMask); | 465 i = _HashBase._nextProbe(i, sizeMask); |
| 466 pair = _index[i]; | 466 pair = _index[i]; |
| 467 } | 467 } |
| 468 return _data; | 468 return _data; |
| 469 } | 469 } |
| 470 | 470 |
| 471 E lookup(Object key) { | 471 E lookup(Object key) { |
| 472 var k = _getKeyOrData(key); | 472 var k = _getKeyOrData(key); |
| 473 return identical(_data, k) ? null : k; | 473 return identical(_data, k) ? null : k; |
| 474 } | 474 } |
| 475 | 475 |
| 476 bool contains(Object key) => !identical(_data, _getKeyOrData(key)); | 476 bool contains(Object key) => !identical(_data, _getKeyOrData(key)); |
| 477 | 477 |
| 478 bool remove(Object key) { | 478 bool remove(Object key) { |
| 479 final int size = _index.length; | 479 final int size = _index.length; |
| 480 final int sizeMask = size - 1; | 480 final int sizeMask = size - 1; |
| 481 final int maxEntries = size >> 1; | 481 final int maxEntries = size >> 1; |
| 482 final int fullHash = _hashCode(key); | 482 final int fullHash = _hashCode(key); |
| 483 final int hashPattern = _HashBase._hashPattern(fullHash, _hashMask, size); | 483 final int hashPattern = _HashBase._hashPattern(fullHash, _hashMask, size); |
| 484 int i = _HashBase._firstProbe(fullHash, sizeMask); | 484 int i = _HashBase._firstProbe(fullHash, sizeMask); |
| 485 int pair = _index[i]; | 485 int pair = _index[i]; |
| 486 while (pair != _HashBase._UNUSED_PAIR) { | 486 while (pair != _HashBase._UNUSED_PAIR) { |
| 487 if (pair != _HashBase._DELETED_PAIR) { | 487 if (pair != _HashBase._DELETED_PAIR) { |
| 488 final int d = hashPattern ^ pair; | 488 final int d = hashPattern ^ pair; |
| 489 if (d < maxEntries && _equals(key, _data[d])) { | 489 if (d < maxEntries && _equals(key, _data[d])) { |
| 490 _index[i] = _HashBase._DELETED_PAIR; | 490 _index[i] = _HashBase._DELETED_PAIR; |
| 491 _HashBase._setDeletedAt(_data, d); | 491 _HashBase._setDeletedAt(_data, d); |
| 492 ++_deletedKeys; | 492 ++_deletedKeys; |
| 493 return true; | 493 return true; |
| 494 } | 494 } |
| 495 } | 495 } |
| 496 i = _HashBase._nextProbe(i, sizeMask); | 496 i = _HashBase._nextProbe(i, sizeMask); |
| 497 pair = _index[i]; | 497 pair = _index[i]; |
| 498 } | 498 } |
| 499 return false; | 499 return false; |
| 500 } | 500 } |
| 501 | 501 |
| 502 Iterator<E> get iterator => | 502 Iterator<E> get iterator => |
| 503 new _CompactIterator<E>(this, _data, _usedData, -1, 1); | 503 new _CompactIterator<E>(this, _data, _usedData, -1, 1); |
| 504 | 504 |
| 505 // Returns a set of the same type, although this | 505 // Returns a set of the same type, although this |
| 506 // is not required by the spec. (For instance, always using an identity set | 506 // is not required by the spec. (For instance, always using an identity set |
| 507 // would be technically correct, albeit surprising.) | 507 // would be technically correct, albeit surprising.) |
| 508 Set<E> toSet() => new _CompactLinkedHashSet<E>()..addAll(this); | 508 Set<E> toSet() => new _CompactLinkedHashSet<E>()..addAll(this); |
| 509 } | 509 } |
| 510 | 510 |
| 511 class _CompactLinkedIdentityHashSet<E> | 511 class _CompactLinkedIdentityHashSet<E> |
| 512 extends _CompactLinkedHashSet<E> with _IdenticalAndIdentityHashCode { | 512 extends _CompactLinkedHashSet<E> with _IdenticalAndIdentityHashCode { |
| 513 Set<E> toSet() => new _CompactLinkedIdentityHashSet<E>()..addAll(this); | 513 Set<E> toSet() => new _CompactLinkedIdentityHashSet<E>()..addAll(this); |
| 514 } | 514 } |
| 515 | 515 |
| 516 class _CompactLinkedCustomHashSet<E> | 516 class _CompactLinkedCustomHashSet<E> |
| 517 extends _CompactLinkedHashSet<E> { | 517 extends _CompactLinkedHashSet<E> { |
| 518 final _equality; | 518 final _equality; |
| 519 final _hasher; | 519 final _hasher; |
| 520 final _validKey; | 520 final _validKey; |
| 521 | 521 |
| 522 int _hashCode(e) => _hasher(e); | 522 int _hashCode(e) => _hasher(e); |
| 523 bool _equals(e1, e2) => _equality(e1, e2); | 523 bool _equals(e1, e2) => _equality(e1, e2); |
| 524 | 524 |
| 525 bool contains(Object o) => _validKey(o) ? super.contains(o) : false; | 525 bool contains(Object o) => _validKey(o) ? super.contains(o) : false; |
| 526 E lookup(Object o) => _validKey(o) ? super.lookup(o) : null; | 526 E lookup(Object o) => _validKey(o) ? super.lookup(o) : null; |
| 527 bool remove(Object o) => _validKey(o) ? super.remove(o) : false; | 527 bool remove(Object o) => _validKey(o) ? super.remove(o) : false; |
| 528 | 528 |
| 529 _CompactLinkedCustomHashSet(this._equality, this._hasher, validKey) | 529 _CompactLinkedCustomHashSet(this._equality, this._hasher, validKey) |
| 530 : _validKey = (validKey != null) ? validKey : new _TypeTest<E>().test; | 530 : _validKey = (validKey != null) ? validKey : new _TypeTest<E>().test; |
| 531 | 531 |
| 532 Set<E> toSet() => | 532 Set<E> toSet() => |
| 533 new _CompactLinkedCustomHashSet<E>(_equality, _hasher, _validKey) | 533 new _CompactLinkedCustomHashSet<E>(_equality, _hasher, _validKey) |
| 534 ..addAll(this); | 534 ..addAll(this); |
| 535 } | 535 } |
| OLD | NEW |