Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(238)

Side by Side Diff: sdk/lib/collection/hash_table.dart

Issue 13913005: Implement JS version of LinkedHashSet and move the various HashTable implementations to the VM coll… (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Map -> set. Created 7 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « sdk/lib/collection/hash_set.dart ('k') | sdk/lib/collection/linked_hash_set.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 part of dart.collection;
6
7 class _DeadEntry {
8 const _DeadEntry();
9 }
10
11 class _NullKey {
12 const _NullKey();
13 int get hashCode => null.hashCode;
14 }
15
16 const _TOMBSTONE = const _DeadEntry();
17 const _NULL = const _NullKey();
18
19 class _HashTable<K> {
20 /**
21 * Table of entries with [_entrySize] slots per entry.
22 *
23 * Capacity in entries must be factor of two.
24 */
25 List _table;
26 /** Current capacity. Always equal to [:_table.length ~/ _entrySize:]. */
27 int _capacity;
28 /** Count of occupied entries, including deleted ones. */
29 int _entryCount = 0;
30 /** Count of deleted entries. */
31 int _deletedCount = 0;
32 /** Counter incremented when table is modified. */
33 int _modificationCount = 0;
34 /** If set, used as the source object for [ConcurrentModificationError]s. */
35 Object _container;
36
37 _HashTable(int initialCapacity) : _capacity = initialCapacity {
38 _table = _createTable(initialCapacity);
39 }
40
41 /** Reads key from table. Converts _NULL marker to null. */
42 Object _key(offset) {
43 assert(!_isFree(_table[offset]));
44 Object key = _table[offset];
45 if (!identical(key, _NULL)) return key;
46 return null;
47 }
48
49 /** Writes key to table. Converts null to _NULL marker. */
50 void _setKey(int offset, Object key) {
51 if (key == null) key = _NULL;
52 _table[offset] = key;
53 }
54
55 int get _elementCount => _entryCount - _deletedCount;
56
57 /** Size of each entry. */
58 int get _entrySize => 1;
59
60 void _checkModification(int expectedModificationCount) {
61 if (_modificationCount != expectedModificationCount) {
62 throw new ConcurrentModificationError(_container);
63 }
64 }
65
66 void _recordModification() {
67 // Value cycles after 2^30 modifications. If you keep hold of an
68 // iterator for that long, you might miss a modification detection,
69 // and iteration can go sour. Don't do that.
70 _modificationCount = (_modificationCount + 1) & (0x3FFFFFFF);
71 }
72
73 /**
74 * Create an empty table.
75 */
76 List _createTable(int capacity) {
77 List table = new List(capacity * _entrySize);
78 return table;
79 }
80
81 /** First table probe. */
82 int _firstProbe(int hashCode, int capacity) {
83 return hashCode & (capacity - 1);
84 }
85
86 /** Following table probes. */
87 int _nextProbe(int previousIndex, int probeCount, int capacity) {
88 // When capacity is a power of 2, this probing algorithm (the triangular
89 // number sequence modulo capacity) is guaranteed to hit all indices exactly
90 // once before repeating.
91 return (previousIndex + probeCount) & (capacity - 1);
92 }
93
94 /** Whether an object is a free-marker (either tombstone or free). */
95 bool _isFree(Object marker) =>
96 marker == null || identical(marker, _TOMBSTONE);
97
98 /**
99 * Look up the offset for an object in the table.
100 *
101 * Finds the offset of the object in the table, if it is there,
102 * or the first free offset for its hashCode.
103 */
104 int _probeForAdd(int hashCode, Object object) {
105 int entrySize = _entrySize;
106 int index = _firstProbe(hashCode, _capacity);
107 int firstTombstone = -1;
108 int probeCount = 0;
109 while (true) {
110 int offset = index * entrySize;
111 Object entry = _table[offset];
112 if (identical(entry, _TOMBSTONE)) {
113 if (firstTombstone < 0) firstTombstone = offset;
114 } else if (entry == null) {
115 if (firstTombstone < 0) return offset;
116 return firstTombstone;
117 } else if (identical(_NULL, entry) ? _equals(null, object)
118 : _equals(entry, object)) {
119 return offset;
120 }
121 // The _nextProbe is designed so that it hits
122 // every index eventually.
123 index = _nextProbe(index, ++probeCount, _capacity);
124 }
125 }
126
127 /**
128 * Look up the offset for an object in the table.
129 *
130 * If the object is in the table, its offset is returned.
131 *
132 * If the object is not in the table, Otherwise a negative value is returned.
133 */
134 int _probeForLookup(int hashCode, Object object) {
135 int entrySize = _entrySize;
136 int index = _firstProbe(hashCode, _capacity);
137 int probeCount = 0;
138 while (true) {
139 int offset = index * entrySize;
140 Object entry = _table[offset];
141 if (entry == null) {
142 return -1;
143 } else if (!identical(_TOMBSTONE, entry)) {
144 if (identical(_NULL, entry) ? _equals(null, object)
145 : _equals(entry, object)) {
146 return offset;
147 }
148 }
149 // The _nextProbe is designed so that it hits
150 // every index eventually.
151 index = _nextProbe(index, ++probeCount, _capacity);
152 }
153 }
154
155 // Override the following two to change equality/hashCode computations
156
157 /**
158 * Compare two object for equality.
159 *
160 * The first object is the one already in the table,
161 * and the second is the one being searched for.
162 */
163 bool _equals(Object element, Object other) {
164 return element == other;
165 }
166
167 /**
168 * Compute hash-code for an object.
169 */
170 int _hashCodeOf(Object object) => object.hashCode;
171
172 /**
173 * Ensure that the table isn't too full for its own good.
174 *
175 * Call this after adding an element.
176 */
177 int _checkCapacity() {
178 // Compute everything in multiples of entrySize to avoid division.
179 int freeCount = _capacity - _entryCount;
180 if (freeCount * 4 < _capacity ||
181 freeCount < _deletedCount) {
182 // Less than 25% free or more deleted entries than free entries.
183 _grow(_entryCount - _deletedCount);
184 }
185 }
186
187 void _grow(int contentCount) {
188 int capacity = _capacity;
189 // Don't grow to less than twice the needed capacity.
190 int minCapacity = contentCount * 2;
191 while (capacity < minCapacity) {
192 capacity *= 2;
193 }
194 // Reset to another table and add all existing elements.
195 List oldTable = _table;
196 _table = _createTable(capacity);
197 _capacity = capacity;
198 _entryCount = 0;
199 _deletedCount = 0;
200 _addAllEntries(oldTable);
201 _recordModification();
202 }
203
204 /**
205 * Copies all non-free entries from the old table to the new empty table.
206 */
207 void _addAllEntries(List oldTable) {
208 for (int i = 0; i < oldTable.length; i += _entrySize) {
209 Object object = oldTable[i];
210 if (!_isFree(object)) {
211 int toOffset = _put(object);
212 _copyEntry(oldTable, i, toOffset);
213 }
214 }
215 }
216
217 /**
218 * Copies everything but the key element from one entry to another.
219 *
220 * Called while growing the base array.
221 *
222 * Override this if any non-key fields need copying.
223 */
224 void _copyEntry(List fromTable, int fromOffset, int toOffset) {}
225
226 // The following three methods are for simple get/set/remove operations.
227 // They only affect the key of an entry. The remaining fields must be
228 // filled by the caller.
229
230 /**
231 * Returns the offset of a key in [_table], or negative if it's not there.
232 */
233 int _get(K key) {
234 return _probeForLookup(_hashCodeOf(key), key);
235 }
236
237 /**
238 * Puts the key into the table and returns its offset into [_table].
239 *
240 * If [_entrySize] is greater than 1, the caller should fill the
241 * remaining fields.
242 *
243 * Remember to call [_checkCapacity] after using this method.
244 */
245 int _put(K key) {
246 int offset = _probeForAdd(_hashCodeOf(key), key);
247 Object oldEntry = _table[offset];
248 if (oldEntry == null) {
249 _entryCount++;
250 } else if (identical(oldEntry, _TOMBSTONE)) {
251 _deletedCount--;
252 } else {
253 return offset;
254 }
255 _setKey(offset, key);
256 _recordModification();
257 return offset;
258 }
259
260 /**
261 * Removes a key from the table and returns its offset into [_table].
262 *
263 * Returns null if the key was not in the table.
264 * If [_entrySize] is greater than 1, the caller should clean up the
265 * remaining fields.
266 */
267 int _remove(K key) {
268 int offset = _probeForLookup(_hashCodeOf(key), key);
269 if (offset >= 0) {
270 _deleteEntry(offset);
271 }
272 return offset;
273 }
274
275 /** Clears the table completely, leaving it empty. */
276 void _clear() {
277 if (_elementCount == 0) return;
278 for (int i = 0; i < _table.length; i++) {
279 _table[i] = null;
280 }
281 _entryCount = _deletedCount = 0;
282 _recordModification();
283 }
284
285 /** Clears an entry in the table. */
286 void _deleteEntry(int offset) {
287 assert(!_isFree(_table[offset]));
288 _setKey(offset, _TOMBSTONE);
289 _deletedCount++;
290 _recordModification();
291 }
292 }
293
294 /**
295 * Generic iterable based on a [_HashTable].
296 */
297 abstract class _HashTableIterable<E> extends Iterable<E> {
298 final _HashTable _hashTable;
299 _HashTableIterable(this._hashTable);
300
301 Iterator<E> get iterator;
302
303 /**
304 * Return the iterated value for a given entry.
305 */
306 E _valueAt(int offset, Object key);
307
308 int get length => _hashTable._elementCount;
309
310 bool get isEmpty => _hashTable._elementCount == 0;
311
312 void forEach(void action(E element)) {
313 int entrySize = _hashTable._entrySize;
314 List table = _hashTable._table;
315 int modificationCount = _hashTable._modificationCount;
316 for (int offset = 0; offset < table.length; offset += entrySize) {
317 Object entry = table[offset];
318 if (!_hashTable._isFree(entry)) {
319 E value = _valueAt(offset, entry);
320 action(value);
321 }
322 _hashTable._checkModification(modificationCount);
323 }
324 }
325 }
326
327 abstract class _HashTableIterator<E> implements Iterator<E> {
328 final _HashTable _hashTable;
329 final int _modificationCount;
330 /** Location right after last found element. */
331 int _offset = 0;
332 E _current = null;
333
334 _HashTableIterator(_HashTable hashTable)
335 : _hashTable = hashTable,
336 _modificationCount = hashTable._modificationCount;
337
338 bool moveNext() {
339 _hashTable._checkModification(_modificationCount);
340
341 List table = _hashTable._table;
342 int entrySize = _hashTable._entrySize;
343
344 while (_offset < table.length) {
345 int currentOffset = _offset;
346 Object entry = table[currentOffset];
347 _offset = currentOffset + entrySize;
348 if (!_hashTable._isFree(entry)) {
349 _current = _valueAt(currentOffset, entry);
350 return true;
351 }
352 }
353 _current = null;
354 return false;
355 }
356
357 E get current => _current;
358
359 E _valueAt(int offset, Object key);
360 }
361
362 class _HashTableKeyIterable<K> extends _HashTableIterable<K> {
363 _HashTableKeyIterable(_HashTable<K> hashTable) : super(hashTable);
364
365 Iterator<K> get iterator => new _HashTableKeyIterator<K>(_hashTable);
366
367 K _valueAt(int offset, Object key) {
368 if (identical(key, _NULL)) return null;
369 return key;
370 }
371
372 bool contains(Object value) => _hashTable._get(value) >= 0;
373 }
374
375 class _HashTableKeyIterator<K> extends _HashTableIterator<K> {
376 _HashTableKeyIterator(_HashTable hashTable) : super(hashTable);
377
378 K _valueAt(int offset, Object key) {
379 if (identical(key, _NULL)) return null;
380 return key;
381 }
382 }
383
384 class _HashTableValueIterable<V> extends _HashTableIterable<V> {
385 final int _entryIndex;
386
387 _HashTableValueIterable(_HashTable hashTable, this._entryIndex)
388 : super(hashTable);
389
390 Iterator<V> get iterator {
391 return new _HashTableValueIterator<V>(_hashTable, _entryIndex);
392 }
393
394 V _valueAt(int offset, Object key) => _hashTable._table[offset + _entryIndex];
395 }
396
397 class _HashTableValueIterator<V> extends _HashTableIterator<V> {
398 final int _entryIndex;
399
400 _HashTableValueIterator(_HashTable hashTable, this._entryIndex)
401 : super(hashTable);
402
403 V _valueAt(int offset, Object key) => _hashTable._table[offset + _entryIndex];
404 }
OLDNEW
« no previous file with comments | « sdk/lib/collection/hash_set.dart ('k') | sdk/lib/collection/linked_hash_set.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698