1 /// Author: Aziz Köksal 2 /// License: GPL3 3 /// $(Maturity average) 4 module dil.String; 5 6 import common; 7 8 /// A string implementation that uses two pointers, 9 /// as opposed to one pointer and a size variable. 10 struct StringT(C) 11 { 12 alias S = StringT; /// Shortcut to own type. 13 /// Explicit constness for construction. 14 alias CS = const S, IS = immutable S; 15 alias inoutS = inout(S); /// Useful for explicit construction of inout Strings. 16 C* ptr; /// Points to the beginning of the string. 17 C* end; /// Points one past the end of the string. 18 19 /// A dummy struct to simulate "tail const". 20 /// 21 /// When a local variable with the type inout(S) has to be defined, 22 /// one cannot modify it or its members. 23 /// This issue can be worked around with this struct. 24 struct S2 25 { 26 const(C)* ptr; 27 const(C)* end; 28 void set(const(C)* p, const(C)* e) 29 { 30 ptr = p; 31 end = e; 32 } 33 void set(const(S) s) 34 { 35 set(s.ptr, s.end); 36 } 37 void set(const(C)[] a) 38 { 39 set(a.ptr, a.ptr + a.length); 40 } 41 static S2 ctor(T)(T x) 42 { 43 S2 s = void; 44 s.set(x); 45 return s; 46 } 47 } 48 49 50 /// Constructs from start and end pointers. 51 this(inout C* p, inout C* e) inout 52 { 53 assert(p <= e); 54 ptr = p; 55 end = e; 56 } 57 58 /// Constructs from a start pointer and a length value. 59 /// NB: use 'u' suffix on int literals, e.g.: this(str.ptr, 13u) 60 this(inout C* p, const size_t length) inout 61 { 62 this(p, p + length); 63 } 64 65 /// Constructs from a character array. 66 this(inout C[] str) inout 67 { 68 this(str.ptr, str.length); 69 } 70 71 /// Constructs from a character-terminated string. 72 this(inout C* p, const C terminator) inout 73 { 74 inout(C)* q = p; 75 while (*q != terminator) 76 q++; 77 this(p, q); 78 } 79 80 /// Constructs from Strings by joining them with joinStr. 81 this(const S[] strs, const S joinStr) 82 { 83 this = joinStr.join(strs); 84 } 85 86 /// ditto 87 this(const C[][] strs, const C[] joinStr) 88 { 89 this = CS(joinStr).join(strs); 90 } 91 92 /// Constructs from a single character. 93 this(const C c) 94 { 95 this = CS(&c, 1u).dup; 96 } 97 98 /// Constructs from an unsigned long. 99 /// NB: Not a regular constructor because "char" converts implicitly to ulong. 100 static S itoa(ulong x) 101 { 102 C[20] buffer; // ulong.max -> "18446744073709551615".len == 20 103 auto end = buffer.ptr + buffer.length; 104 auto p = end; 105 do 106 *--p = '0' + x % 10; 107 while (x /= 10); 108 return S(p, end).dup; 109 } 110 111 /// Checks pointers. 112 invariant() 113 { 114 assert(ptr ? ptr <= end : !end); 115 } 116 117 /// Converts t to S in case it's a different type than S. 118 static auto toS(T)(T t) 119 { 120 static if (is(T : const(S))) 121 return t; 122 else static if (is(typeof(IS(t)))) 123 return IS(t); 124 else static if (is(typeof(CS(t)))) 125 return CS(t); 126 else static if (is(typeof(S(t)))) 127 return S(t); 128 else 129 static assert(0, "no StringT constructor for " ~ T.stringof); 130 } 131 132 /// Returns a character array. 133 inout(C)[] opSlice() inout 134 { 135 return ptr[0..len]; 136 } 137 138 /// Returns a pointer from an index number. 139 /// When x is negative it is subtracted from the end pointer. 140 inout(C)* indexPtr(T)(T x) inout 141 { 142 static if (is(T : Neg)) 143 auto p = end - x.n; 144 else 145 auto p = ptr + x; 146 assert(ptr <= p && p <= end); 147 return p; 148 } 149 150 /// Returns a slice. 151 /// Params: 152 /// x = Start index. Negative values are subtracted from the end. 153 /// y = End index. Negative values are subtracted from the end. 154 inout(S) opSlice(T1, T2)(T1 x, T2 y) inout 155 { 156 return inoutS(indexPtr!T1(x), indexPtr!T2(y)); 157 } 158 159 /// Returns the character at position x. 160 /// Params: 161 /// x = Character index. Negative values are subtracted from the end. 162 inout(C) opIndex(T)(T x) inout 163 { 164 return *indexPtr!T(x); 165 } 166 167 /// Assigns c at position x. 168 ref S opIndexAssign(T)(C c, T x) 169 { 170 *indexPtr!T(x) = c; 171 return this; 172 } 173 174 /// Compares the bytes of two Strings for exact equality. 175 bool opEquals(T)(T x) const 176 { 177 return this[] == toS!T(x)[]; 178 } 179 180 /// Compares to a boolean value. 181 bool opEquals(bool b) const 182 { 183 return cast(bool)this == b; 184 } 185 186 /// Compares the chars of two Strings. 187 /// Returns: 0 if both are equal. 188 int_t opCmp(const S s) const 189 { 190 auto n = (len <= s.len) ? len : s.len; 191 const(C)* p = ptr, p2 = s.ptr; 192 for (; n; n--, p++, p2++) 193 if (int_t diff = *p - *p2) 194 return diff; 195 if (len != s.len) 196 n = len - s.len; 197 return n; 198 } 199 200 /// Compares two Strings ignoring case (only ASCII.) 201 int_t icmp(T)(T x) const 202 { 203 auto s = toS!T(x); 204 auto n = (len <= s.len) ? len : s.len; 205 const(C)* p = ptr, p2 = s.ptr; 206 for (; n; n--, p++, p2++) 207 if (int_t diff = tolower(*p) - tolower(*p2)) 208 return diff; 209 if (len != s.len) 210 n = len - s.len; 211 return n; 212 } 213 214 /// Compares two Strings ignoring case for equality (only ASCII.) 215 bool ieql(T)(T x) const 216 { 217 return icmp(x) == 0; 218 } 219 220 /// Concatenates x copies of this string. 221 S times(uint_t x) const 222 { 223 auto str = this[]; 224 auto slen = str.length; 225 C[] result = new C[x * slen]; 226 auto p = result.ptr; 227 for (; x--; p += slen) 228 p[0..slen] = str; 229 return S(result.ptr, p); 230 } 231 232 /// ditto 233 S opBinary(string op : "*")(uint_t rhs) const 234 { 235 return times(rhs); 236 } 237 238 /// ditto 239 S opBinaryRight(string op : "*")(uint_t lhs) const 240 { 241 return times(lhs); 242 } 243 244 /// Returns a list of Strings where each piece is of length n. 245 /// The last piece may be shorter. 246 inout(S)[] pieces(uint_t n) inout 247 { 248 if (n == 0) 249 return null; // TODO: throw Exception? 250 if (n >= len) 251 return [this]; 252 253 const roundlen = (len + len % n) / n; 254 S2[] result = new S2[roundlen]; 255 const(C)* p = ptr; 256 auto elem = result.ptr; 257 258 for (; p + n <= end; (p += n), elem++) 259 elem.set(p, p + n); 260 if (p < end) 261 elem.set(p, end); 262 263 return cast(inout(S)[])result; 264 } 265 266 /// Divides the String into num parts. 267 /// The remainder is appended to the last piece. 268 inout(S)[] divide(uint_t num) inout 269 { 270 if (num == 0) 271 return null; // TODO: throw Exception? 272 if (num == 1) 273 return [this]; 274 275 const piecelen = len / num; // Length of one piece. 276 S2[] result = new S2[num]; 277 const(C)* p = ptr; 278 auto elem = result.ptr; 279 280 for (; num--; (p += piecelen), elem++) 281 elem.set(p, p + piecelen); 282 if (p < end) // Update last element and include the rest of the String. 283 (--elem).set(p-piecelen, end); 284 285 return cast(inout(S)[])result; 286 } 287 288 /// ditto 289 inout(S)[] opBinary(string op : "/")(uint_t rhs) inout 290 { 291 return divide(rhs); 292 } 293 294 /// Appends another string or character. Returns a new object. 295 S opBinary(string op : "~", T)(T rhs) const 296 { 297 return S(cast(C[])(this[] ~ toS!T(rhs)[])); 298 } 299 300 /// Appends another string or character. Returns itself. 301 ref S opOpAssign(string op : "~", T)(T rhs) 302 { 303 this = this ~ rhs; 304 return this; 305 } 306 307 /// Returns a pointer to the first character, if this String is in rhs. 308 inout(C)* opBinary(string op : "in")(inout C[] rhs) const 309 { 310 return inoutS(rhs).findp(this); 311 } 312 313 /// Returns a pointer to the first character, if lhs is in this String. 314 inout(C)* opBinaryRight(string op : "in", T)(T lhs) inout 315 { 316 return findp(toS!T(lhs)); 317 } 318 319 /// Converts to bool. 320 bool opCast(T : bool)() const 321 { 322 return !isEmpty(); 323 } 324 325 /// Converts to an array string. 326 inout(C)[] opCast(T : inout C[])() inout 327 { 328 return ptr[0..len]; 329 } 330 331 /// Returns the byte length. 332 size_t len() const 333 { 334 return end - ptr; 335 } 336 337 /// Returns a copy. 338 S dup() const 339 { 340 return S(ptr[0..len].dup); 341 } 342 343 /// Returns true if pointers are null. 344 bool isNull() const 345 { 346 return ptr is null; 347 } 348 349 /// Returns true if the string is empty. 350 bool isEmpty() const 351 { 352 return ptr is end; 353 } 354 355 /// ditto 356 immutable(C)[] toString() 357 { 358 return this[].idup; 359 } 360 361 /// Return true if lower-case. 362 static bool islower(const C c) 363 { 364 return 'a' <= c && c <= 'z'; 365 } 366 367 /// Return true if upper-case. 368 static bool isupper(const C c) 369 { 370 return 'A' <= c && c <= 'Z'; 371 } 372 373 /// Returns the lower-case version of c. 374 static inout(C) tolower(inout C c) 375 { 376 return isupper(c) ? cast(typeof(c))(c + 0x20) : c; 377 } 378 379 /// Returns the upper-case version of c. 380 static inout(C) toupper(inout C c) 381 { 382 return islower(c) ? cast(typeof(c))(c - 0x20) : c; 383 } 384 385 /// Converts to lower-case (only ASCII.) 386 ref S tolower() 387 { 388 auto p = ptr; 389 for (; p < end; p++) 390 *p = tolower(*p); 391 return this; 392 } 393 394 /// ditto 395 S tolower() const 396 { 397 return dup.tolower(); 398 } 399 400 /// Converts to upper-case (only ASCII.) 401 ref S toupper() 402 { 403 for (auto p = ptr; p < end; p++) 404 *p = toupper(*p); 405 return this; 406 } 407 408 /// ditto 409 S toupper() const 410 { 411 return dup.toupper(); 412 } 413 414 /// Encodes the byte characters with hexadecimal digits. 415 S toHex(bool lowercase)() const 416 { 417 immutable hexdigits = lowercase ? "0123456789abcdef" : "0123456789ABCDEF"; 418 auto result = S(new C[len * C.sizeof * 2]); // Reserve space. 419 auto pr = result.ptr; 420 for (const(C)* p = ptr; p < end; p++) 421 static if (C.sizeof == 4) 422 { 423 *pr++ = hexdigits[*p >> 28]; 424 *pr++ = hexdigits[*p >> 24 & 0x0F]; 425 *pr++ = hexdigits[*p >> 20 & 0x0F]; 426 *pr++ = hexdigits[*p >> 16 & 0x0F]; 427 *pr++ = hexdigits[*p >> 12 & 0x0F]; 428 *pr++ = hexdigits[*p >> 8 & 0x0F]; 429 *pr++ = hexdigits[*p >> 4 & 0x0F]; 430 *pr++ = hexdigits[*p & 0x0F]; 431 } 432 else 433 static if (C.sizeof == 2) 434 { 435 *pr++ = hexdigits[*p >> 12]; 436 *pr++ = hexdigits[*p >> 8 & 0x0F]; 437 *pr++ = hexdigits[*p >> 4 & 0x0F]; 438 *pr++ = hexdigits[*p & 0x0F]; 439 } 440 else 441 { 442 *pr++ = hexdigits[*p >> 4]; 443 *pr++ = hexdigits[*p & 0x0F]; 444 } 445 assert(pr is result.end); 446 return result; 447 } 448 449 alias tohex = toHex!(true); 450 alias toHEX = toHex!(false); 451 452 /// Calculates a hash value. 453 /// Note: The value will differ between 32bit and 64bit systems, 454 /// and also between little and big endian systems. 455 hash_t hashOf() const 456 { 457 hash_t hash; 458 const(C)* sptr = ptr; 459 auto slen = len; 460 461 auto rem_len = slen % hash_t.sizeof; // Remainder. 462 auto hptr = cast(const(hash_t)*)sptr; 463 if (slen == rem_len) 464 goto Lonly_remainder; 465 466 { 467 // Divide the length by 4 or 8 (x86 vs. x86_64). 468 auto hlen = slen / hash_t.sizeof; 469 assert(hlen, "can't be zero"); 470 471 while (hlen--) // Main loop. 472 hash = hash * 11 + *hptr++; 473 } 474 475 if (rem_len) 476 { // Calculate the hash of the remaining characters. 477 sptr = cast(typeof(sptr))hptr; // hptr points exactly to the remainder. 478 Lonly_remainder: 479 hash_t chunk; 480 while (rem_len--) // Remainder loop. 481 chunk = (chunk << 8) | *sptr++; 482 hash = hash * 11 + chunk; 483 } 484 485 return hash; 486 } 487 488 /// Returns true if this String starts with prefix. 489 bool startsWith(const S prefix) const 490 { 491 return prefix.len <= len && CS(ptr, prefix.len) == prefix; 492 } 493 494 /// ditto 495 bool startsWith(const C[] prefix) const 496 { 497 return startsWith(CS(prefix)); 498 } 499 500 /// Returns true if this String starts with one of the specified prefixes. 501 bool startsWith(const S[] prefixes) const 502 { 503 foreach (prefix; prefixes) 504 if (startsWith(prefix)) 505 return true; 506 return false; 507 } 508 509 /// ditto 510 bool startsWith(const C[][] prefixes) const 511 { 512 foreach (prefix; prefixes) 513 if (startsWith(CS(prefix))) 514 return true; 515 return false; 516 } 517 518 /// Returns true if this String ends with suffix. 519 bool endsWith(const S suffix) const 520 { 521 return suffix.len <= len && CS(end - suffix.len, end) == suffix; 522 } 523 524 /// ditto 525 bool endsWith(const C[] suffix) const 526 { 527 return endsWith(CS(suffix)); 528 } 529 530 /// Returns true if this String ends with one of the specified suffixes. 531 bool endsWith(const S[] suffixes) const 532 { 533 foreach (suffix; suffixes) 534 if (endsWith(suffix)) 535 return true; 536 return false; 537 } 538 539 /// ditto 540 bool endsWith(const C[][] suffixes) const 541 { 542 foreach (suffix; suffixes) 543 if (endsWith(CS(suffix))) 544 return true; 545 return false; 546 } 547 548 /// Returns true if this string is a slice of s. 549 bool slices(const S s) const 550 { 551 return s.ptr <= ptr && end <= s.end; 552 } 553 554 /// ditto 555 bool slices(const C[] s) const 556 { 557 return slices(CS(s)); 558 } 559 560 /// Returns 'a' if RT is of type size_t, otherwise 'b'. 561 static auto choice(RT, A, B)(A a, B b) 562 { 563 static if (is(RT == size_t)) 564 return a; 565 else 566 return b; 567 } 568 569 /// Searches for character c. 570 RT findC(RT, string pred = q{*p == c})(const C c) inout 571 { 572 inout(C)* p = ptr; 573 for (; p < end; p++) 574 if (mixin(pred)) 575 return choice!RT(p - ptr, p); 576 return choice!RT(-1, null); 577 } 578 579 /// Searches for character c starting from the end. 580 RT findrC(RT, string pred = q{*p == c})(const C c) inout 581 { 582 inout(C)* p = end; 583 while (--p >= ptr) 584 if (mixin(pred)) 585 return choice!RT(p - ptr, p); 586 return choice!RT(-1, null); 587 } 588 589 /// Searches for s. 590 RT findS(RT)(const S s) inout 591 { 592 if (s.len == 0) 593 return choice!RT(0, ptr); 594 else 595 if (s.len == 1) 596 return choice!RT(find(s[0]), findp(s[0])); 597 else 598 if (s.len <= len) // Return when the argument string is longer. 599 { 600 inout(C)* p = ptr; 601 const firstChar = *s.ptr; 602 603 for (; p < end; p++) 604 if (*p == firstChar) // Find first matching character. 605 { 606 const(C)* p2 = s.ptr; 607 inout(C)* matchBegin = p; 608 while (p < end && *p++ == *p2++) 609 if (p2 is s.end) // If at the end, we have a match. 610 return choice!RT(matchBegin - ptr, matchBegin); 611 } 612 } 613 return choice!RT(-1, null); 614 } 615 616 /// Searches for s starting from the end. 617 RT findrS(RT)(const S s) inout 618 { 619 if (s.len == 0) 620 return choice!RT(len, end); 621 else 622 if (s.len == 1) 623 return choice!RT(findr(s[0]), findrp(s[0])); 624 else 625 if (s.len <= len) // Return when the argument string is longer. 626 { 627 inout(C)* p = end; 628 const lastChar = *(s.end - 1); 629 630 while (--p >= ptr) 631 if (*p == lastChar) // Find first matching character. 632 { 633 const(C)* p2 = s.end - 1; 634 while (--p >= ptr) 635 if (*p != *--p2) 636 break; 637 else if (p2 is s.ptr) // If at the start, we have a match. 638 return choice!RT(p - ptr, p); 639 } 640 } 641 return choice!RT(-1, null); 642 } 643 644 /// Searches for character c. 645 alias find = findC!(size_t); 646 /// Searches for character c. 647 /// Returns: A pointer to c, or null if not found. 648 alias findp = findC!(inout(C)*); 649 /// Searches for character c starting from the end. 650 alias findr = findrC!(size_t); 651 /// Searches for character c, returning a pointer. 652 alias findrp = findrC!(inout(C)*); 653 /// Searches for s. 654 /// Returns: The position index, or -1 if not found. 655 alias find = findS!(size_t); 656 /// Searches for s. 657 /// Returns: A pointer to the beginning of s, or null if not found. 658 alias findp = findS!(inout(C)*); 659 /// Searches for s starting from the end, returning the index. 660 alias findr = findrS!(size_t); 661 /// Searches for s starting from the end, returning a pointer. 662 alias findrp = findrS!(inout(C)*); 663 664 /// Splits by String s and returns a list of slices. 665 inout(S)[] split(T)(T x) inout 666 { 667 auto s = toS!T(x); 668 S2[] result; 669 const(C)* p = ptr, prev = p; 670 auto slen = s.len; 671 if (slen == 0) 672 { 673 result = new S2[len + 2]; // +2 for first and last empty elements. 674 auto elem = result.ptr; 675 676 for (; p <= end; p++, elem++) 677 { 678 elem.set(prev, p); 679 prev = p; 680 } 681 elem.set(p, p); 682 } 683 else 684 if (slen == 1) 685 return split(*s.ptr); 686 else 687 { 688 const(C)* ps; 689 while ((ps = CS(p, end).findp(s)) !is null) 690 { 691 result ~= S2(p, ps); 692 p = ps + slen; 693 assert(p <= end); 694 } 695 result ~= S2(p, end); 696 } 697 return cast(inout(S)[])result; 698 } 699 700 /// ditto 701 inout(S)[] split(T : C)(T c) inout 702 { 703 S2[] result; 704 const(C)* p = ptr, prev = p; 705 for (; p < end; p++) 706 if (*p == c) 707 { 708 result ~= S2(prev, p); 709 prev = p+1; 710 } 711 result ~= S2(prev, end); 712 return cast(inout(S)[])result; 713 } 714 715 /// Substitutes a with b. 716 ref S sub_(const C a, const C b) 717 { 718 auto p = ptr; 719 for (; p < end; p++) 720 if (*p == a) 721 *p = b; 722 return this; 723 } 724 725 /// ditto 726 ref S sub_(const S a, const S b) 727 { 728 auto alen = a.len, blen = b.len; 729 730 if (alen == 0 && blen == 0) 731 {} 732 else 733 if (alen == 0) 734 { 735 C[] result; 736 const bstr = b[]; 737 const(C)* p = ptr; 738 739 while (p < end) 740 result ~= bstr ~ *p++; 741 result ~= bstr; 742 this = S(result); 743 } 744 else 745 if (alen == 1 && blen == 1) 746 sub_(a[0], b[0]); 747 else 748 if (blen == 0) 749 { 750 C* pwriter = ptr; 751 const(C)* preader = pwriter, pa; 752 753 while ((pa = CS(preader, end).findp(a)) !is null) 754 { 755 while (preader < pa) // Copy till beginning of a. 756 *pwriter++ = *preader++; 757 preader += alen; // Skip a. 758 } 759 if (preader !is pwriter) 760 { // Write the rest. 761 while (preader < end) 762 *pwriter++ = *preader++; 763 end = pwriter; 764 } 765 } 766 else 767 { 768 const(C)* pa = findp(a); 769 if (pa) 770 { 771 C[] result; 772 const bstr = b[]; 773 const(C)* p = ptr; 774 775 do 776 { 777 if (pa) // Append previous string? 778 result ~= CS(p, pa)[]; 779 result ~= bstr; 780 p = pa + alen; // Skip a. 781 } while ((pa = CS(p, end).findp(a)) !is null); 782 if (p < end) 783 result ~= CS(p, end)[]; 784 this = S(result); 785 } 786 } 787 return this; 788 } 789 790 /// ditto 791 ref S sub(A, B)(A a, B b) 792 { 793 static if (is(typeof(sub_(a, b)))) 794 return sub_(a, b); 795 else 796 return sub_(toS!A(a), toS!B(b)); 797 } 798 799 /// ditto 800 S sub(A, B)(A a, B b) const 801 { 802 return dup.sub_(toS!A(a), toS!B(b)); 803 } 804 805 /// Searches for sep and returns the part before and after that. 806 inout(S)[2] partition(T)(T sep) inout 807 { 808 static if (is(sep : C)) 809 { 810 if (auto psep = findp(sep)) 811 return [inoutS(ptr, psep), inoutS(psep + 1, end)]; 812 } 813 else 814 { 815 auto sep_ = toS!T(sep); 816 if (auto psep = findp(sep_)) 817 return [inoutS(ptr, psep), inoutS(psep + sep_.len, end)]; 818 } 819 return [this, inoutS()]; 820 } 821 822 /// Searches for sep and returns the part before and after that. 823 inout(S)[2] rpartition(T)(T sep) inout 824 { 825 static if (is(sep : C)) 826 { 827 if (auto psep = findrp(sep)) 828 return [inoutS(ptr, psep), inoutS(psep + 1, end)]; 829 } 830 else 831 { 832 auto sep_ = toS!T(sep); 833 if (auto psep = findrp(sep_)) 834 return [inoutS(ptr, psep), inoutS(psep + sep_.len, end)]; 835 } 836 return [inoutS(), this]; 837 } 838 839 /// Concatenates strs using this String as a separator. 840 S join(const S[] strs) const 841 { 842 C[] result; 843 if (strs.length) 844 { 845 const sep = this[]; 846 result = strs[0][].dup; 847 foreach (str; strs[1..$]) 848 result ~= sep ~ str[]; 849 } 850 return S(result); 851 } 852 853 /// ditto 854 S join(const C[][] strs) const 855 { 856 C[] result; 857 if (strs.length) 858 { 859 const sep = this[]; 860 result = strs[0].dup; 861 foreach (str; strs[1..$]) 862 result ~= sep ~ str; 863 } 864 return S(result); 865 } 866 867 /// Like join, but also appends the separator. 868 S rjoin(const S[] strs) const 869 { 870 C[] result; 871 const sep = this[]; 872 foreach (str; strs) 873 (result ~= str[]) ~= sep; 874 return S(result); 875 } 876 877 /// ditto 878 S rjoin(const C[][] strs) const 879 { 880 return rjoin(strs.toStrings()); 881 } 882 883 /// Like join, but also prepends the separator. 884 S ljoin(const S[] strs) const 885 { 886 C[] result; 887 const sep = this[]; 888 foreach (str; strs) 889 (result ~= sep) ~= str[]; 890 return S(result); 891 } 892 893 /// ditto 894 S ljoin(const C[][] strs) const 895 { 896 return ljoin(strs.toStrings()); 897 } 898 899 /// Returns itself reversed. 900 ref S reverse() 901 { 902 auto lft = ptr; 903 auto rgt = end - 1; 904 for (auto n = len / 2; n != 0; n--, lft++, rgt--) 905 { // Swap left and right characters. 906 const c = *lft; 907 *lft = *rgt; 908 *rgt = c; 909 } 910 return this; 911 } 912 913 /// Returns a reversed String. 914 S reverse() const 915 { 916 return dup.reverse(); 917 } 918 } 919 920 alias MString = StringT!(char); /// Mutable instantiation for char. 921 alias String = const MString; /// Alias for const. 922 alias IString = immutable MString; /// Alias for immutable. 923 alias WString = StringT!(wchar); /// Instantiation for wchar. 924 alias DString = StringT!(dchar); /// Instantiation for dchar. 925 926 /// Returns a string array slice ranging from begin to end. 927 inout(char)[] slice(inout(char)* begin, inout(char)* end) 928 { 929 return String.inoutS(begin, end)[]; 930 } 931 932 /// Returns a copy of str where a is replaced with b. 933 cstring replace(cstring str, char a, char b) 934 { 935 return String(str).sub(a, b)[]; 936 } 937 938 /// Converts x to a string array. 939 char[] itoa(ulong x) 940 { 941 if (__ctfe) 942 { 943 auto buffer = new char[20]; // ulong.max -> "18446744073709551615".len == 20 944 auto i = buffer.length; 945 do 946 buffer[--i] = '0' + x % 10; 947 while (x /= 10); 948 return buffer[i .. $]; 949 } 950 else 951 return String.itoa(x)[]; 952 } 953 954 /// Returns a list of Strings from a list of char arrays. 955 inout(StringT!C)[] toStrings(C)(inout C[][] strs) 956 { 957 auto result = new StringT!C.S2[strs.length]; 958 auto elem = result.ptr - 1; 959 foreach (i, s; strs) 960 (++elem).set(s); 961 return cast(inout(StringT!C)[])result; 962 } 963 964 /// Calculates a hash value for str. 965 /// Note: The value will differ between 32bit and 64bit systems. 966 /// It will also differ between little and big endian systems. 967 hash_t hashOf(cstring str) 968 { 969 return String(str).hashOf(); 970 } 971 972 /// ditto 973 hash_t hashOfCTF(cstring str) 974 { 975 hash_t hash; 976 977 // Can't reinterpret cast inside CTFs. See: $(DMDBUG 5497) 978 auto count = str.length / hash_t.sizeof; 979 size_t i; // Index into str. 980 while (count--) // Main loop. 981 { 982 hash_t hc; auto t = hash_t.sizeof; // Convert t nr of bytes from str. 983 while (t--) 984 version(BigEndian) 985 hc = (hc << 8) | str[i++]; // Add c as LSByte. 986 else // Add c as MSByte. 987 hc = (hc >> 8) | (str[i++] << (hash_t.sizeof-1)*8); 988 hash = hash * 11 + hc; 989 } 990 991 if (auto t = str.length - i) 992 { // Add remainder hash. 993 hash_t hc; 994 while (t--) // Remainder loop. 995 hc = (hc << 8) | str[i++]; 996 hash = hash * 11 + hc; 997 } 998 return hash; 999 } 1000 1001 void testString() 1002 { 1003 scope msg = new UnittestMsg("Testing struct String."); 1004 alias S = String; 1005 1006 // Constructing. 1007 assert(S("") == ""); 1008 assert(S("a"[0]) == "a"); 1009 assert(S(['a'][0]) == "a"); 1010 assert(S("".ptr, '\0') == ""); // String literals are always zero terminated. 1011 assert(S("abcd".ptr, '\0') == S("abcd")); 1012 assert(S("abcd".ptr, 'c') == S("ab")); 1013 assert(S("abcd".ptr, 2u) == S("ab")); 1014 assert(S.itoa(0) == S("0") && S.itoa(1999) == S("1999")); 1015 1016 // Boolean conversion. 1017 if (S("is cool")) {} 1018 else assert(0); 1019 assert(S() == false && !S()); 1020 assert(S("") == false && !S("")); 1021 assert(S("verdad") == true); 1022 1023 // Concatenation. 1024 { 1025 auto s = MString("x".dup); 1026 ((s ~= S()) ~= 'y') ~= "z"; 1027 assert(s == S("x") ~ S("yz")); 1028 assert(S() ~ S() == S()); 1029 } 1030 1031 // Substitution. 1032 assert(S("abce").sub('e', 'd') == "abcd"); 1033 assert(S("abc ef").sub(' ', 'd') == "abcdef"); 1034 assert(S("abc f").sub(' ', "de") == "abcdef"); 1035 assert(S("abcd").sub("", " ") == " a b c d "); 1036 assert(S("").sub("", "a") == "a"); 1037 assert(S(" a b c d ").sub(" ", "") == "abcd"); 1038 assert(S("ab_cd").sub("_", "") == "abcd"); 1039 assert(S("abcd").sub("abcd", "") == ""); 1040 assert(S("aaaa").sub("a", "") == ""); 1041 assert(S("").sub(S(""), "") == ""); 1042 assert(S("").sub("", S("")) == ""); 1043 assert(S("").sub(S(""), S("")) == ""); 1044 1045 // Duplication. 1046 assert(S("chica").dup == S("chica")); 1047 1048 // Comparison. 1049 assert(S("a") < S("b")); 1050 assert(S("b") > S("a")); 1051 assert(S("a") > S("B")); 1052 assert(S("B") < S("a")); 1053 assert(S("a") <= S("a")); 1054 assert(S("b") >= S("b")); 1055 assert(S("a") == S("a")); 1056 assert(S("a") != S("b")); 1057 assert(S("abcd") == S("abcd")); 1058 assert(S("") == S()); 1059 assert(S() == ""); 1060 assert(S() == S("") && "" == null); 1061 assert(S("ABC").ieql("abc")); 1062 assert(S("XYZ").ieql(S("xyz"))); 1063 assert(S("a").icmp("B") < 0); 1064 assert(S("x").icmp(S("Y")) < 0); 1065 assert(S("B").icmp("a") > 0); 1066 assert(S("Y").icmp(S("x")) > 0); 1067 1068 // Slicing. 1069 assert(S("rapido")[] == "rapido"); 1070 assert(S("rapido")[0..0] == ""); 1071 assert(S("rapido")[1..4] == "api"); 1072 assert(S("rapido")[2..Neg(3)] == "p"); 1073 assert(S("rapido")[Neg(3)..3] == ""); 1074 assert(S("rapido")[Neg(4)..Neg(1)] == "pid"); 1075 assert(S("rapido")[6..6] == ""); 1076 1077 { 1078 auto s = S("rebanada"); 1079 assert(s.slices(s)); 1080 assert(s[0..0].slices(s)); 1081 assert(s[1..Neg(1)].slices(s)); 1082 assert(s[s.len..s.len].slices(s)); 1083 assert(S(s.end, s.end).slices(s)); 1084 } 1085 1086 // Indexing. 1087 assert(S("abcd")[0] == 'a'); 1088 assert(S("abcd")[2] == 'c'); 1089 assert(S("abcd")[Neg(1)] == 'd'); 1090 1091 // Multiplying. 1092 assert(S("ha") * 6 == "hahahahahaha"); 1093 assert(S("oi") * 3 == "oioioi"); 1094 assert(S("palabra") * 0 == ""); 1095 assert(1 * S("mundo") == "mundo"); 1096 assert(S("") * 4 == ""); 1097 1098 // Dividing. 1099 assert(S("abcd") / 1 == [S("abcd")]); 1100 assert(S("abcd") / 2 == [S("ab"), S("cd")]); 1101 assert(S("abcd") / 3 == [S("a"), S("b"), S("cd")]); 1102 assert(S("abcdefghi") / 2 == [S("abcd"), S("efghi")]); 1103 assert(S("abcdefghijk") / 4 == [S("ab"), S("cd"), S("ef"), S("ghijk")]); 1104 1105 assert(S("abcdef").pieces(2) == [S("ab"), S("cd"), S("ef")]); 1106 assert(S("abcdef").pieces(4) == [S("abcd"), S("ef")]); 1107 1108 // Splitting. 1109 assert(S("").split("") == [S(""), S("")]); 1110 assert(S("abc").split("") == [S(""), S("a"), S("b"), S("c"), S("")]); 1111 assert(S("abc").split("b") == [S("a"), S("c")]); 1112 assert(S("abc").split('b') == [S("a"), S("c")]); 1113 1114 // Searching. 1115 assert("Mundo" in S("¡Hola Mundo!")); 1116 assert(S("") in "a"); 1117 assert(S("abcd").find(S("cd")) == 2); 1118 assert(S("").find(S("")) == 0); 1119 assert(S("abcd").findr('d') == 3); 1120 assert(S("abcd").findr('a') == 0); 1121 assert(S("abcd").findr('e') == -1); 1122 { 1123 auto s = S("abcd"); 1124 assert(s.findp(S("abcd")) is s.ptr); 1125 assert(s.findp(S("d")) is s.end - 1); 1126 assert(s.findrp(S("ab")) is s.ptr); 1127 assert(s.findrp(S("cd")) is s.end - 2); 1128 assert(s.findrp(S("")) is s.end); 1129 } 1130 assert(S("abcd").findr(S("ab")) == 0); 1131 assert(S("abcd").findr(S("cd")) == 2); 1132 1133 // Reversing. 1134 assert(S().reverse() == ""); 1135 assert(S("").reverse() == ""); 1136 assert(S("a").reverse() == "a"); 1137 assert(S("abc").reverse() == "cba"); 1138 assert(S("abcd").reverse() == "dcba"); 1139 assert(S("abc").reverse().reverse() == "abc"); 1140 1141 // Matching prefixes and suffixes. 1142 assert(S("abcdefg").startsWith("abc")); 1143 assert(S("abcdefg").startsWith([" ", "abc"])); 1144 assert(!S("ab").startsWith("abc")); 1145 assert(S("abcdefg").endsWith("efg")); 1146 assert(S("abcdefg").endsWith([" ", "efg"])); 1147 assert(!S("fg").endsWith("efg")); 1148 1149 // Partitioning. 1150 assert(S("").partition("") == [S(), S()]); 1151 assert(S("ab.cd").partition(".") == [S("ab"), S("cd")]); 1152 assert(S("ab.cd").partition('.') == [S("ab"), S("cd")]); 1153 assert(S("abcd").partition(".") == [S("abcd"), S("")]); 1154 assert(S("abcd.").partition(".") == [S("abcd"), S("")]); 1155 assert(S(".abcd").partition(".") == [S(""), S("abcd")]); 1156 assert(S("abcd").partition("") == [S(""), S("abcd")]); 1157 1158 assert(S("").rpartition("") == [S(), S()]); 1159 assert(S("ab.cd").rpartition(".") == [S("ab"), S("cd")]); 1160 assert(S("ab.cd").rpartition('.') == [S("ab"), S("cd")]); 1161 assert(S("abcd").rpartition(".") == [S(""), S("abcd")]); 1162 assert(S("abcd.").rpartition(".") == [S("abcd"), S("")]); 1163 assert(S(".abcd").rpartition(".") == [S(""), S("abcd")]); 1164 assert(S("abcd").rpartition("") == [S("abcd"), S("")]); 1165 1166 // Converting to hex string. 1167 assert(S("äöü").tohex() == "c3a4c3b6c3bc"); 1168 assert(S("äöü").toHEX() == "C3A4C3B6C3BC"); 1169 1170 // Case conversion. 1171 assert(S("^agmtz$").toupper() == "^AGMTZ$"); 1172 assert(S("^AGMTZ$").tolower() == "^agmtz$"); 1173 1174 // Joining. 1175 { 1176 string[] strs; 1177 assert(S(strs=["a","b","c","d"], ".") == "a.b.c.d"); 1178 assert(S(strs, "") == "abcd"); 1179 assert(S(strs=["a"], ".") == "a"); 1180 assert(S(",").rjoin(strs) == "a,"); 1181 assert(S(",").ljoin(strs) == ",a"); 1182 assert(S(",").rjoin(strs=["a", "b"]) == "a,b,"); 1183 assert(S(",").ljoin(strs) == ",a,b"); 1184 assert(S(",").rjoin(strs=null) == ""); 1185 assert(S(",").ljoin(strs) == ""); 1186 } 1187 }