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