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

Side by Side Diff: sdk/lib/_internal/lib/collection_patch.dart

Issue 23494027: Make LinkedHashMap also have a factory constructor and be customizable (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Address comments. Created 7 years, 3 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 | « runtime/lib/collection_patch.dart ('k') | sdk/lib/collection/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
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 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 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 // Patch file for dart:collection classes. 5 // Patch file for dart:collection classes.
6 import 'dart:_foreign_helper' show JS; 6 import 'dart:_foreign_helper' show JS;
7 7
8 patch class HashMap<K, V> { 8 patch class HashMap<K, V> {
9 patch factory HashMap({ bool equals(K key1, K key2), int hashCode(K key) }) { 9 patch factory HashMap({ bool equals(K key1, K key2),
10 if (hashCode == null) { 10 int hashCode(K key),
11 bool isValidKey(potentialKey) }) {
12 if (isValidKey == null) {
13 if (hashCode == null) {
14 if (equals == null) {
15 return new _HashMap<K, V>();
16 }
17 if (identical(identical, equals)) {
18 return new _IdentityHashMap<K, V>();
19 }
20 hashCode = _defaultHashCode;
21 } else if (equals == null) {
22 equals = _defaultEquals;
23 }
24 } else {
25 if (hashCode == null) {
26 hashCode = _defaultHashCode;
27 }
11 if (equals == null) { 28 if (equals == null) {
12 return new _HashMapImpl<K, V>(); 29 equals = _defaultEquals;
13 } 30 }
14 if (identical(identical, equals)) {
15 return new _IdentityHashMap<K, V>();
16 }
17 hashCode = _defaultHashCode;
18 } else if (equals == null) {
19 equals = _defaultEquals;
20 } 31 }
21 return new _CustomHashMap<K, V>(equals, hashCode); 32 return new _CustomHashMap<K, V>(equals, hashCode, isValidKey);
22 } 33 }
23 } 34 }
24 35
25 class _HashMapImpl<K, V> implements HashMap<K, V> { 36 class _HashMap<K, V> implements HashMap<K, V> {
26 int _length = 0; 37 int _length = 0;
27 38
28 // The hash map contents are divided into three parts: one part for 39 // The hash map contents are divided into three parts: one part for
29 // string keys, one for numeric keys, and one for the rest. String 40 // string keys, one for numeric keys, and one for the rest. String
30 // and numeric keys map directly to their values, but the rest of 41 // and numeric keys map directly to their values, but the rest of
31 // the entries are stored in bucket lists of the form: 42 // the entries are stored in bucket lists of the form:
32 // 43 //
33 // [key-0, value-0, key-1, value-1, ...] 44 // [key-0, value-0, key-1, value-1, ...]
34 // 45 //
35 // where all keys in the same bucket share the same hash code. 46 // where all keys in the same bucket share the same hash code.
36 var _strings; 47 var _strings;
37 var _nums; 48 var _nums;
38 var _rest; 49 var _rest;
39 50
40 // When iterating over the hash map, it is very convenient to have a 51 // When iterating over the hash map, it is very convenient to have a
41 // list of all the keys. We cache that on the instance and clear the 52 // list of all the keys. We cache that on the instance and clear the
42 // the cache whenever the key set changes. This is also used to 53 // the cache whenever the key set changes. This is also used to
43 // guard against concurrent modifications. 54 // guard against concurrent modifications.
44 List _keys; 55 List _keys;
45 56
46 _HashMapImpl(); 57 _Hash();
47 58
48 int get length => _length; 59 int get length => _length;
49 bool get isEmpty => _length == 0; 60 bool get isEmpty => _length == 0;
50 bool get isNotEmpty => !isEmpty; 61 bool get isNotEmpty => !isEmpty;
51 62
52 Iterable<K> get keys { 63 Iterable<K> get keys {
53 return new HashMapKeyIterable<K>(this); 64 return new HashMapKeyIterable<K>(this);
54 } 65 }
55 66
56 Iterable<V> get values { 67 Iterable<V> get values {
(...skipping 170 matching lines...) Expand 10 before | Expand all | Expand 10 after
227 } 238 }
228 239
229 void _addHashTableEntry(var table, K key, V value) { 240 void _addHashTableEntry(var table, K key, V value) {
230 if (!_hasTableEntry(table, key)) { 241 if (!_hasTableEntry(table, key)) {
231 _length++; 242 _length++;
232 _keys = null; 243 _keys = null;
233 } 244 }
234 _setTableEntry(table, key, value); 245 _setTableEntry(table, key, value);
235 } 246 }
236 247
237 V _removeHashTableEntry(var table, K key) { 248 V _removeHashTableEntry(var table, Object key) {
238 if (table != null && _hasTableEntry(table, key)) { 249 if (table != null && _hasTableEntry(table, key)) {
239 V value = _getTableEntry(table, key); 250 V value = _getTableEntry(table, key);
240 _deleteTableEntry(table, key); 251 _deleteTableEntry(table, key);
241 _length--; 252 _length--;
242 _keys = null; 253 _keys = null;
243 return value; 254 return value;
244 } else { 255 } else {
245 return null; 256 return null;
246 } 257 }
247 } 258 }
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
318 var table = JS('var', 'Object.create(null)'); 329 var table = JS('var', 'Object.create(null)');
319 // Attempt to force the hash table into 'dictionary' mode by 330 // Attempt to force the hash table into 'dictionary' mode by
320 // adding a property to it and deleting it again. 331 // adding a property to it and deleting it again.
321 var temporaryKey = '<non-identifier-key>'; 332 var temporaryKey = '<non-identifier-key>';
322 _setTableEntry(table, temporaryKey, table); 333 _setTableEntry(table, temporaryKey, table);
323 _deleteTableEntry(table, temporaryKey); 334 _deleteTableEntry(table, temporaryKey);
324 return table; 335 return table;
325 } 336 }
326 } 337 }
327 338
328 class _IdentityHashMap<K, V> extends _HashMapImpl<K, V> { 339 class _IdentityHashMap<K, V> extends _HashMap<K, V> {
329 int _findBucketIndex(var bucket, var key) { 340 int _findBucketIndex(var bucket, var key) {
330 if (bucket == null) return -1; 341 if (bucket == null) return -1;
331 int length = JS('int', '#.length', bucket); 342 int length = JS('int', '#.length', bucket);
332 for (int i = 0; i < length; i += 2) { 343 for (int i = 0; i < length; i += 2) {
333 if (identical(JS('var', '#[#]', bucket, i), key)) return i; 344 if (identical(JS('var', '#[#]', bucket, i), key)) return i;
334 } 345 }
335 return -1; 346 return -1;
336 } 347 }
337 } 348 }
338 349
339 class _CustomHashMap<K, V> extends _HashMapImpl<K, V> { 350 class _CustomHashMap<K, V> extends _HashMap<K, V> {
340 final _Equality<K> _equals; 351 final _Equality<K> _equals;
341 final _Hasher<K> _hashCode; 352 final _Hasher<K> _hashCode;
342 _CustomHashMap(this._equals, this._hashCode); 353 final _Predicate _validKey;
354 _CustomHashMap(this._equals, this._hashCode, bool validKey(potentialKey))
355 : _validKey = (validKey != null) ? validKey : ((v) => v is K);
356
357 V operator[](Object key) {
358 if (!_validKey(key)) return null;
359 return super[key];
360 }
361
362 bool containsKey(Object key) {
363 if (!_validKey(key)) return false;
364 return super.containsKey(key);
365 }
366
367 V remove(Object key) {
368 if (!_validKey(key)) return null;
369 return super.remove(key);
370 }
343 371
344 int _computeHashCode(var key) { 372 int _computeHashCode(var key) {
345 // We force the hash codes to be unsigned 30-bit integers to avoid 373 // We force the hash codes to be unsigned 30-bit integers to avoid
346 // issues with problematic keys like '__proto__'. Another option 374 // issues with problematic keys like '__proto__'. Another option
347 // would be to throw an exception if the hash code isn't a number. 375 // would be to throw an exception if the hash code isn't a number.
348 return JS('int', '# & 0x3ffffff', _hashCode(key)); 376 return JS('int', '# & 0x3ffffff', _hashCode(key));
349 } 377 }
350 378
351 int _findBucketIndex(var bucket, var key) { 379 int _findBucketIndex(var bucket, var key) {
352 if (bucket == null) return -1; 380 if (bucket == null) return -1;
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
409 // TODO(kasperl): For now, we have to tell the type inferrer to 437 // TODO(kasperl): For now, we have to tell the type inferrer to
410 // treat the result of doing offset + 1 as an int. Otherwise, we 438 // treat the result of doing offset + 1 as an int. Otherwise, we
411 // get unnecessary bailout code. 439 // get unnecessary bailout code.
412 _offset = JS('int', '#', offset + 1); 440 _offset = JS('int', '#', offset + 1);
413 return true; 441 return true;
414 } 442 }
415 } 443 }
416 } 444 }
417 445
418 patch class LinkedHashMap<K, V> { 446 patch class LinkedHashMap<K, V> {
447 patch factory LinkedHashMap({ bool equals(K key1, K key2),
448 int hashCode(K key),
449 bool isValidKey(potentialKey) }) {
450 if (isValidKey == null) {
451 if (hashCode == null) {
452 if (equals == null) {
453 return new _LinkedHashMap<K, V>();
454 }
455 if (identical(identical, equals)) {
456 return new _LinkedIdentityHashMap<K, V>();
457 }
458 hashCode = _defaultHashCode;
459 } else if (equals == null) {
460 equals = _defaultEquals;
461 }
462 } else {
463 if (hashCode == null) {
464 hashCode = _defaultHashCode;
465 }
466 if (equals == null) {
467 equals = _defaultEquals;
468 }
469 }
470 return new _LinkedCustomHashMap<K, V>(equals, hashCode, isValidKey);
471 }
472 }
473
474 class _LinkedHashMap<K, V> implements LinkedHashMap<K, V> {
419 int _length = 0; 475 int _length = 0;
420 476
421 // The hash map contents are divided into three parts: one part for 477 // The hash map contents are divided into three parts: one part for
422 // string keys, one for numeric keys, and one for the rest. String 478 // string keys, one for numeric keys, and one for the rest. String
423 // and numeric keys map directly to their linked cells, but the rest 479 // and numeric keys map directly to their linked cells, but the rest
424 // of the entries are stored in bucket lists of the form: 480 // of the entries are stored in bucket lists of the form:
425 // 481 //
426 // [cell-0, cell-1, ...] 482 // [cell-0, cell-1, ...]
427 // 483 //
428 // where all keys in the same bucket share the same hash code. 484 // where all keys in the same bucket share the same hash code.
429 var _strings; 485 var _strings;
430 var _nums; 486 var _nums;
431 var _rest; 487 var _rest;
432 488
433 // The keys and values are stored in cells that are linked together 489 // The keys and values are stored in cells that are linked together
434 // to form a double linked list. 490 // to form a double linked list.
435 LinkedHashMapCell _first; 491 LinkedHashMapCell _first;
436 LinkedHashMapCell _last; 492 LinkedHashMapCell _last;
437 493
438 // We track the number of modifications done to the key set of the 494 // We track the number of modifications done to the key set of the
439 // hash map to be able to throw when the map is modified while being 495 // hash map to be able to throw when the map is modified while being
440 // iterated over. 496 // iterated over.
441 int _modifications = 0; 497 int _modifications = 0;
442 498
443 patch LinkedHashMap(); 499 _LinkedHash();
444 500
445 patch int get length => _length; 501 int get length => _length;
446 patch bool get isEmpty => _length == 0; 502 bool get isEmpty => _length == 0;
447 patch bool get isNotEmpty => !isEmpty; 503 bool get isNotEmpty => !isEmpty;
448 504
449 505
450 patch Iterable<K> get keys { 506 Iterable<K> get keys {
451 return new LinkedHashMapKeyIterable<K>(this); 507 return new LinkedHashMapKeyIterable<K>(this);
452 } 508 }
453 509
454 patch Iterable<V> get values { 510 Iterable<V> get values {
455 return keys.map((each) => this[each]); 511 return keys.map((each) => this[each]);
456 } 512 }
457 513
458 patch bool containsKey(Object key) { 514 bool containsKey(Object key) {
459 if (_isStringKey(key)) { 515 if (_isStringKey(key)) {
460 var strings = _strings; 516 var strings = _strings;
461 if (strings == null) return false; 517 if (strings == null) return false;
462 LinkedHashMapCell cell = _getTableEntry(strings, key); 518 LinkedHashMapCell cell = _getTableEntry(strings, key);
463 return cell != null; 519 return cell != null;
464 } else if (_isNumericKey(key)) { 520 } else if (_isNumericKey(key)) {
465 var nums = _nums; 521 var nums = _nums;
466 if (nums == null) return false; 522 if (nums == null) return false;
467 LinkedHashMapCell cell = _getTableEntry(nums, key); 523 LinkedHashMapCell cell = _getTableEntry(nums, key);
468 return cell != null; 524 return cell != null;
469 } else { 525 } else {
470 var rest = _rest; 526 var rest = _rest;
471 if (rest == null) return false; 527 if (rest == null) return false;
472 var bucket = _getBucket(rest, key); 528 var bucket = _getBucket(rest, key);
473 return _findBucketIndex(bucket, key) >= 0; 529 return _findBucketIndex(bucket, key) >= 0;
474 } 530 }
475 } 531 }
476 532
477 patch bool containsValue(Object value) { 533 bool containsValue(Object value) {
478 return keys.any((each) => this[each] == value); 534 return keys.any((each) => this[each] == value);
479 } 535 }
480 536
481 patch void addAll(Map<K, V> other) { 537 void addAll(Map<K, V> other) {
482 other.forEach((K key, V value) { 538 other.forEach((K key, V value) {
483 this[key] = value; 539 this[key] = value;
484 }); 540 });
485 } 541 }
486 542
487 patch V operator[](Object key) { 543 V operator[](Object key) {
488 if (_isStringKey(key)) { 544 if (_isStringKey(key)) {
489 var strings = _strings; 545 var strings = _strings;
490 if (strings == null) return null; 546 if (strings == null) return null;
491 LinkedHashMapCell cell = _getTableEntry(strings, key); 547 LinkedHashMapCell cell = _getTableEntry(strings, key);
492 return (cell == null) ? null : cell._value; 548 return (cell == null) ? null : cell._value;
493 } else if (_isNumericKey(key)) { 549 } else if (_isNumericKey(key)) {
494 var nums = _nums; 550 var nums = _nums;
495 if (nums == null) return null; 551 if (nums == null) return null;
496 LinkedHashMapCell cell = _getTableEntry(nums, key); 552 LinkedHashMapCell cell = _getTableEntry(nums, key);
497 return (cell == null) ? null : cell._value; 553 return (cell == null) ? null : cell._value;
498 } else { 554 } else {
499 var rest = _rest; 555 var rest = _rest;
500 if (rest == null) return null; 556 if (rest == null) return null;
501 var bucket = _getBucket(rest, key); 557 var bucket = _getBucket(rest, key);
502 int index = _findBucketIndex(bucket, key); 558 int index = _findBucketIndex(bucket, key);
503 if (index < 0) return null; 559 if (index < 0) return null;
504 LinkedHashMapCell cell = JS('var', '#[#]', bucket, index); 560 LinkedHashMapCell cell = JS('var', '#[#]', bucket, index);
505 return cell._value; 561 return cell._value;
506 } 562 }
507 } 563 }
508 564
509 patch void operator[]=(K key, V value) { 565 void operator[]=(K key, V value) {
510 if (_isStringKey(key)) { 566 if (_isStringKey(key)) {
511 var strings = _strings; 567 var strings = _strings;
512 if (strings == null) _strings = strings = _newHashTable(); 568 if (strings == null) _strings = strings = _newHashTable();
513 _addHashTableEntry(strings, key, value); 569 _addHashTableEntry(strings, key, value);
514 } else if (_isNumericKey(key)) { 570 } else if (_isNumericKey(key)) {
515 var nums = _nums; 571 var nums = _nums;
516 if (nums == null) _nums = nums = _newHashTable(); 572 if (nums == null) _nums = nums = _newHashTable();
517 _addHashTableEntry(nums, key, value); 573 _addHashTableEntry(nums, key, value);
518 } else { 574 } else {
519 var rest = _rest; 575 var rest = _rest;
520 if (rest == null) _rest = rest = _newHashTable(); 576 if (rest == null) _rest = rest = _newHashTable();
521 var hash = _computeHashCode(key); 577 var hash = _computeHashCode(key);
522 var bucket = JS('var', '#[#]', rest, hash); 578 var bucket = JS('var', '#[#]', rest, hash);
523 if (bucket == null) { 579 if (bucket == null) {
524 LinkedHashMapCell cell = _newLinkedCell(key, value); 580 LinkedHashMapCell cell = _newLinkedCell(key, value);
525 _setTableEntry(rest, hash, JS('var', '[#]', cell)); 581 _setTableEntry(rest, hash, JS('var', '[#]', cell));
526 } else { 582 } else {
527 int index = _findBucketIndex(bucket, key); 583 int index = _findBucketIndex(bucket, key);
528 if (index >= 0) { 584 if (index >= 0) {
529 LinkedHashMapCell cell = JS('var', '#[#]', bucket, index); 585 LinkedHashMapCell cell = JS('var', '#[#]', bucket, index);
530 cell._value = value; 586 cell._value = value;
531 } else { 587 } else {
532 LinkedHashMapCell cell = _newLinkedCell(key, value); 588 LinkedHashMapCell cell = _newLinkedCell(key, value);
533 JS('void', '#.push(#)', bucket, cell); 589 JS('void', '#.push(#)', bucket, cell);
534 } 590 }
535 } 591 }
536 } 592 }
537 } 593 }
538 594
539 patch V putIfAbsent(K key, V ifAbsent()) { 595 V putIfAbsent(K key, V ifAbsent()) {
540 if (containsKey(key)) return this[key]; 596 if (containsKey(key)) return this[key];
541 V value = ifAbsent(); 597 V value = ifAbsent();
542 this[key] = value; 598 this[key] = value;
543 return value; 599 return value;
544 } 600 }
545 601
546 patch V remove(Object key) { 602 V remove(Object key) {
547 if (_isStringKey(key)) { 603 if (_isStringKey(key)) {
548 return _removeHashTableEntry(_strings, key); 604 return _removeHashTableEntry(_strings, key);
549 } else if (_isNumericKey(key)) { 605 } else if (_isNumericKey(key)) {
550 return _removeHashTableEntry(_nums, key); 606 return _removeHashTableEntry(_nums, key);
551 } else { 607 } else {
552 var rest = _rest; 608 var rest = _rest;
553 if (rest == null) return null; 609 if (rest == null) return null;
554 var bucket = _getBucket(rest, key); 610 var bucket = _getBucket(rest, key);
555 int index = _findBucketIndex(bucket, key); 611 int index = _findBucketIndex(bucket, key);
556 if (index < 0) return null; 612 if (index < 0) return null;
557 // Use splice to remove the [cell] element at the index and 613 // Use splice to remove the [cell] element at the index and
558 // unlink the cell before returning its value. 614 // unlink the cell before returning its value.
559 LinkedHashMapCell cell = JS('var', '#.splice(#, 1)[0]', bucket, index); 615 LinkedHashMapCell cell = JS('var', '#.splice(#, 1)[0]', bucket, index);
560 _unlinkCell(cell); 616 _unlinkCell(cell);
561 // TODO(kasperl): Consider getting rid of the bucket list when 617 // TODO(kasperl): Consider getting rid of the bucket list when
562 // the length reaches zero. 618 // the length reaches zero.
563 return cell._value; 619 return cell._value;
564 } 620 }
565 } 621 }
566 622
567 patch void clear() { 623 void clear() {
568 if (_length > 0) { 624 if (_length > 0) {
569 _strings = _nums = _rest = _first = _last = null; 625 _strings = _nums = _rest = _first = _last = null;
570 _length = 0; 626 _length = 0;
571 _modified(); 627 _modified();
572 } 628 }
573 } 629 }
574 630
575 patch void forEach(void action(K key, V value)) { 631 void forEach(void action(K key, V value)) {
576 LinkedHashMapCell cell = _first; 632 LinkedHashMapCell cell = _first;
577 int modifications = _modifications; 633 int modifications = _modifications;
578 while (cell != null) { 634 while (cell != null) {
579 action(cell._key, cell._value); 635 action(cell._key, cell._value);
580 if (modifications != _modifications) { 636 if (modifications != _modifications) {
581 throw new ConcurrentModificationError(this); 637 throw new ConcurrentModificationError(this);
582 } 638 }
583 cell = cell._next; 639 cell = cell._next;
584 } 640 }
585 } 641 }
586 642
587 void _addHashTableEntry(var table, K key, V value) { 643 void _addHashTableEntry(var table, K key, V value) {
588 LinkedHashMapCell cell = _getTableEntry(table, key); 644 LinkedHashMapCell cell = _getTableEntry(table, key);
589 if (cell == null) { 645 if (cell == null) {
590 _setTableEntry(table, key, _newLinkedCell(key, value)); 646 _setTableEntry(table, key, _newLinkedCell(key, value));
591 } else { 647 } else {
592 cell._value = value; 648 cell._value = value;
593 } 649 }
594 } 650 }
595 651
596 V _removeHashTableEntry(var table, K key) { 652 V _removeHashTableEntry(var table, Object key) {
597 if (table == null) return null; 653 if (table == null) return null;
598 LinkedHashMapCell cell = _getTableEntry(table, key); 654 LinkedHashMapCell cell = _getTableEntry(table, key);
599 if (cell == null) return null; 655 if (cell == null) return null;
600 _unlinkCell(cell); 656 _unlinkCell(cell);
601 _deleteTableEntry(table, key); 657 _deleteTableEntry(table, key);
602 return cell._value; 658 return cell._value;
603 } 659 }
604 660
605 void _modified() { 661 void _modified() {
606 // Value cycles after 2^30 modifications. If you keep hold of an 662 // Value cycles after 2^30 modifications. If you keep hold of an
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
648 return key is String && key != '__proto__'; 704 return key is String && key != '__proto__';
649 } 705 }
650 706
651 static bool _isNumericKey(var key) { 707 static bool _isNumericKey(var key) {
652 // Only treat unsigned 30-bit integers as numeric keys. This way, 708 // Only treat unsigned 30-bit integers as numeric keys. This way,
653 // we avoid converting them to strings when we use them as keys in 709 // we avoid converting them to strings when we use them as keys in
654 // the JavaScript hash table object. 710 // the JavaScript hash table object.
655 return key is num && JS('bool', '(# & 0x3ffffff) === #', key, key); 711 return key is num && JS('bool', '(# & 0x3ffffff) === #', key, key);
656 } 712 }
657 713
658 static int _computeHashCode(var key) { 714 int _computeHashCode(var key) {
659 // We force the hash codes to be unsigned 30-bit integers to avoid 715 // We force the hash codes to be unsigned 30-bit integers to avoid
660 // issues with problematic keys like '__proto__'. Another option 716 // issues with problematic keys like '__proto__'. Another option
661 // would be to throw an exception if the hash code isn't a number. 717 // would be to throw an exception if the hash code isn't a number.
662 return JS('int', '# & 0x3ffffff', key.hashCode); 718 return JS('int', '# & 0x3ffffff', key.hashCode);
663 } 719 }
664 720
665 static _getTableEntry(var table, var key) { 721 static _getTableEntry(var table, var key) {
666 return JS('var', '#[#]', table, key); 722 return JS('var', '#[#]', table, key);
667 } 723 }
668 724
669 static void _setTableEntry(var table, var key, var value) { 725 static void _setTableEntry(var table, var key, var value) {
670 assert(value != null); 726 assert(value != null);
671 JS('void', '#[#] = #', table, key, value); 727 JS('void', '#[#] = #', table, key, value);
672 } 728 }
673 729
674 static void _deleteTableEntry(var table, var key) { 730 static void _deleteTableEntry(var table, var key) {
675 JS('void', 'delete #[#]', table, key); 731 JS('void', 'delete #[#]', table, key);
676 } 732 }
677 733
678 static List _getBucket(var table, var key) { 734 List _getBucket(var table, var key) {
679 var hash = _computeHashCode(key); 735 var hash = _computeHashCode(key);
680 return JS('var', '#[#]', table, hash); 736 return JS('var', '#[#]', table, hash);
681 } 737 }
682 738
683 static int _findBucketIndex(var bucket, var key) { 739 int _findBucketIndex(var bucket, var key) {
684 if (bucket == null) return -1; 740 if (bucket == null) return -1;
685 int length = JS('int', '#.length', bucket); 741 int length = JS('int', '#.length', bucket);
686 for (int i = 0; i < length; i++) { 742 for (int i = 0; i < length; i++) {
687 LinkedHashMapCell cell = JS('var', '#[#]', bucket, i); 743 LinkedHashMapCell cell = JS('var', '#[#]', bucket, i);
688 if (cell._key == key) return i; 744 if (cell._key == key) return i;
689 } 745 }
690 return -1; 746 return -1;
691 } 747 }
692 748
693 static _newHashTable() { 749 static _newHashTable() {
694 // Create a new JavaScript object to be used as a hash table. Use 750 // Create a new JavaScript object to be used as a hash table. Use
695 // Object.create to avoid the properties on Object.prototype 751 // Object.create to avoid the properties on Object.prototype
696 // showing up as entries. 752 // showing up as entries.
697 var table = JS('var', 'Object.create(null)'); 753 var table = JS('var', 'Object.create(null)');
698 // Attempt to force the hash table into 'dictionary' mode by 754 // Attempt to force the hash table into 'dictionary' mode by
699 // adding a property to it and deleting it again. 755 // adding a property to it and deleting it again.
700 var temporaryKey = '<non-identifier-key>'; 756 var temporaryKey = '<non-identifier-key>';
701 _setTableEntry(table, temporaryKey, table); 757 _setTableEntry(table, temporaryKey, table);
702 _deleteTableEntry(table, temporaryKey); 758 _deleteTableEntry(table, temporaryKey);
703 return table; 759 return table;
704 } 760 }
761
762 String toString() => Maps.mapToString(this);
763 }
764
765 class _LinkedIdentityHashMap<K, V> extends _LinkedHashMap<K, V> {
766 int _findBucketIndex(var bucket, var key) {
767 if (bucket == null) return -1;
768 int length = JS('int', '#.length', bucket);
769 for (int i = 0; i < length; i++) {
770 LinkedHashMapCell cell = JS('var', '#[#]', bucket, i);
771 if (identical(cell._key, key)) return i;
772 }
773 return -1;
774 }
775 }
776
777 class _LinkedCustomHashMap<K, V> extends _LinkedHashMap<K, V> {
778 final _Equality<K> _equals;
779 final _Hasher<K> _hashCode;
780 final _Predicate _validKey;
781 _LinkedCustomHashMap(this._equals, this._hashCode,
782 bool validKey(potentialKey))
783 : _validKey = (validKey != null) ? validKey : ((v) => v is K);
784
785 V operator[](Object key) {
786 if (!_validKey(key)) return null;
787 return super[key];
788 }
789
790 bool containsKey(Object key) {
791 if (!_validKey(key)) return false;
792 return super.containsKey(key);
793 }
794
795 V remove(Object key) {
796 if (!_validKey(key)) return null;
797 return super.remove(key);
798 }
799
800 int _computeHashCode(var key) {
801 // We force the hash codes to be unsigned 30-bit integers to avoid
802 // issues with problematic keys like '__proto__'. Another option
803 // would be to throw an exception if the hash code isn't a number.
804 return JS('int', '# & 0x3ffffff', _hashCode(key));
805 }
806
807 int _findBucketIndex(var bucket, var key) {
808 if (bucket == null) return -1;
809 int length = JS('int', '#.length', bucket);
810 for (int i = 0; i < length; i++) {
811 LinkedHashMapCell cell = JS('var', '#[#]', bucket, i);
812 if (_equals(cell._key, key)) return i;
813 }
814 return -1;
815 }
705 } 816 }
706 817
707 class LinkedHashMapCell { 818 class LinkedHashMapCell {
708 final _key; 819 final _key;
709 var _value; 820 var _value;
710 821
711 LinkedHashMapCell _next; 822 LinkedHashMapCell _next;
712 LinkedHashMapCell _previous; 823 LinkedHashMapCell _previous;
713 824
714 LinkedHashMapCell(this._key, this._value); 825 LinkedHashMapCell(this._key, this._value);
(...skipping 658 matching lines...) Expand 10 before | Expand all | Expand 10 after
1373 } else if (_cell == null) { 1484 } else if (_cell == null) {
1374 _current = null; 1485 _current = null;
1375 return false; 1486 return false;
1376 } else { 1487 } else {
1377 _current = _cell._element; 1488 _current = _cell._element;
1378 _cell = _cell._next; 1489 _cell = _cell._next;
1379 return true; 1490 return true;
1380 } 1491 }
1381 } 1492 }
1382 } 1493 }
OLDNEW
« no previous file with comments | « runtime/lib/collection_patch.dart ('k') | sdk/lib/collection/hash_map.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698