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