1 /* 2 bdb2d is BerkeleyDB for D language 3 It is part of unDE project (http://unde.su) 4 5 Copyright (C) 2009-2014 Nikolay (unDEFER) Krivchenkov <undefer@gmail.com> 6 7 This program is free software: you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation, either version 3 of the License, or 10 (at your option) any later version. 11 12 This program is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with this program. If not, see <http://www.gnu.org/licenses/>. 19 */ 20 21 module berkeleydb.dbt; 22 23 import berkeleydb.c; 24 import berkeleydb.dbexception; 25 import std.array; 26 import std.traits; 27 import core.memory; 28 29 private inout(ubyte[]) tobytes(T)(inout ref T arg) 30 if (__traits(hasMember, arg, "sizeof")) 31 { 32 33 static if(__traits(hasMember, arg, "ptr")) 34 { 35 auto size = arg.length * typeof(arg[0]).sizeof; 36 return (cast(ubyte*)arg.ptr)[0..size]; 37 } 38 else 39 { 40 auto size = arg.sizeof; 41 return (cast(ubyte*)&arg)[0..size]; 42 } 43 } 44 45 private inout(T) frombytes(T)(inout ubyte[] arg) 46 if (!isPointer!(T) && !isArray!(T) && 47 __traits(hasMember, T, "sizeof") && 48 !__traits(hasMember, T, "ptr")) 49 { 50 assert(T.sizeof == arg.length, "Casting Dbt to structure with not corresponding size"); 51 52 T *res = cast(T*) arg; 53 54 return *res; 55 } 56 57 private inout(T*) frombytes(A: T*, T)(inout ubyte[] arg) 58 if (__traits(hasMember, T, "sizeof") && 59 !__traits(hasMember, T, "ptr")) 60 { 61 assert(T.sizeof == arg.length, "Casting Dbt to structure with not corresponding size"); 62 return cast(T*) arg; 63 } 64 65 private inout(T[]) frombytes(A : T[], T)(inout ubyte[] arg) 66 if (__traits(hasMember, T, "sizeof")) 67 { 68 assert((arg.length % T.sizeof) == 0, "Casting Dbt to array with not corresponding entries size"); 69 70 return (cast(T*)arg)[0..arg.length/T.sizeof]; 71 } 72 73 private inout(ubyte[]) dbttobytes(inout ref DBT dbt) 74 { 75 return (cast(inout ubyte*)dbt.data)[0..dbt.size]; 76 } 77 78 struct Dbt 79 { 80 DBT dbt; 81 alias dbt this; 82 83 this(T)(ref T arg) 84 { 85 opAssign(arg); 86 } 87 88 void opAssign(T)(ref T arg) 89 if (__traits(hasMember, arg, "sizeof")) 90 { 91 static if(__traits(hasMember, arg, "ptr")) 92 { 93 size = cast(uint) (arg.length * typeof(arg[0]).sizeof); 94 data = cast(void*) arg.ptr; 95 } 96 else 97 { 98 size = arg.sizeof; 99 data = cast(void*) &arg; 100 } 101 } 102 103 inout(T) to(T)() inout 104 { 105 return frombytes!T(dbttobytes(dbt)); 106 } 107 } 108 109 static assert(DBT.sizeof == Dbt.sizeof); 110 111 unittest 112 { 113 string hello = "Hello, world!"; 114 115 Dbt dbt = hello; 116 assert(dbt.size == hello.length); 117 118 assert(dbt.to!string() == "Hello, world!"); 119 assert(dbt.to!string() is "Hello, world!"); 120 121 struct S 122 { 123 int a; 124 byte b; 125 } 126 127 S s; 128 s.a = 5000; 129 s.b = 22; 130 131 Dbt dbt_struct; 132 dbt_struct = s; 133 assert( dbt_struct.size == s.sizeof ); 134 assert( dbt_struct.to!S() == s ); 135 136 S *ps = dbt_struct.to!(S*)(); 137 assert( *ps == s ); 138 assert( ps is &s ); 139 } 140 141 struct UserMemDbt 142 { 143 DBT dbt = DBT(null, 0, 0, 0, 0, null, DB_DBT_USERMEM); 144 alias dbt this; 145 146 invariant() 147 { 148 assert(dbt.flags & DB_DBT_USERMEM); 149 } 150 151 this(ref Dbt dbt) 152 { 153 if (!(dbt.flags & DB_DBT_USERMEM)) 154 { 155 throw new DbWrongUsingException("Constructing UserMemDbt from Dbt variable without DB_DBT_USERMEM in flags"); 156 } 157 this.dbt = dbt; 158 } 159 160 this(T)(ref T arg) 161 { 162 opAssign(arg); 163 } 164 165 this(T)(int size, ref T arg) 166 in { 167 assert(size > 0); 168 } 169 body { 170 ulen = size; 171 data = GC.malloc(size); 172 opAssign(arg); 173 } 174 175 this()(int size) 176 in { 177 assert(size > 0); 178 } 179 body { 180 ulen = size; 181 data = GC.malloc(size); 182 } 183 184 /*~this() 185 { 186 GC.free(data); 187 }*/ 188 189 void opAssign(T)(ref T arg) 190 if (__traits(hasMember, arg, "sizeof")) 191 { 192 193 static if(__traits(hasMember, arg, "ptr")) 194 { 195 size = cast(uint)(arg.length * typeof(arg[0]).sizeof); 196 197 if (ulen == 0) 198 { 199 ulen = size; 200 data = GC.malloc(size); 201 } 202 203 assert(size <= ulen); 204 (cast(char*) data)[0..size] = 205 (cast(char*) arg.ptr)[0..size]; 206 } 207 else 208 { 209 size = arg.sizeof; 210 211 if (ulen == 0) 212 { 213 ulen = size; 214 data = GC.malloc(size); 215 } 216 217 assert(size <= ulen); 218 (cast(char*) data)[0..size] = 219 (cast(char*) &arg)[0..size]; 220 } 221 } 222 223 inout(T) to(T)() inout 224 { 225 return frombytes!T(dbttobytes(dbt)); 226 } 227 } 228 229 unittest 230 { 231 string hello = "Hello, world!"; 232 233 UserMemDbt dbt = UserMemDbt(20, hello); 234 assert(dbt.size == hello.length); 235 236 assert(dbt.to!string() == "Hello, world!"); 237 assert(dbt.to!string() !is "Hello, world!"); 238 239 struct S 240 { 241 int a; 242 byte b; 243 } 244 245 S s; 246 s.a = 5000; 247 s.b = 22; 248 249 dbt = s; 250 assert( dbt.size == s.sizeof ); 251 assert( dbt.to!S() == s ); 252 253 S *ps = dbt.to!(S*)(); 254 assert( *ps == s ); 255 assert( ps !is &s ); 256 } 257 258 import std.stdint; 259 import std.exception; 260 import std.range; 261 import std.typecons; 262 263 struct BulkDbt 264 { 265 DBT dbt = DBT(null, 0, 0, 0, 0, null, DB_DBT_BULK | DB_DBT_USERMEM); 266 alias dbt this; 267 uint32_t *pointer; 268 269 invariant() 270 { 271 assert(dbt.flags & DB_DBT_BULK); 272 } 273 274 struct Range(T) { 275 uint32_t *pointer; 276 DBT *dbt; 277 278 private this(BulkDbt* b) { 279 dbt = &b.dbt; 280 pointer = cast(uint32_t*)(cast(ubyte*)dbt.data + 281 dbt.ulen) - 1; 282 } 283 284 /// Forward range primitives. 285 @property bool empty() const { 286 return !pointer || *pointer == -1; 287 } 288 /// ditto 289 @property Range save() { return this; } 290 291 /// ditto 292 @property T front() 293 { 294 uint32_t *__p = pointer; 295 if (*__p == -1) return null; 296 297 ubyte *retdata = cast(uint8_t*)dbt.data + *__p--; 298 uint32_t retdlen = *__p--; 299 300 return frombytes!T(retdata[0..retdlen]); 301 } 302 303 /// ditto 304 void popFront() 305 { 306 enforce(pointer); 307 pointer -= 2; 308 } 309 310 T moveFront() 311 { 312 enforce(pointer); 313 return front(); 314 } 315 } 316 317 struct KeyRange(K, V) { 318 uint32_t *pointer; 319 DBT *dbt; 320 321 struct KeyValuePair{ 322 K key; 323 V value; 324 325 bool opEquals(KeyValuePair arg) 326 { 327 return key == arg.key && value == arg.value; 328 } 329 330 }; 331 332 alias KeyValuePair KV; 333 334 private this(BulkDbt* b) { 335 dbt = &b.dbt; 336 pointer = cast(uint32_t*)(cast(ubyte*)dbt.data + 337 dbt.ulen) - 1; 338 } 339 340 /// Forward range primitives. 341 @property bool empty() const { 342 return !pointer || *pointer == -1; 343 } 344 /// ditto 345 @property KeyRange save() { return this; } 346 347 private @property T _front(T)() 348 { 349 uint32_t *__p = pointer; 350 if (*__p == -1) return null; 351 352 ubyte *retdata = cast(uint8_t*)dbt.data + *__p--; 353 uint32_t retdlen = *__p--; 354 355 return frombytes!T(retdata[0..retdlen]); 356 } 357 358 /// ditto 359 @property KV front() 360 { 361 KV res; 362 res.key = _front!K(); 363 pointer -= 2; 364 res.value = _front!V(); 365 pointer += 2; 366 367 return res; 368 } 369 370 /// ditto 371 void popFront() 372 { 373 enforce(pointer); 374 pointer -= 4; 375 } 376 377 KV moveFront() 378 { 379 enforce(pointer); 380 return front(); 381 } 382 } 383 384 Range!(ubyte[]) opSlice() 385 { 386 return Range!(ubyte[])(&this); 387 } 388 389 Range!(T) range(T)() 390 { 391 return Range!(T)(&this); 392 } 393 394 KeyRange!(K, V) keyrange(K, V)() 395 { 396 return KeyRange!(K, V)(&this); 397 } 398 399 void set_pointer() 400 { 401 pointer = cast(uint32_t*)(cast(ubyte*)data + ulen) -1; 402 while (*pointer != -1) 403 { 404 pointer -= 2; 405 if (pointer < data) 406 { 407 throw new DbWrongUsingException("Constructing BulkDbt from wrong DBT"); 408 } 409 } 410 } 411 412 this(ref Dbt dbt) 413 { 414 if (!(dbt.flags & DB_DBT_BULK)) 415 { 416 throw new DbWrongUsingException("Constructing BulkDbt from Dbt variable without DB_DBT_BULK in flags"); 417 } 418 this.dbt = dbt; 419 set_pointer(); 420 } 421 422 this(T)(int size, ref T arg) 423 if (isForwardRange!T || is(typeof(arg.byKey()))) 424 in { 425 assert(size > 0); 426 } 427 body { 428 ulen = size; 429 data = GC.malloc(size); 430 pointer = cast(uint32_t*)(cast(ubyte*)data + ulen) -1; 431 *pointer = -1; 432 insertFronts(arg); 433 } 434 435 this()(int size) 436 in { 437 assert(size > 0); 438 } 439 body { 440 ulen = size; 441 data = GC.malloc(size); 442 pointer = cast(uint32_t*)(cast(ubyte*)data + ulen) -1; 443 *pointer = -1; 444 } 445 446 447 /*~this() 448 { 449 GC.free(data); 450 }*/ 451 452 @property bool empty() const 453 { 454 return pointer is null; 455 } 456 457 /** 458 Duplicates the container. 459 460 Complexity: $(BIGOH n). 461 */ 462 @property BulkDbt dup() 463 { 464 auto t = this[]; 465 return BulkDbt(ulen, t); 466 } 467 468 /** 469 Forward to $(D opSlice().front). 470 471 Complexity: $(BIGOH 1) 472 */ 473 @property ubyte[] front() 474 { 475 uint32_t *__p = pointer; 476 if (*__p == -1) return null; 477 478 ubyte *retdata = cast(uint8_t*)dbt.data + *__p--; 479 uint32_t retdlen = *__p--; 480 481 return retdata[0..retdlen]; 482 } 483 484 /** 485 Inserts $(D stuff) to the front of the container. 486 487 Returns: The number of elements inserted 488 489 Complexity: $(BIGOH log(n)) 490 */ 491 size_t insertFronts(Stuff)(Stuff stuff) 492 if (isForwardRange!Stuff && is(typeof(tobytes(stuff.front))) || 493 is(typeof(stuff.front): ubyte[])) 494 { 495 size_t result; 496 foreach (item; stuff) 497 { 498 insertFront(item); 499 ++result; 500 } 501 return result; 502 } 503 504 /// ditto 505 size_t insertFront(T)(T _value) 506 if (is(typeof(tobytes(_value)))) 507 { 508 ubyte[] value = tobytes(_value); 509 510 uint32_t *__p = pointer; 511 uint32_t __off = (pointer == 512 (cast(uint32_t*)(cast(ubyte*)dbt.data + dbt.ulen) - 1)) 513 ? 0 : __p[1] + __p[2]; 514 assert(__p && *__p == -1); 515 516 auto writedlen = value.length * typeof(value[0]).sizeof; 517 518 if ((cast(ubyte*)dbt.data + __off + writedlen) > 519 cast(ubyte*)(__p - 2)) 520 throw new DbWrongUsingException("BulkDbt overflow"); 521 else { 522 ubyte *writedata = cast(uint8_t*)dbt.data + __off; 523 writedata[0..writedlen] = value[0..writedlen]; 524 __p[0] = __off; 525 __p[-1] = cast(uint32_t)(writedlen); 526 __p[-2] = cast(uint32_t)-1; 527 pointer = __p - 2; 528 } 529 530 return 1; 531 } 532 533 size_t insertFronts(K, V)(Tuple!(K, V) stuff) 534 { 535 size_t result; 536 insertFront(stuff[0]); 537 ++result; 538 insertFront(stuff[1]); 539 ++result; 540 return result; 541 } 542 543 size_t insertFronts(Stuff)(ref Stuff stuff) 544 if (is(typeof(stuff.byKey()))) 545 { 546 size_t result; 547 foreach (key; stuff.byKey()) 548 { 549 insertFront(key); 550 ++result; 551 insertFront(stuff[key]); 552 ++result; 553 } 554 return result; 555 } 556 557 /// ditto 558 alias insertFront insert; 559 560 /// ditto 561 alias insert stableInsert; 562 563 /// ditto 564 alias insertFront stableInsertFront; 565 566 /** 567 Removes the value at the front of the container. 568 569 Precondition: $(D !empty) 570 571 Complexity: $(BIGOH 1). 572 */ 573 void removeFront() 574 { 575 enforce(pointer); 576 uint32_t *__p = pointer; 577 578 if ((cast(ubyte*)dbt.data + ulen) == cast(ubyte*)__p) 579 throw new DbWrongUsingException("BulkDbt underflow"); 580 581 __p[0] = 0; 582 __p[1] = 0; 583 __p[2] = -1; 584 pointer += 2; 585 } 586 587 /// ditto 588 alias removeFront stableRemoveFront; 589 590 /** 591 Removes $(D howMany) values at the front or back of the 592 container. 593 594 Returns: The number of elements removed 595 596 Complexity: $(BIGOH howMany * log(n)). 597 */ 598 size_t removeFront(size_t howMany) 599 { 600 size_t result; 601 while (result < howMany) 602 { 603 removeFront(); 604 ++result; 605 } 606 return result; 607 } 608 609 /// ditto 610 alias removeFront stableRemoveFront; 611 } 612 613 unittest 614 { 615 auto ar = ["Hello", "World!", "The piece"]; 616 BulkDbt dbt = BulkDbt(100, ar); 617 618 assert(dbt.range!(string).front == "Hello"); 619 assert(dbt.range!(string).drop(1).front == "World!"); 620 assert(dbt.range!(string).drop(2).front == "The piece"); 621 assert(dbt[].walkLength == 3); 622 623 string[string] map; 624 map["Hello"] = "Hi!"; 625 map["Black"] = "White"; 626 map["Cat"] = "Dog"; 627 628 BulkDbt keydbt = BulkDbt(120, map); 629 keydbt.insertFronts(tuple("I", "You")); 630 631 auto keyrange = keydbt.keyrange!(string, string); 632 633 alias typeof(keyrange).KeyValuePair KeyValuePair; 634 635 /*Map doesn't guarantee any order*/ 636 int check( typeof(keyrange.front) value ) 637 { 638 return value == KeyValuePair("Hello", "Hi!") || 639 value == KeyValuePair("Cat", "Dog") || 640 value == KeyValuePair("Black", "White"); 641 } 642 643 assert(check(keyrange.front)); 644 assert(keyrange.drop(1).front != keyrange.front && 645 check(keyrange.drop(1).front)); 646 assert(keyrange.drop(2).front != keyrange.front && 647 keyrange.drop(2).front != keyrange.drop(1).front && 648 check(keyrange.drop(2).front)); 649 assert(keyrange.drop(3).front == KeyValuePair("I", "You")); 650 assert(keydbt.keyrange!(ubyte[], ubyte[]).walkLength == 4); 651 652 keydbt.removeFront(2); 653 keydbt.insertFronts(tuple("Apple", "Pear")); 654 assert(keyrange.drop(3).front == KeyValuePair("Apple", "Pear")); 655 assert(keydbt.keyrange!(ubyte[], ubyte[]).walkLength == 4); 656 } 657 658 struct RecnoBulkDbt 659 { 660 DBT dbt = DBT(null, 0, 0, 0, 0, null, DB_DBT_BULK | DB_DBT_USERMEM); 661 alias dbt this; 662 uint32_t *pointer; 663 664 invariant() 665 { 666 assert(dbt.flags & DB_DBT_BULK); 667 } 668 669 struct KeyRange(K, V) 670 if (isNumeric!(K)) { 671 uint32_t *pointer; 672 DBT *dbt; 673 674 struct KeyValuePair{ 675 K key; 676 V value; 677 678 bool opEquals(KeyValuePair arg) 679 { 680 return key == arg.key && value == arg.value; 681 } 682 683 }; 684 685 alias KeyValuePair KV; 686 687 private this(RecnoBulkDbt* b) { 688 dbt = &b.dbt; 689 pointer = cast(uint32_t*)(cast(ubyte*)dbt.data + 690 dbt.ulen) - 1; 691 } 692 693 /// Forward range primitives. 694 @property bool empty() const { 695 return !pointer || *pointer == 0; 696 } 697 /// ditto 698 @property KeyRange save() { return this; } 699 700 private @property Tuple!(K,V) _front(K, V)() 701 { 702 uint32_t *__p = pointer; 703 if (*__p == 0) return tuple(cast(K) 0, cast(V) null); 704 705 uint32_t key = *__p--; 706 ubyte *retdata = cast(uint8_t*)dbt.data + *__p--; 707 uint32_t retdlen = *__p--; 708 709 return tuple(cast(K) key, frombytes!V(retdata[0..retdlen])); 710 } 711 712 /// ditto 713 @property KV front() 714 { 715 KV res; 716 auto v = _front!(K,V)(); 717 res.key = v[0]; 718 res.value = v[1]; 719 720 return res; 721 } 722 723 /// ditto 724 void popFront() 725 { 726 enforce(pointer); 727 pointer -= 3; 728 } 729 730 KV moveFront() 731 { 732 enforce(pointer); 733 return front(); 734 } 735 } 736 737 KeyRange!(K, V) keyrange(K, V)() 738 { 739 return KeyRange!(K, V)(&this); 740 } 741 742 void set_pointer() 743 { 744 pointer = cast(uint32_t*)(cast(ubyte*)data + ulen) -1; 745 while (*pointer != 0) 746 { 747 pointer -= 3; 748 if (pointer < data) 749 { 750 throw new DbWrongUsingException("Constructing BulkDbt from wrong DBT"); 751 } 752 } 753 } 754 755 this(ref Dbt dbt) 756 { 757 if (!(dbt.flags & DB_DBT_BULK)) 758 { 759 throw new DbWrongUsingException("Constructing RecnoBulkDbt from Dbt variable without DB_DBT_BULK in flags"); 760 } 761 this.dbt = dbt; 762 set_pointer(); 763 } 764 765 this(T)(int size, ref T arg) 766 if (isForwardRange!T || is(typeof(arg.byKey()))) 767 in { 768 assert(size > 0); 769 } 770 body { 771 ulen = size; 772 data = GC.malloc(size); 773 pointer = cast(uint32_t*)(cast(ubyte*)data + ulen) -1; 774 *pointer = 0; 775 insertFronts(arg); 776 } 777 778 this()(int size) 779 in { 780 assert(size > 0); 781 } 782 body { 783 ulen = size; 784 data = GC.malloc(size); 785 pointer = cast(uint32_t*)(cast(ubyte*)data + ulen) -1; 786 *pointer = 0; 787 } 788 789 /*~this() 790 { 791 GC.free(data); 792 }*/ 793 794 @property bool empty() const 795 { 796 return pointer is null; 797 } 798 799 /** 800 Duplicates the container. The elements themselves are not transitively 801 duplicated. 802 803 Complexity: $(BIGOH n). 804 */ 805 /*TODO @property BulkDbt dup() 806 { 807 return BulkDbt(ulen, this[]); 808 }*/ 809 810 /** 811 Forward to $(D opSlice().front). 812 813 Complexity: $(BIGOH 1) 814 */ 815 @property Tuple!(K, V) front(K,V)() 816 if (isNumeric!(K)) { 817 uint32_t *__p = pointer; 818 if (*__p == 0) return tuple(0, null); 819 820 uint32_t key = *__p--; 821 ubyte *retdata = cast(uint8_t*)dbt.data + *__p--; 822 uint32_t retdlen = *__p--; 823 824 return tuple(key, retdata[0..retdlen]); 825 } 826 827 /** 828 Inserts $(D stuff) to the front of the container. 829 830 Returns: The number of elements inserted 831 832 Complexity: $(BIGOH log(n)) 833 */ 834 size_t insertFront(K, V)(Tuple!(K, V) stuff) 835 if (isNumeric!(K)) { 836 uint32_t key = stuff[0]; 837 ubyte[] value = tobytes(stuff[1]); 838 839 uint32_t *__p = pointer; 840 uint32_t __off = (pointer == 841 (cast(uint32_t*)(cast(ubyte*)dbt.data + dbt.ulen) - 1)) 842 ? 0 : __p[1] + __p[2]; 843 assert(__p && *__p == 0); 844 845 auto writedlen = value.length * typeof(value[0]).sizeof; 846 847 if ((cast(ubyte*)dbt.data + __off + writedlen) > 848 cast(ubyte*)(__p - 3)) 849 throw new DbWrongUsingException("RecnoBulkDbt overflow"); 850 else { 851 ubyte *writedata = cast(uint8_t*)dbt.data + __off; 852 writedata[0..writedlen] = value[0..writedlen]; 853 __p[0] = key; 854 __p[-1] = __off; 855 __p[-2] = cast(uint32_t)(writedlen); 856 __p[-3] = 0; 857 pointer = __p - 3; 858 } 859 860 return 1; 861 } 862 863 /// ditto 864 size_t insertFronts(Stuff)(Stuff stuff) 865 if (is(typeof(stuff.byKey()))) 866 { 867 size_t result; 868 foreach (key; stuff.byKey()) 869 { 870 insertFront(tuple(key, stuff[key])); 871 ++result; 872 } 873 return result; 874 } 875 876 /// ditto 877 alias insertFront insert; 878 879 /// ditto 880 alias insert stableInsert; 881 882 /// ditto 883 alias insertFront stableInsertFront; 884 885 /** 886 Removes the value at the front of the container. 887 888 Precondition: $(D !empty) 889 890 Complexity: $(BIGOH 1). 891 */ 892 void removeFront() 893 { 894 enforce(pointer); 895 uint32_t *__p = pointer; 896 897 if ((cast(ubyte*)dbt.data + ulen) == cast(ubyte*)__p) 898 throw new DbWrongUsingException("RecnoBulkDbt underflow"); 899 900 __p[0] = 0; 901 __p[1] = 0; 902 __p[2] = 0; 903 __p[3] = 0; 904 pointer += 3; 905 } 906 907 /// ditto 908 alias removeFront stableRemoveFront; 909 910 /** 911 Removes $(D howMany) values at the front or back of the 912 container. 913 914 Returns: The number of elements removed 915 916 Complexity: $(BIGOH howMany * log(n)). 917 */ 918 size_t removeFront(size_t howMany) 919 { 920 size_t result; 921 while (result < howMany) 922 { 923 removeFront(); 924 ++result; 925 } 926 return result; 927 } 928 929 /// ditto 930 alias removeFront stableRemoveFront; 931 } 932 933 unittest 934 { 935 import std.stdio; 936 937 string[int] map; 938 map[1] = "Hi!"; 939 map[2] = "Dog"; 940 map[100] = "White"; 941 942 RecnoBulkDbt keydbt = RecnoBulkDbt(100, map); 943 944 auto keyrange = keydbt.keyrange!(int, string); 945 946 alias typeof(keyrange).KeyValuePair KeyValuePair; 947 948 /*Map doesn't guarantee any order*/ 949 int check( typeof(keyrange.front) value ) 950 { 951 return value == KeyValuePair(1, "Hi!") || 952 value == KeyValuePair(2, "Dog") || 953 value == KeyValuePair(100, "White"); 954 } 955 956 assert(check(keyrange.front)); 957 assert(keyrange.drop(1).front != keyrange.front && 958 check(keyrange.drop(1).front) ); 959 assert(keyrange.drop(2).front != keyrange.front && 960 keyrange.drop(2).front != keyrange.drop(1).front && 961 check(keyrange.drop(2).front)); 962 assert(keydbt.keyrange!(int, ubyte[]).walkLength == 3); 963 964 keydbt.removeFront(); 965 keydbt.insertFront(tuple(10, "Pear")); 966 assert(keyrange.drop(2).front == KeyValuePair(10, "Pear")); 967 assert(keydbt.keyrange!(int, ubyte[]).walkLength == 3); 968 } 969 970 Dbt *toDbt(RecnoBulkDbt *dbt) 971 { 972 return cast(Dbt*) dbt; 973 } 974 975 Dbt *toDbt(BulkDbt *dbt) 976 { 977 return cast(Dbt*) dbt; 978 } 979 980 Dbt *toDbt(UserMemDbt *dbt) 981 { 982 return cast(Dbt*) dbt; 983 }