#pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "log.hpp" #include namespace fs = std::filesystem; 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(); std::string hexdump(void *ptr, size_t len); inline std::string hexdump(std::string data) { return hexdump(data.data(), data.length()); } 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 hashes) { std::string values = "^"; values += hashes.size(); values += "@"; for (auto value : values) { values += value; values += ";"; } return std::hash()(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; } inline void replace_all(std::string &input, std::string replace, std::string value) { size_t pos = 0; while (true) { pos = input.find(replace, pos); if (pos >= input.length()) break; input.replace(pos, pos + replace.length(), value); pos += value.length() - replace.length(); } } fs::path get_base_dir(); int launch(std::vector args); int launch_self(std::string process_type, std::vector extra_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 Lock; template class LockAccessor { friend class Lock; Lock *lock; bool locked = true; LockAccessor(Lock *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 LockAccessor { friend class Lock; Lock *lock; std::atomic_bool locked = true; LockAccessor(Lock *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 Lock { friend class LockAccessor; T *value; std::recursive_mutex *mutex; bool owned = false; void clear_value() { if (owned && this->value != nullptr) { if constexpr (std::is_pointer_v) { 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 lock() { mutex->lock(); return LockAccessor(this); } std::optional> 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 &operator=(Lock 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; } assert(this->ptr != NULL); } /// @brief Creates a DynPtr object and resizes it. /// @param len The minimum length of the pointer. DynPtr(size_t len) : DynPtr() { resize(len); } DynPtr(std::string input) : DynPtr(input.length()) { memcpy(this->ptr, input.data(), input.length()); } /// @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 T *get() { assert(ptr != NULL); if constexpr (std::is_same_v) { 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 T *get_byte_sized(size_t len) { resize(len); return get(); } /// @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 T *get_item_sized(size_t len) { if constexpr (std::is_same_v) { return get_byte_sized(len); } else { return get_byte_sized(len * sizeof(T)); } } }; template class Fifo { private: std::vector buffer; size_t head = 0; size_t tail = 0; size_t count = 0; public: Fifo(size_t initial_capacity = 16) : buffer(initial_capacity) {} void resize(size_t new_capacity) { std::vector new_buffer(new_capacity); size_t new_count = std::min(count, new_capacity); for (size_t i = 0; i < new_count; ++i) { new_buffer[i] = buffer[(head + i) % buffer.size()]; } buffer = std::move(new_buffer); head = 0; tail = new_count; count = new_count; } size_t size() const { return count; } size_t capacity() const { return buffer.size(); } void push(const T& value) { if (count == buffer.size()) { resize(buffer.size() * 2); } buffer[tail] = value; tail = (tail + 1) % buffer.size(); ++count; } void push(const T* values, size_t length) { if (count + length > buffer.size()) { resize(std::max(buffer.size() * 2, count + length)); } for (size_t i = 0; i < length; ++i) { buffer[tail] = values[i]; tail = (tail + 1) % buffer.size(); ++count; } } T pop() { if (count == 0) { throw std::out_of_range("FIFO is empty"); } T value = buffer[head]; head = (head + 1) % buffer.size(); --count; return value; } size_t pop(T* output, size_t max_length) { size_t popped = std::min(count, max_length); for (size_t i = 0; i < popped; ++i) { output[i] = buffer[head]; head = (head + 1) % buffer.size(); } count -= popped; return popped; } T* reserve(size_t length) { if (count + length > buffer.size()) { resize(std::max(buffer.size() * 2, count + length)); } T* start = &buffer[tail]; tail = (tail + length) % buffer.size(); count += length; return start; } bool empty() const { return count == 0; } void clear() { count = 0; head = 0; tail = 0; } void shrink() { size_t offs = size() - capacity(); if (offs != 0) { size_t old_head = head; size_t old_tail = tail; size_t new_head = head - offs; size_t new_tail = tail - offs; size_t start = std::min(head, tail); size_t end = std::max(head, tail); size_t len = end - start; for (size_t i = 0; i < len; i++) { size_t j = i + start; buffer[i] = buffer[j]; } head = new_head; tail = new_tail; } buffer.resize(size()); } }; void blocking_write(int fd, const void *buf, const size_t len); void blocking_read(int fd, void *buf, const size_t len); int NotSDL_ConvertAudioSamples(const SDL_AudioSpec *src_spec, const Uint8 *src_data, int src_len, const SDL_AudioSpec *dst_spec, Uint8 **dst_data, int *dst_len); template inline T *resolve_any(google::protobuf::Any value) { T *output = new T(); value.UnpackTo(output); return output; } template inline T resolve_value(google::protobuf::Any value) { throw std::exception(); } template<> inline bool resolve_value(google::protobuf::Any value) { BooleanProperty *property = resolve_any(value); bool output = property->value(); delete property; return output; } template<> inline std::string resolve_value(google::protobuf::Any value) { StringProperty *property = resolve_any(value); std::string output = property->value(); delete property; return output; } template<> inline DynPtr resolve_value(google::protobuf::Any value) { BytesProperty *property = resolve_any(value); std::string output = property->value(); delete property; return DynPtr(output); } template<> inline double resolve_value(google::protobuf::Any value) { DoubleProperty *property = resolve_any(value); double output = property->value(); delete property; return output; } template inline google::protobuf::Any value_to_any(T value) { google::protobuf::Any output; output.PackFrom(value); return output; } template<> inline google::protobuf::Any value_to_any(double value) { DoubleProperty output; output.set_value(value); return value_to_any(output); } template<> inline google::protobuf::Any value_to_any(bool value) { BooleanProperty output; output.set_value(value); return value_to_any(output); } template<> inline google::protobuf::Any value_to_any(std::string value) { StringProperty output; output.set_value(value); return value_to_any(output); } PropertyHint make_hint(double min, double max); Property make_property(PropertyType type, std::string name, PropertyId id, std::optional hint); Property make_property(PropertyType type, std::string name, std::string path, std::optional hint = {}); Property make_property(PropertyType type, std::string name, PropertyId id = PropertyId::BackendSpecific, std::optional path = {}, std::optional hint = {}); class LooperLogScaler { double la; double lb; public: double x0; double x1; void update_min_max(double min, double max); LooperLogScaler(double min, double max); double scale_log(double value); double unscale_log(double value); };