1 /******************************************************************************* 2 3 Template for a class implementing a mapping from a user-specified type to a 4 user-specified type. 5 6 The interface of the class has been kept deliberately simple, purely 7 handling the management of the mapping. The handling of the mapping values 8 is left entirely up to the user -- all methods simply return a pointer to 9 the mapping value which the user can do what they like with. (This is an 10 intentional design decision, in order to reduce the complexity of the 11 template.) 12 13 The HashMap is designed as a replacement for ocean.core.ArrayMap. It has 14 several advantages: 15 1. Memory safety. As the ArrayMap's buckets are implemented as dynamic 16 arrays, each bucket will theoretically grow continually in size over 17 extended periods of use. Even when clear()ed, the buffers allocated 18 for the buckets will not reduce in size. The HashMap, on the other 19 hand, uses a pool of elements, meaning that the memory allocated for 20 each bucket is truly variable. 21 2. Code simplicity via removing optional advanced features such as 22 thread safety and value array copying. 23 3. Extensibility. Functionality is split into several modules, including 24 a base class for easier reuse of components. 25 26 Usage example with various types stored in mapping: 27 28 --- 29 30 import ocean.util.container.map.HashMap; 31 32 // Mapping from hash_t -> int 33 auto map = new HashMap!(int); 34 35 hash_t hash = 232323; 36 37 // Add a mapping 38 *(map.put(hash)) = 12; 39 40 // Check if a mapping exists (null if not found) 41 auto exists = hash in map; 42 43 // Remove a mapping 44 map.remove(hash); 45 46 // Clear the map 47 map.clear(); 48 49 // Mapping from hash_t -> char[] 50 auto map2 = new HashMap!(char[]); 51 52 // Add a mapping 53 map2.put(hash).copy("hello"); 54 55 // Mapping from hash_t -> struct 56 struct MyStruct 57 { 58 int x; 59 float y; 60 } 61 62 auto map3 = new HashMap!(MyStruct); 63 64 // Add a mapping 65 *(map3.put(hash)) = MyStruct(12, 23.23); 66 67 --- 68 69 Copyright: 70 Copyright (c) 2009-2016 dunnhumby Germany GmbH. 71 All rights reserved. 72 73 License: 74 Boost Software License Version 1.0. See LICENSE_BOOST.txt for details. 75 Alternatively, this file may be distributed under the terms of the Tango 76 3-Clause BSD License (see LICENSE_BSD.txt for details). 77 78 *******************************************************************************/ 79 80 module ocean.util.container.map.Map; 81 82 83 84 85 import ocean.meta.types.Qualifiers; 86 87 import ocean.util.container.map.model.BucketSet; 88 89 import ocean.util.container.map.model.Bucket; 90 91 import ocean.util.container.map.model.MapIterator; 92 93 import ocean.util.container.map.model.StandardHash; 94 95 version (UnitTestVerbose) import ocean.io.Stdout; 96 97 98 99 /******************************************************************************* 100 101 Debug switch for verbose unittest output (uncomment if desired) 102 103 *******************************************************************************/ 104 105 //version = UnitTestVerbose; 106 107 version ( UnitTestVerbose ) 108 { 109 import ocean.io.Stdout; 110 } 111 112 /******************************************************************************* 113 114 StandardKeyHashingMap class template. Manages a mapping from K to V, using 115 a standard way of hash calculation: 116 117 - If K is a primitive type (integer, floating point, character), the hash 118 value is calculated from the raw key data using the FNV1a hash function. 119 That means, if the keys are dynamic arrays, including strings, the array 120 content is used as the key, not the array instance (ptr/length). 121 - If K is a dynamic or static array of a primitive type, the hash value is 122 calculated from the raw data of the key array content using the FNV1a hash 123 function. 124 - If K is a class, struct or union, it is expected to implement toHash(), 125 which will be used. 126 - Other key types (arrays of non-primitive types, classes/structs/unions 127 which do not implement toHash(), pointers, function references, delegates, 128 associative arrays) are not supported by this class template. 129 130 Params: 131 V = type to store in values of map 132 K = type to store in keys of map 133 134 *******************************************************************************/ 135 136 public class StandardKeyHashingMap ( V, K ) : Map!(V, K) 137 { 138 /*************************************************************************** 139 140 Constructor. 141 142 Params: 143 n = expected number of elements in mapping 144 load_factor = ratio of n to the number of internal buckets. The 145 desired (approximate) number of elements per bucket. For 146 example, 0.5 sets the number of buckets to double n; for 2 the 147 number of buckets is the half of n. load_factor must be greater 148 than 0. The load factor is basically a trade-off between memory 149 usage (number of buckets) and search time (number of elements 150 per bucket). 151 152 ***************************************************************************/ 153 154 public this ( size_t n, float load_factor = 0.75 ) 155 { 156 super(n, load_factor); 157 } 158 159 /*************************************************************************** 160 161 Constructor. 162 163 Params: 164 allocator = custom bucket elements allocator 165 n = expected number of elements in mapping 166 load_factor = ratio of n to the number of internal buckets. The 167 desired (approximate) number of elements per bucket. For 168 example, 0.5 sets the number of buckets to double n; for 2 the 169 number of buckets is the half of n. load_factor must be greater 170 than 0. The load factor is basically a trade-off between memory 171 usage (number of buckets) and search time (number of elements 172 per bucket). 173 174 ***************************************************************************/ 175 176 public this ( IAllocator allocator, size_t n, float load_factor = 0.75 ) 177 { 178 super(allocator, n, load_factor); 179 } 180 181 182 /*************************************************************************** 183 184 Mixin of the toHash() method which is declared abstract in BucketSet. 185 186 ***************************************************************************/ 187 188 override: 189 mixin StandardHash.toHash!(K); 190 } 191 192 /******************************************************************************* 193 194 StandardKeyHashingMap class template. Manages a mapping from K to ubyte[V], 195 using a standard way of hash calculation: 196 197 - If K is a primitive type (integer, floating point, character), the hash 198 value is calculated from the raw key data using the FNV1a hash function. 199 - If K is a dynamic or static array of a primitive type, the hash value is 200 calculated from the raw data of the key array content using the FNV1a hash 201 function. 202 - If K is a class, struct or union, it is expected to implement toHash(), 203 which will be used. 204 - Other key types (arrays of non-primitive types, classes/structs/unions 205 which do not implement toHash(), pointers, function references, delegates, 206 associative arrays) are not supported by this class template. 207 208 Params: 209 V = byte length of the values to store in the map, must be at least 1 210 K = type to store in keys of map 211 212 *******************************************************************************/ 213 214 public class StandardKeyHashingMap ( size_t V, K ) : Map!(V, K) 215 { 216 /*************************************************************************** 217 218 Constructor. 219 220 Params: 221 n = expected number of elements in mapping 222 load_factor = ratio of n to the number of internal buckets. The 223 desired (approximate) number of elements per bucket. For 224 example, 0.5 sets the number of buckets to double n; for 2 the 225 number of buckets is the half of n. load_factor must be greater 226 than 0. The load factor is basically a trade-off between memory 227 usage (number of buckets) and search time (number of elements 228 per bucket). 229 230 ***************************************************************************/ 231 232 public this ( size_t n, float load_factor = 0.75 ) 233 { 234 super(n, load_factor); 235 } 236 237 /*************************************************************************** 238 239 Constructor. 240 241 Params: 242 allocator = custom bucket elements allocator 243 n = expected number of elements in mapping 244 load_factor = ratio of n to the number of internal buckets. The 245 desired (approximate) number of elements per bucket. For 246 example, 0.5 sets the number of buckets to double n; for 2 the 247 number of buckets is the half of n. load_factor must be greater 248 than 0. The load factor is basically a trade-off between memory 249 usage (number of buckets) and search time (number of elements 250 per bucket). 251 252 ***************************************************************************/ 253 254 public this ( IAllocator allocator, size_t n, float load_factor = 0.75 ) 255 { 256 super(allocator, n, load_factor); 257 } 258 259 /*************************************************************************** 260 261 Mixin of the toHash() method which is declared abstract in BucketSet. 262 263 ***************************************************************************/ 264 265 override: 266 mixin StandardHash.toHash!(K); 267 } 268 269 /******************************************************************************* 270 271 Map class template to store values of a certain type. Manages a mapping 272 from K to V, leaving the hash function implementation to the subclass 273 (abstract BucketSet.toHash()). 274 275 Params: 276 V = type to store in values of map 277 K = type to store in keys of map 278 279 *******************************************************************************/ 280 281 public abstract class Map ( V, K ) : BucketSet!(V.sizeof, K) 282 { 283 // add to overload set explicitly 284 alias BucketSet!(V.sizeof, K).remove remove; 285 286 /*************************************************************************** 287 288 MapIterator template instance. 289 290 ***************************************************************************/ 291 292 alias .MapIterator!(V, K) MapIterator; 293 294 /*************************************************************************** 295 296 If V is a static array, opIndex() und opIndexAssign() need to return a 297 dynamic array slicing the value. 298 299 V.init redefinition to work around DMD bug 7752: If V is a static array, 300 then V.init is of the array base type. 301 302 ***************************************************************************/ 303 304 static if (is (V Base : Base[]) && !is (V == Base[])) 305 { 306 static if (is (typeof (V.init) == V)) 307 { 308 pragma (msg, "DMD bug 7752 is fixed, please remove the workaround ", 309 "in ", __FILE__, ":", __LINE__.stringof); 310 } 311 312 static immutable V_is_static_array = true; 313 314 alias Base[] VReturn; 315 316 static immutable Base[V.length] v_init = Base.init; 317 } 318 else 319 { 320 static immutable V_is_static_array = false; 321 322 alias V VReturn; 323 324 static immutable v_init = V.init; 325 } 326 327 /*************************************************************************** 328 329 Mixin of the specialized iterator classes which inherit from 330 BucketSet.Iterator. 331 This makes available three iterator classes that can be newed to allow 332 certain iteration behaviors: 333 334 * Iterator — just a normal iterator 335 * InterruptibleIterator — an iterator that can be interrupted and 336 resumed, but that has to be manually reset with reset() if the 337 iteration is meant to be repeated 338 339 If the map is modified between interrupted iterations, it can happen 340 that new elements that were added in the mean time won't appear in the 341 iteratation, depending on whether they end up in a bucket that was 342 already iterated or not. 343 344 Iterator usage example 345 --- 346 auto map = new HashMap!(size_t); 347 348 auto it = map.new Iterator(); 349 350 // A normal iteration over the map 351 foreach ( k, v; it ) 352 { 353 .. 354 } 355 356 // Equal to 357 foreach ( k, v; map ) 358 { 359 .. 360 } 361 --- 362 363 InterruptibleIterator 364 --- 365 auto map = new HashMap!(size_t); 366 367 auto interruptible_it = map.new InterruptibleIterator(); 368 369 // Iterate over the first 100k elements 370 foreach ( i, k, v; interruptible_it ) 371 { 372 .. 373 // Break after 100k elements 374 if ( i % 100_000 == 0 ) break; 375 } 376 377 // Iterate over the next 100k elments 378 foreach ( i, k, v; interruptible_it ) 379 { 380 .. 381 // Break after 100k elements 382 if ( i % 100_000 == 0 ) break; 383 } 384 385 // Assuming the map had 150k elements, the iteration is done now, 386 // so this won't do anything 387 foreach ( i, k, v; interruptible_it ) 388 { 389 .. 390 // Break after 100k elements 391 if ( i % 100_000 == 0 ) break; 392 } 393 394 assert ( interruptible_it.finished() == true ); 395 --- 396 397 See also: BucketSet.Iterator, MapIterator.IteratorClass 398 399 ***************************************************************************/ 400 401 mixin IteratorClass!(BucketSet!(V.sizeof, K).Iterator, MapIterator); 402 403 /*************************************************************************** 404 405 Constructor. 406 407 Params: 408 n = expected number of elements in mapping 409 load_factor = ratio of n to the number of internal buckets. The 410 desired (approximate) number of elements per bucket. For 411 example, 0.5 sets the number of buckets to double n; for 2 the 412 number of buckets is the half of n. load_factor must be greater 413 than 0. The load factor is basically a trade-off between memory 414 usage (number of buckets) and search time (number of elements 415 per bucket). 416 417 ***************************************************************************/ 418 419 protected this ( size_t n, float load_factor = 0.75 ) 420 { 421 super(n, load_factor); 422 } 423 424 /*************************************************************************** 425 426 Constructor. 427 428 Params: 429 allocator = custom bucket elements allocator 430 n = expected number of elements in mapping 431 load_factor = ratio of n to the number of internal buckets. The 432 desired (approximate) number of elements per bucket. For 433 example, 0.5 sets the number of buckets to double n; for 2 the 434 number of buckets is the half of n. load_factor must be greater 435 than 0. The load factor is basically a trade-off between memory 436 usage (number of buckets) and search time (number of elements 437 per bucket). 438 439 ***************************************************************************/ 440 441 protected this ( IAllocator allocator, size_t n, float load_factor = 0.75 ) 442 { 443 super(allocator, n, load_factor); 444 } 445 446 /*************************************************************************** 447 448 In operator. Looks up the value mapped by key. 449 450 Note: If it is sure that a value for key is in the map, in other words, 451 it would be a bug if it isn't, get() below is the preferred method to 452 use because it guarantees never to return a null pointer. 453 454 Params: 455 key = key to look up the value for 456 457 Returns: 458 pointer to the value mapped by key, if it exists. null otherwise. 459 460 ***************************************************************************/ 461 462 public V* opBinaryRight (string op : "in") ( in K key ) 463 { 464 auto element = this.get_(key); 465 466 return element? cast(V*)element.val[0 .. V.sizeof].ptr : null; 467 } 468 469 /*************************************************************************** 470 471 Obtains a reference to the value mapped by key. A value for key is 472 expected to exist in the map. 473 474 Note: Use this method if it is sure that a value for key is in the map, 475 in other words, it would be a bug if it isn't. To look up a mapping that 476 may or may not exist, use the 'in' operator (opBinaryRight!"in" above). 477 478 Params: 479 key = key to obtain the value for 480 481 Returns: 482 pointer to the value mapped by key. 483 484 Out: 485 The returned pointer is never null, key must be in the map. 486 487 ***************************************************************************/ 488 489 public V* get ( in K key ) 490 out (val) 491 { 492 assert (val !is null); 493 } 494 do 495 { 496 return cast(V*)this.get_(key, true).val[0 .. V.sizeof].ptr; 497 } 498 499 /*************************************************************************** 500 501 Obtains a the value mapped by key. A value for key is expected to exist 502 in the map. 503 504 Note: Use this method if it is sure that a value for key is in the map, 505 in other words, it would be a bug if it isn't. To look up a mapping that 506 may or may not exist, use the 'in' operator (opBinaryRight!"in" above). 507 508 Params: 509 key = key to obtain the value for 510 511 Returns: 512 the value mapped by key. 513 514 ***************************************************************************/ 515 516 public VReturn opIndex ( in K key ) 517 { 518 return *this.get(key); 519 } 520 521 /*************************************************************************** 522 523 Looks up the mapping for key or adds one if not found. 524 525 Note that, if a new mapping was added (added outputs true), the returned 526 pointer may reference a previously removed value. If this is not 527 desired, set the value referenced to by the pointer returned by remove() 528 to the desired initial value (e.g. V.init). 529 530 Params: 531 key = key to look up or add mapping for 532 added = set to true if the mapping did not exist but was added 533 534 Returns: 535 the value mapped to by the specified key. If added outputs true, the 536 value is unspecified and the caller should set the value as desired. 537 538 Out: 539 The returned pointer is never null. 540 541 ***************************************************************************/ 542 543 public V* put ( K key, out bool added ) 544 out (val) 545 { 546 assert (val !is null); 547 } 548 do 549 { 550 return cast(V*)this.put_(key, added).val[0 .. V.sizeof].ptr; 551 } 552 553 /*************************************************************************** 554 555 Adds or updates a mapping from the specified key. 556 557 Note that the returned slice may reference a previously removed value. 558 If this is not desired, set the value referenced to by the pointer 559 returned by remove() to the desired initial value (e.g. V.init). 560 561 Params: 562 key = key to add/update mapping for 563 564 Returns: 565 pointer to the value mapped to by the specified key. The caller 566 should set the value as desired. 567 568 Out: 569 The returned pointer is never null. 570 571 ***************************************************************************/ 572 573 public V* put ( K key ) 574 out (val) 575 { 576 assert (val !is null); 577 } 578 do 579 { 580 return cast(V*)this.put_(key).val[0 .. V.sizeof].ptr; 581 } 582 583 /*************************************************************************** 584 585 Adds or updates a mapping from the specified key. 586 587 Params: 588 key = key to add/update mapping for 589 val = value to map to 590 591 Returns: 592 val 593 594 ***************************************************************************/ 595 596 public VReturn opIndexAssign ( V val, K key ) 597 { 598 static if (V_is_static_array) 599 { 600 return (*this.put(key))[] = val[]; 601 } 602 else 603 { 604 return *this.put(key) = val; 605 } 606 } 607 608 /*************************************************************************** 609 610 Removes the mapping for the specified key and optionally invokes dg with 611 the value that is about to be removed. 612 613 Note that, if references to GC-allocated objects (objects or dynamic 614 arrays), it is a good idea to set the value referenced to by the 615 returned pointer to null to avoid these objects from being prevented 616 from garbage collection. In general pointers should be set to null for 617 the same reason and to avoid dangling pointers. 618 619 If the default allocator is used (that is, no allocator instance was 620 passed to the constructor), the value referenced by the val parameter of 621 dg is accessible and remains unchanged after dg returned until the next 622 call to put() or clear(). 623 624 Params: 625 key = key to remove mapping for 626 dg = optional delegate to call with the removed value (not called 627 if key was not found) 628 629 Returns: 630 true if key was found in the map or false if not. In case of false 631 dg was not called. 632 633 ***************************************************************************/ 634 635 public bool remove ( K key, scope void delegate ( ref V val ) dg = null ) 636 { 637 scope dg2 = (ref Bucket.Element element) 638 { 639 dg(*cast(V*)element.val[0 .. V.sizeof].ptr); 640 }; 641 642 // Issue 16037 643 return this.remove_(key, dg ? dg2 : null); 644 } 645 646 /*************************************************************************** 647 648 'foreach' iteration over key/value pairs currently in the map. 649 650 Note: If V or K (or both) are a static array, the corresponding 651 iteration variable is a dynamic array of the same base type and slices 652 the key or value. 653 (The reason is that static array 'ref' parameters are forbidden in D.) 654 655 Note: It is possible to have interruptible iterations, see documentation 656 for mixin of IteratorClass 657 658 See also: BucketSet.Iterator, MapIterator.IteratorClass 659 660 ***************************************************************************/ 661 662 public int opApply ( MapIterator.Dg dg ) 663 { 664 scope it = this.new Iterator; 665 666 return it.opApply(dg); 667 } 668 669 /*************************************************************************** 670 671 Same as above, but includes a counter 672 673 ***************************************************************************/ 674 675 public int opApply ( MapIterator.Dgi dgi ) 676 { 677 scope it = this.new Iterator; 678 679 return it.opApply(dgi); 680 } 681 682 /*************************************************************************** 683 684 Removes all elements from all buckets and sets the element values to 685 val_init. 686 687 Note: 688 Beware that calling this.clear() is known to sometimes cause 689 unexpected behaviour when the map is reused afterwards (where cyclic 690 links are introduced in the bucket-set). 691 Call this method instead as it has been reported to properly clear 692 the map. 693 694 Params: 695 val_init = initialisation value 696 697 Returns: 698 this instance 699 700 ***************************************************************************/ 701 702 public typeof(this) clearErase ( in V val_init = v_init ) 703 { 704 this.clear_((cast (void*) &val_init)[0 .. val_init.sizeof]); 705 706 return this; 707 } 708 } 709 710 /******************************************************************************* 711 712 HashMap class template to store the raw data of values of a certain size. 713 Manages a mapping from K to ubyte[V], leaving the hash function implementation 714 to the subclass (abstract BucketSet.toHash()). 715 Since static arrays cannot be returned, the access methods return a void[] 716 slice to the value. 717 718 Params: 719 V = byte length of the values to store in the map, must be at least 1 720 K = type to store in keys of map 721 722 *******************************************************************************/ 723 724 public abstract class Map ( size_t V, K ) : BucketSet!(V, K) 725 { 726 import ocean.core.Verify; 727 728 /*************************************************************************** 729 730 V check. 731 732 ***************************************************************************/ 733 734 static assert (V, "Please use Set for zero-length values."); 735 736 /*************************************************************************** 737 738 MapIterator template instance. 739 740 ***************************************************************************/ 741 742 alias .MapIterator!(Bucket.Element.Val, K) MapIterator; 743 744 /*************************************************************************** 745 746 Mixin of the specialized iterator classes which inherit from 747 BucketSet.Iterator. 748 749 This makes available three iterator classes that can be newed to allow 750 certain iteration behaviors: 751 752 * Iterator — just a normal iterator 753 * InterruptibleIterator — an iterator that can be interrupted and 754 resumed, but that has to be manually reset with reset() if the 755 iteration is meant to be repeated 756 757 If the map is modified between interrupted iterations, it can happen 758 that new elements that were added in the mean time won't appear in the 759 iteratation, depending on whether they end up in a bucket that was 760 already iterated or not. 761 762 Iterator usage example 763 --- 764 auto map = new HashMap!(size_t); 765 766 auto it = map.new Iterator(); 767 768 // A normal iteration over the map 769 foreach ( k, v; it ) 770 { 771 .. 772 } 773 774 // Equal to 775 foreach ( k, v; map ) 776 { 777 .. 778 } 779 --- 780 781 InterruptibleIterator 782 --- 783 auto map = new HashMap!(size_t); 784 785 auto interruptible_it = map.new InterruptibleIterator(); 786 787 // Iterate over the first 100k elements 788 foreach ( i, k, v; interruptible_it ) 789 { 790 .. 791 // Break after 100k elements 792 if ( i % 100_000 == 0 ) break; 793 } 794 795 // Iterate over the next 100k elments 796 foreach ( i, k, v; interruptible_it ) 797 { 798 .. 799 // Break after 100k elements 800 if ( i % 100_000 == 0 ) break; 801 } 802 803 // Assuming the map had 150k elements, the iteration is done now, 804 // so this won't do anything 805 foreach ( i, k, v; interruptible_it ) 806 { 807 .. 808 // Break after 100k elements 809 if ( i % 100_000 == 0 ) break; 810 } 811 812 assert ( interruptible_it.finished() == true ); 813 --- 814 815 See also: BucketSet.Iterator, MapIterator.IteratorClass 816 817 ***************************************************************************/ 818 819 mixin IteratorClass!(BucketSet!(V,K).Iterator, MapIterator); 820 821 /*************************************************************************** 822 823 Constructor. 824 825 Params: 826 n = expected number of elements in mapping 827 load_factor = ratio of n to the number of internal buckets. The 828 desired (approximate) number of elements per bucket. For 829 example, 0.5 sets the number of buckets to double n; for 2 the 830 number of buckets is the half of n. load_factor must be greater 831 than 0. The load factor is basically a trade-off between memory 832 usage (number of buckets) and search time (number of elements 833 per bucket). 834 835 ***************************************************************************/ 836 837 protected this ( size_t n, float load_factor = 0.75 ) 838 { 839 super(n, load_factor); 840 } 841 842 /*************************************************************************** 843 844 In operator. Looks up the value mapped by key. 845 846 Note: If it is sure that a value for key is in the map, in other words, 847 it would be a bug if it isn't, get() below is the preferred method to 848 use because it guarantees never to return a null pointer. 849 850 Params: 851 key = key to look up the value for 852 853 Returns: 854 an array slicing the value buffer mapped by key, if it exists, or 855 null otherwise. 856 857 Out: 858 The returned array is either null or has the length V. 859 860 ***************************************************************************/ 861 862 public void[] opBinaryRight (string op : "in") ( in K key ) 863 out (val) 864 { 865 if (val) 866 { 867 assert (val.length == V); 868 } 869 } 870 do 871 { 872 return this.get_(key).val; 873 } 874 875 /*************************************************************************** 876 877 Obtains a reference to the value mapped by key. A value for key is 878 expected to exist in the map. 879 880 Note: Use this method if it is sure that a value for key is in the map, 881 in other words, it would be a bug if it isn't. To look up a mapping that 882 may or may not exist, use the 'in' operator (opBinaryRight!"in"above). 883 884 Params: 885 key = key to obtain the value for 886 887 Returns: 888 pointer to the value mapped by key. 889 890 Out: 891 The returned array is never null and has the length V. 892 893 ***************************************************************************/ 894 895 public void[] get ( in K key ) 896 out (val) 897 { 898 assert (val.length == V); 899 } 900 do 901 { 902 return this.get_(key, true).val; 903 } 904 905 /*************************************************************************** 906 907 Obtains a the value mapped by key. A value for key is expected to exist 908 in the map. 909 910 Note: Use this method if it is sure that a value for key is in the map, 911 in other words, it would be a bug if it isn't. To look up a mapping that 912 may or may not exist, use the 'in' operator (opBinaryRight"in" above). 913 914 Params: 915 key = key to obtain the value for 916 917 Returns: 918 the value mapped by key. 919 920 ***************************************************************************/ 921 922 alias get opIndex; 923 924 /*************************************************************************** 925 926 Looks up the mapping for key or adds one if not found. 927 928 Note that, if a new mapping was added (added outputs true), the returned 929 slice may reference a previously removed value. If this is not desired, 930 copy the desired initial value into the sliced buffer returned by 931 remove(). 932 933 Params: 934 key = key to add/update mapping for 935 added = set to true if the record did not exist but was added 936 937 Returns: 938 an array slicing the value buffer mapped to by the specified key. If 939 added outputs true, the value is unspecified and the caller should 940 set the value as desired. 941 942 Out: 943 The returned array is never null and has the length V. 944 945 ***************************************************************************/ 946 947 public void[] put ( in K key, out bool added ) 948 out (val) 949 { 950 assert (val.length == V); 951 } 952 do 953 { 954 return this.put_(key, added).val; 955 } 956 957 /*************************************************************************** 958 959 Adds or updates a mapping from the specified key. Note that, if a new 960 mapping was added (added outputs true), the returned slice may reference 961 a previously removed value. If this is not desired, copy the desired 962 initial value into the sliced buffer returned by remove(). 963 964 Params: 965 key = key to add/update mapping for 966 967 Returns: 968 an array slicing the value buffer mapped to by the specified key. 969 The caller should set the value as desired. 970 971 Out: 972 The returned array is never null and has the length V. 973 974 **************************************************************************/ 975 976 public void[] put ( K key ) 977 out (val) 978 { 979 assert (val.length == V); 980 } 981 do 982 { 983 bool added; 984 985 return this.put_(key, added).val; 986 } 987 988 /*************************************************************************** 989 990 Adds or updates a mapping from key to val, copying the content of val 991 into the map. 992 993 Params: 994 key = key to add/update mapping for 995 val = value to map to 996 997 Returns: 998 val 999 1000 In: 1001 val.length must be V. 1002 1003 ***************************************************************************/ 1004 1005 public void[] opIndexAssign ( void[] val, K key ) 1006 { 1007 verify (val.length == V, "expected a value of length " ~ V.stringof); 1008 1009 bool added; 1010 1011 this.put_(key, added).val[] = val[]; 1012 1013 return val; 1014 } 1015 1016 /*************************************************************************** 1017 1018 Removes the mapping for the specified key. 1019 1020 Params: 1021 key = key to remove mapping for 1022 1023 Returns: 1024 an array slicing the value buffer of the removed element, if found, 1025 or null otherwise. It is guaranteed that the referenced value will 1026 remain unchanged until the next call to put(), which may reuse it, 1027 or to clear(). 1028 1029 Out: 1030 The returned array is either null or has the length V. 1031 1032 ***************************************************************************/ 1033 1034 public void[] remove ( in K key ) 1035 out (val) 1036 { 1037 if (val) 1038 { 1039 assert (val.length == V); 1040 } 1041 } 1042 do 1043 { 1044 return this.remove_(key).val; 1045 } 1046 1047 /*************************************************************************** 1048 1049 'foreach' iteration over key/value pairs currently in the map. 1050 1051 Note: It is possible to have interruptible iterations, see documentation 1052 for mixin of IteratorClass 1053 1054 See also: BucketSet.Iterator, MapIterator.IteratorClass 1055 1056 Notes: 1057 - During iteration it is forbidden to call clear() or redistribute() or 1058 remove map elements. If elements are added, the iteration may or may 1059 not include these elements. 1060 - If V or K (or both) are a static array, the corresponding iteration 1061 variable is a dynamic array of the same base type and slices the key 1062 or value of the element in the map. (The reason is that static array 1063 'ref' parameters are forbidden in D.) 1064 In this case it is not recommended to do a 'ref' iteration over key or 1065 value. To modify a value during iteration, copy the new value contents 1066 into the array content. Example: 1067 --- 1068 // Using the StandardKeyHashingMap subclass because Map is an 1069 // abstract class (inherits abstract toHash()). 1070 1071 alias int[7] ValueType; 1072 alias char[4] KeyType; 1073 1074 auto map = new StandardKeyHashingMap!(ValueType, KeyType); 1075 1076 // Side note: Use the '[x, y, ...]' array literal only with 1077 // constants or in code locations that are not executed repeatedly 1078 // because for variables it invokes a buffer allocation, leading 1079 // to a memory leak condition when it is done very often. 1080 1081 const int[7] new_value = [2, 3, 5, 7, 11, 13, 17]; 1082 1083 foreach (key, val; map) 1084 { 1085 // - key is of type char[] and slices the key of the current 1086 // map element so key.length is guaranteed to be 4, 1087 // - val is of type int[] and slices the value of the current 1088 // map element so val.length is guaranteed to be 7. 1089 1090 // Modify the value by copying into the static array 1091 // referenced by val. 1092 1093 val[] = new_value[]; 1094 1095 // It is also possible to modify single val array elements. 1096 1097 val[2] += val[5] * 4711; 1098 } 1099 --- 1100 DO NOT do that with the key or modify it in-place in any way! 1101 1102 - It is not recommended to specify 'ref' for the key iteration variable. 1103 If you do it anyway, DO NOT modify the key in-place! 1104 1105 ***************************************************************************/ 1106 1107 public int opApply ( MapIterator.Dg dg ) 1108 { 1109 scope it = this.new Iterator; 1110 1111 return it.opApply(dg); 1112 } 1113 1114 /*************************************************************************** 1115 1116 Same method as above, but includes a counter 1117 1118 ***************************************************************************/ 1119 1120 public int opApply ( MapIterator.Dgi dgi ) 1121 { 1122 scope it = this.new Iterator; 1123 1124 return it.opApply(dgi); 1125 } 1126 1127 /*************************************************************************** 1128 1129 Removes all elements from all buckets and sets the element values to 1130 val_init. 1131 1132 Params: 1133 val_init = initialisation value 1134 1135 Returns: 1136 this instance 1137 1138 **************************************************************************/ 1139 1140 public typeof(this) clear ( void[] val_init = null ) 1141 { 1142 verify (!val_init.length || val_init.length == V, 1143 "val_init.length expected to be 0 or " ~ V); 1144 1145 this.clear_(val_init); 1146 1147 return this; 1148 } 1149 } 1150 1151 /****************************************************************************** 1152 1153 Test key types smaller than size_t (or even equals) don't screw up the 1154 alignment and makes impossible to the GC to scan pointers in the value. 1155 1156 ******************************************************************************/ 1157 1158 version (unittest) 1159 { 1160 import core.memory; 1161 import ocean.core.Test; 1162 1163 void test_key (T) () 1164 { 1165 auto map = new StandardKeyHashingMap!(mstring, T)(10); 1166 1167 for (T i = 0; i < 10; i++) 1168 { 1169 auto p = map.put(i); 1170 *p = "Sociomantic".dup; 1171 } 1172 1173 GC.collect(); 1174 1175 for (T i = 0; i < 10; i++) 1176 { 1177 auto p = map.get(i); 1178 test!("==")(*p, "Sociomantic"[]); 1179 } 1180 } 1181 1182 void test_val (T) () 1183 { 1184 auto map = new StandardKeyHashingMap!(T, mstring)(10); 1185 1186 for (T i = 0; i < 10; i++) 1187 { 1188 auto p = map.put("Sociomantic".dup ~ cast(char) (0x30+i)); 1189 *p = i; 1190 } 1191 1192 GC.collect(); 1193 1194 for (T i = 0; i < 10; i++) 1195 { 1196 auto p = map.get("Sociomantic".dup ~ cast(char) (0x30+i)); 1197 test!("==")(*p, i); 1198 } 1199 } 1200 1201 import MallocAllocator = ocean.util.container.map.model.BucketElementMallocAllocator; 1202 import GCAllocator = ocean.util.container.map.model.BucketElementGCAllocator; 1203 import FreeListAllocator = ocean.util.container.map.model.BucketElementFreeList; 1204 } 1205 1206 unittest 1207 { 1208 test_key!(byte); 1209 test_key!(short); 1210 test_key!(int); 1211 test_key!(long); 1212 1213 test_val!(byte); 1214 test_val!(short); 1215 test_val!(int); 1216 test_val!(long); 1217 } 1218 1219 unittest 1220 { 1221 // ensure opIn works with const keys 1222 auto map = new StandardKeyHashingMap!(int, mstring)(5); 1223 auto v = "something" in map; 1224 test!("is")(v, null); 1225 } 1226 1227 unittest 1228 { 1229 // ensure class keys work 1230 class Key 1231 { 1232 int x; 1233 1234 this ( int x ) 1235 { 1236 this.x = x; 1237 } 1238 1239 override hash_t toHash ( ) 1240 { 1241 return x; 1242 } 1243 } 1244 1245 auto map = new StandardKeyHashingMap!(int, Key)(5); 1246 auto key = new Key(42); 1247 map[key] = 24; 1248 auto v = key in map; 1249 test!("!is")(v, null); 1250 test!("==")(*v, 24); 1251 } 1252 1253 /******************************************************************************* 1254 1255 Instantiate the bucket element allocator templates. 1256 1257 *******************************************************************************/ 1258 1259 unittest 1260 { 1261 MallocAllocator.instantiateAllocator!(StandardKeyHashingMap!(int, int)); 1262 GCAllocator.instantiateAllocator!(StandardKeyHashingMap!(int, int)); 1263 FreeListAllocator.instantiateAllocator!(StandardKeyHashingMap!(int, int)); 1264 }