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