DISTRHO Plugin Framework
String.hpp
1 /*
2  * DISTRHO Plugin Framework (DPF)
3  * Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
4  *
5  * Permission to use, copy, modify, and/or distribute this software for any purpose with
6  * or without fee is hereby granted, provided that the above copyright notice and this
7  * permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
10  * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
11  * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
12  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
13  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16 
17 #ifndef DISTRHO_STRING_HPP_INCLUDED
18 #define DISTRHO_STRING_HPP_INCLUDED
19 
20 #include "../DistrhoUtils.hpp"
21 #include "../extra/ScopedSafeLocale.hpp"
22 
23 #include <algorithm>
24 
26 
27 // -----------------------------------------------------------------------
28 // String class
29 
30 class String
31 {
32 public:
33  // -------------------------------------------------------------------
34  // constructors (no explicit conversions allowed)
35 
36  /*
37  * Empty string.
38  */
39  explicit String() noexcept
40  : fBuffer(_null()),
41  fBufferLen(0),
42  fBufferAlloc(false) {}
43 
44  /*
45  * Simple character.
46  */
47  explicit String(const char c) noexcept
48  : fBuffer(_null()),
49  fBufferLen(0),
50  fBufferAlloc(false)
51  {
52  char ch[2];
53  ch[0] = c;
54  ch[1] = '\0';
55 
56  _dup(ch);
57  }
58 
59  /*
60  * Simple char string.
61  */
62  explicit String(char* const strBuf, const bool reallocData = true) noexcept
63  : fBuffer(_null()),
64  fBufferLen(0),
65  fBufferAlloc(false)
66  {
67  if (reallocData || strBuf == nullptr)
68  {
69  _dup(strBuf);
70  }
71  else
72  {
73  fBuffer = strBuf;
74  fBufferLen = std::strlen(strBuf);
75  fBufferAlloc = true;
76  }
77  }
78 
79  /*
80  * Simple const char string.
81  */
82  explicit String(const char* const strBuf) noexcept
83  : fBuffer(_null()),
84  fBufferLen(0),
85  fBufferAlloc(false)
86  {
87  _dup(strBuf);
88  }
89 
90  /*
91  * Integer.
92  */
93  explicit String(const int value) noexcept
94  : fBuffer(_null()),
95  fBufferLen(0),
96  fBufferAlloc(false)
97  {
98  char strBuf[0xff+1];
99  std::snprintf(strBuf, 0xff, "%d", value);
100  strBuf[0xff] = '\0';
101 
102  _dup(strBuf);
103  }
104 
105  /*
106  * Unsigned integer, possibly in hexadecimal.
107  */
108  explicit String(const unsigned int value, const bool hexadecimal = false) noexcept
109  : fBuffer(_null()),
110  fBufferLen(0),
111  fBufferAlloc(false)
112  {
113  char strBuf[0xff+1];
114  std::snprintf(strBuf, 0xff, hexadecimal ? "0x%x" : "%u", value);
115  strBuf[0xff] = '\0';
116 
117  _dup(strBuf);
118  }
119 
120  /*
121  * Long integer.
122  */
123  explicit String(const long value) noexcept
124  : fBuffer(_null()),
125  fBufferLen(0),
126  fBufferAlloc(false)
127  {
128  char strBuf[0xff+1];
129  std::snprintf(strBuf, 0xff, "%ld", value);
130  strBuf[0xff] = '\0';
131 
132  _dup(strBuf);
133  }
134 
135  /*
136  * Long unsigned integer, possibly hexadecimal.
137  */
138  explicit String(const unsigned long value, const bool hexadecimal = false) noexcept
139  : fBuffer(_null()),
140  fBufferLen(0),
141  fBufferAlloc(false)
142  {
143  char strBuf[0xff+1];
144  std::snprintf(strBuf, 0xff, hexadecimal ? "0x%lx" : "%lu", value);
145  strBuf[0xff] = '\0';
146 
147  _dup(strBuf);
148  }
149 
150  /*
151  * Long long integer.
152  */
153  explicit String(const long long value) noexcept
154  : fBuffer(_null()),
155  fBufferLen(0),
156  fBufferAlloc(false)
157  {
158  char strBuf[0xff+1];
159  std::snprintf(strBuf, 0xff, "%lld", value);
160  strBuf[0xff] = '\0';
161 
162  _dup(strBuf);
163  }
164 
165  /*
166  * Long long unsigned integer, possibly hexadecimal.
167  */
168  explicit String(const unsigned long long value, const bool hexadecimal = false) noexcept
169  : fBuffer(_null()),
170  fBufferLen(0),
171  fBufferAlloc(false)
172  {
173  char strBuf[0xff+1];
174  std::snprintf(strBuf, 0xff, hexadecimal ? "0x%llx" : "%llu", value);
175  strBuf[0xff] = '\0';
176 
177  _dup(strBuf);
178  }
179 
180  /*
181  * Single-precision floating point number.
182  */
183  explicit String(const float value) noexcept
184  : fBuffer(_null()),
185  fBufferLen(0),
186  fBufferAlloc(false)
187  {
188  char strBuf[0xff+1];
189 
190  {
191  const ScopedSafeLocale ssl;
192  std::snprintf(strBuf, 0xff, "%.12g", static_cast<double>(value));
193  }
194 
195  strBuf[0xff] = '\0';
196 
197  _dup(strBuf);
198  }
199 
200  /*
201  * Double-precision floating point number.
202  */
203  explicit String(const double value) noexcept
204  : fBuffer(_null()),
205  fBufferLen(0),
206  fBufferAlloc(false)
207  {
208  char strBuf[0xff+1];
209 
210  {
211  const ScopedSafeLocale ssl;
212  std::snprintf(strBuf, 0xff, "%.24g", value);
213  }
214 
215  strBuf[0xff] = '\0';
216 
217  _dup(strBuf);
218  }
219 
220  // -------------------------------------------------------------------
221  // non-explicit constructor
222 
223  /*
224  * Create string from another string.
225  */
226  String(const String& str) noexcept
227  : fBuffer(_null()),
228  fBufferLen(0),
229  fBufferAlloc(false)
230  {
231  _dup(str.fBuffer);
232  }
233 
234  // -------------------------------------------------------------------
235  // destructor
236 
237  /*
238  * Destructor.
239  */
240  ~String() noexcept
241  {
242  DISTRHO_SAFE_ASSERT_RETURN(fBuffer != nullptr,);
243 
244  if (fBufferAlloc)
245  std::free(fBuffer);
246 
247  fBuffer = nullptr;
248  fBufferLen = 0;
249  fBufferAlloc = false;
250  }
251 
252  // -------------------------------------------------------------------
253  // public methods
254 
255  /*
256  * Get length of the string.
257  */
258  std::size_t length() const noexcept
259  {
260  return fBufferLen;
261  }
262 
263  /*
264  * Check if the string is empty.
265  */
266  bool isEmpty() const noexcept
267  {
268  return (fBufferLen == 0);
269  }
270 
271  /*
272  * Check if the string is not empty.
273  */
274  bool isNotEmpty() const noexcept
275  {
276  return (fBufferLen != 0);
277  }
278 
279  /*
280  * Check if the string contains a specific character, case-sensitive.
281  */
282  bool contains(const char c) const noexcept
283  {
284  for (std::size_t i=0; i<fBufferLen; ++i)
285  {
286  if (fBuffer[i] == c)
287  return true;
288  }
289 
290  return false;
291  }
292 
293  /*
294  * Check if the string contains another string, optionally ignoring case.
295  */
296  bool contains(const char* const strBuf, const bool ignoreCase = false) const noexcept
297  {
298  DISTRHO_SAFE_ASSERT_RETURN(strBuf != nullptr, false);
299 
300  if (ignoreCase)
301  {
302 #ifdef __USE_GNU
303  return (strcasestr(fBuffer, strBuf) != nullptr);
304 #else
305  String tmp1(fBuffer), tmp2(strBuf);
306 
307  // memory allocation failed or empty string(s)
308  if (tmp1.fBuffer == _null() || tmp2.fBuffer == _null())
309  return false;
310 
311  tmp1.toLower();
312  tmp2.toLower();
313  return (std::strstr(tmp1, tmp2) != nullptr);
314 #endif
315  }
316 
317  return (std::strstr(fBuffer, strBuf) != nullptr);
318  }
319 
320  /*
321  * Check if character at 'pos' is a digit.
322  */
323  bool isDigit(const std::size_t pos) const noexcept
324  {
325  DISTRHO_SAFE_ASSERT_RETURN(pos < fBufferLen, false);
326 
327  return (fBuffer[pos] >= '0' && fBuffer[pos] <= '9');
328  }
329 
330  /*
331  * Check if the string starts with the character 'c'.
332  */
333  bool startsWith(const char c) const noexcept
334  {
335  DISTRHO_SAFE_ASSERT_RETURN(c != '\0', false);
336 
337  return (fBufferLen > 0 && fBuffer[0] == c);
338  }
339 
340  /*
341  * Check if the string starts with the string 'prefix'.
342  */
343  bool startsWith(const char* const prefix) const noexcept
344  {
345  DISTRHO_SAFE_ASSERT_RETURN(prefix != nullptr, false);
346 
347  const std::size_t prefixLen(std::strlen(prefix));
348 
349  if (fBufferLen < prefixLen)
350  return false;
351 
352  return (std::strncmp(fBuffer, prefix, prefixLen) == 0);
353  }
354 
355  /*
356  * Check if the string ends with the character 'c'.
357  */
358  bool endsWith(const char c) const noexcept
359  {
360  DISTRHO_SAFE_ASSERT_RETURN(c != '\0', false);
361 
362  return (fBufferLen > 0 && fBuffer[fBufferLen-1] == c);
363  }
364 
365  /*
366  * Check if the string ends with the string 'suffix'.
367  */
368  bool endsWith(const char* const suffix) const noexcept
369  {
370  DISTRHO_SAFE_ASSERT_RETURN(suffix != nullptr, false);
371 
372  const std::size_t suffixLen(std::strlen(suffix));
373 
374  if (fBufferLen < suffixLen)
375  return false;
376 
377  return (std::strncmp(fBuffer + (fBufferLen-suffixLen), suffix, suffixLen) == 0);
378  }
379 
380  /*
381  * Find the first occurrence of character 'c' in the string.
382  * Returns "length()" if the character is not found.
383  */
384  std::size_t find(const char c, bool* const found = nullptr) const noexcept
385  {
386  if (fBufferLen == 0 || c == '\0')
387  {
388  if (found != nullptr)
389  *found = false;
390  return fBufferLen;
391  }
392 
393  for (std::size_t i=0; i < fBufferLen; ++i)
394  {
395  if (fBuffer[i] == c)
396  {
397  if (found != nullptr)
398  *found = true;
399  return i;
400  }
401  }
402 
403  if (found != nullptr)
404  *found = false;
405  return fBufferLen;
406  }
407 
408  /*
409  * Find the first occurrence of string 'strBuf' in the string.
410  * Returns "length()" if the string is not found.
411  */
412  std::size_t find(const char* const strBuf, bool* const found = nullptr) const noexcept
413  {
414  if (fBufferLen == 0 || strBuf == nullptr || strBuf[0] == '\0')
415  {
416  if (found != nullptr)
417  *found = false;
418  return fBufferLen;
419  }
420 
421  if (char* const subStrBuf = std::strstr(fBuffer, strBuf))
422  {
423  const ssize_t ret(subStrBuf - fBuffer);
424 
425  if (ret < 0)
426  {
427  // should never happen!
428  d_safe_assert_int("ret >= 0", __FILE__, __LINE__, int(ret));
429 
430  if (found != nullptr)
431  *found = false;
432  return fBufferLen;
433  }
434 
435  if (found != nullptr)
436  *found = true;
437  return static_cast<std::size_t>(ret);
438  }
439 
440  if (found != nullptr)
441  *found = false;
442  return fBufferLen;
443  }
444 
445  /*
446  * Find the last occurrence of character 'c' in the string.
447  * Returns "length()" if the character is not found.
448  */
449  std::size_t rfind(const char c, bool* const found = nullptr) const noexcept
450  {
451  if (fBufferLen == 0 || c == '\0')
452  {
453  if (found != nullptr)
454  *found = false;
455  return fBufferLen;
456  }
457 
458  for (std::size_t i=fBufferLen; i > 0; --i)
459  {
460  if (fBuffer[i-1] == c)
461  {
462  if (found != nullptr)
463  *found = true;
464  return i-1;
465  }
466  }
467 
468  if (found != nullptr)
469  *found = false;
470  return fBufferLen;
471  }
472 
473  /*
474  * Find the last occurrence of string 'strBuf' in the string.
475  * Returns "length()" if the string is not found.
476  */
477  std::size_t rfind(const char* const strBuf, bool* const found = nullptr) const noexcept
478  {
479  if (found != nullptr)
480  *found = false;
481 
482  if (fBufferLen == 0 || strBuf == nullptr || strBuf[0] == '\0')
483  return fBufferLen;
484 
485  const std::size_t strBufLen(std::strlen(strBuf));
486 
487  std::size_t ret = fBufferLen;
488  const char* tmpBuf = fBuffer;
489 
490  for (std::size_t i=0; i < fBufferLen; ++i)
491  {
492  if (std::strstr(tmpBuf+1, strBuf) == nullptr && std::strncmp(tmpBuf, strBuf, strBufLen) == 0)
493  {
494  if (found != nullptr)
495  *found = true;
496  break;
497  }
498 
499  --ret;
500  ++tmpBuf;
501  }
502 
503  return fBufferLen-ret;
504  }
505 
506  /*
507  * Clear the string.
508  */
509  void clear() noexcept
510  {
511  truncate(0);
512  }
513 
514  /*
515  * Replace all occurrences of character 'before' with character 'after'.
516  */
517  String& replace(const char before, const char after) noexcept
518  {
519  DISTRHO_SAFE_ASSERT_RETURN(before != '\0' /* && after != '\0' */, *this);
520 
521  for (std::size_t i=0; i < fBufferLen; ++i)
522  {
523  if (fBuffer[i] == before)
524  fBuffer[i] = after;
525  }
526 
527  return *this;
528  }
529 
530  /*
531  * Remove all occurrences of character 'c', shifting and truncating the string as necessary.
532  */
533  String& remove(const char c) noexcept
534  {
535  DISTRHO_SAFE_ASSERT_RETURN(c != '\0', *this);
536 
537  if (fBufferLen == 0)
538  return *this;
539 
540  for (std::size_t i=0; i < fBufferLen; ++i)
541  {
542  if (fBuffer[i] == c)
543  {
544  --fBufferLen;
545  std::memmove(fBuffer+i, fBuffer+i+1, fBufferLen-i);
546  }
547  }
548 
549  fBuffer[fBufferLen] = '\0';
550  return *this;
551  }
552 
553  /*
554  * Truncate the string to size 'n'.
555  */
556  String& truncate(const std::size_t n) noexcept
557  {
558  if (n >= fBufferLen)
559  return *this;
560 
561  fBuffer[n] = '\0';
562  fBufferLen = n;
563 
564  return *this;
565  }
566 
567  /*
568  * Convert all non-basic characters to '_'.
569  */
570  String& toBasic() noexcept
571  {
572  for (std::size_t i=0; i < fBufferLen; ++i)
573  {
574  if (fBuffer[i] >= '0' && fBuffer[i] <= '9')
575  continue;
576  if (fBuffer[i] >= 'A' && fBuffer[i] <= 'Z')
577  continue;
578  if (fBuffer[i] >= 'a' && fBuffer[i] <= 'z')
579  continue;
580  if (fBuffer[i] == '_')
581  continue;
582 
583  fBuffer[i] = '_';
584  }
585 
586  return *this;
587  }
588 
589  /*
590  * Convert all ascii characters to lowercase.
591  */
592  String& toLower() noexcept
593  {
594  static const char kCharDiff('a' - 'A');
595 
596  for (std::size_t i=0; i < fBufferLen; ++i)
597  {
598  if (fBuffer[i] >= 'A' && fBuffer[i] <= 'Z')
599  fBuffer[i] = static_cast<char>(fBuffer[i] + kCharDiff);
600  }
601 
602  return *this;
603  }
604 
605  /*
606  * Convert all ascii characters to uppercase.
607  */
608  String& toUpper() noexcept
609  {
610  static const char kCharDiff('a' - 'A');
611 
612  for (std::size_t i=0; i < fBufferLen; ++i)
613  {
614  if (fBuffer[i] >= 'a' && fBuffer[i] <= 'z')
615  fBuffer[i] = static_cast<char>(fBuffer[i] - kCharDiff);
616  }
617 
618  return *this;
619  }
620 
621  /*
622  * Create a new string where all non-basic characters are converted to '_'.
623  * @see toBasic()
624  */
625  String asBasic() const noexcept
626  {
627  String s(*this);
628  return s.toBasic();
629  }
630 
631  /*
632  * Create a new string where all ascii characters are converted lowercase.
633  * @see toLower()
634  */
635  String asLower() const noexcept
636  {
637  String s(*this);
638  return s.toLower();
639  }
640 
641  /*
642  * Create a new string where all ascii characters are converted to uppercase.
643  * @see toUpper()
644  */
645  String asUpper() const noexcept
646  {
647  String s(*this);
648  return s.toUpper();
649  }
650 
651  /*
652  * Direct access to the string buffer (read-only).
653  */
654  const char* buffer() const noexcept
655  {
656  return fBuffer;
657  }
658 
659  /*
660  * Get and release the string buffer, while also clearing this string.
661  * This allows to keep a pointer to the buffer after this object is deleted.
662  * Result must be freed.
663  */
664  char* getAndReleaseBuffer() noexcept
665  {
666  char* ret = fBufferLen > 0 ? fBuffer : nullptr;
667  fBuffer = _null();
668  fBufferLen = 0;
669  fBufferAlloc = false;
670  return ret;
671  }
672 
673  // -------------------------------------------------------------------
674  // base64 stuff, based on http://www.adp-gmbh.ch/cpp/common/base64.html
675  // Copyright (C) 2004-2008 RenĂ© Nyffenegger
676 
677  static String asBase64(const void* const data, const std::size_t dataSize)
678  {
679  static const char* const kBase64Chars =
680  "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
681  "abcdefghijklmnopqrstuvwxyz"
682  "0123456789+/";
683 
684 #ifndef _MSC_VER
685  const std::size_t kTmpBufSize = std::min(d_nextPowerOf2(static_cast<uint32_t>(dataSize/3)), 65536U);
686 #else
687  constexpr std::size_t kTmpBufSize = 65536U;
688 #endif
689 
690  const uchar* bytesToEncode((const uchar*)data);
691 
692  uint i=0, j=0;
693  uint charArray3[3], charArray4[4];
694 
695  char strBuf[kTmpBufSize + 1];
696  strBuf[kTmpBufSize] = '\0';
697  std::size_t strBufIndex = 0;
698 
699  String ret;
700 
701  for (std::size_t s=0; s<dataSize; ++s)
702  {
703  charArray3[i++] = *(bytesToEncode++);
704 
705  if (i == 3)
706  {
707  charArray4[0] = (charArray3[0] & 0xfc) >> 2;
708  charArray4[1] = ((charArray3[0] & 0x03) << 4) + ((charArray3[1] & 0xf0) >> 4);
709  charArray4[2] = ((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6);
710  charArray4[3] = charArray3[2] & 0x3f;
711 
712  for (i=0; i<4; ++i)
713  strBuf[strBufIndex++] = kBase64Chars[charArray4[i]];
714 
715  if (strBufIndex >= kTmpBufSize-7)
716  {
717  strBuf[strBufIndex] = '\0';
718  strBufIndex = 0;
719  ret += strBuf;
720  }
721 
722  i = 0;
723  }
724  }
725 
726  if (i != 0)
727  {
728  for (j=i; j<3; ++j)
729  charArray3[j] = '\0';
730 
731  charArray4[0] = (charArray3[0] & 0xfc) >> 2;
732  charArray4[1] = ((charArray3[0] & 0x03) << 4) + ((charArray3[1] & 0xf0) >> 4);
733  charArray4[2] = ((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6);
734  charArray4[3] = charArray3[2] & 0x3f;
735 
736  for (j=0; j<4 && i<3 && j<i+1; ++j)
737  strBuf[strBufIndex++] = kBase64Chars[charArray4[j]];
738 
739  for (; i++ < 3;)
740  strBuf[strBufIndex++] = '=';
741  }
742 
743  if (strBufIndex != 0)
744  {
745  strBuf[strBufIndex] = '\0';
746  ret += strBuf;
747  }
748 
749  return ret;
750  }
751 
752  // -------------------------------------------------------------------
753  // public operators
754 
755  operator const char*() const noexcept
756  {
757  return fBuffer;
758  }
759 
760  char operator[](const std::size_t pos) const noexcept
761  {
762  if (pos < fBufferLen)
763  return fBuffer[pos];
764 
765  d_safe_assert("pos < fBufferLen", __FILE__, __LINE__);
766 
767  static char fallback;
768  fallback = '\0';
769  return fallback;
770  }
771 
772  char& operator[](const std::size_t pos) noexcept
773  {
774  if (pos < fBufferLen)
775  return fBuffer[pos];
776 
777  d_safe_assert("pos < fBufferLen", __FILE__, __LINE__);
778 
779  static char fallback;
780  fallback = '\0';
781  return fallback;
782  }
783 
784  bool operator==(const char* const strBuf) const noexcept
785  {
786  return (strBuf != nullptr && std::strcmp(fBuffer, strBuf) == 0);
787  }
788 
789  bool operator==(const String& str) const noexcept
790  {
791  return operator==(str.fBuffer);
792  }
793 
794  bool operator!=(const char* const strBuf) const noexcept
795  {
796  return !operator==(strBuf);
797  }
798 
799  bool operator!=(const String& str) const noexcept
800  {
801  return !operator==(str.fBuffer);
802  }
803 
804  String& operator=(const char* const strBuf) noexcept
805  {
806  _dup(strBuf);
807 
808  return *this;
809  }
810 
811  String& operator=(const String& str) noexcept
812  {
813  _dup(str.fBuffer);
814 
815  return *this;
816  }
817 
818  String& operator+=(const char* const strBuf) noexcept
819  {
820  if (strBuf == nullptr || strBuf[0] == '\0')
821  return *this;
822 
823  const std::size_t strBufLen = std::strlen(strBuf);
824 
825  // for empty strings, we can just take the appended string as our entire data
826  if (isEmpty())
827  {
828  _dup(strBuf, strBufLen);
829  return *this;
830  }
831 
832  // we have some data ourselves, reallocate to add the new stuff
833  char* const newBuf = (char*)realloc(fBuffer, fBufferLen + strBufLen + 1);
834  DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, *this);
835 
836  std::memcpy(newBuf + fBufferLen, strBuf, strBufLen + 1);
837 
838  fBuffer = newBuf;
839  fBufferLen += strBufLen;
840 
841  return *this;
842  }
843 
844  String& operator+=(const String& str) noexcept
845  {
846  return operator+=(str.fBuffer);
847  }
848 
849  String operator+(const char* const strBuf) noexcept
850  {
851  if (strBuf == nullptr || strBuf[0] == '\0')
852  return *this;
853  if (isEmpty())
854  return String(strBuf);
855 
856  const std::size_t strBufLen = std::strlen(strBuf);
857  const std::size_t newBufSize = fBufferLen + strBufLen;
858  char* const newBuf = (char*)malloc(newBufSize + 1);
859  DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String());
860 
861  std::memcpy(newBuf, fBuffer, fBufferLen);
862  std::memcpy(newBuf + fBufferLen, strBuf, strBufLen + 1);
863 
864  return String(newBuf, false);
865  }
866 
867  String operator+(const String& str) noexcept
868  {
869  return operator+(str.fBuffer);
870  }
871 
872  // needed for std::map compatibility
873  bool operator<(const String& str) const noexcept
874  {
875  return std::strcmp(fBuffer, str.fBuffer) < 0;
876  }
877 
878  // -------------------------------------------------------------------
879 
880 private:
881  char* fBuffer; // the actual string buffer
882  std::size_t fBufferLen; // string length
883  bool fBufferAlloc; // wherever the buffer is allocated, not using _null()
884 
885  /*
886  * Static null string.
887  * Prevents allocation for new and/or empty strings.
888  */
889  static char* _null() noexcept
890  {
891  static char sNull = '\0';
892  return &sNull;
893  }
894 
895  /*
896  * Helper function.
897  * Called whenever the string needs to be allocated.
898  *
899  * Notes:
900  * - Allocates string only if 'strBuf' is not null and new string contents are different
901  * - If 'strBuf' is null, 'size' must be 0
902  */
903  void _dup(const char* const strBuf, const std::size_t size = 0) noexcept
904  {
905  if (strBuf != nullptr)
906  {
907  // don't recreate string if contents match
908  if (std::strcmp(fBuffer, strBuf) == 0)
909  return;
910 
911  if (fBufferAlloc)
912  std::free(fBuffer);
913 
914  fBufferLen = (size > 0) ? size : std::strlen(strBuf);
915  fBuffer = (char*)std::malloc(fBufferLen+1);
916 
917  if (fBuffer == nullptr)
918  {
919  fBuffer = _null();
920  fBufferLen = 0;
921  fBufferAlloc = false;
922  return;
923  }
924 
925  fBufferAlloc = true;
926 
927  std::strcpy(fBuffer, strBuf);
928  fBuffer[fBufferLen] = '\0';
929  }
930  else
931  {
932  DISTRHO_SAFE_ASSERT_UINT(size == 0, static_cast<uint>(size));
933 
934  // don't recreate null string
935  if (! fBufferAlloc)
936  return;
937 
938  DISTRHO_SAFE_ASSERT(fBuffer != nullptr);
939  std::free(fBuffer);
940 
941  fBuffer = _null();
942  fBufferLen = 0;
943  fBufferAlloc = false;
944  }
945  }
946 
947  DISTRHO_PREVENT_HEAP_ALLOCATION
948 };
949 
950 // -----------------------------------------------------------------------
951 
952 static inline
953 String operator+(const String& strBefore, const char* const strBufAfter) noexcept
954 {
955  if (strBufAfter == nullptr || strBufAfter[0] == '\0')
956  return strBefore;
957  if (strBefore.isEmpty())
958  return String(strBufAfter);
959 
960  const std::size_t strBeforeLen = strBefore.length();
961  const std::size_t strBufAfterLen = std::strlen(strBufAfter);
962  const std::size_t newBufSize = strBeforeLen + strBufAfterLen;
963  char* const newBuf = (char*)malloc(newBufSize + 1);
964  DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String());
965 
966  std::memcpy(newBuf, strBefore.buffer(), strBeforeLen);
967  std::memcpy(newBuf + strBeforeLen, strBufAfter, strBufAfterLen + 1);
968 
969  return String(newBuf, false);
970 }
971 
972 static inline
973 String operator+(const char* const strBufBefore, const String& strAfter) noexcept
974 {
975  if (strAfter.isEmpty())
976  return String(strBufBefore);
977  if (strBufBefore == nullptr || strBufBefore[0] == '\0')
978  return strAfter;
979 
980  const std::size_t strBufBeforeLen = std::strlen(strBufBefore);
981  const std::size_t strAfterLen = strAfter.length();
982  const std::size_t newBufSize = strBufBeforeLen + strAfterLen;
983  char* const newBuf = (char*)malloc(newBufSize + 1);
984  DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String());
985 
986  std::memcpy(newBuf, strBufBefore, strBufBeforeLen);
987  std::memcpy(newBuf + strBufBeforeLen, strAfter.buffer(), strAfterLen + 1);
988 
989  return String(newBuf, false);
990 }
991 
992 // -----------------------------------------------------------------------
993 
995 
996 #endif // DISTRHO_STRING_HPP_INCLUDED
Definition: ScopedSafeLocale.hpp:57
Definition: String.hpp:31
static uint32_t d_nextPowerOf2(uint32_t size) noexcept
Definition: DistrhoUtils.hpp:298
#define END_NAMESPACE_DISTRHO
Definition: DistrhoInfo.hpp:834
#define START_NAMESPACE_DISTRHO
Definition: DistrhoInfo.hpp:828
static void d_safe_assert(const char *const assertion, const char *const file, const int line) noexcept
Definition: DistrhoUtils.hpp:177
static void d_safe_assert_int(const char *const assertion, const char *const file, const int line, const int value) noexcept
Definition: DistrhoUtils.hpp:186