Added utilities from rpcs3

This commit is contained in:
DHrpcs3
2015-12-21 10:22:27 +02:00
commit 1d74fb8f7a
29 changed files with 7664 additions and 0 deletions

165
common.vcxproj Normal file
View File

@@ -0,0 +1,165 @@
<?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|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClInclude Include="include\Atomic.h" />
<ClInclude Include="include\BEType.h" />
<ClInclude Include="include\BitField.h" />
<ClInclude Include="include\config_context.h" />
<ClInclude Include="include\convert.h" />
<ClInclude Include="include\event.h" />
<ClInclude Include="include\File.h" />
<ClInclude Include="include\GNU.h" />
<ClInclude Include="include\Interval.h" />
<ClInclude Include="include\Log.h" />
<ClInclude Include="include\MTRingbuffer.h" />
<ClInclude Include="include\Semaphore.h" />
<ClInclude Include="include\SharedMutex.h" />
<ClInclude Include="include\SleepQueue.h" />
<ClInclude Include="include\StrFmt.h" />
<ClInclude Include="include\Timer.h" />
<ClInclude Include="include\types.h" />
<ClInclude Include="include\VirtualMemory.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\config_context.cpp" />
<ClCompile Include="src\File.cpp" />
<ClCompile Include="src\GNU.cpp" />
<ClCompile Include="src\Log.cpp" />
<ClCompile Include="src\Semaphore.cpp" />
<ClCompile Include="src\SharedMutex.cpp" />
<ClCompile Include="src\SleepQueue.cpp" />
<ClCompile Include="src\StrFmt.cpp" />
<ClCompile Include="src\VirtualMemory.cpp" />
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{B03AE382-DB4D-459E-A52B-C94F55381F09}</ProjectGuid>
<RootNamespace>common</RootNamespace>
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<OutDir>$(SolutionDir)lib\$(Configuration)-$(Platform)\</OutDir>
<IntDir>$(SolutionDir)tmp\$(ProjectName)-$(Configuration)-$(Platform)\</IntDir>
<IncludePath>$(ProjectDir)include\;$(VC_IncludePath);$(WindowsSDK_IncludePath)</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<OutDir>$(SolutionDir)lib\$(Configuration)-$(Platform)\</OutDir>
<IntDir>$(SolutionDir)tmp\$(ProjectName)-$(Configuration)-$(Platform)\</IntDir>
<IncludePath>$(ProjectDir)include\;$(VC_IncludePath);$(WindowsSDK_IncludePath)</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<OutDir>$(SolutionDir)lib\$(Configuration)-$(Platform)\</OutDir>
<IntDir>$(SolutionDir)tmp\$(ProjectName)-$(Configuration)-$(Platform)\</IntDir>
<IncludePath>$(ProjectDir)include\;$(VC_IncludePath);$(WindowsSDK_IncludePath)</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<OutDir>$(SolutionDir)lib\$(Configuration)-$(Platform)\</OutDir>
<IntDir>$(SolutionDir)tmp\$(ProjectName)-$(Configuration)-$(Platform)\</IntDir>
<IncludePath>$(ProjectDir)include\;$(VC_IncludePath);$(WindowsSDK_IncludePath)</IncludePath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

98
common.vcxproj.filters Normal file
View File

@@ -0,0 +1,98 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="src">
<UniqueIdentifier>{fbf072d9-045e-4ff5-ba9b-fe51ae712dfd}</UniqueIdentifier>
<Extensions>cpp</Extensions>
</Filter>
<Filter Include="include">
<UniqueIdentifier>{8a3f953c-4c72-4bdd-a327-e3f173447485}</UniqueIdentifier>
<Extensions>h</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="include\BEType.h">
<Filter>include</Filter>
</ClInclude>
<ClInclude Include="include\BitField.h">
<Filter>include</Filter>
</ClInclude>
<ClInclude Include="include\config_context.h">
<Filter>include</Filter>
</ClInclude>
<ClInclude Include="include\convert.h">
<Filter>include</Filter>
</ClInclude>
<ClInclude Include="include\event.h">
<Filter>include</Filter>
</ClInclude>
<ClInclude Include="include\File.h">
<Filter>include</Filter>
</ClInclude>
<ClInclude Include="include\GNU.h">
<Filter>include</Filter>
</ClInclude>
<ClInclude Include="include\Interval.h">
<Filter>include</Filter>
</ClInclude>
<ClInclude Include="include\Log.h">
<Filter>include</Filter>
</ClInclude>
<ClInclude Include="include\MTRingbuffer.h">
<Filter>include</Filter>
</ClInclude>
<ClInclude Include="include\Semaphore.h">
<Filter>include</Filter>
</ClInclude>
<ClInclude Include="include\SharedMutex.h">
<Filter>include</Filter>
</ClInclude>
<ClInclude Include="include\SleepQueue.h">
<Filter>include</Filter>
</ClInclude>
<ClInclude Include="include\StrFmt.h">
<Filter>include</Filter>
</ClInclude>
<ClInclude Include="include\Timer.h">
<Filter>include</Filter>
</ClInclude>
<ClInclude Include="include\types.h">
<Filter>include</Filter>
</ClInclude>
<ClInclude Include="include\VirtualMemory.h">
<Filter>include</Filter>
</ClInclude>
<ClInclude Include="include\Atomic.h">
<Filter>include</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\config_context.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="src\File.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="src\GNU.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="src\Log.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="src\Semaphore.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="src\SharedMutex.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="src\SleepQueue.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="src\StrFmt.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="src\VirtualMemory.cpp">
<Filter>src</Filter>
</ClCompile>
</ItemGroup>
</Project>

701
include/Atomic.h Normal file
View File

@@ -0,0 +1,701 @@
#pragma once
#include "types.h"
#include "GNU.h"
#include "BEType.h"
#include <atomic>
#if defined(__GNUG__)
template<typename T, typename T2> inline std::enable_if_t<IS_INTEGRAL(T), T> sync_val_compare_and_swap(volatile T* dest, T2 comp, T2 exch)
{
return __sync_val_compare_and_swap(dest, comp, exch);
}
template<typename T, typename T2> inline std::enable_if_t<IS_INTEGRAL(T), bool> sync_bool_compare_and_swap(volatile T* dest, T2 comp, T2 exch)
{
return __sync_bool_compare_and_swap(dest, comp, exch);
}
template<typename T, typename T2> inline std::enable_if_t<IS_INTEGRAL(T), T> sync_lock_test_and_set(volatile T* dest, T2 value)
{
return __sync_lock_test_and_set(dest, value);
}
template<typename T, typename T2> inline std::enable_if_t<IS_INTEGRAL(T), T> sync_fetch_and_add(volatile T* dest, T2 value)
{
return __sync_fetch_and_add(dest, value);
}
template<typename T, typename T2> inline std::enable_if_t<IS_INTEGRAL(T), T> sync_fetch_and_sub(volatile T* dest, T2 value)
{
return __sync_fetch_and_sub(dest, value);
}
template<typename T, typename T2> inline std::enable_if_t<IS_INTEGRAL(T), T> sync_fetch_and_or(volatile T* dest, T2 value)
{
return __sync_fetch_and_or(dest, value);
}
template<typename T, typename T2> inline std::enable_if_t<IS_INTEGRAL(T), T> sync_fetch_and_and(volatile T* dest, T2 value)
{
return __sync_fetch_and_and(dest, value);
}
template<typename T, typename T2> inline std::enable_if_t<IS_INTEGRAL(T), T> sync_fetch_and_xor(volatile T* dest, T2 value)
{
return __sync_fetch_and_xor(dest, value);
}
#elif defined(_MSC_VER)
// atomic compare and swap functions
inline u8 sync_val_compare_and_swap(volatile u8* dest, u8 comp, u8 exch)
{
return _InterlockedCompareExchange8((volatile char*)dest, exch, comp);
}
inline u16 sync_val_compare_and_swap(volatile u16* dest, u16 comp, u16 exch)
{
return _InterlockedCompareExchange16((volatile short*)dest, exch, comp);
}
inline u32 sync_val_compare_and_swap(volatile u32* dest, u32 comp, u32 exch)
{
return _InterlockedCompareExchange((volatile long*)dest, exch, comp);
}
inline u64 sync_val_compare_and_swap(volatile u64* dest, u64 comp, u64 exch)
{
return _InterlockedCompareExchange64((volatile long long*)dest, exch, comp);
}
inline u128 sync_val_compare_and_swap(volatile u128* dest, u128 comp, u128 exch)
{
_InterlockedCompareExchange128((volatile long long*)dest, exch.hi, exch.lo, (long long*)&comp);
return comp;
}
inline bool sync_bool_compare_and_swap(volatile u8* dest, u8 comp, u8 exch)
{
return (u8)_InterlockedCompareExchange8((volatile char*)dest, exch, comp) == comp;
}
inline bool sync_bool_compare_and_swap(volatile u16* dest, u16 comp, u16 exch)
{
return (u16)_InterlockedCompareExchange16((volatile short*)dest, exch, comp) == comp;
}
inline bool sync_bool_compare_and_swap(volatile u32* dest, u32 comp, u32 exch)
{
return (u32)_InterlockedCompareExchange((volatile long*)dest, exch, comp) == comp;
}
inline bool sync_bool_compare_and_swap(volatile u64* dest, u64 comp, u64 exch)
{
return (u64)_InterlockedCompareExchange64((volatile long long*)dest, exch, comp) == comp;
}
inline bool sync_bool_compare_and_swap(volatile u128* dest, u128 comp, u128 exch)
{
return _InterlockedCompareExchange128((volatile long long*)dest, exch.hi, exch.lo, (long long*)&comp) != 0;
}
// atomic exchange functions
inline u8 sync_lock_test_and_set(volatile u8* dest, u8 value)
{
return _InterlockedExchange8((volatile char*)dest, value);
}
inline u16 sync_lock_test_and_set(volatile u16* dest, u16 value)
{
return _InterlockedExchange16((volatile short*)dest, value);
}
inline u32 sync_lock_test_and_set(volatile u32* dest, u32 value)
{
return _InterlockedExchange((volatile long*)dest, value);
}
inline u64 sync_lock_test_and_set(volatile u64* dest, u64 value)
{
return _InterlockedExchange64((volatile long long*)dest, value);
}
inline u128 sync_lock_test_and_set(volatile u128* dest, u128 value)
{
while (true)
{
u128 old;
old.lo = dest->lo;
old.hi = dest->hi;
if (sync_bool_compare_and_swap(dest, old, value)) return old;
}
}
// atomic add functions
inline u8 sync_fetch_and_add(volatile u8* dest, u8 value)
{
return _InterlockedExchangeAdd8((volatile char*)dest, value);
}
inline u16 sync_fetch_and_add(volatile u16* dest, u16 value)
{
return _InterlockedExchangeAdd16((volatile short*)dest, value);
}
inline u32 sync_fetch_and_add(volatile u32* dest, u32 value)
{
return _InterlockedExchangeAdd((volatile long*)dest, value);
}
inline u64 sync_fetch_and_add(volatile u64* dest, u64 value)
{
return _InterlockedExchangeAdd64((volatile long long*)dest, value);
}
inline u128 sync_fetch_and_add(volatile u128* dest, u128 value)
{
while (true)
{
u128 old;
old.lo = dest->lo;
old.hi = dest->hi;
if (sync_bool_compare_and_swap(dest, old, old + value)) return old;
}
}
// atomic sub functions
inline u8 sync_fetch_and_sub(volatile u8* dest, u8 value)
{
return _InterlockedExchangeAdd8((volatile char*)dest, -(char)value);
}
inline u16 sync_fetch_and_sub(volatile u16* dest, u16 value)
{
return _InterlockedExchangeAdd16((volatile short*)dest, -(short)value);
}
inline u32 sync_fetch_and_sub(volatile u32* dest, u32 value)
{
return _InterlockedExchangeAdd((volatile long*)dest, -(long)value);
}
inline u64 sync_fetch_and_sub(volatile u64* dest, u64 value)
{
return _InterlockedExchangeAdd64((volatile long long*)dest, -(long long)value);
}
inline u128 sync_fetch_and_sub(volatile u128* dest, u128 value)
{
while (true)
{
u128 old;
old.lo = dest->lo;
old.hi = dest->hi;
if (sync_bool_compare_and_swap(dest, old, old - value)) return old;
}
}
// atomic `bitwise or` functions
inline u8 sync_fetch_and_or(volatile u8* dest, u8 value)
{
return _InterlockedOr8((volatile char*)dest, value);
}
inline u16 sync_fetch_and_or(volatile u16* dest, u16 value)
{
return _InterlockedOr16((volatile short*)dest, value);
}
inline u32 sync_fetch_and_or(volatile u32* dest, u32 value)
{
return _InterlockedOr((volatile long*)dest, value);
}
inline u64 sync_fetch_and_or(volatile u64* dest, u64 value)
{
return _InterlockedOr64((volatile long long*)dest, value);
}
inline u128 sync_fetch_and_or(volatile u128* dest, u128 value)
{
while (true)
{
u128 old;
old.lo = dest->lo;
old.hi = dest->hi;
if (sync_bool_compare_and_swap(dest, old, old | value)) return old;
}
}
// atomic `bitwise and` functions
inline u8 sync_fetch_and_and(volatile u8* dest, u8 value)
{
return _InterlockedAnd8((volatile char*)dest, value);
}
inline u16 sync_fetch_and_and(volatile u16* dest, u16 value)
{
return _InterlockedAnd16((volatile short*)dest, value);
}
inline u32 sync_fetch_and_and(volatile u32* dest, u32 value)
{
return _InterlockedAnd((volatile long*)dest, value);
}
inline u64 sync_fetch_and_and(volatile u64* dest, u64 value)
{
return _InterlockedAnd64((volatile long long*)dest, value);
}
inline u128 sync_fetch_and_and(volatile u128* dest, u128 value)
{
while (true)
{
u128 old;
old.lo = dest->lo;
old.hi = dest->hi;
if (sync_bool_compare_and_swap(dest, old, old & value)) return old;
}
}
// atomic `bitwise xor` functions
inline u8 sync_fetch_and_xor(volatile u8* dest, u8 value)
{
return _InterlockedXor8((volatile char*)dest, value);
}
inline u16 sync_fetch_and_xor(volatile u16* dest, u16 value)
{
return _InterlockedXor16((volatile short*)dest, value);
}
inline u32 sync_fetch_and_xor(volatile u32* dest, u32 value)
{
return _InterlockedXor((volatile long*)dest, value);
}
inline u64 sync_fetch_and_xor(volatile u64* dest, u64 value)
{
return _InterlockedXor64((volatile long long*)dest, value);
}
inline u128 sync_fetch_and_xor(volatile u128* dest, u128 value)
{
while (true)
{
u128 old;
old.lo = dest->lo;
old.hi = dest->hi;
if (sync_bool_compare_and_swap(dest, old, old ^ value)) return old;
}
}
#endif /* _MSC_VER */
template<typename T, std::size_t Size = sizeof(T)> struct atomic_storage
{
static_assert(!Size, "Invalid atomic type");
};
template<typename T> struct atomic_storage<T, 1>
{
using type = u8;
};
template<typename T> struct atomic_storage<T, 2>
{
using type = u16;
};
template<typename T> struct atomic_storage<T, 4>
{
using type = u32;
};
template<typename T> struct atomic_storage<T, 8>
{
using type = u64;
};
template<typename T> struct atomic_storage<T, 16>
{
using type = u128;
};
template<typename T> using atomic_storage_t = typename atomic_storage<T>::type;
// atomic result wrapper; implements special behaviour for void result type
template<typename T, typename RT, typename VT> struct atomic_op_result_t
{
RT result;
template<typename... Args> atomic_op_result_t(T func, VT& var, Args&&... args)
: result(std::move(func(var, std::forward<Args>(args)...)))
{
}
RT move()
{
return std::move(result);
}
};
// void specialization: result is the initial value of the first arg
template<typename T, typename VT> struct atomic_op_result_t<T, void, VT>
{
VT result;
template<typename... Args> atomic_op_result_t(T func, VT& var, Args&&... args)
: result(var)
{
func(var, std::forward<Args>(args)...);
}
VT move()
{
return std::move(result);
}
};
// member function specialization
template<typename CT, typename... FArgs, typename RT, typename VT> struct atomic_op_result_t<RT(CT::*)(FArgs...), RT, VT>
{
RT result;
template<typename... Args> atomic_op_result_t(RT(CT::*func)(FArgs...), VT& var, Args&&... args)
: result(std::move((var.*func)(std::forward<Args>(args)...)))
{
}
RT move()
{
return std::move(result);
}
};
// member function void specialization
template<typename CT, typename... FArgs, typename VT> struct atomic_op_result_t<void(CT::*)(FArgs...), void, VT>
{
VT result;
template<typename... Args> atomic_op_result_t(void(CT::*func)(FArgs...), VT& var, Args&&... args)
: result(var)
{
(var.*func)(std::forward<Args>(args)...);
}
VT move()
{
return std::move(result);
}
};
// Atomic type with lock-free and standard layout guarantees (and appropriate limitations)
template<typename T> class atomic_t
{
using type = std::remove_cv_t<T>;
using stype = atomic_storage_t<type>;
using storage = atomic_storage<type>;
static_assert(alignof(type) <= alignof(stype), "atomic_t<> error: unexpected alignment");
stype m_data;
template<typename T2> static inline void write_relaxed(volatile T2& data, const T2& value)
{
data = value;
}
static inline void write_relaxed(volatile u128& data, const u128& value)
{
sync_lock_test_and_set(&data, value);
}
template<typename T2> static inline T2 read_relaxed(const volatile T2& data)
{
return data;
}
static inline u128 read_relaxed(const volatile u128& value)
{
return sync_val_compare_and_swap(const_cast<volatile u128*>(&value), u128{0}, u128{0});
}
public:
static inline const stype to_subtype(const type& value)
{
return reinterpret_cast<const stype&>(value);
}
static inline const type from_subtype(const stype value)
{
return reinterpret_cast<const type&>(value);
}
atomic_t() = default;
atomic_t(const atomic_t&) = delete;
atomic_t(type value)
: m_data(to_subtype(value))
{
}
atomic_t& operator =(const atomic_t&) = delete;
atomic_t& operator =(type value)
{
return write_relaxed(m_data, to_subtype(value)), *this;
}
operator type() const volatile
{
return from_subtype(read_relaxed(m_data));
}
// Unsafe direct access
stype* raw_data()
{
return reinterpret_cast<stype*>(&m_data);
}
// Unsafe direct access
type& raw()
{
return reinterpret_cast<type&>(m_data);
}
// Atomically compare data with cmp, replace with exch if equal, return previous data value anyway
type compare_and_swap(const type& cmp, const type& exch) volatile
{
return from_subtype(sync_val_compare_and_swap(&m_data, to_subtype(cmp), to_subtype(exch)));
}
// Atomically compare data with cmp, replace with exch if equal, return true if data was replaced
bool compare_and_swap_test(const type& cmp, const type& exch) volatile
{
return sync_bool_compare_and_swap(&m_data, to_subtype(cmp), to_subtype(exch));
}
// Atomically replace data with exch, return previous data value
type exchange(const type& exch) volatile
{
return from_subtype(sync_lock_test_and_set(&m_data, to_subtype(exch)));
}
// Atomically read data, possibly without memory barrier (not for 128 bit)
type load() const volatile
{
return from_subtype(read_relaxed(m_data));
}
// Atomically write data, possibly without memory barrier (not for 128 bit)
void store(const type& value) volatile
{
write_relaxed(m_data, to_subtype(value));
}
// Perform an atomic operation on data (func is either pointer to member function or callable object with a T& first arg);
// Returns the result of the callable object call or previous (old) value of the atomic variable if the return type is void
template<typename F, typename... Args, typename RT = std::result_of_t<F(T&, Args...)>> auto atomic_op(F func, Args&&... args) volatile -> decltype(atomic_op_result_t<F, RT, T>::result)
{
while (true)
{
// Read the old value from memory
const stype old = read_relaxed(m_data);
// Copy the old value
stype _new = old;
// Call atomic op for the local copy of the old value and save the return value of the function
atomic_op_result_t<F, RT, T> result(func, reinterpret_cast<type&>(_new), args...);
// Atomically compare value with `old`, replace with `_new` and return on success
if (sync_bool_compare_and_swap(&m_data, old, _new)) return result.move();
}
}
// Atomic bitwise OR, returns previous data
type _or(const type& right) volatile
{
return from_subtype(sync_fetch_and_or(&m_data, to_subtype(right)));
}
// Atomic bitwise AND, returns previous data
type _and(const type& right) volatile
{
return from_subtype(sync_fetch_and_and(&m_data, to_subtype(right)));
}
// Atomic bitwise AND NOT (inverts right argument), returns previous data
type _and_not(const type& right) volatile
{
return from_subtype(sync_fetch_and_and(&m_data, ~to_subtype(right)));
}
// Atomic bitwise XOR, returns previous data
type _xor(const type& right) volatile
{
return from_subtype(sync_fetch_and_xor(&m_data, to_subtype(right)));
}
type operator |=(const type& right) volatile
{
return from_subtype(sync_fetch_and_or(&m_data, to_subtype(right)) | to_subtype(right));
}
type operator &=(const type& right) volatile
{
return from_subtype(sync_fetch_and_and(&m_data, to_subtype(right)) & to_subtype(right));
}
type operator ^=(const type& right) volatile
{
return from_subtype(sync_fetch_and_xor(&m_data, to_subtype(right)) ^ to_subtype(right));
}
};
template<typename T> inline std::enable_if_t<IS_INTEGRAL(T), T> operator ++(atomic_t<T>& left)
{
return left.from_subtype(sync_fetch_and_add(left.raw_data(), 1) + 1);
}
template<typename T> inline std::enable_if_t<IS_INTEGRAL(T), T> operator --(atomic_t<T>& left)
{
return left.from_subtype(sync_fetch_and_sub(left.raw_data(), 1) - 1);
}
template<typename T> inline std::enable_if_t<IS_INTEGRAL(T), T> operator ++(atomic_t<T>& left, int)
{
return left.from_subtype(sync_fetch_and_add(left.raw_data(), 1));
}
template<typename T> inline std::enable_if_t<IS_INTEGRAL(T), T> operator --(atomic_t<T>& left, int)
{
return left.from_subtype(sync_fetch_and_sub(left.raw_data(), 1));
}
template<typename T, typename T2> inline std::enable_if_t<IS_INTEGRAL(T) && std::is_convertible<T2, T>::value, T> operator +=(atomic_t<T>& left, const T2& right)
{
return left.from_subtype(sync_fetch_and_add(left.raw_data(), right) + right);
}
template<typename T, typename T2> inline std::enable_if_t<IS_INTEGRAL(T) && std::is_convertible<T2, T>::value, T> operator -=(atomic_t<T>& left, const T2& right)
{
return left.from_subtype(sync_fetch_and_sub(left.raw_data(), right) - right);
}
template<typename T> inline std::enable_if_t<IS_INTEGRAL(T), nse_t<T>> operator ++(atomic_t<nse_t<T>>& left)
{
return left.from_subtype(sync_fetch_and_add(left.raw_data(), 1) + 1);
}
template<typename T> inline std::enable_if_t<IS_INTEGRAL(T), nse_t<T>> operator --(atomic_t<nse_t<T>>& left)
{
return left.from_subtype(sync_fetch_and_sub(left.raw_data(), 1) - 1);
}
template<typename T> inline std::enable_if_t<IS_INTEGRAL(T), nse_t<T>> operator ++(atomic_t<nse_t<T>>& left, int)
{
return left.from_subtype(sync_fetch_and_add(left.raw_data(), 1));
}
template<typename T> inline std::enable_if_t<IS_INTEGRAL(T), nse_t<T>> operator --(atomic_t<nse_t<T>>& left, int)
{
return left.from_subtype(sync_fetch_and_sub(left.raw_data(), 1));
}
template<typename T, typename T2> inline std::enable_if_t<IS_INTEGRAL(T) && std::is_convertible<T2, T>::value, nse_t<T>> operator +=(atomic_t<nse_t<T>>& left, const T2& right)
{
return left.from_subtype(sync_fetch_and_add(left.raw_data(), right) + right);
}
template<typename T, typename T2> inline std::enable_if_t<IS_INTEGRAL(T) && std::is_convertible<T2, T>::value, nse_t<T>> operator -=(atomic_t<nse_t<T>>& left, const T2& right)
{
return left.from_subtype(sync_fetch_and_sub(left.raw_data(), right) - right);
}
template<typename T> inline std::enable_if_t<IS_INTEGRAL(T), se_t<T>> operator ++(atomic_t<se_t<T>>& left)
{
return left.atomic_op([](se_t<T>& value) -> se_t<T>
{
return ++value;
});
}
template<typename T> inline std::enable_if_t<IS_INTEGRAL(T), se_t<T>> operator --(atomic_t<se_t<T>>& left)
{
return left.atomic_op([](se_t<T>& value) -> se_t<T>
{
return --value;
});
}
template<typename T> inline std::enable_if_t<IS_INTEGRAL(T), se_t<T>> operator ++(atomic_t<se_t<T>>& left, int)
{
return left.atomic_op([](se_t<T>& value) -> se_t<T>
{
return value++;
});
}
template<typename T> inline std::enable_if_t<IS_INTEGRAL(T), se_t<T>> operator --(atomic_t<se_t<T>>& left, int)
{
return left.atomic_op([](se_t<T>& value) -> se_t<T>
{
return value--;
});
}
template<typename T, typename T2> inline std::enable_if_t<IS_INTEGRAL(T) && std::is_convertible<T2, T>::value, se_t<T>> operator +=(atomic_t<se_t<T>>& left, const T2& right)
{
return left.atomic_op([&](se_t<T>& value) -> se_t<T>
{
return value += right;
});
}
template<typename T, typename T2> inline std::enable_if_t<IS_INTEGRAL(T) && std::is_convertible<T2, T>::value, se_t<T>> operator -=(atomic_t<se_t<T>>& left, const T2& right)
{
return left.atomic_op([&](se_t<T>& value) -> se_t<T>
{
return value -= right;
});
}
// Atomic BE Type (for PS3 virtual memory)
template<typename T> using atomic_be_t = atomic_t<be_t<T>>;
// Atomic LE Type (for PSV virtual memory)
template<typename T> using atomic_le_t = atomic_t<le_t<T>>;
// Algorithm for std::atomic; similar to atomic_t::atomic_op()
template<typename T, typename F, typename... Args, typename RT = std::result_of_t<F(T&, Args...)>> auto atomic_op(std::atomic<T>& var, F func, Args&&... args) -> decltype(atomic_op_result_t<F, RT, T>::result)
{
auto old = var.load();
while (true)
{
auto _new = old;
atomic_op_result_t<F, RT, T> result(func, _new, args...);
if (var.compare_exchange_strong(old, _new)) return result.move();
}
}

942
include/BEType.h Normal file
View File

@@ -0,0 +1,942 @@
#pragma once
#include "GNU.h"
#ifdef _MSC_VER
#include <intrin.h>
#else
#include <x86intrin.h>
#endif
#define IS_LE_MACHINE // only draft
union v128
{
template<typename T, std::size_t N, std::size_t M> class masked_array_t // array type accessed as (index ^ M)
{
T m_data[N];
public:
T& operator [](std::size_t index)
{
return m_data[index ^ M];
}
const T& operator [](std::size_t index) const
{
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
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>;
#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
normal_array_t<u64> _u64;
normal_array_t<s64> _s64;
reversed_array_t<u64> u64r;
reversed_array_t<s64> s64r;
normal_array_t<u32> _u32;
normal_array_t<s32> _s32;
reversed_array_t<u32> u32r;
reversed_array_t<s32> s32r;
normal_array_t<u16> _u16;
normal_array_t<s16> _s16;
reversed_array_t<u16> u16r;
reversed_array_t<s16> s16r;
normal_array_t<u8> _u8;
normal_array_t<s8> _s8;
reversed_array_t<u8> u8r;
reversed_array_t<s8> s8r;
normal_array_t<f32> _f;
normal_array_t<f64> _d;
reversed_array_t<f32> fr;
reversed_array_t<f64> dr;
__m128 vf;
__m128i vi;
__m128d vd;
class bit_array_128
{
u64 m_data[2];
public:
class bit_element
{
u64& data;
const u64 mask;
public:
bit_element(u64& data, const u64 mask)
: data(data)
, mask(mask)
{
}
operator bool() const
{
return (data & mask) != 0;
}
bit_element& operator =(const bool right)
{
if (right)
{
data |= mask;
}
else
{
data &= ~mask;
}
return *this;
}
bit_element& operator =(const bit_element& right)
{
if (right)
{
data |= mask;
}
else
{
data &= ~mask;
}
return *this;
}
};
// Index 0 returns the MSB and index 127 returns the LSB
bit_element operator [](u32 index)
{
#ifdef IS_LE_MACHINE
return bit_element(m_data[1 - (index >> 6)], 0x8000000000000000ull >> (index & 0x3F));
#else
return bit_element(m_data[index >> 6], 0x8000000000000000ull >> (index & 0x3F));
#endif
}
// Index 0 returns the MSB and index 127 returns the LSB
bool operator [](u32 index) const
{
#ifdef IS_LE_MACHINE
return (m_data[1 - (index >> 6)] & (0x8000000000000000ull >> (index & 0x3F))) != 0;
#else
return (m_data[index >> 6] & (0x8000000000000000ull >> (index & 0x3F))) != 0;
#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;
static v128 from64(u64 _0, u64 _1 = 0)
{
v128 ret;
ret._u64[0] = _0;
ret._u64[1] = _1;
return ret;
}
static v128 from64r(u64 _1, u64 _0 = 0)
{
return from64(_0, _1);
}
static v128 from32(u32 _0, u32 _1 = 0, u32 _2 = 0, u32 _3 = 0)
{
v128 ret;
ret._u32[0] = _0;
ret._u32[1] = _1;
ret._u32[2] = _2;
ret._u32[3] = _3;
return ret;
}
static v128 from32r(u32 _3, u32 _2 = 0, u32 _1 = 0, u32 _0 = 0)
{
return from32(_0, _1, _2, _3);
}
static v128 from32p(u32 value)
{
v128 ret;
ret.vi = _mm_set1_epi32(static_cast<s32>(value));
return ret;
}
static v128 from16p(u16 value)
{
v128 ret;
ret.vi = _mm_set1_epi16(static_cast<s16>(value));
return ret;
}
static v128 from8p(u8 value)
{
v128 ret;
ret.vi = _mm_set1_epi8(static_cast<s8>(value));
return ret;
}
static v128 fromBit(u32 bit)
{
v128 ret = {};
ret._bit[bit] = true;
return ret;
}
static v128 fromV(__m128i value)
{
v128 ret;
ret.vi = value;
return ret;
}
static v128 fromF(__m128 value)
{
v128 ret;
ret.vf = value;
return ret;
}
static v128 fromD(__m128d value)
{
v128 ret;
ret.vd = value;
return ret;
}
static inline v128 add8(const v128& left, const v128& right)
{
return fromV(_mm_add_epi8(left.vi, right.vi));
}
static inline v128 add16(const v128& left, const v128& right)
{
return fromV(_mm_add_epi16(left.vi, right.vi));
}
static inline v128 add32(const v128& left, const v128& right)
{
return fromV(_mm_add_epi32(left.vi, right.vi));
}
static inline v128 addfs(const v128& left, const v128& right)
{
return fromF(_mm_add_ps(left.vf, right.vf));
}
static inline v128 addfd(const v128& left, const v128& right)
{
return fromD(_mm_add_pd(left.vd, right.vd));
}
static inline v128 sub8(const v128& left, const v128& right)
{
return fromV(_mm_sub_epi8(left.vi, right.vi));
}
static inline v128 sub16(const v128& left, const v128& right)
{
return fromV(_mm_sub_epi16(left.vi, right.vi));
}
static inline v128 sub32(const v128& left, const v128& right)
{
return fromV(_mm_sub_epi32(left.vi, right.vi));
}
static inline v128 subfs(const v128& left, const v128& right)
{
return fromF(_mm_sub_ps(left.vf, right.vf));
}
static inline v128 subfd(const v128& left, const v128& right)
{
return fromD(_mm_sub_pd(left.vd, right.vd));
}
static inline v128 maxu8(const v128& left, const v128& right)
{
return fromV(_mm_max_epu8(left.vi, right.vi));
}
static inline v128 minu8(const v128& left, const v128& right)
{
return fromV(_mm_min_epu8(left.vi, right.vi));
}
static inline v128 eq8(const v128& left, const v128& right)
{
return fromV(_mm_cmpeq_epi8(left.vi, right.vi));
}
static inline v128 eq16(const v128& left, const v128& right)
{
return fromV(_mm_cmpeq_epi16(left.vi, right.vi));
}
static inline v128 eq32(const v128& left, const v128& right)
{
return fromV(_mm_cmpeq_epi32(left.vi, right.vi));
}
bool operator ==(const v128& right) const
{
return _u64[0] == right._u64[0] && _u64[1] == right._u64[1];
}
bool operator !=(const v128& right) const
{
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)
static inline v128 andnot(const v128& left, const v128& right)
{
return fromV(_mm_andnot_si128(left.vi, right.vi));
}
void clear()
{
_u64[0] = 0;
_u64[1] = 0;
}
std::string to_hex() 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)
{
return v128::fromV(_mm_or_si128(left.vi, right.vi));
}
inline v128 operator &(const v128& left, const v128& right)
{
return v128::fromV(_mm_and_si128(left.vi, right.vi));
}
inline v128 operator ^(const v128& left, const v128& right)
{
return v128::fromV(_mm_xor_si128(left.vi, right.vi));
}
inline v128 operator ~(const v128& other)
{
return v128::from64(~other._u64[0], ~other._u64[1]);
}
template<typename T, std::size_t Size = sizeof(T)> struct se_storage
{
static_assert(!Size, "Bad se_storage<> type");
};
template<typename T> struct se_storage<T, 2>
{
using type = u16;
[[deprecated]] static constexpr u16 _swap(u16 src) // for reference
{
return (src >> 8) | (src << 8);
}
static inline u16 swap(u16 src)
{
#if defined(__GNUG__)
return __builtin_bswap16(src);
#else
return _byteswap_ushort(src);
#endif
}
static inline u16 to(const T& src)
{
return swap(reinterpret_cast<const u16&>(src));
}
static inline T from(u16 src)
{
const u16 result = swap(src);
return reinterpret_cast<const T&>(result);
}
};
template<typename T> struct se_storage<T, 4>
{
using type = u32;
[[deprecated]] static constexpr u32 _swap(u32 src) // for reference
{
return (src >> 24) | (src << 24) | ((src >> 8) & 0x0000ff00) | ((src << 8) & 0x00ff0000);
}
static inline u32 swap(u32 src)
{
#if defined(__GNUG__)
return __builtin_bswap32(src);
#else
return _byteswap_ulong(src);
#endif
}
static inline u32 to(const T& src)
{
return swap(reinterpret_cast<const u32&>(src));
}
static inline T from(u32 src)
{
const u32 result = swap(src);
return reinterpret_cast<const T&>(result);
}
};
template<typename T> struct se_storage<T, 8>
{
using type = u64;
[[deprecated]] static constexpr u64 _swap(u64 src) // for reference
{
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__)
return __builtin_bswap64(src);
#else
return _byteswap_uint64(src);
#endif
}
static inline u64 to(const T& src)
{
return swap(reinterpret_cast<const u64&>(src));
}
static inline T from(u64 src)
{
const u64 result = swap(src);
return reinterpret_cast<const T&>(result);
}
};
template<typename T> struct se_storage<T, 16>
{
using type = v128;
static inline v128 to(const T& src)
{
return v128::byteswap(reinterpret_cast<const v128&>(src));
}
static inline T from(const v128& src)
{
const v128 result = v128::byteswap(src);
return reinterpret_cast<const T&>(result);
}
};
template<typename T> using se_storage_t = typename se_storage<T>::type;
template<typename T1, typename T2> struct se_convert
{
using type_from = std::remove_cv_t<T1>;
using type_to = std::remove_cv_t<T2>;
using stype_from = se_storage_t<std::remove_cv_t<T1>>;
using stype_to = se_storage_t<std::remove_cv_t<T2>>;
using storage_from = se_storage<std::remove_cv_t<T1>>;
using storage_to = se_storage<std::remove_cv_t<T2>>;
static inline std::enable_if_t<std::is_same<type_from, type_to>::value, stype_to> convert(const stype_from& data)
{
return data;
}
static inline stype_to convert(const stype_from& data, ...)
{
return storage_to::to(storage_from::from(data));
}
};
static struct se_raw_tag_t {} constexpr se_raw{};
template<typename T, bool Se = true> class se_t;
// se_t with switched endianness
template<typename T> class se_t<T, true>
{
using type = typename std::remove_cv<T>::type;
using stype = se_storage_t<type>;
using storage = se_storage<type>;
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_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_enum<type>::value, "se_t<> error: invalid type (enumeration), use integral type instead");
static_assert(alignof(type) == alignof(stype), "se_t<> error: unexpected alignment");
template<typename T2, typename = void> struct bool_converter
{
static inline bool to_bool(const se_t<T2>& value)
{
return static_cast<bool>(value.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)
{
return value.m_data != 0;
}
};
public:
se_t() = default;
se_t(const se_t& right) = default;
se_t(type value)
: m_data(storage::to(value))
{
}
// construct directly from raw data (don't use)
constexpr se_t(const stype& raw_value, const se_raw_tag_t&)
: m_data(raw_value)
{
}
type value() const
{
return storage::from(m_data);
}
// access underlying raw data (don't use)
constexpr const stype& raw_data() const noexcept
{
return m_data;
}
se_t& operator =(const se_t&) = default;
se_t& operator =(type value)
{
return m_data = storage::to(value), *this;
}
operator type() const
{
return storage::from(m_data);
}
// optimization
explicit operator bool() const
{
return bool_converter<type>::to_bool(*this);
}
// optimization
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;
}
// optimization
template<typename CT> std::enable_if_t<IS_INTEGRAL(T) && std::is_convertible<CT, T>::value, se_t&> operator &=(CT right)
{
return m_data &= storage::to(right), *this;
}
// optimization
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;
}
// optimization
template<typename CT> std::enable_if_t<IS_INTEGRAL(T) && std::is_convertible<CT, T>::value, se_t&> operator |=(CT right)
{
return m_data |= storage::to(right), *this;
}
// optimization
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;
}
// optimization
template<typename CT> std::enable_if_t<IS_INTEGRAL(T) && std::is_convertible<CT, T>::value, se_t&> operator ^=(CT right)
{
return m_data ^= storage::to(right), *this;
}
};
// se_t with native endianness
template<typename T> class se_t<T, false>
{
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_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_enum<type>::value, "se_t<> error: invalid type (enumeration), use integral type instead");
public:
se_t() = default;
se_t(const se_t&) = default;
constexpr se_t(type value)
: m_data(value)
{
}
type value() const
{
return m_data;
}
se_t& operator =(const se_t& value) = default;
se_t& operator =(type value)
{
return m_data = value, *this;
}
operator type() const
{
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)
{
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)
{
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)
{
return m_data ^= right, *this;
}
};
// se_t with native endianness (alias)
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)
{
auto value = left.value();
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)
{
auto value = left.value();
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)
{
auto value = left.value();
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)
{
auto value = left.value();
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)
{
auto value = left.value();
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)
{
auto value = left.value();
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)
{
auto value = left.value();
return left = (value >>= right);
}
template<typename T, bool Se> inline se_t<T, Se> operator ++(se_t<T, Se>& left, int)
{
auto value = left.value();
auto result = value++;
left = value;
return result;
}
template<typename T, bool Se> inline se_t<T, Se> operator --(se_t<T, Se>& left, int)
{
auto value = left.value();
auto result = value--;
left = value;
return result;
}
template<typename T, bool Se> inline se_t<T, Se>& operator ++(se_t<T, Se>& right)
{
auto value = right.value();
return right = ++value;
}
template<typename T, bool Se> inline se_t<T, Se>& operator --(se_t<T, Se>& right)
{
auto value = right.value();
return right = --value;
}
// 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)
{
return left.raw_data() == right.raw_data();
}
// 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)
{
return left.raw_data() == se_storage<T1>::to(right);
}
// 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)
{
return se_storage<T2>::to(left) == right.raw_data();
}
// 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)
{
return left.raw_data() != right.raw_data();
}
// 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)
{
return left.raw_data() != se_storage<T1>::to(right);
}
// 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)
{
return se_storage<T2>::to(left) != right.raw_data();
}
// 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)
{
return{ left.raw_data() & right.raw_data(), se_raw };
}
// 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)
{
return{ left.raw_data() & se_storage<T1>::to(right), se_raw };
}
// 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)
{
return{ se_storage<T2>::to(left) & right.raw_data(), se_raw };
}
// 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)
{
return{ left.raw_data() | right.raw_data(), se_raw };
}
// 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)
{
return{ left.raw_data() | se_storage<T1>::to(right), se_raw };
}
// 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)
{
return{ se_storage<T2>::to(left) | right.raw_data(), se_raw };
}
// 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)
{
return{ left.raw_data() ^ right.raw_data(), se_raw };
}
// 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)
{
return{ left.raw_data() ^ se_storage<T1>::to(right), se_raw };
}
// 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)
{
return{ se_storage<T2>::to(left) ^ right.raw_data(), se_raw };
}
// 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)
{
return{ ~right.raw_data(), se_raw };
}
#ifdef IS_LE_MACHINE
template<typename T> using be_t = se_t<T, true>;
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
template<typename T, bool Se, typename = void> struct to_se
{
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<bool, Se> { using type = bool; };
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<s8, Se> { using type = s8; };
#ifdef IS_LE_MACHINE
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;
#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
template<typename T, typename = void> struct to_ne
{
using type = T;
};
template<typename T, bool Se> struct to_ne<se_t<T, Se>>
{
using type = typename std::remove_cv<T>::type;
};
template<typename T> struct to_ne<const T, std::enable_if_t<!std::is_array<T>::value>> // move const qualifier
{
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;

140
include/BitField.h Normal file
View File

@@ -0,0 +1,140 @@
#pragma once
// BitField access helper class (N bits from I position), intended to be put in union
template<typename T, u32 I, u32 N> class bf_t
{
// Checks
static_assert(I < sizeof(T) * 8, "bf_t<> error: I out of bounds");
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
using type = typename std::remove_cv<T>::type;
// Underlying value type (native endianness)
using vtype = typename to_ne<type>::type;
// Mask of size N
constexpr static vtype s_mask = (static_cast<vtype>(1) << N) - 1;
// Underlying data member
type m_data;
// Conversion operator helper (uses SFINAE)
template<typename T2, typename = void> struct converter {};
template<typename T2> struct converter<T2, std::enable_if_t<std::is_unsigned<T2>::value>>
{
// Load unsigned value
static inline T2 convert(const type& data)
{
return (data >> I) & s_mask;
}
};
template<typename T2> struct converter<T2, std::enable_if_t<std::is_signed<T2>::value>>
{
// Load signed value (sign-extended)
static inline T2 convert(const type& data)
{
return data << (sizeof(T) * 8 - I - N) >> (sizeof(T) * 8 - N);
}
};
public:
// Assignment operator (store bitfield value)
bf_t& operator =(vtype value)
{
m_data = (m_data & ~(s_mask << I)) | (value & s_mask) << I;
return *this;
}
// Conversion operator (load bitfield value)
operator vtype() const
{
return converter<vtype>::convert(m_data);
}
// Get raw data with mask applied
type unshifted() const
{
return (m_data & (s_mask << I));
}
// Optimized bool conversion
explicit operator bool() const
{
return unshifted() != 0;
}
// Postfix increment operator
vtype operator ++(int)
{
vtype result = *this;
*this = result + 1;
return result;
}
// Prefix increment operator
bf_t& operator ++()
{
return *this = *this + 1;
}
// Postfix decrement operator
vtype operator --(int)
{
vtype result = *this;
*this = result - 1;
return result;
}
// Prefix decrement operator
bf_t& operator --()
{
return *this = *this - 1;
}
// Addition assignment operator
bf_t& operator +=(vtype right)
{
return *this = *this + right;
}
// Subtraction assignment operator
bf_t& operator -=(vtype right)
{
return *this = *this - right;
}
// Multiplication assignment operator
bf_t& operator *=(vtype right)
{
return *this = *this * right;
}
// Bitwise AND assignment operator
bf_t& operator &=(vtype right)
{
m_data &= (right & s_mask) << I;
return *this;
}
// Bitwise OR assignment operator
bf_t& operator |=(vtype right)
{
m_data |= (right & s_mask) << I;
return *this;
}
// Bitwise XOR assignment operator
bf_t& operator ^=(vtype right)
{
m_data ^= (right & s_mask) << I;
return *this;
}
};
template<typename T, u32 I, u32 N> using bf_be_t = bf_t<be_t<T>, I, N>;
template<typename T, u32 I, u32 N> using bf_le_t = bf_t<le_t<T>, I, N>;

303
include/File.h Normal file
View File

@@ -0,0 +1,303 @@
#pragma once
#include "types.h"
#include <vector>
#include <type_traits>
#include <StrFmt.h>
enum class fsm : u32 // file seek mode
{
begin,
cur,
end,
};
namespace fom // file open mode
{
enum : u32
{
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, // write + create + trunc
};
};
enum class fse : u32 // filesystem (file or dir) error
{
ok, // no error
invalid_arguments,
};
namespace fs
{
thread_local extern fse g_tls_error;
struct stat_t
{
bool is_directory;
bool is_writable;
u64 size;
s64 atime;
s64 mtime;
s64 ctime;
};
// Get file information
bool stat(const std::string& path, stat_t& info);
// Check whether a file or a directory exists (not recommended, use is_file() or is_dir() instead)
bool exists(const std::string& path);
// Check whether the file exists and is NOT a directory
bool is_file(const std::string& file);
// Check whether the directory exists and is NOT a file
bool is_dir(const std::string& dir);
// Delete empty directory
bool remove_dir(const std::string& dir);
// Create directory
bool create_dir(const std::string& dir);
// Create directories
bool create_path(const std::string& path);
// Rename (move) file or directory
bool rename(const std::string& from, const std::string& to);
// Copy file contents
bool copy_file(const std::string& from, const std::string& to, bool overwrite);
// Delete file
bool remove_file(const std::string& file);
// Change file size (possibly appending zeros)
bool truncate_file(const std::string& file, u64 length);
class file final
{
using handle_type = std::intptr_t;
constexpr static handle_type null = -1;
handle_type m_fd = null;
friend class file_ptr;
public:
file() = default;
explicit file(const std::string& filename, u32 mode = fom::read)
{
open(filename, mode);
}
file(file&& other)
: m_fd(other.m_fd)
{
other.m_fd = null;
}
file& operator =(file&& right)
{
std::swap(m_fd, right.m_fd);
return *this;
}
~file();
// Check whether the handle is valid (opened file)
bool is_opened() const
{
return m_fd != null;
}
// Check whether the handle is valid (opened file)
explicit operator bool() const
{
return is_opened();
}
// Open specified file with specified mode
bool open(const std::string& filename, u32 mode = fom::read);
// Change file size (possibly appending zero bytes)
bool trunc(u64 size) const;
// Get file information
bool stat(stat_t& info) const;
// Close the file explicitly (destructor automatically closes the file)
bool close();
// Read the data from the file and return the amount of data written in buffer
u64 read(void* buffer, u64 count) const;
// Write the data to the file and return the amount of data actually written
u64 write(const void* buffer, u64 count) const;
// Move file pointer
u64 seek(s64 offset, fsm seek_mode = fsm::begin) const;
// Get file size
u64 size() const;
// Write std::string
const file& operator <<(const std::string& str) const
{
CHECK_ASSERTION(write(str.data(), str.size()) == str.size());
return *this;
}
// Write POD
template<typename T>
std::enable_if_t<std::is_pod<T>::value && !std::is_pointer<T>::value, const file&> operator <<(const T& data) const
{
CHECK_ASSERTION(write(std::addressof(data), sizeof(T)) == sizeof(T));
return *this;
}
// Write POD std::vector
template<typename T>
std::enable_if_t<std::is_pod<T>::value && !std::is_pointer<T>::value, const file&> operator <<(const std::vector<T>& vec) const
{
CHECK_ASSERTION(write(vec.data(), vec.size() * sizeof(T)) == vec.size() * sizeof(T));
return *this;
}
// Read std::string
bool read(std::string& str) const
{
return read(&str[0], str.size()) == str.size();
}
// Read POD
template<typename T>
std::enable_if_t<std::is_pod<T>::value && !std::is_pointer<T>::value, bool> read(T& data) const
{
return read(&data, sizeof(T)) == sizeof(T);
}
// Read POD std::vector
template<typename T>
std::enable_if_t<std::is_pod<T>::value && !std::is_pointer<T>::value, bool> read(std::vector<T>& vec) const
{
return read(vec.data(), sizeof(T) * vec.size()) == sizeof(T) * vec.size();
}
// Convert to std::string
operator std::string() const
{
std::string result;
result.resize(size() - seek(0, fsm::cur));
CHECK_ASSERTION(read(result));
return result;
}
};
class file_ptr final
{
char* m_ptr = nullptr;
u64 m_size;
public:
file_ptr() = default;
file_ptr(file_ptr&& right)
: m_ptr(right.m_ptr)
, m_size(right.m_size)
{
right.m_ptr = 0;
}
file_ptr& operator =(file_ptr&& right)
{
std::swap(m_ptr, right.m_ptr);
std::swap(m_size, right.m_size);
return *this;
}
file_ptr(const file& f)
{
reset(f);
}
~file_ptr()
{
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
{
std::unique_ptr<char[]> m_path;
std::intptr_t m_dd; // handle (aux)
public:
dir() = default;
explicit dir(const std::string& dirname)
{
open(dirname);
}
dir(dir&& other)
: m_dd(other.m_dd)
, 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)
explicit operator bool() const
{
return is_opened();
}
// Open specified directory
bool open(const std::string& dirname);
// Close the directory explicitly (destructor automatically closes the directory)
bool close();
// Get next directory entry (UTF-8 name and file stat)
bool read(std::string& name, stat_t& info);
};
// Get configuration directory
std::string get_config_dir();
// Get executable directory
std::string get_executable_dir();
}

425
include/GNU.h Normal file
View File

@@ -0,0 +1,425 @@
#pragma once
#include <emmintrin.h>
#include <string>
#if defined(_MSC_VER) && _MSC_VER <= 1800
#define thread_local __declspec(thread)
#elif __APPLE__
#define thread_local __thread
#endif
#if defined(_MSC_VER)
#define never_inline __declspec(noinline)
#else
#define never_inline __attribute__((noinline))
#endif
#if defined(_MSC_VER)
#define safe_buffers __declspec(safebuffers)
#else
#define safe_buffers
#endif
#if defined(_MSC_VER)
#define force_inline __forceinline
#else
#define force_inline __attribute__((always_inline))
#endif
#if defined(_MSC_VER) && _MSC_VER <= 1800
#define alignas(x) _CRT_ALIGN(x)
#endif
#if defined(__GNUG__)
#include <stdlib.h>
#include <cstdint>
#ifndef __APPLE__
#include <malloc.h>
#endif
#define _fpclass(x) std::fpclassify(x)
#define INFINITE 0xFFFFFFFF
#ifdef __APPLE__
// XXX only supports a single timer
#define TIMER_ABSTIME -1
/* The opengroup spec isn't clear on the mapping from REALTIME to CALENDAR
being appropriate or not.
http://pubs.opengroup.org/onlinepubs/009695299/basedefs/time.h.html */
#define CLOCK_REALTIME 1 // #define CALENDAR_CLOCK 1 from mach/clock_types.h
#define CLOCK_MONOTONIC 0 // #define SYSTEM_CLOCK 0
typedef int clockid_t;
/* the mach kernel uses struct mach_timespec, so struct timespec
is loaded from <sys/_types/_timespec.h> for compatability */
// struct timespec { time_t tv_sec; long tv_nsec; };
int clock_gettime(clockid_t clk_id, struct timespec *tp);
#endif /* __APPLE__ */
#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)
{
#if defined(_MSC_VER)
unsigned long res;
return _BitScanReverse(&res, arg) ? res ^ 31 : 32;
#else
return arg ? __builtin_clzll(arg) - 32 : 32;
#endif
}
inline std::uint64_t cntlz64(std::uint64_t arg)
{
#if defined(_MSC_VER)
unsigned long res;
return _BitScanReverse64(&res, arg) ? res ^ 63 : 64;
#else
return arg ? __builtin_clzll(arg) : 64;
#endif
}
// compare 16 packed unsigned bytes (greater than)
inline __m128i sse_cmpgt_epu8(__m128i A, __m128i B)
{
// (A xor 0x80) > (B xor 0x80)
const auto sign = _mm_set1_epi32(0x80808080);
return _mm_cmpgt_epi8(_mm_xor_si128(A, sign), _mm_xor_si128(B, sign));
}
inline __m128i sse_cmpgt_epu16(__m128i A, __m128i B)
{
const auto sign = _mm_set1_epi32(0x80008000);
return _mm_cmpgt_epi16(_mm_xor_si128(A, sign), _mm_xor_si128(B, sign));
}
inline __m128i sse_cmpgt_epu32(__m128i A, __m128i B)
{
const auto sign = _mm_set1_epi32(0x80000000);
return _mm_cmpgt_epi32(_mm_xor_si128(A, sign), _mm_xor_si128(B, sign));
}
inline __m128 sse_exp2_ps(__m128 A)
{
const auto x0 = _mm_max_ps(_mm_min_ps(A, _mm_set1_ps(127.4999961f)), _mm_set1_ps(-127.4999961f));
const auto x1 = _mm_add_ps(x0, _mm_set1_ps(0.5f));
const auto x2 = _mm_sub_epi32(_mm_cvtps_epi32(x1), _mm_and_si128(_mm_castps_si128(_mm_cmpnlt_ps(_mm_setzero_ps(), x1)), _mm_set1_epi32(1)));
const auto x3 = _mm_sub_ps(x0, _mm_cvtepi32_ps(x2));
const auto x4 = _mm_mul_ps(x3, x3);
const auto x5 = _mm_mul_ps(x3, _mm_add_ps(_mm_mul_ps(_mm_add_ps(_mm_mul_ps(x4, _mm_set1_ps(0.023093347705f)), _mm_set1_ps(20.20206567f)), x4), _mm_set1_ps(1513.906801f)));
const auto x6 = _mm_mul_ps(x5, _mm_rcp_ps(_mm_sub_ps(_mm_add_ps(_mm_mul_ps(_mm_set1_ps(233.1842117f), x4), _mm_set1_ps(4368.211667f)), x5)));
return _mm_mul_ps(_mm_add_ps(_mm_add_ps(x6, x6), _mm_set1_ps(1.0f)), _mm_castsi128_ps(_mm_slli_epi32(_mm_add_epi32(x2, _mm_set1_epi32(127)), 23)));
}
inline __m128 sse_log2_ps(__m128 A)
{
const auto _1 = _mm_set1_ps(1.0f);
const auto _c = _mm_set1_ps(1.442695040f);
const auto x0 = _mm_max_ps(A, _mm_castsi128_ps(_mm_set1_epi32(0x00800000)));
const auto x1 = _mm_or_ps(_mm_and_ps(x0, _mm_castsi128_ps(_mm_set1_epi32(0x807fffff))), _1);
const auto x2 = _mm_rcp_ps(_mm_add_ps(x1, _1));
const auto x3 = _mm_mul_ps(_mm_sub_ps(x1, _1), x2);
const auto x4 = _mm_add_ps(x3, x3);
const auto x5 = _mm_mul_ps(x4, x4);
const auto x6 = _mm_add_ps(_mm_mul_ps(_mm_add_ps(_mm_mul_ps(_mm_set1_ps(-0.7895802789f), x5), _mm_set1_ps(16.38666457f)), x5), _mm_set1_ps(-64.1409953f));
const auto x7 = _mm_rcp_ps(_mm_add_ps(_mm_mul_ps(_mm_add_ps(_mm_mul_ps(_mm_set1_ps(-35.67227983f), x5), _mm_set1_ps(312.0937664f)), x5), _mm_set1_ps(-769.6919436f)));
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));
}
#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)
#define CHECK_ASCENDING(constexpr_array) static_assert(::is_ascending(constexpr_array), #constexpr_array " is not sorted in ascending order")
#ifndef _MSC_VER
using u128 = __uint128_t;
#endif
CHECK_SIZE_ALIGN(u128, 16, 16);
#include "types.h"
// bool type replacement for PS3/PSV
class b8
{
std::uint8_t m_value;
public:
b8(const bool value)
: m_value(value)
{
}
operator bool() const //template<typename T, typename = std::enable_if_t<std::is_integral<T>::value>> operator T() const
{
return m_value != 0;
}
};
CHECK_SIZE_ALIGN(b8, 1, 1);
template<typename T, typename = std::enable_if_t<std::is_integral<T>::value>> inline T align(const T& value, u64 align)
{
return static_cast<T>((value + (align - 1)) & ~(align - 1));
}
// 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 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';
}
// returns true if all array elements are unique and sorted in ascending order
template<typename T, std::size_t N> constexpr bool is_ascending(const T(&array)[N], std::size_t from = 0)
{
return from >= N - 1 ? true : array[from] < array[from + 1] ? is_ascending(array, from + 1) : false;
}
// get (first) array element equal to `value` or nullptr if not found
template<typename T, std::size_t N, typename T2> constexpr const T* static_search(const T(&array)[N], const T2& value, std::size_t from = 0)
{
return from >= N ? nullptr : array[from] == value ? array + from : static_search(array, value, from + 1);
}
// 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;
}
};
template<typename T1, typename T2, typename T3 = const char*> struct triplet_t
{
T1 first;
T2 second;
T3 third;
constexpr bool operator ==(const T1& right) const
{
return first == right;
}
};
// return 32 bit sizeof() to avoid widening/narrowing conversions with size_t
#define sizeof32(type) static_cast<u32>(sizeof(type))
// return 32 bit alignof() to avoid widening/narrowing conversions with size_t
#define alignof32(type) static_cast<u32>(alignof(type))
// return 32 bit .size() for container
template<typename T> inline auto size32(const T& container) -> decltype(static_cast<u32>(container.size()))
{
const auto size = container.size();
return size >= 0 && size <= UINT32_MAX ? static_cast<u32>(size) : throw std::length_error(__FUNCTION__);
}
// return 32 bit size for an array
template<typename T, std::size_t Size> constexpr u32 size32(const T(&)[Size])
{
return Size >= 0 && Size <= UINT32_MAX ? static_cast<u32>(Size) : throw std::length_error(__FUNCTION__);
}
#define WRAP_EXPR(expr) [&]{ return expr; }
#define COPY_EXPR(expr) [=]{ return expr; }
#define PURE_EXPR(expr) [] { return expr; }
#define EXCEPTION(text, ...) fmt::exception(__FILE__, __LINE__, __FUNCTION__, text, ##__VA_ARGS__)
#define VM_CAST(value) vm::impl_cast(value, __FILE__, __LINE__, __FUNCTION__)
#define IS_INTEGRAL(t) (std::is_integral<t>::value || std::is_same<std::decay_t<t>, u128>::value)
#define IS_INTEGER(t) (std::is_integral<t>::value || std::is_enum<t>::value || std::is_same<std::decay_t<t>, u128>::value)
#define IS_BINARY_COMPARABLE(t1, t2) (IS_INTEGER(t1) && IS_INTEGER(t2) && sizeof(t1) == sizeof(t2))
#define CHECK_ASSERTION(expr) if (expr) {} else throw EXCEPTION("Assertion failed: " #expr)
#define CHECK_SUCCESS(expr) if (s32 _r = (expr)) throw EXCEPTION(#expr " failed (0x%x)", _r)
// Some forward declarations for the ID manager
template<typename T> struct id_traits;

33
include/Interval.h Normal file
View File

@@ -0,0 +1,33 @@
#pragma once
#include <type_traits>
template<typename T1, typename T2> struct range_t
{
T1 _min; // first value
T2 _max; // second value
};
template<typename T1, typename T2> constexpr range_t<std::decay_t<T1>, std::decay_t<T2>> make_range(T1&& _min, T2&& _max)
{
return{ std::forward<T1>(_min), std::forward<T2>(_max) };
}
template<typename T1, typename T2, typename T> constexpr bool operator <(const range_t<T1, T2>& range, const T& value)
{
return range._min < value && range._max < value;
}
template<typename T1, typename T2, typename T> constexpr bool operator <(const T& value, const range_t<T1, T2>& range)
{
return value < range._min && value < range._max;
}
template<typename T1, typename T2, typename T> constexpr bool operator ==(const range_t<T1, T2>& range, const T& value)
{
return !(value < range._min) && !(range._max < value);
}
template<typename T1, typename T2, typename T> constexpr bool operator ==(const T& value, const range_t<T1, T2>& range)
{
return !(value < range._min) && !(range._max < value);
}

140
include/Log.h Normal file
View File

@@ -0,0 +1,140 @@
#pragma once
#include "types.h"
#include "MTRingbuffer.h"
#include "GNU.h"
#include <memory>
#include <map>
#include <set>
//#define BUFFERED_LOGGING 1
//first parameter is of type Log::LogType and text is of type std::string
#define LOG_SUCCESS(logType, text, ...) log_message(logType, Log::Severity::Success, text, ##__VA_ARGS__)
#define LOG_NOTICE(logType, text, ...) log_message(logType, Log::Severity::Notice, text, ##__VA_ARGS__)
#define LOG_WARNING(logType, text, ...) log_message(logType, Log::Severity::Warning, text, ##__VA_ARGS__)
#define LOG_ERROR(logType, text, ...) log_message(logType, Log::Severity::Error, text, ##__VA_ARGS__)
namespace Log
{
const unsigned int MAX_LOG_BUFFER_LENGTH = 1024*1024;
const unsigned int gBuffSize = 1000;
enum LogType : u32
{
GENERAL = 0,
LOADER,
MEMORY,
RSX,
HLE,
PPU,
SPU,
ARMv7,
TTY,
};
struct LogTypeName
{
LogType mType;
std::string mName;
};
//well I'd love make_array() but alas manually counting is not the end of the world
static const std::array<LogTypeName, 9> gTypeNameTable = { {
{ GENERAL, "G: " },
{ LOADER, "LDR: " },
{ MEMORY, "MEM: " },
{ RSX, "RSX: " },
{ HLE, "HLE: " },
{ PPU, "PPU: " },
{ SPU, "SPU: " },
{ ARMv7, "ARM: " },
{ TTY, "TTY: " }
} };
enum class Severity : u32
{
Notice = 0,
Warning,
Success,
Error,
};
struct LogMessage
{
using size_type = u32;
LogType mType;
Severity mServerity;
std::string mText;
u32 size() const;
void serialize(char *output) const;
static LogMessage deserialize(char *input, u32* size_out=nullptr);
};
struct LogListener
{
virtual ~LogListener() {};
virtual void log(const LogMessage &msg) = 0;
};
struct LogChannel
{
LogChannel();
LogChannel(const std::string& name);
LogChannel(LogChannel& other) = delete;
void log(const LogMessage &msg);
void addListener(std::shared_ptr<LogListener> listener);
void removeListener(std::shared_ptr<LogListener> listener);
std::string name;
private:
bool mEnabled;
Severity mLogLevel;
std::mutex mListenerLock;
std::set<std::shared_ptr<LogListener>> mListeners;
};
struct LogManager
{
LogManager();
~LogManager();
static LogManager& getInstance();
LogChannel& getChannel(LogType type);
void log(LogMessage msg);
void addListener(std::shared_ptr<LogListener> listener);
void removeListener(std::shared_ptr<LogListener> listener);
#ifdef BUFFERED_LOGGING
void consumeLog();
#endif
private:
#ifdef BUFFERED_LOGGING
MTRingbuffer<char, MAX_LOG_BUFFER_LENGTH> mBuffer;
std::condition_variable mBufferReady;
std::mutex mStatusMut;
std::atomic<bool> mExiting;
std::thread mLogConsumer;
#endif
std::array<LogChannel, std::tuple_size<decltype(gTypeNameTable)>::value> mChannels;
//std::array<LogChannel,gTypeNameTable.size()> mChannels; //TODO: use this once Microsoft sorts their shit out
};
}
static struct { inline operator Log::LogType() { return Log::LogType::GENERAL; } } GENERAL;
static struct { inline operator Log::LogType() { return Log::LogType::LOADER; } } LOADER;
static struct { inline operator Log::LogType() { return Log::LogType::MEMORY; } } MEMORY;
static struct { inline operator Log::LogType() { return Log::LogType::RSX; } } RSX;
static struct { inline operator Log::LogType() { return Log::LogType::HLE; } } HLE;
static struct { inline operator Log::LogType() { return Log::LogType::PPU; } } PPU;
static struct { inline operator Log::LogType() { return Log::LogType::SPU; } } SPU;
static struct { inline operator Log::LogType() { return Log::LogType::ARMv7; } } ARMv7;
static struct { inline operator Log::LogType() { return Log::LogType::TTY; } } TTY;
void log_message(Log::LogType type, Log::Severity sev, const char* text);
void log_message(Log::LogType type, Log::Severity sev, std::string text);
template<typename... Args> never_inline void log_message(Log::LogType type, Log::Severity sev, const char* fmt, Args... args)
{
log_message(type, sev, fmt::format(fmt, fmt::do_unveil(args)...));
}

158
include/MTRingbuffer.h Normal file
View File

@@ -0,0 +1,158 @@
#pragma once
#include <mutex>
#include <array>
#include <cstdint>
//Simple non-resizable FIFO Ringbuffer that can be simultaneously be read from and written to
//if we ever get to use boost please replace this with boost::circular_buffer, there's no reason
//why we would have to keep this amateur attempt at such a fundamental data-structure around
template< typename T, unsigned int MAX_MTRINGBUFFER_BUFFER_SIZE>
class MTRingbuffer{
std::array<T, MAX_MTRINGBUFFER_BUFFER_SIZE> mBuffer;
//this is a recursive mutex because the get methods lock it but the only
//way to be sure that they do not block is to check the size and the only
//way to check the size and use get atomically is to lock this mutex,
//so it goes:
//lock get mutex-->check size-->call get-->lock get mutex-->unlock get mutex-->return from get-->unlock get mutex
std::recursive_mutex mMutGet;
std::mutex mMutPut;
size_t mGet;
size_t mPut;
size_t moveGet(size_t by = 1){ return (mGet + by) % MAX_MTRINGBUFFER_BUFFER_SIZE; }
size_t movePut(size_t by = 1){ return (mPut + by) % MAX_MTRINGBUFFER_BUFFER_SIZE; }
public:
MTRingbuffer() : mGet(0), mPut(0){}
//blocks until there's something to get, so check "spaceLeft()" if you want to avoid blocking
//also lock the get mutex around the spaceLeft() check and the pop if you want to avoid racing
T pop()
{
std::lock_guard<std::recursive_mutex> lock(mMutGet);
while (mGet == mPut)
{
//wait until there's actually something to get
//throwing an exception might be better, blocking here is a little awkward
std::this_thread::sleep_for(std::chrono::milliseconds(1)); // hack
}
size_t ret = mGet;
mGet = moveGet();
return mBuffer[ret];
}
//blocks if the buffer is full until there's enough room
void push(T &putEle)
{
std::lock_guard<std::mutex> lock(mMutPut);
while (movePut() == mGet)
{
//if this is reached a lot it's time to increase the buffer size
//or implement dynamic re-sizing
std::this_thread::sleep_for(std::chrono::milliseconds(1)); // hack
}
mBuffer[mPut] = std::forward(putEle);
mPut = movePut();
}
bool empty()
{
return mGet == mPut;
}
//returns the amount of free places, this is the amount of actual free spaces-1
//since mGet==mPut signals an empty buffer we can't actually use the last free
//space, so we shouldn't report it as free.
size_t spaceLeft() //apparently free() is a macro definition in msvc in some conditions
{
if (mGet < mPut)
{
return mBuffer.size() - (mPut - mGet) - 1;
}
else if (mGet > mPut)
{
return mGet - mPut - 1;
}
else
{
return mBuffer.size() - 1;
}
}
size_t size()
{
//the magic -1 is the same magic 1 that is explained in the spaceLeft() function
return mBuffer.size() - spaceLeft() - 1;
}
//takes random access iterator to T
template<typename IteratorType>
void pushRange(IteratorType from, IteratorType until)
{
std::lock_guard<std::mutex> lock(mMutPut);
size_t length = until - from;
//if whatever we're trying to store is greater than the entire buffer the following loop will be infinite
assert(mBuffer.size() > length);
while (spaceLeft() < length)
{
//if this is reached a lot it's time to increase the buffer size
//or implement dynamic re-sizing
std::this_thread::sleep_for(std::chrono::milliseconds(1)); // hack
}
if (mPut + length <= mBuffer.size())
{
std::copy(from, until, mBuffer.begin() + mPut);
}
else
{
size_t tillEnd = mBuffer.size() - mPut;
std::copy(from, from + tillEnd, mBuffer.begin() + mPut);
std::copy(from + tillEnd, until, mBuffer.begin());
}
mPut = movePut(length);
}
//takes output iterator to T
template<typename IteratorType>
void popN(IteratorType output, size_t n)
{
std::lock_guard<std::recursive_mutex> lock(mMutGet);
//make sure we're not trying to retrieve more than is in
assert(n <= size());
peekN<IteratorType>(output, n);
mGet = moveGet(n);
}
//takes output iterator to T
template<typename IteratorType>
void peekN(IteratorType output, size_t n)
{
size_t lGet = mGet;
if (lGet + n <= mBuffer.size())
{
std::copy_n(mBuffer.begin() + lGet, n, output);
}
else
{
auto next = std::copy(mBuffer.begin() + lGet, mBuffer.end(), output);
std::copy_n(mBuffer.begin(), n - (mBuffer.size() - lGet), next);
}
}
//well this is just asking for trouble
//but the comment above the declaration of mMutGet explains why it's there
//if there's a better way please remove this
void lockGet()
{
mMutGet.lock();
}
//well this is just asking for trouble
//but the comment above the declaration of mMutGet explains why it's there
//if there's a better way please remove this
void unlockGet()
{
mMutGet.unlock();
}
};

39
include/Semaphore.h Normal file
View File

@@ -0,0 +1,39 @@
#pragma once
#include "Atomic.h"
#include <mutex>
class semaphore_t
{
// semaphore mutex
std::mutex m_mutex;
// semaphore condition variable
std::condition_variable m_cv;
struct sync_var_t
{
u32 value; // current semaphore value
u32 waiters; // current amount of waiters
};
// current semaphore value
atomic_t<sync_var_t> m_var;
public:
// max semaphore value
const u32 max_value;
semaphore_t(u32 max_value = 1, u32 value = 0)
: m_var(sync_var_t{ value, 0 })
, max_value(max_value)
{
}
bool try_wait();
bool try_post();
void wait();
bool post_and_wait();
};

141
include/SharedMutex.h Normal file
View File

@@ -0,0 +1,141 @@
#pragma once
#include "types.h"
#include <thread>
#include <mutex>
#include <atomic>
#include <Atomic.h>
//! 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.
//! MSVC implementation of std::shared_timed_mutex seems suboptimal.
//! std::shared_mutex is not available until C++17.
class shared_mutex final
{
enum : u32
{
SM_WRITER_LOCK = 1u << 31, // Exclusive lock flag, must be MSB
SM_WRITER_QUEUE = 1u << 30, // Flag set if m_wq_size != 0
SM_READER_QUEUE = 1u << 29, // Flag set if m_rq_size != 0
SM_READER_COUNT = SM_READER_QUEUE - 1, // Valid reader count bit mask
SM_READER_MAX = 1u << 24, // Max reader count
};
std::atomic<u32> 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;
u32 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::condition_variable m_rcv; // Reader queue
std::condition_variable m_wcv; // Writer queue
std::condition_variable m_ocv; // For current exclusive owner
static bool op_lock_shared(u32& ctrl)
{
// Check writer flags and reader limit
return (ctrl & ~SM_READER_QUEUE) < SM_READER_MAX ? ctrl++, true : false;
}
static bool op_lock_excl(u32& ctrl)
{
// Test and set writer lock
return (ctrl & SM_WRITER_LOCK) == 0 ? ctrl |= SM_WRITER_LOCK, true : false;
}
void impl_lock_shared(u32 old_ctrl);
void impl_unlock_shared(u32 new_ctrl);
void impl_lock_excl(u32 ctrl);
void impl_unlock_excl(u32 ctrl);
public:
shared_mutex() = default;
// Lock in shared mode
void lock_shared()
{
const u32 old_ctrl = m_ctrl++;
// Check flags and reader limit
if (old_ctrl >= SM_READER_MAX)
{
impl_lock_shared(old_ctrl);
}
}
// Try to lock in shared mode
bool try_lock_shared()
{
return atomic_op(m_ctrl, [](u32& ctrl)
{
// Check flags and reader limit
return ctrl < SM_READER_MAX ? ctrl++, true : false;
});
}
// Unlock in shared mode
void unlock_shared()
{
const u32 new_ctrl = --m_ctrl;
// Check if notification required
if (new_ctrl >= SM_READER_MAX)
{
impl_unlock_shared(new_ctrl);
}
}
// 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
bool try_lock()
{
u32 value = 0;
return m_ctrl.compare_exchange_strong(value, SM_WRITER_LOCK);
}
// Unlock exclusively
void unlock()
{
const u32 value = m_ctrl.fetch_add(SM_WRITER_LOCK);
// Check if notification required
if (value != SM_WRITER_LOCK)
{
impl_unlock_excl(value);
}
}
};
//! Simplified shared (reader) lock implementation, similar to std::lock_guard.
//! std::shared_lock may be used instead if necessary.
class reader_lock final
{
shared_mutex& m_mutex;
public:
reader_lock(const reader_lock&) = delete;
reader_lock(shared_mutex& mutex)
: m_mutex(mutex)
{
m_mutex.lock_shared();
}
~reader_lock()
{
m_mutex.unlock_shared();
}
};

52
include/SleepQueue.h Normal file
View File

@@ -0,0 +1,52 @@
#pragma once
#include <deque>
#include <memory>
struct sleep_entry_t : std::enable_shared_from_this<sleep_entry_t>
{
virtual void sleep() = 0;
virtual void awake() = 0;
};
using sleep_queue_t = std::deque<std::shared_ptr<sleep_entry_t>>;
static struct defer_sleep_t {} const defer_sleep{};
// automatic object handling a thread entry in the sleep queue
class sleep_queue_entry_t final
{
sleep_entry_t& m_thread;
sleep_queue_t& m_queue;
void add_entry();
void remove_entry();
bool find() const;
public:
// add specified thread to the sleep queue
sleep_queue_entry_t(sleep_entry_t& entry, sleep_queue_t& queue);
// don't add specified thread to the sleep queue
sleep_queue_entry_t(sleep_entry_t& entry, sleep_queue_t& queue, const defer_sleep_t&);
// removes specified thread from the sleep queue if added
~sleep_queue_entry_t();
// add thread to the sleep queue
void enter()
{
add_entry();
}
// remove thread from the sleep queue
void leave()
{
remove_entry();
}
// check whether the thread exists in the sleep queue
explicit operator bool() const
{
return find();
}
};

354
include/StrFmt.h Normal file
View File

@@ -0,0 +1,354 @@
#pragma once
#include <string>
#include <functional>
#include "types.h"
#include "BEType.h"
#include <memory>
#include <array>
#include <vector>
#if defined(_MSC_VER) && _MSC_VER <= 1800
#define snprintf _snprintf
#endif
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_all(const std::string &src, const std::string& from, const std::string& to);
template<size_t list_size>
std::string replace_all(std::string src, const std::pair<std::string, std::string>(&list)[list_size])
{
for (size_t pos = 0; pos < src.length(); ++pos)
{
for (size_t i = 0; i < list_size; ++i)
{
const size_t comp_length = list[i].first.length();
if (src.length() - pos < comp_length)
continue;
if (src.substr(pos, comp_length) == list[i].first)
{
src = (pos ? src.substr(0, pos) + list[i].second : list[i].second) + src.substr(pos + comp_length);
pos += list[i].second.length() - 1;
break;
}
}
}
return src;
}
template<size_t list_size>
std::string replace_all(std::string src, const std::pair<std::string, std::function<std::string()>>(&list)[list_size])
{
for (size_t pos = 0; pos < src.length(); ++pos)
{
for (size_t i = 0; i < list_size; ++i)
{
const size_t comp_length = list[i].first.length();
if (src.length() - pos < comp_length)
continue;
if (src.substr(pos, comp_length) == list[i].first)
{
src = (pos ? src.substr(0, pos) + list[i].second() : list[i].second()) + src.substr(pos + comp_length);
pos += list[i].second().length() - 1;
break;
}
}
}
return src;
}
std::string to_hex(u64 value, u64 count = 1);
std::string to_udec(u64 value);
std::string to_sdec(s64 value);
template<typename T, bool is_enum = std::is_enum<T>::value> struct unveil
{
using result_type = T;
force_inline static result_type get_value(const T& arg)
{
return arg;
}
};
template<> struct unveil<char*, false>
{
using result_type = const char*;
force_inline static result_type get_value(const char* arg)
{
return arg;
}
};
template<std::size_t N> struct unveil<const char[N], false>
{
using result_type = const char*;
force_inline static result_type get_value(const char(&arg)[N])
{
return arg;
}
};
template<> struct unveil<std::string, false>
{
using result_type = const char*;
force_inline static result_type get_value(const std::string& arg)
{
return arg.c_str();
}
};
template<typename T> struct unveil<T, true>
{
using result_type = std::underlying_type_t<T>;
force_inline static result_type get_value(const T& arg)
{
return static_cast<result_type>(arg);
}
};
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>
force_inline typename unveil<T>::result_type do_unveil(const T& arg)
{
return unveil<T>::get_value(arg);
}
// Formatting function with special functionality:
//
// 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> safe_buffers std::string format(const char* fmt, Args... args)
{
// fixed stack buffer for the first attempt
std::array<char, 4096> fixed_buf;
// possibly dynamically allocated buffer for the second attempt
std::unique_ptr<char[]> buf;
// pointer to the current buffer
char* buf_addr = fixed_buf.data();
for (std::size_t buf_size = fixed_buf.size();;)
{
#ifndef _MSC_VER
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-security"
#endif
const std::size_t len = std::snprintf(buf_addr, buf_size, fmt, do_unveil(args)...);
#ifndef _MSC_VER
#pragma GCC diagnostic pop
#endif
if (len > INT_MAX)
{
throw std::runtime_error("std::snprintf() failed");
}
if (len < buf_size)
{
return{ buf_addr, len };
}
buf.reset(buf_addr = new char[buf_size = len + 1]);
}
}
struct exception : public std::exception
{
std::unique_ptr<char[]> message;
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
{
const std::size_t size = std::strlen(other.message.get());
message.reset(new char[size + 1]);
std::memcpy(message.get(), other.message.get(), size + 1);
}
virtual const char* what() const noexcept override
{
return message.get();
}
};
//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::string trim(const std::string& source, const std::string& values = " \t");
template<typename T>
std::string merge(const T& source, const std::string& separator)
{
if (!source.size())
{
return{};
}
std::string result;
auto it = source.begin();
auto end = source.end();
for (--end; it != end; ++it)
{
result += *it + separator;
}
return result + source.back();
}
template<typename T>
std::string merge(std::initializer_list<T> sources, const std::string& separator)
{
if (!sources.size())
{
return{};
}
std::string result;
bool first = true;
for (auto &v : sources)
{
if (first)
{
result = fmt::merge(v, separator);
first = false;
}
else
{
result += separator + fmt::merge(v, separator);
}
}
return result;
}
std::string tolower(std::string source);
std::string toupper(std::string source);
std::string escape(std::string source);
}

53
include/Timer.h Normal file
View File

@@ -0,0 +1,53 @@
#pragma once
#include <chrono>
#include <cstdint>
class Timer
{
private:
bool m_stopped;
std::chrono::high_resolution_clock::time_point m_start;
std::chrono::high_resolution_clock::time_point m_end;
public:
Timer() : m_stopped(false)
{
}
void Start()
{
m_stopped = false;
m_start = std::chrono::high_resolution_clock::now();
}
void Stop()
{
m_stopped = true;
m_end = std::chrono::high_resolution_clock::now();
}
double GetElapsedTimeInSec() const
{
return double(GetElapsedTimeInMicroSec()) / 1000000.0;
}
double GetElapsedTimeInMilliSec() const
{
return double(GetElapsedTimeInMicroSec()) / 1000.0;
}
std::uint64_t GetElapsedTimeInMicroSec() const
{
std::chrono::high_resolution_clock::time_point now = m_stopped ? m_end : std::chrono::high_resolution_clock::now();
return std::chrono::duration_cast<std::chrono::microseconds>(now - m_start).count();
}
std::uint64_t GetElapsedTimeInNanoSec() const
{
std::chrono::high_resolution_clock::time_point now = m_stopped ? m_end : std::chrono::high_resolution_clock::now();
return std::chrono::duration_cast<std::chrono::nanoseconds>(now - m_start).count();
}
};

23
include/VirtualMemory.h Normal file
View File

@@ -0,0 +1,23 @@
#pragma once
#include <cstddef>
namespace memory_helper
{
/**
* Reserve size bytes of virtual memory and returns it.
* The memory should be commited before usage.
*/
void* reserve_memory(size_t size);
/**
* Commit page_size bytes of virtual memory starting at pointer.
* That is, bake reserved memory with physical memory.
* pointer should belong to a range of reserved memory.
*/
void commit_page_memory(void* pointer, size_t page_size);
/**
* Free memory alloced via reserve_memory.
*/
void free_reserved_memory(void* pointer, size_t size);
}

163
include/config_context.h Normal file
View File

@@ -0,0 +1,163 @@
#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 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&);
};

280
include/convert.h Normal file
View File

@@ -0,0 +1,280 @@
#pragma once
#include <string>
#include "types.h"
#include "StrFmt.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 : false;
}
};
template<>
struct to_impl_t<std::string, char>
{
static std::string func(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);
}
}

409
include/event.h Normal file
View File

@@ -0,0 +1,409 @@
#pragma once
#include <functional>
#include <deque>
#include <list>
enum class event_result
{
skip,
handled
};
class events_queue
{
std::deque<std::function<void()>> m_queue;
public:
/*template<typename Type>
events_queue& operator += (event<Type> &evt)
{
evt.set_queue(this);
return *this;
}
*/
void invoke(std::function<void()> function)
{
m_queue.push_back(function);
}
void flush()
{
while (!m_queue.empty())
{
std::function<void()> function = m_queue.front();
function();
m_queue.pop_front();
}
}
};
template<typename ...AT>
class event
{
using func_t = std::function<event_result(AT...)>;
using entry_t = typename std::list<func_t>::iterator;
std::list<func_t> handlers;
events_queue& m_queue;
public:
event(events_queue* queue = nullptr) : m_queue(*queue)
{
}
void invoke(AT... args)
{
m_queue.invoke(std::bind([&](AT... eargs)
{
for (auto &&handler : handlers)
{
if (handler(eargs...) == event_result::handled)
break;
}
}, args...));
}
void operator()(AT... args)
{
invoke(args...);
}
entry_t bind(func_t func)
{
handlers.push_front(func);
return handlers.begin();
}
template<typename T>
entry_t bind(T *caller, event_result(T::*callback)(AT...))
{
return bind([=](AT... args) { return (caller->*callback)(args...); });
}
template<typename T>
entry_t bind(T *caller, void(T::*callback)(AT...))
{
return bind([=](AT... args) { (caller->*callback)(args...); return event_result::skip; });
}
void unbind(entry_t entry)
{
handlers.erase(entry);
}
entry_t operator +=(func_t func)
{
return bind(func);
}
void operator -=(entry_t what)
{
return unbind(what);
}
};
template<>
class event<void>
{
using func_t = std::function<event_result()>;
using entry_t = std::list<func_t>::iterator;
std::list<func_t> m_listeners;
events_queue* m_queue;
void invoke_listeners()
{
for (auto &&listener : m_listeners)
{
if (listener() == event_result::handled)
break;
}
}
public:
event(events_queue* queue = nullptr) : m_queue(queue)
{
}
void invoke()
{
if (m_queue)
m_queue->invoke([=]() { invoke_listeners(); });
else
invoke_listeners();
}
void operator()()
{
invoke();
}
entry_t bind(func_t func)
{
m_listeners.push_front(func);
return m_listeners.begin();
}
template<typename T>
entry_t bind(T *caller, event_result(T::*callback)())
{
return bind([=]() { return (caller->*callback)(); });
}
template<typename T>
entry_t bind(T *caller, void(T::*callback)())
{
return bind([=]() { (caller->*callback)(); return event_result::skip; });
}
void unbind(entry_t what)
{
m_listeners.erase(what);
}
entry_t operator +=(func_t func)
{
return bind(func);
}
void operator -=(entry_t what)
{
return unbind(what);
}
};
class event_binder_t
{
template<typename Type>
class binder_impl_t
{
event_binder_t *m_binder;
event<Type> *m_event;
public:
binder_impl_t(event_binder_t *binder, event<Type> *evt)
: m_binder(binder)
, m_event(evt)
{
}
};
public:
template<typename Type>
binder_impl_t<Type> operator()(event<Type>& evt) const
{
return{ this, &evt };
}
};
template<typename T>
class combined_data;
template<typename T>
class local_data
{
public:
using type = T;
protected:
type m_data;
void set(type value)
{
m_data = value;
}
type get() const
{
return m_data;
}
bool equals(T value) const
{
return get() == value;
}
bool invoke_event(type value)
{
return false;
}
friend combined_data<T>;
};
template<typename T>
class combined_data
{
public:
using type = T;
protected:
local_data<type> m_local_data;
std::function<void(type)> m_invoke_event_function;
std::function<type()> m_get_function;
bool invoke_event(type value)
{
if (m_invoke_event_function)
{
m_invoke_event_function(value);
return true;
}
return false;
}
void set(type value)
{
m_local_data.set(value);
}
type get() const
{
if (m_get_function)
{
return m_get_function();
}
return m_local_data.get();
}
type get_local() const
{
return m_local_data.get();
}
bool equals(T value) const
{
return get_local() == value;
}
public:
void invoke_event_function(std::function<void(type)> function)
{
m_invoke_event_function = function;
}
void get_function(std::function<type()> function)
{
m_get_function = function;
}
};
template<typename T, typename base_type_ = local_data<T>>
class data_event : public base_type_
{
public:
using type = T;
using base_type = base_type_;
protected:
event_result dochange(type new_value)
{
auto old_value = get();
base_type::set(new_value);
onchanged(old_value);
return event_result::handled;
}
public:
event<type> onchange;
event<type> onchanged;
data_event(events_queue *queue = nullptr)
: onchange(queue)
, onchanged(queue)
{
onchange.bind(this, &data_event::dochange);
base_type::set({});
}
type get() const
{
return base_type::get();
}
type operator()() const
{
return get();
}
void change(type value, bool use_custom_invoke_event = true)
{
if (!base_type::equals(value))
{
if (!use_custom_invoke_event || !base_type::invoke_event(value))
{
onchange(value);
}
}
}
operator const type() const
{
return get();
}
operator type()
{
return get();
}
data_event& operator = (type value)
{
change(value);
return *this;
}
template<typename aType> auto operator + (aType value) const { return get() + value; }
template<typename aType> auto operator - (aType value) const { return get() - value; }
template<typename aType> auto operator * (aType value) const { return get() * value; }
template<typename aType> auto operator / (aType value) const { return get() / value; }
template<typename aType> auto operator % (aType value) const { return get() % value; }
template<typename aType> auto operator & (aType value) const { return get() & value; }
template<typename aType> auto operator | (aType value) const { return get() | value; }
template<typename aType> auto operator ^ (aType value) const { return get() ^ value; }
template<typename aType> data_event& operator += (aType value) { return *this = get() + value; }
template<typename aType> data_event& operator -= (aType value) { return *this = get() - value; }
template<typename aType> data_event& operator *= (aType value) { return *this = get() * value; }
template<typename aType> data_event& operator /= (aType value) { return *this = get() / value; }
template<typename aType> data_event& operator %= (aType value) { return *this = get() % value; }
template<typename aType> data_event& operator &= (aType value) { return *this = get() & value; }
template<typename aType> data_event& operator |= (aType value) { return *this = get() | value; }
template<typename aType> data_event& operator ^= (aType value) { return *this = get() ^ value; }
data_event& operator ++()
{
type value = get();
return *this = ++value;
}
type operator ++(int)
{
type value = get();
type result = value;
*this = value++;
return result;
}
data_event& operator --()
{
type value = get();
return *this = --value;
}
type operator --(int)
{
type value = get();
type result = value;
*this = value--;
return result;
}
};
struct with_event_binder
{
event_binder_t event_binder;
};

1102
include/types.h Normal file

File diff suppressed because it is too large Load Diff

881
src/File.cpp Normal file
View File

@@ -0,0 +1,881 @@
#include "Log.h"
#include "File.h"
#ifdef _WIN32
#define _WIN32_WINNT 0x0601
#include <Windows.h>
#define GET_API_ERROR static_cast<u64>(GetLastError())
static std::unique_ptr<wchar_t[]> to_wchar(const std::string& source)
{
const auto length = source.size() + 1; // size + null terminator
const int size = source.size() < INT_MAX ? static_cast<int>(length) : throw EXCEPTION("Invalid source length (0x%llx)", source.size());
std::unique_ptr<wchar_t[]> buffer(new wchar_t[length]); // allocate buffer assuming that length is the max possible size
if (!MultiByteToWideChar(CP_UTF8, 0, source.c_str(), size, buffer.get(), size))
{
throw EXCEPTION("System error 0x%x", GetLastError());
}
return buffer;
}
static void to_utf8(std::string& result, const wchar_t* source)
{
const int length = lstrlenW(source); // source length
if (length == 0)
{
return result.clear();
}
const int size = WideCharToMultiByte(CP_UTF8, 0, source, length, NULL, 0, NULL, NULL); // output size
if (size <= 0)
{
throw EXCEPTION("System error 0x%x", GetLastError());
}
result.resize(size);
if (!WideCharToMultiByte(CP_UTF8, 0, source, length, &result.front(), size, NULL, NULL))
{
throw EXCEPTION("System error 0x%x", GetLastError());
}
}
static time_t to_time(const ULARGE_INTEGER& ft)
{
return ft.QuadPart / 10000000ULL - 11644473600ULL;
}
static time_t to_time(const LARGE_INTEGER& ft)
{
ULARGE_INTEGER v;
v.LowPart = ft.LowPart;
v.HighPart = ft.HighPart;
return to_time(v);
}
static time_t to_time(const FILETIME& ft)
{
ULARGE_INTEGER v;
v.LowPart = ft.dwLowDateTime;
v.HighPart = ft.dwHighDateTime;
return to_time(v);
}
static bool truncate_file(const std::string& file, u64 length)
{
// open the file
const auto handle = CreateFileW(to_wchar(file).get(), GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (handle == INVALID_HANDLE_VALUE)
{
return false;
}
LARGE_INTEGER distance;
distance.QuadPart = length;
// seek and truncate
if (!SetFilePointerEx(handle, distance, NULL, FILE_BEGIN) || !SetEndOfFile(handle))
{
const auto error = GetLastError();
CloseHandle(handle);
SetLastError(error);
return false;
}
return CloseHandle(handle);
}
#else
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <fcntl.h>
#include <unistd.h>
#if defined(__APPLE__) || defined(__FreeBSD__)
#include <copyfile.h>
#include <mach-o/dyld.h>
#else
#include <sys/sendfile.h>
#endif
#include <errno.h>
#define GET_API_ERROR static_cast<u64>(errno)
#endif
thread_local fse fs::g_tls_error = fse::ok;
bool fs::stat(const std::string& path, stat_t& info)
{
g_tls_error = fse::ok;
#ifdef _WIN32
WIN32_FILE_ATTRIBUTE_DATA attrs;
if (!GetFileAttributesExW(to_wchar(path).get(), GetFileExInfoStandard, &attrs))
{
return false;
}
info.is_directory = (attrs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
info.is_writable = (attrs.dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0;
info.size = (u64)attrs.nFileSizeLow | ((u64)attrs.nFileSizeHigh << 32);
info.atime = to_time(attrs.ftLastAccessTime);
info.mtime = to_time(attrs.ftLastWriteTime);
info.ctime = to_time(attrs.ftCreationTime);
#else
struct stat file_info;
if (stat(path.c_str(), &file_info) < 0)
{
return false;
}
info.is_directory = S_ISDIR(file_info.st_mode);
info.is_writable = file_info.st_mode & 0200; // HACK: approximation
info.size = file_info.st_size;
info.atime = file_info.st_atime;
info.mtime = file_info.st_mtime;
info.ctime = file_info.st_ctime;
#endif
return true;
}
bool fs::exists(const std::string& path)
{
g_tls_error = fse::ok;
#ifdef _WIN32
return GetFileAttributesW(to_wchar(path).get()) != 0xFFFFFFFF;
#else
struct stat buffer;
return stat(path.c_str(), &buffer) == 0;
#endif
}
bool fs::is_file(const std::string& file)
{
g_tls_error = fse::ok;
#ifdef _WIN32
DWORD attrs;
if ((attrs = GetFileAttributesW(to_wchar(file).get())) == INVALID_FILE_ATTRIBUTES)
{
return false;
}
return (attrs & FILE_ATTRIBUTE_DIRECTORY) == 0;
#else
struct stat file_info;
if (stat(file.c_str(), &file_info) < 0)
{
return false;
}
return !S_ISDIR(file_info.st_mode);
#endif
}
bool fs::is_dir(const std::string& dir)
{
g_tls_error = fse::ok;
#ifdef _WIN32
DWORD attrs;
if ((attrs = GetFileAttributesW(to_wchar(dir).get())) == INVALID_FILE_ATTRIBUTES)
{
return false;
}
return (attrs & FILE_ATTRIBUTE_DIRECTORY) != 0;
#else
struct stat file_info;
if (stat(dir.c_str(), &file_info) < 0)
{
return false;
}
return S_ISDIR(file_info.st_mode);
#endif
}
bool fs::create_dir(const std::string& dir)
{
g_tls_error = fse::ok;
#ifdef _WIN32
if (!CreateDirectoryW(to_wchar(dir).get(), NULL))
#else
if (mkdir(dir.c_str(), 0777))
#endif
{
LOG_WARNING(GENERAL, "Error creating directory '%s': 0x%llx", dir, GET_API_ERROR);
return false;
}
return true;
}
bool fs::create_path(const std::string& path)
{
g_tls_error = fse::ok;
size_t start = 0;
while (true)
{
// maybe it could be more optimal if goes from the end recursively
size_t pos = path.find_first_of("/\\", start);
if (pos == std::string::npos)
{
pos = path.length();
}
std::string dir = path.substr(0, pos);
start = ++pos;
if (dir.size() == 0)
{
continue;
}
if (!is_dir(dir))
{
// if doesn't exist or not a dir
if (!create_dir(dir))
{
// if creating failed
return false;
}
}
if (pos >= path.length())
{
break;
}
}
return true;
}
bool fs::remove_dir(const std::string& dir)
{
g_tls_error = fse::ok;
#ifdef _WIN32
if (!RemoveDirectoryW(to_wchar(dir).get()))
#else
if (rmdir(dir.c_str()))
#endif
{
LOG_WARNING(GENERAL, "Error deleting directory '%s': 0x%llx", dir, GET_API_ERROR);
return false;
}
return true;
}
bool fs::rename(const std::string& from, const std::string& to)
{
g_tls_error = fse::ok;
#ifdef _WIN32
if (!MoveFileW(to_wchar(from).get(), to_wchar(to).get()))
#else
if (rename(from.c_str(), to.c_str()))
#endif
{
LOG_WARNING(GENERAL, "Error renaming '%s' to '%s': 0x%llx", from, to, GET_API_ERROR);
return false;
}
return true;
}
#ifndef _WIN32
static int OSCopyFile(const char* source, const char* destination, bool overwrite)
{
/* Source: http://stackoverflow.com/questions/2180079/how-can-i-copy-a-file-on-unix-using-c */
const int input = open(source, O_RDONLY);
if (input == -1)
{
return -1;
}
const int output = open(destination, O_WRONLY | O_CREAT | (overwrite ? O_TRUNC : O_EXCL), 0666);
if (output == -1)
{
close(input);
return -1;
}
//Here we use kernel-space copying for performance reasons
#if defined(__APPLE__) || defined(__FreeBSD__)
//fcopyfile works on FreeBSD and OS X 10.5+
const int result = fcopyfile(input, output, 0, COPYFILE_ALL);
#else
//sendfile will work with non-socket output (i.e. regular file) on Linux 2.6.33+
off_t bytesCopied = 0;
struct stat fileinfo = { 0 };
const int result = fstat(input, &fileinfo) == -1 || sendfile(output, input, &bytesCopied, fileinfo.st_size) == -1 ? -1 : 0;
#endif
close(input);
close(output);
return result;
}
#endif
bool fs::copy_file(const std::string& from, const std::string& to, bool overwrite)
{
g_tls_error = fse::ok;
#ifdef _WIN32
if (!CopyFileW(to_wchar(from).get(), to_wchar(to).get(), !overwrite))
#else
if (OSCopyFile(from.c_str(), to.c_str(), overwrite))
#endif
{
LOG_WARNING(GENERAL, "Error copying '%s' to '%s': 0x%llx", from, to, GET_API_ERROR);
return false;
}
return true;
}
bool fs::remove_file(const std::string& file)
{
g_tls_error = fse::ok;
#ifdef _WIN32
if (!DeleteFileW(to_wchar(file).get()))
#else
if (unlink(file.c_str()))
#endif
{
LOG_WARNING(GENERAL, "Error deleting file '%s': 0x%llx", file, GET_API_ERROR);
return false;
}
return true;
}
bool fs::truncate_file(const std::string& file, u64 length)
{
g_tls_error = fse::ok;
#ifdef _WIN32
if (!::truncate_file(file, length))
#else
if (::truncate(file.c_str(), length))
#endif
{
LOG_WARNING(GENERAL, "Error resizing file '%s' to 0x%llx: 0x%llx", file, length, GET_API_ERROR);
return false;
}
return true;
}
fs::file::~file()
{
if (m_fd != null)
{
#ifdef _WIN32
CloseHandle((HANDLE)m_fd);
#else
::close(m_fd);
#endif
}
}
bool fs::file::open(const std::string& filename, u32 mode)
{
this->close();
g_tls_error = fse::ok;
#ifdef _WIN32
DWORD access = 0;
switch (mode & (fom::read | fom::write | fom::append))
{
case fom::read: access |= GENERIC_READ; break;
case fom::read | fom::append: access |= GENERIC_READ; break;
case fom::write: access |= GENERIC_WRITE; break;
case fom::write | fom::append: access |= FILE_APPEND_DATA; break;
case fom::read | fom::write: access |= GENERIC_READ | GENERIC_WRITE; break;
case fom::read | fom::write | fom::append: access |= GENERIC_READ | FILE_APPEND_DATA; break;
default:
{
LOG_ERROR(GENERAL, "fs::file::open('%s') failed: neither fom::read nor fom::write specified (0x%x)", filename, mode);
return false;
}
}
DWORD disp = 0;
switch (mode & (fom::create | fom::trunc | fom::excl))
{
case 0: disp = OPEN_EXISTING; break;
case fom::create: disp = OPEN_ALWAYS; break;
case fom::trunc: disp = TRUNCATE_EXISTING; break;
case fom::create | fom::trunc: disp = CREATE_ALWAYS; break;
case fom::create | fom::excl: disp = CREATE_NEW; break;
case fom::create | fom::excl | fom::trunc: disp = CREATE_NEW; break;
}
if (!disp || (mode & ~(fom::read | fom::write | fom::append | fom::create | fom::trunc | fom::excl)))
{
LOG_ERROR(GENERAL, "fs::file::open('%s') failed: unknown mode specified (0x%x)", filename, mode);
return false;
}
m_fd = (std::intptr_t)CreateFileW(to_wchar(filename).get(), access, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, disp, FILE_ATTRIBUTE_NORMAL, NULL);
#else
int flags = 0;
switch (mode & (fom::read | fom::write))
{
case fom::read: flags |= O_RDONLY; break;
case fom::write: flags |= O_WRONLY; break;
case fom::read | fom::write: flags |= O_RDWR; break;
default:
{
LOG_ERROR(GENERAL, "fs::file::open('%s') failed: neither fom::read nor fom::write specified (0x%x)", filename, mode);
return false;
}
}
if (mode & fom::append) flags |= O_APPEND;
if (mode & fom::create) flags |= O_CREAT;
if (mode & fom::trunc) flags |= O_TRUNC;
if (mode & fom::excl) flags |= O_EXCL;
if (((mode & fom::excl) && !(mode & fom::create)) || (mode & ~(fom::read | fom::write | fom::append | fom::create | fom::trunc | fom::excl)))
{
LOG_ERROR(GENERAL, "fs::file::open('%s') failed: unknown mode specified (0x%x)", filename, mode);
return false;
}
m_fd = ::open(filename.c_str(), flags, 0666);
#endif
if (m_fd == null)
{
LOG_WARNING(GENERAL, "fs::file::open('%s', 0x%x) failed: error 0x%llx", filename, mode, GET_API_ERROR);
return false;
}
return true;
}
bool fs::file::trunc(u64 size) const
{
g_tls_error = fse::ok;
#ifdef _WIN32
LARGE_INTEGER old, pos;
pos.QuadPart = 0;
SetFilePointerEx((HANDLE)m_fd, pos, &old, FILE_CURRENT); // get old position
pos.QuadPart = size;
SetFilePointerEx((HANDLE)m_fd, pos, NULL, FILE_BEGIN); // set new position
SetEndOfFile((HANDLE)m_fd); // change file size
SetFilePointerEx((HANDLE)m_fd, old, NULL, FILE_BEGIN); // restore position
return true; // TODO
#else
return !::ftruncate(m_fd, size);
#endif
}
bool fs::file::stat(stat_t& info) const
{
g_tls_error = fse::ok;
#ifdef _WIN32
FILE_BASIC_INFO basic_info;
if (!GetFileInformationByHandleEx((HANDLE)m_fd, FileBasicInfo, &basic_info, sizeof(FILE_BASIC_INFO)))
{
return false;
}
info.is_directory = (basic_info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
info.is_writable = (basic_info.FileAttributes & FILE_ATTRIBUTE_READONLY) == 0;
info.size = this->size();
info.atime = to_time(basic_info.LastAccessTime);
info.mtime = to_time(basic_info.ChangeTime);
info.ctime = to_time(basic_info.CreationTime);
#else
struct stat file_info;
if (fstat(m_fd, &file_info) < 0)
{
return false;
}
info.is_directory = S_ISDIR(file_info.st_mode);
info.is_writable = file_info.st_mode & 0200; // HACK: approximation
info.size = file_info.st_size;
info.atime = file_info.st_atime;
info.mtime = file_info.st_mtime;
info.ctime = file_info.st_ctime;
#endif
return true;
}
bool fs::file::close()
{
g_tls_error = fse::ok;
if (m_fd == null)
{
return false;
}
auto fd = m_fd;
m_fd = null;
#ifdef _WIN32
return CloseHandle((HANDLE)fd);
#else
return !::close(fd);
#endif
}
u64 fs::file::read(void* buffer, u64 count) const
{
g_tls_error = fse::ok;
// TODO (call ReadFile multiple times if count is too big)
const int size = count <= INT_MAX ? static_cast<int>(count) : throw EXCEPTION("Invalid count (0x%llx)", count);
#ifdef _WIN32
DWORD nread;
if (!ReadFile((HANDLE)m_fd, buffer, size, &nread, NULL))
{
return -1;
}
return nread;
#else
return ::read(m_fd, buffer, size);
#endif
}
u64 fs::file::write(const void* buffer, u64 count) const
{
g_tls_error = fse::ok;
// TODO (call WriteFile multiple times if count is too big)
const int size = count <= INT_MAX ? static_cast<int>(count) : throw EXCEPTION("Invalid count (0x%llx)", count);
#ifdef _WIN32
DWORD nwritten;
if (!WriteFile((HANDLE)m_fd, buffer, size, &nwritten, NULL))
{
return -1;
}
return nwritten;
#else
return ::write(m_fd, buffer, size);
#endif
}
u64 fs::file::seek(s64 offset, fsm seek_mode) const
{
g_tls_error = fse::ok;
#ifdef _WIN32
LARGE_INTEGER pos;
pos.QuadPart = offset;
const DWORD mode =
seek_mode == fsm::begin ? FILE_BEGIN :
seek_mode == fsm::cur ? FILE_CURRENT :
seek_mode == fsm::end ? FILE_END :
throw EXCEPTION("Unknown seek_mode (0x%x)", seek_mode);
if (!SetFilePointerEx((HANDLE)m_fd, pos, &pos, mode))
{
return -1;
}
return pos.QuadPart;
#else
const int whence =
seek_mode == fsm::begin ? SEEK_SET :
seek_mode == fsm::cur ? SEEK_CUR :
seek_mode == fsm::end ? SEEK_END :
throw EXCEPTION("Unknown seek_mode (0x%x)", seek_mode);
return ::lseek(m_fd, offset, whence);
#endif
}
u64 fs::file::size() const
{
g_tls_error = fse::ok;
#ifdef _WIN32
LARGE_INTEGER size;
if (!GetFileSizeEx((HANDLE)m_fd, &size))
{
return -1;
}
return size.QuadPart;
#else
struct stat file_info;
if (::fstat(m_fd, &file_info) < 0)
{
return -1;
}
return file_info.st_size;
#endif
}
fs::dir::~dir()
{
if (m_path)
{
#ifdef _WIN32
if (m_dd != -1) FindClose((HANDLE)m_dd);
#else
::closedir((DIR*)m_dd);
#endif
}
}
void fs::file_ptr::reset(const file& f)
{
reset();
if (f)
{
#ifdef _WIN32
const HANDLE handle = ::CreateFileMapping((HANDLE)f.m_fd, NULL, PAGE_READONLY, 0, 0, NULL);
m_ptr = (char*)::MapViewOfFile(handle, FILE_MAP_READ, 0, 0, 0);
m_size = f.size();
::CloseHandle(handle);
#else
m_ptr = (char*)::mmap(nullptr, m_size = f.size(), PROT_READ, MAP_SHARED, f.m_fd, 0);
if (m_ptr == (void*)-1) m_ptr = nullptr;
#endif
}
}
void fs::file_ptr::reset()
{
if (m_ptr)
{
#ifdef _WIN32
::UnmapViewOfFile(m_ptr);
#else
::munmap(m_ptr, m_size);
#endif
}
}
bool fs::dir::open(const std::string& dirname)
{
this->close();
g_tls_error = fse::ok;
if (!is_dir(dirname))
{
return false;
}
m_path.reset(new char[dirname.size() + 1]);
std::memcpy(m_path.get(), dirname.c_str(), dirname.size() + 1);
#ifdef _WIN32
m_dd = -1;
#else
m_dd = (std::intptr_t)::opendir(m_path.get());
#endif
return true;
}
bool fs::dir::close()
{
g_tls_error = fse::ok;
if (!m_path)
{
return false;
}
m_path.reset();
#ifdef _WIN32
CHECK_ASSERTION(m_dd == -1 || FindClose((HANDLE)m_dd));
#else
CHECK_ASSERTION(!::closedir((DIR*)m_dd));
#endif
return true;
}
bool fs::dir::read(std::string& name, stat_t& info)
{
g_tls_error = fse::ok;
if (!m_path)
{
return false;
}
#ifdef _WIN32
WIN32_FIND_DATAW found;
if (m_dd == -1)
{
m_dd = (std::intptr_t)FindFirstFileW(to_wchar(m_path.get() + std::string("/*")).get(), &found);
if (m_dd == -1)
{
return false;
}
}
else if (!FindNextFileW((HANDLE)m_dd, &found))
{
return false;
}
to_utf8(name, found.cFileName);
info.is_directory = (found.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
info.is_writable = (found.dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0;
info.size = ((u64)found.nFileSizeHigh << 32) | (u64)found.nFileSizeLow;
info.atime = to_time(found.ftLastAccessTime);
info.mtime = to_time(found.ftLastWriteTime);
info.ctime = to_time(found.ftCreationTime);
#else
const auto found = ::readdir((DIR*)m_dd);
struct stat file_info;
if (!found || ::fstatat(::dirfd((DIR*)m_dd), found->d_name, &file_info, 0) < 0)
{
return false;
}
name = found->d_name;
info.is_directory = S_ISDIR(file_info.st_mode);
info.is_writable = file_info.st_mode & 0200; // HACK: approximation
info.size = file_info.st_size;
info.atime = file_info.st_atime;
info.mtime = file_info.st_mtime;
info.ctime = file_info.st_ctime;
#endif
return true;
}
std::string fs::get_config_dir()
{
// Use magic static for dir initialization
static const std::string s_dir = []
{
#ifdef _WIN32
return get_executable_dir(); // ?
#else
std::string dir;
if (const char* home = ::getenv("XDG_CONFIG_HOME"))
dir = home;
else if (const char* home = ::getenv("HOME"))
dir = home + "/.config"s;
else // Just in case
dir = "./config";
dir += "/rpcs3/";
if (::mkdir(dir.c_str(), 0777) == -1 && errno != EEXIST)
{
std::printf("Failed to create configuration directory '%s' (%d).\n", dir.c_str(), errno);
}
return dir;
#endif
}();
return s_dir;
}
std::string fs::get_executable_dir()
{
// Use magic static for dir initialization
static const std::string s_dir = []
{
std::string dir;
#ifdef _WIN32
wchar_t buf[2048];
if (GetModuleFileName(NULL, buf, ::size32(buf)) - 1 >= ::size32(buf) - 1)
{
MessageBoxA(0, fmt::format("GetModuleFileName() failed (0x%x).", GetLastError()).c_str(), "fs::get_config_dir()", MB_ICONERROR);
return dir; // empty
}
to_utf8(dir, buf); // Convert to UTF-8
#elif __APPLE__
char buf[4096];
u32 size = sizeof(buf);
if (_NSGetExecutablePath(buf, &size))
{
std::printf("_NSGetExecutablePath() failed (size=0x%x).\n", size);
return dir; // empty
}
dir = buf;
#else
char buf[4096];
const auto size = ::readlink("/proc/self/exe", buf, sizeof(buf));
if (size <= 0 || size >= sizeof(buf))
{
std::printf("readlink(/proc/self/exe) failed (%d).\n", errno);
return dir; // empty
}
dir = { buf, static_cast<std::size_t>(size) };
#endif
// Replace "\"
for (auto& c : dir)
{
if (c == '\\') c = '/';
}
// Leave only path
dir.resize(dir.rfind('/') + 1);
return dir;
}();
return s_dir;
}

51
src/GNU.cpp Normal file
View File

@@ -0,0 +1,51 @@
#include "GNU.h"
#ifdef __APPLE__
#include <sys/types.h>
#include <sys/_types/_timespec.h>
#include <mach/mach.h>
#include <mach/clock.h>
#include <mach/mach_time.h>
#undef CPU_STATE_MAX
#define MT_NANO (+1.0E-9)
#define MT_GIGA UINT64_C(1000000000)
// TODO create a list of timers,
static double mt_timebase = 0.0;
static uint64_t mt_timestart = 0;
// TODO be more careful in a multithreaded environement
int clock_gettime(clockid_t clk_id, struct timespec *tp)
{
kern_return_t retval = KERN_SUCCESS;
if( clk_id == TIMER_ABSTIME)
{
if (!mt_timestart) { // only one timer, initilized on the first call to the TIMER
mach_timebase_info_data_t tb = { 0 };
mach_timebase_info(&tb);
mt_timebase = tb.numer;
mt_timebase /= tb.denom;
mt_timestart = mach_absolute_time();
}
double diff = (mach_absolute_time() - mt_timestart) * mt_timebase;
tp->tv_sec = diff * MT_NANO;
tp->tv_nsec = diff - (tp->tv_sec * MT_GIGA);
}
else // other clk_ids are mapped to the coresponding mach clock_service
{
clock_serv_t cclock;
mach_timespec_t mts;
host_get_clock_service(mach_host_self(), clk_id, &cclock);
retval = clock_get_time(cclock, &mts);
mach_port_deallocate(mach_task_self(), cclock);
tp->tv_sec = mts.tv_sec;
tp->tv_nsec = mts.tv_nsec;
}
return retval;
}
#endif /* __APPLE__ */

280
src/Log.cpp Normal file
View File

@@ -0,0 +1,280 @@
#include <iostream>
#include <cinttypes>
#include "File.h"
#include "Log.h"
#ifdef _WIN32
#include <Windows.h>
#endif
using namespace Log;
std::unique_ptr<LogManager> g_log_manager;
u32 LogMessage::size() const
{
//1 byte for NULL terminator
return (u32)(sizeof(LogMessage::size_type) + sizeof(LogType) + sizeof(Severity) + sizeof(std::string::value_type) * mText.size() + 1);
}
void LogMessage::serialize(char *output) const
{
LogMessage::size_type size = this->size();
memcpy(output, &size, sizeof(LogMessage::size_type));
output += sizeof(LogMessage::size_type);
memcpy(output, &mType, sizeof(LogType));
output += sizeof(LogType);
memcpy(output, &mServerity, sizeof(Severity));
output += sizeof(Severity);
memcpy(output, mText.c_str(), mText.size() );
output += sizeof(std::string::value_type)*mText.size();
*output = '\0';
}
LogMessage LogMessage::deserialize(char *input, u32* size_out)
{
LogMessage msg;
LogMessage::size_type msgSize = *(reinterpret_cast<LogMessage::size_type*>(input));
input += sizeof(LogMessage::size_type);
msg.mType = *(reinterpret_cast<LogType*>(input));
input += sizeof(LogType);
msg.mServerity = *(reinterpret_cast<Severity*>(input));
input += sizeof(Severity);
if (msgSize > 9000)
{
int wtf = 6;
}
msg.mText.append(input, msgSize - 1 - sizeof(Severity) - sizeof(LogType));
if (size_out){(*size_out) = msgSize;}
return msg;
}
LogChannel::LogChannel() : LogChannel("unknown")
{}
LogChannel::LogChannel(const std::string& name) :
name(name)
, mEnabled(true)
, mLogLevel(Severity::Warning)
{}
void LogChannel::log(const LogMessage &msg)
{
std::lock_guard<std::mutex> lock(mListenerLock);
for (auto &listener : mListeners)
{
listener->log(msg);
}
}
void LogChannel::addListener(std::shared_ptr<LogListener> listener)
{
std::lock_guard<std::mutex> lock(mListenerLock);
mListeners.insert(listener);
}
void LogChannel::removeListener(std::shared_ptr<LogListener> listener)
{
std::lock_guard<std::mutex> lock(mListenerLock);
mListeners.erase(listener);
}
struct CoutListener : LogListener
{
void log(const LogMessage &msg) override
{
std::cerr << msg.mText << std::endl;
}
};
struct FileListener : LogListener
{
fs::file mFile;
bool mPrependChannelName;
FileListener(const std::string& name, bool prependChannel = true)
: mFile(fs::get_config_dir() + name, fom::rewrite)
, mPrependChannelName(prependChannel)
{
if (!mFile)
{
#ifdef _WIN32
MessageBoxA(0, ("Can't create log file: " + name).c_str(), "Error", MB_ICONERROR);
#else
std::printf("Can't create log file: %s\n", name.c_str());
#endif
}
}
void log(const LogMessage &msg) override
{
std::string text = msg.mText;
if (mPrependChannelName)
{
text.insert(0, gTypeNameTable[static_cast<u32>(msg.mType)].mName);
if (msg.mType == Log::TTY)
{
text = fmt::escape(text);
if (text[text.length() - 1] != '\n')
{
text += '\n';
}
}
}
mFile << text;
}
};
LogManager::LogManager()
#ifdef BUFFERED_LOGGING
: mExiting(false), mLogConsumer()
#endif
{
auto it = mChannels.begin();
std::shared_ptr<LogListener> listener(new FileListener("program.log"));
for (const LogTypeName& name : gTypeNameTable)
{
it->name = name.mName;
it->addListener(listener);
it++;
}
std::shared_ptr<LogListener> TTYListener(new FileListener("TTY.log", false));
getChannel(TTY).addListener(TTYListener);
#ifdef BUFFERED_LOGGING
mLogConsumer = std::thread(&LogManager::consumeLog, this);
#endif
}
LogManager::~LogManager()
{
#ifdef BUFFERED_LOGGING
mExiting = true;
mBufferReady.notify_all();
mLogConsumer.join();
}
void LogManager::consumeLog()
{
std::unique_lock<std::mutex> lock(mStatusMut);
while (!mExiting)
{
mBufferReady.wait(lock);
mBuffer.lockGet();
size_t size = mBuffer.size();
std::vector<char> local_messages(size);
mBuffer.popN(&local_messages.front(), size);
mBuffer.unlockGet();
u32 cursor = 0;
u32 removed = 0;
while (cursor < size)
{
Log::LogMessage msg = Log::LogMessage::deserialize(local_messages.data() + cursor, &removed);
cursor += removed;
getChannel(msg.mType).log(msg);
}
}
#endif
}
void LogManager::log(LogMessage msg)
{
//don't do any formatting changes or filtering to the TTY output since we
//use the raw output to do diffs with the output of a real PS3 and some
//programs write text in single bytes to the console
if (msg.mType != TTY)
{
std::string prefix;
switch (msg.mServerity)
{
case Severity::Success:
prefix = "S ";
break;
case Severity::Notice:
prefix = "! ";
break;
case Severity::Warning:
prefix = "W ";
break;
case Severity::Error:
prefix = "E ";
break;
}
msg.mText.insert(0, prefix);
msg.mText.append(1,'\n');
}
#ifdef BUFFERED_LOGGING
size_t size = msg.size();
std::vector<char> temp_buffer(size);
msg.serialize(temp_buffer.data());
mBuffer.pushRange(temp_buffer.begin(), temp_buffer.end());
mBufferReady.notify_one();
#else
mChannels[static_cast<u32>(msg.mType)].log(msg);
#endif
}
void LogManager::addListener(std::shared_ptr<LogListener> listener)
{
for (auto& channel : mChannels)
{
channel.addListener(listener);
}
}
void LogManager::removeListener(std::shared_ptr<LogListener> listener)
{
for (auto& channel : mChannels)
{
channel.removeListener(listener);
}
}
LogManager& LogManager::getInstance()
{
if (!g_log_manager)
{
g_log_manager.reset(new LogManager());
}
return *g_log_manager;
}
LogChannel &LogManager::getChannel(LogType type)
{
return mChannels[static_cast<u32>(type)];
}
void log_message(Log::LogType type, Log::Severity sev, const char* text)
{
log_message(type, sev, std::string(text));
}
void log_message(Log::LogType type, Log::Severity sev, std::string text)
{
if (g_log_manager)
{
g_log_manager->log({ type, sev, std::move(text) });
}
else
{
const auto severity =
sev == Severity::Notice ? "Notice" :
sev == Severity::Warning ? "Warning" :
sev == Severity::Success ? "Success" :
sev == Severity::Error ? "Error" : "Unknown";
#ifdef _WIN32
MessageBoxA(0, text.c_str(), severity,
sev == Severity::Notice ? MB_ICONINFORMATION :
sev == Severity::Warning ? MB_ICONEXCLAMATION :
sev == Severity::Error ? MB_ICONERROR : MB_ICONINFORMATION);
#else
std::printf("[Log:%s] %s\n", severity, text.c_str());
#endif
}
}

119
src/Semaphore.cpp Normal file
View File

@@ -0,0 +1,119 @@
#include "Semaphore.h"
bool semaphore_t::try_wait()
{
// check m_value without interlocked op
if (m_var.load().value == 0)
{
return false;
}
// try to decrement m_value atomically
const auto old = m_var.atomic_op([](sync_var_t& var)
{
if (var.value)
{
var.value--;
}
});
// recheck atomic result
if (old.value == 0)
{
return false;
}
return true;
}
bool semaphore_t::try_post()
{
// check m_value without interlocked op
if (m_var.load().value >= max_value)
{
return false;
}
// try to increment m_value atomically
const auto old = m_var.atomic_op([&](sync_var_t& var)
{
if (var.value < max_value)
{
var.value++;
}
});
// recheck atomic result
if (old.value >= max_value)
{
return false;
}
if (old.waiters)
{
// notify waiting thread
std::lock_guard<std::mutex> lock(m_mutex);
m_cv.notify_one();
}
return true;
}
void semaphore_t::wait()
{
if (m_var.atomic_op([](sync_var_t& var) -> bool
{
if (var.value)
{
var.value--;
return true;
}
else
{
//var.waiters++;
return false;
}
}))
{
return;
}
std::unique_lock<std::mutex> lock(m_mutex);
m_var.atomic_op([](sync_var_t& var)
{
var.waiters++;
});
while (!m_var.atomic_op([](sync_var_t& var) -> bool
{
if (var.value)
{
var.value--;
var.waiters--;
return true;
}
else
{
return false;
}
}))
{
m_cv.wait(lock);
}
}
bool semaphore_t::post_and_wait()
{
// TODO: merge these functions? Probably has a race condition.
if (try_wait()) return false;
try_post();
wait();
return true;
}

103
src/SharedMutex.cpp Normal file
View File

@@ -0,0 +1,103 @@
#include "SharedMutex.h"
#include "StrFmt.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();
}
}

52
src/SleepQueue.cpp Normal file
View File

@@ -0,0 +1,52 @@
#include "SleepQueue.h"
void sleep_queue_entry_t::add_entry()
{
m_queue.emplace_back(std::static_pointer_cast<sleep_entry_t>(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();
}

239
src/StrFmt.cpp Normal file
View File

@@ -0,0 +1,239 @@
#include "StrFmt.h"
std::string v128::to_hex() const
{
return fmt::format("%016llx%016llx", _u64[1], _u64[0]);
}
std::string v128::to_xyzw() const
{
return fmt::format("x: %g y: %g z: %g w: %g", _f[3], _f[2], _f[1], _f[0]);
}
std::string fmt::to_hex(u64 value, u64 count)
{
if (count - 1 >= 16)
{
throw EXCEPTION("Invalid count: 0x%llx", count);
}
count = std::max<u64>(count, 16 - cntlz64(value) / 4);
char res[16] = {};
for (size_t i = count - 1; ~i; i--, value /= 16)
{
res[i] = "0123456789abcdef"[value % 16];
}
return std::string(res, count);
}
std::string fmt::to_udec(u64 value)
{
char res[20] = {};
size_t first = sizeof(res);
if (!value)
{
res[--first] = '0';
}
for (; value; value /= 10)
{
res[--first] = '0' + (value % 10);
}
return std::string(&res[first], sizeof(res) - first);
}
std::string fmt::to_sdec(s64 svalue)
{
const bool sign = svalue < 0;
u64 value = sign ? -svalue : svalue;
char res[20] = {};
size_t first = sizeof(res);
if (!value)
{
res[--first] = '0';
}
for (; value; value /= 10)
{
res[--first] = '0' + (value % 10);
}
if (sign)
{
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)
{
auto pos = src.find(from);
if (pos == std::string::npos)
{
return src;
}
return (pos ? src.substr(0, pos) + to : to) + std::string(src.c_str() + pos + from.length());
}
std::string fmt::replace_all(const std::string &src, const std::string& from, const std::string& to)
{
std::string target = src;
for (auto pos = target.find(from); pos != std::string::npos; pos = target.find(from, pos + 1))
{
target = (pos ? target.substr(0, pos) + to : to) + std::string(target.c_str() + pos + from.length());
pos += to.length();
}
return target;
}
//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> result;
size_t cursor_begin = 0;
for (size_t cursor_end = 0; cursor_end < source.length(); ++cursor_end)
{
for (auto &separator : separators)
{
if (strncmp(source.c_str() + cursor_end, separator.c_str(), separator.length()) == 0)
{
std::string candidate = source.substr(cursor_begin, cursor_end - cursor_begin);
if (!is_skip_empty || !candidate.empty())
result.push_back(candidate);
cursor_begin = cursor_end + separator.length();
cursor_end = cursor_begin - 1;
break;
}
}
}
if (cursor_begin != source.length())
{
result.push_back(source.substr(cursor_begin));
}
return std::move(result);
}
std::string fmt::trim(const std::string& source, const std::string& values)
{
std::size_t begin = source.find_first_not_of(values);
if (begin == source.npos)
return{};
return source.substr(begin, source.find_last_not_of(values) + 1);
}
std::string fmt::tolower(std::string source)
{
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[] =
{
{ "\\", "\\\\" },
{ "\a", "\\a" },
{ "\b", "\\b" },
{ "\f", "\\f" },
{ "\n", "\\n\n" },
{ "\r", "\\r" },
{ "\t", "\\t" },
{ "\v", "\\v" },
};
source = fmt::replace_all(source, escape_list);
for (char c = 0; c < 32; c++)
{
if (c != '\n') source = fmt::replace_all(source, std::string(1, c), fmt::format("\\x%02X", c));
}
return source;
}

47
src/VirtualMemory.cpp Normal file
View File

@@ -0,0 +1,47 @@
#include "Log.h"
#include "VirtualMemory.h"
#include "GNU.h"
#include "StrFmt.h"
#ifdef _WIN32
#include <Windows.h>
#else
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#endif
namespace memory_helper
{
void* reserve_memory(size_t size)
{
#ifdef _WIN32
void* ret = VirtualAlloc(NULL, size, MEM_RESERVE, PAGE_NOACCESS);
CHECK_ASSERTION(ret != NULL);
#else
void* ret = mmap(nullptr, size, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0);
CHECK_ASSERTION(ret != 0);
#endif
return ret;
}
void commit_page_memory(void* pointer, size_t page_size)
{
#ifdef _WIN32
CHECK_ASSERTION(VirtualAlloc((u8*)pointer, page_size, MEM_COMMIT, PAGE_READWRITE) != NULL);
#else
CHECK_ASSERTION(mprotect((u8*)pointer, page_size, PROT_READ | PROT_WRITE) != -1);
#endif
}
void free_reserved_memory(void* pointer, size_t size)
{
#ifdef _WIN32
CHECK_ASSERTION(VirtualFree(pointer, 0, MEM_RELEASE) != 0);
#else
CHECK_ASSERTION(munmap(pointer, size) == 0);
#endif
}
}

171
src/config_context.cpp Normal file
View File

@@ -0,0 +1,171 @@
#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);
}