@@ -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 |