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

Side by Side Diff: pkg/serialization/lib/src/polyfill_identity_set.dart

Issue 11293283: Initial version of a serialization framework (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 8 years, 1 month 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
OLDNEW
(Empty)
1 // Copyright (c) 2012, 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 /**
6 * This provides identity-hashed collections used in serialization. It is a
7 * direct copy of the normal collections with a few small changes to support
8 * identity. As soon as there are proper identity-based collections this should
9 * be removed.
10 */
11
12 // TODO(alanknight): Replace with proper identity collection. Issue 4161
13 library identity_set;
Jennifer Messerly 2012/11/15 08:02:14 I'm going to assume this is basically a copy+paste
Alan Knight 2012/11/15 20:51:03 Yes, it's a cut and paste with two or three very s
14
15 // Hash map implementation with open addressing and quadratic probing.
16 class IdentityMap<K, V> implements HashMap<K, V> {
17
18 // The [_keys] list contains the keys inserted in the map.
19 // The [_keys] list must be a raw list because it
20 // will contain both elements of type K, and the [_DELETED_KEY] of type
21 // [_DeletedKeySentinel].
22 // The alternative of declaring the [_keys] list as of type Object
23 // does not work, because the HashSetIterator constructor would fail:
24 // HashSetIterator(HashSet<E> set)
25 // : _nextValidIndex = -1,
26 // _entries = set_._backingMap._keys {
27 // _advance();
28 // }
29 // With K being type int, for example, it would fail because
30 // List<Object> is not assignable to type List<int> of entries.
31 List _keys;
32
33 // The values inserted in the map. For a filled entry index in this
34 // list, there is always the corresponding key in the [keys_] list
35 // at the same entry index.
36 List<V> _values;
37
38 // The load limit is the number of entries we allow until we double
39 // the size of the lists.
40 int _loadLimit;
41
42 // The current number of entries in the map. Will never be greater
43 // than [_loadLimit].
44 int _numberOfEntries;
45
46 // The current number of deleted entries in the map.
47 int _numberOfDeleted;
48
49 // The sentinel when a key is deleted from the map.
50 static const _DeletedKeySentinel _DELETED_KEY = const _DeletedKeySentinel();
51
52 // The initial capacity of a hash map.
53 static const int _INITIAL_CAPACITY = 8; // must be power of 2
54
55 IdentityMap() {
56 _numberOfEntries = 0;
57 _numberOfDeleted = 0;
58 _loadLimit = _computeLoadLimit(_INITIAL_CAPACITY);
59 _keys = new List(_INITIAL_CAPACITY);
60 _values = new List<V>(_INITIAL_CAPACITY);
61 }
62
63 factory IdentityMap.from(Map<K, V> other) {
64 Map<K, V> result = new IdentityMap<K, V>();
65 other.forEach((K key, V value) { result[key] = value; });
66 return result;
67 }
68
69 static int _computeLoadLimit(int capacity) {
70 return (capacity * 3) ~/ 4;
71 }
72
73 static int _firstProbe(int hashCode, int length) {
74 return hashCode & (length - 1);
75 }
76
77 static int _nextProbe(int currentProbe, int numberOfProbes, int length) {
78 return (currentProbe + numberOfProbes) & (length - 1);
79 }
80
81 int _probeForAdding(K key) {
82 if (key == null) throw const NullPointerException();
83 int hash = _firstProbe(key.hashCode, _keys.length);
84 int numberOfProbes = 1;
85 int initialHash = hash;
86 // insertionIndex points to a slot where a key was deleted.
87 int insertionIndex = -1;
88 while (true) {
89 // [existingKey] can be either of type [K] or [_DeletedKeySentinel].
90 Object existingKey = _keys[hash];
91 if (existingKey === null) {
92 // We are sure the key is not already in the set.
93 // If the current slot is empty and we didn't find any
94 // insertion slot before, return this slot.
95 if (insertionIndex < 0) return hash;
96 // If we did find an insertion slot before, return it.
97 return insertionIndex;
98 /// TODO(alanknight): Note this is changed to be identity (alanknight)
99 } else if (existingKey === key) {
Jennifer Messerly 2012/11/15 08:02:14 fwiw, I think === is going away in favor of identi
Alan Knight 2012/11/15 20:51:03 I think I'll hope I can delete this code entirely
100 // The key is already in the map. Return its slot.
101 return hash;
102 } else if ((insertionIndex < 0) && (_DELETED_KEY === existingKey)) {
103 // The slot contains a deleted element. Because previous calls to this
104 // method may not have had this slot deleted, we must continue iterate
105 // to find if there is a slot with the given key.
106 insertionIndex = hash;
107 }
108
109 // We did not find an insertion slot. Look at the next one.
110 hash = _nextProbe(hash, numberOfProbes++, _keys.length);
111 // _ensureCapacity has guaranteed the following cannot happen.
112 // assert(hash != initialHash);
113 }
114 }
115
116 int _probeForLookup(K key) {
117 if (key == null) throw const NullPointerException();
118 int hash = _firstProbe(key.hashCode, _keys.length);
119 int numberOfProbes = 1;
120 int initialHash = hash;
121 while (true) {
122 // [existingKey] can be either of type [K] or [_DeletedKeySentinel].
123 Object existingKey = _keys[hash];
124 // If the slot does not contain anything (in particular, it does not
125 // contain a deleted key), we know the key is not in the map.
126 if (existingKey === null) return -1;
127 // The key is in the map, return its index.
128 // TODO(alanknight): Changed to be identity
129 if (existingKey === key) return hash;
130 // Go to the next probe.
131 hash = _nextProbe(hash, numberOfProbes++, _keys.length);
132 // _ensureCapacity has guaranteed the following cannot happen.
133 // assert(hash != initialHash);
134 }
135 }
136
137 void _ensureCapacity() {
138 int newNumberOfEntries = _numberOfEntries + 1;
139 // Test if adding an element will reach the load limit.
140 if (newNumberOfEntries >= _loadLimit) {
141 _grow(_keys.length * 2);
142 return;
143 }
144
145 // Make sure that we don't have poor performance when a map
146 // contains lots of deleted entries: we _grow if
147 // there are more deleted entried than free entries.
148 int capacity = _keys.length;
149 int numberOfFreeOrDeleted = capacity - newNumberOfEntries;
150 int numberOfFree = numberOfFreeOrDeleted - _numberOfDeleted;
151 // assert(numberOfFree > 0);
152 if (_numberOfDeleted > numberOfFree) {
153 _grow(_keys.length);
154 }
155 }
156
157 static bool _isPowerOfTwo(int x) {
158 return ((x & (x - 1)) == 0);
159 }
160
161 void _grow(int newCapacity) {
162 assert(_isPowerOfTwo(newCapacity));
163 int capacity = _keys.length;
164 _loadLimit = _computeLoadLimit(newCapacity);
165 List oldKeys = _keys;
166 List<V> oldValues = _values;
167 _keys = new List(newCapacity);
168 _values = new List<V>(newCapacity);
169 for (int i = 0; i < capacity; i++) {
170 // [key] can be either of type [K] or [_DeletedKeySentinel].
171 Object key = oldKeys[i];
172 // If there is no key, we don't need to deal with the current slot.
173 if (key === null || key === _DELETED_KEY) {
174 continue;
175 }
176 V value = oldValues[i];
177 // Insert the {key, value} pair in their new slot.
178 int newIndex = _probeForAdding(key);
179 _keys[newIndex] = key;
180 _values[newIndex] = value;
181 }
182 _numberOfDeleted = 0;
183 }
184
185 void clear() {
186 _numberOfEntries = 0;
187 _numberOfDeleted = 0;
188 int length = _keys.length;
189 for (int i = 0; i < length; i++) {
190 _keys[i] = null;
191 _values[i] = null;
192 }
193 }
194
195 void operator []=(K key, V value) {
196 _ensureCapacity();
197 int index = _probeForAdding(key);
198 if ((_keys[index] === null) || (_keys[index] === _DELETED_KEY)) {
199 _numberOfEntries++;
200 }
201 _keys[index] = key;
202 _values[index] = value;
203 }
204
205 V operator [](K key) {
206 int index = _probeForLookup(key);
207 if (index < 0) return null;
208 return _values[index];
209 }
210
211 V putIfAbsent(K key, V ifAbsent()) {
212 int index = _probeForLookup(key);
213 if (index >= 0) return _values[index];
214
215 V value = ifAbsent();
216 this[key] = value;
217 return value;
218 }
219
220 V remove(K key) {
221 int index = _probeForLookup(key);
222 if (index >= 0) {
223 _numberOfEntries--;
224 V value = _values[index];
225 _values[index] = null;
226 // Set the key to the sentinel to not break the probing chain.
227 _keys[index] = _DELETED_KEY;
228 _numberOfDeleted++;
229 return value;
230 }
231 return null;
232 }
233
234 bool get isEmpty {
235 return _numberOfEntries == 0;
236 }
237
238 int get length {
239 return _numberOfEntries;
240 }
241
242 void forEach(void f(K key, V value)) {
243 int length = _keys.length;
244 for (int i = 0; i < length; i++) {
245 var key = _keys[i];
246 if ((key !== null) && (key !== _DELETED_KEY)) {
247 f(key, _values[i]);
248 }
249 }
250 }
251
252
253 Collection<K> get keys {
254 List<K> list = new List<K>(length);
255 int i = 0;
256 forEach(void _(K key, V value) {
257 list[i++] = key;
258 });
259 return list;
260 }
261
262 Collection<V> get values {
263 List<V> list = new List<V>(length);
264 int i = 0;
265 forEach(void _(K key, V value) {
266 list[i++] = value;
267 });
268 return list;
269 }
270
271 bool containsKey(K key) {
272 return (_probeForLookup(key) != -1);
273 }
274
275 bool containsValue(V value) {
276 int length = _values.length;
277 for (int i = 0; i < length; i++) {
278 var key = _keys[i];
279 if ((key !== null) && (key !== _DELETED_KEY)) {
280 if (_values[i] == value) return true;
281 }
282 }
283 return false;
284 }
285
286 String toString() {
287 return Maps.mapToString(this);
288 }
289 }
290
291 class IdentitySet<E > implements HashSet<E> {
292
293 IdentitySet() {
294 _backingMap = new IdentityMap<E, E>();
295 }
296
297 factory IdentitySet.from(Iterable<E> other) {
298 Set<E> set = new IdentitySet<E>();
299 for (final e in other) {
300 set.add(e);
301 }
302 return set;
303 }
304
305 void clear() {
306 _backingMap.clear();
307 }
308
309 void add(E value) {
310 _backingMap[value] = value;
311 }
312
313 bool contains(E value) {
314 return _backingMap.containsKey(value);
315 }
316
317 bool remove(E value) {
318 if (!_backingMap.containsKey(value)) return false;
319 _backingMap.remove(value);
320 return true;
321 }
322
323 void addAll(Collection<E> collection) {
324 collection.forEach(void _(E value) {
325 add(value);
326 });
327 }
328
329 Set<E> intersection(Collection<E> collection) {
330 Set<E> result = new Set<E>();
331 collection.forEach(void _(E value) {
332 if (contains(value)) result.add(value);
333 });
334 return result;
335 }
336
337 bool isSubsetOf(Collection<E> other) {
338 return new Set<E>.from(other).containsAll(this);
339 }
340
341 void removeAll(Collection<E> collection) {
342 collection.forEach(void _(E value) {
343 remove(value);
344 });
345 }
346
347 bool containsAll(Collection<E> collection) {
348 return collection.every(bool _(E value) {
349 return contains(value);
350 });
351 }
352
353 void forEach(void f(E element)) {
354 _backingMap.forEach(void _(E key, E value) {
355 f(key);
356 });
357 }
358
359 Set map(f(E element)) {
360 Set result = new Set();
361 _backingMap.forEach(void _(E key, E value) {
362 result.add(f(key));
363 });
364 return result;
365 }
366
367 dynamic reduce(dynamic initialValue,
368 dynamic combine(dynamic previousValue, E element)) {
369 return Collections.reduce(this, initialValue, combine);
370 }
371
372 Set<E> filter(bool f(E element)) {
373 Set<E> result = new Set<E>();
374 _backingMap.forEach(void _(E key, E value) {
375 if (f(key)) result.add(key);
376 });
377 return result;
378 }
379
380 bool every(bool f(E element)) {
381 Collection<E> keys = _backingMap.keys;
382 return keys.every(f);
383 }
384
385 bool some(bool f(E element)) {
386 Collection<E> keys = _backingMap.keys;
387 return keys.some(f);
388 }
389
390 bool get isEmpty {
391 return _backingMap.isEmpty;
392 }
393
394 int get length {
395 return _backingMap.length;
396 }
397
398 Iterator<E> iterator() {
399 return new HashSetIterator<E>(this);
400 }
401
402 String toString() {
403 return Collections.collectionToString(this);
404 }
405
406 // The map backing this set. The associations in this map are all
407 // of the form element -> element. If a value is not in the map,
408 // then it is not in the set.
409 IdentityMap<E, E> _backingMap;
410 }
411
412 class HashSetIterator<E> implements Iterator<E> {
413
414 // TODO(4504458): Replace set_ with set.
415 HashSetIterator(IdentitySet<E> set_)
416 : _nextValidIndex = -1,
417 _entries = set_._backingMap._keys {
418 _advance();
419 }
420
421 bool get hasNext {
422 if (_nextValidIndex >= _entries.length) return false;
423 if (_entries[_nextValidIndex] === IdentityMap._DELETED_KEY) {
424 // This happens in case the set was modified in the meantime.
425 // A modification on the set may make this iterator misbehave,
426 // but we should never return the sentinel.
427 _advance();
428 }
429 return _nextValidIndex < _entries.length;
430 }
431
432 E next() {
433 if (!hasNext) {
434 throw new StateError("No more elements");
435 }
436 E res = _entries[_nextValidIndex];
437 _advance();
438 return res;
439 }
440
441 void _advance() {
442 int length = _entries.length;
443 var entry;
444 final deletedKey = IdentityMap._DELETED_KEY;
445 do {
446 if (++_nextValidIndex >= length) break;
447 entry = _entries[_nextValidIndex];
448 } while ((entry === null) || (entry === deletedKey));
449 }
450
451 // The entries in the set. May contain null or the sentinel value.
452 List<E> _entries;
453
454 // The next valid index in [_entries] or the length of [entries_].
455 // If it is the length of [_entries], calling [hasNext] on the
456 // iterator will return false.
457 int _nextValidIndex;
458 }
459
460 /**
461 * A singleton sentinel used to represent when a key is deleted from the map.
462 * We can't use [: const Object() :] as a sentinel because it would end up
463 * canonicalized and then we cannot distinguish the deleted key from the
464 * canonicalized [: Object() :].
465 */
466 class _DeletedKeySentinel {
467 const _DeletedKeySentinel();
468 }
469
470 /************************************************************************/
471 /** Code copied from Maps.dart and Collection.dart */
472
473
474 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
475 // for details. All rights reserved. Use of this source code is governed by a
476 // BSD-style license that can be found in the LICENSE file.
477
478 /*
479 * Helper class which implements complex [Map] operations
480 * in term of basic ones ([Map.getKeys], [Map.operator []],
481 * [Map.operator []=] and [Map.remove].) Not all methods are
482 * necessary to implement each particular operation.
483 */
484 class Maps {
485 static bool containsValue(Map map, value) {
486 for (final v in map.values) {
487 if (value == v) {
488 return true;
489 }
490 }
491 return false;
492 }
493
494 static bool containsKey(Map map, key) {
495 for (final k in map.keys) {
496 /// ################# Changed to be identity (alanknight)
497 if (key === k) {
498 return true;
499 }
500 }
501 return false;
502 }
503
504 static putIfAbsent(Map map, key, ifAbsent()) {
505 if (map.containsKey(key)) {
506 return map[key];
507 }
508 final v = ifAbsent();
509 map[key] = v;
510 return v;
511 }
512
513 static clear(Map map) {
514 for (final k in map.keys) {
515 map.remove(k);
516 }
517 }
518
519 static forEach(Map map, void f(key, value)) {
520 for (final k in map.keys) {
521 f(k, map[k]);
522 }
523 }
524
525 static Collection getValues(Map map) {
526 final result = [];
527 for (final k in map.keys) {
528 result.add(map[k]);
529 }
530 return result;
531 }
532
533 static int length(Map map) => map.keys.length;
534
535 static bool isEmpty(Map map) => length(map) == 0;
536
537 /**
538 * Returns a string representing the specified map. The returned string
539 * looks like this: [:'{key0: value0, key1: value1, ... keyN: valueN}':].
540 * The value returned by its [toString] method is used to represent each
541 * key or value.
542 *
543 * If the map collection contains a reference to itself, either
544 * directly as a key or value, or indirectly through other collections
545 * or maps, the contained reference is rendered as [:'{...}':]. This
546 * prevents the infinite regress that would otherwise occur. So, for example,
547 * calling this method on a map whose sole entry maps the string key 'me'
548 * to a reference to the map would return [:'{me: {...}}':].
549 *
550 * A typical implementation of a map's [toString] method will
551 * simply return the results of this method applied to the collection.
552 */
553 static String mapToString(Map m) {
554 var result = new StringBuffer();
555 _emitMap(m, result, new List());
556 return result.toString();
557 }
558
559 /**
560 * Appends a string representing the specified map to the specified
561 * string buffer. The string is formatted as per [mapToString].
562 * The [:visiting:] list contains references to all of the enclosing
563 * collections and maps (which are currently in the process of being
564 * emitted into [:result:]). The [:visiting:] parameter allows this method
565 * to generate a [:'[...]':] or [:'{...}':] where required. In other words,
566 * it allows this method and [_emitCollection] to identify recursive maps
567 * and collections.
568 */
569 static void _emitMap(Map m, StringBuffer result, List visiting) {
570 visiting.add(m);
571 result.add('{');
572
573 bool first = true;
574 m.forEach((k, v) {
575 if (!first) {
576 result.add(', ');
577 }
578 first = false;
579 Collections._emitObject(k, result, visiting);
580 result.add(': ');
581 Collections._emitObject(v, result, visiting);
582 });
583
584 result.add('}');
585 visiting.removeLast();
586 }
587 }
588
589 /***********************************************************************
590 * Collections.dart
591 */
592 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
593 // for details. All rights reserved. Use of this source code is governed by a
594 // BSD-style license that can be found in the LICENSE file.
595
596 /**
597 * The [Collections] class implements static methods useful when
598 * writing a class that implements [Collection] and the [iterator]
599 * method.
600 */
601 class Collections {
602 static void forEach(Iterable iterable, void f(o)) {
603 for (final e in iterable) {
604 f(e);
605 }
606 }
607
608 static bool some(Iterable iterable, bool f(o)) {
609 for (final e in iterable) {
610 if (f(e)) return true;
611 }
612 return false;
613 }
614
615 static bool every(Iterable iterable, bool f(o)) {
616 for (final e in iterable) {
617 if (!f(e)) return false;
618 }
619 return true;
620 }
621
622 static List map(Iterable source, List destination, f(o)) {
623 for (final e in source) {
624 destination.add(f(e));
625 }
626 return destination;
627 }
628
629 static dynamic reduce(Iterable iterable,
630 dynamic initialValue,
631 dynamic combine(dynamic previousValue, element)) {
632 for (final element in iterable) {
633 initialValue = combine(initialValue, element);
634 }
635 return initialValue;
636 }
637
638 static List filter(Iterable source, List destination, bool f(o)) {
639 for (final e in source) {
640 if (f(e)) destination.add(e);
641 }
642 return destination;
643 }
644
645 static bool isEmpty(Iterable iterable) {
646 return !iterable.iterator().hasNext;
647 }
648
649 // TODO(jjb): visiting list should be an identityHashSet when it exists
650
651 /**
652 * Returns a string representing the specified collection. If the
653 * collection is a [List], the returned string looks like this:
654 * [:'[element0, element1, ... elementN]':]. The value returned by its
655 * [toString] method is used to represent each element. If the specified
656 * collection is not a list, the returned string looks like this:
657 * [:{element0, element1, ... elementN}:]. In other words, the strings
658 * returned for lists are surrounded by square brackets, while the strings
659 * returned for other collections are surrounded by curly braces.
660 *
661 * If the specified collection contains a reference to itself, either
662 * directly or indirectly through other collections or maps, the contained
663 * reference is rendered as [:'[...]':] if it is a list, or [:'{...}':] if
664 * it is not. This prevents the infinite regress that would otherwise occur.
665 * So, for example, calling this method on a list whose sole element is a
666 * reference to itself would return [:'[[...]]':].
667 *
668 * A typical implementation of a collection's [toString] method will
669 * simply return the results of this method applied to the collection.
670 */
671 static String collectionToString(Collection c) {
672 var result = new StringBuffer();
673 _emitCollection(c, result, new List());
674 return result.toString();
675 }
676
677 /**
678 * Appends a string representing the specified collection to the specified
679 * string buffer. The string is formatted as per [collectionToString].
680 * The [:visiting:] list contains references to all of the enclosing
681 * collections and maps (which are currently in the process of being
682 * emitted into [:result:]). The [:visiting:] parameter allows this method to
683 * generate a [:'[...]':] or [:'{...}':] where required. In other words,
684 * it allows this method and [_emitMap] to identify recursive collections
685 * and maps.
686 */
687 static void _emitCollection(Collection c, StringBuffer result, List visiting) {
688 visiting.add(c);
689 bool isList = c is List;
690 result.add(isList ? '[' : '{');
691
692 bool first = true;
693 for (var e in c) {
694 if (!first) {
695 result.add(', ');
696 }
697 first = false;
698 _emitObject(e, result, visiting);
699 }
700
701 result.add(isList ? ']' : '}');
702 visiting.removeLast();
703 }
704
705 /**
706 * Appends a string representing the specified object to the specified
707 * string buffer. If the object is a [Collection] or [Map], it is formatted
708 * as per [collectionToString] or [mapToString]; otherwise, it is formatted
709 * by invoking its own [toString] method.
710 *
711 * The [:visiting:] list contains references to all of the enclosing
712 * collections and maps (which are currently in the process of being
713 * emitted into [:result:]). The [:visiting:] parameter allows this method
714 * to generate a [:'[...]':] or [:'{...}':] where required. In other words,
715 * it allows this method and [_emitCollection] to identify recursive maps
716 * and collections.
717 */
718 static void _emitObject(Object o, StringBuffer result, List visiting) {
719 if (o is Collection) {
720 if (_containsRef(visiting, o)) {
721 result.add(o is List ? '[...]' : '{...}');
722 } else {
723 _emitCollection(o, result, visiting);
724 }
725 } else if (o is Map) {
726 if (_containsRef(visiting, o)) {
727 result.add('{...}');
728 } else {
729 Maps._emitMap(o, result, visiting);
730 }
731 } else { // o is neither a collection nor a map
732 result.add(o);
733 }
734 }
735
736 /**
737 * Returns true if the specified collection contains the specified object
738 * reference.
739 */
740 static _containsRef(Collection c, Object ref) {
741 for (var e in c) {
742 if (e === ref) return true;
743 }
744 return false;
745 }
746 }
747
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698