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 |