OLD | NEW |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 patch class HashMap<K, V> { | 5 patch class HashMap<K, V> { |
6 final _HashMapTable<K, V> _hashTable = new _HashMapTable<K, V>(); | 6 final _HashMapTable<K, V> _hashTable = new _HashMapTable<K, V>(); |
7 | 7 |
8 /* patch */ HashMap() { | 8 /* patch */ HashMap() { |
9 _hashTable._container = this; | 9 _hashTable._container = this; |
10 } | 10 } |
11 | 11 |
12 | |
13 /* patch */ bool containsKey(K key) { | 12 /* patch */ bool containsKey(K key) { |
14 return _hashTable._get(key) >= 0; | 13 return _hashTable._get(key) >= 0; |
15 } | 14 } |
16 | 15 |
17 /* patch */ bool containsValue(V value) { | 16 /* patch */ bool containsValue(V value) { |
18 List table = _hashTable._table; | 17 List table = _hashTable._table; |
19 int entrySize = _hashTable._entrySize; | 18 int entrySize = _hashTable._entrySize; |
20 for (int offset = 0; offset < table.length; offset += entrySize) { | 19 for (int offset = 0; offset < table.length; offset += entrySize) { |
21 if (!_hashTable._isFree(table[offset]) && | 20 if (!_hashTable._isFree(table[offset]) && |
22 _hashTable._value(offset) == value) { | 21 _hashTable._value(offset) == value) { |
(...skipping 160 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
183 | 182 |
184 /* patch */ void retainWhere(bool test(E element)) { | 183 /* patch */ void retainWhere(bool test(E element)) { |
185 _filterWhere(test, false); | 184 _filterWhere(test, false); |
186 } | 185 } |
187 | 186 |
188 /* patch */ void clear() { | 187 /* patch */ void clear() { |
189 _table._clear(); | 188 _table._clear(); |
190 } | 189 } |
191 } | 190 } |
192 | 191 |
193 class _LinkedHashMapTable<K, V> extends _LinkedHashTable<K> { | |
194 static const int _INITIAL_CAPACITY = 8; | |
195 static const int _VALUE_INDEX = 3; | |
196 | |
197 int get _entrySize => 4; | |
198 | |
199 _LinkedHashMapTable() : super(_INITIAL_CAPACITY); | |
200 | |
201 V _value(int offset) => _table[offset + _VALUE_INDEX]; | |
202 void _setValue(int offset, V value) { _table[offset + _VALUE_INDEX] = value; } | |
203 | |
204 _copyEntry(List oldTable, int fromOffset, int toOffset) { | |
205 _table[toOffset + _VALUE_INDEX] = oldTable[fromOffset + _VALUE_INDEX]; | |
206 } | |
207 } | |
208 | |
209 /** | 192 /** |
210 * A hash-based map that iterates keys and values in key insertion order. | 193 * A hash-based map that iterates keys and values in key insertion order. |
211 */ | 194 */ |
212 patch class LinkedHashMap<K, V> { | 195 patch class LinkedHashMap<K, V> { |
213 final _LinkedHashMapTable _hashTable; | 196 final _LinkedHashMapTable _hashTable; |
214 | 197 |
215 /* patch */ LinkedHashMap() : _hashTable = new _LinkedHashMapTable<K, V>() { | 198 /* patch */ LinkedHashMap() : _hashTable = new _LinkedHashMapTable<K, V>() { |
216 _hashTable._container = this; | 199 _hashTable._container = this; |
217 } | 200 } |
218 | 201 |
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
311 new _LinkedHashTableKeyIterable<K>(_hashTable); | 294 new _LinkedHashTableKeyIterable<K>(_hashTable); |
312 | 295 |
313 /* patch */ Iterable<V> get values => | 296 /* patch */ Iterable<V> get values => |
314 new _LinkedHashTableValueIterable<V>(_hashTable, | 297 new _LinkedHashTableValueIterable<V>(_hashTable, |
315 _LinkedHashMapTable._VALUE_INDEX); | 298 _LinkedHashMapTable._VALUE_INDEX); |
316 | 299 |
317 /* patch */ int get length => _hashTable._elementCount; | 300 /* patch */ int get length => _hashTable._elementCount; |
318 | 301 |
319 /* patch */ bool get isEmpty => _hashTable._elementCount == 0; | 302 /* patch */ bool get isEmpty => _hashTable._elementCount == 0; |
320 } | 303 } |
| 304 |
| 305 patch class LinkedHashSet<E> extends _HashSetBase<E> { |
| 306 static const int _INITIAL_CAPACITY = 8; |
| 307 _LinkedHashTable<E> _table; |
| 308 |
| 309 /* patch */ LinkedHashSet() { |
| 310 _table = new _LinkedHashTable(_INITIAL_CAPACITY); |
| 311 _table._container = this; |
| 312 } |
| 313 |
| 314 // Iterable. |
| 315 /* patch */ Iterator<E> get iterator { |
| 316 return new _LinkedHashTableKeyIterator<E>(_table); |
| 317 } |
| 318 |
| 319 /* patch */ int get length => _table._elementCount; |
| 320 |
| 321 /* patch */ bool get isEmpty => _table._elementCount == 0; |
| 322 |
| 323 /* patch */ bool contains(Object object) => _table._get(object) >= 0; |
| 324 |
| 325 /* patch */ void forEach(void action(E element)) { |
| 326 int offset = _table._next(_LinkedHashTable._HEAD_OFFSET); |
| 327 int modificationCount = _table._modificationCount; |
| 328 while (offset != _LinkedHashTable._HEAD_OFFSET) { |
| 329 E key = _table._key(offset); |
| 330 action(key); |
| 331 _table._checkModification(modificationCount); |
| 332 offset = _table._next(offset); |
| 333 } |
| 334 } |
| 335 |
| 336 /* patch */ E get first { |
| 337 int firstOffset = _table._next(_LinkedHashTable._HEAD_OFFSET); |
| 338 if (firstOffset == _LinkedHashTable._HEAD_OFFSET) { |
| 339 throw new StateError("No elements"); |
| 340 } |
| 341 return _table._key(firstOffset); |
| 342 } |
| 343 |
| 344 /* patch */ E get last { |
| 345 int lastOffset = _table._prev(_LinkedHashTable._HEAD_OFFSET); |
| 346 if (lastOffset == _LinkedHashTable._HEAD_OFFSET) { |
| 347 throw new StateError("No elements"); |
| 348 } |
| 349 return _table._key(lastOffset); |
| 350 } |
| 351 |
| 352 // Collection. |
| 353 void _filterWhere(bool test(E element), bool removeMatching) { |
| 354 int entrySize = _table._entrySize; |
| 355 int length = _table._table.length; |
| 356 int offset = _table._next(_LinkedHashTable._HEAD_OFFSET); |
| 357 while (offset != _LinkedHashTable._HEAD_OFFSET) { |
| 358 E key = _table._key(offset); |
| 359 int nextOffset = _table._next(offset); |
| 360 int modificationCount = _table._modificationCount; |
| 361 bool shouldRemove = (removeMatching == test(key)); |
| 362 _table._checkModification(modificationCount); |
| 363 if (shouldRemove) { |
| 364 _table._deleteEntry(offset); |
| 365 } |
| 366 offset = nextOffset; |
| 367 } |
| 368 _table._checkCapacity(); |
| 369 } |
| 370 |
| 371 /* patch */ void add(E element) { |
| 372 _table._put(element); |
| 373 _table._checkCapacity(); |
| 374 } |
| 375 |
| 376 /* patch */ void addAll(Iterable<E> objects) { |
| 377 for (E object in objects) { |
| 378 _table._put(object); |
| 379 _table._checkCapacity(); |
| 380 } |
| 381 } |
| 382 |
| 383 /* patch */ bool remove(Object object) { |
| 384 int offset = _table._remove(object); |
| 385 if (offset >= 0) { |
| 386 _table._checkCapacity(); |
| 387 return true; |
| 388 } |
| 389 return false; |
| 390 } |
| 391 |
| 392 /* patch */ void removeAll(Iterable objectsToRemove) { |
| 393 for (Object object in objectsToRemove) { |
| 394 if (_table._remove(object) >= 0) { |
| 395 _table._checkCapacity(); |
| 396 } |
| 397 } |
| 398 } |
| 399 |
| 400 /* patch */ void removeWhere(bool test(E element)) { |
| 401 _filterWhere(test, true); |
| 402 } |
| 403 |
| 404 /* patch */ void retainWhere(bool test(E element)) { |
| 405 _filterWhere(test, false); |
| 406 } |
| 407 |
| 408 /* patch */ void clear() { |
| 409 _table._clear(); |
| 410 } |
| 411 } |
| 412 |
| 413 class _DeadEntry { |
| 414 const _DeadEntry(); |
| 415 } |
| 416 |
| 417 class _NullKey { |
| 418 const _NullKey(); |
| 419 int get hashCode => null.hashCode; |
| 420 } |
| 421 |
| 422 const _TOMBSTONE = const _DeadEntry(); |
| 423 const _NULL = const _NullKey(); |
| 424 |
| 425 class _HashTable<K> { |
| 426 /** |
| 427 * Table of entries with [_entrySize] slots per entry. |
| 428 * |
| 429 * Capacity in entries must be factor of two. |
| 430 */ |
| 431 List _table; |
| 432 /** Current capacity. Always equal to [:_table.length ~/ _entrySize:]. */ |
| 433 int _capacity; |
| 434 /** Count of occupied entries, including deleted ones. */ |
| 435 int _entryCount = 0; |
| 436 /** Count of deleted entries. */ |
| 437 int _deletedCount = 0; |
| 438 /** Counter incremented when table is modified. */ |
| 439 int _modificationCount = 0; |
| 440 /** If set, used as the source object for [ConcurrentModificationError]s. */ |
| 441 Object _container; |
| 442 |
| 443 _HashTable(int initialCapacity) : _capacity = initialCapacity { |
| 444 _table = _createTable(initialCapacity); |
| 445 } |
| 446 |
| 447 /** Reads key from table. Converts _NULL marker to null. */ |
| 448 Object _key(offset) { |
| 449 assert(!_isFree(_table[offset])); |
| 450 Object key = _table[offset]; |
| 451 if (!identical(key, _NULL)) return key; |
| 452 return null; |
| 453 } |
| 454 |
| 455 /** Writes key to table. Converts null to _NULL marker. */ |
| 456 void _setKey(int offset, Object key) { |
| 457 if (key == null) key = _NULL; |
| 458 _table[offset] = key; |
| 459 } |
| 460 |
| 461 int get _elementCount => _entryCount - _deletedCount; |
| 462 |
| 463 /** Size of each entry. */ |
| 464 int get _entrySize => 1; |
| 465 |
| 466 void _checkModification(int expectedModificationCount) { |
| 467 if (_modificationCount != expectedModificationCount) { |
| 468 throw new ConcurrentModificationError(_container); |
| 469 } |
| 470 } |
| 471 |
| 472 void _recordModification() { |
| 473 // Value cycles after 2^30 modifications. If you keep hold of an |
| 474 // iterator for that long, you might miss a modification detection, |
| 475 // and iteration can go sour. Don't do that. |
| 476 _modificationCount = (_modificationCount + 1) & (0x3FFFFFFF); |
| 477 } |
| 478 |
| 479 /** |
| 480 * Create an empty table. |
| 481 */ |
| 482 List _createTable(int capacity) { |
| 483 List table = new List(capacity * _entrySize); |
| 484 return table; |
| 485 } |
| 486 |
| 487 /** First table probe. */ |
| 488 int _firstProbe(int hashCode, int capacity) { |
| 489 return hashCode & (capacity - 1); |
| 490 } |
| 491 |
| 492 /** Following table probes. */ |
| 493 int _nextProbe(int previousIndex, int probeCount, int capacity) { |
| 494 // When capacity is a power of 2, this probing algorithm (the triangular |
| 495 // number sequence modulo capacity) is guaranteed to hit all indices exactly |
| 496 // once before repeating. |
| 497 return (previousIndex + probeCount) & (capacity - 1); |
| 498 } |
| 499 |
| 500 /** Whether an object is a free-marker (either tombstone or free). */ |
| 501 bool _isFree(Object marker) => |
| 502 marker == null || identical(marker, _TOMBSTONE); |
| 503 |
| 504 /** |
| 505 * Look up the offset for an object in the table. |
| 506 * |
| 507 * Finds the offset of the object in the table, if it is there, |
| 508 * or the first free offset for its hashCode. |
| 509 */ |
| 510 int _probeForAdd(int hashCode, Object object) { |
| 511 int entrySize = _entrySize; |
| 512 int index = _firstProbe(hashCode, _capacity); |
| 513 int firstTombstone = -1; |
| 514 int probeCount = 0; |
| 515 while (true) { |
| 516 int offset = index * entrySize; |
| 517 Object entry = _table[offset]; |
| 518 if (identical(entry, _TOMBSTONE)) { |
| 519 if (firstTombstone < 0) firstTombstone = offset; |
| 520 } else if (entry == null) { |
| 521 if (firstTombstone < 0) return offset; |
| 522 return firstTombstone; |
| 523 } else if (identical(_NULL, entry) ? _equals(null, object) |
| 524 : _equals(entry, object)) { |
| 525 return offset; |
| 526 } |
| 527 // The _nextProbe is designed so that it hits |
| 528 // every index eventually. |
| 529 index = _nextProbe(index, ++probeCount, _capacity); |
| 530 } |
| 531 } |
| 532 |
| 533 /** |
| 534 * Look up the offset for an object in the table. |
| 535 * |
| 536 * If the object is in the table, its offset is returned. |
| 537 * |
| 538 * If the object is not in the table, Otherwise a negative value is returned. |
| 539 */ |
| 540 int _probeForLookup(int hashCode, Object object) { |
| 541 int entrySize = _entrySize; |
| 542 int index = _firstProbe(hashCode, _capacity); |
| 543 int probeCount = 0; |
| 544 while (true) { |
| 545 int offset = index * entrySize; |
| 546 Object entry = _table[offset]; |
| 547 if (entry == null) { |
| 548 return -1; |
| 549 } else if (!identical(_TOMBSTONE, entry)) { |
| 550 if (identical(_NULL, entry) ? _equals(null, object) |
| 551 : _equals(entry, object)) { |
| 552 return offset; |
| 553 } |
| 554 } |
| 555 // The _nextProbe is designed so that it hits |
| 556 // every index eventually. |
| 557 index = _nextProbe(index, ++probeCount, _capacity); |
| 558 } |
| 559 } |
| 560 |
| 561 // Override the following two to change equality/hashCode computations |
| 562 |
| 563 /** |
| 564 * Compare two object for equality. |
| 565 * |
| 566 * The first object is the one already in the table, |
| 567 * and the second is the one being searched for. |
| 568 */ |
| 569 bool _equals(Object element, Object other) { |
| 570 return element == other; |
| 571 } |
| 572 |
| 573 /** |
| 574 * Compute hash-code for an object. |
| 575 */ |
| 576 int _hashCodeOf(Object object) => object.hashCode; |
| 577 |
| 578 /** |
| 579 * Ensure that the table isn't too full for its own good. |
| 580 * |
| 581 * Call this after adding an element. |
| 582 */ |
| 583 int _checkCapacity() { |
| 584 // Compute everything in multiples of entrySize to avoid division. |
| 585 int freeCount = _capacity - _entryCount; |
| 586 if (freeCount * 4 < _capacity || |
| 587 freeCount < _deletedCount) { |
| 588 // Less than 25% free or more deleted entries than free entries. |
| 589 _grow(_entryCount - _deletedCount); |
| 590 } |
| 591 } |
| 592 |
| 593 void _grow(int contentCount) { |
| 594 int capacity = _capacity; |
| 595 // Don't grow to less than twice the needed capacity. |
| 596 int minCapacity = contentCount * 2; |
| 597 while (capacity < minCapacity) { |
| 598 capacity *= 2; |
| 599 } |
| 600 // Reset to another table and add all existing elements. |
| 601 List oldTable = _table; |
| 602 _table = _createTable(capacity); |
| 603 _capacity = capacity; |
| 604 _entryCount = 0; |
| 605 _deletedCount = 0; |
| 606 _addAllEntries(oldTable); |
| 607 _recordModification(); |
| 608 } |
| 609 |
| 610 /** |
| 611 * Copies all non-free entries from the old table to the new empty table. |
| 612 */ |
| 613 void _addAllEntries(List oldTable) { |
| 614 for (int i = 0; i < oldTable.length; i += _entrySize) { |
| 615 Object object = oldTable[i]; |
| 616 if (!_isFree(object)) { |
| 617 int toOffset = _put(object); |
| 618 _copyEntry(oldTable, i, toOffset); |
| 619 } |
| 620 } |
| 621 } |
| 622 |
| 623 /** |
| 624 * Copies everything but the key element from one entry to another. |
| 625 * |
| 626 * Called while growing the base array. |
| 627 * |
| 628 * Override this if any non-key fields need copying. |
| 629 */ |
| 630 void _copyEntry(List fromTable, int fromOffset, int toOffset) {} |
| 631 |
| 632 // The following three methods are for simple get/set/remove operations. |
| 633 // They only affect the key of an entry. The remaining fields must be |
| 634 // filled by the caller. |
| 635 |
| 636 /** |
| 637 * Returns the offset of a key in [_table], or negative if it's not there. |
| 638 */ |
| 639 int _get(K key) { |
| 640 return _probeForLookup(_hashCodeOf(key), key); |
| 641 } |
| 642 |
| 643 /** |
| 644 * Puts the key into the table and returns its offset into [_table]. |
| 645 * |
| 646 * If [_entrySize] is greater than 1, the caller should fill the |
| 647 * remaining fields. |
| 648 * |
| 649 * Remember to call [_checkCapacity] after using this method. |
| 650 */ |
| 651 int _put(K key) { |
| 652 int offset = _probeForAdd(_hashCodeOf(key), key); |
| 653 Object oldEntry = _table[offset]; |
| 654 if (oldEntry == null) { |
| 655 _entryCount++; |
| 656 } else if (identical(oldEntry, _TOMBSTONE)) { |
| 657 _deletedCount--; |
| 658 } else { |
| 659 return offset; |
| 660 } |
| 661 _setKey(offset, key); |
| 662 _recordModification(); |
| 663 return offset; |
| 664 } |
| 665 |
| 666 /** |
| 667 * Removes a key from the table and returns its offset into [_table]. |
| 668 * |
| 669 * Returns null if the key was not in the table. |
| 670 * If [_entrySize] is greater than 1, the caller should clean up the |
| 671 * remaining fields. |
| 672 */ |
| 673 int _remove(K key) { |
| 674 int offset = _probeForLookup(_hashCodeOf(key), key); |
| 675 if (offset >= 0) { |
| 676 _deleteEntry(offset); |
| 677 } |
| 678 return offset; |
| 679 } |
| 680 |
| 681 /** Clears the table completely, leaving it empty. */ |
| 682 void _clear() { |
| 683 if (_elementCount == 0) return; |
| 684 for (int i = 0; i < _table.length; i++) { |
| 685 _table[i] = null; |
| 686 } |
| 687 _entryCount = _deletedCount = 0; |
| 688 _recordModification(); |
| 689 } |
| 690 |
| 691 /** Clears an entry in the table. */ |
| 692 void _deleteEntry(int offset) { |
| 693 assert(!_isFree(_table[offset])); |
| 694 _setKey(offset, _TOMBSTONE); |
| 695 _deletedCount++; |
| 696 _recordModification(); |
| 697 } |
| 698 } |
| 699 |
| 700 /** |
| 701 * Generic iterable based on a [_HashTable]. |
| 702 */ |
| 703 abstract class _HashTableIterable<E> extends Iterable<E> { |
| 704 final _HashTable _hashTable; |
| 705 _HashTableIterable(this._hashTable); |
| 706 |
| 707 Iterator<E> get iterator; |
| 708 |
| 709 /** |
| 710 * Return the iterated value for a given entry. |
| 711 */ |
| 712 E _valueAt(int offset, Object key); |
| 713 |
| 714 int get length => _hashTable._elementCount; |
| 715 |
| 716 bool get isEmpty => _hashTable._elementCount == 0; |
| 717 |
| 718 void forEach(void action(E element)) { |
| 719 int entrySize = _hashTable._entrySize; |
| 720 List table = _hashTable._table; |
| 721 int modificationCount = _hashTable._modificationCount; |
| 722 for (int offset = 0; offset < table.length; offset += entrySize) { |
| 723 Object entry = table[offset]; |
| 724 if (!_hashTable._isFree(entry)) { |
| 725 E value = _valueAt(offset, entry); |
| 726 action(value); |
| 727 } |
| 728 _hashTable._checkModification(modificationCount); |
| 729 } |
| 730 } |
| 731 } |
| 732 |
| 733 abstract class _HashTableIterator<E> implements Iterator<E> { |
| 734 final _HashTable _hashTable; |
| 735 final int _modificationCount; |
| 736 /** Location right after last found element. */ |
| 737 int _offset = 0; |
| 738 E _current = null; |
| 739 |
| 740 _HashTableIterator(_HashTable hashTable) |
| 741 : _hashTable = hashTable, |
| 742 _modificationCount = hashTable._modificationCount; |
| 743 |
| 744 bool moveNext() { |
| 745 _hashTable._checkModification(_modificationCount); |
| 746 |
| 747 List table = _hashTable._table; |
| 748 int entrySize = _hashTable._entrySize; |
| 749 |
| 750 while (_offset < table.length) { |
| 751 int currentOffset = _offset; |
| 752 Object entry = table[currentOffset]; |
| 753 _offset = currentOffset + entrySize; |
| 754 if (!_hashTable._isFree(entry)) { |
| 755 _current = _valueAt(currentOffset, entry); |
| 756 return true; |
| 757 } |
| 758 } |
| 759 _current = null; |
| 760 return false; |
| 761 } |
| 762 |
| 763 E get current => _current; |
| 764 |
| 765 E _valueAt(int offset, Object key); |
| 766 } |
| 767 |
| 768 class _HashTableKeyIterable<K> extends _HashTableIterable<K> { |
| 769 _HashTableKeyIterable(_HashTable<K> hashTable) : super(hashTable); |
| 770 |
| 771 Iterator<K> get iterator => new _HashTableKeyIterator<K>(_hashTable); |
| 772 |
| 773 K _valueAt(int offset, Object key) { |
| 774 if (identical(key, _NULL)) return null; |
| 775 return key; |
| 776 } |
| 777 |
| 778 bool contains(Object value) => _hashTable._get(value) >= 0; |
| 779 } |
| 780 |
| 781 class _HashTableKeyIterator<K> extends _HashTableIterator<K> { |
| 782 _HashTableKeyIterator(_HashTable hashTable) : super(hashTable); |
| 783 |
| 784 K _valueAt(int offset, Object key) { |
| 785 if (identical(key, _NULL)) return null; |
| 786 return key; |
| 787 } |
| 788 } |
| 789 |
| 790 class _HashTableValueIterable<V> extends _HashTableIterable<V> { |
| 791 final int _entryIndex; |
| 792 |
| 793 _HashTableValueIterable(_HashTable hashTable, this._entryIndex) |
| 794 : super(hashTable); |
| 795 |
| 796 Iterator<V> get iterator { |
| 797 return new _HashTableValueIterator<V>(_hashTable, _entryIndex); |
| 798 } |
| 799 |
| 800 V _valueAt(int offset, Object key) => _hashTable._table[offset + _entryIndex]; |
| 801 } |
| 802 |
| 803 class _HashTableValueIterator<V> extends _HashTableIterator<V> { |
| 804 final int _entryIndex; |
| 805 |
| 806 _HashTableValueIterator(_HashTable hashTable, this._entryIndex) |
| 807 : super(hashTable); |
| 808 |
| 809 V _valueAt(int offset, Object key) => _hashTable._table[offset + _entryIndex]; |
| 810 } |
| 811 |
| 812 class _HashMapTable<K, V> extends _HashTable<K> { |
| 813 static const int _INITIAL_CAPACITY = 8; |
| 814 static const int _VALUE_INDEX = 1; |
| 815 |
| 816 _HashMapTable() : super(_INITIAL_CAPACITY); |
| 817 |
| 818 int get _entrySize => 2; |
| 819 |
| 820 V _value(int offset) => _table[offset + _VALUE_INDEX]; |
| 821 void _setValue(int offset, V value) { _table[offset + _VALUE_INDEX] = value; } |
| 822 |
| 823 _copyEntry(List fromTable, int fromOffset, int toOffset) { |
| 824 _table[toOffset + _VALUE_INDEX] = fromTable[fromOffset + _VALUE_INDEX]; |
| 825 } |
| 826 } |
| 827 |
| 828 /** Unique marker object for the head of a linked list of entries. */ |
| 829 class _LinkedHashTableHeadMarker { |
| 830 const _LinkedHashTableHeadMarker(); |
| 831 } |
| 832 |
| 833 const _LinkedHashTableHeadMarker _HEAD_MARKER = |
| 834 const _LinkedHashTableHeadMarker(); |
| 835 |
| 836 class _LinkedHashTable<K> extends _HashTable<K> { |
| 837 static const _NEXT_INDEX = 1; |
| 838 static const _PREV_INDEX = 2; |
| 839 static const _HEAD_OFFSET = 0; |
| 840 |
| 841 _LinkedHashTable(int initialCapacity) : super(initialCapacity); |
| 842 |
| 843 int get _entrySize => 3; |
| 844 |
| 845 List _createTable(int capacity) { |
| 846 List result = new List(capacity * _entrySize); |
| 847 result[_HEAD_OFFSET] = _HEAD_MARKER; |
| 848 result[_HEAD_OFFSET + _NEXT_INDEX] = _HEAD_OFFSET; |
| 849 result[_HEAD_OFFSET + _PREV_INDEX] = _HEAD_OFFSET; |
| 850 return result; |
| 851 } |
| 852 |
| 853 int _next(int offset) => _table[offset + _NEXT_INDEX]; |
| 854 void _setNext(int offset, int to) { _table[offset + _NEXT_INDEX] = to; } |
| 855 |
| 856 int _prev(int offset) => _table[offset + _PREV_INDEX]; |
| 857 void _setPrev(int offset, int to) { _table[offset + _PREV_INDEX] = to; } |
| 858 |
| 859 void _linkLast(int offset) { |
| 860 // Add entry at offset at end of double-linked list. |
| 861 int last = _prev(_HEAD_OFFSET); |
| 862 _setNext(offset, _HEAD_OFFSET); |
| 863 _setPrev(offset, last); |
| 864 _setNext(last, offset); |
| 865 _setPrev(_HEAD_OFFSET, offset); |
| 866 } |
| 867 |
| 868 void _unlink(int offset) { |
| 869 assert(offset != _HEAD_OFFSET); |
| 870 int next = _next(offset); |
| 871 int prev = _prev(offset); |
| 872 _setNext(offset, null); |
| 873 _setPrev(offset, null); |
| 874 _setNext(prev, next); |
| 875 _setPrev(next, prev); |
| 876 } |
| 877 |
| 878 /** |
| 879 * Copies all non-free entries from the old table to the new empty table. |
| 880 */ |
| 881 void _addAllEntries(List oldTable) { |
| 882 int offset = oldTable[_HEAD_OFFSET + _NEXT_INDEX]; |
| 883 while (offset != _HEAD_OFFSET) { |
| 884 Object object = oldTable[offset]; |
| 885 int nextOffset = oldTable[offset + _NEXT_INDEX]; |
| 886 int toOffset = _put(object); |
| 887 _copyEntry(oldTable, offset, toOffset); |
| 888 offset = nextOffset; |
| 889 } |
| 890 } |
| 891 |
| 892 void _clear() { |
| 893 if (_elementCount == 0) return; |
| 894 _setNext(_HEAD_OFFSET, _HEAD_OFFSET); |
| 895 _setPrev(_HEAD_OFFSET, _HEAD_OFFSET); |
| 896 for (int i = _entrySize; i < _table.length; i++) { |
| 897 _table[i] = null; |
| 898 } |
| 899 _entryCount = _deletedCount = 0; |
| 900 _recordModification(); |
| 901 } |
| 902 |
| 903 int _put(K key) { |
| 904 int offset = _probeForAdd(_hashCodeOf(key), key); |
| 905 Object oldEntry = _table[offset]; |
| 906 if (identical(oldEntry, _TOMBSTONE)) { |
| 907 _deletedCount--; |
| 908 } else if (oldEntry == null) { |
| 909 _entryCount++; |
| 910 } else { |
| 911 return offset; |
| 912 } |
| 913 _recordModification(); |
| 914 _setKey(offset, key); |
| 915 _linkLast(offset); |
| 916 return offset; |
| 917 } |
| 918 |
| 919 void _deleteEntry(int offset) { |
| 920 _unlink(offset); |
| 921 _setKey(offset, _TOMBSTONE); |
| 922 _deletedCount++; |
| 923 _recordModification(); |
| 924 } |
| 925 } |
| 926 |
| 927 class _LinkedHashTableKeyIterable<K> extends Iterable<K> { |
| 928 final _LinkedHashTable<K> _table; |
| 929 _LinkedHashTableKeyIterable(this._table); |
| 930 Iterator<K> get iterator => new _LinkedHashTableKeyIterator<K>(_table); |
| 931 |
| 932 bool contains(Object value) => _table._get(value) >= 0; |
| 933 |
| 934 int get length => _table._elementCount; |
| 935 } |
| 936 |
| 937 class _LinkedHashTableKeyIterator<K> extends _LinkedHashTableIterator<K> { |
| 938 _LinkedHashTableKeyIterator(_LinkedHashTable<K> hashTable): super(hashTable); |
| 939 |
| 940 K _getCurrent(int offset) => _hashTable._key(offset); |
| 941 } |
| 942 |
| 943 class _LinkedHashTableValueIterable<V> extends Iterable<V> { |
| 944 final _LinkedHashTable _hashTable; |
| 945 final int _valueIndex; |
| 946 _LinkedHashTableValueIterable(this._hashTable, this._valueIndex); |
| 947 Iterator<V> get iterator => |
| 948 new _LinkedHashTableValueIterator<V>(_hashTable, _valueIndex); |
| 949 int get length => _hashTable._elementCount; |
| 950 } |
| 951 |
| 952 class _LinkedHashTableValueIterator<V> extends _LinkedHashTableIterator<V> { |
| 953 final int _valueIndex; |
| 954 |
| 955 _LinkedHashTableValueIterator(_LinkedHashTable hashTable, this._valueIndex) |
| 956 : super(hashTable); |
| 957 |
| 958 V _getCurrent(int offset) => _hashTable._table[offset + _valueIndex]; |
| 959 } |
| 960 |
| 961 abstract class _LinkedHashTableIterator<T> implements Iterator<T> { |
| 962 final _LinkedHashTable _hashTable; |
| 963 final int _modificationCount; |
| 964 int _offset; |
| 965 T _current; |
| 966 |
| 967 _LinkedHashTableIterator(_LinkedHashTable table) |
| 968 : _hashTable = table, |
| 969 _modificationCount = table._modificationCount, |
| 970 _offset = table._next(_LinkedHashTable._HEAD_OFFSET); |
| 971 |
| 972 bool moveNext() { |
| 973 _hashTable._checkModification(_modificationCount); |
| 974 if (_offset == _LinkedHashTable._HEAD_OFFSET) { |
| 975 _current = null; |
| 976 return false; |
| 977 } |
| 978 _current = _getCurrent(_offset); |
| 979 _offset = _hashTable._next(_offset); |
| 980 return true; |
| 981 } |
| 982 |
| 983 T _getCurrent(int offset); |
| 984 |
| 985 T get current => _current; |
| 986 } |
| 987 |
| 988 class _LinkedHashMapTable<K, V> extends _LinkedHashTable<K> { |
| 989 static const int _INITIAL_CAPACITY = 8; |
| 990 static const int _VALUE_INDEX = 3; |
| 991 |
| 992 int get _entrySize => 4; |
| 993 |
| 994 _LinkedHashMapTable() : super(_INITIAL_CAPACITY); |
| 995 |
| 996 V _value(int offset) => _table[offset + _VALUE_INDEX]; |
| 997 void _setValue(int offset, V value) { _table[offset + _VALUE_INDEX] = value; } |
| 998 |
| 999 _copyEntry(List oldTable, int fromOffset, int toOffset) { |
| 1000 _table[toOffset + _VALUE_INDEX] = oldTable[fromOffset + _VALUE_INDEX]; |
| 1001 } |
| 1002 } |
OLD | NEW |