348 lines
No EOL
8.4 KiB
C++
348 lines
No EOL
8.4 KiB
C++
#pragma once
|
|
#include <string>
|
|
#include <string.h>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <cmath>
|
|
#include <vector>
|
|
#include <stdarg.h>
|
|
#include <optional>
|
|
#include <mutex>
|
|
#include <cmath>
|
|
#include <atomic>
|
|
std::string PadZeros(std::string input, size_t required_length);
|
|
uint8_t TimeToComponentCount(double time_code);
|
|
std::string TimeToString(double time_code, uint8_t min_components = 1);
|
|
std::string get_prefs_path();
|
|
inline const char *vcformat(const char *format, va_list args) {
|
|
va_list args_copy;
|
|
va_copy(args_copy, args);
|
|
char *buf;
|
|
size_t buflen = 0;
|
|
buflen = vsnprintf(NULL, 0, format, args_copy) + 1;
|
|
va_end(args_copy);
|
|
buf = (char*)malloc(buflen);
|
|
if (buf == NULL) {
|
|
return NULL;
|
|
}
|
|
memset(buf, 0, buflen);
|
|
buflen = vsnprintf(buf, buflen, format, args) + 1;
|
|
return buf;
|
|
}
|
|
inline const char *cformat(const char *format, ...) {
|
|
va_list args;
|
|
va_start(args, format);
|
|
const char *output = vcformat(format, args);
|
|
va_end(args);
|
|
return output;
|
|
}
|
|
inline std::string to_string_with_decimals(double value, unsigned decimals) {
|
|
std::string num_text;
|
|
if (value == 0) {
|
|
num_text = "0";
|
|
} else {
|
|
size_t buflen = snprintf(NULL, 0, "%f", value);
|
|
buflen++;
|
|
char *buf = (char*)malloc(buflen);
|
|
memset(buf, 0, buflen);
|
|
snprintf(buf, buflen - 1, "%f", value);
|
|
num_text = std::string(buf);
|
|
free(buf);
|
|
}
|
|
int found = num_text.find(".");
|
|
if (found == -1) {
|
|
found = num_text.size();
|
|
num_text.append(".0");
|
|
}
|
|
if (num_text[0] == '.') {
|
|
num_text = "0" + num_text;
|
|
found++;
|
|
}
|
|
if (decimals == 0) {
|
|
num_text = num_text.substr(0, found);
|
|
return num_text;
|
|
}
|
|
int chars_after_decimal = num_text.size() - found - 1;
|
|
if (chars_after_decimal > decimals) {
|
|
num_text = num_text.substr(0, found + decimals + 1);
|
|
} else {
|
|
for (int i = chars_after_decimal; i < decimals; i++) {
|
|
num_text.push_back('0');
|
|
}
|
|
}
|
|
return num_text;
|
|
}
|
|
inline size_t combine_hashes(std::initializer_list<size_t> hashes) {
|
|
std::string values = "^";
|
|
values += hashes.size();
|
|
values += "@";
|
|
for (auto value : values) {
|
|
values += value;
|
|
values += ";";
|
|
}
|
|
return std::hash<std::string>()(values);
|
|
}
|
|
inline bool is_zeroes(void *ptr, size_t len) {
|
|
uint8_t *ptr8 = (uint8_t*)ptr;
|
|
for (size_t i = 0; i < len; i++) {
|
|
if (ptr8[i] != 0) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
int launch(std::vector<std::string> args);
|
|
#ifdef MIN
|
|
#undef MIN
|
|
#endif
|
|
#define MIN(x, y) ((x < y) ? x : y)
|
|
#ifdef MAX
|
|
#undef MAX
|
|
#endif
|
|
#define MAX(x, y) ((x > y) ? x : y)
|
|
#ifdef ABS
|
|
#undef ABS
|
|
#endif
|
|
#define ABS(x) ((x < 0) ? -x : x)
|
|
template<class T>
|
|
class Lock;
|
|
template<class T>
|
|
class LockAccessor {
|
|
friend class Lock<T>;
|
|
Lock<T> *lock;
|
|
bool locked = true;
|
|
LockAccessor(Lock<T> *lock) {
|
|
this->lock = lock;
|
|
}
|
|
public:
|
|
/// @brief Gets the value of the lock.
|
|
/// @returns The value of the lock
|
|
T get() {
|
|
if (has_value()) {
|
|
return *lock->value;
|
|
} else {
|
|
throw std::exception();
|
|
}
|
|
}
|
|
/// @brief Sets the value of the lock, deinitializing the previous value in the process.
|
|
/// @param value The new value of the lock
|
|
/// @param owned Set to true to have the lock "own" the value. If true, the lock will deinitialize the value.
|
|
void set(T value, bool owned = false) {
|
|
lock->clear_value();
|
|
lock->value = new T(value);
|
|
lock->owned = owned;
|
|
}
|
|
T &operator *() {
|
|
if (has_value()) {
|
|
return *lock->value;
|
|
} else {
|
|
throw std::exception();
|
|
}
|
|
}
|
|
T *operator->() {
|
|
if (has_value()) {
|
|
return lock->value;
|
|
} else {
|
|
throw std::exception();
|
|
}
|
|
}
|
|
void clear() {
|
|
lock->clear_value();
|
|
}
|
|
bool has_value() {
|
|
return lock->has_value();
|
|
}
|
|
void unlock() {
|
|
if (locked) {
|
|
lock->mutex.unlock();
|
|
locked = false;
|
|
}
|
|
}
|
|
~LockAccessor() {
|
|
unlock();
|
|
}
|
|
};
|
|
template<class T>
|
|
class LockAccessor<T*> {
|
|
friend class Lock<T*>;
|
|
Lock<T*> *lock;
|
|
std::atomic_bool locked = true;
|
|
LockAccessor(Lock<T*> *lock) {
|
|
this->lock = lock;
|
|
}
|
|
public:
|
|
T &operator *() {
|
|
if (has_value()) {
|
|
return **lock->value;
|
|
} else {
|
|
throw std::exception();
|
|
}
|
|
}
|
|
/// @brief Gets the value of the lock.
|
|
/// @returns The value of the lock
|
|
T *get() {
|
|
if (has_value()) {
|
|
return *lock->value;
|
|
} else {
|
|
throw std::exception();
|
|
}
|
|
}
|
|
/// @brief Sets the value of the lock, deinitializing the previous value in the process.
|
|
/// @param value The new value of the lock
|
|
/// @param owned Set to true to have the lock "own" the value. If true, the lock will deinitialize the value.
|
|
void set(T *value, bool owned = false) {
|
|
lock->clear_value();
|
|
lock->value = new T*(value);
|
|
lock->owned = owned;
|
|
}
|
|
T *operator->() {
|
|
if (has_value()) {
|
|
return *lock->value;
|
|
} else{
|
|
throw std::exception();
|
|
}
|
|
}
|
|
void clear() {
|
|
lock->clear_value();
|
|
}
|
|
void unlock() {
|
|
if (locked.exchange(false)) {
|
|
lock->mutex->unlock();
|
|
}
|
|
}
|
|
bool has_value() {
|
|
return lock->has_value();
|
|
}
|
|
~LockAccessor() {
|
|
unlock();
|
|
}
|
|
};
|
|
template<class T>
|
|
class Lock {
|
|
friend class LockAccessor<T>;
|
|
T *value;
|
|
std::recursive_mutex *mutex;
|
|
bool owned = false;
|
|
void clear_value() {
|
|
if (owned && this->value != nullptr) {
|
|
if constexpr (std::is_pointer_v<T>) {
|
|
delete *this->value;
|
|
}
|
|
delete this->value;
|
|
}
|
|
this->value = nullptr;
|
|
}
|
|
public:
|
|
bool has_value() {
|
|
return this->value != nullptr;
|
|
}
|
|
T &get_unsafe() {
|
|
if (has_value()) {
|
|
return *value;
|
|
} else {
|
|
throw std::exception();
|
|
}
|
|
}
|
|
LockAccessor<T> lock() {
|
|
mutex->lock();
|
|
return LockAccessor(this);
|
|
}
|
|
std::optional<LockAccessor<T>> try_lock(){
|
|
if (mutex->try_lock()) {
|
|
return LockAccessor(this);
|
|
} else {
|
|
return {};
|
|
}
|
|
}
|
|
/// @brief Constructs a lock on a value. Is owner is true, the value will be owned by the lock.
|
|
/// @param value The value to use with the lock
|
|
/// @param owner Set to true to have the lock delete the value on deinitialization
|
|
Lock(T value, bool owned = false)
|
|
: owned(owned)
|
|
{
|
|
this->value = new T(value);
|
|
mutex = new std::recursive_mutex();
|
|
}
|
|
Lock()
|
|
: value(nullptr)
|
|
, owned(false)
|
|
{
|
|
mutex = new std::recursive_mutex();
|
|
}
|
|
Lock<T> &operator=(Lock<T> lock) = delete;
|
|
~Lock() {
|
|
mutex->lock();
|
|
clear_value();
|
|
mutex->unlock();
|
|
delete this->mutex;
|
|
}
|
|
};
|
|
|
|
class DynPtr {
|
|
public:
|
|
void *ptr;
|
|
private:
|
|
size_t ptr_len;
|
|
size_t adjust_size(size_t len) {
|
|
static const size_t chunk = 1024;
|
|
size_t value = len;
|
|
value /= chunk;
|
|
if (len % chunk != 0) {
|
|
value += 1;
|
|
}
|
|
value *= chunk;
|
|
return value;
|
|
}
|
|
public:
|
|
DynPtr() {
|
|
this->ptr = NULL;
|
|
this->ptr_len = 0;
|
|
}
|
|
void resize(size_t new_len) {
|
|
new_len = adjust_size(new_len);
|
|
if (this->ptr == NULL) {
|
|
this->ptr = malloc(new_len);
|
|
this->ptr_len = new_len;
|
|
} else if (this->ptr_len != new_len) {
|
|
this->ptr = realloc(this->ptr, new_len);
|
|
this->ptr_len = new_len;
|
|
}
|
|
}
|
|
/// @brief Creates a DynPtr object and resizes it.
|
|
/// @param len The minimum length of the pointer.
|
|
DynPtr(size_t len) : DynPtr() {
|
|
resize(len);
|
|
}
|
|
/// @brief Gets the pointer, but does not reallocate it.
|
|
/// @param T The type of pointer to return. May be void to return void*.
|
|
/// @returns The pointer, owned by the DynPtr object.
|
|
template<class T>
|
|
T *get() {
|
|
if constexpr (std::is_same_v<T, void>) {
|
|
return ptr;
|
|
} else {
|
|
return (T*)ptr;
|
|
}
|
|
}
|
|
/// @brief Reallocates the pointer, and returns it.
|
|
/// @param len The amount of bytes for the pointer
|
|
/// @param T The type of pointer to return. May be void to return void*
|
|
/// @returns The reallocated pointer, owned by the DynPtr object.
|
|
template<class T>
|
|
T *get_byte_sized(size_t len) {
|
|
resize(len);
|
|
return get<T>();
|
|
}
|
|
/// @brief Reallocates the pointer, and returns it.
|
|
/// @param len The amount of items the pointer should make room for. For void pointers, this is actually the amount of bytes.
|
|
/// @param T The type of pointer to return, and to use in the sizeof call when not void.
|
|
/// @returns The reallocated pointer, owned by the DynPtr object.
|
|
template<class T>
|
|
T *get_item_sized(size_t len) {
|
|
if constexpr (std::is_same_v<T, void>) {
|
|
return get_byte_sized<T>(len);
|
|
} else {
|
|
return get_byte_sized<T>(len * sizeof(T));
|
|
}
|
|
}
|
|
}; |