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