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