From 14524770d20c18743061d3839c86e146615ea015 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 17 Feb 2014 16:48:56 +0000 Subject: [PATCH] Optimize CarlaString; make it fully noexcept safe --- source/tests/Makefile | 2 +- source/utils/CarlaString.hpp | 167 +++++++++++++++++++++-------------- 2 files changed, 101 insertions(+), 68 deletions(-) diff --git a/source/tests/Makefile b/source/tests/Makefile index 5bdd2a8ab..450d94302 100644 --- a/source/tests/Makefile +++ b/source/tests/Makefile @@ -56,7 +56,7 @@ ansi-pedantic-test_cxx11: ansi-pedantic-test.c ../backend/Carla*.h CarlaString: CarlaString.cpp ../utils/CarlaString.hpp $(CXX) $< $(PEDANTIC_CXX_FLAGS) -o $@ - valgrind ./$@ + valgrind --leak-check=full ./$@ Engine: Engine.cpp $(CXX) $< \ diff --git a/source/utils/CarlaString.hpp b/source/utils/CarlaString.hpp index 650b2677a..d761a65c8 100644 --- a/source/utils/CarlaString.hpp +++ b/source/utils/CarlaString.hpp @@ -38,16 +38,15 @@ public: /* * Empty string. */ - explicit CarlaString() + explicit CarlaString() noexcept { _init(); - _dup(nullptr); } /* * Simple character. */ - explicit CarlaString(const char c) + explicit CarlaString(const char c) noexcept { char ch[2]; ch[0] = c; @@ -60,7 +59,7 @@ public: /* * Simple char string. */ - explicit CarlaString(char* const strBuf) + explicit CarlaString(char* const strBuf) noexcept { _init(); _dup(strBuf); @@ -69,7 +68,7 @@ public: /* * Simple const char string. */ - explicit CarlaString(const char* const strBuf) + explicit CarlaString(const char* const strBuf) noexcept { _init(); _dup(strBuf); @@ -78,7 +77,7 @@ public: /* * Integer. */ - explicit CarlaString(const int value) + explicit CarlaString(const int value) noexcept { char strBuf[0xff+1]; carla_zeroChar(strBuf, 0xff+1); @@ -91,7 +90,7 @@ public: /* * Unsigned integer, possibly in hexadecimal. */ - explicit CarlaString(const unsigned int value, const bool hexadecimal = false) + explicit CarlaString(const uint value, const bool hexadecimal = false) noexcept { char strBuf[0xff+1]; carla_zeroChar(strBuf, 0xff+1); @@ -104,7 +103,7 @@ public: /* * Long integer. */ - explicit CarlaString(const long int value) + explicit CarlaString(const long value) noexcept { char strBuf[0xff+1]; carla_zeroChar(strBuf, 0xff+1); @@ -117,7 +116,7 @@ public: /* * Long unsigned integer, possibly hexadecimal. */ - explicit CarlaString(const unsigned long int value, const bool hexadecimal = false) + explicit CarlaString(const ulong value, const bool hexadecimal = false) noexcept { char strBuf[0xff+1]; carla_zeroChar(strBuf, 0xff+1); @@ -130,7 +129,7 @@ public: /* * Long long integer. */ - explicit CarlaString(const long long int value) + explicit CarlaString(const long long value) noexcept { char strBuf[0xff+1]; carla_zeroChar(strBuf, 0xff+1); @@ -143,7 +142,7 @@ public: /* * Long long unsigned integer, possibly hexadecimal. */ - explicit CarlaString(const unsigned long long int value, const bool hexadecimal = false) + explicit CarlaString(const unsigned long long value, const bool hexadecimal = false) noexcept { char strBuf[0xff+1]; carla_zeroChar(strBuf, 0xff+1); @@ -156,7 +155,7 @@ public: /* * Single-precision floating point number. */ - explicit CarlaString(const float value) + explicit CarlaString(const float value) noexcept { char strBuf[0xff+1]; carla_zeroChar(strBuf, 0xff+1); @@ -169,7 +168,7 @@ public: /* * Double-precision floating point number. */ - explicit CarlaString(const double value) + explicit CarlaString(const double value) noexcept { char strBuf[0xff+1]; carla_zeroChar(strBuf, 0xff+1); @@ -185,7 +184,7 @@ public: /* * Create string from another string. */ - CarlaString(const CarlaString& str) + CarlaString(const CarlaString& str) noexcept { _init(); _dup(str.fBuffer); @@ -197,11 +196,17 @@ public: /* * Destructor. */ - ~CarlaString() + ~CarlaString() noexcept { CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr,); - delete[] fBuffer; + if (fBuffer == _null()) + return; + + try { + delete[] fBuffer; + } catch(...) {} + fBuffer = nullptr; } @@ -235,7 +240,7 @@ public: /* * Check if the string contains another string, optionally ignoring case. */ - bool contains(const char* const strBuf, const bool ignoreCase = false) const + bool contains(const char* const strBuf, const bool ignoreCase = false) const noexcept { CARLA_SAFE_ASSERT_RETURN(strBuf != nullptr, false); @@ -245,6 +250,11 @@ public: return (strcasestr(fBuffer, strBuf) != nullptr); #else CarlaString tmp1(fBuffer), tmp2(strBuf); + + // memory allocation failed or empty string(s) + if (tmp1.fBuffer == _null() || tmp2.fBuffer == _null()) + return false; + tmp1.toLower(); tmp2.toLower(); return (std::strstr((const char*)tmp1, (const char*)tmp2) != nullptr); @@ -257,7 +267,7 @@ public: /* * Overloaded function. */ - bool contains(const CarlaString& str, const bool ignoreCase = false) const + bool contains(const CarlaString& str, const bool ignoreCase = false) const noexcept { return contains(str.fBuffer, ignoreCase); } @@ -297,6 +307,14 @@ public: return (std::strncmp(fBuffer, prefix, prefixLen) == 0); } + /* + * Check if the string starts with the string 'prefix'. + */ + bool startsWith(const CarlaString& prefix) const noexcept + { + return startsWith(prefix.fBuffer); + } + /* * Check if the string ends with the character 'c'. */ @@ -322,6 +340,14 @@ public: return (std::strncmp(fBuffer + (fBufferLen-suffixLen), suffix, suffixLen) == 0); } + /* + * Check if the string ends with the string 'suffix'. + */ + bool endsWith(const CarlaString& suffix) const noexcept + { + return endsWith(suffix.fBuffer); + } + /* * Find the first occurrence of character 'c' in the string. * Returns "length()" if the character is not found. @@ -467,8 +493,6 @@ public: { if (fBuffer[i] == before) fBuffer[i] = after; - else if (fBuffer[i] == '\0') - break; } } @@ -535,38 +559,40 @@ public: } /* - * Return a duplicate string buffer. + * Direct access to the string buffer (read-only). */ - const char* dup() const + const char* buffer() const noexcept { - return carla_strdup(fBuffer); + return fBuffer; } /* - * Direct access to the string buffer. + * Return a duplicate string buffer. */ - const char* getBuffer() const noexcept + const char* dup() const { - return fBuffer; + return carla_strdup(fBuffer); } // ------------------------------------------------------------------- // public operators +#if 0 operator const char*() const noexcept { return fBuffer; } +#endif char& operator[](const size_t pos) const noexcept { if (pos < fBufferLen) return fBuffer[pos]; - static char rfallback; - rfallback = '\0'; + static char fallback; + fallback = '\0'; carla_safe_assert("pos < fBufferLen", __FILE__, __LINE__); - return rfallback; + return fallback; } bool operator==(const char* const strBuf) const noexcept @@ -589,19 +615,19 @@ public: return !operator==(str.fBuffer); } - CarlaString& operator=(const char* const strBuf) + CarlaString& operator=(const char* const strBuf) noexcept { _dup(strBuf); return *this; } - CarlaString& operator=(const CarlaString& str) + CarlaString& operator=(const CarlaString& str) noexcept { return operator=(str.fBuffer); } - CarlaString& operator+=(const char* const strBuf) + CarlaString& operator+=(const char* const strBuf) noexcept { if (strBuf == nullptr) return *this; @@ -617,12 +643,12 @@ public: return *this; } - CarlaString& operator+=(const CarlaString& str) + CarlaString& operator+=(const CarlaString& str) noexcept { return operator+=(str.fBuffer); } - CarlaString operator+(const char* const strBuf) + CarlaString operator+(const char* const strBuf) noexcept { const size_t newBufSize = fBufferLen + ((strBuf != nullptr) ? std::strlen(strBuf) : 0) + 1; char newBuf[newBufSize]; @@ -635,7 +661,7 @@ public: return CarlaString(newBuf); } - CarlaString operator+(const CarlaString& str) + CarlaString operator+(const CarlaString& str) noexcept { return operator+(str.fBuffer); } @@ -645,7 +671,16 @@ public: private: char* fBuffer; // the actual string buffer size_t fBufferLen; // string length - bool fFirstInit; // true when first initiated + + /* + * Static null string. + * Prevents excessive allocations for empty strings. + */ + static char* _null() noexcept + { + static char sNull = '\0'; + return &sNull; + } /* * Shared init function. @@ -653,9 +688,8 @@ private: */ void _init() noexcept { - fBuffer = nullptr; + fBuffer = _null(); fBufferLen = 0; - fFirstInit = true; } /* @@ -663,51 +697,50 @@ private: * Called whenever the string needs to be allocated. * * Notes: - * - Allocates string only if first initiated, or if 'strBuf' is not null and new string contents are different + * - Allocates string only if 'strBuf' is not null and new string contents are different * - If 'strBuf' is null 'size' must be 0 */ - void _dup(const char* const strBuf, const size_t size = 0) + void _dup(const char* const strBuf, const size_t size = 0) noexcept { if (strBuf != nullptr) { // don't recreate string if contents match - if (fFirstInit || std::strcmp(fBuffer, strBuf) != 0) + if (std::strcmp(fBuffer, strBuf) == 0) + return; + + if (fBuffer != _null()) { - if (! fFirstInit) - { - CARLA_SAFE_ASSERT(fBuffer != nullptr); + try { delete[] fBuffer; - } + } catch(...) {} + } - fBufferLen = (size > 0) ? size : std::strlen(strBuf); - fBuffer = new char[fBufferLen+1]; + fBufferLen = (size > 0) ? size : std::strlen(strBuf); - std::strcpy(fBuffer, strBuf); + try { + fBuffer = new char[fBufferLen+1]; + } + catch(...) { + _init(); + return; + } - fBuffer[fBufferLen] = '\0'; + std::strcpy(fBuffer, strBuf); - fFirstInit = false; - } + fBuffer[fBufferLen] = '\0'; } else { CARLA_SAFE_ASSERT(size == 0); // don't recreate null string - if (fFirstInit || fBufferLen != 0) - { - if (! fFirstInit) - { - CARLA_SAFE_ASSERT(fBuffer != nullptr); - delete[] fBuffer; - } + if (fBuffer == _null()) + return; - fBufferLen = 0; - fBuffer = new char[1]; - fBuffer[0] = '\0'; + CARLA_SAFE_ASSERT(fBuffer != nullptr); + delete[] fBuffer; - fFirstInit = false; - } + _init(); } } @@ -718,9 +751,9 @@ private: // ----------------------------------------------------------------------- static inline -CarlaString operator+(const CarlaString& strBefore, const char* const strBufAfter) +CarlaString operator+(const CarlaString& strBefore, const char* const strBufAfter) noexcept { - const char* const strBufBefore = (const char*)strBefore; + const char* const strBufBefore = strBefore.buffer(); const size_t newBufSize = strBefore.length() + ((strBufAfter != nullptr) ? std::strlen(strBufAfter) : 0) + 1; char newBuf[newBufSize]; @@ -731,9 +764,9 @@ CarlaString operator+(const CarlaString& strBefore, const char* const strBufAfte } static inline -CarlaString operator+(const char* const strBufBefore, const CarlaString& strAfter) +CarlaString operator+(const char* const strBufBefore, const CarlaString& strAfter) noexcept { - const char* const strBufAfter = (const char*)strAfter; + const char* const strBufAfter = strAfter.buffer(); const size_t newBufSize = ((strBufBefore != nullptr) ? std::strlen(strBufBefore) : 0) + strAfter.length() + 1; char newBuf[newBufSize];