1 // =================================
  2 // Copyright (c) 2021 Seppo Laakko
  3 // Distributed under the MIT license
  4 // =================================
  5 
  6 using System.Collections;
  7 
  8 namespace System
  9 {
 10     public typedef String<char> string;
 11     public typedef String<wchar> wstring;
 12     public typedef String<uchar> ustring;
 13 
 14     public nothrow const char* EmptyString(char x)
 15     {
 16         return "";
 17     }
 18 
 19     public nothrow const wchar* EmptyString(wchar x)
 20     {
 21         return w"";
 22     }
 23 
 24     public nothrow const uchar* EmptyString(uchar x)
 25     {
 26         return u"";
 27     }
 28 
 29     public class String<CharT>
 30     {
 31         public typedef RandomAccessIter<CharTconst CharT&const CharT*> ConstIterator;
 32         public typedef RandomAccessIter<CharTCharT&CharT*> Iterator;
 33 
 34         public inline nothrow String() : chars(null)len(0)res(0)
 35         {
 36         }
 37         public inline nothrow String(const CharT* chars_) : len(StrLen(chars_))res(0)chars(null)
 38         {
 39             if (len > 0)
 40             {
 41                 Reserve(len);
 42                 StrCopy(charschars_);
 43             }
 44         }
 45         public inline nothrow String(const CharT* chars_long length_) : len(0)res(0)chars(null)
 46         {
 47             if (length_ > 0)
 48             {
 49                 Reserve(length_);
 50                 len = StrCopy(charschars_length_);
 51             }
 52         }
 53         public inline nothrow String(const CharT* beginconst CharT* end) : this(beginend - begin)
 54         {
 55         }
 56         public nothrow String(const String<CharT>& that) : len(that.len)res(0)chars(null)
 57         {
 58             if (len > 0)
 59             {
 60                 Reserve(len);
 61                 StrCopy(charsthat.chars);
 62             }
 63         }
 64         public nothrow String(String<CharT>&& that) : len(that.len)res(that.res)chars(that.chars)
 65         {
 66             that.len = 0;
 67             that.res = 0;
 68             that.chars = null;
 69         }
 70         public inline explicit nothrow String(CharT c) : len(1)res(0)chars(null)
 71         {
 72             Reserve(1);
 73             chars[0] = c;
 74             chars[1] = '\0';
 75         }
 76         public nothrow String(CharT clong n) : len(n)res(0)chars(null)
 77         {
 78             if (n > 0)
 79             {
 80                 Reserve(n);
 81                 for (long i = 0; i < n; ++i;)
 82                 {
 83                     chars[i] = c;
 84                 }
 85                 chars[n] = '\0';
 86             }
 87         }
 88         public nothrow void operator=(const String<CharT>& that)
 89         {
 90             Deallocate();
 91             Reserve(that.len);
 92             len = that.len;
 93             if (len > 0)
 94             {
 95                 StrCopy(charsthat.chars);
 96             }
 97         }
 98         public inline nothrow void operator=(String<CharT>&& that)
 99         {
100             Swap(lenthat.len);
101             Swap(resthat.res);
102             Swap(charsthat.chars);
103         }
104         public ~String()
105         {
106             Deallocate();
107         }
108         public inline nothrow long Length() const
109         {
110             return len;
111         }
112         public inline nothrow long Capacity() const
113         {
114             return res;
115         }
116         public inline nothrow bool IsEmpty() const
117         {
118             return len == 0;
119         }
120         public nothrow void Clear()
121         {
122             Deallocate();
123         }
124         public inline nothrow const CharT* Chars() const
125         {
126             if (chars != null)
127             {
128                 return chars;
129             }
130             return EmptyString(CharT());
131         }
132         public inline CharT operator[](long index) const
133         {
134             if (index < 0 || index >= len)
135             {
136                 ThrowIndexOutOfBoundsException();
137             }
138             return chars[index];
139         }
140         public inline CharT& operator[](long index)
141         {
142             if (index < 0 || index >= len)
143             {
144                 ThrowIndexOutOfBoundsException();
145             }
146             return chars[index];
147         }
148         public nothrow void Reserve(long minLen)
149         {
150             if (minLen > 0)
151             {
152                 long minRes = minLen + 1;
153                 if (minRes > res)
154                 {
155                     Grow(minRes);
156                 }
157             }
158         }
159         public inline nothrow String<CharT>& Append(CharT c)
160         {
161             Reserve(len + 1);
162             chars[len] = c;
163             chars[++len] = '\0';
164             return *this;
165         }
166         public String<CharT>& Append(CharT clong count)
167         {
168             if (count < 0)
169             {
170                 ThrowInvalidParameterException();
171             }
172             if (count > 0)
173             {
174                 Reserve(len + count);
175                 for (long i = 0; i < count; ++i;)
176                 {
177                     chars[len++] = c;
178                 }
179                 chars[len] = '\0';
180             }
181             return *this;
182         }
183         public inline nothrow String<CharT>& Append(const CharT* that)
184         {
185             AppendFrom(thatStrLen(that));
186             return *this;
187         }
188         public inline nothrow String<CharT>& Append(const CharT* thatlong count)
189         {
190             AppendFrom(thatcount);
191             return *this;
192         }
193         public inline nothrow String<CharT>& Append(const String<CharT>& that)
194         {
195             AppendFrom(that.charsthat.len);
196             return *this;
197         }
198         public void Insert(long indexCharT c)
199         {
200             if (index < 0)
201             {
202                 ThrowIndexOutOfBoundsException();
203             }
204             if (index >= len)
205             {
206                 Append(c);
207             }
208             else
209             {
210                 Reserve(len + 1);
211                 for (long i = len - 1; i >= index; --i;)
212                 {
213                     chars[i + 1] = chars[i];
214                 }
215                 chars[index] = c;
216                 ++len;
217                 chars[len] = '\0';
218             }
219         }
220         public void Insert(long indexconst String<CharT>& that)
221         {
222             if (index < 0)
223             {
224                 ThrowIndexOutOfBoundsException();
225             }
226             if (that.IsEmpty()) return;
227             if (index >= len)
228             {
229                 Append(that);
230             }
231             else
232             {
233                 Reserve(len + that.len);
234                 for (long i = len - 1; i >= index; --i;)
235                 {
236                     chars[i + that.len] = chars[i];
237                 }
238                 for (long i = 0; i < that.len; ++i;)
239                 {
240                     chars[index + i] = that.chars[i];
241                 }
242                 len = len + that.len;
243                 chars[len] = '\0';
244             }
245         }
246         public void Remove(long indexlong count)
247         {
248             if (index < 0)
249             {
250                 ThrowIndexOutOfBoundsException();
251             }
252             if (count < 0)
253             {
254                 ThrowInvalidParameterException();
255             }
256             if (IsEmpty()) return;
257             if (index > len)
258             {
259                 index = len;
260             }
261             if (index + count > len)
262             {
263                 count = len - index;
264             }
265             for (long i = index; i < len - count; ++i;)
266             {
267                 chars[i] = chars[i + count];
268             }
269             len = len - count;
270             chars[len] = '\0';
271         }
272         public nothrow void Replace(CharT oldCharCharT newChar)
273         {
274             long n = len;
275             for (long i = 0; i < n; ++i;)
276             {
277                 if (chars[i] == oldChar)
278                 {
279                     chars[i] = newChar;
280                 }
281             }
282         }
283         public nothrow void Replace(const String<CharT>& oldStringconst String<CharT>& newString)
284         {
285              long oldLen = oldString.Length();
286              long newLen = newString.Length();
287              long pos = Find(oldString);
288              while (pos != -1)
289              {
290                  Remove(posoldLen);
291                  Insert(posnewString);
292                  pos = Find(oldStringpos + newLen);
293              }
294         }
295         public nothrow String<CharT> Substring(long start) const
296         {
297             if (start >= 0 && start < len)
298             {
299                 return String<CharT>(chars + start);
300             }
301             return String<CharT>();
302         }
303         public nothrow String<CharT> Substring(long startlong length) const
304         {
305             if (start >= 0 && start < len)
306             {
307                 return String<CharT>(chars + startlength);
308             }
309             return String<CharT>();
310         }
311         public inline nothrow Iterator Begin()
312         {
313             return Iterator(chars);
314         }
315         public inline nothrow ConstIterator Begin() const
316         {
317             return ConstIterator(chars);
318         }
319         public inline nothrow ConstIterator CBegin() const
320         {
321             return ConstIterator(chars);
322         }
323         public inline nothrow Iterator End()
324         {
325             if (chars != null)
326             {
327                 return Iterator(chars + len);
328             }
329             return Iterator(null);
330         }
331         public inline nothrow ConstIterator End() const
332         {
333             if (chars != null)
334             {
335                 return ConstIterator(chars + len);
336             }
337             return ConstIterator(null);
338         }
339         public inline nothrow ConstIterator CEnd() const
340         {
341             if (chars != null)
342             {
343                 return ConstIterator(chars + len);
344             }
345             return ConstIterator(null);
346         }
347         public nothrow bool StartsWith(const String<CharT>& prefix) const
348         {
349             long n = prefix.len;
350             if (len < n) return false;
351             for (long i = 0; i < n; ++i;)
352             {
353                 if (chars[i] != prefix[i]) return false;
354             }
355             return true;
356         }
357         public nothrow bool EndsWith(const String<CharT>& suffix) const
358         {
359             long n = len;
360             long m = suffix.len;
361             if (n < m) return false;
362             for (long i = 0; i < m; ++i;)
363             {
364                 if (chars[i + n - m] != suffix[i]) return false;
365             }
366             return true;
367         }
368         public List<String<CharT>> Split(CharT c)
369         {
370             List<String<CharT>> result;
371             long start = 0;
372             for (long i = 0; i < len; ++i;)
373             {
374                 if (chars[i] == c)
375                 {
376                     result.Add(Substring(starti - start));
377                     start = i + 1;
378                 }
379             }
380             if (start < len)
381             {
382                 result.Add(Substring(start));
383             }
384             return result;
385         }
386         public List<String<CharT>> Split(const String<CharT>& s)
387         {
388             List<String<CharT>> result;
389             long start = 0;
390             while (start < len)
391             {
392                 long end = Find(sstart);
393                 if (end != -1)
394                 {
395                     result.Add(Substring(startend));
396                     start = end + s.len;
397                 }
398                 else
399                 {
400                     result.Add(Substring(start));
401                     start = len;
402                 }
403             }
404             return result;
405         }
406         public inline nothrow long Find(CharT x) const
407         {
408             return Find(x0);
409         }
410         public inline long Find(CharT xlong start) const
411         {
412             if (start < 0)
413             {
414                 ThrowInvalidParameterException();
415             }
416             for (long i = start; i < len; ++i;)
417             {
418                 if (chars[i] == x)
419                 {
420                     return i;
421                 }
422             }
423             return -1;
424         }
425         public inline nothrow long RFind(CharT x) const
426         {
427             return RFind(xlen - 1);
428         }
429         public inline long RFind(CharT xlong start) const
430         {
431             if (start >= len)
432             {
433                 ThrowInvalidParameterException();
434             }
435             for (long i = start; i >= 0; --i;)
436             {
437                 if (chars[i] == x)
438                 {
439                     return i;
440                 }
441             }
442             return -1;
443         }
444         public inline nothrow long Find(const String<CharT>& s) const
445         {
446             return Find(s0);
447         }
448         public long Find(const String<CharT>& slong start) const
449         {
450             if (start < 0)
451             {
452                 ThrowInvalidParameterException();
453             }
454             if (s.IsEmpty()) return start;
455             long n = s.Length();
456             CharT x = s[0];
457             long i = Find(xstart);
458             while (i != -1)
459             {
460                 if (len < i + n) return -1;
461                 bool found = true;
462                 for (long k = 1; k < n; ++k;)
463                 {
464                     if (chars[i + k] != s[k])
465                     {
466                         found = false;
467                         break;
468                     }
469                 }
470                 if (found)
471                 {
472                     return i;
473                 }
474                 i = Find(xi + 1);
475             }
476             return -1;
477         }
478         public nothrow long RFind(const String<CharT>& s) const
479         {
480             return RFind(slen - 1);
481         }
482         public long RFind(const String<CharT>& slong start) const
483         {
484             if (start >= len)
485             {
486                 ThrowInvalidParameterException();
487             }
488             if (s.IsEmpty()) return start;
489             long n = s.Length();
490             CharT x = s[0];
491             long i = RFind(xstart);
492             while (i != -1)
493             {
494                 if (len >= i + n)
495                 {
496                     bool found = true;
497                     for (long k = 1; k < n; ++k;)
498                     {
499                         if (chars[i + k] != s[k])
500                         {
501                             found = false;
502                             break;
503                         }
504                     }
505                     if (found)
506                     {
507                         return i;
508                     }
509                 }
510                 i = RFind(xi - 1);
511             }
512             return -1;
513         }
514         private nothrow void AppendFrom(const CharT* thatlong thatLen)
515         {
516             long newLen = len + thatLen;
517             if (newLen > 0)
518             {
519                 Reserve(newLen);
520                 newLen = len + StrCopy(chars + lenthatthatLen);
521             }
522             len = newLen;
523         }
524         private nothrow void Grow(long minRes)
525         {
526             minRes = MemGrow(minRes);
527         #if (MEM_DEBUG)
528             CharT* newChars = cast<CharT*>(MemAlloc(sizeof(CharT) * minRestypename(*this)));
529         #else
530             CharT* newChars = cast<CharT*>(MemAlloc(sizeof(CharT) * minRes));
531         #endif
532             if (chars != null)
533             {
534                 StrCopy(newCharschars);
535                 MemFree(chars);
536             }
537             chars = newChars;
538             res = minRes;
539         }
540         private nothrow void Deallocate()
541         {
542             len = 0;
543             if (res != 0)
544             {
545                 MemFree(chars);
546                 res = 0;
547             }
548             chars = null;
549         }
550         private long len;
551         private long res;
552         private CharT* chars;
553     }
554 
555     public nothrow bool operator==<CharT>(const String<CharT>& leftconst String<CharT>& right)
556     {
557         long len = left.Length();
558         if (len != right.Length()) return false;
559         for (long i = 0; i < len; ++i;)
560         {
561             if (left[i] != right[i])
562             {
563                 return false;
564             }
565         }
566         return true;
567     }
568 
569     public nothrow bool operator<<CharT>(const String<CharT>& leftconst String<CharT>& right)
570     {
571         long leftLen = left.Length();
572         long rightLen = right.Length();
573         if (leftLen == 0 && rightLen > 0) return true;
574         if (leftLen > 0 && rightLen == 0) return false;
575         long n = Min(leftLenrightLen);
576         for (long i = 0; i < n; ++i;)
577         {
578             CharT l = left[i];
579             CharT r = right[i];
580             if (l < r) return true;
581             if (l > r) return false;
582         }
583         if (leftLen < rightLen) return true;
584         return false;
585     }
586 
587     public nothrow String<CharT> operator+<CharT>(const String<CharT>& firstconst String<CharT>& second)
588     {
589         String<CharT> temp(first);
590         temp.Append(second);
591         return temp;
592     }
593 
594     public nothrow String<CharT> operator+<CharT>(const String<CharT>& firstconst CharT* second)
595     {
596         String<CharT> temp(first);
597         temp.Append(second);
598         return temp;
599     }
600 
601     public nothrow String<CharT> operator+<CharT>(const CharT* firstconst String<CharT>& second)
602     {
603         String<CharT> temp(first);
604         temp.Append(second);
605         return temp;
606     }
607 
608     public bool LastComponentsEqual<CharT>(const String<CharT>& s0const String<CharT>& s1CharT componentSeparator)
609     {
610         List<String<CharT>> c0 = s0.Split(componentSeparator);
611         List<String<CharT>> c1 = s1.Split(componentSeparator);
612         long n0 = c0.Count();
613         long n1 = c1.Count();
614         long n = Min(n0n1);
615         for (long i = 0; i < n; ++i;)
616         {
617             if (c0[n0 - i - 1] != c1[n1 - i - 1]) return false;
618         }
619         return true;
620     }
621 
622     public ustring ToLower(const ustring& s)
623     {
624         ustring lower;
625         for (uchar c : s)
626         {
627             lower.Append(ToLower(c));
628         }
629         return lower;
630     }
631 
632     public ustring ToUpper(const ustring& s)
633     {
634         ustring upper;
635         for (uchar c : s)
636         {
637             upper.Append(ToUpper(c));
638         }
639         return upper;
640     }
641 
642     public wstring ToLower(const wstring& s)
643     {
644         return ToUtf16(ToLower(ToUtf32(s)));
645     }
646 
647     public wstring ToUpper(const wstring& s)
648     {
649         return ToUtf16(ToUpper(ToUtf32(s)));
650     }
651 
652     public string ToLower(const string& s)
653     {
654         return ToUtf8(ToLower(ToUtf32(s)));
655     }
656 
657     public string ToUpper(const string& s)
658     {
659         return ToUtf8(ToUpper(ToUtf32(s)));
660     }
661 }