JUCE-7.0.12-0-g4f43011b96 JUCE-7.0.12-0-g4f43011b96
JUCE — C++ application framework with suport for VST, VST3, LV2 audio plug-ins

« « « Anklang Documentation
Loading...
Searching...
No Matches
fstring.cpp
Go to the documentation of this file.
1 //------------------------------------------------------------------------
2// Project : SDK Base
3// Version : 1.0
4//
5// Category : Helpers
6// Filename : base/source/fstring.cpp
7// Created by : Steinberg, 2008
8// Description : String class
9//
10//-----------------------------------------------------------------------------
11// LICENSE
12// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved
13//-----------------------------------------------------------------------------
14// Redistribution and use in source and binary forms, with or without modification,
15// are permitted provided that the following conditions are met:
16//
17// * Redistributions of source code must retain the above copyright notice,
18// this list of conditions and the following disclaimer.
19// * Redistributions in binary form must reproduce the above copyright notice,
20// this list of conditions and the following disclaimer in the documentation
21// and/or other materials provided with the distribution.
22// * Neither the name of the Steinberg Media Technologies nor the names of its
23// contributors may be used to endorse or promote products derived from this
24// software without specific prior written permission.
25//
26// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
27// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
28// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
29// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
30// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
31// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
33// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
34// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
35// OF THE POSSIBILITY OF SUCH DAMAGE.
36//-----------------------------------------------------------------------------
37
38#include "base/source/fstring.h"
39#include "base/source/fdebug.h"
42
43#include <cstdlib>
44#include <cctype>
45#include <cstdio>
46#include <cstdarg>
47#include <utility>
48#include <complex>
49#include <cmath>
50#include <algorithm>
51#include <cassert>
52
53#if SMTG_OS_WINDOWS
54#ifndef NOMINMAX
55#define NOMINMAX
56#endif
57#include <windows.h>
58#ifdef _MSC_VER
59#pragma warning (disable : 4244)
60#pragma warning (disable : 4267)
61#pragma warning (disable : 4996)
62
63#if DEVELOPMENT
64#include <crtdbg.h>
65
66#define malloc(s) _malloc_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__)
67#define realloc(p,s) _realloc_dbg(p,s, _NORMAL_BLOCK, __FILE__, __LINE__)
68#define free(p) _free_dbg(p, _NORMAL_BLOCK)
69
70#endif // DEVELOPMENT
71#endif // _MSC_VER
72#endif // SMTG_OS_WINDOWS
73
74#ifndef kPrintfBufferSize
75#define kPrintfBufferSize 4096
76#endif
77
78#if SMTG_OS_MACOS
79#include <CoreFoundation/CoreFoundation.h>
80#include <CoreFoundation/CFString.h>
81#include <CoreFoundation/CFStringEncodingExt.h>
82#include <wchar.h>
83
84#if defined (__GNUC__) && (__GNUC__ >= 4) && !__LP64__
85// on 32 bit Mac OS X we can safely ignore the format warnings as sizeof(int) == sizeof(long)
86#pragma GCC diagnostic ignored "-Wformat"
87#endif
88
89#define SMTG_ENABLE_DEBUG_CFALLOCATOR 0
90#define SMTG_DEBUG_CFALLOCATOR (DEVELOPMENT && SMTG_ENABLE_DEBUG_CFALLOCATOR)
91
92#if SMTG_DEBUG_CFALLOCATOR
93#include <libkern/OSAtomic.h>
94#include <dlfcn.h>
95#endif
96
97namespace Steinberg {
98#if SMTG_DEBUG_CFALLOCATOR
99static CFAllocatorRef kCFAllocator = NULL;
100
101struct CFStringDebugAllocator : CFAllocatorContext
102{
103 CFStringDebugAllocator ()
104 {
105 version = 0;
106 info = this;
107 retain = nullptr;
108 release = nullptr;
109 copyDescription = nullptr;
110 allocate = allocateCallBack;
111 reallocate = reallocateCallBack;
112 deallocate = deallocateCallBack;
113 preferredSize = preferredSizeCallBack;
114
115 numAllocations = allocationSize = numDeallocations = 0;
116 cfAllocator = CFAllocatorCreate (kCFAllocatorUseContext, this);
117
118 Dl_info info;
119 if (dladdr ((const void*)CFStringDebugAllocator::allocateCallBack, &info))
120 {
121 moduleName = info.dli_fname;
122 }
123 kCFAllocator = cfAllocator;
124 }
125
126 ~CFStringDebugAllocator ()
127 {
128 kCFAllocator = kCFAllocatorDefault;
129 CFRelease (cfAllocator);
130 FDebugPrint ("CFStringDebugAllocator (%s):\n", moduleName.text8 ());
131 FDebugPrint ("\tNumber of allocations : %u\n", numAllocations);
132 FDebugPrint ("\tNumber of deallocations: %u\n", numDeallocations);
133 FDebugPrint ("\tAllocated Bytes : %u\n", allocationSize);
134 }
135
136 String moduleName;
137 CFAllocatorRef cfAllocator;
138 volatile int64_t numAllocations;
139 volatile int64_t numDeallocations;
140 volatile int64_t allocationSize;
141
142 void* doAllocate (CFIndex allocSize, CFOptionFlags hint)
143 {
144 void* ptr = CFAllocatorAllocate (kCFAllocatorDefault, allocSize, hint);
145 OSAtomicIncrement64 (&numAllocations);
146 OSAtomicAdd64 (allocSize, &allocationSize);
147 return ptr;
148 }
149 void* doReallocate (void* ptr, CFIndex newsize, CFOptionFlags hint)
150 {
151 void* newPtr = CFAllocatorReallocate (kCFAllocatorDefault, ptr, newsize, hint);
152 return newPtr;
153 }
154 void doDeallocate (void* ptr)
155 {
156 CFAllocatorDeallocate (kCFAllocatorDefault, ptr);
157 OSAtomicIncrement64 (&numDeallocations);
158 }
159 CFIndex getPreferredSize (CFIndex size, CFOptionFlags hint)
160 {
161 return CFAllocatorGetPreferredSizeForSize (kCFAllocatorDefault, size, hint);
162 }
163
164 static void* allocateCallBack (CFIndex allocSize, CFOptionFlags hint, void* info)
165 {
166 return static_cast<CFStringDebugAllocator*> (info)->doAllocate (allocSize, hint);
167 }
168 static void* reallocateCallBack (void* ptr, CFIndex newsize, CFOptionFlags hint, void* info)
169 {
170 return static_cast<CFStringDebugAllocator*> (info)->doReallocate (ptr, newsize, hint);
171 }
172
173 static void deallocateCallBack (void* ptr, void* info)
174 {
175 static_cast<CFStringDebugAllocator*> (info)->doDeallocate (ptr);
176 }
177 static CFIndex preferredSizeCallBack (CFIndex size, CFOptionFlags hint, void* info)
178 {
179 return static_cast<CFStringDebugAllocator*> (info)->getPreferredSize (size, hint);
180 }
181};
182static CFStringDebugAllocator gDebugAllocator;
183#else
184
185static const CFAllocatorRef kCFAllocator = ::kCFAllocatorDefault;
186#endif // SMTG_DEBUG_CFALLOCATOR
187}
188
189//-----------------------------------------------------------------------------
190static void* toCFStringRef (const Steinberg::char8* source, Steinberg::uint32 encoding)
191{
192 if (encoding == 0xFFFF)
193 encoding = kCFStringEncodingASCII;
194 if (source)
195 return (void*)CFStringCreateWithCString (Steinberg::kCFAllocator, source, encoding);
196 else
197 return (void*)CFStringCreateWithCString (Steinberg::kCFAllocator, "", encoding);
198}
199
200//-----------------------------------------------------------------------------
201static bool fromCFStringRef (Steinberg::char8* dest, Steinberg::int32 destSize, const void* cfStr, Steinberg::uint32 encoding)
202{
203 CFIndex usedBytes;
204 CFRange range = {0, CFStringGetLength ((CFStringRef)cfStr)};
205 bool result = CFStringGetBytes ((CFStringRef)cfStr, range, encoding, '?', false, (UInt8*)dest, destSize, &usedBytes);
206 dest[usedBytes] = 0;
207 return result;
208}
209#endif // SMTG_OS_MACOS
210
211#if SMTG_OS_WINDOWS
212//-----------------------------------------------------------------------------
213static inline int stricmp16 (const Steinberg::tchar* s1, const Steinberg::tchar* s2)
214{
215 return wcsicmp (Steinberg::wscast (s1), Steinberg::wscast (s2));
216}
217
218//-----------------------------------------------------------------------------
219static inline int strnicmp16 (const Steinberg::tchar* s1, const Steinberg::tchar* s2, size_t l)
220{
221 return wcsnicmp (Steinberg::wscast (s1), Steinberg::wscast (s2), l);
222}
223
224//-----------------------------------------------------------------------------
225static inline int vsnwprintf (Steinberg::char16* buffer, size_t bufferSize,
226 const Steinberg::char16* format, va_list args)
227{
228 return _vsnwprintf (Steinberg::wscast (buffer), bufferSize, Steinberg::wscast (format), args);
229}
230
231//-----------------------------------------------------------------------------
232static inline Steinberg::int32 sprintf16 (Steinberg::char16* str, const Steinberg::char16* format, ...)
233{
234 va_list marker;
235 va_start (marker, format);
236 return vsnwprintf (str, -1, format, marker);
237}
238
239#elif SMTG_OS_LINUX
240#include <codecvt>
241#include <locale>
242#include <cstring>
243#include <string>
244#include <limits>
245#include <cassert>
246#include <wchar.h>
247
248using ConverterFacet = std::codecvt_utf8_utf16<char16_t>;
250
251//------------------------------------------------------------------------
252static ConverterFacet& converterFacet ()
253{
254 static ConverterFacet gFacet;
255 return gFacet;
256}
257
258//------------------------------------------------------------------------
259static Converter& converter ()
260{
261 static Converter gConverter;
262 return gConverter;
263}
264
265//-----------------------------------------------------------------------------
266static inline int stricasecmp (const Steinberg::char8* s1, const Steinberg::char8* s2)
267{
268 return ::strcasecmp (s1, s2);
269}
270
271//-----------------------------------------------------------------------------
272static inline int strnicasecmp (const Steinberg::char8* s1, const Steinberg::char8* s2, size_t n)
273{
274 return ::strncasecmp (s1, s2, n);
275}
276
277//-----------------------------------------------------------------------------
278static inline int stricmp16 (const Steinberg::char16* s1, const Steinberg::char16* s2)
279{
280 auto str1 = converter ().to_bytes (s1);
281 auto str2 = converter ().to_bytes (s2);
282 return stricasecmp (str1.data (), str2.data ());
283}
284
285//-----------------------------------------------------------------------------
286static inline int strnicmp16 (const Steinberg::char16* s1, const Steinberg::char16* s2, int n)
287{
288 auto str1 = converter ().to_bytes (s1);
289 auto str2 = converter ().to_bytes (s2);
290 return strnicasecmp (str1.data (), str2.data (), n);
291}
292
293//-----------------------------------------------------------------------------
294static inline int sprintf16 (Steinberg::char16* wcs, const Steinberg::char16* format, ...)
295{
296 assert (false && "DEPRECATED No Linux implementation");
297 return 0;
298}
299
300//-----------------------------------------------------------------------------
301static inline int vsnwprintf (Steinberg::char16* wcs, size_t maxlen,
302 const Steinberg::char16* format, va_list args)
303{
304 Steinberg::char8 str8[kPrintfBufferSize];
305 auto format_utf8 = converter ().to_bytes(format);
306 auto len = vsnprintf (str8, kPrintfBufferSize, format_utf8.data (), args);
307
308 auto tmp_str = converter ().from_bytes (str8, str8 + len);
309 auto target_len = std::min (tmp_str.size (), maxlen - 1);
310 tmp_str.copy (wcs, target_len);
311 wcs[target_len] = '\0';
312
313 return tmp_str.size ();
314}
315
316//-----------------------------------------------------------------------------
317static inline Steinberg::char16* strrchr16 (const Steinberg::char16* str, Steinberg::char16 c)
318{
319 assert (false && "DEPRECATED No Linux implementation");
320 return nullptr;
321}
322
323#elif SMTG_OS_MACOS
324#define tstrtoi64 strtoll
325#define stricmp strcasecmp
326#define strnicmp strncasecmp
327
328//-----------------------------------------------------------------------------
329static inline Steinberg::int32 strnicmp16 (const Steinberg::char16* str1, const Steinberg::char16* str2, size_t size)
330{
331 if (size == 0)
332 return 0;
333
334 CFIndex str1Len = Steinberg::strlen16 (str1);
335 CFIndex str2Len = Steinberg::strlen16 (str2);
336 if (static_cast<CFIndex> (size) < str2Len) // range is not applied to second string
337 str2Len = size;
338 CFStringRef cfStr1 = CFStringCreateWithCharactersNoCopy (Steinberg::kCFAllocator, (UniChar*)str1, str1Len, kCFAllocatorNull);
339 CFStringRef cfStr2 = CFStringCreateWithCharactersNoCopy (Steinberg::kCFAllocator, (UniChar*)str2, str2Len, kCFAllocatorNull);
340 CFComparisonResult result = CFStringCompareWithOptions (cfStr1, cfStr2, CFRangeMake (0, size), kCFCompareCaseInsensitive);
341 CFRelease (cfStr1);
342 CFRelease (cfStr2);
343 switch (result)
344 {
345 case kCFCompareEqualTo: return 0;
346 case kCFCompareLessThan: return -1;
347 case kCFCompareGreaterThan:
348 default: return 1;
349 };
350}
351
352//-----------------------------------------------------------------------------
353static inline Steinberg::int32 stricmp16 (const Steinberg::char16* str1, CFIndex str1Len, const Steinberg::char16* str2, CFIndex str2Len)
354{
355 CFStringRef cfStr1 = CFStringCreateWithCharactersNoCopy (Steinberg::kCFAllocator, (UniChar*)str1, str1Len, kCFAllocatorNull);
356 CFStringRef cfStr2 = CFStringCreateWithCharactersNoCopy (Steinberg::kCFAllocator, (UniChar*)str2, str2Len, kCFAllocatorNull);
357 CFComparisonResult result = CFStringCompare (cfStr1, cfStr2, kCFCompareCaseInsensitive);
358 CFRelease (cfStr1);
359 CFRelease (cfStr2);
360 switch (result)
361 {
362 case kCFCompareEqualTo: return 0;
363 case kCFCompareLessThan: return -1;
364 case kCFCompareGreaterThan:
365 default: return 1;
366 };
367}
368
369//-----------------------------------------------------------------------------
370static inline Steinberg::int32 stricmp16 (const Steinberg::ConstString& str1, const Steinberg::ConstString& str2)
371{
372 return stricmp16 (str1.text16 (), str1.length (), str2.text16 (), str2.length ());
373}
374
375//-----------------------------------------------------------------------------
376static inline Steinberg::int32 stricmp16 (const Steinberg::char16* str1, const Steinberg::char16* str2)
377{
378 CFIndex str1Len = Steinberg::strlen16 (str1);
379 CFIndex str2Len = Steinberg::strlen16 (str2);
380 return stricmp16 (str1, str1Len, str2, str2Len);
381}
382
383//-----------------------------------------------------------------------------
384static inline Steinberg::char16* strrchr16 (const Steinberg::char16* str, Steinberg::char16 c)
385{
386 Steinberg::int32 len = Steinberg::ConstString (str).length ();
387 while (len > 0)
388 {
389 if (str[len] == c)
390 return const_cast<Steinberg::char16*>(str + len);
391 len--;
392 }
393 return 0;
394}
395
396//-----------------------------------------------------------------------------
397static inline Steinberg::int32 vsnwprintf (Steinberg::char16* str, Steinberg::int32 size, const Steinberg::char16* format, va_list ap)
398{
399 // wrapped using CoreFoundation's CFString
400 CFMutableStringRef formatString = (CFMutableStringRef)Steinberg::ConstString (format).toCFStringRef (0xFFFF, true);
401 CFStringFindAndReplace (formatString, CFSTR("%s"), CFSTR("%S"), CFRangeMake (0, CFStringGetLength (formatString)), 0);
402 CFStringRef resultString = CFStringCreateWithFormatAndArguments (Steinberg::kCFAllocator, 0, formatString, ap);
403 CFRelease (formatString);
404 if (resultString)
405 {
407 res.fromCFStringRef (resultString);
408 res.copyTo16 (str, 0, size);
409 CFRelease (resultString);
410 return 0;
411 }
412 return 1;
413}
414
415//-----------------------------------------------------------------------------
416static inline Steinberg::int32 sprintf16 (Steinberg::char16* str, const Steinberg::char16* format, ...)
417{
418 va_list marker;
419 va_start (marker, format);
420 return vsnwprintf (str, -1, format, marker);
421}
422
423#endif // SMTG_OS_LINUX
424
425/*
426UTF-8 EF BB BF
427UTF-16 Big Endian FE FF
428UTF-16 Little Endian FF FE
429UTF-32 Big Endian 00 00 FE FF
430UTF-32 Little Endian FF FE 00 00
431*/
432
433namespace Steinberg {
434
435//-----------------------------------------------------------------------------
436static inline bool isCaseSensitive (ConstString::CompareMode mode)
437{
438 return mode == ConstString::kCaseSensitive;
439}
440
441//-----------------------------------------------------------------------------
442// ConstString
443//-----------------------------------------------------------------------------
444ConstString::ConstString (const char8* str, int32 length)
445: buffer8 ((char8*)str)
446, len (length < 0 ? (str ? static_cast<uint32> (strlen (str)) : 0) : length)
447, isWide (0)
448{
449}
450
451//-----------------------------------------------------------------------------
452ConstString::ConstString (const char16* str, int32 length)
453: buffer16 ((char16*)str)
454, len (length < 0 ? (str ? strlen16 (str) : 0) : length)
455, isWide (1)
456{
457}
458
459//-----------------------------------------------------------------------------
460ConstString::ConstString (const ConstString& str, int32 offset, int32 length)
461: buffer (str.buffer)
462, len (length < 0 ? (str.len - (offset > 0 ? offset : 0)) : length)
463, isWide (str.isWide)
464{
465 if (offset > 0)
466 {
467 if (isWide)
468 buffer16 += offset;
469 else
470 buffer8 += offset;
471 }
472}
473
474//-----------------------------------------------------------------------------
475ConstString::ConstString (const FVariant& var)
476: buffer (nullptr)
477, len (0)
478, isWide (0)
479{
480 switch (var.getType ())
481 {
482 case FVariant::kString8:
483 buffer8 = (char8*)var.getString8 ();
484 len = buffer8 ? strlen8 (buffer8) : 0;
485 isWide = false;
486 break;
487
488 case FVariant::kString16:
489 buffer16 = (char16*)var.getString16 ();
490 len = buffer16 ? strlen16 (buffer16) : 0;
491 isWide = true;
492 break;
493 }
494}
495
496//-----------------------------------------------------------------------------
497ConstString::ConstString ()
498: buffer (nullptr)
499, len (0)
500, isWide (0)
501{
502}
503
504//-----------------------------------------------------------------------------
505bool ConstString::testChar8 (uint32 index, char8 c) const
506{
507 if (index >= len)
508 return c == 0;
509 if (isWide)
510 {
511 // make c wide
512 char8 src[] = {c, 0};
513 char16 dest[2] = {0};
514 if (multiByteToWideString (dest, src, 2) > 0)
515 return buffer16[index] == dest[0];
516 return false;
517 }
518 return buffer8[index] == c;
519}
520
521//-----------------------------------------------------------------------------
522bool ConstString::testChar16 (uint32 index, char16 c) const
523{
524 if (index >= len)
525 return c == 0;
526 if (!isWide)
527 {
528 // make c ansi
529 char16 src[] = {c, 0};
530 char8 dest[8] = {0};
531 if (wideStringToMultiByte (dest, src, 2) > 0 && dest[1] == 0)
532 return buffer8[index] == dest[0];
533 return false;
534 }
535 return buffer16[index] == c;
536}
537
538//-----------------------------------------------------------------------------
539bool ConstString::extract (String& result, uint32 idx, int32 n) const
540{
541 // AddressSanitizer : when extracting part of "this" on itself, it can lead to heap-use-after-free.
542 SMTG_ASSERT (this != static_cast<ConstString*> (&result))
543
544 if (len == 0|| idx >= len)
545 return false;
546
547 if ((idx + n > len) || n < 0)
548 n = len - idx;
549
550 if (isWide)
551 result.assign (buffer16 + idx, n);
552 else
553 result.assign (buffer8 + idx, n);
554
555 return true;
556}
557
558//-----------------------------------------------------------------------------
559int32 ConstString::copyTo8 (char8* str, uint32 idx, int32 n) const
560{
561 if (!str)
562 return 0;
563
564 if (isWide)
565 {
566 String tmp (text16 ());
567 if (tmp.toMultiByte () == false)
568 return 0;
569 return tmp.copyTo8 (str, idx, n);
570 }
571
572 if (isEmpty () || idx >= len || !buffer8)
573 {
574 str[0] = 0;
575 return 0;
576 }
577
578 if ((idx + n > len) || n < 0)
579 n = len - idx;
580
581 memcpy (str, &(buffer8[idx]), n * sizeof (char8));
582 str[n] = 0;
583 return n;
584}
585
586//-----------------------------------------------------------------------------
587int32 ConstString::copyTo16 (char16* str, uint32 idx, int32 n) const
588{
589 if (!str)
590 return 0;
591
592 if (!isWide)
593 {
594 String tmp (text8 ());
595 if (tmp.toWideString () == false)
596 return 0;
597 return tmp.copyTo16 (str, idx, n);
598 }
599
600 if (isEmpty () || idx >= len || !buffer16)
601 {
602 str[0] = 0;
603 return 0;
604 }
605
606 if ((idx + n > len) || n < 0)
607 n = len - idx;
608
609 memcpy (str, &(buffer16[idx]), n * sizeof (char16));
610 str[n] = 0;
611 return n;
612}
613
614//-----------------------------------------------------------------------------
615int32 ConstString::copyTo (tchar* str, uint32 idx, int32 n) const
616{
617#ifdef UNICODE
618 return copyTo16 (str, idx, n);
619#else
620 return copyTo8 (str, idx, n);
621#endif
622}
623
624//-----------------------------------------------------------------------------
625void ConstString::copyTo (IStringResult* result) const
626{
627 if (isWideString () == false)
628 {
629 result->setText (text8 ());
630 }
631 else
632 {
633 FUnknownPtr<IString> iStr (result);
634 if (iStr)
635 {
636 iStr->setText16 (text16 ());
637 }
638 else
639 {
640 String tmp (*this);
641 tmp.toMultiByte ();
642 result->setText (tmp.text8 ());
643 }
644 }
645}
646
647//-----------------------------------------------------------------------------
648void ConstString::copyTo (IString& string) const
649{
650 if (isWideString ())
651 string.setText16 (text16 ());
652 else
653 string.setText8 (text8 ());
654}
655
656
657
658//-----------------------------------------------------------------------------
659int32 ConstString::compare (const ConstString& str, int32 n, CompareMode mode) const
660{
661 if (n == 0)
662 return 0;
663
664 if (str.isEmpty ())
665 {
666 if (isEmpty ())
667 return 0;
668 return 1;
669 }
670 if (isEmpty ())
671 return -1;
672
673 if (!isWide && !str.isWide)
674 {
675 if (n < 0)
676 {
677 if (isCaseSensitive (mode))
678 return strcmp (*this, str);
679 return stricmp (*this, str);
680 }
681 if (isCaseSensitive (mode))
682 return strncmp (*this, str, n);
683 return strnicmp (*this, str, n);
684 }
685 if (isWide && str.isWide)
686 {
687 if (n < 0)
688 {
689 if (isCaseSensitive (mode))
690 return strcmp16 (*this, str);
691 return stricmp16 (*this, str);
692 }
693 if (isCaseSensitive (mode))
694 return strncmp16 (*this, str, n);
695 return strnicmp16 (*this, str, n);
696 }
697 return compareAt (0, str, n, mode);
698}
699
700//-----------------------------------------------------------------------------
701int32 ConstString::compare (const ConstString& str, CompareMode mode) const
702{
703 return compare (str, -1, mode);
704}
705
706//-----------------------------------------------------------------------------
707int32 ConstString::compareAt (uint32 index, const ConstString& str, int32 n, CompareMode mode) const
708{
709 if (n == 0)
710 return 0;
711
712 if (str.isEmpty ())
713 {
714 if (isEmpty ())
715 return 0;
716 return 1;
717 }
718 if (isEmpty ())
719 return -1;
720
721 if (!isWide && !str.isWide)
722 {
723 char8* toCompare = buffer8;
724 if (index > 0)
725 {
726 if (index >= len)
727 {
728 if (str.isEmpty ())
729 return 0;
730 return -1;
731 }
732 toCompare += index;
733 }
734
735 if (n < 0)
736 {
737 if (isCaseSensitive (mode))
738 return strcmp (toCompare, str);
739 return stricmp (toCompare, str);
740 }
741 if (isCaseSensitive (mode))
742 return strncmp (toCompare, str, n);
743 return strnicmp (toCompare, str, n);
744 }
745 if (isWide && str.isWide)
746 {
747 char16* toCompare = buffer16;
748 if (index > 0)
749 {
750 if (index >= len)
751 {
752 if (str.isEmpty ())
753 return 0;
754 return -1;
755 }
756 toCompare += index;
757 }
758
759 if (n < 0)
760 {
761 if (isCaseSensitive (mode))
762 return strcmp16 (toCompare, str.text16 ());
763 return stricmp16 (toCompare, str.text16 ());
764 }
765 if (isCaseSensitive (mode))
766 return strncmp16 (toCompare, str.text16 (), n);
767 return strnicmp16 (toCompare, str.text16 (), n);
768 }
769
770 if (isWide)
771 {
772 String tmp (str.text8 ());
773 if (tmp.toWideString () == false)
774 return -1;
775 return compareAt (index, tmp, n, mode);
776 }
777
778 String tmp (text8 ());
779 if (tmp.toWideString () == false)
780 return 1;
781 return tmp.compareAt (index, str, n, mode);
782}
783
784//------------------------------------------------------------------------
785Steinberg::int32 ConstString::naturalCompare (const ConstString& str, CompareMode mode /*= kCaseSensitive*/) const
786{
787 if (str.isEmpty ())
788 {
789 if (isEmpty ())
790 return 0;
791 return 1;
792 }
793 if (isEmpty ())
794 return -1;
795
796 if (!isWide && !str.isWide)
797 return strnatcmp8 (buffer8, str.text8 (), isCaseSensitive (mode));
798 if (isWide && str.isWide)
799 return strnatcmp16 (buffer16, str.text16 (), isCaseSensitive (mode));
800
801 if (isWide)
802 {
803 String tmp (str.text8 ());
804 tmp.toWideString ();
805 return strnatcmp16 (buffer16, tmp.text16 (), isCaseSensitive (mode));
806 }
807 String tmp (text8 ());
808 tmp.toWideString ();
809 return strnatcmp16 (tmp.text16 (), str.text16 (), isCaseSensitive (mode));
810}
811
812//-----------------------------------------------------------------------------
813bool ConstString::startsWith (const ConstString& str, CompareMode mode /*= kCaseSensitive*/) const
814{
815 if (str.isEmpty ())
816 {
817 return isEmpty ();
818 }
819 if (isEmpty ())
820 {
821 return false;
822 }
823 if (length () < str.length ())
824 {
825 return false;
826 }
827 if (!isWide && !str.isWide)
828 {
829 if (isCaseSensitive (mode))
830 return strncmp (buffer8, str.buffer8, str.length ()) == 0;
831 return strnicmp (buffer8, str.buffer8, str.length ()) == 0;
832 }
833 if (isWide && str.isWide)
834 {
835 if (isCaseSensitive (mode))
836 return strncmp16 (buffer16, str.buffer16, str.length ()) == 0;
837 return strnicmp16 (buffer16, str.buffer16, str.length ()) == 0;
838 }
839 if (isWide)
840 {
841 String tmp (str.text8 ());
842 tmp.toWideString ();
843 if (tmp.length () > length ())
844 return false;
845 if (isCaseSensitive (mode))
846 return strncmp16 (buffer16, tmp.buffer16, tmp.length ()) == 0;
847 return strnicmp16 (buffer16, tmp.buffer16, tmp.length ()) == 0;
848 }
849 String tmp (text8 ());
850 tmp.toWideString ();
851 if (str.length () > tmp.length ())
852 return false;
853 if (isCaseSensitive (mode))
854 return strncmp16 (tmp.buffer16, str.buffer16, str.length ()) == 0;
855 return strnicmp16 (tmp.buffer16, str.buffer16, str.length ()) == 0;
856}
857
858//-----------------------------------------------------------------------------
859bool ConstString::endsWith (const ConstString& str, CompareMode mode /*= kCaseSensitive*/) const
860{
861 if (str.isEmpty ())
862 {
863 return isEmpty ();
864 }
865 if (isEmpty ())
866 {
867 return false;
868 }
869 if (length () < str.length ())
870 {
871 return false;
872 }
873 if (!isWide && !str.isWide)
874 {
875 if (isCaseSensitive (mode))
876 return strncmp (buffer8 + (length () - str.length ()), str.buffer8, str.length ()) == 0;
877 return strnicmp (buffer8 + (length () - str.length ()), str.buffer8, str.length ()) == 0;
878 }
879 if (isWide && str.isWide)
880 {
881 if (isCaseSensitive (mode))
882 return strncmp16 (buffer16 + (length () - str.length ()), str.buffer16, str.length ()) == 0;
883 return strnicmp16 (buffer16 + (length () - str.length ()), str.buffer16, str.length ()) == 0;
884 }
885 if (isWide)
886 {
887 String tmp (str.text8 ());
888 tmp.toWideString ();
889 if (tmp.length () > length ())
890 return false;
891 if (isCaseSensitive (mode))
892 return strncmp16 (buffer16 + (length () - tmp.length ()), tmp.buffer16, tmp.length ()) == 0;
893 return strnicmp16 (buffer16 + (length () - tmp.length ()), tmp.buffer16, tmp.length ()) == 0;
894 }
895 String tmp (text8 ());
896 tmp.toWideString ();
897 if (str.length () > tmp.length ())
898 return false;
899 if (isCaseSensitive (mode))
900 return strncmp16 (tmp.buffer16 + (tmp.length () - str.length ()), str.buffer16, str.length ()) == 0;
901 return strnicmp16 (tmp.buffer16 + (tmp.length () - str.length ()), str.buffer16, str.length ()) == 0;
902}
903
904//-----------------------------------------------------------------------------
906{
907 return findFirst (str, -1, m) != -1;
908}
909
910//-----------------------------------------------------------------------------
911int32 ConstString::findNext (int32 startIndex, const ConstString& str, int32 n, CompareMode mode, int32 endIndex) const
912{
913 uint32 endLength = len;
914 if (endIndex > -1 && (uint32)endIndex < len)
915 endLength = endIndex + 1;
916
917 if (isWide && str.isWide)
918 {
919 if (startIndex < 0)
920 startIndex = 0;
921
922 uint32 stringLength = str.length ();
923 n = n < 0 ? stringLength : Min<uint32> (n, stringLength);
924
925 if (n > 0)
926 {
927 uint32 i = 0;
928
929 if (isCaseSensitive (mode))
930 {
931 for (i = startIndex; i < endLength; i++)
932 if (strncmp16 (buffer16 + i, str, n) == 0)
933 return i;
934 }
935 else
936 {
937 for (i = startIndex; i < endLength; i++)
938 if (strnicmp16 (buffer16 + i, str, n) == 0)
939 return i;
940 }
941 }
942 return -1;
943 }
944 if (!isWide && !str.isWide)
945 {
946 uint32 stringLength = str.length ();
947 n = n < 0 ? stringLength : Min<uint32> (n, stringLength);
948
949 if (startIndex < 0)
950 startIndex = 0;
951
952 if (n > 0)
953 {
954 uint32 i = 0;
955
956 if (isCaseSensitive (mode))
957 {
958 for (i = startIndex; i < endLength; i++)
959 if (strncmp (buffer8 + i, str, n) == 0)
960 return i;
961 }
962 else
963 {
964 for (i = startIndex; i < endLength; i++)
965 if (strnicmp (buffer8 + i, str, n) == 0)
966 return i;
967 }
968 }
969 return -1;
970 }
971 String tmp;
972 if (isWide)
973 {
974 tmp = str.text8 ();
975 tmp.toWideString ();
976 return findNext (startIndex, tmp, n , mode, endIndex);
977 }
978 tmp = text8 ();
979 tmp.toWideString ();
980 return tmp.findNext (startIndex, str, n, mode, endIndex);
981}
982
983//------------------------------------------------------------------------------------------------
984int32 ConstString::findNext (int32 startIndex, char8 c, CompareMode mode, int32 endIndex) const
985{
986 uint32 endLength = len;
987 if (endIndex > -1 && (uint32)endIndex < len)
988 endLength = endIndex + 1;
989
990 if (isWide)
991 {
992 char8 src[] = {c, 0};
993 char16 dest[8] = {0};
994 if (multiByteToWideString (dest, src, 2) > 0)
995 return findNext (startIndex, dest[0], mode, endIndex);
996 return -1;
997 }
998
999 if (startIndex < 0)
1000 startIndex = 0;
1001 uint32 i;
1002
1003 if (isCaseSensitive (mode))
1004 {
1005 for (i = startIndex; i < endLength; i++)
1006 {
1007 if (buffer8[i] == c)
1008 return i;
1009 }
1010 }
1011 else
1012 {
1013 c = toLower (c);
1014 for (i = startIndex; i < endLength; i++)
1015 {
1016 if (toLower (buffer8[i]) == c)
1017 return i;
1018 }
1019 }
1020 return -1;
1021}
1022
1023//-----------------------------------------------------------------------------
1024int32 ConstString::findNext (int32 startIndex, char16 c, CompareMode mode, int32 endIndex) const
1025{
1026 uint32 endLength = len;
1027 if (endIndex > -1 && (uint32)endIndex < len)
1028 endLength = endIndex + 1;
1029
1030 if (!isWide)
1031 {
1032 char16 src[] = {c, 0};
1033 char8 dest[8] = {0};
1034 if (wideStringToMultiByte (dest, src, 2) > 0 && dest[1] == 0)
1035 return findNext (startIndex, dest[0], mode, endIndex);
1036
1037 return -1;
1038 }
1039
1040 uint32 i;
1041 if (startIndex < 0)
1042 startIndex = 0;
1043
1044 if (isCaseSensitive (mode))
1045 {
1046 for (i = startIndex; i < endLength; i++)
1047 {
1048 if (buffer16[i] == c)
1049 return i;
1050 }
1051 }
1052 else
1053 {
1054 c = toLower (c);
1055 for (i = startIndex; i < endLength; i++)
1056 {
1057 if (toLower (buffer16[i]) == c)
1058 return i;
1059 }
1060 }
1061 return -1;
1062}
1063
1064//-----------------------------------------------------------------------------
1065int32 ConstString::findPrev (int32 startIndex, char8 c, CompareMode mode) const
1066{
1067 if (len == 0)
1068 return -1;
1069
1070 if (isWide)
1071 {
1072 char8 src[] = {c, 0};
1073 char16 dest[8] = {0};
1074 if (multiByteToWideString (dest, src, 2) > 0)
1075 return findPrev (startIndex, dest[0], mode);
1076 return -1;
1077 }
1078
1079 if (startIndex < 0 || startIndex > (int32)len)
1080 startIndex = len;
1081
1082 int32 i;
1083
1084 if (isCaseSensitive (mode))
1085 {
1086 for (i = startIndex; i >= 0; i--)
1087 {
1088 if (buffer8[i] == c)
1089 return i;
1090 }
1091 }
1092 else
1093 {
1094 c = toLower (c);
1095 for (i = startIndex; i >= 0; i--)
1096 {
1097 if (toLower (buffer8[i]) == c)
1098 return i;
1099 }
1100 }
1101 return -1;
1102}
1103
1104//-----------------------------------------------------------------------------
1105int32 ConstString::findPrev (int32 startIndex, char16 c, CompareMode mode) const
1106{
1107 if (len == 0)
1108 return -1;
1109
1110 if (!isWide)
1111 {
1112 char16 src[] = {c, 0};
1113 char8 dest[8] = {0};
1114 if (wideStringToMultiByte (dest, src, 2) > 0 && dest[1] == 0)
1115 return findPrev (startIndex, dest[0], mode);
1116
1117 return -1;
1118 }
1119
1120 if (startIndex < 0 || startIndex > (int32)len)
1121 startIndex = len;
1122
1123 int32 i;
1124
1125 if (isCaseSensitive (mode))
1126 {
1127 for (i = startIndex; i >= 0; i--)
1128 {
1129 if (buffer16[i] == c)
1130 return i;
1131 }
1132 }
1133 else
1134 {
1135 c = toLower (c);
1136 for (i = startIndex; i >= 0; i--)
1137 {
1138 if (toLower (buffer16[i]) == c)
1139 return i;
1140 }
1141 }
1142 return -1;
1143}
1144
1145//-----------------------------------------------------------------------------
1146int32 ConstString::findPrev (int32 startIndex, const ConstString& str, int32 n, CompareMode mode) const
1147{
1148 if (isWide && str.isWide)
1149 {
1150 uint32 stringLength = str.length ();
1151 n = n < 0 ? stringLength : Min<uint32> (n, stringLength);
1152
1153 if (startIndex < 0 || startIndex >= (int32)len)
1154 startIndex = len - 1;
1155
1156 if (n > 0)
1157 {
1158 int32 i = 0;
1159
1160 if (isCaseSensitive (mode))
1161 {
1162 for (i = startIndex; i >= 0; i--)
1163 if (strncmp16 (buffer16 + i, str, n) == 0)
1164 return i;
1165 }
1166 else
1167 {
1168 for (i = startIndex; i >= 0; i--)
1169 if (strnicmp16 (buffer16 + i, str, n) == 0)
1170 return i;
1171 }
1172 }
1173 return -1;
1174 }
1175 if (!isWide && !str.isWide)
1176 {
1177 uint32 stringLength = str.length ();
1178 n = n < 0 ? stringLength : Min<uint32> (n, stringLength);
1179
1180 if (startIndex < 0 || startIndex >= (int32)len)
1181 startIndex = len - 1;
1182
1183 if (n > 0)
1184 {
1185 int32 i = 0;
1186
1187 if (isCaseSensitive (mode))
1188 {
1189 for (i = startIndex; i >= 0; i--)
1190 if (strncmp (buffer8 + i, str, n) == 0)
1191 return i;
1192 }
1193 else
1194 {
1195 for (i = startIndex; i >= 0; i--)
1196 if (strnicmp (buffer8 + i, str, n) == 0)
1197 return i;
1198 }
1199 }
1200 return -1;
1201 }
1202 if (isWide)
1203 {
1204 String tmp (str.text8 ());
1205 tmp.toWideString ();
1206 return findPrev (startIndex, tmp, n, mode);
1207 }
1208 String tmp (text8 ());
1209 tmp.toWideString ();
1210 return tmp.findPrev (startIndex, str, n, mode);
1211}
1212
1213//-----------------------------------------------------------------------------
1214int32 ConstString::countOccurences (char8 c, uint32 startIndex, CompareMode mode) const
1215{
1216 if (isWide)
1217 {
1218 char8 src[] = {c, 0};
1219 char16 dest[8] = {0};
1220 if (multiByteToWideString (dest, src, 2) > 0)
1221 return countOccurences (dest[0], startIndex, mode);
1222 return -1;
1223 }
1224
1225 int32 result = 0;
1226 int32 next = startIndex;
1227 while (true)
1228 {
1229 next = findNext (next, c, mode);
1230 if (next >= 0)
1231 {
1232 next++;
1233 result++;
1234 }
1235 else
1236 break;
1237 }
1238 return result;
1239}
1240
1241//-----------------------------------------------------------------------------
1242int32 ConstString::countOccurences (char16 c, uint32 startIndex, CompareMode mode) const
1243{
1244 if (!isWide)
1245 {
1246 char16 src[] = {c, 0};
1247 char8 dest[8] = {0};
1248 if (wideStringToMultiByte (dest, src, 2) > 0 && dest[1] == 0)
1249 return countOccurences (dest[0], startIndex, mode);
1250
1251 return -1;
1252 }
1253 int32 result = 0;
1254 int32 next = startIndex;
1255 while (true)
1256 {
1257 next = findNext (next, c, mode);
1258 if (next >= 0)
1259 {
1260 next++;
1261 result++;
1262 }
1263 else
1264 break;
1265 }
1266 return result;
1267}
1268
1269//-----------------------------------------------------------------------------
1271{
1272 if (str.isWide != isWide)
1273 {
1274 if (isWide)
1275 {
1276 String tmp (str.text8 ());
1277 if (tmp.toWideString () == false)
1278 return -1;
1279 return getFirstDifferent (tmp, mode);
1280 }
1281
1282 String tmp (text8 ());
1283 if (tmp.toWideString () == false)
1284 return -1;
1285 return tmp.getFirstDifferent (str, mode);
1286 }
1287
1288 uint32 len1 = len;
1289 uint32 len2 = str.len;
1290 uint32 i;
1291
1292 if (isWide)
1293 {
1294 if (isCaseSensitive (mode))
1295 {
1296 for (i = 0; i <= len1 && i <= len2; i++)
1297 {
1298 if (buffer16[i] != str.buffer16[i])
1299 return i;
1300 }
1301 }
1302 else
1303 {
1304 for (i = 0; i <= len1 && i <= len2; i++)
1305 {
1306 if (toLower (buffer16[i]) != toLower (str.buffer16[i]))
1307 return i;
1308 }
1309 }
1310 }
1311 else
1312 {
1313 if (isCaseSensitive (mode))
1314 {
1315 for (i = 0; i <= len1 && i <= len2; i++)
1316 {
1317 if (buffer8[i] != str.buffer8[i])
1318 return i;
1319 }
1320 }
1321 else
1322 {
1323 for (i = 0; i <= len1 && i <= len2; i++)
1324 {
1325 if (toLower (buffer8[i]) != toLower (str.buffer8[i]))
1326 return i;
1327 }
1328 }
1329 }
1330 return -1;
1331}
1332
1333//-----------------------------------------------------------------------------
1334bool ConstString::scanInt64 (int64& value, uint32 offset, bool scanToEnd) const
1335{
1336 if (isEmpty () || offset >= len)
1337 return false;
1338
1339 if (isWide)
1340 return scanInt64_16 (buffer16 + offset, value, scanToEnd);
1341 return scanInt64_8 (buffer8 + offset, value, scanToEnd);
1342}
1343
1344//-----------------------------------------------------------------------------
1345bool ConstString::scanUInt64 (uint64& value, uint32 offset, bool scanToEnd) const
1346{
1347 if (isEmpty () || offset >= len)
1348 return false;
1349
1350 if (isWide)
1351 return scanUInt64_16 (buffer16 + offset, value, scanToEnd);
1352 return scanUInt64_8 (buffer8 + offset, value, scanToEnd);
1353}
1354
1355//-----------------------------------------------------------------------------
1356bool ConstString::scanHex (uint8& value, uint32 offset, bool scanToEnd) const
1357{
1358 if (isEmpty () || offset >= len)
1359 return false;
1360
1361 if (isWide)
1362 return scanHex_16 (buffer16 + offset, value, scanToEnd);
1363 return scanHex_8 (buffer8 + offset, value, scanToEnd);
1364}
1365
1366//-----------------------------------------------------------------------------
1367bool ConstString::scanInt32 (int32& value, uint32 offset, bool scanToEnd) const
1368{
1369 if (isEmpty () || offset >= len)
1370 return false;
1371
1372 if (isWide)
1373 return scanInt32_16 (buffer16 + offset, value, scanToEnd);
1374 return scanInt32_8 (buffer8 + offset, value, scanToEnd);
1375}
1376
1377//-----------------------------------------------------------------------------
1378bool ConstString::scanUInt32 (uint32& value, uint32 offset, bool scanToEnd) const
1379{
1380 if (isEmpty () || offset >= len)
1381 return false;
1382
1383 if (isWide)
1384 return scanUInt32_16 (buffer16 + offset, value, scanToEnd);
1385 return scanUInt32_8 (buffer8 + offset, value, scanToEnd);
1386}
1387
1388//-----------------------------------------------------------------------------
1389bool ConstString::scanInt64_8 (const char8* text, int64& value, bool scanToEnd)
1390{
1391 while (text && text[0])
1392 {
1393 if (sscanf (text, "%" FORMAT_INT64A, &value) == 1)
1394 return true;
1395 if (scanToEnd == false)
1396 return false;
1397 text++;
1398 }
1399 return false;
1400}
1401
1402//-----------------------------------------------------------------------------
1403bool ConstString::scanInt64_16 (const char16* text, int64& value, bool scanToEnd)
1404{
1405 if (text && text[0])
1406 {
1407 String str (text);
1408 str.toMultiByte (kCP_Default);
1409 return scanInt64_8 (str, value, scanToEnd);
1410 }
1411 return false;
1412}
1413
1414//-----------------------------------------------------------------------------
1415bool ConstString::scanUInt64_8 (const char8* text, uint64& value, bool scanToEnd)
1416{
1417 while (text && text[0])
1418 {
1419 if (sscanf (text, "%" FORMAT_UINT64A, &value) == 1)
1420 return true;
1421 if (scanToEnd == false)
1422 return false;
1423 text++;
1424 }
1425 return false;
1426}
1427
1428//-----------------------------------------------------------------------------
1429bool ConstString::scanUInt64_16 (const char16* text, uint64& value, bool scanToEnd)
1430{
1431 if (text && text[0])
1432 {
1433 String str (text);
1434 str.toMultiByte (kCP_Default);
1435 return scanUInt64_8 (str, value, scanToEnd);
1436 }
1437 return false;
1438}
1439
1440//-----------------------------------------------------------------------------
1441bool ConstString::scanInt64 (const tchar* text, int64& value, bool scanToEnd)
1442{
1443#ifdef UNICODE
1444 return scanInt64_16 (text, value,scanToEnd);
1445#else
1446 return scanInt64_8 (text, value, scanToEnd);
1447#endif
1448}
1449
1450//-----------------------------------------------------------------------------
1451bool ConstString::scanUInt64 (const tchar* text, uint64& value, bool scanToEnd)
1452{
1453#ifdef UNICODE
1454 return scanUInt64_16 (text, value, scanToEnd);
1455#else
1456 return scanUInt64_8 (text, value, scanToEnd);
1457#endif
1458}
1459
1460//-----------------------------------------------------------------------------
1461bool ConstString::scanHex_8 (const char8* text, uint8& value, bool scanToEnd)
1462{
1463 while (text && text[0])
1464 {
1465 unsigned int v; // scanf expects an unsigned int for %x
1466 if (sscanf (text, "%x", &v) == 1)
1467 {
1468 value = (uint8)v;
1469 return true;
1470 }
1471 if (scanToEnd == false)
1472 return false;
1473 text++;
1474 }
1475 return false;
1476}
1477
1478//-----------------------------------------------------------------------------
1479bool ConstString::scanHex_16 (const char16* text, uint8& value, bool scanToEnd)
1480{
1481 if (text && text[0])
1482 {
1483 String str (text);
1484 str.toMultiByte (kCP_Default); // scanf uses default codepage
1485 return scanHex_8 (str, value, scanToEnd);
1486 }
1487 return false;
1488}
1489
1490//-----------------------------------------------------------------------------
1491bool ConstString::scanHex (const tchar* text, uint8& value, bool scanToEnd)
1492{
1493#ifdef UNICODE
1494 return scanHex_16 (text, value, scanToEnd);
1495#else
1496 return scanHex_8 (text, value, scanToEnd);
1497#endif
1498}
1499
1500//-----------------------------------------------------------------------------
1501bool ConstString::scanFloat (double& value, uint32 offset, bool scanToEnd) const
1502{
1503 if (isEmpty () || offset >= len)
1504 return false;
1505
1506 String str (*this);
1507 int32 pos = -1;
1508 if (isWide)
1509 {
1510 if ((pos = str.findNext (offset, STR(','))) >= 0 && ((uint32)pos) >= offset)
1511 str.setChar (pos, STR('.'));
1512
1513 str.toMultiByte (kCP_Default); // scanf uses default codepage
1514 }
1515 else
1516 {
1517 if ((pos = str.findNext (offset, ',')) >= 0 && ((uint32)pos) >= offset)
1518 str.setChar (pos, '.');
1519 }
1520
1521 const char8* txt = str.text8 () + offset;
1522 while (txt && txt[0])
1523 {
1524 if (sscanf (txt, "%lf", &value) == 1)
1525 return true;
1526 if (scanToEnd == false)
1527 return false;
1528 txt++;
1529 }
1530 return false;
1531}
1532
1533//-----------------------------------------------------------------------------
1534char16 ConstString::toLower (char16 c)
1535{
1536 #if SMTG_OS_WINDOWS
1537 WCHAR temp[2] = {c, 0};
1538 ::CharLowerW (temp);
1539 return temp[0];
1540 #elif SMTG_OS_MACOS
1541 // only convert characters which in lowercase are also single characters
1542 UniChar characters [2] = {0};
1543 characters[0] = c;
1544 CFMutableStringRef str = CFStringCreateMutableWithExternalCharactersNoCopy (kCFAllocator, characters, 1, 2, kCFAllocatorNull);
1545 if (str)
1546 {
1547 CFStringLowercase (str, NULL);
1548 CFRelease (str);
1549 if (characters[1] == 0)
1550 return characters[0];
1551 }
1552 return c;
1553 #elif SMTG_OS_LINUX
1554 assert (false && "DEPRECATED No Linux implementation");
1555 return c;
1556 #else
1557 return towlower (c);
1558 #endif
1559}
1560
1561//-----------------------------------------------------------------------------
1562char16 ConstString::toUpper (char16 c)
1563{
1564 #if SMTG_OS_WINDOWS
1565 WCHAR temp[2] = {c, 0};
1566 ::CharUpperW (temp);
1567 return temp[0];
1568 #elif SMTG_OS_MACOS
1569 // only convert characters which in uppercase are also single characters (don't translate a sharp-s which would result in SS)
1570 UniChar characters [2] = {0};
1571 characters[0] = c;
1572 CFMutableStringRef str = CFStringCreateMutableWithExternalCharactersNoCopy (kCFAllocator, characters, 1, 2, kCFAllocatorNull);
1573 if (str)
1574 {
1575 CFStringUppercase (str, NULL);
1576 CFRelease (str);
1577 if (characters[1] == 0)
1578 return characters[0];
1579 }
1580 return c;
1581 #elif SMTG_OS_LINUX
1582 assert (false && "DEPRECATED No Linux implementation");
1583 return c;
1584 #else
1585 return towupper (c);
1586 #endif
1587}
1588
1589//-----------------------------------------------------------------------------
1591{
1592 if ((c >= 'A') && (c <= 'Z'))
1593 return c + ('a' - 'A');
1594 #if SMTG_OS_WINDOWS
1595 CHAR temp[2] = {c, 0};
1596 ::CharLowerA (temp);
1597 return temp[0];
1598 #else
1599 return static_cast<char8> (tolower (c));
1600 #endif
1601}
1602
1603//-----------------------------------------------------------------------------
1605{
1606 if ((c >= 'a') && (c <= 'z'))
1607 return c - ('a' - 'A');
1608 #if SMTG_OS_WINDOWS
1609 CHAR temp[2] = {c, 0};
1610 ::CharUpperA (temp);
1611 return temp[0];
1612 #else
1613 return static_cast<char8> (toupper (c));
1614 #endif
1615}
1616
1617//-----------------------------------------------------------------------------
1618bool ConstString::isCharSpace (const char8 character)
1619{
1620 return isspace (character) != 0;
1621}
1622
1623//-----------------------------------------------------------------------------
1624bool ConstString::isCharSpace (const char16 character)
1625{
1626 switch (character)
1627 {
1628 case 0x0020:
1629 case 0x00A0:
1630 case 0x2002:
1631 case 0x2003:
1632 case 0x2004:
1633 case 0x2005:
1634 case 0x2006:
1635 case 0x2007:
1636 case 0x2008:
1637 case 0x2009:
1638 case 0x200A:
1639 case 0x200B:
1640 case 0x202F:
1641 case 0x205F:
1642 case 0x3000:
1643 return true;
1644 }
1645 return false;
1646}
1647
1648//-----------------------------------------------------------------------------
1649bool ConstString::isCharAlpha (const char8 character)
1650{
1651 return isalpha (character) != 0;
1652}
1653
1654//-----------------------------------------------------------------------------
1655bool ConstString::isCharAlpha (const char16 character)
1656{
1657 return iswalpha (character) != 0;
1658}
1659
1660//-----------------------------------------------------------------------------
1661bool ConstString::isCharAlphaNum (const char8 character)
1662{
1663 return isalnum (character) != 0;
1664}
1665
1666//-----------------------------------------------------------------------------
1667bool ConstString::isCharAlphaNum (const char16 character)
1668{
1669 return iswalnum (character) != 0; // this may not work on macOSX when another locale is set inside the c-lib
1670}
1671
1672//-----------------------------------------------------------------------------
1673bool ConstString::isCharDigit (const char8 character)
1674{
1675 return isdigit (character) != 0;
1676}
1677
1678//-----------------------------------------------------------------------------
1679bool ConstString::isCharDigit (const char16 character)
1680{
1681 return iswdigit (character) != 0; // this may not work on macOSX when another locale is set inside the c-lib
1682}
1683
1684//-----------------------------------------------------------------------------
1685bool ConstString::isCharAscii (char8 character)
1686{
1687 return character >= 0;
1688}
1689
1690//-----------------------------------------------------------------------------
1691bool ConstString::isCharAscii (char16 character)
1692{
1693 return character < 128;
1694}
1695
1696//-----------------------------------------------------------------------------
1697bool ConstString::isCharUpper (char8 character)
1698{
1699 return toUpper (character) == character;
1700}
1701
1702//-----------------------------------------------------------------------------
1703bool ConstString::isCharUpper (char16 character)
1704{
1705 return toUpper (character) == character;
1706}
1707
1708//-----------------------------------------------------------------------------
1709bool ConstString::isCharLower (char8 character)
1710{
1711 return toLower (character) == character;
1712}
1713
1714//-----------------------------------------------------------------------------
1715bool ConstString::isCharLower (char16 character)
1716{
1717 return toLower (character) == character;
1718}
1719
1720//-----------------------------------------------------------------------------
1721bool ConstString::isDigit (uint32 index) const
1722{
1723 if (isEmpty () || index >= len)
1724 return false;
1725
1726 if (isWide)
1727 return ConstString::isCharDigit (buffer16[index]);
1728 return ConstString::isCharDigit (buffer8[index]);
1729}
1730
1731//-----------------------------------------------------------------------------
1732int32 ConstString::getTrailingNumberIndex (uint32 width) const
1733{
1734 if (isEmpty ())
1735 return -1;
1736
1737 int32 endIndex = len - 1;
1738 int32 i = endIndex;
1739 while (isDigit ((uint32) i) && i >= 0)
1740 i--;
1741
1742 // now either all are digits or i is on the first non digit
1743 if (i < endIndex)
1744 {
1745 if (width > 0 && (endIndex - i != static_cast<int32> (width)))
1746 return -1;
1747
1748 return i + 1;
1749 }
1750
1751 return -1;
1752}
1753
1754//-----------------------------------------------------------------------------
1755int64 ConstString::getTrailingNumber (int64 fallback) const
1756{
1757 int32 index = getTrailingNumberIndex ();
1758
1759 int64 number = 0;
1760
1761 if (index >= 0)
1762 if (scanInt64 (number, index))
1763 return number;
1764
1765 return fallback;
1766}
1767
1768
1769
1770//-----------------------------------------------------------------------------
1771void ConstString::toVariant (FVariant& var) const
1772{
1773 if (isWide)
1774 {
1775 var.setString16 (buffer16);
1776 }
1777 else
1778 {
1779 var.setString8 (buffer8);
1780 }
1781}
1782
1783//-----------------------------------------------------------------------------
1785{
1786 uint32 i;
1787 if (isWide)
1788 {
1789 for (i = 0; i < len; i++)
1790 if (ConstString::isCharAscii (buffer16 [i]) == false)
1791 return false;
1792 }
1793 else
1794 {
1795 for (i = 0; i < len; i++)
1796 if (ConstString::isCharAscii (buffer8 [i]) == false)
1797 return false;
1798 }
1799 return true;
1800}
1801
1802
1803#if SMTG_OS_MACOS
1804uint32 kDefaultSystemEncoding = kCFStringEncodingMacRoman;
1805//-----------------------------------------------------------------------------
1806static CFStringEncoding MBCodePageToCFStringEncoding (uint32 codePage)
1807{
1808 switch (codePage)
1809 {
1810 case kCP_ANSI: return kDefaultSystemEncoding; // MacRoman or JIS
1811 case kCP_MAC_ROMAN: return kCFStringEncodingMacRoman;
1812 case kCP_ANSI_WEL: return kCFStringEncodingWindowsLatin1;
1813 case kCP_MAC_CEE: return kCFStringEncodingMacCentralEurRoman;
1814 case kCP_Utf8: return kCFStringEncodingUTF8;
1815 case kCP_ShiftJIS: return kCFStringEncodingShiftJIS_X0213_00;
1816 case kCP_US_ASCII: return kCFStringEncodingASCII;
1817 }
1818 return kCFStringEncodingASCII;
1819}
1820#endif
1821
1822//-----------------------------------------------------------------------------
1823int32 ConstString::multiByteToWideString (char16* dest, const char8* source, int32 charCount, uint32 sourceCodePage)
1824{
1825 if (source == nullptr || source[0] == 0)
1826 {
1827 if (dest && charCount > 0)
1828 {
1829 dest[0] = 0;
1830 }
1831 return 0;
1832 }
1833 int32 result = 0;
1834#if SMTG_OS_WINDOWS
1835 result = MultiByteToWideChar (sourceCodePage, MB_ERR_INVALID_CHARS, source, -1, wscast (dest), charCount);
1836#endif
1837
1838#if SMTG_OS_MACOS
1839 CFStringRef cfStr =
1840 (CFStringRef)::toCFStringRef (source, MBCodePageToCFStringEncoding (sourceCodePage));
1841 if (cfStr)
1842 {
1843 CFRange range = {0, CFStringGetLength (cfStr)};
1844 CFIndex usedBytes;
1845 if (CFStringGetBytes (cfStr, range, kCFStringEncodingUnicode, ' ', false, (UInt8*)dest,
1846 charCount * 2, &usedBytes) > 0)
1847 {
1848 result = static_cast<int32> (usedBytes / 2 + 1);
1849 if (dest)
1850 dest[usedBytes / 2] = 0;
1851 }
1852
1853 CFRelease (cfStr);
1854 }
1855#endif
1856
1857#if SMTG_OS_LINUX
1858 if (sourceCodePage == kCP_ANSI || sourceCodePage == kCP_US_ASCII || sourceCodePage == kCP_Utf8)
1859 {
1860 if (dest == nullptr)
1861 {
1862 auto state = std::mbstate_t ();
1863 auto maxChars = charCount ? charCount : std::numeric_limits<int32>::max () - 1;
1864 result = converterFacet ().length (state, source, source + strlen (source), maxChars);
1865 }
1866 else
1867 {
1868 auto utf16Str = converter ().from_bytes (source);
1869 if (!utf16Str.empty ())
1870 {
1871 result = std::min<int32> (charCount, utf16Str.size ());
1872 memcpy (dest, utf16Str.data (), result * sizeof (char16));
1873 dest[result] = 0;
1874 }
1875 }
1876 }
1877 else
1878 {
1879 assert (false && "DEPRECATED No Linux implementation");
1880 }
1881
1882#endif
1883
1884 SMTG_ASSERT (result > 0)
1885 return result;
1886}
1887
1888//-----------------------------------------------------------------------------
1889int32 ConstString::wideStringToMultiByte (char8* dest, const char16* wideString, int32 charCount, uint32 destCodePage)
1890{
1891#if SMTG_OS_WINDOWS
1892 return WideCharToMultiByte (destCodePage, 0, wscast (wideString), -1, dest, charCount, nullptr, nullptr);
1893
1894#elif SMTG_OS_MACOS
1895 int32 result = 0;
1896 if (wideString != 0)
1897 {
1898 if (dest)
1899 {
1900 CFStringRef cfStr = CFStringCreateWithCharactersNoCopy (kCFAllocator, (const UniChar*)wideString, strlen16 (wideString), kCFAllocatorNull);
1901 if (cfStr)
1902 {
1903 if (fromCFStringRef (dest, charCount, cfStr, MBCodePageToCFStringEncoding (destCodePage)))
1904 result = static_cast<int32> (strlen (dest) + 1);
1905 CFRelease (cfStr);
1906 }
1907 }
1908 else
1909 {
1910 return static_cast<int32> (CFStringGetMaximumSizeForEncoding (strlen16 (wideString), MBCodePageToCFStringEncoding (destCodePage)));
1911 }
1912 }
1913 return result;
1914
1915#elif SMTG_OS_LINUX
1916 int32 result = 0;
1917 if (destCodePage == kCP_Utf8)
1918 {
1919 if (dest == nullptr)
1920 {
1921 auto maxChars = charCount ? charCount : tstrlen (wideString);
1922 result = converterFacet ().max_length () * maxChars;
1923 }
1924 else
1925 {
1926 auto utf8Str = converter ().to_bytes (wideString);
1927 if (!utf8Str.empty ())
1928 {
1929 result = std::min<int32> (charCount, utf8Str.size ());
1930 memcpy (dest, utf8Str.data (), result * sizeof (char8));
1931 dest[result] = 0;
1932 }
1933 }
1934 }
1935 else if (destCodePage == kCP_ANSI || destCodePage == kCP_US_ASCII)
1936 {
1937 if (dest == nullptr)
1938 {
1939 result = strlen16 (wideString) + 1;
1940 }
1941 else
1942 {
1943 int32 i = 0;
1944 for (; i < charCount; ++i)
1945 {
1946 if (wideString[i] == 0)
1947 break;
1948 if (wideString[i] <= 0x007F)
1949 dest[i] = wideString[i];
1950 else
1951 dest[i] = '_';
1952 }
1953 dest[i] = 0;
1954 result = i;
1955 }
1956 }
1957 else
1958 {
1959 assert (false && "DEPRECATED No Linux implementation");
1960 }
1961 return result;
1962
1963#else
1964 assert (false && "DEPRECATED No Linux implementation");
1965 return 0;
1966#endif
1967
1968}
1969
1970//-----------------------------------------------------------------------------
1972{
1973 if (isWide == false)
1974 return false;
1975
1976#if SMTG_OS_WINDOWS
1977#ifdef UNICODE
1978 if (n != kUnicodeNormC)
1979 return false;
1980 uint32 normCharCount = static_cast<uint32> (FoldString (MAP_PRECOMPOSED, wscast (buffer16), len, nullptr, 0));
1981 return (normCharCount == len);
1982#else
1983 return false;
1984#endif
1985
1986#elif SMTG_OS_MACOS
1987 if (n != kUnicodeNormC)
1988 return false;
1989
1990 CFStringRef cfStr = (CFStringRef)toCFStringRef ();
1991 CFIndex charCount = CFStringGetLength (cfStr);
1992 CFRelease (cfStr);
1993 return (charCount == len);
1994#else
1995 return false;
1996#endif
1997}
1998
1999//-----------------------------------------------------------------------------
2000// String
2001//-----------------------------------------------------------------------------
2002String::String ()
2003{
2004 isWide = kWideStringDefault ? 1 : 0;
2005}
2006
2007//-----------------------------------------------------------------------------
2008String::String (const char8* str, MBCodePage codePage, int32 n, bool isTerminated)
2009{
2010 isWide = false;
2011 if (str)
2012 {
2013 if (isTerminated && n >= 0 && str[n] != 0)
2014 {
2015 // isTerminated is not always set correctly
2016 isTerminated = false;
2017 }
2018
2019 if (!isTerminated)
2020 {
2021 assign (str, n, isTerminated);
2022 toWideString (codePage);
2023 }
2024 else
2025 {
2026 if (n < 0)
2027 n = static_cast<int32> (strlen (str));
2028 if (n > 0)
2029 _toWideString (str, n, codePage);
2030 }
2031 }
2032}
2033
2034//-----------------------------------------------------------------------------
2035String::String (const char8* str, int32 n, bool isTerminated)
2036{
2037 if (str)
2038 assign (str, n, isTerminated);
2039}
2040
2041//-----------------------------------------------------------------------------
2042String::String (const char16* str, int32 n, bool isTerminated)
2043{
2044 isWide = 1;
2045 if (str)
2046 assign (str, n, isTerminated);
2047}
2048
2049//-----------------------------------------------------------------------------
2050String::String (const String& str, int32 n)
2051{
2052 isWide = str.isWideString ();
2053 if (!str.isEmpty ())
2054 assign (str, n);
2055}
2056
2057//-----------------------------------------------------------------------------
2058String::String (const ConstString& str, int32 n)
2059{
2060 isWide = str.isWideString ();
2061 if (!str.isEmpty ())
2062 assign (str, n);
2063}
2064
2065//-----------------------------------------------------------------------------
2066String::String (const FVariant& var)
2067{
2068 isWide = kWideStringDefault ? 1 : 0;
2069 fromVariant (var);
2070}
2071
2072//-----------------------------------------------------------------------------
2073String::String (IString* str)
2074{
2075 isWide = str->isWideString ();
2076 if (isWide)
2077 assign (str->getText16 ());
2078 else
2079 assign (str->getText8 ());
2080}
2081
2082//-----------------------------------------------------------------------------
2083String::~String ()
2084{
2085 if (buffer)
2086 resize (0, false);
2087}
2088
2089#if SMTG_CPP11_STDLIBSUPPORT
2090//-----------------------------------------------------------------------------
2091String::String (String&& str)
2092{
2093 *this = std::move (str);
2094}
2095
2096//-----------------------------------------------------------------------------
2097String& String::operator= (String&& str)
2098{
2099 SMTG_ASSERT (buffer == nullptr || buffer != str.buffer);
2100 tryFreeBuffer ();
2101
2102 isWide = str.isWide;
2103 buffer = str.buffer;
2104 len = str.len;
2105 str.buffer = nullptr;
2106 str.len = 0;
2107 return *this;
2108}
2109#endif
2110
2111//-----------------------------------------------------------------------------
2113{
2114 if (isWide)
2115 len = strlen16 (text16 ());
2116 else
2117 len = strlen8 (text8 ());
2118}
2119
2120//-----------------------------------------------------------------------------
2121bool String::toWideString (uint32 sourceCodePage)
2122{
2123 if (!isWide && buffer8 && len > 0)
2124 return _toWideString (buffer8, len, sourceCodePage);
2125 isWide = true;
2126 return true;
2127}
2128
2129//-----------------------------------------------------------------------------
2130bool String::_toWideString (const char8* src, int32 length, uint32 sourceCodePage)
2131{
2132 if (!isWide)
2133 {
2134 if (src && length > 0)
2135 {
2136 int32 bytesNeeded = multiByteToWideString (nullptr, src, 0, sourceCodePage) * sizeof (char16);
2137 if (bytesNeeded)
2138 {
2139 bytesNeeded += sizeof (char16);
2140 char16* newStr = (char16*)malloc (bytesNeeded);
2141 if (multiByteToWideString (newStr, src, length + 1, sourceCodePage) < 0)
2142 {
2143 free (newStr);
2144 return false;
2145 }
2146 if (buffer8)
2147 free (buffer8);
2148
2149 buffer16 = newStr;
2150 isWide = true;
2151 updateLength ();
2152 }
2153 else
2154 {
2155 return false;
2156 }
2157 }
2158 isWide = true;
2159 }
2160 return true;
2161}
2162
2163#define SMTG_STRING_CHECK_CONVERSION 1
2164#define SMTG_STRING_CHECK_CONVERSION_NO_BREAK 1
2165
2166#if SMTG_STRING_CHECK_CONVERSION_NO_BREAK
2167 #define SMTG_STRING_CHECK_MSG FDebugPrint
2168#else
2169 #define SMTG_STRING_CHECK_MSG FDebugBreak
2170#endif
2171//-----------------------------------------------------------------------------
2172bool String::checkToMultiByte (uint32 destCodePage) const
2173{
2174 if (!isWide || isEmpty ())
2175 return true;
2176
2177#if DEVELOPMENT && SMTG_STRING_CHECK_CONVERSION
2178 int debugLen = length ();
2179 int debugNonASCII = 0;
2180 for (int32 i = 0; i < length (); i++)
2181 {
2182 if (buffer16[i] > 127)
2183 ++debugNonASCII;
2184 }
2185
2186 String* backUp = nullptr;
2187 if (debugNonASCII > 0)
2188 backUp = NEW String (*this);
2189#endif
2190
2191 // this should be avoided, since it can lead to information loss
2192 bool result = const_cast <String&> (*this).toMultiByte (destCodePage);
2193
2194#if DEVELOPMENT && SMTG_STRING_CHECK_CONVERSION
2195 if (backUp)
2196 {
2197 String temp (*this);
2198 temp.toWideString (destCodePage);
2199
2200 if (temp != *backUp)
2201 {
2202 backUp->toMultiByte (kCP_Utf8);
2203 SMTG_STRING_CHECK_MSG ("Indirect string conversion information loss ! %d/%d non ASCII chars: \"%s\" -> \"%s\"\n", debugNonASCII, debugLen, backUp->buffer8, buffer8);
2204 }
2205 else
2206 SMTG_STRING_CHECK_MSG ("Indirect string potential conversion information loss ! %d/%d non ASCII chars result: \"%s\"\n", debugNonASCII, debugLen, buffer8);
2207
2208 delete backUp;
2209 }
2210#endif
2211
2212 return result;
2213}
2214
2215//-----------------------------------------------------------------------------
2216bool String::toMultiByte (uint32 destCodePage)
2217{
2218 if (isWide)
2219 {
2220 if (buffer16 && len > 0)
2221 {
2222 int32 numChars = wideStringToMultiByte (nullptr, buffer16, 0, destCodePage) + sizeof (char8);
2223 char8* newStr = (char8*) malloc (numChars * sizeof (char8));
2224 if (wideStringToMultiByte (newStr, buffer16, numChars, destCodePage) <= 0)
2225 {
2226 free (newStr);
2227 return false;
2228 }
2229 free (buffer16);
2230 buffer8 = newStr;
2231 isWide = false;
2232 updateLength ();
2233 }
2234 isWide = false;
2235 }
2236 else if (destCodePage != kCP_Default)
2237 {
2238 if (toWideString () == false)
2239 return false;
2240 return toMultiByte (destCodePage);
2241 }
2242 return true;
2243}
2244
2245//-----------------------------------------------------------------------------
2246void String::fromUTF8 (const char8* utf8String)
2247{
2248 resize (0, false);
2249 _toWideString (utf8String, static_cast<int32> (strlen (utf8String)), kCP_Utf8);
2250}
2251
2252//-----------------------------------------------------------------------------
2254{
2255 if (isWide == false)
2256 return false;
2257
2258 if (buffer16 == nullptr)
2259 return true;
2260
2261#if SMTG_OS_WINDOWS
2262#ifdef UNICODE
2263 if (n != kUnicodeNormC)
2264 return false;
2265
2266 uint32 normCharCount = static_cast<uint32> (FoldString (MAP_PRECOMPOSED, wscast (buffer16), len, nullptr, 0));
2267 if (normCharCount == len)
2268 return true;
2269
2270 char16* newString = (char16*)malloc ((normCharCount + 1) * sizeof (char16));
2271 uint32 converterCount = static_cast<uint32> (FoldString (MAP_PRECOMPOSED, wscast (buffer16), len, wscast (newString), normCharCount + 1));
2272 if (converterCount != normCharCount)
2273 {
2274 free (newString);
2275 return false;
2276 }
2277 newString [converterCount] = 0;
2278 free (buffer16);
2279 buffer16 = newString;
2280 updateLength ();
2281 return true;
2282#else
2283 return false;
2284#endif
2285
2286#elif SMTG_OS_MACOS
2287 CFMutableStringRef origStr = (CFMutableStringRef)toCFStringRef (0xFFFF, true);
2288 if (origStr)
2289 {
2290 CFStringNormalizationForm normForm = kCFStringNormalizationFormD;
2291 switch (n)
2292 {
2293 case kUnicodeNormC: normForm = kCFStringNormalizationFormC; break;
2294 case kUnicodeNormD: normForm = kCFStringNormalizationFormD; break;
2295 case kUnicodeNormKC: normForm = kCFStringNormalizationFormKC; break;
2296 case kUnicodeNormKD: normForm = kCFStringNormalizationFormKD; break;
2297 }
2298 CFStringNormalize (origStr, normForm);
2299 bool result = fromCFStringRef (origStr);
2300 CFRelease (origStr);
2301 return result;
2302 }
2303 return false;
2304#else
2305 return false;
2306#endif
2307}
2308
2309//-----------------------------------------------------------------------------
2310void String::tryFreeBuffer ()
2311{
2312 if (buffer)
2313 {
2314 free (buffer);
2315 buffer = nullptr;
2316 }
2317}
2318
2319//-----------------------------------------------------------------------------
2320bool String::resize (uint32 newLength, bool wide, bool fill)
2321{
2322 if (newLength == 0)
2323 {
2324 tryFreeBuffer ();
2325 len = 0;
2326 isWide = wide ? 1 : 0;
2327 }
2328 else
2329 {
2330 size_t newCharSize = wide ? sizeof (char16) : sizeof (char8);
2331 size_t oldCharSize = (isWide != 0) ? sizeof (char16) : sizeof (char8);
2332
2333 size_t newBufferSize = (newLength + 1) * newCharSize;
2334 size_t oldBufferSize = (len + 1) * oldCharSize;
2335
2336 isWide = wide ? 1 : 0;
2337
2338 if (buffer)
2339 {
2340 if (newBufferSize != oldBufferSize)
2341 {
2342 void* newstr = realloc (buffer, newBufferSize);
2343 if (newstr == nullptr)
2344 return false;
2345 buffer = newstr;
2346 if (isWide)
2347 buffer16[newLength] = 0;
2348 else
2349 buffer8[newLength] = 0;
2350 }
2351 else if (wide && newCharSize != oldCharSize)
2352 buffer16[newLength] = 0;
2353 }
2354 else
2355 {
2356 void* newstr = malloc (newBufferSize);
2357 if (newstr == nullptr)
2358 return false;
2359 buffer = newstr;
2360 if (isWide)
2361 {
2362 buffer16[0] = 0;
2363 buffer16[newLength] = 0;
2364 }
2365 else
2366 {
2367 buffer8[0] = 0;
2368 buffer8[newLength] = 0;
2369 }
2370 }
2371
2372 if (fill && len < newLength && buffer)
2373 {
2374 if (isWide)
2375 {
2376 char16 c = ' ';
2377 for (uint32 i = len; i < newLength; i++)
2378 buffer16 [i] = c;
2379 }
2380 else
2381 {
2382 memset (buffer8 + len, ' ', newLength - len);
2383 }
2384 }
2385 }
2386 return true;
2387}
2388
2389//-----------------------------------------------------------------------------
2390bool String::setChar8 (uint32 index, char8 c)
2391{
2392 if (index == len && c == 0)
2393 return true;
2394
2395 if (index >= len)
2396 {
2397 if (c == 0)
2398 {
2399 if (resize (index, isWide, true) == false)
2400 return false;
2401 len = index;
2402 return true;
2403 }
2404
2405 if (resize (index + 1, isWide, true) == false)
2406 return false;
2407 len = index + 1;
2408 }
2409
2410 if (index < len && buffer)
2411 {
2412 if (isWide)
2413 {
2414 if (c == 0)
2415 buffer16[index] = 0;
2416 else
2417 {
2418 char8 src[] = {c, 0};
2419 char16 dest[8] = {0};
2420 if (multiByteToWideString (dest, src, 2) > 0)
2421 buffer16[index] = dest[0];
2422 }
2423 SMTG_ASSERT (buffer16[len] == 0)
2424 }
2425 else
2426 {
2427 buffer8[index] = c;
2428 SMTG_ASSERT (buffer8[len] == 0)
2429 }
2430
2431 if (c == 0)
2432 updateLength ();
2433
2434 return true;
2435 }
2436 return false;
2437}
2438
2439//-----------------------------------------------------------------------------
2440bool String::setChar16 (uint32 index, char16 c)
2441{
2442 if (index == len && c == 0)
2443 return true;
2444
2445 if (index >= len)
2446 {
2447 if (c == 0)
2448 {
2449 if (resize (index, isWide, true) == false)
2450 return false;
2451 len = index;
2452 return true;
2453 }
2454 if (resize (index + 1, isWide, true) == false)
2455 return false;
2456 len = index + 1;
2457 }
2458
2459 if (index < len && buffer)
2460 {
2461 if (isWide)
2462 {
2463 buffer16[index] = c;
2464 SMTG_ASSERT (buffer16[len] == 0)
2465 }
2466 else
2467 {
2468 SMTG_ASSERT (buffer8[len] == 0)
2469 char16 src[] = {c, 0};
2470 char8 dest[8] = {0};
2471 if (wideStringToMultiByte (dest, src, 2) > 0 && dest[1] == 0)
2472 buffer8[index] = dest[0];
2473 else
2474 return false;
2475 }
2476
2477 if (c == 0)
2478 updateLength ();
2479
2480 return true;
2481 }
2482 return false;
2483}
2484
2485//-----------------------------------------------------------------------------
2486String& String::assign (const ConstString& str, int32 n)
2487{
2488 if (str.isWideString ())
2489 return assign (str.text16 (), n < 0 ? str.length () : n);
2490 return assign (str.text8 (), n < 0 ? str.length () : n);
2491}
2492
2493//-----------------------------------------------------------------------------
2494String& String::assign (const char8* str, int32 n, bool isTerminated)
2495{
2496 if (str == buffer8)
2497 return *this;
2498
2499 if (isTerminated)
2500 {
2501 uint32 stringLength = (uint32)((str) ? strlen (str) : 0);
2502 n = n < 0 ? stringLength : Min<uint32> (n, stringLength);
2503 }
2504 else if (n < 0)
2505 return *this;
2506
2507 if (resize (n, false))
2508 {
2509 if (buffer8 && n > 0 && str)
2510 {
2511 memcpy (buffer8, str, n * sizeof (char8));
2512 SMTG_ASSERT (buffer8[n] == 0)
2513 }
2514 isWide = 0;
2515 len = n;
2516 }
2517 return *this;
2518}
2519
2520//-----------------------------------------------------------------------------
2521String& String::assign (const char16* str, int32 n, bool isTerminated)
2522{
2523 if (str == buffer16)
2524 return *this;
2525
2526 if (isTerminated)
2527 {
2528 uint32 stringLength = (uint32)((str) ? strlen16 (str) : 0);
2529 n = n < 0 ? stringLength : Min<uint32> (n, stringLength);
2530 }
2531 else if (n < 0)
2532 return *this;
2533
2534 if (resize (n, true))
2535 {
2536 if (buffer16 && n > 0 && str)
2537 {
2538 memcpy (buffer16, str, n * sizeof (char16));
2539 SMTG_ASSERT (buffer16[n] == 0)
2540 }
2541 isWide = 1;
2542 len = n;
2543 }
2544 return *this;
2545}
2546
2547//-----------------------------------------------------------------------------
2548String& String::assign (char8 c, int32 n)
2549{
2550 if (resize (n, false))
2551 {
2552 if (buffer8 && n > 0)
2553 {
2554 memset (buffer8, c, n * sizeof (char8));
2555 SMTG_ASSERT (buffer8[n] == 0)
2556 }
2557 isWide = 0;
2558 len = n;
2559 }
2560 return *this;
2561
2562}
2563
2564//-----------------------------------------------------------------------------
2565String& String::assign (char16 c, int32 n)
2566{
2567 if (resize (n, true))
2568 {
2569 if (buffer && n > 0)
2570 {
2571 for (int32 i = 0; i < n; i++)
2572 buffer16[i] = c;
2573 SMTG_ASSERT (buffer16[n] == 0)
2574 }
2575 isWide = 1;
2576 len = n;
2577 }
2578 return *this;
2579}
2580
2581//-----------------------------------------------------------------------------
2582String& String::append (const ConstString& str, int32 n)
2583{
2584 if (str.isWideString ())
2585 return append (str.text16 (), n);
2586 return append (str.text8 (), n);
2587}
2588
2589//-----------------------------------------------------------------------------
2590String& String::append (const char8* str, int32 n)
2591{
2592 if (str == buffer8)
2593 return *this;
2594
2595 if (len == 0)
2596 return assign (str, n);
2597
2598 if (isWide)
2599 {
2600 String tmp (str);
2601 if (tmp.toWideString () == false)
2602 return *this;
2603
2604 return append (tmp.buffer16, n);
2605 }
2606
2607 uint32 stringLength = (uint32)((str) ? strlen (str) : 0);
2608 n = n < 0 ? stringLength : Min<uint32> (n, stringLength);
2609
2610 if (n > 0)
2611 {
2612 int32 newlen = n + len;
2613 if (!resize (newlen, false))
2614 return *this;
2615
2616 if (buffer && str)
2617 {
2618 memcpy (buffer8 + len, str, n * sizeof (char8));
2619 SMTG_ASSERT (buffer8[newlen] == 0)
2620 }
2621
2622 len += n;
2623 }
2624 return *this;
2625}
2626
2627//-----------------------------------------------------------------------------
2628String& String::append (const char16* str, int32 n)
2629{
2630 if (str == buffer16)
2631 return *this;
2632
2633 if (len == 0)
2634 return assign (str, n);
2635
2636 if (!isWide)
2637 {
2638 if (toWideString () == false)
2639 return *this;
2640 }
2641
2642 uint32 stringLength = (uint32)((str) ? strlen16 (str) : 0);
2643 n = n < 0 ? stringLength : Min<uint32> (n, stringLength);
2644
2645 if (n > 0)
2646 {
2647 int32 newlen = n + len;
2648 if (!resize (newlen, true))
2649 return *this;
2650
2651 if (buffer16 && str)
2652 {
2653 memcpy (buffer16 + len, str, n * sizeof (char16));
2654 SMTG_ASSERT (buffer16[newlen] == 0)
2655 }
2656
2657 len += n;
2658 }
2659 return *this;
2660}
2661
2662//-----------------------------------------------------------------------------
2663String& String::append (const char8 c, int32 n)
2664{
2665 char8 str[] = {c, 0};
2666 if (n == 1)
2667 {
2668 return append (str, 1);
2669 }
2670 if (n > 1)
2671 {
2672 if (isWide)
2673 {
2674 String tmp (str);
2675 if (tmp.toWideString () == false)
2676 return *this;
2677
2678 return append (tmp.buffer16[0], n);
2679 }
2680
2681 int32 newlen = n + len;
2682 if (!resize (newlen, false))
2683 return *this;
2684
2685 if (buffer)
2686 {
2687 memset (buffer8 + len, c, n * sizeof (char8));
2688 SMTG_ASSERT (buffer8[newlen] == 0)
2689 }
2690
2691 len += n;
2692 }
2693 return *this;
2694}
2695
2696//-----------------------------------------------------------------------------
2697String& String::append (const char16 c, int32 n)
2698{
2699 if (n == 1)
2700 {
2701 char16 str[] = {c, 0};
2702 return append (str, 1);
2703 }
2704 if (n > 1)
2705 {
2706 if (!isWide)
2707 {
2708 if (toWideString () == false)
2709 return *this;
2710 }
2711
2712 int32 newlen = n + len;
2713 if (!resize (newlen, true))
2714 return *this;
2715
2716 if (buffer16)
2717 {
2718 for (int32 i = len; i < newlen; i++)
2719 buffer16[i] = c;
2720 SMTG_ASSERT (buffer16[newlen] == 0)
2721 }
2722
2723 len += n;
2724 }
2725 return *this;
2726}
2727
2728//-----------------------------------------------------------------------------
2729String& String::insertAt (uint32 idx, const ConstString& str, int32 n)
2730{
2731 if (str.isWideString ())
2732 return insertAt (idx, str.text16 (), n);
2733 return insertAt (idx, str.text8 (), n);
2734}
2735
2736//-----------------------------------------------------------------------------
2737String& String::insertAt (uint32 idx, const char8* str, int32 n)
2738{
2739 if (idx > len)
2740 return *this;
2741
2742 if (isWide)
2743 {
2744 String tmp (str);
2745 if (tmp.toWideString () == false)
2746 return *this;
2747 return insertAt (idx, tmp.buffer16, n);
2748 }
2749
2750 uint32 stringLength = (uint32)((str) ? strlen (str) : 0);
2751 n = n < 0 ? stringLength : Min<uint32> (n, stringLength);
2752
2753 if (n > 0)
2754 {
2755 int32 newlen = len + n;
2756 if (!resize (newlen, false))
2757 return *this;
2758
2759 if (buffer && str)
2760 {
2761 if (idx < len)
2762 memmove (buffer8 + idx + n, buffer8 + idx, (len - idx) * sizeof (char8));
2763 memcpy (buffer8 + idx, str, n * sizeof (char8));
2764 SMTG_ASSERT (buffer8[newlen] == 0)
2765 }
2766
2767 len += n;
2768 }
2769 return *this;
2770}
2771
2772//-----------------------------------------------------------------------------
2773String& String::insertAt (uint32 idx, const char16* str, int32 n)
2774{
2775 if (idx > len)
2776 return *this;
2777
2778 if (!isWide)
2779 {
2780 if (toWideString () == false)
2781 return *this;
2782 }
2783
2784 uint32 stringLength = (uint32)((str) ? strlen16 (str) : 0);
2785 n = n < 0 ? stringLength : Min<uint32> (n, stringLength);
2786
2787 if (n > 0)
2788 {
2789 int32 newlen = len + n;
2790 if (!resize (newlen, true))
2791 return *this;
2792
2793 if (buffer && str)
2794 {
2795 if (idx < len)
2796 memmove (buffer16 + idx + n, buffer16 + idx, (len - idx) * sizeof (char16));
2797 memcpy (buffer16 + idx, str, n * sizeof (char16));
2798 SMTG_ASSERT (buffer16[newlen] == 0)
2799 }
2800
2801 len += n;
2802 }
2803 return *this;
2804}
2805
2806//-----------------------------------------------------------------------------
2807String& String::replace (uint32 idx, int32 n1, const ConstString& str, int32 n2)
2808{
2809 if (str.isWideString ())
2810 return replace (idx, n1, str.text16 (), n2);
2811 return replace (idx, n1, str.text8 (), n2);
2812}
2813
2814// "replace" replaces n1 number of characters at the specified index with
2815// n2 characters from the specified string.
2816//-----------------------------------------------------------------------------
2817String& String::replace (uint32 idx, int32 n1, const char8* str, int32 n2)
2818{
2819 if (idx > len || str == nullptr)
2820 return *this;
2821
2822 if (isWide)
2823 {
2824 String tmp (str);
2825 if (tmp.toWideString () == false)
2826 return *this;
2827 if (tmp.length () == 0 || n2 == 0)
2828 return remove (idx, n1);
2829 return replace (idx, n1, tmp.buffer16, n2);
2830 }
2831
2832 if (n1 < 0 || idx + n1 > len)
2833 n1 = len - idx;
2834 if (n1 == 0)
2835 return *this;
2836
2837 uint32 stringLength = (uint32)((str) ? strlen (str) : 0);
2838 n2 = n2 < 0 ? stringLength : Min<uint32> (n2, stringLength);
2839
2840 uint32 newlen = len - n1 + n2;
2841 if (newlen > len)
2842 if (!resize (newlen, false))
2843 return *this;
2844
2845 if (buffer)
2846 {
2847 memmove (buffer8 + idx + n2, buffer8 + idx + n1, (len - (idx + n1)) * sizeof (char8));
2848 memcpy (buffer8 + idx, str, n2 * sizeof (char8));
2849 buffer8[newlen] = 0; // cannot be removed because resize is not called called in all cases (newlen > len)
2850 }
2851
2852 len = newlen;
2853
2854 return *this;
2855}
2856
2857//-----------------------------------------------------------------------------
2858String& String::replace (uint32 idx, int32 n1, const char16* str, int32 n2)
2859{
2860 if (idx > len || str == nullptr)
2861 return *this;
2862
2863 if (!isWide)
2864 {
2865 if (toWideString () == false)
2866 return *this;
2867 }
2868
2869 if (n1 < 0 || idx + n1 > len)
2870 n1 = len - idx;
2871 if (n1 == 0)
2872 return *this;
2873
2874 uint32 stringLength = (uint32)((str) ? strlen16 (str) : 0);
2875 n2 = n2 < 0 ? stringLength : Min<uint32> (n2, stringLength);
2876
2877 uint32 newlen = len - n1 + n2;
2878 if (newlen > len)
2879 if (!resize (newlen, true))
2880 return *this;
2881
2882 if (buffer)
2883 {
2884 memmove (buffer16 + idx + n2, buffer16 + idx + n1, (len - (idx + n1)) * sizeof (char16));
2885 memcpy (buffer16 + idx, str, n2 * sizeof (char16));
2886 buffer16[newlen] = 0; // cannot be removed because resize is not called called in all cases (newlen > len)
2887 }
2888
2889 len = newlen;
2890
2891 return *this;
2892}
2893
2894//-----------------------------------------------------------------------------
2895int32 String::replace (const char8* toReplace, const char8* toReplaceWith, bool all, CompareMode m)
2896{
2897 if (toReplace == nullptr || toReplaceWith == nullptr)
2898 return 0;
2899
2900 int32 result = 0;
2901
2902 int32 idx = findFirst (toReplace, -1, m);
2903 if (idx > -1)
2904 {
2905 int32 toReplaceLen = static_cast<int32> (strlen (toReplace));
2906 int32 toReplaceWithLen = static_cast<int32> (strlen (toReplaceWith));
2907 while (idx > -1)
2908 {
2909 replace (idx, toReplaceLen, toReplaceWith, toReplaceWithLen);
2910 result++;
2911
2912 if (all)
2913 idx = findNext (idx + toReplaceWithLen , toReplace, -1, m);
2914 else
2915 break;
2916 }
2917 }
2918
2919 return result;
2920}
2921
2922//-----------------------------------------------------------------------------
2923int32 String::replace (const char16* toReplace, const char16* toReplaceWith, bool all, CompareMode m)
2924{
2925 if (toReplace == nullptr || toReplaceWith == nullptr)
2926 return 0;
2927
2928 int32 result = 0;
2929
2930 int32 idx = findFirst (toReplace, -1, m);
2931 if (idx > -1)
2932 {
2933 int32 toReplaceLen = strlen16 (toReplace);
2934 int32 toReplaceWithLen = strlen16 (toReplaceWith);
2935 while (idx > -1)
2936 {
2937 replace (idx, toReplaceLen, toReplaceWith, toReplaceWithLen);
2938 result++;
2939
2940 if (all)
2941 idx = findNext (idx + toReplaceWithLen, toReplace, -1, m);
2942 else
2943 break;
2944 }
2945 }
2946 return result;
2947}
2948
2949//-----------------------------------------------------------------------------
2950template <class T>
2951static bool performReplace (T* str, const T* toReplace, T toReplaceBy)
2952{
2953 bool anyReplace = false;
2954 T* p = str;
2955 while (*p)
2956 {
2957 const T* rep = toReplace;
2958 while (*rep)
2959 {
2960 if (*p == *rep)
2961 {
2962 *p = toReplaceBy;
2963 anyReplace = true;
2964 break;
2965 }
2966 rep++;
2967 }
2968 p++;
2969 }
2970 return anyReplace;
2971}
2972
2973//-----------------------------------------------------------------------------
2974bool String::replaceChars8 (const char8* toReplace, char8 toReplaceBy)
2975{
2976 if (isEmpty ())
2977 return false;
2978
2979 if (isWide)
2980 {
2981 String toReplaceW (toReplace);
2982 if (toReplaceW.toWideString () == false)
2983 return false;
2984
2985 char8 src[] = {toReplaceBy, 0};
2986 char16 dest[2] = {0};
2987 if (multiByteToWideString (dest, src, 2) > 0)
2988 {
2989 return replaceChars16 (toReplaceW.text16 (), dest[0]);
2990 }
2991 return false;
2992 }
2993
2994 if (toReplaceBy == 0)
2995 toReplaceBy = ' ';
2996
2997 return performReplace<char8> (buffer8, toReplace, toReplaceBy);
2998}
2999
3000//-----------------------------------------------------------------------------
3001bool String::replaceChars16 (const char16* toReplace, char16 toReplaceBy)
3002{
3003 if (isEmpty ())
3004 return false;
3005
3006 if (!isWide)
3007 {
3008 String toReplaceA (toReplace);
3009 if (toReplaceA.toMultiByte () == false)
3010 return false;
3011
3012 if (toReplaceA.length () > 1)
3013 {
3014 SMTG_WARNING("cannot replace non ASCII chars on non Wide String")
3015 return false;
3016 }
3017
3018 char16 src[] = {toReplaceBy, 0};
3019 char8 dest[8] = {0};
3020 if (wideStringToMultiByte (dest, src, 2) > 0 && dest[1] == 0)
3021 return replaceChars8 (toReplaceA.text8 (), dest[0]);
3022
3023 return false;
3024 }
3025
3026 if (toReplaceBy == 0)
3027 toReplaceBy = STR16 (' ');
3028
3029 return performReplace<char16> (buffer16, toReplace, toReplaceBy);
3030}
3031
3032// "remove" removes the specified number of characters from the string
3033// starting at the specified index.
3034//-----------------------------------------------------------------------------
3035String& String::remove (uint32 idx, int32 n)
3036{
3037 if (isEmpty () || idx >= len || n == 0)
3038 return *this;
3039
3040 if ((idx + n > len) || n < 0)
3041 n = len - idx;
3042 else
3043 {
3044 int32 toMove = len - idx - n;
3045 if (buffer)
3046 {
3047 if (isWide)
3048 memmove (buffer16 + idx, buffer16 + idx + n, toMove * sizeof (char16));
3049 else
3050 memmove (buffer8 + idx, buffer8 + idx + n, toMove * sizeof (char8));
3051 }
3052 }
3053
3054 resize (len - n, isWide);
3055 updateLength ();
3056
3057 return *this;
3058}
3059
3060//-----------------------------------------------------------------------------
3061bool String::removeSubString (const ConstString& subString, bool allOccurences)
3062{
3063 bool removed = false;
3064 while (!removed || allOccurences)
3065 {
3066 int32 idx = findFirst (subString);
3067 if (idx < 0)
3068 break;
3069 remove (idx, subString.length ());
3070 removed = true;
3071 }
3072 return removed;
3073}
3074
3075//-----------------------------------------------------------------------------
3076template <class T, class F>
3077static uint32 performTrim (T* str, uint32 length, F func, bool funcResult)
3078{
3079 uint32 toRemoveAtHead = 0;
3080 uint32 toRemoveAtTail = 0;
3081
3082 T* p = str;
3083
3084 while ((*p) && ((func (*p) != 0) == funcResult))
3085 p++;
3086
3087 toRemoveAtHead = static_cast<uint32> (p - str);
3088
3089 if (toRemoveAtHead < length)
3090 {
3091 p = str + length - 1;
3092
3093 while (((func (*p) != 0) == funcResult) && (p > str))
3094 {
3095 p--;
3096 toRemoveAtTail++;
3097 }
3098 }
3099
3100 uint32 newLength = length - (toRemoveAtHead + toRemoveAtTail);
3101 if (newLength != length)
3102 {
3103 if (toRemoveAtHead)
3104 memmove (str, str + toRemoveAtHead, newLength * sizeof (T));
3105 }
3106 return newLength;
3107}
3108
3109// "trim" trims the leading and trailing unwanted characters from the string.
3110//-----------------------------------------------------------------------------
3111bool String::trim (String::CharGroup group)
3112{
3113 if (isEmpty ())
3114 return false;
3115
3116 uint32 newLength;
3117
3118 switch (group)
3119 {
3120 case kSpace:
3121 if (isWide)
3122 newLength = performTrim<char16> (buffer16, len, iswspace, true);
3123 else
3124 newLength = performTrim<char8> (buffer8, len, isspace, true);
3125 break;
3126
3127 case kNotAlphaNum:
3128 if (isWide)
3129 newLength = performTrim<char16> (buffer16, len, iswalnum, false);
3130 else
3131 newLength = performTrim<char8> (buffer8, len, isalnum, false);
3132 break;
3133
3134 case kNotAlpha:
3135 if (isWide)
3136 newLength = performTrim<char16> (buffer16, len, iswalpha, false);
3137 else
3138 newLength = performTrim<char8> (buffer8, len, isalpha, false);
3139 break;
3140
3141 default: // Undefined enum value
3142 return false;
3143 }
3144
3145 if (newLength != len)
3146 {
3147 resize (newLength, isWide);
3148 len = newLength;
3149 return true;
3150 }
3151 return false;
3152}
3153
3154//-----------------------------------------------------------------------------
3155template <class T, class F>
3156static uint32 performRemove (T* str, uint32 length, F func, bool funcResult)
3157{
3158 T* p = str;
3159
3160 while (*p)
3161 {
3162 if ((func (*p) != 0) == funcResult)
3163 {
3164 size_t toMove = length - (p - str);
3165 memmove (p, p + 1, toMove * sizeof (T));
3166 length--;
3167 }
3168 else
3169 p++;
3170 }
3171 return length;
3172}
3173//-----------------------------------------------------------------------------
3174void String::removeChars (CharGroup group)
3175{
3176 if (isEmpty ())
3177 return;
3178
3179 uint32 newLength;
3180
3181 switch (group)
3182 {
3183 case kSpace:
3184 if (isWide)
3185 newLength = performRemove<char16> (buffer16, len, iswspace, true);
3186 else
3187 newLength = performRemove<char8> (buffer8, len, isspace, true);
3188 break;
3189
3190 case kNotAlphaNum:
3191 if (isWide)
3192 newLength = performRemove<char16> (buffer16, len, iswalnum, false);
3193 else
3194 newLength = performRemove<char8> (buffer8, len, isalnum, false);
3195 break;
3196
3197 case kNotAlpha:
3198 if (isWide)
3199 newLength = performRemove<char16> (buffer16, len, iswalpha, false);
3200 else
3201 newLength = performRemove<char8> (buffer8, len, isalpha, false);
3202 break;
3203
3204 default: // Undefined enum value
3205 return;
3206 }
3207
3208 if (newLength != len)
3209 {
3210 resize (newLength, isWide);
3211 len = newLength;
3212 }
3213}
3214
3215//-----------------------------------------------------------------------------
3216template <class T>
3217static uint32 performRemoveChars (T* str, uint32 length, const T* toRemove)
3218{
3219 T* p = str;
3220
3221 while (*p)
3222 {
3223 bool found = false;
3224 const T* rem = toRemove;
3225 while (*rem)
3226 {
3227 if (*p == *rem)
3228 {
3229 found = true;
3230 break;
3231 }
3232 rem++;
3233 }
3234
3235 if (found)
3236 {
3237 size_t toMove = length - (p - str);
3238 memmove (p, p + 1, toMove * sizeof (T));
3239 length--;
3240 }
3241 else
3242 p++;
3243 }
3244 return length;
3245}
3246
3247//-----------------------------------------------------------------------------
3248bool String::removeChars8 (const char8* toRemove)
3249{
3250 if (isEmpty () || toRemove == nullptr)
3251 return true;
3252
3253 if (isWide)
3254 {
3255 String wStr (toRemove);
3256 if (wStr.toWideString () == false)
3257 return false;
3258 return removeChars16 (wStr.text16 ());
3259 }
3260
3261 uint32 newLength = performRemoveChars<char8> (buffer8, len, toRemove);
3262
3263 if (newLength != len)
3264 {
3265 resize (newLength, false);
3266 len = newLength;
3267 }
3268 return true;
3269}
3270
3271//-----------------------------------------------------------------------------
3272bool String::removeChars16 (const char16* toRemove)
3273{
3274 if (isEmpty () || toRemove == nullptr)
3275 return true;
3276
3277 if (!isWide)
3278 {
3279 String str8 (toRemove);
3280 if (str8.toMultiByte () == false)
3281 return false;
3282 return removeChars8 (str8.text8 ());
3283 }
3284
3285 uint32 newLength = performRemoveChars<char16> (buffer16, len, toRemove);
3286
3287 if (newLength != len)
3288 {
3289 resize (newLength, true);
3290 len = newLength;
3291 }
3292 return true;
3293}
3294
3295//-----------------------------------------------------------------------------
3296String& String::printf (const char8* format, ...)
3297{
3298 char8 string[kPrintfBufferSize];
3299
3300 va_list marker;
3301 va_start (marker, format);
3302
3303 vsnprintf (string, kPrintfBufferSize-1, format, marker);
3304 return assign (string);
3305}
3306
3307
3308//-----------------------------------------------------------------------------
3309String& String::printf (const char16* format, ...)
3310{
3311 char16 string[kPrintfBufferSize];
3312
3313 va_list marker;
3314 va_start (marker, format);
3315
3316 vsnwprintf (string, kPrintfBufferSize-1, format, marker);
3317 return assign (string);
3318}
3319
3320//-----------------------------------------------------------------------------
3321String& String::vprintf (const char8* format, va_list args)
3322{
3323 char8 string[kPrintfBufferSize];
3324
3325 vsnprintf (string, kPrintfBufferSize-1, format, args);
3326 return assign (string);
3327}
3328
3329//-----------------------------------------------------------------------------
3330String& String::vprintf (const char16* format, va_list args)
3331{
3332 char16 string[kPrintfBufferSize];
3333
3334 vsnwprintf (string, kPrintfBufferSize-1, format, args);
3335 return assign (string);
3336}
3337
3338//-----------------------------------------------------------------------------
3339String& String::printInt64 (int64 value)
3340{
3341 if (isWide)
3342 {
3343 #if SMTG_CPP11
3344 return String::printf (STR("%") STR(FORMAT_INT64A), value);
3345 #else
3346 return String::printf (STR("%" FORMAT_INT64A), value);
3347 #endif
3348 }
3349 else
3350 return String::printf ("%" FORMAT_INT64A, value);
3351}
3352
3353//-----------------------------------------------------------------------------
3354String& String::printFloat (double value, uint32 maxPrecision)
3355{
3356 static constexpr auto kMaxAfterCommaResolution = 16;
3357 // escape point for integer values, avoid unnecessary complexity later on
3358 const bool withinInt64Boundaries = value <= std::numeric_limits<int64>::max () && value >= std::numeric_limits<int64>::lowest ();
3359 if (withinInt64Boundaries && (maxPrecision == 0 || std::round (value) == value))
3360 return printInt64 (value);
3361
3362 const auto absValue = std::abs (value);
3363 const uint32 valueExponent = absValue >= 1 ? std::log10 (absValue) : -std::log10 (absValue) + 1;
3364
3365 maxPrecision = std::min<uint32> (kMaxAfterCommaResolution - valueExponent, maxPrecision);
3366
3367 if (isWide)
3368 printf (STR ("%s%dlf"), STR ("%."), maxPrecision);
3369 else
3370 printf ("%s%dlf", "%.", maxPrecision);
3371
3372 if (isWide)
3373 printf (text16 (), value);
3374 else
3375 printf (text8 (), value);
3376
3377 // trim trail zeros
3378 for (int32 i = length () - 1; i >= 0; i--)
3379 {
3380 if (isWide && testChar16 (i, '0') || testChar8 (i, '0'))
3381 remove (i);
3382 else if (isWide && testChar16(i,'.') || testChar8(i, '.'))
3383 {
3384 remove(i);
3385 break;
3386 }
3387 else
3388 break;
3389 }
3390
3391 return *this;
3392}
3393
3394//-----------------------------------------------------------------------------
3395bool String::incrementTrailingNumber (uint32 width, tchar separator, uint32 minNumber, bool applyOnlyFormat)
3396{
3397 if (width > 32)
3398 return false;
3399
3400 int64 number = 1;
3401 int32 index = getTrailingNumberIndex ();
3402 if (index >= 0)
3403 {
3404 if (scanInt64 (number, index))
3405 if (!applyOnlyFormat)
3406 number++;
3407
3408 if (separator != 0 && index > 0 && testChar (index - 1, separator) == true)
3409 index--;
3410
3411 remove (index);
3412 }
3413
3414 if (number < minNumber)
3415 number = minNumber;
3416
3417 if (isWide)
3418 {
3419 char16 format[64];
3420 char16 trail[128];
3421 if (separator && isEmpty () == false)
3422 {
3423 sprintf16 (format, STR16 ("%%c%%0%uu"), width);
3424 sprintf16 (trail, format, separator, (uint32) number);
3425 }
3426 else
3427 {
3428 sprintf16 (format, STR16 ("%%0%uu"), width);
3429 sprintf16 (trail, format, (uint32) number);
3430 }
3431 append (trail);
3432 }
3433 else
3434 {
3435 static constexpr auto kFormatSize = 64u;
3436 static constexpr auto kTrailSize = 64u;
3437 char format[kFormatSize];
3438 char trail[kTrailSize];
3439 if (separator && isEmpty () == false)
3440 {
3441 snprintf (format, kFormatSize, "%%c%%0%uu", width);
3442 snprintf (trail, kTrailSize, format, separator, (uint32) number);
3443 }
3444 else
3445 {
3446 snprintf (format, kFormatSize, "%%0%uu", width);
3447 snprintf (trail, kTrailSize, format, (uint32) number);
3448 }
3449 append (trail);
3450 }
3451
3452 return true;
3453}
3454
3455//-----------------------------------------------------------------------------
3456void String::toLower (uint32 index)
3457{
3458 if (buffer && index < len)
3459 {
3460 if (isWide)
3461 buffer16[index] = ConstString::toLower (buffer16[index]);
3462 else
3463 buffer8[index] = ConstString::toLower (buffer8[index]);
3464 }
3465}
3466
3467//-----------------------------------------------------------------------------
3469{
3470 int32 i = len;
3471 if (buffer && i > 0)
3472 {
3473 if (isWide)
3474 {
3475#if SMTG_OS_MACOS
3476 CFMutableStringRef cfStr = CFStringCreateMutableWithExternalCharactersNoCopy (kCFAllocator, (UniChar*)buffer16, len, len+1, kCFAllocatorNull);
3477 CFStringLowercase (cfStr, NULL);
3478 CFRelease (cfStr);
3479#else
3480 char16* c = buffer16;
3481 while (i--)
3482 {
3483 *c = ConstString::toLower (*c);
3484 c++;
3485 }
3486#endif
3487 }
3488 else
3489 {
3490 char8* c = buffer8;
3491 while (i--)
3492 {
3493 *c = ConstString::toLower (*c);
3494 c++;
3495 }
3496 }
3497 }
3498}
3499
3500//-----------------------------------------------------------------------------
3501void String::toUpper (uint32 index)
3502{
3503 if (buffer && index < len)
3504 {
3505 if (isWide)
3506 buffer16[index] = ConstString::toUpper (buffer16[index]);
3507 else
3508 buffer8[index] = ConstString::toUpper (buffer8[index]);
3509 }
3510}
3511
3512//-----------------------------------------------------------------------------
3514{
3515 int32 i = len;
3516 if (buffer && i > 0)
3517 {
3518 if (isWide)
3519 {
3520#if SMTG_OS_MACOS
3521 CFMutableStringRef cfStr = CFStringCreateMutableWithExternalCharactersNoCopy (kCFAllocator, (UniChar*)buffer16, len, len+1, kCFAllocatorNull);
3522 CFStringUppercase (cfStr, NULL);
3523 CFRelease (cfStr);
3524#else
3525 char16* c = buffer16;
3526 while (i--)
3527 {
3528 *c = ConstString::toUpper (*c);
3529 c++;
3530 }
3531#endif
3532 }
3533 else
3534 {
3535 char8* c = buffer8;
3536 while (i--)
3537 {
3538 *c = ConstString::toUpper (*c);
3539 c++;
3540 }
3541 }
3542 }
3543}
3544
3545//-----------------------------------------------------------------------------
3547{
3548 switch (var.getType ())
3549 {
3550 case FVariant::kString8:
3551 assign (var.getString8 ());
3552 return true;
3553
3554 case FVariant::kString16:
3555 assign (var.getString16 ());
3556 return true;
3557
3558 case FVariant::kFloat:
3559 printFloat (var.getFloat ());
3560 return true;
3561
3562 case FVariant::kInteger:
3563 printInt64 (var.getInt ());
3564 return true;
3565
3566 default:
3567 remove ();
3568 }
3569 return false;
3570}
3571
3572//-----------------------------------------------------------------------------
3573void String::toVariant (FVariant& var) const
3574{
3575 if (isWide)
3576 {
3577 var.setString16 (text16 ());
3578 }
3579 else
3580 {
3581 var.setString8 (text8 ());
3582 }
3583}
3584
3585//-----------------------------------------------------------------------------
3586bool String::fromAttributes (IAttributes* a, IAttrID attrID)
3587{
3588 FVariant variant;
3589 if (a->get (attrID, variant) == kResultTrue)
3590 return fromVariant (variant);
3591 return false;
3592}
3593
3594//-----------------------------------------------------------------------------
3595bool String::toAttributes (IAttributes* a, IAttrID attrID)
3596{
3597 FVariant variant;
3598 toVariant (variant);
3599 if (a->set (attrID, variant) == kResultTrue)
3600 return true;
3601 return false;
3602}
3603
3604// "swapContent" swaps ownership of the strings pointed to
3605//-----------------------------------------------------------------------------
3607{
3608 void* tmp = s.buffer;
3609 uint32 tmpLen = s.len;
3610 bool tmpWide = s.isWide;
3611 s.buffer = buffer;
3612 s.len = len;
3613 s.isWide = isWide;
3614 buffer = tmp;
3615 len = tmpLen;
3616 isWide = tmpWide;
3617}
3618
3619//-----------------------------------------------------------------------------
3621{
3622 resize (0, other.isWide);
3623 buffer = other.buffer;
3624 len = other.len;
3625
3626 other.buffer = nullptr;
3627 other.len = 0;
3628}
3629
3630//-----------------------------------------------------------------------------
3631void String::take (void* b, bool wide)
3632{
3633 resize (0, wide);
3634 buffer = b;
3635 isWide = wide;
3636 updateLength ();
3637}
3638
3639//-----------------------------------------------------------------------------
3640void* String::pass ()
3641{
3642 void* res = buffer;
3643 len = 0;
3644 buffer = nullptr;
3645 return res;
3646}
3647
3648//-----------------------------------------------------------------------------
3650{
3651 void* passed = pass ();
3652
3653 if (isWide)
3654 {
3655 if (passed)
3656 {
3657 var.setString16 ((const char16*)passed);
3658 var.setOwner (true);
3659 }
3660 else
3661 var.setString16 (kEmptyString16);
3662 }
3663 else
3664 {
3665 if (passed)
3666 {
3667 var.setString8 ((const char8*)passed);
3668 var.setOwner (true);
3669 }
3670 else
3671 var.setString8 (kEmptyString8);
3672 }
3673}
3674
3675
3676//-----------------------------------------------------------------------------
3677unsigned char* String::toPascalString (unsigned char* buf)
3678{
3679 if (buffer)
3680 {
3681 if (isWide)
3682 {
3683 String tmp (*this);
3684 tmp.toMultiByte ();
3685 return tmp.toPascalString (buf);
3686 }
3687
3688 int32 length = len;
3689 if (length > 255)
3690 length = 255;
3691 buf[0] = (uint8)length;
3692 while (length >= 0)
3693 {
3694 buf[length + 1] = buffer8[length];
3695 length--;
3696 }
3697 return buf;
3698 }
3699
3700 *buf = 0;
3701 return buf;
3702}
3703
3704//-----------------------------------------------------------------------------
3705const String& String::fromPascalString (const unsigned char* buf)
3706{
3707 resize (0, false);
3708 isWide = 0;
3709 int32 length = buf[0];
3710 resize (length + 1, false);
3711 buffer8[length] = 0; // cannot be removed, because we only do the 0-termination for multibyte buffer8
3712 while (--length >= 0)
3713 buffer8[length] = buf[length + 1];
3714 len = buf[0];
3715 return *this;
3716}
3717
3718#if SMTG_OS_MACOS
3719
3720//-----------------------------------------------------------------------------
3721bool String::fromCFStringRef (const void* cfStr, uint32 encoding)
3722{
3723 if (cfStr == 0)
3724 return false;
3725
3726 CFStringRef strRef = (CFStringRef)cfStr;
3727 if (isWide)
3728 {
3729 CFRange range = { 0, CFStringGetLength (strRef)};
3730 CFIndex usedBytes;
3731 if (resize (static_cast<int32> (range.length + 1), true))
3732 {
3733 if (encoding == 0xFFFF)
3734 encoding = kCFStringEncodingUnicode;
3735 if (CFStringGetBytes (strRef, range, encoding, ' ', false, (UInt8*)buffer16, range.length * 2, &usedBytes) > 0)
3736 {
3737 buffer16[usedBytes/2] = 0;
3738 this->len = strlen16 (buffer16);
3739 return true;
3740 }
3741 }
3742 }
3743 else
3744 {
3745 if (cfStr == 0)
3746 return false;
3747 if (encoding == 0xFFFF)
3748 encoding = kCFStringEncodingASCII;
3749 int32 len = static_cast<int32> (CFStringGetLength (strRef) * 2);
3750 if (resize (++len, false))
3751 {
3752 if (CFStringGetCString (strRef, buffer8, len, encoding))
3753 {
3754 this->len = static_cast<int32> (strlen (buffer8));
3755 return true;
3756 }
3757 }
3758 }
3759
3760 return false;
3761}
3762
3763//-----------------------------------------------------------------------------
3764void* ConstString::toCFStringRef (uint32 encoding, bool mutableCFString) const
3765{
3766 if (mutableCFString)
3767 {
3768 CFMutableStringRef str = CFStringCreateMutable (kCFAllocator, 0);
3769 if (isWide)
3770 {
3771 CFStringAppendCharacters (str, (const UniChar *)buffer16, len);
3772 return str;
3773 }
3774 else
3775 {
3776 if (encoding == 0xFFFF)
3777 encoding = kCFStringEncodingASCII;
3778 CFStringAppendCString (str, buffer8, encoding);
3779 return str;
3780 }
3781 }
3782 else
3783 {
3784 if (isWide)
3785 {
3786 if (encoding == 0xFFFF)
3787 encoding = kCFStringEncodingUnicode;
3788 return (void*)CFStringCreateWithBytes (kCFAllocator, (const unsigned char*)buffer16, len * 2, encoding, false);
3789 }
3790 else
3791 {
3792 if (encoding == 0xFFFF)
3793 encoding = kCFStringEncodingASCII;
3794 if (buffer8)
3795 return (void*)CFStringCreateWithCString (kCFAllocator, buffer8, encoding);
3796 else
3797 return (void*)CFStringCreateWithCString (kCFAllocator, "", encoding);
3798 }
3799 }
3800 return nullptr;
3801}
3802
3803#endif
3804
3805//-----------------------------------------------------------------------------
3806uint32 hashString8 (const char8* s, uint32 m)
3807{
3808 uint32 h = 0;
3809 if (s)
3810 {
3811 for (h = 0; *s != '\0'; s++)
3812 h = (64 * h + *s) % m;
3813 }
3814 return h;
3815}
3816
3817//-----------------------------------------------------------------------------
3818uint32 hashString16 (const char16* s, uint32 m)
3819{
3820 uint32 h = 0;
3821 if (s)
3822 {
3823 for (h = 0; *s != 0; s++)
3824 h = (64 * h + *s) % m;
3825 }
3826 return h;
3827}
3828
3829//------------------------------------------------------------------------
3830template <class T> int32 tstrnatcmp (const T* s1, const T* s2, bool caseSensitive = true)
3831{
3832 if (s1 == nullptr && s2 == nullptr)
3833 return 0;
3834 if (s1 == nullptr)
3835 return -1;
3836 if (s2 == nullptr)
3837 return 1;
3838
3839 while (*s1 && *s2)
3840 {
3842 {
3843 int32 s1LeadingZeros = 0;
3844 while (*s1 == '0')
3845 {
3846 s1++; // skip leading zeros
3847 s1LeadingZeros++;
3848 }
3849 int32 s2LeadingZeros = 0;
3850 while (*s2 == '0')
3851 {
3852 s2++; // skip leading zeros
3853 s2LeadingZeros++;
3854 }
3855
3856 int32 countS1Digits = 0;
3857 while (*(s1 + countS1Digits) && ConstString::isCharDigit (*(s1 + countS1Digits)))
3858 countS1Digits++;
3859 int32 countS2Digits = 0;
3860 while (*(s2 + countS2Digits) && ConstString::isCharDigit (*(s2 + countS2Digits)))
3861 countS2Digits++;
3862
3863 if (countS1Digits != countS2Digits)
3864 return countS1Digits - countS2Digits; // one number is longer than the other
3865
3866 for (int32 i = 0; i < countS1Digits; i++)
3867 {
3868 // countS1Digits == countS2Digits
3869 if (*s1 != *s2)
3870 return (int32)(*s1 - *s2); // the digits differ
3871 s1++;
3872 s2++;
3873 }
3874
3875 if (s1LeadingZeros != s2LeadingZeros)
3876 return s1LeadingZeros - s2LeadingZeros; // differentiate by the number of leading zeros
3877 }
3878 else
3879 {
3880 if (caseSensitive == false)
3881 {
3882 T srcToUpper = static_cast<T> (toupper (*s1));
3883 T dstToUpper = static_cast<T> (toupper (*s2));
3884 if (srcToUpper != dstToUpper)
3885 return (int32)(srcToUpper - dstToUpper);
3886 }
3887 else if (*s1 != *s2)
3888 return (int32)(*s1 - *s2);
3889
3890 s1++;
3891 s2++;
3892 }
3893 }
3894
3895 if (*s1 == 0 && *s2 == 0)
3896 return 0;
3897 if (*s1 == 0)
3898 return -1;
3899 if (*s2 == 0)
3900 return 1;
3901 return (int32)(*s1 - *s2);
3902}
3903
3904//------------------------------------------------------------------------
3905int32 strnatcmp8 (const char8* s1, const char8* s2, bool caseSensitive /*= true*/)
3906{
3907 return tstrnatcmp (s1, s2, caseSensitive);
3908}
3909
3910//------------------------------------------------------------------------
3911int32 strnatcmp16 (const char16* s1, const char16* s2, bool caseSensitive /*= true*/)
3912{
3913 return tstrnatcmp (s1, s2, caseSensitive);
3914}
3915
3916//-----------------------------------------------------------------------------
3917// StringObject Implementation
3918//-----------------------------------------------------------------------------
3919void PLUGIN_API StringObject::setText (const char8* text)
3920{
3921 assign (text);
3922}
3923
3924//-----------------------------------------------------------------------------
3925void PLUGIN_API StringObject::setText8 (const char8* text)
3926{
3927 assign (text);
3928}
3929
3930//-----------------------------------------------------------------------------
3931void PLUGIN_API StringObject::setText16 (const char16* text)
3932{
3933 assign (text);
3934}
3935
3936//-----------------------------------------------------------------------------
3937const char8* PLUGIN_API StringObject::getText8 ()
3938{
3939 return text8 ();
3940}
3941
3942//-----------------------------------------------------------------------------
3943const char16* PLUGIN_API StringObject::getText16 ()
3944{
3945 return text16 ();
3946}
3947
3948//-----------------------------------------------------------------------------
3949void PLUGIN_API StringObject::take (void* s, bool _isWide)
3950{
3951 String::take (s, _isWide);
3952}
3953
3954//-----------------------------------------------------------------------------
3955bool PLUGIN_API StringObject::isWideString () const
3956{
3957 return String::isWideString ();
3958}
3959
3960//------------------------------------------------------------------------
3961} // namespace Steinberg
assert
Invariant String.
Definition fstring.h:117
virtual const char8 * text8() const
Returns pointer to string of type char8.
Definition fstring.h:511
int32 getFirstDifferent(const ConstString &str, CompareMode=kCaseSensitive) const
Returns position of first different character.
Definition fstring.cpp:1270
int64 getTrailingNumber(int64 fallback=0) const
Returns result of scanInt64 or the fallback.
Definition fstring.cpp:1755
bool isAsciiString() const
Checks if all characters in string are in ascii range.
Definition fstring.cpp:1784
virtual const tchar * text() const
Returns pointer to string of type tchar.
Definition fstring.h:501
static bool scanUInt32_16(const char16 *text, uint32 &value, bool scanToEnd=true)
Converts string of type char16 to int32 value.
Definition fstring.h:616
static bool scanInt32_8(const char8 *text, int32 &value, bool scanToEnd=true)
Converts string of type char8 to int32 value.
Definition fstring.h:568
static bool isCharSpace(char8 character)
Returns true if character is a space.
Definition fstring.cpp:1618
static bool scanUInt64_16(const char16 *text, uint64 &value, bool scanToEnd=true)
Converts string of type char16 to uint64 value.
Definition fstring.cpp:1429
static int32 wideStringToMultiByte(char8 *dest, const char16 *source, int32 char8Count, uint32 destCodePage=kCP_Default)
If dest is zero, this returns the maximum number of bytes needed to convert source.
Definition fstring.cpp:1889
@ kCaseSensitive
Comparison is done with regard to character's case.
Definition fstring.h:171
bool endsWith(const ConstString &str, CompareMode m=kCaseSensitive) const
Check if this ends with str.
Definition fstring.cpp:859
bool isDigit(uint32 index) const
Returns true if character at position is a digit.
Definition fstring.cpp:1721
static bool scanHex_16(const char16 *text, uint8 &value, bool scanToEnd=true)
Converts string of type char16 to hex/unit8 value.
Definition fstring.cpp:1479
bool testChar8(uint32 index, char8 c) const
Returns true if character is equal at position 'index'.
Definition fstring.cpp:505
bool scanInt32(int32 &value, uint32 offset=0, bool scanToEnd=true) const
Converts string to int32 value starting at offset.
Definition fstring.cpp:1367
static bool scanHex_8(const char8 *text, uint8 &value, bool scanToEnd=true)
Converts string of type char8 to hex/unit8 value.
Definition fstring.cpp:1461
bool scanHex(uint8 &value, uint32 offset=0, bool scanToEnd=true) const
Converts string to hex/uint8 value starting at offset.
Definition fstring.cpp:1356
bool scanUInt32(uint32 &value, uint32 offset=0, bool scanToEnd=true) const
Converts string to uint32 value starting at offset.
Definition fstring.cpp:1378
bool extract(String &result, uint32 idx, int32 n=-1) const
Get n characters long substring starting at index (n=-1: until end)
Definition fstring.cpp:539
static char8 toLower(char8 c)
Converts to lower case.
Definition fstring.cpp:1590
static bool isCharAlphaNum(char8 character)
Returns true if character is an alphanumeric character.
Definition fstring.cpp:1661
int32 countOccurences(char8 c, uint32 startIndex, CompareMode=kCaseSensitive) const
Counts occurences of c within this starting at index.
Definition fstring.cpp:1214
bool isWideString() const
Returns true if string is wide.
Definition fstring.h:276
bool startsWith(const ConstString &str, CompareMode m=kCaseSensitive) const
Check if this starts with str.
Definition fstring.cpp:813
int32 getTrailingNumberIndex(uint32 width=0) const
Returns start index of trailing number.
Definition fstring.cpp:1732
bool scanUInt64(uint64 &value, uint32 offset=0, bool scanToEnd=true) const
Converts string to uint64 value starting at offset.
Definition fstring.cpp:1345
bool isEmpty() const
Return true if string is empty.
Definition fstring.h:129
static int32 multiByteToWideString(char16 *dest, const char8 *source, int32 wcharCount, uint32 sourceCodePage=kCP_Default)
If dest is zero, this returns the maximum number of bytes needed to convert source.
Definition fstring.cpp:1823
bool contains(const ConstString &str, CompareMode m=kCaseSensitive) const
Check if this contains str
Definition fstring.cpp:905
static bool scanInt64_8(const char8 *text, int64 &value, bool scanToEnd=true)
Converts string of type char8 to int64 value.
Definition fstring.cpp:1389
static bool isCharAscii(char8 character)
Returns true if character is in ASCII range.
Definition fstring.cpp:1685
static bool scanInt64_16(const char16 *text, int64 &value, bool scanToEnd=true)
Converts string of type char16 to int64 value.
Definition fstring.cpp:1403
static bool isCharDigit(char8 character)
Returns true if character is a number.
Definition fstring.cpp:1673
static bool isCharAlpha(char8 character)
Returns true if character is an alphabetic character.
Definition fstring.cpp:1649
bool isNormalized(UnicodeNormalization=kUnicodeNormC)
On PC only kUnicodeNormC is working.
Definition fstring.cpp:1971
bool scanFloat(double &value, uint32 offset=0, bool scanToEnd=true) const
Converts string to double value starting at offset.
Definition fstring.cpp:1501
bool scanInt64(int64 &value, uint32 offset=0, bool scanToEnd=true) const
Converts string to int64 value starting at offset.
Definition fstring.cpp:1334
static bool scanUInt64_8(const char8 *text, uint64 &value, bool scanToEnd=true)
Converts string of type char8 to uint64 value.
Definition fstring.cpp:1415
int32 compareAt(uint32 index, const ConstString &str, int32 n=-1, CompareMode m=kCaseSensitive) const
Compare n characters of str with n characters of this starting at index (return: see above)
Definition fstring.cpp:707
int32 compare(const ConstString &str, int32 n, CompareMode m=kCaseSensitive) const
Compare n characters of str with n characters of this (return: see above)
Definition fstring.cpp:659
static bool scanUInt32_8(const char8 *text, uint32 &value, bool scanToEnd=true)
Converts string of type char8 to int32 value.
Definition fstring.h:604
virtual const char16 * text16() const
Returns pointer to string of type char16.
Definition fstring.h:517
virtual int32 length() const
Return length of string.
Definition fstring.h:128
static char8 toUpper(char8 c)
Converts to upper case.
Definition fstring.cpp:1604
static bool scanInt32_16(const char16 *text, int32 &value, bool scanToEnd=true)
Converts string of type char16 to int32 value.
Definition fstring.h:580
FUnknownPtr - automatic interface conversion and smart pointer in one.
Definition funknown.h:417
A Value of variable type.
Definition fvariant.h:34
Object Data Archive Interface.
Definition ipersistent.h:80
virtual tresult PLUGIN_API set(IAttrID attrID, const FVariant &data)=0
Store any data in the archive.
virtual tresult PLUGIN_API get(IAttrID attrID, FVariant &data)=0
Get data previously stored to the archive.
Interface to return an ascii string of variable size.
Interface to a string of variable size and encoding.
virtual bool PLUGIN_API isWideString() const =0
Returns true if the string is in unicode format, returns false if the string is ASCII.
virtual const char8 *PLUGIN_API getText8()=0
Return ASCII string.
virtual const char16 *PLUGIN_API getText16()=0
Return unicode string.
const char8 *PLUGIN_API getText8() SMTG_OVERRIDE
Return ASCII string.
Definition fstring.cpp:3937
void PLUGIN_API take(void *s, bool _isWide) SMTG_OVERRIDE
!Do not use this method! Early implementations take the given pointer as internal string and this wil...
Definition fstring.cpp:3949
void PLUGIN_API setText8(const char8 *text) SMTG_OVERRIDE
Assign ASCII string.
Definition fstring.cpp:3925
const char16 *PLUGIN_API getText16() SMTG_OVERRIDE
Return unicode string.
Definition fstring.cpp:3943
void PLUGIN_API setText16(const char16 *text) SMTG_OVERRIDE
Assign unicode string.
Definition fstring.cpp:3931
bool PLUGIN_API isWideString() const SMTG_OVERRIDE
Returns true if the string is in unicode format, returns false if the string is ASCII.
Definition fstring.cpp:3955
String & printf(const char8 *format,...)
Print formatted data into string.
Definition fstring.cpp:3296
String & assign(const ConstString &str, int32 n=-1)
Assign n characters of str (-1: all)
Definition fstring.cpp:2486
bool replaceChars8(const char8 *toReplace, char8 toReplaceBy)
Returns true when any replacement was done.
Definition fstring.cpp:2974
const char8 * text8() const SMTG_OVERRIDE
Returns pointer to string of type char8.
Definition fstring.h:640
void swapContent(String &s)
Swaps ownership of the strings pointed to.
Definition fstring.cpp:3606
void toUpper()
Upper case the string.
Definition fstring.cpp:3513
bool normalize(UnicodeNormalization=kUnicodeNormC)
On PC only kUnicodeNormC is working.
Definition fstring.cpp:2253
bool fromAttributes(IAttributes *a, IAttrID attrID)
Assigns string from FAttributes.
Definition fstring.cpp:3586
bool fromVariant(const FVariant &var)
Assigns string from FVariant.
Definition fstring.cpp:3546
bool removeChars8(const char8 *which)
Remove all occurrences of each char in 'which'.
Definition fstring.cpp:3248
String & insertAt(uint32 idx, const ConstString &str, int32 n=-1)
Insert n characters of str at position idx (n=-1: all)
Definition fstring.cpp:2729
bool removeChars16(const char16 *which)
Remove all occurrences of each char in 'which'.
Definition fstring.cpp:3272
const String & fromPascalString(const unsigned char *buf)
Pascal string conversion.
Definition fstring.cpp:3705
bool toWideString(uint32 sourceCodePage=kCP_Default)
Converts to wide string according to sourceCodePage.
Definition fstring.cpp:2121
void take(String &str)
Take ownership of the string of 'str'.
Definition fstring.cpp:3620
unsigned char * toPascalString(unsigned char *buf)
Pascal string conversion.
Definition fstring.cpp:3677
String & append(const ConstString &str, int32 n=-1)
Append n characters of str to this (n=-1: all)
Definition fstring.cpp:2582
String & operator=(const char8 *str)
Assign from a string of type char8.
Definition fstring.h:346
const char16 * text16() const SMTG_OVERRIDE
Returns pointer to string of type char16.
Definition fstring.h:649
bool incrementTrailingNumber(uint32 width=2, tchar separator=STR(' '), uint32 minNumber=1, bool applyOnlyFormat=false)
Increment the trailing number if present else start with minNumber, width specifies the string width ...
Definition fstring.cpp:3395
String & remove(uint32 index=0, int32 n=-1)
Remove n characters from string starting at index (n=-1: until end)
Definition fstring.cpp:3035
void fromUTF8(const char8 *utf8String)
Assigns from UTF8 string.
Definition fstring.cpp:2246
void passToVariant(FVariant &var)
Pass ownership of buffer to Variant - sets Variant ownership.
Definition fstring.cpp:3649
bool trim(CharGroup mode=kSpace)
Trim lead/trail.
Definition fstring.cpp:3111
String & printFloat(double value, uint32 maxPrecision=6)
print a float into a string, trailing zeros will be trimmed
Definition fstring.cpp:3354
void updateLength()
Call this when the string is truncated outside (not recommended though)
Definition fstring.cpp:2112
void toLower()
Lower case the string.
Definition fstring.cpp:3468
String & replace(uint32 idx, int32 n1, const ConstString &str, int32 n2=-1)
Replace n1 characters of this (starting at idx) with n2 characters of str (n1,n2=-1: until end)
Definition fstring.cpp:2807
void removeChars(CharGroup mode=kSpace)
Removes all of group.
Definition fstring.cpp:3174
snprintf
Debugging tools.
#define SMTG_ASSERT(f)
if DEVELOPMENT is not set, these macros will do nothing.
Definition fdebug.h:200
free
sscanf
#define STR16(x)
string methods defines unicode / ASCII
Definition fstrdefs.h:31
@ kCP_MAC_ROMAN
Default Mac codepage.
Definition fstring.h:68
@ kCP_ShiftJIS
Shifted Japan Industrial Standard Encoding.
Definition fstring.h:73
@ kCP_US_ASCII
US-ASCII (7-bit).
Definition fstring.h:74
@ kCP_Default
Default ANSI codepage.
Definition fstring.h:76
@ kCP_MAC_CEE
Mac Central European Encoding.
Definition fstring.h:71
@ kCP_ANSI_WEL
West European Latin Encoding.
Definition fstring.h:70
@ kCP_ANSI
Default ANSI codepage.
Definition fstring.h:67
@ kCP_Utf8
UTF8 Encoding.
Definition fstring.h:72
UnicodeNormalization
Definition fstring.h:80
@ kUnicodeNormKD
Unicode normalization form KD, compatibility decomposition.
Definition fstring.h:84
@ kUnicodeNormKC
Unicode normalization form KC, compatibility composition.
Definition fstring.h:83
@ kUnicodeNormD
Unicode normalization Form D, canonical decomposition.
Definition fstring.h:82
@ kUnicodeNormC
Unicode normalization Form C, canonical composition.
Definition fstring.h:81
isalnum
isalpha
isdigit
isspace
iswalnum
iswalpha
iswdigit
iswspace
T log10(T... args)
malloc
T max(T... args)
memcpy
memmove
memset
T min(T... args)
T next(T... args)
realloc
T round(T... args)
T size(T... args)
typedef int64_t
strcmp
strlen
strncmp
tolower
toupper
towlower
towupper
va_start
vsnprintf