Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3991 lines
92KB

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