/*---------------------------------------------------------------------------* Project: Horizon File: fnd_FixedLengthString.h Copyright (C)2009 Nintendo Co., Ltd. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. $Rev: 31045 $ *---------------------------------------------------------------------------*/ #ifndef NN_FND_FIXEDLENGTHSTRING_H_ #define NN_FND_FIXEDLENGTHSTRING_H_ #include #include #include #include namespace nn { namespace fnd { #pragma push #pragma diag_suppress 1301 #pragma diag_suppress 2530 namespace detail { template class FixedLengthStringHeader { protected: TChar* m_Data; s32 m_Length; }; template struct FixedLengthStringSize { static const size_t value = sizeof(FixedLengthStringHeader) + (TMaxLength + 1) * sizeof(TChar); }; template struct FixedLengthStringBuffer : public FixedLengthStringHeader { static const size_t Length = (TObjectSize - sizeof(FixedLengthStringHeader)) / sizeof(TChar); TChar m_Buffer[Length]; }; } /* @brief 固定長の NULL 終端文字列 演算結果などが TMaxLength を超えた場合、IsValid が false を返す。 */ template < class TChar, size_t TMaxLength, size_t TObjectSize = detail::FixedLengthStringSize::value, class = typename nn::util::enable_if<( TObjectSize >= detail::FixedLengthStringSize::value )>::type > class FixedLengthString : private detail::FixedLengthStringBuffer { typedef detail::FixedLengthStringBuffer Base; public: typedef TChar Char; static const size_t MaxLength = TMaxLength; static const size_t ObjectSize = TObjectSize; static const size_t DefaultHead = (Base::Length - MaxLength) / 2; private: using Base::m_Buffer; using Base::m_Length; using Base::m_Data; size_t GetHeadIndex() const { return m_Data - m_Buffer; } void SetInvalid() { this->m_Length = -1; this->m_Data[0] = 0; this->m_Data = 0; } void CheckValid() const { NN_TASSERT_(IsValid()); } void CheckIndex(s32 i) const { NN_UNUSED_VAR(i); NN_TASSERT_(0 <= i && i <= m_Length); } void CheckLength(s32 length) const { NN_UNUSED_VAR(length); NN_TASSERT_(0 <= length && length <= MaxLength); } static s32 CalculateLength(const Char* s, s32 maxLength) { for (s32 i = 0; i < maxLength + 1; ++i) { if (!s[i]) { return i; } } return maxLength + 1; } template void CopyFrom(const T* s, s32 i = 0) { for (; i < MaxLength + 1; ++i) { if (!*s) { SetLength(i); return; } this->m_Data[i] = *s++; } NN_TWARNING_(true, "too long string."); SetInvalid(); } void MoveData(size_t index = DefaultHead) { NN_TASSERT_(GetLength() + index <= Base::Length); std::memmove(m_Buffer + index, m_Data, GetLength() + 1); SetHead(index); } void SetHead(size_t index = DefaultHead) { this->m_Data = m_Buffer + index; } void SetLength(s32 length) { if (0 <= length && length <= MaxLength) { this->m_Length = length; this->m_Data[length] = 0; } else { NN_TWARNING_(true, "too long string."); SetInvalid(); } } public: bool IsValid() const { return m_Data != 0; } FixedLengthString() { SetHead(); SetLength(0); } template FixedLengthString(const T* s) { if (s) { SetHead(); SetLength(0); CopyFrom(s); } else { SetInvalid(); } } template FixedLengthString(const FixedLengthString& s1) { SetHead(); SetLength(0); AppendTail(s1); } s32 GetLength() const { CheckValid(); return m_Length; } FixedLengthString& Clear() { SetHead(); SetLength(0); return *this; } const Char* GetString() const { CheckValid(); return m_Data; } operator const Char*() const { return GetString(); } const Char& operator[](s32 i) const { CheckValid(); CheckIndex(i); return m_Data[i]; } Char GetChar(s32 i) const { return (*this)[i]; } template FixedLengthString& AppendHead(const FixedLengthString& s) { if (!this->IsValid() || !s.IsValid()) { this->SetInvalid(); return *this; } if (this->GetHeadIndex() < s.GetLength()) { s32 originalLength = this->GetLength(); SetLength(this->GetLength() + s.GetLength()); if (IsValid()) { std::memmove(m_Data + s.GetLength(), m_Data, sizeof(Char) * originalLength); std::memcpy(m_Data, s.GetString(), sizeof(Char) * s.GetLength()); NN_TASSERT_(m_Data[m_Length] == 0); } } else { SetHead(GetHeadIndex() - s.GetLength()); SetLength(this->GetLength() + s.GetLength()); if (IsValid()) { std::memcpy(m_Data, s.GetString(), s.GetLength() * sizeof(Char)); } } return *this; } FixedLengthString& AppendHead(const Char* s) { if (!this->IsValid()) { return *this; } s32 length = CalculateLength(s, MaxLength - this->GetLength()); if (this->GetHeadIndex() < length) { s32 originalLength = this->GetLength(); SetLength(this->GetLength() + length); if (IsValid()) { std::memmove(m_Data + length, m_Data, sizeof(Char) * originalLength); std::memcpy(m_Data, s, sizeof(Char) * length); NN_TASSERT_(m_Data[m_Length] == 0); } } else { SetHead(GetHeadIndex() - length); SetLength(this->GetLength() + length); if (IsValid()) { std::memcpy(m_Data, s, length * sizeof(Char)); } } return *this; } template FixedLengthString& AppendTail(const FixedLengthString& s) { if (!this->IsValid() || !s.IsValid()) { this->SetInvalid(); return *this; } if (GetHeadIndex() > Base::Length - MaxLength) { MoveData(); } Char* p = m_Data + this->GetLength(); SetLength(this->GetLength() + s.GetLength()); if (IsValid()) { std::memcpy(p, s.GetString(), sizeof(Char) * s.GetLength()); NN_TASSERT_(m_Data[m_Length] == 0); } return *this; } FixedLengthString& AppendTail(const Char* s) { if (!this->IsValid()) { return *this; } if (GetHeadIndex() > Base::Length - MaxLength) { MoveData(); } CopyFrom(s, this->GetLength()); return *this; } FixedLengthString& AppendHeadAsHex(bit32 x) { Char hex[9]; for (s32 i = 0; i < 8; ++i) { bit32 n = (x >> ((7 - i) * 4)) & 0xf; NN_TASSERT_(0 <= n && n < 16); hex[i] = static_cast(n < 10 ? '0' + n : 'a' + (n - 10)); } hex[8] = 0; return AppendHead(hex); } FixedLengthString& AppendTailAsHex(bit32 x) { if (!this->IsValid()) { return *this; } if (GetHeadIndex() > Base::Length - MaxLength) { MoveData(); } Char* p = m_Data + this->GetLength(); SetLength(this->GetLength() + 8); if (IsValid()) { for (s32 i = 0; i < 8; ++i) { bit32 n = (x >> ((7 - i) * 4)) & 0xf; NN_TASSERT_(0 <= n && n < 16); p[i] = static_cast(n < 10 ? '0' + n : 'a' + (n - 10)); } NN_TASSERT_(m_Data[m_Length] == 0); } return *this; } template FixedLengthString& AppendTail(const Char2* s, size_t size) { if (!this->IsValid()) { return *this; } if (GetHeadIndex() > Base::Length - MaxLength) { MoveData(); } Char* p = m_Data + this->GetLength(); SetLength(this->GetLength() + size); if (IsValid()) { for (s32 i = 0; i < size; ++i) { p[i] = s[i]; } NN_TASSERT_(m_Data[m_Length] == 0); } return *this; } template FixedLengthString& AppendTail(const Char2 s[Size]) { return AppendTail(s, Size); } FixedLengthString& EraseTail(size_t length) { if (!this->IsValid()) { return *this; } this->SetLength(this->GetLength() - length); return *this; } FixedLengthString& EraseHead(size_t length) { if (!this->IsValid()) { return *this; } this->SetHead(this->GetHeadIndex() + length); this->SetLength(this->GetLength() - length); return *this; } template FixedLengthString& operator+=(const FixedLengthString& rhs) { this->AppendTail(rhs); return *this; } template FixedLengthString& operator+=(const Char2 s[Size]) { return AppendTail(s); } FixedLengthString& operator+=(const Char* rhs) { this->AppendTail(rhs); return *this; } template friend bool operator==(const FixedLengthString& lhs, const FixedLengthString& rhs) { return lhs.GetLength() == rhs.GetLength() && std::memcmp(lhs.GetString(), rhs.GetString(), sizeof(Char) * lhs.GetLength()) == 0; } friend bool operator==(const FixedLengthString& lhs, const Char* rhs) { return std::memcmp(lhs.m_Data, rhs, sizeof(Char) * lhs.GetLength()) == 0 && rhs[lhs.GetLength()] == 0; } friend bool operator==(const Char* lhs, const FixedLengthString& rhs) { return rhs == lhs; } template friend bool operator!=(const FixedLengthString& lhs, const FixedLengthString& rhs) { return !(lhs == rhs); } friend bool operator!=(const FixedLengthString& lhs, const Char* rhs) { return !(lhs == rhs); } friend bool operator!=(const Char* lhs, const FixedLengthString& rhs) { return !(lhs == rhs); } template friend bool operator<(const FixedLengthString& lhs, const FixedLengthString& rhs) { int n = std::memcmp(lhs.GetString(), rhs.GetString(), sizeof(Char) * std::min(lhs.GetLength(), rhs.GetLength())); return n < 0 || (n == 0 && lhs.GetLength() < rhs.GetLength()); } template friend bool operator>(const FixedLengthString& lhs, const FixedLengthString& rhs) { return rhs < lhs; } template friend bool operator<=(const FixedLengthString& lhs, const FixedLengthString& rhs) { return !(lhs > rhs); } template friend bool operator>=(const FixedLengthString& lhs, const FixedLengthString& rhs) { return rhs <= lhs; } }; // 固定長文字列 // 必ずしも NULL 終端ではない // const な 値クラス template class LightFixedLengthString { public: typedef TChar Char; static const size_t MaxLength = TMaxLength; private: Char m_Buffer[MaxLength]; void CopyFrom(const Char* s) { for (size_t i = 0; i < MaxLength; ++i) { this->m_Buffer[i] = *s; if (!*s++) { return; } } NN_TWARNING_(*s, "too long string."); if (*s) { // 長すぎるので無効 this->m_Buffer[0] = 0; } } public: LightFixedLengthString() { this->m_Buffer[0] = 0; } LightFixedLengthString(const Char* s) { CopyFrom(s); } LightFixedLengthString(const Char* s, size_t size) { if (size > MaxLength) { size = MaxLength; } NN_TASSERT_(size <= MaxLength); std::memcpy(this->m_Buffer, s, size); if (size < MaxLength) { this->m_Buffer[size] = 0; } } size_t GetLength() const { size_t i = 0; for (; i < MaxLength; ++i) { if (!m_Buffer[i]) { break; } } return i; } Char operator[](size_t i) const { NN_TASSERT_(i < MaxLength); return m_Buffer[i]; } LightFixedLengthString& operator=(const Char* s) { CopyFrom(s); return *this; } }; #pragma pop }} #endif