2 * DISTRHO Plugin Framework (DPF)
3 * Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
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.
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.
17 #ifndef DISTRHO_STRING_HPP_INCLUDED
18 #define DISTRHO_STRING_HPP_INCLUDED
20 #include "../DistrhoUtils.hpp"
21 #include "../extra/ScopedSafeLocale.hpp"
25 START_NAMESPACE_DISTRHO
27 // -----------------------------------------------------------------------
33 // -------------------------------------------------------------------
34 // constructors (no explicit conversions allowed)
39 explicit String() noexcept
42 fBufferAlloc(false) {}
47 explicit String(const char c
) noexcept
62 explicit String(char* const strBuf
, const bool reallocData
= true) noexcept
67 if (reallocData
|| strBuf
== nullptr)
74 fBufferLen
= std::strlen(strBuf
);
80 * Simple const char string.
82 explicit String(const char* const strBuf
) noexcept
93 explicit String(const int value
) noexcept
99 std::snprintf(strBuf
, 0xff, "%d", value
);
106 * Unsigned integer, possibly in hexadecimal.
108 explicit String(const unsigned int value
, const bool hexadecimal
= false) noexcept
114 std::snprintf(strBuf
, 0xff, hexadecimal
? "0x%x" : "%u", value
);
123 explicit String(const long value
) noexcept
129 std::snprintf(strBuf
, 0xff, "%ld", value
);
136 * Long unsigned integer, possibly hexadecimal.
138 explicit String(const unsigned long value
, const bool hexadecimal
= false) noexcept
144 std::snprintf(strBuf
, 0xff, hexadecimal
? "0x%lx" : "%lu", value
);
153 explicit String(const long long value
) noexcept
159 std::snprintf(strBuf
, 0xff, "%lld", value
);
166 * Long long unsigned integer, possibly hexadecimal.
168 explicit String(const unsigned long long value
, const bool hexadecimal
= false) noexcept
174 std::snprintf(strBuf
, 0xff, hexadecimal
? "0x%llx" : "%llu", value
);
181 * Single-precision floating point number.
183 explicit String(const float value
) noexcept
191 const ScopedSafeLocale ssl
;
192 std::snprintf(strBuf
, 0xff, "%.12g", static_cast<double>(value
));
201 * Double-precision floating point number.
203 explicit String(const double value
) noexcept
211 const ScopedSafeLocale ssl
;
212 std::snprintf(strBuf
, 0xff, "%.24g", value
);
220 // -------------------------------------------------------------------
221 // non-explicit constructor
224 * Create string from another string.
226 String(const String
& str
) noexcept
234 // -------------------------------------------------------------------
242 DISTRHO_SAFE_ASSERT_RETURN(fBuffer
!= nullptr,);
249 fBufferAlloc
= false;
252 // -------------------------------------------------------------------
256 * Get length of the string.
258 std::size_t length() const noexcept
264 * Check if the string is empty.
266 bool isEmpty() const noexcept
268 return (fBufferLen
== 0);
272 * Check if the string is not empty.
274 bool isNotEmpty() const noexcept
276 return (fBufferLen
!= 0);
280 * Check if the string contains a specific character, case-sensitive.
282 bool contains(const char c
) const noexcept
284 for (std::size_t i
=0; i
<fBufferLen
; ++i
)
294 * Check if the string contains another string, optionally ignoring case.
296 bool contains(const char* const strBuf
, const bool ignoreCase
= false) const noexcept
298 DISTRHO_SAFE_ASSERT_RETURN(strBuf
!= nullptr, false);
303 return (strcasestr(fBuffer
, strBuf
) != nullptr);
305 String
tmp1(fBuffer
), tmp2(strBuf
);
307 // memory allocation failed or empty string(s)
308 if (tmp1
.fBuffer
== _null() || tmp2
.fBuffer
== _null())
313 return (std::strstr(tmp1
, tmp2
) != nullptr);
317 return (std::strstr(fBuffer
, strBuf
) != nullptr);
321 * Check if character at 'pos' is a digit.
323 bool isDigit(const std::size_t pos
) const noexcept
325 DISTRHO_SAFE_ASSERT_RETURN(pos
< fBufferLen
, false);
327 return (fBuffer
[pos
] >= '0' && fBuffer
[pos
] <= '9');
331 * Check if the string starts with the character 'c'.
333 bool startsWith(const char c
) const noexcept
335 DISTRHO_SAFE_ASSERT_RETURN(c
!= '\0', false);
337 return (fBufferLen
> 0 && fBuffer
[0] == c
);
341 * Check if the string starts with the string 'prefix'.
343 bool startsWith(const char* const prefix
) const noexcept
345 DISTRHO_SAFE_ASSERT_RETURN(prefix
!= nullptr, false);
347 const std::size_t prefixLen(std::strlen(prefix
));
349 if (fBufferLen
< prefixLen
)
352 return (std::strncmp(fBuffer
, prefix
, prefixLen
) == 0);
356 * Check if the string ends with the character 'c'.
358 bool endsWith(const char c
) const noexcept
360 DISTRHO_SAFE_ASSERT_RETURN(c
!= '\0', false);
362 return (fBufferLen
> 0 && fBuffer
[fBufferLen
-1] == c
);
366 * Check if the string ends with the string 'suffix'.
368 bool endsWith(const char* const suffix
) const noexcept
370 DISTRHO_SAFE_ASSERT_RETURN(suffix
!= nullptr, false);
372 const std::size_t suffixLen(std::strlen(suffix
));
374 if (fBufferLen
< suffixLen
)
377 return (std::strncmp(fBuffer
+ (fBufferLen
-suffixLen
), suffix
, suffixLen
) == 0);
381 * Find the first occurrence of character 'c' in the string.
382 * Returns "length()" if the character is not found.
384 std::size_t find(const char c
, bool* const found
= nullptr) const noexcept
386 if (fBufferLen
== 0 || c
== '\0')
388 if (found
!= nullptr)
393 for (std::size_t i
=0; i
< fBufferLen
; ++i
)
397 if (found
!= nullptr)
403 if (found
!= nullptr)
409 * Find the first occurrence of string 'strBuf' in the string.
410 * Returns "length()" if the string is not found.
412 std::size_t find(const char* const strBuf
, bool* const found
= nullptr) const noexcept
414 if (fBufferLen
== 0 || strBuf
== nullptr || strBuf
[0] == '\0')
416 if (found
!= nullptr)
421 if (char* const subStrBuf
= std::strstr(fBuffer
, strBuf
))
423 const ssize_t
ret(subStrBuf
- fBuffer
);
427 // should never happen!
428 d_safe_assert_int("ret >= 0", __FILE__
, __LINE__
, int(ret
));
430 if (found
!= nullptr)
435 if (found
!= nullptr)
437 return static_cast<std::size_t>(ret
);
440 if (found
!= nullptr)
446 * Find the last occurrence of character 'c' in the string.
447 * Returns "length()" if the character is not found.
449 std::size_t rfind(const char c
, bool* const found
= nullptr) const noexcept
451 if (fBufferLen
== 0 || c
== '\0')
453 if (found
!= nullptr)
458 for (std::size_t i
=fBufferLen
; i
> 0; --i
)
460 if (fBuffer
[i
-1] == c
)
462 if (found
!= nullptr)
468 if (found
!= nullptr)
474 * Find the last occurrence of string 'strBuf' in the string.
475 * Returns "length()" if the string is not found.
477 std::size_t rfind(const char* const strBuf
, bool* const found
= nullptr) const noexcept
479 if (found
!= nullptr)
482 if (fBufferLen
== 0 || strBuf
== nullptr || strBuf
[0] == '\0')
485 const std::size_t strBufLen(std::strlen(strBuf
));
487 std::size_t ret
= fBufferLen
;
488 const char* tmpBuf
= fBuffer
;
490 for (std::size_t i
=0; i
< fBufferLen
; ++i
)
492 if (std::strstr(tmpBuf
+1, strBuf
) == nullptr && std::strncmp(tmpBuf
, strBuf
, strBufLen
) == 0)
494 if (found
!= nullptr)
503 return fBufferLen
-ret
;
509 void clear() noexcept
515 * Replace all occurrences of character 'before' with character 'after'.
517 String
& replace(const char before
, const char after
) noexcept
519 DISTRHO_SAFE_ASSERT_RETURN(before
!= '\0' /* && after != '\0' */, *this);
521 for (std::size_t i
=0; i
< fBufferLen
; ++i
)
523 if (fBuffer
[i
] == before
)
531 * Remove all occurrences of character 'c', shifting and truncating the string as necessary.
533 String
& remove(const char c
) noexcept
535 DISTRHO_SAFE_ASSERT_RETURN(c
!= '\0', *this);
540 for (std::size_t i
=0; i
< fBufferLen
; ++i
)
545 std::memmove(fBuffer
+i
, fBuffer
+i
+1, fBufferLen
-i
);
549 fBuffer
[fBufferLen
] = '\0';
554 * Truncate the string to size 'n'.
556 String
& truncate(const std::size_t n
) noexcept
568 * Convert all non-basic characters to '_'.
570 String
& toBasic() noexcept
572 for (std::size_t i
=0; i
< fBufferLen
; ++i
)
574 if (fBuffer
[i
] >= '0' && fBuffer
[i
] <= '9')
576 if (fBuffer
[i
] >= 'A' && fBuffer
[i
] <= 'Z')
578 if (fBuffer
[i
] >= 'a' && fBuffer
[i
] <= 'z')
580 if (fBuffer
[i
] == '_')
590 * Convert all ascii characters to lowercase.
592 String
& toLower() noexcept
594 static const char kCharDiff('a' - 'A');
596 for (std::size_t i
=0; i
< fBufferLen
; ++i
)
598 if (fBuffer
[i
] >= 'A' && fBuffer
[i
] <= 'Z')
599 fBuffer
[i
] = static_cast<char>(fBuffer
[i
] + kCharDiff
);
606 * Convert all ascii characters to uppercase.
608 String
& toUpper() noexcept
610 static const char kCharDiff('a' - 'A');
612 for (std::size_t i
=0; i
< fBufferLen
; ++i
)
614 if (fBuffer
[i
] >= 'a' && fBuffer
[i
] <= 'z')
615 fBuffer
[i
] = static_cast<char>(fBuffer
[i
] - kCharDiff
);
622 * Create a new string where all non-basic characters are converted to '_'.
625 String
asBasic() const noexcept
632 * Create a new string where all ascii characters are converted lowercase.
635 String
asLower() const noexcept
642 * Create a new string where all ascii characters are converted to uppercase.
645 String
asUpper() const noexcept
652 * Direct access to the string buffer (read-only).
654 const char* buffer() const noexcept
660 * Get and release the string buffer, while also clearing this string.
661 * This allows to keep a pointer to the buffer after this object is deleted.
662 * Result must be freed.
664 char* getAndReleaseBuffer() noexcept
666 char* ret
= fBufferLen
> 0 ? fBuffer
: nullptr;
669 fBufferAlloc
= false;
673 // -------------------------------------------------------------------
674 // base64 stuff, based on http://www.adp-gmbh.ch/cpp/common/base64.html
675 // Copyright (C) 2004-2008 René Nyffenegger
677 static String
asBase64(const void* const data
, const std::size_t dataSize
)
679 static const char* const kBase64Chars
=
680 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
681 "abcdefghijklmnopqrstuvwxyz"
685 const std::size_t kTmpBufSize
= std::min(d_nextPowerOf2(static_cast<uint32_t>(dataSize
/3)), 65536U);
687 constexpr std::size_t kTmpBufSize
= 65536U;
690 const uchar
* bytesToEncode((const uchar
*)data
);
693 uint charArray3
[3], charArray4
[4];
695 char strBuf
[kTmpBufSize
+ 1];
696 strBuf
[kTmpBufSize
] = '\0';
697 std::size_t strBufIndex
= 0;
701 for (std::size_t s
=0; s
<dataSize
; ++s
)
703 charArray3
[i
++] = *(bytesToEncode
++);
707 charArray4
[0] = (charArray3
[0] & 0xfc) >> 2;
708 charArray4
[1] = ((charArray3
[0] & 0x03) << 4) + ((charArray3
[1] & 0xf0) >> 4);
709 charArray4
[2] = ((charArray3
[1] & 0x0f) << 2) + ((charArray3
[2] & 0xc0) >> 6);
710 charArray4
[3] = charArray3
[2] & 0x3f;
713 strBuf
[strBufIndex
++] = kBase64Chars
[charArray4
[i
]];
715 if (strBufIndex
>= kTmpBufSize
-7)
717 strBuf
[strBufIndex
] = '\0';
729 charArray3
[j
] = '\0';
731 charArray4
[0] = (charArray3
[0] & 0xfc) >> 2;
732 charArray4
[1] = ((charArray3
[0] & 0x03) << 4) + ((charArray3
[1] & 0xf0) >> 4);
733 charArray4
[2] = ((charArray3
[1] & 0x0f) << 2) + ((charArray3
[2] & 0xc0) >> 6);
734 charArray4
[3] = charArray3
[2] & 0x3f;
736 for (j
=0; j
<4 && i
<3 && j
<i
+1; ++j
)
737 strBuf
[strBufIndex
++] = kBase64Chars
[charArray4
[j
]];
740 strBuf
[strBufIndex
++] = '=';
743 if (strBufIndex
!= 0)
745 strBuf
[strBufIndex
] = '\0';
752 // -------------------------------------------------------------------
755 operator const char*() const noexcept
760 char operator[](const std::size_t pos
) const noexcept
762 if (pos
< fBufferLen
)
765 d_safe_assert("pos < fBufferLen", __FILE__
, __LINE__
);
767 static char fallback
;
772 char& operator[](const std::size_t pos
) noexcept
774 if (pos
< fBufferLen
)
777 d_safe_assert("pos < fBufferLen", __FILE__
, __LINE__
);
779 static char fallback
;
784 bool operator==(const char* const strBuf
) const noexcept
786 return (strBuf
!= nullptr && std::strcmp(fBuffer
, strBuf
) == 0);
789 bool operator==(const String
& str
) const noexcept
791 return operator==(str
.fBuffer
);
794 bool operator!=(const char* const strBuf
) const noexcept
796 return !operator==(strBuf
);
799 bool operator!=(const String
& str
) const noexcept
801 return !operator==(str
.fBuffer
);
804 String
& operator=(const char* const strBuf
) noexcept
811 String
& operator=(const String
& str
) noexcept
818 String
& operator+=(const char* const strBuf
) noexcept
820 if (strBuf
== nullptr || strBuf
[0] == '\0')
823 const std::size_t strBufLen
= std::strlen(strBuf
);
825 // for empty strings, we can just take the appended string as our entire data
828 _dup(strBuf
, strBufLen
);
832 // we have some data ourselves, reallocate to add the new stuff
833 char* const newBuf
= (char*)realloc(fBuffer
, fBufferLen
+ strBufLen
+ 1);
834 DISTRHO_SAFE_ASSERT_RETURN(newBuf
!= nullptr, *this);
836 std::memcpy(newBuf
+ fBufferLen
, strBuf
, strBufLen
+ 1);
839 fBufferLen
+= strBufLen
;
844 String
& operator+=(const String
& str
) noexcept
846 return operator+=(str
.fBuffer
);
849 String
operator+(const char* const strBuf
) noexcept
851 if (strBuf
== nullptr || strBuf
[0] == '\0')
854 return String(strBuf
);
856 const std::size_t strBufLen
= std::strlen(strBuf
);
857 const std::size_t newBufSize
= fBufferLen
+ strBufLen
;
858 char* const newBuf
= (char*)malloc(newBufSize
+ 1);
859 DISTRHO_SAFE_ASSERT_RETURN(newBuf
!= nullptr, String());
861 std::memcpy(newBuf
, fBuffer
, fBufferLen
);
862 std::memcpy(newBuf
+ fBufferLen
, strBuf
, strBufLen
+ 1);
864 return String(newBuf
, false);
867 String
operator+(const String
& str
) noexcept
869 return operator+(str
.fBuffer
);
872 // needed for std::map compatibility
873 bool operator<(const String
& str
) const noexcept
875 return std::strcmp(fBuffer
, str
.fBuffer
) < 0;
878 // -------------------------------------------------------------------
881 char* fBuffer
; // the actual string buffer
882 std::size_t fBufferLen
; // string length
883 bool fBufferAlloc
; // wherever the buffer is allocated, not using _null()
886 * Static null string.
887 * Prevents allocation for new and/or empty strings.
889 static char* _null() noexcept
891 static char sNull
= '\0';
897 * Called whenever the string needs to be allocated.
900 * - Allocates string only if 'strBuf' is not null and new string contents are different
901 * - If 'strBuf' is null, 'size' must be 0
903 void _dup(const char* const strBuf
, const std::size_t size
= 0) noexcept
905 if (strBuf
!= nullptr)
907 // don't recreate string if contents match
908 if (std::strcmp(fBuffer
, strBuf
) == 0)
914 fBufferLen
= (size
> 0) ? size
: std::strlen(strBuf
);
915 fBuffer
= (char*)std::malloc(fBufferLen
+1);
917 if (fBuffer
== nullptr)
921 fBufferAlloc
= false;
927 std::strcpy(fBuffer
, strBuf
);
928 fBuffer
[fBufferLen
] = '\0';
932 DISTRHO_SAFE_ASSERT_UINT(size
== 0, static_cast<uint
>(size
));
934 // don't recreate null string
938 DISTRHO_SAFE_ASSERT(fBuffer
!= nullptr);
943 fBufferAlloc
= false;
947 DISTRHO_PREVENT_HEAP_ALLOCATION
950 // -----------------------------------------------------------------------
953 String
operator+(const String
& strBefore
, const char* const strBufAfter
) noexcept
955 if (strBufAfter
== nullptr || strBufAfter
[0] == '\0')
957 if (strBefore
.isEmpty())
958 return String(strBufAfter
);
960 const std::size_t strBeforeLen
= strBefore
.length();
961 const std::size_t strBufAfterLen
= std::strlen(strBufAfter
);
962 const std::size_t newBufSize
= strBeforeLen
+ strBufAfterLen
;
963 char* const newBuf
= (char*)malloc(newBufSize
+ 1);
964 DISTRHO_SAFE_ASSERT_RETURN(newBuf
!= nullptr, String());
966 std::memcpy(newBuf
, strBefore
.buffer(), strBeforeLen
);
967 std::memcpy(newBuf
+ strBeforeLen
, strBufAfter
, strBufAfterLen
+ 1);
969 return String(newBuf
, false);
973 String
operator+(const char* const strBufBefore
, const String
& strAfter
) noexcept
975 if (strAfter
.isEmpty())
976 return String(strBufBefore
);
977 if (strBufBefore
== nullptr || strBufBefore
[0] == '\0')
980 const std::size_t strBufBeforeLen
= std::strlen(strBufBefore
);
981 const std::size_t strAfterLen
= strAfter
.length();
982 const std::size_t newBufSize
= strBufBeforeLen
+ strAfterLen
;
983 char* const newBuf
= (char*)malloc(newBufSize
+ 1);
984 DISTRHO_SAFE_ASSERT_RETURN(newBuf
!= nullptr, String());
986 std::memcpy(newBuf
, strBufBefore
, strBufBeforeLen
);
987 std::memcpy(newBuf
+ strBufBeforeLen
, strAfter
.buffer(), strAfterLen
+ 1);
989 return String(newBuf
, false);
992 // -----------------------------------------------------------------------
994 END_NAMESPACE_DISTRHO
996 #endif // DISTRHO_STRING_HPP_INCLUDED