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