1
0
mirror of https://github.com/RPCS3/rpcs3.git synced 2024-11-22 10:42:36 +01:00

Partial commit: Utilities

This commit is contained in:
Nekotekina 2016-02-02 00:55:43 +03:00
parent 5fc6f59821
commit 250ce63527
37 changed files with 4783 additions and 4007 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,57 +1,29 @@
#include "stdafx.h" #include "stdafx.h"
#include "AutoPause.h" #include "Config.h"
#include "Utilities/Log.h"
#include "Utilities/File.h"
#include "Emu/System.h" #include "Emu/System.h"
#include "Emu/state.h" #include "AutoPause.h"
using namespace Debug; cfg::bool_entry g_cfg_debug_autopause_syscall(cfg::root.misc, "Auto Pause at System Call");
cfg::bool_entry g_cfg_debug_autopause_func_call(cfg::root.misc, "Auto Pause at Function Call");
std::unique_ptr<AutoPause> g_autopause; debug::autopause& debug::autopause::get_instance()
AutoPause& AutoPause::getInstance(void)
{ {
if (!g_autopause) // Use magic static
static autopause instance;
return instance;
}
// Load Auto Pause Configuration from file "pause.bin"
void debug::autopause::reload(void)
{
auto& instance = get_instance();
instance.m_pause_function.clear();
instance.m_pause_syscall.clear();
// TODO: better format, possibly a config entry
if (fs::file list{ fs::get_config_dir() + "pause.bin" })
{ {
g_autopause.reset(new AutoPause);
}
return *g_autopause;
}
//Still use binary format. Default Setting should be "disable all auto pause".
AutoPause::AutoPause(void)
{
m_pause_function.reserve(16);
m_pause_syscall.reserve(16);
initialized = false;
//Reload(false, false);
Reload();
}
//Notice: I would not allow to write the binary to file in this command.
AutoPause::~AutoPause(void)
{
initialized = false;
m_pause_function.clear();
m_pause_syscall.clear();
m_pause_function_enable = false;
m_pause_syscall_enable = false;
}
//Load Auto Pause Configuration from file "pause.bin"
//This would be able to create in a GUI window.
void AutoPause::Reload(void)
{
if (fs::is_file(fs::get_config_dir() + "pause.bin"))
{
m_pause_function.clear();
m_pause_function.reserve(16);
m_pause_syscall.clear();
m_pause_syscall.reserve(16);
fs::file list(fs::get_config_dir() + "pause.bin");
//System calls ID and Function calls ID are all u32 iirc.
u32 num; u32 num;
size_t fmax = list.size(); size_t fmax = list.size();
size_t fcur = 0; size_t fcur = 0;
@ -64,60 +36,38 @@ void AutoPause::Reload(void)
if (num < 1024) if (num < 1024)
{ {
//Less than 1024 - be regarded as a system call. instance.m_pause_syscall.emplace(num);
//emplace_back may not cause reductant move/copy operation. LOG_WARNING(HLE, "Set autopause at syscall %lld", num);
m_pause_syscall.emplace_back(num);
LOG_WARNING(HLE, "Auto Pause: Find System Call ID 0x%x", num);
} }
else else
{ {
m_pause_function.emplace_back(num); instance.m_pause_function.emplace(num);
LOG_WARNING(HLE, "Auto Pause: Find Function Call ID 0x%x", num); LOG_WARNING(HLE, "Set autopause at function 0x%08x", num);
} }
} }
} }
m_pause_syscall_enable = rpcs3::config.misc.debug.auto_pause_syscall.value();
m_pause_function_enable = rpcs3::config.misc.debug.auto_pause_func_call.value();
initialized = true;
} }
void AutoPause::TryPause(u32 code) bool debug::autopause::pause_syscall(u64 code)
{ {
if (code < 1024) if (g_cfg_debug_autopause_syscall && get_instance().m_pause_syscall.count(code) != 0)
{
//Would first check Enable setting. Then the list length.
if ((!m_pause_syscall_enable)
|| (m_pause_syscall.size() <= 0))
{
return;
}
for (u32 i = 0; i < m_pause_syscall.size(); ++i)
{
if (code == m_pause_syscall[i])
{ {
Emu.Pause(); Emu.Pause();
LOG_ERROR(HLE, "Auto Pause Triggered: System call 0x%x", code); // Used Error LOG_SUCCESS(HLE, "Autopause triggered at syscall %lld", code);
} return true;
}
}
else
{
//Well similiar.. Seperate the list caused by possible setting difference.
if ((!m_pause_function_enable)
|| (m_pause_function.size() <= 0))
{
return;
} }
for (u32 i = 0; i < m_pause_function.size(); ++i) return false;
{ }
if (code == m_pause_function[i])
{ bool debug::autopause::pause_function(u32 code)
Emu.Pause(); {
LOG_ERROR(HLE, "Auto Pause Triggered: Function call 0x%x", code); // Used Error if (g_cfg_debug_autopause_func_call && get_instance().m_pause_function.count(code) != 0)
} {
} Emu.Pause();
} LOG_SUCCESS(HLE, "Autopause triggered at function 0x%08x", code);
return true;
}
return false;
} }

View File

@ -1,24 +1,20 @@
#pragma once #pragma once
//Regarded as a Debugger Enchantment // Regarded as a Debugger Enchantment
namespace Debug { namespace debug
//To store the pause function/call id, and let those pause there. {
//Would be with a GUI to configure those. // To store the pause function/call id, and let those pause there.
struct AutoPause // Would be with a GUI to configure those.
class autopause
{ {
std::vector<u32> m_pause_syscall; std::unordered_set<u64> m_pause_syscall;
std::vector<u32> m_pause_function; std::unordered_set<u32> m_pause_function;
bool initialized;
bool m_pause_syscall_enable;
bool m_pause_function_enable;
AutoPause(); static autopause& get_instance();
~AutoPause();
public: public:
static AutoPause& getInstance(void);
void Reload(void); static void reload();
static bool pause_syscall(u64 code);
void TryPause(u32 code); static bool pause_function(u32 code);
}; };
} }

View File

@ -1,16 +1,14 @@
#pragma once #pragma once
#ifdef _MSC_VER #include "types.h"
#include <intrin.h> #include "Platform.h"
#else
#include <x86intrin.h>
#endif
#define IS_LE_MACHINE // only draft union alignas(16) v128
union v128
{ {
template<typename T, std::size_t N, std::size_t M> class masked_array_t // array type accessed as (index ^ M) char _bytes[16];
template<typename T, std::size_t N, std::size_t M>
struct masked_array_t // array type accessed as (index ^ M)
{ {
T m_data[N]; T m_data[N];
@ -24,24 +22,11 @@ union v128
{ {
return m_data[index ^ M]; return m_data[index ^ M];
} }
T& at(std::size_t index)
{
return (index ^ M) < N ? m_data[index ^ M] : throw std::out_of_range(__FUNCTION__);
}
const T& at(std::size_t index) const
{
return (index ^ M) < N ? m_data[index ^ M] : throw std::out_of_range(__FUNCTION__);
}
}; };
#ifdef IS_LE_MACHINE #if IS_LE_MACHINE == 1
template<typename T, std::size_t N = 16 / sizeof(T)> using normal_array_t = masked_array_t<T, N, 0>; template<typename T, std::size_t N = 16 / sizeof(T)> using normal_array_t = masked_array_t<T, N, 0>;
template<typename T, std::size_t N = 16 / sizeof(T)> using reversed_array_t = masked_array_t<T, N, N - 1>; template<typename T, std::size_t N = 16 / sizeof(T)> using reversed_array_t = masked_array_t<T, N, N - 1>;
#else
template<typename T, std::size_t N = 16 / sizeof(T)> using normal_array_t = masked_array_t<T, N, N - 1>;
template<typename T, std::size_t N = 16 / sizeof(T)> using reversed_array_t = masked_array_t<T, N, 0>;
#endif #endif
normal_array_t<u64> _u64; normal_array_t<u64> _u64;
@ -73,7 +58,7 @@ union v128
__m128i vi; __m128i vi;
__m128d vd; __m128d vd;
class bit_array_128 struct bit_array_128
{ {
u64 m_data[2]; u64 m_data[2];
@ -125,36 +110,18 @@ union v128
// Index 0 returns the MSB and index 127 returns the LSB // Index 0 returns the MSB and index 127 returns the LSB
bit_element operator [](u32 index) bit_element operator [](u32 index)
{ {
#ifdef IS_LE_MACHINE #if IS_LE_MACHINE == 1
return bit_element(m_data[1 - (index >> 6)], 0x8000000000000000ull >> (index & 0x3F)); return bit_element(m_data[1 - (index >> 6)], 0x8000000000000000ull >> (index & 0x3F));
#else
return bit_element(m_data[index >> 6], 0x8000000000000000ull >> (index & 0x3F));
#endif #endif
} }
// Index 0 returns the MSB and index 127 returns the LSB // Index 0 returns the MSB and index 127 returns the LSB
bool operator [](u32 index) const bool operator [](u32 index) const
{ {
#ifdef IS_LE_MACHINE #if IS_LE_MACHINE == 1
return (m_data[1 - (index >> 6)] & (0x8000000000000000ull >> (index & 0x3F))) != 0; return (m_data[1 - (index >> 6)] & (0x8000000000000000ull >> (index & 0x3F))) != 0;
#else
return (m_data[index >> 6] & (0x8000000000000000ull >> (index & 0x3F))) != 0;
#endif #endif
} }
bit_element at(u32 index)
{
if (index >= 128) throw std::out_of_range(__FUNCTION__);
return operator[](index);
}
bool at(u32 index) const
{
if (index >= 128) throw std::out_of_range(__FUNCTION__);
return operator[](index);
}
} }
_bit; _bit;
@ -320,16 +287,6 @@ union v128
return _u64[0] != right._u64[0] || _u64[1] != right._u64[1]; return _u64[0] != right._u64[0] || _u64[1] != right._u64[1];
} }
bool is_any_1() const // check if any bit is 1
{
return _u64[0] || _u64[1];
}
bool is_any_0() const // check if any bit is 0
{
return ~_u64[0] || ~_u64[1];
}
// result = (~left) & (right) // result = (~left) & (right)
static inline v128 andnot(const v128& left, const v128& right) static inline v128 andnot(const v128& left, const v128& right)
{ {
@ -345,15 +302,8 @@ union v128
std::string to_hex() const; std::string to_hex() const;
std::string to_xyzw() const; std::string to_xyzw() const;
static inline v128 byteswap(const v128 val)
{
return fromV(_mm_shuffle_epi8(val.vi, _mm_set_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)));
}
}; };
CHECK_SIZE_ALIGN(v128, 16, 16);
inline v128 operator |(const v128& left, const v128& right) inline v128 operator |(const v128& left, const v128& right)
{ {
return v128::fromV(_mm_or_si128(left.vi, right.vi)); return v128::fromV(_mm_or_si128(left.vi, right.vi));
@ -374,21 +324,21 @@ inline v128 operator ~(const v128& other)
return v128::from64(~other._u64[0], ~other._u64[1]); return v128::from64(~other._u64[0], ~other._u64[1]);
} }
template<typename T, std::size_t Size = sizeof(T)> struct se_storage #define IS_INTEGER(t) (std::is_integral<t>::value || std::is_enum<t>::value)
#define IS_BINARY_COMPARABLE(t1, t2) (IS_INTEGER(t1) && IS_INTEGER(t2) && sizeof(t1) == sizeof(t2))
template<typename T, std::size_t Size = sizeof(T)>
struct se_storage
{ {
static_assert(!Size, "Bad se_storage<> type"); static_assert(!Size, "Bad se_storage<> type");
}; };
template<typename T> struct se_storage<T, 2> template<typename T>
struct se_storage<T, 2>
{ {
using type = u16; using type = u16;
[[deprecated]] static constexpr u16 _swap(u16 src) // for reference static constexpr u16 swap(u16 src)
{
return (src >> 8) | (src << 8);
}
static inline u16 swap(u16 src)
{ {
#if defined(__GNUG__) #if defined(__GNUG__)
return __builtin_bswap16(src); return __builtin_bswap16(src);
@ -409,16 +359,12 @@ template<typename T> struct se_storage<T, 2>
} }
}; };
template<typename T> struct se_storage<T, 4> template<typename T>
struct se_storage<T, 4>
{ {
using type = u32; using type = u32;
[[deprecated]] static constexpr u32 _swap(u32 src) // for reference static constexpr u32 swap(u32 src)
{
return (src >> 24) | (src << 24) | ((src >> 8) & 0x0000ff00) | ((src << 8) & 0x00ff0000);
}
static inline u32 swap(u32 src)
{ {
#if defined(__GNUG__) #if defined(__GNUG__)
return __builtin_bswap32(src); return __builtin_bswap32(src);
@ -439,22 +385,12 @@ template<typename T> struct se_storage<T, 4>
} }
}; };
template<typename T> struct se_storage<T, 8> template<typename T>
struct se_storage<T, 8>
{ {
using type = u64; using type = u64;
[[deprecated]] static constexpr u64 _swap(u64 src) // for reference static constexpr u64 swap(u64 src)
{
return (src >> 56) | (src << 56) |
((src >> 40) & 0x000000000000ff00) |
((src >> 24) & 0x0000000000ff0000) |
((src >> 8) & 0x00000000ff000000) |
((src << 8) & 0x000000ff00000000) |
((src << 24) & 0x0000ff0000000000) |
((src << 40) & 0x00ff000000000000);
}
static inline u64 swap(u64 src)
{ {
#if defined(__GNUG__) #if defined(__GNUG__)
return __builtin_bswap64(src); return __builtin_bswap64(src);
@ -475,25 +411,32 @@ template<typename T> struct se_storage<T, 8>
} }
}; };
template<typename T> struct se_storage<T, 16> template<typename T>
struct se_storage<T, 16>
{ {
using type = v128; using type = v128;
static inline v128 swap(const v128& src)
{
return v128::fromV(_mm_shuffle_epi8(src.vi, _mm_set_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)));
}
static inline v128 to(const T& src) static inline v128 to(const T& src)
{ {
return v128::byteswap(reinterpret_cast<const v128&>(src)); return swap(reinterpret_cast<const v128&>(src));
} }
static inline T from(const v128& src) static inline T from(const v128& src)
{ {
const v128 result = v128::byteswap(src); const v128 result = swap(src);
return reinterpret_cast<const T&>(result); return reinterpret_cast<const T&>(result);
} }
}; };
template<typename T> using se_storage_t = typename se_storage<T>::type; template<typename T> using se_storage_t = typename se_storage<T>::type;
template<typename T1, typename T2> struct se_convert template<typename T1, typename T2>
struct se_convert
{ {
using type_from = std::remove_cv_t<T1>; using type_from = std::remove_cv_t<T1>;
using type_to = std::remove_cv_t<T2>; using type_to = std::remove_cv_t<T2>;
@ -515,10 +458,12 @@ template<typename T1, typename T2> struct se_convert
static struct se_raw_tag_t {} constexpr se_raw{}; static struct se_raw_tag_t {} constexpr se_raw{};
template<typename T, bool Se = true> class se_t; template<typename T, bool Se = true>
class se_t;
// se_t with switched endianness // Switched endianness
template<typename T> class se_t<T, true> template<typename T>
class se_t<T, true>
{ {
using type = typename std::remove_cv<T>::type; using type = typename std::remove_cv<T>::type;
using stype = se_storage_t<type>; using stype = se_storage_t<type>;
@ -526,14 +471,13 @@ template<typename T> class se_t<T, true>
stype m_data; stype m_data;
static_assert(!std::is_union<type>::value && !std::is_class<type>::value || std::is_same<type, v128>::value || std::is_same<type, u128>::value, "se_t<> error: invalid type (struct or union)");
static_assert(!std::is_pointer<type>::value, "se_t<> error: invalid type (pointer)"); static_assert(!std::is_pointer<type>::value, "se_t<> error: invalid type (pointer)");
static_assert(!std::is_reference<type>::value, "se_t<> error: invalid type (reference)"); static_assert(!std::is_reference<type>::value, "se_t<> error: invalid type (reference)");
static_assert(!std::is_array<type>::value, "se_t<> error: invalid type (array)"); static_assert(!std::is_array<type>::value, "se_t<> error: invalid type (array)");
//static_assert(!std::is_enum<type>::value, "se_t<> error: invalid type (enumeration), use integral type instead"); static_assert(sizeof(type) == alignof(type), "se_t<> error: unexpected alignment");
static_assert(alignof(type) == alignof(stype), "se_t<> error: unexpected alignment");
template<typename T2, typename = void> struct bool_converter template<typename T2, typename = void>
struct bool_converter
{ {
static inline bool to_bool(const se_t<T2>& value) static inline bool to_bool(const se_t<T2>& value)
{ {
@ -541,7 +485,8 @@ template<typename T> class se_t<T, true>
} }
}; };
template<typename T2> struct bool_converter<T2, std::enable_if_t<std::is_integral<T2>::value>> template<typename T2>
struct bool_converter<T2, std::enable_if_t<std::is_integral<T2>::value>>
{ {
static inline bool to_bool(const se_t<T2>& value) static inline bool to_bool(const se_t<T2>& value)
{ {
@ -559,7 +504,7 @@ public:
{ {
} }
// construct directly from raw data (don't use) // Construct directly from raw data (don't use)
constexpr se_t(const stype& raw_value, const se_raw_tag_t&) constexpr se_t(const stype& raw_value, const se_raw_tag_t&)
: m_data(raw_value) : m_data(raw_value)
{ {
@ -570,7 +515,7 @@ public:
return storage::from(m_data); return storage::from(m_data);
} }
// access underlying raw data (don't use) // Access underlying raw data (don't use)
constexpr const stype& raw_data() const noexcept constexpr const stype& raw_data() const noexcept
{ {
return m_data; return m_data;
@ -583,78 +528,96 @@ public:
return m_data = storage::to(value), *this; return m_data = storage::to(value), *this;
} }
using simple_type = simple_t<T>;
operator type() const operator type() const
{ {
return storage::from(m_data); return storage::from(m_data);
} }
// optimization // Optimization
explicit operator bool() const explicit operator bool() const
{ {
return bool_converter<type>::to_bool(*this); return bool_converter<type>::to_bool(*this);
} }
// optimization // Optimization
template<typename T2> std::enable_if_t<IS_BINARY_COMPARABLE(T, T2), se_t&> operator &=(const se_t<T2>& right) template<typename T2>
std::enable_if_t<IS_BINARY_COMPARABLE(T, T2), se_t&> operator &=(const se_t<T2>& right)
{ {
return m_data &= right.raw_data(), *this; return m_data &= right.raw_data(), *this;
} }
// optimization // Optimization
template<typename CT> std::enable_if_t<IS_INTEGRAL(T) && std::is_convertible<CT, T>::value, se_t&> operator &=(CT right) template<typename CT>
std::enable_if_t<std::is_integral<T>::value && std::is_convertible<CT, T>::value, se_t&> operator &=(CT right)
{ {
return m_data &= storage::to(right), *this; return m_data &= storage::to(right), *this;
} }
// optimization // Optimization
template<typename T2> std::enable_if_t<IS_BINARY_COMPARABLE(T, T2), se_t&> operator |=(const se_t<T2>& right) template<typename T2>
std::enable_if_t<IS_BINARY_COMPARABLE(T, T2), se_t&> operator |=(const se_t<T2>& right)
{ {
return m_data |= right.raw_data(), *this; return m_data |= right.raw_data(), *this;
} }
// optimization // Optimization
template<typename CT> std::enable_if_t<IS_INTEGRAL(T) && std::is_convertible<CT, T>::value, se_t&> operator |=(CT right) template<typename CT>
std::enable_if_t<std::is_integral<T>::value && std::is_convertible<CT, T>::value, se_t&> operator |=(CT right)
{ {
return m_data |= storage::to(right), *this; return m_data |= storage::to(right), *this;
} }
// optimization // Optimization
template<typename T2> std::enable_if_t<IS_BINARY_COMPARABLE(T, T2), se_t&> operator ^=(const se_t<T2>& right) template<typename T2>
std::enable_if_t<IS_BINARY_COMPARABLE(T, T2), se_t&> operator ^=(const se_t<T2>& right)
{ {
return m_data ^= right.raw_data(), *this; return m_data ^= right.raw_data(), *this;
} }
// optimization // Optimization
template<typename CT> std::enable_if_t<IS_INTEGRAL(T) && std::is_convertible<CT, T>::value, se_t&> operator ^=(CT right) template<typename CT>
std::enable_if_t<std::is_integral<T>::value && std::is_convertible<CT, T>::value, se_t&> operator ^=(CT right)
{ {
return m_data ^= storage::to(right), *this; return m_data ^= storage::to(right), *this;
} }
}; };
// se_t with native endianness // Native endianness
template<typename T> class se_t<T, false> template<typename T>
class se_t<T, false>
{ {
using type = typename std::remove_cv<T>::type; using type = typename std::remove_cv<T>::type;
type m_data;
static_assert(!std::is_union<type>::value && !std::is_class<type>::value || std::is_same<type, v128>::value || std::is_same<type, u128>::value, "se_t<> error: invalid type (struct or union)");
static_assert(!std::is_pointer<type>::value, "se_t<> error: invalid type (pointer)"); static_assert(!std::is_pointer<type>::value, "se_t<> error: invalid type (pointer)");
static_assert(!std::is_reference<type>::value, "se_t<> error: invalid type (reference)"); static_assert(!std::is_reference<type>::value, "se_t<> error: invalid type (reference)");
static_assert(!std::is_array<type>::value, "se_t<> error: invalid type (array)"); static_assert(!std::is_array<type>::value, "se_t<> error: invalid type (array)");
//static_assert(!std::is_enum<type>::value, "se_t<> error: invalid type (enumeration), use integral type instead"); static_assert(sizeof(type) == alignof(type), "se_t<> error: unexpected alignment");
type m_data;
public: public:
se_t() = default; se_t() = default;
se_t(const se_t&) = default;
constexpr se_t(type value) constexpr se_t(type value)
: m_data(value) : m_data(value)
{ {
} }
type value() const // Construct directly from raw data (don't use)
constexpr se_t(const type& raw_value, const se_raw_tag_t&)
: m_data(raw_value)
{
}
constexpr type value() const
{
return m_data;
}
// Access underlying raw data (don't use)
constexpr const type& raw_data() const noexcept
{ {
return m_data; return m_data;
} }
@ -666,22 +629,27 @@ public:
return m_data = value, *this; return m_data = value, *this;
} }
operator type() const using simple_type = simple_t<T>;
constexpr operator type() const
{ {
return m_data; return m_data;
} }
template<typename CT> std::enable_if_t<IS_INTEGRAL(T) && std::is_convertible<CT, T>::value, se_t&> operator &=(const CT& right) template<typename CT>
std::enable_if_t<std::is_integral<T>::value && std::is_convertible<CT, T>::value, se_t&> operator &=(const CT& right)
{ {
return m_data &= right, *this; return m_data &= right, *this;
} }
template<typename CT> std::enable_if_t<IS_INTEGRAL(T) && std::is_convertible<CT, T>::value, se_t&> operator |=(const CT& right) template<typename CT>
std::enable_if_t<std::is_integral<T>::value && std::is_convertible<CT, T>::value, se_t&> operator |=(const CT& right)
{ {
return m_data |= right, *this; return m_data |= right, *this;
} }
template<typename CT> std::enable_if_t<IS_INTEGRAL(T) && std::is_convertible<CT, T>::value, se_t&> operator ^=(const CT& right) template<typename CT>
std::enable_if_t<std::is_integral<T>::value && std::is_convertible<CT, T>::value, se_t&> operator ^=(const CT& right)
{ {
return m_data ^= right, *this; return m_data ^= right, *this;
} }
@ -690,49 +658,57 @@ public:
// se_t with native endianness (alias) // se_t with native endianness (alias)
template<typename T> using nse_t = se_t<T, false>; template<typename T> using nse_t = se_t<T, false>;
template<typename T, bool Se, typename T1> inline se_t<T, Se>& operator +=(se_t<T, Se>& left, const T1& right) template<typename T, bool Se, typename T1>
inline se_t<T, Se>& operator +=(se_t<T, Se>& left, const T1& right)
{ {
auto value = left.value(); auto value = left.value();
return left = (value += right); return left = (value += right);
} }
template<typename T, bool Se, typename T1> inline se_t<T, Se>& operator -=(se_t<T, Se>& left, const T1& right) template<typename T, bool Se, typename T1>
inline se_t<T, Se>& operator -=(se_t<T, Se>& left, const T1& right)
{ {
auto value = left.value(); auto value = left.value();
return left = (value -= right); return left = (value -= right);
} }
template<typename T, bool Se, typename T1> inline se_t<T, Se>& operator *=(se_t<T, Se>& left, const T1& right) template<typename T, bool Se, typename T1>
inline se_t<T, Se>& operator *=(se_t<T, Se>& left, const T1& right)
{ {
auto value = left.value(); auto value = left.value();
return left = (value *= right); return left = (value *= right);
} }
template<typename T, bool Se, typename T1> inline se_t<T, Se>& operator /=(se_t<T, Se>& left, const T1& right) template<typename T, bool Se, typename T1>
inline se_t<T, Se>& operator /=(se_t<T, Se>& left, const T1& right)
{ {
auto value = left.value(); auto value = left.value();
return left = (value /= right); return left = (value /= right);
} }
template<typename T, bool Se, typename T1> inline se_t<T, Se>& operator %=(se_t<T, Se>& left, const T1& right) template<typename T, bool Se, typename T1>
inline se_t<T, Se>& operator %=(se_t<T, Se>& left, const T1& right)
{ {
auto value = left.value(); auto value = left.value();
return left = (value %= right); return left = (value %= right);
} }
template<typename T, bool Se, typename T1> inline se_t<T, Se>& operator <<=(se_t<T, Se>& left, const T1& right) template<typename T, bool Se, typename T1>
inline se_t<T, Se>& operator <<=(se_t<T, Se>& left, const T1& right)
{ {
auto value = left.value(); auto value = left.value();
return left = (value <<= right); return left = (value <<= right);
} }
template<typename T, bool Se, typename T1> inline se_t<T, Se>& operator >>=(se_t<T, Se>& left, const T1& right) template<typename T, bool Se, typename T1>
inline se_t<T, Se>& operator >>=(se_t<T, Se>& left, const T1& right)
{ {
auto value = left.value(); auto value = left.value();
return left = (value >>= right); return left = (value >>= right);
} }
template<typename T, bool Se> inline se_t<T, Se> operator ++(se_t<T, Se>& left, int) template<typename T, bool Se>
inline se_t<T, Se> operator ++(se_t<T, Se>& left, int)
{ {
auto value = left.value(); auto value = left.value();
auto result = value++; auto result = value++;
@ -740,7 +716,8 @@ template<typename T, bool Se> inline se_t<T, Se> operator ++(se_t<T, Se>& left,
return result; return result;
} }
template<typename T, bool Se> inline se_t<T, Se> operator --(se_t<T, Se>& left, int) template<typename T, bool Se>
inline se_t<T, Se> operator --(se_t<T, Se>& left, int)
{ {
auto value = left.value(); auto value = left.value();
auto result = value--; auto result = value--;
@ -748,193 +725,205 @@ template<typename T, bool Se> inline se_t<T, Se> operator --(se_t<T, Se>& left,
return result; return result;
} }
template<typename T, bool Se> inline se_t<T, Se>& operator ++(se_t<T, Se>& right) template<typename T, bool Se>
inline se_t<T, Se>& operator ++(se_t<T, Se>& right)
{ {
auto value = right.value(); auto value = right.value();
return right = ++value; return right = ++value;
} }
template<typename T, bool Se> inline se_t<T, Se>& operator --(se_t<T, Se>& right) template<typename T, bool Se>
inline se_t<T, Se>& operator --(se_t<T, Se>& right)
{ {
auto value = right.value(); auto value = right.value();
return right = --value; return right = --value;
} }
// optimization // Optimization
template<typename T1, typename T2> inline std::enable_if_t<IS_BINARY_COMPARABLE(T1, T2), bool> operator ==(const se_t<T1>& left, const se_t<T2>& right) template<typename T1, typename T2>
inline std::enable_if_t<IS_BINARY_COMPARABLE(T1, T2), bool> operator ==(const se_t<T1>& left, const se_t<T2>& right)
{ {
return left.raw_data() == right.raw_data(); return left.raw_data() == right.raw_data();
} }
// optimization // Optimization
template<typename T1, typename T2> inline std::enable_if_t<IS_INTEGRAL(T1) && IS_INTEGER(T2) && sizeof(T1) >= sizeof(T2), bool> operator ==(const se_t<T1>& left, T2 right) template<typename T1, typename T2>
inline std::enable_if_t<std::is_integral<T1>::value && IS_INTEGER(T2) && sizeof(T1) >= sizeof(T2), bool> operator ==(const se_t<T1>& left, T2 right)
{ {
return left.raw_data() == se_storage<T1>::to(right); return left.raw_data() == se_storage<T1>::to(right);
} }
// optimization // Optimization
template<typename T1, typename T2> inline std::enable_if_t<IS_INTEGER(T1) && IS_INTEGRAL(T2) && sizeof(T1) <= sizeof(T2), bool> operator ==(T1 left, const se_t<T2>& right) template<typename T1, typename T2>
inline std::enable_if_t<IS_INTEGER(T1) && std::is_integral<T2>::value && sizeof(T1) <= sizeof(T2), bool> operator ==(T1 left, const se_t<T2>& right)
{ {
return se_storage<T2>::to(left) == right.raw_data(); return se_storage<T2>::to(left) == right.raw_data();
} }
// optimization // Optimization
template<typename T1, typename T2> inline std::enable_if_t<IS_BINARY_COMPARABLE(T1, T2), bool> operator !=(const se_t<T1>& left, const se_t<T2>& right) template<typename T1, typename T2>
inline std::enable_if_t<IS_BINARY_COMPARABLE(T1, T2), bool> operator !=(const se_t<T1>& left, const se_t<T2>& right)
{ {
return left.raw_data() != right.raw_data(); return left.raw_data() != right.raw_data();
} }
// optimization // Optimization
template<typename T1, typename T2> inline std::enable_if_t<IS_INTEGRAL(T1) && IS_INTEGER(T2) && sizeof(T1) >= sizeof(T2), bool> operator !=(const se_t<T1>& left, T2 right) template<typename T1, typename T2>
inline std::enable_if_t<std::is_integral<T1>::value && IS_INTEGER(T2) && sizeof(T1) >= sizeof(T2), bool> operator !=(const se_t<T1>& left, T2 right)
{ {
return left.raw_data() != se_storage<T1>::to(right); return left.raw_data() != se_storage<T1>::to(right);
} }
// optimization // Optimization
template<typename T1, typename T2> inline std::enable_if_t<IS_INTEGER(T1) && IS_INTEGRAL(T2) && sizeof(T1) <= sizeof(T2), bool> operator !=(T1 left, const se_t<T2>& right) template<typename T1, typename T2>
inline std::enable_if_t<IS_INTEGER(T1) && std::is_integral<T2>::value && sizeof(T1) <= sizeof(T2), bool> operator !=(T1 left, const se_t<T2>& right)
{ {
return se_storage<T2>::to(left) != right.raw_data(); return se_storage<T2>::to(left) != right.raw_data();
} }
// optimization // Optimization
template<typename T1, typename T2> inline std::enable_if_t<IS_BINARY_COMPARABLE(T1, T2) && sizeof(T1) >= 4, se_t<decltype(T1() & T2())>> operator &(const se_t<T1>& left, const se_t<T2>& right) template<typename T1, typename T2>
inline std::enable_if_t<IS_BINARY_COMPARABLE(T1, T2) && sizeof(T1) >= 4, se_t<decltype(T1() & T2())>> operator &(const se_t<T1>& left, const se_t<T2>& right)
{ {
return{ left.raw_data() & right.raw_data(), se_raw }; return{ left.raw_data() & right.raw_data(), se_raw };
} }
// optimization // Optimization
template<typename T1, typename T2> inline std::enable_if_t<IS_INTEGRAL(T1) && IS_INTEGER(T2) && sizeof(T1) >= sizeof(T2) && sizeof(T1) >= 4, se_t<decltype(T1() & T2())>> operator &(const se_t<T1>& left, T2 right) template<typename T1, typename T2>
inline std::enable_if_t<std::is_integral<T1>::value && IS_INTEGER(T2) && sizeof(T1) >= sizeof(T2) && sizeof(T1) >= 4, se_t<decltype(T1() & T2())>> operator &(const se_t<T1>& left, T2 right)
{ {
return{ left.raw_data() & se_storage<T1>::to(right), se_raw }; return{ left.raw_data() & se_storage<T1>::to(right), se_raw };
} }
// optimization // Optimization
template<typename T1, typename T2> inline std::enable_if_t<IS_INTEGER(T1) && IS_INTEGRAL(T2) && sizeof(T1) <= sizeof(T2) && sizeof(T2) >= 4, se_t<decltype(T1() & T2())>> operator &(T1 left, const se_t<T2>& right) template<typename T1, typename T2>
inline std::enable_if_t<IS_INTEGER(T1) && std::is_integral<T2>::value && sizeof(T1) <= sizeof(T2) && sizeof(T2) >= 4, se_t<decltype(T1() & T2())>> operator &(T1 left, const se_t<T2>& right)
{ {
return{ se_storage<T2>::to(left) & right.raw_data(), se_raw }; return{ se_storage<T2>::to(left) & right.raw_data(), se_raw };
} }
// optimization // Optimization
template<typename T1, typename T2> inline std::enable_if_t<IS_BINARY_COMPARABLE(T1, T2) && sizeof(T1) >= 4, se_t<decltype(T1() | T2())>> operator |(const se_t<T1>& left, const se_t<T2>& right) template<typename T1, typename T2>
inline std::enable_if_t<IS_BINARY_COMPARABLE(T1, T2) && sizeof(T1) >= 4, se_t<decltype(T1() | T2())>> operator |(const se_t<T1>& left, const se_t<T2>& right)
{ {
return{ left.raw_data() | right.raw_data(), se_raw }; return{ left.raw_data() | right.raw_data(), se_raw };
} }
// optimization // Optimization
template<typename T1, typename T2> inline std::enable_if_t<IS_INTEGRAL(T1) && IS_INTEGER(T2) && sizeof(T1) >= sizeof(T2) && sizeof(T1) >= 4, se_t<decltype(T1() | T2())>> operator |(const se_t<T1>& left, T2 right) template<typename T1, typename T2>
inline std::enable_if_t<std::is_integral<T1>::value && IS_INTEGER(T2) && sizeof(T1) >= sizeof(T2) && sizeof(T1) >= 4, se_t<decltype(T1() | T2())>> operator |(const se_t<T1>& left, T2 right)
{ {
return{ left.raw_data() | se_storage<T1>::to(right), se_raw }; return{ left.raw_data() | se_storage<T1>::to(right), se_raw };
} }
// optimization // Optimization
template<typename T1, typename T2> inline std::enable_if_t<IS_INTEGER(T1) && IS_INTEGRAL(T2) && sizeof(T1) <= sizeof(T2) && sizeof(T2) >= 4, se_t<decltype(T1() | T2())>> operator |(T1 left, const se_t<T2>& right) template<typename T1, typename T2>
inline std::enable_if_t<IS_INTEGER(T1) && std::is_integral<T2>::value && sizeof(T1) <= sizeof(T2) && sizeof(T2) >= 4, se_t<decltype(T1() | T2())>> operator |(T1 left, const se_t<T2>& right)
{ {
return{ se_storage<T2>::to(left) | right.raw_data(), se_raw }; return{ se_storage<T2>::to(left) | right.raw_data(), se_raw };
} }
// optimization // Optimization
template<typename T1, typename T2> inline std::enable_if_t<IS_BINARY_COMPARABLE(T1, T2) && sizeof(T1) >= 4, se_t<decltype(T1() ^ T2())>> operator ^(const se_t<T1>& left, const se_t<T2>& right) template<typename T1, typename T2>
inline std::enable_if_t<IS_BINARY_COMPARABLE(T1, T2) && sizeof(T1) >= 4, se_t<decltype(T1() ^ T2())>> operator ^(const se_t<T1>& left, const se_t<T2>& right)
{ {
return{ left.raw_data() ^ right.raw_data(), se_raw }; return{ left.raw_data() ^ right.raw_data(), se_raw };
} }
// optimization // Optimization
template<typename T1, typename T2> inline std::enable_if_t<IS_INTEGRAL(T1) && IS_INTEGER(T2) && sizeof(T1) >= sizeof(T2) && sizeof(T1) >= 4, se_t<decltype(T1() ^ T2())>> operator ^(const se_t<T1>& left, T2 right) template<typename T1, typename T2>
inline std::enable_if_t<std::is_integral<T1>::value && IS_INTEGER(T2) && sizeof(T1) >= sizeof(T2) && sizeof(T1) >= 4, se_t<decltype(T1() ^ T2())>> operator ^(const se_t<T1>& left, T2 right)
{ {
return{ left.raw_data() ^ se_storage<T1>::to(right), se_raw }; return{ left.raw_data() ^ se_storage<T1>::to(right), se_raw };
} }
// optimization // Optimization
template<typename T1, typename T2> inline std::enable_if_t<IS_INTEGER(T1) && IS_INTEGRAL(T2) && sizeof(T1) <= sizeof(T2) && sizeof(T2) >= 4, se_t<decltype(T1() ^ T2())>> operator ^(T1 left, const se_t<T2>& right) template<typename T1, typename T2>
inline std::enable_if_t<IS_INTEGER(T1) && std::is_integral<T2>::value && sizeof(T1) <= sizeof(T2) && sizeof(T2) >= 4, se_t<decltype(T1() ^ T2())>> operator ^(T1 left, const se_t<T2>& right)
{ {
return{ se_storage<T2>::to(left) ^ right.raw_data(), se_raw }; return{ se_storage<T2>::to(left) ^ right.raw_data(), se_raw };
} }
// optimization // Optimization
template<typename T> inline std::enable_if_t<IS_INTEGRAL(T) && sizeof(T) >= 4, se_t<decltype(~T())>> operator ~(const se_t<T>& right) template<typename T>
inline std::enable_if_t<std::is_integral<T>::value && sizeof(T) >= 4, se_t<decltype(~T())>> operator ~(const se_t<T>& right)
{ {
return{ ~right.raw_data(), se_raw }; return{ ~right.raw_data(), se_raw };
} }
#ifdef IS_LE_MACHINE #if IS_LE_MACHINE == 1
template<typename T> using be_t = se_t<T, true>; template<typename T> using be_t = se_t<T, true>;
template<typename T> using le_t = se_t<T, false>; template<typename T> using le_t = se_t<T, false>;
#else
template<typename T> using be_t = se_t<T, false>;
template<typename T> using le_t = se_t<T, true>;
#endif #endif
// Type converter: converts native endianness arithmetic/enum types to appropriate se_t<> type
template<typename T, bool Se, typename = void> struct to_se template<typename T, bool Se, typename = void>
struct to_se
{ {
// Convert arithmetic and enum types
using type = typename std::conditional<std::is_arithmetic<T>::value || std::is_enum<T>::value, se_t<T, Se>, T>::type; using type = typename std::conditional<std::is_arithmetic<T>::value || std::is_enum<T>::value, se_t<T, Se>, T>::type;
}; };
template<typename T, bool Se> struct to_se<const T, Se, std::enable_if_t<!std::is_array<T>::value>> // move const qualifier
{
using type = const typename to_se<T, Se>::type;
};
template<typename T, bool Se> struct to_se<volatile T, Se, std::enable_if_t<!std::is_array<T>::value && !std::is_const<T>::value>> // move volatile qualifier
{
using type = volatile typename to_se<T, Se>::type;
};
template<typename T, bool Se> struct to_se<T[], Se>
{
using type = typename to_se<T, Se>::type[];
};
template<typename T, bool Se, std::size_t N> struct to_se<T[N], Se>
{
using type = typename to_se<T, Se>::type[N];
};
template<bool Se> struct to_se<u128, Se> { using type = se_t<u128, Se>; };
template<bool Se> struct to_se<v128, Se> { using type = se_t<v128, Se>; }; template<bool Se> struct to_se<v128, Se> { using type = se_t<v128, Se>; };
template<bool Se> struct to_se<bool, Se> { using type = bool; }; template<bool Se> struct to_se<bool, Se> { using type = bool; };
template<bool Se> struct to_se<char, Se> { using type = char; }; template<bool Se> struct to_se<char, Se> { using type = char; };
template<bool Se> struct to_se<u8, Se> { using type = u8; }; template<bool Se> struct to_se<u8, Se> { using type = u8; };
template<bool Se> struct to_se<s8, Se> { using type = s8; }; template<bool Se> struct to_se<s8, Se> { using type = s8; };
#ifdef IS_LE_MACHINE template<typename T, bool Se>
struct to_se<const T, Se, std::enable_if_t<!std::is_array<T>::value>>
{
// Move const qualifier
using type = const typename to_se<T, Se>::type;
};
template<typename T, bool Se>
struct to_se<volatile T, Se, std::enable_if_t<!std::is_array<T>::value && !std::is_const<T>::value>>
{
// Move volatile qualifier
using type = volatile typename to_se<T, Se>::type;
};
template<typename T, bool Se>
struct to_se<T[], Se>
{
// Move array qualifier
using type = typename to_se<T, Se>::type[];
};
template<typename T, bool Se, std::size_t N>
struct to_se<T[N], Se>
{
// Move array qualifier
using type = typename to_se<T, Se>::type[N];
};
// BE/LE aliases for to_se<>
#if IS_LE_MACHINE == 1
template<typename T> using to_be_t = typename to_se<T, true>::type; template<typename T> using to_be_t = typename to_se<T, true>::type;
template<typename T> using to_le_t = typename to_se<T, false>::type; template<typename T> using to_le_t = typename to_se<T, false>::type;
#else
template<typename T> using to_be_t = typename to_se<T, false>::type;
template<typename T> using to_le_t = typename to_se<T, true>::type;
#endif #endif
// BE/LE aliases for atomic_t
#if IS_LE_MACHINE == 1
template<typename T> using atomic_be_t = atomic_t<be_t<T>>;
template<typename T> using atomic_le_t = atomic_t<le_t<T>>;
#endif
template<typename T, typename = void> struct to_ne namespace fmt
{ {
using type = T; // Formatting for BE/LE data
}; template<typename T, bool Se>
struct unveil<se_t<T, Se>, void>
{
using result_type = typename unveil<T>::result_type;
template<typename T, bool Se> struct to_ne<se_t<T, Se>> static inline result_type get_value(const se_t<T, Se>& arg)
{ {
using type = typename std::remove_cv<T>::type; return unveil<T>::get_value(arg);
}; }
};
}
template<typename T> struct to_ne<const T, std::enable_if_t<!std::is_array<T>::value>> // move const qualifier #undef IS_BINARY_COMPARABLE
{ #undef IS_INTEGER
using type = const typename to_ne<T>::type;
};
template<typename T> struct to_ne<volatile T, std::enable_if_t<!std::is_array<T>::value && !std::is_const<T>::value>> // move volatile qualifier
{
using type = volatile typename to_ne<T>::type;
};
template<typename T> struct to_ne<T[]>
{
using type = typename to_ne<T>::type[];
};
template<typename T, std::size_t N> struct to_ne<T[N]>
{
using type = typename to_ne<T>::type[N];
};
// restore native endianness for T: returns T for be_t<T> or le_t<T>, T otherwise
template<typename T> using to_ne_t = typename to_ne<T>::type;

View File

@ -1,73 +1,106 @@
#pragma once #pragma once
// BitField access helper class (N bits from I position), intended to be put in union #include "types.h"
template<typename T, u32 I, u32 N> class bf_t
template<typename T, uint N>
struct bf_base
{ {
// Checks using type = T;
static_assert(I < sizeof(T) * 8, "bf_t<> error: I out of bounds"); using vtype = simple_t<type>;
static_assert(N < sizeof(T) * 8, "bf_t<> error: N out of bounds");
static_assert(I + N <= sizeof(T) * 8, "bf_t<> error: values out of bounds");
// Underlying data type // Datatype bitsize
using type = typename std::remove_cv<T>::type; static constexpr uint bitmax = sizeof(T) * CHAR_BIT; static_assert(N - 1 < bitmax, "bf_base<> error: N out of bounds");
// Underlying value type (native endianness) // Field bitsize
using vtype = typename to_ne<type>::type; static constexpr uint bitsize = N;
// Mask of size N // Value mask
constexpr static vtype s_mask = (static_cast<vtype>(1) << N) - 1; static constexpr vtype vmask = static_cast<vtype>(~std::make_unsigned_t<vtype>{} >> (bitmax - bitsize));
// Underlying data member protected:
type m_data; type m_data;
};
// Conversion operator helper (uses SFINAE) // Bitfield accessor (N bits from I position, 0 is LSB)
template<typename T2, typename = void> struct converter {}; template<typename T, uint I, uint N>
struct bf_t : bf_base<T, N>
{
using type = typename bf_t::type;
using vtype = typename bf_t::vtype;
template<typename T2> struct converter<T2, std::enable_if_t<std::is_unsigned<T2>::value>> // Field offset
static constexpr uint bitpos = I; static_assert(bitpos + N <= bf_t::bitmax, "bf_t<> error: I out of bounds");
// Get bitmask of size N, at I pos
static constexpr vtype data_mask()
{
return bf_t::vmask << bitpos;
}
// Bitfield extraction helper
template<typename T2, typename = void>
struct extract_impl
{
static_assert(!sizeof(T2), "bf_t<> error: Invalid type");
};
template<typename T2>
struct extract_impl<T2, std::enable_if_t<std::is_unsigned<T2>::value>>
{ {
// Load unsigned value // Load unsigned value
static inline T2 convert(const type& data) static constexpr T2 extract(const T& data)
{ {
return (data >> I) & s_mask; return (data >> bitpos) & bf_t::vmask;
} }
}; };
template<typename T2> struct converter<T2, std::enable_if_t<std::is_signed<T2>::value>> template<typename T2>
struct extract_impl<T2, std::enable_if_t<std::is_signed<T2>::value>>
{ {
// Load signed value (sign-extended) // Load signed value (sign-extended)
static inline T2 convert(const type& data) static constexpr T2 extract(const T& data)
{ {
return data << (sizeof(T) * 8 - I - N) >> (sizeof(T) * 8 - N); return data << (bf_t::bitmax - bitpos - N) >> (bf_t::bitmax - N);
} }
}; };
public: // Bitfield extraction
// Assignment operator (store bitfield value) static constexpr vtype extract(const T& data)
bf_t& operator =(vtype value)
{ {
m_data = (m_data & ~(s_mask << I)) | (value & s_mask) << I; return extract_impl<vtype>::extract(data);
return *this;
} }
// Conversion operator (load bitfield value) // Bitfield insertion
operator vtype() const static constexpr vtype insert(vtype value)
{ {
return converter<vtype>::convert(m_data); return (value & bf_t::vmask) << bitpos;
} }
// Get raw data with mask applied // Load bitfield value
type unshifted() const constexpr operator vtype() const
{ {
return (m_data & (s_mask << I)); return extract(this->m_data);
} }
// Optimized bool conversion // Load raw data with mask applied
explicit operator bool() const constexpr T unshifted() const
{
return this->m_data & data_mask();
}
// Optimized bool conversion (must be removed if inappropriate)
explicit constexpr operator bool() const
{ {
return unshifted() != 0; return unshifted() != 0;
} }
// Postfix increment operator // Store bitfield value
bf_t& operator =(vtype value)
{
this->m_data = (this->m_data & ~data_mask()) | insert(value);
return *this;
}
vtype operator ++(int) vtype operator ++(int)
{ {
vtype result = *this; vtype result = *this;
@ -75,13 +108,11 @@ public:
return result; return result;
} }
// Prefix increment operator
bf_t& operator ++() bf_t& operator ++()
{ {
return *this = *this + 1; return *this = *this + 1;
} }
// Postfix decrement operator
vtype operator --(int) vtype operator --(int)
{ {
vtype result = *this; vtype result = *this;
@ -89,52 +120,125 @@ public:
return result; return result;
} }
// Prefix decrement operator
bf_t& operator --() bf_t& operator --()
{ {
return *this = *this - 1; return *this = *this - 1;
} }
// Addition assignment operator
bf_t& operator +=(vtype right) bf_t& operator +=(vtype right)
{ {
return *this = *this + right; return *this = *this + right;
} }
// Subtraction assignment operator
bf_t& operator -=(vtype right) bf_t& operator -=(vtype right)
{ {
return *this = *this - right; return *this = *this - right;
} }
// Multiplication assignment operator
bf_t& operator *=(vtype right) bf_t& operator *=(vtype right)
{ {
return *this = *this * right; return *this = *this * right;
} }
// Bitwise AND assignment operator
bf_t& operator &=(vtype right) bf_t& operator &=(vtype right)
{ {
m_data &= (right & s_mask) << I; this->m_data &= (right & bf_t::vmask) << bitpos;
return *this; return *this;
} }
// Bitwise OR assignment operator
bf_t& operator |=(vtype right) bf_t& operator |=(vtype right)
{ {
m_data |= (right & s_mask) << I; this->m_data |= (right & bf_t::vmask) << bitpos;
return *this; return *this;
} }
// Bitwise XOR assignment operator
bf_t& operator ^=(vtype right) bf_t& operator ^=(vtype right)
{ {
m_data ^= (right & s_mask) << I; this->m_data ^= (right & bf_t::vmask) << bitpos;
return *this; return *this;
} }
}; };
template<typename T, u32 I, u32 N> using bf_be_t = bf_t<be_t<T>, I, N>; // Field pack (concatenated from left to right)
template<typename F = void, typename... Fields>
struct cf_t : bf_base<typename F::type, F::bitsize + cf_t<Fields...>::bitsize>
{
using type = typename cf_t::type;
using vtype = typename cf_t::vtype;
template<typename T, u32 I, u32 N> using bf_le_t = bf_t<le_t<T>, I, N>; // Get disjunction of all "data" masks of concatenated values
static constexpr vtype data_mask()
{
return F::data_mask() | cf_t<Fields...>::data_mask();
}
// Extract all bitfields and concatenate
static constexpr vtype extract(const type& data)
{
return F::extract(data) << cf_t<Fields...>::bitsize | cf_t<Fields...>::extract(data);
}
// Split bitfields and insert them
static constexpr vtype insert(vtype value)
{
return F::insert(value >> cf_t<Fields...>::bitsize) | cf_t<Fields...>::insert(value);
}
// Load value
constexpr operator vtype() const
{
return extract(this->m_data);
}
// Store value
cf_t& operator =(vtype value)
{
this->m_data = (this->m_data & ~data_mask()) | insert(value);
return *this;
}
};
// Empty field pack (recursion terminator)
template<>
struct cf_t<void>
{
static constexpr uint bitsize = 0;
static constexpr uint data_mask()
{
return 0;
}
template<typename T>
static constexpr auto extract(const T& data) -> decltype(+T())
{
return 0;
}
template<typename T>
static constexpr T insert(T value)
{
return 0;
}
};
// Fixed field (provides constant values in field pack)
template<typename T, T V, uint N>
struct ff_t : bf_base<T, N>
{
using type = typename ff_t::type;
using vtype = typename ff_t::vtype;
// Return constant value
static constexpr vtype extract(const type& data)
{
static_assert((V & ff_t::vmask) == V, "ff_t<> error: V out of bounds");
return V;
}
// Get value
operator vtype() const
{
return V;
}
};

203
Utilities/Config.cpp Normal file
View File

@ -0,0 +1,203 @@
#include "stdafx.h"
#include "Config.h"
#include "yaml-cpp/yaml.h"
namespace cfg
{
_log::channel cfg("CFG", _log::level::notice);
entry_base::entry_base(type _type)
: m_type(_type)
{
if (_type != type::node)
{
throw std::logic_error("Invalid root node");
}
}
entry_base::entry_base(type _type, node& owner, const std::string& name)
: m_type(_type)
{
if (!owner.m_nodes.emplace(name, this).second)
{
throw std::logic_error("Node already exists");
}
}
entry_base& entry_base::operator[](const std::string& name) const
{
if (m_type == type::node)
{
return *static_cast<const node&>(*this).m_nodes.at(name);
}
throw std::logic_error("Invalid node type");
}
entry_base& entry_base::operator[](const char* name) const
{
if (m_type == type::node)
{
return *static_cast<const node&>(*this).m_nodes.at(name);
}
throw std::logic_error("Invalid node type");
}
// Emit YAML
static void encode(YAML::Emitter& out, const class entry_base& rhs);
// Incrementally load config entries from YAML::Node.
// The config value is preserved if the corresponding YAML node doesn't exist.
static void decode(const YAML::Node& data, class entry_base& rhs);
}
bool cfg::try_to_int64(s64* out, const std::string& value, s64 min, s64 max)
{
// TODO: this could be rewritten without exceptions (but it should be as safe as possible and provide logs)
s64 result;
std::size_t pos;
try
{
result = std::stoll(value, &pos, 0 /* Auto-detect numeric base */);
}
catch (const std::exception& e)
{
if (out) cfg.error("cfg::try_to_int('%s'): exception: %s", value, e.what());
return false;
}
if (pos != value.size())
{
if (out) cfg.error("cfg::try_to_int('%s'): unexpected characters (pos=%zu)", value, pos);
return false;
}
if (result < min || result > max)
{
if (out) cfg.error("cfg::try_to_int('%s'): out of bounds (%lld..%lld)", value, min, max);
return false;
}
if (out) *out = result;
return true;
}
void cfg::encode(YAML::Emitter& out, const cfg::entry_base& rhs)
{
switch (rhs.get_type())
{
case type::node:
{
out << YAML::BeginMap;
for (const auto& np : static_cast<const node&>(rhs).get_nodes())
{
out << YAML::Key << np.first;
out << YAML::Value; encode(out, *np.second);
}
out << YAML::EndMap;
return;
}
case type::set:
{
out << YAML::BeginSeq;
for (const auto& str : static_cast<const set_entry&>(rhs).get_set())
{
out << str;
}
out << YAML::EndSeq;
return;
}
}
out << rhs.to_string();
}
void cfg::decode(const YAML::Node& data, cfg::entry_base& rhs)
{
switch (rhs.get_type())
{
case type::node:
{
if (data.IsScalar() || data.IsSequence())
{
return; // ???
}
for (const auto& pair : data)
{
if (!pair.first.IsScalar()) continue;
// Find the key among existing nodes
const auto name = pair.first.Scalar();
const auto found = static_cast<node&>(rhs).get_nodes().find(name);
if (found != static_cast<node&>(rhs).get_nodes().cend())
{
decode(pair.second, *found->second);
}
else
{
// ???
}
}
break;
}
case type::set:
{
std::vector<std::string> values;
if (YAML::convert<decltype(values)>::decode(data, values))
{
rhs.from_list(std::move(values));
}
break;
}
default:
{
std::string value;
if (YAML::convert<std::string>::decode(data, value))
{
rhs.from_string(value);
}
break; // ???
}
}
}
std::string cfg::node::to_string() const
{
YAML::Emitter out;
cfg::encode(out, *this);
return{ out.c_str(), out.size() };
}
bool cfg::node::from_string(const std::string& value)
{
cfg::decode(YAML::Load(value), *this);
return true;
}
void cfg::node::from_default()
{
for (auto& node : m_nodes)
{
node.second->from_default();
}
}
cfg::root_node& cfg::get_root()
{
// Magic static
static root_node root;
return root;
}

523
Utilities/Config.h Normal file
View File

@ -0,0 +1,523 @@
#pragma once
#include "Utilities/Atomic.h"
#include <set>
#include <map>
namespace cfg
{
// Convert string to signed integer
bool try_to_int64(s64* out, const std::string& value, s64 min, s64 max);
// Config tree entry type.
enum class type : uint
{
node = 0, // cfg::node type
boolean, // cfg::bool_entry type
fixed_map, // cfg::map_entry type
enumeration, // cfg::enum_entry type
integer, // cfg::int_entry type
string, // cfg::string_entry type
set, // cfg::set_entry type
};
// Config tree entry abstract base class
class entry_base
{
const type m_type;
protected:
// Ownerless entry constructor
entry_base(type _type);
// Owned entry constructor
entry_base(type _type, class node& owner, const std::string& name);
public:
// Disallow copy/move constructors and assignments
entry_base(const entry_base&) = delete;
// Get type
type get_type() const { return m_type; }
// Access child node (must exist)
entry_base& operator [](const std::string& name) const; entry_base& operator [](const char* name) const;
// Reset defaults
virtual void from_default() = 0;
// Convert to string (optional)
virtual std::string to_string() const
{
return{};
}
// Try to convert from string (optional)
virtual bool from_string(const std::string&)
{
throw std::logic_error("from_string() not specified");
}
// Get string list (optional)
virtual std::vector<std::string> to_list() const
{
return{};
}
// Set multiple values. Implementation-specific, optional.
virtual bool from_list(std::vector<std::string>&&)
{
throw std::logic_error("from_list() not specified");
}
};
// Config tree node which contains another nodes
class node : public entry_base
{
std::map<std::string, entry_base*> m_nodes;
friend class entry_base;
public:
// Root node constructor
node()
: entry_base(type::node)
{
}
// Registered node constructor
node(node& owner, const std::string& name)
: entry_base(type::node, owner, name)
{
}
// Get child nodes
const std::map<std::string, entry_base*>& get_nodes() const
{
return m_nodes;
}
// Serialize node
std::string to_string() const override;
// Deserialize node
bool from_string(const std::string& value) override;
// Set default values
void from_default() override;
};
struct bool_entry final : public entry_base
{
atomic_t<bool> value;
const bool def;
bool_entry(node& owner, const std::string& name, bool def = false)
: entry_base(type::boolean, owner, name)
, value(def)
, def(def)
{
}
explicit operator bool() const
{
return value.load();
}
bool_entry& operator =(bool value)
{
value = value;
return *this;
}
void from_default() override
{
value = def;
}
std::string to_string() const override
{
return value.load() ? "true" : "false";
}
bool from_string(const std::string& value) override
{
if (value == "false")
this->value = false;
else if (value == "true")
this->value = true;
else
return false;
return true;
}
};
// Value node with fixed set of possible values, each maps to a value of type T.
template<typename T>
struct map_entry final : public entry_base
{
using init_type = std::initializer_list<std::pair<std::string, T>>;
using map_type = std::unordered_map<std::string, T>;
using list_type = std::vector<std::string>;
using value_type = typename map_type::value_type;
static map_type make_map(init_type init)
{
map_type map(init.size());
for (const auto& v : init)
{
// Ensure elements are unique
ASSERT(map.emplace(v.first, v.second).second);
}
return map;
}
static list_type make_list(init_type init)
{
list_type list; list.reserve(init.size());
for (const auto& v : init)
{
list.emplace_back(v.first);
}
return list;
}
public:
const map_type map;
const list_type list; // Element list sorted in original order
const value_type& def; // Pointer to the default value
private:
atomic_t<const value_type*> m_value;
public:
map_entry(node& owner, const std::string& name, const std::string& def, init_type init)
: entry_base(type::fixed_map, owner, name)
, map(make_map(init))
, list(make_list(init))
, def(*map.find(def))
, m_value(&this->def)
{
}
map_entry(node& owner, const std::string& name, std::size_t def_index, init_type init)
: map_entry(owner, name, def_index < init.size() ? (init.begin() + def_index)->first : throw std::logic_error("Invalid default value index"), init)
{
}
map_entry(node& owner, const std::string& name, init_type init)
: map_entry(owner, name, 0, init)
{
}
const T& get() const
{
return m_value.load()->second;
}
void from_default() override
{
m_value = &def;
}
std::string to_string() const override
{
return m_value.load()->first;
}
bool from_string(const std::string& value) override
{
const auto found = map.find(value);
if (found == map.end())
{
return false;
}
else
{
m_value = &*found;
return true;
}
}
std::vector<std::string> to_list() const override
{
return list;
}
};
// Value node with fixed set of possible values, each maps to an enum value of type T.
template<typename T, bool External = false>
class enum_entry final : public entry_base
{
// Value or reference
std::conditional_t<External, atomic_t<T>&, atomic_t<T>> m_value;
public:
const T def;
enum_entry(node& owner, const std::string& name, std::conditional_t<External, atomic_t<T>&, T> value)
: entry_base(type::enumeration, owner, name)
, m_value(value)
, def(value)
{
}
operator T() const
{
return m_value.load();
}
enum_entry& operator =(T value)
{
m_value = value;
return *this;
}
void from_default() override
{
m_value = def;
}
std::string to_string() const override
{
for (const auto& pair : bijective<T, const char*>::map)
{
if (pair.first == m_value)
{
return pair.second;
}
}
return{}; // TODO: ???
}
bool from_string(const std::string& value) override
{
for (const auto& pair : bijective<T, const char*>::map)
{
if (pair.second == value)
{
m_value = pair.first;
return true;
}
}
return false;
}
std::vector<std::string> to_list() const override
{
std::vector<std::string> result;
for (const auto& pair : bijective<T, const char*>::map)
{
result.emplace_back(pair.second);
}
return result;
}
};
// Signed 32/64-bit integer entry with custom Min/Max range.
template<s64 Min, s64 Max>
class int_entry final : public entry_base
{
static_assert(Min < Max, "Invalid cfg::int_entry range");
// Prefer 32 bit type if possible
using int_type = std::conditional_t<Min >= INT32_MIN && Max <= INT32_MAX, s32, s64>;
atomic_t<int_type> m_value;
public:
const int_type def;
int_entry(node& owner, const std::string& name, int_type def = std::min<int_type>(Max, std::max<int_type>(Min, 0)))
: entry_base(type::integer, owner, name)
, m_value(def)
, def(def)
{
}
operator int_type() const
{
return m_value.load();
}
int_entry& operator =(int_type value)
{
if (value < Min || value > Max)
{
throw fmt::exception("Value out of the valid range: %lld" HERE, s64{ value });
}
m_value = value;
return *this;
}
void from_default() override
{
m_value = def;
}
std::string to_string() const override
{
return std::to_string(m_value.load());
}
bool from_string(const std::string& value) override
{
s64 result;
if (try_to_int64(&result, value, Min, Max))
{
m_value = static_cast<int_type>(result);
return true;
}
return false;
}
};
// Alias for 32 bit int
using int32_entry = int_entry<INT32_MIN, INT32_MAX>;
// Alias for 64 bit int
using int64_entry = int_entry<INT64_MIN, INT64_MAX>;
// Simple string entry with mutex
class string_entry final : public entry_base
{
mutable std::mutex m_mutex;
std::string m_value;
public:
const std::string def;
string_entry(node& owner, const std::string& name, const std::string& def = {})
: entry_base(type::string, owner, name)
, m_value(def)
, def(def)
{
}
operator std::string() const
{
std::lock_guard<std::mutex> lock(m_mutex);
return m_value;
}
std::string get() const
{
return *this;
}
string_entry& operator =(const std::string& value)
{
std::lock_guard<std::mutex> lock(m_mutex);
m_value = value;
return *this;
}
std::size_t size() const
{
std::lock_guard<std::mutex> lock(m_mutex);
return m_value.size();
}
void from_default() override
{
*this = def;
}
std::string to_string() const override
{
std::lock_guard<std::mutex> lock(m_mutex);
return m_value;
}
bool from_string(const std::string& value) override
{
*this = value;
return true;
}
};
// Simple set entry with mutex (TODO: template for various types)
class set_entry final : public entry_base
{
mutable std::mutex m_mutex;
std::set<std::string> m_set;
public:
// Default value is empty list in current implementation
set_entry(node& owner, const std::string& name)
: entry_base(type::set, owner, name)
{
}
std::set<std::string> get_set() const
{
std::lock_guard<std::mutex> lock(m_mutex);
return m_set;
}
void set_set(std::set<std::string>&& set)
{
std::lock_guard<std::mutex> lock(m_mutex);
m_set = std::move(set);
}
void from_default() override
{
std::lock_guard<std::mutex> lock(m_mutex);
m_set = {};
}
std::vector<std::string> to_list() const override
{
std::lock_guard<std::mutex> lock(m_mutex);
return{ m_set.begin(), m_set.end() };
}
bool from_list(std::vector<std::string>&& list) override
{
std::lock_guard<std::mutex> lock(m_mutex);
m_set = { std::make_move_iterator(list.begin()), std::make_move_iterator(list.end()) };
return true;
}
};
// Root type with some predefined nodes. Don't change it, this is not mandatory for adding nodes.
struct root_node : node
{
node core { *this, "Core" };
node vfs { *this, "VFS" };
node log { *this, "Log" };
node video { *this, "Video" };
node audio { *this, "Audio" };
node io { *this, "Input/Output" };
node sys { *this, "System" };
node net { *this, "Net" };
node misc { *this, "Miscellaneous" };
};
// Get global configuration root instance
extern root_node& get_root();
// Global configuration root instance (cached reference)
static root_node& root = get_root();
}
// Registered log channel
#define LOG_CHANNEL(name) _log::channel name(#name, _log::level::notice); namespace _log { cfg::enum_entry<_log::level, true> name(cfg::root.log, #name, ::name.enabled); }

File diff suppressed because it is too large Load Diff

View File

@ -1,29 +1,47 @@
#pragma once #pragma once
namespace fom // file open mode #include <memory>
{ #include <string>
enum open_mode : u32 #include <vector>
{ #include <type_traits>
read = 1 << 0, // enable reading
write = 1 << 1, // enable writing
append = 1 << 2, // enable appending (always write to the end of file)
create = 1 << 3, // create file if it doesn't exist
trunc = 1 << 4, // clear opened file if it's not empty
excl = 1 << 5, // failure if the file already exists (used with `create`)
rewrite = write | create | trunc, #include "types.h"
};
};
namespace fs namespace fs
{ {
enum seek_mode : u32 // file seek mode // File open mode flags
enum struct open_mode : u32
{
read,
write,
append,
create,
trunc,
excl,
};
constexpr mset<open_mode> read = open_mode::read; // Enable reading
constexpr mset<open_mode> write = open_mode::write; // Enable writing
constexpr mset<open_mode> append = open_mode::append; // Always append to the end of the file
constexpr mset<open_mode> create = open_mode::create; // Create file if it doesn't exist
constexpr mset<open_mode> trunc = open_mode::trunc; // Clear opened file if it's not empty
constexpr mset<open_mode> excl = open_mode::excl; // Failure if the file already exists (used with `create`)
constexpr mset<open_mode> rewrite = write + create + trunc;
// File seek mode
enum class seek_mode : u32
{ {
seek_set, seek_set,
seek_cur, seek_cur,
seek_end, seek_end,
}; };
constexpr auto seek_set = seek_mode::seek_set; // From beginning
constexpr auto seek_cur = seek_mode::seek_cur; // From current position
constexpr auto seek_end = seek_mode::seek_end; // From end
// File attributes (TODO)
struct stat_t struct stat_t
{ {
bool is_directory; bool is_directory;
@ -34,7 +52,57 @@ namespace fs
s64 ctime; s64 ctime;
}; };
// Get parent directory for the path (returns empty string on failure) // File handle base
struct file_base
{
virtual ~file_base() = default;
virtual stat_t stat() = 0;
virtual bool trunc(u64 length) = 0;
virtual u64 read(void* buffer, u64 size) = 0;
virtual u64 write(const void* buffer, u64 size) = 0;
virtual u64 seek(s64 offset, seek_mode whence) = 0;
virtual u64 size() = 0;
};
// Directory entry (TODO)
struct dir_entry : stat_t
{
std::string name;
};
// Directory handle base
struct dir_base
{
virtual ~dir_base() = default;
virtual bool read(dir_entry&) = 0;
virtual void rewind() = 0;
};
// Virtual device
struct device_base
{
virtual ~device_base() = default;
virtual bool stat(const std::string& path, stat_t& info) = 0;
virtual bool remove_dir(const std::string& path) = 0;
virtual bool create_dir(const std::string& path) = 0;
virtual bool rename(const std::string& from, const std::string& to) = 0;
virtual bool remove(const std::string& path) = 0;
virtual bool trunc(const std::string& path, u64 length) = 0;
virtual std::unique_ptr<file_base> open(const std::string& path, mset<open_mode> mode) = 0;
virtual std::unique_ptr<dir_base> open_dir(const std::string& path) = 0;
};
// Get virtual device for specified path (nullptr for real path)
std::shared_ptr<device_base> get_virtual_device(const std::string& path);
// Set virtual device with specified name (nullptr for deletion)
std::shared_ptr<device_base> set_virtual_device(const std::string& root_name, const std::shared_ptr<device_base>&);
// Try to get parent directory (returns empty string on failure)
std::string get_parent_dir(const std::string& path); std::string get_parent_dir(const std::string& path);
// Get file information // Get file information
@ -72,77 +140,105 @@ namespace fs
class file final class file final
{ {
using handle_type = std::intptr_t; std::unique_ptr<file_base> m_file;
constexpr static handle_type null = -1; [[noreturn]] void xnull() const;
[[noreturn]] void xfail() const;
handle_type m_fd = null;
friend class file_read_map;
friend class file_write_map;
public: public:
// Default constructor
file() = default; file() = default;
explicit file(const std::string& path, u32 mode = fom::read) // Open file with specified mode
explicit file(const std::string& path, mset<open_mode> mode = ::fs::read)
{ {
open(path, mode); open(path, mode);
} }
file(file&& other) // Open file with specified mode
: m_fd(other.m_fd) bool open(const std::string& path, mset<open_mode> mode = ::fs::read);
{
other.m_fd = null;
}
file& operator =(file&& right) // Open memory for read
{ explicit file(const void* ptr, std::size_t size);
std::swap(m_fd, right.m_fd);
return *this;
}
~file(); // Open vector
explicit file(std::vector<char>& vec);
// Check whether the handle is valid (opened file)
bool is_opened() const
{
return m_fd != null;
}
// Check whether the handle is valid (opened file) // Check whether the handle is valid (opened file)
explicit operator bool() const explicit operator bool() const
{ {
return is_opened(); return m_file.operator bool();
} }
// Open specified file with specified mode // Close the file explicitly
bool open(const std::string& path, u32 mode = fom::read); void close()
{
m_file.reset();
}
void reset(std::unique_ptr<file_base>&& ptr)
{
m_file = std::move(ptr);
}
std::unique_ptr<file_base> release()
{
return std::move(m_file);
}
// Change file size (possibly appending zero bytes) // Change file size (possibly appending zero bytes)
bool trunc(u64 size) const; bool trunc(u64 length) const
{
if (!m_file) xnull();
return m_file->trunc(length);
}
// Get file information // Get file information
bool stat(stat_t& info) const; stat_t stat() const
{
// Close the file explicitly (destructor automatically closes the file) if (!m_file) xnull();
void close(); return m_file->stat();
}
// Read the data from the file and return the amount of data written in buffer // Read the data from the file and return the amount of data written in buffer
u64 read(void* buffer, u64 count) const; u64 read(void* buffer, u64 count) const
{
if (!m_file) xnull();
return m_file->read(buffer, count);
}
// Write the data to the file and return the amount of data actually written // Write the data to the file and return the amount of data actually written
u64 write(const void* buffer, u64 count) const; u64 write(const void* buffer, u64 count) const
{
if (!m_file) xnull();
return m_file->write(buffer, count);
}
// Move file pointer // Change current position, returns previous position
u64 seek(s64 offset, seek_mode whence = seek_set) const; u64 seek(s64 offset, seek_mode whence = seek_set) const
{
if (!m_file) xnull();
return m_file->seek(offset, whence);
}
// Get file size // Get file size
u64 size() const; u64 size() const
{
if (!m_file) xnull();
return m_file->size();
}
// Get current position
u64 pos() const
{
if (!m_file) xnull();
return m_file->seek(0, seek_cur);
}
// Write std::string unconditionally // Write std::string unconditionally
const file& write(const std::string& str) const const file& write(const std::string& str) const
{ {
CHECK_ASSERTION(write(str.data(), str.size()) == str.size()); if (write(str.data(), str.size()) != str.size()) xfail();
return *this; return *this;
} }
@ -150,7 +246,7 @@ namespace fs
template<typename T> template<typename T>
std::enable_if_t<std::is_pod<T>::value && !std::is_pointer<T>::value, const file&> write(const T& data) const std::enable_if_t<std::is_pod<T>::value && !std::is_pointer<T>::value, const file&> write(const T& data) const
{ {
CHECK_ASSERTION(write(std::addressof(data), sizeof(T)) == sizeof(T)); if (write(std::addressof(data), sizeof(T)) != sizeof(T)) xfail();
return *this; return *this;
} }
@ -158,7 +254,7 @@ namespace fs
template<typename T> template<typename T>
std::enable_if_t<std::is_pod<T>::value && !std::is_pointer<T>::value, const file&> write(const std::vector<T>& vec) const std::enable_if_t<std::is_pod<T>::value && !std::is_pointer<T>::value, const file&> write(const std::vector<T>& vec) const
{ {
CHECK_ASSERTION(write(vec.data(), vec.size() * sizeof(T)) == vec.size() * sizeof(T)); if (write(vec.data(), vec.size() * sizeof(T)) != vec.size() * sizeof(T)) xfail();
return *this; return *this;
} }
@ -187,7 +283,7 @@ namespace fs
std::enable_if_t<std::is_pod<T>::value && !std::is_pointer<T>::value, T> read() const std::enable_if_t<std::is_pod<T>::value && !std::is_pointer<T>::value, T> read() const
{ {
T result; T result;
CHECK_ASSERTION(read(result)); if (!read(result)) xfail();
return result; return result;
} }
@ -196,7 +292,7 @@ namespace fs
{ {
std::string result; std::string result;
result.resize(size()); result.resize(size());
CHECK_ASSERTION(seek(0) != -1 && read(result)); if (seek(0), !read(result)) xfail();
return result; return result;
} }
@ -206,164 +302,69 @@ namespace fs
{ {
std::vector<T> result; std::vector<T> result;
result.resize(size() / sizeof(T)); result.resize(size() / sizeof(T));
CHECK_ASSERTION(seek(0) != -1 && read(result)); if (seek(0), !read(result)) xfail();
return result; return result;
} }
}; };
// TODO
class file_read_map final
{
char* m_ptr = nullptr;
u64 m_size;
public:
file_read_map() = default;
file_read_map(file_read_map&& right)
: m_ptr(right.m_ptr)
, m_size(right.m_size)
{
right.m_ptr = 0;
}
file_read_map& operator =(file_read_map&& right)
{
std::swap(m_ptr, right.m_ptr);
std::swap(m_size, right.m_size);
return *this;
}
file_read_map(const file& f)
{
reset(f);
}
~file_read_map()
{
reset();
}
// Open file mapping
void reset(const file& f);
// Close file mapping
void reset();
// Get pointer
operator const char*() const
{
return m_ptr;
}
};
// TODO
class file_write_map final
{
char* m_ptr = nullptr;
u64 m_size;
public:
file_write_map() = default;
file_write_map(file_write_map&& right)
: m_ptr(right.m_ptr)
, m_size(right.m_size)
{
right.m_ptr = 0;
}
file_write_map& operator =(file_write_map&& right)
{
std::swap(m_ptr, right.m_ptr);
std::swap(m_size, right.m_size);
return *this;
}
file_write_map(const file& f)
{
reset(f);
}
~file_write_map()
{
reset();
}
// Open file mapping
void reset(const file& f);
// Close file mapping
void reset();
// Get pointer
operator char*() const
{
return m_ptr;
}
};
class dir final class dir final
{ {
std::unique_ptr<char[]> m_path; std::unique_ptr<dir_base> m_dir;
std::intptr_t m_dd; // handle (aux)
[[noreturn]] void xnull() const;
public: public:
dir() = default; dir() = default;
explicit dir(const std::string& dirname) // Open dir handle
explicit dir(const std::string& path)
{ {
open(dirname); open(path);
} }
dir(dir&& other) // Open specified directory
: m_dd(other.m_dd) bool open(const std::string& path);
, m_path(std::move(other.m_path))
{
}
dir& operator =(dir&& right)
{
std::swap(m_dd, right.m_dd);
std::swap(m_path, right.m_path);
return *this;
}
~dir();
// Check whether the handle is valid (opened directory)
bool is_opened() const
{
return m_path.operator bool();
}
// Check whether the handle is valid (opened directory) // Check whether the handle is valid (opened directory)
explicit operator bool() const explicit operator bool() const
{ {
return is_opened(); return m_dir.operator bool();
} }
// Open specified directory // Close the directory explicitly
bool open(const std::string& dirname); void close()
// Close the directory explicitly (destructor automatically closes the directory)
void close();
// Get next directory entry (UTF-8 name and file stat)
bool read(std::string& name, stat_t& info);
bool first(std::string& name, stat_t& info);
struct entry
{ {
std::string name; m_dir.reset();
stat_t info; }
};
void reset(std::unique_ptr<dir_base>&& ptr)
{
m_dir = std::move(ptr);
}
std::unique_ptr<dir_base> release()
{
return std::move(m_dir);
}
// Get next directory entry
bool read(dir_entry& out) const
{
if (!m_dir) xnull();
return m_dir->read(out);
}
// Reset to the beginning
void rewind() const
{
if (!m_dir) xnull();
return m_dir->rewind();
}
class iterator class iterator
{ {
entry m_entry;
dir* m_parent; dir* m_parent;
dir_entry m_entry;
public: public:
enum class mode enum class mode
@ -382,20 +383,16 @@ namespace fs
if (mode_ == mode::from_first) if (mode_ == mode::from_first)
{ {
m_parent->first(m_entry.name, m_entry.info); m_parent->rewind();
}
else
{
m_parent->read(m_entry.name, m_entry.info);
} }
if (m_entry.name.empty()) if (!m_parent->read(m_entry))
{ {
m_parent = nullptr; m_parent = nullptr;
} }
} }
entry& operator *() dir_entry& operator *()
{ {
return m_entry; return m_entry;
} }
@ -414,7 +411,7 @@ namespace fs
iterator begin() iterator begin()
{ {
return{ this }; return{ m_dir ? this : nullptr };
} }
iterator end() iterator end()
@ -428,4 +425,10 @@ namespace fs
// Get executable directory // Get executable directory
const std::string& get_executable_dir(); const std::string& get_executable_dir();
// Delete directory and all its contents recursively
void remove_all(const std::string& path);
// Get size of all files recursively
u64 get_dir_size(const std::string& path);
} }

View File

@ -1,26 +1,17 @@
#include "stdafx.h" #include "Log.h"
#include "Thread.h"
#include "File.h"
#include "Log.h"
#ifdef _WIN32
#include <Windows.h>
#endif
namespace _log namespace _log
{ {
logger& get_logger() static file_listener& get_logger()
{ {
// Use magic static for global logger instance // Use magic static
static logger instance; static file_listener logger("RPCS3.log");
return instance; return logger;
} }
file_listener g_log_file(_PRGNAME_ ".log");
file_writer g_tty_file("TTY.log"); file_writer g_tty_file("TTY.log");
channel GENERAL("", level::notice); channel GENERAL(nullptr, level::notice);
channel LOADER("LDR", level::notice); channel LOADER("LDR", level::notice);
channel MEMORY("MEM", level::notice); channel MEMORY("MEM", level::notice);
channel RSX("RSX", level::notice); channel RSX("RSX", level::notice);
@ -28,72 +19,29 @@ namespace _log
channel PPU("PPU", level::notice); channel PPU("PPU", level::notice);
channel SPU("SPU", level::notice); channel SPU("SPU", level::notice);
channel ARMv7("ARMv7"); channel ARMv7("ARMv7");
}
_log::listener::listener() thread_local std::string(*g_tls_make_prefix)(const channel&, level, const std::string&) = nullptr;
{
// Register self
get_logger().add_listener(this);
}
_log::listener::~listener()
{
// Unregister self
get_logger().remove_listener(this);
}
_log::channel::channel(const std::string& name, _log::level init_level)
: name{ name }
, enabled{ init_level }
{
// TODO: register config property "name" associated with "enabled" member
}
void _log::logger::add_listener(_log::listener* listener)
{
std::lock_guard<shared_mutex> lock(m_mutex);
m_listeners.emplace(listener);
}
void _log::logger::remove_listener(_log::listener* listener)
{
std::lock_guard<shared_mutex> lock(m_mutex);
m_listeners.erase(listener);
}
void _log::logger::broadcast(const _log::channel& ch, _log::level sev, const std::string& text) const
{
reader_lock lock(m_mutex);
for (auto listener : m_listeners)
{
listener->log(ch, sev, text);
}
} }
void _log::broadcast(const _log::channel& ch, _log::level sev, const std::string& text) void _log::broadcast(const _log::channel& ch, _log::level sev, const std::string& text)
{ {
get_logger().broadcast(ch, sev, text); get_logger().log(ch, sev, text);
} }
[[noreturn]] extern void catch_all_exceptions();
_log::file_writer::file_writer(const std::string& name) _log::file_writer::file_writer(const std::string& name)
{ {
try try
{ {
if (!m_file.open(fs::get_config_dir() + name, fom::rewrite | fom::append)) if (!m_file.open(fs::get_config_dir() + name, fs::rewrite + fs::append))
{ {
throw EXCEPTION("Can't create log file %s (error %d)", name, errno); throw fmt::exception("Can't create log file %s (error %d)", name, errno);
} }
} }
catch (const fmt::exception& e) catch (...)
{ {
#ifdef _WIN32 catch_all_exceptions();
MessageBoxA(0, e.what(), "_log::file_writer() failed", MB_ICONERROR);
#else
std::printf("_log::file_writer() failed: %s\n", e.what());
#endif
} }
} }
@ -104,7 +52,7 @@ void _log::file_writer::log(const std::string& text)
std::size_t _log::file_writer::size() const std::size_t _log::file_writer::size() const
{ {
return m_file.seek(0, fs::seek_cur); return m_file.pos();
} }
void _log::file_listener::log(const _log::channel& ch, _log::level sev, const std::string& text) void _log::file_listener::log(const _log::channel& ch, _log::level sev, const std::string& text)
@ -126,14 +74,14 @@ void _log::file_listener::log(const _log::channel& ch, _log::level sev, const st
// TODO: print time? // TODO: print time?
if (auto t = thread_ctrl::get_current()) if (auto func = g_tls_make_prefix)
{ {
msg += '{'; msg += '{';
msg += t->get_name(); msg += func(ch, sev, text);
msg += "} "; msg += "} ";
} }
if (ch.name.size()) if (ch.name)
{ {
msg += ch.name; msg += ch.name;
msg += sev == level::todo ? " TODO: " : ": "; msg += sev == level::todo ? " TODO: " : ": ";

View File

@ -1,6 +1,9 @@
#pragma once #pragma once
#include "SharedMutex.h" #include "types.h"
#include "Atomic.h"
#include "File.h"
#include "StrFmt.h"
namespace _log namespace _log
{ {
@ -19,40 +22,24 @@ namespace _log
struct channel; struct channel;
struct listener; struct listener;
// Log manager
class logger final
{
mutable shared_mutex m_mutex;
std::set<listener*> m_listeners;
public:
// Register listener
void add_listener(listener* listener);
// Unregister listener
void remove_listener(listener* listener);
// Send log message to all listeners
void broadcast(const channel& ch, level sev, const std::string& text) const;
};
// Send log message to global logger instance // Send log message to global logger instance
void broadcast(const channel& ch, level sev, const std::string& text); void broadcast(const channel& ch, level sev, const std::string& text);
// Log channel (source) // Log channel
struct channel struct channel
{ {
// Channel prefix (also used for identification) // Channel prefix (added to every log message)
const std::string name; const char* const name;
// The lowest logging level enabled for this channel (used for early filtering) // The lowest logging level enabled for this channel (used for early filtering)
std::atomic<level> enabled; atomic_t<level> enabled;
// Initialization (max level enabled by default) // Constant initialization: name and initial log level
channel(const std::string& name, level = level::trace); constexpr channel(const char* name, level enabled = level::trace)
: name{ name }
virtual ~channel() = default; , enabled{ enabled }
{
}
// Log without formatting // Log without formatting
force_inline void log(level sev, const std::string& text) const force_inline void log(level sev, const std::string& text) const
@ -71,7 +58,7 @@ namespace _log
#define GEN_LOG_METHOD(_sev)\ #define GEN_LOG_METHOD(_sev)\
template<typename... Args>\ template<typename... Args>\
force_inline void _sev(const char* fmt, const Args&... args)\ force_inline void _sev(const char* fmt, const Args&... args) const\
{\ {\
return format<Args...>(level::_sev, fmt, args...);\ return format<Args...>(level::_sev, fmt, args...);\
} }
@ -90,9 +77,9 @@ namespace _log
// Log listener (destination) // Log listener (destination)
struct listener struct listener
{ {
listener(); listener() = default;
virtual ~listener(); virtual ~listener() = default;
virtual void log(const channel& ch, level sev, const std::string& text) = 0; virtual void log(const channel& ch, level sev, const std::string& text) = 0;
}; };
@ -126,9 +113,6 @@ namespace _log
virtual void log(const channel& ch, level sev, const std::string& text) override; virtual void log(const channel& ch, level sev, const std::string& text) override;
}; };
// Global variable for RPCS3.log
extern file_listener g_log_file;
// Global variable for TTY.log // Global variable for TTY.log
extern file_writer g_tty_file; extern file_writer g_tty_file;
@ -142,8 +126,26 @@ namespace _log
extern channel PPU; extern channel PPU;
extern channel SPU; extern channel SPU;
extern channel ARMv7; extern channel ARMv7;
extern thread_local std::string(*g_tls_make_prefix)(const channel&, level, const std::string&);
} }
template<>
struct bijective<_log::level, const char*>
{
static constexpr std::pair<_log::level, const char*> map[]
{
{ _log::level::always, "Nothing" },
{ _log::level::fatal, "Fatal" },
{ _log::level::error, "Error" },
{ _log::level::todo, "TODO" },
{ _log::level::success, "Success" },
{ _log::level::warning, "Warning" },
{ _log::level::notice, "Notice" },
{ _log::level::trace, "Trace" },
};
};
// Legacy: // Legacy:
#define LOG_SUCCESS(ch, fmt, ...) _log::ch.success(fmt, ##__VA_ARGS__) #define LOG_SUCCESS(ch, fmt, ...) _log::ch.success(fmt, ##__VA_ARGS__)

79
Utilities/Macro.h Normal file
View File

@ -0,0 +1,79 @@
#pragma once
#include <cstdint>
#include <exception>
template<typename T, typename = std::enable_if_t<std::is_integral<T>::value>>
constexpr T align(const T& value, std::uint64_t align)
{
return static_cast<T>((value + (align - 1)) & ~(align - 1));
}
template<typename To, typename From>
constexpr To narrow_impl(const To& result, const From& value, const char* message)
{
return static_cast<From>(result) != value ? throw std::runtime_error(message) : result;
}
// Narrow cast (similar to gsl::narrow) with fixed message
template<typename To, typename From>
constexpr auto narrow(const From& value, const char* fixed_msg = "::narrow() failed") -> decltype(static_cast<To>(static_cast<From>(std::declval<To>())))
{
return narrow_impl(static_cast<To>(value), value, fixed_msg);
}
// Return 32 bit .size() for container
template<typename CT>
constexpr auto size32(const CT& container, const char* fixed_msg = "::size32() failed") -> decltype(static_cast<std::uint32_t>(container.size()))
{
return narrow<std::uint32_t>(container.size(), fixed_msg);
}
// Return 32 bit size for an array
template<typename T, std::size_t Size>
constexpr std::uint32_t size32(const T(&)[Size])
{
static_assert(Size <= UINT32_MAX, "size32() error: too big");
return static_cast<std::uint32_t>(Size);
}
#define CHECK_SIZE(type, size) static_assert(sizeof(type) == size, "Invalid " #type " type size")
#define CHECK_ALIGN(type, align) static_assert(alignof(type) == align, "Invalid " #type " type alignment")
#define CHECK_MAX_SIZE(type, size) static_assert(sizeof(type) <= size, #type " type size is too big")
#define CHECK_SIZE_ALIGN(type, size, align) CHECK_SIZE(type, size); CHECK_ALIGN(type, align)
// Return 32 bit sizeof() to avoid widening/narrowing conversions with size_t
#define SIZE_32(type) static_cast<std::uint32_t>(sizeof(type))
// Return 32 bit alignof() to avoid widening/narrowing conversions with size_t
#define ALIGN_32(type) static_cast<std::uint32_t>(alignof(type))
// Return 32 bit custom offsetof()
#define OFFSET_32(type, x) static_cast<std::uint32_t>(reinterpret_cast<std::uintptr_t>(&reinterpret_cast<const volatile char&>(reinterpret_cast<type*>(0ull)->x)))
// Sometimes to avoid writing std::remove_cv_t<>, example: std::is_same<CV T1, CV T2>
#define CV const volatile
#define CONCATENATE_DETAIL(x, y) x ## y
#define CONCATENATE(x, y) CONCATENATE_DETAIL(x, y)
#define STRINGIZE_DETAIL(x) #x
#define STRINGIZE(x) STRINGIZE_DETAIL(x)
// Macro set, allows to hide "return" in simple lambda expressions.
#define WRAP_EXPR(expr, ...) [&](__VA_ARGS__) { return expr; }
#define COPY_EXPR(expr, ...) [=](__VA_ARGS__) { return expr; }
#define PURE_EXPR(expr, ...) [] (__VA_ARGS__) { return expr; }
#define HERE "\n(in file " __FILE__ ":" STRINGIZE(__LINE__) ")"
// Ensure that the expression is evaluated to true. Always evaluated and allowed to have side effects (unlike assert() macro).
#define ASSERT(expr) if (!(expr)) throw std::runtime_error("Assertion failed: " #expr HERE)
// Expects() and Ensures() are intended to check function arguments and results.
// Expressions are not guaranteed to evaluate. Redefinition with ASSERT macro for better unification.
#define Expects ASSERT
#define Ensures ASSERT
#define DECLARE(static_member) decltype(static_member) static_member
#define STR_CASE(value) case value: return #value

View File

@ -1,4 +1,4 @@
#include "GNU.h" #include "Platform.h"
#ifdef __APPLE__ #ifdef __APPLE__
#include <sys/types.h> #include <sys/types.h>

View File

@ -1,17 +1,22 @@
#pragma once #pragma once
#include <cstdint>
#include <immintrin.h>
#include <emmintrin.h> #include <emmintrin.h>
#if defined(_MSC_VER) && _MSC_VER <= 1800 #define IS_LE_MACHINE 1
#define thread_local __declspec(thread) #define IS_BE_MACHINE 0
#elif __APPLE__
#define thread_local __thread #ifdef _MSC_VER
#include <intrin.h>
#else
#include <x86intrin.h>
#endif #endif
#if defined(_MSC_VER) // Some platforms don't support thread_local well yet.
#define never_inline __declspec(noinline) #ifndef _MSC_VER
#else #define thread_local __thread
#define never_inline __attribute__((noinline)) #define __assume(cond) do { if (!(cond)) __builtin_unreachable(); } while (0)
#endif #endif
#if defined(_MSC_VER) #if defined(_MSC_VER)
@ -20,20 +25,21 @@
#define safe_buffers #define safe_buffers
#endif #endif
#if defined(_MSC_VER)
#define never_inline __declspec(noinline)
#else
#define never_inline __attribute__((noinline))
#endif
#if defined(_MSC_VER) #if defined(_MSC_VER)
#define force_inline __forceinline #define force_inline __forceinline
#else #else
#define force_inline __attribute__((always_inline)) inline #define force_inline __attribute__((always_inline)) inline
#endif #endif
#if defined(_MSC_VER) && _MSC_VER <= 1800
#define alignas(x) _CRT_ALIGN(x)
#endif
#if defined(__GNUG__) #if defined(__GNUG__)
#include <stdlib.h> #include <stdlib.h>
#include <cstdint>
#define _fpclass(x) std::fpclassify(x) #define _fpclass(x) std::fpclassify(x)
#define INFINITE 0xFFFFFFFF #define INFINITE 0xFFFFFFFF
@ -59,170 +65,6 @@ int clock_gettime(clockid_t clk_id, struct timespec *tp);
#endif /* __APPLE__ */ #endif /* __APPLE__ */
#endif /* __GNUG__ */ #endif /* __GNUG__ */
#if defined(_MSC_VER)
// Unsigned 128-bit integer implementation
struct alignas(16) u128
{
std::uint64_t lo, hi;
u128() = default;
u128(const u128&) = default;
u128(std::uint64_t l)
: lo(l)
, hi(0)
{
}
u128 operator +(const u128& r) const
{
u128 value;
_addcarry_u64(_addcarry_u64(0, r.lo, lo, &value.lo), r.hi, hi, &value.hi);
return value;
}
friend u128 operator +(const u128& l, std::uint64_t r)
{
u128 value;
_addcarry_u64(_addcarry_u64(0, r, l.lo, &value.lo), l.hi, 0, &value.hi);
return value;
}
friend u128 operator +(std::uint64_t l, const u128& r)
{
u128 value;
_addcarry_u64(_addcarry_u64(0, r.lo, l, &value.lo), 0, r.hi, &value.hi);
return value;
}
u128 operator -(const u128& r) const
{
u128 value;
_subborrow_u64(_subborrow_u64(0, r.lo, lo, &value.lo), r.hi, hi, &value.hi);
return value;
}
friend u128 operator -(const u128& l, std::uint64_t r)
{
u128 value;
_subborrow_u64(_subborrow_u64(0, r, l.lo, &value.lo), 0, l.hi, &value.hi);
return value;
}
friend u128 operator -(std::uint64_t l, const u128& r)
{
u128 value;
_subborrow_u64(_subborrow_u64(0, r.lo, l, &value.lo), r.hi, 0, &value.hi);
return value;
}
u128 operator +() const
{
return *this;
}
u128 operator -() const
{
u128 value;
_subborrow_u64(_subborrow_u64(0, lo, 0, &value.lo), hi, 0, &value.hi);
return value;
}
u128& operator ++()
{
_addcarry_u64(_addcarry_u64(0, 1, lo, &lo), 0, hi, &hi);
return *this;
}
u128 operator ++(int)
{
u128 value = *this;
_addcarry_u64(_addcarry_u64(0, 1, lo, &lo), 0, hi, &hi);
return value;
}
u128& operator --()
{
_subborrow_u64(_subborrow_u64(0, 1, lo, &lo), 0, hi, &hi);
return *this;
}
u128 operator --(int)
{
u128 value = *this;
_subborrow_u64(_subborrow_u64(0, 1, lo, &lo), 0, hi, &hi);
return value;
}
u128 operator ~() const
{
u128 value;
value.lo = ~lo;
value.hi = ~hi;
return value;
}
u128 operator &(const u128& r) const
{
u128 value;
value.lo = lo & r.lo;
value.hi = hi & r.hi;
return value;
}
u128 operator |(const u128& r) const
{
u128 value;
value.lo = lo | r.lo;
value.hi = hi | r.hi;
return value;
}
u128 operator ^(const u128& r) const
{
u128 value;
value.lo = lo ^ r.lo;
value.hi = hi ^ r.hi;
return value;
}
u128& operator +=(const u128& r)
{
_addcarry_u64(_addcarry_u64(0, r.lo, lo, &lo), r.hi, hi, &hi);
return *this;
}
u128& operator +=(uint64_t r)
{
_addcarry_u64(_addcarry_u64(0, r, lo, &lo), 0, hi, &hi);
return *this;
}
u128& operator &=(const u128& r)
{
lo &= r.lo;
hi &= r.hi;
return *this;
}
u128& operator |=(const u128& r)
{
lo |= r.lo;
hi |= r.hi;
return *this;
}
u128& operator ^=(const u128& r)
{
lo ^= r.lo;
hi ^= r.hi;
return *this;
}
};
#endif
inline std::uint32_t cntlz32(std::uint32_t arg) inline std::uint32_t cntlz32(std::uint32_t arg)
{ {
#if defined(_MSC_VER) #if defined(_MSC_VER)
@ -243,7 +85,67 @@ inline std::uint64_t cntlz64(std::uint64_t arg)
#endif #endif
} }
// compare 16 packed unsigned bytes (greater than) template<typename T>
struct add_flags_result_t
{
T result;
bool carry;
//bool overflow;
bool zero;
bool sign;
add_flags_result_t() = default;
// Straighforward ADD with flags
add_flags_result_t(T a, T b)
: result(a + b)
, carry(result < a)
//, overflow((result ^ ~(a ^ b)) >> (sizeof(T) * 8 - 1) != 0)
, zero(result == 0)
, sign(result >> (sizeof(T) * 8 - 1) != 0)
{
}
// Straighforward ADC with flags
add_flags_result_t(T a, T b, bool c)
: add_flags_result_t(a, b)
{
add_flags_result_t r(result, c);
result = r.result;
carry |= r.carry;
//overflow |= r.overflow;
zero = r.zero;
sign = r.sign;
}
};
inline add_flags_result_t<std::uint32_t> add32_flags(std::uint32_t a, std::uint32_t b)
{
//add_flags_result_t<std::uint32_t> r;
//r.carry = _addcarry_u32(0, a, b, &r.result) != 0;
//r.zero = r.result == 0;
//r.sign = r.result >> 31;
//return r;
return{ a, b };
}
inline add_flags_result_t<std::uint32_t> add32_flags(std::uint32_t a, std::uint32_t b, bool c)
{
return{ a, b, c };
}
inline add_flags_result_t<std::uint64_t> add64_flags(std::uint64_t a, std::uint64_t b)
{
return{ a, b };
}
inline add_flags_result_t<std::uint64_t> add64_flags(std::uint64_t a, std::uint64_t b, bool c)
{
return{ a, b, c };
}
// Compare 16 packed unsigned bytes (greater than)
inline __m128i sse_cmpgt_epu8(__m128i A, __m128i B) inline __m128i sse_cmpgt_epu8(__m128i A, __m128i B)
{ {
// (A xor 0x80) > (B xor 0x80) // (A xor 0x80) > (B xor 0x80)
@ -290,3 +192,36 @@ inline __m128 sse_log2_ps(__m128 A)
const auto x8 = _mm_cvtepi32_ps(_mm_sub_epi32(_mm_srli_epi32(_mm_castps_si128(x0), 23), _mm_set1_epi32(127))); const auto x8 = _mm_cvtepi32_ps(_mm_sub_epi32(_mm_srli_epi32(_mm_castps_si128(x0), 23), _mm_set1_epi32(127)));
return _mm_add_ps(_mm_mul_ps(_mm_mul_ps(_mm_mul_ps(_mm_mul_ps(x5, x6), x7), x4), _c), _mm_add_ps(_mm_mul_ps(x4, _c), x8)); return _mm_add_ps(_mm_mul_ps(_mm_mul_ps(_mm_mul_ps(_mm_mul_ps(x5, x6), x7), x4), _c), _mm_add_ps(_mm_mul_ps(x4, _c), x8));
} }
// Helper function, used by ""_u16, ""_u32, ""_u64
constexpr std::uint8_t to_u8(char c)
{
return static_cast<std::uint8_t>(c);
}
// Convert 2-byte string to u16 value like reinterpret_cast does
constexpr std::uint16_t operator""_u16(const char* s, std::size_t length)
{
return length != 2 ? throw s :
#if IS_LE_MACHINE == 1
to_u8(s[1]) << 8 | to_u8(s[0]);
#endif
}
// Convert 4-byte string to u32 value like reinterpret_cast does
constexpr std::uint32_t operator""_u32(const char* s, std::size_t length)
{
return length != 4 ? throw s :
#if IS_LE_MACHINE == 1
to_u8(s[3]) << 24 | to_u8(s[2]) << 16 | to_u8(s[1]) << 8 | to_u8(s[0]);
#endif
}
// Convert 8-byte string to u64 value like reinterpret_cast does
constexpr std::uint64_t operator""_u64(const char* s, std::size_t length)
{
return length != 8 ? throw s :
#if IS_LE_MACHINE == 1
static_cast<std::uint64_t>(to_u8(s[7]) << 24 | to_u8(s[6]) << 16 | to_u8(s[5]) << 8 | to_u8(s[4])) << 32 | to_u8(s[3]) << 24 | to_u8(s[2]) << 16 | to_u8(s[1]) << 8 | to_u8(s[0]);
#endif
}

View File

@ -10,7 +10,7 @@ bool semaphore_t::try_wait()
} }
// try to decrement m_value atomically // try to decrement m_value atomically
const auto old = m_var.atomic_op([](sync_var_t& var) const auto old = m_var.fetch_op([](sync_var_t& var)
{ {
if (var.value) if (var.value)
{ {
@ -36,7 +36,7 @@ bool semaphore_t::try_post()
} }
// try to increment m_value atomically // try to increment m_value atomically
const auto old = m_var.atomic_op([&](sync_var_t& var) const auto old = m_var.fetch_op([&](sync_var_t& var)
{ {
if (var.value < max_value) if (var.value < max_value)
{ {

View File

@ -8,7 +8,7 @@ class semaphore_t
// semaphore condition variable // semaphore condition variable
std::condition_variable m_cv; std::condition_variable m_cv;
struct sync_var_t struct alignas(8) sync_var_t
{ {
u32 value; // current semaphore value u32 value; // current semaphore value
u32 waiters; // current amount of waiters u32 waiters; // current amount of waiters

View File

@ -1,103 +0,0 @@
#include "stdafx.h"
#include "SharedMutex.h"
void shared_mutex::impl_lock_shared(u32 old_value)
{
// Throw if reader count breaks the "second" limit (it should be impossible)
CHECK_ASSERTION((old_value & SM_READER_COUNT) != SM_READER_COUNT);
std::unique_lock<std::mutex> lock(m_mutex);
// Notify non-zero reader queue size
m_ctrl |= SM_READER_QUEUE;
// Compensate incorrectly increased reader count
if ((--m_ctrl & SM_READER_COUNT) == 0 && m_wq_size)
{
// Notify current exclusive owner (condition passed)
m_ocv.notify_one();
}
CHECK_ASSERTION(++m_rq_size);
// Obtain the reader lock
while (!atomic_op(m_ctrl, op_lock_shared))
{
m_rcv.wait(lock);
}
CHECK_ASSERTION(m_rq_size--);
if (m_rq_size == 0)
{
m_ctrl &= ~SM_READER_QUEUE;
}
}
void shared_mutex::impl_unlock_shared(u32 new_value)
{
// Throw if reader count was zero
CHECK_ASSERTION((new_value & SM_READER_COUNT) != SM_READER_COUNT);
// Mutex cannot be unlocked before notification because m_ctrl has been changed outside
std::lock_guard<std::mutex> lock(m_mutex);
if (m_wq_size && (new_value & SM_READER_COUNT) == 0)
{
// Notify current exclusive owner that the latest reader is gone
m_ocv.notify_one();
}
else if (m_rq_size)
{
m_rcv.notify_one();
}
}
void shared_mutex::impl_lock_excl(u32 value)
{
std::unique_lock<std::mutex> lock(m_mutex);
// Notify non-zero writer queue size
m_ctrl |= SM_WRITER_QUEUE;
CHECK_ASSERTION(++m_wq_size);
// Obtain the writer lock
while (!atomic_op(m_ctrl, op_lock_excl))
{
m_wcv.wait(lock);
}
// Wait for remaining readers
while ((m_ctrl & SM_READER_COUNT) != 0)
{
m_ocv.wait(lock);
}
CHECK_ASSERTION(m_wq_size--);
if (m_wq_size == 0)
{
m_ctrl &= ~SM_WRITER_QUEUE;
}
}
void shared_mutex::impl_unlock_excl(u32 value)
{
// Throw if was not locked exclusively
CHECK_ASSERTION(value & SM_WRITER_LOCK);
// Mutex cannot be unlocked before notification because m_ctrl has been changed outside
std::lock_guard<std::mutex> lock(m_mutex);
if (m_wq_size)
{
// Notify next exclusive owner
m_wcv.notify_one();
}
else if (m_rq_size)
{
// Notify all readers
m_rcv.notify_all();
}
}

View File

@ -1,49 +1,156 @@
#pragma once #pragma once
#include <cstdint>
#include <exception>
#include <thread>
#include <mutex>
#include <condition_variable>
#include "Atomic.h"
//! An attempt to create effective implementation of "shared mutex", lock-free in optimistic case. //! An attempt to create effective implementation of "shared mutex", lock-free in optimistic case.
//! All locking and unlocking may be done by single LOCK XADD or LOCK CMPXCHG instructions. //! All locking and unlocking may be done by single LOCK XADD or LOCK CMPXCHG instructions.
//! MSVC implementation of std::shared_timed_mutex seems suboptimal. //! MSVC implementation of std::shared_timed_mutex seems suboptimal.
//! std::shared_mutex is not available until C++17. //! std::shared_mutex is not available until C++17.
class shared_mutex final class shared_mutex final
{ {
enum : u32 using ctrl_type = u32;
enum : ctrl_type
{ {
SM_WRITER_LOCK = 1u << 31, // Exclusive lock flag, must be MSB SM_WRITER_LOCK = 1u << 31, // Exclusive lock flag, must be MSB
SM_WRITER_QUEUE = 1u << 30, // Flag set if m_wq_size != 0 SM_WAITERS_BIT = 1u << 30, // Flag set if m_wq_size or m_rq_size is non-zero
SM_READER_QUEUE = 1u << 29, // Flag set if m_rq_size != 0 SM_INVALID_BIT = 1u << 29, // Unreachable reader count bit (may be set by incorrect unlock_shared() call)
SM_READER_COUNT = SM_READER_QUEUE - 1, // Valid reader count bit mask SM_READER_MASK = SM_WAITERS_BIT - 1, // Valid reader count bit mask
SM_READER_MAX = 1u << 24, // Max reader count SM_READER_MAX = 1u << 24, // Max reader count
}; };
std::atomic<u32> m_ctrl{}; // Control atomic variable: reader count | SM_* flags atomic_t<ctrl_type> m_ctrl{}; // Control atomic variable: reader count | SM_* flags
std::thread::id m_owner{}; // Current exclusive owner (TODO: implement only for debug mode?)
std::mutex m_mutex; std::mutex m_mutex;
u32 m_rq_size{}; // Reader queue size (threads waiting on m_rcv) std::size_t m_rq_size{}; // Reader queue size (threads waiting on m_rcv)
u32 m_wq_size{}; // Writer queue size (threads waiting on m_wcv+m_ocv) std::size_t m_wq_size{}; // Writer queue size (threads waiting on m_wcv and m_ocv)
std::condition_variable m_rcv; // Reader queue std::condition_variable m_rcv; // Reader queue
std::condition_variable m_wcv; // Writer queue std::condition_variable m_wcv; // Writer queue
std::condition_variable m_ocv; // For current exclusive owner std::condition_variable m_ocv; // For current exclusive owner
static bool op_lock_shared(u32& ctrl) void lock_shared_hard()
{ {
// Check writer flags and reader limit std::unique_lock<std::mutex> lock(m_mutex);
return (ctrl & ~SM_READER_QUEUE) < SM_READER_MAX ? ctrl++, true : false;
// Validate
if ((m_ctrl & SM_INVALID_BIT) != 0) throw std::runtime_error("shared_mutex::lock_shared(): Invalid bit");
if ((m_ctrl & SM_READER_MASK) == 0) throw std::runtime_error("shared_mutex::lock_shared(): No readers");
// Notify non-zero reader queue size
m_ctrl |= SM_WAITERS_BIT, m_rq_size++;
// Fix excess reader count
if ((--m_ctrl & SM_READER_MASK) == 0 && m_wq_size)
{
// Notify exclusive owner
m_ocv.notify_one();
} }
static bool op_lock_excl(u32& ctrl) // Obtain the reader lock
while (true)
{ {
// Test and set writer lock const auto ctrl = m_ctrl.load();
return (ctrl & SM_WRITER_LOCK) == 0 ? ctrl |= SM_WRITER_LOCK, true : false;
// Check writers and reader limit
if (m_wq_size || (ctrl & ~SM_WAITERS_BIT) >= SM_READER_MAX)
{
m_rcv.wait(lock);
continue;
} }
void impl_lock_shared(u32 old_ctrl); if (m_ctrl.compare_and_swap_test(ctrl, ctrl + 1))
void impl_unlock_shared(u32 new_ctrl); {
void impl_lock_excl(u32 ctrl); break;
void impl_unlock_excl(u32 ctrl); }
}
if (!--m_rq_size && !m_wq_size)
{
m_ctrl &= ~SM_WAITERS_BIT;
}
}
void unlock_shared_notify()
{
// Mutex is locked for reliable notification because m_ctrl has been changed outside
std::lock_guard<std::mutex> lock(m_mutex);
if ((m_ctrl & SM_READER_MASK) == 0 && m_wq_size)
{
// Notify exclusive owner
m_ocv.notify_one();
}
else if (m_rq_size)
{
// Notify other readers
m_rcv.notify_one();
}
}
void lock_hard()
{
std::unique_lock<std::mutex> lock(m_mutex);
// Validate
if ((m_ctrl & SM_INVALID_BIT) != 0) throw std::runtime_error("shared_mutex::lock(): Invalid bit");
// Notify non-zero writer queue size
m_ctrl |= SM_WAITERS_BIT, m_wq_size++;
// Obtain the writer lock
while (true)
{
const auto ctrl = m_ctrl.load();
if (ctrl & SM_WRITER_LOCK)
{
m_wcv.wait(lock);
continue;
}
if (m_ctrl.compare_and_swap_test(ctrl, ctrl | SM_WRITER_LOCK))
{
break;
}
}
// Wait for remaining readers
while ((m_ctrl & SM_READER_MASK) != 0)
{
m_ocv.wait(lock);
}
if (!--m_wq_size && !m_rq_size)
{
m_ctrl &= ~SM_WAITERS_BIT;
}
}
void unlock_notify()
{
// Mutex is locked for reliable notification because m_ctrl has been changed outside
std::lock_guard<std::mutex> lock(m_mutex);
if (m_wq_size)
{
// Notify next exclusive owner
m_wcv.notify_one();
}
else if (m_rq_size)
{
// Notify all readers
m_rcv.notify_all();
}
}
public: public:
shared_mutex() = default; shared_mutex() = default;
@ -51,65 +158,49 @@ public:
// Lock in shared mode // Lock in shared mode
void lock_shared() void lock_shared()
{ {
const u32 old_ctrl = m_ctrl++; if (m_ctrl++ >= SM_READER_MAX)
// Check flags and reader limit
if (old_ctrl >= SM_READER_MAX)
{ {
impl_lock_shared(old_ctrl); lock_shared_hard();
} }
} }
// Try to lock in shared mode // Try to lock in shared mode
bool try_lock_shared() bool try_lock_shared()
{ {
return atomic_op(m_ctrl, [](u32& ctrl) auto ctrl = m_ctrl.load();
{
// Check flags and reader limit return ctrl < SM_READER_MAX && m_ctrl.compare_and_swap_test(ctrl, ctrl + 1);
return ctrl < SM_READER_MAX ? ctrl++, true : false;
});
} }
// Unlock in shared mode // Unlock in shared mode
void unlock_shared() void unlock_shared()
{ {
const u32 new_ctrl = --m_ctrl; if (m_ctrl-- >= SM_READER_MAX)
// Check if notification required
if (new_ctrl >= SM_READER_MAX)
{ {
impl_unlock_shared(new_ctrl); unlock_shared_notify();
}
}
// Lock exclusively
void lock()
{
u32 value = 0;
if (!m_ctrl.compare_exchange_strong(value, SM_WRITER_LOCK))
{
impl_lock_excl(value);
} }
} }
// Try to lock exclusively // Try to lock exclusively
bool try_lock() bool try_lock()
{ {
u32 value = 0; return m_ctrl.compare_and_swap_test(0, SM_WRITER_LOCK);
}
return m_ctrl.compare_exchange_strong(value, SM_WRITER_LOCK); // Lock exclusively
void lock()
{
if (m_ctrl.compare_and_swap_test(0, SM_WRITER_LOCK)) return;
lock_hard();
} }
// Unlock exclusively // Unlock exclusively
void unlock() void unlock()
{ {
const u32 value = m_ctrl.fetch_add(SM_WRITER_LOCK); if (m_ctrl.fetch_sub(SM_WRITER_LOCK) != SM_WRITER_LOCK)
// Check if notification required
if (value != SM_WRITER_LOCK)
{ {
impl_unlock_excl(value); unlock_notify();
} }
} }
}; };

View File

@ -1,55 +0,0 @@
#include "stdafx.h"
#include "Emu/CPU/CPUThread.h"
#include "SleepQueue.h"
void sleep_queue_entry_t::add_entry()
{
m_queue.emplace_back(std::static_pointer_cast<CPUThread>(m_thread.shared_from_this()));
}
void sleep_queue_entry_t::remove_entry()
{
for (auto it = m_queue.begin(); it != m_queue.end(); it++)
{
if (it->get() == &m_thread)
{
m_queue.erase(it);
return;
}
}
}
bool sleep_queue_entry_t::find() const
{
for (auto it = m_queue.begin(); it != m_queue.end(); it++)
{
if (it->get() == &m_thread)
{
return true;
}
}
return false;
}
sleep_queue_entry_t::sleep_queue_entry_t(sleep_entry_t& cpu, sleep_queue_t& queue)
: m_thread(cpu)
, m_queue(queue)
{
add_entry();
cpu.sleep();
}
sleep_queue_entry_t::sleep_queue_entry_t(sleep_entry_t& cpu, sleep_queue_t& queue, const defer_sleep_t&)
: m_thread(cpu)
, m_queue(queue)
{
cpu.sleep();
}
sleep_queue_entry_t::~sleep_queue_entry_t()
{
remove_entry();
m_thread.awake();
}

View File

@ -1,45 +1,75 @@
#pragma once #pragma once
using sleep_entry_t = class CPUThread; #include <deque>
using sleep_queue_t = std::deque<std::shared_ptr<sleep_entry_t>>;
static struct defer_sleep_t {} const defer_sleep{}; // Tag used in sleep_entry<> constructor
static struct defer_sleep_tag {} constexpr defer_sleep{};
// automatic object handling a thread entry in the sleep queue // Define sleep queue as std::deque with T* pointers, T - thread type
class sleep_queue_entry_t final template<typename T> using sleep_queue = std::deque<T*>;
// Automatic object handling a thread pointer (T*) in the sleep queue
// Sleep is called in the constructor (if not null)
// Awake is called in the destructor (if not null)
// Sleep queue is actually std::deque with pointers, be careful about the lifetime
template<typename T, void(T::*Sleep)() = &T::sleep, void(T::*Awake)() = &T::awake>
class sleep_entry final
{ {
sleep_entry_t& m_thread; sleep_queue<T>& m_queue;
sleep_queue_t& m_queue; T& m_thread;
void add_entry();
void remove_entry();
bool find() const;
public: public:
// add specified thread to the sleep queue // Constructor; enter() not called
sleep_queue_entry_t(sleep_entry_t& entry, sleep_queue_t& queue); sleep_entry(sleep_queue<T>& queue, T& entry, const defer_sleep_tag&)
: m_queue(queue)
, m_thread(entry)
{
if (Sleep) (m_thread.*Sleep)();
}
// don't add specified thread to the sleep queue // Constructor; calls enter()
sleep_queue_entry_t(sleep_entry_t& entry, sleep_queue_t& queue, const defer_sleep_t&); sleep_entry(sleep_queue<T>& queue, T& entry)
: sleep_entry(queue, entry, defer_sleep)
{
enter();
}
// removes specified thread from the sleep queue if added // Destructor; calls leave()
~sleep_queue_entry_t(); ~sleep_entry()
{
leave();
if (Awake) (m_thread.*Awake)();
}
// add thread to the sleep queue // Add thread to the sleep queue
void enter() void enter()
{ {
add_entry(); for (auto t : m_queue)
{
if (t == &m_thread)
{
// Already exists, is it an error?
return;
}
} }
// remove thread from the sleep queue m_queue.emplace_back(&m_thread);
}
// Remove thread from the sleep queue
void leave() void leave()
{ {
remove_entry(); auto it = std::find(m_queue.begin(), m_queue.end(), &m_thread);
if (it != m_queue.end())
{
m_queue.erase(it);
}
} }
// check whether the thread exists in the sleep queue // Check whether the thread exists in the sleep queue
explicit operator bool() const explicit operator bool() const
{ {
return find(); return std::find(m_queue.begin(), m_queue.end(), &m_thread) != m_queue.end();
} }
}; };

View File

@ -1,9 +1,5 @@
#include "stdafx.h" #include "StrFmt.h"
#pragma warning(push) #include "BEType.h"
#pragma message("TODO: remove wx dependency: <wx/string.h>")
#pragma warning(disable : 4996)
#include <wx/string.h>
#pragma warning(pop)
std::string v128::to_hex() const std::string v128::to_hex() const
{ {
@ -19,7 +15,7 @@ std::string fmt::to_hex(u64 value, u64 count)
{ {
if (count - 1 >= 16) if (count - 1 >= 16)
{ {
throw EXCEPTION("Invalid count: 0x%llx", count); throw exception("fmt::to_hex(): invalid count: 0x%llx", count);
} }
count = std::max<u64>(count, 16 - cntlz64(value) / 4); count = std::max<u64>(count, 16 - cntlz64(value) / 4);
@ -78,8 +74,6 @@ std::string fmt::to_sdec(s64 svalue)
return std::string(&res[first], sizeof(res) - first); return std::string(&res[first], sizeof(res) - first);
} }
//extern const std::string fmt::placeholder = "???";
std::string fmt::replace_first(const std::string& src, const std::string& from, const std::string& to) std::string fmt::replace_first(const std::string& src, const std::string& from, const std::string& to)
{ {
auto pos = src.find(from); auto pos = src.find(from);
@ -104,83 +98,6 @@ std::string fmt::replace_all(const std::string &src, const std::string& from, co
return target; return target;
} }
//TODO: move this wx Stuff somewhere else
//convert a wxString to a std::string encoded in utf8
//CAUTION, only use this to interface with wxWidgets classes
std::string fmt::ToUTF8(const wxString& right)
{
auto ret = std::string(((const char *)right.utf8_str()));
return ret;
}
//convert a std::string encoded in utf8 to a wxString
//CAUTION, only use this to interface with wxWidgets classes
wxString fmt::FromUTF8(const std::string& right)
{
auto ret = wxString::FromUTF8(right.c_str());
return ret;
}
//TODO: remove this after every snippet that uses it is gone
//WARNING: not fully compatible with CmpNoCase from wxString
int fmt::CmpNoCase(const std::string& a, const std::string& b)
{
if (a.length() != b.length())
{
return -1;
}
else
{
return std::equal(a.begin(),
a.end(),
b.begin(),
[](const char& a, const char& b){return ::tolower(a) == ::tolower(b); })
? 0 : -1;
}
}
//TODO: remove this after every snippet that uses it is gone
//WARNING: not fully compatible with CmpNoCase from wxString
void fmt::Replace(std::string &str, const std::string &searchterm, const std::string& replaceterm)
{
size_t cursor = 0;
do
{
cursor = str.find(searchterm, cursor);
if (cursor != std::string::npos)
{
str.replace(cursor, searchterm.size(), replaceterm);
cursor += replaceterm.size();
}
else
{
break;
}
} while (true);
}
std::vector<std::string> fmt::rSplit(const std::string& source, const std::string& delim)
{
std::vector<std::string> ret;
size_t cursor = 0;
do
{
size_t prevcurs = cursor;
cursor = source.find(delim, cursor);
if (cursor != std::string::npos)
{
ret.push_back(source.substr(prevcurs,cursor-prevcurs));
cursor += delim.size();
}
else
{
ret.push_back(source.substr(prevcurs));
break;
}
} while (true);
return ret;
}
std::vector<std::string> fmt::split(const std::string& source, std::initializer_list<std::string> separators, bool is_skip_empty) std::vector<std::string> fmt::split(const std::string& source, std::initializer_list<std::string> separators, bool is_skip_empty)
{ {
std::vector<std::string> result; std::vector<std::string> result;
@ -222,21 +139,7 @@ std::string fmt::trim(const std::string& source, const std::string& values)
return source.substr(begin, source.find_last_not_of(values) + 1); return source.substr(begin, source.find_last_not_of(values) + 1);
} }
std::string fmt::tolower(std::string source) std::string fmt::escape(const std::string& source, std::initializer_list<char> more)
{
std::transform(source.begin(), source.end(), source.begin(), ::tolower);
return source;
}
std::string fmt::toupper(std::string source)
{
std::transform(source.begin(), source.end(), source.begin(), ::toupper);
return source;
}
std::string fmt::escape(std::string source)
{ {
const std::pair<std::string, std::string> escape_list[] = const std::pair<std::string, std::string> escape_list[] =
{ {
@ -244,20 +147,66 @@ std::string fmt::escape(std::string source)
{ "\a", "\\a" }, { "\a", "\\a" },
{ "\b", "\\b" }, { "\b", "\\b" },
{ "\f", "\\f" }, { "\f", "\\f" },
{ "\n", "\\n\n" }, { "\n", "\\n" },
{ "\r", "\\r" }, { "\r", "\\r" },
{ "\t", "\\t" }, { "\t", "\\t" },
{ "\v", "\\v" }, { "\v", "\\v" },
}; };
source = fmt::replace_all(source, escape_list); std::string result = fmt::replace_all(source, escape_list);
for (char c = 0; c < 32; c++) for (char c = 0; c < 32; c++)
{ {
if (c != '\n') source = fmt::replace_all(source, std::string(1, c), fmt::format("\\x%02X", c)); result = fmt::replace_all(result, std::string(1, c), fmt::format("\\x%02X", c));
} }
return source; for (char c : more)
{
result = fmt::replace_all(result, std::string(1, c), fmt::format("\\x%02X", c));
}
return result;
}
std::string fmt::unescape(const std::string& source)
{
std::string result;
for (auto it = source.begin(); it != source.end();)
{
const char bs = *it++;
if (bs == '\\' && it != source.end())
{
switch (const char code = *it++)
{
case 'a': result += '\a'; break;
case 'b': result += '\b'; break;
case 'f': result += '\f'; break;
case 'n': result += '\n'; break;
case 'r': result += '\r'; break;
case 't': result += '\t'; break;
case 'v': result += '\v'; break;
case 'x':
{
// Detect hexadecimal character code (TODO)
if (source.end() - it >= 2)
{
result += std::stoi(std::string{ *it++, *it++ }, 0, 16);
}
}
// Octal/unicode not supported
default: result += code;
}
}
else
{
result += bs;
}
}
return result;
} }
bool fmt::match(const std::string &source, const std::string &mask) bool fmt::match(const std::string &source, const std::string &mask)

View File

@ -1,95 +1,38 @@
#pragma once #pragma once
class wxString; #include <array>
#include <string>
#include <vector>
#include <functional>
#include <memory>
#include "Platform.h"
#include "types.h"
#if defined(_MSC_VER) && _MSC_VER <= 1800 #if defined(_MSC_VER) && _MSC_VER <= 1800
#define snprintf _snprintf #define snprintf _snprintf
#endif #endif
// Copy null-terminated string from std::string to char array with truncation
template<std::size_t N>
inline void strcpy_trunc(char(&dst)[N], const std::string& src)
{
const std::size_t count = src.size() >= N ? N - 1 : src.size();
std::memcpy(dst, src.c_str(), count);
dst[count] = '\0';
}
// Copy null-terminated string from char array to another char array with truncation
template<std::size_t N, std::size_t N2>
inline void strcpy_trunc(char(&dst)[N], const char(&src)[N2])
{
const std::size_t count = N2 >= N ? N - 1 : N2;
std::memcpy(dst, src, count);
dst[count] = '\0';
}
namespace fmt namespace fmt
{ {
//struct empty_t{};
//extern const std::string placeholder;
template <typename T>
std::string AfterLast(const std::string& source, T searchstr)
{
size_t search_pos = source.rfind(searchstr);
search_pos = search_pos == std::string::npos ? 0 : search_pos;
return source.substr(search_pos);
}
template <typename T>
std::string BeforeLast(const std::string& source, T searchstr)
{
size_t search_pos = source.rfind(searchstr);
search_pos = search_pos == std::string::npos ? 0 : search_pos;
return source.substr(0, search_pos);
}
template <typename T>
std::string AfterFirst(const std::string& source, T searchstr)
{
size_t search_pos = source.find(searchstr);
search_pos = search_pos == std::string::npos ? 0 : search_pos;
return source.substr(search_pos);
}
template <typename T>
std::string BeforeFirst(const std::string& source, T searchstr)
{
size_t search_pos = source.find(searchstr);
search_pos = search_pos == std::string::npos ? 0 : search_pos;
return source.substr(0, search_pos);
}
// write `fmt` from `pos` to the first occurence of `fmt::placeholder` to
// the stream `os`. Then write `arg` to to the stream. If there's no
// `fmt::placeholder` after `pos` everything in `fmt` after pos is written
// to `os`. Then `arg` is written to `os` after appending a space character
//template<typename T>
//empty_t write(const std::string &fmt, std::ostream &os, std::string::size_type &pos, T &&arg)
//{
// std::string::size_type ins = fmt.find(placeholder, pos);
// if (ins == std::string::npos)
// {
// os.write(fmt.data() + pos, fmt.size() - pos);
// os << ' ' << arg;
// pos = fmt.size();
// }
// else
// {
// os.write(fmt.data() + pos, ins - pos);
// os << arg;
// pos = ins + placeholder.size();
// }
// return{};
//}
// typesafe version of a sprintf-like function. Returns the printed to
// string. To mark positions where the arguments are supposed to be
// inserted use `fmt::placeholder`. If there's not enough placeholders
// the rest of the arguments are appended at the end, seperated by spaces
//template<typename ... Args>
//std::string SFormat(const std::string &fmt, Args&& ... parameters)
//{
// std::ostringstream os;
// std::string::size_type pos = 0;
// std::initializer_list<empty_t> { write(fmt, os, pos, parameters)... };
// if (!fmt.empty())
// {
// os.write(fmt.data() + pos, fmt.size() - pos);
// }
// std::string result = os.str();
// return result;
//}
std::string replace_first(const std::string& src, const std::string& from, const std::string& to); std::string replace_first(const std::string& src, const std::string& from, const std::string& to);
std::string replace_all(const std::string &src, const std::string& from, const std::string& to); std::string replace_all(const std::string &src, const std::string& from, const std::string& to);
@ -145,7 +88,8 @@ namespace fmt
std::string to_udec(u64 value); std::string to_udec(u64 value);
std::string to_sdec(s64 value); std::string to_sdec(s64 value);
template<typename T, bool is_enum = std::is_enum<T>::value> struct unveil template<typename T, typename>
struct unveil
{ {
using result_type = T; using result_type = T;
@ -155,37 +99,41 @@ namespace fmt
} }
}; };
template<> struct unveil<const char*, false> template<>
struct unveil<const char*, void>
{ {
using result_type = const char* const; using result_type = const char* const;
force_inline static result_type get_value(const char* const& arg) static result_type get_value(const char* const& arg)
{ {
return arg; return arg;
} }
}; };
template<std::size_t N> struct unveil<char[N], false> template<std::size_t N>
struct unveil<char[N], void>
{ {
using result_type = const char* const; using result_type = const char* const;
force_inline static result_type get_value(const char(&arg)[N]) static result_type get_value(const char(&arg)[N])
{ {
return arg; return arg;
} }
}; };
template<> struct unveil<std::string, false> template<>
struct unveil<std::string, void>
{ {
using result_type = const char*; using result_type = const char*;
force_inline static result_type get_value(const std::string& arg) static result_type get_value(const std::string& arg)
{ {
return arg.c_str(); return arg.c_str();
} }
}; };
template<typename T> struct unveil<T, true> template<typename T>
struct unveil<T, std::enable_if_t<std::is_enum<T>::value>>
{ {
using result_type = std::underlying_type_t<T>; using result_type = std::underlying_type_t<T>;
@ -195,31 +143,13 @@ namespace fmt
} }
}; };
template<typename T, bool Se> struct unveil<se_t<T, Se>, false>
{
using result_type = typename unveil<T>::result_type;
force_inline static result_type get_value(const se_t<T, Se>& arg)
{
return unveil<T>::get_value(arg);
}
};
template<typename T> template<typename T>
force_inline typename unveil<T>::result_type do_unveil(const T& arg) force_inline typename unveil<T>::result_type do_unveil(const T& arg)
{ {
return unveil<T>::get_value(arg); return unveil<T>::get_value(arg);
} }
// Formatting function with special functionality: // Formatting function with special functionality (fmt::unveil)
//
// std::string is forced to .c_str()
// be_t<> is forced to .value() (fmt::do_unveil reverts byte order automatically)
//
// External specializations for fmt::do_unveil (can be found in another headers):
// vm::ptr, vm::bptr, ... (fmt::do_unveil) (vm_ptr.h) (with appropriate address type, using .addr() can be avoided)
// vm::ref, vm::bref, ... (fmt::do_unveil) (vm_ref.h)
//
template<typename... Args> template<typename... Args>
safe_buffers std::string format(const char* fmt, const Args&... args) safe_buffers std::string format(const char* fmt, const Args&... args)
{ {
@ -256,51 +186,28 @@ namespace fmt
} }
} }
struct exception : public std::exception // Create exception of type T (std::runtime_error by default) with formatting
template<typename T = std::runtime_error, typename... Args>
never_inline safe_buffers T exception(const char* fmt, const Args&... args) noexcept(noexcept(T{ fmt }))
{ {
std::unique_ptr<char[]> message; return T{ format(fmt, do_unveil(args)...).c_str() };
template<typename... Args> never_inline safe_buffers exception(const char* file, int line, const char* func, const char* text, Args... args) noexcept
{
const std::string data = format(text, args...) + format("\n(in file %s:%d, in function %s)", file, line, func);
message.reset(new char[data.size() + 1]);
std::memcpy(message.get(), data.c_str(), data.size() + 1);
} }
exception(const exception& other) noexcept // Create exception of type T (std::runtime_error by default) without formatting
template<typename T = std::runtime_error>
safe_buffers T exception(const char* msg) noexcept(noexcept(T{ msg }))
{ {
const std::size_t size = std::strlen(other.message.get()); return T{ msg };
message.reset(new char[size + 1]);
std::memcpy(message.get(), other.message.get(), size + 1);
} }
virtual const char* what() const noexcept override // Narrow cast (similar to gsl::narrow) with exception message formatting
template<typename To, typename From, typename... Args>
inline auto narrow(const char* format_str, const From& value, const Args&... args) -> decltype(static_cast<To>(static_cast<From>(std::declval<To>())))
{ {
return message.get(); const auto result = static_cast<To>(value);
if (static_cast<From>(result) != value) throw fmt::exception(format_str, fmt::do_unveil(value), fmt::do_unveil(args)...);
return result;
} }
};
//convert a wxString to a std::string encoded in utf8
//CAUTION, only use this to interface with wxWidgets classes
std::string ToUTF8(const wxString& right);
//convert a std::string encoded in utf8 to a wxString
//CAUTION, only use this to interface with wxWidgets classes
wxString FromUTF8(const std::string& right);
//TODO: remove this after every snippet that uses it is gone
//WARNING: not fully compatible with CmpNoCase from wxString
int CmpNoCase(const std::string& a, const std::string& b);
//TODO: remove this after every snippet that uses it is gone
//WARNING: not fully compatible with Replace from wxString
void Replace(std::string &str, const std::string &searchterm, const std::string& replaceterm);
std::vector<std::string> rSplit(const std::string& source, const std::string& delim);
std::vector<std::string> split(const std::string& source, std::initializer_list<std::string> separators, bool is_skip_empty = true); std::vector<std::string> split(const std::string& source, std::initializer_list<std::string> separators, bool is_skip_empty = true);
std::string trim(const std::string& source, const std::string& values = " \t"); std::string trim(const std::string& source, const std::string& values = " \t");
@ -352,8 +259,35 @@ namespace fmt
return result; return result;
} }
std::string tolower(std::string source); template<typename IT>
std::string toupper(std::string source); std::string to_lower(IT _begin, IT _end)
std::string escape(std::string source); {
std::string result; result.resize(_end - _begin);
std::transform(_begin, _end, result.begin(), ::tolower);
return result;
}
template<typename T>
std::string to_lower(const T& string)
{
return to_lower(std::begin(string), std::end(string));
}
template<typename IT>
std::string to_upper(IT _begin, IT _end)
{
std::string result; result.resize(_end - _begin);
std::transform(_begin, _end, result.begin(), ::toupper);
return result;
}
template<typename T>
std::string to_upper(const T& string)
{
return to_upper(std::begin(string), std::end(string));
}
std::string escape(const std::string& source, std::initializer_list<char> more = {});
std::string unescape(const std::string& source);
bool match(const std::string &source, const std::string &mask); bool match(const std::string &source, const std::string &mask);
} }

View File

@ -1,11 +1,8 @@
#include "stdafx.h" #include "stdafx.h"
#include "Log.h" #include "Emu/Memory/Memory.h"
#include "Emu/System.h" #include "Emu/System.h"
#include "Emu/state.h" #include "Emu/IdManager.h"
#include "Emu/CPU/CPUThreadManager.h"
#include "Emu/CPU/CPUThread.h"
#include "Emu/Cell/RawSPUThread.h" #include "Emu/Cell/RawSPUThread.h"
#include "Emu/SysCalls/SysCalls.h"
#include "Thread.h" #include "Thread.h"
#ifdef _WIN32 #ifdef _WIN32
@ -21,10 +18,19 @@
static void report_fatal_error(const std::string& msg) static void report_fatal_error(const std::string& msg)
{ {
std::string _msg = msg + "\n"
"HOW TO REPORT ERRORS:\n"
"1) Check the FAQ, readme, other sources. Please ensure that your hardware and software configuration is compliant.\n"
"2) You must provide FULL information: how to reproduce the error (your actions), RPCS3.log file, other *.log files whenever requested.\n"
"3) Please ensure that your software (game) is 'Playable' or close. Please note that 'Non-playable' games will be ignored.\n"
"4) If the software (game) is not 'Playable', please ensure that this error is unexpected, i.e. it didn't happen before or similar.\n"
"Please, don't send incorrect reports. Thanks for understanding.\n";
#ifdef _WIN32 #ifdef _WIN32
MessageBoxA(0, msg.c_str(), "Fatal error", MB_ICONERROR); // TODO: unicode message _msg += "Press (Ctrl+C) to copy this message.";
MessageBoxA(0, _msg.c_str(), "Fatal error", MB_ICONERROR); // TODO: unicode message
#else #else
std::printf("Fatal error: %s\nPlease report this error to the developers.\n", msg.c_str()); std::printf("Fatal error: \n%s", _msg.c_str());
#endif #endif
} }
@ -34,9 +40,9 @@ static void report_fatal_error(const std::string& msg)
{ {
throw; throw;
} }
catch (const std::exception& ex) catch (const std::exception& e)
{ {
report_fatal_error("Unhandled exception: "s + ex.what()); report_fatal_error("Unhandled exception of type '"s + typeid(e).name() + "': "s + e.what());
} }
catch (...) catch (...)
{ {
@ -775,8 +781,7 @@ size_t get_x64_access_size(x64_context* context, x64_op_t op, x64_reg_t reg, siz
if (op == X64OP_CMPXCHG) if (op == X64OP_CMPXCHG)
{ {
// detect whether this instruction can't actually modify memory to avoid breaking reservation; // Detect whether the instruction can't actually modify memory to avoid breaking reservation
// this may theoretically cause endless loop, but it shouldn't be a problem if only load_sync() generates such instruction
u64 cmp, exch; u64 cmp, exch;
if (!get_x64_reg_value(context, reg, d_size, i_size, cmp) || !get_x64_reg_value(context, X64R_RAX, d_size, i_size, exch)) if (!get_x64_reg_value(context, reg, d_size, i_size, cmp) || !get_x64_reg_value(context, X64R_RAX, d_size, i_size, exch))
{ {
@ -843,7 +848,7 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context)
// check if address is RawSPU MMIO register // check if address is RawSPU MMIO register
if (addr - RAW_SPU_BASE_ADDR < (6 * RAW_SPU_OFFSET) && (addr % RAW_SPU_OFFSET) >= RAW_SPU_PROB_OFFSET) if (addr - RAW_SPU_BASE_ADDR < (6 * RAW_SPU_OFFSET) && (addr % RAW_SPU_OFFSET) >= RAW_SPU_PROB_OFFSET)
{ {
auto thread = Emu.GetCPU().GetRawSPUThread((addr - RAW_SPU_BASE_ADDR) / RAW_SPU_OFFSET); auto thread = idm::get<RawSPUThread>((addr - RAW_SPU_BASE_ADDR) / RAW_SPU_OFFSET);
if (!thread) if (!thread)
{ {
@ -1051,10 +1056,10 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context)
switch (d_size) switch (d_size)
{ {
case 1: reg_value = sync_lock_test_and_set((u8*)vm::base_priv(addr), (u8)reg_value); break; case 1: reg_value = ((atomic_t<u8>*)vm::base_priv(addr))->exchange((u8)reg_value); break;
case 2: reg_value = sync_lock_test_and_set((u16*)vm::base_priv(addr), (u16)reg_value); break; case 2: reg_value = ((atomic_t<u16>*)vm::base_priv(addr))->exchange((u16)reg_value); break;
case 4: reg_value = sync_lock_test_and_set((u32*)vm::base_priv(addr), (u32)reg_value); break; case 4: reg_value = ((atomic_t<u32>*)vm::base_priv(addr))->exchange((u32)reg_value); break;
case 8: reg_value = sync_lock_test_and_set((u64*)vm::base_priv(addr), (u64)reg_value); break; case 8: reg_value = ((atomic_t<u64>*)vm::base_priv(addr))->exchange((u64)reg_value); break;
default: return false; default: return false;
} }
@ -1074,10 +1079,10 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context)
switch (d_size) switch (d_size)
{ {
case 1: old_value = sync_val_compare_and_swap((u8*)vm::base_priv(addr), (u8)cmp_value, (u8)reg_value); break; case 1: old_value = ((atomic_t<u8>*)vm::base_priv(addr))->compare_and_swap((u8)cmp_value, (u8)reg_value); break;
case 2: old_value = sync_val_compare_and_swap((u16*)vm::base_priv(addr), (u16)cmp_value, (u16)reg_value); break; case 2: old_value = ((atomic_t<u16>*)vm::base_priv(addr))->compare_and_swap((u16)cmp_value, (u16)reg_value); break;
case 4: old_value = sync_val_compare_and_swap((u32*)vm::base_priv(addr), (u32)cmp_value, (u32)reg_value); break; case 4: old_value = ((atomic_t<u32>*)vm::base_priv(addr))->compare_and_swap((u32)cmp_value, (u32)reg_value); break;
case 8: old_value = sync_val_compare_and_swap((u64*)vm::base_priv(addr), (u64)cmp_value, (u64)reg_value); break; case 8: old_value = ((atomic_t<u64>*)vm::base_priv(addr))->compare_and_swap((u64)cmp_value, (u64)reg_value); break;
default: return false; default: return false;
} }
@ -1097,10 +1102,10 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context)
switch (d_size) switch (d_size)
{ {
case 1: value &= sync_fetch_and_and((u8*)vm::base_priv(addr), (u8)value); break; case 1: value = *(atomic_t<u8>*)vm::base_priv(addr) &= (u8)value; break;
case 2: value &= sync_fetch_and_and((u16*)vm::base_priv(addr), (u16)value); break; case 2: value = *(atomic_t<u16>*)vm::base_priv(addr) &= (u16)value; break;
case 4: value &= sync_fetch_and_and((u32*)vm::base_priv(addr), (u32)value); break; case 4: value = *(atomic_t<u32>*)vm::base_priv(addr) &= (u32)value; break;
case 8: value &= sync_fetch_and_and((u64*)vm::base_priv(addr), (u64)value); break; case 8: value = *(atomic_t<u64>*)vm::base_priv(addr) &= (u64)value; break;
default: return false; default: return false;
} }
@ -1126,14 +1131,14 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context)
// TODO: allow recovering from a page fault as a feature of PS3 virtual memory // TODO: allow recovering from a page fault as a feature of PS3 virtual memory
} }
// Throw virtual memory access violation exception [[noreturn]] static void throw_access_violation(const char* cause, u64 addr)
[[noreturn]] void throw_access_violation(const char* cause, u32 address) // Don't change function definition
{ {
throw EXCEPTION("Access violation %s location 0x%08x", cause, address); vm::throw_access_violation(addr, cause);
std::abort();
} }
// Modify context in order to convert hardware exception to C++ exception // Modify context in order to convert hardware exception to C++ exception
void prepare_throw_access_violation(x64_context* context, const char* cause, u32 address) static void prepare_throw_access_violation(x64_context* context, const char* cause, u32 address)
{ {
// Set throw_access_violation() call args (old register values are lost) // Set throw_access_violation() call args (old register values are lost)
ARG1(context) = (u64)cause; ARG1(context) = (u64)cause;
@ -1151,14 +1156,17 @@ static LONG exception_handler(PEXCEPTION_POINTERS pExp)
const u64 addr64 = pExp->ExceptionRecord->ExceptionInformation[1] - (u64)vm::base(0); const u64 addr64 = pExp->ExceptionRecord->ExceptionInformation[1] - (u64)vm::base(0);
const bool is_writing = pExp->ExceptionRecord->ExceptionInformation[0] != 0; const bool is_writing = pExp->ExceptionRecord->ExceptionInformation[0] != 0;
if (pExp->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION && addr64 < 0x100000000ull && thread_ctrl::get_current() && handle_access_violation((u32)addr64, is_writing, pExp->ContextRecord)) if (pExp->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION && addr64 < 0x100000000ull)
{
vm::g_tls_fault_count++;
if (thread_ctrl::get_current() && handle_access_violation((u32)addr64, is_writing, pExp->ContextRecord))
{ {
return EXCEPTION_CONTINUE_EXECUTION; return EXCEPTION_CONTINUE_EXECUTION;
} }
else
{
return EXCEPTION_CONTINUE_SEARCH;
} }
return EXCEPTION_CONTINUE_SEARCH;
} }
static LONG exception_filter(PEXCEPTION_POINTERS pExp) static LONG exception_filter(PEXCEPTION_POINTERS pExp)
@ -1170,8 +1178,9 @@ static LONG exception_filter(PEXCEPTION_POINTERS pExp)
const u64 addr64 = pExp->ExceptionRecord->ExceptionInformation[1] - (u64)vm::base(0); const u64 addr64 = pExp->ExceptionRecord->ExceptionInformation[1] - (u64)vm::base(0);
const auto cause = pExp->ExceptionRecord->ExceptionInformation[0] != 0 ? "writing" : "reading"; const auto cause = pExp->ExceptionRecord->ExceptionInformation[0] != 0 ? "writing" : "reading";
if (addr64 < 0x100000000ull) if (!(vm::g_tls_fault_count & (1ull << 63)) && addr64 < 0x100000000ull)
{ {
vm::g_tls_fault_count |= (1ull << 63);
// Setup throw_access_violation() call on the context // Setup throw_access_violation() call on the context
prepare_throw_access_violation(pExp->ContextRecord, cause, (u32)addr64); prepare_throw_access_violation(pExp->ContextRecord, cause, (u32)addr64);
return EXCEPTION_CONTINUE_EXECUTION; return EXCEPTION_CONTINUE_EXECUTION;
@ -1190,33 +1199,23 @@ static LONG exception_filter(PEXCEPTION_POINTERS pExp)
} }
msg += fmt::format("Instruction address: %p.\n", pExp->ContextRecord->Rip); msg += fmt::format("Instruction address: %p.\n", pExp->ContextRecord->Rip);
msg += fmt::format("Image base: %p.", GetModuleHandle(NULL)); msg += fmt::format("Image base: %p.\n", GetModuleHandle(NULL));
if (pExp->ExceptionRecord->ExceptionCode == EXCEPTION_ILLEGAL_INSTRUCTION)
{
msg += "\n"
"Illegal instruction exception occured.\n"
"Note that your CPU must support SSSE3 extension.\n";
}
// TODO: print registers and the callstack // TODO: print registers and the callstack
// Exception specific messages
if (pExp->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
{
msg += "\n\nAn internal access violation has occured."
"\nPlease only report this error to the developers, if you're an advanced user and have obtained a stack trace.";
}
else if (pExp->ExceptionRecord->ExceptionCode == EXCEPTION_ILLEGAL_INSTRUCTION)
{
msg += "\n\nAn internal illegal instruction exception has occured."
"\nRPCS3 requires a modern x86-64 CPU that supports SSSE3 (and SSE4.1 for PPU LLVM recompiler)."
"\nPlease make sure that your CPU supports these extensions.";
}
else
{
msg += "\n\nAn unknown internal exception has occured. Please report this to the developers.\nYou can press (Ctrl+C) to copy this message.";
}
// Report fatal error // Report fatal error
report_fatal_error(msg); report_fatal_error(msg);
return EXCEPTION_CONTINUE_SEARCH; return EXCEPTION_CONTINUE_SEARCH;
} }
const bool g_exception_handler_set = []() -> bool const bool s_exception_handler_set = []() -> bool
{ {
if (!AddVectoredExceptionHandler(1, (PVECTORED_EXCEPTION_HANDLER)exception_handler)) if (!AddVectoredExceptionHandler(1, (PVECTORED_EXCEPTION_HANDLER)exception_handler))
{ {
@ -1248,12 +1247,12 @@ static void signal_handler(int sig, siginfo_t* info, void* uct)
const u64 addr64 = (u64)info->si_addr - (u64)vm::base(0); const u64 addr64 = (u64)info->si_addr - (u64)vm::base(0);
const auto cause = is_writing ? "writing" : "reading"; const auto cause = is_writing ? "writing" : "reading";
// TODO: Exception specific informative messages if (addr64 < 0x100000000ull)
if (addr64 < 0x100000000ull && thread_ctrl::get_current())
{ {
vm::g_tls_fault_count++;
// Try to process access violation // Try to process access violation
if (!handle_access_violation((u32)addr64, is_writing, context)) if (!thread_ctrl::get_current() || !handle_access_violation((u32)addr64, is_writing, context))
{ {
// Setup throw_access_violation() call on the context // Setup throw_access_violation() call on the context
prepare_throw_access_violation(context, cause, (u32)addr64); prepare_throw_access_violation(context, cause, (u32)addr64);
@ -1267,7 +1266,7 @@ static void signal_handler(int sig, siginfo_t* info, void* uct)
} }
} }
const bool g_exception_handler_set = []() -> bool const bool s_exception_handler_set = []() -> bool
{ {
struct ::sigaction sa; struct ::sigaction sa;
sa.sa_flags = SA_SIGINFO; sa.sa_flags = SA_SIGINFO;
@ -1285,17 +1284,33 @@ const bool g_exception_handler_set = []() -> bool
#endif #endif
thread_local thread_ctrl* thread_ctrl::g_tls_this_thread = nullptr; const bool s_self_test = []() -> bool
{
// Find ret instruction
if ((*(u8*)throw_access_violation & 0xF6) == 0xC2)
{
std::abort();
}
return true;
}();
thread_local DECLARE(thread_ctrl::g_tls_this_thread) = nullptr;
// TODO // TODO
std::atomic<u32> g_thread_count{ 0 }; atomic_t<u32> g_thread_count{ 0 };
void thread_ctrl::initialize() void thread_ctrl::initialize()
{ {
SetCurrentThreadDebugName(g_tls_this_thread->m_name().c_str()); SetCurrentThreadDebugName(g_tls_this_thread->m_name.c_str());
_log::g_tls_make_prefix = [](const auto&, auto, const auto&)
{
return g_tls_this_thread->m_name;
};
// TODO // TODO
g_thread_count++; ++g_thread_count;
} }
void thread_ctrl::finalize() noexcept void thread_ctrl::finalize() noexcept
@ -1304,79 +1319,36 @@ void thread_ctrl::finalize() noexcept
vm::reservation_free(); vm::reservation_free();
// TODO // TODO
g_thread_count--; --g_thread_count;
// Call atexit functions // Call atexit functions
for (const auto& func : decltype(m_atexit)(std::move(g_tls_this_thread->m_atexit))) g_tls_this_thread->m_atexit.exec();
{
func();
}
} }
thread_ctrl::~thread_ctrl() std::string named_thread::get_name() const
{
m_thread.detach();
if (m_future.valid())
{
try
{
m_future.get();
}
catch (...)
{
catch_all_exceptions();
}
}
}
std::string thread_ctrl::get_name() const
{
CHECK_ASSERTION(m_name);
return m_name();
}
std::string named_thread_t::get_name() const
{ {
return fmt::format("('%s') Unnamed Thread", typeid(*this).name()); return fmt::format("('%s') Unnamed Thread", typeid(*this).name());
} }
void named_thread_t::start() void named_thread::start()
{ {
CHECK_ASSERTION(!m_thread); Expects(!m_thread);
// Get shared_ptr instance (will throw if called from the constructor or the object has been created incorrectly) // Get shared_ptr instance (will throw if called from the constructor or the object has been created incorrectly)
auto ptr = shared_from_this(); auto&& ptr = shared_from_this();
// Make name getter
auto name = [wptr = std::weak_ptr<named_thread_t>(ptr), type = &typeid(*this)]()
{
// Return actual name if available
if (const auto ptr = wptr.lock())
{
return ptr->get_name();
}
else
{
return fmt::format("('%s') Deleted Thread", type->name());
}
};
// Run thread // Run thread
m_thread = thread_ctrl::spawn(std::move(name), [thread = std::move(ptr)]() m_thread = thread_ctrl::spawn(get_name(), [thread = std::move(ptr)]()
{ {
try try
{ {
LOG_TRACE(GENERAL, "Thread started"); LOG_TRACE(GENERAL, "Thread started");
thread->on_task(); thread->on_task();
LOG_TRACE(GENERAL, "Thread ended"); LOG_TRACE(GENERAL, "Thread ended");
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {
LOG_FATAL(GENERAL, "Exception: %s", e.what()); LOG_FATAL(GENERAL, "%s thrown: %s", typeid(e).name(), e.what());
Emu.Pause(); Emu.Pause();
} }
catch (EmulationStopped) catch (EmulationStopped)
@ -1388,9 +1360,9 @@ void named_thread_t::start()
}); });
} }
void named_thread_t::join() void named_thread::join()
{ {
CHECK_ASSERTION(m_thread != nullptr); Expects(m_thread);
try try
{ {
@ -1403,11 +1375,3 @@ void named_thread_t::join()
throw; throw;
} }
} }
const std::function<bool()> SQUEUE_ALWAYS_EXIT = [](){ return true; };
const std::function<bool()> SQUEUE_NEVER_EXIT = [](){ return false; };
bool squeue_test_exit()
{
return Emu.IsStopped();
}

View File

@ -1,24 +1,96 @@
#pragma once #pragma once
#include <exception>
#include <string>
#include <memory>
#include <thread>
#include <mutex>
#include <condition_variable>
#include "Platform.h"
// Will report exception and call std::abort() if put in catch(...) // Will report exception and call std::abort() if put in catch(...)
[[noreturn]] void catch_all_exceptions(); [[noreturn]] void catch_all_exceptions();
// Simple list of void() functors
class task_stack
{
struct task_base
{
std::unique_ptr<task_base> next;
virtual ~task_base() = default;
virtual void exec()
{
if (next)
{
next->exec();
}
}
};
std::unique_ptr<task_base> m_stack;
never_inline void push(std::unique_ptr<task_base> task)
{
m_stack.swap(task->next);
m_stack.swap(task);
}
public:
template<typename F>
void push(F&& func)
{
struct task_t : task_base
{
std::remove_reference_t<F> func;
task_t(F&& func)
: func(std::forward<F>(func))
{
}
void exec() override
{
func();
task_base::exec();
}
};
return push(std::unique_ptr<task_base>{ new task_t(std::forward<F>(func)) });
}
void reset()
{
m_stack.reset();
}
void exec() const
{
if (m_stack)
{
m_stack->exec();
}
}
};
// Thread control class // Thread control class
class thread_ctrl final class thread_ctrl final
{ {
static thread_local thread_ctrl* g_tls_this_thread; static thread_local thread_ctrl* g_tls_this_thread;
// Name getter // Fixed name
std::function<std::string()> m_name; std::string m_name;
// Thread handle (be careful) // Thread handle (be careful)
std::thread m_thread; std::thread m_thread;
// Thread result // Thread result (exception)
std::future<void> m_future; std::exception_ptr m_exception;
// Functions scheduled at thread exit // Functions scheduled at thread exit
std::deque<std::function<void()>> m_atexit; task_stack m_atexit;
// Called at the thread start // Called at the thread start
static void initialize(); static void initialize();
@ -27,24 +99,41 @@ class thread_ctrl final
static void finalize() noexcept; static void finalize() noexcept;
public: public:
template<typename T> template<typename N>
thread_ctrl(T&& name) thread_ctrl(N&& name)
: m_name(std::forward<T>(name)) : m_name(std::forward<N>(name))
{ {
} }
// Disable copy/move constructors and operators // Disable copy/move constructors and operators
thread_ctrl(const thread_ctrl&) = delete; thread_ctrl(const thread_ctrl&) = delete;
~thread_ctrl(); ~thread_ctrl()
{
if (m_thread.joinable())
{
m_thread.detach();
}
}
// Get thread name // Get thread name
std::string get_name() const; const std::string& get_name() const
{
return m_name;
}
// Get future result (may throw) // Get thread result (may throw)
void join() void join()
{ {
return m_future.get(); if (m_thread.joinable())
{
m_thread.join();
}
if (auto&& e = std::move(m_exception))
{
std::rethrow_exception(e);
}
} }
// Get current thread (may be nullptr) // Get current thread (may be nullptr)
@ -54,12 +143,10 @@ public:
} }
// Register function at thread exit (for the current thread) // Register function at thread exit (for the current thread)
template<typename T> template<typename F>
static inline void at_exit(T&& func) static inline void at_exit(F&& func)
{ {
CHECK_ASSERTION(g_tls_this_thread); return g_tls_this_thread->m_atexit.push(std::forward<F>(func));
g_tls_this_thread->m_atexit.emplace_front(std::forward<T>(func));
} }
// Named thread factory // Named thread factory
@ -68,12 +155,9 @@ public:
{ {
auto ctrl = std::make_shared<thread_ctrl>(std::forward<N>(name)); auto ctrl = std::make_shared<thread_ctrl>(std::forward<N>(name));
std::promise<void> promise; ctrl->m_thread = std::thread([ctrl, task = std::forward<F>(func)]()
ctrl->m_future = promise.get_future();
ctrl->m_thread = std::thread([ctrl, task = std::forward<F>(func)](std::promise<void> promise)
{ {
// Initialize TLS variable
g_tls_this_thread = ctrl.get(); g_tls_this_thread = ctrl.get();
try try
@ -81,21 +165,21 @@ public:
initialize(); initialize();
task(); task();
finalize(); finalize();
promise.set_value();
} }
catch (...) catch (...)
{ {
finalize(); finalize();
promise.set_exception(std::current_exception());
}
}, std::move(promise)); // Set exception
ctrl->m_exception = std::current_exception();
}
});
return ctrl; return ctrl;
} }
}; };
class named_thread_t : public std::enable_shared_from_this<named_thread_t> class named_thread : public std::enable_shared_from_this<named_thread>
{ {
// Pointer to managed resource (shared with actual thread) // Pointer to managed resource (shared with actual thread)
std::shared_ptr<thread_ctrl> m_thread; std::shared_ptr<thread_ctrl> m_thread;
@ -107,6 +191,27 @@ public:
// Thread mutex for external use (can be used with `cv`) // Thread mutex for external use (can be used with `cv`)
std::mutex mutex; std::mutex mutex;
// Lock mutex, notify condition variable
void safe_notify()
{
// Lock for reliable notification, condition is assumed to be changed externally
std::unique_lock<std::mutex> lock(mutex);
cv.notify_one();
}
// ID initialization
virtual void on_init()
{
start();
}
// ID finalization
virtual void on_stop()
{
join();
}
protected: protected:
// Thread task (called in the thread) // Thread task (called in the thread)
virtual void on_task() = 0; virtual void on_task() = 0;
@ -114,19 +219,13 @@ protected:
// Thread finalization (called after on_task) // Thread finalization (called after on_task)
virtual void on_exit() {} virtual void on_exit() {}
// ID initialization (called through id_aux_initialize)
virtual void on_id_aux_initialize() { start(); }
// ID finalization (called through id_aux_finalize)
virtual void on_id_aux_finalize() { join(); }
public: public:
named_thread_t() = default; named_thread() = default;
virtual ~named_thread_t() = default; virtual ~named_thread() = default;
// Deleted copy/move constructors + copy/move operators // Deleted copy/move constructors + copy/move operators
named_thread_t(const named_thread_t&) = delete; named_thread(const named_thread&) = delete;
// Get thread name // Get thread name
virtual std::string get_name() const; virtual std::string get_name() const;
@ -134,377 +233,40 @@ public:
// Start thread (cannot be called from the constructor: should throw bad_weak_ptr in such case) // Start thread (cannot be called from the constructor: should throw bad_weak_ptr in such case)
void start(); void start();
// Join thread (get future result) // Join thread (get thread result)
void join(); void join();
// Check whether the thread is not in "empty state" // Get thread_ctrl
bool is_started() const { return m_thread.operator bool(); } const thread_ctrl* get_thread_ctrl() const
{
return m_thread.get();
}
// Compare with the current thread // Compare with the current thread
bool is_current() const { CHECK_ASSERTION(m_thread); return thread_ctrl::get_current() == m_thread.get(); } bool is_current() const
{
// Get thread_ctrl return m_thread && thread_ctrl::get_current() == m_thread.get();
const thread_ctrl* get_thread_ctrl() const { return m_thread.get(); } }
friend void id_aux_initialize(named_thread_t* ptr) { ptr->on_id_aux_initialize(); }
friend void id_aux_finalize(named_thread_t* ptr) { ptr->on_id_aux_finalize(); }
}; };
// Wrapper for named thread, joins automatically in the destructor, can only be used in function scope // Wrapper for named thread, joins automatically in the destructor, can only be used in function scope
class scope_thread_t final class scope_thread final
{ {
std::shared_ptr<thread_ctrl> m_thread; std::shared_ptr<thread_ctrl> m_thread;
public: public:
template<typename N, typename F> template<typename N, typename F>
scope_thread_t(N&& name, F&& func) scope_thread(N&& name, F&& func)
: m_thread(thread_ctrl::spawn(std::forward<N>(name), std::forward<F>(func))) : m_thread(thread_ctrl::spawn(std::forward<N>(name), std::forward<F>(func)))
{ {
} }
// Deleted copy/move constructors + copy/move operators // Deleted copy/move constructors + copy/move operators
scope_thread_t(const scope_thread_t&) = delete; scope_thread(const scope_thread&) = delete;
// Destructor with exceptions allowed // Destructor with exceptions allowed
~scope_thread_t() noexcept(false) ~scope_thread() noexcept(false)
{ {
m_thread->join(); m_thread->join();
} }
}; };
extern const std::function<bool()> SQUEUE_ALWAYS_EXIT;
extern const std::function<bool()> SQUEUE_NEVER_EXIT;
bool squeue_test_exit();
template<typename T, u32 sq_size = 256>
class squeue_t
{
struct squeue_sync_var_t
{
struct
{
u32 position : 31;
u32 pop_lock : 1;
};
struct
{
u32 count : 31;
u32 push_lock : 1;
};
};
atomic_t<squeue_sync_var_t> m_sync;
mutable std::mutex m_rcv_mutex;
mutable std::mutex m_wcv_mutex;
mutable std::condition_variable m_rcv;
mutable std::condition_variable m_wcv;
T m_data[sq_size];
enum squeue_sync_var_result : u32
{
SQSVR_OK = 0,
SQSVR_LOCKED = 1,
SQSVR_FAILED = 2,
};
public:
squeue_t()
: m_sync(squeue_sync_var_t{})
{
}
u32 get_max_size() const
{
return sq_size;
}
bool is_full() const
{
return m_sync.load().count == sq_size;
}
bool push(const T& data, const std::function<bool()>& test_exit)
{
u32 pos = 0;
while (u32 res = m_sync.atomic_op([&pos](squeue_sync_var_t& sync) -> u32
{
assert(sync.count <= sq_size);
assert(sync.position < sq_size);
if (sync.push_lock)
{
return SQSVR_LOCKED;
}
if (sync.count == sq_size)
{
return SQSVR_FAILED;
}
sync.push_lock = 1;
pos = sync.position + sync.count;
return SQSVR_OK;
}))
{
if (res == SQSVR_FAILED && (test_exit() || squeue_test_exit()))
{
return false;
}
std::unique_lock<std::mutex> wcv_lock(m_wcv_mutex);
m_wcv.wait_for(wcv_lock, std::chrono::milliseconds(1));
}
m_data[pos >= sq_size ? pos - sq_size : pos] = data;
m_sync.atomic_op([](squeue_sync_var_t& sync)
{
assert(sync.count <= sq_size);
assert(sync.position < sq_size);
assert(sync.push_lock);
sync.push_lock = 0;
sync.count++;
});
m_rcv.notify_one();
m_wcv.notify_one();
return true;
}
bool push(const T& data, const volatile bool* do_exit)
{
return push(data, [do_exit](){ return do_exit && *do_exit; });
}
force_inline bool push(const T& data)
{
return push(data, SQUEUE_NEVER_EXIT);
}
force_inline bool try_push(const T& data)
{
return push(data, SQUEUE_ALWAYS_EXIT);
}
bool pop(T& data, const std::function<bool()>& test_exit)
{
u32 pos = 0;
while (u32 res = m_sync.atomic_op([&pos](squeue_sync_var_t& sync) -> u32
{
assert(sync.count <= sq_size);
assert(sync.position < sq_size);
if (!sync.count)
{
return SQSVR_FAILED;
}
if (sync.pop_lock)
{
return SQSVR_LOCKED;
}
sync.pop_lock = 1;
pos = sync.position;
return SQSVR_OK;
}))
{
if (res == SQSVR_FAILED && (test_exit() || squeue_test_exit()))
{
return false;
}
std::unique_lock<std::mutex> rcv_lock(m_rcv_mutex);
m_rcv.wait_for(rcv_lock, std::chrono::milliseconds(1));
}
data = m_data[pos];
m_sync.atomic_op([](squeue_sync_var_t& sync)
{
assert(sync.count <= sq_size);
assert(sync.position < sq_size);
assert(sync.pop_lock);
sync.pop_lock = 0;
sync.position++;
sync.count--;
if (sync.position == sq_size)
{
sync.position = 0;
}
});
m_rcv.notify_one();
m_wcv.notify_one();
return true;
}
bool pop(T& data, const volatile bool* do_exit)
{
return pop(data, [do_exit](){ return do_exit && *do_exit; });
}
force_inline bool pop(T& data)
{
return pop(data, SQUEUE_NEVER_EXIT);
}
force_inline bool try_pop(T& data)
{
return pop(data, SQUEUE_ALWAYS_EXIT);
}
bool peek(T& data, u32 start_pos, const std::function<bool()>& test_exit)
{
assert(start_pos < sq_size);
u32 pos = 0;
while (u32 res = m_sync.atomic_op([&pos, start_pos](squeue_sync_var_t& sync) -> u32
{
assert(sync.count <= sq_size);
assert(sync.position < sq_size);
if (sync.count <= start_pos)
{
return SQSVR_FAILED;
}
if (sync.pop_lock)
{
return SQSVR_LOCKED;
}
sync.pop_lock = 1;
pos = sync.position + start_pos;
return SQSVR_OK;
}))
{
if (res == SQSVR_FAILED && (test_exit() || squeue_test_exit()))
{
return false;
}
std::unique_lock<std::mutex> rcv_lock(m_rcv_mutex);
m_rcv.wait_for(rcv_lock, std::chrono::milliseconds(1));
}
data = m_data[pos >= sq_size ? pos - sq_size : pos];
m_sync.atomic_op([](squeue_sync_var_t& sync)
{
assert(sync.count <= sq_size);
assert(sync.position < sq_size);
assert(sync.pop_lock);
sync.pop_lock = 0;
});
m_rcv.notify_one();
return true;
}
bool peek(T& data, u32 start_pos, const volatile bool* do_exit)
{
return peek(data, start_pos, [do_exit](){ return do_exit && *do_exit; });
}
force_inline bool peek(T& data, u32 start_pos = 0)
{
return peek(data, start_pos, SQUEUE_NEVER_EXIT);
}
force_inline bool try_peek(T& data, u32 start_pos = 0)
{
return peek(data, start_pos, SQUEUE_ALWAYS_EXIT);
}
class squeue_data_t
{
T* const m_data;
const u32 m_pos;
const u32 m_count;
squeue_data_t(T* data, u32 pos, u32 count)
: m_data(data)
, m_pos(pos)
, m_count(count)
{
}
public:
T& operator [] (u32 index)
{
assert(index < m_count);
index += m_pos;
index = index < sq_size ? index : index - sq_size;
return m_data[index];
}
};
void process(void(*proc)(squeue_data_t data))
{
u32 pos, count;
while (m_sync.atomic_op([&pos, &count](squeue_sync_var_t& sync) -> u32
{
assert(sync.count <= sq_size);
assert(sync.position < sq_size);
if (sync.pop_lock || sync.push_lock)
{
return SQSVR_LOCKED;
}
pos = sync.position;
count = sync.count;
sync.pop_lock = 1;
sync.push_lock = 1;
return SQSVR_OK;
}))
{
std::unique_lock<std::mutex> rcv_lock(m_rcv_mutex);
m_rcv.wait_for(rcv_lock, std::chrono::milliseconds(1));
}
proc(squeue_data_t(m_data, pos, count));
m_sync.atomic_op([](squeue_sync_var_t& sync)
{
assert(sync.count <= sq_size);
assert(sync.position < sq_size);
assert(sync.pop_lock && sync.push_lock);
sync.pop_lock = 0;
sync.push_lock = 0;
});
m_wcv.notify_one();
m_rcv.notify_one();
}
void clear()
{
while (m_sync.atomic_op([](squeue_sync_var_t& sync) -> u32
{
assert(sync.count <= sq_size);
assert(sync.position < sq_size);
if (sync.pop_lock || sync.push_lock)
{
return SQSVR_LOCKED;
}
sync.pop_lock = 1;
sync.push_lock = 1;
return SQSVR_OK;
}))
{
std::unique_lock<std::mutex> rcv_lock(m_rcv_mutex);
m_rcv.wait_for(rcv_lock, std::chrono::milliseconds(1));
}
m_sync.exchange({});
m_wcv.notify_one();
m_rcv.notify_one();
}
};

View File

@ -17,10 +17,10 @@ namespace memory_helper
{ {
#ifdef _WIN32 #ifdef _WIN32
void* ret = VirtualAlloc(NULL, size, MEM_RESERVE, PAGE_NOACCESS); void* ret = VirtualAlloc(NULL, size, MEM_RESERVE, PAGE_NOACCESS);
CHECK_ASSERTION(ret != NULL); Ensures(ret != NULL);
#else #else
void* ret = mmap(nullptr, size, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0); void* ret = mmap(nullptr, size, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0);
CHECK_ASSERTION(ret != 0); Ensures(ret != 0);
#endif #endif
return ret; return ret;
} }
@ -28,18 +28,18 @@ namespace memory_helper
void commit_page_memory(void* pointer, size_t page_size) void commit_page_memory(void* pointer, size_t page_size)
{ {
#ifdef _WIN32 #ifdef _WIN32
CHECK_ASSERTION(VirtualAlloc((u8*)pointer, page_size, MEM_COMMIT, PAGE_READWRITE) != NULL); ASSERT(VirtualAlloc((u8*)pointer, page_size, MEM_COMMIT, PAGE_READWRITE) != NULL);
#else #else
CHECK_ASSERTION(mprotect((u8*)pointer, page_size, PROT_READ | PROT_WRITE) != -1); ASSERT(mprotect((u8*)pointer, page_size, PROT_READ | PROT_WRITE) != -1);
#endif #endif
} }
void free_reserved_memory(void* pointer, size_t size) void free_reserved_memory(void* pointer, size_t size)
{ {
#ifdef _WIN32 #ifdef _WIN32
CHECK_ASSERTION(VirtualFree(pointer, 0, MEM_RELEASE) != 0); ASSERT(VirtualFree(pointer, 0, MEM_RELEASE) != 0);
#else #else
CHECK_ASSERTION(munmap(pointer, size) == 0); ASSERT(munmap(pointer, size) == 0);
#endif #endif
} }
} }

View File

@ -1,172 +0,0 @@
#include "stdafx.h"
#include "config_context.h"
#include "StrFmt.h"
#include <iostream>
#include <sstream>
void config_context_t::group::init()
{
if(!m_cfg->m_groups[full_name()])
m_cfg->m_groups[full_name()] = this;
}
config_context_t::group::group(config_context_t* cfg, const std::string& name)
: m_cfg(cfg)
, m_name(name)
, m_parent(nullptr)
{
init();
}
config_context_t::group::group(group* parent, const std::string& name)
: m_cfg(parent->m_cfg)
, m_name(name)
, m_parent(parent)
{
init();
}
void config_context_t::group::set_parent(config_context_t* cfg)
{
m_cfg = cfg;
init();
}
std::string config_context_t::group::name() const
{
return m_name;
}
std::string config_context_t::group::full_name() const
{
if (m_parent)
return m_parent->full_name() + "/" + m_name;
return m_name;
}
void config_context_t::assign(const config_context_t& rhs)
{
for (auto &rhs_g : rhs.m_groups)
{
auto g = m_groups.at(rhs_g.first);
for (auto rhs_e : rhs_g.second->entries)
{
if (g->entries[rhs_e.first])
g->entries[rhs_e.first]->value_from(rhs_e.second);
else
g->add_entry(rhs_e.first, rhs_e.second->string_value());
}
}
}
void config_context_t::deserialize(std::istream& stream)
{
set_defaults();
uint line_index = 0;
std::string line;
group *current_group = nullptr;
while (std::getline(stream, line))
{
++line_index;
line = fmt::trim(line);
if (line.empty())
continue;
if (line.front() == '[' && line.back() == ']')
{
std::string group_name = line.substr(1, line.length() - 2);
auto found = m_groups.find(group_name);
if (found == m_groups.end())
{
std::cerr << line_index << ": group '" << group_name << "' not exists. ignored" << std::endl;
current_group = nullptr;
continue;
}
current_group = found->second;
continue;
}
if (current_group == nullptr)
{
std::cerr << line_index << ": line '" << line << "' ignored, no group." << std::endl;
continue;
}
auto name_value = fmt::split(line, { "=" });
switch (name_value.size())
{
case 1:
{
if (current_group->entries[fmt::trim(name_value[0])])
current_group->entries[fmt::trim(name_value[0])]->string_value({});
else
current_group->add_entry(fmt::trim(name_value[0]), std::string{});
}
break;
default:
std::cerr << line_index << ": line '" << line << "' has more than one symbol '='. used only first" << std::endl;
case 2:
{
if (current_group->entries[fmt::trim(name_value[0])])
current_group->entries[fmt::trim(name_value[0])]->string_value(fmt::trim(name_value[1]));
else
current_group->add_entry(fmt::trim(name_value[0]), fmt::trim(name_value[1]));
}
break;
}
}
}
void config_context_t::serialize(std::ostream& stream) const
{
for (auto &g : m_groups)
{
stream << "[" + g.first + "]" << std::endl;
for (auto &e : g.second->entries)
{
stream << e.first << "=" << e.second->string_value() << std::endl;
}
stream << std::endl;
}
}
void config_context_t::set_defaults()
{
for (auto &g : m_groups)
{
for (auto &e : g.second->entries)
{
e.second->to_default();
}
}
}
std::string config_context_t::to_string() const
{
std::ostringstream result;
serialize(result);
return result.str();
}
void config_context_t::from_string(const std::string& str)
{
std::istringstream source(str);
deserialize(source);
}

View File

@ -1,164 +0,0 @@
#pragma once
#include <unordered_map>
#include <string>
#include "convert.h"
class config_context_t
{
public:
class entry_base;
protected:
class group
{
group* m_parent;
config_context_t* m_cfg;
std::string m_name;
std::vector<std::unique_ptr<entry_base>> m_entries;
void init();
public:
std::unordered_map<std::string, entry_base *> entries;
group(config_context_t* cfg, const std::string& name);
group(group* parent, const std::string& name);
void set_parent(config_context_t* cfg);
std::string name() const;
std::string full_name() const;
template<typename T>
void add_entry(const std::string& name, const T& def_value)
{
m_entries.emplace_back(std::make_unique<entry<T>>(this, name, def_value));
}
template<typename T>
T get_entry_value(const std::string& name, const T& def_value)
{
if (!entries[name])
add_entry(name, def_value);
return convert::to<T>(entries[name]->string_value());
}
template<typename T>
void set_entry_value(const std::string& name, const T& value)
{
if (entries[name])
entries[name]->string_value(convert::to<std::string>(value));
else
add_entry(name, value);
}
friend config_context_t;
};
public:
class entry_base
{
public:
virtual ~entry_base() = default;
virtual std::string name() = 0;
virtual void to_default() = 0;
virtual std::string string_value() = 0;
virtual void string_value(const std::string& value) = 0;
virtual void value_from(const entry_base* rhs) = 0;
};
template<typename T>
class entry : public entry_base
{
T m_default_value;
T m_value;
group* m_parent;
std::string m_name;
public:
entry(group* parent, const std::string& name, const T& default_value)
: m_parent(parent)
, m_name(name)
, m_default_value(default_value)
, m_value(default_value)
{
if(!parent->entries[name])
parent->entries[name] = this;
}
T default_value() const
{
return m_default_value;
}
T value() const
{
return m_value;
}
void value(const T& new_value)
{
m_value = new_value;
}
std::string name() override
{
return m_name;
}
void to_default() override
{
value(default_value());
}
std::string string_value() override
{
return convert::to<std::string>(value());
}
void string_value(const std::string &new_value) override
{
value(convert::to<T>(new_value));
}
void value_from(const entry_base* rhs) override
{
value(static_cast<const entry*>(rhs)->value());
}
entry& operator = (const T& new_value)
{
value(new_value);
return *this;
}
template<typename T2>
entry& operator = (const T2& new_value)
{
value(static_cast<T>(new_value));
return *this;
}
explicit operator const T&() const
{
return m_value;
}
};
private:
std::unordered_map<std::string, group*> m_groups;
public:
config_context_t() = default;
void assign(const config_context_t& rhs);
void serialize(std::ostream& stream) const;
void deserialize(std::istream& stream);
void set_defaults();
std::string to_string() const;
void from_string(const std::string&);
};

View File

@ -1,279 +0,0 @@
#pragma once
#include <string>
#include "types.h"
namespace convert
{
template<typename ReturnType, typename FromType>
struct to_impl_t;
template<typename Type>
struct to_impl_t<Type, Type>
{
static Type func(const Type& value)
{
return value;
}
};
template<>
struct to_impl_t<std::string, bool>
{
static std::string func(bool value)
{
return value ? "true" : "false";
}
};
template<>
struct to_impl_t<bool, std::string>
{
static bool func(const std::string& value)
{
return value == "true" ? true : value == "false" ? false : throw std::invalid_argument(__FUNCTION__);
}
};
template<>
struct to_impl_t<std::string, signed char>
{
static std::string func(signed char value)
{
return std::to_string(value);
}
};
template<>
struct to_impl_t<std::string, unsigned char>
{
static std::string func(unsigned char value)
{
return std::to_string(value);
}
};
template<>
struct to_impl_t<std::string, short>
{
static std::string func(short value)
{
return std::to_string(value);
}
};
template<>
struct to_impl_t<std::string, unsigned short>
{
static std::string func(unsigned short value)
{
return std::to_string(value);
}
};
template<>
struct to_impl_t<std::string, int>
{
static std::string func(int value)
{
return std::to_string(value);
}
};
template<>
struct to_impl_t<std::string, unsigned int>
{
static std::string func(unsigned int value)
{
return std::to_string(value);
}
};
template<>
struct to_impl_t<std::string, long>
{
static std::string func(long value)
{
return std::to_string(value);
}
};
template<>
struct to_impl_t<std::string, unsigned long>
{
static std::string func(unsigned long value)
{
return std::to_string(value);
}
};
template<>
struct to_impl_t<std::string, long long>
{
static std::string func(long long value)
{
return std::to_string(value);
}
};
template<>
struct to_impl_t<std::string, unsigned long long>
{
static std::string func(unsigned long long value)
{
return std::to_string(value);
}
};
template<>
struct to_impl_t<std::string, float>
{
static std::string func(float value)
{
return std::to_string(value);
}
};
template<>
struct to_impl_t<std::string, double>
{
static std::string func(double value)
{
return std::to_string(value);
}
};
template<>
struct to_impl_t<std::string, long double>
{
static std::string func(long double value)
{
return std::to_string(value);
}
};
template<>
struct to_impl_t<std::string, size2i>
{
static std::string func(size2i value)
{
return std::to_string(value.width) + "x" + std::to_string(value.height);
}
};
template<>
struct to_impl_t<std::string, position2i>
{
static std::string func(position2i value)
{
return std::to_string(value.x) + ":" + std::to_string(value.y);
}
};
template<>
struct to_impl_t<int, std::string>
{
static int func(const std::string& value)
{
return std::stoi(value);
}
};
template<>
struct to_impl_t<unsigned int, std::string>
{
static unsigned int func(const std::string& value)
{
return (unsigned long)std::stoul(value);
}
};
template<>
struct to_impl_t<long, std::string>
{
static long func(const std::string& value)
{
return std::stol(value);
}
};
template<>
struct to_impl_t<unsigned long, std::string>
{
static unsigned long func(const std::string& value)
{
return std::stoul(value);
}
};
template<>
struct to_impl_t<long long, std::string>
{
static long long func(const std::string& value)
{
return std::stoll(value);
}
};
template<>
struct to_impl_t<unsigned long long, std::string>
{
static unsigned long long func(const std::string& value)
{
return std::stoull(value);
}
};
template<>
struct to_impl_t<float, std::string>
{
static float func(const std::string& value)
{
return std::stof(value);
}
};
template<>
struct to_impl_t<double, std::string>
{
static double func(const std::string& value)
{
return std::stod(value);
}
};
template<>
struct to_impl_t<long double, std::string>
{
static long double func(const std::string& value)
{
return std::stold(value);
}
};
template<>
struct to_impl_t<size2i, std::string>
{
static size2i func(const std::string& value)
{
const auto& data = fmt::split(value, { "x" });
return { std::stoi(data[0]), std::stoi(data[1]) };
}
};
template<>
struct to_impl_t<position2i, std::string>
{
static position2i func(const std::string& value)
{
const auto& data = fmt::split(value, { ":" });
return { std::stoi(data[0]), std::stoi(data[1]) };
}
};
template<typename ReturnType, typename FromType>
ReturnType to(FromType value)
{
return to_impl_t<std::remove_all_extents_t<ReturnType>, std::remove_all_extents_t<FromType>>::func(value);
}
}

View File

@ -1,41 +0,0 @@
#include "stdafx.h"
#include "restore_new.h"
#include "Utilities/Log.h"
#pragma warning(push)
#pragma message("TODO: remove wx dependency: <wx/image.h>")
#pragma warning(disable : 4996)
#include <wx/image.h>
#pragma warning(pop)
#include "define_new_memleakdetect.h"
#ifndef _WIN32
#include <dirent.h>
#endif
#include "rPlatform.h"
rImage::rImage()
{
handle = static_cast<void*>(new wxImage());
}
rImage::~rImage()
{
delete static_cast<wxImage*>(handle);
}
void rImage::Create(int width, int height, void *data, void *alpha)
{
static_cast<wxImage*>(handle)->Create(width, height, static_cast<unsigned char*>(data), static_cast<unsigned char*>(alpha));
}
void rImage::SaveFile(const std::string& name, rImageType type)
{
if (type == rBITMAP_TYPE_PNG)
{
static_cast<wxImage*>(handle)->SaveFile(fmt::FromUTF8(name),wxBITMAP_TYPE_PNG);
}
else
{
throw EXCEPTION("unsupported type");
}
}

View File

@ -1,40 +0,0 @@
#pragma once
/**********************************************************************
*********** RSX Debugger
************************************************************************/
struct RSXDebuggerProgram
{
u32 id;
u32 vp_id;
u32 fp_id;
std::string vp_shader;
std::string fp_shader;
bool modified;
RSXDebuggerProgram()
: modified(false)
{
}
};
extern std::vector<RSXDebuggerProgram> m_debug_programs;
/**********************************************************************
*********** Image stuff
************************************************************************/
enum rImageType
{
rBITMAP_TYPE_PNG
};
struct rImage
{
rImage();
rImage(const rImage &) = delete;
~rImage();
void Create(int width , int height, void *data, void *alpha);
void SaveFile(const std::string& name, rImageType type);
void *handle;
};

View File

@ -1,235 +0,0 @@
#include "stdafx.h"
#include "rTime.h"
#pragma warning(push)
#pragma message("TODO: remove wx dependency: <wx/datetime.h>")
#pragma warning(disable : 4996)
#include <wx/datetime.h>
#pragma warning(pop)
std::string rDefaultDateTimeFormat = "%c";
rTimeSpan::rTimeSpan()
{
handle = static_cast<void *>(new wxTimeSpan());
}
rTimeSpan::~rTimeSpan()
{
delete static_cast<wxTimeSpan*>(handle);
}
rTimeSpan::rTimeSpan(const rTimeSpan& other)
{
handle = static_cast<void *>(new wxTimeSpan(*static_cast<wxTimeSpan*>(other.handle)));
}
rTimeSpan::rTimeSpan(int a, int b , int c, int d)
{
handle = static_cast<void *>(new wxTimeSpan(a,b,c,d));
}
rDateSpan::rDateSpan()
{
handle = static_cast<void *>(new wxDateSpan());
}
rDateSpan::~rDateSpan()
{
delete static_cast<wxDateSpan*>(handle);
}
rDateSpan::rDateSpan(const rDateSpan& other)
{
handle = static_cast<void *>(new wxDateSpan(*static_cast<wxDateSpan*>(other.handle)));
}
rDateSpan::rDateSpan(int a, int b, int c, int d)
{
handle = static_cast<void *>(new wxDateSpan(a,b,c,d));
}
rDateTime::rDateTime()
{
handle = static_cast<void *>(new wxDateTime());
}
rDateTime::~rDateTime()
{
delete static_cast<wxDateTime*>(handle);
}
rDateTime::rDateTime(const rDateTime& other)
{
handle = static_cast<void *>(new wxDateTime(*static_cast<wxDateTime*>(other.handle)));
}
rDateTime::rDateTime(const time_t& time)
{
handle = static_cast<void *>(new wxDateTime(time));
}
rDateTime::rDateTime(u16 day, rDateTime::Month month, u16 year, u16 hour, u16 minute, u16 second, u32 millisecond)
{
handle = static_cast<void *>(new wxDateTime(day,(wxDateTime::Month)month,year,hour,minute,second,millisecond));
}
rDateTime rDateTime::UNow()
{
rDateTime time;
delete static_cast<wxDateTime*>(time.handle);
time.handle = static_cast<void *>(new wxDateTime(wxDateTime::UNow()));
return time;
}
rDateTime rDateTime::FromUTC(bool val)
{
rDateTime time(*this);
void *temp = time.handle;
time.handle = static_cast<void *>(new wxDateTime(static_cast<wxDateTime*>(temp)->FromTimezone(wxDateTime::GMT0, val)));
delete static_cast<wxDateTime*>(temp);
return time;
}
rDateTime rDateTime::ToUTC(bool val)
{
rDateTime time(*this);
void *temp = time.handle;
time.handle = static_cast<void *>(new wxDateTime(static_cast<wxDateTime*>(temp)->ToTimezone(wxDateTime::GMT0, val)));
delete static_cast<wxDateTime*>(temp);
return time;
}
time_t rDateTime::GetTicks()
{
return static_cast<wxDateTime*>(handle)->GetTicks();
}
void rDateTime::Add(const rTimeSpan& span)
{
static_cast<wxDateTime*>(handle)->Add(*static_cast<wxTimeSpan*>(span.handle));
}
void rDateTime::Add(const rDateSpan& span)
{
static_cast<wxDateTime*>(handle)->Add(*static_cast<wxDateSpan*>(span.handle));
}
wxDateTime::TimeZone convertTZ(rDateTime::rTimeZone tz)
{
switch (tz)
{
case rDateTime::Local:
return wxDateTime::Local;
case rDateTime::GMT0:
return wxDateTime::GMT0;
case rDateTime::UTC:
return wxDateTime::UTC;
default:
throw EXCEPTION("WRONG DATETIME");
}
}
std::string rDateTime::Format(const std::string &format, const rTimeZone &tz) const
{
return fmt::ToUTF8(static_cast<wxDateTime*>(handle)->Format(fmt::FromUTF8(format),convertTZ(tz)));
}
void rDateTime::ParseDateTime(const char* format)
{
static_cast<wxDateTime*>(handle)->ParseDateTime(format);
}
u32 rDateTime::GetAsDOS()
{
return static_cast<wxDateTime*>(handle)->GetAsDOS();
}
rDateTime &rDateTime::SetFromDOS(u32 fromdos)
{
static_cast<wxDateTime*>(handle)->SetFromDOS(fromdos);
return *this;
}
bool rDateTime::IsLeapYear(int year, rDateTime::Calender cal)
{
if (cal == Gregorian)
{
return wxDateTime::IsLeapYear(year, wxDateTime::Gregorian);
}
else
{
return wxDateTime::IsLeapYear(year, wxDateTime::Julian);
}
}
int rDateTime::GetNumberOfDays(rDateTime::Month month, int year, rDateTime::Calender cal)
{
if (cal == Gregorian)
{
return wxDateTime::GetNumberOfDays(static_cast<wxDateTime::Month>(month), year, wxDateTime::Gregorian);
}
else
{
return wxDateTime::GetNumberOfDays(static_cast<wxDateTime::Month>(month), year, wxDateTime::Julian);
}
}
void rDateTime::SetToWeekDay(rDateTime::WeekDay day, int n, rDateTime::Month month, int year)
{
static_cast<wxDateTime*>(handle)->SetToWeekDay(
static_cast<wxDateTime::WeekDay>(day)
, n
, static_cast<wxDateTime::Month>(month)
, year
);
}
int rDateTime::GetWeekDay()
{
return static_cast<wxDateTime*>(handle)->GetWeekDay();
}
u16 rDateTime::GetYear(rDateTime::TZ timezone)
{
return static_cast<wxDateTime*>(handle)->GetYear(convertTZ(timezone));
}
u16 rDateTime::GetMonth(rDateTime::TZ timezone)
{
return static_cast<wxDateTime*>(handle)->GetMonth(convertTZ(timezone));
}
u16 rDateTime::GetDay(rDateTime::TZ timezone)
{
return static_cast<wxDateTime*>(handle)->GetDay(convertTZ(timezone));
}
u16 rDateTime::GetHour(rDateTime::TZ timezone)
{
return static_cast<wxDateTime*>(handle)->GetHour(convertTZ(timezone));
}
u16 rDateTime::GetMinute(rDateTime::TZ timezone)
{
return static_cast<wxDateTime*>(handle)->GetMinute(convertTZ(timezone));
}
u16 rDateTime::GetSecond(rDateTime::TZ timezone)
{
return static_cast<wxDateTime*>(handle)->GetSecond(convertTZ(timezone));
}
u32 rDateTime::GetMillisecond(rDateTime::TZ timezone)
{
return static_cast<wxDateTime*>(handle)->GetMillisecond(convertTZ(timezone));
}

View File

@ -1,102 +0,0 @@
#pragma once
extern std::string rDefaultDateTimeFormat;
struct rTimeSpan
{
rTimeSpan();
~rTimeSpan();
rTimeSpan(const rTimeSpan& other);
rTimeSpan(int, int, int, int);
void *handle;
};
struct rDateSpan
{
rDateSpan();
~rDateSpan();
rDateSpan(const rDateSpan& other);
rDateSpan(int, int, int, int);
void *handle;
};
struct rDateTime
{
enum TZ
{
Local, GMT0,UTC
};
enum Calender
{
Gregorian, Julian
};
using rTimeZone = TZ;
enum WeekDay
{
Sun = 0,
Mon,
Tue,
Wed,
Thu,
Fri,
Sat,
Inv_WeekDay
};
enum Month {
Jan = 0,
Feb = 1,
Mar = 2,
Apr = 3,
May = 4,
Jun = 5,
Jul = 6,
Aug = 7,
Sep = 8,
Oct = 9,
Nov = 10,
Dec = 11,
Inv_Month = 12
};
rDateTime();
~rDateTime();
rDateTime(const rDateTime& other);
rDateTime(const time_t &time);
rDateTime(u16 day, rDateTime::Month month, u16 year, u16 hour, u16 minute, u16 second, u32 millisecond);
static rDateTime UNow();
rDateTime FromUTC(bool val);
rDateTime ToUTC(bool val);
time_t GetTicks();
void Add(const rTimeSpan& span);
void Add(const rDateSpan& span);
void Close();
std::string Format(const std::string &format = rDefaultDateTimeFormat, const rTimeZone &tz = Local) const;
void ParseDateTime(const char* format);
u32 GetAsDOS();
rDateTime &SetFromDOS(u32 fromdos);
static bool IsLeapYear(int year, rDateTime::Calender cal);
static int GetNumberOfDays(rDateTime::Month month, int year, rDateTime::Calender cal);
void SetToWeekDay(rDateTime::WeekDay day, int n, rDateTime::Month month, int year);
int GetWeekDay();
u16 GetYear( rDateTime::TZ timezone);
u16 GetMonth(rDateTime::TZ timezone);
u16 GetDay(rDateTime::TZ timezone);
u16 GetHour(rDateTime::TZ timezone);
u16 GetMinute(rDateTime::TZ timezone);
u16 GetSecond(rDateTime::TZ timezone);
u32 GetMillisecond(rDateTime::TZ timezone);
void *handle;
};

View File

@ -1,16 +1,21 @@
#pragma once #pragma once
#include <new>
#include <typeinfo> #include <typeinfo>
#include <type_traits>
#include <exception>
#include <utility>
#include <cstdint> #include <cstdint>
#include <cmath> #include <cmath>
#include <algorithm>
#include "Platform.h"
using schar = signed char;
using uchar = unsigned char; using uchar = unsigned char;
using ushort = unsigned short; using ushort = unsigned short;
using uint = unsigned int; using uint = unsigned int;
using ulong = unsigned long; using ulong = unsigned long;
using ullong = unsigned long long; using ullong = unsigned long long;
using llong = long long; using llong = long long;
using u8 = std::uint8_t; using u8 = std::uint8_t;
@ -23,6 +28,350 @@ using s16 = std::int16_t;
using s32 = std::int32_t; using s32 = std::int32_t;
using s64 = std::int64_t; using s64 = std::int64_t;
// Specialization with static constexpr pair<T1, T2> map[] member expected
template<typename T1, typename T2>
struct bijective;
template<typename T, std::size_t Size = sizeof(T)>
struct atomic_storage;
template<typename T1, typename T2, typename = void>
struct atomic_add;
template<typename T1, typename T2, typename = void>
struct atomic_sub;
template<typename T1, typename T2, typename = void>
struct atomic_and;
template<typename T1, typename T2, typename = void>
struct atomic_or;
template<typename T1, typename T2, typename = void>
struct atomic_xor;
template<typename T, typename = void>
struct atomic_pre_inc;
template<typename T, typename = void>
struct atomic_post_inc;
template<typename T, typename = void>
struct atomic_pre_dec;
template<typename T, typename = void>
struct atomic_post_dec;
template<typename T1, typename T2, typename = void>
struct atomic_test_and_set;
template<typename T1, typename T2, typename = void>
struct atomic_test_and_reset;
template<typename T1, typename T2, typename = void>
struct atomic_test_and_complement;
template<typename T>
class atomic_t;
namespace fmt
{
template<typename T, typename = void>
struct unveil;
}
// TODO: replace with std::void_t when available
namespace void_details
{
template<class... >
struct make_void
{
using type = void;
};
}
template<class... T> using void_t = typename void_details::make_void<T...>::type;
// Extract T::simple_type if available, remove cv qualifiers
template<typename T, typename = void>
struct simple_type_helper
{
using type = typename std::remove_cv<T>::type;
};
template<typename T>
struct simple_type_helper<T, void_t<typename T::simple_type>>
{
using type = typename T::simple_type;
};
template<typename T> using simple_t = typename simple_type_helper<T>::type;
// Bool type equivalent
class b8
{
std::uint8_t m_value;
public:
b8() = default;
constexpr b8(bool value)
: m_value(value)
{
}
constexpr operator bool() const
{
return m_value != 0;
}
};
// Bool wrapper for restricting bool result conversions
struct explicit_bool_t
{
const bool value;
constexpr explicit_bool_t(bool value)
: value(value)
{
}
explicit constexpr operator bool() const
{
return value;
}
};
#ifndef _MSC_VER
using u128 = __uint128_t;
using s128 = __int128_t;
#else
#include "intrin.h"
// Unsigned 128-bit integer implementation (TODO)
struct alignas(16) u128
{
std::uint64_t lo, hi;
u128() = default;
constexpr u128(std::uint64_t l)
: lo(l)
, hi(0)
{
}
friend u128 operator +(const u128& l, const u128& r)
{
u128 value;
_addcarry_u64(_addcarry_u64(0, r.lo, l.lo, &value.lo), r.hi, l.hi, &value.hi);
return value;
}
friend u128 operator +(const u128& l, std::uint64_t r)
{
u128 value;
_addcarry_u64(_addcarry_u64(0, r, l.lo, &value.lo), l.hi, 0, &value.hi);
return value;
}
friend u128 operator +(std::uint64_t l, const u128& r)
{
u128 value;
_addcarry_u64(_addcarry_u64(0, r.lo, l, &value.lo), 0, r.hi, &value.hi);
return value;
}
friend u128 operator -(const u128& l, const u128& r)
{
u128 value;
_subborrow_u64(_subborrow_u64(0, r.lo, l.lo, &value.lo), r.hi, l.hi, &value.hi);
return value;
}
friend u128 operator -(const u128& l, std::uint64_t r)
{
u128 value;
_subborrow_u64(_subborrow_u64(0, r, l.lo, &value.lo), 0, l.hi, &value.hi);
return value;
}
friend u128 operator -(std::uint64_t l, const u128& r)
{
u128 value;
_subborrow_u64(_subborrow_u64(0, r.lo, l, &value.lo), r.hi, 0, &value.hi);
return value;
}
u128 operator +() const
{
return *this;
}
u128 operator -() const
{
u128 value;
_subborrow_u64(_subborrow_u64(0, lo, 0, &value.lo), hi, 0, &value.hi);
return value;
}
u128& operator ++()
{
_addcarry_u64(_addcarry_u64(0, 1, lo, &lo), 0, hi, &hi);
return *this;
}
u128 operator ++(int)
{
u128 value = *this;
_addcarry_u64(_addcarry_u64(0, 1, lo, &lo), 0, hi, &hi);
return value;
}
u128& operator --()
{
_subborrow_u64(_subborrow_u64(0, 1, lo, &lo), 0, hi, &hi);
return *this;
}
u128 operator --(int)
{
u128 value = *this;
_subborrow_u64(_subborrow_u64(0, 1, lo, &lo), 0, hi, &hi);
return value;
}
u128 operator ~() const
{
u128 value;
value.lo = ~lo;
value.hi = ~hi;
return value;
}
friend u128 operator &(const u128& l, const u128& r)
{
u128 value;
value.lo = l.lo & r.lo;
value.hi = l.hi & r.hi;
return value;
}
friend u128 operator |(const u128& l, const u128& r)
{
u128 value;
value.lo = l.lo | r.lo;
value.hi = l.hi | r.hi;
return value;
}
friend u128 operator ^(const u128& l, const u128& r)
{
u128 value;
value.lo = l.lo ^ r.lo;
value.hi = l.hi ^ r.hi;
return value;
}
u128& operator +=(const u128& r)
{
_addcarry_u64(_addcarry_u64(0, r.lo, lo, &lo), r.hi, hi, &hi);
return *this;
}
u128& operator +=(uint64_t r)
{
_addcarry_u64(_addcarry_u64(0, r, lo, &lo), 0, hi, &hi);
return *this;
}
u128& operator &=(const u128& r)
{
lo &= r.lo;
hi &= r.hi;
return *this;
}
u128& operator |=(const u128& r)
{
lo |= r.lo;
hi |= r.hi;
return *this;
}
u128& operator ^=(const u128& r)
{
lo ^= r.lo;
hi ^= r.hi;
return *this;
}
};
// Signed 128-bit integer implementation (TODO)
struct alignas(16) s128
{
std::uint64_t lo;
std::int64_t hi;
s128() = default;
constexpr s128(std::int64_t l)
: hi(l >> 63)
, lo(l)
{
}
constexpr s128(std::uint64_t l)
: hi(0)
, lo(l)
{
}
};
#endif
namespace std
{
/* Let's hack. */
template<>
struct is_integral<u128> : true_type
{
};
template<>
struct is_integral<s128> : true_type
{
};
template<>
struct make_unsigned<u128>
{
using type = u128;
};
template<>
struct make_unsigned<s128>
{
using type = u128;
};
template<>
struct make_signed<u128>
{
using type = s128;
};
template<>
struct make_signed<s128>
{
using type = s128;
};
}
static_assert(std::is_arithmetic<u128>::value && std::is_integral<u128>::value && alignof(u128) == 16 && sizeof(u128) == 16, "Wrong u128 implementation");
static_assert(std::is_arithmetic<s128>::value && std::is_integral<s128>::value && alignof(s128) == 16 && sizeof(s128) == 16, "Wrong s128 implementation");
union alignas(2) f16 union alignas(2) f16
{ {
u16 _u16; u16 _u16;
@ -55,6 +404,313 @@ struct ignore
} }
}; };
// Allows to define integer convertible to multiple enum types
template<typename T = void, typename... Ts>
struct multicast : multicast<Ts...>
{
static_assert(std::is_enum<T>::value, "multicast<> error: invalid conversion type (enum type expected)");
multicast() = default;
template<typename UT>
constexpr multicast(const UT& value)
: multicast<Ts...>(value)
, m_value{ value } // Forbid narrowing
{
}
constexpr operator T() const
{
// Cast to enum type
return static_cast<T>(m_value);
}
private:
std::underlying_type_t<T> m_value;
};
// Recursion terminator
template<>
struct multicast<void>
{
multicast() = default;
template<typename UT>
constexpr multicast(const UT& value)
{
}
};
// Small bitset for enum class types with available values [0, bitsize).
// T must be either enum type or convertible to (registered with via simple_t<T>).
// Internal representation is single value of type T.
template<typename T>
struct mset
{
using type = simple_t<T>;
using under = std::underlying_type_t<type>;
static constexpr auto bitsize = sizeof(type) * CHAR_BIT;
mset() = default;
constexpr mset(type _enum_const)
: m_value(static_cast<type>(shift(_enum_const)))
{
}
constexpr mset(under raw_value, const std::nothrow_t&)
: m_value(static_cast<T>(raw_value))
{
}
// Get underlying value
constexpr under _value() const
{
return static_cast<under>(m_value);
}
explicit constexpr operator bool() const
{
return _value() ? true : false;
}
mset& operator +=(mset rhs)
{
return *this = { _value() | rhs._value(), std::nothrow };
}
mset& operator -=(mset rhs)
{
return *this = { _value() & ~rhs._value(), std::nothrow };
}
mset& operator &=(mset rhs)
{
return *this = { _value() & rhs._value(), std::nothrow };
}
mset& operator ^=(mset rhs)
{
return *this = { _value() ^ rhs._value(), std::nothrow };
}
friend constexpr mset operator +(mset lhs, mset rhs)
{
return{ lhs._value() | rhs._value(), std::nothrow };
}
friend constexpr mset operator -(mset lhs, mset rhs)
{
return{ lhs._value() & ~rhs._value(), std::nothrow };
}
friend constexpr mset operator &(mset lhs, mset rhs)
{
return{ lhs._value() & rhs._value(), std::nothrow };
}
friend constexpr mset operator ^(mset lhs, mset rhs)
{
return{ lhs._value() ^ rhs._value(), std::nothrow };
}
bool test(mset rhs) const
{
const under v = _value();
const under s = rhs._value();
return (v & s) != 0;
}
bool test_and_set(mset rhs)
{
const under v = _value();
const under s = rhs._value();
*this = { v | s, std::nothrow };
return (v & s) != 0;
}
bool test_and_reset(mset rhs)
{
const under v = _value();
const under s = rhs._value();
*this = { v & ~s, std::nothrow };
return (v & s) != 0;
}
bool test_and_complement(mset rhs)
{
const under v = _value();
const under s = rhs._value();
*this = { v ^ s, std::nothrow };
return (v & s) != 0;
}
private:
[[noreturn]] static under xrange()
{
throw std::out_of_range("mset<>: bit out of range");
}
static constexpr under shift(const T& value)
{
return static_cast<under>(value) < bitsize ? static_cast<under>(1) << static_cast<under>(value) : xrange();
}
T m_value;
};
template<typename T, typename RT = T>
constexpr RT to_mset()
{
return RT{};
}
// Fold enum constants into mset<>
template<typename T = void, typename Arg, typename... Args, typename RT = std::conditional_t<std::is_void<T>::value, mset<Arg>, T>>
constexpr RT to_mset(Arg&& _enum_const, Args&&... args)
{
return RT{ std::forward<Arg>(_enum_const) } + to_mset<RT>(std::forward<Args>(args)...);
}
template<typename T, typename CT>
struct atomic_add<mset<T>, CT, std::enable_if_t<std::is_enum<T>::value>>
{
using under = typename mset<T>::under;
static force_inline mset<T> op1(mset<T>& left, mset<T> right)
{
return{ atomic_storage<under>::fetch_or(reinterpret_cast<under&>(left), right._value()), std::nothrow };
}
static constexpr auto fetch_op = &op1;
static force_inline mset<T> op2(mset<T>& left, mset<T> right)
{
return{ atomic_storage<under>::or_fetch(reinterpret_cast<under&>(left), right._value()), std::nothrow };
}
static constexpr auto op_fetch = &op2;
static constexpr auto atomic_op = &op2;
};
template<typename T, typename CT>
struct atomic_sub<mset<T>, CT, std::enable_if_t<std::is_enum<T>::value>>
{
using under = typename mset<T>::under;
static force_inline mset<T> op1(mset<T>& left, mset<T> right)
{
return{ atomic_storage<under>::fetch_and(reinterpret_cast<under&>(left), ~right._value()), std::nothrow };
}
static constexpr auto fetch_op = &op1;
static force_inline mset<T> op2(mset<T>& left, mset<T> right)
{
return{ atomic_storage<under>::and_fetch(reinterpret_cast<under&>(left), ~right._value()), std::nothrow };
}
static constexpr auto op_fetch = &op2;
static constexpr auto atomic_op = &op2;
};
template<typename T, typename CT>
struct atomic_and<mset<T>, CT, std::enable_if_t<std::is_enum<T>::value>>
{
using under = typename mset<T>::under;
static force_inline mset<T> op1(mset<T>& left, mset<T> right)
{
return{ atomic_storage<under>::fetch_and(reinterpret_cast<under&>(left), right._value()), std::nothrow };
}
static constexpr auto fetch_op = &op1;
static force_inline mset<T> op2(mset<T>& left, mset<T> right)
{
return{ atomic_storage<under>::and_fetch(reinterpret_cast<under&>(left), right._value()), std::nothrow };
}
static constexpr auto op_fetch = &op2;
static constexpr auto atomic_op = &op2;
};
template<typename T, typename CT>
struct atomic_xor<mset<T>, CT, std::enable_if_t<std::is_enum<T>::value>>
{
using under = typename mset<T>::under;
static force_inline mset<T> op1(mset<T>& left, mset<T> right)
{
return{ atomic_storage<under>::fetch_xor(reinterpret_cast<under&>(left), right._value()), std::nothrow };
}
static constexpr auto fetch_op = &op1;
static force_inline mset<T> op2(mset<T>& left, mset<T> right)
{
return{ atomic_storage<under>::xor_fetch(reinterpret_cast<under&>(left), right._value()), std::nothrow };
}
static constexpr auto op_fetch = &op2;
static constexpr auto atomic_op = &op2;
};
template<typename T>
struct atomic_test_and_set<mset<T>, T, std::enable_if_t<std::is_enum<T>::value>>
{
using under = typename mset<T>::under;
static force_inline bool _op(mset<T>& left, const T& value)
{
return atomic_storage<under>::bts(reinterpret_cast<under&>(left), static_cast<uint>(value));
}
static constexpr auto atomic_op = &_op;
};
template<typename T>
struct atomic_test_and_reset<mset<T>, T, std::enable_if_t<std::is_enum<T>::value>>
{
using under = typename mset<T>::under;
static force_inline bool _op(mset<T>& left, const T& value)
{
return atomic_storage<under>::btr(reinterpret_cast<under&>(left), static_cast<uint>(value));
}
static constexpr auto atomic_op = &_op;
};
template<typename T>
struct atomic_test_and_complement<mset<T>, T, std::enable_if_t<std::is_enum<T>::value>>
{
using under = typename mset<T>::under;
static force_inline bool _op(mset<T>& left, const T& value)
{
return atomic_storage<under>::btc(reinterpret_cast<under&>(left), static_cast<uint>(value));
}
static constexpr auto atomic_op = &_op;
};
template<typename T1, typename T2 = const char*, typename T = T1, typename DT = T2>
T2 bijective_find(const T& left, const DT& def = {})
{
for (const auto& pair : bijective<T1, T2>::map)
{
if (pair.first == left)
{
return pair.second;
}
}
return def;
}
template<typename T> template<typename T>
struct size2_base struct size2_base
{ {
@ -1088,15 +1744,3 @@ using color2d = color2_base<double>;
using color1i = color1_base<int>; using color1i = color1_base<int>;
using color1f = color1_base<float>; using color1f = color1_base<float>;
using color1d = color1_base<double>; using color1d = color1_base<double>;
namespace std
{
template<>
struct hash<::position2i>
{
size_t operator()(const ::position2i& position) const
{
return (static_cast<size_t>(position.x) << 32) | position.y;
}
};
}

View File

@ -300,7 +300,7 @@
// Recommended setting: 0 as the options below already provide a relatively // Recommended setting: 0 as the options below already provide a relatively
// good level of interoperability and changing this option arguably isn't worth // good level of interoperability and changing this option arguably isn't worth
// diverging from the official builds of the library. // diverging from the official builds of the library.
#define wxUSE_STL 0 #define wxUSE_STL 1
// This is not a real option but is used as the default value for // This is not a real option but is used as the default value for
// wxUSE_STD_IOSTREAM, wxUSE_STD_STRING and wxUSE_STD_CONTAINERS_COMPATIBLY. // wxUSE_STD_IOSTREAM, wxUSE_STD_STRING and wxUSE_STD_CONTAINERS_COMPATIBLY.

View File

@ -0,0 +1,99 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{FDC361C5-7734-493B-8CFB-037308B35122}</ProjectGuid>
<RootNamespace>yamlcpp</RootNamespace>
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v140</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="..\rpcs3_default.props" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="..\rpcs3_debug.props" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="..\rpcs3_release.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<ItemGroup>
<ClCompile Include="yaml-cpp\src\binary.cpp">
</ClCompile>
<ClCompile Include="yaml-cpp\src\convert.cpp">
</ClCompile>
<ClCompile Include="yaml-cpp\src\directives.cpp">
</ClCompile>
<ClCompile Include="yaml-cpp\src\emit.cpp">
</ClCompile>
<ClCompile Include="yaml-cpp\src\emitfromevents.cpp">
</ClCompile>
<ClCompile Include="yaml-cpp\src\emitter.cpp">
</ClCompile>
<ClCompile Include="yaml-cpp\src\emitterstate.cpp">
</ClCompile>
<ClCompile Include="yaml-cpp\src\emitterutils.cpp">
</ClCompile>
<ClCompile Include="yaml-cpp\src\exp.cpp">
</ClCompile>
<ClCompile Include="yaml-cpp\src\memory.cpp">
</ClCompile>
<ClCompile Include="yaml-cpp\src\node.cpp">
</ClCompile>
<ClCompile Include="yaml-cpp\src\nodebuilder.cpp">
</ClCompile>
<ClCompile Include="yaml-cpp\src\nodeevents.cpp">
</ClCompile>
<ClCompile Include="yaml-cpp\src\node_data.cpp">
</ClCompile>
<ClCompile Include="yaml-cpp\src\null.cpp">
</ClCompile>
<ClCompile Include="yaml-cpp\src\ostream_wrapper.cpp">
</ClCompile>
<ClCompile Include="yaml-cpp\src\parse.cpp">
</ClCompile>
<ClCompile Include="yaml-cpp\src\parser.cpp">
</ClCompile>
<ClCompile Include="yaml-cpp\src\regex_yaml.cpp">
</ClCompile>
<ClCompile Include="yaml-cpp\src\scanner.cpp">
</ClCompile>
<ClCompile Include="yaml-cpp\src\scanscalar.cpp">
</ClCompile>
<ClCompile Include="yaml-cpp\src\scantag.cpp">
</ClCompile>
<ClCompile Include="yaml-cpp\src\scantoken.cpp">
</ClCompile>
<ClCompile Include="yaml-cpp\src\simplekey.cpp">
</ClCompile>
<ClCompile Include="yaml-cpp\src\singledocparser.cpp">
</ClCompile>
<ClCompile Include="yaml-cpp\src\stream.cpp">
</ClCompile>
<ClCompile Include="yaml-cpp\src\tag.cpp">
</ClCompile>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -0,0 +1,92 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\binary.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\convert.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\directives.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\emit.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\emitfromevents.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\emitter.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\emitterstate.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\emitterutils.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\exp.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\memory.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\node.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\node_data.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\nodebuilder.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\nodeevents.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\null.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\ostream_wrapper.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\parse.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\parser.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\regex_yaml.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\scanner.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\scanscalar.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\scantag.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\scantoken.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\simplekey.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\singledocparser.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\stream.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\tag.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup />
</Project>