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