#pragma once #include #include #include #ifndef SEAD_PRIM_SAFE_STRING_H_ #include #endif namespace sead { template inline typename SafeStringBase::token_iterator& SafeStringBase::token_iterator::operator++() { s32 index = this->mIndex; const s32 length = this->mString->calcLength(); if (0 <= index && index <= length) { while (true) { SEAD_ASSERT(0 <= index && index <= length); if (this->mString->unsafeAt_(index) == cNullChar) break; if (mDelimiter.include(this->mString->unsafeAt_(index))) break; ++index; } this->mIndex = index + 1; } else { SEAD_ASSERT_MSG(false, "index(%d) out of range [0, %d].\n", index, length); } return *this; } template inline typename SafeStringBase::token_iterator& SafeStringBase::token_iterator::operator--() { s32 index = this->mIndex; const s32 length = this->mString->calcLength(); if (index == 0) return *this; if (index == 1) { this->mIndex = 0; return *this; } if (0 <= index && index <= length + 1) { index -= 2; s32 j; while (true) { j = index; SEAD_ASSERT(0 <= index && index <= length); if (this->mString->unsafeAt_(index) == cNullChar) break; if (mDelimiter.include(this->mString->unsafeAt_(index))) break; --index; if (j == 0) { j = index; break; } } this->mIndex = j + 1; } else { SEAD_ASSERT_MSG(false, "index(%d) out of range [0, %d].\n", index, length + 1); } return *this; } template inline s32 SafeStringBase::token_iterator::get(BufferedSafeStringBase* out) const { token_iterator it = *this; ++it; const s32 part_length = it.getIndex() - this->getIndex() - 1; const SafeStringBase part = this->mString->getPart(*this); return out->copy(part, part_length); } template inline s32 SafeStringBase::token_iterator::getAndForward(BufferedSafeStringBase* out) { s32 index = this->mIndex; const s32 length = this->mString->calcLength(); if (index < 0 || index > length) { SEAD_ASSERT_MSG(false, "index(%d) out of range [0, %d].\n", index, length); return 0; } T* outc = out->getBuffer(); const s32 out_max_length = out->getBufferSize() - 1; s32 i = 0; while (true) { SEAD_ASSERT(0 <= index && index <= length); if (out_max_length < i) { SEAD_ASSERT_MSG(false, "token str exceeds out buffer length[%d]", out_max_length); return 0; } const T& c = this->mString->unsafeAt_(index); if (c == cNullChar || mDelimiter.include(c)) break; outc[i] = c; ++i; ++index; } outc[i] = cNullChar; this->mIndex = index + 1; return i; } template inline s32 SafeStringBase::token_iterator::cutOffGet(BufferedSafeStringBase* out) const { token_iterator it = *this; ++it; const s32 part_length = it.getIndex() - this->getIndex() - 1; const SafeStringBase part = this->mString->getPart(this->getIndex()); return out->cutOffCopy(part, part_length); } template inline s32 SafeStringBase::token_iterator::cutOffGetAndForward(BufferedSafeStringBase* out) { s32 index = this->mIndex; const s32 length = this->mString->calcLength(); if (index < 0 || index > length) { SEAD_ASSERT_MSG(false, "index(%d) out of range [0, %d].\n", index, length); return 0; } T* outc = out->getBuffer(); const s32 out_max_length = out->getBufferSize() - 1; s32 i = 0; while (true) { SEAD_ASSERT(0 <= index && index <= length); const T& c = this->mString->unsafeAt_(index); if (c == cNullChar || mDelimiter.include(c)) break; if (i < out_max_length) outc[i++] = c; ++index; } SEAD_ASSERT(i <= out_max_length); outc[i] = cNullChar; this->mIndex = index + 1; return i; } template inline const T& SafeStringBase::at(s32 idx) const { const int length = calcLength(); if (idx < 0 || idx > length) { SEAD_ASSERT_MSG(false, "index(%d) out of range[0, %d]", idx, length); return cNullChar; } return mStringTop[idx]; } template inline SafeStringBase SafeStringBase::getPart(s32 at) const { s32 len = calcLength(); if (at < 0 || at > len) { SEAD_ASSERT_MSG(false, "index(%d) out of range[0, %d]", at, len); return SafeStringBase::cEmptyString; } return SafeStringBase(mStringTop + at); } template inline SafeStringBase SafeStringBase::getPart(const SafeStringBase::iterator& it) const { return getPart(it.getIndex()); } template inline SafeStringBase SafeStringBase::getPart(const SafeStringBase::token_iterator& it) const { return getPart(it.getIndex()); } template inline s32 SafeStringBase::calcLength() const { SEAD_ASSERT(mStringTop); assureTerminationImpl_(); s32 length = 0; for (;;) { if (length > cMaximumLength || mStringTop[length] == cNullChar) break; length++; } if (length > cMaximumLength) { SEAD_ASSERT_MSG(false, "too long string"); return 0; } return length; } template inline bool SafeStringBase::include(const T& c) const { assureTerminationImpl_(); for (s32 i = 0; i <= cMaximumLength; ++i) { if (unsafeAt_(i) == cNullChar) break; if (unsafeAt_(i) == c) return true; } return false; } template inline bool SafeStringBase::include(const SafeStringBase& str) const { return findIndex(str) != -1; } template inline bool SafeStringBase::isEqual(const SafeStringBase& str) const { assureTerminationImpl_(); if (cstr() == str.cstr()) return true; for (s32 i = 0; i <= cMaximumLength; i++) { if (unsafeAt_(i) != str.unsafeAt_(i)) return false; if (unsafeAt_(i) == cNullChar) return true; } SEAD_ASSERT_MSG(false, "too long string\n"); return false; } template inline s32 SafeStringBase::comparen(const SafeStringBase& str, s32 n) const { assureTerminationImpl_(); const char* top = cstr(); if (top == str.cstr()) return 0; if (n > cMaximumLength) { SEAD_ASSERT_MSG(false, "paramater(%d) out of bounds [0, %d]", n, cMaximumLength); n = cMaximumLength; } for (s32 i = 0; i < n; ++i) { const s32 cmp = unsafeAt_(i) - str.unsafeAt_(i); if (cmp != 0) return cmp < 0 ? -1 : 1; if (unsafeAt_(i) == cNullChar) return 0; } return 0; } template inline s32 SafeStringBase::findIndex(const SafeStringBase& str) const { const s32 len = calcLength(); const s32 sub_str_len = str.calcLength(); for (s32 i = 0; i <= len - sub_str_len; ++i) { if (SafeStringBase(&mStringTop[i]).comparen(str, sub_str_len) == 0) return i; } return -1; } template inline s32 SafeStringBase::findIndex(const SafeStringBase& str, s32 start_pos) const { const s32 len = calcLength(); if (start_pos < 0 || start_pos > len) { SEAD_ASSERT_MSG(false, "start_pos(%d) out of range[0, %d]", start_pos, len); return -1; } const s32 sub_str_len = str.calcLength(); for (s32 i = start_pos; i <= len - sub_str_len; ++i) { if (SafeStringBase(&mStringTop[i]).comparen(str, sub_str_len) == 0) return i; } return -1; } template inline s32 SafeStringBase::rfindIndex(const SafeStringBase& str) const { const s32 len = calcLength(); const s32 sub_str_len = str.calcLength(); for (s32 i = len - sub_str_len; i >= 0; --i) { if (SafeStringBase(&mStringTop[i]).comparen(str, sub_str_len) == 0) return i; } return -1; } template inline bool SafeStringBase::isEmpty() const { return unsafeAt_(0) == cNullChar; } template inline bool SafeStringBase::startsWith(const SafeStringBase& prefix) const { const T* strc = mStringTop; const T* prefixc = prefix.mStringTop; s32 i = 0; while (prefixc[i] != cNullChar) { if (strc[i] != prefixc[i]) return false; ++i; } return true; } template inline bool SafeStringBase::endsWith(const SafeStringBase& suffix) const { const s32 sub_str_len = suffix.calcLength(); if (sub_str_len == 0) return true; const T* strc = mStringTop; const T* suffixc = suffix.mStringTop; const s32 len = calcLength(); if (len < sub_str_len) return false; for (s32 i = 0; i < sub_str_len; ++i) { if (strc[len - sub_str_len + i] != suffixc[i]) return false; } return true; } template inline const T& BufferedSafeStringBase::operator[](s32 idx) const { if (idx >= 0 && idx < this->mBufferSize) return this->mStringTop[idx]; SEAD_ASSERT_MSG(false, "index(%d) out of range[0, %d]", idx, this->mBufferSize - 1); return this->cNullChar; } template inline s32 BufferedSafeStringBase::copy(const SafeStringBase& src, s32 copyLength) { T* dst = getMutableStringTop_(); const T* csrc = src.cstr(); if (dst == csrc) return 0; if (copyLength < 0) copyLength = src.calcLength(); if (copyLength >= mBufferSize) { SEAD_ASSERT_MSG(false, "Buffer overflow. (Buffer Size: %d, Copy Size: %d)", mBufferSize, copyLength); copyLength = mBufferSize - 1; } MemUtil::copy(dst, csrc, copyLength * sizeof(T)); dst[copyLength] = SafeStringBase::cNullChar; return copyLength; } template inline s32 BufferedSafeStringBase::copyAt(s32 at, const SafeStringBase& src, s32 copyLength) { T* dst = getMutableStringTop_(); s32 len = this->calcLength(); if (at < 0) { at = len + at + 1; if (at < 0) { SEAD_ASSERT_MSG(false, "at(%d) out of range[0, %d]", at, len); at = 0; } } if (copyLength < 0) copyLength = src.calcLength(); if (copyLength >= mBufferSize - at) { SEAD_ASSERT_MSG(false, "Buffer overflow. (Buffer Size: %d, At: %d, Copy Length: %d)", mBufferSize, at, copyLength); copyLength = mBufferSize - at - 1; } if (copyLength <= 0) return 0; MemUtil::copy(dst + at, src.cstr(), copyLength * sizeof(T)); if (at + copyLength > len) dst[at + copyLength] = SafeStringBase::cNullChar; return copyLength; } template inline s32 BufferedSafeStringBase::cutOffCopy(const SafeStringBase& src, s32 copyLength) { T* dst = getMutableStringTop_(); const T* csrc = src.cstr(); if (dst == csrc) return 0; if (copyLength < 0) copyLength = src.calcLength(); if (copyLength >= mBufferSize) copyLength = mBufferSize - 1; MemUtil::copy(dst, csrc, copyLength * sizeof(T)); dst[copyLength] = SafeStringBase::cNullChar; return copyLength; } template inline s32 BufferedSafeStringBase::cutOffCopyAt(s32 at, const SafeStringBase& src, s32 copyLength) { T* dst = getMutableStringTop_(); s32 len = this->calcLength(); if (at < 0) { at = len + at + 1; if (at < 0) at = 0; } if (copyLength < 0) copyLength = src.calcLength(); if (copyLength >= mBufferSize - at) copyLength = mBufferSize - at - 1; if (copyLength <= 0) return 0; MemUtil::copy(dst + at, src.cstr(), copyLength * sizeof(T)); if (at + copyLength > len) dst[at + copyLength] = SafeStringBase::cNullChar; return copyLength; } template inline s32 BufferedSafeStringBase::copyAtWithTerminate(s32 at, const SafeStringBase& src, s32 copyLength) { T* dst = getMutableStringTop_(); if (at < 0) { const s32 len = this->calcLength(); at = len + at + 1; if (at < 0) { SEAD_ASSERT_MSG(false, "at(%d) out of range[0, %d]", at, len); at = 0; } } if (copyLength < 0) copyLength = src.calcLength(); if (copyLength >= mBufferSize - at) { SEAD_ASSERT_MSG(false, "Buffer overflow. (Buffer Size: %d, At: %d, Copy Length: %d)", mBufferSize, at, copyLength); copyLength = mBufferSize - at - 1; } if (copyLength <= 0) return 0; MemUtil::copy(dst + at, src.cstr(), copyLength * sizeof(T)); dst[at + copyLength] = SafeStringBase::cNullChar; return copyLength; } template inline s32 BufferedSafeStringBase::append(const SafeStringBase& str, s32 append_length) { return copyAt(-1, str, append_length); } template inline s32 BufferedSafeStringBase::append(T c, s32 num) { if (num < 0) { SEAD_ASSERT_MSG(false, "append error. num < 0, num = %d", num); return 0; } if (num < 1) return 0; const s32 length = this->calcLength(); if (getBufferSize() - length <= num) { SEAD_ASSERT_MSG(false, "Buffer overflow. (Buffer Size: %d, Length: %d, Num: %d)", getBufferSize(), length, num); num = getBufferSize() - length - 1; } T* top = getMutableStringTop_(); for (s32 i = 0; i < num; ++i) top[length + i] = c; top[num + length] = this->cNullChar; return num; } template s32 BufferedSafeStringBase::prepend(const SafeStringBase& str, s32 prepend_length) { if (prepend_length == -1) prepend_length = str.calcLength(); s32 length = this->calcLength(); T* buffer = getMutableStringTop_(); const s32 buffer_size = mBufferSize; if (prepend_length >= buffer_size - length) { SEAD_ASSERT_MSG(false, "Buffer overflow. (Buffer Size: %d, Length: %d, Prepend Length: %d)", buffer_size, length, prepend_length); if (prepend_length >= buffer_size) prepend_length = buffer_size - 1; length = buffer_size + (-prepend_length - 1); } MemUtil::copyOverlap(&buffer[prepend_length], buffer, length * sizeof(T)); MemUtil::copy(buffer, str.cstr(), prepend_length * sizeof(T)); buffer[length + prepend_length] = SafeStringBase::cNullChar; return length + prepend_length; } // UNCHECKED template inline s32 BufferedSafeStringBase::chop(s32 chop_num) { s32 length = this->calcLength(); T* buffer = getMutableStringTop_(); const auto fail = [=] { SEAD_ASSERT_MSG(false, "chop_num(%d) out of range[0, %d]", chop_num, length); }; if (chop_num < 0) { fail(); return 0; } if (chop_num > length) { fail(); chop_num = length; } const s32 new_length = length - chop_num; buffer[new_length] = SafeStringBase::cNullChar; return chop_num; } // UNCHECKED template inline s32 BufferedSafeStringBase::chopMatchedChar(T c) { const s32 length = this->calcLength(); if (length < 1) return 0; const s32 new_length = length - 1; T* buffer = getMutableStringTop_(); if (buffer[new_length] == c) { buffer[new_length] = SafeStringBase::cNullChar; return 1; } return 0; } // UNCHECKED template inline s32 BufferedSafeStringBase::chopMatchedChar(const T* characters) { const s32 length = this->calcLength(); if (length < 1) return 0; T* buffer = getMutableStringTop_(); for (const T* it = characters; *it; ++it) { if (buffer[length - 1] == *it) { buffer[length - 1] = SafeStringBase::cNullChar; return 1; } } return 0; } // UNCHECKED template inline s32 BufferedSafeStringBase::chopUnprintableAsciiChar() { const s32 length = this->calcLength(); if (length < 1) return 0; T* buffer = getMutableStringTop_(); if (buffer[length - 1] <= ' ' || buffer[length - 1] == 0x7F) { buffer[length - 1] = this->cNullChar; return 1; } return 0; } // UNCHECKED template inline s32 BufferedSafeStringBase::rstrip(const T* characters) { const s32 length = this->calcLength(); if (length <= 0) return 0; T* buffer = getMutableStringTop_(); s32 new_length = length; const auto should_strip = [characters, buffer](s32 idx) { for (auto it = characters; *it; ++it) { if (buffer[idx] == *it) return true; } return false; }; while (new_length >= 1 && should_strip(new_length - 1)) --new_length; if (length <= new_length) return 0; buffer[new_length] = SafeStringBase::cNullChar; return length - new_length; } // UNCHECKED template inline s32 BufferedSafeStringBase::rstripUnprintableAsciiChars() { const s32 length = this->calcLength(); if (length < 1) return 0; T* buffer = getMutableStringTop_(); s32 new_length = length; while (new_length && (buffer[new_length - 1] <= 0x20 || buffer[new_length - 1] == 0x7F)) --new_length; if (length <= new_length) return 0; buffer[new_length] = this->cNullChar; return length - new_length; } template inline s32 BufferedSafeStringBase::trim(s32 trim_length) { T* mutableString = getMutableStringTop_(); if (trim_length >= mBufferSize) { SEAD_ASSERT_MSG(false, "trim_length(%d) out of bounds. [0, %d)", trim_length, mBufferSize); return this->calcLength(); } if (trim_length < 0) { SEAD_ASSERT_MSG(false, "trim_length(%d) out of bounds. [0, %d)", trim_length, mBufferSize); trim_length = 0; } mutableString[trim_length] = SafeStringBase::cNullChar; return trim_length; } template inline s32 calcStrLength_(const T* str) { s32 len = 0; while (str[len]) ++len; return len; } template inline s32 BufferedSafeStringBase::trimMatchedString(const SafeStringBase& suffix) { const s32 length = this->calcLength(); T* buffer = getMutableStringTop_(); const s32 suffix_length = suffix.calcLength(); const s32 new_length = length - suffix_length; if (length < suffix_length) return length; if (SafeStringBase(&buffer[new_length]).comparen(suffix, suffix_length) != 0) return length; buffer[new_length] = SafeStringBase::cNullChar; return new_length; } // UNCHECKED template inline s32 BufferedSafeStringBase::replaceChar(T old_char, T new_char) { const s32 length = this->calcLength(); T* buffer = getMutableStringTop_(); s32 replaced_count = 0; for (s32 i = 0; i < length; ++i) { if (buffer[i] == old_char) { ++replaced_count; buffer[i] = new_char; } } return replaced_count; } template inline s32 BufferedSafeStringBase::replaceCharList(const SafeStringBase& old_chars, const SafeStringBase& new_chars) { const s32 length = this->calcLength(); T* buffer = getMutableStringTop_(); s32 old_chars_len = old_chars.calcLength(); const s32 new_chars_len = new_chars.calcLength(); if (old_chars_len != new_chars_len) { // yes, this is undefined behavior for T = char16. Nintendo, fix your code SEAD_ASSERT_MSG(false, "old_chars(%s).length is not equal to new_chars(%s).length.", old_chars.cstr(), new_chars.cstr()); if (old_chars_len > new_chars_len) old_chars_len = new_chars_len; } const T* old_chars_c = old_chars.cstr(); const T* new_chars_c = new_chars.cstr(); s32 replaced_count = 0; for (s32 i = 0; i < length; ++i) { for (s32 character_idx = 0; character_idx < old_chars_len; ++character_idx) { if (buffer[i] == old_chars_c[character_idx]) { ++replaced_count; buffer[i] = new_chars_c[character_idx]; break; } } } return replaced_count; } template inline s32 BufferedSafeStringBase::setReplaceString(const SafeStringBase& target_str, const SafeStringBase& old_str, const SafeStringBase& new_str) { bool is_buffer_overflow = false; const s32 ret = replaceStringImpl_(getMutableStringTop_(), nullptr, getBufferSize(), target_str.cstr(), target_str.calcLength(), old_str, new_str, &is_buffer_overflow); SEAD_ASSERT_MSG(!is_buffer_overflow, "Buffer overflow! (%s : s/%s/%s/g, Buffer Size: %d )", target_str.cstr(), old_str.cstr(), new_str.cstr(), getBufferSize()); return ret; } template inline s32 BufferedSafeStringBase::replaceString(const SafeStringBase& old_str, const SafeStringBase& new_str) { bool is_buffer_overflow = false; const s32 ret = replaceStringImpl_(getMutableStringTop_(), nullptr, getBufferSize(), this->cstr(), this->calcLength(), old_str, new_str, &is_buffer_overflow); SEAD_ASSERT_MSG(!is_buffer_overflow, "Buffer overflow! (%s(replacing) : s/%s/%s/g, Buffer Size: %d )", this->cstr(), old_str.cstr(), new_str.cstr(), getBufferSize()); return ret; } template template inline s32 BufferedSafeStringBase::convertFromOtherType_(const SafeStringBase& src, s32 src_size) { s32 copy_size = src.calcLength(); if (src_size != -1) { if (src_size < 0) { SEAD_ASSERT_MSG(false, "src_size(%d) out of bounds [%d,%d]", src_size, 0, copy_size); copy_size = 0; return copy_size; } if (copy_size < src_size) SEAD_ASSERT_MSG(false, "src_size(%d) out of bounds [%d,%d]", src_size, 0, copy_size); else copy_size = src_size; } if (getBufferSize() <= copy_size) { SEAD_ASSERT_MSG(false, "copy_size(%d) out of bounds[%d, %d)", copy_size, 0, getBufferSize()); copy_size = getBufferSize() - 1; } T* raw_dst = getMutableStringTop_(); const OtherType* raw_src = src.cstr(); for (s32 i = 0; i < copy_size; ++i) raw_dst[i] = raw_src[i]; raw_dst[copy_size] = this->cNullChar; return copy_size; } template inline s32 BufferedSafeStringBase::convertFromMultiByteString(const SafeStringBase& str, s32 str_length) { if constexpr (std::is_same()) return copy(str, str_length); else return convertFromOtherType_(str, str_length); } template inline s32 BufferedSafeStringBase::convertFromWideCharString(const SafeStringBase& str, s32 str_length) { if constexpr (std::is_same()) return copy(str, str_length); else return convertFromOtherType_(str, str_length); } } // namespace sead