looper/util.hpp

348 lines
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));
}
}
};