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

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 // TODO(alanknight): Replace with proper identity collection. Issue 4161
6 library identity_set;
7
8 import 'dart:collection';
9
10 // Hash map implementation with open addressing and quadratic probing.
11 class IdentityMap<K, V> implements HashMap<K, V> {
12
13 // The [_keys] list contains the keys inserted in the map.
14 // The [_keys] list must be a raw list because it
15 // will contain both elements of type K, and the [_DELETED_KEY] of type
16 // [_DeletedKeySentinel].
17 // The alternative of declaring the [_keys] list as of type Object
18 // does not work, because the HashSetIterator constructor would fail:
19 // HashSetIterator(HashSet<E> set)
20 // : _nextValidIndex = -1,
21 // _entries = set_._backingMap._keys {
22 // _advance();
23 // }
24 // With K being type int, for example, it would fail because
25 // List<Object> is not assignable to type List<int> of entries.
26 List _keys;
27
28 // The values inserted in the map. For a filled entry index in this
29 // list, there is always the corresponding key in the [keys_] list
30 // at the same entry index.
31 List<V> _values;
32
33 // The load limit is the number of entries we allow until we double
34 // the size of the lists.
35 int _loadLimit;
36
37 // The current number of entries in the map. Will never be greater
38 // than [_loadLimit].
39 int _numberOfEntries;
40
41 // The current number of deleted entries in the map.
42 int _numberOfDeleted;
43
44 // The sentinel when a key is deleted from the map.
45 static const _DeletedKeySentinel _DELETED_KEY = const _DeletedKeySentinel();
46
47 // The initial capacity of a hash map.
48 static const int _INITIAL_CAPACITY = 8; // must be power of 2
49
50 IdentityMap() {
51 _numberOfEntries = 0;
52 _numberOfDeleted = 0;
53 _loadLimit = _computeLoadLimit(_INITIAL_CAPACITY);
54 _keys = new List(_INITIAL_CAPACITY);
55 _values = new List<V>(_INITIAL_CAPACITY);
56 }
57
58 factory IdentityMap.from(Map<K, V> other) {
59 Map<K, V> result = new IdentityMap<K, V>();
60 other.forEach((K key, V value) { result[key] = value; });
61 return result;
62 }
63
64 static int _computeLoadLimit(int capacity) {
65 return (capacity * 3) ~/ 4;
66 }
67
68 static int _firstProbe(int hashCode, int length) {
69 return hashCode & (length - 1);
70 }
71
72 static int _nextProbe(int currentProbe, int numberOfProbes, int length) {
73 return (currentProbe + numberOfProbes) & (length - 1);
74 }
75
76 int _probeForAdding(K key) {
77 if (key == null) throw const NullPointerException();
78 int hash = _firstProbe(key.hashCode, _keys.length);
79 int numberOfProbes = 1;
80 int initialHash = hash;
81 // insertionIndex points to a slot where a key was deleted.
82 int insertionIndex = -1;
83 while (true) {
84 // [existingKey] can be either of type [K] or [_DeletedKeySentinel].
85 Object existingKey = _keys[hash];
86 if (existingKey == null) {
87 // We are sure the key is not already in the set.
88 // If the current slot is empty and we didn't find any
89 // insertion slot before, return this slot.
90 if (insertionIndex < 0) return hash;
91 // If we did find an insertion slot before, return it.
92 return insertionIndex;
93 } else if (identical(existingKey, key)) {
94 // The key is already in the map. Return its slot.
95 return hash;
96 } else if ((insertionIndex < 0) &&
97 (identical(existingKey, _DELETED_KEY))) {
98 // The slot contains a deleted element. Because previous calls to this
99 // method may not have had this slot deleted, we must continue iterate
100 // to find if there is a slot with the given key.
101 insertionIndex = hash;
102 }
103
104 // We did not find an insertion slot. Look at the next one.
105 hash = _nextProbe(hash, numberOfProbes++, _keys.length);
106 // _ensureCapacity has guaranteed the following cannot happen.
107 // assert(hash != initialHash);
108 }
109 }
110
111 int _probeForLookup(K key) {
112 if (key == null) throw const NullPointerException();
113 int hash = _firstProbe(key.hashCode, _keys.length);
114 int numberOfProbes = 1;
115 int initialHash = hash;
116 while (true) {
117 // [existingKey] can be either of type [K] or [_DeletedKeySentinel].
118 Object existingKey = _keys[hash];
119 // If the slot does not contain anything (in particular, it does not
120 // contain a deleted key), we know the key is not in the map.
121 if (existingKey == null) return -1;
122 // The key is in the map, return its index.
123 if (identical(existingKey, key)) return hash;
124 // Go to the next probe.
125 hash = _nextProbe(hash, numberOfProbes++, _keys.length);
126 // _ensureCapacity has guaranteed the following cannot happen.
127 // assert(hash != initialHash);
128 }
129 }
130
131 void _ensureCapacity() {
132 int newNumberOfEntries = _numberOfEntries + 1;
133 // Test if adding an element will reach the load limit.
134 if (newNumberOfEntries >= _loadLimit) {
135 _grow(_keys.length * 2);
136 return;
137 }
138
139 // Make sure that we don't have poor performance when a map
140 // contains lots of deleted entries: we _grow if
141 // there are more deleted entried than free entries.
142 int capacity = _keys.length;
143 int numberOfFreeOrDeleted = capacity - newNumberOfEntries;
144 int numberOfFree = numberOfFreeOrDeleted - _numberOfDeleted;
145 // assert(numberOfFree > 0);
146 if (_numberOfDeleted > numberOfFree) {
147 _grow(_keys.length);
148 }
149 }
150
151 static bool _isPowerOfTwo(int x) {
152 return ((x & (x - 1)) == 0);
153 }
154
155 void _grow(int newCapacity) {
156 assert(_isPowerOfTwo(newCapacity));
157 int capacity = _keys.length;
158 _loadLimit = _computeLoadLimit(newCapacity);
159 List oldKeys = _keys;
160 List<V> oldValues = _values;
161 _keys = new List(newCapacity);
162 _values = new List<V>(newCapacity);
163 for (int i = 0; i < capacity; i++) {
164 // [key] can be either of type [K] or [_DeletedKeySentinel].
165 Object key = oldKeys[i];
166 // If there is no key, we don't need to deal with the current slot.
167 if (key == null || identical(key, _DELETED_KEY)) {
168 continue;
169 }
170 V value = oldValues[i];
171 // Insert the {key, value} pair in their new slot.
172 int newIndex = _probeForAdding(key);
173 _keys[newIndex] = key;
174 _values[newIndex] = value;
175 }
176 _numberOfDeleted = 0;
177 }
178
179 void clear() {
180 _numberOfEntries = 0;
181 _numberOfDeleted = 0;
182 int length = _keys.length;
183 for (int i = 0; i < length; i++) {
184 _keys[i] = null;
185 _values[i] = null;
186 }
187 }
188
189 void operator []=(K key, V value) {
190 _ensureCapacity();
191 int index = _probeForAdding(key);
192 if ((_keys[index] == null) || (identical(_keys[index], _DELETED_KEY))) {
193 _numberOfEntries++;
194 }
195 _keys[index] = key;
196 _values[index] = value;
197 }
198
199 V operator [](K key) {
200 int index = _probeForLookup(key);
201 if (index < 0) return null;
202 return _values[index];
203 }
204
205 V putIfAbsent(K key, V ifAbsent()) {
206 int index = _probeForLookup(key);
207 if (index >= 0) return _values[index];
208
209 V value = ifAbsent();
210 this[key] = value;
211 return value;
212 }
213
214 V remove(K key) {
215 int index = _probeForLookup(key);
216 if (index >= 0) {
217 _numberOfEntries--;
218 V value = _values[index];
219 _values[index] = null;
220 // Set the key to the sentinel to not break the probing chain.
221 _keys[index] = _DELETED_KEY;
222 _numberOfDeleted++;
223 return value;
224 }
225 return null;
226 }
227
228 bool get isEmpty {
229 return _numberOfEntries == 0;
230 }
231
232 int get length {
233 return _numberOfEntries;
234 }
235
236 void forEach(void f(K key, V value)) {
237 int length = _keys.length;
238 for (int i = 0; i < length; i++) {
239 var key = _keys[i];
240 if ((key != null) && (!identical(key, _DELETED_KEY))) {
241 f(key, _values[i]);
242 }
243 }
244 }
245
246
247 Collection<K> get keys {
248 List<K> list = new List<K>(length);
249 int i = 0;
250 forEach((K key, V value) {
251 list[i++] = key;
252 });
253 return list;
254 }
255
256 Collection<V> get values {
257 List<V> list = new List<V>(length);
258 int i = 0;
259 forEach((K key, V value) {
260 list[i++] = value;
261 });
262 return list;
263 }
264
265 bool containsKey(K key) {
266 return (_probeForLookup(key) != -1);
267 }
268
269 bool containsValue(V value) {
270 int length = _values.length;
271 for (int i = 0; i < length; i++) {
272 var key = _keys[i];
273 if ((key != null) && (!identical(key, _DELETED_KEY))) {
274 if (_values[i] == value) return true;
275 }
276 }
277 return false;
278 }
279
280 String toString() {
281 return Maps.mapToString(this);
282 }
283 }
284
285
286 /**
287 * A singleton sentinel used to represent when a key is deleted from the map.
288 * We can't use [: const Object() :] as a sentinel because it would end up
289 * canonicalized and then we cannot distinguish the deleted key from the
290 * canonicalized [: Object() :].
291 */
292 class _DeletedKeySentinel {
293 const _DeletedKeySentinel();
294 }
295
296
297 /**
298 * This class represents a pair of two objects, used by LinkedHashMap
299 * to store a {key, value} in a list.
300 */
301 class _KeyValuePair<K, V> {
302 _KeyValuePair(this.key, this.value) {}
303
304 final K key;
305 V value;
306 }
307
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698