1 /** 2 Mutable @nogc @safe string struct using `std.experimental.allocator` for allocations. 3 4 License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). 5 Authors: Adam Búš 6 */ 7 module btl..string; 8 9 10 import std.traits : Unqual, Unconst, CopyTypeQualifiers, isSomeChar, isSomeString, isIntegral; 11 import std.meta : AliasSeq; 12 13 import btl.traits.common; 14 15 import btl.internal.allocator; 16 import btl.internal.traits; 17 18 import btl.string.encoding; 19 import btl.string.storage; 20 21 22 23 /** 24 Type used in forward constructors. 25 */ 26 alias Forward = btl.traits.common.Forward; 27 28 29 /** 30 Default allcoator for `BasicString`. 31 */ 32 public alias DefaultAllocator = Mallocator; 33 34 35 /** 36 True if `T` is a `BasicString` or implicitly converts to one, otherwise false. 37 */ 38 template isBasicString(T){ 39 import std.traits : isInstanceOf; 40 41 enum bool isBasicString = isInstanceOf!(BasicString, T); 42 } 43 44 45 /** 46 The `BasicString` is the generalization of struct string for character of type `char`, `wchar` or `dchar`. 47 48 `BasicString` use utf-8, utf-16 or utf-32 encoding. 49 50 `BasicString` use SSO (Small String Optimization). 51 52 Template parameters: 53 54 `_Char` Character type. (`char`, `wchar` or `dchar`). 55 56 `_Allocator` Type of the allocator object used to define the storage allocation model. By default `DefaultAllocator` is used. 57 58 `N` Minimal capacity of `BasicString` (increase max length of small string). 59 60 */ 61 template BasicString( 62 _Char, 63 size_t N = 1, 64 _Allocator = DefaultAllocator 65 ) 66 if(isSomeChar!_Char && is(Unqual!_Char == _Char)){ 67 import std.range : empty, ElementEncodingType, isInputRange, isRandomAccessRange, hasLength; 68 import std.traits : Unqual, isIntegral, hasMember, isArray, isSafe; 69 import core.lifetime: forward, move; 70 71 enum bool _allowHeap = !is(immutable _Allocator == immutable void); 72 73 alias Storage = .Storage!(_Char, N, size_t, _allowHeap); 74 75 struct BasicString{ 76 77 /** 78 True if allocator doesn't have state. 79 */ 80 public enum bool hasStatelessAllocator = isStatelessAllocator!_Allocator; 81 82 83 84 /** 85 Character type. (`char`, `wchar` or `dchar`). 86 */ 87 public alias CharType = _Char; 88 89 90 91 /** 92 Type of the allocator object used to define the storage allocation model. By default `DefaultAllocator` is used. 93 */ 94 public alias AllocatorType = _Allocator; 95 96 97 98 /** 99 Allow heap (`false` only if `Allcoator` is void) 100 */ 101 public alias allowHeap = _allowHeap; 102 103 104 105 /** 106 Maximal capacity of string, in terms of number of characters (utf code units). 107 */ 108 public alias maximalCapacity = Storage.maximalCapacity; 109 110 111 112 /** 113 Minimal capacity of string (same as maximum capacity of small string), in terms of number of characters (utf code units). 114 115 Examples: 116 -------------------- 117 BasicString!char str; 118 assert(str.empty); 119 assert(str.capacity == BasicString!char.minimalCapacity); 120 assert(str.capacity > 0); 121 -------------------- 122 */ 123 public alias minimalCapacity = Storage.minimalCapacity; 124 125 126 127 /** 128 Returns allocator. 129 */ 130 static if(!allowHeap) 131 public alias allocator = statelessAllcoator!NullAllocator; 132 else static if(hasStatelessAllocator) 133 public alias allocator = statelessAllcoator!AllocatorType; 134 else 135 public @property auto allocator()inout{ 136 return this._allocator; 137 } 138 139 140 141 /** 142 Returns whether the string is empty (i.e. whether its length is 0). 143 144 Examples: 145 -------------------- 146 BasicString!char str; 147 assert(str.empty); 148 149 str = "123"; 150 assert(!str.empty); 151 -------------------- 152 */ 153 public @property bool empty()const scope pure nothrow @safe @nogc{ 154 return (this.length == 0); 155 } 156 157 158 159 /** 160 Returns whether the string is full (i.e. whether its length is equal to maximalCapacity). 161 162 Examples: 163 -------------------- 164 BasicString!(char, 1, void) str; //FixedString 165 assert(str.empty); 166 assert(str.minimalCapacity > 0); 167 assert(!str.full); 168 169 str.resize(str.maximalCapacity); 170 assert(str.full); 171 -------------------- 172 */ 173 public @property bool full()const scope pure nothrow @safe @nogc{ 174 return (this.length == maximalCapacity); 175 } 176 177 178 179 /** 180 Returns the length of the string, in terms of number of characters (utf code units). 181 182 This is the number of actual characters that conform the contents of the `BasicString`, which is not necessarily equal to its storage capacity. 183 184 Examples: 185 -------------------- 186 BasicString!char str = "123"; 187 assert(str.length == 3); 188 189 BasicString!wchar wstr = "123"; 190 assert(wstr.length == 3); 191 192 BasicString!dchar dstr = "123"; 193 assert(dstr.length == 3); 194 195 -------------------- 196 */ 197 public @property size_t length()const scope pure nothrow @trusted @nogc{ 198 return this.storage.length; 199 } 200 201 202 203 /** 204 Returns the size of the storage space currently allocated for the `BasicString`, expressed in terms of characters (utf code units). 205 206 This capacity is not necessarily equal to the string length. It can be equal or greater, with the extra space allowing the object to optimize its operations when new characters are added to the `BasicString`. 207 208 Notice that this capacity does not suppose a limit on the length of the `BasicString`. When this capacity is exhausted and more is needed, it is automatically expanded by the object (reallocating it storage space). 209 210 The capacity of a `BasicString` can be altered any time the object is modified, even if this modification implies a reduction in size. 211 212 The capacity of a `BasicString` can be explicitly altered by calling member `reserve`. 213 214 Examples: 215 -------------------- 216 BasicString!char str; 217 assert(str.capacity == BasicString!char.minimalCapacity); 218 219 str.reserve(str.capacity + 1); 220 assert(str.capacity > BasicString!char.minimalCapacity); 221 -------------------- 222 */ 223 public @property size_t capacity()const scope pure nothrow @trusted @nogc{ 224 return this.storage.capacity; 225 } 226 227 228 229 /** 230 Return pointer to the first element. 231 232 The pointer returned may be invalidated by further calls to other member functions that modify the object. 233 234 Examples: 235 -------------------- 236 BasicString!char str = "123"; 237 char* ptr = str.ptr; 238 assert(ptr[0 .. 3] == "123"); 239 -------------------- 240 241 */ 242 static if(allowHeap) 243 public @property inout(CharType)* ptr()inout return pure nothrow @system @nogc{ 244 return this.storage.ptr; 245 } 246 else 247 public @property inout(CharType)* ptr()inout return pure nothrow @trusted @nogc{ 248 return this.storage.ptr; 249 } 250 251 252 253 /** 254 Return `true` if string is small (Small String Optimization) 255 */ 256 public @property bool small()const scope pure nothrow @safe @nogc{ 257 return this.storage.small; 258 } 259 260 261 262 /** 263 Return `true` if string is valid utf string. 264 */ 265 public @property bool valid()const scope pure nothrow @safe @nogc{ 266 return validate(this.storage.chars); 267 } 268 269 270 271 /** 272 Returns first utf code point(`dchar`) of the `BasicString`. 273 274 This function shall not be called on empty strings. 275 276 Examples: 277 -------------------- 278 BasicString!char str = "á123"; 279 280 assert(str.frontCodePoint == 'á'); 281 -------------------- 282 */ 283 public @property dchar frontCodePoint()const scope pure nothrow @trusted @nogc{ 284 auto chars = this.storage.allocatedChars; 285 return decode(chars); 286 } 287 288 289 290 /** 291 Returns the first character(utf8: `char`, utf16: `wchar`, utf32: `dchar`) of the `BasicString`. 292 293 This function shall not be called on empty strings. 294 295 Examples: 296 -------------------- 297 { 298 BasicString!char str = "123"; 299 300 assert(str.frontCodeUnit == '1'); 301 } 302 303 { 304 BasicString!char str = "á23"; 305 306 immutable(char)[2] a = "á"; 307 assert(str.frontCodeUnit == a[0]); 308 } 309 310 { 311 BasicString!char str = "123"; 312 313 str.frontCodeUnit = 'x'; 314 315 assert(str == "x23"); 316 } 317 -------------------- 318 */ 319 public @property CharType frontCodeUnit()const scope pure nothrow @trusted @nogc{ 320 return *this.ptr; 321 } 322 323 /// ditto 324 public @property CharType frontCodeUnit(const CharType val)scope pure nothrow @trusted @nogc{ 325 return *this.ptr = val; 326 } 327 328 329 330 /** 331 Returns last utf code point(`dchar`) of the `BasicString`. 332 333 This function shall not be called on empty strings. 334 335 Examples: 336 -------------------- 337 { 338 BasicString!char str = "123á"; 339 340 assert(str.backCodePoint == 'á'); 341 } 342 343 { 344 BasicString!char str = "123á"; 345 str.backCodePoint = '4'; 346 assert(str == "1234"); 347 } 348 -------------------- 349 */ 350 public @property dchar backCodePoint()const scope pure nothrow @trusted @nogc{ 351 352 static if(is(CharType == dchar)){ 353 return this.backCodeUnit(); 354 } 355 else{ 356 auto chars = this.storage.chars; 357 358 if(chars.length == 0) 359 return dchar.init; 360 361 const ubyte len = strideBack(chars); 362 if(len == 0) 363 return dchar.init; 364 365 chars = chars[$ - len .. $]; 366 return decode(chars); 367 } 368 } 369 370 /// ditto 371 public @property dchar backCodePoint()(const dchar val)scope{ 372 373 static if(is(CharType == dchar)){ 374 return this.backCodeUnit(val); 375 } 376 else{ 377 auto chars = this.storage.chars; 378 379 if(chars.length == 0) 380 return dchar.init; 381 382 const ubyte len = strideBack(chars); 383 if(len == 0) 384 return dchar.init; 385 386 this.storage.length = (chars.length - len); 387 this.append(val); 388 return val; 389 } 390 } 391 392 393 /** 394 Returns the last character(utf8: `char`, utf16: `wchar`, utf32: `dchar`) of the `BasicString`. 395 396 This function shall not be called on empty strings. 397 398 Examples: 399 -------------------- 400 { 401 BasicString!char str = "123"; 402 403 assert(str.backCodeUnit == '3'); 404 } 405 406 { 407 BasicString!char str = "12á"; 408 409 immutable(char)[2] a = "á"; 410 assert(str.backCodeUnit == a[1]); 411 } 412 413 { 414 BasicString!char str = "123"; 415 416 str.backCodeUnit = 'x'; 417 assert(str == "12x"); 418 } 419 -------------------- 420 */ 421 public @property CharType backCodeUnit()const scope pure nothrow @trusted @nogc{ 422 auto chars = this.storage.chars; 423 424 return (chars.length == 0) 425 ? CharType.init 426 : chars[$ - 1]; 427 } 428 429 /// ditto 430 public @property CharType backCodeUnit(const CharType val)scope pure nothrow @trusted @nogc{ 431 auto chars = this.storage.chars; 432 433 return (chars.length == 0) 434 ? CharType.init 435 : (chars[$ - 1] = val); 436 } 437 438 439 440 /** 441 Erases the last utf code point of the `BasicString`, effectively reducing its length by code point length. 442 443 Return number of erased characters, 0 if string is empty or if last character is not valid code point. 444 445 Examples: 446 -------------------- 447 { 448 BasicString!char str = "á1"; //'á' is encoded as 2 chars 449 450 assert(str.popBackCodePoint == 1); 451 assert(str == "á"); 452 453 assert(str.popBackCodePoint == 2); 454 assert(str.empty); 455 456 assert(str.popBackCodePoint == 0); 457 assert(str.empty); 458 } 459 460 { 461 BasicString!char str = "1á"; //'á' is encoded as 2 chars 462 assert(str.length == 3); 463 464 str.erase(str.length - 1); 465 assert(str.length == 2); 466 467 assert(str.popBackCodePoint == 0); //popBackCodePoint cannot remove invalid code points 468 assert(str.length == 2); 469 } 470 -------------------- 471 */ 472 public ubyte popBackCodePoint()scope pure nothrow @trusted @nogc{ 473 if(this.empty) 474 return 0; 475 476 const ubyte n = strideBack(this.storage.chars); 477 478 this.storage.length = (this.length - n); 479 480 return n; 481 } 482 483 484 485 /** 486 Erases the last code unit of the `BasicString`, effectively reducing its length by 1. 487 488 Return number of erased characters, `false` if string is empty or `true` if is not. 489 490 Examples: 491 -------------------- 492 BasicString!char str = "á1"; //'á' is encoded as 2 chars 493 assert(str.length == 3); 494 495 assert(str.popBackCodeUnit); 496 assert(str.length == 2); 497 498 assert(str.popBackCodeUnit); 499 assert(str.length == 1); 500 501 assert(str.popBackCodeUnit); 502 assert(str.empty); 503 504 assert(!str.popBackCodeUnit); 505 assert(str.empty); 506 -------------------- 507 */ 508 public bool popBackCodeUnit()scope pure nothrow @trusted @nogc{ 509 if(this.empty) 510 return false; 511 512 this.storage.length = (this.storage.length - 1); 513 514 return true; 515 } 516 517 518 519 /** 520 Erases the contents of the `BasicString`, which becomes an empty string (with a length of 0 characters). 521 522 Doesn't change capacity of string. 523 524 Examples: 525 -------------------- 526 BasicString!char str = "123"; 527 528 str.reserve(str.capacity * 2); 529 assert(str.length == 3); 530 531 const size_t cap = str.capacity; 532 str.clear(); 533 assert(str.capacity == cap); 534 -------------------- 535 */ 536 public void clear()scope pure nothrow @trusted @nogc{ 537 this.storage.length = 0; 538 } 539 540 541 542 /** 543 Erases and deallocate the contents of the `BasicString`, which becomes an empty string (with a length of 0 characters). 544 545 Examples: 546 -------------------- 547 BasicString!char str = "123"; 548 549 str.reserve(str.capacity * 2); 550 assert(str.length == 3); 551 552 const size_t cap = str.capacity; 553 str.clear(); 554 assert(str.capacity == cap); 555 556 str.release(); 557 assert(str.capacity < cap); 558 assert(str.capacity == BasicString!char.minimalCapacity); 559 -------------------- 560 */ 561 public void release()scope{ 562 if(this.storage.external){ 563 //this._deallocate(this.storage.heap.allocatedChars); 564 this._deallocate_heap(this.storage.heap); 565 } 566 this.storage.reset(); 567 } 568 569 570 571 /** 572 Requests that the string capacity be adapted to a planned change in size to a length of up to n characters (utf code units). 573 574 If n is greater than the current string capacity, the function causes the container to increase its capacity to n characters (or greater). 575 576 In all other cases, it do nothing. 577 578 This function has no effect on the string length and cannot alter its content. 579 580 Examples: 581 -------------------- 582 BasicString!char str = "123"; 583 assert(str.capacity == BasicString!char.minimalCapacity); 584 585 const size_t cap = (str.capacity * 2); 586 str.reserve(cap); 587 assert(str.capacity > BasicString!char.minimalCapacity); 588 assert(str.capacity >= cap); 589 -------------------- 590 */ 591 public void reserve(const size_t n)scope{ 592 593 void _reserve_short(const size_t n)scope{ 594 assert(this.storage.small); 595 596 const size_t old_capacity = this.storage.capacity; 597 598 if(n <= old_capacity) 599 return;// old_capacity; 600 601 const size_t length = this.storage.length; 602 //const size_t new_capacity = max(old_capacity * 2, (n + 1)) & ~0x1; 603 const size_t new_capacity = _grow_capacity(old_capacity, n); 604 605 //assert(new_capacity >= max(old_capacity * 2, n)); 606 //assert(new_capacity % 2 == 0); 607 608 609 Storage.Heap heap; 610 this._allocate_heap(heap, new_capacity); 611 612 memCopy(heap.ptr, this.storage.ptr, length); 613 assert(this.storage.chars == heap.chars); 614 615 this.storage.heap = heap; 616 617 //return new_capacity; 618 } 619 620 void _reserve_long(const size_t n)scope{ 621 assert(this.storage.external); 622 623 const size_t old_capacity = this.storage.heap.capacity; 624 625 if(n <= old_capacity) 626 return;// old_capacity; 627 628 const size_t length = this.storage.heap.length; 629 //const size_t new_capacity = max(old_capacity * 2, (n + 1)) & ~0x1; 630 const size_t new_capacity = _grow_capacity(old_capacity, n); 631 632 //assert(new_capacity >= max(old_capacity * 2, n)); 633 //assert(new_capacity % 2 == 0); 634 635 this._reallocate_heap(this.storage.heap, new_capacity); 636 } 637 638 return (this.storage.small) 639 ? _reserve_short(n) 640 : _reserve_long(n); 641 } 642 643 644 645 /** 646 Resizes the string to a length of `n` characters (utf code units). 647 648 If `n` is smaller than the current string length, the current value is shortened to its first `n` character, removing the characters beyond the nth. 649 650 If `n` is greater than the current string length, the current content is extended by inserting at the end as many characters as needed to reach a size of n. 651 652 If `ch` is specified, the new elements are initialized as copies of `ch`, otherwise, they are `_`. 653 654 Examples: 655 -------------------- 656 BasicString!char str = "123"; 657 658 str.resize(5, 'x'); 659 assert(str == "123xx"); 660 661 str.resize(2); 662 assert(str == "12"); 663 -------------------- 664 */ 665 public void resize(const size_t n, const CharType ch = '_')scope{ 666 const size_t old_length = this.length; 667 668 if(old_length > n){ 669 this.storage.length = n; 670 } 671 else if(old_length < n){ 672 this.append(ch, n - old_length); 673 } 674 } 675 676 677 678 /** 679 Downsizes the string to a length of `n` characters (utf code units). 680 681 If `n` is smaller than the current string length, the current value is shortened to its first `n` character, removing the characters beyond the nth. 682 683 If `n` is greater or equal than the current string length, the nothing happend. 684 685 Examples: 686 -------------------- 687 BasicString!char str = "123"; 688 689 str.downsize(5); 690 assert(str == "123"); 691 692 str.downsize(2); 693 assert(str == "12"); 694 -------------------- 695 */ 696 public void downsize(const size_t n)scope pure nothrow @safe @nogc{ 697 const size_t old_length = this.length; 698 699 if(old_length > n){ 700 this.storage.length = n; 701 } 702 } 703 704 705 706 /** 707 Upsizes the string to a length of `n` characters (utf code units). 708 709 If `n` is smaller or equal than the current string length, the current value doesn't change. 710 711 If `n` is greater than the current string length, the current content is extended by inserting at the end as many characters as needed to reach a size of n. 712 713 If `ch` is specified, the new elements are initialized as copies of `ch`, otherwise, they are `_`. 714 715 Examples: 716 -------------------- 717 BasicString!char str = "123"; 718 719 str.upsize(5, 'x'); 720 assert(str == "123xx"); 721 722 str.upsize(2); 723 assert(str == "123xx"); 724 -------------------- 725 */ 726 public void upsize(const size_t n, const CharType ch = '_')scope{ 727 const size_t old_length = this.length; 728 729 if(old_length < n){ 730 this.append(ch, n - old_length); 731 } 732 } 733 734 735 736 /** 737 Requests the `BasicString` to reduce its capacity to fit its length. 738 739 The request is non-binding. 740 741 This function has no effect on the string length and cannot alter its content. 742 743 Examples: 744 -------------------- 745 BasicString!char str = "123"; 746 assert(str.capacity == BasicString!char.minimalCapacity); 747 748 str.reserve(str.capacity * 2); 749 assert(str.capacity > BasicString!char.minimalCapacity); 750 751 str.shrinkToFit(); 752 assert(str.capacity == BasicString!char.minimalCapacity); 753 -------------------- 754 */ 755 public void shrinkToFit(bool realloc = true)()scope{ 756 if(!this.storage.external) 757 return; 758 759 const size_t old_capacity = this.storage.heap.capacity; 760 const size_t length = this.storage.heap.length; 761 762 763 if(length == old_capacity) 764 return; 765 766 767 if(length <= minimalCapacity){ 768 auto heap = this.storage.heap; 769 770 this.storage.reset(length); 771 memCopy(this.storage.inline.ptr, heap.ptr, length); 772 773 this._deallocate_heap(heap); 774 775 assert(!this.storage.external); 776 return; 777 } 778 779 if(realloc == false){ 780 return; 781 } 782 783 const size_t new_capacity = length; 784 785 if(new_capacity >= old_capacity) 786 return; 787 788 assert(new_capacity >= length); 789 //assert(new_capacity % 2 == 0); 790 791 this._reallocate_heap!(!realloc)(this.storage.heap, new_capacity); 792 } 793 794 795 796 /* 797 Checks if `this` is not empty`. 798 799 Examples: 800 -------------------- 801 BasicString!char str; 802 assert(false == cast(bool)x); //explicit cast 803 assert(!x); //implicit cast 804 x = "\t"; 805 assert(cast(bool)x); //explicit cast 806 assert(x); //implicit cast 807 -------------------- 808 */ 809 /+public bool opCast(To : bool)()const scope pure nothrow @safe @nogc 810 if(is(To : bool)){ //docs 811 return !this.empty; 812 }+/ 813 814 815 816 /** 817 Support for quelifier cast. 818 */ 819 public ref To opCast(To, this This)()return scope pure nothrow @nogc 820 if(is(immutable To : immutable typeof(this))){ 821 static if(is(This : To)){ 822 return *(()@trusted => cast(To*)&this )(); 823 } 824 else{ 825 return *(()@system => cast(To*)&this )(); 826 } 827 } 828 829 830 831 /** 832 Destroys the `BasicString` object. 833 834 This deallocates all the storage capacity allocated by the `BasicString` using its allocator. 835 */ 836 public ~this()scope{ 837 if(this.storage.external){ 838 this._deallocate_heap(this.storage.heap); 839 debug this.storage.reset(); 840 } 841 } 842 843 844 845 /** 846 Constructs a empty `BasicString` object. 847 848 Examples: 849 -------------------- 850 { 851 BasicString!char str = null; 852 assert(str.empty); 853 } 854 -------------------- 855 */ 856 public this(typeof(null) nil, Forward = Forward.init)scope pure nothrow @safe @nogc{ 857 } 858 859 860 861 /** 862 Constructs a empty `BasicString` object with `allocator`. 863 864 Parameters: 865 `allocator` allocator parameter. 866 867 Examples: 868 -------------------- 869 { 870 BasicString!(char, DefaultAllocator) str = DefaultAllocator.init; 871 assert(str.empty); 872 } 873 -------------------- 874 */ 875 static if(allowHeap) 876 public this(AllocatorType allocator)scope{ 877 static if(!hasStatelessAllocator) 878 this._allocator = forward!allocator; 879 } 880 881 882 883 /** 884 Constructs a `BasicString` object, initializing its value to char value `character`. 885 886 Parameters: 887 `character` can by type char|wchar|dchar. 888 889 Examples: 890 -------------------- 891 { 892 BasicString!char str = 'x'; 893 assert(str == "x"); 894 } 895 896 { 897 BasicString!char str = '読'; 898 assert(str == "読"); 899 } 900 -------------------- 901 */ 902 public this(C)(const C character, Forward = Forward.init)scope 903 if(isSomeChar!C){ 904 this._ctor(character); 905 } 906 907 908 909 /** 910 Constructs a `BasicString` object, initializing its value to char value `character`. 911 912 Parameters: 913 `character` can by type char|wchar|dchar. 914 915 `allocator` allocator parameter. 916 917 Examples: 918 -------------------- 919 { 920 auto str = BasicString!(char, DefaultAllocator)('読', DefaultAllocator.init); 921 assert(str == "読"); 922 } 923 -------------------- 924 */ 925 public this(C)(const C character, AllocatorType allocator)scope 926 if(isSomeChar!C){ 927 this(forward!allocator); 928 this._ctor(character); 929 } 930 931 932 933 /** 934 Constructs a `BasicString` object from char slice `slice`. 935 936 Parameters: 937 `slice` is slice of characters (`const char[]`, `const wchar[]`, `const dchar[]`). 938 939 Examples: 940 -------------------- 941 { 942 BasicString!char str = "test"; 943 assert(str == "test"); 944 } 945 946 { 947 BasicString!char str = "test 読"d; 948 assert(str == "test 読"); 949 } 950 951 { 952 wchar[3] data = [cast(wchar)'1', '2', '3']; 953 BasicString!char str = data[]; 954 assert(str == "123"); 955 } 956 -------------------- 957 */ 958 public this(this This)(scope const CharType[] slice, Forward = Forward.init)scope{ 959 this._ctor(slice); 960 } 961 962 /// ditto 963 public this(this This, C)(scope const C[] slice, Forward = Forward.init)scope 964 if(isSomeChar!C && !is(immutable C == immutable CharType)){ 965 this._ctor(slice); 966 } 967 968 969 970 /** 971 Constructs a `BasicString` object from char slice `slice`. 972 973 Parameters: 974 `slice` is slice of characters (`const char[]`, `const wchar[]`, `const dchar[]`). 975 976 `allocator` allocator parameter. 977 978 Examples: 979 -------------------- 980 { 981 auto str = BasicString!(char, DefaultAllocator)("test", DefaultAllocator.init); 982 assert(str == "test"); 983 } 984 985 { 986 auto str = BasicString!(char, DefaultAllocator)("test 読"d, DefaultAllocator.init); 987 assert(str == "test 読"); 988 } 989 990 { 991 992 wchar[3] data = [cast(wchar)'1', '2', '3']; 993 auto str = BasicString!(char, DefaultAllocator)(data[], DefaultAllocator.init); 994 assert(str == "123"); 995 } 996 -------------------- 997 */ 998 public this(this This)(scope const CharType[] slice, AllocatorType allocator)scope{ 999 this(forward!allocator); 1000 this._ctor(slice); 1001 } 1002 1003 /// ditto 1004 public this(this This, C)(scope const C[] slice, AllocatorType allocator)scope 1005 if(isSomeChar!C && !is(immutable C == immutable CharType)){ 1006 this(forward!allocator); 1007 this._ctor(slice); 1008 } 1009 1010 1011 1012 /** 1013 Constructs a `BasicString` object, initializing its value from integer `integer`. 1014 1015 Parameters: 1016 1017 `integer` integers value. 1018 1019 Examples: 1020 -------------------- 1021 { 1022 BasicString!char str = 123uL; 1023 assert(str == "123"); 1024 } 1025 1026 { 1027 BasicString!dchar str = -123; 1028 assert(str == "-123"); 1029 } 1030 -------------------- 1031 */ 1032 public this(I)(I integer, Forward = Forward.init)scope 1033 if(isIntegral!I){ 1034 this._ctor(integer); 1035 } 1036 1037 1038 1039 /** 1040 Constructs a `BasicString` object, initializing its value from integer `integer`. 1041 1042 Parameters: 1043 1044 `integer` integers value. 1045 1046 `allocator` allocator parameter. 1047 1048 Examples: 1049 -------------------- 1050 { 1051 auto str = BasicString!(char, DefaultAllocator)(123uL, DefaultAllocator.init); 1052 assert(str == "123"); 1053 } 1054 1055 { 1056 auto str = BasicString!(dchar, DefaultAllocator)(-123, DefaultAllocator.init); 1057 assert(str == "-123"); 1058 } 1059 -------------------- 1060 */ 1061 public this(I)(I integer, AllocatorType allocator)scope 1062 if(isIntegral!I){ 1063 this(forward!allocator); 1064 this._ctor(integer); 1065 } 1066 1067 1068 1069 /** 1070 Constructs a `BasicString` object from other `BasicString` object. 1071 1072 Parameters: 1073 1074 `rhs` `BasicString` rvalue/lvalue 1075 1076 `allocator` optional allocator parameter. 1077 1078 Examples: 1079 -------------------- 1080 { 1081 BasicString!char a = "123"; 1082 BasicString!char b = a; 1083 assert(b == "123"); 1084 } 1085 1086 { 1087 BasicString!dchar a = "123"; 1088 BasicString!char b = a; 1089 assert(b == "123"); 1090 } 1091 1092 { 1093 BasicString!dchar a = "123"; 1094 auto b = BasicString!char(a, DefaultAllocator.init); 1095 assert(b == "123"); 1096 } 1097 1098 import core.lifetime : move; 1099 { 1100 BasicString!char a = "123"; 1101 BasicString!char b = move(a); 1102 assert(b == "123"); 1103 } 1104 -------------------- 1105 */ 1106 public this(this This, Rhs)(scope auto ref Rhs rhs)scope 1107 if( isBasicString!Rhs 1108 && isConstructable!(Rhs, This) 1109 && (isRef!rhs || !is(immutable This == immutable Rhs)) 1110 ){ 1111 this(forward!rhs, Forward.init); 1112 } 1113 1114 /// ditto 1115 public this(this This, Rhs)(scope auto ref const Rhs rhs, AllocatorType allocator)scope 1116 if(isBasicString!Rhs){ 1117 this(forward!allocator); 1118 this._ctor(rhs.storage.chars); 1119 } 1120 1121 1122 1123 /** 1124 Forward constructor. 1125 */ 1126 public this(this This, Rhs)(scope auto ref Rhs rhs, Forward)scope 1127 if(isBasicString!Rhs && isConstructable!(Rhs, This)){ 1128 1129 static if(isMoveConstructable!(rhs, This)){ 1130 1131 if(rhs.storage.external){ 1132 //heap -> heap: 1133 if(minimalCapacity <= Rhs.minimalCapacity || minimalCapacity < rhs.length){ 1134 static if(!hasStatelessAllocator) 1135 this._allocator = move(rhs._allocator); 1136 1137 this.storage.heap = rhs.storage.heap; //this.storage.setHeap(rhs.storage.heap); 1138 rhs.storage.reset(); 1139 } 1140 //heap -> inline: 1141 else{ 1142 assert(rhs.length <= minimalCapacity); 1143 1144 this._ctor(rhs.storage.heap.chars); 1145 1146 rhs.release(); 1147 1148 static if(!hasStatelessAllocator) 1149 this._allocator = move(rhs._allocator); 1150 } 1151 } 1152 else{ 1153 static if(!hasStatelessAllocator) 1154 this._allocator = move(rhs._allocator); 1155 1156 //inline -> inline: 1157 //inline -> heap: 1158 this._ctor(rhs.storage.inline.chars); 1159 } 1160 } 1161 else{ 1162 static if(!hasStatelessAllocator) 1163 this._allocator = rhs._allocator; 1164 1165 this._ctor(rhs.storage.chars); 1166 } 1167 } 1168 1169 1170 1171 /** 1172 Copy constructor if `AllocatorType` is statless. 1173 1174 Parameter `rhs` is const. 1175 */ 1176 static if(hasStatelessAllocator) 1177 public this(ref scope const typeof(this) rhs)scope{ 1178 this(rhs.storage.chars); 1179 } 1180 1181 1182 1183 /** 1184 Copy constructor if `AllocatorType` has state. 1185 1186 Parameter `rhs` is mutable. 1187 */ 1188 static if(!hasStatelessAllocator) 1189 public this(ref scope typeof(this) rhs)scope{ 1190 this(rhs.storage.chars, rhs.allocator); 1191 } 1192 1193 1194 1195 1196 private void _ctor(this This, C)(const C character)scope 1197 if(isSomeChar!C){ 1198 auto self = (()@trusted => cast(Unqual!This*)&this )(); 1199 1200 assert(minimalCapacity >= encodedLength!CharType(character)); 1201 self.storage.inline.length = character.encodeTo(self.storage.inline.allocatedChars); 1202 } 1203 1204 private void _ctor(this This, C)(scope const C[] str)scope 1205 if(isSomeChar!C){ 1206 auto self = (()@trusted => cast(Unqual!This*)&this )(); 1207 1208 const size_t len = encodedLength!CharType(str); 1209 1210 self.reserve(len); 1211 self.storage.length = str[].encodeTo(self.storage.allocatedChars); 1212 } 1213 1214 private void _ctor(this This, I)(const I integer)scope 1215 if(isIntegral!I){ 1216 auto self = (()@trusted => cast(Unqual!This*)&this )(); 1217 1218 const size_t len = encodedLength!CharType(integer); 1219 1220 self.reserve(len); 1221 self.storage.length = integer.encodeTo(self.storage.allocatedChars); 1222 } 1223 1224 1225 1226 /** 1227 Assigns a new value `rhs` to the string, replacing its current contents. 1228 1229 Parameter `rhs` can by type of `null`, `BasicString!(...)`, `char|wchar|dchar` array/slice/character or integer (integer is transformed to string). 1230 1231 Return referece to `this`. 1232 1233 Examples: 1234 -------------------- 1235 BasicString!char str = "123"; 1236 assert(!str.empty); 1237 1238 str = null; 1239 assert(str.empty); 1240 1241 str = 'X'; 1242 assert(str == "X"); 1243 1244 str = "abc"w; 1245 assert(str == "abc"); 1246 1247 str = -123; 1248 assert(str == "-123"); 1249 1250 str = BasicString!char("42"); 1251 assert(str == "42"); 1252 1253 str = BasicString!wchar("abc"); 1254 assert(str == "abc"); 1255 -------------------- 1256 */ 1257 public void opAssign(typeof(null) nil)scope pure nothrow @safe @nogc{ 1258 this.clear(); 1259 } 1260 1261 /// ditto 1262 public void opAssign(scope const CharType[] slice)scope{ 1263 this.clear(); 1264 1265 this.reserve(encodedLength!CharType(slice)); 1266 this.storage.length = slice.encodeTo(this.storage.allocatedChars); 1267 } 1268 1269 /// ditto 1270 public void opAssign(C)(scope const C[] slice)scope 1271 if(isSomeChar!C){ 1272 this.clear(); 1273 1274 this.reserve(encodedLength!CharType(slice)); 1275 this.storage.length = slice.encodeTo(this.storage.allocatedChars); 1276 } 1277 1278 /// ditto 1279 public void opAssign(C)(const C character)scope 1280 if(isSomeChar!C){ 1281 this.clear(); 1282 1283 assert(character.encodedLength!CharType <= minimalCapacity); 1284 this.storage.length = character.encodeTo(this.storage.allocatedChars); 1285 } 1286 1287 /// ditto 1288 public void opAssign(I)(const I integer)scope 1289 if(isIntegral!I){ 1290 this.clear(); 1291 1292 assert(integer.encodedLength!CharType <= minimalCapacity); 1293 this.storage.length = integer.encodeTo(this.storage.allocatedChars); 1294 } 1295 1296 /// ditto 1297 public void opAssign(scope typeof(this) rhs)scope{ 1298 this.proxySwap(rhs); 1299 } 1300 1301 /// ditto 1302 public void opAssign(Rhs)(scope auto ref Rhs rhs)scope 1303 if(isBasicString!Rhs && isAssignable!(Rhs, typeof(this))){ 1304 static if(isMoveAssignable!(rhs, typeof(this))){ 1305 1306 if(rhs.storage.external){ 1307 if(minimalCapacity <= Rhs.minimalCapacity || minimalCapacity < rhs.length){ 1308 static if(!hasStatelessAllocator) 1309 this._allocator = move(rhs._allocator); 1310 1311 this.release(); 1312 this.storage.heap = rhs.storage.heap; //this.storage.setHeap(rhs.storage.heap); 1313 rhs.storage.reset(); 1314 return; 1315 } 1316 } 1317 } 1318 else{ 1319 return this.opAssign(rhs.storage.chars); 1320 } 1321 } 1322 1323 1324 1325 /** 1326 Extends the `BasicString` by appending additional characters at the end of its current value. 1327 1328 This is alias to `append` 1329 */ 1330 public template opOpAssign(string op) 1331 if(op == "+" || op == "~"){ 1332 alias opOpAssign = append; 1333 } 1334 1335 1336 1337 /** 1338 Returns a newly constructed `BasicString` object with its value being the concatenation of the characters in `this` followed by those of `rhs`. 1339 1340 Parameter `rhs` can by type of `BasicString!(...)`, `char|wchar|dchar` array/slice/character. 1341 1342 Examples: 1343 -------------------- 1344 BasicString!char str = null; 1345 assert(str.empty); 1346 1347 str = str + '1'; 1348 assert(str == "1"); 1349 1350 str = str + "23"d; 1351 assert(str == "123"); 1352 1353 str = str + BasicString!dchar("456"); 1354 assert(str == "123456"); 1355 -------------------- 1356 */ 1357 public typeof(this) opBinary(string op)(scope const CharType[] rhs)scope 1358 if(op == "+" || op == "~"){ 1359 static if(hasStatelessAllocator) 1360 return this.build(this.storage.chars, rhs); 1361 else 1362 return this.build(this._allocator, this.storage.chars, rhs); 1363 } 1364 1365 /// ditto 1366 public typeof(this) opBinary(string op, Rhs)(scope auto ref const Rhs rhs)scope 1367 if((op == "+" || op == "~") 1368 && (isBasicString!Rhs || isSomeChar!Rhs || isSomeString!Rhs || isCharArray!Rhs || isIntegral!Rhs) 1369 ){ 1370 static if(hasStatelessAllocator) 1371 return this.build(this.storage.chars, forward!rhs); 1372 else 1373 return this.build(this._allocator, this.storage.chars, forward!rhs); 1374 } 1375 1376 1377 1378 /** 1379 Returns a newly constructed `BasicString` object with its value being the concatenation of the characters in `lhs` followed by those of `this`. 1380 1381 Parameter `lhs` can by type of `BasicString!(...)`, `char|wchar|dchar` array/slice/character. 1382 1383 Examples: 1384 -------------------- 1385 BasicString!char str = null; 1386 assert(str.empty); 1387 1388 str = '1' + str; 1389 assert(str == "1"); 1390 1391 str = "32"d + str; 1392 assert(str == "321"); 1393 1394 str = BasicString!dchar("654") + str; 1395 assert(str == "654321"); 1396 -------------------- 1397 */ 1398 public typeof(this) opBinaryRight(string op)(scope const CharType[] lhs)scope 1399 if(op == "+" || op == "~"){ 1400 static if(hasStatelessAllocator) 1401 return this.build(lhs, this.storage.chars); 1402 else 1403 return this.build(this._allocator, lhs, this.storage.chars); 1404 } 1405 1406 /// ditto 1407 public typeof(this) opBinaryRight(string op, Lhs)(scope auto ref const Lhs lhs)scope 1408 if((op == "+" || op == "~") 1409 && (isSomeChar!Lhs || isSomeString!Lhs || isCharArray!Lhs || isIntegral!Lhs) 1410 ){ 1411 static if(hasStatelessAllocator) 1412 return this.build(forward!lhs, this.storage.chars); 1413 else 1414 return this.build(this._allocator, forward!lhs, this.storage.chars); 1415 } 1416 1417 1418 1419 /** 1420 Same as operator `in` 1421 1422 1423 Examples: 1424 -------------------- 1425 BasicString!char str = "a123読"; 1426 1427 assert(str.contains('a')); 1428 assert(!str.contains('b')); 1429 assert(str.contains('3')); 1430 assert(str.contains('読')); 1431 -------------------- 1432 */ 1433 public bool contains(Elm)(scope auto ref Elm elm)scope const{ 1434 static if(isSomeChar!Elm){ 1435 static if(Elm.sizeof <= CharType.sizeof){ 1436 auto chars = this.storage.chars; 1437 foreach(c; chars){ 1438 if(elm == c) 1439 return true; 1440 } 1441 1442 return false; 1443 1444 } 1445 else{ 1446 auto chars = this.storage.chars; 1447 if(chars.length == 0) 1448 return false; 1449 1450 do{ 1451 if(decode(chars) == elm) 1452 return true; 1453 }while(chars.length); 1454 1455 return false; 1456 } 1457 } 1458 else{ 1459 static assert(0, "TODO"); 1460 } 1461 } 1462 1463 1464 1465 /** 1466 Operator `in` 1467 1468 1469 Examples: 1470 -------------------- 1471 BasicString!char str = "a123読"; 1472 1473 assert('a' in str); 1474 assert('b' !in str); 1475 assert('3' in str); 1476 assert('読' in str); 1477 -------------------- 1478 */ 1479 public bool opBinaryRight(string op, Elm)(scope auto ref Elm elm)scope const 1480 if(op == "in"){ 1481 return this.contains(forward!elm); 1482 } 1483 1484 1485 1486 /** 1487 Calculates the hash value of string. 1488 */ 1489 public size_t toHash()const pure nothrow @safe @nogc{ 1490 return hashOf(this.storage.chars); 1491 } 1492 1493 1494 1495 /** 1496 Compares the contents of a string with another string, range, char/wchar/dchar or integer. 1497 1498 Returns `true` if they are equal, `false` otherwise 1499 1500 Examples: 1501 -------------------- 1502 BasicString!char str = "123"; 1503 1504 assert(str == "123"); 1505 assert("123" == str); 1506 1507 assert(str == "123"w); 1508 assert("123"w == str); 1509 1510 assert(str == "123"d); 1511 assert("123"d == str); 1512 1513 assert(str == BasicString!wchar("123")); 1514 assert(BasicString!wchar("123") == str); 1515 1516 assert(str == 123); 1517 assert(123 == str); 1518 1519 import std.range : only; 1520 assert(str == only('1', '2', '3')); 1521 assert(only('1', '2', '3') == str); 1522 -------------------- 1523 */ 1524 public bool opEquals(scope const CharType[] rhs)const scope pure nothrow @safe @nogc{ 1525 return this._op_equals(rhs[]); 1526 } 1527 1528 /// ditto 1529 public bool opEquals(Rhs)(scope auto ref Rhs rhs)const scope 1530 if(isBasicString!Rhs || isSomeChar!Rhs || isSomeString!Rhs || isCharArray!Rhs || isIntegral!Rhs || isInputCharRange!Rhs){ 1531 1532 static if(isBasicString!Rhs){ 1533 return this._op_equals(rhs.storage.chars); 1534 } 1535 else static if(isSomeString!Rhs || isCharArray!Rhs){ 1536 return this._op_equals(rhs[]); 1537 } 1538 else static if(isSomeChar!Rhs){ 1539 import std.range : only; 1540 return this._op_equals(only(rhs)); 1541 } 1542 else static if(isIntegral!Rhs){ 1543 import std.conv : toChars; 1544 return this._op_equals(toChars(rhs + 0)); 1545 } 1546 else static if(isInputRange!Rhs){ 1547 return this._op_equals(rhs); 1548 } 1549 else{ 1550 static assert(0, "invalid type '" ~ Rhs.stringof ~ "'"); 1551 } 1552 } 1553 1554 // 1555 private bool _op_equals(Range)(scope auto ref Range rhs)const scope 1556 if(isInputCharRange!Range){ 1557 alias RhsChar = Unqual!(ElementEncodingType!Range); 1558 auto lhs = this.storage.chars; 1559 1560 enum bool lengthComperable = hasLength!Range && is(Unqual!CharType == RhsChar); 1561 1562 static if(lengthComperable){ 1563 if(lhs.length != rhs.length) 1564 return false; 1565 } 1566 /+TODO: else static if(hasLength!Range){ //TODO 1567 static if(CharType.sizeof < RhsChar.sizeof){ 1568 if(lhs.length * (RhsChar.sizeof / CharType.sizeof) < rhs.length) 1569 return false; 1570 } 1571 else static if(CharType.sizeof > RhsChar.sizeof){ 1572 if(lhs.length > rhs.length * (CharType.sizeof / RhsChar.sizeof)) 1573 return false; 1574 1575 } 1576 else static assert(0, "no impl") 1577 }+/ 1578 1579 while(true){ 1580 static if(lengthComperable){ 1581 if(lhs.length == 0){ 1582 assert(rhs.empty); 1583 return true; 1584 } 1585 } 1586 else{ 1587 if(lhs.length == 0) 1588 return rhs.empty; 1589 1590 if(rhs.empty) 1591 return false; 1592 } 1593 1594 static if(is(Unqual!CharType == RhsChar)){ 1595 1596 const a = lhs.frontCodeUnit; 1597 lhs.popFrontCodeUnit(); 1598 1599 const b = rhs.frontCodeUnit; 1600 rhs.popFrontCodeUnit(); 1601 1602 static assert(is(Unqual!(typeof(a)) == Unqual!(typeof(b)))); 1603 } 1604 else{ 1605 const a = decode(lhs); 1606 const b = decode(rhs); 1607 } 1608 1609 if(a != b) 1610 return false; 1611 1612 } 1613 } 1614 1615 1616 /** 1617 Compares the contents of a string with another string, range, char/wchar/dchar or integer. 1618 */ 1619 public int opCmp(scope const CharType[] rhs)const scope pure nothrow @safe @nogc{ 1620 return this._op_cmp(rhs[]); 1621 } 1622 1623 /// ditto 1624 public int opCmp(Rhs)(scope auto ref Rhs rhs)const scope 1625 if(isBasicString!Rhs || isSomeChar!Rhs || isSomeString!Rhs || isCharArray!Rhs || isIntegral!Rhs || isInputCharRange!Rhs){ 1626 1627 static if(isBasicString!Rhs){ 1628 return this._op_cmp(rhs.storage.chars); 1629 } 1630 else static if(isSomeString!Rhs || isCharArray!Rhs){ 1631 return this._op_cmp(rhs[]); 1632 } 1633 else static if(isSomeChar!Rhs){ 1634 import std.range : only; 1635 return this._op_cmp(only(rhs)); 1636 } 1637 else static if(isIntegral!Rhs){ 1638 import std.conv : toChars; 1639 return this._op_cmp(toChars(rhs + 0)); 1640 } 1641 else static if(isInputRange!Rhs){ 1642 return this._op_cmp(val); 1643 } 1644 else{ 1645 static assert(0, "invalid type '" ~ Rhs.stringof ~ "'"); 1646 } 1647 } 1648 1649 // 1650 private int _op_cmp(Range)(Range rhs)const scope 1651 if(isInputCharRange!Range){ 1652 import std.range : empty, ElementEncodingType; 1653 1654 auto lhs = this.storage.chars; 1655 alias RhsChar = Unqual!(ElementEncodingType!Range); 1656 1657 while(true){ 1658 if(lhs.empty) 1659 return rhs.empty ? 0 : -1; 1660 1661 if(rhs.empty) 1662 return 1; 1663 1664 static if(is(Unqual!CharType == RhsChar)){ 1665 1666 const a = lhs.frontCodeUnit; 1667 lhs.popFrontCodeUnit(); 1668 1669 const b = rhs.frontCodeUnit; 1670 rhs.popFrontCodeUnit(); 1671 1672 static assert(is(Unqual!(typeof(a)) == Unqual!(typeof(b)))); 1673 } 1674 else{ 1675 const a = decode(lhs); 1676 const b = decode(rhs); 1677 } 1678 1679 if(a < b) 1680 return -1; 1681 1682 if(a > b) 1683 return 1; 1684 } 1685 } 1686 1687 1688 1689 /** 1690 Return slice of all character. 1691 1692 The slice returned may be invalidated by further calls to other member functions that modify the object. 1693 1694 Examples: 1695 -------------------- 1696 ()@system{ 1697 BasicString!char str = "123"; 1698 1699 char[] slice = str[]; 1700 assert(slice.length == str.length); 1701 assert(slice.ptr is str.ptr); 1702 1703 str.reserve(str.capacity * 2); 1704 assert(slice.length == str.length); 1705 assert(slice.ptr !is str.ptr); // slice contains dangling pointer. 1706 }(); 1707 ()@safe{ 1708 FixedString!(char, 20) str = "123"; 1709 1710 char[] slice = str[]; 1711 assert(slice.length == str.length); 1712 assert(slice.ptr is str.ptr); 1713 1714 assert(slice == "123"); 1715 }(); 1716 -------------------- 1717 */ 1718 static if(allowHeap) 1719 public inout(CharType)[] opIndex()inout return pure nothrow @system @nogc{ 1720 return this.storage.chars; 1721 } 1722 else 1723 public inout(CharType)[] opIndex()inout return pure nothrow @trusted @nogc{ 1724 return this.storage.chars; 1725 } 1726 1727 1728 1729 /** 1730 Returns character at specified location `pos`. 1731 1732 Examples: 1733 -------------------- 1734 BasicString!char str = "abcd"; 1735 1736 assert(str[1] == 'b'); 1737 -------------------- 1738 */ 1739 public CharType opIndex(const size_t pos)const scope pure nothrow @trusted @nogc{ 1740 this._bounds_check(pos); 1741 1742 return *(this.ptr + pos); 1743 } 1744 1745 1746 1747 /** 1748 Returns a slice [begin .. end]. If the requested substring extends past the end of the string, the returned slice is [begin .. length()]. 1749 1750 The slice returned may be invalidated by further calls to other member functions that modify the object. 1751 1752 Examples: 1753 -------------------- 1754 ()@system{ 1755 BasicString!char str = "123456"; 1756 assert(str[1 .. $-1] == "2345"); 1757 }(); 1758 ()@safe{ 1759 FixedString!(char, 20) str = "123456"; 1760 assert(str[1 .. $-1] == "2345"); 1761 }(); 1762 -------------------- 1763 */ 1764 static if(allowHeap) 1765 public inout(CharType)[] opSlice(const size_t begin, const size_t end)inout return pure nothrow @system @nogc{ 1766 this._bounds_check([begin, end]); 1767 const len = this.length; 1768 1769 return this.ptr[min(len, begin) .. min(len, end)]; 1770 } 1771 else 1772 public inout(CharType)[] opSlice(const size_t begin, const size_t end)inout return pure nothrow @trusted @nogc{ 1773 this._bounds_check([begin, end]); 1774 const len = this.length; 1775 1776 return this.ptr[min(len, begin) .. min(len, end)]; 1777 } 1778 1779 1780 1781 /** 1782 Assign character at specified location `pos` to value `val`. 1783 1784 Returns 'val'. 1785 1786 Examples: 1787 -------------------- 1788 BasicString!char str = "abcd"; 1789 1790 str[1] = 'x'; 1791 1792 assert(str == "axcd"); 1793 -------------------- 1794 */ 1795 public CharType opIndexAssign(const CharType val, const size_t pos)scope pure nothrow @trusted @nogc{ 1796 this._bounds_check(pos); 1797 1798 return *(this.ptr + pos) = val; 1799 } 1800 1801 1802 1803 /** 1804 Returns the length of the string, in terms of number of characters. 1805 1806 Same as `length()`. 1807 */ 1808 public size_t opDollar()const scope pure nothrow @safe @nogc{ 1809 return this.length; 1810 } 1811 1812 1813 1814 /** 1815 Swaps the contents of `this` and `rhs`. 1816 1817 Examples: 1818 -------------------- 1819 BasicString!char a = "1"; 1820 BasicString!char b = "2"; 1821 1822 a.proxySwap(b); 1823 assert(a == "2"); 1824 assert(b == "1"); 1825 1826 import std.algorithm.mutation : swap; 1827 1828 swap(a, b); 1829 assert(a == "1"); 1830 assert(b == "2"); 1831 -------------------- 1832 */ 1833 public void proxySwap(scope ref typeof(this) rhs)scope pure nothrow @trusted @nogc{ 1834 import std.algorithm.mutation : swap; 1835 swap( 1836 *(()@trusted => &this.storage )(), 1837 *(()@trusted => &rhs.storage )() 1838 ); 1839 1840 static if(!hasStatelessAllocator) 1841 swap( 1842 *(()@trusted => &this._allocator )(), 1843 *(()@trusted => &rhs._allocator )() 1844 ); 1845 1846 } 1847 1848 1849 1850 1851 /+public size_t appendMore(Params...)(scope auto ref Params params)scope{ 1852 size_t result = 0; 1853 1854 static foreach(alias param; params){ 1855 result += this.append(param); 1856 } 1857 1858 return result; 1859 }+/ 1860 1861 1862 1863 /** 1864 Extends the `BasicString` by appending additional characters at the end of string. 1865 1866 Return number of inserted characters. 1867 1868 Parameters: 1869 `val` appended value. 1870 1871 `count` Number of times `val` is appended. 1872 1873 Examples: 1874 -------------------- 1875 { 1876 BasicString!char str = "123456"; 1877 1878 str.append('x', 2); 1879 assert(str == "123456xx"); 1880 } 1881 1882 { 1883 BasicString!char str = "123456"; 1884 1885 str.append("abc"); 1886 assert(str == "123456abc"); 1887 } 1888 1889 { 1890 BasicString!char str = "123456"; 1891 BasicString!char str2 = "xyz"; 1892 1893 str.append(str2); 1894 assert(str == "123456xyz"); 1895 } 1896 1897 { 1898 BasicString!char str = "12"; 1899 1900 str.append(+34); 1901 assert(str == "1234"); 1902 } 1903 -------------------- 1904 */ 1905 public size_t append(const CharType[] val, const size_t count = 1)scope{ 1906 return this._append_impl(val, count); 1907 } 1908 1909 /// ditto 1910 public size_t append(Val)(scope auto ref Val val, const size_t count = 1)scope 1911 if(isBasicString!Val || isSomeChar!Val || isSomeString!Val || isCharArray!Val || isIntegral!Val){ 1912 static if(isBasicString!Val){ 1913 return this._append_impl(val.storage.chars, count); 1914 } 1915 else static if(isSomeString!Val || isCharArray!Val){ 1916 return this._append_impl(val[], count); 1917 } 1918 else static if(isSomeChar!Val || isIntegral!Val){ 1919 return this._append_impl(val, count); 1920 } 1921 else{ 1922 static assert(0, "invalid type '" ~ Val.stringof ~ "'"); 1923 } 1924 } 1925 1926 /// ditto 1927 public size_t append(Val)(scope Val val)scope 1928 if(!isArray!Val && isInputCharRange!Val){ 1929 size_t result = 0; 1930 1931 static if(hasLength!Val){ 1932 this.reserve(this.length + val.length); 1933 } 1934 1935 while(!val.empty){ 1936 result += this._append_impl(val.front, 1); 1937 val.popFront(); 1938 } 1939 1940 return result; 1941 } 1942 1943 private size_t _append_impl(Val)(const Val val, const size_t count)scope 1944 if(isSomeChar!Val || isSomeString!Val || isIntegral!Val){ 1945 if(count == 0) 1946 return 0; 1947 1948 const size_t old_length = this.length; 1949 const size_t new_count = count * encodedLength!CharType(val); 1950 1951 CharType[] new_chars = this._expand(new_count); 1952 const size_t tmp = val.encodeTo(new_chars, count); 1953 assert(tmp == new_count); 1954 1955 return tmp; 1956 } 1957 1958 1959 1960 /** 1961 Inserts additional characters into the `BasicString` right before the character indicated by `pos` or `ptr`. 1962 1963 Return number of inserted characters. 1964 1965 If parameters are out of range then there is no inserted value and in debug mode assert throw error. 1966 1967 Parameters are out of range if `pos` is larger then `.length` or `ptr` is smaller then `this.ptr` or `ptr` point to address larger then `this.ptr + this.length` 1968 1969 Parameters: 1970 `pos` Insertion point, the new contents are inserted before the character at position `pos`. 1971 1972 `ptr` Pointer pointing to the insertion point, the new contents are inserted before the character pointed by ptr. 1973 1974 `val` Value inserted before insertion point `pos` or `ptr`. 1975 1976 `count` Number of times `val` is inserted. 1977 1978 Examples: 1979 -------------------- 1980 { 1981 BasicString!char str = "123456"; 1982 1983 str.insert(2, 'x', 2); 1984 assert(str == "12xx3456"); 1985 } 1986 1987 { 1988 BasicString!char str = "123456"; 1989 1990 str.insert(2, "abc"); 1991 assert(str == "12abc3456"); 1992 } 1993 1994 { 1995 BasicString!char str = "123456"; 1996 BasicString!char str2 = "abc"; 1997 1998 str.insert(2, str2); 1999 assert(str == "12abc3456"); 2000 } 2001 2002 { 2003 BasicString!char str = "123456"; 2004 2005 str.insert(str.ptr + 2, 'x', 2); 2006 assert(str == "12xx3456"); 2007 } 2008 2009 { 2010 BasicString!char str = "123456"; 2011 2012 str.insert(str.ptr + 2, "abc"); 2013 assert(str == "12abc3456"); 2014 } 2015 2016 { 2017 BasicString!char str = "123456"; 2018 BasicString!char str2 = "abc"; 2019 2020 str.insert(str.ptr + 2, str2); 2021 assert(str == "12abc3456"); 2022 } 2023 -------------------- 2024 2025 */ 2026 public size_t insert(const size_t pos, const scope CharType[] val, const size_t count = 1)scope{ 2027 return this._insert(pos, val, count); 2028 } 2029 2030 /// ditto 2031 public size_t insert(Val)(const size_t pos, auto ref const scope Val val, const size_t count = 1)scope 2032 if(isBasicString!Val || isSomeChar!Val || isSomeString!Val || isIntegral!Val){ 2033 2034 static if(isBasicString!Val || isSomeString!Val){ 2035 return this._insert(pos, val[], count); 2036 } 2037 else static if(isSomeChar!Val || isIntegral!Val){ 2038 return this._insert(pos, val, count); 2039 } 2040 else{ 2041 static assert(0, "invalid type '" ~ Val.stringof ~ "'"); 2042 } 2043 } 2044 2045 /// ditto 2046 public size_t insert(const CharType* ptr, const scope CharType[] val, const size_t count = 1)scope{ 2047 const size_t pos = this._insert_ptr_to_pos(ptr); 2048 2049 return this._insert(pos, val, count); 2050 } 2051 2052 /// ditto 2053 public size_t insert(Val)(const CharType* ptr, auto ref const scope Val val, const size_t count = 1)scope 2054 if(isBasicString!Val || isSomeChar!Val || isSomeString!Val || isIntegral!Val){ 2055 const size_t pos = this._insert_ptr_to_pos(ptr); 2056 2057 static if(isBasicString!Val || isSomeString!Val){ 2058 return this._insert(pos, val[], count); 2059 } 2060 else static if(isSomeChar!Val || isIntegral!Val){ 2061 return this._insert(pos, val, count); 2062 } 2063 else{ 2064 static assert(0, "invalid type '" ~ Val.stringof ~ "'"); 2065 } 2066 } 2067 2068 // 2069 private size_t _insert(Val)(const size_t pos, const Val val, const size_t count)scope 2070 if(isSomeChar!Val || isSomeString!Val || isIntegral!I){ 2071 2072 const size_t new_count = count * encodedLength!CharType(val); 2073 if(new_count == 0) 2074 return 0; 2075 2076 CharType[] new_chars = this._expand_move(pos, new_count); 2077 2078 return val.encodeTo(new_chars, count); 2079 } 2080 2081 // 2082 private size_t _insert_ptr_to_pos(const CharType* ptr)scope const pure nothrow @trusted @nogc{ 2083 return (ptr > this.ptr) 2084 ? (ptr - this.ptr) 2085 : 0; 2086 } 2087 2088 2089 2090 /** 2091 Removes specified characters from the string. 2092 2093 Parameters: 2094 `pos` position of first character to be removed. 2095 2096 `n` number of character to be removed. 2097 2098 `ptr` pointer to character to be removed. 2099 2100 `slice` sub-slice to be removed, `slice` must be subset of `this` 2101 2102 Examples: 2103 -------------------- 2104 { 2105 BasicString!char str = "123456"; 2106 2107 str.erase(2); 2108 assert(str == "12"); 2109 } 2110 2111 { 2112 BasicString!char str = "123456"; 2113 2114 str.erase(1, 2); 2115 assert(str == "1456"); 2116 } 2117 2118 { 2119 BasicString!char str = "123456"; 2120 2121 str.erase(str.ptr + 2); 2122 assert(str == "12"); 2123 } 2124 2125 { 2126 BasicString!char str = "123456"; 2127 2128 str.erase(str[1 .. $-1]); 2129 assert(str == "16"); 2130 } 2131 -------------------- 2132 */ 2133 public void erase(const size_t pos)scope pure nothrow @trusted @nogc{ 2134 this.storage.length = min(this.length, pos); 2135 } 2136 2137 /// ditto 2138 public void erase(const size_t pos, const size_t n)scope pure nothrow @trusted @nogc{ 2139 const chars = this.storage.chars; 2140 2141 if(pos >= this.length) 2142 return; 2143 2144 this._erase(pos, n); 2145 } 2146 2147 /// ditto 2148 public void erase(scope const CharType* ptr)scope pure nothrow @trusted @nogc{ 2149 const chars = this.storage.chars; 2150 2151 if(ptr <= chars.ptr) 2152 this.storage.length = 0; 2153 else 2154 this.storage.length = min(chars.length, ptr - chars.ptr); 2155 } 2156 2157 /// ditto 2158 public void erase(scope const CharType[] slice)scope pure nothrow @trusted @nogc{ 2159 const chars = this.storage.chars; 2160 2161 if(slice.ptr <= chars.ptr){ 2162 const size_t offset = (chars.ptr - slice.ptr); 2163 2164 if(slice.length <= offset) 2165 return; 2166 2167 enum size_t pos = 0; 2168 const size_t len = (slice.length - offset); 2169 2170 this._erase(pos, len); 2171 } 2172 else{ 2173 const size_t offset = (slice.ptr - chars.ptr); 2174 2175 if(chars.length <= offset) 2176 return; 2177 2178 alias pos = offset; 2179 const size_t len = slice.length; 2180 2181 this._erase(pos, len); 2182 2183 } 2184 } 2185 2186 // 2187 private void _erase(const size_t pos, const size_t n)scope pure nothrow @trusted @nogc{ 2188 const len = this.length; 2189 assert(pos < len); 2190 2191 const size_t top = (pos + n); 2192 2193 if(top >= len) 2194 this.storage.length = pos; 2195 else if(n != 0) 2196 this._reduce_move(top, n); 2197 } 2198 2199 2200 2201 /** 2202 Replaces the portion of the string that begins at character `pos` and spans `len` characters (or the part of the string in the slice `slice`) by new contents. 2203 2204 Parameters: 2205 `pos` position of the first character to be replaced. 2206 2207 `len` number of characters to replace (if the string is shorter, as many characters as possible are replaced). 2208 2209 `slice` sub-slice to be removed, `slice` must be subset of `this` 2210 2211 `val` inserted value. 2212 2213 `count` number of times `val` is inserted. 2214 2215 Examples: 2216 -------------------- 2217 { 2218 BasicString!char str = "123456"; 2219 2220 str.replace(2, 2, 'x', 5); 2221 assert(str == "12xxxxx56"); 2222 } 2223 2224 { 2225 BasicString!char str = "123456"; 2226 2227 str.replace(2, 2, "abcdef"); 2228 assert(str == "12abcdef56"); 2229 } 2230 2231 { 2232 BasicString!char str = "123456"; 2233 BasicString!char str2 = "xy"; 2234 2235 str.replace(2, 3, str2); 2236 assert(str == "12xy56"); 2237 } 2238 2239 { 2240 BasicString!char str = "123456"; 2241 2242 str.replace(str[2 .. 4], 'x', 5); 2243 assert(str == "12xxxxx56"); 2244 } 2245 2246 { 2247 BasicString!char str = "123456"; 2248 2249 str.replace(str[2 .. 4], "abcdef"); 2250 assert(str == "12abcdef56"); 2251 } 2252 2253 { 2254 BasicString!char str = "123456"; 2255 BasicString!char str2 = "xy"; 2256 2257 str.replace(str[2 .. $], str2); 2258 assert(str == "12xy56"); 2259 } 2260 -------------------- 2261 */ 2262 public ref typeof(this) replace(const size_t pos, const size_t len, scope const CharType[] val, const size_t count = 1)return{ 2263 this._replace(pos, len, val, count); 2264 return this; 2265 } 2266 2267 /// ditto 2268 public ref typeof(this) replace(Val)(const size_t pos, const size_t len, scope auto ref const Val val, const size_t count = 1)return 2269 if(isBasicString!Val || isSomeChar!Val || isSomeString!Val || isIntegral!Val || isCharArray!Val){ 2270 2271 static if(isBasicString!Val || isSomeString!Val || isCharArray!Val){ 2272 this._replace(pos, len, val[], count); 2273 } 2274 else static if(isSomeChar!Val || isIntegral!Val){ 2275 this._replace(pos, len, val, count); 2276 } 2277 else{ 2278 static assert(0, "invalid type '" ~ Val.stringof ~ "'"); 2279 } 2280 2281 return this; 2282 } 2283 2284 /// ditto 2285 public ref typeof(this) replace(scope const CharType[] slice, scope const CharType[] val, const size_t count = 1)return{ 2286 this._replace(slice, val, count); 2287 return this; 2288 } 2289 2290 /// ditto 2291 public ref typeof(this) replace(Val)(scope const CharType[] slice, scope auto ref const Val val, const size_t count = 1)return 2292 if(isBasicString!Val || isSomeChar!Val || isSomeString!Val || isIntegral!Val || isCharArray!Val){ 2293 2294 static if(isBasicString!Val || isSomeString!Val || isCharArray!Val){ 2295 this._replace(slice, val[], count); 2296 } 2297 else static if(isSomeChar!Val || isIntegral!Val){ 2298 this._replace(slice, val, count); 2299 } 2300 else{ 2301 static assert(0, "invalid type '" ~ Val.stringof ~ "'"); 2302 } 2303 2304 return this; 2305 } 2306 2307 // 2308 private void _replace(Val)(scope const CharType[] slice, scope const Val val, const size_t count)scope 2309 if(isSomeChar!Val || isSomeString!Val || isIntegral!Val){ 2310 const chars = this.storage.chars; 2311 2312 if(slice.ptr < chars.ptr){ 2313 const size_t offset = (()@trusted => chars.ptr - slice.ptr)(); 2314 const size_t pos = 0; 2315 const size_t len = (slice.length > offset) 2316 ? (slice.length - offset) 2317 : 0; 2318 2319 this.replace(pos, len, val, count); 2320 } 2321 else{ 2322 const size_t offset = (()@trusted => slice.ptr - chars.ptr)(); 2323 const size_t pos = offset; 2324 const size_t len = slice.length; 2325 2326 this.replace(pos, len, val, count); 2327 } 2328 } 2329 2330 // 2331 private void _replace(Val)(const size_t pos, const size_t len, scope const Val val, const size_t count)scope 2332 if(isSomeChar!Val || isSomeString!Val || isIntegral!Val){ 2333 2334 const size_t new_count = count * encodedLength!CharType(val); 2335 if(new_count == 0){ 2336 if(pos < this.length) 2337 this.erase(pos, len); 2338 2339 return; 2340 } 2341 2342 assert(new_count != 0); 2343 2344 auto chars = this.storage.chars; 2345 const size_t old_length = chars.length; 2346 const size_t begin = min(pos, chars.length); //alias begin = pos; 2347 2348 const size_t end = min(chars.length, (pos + len)); 2349 const size_t new_len = min(end - begin, new_count); 2350 //const size_t new_len = min(len, new_count); 2351 //const size_t end = (begin + new_len); 2352 2353 2354 if(begin == end){ 2355 ///insert: 2356 CharType[] new_chars = this._expand_move(begin, new_count); 2357 const x = val.encodeTo(new_chars, count); 2358 assert(x == new_count); 2359 2360 } 2361 else if(new_count == new_len){ 2362 ///exact assign: 2363 const x = val.encodeTo(chars[begin .. end], count); 2364 assert(x == new_count); 2365 } 2366 else if(new_count < new_len){ 2367 ///asign + erase: 2368 const x = val.encodeTo(chars[begin .. end], count); 2369 assert(x == new_count); 2370 2371 ()@trusted{ 2372 this._reduce_move(end, (new_len - new_count)); 2373 }(); 2374 } 2375 else{ 2376 ///asing + expand(insert): 2377 assert(new_count > new_len); 2378 2379 const size_t expand_len = (new_count - new_len); 2380 2381 CharType[] new_chars = this._expand_move(end, expand_len); 2382 2383 const x = val.encodeTo((()@trusted => (new_chars.ptr - new_len)[0 .. new_count])(), count); 2384 assert(x == new_count); 2385 } 2386 } 2387 2388 2389 ///Alias to append. 2390 public alias put = append; 2391 2392 ///Alias to `popBackCodeUnit`. 2393 public alias popBack = popBackCodeUnit; 2394 2395 ///Alias to `frontCodeUnit`. 2396 public alias front = frontCodeUnit; 2397 2398 ///Alias to `backCodeUnit`. 2399 public alias back = backCodeUnit; 2400 2401 2402 /** 2403 Static function which return `BasicString` construct from arguments `args`. 2404 2405 Parameters: 2406 `allocator` exists only if template parameter `_Allocator` has state. 2407 2408 `args` values of type `char|wchar|dchar` array/slice/character or `BasicString`. 2409 2410 Examples: 2411 -------------------- 2412 BasicString!char str = BasicString!char.build('1', cast(dchar)'2', "345"d, BasicString!wchar("678")); 2413 2414 assert(str == "12345678"); 2415 -------------------- 2416 */ 2417 public static typeof(this) build(Args...)(scope auto ref const Args args) 2418 if(Args.length > 0 && !is(immutable Args[0] == immutable AllocatorType)){ 2419 import core.lifetime : forward; 2420 2421 auto result = BasicString.init; 2422 2423 result._build_impl(forward!args); 2424 2425 //return move(result); 2426 return (()@trusted => move(*&result) )(); 2427 } 2428 2429 /// ditto 2430 public static typeof(this) build(Args...)(AllocatorType allocator, scope auto ref const Args args){ 2431 import core.lifetime : forward; 2432 2433 auto result = BasicString(forward!allocator); 2434 2435 result._build_impl(forward!args); 2436 2437 //return move(result); 2438 return (()@trusted => move(*&result) )(); 2439 } 2440 2441 private void _build_impl(Args...)(scope auto ref const Args args)scope{ 2442 import std.traits : isArray; 2443 2444 assert(this.empty); 2445 //this.clear(); 2446 size_t new_length = 0; 2447 2448 static foreach(enum I, alias Arg; Args){ 2449 static if(isBasicString!Arg) 2450 new_length += encodedLength!CharType(args[I].storage.chars); 2451 else static if(isArray!Arg && isSomeChar!(ElementEncodingType!Arg)) 2452 new_length += encodedLength!CharType(args[I][]); 2453 else static if(isSomeChar!Arg) 2454 new_length += encodedLength!CharType(args[I]); 2455 else static if(isIntegral!Arg) 2456 new_length += encodedLength!CharType(args[I]); 2457 else static assert(0, "wrong type '" ~ typeof(args[I]).stringof ~ "'"); 2458 } 2459 2460 if(new_length == 0) 2461 return; 2462 2463 2464 alias result = this; 2465 2466 result.reserve(new_length); 2467 2468 CharType[] data = result.storage.allocatedChars; 2469 2470 static foreach(enum I, alias Arg; Args){ 2471 static if(isBasicString!Arg) 2472 data = data[args[I].storage.chars.encodeTo(data) .. $]; 2473 else static if(isArray!Arg) 2474 data = data[args[I][].encodeTo(data) .. $]; 2475 else static if(isSomeChar!Arg) 2476 data = data[args[I].encodeTo(data) .. $]; 2477 else static if(isIntegral!Arg) 2478 data = data[args[I].encodeTo(data) .. $]; 2479 else static assert(0, "wrong type '" ~ Arg.stringof ~ "'"); 2480 } 2481 2482 result.storage.length = new_length; 2483 } 2484 2485 2486 2487 public immutable(CharType)[] toGcString()scope const pure nothrow @trusted{ 2488 return this[].idup; 2489 } 2490 2491 2492 2493 static if(!allowHeap){ 2494 private alias _allocator = statelessAllcoator!NullAllocator; 2495 private enum safeAllocate = true; 2496 } 2497 else static if(hasStatelessAllocator){ 2498 public alias _allocator = statelessAllcoator!AllocatorType; 2499 2500 private enum safeAllocate = isSafe!((){ 2501 size_t capacity = size_t.max; 2502 2503 cast(void)_allocator.allocate(capacity); 2504 }); 2505 } 2506 else{ 2507 public AllocatorType _allocator; 2508 2509 private enum safeAllocate = isSafe!((ref AllocatorType a){ 2510 size_t capacity = size_t.max; 2511 2512 cast(void)a.allocate(capacity); 2513 }); 2514 } 2515 2516 private Storage storage; 2517 2518 //grow: 2519 private static size_t _grow_capacity(size_t old_capacity, size_t new_capacity)pure nothrow @safe @nogc{ 2520 static if(minimalCapacity == 0) 2521 return max(2, old_capacity * 2, new_capacity); 2522 else 2523 return max(old_capacity * 2, new_capacity); 2524 } 2525 2526 //enforce: 2527 private void _enforce(bool con, string msg)const pure nothrow @safe @nogc{ 2528 if(!con)assert(0, msg); 2529 } 2530 2531 //allocation: 2532 private void _allocate_heap()(ref Storage.Heap heap, size_t new_capacity){ 2533 new_capacity = Storage.heapCapacity(new_capacity); //assert(new_capacity == storage.heapCapacity(new_capacity)); 2534 2535 void[] data = this._allocator.allocate(new_capacity * CharType.sizeof); 2536 2537 _enforce(data.length != 0, "allocation fail"); 2538 2539 heap.ptr = (()@trusted => cast(CharType*)data.ptr )(); 2540 heap.capacity = new_capacity; 2541 heap.length = length; 2542 } 2543 2544 private void _deallocate_heap()(const ref Storage.Heap heap){ 2545 void[] data = (()@trusted => cast(void[])heap.data )(); 2546 2547 static if(N == 0){ 2548 if(data.length == 0) 2549 return; 2550 } 2551 2552 static if(safeAllocate) 2553 const d = ()@trusted{ 2554 return this._allocator.deallocate(data); 2555 }(); 2556 else 2557 const d = this._allocator.deallocate(data); 2558 2559 _enforce(d, "deallocation fail"); 2560 } 2561 2562 private void _reallocate_heap(bool optional = false)(ref Storage.Heap heap, size_t new_capacity){ 2563 new_capacity = Storage.heapCapacity(new_capacity); //assert(new_capacity == storage.heapCapacity(new_capacity)); 2564 void[] data = heap.data; 2565 2566 static if(hasMember!(typeof(_allocator), "reallocate")){ 2567 static if(safeAllocate) 2568 const bool reallocated = ()@trusted{ 2569 return this._allocator.reallocate(data, new_capacity * CharType.sizeof); 2570 }(); 2571 else 2572 const bool reallocated = this._allocator.reallocate(data, new_capacity * CharType.sizeof); 2573 } 2574 else{ 2575 const bool reallocated = false; 2576 } 2577 2578 if(reallocated){ 2579 ()@trusted{ 2580 heap.ptr = cast(CharType*)data.ptr; 2581 heap.capacity = new_capacity; 2582 2583 }(); 2584 assert(data.length / CharType.sizeof == new_capacity); 2585 return; 2586 } 2587 2588 static if(!optional){ 2589 Storage.Heap heap_new; 2590 2591 { 2592 this._allocate_heap(heap_new, new_capacity); 2593 2594 memCopy(heap_new.ptr, heap.ptr, heap.length); 2595 heap_new.length = heap.length; 2596 } 2597 2598 this._deallocate_heap(heap); 2599 2600 heap = heap_new; 2601 } 2602 } 2603 2604 //reduce/expand: 2605 private void _reduce_move(const size_t pos, const size_t n)scope pure nothrow @system @nogc{ 2606 assert(pos <= this.length); 2607 assert(pos >= n); 2608 assert(n > 0); 2609 2610 auto chars = this.storage.chars; 2611 const size_t len = (chars.length - pos); 2612 2613 memMove( 2614 chars.ptr + (pos - n), 2615 (chars.ptr + pos), 2616 len 2617 ); 2618 2619 this.storage.length = (chars.length - n); 2620 } 2621 2622 private CharType[] _expand_move(const size_t pos, const size_t n)scope return { 2623 assert(n > 0); 2624 2625 auto chars = this.storage.chars; 2626 if(pos >= chars.length) 2627 return this._expand(n); 2628 2629 const size_t new_length = (chars.length + n); 2630 this.reserve(new_length); 2631 2632 2633 return ()@trusted{ 2634 auto chars = this.storage.chars; 2635 this.storage.length = new_length; 2636 2637 const size_t len = (chars.length - pos); 2638 2639 memMove( 2640 (chars.ptr + pos + n), 2641 (chars.ptr + pos), 2642 len 2643 ); 2644 2645 return (chars.ptr + pos)[0 .. n]; 2646 }(); 2647 } 2648 2649 private CharType[] _expand(const size_t n)scope return{ 2650 const size_t old_length = this.length; 2651 const size_t new_length = (old_length + n); 2652 2653 this.reserve(new_length); 2654 2655 return ()@trusted{ 2656 this.storage.length = new_length; 2657 return this.storage.ptr[old_length .. new_length]; 2658 }(); 2659 } 2660 2661 //bounds check: 2662 private pragma(inline, true) void _bounds_check(const size_t pos)const pure nothrow @safe @nogc{ 2663 assert(pos < this.length); 2664 2665 version(BTL_STRING_BOUNDS_CHECK){ 2666 if(pos >= this.length){ 2667 assert(0, "btl.string bounds check error"); 2668 } 2669 } 2670 } 2671 2672 private pragma(inline, true) void _bounds_check(const size_t pos, const size_t len)const pure nothrow @safe @nogc{ 2673 assert(pos < this.length); 2674 assert((pos + len) <= this.length); 2675 2676 version(BTL_STRING_BOUNDS_CHECK){ 2677 if(pos >= this.length){ 2678 assert(0, "btl.string bounds check error"); 2679 } 2680 if((pos + len) > this.length){ 2681 assert(0, "btl.string bounds check error"); 2682 } 2683 } 2684 } 2685 2686 private pragma(inline, true) void _bounds_check(const size_t[2] index)const pure nothrow @safe @nogc{ 2687 assert(index[0] < this.length); 2688 assert(index[1] <= this.length); 2689 2690 version(BTL_STRING_BOUNDS_CHECK){ 2691 if(index[0] >= this.length){ 2692 assert(0, "btl.string bounds check error"); 2693 } 2694 if(index[1] > this.length){ 2695 assert(0, "btl.string bounds check error"); 2696 } 2697 } 2698 } 2699 2700 } 2701 } 2702 2703 2704 /// 2705 pure nothrow @safe @nogc unittest { 2706 alias String = BasicString!( 2707 char, //character type 2708 32, //additional padding to increas max size of small string (small string does not allocate memory). 2709 DefaultAllocator //allocator type (can be stateless or with state) 2710 ); 2711 2712 2713 //copy: 2714 { 2715 String a = "123"; 2716 String b = a; 2717 2718 a = "456"d; 2719 2720 assert(a == "456"); 2721 assert(b == "123"); 2722 } 2723 2724 2725 //append: 2726 { 2727 String str = "12"; 2728 2729 str.append("34"); //same as str += "34" 2730 str.append("56"w); //same as str += "56"w 2731 str.append(7); //same as str += 7; 2732 str.append('8'); 2733 2734 assert(str == "12345678"); 2735 2736 str.clear(); 2737 2738 assert(str.empty); 2739 } 2740 2741 //erase: 2742 { 2743 String str = "123456789"; 2744 2745 str.erase(2, 2); 2746 2747 assert(str == "1256789"); 2748 } 2749 2750 //insert: 2751 { 2752 String str = "123456789"; 2753 2754 str.insert(1, "xyz"); 2755 2756 assert(str == "1xyz23456789"); 2757 } 2758 2759 //replace: 2760 { 2761 String str = "123456789"; 2762 2763 str.replace(1, 2, "xyz"); 2764 2765 assert(str == "1xyz456789"); 2766 } 2767 2768 //slice to string: 2769 ()@trusted{ 2770 String str = "123456789"; 2771 2772 const(char)[] dstr = str[]; 2773 2774 assert(str == dstr); 2775 }(); 2776 } 2777 2778 2779 /** 2780 Alias to `BasicString` with different order of template parameters 2781 */ 2782 template BasicString( 2783 _Char, 2784 _Allocator, 2785 size_t N = 1 2786 ) 2787 if(isSomeChar!_Char && is(Unqual!_Char == _Char)){ 2788 alias BasicString = .BasicString!(_Char, N, _Allocator); 2789 } 2790 2791 2792 2793 2794 2795 /** 2796 Alias to `BasicString` with `void` allcoator (fixed capacity to `N` characters). 2797 */ 2798 alias FixedString(Char, size_t N) = BasicString!(Char, N, void); 2799 2800 2801 2802 /** 2803 Alias to `BasicString` with forced small string optimization fot min `N` characters. 2804 */ 2805 alias SmallString(Char, size_t N, _Allocator = DefaultAllocator) = BasicString!(Char, max(N, 1), _Allocator); 2806 2807 2808 2809 /** 2810 Alias to `BasicString` without small string optimization (`N` == 0). 2811 */ 2812 alias LargeString(Char, _Allocator = DefaultAllocator) = BasicString!(Char, 0, _Allocator); 2813 2814 2815 2816 /** 2817 Standard string type (alias to `BasicString!char`). 2818 */ 2819 alias String = BasicString!char; 2820 2821 2822 2823 //local traits: 2824 private { 2825 2826 //copy ctor: (TODO not in use for now) 2827 template hasCopyConstructor(From, To) 2828 if(is(immutable From == immutable To)){ 2829 2830 enum bool hasCopyConstructor = true 2831 && !is(From == shared) 2832 && isConstructable!(From, To) 2833 && (From.hasStatelessAllocator 2834 || isCopyConstructableElement!( 2835 GetAllocatorType!From, 2836 GetAllocatorType!To 2837 ) 2838 ) 2839 && (From.hasStatelessAllocator 2840 || isCopyConstructableElement!( 2841 GetAllocatorType!From, 2842 To.AllocatorType 2843 ) 2844 ); 2845 } 2846 2847 //Move Constructable: 2848 template isMoveConstructable(alias from, To){ 2849 alias From = typeof(from); 2850 2851 enum bool isMoveConstructable = true 2852 && !isRef!from 2853 && isConstructable!(From, To) 2854 && is(GetCharType!From*: GetCharType!To*) 2855 //&& (From.minimalCapacity == To.minimalCapacity) //TODO remove 2856 && is(immutable From.AllocatorType == immutable To.AllocatorType) 2857 && (From.hasStatelessAllocator 2858 || isMoveConstructableElement!( 2859 GetAllocatorType!From, 2860 GetAllocatorType!To 2861 ) 2862 ); 2863 } 2864 2865 //Move Assignable: 2866 template isMoveAssignable(alias from, To){ 2867 alias From = typeof(from); 2868 2869 enum isMoveAssignable = true 2870 && !isRef!from 2871 && isAssignable!(From, To) 2872 && is(GetCharType!From*: GetCharType!To*) 2873 //&& (From.minimalCapacity == To.minimalCapacity) //TODO remove 2874 && is(immutable From.AllocatorType == immutable To.AllocatorType) 2875 && (From.hasStatelessAllocator 2876 || isMoveAssignableElement!( 2877 GetAllocatorType!From, 2878 GetAllocatorType!To 2879 ) 2880 ); 2881 } 2882 2883 //Constructable: 2884 template isConstructable(From, To){ 2885 2886 enum isConstructable = true 2887 && !is(From == shared) 2888 && (From.hasStatelessAllocator 2889 ? is(immutable From.AllocatorType == immutable To.AllocatorType) 2890 : is(immutable From.AllocatorType : immutable To.AllocatorType) 2891 ); 2892 } 2893 2894 //Assignable: 2895 template isAssignable(From, To){ 2896 import std.traits : isMutable; 2897 2898 enum bool isAssignable = true 2899 && isMutable!To 2900 && !is(From == shared) && !is(To == shared); 2901 } 2902 2903 2904 template GetCharType(Vec){ 2905 alias GetCharType = CopyTypeQualifiers!(Vec, Vec.CharType); 2906 } 2907 2908 template GetAllocatorType(Vec){ 2909 alias GetAllocatorType = CopyTypeQualifiers!(Vec, Vec.AllocatorType); 2910 } 2911 2912 } 2913 2914 //encoding: 2915 private{ 2916 2917 size_t encodedLength(_Char, From)(const scope From from)pure nothrow @nogc @safe 2918 if(isSomeChar!_Char && isSomeChar!From){ 2919 return codeLength!_Char(from); 2920 } 2921 2922 size_t encodedLength(_Char, From)(scope const(From)[] from)pure nothrow @nogc @safe 2923 if(isSomeChar!_Char && isSomeChar!From){ 2924 static if(_Char.sizeof == From.sizeof){ 2925 return from.length; 2926 } 2927 else static if(_Char.sizeof < From.sizeof){ 2928 size_t result = 0; 2929 2930 foreach(const From c; from[]){ 2931 result += c.encodedLength!_Char; 2932 } 2933 2934 return result; 2935 } 2936 else{ 2937 static assert(_Char.sizeof > From.sizeof); 2938 2939 size_t result = 0; 2940 2941 while(from.length){ 2942 result += decode(from).encodedLength!_Char; 2943 } 2944 2945 return result; 2946 } 2947 2948 } 2949 2950 size_t encodedLength(_Char, From)(const From from)pure nothrow @nogc @safe 2951 if(isSomeChar!_Char && isIntegral!From){ 2952 import std.math : log10, abs; 2953 2954 if(from == 0) 2955 return 1; 2956 2957 return cast(size_t)(cast(int)(log10(abs(from))+1) + (from < 0 ? 1 : 0)); 2958 2959 } 2960 2961 2962 2963 size_t encodeTo(_Char, From)(scope const(From)[] from, scope _Char[] to, const size_t count = 1)pure nothrow @nogc 2964 if(isSomeChar!_Char && isSomeChar!From){ 2965 2966 if(count == 0) 2967 return 0; 2968 2969 assert(from.encodedLength!_Char * count <= to.length); 2970 2971 debug const predictedEncodedLength = encodedLength!_Char(from); 2972 2973 static if(_Char.sizeof == From.sizeof){ 2974 2975 const size_t len = from.length; 2976 ()@trusted{ 2977 assert(to.length >= len); 2978 assert(from.length <= len); 2979 memCopy(to.ptr, from.ptr, len); //to[0 .. len] = from[]; 2980 }(); 2981 2982 } 2983 else{ 2984 2985 size_t len = 0; 2986 while(from.length){ 2987 len += decode(from).encode(to[len .. $]); 2988 } 2989 } 2990 2991 debug assert(predictedEncodedLength == len); 2992 2993 for(size_t i = 1; i < count; ++i){ 2994 //to[len .. len * 2] = to[0 .. len]; 2995 ()@trusted{ 2996 assert(to.length >= (len * 2)); 2997 memCopy(to.ptr + len, to.ptr, len); //to[0 .. len] = from[]; 2998 }(); 2999 to = to[len .. $]; 3000 } 3001 3002 return (len * count); 3003 } 3004 3005 size_t encodeTo(_Char, From)(const From from, scope _Char[] to, const size_t count = 1)pure nothrow @nogc 3006 if(isSomeChar!_Char && isSomeChar!From){ 3007 3008 if(count == 0) 3009 return 0; 3010 3011 assert(from.encodedLength!_Char * count <= to.length); 3012 3013 debug const predictedEncodedLength = encodedLength!_Char(from); 3014 3015 static if(_Char.sizeof == From.sizeof){ 3016 enum size_t len = 1; 3017 3018 assert(count <= to.length); 3019 for(size_t i = 0; i < count; ++i){ 3020 to[i] = from; 3021 } 3022 } 3023 else{ 3024 const size_t len = dchar(from).encode(to[]); 3025 3026 for(size_t i = 1; i < count; ++i){ 3027 //to[len .. len * 2] = to[0 .. len]; 3028 ()@trusted{ 3029 assert(to.length >= (len * 2)); 3030 memCopy(to.ptr + len, to.ptr, len); //to[0 .. len] = from[]; 3031 }(); 3032 to = to[len .. $]; 3033 } 3034 3035 assert(encodedLength!_Char(from) == len); 3036 } 3037 3038 debug assert(predictedEncodedLength == len); 3039 3040 return (len * count); 3041 } 3042 3043 size_t encodeTo(_Char, From)(const From from, scope _Char[] to, const size_t count = 1)pure nothrow @nogc @safe 3044 if(isSomeChar!_Char && isIntegral!From){ 3045 import std.conv : toChars; 3046 3047 if(count == 0) 3048 return 0; 3049 3050 auto ichars = toChars(from + 0); 3051 3052 assert(encodedLength!_Char(from) == ichars.length); 3053 3054 for(size_t c = 0; c < count; ++c){ 3055 for(size_t i = 0; i < ichars.length; ++i) 3056 to[c+i] = ichars[i]; 3057 } 3058 3059 return (ichars.length * count); 3060 3061 3062 } 3063 } 3064 3065 3066 //mem[move|copy] 3067 private{ 3068 void memMove(T)(scope T* target, scope const(T)* source, size_t length)@trusted{ 3069 import core.stdc.string : memmove; 3070 3071 memmove(target, source, length * T.sizeof); 3072 /+version(D_BetterC){ 3073 import core.stdc.string : memmove; 3074 3075 memmove(target, source, length * T.sizeof); 3076 } 3077 else{ 3078 target[0 .. length] = source[0 .. length]; 3079 }+/ 3080 } 3081 3082 void memCopy(T)(scope T* target, scope const(T)* source, size_t length)@trusted{ 3083 import core.stdc.string : memcpy; 3084 3085 memcpy(target, source, length * T.sizeof); 3086 /+ 3087 version(D_BetterC){ 3088 import core.stdc.string : memcpy; 3089 3090 memcpy(target, source, length * T.sizeof); 3091 } 3092 else{ 3093 target[0 .. length] = source[0 .. length]; 3094 }+/ 3095 } 3096 } 3097 3098 //range frontCodeUnit & popFrontCodeUnit: 3099 private{ 3100 auto frontCodeUnit(Range)(auto ref Range r){ 3101 import std.traits : isAutodecodableString; 3102 3103 static if(isAutodecodableString!Range){ 3104 assert(r.length > 0); 3105 return r[0]; 3106 } 3107 else{ 3108 import std.range.primitives : front; 3109 return r.front; 3110 } 3111 } 3112 3113 void popFrontCodeUnit(Range)(ref Range r){ 3114 import std.traits : isAutodecodableString; 3115 3116 static if(isAutodecodableString!Range){ 3117 assert(r.length > 0); 3118 r = r[1 .. $]; 3119 } 3120 else{ 3121 import std.range.primitives : popFront; 3122 return r.popFront; 3123 } 3124 } 3125 } 3126 3127 3128 //doc: 3129 version(unittest){ 3130 //doc.minimalCapacity: 3131 pure nothrow @safe @nogc unittest{ 3132 BasicString!char str; 3133 assert(str.empty); 3134 assert(str.capacity == BasicString!char.minimalCapacity); 3135 assert(str.capacity > 0); 3136 } 3137 3138 //doc.empty: 3139 pure nothrow @safe @nogc unittest{ 3140 BasicString!char str; 3141 assert(str.empty); 3142 3143 str = "123"; 3144 assert(!str.empty); 3145 } 3146 3147 //doc.full: 3148 pure nothrow @safe @nogc unittest{ 3149 BasicString!(char, 1, void) str; //FixedString 3150 assert(str.empty); 3151 assert(str.minimalCapacity > 0); 3152 assert(!str.full); 3153 3154 str.resize(str.maximalCapacity); 3155 assert(str.full); 3156 } 3157 3158 //doc.length: 3159 pure nothrow @safe @nogc unittest{ 3160 BasicString!char str = "123"; 3161 assert(str.length == 3); 3162 3163 BasicString!wchar wstr = "123"; 3164 assert(wstr.length == 3); 3165 3166 BasicString!dchar dstr = "123"; 3167 assert(dstr.length == 3); 3168 } 3169 3170 //doc.capacity: 3171 pure nothrow @safe @nogc unittest{ 3172 BasicString!char str; 3173 assert(str.capacity == BasicString!char.minimalCapacity); 3174 3175 str.reserve(str.capacity + 1); 3176 assert(str.capacity > BasicString!char.minimalCapacity); 3177 } 3178 3179 //doc.ptr: 3180 pure nothrow @system @nogc unittest{ 3181 BasicString!char str = "123"; 3182 char* ptr = str.ptr; 3183 assert(ptr[0 .. 3] == "123"); 3184 } 3185 3186 //doc.frontCodePoint: 3187 pure nothrow @safe @nogc unittest{ 3188 BasicString!char str = "á123"; 3189 3190 assert(str.frontCodePoint == 'á'); 3191 } 3192 3193 //doc.frontCodeUnit: 3194 pure nothrow @safe @nogc unittest{ 3195 { 3196 BasicString!char str = "123"; 3197 3198 assert(str.frontCodeUnit == '1'); 3199 } 3200 3201 { 3202 BasicString!char str = "á23"; 3203 3204 immutable(char)[2] a = "á"; 3205 assert(str.frontCodeUnit == a[0]); 3206 } 3207 3208 { 3209 BasicString!char str = "123"; 3210 3211 str.frontCodeUnit = 'x'; 3212 3213 assert(str == "x23"); 3214 } 3215 } 3216 3217 //doc.backCodePoint: 3218 pure nothrow @safe @nogc unittest{ 3219 { 3220 BasicString!char str = "123á"; 3221 3222 assert(str.backCodePoint == 'á'); 3223 } 3224 3225 { 3226 BasicString!char str = "123á"; 3227 str.backCodePoint = '4'; 3228 assert(str == "1234"); 3229 } 3230 } 3231 3232 //doc.backCodeUnit: 3233 pure nothrow @safe @nogc unittest{ 3234 { 3235 BasicString!char str = "123"; 3236 3237 assert(str.backCodeUnit == '3'); 3238 } 3239 3240 { 3241 BasicString!char str = "12á"; 3242 3243 immutable(char)[2] a = "á"; 3244 assert(str.backCodeUnit == a[1]); 3245 } 3246 3247 { 3248 BasicString!char str = "123"; 3249 3250 str.backCodeUnit = 'x'; 3251 assert(str == "12x"); 3252 } 3253 } 3254 3255 //doc.popBackCodePoint: 3256 pure nothrow @safe @nogc unittest{ 3257 { 3258 BasicString!char str = "á1"; //'á' is encoded as 2 chars 3259 3260 assert(str.popBackCodePoint == 1); 3261 assert(str == "á"); 3262 3263 assert(str.popBackCodePoint == 2); 3264 assert(str.empty); 3265 3266 assert(str.popBackCodePoint == 0); 3267 assert(str.empty); 3268 } 3269 3270 { 3271 BasicString!char str = "1á"; //'á' is encoded as 2 chars 3272 assert(str.length == 3); 3273 3274 str.erase(str.length - 1); 3275 assert(str.length == 2); 3276 3277 assert(str.popBackCodePoint == 0); //popBackCodePoint cannot remove invalid code points 3278 assert(str.length == 2); 3279 } 3280 } 3281 3282 //doc.popBackCodeUnit: 3283 pure nothrow @safe @nogc unittest{ 3284 BasicString!char str = "á1"; //'á' is encoded as 2 chars 3285 assert(str.length == 3); 3286 3287 assert(str.popBackCodeUnit); 3288 assert(str.length == 2); 3289 3290 assert(str.popBackCodeUnit); 3291 assert(str.length == 1); 3292 3293 assert(str.popBackCodeUnit); 3294 assert(str.empty); 3295 3296 assert(!str.popBackCodeUnit); 3297 assert(str.empty); 3298 3299 } 3300 3301 //doc.clear: 3302 pure nothrow @safe @nogc unittest{ 3303 BasicString!char str = "123"; 3304 3305 str.reserve(str.capacity * 2); 3306 assert(str.length == 3); 3307 3308 const size_t cap = str.capacity; 3309 str.clear(); 3310 assert(str.capacity == cap); 3311 3312 } 3313 3314 //doc.release: 3315 pure nothrow @safe @nogc unittest{ 3316 BasicString!char str = "123"; 3317 3318 str.reserve(str.capacity * 2); 3319 assert(str.length == 3); 3320 3321 const size_t cap = str.capacity; 3322 str.clear(); 3323 assert(str.capacity == cap); 3324 3325 str.release(); 3326 assert(str.capacity < cap); 3327 assert(str.capacity == BasicString!char.minimalCapacity); 3328 3329 } 3330 3331 //doc.reserve: 3332 pure nothrow @safe @nogc unittest{ 3333 BasicString!char str = "123"; 3334 assert(str.capacity == BasicString!char.minimalCapacity); 3335 3336 const size_t cap = (str.capacity * 2); 3337 str.reserve(cap); 3338 assert(str.capacity > BasicString!char.minimalCapacity); 3339 assert(str.capacity >= cap); 3340 3341 } 3342 3343 //doc.reserve: 3344 pure nothrow @safe @nogc unittest{ 3345 BasicString!char str = "123"; 3346 assert(str.capacity == BasicString!char.minimalCapacity); 3347 3348 const size_t cap = (str.capacity * 2); 3349 str.reserve(cap); 3350 assert(str.capacity > BasicString!char.minimalCapacity); 3351 assert(str.capacity >= cap); 3352 } 3353 3354 //doc.resize: 3355 pure nothrow @safe @nogc unittest{ 3356 BasicString!char str = "123"; 3357 3358 str.resize(5, 'x'); 3359 assert(str == "123xx"); 3360 3361 str.resize(2); 3362 assert(str == "12"); 3363 } 3364 3365 //doc.downsize: 3366 pure nothrow @safe @nogc unittest{ 3367 BasicString!char str = "123"; 3368 3369 str.downsize(5); 3370 assert(str == "123"); 3371 3372 str.downsize(2); 3373 assert(str == "12"); 3374 } 3375 3376 //doc.upsize: 3377 pure nothrow @safe @nogc unittest{ 3378 BasicString!char str = "123"; 3379 3380 str.upsize(5, 'x'); 3381 assert(str == "123xx"); 3382 3383 str.upsize(2); 3384 assert(str == "123xx"); 3385 } 3386 3387 //doc.shrinkToFit: 3388 pure nothrow @safe @nogc unittest{ 3389 BasicString!char str = "123"; 3390 assert(str.capacity == BasicString!char.minimalCapacity); 3391 3392 str.reserve(str.capacity * 2); 3393 assert(str.capacity > BasicString!char.minimalCapacity); 3394 3395 str.shrinkToFit(); 3396 assert(str.capacity == BasicString!char.minimalCapacity); 3397 } 3398 3399 //doc.ctor(null): 3400 pure nothrow @safe @nogc unittest{ 3401 { 3402 BasicString!char str = null; 3403 assert(str.empty); 3404 } 3405 } 3406 3407 //doc.ctor(allocator): 3408 pure nothrow @safe @nogc unittest{ 3409 { 3410 BasicString!(char, DefaultAllocator) str = DefaultAllocator.init; 3411 assert(str.empty); 3412 } 3413 } 3414 3415 //doc.ctor(character): 3416 pure nothrow @safe @nogc unittest{ 3417 { 3418 BasicString!char str = 'x'; 3419 assert(str == "x"); 3420 } 3421 3422 { 3423 BasicString!char str = '読'; 3424 assert(str == "読"); 3425 } 3426 } 3427 3428 //doc.ctor(character, allocator): 3429 pure nothrow @safe @nogc unittest{ 3430 { 3431 auto str = BasicString!(char, DefaultAllocator)('読', DefaultAllocator.init); 3432 assert(str == "読"); 3433 } 3434 } 3435 3436 //doc.ctor(slice): 3437 pure nothrow @safe @nogc unittest{ 3438 { 3439 BasicString!char str = "test"; 3440 assert(str == "test"); 3441 } 3442 3443 { 3444 BasicString!char str = "test 読"d; 3445 assert(str == "test 読"); 3446 } 3447 3448 { 3449 wchar[3] data = [cast(wchar)'1', '2', '3']; 3450 BasicString!char str = data[]; 3451 assert(str == "123"); 3452 } 3453 } 3454 3455 //doc.ctor(slice, allocator): 3456 pure nothrow @safe @nogc unittest{ 3457 { 3458 auto str = BasicString!(char, DefaultAllocator)("test", DefaultAllocator.init); 3459 assert(str == "test"); 3460 } 3461 3462 { 3463 auto str = BasicString!(char, DefaultAllocator)("test 読"d, DefaultAllocator.init); 3464 assert(str == "test 読"); 3465 } 3466 3467 { 3468 wchar[3] data = [cast(wchar)'1', '2', '3']; 3469 auto str = BasicString!(char, DefaultAllocator)(data[], DefaultAllocator.init); 3470 assert(str == "123"); 3471 } 3472 } 3473 3474 //doc.ctor(integer): 3475 pure nothrow @safe @nogc unittest{ 3476 { 3477 BasicString!char str = 123uL; 3478 assert(str == "123"); 3479 } 3480 3481 { 3482 BasicString!dchar str = -123; 3483 assert(str == "-123"); 3484 } 3485 } 3486 3487 //doc.ctor(integer, allocator): 3488 pure nothrow @safe @nogc unittest{ 3489 { 3490 auto str = BasicString!(char, DefaultAllocator)(123uL, DefaultAllocator.init); 3491 assert(str == "123"); 3492 } 3493 3494 { 3495 auto str = BasicString!(dchar, DefaultAllocator)(-123, DefaultAllocator.init); 3496 assert(str == "-123"); 3497 } 3498 } 3499 3500 //doc.ctor(rhs): 3501 //doc.ctor(rhs, allcoator): 3502 pure nothrow @safe @nogc unittest{ 3503 { 3504 BasicString!char a = "123"; 3505 BasicString!char b = a; 3506 assert(b == "123"); 3507 } 3508 3509 { 3510 BasicString!dchar a = "123"; 3511 BasicString!char b = a; 3512 assert(b == "123"); 3513 } 3514 3515 { 3516 BasicString!dchar a = "123"; 3517 auto b = BasicString!char(a, DefaultAllocator.init); 3518 assert(b == "123"); 3519 } 3520 3521 import core.lifetime : move; 3522 { 3523 BasicString!char a = "123"; 3524 BasicString!char b = move(a); 3525 assert(b == "123"); 3526 } 3527 3528 } 3529 3530 //doc.opAssign(null): 3531 //doc.opAssign(slice): 3532 //doc.opAssign(character): 3533 //doc.opAssign(integer): 3534 //doc.opAssign(rhs): 3535 pure nothrow @safe @nogc unittest{ 3536 BasicString!char str = "123"; 3537 assert(!str.empty); 3538 3539 str = null; 3540 assert(str.empty); 3541 3542 str = 'X'; 3543 assert(str == "X"); 3544 3545 str = "abc"w; 3546 assert(str == "abc"); 3547 3548 str = -123; 3549 assert(str == "-123"); 3550 3551 str = BasicString!char("42"); 3552 assert(str == "42"); 3553 3554 str = BasicString!wchar("abc"); 3555 assert(str == "abc"); 3556 3557 } 3558 3559 //doc.opBinary(rhs): 3560 pure nothrow @safe @nogc unittest{ 3561 BasicString!char str = null; 3562 assert(str.empty); 3563 3564 str = str + '1'; 3565 assert(str == "1"); 3566 3567 str = str + "23"d; 3568 assert(str == "123"); 3569 3570 str = str + BasicString!dchar("456"); 3571 assert(str == "123456"); 3572 3573 } 3574 3575 //doc.opBinaryRight(rhs): 3576 pure nothrow @safe @nogc unittest{ 3577 BasicString!char str = null; 3578 assert(str.empty); 3579 3580 str = '1' + str; 3581 assert(str == "1"); 3582 3583 str = "32"d + str; 3584 assert(str == "321"); 3585 3586 str = BasicString!dchar("654") + str; 3587 assert(str == "654321"); 3588 } 3589 3590 //doc.opBinaryRight!"in": 3591 pure nothrow @safe @nogc unittest{ 3592 BasicString!char str = "a123読"; 3593 3594 assert('a' in str); 3595 assert('b' !in str); 3596 assert('3' in str); 3597 assert('読' in str); 3598 } 3599 3600 //doc.opEquals(rhs): 3601 pure nothrow @safe @nogc unittest{ 3602 BasicString!char str = "123"; 3603 3604 assert(str == "123"); 3605 assert("123" == str); 3606 3607 assert(str == "123"w); 3608 assert("123"w == str); 3609 3610 assert(str == "123"d); 3611 assert("123"d == str); 3612 3613 assert(str == BasicString!wchar("123")); 3614 assert(BasicString!wchar("123") == str); 3615 3616 assert(str == 123); 3617 assert(123 == str); 3618 3619 import std.range : only; 3620 assert(str == only('1', '2', '3')); 3621 assert(only('1', '2', '3') == str); 3622 } 3623 3624 //doc.opCmp(rhs): 3625 pure nothrow @safe @nogc unittest{ 3626 3627 } 3628 3629 //doc.opIndex(): 3630 pure nothrow @system @nogc unittest{ 3631 ()@system{ 3632 BasicString!char str = "123"; 3633 3634 char[] slice = str[]; 3635 assert(slice.length == str.length); 3636 assert(slice.ptr is str.ptr); 3637 3638 str.reserve(str.capacity * 2); 3639 assert(slice.length == str.length); 3640 assert(slice.ptr !is str.ptr); // slice contains dangling pointer. 3641 }(); 3642 ()@safe{ 3643 FixedString!(char, 20) str = "123"; 3644 3645 char[] slice = str[]; 3646 assert(slice.length == str.length); 3647 assert(slice.ptr is str.ptr); 3648 3649 assert(slice == "123"); 3650 }(); 3651 } 3652 3653 //doc.opIndex(pos): 3654 pure nothrow @system @nogc unittest{ 3655 BasicString!char str = "abcd"; 3656 3657 assert(str[1] == 'b'); 3658 } 3659 3660 //doc.opSlice(begin, end): 3661 pure nothrow @system @nogc unittest{ 3662 ()@system{ 3663 BasicString!char str = "123456"; 3664 assert(str[1 .. $-1] == "2345"); 3665 }(); 3666 ()@safe{ 3667 FixedString!(char, 20) str = "123456"; 3668 assert(str[1 .. $-1] == "2345"); 3669 }(); 3670 } 3671 3672 //doc.opIndexAssign(begin, end): 3673 pure nothrow @safe @nogc unittest{ 3674 BasicString!char str = "abcd"; 3675 3676 str[1] = 'x'; 3677 3678 assert(str == "axcd"); 3679 } 3680 3681 //doc.proxySwap(rhs): 3682 pure nothrow @safe @nogc unittest{ 3683 BasicString!char a = "1"; 3684 BasicString!char b = "2"; 3685 3686 a.proxySwap(b); 3687 assert(a == "2"); 3688 assert(b == "1"); 3689 3690 import std.algorithm.mutation : swap; 3691 3692 swap(a, b); 3693 assert(a == "1"); 3694 assert(b == "2"); 3695 } 3696 3697 //doc.append(val, count): 3698 pure nothrow @safe @nogc unittest{ 3699 { 3700 BasicString!char str = "123456"; 3701 3702 str.append('x', 2); 3703 assert(str == "123456xx"); 3704 } 3705 3706 { 3707 BasicString!char str = "123456"; 3708 3709 str.append("abc"); 3710 assert(str == "123456abc"); 3711 } 3712 3713 { 3714 BasicString!char str = "123456"; 3715 BasicString!char str2 = "xyz"; 3716 3717 str.append(str2); 3718 assert(str == "123456xyz"); 3719 } 3720 3721 { 3722 BasicString!char str = "12"; 3723 3724 str.append(+34); 3725 assert(str == "1234"); 3726 } 3727 } 3728 3729 //doc.insert(pos, val, count): 3730 //doc.insert(ptr, val, count): 3731 pure nothrow @system @nogc unittest{ 3732 { 3733 BasicString!char str = "123456"; 3734 3735 str.insert(2, 'x', 2); 3736 assert(str == "12xx3456"); 3737 } 3738 3739 { 3740 BasicString!char str = "123456"; 3741 3742 str.insert(2, "abc"); 3743 assert(str == "12abc3456"); 3744 } 3745 3746 { 3747 BasicString!char str = "123456"; 3748 BasicString!char str2 = "abc"; 3749 3750 str.insert(2, str2); 3751 assert(str == "12abc3456"); 3752 } 3753 3754 { 3755 BasicString!char str = "123456"; 3756 3757 str.insert(str.ptr + 2, 'x', 2); 3758 assert(str == "12xx3456"); 3759 } 3760 3761 { 3762 BasicString!char str = "123456"; 3763 3764 str.insert(str.ptr + 2, "abc"); 3765 assert(str == "12abc3456"); 3766 } 3767 3768 { 3769 BasicString!char str = "123456"; 3770 BasicString!char str2 = "abc"; 3771 3772 str.insert(str.ptr + 2, str2); 3773 assert(str == "12abc3456"); 3774 } 3775 } 3776 3777 3778 //doc.erase(pos): 3779 //doc.erase(pos, n): 3780 //doc.erase(ptr): 3781 //doc.erase(slice): 3782 pure nothrow @system @nogc unittest{ 3783 { 3784 BasicString!char str = "123456"; 3785 3786 str.erase(2); 3787 assert(str == "12"); 3788 } 3789 3790 { 3791 BasicString!char str = "123456"; 3792 3793 str.erase(1, 2); 3794 assert(str == "1456"); 3795 } 3796 3797 { 3798 BasicString!char str = "123456"; 3799 3800 str.erase(str.ptr + 2); 3801 assert(str == "12"); 3802 } 3803 3804 { 3805 BasicString!char str = "123456"; 3806 3807 str.erase(str[1 .. $-1]); 3808 assert(str == "16"); 3809 } 3810 } 3811 3812 3813 //doc.replace(pos, len val, count): 3814 //doc.replace(slice, val, count): 3815 pure nothrow @system @nogc unittest{ 3816 { 3817 BasicString!char str = "123456"; 3818 3819 str.replace(2, 2, 'x', 5); 3820 assert(str == "12xxxxx56"); 3821 } 3822 3823 { 3824 BasicString!char str = "123456"; 3825 3826 str.replace(2, 2, "abcdef"); 3827 assert(str == "12abcdef56"); 3828 } 3829 3830 { 3831 BasicString!char str = "123456"; 3832 BasicString!char str2 = "xy"; 3833 3834 str.replace(2, 3, str2); 3835 assert(str == "12xy56"); 3836 } 3837 3838 { 3839 BasicString!char str = "123456"; 3840 3841 str.replace(str[2 .. 4], 'x', 5); 3842 assert(str == "12xxxxx56"); 3843 } 3844 3845 { 3846 BasicString!char str = "123456"; 3847 3848 str.replace(str[2 .. 4], "abcdef"); 3849 assert(str == "12abcdef56"); 3850 } 3851 3852 { 3853 BasicString!char str = "123456"; 3854 BasicString!char str2 = "xy"; 3855 3856 str.replace(str[2 .. $], str2); 3857 assert(str == "12xy56"); 3858 } 3859 } 3860 3861 3862 //doc.build(...): 3863 pure nothrow @system @nogc unittest{ 3864 BasicString!char str = BasicString!char.build('1', cast(dchar)'2', "345"d, BasicString!wchar("678")); 3865 3866 assert(str == "12345678"); 3867 3868 } 3869 3870 3871 } 3872 3873 3874 3875 //normal ut 3876 version(unittest){ 3877 version(BTL_BASIC_STRING_TESTS) 3878 struct TestStatelessAllocator(bool Realloc){ 3879 3880 private struct Allocation{ 3881 void[] alloc; 3882 long count; 3883 3884 3885 this(void[] alloc, long c)pure nothrow @safe @nogc{ 3886 this.alloc = alloc; 3887 this.count = c; 3888 } 3889 } 3890 3891 private static Allocation[] allocations; 3892 private static void[][] bad_dealocations; 3893 3894 3895 private void add(void[] b)scope nothrow @trusted{ 3896 if(b.length == 0) 3897 return; 3898 3899 foreach(ref a; allocations){ 3900 if(a.alloc.ptr is b.ptr && a.alloc.length == b.length){ 3901 a.count += 1; 3902 return; 3903 } 3904 } 3905 3906 allocations ~= Allocation(b, 1); 3907 } 3908 3909 private void del(void[] b)scope nothrow @trusted{ 3910 foreach(ref a; allocations){ 3911 if(a.alloc.ptr is b.ptr && a.alloc.length == b.length){ 3912 a.count -= 1; 3913 return; 3914 } 3915 } 3916 3917 bad_dealocations ~= b; 3918 } 3919 3920 3921 import std.experimental.allocator.common : platformAlignment; 3922 3923 public enum uint alignment = platformAlignment; 3924 3925 public void[] allocate(size_t bytes)scope @trusted nothrow{ 3926 auto data = Mallocator.instance.allocate(bytes); 3927 if(data.length == 0) 3928 return null; 3929 3930 this.add(data); 3931 3932 return data; 3933 } 3934 3935 public bool deallocate(void[] b)scope @system nothrow{ 3936 const result = Mallocator.instance.deallocate(b); 3937 assert(result); 3938 3939 this.del(b); 3940 3941 return result; 3942 3943 } 3944 3945 public bool reallocate(ref void[] b, size_t s)scope @system nothrow{ 3946 static if(Realloc){ 3947 void[] old = b; 3948 3949 const result = Mallocator.instance.reallocate(b, s); 3950 3951 this.del(old); 3952 this.add(b); 3953 3954 return result; 3955 3956 } 3957 else return false; 3958 } 3959 3960 3961 public bool empty()scope const nothrow @safe @nogc{ 3962 import std.algorithm : all; 3963 3964 return true 3965 && bad_dealocations.length == 0 3966 && allocations.all!((a) => a.count == 0); 3967 3968 } 3969 3970 static typeof(this) instance; 3971 } 3972 3973 version(BTL_BASIC_STRING_TESTS) 3974 class TestStateAllocator(bool Realloc){ 3975 3976 private struct Allocation{ 3977 void[] alloc; 3978 long count; 3979 3980 3981 this(void[] alloc, long c)pure nothrow @safe @nogc{ 3982 this.alloc = alloc; 3983 this.count = c; 3984 } 3985 } 3986 3987 private Allocation[] allocations; 3988 private void[][] bad_dealocations; 3989 3990 3991 private void add(void[] b)scope nothrow @trusted{ 3992 if(b.length == 0) 3993 return; 3994 3995 foreach(ref a; allocations){ 3996 if(a.alloc.ptr is b.ptr && a.alloc.length == b.length){ 3997 a.count += 1; 3998 return; 3999 } 4000 } 4001 4002 allocations ~= Allocation(b, 1); 4003 } 4004 4005 private void del(void[] b)scope nothrow @trusted{ 4006 foreach(ref a; allocations){ 4007 if(a.alloc.ptr is b.ptr && a.alloc.length == b.length){ 4008 a.count -= 1; 4009 return; 4010 } 4011 } 4012 4013 bad_dealocations ~= b; 4014 } 4015 4016 4017 import std.experimental.allocator.common : platformAlignment; 4018 4019 public enum uint alignment = platformAlignment; 4020 4021 public void[] allocate(size_t bytes)scope @trusted nothrow{ 4022 auto data = Mallocator.instance.allocate(bytes); 4023 if(data.length == 0) 4024 return null; 4025 4026 this.add(data); 4027 4028 return data; 4029 } 4030 4031 public bool deallocate(void[] b)scope @system nothrow{ 4032 const result = Mallocator.instance.deallocate(b); 4033 assert(result); 4034 4035 this.del(b); 4036 4037 return result; 4038 4039 } 4040 4041 public bool reallocate(ref void[] b, size_t s)scope @system nothrow{ 4042 static if(Realloc){ 4043 void[] old = b; 4044 4045 const result = Mallocator.instance.reallocate(b, s); 4046 4047 this.del(old); 4048 this.add(b); 4049 4050 return result; 4051 4052 } 4053 else return false; 4054 } 4055 4056 4057 public bool empty()scope const nothrow @safe @nogc{ 4058 import std.algorithm : all; 4059 4060 return true 4061 && bad_dealocations.length == 0 4062 && allocations.all!((a) => a.count == 0); 4063 4064 } 4065 } 4066 4067 4068 4069 4070 version(BTL_BASIC_STRING_TESTS){ 4071 private auto trustedSlice(S)(scope auto ref S str)@trusted{ 4072 return str[]; 4073 } 4074 private auto trustedSlice(S)(scope auto ref S str, size_t b, size_t e)@trusted{ 4075 return str[b .. e]; 4076 } 4077 private auto trustedSliceToEnd(S)(scope auto ref S str, size_t b)@trusted{ 4078 return str[b .. $]; 4079 } 4080 4081 void unittest_allocator_impl(Char, Allocator)(Allocator allocator){ 4082 alias Str = BasicString!(Char, Allocator); 4083 4084 static if(Str.hasStatelessAllocator) 4085 alias allocatorWithState = AliasSeq!(); 4086 else 4087 alias allocatorWithState = AliasSeq!(allocator); 4088 4089 Str str = Str("1", allocatorWithState); 4090 auto a = str.allocator; 4091 } 4092 4093 void unittest_reserve_impl(Char, Allocator)(Allocator allocator){ 4094 alias Str = BasicString!(Char, Allocator); 4095 4096 static if(Str.hasStatelessAllocator) 4097 alias allocatorWithState = AliasSeq!(); 4098 else 4099 alias allocatorWithState = AliasSeq!(allocator); 4100 4101 ///reserve: 4102 Str str = Str("1", allocatorWithState); 4103 assert(str.capacity == Str.minimalCapacity); 4104 //---------------------------- 4105 const size_t new_capacity = str.capacity * 2 + 1; 4106 str.reserve(new_capacity); 4107 assert(str.capacity >= new_capacity); 4108 //---------------------------- 4109 str.clear(); 4110 assert(str.empty); 4111 } 4112 4113 void unittest_resize_impl(Char, Allocator)(Allocator allocator){ 4114 alias Str = BasicString!(Char, Allocator); 4115 4116 static if(Str.hasStatelessAllocator) 4117 alias allocatorWithState = AliasSeq!(); 4118 else 4119 alias allocatorWithState = AliasSeq!(allocator); 4120 4121 ///resize: 4122 Str str = Str("1", allocatorWithState); 4123 assert(str.capacity == Str.minimalCapacity); 4124 //---------------------------- 4125 str.resize(Str.minimalCapacity); 4126 assert(str.capacity == Str.minimalCapacity); 4127 assert(str.length == Str.minimalCapacity); 4128 //---------------------------- 4129 str.resize(Str.minimalCapacity - 1, '_'); 4130 assert(str.capacity == Str.minimalCapacity); 4131 assert(str.length == Str.minimalCapacity - 1); 4132 //---------------------------- 4133 str.resize(Str.minimalCapacity + 3, '_'); 4134 assert(str.capacity > Str.minimalCapacity); 4135 assert(str.length == Str.minimalCapacity + 3); 4136 4137 } 4138 4139 4140 void unittest_ctor_string_impl(Char, Allocator)(Allocator allocator){ 4141 alias Str = BasicString!(Char, Allocator); 4142 4143 static if(Str.hasStatelessAllocator) 4144 alias allocatorWithState = AliasSeq!(); 4145 else 4146 alias allocatorWithState = AliasSeq!(allocator); 4147 4148 static foreach(enum val; AliasSeq!( 4149 "0123", 4150 "0123456789_0123456789_0123456789_0123456789", 4151 )) 4152 static foreach(enum I; AliasSeq!(1, 2, 3, 4)){{ 4153 static if(I == 1){ 4154 char[val.length] s = val; 4155 wchar[val.length] w = val; 4156 dchar[val.length] d = val; 4157 } 4158 else static if(I == 2){ 4159 immutable(char)[val.length] s = val; 4160 immutable(wchar)[val.length] w = val; 4161 immutable(dchar)[val.length] d = val; 4162 } 4163 else static if(I == 3){ 4164 enum string s = val; 4165 enum wstring w = val; 4166 enum dstring d = val; 4167 } 4168 else static if(I == 4){ 4169 auto s = BasicString!(char, Allocator)(val, allocatorWithState); 4170 auto w = BasicString!(wchar, Allocator)(val, allocatorWithState); 4171 auto d = BasicString!(dchar, Allocator)(val, allocatorWithState); 4172 } 4173 else static assert(0, "no impl"); 4174 4175 auto str1 = Str(s, allocatorWithState); 4176 str1 = s; 4177 auto str2 = Str(s.trustedSlice, allocatorWithState); 4178 str2 = s.trustedSlice; 4179 assert(str1 == str2); 4180 4181 auto wstr1 = Str(w, allocatorWithState); 4182 wstr1 = w; 4183 auto wstr2 = Str(w.trustedSlice, allocatorWithState); 4184 wstr2 = w.trustedSlice; 4185 assert(wstr1 == wstr2); 4186 4187 auto dstr1 = Str(d, allocatorWithState); 4188 dstr1 = d; 4189 auto dstr2 = Str(d.trustedSlice, allocatorWithState); 4190 dstr2 = d.trustedSlice; 4191 assert(dstr1 == dstr2); 4192 }} 4193 4194 4195 } 4196 4197 void unittest_ctor_char_impl(Char, Allocator)(Allocator allocator){ 4198 alias Str = BasicString!(Char, Allocator); 4199 4200 static if(Str.hasStatelessAllocator) 4201 alias allocatorWithState = AliasSeq!(); 4202 else 4203 alias allocatorWithState = AliasSeq!(allocator); 4204 4205 static foreach(enum val; AliasSeq!( 4206 cast(char)'1', 4207 cast(wchar)'2', 4208 cast(dchar)'3', 4209 )) 4210 static foreach(enum I; AliasSeq!(1, 2, 3)){{ 4211 static if(I == 1){ 4212 char c = val; 4213 wchar w = val; 4214 dchar d = val; 4215 } 4216 else static if(I == 2){ 4217 immutable(char) c = val; 4218 immutable(wchar) w = val; 4219 immutable(dchar) d = val; 4220 } 4221 else static if(I == 3){ 4222 enum char c = val; 4223 enum wchar w = val; 4224 enum dchar d = val; 4225 } 4226 else static assert(0, "no impl"); 4227 4228 auto str = Str(c, allocatorWithState); 4229 str = c; 4230 4231 auto wstr = Str(w, allocatorWithState); 4232 wstr = w; 4233 4234 auto dstr = Str(d, allocatorWithState); 4235 dstr = d; 4236 }} 4237 4238 } 4239 4240 4241 void unittest_shrink_to_fit_impl(Char, Allocator)(Allocator allocator){ 4242 alias Str = BasicString!(Char, Allocator); 4243 4244 static if(Str.hasStatelessAllocator) 4245 alias allocatorWithState = AliasSeq!(); 4246 else 4247 alias allocatorWithState = AliasSeq!(allocator); 4248 4249 Str str = Str("123", allocatorWithState); 4250 assert(str.capacity == Str.minimalCapacity); 4251 4252 //---------------------------- 4253 assert(str.small); 4254 str.resize(str.capacity * 2, 'x'); 4255 assert(!str.small); 4256 4257 4258 const size_t cap = str.capacity; 4259 const size_t len = str.length; 4260 str.shrinkToFit(); 4261 assert(str.length == len); 4262 assert(str.capacity == cap); 4263 4264 str = "123"; 4265 assert(str.length == 3); 4266 assert(str.capacity == cap); 4267 4268 str.shrinkToFit(); 4269 assert(str.length == 3); 4270 assert(str.capacity == Str.minimalCapacity); 4271 4272 //---------------------------- 4273 str.clear(); 4274 assert(str.empty); 4275 4276 } 4277 4278 4279 void unittest_operator_plus_string_impl(Char, Allocator)(Allocator allocator){ 4280 alias Str = BasicString!(Char, Allocator); 4281 4282 static if(Str.hasStatelessAllocator) 4283 alias allocatorWithState = AliasSeq!(); 4284 else 4285 alias allocatorWithState = AliasSeq!(allocator); 4286 4287 static foreach(enum I; AliasSeq!(1, 2, 3, 4)){{ 4288 static if(I == 1){ 4289 auto s = BasicString!(char, Allocator)("45", allocatorWithState); 4290 auto w = BasicString!(wchar, Allocator)("67", allocatorWithState); 4291 auto d = BasicString!(dchar, Allocator)("89", allocatorWithState); 4292 } 4293 else static if(I == 2){ 4294 immutable(char)[2] s = "45"; 4295 immutable(wchar)[2] w = "67"; 4296 immutable(dchar)[2] d = "89"; 4297 } 4298 else static if(I == 3){ 4299 enum string s = "45"; 4300 enum wstring w = "67"; 4301 enum dstring d = "89"; 4302 } 4303 else static if(I == 4){ 4304 char[2] sx = ['4', '5']; 4305 wchar[2] wx = ['6', '7']; 4306 dchar[2] dx = ['8', '9']; 4307 char[] s = sx[]; 4308 wchar[] w = wx[]; 4309 dchar[] d = dx[]; 4310 } 4311 else static assert(0, "no impl"); 4312 4313 4314 Str str = Str("123", allocatorWithState); 4315 assert(str.capacity == Str.minimalCapacity); 4316 4317 ////---------------------------- 4318 str = (str + s); 4319 str = (s + str); 4320 str += s; 4321 assert(str == "451234545"); 4322 4323 str = (str + w); 4324 str = (w + str); 4325 str += w; 4326 assert(str == "674512345456767"); 4327 4328 str = (str + d); 4329 str = (d + str); 4330 str += d; 4331 assert(str == "896745123454567678989"); 4332 4333 //---------------------------- 4334 str.clear(); 4335 assert(str.empty); 4336 }} 4337 } 4338 4339 void unittest_operator_plus_char_impl(Char, Allocator)(Allocator allocator){ 4340 alias Str = BasicString!(Char, Allocator); 4341 4342 static if(Str.hasStatelessAllocator) 4343 alias allocatorWithState = AliasSeq!(); 4344 else 4345 alias allocatorWithState = AliasSeq!(allocator); 4346 4347 static foreach(enum I; AliasSeq!(1, 2)){{ 4348 static if(I == 1){ 4349 char c = 'a'; 4350 wchar w = 'b'; 4351 dchar d = 'c'; 4352 } 4353 else static if(I == 2){ 4354 enum char c = 'a'; 4355 enum wchar w = 'b'; 4356 enum dchar d = 'c'; 4357 } 4358 else static assert(0, "no impl"); 4359 4360 4361 Str str = Str("123", allocatorWithState); 4362 assert(str.capacity == Str.minimalCapacity); 4363 4364 ////---------------------------- 4365 str = (str + c); 4366 str = (c + str); 4367 str += c; 4368 assert(str == "a123aa"); 4369 4370 str = (str + w); 4371 str = (w + str); 4372 str += w; 4373 assert(str == "ba123aabb"); 4374 4375 str = (str + d); 4376 str = (d + str); 4377 str += d; 4378 assert(str == "cba123aabbcc"); 4379 4380 //---------------------------- 4381 str.clear(); 4382 assert(str.empty); 4383 }} 4384 } 4385 4386 4387 void unittest_append_impl(Char, Allocator)(Allocator allocator){ 4388 alias Str = BasicString!(Char, Allocator); 4389 4390 static if(Str.hasStatelessAllocator) 4391 alias allocatorWithState = AliasSeq!(); 4392 else 4393 alias allocatorWithState = AliasSeq!(allocator); 4394 4395 static foreach(enum val; AliasSeq!( 4396 "0123", 4397 "0123456789_0123456789_0123456789_0123456789", 4398 )) 4399 static foreach(enum rep; AliasSeq!( 4400 'x', 4401 "", 4402 "a", 4403 "ab", 4404 "abcdefgh_abcdefgh_abcdefgh_abcdefgh", 4405 )) 4406 static foreach(enum size_t count; AliasSeq!(0, 1, 2, 3)) 4407 static foreach(alias T; AliasSeq!(char, wchar, dchar)){{ 4408 import std.traits : isArray; 4409 4410 static if(isArray!(typeof(rep))) 4411 alias Rep = immutable(T)[]; 4412 else 4413 alias Rep = T; 4414 4415 4416 Str str = Str(val, allocatorWithState); 4417 4418 str.append(cast(Rep)rep, count); 4419 4420 4421 Str rep_complet = Str(allocatorWithState); 4422 for(size_t i = 0; i < count; ++i) 4423 rep_complet += cast(Rep)rep; 4424 4425 import std.range; 4426 assert(str == Str.build(allocatorWithState, val, rep_complet.trustedSlice)); 4427 //assert(str == chain(val, rep_complet[])); 4428 4429 }} 4430 4431 } 4432 4433 void unittest_insert_impl(Char, Allocator)(Allocator allocator){ 4434 alias Str = BasicString!(Char, Allocator); 4435 4436 static if(Str.hasStatelessAllocator) 4437 alias allocatorWithState = AliasSeq!(); 4438 else 4439 alias allocatorWithState = AliasSeq!(allocator); 4440 4441 static foreach(enum val; AliasSeq!( 4442 "0123", 4443 "0123456789_0123456789_0123456789_0123456789", 4444 )) 4445 static foreach(enum rep_source; AliasSeq!( 4446 'x', 4447 "", 4448 "a", 4449 "ab", 4450 "abcdefgh_abcdefgh_abcdefgh_abcdefgh", 4451 )) 4452 static foreach(enum size_t count; AliasSeq!(0, 1, 2, 3)) 4453 static foreach(alias T; AliasSeq!(char, wchar, dchar)){{ 4454 import std.traits : isArray; 4455 4456 static if(isArray!(typeof(rep_source))) 4457 enum immutable(T)[] rep = rep_source; 4458 else 4459 enum T rep = rep_source; 4460 4461 4462 Str rep_complet = Str(allocatorWithState); 4463 for(size_t i = 0; i < count; ++i) 4464 rep_complet += rep; 4465 4466 { 4467 Str str = Str(val, allocatorWithState); 4468 4469 const x = str.insert(2, rep, count); 4470 assert(Str(rep, allocatorWithState).length * count == x); 4471 assert(str == Str.build(allocatorWithState, val.trustedSlice(0, 2), rep_complet, val.trustedSliceToEnd(2))); 4472 } 4473 { 4474 Str str = Str(val, allocatorWithState); 4475 4476 const x = str.insert(str.length, rep, count); 4477 assert(Str(rep, allocatorWithState).length * count == x); 4478 assert(str == Str.build(allocatorWithState, val, rep_complet)); 4479 } 4480 { 4481 Str str = Str(val, allocatorWithState); 4482 4483 const x = str.insert(str.length + 2000, rep, count); 4484 assert(Str(rep, allocatorWithState).length * count == x); 4485 assert(str == Str.build(allocatorWithState, val, rep_complet)); 4486 } 4487 { 4488 Str str = Str(val, allocatorWithState); 4489 4490 const x = str.insert(0, rep, count); 4491 assert(Str(rep, allocatorWithState).length * count == x); 4492 assert(str == Str.build(allocatorWithState, rep_complet, val)); 4493 } 4494 //------------------------------------------------ 4495 { 4496 Str str = Str(val, allocatorWithState); 4497 4498 const x = str.insert((()@trusted => str.ptr + 2)(), rep, count); 4499 assert(Str(rep, allocatorWithState).length * count == x); 4500 assert(str == Str.build(allocatorWithState, val.trustedSlice(0, 2), rep_complet, val.trustedSliceToEnd(2))); 4501 } 4502 { 4503 Str str = Str(val, allocatorWithState); 4504 4505 const x = str.insert((()@trusted => str.ptr + str.length)(), rep, count); 4506 assert(Str(rep, allocatorWithState).length * count == x); 4507 assert(str == Str.build(allocatorWithState, val, rep_complet)); 4508 } 4509 { 4510 Str str = Str(val, allocatorWithState); 4511 4512 const x = str.insert((()@trusted => str.ptr + str.length + 2000)(), rep, count); 4513 assert(Str(rep, allocatorWithState).length * count == x); 4514 assert(str == Str.build(allocatorWithState, val, rep_complet)); 4515 } 4516 { 4517 Str str = Str(val, allocatorWithState); 4518 4519 const x = str.insert((()@trusted => str.ptr)(), rep, count); 4520 assert(Str(rep, allocatorWithState).length * count == x); 4521 assert(str == Str.build(allocatorWithState, rep_complet, val)); 4522 } 4523 { 4524 Str str = Str(val, allocatorWithState); 4525 4526 const x = str.insert((()@trusted => str.ptr - 1000)(), rep, count); 4527 assert(Str(rep, allocatorWithState).length * count == x); 4528 assert(str == Str.build(allocatorWithState, rep_complet, val)); 4529 } 4530 4531 }} 4532 } 4533 4534 void unittest_erase_impl(Char, Allocator)(Allocator allocator){ 4535 alias Str = BasicString!(Char, Allocator); 4536 4537 static if(Str.hasStatelessAllocator) 4538 alias allocatorWithState = AliasSeq!(); 4539 else 4540 alias allocatorWithState = AliasSeq!(allocator); 4541 4542 static foreach(enum val; AliasSeq!( 4543 "0123", 4544 "0123456789_0123456789_0123456789_0123456789", 4545 )){ 4546 { 4547 Str str = Str(val, allocatorWithState); 4548 4549 str.erase(2); 4550 //assert(str.equals_test(Str(val[0 .. 2]))); 4551 assert(str == Str(val[0 .. 2], allocatorWithState)); 4552 } 4553 { 4554 Str str = Str(val, allocatorWithState); 4555 4556 str.erase(1, 2); 4557 assert(str == Str.build(allocatorWithState, val[0 .. 1], val[3 .. $])); 4558 } 4559 { 4560 Str str = Str(val, allocatorWithState); 4561 4562 str.erase(1, 1000); 4563 assert(str == Str.build(allocatorWithState, val[0 .. 1])); 4564 } 4565 { 4566 Str str = Str(val, allocatorWithState); 4567 4568 str.erase(str.trustedSliceToEnd(2)); 4569 assert(str == Str.build(allocatorWithState, val[0 .. 2])); 4570 } 4571 { 4572 Str str = Str(val, allocatorWithState); 4573 4574 str.erase(str.trustedSlice(1, 3)); 4575 assert(str == Str.build(allocatorWithState, val.trustedSlice(0, 1), val.trustedSliceToEnd(3))); 4576 } 4577 { 4578 Str str = Str(val, allocatorWithState); 4579 4580 str.erase((()@trusted => str.ptr + 2)()); 4581 assert(str == Str(val.trustedSlice(0, 2), allocatorWithState)); 4582 } 4583 } 4584 4585 ///downsize (erase): 4586 { 4587 Str str = Str("123", allocatorWithState); 4588 assert(str.length == 3); 4589 4590 //---------------------------- 4591 str.erase(3); 4592 assert(str.length == 3); 4593 4594 str.erase(1000); 4595 assert(str.length == 3); 4596 4597 str.erase(1); 4598 assert(str.length == 1); 4599 4600 //---------------------------- 4601 const size_t new_length = str.capacity * 2; 4602 str.resize(new_length); 4603 assert(str.capacity >= new_length); 4604 assert(str.length == new_length); 4605 4606 str.erase(3); 4607 assert(str.length == 3); 4608 4609 str.erase(1000); 4610 assert(str.length == 3); 4611 4612 str.erase(1); 4613 assert(str.length == 1); 4614 4615 //---------------------------- 4616 str.clear(); 4617 assert(str.empty); 4618 } 4619 } 4620 4621 void unittest_replace_impl(Char, Allocator)(Allocator allocator){ 4622 alias Str = BasicString!(Char, Allocator); 4623 4624 static if(Str.hasStatelessAllocator) 4625 alias allocatorWithState = AliasSeq!(); 4626 else 4627 alias allocatorWithState = AliasSeq!(allocator); 4628 4629 static foreach(enum val; AliasSeq!( 4630 "0123", 4631 "0123456789_0123456789_0123456789_0123456789", 4632 )) 4633 static foreach(enum rep_source; AliasSeq!( 4634 'x', 4635 "", 4636 "a", 4637 "ab", 4638 "abcdefgh_abcdefgh_abcdefgh_abcdefgh", 4639 )) 4640 static foreach(enum size_t count; AliasSeq!(0, 1, 2, 3)) 4641 static foreach(alias T; AliasSeq!(char, wchar, dchar)){{ 4642 import std.traits : isArray; 4643 4644 static if(isArray!(typeof(rep_source))) 4645 enum immutable(T)[] rep = rep_source; 4646 else 4647 enum T rep = rep_source; 4648 4649 4650 Str rep_complet = Str(allocatorWithState); 4651 for(size_t i = 0; i < count; ++i) 4652 rep_complet += rep; 4653 4654 { 4655 Str str = Str(val, allocatorWithState); 4656 4657 str.replace(1, 2, rep, count); 4658 //debug writeln(val, ": ", str[], " vs ", val[0 .. 1], " | ", rep_complet[], " | ", val[3 .. $]); 4659 //assert(str[] == Str.build(val[0 .. 1], rep_complet[], val[3 .. $])); 4660 } 4661 { 4662 Str str = Str(val, allocatorWithState); 4663 4664 str.replace(1, val.length - 1, rep, count); 4665 //assert(str[] == Str.build(val[0 .. 1], rep_complet[])); 4666 } 4667 { 4668 Str str = Str(val, allocatorWithState); 4669 4670 str.replace(1, val.length + 2000, rep, count); 4671 //assert(str[] == Str.build(val[0 .. 1], rep_complet[])); 4672 } 4673 //------------------------ 4674 { 4675 Str str = Str(val, allocatorWithState); 4676 4677 str.replace((()@trusted => str[1 .. $ - 1])(), rep, count); 4678 //assert(str[] == Str.build(val[0 .. 1], rep_complet[], val[$ - 1 .. $])); 4679 } 4680 { 4681 Str str = Str(val, allocatorWithState); 4682 4683 str.replace((()@trusted => str[1 .. $])(), rep, count); 4684 //assert(str[] == Str.build(val[0 .. 1], rep_complet[])); 4685 } 4686 { 4687 Str str = Str(val, allocatorWithState); 4688 4689 str.replace((()@trusted => str.ptr[1 .. str.length + 2000])(), rep, count); 4690 //assert(str[] == Str.build(val[0 .. 1], rep_complet[])); 4691 } 4692 4693 }} 4694 } 4695 4696 4697 void unittest_output_range_impl(Char, Allocator)(Allocator allocator){ 4698 alias Str = BasicString!(Char, Allocator); 4699 4700 static if(Str.hasStatelessAllocator) 4701 alias allocatorWithState = AliasSeq!(); 4702 else 4703 alias allocatorWithState = AliasSeq!(allocator); 4704 4705 static foreach(alias T; AliasSeq!(char, wchar, dchar)){{ 4706 import std.range : only; 4707 import std.algorithm.mutation : copy; 4708 4709 { 4710 Str str = only( 4711 cast(immutable(T)[])"a", 4712 cast(immutable(T)[])"bc", 4713 cast(immutable(T)[])"", 4714 cast(immutable(T)[])"d" 4715 ).copy(Str(allocatorWithState)); 4716 4717 } 4718 { 4719 Str str = only( 4720 cast(T)'a', 4721 cast(T)'b', 4722 cast(T)'c' 4723 ).copy(Str(allocatorWithState)); 4724 4725 assert(str == Str("abc", allocatorWithState)); 4726 4727 } 4728 }} 4729 } 4730 4731 } 4732 4733 4734 version(BTL_BASIC_STRING_TESTS) 4735 void unittest_impl(Char, Allocator)(Allocator allocator){ 4736 unittest_allocator_impl!Char(allocator); 4737 4738 unittest_reserve_impl!Char(allocator); 4739 unittest_resize_impl!Char(allocator); 4740 4741 unittest_ctor_string_impl!Char(allocator); 4742 unittest_ctor_char_impl!Char(allocator); 4743 4744 unittest_shrink_to_fit_impl!Char(allocator); 4745 4746 unittest_operator_plus_string_impl!Char(allocator); 4747 unittest_operator_plus_char_impl!Char(allocator); 4748 4749 unittest_append_impl!Char(allocator); 4750 unittest_insert_impl!Char(allocator); 4751 unittest_erase_impl!Char(allocator); 4752 unittest_replace_impl!Char(allocator); 4753 4754 unittest_output_range_impl!Char(allocator); 4755 4756 } 4757 4758 4759 4760 version(BTL_BASIC_STRING_TESTS) 4761 void unittest_impl(Allocator)(Allocator allocator = Allocator.init){ 4762 import std.stdio : writeln; 4763 import std.range : only; 4764 4765 static foreach(alias Char; AliasSeq!(char, wchar, dchar)) 4766 unittest_impl!Char(allocator); 4767 4768 } 4769 4770 version(BTL_BASIC_STRING_TESTS) 4771 @nogc @safe pure nothrow unittest{ 4772 unittest_impl!Mallocator(); 4773 } 4774 4775 version(BTL_BASIC_STRING_TESTS) 4776 nothrow unittest{ 4777 version(D_BetterC){} 4778 else{ 4779 static foreach(enum bool Realloc; [false, true]){ 4780 { 4781 alias AX = TestStatelessAllocator!Realloc; 4782 4783 assert(AX.instance.empty); 4784 4785 unittest_impl!AX(); 4786 4787 assert(AX.instance.empty); 4788 } 4789 4790 { 4791 alias AX = TestStateAllocator!Realloc; 4792 4793 auto allocator = new AX; 4794 4795 4796 assert(allocator.empty); 4797 4798 unittest_impl(allocator); 4799 4800 assert(allocator.empty); 4801 4802 4803 //unittest_impl(allocator); 4804 4805 } 4806 4807 { 4808 alias AX = TestStateAllocator!Realloc; 4809 auto allocator = new AX; 4810 4811 assert(allocator.empty); 4812 4813 { 4814 alias Str = BasicString!(char, AX); 4815 4816 //Str b; 4817 //auto a = Str(allocator, "0123456789_0123456789_0123456789_"); 4818 4819 } 4820 4821 assert(allocator.empty); 4822 4823 } 4824 } 4825 } 4826 } 4827 4828 } 4829 4830 4831 //move ctor 4832 pure nothrow unittest{ 4833 import std.range : iota, array; 4834 import std.algorithm : map; 4835 import core.lifetime : move; 4836 4837 alias Str1 = BasicString!(char, 1); 4838 alias Str2 = BasicString!(char, 2); 4839 alias Str128 = BasicString!(char, 128); 4840 4841 //heap -> heap 4842 { 4843 Str1 a = iota(0, Str1.minimalCapacity * 2) 4844 .map!(i => i%2 ? 'a' : 'b') 4845 .array; 4846 void* ptr = a.ptr; 4847 Str1 b = move(a); 4848 4849 assert(b.ptr is ptr); 4850 } 4851 4852 //heap -> heap 4853 { 4854 Str1 a = iota(0, Str1.minimalCapacity * 2) 4855 .map!(i => (i%2) ? 'a' : 'b') 4856 .array; 4857 void* ptr = a.ptr; 4858 Str2 b = move(a); 4859 4860 assert(b.ptr is ptr); 4861 } 4862 4863 //inline -> heap 4864 { 4865 Str128 a = iota(0, Str2.minimalCapacity * 2) 4866 .map!(i => (i%2) ? 'a' : 'b') 4867 .array; 4868 void* ptr = a.ptr; 4869 Str2 b = move(a); 4870 4871 assert(b.ptr !is ptr); 4872 } 4873 4874 //heap -> inline 4875 { 4876 Str2 a = iota(0, Str2.minimalCapacity * 2) 4877 .map!(i => (i%2) ? 'a' : 'b') 4878 .array; 4879 void* ptr = a.ptr; 4880 Str128 b = move(a); 4881 4882 assert(b.ptr !is ptr); 4883 } 4884 } 4885 4886 //move assign 4887 pure nothrow unittest{ 4888 import std.range : iota, array; 4889 import std.algorithm : map; 4890 import core.lifetime : move; 4891 4892 alias Str1 = BasicString!(char, 1); 4893 alias Str2 = BasicString!(char, 2); 4894 alias Str128 = BasicString!(char, 128); 4895 4896 //heap -> heap 4897 { 4898 Str1 a = iota(0, Str1.minimalCapacity * 2) 4899 .map!(i => i%2 ? 'a' : 'b') 4900 .array; 4901 void* ptr = a.ptr; 4902 Str1 b; 4903 b = move(a); 4904 4905 assert(b.ptr is ptr); 4906 } 4907 4908 //heap -> heap 4909 { 4910 Str1 a = iota(0, Str1.minimalCapacity * 2) 4911 .map!(i => (i%2) ? 'a' : 'b') 4912 .array; 4913 void* ptr = a.ptr; 4914 Str2 b; 4915 b = move(a); 4916 4917 assert(b.ptr is ptr); 4918 } 4919 4920 //inline -> heap 4921 { 4922 Str128 a = iota(0, Str2.minimalCapacity * 2) 4923 .map!(i => (i%2) ? 'a' : 'b') 4924 .array; 4925 void* ptr = a.ptr; 4926 Str2 b; 4927 b = move(a); 4928 4929 assert(b.ptr !is ptr); 4930 } 4931 4932 //heap -> inline 4933 { 4934 Str2 a = iota(0, Str2.minimalCapacity * 2) 4935 .map!(i => (i%2) ? 'a' : 'b') 4936 .array; 4937 void* ptr = a.ptr; 4938 Str128 b; 4939 b = move(a); 4940 4941 assert(b.ptr !is ptr); 4942 } 4943 } 4944 4945 //LargeString: 4946 pure nothrow @safe @nogc unittest{ 4947 LargeString!char str; 4948 4949 assert(str.small); 4950 assert(str.minimalCapacity == 0); 4951 4952 str = ""; 4953 assert(str.small); 4954 assert(str.capacity == 0); 4955 4956 4957 str = "1"; 4958 assert(!str.small); 4959 assert(str.capacity >= 1); 4960 4961 str = null; 4962 assert(!str.small); 4963 4964 str.release(); 4965 assert(str.small); 4966 } 4967 4968 //NullString: 4969 pure nothrow @safe @nogc unittest{ 4970 BasicString!(char, 0, void) str; 4971 4972 assert(str.small); 4973 assert(str.minimalCapacity == 0); 4974 4975 str = ""; 4976 assert(str.small); 4977 assert(str.capacity == 0); 4978 4979 4980 /+str = "1"; 4981 assert(!str.small); 4982 assert(str.capacity >= 1);+/ 4983 4984 str = null; 4985 assert(str.small); 4986 4987 str.release(); 4988 assert(str.small); 4989 } 4990 4991 //FixedString: 4992 pure nothrow @safe @nogc unittest{ 4993 FixedString!(char, 10) str; 4994 4995 assert(str.small); 4996 assert(str.minimalCapacity >= 10); 4997 4998 str = ""; 4999 assert(str.small); 5000 5001 for(int i = str.minimalCapacity; i != 0; --i) 5002 str ~= 'x'; 5003 5004 assert(str.length == str.capacity); 5005 assert(str.capacity == str.minimalCapacity); 5006 5007 ///str ~= 'y'; 5008 } 5009 5010 //append range 5011 unittest{ 5012 import std.range : ElementEncodingType, isInputRange, isRandomAccessRange; 5013 5014 String str; 5015 5016 struct Range{ 5017 int i; 5018 this(int i){this.i = i;} 5019 char front(){return 'x';} 5020 void popFront(){i -= 1;} 5021 bool empty(){return i == 0;} 5022 int length(){return i;} 5023 5024 } 5025 static assert(isInputRange!Range); 5026 str.append(Range(4)); 5027 5028 assert(str == "xxxx"); 5029 5030 } 5031 5032 /+ 5033 //appendMore 5034 unittest{ 5035 import std.range : ElementEncodingType, isInputRange, isRandomAccessRange; 5036 5037 String str; 5038 5039 str.appendMore(1, 2, '-', "lol"); 5040 5041 assert(str == "12-lol"); 5042 5043 } 5044 +/