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 
25 START_NAMESPACE_DISTRHO
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 copyData = true) noexcept
63  : fBuffer(_null()),
64  fBufferLen(0),
65  fBufferAlloc(false)
66  {
67  if (copyData || 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  * Direct access to the string buffer (read-only).
623  */
624  const char* buffer() const noexcept
625  {
626  return fBuffer;
627  }
628 
629  /*
630  * Get and release the string buffer, while also clearing this string.
631  * This allows to keep a pointer to the buffer after this object is deleted.
632  * Result must be freed.
633  */
634  char* getAndReleaseBuffer() noexcept
635  {
636  char* ret = fBufferLen > 0 ? fBuffer : nullptr;
637  fBuffer = _null();
638  fBufferLen = 0;
639  fBufferAlloc = false;
640  return ret;
641  }
642 
643  // -------------------------------------------------------------------
644  // base64 stuff, based on http://www.adp-gmbh.ch/cpp/common/base64.html
645  // Copyright (C) 2004-2008 RenĂ© Nyffenegger
646 
647  static String asBase64(const void* const data, const std::size_t dataSize)
648  {
649  static const char* const kBase64Chars =
650  "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
651  "abcdefghijklmnopqrstuvwxyz"
652  "0123456789+/";
653 
654 #ifndef _MSC_VER
655  const std::size_t kTmpBufSize = std::min(d_nextPowerOf2(static_cast<uint32_t>(dataSize/3)), 65536U);
656 #else
657  constexpr std::size_t kTmpBufSize = 65536U;
658 #endif
659 
660  const uchar* bytesToEncode((const uchar*)data);
661 
662  uint i=0, j=0;
663  uint charArray3[3], charArray4[4];
664 
665  char strBuf[kTmpBufSize + 1];
666  strBuf[kTmpBufSize] = '\0';
667  std::size_t strBufIndex = 0;
668 
669  String ret;
670 
671  for (std::size_t s=0; s<dataSize; ++s)
672  {
673  charArray3[i++] = *(bytesToEncode++);
674 
675  if (i == 3)
676  {
677  charArray4[0] = (charArray3[0] & 0xfc) >> 2;
678  charArray4[1] = ((charArray3[0] & 0x03) << 4) + ((charArray3[1] & 0xf0) >> 4);
679  charArray4[2] = ((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6);
680  charArray4[3] = charArray3[2] & 0x3f;
681 
682  for (i=0; i<4; ++i)
683  strBuf[strBufIndex++] = kBase64Chars[charArray4[i]];
684 
685  if (strBufIndex >= kTmpBufSize-7)
686  {
687  strBuf[strBufIndex] = '\0';
688  strBufIndex = 0;
689  ret += strBuf;
690  }
691 
692  i = 0;
693  }
694  }
695 
696  if (i != 0)
697  {
698  for (j=i; j<3; ++j)
699  charArray3[j] = '\0';
700 
701  charArray4[0] = (charArray3[0] & 0xfc) >> 2;
702  charArray4[1] = ((charArray3[0] & 0x03) << 4) + ((charArray3[1] & 0xf0) >> 4);
703  charArray4[2] = ((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6);
704  charArray4[3] = charArray3[2] & 0x3f;
705 
706  for (j=0; j<4 && i<3 && j<i+1; ++j)
707  strBuf[strBufIndex++] = kBase64Chars[charArray4[j]];
708 
709  for (; i++ < 3;)
710  strBuf[strBufIndex++] = '=';
711  }
712 
713  if (strBufIndex != 0)
714  {
715  strBuf[strBufIndex] = '\0';
716  ret += strBuf;
717  }
718 
719  return ret;
720  }
721 
722  // -------------------------------------------------------------------
723  // public operators
724 
725  operator const char*() const noexcept
726  {
727  return fBuffer;
728  }
729 
730  char operator[](const std::size_t pos) const noexcept
731  {
732  if (pos < fBufferLen)
733  return fBuffer[pos];
734 
735  d_safe_assert("pos < fBufferLen", __FILE__, __LINE__);
736 
737  static char fallback;
738  fallback = '\0';
739  return fallback;
740  }
741 
742  char& operator[](const std::size_t pos) noexcept
743  {
744  if (pos < fBufferLen)
745  return fBuffer[pos];
746 
747  d_safe_assert("pos < fBufferLen", __FILE__, __LINE__);
748 
749  static char fallback;
750  fallback = '\0';
751  return fallback;
752  }
753 
754  bool operator==(const char* const strBuf) const noexcept
755  {
756  return (strBuf != nullptr && std::strcmp(fBuffer, strBuf) == 0);
757  }
758 
759  bool operator==(const String& str) const noexcept
760  {
761  return operator==(str.fBuffer);
762  }
763 
764  bool operator!=(const char* const strBuf) const noexcept
765  {
766  return !operator==(strBuf);
767  }
768 
769  bool operator!=(const String& str) const noexcept
770  {
771  return !operator==(str.fBuffer);
772  }
773 
774  String& operator=(const char* const strBuf) noexcept
775  {
776  _dup(strBuf);
777 
778  return *this;
779  }
780 
781  String& operator=(const String& str) noexcept
782  {
783  _dup(str.fBuffer);
784 
785  return *this;
786  }
787 
788  String& operator+=(const char* const strBuf) noexcept
789  {
790  if (strBuf == nullptr || strBuf[0] == '\0')
791  return *this;
792 
793  const std::size_t strBufLen = std::strlen(strBuf);
794 
795  // for empty strings, we can just take the appended string as our entire data
796  if (isEmpty())
797  {
798  _dup(strBuf, strBufLen);
799  return *this;
800  }
801 
802  // we have some data ourselves, reallocate to add the new stuff
803  char* const newBuf = (char*)realloc(fBuffer, fBufferLen + strBufLen + 1);
804  DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, *this);
805 
806  std::memcpy(newBuf + fBufferLen, strBuf, strBufLen + 1);
807 
808  fBuffer = newBuf;
809  fBufferLen += strBufLen;
810 
811  return *this;
812  }
813 
814  String& operator+=(const String& str) noexcept
815  {
816  return operator+=(str.fBuffer);
817  }
818 
819  String operator+(const char* const strBuf) noexcept
820  {
821  if (strBuf == nullptr || strBuf[0] == '\0')
822  return *this;
823  if (isEmpty())
824  return String(strBuf);
825 
826  const std::size_t strBufLen = std::strlen(strBuf);
827  const std::size_t newBufSize = fBufferLen + strBufLen;
828  char* const newBuf = (char*)malloc(newBufSize + 1);
829  DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String());
830 
831  std::memcpy(newBuf, fBuffer, fBufferLen);
832  std::memcpy(newBuf + fBufferLen, strBuf, strBufLen + 1);
833 
834  return String(newBuf);
835  }
836 
837  String operator+(const String& str) noexcept
838  {
839  return operator+(str.fBuffer);
840  }
841 
842  // -------------------------------------------------------------------
843 
844 private:
845  char* fBuffer; // the actual string buffer
846  std::size_t fBufferLen; // string length
847  bool fBufferAlloc; // wherever the buffer is allocated, not using _null()
848 
849  /*
850  * Static null string.
851  * Prevents allocation for new and/or empty strings.
852  */
853  static char* _null() noexcept
854  {
855  static char sNull = '\0';
856  return &sNull;
857  }
858 
859  /*
860  * Helper function.
861  * Called whenever the string needs to be allocated.
862  *
863  * Notes:
864  * - Allocates string only if 'strBuf' is not null and new string contents are different
865  * - If 'strBuf' is null, 'size' must be 0
866  */
867  void _dup(const char* const strBuf, const std::size_t size = 0) noexcept
868  {
869  if (strBuf != nullptr)
870  {
871  // don't recreate string if contents match
872  if (std::strcmp(fBuffer, strBuf) == 0)
873  return;
874 
875  if (fBufferAlloc)
876  std::free(fBuffer);
877 
878  fBufferLen = (size > 0) ? size : std::strlen(strBuf);
879  fBuffer = (char*)std::malloc(fBufferLen+1);
880 
881  if (fBuffer == nullptr)
882  {
883  fBuffer = _null();
884  fBufferLen = 0;
885  fBufferAlloc = false;
886  return;
887  }
888 
889  fBufferAlloc = true;
890 
891  std::strcpy(fBuffer, strBuf);
892  fBuffer[fBufferLen] = '\0';
893  }
894  else
895  {
896  DISTRHO_SAFE_ASSERT_UINT(size == 0, static_cast<uint>(size));
897 
898  // don't recreate null string
899  if (! fBufferAlloc)
900  return;
901 
902  DISTRHO_SAFE_ASSERT(fBuffer != nullptr);
903  std::free(fBuffer);
904 
905  fBuffer = _null();
906  fBufferLen = 0;
907  fBufferAlloc = false;
908  }
909  }
910 
911  DISTRHO_PREVENT_HEAP_ALLOCATION
912 };
913 
914 // -----------------------------------------------------------------------
915 
916 static inline
917 String operator+(const String& strBefore, const char* const strBufAfter) noexcept
918 {
919  if (strBufAfter == nullptr || strBufAfter[0] == '\0')
920  return strBefore;
921  if (strBefore.isEmpty())
922  return String(strBufAfter);
923 
924  const std::size_t strBeforeLen = strBefore.length();
925  const std::size_t strBufAfterLen = std::strlen(strBufAfter);
926  const std::size_t newBufSize = strBeforeLen + strBufAfterLen;
927  char* const newBuf = (char*)malloc(newBufSize + 1);
928  DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String());
929 
930  std::memcpy(newBuf, strBefore.buffer(), strBeforeLen);
931  std::memcpy(newBuf + strBeforeLen, strBufAfter, strBufAfterLen + 1);
932 
933  return String(newBuf);
934 }
935 
936 static inline
937 String operator+(const char* const strBufBefore, const String& strAfter) noexcept
938 {
939  if (strAfter.isEmpty())
940  return String(strBufBefore);
941  if (strBufBefore == nullptr || strBufBefore[0] == '\0')
942  return strAfter;
943 
944  const std::size_t strBufBeforeLen = std::strlen(strBufBefore);
945  const std::size_t strAfterLen = strAfter.length();
946  const std::size_t newBufSize = strBufBeforeLen + strAfterLen;
947  char* const newBuf = (char*)malloc(newBufSize + 1);
948  DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String());
949 
950  std::memcpy(newBuf, strBufBefore, strBufBeforeLen);
951  std::memcpy(newBuf + strBufBeforeLen, strAfter.buffer(), strAfterLen + 1);
952 
953  return String(newBuf);
954 }
955 
956 // -----------------------------------------------------------------------
957 
958 END_NAMESPACE_DISTRHO
959 
960 #endif // DISTRHO_STRING_HPP_INCLUDED
String
Definition: String.hpp:30
ScopedSafeLocale
Definition: ScopedSafeLocale.hpp:57