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