Add all the files

This commit is contained in:
Exzap 2022-08-22 22:21:23 +02:00
parent e3db07a16a
commit d60742f52b
1445 changed files with 430238 additions and 0 deletions

View file

@ -0,0 +1,52 @@
#pragma once
#include <mutex>
#include <memory>
template<typename T>
class SingletonClass
{
public:
static T* getInstance()
{
static T instance;
return &instance;
}
protected:
SingletonClass() = default;
};
template<typename T>
class SingletonRef
{
public:
/*static std::shared_ptr<T> getInstance() C++20 only
{
static std::atomic<std::weak_ptr<T>> s_instance;
std::shared_ptr<T> result;
s_instance.compare_exchange_weak(result, std::make_shared<T>());
return result;
}*/
static std::shared_ptr<T> getInstance()
{
std::scoped_lock lock(s_mutex);
auto result = s_instance.lock();
if(!result)
{
result = std::make_shared<T>();
s_instance = result;
}
return result;
}
protected:
SingletonRef() = default;
private:
static inline std::weak_ptr<T> s_instance;
static inline std::mutex s_mutex;
};

View file

@ -0,0 +1,105 @@
#pragma once
#include <mutex>
#include <condition_variable>
#include <queue>
template <typename T>
class ConcurrentQueue
{
public:
ConcurrentQueue() = default;
ConcurrentQueue(const ConcurrentQueue&) = delete;
ConcurrentQueue& operator=(const ConcurrentQueue&) = delete;
size_t push(const T& item)
{
std::unique_lock<std::mutex> mlock(m_mutex);
m_queue.push(item);
const size_t result = m_queue.size();
mlock.unlock();
m_condVar.notify_one();
return result;
}
template<typename ... Args>
size_t push(Args&& ... args)
{
std::unique_lock<std::mutex> mlock(m_mutex);
m_queue.emplace(std::forward<Args>(args)...);
const size_t result = m_queue.size();
mlock.unlock();
m_condVar.notify_one();
return result;
}
T peek()
{
std::unique_lock<std::mutex> mlock(m_mutex);
while (m_queue.empty())
{
m_condVar.wait(mlock);
}
return m_queue.front();
}
bool peek2(T& item)
{
std::unique_lock<std::mutex> mlock(m_mutex);
if (m_queue.empty())
return false;
item = m_queue.front();
m_queue.pop();
return true;
}
T pop()
{
std::unique_lock<std::mutex> mlock(m_mutex);
while (m_queue.empty())
{
m_condVar.wait(mlock);
}
auto val = m_queue.front();
m_queue.pop();
return val;
}
void pop(T& item)
{
std::unique_lock<std::mutex> mlock(m_mutex);
while (m_queue.empty())
{
m_condVar.wait(mlock);
}
item = m_queue.front();
m_queue.pop();
}
void clear()
{
std::unique_lock<std::mutex> mlock(m_mutex);
while (!m_queue.empty())
m_queue.pop();
}
size_t size()
{
std::unique_lock<std::mutex> mlock(m_mutex);
return m_queue.size();
}
bool empty()
{
std::unique_lock<std::mutex> mlock(m_mutex);
return m_queue.empty();
}
private:
std::mutex m_mutex;
std::condition_variable m_condVar;
std::queue<T> m_queue;
};

View file

@ -0,0 +1,35 @@
#pragma once
// https://ideone.com/k0H8Ei
#include <iostream>
#include <map>
#include <boost/iterator/transform_iterator.hpp>
template <typename T, typename F>
struct map_adaptor
{
map_adaptor(const T& t, const F& f) : _t(t), _f(f) {}
map_adaptor(map_adaptor& a) = delete;
map_adaptor(map_adaptor&& a) = default;
[[nodiscard]] auto begin() { return boost::make_transform_iterator(_t.begin(), _f); }
[[nodiscard]] auto end() { return boost::make_transform_iterator(_t.end(), _f); }
[[nodiscard]] auto cbegin() const { return boost::make_transform_iterator(_t.cbegin(), _f); }
[[nodiscard]] auto cend() const { return boost::make_transform_iterator(_t.cend(), _f); }
protected:
const T& _t;
F _f;
};
template <typename T, typename F>
auto get_map_adaptor(const T& t, const F& f) { return map_adaptor<T, F>(t, f); }
template <typename T>
auto get_keys(const T& t) { return get_map_adaptor(t, [](const auto& p) { return p.first; }); }
template <typename T>
auto get_values(const T& t) { return get_map_adaptor(t, [](const auto& p) { return p.second; }); }

View file

@ -0,0 +1,128 @@
#pragma once
template<typename T>
class MemoryPool
{
static_assert(sizeof(T) >= sizeof(void*)); // object must be large enough to store a single pointer
public:
MemoryPool(int allocationGranularity)
: m_numObjectsAllocated(0)
{
m_allocationGranularity = allocationGranularity;
m_nextFreeObject = nullptr;
}
template <typename... Ts>
T* allocObj(Ts&&... args)
{
if (m_nextFreeObject)
{
T* allocatedObj = m_nextFreeObject;
m_nextFreeObject = *(T**)allocatedObj;
new (allocatedObj) T(std::forward<Ts>(args)...);
return allocatedObj;
}
// enlarge pool
increasePoolSize();
T* allocatedObj = m_nextFreeObject;
m_nextFreeObject = *(T**)allocatedObj;
new (allocatedObj) T(std::forward<Ts>(args)...);
return allocatedObj;
}
void freeObj(T* obj)
{
obj->~T();
pushElementOnFreeStack(obj);
}
private:
void pushElementOnFreeStack(T* obj)
{
*(T**)obj = m_nextFreeObject;
m_nextFreeObject = obj;
}
void increasePoolSize()
{
T* newElements = static_cast<T*>(::operator new(m_allocationGranularity * sizeof(T), std::nothrow));
m_numObjectsAllocated += m_allocationGranularity;
for (int i = 0; i < m_allocationGranularity; i++)
{
pushElementOnFreeStack(newElements);
newElements++;
}
}
private:
T* m_nextFreeObject;
int m_allocationGranularity;
int m_numObjectsAllocated;
};
// this memory pool calls the default constructor when the internal pool is allocated
// no constructor/destructor will be called on acquire/release
template<typename T>
class MemoryPoolPermanentObjects
{
struct internalObject_t
{
T v;
internalObject_t* next;
};
public:
MemoryPoolPermanentObjects(int allocationGranularity)
: m_numObjectsAllocated(0)
{
m_allocationGranularity = allocationGranularity;
m_nextFreeObject = nullptr;
}
template <typename... Ts>
T* acquireObj(Ts&&... args)
{
if (m_nextFreeObject)
{
internalObject_t* allocatedObject = m_nextFreeObject;
m_nextFreeObject = allocatedObject->next;
return &allocatedObject->v;
}
// enlarge pool
increasePoolSize();
internalObject_t* allocatedObject = m_nextFreeObject;
m_nextFreeObject = allocatedObject->next;
return &allocatedObject->v;
}
void releaseObj(T* obj)
{
internalObject_t* internalObj = (internalObject_t*)((uint8*)obj - offsetof(internalObject_t, v));
pushElementOnFreeStack(internalObj);
}
private:
void pushElementOnFreeStack(internalObject_t* obj)
{
obj->next = m_nextFreeObject;
m_nextFreeObject = obj;
}
void increasePoolSize()
{
internalObject_t* newElements = static_cast<internalObject_t*>(::operator new(m_allocationGranularity * sizeof(internalObject_t), std::nothrow));
m_numObjectsAllocated += m_allocationGranularity;
for (int i = 0; i < m_allocationGranularity; i++)
{
new (&newElements->v) T();
pushElementOnFreeStack(newElements);
newElements++;
}
}
private:
internalObject_t* m_nextFreeObject;
int m_allocationGranularity;
int m_numObjectsAllocated;
};

View file

@ -0,0 +1,171 @@
#pragma once
#include <mutex>
#include <condition_variable>
class Semaphore
{
public:
void notify()
{
std::lock_guard lock(m_mutex);
++m_count;
m_condition.notify_one();
}
void wait()
{
std::unique_lock lock(m_mutex);
while (m_count == 0)
{
m_condition.wait(lock);
}
--m_count;
}
bool try_wait()
{
std::lock_guard lock(m_mutex);
if (m_count == 0)
return false;
--m_count;
return true;
}
void reset()
{
std::lock_guard lock(m_mutex);
m_count = 0;
}
private:
std::mutex m_mutex;
std::condition_variable m_condition;
uint64 m_count = 0;
};
class CounterSemaphore
{
public:
void reset()
{
std::lock_guard lock(m_mutex);
m_count = 0;
}
void increment()
{
std::lock_guard lock(m_mutex);
++m_count;
if (m_count == 1)
m_condition.notify_all();
}
void decrement()
{
std::lock_guard lock(m_mutex);
--m_count;
cemu_assert_debug(m_count >= 0);
if (m_count == 0)
m_condition.notify_all();
}
// decrement only if non-zero
// otherwise wait
void decrementWithWait()
{
std::unique_lock lock(m_mutex);
while (m_count == 0)
m_condition.wait(lock);
m_count--;
}
// decrement only if non-zero
// otherwise wait
// may wake up spuriously
bool decrementWithWaitAndTimeout(uint32 ms)
{
std::unique_lock lock(m_mutex);
if (m_count == 0)
{
m_condition.wait_for(lock, std::chrono::milliseconds(ms));
}
if (m_count == 0)
return false;
m_count--;
return true;
}
void waitUntilZero()
{
std::unique_lock lock(m_mutex);
while (m_count != 0)
m_condition.wait(lock);
}
void waitUntilNonZero()
{
std::unique_lock lock(m_mutex);
while (m_count == 0)
m_condition.wait(lock);
}
bool isZero() const
{
return m_count == 0;
}
private:
std::mutex m_mutex;
std::condition_variable m_condition;
sint64 m_count = 0;
};
template<typename T>
class StateSemaphore
{
public:
StateSemaphore(T initialState) : m_state(initialState) {};
T getValue()
{
std::unique_lock lock(m_mutex);
return m_state;
}
bool hasState(T state)
{
std::unique_lock lock(m_mutex);
return m_state == state;
}
void setValue(T newState)
{
std::unique_lock lock(m_mutex);
m_state = newState;
m_condition.notify_all();
}
void setValue(T newState, T expectedValue)
{
std::unique_lock lock(m_mutex);
while (m_state != expectedValue)
m_condition.wait(lock);
m_state = newState;
m_condition.notify_all();
}
void waitUntilValue(T state)
{
std::unique_lock lock(m_mutex);
while (m_state != state)
m_condition.wait(lock);
}
private:
std::mutex m_mutex;
std::condition_variable m_condition;
T m_state;
};

View file

@ -0,0 +1,353 @@
#pragma once
class MemStreamReader
{
public:
MemStreamReader(const uint8* data, sint32 size) : m_data(data), m_size(size)
{
m_cursorPos = 0;
}
template<typename T> T readBE();
template<typename T> T readLE();
template<> uint8 readBE()
{
if (!reserveReadLength(sizeof(uint8)))
return 0;
uint8 v = m_data[m_cursorPos];
m_cursorPos += sizeof(uint8);
return v;
}
template<> uint16 readBE()
{
if (!reserveReadLength(sizeof(uint16)))
return 0;
const uint8* p = m_data + m_cursorPos;
uint16 v;
std::memcpy(&v, p, sizeof(v));
v = _BE(v);
m_cursorPos += sizeof(uint16);
return v;
}
template<> uint32 readBE()
{
if (!reserveReadLength(sizeof(uint32)))
return 0;
const uint8* p = m_data + m_cursorPos;
uint32 v;
std::memcpy(&v, p, sizeof(v));
v = _BE(v);
m_cursorPos += sizeof(uint32);
return v;
}
template<> uint64 readBE()
{
if (!reserveReadLength(sizeof(uint64)))
return 0;
const uint8* p = m_data + m_cursorPos;
uint64 v;
std::memcpy(&v, p, sizeof(v));
v = _BE(v);
m_cursorPos += sizeof(uint64);
return v;
}
template<> std::string readBE()
{
std::string s;
uint32 stringSize = readBE<uint32>();
if (hasError())
return s;
if (stringSize >= (32 * 1024 * 1024))
{
// out of bounds read or suspiciously large string
m_hasError = true;
return std::string();
}
s.resize(stringSize);
readData(s.data(), stringSize);
return s;
}
template<> uint8 readLE()
{
return readBE<uint8>();
}
template<> uint32 readLE()
{
if (!reserveReadLength(sizeof(uint32)))
return 0;
const uint8* p = m_data + m_cursorPos;
uint32 v;
std::memcpy(&v, p, sizeof(v));
v = _LE(v);
m_cursorPos += sizeof(uint32);
return v;
}
template<> uint64 readLE()
{
if (!reserveReadLength(sizeof(uint64)))
return 0;
const uint8* p = m_data + m_cursorPos;
uint64 v;
std::memcpy(&v, p, sizeof(v));
v = _LE(v);
m_cursorPos += sizeof(uint64);
return v;
}
template<typename T>
std::vector<T> readPODVector()
{
uint32 numElements = readBE<uint32>();
if (hasError())
{
return std::vector<T>();
}
std::vector<T> v;
v.reserve(numElements);
v.resize(numElements);
readData(v.data(), v.size() * sizeof(T));
return v;
}
// read string terminated by newline character (or end of stream)
// will also trim off any carriage return
std::string_view readLine()
{
size_t length = 0;
if (m_cursorPos >= m_size)
{
m_hasError = true;
return std::basic_string_view((const char*)nullptr, 0);
}
// end of line is determined by '\n'
const char* lineStrBegin = (const char*)(m_data + m_cursorPos);
const char* lineStrEnd = nullptr;
while (m_cursorPos < m_size)
{
if (m_data[m_cursorPos] == '\n')
{
lineStrEnd = (const char*)(m_data + m_cursorPos);
m_cursorPos++; // skip the newline character
break;
}
m_cursorPos++;
}
if(lineStrEnd == nullptr)
lineStrEnd = (const char*)(m_data + m_cursorPos);
// truncate any '\r' at the beginning and end
while (lineStrBegin < lineStrEnd)
{
if (lineStrBegin[0] != '\r')
break;
lineStrBegin++;
}
while (lineStrEnd > lineStrBegin)
{
if (lineStrEnd[-1] != '\r')
break;
lineStrEnd--;
}
length = (lineStrEnd - lineStrBegin);
return std::basic_string_view((const char*)lineStrBegin, length);
}
bool readData(void* ptr, size_t size)
{
if (m_cursorPos + size > m_size)
{
m_cursorPos = m_size;
m_hasError = true;
return false;
}
memcpy(ptr, m_data + m_cursorPos, size);
m_cursorPos += (sint32)size;
return true;
}
std::span<uint8> readDataNoCopy(size_t size)
{
if (m_cursorPos + size > m_size)
{
m_cursorPos = m_size;
m_hasError = true;
return std::span<uint8>();
}
auto r = std::span<uint8>((uint8*)m_data + m_cursorPos, size);
m_cursorPos += (sint32)size;
return r;
}
// returns true if any of the reads was out of bounds
bool hasError() const
{
return m_hasError;
}
bool isEndOfStream() const
{
return m_cursorPos == m_size;
}
private:
bool reserveReadLength(size_t length)
{
if (m_cursorPos + length > m_size)
{
m_cursorPos = m_size;
m_hasError = true;
return false;
}
return true;
}
void skipCRLF()
{
while (m_cursorPos < m_size)
{
if (m_data[m_cursorPos] != '\r' && m_data[m_cursorPos] != '\n')
break;
m_cursorPos++;
}
}
const uint8* m_data;
sint32 m_size;
sint32 m_cursorPos;
bool m_hasError{ false };
};
class MemStreamWriter
{
public:
MemStreamWriter(size_t reservedSize)
{
if (reservedSize > 0)
m_buffer.reserve(reservedSize);
else
m_buffer.reserve(128);
}
void writeData(const void* ptr, size_t size)
{
m_buffer.resize(m_buffer.size() + size);
uint8* p = m_buffer.data() + m_buffer.size() - size;
memcpy(p, ptr, size);
}
template<typename T> void writeBE(const T& v);
template<>
void writeBE<uint64>(const uint64& v)
{
m_buffer.resize(m_buffer.size() + 8);
uint8* p = m_buffer.data() + m_buffer.size() - 8;
uint64 tmp = _BE(v);
std::memcpy(p, &tmp, sizeof(tmp));
}
template<>
void writeBE<uint32>(const uint32& v)
{
m_buffer.resize(m_buffer.size() + 4);
uint8* p = m_buffer.data() + m_buffer.size() - 4;
uint32 tmp = _BE(v);
std::memcpy(p, &tmp, sizeof(tmp));
}
template<>
void writeBE<uint16>(const uint16& v)
{
m_buffer.resize(m_buffer.size() + 2);
uint8* p = m_buffer.data() + m_buffer.size() - 2;
uint16 tmp = _BE(v);
std::memcpy(p, &tmp, sizeof(tmp));
}
template<>
void writeBE<uint8>(const uint8& v)
{
m_buffer.emplace_back(v);
}
template<>
void writeBE<std::string>(const std::string& v)
{
writeBE<uint32>((uint32)v.size());
writeData(v.data(), v.size());
}
template<typename T> void writeLE(const T& v);
template<>
void writeLE<uint64>(const uint64& v)
{
m_buffer.resize(m_buffer.size() + 8);
uint8* p = m_buffer.data() + m_buffer.size() - 8;
uint64 tmp = _LE(v);
std::memcpy(p, &tmp, sizeof(tmp));
}
template<>
void writeLE<uint32>(const uint32& v)
{
m_buffer.resize(m_buffer.size() + 4);
uint8* p = m_buffer.data() + m_buffer.size() - 4;
uint32 tmp = _LE(v);
std::memcpy(p, &tmp, sizeof(tmp));
}
template<typename T>
void writePODVector(const std::vector<T>& v)
{
writeBE<uint32>(v.size());
writeData(v.data(), v.size() * sizeof(T));
}
// get result buffer without copy
// resets internal state
void getResultAndReset(std::vector<uint8>& data)
{
std::swap(m_buffer, data);
m_buffer.clear();
}
std::span<uint8> getResult()
{
return std::span<uint8>(m_buffer.data(), m_buffer.size());
}
private:
std::vector<uint8> m_buffer;
};
class SerializerHelper
{
public:
bool serialize(std::vector<uint8>& data)
{
MemStreamWriter streamWriter(0);
bool r = serializeImpl(streamWriter);
if (!r)
return false;
streamWriter.getResultAndReset(data);
return true;
}
bool deserialize(std::vector<uint8>& data)
{
MemStreamReader memStreamReader(data.data(), (sint32)data.size());
return deserializeImpl(memStreamReader);
}
protected:
virtual bool serializeImpl(MemStreamWriter& streamWriter) = 0;
virtual bool deserializeImpl(MemStreamReader& streamReader) = 0;
};

View file

@ -0,0 +1,17 @@
#pragma once
template<typename TType>
class Singleton
{
protected:
Singleton() = default;
Singleton(const Singleton&) = delete;
Singleton(Singleton&&) noexcept = delete;
public:
static TType& instance() noexcept
{
static TType s_instance;
return s_instance;
}
};

View file

@ -0,0 +1,86 @@
#pragma once
class StringBuf
{
public:
StringBuf(uint32 bufferSize)
{
this->str = (uint8*)malloc(bufferSize + 4);
this->allocated = true;
this->length = 0;
this->limit = bufferSize;
}
~StringBuf()
{
if (this->allocated)
free(this->str);
}
template<typename TFmt, typename ... TArgs>
void addFmt(const TFmt& format, TArgs&&... args)
{
auto r = fmt::vformat_to_n((char*)(this->str + this->length), (size_t)(this->limit - this->length), fmt::to_string_view(format), fmt::make_args_checked<TArgs...>(format, args...));
this->length += (uint32)r.size;
}
void add(const char* appendedStr)
{
const char* outputStart = (char*)(this->str + this->length);
char* output = (char*)outputStart;
const char* outputEnd = (char*)(this->str + this->limit - 1);
while (output < outputEnd)
{
char c = *appendedStr;
if (c == '\0')
break;
*output = c;
appendedStr++;
output++;
}
this->length += (uint32)(output - outputStart);
*output = '\0';
}
void add(std::string_view appendedStr)
{
size_t remainingLen = this->limit - this->length;
size_t copyLen = appendedStr.size();
if (remainingLen > copyLen)
copyLen = remainingLen;
char* outputStart = (char*)(this->str + this->length);
std::copy(appendedStr.data(), appendedStr.data() + copyLen, outputStart);
outputStart[copyLen] = '\0';
}
void reset()
{
length = 0;
}
uint32 getLen() const
{
return length;
}
const char* c_str() const
{
str[length] = '\0';
return (const char*)str;
}
void shrink_to_fit()
{
if (!this->allocated)
return;
uint32 newLimit = this->length;
this->str = (uint8*)realloc(this->str, newLimit + 4);
this->limit = newLimit;
}
private:
uint8* str;
uint32 length; /* in bytes */
uint32 limit; /* in bytes */
bool allocated;
};

View file

@ -0,0 +1,112 @@
#pragma once
#include "boost/nowide/convert.hpp"
#include <charconv>
namespace StringHelpers
{
// convert Wii U big-endian wchar_t string to utf8 string
static std::string ToUtf8(const uint16be* ptr, size_t maxLength)
{
std::wstringstream result;
while (*ptr != 0 && maxLength > 0)
{
auto c = (uint16)*ptr;
result << static_cast<wchar_t>(c);
ptr++;
maxLength--;
}
return boost::nowide::narrow(result.str());
}
static std::string ToUtf8(std::span<uint16be> input)
{
return ToUtf8(input.data(), input.size());
}
// convert utf8 string to Wii U big-endian wchar_t string
static std::basic_string<uint16be> FromUtf8(std::string_view str)
{
std::basic_string<uint16be> tmpStr;
std::wstring w = boost::nowide::widen(str.data(), str.size());
for (auto& c : w)
tmpStr.push_back((uint16)c);
return tmpStr;
}
static sint32 ToInt(const std::string_view& input, sint32 defaultValue = 0)
{
sint32 value = defaultValue;
if (input.size() >= 2 && (input[0] == '0' && (input[1] == 'x' || input[1] == 'X')))
{
// hex number
const std::from_chars_result result = std::from_chars(input.data() + 2, input.data() + input.size(), value, 16);
if (result.ec == std::errc::invalid_argument || result.ec == std::errc::result_out_of_range)
return defaultValue;
}
else
{
// decimal value
const std::from_chars_result result = std::from_chars(input.data(), input.data() + input.size(), value);
if (result.ec == std::errc::invalid_argument || result.ec == std::errc::result_out_of_range)
return defaultValue;
}
return value;
}
static sint64 ToInt64(const std::string_view& input, sint64 defaultValue = 0)
{
sint64 value = defaultValue;
if (input.size() >= 2 && (input[0] == '0' && (input[1] == 'x' || input[1] == 'X')))
{
// hex number
const std::from_chars_result result = std::from_chars(input.data() + 2, input.data() + input.size(), value, 16);
if (result.ec == std::errc::invalid_argument || result.ec == std::errc::result_out_of_range)
return defaultValue;
}
else
{
// decimal value
const std::from_chars_result result = std::from_chars(input.data(), input.data() + input.size(), value);
if (result.ec == std::errc::invalid_argument || result.ec == std::errc::result_out_of_range)
return defaultValue;
}
return value;
}
static size_t ParseHexString(std::string_view input, uint8* output, size_t maxOutputLength)
{
size_t parsedLen = 0;
for (size_t i = 0; i < input.size() - 1; i += 2)
{
if (maxOutputLength <= 0)
break;
uint8 b = 0;
uint8 c = input[i + 0];
// high nibble
if (c >= '0' && c <= '9')
b |= ((c - '0') << 4);
else if (c >= 'a' && c <= 'f')
b |= ((c - 'a' + 10) << 4);
else if (c >= 'A' && c <= 'F')
b |= ((c - 'A' + 10) << 4);
else
break;
// low nibble
c = input[i + 1];
if (c >= '0' && c <= '9')
b |= (c - '0');
else if (c >= 'a' && c <= 'f')
b |= (c - 'a' + 10);
else if (c >= 'A' && c <= 'F')
b |= (c - 'A' + 10);
else
break;
*output = b;
output++;
maxOutputLength--;
parsedLen++;
}
return parsedLen;
}
};

View file

@ -0,0 +1,252 @@
#pragma once
class StringTokenParser
{
public:
StringTokenParser() : m_str(nullptr), m_len(0) {};
StringTokenParser(const char* input, sint32 inputLen) : m_str(input), m_len(inputLen) {};
StringTokenParser(std::string_view str) : m_str(str.data()), m_len((sint32)str.size()) {};
// skip whitespaces at current ptr position
void skipWhitespaces()
{
m_str = _skipWhitespaces(m_str, m_len);
}
// decrease string length as long as there is a whitespace at the end
void trimWhitespaces()
{
while (m_len > 0)
{
const char c = m_str[m_len - 1];
if (c != ' ' && c != '\t')
break;
m_len--;
}
}
bool isEndOfString()
{
return m_len <= 0;
}
sint32 skipToCharacter(const char c)
{
auto str = m_str;
auto len = m_len;
sint32 idx = 0;
while (len > 0)
{
if (*str == c)
{
m_str = str;
m_len = len;
return idx;
}
len--;
str++;
idx++;
}
return -1;
}
bool matchWordI(const char* word)
{
auto str = m_str;
auto length = m_len;
str = _skipWhitespaces(str, length);
for (sint32 i = 0; i <= length; i++)
{
if (word[i] == '\0')
{
m_str = str + i;
m_len = length - i;
return true;
}
if (i == length)
return false;
char c1 = str[i];
char c2 = word[i];
c1 = _toUpperCase(c1);
c2 = _toUpperCase(c2);
if (c1 != c2)
return false;
}
return false;
}
bool compareCharacter(sint32 relativeIndex, const char c)
{
if (relativeIndex >= m_len)
return false;
return m_str[relativeIndex] == c;
}
bool compareCharacterI(sint32 relativeIndex, const char c)
{
if (relativeIndex >= m_len)
return false;
return _toUpperCase(m_str[relativeIndex]) == _toUpperCase(c);
}
void skipCharacters(sint32 count)
{
if (count > m_len)
count = m_len;
m_str += count;
m_len -= count;
}
bool parseU32(uint32& val)
{
auto str = m_str;
auto length = m_len;
str = _skipWhitespaces(str, length);
uint32 value = 0;
sint32 index = 0;
bool isHex = false;
if (length <= 0)
return false;
if (length >= 2 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X'))
{
isHex = true;
index += 2;
}
else if (str[index] == '0')
{
isHex = true;
index++;
}
if (length <= index)
return false;
if (isHex)
{
sint32 firstDigitIndex = index;
for (; index < length; index++)
{
const char c = str[index];
if (c >= '0' && c <= '9')
{
value *= 0x10;
value += (c - '0');
}
else if (c >= 'a' && c <= 'f')
{
value *= 0x10;
value += (c - 'a' + 10);
}
else if (c >= 'A' && c <= 'F')
{
value *= 0x10;
value += (c - 'A' + 10);
}
else
break;
}
if (index == firstDigitIndex)
return false;
m_str = str + index;
m_len = length - index;
}
else
{
sint32 firstDigitIndex = index;
for (; index < length; index++)
{
const char c = str[index];
if (c >= '0' && c <= '9')
{
value *= 10;
value += (c - '0');
}
else
break;
}
if (index == firstDigitIndex)
return false;
m_str = str + index;
m_len = length - index;
}
val = value;
return true;
}
bool parseSymbolName(const char*& symbolStr, sint32& symbolLength)
{
auto str = m_str;
auto length = m_len;
str = _skipWhitespaces(str, length);
// symbols must start with a letter or _
if (length <= 0)
return false;
if (!(str[0] >= 'a' && str[0] <= 'z') &&
!(str[0] >= 'A' && str[0] <= 'Z') &&
!(str[0] == '_'))
return false;
sint32 idx = 1;
while (idx < length)
{
const char c = str[idx];
if (!(c >= 'a' && c <= 'z') &&
!(c >= 'A' && c <= 'Z') &&
!(c >= '0' && c <= '9') &&
!(c == '_') && !(c == '.'))
break;
idx++;
}
symbolStr = str;
symbolLength = idx;
m_str = str + idx;
m_len = length - idx;
return true;
}
const char* getCurrentPtr()
{
return m_str;
}
sint32 getCurrentLen()
{
return m_len;
}
void storeParserState(StringTokenParser* bak)
{
bak->m_str = m_str;
bak->m_len = m_len;
}
void restoreParserState(const StringTokenParser* bak)
{
m_str = bak->m_str;
m_len = bak->m_len;
}
private:
const char* _skipWhitespaces(const char* str, sint32& length)
{
while (length > 0)
{
if (*str != ' ' && *str != '\t')
break;
str++;
length--;
}
return str;
}
char _toUpperCase(const char c)
{
if (c >= 'a' && c <= 'z')
return c + ('A' - 'a');
return c;
}
private:
const char* m_str;
sint32 m_len;
};

View file

@ -0,0 +1,23 @@
#pragma once
#include "util/helpers/helpers.h"
class SystemException : public std::runtime_error
{
public:
SystemException()
: std::runtime_error(GetSystemErrorMessage().c_str()), m_error_code(GetExceptionError())
{}
SystemException(const std::exception& ex)
: std::runtime_error(GetSystemErrorMessage(ex).c_str()), m_error_code(GetExceptionError())
{}
SystemException(const std::error_code& ec)
: std::runtime_error(GetSystemErrorMessage(ec).c_str()), m_error_code(GetExceptionError())
{}
[[nodiscard]] DWORD GetErrorCode() const { return m_error_code; }
private:
DWORD m_error_code;
};

View file

@ -0,0 +1,20 @@
#pragma once
template<typename TCtor, typename TDtor>
class TempState
{
public:
TempState(TCtor ctor, TDtor dtor)
: m_dtor(std::move(dtor))
{
ctor();
}
~TempState()
{
m_dtor();
}
private:
TDtor m_dtor;
};

View file

@ -0,0 +1,15 @@
#pragma once
// expects the enum class (T) to have a value called ENUM_COUNT which is the maximum value + 1
template<typename E, class T>
class enum_array : public std::array<T, static_cast<size_t>(E::ENUM_COUNT)> {
public:
T& operator[] (E e) {
return std::array<T, static_cast<size_t>(E::ENUM_COUNT)>::operator[]((std::size_t)e);
}
const T& operator[] (E e) const {
return std::array<T, static_cast<size_t>(E::ENUM_COUNT)>::operator[]((std::size_t)e);
}
};

View file

@ -0,0 +1,74 @@
#pragma once
#include<array>
template<typename T, uint32 maxElements, bool checkMaxSize = true>
class FixedSizeList
{
public:
std::array<T, maxElements> m_elementArray;
int count = 0;
void add(T n)
{
if (checkMaxSize && count >= maxElements)
return;
m_elementArray[count] = n;
count++;
}
void addUnique(T n)
{
if (checkMaxSize && count >= maxElements)
return;
for (int i = 0; i < count; i++)
{
if (m_elementArray[i] == n)
return;
}
m_elementArray[count] = n;
count++;
}
void remove(T n)
{
for (int i = 0; i < count; i++)
{
if (m_elementArray[i] == n)
{
m_elementArray[i] = m_elementArray[count - 1];
count--;
return;
}
}
}
bool containsAndRemove(T n)
{
for (int i = 0; i < count; i++)
{
if (m_elementArray[i] == n)
{
m_elementArray[i] = m_elementArray[count - 1];
count--;
return true;
}
}
return false;
}
sint32 find(T n)
{
for (int i = 0; i < count; i++)
{
if (m_elementArray[i] == n)
{
return i;
}
}
return -1;
}
private:
};

View file

@ -0,0 +1,38 @@
#pragma once
// minimal but efficient non-recursive spinlock implementation
#include <atomic>
class FSpinlock
{
public:
void acquire()
{
while( true )
{
if (!m_lockBool.exchange(true, std::memory_order_acquire))
break;
while (m_lockBool.load(std::memory_order_relaxed)) _mm_pause();
}
}
bool tryAcquire()
{
return !m_lockBool.exchange(true, std::memory_order_acquire);
}
void release()
{
m_lockBool.store(false, std::memory_order_release);
}
bool isHolding() const
{
return m_lockBool.load(std::memory_order_relaxed);
}
private:
std::atomic<bool> m_lockBool = false;
};

View file

@ -0,0 +1,434 @@
#include "helpers.h"
#include <algorithm>
#include <functional>
#include <cctype>
#include <random>
#include <wx/translation.h>
#include "config/ActiveSettings.h"
#if BOOST_OS_WINDOWS
#include <TlHelp32.h>
#endif
std::string& ltrim(std::string& str, const std::string& chars)
{
str.erase(0, str.find_first_not_of(chars));
return str;
}
std::string& rtrim(std::string& str, const std::string& chars)
{
str.erase(str.find_last_not_of(chars) + 1);
return str;
}
std::string& trim(std::string& str, const std::string& chars)
{
return ltrim(rtrim(str, chars), chars);
}
std::string_view& ltrim(std::string_view& str, const std::string& chars)
{
str.remove_prefix(std::min(str.find_first_not_of(chars), str.size()));
return str;
}
std::string_view& rtrim(std::string_view& str, const std::string& chars)
{
str.remove_suffix(std::max(str.size() - str.find_last_not_of(chars) - 1, (size_t)0));
return str;
}
std::string_view& trim(std::string_view& str, const std::string& chars)
{
return ltrim(rtrim(str, chars), chars);
}
#if BOOST_OS_WINDOWS > 0
std::wstring GetSystemErrorMessageW()
{
return GetSystemErrorMessageW(GetLastError());
}
std::wstring GetSystemErrorMessageW(DWORD error_code)
{
if(error_code == ERROR_SUCCESS)
return {};
LPWSTR lpMsgBuf = nullptr;
FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, error_code, 0, (LPWSTR)&lpMsgBuf, 0, nullptr);
if (lpMsgBuf)
{
std::wstring str = fmt::format(L"{}: {}", _("Error").ToStdWstring(), lpMsgBuf); // TRANSLATE
LocalFree(lpMsgBuf);
return str;
}
return fmt::format(L"{}: {:#x}", _("Error code").ToStdWstring(), error_code);
}
std::string GetSystemErrorMessage(DWORD error_code)
{
if(error_code == ERROR_SUCCESS)
return {};
LPSTR lpMsgBuf = nullptr;
FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, error_code, 0, (LPSTR)&lpMsgBuf, 0, nullptr);
if (lpMsgBuf)
{
std::string str = fmt::format("{}: {}", _("Error").ToStdString(), lpMsgBuf); // TRANSLATE
LocalFree(lpMsgBuf);
return str;
}
return fmt::format("{}: {:#x}", _("Error code").ToStdString(), error_code);
}
std::string GetSystemErrorMessage()
{
return GetSystemErrorMessage(GetLastError());
}
#else
std::string GetSystemErrorMessage()
{
return "";
}
#endif
std::string GetSystemErrorMessage(const std::exception& ex)
{
const std::string msg = GetSystemErrorMessage();
if(msg.empty())
return ex.what();
return fmt::format("{}\n{}",msg, ex.what());
}
std::string GetSystemErrorMessage(const std::error_code& ec)
{
const std::string msg = GetSystemErrorMessage();
if(msg.empty())
return ec.message();
return fmt::format("{}\n{}",msg, ec.message());
}
#if BOOST_OS_WINDOWS > 0
const DWORD MS_VC_EXCEPTION = 0x406D1388;
#pragma pack(push,8)
typedef struct tagTHREADNAME_INFO
{
DWORD dwType; // Must be 0x1000.
LPCSTR szName; // Pointer to name (in user addr space).
DWORD dwThreadID; // Thread ID (-1=caller thread).
DWORD dwFlags; // Reserved for future use, must be zero.
} THREADNAME_INFO;
#pragma pack(pop)
#endif
void SetThreadName(const char* name)
{
#if BOOST_OS_WINDOWS > 0
#ifndef _PUBLIC_RELEASE
THREADNAME_INFO info;
info.dwType = 0x1000;
info.szName = name;
info.dwThreadID = GetCurrentThreadId();
info.dwFlags = 0;
#pragma warning(push)
#pragma warning(disable: 6320 6322)
__try {
RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info);
}
__except (EXCEPTION_EXECUTE_HANDLER) {
}
#pragma warning(pop)
#endif
#else
pthread_setname_np(pthread_self(), name);
#endif
}
#if BOOST_OS_WINDOWS > 0
std::pair<DWORD, DWORD> GetWindowsVersion()
{
using RtlGetVersion_t = LONG(*)(POSVERSIONINFOEXW);
static RtlGetVersion_t pRtlGetVersion = nullptr;
if(!pRtlGetVersion)
pRtlGetVersion = (RtlGetVersion_t)GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlGetVersion");
cemu_assert(pRtlGetVersion);
OSVERSIONINFOEXW version_info{};
pRtlGetVersion(&version_info);
return { version_info.dwMajorVersion, version_info.dwMinorVersion };
}
bool IsWindows81OrGreater()
{
const auto [major, minor] = GetWindowsVersion();
return major > 6 || (major == 6 && minor >= 3);
}
bool IsWindows10OrGreater()
{
const auto [major, minor] = GetWindowsVersion();
return major >= 10;
}
#endif
fs::path GetParentProcess()
{
fs::path result;
#if BOOST_OS_WINDOWS
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if(hSnapshot != INVALID_HANDLE_VALUE)
{
DWORD pid = GetCurrentProcessId();
PROCESSENTRY32 pe{};
pe.dwSize = sizeof(pe);
for(BOOL ret = Process32First(hSnapshot, &pe); ret; ret = Process32Next(hSnapshot, &pe))
{
if(pe.th32ProcessID == pid)
{
HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pe.th32ParentProcessID);
if(hProcess)
{
wchar_t tmp[MAX_PATH];
DWORD size = std::size(tmp);
if (QueryFullProcessImageNameW(hProcess, 0, tmp, &size) && size > 0)
result = tmp;
CloseHandle(hProcess);
}
break;
}
}
CloseHandle(hSnapshot);
}
#else
assert_dbg();
#endif
return result;
}
std::string ltrim_copy(const std::string& s)
{
std::string result = s;
ltrim(result);
return result;
}
std::string rtrim_copy(const std::string& s)
{
std::string result = s;
rtrim(result);
return result;
}
uint32_t GetPhysicalCoreCount()
{
static uint32_t s_core_count = 0;
if (s_core_count != 0)
return s_core_count;
#if BOOST_OS_WINDOWS
auto core_count = std::thread::hardware_concurrency();
// Get physical cores
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION buffer = nullptr;
DWORD returnLength = 0;
GetLogicalProcessorInformation(buffer, &returnLength);
if (returnLength > 0)
{
buffer = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION)malloc(returnLength);
if (GetLogicalProcessorInformation(buffer, &returnLength))
{
uint32_t counter = 0;
for (DWORD i = 0; i < returnLength / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); ++i)
{
if (buffer[i].Relationship == RelationProcessorCore)
++counter;
}
if (counter > 0 && counter < core_count)
core_count = counter;
}
free(buffer);
}
s_core_count = core_count;
return core_count;
#else
return std::thread::hardware_concurrency();
#endif
}
bool TestWriteAccess(const fs::path& p)
{
// must be path and must exist
if (!fs::exists(p) || !fs::is_directory(p))
return false;
// retry 3 times
for (int i = 0; i < 3; ++i)
{
const auto filename = p / fmt::format("_{}.tmp", GenerateRandomString(8));
if (fs::exists(filename))
continue;
std::ofstream file(filename);
if (!file.is_open()) // file couldn't be created
break;
file.close();
std::error_code ec;
fs::remove(filename, ec);
return true;
}
return false;
}
// make path relative to Cemu directory
fs::path MakeRelativePath(const fs::path& path)
{
try
{
const fs::path base = ActiveSettings::GetPath();
return fs::relative(path, base);
}
catch (const std::exception&)
{
return path;
}
}
#ifdef HAS_DIRECTINPUT
bool GUIDFromString(const char* string, GUID& guid)
{
unsigned long p0;
int p1, p2, p3, p4, p5, p6, p7, p8, p9, p10;
const sint32 count = sscanf_s(string, "%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", &p0, &p1, &p2, &p3, &p4, &p5, &p6, &p7, &p8, &p9, &p10);
if (count != 11)
return false;
guid.Data1 = p0;
guid.Data2 = p1;
guid.Data3 = p2;
guid.Data4[0] = p3;
guid.Data4[1] = p4;
guid.Data4[2] = p5;
guid.Data4[3] = p6;
guid.Data4[4] = p7;
guid.Data4[5] = p8;
guid.Data4[6] = p9;
guid.Data4[7] = p10;
return count == 11;
}
std::string StringFromGUID(const GUID& guid)
{
char temp[256];
sprintf(temp, "%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
guid.Data1, guid.Data2, guid.Data3,
guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]);
return std::string(temp);
}
std::wstring WStringFromGUID(const GUID& guid)
{
wchar_t temp[256];
swprintf_s(temp, L"%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
guid.Data1, guid.Data2, guid.Data3,
guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]);
return std::wstring(temp);
}
#endif
std::vector<std::string_view> TokenizeView(std::string_view str, char delimiter)
{
std::vector<std::string_view> result;
size_t last_token_index = 0;
for (auto index = str.find(delimiter); index != std::string_view::npos; index = str.find(delimiter, index + 1))
{
const auto token = str.substr(last_token_index, index - last_token_index);
result.emplace_back(token);
last_token_index = index + 1;
}
try
{
const auto token = str.substr(last_token_index);
result.emplace_back(token);
}
catch (const std::invalid_argument&) {}
return result;
}
std::vector<std::string> Tokenize(std::string_view str, char delimiter)
{
std::vector<std::string> result;
size_t last_token_index = 0;
for (auto index = str.find(delimiter); index != std::string_view::npos; index = str.find(delimiter, index + 1))
{
const auto token = str.substr(last_token_index, index - last_token_index);
result.emplace_back(token);
last_token_index = index + 1;
}
try
{
const auto token = str.substr(last_token_index);
result.emplace_back(token);
}
catch (const std::invalid_argument&) {}
return result;
}
std::string GenerateRandomString(size_t length)
{
const std::string kCharacters{
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"1234567890" };
return GenerateRandomString(length, kCharacters);
}
std::string GenerateRandomString(size_t length, std::string_view characters)
{
assert(!characters.empty());
std::stringstream result;
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<decltype(characters.size())> index_dist(0, characters.size() - 1);
for (uint32_t i = 0; i < length; ++i)
{
result << characters[index_dist(gen)];
}
return result.str();
}

239
src/util/helpers/helpers.h Normal file
View file

@ -0,0 +1,239 @@
#pragma once
#include <charconv>
#include <filesystem>
#include <string_view>
#include "util/math/vector2.h"
#include "util/math/vector3.h"
#ifdef __clang__
#include "Common/linux/fast_float.h"
#endif
template <typename TType>
constexpr auto to_underlying(TType v) noexcept
{
return static_cast<std::underlying_type_t<TType>>(v);
}
// wrapper to allow reverse iteration with range-based loops before C++20
template<typename T>
class reverse_itr {
private:
T& iterable_;
public:
explicit reverse_itr(T& iterable) : iterable_{ iterable } {}
auto begin() const { return std::rbegin(iterable_); }
auto end() const { return std::rend(iterable_); }
};
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
template<typename T>
T deg2rad(T v) { return v * static_cast<T>(M_PI) / static_cast<T>(180); }
template<typename T>
T rad2deg(T v) { return v * static_cast<T>(180) / static_cast<T>(M_PI); }
template<typename T>
Vector3<T> deg2rad(const Vector3<T>& v) { return { deg2rad(v.x), deg2rad(v.y), deg2rad(v.z) }; }
template<typename T>
Vector3<T> rad2deg(const Vector3<T>& v) { return { rad2deg(v.x), rad2deg(v.y), rad2deg(v.z) }; }
template<typename T>
Vector2<T> deg2rad(const Vector2<T>& v) { return { deg2rad(v.x), deg2rad(v.y) }; }
template<typename T>
Vector2<T> rad2deg(const Vector2<T>& v) { return { rad2deg(v.x), rad2deg(v.y) }; }
uint32_t GetPhysicalCoreCount();
// Creates a temporary file to test for write access
bool TestWriteAccess(const fs::path& p);
fs::path MakeRelativePath(const fs::path& path);
#ifdef HAS_DIRECTINPUT
bool GUIDFromString(const char* string, GUID& guid);
std::string StringFromGUID(const GUID& guid);
std::wstring WStringFromGUID(const GUID& guid);
#endif
std::vector<std::string_view> TokenizeView(std::string_view string, char delimiter);
std::vector<std::string> Tokenize(std::string_view string, char delimiter);
std::string ltrim_copy(const std::string& s);
std::string rtrim_copy(const std::string& s);
std::string& ltrim(std::string& str, const std::string& chars = "\t\n\v\f\r ");
std::string& rtrim(std::string& str, const std::string& chars = "\t\n\v\f\r ");
std::string& trim(std::string& str, const std::string& chars = "\t\n\v\f\r ");
std::string_view& ltrim(std::string_view& str, const std::string& chars = "\t\n\v\f\r ");
std::string_view& rtrim(std::string_view& str, const std::string& chars = "\t\n\v\f\r ");
std::string_view& trim(std::string_view& str, const std::string& chars = "\t\n\v\f\r ");
std::string GenerateRandomString(size_t length);
std::string GenerateRandomString(size_t length, std::string_view characters);
std::wstring GetSystemErrorMessageW();
std::wstring GetSystemErrorMessageW(DWORD error_code);
std::string GetSystemErrorMessage();
std::string GetSystemErrorMessage(DWORD error_code);
std::string GetSystemErrorMessage(const std::exception& ex);
std::string GetSystemErrorMessage(const std::error_code& ec);
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...)->overloaded<Ts...>;
template<typename T>
bool equals(T v1, T v2)
{
/*
return std::fabs(x-y) <= std::numeric_limits<T>::epsilon() * std::fabs(x+y) * ulp
// unless the result is subnormal
|| std::fabs(x-y) < std::numeric_limits<T>::min();
*/
if constexpr (std::is_floating_point_v<T>)
return std::abs(v1 - v2) < (T)0.000001;
else if constexpr (std::is_same_v<T, const char*>)
return strcmp(v1, v2) == 0;
else
return v1 == v2;
}
template<typename T>
T ConvertString(std::string_view str, sint32 base)
{
if (str.empty())
return {};
static_assert(std::is_integral_v<T>);
T result;
ltrim(str);
// from_chars cant deal with hex numbers starting with "0x"
if (base == 16)
{
const sint32 index = str[0] == '-' ? 1 : 0;
if (str.size() >= 2 && str[index+0] == '0' && tolower(str[index+1]) == 'x')
str = str.substr(index + 2);
if (std::from_chars(str.data(), str.data() + str.size(), result, base).ec == std::errc())
{
if (index == 1)
{
if constexpr(std::is_unsigned_v<T>)
result = static_cast<T>(-static_cast<std::make_signed_t<T>>(result));
else
result = -result;
}
return result;
}
return {};
}
if(std::from_chars(str.data(), str.data() + str.size(), result, base).ec == std::errc())
return result;
return {};
}
template<typename T>
T ConvertString(std::string_view str)
{
if (str.empty())
return {};
T result;
ltrim(str);
if constexpr (std::is_same_v<T, bool>)
{
return str == "1" || boost::iequals(str, "true");
}
else if constexpr(std::is_floating_point_v<T>)
{
// from_chars can't deal with float conversation starting with "+"
ltrim(str, "+");
#ifdef __clang__
if (fast_float::from_chars(str.data(), str.data() + str.size(), result).ec == std::errc())
return result;
#else
if (std::from_chars(str.data(), str.data() + str.size(), result).ec == std::errc())
return result;
#endif
return {};
}
else if constexpr(std::is_enum_v<T>)
{
return (T)ConvertString<std::underlying_type_t<T>>(str);
}
else
{
const sint32 index = str[0] == '-' ? 1 : 0;
if (str.size() >= 2 && str[index + 0] == '0' && tolower(str[index + 1]) == 'x')
result = ConvertString<T>(str, 16);
else
result = ConvertString<T>(str, 10);
}
return result;
}
template <typename T>
constexpr T DegToRad(T deg) { return (T)((double)deg * M_PI / 180); }
template <typename T>
constexpr T RadToDeg(T rad) { return (T)((double)rad * 180 / M_PI); }
template<typename T>
std::string ToString(T value)
{
std::ostringstream str;
str.imbue(std::locale("C"));
str << value;
return str.str();
}
template<typename T>
T FromString(std::string value)
{
std::istringstream str(value);
str.imbue(std::locale("C"));
T tmp;
str >> tmp;
return tmp;
}
template<typename T>
size_t RemoveDuplicatesKeepOrder(std::vector<T>& vec)
{
std::set<T> tmp;
auto new_end = std::remove_if(vec.begin(), vec.end(), [&tmp](const T& value)
{
if (tmp.find(value) != std::end(tmp))
return true;
tmp.insert(value);
return false;
});
vec.erase(new_end, vec.end());
return vec.size();
}
void SetThreadName(const char* name);
inline uint64 MakeU64(uint32 high, uint32 low)
{
return ((uint64)high << 32) | ((uint64)low);
}
// MAJOR; MINOR
std::pair<DWORD, DWORD> GetWindowsVersion();
bool IsWindows81OrGreater();
bool IsWindows10OrGreater();
fs::path GetParentProcess();

View file

@ -0,0 +1,124 @@
#pragma once
#include <mutex>
template<typename T, uint32 elements, typename P = uint32>
class RingBuffer
{
public:
RingBuffer<T, elements, P>();
bool Push(const T& v);
template<class Q = T>
typename std::enable_if< !std::is_array<T>::value, Q >::type
Pop()
{
std::unique_lock<std::mutex> lock(m_mutex);
if (m_readPointer == m_writePointer)
{
return T();
}
const T& tmp = m_data[m_readPointer];
m_readPointer = (m_readPointer + 1) % elements;
return tmp;
}
T& GetSlot();
T& GetSlotAndAdvance();
void Advance();
void Clear();
P GetReadPointer();
P GetWritePointer();
bool HasData();
private:
T m_data[elements];
P m_readPointer;
P m_writePointer;
std::mutex m_mutex;
};
template <typename T, uint32 elements, typename P>
RingBuffer<T, elements, P>::RingBuffer()
: m_readPointer(0), m_writePointer(0)
{
}
template <typename T, uint32 elements, typename P>
bool RingBuffer<T, elements, P>::Push(const T& v)
{
std::unique_lock<std::mutex> lock(m_mutex);
if (m_readPointer == ((m_writePointer + 1) % elements))
{
debugBreakpoint(); // buffer is full
return false;
}
m_data[m_writePointer] = v;
m_writePointer = (m_writePointer + 1) % elements;
return true;
}
template <typename T, uint32 elements, typename P>
T& RingBuffer<T, elements, P>::GetSlot()
{
std::unique_lock<std::mutex> lock(m_mutex);
T& result = m_data[m_writePointer];
m_writePointer = (m_writePointer + 1) % elements;
return result;
}
template <typename T, uint32 elements, typename P>
T& RingBuffer<T, elements, P>::GetSlotAndAdvance()
{
std::unique_lock<std::mutex> lock(m_mutex);
T& result = m_data[m_writePointer];
m_writePointer = (m_writePointer + 1) % elements;
m_readPointer = (m_readPointer + 1) % elements;
return result;
}
template <typename T, uint32 elements, typename P>
void RingBuffer<T, elements, P>::Advance()
{
std::unique_lock<std::mutex> lock(m_mutex);
if (m_readPointer != m_writePointer)
{
m_readPointer = (m_readPointer + 1) % elements;
}
}
template <typename T, uint32 elements, typename P>
void RingBuffer<T, elements, P>::Clear()
{
std::unique_lock<std::mutex> lock(m_mutex);
m_readPointer = 0;
m_writePointer = 0;
}
template <typename T, uint32 elements, typename P>
P RingBuffer<T, elements, P>::GetReadPointer()
{
std::unique_lock<std::mutex> lock(m_mutex);
P tmp = m_readPointer;
return tmp;
}
template <typename T, uint32 elements, typename P>
P RingBuffer<T, elements, P>::GetWritePointer()
{
std::unique_lock<std::mutex> lock(m_mutex);
P tmp = m_writePointer;
return tmp;
}
template <typename T, uint32 elements, typename P>
bool RingBuffer<T, elements, P>::HasData()
{
std::unique_lock<std::mutex> lock(m_mutex);
return m_readPointer != m_writePointer;
}