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

Side by Side Diff: dart/runtime/lib/compact_hash.dart

Issue 1005083004: Avoid infinite growth of linked hash map implementation (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge
Patch Set: Created 5 years, 9 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 | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 6
7 // Hash table with open addressing that separates the index from keys/values. 7 // Hash table with open addressing that separates the index from keys/values.
8 abstract class _HashBase { 8 abstract class _HashBase {
9 // Each occupied entry in _index is a fixed-size integer that encodes a pair: 9 // Each occupied entry in _index is a fixed-size integer that encodes a pair:
10 // [ hash pattern for key | index of entry in _data ] 10 // [ hash pattern for key | index of entry in _data ]
11 // The hash pattern is based on hashCode, but is guaranteed to be non-zero. 11 // The hash pattern is based on hashCode, but is guaranteed to be non-zero.
12 // The length of _index is always a power of two, and there is always at 12 // The length of _index is always a power of two, and there is always at
13 // least one unoccupied entry. 13 // least one unoccupied entry.
14 Uint32List _index; 14 Uint32List _index;
15 15
16 // The number of bits used for each component is determined by table size. 16 // The number of bits used for each component is determined by table size.
17 // The length of _index is twice the number of entries in _data, and both 17 // The length of _index is twice the number of entries in _data, and both
18 // are doubled when _data is full. Thus, _index will have a max load factor 18 // are doubled when _data is full. Thus, _index will have a max load factor
19 // of 1/2, which enables one more bit to be used for the hash. 19 // of 1/2, which enables one more bit to be used for the hash.
20 // TODO(koda): Consider growing _data by factor sqrt(2), twice as often. 20 // TODO(koda): Consider growing _data by factor sqrt(2), twice as often.
21 static const int _INITIAL_INDEX_BITS = 3; 21 static const int _INITIAL_INDEX_BITS = 3;
22 static const int _INITIAL_INDEX_SIZE = 1 << (_INITIAL_INDEX_BITS + 1); 22 static const int _INITIAL_INDEX_SIZE = 1 << (_INITIAL_INDEX_BITS + 1);
23 23
24 // Unused and deleted entries are marked by 0 and 1, respectively. 24 // Unused and deleted entries are marked by 0 and 1, respectively.
25 static const int _UNUSED_PAIR = 0; 25 static const int _UNUSED_PAIR = 0;
26 static const int _DELETED_PAIR = 1; 26 static const int _DELETED_PAIR = 1;
27 27
28 // Cached in-place mask for the hash pattern component. On 32-bit, the top 28 // Cached in-place mask for the hash pattern component. On 32-bit, the top
29 // bits are wasted to avoid Mint allocation. 29 // bits are wasted to avoid Mint allocation.
30 // TODO(koda): Reclaim the bits by making the compiler treat hash patterns 30 // TODO(koda): Reclaim the bits by making the compiler treat hash patterns
31 // as unsigned words. 31 // as unsigned words.
32 int _hashMask = int.is64Bit() ? 32 int _hashMask = int.is64Bit() ?
33 (1 << (32 - _INITIAL_INDEX_BITS)) - 1 : 33 (1 << (32 - _INITIAL_INDEX_BITS)) - 1 :
34 (1 << (30 - _INITIAL_INDEX_BITS)) - 1; 34 (1 << (30 - _INITIAL_INDEX_BITS)) - 1;
35 35
36 static int _hashPattern(int fullHash, int hashMask, int size) { 36 static int _hashPattern(int fullHash, int hashMask, int size) {
37 final int maskedHash = fullHash & hashMask; 37 final int maskedHash = fullHash & hashMask;
38 // TODO(koda): Consider keeping bit length and use left shift. 38 // TODO(koda): Consider keeping bit length and use left shift.
39 return (maskedHash == 0) ? (size >> 1) : maskedHash * (size >> 1); 39 return (maskedHash == 0) ? (size >> 1) : maskedHash * (size >> 1);
40 } 40 }
41 41
42 // Linear probing. 42 // Linear probing.
43 static int _firstProbe(int fullHash, int sizeMask) { 43 static int _firstProbe(int fullHash, int sizeMask) {
44 final int i = fullHash & sizeMask; 44 final int i = fullHash & sizeMask;
45 // Light, fast shuffle to mitigate bad hashCode (e.g., sequential). 45 // Light, fast shuffle to mitigate bad hashCode (e.g., sequential).
46 return ((i << 1) + i) & sizeMask; 46 return ((i << 1) + i) & sizeMask;
47 } 47 }
48 static int _nextProbe(int i, int sizeMask) => (i + 1) & sizeMask; 48 static int _nextProbe(int i, int sizeMask) => (i + 1) & sizeMask;
49 49
50 // Fixed-length list of keys (set) or key/value at even/odd indices (map). 50 // Fixed-length list of keys (set) or key/value at even/odd indices (map).
51 List _data; 51 List _data;
52 // Length of _data that is used (i.e., keys + values for a map). 52 // Length of _data that is used (i.e., keys + values for a map).
53 int _usedData = 0; 53 int _usedData = 0;
54 // Number of deleted keys. 54 // Number of deleted keys.
55 int _deletedKeys = 0; 55 int _deletedKeys = 0;
56 56
57 // A self-loop is used to mark a deleted key or value. 57 // A self-loop is used to mark a deleted key or value.
58 static bool _isDeleted(List data, Object keyOrValue) => 58 static bool _isDeleted(List data, Object keyOrValue) =>
59 identical(keyOrValue, data); 59 identical(keyOrValue, data);
60 static void _setDeletedAt(List data, int d) { 60 static void _setDeletedAt(List data, int d) {
61 data[d] = data; 61 data[d] = data;
62 } 62 }
63 63
64 // Concurrent modification detection relies on this checksum monotonically 64 // Concurrent modification detection relies on this checksum monotonically
65 // increasing between reallocations of _data. 65 // increasing between reallocations of _data.
66 int get _checkSum => _usedData + _deletedKeys; 66 int get _checkSum => _usedData + _deletedKeys;
(...skipping 15 matching lines...) Expand all
82 // appended to _data. 82 // appended to _data.
83 class _CompactLinkedHashMap<K, V> 83 class _CompactLinkedHashMap<K, V>
84 extends MapBase<K, V> with _HashBase, _OperatorEqualsAndHashCode 84 extends MapBase<K, V> with _HashBase, _OperatorEqualsAndHashCode
85 implements LinkedHashMap<K, V> { 85 implements LinkedHashMap<K, V> {
86 86
87 _CompactLinkedHashMap() { 87 _CompactLinkedHashMap() {
88 assert(_HashBase._UNUSED_PAIR == 0); 88 assert(_HashBase._UNUSED_PAIR == 0);
89 _index = new Uint32List(_HashBase._INITIAL_INDEX_SIZE); 89 _index = new Uint32List(_HashBase._INITIAL_INDEX_SIZE);
90 _data = new List(_HashBase._INITIAL_INDEX_SIZE); 90 _data = new List(_HashBase._INITIAL_INDEX_SIZE);
91 } 91 }
92 92
93 int get length => (_usedData >> 1) - _deletedKeys; 93 int get length => (_usedData >> 1) - _deletedKeys;
94 bool get isEmpty => length == 0; 94 bool get isEmpty => length == 0;
95 bool get isNotEmpty => !isEmpty; 95 bool get isNotEmpty => !isEmpty;
96 96
97 void _rehash() { 97 void _rehash() {
98 if ((_deletedKeys << 1) > _usedData) { 98 if ((_deletedKeys << 2) > _usedData) {
koda 2015/03/24 16:27:57 Perhaps expressing this in terms of 'length' inste
kustermann 2015/03/24 16:44:01 Good point. Ivan wanted me to submit the CL as is
99 // TODO(koda): Consider shrinking. 99 // TODO(koda): Consider shrinking.
100 // TODO(koda): Consider in-place compaction and more costly CME check. 100 // TODO(koda): Consider in-place compaction and more costly CME check.
101 _init(_index.length, _hashMask, _data, _usedData); 101 _init(_index.length, _hashMask, _data, _usedData);
102 } else { 102 } else {
103 // TODO(koda): Support 32->64 bit transition (and adjust _hashMask). 103 // TODO(koda): Support 32->64 bit transition (and adjust _hashMask).
104 _init(_index.length << 1, _hashMask >> 1, _data, _usedData); 104 _init(_index.length << 1, _hashMask >> 1, _data, _usedData);
105 } 105 }
106 } 106 }
107 107
108 void clear() { 108 void clear() {
109 if (!isEmpty) { 109 if (!isEmpty) {
110 _init(_index.length, _hashMask); 110 _init(_index.length, _hashMask);
111 } 111 }
112 } 112 }
113 113
114 // Allocate new _index and _data, and optionally copy existing contents. 114 // Allocate new _index and _data, and optionally copy existing contents.
115 void _init(int size, int hashMask, [List oldData, int oldUsed]) { 115 void _init(int size, int hashMask, [List oldData, int oldUsed]) {
116 assert(size & (size - 1) == 0); 116 assert(size & (size - 1) == 0);
117 assert(_HashBase._UNUSED_PAIR == 0); 117 assert(_HashBase._UNUSED_PAIR == 0);
118 _index = new Uint32List(size); 118 _index = new Uint32List(size);
119 _hashMask = hashMask; 119 _hashMask = hashMask;
120 _data = new List(size); 120 _data = new List(size);
121 _usedData = 0; 121 _usedData = 0;
122 _deletedKeys = 0; 122 _deletedKeys = 0;
123 if (oldData != null) { 123 if (oldData != null) {
124 for (int i = 0; i < oldUsed; i += 2) { 124 for (int i = 0; i < oldUsed; i += 2) {
125 var key = oldData[i]; 125 var key = oldData[i];
126 if (!_HashBase._isDeleted(oldData, key)) { 126 if (!_HashBase._isDeleted(oldData, key)) {
127 // TODO(koda): While there are enough hash bits, avoid hashCode calls. 127 // TODO(koda): While there are enough hash bits, avoid hashCode calls.
128 this[key] = oldData[i + 1]; 128 this[key] = oldData[i + 1];
129 } 129 }
130 } 130 }
131 } 131 }
132 } 132 }
133 133
134 void _insert(K key, V value, int hashPattern, int i) { 134 void _insert(K key, V value, int hashPattern, int i) {
135 if (_usedData == _data.length) { 135 if (_usedData == _data.length) {
136 _rehash(); 136 _rehash();
137 this[key] = value; 137 this[key] = value;
138 } else { 138 } else {
139 assert(1 <= hashPattern && hashPattern < (1 << 32)); 139 assert(1 <= hashPattern && hashPattern < (1 << 32));
140 final int index = _usedData >> 1; 140 final int index = _usedData >> 1;
141 assert((index & hashPattern) == 0); 141 assert((index & hashPattern) == 0);
142 _index[i] = hashPattern | index; 142 _index[i] = hashPattern | index;
143 _data[_usedData++] = key; 143 _data[_usedData++] = key;
144 _data[_usedData++] = value; 144 _data[_usedData++] = value;
145 } 145 }
146 } 146 }
147 147
148 // If key is present, returns the index of the value in _data, else returns 148 // If key is present, returns the index of the value in _data, else returns
149 // the negated insertion point in _index. 149 // the negated insertion point in _index.
150 int _findValueOrInsertPoint(K key, int fullHash, int hashPattern, int size) { 150 int _findValueOrInsertPoint(K key, int fullHash, int hashPattern, int size) {
151 final int sizeMask = size - 1; 151 final int sizeMask = size - 1;
152 final int maxEntries = size >> 1; 152 final int maxEntries = size >> 1;
153 int i = _HashBase._firstProbe(fullHash, sizeMask); 153 int i = _HashBase._firstProbe(fullHash, sizeMask);
154 int firstDeleted = -1; 154 int firstDeleted = -1;
155 int pair = _index[i]; 155 int pair = _index[i];
156 while (pair != _HashBase._UNUSED_PAIR) { 156 while (pair != _HashBase._UNUSED_PAIR) {
157 if (pair == _HashBase._DELETED_PAIR) { 157 if (pair == _HashBase._DELETED_PAIR) {
158 if (firstDeleted < 0){ 158 if (firstDeleted < 0){
159 firstDeleted = i; 159 firstDeleted = i;
160 } 160 }
161 } else { 161 } else {
162 final int entry = hashPattern ^ pair; 162 final int entry = hashPattern ^ pair;
163 if (entry < maxEntries) { 163 if (entry < maxEntries) {
164 final int d = entry << 1; 164 final int d = entry << 1;
165 if (_equals(key, _data[d])) { 165 if (_equals(key, _data[d])) {
166 return d + 1; 166 return d + 1;
167 } 167 }
168 } 168 }
169 } 169 }
170 i = _HashBase._nextProbe(i, sizeMask); 170 i = _HashBase._nextProbe(i, sizeMask);
171 pair = _index[i]; 171 pair = _index[i];
172 } 172 }
173 return firstDeleted >= 0 ? -firstDeleted : -i; 173 return firstDeleted >= 0 ? -firstDeleted : -i;
174 } 174 }
175 175
176 void operator[]=(K key, V value) { 176 void operator[]=(K key, V value) {
177 final int size = _index.length; 177 final int size = _index.length;
178 final int sizeMask = size - 1; 178 final int sizeMask = size - 1;
179 final int fullHash = _hashCode(key); 179 final int fullHash = _hashCode(key);
180 final int hashPattern = _HashBase._hashPattern(fullHash, _hashMask, size); 180 final int hashPattern = _HashBase._hashPattern(fullHash, _hashMask, size);
181 final int d = _findValueOrInsertPoint(key, fullHash, hashPattern, size); 181 final int d = _findValueOrInsertPoint(key, fullHash, hashPattern, size);
182 if (d > 0) { 182 if (d > 0) {
183 _data[d] = value; 183 _data[d] = value;
184 } else { 184 } else {
185 final int i = -d; 185 final int i = -d;
186 _insert(key, value, hashPattern, i); 186 _insert(key, value, hashPattern, i);
187 } 187 }
188 } 188 }
189 189
190 V putIfAbsent(K key, V ifAbsent()) { 190 V putIfAbsent(K key, V ifAbsent()) {
191 final int size = _index.length; 191 final int size = _index.length;
192 final int sizeMask = size - 1; 192 final int sizeMask = size - 1;
193 final int maxEntries = size >> 1; 193 final int maxEntries = size >> 1;
194 final int fullHash = _hashCode(key); 194 final int fullHash = _hashCode(key);
195 final int hashPattern = _HashBase._hashPattern(fullHash, _hashMask, size); 195 final int hashPattern = _HashBase._hashPattern(fullHash, _hashMask, size);
196 final int d = _findValueOrInsertPoint(key, fullHash, hashPattern, size); 196 final int d = _findValueOrInsertPoint(key, fullHash, hashPattern, size);
197 if (d > 0) { 197 if (d > 0) {
198 return _data[d]; 198 return _data[d];
199 } 199 }
200 // 'ifAbsent' is allowed to modify the map. 200 // 'ifAbsent' is allowed to modify the map.
201 List oldData = _data; 201 List oldData = _data;
202 int oldCheckSum = _checkSum; 202 int oldCheckSum = _checkSum;
203 V value = ifAbsent(); 203 V value = ifAbsent();
204 if (_isModifiedSince(oldData, oldCheckSum)) { 204 if (_isModifiedSince(oldData, oldCheckSum)) {
205 this[key] = value; 205 this[key] = value;
206 } else { 206 } else {
207 final int i = -d; 207 final int i = -d;
208 _insert(key, value, hashPattern, i); 208 _insert(key, value, hashPattern, i);
209 } 209 }
210 return value; 210 return value;
211 } 211 }
212 212
213 V remove(Object key) { 213 V remove(Object key) {
214 final int size = _index.length; 214 final int size = _index.length;
215 final int sizeMask = size - 1; 215 final int sizeMask = size - 1;
216 final int maxEntries = size >> 1; 216 final int maxEntries = size >> 1;
217 final int fullHash = _hashCode(key); 217 final int fullHash = _hashCode(key);
218 final int hashPattern = _HashBase._hashPattern(fullHash, _hashMask, size); 218 final int hashPattern = _HashBase._hashPattern(fullHash, _hashMask, size);
219 int i = _HashBase._firstProbe(fullHash, sizeMask); 219 int i = _HashBase._firstProbe(fullHash, sizeMask);
220 int pair = _index[i]; 220 int pair = _index[i];
221 while (pair != _HashBase._UNUSED_PAIR) { 221 while (pair != _HashBase._UNUSED_PAIR) {
222 if (pair != _HashBase._DELETED_PAIR) { 222 if (pair != _HashBase._DELETED_PAIR) {
223 final int entry = hashPattern ^ pair; 223 final int entry = hashPattern ^ pair;
224 if (entry < maxEntries) { 224 if (entry < maxEntries) {
225 final int d = entry << 1; 225 final int d = entry << 1;
226 if (_equals(key, _data[d])) { 226 if (_equals(key, _data[d])) {
227 _index[i] = _HashBase._DELETED_PAIR; 227 _index[i] = _HashBase._DELETED_PAIR;
228 _HashBase._setDeletedAt(_data, d); 228 _HashBase._setDeletedAt(_data, d);
229 V value = _data[d + 1]; 229 V value = _data[d + 1];
230 _HashBase._setDeletedAt(_data, d + 1); 230 _HashBase._setDeletedAt(_data, d + 1);
231 ++_deletedKeys; 231 ++_deletedKeys;
232 return value; 232 return value;
233 } 233 }
234 } 234 }
235 } 235 }
236 i = _HashBase._nextProbe(i, sizeMask); 236 i = _HashBase._nextProbe(i, sizeMask);
237 pair = _index[i]; 237 pair = _index[i];
238 } 238 }
239 return null; 239 return null;
240 } 240 }
241 241
242 // If key is absent, return _data (which is never a value). 242 // If key is absent, return _data (which is never a value).
243 Object _getValueOrData(Object key) { 243 Object _getValueOrData(Object key) {
244 final int size = _index.length; 244 final int size = _index.length;
245 final int sizeMask = size - 1; 245 final int sizeMask = size - 1;
246 final int maxEntries = size >> 1; 246 final int maxEntries = size >> 1;
247 final int fullHash = _hashCode(key); 247 final int fullHash = _hashCode(key);
248 final int hashPattern = _HashBase._hashPattern(fullHash, _hashMask, size); 248 final int hashPattern = _HashBase._hashPattern(fullHash, _hashMask, size);
249 int i = _HashBase._firstProbe(fullHash, sizeMask); 249 int i = _HashBase._firstProbe(fullHash, sizeMask);
250 int pair = _index[i]; 250 int pair = _index[i];
251 while (pair != _HashBase._UNUSED_PAIR) { 251 while (pair != _HashBase._UNUSED_PAIR) {
252 if (pair != _HashBase._DELETED_PAIR) { 252 if (pair != _HashBase._DELETED_PAIR) {
253 final int entry = hashPattern ^ pair; 253 final int entry = hashPattern ^ pair;
254 if (entry < maxEntries) { 254 if (entry < maxEntries) {
255 final int d = entry << 1; 255 final int d = entry << 1;
256 if (_equals(key, _data[d])) { 256 if (_equals(key, _data[d])) {
257 return _data[d + 1]; 257 return _data[d + 1];
258 } 258 }
259 } 259 }
260 } 260 }
261 i = _HashBase._nextProbe(i, sizeMask); 261 i = _HashBase._nextProbe(i, sizeMask);
262 pair = _index[i]; 262 pair = _index[i];
263 } 263 }
264 return _data; 264 return _data;
265 } 265 }
266 266
267 bool containsKey(Object key) => !identical(_data, _getValueOrData(key)); 267 bool containsKey(Object key) => !identical(_data, _getValueOrData(key));
268 268
269 V operator[](Object key) { 269 V operator[](Object key) {
270 var v = _getValueOrData(key); 270 var v = _getValueOrData(key);
271 return identical(_data, v) ? null : v; 271 return identical(_data, v) ? null : v;
272 } 272 }
273 273
274 bool containsValue(Object value) { 274 bool containsValue(Object value) {
275 for (var v in values) { 275 for (var v in values) {
276 // Spec. says this should always use "==", also for identity maps, etc. 276 // Spec. says this should always use "==", also for identity maps, etc.
277 if (v == value) { 277 if (v == value) {
278 return true; 278 return true;
279 } 279 }
280 } 280 }
281 return false; 281 return false;
282 } 282 }
283 283
(...skipping 18 matching lines...) Expand all
302 302
303 class _CompactLinkedCustomHashMap<K, V> 303 class _CompactLinkedCustomHashMap<K, V>
304 extends _CompactLinkedHashMap<K, V> { 304 extends _CompactLinkedHashMap<K, V> {
305 final _equality; 305 final _equality;
306 final _hasher; 306 final _hasher;
307 final _validKey; 307 final _validKey;
308 308
309 // TODO(koda): Ask gbracha why I cannot have fields _equals/_hashCode. 309 // TODO(koda): Ask gbracha why I cannot have fields _equals/_hashCode.
310 int _hashCode(e) => _hasher(e); 310 int _hashCode(e) => _hasher(e);
311 bool _equals(e1, e2) => _equality(e1, e2); 311 bool _equals(e1, e2) => _equality(e1, e2);
312 312
313 bool containsKey(Object o) => _validKey(o) ? super.containsKey(o) : false; 313 bool containsKey(Object o) => _validKey(o) ? super.containsKey(o) : false;
314 V operator[](Object o) => _validKey(o) ? super[o] : null; 314 V operator[](Object o) => _validKey(o) ? super[o] : null;
315 V remove(Object o) => _validKey(o) ? super.remove(o) : null; 315 V remove(Object o) => _validKey(o) ? super.remove(o) : null;
316 316
317 _CompactLinkedCustomHashMap(this._equality, this._hasher, validKey) 317 _CompactLinkedCustomHashMap(this._equality, this._hasher, validKey)
318 : _validKey = (validKey != null) ? validKey : new _TypeTest<K>().test; 318 : _validKey = (validKey != null) ? validKey : new _TypeTest<K>().test;
319 } 319 }
320 320
321 // Iterates through _data[_offset + _step], _data[_offset + 2*_step], ... 321 // Iterates through _data[_offset + _step], _data[_offset + 2*_step], ...
322 // and checks for concurrent modification. 322 // and checks for concurrent modification.
323 class _CompactIterable<E> extends IterableBase<E> { 323 class _CompactIterable<E> extends IterableBase<E> {
324 final _table; 324 final _table;
325 final List _data; 325 final List _data;
326 final int _len; 326 final int _len;
327 final int _offset; 327 final int _offset;
328 final int _step; 328 final int _step;
329 329
330 _CompactIterable(this._table, this._data, this._len, 330 _CompactIterable(this._table, this._data, this._len,
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
380 380
381 int get length => _usedData - _deletedKeys; 381 int get length => _usedData - _deletedKeys;
382 382
383 void _rehash() { 383 void _rehash() {
384 if ((_deletedKeys << 1) > _usedData) { 384 if ((_deletedKeys << 1) > _usedData) {
385 _init(_index.length, _hashMask, _data, _usedData); 385 _init(_index.length, _hashMask, _data, _usedData);
386 } else { 386 } else {
387 _init(_index.length << 1, _hashMask >> 1, _data, _usedData); 387 _init(_index.length << 1, _hashMask >> 1, _data, _usedData);
388 } 388 }
389 } 389 }
390 390
391 void clear() { 391 void clear() {
392 if (!isEmpty) { 392 if (!isEmpty) {
393 _init(_index.length, _hashMask); 393 _init(_index.length, _hashMask);
394 } 394 }
395 } 395 }
396 396
397 void _init(int size, int hashMask, [List oldData, int oldUsed]) { 397 void _init(int size, int hashMask, [List oldData, int oldUsed]) {
398 _index = new Uint32List(size); 398 _index = new Uint32List(size);
399 _hashMask = hashMask; 399 _hashMask = hashMask;
400 _data = new List(size >> 1); 400 _data = new List(size >> 1);
401 _usedData = 0; 401 _usedData = 0;
402 _deletedKeys = 0; 402 _deletedKeys = 0;
403 if (oldData != null) { 403 if (oldData != null) {
404 for (int i = 0; i < oldUsed; i += 1) { 404 for (int i = 0; i < oldUsed; i += 1) {
405 var key = oldData[i]; 405 var key = oldData[i];
406 if (!_HashBase._isDeleted(oldData, key)) { 406 if (!_HashBase._isDeleted(oldData, key)) {
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
438 add(key); 438 add(key);
439 } else { 439 } else {
440 final int insertionPoint = (firstDeleted >= 0) ? firstDeleted : i; 440 final int insertionPoint = (firstDeleted >= 0) ? firstDeleted : i;
441 assert(1 <= hashPattern && hashPattern < (1 << 32)); 441 assert(1 <= hashPattern && hashPattern < (1 << 32));
442 assert((hashPattern & _usedData) == 0); 442 assert((hashPattern & _usedData) == 0);
443 _index[insertionPoint] = hashPattern | _usedData; 443 _index[insertionPoint] = hashPattern | _usedData;
444 _data[_usedData++] = key; 444 _data[_usedData++] = key;
445 } 445 }
446 return true; 446 return true;
447 } 447 }
448 448
449 // If key is absent, return _data (which is never a value). 449 // If key is absent, return _data (which is never a value).
450 Object _getKeyOrData(Object key) { 450 Object _getKeyOrData(Object key) {
451 final int size = _index.length; 451 final int size = _index.length;
452 final int sizeMask = size - 1; 452 final int sizeMask = size - 1;
453 final int maxEntries = size >> 1; 453 final int maxEntries = size >> 1;
454 final int fullHash = _hashCode(key); 454 final int fullHash = _hashCode(key);
455 final int hashPattern = _HashBase._hashPattern(fullHash, _hashMask, size); 455 final int hashPattern = _HashBase._hashPattern(fullHash, _hashMask, size);
456 int i = _HashBase._firstProbe(fullHash, sizeMask); 456 int i = _HashBase._firstProbe(fullHash, sizeMask);
457 int pair = _index[i]; 457 int pair = _index[i];
458 while (pair != _HashBase._UNUSED_PAIR) { 458 while (pair != _HashBase._UNUSED_PAIR) {
459 if (pair != _HashBase._DELETED_PAIR) { 459 if (pair != _HashBase._DELETED_PAIR) {
460 final int d = hashPattern ^ pair; 460 final int d = hashPattern ^ pair;
461 if (d < maxEntries && _equals(key, _data[d])) { 461 if (d < maxEntries && _equals(key, _data[d])) {
462 return _data[d]; // Note: Must return the existing key. 462 return _data[d]; // Note: Must return the existing key.
463 } 463 }
464 } 464 }
465 i = _HashBase._nextProbe(i, sizeMask); 465 i = _HashBase._nextProbe(i, sizeMask);
466 pair = _index[i]; 466 pair = _index[i];
467 } 467 }
468 return _data; 468 return _data;
469 } 469 }
470 470
471 E lookup(Object key) { 471 E lookup(Object key) {
472 var k = _getKeyOrData(key); 472 var k = _getKeyOrData(key);
473 return identical(_data, k) ? null : k; 473 return identical(_data, k) ? null : k;
474 } 474 }
475 475
476 bool contains(Object key) => !identical(_data, _getKeyOrData(key)); 476 bool contains(Object key) => !identical(_data, _getKeyOrData(key));
477 477
478 bool remove(Object key) { 478 bool remove(Object key) {
479 final int size = _index.length; 479 final int size = _index.length;
480 final int sizeMask = size - 1; 480 final int sizeMask = size - 1;
481 final int maxEntries = size >> 1; 481 final int maxEntries = size >> 1;
482 final int fullHash = _hashCode(key); 482 final int fullHash = _hashCode(key);
483 final int hashPattern = _HashBase._hashPattern(fullHash, _hashMask, size); 483 final int hashPattern = _HashBase._hashPattern(fullHash, _hashMask, size);
484 int i = _HashBase._firstProbe(fullHash, sizeMask); 484 int i = _HashBase._firstProbe(fullHash, sizeMask);
485 int pair = _index[i]; 485 int pair = _index[i];
486 while (pair != _HashBase._UNUSED_PAIR) { 486 while (pair != _HashBase._UNUSED_PAIR) {
487 if (pair != _HashBase._DELETED_PAIR) { 487 if (pair != _HashBase._DELETED_PAIR) {
488 final int d = hashPattern ^ pair; 488 final int d = hashPattern ^ pair;
489 if (d < maxEntries && _equals(key, _data[d])) { 489 if (d < maxEntries && _equals(key, _data[d])) {
490 _index[i] = _HashBase._DELETED_PAIR; 490 _index[i] = _HashBase._DELETED_PAIR;
491 _HashBase._setDeletedAt(_data, d); 491 _HashBase._setDeletedAt(_data, d);
492 ++_deletedKeys; 492 ++_deletedKeys;
493 return true; 493 return true;
494 } 494 }
495 } 495 }
496 i = _HashBase._nextProbe(i, sizeMask); 496 i = _HashBase._nextProbe(i, sizeMask);
497 pair = _index[i]; 497 pair = _index[i];
498 } 498 }
499 return false; 499 return false;
500 } 500 }
501 501
502 Iterator<E> get iterator => 502 Iterator<E> get iterator =>
503 new _CompactIterator<E>(this, _data, _usedData, -1, 1); 503 new _CompactIterator<E>(this, _data, _usedData, -1, 1);
504 504
505 // Returns a set of the same type, although this 505 // Returns a set of the same type, although this
506 // is not required by the spec. (For instance, always using an identity set 506 // is not required by the spec. (For instance, always using an identity set
507 // would be technically correct, albeit surprising.) 507 // would be technically correct, albeit surprising.)
508 Set<E> toSet() => new _CompactLinkedHashSet<E>()..addAll(this); 508 Set<E> toSet() => new _CompactLinkedHashSet<E>()..addAll(this);
509 } 509 }
510 510
511 class _CompactLinkedIdentityHashSet<E> 511 class _CompactLinkedIdentityHashSet<E>
512 extends _CompactLinkedHashSet<E> with _IdenticalAndIdentityHashCode { 512 extends _CompactLinkedHashSet<E> with _IdenticalAndIdentityHashCode {
513 Set<E> toSet() => new _CompactLinkedIdentityHashSet<E>()..addAll(this); 513 Set<E> toSet() => new _CompactLinkedIdentityHashSet<E>()..addAll(this);
514 } 514 }
515 515
516 class _CompactLinkedCustomHashSet<E> 516 class _CompactLinkedCustomHashSet<E>
517 extends _CompactLinkedHashSet<E> { 517 extends _CompactLinkedHashSet<E> {
518 final _equality; 518 final _equality;
519 final _hasher; 519 final _hasher;
520 final _validKey; 520 final _validKey;
521 521
522 int _hashCode(e) => _hasher(e); 522 int _hashCode(e) => _hasher(e);
523 bool _equals(e1, e2) => _equality(e1, e2); 523 bool _equals(e1, e2) => _equality(e1, e2);
524 524
525 bool contains(Object o) => _validKey(o) ? super.contains(o) : false; 525 bool contains(Object o) => _validKey(o) ? super.contains(o) : false;
526 E lookup(Object o) => _validKey(o) ? super.lookup(o) : null; 526 E lookup(Object o) => _validKey(o) ? super.lookup(o) : null;
527 bool remove(Object o) => _validKey(o) ? super.remove(o) : false; 527 bool remove(Object o) => _validKey(o) ? super.remove(o) : false;
528 528
529 _CompactLinkedCustomHashSet(this._equality, this._hasher, validKey) 529 _CompactLinkedCustomHashSet(this._equality, this._hasher, validKey)
530 : _validKey = (validKey != null) ? validKey : new _TypeTest<E>().test; 530 : _validKey = (validKey != null) ? validKey : new _TypeTest<E>().test;
531 531
532 Set<E> toSet() => 532 Set<E> toSet() =>
533 new _CompactLinkedCustomHashSet<E>(_equality, _hasher, _validKey) 533 new _CompactLinkedCustomHashSet<E>(_equality, _hasher, _validKey)
534 ..addAll(this); 534 ..addAll(this);
535 } 535 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698