1 /** 2 * Associative Array utility functions 3 * 4 * License: 5 * This Source Code Form is subject to the terms of 6 * the Mozilla Public License, v. 2.0. If a copy of 7 * the MPL was not distributed with this file, You 8 * can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * Authors: 11 * Vladimir Panteleev <ae@cy.md> 12 */ 13 14 module ae.utils.aa; 15 16 import std.algorithm; 17 import std.range; 18 import std.traits; 19 import std.typecons; 20 21 // *************************************************************************** 22 23 /// Polyfill for object.require 24 static if (!__traits(hasMember, object, "require")) 25 ref V require(K, V)(ref V[K] aa, K key, lazy V value = V.init) 26 { 27 auto p = key in aa; 28 if (p) 29 return *p; 30 return aa[key] = value; 31 } 32 33 unittest 34 { 35 int[int] aa; 36 aa.require(1, 2); 37 assert(aa[1] == 2); 38 aa.require(2, 3) = 4; 39 assert(aa[2] == 4); 40 aa.require(1, 5); 41 assert(aa[1] == 2); 42 aa.require(1, 6) = 7; 43 assert(aa[1] == 7); 44 } 45 46 static if (!__traits(hasMember, object, "update")) 47 { 48 /// Polyfill for object.update 49 void updatePolyfill(K, V, C, U)(ref V[K] aa, K key, scope C create, scope U update) 50 if (is(typeof(create()) : V) && is(typeof(update(aa[K.init])) : V)) 51 { 52 auto p = key in aa; 53 if (p) 54 *p = update(*p); 55 else 56 aa[key] = create(); 57 } 58 59 /// Work around https://issues.dlang.org/show_bug.cgi?id=15795 60 alias update = updatePolyfill; 61 } 62 63 // https://github.com/dlang/druntime/pull/3012 64 private enum haveObjectUpdateWithVoidUpdate = is(typeof({ 65 int[int] aa; 66 .object.update(aa, 0, { return 0; }, (ref int v) { }); 67 })); 68 69 static if (!haveObjectUpdateWithVoidUpdate) 70 { 71 /// Polyfill for object.update with void update function 72 void updateVoid(K, V, C, U)(ref V[K] aa, K key, scope C create, scope U update) 73 if (is(typeof(create()) : V) && is(typeof(update(aa[K.init])) == void)) 74 { 75 // We can polyfill this in two ways. 76 // What's more expensive, copying the value, or a second key lookup? 77 enum haveObjectUpdate = __traits(hasMember, object, "update"); 78 enum valueIsExpensiveToCopy = V.sizeof > string.sizeof 79 || hasElaborateCopyConstructor!V 80 || hasElaborateDestructor!V; 81 static if (haveObjectUpdate && !valueIsExpensiveToCopy) 82 { 83 .object.update(aa, key, create, 84 (ref V v) { update(v); return v; }); 85 } 86 else 87 { 88 auto p = key in aa; 89 if (p) 90 update(*p); 91 else 92 aa[key] = create(); 93 } 94 } 95 96 /// Work around https://issues.dlang.org/show_bug.cgi?id=15795 97 alias update = updateVoid; 98 } 99 else 100 alias updateVoid = object.update; /// Use `object.update` for void update function 101 102 // Inject overload 103 static if (__traits(hasMember, object, "update")) 104 private alias update = object.update; 105 106 // *************************************************************************** 107 108 /// Get a value from an AA, and throw an exception (not an error) if not found 109 ref auto aaGet(AA, K)(auto ref AA aa, auto ref K key) 110 if (is(typeof(key in aa))) 111 { 112 import std.conv; 113 114 auto p = key in aa; 115 if (p) 116 return *p; 117 else 118 static if (is(typeof(text(key)))) 119 throw new Exception("Absent value: " ~ text(key)); 120 else 121 throw new Exception("Absent value"); 122 } 123 124 /// If key is not in aa, add it with defaultValue. 125 /// Returns a reference to the value corresponding to key. 126 ref V getOrAdd(K, V)(ref V[K] aa, auto ref K key, auto ref V defaultValue) 127 { 128 return aa.require(key, defaultValue); 129 } 130 131 /// ditto 132 ref V getOrAdd(K, V)(ref V[K] aa, auto ref K key) 133 { 134 return getOrAdd(aa, key, V.init); 135 } 136 137 unittest 138 { 139 int[int] aa; 140 aa.getOrAdd(1, 2) = 3; 141 assert(aa[1] == 3); 142 assert(aa.getOrAdd(1, 4) == 3); 143 } 144 145 /// If key is not in aa, add it with the given value, and return true. 146 /// Otherwise, return false. 147 bool addNew(K, V)(ref V[K] aa, auto ref K key, auto ref V value) 148 { 149 bool added = void; 150 updateVoid(aa, key, 151 delegate V ( ) { added = true ; return value; }, 152 delegate void(ref V v) { added = false; }, 153 ); 154 return added; 155 } 156 157 /// ditto 158 bool addNew(K, V, bool ordered, bool multi)(ref HashCollection!(K, V, ordered, multi) aa, auto ref K key, auto ref V value) 159 if (!is(V == void)) // Not a set 160 { 161 bool added = void; 162 aa.update(key, 163 delegate V ( ) { added = true ; return value; }, 164 delegate void(ref V v) { added = false; }, 165 ); 166 return added; 167 } 168 169 unittest 170 { 171 int[int] aa; 172 assert( aa.addNew(1, 2)); 173 assert(!aa.addNew(1, 3)); 174 assert(aa[1] == 2); 175 } 176 177 unittest 178 { 179 OrderedMap!(int, int) aa; 180 assert( aa.addNew(1, 2)); 181 assert(!aa.addNew(1, 3)); 182 assert(aa[1] == 2); 183 } 184 185 // *************************************************************************** 186 187 /// Key/value pair 188 struct KeyValuePair(K, V) { K key; /***/ V value; /***/ } 189 190 /// Get key/value pairs from AA 191 deprecated KeyValuePair!(K, V)[] pairs(K, V)(V[K] aa) 192 { 193 KeyValuePair!(K, V)[] result; 194 foreach (key, value; aa) 195 result ~= KeyValuePair!(K, V)(key, value); 196 return result; 197 } 198 199 /// Get key/value pairs from AA, sorted by keys 200 KeyValuePair!(K, V)[] sortedPairs(K, V)(V[K] aa) 201 { 202 KeyValuePair!(K, V)[] result; 203 foreach (key; aa.keys.sort) 204 result ~= KeyValuePair!(K, V)(key, aa[key]); 205 return result; 206 } 207 208 /// Get values from AA, sorted by keys 209 V[] sortedValues(K, V)(in V[K] aa) 210 { 211 V[] result; 212 foreach (key; aa.keys.sort()) 213 result ~= aa[key]; 214 return result; 215 } 216 217 /// Merge source into target. Return target. 218 V[K] merge(K, V)(auto ref V[K] target, in V[K] source) 219 { 220 foreach (k, v; source) 221 target[k] = v; 222 return target; 223 } 224 225 unittest 226 { 227 int[int] target; 228 int[int] source = [2:4]; 229 merge(target, source); 230 assert(source == target); 231 232 target = [1:1, 2:2, 3:3]; 233 merge(target, source); 234 assert(target == [1:1, 2:4, 3:3]); 235 236 assert(merge([1:1], [2:2]) == [1:1, 2:2]); 237 } 238 239 /// Slurp a range of two elements (or two-element struct/class) into an AA. 240 auto toAA(R)(R r) 241 if (is(typeof(r.front[1]))) 242 { 243 alias K = typeof(r.front[0]); 244 alias V = typeof(r.front[1]); 245 V[K] result; 246 foreach (pair; r) 247 { 248 assert(pair.length == 2); 249 result[pair[0]] = pair[1]; 250 } 251 return result; 252 } 253 254 /// ditto 255 auto toAA(R)(R r) 256 if (is(typeof(r.front.tupleof)) && r.front.tupleof.length == 2 && !is(typeof(r.front[1]))) 257 { 258 return r.map!(el => tuple(el.tupleof)).toAA(); 259 } 260 261 deprecated unittest 262 { 263 assert([[2, 4]].toAA() == [2:4]); 264 assert([2:4].pairs.toAA() == [2:4]); 265 } 266 267 /// Ensure that arr is non-null if empty. 268 V[K] nonNull(K, V)(V[K] aa) 269 { 270 if (aa !is null) 271 return aa; 272 aa[K.init] = V.init; 273 aa.remove(K.init); 274 assert(aa !is null); 275 return aa; 276 } 277 278 unittest 279 { 280 int[int] aa; 281 assert(aa is null); 282 aa = aa.nonNull; 283 assert(aa !is null); 284 assert(aa.length == 0); 285 } 286 287 // *************************************************************************** 288 289 // Helpers for HashCollection 290 private 291 { 292 alias Void = void[0]; // Zero-sized type 293 static assert(Void.sizeof == 0); 294 295 // Abstraction layer for single/multi-value type holding one or many T. 296 // Optimizer representation for Void. 297 struct SingleOrMultiValue(bool multi, T) 298 { 299 alias ValueType = Select!(multi, 300 // multi==true 301 Select!(is(T == Void), 302 size_t, // "store" the items by keeping track of their count only. 303 T[], 304 ), 305 306 // multi==false 307 Select!(is(T == Void), 308 Void, 309 T[1], 310 ), 311 ); 312 313 // Using free functions instead of struct methods, 314 // as structs always have non-zero size. 315 static: 316 317 size_t length(ref const ValueType v) nothrow 318 { 319 static if (is(T == Void)) 320 static if (multi) 321 return v; // count 322 else 323 return 1; 324 else 325 return v.length; // static or dynamic array 326 } 327 } 328 } 329 330 /// Base type for ordered/unordered single-value/multi-value map/set 331 /*private*/ struct HashCollection(K, V, bool ordered, bool multi) 332 { 333 private: 334 enum bool haveValues = !is(V == void); // Not a set 335 336 // The type for values used when a value variable is needed 337 alias ValueVarType = Select!(haveValues, V, Void); 338 339 // The type of a single element of the values of `this.lookup`. 340 // When ordered==true, we use size_t (index into `this.items`). 341 alias LookupItem = Select!(ordered, size_t, ValueVarType); 342 343 // The type of the values of `this.lookup`. 344 alias SM = SingleOrMultiValue!(multi, LookupItem); 345 alias LookupValue = SM.ValueType; 346 347 static if (haveValues) 348 { 349 alias ReturnType(Fallback) = V; 350 alias OpIndexKeyType = K; 351 alias OpIndexValueType = V; 352 } 353 else 354 { 355 static if (ordered) 356 { 357 alias OpIndexKeyType = size_t; 358 alias OpIndexValueType = K; 359 alias ReturnType(Fallback) = K; 360 } 361 else 362 { 363 alias OpIndexKeyType = void; 364 alias OpIndexValueType = void; 365 alias ReturnType(Fallback) = Fallback; 366 } 367 } 368 enum haveReturnType = !is(ReturnType!void == void); 369 enum haveIndexing = haveValues || ordered; 370 371 alias IK = OpIndexKeyType; 372 alias IV = OpIndexValueType; 373 374 // The contract we try to follow is that adding/removing items in 375 // one copy of the object will not affect other copies. 376 // Therefore, when we have array fields, make sure they are dup'd 377 // on copy, so that we don't trample older copies' data. 378 enum bool needDupOnCopy = ordered; 379 380 static if (haveReturnType) 381 { 382 static if (ordered) 383 /* */ ref inout(ReturnType!void) lookupToReturnValue(in LookupItem lookupItem) inout { return items[lookupItem].returnValue; } 384 else 385 static ref inout(ReturnType!void) lookupToReturnValue(ref inout(LookupItem) lookupItem) { return lookupItem ; } 386 } 387 388 // *** Data *** 389 390 // This is used for all key hash lookups. 391 LookupValue[K] lookup; 392 393 static if (ordered) 394 { 395 struct Item 396 { 397 K key; 398 ValueVarType value; 399 400 static if (haveValues) 401 private alias returnValue = value; 402 else 403 private alias returnValue = key; 404 } 405 Item[] items; 406 407 enum bool canDup = is(typeof(lookup.dup)) && is(typeof(items.dup)); 408 } 409 else 410 { 411 enum bool canDup = is(typeof(lookup.dup)); 412 } 413 414 public: 415 416 // *** Lifetime *** 417 418 /// Postblit 419 static if (needDupOnCopy) 420 { 421 static if (canDup) 422 this(this) 423 { 424 lookup = lookup.dup; 425 items = items.dup; 426 } 427 else 428 @disable this(this); 429 } 430 431 /// Create shallow copy 432 static if (canDup) 433 typeof(this) dup() 434 { 435 static if (needDupOnCopy) 436 return this; 437 else 438 { 439 typeof(this) copy; 440 copy.lookup = lookup.dup; 441 static if (ordered) 442 copy.items = items.dup; 443 return copy; 444 } 445 } 446 447 // *** Conversions (from) *** 448 449 /// Construct from something else 450 this(Input)(Input input) 451 if (is(typeof(opAssign(input)))) 452 { 453 opAssign(input); 454 } 455 456 /// Null assignment 457 ref typeof(this) opAssign(typeof(null) _) 458 { 459 clear(); 460 return this; 461 } 462 463 /// Convert from an associative type 464 ref typeof(this) opAssign(AA)(AA aa) 465 if (haveValues 466 && !is(AA : typeof(this)) 467 && is(typeof({ foreach (ref k, ref v; aa) add(k, v); }))) 468 { 469 clear(); 470 foreach (ref k, ref v; aa) 471 add(k, v); 472 return this; 473 } 474 475 /// Convert from an associative type of multiple items 476 ref typeof(this) opAssign(AA)(AA aa) 477 if (haveValues 478 && multi 479 && !is(AA : typeof(this)) 480 && is(typeof({ foreach (ref k, ref vs; aa) foreach (ref v; vs) add(k, v); }))) 481 { 482 clear(); 483 foreach (ref k, ref vs; aa) 484 foreach (ref v; vs) 485 add(k, v); 486 return this; 487 } 488 489 /// Convert from a range of tuples 490 ref typeof(this) opAssign(R)(R input) 491 if (haveValues 492 && is(typeof({ foreach (ref pair; input) add(pair[0], pair[1]); })) 493 && !is(typeof({ foreach (ref k, ref v; input) add(k, v); })) 494 && is(typeof(input.front.length)) 495 && input.front.length == 2) 496 { 497 clear(); 498 foreach (ref pair; input) 499 add(pair[0], pair[1]); 500 return this; 501 } 502 503 /// Convert from a range of key/value pairs 504 ref typeof(this) opAssign(R)(R input) 505 if (haveValues 506 && is(typeof({ foreach (ref pair; input) add(pair.key, pair.value); })) 507 && !is(typeof({ foreach (ref k, ref v; input) add(k, v); }))) 508 { 509 clear(); 510 foreach (ref pair; input) 511 add(pair.key, pair.value); 512 return this; 513 } 514 515 /// Convert from a range of values 516 ref typeof(this) opAssign(R)(R input) 517 if (!haveValues 518 && !is(R : typeof(this)) 519 && is(typeof({ foreach (ref v; input) add(v); }))) 520 { 521 clear(); 522 foreach (ref v; input) 523 add(v); 524 return this; 525 } 526 527 // *** Conversions (to) *** 528 529 /// Convert to bool (true if non-null) 530 bool opCast(T)() const 531 if (is(T == bool)) 532 { 533 return lookup !is null; 534 } 535 536 /// Convert to D associative array 537 static if (!ordered) 538 { 539 const(LookupValue[K]) toAA() const 540 { 541 return lookup; 542 } 543 544 static if (is(typeof(lookup.dup))) 545 LookupValue[K] toAA() 546 { 547 return lookup.dup; 548 } 549 550 deprecated alias items = toAA; 551 } 552 553 // *** Query (basic) *** 554 555 /// True when there are no items. 556 bool empty() pure const nothrow @nogc @trusted 557 { 558 static if (ordered) 559 return items.length == 0; // optimization 560 else 561 return lookup.byKey.empty; // generic version 562 } 563 564 /// Total number of items, including with duplicate keys. 565 size_t length() pure const nothrow @nogc @trusted 566 { 567 static if (ordered) 568 return items.length; // optimization 569 else 570 static if (!multi) 571 return lookup.length; // optimization 572 else // generic version 573 { 574 size_t result; 575 foreach (ref v; lookup.byValue) 576 result += SM.length(v); 577 return result; 578 } 579 } 580 581 // *** Query (by key) *** 582 583 /// Check if item with this key has been added. 584 /// When applicable, return a pointer to the last value added with this key. 585 Select!(haveReturnType, inout(ReturnType!void)*, bool) opBinaryRight(string op : "in", _K)(auto ref _K key) inout 586 if (is(typeof(key in lookup))) 587 { 588 enum missValue = select!haveReturnType(null, false); 589 590 auto p = key in lookup; 591 if (!p) 592 return missValue; 593 594 static if (haveReturnType) 595 return &lookupToReturnValue((*p)[$-1]); 596 else 597 return true; 598 } 599 600 /// Index operator. 601 /// The key must exist. Indexing with a key which does not exist 602 /// is an error. 603 static if (haveIndexing) 604 ref inout(IV) opIndex()(auto ref IK k) inout 605 { 606 static if (haveValues) 607 return lookupToReturnValue(lookup[k][$-1]); 608 else 609 return items[k].returnValue; 610 } 611 612 /// Retrieve last value associated with key, or `defaultValue` if none. 613 static if (haveIndexing) 614 auto ref inout(IV) get()(auto ref IK k, auto ref inout(IV) defaultValue) inout 615 { 616 static if (haveValues) 617 { 618 auto p = k in lookup; 619 return p ? lookupToReturnValue((*p)[$-1]) : defaultValue; 620 } 621 else 622 return k < items.length ? items[k].returnValue : defaultValue; 623 } 624 625 // *** Query (ranges) *** 626 627 /// Return a range which iterates over key/value pairs. 628 static if (haveValues) 629 auto byKeyValue(this This)() 630 { 631 static if (ordered) 632 return items; 633 else 634 { 635 return lookup 636 .byKeyValue 637 .map!(pair => 638 pair 639 .value 640 .map!(value => KeyValuePair!(K, V)(pair.key, value)) 641 ) 642 .joiner; 643 } 644 } 645 646 /// ditto 647 static if (haveValues) 648 auto byPair(this This)() 649 { 650 return byKeyValue 651 .map!(pair => tuple!("key", "value")(pair.key, pair.value)); 652 } 653 654 /// Return a range which iterates over all keys. 655 /// Duplicate keys will occur several times in the range. 656 auto byKey(this This)() 657 { 658 static if (ordered) 659 { 660 static ref getKey(MItem)(ref MItem item) { return item.key; } 661 return items.map!getKey; 662 } 663 else 664 { 665 return lookup 666 .byKeyValue 667 .map!(pair => 668 pair.key.repeat(SM.length(pair.value)) 669 ) 670 .joiner; 671 } 672 } 673 674 /// Return a range which iterates over all values. 675 static if (haveValues) 676 auto byValue(this This)() 677 { 678 static if (ordered) 679 { 680 static ref getValue(MItem)(ref MItem item) { return item.value; } 681 return items.map!getValue; 682 } 683 else 684 { 685 return lookup 686 .byKeyValue 687 .map!(pair => 688 pair 689 .value 690 ) 691 .joiner; 692 } 693 } 694 695 /// Returns all keys as an array. 696 @property auto keys(this This)() { return byKey.array; } 697 698 /// Returns all values as an array. 699 @property auto values(this This)() { return byValue.array; } 700 701 // *** Query (search by key) *** 702 703 static if (ordered) 704 { 705 /// Returns index of key `k`. 706 size_t indexOf()(auto ref const K k) 707 { 708 auto p = k in lookup; 709 return p ? (*p)[0] : -1; 710 } 711 712 /// Returns all indices of key `k`. 713 size_t[] indicesOf()(auto ref const K k) 714 { 715 auto p = k in lookup; 716 return p ? (*p)[] : null; 717 } 718 } 719 720 /// Return the number of items with the given key. 721 /// When multi==false, always returns 0 or 1. 722 size_t count()(auto ref K k) 723 { 724 static if (ordered) 725 return indicesOf(k).length; 726 else 727 { 728 auto p = k in lookup; 729 return p ? SM.length(*p) : 0; 730 } 731 } 732 733 /// Return a range with all values with the given key. 734 /// If the key is not present, returns an empty range. 735 static if (haveValues) 736 auto byValueOf(this This)(auto ref K k) 737 { 738 static if (ordered) 739 return indicesOf(k).map!(index => items[index].value); 740 else 741 return valuesOf(k); 742 } 743 744 /// Return an array with all values with the given key. 745 /// If the key is not present, returns an empty array. 746 static if (haveValues) 747 V[] valuesOf()(auto ref K k) 748 { 749 static if (ordered) 750 return byValueOf(k).array; 751 else 752 { 753 static if (multi) 754 return lookup.get(k, null); 755 else 756 { 757 auto p = k in lookup; 758 return p ? (*p)[] : null; 759 } 760 } 761 } 762 763 static if (haveValues) 764 deprecated alias getAll = valuesOf; 765 766 // *** Iteration *** 767 768 // Note: When iterating over keys in an AA, you must choose 769 // mutable OR ref, but not both. This is an important reason for 770 // the complexity below. 771 772 private enum isParameterRef(size_t index, fun...) = (){ 773 foreach (keyStorageClass; __traits(getParameterStorageClasses, fun[0], index)) 774 if (keyStorageClass == "ref") 775 return true; 776 return false; 777 }(); 778 779 private int opApplyImpl(this This, Dg)(scope Dg dg) 780 { 781 enum single = arity!dg == 1; 782 783 int result = 0; 784 785 static if (ordered) 786 { 787 foreach (ref item; items) 788 { 789 static if (single) 790 result = dg(item.returnValue); 791 else 792 result = dg(item.key, item.value); 793 if (result) 794 break; 795 } 796 } 797 else 798 { 799 static if (single && haveValues) 800 { 801 // Dg accepts value only, so use whatever we want for the key iteration. 802 alias LK = const(K); 803 enum useRef = true; 804 } 805 else 806 { 807 // Dg accepts a key (and maybe a value), so use the Dg signature for iteration. 808 alias LK = Parameters!Dg[0]; 809 enum useRef = isParameterRef!(0, Dg); 810 } 811 // LookupValue or const(LookupValue), depending on the constness of This 812 alias LV = typeof(lookup.values[0]); 813 814 bool handle()(ref LK key, ref LV values) 815 { 816 static if (haveValues) 817 { 818 foreach (ref value; values) 819 { 820 static if (single) 821 result = dg(value); 822 else 823 result = dg(key, value); 824 if (result) 825 return false; 826 } 827 } 828 else 829 { 830 foreach (iteration; 0 .. SM.length(values)) 831 { 832 static assert(single); 833 result = dg(key); 834 if (result) 835 return false; 836 } 837 } 838 return true; 839 } 840 841 static if (useRef) 842 { 843 foreach (ref LK key, ref LV values; lookup) 844 if (!handle(key, values)) 845 break; 846 } 847 else 848 { 849 foreach (LK key, ref LV values; lookup) 850 if (!handle(key, values)) 851 break; 852 } 853 } 854 return result; 855 } 856 857 private alias KeyIterationType(bool isConst, bool byRef) = typeof(*(){ 858 859 static if (isConst) 860 const bool[K] aa; 861 else 862 bool[K] aa; 863 864 static if (byRef) 865 foreach (ref k, v; aa) 866 return &k; 867 else 868 foreach (k, v; aa) 869 return &k; 870 871 assert(false); 872 }()); 873 874 private enum needRefOverload(bool isConst) = 875 // Unfortunately this doesn't work: https://issues.dlang.org/show_bug.cgi?id=21683 876 // !is(KeyIterationType!(isConst, false) == KeyIterationType!(isConst, true)); 877 !isCopyable!K; 878 879 private template needIter(bool isConst, bool byRef) 880 { 881 static if (!isCopyable!K) 882 enum needIter = byRef; 883 else 884 static if (!byRef) 885 enum needIter = true; 886 else 887 enum needIter = needRefOverload!isConst; 888 } 889 890 static if (haveValues) 891 { 892 /// Iterate over values (maps). 893 int opApply(scope int delegate( ref V) dg) { return opApplyImpl(dg); } 894 int opApply(scope int delegate(const ref V) dg) const { return opApplyImpl(dg); } /// ditto 895 } 896 else 897 { 898 /// Iterate over keys (sets). 899 static if (needIter!(false, false)) int opApply(scope int delegate( KeyIterationType!(false, false)) dg) { return opApplyImpl(dg); } 900 static if (needIter!(true , false)) int opApply(scope int delegate( KeyIterationType!(true , false)) dg) const { return opApplyImpl(dg); } /// ditto 901 static if (needIter!(false, true )) int opApply(scope int delegate(ref KeyIterationType!(false, true )) dg) { return opApplyImpl(dg); } /// ditto 902 static if (needIter!(true , true )) int opApply(scope int delegate(ref KeyIterationType!(true , true )) dg) const { return opApplyImpl(dg); } /// ditto 903 } 904 905 static if (haveValues) 906 { 907 /// Iterate over keys and values. 908 static if (needIter!(false, false)) int opApply(scope int delegate( KeyIterationType!(false, false), ref V) dg) { return opApplyImpl(dg); } 909 static if (needIter!(true , false)) int opApply(scope int delegate( KeyIterationType!(true , false), const ref V) dg) const { return opApplyImpl(dg); } /// ditto 910 static if (needIter!(false, true )) int opApply(scope int delegate(ref KeyIterationType!(false, true ), ref V) dg) { return opApplyImpl(dg); } /// ditto 911 static if (needIter!(true , true )) int opApply(scope int delegate(ref KeyIterationType!(true , true ), const ref V) dg) const { return opApplyImpl(dg); } /// ditto 912 } 913 914 private struct ByRef(bool isConst) 915 { 916 static if (isConst) 917 const(HashCollection)* c; 918 else 919 HashCollection* c; 920 921 static if (haveValues) 922 { 923 static if (isConst) 924 int opApply(scope int delegate(ref KeyIterationType!(true , true ), const ref V) dg) const { return c.opApplyImpl(dg); } 925 else 926 int opApply(scope int delegate(ref KeyIterationType!(false, true ), ref V) dg) { return c.opApplyImpl(dg); } 927 } 928 else 929 { 930 static if (isConst) 931 int opApply(scope int delegate(ref KeyIterationType!(true , true )) dg) const { return c.opApplyImpl(dg); } 932 else 933 int opApply(scope int delegate(ref KeyIterationType!(false, true )) dg) { return c.opApplyImpl(dg); } 934 } 935 } 936 937 /// Returns an object that allows iterating over this collection with ref keys. 938 /// Workaround for https://issues.dlang.org/show_bug.cgi?id=21683 939 auto byRef() return { return ByRef!false(&this); } 940 auto byRef() const return { return ByRef!true (&this); } /// ditto 941 942 // *** Mutation (addition) *** 943 944 private enum AddMode 945 { 946 add, /// Always add value 947 replace, /// Replace all previous values 948 require, /// Only add value if it did not exist before 949 } 950 951 private ref ReturnType!void addImpl(AddMode mode, AK, GV)(ref AK key, scope GV getValue) 952 if (is(AK : K)) 953 { 954 static if (ordered) 955 { 956 size_t addedIndex; 957 958 static if (multi && mode == AddMode.add) 959 { 960 addedIndex = items.length; 961 lookup[key] ~= addedIndex; 962 items ~= Item(key, getValue()); 963 } 964 else 965 { 966 lookup.updateVoid(key, 967 delegate LookupValue() 968 { 969 addedIndex = items.length; 970 items ~= Item(key, getValue()); 971 return [addedIndex]; 972 }, 973 delegate void(ref LookupValue existingIndex) 974 { 975 addedIndex = existingIndex[0]; 976 static if (mode != AddMode.require) 977 { 978 static if (multi) 979 { 980 static assert(mode == AddMode.replace); 981 existingIndex = existingIndex[0 .. 1]; 982 } 983 items[addedIndex].value = getValue(); 984 } 985 }); 986 } 987 988 return items[addedIndex].returnValue; 989 } 990 else // ordered 991 { 992 static if (haveValues) 993 { 994 static if (mode == AddMode.require) 995 return (lookup.require(key, [getValue()]))[0]; 996 else 997 static if (multi && mode == AddMode.add) 998 return (lookup[key] ~= getValue())[$-1]; 999 else 1000 return (lookup[key] = [getValue()])[0]; 1001 } 1002 else 1003 { 1004 static if (multi) 1005 { 1006 static if (mode == AddMode.require) 1007 lookup.require(key, 1); 1008 else 1009 static if (mode == AddMode.add) 1010 lookup[key]++; 1011 else 1012 lookup[key] = 1; 1013 } 1014 else 1015 lookup[key] = LookupValue.init; 1016 // This branch returns void, as there is no reasonable 1017 // ref to an AA key that we can return here. 1018 } 1019 } 1020 } 1021 1022 /*private*/ template _addSetFunc(AddMode mode) 1023 { 1024 static if (haveValues) 1025 { 1026 ref ReturnType!void _addSetFunc(AK, AV)(auto ref AK key, auto ref AV value) 1027 if (is(AK : K) && is(AV : V)) 1028 { 1029 return addImpl!mode(key, () => value); 1030 } 1031 } 1032 else 1033 { 1034 ref ReturnType!void _addSetFunc(AK)(auto ref AK key) 1035 if (is(AK : K)) 1036 { 1037 ValueVarType value; // void[0] 1038 return addImpl!mode(key, () => value); 1039 } 1040 } 1041 } 1042 1043 /// Add an item. 1044 alias add = _addSetFunc!(AddMode.add); 1045 1046 /// Ensure a key exists (with the given value). 1047 /// When `multi==true`, replaces all previous entries with this key. 1048 /// Otherwise, behaves identically to `add`. 1049 alias set = _addSetFunc!(AddMode.replace); 1050 1051 /// Add `value` only if `key` is not present. 1052 static if (haveValues) 1053 ref V require()(auto ref K key, lazy V value = V.init) 1054 { 1055 return addImpl!(AddMode.require)(key, () => value); 1056 } 1057 1058 deprecated alias getOrAdd = require; 1059 1060 private alias UpdateFuncRT(U) = typeof({ U u = void; V v = void; return u(v); }()); 1061 1062 /// If `key` is present, call `update` for every value; 1063 /// otherwise, add new value with `create`. 1064 static if (haveValues) 1065 void update(C, U)(auto ref K key, scope C create, scope U update) 1066 if (is(typeof(create()) : V) && (is(UpdateFuncRT!U : V) || is(UpdateFuncRT!U == void))) 1067 { 1068 static if (ordered) 1069 { 1070 lookup.updateVoid(key, 1071 delegate LookupValue() 1072 { 1073 auto addedIndex = items.length; 1074 items ~= Item(key, create()); 1075 return [addedIndex]; 1076 }, 1077 delegate void(ref LookupValue existingIndex) 1078 { 1079 foreach (i; existingIndex) 1080 static if (is(UpdateFuncRT!U == void)) 1081 update(items[i].value); 1082 else 1083 items[i].value = update(items[i].value); 1084 }); 1085 } 1086 else // ordered 1087 { 1088 lookup.updateVoid(key, 1089 delegate LookupValue () 1090 { 1091 return [create()]; 1092 }, 1093 delegate void (ref LookupValue values) 1094 { 1095 foreach (ref value; values) 1096 static if (is(UpdateFuncRT!U == void)) 1097 update(value); 1098 else 1099 value = update(value); 1100 }); 1101 } 1102 } 1103 1104 // *** Mutation (editing) *** 1105 1106 static if (haveIndexing) 1107 { 1108 static if (haveValues) 1109 { 1110 /// Same as `set(k, v)`. 1111 ref IV opIndexAssign(AK, AV)(auto ref AV v, auto ref AK k) 1112 if (is(AK : K) && is(AV : V)) 1113 { 1114 return set(k, v); 1115 } 1116 1117 /// Perform cumulative operation with value 1118 /// (initialized with `.init` if the key does not exist). 1119 ref IV opIndexOpAssign(string op, AK, AV)(auto ref AV v, auto ref AK k) 1120 if (is(AK : K) && is(AV : V)) 1121 { 1122 auto pv = &require(k); 1123 return mixin("(*pv) " ~ op ~ "= v"); 1124 } 1125 1126 /// Perform unary operation with value 1127 /// (initialized with `.init` if the key does not exist). 1128 ref IV opIndexUnary(string op, AK)(auto ref AK k) 1129 if (is(AK : K)) 1130 { 1131 auto pv = &require(k); 1132 mixin("(*pv) " ~ op ~ ";"); 1133 return *pv; 1134 } 1135 } 1136 else 1137 { 1138 private ref K editIndex(size_t index, scope void delegate(ref K) edit) 1139 { 1140 auto item = &items[index]; 1141 K oldKey = item.key; 1142 auto pOldIndices = oldKey in lookup; 1143 assert(pOldIndices); 1144 1145 edit(item.key); 1146 1147 // Add new value 1148 1149 lookup.updateVoid(item.key, 1150 delegate LookupValue() 1151 { 1152 // New value did not exist. 1153 if ((*pOldIndices).length == 1) 1154 { 1155 // Optimization - migrate the Indexes value 1156 assert((*pOldIndices)[0] == index); 1157 return *pOldIndices; 1158 } 1159 else 1160 return [index]; 1161 }, 1162 delegate void(ref LookupValue existingIndex) 1163 { 1164 // Value(s) with the new key already existed 1165 static if (multi) 1166 existingIndex ~= index; 1167 else 1168 assert(false, "Collision after in-place edit of a non-multi ordered set element"); 1169 }); 1170 1171 // Remove old value 1172 1173 if ((*pOldIndices).length == 1) 1174 lookup.remove(oldKey); 1175 else 1176 static if (multi) 1177 *pOldIndices = (*pOldIndices).remove!(i => i == index); 1178 else 1179 assert(false); // Should be unreachable (`if` above will always be true) 1180 1181 return item.key; 1182 } 1183 1184 /// Allows writing to ordered sets by index. 1185 /// The total number of elements never changes as a result 1186 /// of such an operation - a consequence of which is that 1187 /// if multi==false, changing the value to one that's 1188 /// already in the set is an error. 1189 ref IV opIndexAssign()(auto ref IV v, auto ref IK k) 1190 { 1191 static if (haveValues) 1192 return set(k, v); 1193 else 1194 return editIndex(k, (ref IV e) { e = v; }); 1195 } 1196 1197 /// Perform cumulative operation with value at index. 1198 ref IV opIndexOpAssign(string op)(auto ref VV v, auto ref IK k) 1199 { 1200 return editIndex(k, (ref IV e) { mixin("e " ~ op ~ "= v;"); }); 1201 } 1202 1203 /// Perform unary operation with value at index. 1204 ref IV opIndexUnary(string op)(auto ref IK k) 1205 { 1206 return editIndex(k, (ref IV e) { mixin("e " ~ op ~ ";"); }); 1207 } 1208 } 1209 } 1210 1211 // *** Mutation (removal) *** 1212 1213 /// Removes all elements with the given key. 1214 bool remove(AK)(auto ref AK key) 1215 if (is(typeof(lookup.remove(key)))) 1216 { 1217 static if (ordered) 1218 { 1219 auto p = key in lookup; 1220 if (!p) 1221 return false; 1222 1223 auto targets = *p; 1224 foreach (target; targets) 1225 { 1226 items = items.remove!(SwapStrategy.stable)(target); 1227 foreach (ref k, ref vs; lookup) 1228 foreach (ref v; vs) 1229 if (v > target) 1230 v--; 1231 } 1232 auto success = lookup.remove(key); 1233 assert(success); 1234 return true; 1235 } 1236 else 1237 return lookup.remove(key); 1238 } 1239 1240 /// Removes all elements. 1241 void clear() 1242 { 1243 lookup.clear(); 1244 static if (ordered) 1245 items.length = 0; 1246 } 1247 } 1248 1249 /// An associative array which retains the order in which elements were added. 1250 alias OrderedMap(K, V) = HashCollection!(K, V, true, false); 1251 1252 unittest 1253 { 1254 alias M = OrderedMap!(string, int); 1255 M m; 1256 m["a"] = 1; 1257 m["b"] = 2; 1258 m["c"] = 3; 1259 assert(m.length == 3); 1260 assert("a" in m); 1261 assert("d" !in m); 1262 1263 { 1264 auto r = m.byKeyValue; 1265 assert(!r.empty); 1266 assert(r.front.key == "a"); 1267 r.popFront(); 1268 assert(!r.empty); 1269 assert(r.front.key == "b"); 1270 r.popFront(); 1271 assert(!r.empty); 1272 assert(r.front.key == "c"); 1273 r.popFront(); 1274 assert(r.empty); 1275 } 1276 1277 assert(m.byKey.equal(["a", "b", "c"])); 1278 assert(m.byValue.equal([1, 2, 3])); 1279 assert(m.byKeyValue.map!(p => p.key).equal(m.byKey)); 1280 assert(m.byKeyValue.map!(p => p.value).equal(m.byValue)); 1281 assert(m.keys == ["a", "b", "c"]); 1282 assert(m.values == [1, 2, 3]); 1283 1284 { 1285 const(M)* c = &m; 1286 assert(c.byKey.equal(["a", "b", "c"])); 1287 assert(c.byValue.equal([1, 2, 3])); 1288 assert(c.keys == ["a", "b", "c"]); 1289 assert(c.values == [1, 2, 3]); 1290 } 1291 1292 m.byValue.front = 5; 1293 assert(m.byValue.equal([5, 2, 3])); 1294 1295 m.remove("a"); 1296 assert(m.length == 2); 1297 m["x"] -= 1; 1298 assert(m["x"] == -1); 1299 ++m["y"]; 1300 assert(m["y"] == 1); 1301 auto cm = cast(const)m.dup; 1302 foreach (k, v; cm) 1303 if (k == "x") 1304 assert(v == -1); 1305 } 1306 1307 unittest 1308 { 1309 OrderedMap!(string, int) m; 1310 m["a"] = 1; 1311 m["b"] = 2; 1312 m.remove("a"); 1313 assert(m["b"] == 2); 1314 } 1315 1316 unittest 1317 { 1318 OrderedMap!(string, int) m; 1319 m["a"] = 1; 1320 auto m2 = m; 1321 m2.remove("a"); 1322 m2["b"] = 2; 1323 assert(m["a"] == 1); 1324 } 1325 1326 unittest 1327 { 1328 OrderedMap!(string, int) m; 1329 m["a"] = 1; 1330 m["b"] = 2; 1331 auto m2 = m; 1332 m.remove("a"); 1333 assert(m2["a"] == 1); 1334 } 1335 1336 unittest 1337 { 1338 class C {} 1339 const OrderedMap!(string, C) m; 1340 cast(void)m.byKeyValue; 1341 } 1342 1343 unittest 1344 { 1345 OrderedMap!(int, int) m; 1346 m.update(10, 1347 { return 20; }, 1348 (ref int k) { k++; return 30; }, 1349 ); 1350 assert(m.length == 1 && m[10] == 20); 1351 m.update(10, 1352 { return 40; }, 1353 (ref int k) { k++; return 50; }, 1354 ); 1355 assert(m.length == 1 && m[10] == 50); 1356 } 1357 1358 // https://issues.dlang.org/show_bug.cgi?id=18606 1359 unittest 1360 { 1361 struct S 1362 { 1363 struct T 1364 { 1365 int foo; 1366 int[] bar; 1367 } 1368 1369 OrderedMap!(int, T) m; 1370 } 1371 } 1372 1373 unittest 1374 { 1375 OrderedMap!(string, int) m; 1376 static assert(is(typeof(m.keys))); 1377 static assert(is(typeof(m.values))); 1378 } 1379 1380 unittest 1381 { 1382 OrderedMap!(string, int) m; 1383 foreach (k, v; m) 1384 k = k ~ k; 1385 } 1386 1387 /// Like assocArray 1388 auto orderedMap(R)(R input) 1389 if (is(typeof(input.front.length) : size_t) && input.front.length == 2) 1390 { 1391 alias K = typeof(input.front[0]); 1392 alias V = typeof(input.front[1]); 1393 return OrderedMap!(K, V)(input); 1394 } 1395 1396 auto orderedMap(R)(R input) 1397 if (is(typeof(input.front.key)) && is(typeof(input.front.value)) && !is(typeof(input.front.length))) 1398 { 1399 alias K = typeof(input.front.key); 1400 alias V = typeof(input.front.value); 1401 return OrderedMap!(K, V)(input); 1402 } /// ditto 1403 1404 unittest 1405 { 1406 auto map = 3.iota.map!(n => tuple(n, n + 1)).orderedMap; 1407 assert(map.length == 3 && map[1] == 2); 1408 } 1409 1410 unittest 1411 { 1412 OrderedMap!(string, int) m; 1413 m = m.byKeyValue.orderedMap; 1414 m = m.byPair.orderedMap; 1415 } 1416 1417 // *************************************************************************** 1418 1419 /// Helper/wrapper for void[0][T] 1420 alias HashSet(T) = HashCollection!(T, void, false, false); 1421 1422 unittest 1423 { 1424 HashSet!int s; 1425 assert(!s); 1426 assert(s.length == 0); 1427 assert(!(1 in s)); 1428 assert(1 !in s); 1429 s.add(1); 1430 assert(1 in s); 1431 assert(s.length == 1); 1432 foreach (k; s) 1433 assert(k == 1); 1434 foreach (ref k; s.byRef) 1435 assert(k == 1); 1436 s.remove(1); 1437 assert(s.length == 0); 1438 1439 s.add(1); 1440 auto t = s.dup; 1441 s.add(2); 1442 assert(t.length==1); 1443 t.remove(1); 1444 assert(t.length==0); 1445 } 1446 1447 unittest 1448 { 1449 struct S { int[int] aa; } 1450 HashSet!S set; 1451 S s; 1452 set.add(s); 1453 assert(s in set); 1454 } 1455 1456 /// Construct a set from the range `r`. 1457 auto toSet(R)(R r) 1458 { 1459 alias E = ElementType!R; 1460 return HashSet!E(r); 1461 } 1462 1463 unittest 1464 { 1465 auto set = [1, 2, 3].toSet(); 1466 assert(2 in set); 1467 assert(4 !in set); 1468 } 1469 1470 unittest 1471 { 1472 HashSet!int m; 1473 const int i; 1474 m.remove(i); 1475 } 1476 1477 unittest 1478 { 1479 HashSet!Object m; 1480 Object o; 1481 m.remove(o); 1482 } 1483 1484 unittest 1485 { 1486 struct S 1487 { 1488 @disable this(this); 1489 } 1490 1491 HashSet!S set; 1492 } 1493 1494 // *************************************************************************** 1495 1496 /// An ordered set of `T`, which retains 1497 /// the order in which elements are added. 1498 alias OrderedSet(T) = HashCollection!(T, void, true, false); 1499 1500 unittest 1501 { 1502 OrderedSet!int set; 1503 1504 assert(1 !in set); 1505 set.add(1); 1506 assert(1 in set); 1507 set.remove(1); 1508 assert(1 !in set); 1509 1510 set.add(1); 1511 set.clear(); 1512 assert(1 !in set); 1513 1514 set = set.init; 1515 assert(!set); 1516 set.add(1); 1517 assert(!!set); 1518 1519 assert(set[0] == 1); 1520 set[0] = 2; 1521 assert(set[0] == 2); 1522 assert(1 !in set); 1523 assert(2 in set); 1524 1525 assert(set.length == 1); 1526 set.remove(2); 1527 assert(set.length == 0); 1528 1529 set.add(1); 1530 auto set2 = set; 1531 set.remove(1); 1532 set.add(2); 1533 assert(1 !in set && 2 in set); 1534 assert(1 in set2 && 2 !in set2); 1535 1536 foreach (v; set) 1537 assert(v == 2); 1538 } 1539 1540 /// Construct an ordered set from the range `r`. 1541 auto orderedSet(R)(R r) 1542 { 1543 alias E = ElementType!R; 1544 return OrderedSet!E(r); 1545 } 1546 1547 // *************************************************************************** 1548 1549 /// An object which acts mostly as an associative array, 1550 /// with the added property of being able to hold keys with 1551 /// multiple values. These are only exposed explicitly and 1552 /// through iteration 1553 alias MultiAA(K, V) = HashCollection!(K, V, false, true); 1554 1555 unittest 1556 { 1557 alias MASS = MultiAA!(string, int); 1558 MASS aa; 1559 aa.add("foo", 42); 1560 assert(aa["foo"] == 42); 1561 assert(aa.valuesOf("foo") == [42]); 1562 assert(aa.byPair.front.key == "foo"); 1563 1564 auto aa2 = MASS([tuple("foo", 42)]); 1565 aa2 = ["a":1,"b":2]; 1566 1567 const int i; 1568 aa["a"] = i; 1569 } 1570 1571 unittest 1572 { 1573 MultiAA!(int, int) m; 1574 int[][int] a; 1575 m = a; 1576 }