mirror of
https://github.com/cemu-project/Cemu.git
synced 2025-07-07 23:41:18 +12:00
Add all the files
This commit is contained in:
parent
e3db07a16a
commit
d60742f52b
1445 changed files with 430238 additions and 0 deletions
52
src/util/helpers/ClassWrapper.h
Normal file
52
src/util/helpers/ClassWrapper.h
Normal 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;
|
||||
};
|
105
src/util/helpers/ConcurrentQueue.h
Normal file
105
src/util/helpers/ConcurrentQueue.h
Normal 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;
|
||||
};
|
35
src/util/helpers/MapAdaptor.h
Normal file
35
src/util/helpers/MapAdaptor.h
Normal 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; }); }
|
128
src/util/helpers/MemoryPool.h
Normal file
128
src/util/helpers/MemoryPool.h
Normal 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;
|
||||
};
|
171
src/util/helpers/Semaphore.h
Normal file
171
src/util/helpers/Semaphore.h
Normal 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;
|
||||
};
|
353
src/util/helpers/Serializer.h
Normal file
353
src/util/helpers/Serializer.h
Normal 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;
|
||||
};
|
17
src/util/helpers/Singleton.h
Normal file
17
src/util/helpers/Singleton.h
Normal 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;
|
||||
}
|
||||
};
|
86
src/util/helpers/StringBuf.h
Normal file
86
src/util/helpers/StringBuf.h
Normal 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;
|
||||
};
|
112
src/util/helpers/StringHelpers.h
Normal file
112
src/util/helpers/StringHelpers.h
Normal 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;
|
||||
}
|
||||
};
|
||||
|
252
src/util/helpers/StringParser.h
Normal file
252
src/util/helpers/StringParser.h
Normal 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;
|
||||
};
|
||||
|
23
src/util/helpers/SystemException.h
Normal file
23
src/util/helpers/SystemException.h
Normal 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;
|
||||
};
|
20
src/util/helpers/TempState.h
Normal file
20
src/util/helpers/TempState.h
Normal 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;
|
||||
};
|
15
src/util/helpers/enum_array.hpp
Normal file
15
src/util/helpers/enum_array.hpp
Normal 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);
|
||||
}
|
||||
};
|
74
src/util/helpers/fixedSizeList.h
Normal file
74
src/util/helpers/fixedSizeList.h
Normal 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:
|
||||
};
|
||||
|
38
src/util/helpers/fspinlock.h
Normal file
38
src/util/helpers/fspinlock.h
Normal 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;
|
||||
};
|
434
src/util/helpers/helpers.cpp
Normal file
434
src/util/helpers/helpers.cpp
Normal 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
239
src/util/helpers/helpers.h
Normal 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();
|
124
src/util/helpers/ringbuffer.h
Normal file
124
src/util/helpers/ringbuffer.h
Normal 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;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue