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, "%.12g", static_cast<double>(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, "%.24g", 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  * Get and release the string buffer, while also clearing this string.
573  * Result must be freed.
574  */
575  char* getAndReleaseBuffer() noexcept
576  {
577  char* const ret = fBuffer;
578  fBuffer = _null();
579  fBufferLen = 0;
580  return ret;
581  }
582 
583  // -------------------------------------------------------------------
584  // base64 stuff, based on http://www.adp-gmbh.ch/cpp/common/base64.html
585  // Copyright (C) 2004-2008 RenĂ© Nyffenegger
586 
587  static String asBase64(const void* const data, const std::size_t dataSize)
588  {
589  static const char* const kBase64Chars =
590  "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
591  "abcdefghijklmnopqrstuvwxyz"
592  "0123456789+/";
593 
594  const std::size_t kTmpBufSize = std::min(d_nextPowerOf2(dataSize/3), 65536U);
595 
596  const uchar* bytesToEncode((const uchar*)data);
597 
598  uint i=0, j=0;
599  uint charArray3[3], charArray4[4];
600 
601  char strBuf[kTmpBufSize+1];
602  strBuf[kTmpBufSize] = '\0';
603  std::size_t strBufIndex = 0;
604 
605  String ret;
606 
607  for (std::size_t s=0; s<dataSize; ++s)
608  {
609  charArray3[i++] = *(bytesToEncode++);
610 
611  if (i == 3)
612  {
613  charArray4[0] = (charArray3[0] & 0xfc) >> 2;
614  charArray4[1] = ((charArray3[0] & 0x03) << 4) + ((charArray3[1] & 0xf0) >> 4);
615  charArray4[2] = ((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6);
616  charArray4[3] = charArray3[2] & 0x3f;
617 
618  for (i=0; i<4; ++i)
619  strBuf[strBufIndex++] = kBase64Chars[charArray4[i]];
620 
621  if (strBufIndex >= kTmpBufSize-7)
622  {
623  strBuf[strBufIndex] = '\0';
624  strBufIndex = 0;
625  ret += strBuf;
626  }
627 
628  i = 0;
629  }
630  }
631 
632  if (i != 0)
633  {
634  for (j=i; j<3; ++j)
635  charArray3[j] = '\0';
636 
637  charArray4[0] = (charArray3[0] & 0xfc) >> 2;
638  charArray4[1] = ((charArray3[0] & 0x03) << 4) + ((charArray3[1] & 0xf0) >> 4);
639  charArray4[2] = ((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6);
640  charArray4[3] = charArray3[2] & 0x3f;
641 
642  for (j=0; j<4 && i<3 && j<i+1; ++j)
643  strBuf[strBufIndex++] = kBase64Chars[charArray4[j]];
644 
645  for (; i++ < 3;)
646  strBuf[strBufIndex++] = '=';
647  }
648 
649  if (strBufIndex != 0)
650  {
651  strBuf[strBufIndex] = '\0';
652  ret += strBuf;
653  }
654 
655  return ret;
656  }
657 
658  // -------------------------------------------------------------------
659  // public operators
660 
661  operator const char*() const noexcept
662  {
663  return fBuffer;
664  }
665 
666  char operator[](const std::size_t pos) const 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  char& operator[](const std::size_t pos) noexcept
679  {
680  if (pos < fBufferLen)
681  return fBuffer[pos];
682 
683  d_safe_assert("pos < fBufferLen", __FILE__, __LINE__);
684 
685  static char fallback;
686  fallback = '\0';
687  return fallback;
688  }
689 
690  bool operator==(const char* const strBuf) const noexcept
691  {
692  return (strBuf != nullptr && std::strcmp(fBuffer, strBuf) == 0);
693  }
694 
695  bool operator==(const String& str) const noexcept
696  {
697  return operator==(str.fBuffer);
698  }
699 
700  bool operator!=(const char* const strBuf) const noexcept
701  {
702  return !operator==(strBuf);
703  }
704 
705  bool operator!=(const String& str) const noexcept
706  {
707  return !operator==(str.fBuffer);
708  }
709 
710  String& operator=(const char* const strBuf) noexcept
711  {
712  _dup(strBuf);
713 
714  return *this;
715  }
716 
717  String& operator=(const String& str) noexcept
718  {
719  _dup(str.fBuffer);
720 
721  return *this;
722  }
723 
724  String& operator+=(const char* const strBuf) noexcept
725  {
726  if (strBuf == nullptr)
727  return *this;
728 
729  const std::size_t newBufSize = fBufferLen + std::strlen(strBuf) + 1;
730  char newBuf[newBufSize];
731 
732  std::strcpy(newBuf, fBuffer);
733  std::strcat(newBuf, strBuf);
734 
735  _dup(newBuf, newBufSize-1);
736 
737  return *this;
738  }
739 
740  String& operator+=(const String& str) noexcept
741  {
742  return operator+=(str.fBuffer);
743  }
744 
745  String operator+(const char* const strBuf) noexcept
746  {
747  const std::size_t newBufSize = fBufferLen + ((strBuf != nullptr) ? std::strlen(strBuf) : 0) + 1;
748  char newBuf[newBufSize];
749 
750  std::strcpy(newBuf, fBuffer);
751 
752  if (strBuf != nullptr)
753  std::strcat(newBuf, strBuf);
754 
755  return String(newBuf);
756  }
757 
758  String operator+(const String& str) noexcept
759  {
760  return operator+(str.fBuffer);
761  }
762 
763  // -------------------------------------------------------------------
764 
765 private:
766  char* fBuffer; // the actual string buffer
767  std::size_t fBufferLen; // string length
768 
769  /*
770  * Static null string.
771  * Prevents allocation for new and/or empty strings.
772  */
773  static char* _null() noexcept
774  {
775  static char sNull = '\0';
776  return &sNull;
777  }
778 
779  /*
780  * Helper function.
781  * Called whenever the string needs to be allocated.
782  *
783  * Notes:
784  * - Allocates string only if 'strBuf' is not null and new string contents are different
785  * - If 'strBuf' is null, 'size' must be 0
786  */
787  void _dup(const char* const strBuf, const std::size_t size = 0) noexcept
788  {
789  if (strBuf != nullptr)
790  {
791  // don't recreate string if contents match
792  if (std::strcmp(fBuffer, strBuf) == 0)
793  return;
794 
795  if (fBuffer != _null())
796  std::free(fBuffer);
797 
798  fBufferLen = (size > 0) ? size : std::strlen(strBuf);
799  fBuffer = (char*)std::malloc(fBufferLen+1);
800 
801  if (fBuffer == nullptr)
802  {
803  fBuffer = _null();
804  fBufferLen = 0;
805  return;
806  }
807 
808  std::strcpy(fBuffer, strBuf);
809 
810  fBuffer[fBufferLen] = '\0';
811  }
812  else
813  {
814  DISTRHO_SAFE_ASSERT(size == 0);
815 
816  // don't recreate null string
817  if (fBuffer == _null())
818  return;
819 
820  DISTRHO_SAFE_ASSERT(fBuffer != nullptr);
821  std::free(fBuffer);
822 
823  fBuffer = _null();
824  fBufferLen = 0;
825  }
826  }
827 
828  DISTRHO_PREVENT_HEAP_ALLOCATION
829 };
830 
831 // -----------------------------------------------------------------------
832 
833 static inline
834 String operator+(const String& strBefore, const char* const strBufAfter) noexcept
835 {
836  const char* const strBufBefore = strBefore.buffer();
837  const std::size_t newBufSize = strBefore.length() + ((strBufAfter != nullptr) ? std::strlen(strBufAfter) : 0) + 1;
838  char newBuf[newBufSize];
839 
840  std::strcpy(newBuf, strBufBefore);
841  std::strcat(newBuf, strBufAfter);
842 
843  return String(newBuf);
844 }
845 
846 static inline
847 String operator+(const char* const strBufBefore, const String& strAfter) noexcept
848 {
849  const char* const strBufAfter = strAfter.buffer();
850  const std::size_t newBufSize = ((strBufBefore != nullptr) ? std::strlen(strBufBefore) : 0) + strAfter.length() + 1;
851  char newBuf[newBufSize];
852 
853  std::strcpy(newBuf, strBufBefore);
854  std::strcat(newBuf, strBufAfter);
855 
856  return String(newBuf);
857 }
858 
859 // -----------------------------------------------------------------------
860 
861 END_NAMESPACE_DISTRHO
862 
863 #endif // DISTRHO_STRING_HPP_INCLUDED
String
Definition: String.hpp:29