Files
sdk/runtime/lib/collection_patch.dart
T
regis@google.com 9eb12ad303 Implement updated method overriding rules in the vm (issue 11495).
Introduce --error-on-bad-override flag and use it when generating snapshots.
Fix signature checking when patching methods.
Fix errors in patch files.
Fix receiver type of constructors.
Fix finalization of mixin application typedefs.
Update tests and status files.

R=asiva@google.com

Review URL: https://codereview.chromium.org//22685007

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@26027 260f80e4-7a28-3924-810f-c04153c831b5
2013-08-12 20:28:52 +00:00

1011 lines
29 KiB
Dart

// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
patch class HashMap<K, V> {
final _HashMapTable<K, V> _hashTable = new _HashMapTable<K, V>();
/* patch */ HashMap() {
_hashTable._container = this;
}
/* patch */ bool containsKey(Object key) {
return _hashTable._get(key) >= 0;
}
/* patch */ bool containsValue(Object value) {
List table = _hashTable._table;
int entrySize = _hashTable._entrySize;
for (int offset = 0; offset < table.length; offset += entrySize) {
if (!_hashTable._isFree(table[offset]) &&
_hashTable._value(offset) == value) {
return true;
}
}
return false;
}
/* patch */ void addAll(Map<K, V> other) {
other.forEach((K key, V value) {
int offset = _hashTable._put(key);
_hashTable._setValue(offset, value);
_hashTable._checkCapacity();
});
}
/* patch */ V operator [](Object key) {
int offset = _hashTable._get(key);
if (offset >= 0) return _hashTable._value(offset);
return null;
}
/* patch */ void operator []=(K key, V value) {
int offset = _hashTable._put(key);
_hashTable._setValue(offset, value);
_hashTable._checkCapacity();
}
/* patch */ V putIfAbsent(K key, V ifAbsent()) {
int offset = _hashTable._probeForAdd(_hashTable._hashCodeOf(key), key);
Object entry = _hashTable._table[offset];
if (!_hashTable._isFree(entry)) {
return _hashTable._value(offset);
}
int modificationCount = _hashTable._modificationCount;
V value = ifAbsent();
if (modificationCount == _hashTable._modificationCount) {
_hashTable._setKey(offset, key);
_hashTable._setValue(offset, value);
if (entry == null) {
_hashTable._entryCount++;
_hashTable._checkCapacity();
} else {
assert(identical(entry, _TOMBSTONE));
_hashTable._deletedCount--;
}
_hashTable._recordModification();
} else {
// The table might have changed, so we can't trust [offset] any more.
// Do another lookup before setting the value.
offset = _hashTable._put(key);
_hashTable._setValue(offset, value);
_hashTable._checkCapacity();
}
return value;
}
/* patch */ V remove(Object key) {
int offset = _hashTable._remove(key);
if (offset < 0) return null;
V oldValue = _hashTable._value(offset);
_hashTable._setValue(offset, null);
_hashTable._checkCapacity();
return oldValue;
}
/* patch */ void clear() {
_hashTable._clear();
}
/* patch */ void forEach(void action(K key, V value)) {
int modificationCount = _hashTable._modificationCount;
List table = _hashTable._table;
int entrySize = _hashTable._entrySize;
for (int offset = 0; offset < table.length; offset += entrySize) {
Object entry = table[offset];
if (!_hashTable._isFree(entry)) {
K key = identical(entry, _NULL) ? null : entry;
V value = _hashTable._value(offset);
action(key, value);
_hashTable._checkModification(modificationCount);
}
}
}
/* patch */ Iterable<K> get keys => new _HashTableKeyIterable<K>(_hashTable);
/* patch */ Iterable<V> get values =>
new _HashTableValueIterable<V>(_hashTable, _HashMapTable._VALUE_INDEX);
/* patch */ int get length => _hashTable._elementCount;
/* patch */ bool get isEmpty => _hashTable._elementCount == 0;
/* patch */ bool get isNotEmpty => !isEmpty;
}
patch class HashSet<E> {
static const int _INITIAL_CAPACITY = 8;
final _HashTable<E> _table;
/* patch */ HashSet() : _table = new _HashTable(_INITIAL_CAPACITY) {
_table._container = this;
}
factory HashSet.from(Iterable<E> iterable) {
return new HashSet<E>()..addAll(iterable);
}
// Iterable.
/* patch */ Iterator<E> get iterator => new _HashTableKeyIterator<E>(_table);
/* patch */ int get length => _table._elementCount;
/* patch */ bool get isEmpty => _table._elementCount == 0;
/* patch */ bool get isNotEmpty => !isEmpty;
/* patch */ bool contains(Object object) => _table._get(object) >= 0;
// Collection.
/* patch */ void add(E element) {
_table._put(element);
_table._checkCapacity();
}
/* patch */ void addAll(Iterable<E> objects) {
for (E object in objects) {
_table._put(object);
_table._checkCapacity();
}
}
/* patch */ bool remove(Object object) {
int offset = _table._remove(object);
_table._checkCapacity();
return offset >= 0;
}
/* patch */ void removeAll(Iterable<Object> objectsToRemove) {
for (Object object in objectsToRemove) {
_table._remove(object);
_table._checkCapacity();
}
}
void _filterWhere(bool test(E element), bool removeMatching) {
int entrySize = _table._entrySize;
int length = _table._table.length;
for (int offset = 0; offset < length; offset += entrySize) {
Object entry = _table._table[offset];
if (!_table._isFree(entry)) {
E key = identical(entry, _NULL) ? null : entry;
int modificationCount = _table._modificationCount;
bool shouldRemove = (removeMatching == test(key));
_table._checkModification(modificationCount);
if (shouldRemove) {
_table._deleteEntry(offset);
}
}
}
_table._checkCapacity();
}
/* patch */ void removeWhere(bool test(E element)) {
_filterWhere(test, true);
}
/* patch */ void retainWhere(bool test(E element)) {
_filterWhere(test, false);
}
/* patch */ void clear() {
_table._clear();
}
}
/**
* A hash-based map that iterates keys and values in key insertion order.
*/
patch class LinkedHashMap<K, V> {
final _LinkedHashMapTable _hashTable;
/* patch */ LinkedHashMap() : _hashTable = new _LinkedHashMapTable<K, V>() {
_hashTable._container = this;
}
/* patch */ bool containsKey(Object key) {
return _hashTable._get(key) >= 0;
}
/* patch */ bool containsValue(Object value) {
int modificationCount = _hashTable._modificationCount;
for (int offset = _hashTable._next(_LinkedHashTable._HEAD_OFFSET);
offset != _LinkedHashTable._HEAD_OFFSET;
offset = _hashTable._next(offset)) {
if (_hashTable._value(offset) == value) {
return true;
}
// The == call may modify the table.
_hashTable._checkModification(modificationCount);
}
return false;
}
/* patch */ void addAll(Map<K, V> other) {
other.forEach((K key, V value) {
int offset = _hashTable._put(key);
_hashTable._setValue(offset, value);
_hashTable._checkCapacity();
});
}
/* patch */ V operator [](Object key) {
int offset = _hashTable._get(key);
if (offset >= 0) return _hashTable._value(offset);
return null;
}
/* patch */ void operator []=(K key, V value) {
int offset = _hashTable._put(key);
_hashTable._setValue(offset, value);
_hashTable._checkCapacity();
}
/* patch */ V putIfAbsent(K key, V ifAbsent()) {
int offset = _hashTable._probeForAdd(_hashTable._hashCodeOf(key), key);
Object entry = _hashTable._table[offset];
if (!_hashTable._isFree(entry)) {
return _hashTable._value(offset);
}
int modificationCount = _hashTable._modificationCount;
V value = ifAbsent();
if (modificationCount == _hashTable._modificationCount) {
_hashTable._setKey(offset, key);
_hashTable._setValue(offset, value);
_hashTable._linkLast(offset);
if (entry == null) {
_hashTable._entryCount++;
_hashTable._checkCapacity();
} else {
assert(identical(entry, _TOMBSTONE));
_hashTable._deletedCount--;
}
_hashTable._recordModification();
} else {
// The table might have changed, so we can't trust [offset] any more.
// Do another lookup before setting the value.
offset = _hashTable._put(key);
_hashTable._setValue(offset, value);
_hashTable._checkCapacity();
}
return value;
}
/* patch */ V remove(Object key) {
int offset = _hashTable._remove(key);
if (offset < 0) return null;
Object oldValue = _hashTable._value(offset);
_hashTable._setValue(offset, null);
_hashTable._checkCapacity();
return oldValue;
}
/* patch */ void clear() {
_hashTable._clear();
}
/* patch */ void forEach(void action (K key, V value)) {
int modificationCount = _hashTable._modificationCount;
for (int offset = _hashTable._next(_LinkedHashTable._HEAD_OFFSET);
offset != _LinkedHashTable._HEAD_OFFSET;
offset = _hashTable._next(offset)) {
action(_hashTable._key(offset), _hashTable._value(offset));
_hashTable._checkModification(modificationCount);
}
}
/* patch */ Iterable<K> get keys =>
new _LinkedHashTableKeyIterable<K>(_hashTable);
/* patch */ Iterable<V> get values =>
new _LinkedHashTableValueIterable<V>(_hashTable,
_LinkedHashMapTable._VALUE_INDEX);
/* patch */ int get length => _hashTable._elementCount;
/* patch */ bool get isEmpty => _hashTable._elementCount == 0;
/* patch */ bool get isNotEmpty => !isEmpty;
}
patch class LinkedHashSet<E> extends _HashSetBase<E> {
static const int _INITIAL_CAPACITY = 8;
_LinkedHashTable<E> _table;
/* patch */ LinkedHashSet() {
_table = new _LinkedHashTable(_INITIAL_CAPACITY);
_table._container = this;
}
// Iterable.
/* patch */ Iterator<E> get iterator {
return new _LinkedHashTableKeyIterator<E>(_table);
}
/* patch */ int get length => _table._elementCount;
/* patch */ bool get isEmpty => _table._elementCount == 0;
/* patch */ bool get isNotEmpty => !isEmpty;
/* patch */ bool contains(Object object) => _table._get(object) >= 0;
/* patch */ void forEach(void action(E element)) {
int offset = _table._next(_LinkedHashTable._HEAD_OFFSET);
int modificationCount = _table._modificationCount;
while (offset != _LinkedHashTable._HEAD_OFFSET) {
E key = _table._key(offset);
action(key);
_table._checkModification(modificationCount);
offset = _table._next(offset);
}
}
/* patch */ E get first {
int firstOffset = _table._next(_LinkedHashTable._HEAD_OFFSET);
if (firstOffset == _LinkedHashTable._HEAD_OFFSET) {
throw new StateError("No elements");
}
return _table._key(firstOffset);
}
/* patch */ E get last {
int lastOffset = _table._prev(_LinkedHashTable._HEAD_OFFSET);
if (lastOffset == _LinkedHashTable._HEAD_OFFSET) {
throw new StateError("No elements");
}
return _table._key(lastOffset);
}
// Collection.
void _filterWhere(bool test(E element), bool removeMatching) {
int entrySize = _table._entrySize;
int length = _table._table.length;
int offset = _table._next(_LinkedHashTable._HEAD_OFFSET);
while (offset != _LinkedHashTable._HEAD_OFFSET) {
E key = _table._key(offset);
int nextOffset = _table._next(offset);
int modificationCount = _table._modificationCount;
bool shouldRemove = (removeMatching == test(key));
_table._checkModification(modificationCount);
if (shouldRemove) {
_table._deleteEntry(offset);
}
offset = nextOffset;
}
_table._checkCapacity();
}
/* patch */ void add(E element) {
_table._put(element);
_table._checkCapacity();
}
/* patch */ void addAll(Iterable<E> objects) {
for (E object in objects) {
_table._put(object);
_table._checkCapacity();
}
}
/* patch */ bool remove(Object object) {
int offset = _table._remove(object);
if (offset >= 0) {
_table._checkCapacity();
return true;
}
return false;
}
/* patch */ void removeAll(Iterable objectsToRemove) {
for (Object object in objectsToRemove) {
if (_table._remove(object) >= 0) {
_table._checkCapacity();
}
}
}
/* patch */ void removeWhere(bool test(E element)) {
_filterWhere(test, true);
}
/* patch */ void retainWhere(bool test(E element)) {
_filterWhere(test, false);
}
/* patch */ void clear() {
_table._clear();
}
}
class _DeadEntry {
const _DeadEntry();
}
class _NullKey {
const _NullKey();
int get hashCode => null.hashCode;
}
const _TOMBSTONE = const _DeadEntry();
const _NULL = const _NullKey();
class _HashTable<K> {
/**
* Table of entries with [_entrySize] slots per entry.
*
* Capacity in entries must be factor of two.
*/
List _table;
/** Current capacity. Always equal to [:_table.length ~/ _entrySize:]. */
int _capacity;
/** Count of occupied entries, including deleted ones. */
int _entryCount = 0;
/** Count of deleted entries. */
int _deletedCount = 0;
/** Counter incremented when table is modified. */
int _modificationCount = 0;
/** If set, used as the source object for [ConcurrentModificationError]s. */
Object _container;
_HashTable(int initialCapacity) : _capacity = initialCapacity {
_table = _createTable(initialCapacity);
}
/** Reads key from table. Converts _NULL marker to null. */
Object _key(offset) {
assert(!_isFree(_table[offset]));
Object key = _table[offset];
if (!identical(key, _NULL)) return key;
return null;
}
/** Writes key to table. Converts null to _NULL marker. */
void _setKey(int offset, Object key) {
if (key == null) key = _NULL;
_table[offset] = key;
}
int get _elementCount => _entryCount - _deletedCount;
/** Size of each entry. */
int get _entrySize => 1;
void _checkModification(int expectedModificationCount) {
if (_modificationCount != expectedModificationCount) {
throw new ConcurrentModificationError(_container);
}
}
void _recordModification() {
// Value cycles after 2^30 modifications. If you keep hold of an
// iterator for that long, you might miss a modification detection,
// and iteration can go sour. Don't do that.
_modificationCount = (_modificationCount + 1) & (0x3FFFFFFF);
}
/**
* Create an empty table.
*/
List _createTable(int capacity) {
List table = new List(capacity * _entrySize);
return table;
}
/** First table probe. */
int _firstProbe(int hashCode, int capacity) {
return hashCode & (capacity - 1);
}
/** Following table probes. */
int _nextProbe(int previousIndex, int probeCount, int capacity) {
// When capacity is a power of 2, this probing algorithm (the triangular
// number sequence modulo capacity) is guaranteed to hit all indices exactly
// once before repeating.
return (previousIndex + probeCount) & (capacity - 1);
}
/** Whether an object is a free-marker (either tombstone or free). */
bool _isFree(Object marker) =>
marker == null || identical(marker, _TOMBSTONE);
/**
* Look up the offset for an object in the table.
*
* Finds the offset of the object in the table, if it is there,
* or the first free offset for its hashCode.
*/
int _probeForAdd(int hashCode, Object object) {
int entrySize = _entrySize;
int index = _firstProbe(hashCode, _capacity);
int firstTombstone = -1;
int probeCount = 0;
while (true) {
int offset = index * entrySize;
Object entry = _table[offset];
if (identical(entry, _TOMBSTONE)) {
if (firstTombstone < 0) firstTombstone = offset;
} else if (entry == null) {
if (firstTombstone < 0) return offset;
return firstTombstone;
} else if (identical(_NULL, entry) ? _equals(null, object)
: _equals(entry, object)) {
return offset;
}
// The _nextProbe is designed so that it hits
// every index eventually.
index = _nextProbe(index, ++probeCount, _capacity);
}
}
/**
* Look up the offset for an object in the table.
*
* If the object is in the table, its offset is returned.
*
* If the object is not in the table, Otherwise a negative value is returned.
*/
int _probeForLookup(int hashCode, Object object) {
int entrySize = _entrySize;
int index = _firstProbe(hashCode, _capacity);
int probeCount = 0;
while (true) {
int offset = index * entrySize;
Object entry = _table[offset];
if (entry == null) {
return -1;
} else if (!identical(_TOMBSTONE, entry)) {
if (identical(_NULL, entry) ? _equals(null, object)
: _equals(entry, object)) {
return offset;
}
}
// The _nextProbe is designed so that it hits
// every index eventually.
index = _nextProbe(index, ++probeCount, _capacity);
}
}
// Override the following two to change equality/hashCode computations
/**
* Compare two object for equality.
*
* The first object is the one already in the table,
* and the second is the one being searched for.
*/
bool _equals(Object element, Object other) {
return element == other;
}
/**
* Compute hash-code for an object.
*/
int _hashCodeOf(Object object) => object.hashCode;
/**
* Ensure that the table isn't too full for its own good.
*
* Call this after adding an element.
*/
int _checkCapacity() {
// Compute everything in multiples of entrySize to avoid division.
int freeCount = _capacity - _entryCount;
if (freeCount * 4 < _capacity ||
freeCount < _deletedCount) {
// Less than 25% free or more deleted entries than free entries.
_grow(_entryCount - _deletedCount);
}
}
void _grow(int contentCount) {
int capacity = _capacity;
// Don't grow to less than twice the needed capacity.
int minCapacity = contentCount * 2;
while (capacity < minCapacity) {
capacity *= 2;
}
// Reset to another table and add all existing elements.
List oldTable = _table;
_table = _createTable(capacity);
_capacity = capacity;
_entryCount = 0;
_deletedCount = 0;
_addAllEntries(oldTable);
_recordModification();
}
/**
* Copies all non-free entries from the old table to the new empty table.
*/
void _addAllEntries(List oldTable) {
for (int i = 0; i < oldTable.length; i += _entrySize) {
Object object = oldTable[i];
if (!_isFree(object)) {
int toOffset = _put(object);
_copyEntry(oldTable, i, toOffset);
}
}
}
/**
* Copies everything but the key element from one entry to another.
*
* Called while growing the base array.
*
* Override this if any non-key fields need copying.
*/
void _copyEntry(List fromTable, int fromOffset, int toOffset) {}
// The following three methods are for simple get/set/remove operations.
// They only affect the key of an entry. The remaining fields must be
// filled by the caller.
/**
* Returns the offset of a key in [_table], or negative if it's not there.
*/
int _get(Object key) {
return _probeForLookup(_hashCodeOf(key), key);
}
/**
* Puts the key into the table and returns its offset into [_table].
*
* If [_entrySize] is greater than 1, the caller should fill the
* remaining fields.
*
* Remember to call [_checkCapacity] after using this method.
*/
int _put(K key) {
int offset = _probeForAdd(_hashCodeOf(key), key);
Object oldEntry = _table[offset];
if (oldEntry == null) {
_entryCount++;
} else if (identical(oldEntry, _TOMBSTONE)) {
_deletedCount--;
} else {
return offset;
}
_setKey(offset, key);
_recordModification();
return offset;
}
/**
* Removes a key from the table and returns its offset into [_table].
*
* Returns null if the key was not in the table.
* If [_entrySize] is greater than 1, the caller should clean up the
* remaining fields.
*/
int _remove(Object key) {
int offset = _probeForLookup(_hashCodeOf(key), key);
if (offset >= 0) {
_deleteEntry(offset);
}
return offset;
}
/** Clears the table completely, leaving it empty. */
void _clear() {
if (_elementCount == 0) return;
for (int i = 0; i < _table.length; i++) {
_table[i] = null;
}
_entryCount = _deletedCount = 0;
_recordModification();
}
/** Clears an entry in the table. */
void _deleteEntry(int offset) {
assert(!_isFree(_table[offset]));
_setKey(offset, _TOMBSTONE);
_deletedCount++;
_recordModification();
}
}
/**
* Generic iterable based on a [_HashTable].
*/
abstract class _HashTableIterable<E> extends IterableBase<E> {
final _HashTable _hashTable;
_HashTableIterable(this._hashTable);
Iterator<E> get iterator;
/**
* Return the iterated value for a given entry.
*/
E _valueAt(int offset, Object key);
int get length => _hashTable._elementCount;
bool get isEmpty => _hashTable._elementCount == 0;
void forEach(void action(E element)) {
int entrySize = _hashTable._entrySize;
List table = _hashTable._table;
int modificationCount = _hashTable._modificationCount;
for (int offset = 0; offset < table.length; offset += entrySize) {
Object entry = table[offset];
if (!_hashTable._isFree(entry)) {
E value = _valueAt(offset, entry);
action(value);
}
_hashTable._checkModification(modificationCount);
}
}
}
abstract class _HashTableIterator<E> implements Iterator<E> {
final _HashTable _hashTable;
final int _modificationCount;
/** Location right after last found element. */
int _offset = 0;
E _current = null;
_HashTableIterator(_HashTable hashTable)
: _hashTable = hashTable,
_modificationCount = hashTable._modificationCount;
bool moveNext() {
_hashTable._checkModification(_modificationCount);
List table = _hashTable._table;
int entrySize = _hashTable._entrySize;
while (_offset < table.length) {
int currentOffset = _offset;
Object entry = table[currentOffset];
_offset = currentOffset + entrySize;
if (!_hashTable._isFree(entry)) {
_current = _valueAt(currentOffset, entry);
return true;
}
}
_current = null;
return false;
}
E get current => _current;
E _valueAt(int offset, Object key);
}
class _HashTableKeyIterable<K> extends _HashTableIterable<K> {
_HashTableKeyIterable(_HashTable<K> hashTable) : super(hashTable);
Iterator<K> get iterator => new _HashTableKeyIterator<K>(_hashTable);
K _valueAt(int offset, Object key) {
if (identical(key, _NULL)) return null;
return key;
}
bool contains(Object value) => _hashTable._get(value) >= 0;
}
class _HashTableKeyIterator<K> extends _HashTableIterator<K> {
_HashTableKeyIterator(_HashTable hashTable) : super(hashTable);
K _valueAt(int offset, Object key) {
if (identical(key, _NULL)) return null;
return key;
}
}
class _HashTableValueIterable<V> extends _HashTableIterable<V> {
final int _entryIndex;
_HashTableValueIterable(_HashTable hashTable, this._entryIndex)
: super(hashTable);
Iterator<V> get iterator {
return new _HashTableValueIterator<V>(_hashTable, _entryIndex);
}
V _valueAt(int offset, Object key) => _hashTable._table[offset + _entryIndex];
}
class _HashTableValueIterator<V> extends _HashTableIterator<V> {
final int _entryIndex;
_HashTableValueIterator(_HashTable hashTable, this._entryIndex)
: super(hashTable);
V _valueAt(int offset, Object key) => _hashTable._table[offset + _entryIndex];
}
class _HashMapTable<K, V> extends _HashTable<K> {
static const int _INITIAL_CAPACITY = 8;
static const int _VALUE_INDEX = 1;
_HashMapTable() : super(_INITIAL_CAPACITY);
int get _entrySize => 2;
V _value(int offset) => _table[offset + _VALUE_INDEX];
void _setValue(int offset, V value) { _table[offset + _VALUE_INDEX] = value; }
_copyEntry(List fromTable, int fromOffset, int toOffset) {
_table[toOffset + _VALUE_INDEX] = fromTable[fromOffset + _VALUE_INDEX];
}
}
/** Unique marker object for the head of a linked list of entries. */
class _LinkedHashTableHeadMarker {
const _LinkedHashTableHeadMarker();
}
const _LinkedHashTableHeadMarker _HEAD_MARKER =
const _LinkedHashTableHeadMarker();
class _LinkedHashTable<K> extends _HashTable<K> {
static const _NEXT_INDEX = 1;
static const _PREV_INDEX = 2;
static const _HEAD_OFFSET = 0;
_LinkedHashTable(int initialCapacity) : super(initialCapacity);
int get _entrySize => 3;
List _createTable(int capacity) {
List result = new List(capacity * _entrySize);
result[_HEAD_OFFSET] = _HEAD_MARKER;
result[_HEAD_OFFSET + _NEXT_INDEX] = _HEAD_OFFSET;
result[_HEAD_OFFSET + _PREV_INDEX] = _HEAD_OFFSET;
return result;
}
int _next(int offset) => _table[offset + _NEXT_INDEX];
void _setNext(int offset, int to) { _table[offset + _NEXT_INDEX] = to; }
int _prev(int offset) => _table[offset + _PREV_INDEX];
void _setPrev(int offset, int to) { _table[offset + _PREV_INDEX] = to; }
void _linkLast(int offset) {
// Add entry at offset at end of double-linked list.
int last = _prev(_HEAD_OFFSET);
_setNext(offset, _HEAD_OFFSET);
_setPrev(offset, last);
_setNext(last, offset);
_setPrev(_HEAD_OFFSET, offset);
}
void _unlink(int offset) {
assert(offset != _HEAD_OFFSET);
int next = _next(offset);
int prev = _prev(offset);
_setNext(offset, null);
_setPrev(offset, null);
_setNext(prev, next);
_setPrev(next, prev);
}
/**
* Copies all non-free entries from the old table to the new empty table.
*/
void _addAllEntries(List oldTable) {
int offset = oldTable[_HEAD_OFFSET + _NEXT_INDEX];
while (offset != _HEAD_OFFSET) {
Object object = oldTable[offset];
int nextOffset = oldTable[offset + _NEXT_INDEX];
int toOffset = _put(object);
_copyEntry(oldTable, offset, toOffset);
offset = nextOffset;
}
}
void _clear() {
if (_elementCount == 0) return;
_setNext(_HEAD_OFFSET, _HEAD_OFFSET);
_setPrev(_HEAD_OFFSET, _HEAD_OFFSET);
for (int i = _entrySize; i < _table.length; i++) {
_table[i] = null;
}
_entryCount = _deletedCount = 0;
_recordModification();
}
int _put(K key) {
int offset = _probeForAdd(_hashCodeOf(key), key);
Object oldEntry = _table[offset];
if (identical(oldEntry, _TOMBSTONE)) {
_deletedCount--;
} else if (oldEntry == null) {
_entryCount++;
} else {
return offset;
}
_recordModification();
_setKey(offset, key);
_linkLast(offset);
return offset;
}
void _deleteEntry(int offset) {
_unlink(offset);
_setKey(offset, _TOMBSTONE);
_deletedCount++;
_recordModification();
}
}
class _LinkedHashTableKeyIterable<K> extends IterableBase<K> {
final _LinkedHashTable<K> _table;
_LinkedHashTableKeyIterable(this._table);
Iterator<K> get iterator => new _LinkedHashTableKeyIterator<K>(_table);
bool contains(Object value) => _table._get(value) >= 0;
int get length => _table._elementCount;
}
class _LinkedHashTableKeyIterator<K> extends _LinkedHashTableIterator<K> {
_LinkedHashTableKeyIterator(_LinkedHashTable<K> hashTable): super(hashTable);
K _getCurrent(int offset) => _hashTable._key(offset);
}
class _LinkedHashTableValueIterable<V> extends IterableBase<V> {
final _LinkedHashTable _hashTable;
final int _valueIndex;
_LinkedHashTableValueIterable(this._hashTable, this._valueIndex);
Iterator<V> get iterator =>
new _LinkedHashTableValueIterator<V>(_hashTable, _valueIndex);
int get length => _hashTable._elementCount;
}
class _LinkedHashTableValueIterator<V> extends _LinkedHashTableIterator<V> {
final int _valueIndex;
_LinkedHashTableValueIterator(_LinkedHashTable hashTable, this._valueIndex)
: super(hashTable);
V _getCurrent(int offset) => _hashTable._table[offset + _valueIndex];
}
abstract class _LinkedHashTableIterator<T> implements Iterator<T> {
final _LinkedHashTable _hashTable;
final int _modificationCount;
int _offset;
T _current;
_LinkedHashTableIterator(_LinkedHashTable table)
: _hashTable = table,
_modificationCount = table._modificationCount,
_offset = table._next(_LinkedHashTable._HEAD_OFFSET);
bool moveNext() {
_hashTable._checkModification(_modificationCount);
if (_offset == _LinkedHashTable._HEAD_OFFSET) {
_current = null;
return false;
}
_current = _getCurrent(_offset);
_offset = _hashTable._next(_offset);
return true;
}
T _getCurrent(int offset);
T get current => _current;
}
class _LinkedHashMapTable<K, V> extends _LinkedHashTable<K> {
static const int _INITIAL_CAPACITY = 8;
static const int _VALUE_INDEX = 3;
int get _entrySize => 4;
_LinkedHashMapTable() : super(_INITIAL_CAPACITY);
V _value(int offset) => _table[offset + _VALUE_INDEX];
void _setValue(int offset, V value) { _table[offset + _VALUE_INDEX] = value; }
_copyEntry(List oldTable, int fromOffset, int toOffset) {
_table[toOffset + _VALUE_INDEX] = oldTable[fromOffset + _VALUE_INDEX];
}
}