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