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 }