1 //
2 // Copyright (c) 2008-2017 the Urho3D project.
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
5 // of this software and associated documentation files (the "Software"), to deal
6 // in the Software without restriction, including without limitation the rights
7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 // copies of the Software, and to permit persons to whom the Software is
9 // furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 // THE SOFTWARE.
21 //
22 
23 #include "../Precompiled.h"
24 
25 #include "../IO/Log.h"
26 
27 #include <cstdio>
28 
29 #include "../DebugNew.h"
30 
31 #ifdef _MSC_VER
32 #pragma warning(disable:6293)
33 #endif
34 
35 namespace Urho3D
36 {
37 
38 char String::endZero = 0;
39 
40 const String String::EMPTY;
41 
String(const WString & str)42 String::String(const WString& str) :
43     length_(0),
44     capacity_(0),
45     buffer_(&endZero)
46 {
47     SetUTF8FromWChar(str.CString());
48 }
49 
String(int value)50 String::String(int value) :
51     length_(0),
52     capacity_(0),
53     buffer_(&endZero)
54 {
55     char tempBuffer[CONVERSION_BUFFER_LENGTH];
56     sprintf(tempBuffer, "%d", value);
57     *this = tempBuffer;
58 }
59 
String(short value)60 String::String(short value) :
61     length_(0),
62     capacity_(0),
63     buffer_(&endZero)
64 {
65     char tempBuffer[CONVERSION_BUFFER_LENGTH];
66     sprintf(tempBuffer, "%d", value);
67     *this = tempBuffer;
68 }
69 
String(long value)70 String::String(long value) :
71     length_(0),
72     capacity_(0),
73     buffer_(&endZero)
74 {
75     char tempBuffer[CONVERSION_BUFFER_LENGTH];
76     sprintf(tempBuffer, "%ld", value);
77     *this = tempBuffer;
78 }
79 
String(long long value)80 String::String(long long value) :
81     length_(0),
82     capacity_(0),
83     buffer_(&endZero)
84 {
85     char tempBuffer[CONVERSION_BUFFER_LENGTH];
86     sprintf(tempBuffer, "%lld", value);
87     *this = tempBuffer;
88 }
89 
String(unsigned value)90 String::String(unsigned value) :
91     length_(0),
92     capacity_(0),
93     buffer_(&endZero)
94 {
95     char tempBuffer[CONVERSION_BUFFER_LENGTH];
96     sprintf(tempBuffer, "%u", value);
97     *this = tempBuffer;
98 }
99 
String(unsigned short value)100 String::String(unsigned short value) :
101     length_(0),
102     capacity_(0),
103     buffer_(&endZero)
104 {
105     char tempBuffer[CONVERSION_BUFFER_LENGTH];
106     sprintf(tempBuffer, "%u", value);
107     *this = tempBuffer;
108 }
109 
String(unsigned long value)110 String::String(unsigned long value) :
111     length_(0),
112     capacity_(0),
113     buffer_(&endZero)
114 {
115     char tempBuffer[CONVERSION_BUFFER_LENGTH];
116     sprintf(tempBuffer, "%lu", value);
117     *this = tempBuffer;
118 }
119 
String(unsigned long long value)120 String::String(unsigned long long value) :
121     length_(0),
122     capacity_(0),
123     buffer_(&endZero)
124 {
125     char tempBuffer[CONVERSION_BUFFER_LENGTH];
126     sprintf(tempBuffer, "%llu", value);
127     *this = tempBuffer;
128 }
129 
String(float value)130 String::String(float value) :
131     length_(0),
132     capacity_(0),
133     buffer_(&endZero)
134 {
135     char tempBuffer[CONVERSION_BUFFER_LENGTH];
136     sprintf(tempBuffer, "%g", value);
137     *this = tempBuffer;
138 }
139 
String(double value)140 String::String(double value) :
141     length_(0),
142     capacity_(0),
143     buffer_(&endZero)
144 {
145     char tempBuffer[CONVERSION_BUFFER_LENGTH];
146     sprintf(tempBuffer, "%.15g", value);
147     *this = tempBuffer;
148 }
149 
String(bool value)150 String::String(bool value) :
151     length_(0),
152     capacity_(0),
153     buffer_(&endZero)
154 {
155     if (value)
156         *this = "true";
157     else
158         *this = "false";
159 }
160 
String(char value)161 String::String(char value) :
162     length_(0),
163     capacity_(0),
164     buffer_(&endZero)
165 {
166     Resize(1);
167     buffer_[0] = value;
168 }
169 
String(char value,unsigned length)170 String::String(char value, unsigned length) :
171     length_(0),
172     capacity_(0),
173     buffer_(&endZero)
174 {
175     Resize(length);
176     for (unsigned i = 0; i < length; ++i)
177         buffer_[i] = value;
178 }
179 
operator +=(int rhs)180 String& String::operator +=(int rhs)
181 {
182     return *this += String(rhs);
183 }
184 
operator +=(short rhs)185 String& String::operator +=(short rhs)
186 {
187     return *this += String(rhs);
188 }
189 
operator +=(long rhs)190 String& String::operator +=(long rhs)
191 {
192     return *this += String(rhs);
193 }
194 
operator +=(long long rhs)195 String& String::operator +=(long long rhs)
196 {
197     return *this += String(rhs);
198 }
199 
operator +=(unsigned rhs)200 String& String::operator +=(unsigned rhs)
201 {
202     return *this += String(rhs);
203 }
204 
operator +=(unsigned short rhs)205 String& String::operator +=(unsigned short rhs)
206 {
207     return *this += String(rhs);
208 }
209 
operator +=(unsigned long rhs)210 String& String::operator +=(unsigned long rhs)
211 {
212     return *this += String(rhs);
213 }
214 
operator +=(unsigned long long rhs)215 String& String::operator +=(unsigned long long rhs)
216 {
217     return *this += String(rhs);
218 }
219 
operator +=(float rhs)220 String& String::operator +=(float rhs)
221 {
222     return *this += String(rhs);
223 }
224 
operator +=(bool rhs)225 String& String::operator +=(bool rhs)
226 {
227     return *this += String(rhs);
228 }
229 
Replace(char replaceThis,char replaceWith,bool caseSensitive)230 void String::Replace(char replaceThis, char replaceWith, bool caseSensitive)
231 {
232     if (caseSensitive)
233     {
234         for (unsigned i = 0; i < length_; ++i)
235         {
236             if (buffer_[i] == replaceThis)
237                 buffer_[i] = replaceWith;
238         }
239     }
240     else
241     {
242         replaceThis = (char)tolower(replaceThis);
243         for (unsigned i = 0; i < length_; ++i)
244         {
245             if (tolower(buffer_[i]) == replaceThis)
246                 buffer_[i] = replaceWith;
247         }
248     }
249 }
250 
Replace(const String & replaceThis,const String & replaceWith,bool caseSensitive)251 void String::Replace(const String& replaceThis, const String& replaceWith, bool caseSensitive)
252 {
253     unsigned nextPos = 0;
254 
255     while (nextPos < length_)
256     {
257         unsigned pos = Find(replaceThis, nextPos, caseSensitive);
258         if (pos == NPOS)
259             break;
260         Replace(pos, replaceThis.length_, replaceWith);
261         nextPos = pos + replaceWith.length_;
262     }
263 }
264 
Replace(unsigned pos,unsigned length,const String & replaceWith)265 void String::Replace(unsigned pos, unsigned length, const String& replaceWith)
266 {
267     // If substring is illegal, do nothing
268     if (pos + length > length_)
269         return;
270 
271     Replace(pos, length, replaceWith.buffer_, replaceWith.length_);
272 }
273 
Replace(unsigned pos,unsigned length,const char * replaceWith)274 void String::Replace(unsigned pos, unsigned length, const char* replaceWith)
275 {
276     // If substring is illegal, do nothing
277     if (pos + length > length_)
278         return;
279 
280     Replace(pos, length, replaceWith, CStringLength(replaceWith));
281 }
282 
Replace(const String::Iterator & start,const String::Iterator & end,const String & replaceWith)283 String::Iterator String::Replace(const String::Iterator& start, const String::Iterator& end, const String& replaceWith)
284 {
285     unsigned pos = (unsigned)(start - Begin());
286     if (pos >= length_)
287         return End();
288     unsigned length = (unsigned)(end - start);
289     Replace(pos, length, replaceWith);
290 
291     return Begin() + pos;
292 }
293 
Replaced(char replaceThis,char replaceWith,bool caseSensitive) const294 String String::Replaced(char replaceThis, char replaceWith, bool caseSensitive) const
295 {
296     String ret(*this);
297     ret.Replace(replaceThis, replaceWith, caseSensitive);
298     return ret;
299 }
300 
Replaced(const String & replaceThis,const String & replaceWith,bool caseSensitive) const301 String String::Replaced(const String& replaceThis, const String& replaceWith, bool caseSensitive) const
302 {
303     String ret(*this);
304     ret.Replace(replaceThis, replaceWith, caseSensitive);
305     return ret;
306 }
307 
Append(const String & str)308 String& String::Append(const String& str)
309 {
310     return *this += str;
311 }
312 
Append(const char * str)313 String& String::Append(const char* str)
314 {
315     return *this += str;
316 }
317 
Append(char c)318 String& String::Append(char c)
319 {
320     return *this += c;
321 }
322 
Append(const char * str,unsigned length)323 String& String::Append(const char* str, unsigned length)
324 {
325     if (str)
326     {
327         unsigned oldLength = length_;
328         Resize(oldLength + length);
329         CopyChars(&buffer_[oldLength], str, length);
330     }
331     return *this;
332 }
333 
Insert(unsigned pos,const String & str)334 void String::Insert(unsigned pos, const String& str)
335 {
336     if (pos > length_)
337         pos = length_;
338 
339     if (pos == length_)
340         (*this) += str;
341     else
342         Replace(pos, 0, str);
343 }
344 
Insert(unsigned pos,char c)345 void String::Insert(unsigned pos, char c)
346 {
347     if (pos > length_)
348         pos = length_;
349 
350     if (pos == length_)
351         (*this) += c;
352     else
353     {
354         unsigned oldLength = length_;
355         Resize(length_ + 1);
356         MoveRange(pos + 1, pos, oldLength - pos);
357         buffer_[pos] = c;
358     }
359 }
360 
Insert(const String::Iterator & dest,const String & str)361 String::Iterator String::Insert(const String::Iterator& dest, const String& str)
362 {
363     unsigned pos = (unsigned)(dest - Begin());
364     if (pos > length_)
365         pos = length_;
366     Insert(pos, str);
367 
368     return Begin() + pos;
369 }
370 
Insert(const String::Iterator & dest,const String::Iterator & start,const String::Iterator & end)371 String::Iterator String::Insert(const String::Iterator& dest, const String::Iterator& start, const String::Iterator& end)
372 {
373     unsigned pos = (unsigned)(dest - Begin());
374     if (pos > length_)
375         pos = length_;
376     unsigned length = (unsigned)(end - start);
377     Replace(pos, 0, &(*start), length);
378 
379     return Begin() + pos;
380 }
381 
Insert(const String::Iterator & dest,char c)382 String::Iterator String::Insert(const String::Iterator& dest, char c)
383 {
384     unsigned pos = (unsigned)(dest - Begin());
385     if (pos > length_)
386         pos = length_;
387     Insert(pos, c);
388 
389     return Begin() + pos;
390 }
391 
Erase(unsigned pos,unsigned length)392 void String::Erase(unsigned pos, unsigned length)
393 {
394     Replace(pos, length, String::EMPTY);
395 }
396 
Erase(const String::Iterator & it)397 String::Iterator String::Erase(const String::Iterator& it)
398 {
399     unsigned pos = (unsigned)(it - Begin());
400     if (pos >= length_)
401         return End();
402     Erase(pos);
403 
404     return Begin() + pos;
405 }
406 
Erase(const String::Iterator & start,const String::Iterator & end)407 String::Iterator String::Erase(const String::Iterator& start, const String::Iterator& end)
408 {
409     unsigned pos = (unsigned)(start - Begin());
410     if (pos >= length_)
411         return End();
412     unsigned length = (unsigned)(end - start);
413     Erase(pos, length);
414 
415     return Begin() + pos;
416 }
417 
Resize(unsigned newLength)418 void String::Resize(unsigned newLength)
419 {
420     if (!capacity_)
421     {
422         // If zero length requested, do not allocate buffer yet
423         if (!newLength)
424             return;
425 
426         // Calculate initial capacity
427         capacity_ = newLength + 1;
428         if (capacity_ < MIN_CAPACITY)
429             capacity_ = MIN_CAPACITY;
430 
431         buffer_ = new char[capacity_];
432     }
433     else
434     {
435         if (newLength && capacity_ < newLength + 1)
436         {
437             // Increase the capacity with half each time it is exceeded
438             while (capacity_ < newLength + 1)
439                 capacity_ += (capacity_ + 1) >> 1;
440 
441             char* newBuffer = new char[capacity_];
442             // Move the existing data to the new buffer, then delete the old buffer
443             if (length_)
444                 CopyChars(newBuffer, buffer_, length_);
445             delete[] buffer_;
446 
447             buffer_ = newBuffer;
448         }
449     }
450 
451     buffer_[newLength] = 0;
452     length_ = newLength;
453 }
454 
Reserve(unsigned newCapacity)455 void String::Reserve(unsigned newCapacity)
456 {
457     if (newCapacity < length_ + 1)
458         newCapacity = length_ + 1;
459     if (newCapacity == capacity_)
460         return;
461 
462     char* newBuffer = new char[newCapacity];
463     // Move the existing data to the new buffer, then delete the old buffer
464     CopyChars(newBuffer, buffer_, length_ + 1);
465     if (capacity_)
466         delete[] buffer_;
467 
468     capacity_ = newCapacity;
469     buffer_ = newBuffer;
470 }
471 
Compact()472 void String::Compact()
473 {
474     if (capacity_)
475         Reserve(length_ + 1);
476 }
477 
Clear()478 void String::Clear()
479 {
480     Resize(0);
481 }
482 
Swap(String & str)483 void String::Swap(String& str)
484 {
485     Urho3D::Swap(length_, str.length_);
486     Urho3D::Swap(capacity_, str.capacity_);
487     Urho3D::Swap(buffer_, str.buffer_);
488 }
489 
Substring(unsigned pos) const490 String String::Substring(unsigned pos) const
491 {
492     if (pos < length_)
493     {
494         String ret;
495         ret.Resize(length_ - pos);
496         CopyChars(ret.buffer_, buffer_ + pos, ret.length_);
497 
498         return ret;
499     }
500     else
501         return String();
502 }
503 
Substring(unsigned pos,unsigned length) const504 String String::Substring(unsigned pos, unsigned length) const
505 {
506     if (pos < length_)
507     {
508         String ret;
509         if (pos + length > length_)
510             length = length_ - pos;
511         ret.Resize(length);
512         CopyChars(ret.buffer_, buffer_ + pos, ret.length_);
513 
514         return ret;
515     }
516     else
517         return String();
518 }
519 
Trimmed() const520 String String::Trimmed() const
521 {
522     unsigned trimStart = 0;
523     unsigned trimEnd = length_;
524 
525     while (trimStart < trimEnd)
526     {
527         char c = buffer_[trimStart];
528         if (c != ' ' && c != 9)
529             break;
530         ++trimStart;
531     }
532     while (trimEnd > trimStart)
533     {
534         char c = buffer_[trimEnd - 1];
535         if (c != ' ' && c != 9)
536             break;
537         --trimEnd;
538     }
539 
540     return Substring(trimStart, trimEnd - trimStart);
541 }
542 
ToLower() const543 String String::ToLower() const
544 {
545     String ret(*this);
546     for (unsigned i = 0; i < ret.length_; ++i)
547         ret[i] = (char)tolower(buffer_[i]);
548 
549     return ret;
550 }
551 
ToUpper() const552 String String::ToUpper() const
553 {
554     String ret(*this);
555     for (unsigned i = 0; i < ret.length_; ++i)
556         ret[i] = (char)toupper(buffer_[i]);
557 
558     return ret;
559 }
560 
Split(char separator,bool keepEmptyStrings) const561 Vector<String> String::Split(char separator, bool keepEmptyStrings) const
562 {
563     return Split(CString(), separator, keepEmptyStrings);
564 }
565 
Join(const Vector<String> & subStrings,const String & glue)566 void String::Join(const Vector<String>& subStrings, const String& glue)
567 {
568     *this = Joined(subStrings, glue);
569 }
570 
Find(char c,unsigned startPos,bool caseSensitive) const571 unsigned String::Find(char c, unsigned startPos, bool caseSensitive) const
572 {
573     if (caseSensitive)
574     {
575         for (unsigned i = startPos; i < length_; ++i)
576         {
577             if (buffer_[i] == c)
578                 return i;
579         }
580     }
581     else
582     {
583         c = (char)tolower(c);
584         for (unsigned i = startPos; i < length_; ++i)
585         {
586             if (tolower(buffer_[i]) == c)
587                 return i;
588         }
589     }
590 
591     return NPOS;
592 }
593 
Find(const String & str,unsigned startPos,bool caseSensitive) const594 unsigned String::Find(const String& str, unsigned startPos, bool caseSensitive) const
595 {
596     if (!str.length_ || str.length_ > length_)
597         return NPOS;
598 
599     char first = str.buffer_[0];
600     if (!caseSensitive)
601         first = (char)tolower(first);
602 
603     for (unsigned i = startPos; i <= length_ - str.length_; ++i)
604     {
605         char c = buffer_[i];
606         if (!caseSensitive)
607             c = (char)tolower(c);
608 
609         if (c == first)
610         {
611             unsigned skip = NPOS;
612             bool found = true;
613             for (unsigned j = 1; j < str.length_; ++j)
614             {
615                 c = buffer_[i + j];
616                 char d = str.buffer_[j];
617                 if (!caseSensitive)
618                 {
619                     c = (char)tolower(c);
620                     d = (char)tolower(d);
621                 }
622 
623                 if (skip == NPOS && c == first)
624                     skip = i + j - 1;
625 
626                 if (c != d)
627                 {
628                     found = false;
629                     if (skip != NPOS)
630                         i = skip;
631                     break;
632                 }
633             }
634             if (found)
635                 return i;
636         }
637     }
638 
639     return NPOS;
640 }
641 
FindLast(char c,unsigned startPos,bool caseSensitive) const642 unsigned String::FindLast(char c, unsigned startPos, bool caseSensitive) const
643 {
644     if (startPos >= length_)
645         startPos = length_ - 1;
646 
647     if (caseSensitive)
648     {
649         for (unsigned i = startPos; i < length_; --i)
650         {
651             if (buffer_[i] == c)
652                 return i;
653         }
654     }
655     else
656     {
657         c = (char)tolower(c);
658         for (unsigned i = startPos; i < length_; --i)
659         {
660             if (tolower(buffer_[i]) == c)
661                 return i;
662         }
663     }
664 
665     return NPOS;
666 }
667 
FindLast(const String & str,unsigned startPos,bool caseSensitive) const668 unsigned String::FindLast(const String& str, unsigned startPos, bool caseSensitive) const
669 {
670     if (!str.length_ || str.length_ > length_)
671         return NPOS;
672     if (startPos > length_ - str.length_)
673         startPos = length_ - str.length_;
674 
675     char first = str.buffer_[0];
676     if (!caseSensitive)
677         first = (char)tolower(first);
678 
679     for (unsigned i = startPos; i < length_; --i)
680     {
681         char c = buffer_[i];
682         if (!caseSensitive)
683             c = (char)tolower(c);
684 
685         if (c == first)
686         {
687             bool found = true;
688             for (unsigned j = 1; j < str.length_; ++j)
689             {
690                 c = buffer_[i + j];
691                 char d = str.buffer_[j];
692                 if (!caseSensitive)
693                 {
694                     c = (char)tolower(c);
695                     d = (char)tolower(d);
696                 }
697 
698                 if (c != d)
699                 {
700                     found = false;
701                     break;
702                 }
703             }
704             if (found)
705                 return i;
706         }
707     }
708 
709     return NPOS;
710 }
711 
StartsWith(const String & str,bool caseSensitive) const712 bool String::StartsWith(const String& str, bool caseSensitive) const
713 {
714     return Find(str, 0, caseSensitive) == 0;
715 }
716 
EndsWith(const String & str,bool caseSensitive) const717 bool String::EndsWith(const String& str, bool caseSensitive) const
718 {
719     unsigned pos = FindLast(str, Length() - 1, caseSensitive);
720     return pos != NPOS && pos == Length() - str.Length();
721 }
722 
Compare(const String & str,bool caseSensitive) const723 int String::Compare(const String& str, bool caseSensitive) const
724 {
725     return Compare(CString(), str.CString(), caseSensitive);
726 }
727 
Compare(const char * str,bool caseSensitive) const728 int String::Compare(const char* str, bool caseSensitive) const
729 {
730     return Compare(CString(), str, caseSensitive);
731 }
732 
SetUTF8FromLatin1(const char * str)733 void String::SetUTF8FromLatin1(const char* str)
734 {
735     char temp[7];
736 
737     Clear();
738 
739     if (!str)
740         return;
741 
742     while (*str)
743     {
744         char* dest = temp;
745         EncodeUTF8(dest, (unsigned)*str++);
746         *dest = 0;
747         Append(temp);
748     }
749 }
750 
SetUTF8FromWChar(const wchar_t * str)751 void String::SetUTF8FromWChar(const wchar_t* str)
752 {
753     char temp[7];
754 
755     Clear();
756 
757     if (!str)
758         return;
759 
760 #ifdef _WIN32
761     while (*str)
762     {
763         unsigned unicodeChar = DecodeUTF16(str);
764         char* dest = temp;
765         EncodeUTF8(dest, unicodeChar);
766         *dest = 0;
767         Append(temp);
768     }
769 #else
770     while (*str)
771     {
772         char* dest = temp;
773         EncodeUTF8(dest, (unsigned)*str++);
774         *dest = 0;
775         Append(temp);
776     }
777 #endif
778 }
779 
LengthUTF8() const780 unsigned String::LengthUTF8() const
781 {
782     unsigned ret = 0;
783 
784     const char* src = buffer_;
785     if (!src)
786         return ret;
787     const char* end = buffer_ + length_;
788 
789     while (src < end)
790     {
791         DecodeUTF8(src);
792         ++ret;
793     }
794 
795     return ret;
796 }
797 
ByteOffsetUTF8(unsigned index) const798 unsigned String::ByteOffsetUTF8(unsigned index) const
799 {
800     unsigned byteOffset = 0;
801     unsigned utfPos = 0;
802 
803     while (utfPos < index && byteOffset < length_)
804     {
805         NextUTF8Char(byteOffset);
806         ++utfPos;
807     }
808 
809     return byteOffset;
810 }
811 
NextUTF8Char(unsigned & byteOffset) const812 unsigned String::NextUTF8Char(unsigned& byteOffset) const
813 {
814     if (!buffer_)
815         return 0;
816 
817     const char* src = buffer_ + byteOffset;
818     unsigned ret = DecodeUTF8(src);
819     byteOffset = (unsigned)(src - buffer_);
820 
821     return ret;
822 }
823 
AtUTF8(unsigned index) const824 unsigned String::AtUTF8(unsigned index) const
825 {
826     unsigned byteOffset = ByteOffsetUTF8(index);
827     return NextUTF8Char(byteOffset);
828 }
829 
ReplaceUTF8(unsigned index,unsigned unicodeChar)830 void String::ReplaceUTF8(unsigned index, unsigned unicodeChar)
831 {
832     unsigned utfPos = 0;
833     unsigned byteOffset = 0;
834 
835     while (utfPos < index && byteOffset < length_)
836     {
837         NextUTF8Char(byteOffset);
838         ++utfPos;
839     }
840 
841     if (utfPos < index)
842         return;
843 
844     unsigned beginCharPos = byteOffset;
845     NextUTF8Char(byteOffset);
846 
847     char temp[7];
848     char* dest = temp;
849     EncodeUTF8(dest, unicodeChar);
850     *dest = 0;
851 
852     Replace(beginCharPos, byteOffset - beginCharPos, temp, (unsigned)(dest - temp));
853 }
854 
AppendUTF8(unsigned unicodeChar)855 String& String::AppendUTF8(unsigned unicodeChar)
856 {
857     char temp[7];
858     char* dest = temp;
859     EncodeUTF8(dest, unicodeChar);
860     *dest = 0;
861     return Append(temp);
862 }
863 
SubstringUTF8(unsigned pos) const864 String String::SubstringUTF8(unsigned pos) const
865 {
866     unsigned utf8Length = LengthUTF8();
867     unsigned byteOffset = ByteOffsetUTF8(pos);
868     String ret;
869 
870     while (pos < utf8Length)
871     {
872         ret.AppendUTF8(NextUTF8Char(byteOffset));
873         ++pos;
874     }
875 
876     return ret;
877 }
878 
SubstringUTF8(unsigned pos,unsigned length) const879 String String::SubstringUTF8(unsigned pos, unsigned length) const
880 {
881     unsigned utf8Length = LengthUTF8();
882     unsigned byteOffset = ByteOffsetUTF8(pos);
883     unsigned endPos = pos + length;
884     String ret;
885 
886     while (pos < endPos && pos < utf8Length)
887     {
888         ret.AppendUTF8(NextUTF8Char(byteOffset));
889         ++pos;
890     }
891 
892     return ret;
893 }
894 
EncodeUTF8(char * & dest,unsigned unicodeChar)895 void String::EncodeUTF8(char*& dest, unsigned unicodeChar)
896 {
897     if (unicodeChar < 0x80)
898         *dest++ = unicodeChar;
899     else if (unicodeChar < 0x800)
900     {
901         dest[0] = (char)(0xc0 | ((unicodeChar >> 6) & 0x1f));
902         dest[1] = (char)(0x80 | (unicodeChar & 0x3f));
903         dest += 2;
904     }
905     else if (unicodeChar < 0x10000)
906     {
907         dest[0] = (char)(0xe0 | ((unicodeChar >> 12) & 0xf));
908         dest[1] = (char)(0x80 | ((unicodeChar >> 6) & 0x3f));
909         dest[2] = (char)(0x80 | (unicodeChar & 0x3f));
910         dest += 3;
911     }
912     else if (unicodeChar < 0x200000)
913     {
914         dest[0] = (char)(0xf0 | ((unicodeChar >> 18) & 0x7));
915         dest[1] = (char)(0x80 | ((unicodeChar >> 12) & 0x3f));
916         dest[2] = (char)(0x80 | ((unicodeChar >> 6) & 0x3f));
917         dest[3] = (char)(0x80 | (unicodeChar & 0x3f));
918         dest += 4;
919     }
920     else if (unicodeChar < 0x4000000)
921     {
922         dest[0] = (char)(0xf8 | ((unicodeChar >> 24) & 0x3));
923         dest[1] = (char)(0x80 | ((unicodeChar >> 18) & 0x3f));
924         dest[2] = (char)(0x80 | ((unicodeChar >> 12) & 0x3f));
925         dest[3] = (char)(0x80 | ((unicodeChar >> 6) & 0x3f));
926         dest[4] = (char)(0x80 | (unicodeChar & 0x3f));
927         dest += 5;
928     }
929     else
930     {
931         dest[0] = (char)(0xfc | ((unicodeChar >> 30) & 0x1));
932         dest[1] = (char)(0x80 | ((unicodeChar >> 24) & 0x3f));
933         dest[2] = (char)(0x80 | ((unicodeChar >> 18) & 0x3f));
934         dest[3] = (char)(0x80 | ((unicodeChar >> 12) & 0x3f));
935         dest[4] = (char)(0x80 | ((unicodeChar >> 6) & 0x3f));
936         dest[5] = (char)(0x80 | (unicodeChar & 0x3f));
937         dest += 6;
938     }
939 }
940 
941 #define GET_NEXT_CONTINUATION_BYTE(ptr) *ptr; if ((unsigned char)*ptr < 0x80 || (unsigned char)*ptr >= 0xc0) return '?'; else ++ptr;
942 
DecodeUTF8(const char * & src)943 unsigned String::DecodeUTF8(const char*& src)
944 {
945     if (src == 0)
946         return 0;
947 
948     unsigned char char1 = *src++;
949 
950     // Check if we are in the middle of a UTF8 character
951     if (char1 >= 0x80 && char1 < 0xc0)
952     {
953         while ((unsigned char)*src >= 0x80 && (unsigned char)*src < 0xc0)
954             ++src;
955         return '?';
956     }
957 
958     if (char1 < 0x80)
959         return char1;
960     else if (char1 < 0xe0)
961     {
962         unsigned char char2 = GET_NEXT_CONTINUATION_BYTE(src);
963         return (unsigned)((char2 & 0x3f) | ((char1 & 0x1f) << 6));
964     }
965     else if (char1 < 0xf0)
966     {
967         unsigned char char2 = GET_NEXT_CONTINUATION_BYTE(src);
968         unsigned char char3 = GET_NEXT_CONTINUATION_BYTE(src);
969         return (unsigned)((char3 & 0x3f) | ((char2 & 0x3f) << 6) | ((char1 & 0xf) << 12));
970     }
971     else if (char1 < 0xf8)
972     {
973         unsigned char char2 = GET_NEXT_CONTINUATION_BYTE(src);
974         unsigned char char3 = GET_NEXT_CONTINUATION_BYTE(src);
975         unsigned char char4 = GET_NEXT_CONTINUATION_BYTE(src);
976         return (unsigned)((char4 & 0x3f) | ((char3 & 0x3f) << 6) | ((char2 & 0x3f) << 12) | ((char1 & 0x7) << 18));
977     }
978     else if (char1 < 0xfc)
979     {
980         unsigned char char2 = GET_NEXT_CONTINUATION_BYTE(src);
981         unsigned char char3 = GET_NEXT_CONTINUATION_BYTE(src);
982         unsigned char char4 = GET_NEXT_CONTINUATION_BYTE(src);
983         unsigned char char5 = GET_NEXT_CONTINUATION_BYTE(src);
984         return (unsigned)((char5 & 0x3f) | ((char4 & 0x3f) << 6) | ((char3 & 0x3f) << 12) | ((char2 & 0x3f) << 18) |
985                           ((char1 & 0x3) << 24));
986     }
987     else
988     {
989         unsigned char char2 = GET_NEXT_CONTINUATION_BYTE(src);
990         unsigned char char3 = GET_NEXT_CONTINUATION_BYTE(src);
991         unsigned char char4 = GET_NEXT_CONTINUATION_BYTE(src);
992         unsigned char char5 = GET_NEXT_CONTINUATION_BYTE(src);
993         unsigned char char6 = GET_NEXT_CONTINUATION_BYTE(src);
994         return (unsigned)((char6 & 0x3f) | ((char5 & 0x3f) << 6) | ((char4 & 0x3f) << 12) | ((char3 & 0x3f) << 18) |
995                           ((char2 & 0x3f) << 24) | ((char1 & 0x1) << 30));
996     }
997 }
998 
999 #ifdef _WIN32
EncodeUTF16(wchar_t * & dest,unsigned unicodeChar)1000 void String::EncodeUTF16(wchar_t*& dest, unsigned unicodeChar)
1001 {
1002     if (unicodeChar < 0x10000)
1003         *dest++ = unicodeChar;
1004     else
1005     {
1006         unicodeChar -= 0x10000;
1007         *dest++ = 0xd800 | ((unicodeChar >> 10) & 0x3ff);
1008         *dest++ = 0xdc00 | (unicodeChar & 0x3ff);
1009     }
1010 }
1011 
DecodeUTF16(const wchar_t * & src)1012 unsigned String::DecodeUTF16(const wchar_t*& src)
1013 {
1014     if (src == 0)
1015         return 0;
1016 
1017     unsigned short word1 = *src++;
1018 
1019     // Check if we are at a low surrogate
1020     if (word1 >= 0xdc00 && word1 < 0xe000)
1021     {
1022         while (*src >= 0xdc00 && *src < 0xe000)
1023             ++src;
1024         return '?';
1025     }
1026 
1027     if (word1 < 0xd800 || word1 >= 0xe000)
1028         return word1;
1029     else
1030     {
1031         unsigned short word2 = *src++;
1032         if (word2 < 0xdc00 || word2 >= 0xe000)
1033         {
1034             --src;
1035             return '?';
1036         }
1037         else
1038             return (((word1 & 0x3ff) << 10) | (word2 & 0x3ff)) + 0x10000;
1039     }
1040 }
1041 #endif
1042 
Split(const char * str,char separator,bool keepEmptyStrings)1043 Vector<String> String::Split(const char* str, char separator, bool keepEmptyStrings)
1044 {
1045     Vector<String> ret;
1046     const char* strEnd = str + String::CStringLength(str);
1047 
1048     for (const char* splitEnd = str; splitEnd != strEnd; ++splitEnd)
1049     {
1050         if (*splitEnd == separator)
1051         {
1052             const ptrdiff_t splitLen = splitEnd - str;
1053             if (splitLen > 0 || keepEmptyStrings)
1054                 ret.Push(String(str, splitLen));
1055             str = splitEnd + 1;
1056         }
1057     }
1058 
1059     const ptrdiff_t splitLen = strEnd - str;
1060     if (splitLen > 0 || keepEmptyStrings)
1061         ret.Push(String(str, splitLen));
1062 
1063     return ret;
1064 }
1065 
Joined(const Vector<String> & subStrings,const String & glue)1066 String String::Joined(const Vector<String>& subStrings, const String& glue)
1067 {
1068     if (subStrings.Empty())
1069         return String();
1070 
1071     String joinedString(subStrings[0]);
1072     for (unsigned i = 1; i < subStrings.Size(); ++i)
1073         joinedString.Append(glue).Append(subStrings[i]);
1074 
1075     return joinedString;
1076 }
1077 
AppendWithFormat(const char * formatString,...)1078 String& String::AppendWithFormat(const char* formatString, ...)
1079 {
1080     va_list args;
1081     va_start(args, formatString);
1082     AppendWithFormatArgs(formatString, args);
1083     va_end(args);
1084     return *this;
1085 }
1086 
AppendWithFormatArgs(const char * formatString,va_list args)1087 String& String::AppendWithFormatArgs(const char* formatString, va_list args)
1088 {
1089     int pos = 0, lastPos = 0;
1090     int length = (int)strlen(formatString);
1091 
1092     while (true)
1093     {
1094         // Scan the format string and find %a argument where a is one of d, f, s ...
1095         while (pos < length && formatString[pos] != '%') pos++;
1096         Append(formatString + lastPos, (unsigned)(pos - lastPos));
1097         if (pos >= length)
1098             return *this;
1099 
1100         char format = formatString[pos + 1];
1101         pos += 2;
1102         lastPos = pos;
1103 
1104         switch (format)
1105         {
1106         // Integer
1107         case 'd':
1108         case 'i':
1109             {
1110                 int arg = va_arg(args, int);
1111                 Append(String(arg));
1112                 break;
1113             }
1114 
1115         // Unsigned
1116         case 'u':
1117             {
1118                 unsigned arg = va_arg(args, unsigned);
1119                 Append(String(arg));
1120                 break;
1121             }
1122 
1123         // Unsigned long
1124         case 'l':
1125             {
1126                 unsigned long arg = va_arg(args, unsigned long);
1127                 Append(String(arg));
1128                 break;
1129             }
1130 
1131         // Real
1132         case 'f':
1133             {
1134                 double arg = va_arg(args, double);
1135                 Append(String(arg));
1136                 break;
1137             }
1138 
1139         // Character
1140         case 'c':
1141             {
1142                 int arg = va_arg(args, int);
1143                 Append((char)arg);
1144                 break;
1145             }
1146 
1147         // C string
1148         case 's':
1149             {
1150                 char* arg = va_arg(args, char*);
1151                 Append(arg);
1152                 break;
1153             }
1154 
1155         // Hex
1156         case 'x':
1157             {
1158                 char buf[CONVERSION_BUFFER_LENGTH];
1159                 int arg = va_arg(args, int);
1160                 int arglen = ::sprintf(buf, "%x", arg);
1161                 Append(buf, (unsigned)arglen);
1162                 break;
1163             }
1164 
1165         // Pointer
1166         case 'p':
1167             {
1168                 char buf[CONVERSION_BUFFER_LENGTH];
1169                 int arg = va_arg(args, int);
1170                 int arglen = ::sprintf(buf, "%p", reinterpret_cast<void*>(arg));
1171                 Append(buf, (unsigned)arglen);
1172                 break;
1173             }
1174 
1175         case '%':
1176             {
1177                 Append("%", 1);
1178                 break;
1179             }
1180 
1181         default:
1182             URHO3D_LOGWARNINGF("Unsupported format specifier: '%c'", format);
1183             break;
1184         }
1185     }
1186 }
1187 
Compare(const char * lhs,const char * rhs,bool caseSensitive)1188 int String::Compare(const char* lhs, const char* rhs, bool caseSensitive)
1189 {
1190     if (!lhs || !rhs)
1191         return lhs ? 1 : (rhs ? -1 : 0);
1192 
1193     if (caseSensitive)
1194         return strcmp(lhs, rhs);
1195     else
1196     {
1197         for (;;)
1198         {
1199             char l = (char)tolower(*lhs);
1200             char r = (char)tolower(*rhs);
1201             if (!l || !r)
1202                 return l ? 1 : (r ? -1 : 0);
1203             if (l < r)
1204                 return -1;
1205             if (l > r)
1206                 return 1;
1207 
1208             ++lhs;
1209             ++rhs;
1210         }
1211     }
1212 }
1213 
Replace(unsigned pos,unsigned length,const char * srcStart,unsigned srcLength)1214 void String::Replace(unsigned pos, unsigned length, const char* srcStart, unsigned srcLength)
1215 {
1216     int delta = (int)srcLength - (int)length;
1217 
1218     if (pos + length < length_)
1219     {
1220         if (delta < 0)
1221         {
1222             MoveRange(pos + srcLength, pos + length, length_ - pos - length);
1223             Resize(length_ + delta);
1224         }
1225         if (delta > 0)
1226         {
1227             Resize(length_ + delta);
1228             MoveRange(pos + srcLength, pos + length, length_ - pos - length - delta);
1229         }
1230     }
1231     else
1232         Resize(length_ + delta);
1233 
1234     CopyChars(buffer_ + pos, srcStart, srcLength);
1235 }
1236 
WString()1237 WString::WString() :
1238     length_(0),
1239     buffer_(0)
1240 {
1241 }
1242 
WString(const String & str)1243 WString::WString(const String& str) :
1244     length_(0),
1245     buffer_(0)
1246 {
1247 #ifdef _WIN32
1248     unsigned neededSize = 0;
1249     wchar_t temp[3];
1250 
1251     unsigned byteOffset = 0;
1252     while (byteOffset < str.Length())
1253     {
1254         wchar_t* dest = temp;
1255         String::EncodeUTF16(dest, str.NextUTF8Char(byteOffset));
1256         neededSize += dest - temp;
1257     }
1258 
1259     Resize(neededSize);
1260 
1261     byteOffset = 0;
1262     wchar_t* dest = buffer_;
1263     while (byteOffset < str.Length())
1264         String::EncodeUTF16(dest, str.NextUTF8Char(byteOffset));
1265 #else
1266     Resize(str.LengthUTF8());
1267 
1268     unsigned byteOffset = 0;
1269     wchar_t* dest = buffer_;
1270     while (byteOffset < str.Length())
1271         *dest++ = (wchar_t)str.NextUTF8Char(byteOffset);
1272 #endif
1273 }
1274 
~WString()1275 WString::~WString()
1276 {
1277     delete[] buffer_;
1278 }
1279 
Resize(unsigned newLength)1280 void WString::Resize(unsigned newLength)
1281 {
1282     if (!newLength)
1283     {
1284         delete[] buffer_;
1285         buffer_ = 0;
1286         length_ = 0;
1287     }
1288     else
1289     {
1290         wchar_t* newBuffer = new wchar_t[newLength + 1];
1291         if (buffer_)
1292         {
1293             unsigned copyLength = length_ < newLength ? length_ : newLength;
1294             memcpy(newBuffer, buffer_, copyLength * sizeof(wchar_t));
1295             delete[] buffer_;
1296         }
1297         newBuffer[newLength] = 0;
1298         buffer_ = newBuffer;
1299         length_ = newLength;
1300     }
1301 }
1302 
1303 }
1304