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