#pragma once #include #include #include #include #include #include #include #include #include #include #include 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 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; } int launch(std::vector 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; } } /// @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 T *get() { 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)); } } };