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