OLD | NEW |
(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 library web_ui.observe.map; |
| 6 |
| 7 import 'dart:collection'; |
| 8 import 'package:web_ui/observe.dart'; |
| 9 |
| 10 // TODO(jmesserly): also need versions of SplayTreeMap, LinkedHashMap. |
| 11 |
| 12 /** |
| 13 * Represents an observable map of model values. If any items are added, |
| 14 * removed, or replaced, then observers that are registered with |
| 15 * [observe] will be notified. |
| 16 */ |
| 17 abstract class ObservableMap<K, V> implements Observable, Map<K, V> { |
| 18 /** |
| 19 * Creates a map with the default implementation. |
| 20 */ |
| 21 factory ObservableMap() => new ObservableHashMap<K, V>(); |
| 22 |
| 23 /** |
| 24 * Creates a [Map] that contains all key value pairs of [other]. |
| 25 */ |
| 26 factory ObservableMap.from(Map<K, V> other) => |
| 27 new ObservableHashMap<K, V>.from(other); |
| 28 } |
| 29 |
| 30 |
| 31 /** |
| 32 * Hash map version of the [ObservableMap] interface. A [ObservableHashMap] does |
| 33 * not provide any guarantees on the order of keys and values in [keys] |
| 34 * and [values]. |
| 35 */ |
| 36 class ObservableHashMap<K, V> extends Observable |
| 37 implements ObservableMap<K, V>, HashMap<K, V> { |
| 38 |
| 39 // The [_keys] list contains the keys inserted in the map. |
| 40 // The [_keys] list must be a raw list because it |
| 41 // will contain both elements of type K, and the [_DELETED_KEY] of type |
| 42 // [_DeletedKeySentinel]. |
| 43 // The alternative of declaring the [_keys] list as of type Object |
| 44 // does not work, because the HashSetIterator constructor would fail: |
| 45 // HashSetIterator(HashSet<E> set) |
| 46 // : _nextValidIndex = -1, |
| 47 // _entries = set_._backingMap._keys { |
| 48 // _advance(); |
| 49 // } |
| 50 // With K being type int, for example, it would fail because |
| 51 // List<Object> is not assignable to type List<int> of entries. |
| 52 List _keys; |
| 53 |
| 54 // The values inserted in the map. For a filled entry index in this |
| 55 // list, there is always the corresponding key in the [keys_] list |
| 56 // at the same entry index. |
| 57 List<V> _values; |
| 58 |
| 59 // The load limit is the number of entries we allow until we double |
| 60 // the size of the lists. |
| 61 int _loadLimit; |
| 62 |
| 63 // The current number of entries in the map. Will never be greater |
| 64 // than [_loadLimit]. |
| 65 int _numberOfEntriesRaw; |
| 66 |
| 67 // The current number of deleted entries in the map. |
| 68 int _numberOfDeleted; |
| 69 |
| 70 // The sentinel when a key is deleted from the map. |
| 71 static const _DeletedKeySentinel _DELETED_KEY = const _DeletedKeySentinel(); |
| 72 |
| 73 // The initial capacity of a hash map. |
| 74 static const int _INITIAL_CAPACITY = 8; // must be power of 2 |
| 75 |
| 76 ObservableHashMap() { |
| 77 _numberOfEntries = 0; |
| 78 _numberOfDeleted = 0; |
| 79 _loadLimit = _computeLoadLimit(_INITIAL_CAPACITY); |
| 80 _keys = new List.fixedLength(_INITIAL_CAPACITY); |
| 81 _values = new List<V>.fixedLength(_INITIAL_CAPACITY); |
| 82 } |
| 83 |
| 84 factory ObservableHashMap.from(Map<K, V> other) { |
| 85 Map<K, V> result = new ObservableHashMap<K, V>(); |
| 86 other.forEach((K key, V value) { result[key] = value; }); |
| 87 return result; |
| 88 } |
| 89 |
| 90 int get _numberOfEntries { |
| 91 if (observeReads) notifyRead(ChangeRecord.FIELD, 'length'); |
| 92 return _numberOfEntriesRaw; |
| 93 } |
| 94 |
| 95 void set _numberOfEntries(int value) { |
| 96 if (hasObservers) { |
| 97 notifyChange(ChangeRecord.FIELD, 'length', _numberOfEntriesRaw, value); |
| 98 } |
| 99 _numberOfEntriesRaw = value; |
| 100 } |
| 101 |
| 102 static int _computeLoadLimit(int capacity) { |
| 103 return (capacity * 3) ~/ 4; |
| 104 } |
| 105 |
| 106 static int _firstProbe(int hashCode, int length) { |
| 107 return hashCode & (length - 1); |
| 108 } |
| 109 |
| 110 static int _nextProbe(int currentProbe, int numberOfProbes, int length) { |
| 111 return (currentProbe + numberOfProbes) & (length - 1); |
| 112 } |
| 113 |
| 114 int _probeForAdding(K key) { |
| 115 if (key == null) throw new ArgumentError(null); |
| 116 int hash = _firstProbe(key.hashCode, _keys.length); |
| 117 int numberOfProbes = 1; |
| 118 int initialHash = hash; |
| 119 // insertionIndex points to a slot where a key was deleted. |
| 120 int insertionIndex = -1; |
| 121 while (true) { |
| 122 // [existingKey] can be either of type [K] or [_DeletedKeySentinel]. |
| 123 Object existingKey = _keys[hash]; |
| 124 if (existingKey == null) { |
| 125 // We are sure the key is not already in the set. |
| 126 // If the current slot is empty and we didn't find any |
| 127 // insertion slot before, return this slot. |
| 128 if (insertionIndex < 0) return hash; |
| 129 // If we did find an insertion slot before, return it. |
| 130 return insertionIndex; |
| 131 } else if (existingKey == key) { |
| 132 // The key is already in the map. Return its slot. |
| 133 return hash; |
| 134 } else if ((insertionIndex < 0) && |
| 135 (identical(existingKey, _DELETED_KEY))) { |
| 136 // The slot contains a deleted element. Because previous calls to this |
| 137 // method may not have had this slot deleted, we must continue iterate |
| 138 // to find if there is a slot with the given key. |
| 139 insertionIndex = hash; |
| 140 } |
| 141 |
| 142 // We did not find an insertion slot. Look at the next one. |
| 143 hash = _nextProbe(hash, numberOfProbes++, _keys.length); |
| 144 // _ensureCapacity has guaranteed the following cannot happen. |
| 145 // assert(hash != initialHash); |
| 146 } |
| 147 } |
| 148 |
| 149 int _probeForLookup(K key) { |
| 150 if (key == null) throw new ArgumentError(null); |
| 151 if (observeReads) notifyRead(ChangeRecord.INDEX, key); |
| 152 int hash = _firstProbe(key.hashCode, _keys.length); |
| 153 int numberOfProbes = 1; |
| 154 int initialHash = hash; |
| 155 while (true) { |
| 156 // [existingKey] can be either of type [K] or [_DeletedKeySentinel]. |
| 157 Object existingKey = _keys[hash]; |
| 158 // If the slot does not contain anything (in particular, it does not |
| 159 // contain a deleted key), we know the key is not in the map. |
| 160 if (existingKey == null) return -1; |
| 161 // The key is in the map, return its index. |
| 162 if (existingKey == key) return hash; |
| 163 // Go to the next probe. |
| 164 hash = _nextProbe(hash, numberOfProbes++, _keys.length); |
| 165 // _ensureCapacity has guaranteed the following cannot happen. |
| 166 // assert(hash != initialHash); |
| 167 } |
| 168 } |
| 169 |
| 170 void _ensureCapacity() { |
| 171 int newNumberOfEntries = _numberOfEntries + 1; |
| 172 // Test if adding an element will reach the load limit. |
| 173 if (newNumberOfEntries >= _loadLimit) { |
| 174 _grow(_keys.length * 2); |
| 175 return; |
| 176 } |
| 177 |
| 178 // Make sure that we don't have poor performance when a map |
| 179 // contains lots of deleted entries: we _grow if |
| 180 // there are more deleted entried than free entries. |
| 181 int capacity = _keys.length; |
| 182 int numberOfFreeOrDeleted = capacity - newNumberOfEntries; |
| 183 int numberOfFree = numberOfFreeOrDeleted - _numberOfDeleted; |
| 184 // assert(numberOfFree > 0); |
| 185 if (_numberOfDeleted > numberOfFree) { |
| 186 _grow(_keys.length); |
| 187 } |
| 188 } |
| 189 |
| 190 static bool _isPowerOfTwo(int x) { |
| 191 return ((x & (x - 1)) == 0); |
| 192 } |
| 193 |
| 194 void _grow(int newCapacity) { |
| 195 assert(_isPowerOfTwo(newCapacity)); |
| 196 int capacity = _keys.length; |
| 197 _loadLimit = _computeLoadLimit(newCapacity); |
| 198 List oldKeys = _keys; |
| 199 List<V> oldValues = _values; |
| 200 _keys = new List.fixedLength(newCapacity); |
| 201 _values = new List<V>.fixedLength(newCapacity); |
| 202 for (int i = 0; i < capacity; i++) { |
| 203 // [key] can be either of type [K] or [_DeletedKeySentinel]. |
| 204 Object key = oldKeys[i]; |
| 205 // If there is no key, we don't need to deal with the current slot. |
| 206 if (key == null || identical(key, _DELETED_KEY)) { |
| 207 continue; |
| 208 } |
| 209 V value = oldValues[i]; |
| 210 // Insert the {key, value} pair in their new slot. |
| 211 int newIndex = _probeForAdding(key); |
| 212 _keys[newIndex] = key; |
| 213 _values[newIndex] = value; |
| 214 } |
| 215 _numberOfDeleted = 0; |
| 216 } |
| 217 |
| 218 void clear() { |
| 219 _numberOfEntries = 0; |
| 220 _numberOfDeleted = 0; |
| 221 int length = _keys.length; |
| 222 for (int i = 0; i < length; i++) { |
| 223 var key = _keys[i]; |
| 224 if (hasObservers && key != null && !identical(key, _DELETED_KEY)) { |
| 225 notifyChange(ChangeRecord.REMOVE, key, _values[i], null); |
| 226 } |
| 227 _keys[i] = null; |
| 228 _values[i] = null; |
| 229 } |
| 230 } |
| 231 |
| 232 void operator []=(K key, V value) { |
| 233 _ensureCapacity(); |
| 234 int index = _probeForAdding(key); |
| 235 if ((_keys[index] == null) || (identical(_keys[index], _DELETED_KEY))) { |
| 236 _numberOfEntries++; |
| 237 if (hasObservers) notifyChange(ChangeRecord.INSERT, key, null, value); |
| 238 } else if (hasObservers) { |
| 239 notifyChange(ChangeRecord.INDEX, key, _values[index], value); |
| 240 } |
| 241 _keys[index] = key; |
| 242 _values[index] = value; |
| 243 } |
| 244 |
| 245 V operator [](K key) { |
| 246 int index = _probeForLookup(key); |
| 247 if (index < 0) return null; |
| 248 return _values[index]; |
| 249 } |
| 250 |
| 251 V putIfAbsent(K key, V ifAbsent()) { |
| 252 int index = _probeForLookup(key); |
| 253 if (index >= 0) return _values[index]; |
| 254 |
| 255 V value = ifAbsent(); |
| 256 this[key] = value; |
| 257 return value; |
| 258 } |
| 259 |
| 260 V remove(K key) { |
| 261 int index = _probeForLookup(key); |
| 262 if (index >= 0) { |
| 263 _numberOfEntries--; |
| 264 V value = _values[index]; |
| 265 _values[index] = null; |
| 266 if (hasObservers) notifyChange(ChangeRecord.REMOVE, key, value, null); |
| 267 // Set the key to the sentinel to not break the probing chain. |
| 268 _keys[index] = _DELETED_KEY; |
| 269 _numberOfDeleted++; |
| 270 return value; |
| 271 } |
| 272 return null; |
| 273 } |
| 274 |
| 275 bool get isEmpty { |
| 276 return _numberOfEntries == 0; |
| 277 } |
| 278 |
| 279 int get length { |
| 280 return _numberOfEntries; |
| 281 } |
| 282 |
| 283 void forEach(void f(K key, V value)) { |
| 284 Iterator<int> it = new _HashMapImplIndexIterator(this); |
| 285 while (it.moveNext()) { |
| 286 f(_keys[it.current], _values[it.current]); |
| 287 } |
| 288 } |
| 289 |
| 290 Iterable<K> get keys => new _HashMapImplKeyIterable<K>(this); |
| 291 |
| 292 Iterable<V> get values => new _HashMapImplValueIterable<V>(this); |
| 293 |
| 294 bool containsKey(K key) { |
| 295 return (_probeForLookup(key) != -1); |
| 296 } |
| 297 |
| 298 bool containsValue(V value) => values.contains(value); |
| 299 |
| 300 String toString() => Maps.mapToString(this); |
| 301 } |
| 302 |
| 303 class _HashMapImplKeyIterable<E> extends Iterable<E> { |
| 304 final ObservableHashMap _map; |
| 305 _HashMapImplKeyIterable(this._map); |
| 306 |
| 307 Iterator<E> get iterator => new _HashMapImplKeyIterator<E>(_map); |
| 308 } |
| 309 |
| 310 class _HashMapImplValueIterable<E> extends Iterable<E> { |
| 311 final ObservableHashMap _map; |
| 312 _HashMapImplValueIterable(this._map); |
| 313 |
| 314 Iterator<E> get iterator => new _HashMapImplValueIterator<E>(_map); |
| 315 } |
| 316 |
| 317 abstract class _HashMapImplIterator<E> implements Iterator<E> { |
| 318 final ObservableHashMap _map; |
| 319 int _index = -1; |
| 320 E _current; |
| 321 |
| 322 _HashMapImplIterator(this._map); |
| 323 |
| 324 E _computeCurrentFromIndex(int index, List keys, List values); |
| 325 |
| 326 bool moveNext() { |
| 327 // Conceptually iteration depends on the ObservableMap size |
| 328 if (observeReads) _map.notifyRead(ChangeRecord.FIELD, 'length'); |
| 329 |
| 330 int length = _map._keys.length; |
| 331 int newIndex = _index + 1; |
| 332 while (newIndex < length) { |
| 333 var key = _map._keys[newIndex]; |
| 334 if (key != null && !identical(key, ObservableHashMap._DELETED_KEY)) { |
| 335 if (observeReads) { |
| 336 _map.notifyRead(ChangeRecord.INDEX, key); |
| 337 } |
| 338 _current = _computeCurrentFromIndex(newIndex, _map._keys, _map._values); |
| 339 _index = newIndex; |
| 340 return true; |
| 341 } |
| 342 newIndex++; |
| 343 } |
| 344 _index = length; |
| 345 _current = null; |
| 346 return false; |
| 347 } |
| 348 |
| 349 E get current => _current; |
| 350 } |
| 351 |
| 352 class _HashMapImplKeyIterator<E> extends _HashMapImplIterator<E> { |
| 353 _HashMapImplKeyIterator(ObservableHashMap map) : super(map); |
| 354 |
| 355 E _computeCurrentFromIndex(int index, List keys, List values) { |
| 356 return keys[index]; |
| 357 } |
| 358 } |
| 359 |
| 360 class _HashMapImplValueIterator<E> extends _HashMapImplIterator<E> { |
| 361 _HashMapImplValueIterator(ObservableHashMap map) : super(map); |
| 362 |
| 363 E _computeCurrentFromIndex(int index, List keys, List values) { |
| 364 return values[index]; |
| 365 } |
| 366 } |
| 367 |
| 368 class _HashMapImplIndexIterator extends _HashMapImplIterator<int> { |
| 369 _HashMapImplIndexIterator(ObservableHashMap map) : super(map); |
| 370 |
| 371 int _computeCurrentFromIndex(int index, List keys, List values) { |
| 372 return index; |
| 373 } |
| 374 } |
| 375 |
| 376 /** |
| 377 * A singleton sentinel used to represent when a key is deleted from the map. |
| 378 * We can't use [: const Object() :] as a sentinel because it would end up |
| 379 * canonicalized and then we cannot distinguish the deleted key from the |
| 380 * canonicalized [: Object() :]. |
| 381 */ |
| 382 class _DeletedKeySentinel { |
| 383 const _DeletedKeySentinel(); |
| 384 } |
| 385 |
| 386 |
| 387 /** |
| 388 * This class represents a pair of two objects, used by LinkedHashMap |
| 389 * to store a {key, value} in a list. |
| 390 */ |
| 391 class _KeyValuePair<K, V> { |
| 392 _KeyValuePair(this.key, this.value) {} |
| 393 |
| 394 final K key; |
| 395 V value; |
| 396 } |
OLD | NEW |