1 /*
2   ==============================================================================
3 
4    This file is part of the JUCE library.
5    Copyright (c) 2020 - Raw Material Software Limited
6 
7    JUCE is an open source library subject to commercial or open-source
8    licensing.
9 
10    The code included in this file is provided under the terms of the ISC license
11    http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12    To use, copy, modify, and/or distribute this software for any purpose with or
13    without fee is hereby granted provided that the above copyright notice and
14    this permission notice appear in all copies.
15 
16    JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17    EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18    DISCLAIMED.
19 
20   ==============================================================================
21 */
22 
23 namespace juce
24 {
25 
26 JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4514 4996)
27 
28 NewLine newLine;
29 
30 #if defined (JUCE_STRINGS_ARE_UNICODE) && ! JUCE_STRINGS_ARE_UNICODE
31  #error "JUCE_STRINGS_ARE_UNICODE is deprecated! All strings are now unicode by default."
32 #endif
33 
34 #if JUCE_NATIVE_WCHAR_IS_UTF8
35  using CharPointer_wchar_t = CharPointer_UTF8;
36 #elif JUCE_NATIVE_WCHAR_IS_UTF16
37  using CharPointer_wchar_t = CharPointer_UTF16;
38 #else
39  using CharPointer_wchar_t = CharPointer_UTF32;
40 #endif
41 
castToCharPointer_wchar_t(const void * t)42 static CharPointer_wchar_t castToCharPointer_wchar_t (const void* t) noexcept
43 {
44     return CharPointer_wchar_t (static_cast<const CharPointer_wchar_t::CharType*> (t));
45 }
46 
47 //==============================================================================
48 // (Mirrors the structure of StringHolder, but without the atomic member, so can be statically constructed)
49 struct EmptyString
50 {
51     int refCount;
52     size_t allocatedBytes;
53     String::CharPointerType::CharType text;
54 };
55 
56 static const EmptyString emptyString { 0x3fffffff, sizeof (String::CharPointerType::CharType), 0 };
57 
58 //==============================================================================
59 class StringHolder
60 {
61 public:
62     StringHolder() = delete;
63 
64     using CharPointerType  = String::CharPointerType;
65     using CharType         = String::CharPointerType::CharType;
66 
67     //==============================================================================
createUninitialisedBytes(size_t numBytes)68     static CharPointerType createUninitialisedBytes (size_t numBytes)
69     {
70         numBytes = (numBytes + 3) & ~(size_t) 3;
71         auto s = unalignedPointerCast<StringHolder*> (new char [sizeof (StringHolder) - sizeof (CharType) + numBytes]);
72         s->refCount.value = 0;
73         s->allocatedNumBytes = numBytes;
74         return CharPointerType (s->text);
75     }
76 
77     template <class CharPointer>
createFromCharPointer(const CharPointer text)78     static CharPointerType createFromCharPointer (const CharPointer text)
79     {
80         if (text.getAddress() == nullptr || text.isEmpty())
81             return CharPointerType (&(emptyString.text));
82 
83         auto bytesNeeded = sizeof (CharType) + CharPointerType::getBytesRequiredFor (text);
84         auto dest = createUninitialisedBytes (bytesNeeded);
85         CharPointerType (dest).writeAll (text);
86         return dest;
87     }
88 
89     template <class CharPointer>
createFromCharPointer(const CharPointer text,size_t maxChars)90     static CharPointerType createFromCharPointer (const CharPointer text, size_t maxChars)
91     {
92         if (text.getAddress() == nullptr || text.isEmpty() || maxChars == 0)
93             return CharPointerType (&(emptyString.text));
94 
95         auto end = text;
96         size_t numChars = 0;
97         size_t bytesNeeded = sizeof (CharType);
98 
99         while (numChars < maxChars && ! end.isEmpty())
100         {
101             bytesNeeded += CharPointerType::getBytesRequiredFor (end.getAndAdvance());
102             ++numChars;
103         }
104 
105         auto dest = createUninitialisedBytes (bytesNeeded);
106         CharPointerType (dest).writeWithCharLimit (text, (int) numChars + 1);
107         return dest;
108     }
109 
110     template <class CharPointer>
createFromCharPointer(const CharPointer start,const CharPointer end)111     static CharPointerType createFromCharPointer (const CharPointer start, const CharPointer end)
112     {
113         if (start.getAddress() == nullptr || start.isEmpty())
114             return CharPointerType (&(emptyString.text));
115 
116         auto e = start;
117         int numChars = 0;
118         auto bytesNeeded = sizeof (CharType);
119 
120         while (e < end && ! e.isEmpty())
121         {
122             bytesNeeded += CharPointerType::getBytesRequiredFor (e.getAndAdvance());
123             ++numChars;
124         }
125 
126         auto dest = createUninitialisedBytes (bytesNeeded);
127         CharPointerType (dest).writeWithCharLimit (start, numChars + 1);
128         return dest;
129     }
130 
createFromCharPointer(const CharPointerType start,const CharPointerType end)131     static CharPointerType createFromCharPointer (const CharPointerType start, const CharPointerType end)
132     {
133         if (start.getAddress() == nullptr || start.isEmpty())
134             return CharPointerType (&(emptyString.text));
135 
136         auto numBytes = (size_t) (reinterpret_cast<const char*> (end.getAddress())
137                                    - reinterpret_cast<const char*> (start.getAddress()));
138         auto dest = createUninitialisedBytes (numBytes + sizeof (CharType));
139         memcpy (dest.getAddress(), start, numBytes);
140         dest.getAddress()[numBytes / sizeof (CharType)] = 0;
141         return dest;
142     }
143 
createFromFixedLength(const char * const src,const size_t numChars)144     static CharPointerType createFromFixedLength (const char* const src, const size_t numChars)
145     {
146         auto dest = createUninitialisedBytes (numChars * sizeof (CharType) + sizeof (CharType));
147         CharPointerType (dest).writeWithCharLimit (CharPointer_UTF8 (src), (int) (numChars + 1));
148         return dest;
149     }
150 
151     //==============================================================================
retain(const CharPointerType text)152     static void retain (const CharPointerType text) noexcept
153     {
154         auto* b = bufferFromText (text);
155 
156         if (! isEmptyString (b))
157             ++(b->refCount);
158     }
159 
release(StringHolder * const b)160     static void release (StringHolder* const b) noexcept
161     {
162         if (! isEmptyString (b))
163             if (--(b->refCount) == -1)
164                 delete[] reinterpret_cast<char*> (b);
165     }
166 
release(const CharPointerType text)167     static void release (const CharPointerType text) noexcept
168     {
169         release (bufferFromText (text));
170     }
171 
getReferenceCount(const CharPointerType text)172     static int getReferenceCount (const CharPointerType text) noexcept
173     {
174         return bufferFromText (text)->refCount.get() + 1;
175     }
176 
177     //==============================================================================
makeUniqueWithByteSize(const CharPointerType text,size_t numBytes)178     static CharPointerType makeUniqueWithByteSize (const CharPointerType text, size_t numBytes)
179     {
180         auto* b = bufferFromText (text);
181 
182         if (isEmptyString (b))
183         {
184             auto newText = createUninitialisedBytes (numBytes);
185             newText.writeNull();
186             return newText;
187         }
188 
189         if (b->allocatedNumBytes >= numBytes && b->refCount.get() <= 0)
190             return text;
191 
192         auto newText = createUninitialisedBytes (jmax (b->allocatedNumBytes, numBytes));
193         memcpy (newText.getAddress(), text.getAddress(), b->allocatedNumBytes);
194         release (b);
195 
196         return newText;
197     }
198 
getAllocatedNumBytes(const CharPointerType text)199     static size_t getAllocatedNumBytes (const CharPointerType text) noexcept
200     {
201         return bufferFromText (text)->allocatedNumBytes;
202     }
203 
204     //==============================================================================
205     Atomic<int> refCount;
206     size_t allocatedNumBytes;
207     CharType text[1];
208 
209 private:
bufferFromText(const CharPointerType text)210     static StringHolder* bufferFromText (const CharPointerType text) noexcept
211     {
212         // (Can't use offsetof() here because of warnings about this not being a POD)
213         return unalignedPointerCast<StringHolder*> (reinterpret_cast<char*> (text.getAddress())
214                     - (reinterpret_cast<size_t> (reinterpret_cast<StringHolder*> (128)->text) - 128));
215     }
216 
isEmptyString(StringHolder * other)217     static bool isEmptyString (StringHolder* other)
218     {
219         return (other->refCount.get() & 0x30000000) != 0;
220     }
221 
compileTimeChecks()222     void compileTimeChecks()
223     {
224         // Let me know if any of these assertions fail on your system!
225        #if JUCE_NATIVE_WCHAR_IS_UTF8
226         static_assert (sizeof (wchar_t) == 1, "JUCE_NATIVE_WCHAR_IS_* macro has incorrect value");
227        #elif JUCE_NATIVE_WCHAR_IS_UTF16
228         static_assert (sizeof (wchar_t) == 2, "JUCE_NATIVE_WCHAR_IS_* macro has incorrect value");
229        #elif JUCE_NATIVE_WCHAR_IS_UTF32
230         static_assert (sizeof (wchar_t) == 4, "JUCE_NATIVE_WCHAR_IS_* macro has incorrect value");
231        #else
232         #error "native wchar_t size is unknown"
233        #endif
234 
235         static_assert (sizeof (EmptyString) == sizeof (StringHolder),
236                        "StringHolder is not large enough to hold an empty String");
237     }
238 };
239 
JUCE_DECLARE_DEPRECATED_STATIC(const String String::empty;)240 JUCE_DECLARE_DEPRECATED_STATIC (const String String::empty;)
241 
242 //==============================================================================
243 String::String() noexcept  : text (&(emptyString.text))
244 {
245 }
246 
~String()247 String::~String() noexcept
248 {
249     StringHolder::release (text);
250 }
251 
String(const String & other)252 String::String (const String& other) noexcept   : text (other.text)
253 {
254     StringHolder::retain (text);
255 }
256 
swapWith(String & other)257 void String::swapWith (String& other) noexcept
258 {
259     std::swap (text, other.text);
260 }
261 
clear()262 void String::clear() noexcept
263 {
264     StringHolder::release (text);
265     text = &(emptyString.text);
266 }
267 
operator =(const String & other)268 String& String::operator= (const String& other) noexcept
269 {
270     StringHolder::retain (other.text);
271     StringHolder::release (text.atomicSwap (other.text));
272     return *this;
273 }
274 
String(String && other)275 String::String (String&& other) noexcept   : text (other.text)
276 {
277     other.text = &(emptyString.text);
278 }
279 
operator =(String && other)280 String& String::operator= (String&& other) noexcept
281 {
282     std::swap (text, other.text);
283     return *this;
284 }
285 
PreallocationBytes(const size_t num)286 inline String::PreallocationBytes::PreallocationBytes (const size_t num) noexcept : numBytes (num) {}
287 
String(const PreallocationBytes & preallocationSize)288 String::String (const PreallocationBytes& preallocationSize)
289     : text (StringHolder::createUninitialisedBytes (preallocationSize.numBytes + sizeof (CharPointerType::CharType)))
290 {
291 }
292 
preallocateBytes(const size_t numBytesNeeded)293 void String::preallocateBytes (const size_t numBytesNeeded)
294 {
295     text = StringHolder::makeUniqueWithByteSize (text, numBytesNeeded + sizeof (CharPointerType::CharType));
296 }
297 
getReferenceCount() const298 int String::getReferenceCount() const noexcept
299 {
300     return StringHolder::getReferenceCount (text);
301 }
302 
303 //==============================================================================
String(const char * const t)304 String::String (const char* const t)
305     : text (StringHolder::createFromCharPointer (CharPointer_ASCII (t)))
306 {
307     /*  If you get an assertion here, then you're trying to create a string from 8-bit data
308         that contains values greater than 127. These can NOT be correctly converted to unicode
309         because there's no way for the String class to know what encoding was used to
310         create them. The source data could be UTF-8, ASCII or one of many local code-pages.
311 
312         To get around this problem, you must be more explicit when you pass an ambiguous 8-bit
313         string to the String class - so for example if your source data is actually UTF-8,
314         you'd call String (CharPointer_UTF8 ("my utf8 string..")), and it would be able to
315         correctly convert the multi-byte characters to unicode. It's *highly* recommended that
316         you use UTF-8 with escape characters in your source code to represent extended characters,
317         because there's no other way to represent these strings in a way that isn't dependent on
318         the compiler, source code editor and platform.
319 
320         Note that the Projucer has a handy string literal generator utility that will convert
321         any unicode string to a valid C++ string literal, creating ascii escape sequences that will
322         work in any compiler.
323     */
324     jassert (t == nullptr || CharPointer_ASCII::isValidString (t, std::numeric_limits<int>::max()));
325 }
326 
String(const char * const t,const size_t maxChars)327 String::String (const char* const t, const size_t maxChars)
328     : text (StringHolder::createFromCharPointer (CharPointer_ASCII (t), maxChars))
329 {
330     /*  If you get an assertion here, then you're trying to create a string from 8-bit data
331         that contains values greater than 127. These can NOT be correctly converted to unicode
332         because there's no way for the String class to know what encoding was used to
333         create them. The source data could be UTF-8, ASCII or one of many local code-pages.
334 
335         To get around this problem, you must be more explicit when you pass an ambiguous 8-bit
336         string to the String class - so for example if your source data is actually UTF-8,
337         you'd call String (CharPointer_UTF8 ("my utf8 string..")), and it would be able to
338         correctly convert the multi-byte characters to unicode. It's *highly* recommended that
339         you use UTF-8 with escape characters in your source code to represent extended characters,
340         because there's no other way to represent these strings in a way that isn't dependent on
341         the compiler, source code editor and platform.
342 
343         Note that the Projucer has a handy string literal generator utility that will convert
344         any unicode string to a valid C++ string literal, creating ascii escape sequences that will
345         work in any compiler.
346     */
347     jassert (t == nullptr || CharPointer_ASCII::isValidString (t, (int) maxChars));
348 }
349 
String(const wchar_t * const t)350 String::String (const wchar_t* const t)      : text (StringHolder::createFromCharPointer (castToCharPointer_wchar_t (t))) {}
String(const CharPointer_UTF8 t)351 String::String (const CharPointer_UTF8  t)   : text (StringHolder::createFromCharPointer (t)) {}
String(const CharPointer_UTF16 t)352 String::String (const CharPointer_UTF16 t)   : text (StringHolder::createFromCharPointer (t)) {}
String(const CharPointer_UTF32 t)353 String::String (const CharPointer_UTF32 t)   : text (StringHolder::createFromCharPointer (t)) {}
String(const CharPointer_ASCII t)354 String::String (const CharPointer_ASCII t)   : text (StringHolder::createFromCharPointer (t)) {}
355 
String(CharPointer_UTF8 t,size_t maxChars)356 String::String (CharPointer_UTF8  t, size_t maxChars)   : text (StringHolder::createFromCharPointer (t, maxChars)) {}
String(CharPointer_UTF16 t,size_t maxChars)357 String::String (CharPointer_UTF16 t, size_t maxChars)   : text (StringHolder::createFromCharPointer (t, maxChars)) {}
String(CharPointer_UTF32 t,size_t maxChars)358 String::String (CharPointer_UTF32 t, size_t maxChars)   : text (StringHolder::createFromCharPointer (t, maxChars)) {}
String(const wchar_t * t,size_t maxChars)359 String::String (const wchar_t* t, size_t maxChars)      : text (StringHolder::createFromCharPointer (castToCharPointer_wchar_t (t), maxChars)) {}
360 
String(CharPointer_UTF8 start,CharPointer_UTF8 end)361 String::String (CharPointer_UTF8  start, CharPointer_UTF8  end)  : text (StringHolder::createFromCharPointer (start, end)) {}
String(CharPointer_UTF16 start,CharPointer_UTF16 end)362 String::String (CharPointer_UTF16 start, CharPointer_UTF16 end)  : text (StringHolder::createFromCharPointer (start, end)) {}
String(CharPointer_UTF32 start,CharPointer_UTF32 end)363 String::String (CharPointer_UTF32 start, CharPointer_UTF32 end)  : text (StringHolder::createFromCharPointer (start, end)) {}
364 
String(const std::string & s)365 String::String (const std::string& s) : text (StringHolder::createFromFixedLength (s.data(), s.size())) {}
String(StringRef s)366 String::String (StringRef s)          : text (StringHolder::createFromCharPointer (s.text)) {}
367 
charToString(juce_wchar character)368 String String::charToString (juce_wchar character)
369 {
370     String result (PreallocationBytes (CharPointerType::getBytesRequiredFor (character)));
371     CharPointerType t (result.text);
372     t.write (character);
373     t.writeNull();
374     return result;
375 }
376 
377 //==============================================================================
378 namespace NumberToStringConverters
379 {
380     enum
381     {
382         charsNeededForInt = 32,
383         charsNeededForDouble = 48
384     };
385 
386     template <typename Type>
printDigits(char * t,Type v)387     static char* printDigits (char* t, Type v) noexcept
388     {
389         *--t = 0;
390 
391         do
392         {
393             *--t = static_cast<char> ('0' + (char) (v % 10));
394             v /= 10;
395 
396         } while (v > 0);
397 
398         return t;
399     }
400 
401     // pass in a pointer to the END of a buffer..
numberToString(char * t,int64 n)402     static char* numberToString (char* t, int64 n) noexcept
403     {
404         if (n >= 0)
405             return printDigits (t, static_cast<uint64> (n));
406 
407         // NB: this needs to be careful not to call -std::numeric_limits<int64>::min(),
408         // which has undefined behaviour
409         t = printDigits (t, static_cast<uint64> (-(n + 1)) + 1);
410         *--t = '-';
411         return t;
412     }
413 
numberToString(char * t,uint64 v)414     static char* numberToString (char* t, uint64 v) noexcept
415     {
416         return printDigits (t, v);
417     }
418 
numberToString(char * t,int n)419     static char* numberToString (char* t, int n) noexcept
420     {
421         if (n >= 0)
422             return printDigits (t, static_cast<unsigned int> (n));
423 
424         // NB: this needs to be careful not to call -std::numeric_limits<int>::min(),
425         // which has undefined behaviour
426         t = printDigits (t, static_cast<unsigned int> (-(n + 1)) + 1);
427         *--t = '-';
428         return t;
429     }
430 
numberToString(char * t,unsigned int v)431     static char* numberToString (char* t, unsigned int v) noexcept
432     {
433         return printDigits (t, v);
434     }
435 
numberToString(char * t,long n)436     static char* numberToString (char* t, long n) noexcept
437     {
438         if (n >= 0)
439             return printDigits (t, static_cast<unsigned long> (n));
440 
441         t = printDigits (t, static_cast<unsigned long> (-(n + 1)) + 1);
442         *--t = '-';
443         return t;
444     }
445 
numberToString(char * t,unsigned long v)446     static char* numberToString (char* t, unsigned long v) noexcept
447     {
448         return printDigits (t, v);
449     }
450 
451     struct StackArrayStream  : public std::basic_streambuf<char, std::char_traits<char>>
452     {
StackArrayStreamjuce::NumberToStringConverters::StackArrayStream453         explicit StackArrayStream (char* d)
454         {
455             static const std::locale classicLocale (std::locale::classic());
456             imbue (classicLocale);
457             setp (d, d + charsNeededForDouble);
458         }
459 
writeDoublejuce::NumberToStringConverters::StackArrayStream460         size_t writeDouble (double n, int numDecPlaces, bool useScientificNotation)
461         {
462             {
463                 std::ostream o (this);
464 
465                 if (numDecPlaces > 0)
466                 {
467                     o.setf (useScientificNotation ? std::ios_base::scientific : std::ios_base::fixed);
468                     o.precision ((std::streamsize) numDecPlaces);
469                 }
470 
471                 o << n;
472             }
473 
474             return (size_t) (pptr() - pbase());
475         }
476     };
477 
doubleToString(char * buffer,double n,int numDecPlaces,bool useScientificNotation,size_t & len)478     static char* doubleToString (char* buffer, double n, int numDecPlaces, bool useScientificNotation, size_t& len) noexcept
479     {
480         StackArrayStream strm (buffer);
481         len = strm.writeDouble (n, numDecPlaces, useScientificNotation);
482         jassert (len <= charsNeededForDouble);
483         return buffer;
484     }
485 
486     template <typename IntegerType>
createFromInteger(IntegerType number)487     static String::CharPointerType createFromInteger (IntegerType number)
488     {
489         char buffer [charsNeededForInt];
490         auto* end = buffer + numElementsInArray (buffer);
491         auto* start = numberToString (end, number);
492         return StringHolder::createFromFixedLength (start, (size_t) (end - start - 1));
493     }
494 
createFromDouble(double number,int numberOfDecimalPlaces,bool useScientificNotation)495     static String::CharPointerType createFromDouble (double number, int numberOfDecimalPlaces, bool useScientificNotation)
496     {
497         char buffer [charsNeededForDouble];
498         size_t len;
499         auto start = doubleToString (buffer, number, numberOfDecimalPlaces, useScientificNotation, len);
500         return StringHolder::createFromFixedLength (start, len);
501     }
502 }
503 
504 //==============================================================================
String(int number)505 String::String (int number)            : text (NumberToStringConverters::createFromInteger (number)) {}
String(unsigned int number)506 String::String (unsigned int number)   : text (NumberToStringConverters::createFromInteger (number)) {}
String(short number)507 String::String (short number)          : text (NumberToStringConverters::createFromInteger ((int) number)) {}
String(unsigned short number)508 String::String (unsigned short number) : text (NumberToStringConverters::createFromInteger ((unsigned int) number)) {}
String(int64 number)509 String::String (int64  number)         : text (NumberToStringConverters::createFromInteger (number)) {}
String(uint64 number)510 String::String (uint64 number)         : text (NumberToStringConverters::createFromInteger (number)) {}
String(long number)511 String::String (long number)           : text (NumberToStringConverters::createFromInteger (number)) {}
String(unsigned long number)512 String::String (unsigned long number)  : text (NumberToStringConverters::createFromInteger (number)) {}
513 
String(float number)514 String::String (float  number)         : text (NumberToStringConverters::createFromDouble ((double) number, 0, false)) {}
String(double number)515 String::String (double number)         : text (NumberToStringConverters::createFromDouble (         number, 0, false)) {}
String(float number,int numberOfDecimalPlaces,bool useScientificNotation)516 String::String (float  number, int numberOfDecimalPlaces, bool useScientificNotation)  : text (NumberToStringConverters::createFromDouble ((double) number, numberOfDecimalPlaces, useScientificNotation)) {}
String(double number,int numberOfDecimalPlaces,bool useScientificNotation)517 String::String (double number, int numberOfDecimalPlaces, bool useScientificNotation)  : text (NumberToStringConverters::createFromDouble (         number, numberOfDecimalPlaces, useScientificNotation)) {}
518 
519 //==============================================================================
length() const520 int String::length() const noexcept
521 {
522     return (int) text.length();
523 }
524 
findByteOffsetOfEnd(String::CharPointerType text)525 static size_t findByteOffsetOfEnd (String::CharPointerType text) noexcept
526 {
527     return (size_t) (((char*) text.findTerminatingNull().getAddress()) - (char*) text.getAddress());
528 }
529 
getByteOffsetOfEnd() const530 size_t String::getByteOffsetOfEnd() const noexcept
531 {
532     return findByteOffsetOfEnd (text);
533 }
534 
operator [](int index) const535 juce_wchar String::operator[] (int index) const noexcept
536 {
537     jassert (index == 0 || (index > 0 && index <= (int) text.lengthUpTo ((size_t) index + 1)));
538     return text [index];
539 }
540 
541 template <typename Type>
542 struct HashGenerator
543 {
544     template <typename CharPointer>
calculatejuce::HashGenerator545     static Type calculate (CharPointer t) noexcept
546     {
547         Type result = {};
548 
549         while (! t.isEmpty())
550             result = ((Type) multiplier) * result + (Type) t.getAndAdvance();
551 
552         return result;
553     }
554 
555     enum { multiplier = sizeof (Type) > 4 ? 101 : 31 };
556 };
557 
hashCode() const558 int String::hashCode() const noexcept       { return (int) HashGenerator<uint32>    ::calculate (text); }
hashCode64() const559 int64 String::hashCode64() const noexcept   { return (int64) HashGenerator<uint64>  ::calculate (text); }
hash() const560 size_t String::hash() const noexcept        { return HashGenerator<size_t>          ::calculate (text); }
561 
562 //==============================================================================
operator ==(const String & s1,const String & s2)563 JUCE_API bool JUCE_CALLTYPE operator== (const String& s1, const String& s2) noexcept            { return s1.compare (s2) == 0; }
operator !=(const String & s1,const String & s2)564 JUCE_API bool JUCE_CALLTYPE operator!= (const String& s1, const String& s2) noexcept            { return s1.compare (s2) != 0; }
operator ==(const String & s1,const char * s2)565 JUCE_API bool JUCE_CALLTYPE operator== (const String& s1, const char* s2) noexcept              { return s1.compare (s2) == 0; }
operator !=(const String & s1,const char * s2)566 JUCE_API bool JUCE_CALLTYPE operator!= (const String& s1, const char* s2) noexcept              { return s1.compare (s2) != 0; }
operator ==(const String & s1,const wchar_t * s2)567 JUCE_API bool JUCE_CALLTYPE operator== (const String& s1, const wchar_t* s2) noexcept           { return s1.compare (s2) == 0; }
operator !=(const String & s1,const wchar_t * s2)568 JUCE_API bool JUCE_CALLTYPE operator!= (const String& s1, const wchar_t* s2) noexcept           { return s1.compare (s2) != 0; }
operator ==(const String & s1,StringRef s2)569 JUCE_API bool JUCE_CALLTYPE operator== (const String& s1, StringRef s2) noexcept                { return s1.getCharPointer().compare (s2.text) == 0; }
operator !=(const String & s1,StringRef s2)570 JUCE_API bool JUCE_CALLTYPE operator!= (const String& s1, StringRef s2) noexcept                { return s1.getCharPointer().compare (s2.text) != 0; }
operator <(const String & s1,StringRef s2)571 JUCE_API bool JUCE_CALLTYPE operator<  (const String& s1, StringRef s2) noexcept                { return s1.getCharPointer().compare (s2.text) < 0; }
operator <=(const String & s1,StringRef s2)572 JUCE_API bool JUCE_CALLTYPE operator<= (const String& s1, StringRef s2) noexcept                { return s1.getCharPointer().compare (s2.text) <= 0; }
operator >(const String & s1,StringRef s2)573 JUCE_API bool JUCE_CALLTYPE operator>  (const String& s1, StringRef s2) noexcept                { return s1.getCharPointer().compare (s2.text) > 0; }
operator >=(const String & s1,StringRef s2)574 JUCE_API bool JUCE_CALLTYPE operator>= (const String& s1, StringRef s2) noexcept                { return s1.getCharPointer().compare (s2.text) >= 0; }
operator ==(const String & s1,const CharPointer_UTF8 s2)575 JUCE_API bool JUCE_CALLTYPE operator== (const String& s1, const CharPointer_UTF8 s2) noexcept   { return s1.getCharPointer().compare (s2) == 0; }
operator !=(const String & s1,const CharPointer_UTF8 s2)576 JUCE_API bool JUCE_CALLTYPE operator!= (const String& s1, const CharPointer_UTF8 s2) noexcept   { return s1.getCharPointer().compare (s2) != 0; }
operator ==(const String & s1,const CharPointer_UTF16 s2)577 JUCE_API bool JUCE_CALLTYPE operator== (const String& s1, const CharPointer_UTF16 s2) noexcept  { return s1.getCharPointer().compare (s2) == 0; }
operator !=(const String & s1,const CharPointer_UTF16 s2)578 JUCE_API bool JUCE_CALLTYPE operator!= (const String& s1, const CharPointer_UTF16 s2) noexcept  { return s1.getCharPointer().compare (s2) != 0; }
operator ==(const String & s1,const CharPointer_UTF32 s2)579 JUCE_API bool JUCE_CALLTYPE operator== (const String& s1, const CharPointer_UTF32 s2) noexcept  { return s1.getCharPointer().compare (s2) == 0; }
operator !=(const String & s1,const CharPointer_UTF32 s2)580 JUCE_API bool JUCE_CALLTYPE operator!= (const String& s1, const CharPointer_UTF32 s2) noexcept  { return s1.getCharPointer().compare (s2) != 0; }
581 
equalsIgnoreCase(const wchar_t * const t) const582 bool String::equalsIgnoreCase (const wchar_t* const t) const noexcept
583 {
584     return t != nullptr ? text.compareIgnoreCase (castToCharPointer_wchar_t (t)) == 0
585                         : isEmpty();
586 }
587 
equalsIgnoreCase(const char * const t) const588 bool String::equalsIgnoreCase (const char* const t) const noexcept
589 {
590     return t != nullptr ? text.compareIgnoreCase (CharPointer_UTF8 (t)) == 0
591                         : isEmpty();
592 }
593 
equalsIgnoreCase(StringRef t) const594 bool String::equalsIgnoreCase (StringRef t) const noexcept
595 {
596     return text.compareIgnoreCase (t.text) == 0;
597 }
598 
equalsIgnoreCase(const String & other) const599 bool String::equalsIgnoreCase (const String& other) const noexcept
600 {
601     return text == other.text
602             || text.compareIgnoreCase (other.text) == 0;
603 }
604 
compare(const String & other) const605 int String::compare (const String& other) const noexcept           { return (text == other.text) ? 0 : text.compare (other.text); }
compare(const char * const other) const606 int String::compare (const char* const other) const noexcept       { return text.compare (CharPointer_UTF8 (other)); }
compare(const wchar_t * const other) const607 int String::compare (const wchar_t* const other) const noexcept    { return text.compare (castToCharPointer_wchar_t (other)); }
compareIgnoreCase(const String & other) const608 int String::compareIgnoreCase (const String& other) const noexcept { return (text == other.text) ? 0 : text.compareIgnoreCase (other.text); }
609 
stringCompareRight(String::CharPointerType s1,String::CharPointerType s2)610 static int stringCompareRight (String::CharPointerType s1, String::CharPointerType s2) noexcept
611 {
612     for (int bias = 0;;)
613     {
614         auto c1 = s1.getAndAdvance();
615         bool isDigit1 = CharacterFunctions::isDigit (c1);
616 
617         auto c2 = s2.getAndAdvance();
618         bool isDigit2 = CharacterFunctions::isDigit (c2);
619 
620         if (! (isDigit1 || isDigit2))   return bias;
621         if (! isDigit1)                 return -1;
622         if (! isDigit2)                 return 1;
623 
624         if (c1 != c2 && bias == 0)
625             bias = c1 < c2 ? -1 : 1;
626 
627         jassert (c1 != 0 && c2 != 0);
628     }
629 }
630 
stringCompareLeft(String::CharPointerType s1,String::CharPointerType s2)631 static int stringCompareLeft (String::CharPointerType s1, String::CharPointerType s2) noexcept
632 {
633     for (;;)
634     {
635         auto c1 = s1.getAndAdvance();
636         bool isDigit1 = CharacterFunctions::isDigit (c1);
637 
638         auto c2 = s2.getAndAdvance();
639         bool isDigit2 = CharacterFunctions::isDigit (c2);
640 
641         if (! (isDigit1 || isDigit2))   return 0;
642         if (! isDigit1)                 return -1;
643         if (! isDigit2)                 return 1;
644         if (c1 < c2)                    return -1;
645         if (c1 > c2)                    return 1;
646     }
647 }
648 
naturalStringCompare(String::CharPointerType s1,String::CharPointerType s2,bool isCaseSensitive)649 static int naturalStringCompare (String::CharPointerType s1, String::CharPointerType s2, bool isCaseSensitive) noexcept
650 {
651     bool firstLoop = true;
652 
653     for (;;)
654     {
655         const bool hasSpace1 = s1.isWhitespace();
656         const bool hasSpace2 = s2.isWhitespace();
657 
658         if ((! firstLoop) && (hasSpace1 ^ hasSpace2))
659         {
660             if (s1.isEmpty())  return -1;
661             if (s2.isEmpty())  return 1;
662 
663             return hasSpace2 ? 1 : -1;
664         }
665 
666         firstLoop = false;
667 
668         if (hasSpace1)  s1 = s1.findEndOfWhitespace();
669         if (hasSpace2)  s2 = s2.findEndOfWhitespace();
670 
671         if (s1.isDigit() && s2.isDigit())
672         {
673             auto result = (*s1 == '0' || *s2 == '0') ? stringCompareLeft  (s1, s2)
674                                                      : stringCompareRight (s1, s2);
675 
676             if (result != 0)
677                 return result;
678         }
679 
680         auto c1 = s1.getAndAdvance();
681         auto c2 = s2.getAndAdvance();
682 
683         if (c1 != c2 && ! isCaseSensitive)
684         {
685             c1 = CharacterFunctions::toUpperCase (c1);
686             c2 = CharacterFunctions::toUpperCase (c2);
687         }
688 
689         if (c1 == c2)
690         {
691             if (c1 == 0)
692                 return 0;
693         }
694         else
695         {
696             const bool isAlphaNum1 = CharacterFunctions::isLetterOrDigit (c1);
697             const bool isAlphaNum2 = CharacterFunctions::isLetterOrDigit (c2);
698 
699             if (isAlphaNum2 && ! isAlphaNum1) return -1;
700             if (isAlphaNum1 && ! isAlphaNum2) return 1;
701 
702             return c1 < c2 ? -1 : 1;
703         }
704 
705         jassert (c1 != 0 && c2 != 0);
706     }
707 }
708 
compareNatural(StringRef other,bool isCaseSensitive) const709 int String::compareNatural (StringRef other, bool isCaseSensitive) const noexcept
710 {
711     return naturalStringCompare (getCharPointer(), other.text, isCaseSensitive);
712 }
713 
714 //==============================================================================
append(const String & textToAppend,size_t maxCharsToTake)715 void String::append (const String& textToAppend, size_t maxCharsToTake)
716 {
717     appendCharPointer (this == &textToAppend ? String (textToAppend).text
718                                              : textToAppend.text, maxCharsToTake);
719 }
720 
appendCharPointer(const CharPointerType textToAppend)721 void String::appendCharPointer (const CharPointerType textToAppend)
722 {
723     appendCharPointer (textToAppend, textToAppend.findTerminatingNull());
724 }
725 
appendCharPointer(const CharPointerType startOfTextToAppend,const CharPointerType endOfTextToAppend)726 void String::appendCharPointer (const CharPointerType startOfTextToAppend,
727                                 const CharPointerType endOfTextToAppend)
728 {
729     jassert (startOfTextToAppend.getAddress() != nullptr && endOfTextToAppend.getAddress() != nullptr);
730 
731     auto extraBytesNeeded = getAddressDifference (endOfTextToAppend.getAddress(),
732                                                   startOfTextToAppend.getAddress());
733     jassert (extraBytesNeeded >= 0);
734 
735     if (extraBytesNeeded > 0)
736     {
737         auto byteOffsetOfNull = getByteOffsetOfEnd();
738         preallocateBytes ((size_t) extraBytesNeeded + byteOffsetOfNull);
739 
740         auto* newStringStart = addBytesToPointer (text.getAddress(), (int) byteOffsetOfNull);
741         memcpy (newStringStart, startOfTextToAppend.getAddress(), (size_t) extraBytesNeeded);
742         CharPointerType (addBytesToPointer (newStringStart, extraBytesNeeded)).writeNull();
743     }
744 }
745 
operator +=(const wchar_t * t)746 String& String::operator+= (const wchar_t* t)
747 {
748     appendCharPointer (castToCharPointer_wchar_t (t));
749     return *this;
750 }
751 
operator +=(const char * t)752 String& String::operator+= (const char* t)
753 {
754     appendCharPointer (CharPointer_UTF8 (t)); // (using UTF8 here triggers a faster code-path than ascii)
755     return *this;
756 }
757 
operator +=(const String & other)758 String& String::operator+= (const String& other)
759 {
760     if (isEmpty())
761         return operator= (other);
762 
763     if (this == &other)
764         return operator+= (String (*this));
765 
766     appendCharPointer (other.text);
767     return *this;
768 }
769 
operator +=(StringRef other)770 String& String::operator+= (StringRef other)
771 {
772     return operator+= (String (other));
773 }
774 
operator +=(char ch)775 String& String::operator+= (char ch)
776 {
777     const char asString[] = { ch, 0 };
778     return operator+= (asString);
779 }
780 
operator +=(wchar_t ch)781 String& String::operator+= (wchar_t ch)
782 {
783     const wchar_t asString[] = { ch, 0 };
784     return operator+= (asString);
785 }
786 
787 #if ! JUCE_NATIVE_WCHAR_IS_UTF32
operator +=(juce_wchar ch)788 String& String::operator+= (juce_wchar ch)
789 {
790     const juce_wchar asString[] = { ch, 0 };
791     appendCharPointer (CharPointer_UTF32 (asString));
792     return *this;
793 }
794 #endif
795 
796 namespace StringHelpers
797 {
798     template <typename T>
operationAddAssign(String & str,const T number)799     inline String& operationAddAssign (String& str, const T number)
800     {
801         char buffer [(sizeof(T) * 8) / 2];
802         auto* end = buffer + numElementsInArray (buffer);
803         auto* start = NumberToStringConverters::numberToString (end, number);
804 
805        #if JUCE_STRING_UTF_TYPE == 8
806         str.appendCharPointer (String::CharPointerType (start), String::CharPointerType (end));
807        #else
808         str.appendCharPointer (CharPointer_ASCII (start), CharPointer_ASCII (end));
809        #endif
810 
811         return str;
812     }
813 }
814 
operator +=(const int number)815 String& String::operator+= (const int number)     { return StringHelpers::operationAddAssign<int>    (*this, number); }
operator +=(const long number)816 String& String::operator+= (const long number)    { return StringHelpers::operationAddAssign<long>   (*this, number); }
operator +=(const int64 number)817 String& String::operator+= (const int64 number)   { return StringHelpers::operationAddAssign<int64>  (*this, number); }
operator +=(const uint64 number)818 String& String::operator+= (const uint64 number)  { return StringHelpers::operationAddAssign<uint64> (*this, number); }
819 
820 //==============================================================================
operator +(const char * s1,const String & s2)821 JUCE_API String JUCE_CALLTYPE operator+ (const char* s1, const String& s2)    { String s (s1); return s += s2; }
operator +(const wchar_t * s1,const String & s2)822 JUCE_API String JUCE_CALLTYPE operator+ (const wchar_t* s1, const String& s2) { String s (s1); return s += s2; }
823 
operator +(char s1,const String & s2)824 JUCE_API String JUCE_CALLTYPE operator+ (char s1, const String& s2)           { return String::charToString ((juce_wchar) (uint8) s1) + s2; }
operator +(wchar_t s1,const String & s2)825 JUCE_API String JUCE_CALLTYPE operator+ (wchar_t s1, const String& s2)        { return String::charToString (s1) + s2; }
826 
operator +(String s1,const String & s2)827 JUCE_API String JUCE_CALLTYPE operator+ (String s1, const String& s2)         { return s1 += s2; }
operator +(String s1,const char * s2)828 JUCE_API String JUCE_CALLTYPE operator+ (String s1, const char* s2)           { return s1 += s2; }
operator +(String s1,const wchar_t * s2)829 JUCE_API String JUCE_CALLTYPE operator+ (String s1, const wchar_t* s2)        { return s1 += s2; }
operator +(String s1,const std::string & s2)830 JUCE_API String JUCE_CALLTYPE operator+ (String s1, const std::string& s2)    { return s1 += s2.c_str(); }
831 
operator +(String s1,char s2)832 JUCE_API String JUCE_CALLTYPE operator+ (String s1, char s2)                  { return s1 += s2; }
operator +(String s1,wchar_t s2)833 JUCE_API String JUCE_CALLTYPE operator+ (String s1, wchar_t s2)               { return s1 += s2; }
834 
835 #if ! JUCE_NATIVE_WCHAR_IS_UTF32
operator +(juce_wchar s1,const String & s2)836 JUCE_API String JUCE_CALLTYPE operator+ (juce_wchar s1, const String& s2)     { return String::charToString (s1) + s2; }
operator +(String s1,juce_wchar s2)837 JUCE_API String JUCE_CALLTYPE operator+ (String s1, juce_wchar s2)            { return s1 += s2; }
operator <<(String & s1,juce_wchar s2)838 JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, juce_wchar s2)         { return s1 += s2; }
839 #endif
840 
operator <<(String & s1,char s2)841 JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, char s2)               { return s1 += s2; }
operator <<(String & s1,wchar_t s2)842 JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, wchar_t s2)            { return s1 += s2; }
843 
operator <<(String & s1,const char * s2)844 JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const char* s2)        { return s1 += s2; }
operator <<(String & s1,const wchar_t * s2)845 JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const wchar_t* s2)     { return s1 += s2; }
operator <<(String & s1,const String & s2)846 JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const String& s2)      { return s1 += s2; }
operator <<(String & s1,StringRef s2)847 JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, StringRef s2)          { return s1 += s2; }
operator <<(String & s1,const std::string & s2)848 JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const std::string& s2) { return s1 += s2.c_str(); }
849 
operator <<(String & s1,uint8 number)850 JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, uint8  number)         { return s1 += (int) number; }
operator <<(String & s1,short number)851 JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, short  number)         { return s1 += (int) number; }
operator <<(String & s1,int number)852 JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, int    number)         { return s1 += number; }
operator <<(String & s1,long number)853 JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, long   number)         { return s1 += String (number); }
operator <<(String & s1,unsigned long number)854 JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, unsigned long number)  { return s1 += String (number); }
operator <<(String & s1,int64 number)855 JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, int64  number)         { return s1 += String (number); }
operator <<(String & s1,uint64 number)856 JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, uint64 number)         { return s1 += String (number); }
operator <<(String & s1,float number)857 JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, float  number)         { return s1 += String (number); }
operator <<(String & s1,double number)858 JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, double number)         { return s1 += String (number); }
859 
operator <<(OutputStream & stream,const String & text)860 JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const String& text)
861 {
862     return operator<< (stream, StringRef (text));
863 }
864 
operator <<(OutputStream & stream,StringRef text)865 JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, StringRef text)
866 {
867     auto numBytes = CharPointer_UTF8::getBytesRequiredFor (text.text);
868 
869    #if (JUCE_STRING_UTF_TYPE == 8)
870     stream.write (text.text.getAddress(), numBytes);
871    #else
872     // (This avoids using toUTF8() to prevent the memory bloat that it would leave behind
873     // if lots of large, persistent strings were to be written to streams).
874     HeapBlock<char> temp (numBytes + 1);
875     CharPointer_UTF8 (temp).writeAll (text.text);
876     stream.write (temp, numBytes);
877    #endif
878 
879     return stream;
880 }
881 
882 //==============================================================================
indexOfChar(juce_wchar character) const883 int String::indexOfChar (juce_wchar character) const noexcept
884 {
885     return text.indexOf (character);
886 }
887 
indexOfChar(int startIndex,juce_wchar character) const888 int String::indexOfChar (int startIndex, juce_wchar character) const noexcept
889 {
890     auto t = text;
891 
892     for (int i = 0; ! t.isEmpty(); ++i)
893     {
894         if (i >= startIndex)
895         {
896             if (t.getAndAdvance() == character)
897                 return i;
898         }
899         else
900         {
901             ++t;
902         }
903     }
904 
905     return -1;
906 }
907 
lastIndexOfChar(juce_wchar character) const908 int String::lastIndexOfChar (juce_wchar character) const noexcept
909 {
910     auto t = text;
911     int last = -1;
912 
913     for (int i = 0; ! t.isEmpty(); ++i)
914         if (t.getAndAdvance() == character)
915             last = i;
916 
917     return last;
918 }
919 
indexOfAnyOf(StringRef charactersToLookFor,int startIndex,bool ignoreCase) const920 int String::indexOfAnyOf (StringRef charactersToLookFor, int startIndex, bool ignoreCase) const noexcept
921 {
922     auto t = text;
923 
924     for (int i = 0; ! t.isEmpty(); ++i)
925     {
926         if (i >= startIndex)
927         {
928             if (charactersToLookFor.text.indexOf (t.getAndAdvance(), ignoreCase) >= 0)
929                 return i;
930         }
931         else
932         {
933             ++t;
934         }
935     }
936 
937     return -1;
938 }
939 
indexOf(StringRef other) const940 int String::indexOf (StringRef other) const noexcept
941 {
942     return other.isEmpty() ? 0 : text.indexOf (other.text);
943 }
944 
indexOfIgnoreCase(StringRef other) const945 int String::indexOfIgnoreCase (StringRef other) const noexcept
946 {
947     return other.isEmpty() ? 0 : CharacterFunctions::indexOfIgnoreCase (text, other.text);
948 }
949 
indexOf(int startIndex,StringRef other) const950 int String::indexOf (int startIndex, StringRef other) const noexcept
951 {
952     if (other.isEmpty())
953         return -1;
954 
955     auto t = text;
956 
957     for (int i = startIndex; --i >= 0;)
958     {
959         if (t.isEmpty())
960             return -1;
961 
962         ++t;
963     }
964 
965     auto found = t.indexOf (other.text);
966     return found >= 0 ? found + startIndex : found;
967 }
968 
indexOfIgnoreCase(const int startIndex,StringRef other) const969 int String::indexOfIgnoreCase (const int startIndex, StringRef other) const noexcept
970 {
971     if (other.isEmpty())
972         return -1;
973 
974     auto t = text;
975 
976     for (int i = startIndex; --i >= 0;)
977     {
978         if (t.isEmpty())
979             return -1;
980 
981         ++t;
982     }
983 
984     auto found = CharacterFunctions::indexOfIgnoreCase (t, other.text);
985     return found >= 0 ? found + startIndex : found;
986 }
987 
lastIndexOf(StringRef other) const988 int String::lastIndexOf (StringRef other) const noexcept
989 {
990     if (other.isNotEmpty())
991     {
992         auto len = other.length();
993         int i = length() - len;
994 
995         if (i >= 0)
996         {
997             for (auto n = text + i; i >= 0; --i)
998             {
999                 if (n.compareUpTo (other.text, len) == 0)
1000                     return i;
1001 
1002                 --n;
1003             }
1004         }
1005     }
1006 
1007     return -1;
1008 }
1009 
lastIndexOfIgnoreCase(StringRef other) const1010 int String::lastIndexOfIgnoreCase (StringRef other) const noexcept
1011 {
1012     if (other.isNotEmpty())
1013     {
1014         auto len = other.length();
1015         int i = length() - len;
1016 
1017         if (i >= 0)
1018         {
1019             for (auto n = text + i; i >= 0; --i)
1020             {
1021                 if (n.compareIgnoreCaseUpTo (other.text, len) == 0)
1022                     return i;
1023 
1024                 --n;
1025             }
1026         }
1027     }
1028 
1029     return -1;
1030 }
1031 
lastIndexOfAnyOf(StringRef charactersToLookFor,const bool ignoreCase) const1032 int String::lastIndexOfAnyOf (StringRef charactersToLookFor, const bool ignoreCase) const noexcept
1033 {
1034     auto t = text;
1035     int last = -1;
1036 
1037     for (int i = 0; ! t.isEmpty(); ++i)
1038         if (charactersToLookFor.text.indexOf (t.getAndAdvance(), ignoreCase) >= 0)
1039             last = i;
1040 
1041     return last;
1042 }
1043 
contains(StringRef other) const1044 bool String::contains (StringRef other) const noexcept
1045 {
1046     return indexOf (other) >= 0;
1047 }
1048 
containsChar(const juce_wchar character) const1049 bool String::containsChar (const juce_wchar character) const noexcept
1050 {
1051     return text.indexOf (character) >= 0;
1052 }
1053 
containsIgnoreCase(StringRef t) const1054 bool String::containsIgnoreCase (StringRef t) const noexcept
1055 {
1056     return indexOfIgnoreCase (t) >= 0;
1057 }
1058 
indexOfWholeWord(StringRef word) const1059 int String::indexOfWholeWord (StringRef word) const noexcept
1060 {
1061     if (word.isNotEmpty())
1062     {
1063         auto t = text;
1064         auto wordLen = word.length();
1065         auto end = (int) t.length() - wordLen;
1066 
1067         for (int i = 0; i <= end; ++i)
1068         {
1069             if (t.compareUpTo (word.text, wordLen) == 0
1070                   && (i == 0 || ! (t - 1).isLetterOrDigit())
1071                   && ! (t + wordLen).isLetterOrDigit())
1072                 return i;
1073 
1074             ++t;
1075         }
1076     }
1077 
1078     return -1;
1079 }
1080 
indexOfWholeWordIgnoreCase(StringRef word) const1081 int String::indexOfWholeWordIgnoreCase (StringRef word) const noexcept
1082 {
1083     if (word.isNotEmpty())
1084     {
1085         auto t = text;
1086         auto wordLen = word.length();
1087         auto end = (int) t.length() - wordLen;
1088 
1089         for (int i = 0; i <= end; ++i)
1090         {
1091             if (t.compareIgnoreCaseUpTo (word.text, wordLen) == 0
1092                   && (i == 0 || ! (t - 1).isLetterOrDigit())
1093                   && ! (t + wordLen).isLetterOrDigit())
1094                 return i;
1095 
1096             ++t;
1097         }
1098     }
1099 
1100     return -1;
1101 }
1102 
containsWholeWord(StringRef wordToLookFor) const1103 bool String::containsWholeWord (StringRef wordToLookFor) const noexcept
1104 {
1105     return indexOfWholeWord (wordToLookFor) >= 0;
1106 }
1107 
containsWholeWordIgnoreCase(StringRef wordToLookFor) const1108 bool String::containsWholeWordIgnoreCase (StringRef wordToLookFor) const noexcept
1109 {
1110     return indexOfWholeWordIgnoreCase (wordToLookFor) >= 0;
1111 }
1112 
1113 //==============================================================================
1114 template <typename CharPointer>
1115 struct WildCardMatcher
1116 {
matchesjuce::WildCardMatcher1117     static bool matches (CharPointer wildcard, CharPointer test, const bool ignoreCase) noexcept
1118     {
1119         for (;;)
1120         {
1121             auto wc = wildcard.getAndAdvance();
1122 
1123             if (wc == '*')
1124                 return wildcard.isEmpty() || matchesAnywhere (wildcard, test, ignoreCase);
1125 
1126             if (! characterMatches (wc, test.getAndAdvance(), ignoreCase))
1127                 return false;
1128 
1129             if (wc == 0)
1130                 return true;
1131         }
1132     }
1133 
characterMatchesjuce::WildCardMatcher1134     static bool characterMatches (const juce_wchar wc, const juce_wchar tc, const bool ignoreCase) noexcept
1135     {
1136         return (wc == tc) || (wc == '?' && tc != 0)
1137                 || (ignoreCase && CharacterFunctions::toLowerCase (wc) == CharacterFunctions::toLowerCase (tc));
1138     }
1139 
matchesAnywherejuce::WildCardMatcher1140     static bool matchesAnywhere (const CharPointer wildcard, CharPointer test, const bool ignoreCase) noexcept
1141     {
1142         for (; ! test.isEmpty(); ++test)
1143             if (matches (wildcard, test, ignoreCase))
1144                 return true;
1145 
1146         return false;
1147     }
1148 };
1149 
matchesWildcard(StringRef wildcard,const bool ignoreCase) const1150 bool String::matchesWildcard (StringRef wildcard, const bool ignoreCase) const noexcept
1151 {
1152     return WildCardMatcher<CharPointerType>::matches (wildcard.text, text, ignoreCase);
1153 }
1154 
1155 //==============================================================================
repeatedString(StringRef stringToRepeat,int numberOfTimesToRepeat)1156 String String::repeatedString (StringRef stringToRepeat, int numberOfTimesToRepeat)
1157 {
1158     if (numberOfTimesToRepeat <= 0)
1159         return {};
1160 
1161     String result (PreallocationBytes (findByteOffsetOfEnd (stringToRepeat) * (size_t) numberOfTimesToRepeat));
1162     auto n = result.text;
1163 
1164     while (--numberOfTimesToRepeat >= 0)
1165         n.writeAll (stringToRepeat.text);
1166 
1167     return result;
1168 }
1169 
paddedLeft(const juce_wchar padCharacter,int minimumLength) const1170 String String::paddedLeft (const juce_wchar padCharacter, int minimumLength) const
1171 {
1172     jassert (padCharacter != 0);
1173 
1174     auto extraChars = minimumLength;
1175     auto end = text;
1176 
1177     while (! end.isEmpty())
1178     {
1179         --extraChars;
1180         ++end;
1181     }
1182 
1183     if (extraChars <= 0 || padCharacter == 0)
1184         return *this;
1185 
1186     auto currentByteSize = (size_t) (((char*) end.getAddress()) - (char*) text.getAddress());
1187     String result (PreallocationBytes (currentByteSize + (size_t) extraChars * CharPointerType::getBytesRequiredFor (padCharacter)));
1188     auto n = result.text;
1189 
1190     while (--extraChars >= 0)
1191         n.write (padCharacter);
1192 
1193     n.writeAll (text);
1194     return result;
1195 }
1196 
paddedRight(const juce_wchar padCharacter,int minimumLength) const1197 String String::paddedRight (const juce_wchar padCharacter, int minimumLength) const
1198 {
1199     jassert (padCharacter != 0);
1200 
1201     auto extraChars = minimumLength;
1202     CharPointerType end (text);
1203 
1204     while (! end.isEmpty())
1205     {
1206         --extraChars;
1207         ++end;
1208     }
1209 
1210     if (extraChars <= 0 || padCharacter == 0)
1211         return *this;
1212 
1213     auto currentByteSize = (size_t) (((char*) end.getAddress()) - (char*) text.getAddress());
1214     String result (PreallocationBytes (currentByteSize + (size_t) extraChars * CharPointerType::getBytesRequiredFor (padCharacter)));
1215     auto n = result.text;
1216 
1217     n.writeAll (text);
1218 
1219     while (--extraChars >= 0)
1220         n.write (padCharacter);
1221 
1222     n.writeNull();
1223     return result;
1224 }
1225 
1226 //==============================================================================
replaceSection(int index,int numCharsToReplace,StringRef stringToInsert) const1227 String String::replaceSection (int index, int numCharsToReplace, StringRef stringToInsert) const
1228 {
1229     if (index < 0)
1230     {
1231         // a negative index to replace from?
1232         jassertfalse;
1233         index = 0;
1234     }
1235 
1236     if (numCharsToReplace < 0)
1237     {
1238         // replacing a negative number of characters?
1239         numCharsToReplace = 0;
1240         jassertfalse;
1241     }
1242 
1243     auto insertPoint = text;
1244 
1245     for (int i = 0; i < index; ++i)
1246     {
1247         if (insertPoint.isEmpty())
1248         {
1249             // replacing beyond the end of the string?
1250             jassertfalse;
1251             return *this + stringToInsert;
1252         }
1253 
1254         ++insertPoint;
1255     }
1256 
1257     auto startOfRemainder = insertPoint;
1258 
1259     for (int i = 0; i < numCharsToReplace && ! startOfRemainder.isEmpty(); ++i)
1260         ++startOfRemainder;
1261 
1262     if (insertPoint == text && startOfRemainder.isEmpty())
1263         return stringToInsert.text;
1264 
1265     auto initialBytes = (size_t) (((char*) insertPoint.getAddress()) - (char*) text.getAddress());
1266     auto newStringBytes = findByteOffsetOfEnd (stringToInsert);
1267     auto remainderBytes = (size_t) (((char*) startOfRemainder.findTerminatingNull().getAddress()) - (char*) startOfRemainder.getAddress());
1268 
1269     auto newTotalBytes = initialBytes + newStringBytes + remainderBytes;
1270 
1271     if (newTotalBytes <= 0)
1272         return {};
1273 
1274     String result (PreallocationBytes ((size_t) newTotalBytes));
1275 
1276     auto* dest = (char*) result.text.getAddress();
1277     memcpy (dest, text.getAddress(), initialBytes);
1278     dest += initialBytes;
1279     memcpy (dest, stringToInsert.text.getAddress(), newStringBytes);
1280     dest += newStringBytes;
1281     memcpy (dest, startOfRemainder.getAddress(), remainderBytes);
1282     dest += remainderBytes;
1283     CharPointerType ((CharPointerType::CharType*) dest).writeNull();
1284 
1285     return result;
1286 }
1287 
replace(StringRef stringToReplace,StringRef stringToInsert,const bool ignoreCase) const1288 String String::replace (StringRef stringToReplace, StringRef stringToInsert, const bool ignoreCase) const
1289 {
1290     auto stringToReplaceLen = stringToReplace.length();
1291     auto stringToInsertLen = stringToInsert.length();
1292 
1293     int i = 0;
1294     String result (*this);
1295 
1296     while ((i = (ignoreCase ? result.indexOfIgnoreCase (i, stringToReplace)
1297                             : result.indexOf (i, stringToReplace))) >= 0)
1298     {
1299         result = result.replaceSection (i, stringToReplaceLen, stringToInsert);
1300         i += stringToInsertLen;
1301     }
1302 
1303     return result;
1304 }
1305 
replaceFirstOccurrenceOf(StringRef stringToReplace,StringRef stringToInsert,const bool ignoreCase) const1306 String String::replaceFirstOccurrenceOf (StringRef stringToReplace, StringRef stringToInsert, const bool ignoreCase) const
1307 {
1308     auto stringToReplaceLen = stringToReplace.length();
1309     auto index = ignoreCase ? indexOfIgnoreCase (stringToReplace)
1310                             : indexOf (stringToReplace);
1311 
1312     if (index >= 0)
1313         return replaceSection (index, stringToReplaceLen, stringToInsert);
1314 
1315     return *this;
1316 }
1317 
1318 struct StringCreationHelper
1319 {
StringCreationHelperjuce::StringCreationHelper1320     StringCreationHelper (size_t initialBytes)  : allocatedBytes (initialBytes)
1321     {
1322         result.preallocateBytes (allocatedBytes);
1323         dest = result.getCharPointer();
1324     }
1325 
StringCreationHelperjuce::StringCreationHelper1326     StringCreationHelper (const String::CharPointerType s)
1327         : source (s), allocatedBytes (StringHolder::getAllocatedNumBytes (s))
1328     {
1329         result.preallocateBytes (allocatedBytes);
1330         dest = result.getCharPointer();
1331     }
1332 
writejuce::StringCreationHelper1333     void write (juce_wchar c)
1334     {
1335         bytesWritten += String::CharPointerType::getBytesRequiredFor (c);
1336 
1337         if (bytesWritten > allocatedBytes)
1338         {
1339             allocatedBytes += jmax ((size_t) 8, allocatedBytes / 16);
1340             auto destOffset = (size_t) (((char*) dest.getAddress()) - (char*) result.getCharPointer().getAddress());
1341             result.preallocateBytes (allocatedBytes);
1342             dest = addBytesToPointer (result.getCharPointer().getAddress(), (int) destOffset);
1343         }
1344 
1345         dest.write (c);
1346     }
1347 
1348     String result;
1349     String::CharPointerType source { nullptr }, dest { nullptr };
1350     size_t allocatedBytes, bytesWritten = 0;
1351 };
1352 
replaceCharacter(const juce_wchar charToReplace,const juce_wchar charToInsert) const1353 String String::replaceCharacter (const juce_wchar charToReplace, const juce_wchar charToInsert) const
1354 {
1355     if (! containsChar (charToReplace))
1356         return *this;
1357 
1358     StringCreationHelper builder (text);
1359 
1360     for (;;)
1361     {
1362         auto c = builder.source.getAndAdvance();
1363 
1364         if (c == charToReplace)
1365             c = charToInsert;
1366 
1367         builder.write (c);
1368 
1369         if (c == 0)
1370             break;
1371     }
1372 
1373     return std::move (builder.result);
1374 }
1375 
replaceCharacters(StringRef charactersToReplace,StringRef charactersToInsertInstead) const1376 String String::replaceCharacters (StringRef charactersToReplace, StringRef charactersToInsertInstead) const
1377 {
1378     // Each character in the first string must have a matching one in the
1379     // second, so the two strings must be the same length.
1380     jassert (charactersToReplace.length() == charactersToInsertInstead.length());
1381 
1382     StringCreationHelper builder (text);
1383 
1384     for (;;)
1385     {
1386         auto c = builder.source.getAndAdvance();
1387         auto index = charactersToReplace.text.indexOf (c);
1388 
1389         if (index >= 0)
1390             c = charactersToInsertInstead [index];
1391 
1392         builder.write (c);
1393 
1394         if (c == 0)
1395             break;
1396     }
1397 
1398     return std::move (builder.result);
1399 }
1400 
1401 //==============================================================================
startsWith(StringRef other) const1402 bool String::startsWith (StringRef other) const noexcept
1403 {
1404     return text.compareUpTo (other.text, other.length()) == 0;
1405 }
1406 
startsWithIgnoreCase(StringRef other) const1407 bool String::startsWithIgnoreCase (StringRef other) const noexcept
1408 {
1409     return text.compareIgnoreCaseUpTo (other.text, other.length()) == 0;
1410 }
1411 
startsWithChar(const juce_wchar character) const1412 bool String::startsWithChar (const juce_wchar character) const noexcept
1413 {
1414     jassert (character != 0); // strings can't contain a null character!
1415 
1416     return *text == character;
1417 }
1418 
endsWithChar(const juce_wchar character) const1419 bool String::endsWithChar (const juce_wchar character) const noexcept
1420 {
1421     jassert (character != 0); // strings can't contain a null character!
1422 
1423     if (text.isEmpty())
1424         return false;
1425 
1426     auto t = text.findTerminatingNull();
1427     return *--t == character;
1428 }
1429 
endsWith(StringRef other) const1430 bool String::endsWith (StringRef other) const noexcept
1431 {
1432     auto end = text.findTerminatingNull();
1433     auto otherEnd = other.text.findTerminatingNull();
1434 
1435     while (end > text && otherEnd > other.text)
1436     {
1437         --end;
1438         --otherEnd;
1439 
1440         if (*end != *otherEnd)
1441             return false;
1442     }
1443 
1444     return otherEnd == other.text;
1445 }
1446 
endsWithIgnoreCase(StringRef other) const1447 bool String::endsWithIgnoreCase (StringRef other) const noexcept
1448 {
1449     auto end = text.findTerminatingNull();
1450     auto otherEnd = other.text.findTerminatingNull();
1451 
1452     while (end > text && otherEnd > other.text)
1453     {
1454         --end;
1455         --otherEnd;
1456 
1457         if (end.toLowerCase() != otherEnd.toLowerCase())
1458             return false;
1459     }
1460 
1461     return otherEnd == other.text;
1462 }
1463 
1464 //==============================================================================
toUpperCase() const1465 String String::toUpperCase() const
1466 {
1467     StringCreationHelper builder (text);
1468 
1469     for (;;)
1470     {
1471         auto c = builder.source.toUpperCase();
1472         builder.write (c);
1473 
1474         if (c == 0)
1475             break;
1476 
1477         ++(builder.source);
1478     }
1479 
1480     return std::move (builder.result);
1481 }
1482 
toLowerCase() const1483 String String::toLowerCase() const
1484 {
1485     StringCreationHelper builder (text);
1486 
1487     for (;;)
1488     {
1489         auto c = builder.source.toLowerCase();
1490         builder.write (c);
1491 
1492         if (c == 0)
1493             break;
1494 
1495         ++(builder.source);
1496     }
1497 
1498     return std::move (builder.result);
1499 }
1500 
1501 //==============================================================================
getLastCharacter() const1502 juce_wchar String::getLastCharacter() const noexcept
1503 {
1504     return isEmpty() ? juce_wchar() : text [length() - 1];
1505 }
1506 
substring(int start,const int end) const1507 String String::substring (int start, const int end) const
1508 {
1509     if (start < 0)
1510         start = 0;
1511 
1512     if (end <= start)
1513         return {};
1514 
1515     int i = 0;
1516     auto t1 = text;
1517 
1518     while (i < start)
1519     {
1520         if (t1.isEmpty())
1521             return {};
1522 
1523         ++i;
1524         ++t1;
1525     }
1526 
1527     auto t2 = t1;
1528 
1529     while (i < end)
1530     {
1531         if (t2.isEmpty())
1532         {
1533             if (start == 0)
1534                 return *this;
1535 
1536             break;
1537         }
1538 
1539         ++i;
1540         ++t2;
1541     }
1542 
1543     return String (t1, t2);
1544 }
1545 
substring(int start) const1546 String String::substring (int start) const
1547 {
1548     if (start <= 0)
1549         return *this;
1550 
1551     auto t = text;
1552 
1553     while (--start >= 0)
1554     {
1555         if (t.isEmpty())
1556             return {};
1557 
1558         ++t;
1559     }
1560 
1561     return String (t);
1562 }
1563 
dropLastCharacters(const int numberToDrop) const1564 String String::dropLastCharacters (const int numberToDrop) const
1565 {
1566     return String (text, (size_t) jmax (0, length() - numberToDrop));
1567 }
1568 
getLastCharacters(const int numCharacters) const1569 String String::getLastCharacters (const int numCharacters) const
1570 {
1571     return String (text + jmax (0, length() - jmax (0, numCharacters)));
1572 }
1573 
fromFirstOccurrenceOf(StringRef sub,bool includeSubString,bool ignoreCase) const1574 String String::fromFirstOccurrenceOf (StringRef sub, bool includeSubString, bool ignoreCase) const
1575 {
1576     auto i = ignoreCase ? indexOfIgnoreCase (sub)
1577                         : indexOf (sub);
1578     if (i < 0)
1579         return {};
1580 
1581     return substring (includeSubString ? i : i + sub.length());
1582 }
1583 
fromLastOccurrenceOf(StringRef sub,bool includeSubString,bool ignoreCase) const1584 String String::fromLastOccurrenceOf (StringRef sub, bool includeSubString, bool ignoreCase) const
1585 {
1586     auto i = ignoreCase ? lastIndexOfIgnoreCase (sub)
1587                         : lastIndexOf (sub);
1588     if (i < 0)
1589         return *this;
1590 
1591     return substring (includeSubString ? i : i + sub.length());
1592 }
1593 
upToFirstOccurrenceOf(StringRef sub,bool includeSubString,bool ignoreCase) const1594 String String::upToFirstOccurrenceOf (StringRef sub, bool includeSubString, bool ignoreCase) const
1595 {
1596     auto i = ignoreCase ? indexOfIgnoreCase (sub)
1597                         : indexOf (sub);
1598     if (i < 0)
1599         return *this;
1600 
1601     return substring (0, includeSubString ? i + sub.length() : i);
1602 }
1603 
upToLastOccurrenceOf(StringRef sub,bool includeSubString,bool ignoreCase) const1604 String String::upToLastOccurrenceOf (StringRef sub, bool includeSubString, bool ignoreCase) const
1605 {
1606     auto i = ignoreCase ? lastIndexOfIgnoreCase (sub)
1607                         : lastIndexOf (sub);
1608     if (i < 0)
1609         return *this;
1610 
1611     return substring (0, includeSubString ? i + sub.length() : i);
1612 }
1613 
isQuoteCharacter(juce_wchar c)1614 static bool isQuoteCharacter (juce_wchar c) noexcept
1615 {
1616     return c == '"' || c == '\'';
1617 }
1618 
isQuotedString() const1619 bool String::isQuotedString() const
1620 {
1621     return isQuoteCharacter (*text.findEndOfWhitespace());
1622 }
1623 
unquoted() const1624 String String::unquoted() const
1625 {
1626     if (! isQuoteCharacter (*text))
1627         return *this;
1628 
1629     auto len = length();
1630     return substring (1, len - (isQuoteCharacter (text[len - 1]) ? 1 : 0));
1631 }
1632 
quoted(juce_wchar quoteCharacter) const1633 String String::quoted (juce_wchar quoteCharacter) const
1634 {
1635     if (isEmpty())
1636         return charToString (quoteCharacter) + quoteCharacter;
1637 
1638     String t (*this);
1639 
1640     if (! t.startsWithChar (quoteCharacter))
1641         t = charToString (quoteCharacter) + t;
1642 
1643     if (! t.endsWithChar (quoteCharacter))
1644         t += quoteCharacter;
1645 
1646     return t;
1647 }
1648 
1649 //==============================================================================
findTrimmedEnd(const String::CharPointerType start,String::CharPointerType end)1650 static String::CharPointerType findTrimmedEnd (const String::CharPointerType start,
1651                                                String::CharPointerType end)
1652 {
1653     while (end > start)
1654     {
1655         if (! (--end).isWhitespace())
1656         {
1657             ++end;
1658             break;
1659         }
1660     }
1661 
1662     return end;
1663 }
1664 
trim() const1665 String String::trim() const
1666 {
1667     if (isNotEmpty())
1668     {
1669         auto start = text.findEndOfWhitespace();
1670         auto end = start.findTerminatingNull();
1671         auto trimmedEnd = findTrimmedEnd (start, end);
1672 
1673         if (trimmedEnd <= start)
1674             return {};
1675 
1676         if (text < start || trimmedEnd < end)
1677             return String (start, trimmedEnd);
1678     }
1679 
1680     return *this;
1681 }
1682 
trimStart() const1683 String String::trimStart() const
1684 {
1685     if (isNotEmpty())
1686     {
1687         auto t = text.findEndOfWhitespace();
1688 
1689         if (t != text)
1690             return String (t);
1691     }
1692 
1693     return *this;
1694 }
1695 
trimEnd() const1696 String String::trimEnd() const
1697 {
1698     if (isNotEmpty())
1699     {
1700         auto end = text.findTerminatingNull();
1701         auto trimmedEnd = findTrimmedEnd (text, end);
1702 
1703         if (trimmedEnd < end)
1704             return String (text, trimmedEnd);
1705     }
1706 
1707     return *this;
1708 }
1709 
trimCharactersAtStart(StringRef charactersToTrim) const1710 String String::trimCharactersAtStart (StringRef charactersToTrim) const
1711 {
1712     auto t = text;
1713 
1714     while (charactersToTrim.text.indexOf (*t) >= 0)
1715         ++t;
1716 
1717     return t == text ? *this : String (t);
1718 }
1719 
trimCharactersAtEnd(StringRef charactersToTrim) const1720 String String::trimCharactersAtEnd (StringRef charactersToTrim) const
1721 {
1722     if (isNotEmpty())
1723     {
1724         auto end = text.findTerminatingNull();
1725         auto trimmedEnd = end;
1726 
1727         while (trimmedEnd > text)
1728         {
1729             if (charactersToTrim.text.indexOf (*--trimmedEnd) < 0)
1730             {
1731                 ++trimmedEnd;
1732                 break;
1733             }
1734         }
1735 
1736         if (trimmedEnd < end)
1737             return String (text, trimmedEnd);
1738     }
1739 
1740     return *this;
1741 }
1742 
1743 //==============================================================================
retainCharacters(StringRef charactersToRetain) const1744 String String::retainCharacters (StringRef charactersToRetain) const
1745 {
1746     if (isEmpty())
1747         return {};
1748 
1749     StringCreationHelper builder (text);
1750 
1751     for (;;)
1752     {
1753         auto c = builder.source.getAndAdvance();
1754 
1755         if (charactersToRetain.text.indexOf (c) >= 0)
1756             builder.write (c);
1757 
1758         if (c == 0)
1759             break;
1760     }
1761 
1762     builder.write (0);
1763     return std::move (builder.result);
1764 }
1765 
removeCharacters(StringRef charactersToRemove) const1766 String String::removeCharacters (StringRef charactersToRemove) const
1767 {
1768     if (isEmpty())
1769         return {};
1770 
1771     StringCreationHelper builder (text);
1772 
1773     for (;;)
1774     {
1775         auto c = builder.source.getAndAdvance();
1776 
1777         if (charactersToRemove.text.indexOf (c) < 0)
1778             builder.write (c);
1779 
1780         if (c == 0)
1781             break;
1782     }
1783 
1784     return std::move (builder.result);
1785 }
1786 
initialSectionContainingOnly(StringRef permittedCharacters) const1787 String String::initialSectionContainingOnly (StringRef permittedCharacters) const
1788 {
1789     for (auto t = text; ! t.isEmpty(); ++t)
1790         if (permittedCharacters.text.indexOf (*t) < 0)
1791             return String (text, t);
1792 
1793     return *this;
1794 }
1795 
initialSectionNotContaining(StringRef charactersToStopAt) const1796 String String::initialSectionNotContaining (StringRef charactersToStopAt) const
1797 {
1798     for (auto t = text; ! t.isEmpty(); ++t)
1799         if (charactersToStopAt.text.indexOf (*t) >= 0)
1800             return String (text, t);
1801 
1802     return *this;
1803 }
1804 
containsOnly(StringRef chars) const1805 bool String::containsOnly (StringRef chars) const noexcept
1806 {
1807     for (auto t = text; ! t.isEmpty();)
1808         if (chars.text.indexOf (t.getAndAdvance()) < 0)
1809             return false;
1810 
1811     return true;
1812 }
1813 
containsAnyOf(StringRef chars) const1814 bool String::containsAnyOf (StringRef chars) const noexcept
1815 {
1816     for (auto t = text; ! t.isEmpty();)
1817         if (chars.text.indexOf (t.getAndAdvance()) >= 0)
1818             return true;
1819 
1820     return false;
1821 }
1822 
containsNonWhitespaceChars() const1823 bool String::containsNonWhitespaceChars() const noexcept
1824 {
1825     for (auto t = text; ! t.isEmpty(); ++t)
1826         if (! t.isWhitespace())
1827             return true;
1828 
1829     return false;
1830 }
1831 
formattedRaw(const char * pf,...)1832 String String::formattedRaw (const char* pf, ...)
1833 {
1834     size_t bufferSize = 256;
1835 
1836     for (;;)
1837     {
1838         va_list args;
1839         va_start (args, pf);
1840 
1841        #if JUCE_WINDOWS
1842         JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
1843        #endif
1844 
1845       #if JUCE_ANDROID
1846         HeapBlock<char> temp (bufferSize);
1847         int num = (int) vsnprintf (temp.get(), bufferSize - 1, pf, args);
1848         if (num >= static_cast<int> (bufferSize))
1849             num = -1;
1850       #else
1851         String wideCharVersion (pf);
1852         HeapBlock<wchar_t> temp (bufferSize);
1853         const int num = (int)
1854        #if JUCE_WINDOWS
1855             _vsnwprintf
1856        #else
1857             vswprintf
1858        #endif
1859                 (temp.get(), bufferSize - 1, wideCharVersion.toWideCharPointer(), args);
1860       #endif
1861 
1862        #if JUCE_WINDOWS
1863         JUCE_END_IGNORE_WARNINGS_GCC_LIKE
1864        #endif
1865         va_end (args);
1866 
1867         if (num > 0)
1868             return String (temp.get());
1869 
1870         bufferSize += 256;
1871 
1872         if (num == 0 || bufferSize > 65536) // the upper limit is a sanity check to avoid situations where vprintf repeatedly
1873             break;                          // returns -1 because of an error rather than because it needs more space.
1874     }
1875 
1876     return {};
1877 }
1878 
1879 //==============================================================================
getIntValue() const1880 int String::getIntValue() const noexcept            { return text.getIntValue32(); }
getLargeIntValue() const1881 int64 String::getLargeIntValue() const noexcept     { return text.getIntValue64(); }
getFloatValue() const1882 float String::getFloatValue() const noexcept        { return (float) getDoubleValue(); }
getDoubleValue() const1883 double String::getDoubleValue() const noexcept      { return text.getDoubleValue(); }
1884 
getTrailingIntValue() const1885 int String::getTrailingIntValue() const noexcept
1886 {
1887     int n = 0;
1888     int mult = 1;
1889     auto t = text.findTerminatingNull();
1890 
1891     while (--t >= text)
1892     {
1893         if (! t.isDigit())
1894         {
1895             if (*t == '-')
1896                 n = -n;
1897 
1898             break;
1899         }
1900 
1901         n += (int) (((juce_wchar) mult) * (*t - '0'));
1902         mult *= 10;
1903     }
1904 
1905     return n;
1906 }
1907 
1908 static const char hexDigits[] = "0123456789abcdef";
1909 
1910 template <typename Type>
hexToString(Type v)1911 static String hexToString (Type v)
1912 {
1913     String::CharPointerType::CharType buffer[32];
1914     auto* end = buffer + numElementsInArray (buffer) - 1;
1915     auto* t = end;
1916     *t = 0;
1917 
1918     do
1919     {
1920         *--t = hexDigits [(int) (v & 15)];
1921         v = static_cast<Type> (v >> 4);
1922 
1923     } while (v != 0);
1924 
1925     return String (String::CharPointerType (t),
1926                    String::CharPointerType (end));
1927 }
1928 
createHex(uint8 n)1929 String String::createHex (uint8 n)    { return hexToString (n); }
createHex(uint16 n)1930 String String::createHex (uint16 n)   { return hexToString (n); }
createHex(uint32 n)1931 String String::createHex (uint32 n)   { return hexToString (n); }
createHex(uint64 n)1932 String String::createHex (uint64 n)   { return hexToString (n); }
1933 
toHexString(const void * const d,const int size,const int groupSize)1934 String String::toHexString (const void* const d, const int size, const int groupSize)
1935 {
1936     if (size <= 0)
1937         return {};
1938 
1939     int numChars = (size * 2) + 2;
1940     if (groupSize > 0)
1941         numChars += size / groupSize;
1942 
1943     String s (PreallocationBytes ((size_t) numChars * sizeof (CharPointerType::CharType)));
1944 
1945     auto* data = static_cast<const unsigned char*> (d);
1946     auto dest = s.text;
1947 
1948     for (int i = 0; i < size; ++i)
1949     {
1950         const unsigned char nextByte = *data++;
1951         dest.write ((juce_wchar) hexDigits [nextByte >> 4]);
1952         dest.write ((juce_wchar) hexDigits [nextByte & 0xf]);
1953 
1954         if (groupSize > 0 && (i % groupSize) == (groupSize - 1) && i < (size - 1))
1955             dest.write ((juce_wchar) ' ');
1956     }
1957 
1958     dest.writeNull();
1959     return s;
1960 }
1961 
getHexValue32() const1962 int   String::getHexValue32() const noexcept    { return CharacterFunctions::HexParser<int>  ::parse (text); }
getHexValue64() const1963 int64 String::getHexValue64() const noexcept    { return CharacterFunctions::HexParser<int64>::parse (text); }
1964 
1965 //==============================================================================
getStringFromWindows1252Codepage(const char * data,size_t num)1966 static String getStringFromWindows1252Codepage (const char* data, size_t num)
1967 {
1968     HeapBlock<juce_wchar> unicode (num + 1);
1969 
1970     for (size_t i = 0; i < num; ++i)
1971         unicode[i] = CharacterFunctions::getUnicodeCharFromWindows1252Codepage ((uint8) data[i]);
1972 
1973     unicode[num] = 0;
1974     return CharPointer_UTF32 (unicode);
1975 }
1976 
createStringFromData(const void * const unknownData,int size)1977 String String::createStringFromData (const void* const unknownData, int size)
1978 {
1979     auto* data = static_cast<const uint8*> (unknownData);
1980 
1981     if (size <= 0 || data == nullptr)
1982         return {};
1983 
1984     if (size == 1)
1985         return charToString ((juce_wchar) data[0]);
1986 
1987     if (CharPointer_UTF16::isByteOrderMarkBigEndian (data)
1988          || CharPointer_UTF16::isByteOrderMarkLittleEndian (data))
1989     {
1990         const int numChars = size / 2 - 1;
1991 
1992         StringCreationHelper builder ((size_t) numChars);
1993 
1994         auto src = unalignedPointerCast<const uint16*> (data + 2);
1995 
1996         if (CharPointer_UTF16::isByteOrderMarkBigEndian (data))
1997         {
1998             for (int i = 0; i < numChars; ++i)
1999                 builder.write ((juce_wchar) ByteOrder::swapIfLittleEndian (src[i]));
2000         }
2001         else
2002         {
2003             for (int i = 0; i < numChars; ++i)
2004                 builder.write ((juce_wchar) ByteOrder::swapIfBigEndian (src[i]));
2005         }
2006 
2007         builder.write (0);
2008         return std::move (builder.result);
2009     }
2010 
2011     auto* start = (const char*) data;
2012 
2013     if (size >= 3 && CharPointer_UTF8::isByteOrderMark (data))
2014     {
2015         start += 3;
2016         size -= 3;
2017     }
2018 
2019     if (CharPointer_UTF8::isValidString (start, size))
2020         return String (CharPointer_UTF8 (start),
2021                        CharPointer_UTF8 (start + size));
2022 
2023     return getStringFromWindows1252Codepage (start, (size_t) size);
2024 }
2025 
2026 //==============================================================================
2027 static const juce_wchar emptyChar = 0;
2028 
2029 template <class CharPointerType_Src, class CharPointerType_Dest>
2030 struct StringEncodingConverter
2031 {
convertjuce::StringEncodingConverter2032     static CharPointerType_Dest convert (const String& s)
2033     {
2034         auto& source = const_cast<String&> (s);
2035 
2036         using DestChar = typename CharPointerType_Dest::CharType;
2037 
2038         if (source.isEmpty())
2039             return CharPointerType_Dest (reinterpret_cast<const DestChar*> (&emptyChar));
2040 
2041         CharPointerType_Src text (source.getCharPointer());
2042         auto extraBytesNeeded = CharPointerType_Dest::getBytesRequiredFor (text) + sizeof (typename CharPointerType_Dest::CharType);
2043         auto endOffset = (text.sizeInBytes() + 3) & ~3u; // the new string must be word-aligned or many Windows
2044                                                          // functions will fail to read it correctly!
2045         source.preallocateBytes (endOffset + extraBytesNeeded);
2046         text = source.getCharPointer();
2047 
2048         void* const newSpace = addBytesToPointer (text.getAddress(), (int) endOffset);
2049         const CharPointerType_Dest extraSpace (static_cast<DestChar*> (newSpace));
2050 
2051        #if JUCE_DEBUG // (This just avoids spurious warnings from valgrind about the uninitialised bytes at the end of the buffer..)
2052         auto bytesToClear = (size_t) jmin ((int) extraBytesNeeded, 4);
2053         zeromem (addBytesToPointer (newSpace, extraBytesNeeded - bytesToClear), bytesToClear);
2054        #endif
2055 
2056         CharPointerType_Dest (extraSpace).writeAll (text);
2057         return extraSpace;
2058     }
2059 };
2060 
2061 template <>
2062 struct StringEncodingConverter<CharPointer_UTF8, CharPointer_UTF8>
2063 {
convertjuce::StringEncodingConverter2064     static CharPointer_UTF8 convert (const String& source) noexcept   { return CharPointer_UTF8 (unalignedPointerCast<CharPointer_UTF8::CharType*> (source.getCharPointer().getAddress())); }
2065 };
2066 
2067 template <>
2068 struct StringEncodingConverter<CharPointer_UTF16, CharPointer_UTF16>
2069 {
convertjuce::StringEncodingConverter2070     static CharPointer_UTF16 convert (const String& source) noexcept  { return CharPointer_UTF16 (unalignedPointerCast<CharPointer_UTF16::CharType*> (source.getCharPointer().getAddress())); }
2071 };
2072 
2073 template <>
2074 struct StringEncodingConverter<CharPointer_UTF32, CharPointer_UTF32>
2075 {
convertjuce::StringEncodingConverter2076     static CharPointer_UTF32 convert (const String& source) noexcept  { return CharPointer_UTF32 (unalignedPointerCast<CharPointer_UTF32::CharType*> (source.getCharPointer().getAddress())); }
2077 };
2078 
toUTF8() const2079 CharPointer_UTF8  String::toUTF8()  const { return StringEncodingConverter<CharPointerType, CharPointer_UTF8 >::convert (*this); }
toUTF16() const2080 CharPointer_UTF16 String::toUTF16() const { return StringEncodingConverter<CharPointerType, CharPointer_UTF16>::convert (*this); }
toUTF32() const2081 CharPointer_UTF32 String::toUTF32() const { return StringEncodingConverter<CharPointerType, CharPointer_UTF32>::convert (*this); }
2082 
toRawUTF8() const2083 const char* String::toRawUTF8() const
2084 {
2085     return toUTF8().getAddress();
2086 }
2087 
toWideCharPointer() const2088 const wchar_t* String::toWideCharPointer() const
2089 {
2090     return StringEncodingConverter<CharPointerType, CharPointer_wchar_t>::convert (*this).getAddress();
2091 }
2092 
toStdString() const2093 std::string String::toStdString() const
2094 {
2095     return std::string (toRawUTF8());
2096 }
2097 
2098 //==============================================================================
2099 template <class CharPointerType_Src, class CharPointerType_Dest>
2100 struct StringCopier
2101 {
copyToBufferjuce::StringCopier2102     static size_t copyToBuffer (const CharPointerType_Src source, typename CharPointerType_Dest::CharType* const buffer, const size_t maxBufferSizeBytes)
2103     {
2104         jassert (((ssize_t) maxBufferSizeBytes) >= 0); // keep this value positive!
2105 
2106         if (buffer == nullptr)
2107             return CharPointerType_Dest::getBytesRequiredFor (source) + sizeof (typename CharPointerType_Dest::CharType);
2108 
2109         return CharPointerType_Dest (buffer).writeWithDestByteLimit (source, maxBufferSizeBytes);
2110     }
2111 };
2112 
copyToUTF8(CharPointer_UTF8::CharType * const buffer,size_t maxBufferSizeBytes) const2113 size_t String::copyToUTF8 (CharPointer_UTF8::CharType* const buffer, size_t maxBufferSizeBytes) const noexcept
2114 {
2115     return StringCopier<CharPointerType, CharPointer_UTF8>::copyToBuffer (text, buffer, maxBufferSizeBytes);
2116 }
2117 
copyToUTF16(CharPointer_UTF16::CharType * const buffer,size_t maxBufferSizeBytes) const2118 size_t String::copyToUTF16 (CharPointer_UTF16::CharType* const buffer, size_t maxBufferSizeBytes) const noexcept
2119 {
2120     return StringCopier<CharPointerType, CharPointer_UTF16>::copyToBuffer (text, buffer, maxBufferSizeBytes);
2121 }
2122 
copyToUTF32(CharPointer_UTF32::CharType * const buffer,size_t maxBufferSizeBytes) const2123 size_t String::copyToUTF32 (CharPointer_UTF32::CharType* const buffer, size_t maxBufferSizeBytes) const noexcept
2124 {
2125     return StringCopier<CharPointerType, CharPointer_UTF32>::copyToBuffer (text, buffer, maxBufferSizeBytes);
2126 }
2127 
2128 //==============================================================================
getNumBytesAsUTF8() const2129 size_t String::getNumBytesAsUTF8() const noexcept
2130 {
2131     return CharPointer_UTF8::getBytesRequiredFor (text);
2132 }
2133 
fromUTF8(const char * const buffer,int bufferSizeBytes)2134 String String::fromUTF8 (const char* const buffer, int bufferSizeBytes)
2135 {
2136     if (buffer != nullptr)
2137     {
2138         if (bufferSizeBytes < 0)
2139             return String (CharPointer_UTF8 (buffer));
2140 
2141         if (bufferSizeBytes > 0)
2142         {
2143             jassert (CharPointer_UTF8::isValidString (buffer, bufferSizeBytes));
2144             return String (CharPointer_UTF8 (buffer), CharPointer_UTF8 (buffer + bufferSizeBytes));
2145         }
2146     }
2147 
2148     return {};
2149 }
2150 
2151 JUCE_END_IGNORE_WARNINGS_MSVC
2152 
2153 //==============================================================================
StringRef()2154 StringRef::StringRef() noexcept  : text ((const String::CharPointerType::CharType*) "\0\0\0")
2155 {
2156 }
2157 
StringRef(const char * stringLiteral)2158 StringRef::StringRef (const char* stringLiteral) noexcept
2159    #if JUCE_STRING_UTF_TYPE != 8
2160     : text (nullptr), stringCopy (stringLiteral)
2161    #else
2162     : text (stringLiteral)
2163    #endif
2164 {
2165    #if JUCE_STRING_UTF_TYPE != 8
2166     text = stringCopy.getCharPointer();
2167    #endif
2168 
2169     jassert (stringLiteral != nullptr); // This must be a valid string literal, not a null pointer!!
2170 
2171    #if JUCE_NATIVE_WCHAR_IS_UTF8
2172     /*  If you get an assertion here, then you're trying to create a string from 8-bit data
2173         that contains values greater than 127. These can NOT be correctly converted to unicode
2174         because there's no way for the String class to know what encoding was used to
2175         create them. The source data could be UTF-8, ASCII or one of many local code-pages.
2176 
2177         To get around this problem, you must be more explicit when you pass an ambiguous 8-bit
2178         string to the StringRef class - so for example if your source data is actually UTF-8,
2179         you'd call StringRef (CharPointer_UTF8 ("my utf8 string..")), and it would be able to
2180         correctly convert the multi-byte characters to unicode. It's *highly* recommended that
2181         you use UTF-8 with escape characters in your source code to represent extended characters,
2182         because there's no other way to represent these strings in a way that isn't dependent on
2183         the compiler, source code editor and platform.
2184     */
2185     jassert (CharPointer_ASCII::isValidString (stringLiteral, std::numeric_limits<int>::max()));
2186    #endif
2187 }
2188 
StringRef(String::CharPointerType stringLiteral)2189 StringRef::StringRef (String::CharPointerType stringLiteral) noexcept  : text (stringLiteral)
2190 {
2191     jassert (stringLiteral.getAddress() != nullptr); // This must be a valid string literal, not a null pointer!!
2192 }
2193 
StringRef(const String & string)2194 StringRef::StringRef (const String& string) noexcept   : text (string.getCharPointer()) {}
StringRef(const std::string & string)2195 StringRef::StringRef (const std::string& string)       : StringRef (string.c_str()) {}
2196 
2197 //==============================================================================
2198 
reduceLengthOfFloatString(const String & input)2199 static String reduceLengthOfFloatString (const String& input)
2200 {
2201     const auto start = input.getCharPointer();
2202     const auto end = start + (int) input.length();
2203     auto trimStart = end;
2204     auto trimEnd = trimStart;
2205     auto exponentTrimStart = end;
2206     auto exponentTrimEnd = exponentTrimStart;
2207 
2208     decltype (*start) currentChar = '\0';
2209 
2210     for (auto c = end - 1; c > start; --c)
2211     {
2212         currentChar = *c;
2213 
2214         if (currentChar == '0' && c + 1 == trimStart)
2215         {
2216             --trimStart;
2217         }
2218         else if (currentChar == '.')
2219         {
2220             if (trimStart == c + 1 && trimStart != end && *trimStart == '0')
2221                 ++trimStart;
2222 
2223             break;
2224         }
2225         else if (currentChar == 'e' || currentChar == 'E')
2226         {
2227             auto cNext = c + 1;
2228 
2229             if (cNext != end)
2230             {
2231                 if (*cNext == '-')
2232                     ++cNext;
2233 
2234                 exponentTrimStart = cNext;
2235 
2236                 if (cNext != end && *cNext == '+')
2237                     ++cNext;
2238 
2239                 exponentTrimEnd = cNext;
2240             }
2241 
2242             while (cNext != end && *cNext++ == '0')
2243                 exponentTrimEnd = cNext;
2244 
2245             if (exponentTrimEnd == end)
2246                 exponentTrimStart = c;
2247 
2248             trimStart = c;
2249             trimEnd = trimStart;
2250         }
2251     }
2252 
2253     if ((trimStart != trimEnd && currentChar == '.') || exponentTrimStart != exponentTrimEnd)
2254     {
2255         if (trimStart == trimEnd)
2256             return String (start, exponentTrimStart) + String (exponentTrimEnd, end);
2257 
2258         if (exponentTrimStart == exponentTrimEnd)
2259             return String (start, trimStart) + String (trimEnd, end);
2260 
2261         if (trimEnd == exponentTrimStart)
2262             return String (start, trimStart) + String (exponentTrimEnd, end);
2263 
2264         return String (start, trimStart) + String (trimEnd, exponentTrimStart) + String (exponentTrimEnd, end);
2265     }
2266 
2267     return input;
2268 }
2269 
serialiseDouble(double input)2270 static String serialiseDouble (double input)
2271 {
2272     auto absInput = std::abs (input);
2273 
2274     if (absInput >= 1.0e6 || absInput <= 1.0e-5)
2275         return reduceLengthOfFloatString ({ input, 15, true });
2276 
2277     int intInput = (int) input;
2278 
2279     if ((double) intInput == input)
2280         return { input, 1 };
2281 
2282     auto numberOfDecimalPlaces = [absInput]
2283     {
2284         if (absInput < 1.0)
2285         {
2286             if (absInput >= 1.0e-3)
2287             {
2288                 if (absInput >= 1.0e-1) return 16;
2289                 if (absInput >= 1.0e-2) return 17;
2290                 return 18;
2291             }
2292 
2293             if (absInput >= 1.0e-4) return 19;
2294             return 20;
2295         }
2296 
2297         if (absInput < 1.0e3)
2298         {
2299             if (absInput < 1.0e1) return 15;
2300             if (absInput < 1.0e2) return 14;
2301             return 13;
2302         }
2303 
2304         if (absInput < 1.0e4) return 12;
2305         if (absInput < 1.0e5) return 11;
2306         return 10;
2307     }();
2308 
2309     return reduceLengthOfFloatString (String (input, numberOfDecimalPlaces));
2310 }
2311 
2312 
2313 //==============================================================================
2314 //==============================================================================
2315 #if JUCE_UNIT_TESTS
2316 
2317 #define STRINGIFY2(X) #X
2318 #define STRINGIFY(X) STRINGIFY2(X)
2319 
2320 class StringTests  : public UnitTest
2321 {
2322 public:
StringTests()2323     StringTests()
2324         : UnitTest ("String class", UnitTestCategories::text)
2325     {}
2326 
2327     template <class CharPointerType>
2328     struct TestUTFConversion
2329     {
testjuce::StringTests::TestUTFConversion2330         static void test (UnitTest& test, Random& r)
2331         {
2332             String s (createRandomWideCharString (r));
2333 
2334             typename CharPointerType::CharType buffer [300];
2335 
2336             memset (buffer, 0xff, sizeof (buffer));
2337             CharPointerType (buffer).writeAll (s.toUTF32());
2338             test.expectEquals (String (CharPointerType (buffer)), s);
2339 
2340             memset (buffer, 0xff, sizeof (buffer));
2341             CharPointerType (buffer).writeAll (s.toUTF16());
2342             test.expectEquals (String (CharPointerType (buffer)), s);
2343 
2344             memset (buffer, 0xff, sizeof (buffer));
2345             CharPointerType (buffer).writeAll (s.toUTF8());
2346             test.expectEquals (String (CharPointerType (buffer)), s);
2347 
2348             test.expect (CharPointerType::isValidString (buffer, (int) strlen ((const char*) buffer)));
2349         }
2350     };
2351 
createRandomWideCharString(Random & r)2352     static String createRandomWideCharString (Random& r)
2353     {
2354         juce_wchar buffer[50] = { 0 };
2355 
2356         for (int i = 0; i < numElementsInArray (buffer) - 1; ++i)
2357         {
2358             if (r.nextBool())
2359             {
2360                 do
2361                 {
2362                     buffer[i] = (juce_wchar) (1 + r.nextInt (0x10ffff - 1));
2363                 }
2364                 while (! CharPointer_UTF16::canRepresent (buffer[i]));
2365             }
2366             else
2367                 buffer[i] = (juce_wchar) (1 + r.nextInt (0xff));
2368         }
2369 
2370         return CharPointer_UTF32 (buffer);
2371     }
2372 
runTest()2373     void runTest() override
2374     {
2375         Random r = getRandom();
2376 
2377         {
2378             beginTest ("Basics");
2379 
2380             expect (String().length() == 0);
2381             expect (String() == String());
2382             String s1, s2 ("abcd");
2383             expect (s1.isEmpty() && ! s1.isNotEmpty());
2384             expect (s2.isNotEmpty() && ! s2.isEmpty());
2385             expect (s2.length() == 4);
2386             s1 = "abcd";
2387             expect (s2 == s1 && s1 == s2);
2388             expect (s1 == "abcd" && s1 == L"abcd");
2389             expect (String ("abcd") == String (L"abcd"));
2390             expect (String ("abcdefg", 4) == L"abcd");
2391             expect (String ("abcdefg", 4) == String (L"abcdefg", 4));
2392             expect (String::charToString ('x') == "x");
2393             expect (String::charToString (0) == String());
2394             expect (s2 + "e" == "abcde" && s2 + 'e' == "abcde");
2395             expect (s2 + L'e' == "abcde" && s2 + L"e" == "abcde");
2396             expect (s1.equalsIgnoreCase ("abcD") && s1 < "abce" && s1 > "abbb");
2397             expect (s1.startsWith ("ab") && s1.startsWith ("abcd") && ! s1.startsWith ("abcde"));
2398             expect (s1.startsWithIgnoreCase ("aB") && s1.endsWithIgnoreCase ("CD"));
2399             expect (s1.endsWith ("bcd") && ! s1.endsWith ("aabcd"));
2400             expectEquals (s1.indexOf (String()), 0);
2401             expectEquals (s1.indexOfIgnoreCase (String()), 0);
2402             expect (s1.startsWith (String()) && s1.endsWith (String()) && s1.contains (String()));
2403             expect (s1.contains ("cd") && s1.contains ("ab") && s1.contains ("abcd"));
2404             expect (s1.containsChar ('a'));
2405             expect (! s1.containsChar ('x'));
2406             expect (! s1.containsChar (0));
2407             expect (String ("abc foo bar").containsWholeWord ("abc") && String ("abc foo bar").containsWholeWord ("abc"));
2408         }
2409 
2410         {
2411             beginTest ("Operations");
2412 
2413             String s ("012345678");
2414             expect (s.hashCode() != 0);
2415             expect (s.hashCode64() != 0);
2416             expect (s.hashCode() != (s + s).hashCode());
2417             expect (s.hashCode64() != (s + s).hashCode64());
2418             expect (s.compare (String ("012345678")) == 0);
2419             expect (s.compare (String ("012345679")) < 0);
2420             expect (s.compare (String ("012345676")) > 0);
2421             expect (String("a").compareNatural ("A") == 0);
2422             expect (String("A").compareNatural ("B") < 0);
2423             expect (String("a").compareNatural ("B") < 0);
2424             expect (String("10").compareNatural ("2") > 0);
2425             expect (String("Abc 10").compareNatural ("aBC 2") > 0);
2426             expect (String("Abc 1").compareNatural ("aBC 2") < 0);
2427             expect (s.substring (2, 3) == String::charToString (s[2]));
2428             expect (s.substring (0, 1) == String::charToString (s[0]));
2429             expect (s.getLastCharacter() == s [s.length() - 1]);
2430             expect (String::charToString (s.getLastCharacter()) == s.getLastCharacters (1));
2431             expect (s.substring (0, 3) == L"012");
2432             expect (s.substring (0, 100) == s);
2433             expect (s.substring (-1, 100) == s);
2434             expect (s.substring (3) == "345678");
2435             expect (s.indexOf (String (L"45")) == 4);
2436             expect (String ("444445").indexOf ("45") == 4);
2437             expect (String ("444445").lastIndexOfChar ('4') == 4);
2438             expect (String ("45454545x").lastIndexOf (String (L"45")) == 6);
2439             expect (String ("45454545x").lastIndexOfAnyOf ("456") == 7);
2440             expect (String ("45454545x").lastIndexOfAnyOf (String (L"456x")) == 8);
2441             expect (String ("abABaBaBa").lastIndexOfIgnoreCase ("aB") == 6);
2442             expect (s.indexOfChar (L'4') == 4);
2443             expect (s + s == "012345678012345678");
2444             expect (s.startsWith (s));
2445             expect (s.startsWith (s.substring (0, 4)));
2446             expect (s.startsWith (s.dropLastCharacters (4)));
2447             expect (s.endsWith (s.substring (5)));
2448             expect (s.endsWith (s));
2449             expect (s.contains (s.substring (3, 6)));
2450             expect (s.contains (s.substring (3)));
2451             expect (s.startsWithChar (s[0]));
2452             expect (s.endsWithChar (s.getLastCharacter()));
2453             expect (s [s.length()] == 0);
2454             expect (String ("abcdEFGH").toLowerCase() == String ("abcdefgh"));
2455             expect (String ("abcdEFGH").toUpperCase() == String ("ABCDEFGH"));
2456 
2457             expect (String (StringRef ("abc")) == "abc");
2458             expect (String (StringRef ("abc")) == StringRef ("abc"));
2459             expect (String ("abc") + StringRef ("def") == "abcdef");
2460 
2461             String s2 ("123");
2462             s2 << ((int) 4) << ((short) 5) << "678" << L"9" << '0';
2463             s2 += "xyz";
2464             expect (s2 == "1234567890xyz");
2465             s2 += (int) 123;
2466             expect (s2 == "1234567890xyz123");
2467             s2 += (int64) 123;
2468             expect (s2 == "1234567890xyz123123");
2469             s2 << StringRef ("def");
2470             expect (s2 == "1234567890xyz123123def");
2471 
2472             // int16
2473             {
2474                 String numStr (std::numeric_limits<int16>::max());
2475                 expect (numStr == "32767");
2476             }
2477             {
2478                 String numStr (std::numeric_limits<int16>::min());
2479                 expect (numStr == "-32768");
2480             }
2481             {
2482                 String numStr;
2483                 numStr << std::numeric_limits<int16>::max();
2484                 expect (numStr == "32767");
2485             }
2486             {
2487                 String numStr;
2488                 numStr << std::numeric_limits<int16>::min();
2489                 expect (numStr == "-32768");
2490             }
2491             // int32
2492             {
2493                 String numStr (std::numeric_limits<int32>::max());
2494                 expect (numStr == "2147483647");
2495             }
2496             {
2497                 String numStr (std::numeric_limits<int32>::min());
2498                 expect (numStr == "-2147483648");
2499             }
2500             {
2501                 String numStr;
2502                 numStr << std::numeric_limits<int32>::max();
2503                 expect (numStr == "2147483647");
2504             }
2505             {
2506                 String numStr;
2507                 numStr << std::numeric_limits<int32>::min();
2508                 expect (numStr == "-2147483648");
2509             }
2510             // uint32
2511             {
2512                 String numStr (std::numeric_limits<uint32>::max());
2513                 expect (numStr == "4294967295");
2514             }
2515             {
2516                 String numStr (std::numeric_limits<uint32>::min());
2517                 expect (numStr == "0");
2518             }
2519             // int64
2520             {
2521                 String numStr (std::numeric_limits<int64>::max());
2522                 expect (numStr == "9223372036854775807");
2523             }
2524             {
2525                 String numStr (std::numeric_limits<int64>::min());
2526                 expect (numStr == "-9223372036854775808");
2527             }
2528             {
2529                 String numStr;
2530                 numStr << std::numeric_limits<int64>::max();
2531                 expect (numStr == "9223372036854775807");
2532             }
2533             {
2534                 String numStr;
2535                 numStr << std::numeric_limits<int64>::min();
2536                 expect (numStr == "-9223372036854775808");
2537             }
2538             // uint64
2539             {
2540                 String numStr (std::numeric_limits<uint64>::max());
2541                 expect (numStr == "18446744073709551615");
2542             }
2543             {
2544                 String numStr (std::numeric_limits<uint64>::min());
2545                 expect (numStr == "0");
2546             }
2547             {
2548                 String numStr;
2549                 numStr << std::numeric_limits<uint64>::max();
2550                 expect (numStr == "18446744073709551615");
2551             }
2552             {
2553                 String numStr;
2554                 numStr << std::numeric_limits<uint64>::min();
2555                 expect (numStr == "0");
2556             }
2557             // size_t
2558             {
2559                 String numStr (std::numeric_limits<size_t>::min());
2560                 expect (numStr == "0");
2561             }
2562 
2563             beginTest ("Numeric conversions");
2564             expect (String().getIntValue() == 0);
2565             expect (String().getDoubleValue() == 0.0);
2566             expect (String().getFloatValue() == 0.0f);
2567             expect (s.getIntValue() == 12345678);
2568             expect (s.getLargeIntValue() == (int64) 12345678);
2569             expect (s.getDoubleValue() == 12345678.0);
2570             expect (s.getFloatValue() == 12345678.0f);
2571             expect (String (-1234).getIntValue() == -1234);
2572             expect (String ((int64) -1234).getLargeIntValue() == -1234);
2573             expect (String (-1234.56).getDoubleValue() == -1234.56);
2574             expect (String (-1234.56f).getFloatValue() == -1234.56f);
2575             expect (String (std::numeric_limits<int>::max()).getIntValue() == std::numeric_limits<int>::max());
2576             expect (String (std::numeric_limits<int>::min()).getIntValue() == std::numeric_limits<int>::min());
2577             expect (String (std::numeric_limits<int64>::max()).getLargeIntValue() == std::numeric_limits<int64>::max());
2578             expect (String (std::numeric_limits<int64>::min()).getLargeIntValue() == std::numeric_limits<int64>::min());
2579             expect (("xyz" + s).getTrailingIntValue() == s.getIntValue());
2580             expect (s.getHexValue32() == 0x12345678);
2581             expect (s.getHexValue64() == (int64) 0x12345678);
2582             expect (String::toHexString (0x1234abcd).equalsIgnoreCase ("1234abcd"));
2583             expect (String::toHexString ((int64) 0x1234abcd).equalsIgnoreCase ("1234abcd"));
2584             expect (String::toHexString ((short) 0x12ab).equalsIgnoreCase ("12ab"));
2585             expect (String::toHexString ((size_t) 0x12ab).equalsIgnoreCase ("12ab"));
2586             expect (String::toHexString ((long) 0x12ab).equalsIgnoreCase ("12ab"));
2587             expect (String::toHexString ((int8)  -1).equalsIgnoreCase ("ff"));
2588             expect (String::toHexString ((int16) -1).equalsIgnoreCase ("ffff"));
2589             expect (String::toHexString ((int32) -1).equalsIgnoreCase ("ffffffff"));
2590             expect (String::toHexString ((int64) -1).equalsIgnoreCase ("ffffffffffffffff"));
2591 
2592             unsigned char data[] = { 1, 2, 3, 4, 0xa, 0xb, 0xc, 0xd };
2593             expect (String::toHexString (data, 8, 0).equalsIgnoreCase ("010203040a0b0c0d"));
2594             expect (String::toHexString (data, 8, 1).equalsIgnoreCase ("01 02 03 04 0a 0b 0c 0d"));
2595             expect (String::toHexString (data, 8, 2).equalsIgnoreCase ("0102 0304 0a0b 0c0d"));
2596 
2597             expectEquals (String (12345.67, 4),     String ("12345.6700"));
2598             expectEquals (String (12345.67, 6),     String ("12345.670000"));
2599             expectEquals (String (2589410.5894, 7), String ("2589410.5894000"));
2600             expectEquals (String (12345.67, 8),     String ("12345.67000000"));
2601             expectEquals (String (1e19, 4),         String ("10000000000000000000.0000"));
2602             expectEquals (String (1e-34, 36),       String ("0.000000000000000000000000000000000100"));
2603             expectEquals (String (1.39, 1),         String ("1.4"));
2604 
2605             expectEquals (String (12345.67, 4,     true), String ("1.2346e+04"));
2606             expectEquals (String (12345.67, 6,     true), String ("1.234567e+04"));
2607             expectEquals (String (2589410.5894, 7, true), String ("2.5894106e+06"));
2608             expectEquals (String (12345.67, 8,     true), String ("1.23456700e+04"));
2609             expectEquals (String (1e19, 4,         true), String ("1.0000e+19"));
2610             expectEquals (String (1e-34, 5,        true), String ("1.00000e-34"));
2611             expectEquals (String (1.39, 1,         true), String ("1.4e+00"));
2612 
2613             beginTest ("Subsections");
2614             String s3;
2615             s3 = "abcdeFGHIJ";
2616             expect (s3.equalsIgnoreCase ("ABCdeFGhiJ"));
2617             expect (s3.compareIgnoreCase (L"ABCdeFGhiJ") == 0);
2618             expect (s3.containsIgnoreCase (s3.substring (3)));
2619             expect (s3.indexOfAnyOf ("xyzf", 2, true) == 5);
2620             expect (s3.indexOfAnyOf (String (L"xyzf"), 2, false) == -1);
2621             expect (s3.indexOfAnyOf ("xyzF", 2, false) == 5);
2622             expect (s3.containsAnyOf (String (L"zzzFs")));
2623             expect (s3.startsWith ("abcd"));
2624             expect (s3.startsWithIgnoreCase (String (L"abCD")));
2625             expect (s3.startsWith (String()));
2626             expect (s3.startsWithChar ('a'));
2627             expect (s3.endsWith (String ("HIJ")));
2628             expect (s3.endsWithIgnoreCase (String (L"Hij")));
2629             expect (s3.endsWith (String()));
2630             expect (s3.endsWithChar (L'J'));
2631             expect (s3.indexOf ("HIJ") == 7);
2632             expect (s3.indexOf (String (L"HIJK")) == -1);
2633             expect (s3.indexOfIgnoreCase ("hij") == 7);
2634             expect (s3.indexOfIgnoreCase (String (L"hijk")) == -1);
2635             expect (s3.toStdString() == s3.toRawUTF8());
2636 
2637             String s4 (s3);
2638             s4.append (String ("xyz123"), 3);
2639             expect (s4 == s3 + "xyz");
2640 
2641             expect (String (1234) < String (1235));
2642             expect (String (1235) > String (1234));
2643             expect (String (1234) >= String (1234));
2644             expect (String (1234) <= String (1234));
2645             expect (String (1235) >= String (1234));
2646             expect (String (1234) <= String (1235));
2647 
2648             String s5 ("word word2 word3");
2649             expect (s5.containsWholeWord (String ("word2")));
2650             expect (s5.indexOfWholeWord ("word2") == 5);
2651             expect (s5.containsWholeWord (String (L"word")));
2652             expect (s5.containsWholeWord ("word3"));
2653             expect (s5.containsWholeWord (s5));
2654             expect (s5.containsWholeWordIgnoreCase (String (L"Word2")));
2655             expect (s5.indexOfWholeWordIgnoreCase ("Word2") == 5);
2656             expect (s5.containsWholeWordIgnoreCase (String (L"Word")));
2657             expect (s5.containsWholeWordIgnoreCase ("Word3"));
2658             expect (! s5.containsWholeWordIgnoreCase (String (L"Wordx")));
2659             expect (! s5.containsWholeWordIgnoreCase ("xWord2"));
2660             expect (s5.containsNonWhitespaceChars());
2661             expect (s5.containsOnly ("ordw23 "));
2662             expect (! String (" \n\r\t").containsNonWhitespaceChars());
2663 
2664             expect (s5.matchesWildcard (String (L"wor*"), false));
2665             expect (s5.matchesWildcard ("wOr*", true));
2666             expect (s5.matchesWildcard (String (L"*word3"), true));
2667             expect (s5.matchesWildcard ("*word?", true));
2668             expect (s5.matchesWildcard (String (L"Word*3"), true));
2669             expect (! s5.matchesWildcard (String (L"*34"), true));
2670             expect (String ("xx**y").matchesWildcard ("*y", true));
2671             expect (String ("xx**y").matchesWildcard ("x*y", true));
2672             expect (String ("xx**y").matchesWildcard ("xx*y", true));
2673             expect (String ("xx**y").matchesWildcard ("xx*", true));
2674             expect (String ("xx?y").matchesWildcard ("x??y", true));
2675             expect (String ("xx?y").matchesWildcard ("xx?y", true));
2676             expect (! String ("xx?y").matchesWildcard ("xx?y?", true));
2677             expect (String ("xx?y").matchesWildcard ("xx??", true));
2678 
2679             expectEquals (s5.fromFirstOccurrenceOf (String(), true, false), s5);
2680             expectEquals (s5.fromFirstOccurrenceOf ("xword2", true, false), s5.substring (100));
2681             expectEquals (s5.fromFirstOccurrenceOf (String (L"word2"), true, false), s5.substring (5));
2682             expectEquals (s5.fromFirstOccurrenceOf ("Word2", true, true), s5.substring (5));
2683             expectEquals (s5.fromFirstOccurrenceOf ("word2", false, false), s5.getLastCharacters (6));
2684             expectEquals (s5.fromFirstOccurrenceOf ("Word2", false, true), s5.getLastCharacters (6));
2685 
2686             expectEquals (s5.fromLastOccurrenceOf (String(), true, false), s5);
2687             expectEquals (s5.fromLastOccurrenceOf ("wordx", true, false), s5);
2688             expectEquals (s5.fromLastOccurrenceOf ("word", true, false), s5.getLastCharacters (5));
2689             expectEquals (s5.fromLastOccurrenceOf ("worD", true, true), s5.getLastCharacters (5));
2690             expectEquals (s5.fromLastOccurrenceOf ("word", false, false), s5.getLastCharacters (1));
2691             expectEquals (s5.fromLastOccurrenceOf ("worD", false, true), s5.getLastCharacters (1));
2692 
2693             expect (s5.upToFirstOccurrenceOf (String(), true, false).isEmpty());
2694             expectEquals (s5.upToFirstOccurrenceOf ("word4", true, false), s5);
2695             expectEquals (s5.upToFirstOccurrenceOf ("word2", true, false), s5.substring (0, 10));
2696             expectEquals (s5.upToFirstOccurrenceOf ("Word2", true, true), s5.substring (0, 10));
2697             expectEquals (s5.upToFirstOccurrenceOf ("word2", false, false), s5.substring (0, 5));
2698             expectEquals (s5.upToFirstOccurrenceOf ("Word2", false, true), s5.substring (0, 5));
2699 
2700             expectEquals (s5.upToLastOccurrenceOf (String(), true, false), s5);
2701             expectEquals (s5.upToLastOccurrenceOf ("zword", true, false), s5);
2702             expectEquals (s5.upToLastOccurrenceOf ("word", true, false), s5.dropLastCharacters (1));
2703             expectEquals (s5.dropLastCharacters(1).upToLastOccurrenceOf ("word", true, false), s5.dropLastCharacters (1));
2704             expectEquals (s5.upToLastOccurrenceOf ("Word", true, true), s5.dropLastCharacters (1));
2705             expectEquals (s5.upToLastOccurrenceOf ("word", false, false), s5.dropLastCharacters (5));
2706             expectEquals (s5.upToLastOccurrenceOf ("Word", false, true), s5.dropLastCharacters (5));
2707 
2708             expectEquals (s5.replace ("word", "xyz", false), String ("xyz xyz2 xyz3"));
2709             expect (s5.replace ("Word", "xyz", true) == "xyz xyz2 xyz3");
2710             expect (s5.dropLastCharacters (1).replace ("Word", String ("xyz"), true) == L"xyz xyz2 xyz");
2711             expect (s5.replace ("Word", "", true) == " 2 3");
2712             expectEquals (s5.replace ("Word2", "xyz", true), String ("word xyz word3"));
2713             expect (s5.replaceCharacter (L'w', 'x') != s5);
2714             expectEquals (s5.replaceCharacter ('w', L'x').replaceCharacter ('x', 'w'), s5);
2715             expect (s5.replaceCharacters ("wo", "xy") != s5);
2716             expectEquals (s5.replaceCharacters ("wo", "xy").replaceCharacters ("xy", "wo"), s5);
2717             expectEquals (s5.retainCharacters ("1wordxya"), String ("wordwordword"));
2718             expect (s5.retainCharacters (String()).isEmpty());
2719             expect (s5.removeCharacters ("1wordxya") == " 2 3");
2720             expectEquals (s5.removeCharacters (String()), s5);
2721             expect (s5.initialSectionContainingOnly ("word") == L"word");
2722             expect (String ("word").initialSectionContainingOnly ("word") == L"word");
2723             expectEquals (s5.initialSectionNotContaining (String ("xyz ")), String ("word"));
2724             expectEquals (s5.initialSectionNotContaining (String (";[:'/")), s5);
2725             expect (! s5.isQuotedString());
2726             expect (s5.quoted().isQuotedString());
2727             expect (! s5.quoted().unquoted().isQuotedString());
2728             expect (! String ("x'").isQuotedString());
2729             expect (String ("'x").isQuotedString());
2730 
2731             String s6 (" \t xyz  \t\r\n");
2732             expectEquals (s6.trim(), String ("xyz"));
2733             expect (s6.trim().trim() == "xyz");
2734             expectEquals (s5.trim(), s5);
2735             expectEquals (s6.trimStart().trimEnd(), s6.trim());
2736             expectEquals (s6.trimStart().trimEnd(), s6.trimEnd().trimStart());
2737             expectEquals (s6.trimStart().trimStart().trimEnd().trimEnd(), s6.trimEnd().trimStart());
2738             expect (s6.trimStart() != s6.trimEnd());
2739             expectEquals (("\t\r\n " + s6 + "\t\n \r").trim(), s6.trim());
2740             expect (String::repeatedString ("xyz", 3) == L"xyzxyzxyz");
2741         }
2742 
2743         {
2744             beginTest ("UTF conversions");
2745 
2746             TestUTFConversion <CharPointer_UTF32>::test (*this, r);
2747             TestUTFConversion <CharPointer_UTF8>::test (*this, r);
2748             TestUTFConversion <CharPointer_UTF16>::test (*this, r);
2749         }
2750 
2751         {
2752             beginTest ("StringArray");
2753 
2754             StringArray s;
2755             s.addTokens ("4,3,2,1,0", ";,", "x");
2756             expectEquals (s.size(), 5);
2757 
2758             expectEquals (s.joinIntoString ("-"), String ("4-3-2-1-0"));
2759             s.remove (2);
2760             expectEquals (s.joinIntoString ("--"), String ("4--3--1--0"));
2761             expectEquals (s.joinIntoString (StringRef()), String ("4310"));
2762             s.clear();
2763             expectEquals (s.joinIntoString ("x"), String());
2764 
2765             StringArray toks;
2766             toks.addTokens ("x,,", ";,", "");
2767             expectEquals (toks.size(), 3);
2768             expectEquals (toks.joinIntoString ("-"), String ("x--"));
2769             toks.clear();
2770 
2771             toks.addTokens (",x,", ";,", "");
2772             expectEquals (toks.size(), 3);
2773             expectEquals (toks.joinIntoString ("-"), String ("-x-"));
2774             toks.clear();
2775 
2776             toks.addTokens ("x,'y,z',", ";,", "'");
2777             expectEquals (toks.size(), 3);
2778             expectEquals (toks.joinIntoString ("-"), String ("x-'y,z'-"));
2779         }
2780 
2781         {
2782             beginTest ("var");
2783 
2784             var v1 = 0;
2785             var v2 = 0.16;
2786             var v3 = "0.16";
2787             var v4 = (int64) 0;
2788             var v5 = 0.0;
2789             expect (! v2.equals (v1));
2790             expect (! v1.equals (v2));
2791             expect (v2.equals (v3));
2792             expect (! v3.equals (v1));
2793             expect (! v1.equals (v3));
2794             expect (v1.equals (v4));
2795             expect (v4.equals (v1));
2796             expect (v5.equals (v4));
2797             expect (v4.equals (v5));
2798             expect (! v2.equals (v4));
2799             expect (! v4.equals (v2));
2800         }
2801 
2802         {
2803             beginTest ("Significant figures");
2804 
2805             // Integers
2806 
2807             expectEquals (String::toDecimalStringWithSignificantFigures (13, 1), String ("10"));
2808             expectEquals (String::toDecimalStringWithSignificantFigures (13, 2), String ("13"));
2809             expectEquals (String::toDecimalStringWithSignificantFigures (13, 3), String ("13.0"));
2810             expectEquals (String::toDecimalStringWithSignificantFigures (13, 4), String ("13.00"));
2811 
2812             expectEquals (String::toDecimalStringWithSignificantFigures (19368, 1), String ("20000"));
2813             expectEquals (String::toDecimalStringWithSignificantFigures (19348, 3), String ("19300"));
2814 
2815             expectEquals (String::toDecimalStringWithSignificantFigures (-5, 1), String ("-5"));
2816             expectEquals (String::toDecimalStringWithSignificantFigures (-5, 3), String ("-5.00"));
2817 
2818             // Zero
2819 
2820             expectEquals (String::toDecimalStringWithSignificantFigures (0, 1), String ("0"));
2821             expectEquals (String::toDecimalStringWithSignificantFigures (0, 2), String ("0.0"));
2822             expectEquals (String::toDecimalStringWithSignificantFigures (0, 3), String ("0.00"));
2823 
2824             // Floating point
2825 
2826             expectEquals (String::toDecimalStringWithSignificantFigures (19.0, 1), String ("20"));
2827             expectEquals (String::toDecimalStringWithSignificantFigures (19.0, 2), String ("19"));
2828             expectEquals (String::toDecimalStringWithSignificantFigures (19.0, 3), String ("19.0"));
2829             expectEquals (String::toDecimalStringWithSignificantFigures (19.0, 4), String ("19.00"));
2830 
2831             expectEquals (String::toDecimalStringWithSignificantFigures (-5.45, 1), String ("-5"));
2832             expectEquals (String::toDecimalStringWithSignificantFigures (-5.45, 3), String ("-5.45"));
2833 
2834             expectEquals (String::toDecimalStringWithSignificantFigures (12345.6789, 9), String ("12345.6789"));
2835             expectEquals (String::toDecimalStringWithSignificantFigures (12345.6789, 8), String ("12345.679"));
2836             expectEquals (String::toDecimalStringWithSignificantFigures (12345.6789, 5), String ("12346"));
2837 
2838             expectEquals (String::toDecimalStringWithSignificantFigures (0.00028647, 6), String ("0.000286470"));
2839             expectEquals (String::toDecimalStringWithSignificantFigures (0.0028647,  6), String ("0.00286470"));
2840             expectEquals (String::toDecimalStringWithSignificantFigures (2.8647,     6), String ("2.86470"));
2841 
2842             expectEquals (String::toDecimalStringWithSignificantFigures (-0.0000000000019, 1), String ("-0.000000000002"));
2843         }
2844 
2845         {
2846             beginTest ("Float trimming");
2847 
2848             {
2849                 StringPairArray tests;
2850                 tests.set ("1", "1");
2851                 tests.set ("1.0", "1.0");
2852                 tests.set ("-1", "-1");
2853                 tests.set ("-100", "-100");
2854                 tests.set ("110", "110");
2855                 tests.set ("9090", "9090");
2856                 tests.set ("1000.0", "1000.0");
2857                 tests.set ("1.0", "1.0");
2858                 tests.set ("-1.00", "-1.0");
2859                 tests.set ("1.20", "1.2");
2860                 tests.set ("1.300", "1.3");
2861                 tests.set ("1.301", "1.301");
2862                 tests.set ("1e", "1");
2863                 tests.set ("-1e+", "-1");
2864                 tests.set ("1e-", "1");
2865                 tests.set ("1e0", "1");
2866                 tests.set ("1e+0", "1");
2867                 tests.set ("1e-0", "1");
2868                 tests.set ("1e000", "1");
2869                 tests.set ("1e+000", "1");
2870                 tests.set ("-1e-000", "-1");
2871                 tests.set ("1e100", "1e100");
2872                 tests.set ("100e100", "100e100");
2873                 tests.set ("100.0e0100", "100.0e100");
2874                 tests.set ("-1e1", "-1e1");
2875                 tests.set ("1e10", "1e10");
2876                 tests.set ("-1e+10", "-1e10");
2877                 tests.set ("1e-10", "1e-10");
2878                 tests.set ("1e0010", "1e10");
2879                 tests.set ("1e-0010", "1e-10");
2880                 tests.set ("1e-1", "1e-1");
2881                 tests.set ("-1.0e1", "-1.0e1");
2882                 tests.set ("1.0e-1", "1.0e-1");
2883                 tests.set ("1.00e-1", "1.0e-1");
2884                 tests.set ("1.001e1", "1.001e1");
2885                 tests.set ("1.010e+1", "1.01e1");
2886                 tests.set ("-1.1000e1", "-1.1e1");
2887 
2888                 for (auto& input : tests.getAllKeys())
2889                     expectEquals (reduceLengthOfFloatString (input), tests[input]);
2890             }
2891 
2892             {
2893                 std::map<double, String> tests;
2894                 tests[1] = "1.0";
2895                 tests[1.1] = "1.1";
2896                 tests[1.01] = "1.01";
2897                 tests[0.76378] = "7.6378e-1";
2898                 tests[-10] = "-1.0e1";
2899                 tests[10.01] = "1.001e1";
2900                 tests[10691.01] = "1.069101e4";
2901                 tests[0.0123] = "1.23e-2";
2902                 tests[-3.7e-27] = "-3.7e-27";
2903                 tests[1e+40] = "1.0e40";
2904 
2905                 for (auto& test : tests)
2906                     expectEquals (reduceLengthOfFloatString (String (test.first, 15, true)), test.second);
2907             }
2908         }
2909 
2910         {
2911             beginTest ("Serialisation");
2912 
2913             std::map <double, String> tests;
2914 
2915             tests[364] = "364.0";
2916             tests[1e7] = "1.0e7";
2917             tests[12345678901] = "1.2345678901e10";
2918 
2919             tests[1234567890123456.7] = "1.234567890123457e15";
2920             tests[12345678.901234567] = "1.234567890123457e7";
2921             tests[1234567.8901234567] = "1.234567890123457e6";
2922             tests[123456.78901234567] = "123456.7890123457";
2923             tests[12345.678901234567] = "12345.67890123457";
2924             tests[1234.5678901234567] = "1234.567890123457";
2925             tests[123.45678901234567] = "123.4567890123457";
2926             tests[12.345678901234567] = "12.34567890123457";
2927             tests[1.2345678901234567] = "1.234567890123457";
2928             tests[0.12345678901234567] = "0.1234567890123457";
2929             tests[0.012345678901234567] = "0.01234567890123457";
2930             tests[0.0012345678901234567] = "0.001234567890123457";
2931             tests[0.00012345678901234567] = "0.0001234567890123457";
2932             tests[0.000012345678901234567] = "0.00001234567890123457";
2933             tests[0.0000012345678901234567] = "1.234567890123457e-6";
2934             tests[0.00000012345678901234567] = "1.234567890123457e-7";
2935 
2936             for (auto& test : tests)
2937             {
2938                 expectEquals (serialiseDouble (test.first), test.second);
2939                 expectEquals (serialiseDouble (-test.first), "-" + test.second);
2940             }
2941         }
2942 
2943         {
2944             beginTest ("Loops");
2945 
2946             String str (CharPointer_UTF8 ("\xc2\xaf\\_(\xe3\x83\x84)_/\xc2\xaf"));
2947             std::vector<juce_wchar> parts { 175, 92, 95, 40, 12484, 41, 95, 47, 175 };
2948             size_t index = 0;
2949 
2950             for (auto c : str)
2951                 expectEquals (c, parts[index++]);
2952         }
2953     }
2954 };
2955 
2956 static StringTests stringUnitTests;
2957 
2958 #endif
2959 
2960 } // namespace juce
2961