1 /*---------------------------------------------------------------------------*
2   Project:  Horizon
3   File:     fnd_FixedLengthString.h
4 
5   Copyright (C)2009 Nintendo Co., Ltd.  All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain
8   proprietary information of Nintendo of America Inc. and/or Nintendo
9   Company Ltd., and are protected by Federal copyright law.  They may
10   not be disclosed to third parties or copied or duplicated in any form,
11   in whole or in part, without the prior written consent of Nintendo.
12 
13   $Rev: 31045 $
14  *---------------------------------------------------------------------------*/
15 
16 #ifndef NN_FND_FIXEDLENGTHSTRING_H_
17 #define NN_FND_FIXEDLENGTHSTRING_H_
18 
19 #include <cstring>
20 #include <algorithm>
21 #include <nn/util/util_TypeTraits.h>
22 #include <nn/util/util_StaticAssert.h>
23 
24 namespace nn { namespace fnd {
25 
26 #pragma push
27 #pragma diag_suppress 1301
28 #pragma diag_suppress 2530
29 
30 namespace detail
31 {
32     template <class TChar>
33     class FixedLengthStringHeader
34     {
35     protected:
36         TChar* m_Data;
37         s32 m_Length;
38     };
39 
40     template <class TChar, size_t TMaxLength>
41     struct FixedLengthStringSize
42     {
43         static const size_t value = sizeof(FixedLengthStringHeader<TChar>) + (TMaxLength + 1) * sizeof(TChar);
44     };
45 
46     template <class TChar, size_t TObjectSize>
47     struct FixedLengthStringBuffer : public FixedLengthStringHeader<TChar>
48     {
49         static const size_t Length = (TObjectSize - sizeof(FixedLengthStringHeader<TChar>)) / sizeof(TChar);
50         TChar m_Buffer[Length];
51     };
52 }
53 
54 /*
55     @brief 固定長の NULL 終端文字列
56 
57            演算結果などが TMaxLength を超えた場合、IsValid が false を返す。
58 */
59 template <
60     class TChar,
61     size_t TMaxLength,
62     size_t TObjectSize = detail::FixedLengthStringSize<TChar, TMaxLength>::value,
63     class = typename nn::util::enable_if<(
64         TObjectSize >= detail::FixedLengthStringSize<TChar, TMaxLength>::value
65     )>::type
66 >
67 class FixedLengthString : private detail::FixedLengthStringBuffer<TChar, TObjectSize>
68 {
69     typedef detail::FixedLengthStringBuffer<TChar, TObjectSize> Base;
70 public:
71 
72     typedef TChar Char;
73     static const size_t MaxLength = TMaxLength;
74     static const size_t ObjectSize = TObjectSize;
75     static const size_t DefaultHead = (Base::Length - MaxLength) / 2;
76 
77 private:
78 
79     using Base::m_Buffer;
80     using Base::m_Length;
81     using Base::m_Data;
82 
GetHeadIndex()83     size_t GetHeadIndex() const
84     {
85         return m_Data - m_Buffer;
86     }
87 
SetInvalid()88     void SetInvalid()
89     {
90         this->m_Length = -1;
91         this->m_Data[0] = 0;
92         this->m_Data = 0;
93     }
94 
CheckValid()95     void CheckValid() const
96     {
97         NN_TASSERT_(IsValid());
98     }
99 
CheckIndex(s32 i)100     void CheckIndex(s32 i) const
101     {
102         NN_UNUSED_VAR(i);
103         NN_TASSERT_(0 <= i && i <= m_Length);
104     }
105 
CheckLength(s32 length)106     void CheckLength(s32 length) const
107     {
108         NN_UNUSED_VAR(length);
109         NN_TASSERT_(0 <= length && length <= MaxLength);
110     }
111 
CalculateLength(const Char * s,s32 maxLength)112     static s32 CalculateLength(const Char* s, s32 maxLength)
113     {
114         for (s32 i = 0; i < maxLength + 1; ++i)
115         {
116             if (!s[i])
117             {
118                 return i;
119             }
120         }
121         return maxLength + 1;
122     }
123 
124     template <class T>
125     void CopyFrom(const T* s, s32 i = 0)
126     {
127         for (; i < MaxLength + 1; ++i)
128         {
129             if (!*s)
130             {
131                 SetLength(i);
132                 return;
133             }
134             this->m_Data[i] = *s++;
135         }
136         NN_TWARNING_(true, "too long string.");
137         SetInvalid();
138     }
139 
140     void MoveData(size_t index = DefaultHead)
141     {
142         NN_TASSERT_(GetLength() + index <= Base::Length);
143         std::memmove(m_Buffer + index, m_Data, GetLength() + 1);
144         SetHead(index);
145     }
146 
147     void SetHead(size_t index = DefaultHead)
148     {
149         this->m_Data = m_Buffer + index;
150     }
151 
SetLength(s32 length)152     void SetLength(s32 length)
153     {
154         if (0 <= length && length <= MaxLength)
155         {
156             this->m_Length = length;
157             this->m_Data[length] = 0;
158         }
159         else
160         {
161             NN_TWARNING_(true, "too long string.");
162             SetInvalid();
163         }
164     }
165 
166 public:
167 
IsValid()168     bool IsValid() const
169     {
170         return m_Data != 0;
171     }
172 
FixedLengthString()173     FixedLengthString()
174     {
175         SetHead();
176         SetLength(0);
177     }
178 
179     template <class T>
FixedLengthString(const T * s)180     FixedLengthString(const T* s)
181     {
182         if (s)
183         {
184             SetHead();
185             SetLength(0);
186             CopyFrom(s);
187         }
188         else
189         {
190             SetInvalid();
191         }
192     }
193 
194     template <size_t Length, size_t Size>
FixedLengthString(const FixedLengthString<TChar,Length,Size> & s1)195     FixedLengthString(const FixedLengthString<TChar, Length, Size>& s1)
196     {
197         SetHead();
198         SetLength(0);
199         AppendTail(s1);
200     }
201 
GetLength()202     s32 GetLength() const
203     {
204         CheckValid();
205         return m_Length;
206     }
207 
Clear()208     FixedLengthString& Clear()
209     {
210         SetHead();
211         SetLength(0);
212         return *this;
213     }
214 
GetString()215     const Char* GetString() const
216     {
217         CheckValid();
218         return m_Data;
219     }
220 
221     operator const Char*() const { return GetString(); }
222 
223     const Char& operator[](s32 i) const
224     {
225         CheckValid();
226         CheckIndex(i);
227         return m_Data[i];
228     }
229 
GetChar(s32 i)230     Char GetChar(s32 i) const { return (*this)[i]; }
231 
232     template <size_t Length, size_t Size>
AppendHead(const FixedLengthString<Char,Length,Size> & s)233     FixedLengthString& AppendHead(const FixedLengthString<Char, Length, Size>& s)
234     {
235         if (!this->IsValid() || !s.IsValid())
236         {
237             this->SetInvalid();
238             return *this;
239         }
240         if (this->GetHeadIndex() < s.GetLength())
241         {
242             s32 originalLength = this->GetLength();
243             SetLength(this->GetLength() + s.GetLength());
244             if (IsValid())
245             {
246                 std::memmove(m_Data + s.GetLength(), m_Data, sizeof(Char) * originalLength);
247                 std::memcpy(m_Data, s.GetString(), sizeof(Char) * s.GetLength());
248                 NN_TASSERT_(m_Data[m_Length] == 0);
249             }
250         }
251         else
252         {
253             SetHead(GetHeadIndex() - s.GetLength());
254             SetLength(this->GetLength() + s.GetLength());
255             if (IsValid())
256             {
257                 std::memcpy(m_Data, s.GetString(), s.GetLength() * sizeof(Char));
258             }
259         }
260         return *this;
261     }
262 
AppendHead(const Char * s)263     FixedLengthString& AppendHead(const Char* s)
264     {
265         if (!this->IsValid())
266         {
267             return *this;
268         }
269         s32 length = CalculateLength(s, MaxLength - this->GetLength());
270         if (this->GetHeadIndex() < length)
271         {
272             s32 originalLength = this->GetLength();
273             SetLength(this->GetLength() + length);
274             if (IsValid())
275             {
276                 std::memmove(m_Data + length, m_Data, sizeof(Char) * originalLength);
277                 std::memcpy(m_Data, s, sizeof(Char) * length);
278                 NN_TASSERT_(m_Data[m_Length] == 0);
279             }
280         }
281         else
282         {
283             SetHead(GetHeadIndex() - length);
284             SetLength(this->GetLength() + length);
285             if (IsValid())
286             {
287                 std::memcpy(m_Data, s, length * sizeof(Char));
288             }
289         }
290         return *this;
291     }
292 
293     template <size_t Length, size_t Size>
AppendTail(const FixedLengthString<Char,Length,Size> & s)294     FixedLengthString& AppendTail(const FixedLengthString<Char, Length, Size>& s)
295     {
296         if (!this->IsValid() || !s.IsValid())
297         {
298             this->SetInvalid();
299             return *this;
300         }
301         if (GetHeadIndex() > Base::Length - MaxLength)
302         {
303             MoveData();
304         }
305         Char* p = m_Data + this->GetLength();
306         SetLength(this->GetLength() + s.GetLength());
307         if (IsValid())
308         {
309             std::memcpy(p, s.GetString(), sizeof(Char) * s.GetLength());
310             NN_TASSERT_(m_Data[m_Length] == 0);
311         }
312         return *this;
313     }
314 
AppendTail(const Char * s)315     FixedLengthString& AppendTail(const Char* s)
316     {
317         if (!this->IsValid())
318         {
319             return *this;
320         }
321         if (GetHeadIndex() > Base::Length - MaxLength)
322         {
323             MoveData();
324         }
325         CopyFrom(s, this->GetLength());
326         return *this;
327     }
328 
AppendHeadAsHex(bit32 x)329     FixedLengthString& AppendHeadAsHex(bit32 x)
330     {
331         Char hex[9];
332         for (s32 i = 0; i < 8; ++i)
333         {
334             bit32 n = (x >> ((7 - i) * 4)) & 0xf;
335             NN_TASSERT_(0 <= n && n < 16);
336             hex[i] = static_cast<Char>(n < 10 ? '0' + n : 'a' + (n - 10));
337         }
338         hex[8] = 0;
339         return AppendHead(hex);
340     }
341 
AppendTailAsHex(bit32 x)342     FixedLengthString& AppendTailAsHex(bit32 x)
343     {
344         if (!this->IsValid())
345         {
346             return *this;
347         }
348         if (GetHeadIndex() > Base::Length - MaxLength)
349         {
350             MoveData();
351         }
352         Char* p = m_Data + this->GetLength();
353         SetLength(this->GetLength() + 8);
354         if (IsValid())
355         {
356             for (s32 i = 0; i < 8; ++i)
357             {
358                 bit32 n = (x >> ((7 - i) * 4)) & 0xf;
359                 NN_TASSERT_(0 <= n && n < 16);
360                 p[i] = static_cast<Char>(n < 10 ? '0' + n : 'a' + (n - 10));
361             }
362             NN_TASSERT_(m_Data[m_Length] == 0);
363         }
364         return *this;
365     }
366 
367     template <class Char2>
AppendTail(const Char2 * s,size_t size)368     FixedLengthString& AppendTail(const Char2* s, size_t size)
369     {
370         if (!this->IsValid())
371         {
372             return *this;
373         }
374         if (GetHeadIndex() > Base::Length - MaxLength)
375         {
376             MoveData();
377         }
378         Char* p = m_Data + this->GetLength();
379         SetLength(this->GetLength() + size);
380         if (IsValid())
381         {
382             for (s32 i = 0; i < size; ++i)
383             {
384                 p[i] = s[i];
385             }
386             NN_TASSERT_(m_Data[m_Length] == 0);
387         }
388         return *this;
389     }
390 
391     template <class Char2, size_t Size>
AppendTail(const Char2 s[Size])392     FixedLengthString& AppendTail(const Char2 s[Size])
393     {
394         return AppendTail(s, Size);
395     }
396 
EraseTail(size_t length)397     FixedLengthString& EraseTail(size_t length)
398     {
399         if (!this->IsValid())
400         {
401             return *this;
402         }
403         this->SetLength(this->GetLength() - length);
404         return *this;
405     }
406 
EraseHead(size_t length)407     FixedLengthString& EraseHead(size_t length)
408     {
409         if (!this->IsValid())
410         {
411             return *this;
412         }
413         this->SetHead(this->GetHeadIndex() + length);
414         this->SetLength(this->GetLength() - length);
415         return *this;
416     }
417 
418     template <size_t Length, size_t Size>
419     FixedLengthString& operator+=(const FixedLengthString<Char, Length, Size>& rhs)
420     {
421         this->AppendTail(rhs);
422         return *this;
423     }
424 
425     template <class Char2, size_t Size>
426     FixedLengthString& operator+=(const Char2 s[Size]) { return AppendTail(s); }
427 
428     FixedLengthString& operator+=(const Char* rhs)
429     {
430         this->AppendTail(rhs);
431         return *this;
432     }
433 
434     template <size_t Length2, size_t Size2>
435     friend bool operator==(const FixedLengthString& lhs, const FixedLengthString<Char, Length2, Size2>& rhs)
436     {
437         return lhs.GetLength() == rhs.GetLength() && std::memcmp(lhs.GetString(), rhs.GetString(), sizeof(Char) * lhs.GetLength()) == 0;
438     }
439 
440     friend bool operator==(const FixedLengthString& lhs, const Char* rhs)
441     {
442         return std::memcmp(lhs.m_Data, rhs, sizeof(Char) * lhs.GetLength()) == 0 && rhs[lhs.GetLength()] == 0;
443     }
444 
445     friend bool operator==(const Char* lhs, const FixedLengthString& rhs)
446     {
447         return rhs == lhs;
448     }
449 
450     template <size_t Length2, size_t Size2>
451     friend bool operator!=(const FixedLengthString& lhs, const FixedLengthString<Char, Length2, Size2>& rhs)
452     {
453         return !(lhs == rhs);
454     }
455 
456     friend bool operator!=(const FixedLengthString& lhs, const Char* rhs)
457     {
458         return !(lhs == rhs);
459     }
460 
461     friend bool operator!=(const Char* lhs, const FixedLengthString& rhs)
462     {
463         return !(lhs == rhs);
464     }
465 
466     template <size_t Length, size_t Size>
467     friend bool operator<(const FixedLengthString& lhs, const FixedLengthString<TChar, Length, Size>& rhs)
468     {
469         int n = std::memcmp(lhs.GetString(), rhs.GetString(), sizeof(Char) * std::min(lhs.GetLength(), rhs.GetLength()));
470         return n < 0 || (n == 0 && lhs.GetLength() < rhs.GetLength());
471     }
472 
473     template <size_t Length, size_t Size>
474     friend bool operator>(const FixedLengthString& lhs, const FixedLengthString<TChar, Length, Size>& rhs)
475     {
476         return rhs < lhs;
477     }
478 
479     template <size_t Length, size_t Size>
480     friend bool operator<=(const FixedLengthString& lhs, const FixedLengthString<TChar, Length, Size>& rhs)
481     {
482         return !(lhs > rhs);
483     }
484 
485     template <size_t Length, size_t Size>
486     friend bool operator>=(const FixedLengthString& lhs, const FixedLengthString<TChar, Length, Size>& rhs)
487     {
488         return rhs <= lhs;
489     }
490 
491 };
492 
493 // 固定長文字列
494 // 必ずしも NULL 終端ではない
495 // const な 値クラス
496 template <class TChar, size_t TMaxLength>
497 class LightFixedLengthString
498 {
499 public:
500 
501     typedef TChar Char;
502     static const size_t MaxLength = TMaxLength;
503 
504 private:
505 
506     Char m_Buffer[MaxLength];
507 
CopyFrom(const Char * s)508     void CopyFrom(const Char* s)
509     {
510         for (size_t i = 0; i < MaxLength; ++i)
511         {
512             this->m_Buffer[i] = *s;
513             if (!*s++)
514             {
515                 return;
516             }
517         }
518         NN_TWARNING_(*s, "too long string.");
519         if (*s)
520         {
521             // 長すぎるので無効
522             this->m_Buffer[0] = 0;
523         }
524     }
525 
526 public:
527 
LightFixedLengthString()528     LightFixedLengthString()
529     {
530         this->m_Buffer[0] = 0;
531     }
532 
LightFixedLengthString(const Char * s)533     LightFixedLengthString(const Char* s)
534     {
535         CopyFrom(s);
536     }
537 
LightFixedLengthString(const Char * s,size_t size)538     LightFixedLengthString(const Char* s, size_t size)
539     {
540         if (size > MaxLength)
541         {
542             size = MaxLength;
543         }
544         NN_TASSERT_(size <= MaxLength);
545         std::memcpy(this->m_Buffer, s, size);
546         if (size < MaxLength)
547         {
548             this->m_Buffer[size] = 0;
549         }
550     }
551 
GetLength()552     size_t GetLength() const
553     {
554         size_t i = 0;
555         for (; i < MaxLength; ++i)
556         {
557             if (!m_Buffer[i])
558             {
559                 break;
560             }
561         }
562         return i;
563     }
564 
565     Char operator[](size_t i) const
566     {
567         NN_TASSERT_(i < MaxLength);
568         return m_Buffer[i];
569     }
570 
571     LightFixedLengthString& operator=(const Char* s)
572     {
573         CopyFrom(s);
574         return *this;
575     }
576 
577 };
578 
579 #pragma pop
580 
581 }}
582 
583 #endif
584