mirror of
https://github.com/RPCS3/common.git
synced 2026-01-31 01:25:19 +01:00
Added utilities from rpcs3
This commit is contained in:
165
common.vcxproj
Normal file
165
common.vcxproj
Normal 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
98
common.vcxproj.filters
Normal 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
701
include/Atomic.h
Normal 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
942
include/BEType.h
Normal 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
140
include/BitField.h
Normal 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
303
include/File.h
Normal 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
425
include/GNU.h
Normal 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
33
include/Interval.h
Normal 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
140
include/Log.h
Normal 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
158
include/MTRingbuffer.h
Normal 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
39
include/Semaphore.h
Normal 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
141
include/SharedMutex.h
Normal 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
52
include/SleepQueue.h
Normal 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
354
include/StrFmt.h
Normal 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
53
include/Timer.h
Normal 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
23
include/VirtualMemory.h
Normal 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
163
include/config_context.h
Normal 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
280
include/convert.h
Normal 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
409
include/event.h
Normal 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
1102
include/types.h
Normal file
File diff suppressed because it is too large
Load Diff
881
src/File.cpp
Normal file
881
src/File.cpp
Normal 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
51
src/GNU.cpp
Normal 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
280
src/Log.cpp
Normal 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
119
src/Semaphore.cpp
Normal 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
103
src/SharedMutex.cpp
Normal 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
52
src/SleepQueue.cpp
Normal 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
239
src/StrFmt.cpp
Normal 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
47
src/VirtualMemory.cpp
Normal 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
171
src/config_context.cpp
Normal 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);
|
||||
}
|
||||
Reference in New Issue
Block a user