| @@ -21,6 +21,12 @@ | |||
| #include <cmath> | |||
| #ifdef PROPER_CPP11_SUPPORT | |||
| # include <cstdint> | |||
| #else | |||
| # include <stdint.h> | |||
| #endif | |||
| #ifndef M_PI | |||
| # define M_PI 3.14159265358979323846 | |||
| #endif | |||
| @@ -19,7 +19,6 @@ | |||
| #include "src/DistrhoDefines.h" | |||
| #include <cassert> | |||
| #include <cstdarg> | |||
| #include <cstdio> | |||
| #include <cstdlib> | |||
| @@ -59,7 +58,7 @@ inline float | |||
| // misc functions | |||
| static inline | |||
| long d_cconst(int a, int b, int c, int d) noexcept | |||
| long d_cconst(const int a, const int b, const int c, const int d) noexcept | |||
| { | |||
| return (a << 24) | (b << 16) | (c << 8) | (d << 0); | |||
| } | |||
| @@ -71,622 +70,97 @@ long d_cconst(int a, int b, int c, int d) noexcept | |||
| # define d_debug(...) | |||
| #else | |||
| static inline | |||
| void d_debug(const char* const fmt, ...) | |||
| void d_debug(const char* const fmt, ...) noexcept | |||
| { | |||
| ::va_list args; | |||
| ::va_start(args, fmt); | |||
| std::fprintf(stdout, "\x1b[30;1m"); | |||
| std::vfprintf(stdout, fmt, args); | |||
| std::fprintf(stdout, "\x1b[0m\n"); | |||
| ::va_end(args); | |||
| try { | |||
| ::va_list args; | |||
| ::va_start(args, fmt); | |||
| std::fprintf(stdout, "\x1b[30;1m"); | |||
| std::vfprintf(stdout, fmt, args); | |||
| std::fprintf(stdout, "\x1b[0m\n"); | |||
| ::va_end(args); | |||
| } catch (...) {} | |||
| } | |||
| #endif | |||
| static inline | |||
| void d_stdout(const char* const fmt, ...) | |||
| void d_stdout(const char* const fmt, ...) noexcept | |||
| { | |||
| ::va_list args; | |||
| ::va_start(args, fmt); | |||
| std::vfprintf(stdout, fmt, args); | |||
| std::fprintf(stdout, "\n"); | |||
| ::va_end(args); | |||
| try { | |||
| ::va_list args; | |||
| ::va_start(args, fmt); | |||
| std::vfprintf(stdout, fmt, args); | |||
| std::fprintf(stdout, "\n"); | |||
| ::va_end(args); | |||
| } catch (...) {} | |||
| } | |||
| static inline | |||
| void d_stderr(const char* const fmt, ...) | |||
| void d_stderr(const char* const fmt, ...) noexcept | |||
| { | |||
| ::va_list args; | |||
| ::va_start(args, fmt); | |||
| std::vfprintf(stderr, fmt, args); | |||
| std::fprintf(stderr, "\n"); | |||
| ::va_end(args); | |||
| try { | |||
| ::va_list args; | |||
| ::va_start(args, fmt); | |||
| std::vfprintf(stderr, fmt, args); | |||
| std::fprintf(stderr, "\n"); | |||
| ::va_end(args); | |||
| } catch (...) {} | |||
| } | |||
| static inline | |||
| void d_stderr2(const char* const fmt, ...) | |||
| void d_stderr2(const char* const fmt, ...) noexcept | |||
| { | |||
| ::va_list args; | |||
| ::va_start(args, fmt); | |||
| std::fprintf(stderr, "\x1b[31m"); | |||
| std::vfprintf(stderr, fmt, args); | |||
| std::fprintf(stderr, "\x1b[0m\n"); | |||
| ::va_end(args); | |||
| try { | |||
| ::va_list args; | |||
| ::va_start(args, fmt); | |||
| std::fprintf(stderr, "\x1b[31m"); | |||
| std::vfprintf(stderr, fmt, args); | |||
| std::fprintf(stderr, "\x1b[0m\n"); | |||
| ::va_end(args); | |||
| } catch (...) {} | |||
| } | |||
| static inline | |||
| void d_safe_assert(const char* const assertion, const char* const file, const int line) | |||
| void d_safe_assert(const char* const assertion, const char* const file, const int line) noexcept | |||
| { | |||
| d_stderr2("assertion failure: \"%s\" in file %s, line %i", assertion, file, line); | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| // d_*sleep | |||
| static inline | |||
| void d_sleep(unsigned int secs) | |||
| { | |||
| #ifdef DISTRHO_OS_WINDOWS | |||
| ::Sleep(secs * 1000); | |||
| #else | |||
| ::sleep(secs); | |||
| #endif | |||
| } | |||
| static inline | |||
| void d_msleep(unsigned int msecs) | |||
| void d_safe_exception(const char* const exception, const char* const file, const int line) noexcept | |||
| { | |||
| #ifdef DISTRHO_OS_WINDOWS | |||
| ::Sleep(msecs); | |||
| #else | |||
| ::usleep(msecs * 1000); | |||
| #endif | |||
| d_stderr2("exception caught: \"%s\" in file %s, line %i", exception, file, line); | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| // d_string class | |||
| // d_*sleep | |||
| class d_string | |||
| static inline | |||
| void d_sleep(const uint secs) | |||
| { | |||
| public: | |||
| // ------------------------------------------------------------------- | |||
| // constructors (no explicit conversions allowed) | |||
| /* | |||
| * Empty string. | |||
| */ | |||
| explicit d_string() | |||
| { | |||
| _init(); | |||
| _dup(nullptr); | |||
| } | |||
| /* | |||
| * Simple character. | |||
| */ | |||
| explicit d_string(const char c) | |||
| { | |||
| char ch[2]; | |||
| ch[0] = c; | |||
| ch[1] = '\0'; | |||
| _init(); | |||
| _dup(ch); | |||
| } | |||
| /* | |||
| * Simple char string. | |||
| */ | |||
| explicit d_string(char* const strBuf) | |||
| { | |||
| _init(); | |||
| _dup(strBuf); | |||
| } | |||
| /* | |||
| * Simple const char string. | |||
| */ | |||
| explicit d_string(const char* const strBuf) | |||
| { | |||
| _init(); | |||
| _dup(strBuf); | |||
| } | |||
| /* | |||
| * Integer. | |||
| */ | |||
| explicit d_string(const int value) | |||
| { | |||
| char strBuf[0xff+1]; | |||
| std::memset(strBuf, 0, (0xff+1)*sizeof(char)); | |||
| std::snprintf(strBuf, 0xff, "%d", value); | |||
| _init(); | |||
| _dup(strBuf); | |||
| } | |||
| /* | |||
| * Unsigned integer, possibly in hexadecimal. | |||
| */ | |||
| explicit d_string(const unsigned int value, const bool hexadecimal = false) | |||
| { | |||
| char strBuf[0xff+1]; | |||
| std::memset(strBuf, 0, (0xff+1)*sizeof(char)); | |||
| std::snprintf(strBuf, 0xff, hexadecimal ? "0x%x" : "%u", value); | |||
| _init(); | |||
| _dup(strBuf); | |||
| } | |||
| /* | |||
| * Long integer. | |||
| */ | |||
| explicit d_string(const long int value) | |||
| { | |||
| char strBuf[0xff+1]; | |||
| std::memset(strBuf, 0, (0xff+1)*sizeof(char)); | |||
| std::snprintf(strBuf, 0xff, "%ld", value); | |||
| _init(); | |||
| _dup(strBuf); | |||
| } | |||
| /* | |||
| * Long unsigned integer, possibly hexadecimal. | |||
| */ | |||
| explicit d_string(const unsigned long int value, const bool hexadecimal = false) | |||
| { | |||
| char strBuf[0xff+1]; | |||
| std::memset(strBuf, 0, (0xff+1)*sizeof(char)); | |||
| std::snprintf(strBuf, 0xff, hexadecimal ? "0x%lx" : "%lu", value); | |||
| _init(); | |||
| _dup(strBuf); | |||
| } | |||
| DISTRHO_SAFE_ASSERT_RETURN(secs > 0,); | |||
| /* | |||
| * Single-precision floating point number. | |||
| */ | |||
| explicit d_string(const float value) | |||
| { | |||
| char strBuf[0xff+1]; | |||
| std::memset(strBuf, 0, (0xff+1)*sizeof(char)); | |||
| std::snprintf(strBuf, 0xff, "%f", value); | |||
| _init(); | |||
| _dup(strBuf); | |||
| } | |||
| /* | |||
| * Double-precision floating point number. | |||
| */ | |||
| explicit d_string(const double value) | |||
| { | |||
| char strBuf[0xff+1]; | |||
| std::memset(strBuf, 0, (0xff+1)*sizeof(char)); | |||
| std::snprintf(strBuf, 0xff, "%g", value); | |||
| _init(); | |||
| _dup(strBuf); | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| // non-explicit constructor | |||
| /* | |||
| * Create string from another string. | |||
| */ | |||
| d_string(const d_string& str) | |||
| { | |||
| _init(); | |||
| _dup(str.fBuffer); | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| // destructor | |||
| /* | |||
| * Destructor. | |||
| */ | |||
| ~d_string() | |||
| { | |||
| assert(fBuffer != nullptr); | |||
| delete[] fBuffer; | |||
| fBuffer = nullptr; | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| // 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 | |||
| { | |||
| if (strBuf == nullptr) | |||
| return false; | |||
| if (ignoreCase) | |||
| { | |||
| #ifdef __USE_GNU | |||
| return (strcasestr(fBuffer, strBuf) != nullptr); | |||
| try { | |||
| #ifdef DISTRHO_OS_WIN | |||
| ::Sleep(secs * 1000); | |||
| #else | |||
| d_string tmp1(fBuffer), tmp2(strBuf); | |||
| tmp1.toLower(); | |||
| tmp2.toLower(); | |||
| return (std::strstr((const char*)tmp1, (const char*)tmp2) != nullptr); | |||
| ::sleep(secs); | |||
| #endif | |||
| } | |||
| return (std::strstr(fBuffer, strBuf) != nullptr); | |||
| } | |||
| /* | |||
| * Overloaded function. | |||
| */ | |||
| bool contains(const d_string& str, const bool ignoreCase = false) const | |||
| { | |||
| return contains(str.fBuffer, ignoreCase); | |||
| } | |||
| /* | |||
| * Check if character at 'pos' is a digit. | |||
| */ | |||
| bool isDigit(const size_t pos) const noexcept | |||
| { | |||
| if (pos >= fBufferLen) | |||
| return false; | |||
| return (fBuffer[pos] >= '0' && fBuffer[pos] <= '9'); | |||
| } | |||
| /* | |||
| * Check if the string starts with the character 'c'. | |||
| */ | |||
| bool startsWith(const char c) const | |||
| { | |||
| if (c == '\0') | |||
| return false; | |||
| return (fBufferLen > 0 && fBuffer[0] == c); | |||
| } | |||
| /* | |||
| * Check if the string starts with the string 'prefix'. | |||
| */ | |||
| bool startsWith(const char* const prefix) const | |||
| { | |||
| if (prefix == nullptr) | |||
| return false; | |||
| const size_t prefixLen(std::strlen(prefix)); | |||
| if (fBufferLen < prefixLen) | |||
| return false; | |||
| return (std::strncmp(fBuffer + (fBufferLen-prefixLen), prefix, prefixLen) == 0); | |||
| } | |||
| /* | |||
| * Check if the string ends with the character 'c'. | |||
| */ | |||
| bool endsWith(const char c) const | |||
| { | |||
| if (c == '\0') | |||
| return false; | |||
| return (fBufferLen > 0 && fBuffer[fBufferLen] == c); | |||
| } | |||
| /* | |||
| * Check if the string ends with the string 'suffix'. | |||
| */ | |||
| bool endsWith(const char* const suffix) const | |||
| { | |||
| if (suffix == nullptr) | |||
| return false; | |||
| const size_t suffixLen(std::strlen(suffix)); | |||
| if (fBufferLen < suffixLen) | |||
| return false; | |||
| return (std::strncmp(fBuffer + (fBufferLen-suffixLen), suffix, suffixLen) == 0); | |||
| } | |||
| /* | |||
| * 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 | |||
| { | |||
| if (before == '\0' || after == '\0') | |||
| return; | |||
| for (size_t i=0; i < fBufferLen; ++i) | |||
| { | |||
| if (fBuffer[i] == before) | |||
| fBuffer[i] = after; | |||
| else if (fBuffer[i] == '\0') | |||
| break; | |||
| } | |||
| } | |||
| /* | |||
| * 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] += 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] -= kCharDiff; | |||
| } | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| // public operators | |||
| operator const char*() const noexcept | |||
| { | |||
| return fBuffer; | |||
| } | |||
| char& operator[](const size_t pos) const noexcept | |||
| { | |||
| return fBuffer[pos]; | |||
| } | |||
| bool operator==(const char* const strBuf) const | |||
| { | |||
| return (strBuf != nullptr && std::strcmp(fBuffer, strBuf) == 0); | |||
| } | |||
| bool operator==(const d_string& str) const | |||
| { | |||
| return operator==(str.fBuffer); | |||
| } | |||
| bool operator!=(const char* const strBuf) const | |||
| { | |||
| return !operator==(strBuf); | |||
| } | |||
| bool operator!=(const d_string& str) const | |||
| { | |||
| return !operator==(str.fBuffer); | |||
| } | |||
| d_string& operator=(const char* const strBuf) | |||
| { | |||
| _dup(strBuf); | |||
| return *this; | |||
| } | |||
| d_string& operator=(const d_string& str) | |||
| { | |||
| return operator=(str.fBuffer); | |||
| } | |||
| d_string& operator+=(const char* const strBuf) | |||
| { | |||
| 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) | |||
| { | |||
| return operator+=(str.fBuffer); | |||
| } | |||
| d_string operator+(const char* const strBuf) | |||
| { | |||
| 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) | |||
| { | |||
| return operator+(str.fBuffer); | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| private: | |||
| char* fBuffer; // the actual string buffer | |||
| size_t fBufferLen; // string length | |||
| bool fFirstInit; // true when first initiated | |||
| /* | |||
| * Shared init function. | |||
| * Called on all constructors. | |||
| */ | |||
| void _init() noexcept | |||
| { | |||
| fBuffer = nullptr; | |||
| fBufferLen = 0; | |||
| fFirstInit = true; | |||
| } | |||
| /* | |||
| * Helper function. | |||
| * 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 | |||
| * - If 'strBuf' is null 'size' must be 0 | |||
| */ | |||
| void _dup(const char* const strBuf, const size_t size = 0) | |||
| { | |||
| if (strBuf != nullptr) | |||
| { | |||
| // don't recreate string if contents match | |||
| if (fFirstInit || std::strcmp(fBuffer, strBuf) != 0) | |||
| { | |||
| if (! fFirstInit) | |||
| { | |||
| assert(fBuffer != nullptr); | |||
| delete[] fBuffer; | |||
| } | |||
| fBufferLen = (size > 0) ? size : std::strlen(strBuf); | |||
| fBuffer = new char[fBufferLen+1]; | |||
| std::strcpy(fBuffer, strBuf); | |||
| fBuffer[fBufferLen] = '\0'; | |||
| fFirstInit = false; | |||
| } | |||
| } | |||
| else | |||
| { | |||
| assert(size == 0); | |||
| // don't recreate null string | |||
| if (fFirstInit || fBufferLen != 0) | |||
| { | |||
| if (! fFirstInit) | |||
| { | |||
| assert(fBuffer != nullptr); | |||
| delete[] fBuffer; | |||
| } | |||
| fBufferLen = 0; | |||
| fBuffer = new char[1]; | |||
| fBuffer[0] = '\0'; | |||
| fFirstInit = false; | |||
| } | |||
| } | |||
| } | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| static inline | |||
| d_string operator+(const d_string& strBefore, const char* const strBufAfter) | |||
| { | |||
| const char* const strBufBefore = (const char*)strBefore; | |||
| 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); | |||
| } DISTRHO_SAFE_EXCEPTION("carla_sleep"); | |||
| } | |||
| static inline | |||
| d_string operator+(const char* const strBufBefore, const d_string& strAfter) | |||
| void d_msleep(const uint msecs) | |||
| { | |||
| const char* const strBufAfter = (const char*)strAfter; | |||
| const size_t newBufSize = ((strBufBefore != nullptr) ? std::strlen(strBufBefore) : 0) + strAfter.length() + 1; | |||
| char newBuf[newBufSize]; | |||
| DISTRHO_SAFE_ASSERT_RETURN(msecs > 0,); | |||
| std::strcpy(newBuf, strBufBefore); | |||
| std::strcat(newBuf, strBufAfter); | |||
| return d_string(newBuf); | |||
| try { | |||
| #ifdef DISTRHO_OS_WIN | |||
| ::Sleep(msecs); | |||
| #else | |||
| ::usleep(msecs * 1000); | |||
| #endif | |||
| } DISTRHO_SAFE_EXCEPTION("carla_msleep"); | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| @@ -0,0 +1,746 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2014 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * 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 "../DistrhoUtils.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<size_t>(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<char>(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<char>(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 | |||
| @@ -108,6 +108,59 @@ | |||
| # define nullptr (0) | |||
| #endif | |||
| /* Define DISTRHO_SAFE_ASSERT* */ | |||
| #define DISTRHO_SAFE_ASSERT(cond) if (cond) pass(); else d_safe_assert(#cond, __FILE__, __LINE__); | |||
| #define DISTRHO_SAFE_ASSERT_BREAK(cond) if (cond) pass(); else { d_safe_assert(#cond, __FILE__, __LINE__); break; } | |||
| #define DISTRHO_SAFE_ASSERT_CONTINUE(cond) if (cond) pass(); else { d_safe_assert(#cond, __FILE__, __LINE__); continue; } | |||
| #define DISTRHO_SAFE_ASSERT_RETURN(cond, ret) if (cond) pass(); else { d_safe_assert(#cond, __FILE__, __LINE__); return ret; } | |||
| /* Define DISTRHO_SAFE_EXCEPTION */ | |||
| #define DISTRHO_SAFE_EXCEPTION(msg) catch(...) { d_safe_exception(msg, __FILE__, __LINE__); } | |||
| #define DISTRHO_SAFE_EXCEPTION_BREAK(msg) catch(...) { d_safe_exception(msg, __FILE__, __LINE__); break; } | |||
| #define DISTRHO_SAFE_EXCEPTION_CONTINUE(msg) catch(...) { d_safe_exception(msg, __FILE__, __LINE__); continue; } | |||
| #define DISTRHO_SAFE_EXCEPTION_RETURN(msg, ret) catch(...) { d_safe_exception(msg, __FILE__, __LINE__); return ret; } | |||
| /* Define DISTRHO_DECLARE_NON_COPY_CLASS */ | |||
| #ifdef DISTRHO_PROPER_CPP11_SUPPORT | |||
| # define DISTRHO_DECLARE_NON_COPY_CLASS(ClassName) \ | |||
| private: \ | |||
| ClassName(ClassName&) = delete; \ | |||
| ClassName(const ClassName&) = delete; \ | |||
| ClassName& operator=(ClassName&) = delete; \ | |||
| ClassName& operator=(const ClassName&) = delete; | |||
| #else | |||
| # define DISTRHO_DECLARE_NON_COPY_CLASS(ClassName) \ | |||
| private: \ | |||
| ClassName(ClassName&); \ | |||
| ClassName(const ClassName&); \ | |||
| ClassName& operator=(ClassName&); \ | |||
| ClassName& operator=(const ClassName&); | |||
| #endif | |||
| /* Define DISTRHO_DECLARE_NON_COPY_STRUCT */ | |||
| #ifdef DISTRHO_PROPER_CPP11_SUPPORT | |||
| # define DISTRHO_DECLARE_NON_COPY_STRUCT(StructName) \ | |||
| StructName(StructName&) = delete; \ | |||
| StructName(const StructName&) = delete; \ | |||
| StructName& operator=(StructName&) = delete; \ | |||
| StructName& operator=(const StructName&) = delete; | |||
| #else | |||
| # define DISTRHO_DECLARE_NON_COPY_STRUCT(StructName) | |||
| #endif | |||
| /* Define DISTRHO_PREVENT_HEAP_ALLOCATION */ | |||
| #ifdef DISTRHO_PROPER_CPP11_SUPPORT | |||
| # define DISTRHO_PREVENT_HEAP_ALLOCATION \ | |||
| private: \ | |||
| static void* operator new(size_t) = delete; \ | |||
| static void operator delete(void*) = delete; | |||
| #else | |||
| # define DISTRHO_PREVENT_HEAP_ALLOCATION \ | |||
| private: \ | |||
| static void* operator new(size_t); \ | |||
| static void operator delete(void*); | |||
| #endif | |||
| /* Define namespace */ | |||
| #ifndef DISTRHO_NO_NAMESPACE | |||
| # ifndef DISTRHO_NAMESPACE | |||
| @@ -124,4 +177,10 @@ | |||
| #define DISTRHO_UI_URI DISTRHO_PLUGIN_URI "#UI" | |||
| /* Useful typedefs */ | |||
| typedef unsigned char uchar; | |||
| typedef unsigned long int ulong; | |||
| typedef unsigned short int ushort; | |||
| typedef unsigned int uint; | |||
| #endif // DISTRHO_DEFINES_H_INCLUDED | |||