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

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

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

Powered by Google App Engine
This is Rietveld 408576698