| 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 import 'dart:_internal' as internal; | 6 import 'dart:_internal' as internal; |
| 7 | 7 |
| 8 // Hash table with open addressing that separates the index from keys/values. | 8 // Hash table with open addressing that separates the index from keys/values. |
| 9 | 9 |
| 10 abstract class _HashFieldBase { | 10 abstract class _HashFieldBase { |
| 11 // Each occupied entry in _index is a fixed-size integer that encodes a pair: | 11 // Each occupied entry in _index is a fixed-size integer that encodes a pair: |
| 12 // [ hash pattern for key | index of entry in _data ] | 12 // [ hash pattern for key | index of entry in _data ] |
| 13 // The hash pattern is based on hashCode, but is guaranteed to be non-zero. | 13 // The hash pattern is based on hashCode, but is guaranteed to be non-zero. |
| 14 // The length of _index is always a power of two, and there is always at | 14 // The length of _index is always a power of two, and there is always at |
| 15 // least one unoccupied entry. | 15 // least one unoccupied entry. |
| 16 // NOTE: When maps are deserialized, their _index and _hashMask is regenerated | 16 // NOTE: When maps are deserialized, their _index and _hashMask is regenerated |
| 17 // lazily by _regenerateIndex. | 17 // lazily by _regenerateIndex. |
| 18 // TODO(koda): Consider also using null _index for tiny, linear-search tables. | 18 // TODO(koda): Consider also using null _index for tiny, linear-search tables. |
| 19 Uint32List _index = new Uint32List(_HashBase._INITIAL_INDEX_SIZE); | 19 Uint32List _index; |
| 20 | 20 |
| 21 // Cached in-place mask for the hash pattern component. | 21 // Cached in-place mask for the hash pattern component. |
| 22 int _hashMask = _HashBase._indexSizeToHashMask(_HashBase._INITIAL_INDEX_SIZE); | 22 int _hashMask; |
| 23 | 23 |
| 24 // Fixed-length list of keys (set) or key/value at even/odd indices (map). | 24 // Fixed-length list of keys (set) or key/value at even/odd indices (map). |
| 25 List _data = new List(_HashBase._INITIAL_INDEX_SIZE); | 25 List _data; |
| 26 | 26 |
| 27 // Length of _data that is used (i.e., keys + values for a map). | 27 // Length of _data that is used (i.e., keys + values for a map). |
| 28 int _usedData = 0; | 28 int _usedData = 0; |
| 29 | 29 |
| 30 // Number of deleted keys. | 30 // Number of deleted keys. |
| 31 int _deletedKeys = 0; | 31 int _deletedKeys = 0; |
| 32 } | 32 } |
| 33 | 33 |
| 34 // Base class for VM-internal classes; keep in sync with _HashFieldBase. | 34 // Base class for VM-internal classes; keep in sync with _HashFieldBase. |
| 35 abstract class _HashVMBase { | 35 abstract class _HashVMBase { |
| (...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 118 with MapMixin<K, V>, _LinkedHashMapMixin<K, V>, _HashBase, | 118 with MapMixin<K, V>, _LinkedHashMapMixin<K, V>, _HashBase, |
| 119 _OperatorEqualsAndHashCode | 119 _OperatorEqualsAndHashCode |
| 120 implements LinkedHashMap<K, V> { | 120 implements LinkedHashMap<K, V> { |
| 121 factory _InternalLinkedHashMap() native "LinkedHashMap_allocate"; | 121 factory _InternalLinkedHashMap() native "LinkedHashMap_allocate"; |
| 122 } | 122 } |
| 123 | 123 |
| 124 class _LinkedHashMapMixin<K, V> { | 124 class _LinkedHashMapMixin<K, V> { |
| 125 int get length => (_usedData >> 1) - _deletedKeys; | 125 int get length => (_usedData >> 1) - _deletedKeys; |
| 126 bool get isEmpty => length == 0; | 126 bool get isEmpty => length == 0; |
| 127 bool get isNotEmpty => !isEmpty; | 127 bool get isNotEmpty => !isEmpty; |
| 128 | 128 |
| 129 void _rehash() { | 129 void _rehash() { |
| 130 if ((_deletedKeys << 2) > _usedData) { | 130 if ((_deletedKeys << 2) > _usedData) { |
| 131 // TODO(koda): Consider shrinking. | 131 // TODO(koda): Consider shrinking. |
| 132 // TODO(koda): Consider in-place compaction and more costly CME check. | 132 // TODO(koda): Consider in-place compaction and more costly CME check. |
| 133 _init(_index.length, _hashMask, _data, _usedData); | 133 _init(_index.length, _hashMask, _data, _usedData); |
| 134 } else { | 134 } else { |
| 135 // TODO(koda): Support 32->64 bit transition (and adjust _hashMask). | 135 // TODO(koda): Support 32->64 bit transition (and adjust _hashMask). |
| 136 _init(_index.length << 1, _hashMask >> 1, _data, _usedData); | 136 _init(_index.length << 1, _hashMask >> 1, _data, _usedData); |
| 137 } | 137 } |
| 138 } | 138 } |
| 139 | 139 |
| 140 void clear() { | 140 void clear() { |
| 141 if (!isEmpty) { | 141 if (!isEmpty) { |
| 142 // Use _data.length, since _index might be null. | 142 // Use _data.length, since _index might be null. |
| 143 _init(_data.length, _hashMask); | 143 _init(_data.length, _hashMask, null, 0); |
| 144 } | 144 } |
| 145 } | 145 } |
| 146 | 146 |
| 147 // Allocate new _index and _data, and optionally copy existing contents. | 147 // Allocate new _index and _data, and optionally copy existing contents. |
| 148 void _init(int size, int hashMask, [List oldData, int oldUsed]) { | 148 void _init(int size, int hashMask, List oldData, int oldUsed) { |
| 149 assert(size & (size - 1) == 0); | 149 assert(size & (size - 1) == 0); |
| 150 assert(_HashBase._UNUSED_PAIR == 0); | 150 assert(_HashBase._UNUSED_PAIR == 0); |
| 151 _index = new Uint32List(size); | 151 _index = new Uint32List(size); |
| 152 _hashMask = hashMask; | 152 _hashMask = hashMask; |
| 153 _data = new List(size); | 153 _data = new List(size); |
| 154 _usedData = 0; | 154 _usedData = 0; |
| 155 _deletedKeys = 0; | 155 _deletedKeys = 0; |
| 156 if (oldData != null) { | 156 if (oldData != null) { |
| 157 for (int i = 0; i < oldUsed; i += 2) { | 157 for (int i = 0; i < oldUsed; i += 2) { |
| 158 var key = oldData[i]; | 158 var key = oldData[i]; |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 199 // If key is present, returns the index of the value in _data, else returns | 199 // If key is present, returns the index of the value in _data, else returns |
| 200 // the negated insertion point in _index. | 200 // the negated insertion point in _index. |
| 201 int _findValueOrInsertPoint(K key, int fullHash, int hashPattern, int size) { | 201 int _findValueOrInsertPoint(K key, int fullHash, int hashPattern, int size) { |
| 202 final int sizeMask = size - 1; | 202 final int sizeMask = size - 1; |
| 203 final int maxEntries = size >> 1; | 203 final int maxEntries = size >> 1; |
| 204 int i = _HashBase._firstProbe(fullHash, sizeMask); | 204 int i = _HashBase._firstProbe(fullHash, sizeMask); |
| 205 int firstDeleted = -1; | 205 int firstDeleted = -1; |
| 206 int pair = _index[i]; | 206 int pair = _index[i]; |
| 207 while (pair != _HashBase._UNUSED_PAIR) { | 207 while (pair != _HashBase._UNUSED_PAIR) { |
| 208 if (pair == _HashBase._DELETED_PAIR) { | 208 if (pair == _HashBase._DELETED_PAIR) { |
| 209 if (firstDeleted < 0){ | 209 if (firstDeleted < 0) { |
| 210 firstDeleted = i; | 210 firstDeleted = i; |
| 211 } | 211 } |
| 212 } else { | 212 } else { |
| 213 final int entry = hashPattern ^ pair; | 213 final int entry = hashPattern ^ pair; |
| 214 if (entry < maxEntries) { | 214 if (entry < maxEntries) { |
| 215 final int d = entry << 1; | 215 final int d = entry << 1; |
| 216 if (_equals(key, _data[d])) { | 216 if (_equals(key, _data[d])) { |
| 217 return d + 1; | 217 return d + 1; |
| 218 } | 218 } |
| 219 } | 219 } |
| (...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 344 Iterable<K> get keys => | 344 Iterable<K> get keys => |
| 345 new _CompactIterable<K>(this, _data, _usedData, -2, 2); | 345 new _CompactIterable<K>(this, _data, _usedData, -2, 2); |
| 346 Iterable<V> get values => | 346 Iterable<V> get values => |
| 347 new _CompactIterable<V>(this, _data, _usedData, -1, 2); | 347 new _CompactIterable<V>(this, _data, _usedData, -1, 2); |
| 348 } | 348 } |
| 349 | 349 |
| 350 class _CompactLinkedIdentityHashMap<K, V> extends _HashFieldBase | 350 class _CompactLinkedIdentityHashMap<K, V> extends _HashFieldBase |
| 351 with MapMixin<K, V>, _LinkedHashMapMixin<K, V>, _HashBase, | 351 with MapMixin<K, V>, _LinkedHashMapMixin<K, V>, _HashBase, |
| 352 _IdenticalAndIdentityHashCode | 352 _IdenticalAndIdentityHashCode |
| 353 implements LinkedHashMap<K, V> { | 353 implements LinkedHashMap<K, V> { |
| 354 |
| 355 _CompactLinkedIdentityHashMap() { |
| 356 _index = new Uint32List(_HashBase._INITIAL_INDEX_SIZE); |
| 357 _hashMask = _HashBase._indexSizeToHashMask(_HashBase._INITIAL_INDEX_SIZE); |
| 358 _data = new List(_HashBase._INITIAL_INDEX_SIZE); |
| 359 } |
| 354 } | 360 } |
| 355 | 361 |
| 356 class _CompactLinkedCustomHashMap<K, V> extends _HashFieldBase | 362 class _CompactLinkedCustomHashMap<K, V> extends _HashFieldBase |
| 357 with MapMixin<K, V>, _LinkedHashMapMixin<K, V>, _HashBase | 363 with MapMixin<K, V>, _LinkedHashMapMixin<K, V>, _HashBase |
| 358 implements LinkedHashMap<K, V> { | 364 implements LinkedHashMap<K, V> { |
| 359 final _equality; | 365 final _equality; |
| 360 final _hasher; | 366 final _hasher; |
| 361 final _validKey; | 367 final _validKey; |
| 362 | 368 |
| 363 // TODO(koda): Ask gbracha why I cannot have fields _equals/_hashCode. | 369 // TODO(koda): Ask gbracha why I cannot have fields _equals/_hashCode. |
| 364 int _hashCode(e) => _hasher(e); | 370 int _hashCode(e) => _hasher(e); |
| 365 bool _equals(e1, e2) => _equality(e1, e2); | 371 bool _equals(e1, e2) => _equality(e1, e2); |
| 366 | 372 |
| 367 bool containsKey(Object o) => _validKey(o) ? super.containsKey(o) : false; | 373 bool containsKey(Object o) => _validKey(o) ? super.containsKey(o) : false; |
| 368 V operator[](Object o) => _validKey(o) ? super[o] : null; | 374 V operator[](Object o) => _validKey(o) ? super[o] : null; |
| 369 V remove(Object o) => _validKey(o) ? super.remove(o) : null; | 375 V remove(Object o) => _validKey(o) ? super.remove(o) : null; |
| 370 | 376 |
| 371 _CompactLinkedCustomHashMap(this._equality, this._hasher, validKey) | 377 _CompactLinkedCustomHashMap(this._equality, this._hasher, validKey) |
| 372 : _validKey = (validKey != null) ? validKey : new _TypeTest<K>().test; | 378 : _validKey = (validKey != null) ? validKey : new _TypeTest<K>().test { |
| 379 _index = new Uint32List(_HashBase._INITIAL_INDEX_SIZE); |
| 380 _hashMask = _HashBase._indexSizeToHashMask(_HashBase._INITIAL_INDEX_SIZE); |
| 381 _data = new List(_HashBase._INITIAL_INDEX_SIZE); |
| 382 } |
| 373 } | 383 } |
| 374 | 384 |
| 375 // Iterates through _data[_offset + _step], _data[_offset + 2*_step], ... | 385 // Iterates through _data[_offset + _step], _data[_offset + 2*_step], ... |
| 376 // and checks for concurrent modification. | 386 // and checks for concurrent modification. |
| 377 class _CompactIterable<E> extends IterableBase<E> { | 387 class _CompactIterable<E> extends IterableBase<E> { |
| 378 final _table; | 388 final _table; |
| 379 final List _data; | 389 final List _data; |
| 380 final int _len; | 390 final int _len; |
| 381 final int _offset; | 391 final int _offset; |
| 382 final int _step; | 392 final int _step; |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 422 } | 432 } |
| 423 | 433 |
| 424 // Set implementation, analogous to _CompactLinkedHashMap. | 434 // Set implementation, analogous to _CompactLinkedHashMap. |
| 425 class _CompactLinkedHashSet<E> extends _HashFieldBase | 435 class _CompactLinkedHashSet<E> extends _HashFieldBase |
| 426 with _HashBase, _OperatorEqualsAndHashCode, SetMixin<E> | 436 with _HashBase, _OperatorEqualsAndHashCode, SetMixin<E> |
| 427 implements LinkedHashSet<E> { | 437 implements LinkedHashSet<E> { |
| 428 | 438 |
| 429 _CompactLinkedHashSet() { | 439 _CompactLinkedHashSet() { |
| 430 assert(_HashBase._UNUSED_PAIR == 0); | 440 assert(_HashBase._UNUSED_PAIR == 0); |
| 431 _index = new Uint32List(_HashBase._INITIAL_INDEX_SIZE); | 441 _index = new Uint32List(_HashBase._INITIAL_INDEX_SIZE); |
| 442 _hashMask = _HashBase._indexSizeToHashMask(_HashBase._INITIAL_INDEX_SIZE); |
| 432 _data = new List(_HashBase._INITIAL_INDEX_SIZE >> 1); | 443 _data = new List(_HashBase._INITIAL_INDEX_SIZE >> 1); |
| 433 } | 444 } |
| 434 | 445 |
| 435 int get length => _usedData - _deletedKeys; | 446 int get length => _usedData - _deletedKeys; |
| 436 | 447 |
| 437 void _rehash() { | 448 void _rehash() { |
| 438 if ((_deletedKeys << 1) > _usedData) { | 449 if ((_deletedKeys << 1) > _usedData) { |
| 439 _init(_index.length, _hashMask, _data, _usedData); | 450 _init(_index.length, _hashMask, _data, _usedData); |
| 440 } else { | 451 } else { |
| 441 _init(_index.length << 1, _hashMask >> 1, _data, _usedData); | 452 _init(_index.length << 1, _hashMask >> 1, _data, _usedData); |
| 442 } | 453 } |
| 443 } | 454 } |
| 444 | 455 |
| 445 void clear() { | 456 void clear() { |
| 446 if (!isEmpty) { | 457 if (!isEmpty) { |
| 447 _init(_index.length, _hashMask); | 458 _init(_index.length, _hashMask, null, 0); |
| 448 } | 459 } |
| 449 } | 460 } |
| 450 | 461 |
| 451 void _init(int size, int hashMask, [List oldData, int oldUsed]) { | 462 void _init(int size, int hashMask, List oldData, int oldUsed) { |
| 452 _index = new Uint32List(size); | 463 _index = new Uint32List(size); |
| 453 _hashMask = hashMask; | 464 _hashMask = hashMask; |
| 454 _data = new List(size >> 1); | 465 _data = new List(size >> 1); |
| 455 _usedData = 0; | 466 _usedData = 0; |
| 456 _deletedKeys = 0; | 467 _deletedKeys = 0; |
| 457 if (oldData != null) { | 468 if (oldData != null) { |
| 458 for (int i = 0; i < oldUsed; i += 1) { | 469 for (int i = 0; i < oldUsed; i += 1) { |
| 459 var key = oldData[i]; | 470 var key = oldData[i]; |
| 460 if (!_HashBase._isDeleted(oldData, key)) { | 471 if (!_HashBase._isDeleted(oldData, key)) { |
| 461 add(key); | 472 add(key); |
| 462 } | 473 } |
| 463 } | 474 } |
| 464 } | 475 } |
| 465 } | 476 } |
| 466 | 477 |
| 467 bool add(E key) { | 478 bool add(E key) { |
| 468 final int size = _index.length; | 479 final int size = _index.length; |
| 469 final int sizeMask = size - 1; | 480 final int sizeMask = size - 1; |
| 470 final int maxEntries = size >> 1; | 481 final int maxEntries = size >> 1; |
| 471 final int fullHash = _hashCode(key); | 482 final int fullHash = _hashCode(key); |
| 472 final int hashPattern = _HashBase._hashPattern(fullHash, _hashMask, size); | 483 final int hashPattern = _HashBase._hashPattern(fullHash, _hashMask, size); |
| 473 int i = _HashBase._firstProbe(fullHash, sizeMask); | 484 int i = _HashBase._firstProbe(fullHash, sizeMask); |
| 474 int firstDeleted = -1; | 485 int firstDeleted = -1; |
| 475 int pair = _index[i]; | 486 int pair = _index[i]; |
| 476 while (pair != _HashBase._UNUSED_PAIR) { | 487 while (pair != _HashBase._UNUSED_PAIR) { |
| 477 if (pair == _HashBase._DELETED_PAIR) { | 488 if (pair == _HashBase._DELETED_PAIR) { |
| 478 if (firstDeleted < 0){ | 489 if (firstDeleted < 0) { |
| 479 firstDeleted = i; | 490 firstDeleted = i; |
| 480 } | 491 } |
| 481 } else { | 492 } else { |
| 482 final int d = hashPattern ^ pair; | 493 final int d = hashPattern ^ pair; |
| 483 if (d < maxEntries && _equals(key, _data[d])) { | 494 if (d < maxEntries && _equals(key, _data[d])) { |
| 484 return false; | 495 return false; |
| 485 } | 496 } |
| 486 } | 497 } |
| 487 i = _HashBase._nextProbe(i, sizeMask); | 498 i = _HashBase._nextProbe(i, sizeMask); |
| 488 pair = _index[i]; | 499 pair = _index[i]; |
| (...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 580 E lookup(Object o) => _validKey(o) ? super.lookup(o) : null; | 591 E lookup(Object o) => _validKey(o) ? super.lookup(o) : null; |
| 581 bool remove(Object o) => _validKey(o) ? super.remove(o) : false; | 592 bool remove(Object o) => _validKey(o) ? super.remove(o) : false; |
| 582 | 593 |
| 583 _CompactLinkedCustomHashSet(this._equality, this._hasher, validKey) | 594 _CompactLinkedCustomHashSet(this._equality, this._hasher, validKey) |
| 584 : _validKey = (validKey != null) ? validKey : new _TypeTest<E>().test; | 595 : _validKey = (validKey != null) ? validKey : new _TypeTest<E>().test; |
| 585 | 596 |
| 586 Set<E> toSet() => | 597 Set<E> toSet() => |
| 587 new _CompactLinkedCustomHashSet<E>(_equality, _hasher, _validKey) | 598 new _CompactLinkedCustomHashSet<E>(_equality, _hasher, _validKey) |
| 588 ..addAll(this); | 599 ..addAll(this); |
| 589 } | 600 } |
| OLD | NEW |