diff --git a/source/modules/distrho/extra/d_leakdetector.hpp b/source/modules/distrho/extra/d_leakdetector.hpp new file mode 100644 index 000000000..ca5a1b7db --- /dev/null +++ b/source/modules/distrho/extra/d_leakdetector.hpp @@ -0,0 +1,139 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2014 Filipe Coelho + * + * Permission to use, copy, modify, and/or distribute this software for any purpose with + * or without fee is hereby granted, provided that the above copyright notice and this + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef DISTRHO_LEAK_DETECTOR_HPP_INCLUDED +#define DISTRHO_LEAK_DETECTOR_HPP_INCLUDED + +#include "../DistrhoUtils.hpp" + +// ----------------------------------------------------------------------- +// The following code was based from juce-core LeakDetector class +// Copyright (C) 2013 Raw Material Software Ltd. + +/** A good old-fashioned C macro concatenation helper. + This combines two items (which may themselves be macros) into a single string, + avoiding the pitfalls of the ## macro operator. +*/ +#define DISTRHO_JOIN_MACRO_HELPER(a, b) a ## b +#define DISTRHO_JOIN_MACRO(item1, item2) DISTRHO_JOIN_MACRO_HELPER(item1, item2) + +/** This macro lets you embed a leak-detecting object inside a class.\n + To use it, simply declare a DISTRHO_LEAK_DETECTOR(YourClassName) inside a private section + of the class declaration. E.g. + \code + class MyClass + { + public: + MyClass(); + void blahBlah(); + + private: + DISTRHO_LEAK_DETECTOR(MyClass) + }; + \endcode +*/ +#define DISTRHO_LEAK_DETECTOR(ClassName) \ + friend class ::DistrhoLeakedObjectDetector; \ + static const char* getLeakedObjectClassName() noexcept { return #ClassName; } \ + ::DistrhoLeakedObjectDetector DISTRHO_JOIN_MACRO(leakDetector, __LINE__); + +#define DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ClassName) \ + DISTRHO_DECLARE_NON_COPY_CLASS(ClassName) \ + DISTRHO_LEAK_DETECTOR(ClassName) + +//============================================================================== +/** + Embedding an instance of this class inside another class can be used as a low-overhead + way of detecting leaked instances. + + This class keeps an internal static count of the number of instances that are + active, so that when the app is shutdown and the static destructors are called, + it can check whether there are any left-over instances that may have been leaked. + + To use it, use the DISTRHO_LEAK_DETECTOR macro as a simple way to put one in your + class declaration. +*/ +template +class DistrhoLeakedObjectDetector +{ +public: + //============================================================================== + DistrhoLeakedObjectDetector() noexcept { ++(getCounter().numObjects); } + DistrhoLeakedObjectDetector(const DistrhoLeakedObjectDetector&) noexcept { ++(getCounter().numObjects); } + + ~DistrhoLeakedObjectDetector() noexcept + { + if (--(getCounter().numObjects) < 0) + { + /** If you hit this, then you've managed to delete more instances of this class than you've + created.. That indicates that you're deleting some dangling pointers. + + Note that although this assertion will have been triggered during a destructor, it might + not be this particular deletion that's at fault - the incorrect one may have happened + at an earlier point in the program, and simply not been detected until now. + + Most errors like this are caused by using old-fashioned, non-RAII techniques for + your object management. Tut, tut. Always, always use ScopedPointers, OwnedArrays, + ReferenceCountedObjects, etc, and avoid the 'delete' operator at all costs! + */ + d_stderr2("*** Dangling pointer deletion! Class: '%s', Count: %i", getLeakedObjectClassName(), getCounter().numObjects); + } + } + +private: + //============================================================================== + class LeakCounter + { + public: + LeakCounter() noexcept + { + numObjects = 0; + } + + ~LeakCounter() noexcept + { + if (numObjects > 0) + { + /** If you hit this, then you've leaked one or more objects of the type specified by + the 'OwnerClass' template parameter - the name should have been printed by the line above. + + If you're leaking, it's probably because you're using old-fashioned, non-RAII techniques for + your object management. Tut, tut. Always, always use ScopedPointers, OwnedArrays, + ReferenceCountedObjects, etc, and avoid the 'delete' operator at all costs! + */ + d_stderr2("*** Leaked objects detected: %i instance(s) of class '%s'", numObjects, getLeakedObjectClassName()); + } + } + + // this should be an atomic... + volatile int numObjects; + }; + + static const char* getLeakedObjectClassName() noexcept + { + return OwnerClass::getLeakedObjectClassName(); + } + + static LeakCounter& getCounter() noexcept + { + static LeakCounter counter; + return counter; + } +}; + +// ----------------------------------------------------------------------- + +#endif // DISTRHO_LEAK_DETECTOR_HPP_INCLUDED diff --git a/source/modules/distrho/extra/d_mutex.hpp b/source/modules/distrho/extra/d_mutex.hpp new file mode 100644 index 000000000..34d06c49c --- /dev/null +++ b/source/modules/distrho/extra/d_mutex.hpp @@ -0,0 +1,221 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2014 Filipe Coelho + * + * Permission to use, copy, modify, and/or distribute this software for any purpose with + * or without fee is hereby granted, provided that the above copyright notice and this + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef DISTRHO_MUTEX_HPP_INCLUDED +#define DISTRHO_MUTEX_HPP_INCLUDED + +#include "../DistrhoUtils.hpp" + +#include + +// ----------------------------------------------------------------------- +// d_mutex class + +class d_mutex +{ +public: + /* + * Constructor. + */ + d_mutex() noexcept + { + pthread_mutex_init(&fMutex, nullptr); + } + + /* + * Destructor. + */ + ~d_mutex() noexcept + { + pthread_mutex_destroy(&fMutex); + } + + /* + * Lock the mutex. + */ + void lock() const noexcept + { + pthread_mutex_lock(&fMutex); + } + + /* + * Try to lock the mutex. + * Returns true if successful. + */ + bool tryLock() const noexcept + { + return (pthread_mutex_trylock(&fMutex) == 0); + } + + /* + * Unlock the mutex, optionally resetting the tryLock check. + */ + void unlock() const noexcept + { + pthread_mutex_unlock(&fMutex); + } + +private: + mutable pthread_mutex_t fMutex; + + DISTRHO_PREVENT_HEAP_ALLOCATION + DISTRHO_DECLARE_NON_COPY_CLASS(d_mutex) +}; + +// ----------------------------------------------------------------------- +// d_rmutex class + +class d_rmutex +{ +public: + /* + * Constructor. + */ + d_rmutex() noexcept + { +#ifdef DISTRHO_OS_WIN + InitializeCriticalSection(&fSection); +#else + pthread_mutexattr_t atts; + pthread_mutexattr_init(&atts); + pthread_mutexattr_settype(&atts, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&fMutex, &atts); + pthread_mutexattr_destroy(&atts); +#endif + } + + /* + * Destructor. + */ + ~d_rmutex() noexcept + { +#ifdef DISTRHO_OS_WIN + DeleteCriticalSection(&fSection); +#else + pthread_mutex_destroy(&fMutex); +#endif + } + + /* + * Lock the mutex. + */ + void lock() const noexcept + { +#ifdef DISTRHO_OS_WIN + EnterCriticalSection(&fSection); +#else + pthread_mutex_lock(&fMutex); +#endif + } + + /* + * Try to lock the mutex. + * Returns true if successful. + */ + bool tryLock() const noexcept + { +#ifdef DISTRHO_OS_WIN + return (TryEnterCriticalSection(&fSection) != FALSE); +#else + return (pthread_mutex_trylock(&fMutex) == 0); +#endif + } + + /* + * Unlock the mutex. + */ + void unlock() const noexcept + { +#ifdef DISTRHO_OS_WIN + LeaveCriticalSection(&fSection); +#else + pthread_mutex_unlock(&fMutex); +#endif + } + +private: +#ifdef DISTRHO_OS_WIN + mutable CRITICAL_SECTION fSection; +#else + mutable pthread_mutex_t fMutex; +#endif + + DISTRHO_PREVENT_HEAP_ALLOCATION + DISTRHO_DECLARE_NON_COPY_CLASS(d_rmutex) +}; + +// ----------------------------------------------------------------------- +// Helper class to lock&unlock a mutex during a function scope. + +template +class d_scopeLocker +{ +public: + d_scopeLocker(const Mutex& mutex) noexcept + : fMutex(mutex) + { + fMutex.lock(); + } + + ~d_scopeLocker() noexcept + { + fMutex.unlock(); + } + +private: + const Mutex& fMutex; + + DISTRHO_PREVENT_HEAP_ALLOCATION + DISTRHO_DECLARE_NON_COPY_CLASS(d_scopeLocker) +}; + +// ----------------------------------------------------------------------- +// Helper class to unlock&lock a mutex during a function scope. + +template +class d_scopeUnlocker +{ +public: + d_scopeUnlocker(const Mutex& mutex) noexcept + : fMutex(mutex) + { + fMutex.unlock(); + } + + ~d_scopeUnlocker() noexcept + { + fMutex.lock(); + } + +private: + const Mutex& fMutex; + + DISTRHO_PREVENT_HEAP_ALLOCATION + DISTRHO_DECLARE_NON_COPY_CLASS(d_scopeUnlocker) +}; + +// ----------------------------------------------------------------------- +// Define types + +typedef d_scopeLocker d_mutexLocker; +typedef d_scopeLocker d_rmutexLocker; + +typedef d_scopeUnlocker d_mutexUnlocker; +typedef d_scopeUnlocker d_rmutexUnlocker; + +// ----------------------------------------------------------------------- + +#endif // DISTRHO_MUTEX_HPP_INCLUDED diff --git a/source/modules/distrho/extra/d_string.hpp b/source/modules/distrho/extra/d_string.hpp new file mode 100644 index 000000000..3020d9015 --- /dev/null +++ b/source/modules/distrho/extra/d_string.hpp @@ -0,0 +1,746 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2014 Filipe Coelho + * + * Permission to use, copy, modify, and/or distribute this software for any purpose with + * or without fee is hereby granted, provided that the above copyright notice and this + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef DISTRHO_STRING_HPP_INCLUDED +#define DISTRHO_STRING_HPP_INCLUDED + +#include "d_leakdetector.hpp" + +// ----------------------------------------------------------------------- +// d_string class + +class d_string +{ +public: + // ------------------------------------------------------------------- + // constructors (no explicit conversions allowed) + + /* + * Empty string. + */ + explicit d_string() noexcept + { + _init(); + } + + /* + * Simple character. + */ + explicit d_string(const char c) noexcept + { + char ch[2]; + ch[0] = c; + ch[1] = '\0'; + + _init(); + _dup(ch); + } + + /* + * Simple char string. + */ + explicit d_string(char* const strBuf) noexcept + { + _init(); + _dup(strBuf); + } + + /* + * Simple const char string. + */ + explicit d_string(const char* const strBuf) noexcept + { + _init(); + _dup(strBuf); + } + + /* + * Integer. + */ + explicit d_string(const int value) noexcept + { + char strBuf[0xff+1]; + std::snprintf(strBuf, 0xff, "%d", value); + strBuf[0xff] = '\0'; + + _init(); + _dup(strBuf); + } + + /* + * Unsigned integer, possibly in hexadecimal. + */ + explicit d_string(const unsigned int value, const bool hexadecimal = false) noexcept + { + char strBuf[0xff+1]; + std::snprintf(strBuf, 0xff, hexadecimal ? "0x%x" : "%u", value); + strBuf[0xff] = '\0'; + + _init(); + _dup(strBuf); + } + + /* + * Long integer. + */ + explicit d_string(const long value) noexcept + { + char strBuf[0xff+1]; + std::snprintf(strBuf, 0xff, "%ld", value); + strBuf[0xff] = '\0'; + + _init(); + _dup(strBuf); + } + + /* + * Long unsigned integer, possibly hexadecimal. + */ + explicit d_string(const unsigned long value, const bool hexadecimal = false) noexcept + { + char strBuf[0xff+1]; + std::snprintf(strBuf, 0xff, hexadecimal ? "0x%lx" : "%lu", value); + strBuf[0xff] = '\0'; + + _init(); + _dup(strBuf); + } + + /* + * Long long integer. + */ + explicit d_string(const long long value) noexcept + { + char strBuf[0xff+1]; + std::snprintf(strBuf, 0xff, "%lld", value); + strBuf[0xff] = '\0'; + + _init(); + _dup(strBuf); + } + + /* + * Long long unsigned integer, possibly hexadecimal. + */ + explicit d_string(const unsigned long long value, const bool hexadecimal = false) noexcept + { + char strBuf[0xff+1]; + std::snprintf(strBuf, 0xff, hexadecimal ? "0x%llx" : "%llu", value); + strBuf[0xff] = '\0'; + + _init(); + _dup(strBuf); + } + + /* + * Single-precision floating point number. + */ + explicit d_string(const float value) noexcept + { + char strBuf[0xff+1]; + std::snprintf(strBuf, 0xff, "%f", value); + strBuf[0xff] = '\0'; + + _init(); + _dup(strBuf); + } + + /* + * Double-precision floating point number. + */ + explicit d_string(const double value) noexcept + { + char strBuf[0xff+1]; + std::snprintf(strBuf, 0xff, "%g", value); + strBuf[0xff] = '\0'; + + _init(); + _dup(strBuf); + } + + // ------------------------------------------------------------------- + // non-explicit constructor + + /* + * Create string from another string. + */ + d_string(const d_string& str) noexcept + { + _init(); + _dup(str.fBuffer); + } + + // ------------------------------------------------------------------- + // destructor + + /* + * Destructor. + */ + ~d_string() noexcept + { + DISTRHO_SAFE_ASSERT_RETURN(fBuffer != nullptr,); + + if (fBuffer == _null()) + return; + + std::free(fBuffer); + + fBuffer = nullptr; + fBufferLen = 0; + } + + // ------------------------------------------------------------------- + // public methods + + /* + * Get length of the string. + */ + size_t length() const noexcept + { + return fBufferLen; + } + + /* + * Check if the string is empty. + */ + bool isEmpty() const noexcept + { + return (fBufferLen == 0); + } + + /* + * Check if the string is not empty. + */ + bool isNotEmpty() const noexcept + { + return (fBufferLen != 0); + } + + /* + * Check if the string contains another string, optionally ignoring case. + */ + bool contains(const char* const strBuf, const bool ignoreCase = false) const noexcept + { + DISTRHO_SAFE_ASSERT_RETURN(strBuf != nullptr, false); + + if (ignoreCase) + { +#ifdef __USE_GNU + return (strcasestr(fBuffer, strBuf) != nullptr); +#else + d_string 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(tmp1, tmp2) != nullptr); +#endif + } + + return (std::strstr(fBuffer, strBuf) != nullptr); + } + + /* + * Check if character at 'pos' is a digit. + */ + bool isDigit(const size_t pos) const noexcept + { + DISTRHO_SAFE_ASSERT_RETURN(pos < fBufferLen, false); + + return (fBuffer[pos] >= '0' && fBuffer[pos] <= '9'); + } + + /* + * Check if the string starts with the character 'c'. + */ + bool startsWith(const char c) const noexcept + { + DISTRHO_SAFE_ASSERT_RETURN(c != '\0', false); + + return (fBufferLen > 0 && fBuffer[0] == c); + } + + /* + * Check if the string starts with the string 'prefix'. + */ + bool startsWith(const char* const prefix) const noexcept + { + DISTRHO_SAFE_ASSERT_RETURN(prefix != nullptr, false); + + const size_t prefixLen(std::strlen(prefix)); + + if (fBufferLen < prefixLen) + return false; + + return (std::strncmp(fBuffer, prefix, prefixLen) == 0); + } + + /* + * Check if the string ends with the character 'c'. + */ + bool endsWith(const char c) const noexcept + { + DISTRHO_SAFE_ASSERT_RETURN(c != '\0', false); + + return (fBufferLen > 0 && fBuffer[fBufferLen-1] == c); + } + + /* + * Check if the string ends with the string 'suffix'. + */ + bool endsWith(const char* const suffix) const noexcept + { + DISTRHO_SAFE_ASSERT_RETURN(suffix != nullptr, false); + + const size_t suffixLen(std::strlen(suffix)); + + if (fBufferLen < suffixLen) + return false; + + return (std::strncmp(fBuffer + (fBufferLen-suffixLen), suffix, suffixLen) == 0); + } + + /* + * Find the first occurrence of character 'c' in the string. + * Returns "length()" if the character is not found. + */ + size_t find(const char c, bool* const found = nullptr) const noexcept + { + if (fBufferLen == 0 || c == '\0') + { + if (found != nullptr) + *found = false; + return fBufferLen; + } + + for (size_t i=0; i < fBufferLen; ++i) + { + if (fBuffer[i] == c) + { + if (found != nullptr) + *found = true; + return i; + } + } + + if (found != nullptr) + *found = false; + return fBufferLen; + } + + /* + * Find the first occurrence of string 'strBuf' in the string. + * Returns "length()" if the string is not found. + */ + size_t find(const char* const strBuf, bool* const found = nullptr) const noexcept + { + if (fBufferLen == 0 || strBuf == nullptr || strBuf[0] == '\0') + { + if (found != nullptr) + *found = false; + return fBufferLen; + } + + if (char* const subStrBuf = std::strstr(fBuffer, strBuf)) + { + const ssize_t ret(subStrBuf - fBuffer); + + if (ret < 0) + { + // should never happen! + d_safe_assert("ret >= 0", __FILE__, __LINE__); + + if (found != nullptr) + *found = false; + return fBufferLen; + } + + if (found != nullptr) + *found = true; + return static_cast(ret); + } + + if (found != nullptr) + *found = false; + return fBufferLen; + } + + /* + * Find the last occurrence of character 'c' in the string. + * Returns "length()" if the character is not found. + */ + size_t rfind(const char c, bool* const found = nullptr) const noexcept + { + if (fBufferLen == 0 || c == '\0') + { + if (found != nullptr) + *found = false; + return fBufferLen; + } + + for (size_t i=fBufferLen; i > 0; --i) + { + if (fBuffer[i-1] == c) + { + if (found != nullptr) + *found = true; + return i-1; + } + } + + if (found != nullptr) + *found = false; + return fBufferLen; + } + + /* + * Find the last occurrence of string 'strBuf' in the string. + * Returns "length()" if the string is not found. + */ + size_t rfind(const char* const strBuf, bool* const found = nullptr) const noexcept + { + if (found != nullptr) + *found = false; + + if (fBufferLen == 0 || strBuf == nullptr || strBuf[0] == '\0') + return fBufferLen; + + const size_t strBufLen(std::strlen(strBuf)); + + size_t ret = fBufferLen; + const char* tmpBuf = fBuffer; + + for (size_t i=0; i < fBufferLen; ++i) + { + if (std::strstr(tmpBuf+1, strBuf) == nullptr && std::strncmp(tmpBuf, strBuf, strBufLen) == 0) + { + if (found != nullptr) + *found = true; + break; + } + + --ret; + ++tmpBuf; + } + + return fBufferLen-ret; + } + + /* + * Clear the string. + */ + void clear() noexcept + { + truncate(0); + } + + /* + * Replace all occurrences of character 'before' with character 'after'. + */ + void replace(const char before, const char after) noexcept + { + DISTRHO_SAFE_ASSERT_RETURN(before != '\0' && after != '\0',); + + for (size_t i=0; i < fBufferLen; ++i) + { + if (fBuffer[i] == before) + fBuffer[i] = after; + } + } + + /* + * Truncate the string to size 'n'. + */ + void truncate(const size_t n) noexcept + { + if (n >= fBufferLen) + return; + + for (size_t i=n; i < fBufferLen; ++i) + fBuffer[i] = '\0'; + + fBufferLen = n; + } + + /* + * Convert all non-basic characters to '_'. + */ + void toBasic() noexcept + { + for (size_t i=0; i < fBufferLen; ++i) + { + if (fBuffer[i] >= '0' && fBuffer[i] <= '9') + continue; + if (fBuffer[i] >= 'A' && fBuffer[i] <= 'Z') + continue; + if (fBuffer[i] >= 'a' && fBuffer[i] <= 'z') + continue; + if (fBuffer[i] == '_') + continue; + + fBuffer[i] = '_'; + } + } + + /* + * Convert to all ascii characters to lowercase. + */ + void toLower() noexcept + { + static const char kCharDiff('a' - 'A'); + + for (size_t i=0; i < fBufferLen; ++i) + { + if (fBuffer[i] >= 'A' && fBuffer[i] <= 'Z') + fBuffer[i] = static_cast(fBuffer[i] + kCharDiff); + } + } + + /* + * Convert to all ascii characters to uppercase. + */ + void toUpper() noexcept + { + static const char kCharDiff('a' - 'A'); + + for (size_t i=0; i < fBufferLen; ++i) + { + if (fBuffer[i] >= 'a' && fBuffer[i] <= 'z') + fBuffer[i] = static_cast(fBuffer[i] - kCharDiff); + } + } + + /* + * Direct access to the string buffer (read-only). + */ + const char* buffer() const noexcept + { + return fBuffer; + } + + // ------------------------------------------------------------------- + // public operators + + operator const char*() const noexcept + { + return fBuffer; + } + + char operator[](const size_t pos) const noexcept + { + if (pos < fBufferLen) + return fBuffer[pos]; + + d_safe_assert("pos < fBufferLen", __FILE__, __LINE__); + + static char fallback; + fallback = '\0'; + return fallback; + } + + char& operator[](const size_t pos) noexcept + { + if (pos < fBufferLen) + return fBuffer[pos]; + + d_safe_assert("pos < fBufferLen", __FILE__, __LINE__); + + static char fallback; + fallback = '\0'; + return fallback; + } + + bool operator==(const char* const strBuf) const noexcept + { + return (strBuf != nullptr && std::strcmp(fBuffer, strBuf) == 0); + } + + bool operator==(const d_string& str) const noexcept + { + return operator==(str.fBuffer); + } + + bool operator!=(const char* const strBuf) const noexcept + { + return !operator==(strBuf); + } + + bool operator!=(const d_string& str) const noexcept + { + return !operator==(str.fBuffer); + } + + d_string& operator=(const char* const strBuf) noexcept + { + _dup(strBuf); + + return *this; + } + + d_string& operator=(const d_string& str) noexcept + { + _dup(str.fBuffer); + + return *this; + } + + d_string& operator+=(const char* const strBuf) noexcept + { + if (strBuf == nullptr) + return *this; + + const size_t newBufSize = fBufferLen + std::strlen(strBuf) + 1; + char newBuf[newBufSize]; + + std::strcpy(newBuf, fBuffer); + std::strcat(newBuf, strBuf); + + _dup(newBuf, newBufSize-1); + + return *this; + } + + d_string& operator+=(const d_string& str) noexcept + { + return operator+=(str.fBuffer); + } + + d_string operator+(const char* const strBuf) noexcept + { + const size_t newBufSize = fBufferLen + ((strBuf != nullptr) ? std::strlen(strBuf) : 0) + 1; + char newBuf[newBufSize]; + + std::strcpy(newBuf, fBuffer); + + if (strBuf != nullptr) + std::strcat(newBuf, strBuf); + + return d_string(newBuf); + } + + d_string operator+(const d_string& str) noexcept + { + return operator+(str.fBuffer); + } + + // ------------------------------------------------------------------- + +private: + char* fBuffer; // the actual string buffer + size_t fBufferLen; // string length + + /* + * Static null string. + * Prevents allocation for new and/or empty strings. + */ + static char* _null() noexcept + { + static char sNull = '\0'; + return &sNull; + } + + /* + * Shared init function. + * Called on all constructors. + */ + void _init() noexcept + { + fBuffer = _null(); + fBufferLen = 0; + } + + /* + * Helper function. + * Called whenever the string needs to be allocated. + * + * Notes: + * - 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) noexcept + { + if (strBuf != nullptr) + { + // don't recreate string if contents match + if (std::strcmp(fBuffer, strBuf) == 0) + return; + + if (fBuffer != _null()) + std::free(fBuffer); + + fBufferLen = (size > 0) ? size : std::strlen(strBuf); + fBuffer = (char*)std::malloc(fBufferLen+1); + + if (fBuffer == nullptr) + return _init(); + + std::strcpy(fBuffer, strBuf); + + fBuffer[fBufferLen] = '\0'; + } + else + { + DISTRHO_SAFE_ASSERT(size == 0); + + // don't recreate null string + if (fBuffer == _null()) + return; + + DISTRHO_SAFE_ASSERT(fBuffer != nullptr); + std::free(fBuffer); + + _init(); + } + } + + DISTRHO_LEAK_DETECTOR(d_string) + DISTRHO_PREVENT_HEAP_ALLOCATION +}; + +// ----------------------------------------------------------------------- + +static inline +d_string operator+(const d_string& strBefore, const char* const strBufAfter) noexcept +{ + const char* const strBufBefore = strBefore.buffer(); + const size_t newBufSize = strBefore.length() + ((strBufAfter != nullptr) ? std::strlen(strBufAfter) : 0) + 1; + char newBuf[newBufSize]; + + std::strcpy(newBuf, strBufBefore); + std::strcat(newBuf, strBufAfter); + + return d_string(newBuf); +} + +static inline +d_string operator+(const char* const strBufBefore, const d_string& strAfter) noexcept +{ + const char* const strBufAfter = strAfter.buffer(); + const size_t newBufSize = ((strBufBefore != nullptr) ? std::strlen(strBufBefore) : 0) + strAfter.length() + 1; + char newBuf[newBufSize]; + + std::strcpy(newBuf, strBufBefore); + std::strcat(newBuf, strBufAfter); + + return d_string(newBuf); +} + +// ----------------------------------------------------------------------- + +#endif // DISTRHO_STRING_HPP_INCLUDED diff --git a/source/modules/distrho/src/DistrhoPluginChecks.h b/source/modules/distrho/src/DistrhoPluginChecks.h new file mode 100644 index 000000000..3ced26189 --- /dev/null +++ b/source/modules/distrho/src/DistrhoPluginChecks.h @@ -0,0 +1,64 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2014 Filipe Coelho + * + * Permission to use, copy, modify, and/or distribute this software for any purpose with + * or without fee is hereby granted, provided that the above copyright notice and this + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef DISTRHO_PLUGIN_CHECKS_H_INCLUDED +#define DISTRHO_PLUGIN_CHECKS_H_INCLUDED + +#include "DistrhoPluginInfo.h" + +#ifndef DISTRHO_PLUGIN_NAME +# error DISTRHO_PLUGIN_NAME undefined! +#endif + +#ifndef DISTRHO_PLUGIN_HAS_UI +# error DISTRHO_PLUGIN_HAS_UI undefined! +#endif + +#ifndef DISTRHO_PLUGIN_IS_SYNTH +# error DISTRHO_PLUGIN_IS_SYNTH undefined! +#endif + +#ifndef DISTRHO_PLUGIN_NUM_INPUTS +# error DISTRHO_PLUGIN_NUM_INPUTS undefined! +#endif + +#ifndef DISTRHO_PLUGIN_NUM_OUTPUTS +# error DISTRHO_PLUGIN_NUM_OUTPUTS undefined! +#endif + +#ifndef DISTRHO_PLUGIN_WANT_LATENCY +# error DISTRHO_PLUGIN_WANT_LATENCY undefined! +#endif + +#ifndef DISTRHO_PLUGIN_WANT_PROGRAMS +# error DISTRHO_PLUGIN_WANT_PROGRAMS undefined! +#endif + +#ifndef DISTRHO_PLUGIN_WANT_STATE +# error DISTRHO_PLUGIN_WANT_STATE undefined! +#endif + +#ifndef DISTRHO_PLUGIN_WANT_TIMEPOS +# error DISTRHO_PLUGIN_WANT_TIMEPOS undefined! +#endif + +#ifndef DISTRHO_PLUGIN_WANT_DIRECT_ACCESS +# define DISTRHO_PLUGIN_WANT_DIRECT_ACCESS 0 +#endif + +#define DISTRHO_UI_URI DISTRHO_PLUGIN_URI "#UI" + +#endif // DISTRHO_PLUGIN_CHECKS_H_INCLUDED