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