1 //_ aaA.d 2 3 /** 4 * Part of the D programming language runtime library. 5 * Implementation of associative arrays. 6 */ 7 8 /* 9 * Copyright (C) 2000-2008 by Digital Mars, http://www.digitalmars.com 10 * Written by Walter Bright 11 * 12 * This software is provided 'as-is', without any express or implied 13 * warranty. In no event will the authors be held liable for any damages 14 * arising from the use of this software. 15 * 16 * Permission is granted to anyone to use this software for any purpose, 17 * including commercial applications, and to alter it and redistribute it 18 * freely, subject to the following restrictions: 19 * 20 * o The origin of this software must not be misrepresented; you must not 21 * claim that you wrote the original software. If you use this software 22 * in a product, an acknowledgment in the product documentation would be 23 * appreciated but is not required. 24 * o Altered source versions must be plainly marked as such, and must not 25 * be misrepresented as being the original software. 26 * o This notice may not be removed or altered from any source 27 * distribution. 28 */ 29 30 /* NOTE: This file has been patched from the original DMD distribution to 31 work with the GDC compiler. 32 33 Modified by David Friedman, September 2004 34 */ 35 36 /* 37 * Modified by Sean Kelly <sean@f4.ca> for use with Tango. 38 */ 39 40 module rt.compiler.gdc.rt.aaA; 41 42 private 43 { 44 import tango.stdc.string; 45 46 enum BlkAttr : uint 47 { 48 FINALIZE = 0b0000_0001, 49 NO_SCAN = 0b0000_0010, 50 NO_MOVE = 0b0000_0100, 51 ALL_BITS = 0b1111_1111 52 } 53 54 extern (C) void* gc_malloc( size_t sz, uint ba = 0, PointerMap bitMask = PointerMap.init); 55 extern (C) void* gc_calloc( size_t sz, uint ba = 0, PointerMap bitMask = PointerMap.init); 56 extern (C) void gc_free( void* p ); 57 } 58 59 // Auto-rehash and pre-allocate - Dave Fladebo 60 61 static size_t[] prime_list = [ 62 97UL, 389UL, 63 1_543UL, 6_151UL, 64 24_593UL, 98_317UL, 65 393_241UL, 1_572_869UL, 66 6_291_469UL, 25_165_843UL, 67 100_663_319UL, 402_653_189UL, 68 1_610_612_741UL, 4_294_967_291UL, 69 // 8_589_934_513UL, 17_179_869_143UL 70 ]; 71 72 /* This is the type of the return value for dynamic arrays. 73 * It should be a type that is returned in registers. 74 * Although DMD will return types of Array in registers, 75 * gcc will not, so we instead use a 'long'. 76 */ 77 alias void[] ArrayRet_t; 78 79 struct Array 80 { 81 size_t length; 82 void* ptr; 83 } 84 85 struct aaA 86 { 87 aaA *left; 88 aaA *right; 89 hash_t hash; 90 /* key */ 91 /* value */ 92 } 93 94 struct BB 95 { 96 aaA*[] b; 97 size_t nodes; // total number of aaA nodes 98 TypeInfo keyti; // TODO: replace this with TypeInfo_AssociativeArray when available in _aaGet() 99 } 100 101 /* This is the type actually seen by the programmer, although 102 * it is completely opaque. 103 */ 104 105 struct AA 106 { 107 BB* a; 108 } 109 110 /********************************** 111 * Align to next pointer boundary, so that 112 * GC won't be faced with misaligned pointers 113 * in value. 114 */ 115 116 size_t aligntsize(size_t tsize) 117 { 118 version (X86_64) 119 // Size of key needed to align value on 16 bytes 120 return (tsize + 15) & ~(15); 121 else 122 return (tsize + size_t.sizeof - 1) & ~(size_t.sizeof - 1); 123 } 124 125 extern (C): 126 127 /************************************************* 128 * Invariant for aa. 129 */ 130 131 /+ 132 void _aaInvAh(aaA*[] aa) 133 { 134 for (size_t i = 0; i < aa.length; i++) 135 { 136 if (aa[i]) 137 _aaInvAh_x(aa[i]); 138 } 139 } 140 141 private int _aaCmpAh_x(aaA *e1, aaA *e2) 142 { int c; 143 144 c = e1.hash - e2.hash; 145 if (c == 0) 146 { 147 c = e1.key.length - e2.key.length; 148 if (c == 0) 149 c = memcmp((char *)e1.key, (char *)e2.key, e1.key.length); 150 } 151 return c; 152 } 153 154 private void _aaInvAh_x(aaA *e) 155 { 156 hash_t key_hash; 157 aaA *e1; 158 aaA *e2; 159 160 key_hash = getHash(e.key); 161 assert(key_hash == e.hash); 162 163 while (1) 164 { int c; 165 166 e1 = e.left; 167 if (e1) 168 { 169 _aaInvAh_x(e1); // ordinary recursion 170 do 171 { 172 c = _aaCmpAh_x(e1, e); 173 assert(c < 0); 174 e1 = e1.right; 175 } while (e1 != null); 176 } 177 178 e2 = e.right; 179 if (e2) 180 { 181 do 182 { 183 c = _aaCmpAh_x(e, e2); 184 assert(c < 0); 185 e2 = e2.left; 186 } while (e2 != null); 187 e = e.right; // tail recursion 188 } 189 else 190 break; 191 } 192 } 193 +/ 194 195 /**************************************************** 196 * Determine number of entries in associative array. 197 */ 198 199 size_t _aaLen(AA aa) 200 in 201 { 202 //printf("_aaLen()+\n"); 203 //_aaInv(aa); 204 } 205 out (result) 206 { 207 size_t len = 0; 208 209 void _aaLen_x(aaA* ex) 210 { 211 auto e = ex; 212 len++; 213 214 while (1) 215 { 216 if (e.right) 217 _aaLen_x(e.right); 218 e = e.left; 219 if (!e) 220 break; 221 len++; 222 } 223 } 224 225 if (aa.a) 226 { 227 foreach (e; aa.a.b) 228 { 229 if (e) 230 _aaLen_x(e); 231 } 232 } 233 assert(len == result); 234 235 //printf("_aaLen()-\n"); 236 } 237 body 238 { 239 return aa.a ? aa.a.nodes : 0; 240 } 241 242 243 /************************************************* 244 * Get pointer to value in associative array indexed by key. 245 * Add entry for key if it is not already there. 246 */ 247 248 void *_aaGetp(AA* aa, TypeInfo keyti, size_t valuesize, void *pkey) 249 in 250 { 251 assert(aa); 252 } 253 out (result) 254 { 255 assert(result); 256 assert(aa.a); 257 assert(aa.a.b.length); 258 //assert(_aaInAh(*aa.a, key)); 259 } 260 body 261 { 262 size_t i; 263 aaA *e; 264 auto keysize = aligntsize(keyti.tsize()); 265 266 if (!aa.a) 267 aa.a = new BB(); 268 269 aa.a.keyti = keyti; 270 271 if (!aa.a.b.length) 272 { 273 alias aaA *pa; 274 auto len = prime_list[0]; 275 276 aa.a.b = new pa[len]; 277 } 278 279 auto key_hash = keyti.getHash(pkey); 280 281 //printf("hash = %d\n", key_hash); 282 283 i = key_hash % aa.a.b.length; 284 auto pe = &aa.a.b[i]; 285 286 while ((e = *pe) !is null) 287 { 288 if (key_hash == e.hash) 289 { 290 auto c = keyti.compare(pkey, e + 1); 291 if (c == 0) 292 goto Lret; 293 pe = (c < 0) ? &e.left : &e.right; 294 } 295 else 296 pe = (key_hash < e.hash) ? &e.left : &e.right; 297 } 298 299 // Not found, create new elem 300 //printf("create new one\n"); 301 size_t size = aaA.sizeof + keysize + valuesize; 302 e = cast(aaA *) gc_calloc(size); 303 memcpy(e + 1, pkey, keysize); 304 e.hash = key_hash; 305 *pe = e; 306 307 auto nodes = ++aa.a.nodes; 308 //printf("length = %d, nodes = %d\n", (*aa.a).length, nodes); 309 if (nodes > aa.a.b.length * 4) 310 { 311 _aaRehash(aa,keyti); 312 } 313 314 Lret: 315 return cast(void *)(e + 1) + keysize; 316 } 317 318 319 /************************************************* 320 * Get pointer to value in associative array indexed by key. 321 * Returns null if it is not already there. 322 */ 323 324 void *_aaGetRvaluep(AA aa, TypeInfo keyti, size_t valuesize, void *pkey) 325 { 326 //printf("_aaGetRvalue(valuesize = %u)\n", valuesize); 327 if (!aa.a) 328 return null; 329 330 auto keysize = aligntsize(keyti.tsize()); 331 auto len = aa.a.b.length; 332 333 if (len) 334 { 335 auto key_hash = keyti.getHash(pkey); 336 //printf("hash = %d\n", key_hash); 337 size_t i = key_hash % len; 338 auto e = aa.a.b[i]; 339 while (e !is null) 340 { 341 if (key_hash == e.hash) 342 { 343 auto c = keyti.compare(pkey, e + 1); 344 if (c == 0) 345 return cast(void *)(e + 1) + keysize; 346 e = (c < 0) ? e.left : e.right; 347 } 348 else 349 e = (key_hash < e.hash) ? e.left : e.right; 350 } 351 } 352 return null; // not found, caller will throw exception 353 } 354 355 356 /************************************************* 357 * Determine if key is in aa. 358 * Returns: 359 * null not in aa 360 * !=null in aa, return pointer to value 361 */ 362 363 void* _aaInp(AA aa, TypeInfo keyti, void *pkey) 364 in 365 { 366 } 367 out (result) 368 { 369 //assert(result == 0 || result == 1); 370 } 371 body 372 { 373 if (aa.a) 374 { 375 //printf("_aaIn(), .length = %d, .ptr = %x\n", aa.a.length, cast(uint)aa.a.ptr); 376 auto len = aa.a.b.length; 377 378 if (len) 379 { 380 auto key_hash = keyti.getHash(pkey); 381 //printf("hash = %d\n", key_hash); 382 size_t i = key_hash % len; 383 auto e = aa.a.b[i]; 384 while (e !is null) 385 { 386 if (key_hash == e.hash) 387 { 388 auto c = keyti.compare(pkey, e + 1); 389 if (c == 0) 390 return cast(void *)(e + 1) + aligntsize(keyti.tsize()); 391 e = (c < 0) ? e.left : e.right; 392 } 393 else 394 e = (key_hash < e.hash) ? e.left : e.right; 395 } 396 } 397 } 398 399 // Not found 400 return null; 401 } 402 403 /************************************************* 404 * Delete key entry in aa[]. 405 * If key is not in aa[], do nothing. 406 */ 407 408 void _aaDelp(AA aa, TypeInfo keyti, void *pkey) 409 { 410 aaA *e; 411 412 if (aa.a && aa.a.b.length) 413 { 414 auto key_hash = keyti.getHash(pkey); 415 //printf("hash = %d\n", key_hash); 416 size_t i = key_hash % aa.a.b.length; 417 auto pe = &aa.a.b[i]; 418 while ((e = *pe) !is null) // null means not found 419 { 420 if (key_hash == e.hash) 421 { 422 auto c = keyti.compare(pkey, e + 1); 423 if (c == 0) 424 { 425 if (!e.left && !e.right) 426 { 427 *pe = null; 428 } 429 else if (e.left && !e.right) 430 { 431 *pe = e.left; 432 e.left = null; 433 } 434 else if (!e.left && e.right) 435 { 436 *pe = e.right; 437 e.right = null; 438 } 439 else 440 { 441 *pe = e.left; 442 e.left = null; 443 do 444 pe = &(*pe).right; 445 while (*pe); 446 *pe = e.right; 447 e.right = null; 448 } 449 450 aa.a.nodes--; 451 gc_free(e); 452 break; 453 } 454 pe = (c < 0) ? &e.left : &e.right; 455 } 456 else 457 pe = (key_hash < e.hash) ? &e.left : &e.right; 458 } 459 } 460 } 461 462 463 /******************************************** 464 * Produce array of values from aa. 465 */ 466 467 ArrayRet_t _aaValues(AA aa, size_t keysize, size_t valuesize) 468 in 469 { 470 assert(keysize == aligntsize(keysize)); 471 } 472 body 473 { 474 size_t resi; 475 Array a; 476 477 void _aaValues_x(aaA* e) 478 { 479 do 480 { 481 memcpy(a.ptr + resi * valuesize, 482 cast(byte*)e + aaA.sizeof + keysize, 483 valuesize); 484 resi++; 485 if (e.left) 486 { if (!e.right) 487 { e = e.left; 488 continue; 489 } 490 _aaValues_x(e.left); 491 } 492 e = e.right; 493 } while (e !is null); 494 } 495 496 if (aa.a) 497 { 498 a.length = _aaLen(aa); 499 a.ptr = cast(byte*) gc_malloc(a.length * valuesize, 500 valuesize < (void*).sizeof ? BlkAttr.NO_SCAN : 0); 501 resi = 0; 502 foreach (e; aa.a.b) 503 { 504 if (e) 505 _aaValues_x(e); 506 } 507 assert(resi == a.length); 508 } 509 return *cast(ArrayRet_t*)(&a); 510 } 511 512 513 /******************************************** 514 * Rehash an array. 515 */ 516 517 AA _aaRehash(AA* paa, TypeInfo keyti) 518 in 519 { 520 //_aaInvAh(paa); 521 } 522 out (result) 523 { 524 //_aaInvAh(result); 525 } 526 body 527 { 528 BB newb; 529 530 void _aaRehash_x(aaA* olde) 531 { 532 while (1) 533 { 534 auto left = olde.left; 535 auto right = olde.right; 536 olde.left = null; 537 olde.right = null; 538 539 aaA *e; 540 541 //printf("rehash %p\n", olde); 542 auto key_hash = olde.hash; 543 size_t i = key_hash % newb.b.length; 544 auto pe = &newb.b[i]; 545 while ((e = *pe) !is null) 546 { 547 //printf("\te = %p, e.left = %p, e.right = %p\n", e, e.left, e.right); 548 assert(e.left != e); 549 assert(e.right != e); 550 if (key_hash == e.hash) 551 { 552 auto c = keyti.compare(olde + 1, e + 1); 553 assert(c != 0); 554 pe = (c < 0) ? &e.left : &e.right; 555 } 556 else 557 pe = (key_hash < e.hash) ? &e.left : &e.right; 558 } 559 *pe = olde; 560 561 if (right) 562 { 563 if (!left) 564 { olde = right; 565 continue; 566 } 567 _aaRehash_x(right); 568 } 569 if (!left) 570 break; 571 olde = left; 572 } 573 } 574 575 //printf("Rehash\n"); 576 if (paa.a) 577 { 578 auto aa = paa.a; 579 auto len = _aaLen(*paa); 580 if (len) 581 { size_t i; 582 583 for (i = 0; i < prime_list.length - 1; i++) 584 { 585 if (len <= prime_list[i]) 586 break; 587 } 588 len = prime_list[i]; 589 newb.b = new aaA*[len]; 590 591 foreach (e; aa.b) 592 { 593 if (e) 594 _aaRehash_x(e); 595 } 596 delete aa.b; 597 598 newb.nodes = aa.nodes; 599 newb.keyti = aa.keyti; 600 } 601 602 *paa.a = newb; 603 _aaBalance(paa); 604 } 605 return *paa; 606 } 607 608 /******************************************** 609 * Balance an array. 610 */ 611 612 void _aaBalance(AA* paa) 613 { 614 //printf("_aaBalance()\n"); 615 if (paa.a) 616 { 617 aaA*[16] tmp; 618 aaA*[] array = tmp; 619 auto aa = paa.a; 620 foreach (j, e; aa.b) 621 { 622 /* Temporarily store contents of bucket in array[] 623 */ 624 size_t k = 0; 625 void addToArray(aaA* e) 626 { 627 while (e) 628 { addToArray(e.left); 629 if (k == array.length) 630 array.length = array.length * 2; 631 array[k++] = e; 632 e = e.right; 633 } 634 } 635 addToArray(e); 636 /* The contents of the bucket are now sorted into array[]. 637 * Rebuild the tree. 638 */ 639 void buildTree(aaA** p, size_t x1, size_t x2) 640 { 641 if (x1 >= x2) 642 *p = null; 643 else 644 { auto mid = (x1 + x2) >> 1; 645 *p = array[mid]; 646 buildTree(&(*p).left, x1, mid); 647 buildTree(&(*p).right, mid + 1, x2); 648 } 649 } 650 auto p = &aa.b[j]; 651 buildTree(p, 0, k); 652 } 653 } 654 } 655 656 /******************************************** 657 * Produce array of N byte keys from aa. 658 */ 659 660 ArrayRet_t _aaKeys(AA aa, size_t keysize) 661 { 662 byte[] res; 663 size_t resi; 664 665 void _aaKeys_x(aaA* e) 666 { 667 do 668 { 669 memcpy(&res[resi * keysize], cast(byte*)(e + 1), keysize); 670 resi++; 671 if (e.left) 672 { if (!e.right) 673 { e = e.left; 674 continue; 675 } 676 _aaKeys_x(e.left); 677 } 678 e = e.right; 679 } while (e !is null); 680 } 681 682 auto len = _aaLen(aa); 683 if (!len) 684 return null; 685 res = (cast(byte*) gc_malloc(len * keysize, 686 !(aa.a.keyti.flags() & 1) ? BlkAttr.NO_SCAN : 0))[0 .. len * keysize]; 687 resi = 0; 688 foreach (e; aa.a.b) 689 { 690 if (e) 691 _aaKeys_x(e); 692 } 693 assert(resi == len); 694 695 Array a; 696 a.length = len; 697 a.ptr = res.ptr; 698 return *cast(ArrayRet_t*)(&a); 699 } 700 701 702 /********************************************** 703 * 'apply' for associative arrays - to support foreach 704 */ 705 706 // dg is D, but _aaApply() is C 707 extern (D) typedef int delegate(void *) dg_t; 708 709 int _aaApply(AA aa, size_t keysize, dg_t dg) 710 in 711 { 712 assert(aligntsize(keysize) == keysize); 713 } 714 body 715 { int result; 716 717 //printf("_aaApply(aa = x%llx, keysize = %d, dg = x%llx)\n", aa.a, keysize, dg); 718 719 int treewalker(aaA* e) 720 { int result; 721 722 do 723 { 724 //printf("treewalker(e = %p, dg = x%llx)\n", e, dg); 725 result = dg(cast(void *)(e + 1) + keysize); 726 if (result) 727 break; 728 if (e.right) 729 { if (!e.left) 730 { 731 e = e.right; 732 continue; 733 } 734 result = treewalker(e.right); 735 if (result) 736 break; 737 } 738 e = e.left; 739 } while (e); 740 741 return result; 742 } 743 744 if (aa.a) 745 { 746 foreach (e; aa.a.b) 747 { 748 if (e) 749 { 750 result = treewalker(e); 751 if (result) 752 break; 753 } 754 } 755 } 756 return result; 757 } 758 759 // dg is D, but _aaApply2() is C 760 extern (D) typedef int delegate(void *, void *) dg2_t; 761 762 int _aaApply2(AA aa, size_t keysize, dg2_t dg) 763 in 764 { 765 assert(aligntsize(keysize) == keysize); 766 } 767 body 768 { int result; 769 770 //printf("_aaApply(aa = x%llx, keysize = %d, dg = x%llx)\n", aa.a, keysize, dg); 771 772 int treewalker(aaA* e) 773 { int result; 774 775 do 776 { 777 //printf("treewalker(e = %p, dg = x%llx)\n", e, dg); 778 result = dg(cast(void *)(e + 1), cast(void *)(e + 1) + keysize); 779 if (result) 780 break; 781 if (e.right) 782 { if (!e.left) 783 { 784 e = e.right; 785 continue; 786 } 787 result = treewalker(e.right); 788 if (result) 789 break; 790 } 791 e = e.left; 792 } while (e); 793 794 return result; 795 } 796 797 if (aa.a) 798 { 799 foreach (e; aa.a.b) 800 { 801 if (e) 802 { 803 result = treewalker(e); 804 if (result) 805 break; 806 } 807 } 808 } 809 return result; 810 } 811 812 813 /*********************************** 814 * Construct an associative array of type ti from 815 * length pairs of key/value pairs. 816 */ 817 extern (C) BB* _d_assocarrayliteralTp(TypeInfo_AssociativeArray ti, void[] keys, void[] values) 818 { 819 auto valueti = ti.next; 820 auto valuesize = valueti.tsize(); // value size 821 auto keyti = ti.key; 822 auto keysize = keyti.tsize(); // key size 823 auto length = keys.length; 824 BB* result; 825 826 //printf("_d_assocarrayliteralT(keysize = %d, valuesize = %d, length = %d)\n", keysize, valuesize, length); 827 //printf("tivalue = %.*s\n", ti.next.classinfo.name); 828 assert(length == values.length); 829 if (length == 0 || valuesize == 0 || keysize == 0) 830 { 831 ; 832 } 833 else 834 { 835 result = new BB(); 836 837 size_t i; 838 for (i = 0; i < prime_list.length - 1; i++) 839 { 840 if (length <= prime_list[i]) 841 break; 842 } 843 auto len = prime_list[i]; 844 result.b = new aaA*[len]; 845 846 size_t keytsize = aligntsize(keysize); 847 848 for (size_t j = 0; j < length; j++) 849 { auto pkey = keys.ptr + j * keysize; 850 auto pvalue = values.ptr + j * valuesize; 851 aaA* e; 852 853 auto key_hash = keyti.getHash(pkey); 854 //printf("hash = %d\n", key_hash); 855 i = key_hash % len; 856 auto pe = &result.b[i]; 857 while (1) 858 { 859 e = *pe; 860 if (!e) 861 { 862 // Not found, create new elem 863 //printf("create new one\n"); 864 e = cast(aaA *) cast(void*) new void[aaA.sizeof + keytsize + valuesize]; 865 memcpy(e + 1, pkey, keysize); 866 e.hash = key_hash; 867 *pe = e; 868 result.nodes++; 869 break; 870 } 871 if (key_hash == e.hash) 872 { 873 auto c = keyti.compare(pkey, e + 1); 874 if (c == 0) 875 break; 876 pe = (c < 0) ? &e.left : &e.right; 877 } 878 else 879 pe = (key_hash < e.hash) ? &e.left : &e.right; 880 } 881 memcpy(cast(void *)(e + 1) + keytsize, pvalue, valuesize); 882 } 883 } 884 return result; 885 } 886 887 /*********************************** 888 * Compare AA contents for equality. 889 * Returns: 890 * 1 equal 891 * 0 not equal 892 */ 893 int _aaEqual(TypeInfo_AssociativeArray ti, AA e1, AA e2) 894 { 895 //printf("_aaEqual()\n"); 896 //printf("keyti = %.*s\n", ti.key.classinfo.name); 897 //printf("valueti = %.*s\n", ti.next.classinfo.name); 898 899 if (e1.a is e2.a) 900 return 1; 901 902 size_t len = _aaLen(e1); 903 if (len != _aaLen(e2)) 904 return 0; 905 906 /* Algorithm: Visit each key/value pair in e1. If that key doesn't exist 907 * in e2, or if the value in e1 doesn't match the one in e2, the arrays 908 * are not equal, and exit early. 909 * After all pairs are checked, the arrays must be equal. 910 */ 911 912 auto keyti = ti.key; 913 auto valueti = ti.next; 914 auto keysize = aligntsize(keyti.tsize()); 915 auto len2 = e2.a.b.length; 916 917 int _aaKeys_x(aaA* e) 918 { 919 do 920 { 921 auto pkey = cast(void*)(e + 1); 922 auto pvalue = pkey + keysize; 923 //printf("key = %d, value = %g\n", *cast(int*)pkey, *cast(double*)pvalue); 924 925 // We have key/value for e1. See if they exist in e2 926 927 auto key_hash = keyti.getHash(pkey); 928 //printf("hash = %d\n", key_hash); 929 auto i = key_hash % len2; 930 auto f = e2.a.b[i]; 931 while (1) 932 { 933 //printf("f is %p\n", f); 934 if (f is null) 935 return 0; // key not found, so AA's are not equal 936 if (key_hash == f.hash) 937 { 938 //printf("hash equals\n"); 939 auto c = keyti.compare(pkey, f + 1); 940 if (c == 0) 941 { // Found key in e2. Compare values 942 //printf("key equals\n"); 943 auto pvalue2 = cast(void *)(f + 1) + keysize; 944 if (valueti.equals(pvalue, pvalue2)) 945 { 946 //printf("value equals\n"); 947 break; 948 } 949 else 950 return 0; // values don't match, so AA's are not equal 951 } 952 f = (c < 0) ? f.left : f.right; 953 } 954 else 955 f = (key_hash < f.hash) ? f.left : f.right; 956 } 957 958 // Look at next entry in e1 959 if (e.left) 960 { if (!e.right) 961 { e = e.left; 962 continue; 963 } 964 if (_aaKeys_x(e.left) == 0) 965 return 0; 966 } 967 e = e.right; 968 } while (e !is null); 969 return 1; // this subtree matches 970 } 971 972 foreach (e; e1.a.b) 973 { 974 if (e) 975 { if (_aaKeys_x(e) == 0) 976 return 0; 977 } 978 } 979 980 return 1; // equal 981 } 982