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