1#pragma once
2
3#include <algorithm>
4#include <type_traits>
5
6#include <prim/seadMemUtil.h>
7#ifndef SEAD_PRIM_SAFE_STRING_H_
8#include <prim/seadSafeString.h>
9#endif
10
11namespace sead
12{
13template <typename T>
14inline typename SafeStringBase<T>::token_iterator& SafeStringBase<T>::token_iterator::operator++()
15{
16 s32 index = this->mIndex;
17 const s32 length = this->mString->calcLength();
18 if (0 <= index && index <= length)
19 {
20 while (true)
21 {
22 SEAD_ASSERT(0 <= index && index <= length);
23 if (this->mString->unsafeAt_(index) == cNullChar)
24 break;
25 if (mDelimiter.include(this->mString->unsafeAt_(index)))
26 break;
27 ++index;
28 }
29
30 this->mIndex = index + 1;
31 }
32 else
33 {
34 SEAD_ASSERT_MSG(false, "index(%d) out of range [0, %d].\n", index, length);
35 }
36 return *this;
37}
38
39template <typename T>
40inline typename SafeStringBase<T>::token_iterator& SafeStringBase<T>::token_iterator::operator--()
41{
42 s32 index = this->mIndex;
43 const s32 length = this->mString->calcLength();
44
45 if (index == 0)
46 return *this;
47
48 if (index == 1)
49 {
50 this->mIndex = 0;
51 return *this;
52 }
53
54 if (0 <= index && index <= length + 1)
55 {
56 index -= 2;
57 s32 j;
58 while (true)
59 {
60 j = index;
61 SEAD_ASSERT(0 <= index && index <= length);
62 if (this->mString->unsafeAt_(index) == cNullChar)
63 break;
64 if (mDelimiter.include(this->mString->unsafeAt_(index)))
65 break;
66 --index;
67 if (j == 0)
68 {
69 j = index;
70 break;
71 }
72 }
73
74 this->mIndex = j + 1;
75 }
76 else
77 {
78 SEAD_ASSERT_MSG(false, "index(%d) out of range [0, %d].\n", index, length + 1);
79 }
80 return *this;
81}
82
83template <typename T>
84inline s32 SafeStringBase<T>::token_iterator::get(BufferedSafeStringBase<T>* out) const
85{
86 token_iterator it = *this;
87 ++it;
88 const s32 part_length = it.getIndex() - this->getIndex() - 1;
89
90 const SafeStringBase<T> part = this->mString->getPart(*this);
91 return out->copy(part, part_length);
92}
93
94template <typename T>
95inline s32 SafeStringBase<T>::token_iterator::getAndForward(BufferedSafeStringBase<T>* out)
96{
97 s32 index = this->mIndex;
98 const s32 length = this->mString->calcLength();
99 if (index < 0 || index > length)
100 {
101 SEAD_ASSERT_MSG(false, "index(%d) out of range [0, %d].\n", index, length);
102 return 0;
103 }
104
105 T* outc = out->getBuffer();
106 const s32 out_max_length = out->getBufferSize() - 1;
107
108 s32 i = 0;
109 while (true)
110 {
111 SEAD_ASSERT(0 <= index && index <= length);
112 if (out_max_length < i)
113 {
114 SEAD_ASSERT_MSG(false, "token str exceeds out buffer length[%d]", out_max_length);
115 return 0;
116 }
117
118 const T& c = this->mString->unsafeAt_(index);
119 if (c == cNullChar || mDelimiter.include(c))
120 break;
121
122 outc[i] = c;
123 ++i;
124 ++index;
125 }
126
127 outc[i] = cNullChar;
128 this->mIndex = index + 1;
129 return i;
130}
131
132template <typename T>
133inline s32 SafeStringBase<T>::token_iterator::cutOffGet(BufferedSafeStringBase<T>* out) const
134{
135 token_iterator it = *this;
136 ++it;
137 const s32 part_length = it.getIndex() - this->getIndex() - 1;
138
139 const SafeStringBase<T> part = this->mString->getPart(this->getIndex());
140 return out->cutOffCopy(part, part_length);
141}
142
143template <typename T>
144inline s32 SafeStringBase<T>::token_iterator::cutOffGetAndForward(BufferedSafeStringBase<T>* out)
145{
146 s32 index = this->mIndex;
147 const s32 length = this->mString->calcLength();
148 if (index < 0 || index > length)
149 {
150 SEAD_ASSERT_MSG(false, "index(%d) out of range [0, %d].\n", index, length);
151 return 0;
152 }
153
154 T* outc = out->getBuffer();
155 const s32 out_max_length = out->getBufferSize() - 1;
156
157 s32 i = 0;
158 while (true)
159 {
160 SEAD_ASSERT(0 <= index && index <= length);
161
162 const T& c = this->mString->unsafeAt_(index);
163 if (c == cNullChar || mDelimiter.include(c))
164 break;
165
166 if (i < out_max_length)
167 outc[i++] = c;
168 ++index;
169 }
170
171 SEAD_ASSERT(i <= out_max_length);
172 outc[i] = cNullChar;
173 this->mIndex = index + 1;
174 return i;
175}
176
177template <typename T>
178inline const T& SafeStringBase<T>::at(s32 idx) const
179{
180 const int length = calcLength();
181 if (idx < 0 || idx > length)
182 {
183 SEAD_ASSERT_MSG(false, "index(%d) out of range[0, %d]", idx, length);
184 return cNullChar;
185 }
186 return mStringTop[idx];
187}
188
189template <typename T>
190inline SafeStringBase<T> SafeStringBase<T>::getPart(s32 at) const
191{
192 s32 len = calcLength();
193 if (at < 0 || at > len)
194 {
195 SEAD_ASSERT_MSG(false, "index(%d) out of range[0, %d]", at, len);
196 return SafeStringBase<T>::cEmptyString;
197 }
198
199 return SafeStringBase<T>(mStringTop + at);
200}
201
202template <typename T>
203inline SafeStringBase<T> SafeStringBase<T>::getPart(const SafeStringBase::iterator& it) const
204{
205 return getPart(it.getIndex());
206}
207
208template <typename T>
209inline SafeStringBase<T> SafeStringBase<T>::getPart(const SafeStringBase::token_iterator& it) const
210{
211 return getPart(it.getIndex());
212}
213
214template <typename T>
215inline s32 SafeStringBase<T>::calcLength() const
216{
217 SEAD_ASSERT(mStringTop);
218 assureTerminationImpl_();
219 s32 length = 0;
220
221 for (;;)
222 {
223 if (length > cMaximumLength || mStringTop[length] == cNullChar)
224 break;
225
226 length++;
227 }
228
229 if (length > cMaximumLength)
230 {
231 SEAD_ASSERT_MSG(false, "too long string");
232 return 0;
233 }
234
235 return length;
236}
237
238template <typename T>
239inline bool SafeStringBase<T>::include(const T& c) const
240{
241 assureTerminationImpl_();
242 for (s32 i = 0; i <= cMaximumLength; ++i)
243 {
244 if (unsafeAt_(idx: i) == cNullChar)
245 break;
246 if (unsafeAt_(idx: i) == c)
247 return true;
248 }
249 return false;
250}
251
252template <typename T>
253inline bool SafeStringBase<T>::include(const SafeStringBase<T>& str) const
254{
255 return findIndex(str) != -1;
256}
257
258template <typename T>
259inline bool SafeStringBase<T>::isEqual(const SafeStringBase<T>& str) const
260{
261 assureTerminationImpl_();
262 if (cstr() == str.cstr())
263 return true;
264
265 for (s32 i = 0; i <= cMaximumLength; i++)
266 {
267 if (unsafeAt_(idx: i) != str.unsafeAt_(idx: i))
268 return false;
269
270 if (unsafeAt_(idx: i) == cNullChar)
271 return true;
272 }
273
274 SEAD_ASSERT_MSG(false, "too long string\n");
275 return false;
276}
277
278template <typename T>
279inline s32 SafeStringBase<T>::comparen(const SafeStringBase<T>& str, s32 n) const
280{
281 assureTerminationImpl_();
282 const char* top = cstr();
283 if (top == str.cstr())
284 return 0;
285
286 if (n > cMaximumLength)
287 {
288 SEAD_ASSERT_MSG(false, "paramater(%d) out of bounds [0, %d]", n, cMaximumLength);
289 n = cMaximumLength;
290 }
291
292 for (s32 i = 0; i < n; ++i)
293 {
294 const s32 cmp = unsafeAt_(idx: i) - str.unsafeAt_(idx: i);
295
296 if (cmp != 0)
297 return cmp < 0 ? -1 : 1;
298
299 if (unsafeAt_(idx: i) == cNullChar)
300 return 0;
301 }
302
303 return 0;
304}
305
306template <typename T>
307inline s32 SafeStringBase<T>::findIndex(const SafeStringBase<T>& str) const
308{
309 const s32 len = calcLength();
310 const s32 sub_str_len = str.calcLength();
311
312 for (s32 i = 0; i <= len - sub_str_len; ++i)
313 {
314 if (SafeStringBase<T>(&mStringTop[i]).comparen(str, n: sub_str_len) == 0)
315 return i;
316 }
317 return -1;
318}
319
320template <typename T>
321inline s32 SafeStringBase<T>::findIndex(const SafeStringBase<T>& str, s32 start_pos) const
322{
323 const s32 len = calcLength();
324
325 if (start_pos < 0 || start_pos > len)
326 {
327 SEAD_ASSERT_MSG(false, "start_pos(%d) out of range[0, %d]", start_pos, len);
328 return -1;
329 }
330
331 const s32 sub_str_len = str.calcLength();
332
333 for (s32 i = start_pos; i <= len - sub_str_len; ++i)
334 {
335 if (SafeStringBase<T>(&mStringTop[i]).comparen(str, n: sub_str_len) == 0)
336 return i;
337 }
338 return -1;
339}
340
341template <typename T>
342inline s32 SafeStringBase<T>::rfindIndex(const SafeStringBase<T>& str) const
343{
344 const s32 len = calcLength();
345 const s32 sub_str_len = str.calcLength();
346
347 for (s32 i = len - sub_str_len; i >= 0; --i)
348 {
349 if (SafeStringBase<T>(&mStringTop[i]).comparen(str, n: sub_str_len) == 0)
350 return i;
351 }
352 return -1;
353}
354
355template <typename T>
356inline bool SafeStringBase<T>::isEmpty() const
357{
358 return unsafeAt_(idx: 0) == cNullChar;
359}
360
361template <typename T>
362inline bool SafeStringBase<T>::startsWith(const SafeStringBase<T>& prefix) const
363{
364 const T* strc = mStringTop;
365 const T* prefixc = prefix.mStringTop;
366 s32 i = 0;
367 while (prefixc[i] != cNullChar)
368 {
369 if (strc[i] != prefixc[i])
370 return false;
371 ++i;
372 }
373 return true;
374}
375
376template <typename T>
377inline bool SafeStringBase<T>::endsWith(const SafeStringBase<T>& suffix) const
378{
379 const s32 sub_str_len = suffix.calcLength();
380 if (sub_str_len == 0)
381 return true;
382
383 const T* strc = mStringTop;
384 const T* suffixc = suffix.mStringTop;
385
386 const s32 len = calcLength();
387 if (len < sub_str_len)
388 return false;
389
390 for (s32 i = 0; i < sub_str_len; ++i)
391 {
392 if (strc[len - sub_str_len + i] != suffixc[i])
393 return false;
394 }
395 return true;
396}
397
398template <typename T>
399inline const T& BufferedSafeStringBase<T>::operator[](s32 idx) const
400{
401 if (idx >= 0 && idx < this->mBufferSize)
402 return this->mStringTop[idx];
403
404 SEAD_ASSERT_MSG(false, "index(%d) out of range[0, %d]", idx, this->mBufferSize - 1);
405 return this->cNullChar;
406}
407
408template <typename T>
409inline s32 BufferedSafeStringBase<T>::copy(const SafeStringBase<T>& src, s32 copyLength)
410{
411 T* dst = getMutableStringTop_();
412 const T* csrc = src.cstr();
413 if (dst == csrc)
414 return 0;
415
416 if (copyLength < 0)
417 copyLength = src.calcLength();
418
419 if (copyLength >= mBufferSize)
420 {
421 SEAD_ASSERT_MSG(false, "Buffer overflow. (Buffer Size: %d, Copy Size: %d)", mBufferSize,
422 copyLength);
423 copyLength = mBufferSize - 1;
424 }
425
426 MemUtil::copy(dest: dst, src: csrc, size: copyLength * sizeof(T));
427 dst[copyLength] = SafeStringBase<T>::cNullChar;
428
429 return copyLength;
430}
431
432template <typename T>
433inline s32 BufferedSafeStringBase<T>::copyAt(s32 at, const SafeStringBase<T>& src, s32 copyLength)
434{
435 T* dst = getMutableStringTop_();
436 s32 len = this->calcLength();
437
438 if (at < 0)
439 {
440 at = len + at + 1;
441 if (at < 0)
442 {
443 SEAD_ASSERT_MSG(false, "at(%d) out of range[0, %d]", at, len);
444 at = 0;
445 }
446 }
447
448 if (copyLength < 0)
449 copyLength = src.calcLength();
450
451 if (copyLength >= mBufferSize - at)
452 {
453 SEAD_ASSERT_MSG(false, "Buffer overflow. (Buffer Size: %d, At: %d, Copy Length: %d)",
454 mBufferSize, at, copyLength);
455 copyLength = mBufferSize - at - 1;
456 }
457
458 if (copyLength <= 0)
459 return 0;
460
461 MemUtil::copy(dest: dst + at, src: src.cstr(), size: copyLength * sizeof(T));
462 if (at + copyLength > len)
463 dst[at + copyLength] = SafeStringBase<T>::cNullChar;
464
465 return copyLength;
466}
467
468template <typename T>
469inline s32 BufferedSafeStringBase<T>::cutOffCopy(const SafeStringBase<T>& src, s32 copyLength)
470{
471 T* dst = getMutableStringTop_();
472 const T* csrc = src.cstr();
473 if (dst == csrc)
474 return 0;
475
476 if (copyLength < 0)
477 copyLength = src.calcLength();
478
479 if (copyLength >= mBufferSize)
480 copyLength = mBufferSize - 1;
481
482 MemUtil::copy(dest: dst, src: csrc, size: copyLength * sizeof(T));
483 dst[copyLength] = SafeStringBase<T>::cNullChar;
484
485 return copyLength;
486}
487
488template <typename T>
489inline s32 BufferedSafeStringBase<T>::cutOffCopyAt(s32 at, const SafeStringBase<T>& src,
490 s32 copyLength)
491{
492 T* dst = getMutableStringTop_();
493 s32 len = this->calcLength();
494
495 if (at < 0)
496 {
497 at = len + at + 1;
498 if (at < 0)
499 at = 0;
500 }
501
502 if (copyLength < 0)
503 copyLength = src.calcLength();
504
505 if (copyLength >= mBufferSize - at)
506 copyLength = mBufferSize - at - 1;
507
508 if (copyLength <= 0)
509 return 0;
510
511 MemUtil::copy(dest: dst + at, src: src.cstr(), size: copyLength * sizeof(T));
512 if (at + copyLength > len)
513 dst[at + copyLength] = SafeStringBase<T>::cNullChar;
514
515 return copyLength;
516}
517
518template <typename T>
519inline s32 BufferedSafeStringBase<T>::copyAtWithTerminate(s32 at, const SafeStringBase<T>& src,
520 s32 copyLength)
521{
522 T* dst = getMutableStringTop_();
523
524 if (at < 0)
525 {
526 const s32 len = this->calcLength();
527 at = len + at + 1;
528 if (at < 0)
529 {
530 SEAD_ASSERT_MSG(false, "at(%d) out of range[0, %d]", at, len);
531 at = 0;
532 }
533 }
534
535 if (copyLength < 0)
536 copyLength = src.calcLength();
537
538 if (copyLength >= mBufferSize - at)
539 {
540 SEAD_ASSERT_MSG(false, "Buffer overflow. (Buffer Size: %d, At: %d, Copy Length: %d)",
541 mBufferSize, at, copyLength);
542 copyLength = mBufferSize - at - 1;
543 }
544
545 if (copyLength <= 0)
546 return 0;
547
548 MemUtil::copy(dest: dst + at, src: src.cstr(), size: copyLength * sizeof(T));
549 dst[at + copyLength] = SafeStringBase<T>::cNullChar;
550
551 return copyLength;
552}
553
554template <typename T>
555inline s32 BufferedSafeStringBase<T>::append(const SafeStringBase<T>& str, s32 append_length)
556{
557 return copyAt(at: -1, src: str, copyLength: append_length);
558}
559
560template <typename T>
561inline s32 BufferedSafeStringBase<T>::append(T c)
562{
563 const s32 length = this->calcLength();
564
565 if (length >= getBufferSize() - 1)
566 {
567 SEAD_ASSERT_MSG(false, "Buffer overflow. (Buffer Size: %d, Length: %d)", getBufferSize(),
568 length);
569 return 0;
570 }
571
572 T* top = getMutableStringTop_();
573 top[length] = c;
574 top[length + 1] = this->cNullChar;
575 return 1;
576}
577
578template <typename T>
579inline s32 BufferedSafeStringBase<T>::append(T c, s32 num)
580{
581 if (num < 0)
582 {
583 SEAD_ASSERT_MSG(false, "append error. num < 0, num = %d", num);
584 return 0;
585 }
586
587 if (num < 1)
588 return 0;
589
590 const s32 length = this->calcLength();
591
592 if (length >= getBufferSize() - num)
593 {
594 SEAD_ASSERT_MSG(false, "Buffer overflow. (Buffer Size: %d, Length: %d, Num: %d)",
595 getBufferSize(), length, num);
596 num = getBufferSize() - length - 1;
597 }
598
599 T* top = getMutableStringTop_();
600 for (s32 i = 0; i < num; ++i)
601 top[length + i] = c;
602
603 top[num + length] = this->cNullChar;
604 return num;
605}
606
607template <typename T>
608s32 BufferedSafeStringBase<T>::prepend(const SafeStringBase<T>& str, s32 prepend_length)
609{
610 if (prepend_length == -1)
611 prepend_length = str.calcLength();
612
613 s32 length = this->calcLength();
614 T* buffer = getMutableStringTop_();
615 const s32 buffer_size = mBufferSize;
616
617 if (prepend_length >= buffer_size - length)
618 {
619 SEAD_ASSERT_MSG(false, "Buffer overflow. (Buffer Size: %d, Length: %d, Prepend Length: %d)",
620 buffer_size, length, prepend_length);
621 if (prepend_length >= buffer_size)
622 prepend_length = buffer_size - 1;
623 length = buffer_size + (-prepend_length - 1);
624 }
625
626 MemUtil::copyOverlap(dest: &buffer[prepend_length], src: buffer, size: length * sizeof(T));
627 MemUtil::copy(dest: buffer, src: str.cstr(), size: prepend_length * sizeof(T));
628 buffer[length + prepend_length] = SafeStringBase<T>::cNullChar;
629 return length + prepend_length;
630}
631
632// UNCHECKED
633template <typename T>
634inline s32 BufferedSafeStringBase<T>::chop(s32 chop_num)
635{
636 s32 length = this->calcLength();
637 T* buffer = getMutableStringTop_();
638 const auto fail = [=] {
639 SEAD_ASSERT_MSG(false, "chop_num(%d) out of range[0, %d]", chop_num, length);
640 };
641
642 if (chop_num < 0)
643 {
644 fail();
645 return 0;
646 }
647
648 if (chop_num > length)
649 {
650 fail();
651 chop_num = length;
652 }
653
654 const s32 new_length = length - chop_num;
655 buffer[new_length] = SafeStringBase<T>::cNullChar;
656 return chop_num;
657}
658
659// UNCHECKED
660template <typename T>
661inline s32 BufferedSafeStringBase<T>::chopMatchedChar(T c)
662{
663 const s32 length = this->calcLength();
664 if (length < 1)
665 return 0;
666
667 const s32 new_length = length - 1;
668 T* buffer = getMutableStringTop_();
669 if (buffer[new_length] == c)
670 {
671 buffer[new_length] = SafeStringBase<T>::cNullChar;
672 return 1;
673 }
674
675 return 0;
676}
677
678// UNCHECKED
679template <typename T>
680inline s32 BufferedSafeStringBase<T>::chopMatchedChar(const T* characters)
681{
682 const s32 length = this->calcLength();
683 if (length < 1)
684 return 0;
685
686 T* buffer = getMutableStringTop_();
687 for (const T* it = characters; *it; ++it)
688 {
689 if (buffer[length - 1] == *it)
690 {
691 buffer[length - 1] = SafeStringBase<T>::cNullChar;
692 return 1;
693 }
694 }
695
696 return 0;
697}
698
699// UNCHECKED
700template <typename T>
701inline s32 BufferedSafeStringBase<T>::chopUnprintableAsciiChar()
702{
703 const s32 length = this->calcLength();
704 if (length < 1)
705 return 0;
706
707 T* buffer = getMutableStringTop_();
708 if (buffer[length - 1] <= ' ' || buffer[length - 1] == 0x7F)
709 {
710 buffer[length - 1] = this->cNullChar;
711 return 1;
712 }
713
714 return 0;
715}
716
717// UNCHECKED
718template <typename T>
719inline s32 BufferedSafeStringBase<T>::rstrip(const T* characters)
720{
721 const s32 length = this->calcLength();
722 if (length <= 0)
723 return 0;
724
725 T* buffer = getMutableStringTop_();
726 s32 new_length = length;
727 const auto should_strip = [characters, buffer](s32 idx) {
728 for (auto it = characters; *it; ++it)
729 {
730 if (buffer[idx] == *it)
731 return true;
732 }
733 return false;
734 };
735 while (new_length >= 1 && should_strip(new_length - 1))
736 --new_length;
737
738 if (length <= new_length)
739 return 0;
740
741 buffer[new_length] = SafeStringBase<T>::cNullChar;
742 return length - new_length;
743}
744
745// UNCHECKED
746template <typename T>
747inline s32 BufferedSafeStringBase<T>::rstripUnprintableAsciiChars()
748{
749 const s32 length = this->calcLength();
750 if (length < 1)
751 return 0;
752
753 T* buffer = getMutableStringTop_();
754 s32 new_length = length;
755 while (new_length && (buffer[new_length - 1] <= 0x20 || buffer[new_length - 1] == 0x7F))
756 --new_length;
757
758 if (length <= new_length)
759 return 0;
760
761 buffer[new_length] = this->cNullChar;
762 return length - new_length;
763}
764
765template <typename T>
766inline s32 BufferedSafeStringBase<T>::trim(s32 trim_length)
767{
768 T* mutableString = getMutableStringTop_();
769
770 if (trim_length >= mBufferSize)
771 {
772 SEAD_ASSERT_MSG(false, "trim_length(%d) out of bounds. [0, %d)", trim_length, mBufferSize);
773 return this->calcLength();
774 }
775
776 if (trim_length < 0)
777 {
778 SEAD_ASSERT_MSG(false, "trim_length(%d) out of bounds. [0, %d)", trim_length, mBufferSize);
779 trim_length = 0;
780 }
781
782 mutableString[trim_length] = SafeStringBase<T>::cNullChar;
783 return trim_length;
784}
785
786template <typename T>
787inline s32 calcStrLength_(const T* str)
788{
789 s32 len = 0;
790 while (str[len])
791 ++len;
792 return len;
793}
794
795template <typename T>
796inline s32 BufferedSafeStringBase<T>::trimMatchedString(const SafeStringBase<T>& suffix)
797{
798 const s32 length = this->calcLength();
799 T* buffer = getMutableStringTop_();
800
801 const s32 suffix_length = suffix.calcLength();
802 const s32 new_length = length - suffix_length;
803
804 if (length < suffix_length)
805 return length;
806
807 if (SafeStringBase<T>(&buffer[new_length]).comparen(suffix, suffix_length) != 0)
808 return length;
809
810 buffer[new_length] = SafeStringBase<T>::cNullChar;
811 return new_length;
812}
813
814// UNCHECKED
815template <typename T>
816inline s32 BufferedSafeStringBase<T>::replaceChar(T old_char, T new_char)
817{
818 const s32 length = this->calcLength();
819 T* buffer = getMutableStringTop_();
820
821 s32 replaced_count = 0;
822 for (s32 i = 0; i < length; ++i)
823 {
824 if (buffer[i] == old_char)
825 {
826 ++replaced_count;
827 buffer[i] = new_char;
828 }
829 }
830 return replaced_count;
831}
832
833template <typename T>
834inline s32 BufferedSafeStringBase<T>::replaceCharList(const SafeStringBase<T>& old_chars,
835 const SafeStringBase<T>& new_chars)
836{
837 const s32 length = this->calcLength();
838 T* buffer = getMutableStringTop_();
839
840 s32 old_chars_len = old_chars.calcLength();
841 const s32 new_chars_len = new_chars.calcLength();
842
843 if (old_chars_len != new_chars_len)
844 {
845 // yes, this is undefined behavior for T = char16. Nintendo, fix your code
846 SEAD_ASSERT_MSG(false, "old_chars(%s).length is not equal to new_chars(%s).length.",
847 old_chars.cstr(), new_chars.cstr());
848 if (old_chars_len > new_chars_len)
849 old_chars_len = new_chars_len;
850 }
851
852 const T* old_chars_c = old_chars.cstr();
853 const T* new_chars_c = new_chars.cstr();
854
855 s32 replaced_count = 0;
856 for (s32 i = 0; i < length; ++i)
857 {
858 for (s32 character_idx = 0; character_idx < old_chars_len; ++character_idx)
859 {
860 if (buffer[i] == old_chars_c[character_idx])
861 {
862 ++replaced_count;
863 buffer[i] = new_chars_c[character_idx];
864 break;
865 }
866 }
867 }
868 return replaced_count;
869}
870
871template <typename T>
872inline s32 BufferedSafeStringBase<T>::setReplaceString(const SafeStringBase<T>& target_str,
873 const SafeStringBase<T>& old_str,
874 const SafeStringBase<T>& new_str)
875{
876 bool is_buffer_overflow = false;
877 const s32 ret =
878 replaceStringImpl_(getMutableStringTop_(), nullptr, getBufferSize(), target_str.cstr(),
879 target_str.calcLength(), old_str, new_str, &is_buffer_overflow);
880 SEAD_ASSERT_MSG(!is_buffer_overflow, "Buffer overflow! (%s : s/%s/%s/g, Buffer Size: %d )",
881 target_str.cstr(), old_str.cstr(), new_str.cstr(), getBufferSize());
882 return ret;
883}
884
885template <typename T>
886inline s32 BufferedSafeStringBase<T>::replaceString(const SafeStringBase<T>& old_str,
887 const SafeStringBase<T>& new_str)
888{
889 bool is_buffer_overflow = false;
890 const s32 ret =
891 replaceStringImpl_(getMutableStringTop_(), nullptr, getBufferSize(), this->cstr(),
892 this->calcLength(), old_str, new_str, &is_buffer_overflow);
893 SEAD_ASSERT_MSG(!is_buffer_overflow,
894 "Buffer overflow! (%s(replacing) : s/%s/%s/g, Buffer Size: %d )", this->cstr(),
895 old_str.cstr(), new_str.cstr(), getBufferSize());
896 return ret;
897}
898
899template <typename T>
900template <typename OtherType>
901inline s32 BufferedSafeStringBase<T>::convertFromOtherType_(const SafeStringBase<OtherType>& src,
902 s32 src_size)
903{
904 s32 copy_size = src.calcLength();
905
906 if (src_size != -1)
907 {
908 if (src_size < 0)
909 {
910 SEAD_ASSERT_MSG(false, "src_size(%d) out of bounds [%d,%d]", src_size, 0, copy_size);
911 copy_size = 0;
912 return copy_size;
913 }
914 if (copy_size < src_size)
915 SEAD_ASSERT_MSG(false, "src_size(%d) out of bounds [%d,%d]", src_size, 0, copy_size);
916 else
917 copy_size = src_size;
918 }
919
920 if (getBufferSize() <= copy_size)
921 {
922 SEAD_ASSERT_MSG(false, "copy_size(%d) out of bounds[%d, %d)", copy_size, 0,
923 getBufferSize());
924 copy_size = getBufferSize() - 1;
925 }
926
927 T* raw_dst = getMutableStringTop_();
928 const OtherType* raw_src = src.cstr();
929
930 for (s32 i = 0; i < copy_size; ++i)
931 raw_dst[i] = raw_src[i];
932
933 raw_dst[copy_size] = this->cNullChar;
934 return copy_size;
935}
936
937template <typename T>
938inline s32 BufferedSafeStringBase<T>::convertFromMultiByteString(const SafeStringBase<char>& str,
939 s32 str_length)
940{
941 if constexpr (std::is_same<char, T>())
942 return copy(src: str, copyLength: str_length);
943 else
944 return convertFromOtherType_(str, str_length);
945}
946
947template <typename T>
948inline s32 BufferedSafeStringBase<T>::convertFromWideCharString(const SafeStringBase<char16>& str,
949 s32 str_length)
950{
951 if constexpr (std::is_same<char16, T>())
952 return copy(src: str, copyLength: str_length);
953 else
954 return convertFromOtherType_(str, str_length);
955}
956
957} // namespace sead
958