Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3352e235b6 | |||
| 816169fe9e |
@@ -1,33 +0,0 @@
|
||||
Standard: Cpp11
|
||||
UseTab: ForIndentation
|
||||
TabWidth: 1
|
||||
IndentWidth: 1
|
||||
AccessModifierOffset: -1
|
||||
PointerAlignment: Left
|
||||
NamespaceIndentation: All
|
||||
ColumnLimit: 200
|
||||
BreakBeforeBraces: Allman
|
||||
BreakConstructorInitializersBeforeComma: true
|
||||
BreakBeforeBinaryOperators: false
|
||||
BreakBeforeTernaryOperators: false
|
||||
AlwaysBreakTemplateDeclarations: true
|
||||
AllowShortIfStatementsOnASingleLine: true
|
||||
AllowShortBlocksOnASingleLine: true
|
||||
AllowShortCaseLabelsOnASingleLine: true
|
||||
AllowShortFunctionsOnASingleLine: false
|
||||
AllowShortLoopsOnASingleLine: true
|
||||
Cpp11BracedListStyle: true
|
||||
IndentCaseLabels: false
|
||||
SortIncludes: false
|
||||
ReflowComments: true
|
||||
AlignConsecutiveAssignments: true
|
||||
AlignTrailingComments: true
|
||||
AlignAfterOpenBracket: DontAlign
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||
BinPackArguments: true
|
||||
BinPackParameters: true
|
||||
AlwaysBreakAfterReturnType: None
|
||||
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||
IndentWrappedFunctionNames: false
|
||||
|
||||
#SpaceAfterTemplateKeyword: false
|
||||
@@ -1,7 +0,0 @@
|
||||
root = true
|
||||
|
||||
[*.{h,cpp,hpp}]
|
||||
charset = utf-8
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
trim_trailing_whitespace = true
|
||||
+1
-9
@@ -41,6 +41,7 @@
|
||||
/Vulkan/Vulkan-build
|
||||
/Vulkan/glslang-build
|
||||
|
||||
/wxWidgets/lib
|
||||
/bin/rpcs3.ini
|
||||
/bin/rpcs3.ipdb
|
||||
/bin/rpcs3.iobj
|
||||
@@ -54,12 +55,9 @@
|
||||
rpcs3/git-version.h
|
||||
|
||||
# Visual Studio Files
|
||||
.vs/*
|
||||
*.ipch
|
||||
*.vspx
|
||||
*.psess
|
||||
*.VC.*
|
||||
*.vcxproj.user
|
||||
|
||||
# Copyrighted files
|
||||
/bin/data/
|
||||
@@ -87,9 +85,3 @@ CMakeCache.txt
|
||||
# cotire
|
||||
rpcs3/cotire/*
|
||||
rpcs3/rpcs3_*_cotire.cmake
|
||||
|
||||
# Qt
|
||||
moc_*.cpp
|
||||
qrc_resources.cpp
|
||||
rpcs3_automoc.cpp
|
||||
ui_*.h
|
||||
|
||||
+6
-14
@@ -1,3 +1,7 @@
|
||||
[submodule "wxWidgets"]
|
||||
path = wxWidgets
|
||||
url = https://github.com/wxWidgets/wxWidgets
|
||||
ignore = dirty
|
||||
[submodule "rpcs3-ffmpeg"]
|
||||
path = 3rdparty/ffmpeg
|
||||
url = https://github.com/hrydgard/ppsspp-ffmpeg
|
||||
@@ -8,6 +12,7 @@
|
||||
[submodule "llvm"]
|
||||
path = llvm
|
||||
url = https://github.com/llvm-mirror/llvm
|
||||
branch = release_36
|
||||
[submodule "rsx_program_decompiler"]
|
||||
path = rsx_program_decompiler
|
||||
url = https://github.com/RPCS3/rsx_program_decompiler
|
||||
@@ -16,7 +21,7 @@
|
||||
url = https://github.com/Microsoft/GSL.git
|
||||
[submodule "libpng"]
|
||||
path = 3rdparty/libpng
|
||||
url = https://github.com/Zangetsu38/libpng
|
||||
url = https://github.com/RPCS3/libpng
|
||||
ignore = dirty
|
||||
[submodule "Vulkan/glslang"]
|
||||
path = Vulkan/glslang
|
||||
@@ -30,16 +35,3 @@
|
||||
[submodule "3rdparty/pugixml"]
|
||||
path = 3rdparty/pugixml
|
||||
url = https://github.com/RPCS3/pugixml
|
||||
[submodule "3rdparty/cereal"]
|
||||
path = 3rdparty/cereal
|
||||
url = https://github.com/USCiLab/cereal.git
|
||||
[submodule "rsx-debugger"]
|
||||
path = rsx-debugger
|
||||
url = https://github.com/RPCS3/rsx-debugger.git
|
||||
[submodule "3rdparty/zlib"]
|
||||
path = 3rdparty/zlib
|
||||
url = https://github.com/madler/zlib
|
||||
[submodule "3rdparty/hidapi"]
|
||||
path = 3rdparty/hidapi
|
||||
url = https://github.com/Zangetsu38/hidapi
|
||||
branch = master
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"userBlacklist": ["AlexAltea", "tambry", "DHrpcs3"]
|
||||
"userBlacklist": ["AlexAltea", "tambry"]
|
||||
}
|
||||
|
||||
+48
-97
@@ -3,137 +3,88 @@ sudo: required
|
||||
dist: trusty
|
||||
|
||||
os:
|
||||
- linux
|
||||
#- osx
|
||||
- linux
|
||||
- osx
|
||||
|
||||
# osx_image: xcode6.4
|
||||
osx_image: xcode6.4
|
||||
|
||||
compiler:
|
||||
- clang
|
||||
- gcc
|
||||
|
||||
cache:
|
||||
ccache: true
|
||||
directories:
|
||||
- $HOME/hombebrew_cache
|
||||
- clang
|
||||
- gcc
|
||||
|
||||
env:
|
||||
global:
|
||||
- secure: BXyNziNYFLqLtY6Q58BDEUpRJr81HUtHpxf2Sr646bDcbG0Rf2rb2utqHZJ1Om926WdH7RqHuoVti4dr2X1lh4nHMkLD7MrIrfIkNN5T9aZdk19uw9IyMtz/cXon7/wqVo24tVqtBI0UghFxKii/GTDJEvRKgfpPp1e1vKSNGa4=
|
||||
# Which Travis environment to run Coverity on
|
||||
- coverity_scan_run_condition='"$TRAVIS_OS_NAME" = linux -a "$CC" = gcc'
|
||||
# Test mode is for testing if it's working with Coverity. Change to true if testing, to avoid reaching the quota.
|
||||
- coverity_scan_script_test_mode=false
|
||||
- secure: "Vf+FY48nip9JppMnq11105NealdErSWsoUhHo63/V3V+LKfA9guenxCp93/qoSIdSGC/sJwb0yIIMGvkTT/rxDJNh6Z+BWUTb2E0WEIIQbvTJNOSUzoq7dfF1LT61XjVjByFzcbC2xjtaBowmcAYEs1jGUUuEjYVCMmD5lY8hUg="
|
||||
# Which Travis environment to run Coverity on
|
||||
- coverity_scan_run_condition='"$TRAVIS_OS_NAME" = linux -a "$CC" = gcc'
|
||||
# Test mode is for testing if it's working with Coverity. Change to true if testing, to avoid reaching the quota.
|
||||
- coverity_scan_script_test_mode=false
|
||||
|
||||
matrix:
|
||||
exclude:
|
||||
- os: osx
|
||||
compiler: gcc
|
||||
include:
|
||||
- os: linux
|
||||
compiler: clang
|
||||
env: WITHOUT_LLVM="yes"
|
||||
|
||||
exclude:
|
||||
- os: osx
|
||||
compiler: gcc
|
||||
|
||||
git:
|
||||
submodules: false
|
||||
|
||||
before_install:
|
||||
- if [ "$CC" = "clang" ]; then
|
||||
export CXX="clang++-4.0" CC="clang-4.0";
|
||||
fi;
|
||||
# shutdown services on Travis, which may have a memory impact
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then
|
||||
sudo apt-get install -y libwxgtk3.0-dev;
|
||||
fi;
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" ] && [ "$CXX" = "g++" ]; then
|
||||
export CXX="g++-5" CC="gcc-5" CXXFLAGS="-Wno-format-security";
|
||||
export GCC_COLORS='error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01';
|
||||
fi;
|
||||
# Add coverall for C++ so coverall.io could be triggered. Even it should be --coverage and gcov.
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then
|
||||
sudo add-apt-repository ppa:beineri/opt-qt59-trusty -y;
|
||||
sudo apt-get update;
|
||||
sudo apt-get install qt59base -y --allow-unauthenticated;
|
||||
fi;
|
||||
# Add coverall for C++ so coverall.io could be triggered. Even it should be --coverage and gcov.
|
||||
# Install updated libglew-dev since the version provided by trusty is outdated
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then
|
||||
pip install --user cpp-coveralls requests[security];
|
||||
wget http://mirrors.kernel.org/ubuntu/pool/main/g/glew/libglew-dev_1.13.0-2_amd64.deb;
|
||||
wget http://mirrors.kernel.org/ubuntu/pool/main/g/glew/libglew1.13_1.13.0-2_amd64.deb;
|
||||
wget http://mirrors.kernel.org/ubuntu/pool/universe/v/vulkan/libvulkan-dev_1.0.21.0+dfsg1-1~16.04.1_amd64.deb;
|
||||
wget http://mirrors.kernel.org/ubuntu/pool/universe/v/vulkan/libvulkan1_1.0.21.0+dfsg1-1~16.04.1_amd64.deb;
|
||||
sudo dpkg -i libglew1.13_1.13.0-2_amd64.deb libglew-dev_1.13.0-2_amd64.deb libvulkan1_1.0.21.0+dfsg1-1~16.04.1_amd64.deb libvulkan-dev_1.0.21.0+dfsg1-1~16.04.1_amd64.deb;
|
||||
sudo pip install cpp-coveralls requests[security];
|
||||
else
|
||||
brew update; brew update;
|
||||
brew install ccache glew llvm40;
|
||||
brew install glew wxwidgets llvm36;
|
||||
fi;
|
||||
|
||||
before_script:
|
||||
- git submodule update --init asmjit 3rdparty/ffmpeg 3rdparty/pugixml 3rdparty/GSL 3rdparty/libpng Utilities/yaml-cpp 3rdparty/cereal 3rdparty/hidapi Vulkan/glslang Vulkan/Vulkan-LoaderAndValidationLayers
|
||||
- source /opt/qt59/bin/qt59-env.sh
|
||||
- mkdir build
|
||||
- cd build
|
||||
- if [ -z "$WITHOUT_LLVM" ]; then
|
||||
cmake .. -DCMAKE_INSTALL_PREFIX=/usr;
|
||||
else
|
||||
cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DWITHOUT_LLVM=ON;
|
||||
fi;
|
||||
- make -j 3
|
||||
- # AppImage generation
|
||||
- if [ "$TRAVIS_BRANCH" = "master" ] && [ "$CC" = "clang-4.0" ] && [ "$TRAVIS_PULL_REQUEST" = false ]; then
|
||||
export LD_LIBRARY_PATH=${PWD}/3rdparty/libpng:/opt/qt59/lib/:$LD_LIBRARY_PATH ;
|
||||
make DESTDIR=appdir install ; find appdir/ ;
|
||||
find ../bin ;
|
||||
wget -c "https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage" ;
|
||||
chmod a+x linuxdeployqt*.AppImage ;
|
||||
unset QTDIR; unset QT_PLUGIN_PATH ;
|
||||
./linuxdeployqt*.AppImage ./appdir/usr/share/applications/*.desktop -bundle-non-qt-libs ;
|
||||
./linuxdeployqt*.AppImage ./appdir/usr/share/applications/*.desktop -appimage ;
|
||||
find ./appdir -executable -type f -exec ldd {} \; | grep " => /usr" | cut -d " " -f 2-3 | sort | uniq ;
|
||||
zip -r9 rpcs3.zip ./RPCS3*.AppImage;
|
||||
if [ -z "$WITHOUT_LLVM" ]; then
|
||||
export LLVM="-LLVM";
|
||||
fi;
|
||||
curl ${UPLOAD_URL}${TRAVIS_COMMIT:0:7}-${TRAVIS_BUILD_NUMBER}${LLVM}_linux64 --upload-file rpcs3.zip;
|
||||
fi;
|
||||
- git submodule update --init rsx_program_decompiler asmjit 3rdparty/ffmpeg 3rdparty/pugixml 3rdparty/GSL 3rdparty/libpng Vulkan/glslang Vulkan/Vulkan-LoaderAndValidationLayers Utilities/yaml-cpp
|
||||
- mkdir build
|
||||
- cd build
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then cmake ..; else cmake .. -DLLVM_DIR=/usr/local/opt/llvm36/lib/llvm-3.6/share/llvm/cmake; fi
|
||||
|
||||
script:
|
||||
# Add a command to show all the variables. May be useful for debugging Travis.
|
||||
#- echo "--Shell Export Lists START--" ; export -p; echo "--Shell Export Lists STOP--";
|
||||
# And to ensure the versions of toolchain
|
||||
- echo "--CXX version?"; "$CXX" --version; echo "--CXX version confirmed";
|
||||
#- if [ "$COVERITY_SCAN_BRANCH" != 1 ]; then make -j 3; fi
|
||||
# Add a command to show all the variables. May be useful for debugging Travis.
|
||||
# - echo "--Shell Export Lists START--" ; export -p; echo "--Shell Export Lists STOP--";
|
||||
# And to ensure the versions of toolchain
|
||||
- echo "--CXX version?"; "$CXX" --version; echo "--CXX version confirmed";
|
||||
- if [ "$COVERITY_SCAN_BRANCH" != 1 ]; then make -j 4; fi
|
||||
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- llvm-toolchain-trusty-4.0
|
||||
- ubuntu-toolchain-r-test
|
||||
# - llvm-toolchain-trusty-3.6 temporarily disabled
|
||||
packages:
|
||||
- cmake
|
||||
- libasound2-dev
|
||||
- libopenal-dev
|
||||
- freeglut3-dev
|
||||
#- libglew-dev
|
||||
#- libvulkan1
|
||||
#- libvulkan-dev
|
||||
- libc6-dev
|
||||
- llvm-4.0
|
||||
- llvm-4.0-dev
|
||||
- clang-4.0
|
||||
- libedit-dev
|
||||
- g++-5
|
||||
- gcc-5
|
||||
- libstdc++-5-dev
|
||||
- lib32stdc++6
|
||||
- zlib1g-dev
|
||||
# We need to install qt 5.8 manually because the version trusty provides is too old.
|
||||
#- qtbase5-dev
|
||||
- libudev-dev
|
||||
- cmake
|
||||
- libopenal-dev
|
||||
- freeglut3-dev
|
||||
- libglew-dev
|
||||
- libc6-dev
|
||||
# - llvm-3.6
|
||||
# - llvm-3.6-dev
|
||||
- libedit-dev
|
||||
- g++-5
|
||||
- gcc-5
|
||||
# - clang-3.6
|
||||
- libstdc++-5-dev
|
||||
- lib32stdc++6
|
||||
- zlib1g-dev
|
||||
coverity_scan:
|
||||
project:
|
||||
name: $TRAVIS_REPO_SLUG
|
||||
description: "PS3 emulator/debugger"
|
||||
notification_email: nekotekina@gmail.com
|
||||
build_command: "make -j 3"
|
||||
notification_email: raul.tambre@gmail.com
|
||||
build_command: "make -j 4"
|
||||
branch_pattern: coverity_scan
|
||||
|
||||
after_success:
|
||||
- if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "$TRAVIS_OS_NAME" = linux ]; then coveralls --extension .c --extension .cpp --extension .h; fi
|
||||
- if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "$TRAVIS_OS_NAME" = linux ]; then coveralls --extension .c --extension .cpp --extension .h; fi
|
||||
|
||||
Vendored
-1
Submodule 3rdparty/cereal deleted from 42a45b6e15
Vendored
-1
Submodule 3rdparty/hidapi deleted from 66eed15b05
Vendored
+1
-1
Submodule 3rdparty/libpng updated: bbbcaf1122...ea77a6fd49
Vendored
-1158
File diff suppressed because it is too large
Load Diff
Vendored
-1
Submodule 3rdparty/zlib deleted from cacf7f1d4e
+2
-31
@@ -1,11 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.0.2)
|
||||
|
||||
option(WITH_GDB "WITH_GDB" OFF)
|
||||
option(WITHOUT_LLVM "WITHOUT_LLVM" OFF)
|
||||
|
||||
if (WITH_GDB)
|
||||
add_definitions(-DWITH_GDB_DEBUGGER)
|
||||
endif()
|
||||
cmake_minimum_required(VERSION 2.8.12)
|
||||
|
||||
set(ASMJIT_STATIC TRUE)
|
||||
|
||||
@@ -18,12 +11,6 @@ if(NOT CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
message( FATAL_ERROR "RPCS3 can only be compiled on 64-bit platforms." )
|
||||
endif()
|
||||
|
||||
find_program(CCACHE_FOUND ccache)
|
||||
if (CCACHE_FOUND)
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache)
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache)
|
||||
endif()
|
||||
|
||||
add_definitions(-DCMAKE_BUILD)
|
||||
|
||||
# We use libpng's static library and don't need to build the shared library and run the tests
|
||||
@@ -35,25 +22,9 @@ if (NOT USE_SYSTEM_LIBPNG)
|
||||
add_subdirectory( 3rdparty/libpng )
|
||||
endif()
|
||||
|
||||
option(VULKAN_PREBUILT "" OFF)
|
||||
|
||||
# TODO: do real installation, including copying directory structure
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE "${PROJECT_BINARY_DIR}/bin")
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG "${PROJECT_BINARY_DIR}/bin")
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${PROJECT_BINARY_DIR}/bin")
|
||||
|
||||
add_subdirectory( Vulkan )
|
||||
add_subdirectory( rpcs3 )
|
||||
|
||||
include_directories(3rdparty/hidapi/hidapi)
|
||||
if(APPLE)
|
||||
add_subdirectory(3rdparty/hidapi/mac)
|
||||
#list(APPEND LIBS hidapi)
|
||||
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
add_subdirectory(3rdparty/hidapi/linux)
|
||||
elseif(MSVC)
|
||||
add_subdirectory(3rdparty/hidapi/windows)
|
||||
else()
|
||||
add_subdirectory(3rdparty/hidapi/libusb)
|
||||
#list(APPEND LIBS hidapi-libusb)
|
||||
endif()
|
||||
add_subdirectory( rsx_program_decompiler )
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
# Getting Started
|
||||
|
||||
Before getting started using the emulator, read the [Quickstart Guide](https://rpcs3.net/quickstart) and the [FAQ](https://rpcs3.net/faq). After reading those, if you need support, visit our [Forums](http://www.emunewz.net/forum/forumdisplay.php?fid=172).
|
||||
|
||||
# Issue Reporting
|
||||
|
||||
**The GitHub Issue Tracker is not the place to ask for support or to submit [Game Compatibility](https://rpcs3.net/compatibility) reports.** Requests for support or incorrect reports will be closed. If you are not sure whether the issue you want to report is an actual issue that is not yet known, please use the forums to submit such report.
|
||||
|
||||
**Before reporting an issue:**
|
||||
- Check if your system matches all the minimum requirements listed in the [Quickstart Guide](https://rpcs3.net/quickstart);
|
||||
- Check if the issue is meaningful for the team (e.g. The Last of Us doesn't work is obvious and therefore useless);
|
||||
- Search older issues/forum threads to see if your issue was already submitted.
|
||||
- Use understandable English. It doesn't need to be perfect, but clear enough to understand your message.
|
||||
- While reporting issues, don't forget to include details about your system (OS, CPU, GPU, etc.), as well as the RPCS3.log file.
|
||||
|
||||
Submitting your test results for Commercial Games must be done on our forums' [Commercial Games](http://www.emunewz.net/forum/forumdisplay.php?fid=193) section. If there's already a thread for the game you want to report post in it, otherwise please make a new thread following the [Guidelines for submitting new games](http://www.emunewz.net/forum/showthread.php?tid=174376).
|
||||
|
||||
# Contributing
|
||||
|
||||
Check the [Coding Style Guidelines](https://github.com/RPCS3/rpcs3/wiki/Coding-Style), [Roadmap](https://github.com/RPCS3/rpcs3/wiki/Roadmap) and [Developer Information](https://github.com/RPCS3/rpcs3/wiki/Developer-Information). If you have any questions, hit us up on our [Discord Server](https://discord.me/RPCS3) in the **#development** channel.
|
||||
@@ -4,74 +4,51 @@ RPCS3
|
||||
[](https://travis-ci.org/RPCS3/rpcs3)
|
||||
[](https://ci.appveyor.com/project/rpcs3/rpcs3/branch/master)
|
||||
[](https://scan.coverity.com/projects/3960)
|
||||
[](https://coveralls.io/r/RPCS3/rpcs3)
|
||||
|
||||
The world's first open-source PlayStation 3 emulator/debugger written in C++ for Windows and Linux.
|
||||
An open-source PlayStation 3 emulator/debugger written in C++.
|
||||
|
||||
You can find some basic information in our [**website**](https://rpcs3.net/).
|
||||
For discussion about this emulator and PS3 emulation please visit our [**forums**](http://www.emunewz.net/forum/forumdisplay.php?fid=172) and our [**Discord server**](https://discord.me/RPCS3).
|
||||
|
||||
[**Support Lead Developer Nekotekina on Patreon**](https://www.patreon.com/Nekotekina)
|
||||
You can find some basic information in the [FAQ](https://github.com/RPCS3/rpcs3/wiki/FAQ). For discussion about this emulator and PS3 emulation please visit the [official forums](http://www.emunewz.net/forum/forumdisplay.php?fid=162).
|
||||
|
||||
|
||||
## Development
|
||||
### Development
|
||||
|
||||
If you want to contribute please take a look at the [Coding Style](https://github.com/RPCS3/rpcs3/wiki/Coding-Style), [Roadmap](https://github.com/RPCS3/rpcs3/wiki/Roadmap) and [Developer Information](https://github.com/RPCS3/rpcs3/wiki/Developer-Information) pages. You should as well contact any of the developers in the forums or in Discord in order to know more about the current situation of the emulator.
|
||||
If you want to contribute please take a look at the [Coding Style](https://github.com/RPCS3/rpcs3/wiki/Coding-Style), [Roadmap](https://github.com/RPCS3/rpcs3/wiki/Roadmap) and [Developer Information](https://github.com/RPCS3/rpcs3/wiki/Developer-Information) pages. You should as well contact any of the developers in the forum in order to know about the current situation of the emulator.
|
||||
|
||||
|
||||
## Dependencies
|
||||
### Dependencies
|
||||
|
||||
### Windows
|
||||
* [Visual Studio 2017](https://www.visualstudio.com/downloads/)
|
||||
* [Visual C++ Redistributable Packages for Visual Studio 2017](https://go.microsoft.com/fwlink/?LinkId=746572)
|
||||
* [Cmake 3.1.0+](https://www.cmake.org/download/) (required; add to PATH)
|
||||
__Windows__
|
||||
* [Visual Studio 2015](https://www.visualstudio.com/en-us/downloads/download-visual-studio-vs.aspx)
|
||||
* [Visual C++ Redistributable Packages for Visual Studio 2015](http://www.microsoft.com/en-us/download/details.aspx?id=48145)
|
||||
* [Cmake 3.1.0+](http://www.cmake.org/download/) (required; add to PATH)
|
||||
* [Python 3.3+](https://www.python.org/downloads/) (required; add to PATH)
|
||||
* [Qt 5.9+](https://www.qt.io/download-open-source/) (required; add QTDIR `<QtInstallFolder>\5.9\msvc2015_64\` environment variable if you do not want to use the Visual Studio Qt Plugin)
|
||||
* [Visual Studio Qt Plugin](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.QtVisualStudioTools2015) (optional; see above)
|
||||
|
||||
### Linux
|
||||
* [Qt 5.9+](https://www.qt.io/download-open-source/)
|
||||
* GCC 5.1+ or Clang 3.5.0+ ([not GCC 6.1](https://github.com/RPCS3/rpcs3/issues/1691))
|
||||
* Debian & Ubuntu: `sudo apt-get install cmake build-essential libasound2-dev libopenal-dev libglew-dev zlib1g-dev libedit-dev libvulkan-dev libudev-dev git qt5-default`
|
||||
* Arch: `sudo pacman -S glew openal cmake llvm qt5-base`
|
||||
* Fedora: `sudo dnf install cmake qt5-devel vulkan-devel libGLEW`
|
||||
__Linux__
|
||||
* GCC 5.1+ or Clang 3.5.0+
|
||||
* Debian & Ubuntu: `sudo apt-get install cmake build-essential libopenal-dev libwxgtk3.0-dev libglew-dev zlib1g-dev libedit-dev libvulkan-dev`
|
||||
* Arch: `sudo pacman -S glew openal wxgtk cmake llvm`
|
||||
|
||||
### Mac OSX
|
||||
Mac OSX is not supported at this moment because it doesn't meet system requirements (OpenGL 4.3)
|
||||
__Mac OSX__
|
||||
* Xcode 6+ (tested with Xcode 6.4)
|
||||
* Install with Homebrew: `brew install glew`
|
||||
* Install with Homebrew: `brew install glew wxwidgets`
|
||||
* Remove '-framework QuickTime' from '_ldflags' in /usr/local/bin/wx-config
|
||||
|
||||
|
||||
## Building on Windows:
|
||||
### Building
|
||||
|
||||
To initialize the repository don't forget to execute `git submodule update --init` to pull the submodules.
|
||||
|
||||
### Configuring Qt
|
||||
|
||||
*If you're using Visual Studio 2017 without Qt plugin support (or simply dont want to use it):*
|
||||
1) Add `QTDIR` environment variable and set it to `<QtInstallFolder>\5.9\msvc2017_64\` </br>
|
||||
|
||||
*If you wish to use the Visual Studio plugin for Qt:* </br>
|
||||
1) Go to the Qt5 menu and edit Qt5 options. Add the path to your Qt installation with compiler e.g. `C:\Qt\5.8\msvc2015_64`. </br>
|
||||
2) While selecting the rpcs3qt project, go to Qt5->Project Setting and select the version you added.
|
||||
|
||||
### Building the projects
|
||||
1) Build the projects in *__BUILD_BEFORE* folder: right-click on every project > *Build*. </br>
|
||||
2) Press *BUILD* > *Build Solution* or *Rebuild Solution*. </br>
|
||||
|
||||
|
||||
## Building on Linux & Mac OSX:
|
||||
|
||||
1) `git clone https://github.com/RPCS3/rpcs3.git` </br>
|
||||
2) `cd rpcs3/` </br>
|
||||
3) `git submodule update --init` </br>
|
||||
4) `cmake CMakeLists.txt && make GitVersion && make` </br>
|
||||
5) Run RPCS3 with `./bin/rpcs3` </br>
|
||||
|
||||
* __Windows__:
|
||||
1) Open the *.SLN* file.
|
||||
2) Build the projects in *__BUILD_BEFORE* folder: right-click on every project > *Build*.
|
||||
3) Press *BUILD* > *Build Solution* or *Rebuild Solution*.
|
||||
* __Linux & Mac OSX__:
|
||||
If you want to build with LLVM, then LLVM 3.8 is required.
|
||||
`cd rpcs3 && cmake CMakeLists.txt && make && cd ../` then run with `cd bin && ./rpcs3`.
|
||||
If you are on OSX and want to build with llvm don't forget to add `-DLLVM_DIR=...` (or wherever llvm brew was installed) to cmake invocation.
|
||||
|
||||
When using GDB, configure it to ignore SIGSEGV signal (`handle SIGSEGV nostop noprint`).
|
||||
|
||||
|
||||
## CMake Build Options (Linux & Mac OSX)
|
||||
##### CMake Build Options (Linux & Mac OSX)
|
||||
|
||||
- ```-DUSE_SYSTEM_LIBPNG=ON/OFF``` (default = *OFF*) </br>
|
||||
Build against the shared libpng instead of using the builtin one. libpng 1.6+ highly recommended. Try this option if you get version conflict errors or only see black game icons.
|
||||
@@ -79,7 +56,5 @@ Build against the shared libpng instead of using the builtin one. libpng 1.6+ hi
|
||||
- ```-DUSE_SYSTEM_FFMPEG=ON/OFF``` (default = *OFF*) </br>
|
||||
Build against the shared ffmpeg libraries instead of using the builtin patched version. Try this if the builtin version breaks the OpenGL renderer for you.
|
||||
|
||||
|
||||
## License
|
||||
|
||||
Most files are licensed under the terms of GNU GPLv2 License, see LICENSE file for details. Some files may be licensed differently, check appropriate file headers for details.
|
||||
### Support
|
||||
* [Donate by PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=MPJ3S9XQXCE3G)
|
||||
|
||||
+42
-103
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "Platform.h"
|
||||
|
||||
// Helper class, provides access to compiler-specific atomic intrinsics
|
||||
template<typename T, std::size_t Size>
|
||||
@@ -142,38 +143,26 @@ struct atomic_storage
|
||||
return atomic_storage<T>::sub_fetch(dest, 1);
|
||||
}
|
||||
|
||||
static inline bool test_and_set(T& dest, T mask)
|
||||
{
|
||||
return (atomic_storage<T>::fetch_or(dest, mask) & mask) != 0;
|
||||
}
|
||||
|
||||
static inline bool test_and_reset(T& dest, T mask)
|
||||
{
|
||||
return (atomic_storage<T>::fetch_and(dest, ~mask) & mask) != 0;
|
||||
}
|
||||
|
||||
static inline bool test_and_complement(T& dest, T mask)
|
||||
{
|
||||
return (atomic_storage<T>::fetch_xor(dest, mask) & mask) != 0;
|
||||
}
|
||||
|
||||
static inline bool bts(T& dest, uint bit)
|
||||
{
|
||||
return atomic_storage<T>::test_and_set(dest, static_cast<T>(1) << bit);
|
||||
const T mask = static_cast<T>(1) << bit;
|
||||
return (atomic_storage<T>::fetch_or(dest, mask) & mask) != 0;
|
||||
}
|
||||
|
||||
static inline bool btr(T& dest, uint bit)
|
||||
{
|
||||
return atomic_storage<T>::test_and_reset(dest, static_cast<T>(1) << bit);
|
||||
const T mask = static_cast<T>(1) << bit;
|
||||
return (atomic_storage<T>::fetch_and(dest, ~mask) & mask) != 0;
|
||||
}
|
||||
|
||||
static inline bool btc(T& dest, uint bit)
|
||||
{
|
||||
return atomic_storage<T>::test_and_complement(dest, static_cast<T>(1) << bit);
|
||||
const T mask = static_cast<T>(1) << bit;
|
||||
return (atomic_storage<T>::fetch_xor(dest, mask) & mask) != 0;
|
||||
}
|
||||
};
|
||||
|
||||
/* The rest: ugly MSVC intrinsics + inline asm implementations */
|
||||
/* The rest: ugly MSVC intrinsics + possibly __asm__ implementations (TODO) */
|
||||
|
||||
template<typename T>
|
||||
struct atomic_storage<T, 1> : atomic_storage<T, 0>
|
||||
@@ -296,30 +285,6 @@ struct atomic_storage<T, 2> : atomic_storage<T, 0>
|
||||
short r = _InterlockedDecrement16((volatile short*)&dest);
|
||||
return (T&)r;
|
||||
}
|
||||
#else
|
||||
static inline bool bts(T& dest, uint bit)
|
||||
{
|
||||
bool result;
|
||||
ushort _bit = (ushort)bit;
|
||||
__asm__("lock btsw %2, %0\n" "setc %1" : "+m" (dest), "=r" (result) : "Ir" (_bit) : "cc");
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline bool btr(T& dest, uint bit)
|
||||
{
|
||||
bool result;
|
||||
ushort _bit = (ushort)bit;
|
||||
__asm__("lock btrw %2, %0\n" "setc %1" : "+m" (dest), "=r" (result) : "Ir" (_bit) : "cc");
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline bool btc(T& dest, uint bit)
|
||||
{
|
||||
bool result;
|
||||
ushort _bit = (ushort)bit;
|
||||
__asm__("lock btcw %2, %0\n" "setc %1" : "+m" (dest), "=r" (result) : "Ir" (_bit) : "cc");
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -398,27 +363,6 @@ struct atomic_storage<T, 4> : atomic_storage<T, 0>
|
||||
{
|
||||
return _interlockedbittestandreset((volatile long*)&dest, bit) != 0;
|
||||
}
|
||||
#else
|
||||
static inline bool bts(T& dest, uint bit)
|
||||
{
|
||||
bool result;
|
||||
__asm__("lock btsl %2, %0\n" "setc %1" : "+m" (dest), "=r" (result) : "Ir" (bit) : "cc");
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline bool btr(T& dest, uint bit)
|
||||
{
|
||||
bool result;
|
||||
__asm__("lock btrl %2, %0\n" "setc %1" : "+m" (dest), "=r" (result) : "Ir" (bit) : "cc");
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline bool btc(T& dest, uint bit)
|
||||
{
|
||||
bool result;
|
||||
__asm__("lock btcl %2, %0\n" "setc %1" : "+m" (dest), "=r" (result) : "Ir" (bit) : "cc");
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -497,30 +441,6 @@ struct atomic_storage<T, 8> : atomic_storage<T, 0>
|
||||
{
|
||||
return _interlockedbittestandreset64((volatile llong*)&dest, bit) != 0;
|
||||
}
|
||||
#else
|
||||
static inline bool bts(T& dest, uint bit)
|
||||
{
|
||||
bool result;
|
||||
ullong _bit = bit;
|
||||
__asm__("lock btsq %2, %0\n" "setc %1" : "+m" (dest), "=r" (result) : "Ir" (_bit) : "cc");
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline bool btr(T& dest, uint bit)
|
||||
{
|
||||
bool result;
|
||||
ullong _bit = bit;
|
||||
__asm__("lock btrq %2, %0\n" "setc %1" : "+m" (dest), "=r" (result) : "Ir" (_bit) : "cc");
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline bool btc(T& dest, uint bit)
|
||||
{
|
||||
bool result;
|
||||
ullong _bit = bit;
|
||||
__asm__("lock btcq %2, %0\n" "setc %1" : "+m" (dest), "=r" (result) : "Ir" (_bit) : "cc");
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -536,7 +456,7 @@ struct atomic_storage<T, 16> : atomic_storage<T, 0>
|
||||
|
||||
static inline T load(const T& dest)
|
||||
{
|
||||
llong result[2]{0, 0};
|
||||
llong result[2];
|
||||
_InterlockedCompareExchange128((volatile llong*)&dest, 0, 0, result);
|
||||
return *(T*)+result;
|
||||
}
|
||||
@@ -558,8 +478,6 @@ struct atomic_storage<T, 16> : atomic_storage<T, 0>
|
||||
return *(T*)+cmp;
|
||||
}
|
||||
#endif
|
||||
|
||||
// TODO
|
||||
};
|
||||
|
||||
template<typename T1, typename T2, typename>
|
||||
@@ -712,16 +630,21 @@ struct atomic_test_and_set
|
||||
{
|
||||
bool operator()(T1& lhs, const T2& rhs) const
|
||||
{
|
||||
return test_and_set(lhs, rhs);
|
||||
return lhs.test_and_set(rhs);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T1, typename T2>
|
||||
struct atomic_test_and_set<T1, T2, std::enable_if_t<std::is_integral<T1>::value && std::is_convertible<T2, T1>::value>>
|
||||
{
|
||||
static constexpr auto fetch_op = &atomic_storage<T1>::test_and_set;
|
||||
static constexpr auto op_fetch = &atomic_storage<T1>::test_and_set;
|
||||
static constexpr auto atomic_op = &atomic_storage<T1>::test_and_set;
|
||||
static inline bool op(T1& lhs, const T2& rhs)
|
||||
{
|
||||
return (atomic_storage<T1>::fetch_or(lhs, rhs) & rhs) != 0;
|
||||
}
|
||||
|
||||
static constexpr auto fetch_op = &op;
|
||||
static constexpr auto op_fetch = &op;
|
||||
static constexpr auto atomic_op = &op;
|
||||
};
|
||||
|
||||
template<typename T1, typename T2, typename>
|
||||
@@ -729,16 +652,21 @@ struct atomic_test_and_reset
|
||||
{
|
||||
bool operator()(T1& lhs, const T2& rhs) const
|
||||
{
|
||||
return test_and_reset(lhs, rhs);
|
||||
return lhs.test_and_reset(rhs);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T1, typename T2>
|
||||
struct atomic_test_and_reset<T1, T2, std::enable_if_t<std::is_integral<T1>::value && std::is_convertible<T2, T1>::value>>
|
||||
{
|
||||
static constexpr auto fetch_op = &atomic_storage<T1>::test_and_reset;
|
||||
static constexpr auto op_fetch = &atomic_storage<T1>::test_and_reset;
|
||||
static constexpr auto atomic_op = &atomic_storage<T1>::test_and_reset;
|
||||
static inline bool op(T1& lhs, const T2& rhs)
|
||||
{
|
||||
return (atomic_storage<T1>::fetch_and(lhs, ~rhs) & rhs) != 0;
|
||||
}
|
||||
|
||||
static constexpr auto fetch_op = &op;
|
||||
static constexpr auto op_fetch = &op;
|
||||
static constexpr auto atomic_op = &op;
|
||||
};
|
||||
|
||||
template<typename T1, typename T2, typename>
|
||||
@@ -746,16 +674,21 @@ struct atomic_test_and_complement
|
||||
{
|
||||
bool operator()(T1& lhs, const T2& rhs) const
|
||||
{
|
||||
return test_and_complement(lhs, rhs);
|
||||
return lhs.test_and_complement(rhs);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T1, typename T2>
|
||||
struct atomic_test_and_complement<T1, T2, std::enable_if_t<std::is_integral<T1>::value && std::is_convertible<T2, T1>::value>>
|
||||
{
|
||||
static constexpr auto fetch_op = &atomic_storage<T1>::test_and_complement;
|
||||
static constexpr auto op_fetch = &atomic_storage<T1>::test_and_complement;
|
||||
static constexpr auto atomic_op = &atomic_storage<T1>::test_and_complement;
|
||||
static inline bool op(T1& lhs, const T2& rhs)
|
||||
{
|
||||
return (atomic_storage<T1>::fetch_xor(lhs, rhs) & rhs) != 0;
|
||||
}
|
||||
|
||||
static constexpr auto fetch_op = &op;
|
||||
static constexpr auto op_fetch = &op;
|
||||
static constexpr auto atomic_op = &op;
|
||||
};
|
||||
|
||||
// Atomic type with lock-free and standard layout guarantees (and appropriate limitations)
|
||||
@@ -1021,6 +954,12 @@ public:
|
||||
return atomic_op(atomic_post_dec<type>{});
|
||||
}
|
||||
|
||||
template<typename T2 = T>
|
||||
auto test(const T2& rhs) const
|
||||
{
|
||||
return load().test(rhs);
|
||||
}
|
||||
|
||||
template<typename T2 = T>
|
||||
auto test_and_set(const T2& rhs)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
#include "stdafx.h"
|
||||
#include "Config.h"
|
||||
#include "Emu/System.h"
|
||||
#include "AutoPause.h"
|
||||
|
||||
cfg::bool_entry g_cfg_debug_autopause_syscall(cfg::root.misc, "Auto Pause at System Call");
|
||||
cfg::bool_entry g_cfg_debug_autopause_func_call(cfg::root.misc, "Auto Pause at Function Call");
|
||||
|
||||
debug::autopause& debug::autopause::get_instance()
|
||||
{
|
||||
// Use magic static
|
||||
static autopause instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
// Load Auto Pause Configuration from file "pause.bin"
|
||||
void debug::autopause::reload(void)
|
||||
{
|
||||
auto& instance = get_instance();
|
||||
|
||||
instance.m_pause_function.clear();
|
||||
instance.m_pause_syscall.clear();
|
||||
|
||||
// TODO: better format, possibly a config entry
|
||||
if (fs::file list{ fs::get_config_dir() + "pause.bin" })
|
||||
{
|
||||
u32 num;
|
||||
size_t fmax = list.size();
|
||||
size_t fcur = 0;
|
||||
list.seek(0);
|
||||
while (fcur <= fmax - sizeof(u32))
|
||||
{
|
||||
list.read(&num, sizeof(u32));
|
||||
fcur += sizeof(u32);
|
||||
if (num == 0xFFFFFFFF) break;
|
||||
|
||||
if (num < 1024)
|
||||
{
|
||||
instance.m_pause_syscall.emplace(num);
|
||||
LOG_WARNING(HLE, "Set autopause at syscall %lld", num);
|
||||
}
|
||||
else
|
||||
{
|
||||
instance.m_pause_function.emplace(num);
|
||||
LOG_WARNING(HLE, "Set autopause at function 0x%08x", num);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool debug::autopause::pause_syscall(u64 code)
|
||||
{
|
||||
if (g_cfg_debug_autopause_syscall && get_instance().m_pause_syscall.count(code) != 0)
|
||||
{
|
||||
Emu.Pause();
|
||||
LOG_SUCCESS(HLE, "Autopause triggered at syscall %lld", code);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool debug::autopause::pause_function(u32 code)
|
||||
{
|
||||
if (g_cfg_debug_autopause_func_call && get_instance().m_pause_function.count(code) != 0)
|
||||
{
|
||||
Emu.Pause();
|
||||
LOG_SUCCESS(HLE, "Autopause triggered at function 0x%08x", code);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
// Regarded as a Debugger Enchantment
|
||||
namespace debug
|
||||
{
|
||||
// To store the pause function/call id, and let those pause there.
|
||||
// Would be with a GUI to configure those.
|
||||
class autopause
|
||||
{
|
||||
std::unordered_set<u64> m_pause_syscall;
|
||||
std::unordered_set<u32> m_pause_function;
|
||||
|
||||
static autopause& get_instance();
|
||||
public:
|
||||
|
||||
static void reload();
|
||||
static bool pause_syscall(u64 code);
|
||||
static bool pause_function(u32 code);
|
||||
};
|
||||
}
|
||||
+361
-246
@@ -1,58 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "Platform.h"
|
||||
|
||||
// 128-bit vector type and also se_storage<> storage type
|
||||
union alignas(16) v128
|
||||
{
|
||||
char _bytes[16];
|
||||
|
||||
template <typename T, std::size_t N, std::size_t M>
|
||||
template<typename T, std::size_t N, std::size_t M>
|
||||
struct masked_array_t // array type accessed as (index ^ M)
|
||||
{
|
||||
T m_data[N];
|
||||
|
||||
public:
|
||||
T& operator[](std::size_t index)
|
||||
T& operator [](std::size_t index)
|
||||
{
|
||||
return m_data[index ^ M];
|
||||
}
|
||||
|
||||
const T& operator[](std::size_t index) const
|
||||
const T& operator [](std::size_t index) const
|
||||
{
|
||||
return m_data[index ^ M];
|
||||
}
|
||||
};
|
||||
|
||||
#if IS_LE_MACHINE == 1
|
||||
template <typename T, std::size_t N = 16 / sizeof(T)>
|
||||
using normal_array_t = masked_array_t<T, N, 0>;
|
||||
template <typename T, std::size_t N = 16 / sizeof(T)>
|
||||
using reversed_array_t = masked_array_t<T, N, N - 1>;
|
||||
template<typename T, std::size_t N = 16 / sizeof(T)> using normal_array_t = masked_array_t<T, N, 0>;
|
||||
template<typename T, std::size_t N = 16 / sizeof(T)> using reversed_array_t = masked_array_t<T, N, N - 1>;
|
||||
#endif
|
||||
|
||||
normal_array_t<u64> _u64;
|
||||
normal_array_t<s64> _s64;
|
||||
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;
|
||||
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;
|
||||
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<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;
|
||||
normal_array_t<f32> _f;
|
||||
normal_array_t<f64> _d;
|
||||
reversed_array_t<f32> fr;
|
||||
reversed_array_t<f64> dr;
|
||||
|
||||
@@ -82,7 +80,7 @@ union alignas(16) v128
|
||||
return (data & mask) != 0;
|
||||
}
|
||||
|
||||
bit_element& operator=(const bool right)
|
||||
bit_element& operator =(const bool right)
|
||||
{
|
||||
if (right)
|
||||
{
|
||||
@@ -95,7 +93,7 @@ union alignas(16) v128
|
||||
return *this;
|
||||
}
|
||||
|
||||
bit_element& operator=(const bit_element& right)
|
||||
bit_element& operator =(const bit_element& right)
|
||||
{
|
||||
if (right)
|
||||
{
|
||||
@@ -110,7 +108,7 @@ union alignas(16) v128
|
||||
};
|
||||
|
||||
// Index 0 returns the MSB and index 127 returns the LSB
|
||||
bit_element operator[](u32 index)
|
||||
bit_element operator [](u32 index)
|
||||
{
|
||||
#if IS_LE_MACHINE == 1
|
||||
return bit_element(m_data[1 - (index >> 6)], 0x8000000000000000ull >> (index & 0x3F));
|
||||
@@ -118,13 +116,14 @@ union alignas(16) v128
|
||||
}
|
||||
|
||||
// Index 0 returns the MSB and index 127 returns the LSB
|
||||
bool operator[](u32 index) const
|
||||
bool operator [](u32 index) const
|
||||
{
|
||||
#if IS_LE_MACHINE == 1
|
||||
return (m_data[1 - (index >> 6)] & (0x8000000000000000ull >> (index & 0x3F))) != 0;
|
||||
#endif
|
||||
}
|
||||
} _bit;
|
||||
}
|
||||
_bit;
|
||||
|
||||
static v128 from64(u64 _0, u64 _1 = 0)
|
||||
{
|
||||
@@ -278,12 +277,12 @@ union alignas(16) v128
|
||||
return fromV(_mm_cmpeq_epi32(left.vi, right.vi));
|
||||
}
|
||||
|
||||
bool operator==(const v128& right) const
|
||||
bool operator ==(const v128& right) const
|
||||
{
|
||||
return _u64[0] == right._u64[0] && _u64[1] == right._u64[1];
|
||||
}
|
||||
|
||||
bool operator!=(const v128& right) const
|
||||
bool operator !=(const v128& right) const
|
||||
{
|
||||
return _u64[0] != right._u64[0] || _u64[1] != right._u64[1];
|
||||
}
|
||||
@@ -299,79 +298,43 @@ union alignas(16) v128
|
||||
_u64[0] = 0;
|
||||
_u64[1] = 0;
|
||||
}
|
||||
|
||||
std::string to_hex() const;
|
||||
|
||||
std::string to_xyzw() const;
|
||||
};
|
||||
|
||||
template <typename T, std::size_t N, std::size_t M>
|
||||
struct offset32_array<v128::masked_array_t<T, N, M>>
|
||||
{
|
||||
template <typename Arg>
|
||||
static inline u32 index32(const Arg& arg)
|
||||
{
|
||||
return SIZE_32(T) * (static_cast<u32>(arg) ^ static_cast<u32>(M));
|
||||
}
|
||||
};
|
||||
|
||||
inline v128 operator|(const v128& left, const v128& right)
|
||||
inline v128 operator |(const v128& left, const v128& right)
|
||||
{
|
||||
return v128::fromV(_mm_or_si128(left.vi, right.vi));
|
||||
}
|
||||
|
||||
inline v128 operator&(const v128& left, const v128& right)
|
||||
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)
|
||||
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)
|
||||
inline v128 operator ~(const v128& other)
|
||||
{
|
||||
return v128::from64(~other._u64[0], ~other._u64[1]);
|
||||
}
|
||||
|
||||
template <typename T, std::size_t Align, std::size_t Size>
|
||||
#define IS_INTEGER(t) (std::is_integral<t>::value || std::is_enum<t>::value)
|
||||
#define IS_BINARY_COMPARABLE(t1, t2) (IS_INTEGER(t1) && IS_INTEGER(t2) && sizeof(t1) == sizeof(t2))
|
||||
|
||||
template<typename T, std::size_t Size = sizeof(T)>
|
||||
struct se_storage
|
||||
{
|
||||
using type = std::aligned_storage_t<Size, Align>;
|
||||
|
||||
// Unoptimized generic byteswap for unaligned data
|
||||
static void reverse(u8* dst, const u8* src)
|
||||
{
|
||||
for (std::size_t i = 0; i < Size; i++)
|
||||
{
|
||||
dst[i] = src[Size - 1 - i];
|
||||
}
|
||||
}
|
||||
|
||||
static type to(const T& src)
|
||||
{
|
||||
type result;
|
||||
reverse(reinterpret_cast<u8*>(&result), reinterpret_cast<const u8*>(&src));
|
||||
return result;
|
||||
}
|
||||
|
||||
static T from(const type& src)
|
||||
{
|
||||
T result;
|
||||
reverse(reinterpret_cast<u8*>(&result), reinterpret_cast<const u8*>(&src));
|
||||
return result;
|
||||
}
|
||||
|
||||
static type copy(const type& src)
|
||||
{
|
||||
type result;
|
||||
for (std::size_t i = 0; i < Size; i++)
|
||||
{
|
||||
reinterpret_cast<u8*>(&result)[i] = reinterpret_cast<const u8*>(&src)[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
static_assert(!Size, "Bad se_storage<> type");
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct se_storage<T, 2, 2>
|
||||
template<typename T>
|
||||
struct se_storage<T, 2>
|
||||
{
|
||||
using type = u16;
|
||||
|
||||
@@ -394,15 +357,10 @@ struct se_storage<T, 2, 2>
|
||||
const u16 result = swap(src);
|
||||
return reinterpret_cast<const T&>(result);
|
||||
}
|
||||
|
||||
static inline T copy(const T& src)
|
||||
{
|
||||
return src;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct se_storage<T, 4, 4>
|
||||
template<typename T>
|
||||
struct se_storage<T, 4>
|
||||
{
|
||||
using type = u32;
|
||||
|
||||
@@ -425,15 +383,10 @@ struct se_storage<T, 4, 4>
|
||||
const u32 result = swap(src);
|
||||
return reinterpret_cast<const T&>(result);
|
||||
}
|
||||
|
||||
static inline T copy(const T& src)
|
||||
{
|
||||
return src;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct se_storage<T, 8, 8>
|
||||
template<typename T>
|
||||
struct se_storage<T, 8>
|
||||
{
|
||||
using type = u64;
|
||||
|
||||
@@ -456,15 +409,10 @@ struct se_storage<T, 8, 8>
|
||||
const u64 result = swap(src);
|
||||
return reinterpret_cast<const T&>(result);
|
||||
}
|
||||
|
||||
static inline T copy(const T& src)
|
||||
{
|
||||
return src;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct se_storage<T, 16, 16>
|
||||
template<typename T>
|
||||
struct se_storage<T, 16>
|
||||
{
|
||||
using type = v128;
|
||||
|
||||
@@ -483,20 +431,43 @@ struct se_storage<T, 16, 16>
|
||||
const v128 result = swap(src);
|
||||
return reinterpret_cast<const T&>(result);
|
||||
}
|
||||
};
|
||||
|
||||
static inline T copy(const T& src)
|
||||
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 src;
|
||||
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;
|
||||
|
||||
// Switched endianness
|
||||
template <typename T, std::size_t Align>
|
||||
class se_t<T, true, Align>
|
||||
template<typename T>
|
||||
class se_t<T, true>
|
||||
{
|
||||
using type = typename std::remove_cv<T>::type;
|
||||
using stype = typename se_storage<type, Align>::type;
|
||||
using storage = se_storage<type, Align>;
|
||||
using stype = se_storage_t<type>;
|
||||
using storage = se_storage<type>;
|
||||
|
||||
stype m_data;
|
||||
|
||||
@@ -505,6 +476,24 @@ class se_t<T, true, Align>
|
||||
static_assert(!std::is_array<type>::value, "se_t<> error: invalid type (array)");
|
||||
static_assert(sizeof(type) == alignof(type), "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;
|
||||
|
||||
@@ -514,15 +503,27 @@ public:
|
||||
: 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);
|
||||
}
|
||||
|
||||
se_t& operator=(const se_t&) = default;
|
||||
// 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)
|
||||
se_t& operator =(type value)
|
||||
{
|
||||
return m_data = storage::to(value), *this;
|
||||
}
|
||||
@@ -533,127 +534,181 @@ public:
|
||||
{
|
||||
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<std::is_integral<T>::value && 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<std::is_integral<T>::value && 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<std::is_integral<T>::value && std::is_convertible<CT, T>::value, se_t&> operator ^=(CT right)
|
||||
{
|
||||
return m_data ^= storage::to(right), *this;
|
||||
}
|
||||
};
|
||||
|
||||
// Native endianness
|
||||
template <typename T, std::size_t Align>
|
||||
class se_t<T, false, Align>
|
||||
template<typename T>
|
||||
class se_t<T, false>
|
||||
{
|
||||
using type = typename std::remove_cv<T>::type;
|
||||
using stype = typename se_storage<type, Align>::type;
|
||||
using storage = se_storage<type, Align>;
|
||||
|
||||
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(sizeof(type) == alignof(type), "se_t<> error: unexpected alignment");
|
||||
|
||||
stype m_data;
|
||||
type m_data;
|
||||
|
||||
public:
|
||||
se_t() = default;
|
||||
|
||||
se_t(type value)
|
||||
: m_data(reinterpret_cast<const stype&>(value))
|
||||
constexpr se_t(type value)
|
||||
: m_data(value)
|
||||
{
|
||||
}
|
||||
|
||||
type value() const
|
||||
// Construct directly from raw data (don't use)
|
||||
constexpr se_t(const type& raw_value, const se_raw_tag_t&)
|
||||
: m_data(raw_value)
|
||||
{
|
||||
return storage::copy(reinterpret_cast<const type&>(m_data));
|
||||
}
|
||||
|
||||
se_t& operator=(const se_t& value) = default;
|
||||
|
||||
se_t& operator=(type value)
|
||||
constexpr type value() const
|
||||
{
|
||||
return m_data = reinterpret_cast<const stype&>(value), *this;
|
||||
return m_data;
|
||||
}
|
||||
|
||||
// Access underlying raw data (don't use)
|
||||
constexpr const type& raw_data() const noexcept
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
|
||||
se_t& operator =(const se_t& value) = default;
|
||||
|
||||
se_t& operator =(type value)
|
||||
{
|
||||
return m_data = value, *this;
|
||||
}
|
||||
|
||||
using simple_type = simple_t<T>;
|
||||
|
||||
operator type() const
|
||||
constexpr operator type() const
|
||||
{
|
||||
return storage::copy(reinterpret_cast<const type&>(m_data));
|
||||
return m_data;
|
||||
}
|
||||
|
||||
template<typename CT>
|
||||
std::enable_if_t<std::is_integral<T>::value && std::is_convertible<CT, T>::value, se_t&> operator &=(const CT& right)
|
||||
{
|
||||
return m_data &= right, *this;
|
||||
}
|
||||
|
||||
template<typename CT>
|
||||
std::enable_if_t<std::is_integral<T>::value && std::is_convertible<CT, T>::value, se_t&> operator |=(const CT& right)
|
||||
{
|
||||
return m_data |= right, *this;
|
||||
}
|
||||
|
||||
template<typename CT>
|
||||
std::enable_if_t<std::is_integral<T>::value && std::is_convertible<CT, T>::value, se_t&> operator ^=(const CT& right)
|
||||
{
|
||||
return m_data ^= right, *this;
|
||||
}
|
||||
};
|
||||
|
||||
// se_t<> with native endianness
|
||||
template <typename T, std::size_t Align = alignof(T)>
|
||||
using nse_t = se_t<T, false, Align>;
|
||||
// se_t with native endianness (alias)
|
||||
template<typename T> using nse_t = se_t<T, false>;
|
||||
|
||||
template <typename T, bool Se, std::size_t Align, typename T1>
|
||||
inline se_t<T, Se, Align>& operator+=(se_t<T, Se, Align>& left, const T1& right)
|
||||
template<typename T, bool Se, typename T1>
|
||||
inline se_t<T, Se>& operator +=(se_t<T, Se>& left, const T1& right)
|
||||
{
|
||||
auto value = left.value();
|
||||
return left = (value += right);
|
||||
}
|
||||
|
||||
template <typename T, bool Se, std::size_t Align, typename T1>
|
||||
inline se_t<T, Se, Align>& operator-=(se_t<T, Se, Align>& left, const T1& right)
|
||||
template<typename T, bool Se, typename T1>
|
||||
inline se_t<T, Se>& operator -=(se_t<T, Se>& left, const T1& right)
|
||||
{
|
||||
auto value = left.value();
|
||||
return left = (value -= right);
|
||||
}
|
||||
|
||||
template <typename T, bool Se, std::size_t Align, typename T1>
|
||||
inline se_t<T, Se, Align>& operator*=(se_t<T, Se, Align>& left, const T1& right)
|
||||
template<typename T, bool Se, typename T1>
|
||||
inline se_t<T, Se>& operator *=(se_t<T, Se>& left, const T1& right)
|
||||
{
|
||||
auto value = left.value();
|
||||
return left = (value *= right);
|
||||
}
|
||||
|
||||
template <typename T, bool Se, std::size_t Align, typename T1>
|
||||
inline se_t<T, Se, Align>& operator/=(se_t<T, Se, Align>& left, const T1& right)
|
||||
template<typename T, bool Se, typename T1>
|
||||
inline se_t<T, Se>& operator /=(se_t<T, Se>& left, const T1& right)
|
||||
{
|
||||
auto value = left.value();
|
||||
return left = (value /= right);
|
||||
}
|
||||
|
||||
template <typename T, bool Se, std::size_t Align, typename T1>
|
||||
inline se_t<T, Se, Align>& operator%=(se_t<T, Se, Align>& left, const T1& right)
|
||||
template<typename T, bool Se, typename T1>
|
||||
inline se_t<T, Se>& operator %=(se_t<T, Se>& left, const T1& right)
|
||||
{
|
||||
auto value = left.value();
|
||||
return left = (value %= right);
|
||||
}
|
||||
|
||||
template <typename T, bool Se, std::size_t Align, typename T1>
|
||||
inline se_t<T, Se, Align>& operator&=(se_t<T, Se, Align>& left, const T1& right)
|
||||
{
|
||||
auto value = left.value();
|
||||
return left = (value &= right);
|
||||
}
|
||||
|
||||
template <typename T, bool Se, std::size_t Align, typename T1>
|
||||
inline se_t<T, Se, Align>& operator|=(se_t<T, Se, Align>& left, const T1& right)
|
||||
{
|
||||
auto value = left.value();
|
||||
return left = (value |= right);
|
||||
}
|
||||
|
||||
template <typename T, bool Se, std::size_t Align, typename T1>
|
||||
inline se_t<T, Se, Align>& operator^=(se_t<T, Se, Align>& left, const T1& right)
|
||||
{
|
||||
auto value = left.value();
|
||||
return left = (value ^= right);
|
||||
}
|
||||
|
||||
template <typename T, bool Se, std::size_t Align, typename T1>
|
||||
inline se_t<T, Se, Align>& operator<<=(se_t<T, Se, Align>& left, const T1& right)
|
||||
template<typename T, bool Se, typename T1>
|
||||
inline se_t<T, Se>& operator <<=(se_t<T, Se>& left, const T1& right)
|
||||
{
|
||||
auto value = left.value();
|
||||
return left = (value <<= right);
|
||||
}
|
||||
|
||||
template <typename T, bool Se, std::size_t Align, typename T1>
|
||||
inline se_t<T, Se, Align>& operator>>=(se_t<T, Se, Align>& left, const T1& right)
|
||||
template<typename T, bool Se, typename T1>
|
||||
inline se_t<T, Se>& operator >>=(se_t<T, Se>& left, const T1& right)
|
||||
{
|
||||
auto value = left.value();
|
||||
return left = (value >>= right);
|
||||
}
|
||||
|
||||
template <typename T, bool Se, std::size_t Align>
|
||||
inline se_t<T, Se, Align> operator++(se_t<T, Se, Align>& left, int)
|
||||
template<typename T, bool Se>
|
||||
inline se_t<T, Se> operator ++(se_t<T, Se>& left, int)
|
||||
{
|
||||
auto value = left.value();
|
||||
auto result = value++;
|
||||
@@ -661,8 +716,8 @@ inline se_t<T, Se, Align> operator++(se_t<T, Se, Align>& left, int)
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T, bool Se, std::size_t Align>
|
||||
inline se_t<T, Se, Align> operator--(se_t<T, Se, Align>& left, int)
|
||||
template<typename T, bool Se>
|
||||
inline se_t<T, Se> operator --(se_t<T, Se>& left, int)
|
||||
{
|
||||
auto value = left.value();
|
||||
auto result = value--;
|
||||
@@ -670,111 +725,173 @@ inline se_t<T, Se, Align> operator--(se_t<T, Se, Align>& left, int)
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T, bool Se, std::size_t Align>
|
||||
inline se_t<T, Se, Align>& operator++(se_t<T, Se, Align>& right)
|
||||
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, std::size_t Align>
|
||||
inline se_t<T, Se, Align>& operator--(se_t<T, Se, Align>& right)
|
||||
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<std::is_integral<T1>::value && IS_INTEGER(T2) && sizeof(T1) >= sizeof(T2), bool> operator ==(const se_t<T1>& left, T2 right)
|
||||
{
|
||||
return left.raw_data() == se_storage<T1>::to(right);
|
||||
}
|
||||
|
||||
// Optimization
|
||||
template<typename T1, typename T2>
|
||||
inline std::enable_if_t<IS_INTEGER(T1) && std::is_integral<T2>::value && sizeof(T1) <= sizeof(T2), bool> operator ==(T1 left, const se_t<T2>& right)
|
||||
{
|
||||
return se_storage<T2>::to(left) == right.raw_data();
|
||||
}
|
||||
|
||||
// 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<std::is_integral<T1>::value && IS_INTEGER(T2) && sizeof(T1) >= sizeof(T2), bool> operator !=(const se_t<T1>& left, T2 right)
|
||||
{
|
||||
return left.raw_data() != se_storage<T1>::to(right);
|
||||
}
|
||||
|
||||
// Optimization
|
||||
template<typename T1, typename T2>
|
||||
inline std::enable_if_t<IS_INTEGER(T1) && std::is_integral<T2>::value && sizeof(T1) <= sizeof(T2), bool> operator !=(T1 left, const se_t<T2>& right)
|
||||
{
|
||||
return se_storage<T2>::to(left) != right.raw_data();
|
||||
}
|
||||
|
||||
// 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<std::is_integral<T1>::value && IS_INTEGER(T2) && sizeof(T1) >= sizeof(T2) && sizeof(T1) >= 4, se_t<decltype(T1() & T2())>> operator &(const se_t<T1>& left, T2 right)
|
||||
{
|
||||
return{ left.raw_data() & se_storage<T1>::to(right), se_raw };
|
||||
}
|
||||
|
||||
// Optimization
|
||||
template<typename T1, typename T2>
|
||||
inline std::enable_if_t<IS_INTEGER(T1) && std::is_integral<T2>::value && sizeof(T1) <= sizeof(T2) && sizeof(T2) >= 4, se_t<decltype(T1() & T2())>> operator &(T1 left, const se_t<T2>& right)
|
||||
{
|
||||
return{ se_storage<T2>::to(left) & right.raw_data(), se_raw };
|
||||
}
|
||||
|
||||
// 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<std::is_integral<T1>::value && IS_INTEGER(T2) && sizeof(T1) >= sizeof(T2) && sizeof(T1) >= 4, se_t<decltype(T1() | T2())>> operator |(const se_t<T1>& left, T2 right)
|
||||
{
|
||||
return{ left.raw_data() | se_storage<T1>::to(right), se_raw };
|
||||
}
|
||||
|
||||
// Optimization
|
||||
template<typename T1, typename T2>
|
||||
inline std::enable_if_t<IS_INTEGER(T1) && std::is_integral<T2>::value && sizeof(T1) <= sizeof(T2) && sizeof(T2) >= 4, se_t<decltype(T1() | T2())>> operator |(T1 left, const se_t<T2>& right)
|
||||
{
|
||||
return{ se_storage<T2>::to(left) | right.raw_data(), se_raw };
|
||||
}
|
||||
|
||||
// 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<std::is_integral<T1>::value && IS_INTEGER(T2) && sizeof(T1) >= sizeof(T2) && sizeof(T1) >= 4, se_t<decltype(T1() ^ T2())>> operator ^(const se_t<T1>& left, T2 right)
|
||||
{
|
||||
return{ left.raw_data() ^ se_storage<T1>::to(right), se_raw };
|
||||
}
|
||||
|
||||
// Optimization
|
||||
template<typename T1, typename T2>
|
||||
inline std::enable_if_t<IS_INTEGER(T1) && std::is_integral<T2>::value && sizeof(T1) <= sizeof(T2) && sizeof(T2) >= 4, se_t<decltype(T1() ^ T2())>> operator ^(T1 left, const se_t<T2>& right)
|
||||
{
|
||||
return{ se_storage<T2>::to(left) ^ right.raw_data(), se_raw };
|
||||
}
|
||||
|
||||
// Optimization
|
||||
template<typename T>
|
||||
inline std::enable_if_t<std::is_integral<T>::value && sizeof(T) >= 4, se_t<decltype(~T())>> operator ~(const se_t<T>& right)
|
||||
{
|
||||
return{ ~right.raw_data(), se_raw };
|
||||
}
|
||||
|
||||
#if IS_LE_MACHINE == 1
|
||||
template <typename T, std::size_t Align = alignof(T)>
|
||||
using be_t = se_t<T, true, Align>;
|
||||
template <typename T, std::size_t Align = alignof(T)>
|
||||
using le_t = se_t<T, false, Align>;
|
||||
template<typename T> using be_t = se_t<T, true>;
|
||||
template<typename T> using le_t = se_t<T, false>;
|
||||
#endif
|
||||
|
||||
// Type converter: converts native endianness arithmetic/enum types to appropriate se_t<> type
|
||||
template <typename T, bool Se, typename = void>
|
||||
template<typename T, bool Se, typename = void>
|
||||
struct to_se
|
||||
{
|
||||
template <typename T2, typename = void>
|
||||
struct to_se_
|
||||
{
|
||||
using type = T2;
|
||||
};
|
||||
|
||||
template <typename T2>
|
||||
struct to_se_<T2, std::enable_if_t<std::is_arithmetic<T2>::value || std::is_enum<T2>::value>>
|
||||
{
|
||||
using type = se_t<T2, Se>;
|
||||
};
|
||||
|
||||
// Convert arithmetic and enum types
|
||||
using type = typename to_se_<T>::type;
|
||||
using type = typename std::conditional<std::is_arithmetic<T>::value || std::is_enum<T>::value, se_t<T, Se>, T>::type;
|
||||
};
|
||||
|
||||
template <bool Se>
|
||||
struct to_se<v128, Se>
|
||||
{
|
||||
using type = se_t<v128, Se>;
|
||||
};
|
||||
template<bool Se> struct to_se<v128, Se> { using type = se_t<v128, Se>; };
|
||||
template<bool Se> struct to_se<bool, Se> { using type = bool; };
|
||||
template<bool Se> struct to_se<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; };
|
||||
|
||||
template <bool Se>
|
||||
struct to_se<u128, Se>
|
||||
{
|
||||
using type = se_t<u128, Se>;
|
||||
};
|
||||
|
||||
template <bool Se>
|
||||
struct to_se<s128, Se>
|
||||
{
|
||||
using type = se_t<s128, 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;
|
||||
};
|
||||
|
||||
template <typename T, bool Se>
|
||||
struct to_se<const T, Se, std::enable_if_t<!std::is_array<T>::value>>
|
||||
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>
|
||||
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>
|
||||
template<typename T, bool Se>
|
||||
struct to_se<T[], Se>
|
||||
{
|
||||
// Move array qualifier
|
||||
using type = typename to_se<T, Se>::type[];
|
||||
};
|
||||
|
||||
template <typename T, bool Se, std::size_t N>
|
||||
template<typename T, bool Se, std::size_t N>
|
||||
struct to_se<T[N], Se>
|
||||
{
|
||||
// Move array qualifier
|
||||
@@ -783,27 +900,25 @@ struct to_se<T[N], Se>
|
||||
|
||||
// BE/LE aliases for to_se<>
|
||||
#if IS_LE_MACHINE == 1
|
||||
template <typename T>
|
||||
using to_be_t = typename to_se<T, true>::type;
|
||||
template <typename T>
|
||||
using to_le_t = typename to_se<T, false>::type;
|
||||
template<typename T> using to_be_t = typename to_se<T, true>::type;
|
||||
template<typename T> using to_le_t = typename to_se<T, false>::type;
|
||||
#endif
|
||||
|
||||
// BE/LE aliases for atomic_t
|
||||
#if IS_LE_MACHINE == 1
|
||||
template <typename T>
|
||||
using atomic_be_t = atomic_t<be_t<T>>;
|
||||
template <typename T>
|
||||
using atomic_le_t = atomic_t<le_t<T>>;
|
||||
template<typename T> using atomic_be_t = atomic_t<be_t<T>>;
|
||||
template<typename T> using atomic_le_t = atomic_t<le_t<T>>;
|
||||
#endif
|
||||
|
||||
template <typename T, bool Se, std::size_t Align>
|
||||
struct fmt_unveil<se_t<T, Se, Align>, void>
|
||||
// Formatting for BE/LE data
|
||||
template<typename T, bool Se>
|
||||
struct unveil<se_t<T, Se>, void>
|
||||
{
|
||||
using type = typename fmt_unveil<T>::type;
|
||||
|
||||
static inline auto get(const se_t<T, Se, Align>& arg)
|
||||
static inline auto get(const se_t<T, Se>& arg)
|
||||
{
|
||||
return fmt_unveil<T>::get(arg);
|
||||
return unveil<T>::get(arg);
|
||||
}
|
||||
};
|
||||
|
||||
#undef IS_BINARY_COMPARABLE
|
||||
#undef IS_INTEGER
|
||||
|
||||
+19
-58
@@ -7,19 +7,15 @@ struct bf_base
|
||||
{
|
||||
using type = T;
|
||||
using vtype = simple_t<type>;
|
||||
using utype = typename std::make_unsigned<vtype>::type;
|
||||
|
||||
// Datatype bitsize
|
||||
static constexpr uint bitmax = sizeof(T) * 8; static_assert(N - 1 < bitmax, "bf_base<> error: N out of bounds");
|
||||
static constexpr uint bitmax = sizeof(T) * CHAR_BIT; static_assert(N - 1 < bitmax, "bf_base<> error: N out of bounds");
|
||||
|
||||
// Field bitsize
|
||||
static constexpr uint bitsize = N;
|
||||
|
||||
// Value mask
|
||||
static constexpr utype vmask = static_cast<utype>(~utype{} >> (bitmax - bitsize));
|
||||
|
||||
// All ones mask
|
||||
static constexpr utype mask1 = static_cast<utype>(~utype{});
|
||||
static constexpr vtype vmask = static_cast<vtype>(~std::make_unsigned_t<vtype>{} >> (bitmax - bitsize));
|
||||
|
||||
protected:
|
||||
type m_data;
|
||||
@@ -31,15 +27,14 @@ struct bf_t : bf_base<T, N>
|
||||
{
|
||||
using type = typename bf_t::type;
|
||||
using vtype = typename bf_t::vtype;
|
||||
using utype = typename bf_t::utype;
|
||||
|
||||
// Field offset
|
||||
static constexpr uint bitpos = I; static_assert(bitpos + N <= bf_t::bitmax, "bf_t<> error: I out of bounds");
|
||||
|
||||
// Get bitmask of size N, at I pos
|
||||
static constexpr utype data_mask()
|
||||
static constexpr vtype data_mask()
|
||||
{
|
||||
return static_cast<utype>(static_cast<utype>(bf_t::mask1 >> (bf_t::bitmax - bf_t::bitsize)) << bitpos);
|
||||
return bf_t::vmask << bitpos;
|
||||
}
|
||||
|
||||
// Bitfield extraction helper
|
||||
@@ -55,7 +50,7 @@ struct bf_t : bf_base<T, N>
|
||||
// Load unsigned value
|
||||
static constexpr T2 extract(const T& data)
|
||||
{
|
||||
return static_cast<T2>((static_cast<utype>(data) >> bitpos) & bf_t::vmask);
|
||||
return (data >> bitpos) & bf_t::vmask;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -65,7 +60,7 @@ struct bf_t : bf_base<T, N>
|
||||
// Load signed value (sign-extended)
|
||||
static constexpr T2 extract(const T& data)
|
||||
{
|
||||
return static_cast<T2>(static_cast<vtype>(static_cast<utype>(data) << (bf_t::bitmax - bitpos - N)) >> (bf_t::bitmax - N));
|
||||
return data << (bf_t::bitmax - bitpos - N) >> (bf_t::bitmax - N);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -78,7 +73,7 @@ struct bf_t : bf_base<T, N>
|
||||
// Bitfield insertion
|
||||
static constexpr vtype insert(vtype value)
|
||||
{
|
||||
return static_cast<vtype>((value & bf_t::vmask) << bitpos);
|
||||
return (value & bf_t::vmask) << bitpos;
|
||||
}
|
||||
|
||||
// Load bitfield value
|
||||
@@ -90,7 +85,7 @@ struct bf_t : bf_base<T, N>
|
||||
// Load raw data with mask applied
|
||||
constexpr T unshifted() const
|
||||
{
|
||||
return static_cast<T>(this->m_data & data_mask());
|
||||
return this->m_data & data_mask();
|
||||
}
|
||||
|
||||
// Optimized bool conversion (must be removed if inappropriate)
|
||||
@@ -102,14 +97,14 @@ struct bf_t : bf_base<T, N>
|
||||
// Store bitfield value
|
||||
bf_t& operator =(vtype value)
|
||||
{
|
||||
this->m_data = static_cast<vtype>((this->m_data & ~data_mask()) | insert(value));
|
||||
this->m_data = (this->m_data & ~data_mask()) | insert(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
vtype operator ++(int)
|
||||
{
|
||||
utype result = *this;
|
||||
*this = static_cast<vtype>(result + 1);
|
||||
vtype result = *this;
|
||||
*this = result + 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -120,8 +115,8 @@ struct bf_t : bf_base<T, N>
|
||||
|
||||
vtype operator --(int)
|
||||
{
|
||||
utype result = *this;
|
||||
*this = static_cast<vtype>(result - 1);
|
||||
vtype result = *this;
|
||||
*this = result - 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -147,19 +142,19 @@ struct bf_t : bf_base<T, N>
|
||||
|
||||
bf_t& operator &=(vtype right)
|
||||
{
|
||||
this->m_data &= static_cast<vtype>((static_cast<utype>(right) & bf_t::vmask) << bitpos);
|
||||
this->m_data &= (right & bf_t::vmask) << bitpos;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bf_t& operator |=(vtype right)
|
||||
{
|
||||
this->m_data |= static_cast<vtype>((static_cast<utype>(right) & bf_t::vmask) << bitpos);
|
||||
this->m_data |= (right & bf_t::vmask) << bitpos;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bf_t& operator ^=(vtype right)
|
||||
{
|
||||
this->m_data ^= static_cast<vtype>((static_cast<utype>(right) & bf_t::vmask) << bitpos);
|
||||
this->m_data ^= (right & bf_t::vmask) << bitpos;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
@@ -170,24 +165,23 @@ struct cf_t : bf_base<typename F::type, F::bitsize + cf_t<Fields...>::bitsize>
|
||||
{
|
||||
using type = typename cf_t::type;
|
||||
using vtype = typename cf_t::vtype;
|
||||
using utype = typename cf_t::utype;
|
||||
|
||||
// Get disjunction of all "data" masks of concatenated values
|
||||
static constexpr vtype data_mask()
|
||||
{
|
||||
return static_cast<vtype>(F::data_mask() | cf_t<Fields...>::data_mask());
|
||||
return F::data_mask() | cf_t<Fields...>::data_mask();
|
||||
}
|
||||
|
||||
// Extract all bitfields and concatenate
|
||||
static constexpr vtype extract(const type& data)
|
||||
{
|
||||
return static_cast<vtype>(static_cast<utype>(F::extract(data)) << cf_t<Fields...>::bitsize | cf_t<Fields...>::extract(data));
|
||||
return F::extract(data) << cf_t<Fields...>::bitsize | cf_t<Fields...>::extract(data);
|
||||
}
|
||||
|
||||
// Split bitfields and insert them
|
||||
static constexpr vtype insert(vtype value)
|
||||
{
|
||||
return static_cast<vtype>(F::insert(value >> cf_t<Fields...>::bitsize) | cf_t<Fields...>::insert(value));
|
||||
return F::insert(value >> cf_t<Fields...>::bitsize) | cf_t<Fields...>::insert(value);
|
||||
}
|
||||
|
||||
// Load value
|
||||
@@ -248,36 +242,3 @@ struct ff_t : bf_base<T, N>
|
||||
return V;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, uint I, uint N>
|
||||
struct fmt_unveil<bf_t<T, I, N>, void>
|
||||
{
|
||||
using type = typename fmt_unveil<simple_t<T>>::type;
|
||||
|
||||
static inline auto get(const bf_t<T, I, N>& bf)
|
||||
{
|
||||
return fmt_unveil<type>::get(bf);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename F, typename... Fields>
|
||||
struct fmt_unveil<cf_t<F, Fields...>, void>
|
||||
{
|
||||
using type = typename fmt_unveil<simple_t<typename F::type>>::type;
|
||||
|
||||
static inline auto get(const cf_t<F, Fields...>& cf)
|
||||
{
|
||||
return fmt_unveil<type>::get(cf);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, T V, uint N>
|
||||
struct fmt_unveil<ff_t<T, V, N>, void>
|
||||
{
|
||||
using type = typename fmt_unveil<simple_t<T>>::type;
|
||||
|
||||
static inline auto get(const ff_t<T, V, N>& ff)
|
||||
{
|
||||
return fmt_unveil<type>::get(ff);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,258 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
// Small bitset for enum class types with available values [0, BitSize).
|
||||
// T must be either enum type or convertible to (registered with via simple_t<T>).
|
||||
// Internal representation is single value of type T.
|
||||
template<typename T, std::size_t BitSize = sizeof(T) * CHAR_BIT>
|
||||
struct bitset_t
|
||||
{
|
||||
using type = simple_t<T>;
|
||||
using under = std::underlying_type_t<type>;
|
||||
enum class raw_type : under {};
|
||||
|
||||
static constexpr auto bitsize = BitSize;
|
||||
|
||||
bitset_t() = default;
|
||||
|
||||
constexpr bitset_t(type _enum_const)
|
||||
: m_value(static_cast<type>(shift(_enum_const)))
|
||||
{
|
||||
}
|
||||
|
||||
constexpr bitset_t(raw_type raw_value)
|
||||
: m_value(static_cast<T>(static_cast<under>(raw_value)))
|
||||
{
|
||||
}
|
||||
|
||||
// Get underlying value
|
||||
constexpr under _value() const
|
||||
{
|
||||
return static_cast<under>(m_value);
|
||||
}
|
||||
|
||||
explicit constexpr operator bool() const
|
||||
{
|
||||
return _value() ? true : false;
|
||||
}
|
||||
|
||||
bitset_t& operator +=(bitset_t rhs)
|
||||
{
|
||||
return *this = static_cast<raw_type>(_value() | rhs._value());
|
||||
}
|
||||
|
||||
bitset_t& operator -=(bitset_t rhs)
|
||||
{
|
||||
return *this = static_cast<raw_type>(_value() & ~rhs._value());
|
||||
}
|
||||
|
||||
bitset_t& operator &=(bitset_t rhs)
|
||||
{
|
||||
return *this = static_cast<raw_type>(_value() & rhs._value());
|
||||
}
|
||||
|
||||
bitset_t& operator ^=(bitset_t rhs)
|
||||
{
|
||||
return *this = static_cast<raw_type>(_value() ^ rhs._value());
|
||||
}
|
||||
|
||||
friend constexpr bitset_t operator +(bitset_t lhs, bitset_t rhs)
|
||||
{
|
||||
return static_cast<raw_type>(lhs._value() | rhs._value());
|
||||
}
|
||||
|
||||
friend constexpr bitset_t operator -(bitset_t lhs, bitset_t rhs)
|
||||
{
|
||||
return static_cast<raw_type>(lhs._value() & ~rhs._value());
|
||||
}
|
||||
|
||||
friend constexpr bitset_t operator &(bitset_t lhs, bitset_t rhs)
|
||||
{
|
||||
return static_cast<raw_type>(lhs._value() & rhs._value());
|
||||
}
|
||||
|
||||
friend constexpr bitset_t operator ^(bitset_t lhs, bitset_t rhs)
|
||||
{
|
||||
return static_cast<raw_type>(lhs._value() ^ rhs._value());
|
||||
}
|
||||
|
||||
bool test(bitset_t rhs) const
|
||||
{
|
||||
const under v = _value();
|
||||
const under s = rhs._value();
|
||||
return (v & s) != 0;
|
||||
}
|
||||
|
||||
bool test_and_set(bitset_t rhs)
|
||||
{
|
||||
const under v = _value();
|
||||
const under s = rhs._value();
|
||||
*this = static_cast<raw_type>(v | s);
|
||||
return (v & s) != 0;
|
||||
}
|
||||
|
||||
bool test_and_reset(bitset_t rhs)
|
||||
{
|
||||
const under v = _value();
|
||||
const under s = rhs._value();
|
||||
*this = static_cast<raw_type>(v & ~s);
|
||||
return (v & s) != 0;
|
||||
}
|
||||
|
||||
bool test_and_complement(bitset_t rhs)
|
||||
{
|
||||
const under v = _value();
|
||||
const under s = rhs._value();
|
||||
*this = static_cast<raw_type>(v ^ s);
|
||||
return (v & s) != 0;
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr under shift(const T& value)
|
||||
{
|
||||
return static_cast<under>(value) < BitSize ? static_cast<under>(1) << static_cast<under>(value) : throw value;
|
||||
}
|
||||
|
||||
T m_value;
|
||||
};
|
||||
|
||||
template<typename T, typename RT = T>
|
||||
constexpr RT make_bitset()
|
||||
{
|
||||
return RT{};
|
||||
}
|
||||
|
||||
// Fold enum constants into bitset_t<> (must be implemented with constexpr initializer_list constructor instead)
|
||||
template<typename T = void, typename Arg, typename... Args, typename RT = std::conditional_t<std::is_void<T>::value, bitset_t<Arg>, T>>
|
||||
constexpr RT make_bitset(Arg&& _enum_const, Args&&... args)
|
||||
{
|
||||
return RT{ std::forward<Arg>(_enum_const) } + make_bitset<RT>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename T, typename CT>
|
||||
struct atomic_add<bitset_t<T>, CT, std::enable_if_t<std::is_enum<T>::value>>
|
||||
{
|
||||
using under = typename bitset_t<T>::under;
|
||||
using raw_type = typename bitset_t<T>::raw_type;
|
||||
|
||||
static inline bitset_t<T> op1(bitset_t<T>& left, bitset_t<T> right)
|
||||
{
|
||||
return static_cast<raw_type>(atomic_storage<under>::fetch_or(reinterpret_cast<under&>(left), right._value()));
|
||||
}
|
||||
|
||||
static constexpr auto fetch_op = &op1;
|
||||
|
||||
static inline bitset_t<T> op2(bitset_t<T>& left, bitset_t<T> right)
|
||||
{
|
||||
return static_cast<raw_type>(atomic_storage<under>::or_fetch(reinterpret_cast<under&>(left), right._value()));
|
||||
}
|
||||
|
||||
static constexpr auto op_fetch = &op2;
|
||||
static constexpr auto atomic_op = &op2;
|
||||
};
|
||||
|
||||
template<typename T, typename CT>
|
||||
struct atomic_sub<bitset_t<T>, CT, std::enable_if_t<std::is_enum<T>::value>>
|
||||
{
|
||||
using under = typename bitset_t<T>::under;
|
||||
using raw_type = typename bitset_t<T>::raw_type;
|
||||
|
||||
static inline bitset_t<T> op1(bitset_t<T>& left, bitset_t<T> right)
|
||||
{
|
||||
return static_cast<raw_type>(atomic_storage<under>::fetch_and(reinterpret_cast<under&>(left), ~right._value()));
|
||||
}
|
||||
|
||||
static constexpr auto fetch_op = &op1;
|
||||
|
||||
static inline bitset_t<T> op2(bitset_t<T>& left, bitset_t<T> right)
|
||||
{
|
||||
return static_cast<raw_type>(atomic_storage<under>::and_fetch(reinterpret_cast<under&>(left), ~right._value()));
|
||||
}
|
||||
|
||||
static constexpr auto op_fetch = &op2;
|
||||
static constexpr auto atomic_op = &op2;
|
||||
};
|
||||
|
||||
template<typename T, typename CT>
|
||||
struct atomic_and<bitset_t<T>, CT, std::enable_if_t<std::is_enum<T>::value>>
|
||||
{
|
||||
using under = typename bitset_t<T>::under;
|
||||
using raw_type = typename bitset_t<T>::raw_type;
|
||||
|
||||
static inline bitset_t<T> op1(bitset_t<T>& left, bitset_t<T> right)
|
||||
{
|
||||
return static_cast<raw_type>(atomic_storage<under>::fetch_and(reinterpret_cast<under&>(left), right._value()));
|
||||
}
|
||||
|
||||
static constexpr auto fetch_op = &op1;
|
||||
|
||||
static inline bitset_t<T> op2(bitset_t<T>& left, bitset_t<T> right)
|
||||
{
|
||||
return static_cast<raw_type>(atomic_storage<under>::and_fetch(reinterpret_cast<under&>(left), right._value()));
|
||||
}
|
||||
|
||||
static constexpr auto op_fetch = &op2;
|
||||
static constexpr auto atomic_op = &op2;
|
||||
};
|
||||
|
||||
template<typename T, typename CT>
|
||||
struct atomic_xor<bitset_t<T>, CT, std::enable_if_t<std::is_enum<T>::value>>
|
||||
{
|
||||
using under = typename bitset_t<T>::under;
|
||||
using raw_type = typename bitset_t<T>::raw_type;
|
||||
|
||||
static inline bitset_t<T> op1(bitset_t<T>& left, bitset_t<T> right)
|
||||
{
|
||||
return static_cast<raw_type>(atomic_storage<under>::fetch_xor(reinterpret_cast<under&>(left), right._value()));
|
||||
}
|
||||
|
||||
static constexpr auto fetch_op = &op1;
|
||||
|
||||
static inline bitset_t<T> op2(bitset_t<T>& left, bitset_t<T> right)
|
||||
{
|
||||
return static_cast<raw_type>(atomic_storage<under>::xor_fetch(reinterpret_cast<under&>(left), right._value()));
|
||||
}
|
||||
|
||||
static constexpr auto op_fetch = &op2;
|
||||
static constexpr auto atomic_op = &op2;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct atomic_test_and_set<bitset_t<T>, T, std::enable_if_t<std::is_enum<T>::value>>
|
||||
{
|
||||
using under = typename bitset_t<T>::under;
|
||||
|
||||
static inline bool _op(bitset_t<T>& left, const T& value)
|
||||
{
|
||||
return atomic_storage<under>::bts(reinterpret_cast<under&>(left), static_cast<uint>(value));
|
||||
}
|
||||
|
||||
static constexpr auto atomic_op = &_op;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct atomic_test_and_reset<bitset_t<T>, T, std::enable_if_t<std::is_enum<T>::value>>
|
||||
{
|
||||
using under = typename bitset_t<T>::under;
|
||||
|
||||
static inline bool _op(bitset_t<T>& left, const T& value)
|
||||
{
|
||||
return atomic_storage<under>::btr(reinterpret_cast<under&>(left), static_cast<uint>(value));
|
||||
}
|
||||
|
||||
static constexpr auto atomic_op = &_op;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct atomic_test_and_complement<bitset_t<T>, T, std::enable_if_t<std::is_enum<T>::value>>
|
||||
{
|
||||
using under = typename bitset_t<T>::under;
|
||||
|
||||
static inline bool _op(bitset_t<T>& left, const T& value)
|
||||
{
|
||||
return atomic_storage<under>::btc(reinterpret_cast<under&>(left), static_cast<uint>(value));
|
||||
}
|
||||
|
||||
static constexpr auto atomic_op = &_op;
|
||||
};
|
||||
-1702
File diff suppressed because it is too large
Load Diff
+39
-152
@@ -5,47 +5,52 @@
|
||||
|
||||
namespace cfg
|
||||
{
|
||||
logs::channel cfg("CFG");
|
||||
logs::channel cfg("CFG", logs::level::notice);
|
||||
|
||||
_base::_base(type _type)
|
||||
entry_base::entry_base(type _type)
|
||||
: m_type(_type)
|
||||
{
|
||||
if (_type != type::node)
|
||||
{
|
||||
fmt::throw_exception<std::logic_error>("Invalid root node" HERE);
|
||||
throw std::logic_error("Invalid root node");
|
||||
}
|
||||
}
|
||||
|
||||
_base::_base(type _type, node* owner, const std::string& name)
|
||||
entry_base::entry_base(type _type, node& owner, const std::string& name)
|
||||
: m_type(_type)
|
||||
{
|
||||
for (const auto& pair : owner->m_nodes)
|
||||
if (!owner.m_nodes.emplace(name, this).second)
|
||||
{
|
||||
if (pair.first == name)
|
||||
{
|
||||
fmt::throw_exception<std::logic_error>("Node already exists: %s" HERE, name);
|
||||
}
|
||||
throw std::logic_error("Node already exists");
|
||||
}
|
||||
}
|
||||
|
||||
entry_base& entry_base::operator[](const std::string& name) const
|
||||
{
|
||||
if (m_type == type::node)
|
||||
{
|
||||
return *static_cast<const node&>(*this).m_nodes.at(name);
|
||||
}
|
||||
|
||||
owner->m_nodes.emplace_back(name, this);
|
||||
throw std::logic_error("Invalid node type");
|
||||
}
|
||||
|
||||
bool _base::from_string(const std::string&)
|
||||
entry_base& entry_base::operator[](const char* name) const
|
||||
{
|
||||
fmt::throw_exception<std::logic_error>("from_string() purecall" HERE);
|
||||
}
|
||||
if (m_type == type::node)
|
||||
{
|
||||
return *static_cast<const node&>(*this).m_nodes.at(name);
|
||||
}
|
||||
|
||||
bool _base::from_list(std::vector<std::string>&&)
|
||||
{
|
||||
fmt::throw_exception<std::logic_error>("from_list() purecall" HERE);
|
||||
throw std::logic_error("Invalid node type");
|
||||
}
|
||||
|
||||
// Emit YAML
|
||||
static void encode(YAML::Emitter& out, const class _base& rhs);
|
||||
static void encode(YAML::Emitter& out, const class entry_base& rhs);
|
||||
|
||||
// Incrementally load config entries from YAML::Node.
|
||||
// The config value is preserved if the corresponding YAML node doesn't exist.
|
||||
static void decode(const YAML::Node& data, class _base& rhs);
|
||||
static void decode(const YAML::Node& data, class entry_base& rhs);
|
||||
}
|
||||
|
||||
bool cfg::try_to_int64(s64* out, const std::string& value, s64 min, s64 max)
|
||||
@@ -80,69 +85,7 @@ bool cfg::try_to_int64(s64* out, const std::string& value, s64 min, s64 max)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cfg::try_to_enum_value(u64* out, decltype(&fmt_class_string<int>::format) func, const std::string& value)
|
||||
{
|
||||
for (u64 i = 0;; i++)
|
||||
{
|
||||
std::string var;
|
||||
func(var, i);
|
||||
|
||||
if (var == value)
|
||||
{
|
||||
if (out) *out = i;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string hex;
|
||||
fmt_class_string<u64>::format(hex, i);
|
||||
if (var == hex)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
std::size_t pos;
|
||||
const auto val = std::stoull(value, &pos, 0);
|
||||
|
||||
if (pos != value.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (out) *out = val;
|
||||
return true;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> cfg::try_to_enum_list(decltype(&fmt_class_string<int>::format) func)
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
|
||||
for (u64 i = 0;; i++)
|
||||
{
|
||||
std::string var;
|
||||
func(var, i);
|
||||
|
||||
std::string hex;
|
||||
fmt_class_string<u64>::format(hex, i);
|
||||
if (var == hex)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
result.emplace_back(std::move(var));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void cfg::encode(YAML::Emitter& out, const cfg::_base& rhs)
|
||||
void cfg::encode(YAML::Emitter& out, const cfg::entry_base& rhs)
|
||||
{
|
||||
switch (rhs.get_type())
|
||||
{
|
||||
@@ -152,8 +95,7 @@ void cfg::encode(YAML::Emitter& out, const cfg::_base& rhs)
|
||||
for (const auto& np : static_cast<const node&>(rhs).get_nodes())
|
||||
{
|
||||
out << YAML::Key << np.first;
|
||||
out << YAML::Value;
|
||||
encode(out, *np.second);
|
||||
out << YAML::Value; encode(out, *np.second);
|
||||
}
|
||||
|
||||
out << YAML::EndMap;
|
||||
@@ -170,25 +112,12 @@ void cfg::encode(YAML::Emitter& out, const cfg::_base& rhs)
|
||||
out << YAML::EndSeq;
|
||||
return;
|
||||
}
|
||||
case type::log:
|
||||
{
|
||||
out << YAML::BeginMap;
|
||||
for (const auto& np : static_cast<const log_entry&>(rhs).get_map())
|
||||
{
|
||||
if (np.second == logs::level::notice) continue;
|
||||
out << YAML::Key << np.first;
|
||||
out << YAML::Value << fmt::format("%s", np.second);
|
||||
}
|
||||
|
||||
out << YAML::EndMap;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
out << rhs.to_string();
|
||||
}
|
||||
|
||||
void cfg::decode(const YAML::Node& data, cfg::_base& rhs)
|
||||
void cfg::decode(const YAML::Node& data, cfg::entry_base& rhs)
|
||||
{
|
||||
switch (rhs.get_type())
|
||||
{
|
||||
@@ -204,12 +133,16 @@ void cfg::decode(const YAML::Node& data, cfg::_base& rhs)
|
||||
if (!pair.first.IsScalar()) continue;
|
||||
|
||||
// Find the key among existing nodes
|
||||
for (const auto& _pair : static_cast<node&>(rhs).get_nodes())
|
||||
const auto name = pair.first.Scalar();
|
||||
const auto found = static_cast<node&>(rhs).get_nodes().find(name);
|
||||
|
||||
if (found != static_cast<node&>(rhs).get_nodes().cend())
|
||||
{
|
||||
if (_pair.first == pair.first.Scalar())
|
||||
{
|
||||
decode(pair.second, *_pair.second);
|
||||
}
|
||||
decode(pair.second, *found->second);
|
||||
}
|
||||
else
|
||||
{
|
||||
// ???
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,29 +159,6 @@ void cfg::decode(const YAML::Node& data, cfg::_base& rhs)
|
||||
|
||||
break;
|
||||
}
|
||||
case type::log:
|
||||
{
|
||||
if (data.IsScalar() || data.IsSequence())
|
||||
{
|
||||
return; // ???
|
||||
}
|
||||
|
||||
std::map<std::string, logs::level> values;
|
||||
|
||||
for (const auto& pair : data)
|
||||
{
|
||||
if (!pair.first.IsScalar() || !pair.second.IsScalar()) continue;
|
||||
|
||||
u64 value;
|
||||
if (cfg::try_to_enum_value(&value, &fmt_class_string<logs::level>::format, pair.second.Scalar()))
|
||||
{
|
||||
values.emplace(pair.first.Scalar(), static_cast<logs::level>(static_cast<int>(value)));
|
||||
}
|
||||
}
|
||||
|
||||
static_cast<log_entry&>(rhs).set_map(std::move(values));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
std::string value;
|
||||
@@ -285,32 +195,9 @@ void cfg::node::from_default()
|
||||
}
|
||||
}
|
||||
|
||||
void cfg::_bool::from_default()
|
||||
cfg::root_node& cfg::get_root()
|
||||
{
|
||||
m_value = def;
|
||||
}
|
||||
|
||||
void cfg::string::from_default()
|
||||
{
|
||||
m_value = def;
|
||||
}
|
||||
|
||||
void cfg::set_entry::from_default()
|
||||
{
|
||||
m_set = {};
|
||||
}
|
||||
|
||||
void cfg::log_entry::set_map(std::map<std::string, logs::level>&& map)
|
||||
{
|
||||
logs::reset();
|
||||
|
||||
for (auto&& pair : (m_map = std::move(map)))
|
||||
{
|
||||
logs::set_level(pair.first, pair.second);
|
||||
}
|
||||
}
|
||||
|
||||
void cfg::log_entry::from_default()
|
||||
{
|
||||
set_map({});
|
||||
// Magic static
|
||||
static root_node root;
|
||||
return root;
|
||||
}
|
||||
|
||||
+264
-92
@@ -1,57 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
#include "Utilities/types.h"
|
||||
#include "Utilities/StrFmt.h"
|
||||
#include "Utilities/Log.h"
|
||||
#include "Utilities/Atomic.h"
|
||||
|
||||
#include <initializer_list>
|
||||
#include <exception>
|
||||
#include <utility>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
|
||||
namespace cfg
|
||||
{
|
||||
// Convert string to signed integer
|
||||
bool try_to_int64(s64* out, const std::string& value, s64 min, s64 max);
|
||||
|
||||
// Internal hack
|
||||
bool try_to_enum_value(u64* out, decltype(&fmt_class_string<int>::format) func, const std::string&);
|
||||
|
||||
// Internal hack
|
||||
std::vector<std::string> try_to_enum_list(decltype(&fmt_class_string<int>::format) func);
|
||||
|
||||
// Config tree entry type.
|
||||
enum class type : uint
|
||||
{
|
||||
node = 0, // cfg::node type
|
||||
_bool, // cfg::_bool type
|
||||
_enum, // cfg::_enum type
|
||||
_int, // cfg::_int type
|
||||
string, // cfg::string type
|
||||
boolean, // cfg::bool_entry type
|
||||
fixed_map, // cfg::map_entry type
|
||||
enumeration, // cfg::enum_entry type
|
||||
integer, // cfg::int_entry type
|
||||
string, // cfg::string_entry type
|
||||
set, // cfg::set_entry type
|
||||
log,
|
||||
};
|
||||
|
||||
// Config tree entry abstract base class
|
||||
class _base
|
||||
class entry_base
|
||||
{
|
||||
const type m_type;
|
||||
|
||||
protected:
|
||||
// Ownerless entry constructor
|
||||
_base(type _type);
|
||||
entry_base(type _type);
|
||||
|
||||
// Owned entry constructor
|
||||
_base(type _type, class node* owner, const std::string& name);
|
||||
entry_base(type _type, class node& owner, const std::string& name);
|
||||
|
||||
public:
|
||||
// Disallow copy/move constructors and assignments
|
||||
_base(const _base&) = delete;
|
||||
entry_base(const entry_base&) = delete;
|
||||
|
||||
// Get type
|
||||
type get_type() const { return m_type; }
|
||||
|
||||
// Access child node (must exist)
|
||||
entry_base& operator [](const std::string& name) const; entry_base& operator [](const char* name) const;
|
||||
|
||||
// Reset defaults
|
||||
virtual void from_default() = 0;
|
||||
|
||||
@@ -62,7 +62,10 @@ namespace cfg
|
||||
}
|
||||
|
||||
// Try to convert from string (optional)
|
||||
virtual bool from_string(const std::string&);
|
||||
virtual bool from_string(const std::string&)
|
||||
{
|
||||
throw std::logic_error("from_string() not specified");
|
||||
}
|
||||
|
||||
// Get string list (optional)
|
||||
virtual std::vector<std::string> to_list() const
|
||||
@@ -71,31 +74,34 @@ namespace cfg
|
||||
}
|
||||
|
||||
// Set multiple values. Implementation-specific, optional.
|
||||
virtual bool from_list(std::vector<std::string>&&);
|
||||
virtual bool from_list(std::vector<std::string>&&)
|
||||
{
|
||||
throw std::logic_error("from_list() not specified");
|
||||
}
|
||||
};
|
||||
|
||||
// Config tree node which contains another nodes
|
||||
class node : public _base
|
||||
class node : public entry_base
|
||||
{
|
||||
std::vector<std::pair<std::string, _base*>> m_nodes;
|
||||
std::map<std::string, entry_base*> m_nodes;
|
||||
|
||||
friend class _base;
|
||||
friend class entry_base;
|
||||
|
||||
public:
|
||||
// Root node constructor
|
||||
node()
|
||||
: _base(type::node)
|
||||
: entry_base(type::node)
|
||||
{
|
||||
}
|
||||
|
||||
// Registered node constructor
|
||||
node(node* owner, const std::string& name)
|
||||
: _base(type::node, owner, name)
|
||||
node(node& owner, const std::string& name)
|
||||
: entry_base(type::node, owner, name)
|
||||
{
|
||||
}
|
||||
|
||||
// Get child nodes
|
||||
const auto& get_nodes() const
|
||||
const std::map<std::string, entry_base*>& get_nodes() const
|
||||
{
|
||||
return m_nodes;
|
||||
}
|
||||
@@ -110,38 +116,46 @@ namespace cfg
|
||||
void from_default() override;
|
||||
};
|
||||
|
||||
class _bool final : public _base
|
||||
struct bool_entry final : public entry_base
|
||||
{
|
||||
bool m_value;
|
||||
atomic_t<bool> value;
|
||||
|
||||
public:
|
||||
const bool def;
|
||||
|
||||
_bool(node* owner, const std::string& name, bool def = false)
|
||||
: _base(type::_bool, owner, name)
|
||||
, m_value(def)
|
||||
bool_entry(node& owner, const std::string& name, bool def = false)
|
||||
: entry_base(type::boolean, owner, name)
|
||||
, value(def)
|
||||
, def(def)
|
||||
{
|
||||
}
|
||||
|
||||
explicit operator bool() const
|
||||
{
|
||||
return m_value;
|
||||
return value.load();
|
||||
}
|
||||
|
||||
void from_default() override;
|
||||
bool_entry& operator =(bool value)
|
||||
{
|
||||
value = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void from_default() override
|
||||
{
|
||||
value = def;
|
||||
}
|
||||
|
||||
std::string to_string() const override
|
||||
{
|
||||
return m_value ? "true" : "false";
|
||||
return value.load() ? "true" : "false";
|
||||
}
|
||||
|
||||
bool from_string(const std::string& value) override
|
||||
{
|
||||
if (value == "false")
|
||||
m_value = false;
|
||||
this->value = false;
|
||||
else if (value == "true")
|
||||
m_value = true;
|
||||
this->value = true;
|
||||
else
|
||||
return false;
|
||||
|
||||
@@ -149,17 +163,116 @@ namespace cfg
|
||||
}
|
||||
};
|
||||
|
||||
// Value node with fixed set of possible values, each maps to an enum value of type T.
|
||||
template <typename T>
|
||||
class _enum final : public _base
|
||||
// Value node with fixed set of possible values, each maps to a value of type T.
|
||||
template<typename T>
|
||||
struct map_entry final : public entry_base
|
||||
{
|
||||
T m_value;
|
||||
using init_type = std::initializer_list<std::pair<std::string, T>>;
|
||||
using map_type = std::unordered_map<std::string, T>;
|
||||
using list_type = std::vector<std::string>;
|
||||
using value_type = typename map_type::value_type;
|
||||
|
||||
static map_type make_map(init_type init)
|
||||
{
|
||||
map_type map(init.size());
|
||||
|
||||
for (const auto& v : init)
|
||||
{
|
||||
// Ensure elements are unique
|
||||
VERIFY(map.emplace(v.first, v.second).second);
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
static list_type make_list(init_type init)
|
||||
{
|
||||
list_type list; list.reserve(init.size());
|
||||
|
||||
for (const auto& v : init)
|
||||
{
|
||||
list.emplace_back(v.first);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public:
|
||||
const map_type map;
|
||||
const list_type list; // Element list sorted in original order
|
||||
const value_type& def; // Pointer to the default value
|
||||
|
||||
private:
|
||||
atomic_t<const value_type*> m_value;
|
||||
|
||||
public:
|
||||
map_entry(node& owner, const std::string& name, const std::string& def, init_type init)
|
||||
: entry_base(type::fixed_map, owner, name)
|
||||
, map(make_map(init))
|
||||
, list(make_list(init))
|
||||
, def(*map.find(def))
|
||||
, m_value(&this->def)
|
||||
{
|
||||
}
|
||||
|
||||
map_entry(node& owner, const std::string& name, std::size_t def_index, init_type init)
|
||||
: map_entry(owner, name, def_index < init.size() ? (init.begin() + def_index)->first : throw std::logic_error("Invalid default value index"), init)
|
||||
{
|
||||
}
|
||||
|
||||
map_entry(node& owner, const std::string& name, init_type init)
|
||||
: map_entry(owner, name, 0, init)
|
||||
{
|
||||
}
|
||||
|
||||
const T& get() const
|
||||
{
|
||||
return m_value.load()->second;
|
||||
}
|
||||
|
||||
void from_default() override
|
||||
{
|
||||
m_value = &def;
|
||||
}
|
||||
|
||||
std::string to_string() const override
|
||||
{
|
||||
return m_value.load()->first;
|
||||
}
|
||||
|
||||
bool from_string(const std::string& value) override
|
||||
{
|
||||
const auto found = map.find(value);
|
||||
|
||||
if (found == map.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_value = &*found;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> to_list() const override
|
||||
{
|
||||
return list;
|
||||
}
|
||||
};
|
||||
|
||||
// Value node with fixed set of possible values, each maps to an enum value of type T.
|
||||
template<typename T, bool External = false>
|
||||
class enum_entry final : public entry_base
|
||||
{
|
||||
// Value or reference
|
||||
std::conditional_t<External, atomic_t<T>&, atomic_t<T>> m_value;
|
||||
|
||||
public:
|
||||
const T def;
|
||||
|
||||
_enum(node* owner, const std::string& name, T value = {})
|
||||
: _base(type::_enum, owner, name)
|
||||
enum_entry(node& owner, const std::string& name, std::conditional_t<External, atomic_t<T>&, T> value)
|
||||
: entry_base(type::enumeration, owner, name)
|
||||
, m_value(value)
|
||||
, def(value)
|
||||
{
|
||||
@@ -167,7 +280,13 @@ namespace cfg
|
||||
|
||||
operator T() const
|
||||
{
|
||||
return m_value;
|
||||
return m_value.load();
|
||||
}
|
||||
|
||||
enum_entry& operator =(T value)
|
||||
{
|
||||
m_value = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void from_default() override
|
||||
@@ -177,20 +296,26 @@ namespace cfg
|
||||
|
||||
std::string to_string() const override
|
||||
{
|
||||
std::string result;
|
||||
fmt_class_string<T>::format(result, fmt_unveil<T>::get(m_value));
|
||||
return result; // TODO: ???
|
||||
for (std::size_t i = 0; i < sizeof(bijective<T, const char*>::map) / sizeof(bijective_pair<T, const char*>); i++)
|
||||
{
|
||||
if (bijective<T, const char*>::map[i].v1 == m_value)
|
||||
{
|
||||
return bijective<T, const char*>::map[i].v2;
|
||||
}
|
||||
}
|
||||
|
||||
return{}; // TODO: ???
|
||||
}
|
||||
|
||||
bool from_string(const std::string& value) override
|
||||
{
|
||||
u64 result;
|
||||
|
||||
if (try_to_enum_value(&result, &fmt_class_string<T>::format, value))
|
||||
for (std::size_t i = 0; i < sizeof(bijective<T, const char*>::map) / sizeof(bijective_pair<T, const char*>); i++)
|
||||
{
|
||||
// No narrowing check, it's hard to do right there
|
||||
m_value = static_cast<T>(static_cast<std::underlying_type_t<T>>(result));
|
||||
return true;
|
||||
if (bijective<T, const char*>::map[i].v2 == value)
|
||||
{
|
||||
m_value = bijective<T, const char*>::map[i].v1;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -198,26 +323,33 @@ namespace cfg
|
||||
|
||||
std::vector<std::string> to_list() const override
|
||||
{
|
||||
return try_to_enum_list(&fmt_class_string<T>::format);
|
||||
std::vector<std::string> result;
|
||||
|
||||
for (std::size_t i = 0; i < sizeof(bijective<T, const char*>::map) / sizeof(bijective_pair<T, const char*>); i++)
|
||||
{
|
||||
result.emplace_back(bijective<T, const char*>::map[i].v2);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
// Signed 32/64-bit integer entry with custom Min/Max range.
|
||||
template <s64 Min, s64 Max>
|
||||
class _int final : public _base
|
||||
template<s64 Min, s64 Max>
|
||||
class int_entry final : public entry_base
|
||||
{
|
||||
static_assert(Min < Max, "Invalid cfg::_int range");
|
||||
static_assert(Min < Max, "Invalid cfg::int_entry range");
|
||||
|
||||
// Prefer 32 bit type if possible
|
||||
using int_type = std::conditional_t<Min >= INT32_MIN && Max <= INT32_MAX, s32, s64>;
|
||||
|
||||
int_type m_value;
|
||||
atomic_t<int_type> m_value;
|
||||
|
||||
public:
|
||||
const int_type def;
|
||||
|
||||
_int(node* owner, const std::string& name, int_type def = std::min<int_type>(Max, std::max<int_type>(Min, 0)))
|
||||
: _base(type::_int, owner, name)
|
||||
int_entry(node& owner, const std::string& name, int_type def = std::min<int_type>(Max, std::max<int_type>(Min, 0)))
|
||||
: entry_base(type::integer, owner, name)
|
||||
, m_value(def)
|
||||
, def(def)
|
||||
{
|
||||
@@ -225,7 +357,18 @@ namespace cfg
|
||||
|
||||
operator int_type() const
|
||||
{
|
||||
return m_value;
|
||||
return m_value.load();
|
||||
}
|
||||
|
||||
int_entry& operator =(int_type value)
|
||||
{
|
||||
if (value < Min || value > Max)
|
||||
{
|
||||
throw fmt::exception("Value out of the valid range: %lld" HERE, s64{ value });
|
||||
}
|
||||
|
||||
m_value = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void from_default() override
|
||||
@@ -235,7 +378,7 @@ namespace cfg
|
||||
|
||||
std::string to_string() const override
|
||||
{
|
||||
return std::to_string(m_value);
|
||||
return std::to_string(m_value.load());
|
||||
}
|
||||
|
||||
bool from_string(const std::string& value) override
|
||||
@@ -252,21 +395,22 @@ namespace cfg
|
||||
};
|
||||
|
||||
// Alias for 32 bit int
|
||||
using int32 = _int<INT32_MIN, INT32_MAX>;
|
||||
using int32_entry = int_entry<INT32_MIN, INT32_MAX>;
|
||||
|
||||
// Alias for 64 bit int
|
||||
using int64 = _int<INT64_MIN, INT64_MAX>;
|
||||
using int64_entry = int_entry<INT64_MIN, INT64_MAX>;
|
||||
|
||||
// Simple string entry with mutex
|
||||
class string final : public _base
|
||||
class string_entry final : public entry_base
|
||||
{
|
||||
mutable std::mutex m_mutex;
|
||||
std::string m_value;
|
||||
|
||||
public:
|
||||
const std::string def;
|
||||
|
||||
string(node* owner, const std::string& name, const std::string& def = {})
|
||||
: _base(type::string, owner, name)
|
||||
string_entry(node& owner, const std::string& name, const std::string& def = {})
|
||||
: entry_base(type::string, owner, name)
|
||||
, m_value(def)
|
||||
, def(def)
|
||||
{
|
||||
@@ -274,87 +418,115 @@ namespace cfg
|
||||
|
||||
operator std::string() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
return m_value;
|
||||
}
|
||||
|
||||
const std::string& get() const
|
||||
std::string get() const
|
||||
{
|
||||
return m_value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
string_entry& operator =(const std::string& value)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
m_value = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::size_t size() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
return m_value.size();
|
||||
}
|
||||
|
||||
void from_default() override;
|
||||
void from_default() override
|
||||
{
|
||||
*this = def;
|
||||
}
|
||||
|
||||
std::string to_string() const override
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
return m_value;
|
||||
}
|
||||
|
||||
bool from_string(const std::string& value) override
|
||||
{
|
||||
m_value = value;
|
||||
*this = value;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// Simple set entry with mutex (TODO: template for various types)
|
||||
class set_entry final : public _base
|
||||
class set_entry final : public entry_base
|
||||
{
|
||||
mutable std::mutex m_mutex;
|
||||
|
||||
std::set<std::string> m_set;
|
||||
|
||||
public:
|
||||
// Default value is empty list in current implementation
|
||||
set_entry(node* owner, const std::string& name)
|
||||
: _base(type::set, owner, name)
|
||||
set_entry(node& owner, const std::string& name)
|
||||
: entry_base(type::set, owner, name)
|
||||
{
|
||||
}
|
||||
|
||||
std::set<std::string> get_set() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
return m_set;
|
||||
}
|
||||
|
||||
void set_set(std::set<std::string>&& set)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
m_set = std::move(set);
|
||||
}
|
||||
|
||||
void from_default() override;
|
||||
void from_default() override
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
m_set = {};
|
||||
}
|
||||
|
||||
std::vector<std::string> to_list() const override
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
return{ m_set.begin(), m_set.end() };
|
||||
}
|
||||
|
||||
bool from_list(std::vector<std::string>&& list) override
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
m_set = { std::make_move_iterator(list.begin()), std::make_move_iterator(list.end()) };
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class log_entry final : public _base
|
||||
// Root type with some predefined nodes. Don't change it, this is not mandatory for adding nodes.
|
||||
struct root_node : node
|
||||
{
|
||||
std::map<std::string, logs::level> m_map;
|
||||
|
||||
public:
|
||||
log_entry(node* owner, const std::string& name)
|
||||
: _base(type::log, owner, name)
|
||||
{
|
||||
}
|
||||
|
||||
std::map<std::string, logs::level> get_map() const
|
||||
{
|
||||
return m_map;
|
||||
}
|
||||
|
||||
void set_map(std::map<std::string, logs::level>&& map);
|
||||
|
||||
void from_default() override;
|
||||
node core { *this, "Core" };
|
||||
node vfs { *this, "VFS" };
|
||||
node log { *this, "Log" };
|
||||
node video { *this, "Video" };
|
||||
node audio { *this, "Audio" };
|
||||
node io { *this, "Input/Output" };
|
||||
node sys { *this, "System" };
|
||||
node net { *this, "Net" };
|
||||
node misc { *this, "Miscellaneous" };
|
||||
};
|
||||
|
||||
// Get global configuration root instance
|
||||
extern root_node& get_root();
|
||||
|
||||
// Global configuration root instance (cached reference)
|
||||
static root_node& root = get_root();
|
||||
}
|
||||
|
||||
// Registered log channel
|
||||
#define LOG_CHANNEL(name) extern logs::channel name; namespace logs { static cfg::enum_entry<logs::level, true> name(cfg::root.log, #name, ::name.enabled); }
|
||||
|
||||
+216
-362
@@ -1,16 +1,12 @@
|
||||
#include "File.h"
|
||||
#include "mutex.h"
|
||||
#include "StrFmt.h"
|
||||
#include "BEType.h"
|
||||
#include "Crypto/sha1.h"
|
||||
#include "Macro.h"
|
||||
#include "SharedMutex.h"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <cerrno>
|
||||
|
||||
using namespace std::literals::string_literals;
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include <cwchar>
|
||||
@@ -18,35 +14,36 @@ using namespace std::literals::string_literals;
|
||||
|
||||
static std::unique_ptr<wchar_t[]> to_wchar(const std::string& source)
|
||||
{
|
||||
// String size + null terminator
|
||||
const std::size_t buf_size = source.size() + 1;
|
||||
const auto buf_size = source.size() + 1; // size + null terminator
|
||||
|
||||
// Safe size
|
||||
const int size = narrow<int>(buf_size, "to_wchar" HERE);
|
||||
const int size = source.size() < INT_MAX ? static_cast<int>(buf_size) : throw fmt::exception("to_wchar(): invalid source length (0x%llx)", source.size());
|
||||
|
||||
// Buffer for max possible output length
|
||||
std::unique_ptr<wchar_t[]> buffer(new wchar_t[buf_size]);
|
||||
std::unique_ptr<wchar_t[]> buffer(new wchar_t[buf_size]); // allocate buffer assuming that length is the max possible size
|
||||
|
||||
verify("to_wchar" HERE), MultiByteToWideChar(CP_UTF8, 0, source.c_str(), size, buffer.get(), size);
|
||||
if (!MultiByteToWideChar(CP_UTF8, 0, source.c_str(), size, buffer.get(), size))
|
||||
{
|
||||
throw fmt::exception("to_wchar(): MultiByteToWideChar() failed: error %u.", GetLastError());
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static void to_utf8(std::string& out, const wchar_t* source)
|
||||
static void to_utf8(std::string& result, const wchar_t* source)
|
||||
{
|
||||
// String size
|
||||
const std::size_t length = std::wcslen(source);
|
||||
const auto length = std::wcslen(source);
|
||||
|
||||
// Safe buffer size for max possible output length (including null terminator)
|
||||
const int buf_size = narrow<int>(length * 3 + 1, "to_utf8" HERE);
|
||||
const int buf_size = length <= INT_MAX / 3 ? static_cast<int>(length) * 3 + 1 : throw fmt::exception("to_utf8(): invalid source length (0x%llx)", length);
|
||||
|
||||
// Resize buffer
|
||||
out.resize(buf_size - 1);
|
||||
result.resize(buf_size); // set max possible length for utf-8 + null terminator
|
||||
|
||||
const int result = WideCharToMultiByte(CP_UTF8, 0, source, static_cast<int>(length) + 1, &out.front(), buf_size, NULL, NULL);
|
||||
|
||||
// Fix the size
|
||||
out.resize(verify("to_utf8" HERE, result) - 1);
|
||||
if (const int nwritten = WideCharToMultiByte(CP_UTF8, 0, source, static_cast<int>(length) + 1, &result.front(), buf_size, NULL, NULL))
|
||||
{
|
||||
result.resize(nwritten - 1); // fix the size, remove null terminator
|
||||
}
|
||||
else
|
||||
{
|
||||
throw fmt::exception("to_utf8(): WideCharToMultiByte() failed: error %u.", GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
static time_t to_time(const ULARGE_INTEGER& ft)
|
||||
@@ -72,28 +69,16 @@ static time_t to_time(const FILETIME& ft)
|
||||
return to_time(v);
|
||||
}
|
||||
|
||||
static FILETIME from_time(s64 _time)
|
||||
{
|
||||
const ullong wtime = (_time + 11644473600ULL) * 10000000ULL;
|
||||
FILETIME result;
|
||||
result.dwLowDateTime = static_cast<DWORD>(wtime);
|
||||
result.dwHighDateTime = static_cast<DWORD>(wtime >> 32);
|
||||
return result;
|
||||
}
|
||||
|
||||
static fs::error to_error(DWORD e)
|
||||
{
|
||||
switch (e)
|
||||
{
|
||||
case ERROR_FILE_NOT_FOUND: return fs::error::noent;
|
||||
case ERROR_PATH_NOT_FOUND: return fs::error::noent;
|
||||
case ERROR_ACCESS_DENIED: return fs::error::acces;
|
||||
case ERROR_ALREADY_EXISTS: return fs::error::exist;
|
||||
case ERROR_FILE_EXISTS: return fs::error::exist;
|
||||
case ERROR_NEGATIVE_SEEK: return fs::error::inval;
|
||||
case ERROR_DIRECTORY: return fs::error::inval;
|
||||
case ERROR_INVALID_NAME: return fs::error::inval;
|
||||
default: fmt::throw_exception("Unknown Win32 error: %u.", e);
|
||||
default: throw fmt::exception("Unknown Win32 error: %u.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,20 +87,16 @@ static fs::error to_error(DWORD e)
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/statvfs.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <libgen.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <utime.h>
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#if defined(__APPLE__) || defined(__FreeBSD__)
|
||||
#include <copyfile.h>
|
||||
#include <mach-o/dyld.h>
|
||||
#elif defined(__DragonFly__) || defined(__FreeBSD__)
|
||||
#include <sys/socket.h> // sendfile
|
||||
#elif defined(__linux__) || defined(__sun)
|
||||
#else
|
||||
#include <sys/sendfile.h>
|
||||
#endif
|
||||
|
||||
@@ -126,8 +107,7 @@ static fs::error to_error(int e)
|
||||
case ENOENT: return fs::error::noent;
|
||||
case EEXIST: return fs::error::exist;
|
||||
case EINVAL: return fs::error::inval;
|
||||
case EACCES: return fs::error::acces;
|
||||
default: fmt::throw_exception("Unknown system error: %d.", e);
|
||||
default: throw fmt::exception("Unknown system error: %d.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,28 +134,6 @@ namespace fs
|
||||
static device_manager instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
file_base::~file_base()
|
||||
{
|
||||
}
|
||||
|
||||
stat_t file_base::stat()
|
||||
{
|
||||
fmt::throw_exception("fs::file::stat() not supported for %s", typeid(*this).name());
|
||||
}
|
||||
|
||||
void file_base::sync()
|
||||
{
|
||||
// Do notning
|
||||
}
|
||||
|
||||
dir_base::~dir_base()
|
||||
{
|
||||
}
|
||||
|
||||
device_base::~device_base()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<fs::device_base> fs::device_manager::get_device(const std::string& path)
|
||||
@@ -212,7 +170,7 @@ std::shared_ptr<fs::device_base> fs::get_virtual_device(const std::string& path)
|
||||
|
||||
std::shared_ptr<fs::device_base> fs::set_virtual_device(const std::string& name, const std::shared_ptr<device_base>& device)
|
||||
{
|
||||
verify(HERE), name.size() > 2, name[0] == '/', name[1] == '/', name.find('/', 2) == -1;
|
||||
EXPECTS(name.size() > 2 && name[0] == '/' && name[1] == '/' && name.find('/', 2) == -1);
|
||||
|
||||
return get_device_manager().set_device(name, device);
|
||||
}
|
||||
@@ -241,6 +199,33 @@ std::string fs::get_parent_dir(const std::string& path)
|
||||
}
|
||||
}
|
||||
|
||||
static const auto test_get_parent_dir = []() -> bool
|
||||
{
|
||||
// Success:
|
||||
VERIFY(fs::get_parent_dir("/x/y///") == "/x");
|
||||
VERIFY(fs::get_parent_dir("/x/y/") == "/x");
|
||||
VERIFY(fs::get_parent_dir("/x/y") == "/x");
|
||||
VERIFY(fs::get_parent_dir("x:/y") == "x:");
|
||||
VERIFY(fs::get_parent_dir("//x/y") == "//x");
|
||||
|
||||
// Failure:
|
||||
VERIFY(fs::get_parent_dir("").empty());
|
||||
VERIFY(fs::get_parent_dir("x/").empty());
|
||||
VERIFY(fs::get_parent_dir("x").empty());
|
||||
VERIFY(fs::get_parent_dir("x///").empty());
|
||||
VERIFY(fs::get_parent_dir("/x/").empty());
|
||||
VERIFY(fs::get_parent_dir("/x").empty());
|
||||
VERIFY(fs::get_parent_dir("/").empty());
|
||||
VERIFY(fs::get_parent_dir("//").empty());
|
||||
VERIFY(fs::get_parent_dir("//x").empty());
|
||||
VERIFY(fs::get_parent_dir("//x/").empty());
|
||||
VERIFY(fs::get_parent_dir("///").empty());
|
||||
VERIFY(fs::get_parent_dir("///x").empty());
|
||||
VERIFY(fs::get_parent_dir("///x/").empty());
|
||||
|
||||
return false;
|
||||
}();
|
||||
|
||||
bool fs::stat(const std::string& path, stat_t& info)
|
||||
{
|
||||
if (auto device = get_virtual_device(path))
|
||||
@@ -406,45 +391,6 @@ bool fs::is_dir(const std::string& path)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fs::statfs(const std::string& path, fs::device_stat& info)
|
||||
{
|
||||
if (auto device = get_virtual_device(path))
|
||||
{
|
||||
return device->statfs(path, info);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
ULARGE_INTEGER avail_free;
|
||||
ULARGE_INTEGER total_size;
|
||||
ULARGE_INTEGER total_free;
|
||||
|
||||
if (!GetDiskFreeSpaceExW(to_wchar(path).get(), &avail_free, &total_size, &total_free))
|
||||
{
|
||||
g_tls_error = to_error(GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
info.block_size = 4096; // TODO
|
||||
info.total_size = total_size.QuadPart;
|
||||
info.total_free = total_free.QuadPart;
|
||||
info.avail_free = avail_free.QuadPart;
|
||||
#else
|
||||
struct ::statvfs buf;
|
||||
if (::statvfs(path.c_str(), &buf) != 0)
|
||||
{
|
||||
g_tls_error = to_error(errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
info.block_size = buf.f_frsize;
|
||||
info.total_size = info.block_size * buf.f_blocks;
|
||||
info.total_free = info.block_size * buf.f_bfree;
|
||||
info.avail_free = info.block_size * buf.f_bavail;
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fs::create_dir(const std::string& path)
|
||||
{
|
||||
if (auto device = get_virtual_device(path))
|
||||
@@ -515,7 +461,7 @@ bool fs::rename(const std::string& from, const std::string& to)
|
||||
|
||||
if (device != get_virtual_device(to))
|
||||
{
|
||||
fmt::throw_exception("fs::rename() between different devices not implemented.\nFrom: %s\nTo: %s", from, to);
|
||||
throw fmt::exception("fs::rename() between different devices not implemented.\nFrom: %s\nTo: %s", from, to);
|
||||
}
|
||||
|
||||
if (device)
|
||||
@@ -548,7 +494,7 @@ bool fs::copy_file(const std::string& from, const std::string& to, bool overwrit
|
||||
|
||||
if (device != get_virtual_device(to) || device) // TODO
|
||||
{
|
||||
fmt::throw_exception("fs::copy_file() for virtual devices not implemented.\nFrom: %s\nTo: %s", from, to);
|
||||
throw fmt::exception("fs::copy_file() for virtual devices not implemented.\nFrom: %s\nTo: %s", from, to);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -580,20 +526,14 @@ bool fs::copy_file(const std::string& from, const std::string& to, bool overwrit
|
||||
}
|
||||
|
||||
// Here we use kernel-space copying for performance reasons
|
||||
#if defined(__APPLE__)
|
||||
// fcopyfile works on OS X 10.5+
|
||||
#if defined(__APPLE__) || defined(__FreeBSD__)
|
||||
// fcopyfile works on FreeBSD and OS X 10.5+
|
||||
if (::fcopyfile(input, output, 0, COPYFILE_ALL))
|
||||
#elif defined(__DragonFly__) || defined(__FreeBSD__)
|
||||
if (::sendfile(input, output, 0, 0, NULL, NULL, 0))
|
||||
#elif defined(__linux__) || defined(__sun)
|
||||
#else
|
||||
// sendfile will work with non-socket output (i.e. regular file) on Linux 2.6.33+
|
||||
off_t bytes_copied = 0;
|
||||
struct ::stat fileinfo = { 0 };
|
||||
if (::fstat(input, &fileinfo) || ::sendfile(output, input, &bytes_copied, fileinfo.st_size))
|
||||
#else // NetBSD, OpenBSD, etc.
|
||||
fmt::throw_exception("fs::copy_file() isn't implemented for this platform.\nFrom: %s\nTo: %s", from, to);
|
||||
errno = 0;
|
||||
if (true)
|
||||
#endif
|
||||
{
|
||||
const int err = errno;
|
||||
@@ -676,92 +616,50 @@ bool fs::truncate_file(const std::string& path, u64 length)
|
||||
#endif
|
||||
}
|
||||
|
||||
bool fs::utime(const std::string& path, s64 atime, s64 mtime)
|
||||
{
|
||||
if (auto device = get_virtual_device(path))
|
||||
{
|
||||
return device->utime(path, atime, mtime);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// Open the file
|
||||
const auto handle = CreateFileW(to_wchar(path).get(), FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
g_tls_error = to_error(GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
FILETIME _atime = from_time(atime);
|
||||
FILETIME _mtime = from_time(mtime);
|
||||
if (!SetFileTime(handle, nullptr, &_atime, &_mtime))
|
||||
{
|
||||
g_tls_error = to_error(GetLastError());
|
||||
CloseHandle(handle);
|
||||
return false;
|
||||
}
|
||||
|
||||
CloseHandle(handle);
|
||||
return true;
|
||||
#else
|
||||
::utimbuf buf;
|
||||
buf.actime = atime;
|
||||
buf.modtime = mtime;
|
||||
|
||||
if (::utime(path.c_str(), &buf) != 0)
|
||||
{
|
||||
g_tls_error = to_error(errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void fs::file::xnull() const
|
||||
{
|
||||
fmt::throw_exception<std::logic_error>("fs::file is null");
|
||||
throw std::logic_error("fs::file is null");
|
||||
}
|
||||
|
||||
void fs::file::xfail() const
|
||||
{
|
||||
fmt::throw_exception("Unexpected fs::error %s", g_tls_error);
|
||||
throw fmt::exception("Unexpected fs::error %u", g_tls_error);
|
||||
}
|
||||
|
||||
fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
bool fs::file::open(const std::string& path, bitset_t<open_mode> mode)
|
||||
{
|
||||
if (auto device = get_virtual_device(path))
|
||||
{
|
||||
if (auto&& _file = device->open(path, mode))
|
||||
{
|
||||
m_file = std::move(_file);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
DWORD access = 0;
|
||||
if (test(mode & fs::read)) access |= GENERIC_READ;
|
||||
if (test(mode & fs::write)) access |= test(mode & fs::append) ? FILE_APPEND_DATA : GENERIC_WRITE;
|
||||
if (mode & fs::read) access |= GENERIC_READ;
|
||||
if (mode & fs::write) access |= mode & fs::append ? FILE_APPEND_DATA : GENERIC_WRITE;
|
||||
|
||||
DWORD disp = 0;
|
||||
if (test(mode & fs::create))
|
||||
if (mode & fs::create)
|
||||
{
|
||||
disp =
|
||||
test(mode & fs::excl) ? CREATE_NEW :
|
||||
test(mode & fs::trunc) ? CREATE_ALWAYS : OPEN_ALWAYS;
|
||||
mode & fs::excl ? CREATE_NEW :
|
||||
mode & fs::trunc ? CREATE_ALWAYS : OPEN_ALWAYS;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (test(mode & fs::excl))
|
||||
if (mode & fs::excl)
|
||||
{
|
||||
g_tls_error = error::inval;
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
disp = test(mode & fs::trunc) ? TRUNCATE_EXISTING : OPEN_EXISTING;
|
||||
disp = mode & fs::trunc ? TRUNCATE_EXISTING : OPEN_EXISTING;
|
||||
}
|
||||
|
||||
const HANDLE handle = CreateFileW(to_wchar(path).get(), access, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, disp, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
@@ -769,7 +667,7 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
if (handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
g_tls_error = to_error(GetLastError());
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
class windows_file final : public file_base
|
||||
@@ -790,7 +688,10 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
stat_t stat() override
|
||||
{
|
||||
FILE_BASIC_INFO basic_info;
|
||||
verify("file::stat" HERE), GetFileInformationByHandleEx(m_handle, FileBasicInfo, &basic_info, sizeof(FILE_BASIC_INFO));
|
||||
if (!GetFileInformationByHandleEx(m_handle, FileBasicInfo, &basic_info, sizeof(FILE_BASIC_INFO)))
|
||||
{
|
||||
throw fmt::exception("Win32 error: %u." HERE, GetLastError());
|
||||
}
|
||||
|
||||
stat_t info;
|
||||
info.is_directory = (basic_info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
||||
@@ -803,11 +704,6 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
return info;
|
||||
}
|
||||
|
||||
void sync() override
|
||||
{
|
||||
verify("file::sync" HERE), FlushFileBuffers(m_handle);
|
||||
}
|
||||
|
||||
bool trunc(u64 length) override
|
||||
{
|
||||
LARGE_INTEGER old, pos;
|
||||
@@ -826,17 +722,27 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
return false;
|
||||
}
|
||||
|
||||
verify("file::trunc" HERE), SetEndOfFile(m_handle), SetFilePointerEx(m_handle, old, NULL, FILE_BEGIN);
|
||||
return true;
|
||||
const BOOL result = SetEndOfFile(m_handle); // change file size
|
||||
|
||||
if (!result || !SetFilePointerEx(m_handle, old, NULL, FILE_BEGIN)) // restore position
|
||||
{
|
||||
throw fmt::exception("Win32 error: %u." HERE, GetLastError());
|
||||
}
|
||||
|
||||
return result != FALSE;
|
||||
}
|
||||
|
||||
u64 read(void* buffer, u64 count) override
|
||||
{
|
||||
// TODO (call ReadFile multiple times if count is too big)
|
||||
const int size = narrow<int>(count, "file::read" HERE);
|
||||
const int size = ::narrow<int>(count, "Too big count" HERE);
|
||||
EXPECTS(size >= 0);
|
||||
|
||||
DWORD nread;
|
||||
verify("file::read" HERE), ReadFile(m_handle, buffer, size, &nread, NULL);
|
||||
if (!ReadFile(m_handle, buffer, size, &nread, NULL))
|
||||
{
|
||||
throw fmt::exception("Win32 error: %u." HERE, GetLastError());
|
||||
}
|
||||
|
||||
return nread;
|
||||
}
|
||||
@@ -844,10 +750,14 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
u64 write(const void* buffer, u64 count) override
|
||||
{
|
||||
// TODO (call WriteFile multiple times if count is too big)
|
||||
const int size = narrow<int>(count, "file::write" HERE);
|
||||
const int size = ::narrow<int>(count, "Too big count" HERE);
|
||||
EXPECTS(size >= 0);
|
||||
|
||||
DWORD nwritten;
|
||||
verify("file::write" HERE), WriteFile(m_handle, buffer, size, &nwritten, NULL);
|
||||
if (!WriteFile(m_handle, buffer, size, &nwritten, NULL))
|
||||
{
|
||||
throw fmt::exception("Win32 error: %u." HERE, GetLastError());
|
||||
}
|
||||
|
||||
return nwritten;
|
||||
}
|
||||
@@ -861,12 +771,11 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
whence == seek_set ? FILE_BEGIN :
|
||||
whence == seek_cur ? FILE_CURRENT :
|
||||
whence == seek_end ? FILE_END :
|
||||
(fmt::throw_exception("Invalid whence (0x%x)" HERE, whence), 0);
|
||||
throw fmt::exception("Invalid whence (0x%x)" HERE, whence);
|
||||
|
||||
if (!SetFilePointerEx(m_handle, pos, &pos, mode))
|
||||
{
|
||||
g_tls_error = to_error(GetLastError());
|
||||
return -1;
|
||||
throw fmt::exception("Win32 error: %u." HERE, GetLastError());
|
||||
}
|
||||
|
||||
return pos.QuadPart;
|
||||
@@ -875,7 +784,10 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
u64 size() override
|
||||
{
|
||||
LARGE_INTEGER size;
|
||||
verify("file::size" HERE), GetFileSizeEx(m_handle, &size);
|
||||
if (!GetFileSizeEx(m_handle, &size))
|
||||
{
|
||||
throw fmt::exception("Win32 error: %u." HERE, GetLastError());
|
||||
}
|
||||
|
||||
return size.QuadPart;
|
||||
}
|
||||
@@ -885,21 +797,21 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
#else
|
||||
int flags = 0;
|
||||
|
||||
if (test(mode & fs::read) && test(mode & fs::write)) flags |= O_RDWR;
|
||||
else if (test(mode & fs::read)) flags |= O_RDONLY;
|
||||
else if (test(mode & fs::write)) flags |= O_WRONLY;
|
||||
if (mode & fs::read && mode & fs::write) flags |= O_RDWR;
|
||||
else if (mode & fs::read) flags |= O_RDONLY;
|
||||
else if (mode & fs::write) flags |= O_WRONLY;
|
||||
|
||||
if (test(mode & fs::append)) flags |= O_APPEND;
|
||||
if (test(mode & fs::create)) flags |= O_CREAT;
|
||||
if (test(mode & fs::trunc)) flags |= O_TRUNC;
|
||||
if (test(mode & fs::excl)) flags |= O_EXCL;
|
||||
if (mode & fs::append) flags |= O_APPEND;
|
||||
if (mode & fs::create) flags |= O_CREAT;
|
||||
if (mode & fs::trunc) flags |= O_TRUNC;
|
||||
if (mode & fs::excl) flags |= O_EXCL;
|
||||
|
||||
const int fd = ::open(path.c_str(), flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||
|
||||
if (fd == -1)
|
||||
{
|
||||
g_tls_error = to_error(errno);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
class unix_file final : public file_base
|
||||
@@ -920,7 +832,10 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
stat_t stat() override
|
||||
{
|
||||
struct ::stat file_info;
|
||||
verify("file::stat" HERE), ::fstat(m_fd, &file_info) == 0;
|
||||
if (::fstat(m_fd, &file_info) != 0)
|
||||
{
|
||||
throw fmt::exception("System error: %d." HERE, errno);
|
||||
}
|
||||
|
||||
stat_t info;
|
||||
info.is_directory = S_ISDIR(file_info.st_mode);
|
||||
@@ -933,11 +848,6 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
return info;
|
||||
}
|
||||
|
||||
void sync() override
|
||||
{
|
||||
verify("file::sync" HERE), ::fsync(m_fd) == 0;
|
||||
}
|
||||
|
||||
bool trunc(u64 length) override
|
||||
{
|
||||
if (::ftruncate(m_fd, length) != 0)
|
||||
@@ -952,7 +862,10 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
u64 read(void* buffer, u64 count) override
|
||||
{
|
||||
const auto result = ::read(m_fd, buffer, count);
|
||||
verify("file::read" HERE), result != -1;
|
||||
if (result == -1)
|
||||
{
|
||||
throw fmt::exception("System error: %d." HERE, errno);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -960,7 +873,10 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
u64 write(const void* buffer, u64 count) override
|
||||
{
|
||||
const auto result = ::write(m_fd, buffer, count);
|
||||
verify("file::write" HERE), result != -1;
|
||||
if (result == -1)
|
||||
{
|
||||
throw fmt::exception("System error: %d." HERE, errno);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -971,14 +887,12 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
whence == seek_set ? SEEK_SET :
|
||||
whence == seek_cur ? SEEK_CUR :
|
||||
whence == seek_end ? SEEK_END :
|
||||
(fmt::throw_exception("Invalid whence (0x%x)" HERE, whence), 0);
|
||||
throw fmt::exception("Invalid whence (0x%x)" HERE, whence);
|
||||
|
||||
const auto result = ::lseek(m_fd, offset, mode);
|
||||
|
||||
if (result == -1)
|
||||
{
|
||||
g_tls_error = to_error(errno);
|
||||
return -1;
|
||||
throw fmt::exception("System error: %d." HERE, errno);
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -987,7 +901,10 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
u64 size() override
|
||||
{
|
||||
struct ::stat file_info;
|
||||
verify("file::size" HERE), ::fstat(m_fd, &file_info) == 0;
|
||||
if (::fstat(m_fd, &file_info) != 0)
|
||||
{
|
||||
throw fmt::exception("System error: %d." HERE, errno);
|
||||
}
|
||||
|
||||
return file_info.st_size;
|
||||
}
|
||||
@@ -995,6 +912,8 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
|
||||
m_file = std::make_unique<unix_file>(fd);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
fs::file::file(const void* ptr, std::size_t size)
|
||||
@@ -1013,48 +932,37 @@ fs::file::file(const void* ptr, std::size_t size)
|
||||
{
|
||||
}
|
||||
|
||||
fs::stat_t stat() override
|
||||
{
|
||||
throw std::logic_error("Not supported" HERE);
|
||||
}
|
||||
|
||||
bool trunc(u64 length) override
|
||||
{
|
||||
return false;
|
||||
throw std::logic_error("Not allowed" HERE);
|
||||
}
|
||||
|
||||
u64 read(void* buffer, u64 count) override
|
||||
{
|
||||
if (m_pos < m_size)
|
||||
{
|
||||
// Get readable size
|
||||
if (const u64 result = std::min<u64>(count, m_size - m_pos))
|
||||
{
|
||||
std::memcpy(buffer, m_ptr + m_pos, result);
|
||||
m_pos += result;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
const u64 start = m_pos;
|
||||
const u64 end = seek(count, fs::seek_cur);
|
||||
const u64 read_size = end >= start ? end - start : throw std::logic_error("Stream overflow" HERE);
|
||||
std::memcpy(buffer, m_ptr + start, read_size);
|
||||
return read_size;
|
||||
}
|
||||
|
||||
u64 write(const void* buffer, u64 count) override
|
||||
{
|
||||
return 0;
|
||||
throw std::logic_error("Not allowed" HERE);
|
||||
}
|
||||
|
||||
u64 seek(s64 offset, fs::seek_mode whence) override
|
||||
{
|
||||
const s64 new_pos =
|
||||
whence == fs::seek_set ? offset :
|
||||
whence == fs::seek_cur ? offset + m_pos :
|
||||
whence == fs::seek_end ? offset + size() :
|
||||
(fmt::raw_error("fs::file::memory_stream::seek(): invalid whence"), 0);
|
||||
|
||||
if (new_pos < 0)
|
||||
{
|
||||
fs::g_tls_error = fs::error::inval;
|
||||
return -1;
|
||||
}
|
||||
|
||||
m_pos = new_pos;
|
||||
return m_pos;
|
||||
return
|
||||
whence == fs::seek_set ? m_pos = std::min<u64>(offset, m_size) :
|
||||
whence == fs::seek_cur ? m_pos = std::min<u64>(offset + m_pos, m_size) :
|
||||
whence == fs::seek_end ? m_pos = std::min<u64>(offset + m_size, m_size) :
|
||||
throw fmt::exception("Invalid whence (0x%x)" HERE, whence);
|
||||
}
|
||||
|
||||
u64 size() override
|
||||
@@ -1068,7 +976,7 @@ fs::file::file(const void* ptr, std::size_t size)
|
||||
|
||||
void fs::dir::xnull() const
|
||||
{
|
||||
fmt::throw_exception<std::logic_error>("fs::dir is null");
|
||||
throw std::logic_error("fs::dir is null");
|
||||
}
|
||||
|
||||
bool fs::dir::open(const std::string& path)
|
||||
@@ -1086,7 +994,7 @@ bool fs::dir::open(const std::string& path)
|
||||
|
||||
#ifdef _WIN32
|
||||
WIN32_FIND_DATAW found;
|
||||
const auto handle = FindFirstFileExW(to_wchar(path + "/*").get(), FindExInfoBasic, &found, FindExSearchNameMatch, NULL, FIND_FIRST_EX_CASE_SENSITIVE | FIND_FIRST_EX_LARGE_FETCH);
|
||||
const auto handle = FindFirstFileW(to_wchar(path + "/*").get(), &found);
|
||||
|
||||
if (handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
@@ -1096,6 +1004,8 @@ bool fs::dir::open(const std::string& path)
|
||||
|
||||
class windows_dir final : public dir_base
|
||||
{
|
||||
const HANDLE m_handle;
|
||||
|
||||
std::vector<dir_entry> m_entries;
|
||||
std::size_t m_pos = 0;
|
||||
|
||||
@@ -1115,24 +1025,33 @@ bool fs::dir::open(const std::string& path)
|
||||
}
|
||||
|
||||
public:
|
||||
windows_dir(HANDLE handle, WIN32_FIND_DATAW& found)
|
||||
windows_dir(HANDLE handle, const WIN32_FIND_DATAW& found)
|
||||
: m_handle(handle)
|
||||
{
|
||||
add_entry(found);
|
||||
}
|
||||
|
||||
while (FindNextFileW(handle, &found))
|
||||
{
|
||||
add_entry(found);
|
||||
}
|
||||
|
||||
verify("dir::read" HERE), ERROR_NO_MORE_FILES == GetLastError();
|
||||
FindClose(handle);
|
||||
~windows_dir()
|
||||
{
|
||||
FindClose(m_handle);
|
||||
}
|
||||
|
||||
bool read(dir_entry& out) override
|
||||
{
|
||||
if (m_pos >= m_entries.size())
|
||||
if (m_pos == m_entries.size())
|
||||
{
|
||||
return false;
|
||||
WIN32_FIND_DATAW found;
|
||||
if (!FindNextFileW(m_handle, &found))
|
||||
{
|
||||
if (ERROR_NO_MORE_FILES == GetLastError())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
throw fmt::exception("Win32 error: %u." HERE, GetLastError());
|
||||
}
|
||||
|
||||
add_entry(found);
|
||||
}
|
||||
|
||||
out = m_entries[m_pos++];
|
||||
@@ -1179,7 +1098,10 @@ bool fs::dir::open(const std::string& path)
|
||||
}
|
||||
|
||||
struct ::stat file_info;
|
||||
verify("dir::read" HERE), ::fstatat(::dirfd(m_dd), found->d_name, &file_info, 0) == 0;
|
||||
if (::fstatat(::dirfd(m_dd), found->d_name, &file_info, 0) != 0)
|
||||
{
|
||||
throw fmt::exception("System error: %d." HERE, errno);
|
||||
}
|
||||
|
||||
info.name = found->d_name;
|
||||
info.is_directory = S_ISDIR(file_info.st_mode);
|
||||
@@ -1209,22 +1131,11 @@ const std::string& fs::get_config_dir()
|
||||
// Use magic static
|
||||
static const std::string s_dir = []
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return get_executable_dir(); // ?
|
||||
#else
|
||||
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: error %u.", GetLastError()).c_str(), "fs::get_config_dir()", MB_ICONERROR);
|
||||
return dir; // empty
|
||||
}
|
||||
|
||||
to_utf8(dir, buf); // Convert to UTF-8
|
||||
|
||||
std::replace(dir.begin(), dir.end(), '\\', '/');
|
||||
|
||||
dir.resize(dir.rfind('/') + 1);
|
||||
#else
|
||||
if (const char* home = ::getenv("XDG_CONFIG_HOME"))
|
||||
dir = home;
|
||||
else if (const char* home = ::getenv("HOME"))
|
||||
@@ -1237,85 +1148,63 @@ const std::string& fs::get_config_dir()
|
||||
if (!is_dir(dir) && !create_path(dir))
|
||||
{
|
||||
std::printf("Failed to create configuration directory '%s' (%d).\n", dir.c_str(), errno);
|
||||
return get_executable_dir();
|
||||
}
|
||||
#endif
|
||||
|
||||
return dir;
|
||||
#endif
|
||||
}();
|
||||
|
||||
return s_dir;
|
||||
}
|
||||
|
||||
std::string fs::get_data_dir(const std::string& prefix, const std::string& location, const std::string& suffix)
|
||||
const std::string& fs::get_executable_dir()
|
||||
{
|
||||
// Use magic static
|
||||
static const std::string s_dir = []
|
||||
{
|
||||
const std::string& dir = get_config_dir() + "data/";
|
||||
std::string dir;
|
||||
|
||||
if (!is_dir(dir) && !create_path(dir))
|
||||
#ifdef _WIN32
|
||||
wchar_t buf[2048];
|
||||
if (GetModuleFileName(NULL, buf, ::size32(buf)) - 1 >= ::size32(buf) - 1)
|
||||
{
|
||||
return get_config_dir();
|
||||
MessageBoxA(0, fmt::format("GetModuleFileName() failed: error %u.", GetLastError()).c_str(), "fs::get_executable_dir()", MB_ICONERROR);
|
||||
return dir; // empty
|
||||
}
|
||||
|
||||
to_utf8(dir, buf); // Convert to UTF-8
|
||||
|
||||
std::replace(dir.begin(), dir.end(), '\\', '/');
|
||||
|
||||
#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.assign(buf, size);
|
||||
#endif
|
||||
|
||||
// Leave only path
|
||||
dir.resize(dir.rfind('/') + 1);
|
||||
return dir;
|
||||
}();
|
||||
|
||||
std::vector<u8> buf;
|
||||
buf.reserve(location.size() + 1);
|
||||
|
||||
// Normalize location
|
||||
for (char c : location)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (c == '/' || c == '\\')
|
||||
#else
|
||||
if (c == '/')
|
||||
#endif
|
||||
{
|
||||
if (buf.empty() || buf.back() != '/')
|
||||
{
|
||||
buf.push_back('/');
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
buf.push_back(c);
|
||||
}
|
||||
|
||||
// Calculate hash
|
||||
u8 hash[20];
|
||||
sha1(buf.data(), buf.size(), hash);
|
||||
|
||||
// Concatenate
|
||||
std::string&& result = fmt::format("%s%s/%016llx%08x-%s/", s_dir, prefix, reinterpret_cast<be_t<u64>&>(hash[0]), reinterpret_cast<be_t<u32>&>(hash[8]), suffix);
|
||||
|
||||
if (!is_dir(result))
|
||||
{
|
||||
// Create dir if necessary
|
||||
if (create_path(result))
|
||||
{
|
||||
// Acknowledge original location
|
||||
file(result + ".location", rewrite).write(buf);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string fs::get_data_dir(const std::string& prefix, const std::string& path)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
const auto& delim = "/\\";
|
||||
#else
|
||||
const auto& delim = "/";
|
||||
#endif
|
||||
|
||||
// Extract file name and location
|
||||
const std::string& location = fs::get_parent_dir(path);
|
||||
const std::size_t name_pos = path.find_first_not_of(delim, location.size());
|
||||
|
||||
return fs::get_data_dir(prefix, location, name_pos == -1 ? std::string{} : path.substr(name_pos));
|
||||
return s_dir;
|
||||
}
|
||||
|
||||
void fs::remove_all(const std::string& path, bool remove_root)
|
||||
@@ -1368,38 +1257,3 @@ u64 fs::get_dir_size(const std::string& path)
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template<>
|
||||
void fmt_class_string<fs::seek_mode>::format(std::string& out, u64 arg)
|
||||
{
|
||||
format_enum(out, arg, [](auto arg)
|
||||
{
|
||||
switch (arg)
|
||||
{
|
||||
STR_CASE(fs::seek_mode::seek_set);
|
||||
STR_CASE(fs::seek_mode::seek_cur);
|
||||
STR_CASE(fs::seek_mode::seek_end);
|
||||
}
|
||||
|
||||
return unknown;
|
||||
});
|
||||
}
|
||||
|
||||
template<>
|
||||
void fmt_class_string<fs::error>::format(std::string& out, u64 arg)
|
||||
{
|
||||
format_enum(out, arg, [](auto arg)
|
||||
{
|
||||
switch (arg)
|
||||
{
|
||||
case fs::error::ok: return "OK";
|
||||
|
||||
case fs::error::inval: return "Invalid arguments";
|
||||
case fs::error::noent: return "Not found";
|
||||
case fs::error::exist: return "Already exists";
|
||||
case fs::error::acces: return "Access violation";
|
||||
}
|
||||
|
||||
return unknown;
|
||||
});
|
||||
}
|
||||
|
||||
+26
-175
@@ -1,17 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "bit_set.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <type_traits>
|
||||
|
||||
#include "types.h"
|
||||
#include "BitSet.h"
|
||||
|
||||
namespace fs
|
||||
{
|
||||
// File open mode flags
|
||||
enum class open_mode : u32
|
||||
enum struct open_mode : u32
|
||||
{
|
||||
read,
|
||||
write,
|
||||
@@ -19,18 +19,16 @@ namespace fs
|
||||
create,
|
||||
trunc,
|
||||
excl,
|
||||
|
||||
__bitset_enum_max
|
||||
};
|
||||
|
||||
constexpr auto read = +open_mode::read; // Enable reading
|
||||
constexpr auto write = +open_mode::write; // Enable writing
|
||||
constexpr auto append = +open_mode::append; // Always append to the end of the file
|
||||
constexpr auto create = +open_mode::create; // Create file if it doesn't exist
|
||||
constexpr auto trunc = +open_mode::trunc; // Clear opened file if it's not empty
|
||||
constexpr auto excl = +open_mode::excl; // Failure if the file already exists (used with `create`)
|
||||
constexpr bitset_t<open_mode> read = open_mode::read; // Enable reading
|
||||
constexpr bitset_t<open_mode> write = open_mode::write; // Enable writing
|
||||
constexpr bitset_t<open_mode> append = open_mode::append; // Always append to the end of the file
|
||||
constexpr bitset_t<open_mode> create = open_mode::create; // Create file if it doesn't exist
|
||||
constexpr bitset_t<open_mode> trunc = open_mode::trunc; // Clear opened file if it's not empty
|
||||
constexpr bitset_t<open_mode> excl = open_mode::excl; // Failure if the file already exists (used with `create`)
|
||||
|
||||
constexpr auto rewrite = open_mode::write + open_mode::create + open_mode::trunc;
|
||||
constexpr bitset_t<open_mode> rewrite = write + create + trunc;
|
||||
|
||||
// File seek mode
|
||||
enum class seek_mode : u32
|
||||
@@ -58,10 +56,9 @@ namespace fs
|
||||
// File handle base
|
||||
struct file_base
|
||||
{
|
||||
virtual ~file_base();
|
||||
virtual ~file_base() = default;
|
||||
|
||||
virtual stat_t stat();
|
||||
virtual void sync();
|
||||
virtual stat_t stat() = 0;
|
||||
virtual bool trunc(u64 length) = 0;
|
||||
virtual u64 read(void* buffer, u64 size) = 0;
|
||||
virtual u64 write(const void* buffer, u64 size) = 0;
|
||||
@@ -78,36 +75,25 @@ namespace fs
|
||||
// Directory handle base
|
||||
struct dir_base
|
||||
{
|
||||
virtual ~dir_base();
|
||||
virtual ~dir_base() = default;
|
||||
|
||||
virtual bool read(dir_entry&) = 0;
|
||||
virtual void rewind() = 0;
|
||||
};
|
||||
|
||||
// Device information
|
||||
struct device_stat
|
||||
{
|
||||
u64 block_size;
|
||||
u64 total_size;
|
||||
u64 total_free; // Total size of free space
|
||||
u64 avail_free; // Free space available to unprivileged user
|
||||
};
|
||||
|
||||
// Virtual device
|
||||
struct device_base
|
||||
{
|
||||
virtual ~device_base();
|
||||
virtual ~device_base() = default;
|
||||
|
||||
virtual bool stat(const std::string& path, stat_t& info) = 0;
|
||||
virtual bool statfs(const std::string& path, device_stat& info) = 0;
|
||||
virtual bool remove_dir(const std::string& path) = 0;
|
||||
virtual bool create_dir(const std::string& path) = 0;
|
||||
virtual bool rename(const std::string& from, const std::string& to) = 0;
|
||||
virtual bool remove(const std::string& path) = 0;
|
||||
virtual bool trunc(const std::string& path, u64 length) = 0;
|
||||
virtual bool utime(const std::string& path, s64 atime, s64 mtime) = 0;
|
||||
|
||||
virtual std::unique_ptr<file_base> open(const std::string& path, bs_t<open_mode> mode) = 0;
|
||||
virtual std::unique_ptr<file_base> open(const std::string& path, bitset_t<open_mode> mode) = 0;
|
||||
virtual std::unique_ptr<dir_base> open_dir(const std::string& path) = 0;
|
||||
};
|
||||
|
||||
@@ -132,9 +118,6 @@ namespace fs
|
||||
// Check whether the directory exists and is NOT a file
|
||||
bool is_dir(const std::string& path);
|
||||
|
||||
// Get filesystem information
|
||||
bool statfs(const std::string& path, device_stat& info);
|
||||
|
||||
// Delete empty directory
|
||||
bool remove_dir(const std::string& path);
|
||||
|
||||
@@ -156,9 +139,6 @@ namespace fs
|
||||
// Change file size (possibly appending zeros)
|
||||
bool truncate_file(const std::string& path, u64 length);
|
||||
|
||||
// Set file access/modification time
|
||||
bool utime(const std::string& path, s64 atime, s64 mtime);
|
||||
|
||||
class file final
|
||||
{
|
||||
std::unique_ptr<file_base> m_file;
|
||||
@@ -171,19 +151,17 @@ namespace fs
|
||||
file() = default;
|
||||
|
||||
// Open file with specified mode
|
||||
explicit file(const std::string& path, bs_t<open_mode> mode = ::fs::read);
|
||||
explicit file(const std::string& path, bitset_t<open_mode> mode = ::fs::read)
|
||||
{
|
||||
open(path, mode);
|
||||
}
|
||||
|
||||
// Open file with specified mode
|
||||
bool open(const std::string& path, bitset_t<open_mode> mode = ::fs::read);
|
||||
|
||||
// Open memory for read
|
||||
explicit file(const void* ptr, std::size_t size);
|
||||
|
||||
// Open file with specified args (forward to constructor)
|
||||
template <typename... Args>
|
||||
bool open(Args&&... args)
|
||||
{
|
||||
*this = fs::file(std::forward<Args>(args)...);
|
||||
return m_file.operator bool();
|
||||
}
|
||||
|
||||
// Check whether the handle is valid (opened file)
|
||||
explicit operator bool() const
|
||||
{
|
||||
@@ -220,13 +198,6 @@ namespace fs
|
||||
return m_file->stat();
|
||||
}
|
||||
|
||||
// Sync file buffers
|
||||
void sync() const
|
||||
{
|
||||
if (!m_file) xnull();
|
||||
return m_file->sync();
|
||||
}
|
||||
|
||||
// Read the data from the file and return the amount of data written in buffer
|
||||
u64 read(void* buffer, u64 count) const
|
||||
{
|
||||
@@ -465,11 +436,8 @@ namespace fs
|
||||
// Get configuration directory
|
||||
const std::string& get_config_dir();
|
||||
|
||||
// Get data/cache directory for specified prefix and suffix
|
||||
std::string get_data_dir(const std::string& prefix, const std::string& location, const std::string& suffix);
|
||||
|
||||
// Get data/cache directory for specified prefix and path (suffix will be filename)
|
||||
std::string get_data_dir(const std::string& prefix, const std::string& path);
|
||||
// Get executable directory
|
||||
const std::string& get_executable_dir();
|
||||
|
||||
// Delete directory and all its contents recursively
|
||||
void remove_all(const std::string& path, bool remove_root = true);
|
||||
@@ -484,125 +452,8 @@ namespace fs
|
||||
inval,
|
||||
noent,
|
||||
exist,
|
||||
acces,
|
||||
};
|
||||
|
||||
// Error code returned
|
||||
extern thread_local error g_tls_error;
|
||||
|
||||
template <typename T>
|
||||
struct container_stream final : file_base
|
||||
{
|
||||
// T can be a reference, but this is not recommended
|
||||
using value_type = typename std::remove_reference_t<T>::value_type;
|
||||
|
||||
T obj;
|
||||
u64 pos;
|
||||
|
||||
container_stream(T&& obj)
|
||||
: obj(std::forward<T>(obj))
|
||||
, pos(0)
|
||||
{
|
||||
}
|
||||
|
||||
~container_stream() override
|
||||
{
|
||||
}
|
||||
|
||||
bool trunc(u64 length) override
|
||||
{
|
||||
obj.resize(length);
|
||||
return true;
|
||||
}
|
||||
|
||||
u64 read(void* buffer, u64 size) override
|
||||
{
|
||||
const u64 end = obj.size();
|
||||
|
||||
if (pos < end)
|
||||
{
|
||||
// Get readable size
|
||||
if (const u64 max = std::min<u64>(size, end - pos))
|
||||
{
|
||||
std::copy(obj.cbegin() + pos, obj.cbegin() + pos + max, static_cast<value_type*>(buffer));
|
||||
pos = pos + max;
|
||||
return max;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u64 write(const void* buffer, u64 size) override
|
||||
{
|
||||
const u64 old_size = obj.size();
|
||||
|
||||
if (old_size + size < old_size)
|
||||
{
|
||||
fmt::raw_error("fs::container_stream<>::write(): overflow");
|
||||
}
|
||||
|
||||
if (pos > old_size)
|
||||
{
|
||||
// Fill gap if necessary (default-initialized)
|
||||
obj.resize(pos);
|
||||
}
|
||||
|
||||
const auto src = static_cast<const value_type*>(buffer);
|
||||
|
||||
// Overwrite existing part
|
||||
const u64 overlap = std::min<u64>(obj.size() - pos, size);
|
||||
std::copy(src, src + overlap, obj.begin() + pos);
|
||||
|
||||
// Append new data
|
||||
obj.insert(obj.end(), src + overlap, src + size);
|
||||
pos += size;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
u64 seek(s64 offset, seek_mode whence) override
|
||||
{
|
||||
const s64 new_pos =
|
||||
whence == fs::seek_set ? offset :
|
||||
whence == fs::seek_cur ? offset + pos :
|
||||
whence == fs::seek_end ? offset + size() :
|
||||
(fmt::raw_error("fs::container_stream<>::seek(): invalid whence"), 0);
|
||||
|
||||
if (new_pos < 0)
|
||||
{
|
||||
fs::g_tls_error = fs::error::inval;
|
||||
return -1;
|
||||
}
|
||||
|
||||
pos = new_pos;
|
||||
return pos;
|
||||
}
|
||||
|
||||
u64 size() override
|
||||
{
|
||||
return obj.size();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
file make_stream(T&& container = T{})
|
||||
{
|
||||
file result;
|
||||
result.reset(std::make_unique<container_stream<T>>(std::forward<T>(container)));
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
bool write_file(const std::string& path, bs_t<fs::open_mode> mode, const Args&... args)
|
||||
{
|
||||
if (fs::file f{path, mode})
|
||||
{
|
||||
// Write args sequentially
|
||||
int seq[]{ (f.write(args), 0)... };
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,817 +0,0 @@
|
||||
#include "stdafx.h"
|
||||
#ifdef WITH_GDB_DEBUGGER
|
||||
|
||||
#include "GDBDebugServer.h"
|
||||
#include "Log.h"
|
||||
#include <algorithm>
|
||||
#include "Emu/Memory/Memory.h"
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/IdManager.h"
|
||||
#include "Emu/CPU/CPUThread.h"
|
||||
#include "Emu/Cell/PPUThread.h"
|
||||
#include "Emu/Cell/RawSPUThread.h"
|
||||
#include "Emu/Cell/SPUThread.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
#include"fcntl.h"
|
||||
#endif
|
||||
|
||||
extern void ppu_set_breakpoint(u32 addr);
|
||||
extern void ppu_remove_breakpoint(u32 addr);
|
||||
|
||||
logs::channel gdbDebugServer("gdbDebugServer");
|
||||
|
||||
int sock_init(void)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
WSADATA wsa_data;
|
||||
return WSAStartup(MAKEWORD(1, 1), &wsa_data);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
int sock_quit(void)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return WSACleanup();
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
int closesocket(socket_t s) {
|
||||
return close(s);
|
||||
}
|
||||
const int SOCKET_ERROR = -1;
|
||||
const socket_t INVALID_SOCKET = -1;
|
||||
#define sscanf_s sscanf
|
||||
#define HEX_U32 "x"
|
||||
#define HEX_U64 "lx"
|
||||
#else
|
||||
#define HEX_U32 "lx"
|
||||
#define HEX_U64 "llx"
|
||||
#endif
|
||||
|
||||
bool check_errno_again() {
|
||||
#ifdef _WIN32
|
||||
int err = GetLastError();
|
||||
return (err == WSAEWOULDBLOCK);
|
||||
#else
|
||||
int err = errno;
|
||||
return (err == EAGAIN) || (err == EWOULDBLOCK);
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string u32_to_hex(u32 i) {
|
||||
return fmt::format("%" HEX_U32, i);
|
||||
}
|
||||
|
||||
std::string u64_to_padded_hex(u64 value) {
|
||||
return fmt::format("%.16" HEX_U64, value);
|
||||
}
|
||||
|
||||
std::string u32_to_padded_hex(u32 value) {
|
||||
return fmt::format("%.8" HEX_U32, value);
|
||||
}
|
||||
|
||||
u8 hex_to_u8(std::string val) {
|
||||
u8 result;
|
||||
sscanf_s(val.c_str(), "%02hhX", &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
u32 hex_to_u32(std::string val) {
|
||||
u32 result;
|
||||
sscanf_s(val.c_str(), "%" HEX_U32, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
u64 hex_to_u64(std::string val) {
|
||||
u64 result;
|
||||
sscanf_s(val.c_str(), "%" HEX_U64, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
void GDBDebugServer::start_server()
|
||||
{
|
||||
server_socket = socket(AF_INET, SOCK_STREAM, 0);
|
||||
|
||||
if (server_socket == INVALID_SOCKET) {
|
||||
gdbDebugServer.error("Error creating server socket");
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
{
|
||||
int mode = 1;
|
||||
ioctlsocket(server_socket, FIONBIO, (u_long FAR *)&mode);
|
||||
}
|
||||
#else
|
||||
fcntl(server_socket, F_SETFL, fcntl(server_socket, F_GETFL) | O_NONBLOCK);
|
||||
#endif
|
||||
|
||||
int err;
|
||||
|
||||
sockaddr_in server_saddr;
|
||||
server_saddr.sin_family = AF_INET;
|
||||
int port = g_cfg.misc.gdb_server_port;
|
||||
server_saddr.sin_port = htons(port);
|
||||
server_saddr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
|
||||
err = bind(server_socket, (struct sockaddr *) &server_saddr, sizeof(server_saddr));
|
||||
if (err == SOCKET_ERROR) {
|
||||
gdbDebugServer.error("Error binding to port %d", port);
|
||||
return;
|
||||
}
|
||||
|
||||
err = listen(server_socket, 1);
|
||||
if (err == SOCKET_ERROR) {
|
||||
gdbDebugServer.error("Error listening on port %d", port);
|
||||
return;
|
||||
}
|
||||
|
||||
gdbDebugServer.success("GDB Debug Server listening on port %d", port);
|
||||
}
|
||||
|
||||
int GDBDebugServer::read(void * buf, int cnt)
|
||||
{
|
||||
while (!stop) {
|
||||
int result = recv(client_socket, reinterpret_cast<char*>(buf), cnt, 0);
|
||||
|
||||
if (result == SOCKET_ERROR) {
|
||||
if (check_errno_again()) {
|
||||
thread_ctrl::wait_for(50);
|
||||
continue;
|
||||
}
|
||||
|
||||
gdbDebugServer.error("Error during socket read");
|
||||
fmt::throw_exception("Error during socket read" HERE);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
char GDBDebugServer::read_char()
|
||||
{
|
||||
char result;
|
||||
int cnt = read(&result, 1);
|
||||
if (!cnt) {
|
||||
fmt::throw_exception("Tried to read char, but no data was available" HERE);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
u8 GDBDebugServer::read_hexbyte()
|
||||
{
|
||||
char buf[2];
|
||||
read(buf, 2);
|
||||
return static_cast<u8>(strtol(buf, nullptr, 16));
|
||||
}
|
||||
|
||||
void GDBDebugServer::try_read_cmd(gdb_cmd & out_cmd)
|
||||
{
|
||||
char c = read_char();
|
||||
//interrupt
|
||||
if (UNLIKELY(c == 0x03)) {
|
||||
out_cmd.cmd = "\0x03";
|
||||
out_cmd.data = "";
|
||||
out_cmd.checksum = 0;
|
||||
return;
|
||||
}
|
||||
if (UNLIKELY(c != '$')) {
|
||||
//gdb starts conversation with + for some reason
|
||||
if (c == '+') {
|
||||
c = read_char();
|
||||
}
|
||||
if (c != '$') {
|
||||
fmt::throw_exception("Expected start of packet character '$', got '%c' instead" HERE, c);
|
||||
}
|
||||
}
|
||||
//clear packet data
|
||||
out_cmd.cmd = "";
|
||||
out_cmd.data = "";
|
||||
out_cmd.checksum = 0;
|
||||
bool cmd_part = true;
|
||||
u8 checksum = 0;
|
||||
while(true) {
|
||||
c = read_char();
|
||||
if (c == '#') {
|
||||
break;
|
||||
}
|
||||
checksum = (checksum + reinterpret_cast<u8&>(c)) % 256;
|
||||
//escaped char
|
||||
if (c == '}') {
|
||||
c = read_char() ^ 0x20;
|
||||
checksum = (checksum + reinterpret_cast<u8&>(c)) % 256;
|
||||
}
|
||||
//cmd-data splitters
|
||||
if (cmd_part && ((c == ':') || (c == '.') || (c == ';'))) {
|
||||
cmd_part = false;
|
||||
}
|
||||
if (cmd_part) {
|
||||
out_cmd.cmd += c;
|
||||
//only q and v commands can have multi-char command
|
||||
if ((out_cmd.cmd.length() == 1) && (c != 'q') && (c != 'v')) {
|
||||
cmd_part = false;
|
||||
}
|
||||
} else {
|
||||
out_cmd.data += c;
|
||||
}
|
||||
}
|
||||
out_cmd.checksum = read_hexbyte();
|
||||
if (out_cmd.checksum != checksum) {
|
||||
throw new wrong_checksum_exception("Wrong checksum for packet" HERE);
|
||||
}
|
||||
}
|
||||
|
||||
bool GDBDebugServer::read_cmd(gdb_cmd & out_cmd)
|
||||
{
|
||||
while (true) {
|
||||
try {
|
||||
try_read_cmd(out_cmd);
|
||||
ack(true);
|
||||
return true;
|
||||
}
|
||||
catch (wrong_checksum_exception) {
|
||||
ack(false);
|
||||
}
|
||||
catch (std::runtime_error e) {
|
||||
gdbDebugServer.error(e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GDBDebugServer::send(const char * buf, int cnt)
|
||||
{
|
||||
gdbDebugServer.trace("Sending %s (%d bytes)", buf, cnt);
|
||||
while (!stop) {
|
||||
int res = ::send(client_socket, buf, cnt, 0);
|
||||
if (res == SOCKET_ERROR) {
|
||||
if (check_errno_again()) {
|
||||
thread_ctrl::wait_for(50);
|
||||
continue;
|
||||
}
|
||||
gdbDebugServer.error("Failed sending %d bytes", cnt);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void GDBDebugServer::send_char(char c)
|
||||
{
|
||||
send(&c, 1);
|
||||
}
|
||||
|
||||
void GDBDebugServer::ack(bool accepted)
|
||||
{
|
||||
send_char(accepted ? '+' : '-');
|
||||
}
|
||||
|
||||
void GDBDebugServer::send_cmd(const std::string & cmd)
|
||||
{
|
||||
u8 checksum = 0;
|
||||
std::string buf;
|
||||
buf.reserve(cmd.length() + 4);
|
||||
buf += "$";
|
||||
for (int i = 0; i < cmd.length(); ++i) {
|
||||
checksum = (checksum + append_encoded_char(cmd[i], buf)) % 256;
|
||||
}
|
||||
buf += "#";
|
||||
buf += to_hexbyte(checksum);
|
||||
send(buf.c_str(), static_cast<int>(buf.length()));
|
||||
}
|
||||
|
||||
bool GDBDebugServer::send_cmd_ack(const std::string & cmd)
|
||||
{
|
||||
while (true) {
|
||||
send_cmd(cmd);
|
||||
char c = read_char();
|
||||
if (LIKELY(c == '+')) {
|
||||
return true;
|
||||
}
|
||||
if (UNLIKELY(c != '-')) {
|
||||
gdbDebugServer.error("Wrong acknowledge character received %c", c);
|
||||
return false;
|
||||
}
|
||||
gdbDebugServer.warning("Client rejected our cmd");
|
||||
}
|
||||
}
|
||||
|
||||
u8 GDBDebugServer::append_encoded_char(char c, std::string & str)
|
||||
{
|
||||
u8 checksum = 0;
|
||||
if (UNLIKELY((c == '#') || (c == '$') || (c == '}'))) {
|
||||
str += '}';
|
||||
c ^= 0x20;
|
||||
checksum = '}';
|
||||
}
|
||||
checksum = (checksum + reinterpret_cast<u8&>(c)) % 256;
|
||||
str += c;
|
||||
return checksum;
|
||||
}
|
||||
|
||||
std::string GDBDebugServer::to_hexbyte(u8 i)
|
||||
{
|
||||
std::string result = "00";
|
||||
u8 i1 = i & 0xF;
|
||||
u8 i2 = i >> 4;
|
||||
result[0] = i2 > 9 ? 'a' + i2 - 10 : '0' + i2;
|
||||
result[1] = i1 > 9 ? 'a' + i1 - 10 : '0' + i1;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool GDBDebugServer::select_thread(u64 id)
|
||||
{
|
||||
//in case we have none at all
|
||||
selected_thread.reset();
|
||||
const auto on_select = [&](u32, cpu_thread& cpu)
|
||||
{
|
||||
return (id == ALL_THREADS) || (id == ANY_THREAD) || (cpu.id == id);
|
||||
};
|
||||
if (auto ppu = idm::select<ppu_thread>(on_select)) {
|
||||
selected_thread = ppu.ptr;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string GDBDebugServer::get_reg(std::shared_ptr<ppu_thread> thread, u32 rid)
|
||||
{
|
||||
std::string result;
|
||||
//ids from gdb/features/rs6000/powerpc-64.c
|
||||
//pc
|
||||
switch (rid) {
|
||||
case 64:
|
||||
return u64_to_padded_hex(thread->cia);
|
||||
//msr?
|
||||
case 65:
|
||||
return std::string(16, 'x');
|
||||
case 66:
|
||||
return u32_to_padded_hex(thread->cr_pack());
|
||||
case 67:
|
||||
return u64_to_padded_hex(thread->lr);
|
||||
case 68:
|
||||
return u64_to_padded_hex(thread->ctr);
|
||||
//xer
|
||||
case 69:
|
||||
return std::string(8, 'x');
|
||||
//fpscr
|
||||
case 70:
|
||||
return std::string(8, 'x');
|
||||
default:
|
||||
if (rid > 70) return "";
|
||||
return (rid > 31)
|
||||
? u64_to_padded_hex(reinterpret_cast<u64&>(thread->fpr[rid - 32])) //fpr
|
||||
: u64_to_padded_hex(thread->gpr[rid]); //gpr
|
||||
}
|
||||
}
|
||||
|
||||
bool GDBDebugServer::set_reg(std::shared_ptr<ppu_thread> thread, u32 rid, std::string value)
|
||||
{
|
||||
switch (rid) {
|
||||
case 64:
|
||||
thread->cia = static_cast<u32>(hex_to_u64(value));
|
||||
return true;
|
||||
//msr?
|
||||
case 65:
|
||||
return true;
|
||||
case 66:
|
||||
thread->cr_unpack(hex_to_u32(value));
|
||||
return true;
|
||||
case 67:
|
||||
thread->lr = hex_to_u64(value);
|
||||
return true;
|
||||
case 68:
|
||||
thread->ctr = hex_to_u64(value);
|
||||
return true;
|
||||
//xer
|
||||
case 69:
|
||||
return true;
|
||||
//fpscr
|
||||
case 70:
|
||||
return true;
|
||||
default:
|
||||
if (rid > 70) return false;
|
||||
if (rid > 31) {
|
||||
u64 val = hex_to_u64(value);
|
||||
thread->fpr[rid - 32] = reinterpret_cast<f64&>(val);
|
||||
} else {
|
||||
thread->gpr[rid] = hex_to_u64(value);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
u32 GDBDebugServer::get_reg_size(std::shared_ptr<ppu_thread> thread, u32 rid)
|
||||
{
|
||||
switch (rid) {
|
||||
case 66:
|
||||
case 69:
|
||||
case 70:
|
||||
return 4;
|
||||
default:
|
||||
if (rid > 70) {
|
||||
return 0;
|
||||
}
|
||||
return 8;
|
||||
}
|
||||
}
|
||||
|
||||
bool GDBDebugServer::send_reason()
|
||||
{
|
||||
return send_cmd_ack("S05");
|
||||
}
|
||||
|
||||
bool GDBDebugServer::cmd_extended_mode(gdb_cmd & cmd)
|
||||
{
|
||||
return send_cmd_ack("OK");
|
||||
}
|
||||
|
||||
bool GDBDebugServer::cmd_reason(gdb_cmd & cmd)
|
||||
{
|
||||
return send_reason();
|
||||
}
|
||||
|
||||
bool GDBDebugServer::cmd_supported(gdb_cmd & cmd)
|
||||
{
|
||||
return send_cmd_ack("PacketSize=1200");
|
||||
}
|
||||
|
||||
bool GDBDebugServer::cmd_thread_info(gdb_cmd & cmd)
|
||||
{
|
||||
std::string result = "";
|
||||
const auto on_select = [&](u32, cpu_thread& cpu)
|
||||
{
|
||||
if (result.length()) {
|
||||
result += ",";
|
||||
}
|
||||
result += u64_to_padded_hex(static_cast<u64>(cpu.id));
|
||||
};
|
||||
idm::select<ppu_thread>(on_select);
|
||||
idm::select<ARMv7Thread>(on_select);
|
||||
idm::select<RawSPUThread>(on_select);
|
||||
idm::select<SPUThread>(on_select);
|
||||
|
||||
//todo: this may exceed max command length
|
||||
result = "m" + result + "l";
|
||||
|
||||
return send_cmd_ack(result);;
|
||||
}
|
||||
|
||||
bool GDBDebugServer::cmd_current_thread(gdb_cmd & cmd)
|
||||
{
|
||||
return send_cmd_ack(selected_thread.expired() ? "" : u64_to_padded_hex(selected_thread.lock()->id));
|
||||
}
|
||||
|
||||
bool GDBDebugServer::cmd_read_register(gdb_cmd & cmd)
|
||||
{
|
||||
select_thread(general_ops_thread_id);
|
||||
auto th = selected_thread.lock();
|
||||
if (th->id_type() == 1) {
|
||||
auto ppu = std::static_pointer_cast<ppu_thread>(th);
|
||||
u32 rid = hex_to_u32(cmd.data);
|
||||
std::string result = get_reg(ppu, rid);
|
||||
if (!result.length()) {
|
||||
gdbDebugServer.warning("Wrong register id %d", rid);
|
||||
return send_cmd_ack("E01");
|
||||
}
|
||||
return send_cmd_ack(result);
|
||||
}
|
||||
gdbDebugServer.warning("Unimplemented thread type %d", th->id_type());
|
||||
return send_cmd_ack("");
|
||||
}
|
||||
|
||||
bool GDBDebugServer::cmd_write_register(gdb_cmd & cmd)
|
||||
{
|
||||
select_thread(general_ops_thread_id);
|
||||
auto th = selected_thread.lock();
|
||||
if (th->id_type() == 1) {
|
||||
auto ppu = std::static_pointer_cast<ppu_thread>(th);
|
||||
size_t eq_pos = cmd.data.find('=');
|
||||
if (eq_pos == std::string::npos) {
|
||||
gdbDebugServer.warning("Wrong write_register cmd data %s", cmd.data.c_str());
|
||||
return send_cmd_ack("E02");
|
||||
}
|
||||
u32 rid = hex_to_u32(cmd.data.substr(0, eq_pos));
|
||||
std::string value = cmd.data.substr(eq_pos + 1);
|
||||
if (!set_reg(ppu, rid, value)) {
|
||||
gdbDebugServer.warning("Wrong register id %d", rid);
|
||||
return send_cmd_ack("E01");
|
||||
}
|
||||
return send_cmd_ack("OK");
|
||||
}
|
||||
gdbDebugServer.warning("Unimplemented thread type %d", th->id_type());
|
||||
return send_cmd_ack("");
|
||||
}
|
||||
|
||||
bool GDBDebugServer::cmd_read_memory(gdb_cmd & cmd)
|
||||
{
|
||||
size_t s = cmd.data.find(',');
|
||||
u32 addr = hex_to_u32(cmd.data.substr(0, s));
|
||||
u32 len = hex_to_u32(cmd.data.substr(s + 1));
|
||||
std::string result;
|
||||
result.reserve(len * 2);
|
||||
for (u32 i = 0; i < len; ++i) {
|
||||
if (vm::check_addr(addr + i)) {
|
||||
result += to_hexbyte(vm::read8(addr + i));
|
||||
} else {
|
||||
break;
|
||||
//result += "xx";
|
||||
}
|
||||
}
|
||||
if (len && !result.length()) {
|
||||
//nothing read
|
||||
return send_cmd_ack("E01");
|
||||
}
|
||||
return send_cmd_ack(result);
|
||||
}
|
||||
|
||||
bool GDBDebugServer::cmd_write_memory(gdb_cmd & cmd)
|
||||
{
|
||||
size_t s = cmd.data.find(',');
|
||||
size_t s2 = cmd.data.find(':');
|
||||
if ((s == std::string::npos) || (s2 == std::string::npos)) {
|
||||
gdbDebugServer.warning("Malformed write memory request received: %s", cmd.data.c_str());
|
||||
return send_cmd_ack("E01");
|
||||
}
|
||||
u32 addr = hex_to_u32(cmd.data.substr(0, s));
|
||||
u32 len = hex_to_u32(cmd.data.substr(s + 1, s2 - s - 1));
|
||||
const char* data_ptr = (cmd.data.c_str()) + s2 + 1;
|
||||
for (u32 i = 0; i < len; ++i) {
|
||||
if (vm::check_addr(addr + i)) {
|
||||
u8 val;
|
||||
int res = sscanf_s(data_ptr, "%02hhX", &val);
|
||||
if (!res) {
|
||||
gdbDebugServer.warning("Couldn't read u8 from string %s", data_ptr);
|
||||
return send_cmd_ack("E02");
|
||||
}
|
||||
data_ptr += 2;
|
||||
vm::write8(addr + i, val);
|
||||
} else {
|
||||
return send_cmd_ack("E03");
|
||||
}
|
||||
}
|
||||
return send_cmd_ack("OK");
|
||||
}
|
||||
|
||||
bool GDBDebugServer::cmd_read_all_registers(gdb_cmd & cmd)
|
||||
{
|
||||
std::string result;
|
||||
select_thread(general_ops_thread_id);
|
||||
|
||||
auto th = selected_thread.lock();
|
||||
if (th->id_type() == 1) {
|
||||
auto ppu = std::static_pointer_cast<ppu_thread>(th);
|
||||
//68 64-bit registers, and 3 32-bit
|
||||
result.reserve(68*16 + 3*8);
|
||||
for (int i = 0; i < 71; ++i) {
|
||||
result += get_reg(ppu, i);
|
||||
}
|
||||
return send_cmd_ack(result);
|
||||
}
|
||||
gdbDebugServer.warning("Unimplemented thread type %d", th->id_type());
|
||||
return send_cmd_ack("");
|
||||
}
|
||||
|
||||
bool GDBDebugServer::cmd_write_all_registers(gdb_cmd & cmd)
|
||||
{
|
||||
select_thread(general_ops_thread_id);
|
||||
auto th = selected_thread.lock();
|
||||
if (th->id_type() == 1) {
|
||||
auto ppu = std::static_pointer_cast<ppu_thread>(th);
|
||||
int ptr = 0;
|
||||
for (int i = 0; i < 71; ++i) {
|
||||
int sz = get_reg_size(ppu, i);
|
||||
set_reg(ppu, i, cmd.data.substr(ptr, sz * 2));
|
||||
ptr += sz * 2;
|
||||
}
|
||||
return send_cmd_ack("OK");
|
||||
}
|
||||
gdbDebugServer.warning("Unimplemented thread type %d", th->id_type());
|
||||
return send_cmd_ack("E01");
|
||||
}
|
||||
|
||||
bool GDBDebugServer::cmd_set_thread_ops(gdb_cmd & cmd)
|
||||
{
|
||||
char type = cmd.data[0];
|
||||
std::string thread = cmd.data.substr(1);
|
||||
u64 id = thread == "-1" ? ALL_THREADS : hex_to_u64(thread);
|
||||
if (type == 'c') {
|
||||
continue_ops_thread_id = id;
|
||||
} else {
|
||||
general_ops_thread_id = id;
|
||||
}
|
||||
if (select_thread(id)) {
|
||||
return send_cmd_ack("OK");
|
||||
}
|
||||
gdbDebugServer.warning("Client asked to use thread %llx for %s, but no matching thread was found", id, type == 'c' ? "continue ops" : "general ops");
|
||||
return send_cmd_ack("E01");
|
||||
}
|
||||
|
||||
bool GDBDebugServer::cmd_attached_to_what(gdb_cmd & cmd)
|
||||
{
|
||||
//creating processes from client is not available yet
|
||||
return send_cmd_ack("1");
|
||||
}
|
||||
|
||||
bool GDBDebugServer::cmd_kill(gdb_cmd & cmd)
|
||||
{
|
||||
Emu.Stop();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GDBDebugServer::cmd_continue_support(gdb_cmd & cmd)
|
||||
{
|
||||
return send_cmd_ack("vCont;c;s;C;S");
|
||||
}
|
||||
|
||||
bool GDBDebugServer::cmd_vcont(gdb_cmd & cmd)
|
||||
{
|
||||
//todo: handle multiple actions and thread ids
|
||||
this->from_breakpoint = false;
|
||||
if (cmd.data[1] == 'c') {
|
||||
select_thread(continue_ops_thread_id);
|
||||
auto ppu = std::static_pointer_cast<ppu_thread>(selected_thread.lock());
|
||||
ppu->state -= cpu_flag::dbg_pause;
|
||||
if (Emu.IsPaused()) {
|
||||
Emu.Resume();
|
||||
}
|
||||
thread_ctrl::wait();
|
||||
//we are in all-stop mode
|
||||
Emu.Pause();
|
||||
return send_reason();
|
||||
} else if (cmd.data[1] == 's') {
|
||||
select_thread(continue_ops_thread_id);
|
||||
auto ppu = std::static_pointer_cast<ppu_thread>(selected_thread.lock());
|
||||
ppu->state += cpu_flag::dbg_step;
|
||||
ppu->state -= cpu_flag::dbg_pause;
|
||||
if (Emu.IsPaused()) {
|
||||
Emu.Resume();
|
||||
} else {
|
||||
ppu->notify();
|
||||
}
|
||||
thread_ctrl::wait();
|
||||
//we are in all-stop mode
|
||||
Emu.Pause();
|
||||
return send_reason();
|
||||
}
|
||||
return send_cmd_ack("");
|
||||
}
|
||||
|
||||
static const u32 INVALID_PTR = 0xffffffff;
|
||||
|
||||
bool GDBDebugServer::cmd_set_breakpoint(gdb_cmd & cmd)
|
||||
{
|
||||
char type = cmd.data[0];
|
||||
//software breakpoint
|
||||
if (type == '0') {
|
||||
u32 addr = INVALID_PTR;
|
||||
if (cmd.data.find(';') != std::string::npos) {
|
||||
gdbDebugServer.warning("Received request to set breakpoint with condition, but they are not supported");
|
||||
return send_cmd_ack("E01");
|
||||
}
|
||||
sscanf_s(cmd.data.c_str(), "0,%x", &addr);
|
||||
if (addr == INVALID_PTR) {
|
||||
gdbDebugServer.warning("Can't parse breakpoint request, data: %s", cmd.data.c_str());
|
||||
return send_cmd_ack("E02");
|
||||
}
|
||||
ppu_set_breakpoint(addr);
|
||||
return send_cmd_ack("OK");
|
||||
}
|
||||
//other breakpoint types are not supported
|
||||
return send_cmd_ack("");
|
||||
}
|
||||
|
||||
bool GDBDebugServer::cmd_remove_breakpoint(gdb_cmd & cmd)
|
||||
{
|
||||
char type = cmd.data[0];
|
||||
//software breakpoint
|
||||
if (type == '0') {
|
||||
u32 addr = INVALID_PTR;
|
||||
sscanf_s(cmd.data.c_str(), "0,%x", &addr);
|
||||
if (addr == INVALID_PTR) {
|
||||
gdbDebugServer.warning("Can't parse breakpoint remove request, data: %s", cmd.data.c_str());
|
||||
return send_cmd_ack("E01");
|
||||
}
|
||||
ppu_remove_breakpoint(addr);
|
||||
return send_cmd_ack("OK");
|
||||
}
|
||||
//other breakpoint types are not supported
|
||||
return send_cmd_ack("");
|
||||
|
||||
}
|
||||
|
||||
#define PROCESS_CMD(cmds,handler) if (cmd.cmd == cmds) { if (!handler(cmd)) break; else continue; }
|
||||
|
||||
void GDBDebugServer::on_task()
|
||||
{
|
||||
sock_init();
|
||||
|
||||
start_server();
|
||||
|
||||
while (!stop) {
|
||||
sockaddr_in client;
|
||||
socklen_t client_len = sizeof(client);
|
||||
client_socket = accept(server_socket, (struct sockaddr *) &client, &client_len);
|
||||
|
||||
if (client_socket == INVALID_SOCKET) {
|
||||
if (check_errno_again()) {
|
||||
thread_ctrl::wait_for(50);
|
||||
continue;
|
||||
}
|
||||
|
||||
gdbDebugServer.error("Could not establish new connection\n");
|
||||
return;
|
||||
}
|
||||
//stop immediately
|
||||
Emu.Pause();
|
||||
|
||||
try {
|
||||
char hostbuf[32];
|
||||
inet_ntop(client.sin_family, reinterpret_cast<void*>(&client.sin_addr), hostbuf, 32);
|
||||
gdbDebugServer.success("Got connection to GDB debug server from %s:%d", hostbuf, client.sin_port);
|
||||
|
||||
gdb_cmd cmd;
|
||||
|
||||
while (!stop) {
|
||||
if (!read_cmd(cmd)) {
|
||||
break;
|
||||
}
|
||||
gdbDebugServer.trace("Command %s with data %s received", cmd.cmd.c_str(), cmd.data.c_str());
|
||||
PROCESS_CMD("!", cmd_extended_mode);
|
||||
PROCESS_CMD("?", cmd_reason);
|
||||
PROCESS_CMD("qSupported", cmd_supported);
|
||||
PROCESS_CMD("qfThreadInfo", cmd_thread_info);
|
||||
PROCESS_CMD("qC", cmd_current_thread);
|
||||
PROCESS_CMD("p", cmd_read_register);
|
||||
PROCESS_CMD("P", cmd_write_register);
|
||||
PROCESS_CMD("m", cmd_read_memory);
|
||||
PROCESS_CMD("M", cmd_write_memory);
|
||||
PROCESS_CMD("g", cmd_read_all_registers);
|
||||
PROCESS_CMD("G", cmd_write_all_registers);
|
||||
PROCESS_CMD("H", cmd_set_thread_ops);
|
||||
PROCESS_CMD("qAttached", cmd_attached_to_what);
|
||||
PROCESS_CMD("k", cmd_kill);
|
||||
PROCESS_CMD("vCont?", cmd_continue_support);
|
||||
PROCESS_CMD("vCont", cmd_vcont);
|
||||
PROCESS_CMD("z", cmd_remove_breakpoint);
|
||||
PROCESS_CMD("Z", cmd_set_breakpoint);
|
||||
|
||||
gdbDebugServer.trace("Unsupported command received %s", cmd.cmd.c_str());
|
||||
if (!send_cmd_ack("")) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (std::runtime_error& e)
|
||||
{
|
||||
if (client_socket) {
|
||||
closesocket(client_socket);
|
||||
client_socket = 0;
|
||||
}
|
||||
gdbDebugServer.error(e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#undef PROCESS_CMD
|
||||
|
||||
void GDBDebugServer::on_exit()
|
||||
{
|
||||
if (server_socket) {
|
||||
closesocket(server_socket);
|
||||
}
|
||||
if (client_socket) {
|
||||
closesocket(client_socket);
|
||||
}
|
||||
sock_quit();
|
||||
}
|
||||
|
||||
std::string GDBDebugServer::get_name() const
|
||||
{
|
||||
return "GDBDebugger";
|
||||
}
|
||||
|
||||
void GDBDebugServer::on_stop()
|
||||
{
|
||||
this->stop = true;
|
||||
//just in case we are waiting for breakpoint
|
||||
this->notify();
|
||||
named_thread::on_stop();
|
||||
}
|
||||
|
||||
u32 g_gdb_debugger_id = 0;
|
||||
|
||||
#ifndef _WIN32
|
||||
#undef sscanf_s
|
||||
#endif
|
||||
|
||||
#undef HEX_U32
|
||||
#undef HEX_U64
|
||||
|
||||
#endif
|
||||
@@ -1,131 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef WITH_GDB_DEBUGGER
|
||||
|
||||
#include "Thread.h"
|
||||
#include <Emu/IdManager.h>
|
||||
#include "Emu/CPU/CPUThread.h"
|
||||
#include "Emu/Cell/PPUThread.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <WS2tcpip.h>
|
||||
#else
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
using socket_t = SOCKET;
|
||||
#else
|
||||
using socket_t = int;
|
||||
#endif
|
||||
|
||||
typedef struct gdb_cmd {
|
||||
std::string cmd;
|
||||
std::string data;
|
||||
u8 checksum;
|
||||
} gdb_cmd;
|
||||
|
||||
class wrong_checksum_exception : public std::runtime_error {
|
||||
public:
|
||||
wrong_checksum_exception(char const* const message) : runtime_error(message) {}
|
||||
};
|
||||
|
||||
const u64 ALL_THREADS = 0xffffffffffffffff;
|
||||
const u64 ANY_THREAD = 0;
|
||||
|
||||
class GDBDebugServer : public named_thread {
|
||||
|
||||
socket_t server_socket;
|
||||
socket_t client_socket;
|
||||
std::weak_ptr<cpu_thread> selected_thread;
|
||||
u64 continue_ops_thread_id = ANY_THREAD;
|
||||
u64 general_ops_thread_id = ANY_THREAD;
|
||||
|
||||
//initialize server socket and start listening
|
||||
void start_server();
|
||||
//read at most cnt bytes to buf, returns nubmer of bytes actually read
|
||||
int read(void* buf, int cnt);
|
||||
//reads one character
|
||||
char read_char();
|
||||
//reads pairs of hex characters and returns their integer value
|
||||
u8 read_hexbyte();
|
||||
//tries to read command, throws exceptions if anything goes wrong
|
||||
void try_read_cmd(gdb_cmd& out_cmd);
|
||||
//reads commands until receiveing one with valid checksum
|
||||
//in case of other exception (i.e. wrong first char of command)
|
||||
//it will log exception text and return false
|
||||
//in that case best for caller would be to stop reading, because
|
||||
//chance of getting correct command is low
|
||||
bool read_cmd(gdb_cmd& out_cmd);
|
||||
//send cnt bytes from buf to client
|
||||
void send(const char* buf, int cnt);
|
||||
//send character to client
|
||||
void send_char(char c);
|
||||
//acknowledge packet, either as accepted or declined
|
||||
void ack(bool accepted);
|
||||
//sends command body cmd to client
|
||||
void send_cmd(const std::string & cmd);
|
||||
//sends command to client until receives positive acknowledgement
|
||||
//returns false in case some error happened, and command wasn't sent
|
||||
bool send_cmd_ack(const std::string & cmd);
|
||||
//appends encoded char c to string str, and returns checksum. encoded byte can occupy 2 bytes
|
||||
static u8 append_encoded_char(char c, std::string& str);
|
||||
//convert u8 to 2 byte hexademical representation
|
||||
static std::string to_hexbyte(u8 i);
|
||||
//choose thread, support ALL_THREADS and ANY_THREAD values, returns true if some thread was selected
|
||||
bool select_thread(u64 id);
|
||||
//returns register value as hex string by register id (in gdb), in case of wrong id returns empty string
|
||||
static std::string get_reg(std::shared_ptr<ppu_thread> thread, u32 rid);
|
||||
//sets register value to hex string by register id (in gdb), in case of wrong id returns false
|
||||
static bool set_reg(std::shared_ptr<ppu_thread> thread, u32 rid, std::string value);
|
||||
//returns size of register with id rid in bytes, zero if invalid rid is provided
|
||||
static u32 get_reg_size(std::shared_ptr<ppu_thread> thread, u32 rid);
|
||||
//send reason of stop, returns false if sending response failed
|
||||
bool send_reason();
|
||||
|
||||
//commands
|
||||
bool cmd_extended_mode(gdb_cmd& cmd);
|
||||
bool cmd_reason(gdb_cmd& cmd);
|
||||
bool cmd_supported(gdb_cmd& cmd);
|
||||
bool cmd_thread_info(gdb_cmd& cmd);
|
||||
bool cmd_current_thread(gdb_cmd& cmd);
|
||||
bool cmd_read_register(gdb_cmd& cmd);
|
||||
bool cmd_write_register(gdb_cmd& cmd);
|
||||
bool cmd_read_memory(gdb_cmd& cmd);
|
||||
bool cmd_write_memory(gdb_cmd& cmd);
|
||||
bool cmd_read_all_registers(gdb_cmd& cmd);
|
||||
bool cmd_write_all_registers(gdb_cmd& cmd);
|
||||
bool cmd_set_thread_ops(gdb_cmd& cmd);
|
||||
bool cmd_attached_to_what(gdb_cmd& cmd);
|
||||
bool cmd_kill(gdb_cmd& cmd);
|
||||
bool cmd_continue_support(gdb_cmd& cmd);
|
||||
bool cmd_vcont(gdb_cmd& cmd);
|
||||
bool cmd_set_breakpoint(gdb_cmd& cmd);
|
||||
bool cmd_remove_breakpoint(gdb_cmd& cmd);
|
||||
|
||||
protected:
|
||||
void on_task() override final;
|
||||
void on_exit() override final;
|
||||
|
||||
public:
|
||||
static const u32 id_base = 1;
|
||||
static const u32 id_step = 1;
|
||||
static const u32 id_count = 0x100000;
|
||||
|
||||
bool from_breakpoint = true;
|
||||
bool stop = false;
|
||||
|
||||
virtual std::string get_name() const;
|
||||
virtual void on_stop() override final;
|
||||
};
|
||||
|
||||
extern u32 g_gdb_debugger_id;
|
||||
|
||||
#endif
|
||||
+184
-329
@@ -5,14 +5,12 @@
|
||||
#include <unordered_set>
|
||||
#include <set>
|
||||
#include <array>
|
||||
#include <deque>
|
||||
|
||||
#include "types.h"
|
||||
#include "Macro.h"
|
||||
#include "StrFmt.h"
|
||||
#include "File.h"
|
||||
#include "Log.h"
|
||||
#include "mutex.h"
|
||||
#include "VirtualMemory.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push, 0)
|
||||
@@ -22,19 +20,24 @@
|
||||
#include "llvm/ExecutionEngine/ExecutionEngine.h"
|
||||
#include "llvm/ExecutionEngine/RTDyldMemoryManager.h"
|
||||
#include "llvm/ExecutionEngine/JITEventListener.h"
|
||||
#include "llvm/ExecutionEngine/ObjectCache.h"
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#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
|
||||
|
||||
#include "JIT.h"
|
||||
|
||||
// Memory manager mutex
|
||||
shared_mutex s_mutex;
|
||||
// Global LLVM context (thread-unsafe)
|
||||
llvm::LLVMContext g_llvm_ctx;
|
||||
|
||||
// Size of virtual memory area reserved: 512 MB
|
||||
static const u64 s_memory_size = 0x20000000;
|
||||
@@ -42,133 +45,69 @@ static const u64 s_memory_size = 0x20000000;
|
||||
// Try to reserve a portion of virtual memory in the first 2 GB address space beforehand, if possible.
|
||||
static void* const s_memory = []() -> void*
|
||||
{
|
||||
llvm::InitializeNativeTarget();
|
||||
llvm::InitializeNativeTargetAsmPrinter();
|
||||
LLVMLinkInMCJIT();
|
||||
|
||||
for (u64 addr = 0x10000000; addr <= 0x80000000 - s_memory_size; addr += 0x1000000)
|
||||
#ifdef _WIN32
|
||||
for (u64 addr = 0x1000000; addr <= 0x60000000; addr += 0x1000000)
|
||||
{
|
||||
if (auto ptr = utils::memory_reserve(s_memory_size, (void*)addr))
|
||||
if (VirtualAlloc((void*)addr, s_memory_size, MEM_RESERVE, PAGE_NOACCESS))
|
||||
{
|
||||
return ptr;
|
||||
return (void*)addr;
|
||||
}
|
||||
}
|
||||
|
||||
return utils::memory_reserve(s_memory_size);
|
||||
return VirtualAlloc(NULL, s_memory_size, MEM_RESERVE, PAGE_NOACCESS);
|
||||
#else
|
||||
return ::mmap((void*)0x10000000, s_memory_size, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
#endif
|
||||
}();
|
||||
|
||||
static void* s_next = s_memory;
|
||||
// EH frames
|
||||
static u8* s_unwind_info;
|
||||
static u64 s_unwind_size;
|
||||
|
||||
#ifdef _WIN32
|
||||
static std::deque<std::vector<RUNTIME_FUNCTION>> s_unwater;
|
||||
static std::vector<std::vector<RUNTIME_FUNCTION>> s_unwind; // .pdata
|
||||
static std::vector<RUNTIME_FUNCTION> s_unwind; // Custom .pdata section replacement
|
||||
#endif
|
||||
|
||||
// Reset memory manager
|
||||
extern void jit_finalize()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
for (auto&& unwind : s_unwind)
|
||||
{
|
||||
if (!RtlDeleteFunctionTable(unwind.data()))
|
||||
{
|
||||
LOG_FATAL(GENERAL, "RtlDeleteFunctionTable() failed! Error %u", GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
s_unwind.clear();
|
||||
#else
|
||||
// TODO: unregister EH frames if necessary
|
||||
#endif
|
||||
|
||||
utils::memory_decommit(s_memory, s_memory_size);
|
||||
|
||||
s_next = s_memory;
|
||||
}
|
||||
|
||||
// Helper class
|
||||
struct MemoryManager : llvm::RTDyldMemoryManager
|
||||
struct MemoryManager final : llvm::RTDyldMemoryManager
|
||||
{
|
||||
std::unordered_map<std::string, std::uintptr_t>& m_link;
|
||||
std::unordered_map<std::string, std::uintptr_t> table;
|
||||
|
||||
std::array<u8, 16>* m_tramps{};
|
||||
|
||||
u8* m_code_addr{}; // TODO
|
||||
|
||||
MemoryManager(std::unordered_map<std::string, std::uintptr_t>& table)
|
||||
: m_link(table)
|
||||
MemoryManager(std::unordered_map<std::string, std::uintptr_t>&& table)
|
||||
: table(std::move(table))
|
||||
{
|
||||
}
|
||||
|
||||
[[noreturn]] static void null()
|
||||
{
|
||||
fmt::throw_exception("Null function" HERE);
|
||||
throw std::runtime_error("Null function" HERE);
|
||||
}
|
||||
|
||||
llvm::JITSymbol findSymbol(const std::string& name) override
|
||||
virtual u64 getSymbolAddress(const std::string& name) override
|
||||
{
|
||||
auto& addr = m_link[name];
|
||||
|
||||
// Find function address
|
||||
if (!addr)
|
||||
if (u64 addr = RTDyldMemoryManager::getSymbolAddress(name))
|
||||
{
|
||||
addr = RTDyldMemoryManager::getSymbolAddress(name);
|
||||
|
||||
if (addr)
|
||||
{
|
||||
LOG_WARNING(GENERAL, "LLVM: Symbol requested: %s -> 0x%016llx", name, addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR(GENERAL, "LLVM: Linkage failed: %s", name);
|
||||
addr = (u64)null;
|
||||
}
|
||||
// This may be bad if LLVM requests some built-in functions like fma.
|
||||
LOG_ERROR(GENERAL, "LLVM: Symbol requested %s -> 0x%016llx", name, addr);
|
||||
return addr;
|
||||
}
|
||||
|
||||
// Verify address for small code model
|
||||
if ((u64)s_memory > 0x80000000 - s_memory_size ? (u64)addr - (u64)s_memory >= s_memory_size : addr >= 0x80000000)
|
||||
const auto found = table.find(name);
|
||||
|
||||
if (found != table.end())
|
||||
{
|
||||
// Lock memory manager
|
||||
writer_lock lock(s_mutex);
|
||||
|
||||
// Allocate memory for trampolines
|
||||
if (!m_tramps)
|
||||
{
|
||||
m_tramps = reinterpret_cast<decltype(m_tramps)>(s_next);
|
||||
utils::memory_commit(s_next, 4096, utils::protection::wx);
|
||||
s_next = (u8*)((u64)s_next + 4096);
|
||||
}
|
||||
|
||||
// Create a trampoline
|
||||
auto& data = *m_tramps++;
|
||||
data[0x0] = 0xff; // JMP [rip+2]
|
||||
data[0x1] = 0x25;
|
||||
data[0x2] = 0x02;
|
||||
data[0x3] = 0x00;
|
||||
data[0x4] = 0x00;
|
||||
data[0x5] = 0x00;
|
||||
data[0x6] = 0x48; // MOV rax, imm64 (not executed)
|
||||
data[0x7] = 0xb8;
|
||||
std::memcpy(data.data() + 8, &addr, 8);
|
||||
addr = (u64)&data;
|
||||
|
||||
// Reset pointer (memory page exhausted)
|
||||
if (((u64)m_tramps % 4096) == 0)
|
||||
{
|
||||
m_tramps = nullptr;
|
||||
}
|
||||
return found->second;
|
||||
}
|
||||
|
||||
return {addr, llvm::JITSymbolFlags::Exported};
|
||||
// It's fine if some function is never called, for example.
|
||||
LOG_ERROR(GENERAL, "LLVM: Linkage failed for %s", name);
|
||||
return (u64)null;
|
||||
}
|
||||
|
||||
virtual u8* allocateCodeSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name) override
|
||||
{
|
||||
// Lock memory manager
|
||||
writer_lock lock(s_mutex);
|
||||
|
||||
// Simple allocation
|
||||
const u64 next = ::align((u64)s_next + size, 4096);
|
||||
const u64 next = ::align((u64)m_next + size, 4096);
|
||||
|
||||
if (next > (u64)s_memory + s_memory_size)
|
||||
{
|
||||
@@ -176,20 +115,24 @@ struct MemoryManager : llvm::RTDyldMemoryManager
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
utils::memory_commit(s_next, size, utils::protection::wx);
|
||||
m_code_addr = (u8*)s_next;
|
||||
#ifdef _WIN32
|
||||
if (!VirtualAlloc(m_next, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE))
|
||||
#else
|
||||
if (::mprotect(m_next, size, PROT_READ | PROT_WRITE | PROT_EXEC))
|
||||
#endif
|
||||
{
|
||||
LOG_FATAL(GENERAL, "LLVM: Failed to allocate memory at 0x%p", m_next);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
LOG_NOTICE(GENERAL, "LLVM: Code section %u '%s' allocated -> %p (size=0x%llx, aligned 0x%x)", sec_id, sec_name.data(), s_next, size, align);
|
||||
return (u8*)std::exchange(s_next, (void*)next);
|
||||
LOG_SUCCESS(GENERAL, "LLVM: Code section %u '%s' allocated -> 0x%p (size=0x%llx, aligned 0x%x)", sec_id, sec_name.data(), m_next, size, align);
|
||||
return (u8*)std::exchange(m_next, (void*)next);
|
||||
}
|
||||
|
||||
virtual u8* allocateDataSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name, bool is_ro) override
|
||||
{
|
||||
// Lock memory manager
|
||||
writer_lock lock(s_mutex);
|
||||
|
||||
// Simple allocation
|
||||
const u64 next = ::align((u64)s_next + size, 4096);
|
||||
const u64 next = ::align((u64)m_next + size, 4096);
|
||||
|
||||
if (next > (u64)s_memory + s_memory_size)
|
||||
{
|
||||
@@ -197,276 +140,188 @@ struct MemoryManager : llvm::RTDyldMemoryManager
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!is_ro)
|
||||
#ifdef _WIN32
|
||||
if (!VirtualAlloc(m_next, size, MEM_COMMIT, PAGE_READWRITE))
|
||||
#else
|
||||
if (::mprotect(m_next, size, PROT_READ | PROT_WRITE))
|
||||
#endif
|
||||
{
|
||||
LOG_ERROR(GENERAL, "LLVM: Writeable data section not supported!");
|
||||
LOG_FATAL(GENERAL, "LLVM: Failed to allocate memory at 0x%p", m_next);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
utils::memory_commit(s_next, size);
|
||||
|
||||
LOG_NOTICE(GENERAL, "LLVM: Data section %u '%s' allocated -> %p (size=0x%llx, aligned 0x%x, %s)", sec_id, sec_name.data(), s_next, size, align, is_ro ? "ro" : "rw");
|
||||
return (u8*)std::exchange(s_next, (void*)next);
|
||||
LOG_SUCCESS(GENERAL, "LLVM: Data section %u '%s' allocated -> 0x%p (size=0x%llx, aligned 0x%x, %s)", sec_id, sec_name.data(), m_next, size, align, is_ro ? "ro" : "rw");
|
||||
return (u8*)std::exchange(m_next, (void*)next);
|
||||
}
|
||||
|
||||
virtual bool finalizeMemory(std::string* = nullptr) override
|
||||
{
|
||||
// Lock memory manager
|
||||
writer_lock lock(s_mutex);
|
||||
|
||||
// TODO: make only read-only sections read-only
|
||||
//#ifdef _WIN32
|
||||
// DWORD op;
|
||||
// VirtualProtect(s_memory, (u64)m_next - (u64)s_memory, PAGE_READONLY, &op);
|
||||
// VirtualProtect(s_code_addr, s_code_size, PAGE_EXECUTE_READ, &op);
|
||||
//#else
|
||||
// ::mprotect(s_memory, (u64)m_next - (u64)s_memory, PROT_READ);
|
||||
// ::mprotect(s_code_addr, s_code_size, PROT_READ | PROT_EXEC);
|
||||
//#endif
|
||||
// TODO: make sections read-only when necessary
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void registerEHFrames(u8* addr, u64 load_addr, std::size_t size) override
|
||||
{
|
||||
#ifdef _WIN32
|
||||
// Lock memory manager
|
||||
writer_lock lock(s_mutex);
|
||||
|
||||
// Use s_memory as a BASE, compute the difference
|
||||
const u64 unwind_diff = (u64)addr - (u64)s_memory;
|
||||
|
||||
// Fix RUNTIME_FUNCTION records (.pdata section)
|
||||
auto pdata = std::move(s_unwater.front());
|
||||
s_unwater.pop_front();
|
||||
|
||||
for (auto& rf : pdata)
|
||||
{
|
||||
rf.UnwindData += static_cast<DWORD>(unwind_diff);
|
||||
}
|
||||
|
||||
// Register .xdata UNWIND_INFO structs
|
||||
if (!RtlAddFunctionTable(pdata.data(), (DWORD)pdata.size(), (u64)s_memory))
|
||||
{
|
||||
LOG_ERROR(GENERAL, "RtlAddFunctionTable() failed! Error %u", GetLastError());
|
||||
}
|
||||
else
|
||||
{
|
||||
s_unwind.emplace_back(std::move(pdata));
|
||||
}
|
||||
#endif
|
||||
s_unwind_info = addr;
|
||||
s_unwind_size = size;
|
||||
|
||||
return RTDyldMemoryManager::registerEHFrames(addr, load_addr, size);
|
||||
}
|
||||
|
||||
virtual void deregisterEHFrames() override
|
||||
virtual void deregisterEHFrames(u8* addr, u64 load_addr, std::size_t size) override
|
||||
{
|
||||
LOG_ERROR(GENERAL, "deregisterEHFrames() called"); // Not expected
|
||||
|
||||
return RTDyldMemoryManager::deregisterEHFrames();
|
||||
}
|
||||
};
|
||||
|
||||
// Helper class
|
||||
struct EventListener : llvm::JITEventListener
|
||||
{
|
||||
MemoryManager& m_mem;
|
||||
|
||||
EventListener(MemoryManager& mem)
|
||||
: m_mem(mem)
|
||||
{
|
||||
return RTDyldMemoryManager::deregisterEHFrames(addr, load_addr, size);
|
||||
}
|
||||
|
||||
virtual void NotifyObjectEmitted(const llvm::object::ObjectFile& obj, const llvm::RuntimeDyld::LoadedObjectInfo& inf) override
|
||||
~MemoryManager()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
for (auto it = obj.section_begin(), end = obj.section_end(); it != end; ++it)
|
||||
if (!RtlDeleteFunctionTable(s_unwind.data()))
|
||||
{
|
||||
llvm::StringRef name;
|
||||
it->getName(name);
|
||||
|
||||
if (name == ".pdata")
|
||||
{
|
||||
llvm::StringRef data;
|
||||
it->getContents(data);
|
||||
|
||||
std::vector<RUNTIME_FUNCTION> rfs(data.size() / sizeof(RUNTIME_FUNCTION));
|
||||
|
||||
auto offsets = reinterpret_cast<DWORD*>(rfs.data());
|
||||
|
||||
// Initialize .pdata section using relocation info
|
||||
for (auto ri = it->relocation_begin(), end = it->relocation_end(); ri != end; ++ri)
|
||||
{
|
||||
if (ri->getType() == 3 /*R_X86_64_GOT32*/)
|
||||
{
|
||||
const u64 value = *reinterpret_cast<const DWORD*>(data.data() + ri->getOffset());
|
||||
offsets[ri->getOffset() / sizeof(DWORD)] = static_cast<DWORD>(value + ri->getSymbol()->getAddress().get());
|
||||
}
|
||||
}
|
||||
|
||||
// Lock memory manager
|
||||
writer_lock lock(s_mutex);
|
||||
|
||||
// Use s_memory as a BASE, compute the difference
|
||||
const u64 code_diff = (u64)m_mem.m_code_addr - (u64)s_memory;
|
||||
|
||||
// Fix RUNTIME_FUNCTION records (.pdata section)
|
||||
for (auto& rf : rfs)
|
||||
{
|
||||
rf.BeginAddress += static_cast<DWORD>(code_diff);
|
||||
rf.EndAddress += static_cast<DWORD>(code_diff);
|
||||
}
|
||||
|
||||
s_unwater.emplace_back(std::move(rfs));
|
||||
}
|
||||
LOG_FATAL(GENERAL, "RtlDeleteFunctionTable(addr=0x%p) failed! Error %u", s_unwind_info, GetLastError());
|
||||
}
|
||||
|
||||
if (!VirtualFree(s_memory, 0, MEM_DECOMMIT))
|
||||
{
|
||||
LOG_FATAL(GENERAL, "VirtualFree(0x%p) failed! Error %u", s_memory, GetLastError());
|
||||
}
|
||||
#else
|
||||
if (::mprotect(s_memory, s_memory_size, PROT_NONE))
|
||||
{
|
||||
LOG_FATAL(GENERAL, "mprotect(0x%p) failed! Error %d", s_memory, errno);
|
||||
}
|
||||
|
||||
// TODO: unregister EH frames if necessary
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
void* m_next = s_memory;
|
||||
};
|
||||
|
||||
// Helper class
|
||||
class ObjectCache final : public llvm::ObjectCache
|
||||
struct EventListener final : llvm::JITEventListener
|
||||
{
|
||||
const std::string& m_path;
|
||||
|
||||
public:
|
||||
ObjectCache(const std::string& path)
|
||||
: m_path(path)
|
||||
virtual void NotifyObjectEmitted(const llvm::object::ObjectFile& obj, const llvm::RuntimeDyld::LoadedObjectInfo& inf) override
|
||||
{
|
||||
}
|
||||
|
||||
~ObjectCache() override = default;
|
||||
|
||||
void notifyObjectCompiled(const llvm::Module* module, llvm::MemoryBufferRef obj) override
|
||||
{
|
||||
std::string name = m_path;
|
||||
name.append(module->getName());
|
||||
fs::file(name, fs::rewrite).write(obj.getBufferStart(), obj.getBufferSize());
|
||||
LOG_SUCCESS(GENERAL, "LLVM: Created module: %s", module->getName().data());
|
||||
}
|
||||
|
||||
std::unique_ptr<llvm::MemoryBuffer> getObject(const llvm::Module* module) override
|
||||
{
|
||||
std::string name = m_path;
|
||||
name.append(module->getName());
|
||||
|
||||
if (fs::file cached{name, fs::read})
|
||||
{
|
||||
auto buf = llvm::MemoryBuffer::getNewUninitMemBuffer(cached.size());
|
||||
cached.read(const_cast<char*>(buf->getBufferStart()), buf->getBufferSize());
|
||||
LOG_SUCCESS(GENERAL, "LLVM: Loaded module: %s", module->getName().data());
|
||||
return buf;
|
||||
}
|
||||
else
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
const llvm::StringRef elf = obj.getData();
|
||||
fs::file(fs::get_config_dir() + "LLVM.obj", fs::rewrite)
|
||||
.write(elf.data(), elf.size());
|
||||
}
|
||||
};
|
||||
|
||||
jit_compiler::jit_compiler(const std::unordered_map<std::string, std::uintptr_t>& _link, std::string _cpu)
|
||||
: m_link(std::move(_link))
|
||||
, m_cpu(std::move(_cpu))
|
||||
static EventListener s_listener;
|
||||
|
||||
jit_compiler::jit_compiler(std::unique_ptr<llvm::Module>&& _module, std::unordered_map<std::string, std::uintptr_t>&& table)
|
||||
{
|
||||
if (m_cpu.empty())
|
||||
{
|
||||
m_cpu = llvm::sys::getHostCPUName();
|
||||
}
|
||||
EXPECTS(s_memory);
|
||||
|
||||
std::string result;
|
||||
|
||||
if (m_link.empty())
|
||||
{
|
||||
// Auxiliary JIT (does not use custom memory manager, only writes the objects)
|
||||
m_engine.reset(llvm::EngineBuilder(std::make_unique<llvm::Module>("null_", m_context))
|
||||
.setErrorStr(&result)
|
||||
.setOptLevel(llvm::CodeGenOpt::Aggressive)
|
||||
.setCodeModel(llvm::CodeModel::Small)
|
||||
.setMCPU(m_cpu)
|
||||
.create());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Primary JIT
|
||||
auto mem = std::make_unique<MemoryManager>(m_link);
|
||||
m_jit_el = std::make_unique<EventListener>(*mem);
|
||||
const auto module_ptr = _module.get();
|
||||
|
||||
m_engine.reset(llvm::EngineBuilder(std::make_unique<llvm::Module>("null", m_context))
|
||||
.setErrorStr(&result)
|
||||
.setMCJITMemoryManager(std::move(mem))
|
||||
.setOptLevel(llvm::CodeGenOpt::Aggressive)
|
||||
.setCodeModel(llvm::CodeModel::Small)
|
||||
.setMCPU(m_cpu)
|
||||
.create());
|
||||
// Initialization
|
||||
llvm::InitializeNativeTarget();
|
||||
llvm::InitializeNativeTargetAsmPrinter();
|
||||
LLVMLinkInMCJIT();
|
||||
|
||||
if (m_engine)
|
||||
{
|
||||
m_engine->RegisterJITEventListener(m_jit_el.get());
|
||||
}
|
||||
}
|
||||
m_engine.reset(llvm::EngineBuilder(std::move(_module))
|
||||
.setErrorStr(&result)
|
||||
.setMCJITMemoryManager(std::make_unique<MemoryManager>(std::move(table)))
|
||||
.setOptLevel(llvm::CodeGenOpt::Aggressive)
|
||||
.setRelocationModel(llvm::Reloc::PIC_)
|
||||
.setCodeModel((u64)s_memory <= 0x60000000 ? llvm::CodeModel::Medium : llvm::CodeModel::Large) // TODO
|
||||
.setMCPU(llvm::sys::getHostCPUName())
|
||||
.create());
|
||||
|
||||
if (!m_engine)
|
||||
{
|
||||
fmt::throw_exception("LLVM: Failed to create ExecutionEngine: %s", result);
|
||||
throw fmt::exception("LLVM: Failed to create ExecutionEngine: %s", result);
|
||||
}
|
||||
|
||||
m_engine->setProcessAllSections(true); // ???
|
||||
m_engine->RegisterJITEventListener(&s_listener);
|
||||
m_engine->finalizeObject();
|
||||
|
||||
for (auto& func : module_ptr->functions())
|
||||
{
|
||||
if (!func.empty())
|
||||
{
|
||||
const std::string& name = func.getName();
|
||||
|
||||
// Register compiled function
|
||||
m_map[name] = m_engine->getFunctionAddress(name);
|
||||
}
|
||||
|
||||
// Delete IR to lower memory consumption
|
||||
func.deleteBody();
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// Register .xdata UNWIND_INFO (.pdata section is empty for some reason)
|
||||
std::set<u64> func_set;
|
||||
|
||||
for (const auto& pair : m_map)
|
||||
{
|
||||
func_set.emplace(pair.second);
|
||||
}
|
||||
|
||||
// Hack (cannot obtain last function size)
|
||||
func_set.emplace(::align(*--func_set.end() + 4096, 4096));
|
||||
|
||||
const u64 base = (u64)s_memory;
|
||||
const u8* bits = s_unwind_info;
|
||||
|
||||
s_unwind.clear();
|
||||
s_unwind.reserve(m_map.size());
|
||||
|
||||
for (auto it = func_set.begin(), end = --func_set.end(); it != end; it++)
|
||||
{
|
||||
const u64 addr = *it;
|
||||
const u64 next = *func_set.upper_bound(addr);
|
||||
|
||||
// Generate RUNTIME_FUNCTION record
|
||||
RUNTIME_FUNCTION uw;
|
||||
uw.BeginAddress = static_cast<u32>(addr - base);
|
||||
uw.EndAddress = static_cast<u32>(next - base);
|
||||
uw.UnwindData = static_cast<u32>((u64)bits - base);
|
||||
s_unwind.emplace_back(uw);
|
||||
|
||||
// Parse .xdata UNWIND_INFO record
|
||||
const u8 flags = *bits++; // Version and flags
|
||||
const u8 prolog = *bits++; // Size of prolog
|
||||
const u8 count = *bits++; // Count of unwind codes
|
||||
const u8 frame = *bits++; // Frame Reg + Off
|
||||
bits += ::align(std::max<u8>(1, count), 2) * sizeof(u16); // UNWIND_CODE array
|
||||
|
||||
if (flags != 1)
|
||||
{
|
||||
LOG_ERROR(GENERAL, "LLVM: unsupported UNWIND_INFO version/flags (0x%02x)", flags);
|
||||
break;
|
||||
}
|
||||
|
||||
LOG_TRACE(GENERAL, "LLVM: .xdata at 0x%llx: function 0x%x..0x%x: p0x%02x, c0x%02x, f0x%02x", uw.UnwindData + base, uw.BeginAddress + base, uw.EndAddress + base, prolog, count, frame);
|
||||
}
|
||||
|
||||
if (s_unwind_info + s_unwind_size != bits)
|
||||
{
|
||||
LOG_FATAL(GENERAL, "LLVM: .xdata analysis failed! (0x%p != 0x%p)", s_unwind_info + s_unwind_size, bits);
|
||||
}
|
||||
else if (!RtlAddFunctionTable(s_unwind.data(), (DWORD)s_unwind.size(), base))
|
||||
{
|
||||
LOG_FATAL(GENERAL, "RtlAddFunctionTable(addr=0x%p) failed! Error %u", s_unwind_info, GetLastError());
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_SUCCESS(GENERAL, "LLVM: UNWIND_INFO registered (addr=0x%p, size=0x%llx)", s_unwind_info, s_unwind_size);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
jit_compiler::~jit_compiler()
|
||||
{
|
||||
}
|
||||
|
||||
void jit_compiler::add(std::unique_ptr<llvm::Module> module, const std::string& path)
|
||||
{
|
||||
ObjectCache cache{path};
|
||||
m_engine->setObjectCache(&cache);
|
||||
|
||||
const auto ptr = module.get();
|
||||
m_engine->addModule(std::move(module));
|
||||
m_engine->generateCodeForModule(ptr);
|
||||
m_engine->setObjectCache(nullptr);
|
||||
|
||||
for (auto& func : ptr->functions())
|
||||
{
|
||||
// Delete IR to lower memory consumption
|
||||
func.deleteBody();
|
||||
}
|
||||
}
|
||||
|
||||
void jit_compiler::fin()
|
||||
{
|
||||
m_engine->finalizeObject();
|
||||
}
|
||||
|
||||
u64 jit_compiler::get(const std::string& name)
|
||||
{
|
||||
return m_engine->getGlobalValueAddress(name);
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, u64> jit_compiler::add(std::unordered_map<std::string, std::string> data)
|
||||
{
|
||||
// Lock memory manager
|
||||
writer_lock lock(s_mutex);
|
||||
|
||||
std::unordered_map<std::string, u64> result;
|
||||
|
||||
std::size_t size = 0;
|
||||
|
||||
for (auto&& pair : data)
|
||||
{
|
||||
size += ::align(pair.second.size(), 16);
|
||||
}
|
||||
|
||||
utils::memory_commit(s_next, size, utils::protection::wx);
|
||||
std::memset(s_next, 0xc3, ::align(size, 4096));
|
||||
|
||||
for (auto&& pair : data)
|
||||
{
|
||||
std::memcpy(s_next, pair.second.data(), pair.second.size());
|
||||
result.emplace(pair.first, (u64)s_next);
|
||||
s_next = (void*)::align((u64)s_next + pair.second.size(), 16);
|
||||
}
|
||||
|
||||
s_next = (void*)::align((u64)s_next, 4096);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+13
-40
@@ -7,9 +7,7 @@
|
||||
#include <unordered_map>
|
||||
|
||||
#include "types.h"
|
||||
#include "mutex.h"
|
||||
|
||||
#include "restore_new.h"
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push, 0)
|
||||
#endif
|
||||
@@ -19,58 +17,33 @@
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
#include "define_new_memleakdetect.h"
|
||||
|
||||
extern llvm::LLVMContext g_llvm_ctx;
|
||||
|
||||
// Temporary compiler interface
|
||||
class jit_compiler final
|
||||
{
|
||||
// Local LLVM context
|
||||
llvm::LLVMContext m_context;
|
||||
|
||||
// JIT Event Listener
|
||||
std::unique_ptr<struct EventListener> m_jit_el;
|
||||
|
||||
// Execution instance
|
||||
std::unique_ptr<llvm::ExecutionEngine> m_engine;
|
||||
|
||||
// Link table
|
||||
std::unordered_map<std::string, u64> m_link;
|
||||
|
||||
// Arch
|
||||
std::string m_cpu;
|
||||
// Compiled functions
|
||||
std::unordered_map<std::string, std::uintptr_t> m_map;
|
||||
|
||||
public:
|
||||
jit_compiler(const std::unordered_map<std::string, u64>& _link, std::string _cpu);
|
||||
jit_compiler(std::unique_ptr<llvm::Module>&&, std::unordered_map<std::string, std::uintptr_t>&&);
|
||||
~jit_compiler();
|
||||
|
||||
// Get LLVM context
|
||||
auto& get_context()
|
||||
{
|
||||
return m_context;
|
||||
}
|
||||
|
||||
// Add module
|
||||
void add(std::unique_ptr<llvm::Module> module, const std::string& path);
|
||||
|
||||
// Finalize
|
||||
void fin();
|
||||
|
||||
// Get compiled function address
|
||||
u64 get(const std::string& name);
|
||||
|
||||
// Add functions directly to the memory manager (name -> code)
|
||||
static std::unordered_map<std::string, u64> add(std::unordered_map<std::string, std::string>);
|
||||
|
||||
// Get CPU info
|
||||
const std::string& cpu() const
|
||||
std::uintptr_t get(const std::string& name) const
|
||||
{
|
||||
return m_cpu;
|
||||
}
|
||||
const auto found = m_map.find(name);
|
||||
|
||||
if (found != m_map.end())
|
||||
{
|
||||
return found->second;
|
||||
}
|
||||
|
||||
// Check JIT purpose
|
||||
bool is_primary() const
|
||||
{
|
||||
return !m_link.empty();
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
+65
-210
@@ -1,49 +1,28 @@
|
||||
#include "Log.h"
|
||||
#include "File.h"
|
||||
#include "StrFmt.h"
|
||||
#include "sema.h"
|
||||
|
||||
#include "rpcs3_version.h"
|
||||
#include <cstdarg>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
#else
|
||||
#include <chrono>
|
||||
#endif
|
||||
|
||||
static std::string empty_string()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
// Thread-specific log prefix provider
|
||||
thread_local std::string(*g_tls_log_prefix)() = &empty_string;
|
||||
thread_local std::string(*g_tls_log_prefix)() = nullptr;
|
||||
|
||||
template<>
|
||||
void fmt_class_string<logs::level>::format(std::string& out, u64 arg)
|
||||
{
|
||||
format_enum(out, arg, [](auto lev)
|
||||
{
|
||||
switch (lev)
|
||||
{
|
||||
case logs::level::always: return "Nothing";
|
||||
case logs::level::fatal: return "Fatal";
|
||||
case logs::level::error: return "Error";
|
||||
case logs::level::todo: return "TODO";
|
||||
case logs::level::success: return "Success";
|
||||
case logs::level::warning: return "Warning";
|
||||
case logs::level::notice: return "Notice";
|
||||
case logs::level::trace: return "Trace";
|
||||
}
|
||||
|
||||
return unknown;
|
||||
});
|
||||
}
|
||||
#ifndef _MSC_VER
|
||||
constexpr DECLARE(bijective<logs::level, const char*>::map);
|
||||
#endif
|
||||
|
||||
namespace logs
|
||||
{
|
||||
struct listener
|
||||
{
|
||||
listener() = default;
|
||||
|
||||
virtual ~listener() = default;
|
||||
|
||||
virtual void log(const channel& ch, level sev, const std::string& text) = 0;
|
||||
};
|
||||
|
||||
class file_writer
|
||||
{
|
||||
// Could be memory-mapped file
|
||||
@@ -55,7 +34,10 @@ namespace logs
|
||||
virtual ~file_writer() = default;
|
||||
|
||||
// Append raw data
|
||||
void log(const char* text, std::size_t size);
|
||||
void log(const std::string& text);
|
||||
|
||||
// Get current file size (may be used by secondary readers)
|
||||
std::size_t size() const;
|
||||
};
|
||||
|
||||
struct file_listener : public file_writer, public listener
|
||||
@@ -64,162 +46,35 @@ namespace logs
|
||||
: file_writer(name)
|
||||
, listener()
|
||||
{
|
||||
const std::string& start = fmt::format("\xEF\xBB\xBF" "RPCS3 v%s\n", rpcs3::version.to_string());
|
||||
file_writer::log(start.data(), start.size());
|
||||
}
|
||||
|
||||
// Encode level, current thread name, channel name and write log message
|
||||
virtual void log(u64 stamp, const message& msg, const std::string& prefix, const std::string& text) override;
|
||||
virtual void log(const channel& ch, level sev, const std::string& text) override;
|
||||
};
|
||||
|
||||
static file_listener* get_logger()
|
||||
static file_listener& get_logger()
|
||||
{
|
||||
// Use magic static
|
||||
static file_listener logger("RPCS3.log");
|
||||
return &logger;
|
||||
return logger;
|
||||
}
|
||||
|
||||
static u64 get_stamp()
|
||||
{
|
||||
static struct time_initializer
|
||||
{
|
||||
#ifdef _WIN32
|
||||
LARGE_INTEGER freq;
|
||||
LARGE_INTEGER start;
|
||||
|
||||
time_initializer()
|
||||
{
|
||||
QueryPerformanceFrequency(&freq);
|
||||
QueryPerformanceCounter(&start);
|
||||
}
|
||||
#else
|
||||
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
|
||||
#endif
|
||||
|
||||
u64 get() const
|
||||
{
|
||||
#ifdef _WIN32
|
||||
LARGE_INTEGER now;
|
||||
QueryPerformanceCounter(&now);
|
||||
const LONGLONG diff = now.QuadPart - start.QuadPart;
|
||||
return diff / freq.QuadPart * 1'000'000 + diff % freq.QuadPart * 1'000'000 / freq.QuadPart;
|
||||
#else
|
||||
return std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - start).count();
|
||||
#endif
|
||||
}
|
||||
} timebase{};
|
||||
|
||||
return timebase.get();
|
||||
}
|
||||
|
||||
channel GENERAL("");
|
||||
channel LOADER("LDR");
|
||||
channel MEMORY("MEM");
|
||||
channel RSX("RSX");
|
||||
channel HLE("HLE");
|
||||
channel PPU("PPU");
|
||||
channel SPU("SPU");
|
||||
channel GENERAL(nullptr, level::notice);
|
||||
channel LOADER("LDR", level::notice);
|
||||
channel MEMORY("MEM", level::notice);
|
||||
channel RSX("RSX", level::notice);
|
||||
channel HLE("HLE", level::notice);
|
||||
channel PPU("PPU", level::notice);
|
||||
channel SPU("SPU", level::notice);
|
||||
channel ARMv7("ARMv7");
|
||||
|
||||
struct channel_info
|
||||
{
|
||||
channel* pointer = nullptr;
|
||||
level enabled = level::notice;
|
||||
|
||||
void set_level(level value)
|
||||
{
|
||||
enabled = value;
|
||||
|
||||
if (pointer)
|
||||
{
|
||||
pointer->enabled = value;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Channel registry mutex
|
||||
semaphore<> g_mutex;
|
||||
|
||||
// Channel registry
|
||||
std::unordered_map<std::string, channel_info> g_channels;
|
||||
|
||||
void reset()
|
||||
{
|
||||
semaphore_lock lock(g_mutex);
|
||||
|
||||
for (auto&& pair : g_channels)
|
||||
{
|
||||
pair.second.set_level(level::notice);
|
||||
}
|
||||
}
|
||||
|
||||
void set_level(const std::string& ch_name, level value)
|
||||
{
|
||||
semaphore_lock lock(g_mutex);
|
||||
|
||||
g_channels[ch_name].set_level(value);
|
||||
}
|
||||
}
|
||||
|
||||
logs::listener::~listener()
|
||||
void logs::channel::broadcast(const logs::channel& ch, logs::level sev, const char* fmt...)
|
||||
{
|
||||
}
|
||||
|
||||
void logs::listener::add(logs::listener* _new)
|
||||
{
|
||||
// Get first (main) listener
|
||||
listener* lis = get_logger();
|
||||
|
||||
// Install new listener at the end of linked list
|
||||
while (lis->m_next || !lis->m_next.compare_and_swap_test(nullptr, _new))
|
||||
{
|
||||
lis = lis->m_next;
|
||||
}
|
||||
}
|
||||
|
||||
void logs::message::broadcast(const char* fmt, const fmt_type_info* sup, const u64* args)
|
||||
{
|
||||
// Get timestamp
|
||||
const u64 stamp = get_stamp();
|
||||
|
||||
// Register channel
|
||||
if (ch->enabled == level::_uninit)
|
||||
{
|
||||
semaphore_lock lock(g_mutex);
|
||||
|
||||
auto& info = g_channels[ch->name];
|
||||
|
||||
if (info.pointer && info.pointer != ch)
|
||||
{
|
||||
fmt::throw_exception("logs::channel repetition: %s", ch->name);
|
||||
}
|
||||
else if (!info.pointer)
|
||||
{
|
||||
info.pointer = ch;
|
||||
ch->enabled = info.enabled;
|
||||
|
||||
// Check level again
|
||||
if (info.enabled < sev)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get text
|
||||
std::string text;
|
||||
fmt::raw_append(text, fmt, sup, args);
|
||||
std::string prefix = g_tls_log_prefix();
|
||||
|
||||
// Get first (main) listener
|
||||
listener* lis = get_logger();
|
||||
|
||||
// Send message to all listeners
|
||||
while (lis)
|
||||
{
|
||||
lis->log(stamp, *this, prefix, text);
|
||||
lis = lis->m_next;
|
||||
}
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
get_logger().log(ch, sev, fmt::unsafe_vformat(fmt, args));
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
[[noreturn]] extern void catch_all_exceptions();
|
||||
@@ -230,7 +85,7 @@ logs::file_writer::file_writer(const std::string& name)
|
||||
{
|
||||
if (!m_file.open(fs::get_config_dir() + name, fs::rewrite + fs::append))
|
||||
{
|
||||
fmt::throw_exception("Can't create log file %s (error %s)", name, fs::g_tls_error);
|
||||
throw fmt::exception("Can't create log file %s (error %d)", name, fs::g_tls_error);
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
@@ -239,54 +94,54 @@ logs::file_writer::file_writer(const std::string& name)
|
||||
}
|
||||
}
|
||||
|
||||
void logs::file_writer::log(const char* text, std::size_t size)
|
||||
void logs::file_writer::log(const std::string& text)
|
||||
{
|
||||
m_file.write(text, size);
|
||||
m_file.write(text);
|
||||
}
|
||||
|
||||
void logs::file_listener::log(u64 stamp, const logs::message& msg, const std::string& prefix, const std::string& _text)
|
||||
std::size_t logs::file_writer::size() const
|
||||
{
|
||||
std::string text; text.reserve(prefix.size() + _text.size() + 200);
|
||||
return m_file.pos();
|
||||
}
|
||||
|
||||
void logs::file_listener::log(const logs::channel& ch, logs::level sev, const std::string& text)
|
||||
{
|
||||
std::string msg; msg.reserve(text.size() + 200);
|
||||
|
||||
// Used character: U+00B7 (Middle Dot)
|
||||
switch (msg.sev)
|
||||
switch (sev)
|
||||
{
|
||||
case level::always: text = u8"·A "; break;
|
||||
case level::fatal: text = u8"·F "; break;
|
||||
case level::error: text = u8"·E "; break;
|
||||
case level::todo: text = u8"·U "; break;
|
||||
case level::success: text = u8"·S "; break;
|
||||
case level::warning: text = u8"·W "; break;
|
||||
case level::notice: text = u8"·! "; break;
|
||||
case level::trace: text = u8"·T "; break;
|
||||
case level::always: msg = u8"·A "; break;
|
||||
case level::fatal: msg = u8"·F "; break;
|
||||
case level::error: msg = u8"·E "; break;
|
||||
case level::todo: msg = u8"·U "; break;
|
||||
case level::success: msg = u8"·S "; break;
|
||||
case level::warning: msg = u8"·W "; break;
|
||||
case level::notice: msg = u8"·! "; break;
|
||||
case level::trace: msg = u8"·T "; break;
|
||||
}
|
||||
|
||||
// Print miscosecond timestamp
|
||||
const u64 hours = stamp / 3600'000'000;
|
||||
const u64 mins = (stamp % 3600'000'000) / 60'000'000;
|
||||
const u64 secs = (stamp % 60'000'000) / 1'000'000;
|
||||
const u64 frac = (stamp % 1'000'000);
|
||||
fmt::append(text, "%u:%02u:%02u.%06u ", hours, mins, secs, frac);
|
||||
// TODO: print time?
|
||||
|
||||
if (prefix.size() > 0)
|
||||
if (auto prefix = g_tls_log_prefix)
|
||||
{
|
||||
text += "{";
|
||||
text += prefix;
|
||||
text += "} ";
|
||||
msg += '{';
|
||||
msg += prefix();
|
||||
msg += "} ";
|
||||
}
|
||||
|
||||
if ('\0' != *msg.ch->name)
|
||||
if (ch.name)
|
||||
{
|
||||
text += msg.ch->name;
|
||||
text += msg.sev == level::todo ? " TODO: " : ": ";
|
||||
msg += ch.name;
|
||||
msg += sev == level::todo ? " TODO: " : ": ";
|
||||
}
|
||||
else if (msg.sev == level::todo)
|
||||
else if (sev == level::todo)
|
||||
{
|
||||
text += "TODO: ";
|
||||
msg += "TODO: ";
|
||||
}
|
||||
|
||||
text += _text;
|
||||
text += '\n';
|
||||
msg += text;
|
||||
msg += '\n';
|
||||
|
||||
file_writer::log(text.data(), text.size());
|
||||
file_writer::log(msg);
|
||||
}
|
||||
|
||||
+40
-59
@@ -2,54 +2,19 @@
|
||||
|
||||
#include "types.h"
|
||||
#include "Atomic.h"
|
||||
#include "StrFmt.h"
|
||||
#include <climits>
|
||||
|
||||
namespace logs
|
||||
{
|
||||
enum class level : uint
|
||||
{
|
||||
always, // Highest log severity (unused, cannot be disabled)
|
||||
always, // highest level (unused, cannot be disabled)
|
||||
fatal,
|
||||
error,
|
||||
todo,
|
||||
success,
|
||||
warning,
|
||||
notice,
|
||||
trace, // Lowest severity (usually disabled)
|
||||
|
||||
_uninit = UINT_MAX, // Special value for delayed initialization
|
||||
};
|
||||
|
||||
struct channel;
|
||||
|
||||
// Message information (temporary data)
|
||||
struct message
|
||||
{
|
||||
channel* ch;
|
||||
level sev;
|
||||
|
||||
// Send log message to global logger instance
|
||||
void broadcast(const char*, const fmt_type_info*, const u64*);
|
||||
};
|
||||
|
||||
class listener
|
||||
{
|
||||
// Next listener (linked list)
|
||||
atomic_t<listener*> m_next{};
|
||||
|
||||
friend struct message;
|
||||
|
||||
public:
|
||||
constexpr listener() = default;
|
||||
|
||||
virtual ~listener();
|
||||
|
||||
// Process log message
|
||||
virtual void log(u64 stamp, const message& msg, const std::string& prefix, const std::string& text) = 0;
|
||||
|
||||
// Add new listener
|
||||
static void add(listener*);
|
||||
trace, // lowest level (usually disabled)
|
||||
};
|
||||
|
||||
struct channel
|
||||
@@ -60,26 +25,28 @@ namespace logs
|
||||
// The lowest logging level enabled for this channel (used for early filtering)
|
||||
atomic_t<level> enabled;
|
||||
|
||||
// Constant initialization: channel name
|
||||
constexpr channel(const char* name)
|
||||
// Constant initialization: name and initial log level
|
||||
constexpr channel(const char* name, level enabled = level::trace)
|
||||
: name(name)
|
||||
, enabled(level::_uninit)
|
||||
, enabled(enabled)
|
||||
{
|
||||
}
|
||||
|
||||
// Formatting function
|
||||
template<typename... Args>
|
||||
SAFE_BUFFERS FORCE_INLINE void format(level sev, const char* fmt, const Args&... args)
|
||||
void format(level sev, const char* fmt, const Args&... args) const
|
||||
{
|
||||
if (UNLIKELY(sev <= enabled))
|
||||
{
|
||||
message{this, sev}.broadcast(fmt, fmt::get_type_info<fmt_unveil_t<Args>...>(), fmt_args_t<Args...>{fmt_unveil<Args>::get(args)...});
|
||||
}
|
||||
#ifdef _MSC_VER
|
||||
if (sev <= enabled)
|
||||
#else
|
||||
if (__builtin_expect(sev <= enabled, 0))
|
||||
#endif
|
||||
broadcast(*this, sev, fmt, ::unveil<Args>::get(args)...);
|
||||
}
|
||||
|
||||
#define GEN_LOG_METHOD(_sev)\
|
||||
template<typename... Args>\
|
||||
SAFE_BUFFERS void _sev(const char* fmt, const Args&... args)\
|
||||
void _sev(const char* fmt, const Args&... args) const\
|
||||
{\
|
||||
return format<Args...>(level::_sev, fmt, args...);\
|
||||
}
|
||||
@@ -93,6 +60,10 @@ namespace logs
|
||||
GEN_LOG_METHOD(trace)
|
||||
|
||||
#undef GEN_LOG_METHOD
|
||||
|
||||
private:
|
||||
// Send log message to global logger instance
|
||||
static void broadcast(const channel& ch, level sev, const char* fmt...);
|
||||
};
|
||||
|
||||
/* Small set of predefined channels */
|
||||
@@ -105,20 +76,30 @@ namespace logs
|
||||
extern channel PPU;
|
||||
extern channel SPU;
|
||||
extern channel ARMv7;
|
||||
|
||||
// Log level control: set all channels to level::notice
|
||||
void reset();
|
||||
|
||||
// Log level control: register channel if necessary, set channel level
|
||||
void set_level(const std::string&, level);
|
||||
}
|
||||
|
||||
template<>
|
||||
struct bijective<logs::level, const char*>
|
||||
{
|
||||
static constexpr bijective_pair<logs::level, const char*> map[]
|
||||
{
|
||||
{ logs::level::always, "Nothing" },
|
||||
{ logs::level::fatal, "Fatal" },
|
||||
{ logs::level::error, "Error" },
|
||||
{ logs::level::todo, "TODO" },
|
||||
{ logs::level::success, "Success" },
|
||||
{ logs::level::warning, "Warning" },
|
||||
{ logs::level::notice, "Notice" },
|
||||
{ logs::level::trace, "Trace" },
|
||||
};
|
||||
};
|
||||
|
||||
// Legacy:
|
||||
|
||||
#define LOG_SUCCESS(ch, fmt, ...) logs::ch.success("" fmt, ##__VA_ARGS__)
|
||||
#define LOG_NOTICE(ch, fmt, ...) logs::ch.notice ("" fmt, ##__VA_ARGS__)
|
||||
#define LOG_WARNING(ch, fmt, ...) logs::ch.warning("" fmt, ##__VA_ARGS__)
|
||||
#define LOG_ERROR(ch, fmt, ...) logs::ch.error ("" fmt, ##__VA_ARGS__)
|
||||
#define LOG_TODO(ch, fmt, ...) logs::ch.todo ("" fmt, ##__VA_ARGS__)
|
||||
#define LOG_TRACE(ch, fmt, ...) logs::ch.trace ("" fmt, ##__VA_ARGS__)
|
||||
#define LOG_FATAL(ch, fmt, ...) logs::ch.fatal ("" fmt, ##__VA_ARGS__)
|
||||
#define LOG_SUCCESS(ch, fmt, ...) logs::ch.success(fmt, ##__VA_ARGS__)
|
||||
#define LOG_NOTICE(ch, fmt, ...) logs::ch.notice (fmt, ##__VA_ARGS__)
|
||||
#define LOG_WARNING(ch, fmt, ...) logs::ch.warning(fmt, ##__VA_ARGS__)
|
||||
#define LOG_ERROR(ch, fmt, ...) logs::ch.error (fmt, ##__VA_ARGS__)
|
||||
#define LOG_TODO(ch, fmt, ...) logs::ch.todo (fmt, ##__VA_ARGS__)
|
||||
#define LOG_TRACE(ch, fmt, ...) logs::ch.trace (fmt, ##__VA_ARGS__)
|
||||
#define LOG_FATAL(ch, fmt, ...) logs::ch.fatal (fmt, ##__VA_ARGS__)
|
||||
|
||||
@@ -61,7 +61,7 @@ public:
|
||||
Show();
|
||||
}
|
||||
|
||||
void Update(const u8 thread_id, const u64 value, const wxString& msg)
|
||||
force_inline void Update(const u8 thread_id, const u64 value, const wxString& msg)
|
||||
{
|
||||
if(thread_id > m_cores) return;
|
||||
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <exception>
|
||||
|
||||
template<typename T, typename = std::enable_if_t<std::is_integral<T>::value>>
|
||||
constexpr T align(const T& value, std::uint64_t align)
|
||||
{
|
||||
return static_cast<T>((value + (align - 1)) & ~(align - 1));
|
||||
}
|
||||
|
||||
template<typename To, typename From>
|
||||
constexpr To narrow_impl(const To& result, const From& value, const char* message)
|
||||
{
|
||||
return static_cast<From>(result) != value ? throw std::runtime_error(message) : result;
|
||||
}
|
||||
|
||||
// Narrow cast (similar to gsl::narrow) with fixed message
|
||||
template<typename To, typename From>
|
||||
constexpr auto narrow(const From& value, const char* fixed_msg = "::narrow() failed") -> decltype(static_cast<To>(static_cast<From>(std::declval<To>())))
|
||||
{
|
||||
return narrow_impl(static_cast<To>(value), value, fixed_msg);
|
||||
}
|
||||
|
||||
// Return 32 bit .size() for container
|
||||
template<typename CT>
|
||||
constexpr auto size32(const CT& container, const char* fixed_msg = "::size32() failed") -> decltype(static_cast<std::uint32_t>(container.size()))
|
||||
{
|
||||
return narrow<std::uint32_t>(container.size(), fixed_msg);
|
||||
}
|
||||
|
||||
// Return 32 bit size for an array
|
||||
template<typename T, std::size_t Size>
|
||||
constexpr std::uint32_t size32(const T(&)[Size])
|
||||
{
|
||||
static_assert(Size <= UINT32_MAX, "size32() error: too big");
|
||||
return static_cast<std::uint32_t>(Size);
|
||||
}
|
||||
|
||||
#define CHECK_SIZE(type, size) static_assert(sizeof(type) == size, "Invalid " #type " type size")
|
||||
#define CHECK_ALIGN(type, align) static_assert(alignof(type) == align, "Invalid " #type " type alignment")
|
||||
#define CHECK_MAX_SIZE(type, size) static_assert(sizeof(type) <= size, #type " type size is too big")
|
||||
#define CHECK_SIZE_ALIGN(type, size, align) CHECK_SIZE(type, size); CHECK_ALIGN(type, align)
|
||||
#define CHECK_STORAGE(type, storage) static_assert(sizeof(type) <= sizeof(storage) && alignof(type) <= alignof(decltype(storage)), #type " is too small")
|
||||
|
||||
// Return 32 bit sizeof() to avoid widening/narrowing conversions with size_t
|
||||
#define SIZE_32(type) static_cast<std::uint32_t>(sizeof(type))
|
||||
|
||||
// Return 32 bit alignof() to avoid widening/narrowing conversions with size_t
|
||||
#define ALIGN_32(type) static_cast<std::uint32_t>(alignof(type))
|
||||
|
||||
// Return 32 bit custom offsetof()
|
||||
#define OFFSET_32(type, x) static_cast<std::uint32_t>(reinterpret_cast<std::uintptr_t>(&reinterpret_cast<const volatile char&>(reinterpret_cast<type*>(0ull)->x)))
|
||||
|
||||
// Sometimes to avoid writing std::remove_cv_t<>, example: std::is_same<CV T1, CV T2>
|
||||
#define CV const volatile
|
||||
|
||||
#define CONCATENATE_DETAIL(x, y) x ## y
|
||||
#define CONCATENATE(x, y) CONCATENATE_DETAIL(x, y)
|
||||
|
||||
#define STRINGIZE_DETAIL(x) #x
|
||||
#define STRINGIZE(x) STRINGIZE_DETAIL(x)
|
||||
|
||||
// Macro set, wraps an expression into lambda
|
||||
#define WRAP_EXPR(expr, ...) [&](__VA_ARGS__) { return expr; }
|
||||
#define COPY_EXPR(expr, ...) [=](__VA_ARGS__) { return expr; }
|
||||
#define PURE_EXPR(expr, ...) [] (__VA_ARGS__) { return expr; }
|
||||
|
||||
#define return_ return
|
||||
|
||||
#define HERE "\n(in file " __FILE__ ":" STRINGIZE(__LINE__) ")"
|
||||
|
||||
// Ensure that the expression is evaluated to true. Always evaluated and allowed to have side effects (unlike assert() macro).
|
||||
#define VERIFY(expr) do { if (!(expr)) throw std::runtime_error("Verification failed: " #expr HERE); } while (0)
|
||||
|
||||
// EXPECTS() and ENSURES() are intended to check function arguments and results.
|
||||
// Expressions are not guaranteed to evaluate.
|
||||
#define EXPECTS(expr) do { if (!(expr)) throw std::runtime_error("Precondition failed: " #expr HERE); } while (0)
|
||||
#define ENSURES(expr) do { if (!(expr)) throw std::runtime_error("Postcondition failed: " #expr HERE); } while (0)
|
||||
|
||||
#define DECLARE(...) decltype(__VA_ARGS__) __VA_ARGS__
|
||||
|
||||
#define STR_CASE(value) case value: return #value
|
||||
@@ -0,0 +1,51 @@
|
||||
#include "Platform.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__ */
|
||||
@@ -0,0 +1,122 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <immintrin.h>
|
||||
#include <emmintrin.h>
|
||||
|
||||
#define IS_LE_MACHINE 1
|
||||
#define IS_BE_MACHINE 0
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <intrin.h>
|
||||
#else
|
||||
#include <x86intrin.h>
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define ASSUME(cond) __assume(cond)
|
||||
#define LIKELY(cond) (cond)
|
||||
#define UNLIKELY(cond) (cond)
|
||||
#else
|
||||
#define ASSUME(cond) do { if (!(cond)) __builtin_unreachable(); } while (0)
|
||||
#define LIKELY(cond) __builtin_expect(!!(cond), 1)
|
||||
#define UNLIKELY(cond) __builtin_expect(!!(cond), 0)
|
||||
#endif
|
||||
|
||||
// Some platforms don't support thread_local well yet.
|
||||
#ifndef _MSC_VER
|
||||
#define thread_local __thread
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define never_inline __declspec(noinline)
|
||||
#else
|
||||
#define never_inline __attribute__((noinline))
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define force_inline __forceinline
|
||||
#else
|
||||
#define force_inline __attribute__((always_inline)) inline
|
||||
#endif
|
||||
|
||||
#if defined(__GNUG__)
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#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__ */
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// Helper function, used by ""_u16, ""_u32, ""_u64
|
||||
constexpr std::uint8_t to_u8(char c)
|
||||
{
|
||||
return static_cast<std::uint8_t>(c);
|
||||
}
|
||||
|
||||
// Convert 2-byte string to u16 value like reinterpret_cast does
|
||||
constexpr std::uint16_t operator""_u16(const char* s, std::size_t length)
|
||||
{
|
||||
return length != 2 ? throw s :
|
||||
#if IS_LE_MACHINE == 1
|
||||
to_u8(s[1]) << 8 | to_u8(s[0]);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Convert 4-byte string to u32 value like reinterpret_cast does
|
||||
constexpr std::uint32_t operator""_u32(const char* s, std::size_t length)
|
||||
{
|
||||
return length != 4 ? throw s :
|
||||
#if IS_LE_MACHINE == 1
|
||||
to_u8(s[3]) << 24 | to_u8(s[2]) << 16 | to_u8(s[1]) << 8 | to_u8(s[0]);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Convert 8-byte string to u64 value like reinterpret_cast does
|
||||
constexpr std::uint64_t operator""_u64(const char* s, std::size_t length)
|
||||
{
|
||||
return length != 8 ? throw s :
|
||||
#if IS_LE_MACHINE == 1
|
||||
static_cast<std::uint64_t>(to_u8(s[7]) << 24 | to_u8(s[6]) << 16 | to_u8(s[5]) << 8 | to_u8(s[4])) << 32 | to_u8(s[3]) << 24 | to_u8(s[2]) << 16 | to_u8(s[1]) << 8 | to_u8(s[0]);
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
#include "Utilities/Semaphore.h"
|
||||
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
struct benaphore::internal
|
||||
{
|
||||
std::mutex mutex;
|
||||
|
||||
std::size_t acq_order{};
|
||||
std::size_t rel_order{};
|
||||
|
||||
std::condition_variable cond;
|
||||
};
|
||||
|
||||
void benaphore::wait_hard()
|
||||
{
|
||||
initialize_once();
|
||||
|
||||
std::unique_lock<std::mutex> lock(m_data->mutex);
|
||||
|
||||
// Notify non-zero waiter queue size
|
||||
if (m_value.exchange(-1) == 1)
|
||||
{
|
||||
// Return immediately (acquired)
|
||||
m_value = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Remember the order
|
||||
const std::size_t order = ++m_data->acq_order;
|
||||
|
||||
// Wait for the appropriate rel_order (TODO)
|
||||
while (m_data->rel_order < order)
|
||||
{
|
||||
m_data->cond.wait(lock);
|
||||
}
|
||||
|
||||
if (order == m_data->acq_order && m_data->acq_order == m_data->rel_order)
|
||||
{
|
||||
// Cleaup
|
||||
m_data->acq_order = 0;
|
||||
m_data->rel_order = 0;
|
||||
m_value.compare_and_swap(-1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void benaphore::post_hard()
|
||||
{
|
||||
initialize_once();
|
||||
|
||||
std::unique_lock<std::mutex> lock(m_data->mutex);
|
||||
|
||||
if (m_value.compare_and_swap(0, 1) != -1)
|
||||
{
|
||||
// Do nothing (released)
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_data->acq_order == m_data->rel_order)
|
||||
{
|
||||
m_value = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
// Awake one thread
|
||||
m_data->rel_order += 1;
|
||||
|
||||
// Unlock and notify
|
||||
lock.unlock();
|
||||
m_data->cond.notify_one();
|
||||
}
|
||||
|
||||
void benaphore::initialize_once()
|
||||
{
|
||||
if (UNLIKELY(!m_data))
|
||||
{
|
||||
auto ptr = new benaphore::internal;
|
||||
|
||||
if (!m_data.compare_and_swap_test(nullptr, ptr))
|
||||
{
|
||||
delete ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
benaphore::~benaphore()
|
||||
{
|
||||
delete m_data;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "Atomic.h"
|
||||
#include "Platform.h"
|
||||
|
||||
// Binary semaphore
|
||||
class benaphore
|
||||
{
|
||||
struct internal;
|
||||
|
||||
// Reserved value (-1) enforces *_hard() calls
|
||||
atomic_t<u32> m_value{};
|
||||
|
||||
atomic_t<internal*> m_data{};
|
||||
|
||||
void wait_hard();
|
||||
void post_hard();
|
||||
|
||||
public:
|
||||
constexpr benaphore() = default;
|
||||
|
||||
~benaphore();
|
||||
|
||||
// Initialize internal data
|
||||
void initialize_once();
|
||||
|
||||
void wait()
|
||||
{
|
||||
if (UNLIKELY(!m_value.compare_and_swap_test(1, 0)))
|
||||
{
|
||||
wait_hard();
|
||||
}
|
||||
}
|
||||
|
||||
bool try_wait()
|
||||
{
|
||||
return m_value.compare_and_swap_test(1, 0);
|
||||
}
|
||||
|
||||
void post()
|
||||
{
|
||||
if (UNLIKELY(!m_value.compare_and_swap_test(0, 1)))
|
||||
{
|
||||
post_hard();
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,187 @@
|
||||
#include "SharedMutex.h"
|
||||
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
struct shared_mutex::internal
|
||||
{
|
||||
std::mutex mutex;
|
||||
|
||||
std::size_t rq_size{}; // Reader queue size (threads waiting on m_rcv)
|
||||
std::size_t wq_size{}; // Writer queue size (threads waiting on m_wcv and m_ocv)
|
||||
|
||||
std::condition_variable rcv; // Reader queue
|
||||
std::condition_variable wcv; // Writer queue
|
||||
std::condition_variable ocv; // For current exclusive owner
|
||||
};
|
||||
|
||||
void shared_mutex::lock_shared_hard()
|
||||
{
|
||||
initialize_once();
|
||||
|
||||
std::unique_lock<std::mutex> lock(m_data->mutex);
|
||||
|
||||
// Validate
|
||||
if ((m_ctrl & SM_INVALID_BIT) != 0) throw std::runtime_error("shared_mutex::lock_shared(): Invalid bit");
|
||||
if ((m_ctrl & SM_READER_MASK) == 0) throw std::runtime_error("shared_mutex::lock_shared(): No readers");
|
||||
|
||||
// Notify non-zero reader queue size
|
||||
m_ctrl |= SM_WAITERS_BIT, m_data->rq_size++;
|
||||
|
||||
// Fix excess reader count
|
||||
if ((--m_ctrl & SM_READER_MASK) == 0 && m_data->wq_size)
|
||||
{
|
||||
// Notify exclusive owner
|
||||
m_data->ocv.notify_one();
|
||||
}
|
||||
|
||||
// Obtain the reader lock
|
||||
while (true)
|
||||
{
|
||||
const auto ctrl = m_ctrl.load();
|
||||
|
||||
// Check writers and reader limit
|
||||
if (m_data->wq_size || (ctrl & ~SM_WAITERS_BIT) >= SM_READER_MAX)
|
||||
{
|
||||
m_data->rcv.wait(lock);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m_ctrl.compare_and_swap_test(ctrl, ctrl + 1))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!--m_data->rq_size && !m_data->wq_size)
|
||||
{
|
||||
m_ctrl &= ~SM_WAITERS_BIT;
|
||||
}
|
||||
}
|
||||
|
||||
void shared_mutex::unlock_shared_notify()
|
||||
{
|
||||
initialize_once();
|
||||
|
||||
std::unique_lock<std::mutex> lock(m_data->mutex);
|
||||
|
||||
if ((m_ctrl & SM_READER_MASK) == 0 && m_data->wq_size)
|
||||
{
|
||||
// Notify exclusive owner
|
||||
lock.unlock();
|
||||
m_data->ocv.notify_one();
|
||||
}
|
||||
else if (m_data->rq_size)
|
||||
{
|
||||
// Notify other readers
|
||||
lock.unlock();
|
||||
m_data->rcv.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
void shared_mutex::lock_hard()
|
||||
{
|
||||
initialize_once();
|
||||
|
||||
std::unique_lock<std::mutex> lock(m_data->mutex);
|
||||
|
||||
// Validate
|
||||
if ((m_ctrl & SM_INVALID_BIT) != 0) throw std::runtime_error("shared_mutex::lock(): Invalid bit");
|
||||
|
||||
// Notify non-zero writer queue size
|
||||
m_ctrl |= SM_WAITERS_BIT, m_data->wq_size++;
|
||||
|
||||
// Obtain the writer lock
|
||||
while (true)
|
||||
{
|
||||
const auto ctrl = m_ctrl.load();
|
||||
|
||||
if (ctrl & SM_WRITER_LOCK)
|
||||
{
|
||||
m_data->wcv.wait(lock);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m_ctrl.compare_and_swap_test(ctrl, ctrl | SM_WRITER_LOCK))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for remaining readers
|
||||
while ((m_ctrl & SM_READER_MASK) != 0)
|
||||
{
|
||||
m_data->ocv.wait(lock);
|
||||
}
|
||||
|
||||
if (!--m_data->wq_size && !m_data->rq_size)
|
||||
{
|
||||
m_ctrl &= ~SM_WAITERS_BIT;
|
||||
}
|
||||
}
|
||||
|
||||
void shared_mutex::unlock_notify()
|
||||
{
|
||||
initialize_once();
|
||||
|
||||
std::unique_lock<std::mutex> lock(m_data->mutex);
|
||||
|
||||
if (m_data->wq_size)
|
||||
{
|
||||
// Notify next exclusive owner
|
||||
lock.unlock();
|
||||
m_data->wcv.notify_one();
|
||||
}
|
||||
else if (m_data->rq_size)
|
||||
{
|
||||
// Notify all readers
|
||||
lock.unlock();
|
||||
m_data->rcv.notify_all();
|
||||
}
|
||||
}
|
||||
|
||||
void shared_mutex::lock_upgrade_hard()
|
||||
{
|
||||
unlock_shared();
|
||||
lock();
|
||||
}
|
||||
|
||||
void shared_mutex::lock_degrade_hard()
|
||||
{
|
||||
initialize_once();
|
||||
|
||||
std::unique_lock<std::mutex> lock(m_data->mutex);
|
||||
|
||||
m_ctrl -= SM_WRITER_LOCK - 1;
|
||||
|
||||
if (m_data->rq_size)
|
||||
{
|
||||
// Notify all readers
|
||||
lock.unlock();
|
||||
m_data->rcv.notify_all();
|
||||
}
|
||||
else if (m_data->wq_size)
|
||||
{
|
||||
// Notify next exclusive owner
|
||||
lock.unlock();
|
||||
m_data->wcv.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
void shared_mutex::initialize_once()
|
||||
{
|
||||
if (UNLIKELY(!m_data))
|
||||
{
|
||||
auto ptr = new shared_mutex::internal;
|
||||
|
||||
if (!m_data.compare_and_swap_test(nullptr, ptr))
|
||||
{
|
||||
delete ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
shared_mutex::~shared_mutex()
|
||||
{
|
||||
delete m_data;
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "Atomic.h"
|
||||
#include "Platform.h"
|
||||
|
||||
//! An attempt to create effective implementation of "shared mutex", lock-free in optimistic case.
|
||||
//! All locking and unlocking may be done by a single LOCK XADD or LOCK CMPXCHG instruction.
|
||||
//! 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_WAITERS_BIT = 1u << 30, // Flag set if m_wq_size or m_rq_size is non-zero
|
||||
SM_INVALID_BIT = 1u << 29, // Unreachable reader count bit (may be set by incorrect unlock_shared() call)
|
||||
|
||||
SM_READER_MASK = SM_WAITERS_BIT - 1, // Valid reader count bit mask
|
||||
SM_READER_MAX = 1u << 24, // Max reader count
|
||||
};
|
||||
|
||||
atomic_t<u32> m_ctrl{}; // Control variable: reader count | SM_* flags
|
||||
|
||||
struct internal;
|
||||
|
||||
atomic_t<internal*> m_data{}; // Internal data
|
||||
|
||||
void lock_shared_hard();
|
||||
void unlock_shared_notify();
|
||||
|
||||
void lock_hard();
|
||||
void unlock_notify();
|
||||
|
||||
void lock_upgrade_hard();
|
||||
void lock_degrade_hard();
|
||||
|
||||
public:
|
||||
constexpr shared_mutex() = default;
|
||||
|
||||
// Initialize internal data
|
||||
void initialize_once();
|
||||
|
||||
~shared_mutex();
|
||||
|
||||
bool try_lock_shared()
|
||||
{
|
||||
const u32 ctrl = m_ctrl.load();
|
||||
|
||||
return ctrl < SM_READER_MAX && m_ctrl.compare_and_swap_test(ctrl, ctrl + 1);
|
||||
}
|
||||
|
||||
void lock_shared()
|
||||
{
|
||||
// Optimization: unconditional increment, compensated later
|
||||
if (UNLIKELY(m_ctrl++ >= SM_READER_MAX))
|
||||
{
|
||||
lock_shared_hard();
|
||||
}
|
||||
}
|
||||
|
||||
void unlock_shared()
|
||||
{
|
||||
if (UNLIKELY(m_ctrl-- >= SM_READER_MAX))
|
||||
{
|
||||
unlock_shared_notify();
|
||||
}
|
||||
}
|
||||
|
||||
bool try_lock()
|
||||
{
|
||||
return !m_ctrl && m_ctrl.compare_and_swap_test(0, SM_WRITER_LOCK);
|
||||
}
|
||||
|
||||
void lock()
|
||||
{
|
||||
if (UNLIKELY(!m_ctrl.compare_and_swap_test(0, SM_WRITER_LOCK)))
|
||||
{
|
||||
lock_hard();
|
||||
}
|
||||
}
|
||||
|
||||
void unlock()
|
||||
{
|
||||
m_ctrl &= ~SM_WRITER_LOCK;
|
||||
|
||||
if (UNLIKELY(m_ctrl))
|
||||
{
|
||||
unlock_notify();
|
||||
}
|
||||
}
|
||||
|
||||
bool try_lock_upgrade()
|
||||
{
|
||||
return m_ctrl == 1 && m_ctrl.compare_and_swap_test(1, SM_WRITER_LOCK);
|
||||
}
|
||||
|
||||
bool try_lock_degrade()
|
||||
{
|
||||
return m_ctrl == SM_WRITER_LOCK && m_ctrl.compare_and_swap_test(SM_WRITER_LOCK, 1);
|
||||
}
|
||||
|
||||
void lock_upgrade()
|
||||
{
|
||||
if (UNLIKELY(!m_ctrl.compare_and_swap_test(1, SM_WRITER_LOCK)))
|
||||
{
|
||||
lock_upgrade_hard();
|
||||
}
|
||||
}
|
||||
|
||||
void lock_degrade()
|
||||
{
|
||||
if (UNLIKELY(!m_ctrl.compare_and_swap_test(SM_WRITER_LOCK, 1)))
|
||||
{
|
||||
lock_degrade_hard();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//! Simplified shared (reader) lock implementation.
|
||||
//! 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();
|
||||
}
|
||||
};
|
||||
|
||||
//! Simplified exclusive (writer) lock implementation.
|
||||
//! std::lock_guard may or std::unique_lock be used instead if necessary.
|
||||
class writer_lock final
|
||||
{
|
||||
shared_mutex& m_mutex;
|
||||
|
||||
public:
|
||||
writer_lock(const writer_lock&) = delete;
|
||||
|
||||
writer_lock(shared_mutex& mutex)
|
||||
: m_mutex(mutex)
|
||||
{
|
||||
m_mutex.lock();
|
||||
}
|
||||
|
||||
~writer_lock()
|
||||
{
|
||||
m_mutex.unlock();
|
||||
}
|
||||
};
|
||||
|
||||
// Exclusive (writer) lock in the scope of shared (reader) lock.
|
||||
class upgraded_lock final
|
||||
{
|
||||
shared_mutex& m_mutex;
|
||||
|
||||
public:
|
||||
upgraded_lock(const writer_lock&) = delete;
|
||||
|
||||
upgraded_lock(shared_mutex& mutex)
|
||||
: m_mutex(mutex)
|
||||
{
|
||||
m_mutex.lock_upgrade();
|
||||
}
|
||||
|
||||
~upgraded_lock()
|
||||
{
|
||||
m_mutex.lock_degrade();
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,85 @@
|
||||
#pragma once
|
||||
|
||||
#include <deque>
|
||||
|
||||
// Tag used in sleep_entry<> constructor
|
||||
static struct defer_sleep_tag {} constexpr defer_sleep{};
|
||||
|
||||
// Define sleep queue as std::deque with T* pointers, T - thread type
|
||||
template<typename T> using sleep_queue = std::deque<T*>;
|
||||
|
||||
// Automatic object handling a thread pointer (T*) in the sleep queue
|
||||
// Sleep is called in the constructor (if not null)
|
||||
// Awake is called in the destructor (if not null)
|
||||
// Sleep queue is actually std::deque with pointers, be careful about the lifetime
|
||||
template<typename T, void(T::*Sleep)() = &T::sleep, void(T::*Awake)() = &T::awake>
|
||||
class sleep_entry final
|
||||
{
|
||||
sleep_queue<T>& m_queue;
|
||||
T& m_thread;
|
||||
|
||||
public:
|
||||
// Constructor; enter() not called
|
||||
sleep_entry(sleep_queue<T>& queue, T& entry, const defer_sleep_tag&)
|
||||
: m_queue(queue)
|
||||
, m_thread(entry)
|
||||
{
|
||||
if (Sleep) (m_thread.*Sleep)();
|
||||
}
|
||||
|
||||
// Constructor; calls enter()
|
||||
sleep_entry(sleep_queue<T>& queue, T& entry)
|
||||
: sleep_entry(queue, entry, defer_sleep)
|
||||
{
|
||||
enter();
|
||||
}
|
||||
|
||||
// Destructor; calls leave()
|
||||
~sleep_entry()
|
||||
{
|
||||
leave();
|
||||
if (Awake) (m_thread.*Awake)();
|
||||
}
|
||||
|
||||
// Add thread to the sleep queue
|
||||
void enter()
|
||||
{
|
||||
for (auto t : m_queue)
|
||||
{
|
||||
if (t == &m_thread)
|
||||
{
|
||||
// Already exists, is it an error?
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_queue.emplace_back(&m_thread);
|
||||
}
|
||||
|
||||
// Remove thread from the sleep queue
|
||||
void leave()
|
||||
{
|
||||
for (auto it = m_queue.begin(), end = m_queue.end(); it != end; it++)
|
||||
{
|
||||
if (*it == &m_thread)
|
||||
{
|
||||
m_queue.erase(it);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check whether the thread exists in the sleep queue
|
||||
explicit operator bool() const
|
||||
{
|
||||
for (auto it = m_queue.begin(), end = m_queue.end(); it != end; it++)
|
||||
{
|
||||
if (*it == &m_thread)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
+56
-288
@@ -1,305 +1,73 @@
|
||||
#include "StrFmt.h"
|
||||
#include "BEType.h"
|
||||
#include "StrUtil.h"
|
||||
#include "cfmt.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <algorithm>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
#else
|
||||
#include <errno.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::unsafe_vformat(const char* fmt, va_list _args) noexcept
|
||||
{
|
||||
// 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();;)
|
||||
{
|
||||
va_list args;
|
||||
va_copy(args, _args);
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wformat-security"
|
||||
#endif
|
||||
|
||||
template <>
|
||||
void fmt_class_string<std::pair<const fmt_type_info*, u64>>::format(std::string& out, u64 arg)
|
||||
{
|
||||
// Dynamic format arg
|
||||
const auto& pair = get_object(arg);
|
||||
|
||||
if (pair.first)
|
||||
{
|
||||
pair.first->fmt_string(out, pair.second);
|
||||
}
|
||||
}
|
||||
|
||||
void fmt_class_string<const void*>::format(std::string& out, u64 arg)
|
||||
{
|
||||
if (arg)
|
||||
{
|
||||
fmt::append(out, "%p", reinterpret_cast<const void*>(static_cast<std::uintptr_t>(arg)));
|
||||
}
|
||||
else
|
||||
{
|
||||
out += "(NULL)";
|
||||
}
|
||||
}
|
||||
|
||||
void fmt_class_string<const char*>::format(std::string& out, u64 arg)
|
||||
{
|
||||
if (arg)
|
||||
{
|
||||
out += reinterpret_cast<const char*>(static_cast<std::uintptr_t>(arg));
|
||||
}
|
||||
else
|
||||
{
|
||||
out += "(NULL)";
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void fmt_class_string<std::string>::format(std::string& out, u64 arg)
|
||||
{
|
||||
out += get_object(arg).c_str(); // TODO?
|
||||
}
|
||||
|
||||
template <>
|
||||
void fmt_class_string<std::vector<char>>::format(std::string& out, u64 arg)
|
||||
{
|
||||
const std::vector<char>& obj = get_object(arg);
|
||||
out.append(obj.cbegin(), obj.cend());
|
||||
}
|
||||
|
||||
template <>
|
||||
void fmt_class_string<char>::format(std::string& out, u64 arg)
|
||||
{
|
||||
fmt::append(out, "%#hhx", static_cast<char>(arg));
|
||||
}
|
||||
|
||||
template <>
|
||||
void fmt_class_string<uchar>::format(std::string& out, u64 arg)
|
||||
{
|
||||
fmt::append(out, "%#hhx", static_cast<uchar>(arg));
|
||||
}
|
||||
|
||||
template <>
|
||||
void fmt_class_string<schar>::format(std::string& out, u64 arg)
|
||||
{
|
||||
fmt::append(out, "%#hhx", static_cast<schar>(arg));
|
||||
}
|
||||
|
||||
template <>
|
||||
void fmt_class_string<short>::format(std::string& out, u64 arg)
|
||||
{
|
||||
fmt::append(out, "%#hx", static_cast<short>(arg));
|
||||
}
|
||||
|
||||
template <>
|
||||
void fmt_class_string<ushort>::format(std::string& out, u64 arg)
|
||||
{
|
||||
fmt::append(out, "%#hx", static_cast<ushort>(arg));
|
||||
}
|
||||
|
||||
template <>
|
||||
void fmt_class_string<int>::format(std::string& out, u64 arg)
|
||||
{
|
||||
fmt::append(out, "%#x", static_cast<int>(arg));
|
||||
}
|
||||
|
||||
template <>
|
||||
void fmt_class_string<uint>::format(std::string& out, u64 arg)
|
||||
{
|
||||
fmt::append(out, "%#x", static_cast<uint>(arg));
|
||||
}
|
||||
|
||||
template <>
|
||||
void fmt_class_string<long>::format(std::string& out, u64 arg)
|
||||
{
|
||||
fmt::append(out, "%#lx", static_cast<long>(arg));
|
||||
}
|
||||
|
||||
template <>
|
||||
void fmt_class_string<ulong>::format(std::string& out, u64 arg)
|
||||
{
|
||||
fmt::append(out, "%#lx", static_cast<ulong>(arg));
|
||||
}
|
||||
|
||||
template <>
|
||||
void fmt_class_string<llong>::format(std::string& out, u64 arg)
|
||||
{
|
||||
fmt::append(out, "%#llx", static_cast<llong>(arg));
|
||||
}
|
||||
|
||||
template <>
|
||||
void fmt_class_string<ullong>::format(std::string& out, u64 arg)
|
||||
{
|
||||
fmt::append(out, "%#llx", static_cast<ullong>(arg));
|
||||
}
|
||||
|
||||
template <>
|
||||
void fmt_class_string<float>::format(std::string& out, u64 arg)
|
||||
{
|
||||
fmt::append(out, "%gf", static_cast<float>(reinterpret_cast<f64&>(arg)));
|
||||
}
|
||||
|
||||
template <>
|
||||
void fmt_class_string<double>::format(std::string& out, u64 arg)
|
||||
{
|
||||
fmt::append(out, "%g", reinterpret_cast<f64&>(arg));
|
||||
}
|
||||
|
||||
template <>
|
||||
void fmt_class_string<bool>::format(std::string& out, u64 arg)
|
||||
{
|
||||
out += arg ? "true" : "false";
|
||||
}
|
||||
|
||||
template <>
|
||||
void fmt_class_string<v128>::format(std::string& out, u64 arg)
|
||||
{
|
||||
const v128& vec = get_object(arg);
|
||||
fmt::append(out, "0x%016llx%016llx", vec._u64[1], vec._u64[0]);
|
||||
}
|
||||
|
||||
namespace fmt
|
||||
{
|
||||
void raw_error(const char* msg)
|
||||
{
|
||||
throw std::runtime_error{msg};
|
||||
}
|
||||
|
||||
void raw_verify_error(const char* msg, const fmt_type_info* sup, u64 arg)
|
||||
{
|
||||
std::string out{"Verification failed"};
|
||||
|
||||
// Print error code (may be irrelevant)
|
||||
#ifdef _WIN32
|
||||
if (DWORD error = GetLastError())
|
||||
{
|
||||
fmt::append(out, " (e=%#x)", error);
|
||||
}
|
||||
#else
|
||||
if (int error = errno)
|
||||
{
|
||||
fmt::append(out, " (e=%d)", error);
|
||||
}
|
||||
const std::size_t len = std::vsnprintf(buf_addr, buf_size, fmt, args);
|
||||
#ifndef _MSC_VER
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
va_end(args);
|
||||
|
||||
if (sup)
|
||||
assert(len <= INT_MAX);
|
||||
|
||||
if (len < buf_size)
|
||||
{
|
||||
out += " (";
|
||||
sup->fmt_string(out, arg); // Print value
|
||||
out += ")";
|
||||
return{ buf_addr, len };
|
||||
}
|
||||
|
||||
if (msg)
|
||||
{
|
||||
out += ": ";
|
||||
out += msg;
|
||||
}
|
||||
|
||||
throw std::runtime_error{out};
|
||||
buf.reset(buf_addr = new char[buf_size = len + 1]);
|
||||
}
|
||||
|
||||
void raw_narrow_error(const char* msg, const fmt_type_info* sup, u64 arg)
|
||||
{
|
||||
std::string out{"Narrow error"};
|
||||
|
||||
if (sup)
|
||||
{
|
||||
out += " (";
|
||||
sup->fmt_string(out, arg); // Print value
|
||||
out += ")";
|
||||
}
|
||||
|
||||
if (msg)
|
||||
{
|
||||
out += ": ";
|
||||
out += msg;
|
||||
}
|
||||
|
||||
throw std::range_error{out};
|
||||
}
|
||||
|
||||
// Hidden template
|
||||
template <typename T>
|
||||
void raw_throw_exception(const char* fmt, const fmt_type_info* sup, const u64* args)
|
||||
{
|
||||
std::string out;
|
||||
raw_append(out, fmt, sup, args);
|
||||
throw T{out};
|
||||
}
|
||||
|
||||
// Explicit instantiations (not exhaustive)
|
||||
template void raw_throw_exception<std::runtime_error>(const char*, const fmt_type_info*, const u64*);
|
||||
template void raw_throw_exception<std::logic_error>(const char*, const fmt_type_info*, const u64*);
|
||||
template void raw_throw_exception<std::domain_error>(const char*, const fmt_type_info*, const u64*);
|
||||
template void raw_throw_exception<std::invalid_argument>(const char*, const fmt_type_info*, const u64*);
|
||||
template void raw_throw_exception<std::out_of_range>(const char*, const fmt_type_info*, const u64*);
|
||||
template void raw_throw_exception<std::range_error>(const char*, const fmt_type_info*, const u64*);
|
||||
template void raw_throw_exception<std::overflow_error>(const char*, const fmt_type_info*, const u64*);
|
||||
template void raw_throw_exception<std::underflow_error>(const char*, const fmt_type_info*, const u64*);
|
||||
|
||||
struct cfmt_src;
|
||||
}
|
||||
|
||||
// Temporary implementation
|
||||
struct fmt::cfmt_src
|
||||
std::string fmt::unsafe_format(const char* fmt...) noexcept
|
||||
{
|
||||
const fmt_type_info* sup;
|
||||
const u64* args;
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
auto result = unsafe_vformat(fmt, args);
|
||||
va_end(args);
|
||||
|
||||
bool test(std::size_t index) const
|
||||
{
|
||||
if (!sup[index].fmt_string)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T get(std::size_t index) const
|
||||
{
|
||||
return *reinterpret_cast<const T*>(reinterpret_cast<const u8*>(args + index));
|
||||
}
|
||||
|
||||
void skip(std::size_t extra)
|
||||
{
|
||||
++sup += extra;
|
||||
++args += extra;
|
||||
}
|
||||
|
||||
std::size_t fmt_string(std::string& out, std::size_t extra) const
|
||||
{
|
||||
const std::size_t start = out.size();
|
||||
sup[extra].fmt_string(out, args[extra]);
|
||||
return out.size() - start;
|
||||
}
|
||||
|
||||
// Returns type size (0 if unknown, pointer, unsigned, assumed max)
|
||||
std::size_t type(std::size_t extra) const
|
||||
{
|
||||
// Hack: use known function pointers to determine type
|
||||
#define TYPE(type) \
|
||||
if (sup[extra].fmt_string == &fmt_class_string<type>::format) return sizeof(type);
|
||||
|
||||
TYPE(int);
|
||||
TYPE(llong);
|
||||
TYPE(schar);
|
||||
TYPE(short);
|
||||
if (std::is_signed<char>::value) TYPE(char);
|
||||
TYPE(long);
|
||||
|
||||
#undef TYPE
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static constexpr std::size_t size_char = 1;
|
||||
static constexpr std::size_t size_short = 2;
|
||||
static constexpr std::size_t size_int = 0;
|
||||
static constexpr std::size_t size_long = sizeof(ulong);
|
||||
static constexpr std::size_t size_llong = sizeof(ullong);
|
||||
static constexpr std::size_t size_size = sizeof(std::size_t);
|
||||
static constexpr std::size_t size_max = sizeof(std::uintmax_t);
|
||||
static constexpr std::size_t size_diff = sizeof(std::ptrdiff_t);
|
||||
};
|
||||
|
||||
void fmt::raw_append(std::string& out, const char* fmt, const fmt_type_info* sup, const u64* args) noexcept
|
||||
fmt::exception_base::exception_base(const char* fmt...)
|
||||
: std::runtime_error((va_start(m_args, fmt), unsafe_vformat(fmt, m_args)))
|
||||
{
|
||||
cfmt_append(out, fmt, cfmt_src{sup, args});
|
||||
va_end(m_args);
|
||||
}
|
||||
|
||||
std::string fmt::replace_first(const std::string& src, const std::string& from, const std::string& to)
|
||||
@@ -314,7 +82,7 @@ std::string fmt::replace_first(const std::string& src, const std::string& from,
|
||||
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 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))
|
||||
@@ -334,7 +102,7 @@ std::vector<std::string> fmt::split(const std::string& source, std::initializer_
|
||||
|
||||
for (size_t cursor_end = 0; cursor_end < source.length(); ++cursor_end)
|
||||
{
|
||||
for (auto& separator : separators)
|
||||
for (auto &separator : separators)
|
||||
{
|
||||
if (strncmp(source.c_str() + cursor_end, separator.c_str(), separator.length()) == 0)
|
||||
{
|
||||
@@ -343,7 +111,7 @@ std::vector<std::string> fmt::split(const std::string& source, std::initializer_
|
||||
result.push_back(candidate);
|
||||
|
||||
cursor_begin = cursor_end + separator.length();
|
||||
cursor_end = cursor_begin - 1;
|
||||
cursor_end = cursor_begin - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -362,7 +130,7 @@ 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{};
|
||||
|
||||
return source.substr(begin, source.find_last_not_of(values) + 1);
|
||||
}
|
||||
@@ -375,7 +143,7 @@ std::string fmt::to_upper(const std::string& string)
|
||||
return result;
|
||||
}
|
||||
|
||||
bool fmt::match(const std::string& source, const std::string& mask)
|
||||
bool fmt::match(const std::string &source, const std::string &mask)
|
||||
{
|
||||
std::size_t source_position = 0, mask_position = 0;
|
||||
|
||||
|
||||
+31
-270
@@ -1,291 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#include <cstdarg>
|
||||
#include <exception>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include "Platform.h"
|
||||
#include "types.h"
|
||||
|
||||
namespace fmt
|
||||
{
|
||||
template <typename... Args>
|
||||
static std::string format(const char*, const Args&...);
|
||||
}
|
||||
std::string unsafe_format(const char* fmt...) noexcept;
|
||||
std::string unsafe_vformat(const char*, va_list) noexcept;
|
||||
|
||||
template <typename T, typename>
|
||||
struct fmt_unveil
|
||||
{
|
||||
static_assert(sizeof(T) > 0, "fmt_unveil<> error: incomplete type");
|
||||
|
||||
using type = T;
|
||||
|
||||
static inline u64 get(const T& arg)
|
||||
// Formatting function
|
||||
template<typename... Args>
|
||||
inline std::string format(const char* fmt, const Args&... args)
|
||||
{
|
||||
return reinterpret_cast<std::uintptr_t>(&arg);
|
||||
return unsafe_format(fmt, ::unveil<Args>::get(args)...);
|
||||
}
|
||||
|
||||
// Temporary value container (can possibly be created by other fmt_unveil<> specializations)
|
||||
struct u64_wrapper
|
||||
// Helper class
|
||||
class exception_base : public std::runtime_error
|
||||
{
|
||||
T arg;
|
||||
// Helper (there is no other room)
|
||||
va_list m_args;
|
||||
|
||||
// Allow implicit conversion
|
||||
operator u64() const
|
||||
protected:
|
||||
// Internal formatting constructor
|
||||
exception_base(const char* fmt...);
|
||||
};
|
||||
|
||||
// Exception type derived from std::runtime_error with formatting constructor
|
||||
class exception : public exception_base
|
||||
{
|
||||
public:
|
||||
template<typename... Args>
|
||||
exception(const char* fmt, const Args&... args)
|
||||
: exception_base(fmt, ::unveil<Args>::get(args)...)
|
||||
{
|
||||
return reinterpret_cast<std::uintptr_t>(&arg);
|
||||
}
|
||||
};
|
||||
|
||||
// This overload resolution takes the precedence
|
||||
static inline u64_wrapper get(T&& arg)
|
||||
// Narrow cast (similar to gsl::narrow) with exception message formatting
|
||||
template<typename To, typename From, typename... Args>
|
||||
inline auto narrow(const char* format_str, const From& value, const Args&... args) -> decltype(static_cast<To>(static_cast<From>(std::declval<To>())))
|
||||
{
|
||||
return u64_wrapper{std::move(arg)};
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct fmt_unveil<T, std::enable_if_t<std::is_integral<T>::value && sizeof(T) <= 8 && alignof(T) <= 8>>
|
||||
{
|
||||
using type = T;
|
||||
|
||||
static inline u64 get(T arg)
|
||||
{
|
||||
return static_cast<T>(arg);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct fmt_unveil<T, std::enable_if_t<std::is_floating_point<T>::value && sizeof(T) <= 8 && alignof(T) <= 8>>
|
||||
{
|
||||
using type = T;
|
||||
|
||||
// Convert FP to f64 and reinterpret as u64
|
||||
static inline u64 get(const f64& arg)
|
||||
{
|
||||
return *reinterpret_cast<const u64*>(reinterpret_cast<const u8*>(&arg));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct fmt_unveil<f16, void>
|
||||
{
|
||||
using type = f16;
|
||||
|
||||
static inline u64 get(const f16& arg)
|
||||
{
|
||||
return fmt_unveil<f64>::get(arg.operator float());
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct fmt_unveil<T, std::enable_if_t<std::is_enum<T>::value>>
|
||||
{
|
||||
using type = T;
|
||||
|
||||
static inline u64 get(T arg)
|
||||
{
|
||||
return static_cast<std::underlying_type_t<T>>(arg);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct fmt_unveil<T*, void>
|
||||
{
|
||||
using type = const T*;
|
||||
|
||||
static inline u64 get(const T* arg)
|
||||
{
|
||||
return reinterpret_cast<std::uintptr_t>(arg);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
struct fmt_unveil<T[N], void>
|
||||
{
|
||||
using type = const T*;
|
||||
|
||||
static inline u64 get(const T* arg)
|
||||
{
|
||||
return reinterpret_cast<std::uintptr_t>(arg);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct fmt_unveil<b8, void>
|
||||
{
|
||||
using type = bool;
|
||||
|
||||
static inline u64 get(const b8& value)
|
||||
{
|
||||
return fmt_unveil<bool>::get(value);
|
||||
}
|
||||
};
|
||||
|
||||
// String type format provider, also type classifier (format() called if an argument is formatted as "%s")
|
||||
template <typename T, typename = void>
|
||||
struct fmt_class_string
|
||||
{
|
||||
// Formatting function (must be explicitly specialized)
|
||||
static void format(std::string& out, u64 arg);
|
||||
|
||||
// Helper typedef (visible in format())
|
||||
using type = T;
|
||||
|
||||
// Helper function (converts arg to object reference)
|
||||
static SAFE_BUFFERS FORCE_INLINE const T& get_object(u64 arg)
|
||||
{
|
||||
return *reinterpret_cast<const T*>(static_cast<std::uintptr_t>(arg));
|
||||
}
|
||||
|
||||
// Enum -> string function type
|
||||
using convert_t = const char*(*)(T value);
|
||||
|
||||
// Enum -> string function registered
|
||||
static convert_t convert_enum;
|
||||
|
||||
// Helper function (safely converts arg to enum value)
|
||||
static SAFE_BUFFERS FORCE_INLINE void format_enum(std::string& out, u64 arg, convert_t convert)
|
||||
{
|
||||
// Save convert function
|
||||
if (convert_enum == nullptr)
|
||||
{
|
||||
convert_enum = convert;
|
||||
}
|
||||
|
||||
const auto value = static_cast<std::underlying_type_t<T>>(arg);
|
||||
|
||||
// Check narrowing
|
||||
if (static_cast<u64>(value) == arg)
|
||||
{
|
||||
if (const char* str = convert(static_cast<T>(value)))
|
||||
{
|
||||
out += str;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to underlying type formatting
|
||||
fmt_class_string<std::underlying_type_t<T>>::format(out, static_cast<u64>(value));
|
||||
}
|
||||
|
||||
// Helper function (bitset formatting)
|
||||
static SAFE_BUFFERS FORCE_INLINE void format_bitset(std::string& out, u64 arg, const char* prefix, const char* delim, const char* suffix, void (*fmt)(std::string&, u64))
|
||||
{
|
||||
// Start from raw value
|
||||
fmt_class_string<u64>::format(out, arg);
|
||||
|
||||
out += prefix;
|
||||
|
||||
for (u64 i = 0; i < 63; i++)
|
||||
{
|
||||
const u64 mask = 1ull << i;
|
||||
|
||||
if (arg & mask)
|
||||
{
|
||||
fmt(out, i);
|
||||
|
||||
if (arg >> (i + 1))
|
||||
{
|
||||
out += delim;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (arg & (1ull << 63))
|
||||
{
|
||||
fmt(out, 63);
|
||||
}
|
||||
|
||||
out += suffix;
|
||||
}
|
||||
|
||||
// Helper constant (may be used in format_enum as lambda return value)
|
||||
static constexpr const char* unknown = nullptr;
|
||||
};
|
||||
|
||||
template <typename T, typename V>
|
||||
const char*(*fmt_class_string<T, V>::convert_enum)(T) = nullptr;
|
||||
|
||||
template <>
|
||||
struct fmt_class_string<const void*, void>
|
||||
{
|
||||
static void format(std::string& out, u64 arg);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct fmt_class_string<T*, void> : fmt_class_string<const void*, void>
|
||||
{
|
||||
// Classify all pointers as const void*
|
||||
};
|
||||
|
||||
template <>
|
||||
struct fmt_class_string<const char*, void>
|
||||
{
|
||||
static void format(std::string& out, u64 arg);
|
||||
};
|
||||
|
||||
template <>
|
||||
struct fmt_class_string<char*, void> : fmt_class_string<const char*>
|
||||
{
|
||||
// Classify char* as const char*
|
||||
};
|
||||
|
||||
struct fmt_type_info
|
||||
{
|
||||
decltype(&fmt_class_string<int>::format) fmt_string;
|
||||
|
||||
template <typename T>
|
||||
static constexpr fmt_type_info make()
|
||||
{
|
||||
return fmt_type_info
|
||||
{
|
||||
&fmt_class_string<T>::format,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// Argument array type (each element generated via fmt_unveil<>)
|
||||
template <typename... Args>
|
||||
using fmt_args_t = const u64(&&)[sizeof...(Args) + 1];
|
||||
|
||||
namespace fmt
|
||||
{
|
||||
template <typename... Args>
|
||||
SAFE_BUFFERS FORCE_INLINE const fmt_type_info* get_type_info()
|
||||
{
|
||||
// Constantly initialized null-terminated list of type-specific information
|
||||
static constexpr fmt_type_info result[sizeof...(Args) + 1]{fmt_type_info::make<Args>()...};
|
||||
|
||||
const auto result = static_cast<To>(value);
|
||||
if (static_cast<From>(result) != value) throw fmt::exception(format_str, value, args...);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Internal formatting function
|
||||
void raw_append(std::string& out, const char*, const fmt_type_info*, const u64*) noexcept;
|
||||
|
||||
// Formatting function
|
||||
template <typename... Args>
|
||||
SAFE_BUFFERS FORCE_INLINE void append(std::string& out, const char* fmt, const Args&... args)
|
||||
{
|
||||
raw_append(out, fmt, fmt::get_type_info<fmt_unveil_t<Args>...>(), fmt_args_t<Args...>{fmt_unveil<Args>::get(args)...});
|
||||
}
|
||||
|
||||
// Formatting function
|
||||
template <typename... Args>
|
||||
SAFE_BUFFERS FORCE_INLINE std::string format(const char* fmt, const Args&... args)
|
||||
{
|
||||
std::string result;
|
||||
append<Args...>(result, fmt, args...);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Internal exception message formatting template, must be explicitly specialized or instantiated in cpp to minimize code bloat
|
||||
template <typename T>
|
||||
[[noreturn]] void raw_throw_exception(const char*, const fmt_type_info*, const u64*);
|
||||
|
||||
// Throw exception with formatting
|
||||
template <typename T = std::runtime_error, typename... Args>
|
||||
[[noreturn]] SAFE_BUFFERS FORCE_INLINE void throw_exception(const char* fmt, const Args&... args)
|
||||
{
|
||||
raw_throw_exception<T>(fmt, fmt::get_type_info<fmt_unveil_t<Args>...>(), fmt_args_t<Args...>{fmt_unveil<Args>::get(args)...});
|
||||
}
|
||||
}
|
||||
|
||||
+17
-17
@@ -7,8 +7,8 @@
|
||||
#include <functional>
|
||||
|
||||
// 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)
|
||||
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);
|
||||
@@ -16,8 +16,8 @@ inline void strcpy_trunc(char (&dst)[N], const std::string& src)
|
||||
}
|
||||
|
||||
// Copy null-terminated string from char array to another char array with truncation
|
||||
template <std::size_t N, std::size_t N2>
|
||||
inline void strcpy_trunc(char (&dst)[N], const char (&src)[N2])
|
||||
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);
|
||||
@@ -27,10 +27,10 @@ inline void strcpy_trunc(char (&dst)[N], const char (&src)[N2])
|
||||
namespace fmt
|
||||
{
|
||||
std::string replace_first(const std::string& src, const std::string& from, const std::string& to);
|
||||
std::string replace_all(const std::string& src, const std::string& from, const std::string& to);
|
||||
std::string replace_all(const std::string &src, const std::string& from, const std::string& to);
|
||||
|
||||
template <size_t list_size>
|
||||
std::string replace_all(std::string src, const std::pair<std::string, std::string> (&list)[list_size])
|
||||
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)
|
||||
{
|
||||
@@ -53,8 +53,8 @@ namespace fmt
|
||||
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])
|
||||
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)
|
||||
{
|
||||
@@ -80,17 +80,17 @@ namespace fmt
|
||||
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>
|
||||
template<typename T>
|
||||
std::string merge(const T& source, const std::string& separator)
|
||||
{
|
||||
if (!source.size())
|
||||
{
|
||||
return {};
|
||||
return{};
|
||||
}
|
||||
|
||||
std::string result;
|
||||
|
||||
auto it = source.begin();
|
||||
auto it = source.begin();
|
||||
auto end = source.end();
|
||||
for (--end; it != end; ++it)
|
||||
{
|
||||
@@ -100,23 +100,23 @@ namespace fmt
|
||||
return result + source.back();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template<typename T>
|
||||
std::string merge(std::initializer_list<T> sources, const std::string& separator)
|
||||
{
|
||||
if (!sources.size())
|
||||
{
|
||||
return {};
|
||||
return{};
|
||||
}
|
||||
|
||||
std::string result;
|
||||
bool first = true;
|
||||
|
||||
for (auto& v : sources)
|
||||
for (auto &v : sources)
|
||||
{
|
||||
if (first)
|
||||
{
|
||||
result = fmt::merge(v, separator);
|
||||
first = false;
|
||||
first = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -129,5 +129,5 @@ namespace fmt
|
||||
|
||||
std::string to_upper(const std::string& string);
|
||||
|
||||
bool match(const std::string& source, const std::string& mask);
|
||||
bool match(const std::string &source, const std::string &mask);
|
||||
}
|
||||
|
||||
+692
-564
File diff suppressed because it is too large
Load Diff
+159
-107
@@ -1,14 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "Atomic.h"
|
||||
|
||||
#include <exception>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
#include "sema.h"
|
||||
#include "cond.h"
|
||||
#include "Platform.h"
|
||||
#include "Atomic.h"
|
||||
|
||||
// Will report exception and call std::abort() if put in catch(...)
|
||||
[[noreturn]] void catch_all_exceptions();
|
||||
@@ -20,19 +17,19 @@ class task_stack
|
||||
{
|
||||
std::unique_ptr<task_base> next;
|
||||
|
||||
virtual ~task_base();
|
||||
virtual ~task_base() = default;
|
||||
|
||||
virtual void invoke()
|
||||
virtual void exec()
|
||||
{
|
||||
if (next)
|
||||
{
|
||||
next->invoke();
|
||||
next->exec();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename F>
|
||||
struct task_type final : task_base
|
||||
template<typename F>
|
||||
struct task_type : task_base
|
||||
{
|
||||
std::remove_reference_t<F> func;
|
||||
|
||||
@@ -41,10 +38,10 @@ class task_stack
|
||||
{
|
||||
}
|
||||
|
||||
void invoke() final override
|
||||
void exec() override
|
||||
{
|
||||
func();
|
||||
task_base::invoke();
|
||||
task_base::exec();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -53,7 +50,7 @@ class task_stack
|
||||
public:
|
||||
task_stack() = default;
|
||||
|
||||
template <typename F>
|
||||
template<typename F>
|
||||
task_stack(F&& func)
|
||||
: m_stack(new task_type<F>(std::forward<F>(func)))
|
||||
{
|
||||
@@ -73,11 +70,11 @@ public:
|
||||
m_stack.reset();
|
||||
}
|
||||
|
||||
void invoke() const
|
||||
void exec() const
|
||||
{
|
||||
if (m_stack)
|
||||
{
|
||||
m_stack->invoke();
|
||||
m_stack->exec();
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -85,32 +82,20 @@ public:
|
||||
// Thread control class
|
||||
class thread_ctrl final
|
||||
{
|
||||
// Current thread
|
||||
public: // TODO
|
||||
struct internal;
|
||||
|
||||
private:
|
||||
static thread_local thread_ctrl* g_tls_this_thread;
|
||||
|
||||
// Self pointer
|
||||
std::shared_ptr<thread_ctrl> m_self;
|
||||
// Thread handle storage
|
||||
std::aligned_storage_t<16> m_thread;
|
||||
|
||||
// Thread handle (platform-specific)
|
||||
atomic_t<std::uintptr_t> m_thread{0};
|
||||
// Thread join contention counter
|
||||
atomic_t<u32> m_joining{};
|
||||
|
||||
// Thread mutex
|
||||
mutable semaphore<> m_mutex;
|
||||
|
||||
// Thread condition variable
|
||||
cond_variable m_cond;
|
||||
|
||||
// Thread flags
|
||||
atomic_t<u32> m_signal{0};
|
||||
|
||||
// Thread joining condition variable
|
||||
cond_variable m_jcv;
|
||||
|
||||
// Remotely set or caught exception
|
||||
std::exception_ptr m_exception;
|
||||
|
||||
// Thread initial task or atexit task
|
||||
task_stack m_task;
|
||||
// Thread internals
|
||||
atomic_t<internal*> m_data{};
|
||||
|
||||
// Fixed name
|
||||
std::string m_name;
|
||||
@@ -122,19 +107,19 @@ class thread_ctrl final
|
||||
void initialize();
|
||||
|
||||
// Called at the thread end
|
||||
void finalize(std::exception_ptr) noexcept;
|
||||
void finalize() noexcept;
|
||||
|
||||
// Add task (atexit)
|
||||
static void _push(task_stack);
|
||||
// Get atexit function
|
||||
void push_atexit(task_stack);
|
||||
|
||||
// Internal waiting function, may throw. Infinite value is -1.
|
||||
static bool _wait_for(u64 usec);
|
||||
// Start waiting
|
||||
void wait_start(u64 timeout);
|
||||
|
||||
// Internal throwing function. Mutex must be locked and will be unlocked.
|
||||
[[noreturn]] void _throw();
|
||||
// Proceed waiting
|
||||
bool wait_wait(u64 timeout);
|
||||
|
||||
// Internal notification function
|
||||
void _notify(cond_variable thread_ctrl::*);
|
||||
// Check exception
|
||||
void test();
|
||||
|
||||
public:
|
||||
thread_ctrl(std::string&& name);
|
||||
@@ -149,57 +134,120 @@ public:
|
||||
return m_name;
|
||||
}
|
||||
|
||||
// Get exception
|
||||
std::exception_ptr get_exception() const;
|
||||
|
||||
// Set exception
|
||||
void set_exception(std::exception_ptr ptr);
|
||||
// Initialize internal data
|
||||
void initialize_once();
|
||||
|
||||
// Get thread result (may throw, simultaneous joining allowed)
|
||||
void join();
|
||||
|
||||
// Notify the thread
|
||||
// Lock thread mutex
|
||||
void lock();
|
||||
|
||||
// Lock conditionally (double-checked)
|
||||
template<typename F>
|
||||
bool lock_if(F&& pred)
|
||||
{
|
||||
if (pred())
|
||||
{
|
||||
lock();
|
||||
|
||||
try
|
||||
{
|
||||
if (LIKELY(pred()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
unlock();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
unlock();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Unlock thread mutex (internal data must be initialized)
|
||||
void unlock();
|
||||
|
||||
// Lock, unlock, notify the thread (required if the condition changed locklessly)
|
||||
void lock_notify();
|
||||
|
||||
// Notify the thread (internal data must be initialized)
|
||||
void notify();
|
||||
|
||||
// Wait once with timeout. Abortable, may throw. May spuriously return false.
|
||||
static inline bool wait_for(u64 usec)
|
||||
// Set exception (internal data must be initialized, thread mutex must be locked)
|
||||
void set_exception(std::exception_ptr);
|
||||
|
||||
// Current thread sleeps for specified amount of microseconds.
|
||||
// Wrapper for std::this_thread::sleep, doesn't require valid thread_ctrl.
|
||||
[[deprecated]] static void sleep(u64 useconds);
|
||||
|
||||
// Wait until pred(). Abortable, may throw. Thread must be locked.
|
||||
// Timeout in microseconds (zero means infinite).
|
||||
template<typename F>
|
||||
static inline auto wait(u64 useconds, F&& pred)
|
||||
{
|
||||
return _wait_for(usec);
|
||||
g_tls_this_thread->wait_start(useconds);
|
||||
|
||||
while (true)
|
||||
{
|
||||
g_tls_this_thread->test();
|
||||
|
||||
if (auto&& result = pred())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
else if (!g_tls_this_thread->wait_wait(useconds) && useconds)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wait. Abortable, may throw.
|
||||
static inline void wait()
|
||||
{
|
||||
_wait_for(-1);
|
||||
}
|
||||
|
||||
// Wait until pred(). Abortable, may throw.
|
||||
template<typename F, typename RT = std::result_of_t<F()>>
|
||||
static inline RT wait(F&& pred)
|
||||
// Wait until pred(). Abortable, may throw. Thread must be locked.
|
||||
template<typename F>
|
||||
static inline auto wait(F&& pred)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (RT result = pred())
|
||||
g_tls_this_thread->test();
|
||||
|
||||
if (auto&& result = pred())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
_wait_for(-1);
|
||||
g_tls_this_thread->wait_wait(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Wait eternally until aborted.
|
||||
// Wait once. Thread must be locked.
|
||||
static inline void wait()
|
||||
{
|
||||
g_tls_this_thread->test();
|
||||
g_tls_this_thread->wait_wait(0);
|
||||
g_tls_this_thread->test();
|
||||
}
|
||||
|
||||
// Wait unconditionally until aborted. Thread must be locked.
|
||||
[[noreturn]] static inline void eternalize()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
_wait_for(-1);
|
||||
g_tls_this_thread->test();
|
||||
g_tls_this_thread->wait_wait(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Test exception (may throw).
|
||||
static void test();
|
||||
|
||||
// Get current thread (may be nullptr)
|
||||
static thread_ctrl* get_current()
|
||||
{
|
||||
@@ -210,32 +258,22 @@ public:
|
||||
template<typename F>
|
||||
static inline void atexit(F&& func)
|
||||
{
|
||||
_push(std::forward<F>(func));
|
||||
}
|
||||
|
||||
// Create detached named thread
|
||||
template<typename N, typename F>
|
||||
static inline void spawn(N&& name, F&& func)
|
||||
{
|
||||
auto out = std::make_shared<thread_ctrl>(std::forward<N>(name));
|
||||
|
||||
thread_ctrl::start(out, std::forward<F>(func));
|
||||
return g_tls_this_thread->push_atexit(std::forward<F>(func));
|
||||
}
|
||||
|
||||
// Named thread factory
|
||||
template<typename N, typename F>
|
||||
static inline void spawn(std::shared_ptr<thread_ctrl>& out, N&& name, F&& func)
|
||||
static inline std::shared_ptr<thread_ctrl> spawn(N&& name, F&& func)
|
||||
{
|
||||
out = std::make_shared<thread_ctrl>(std::forward<N>(name));
|
||||
auto ctrl = std::make_shared<thread_ctrl>(std::forward<N>(name));
|
||||
|
||||
thread_ctrl::start(out, std::forward<F>(func));
|
||||
thread_ctrl::start(ctrl, std::forward<F>(func));
|
||||
|
||||
return ctrl;
|
||||
}
|
||||
|
||||
static void set_native_priority(int priority);
|
||||
static void set_ideal_processor_core(int core);
|
||||
};
|
||||
|
||||
class named_thread
|
||||
class named_thread : public std::enable_shared_from_this<named_thread>
|
||||
{
|
||||
// Pointer to managed resource (shared with actual thread)
|
||||
std::shared_ptr<thread_ctrl> m_thread;
|
||||
@@ -252,8 +290,8 @@ public:
|
||||
virtual std::string get_name() const;
|
||||
|
||||
protected:
|
||||
// Start thread (cannot be called from the constructor: should throw in such case)
|
||||
void start_thread(const std::shared_ptr<void>& _this);
|
||||
// Start thread (cannot be called from the constructor: should throw bad_weak_ptr in such case)
|
||||
void start();
|
||||
|
||||
// Thread task (called in the thread)
|
||||
virtual void on_task() = 0;
|
||||
@@ -261,14 +299,11 @@ protected:
|
||||
// Thread finalization (called after on_task)
|
||||
virtual void on_exit() {}
|
||||
|
||||
// Called once upon thread spawn within the thread's own context
|
||||
virtual void on_spawn() {}
|
||||
|
||||
public:
|
||||
// ID initialization
|
||||
virtual void on_init(const std::shared_ptr<void>& _this)
|
||||
virtual void on_init()
|
||||
{
|
||||
return start_thread(_this);
|
||||
start();
|
||||
}
|
||||
|
||||
// ID finalization
|
||||
@@ -278,19 +313,42 @@ public:
|
||||
}
|
||||
|
||||
// Access thread_ctrl
|
||||
thread_ctrl* get() const
|
||||
thread_ctrl* operator->() const
|
||||
{
|
||||
return m_thread.get();
|
||||
}
|
||||
};
|
||||
|
||||
void join() const
|
||||
// Simple thread mutex locker
|
||||
class thread_lock final
|
||||
{
|
||||
thread_ctrl* m_thread;
|
||||
|
||||
public:
|
||||
thread_lock(const thread_lock&) = delete;
|
||||
|
||||
// Lock specified thread
|
||||
thread_lock(thread_ctrl* thread)
|
||||
: m_thread(thread)
|
||||
{
|
||||
return m_thread->join();
|
||||
m_thread->lock();
|
||||
}
|
||||
|
||||
void notify() const
|
||||
// Lock specified named_thread
|
||||
thread_lock(named_thread& thread)
|
||||
: thread_lock(thread.operator->())
|
||||
{
|
||||
return m_thread->notify();
|
||||
}
|
||||
|
||||
// Lock current thread
|
||||
thread_lock()
|
||||
: thread_lock(thread_ctrl::get_current())
|
||||
{
|
||||
}
|
||||
|
||||
~thread_lock()
|
||||
{
|
||||
m_thread->unlock();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -302,8 +360,8 @@ class scope_thread final
|
||||
public:
|
||||
template<typename N, typename F>
|
||||
scope_thread(N&& name, F&& func)
|
||||
: m_thread(thread_ctrl::spawn(std::forward<N>(name), std::forward<F>(func)))
|
||||
{
|
||||
thread_ctrl::spawn(m_thread, std::forward<N>(name), std::forward<F>(func));
|
||||
}
|
||||
|
||||
// Deleted copy/move constructors + copy/move operators
|
||||
@@ -314,10 +372,4 @@ public:
|
||||
{
|
||||
m_thread->join();
|
||||
}
|
||||
|
||||
// Access thread_ctrl
|
||||
thread_ctrl* get() const
|
||||
{
|
||||
return m_thread.get();
|
||||
}
|
||||
};
|
||||
|
||||
+6
-6
@@ -6,8 +6,8 @@ class Timer
|
||||
{
|
||||
private:
|
||||
bool m_stopped;
|
||||
std::chrono::steady_clock::time_point m_start;
|
||||
std::chrono::steady_clock::time_point m_end;
|
||||
std::chrono::high_resolution_clock::time_point m_start;
|
||||
std::chrono::high_resolution_clock::time_point m_end;
|
||||
|
||||
public:
|
||||
Timer() : m_stopped(false)
|
||||
@@ -17,13 +17,13 @@ public:
|
||||
void Start()
|
||||
{
|
||||
m_stopped = false;
|
||||
m_start = std::chrono::steady_clock::now();
|
||||
m_start = std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
void Stop()
|
||||
{
|
||||
m_stopped = true;
|
||||
m_end = std::chrono::steady_clock::now();
|
||||
m_end = std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
double GetElapsedTimeInSec() const
|
||||
@@ -38,14 +38,14 @@ public:
|
||||
|
||||
u64 GetElapsedTimeInMicroSec() const
|
||||
{
|
||||
std::chrono::steady_clock::time_point now = m_stopped ? m_end : std::chrono::steady_clock::now();
|
||||
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();
|
||||
}
|
||||
|
||||
u64 GetElapsedTimeInNanoSec() const
|
||||
{
|
||||
std::chrono::steady_clock::time_point now = m_stopped ? m_end : std::chrono::steady_clock::now();
|
||||
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();
|
||||
}
|
||||
|
||||
+13
-56
@@ -11,78 +11,35 @@
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
namespace utils
|
||||
namespace memory_helper
|
||||
{
|
||||
// Convert memory protection (internal)
|
||||
static auto operator +(protection prot)
|
||||
void* reserve_memory(size_t size)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
DWORD _prot = PAGE_NOACCESS;
|
||||
switch (prot)
|
||||
{
|
||||
case protection::rw: _prot = PAGE_READWRITE; break;
|
||||
case protection::ro: _prot = PAGE_READONLY; break;
|
||||
case protection::no: break;
|
||||
case protection::wx: _prot = PAGE_EXECUTE_READWRITE; break;
|
||||
case protection::rx: _prot = PAGE_EXECUTE_READ; break;
|
||||
}
|
||||
void* ret = VirtualAlloc(NULL, size, MEM_RESERVE, PAGE_NOACCESS);
|
||||
ENSURES(ret != NULL);
|
||||
#else
|
||||
int _prot = PROT_NONE;
|
||||
switch (prot)
|
||||
{
|
||||
case protection::rw: _prot = PROT_READ | PROT_WRITE; break;
|
||||
case protection::ro: _prot = PROT_READ; break;
|
||||
case protection::no: break;
|
||||
case protection::wx: _prot = PROT_READ | PROT_WRITE | PROT_EXEC; break;
|
||||
case protection::rx: _prot = PROT_READ | PROT_EXEC; break;
|
||||
}
|
||||
void* ret = mmap(nullptr, size, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
ENSURES(ret != 0);
|
||||
#endif
|
||||
|
||||
return _prot;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void* memory_reserve(std::size_t size, void* use_addr)
|
||||
void commit_page_memory(void* pointer, size_t size)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return ::VirtualAlloc(use_addr, size, MEM_RESERVE, PAGE_NOACCESS);
|
||||
VERIFY(VirtualAlloc(pointer, size, MEM_COMMIT, PAGE_READWRITE) != NULL);
|
||||
#else
|
||||
auto ptr = ::mmap(use_addr, size, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
|
||||
if (use_addr && ptr != use_addr)
|
||||
{
|
||||
::munmap(ptr, size);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return ptr;
|
||||
VERIFY(mprotect((void*)((u64)pointer & -4096), size, PROT_READ | PROT_WRITE) != -1);
|
||||
#endif
|
||||
}
|
||||
|
||||
void memory_commit(void* pointer, std::size_t size, protection prot)
|
||||
void free_reserved_memory(void* pointer, size_t size)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
verify(HERE), ::VirtualAlloc(pointer, size, MEM_COMMIT, +prot);
|
||||
VERIFY(VirtualFree(pointer, 0, MEM_DECOMMIT) != 0);
|
||||
#else
|
||||
verify(HERE), ::mprotect((void*)((u64)pointer & -4096), ::align(size, 4096), +prot) != -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
void memory_decommit(void* pointer, std::size_t size)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
verify(HERE), ::VirtualFree(pointer, 0, MEM_DECOMMIT);
|
||||
#else
|
||||
verify(HERE), ::mmap(pointer, size, PROT_NONE, MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
void memory_protect(void* pointer, std::size_t size, protection prot)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
DWORD old;
|
||||
verify(HERE), ::VirtualProtect(pointer, size, +prot, &old);
|
||||
#else
|
||||
verify(HERE), ::mprotect((void*)((u64)pointer & -4096), ::align(size, 4096), +prot) != -1;
|
||||
VERIFY(mprotect(pointer, size, PROT_NONE) != -1);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,35 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
namespace utils
|
||||
namespace memory_helper
|
||||
{
|
||||
// Memory protection type
|
||||
enum class protection
|
||||
{
|
||||
rw, // Read + write (default)
|
||||
ro, // Read only
|
||||
no, // No access
|
||||
wx, // Read + write + execute
|
||||
rx, // Read + execute
|
||||
};
|
||||
|
||||
/**
|
||||
* Reserve `size` bytes of virtual memory and returns it.
|
||||
* The memory should be commited before usage.
|
||||
*/
|
||||
void* memory_reserve(std::size_t size, void* use_addr = nullptr);
|
||||
void* reserve_memory(std::size_t size);
|
||||
|
||||
/**
|
||||
* Commit `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 memory_commit(void* pointer, std::size_t size, protection prot = protection::rw);
|
||||
void commit_page_memory(void* pointer, std::size_t size);
|
||||
|
||||
/**
|
||||
* Decommit all memory committed via commit_page_memory.
|
||||
*/
|
||||
void memory_decommit(void* pointer, std::size_t size);
|
||||
|
||||
// Set memory protection
|
||||
void memory_protect(void* pointer, std::size_t size, protection prot);
|
||||
void free_reserved_memory(void* pointer, std::size_t size);
|
||||
}
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
#include "bin_patch.h"
|
||||
#include "yaml-cpp/yaml.h"
|
||||
#include "File.h"
|
||||
#include "Config.h"
|
||||
|
||||
template <>
|
||||
void fmt_class_string<patch_type>::format(std::string& out, u64 arg)
|
||||
{
|
||||
format_enum(out, arg, [](patch_type value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case patch_type::byte: return "byte";
|
||||
case patch_type::le16: return "le16";
|
||||
case patch_type::le32: return "le32";
|
||||
case patch_type::le64: return "le64";
|
||||
case patch_type::be16: return "be16";
|
||||
case patch_type::be32: return "be32";
|
||||
case patch_type::be64: return "be64";
|
||||
}
|
||||
|
||||
return unknown;
|
||||
});
|
||||
}
|
||||
|
||||
void patch_engine::append(const std::string& patch)
|
||||
{
|
||||
if (fs::file f{patch})
|
||||
{
|
||||
auto root = YAML::Load(f.to_string());
|
||||
|
||||
for (auto pair : root)
|
||||
{
|
||||
auto& name = pair.first.Scalar();
|
||||
auto& data = m_map[name];
|
||||
|
||||
for (auto patch : pair.second)
|
||||
{
|
||||
u64 type64 = 0;
|
||||
cfg::try_to_enum_value(&type64, &fmt_class_string<patch_type>::format, patch[0].Scalar());
|
||||
|
||||
struct patch info;
|
||||
info.type = static_cast<patch_type>(type64);
|
||||
info.offset = patch[1].as<u32>();
|
||||
info.value = patch[2].as<u64>();
|
||||
data.emplace_back(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void patch_engine::apply(const std::string& name, u8* dst) const
|
||||
{
|
||||
const auto found = m_map.find(name);
|
||||
|
||||
if (found == m_map.cend())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Apply modifications sequentially
|
||||
for (const auto& p : found->second)
|
||||
{
|
||||
auto ptr = dst + p.offset;
|
||||
|
||||
switch (p.type)
|
||||
{
|
||||
case patch_type::byte:
|
||||
{
|
||||
*ptr = static_cast<u8>(p.value);
|
||||
break;
|
||||
}
|
||||
case patch_type::le16:
|
||||
{
|
||||
*reinterpret_cast<le_t<u16, 1>*>(ptr) = static_cast<u16>(p.value);
|
||||
break;
|
||||
}
|
||||
case patch_type::le32:
|
||||
{
|
||||
*reinterpret_cast<le_t<u32, 1>*>(ptr) = static_cast<u32>(p.value);
|
||||
break;
|
||||
}
|
||||
case patch_type::le64:
|
||||
{
|
||||
*reinterpret_cast<le_t<u64, 1>*>(ptr) = static_cast<u64>(p.value);
|
||||
break;
|
||||
}
|
||||
case patch_type::be16:
|
||||
{
|
||||
*reinterpret_cast<be_t<u16, 1>*>(ptr) = static_cast<u16>(p.value);
|
||||
break;
|
||||
}
|
||||
case patch_type::be32:
|
||||
{
|
||||
*reinterpret_cast<be_t<u32, 1>*>(ptr) = static_cast<u32>(p.value);
|
||||
break;
|
||||
}
|
||||
case patch_type::be64:
|
||||
{
|
||||
*reinterpret_cast<be_t<u64, 1>*>(ptr) = static_cast<u64>(p.value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "BEType.h"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
enum class patch_type
|
||||
{
|
||||
byte,
|
||||
le16,
|
||||
le32,
|
||||
le64,
|
||||
be16,
|
||||
be32,
|
||||
be64,
|
||||
};
|
||||
|
||||
class patch_engine
|
||||
{
|
||||
struct patch
|
||||
{
|
||||
patch_type type;
|
||||
u32 offset;
|
||||
u64 value;
|
||||
};
|
||||
|
||||
// Database
|
||||
std::unordered_map<std::string, std::vector<patch>> m_map;
|
||||
|
||||
public:
|
||||
// Load from file
|
||||
void append(const std::string& path);
|
||||
|
||||
// Apply patch
|
||||
void apply(const std::string& name, u8* dst) const;
|
||||
};
|
||||
@@ -1,732 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
This header helps to extend scoped enum types (enum class) in two possible ways:
|
||||
1) Enabling bitwise operators for enums
|
||||
2) Advanced bs_t<> template (this converts enum type to another "bitset" enum type)
|
||||
|
||||
To enable bitwise operators, enum scope must contain `__bitwise_ops` entry.
|
||||
|
||||
enum class flags
|
||||
{
|
||||
__bitwise_ops, // Not essential, but recommended to put it first
|
||||
|
||||
flag1 = 1 << 0,
|
||||
flag2 = 1 << 1,
|
||||
};
|
||||
|
||||
Examples:
|
||||
`flags::flag1 | flags::flag2` - bitwise OR
|
||||
`flags::flag1 & flags::flag2` - bitwise AND
|
||||
`flags::flag1 ^ flags::flag2` - bitwise XOR
|
||||
`~flags::flag1` - bitwise NEG
|
||||
|
||||
To enable bs_t<> template, enum scope must contain `__bitset_enum_max` entry.
|
||||
|
||||
enum class flagzz : u32
|
||||
{
|
||||
flag1, // Bit indices start from zero
|
||||
flag2,
|
||||
|
||||
__bitset_enum_max // It must be the last value
|
||||
};
|
||||
|
||||
Now some operators are enabled for two enum types: `flagzz` and `bs_t<flagzz>`.
|
||||
These are very different from previously described bitwise operators.
|
||||
|
||||
Examples:
|
||||
`+flagzz::flag1` - unary `+` operator convert flagzz value to bs_t<flagzz>
|
||||
`flagzz::flag1 + flagzz::flag2` - bitset union
|
||||
`flagzz::flag1 - flagzz::flag2` - bitset difference
|
||||
Intersection (&) and symmetric difference (^) is also available.
|
||||
|
||||
*/
|
||||
|
||||
#include "types.h"
|
||||
|
||||
// Helper template
|
||||
template<typename T>
|
||||
struct bs_base
|
||||
{
|
||||
// Underlying type
|
||||
using under = std::underlying_type_t<T>;
|
||||
|
||||
// Actual bitset type
|
||||
enum class type : under
|
||||
{
|
||||
null = 0, // Empty bitset
|
||||
|
||||
__bitset_set_type = 0 // SFINAE marker
|
||||
};
|
||||
|
||||
static constexpr std::size_t bitmax = sizeof(T) * 8;
|
||||
static constexpr std::size_t bitsize = static_cast<under>(T::__bitset_enum_max);
|
||||
|
||||
static_assert(std::is_enum<T>::value, "bs_t<> error: invalid type (must be enum)");
|
||||
static_assert(!bitsize || bitsize <= bitmax, "bs_t<> error: invalid __bitset_enum_max");
|
||||
|
||||
// Helper function
|
||||
static constexpr under shift(T value)
|
||||
{
|
||||
return static_cast<under>(1) << static_cast<under>(value);
|
||||
}
|
||||
|
||||
friend type& operator +=(type& lhs, type rhs)
|
||||
{
|
||||
reinterpret_cast<under&>(lhs) |= static_cast<under>(rhs);
|
||||
return lhs;
|
||||
}
|
||||
|
||||
friend type& operator -=(type& lhs, type rhs)
|
||||
{
|
||||
reinterpret_cast<under&>(lhs) &= ~static_cast<under>(rhs);
|
||||
return lhs;
|
||||
}
|
||||
|
||||
friend type& operator &=(type& lhs, type rhs)
|
||||
{
|
||||
reinterpret_cast<under&>(lhs) &= static_cast<under>(rhs);
|
||||
return lhs;
|
||||
}
|
||||
|
||||
friend type& operator ^=(type& lhs, type rhs)
|
||||
{
|
||||
reinterpret_cast<under&>(lhs) ^= static_cast<under>(rhs);
|
||||
return lhs;
|
||||
}
|
||||
|
||||
friend type& operator +=(type& lhs, T rhs)
|
||||
{
|
||||
reinterpret_cast<under&>(lhs) |= shift(rhs);
|
||||
return lhs;
|
||||
}
|
||||
|
||||
friend type& operator -=(type& lhs, T rhs)
|
||||
{
|
||||
reinterpret_cast<under&>(lhs) &= ~shift(rhs);
|
||||
return lhs;
|
||||
}
|
||||
|
||||
friend type& operator &=(type& lhs, T rhs)
|
||||
{
|
||||
reinterpret_cast<under&>(lhs) &= shift(rhs);
|
||||
return lhs;
|
||||
}
|
||||
|
||||
friend type& operator ^=(type& lhs, T rhs)
|
||||
{
|
||||
reinterpret_cast<under&>(lhs) ^= shift(rhs);
|
||||
return lhs;
|
||||
}
|
||||
|
||||
friend constexpr type operator +(type lhs, type rhs)
|
||||
{
|
||||
return static_cast<type>(static_cast<under>(lhs) | static_cast<under>(rhs));
|
||||
}
|
||||
|
||||
friend constexpr type operator -(type lhs, type rhs)
|
||||
{
|
||||
return static_cast<type>(static_cast<under>(lhs) & ~static_cast<under>(rhs));
|
||||
}
|
||||
|
||||
friend constexpr type operator &(type lhs, type rhs)
|
||||
{
|
||||
return static_cast<type>(static_cast<under>(lhs) & static_cast<under>(rhs));
|
||||
}
|
||||
|
||||
friend constexpr type operator ^(type lhs, type rhs)
|
||||
{
|
||||
return static_cast<type>(static_cast<under>(lhs) ^ static_cast<under>(rhs));
|
||||
}
|
||||
|
||||
friend constexpr type operator &(type lhs, T rhs)
|
||||
{
|
||||
return static_cast<type>(static_cast<under>(lhs) & shift(rhs));
|
||||
}
|
||||
|
||||
friend constexpr type operator ^(type lhs, T rhs)
|
||||
{
|
||||
return static_cast<type>(static_cast<under>(lhs) ^ shift(rhs));
|
||||
}
|
||||
|
||||
friend constexpr type operator &(T lhs, type rhs)
|
||||
{
|
||||
return static_cast<type>(shift(lhs) & static_cast<under>(rhs));
|
||||
}
|
||||
|
||||
friend constexpr type operator ^(T lhs, type rhs)
|
||||
{
|
||||
return static_cast<type>(shift(lhs) ^ static_cast<under>(rhs));
|
||||
}
|
||||
|
||||
friend constexpr bool operator ==(T lhs, type rhs)
|
||||
{
|
||||
return shift(lhs) == rhs;
|
||||
}
|
||||
|
||||
friend constexpr bool operator ==(type lhs, T rhs)
|
||||
{
|
||||
return lhs == shift(rhs);
|
||||
}
|
||||
|
||||
friend constexpr bool operator !=(T lhs, type rhs)
|
||||
{
|
||||
return shift(lhs) != rhs;
|
||||
}
|
||||
|
||||
friend constexpr bool operator !=(type lhs, T rhs)
|
||||
{
|
||||
return lhs != shift(rhs);
|
||||
}
|
||||
|
||||
friend constexpr bool test(type value)
|
||||
{
|
||||
return static_cast<under>(value) != 0;
|
||||
}
|
||||
|
||||
friend constexpr bool test(type lhs, type rhs)
|
||||
{
|
||||
return (static_cast<under>(lhs) & static_cast<under>(rhs)) != 0;
|
||||
}
|
||||
|
||||
friend constexpr bool test(type lhs, T rhs)
|
||||
{
|
||||
return (static_cast<under>(lhs) & shift(rhs)) != 0;
|
||||
}
|
||||
|
||||
friend constexpr bool test(T lhs, type rhs)
|
||||
{
|
||||
return (shift(lhs) & static_cast<under>(rhs)) != 0;
|
||||
}
|
||||
|
||||
friend bool test_and_set(type& lhs, type rhs)
|
||||
{
|
||||
return test_and_set(reinterpret_cast<under&>(lhs), static_cast<under>(rhs));
|
||||
}
|
||||
|
||||
friend bool test_and_set(type& lhs, T rhs)
|
||||
{
|
||||
return test_and_set(reinterpret_cast<under&>(lhs), shift(rhs));
|
||||
}
|
||||
|
||||
friend bool test_and_reset(type& lhs, type rhs)
|
||||
{
|
||||
return test_and_reset(reinterpret_cast<under&>(lhs), static_cast<under>(rhs));
|
||||
}
|
||||
|
||||
friend bool test_and_reset(type& lhs, T rhs)
|
||||
{
|
||||
return test_and_reset(reinterpret_cast<under&>(lhs), shift(rhs));
|
||||
}
|
||||
|
||||
friend bool test_and_complement(type& lhs, type rhs)
|
||||
{
|
||||
return test_and_complement(reinterpret_cast<under&>(lhs), static_cast<under>(rhs));
|
||||
}
|
||||
|
||||
friend bool test_and_complement(type& lhs, T rhs)
|
||||
{
|
||||
return test_and_complement(reinterpret_cast<under&>(lhs), shift(rhs));
|
||||
}
|
||||
};
|
||||
|
||||
// Bitset type for enum class with available bits [0, T::__bitset_enum_max)
|
||||
template<typename T>
|
||||
using bs_t = typename bs_base<T>::type;
|
||||
|
||||
// Unary '+' operator: promote plain enum value to bitset value
|
||||
template<typename T, typename = decltype(T::__bitset_enum_max)>
|
||||
constexpr bs_t<T> operator +(T value)
|
||||
{
|
||||
return static_cast<bs_t<T>>(bs_base<T>::shift(value));
|
||||
}
|
||||
|
||||
// Binary '+' operator: bitset union
|
||||
template<typename T, typename = decltype(T::__bitset_enum_max)>
|
||||
constexpr bs_t<T> operator +(T lhs, T rhs)
|
||||
{
|
||||
return static_cast<bs_t<T>>(bs_base<T>::shift(lhs) | bs_base<T>::shift(rhs));
|
||||
}
|
||||
|
||||
// Binary '+' operator: bitset union
|
||||
template<typename T, typename = decltype(T::__bitset_enum_max)>
|
||||
constexpr bs_t<T> operator +(typename bs_base<T>::type lhs, T rhs)
|
||||
{
|
||||
return static_cast<bs_t<T>>(static_cast<typename bs_base<T>::under>(lhs) | bs_base<T>::shift(rhs));
|
||||
}
|
||||
|
||||
// Binary '+' operator: bitset union
|
||||
template<typename T, typename = decltype(T::__bitset_enum_max)>
|
||||
constexpr bs_t<T> operator +(T lhs, typename bs_base<T>::type rhs)
|
||||
{
|
||||
return static_cast<bs_t<T>>(bs_base<T>::shift(lhs) | static_cast<typename bs_base<T>::under>(rhs));
|
||||
}
|
||||
|
||||
// Binary '-' operator: bitset difference
|
||||
template<typename T, typename = decltype(T::__bitset_enum_max)>
|
||||
constexpr bs_t<T> operator -(T lhs, T rhs)
|
||||
{
|
||||
return static_cast<bs_t<T>>(bs_base<T>::shift(lhs) & ~bs_base<T>::shift(rhs));
|
||||
}
|
||||
|
||||
// Binary '-' operator: bitset difference
|
||||
template<typename T, typename = decltype(T::__bitset_enum_max)>
|
||||
constexpr bs_t<T> operator -(typename bs_base<T>::type lhs, T rhs)
|
||||
{
|
||||
return static_cast<bs_t<T>>(static_cast<typename bs_base<T>::under>(lhs) & ~bs_base<T>::shift(rhs));
|
||||
}
|
||||
|
||||
// Binary '-' operator: bitset difference
|
||||
template<typename T, typename = decltype(T::__bitset_enum_max)>
|
||||
constexpr bs_t<T> operator -(T lhs, typename bs_base<T>::type rhs)
|
||||
{
|
||||
return static_cast<bs_t<T>>(bs_base<T>::shift(lhs) & ~static_cast<typename bs_base<T>::under>(rhs));
|
||||
}
|
||||
|
||||
template<typename BS, typename T>
|
||||
struct atomic_add<BS, T, void_t<decltype(T::__bitset_enum_max), std::enable_if_t<std::is_same<BS, bs_t<T>>::value>>>
|
||||
{
|
||||
using under = typename bs_base<T>::under;
|
||||
|
||||
static inline bs_t<T> op1(bs_t<T>& left, T right)
|
||||
{
|
||||
return static_cast<bs_t<T>>(atomic_storage<under>::fetch_or(reinterpret_cast<under&>(left), bs_base<T>::shift(right)));
|
||||
}
|
||||
|
||||
static constexpr auto fetch_op = &op1;
|
||||
|
||||
static inline bs_t<T> op2(bs_t<T>& left, T right)
|
||||
{
|
||||
return static_cast<bs_t<T>>(atomic_storage<under>::or_fetch(reinterpret_cast<under&>(left), bs_base<T>::shift(right)));
|
||||
}
|
||||
|
||||
static constexpr auto op_fetch = &op2;
|
||||
static constexpr auto atomic_op = &op2;
|
||||
};
|
||||
|
||||
template<typename BS, typename T>
|
||||
struct atomic_sub<BS, T, void_t<decltype(T::__bitset_enum_max), std::enable_if_t<std::is_same<BS, bs_t<T>>::value>>>
|
||||
{
|
||||
using under = typename bs_base<T>::under;
|
||||
|
||||
static inline bs_t<T> op1(bs_t<T>& left, T right)
|
||||
{
|
||||
return static_cast<bs_t<T>>(atomic_storage<under>::fetch_and(reinterpret_cast<under&>(left), ~bs_base<T>::shift(right)));
|
||||
}
|
||||
|
||||
static constexpr auto fetch_op = &op1;
|
||||
|
||||
static inline bs_t<T> op2(bs_t<T>& left, T right)
|
||||
{
|
||||
return static_cast<bs_t<T>>(atomic_storage<under>::and_fetch(reinterpret_cast<under&>(left), ~bs_base<T>::shift(right)));
|
||||
}
|
||||
|
||||
static constexpr auto op_fetch = &op2;
|
||||
static constexpr auto atomic_op = &op2;
|
||||
};
|
||||
|
||||
template<typename BS, typename T>
|
||||
struct atomic_and<BS, T, void_t<decltype(T::__bitset_enum_max), std::enable_if_t<std::is_same<BS, bs_t<T>>::value>>>
|
||||
{
|
||||
using under = typename bs_base<T>::under;
|
||||
|
||||
static inline bs_t<T> op1(bs_t<T>& left, T right)
|
||||
{
|
||||
return static_cast<bs_t<T>>(atomic_storage<under>::fetch_and(reinterpret_cast<under&>(left), bs_base<T>::shift(right)));
|
||||
}
|
||||
|
||||
static constexpr auto fetch_op = &op1;
|
||||
|
||||
static inline bs_t<T> op2(bs_t<T>& left, T right)
|
||||
{
|
||||
return static_cast<bs_t<T>>(atomic_storage<under>::and_fetch(reinterpret_cast<under&>(left), bs_base<T>::shift(right)));
|
||||
}
|
||||
|
||||
static constexpr auto op_fetch = &op2;
|
||||
static constexpr auto atomic_op = &op2;
|
||||
};
|
||||
|
||||
template<typename BS, typename T>
|
||||
struct atomic_xor<BS, T, void_t<decltype(T::__bitset_enum_max), std::enable_if_t<std::is_same<BS, bs_t<T>>::value>>>
|
||||
{
|
||||
using under = typename bs_base<T>::under;
|
||||
|
||||
static inline bs_t<T> op1(bs_t<T>& left, T right)
|
||||
{
|
||||
return static_cast<bs_t<T>>(atomic_storage<under>::fetch_xor(reinterpret_cast<under&>(left), bs_base<T>::shift(right)));
|
||||
}
|
||||
|
||||
static constexpr auto fetch_op = &op1;
|
||||
|
||||
static inline bs_t<T> op2(bs_t<T>& left, T right)
|
||||
{
|
||||
return static_cast<bs_t<T>>(atomic_storage<under>::xor_fetch(reinterpret_cast<under&>(left), bs_base<T>::shift(right)));
|
||||
}
|
||||
|
||||
static constexpr auto op_fetch = &op2;
|
||||
static constexpr auto atomic_op = &op2;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct atomic_add<T, T, void_t<decltype(T::__bitset_set_type)>>
|
||||
{
|
||||
using under = std::underlying_type_t<T>;
|
||||
|
||||
static inline T op1(T& left, T right)
|
||||
{
|
||||
return static_cast<T>(atomic_storage<under>::fetch_or(reinterpret_cast<under&>(left), static_cast<under>(right)));
|
||||
}
|
||||
|
||||
static constexpr auto fetch_op = &op1;
|
||||
|
||||
static inline T op2(T& left, T right)
|
||||
{
|
||||
return static_cast<T>(atomic_storage<under>::or_fetch(reinterpret_cast<under&>(left), static_cast<under>(right)));
|
||||
}
|
||||
|
||||
static constexpr auto op_fetch = &op2;
|
||||
static constexpr auto atomic_op = &op2;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct atomic_sub<T, T, void_t<decltype(T::__bitset_set_type)>>
|
||||
{
|
||||
using under = std::underlying_type_t<T>;
|
||||
|
||||
static inline T op1(T& left, T right)
|
||||
{
|
||||
return static_cast<T>(atomic_storage<under>::fetch_and(reinterpret_cast<under&>(left), ~static_cast<under>(right)));
|
||||
}
|
||||
|
||||
static constexpr auto fetch_op = &op1;
|
||||
|
||||
static inline T op2(T& left, T right)
|
||||
{
|
||||
return static_cast<T>(atomic_storage<under>::and_fetch(reinterpret_cast<under&>(left), ~static_cast<under>(right)));
|
||||
}
|
||||
|
||||
static constexpr auto op_fetch = &op2;
|
||||
static constexpr auto atomic_op = &op2;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct atomic_and<T, T, void_t<decltype(T::__bitset_set_type)>>
|
||||
{
|
||||
using under = std::underlying_type_t<T>;
|
||||
|
||||
static inline T op1(T& left, T right)
|
||||
{
|
||||
return static_cast<T>(atomic_storage<under>::fetch_and(reinterpret_cast<under&>(left), static_cast<under>(right)));
|
||||
}
|
||||
|
||||
static constexpr auto fetch_op = &op1;
|
||||
|
||||
static inline T op2(T& left, T right)
|
||||
{
|
||||
return static_cast<T>(atomic_storage<under>::and_fetch(reinterpret_cast<under&>(left), static_cast<under>(right)));
|
||||
}
|
||||
|
||||
static constexpr auto op_fetch = &op2;
|
||||
static constexpr auto atomic_op = &op2;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct atomic_xor<T, T, void_t<decltype(T::__bitset_set_type)>>
|
||||
{
|
||||
using under = std::underlying_type_t<T>;
|
||||
|
||||
static inline T op1(T& left, T right)
|
||||
{
|
||||
return static_cast<T>(atomic_storage<under>::fetch_xor(reinterpret_cast<under&>(left), static_cast<under>(right)));
|
||||
}
|
||||
|
||||
static constexpr auto fetch_op = &op1;
|
||||
|
||||
static inline T op2(T& left, T right)
|
||||
{
|
||||
return static_cast<T>(atomic_storage<under>::xor_fetch(reinterpret_cast<under&>(left), static_cast<under>(right)));
|
||||
}
|
||||
|
||||
static constexpr auto op_fetch = &op2;
|
||||
static constexpr auto atomic_op = &op2;
|
||||
};
|
||||
|
||||
template<typename BS, typename T>
|
||||
struct atomic_test_and_set<BS, T, void_t<decltype(T::__bitset_enum_max), std::enable_if_t<std::is_same<BS, bs_t<T>>::value>>>
|
||||
{
|
||||
using under = typename bs_base<T>::under;
|
||||
|
||||
static inline bool _op(bs_t<T>& left, T value)
|
||||
{
|
||||
return atomic_storage<under>::bts(reinterpret_cast<under&>(left), static_cast<uint>(static_cast<under>(value)));
|
||||
}
|
||||
|
||||
static constexpr auto fetch_op = &_op;
|
||||
static constexpr auto op_fetch = &_op;
|
||||
static constexpr auto atomic_op = &_op;
|
||||
};
|
||||
|
||||
template<typename BS, typename T>
|
||||
struct atomic_test_and_reset<BS, T, void_t<decltype(T::__bitset_enum_max), std::enable_if_t<std::is_same<BS, bs_t<T>>::value>>>
|
||||
{
|
||||
using under = typename bs_base<T>::under;
|
||||
|
||||
static inline bool _op(bs_t<T>& left, T value)
|
||||
{
|
||||
return atomic_storage<under>::btr(reinterpret_cast<under&>(left), static_cast<uint>(static_cast<under>(value)));
|
||||
}
|
||||
|
||||
static constexpr auto fetch_op = &_op;
|
||||
static constexpr auto op_fetch = &_op;
|
||||
static constexpr auto atomic_op = &_op;
|
||||
};
|
||||
|
||||
template<typename BS, typename T>
|
||||
struct atomic_test_and_complement<BS, T, void_t<decltype(T::__bitset_enum_max), std::enable_if_t<std::is_same<BS, bs_t<T>>::value>>>
|
||||
{
|
||||
using under = typename bs_base<T>::under;
|
||||
|
||||
static inline bool _op(bs_t<T>& left, T value)
|
||||
{
|
||||
return atomic_storage<under>::btc(reinterpret_cast<under&>(left), static_cast<uint>(static_cast<under>(value)));
|
||||
}
|
||||
|
||||
static constexpr auto fetch_op = &_op;
|
||||
static constexpr auto op_fetch = &_op;
|
||||
static constexpr auto atomic_op = &_op;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct atomic_test_and_set<T, T, void_t<decltype(T::__bitset_set_type)>>
|
||||
{
|
||||
using under = std::underlying_type_t<T>;
|
||||
|
||||
static inline bool _op(T& left, T value)
|
||||
{
|
||||
return atomic_storage<under>::test_and_set(reinterpret_cast<under&>(left), static_cast<under>(value));
|
||||
}
|
||||
|
||||
static constexpr auto fetch_op = &_op;
|
||||
static constexpr auto op_fetch = &_op;
|
||||
static constexpr auto atomic_op = &_op;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct atomic_test_and_reset<T, T, void_t<decltype(T::__bitset_set_type)>>
|
||||
{
|
||||
using under = std::underlying_type_t<T>;
|
||||
|
||||
static inline bool _op(T& left, T value)
|
||||
{
|
||||
return atomic_storage<under>::test_and_reset(reinterpret_cast<under&>(left), static_cast<under>(value));
|
||||
}
|
||||
|
||||
static constexpr auto fetch_op = &_op;
|
||||
static constexpr auto op_fetch = &_op;
|
||||
static constexpr auto atomic_op = &_op;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct atomic_test_and_complement<T, T, void_t<decltype(T::__bitset_set_type)>>
|
||||
{
|
||||
using under = std::underlying_type_t<T>;
|
||||
|
||||
static inline bool _op(T& left, T value)
|
||||
{
|
||||
return atomic_storage<under>::test_and_complement(reinterpret_cast<under&>(left), static_cast<under>(value));
|
||||
}
|
||||
|
||||
static constexpr auto fetch_op = &_op;
|
||||
static constexpr auto op_fetch = &_op;
|
||||
static constexpr auto atomic_op = &_op;
|
||||
};
|
||||
|
||||
// Binary '|' operator: bitwise OR
|
||||
template<typename T, typename = decltype(T::__bitwise_ops)>
|
||||
constexpr T operator |(T lhs, T rhs)
|
||||
{
|
||||
return static_cast<T>(std::underlying_type_t<T>(lhs) | std::underlying_type_t<T>(rhs));
|
||||
}
|
||||
|
||||
// Binary '&' operator: bitwise AND
|
||||
template<typename T, typename = decltype(T::__bitwise_ops)>
|
||||
constexpr T operator &(T lhs, T rhs)
|
||||
{
|
||||
return static_cast<T>(std::underlying_type_t<T>(lhs) & std::underlying_type_t<T>(rhs));
|
||||
}
|
||||
|
||||
// Binary '^' operator: bitwise XOR
|
||||
template<typename T, typename = decltype(T::__bitwise_ops)>
|
||||
constexpr T operator ^(T lhs, T rhs)
|
||||
{
|
||||
return static_cast<T>(std::underlying_type_t<T>(lhs) ^ std::underlying_type_t<T>(rhs));
|
||||
}
|
||||
|
||||
// Unary '~' operator: bitwise NEG
|
||||
template<typename T, typename = decltype(T::__bitwise_ops)>
|
||||
constexpr T operator ~(T value)
|
||||
{
|
||||
return static_cast<T>(~std::underlying_type_t<T>(value));
|
||||
}
|
||||
|
||||
// Bitwise OR assignment
|
||||
template<typename T, typename = decltype(T::__bitwise_ops)>
|
||||
inline T& operator |=(T& lhs, T rhs)
|
||||
{
|
||||
reinterpret_cast<std::underlying_type_t<T>&>(lhs) |= std::underlying_type_t<T>(rhs);
|
||||
return lhs;
|
||||
}
|
||||
|
||||
// Bitwise AND assignment
|
||||
template<typename T, typename = decltype(T::__bitwise_ops)>
|
||||
inline T& operator &=(T& lhs, T rhs)
|
||||
{
|
||||
reinterpret_cast<std::underlying_type_t<T>&>(lhs) &= std::underlying_type_t<T>(rhs);
|
||||
return lhs;
|
||||
}
|
||||
|
||||
// Bitwise XOR assignment
|
||||
template<typename T, typename = decltype(T::__bitwise_ops)>
|
||||
inline T& operator ^=(T& lhs, T rhs)
|
||||
{
|
||||
reinterpret_cast<std::underlying_type_t<T>&>(lhs) ^= std::underlying_type_t<T>(rhs);
|
||||
return lhs;
|
||||
}
|
||||
|
||||
template<typename T, typename = decltype(T::__bitwise_ops)>
|
||||
constexpr bool test(T value)
|
||||
{
|
||||
return std::underlying_type_t<T>(value) != 0;
|
||||
}
|
||||
|
||||
template<typename T, typename = decltype(T::__bitwise_ops)>
|
||||
constexpr bool test(T lhs, T rhs)
|
||||
{
|
||||
return (std::underlying_type_t<T>(lhs) & std::underlying_type_t<T>(rhs)) != 0;
|
||||
}
|
||||
|
||||
template<typename T, typename = decltype(T::__bitwise_ops)>
|
||||
inline bool test_and_set(T& lhs, T rhs)
|
||||
{
|
||||
return test_and_set(reinterpret_cast<std::underlying_type_t<T>&>(lhs), std::underlying_type_t<T>(rhs));
|
||||
}
|
||||
|
||||
template<typename T, typename = decltype(T::__bitwise_ops)>
|
||||
inline bool test_and_reset(T& lhs, T rhs)
|
||||
{
|
||||
return test_and_reset(reinterpret_cast<std::underlying_type_t<T>&>(lhs), std::underlying_type_t<T>(rhs));
|
||||
}
|
||||
|
||||
template<typename T, typename = decltype(T::__bitwise_ops)>
|
||||
inline bool test_and_complement(T& lhs, T rhs)
|
||||
{
|
||||
return test_and_complement(reinterpret_cast<std::underlying_type_t<T>&>(lhs), std::underlying_type_t<T>(rhs));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct atomic_or<T, T, void_t<decltype(T::__bitwise_ops), std::enable_if_t<std::is_enum<T>::value>>>
|
||||
{
|
||||
using under = std::underlying_type_t<T>;
|
||||
|
||||
static inline T op1(T& left, T right)
|
||||
{
|
||||
return static_cast<T>(atomic_storage<under>::fetch_or(reinterpret_cast<under&>(left), static_cast<under>(right)));
|
||||
}
|
||||
|
||||
static constexpr auto fetch_op = &op1;
|
||||
|
||||
static inline T op2(T& left, T right)
|
||||
{
|
||||
return static_cast<T>(atomic_storage<under>::or_fetch(reinterpret_cast<under&>(left), static_cast<under>(right)));
|
||||
}
|
||||
|
||||
static constexpr auto op_fetch = &op2;
|
||||
static constexpr auto atomic_op = &op2;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct atomic_and<T, T, void_t<decltype(T::__bitwise_ops), std::enable_if_t<std::is_enum<T>::value>>>
|
||||
{
|
||||
using under = std::underlying_type_t<T>;
|
||||
|
||||
static inline T op1(T& left, T right)
|
||||
{
|
||||
return static_cast<T>(atomic_storage<under>::fetch_and(reinterpret_cast<under&>(left), static_cast<under>(right)));
|
||||
}
|
||||
|
||||
static constexpr auto fetch_op = &op1;
|
||||
|
||||
static inline T op2(T& left, T right)
|
||||
{
|
||||
return static_cast<T>(atomic_storage<under>::and_fetch(reinterpret_cast<under&>(left), static_cast<under>(right)));
|
||||
}
|
||||
|
||||
static constexpr auto op_fetch = &op2;
|
||||
static constexpr auto atomic_op = &op2;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct atomic_xor<T, T, void_t<decltype(T::__bitwise_ops), std::enable_if_t<std::is_enum<T>::value>>>
|
||||
{
|
||||
using under = std::underlying_type_t<T>;
|
||||
|
||||
static inline T op1(T& left, T right)
|
||||
{
|
||||
return static_cast<T>(atomic_storage<under>::fetch_xor(reinterpret_cast<under&>(left), static_cast<under>(right)));
|
||||
}
|
||||
|
||||
static constexpr auto fetch_op = &op1;
|
||||
|
||||
static inline T op2(T& left, T right)
|
||||
{
|
||||
return static_cast<T>(atomic_storage<under>::xor_fetch(reinterpret_cast<under&>(left), static_cast<under>(right)));
|
||||
}
|
||||
|
||||
static constexpr auto op_fetch = &op2;
|
||||
static constexpr auto atomic_op = &op2;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct atomic_test_and_set<T, T, void_t<decltype(T::__bitwise_ops), std::enable_if_t<std::is_enum<T>::value>>>
|
||||
{
|
||||
using under = std::underlying_type_t<T>;
|
||||
|
||||
static inline bool _op(T& left, T value)
|
||||
{
|
||||
return atomic_storage<under>::test_and_set(reinterpret_cast<under&>(left), static_cast<under>(value));
|
||||
}
|
||||
|
||||
static constexpr auto fetch_op = &_op;
|
||||
static constexpr auto op_fetch = &_op;
|
||||
static constexpr auto atomic_op = &_op;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct atomic_test_and_reset<T, T, void_t<decltype(T::__bitwise_ops), std::enable_if_t<std::is_enum<T>::value>>>
|
||||
{
|
||||
using under = std::underlying_type_t<T>;
|
||||
|
||||
static inline bool _op(T& left, T value)
|
||||
{
|
||||
return atomic_storage<under>::test_and_reset(reinterpret_cast<under&>(left), static_cast<under>(value));
|
||||
}
|
||||
|
||||
static constexpr auto fetch_op = &_op;
|
||||
static constexpr auto op_fetch = &_op;
|
||||
static constexpr auto atomic_op = &_op;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct atomic_test_and_complement<T, T, void_t<decltype(T::__bitwise_ops), std::enable_if_t<std::is_enum<T>::value>>>
|
||||
{
|
||||
using under = std::underlying_type_t<T>;
|
||||
|
||||
static inline bool _op(T& left, T value)
|
||||
{
|
||||
return atomic_storage<under>::test_and_complement(reinterpret_cast<under&>(left), static_cast<under>(value));
|
||||
}
|
||||
|
||||
static constexpr auto fetch_op = &_op;
|
||||
static constexpr auto op_fetch = &_op;
|
||||
static constexpr auto atomic_op = &_op;
|
||||
};
|
||||
@@ -1,634 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include <climits>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
/*
|
||||
C-style format parser. Appends formatted string to `out`, returns number of characters written.
|
||||
`out`: mutable reference to std::string, std::vector<char> or other compatible container
|
||||
`fmt`: null-terminated string of `Char` type (char or constructible from char)
|
||||
`src`: rvalue reference to argument provider.
|
||||
*/
|
||||
template<typename Dst, typename Char, typename Src>
|
||||
std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
{
|
||||
const std::size_t start_pos = out.size();
|
||||
|
||||
struct cfmt_context
|
||||
{
|
||||
std::size_t size; // Size of current format sequence
|
||||
|
||||
u8 args; // Number of extra args used
|
||||
u8 type; // Integral type bytesize
|
||||
bool dot; // Precision enabled
|
||||
bool left;
|
||||
bool sign;
|
||||
bool space;
|
||||
bool alter;
|
||||
bool zeros;
|
||||
|
||||
uint width;
|
||||
uint prec;
|
||||
};
|
||||
|
||||
cfmt_context ctx{0};
|
||||
|
||||
// Error handling: print untouched sequence, stop further formatting
|
||||
const auto drop_sequence = [&]
|
||||
{
|
||||
out.insert(out.end(), fmt - ctx.size, fmt);
|
||||
ctx.size = -1;
|
||||
};
|
||||
|
||||
const auto read_decimal = [&](uint result) -> uint
|
||||
{
|
||||
while (fmt[0] >= '0' && fmt[0] <= '9' && result <= (UINT_MAX / 10))
|
||||
{
|
||||
result = result * 10 + (fmt[0] - '0');
|
||||
fmt++, ctx.size++;
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
const auto write_octal = [&](u64 value, u64 min_num)
|
||||
{
|
||||
out.resize(out.size() + std::max<u64>(min_num, 66 / 3 - (cntlz64(value | 1, true) + 2) / 3), '0');
|
||||
|
||||
// Write in reversed order
|
||||
for (auto i = out.rbegin(); value; i++, value /= 8)
|
||||
{
|
||||
*i = value % 8 + '0';
|
||||
}
|
||||
};
|
||||
|
||||
const auto write_hex = [&](u64 value, bool upper, u64 min_num)
|
||||
{
|
||||
out.resize(out.size() + std::max<u64>(min_num, 64 / 4 - cntlz64(value | 1, true) / 4), '0');
|
||||
|
||||
// Write in reversed order
|
||||
for (auto i = out.rbegin(); value; i++, value /= 16)
|
||||
{
|
||||
*i = (upper ? "0123456789ABCDEF" : "0123456789abcdef")[value % 16];
|
||||
}
|
||||
};
|
||||
|
||||
const auto write_decimal = [&](u64 value, s64 min_size)
|
||||
{
|
||||
const std::size_t start = out.size();
|
||||
|
||||
do
|
||||
{
|
||||
out.push_back(value % 10 + '0');
|
||||
value /= 10;
|
||||
}
|
||||
while (0 < --min_size || value);
|
||||
|
||||
// Revert written characters
|
||||
for (std::size_t i = start, j = out.size() - 1; i < j; i++, j--)
|
||||
{
|
||||
std::swap(out[i], out[j]);
|
||||
}
|
||||
};
|
||||
|
||||
// Single pass over fmt string (null-terminated), TODO: check correct order
|
||||
while (const Char ch = *fmt++) if (ctx.size == 0)
|
||||
{
|
||||
if (ch == '%')
|
||||
{
|
||||
ctx.size = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
out.push_back(ch);
|
||||
}
|
||||
}
|
||||
else if (ctx.size == 1 && ch == '%')
|
||||
{
|
||||
ctx = {0};
|
||||
out.push_back(ch);
|
||||
}
|
||||
else if (ctx.size == -1)
|
||||
{
|
||||
out.push_back(ch);
|
||||
}
|
||||
else switch (ctx.size++, ch)
|
||||
{
|
||||
case '-': ctx.left = true; break;
|
||||
case '+': ctx.sign = true; break;
|
||||
case ' ': ctx.space = true; break;
|
||||
case '#': ctx.alter = true; break;
|
||||
case '0': ctx.zeros = true; break;
|
||||
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
{
|
||||
if (UNLIKELY(ctx.width))
|
||||
{
|
||||
drop_sequence();
|
||||
}
|
||||
else
|
||||
{
|
||||
ctx.width = read_decimal(ch - '0');
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case '*':
|
||||
{
|
||||
if (UNLIKELY(ctx.width || !src.test(ctx.args)))
|
||||
{
|
||||
drop_sequence();
|
||||
}
|
||||
else
|
||||
{
|
||||
const int warg = src.template get<int>(ctx.args++);
|
||||
ctx.width = std::abs(warg);
|
||||
ctx.left |= warg < 0;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case '.':
|
||||
{
|
||||
if (UNLIKELY(ctx.dot || ctx.prec))
|
||||
{
|
||||
drop_sequence();
|
||||
}
|
||||
else if (*fmt >= '0' && *fmt <= '9') // TODO: does it allow '0'?
|
||||
{
|
||||
ctx.prec = read_decimal(0);
|
||||
ctx.dot = true;
|
||||
}
|
||||
else if (*fmt == '*')
|
||||
{
|
||||
if (UNLIKELY(!src.test(ctx.args)))
|
||||
{
|
||||
drop_sequence();
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt++, ctx.size++;
|
||||
const int parg = src.template get<int>(ctx.args++);
|
||||
ctx.prec = std::max(parg, 0);
|
||||
ctx.dot = parg >= 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ctx.prec = 0;
|
||||
ctx.dot = true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'h':
|
||||
{
|
||||
if (UNLIKELY(ctx.type))
|
||||
{
|
||||
drop_sequence();
|
||||
}
|
||||
else if (fmt[0] == 'h')
|
||||
{
|
||||
fmt++, ctx.size++;
|
||||
ctx.type = src.size_char;
|
||||
}
|
||||
else
|
||||
{
|
||||
ctx.type = src.size_short;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'l':
|
||||
{
|
||||
if (UNLIKELY(ctx.type))
|
||||
{
|
||||
drop_sequence();
|
||||
}
|
||||
else if (fmt[0] == 'l')
|
||||
{
|
||||
fmt++, ctx.size++;
|
||||
ctx.type = src.size_llong;
|
||||
}
|
||||
else
|
||||
{
|
||||
ctx.type = src.size_long;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'z':
|
||||
{
|
||||
if (UNLIKELY(ctx.type))
|
||||
{
|
||||
drop_sequence();
|
||||
}
|
||||
else
|
||||
{
|
||||
ctx.type = src.size_size;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'j':
|
||||
{
|
||||
if (UNLIKELY(ctx.type))
|
||||
{
|
||||
drop_sequence();
|
||||
}
|
||||
else
|
||||
{
|
||||
ctx.type = src.size_max;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 't':
|
||||
{
|
||||
if (UNLIKELY(ctx.type))
|
||||
{
|
||||
drop_sequence();
|
||||
}
|
||||
else
|
||||
{
|
||||
ctx.type = src.size_diff;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'c':
|
||||
{
|
||||
if (UNLIKELY(ctx.type || !src.test(ctx.args)))
|
||||
{
|
||||
drop_sequence();
|
||||
break;
|
||||
}
|
||||
|
||||
const std::size_t start = out.size();
|
||||
out.push_back(src.template get<Char>(ctx.args));
|
||||
|
||||
if (1 < ctx.width)
|
||||
{
|
||||
// Add spaces if necessary
|
||||
out.insert(out.begin() + start + ctx.left, ctx.width - 1, ' ');
|
||||
}
|
||||
|
||||
src.skip(ctx.args);
|
||||
ctx = {0};
|
||||
break;
|
||||
}
|
||||
|
||||
case 's':
|
||||
{
|
||||
if (UNLIKELY(ctx.type || !src.test(ctx.args)))
|
||||
{
|
||||
drop_sequence();
|
||||
break;
|
||||
}
|
||||
|
||||
const std::size_t start = out.size();
|
||||
const std::size_t size1 = src.fmt_string(out, ctx.args);
|
||||
|
||||
if (ctx.dot && size1 > ctx.prec)
|
||||
{
|
||||
// Shrink if necessary
|
||||
out.resize(start + ctx.prec);
|
||||
}
|
||||
|
||||
const std::size_t size2 = out.size() - start;
|
||||
|
||||
if (size2 < ctx.width)
|
||||
{
|
||||
// Add spaces if necessary
|
||||
out.insert(ctx.left ? out.end() : out.begin() + start, ctx.width - size2, ' ');
|
||||
}
|
||||
|
||||
src.skip(ctx.args);
|
||||
ctx = {0};
|
||||
break;
|
||||
}
|
||||
|
||||
case 'd':
|
||||
case 'i':
|
||||
{
|
||||
if (UNLIKELY(!src.test(ctx.args)))
|
||||
{
|
||||
drop_sequence();
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ctx.type)
|
||||
{
|
||||
ctx.type = (u8)src.type(ctx.args);
|
||||
|
||||
if (!ctx.type)
|
||||
{
|
||||
ctx.type = src.size_int;
|
||||
}
|
||||
}
|
||||
|
||||
// Sign-extended argument expected
|
||||
const u64 val = src.template get<u64>(ctx.args);
|
||||
const bool negative = ctx.type && static_cast<s64>(val) < 0;
|
||||
|
||||
const std::size_t start = out.size();
|
||||
|
||||
if (!ctx.dot || ctx.prec)
|
||||
{
|
||||
if (negative)
|
||||
{
|
||||
out.push_back('-');
|
||||
}
|
||||
else if (ctx.sign)
|
||||
{
|
||||
out.push_back('+');
|
||||
}
|
||||
else if (ctx.space)
|
||||
{
|
||||
out.push_back(' ');
|
||||
}
|
||||
|
||||
write_decimal(negative ? 0 - val : val, ctx.prec);
|
||||
}
|
||||
|
||||
const std::size_t size2 = out.size() - start;
|
||||
|
||||
if (size2 < ctx.width)
|
||||
{
|
||||
// Add padding if necessary
|
||||
if (ctx.zeros && !ctx.left && !ctx.dot)
|
||||
{
|
||||
out.insert(out.begin() + start + (negative || ctx.sign || ctx.space), ctx.width - size2, '0');
|
||||
}
|
||||
else
|
||||
{
|
||||
out.insert(ctx.left ? out.end() : out.begin() + start, ctx.width - size2, ' ');
|
||||
}
|
||||
}
|
||||
|
||||
src.skip(ctx.args);
|
||||
ctx = {0};
|
||||
break;
|
||||
}
|
||||
|
||||
case 'o':
|
||||
{
|
||||
if (UNLIKELY(!src.test(ctx.args)))
|
||||
{
|
||||
drop_sequence();
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ctx.type)
|
||||
{
|
||||
ctx.type = (u8)src.type(ctx.args);
|
||||
|
||||
if (!ctx.type)
|
||||
{
|
||||
ctx.type = src.size_int;
|
||||
}
|
||||
}
|
||||
|
||||
const u64 mask =
|
||||
ctx.type == 1 ? 0xffull :
|
||||
ctx.type == 2 ? 0xffffull :
|
||||
ctx.type == 4 ? 0xffffffffull : 0xffffffffffffffffull;
|
||||
|
||||
// Trunc sign-extended signed types
|
||||
const u64 val = src.template get<u64>(ctx.args) & mask;
|
||||
|
||||
const std::size_t start = out.size();
|
||||
|
||||
if (ctx.alter)
|
||||
{
|
||||
out.push_back('0');
|
||||
|
||||
if (val)
|
||||
{
|
||||
write_octal(val, ctx.prec ? ctx.prec - 1 : 0);
|
||||
}
|
||||
}
|
||||
else if (!ctx.dot || ctx.prec)
|
||||
{
|
||||
write_octal(val, ctx.prec);
|
||||
}
|
||||
|
||||
const std::size_t size2 = out.size() - start;
|
||||
|
||||
if (size2 < ctx.width)
|
||||
{
|
||||
// Add padding if necessary
|
||||
out.insert(ctx.left ? out.end() : out.begin() + start, ctx.width - size2, ctx.zeros && !ctx.left && !ctx.dot ? '0' : ' ');
|
||||
}
|
||||
|
||||
src.skip(ctx.args);
|
||||
ctx = {0};
|
||||
break;
|
||||
}
|
||||
|
||||
case 'x':
|
||||
case 'X':
|
||||
{
|
||||
if (UNLIKELY(!src.test(ctx.args)))
|
||||
{
|
||||
drop_sequence();
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ctx.type)
|
||||
{
|
||||
ctx.type = (u8)src.type(ctx.args);
|
||||
|
||||
if (!ctx.type)
|
||||
{
|
||||
ctx.type = src.size_int;
|
||||
}
|
||||
}
|
||||
|
||||
const u64 mask =
|
||||
ctx.type == 1 ? 0xffull :
|
||||
ctx.type == 2 ? 0xffffull :
|
||||
ctx.type == 4 ? 0xffffffffull : 0xffffffffffffffffull;
|
||||
|
||||
// Trunc sign-extended signed types
|
||||
const u64 val = src.template get<u64>(ctx.args) & mask;
|
||||
|
||||
const std::size_t start = out.size();
|
||||
|
||||
if (ctx.alter)
|
||||
{
|
||||
out.push_back('0');
|
||||
|
||||
if (val)
|
||||
{
|
||||
out.push_back(ch); // Prepend 0x or 0X
|
||||
write_hex(val, ch == 'X', ctx.prec);
|
||||
}
|
||||
}
|
||||
else if (!ctx.dot || ctx.prec)
|
||||
{
|
||||
write_hex(val, ch == 'X', ctx.prec);
|
||||
}
|
||||
|
||||
const std::size_t size2 = out.size() - start;
|
||||
|
||||
if (size2 < ctx.width)
|
||||
{
|
||||
// Add padding if necessary
|
||||
if (ctx.zeros && !ctx.left && !ctx.dot)
|
||||
{
|
||||
out.insert(out.begin() + start + (ctx.alter && val ? 2 : 0), ctx.width - size2, '0');
|
||||
}
|
||||
else
|
||||
{
|
||||
out.insert(ctx.left ? out.end() : out.begin() + start, ctx.width - size2, ' ');
|
||||
}
|
||||
}
|
||||
|
||||
src.skip(ctx.args);
|
||||
ctx = {0};
|
||||
break;
|
||||
}
|
||||
|
||||
case 'u':
|
||||
{
|
||||
if (UNLIKELY(!src.test(ctx.args)))
|
||||
{
|
||||
drop_sequence();
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ctx.type)
|
||||
{
|
||||
ctx.type = (u8)src.type(ctx.args);
|
||||
|
||||
if (!ctx.type)
|
||||
{
|
||||
ctx.type = src.size_int;
|
||||
}
|
||||
}
|
||||
|
||||
const u64 mask =
|
||||
ctx.type == 1 ? 0xffull :
|
||||
ctx.type == 2 ? 0xffffull :
|
||||
ctx.type == 4 ? 0xffffffffull : 0xffffffffffffffffull;
|
||||
|
||||
// Trunc sign-extended signed types
|
||||
const u64 val = src.template get<u64>(ctx.args) & mask;
|
||||
|
||||
const std::size_t start = out.size();
|
||||
|
||||
if (!ctx.dot || ctx.prec)
|
||||
{
|
||||
write_decimal(val, ctx.prec);
|
||||
}
|
||||
|
||||
const std::size_t size2 = out.size() - start;
|
||||
|
||||
if (size2 < ctx.width)
|
||||
{
|
||||
// Add padding if necessary
|
||||
out.insert(ctx.left ? out.end() : out.begin() + start, ctx.width - size2, ctx.zeros && !ctx.left && !ctx.dot ? '0' : ' ');
|
||||
}
|
||||
|
||||
src.skip(ctx.args);
|
||||
ctx = {0};
|
||||
break;
|
||||
}
|
||||
|
||||
case 'p':
|
||||
{
|
||||
if (UNLIKELY(!src.test(ctx.args) || ctx.type))
|
||||
{
|
||||
drop_sequence();
|
||||
break;
|
||||
}
|
||||
|
||||
const u64 val = src.template get<u64>(ctx.args);
|
||||
|
||||
const std::size_t start = out.size();
|
||||
|
||||
write_hex(val, false, sizeof(void*) * 2);
|
||||
|
||||
const std::size_t size2 = out.size() - start;
|
||||
|
||||
if (size2 < ctx.width)
|
||||
{
|
||||
// Add padding if necessary
|
||||
out.insert(ctx.left ? out.end() : out.begin() + start, ctx.width - size2, ' ');
|
||||
}
|
||||
|
||||
src.skip(ctx.args);
|
||||
ctx = {0};
|
||||
break;
|
||||
}
|
||||
|
||||
case 'f':
|
||||
case 'F':
|
||||
case 'e':
|
||||
case 'E':
|
||||
case 'a':
|
||||
case 'A':
|
||||
case 'g':
|
||||
case 'G':
|
||||
{
|
||||
if (UNLIKELY(!src.test(ctx.args) || ctx.type))
|
||||
{
|
||||
drop_sequence();
|
||||
break;
|
||||
}
|
||||
|
||||
// Fallback (TODO)
|
||||
|
||||
const std::string _fmt(fmt - ctx.size, fmt);
|
||||
|
||||
const f64 arg0 = src.template get<f64>(0);
|
||||
const u64 arg1 = ctx.args >= 1 ? src.template get<u64>(1) : 0;
|
||||
const u64 arg2 = ctx.args >= 2 ? src.template get<u64>(2) : 0;
|
||||
|
||||
if (const std::size_t _size = std::snprintf(0, 0, _fmt.c_str(), arg0, arg1, arg2))
|
||||
{
|
||||
out.resize(out.size() + _size);
|
||||
std::snprintf(&out.front() + out.size() - _size, _size + 1, _fmt.c_str(), arg0, arg1, arg2);
|
||||
}
|
||||
|
||||
src.skip(ctx.args);
|
||||
ctx = {0};
|
||||
break;
|
||||
}
|
||||
|
||||
case 'L': // long double, not supported
|
||||
case 'n': // writeback, not supported
|
||||
default:
|
||||
{
|
||||
drop_sequence();
|
||||
}
|
||||
}
|
||||
|
||||
// Handle unfinished sequence
|
||||
if (ctx.size && ctx.size != -1)
|
||||
{
|
||||
fmt--, drop_sequence();
|
||||
}
|
||||
|
||||
return out.size() - start_pos;
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
#include "cond.h"
|
||||
#include "sync.h"
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <thread>
|
||||
#endif
|
||||
|
||||
bool cond_variable::imp_wait(u32 _old, u64 _timeout) noexcept
|
||||
{
|
||||
verify(HERE), _old != -1; // Very unlikely: it requires 2^32 distinct threads to wait simultaneously
|
||||
|
||||
#ifdef _WIN32
|
||||
LARGE_INTEGER timeout;
|
||||
timeout.QuadPart = _timeout * -10;
|
||||
|
||||
if (HRESULT rc = NtWaitForKeyedEvent(nullptr, &m_value, false, _timeout == -1 ? nullptr : &timeout))
|
||||
{
|
||||
verify(HERE), rc == WAIT_TIMEOUT;
|
||||
|
||||
// Retire
|
||||
if (!m_value.fetch_op([](u32& value) { if (value) value--; }))
|
||||
{
|
||||
NtWaitForKeyedEvent(nullptr, &m_value, false, nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
#else
|
||||
timespec timeout;
|
||||
timeout.tv_sec = _timeout / 1000000;
|
||||
timeout.tv_nsec = (_timeout % 1000000) * 1000;
|
||||
|
||||
for (u32 value = _old + 1;; value = m_value)
|
||||
{
|
||||
const int err = futex((int*)&m_value.raw(), FUTEX_WAIT_PRIVATE, value, _timeout == -1 ? nullptr : &timeout, nullptr, 0) == 0
|
||||
? 0
|
||||
: errno;
|
||||
|
||||
// Normal or timeout wakeup
|
||||
if (!err || (_timeout != -1 && err == ETIMEDOUT))
|
||||
{
|
||||
// Cleanup (remove waiter)
|
||||
verify(HERE), m_value--;
|
||||
return !err;
|
||||
}
|
||||
|
||||
// Not a wakeup
|
||||
verify(HERE), err == EAGAIN;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void cond_variable::imp_wake(u32 _count) noexcept
|
||||
{
|
||||
#ifdef _WIN32
|
||||
// Try to subtract required amount of waiters
|
||||
const u32 count = m_value.atomic_op([=](u32& value)
|
||||
{
|
||||
if (value > _count)
|
||||
{
|
||||
value -= _count;
|
||||
return _count;
|
||||
}
|
||||
|
||||
return std::exchange(value, 0);
|
||||
});
|
||||
|
||||
for (u32 i = count; i > 0; i--)
|
||||
{
|
||||
NtReleaseKeyedEvent(nullptr, &m_value, false, nullptr);
|
||||
}
|
||||
#else
|
||||
for (u32 i = _count; i > 0; std::this_thread::yield())
|
||||
{
|
||||
const u32 value = m_value;
|
||||
|
||||
// Constrain remaining amount with imaginary waiter count
|
||||
if (i > value)
|
||||
{
|
||||
i = value;
|
||||
}
|
||||
|
||||
if (!value || i == 0)
|
||||
{
|
||||
// Nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
if (const int res = futex((int*)&m_value.raw(), FUTEX_WAKE_PRIVATE, i > INT_MAX ? INT_MAX : i, nullptr, nullptr, 0))
|
||||
{
|
||||
verify(HERE), res >= 0 && (u32)res <= i;
|
||||
i -= res;
|
||||
}
|
||||
|
||||
if (!m_value || i == 0)
|
||||
{
|
||||
// Escape
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "Atomic.h"
|
||||
|
||||
// Lightweight condition variable
|
||||
class cond_variable
|
||||
{
|
||||
// Internal waiter counter
|
||||
atomic_t<u32> m_value{0};
|
||||
|
||||
protected:
|
||||
// Internal waiting function
|
||||
bool imp_wait(u32 _old, u64 _timeout) noexcept;
|
||||
|
||||
// Try to notify up to _count threads
|
||||
void imp_wake(u32 _count) noexcept;
|
||||
|
||||
public:
|
||||
constexpr cond_variable() = default;
|
||||
|
||||
// Intrusive wait algorithm for lockable objects
|
||||
template <typename T>
|
||||
explicit_bool_t wait(T& object, u64 usec_timeout = -1)
|
||||
{
|
||||
const u32 _old = m_value.fetch_add(1); // Increment waiter counter
|
||||
object.unlock();
|
||||
const bool res = imp_wait(_old, usec_timeout);
|
||||
object.lock();
|
||||
return res;
|
||||
}
|
||||
|
||||
// Wake one thread
|
||||
void notify_one() noexcept
|
||||
{
|
||||
if (m_value)
|
||||
{
|
||||
imp_wake(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Wake all threads
|
||||
void notify_all() noexcept
|
||||
{
|
||||
if (m_value)
|
||||
{
|
||||
imp_wake(-1);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1,10 +0,0 @@
|
||||
Copyright (C) 2011-2012 Andrzej Krzemienski
|
||||
|
||||
Distributed under the Boost Software License, Version 1.0
|
||||
(see accompanying file LICENSE_1_0.txt or a copy at
|
||||
http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
The idea and interface is based on Boost.Optional library
|
||||
authored by Fernando Luis Cacciola Carballal
|
||||
|
||||
Home at https://github.com/akrzemi1/Optional
|
||||
@@ -57,13 +57,4 @@ namespace utils
|
||||
{
|
||||
return loaded();
|
||||
}
|
||||
|
||||
void* get_proc_address(const char* lib, const char* name)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return reinterpret_cast<void*>(GetProcAddress(GetModuleHandleA(lib), name));
|
||||
#else
|
||||
return dlsym(dlopen(lib, RTLD_NOLOAD), name);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,43 +38,4 @@ namespace utils
|
||||
bool loaded() const;
|
||||
explicit operator bool() const;
|
||||
};
|
||||
|
||||
// (assume the lib is always loaded)
|
||||
void* get_proc_address(const char* lib, const char* name);
|
||||
|
||||
template <typename F>
|
||||
struct dynamic_import
|
||||
{
|
||||
static_assert(sizeof(F) == 0, "Invalid function type");
|
||||
};
|
||||
|
||||
template <typename R, typename... Args>
|
||||
struct dynamic_import<R(Args...)>
|
||||
{
|
||||
R(*ptr)(Args...);
|
||||
const char* const lib;
|
||||
const char* const name;
|
||||
|
||||
// Constant initialization
|
||||
constexpr dynamic_import(const char* lib, const char* name)
|
||||
: ptr(nullptr)
|
||||
, lib(lib)
|
||||
, name(name)
|
||||
{
|
||||
}
|
||||
|
||||
// Caller
|
||||
R operator()(Args... args)
|
||||
{
|
||||
if (!ptr)
|
||||
{
|
||||
// TODO: atomic
|
||||
ptr = reinterpret_cast<R(*)(Args...)>(get_proc_address(lib, name));
|
||||
}
|
||||
|
||||
return ptr(args...);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#define DYNAMIC_IMPORT(lib, name, ...) static utils::dynamic_import<__VA_ARGS__> name(lib, #name);
|
||||
|
||||
@@ -1,193 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <deque>
|
||||
#include <list>
|
||||
|
||||
#include "Atomic.h"
|
||||
|
||||
template <typename T, T Mod = T::__state_enum_max, typename Under = std::underlying_type_t<T>>
|
||||
T operator ++(T& value, int)
|
||||
{
|
||||
return std::exchange(value, static_cast<T>(value < T{} || value >= Mod ? static_cast<Under>(0) : static_cast<Under>(value) + 1));
|
||||
}
|
||||
|
||||
template <typename T, T Mod = T::__state_enum_max, typename Under = std::underlying_type_t<T>>
|
||||
T operator --(T& value, int)
|
||||
{
|
||||
return std::exchange(value, static_cast<T>(value <= T{} || value >= static_cast<Under>(Mod) - 1 ? static_cast<Under>(Mod) - 1 : static_cast<Under>(value) - 1));
|
||||
}
|
||||
|
||||
template <typename T, typename CRT, std::size_t Size = static_cast<std::underlying_type_t<T>>(T::__state_enum_max)>
|
||||
class state_machine
|
||||
{
|
||||
using under = std::underlying_type_t<T>;
|
||||
using ftype = void(CRT::*)(T);
|
||||
|
||||
atomic_t<T> m_value;
|
||||
|
||||
template <std::size_t... Ind>
|
||||
static inline ftype transition_map(std::integer_sequence<std::size_t, Ind...>, T state)
|
||||
{
|
||||
// Constantly initialized list of functions
|
||||
static constexpr ftype map[Size]{&CRT::template transition<static_cast<T>(Ind)>...};
|
||||
|
||||
// Unsafe table lookup (TODO)
|
||||
return map[static_cast<under>(state)];
|
||||
}
|
||||
|
||||
// "Convert" variable argument to template argument
|
||||
static inline ftype transition_get(T state)
|
||||
{
|
||||
return transition_map(std::make_index_sequence<Size>(), state);
|
||||
}
|
||||
|
||||
public:
|
||||
constexpr state_machine()
|
||||
: m_value{T{}}
|
||||
{
|
||||
}
|
||||
|
||||
constexpr state_machine(T state)
|
||||
: m_value{state}
|
||||
{
|
||||
}
|
||||
|
||||
// Get current state
|
||||
T state_get() const
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
// Unconditionally set state
|
||||
void state_set(T state)
|
||||
{
|
||||
T _old = m_value.exchange(state);
|
||||
|
||||
if (_old != state)
|
||||
{
|
||||
(static_cast<CRT*>(this)->*transition_get(state))(_old);
|
||||
}
|
||||
}
|
||||
|
||||
// Conditionally set state (optimized)
|
||||
explicit_bool_t state_test_and_set(T expected, T state)
|
||||
{
|
||||
if (m_value == expected && m_value.compare_and_swap_test(expected, state))
|
||||
{
|
||||
(static_cast<CRT*>(this)->*transition_get(state))(expected);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Conditionally set state (list version)
|
||||
explicit_bool_t state_test_and_set(std::initializer_list<T> expected, T state)
|
||||
{
|
||||
T _old;
|
||||
|
||||
if (m_value.atomic_op([&](T& value)
|
||||
{
|
||||
for (T x : expected)
|
||||
{
|
||||
if (value == x)
|
||||
{
|
||||
_old = std::exchange(value, state);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}))
|
||||
{
|
||||
(static_cast<CRT*>(this)->*transition_get(state))(_old);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Unconditionally set next state
|
||||
void state_next()
|
||||
{
|
||||
T _old, state = m_value.op_fetch([&](T& value)
|
||||
{
|
||||
_old = value++;
|
||||
});
|
||||
|
||||
(static_cast<CRT*>(this)->*transition_get(state))(_old);
|
||||
}
|
||||
|
||||
// Unconditionally set previous state
|
||||
void state_prev()
|
||||
{
|
||||
T _old, state = m_value.op_fetch([&](T& value)
|
||||
{
|
||||
_old = value--;
|
||||
});
|
||||
|
||||
(static_cast<CRT*>(this)->*transition_get(state))(_old);
|
||||
}
|
||||
|
||||
// Get number of states
|
||||
static constexpr std::size_t size()
|
||||
{
|
||||
return Size;
|
||||
}
|
||||
};
|
||||
|
||||
//enum class test_state
|
||||
//{
|
||||
// on,
|
||||
// off,
|
||||
// la,
|
||||
//
|
||||
// __state_enum_max // 3
|
||||
//};
|
||||
//
|
||||
//struct test_machine final : state_machine<test_state, test_machine>
|
||||
//{
|
||||
// template <test_state>
|
||||
// void transition(test_state old_state);
|
||||
//
|
||||
// void on()
|
||||
// {
|
||||
// state_set(test_state::on);
|
||||
// }
|
||||
//
|
||||
// void off()
|
||||
// {
|
||||
// state_set(test_state::off);
|
||||
// }
|
||||
//
|
||||
// void test()
|
||||
// {
|
||||
// state_next();
|
||||
// }
|
||||
//};
|
||||
//
|
||||
//template <>
|
||||
//void test_machine::transition<test_state::on>(test_state)
|
||||
//{
|
||||
// LOG_SUCCESS(GENERAL, "ON");
|
||||
//}
|
||||
//
|
||||
//template <>
|
||||
//void test_machine::transition<test_state::off>(test_state)
|
||||
//{
|
||||
// LOG_SUCCESS(GENERAL, "OFF");
|
||||
//}
|
||||
//
|
||||
//
|
||||
//template <>
|
||||
//void test_machine::transition<test_state::la>(test_state)
|
||||
//{
|
||||
// on();
|
||||
// off();
|
||||
// test();
|
||||
//}
|
||||
|
||||
enum class event_result
|
||||
{
|
||||
skip,
|
||||
|
||||
+4
-229
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "types.h"
|
||||
#include "Atomic.h"
|
||||
#include "Platform.h"
|
||||
|
||||
//! Simple sizeless array base for concurrent access. Cannot shrink, only growths automatically.
|
||||
//! There is no way to know the current size. The smaller index is, the faster it's accessed.
|
||||
@@ -66,9 +67,9 @@ public:
|
||||
constexpr lf_fifo() = default;
|
||||
|
||||
// Get current "push" position
|
||||
u32 size() const
|
||||
u32 size()
|
||||
{
|
||||
return reinterpret_cast<const atomic_t<u32>&>(m_ctrl).load(); // Hack
|
||||
return reinterpret_cast<atomic_t<u32>&>(m_ctrl).load(); // Hack
|
||||
}
|
||||
|
||||
// Acquire the place for one or more elements.
|
||||
@@ -78,7 +79,7 @@ public:
|
||||
}
|
||||
|
||||
// Get current "pop" position
|
||||
u32 peek() const
|
||||
u32 peek()
|
||||
{
|
||||
return m_ctrl.load().pop;
|
||||
}
|
||||
@@ -102,229 +103,3 @@ public:
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
//! Simple lock-free map. Based on lf_array<>. All elements are accessible, implicitly initialized.
|
||||
template<typename K, typename T, typename Hash = value_hash<K>, std::size_t Size = 256>
|
||||
class lf_hashmap
|
||||
{
|
||||
struct pair_t
|
||||
{
|
||||
// Default-constructed key means "no key"
|
||||
atomic_t<K> key{};
|
||||
T value{};
|
||||
};
|
||||
|
||||
//
|
||||
lf_array<pair_t, Size> m_data{};
|
||||
|
||||
// Value for default-constructed key
|
||||
T m_default_key_data{};
|
||||
|
||||
public:
|
||||
constexpr lf_hashmap() = default;
|
||||
|
||||
// Access element (added implicitly)
|
||||
T& operator [](const K& key)
|
||||
{
|
||||
if (UNLIKELY(key == K{}))
|
||||
{
|
||||
return m_default_key_data;
|
||||
}
|
||||
|
||||
// Calculate hash and array position
|
||||
for (std::size_t pos = Hash{}(key) % Size;; pos += Size)
|
||||
{
|
||||
// Access the array
|
||||
auto& pair = m_data[pos];
|
||||
|
||||
// Check the key value (optimistic)
|
||||
if (LIKELY(pair.key == key) || pair.key.compare_and_swap_test(K{}, key))
|
||||
{
|
||||
return pair.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Fixed-size single-producer single-consumer queue
|
||||
template <typename T, std::uint32_t N>
|
||||
class lf_spsc
|
||||
{
|
||||
// If N is a power of 2, m_push/m_pop can safely overflow and the algorithm is simplified
|
||||
static_assert(N && (1u << 31) % N == 0, "lf_spsc<> error: size must be power of 2");
|
||||
|
||||
protected:
|
||||
volatile std::uint32_t m_push{0};
|
||||
volatile std::uint32_t m_pop{0};
|
||||
T m_data[N]{};
|
||||
|
||||
public:
|
||||
constexpr lf_spsc() = default;
|
||||
|
||||
// Try to push (producer only)
|
||||
template <typename T2>
|
||||
bool try_push(T2&& data)
|
||||
{
|
||||
const std::uint32_t pos = m_push;
|
||||
|
||||
if (pos - m_pop >= N)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_mm_lfence();
|
||||
m_data[pos % N] = std::forward<T2>(data);
|
||||
_mm_sfence();
|
||||
m_push = pos + 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Try to get push pointer (producer only)
|
||||
operator T*()
|
||||
{
|
||||
const std::uint32_t pos = m_push;
|
||||
|
||||
if (pos - m_pop >= N)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
_mm_lfence();
|
||||
return m_data + (pos % N);
|
||||
}
|
||||
|
||||
// Increment push counter (producer only)
|
||||
void end_push()
|
||||
{
|
||||
const std::uint32_t pos = m_push;
|
||||
|
||||
if (pos - m_pop < N)
|
||||
{
|
||||
_mm_sfence();
|
||||
m_push = pos + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Unsafe access
|
||||
T& get_push(std::size_t i)
|
||||
{
|
||||
_mm_lfence();
|
||||
return m_data[(m_push + i) % N];
|
||||
}
|
||||
|
||||
// Try to pop (consumer only)
|
||||
template <typename T2>
|
||||
bool try_pop(T2& out)
|
||||
{
|
||||
const std::uint32_t pos = m_pop;
|
||||
|
||||
if (m_push - pos <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_mm_lfence();
|
||||
out = std::move(m_data[pos % N]);
|
||||
_mm_sfence();
|
||||
m_pop = pos + 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Increment pop counter (consumer only)
|
||||
void end_pop()
|
||||
{
|
||||
const std::uint32_t pos = m_pop;
|
||||
|
||||
if (m_push - pos > 0)
|
||||
{
|
||||
_mm_sfence();
|
||||
m_pop = pos + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Get size (consumer only)
|
||||
std::uint32_t size() const
|
||||
{
|
||||
return m_push - m_pop;
|
||||
}
|
||||
|
||||
// Direct access (consumer only)
|
||||
T& operator [](std::size_t i)
|
||||
{
|
||||
_mm_lfence();
|
||||
return m_data[(m_pop + i) % N];
|
||||
}
|
||||
};
|
||||
|
||||
// Fixed-size multi-producer single-consumer queue
|
||||
template <typename T, std::uint32_t N>
|
||||
class lf_mpsc : lf_spsc<T, N>
|
||||
{
|
||||
protected:
|
||||
using lf_spsc<T, N>::m_push;
|
||||
using lf_spsc<T, N>::m_pop;
|
||||
using lf_spsc<T, N>::m_data;
|
||||
|
||||
enum : std::uint64_t
|
||||
{
|
||||
c_ack = 1ull << 0,
|
||||
c_rel = 1ull << 32,
|
||||
};
|
||||
|
||||
atomic_t<std::uint64_t> m_lock{0};
|
||||
|
||||
void release(std::uint64_t value)
|
||||
{
|
||||
// Push all pending elements at once when possible
|
||||
if (value && value % c_rel == value / c_rel)
|
||||
{
|
||||
_mm_sfence();
|
||||
m_push += value % c_rel;
|
||||
m_lock.compare_and_swap_test(value, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
constexpr lf_mpsc() = default;
|
||||
|
||||
// Try to get push pointer
|
||||
operator T*()
|
||||
{
|
||||
const std::uint64_t old = m_lock.fetch_add(c_ack);
|
||||
const std::uint32_t pos = m_push;
|
||||
|
||||
if (old % N >= N || pos - m_pop >= N - (old % N))
|
||||
{
|
||||
release(m_lock.sub_fetch(c_ack));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return m_data + ((pos + old) % N);
|
||||
}
|
||||
|
||||
// Increment push counter (producer only)
|
||||
void end_push()
|
||||
{
|
||||
release(m_lock.add_fetch(c_rel));
|
||||
}
|
||||
|
||||
// Try to push
|
||||
template <typename T2>
|
||||
bool try_push(T2&& data)
|
||||
{
|
||||
if (T* ptr = *this)
|
||||
{
|
||||
*ptr = std::forward<T2>(data);
|
||||
end_push();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enable consumer methods
|
||||
using lf_spsc<T, N>::try_pop;
|
||||
using lf_spsc<T, N>::end_pop;
|
||||
using lf_spsc<T, N>::size;
|
||||
using lf_spsc<T, N>::operator [];
|
||||
};
|
||||
|
||||
@@ -1,252 +0,0 @@
|
||||
#include "mutex.h"
|
||||
#include "sync.h"
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
void shared_mutex::imp_lock_shared(s64 _old)
|
||||
{
|
||||
verify("shared_mutex overflow" HERE), _old <= c_max;
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
busy_wait();
|
||||
|
||||
const s64 value = m_value.load();
|
||||
|
||||
if (value >= c_min && m_value.compare_and_swap_test(value, value - c_min))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// Acquire writer lock
|
||||
imp_wait(m_value.load());
|
||||
|
||||
// Convert to reader lock
|
||||
s64 value = m_value.fetch_add(c_one - c_min);
|
||||
|
||||
// Proceed exclusively
|
||||
return;
|
||||
|
||||
if (value != 0)
|
||||
{
|
||||
imp_unlock(value);
|
||||
}
|
||||
|
||||
// Wait as a reader if necessary
|
||||
if (value + c_one - c_min < 0)
|
||||
{
|
||||
NtWaitForKeyedEvent(nullptr, (int*)&m_value + 1, false, nullptr);
|
||||
}
|
||||
#else
|
||||
// Acquire writer lock
|
||||
imp_wait(0);
|
||||
|
||||
// Convert to reader lock
|
||||
m_value += c_one - c_min;
|
||||
|
||||
// Disabled code
|
||||
while (false)
|
||||
{
|
||||
const s64 value0 = m_value.fetch_op([](s64& value)
|
||||
{
|
||||
if (value >= c_min)
|
||||
{
|
||||
value -= c_min;
|
||||
}
|
||||
});
|
||||
|
||||
if (value0 >= c_min)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Acquire writer lock
|
||||
imp_wait(value0);
|
||||
|
||||
// Convert to reader lock
|
||||
s64 value1 = m_value.fetch_add(c_one - c_min);
|
||||
|
||||
if (value1 != 0)
|
||||
{
|
||||
imp_unlock(value1);
|
||||
}
|
||||
|
||||
value1 += c_one - c_min;
|
||||
|
||||
if (value1 > 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait as a reader if necessary
|
||||
while (futex((int*)&m_value.raw() + IS_LE_MACHINE, FUTEX_WAIT_BITSET_PRIVATE, int(value1 >> 32), nullptr, nullptr, INT_MIN))
|
||||
{
|
||||
value1 = m_value.load();
|
||||
|
||||
if (value1 >= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If blocked by writers, release the reader lock and try again
|
||||
const s64 value2 = m_value.fetch_op([](s64& value)
|
||||
{
|
||||
if (value < 0)
|
||||
{
|
||||
value += c_min;
|
||||
}
|
||||
});
|
||||
|
||||
if (value2 >= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
imp_unlock_shared(value2);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void shared_mutex::imp_unlock_shared(s64 _old)
|
||||
{
|
||||
verify("shared_mutex overflow" HERE), _old + c_min <= c_max;
|
||||
|
||||
// Check reader count, notify the writer if necessary
|
||||
if ((_old + c_min) % c_one == 0)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
NtReleaseKeyedEvent(nullptr, &m_value, false, nullptr);
|
||||
#else
|
||||
m_value -= c_sig;
|
||||
|
||||
futex((int*)&m_value.raw() + IS_LE_MACHINE, FUTEX_WAKE_BITSET_PRIVATE, 1, nullptr, nullptr, u32(c_sig >> 32));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void shared_mutex::imp_wait(s64)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (m_value.sub_fetch(c_one))
|
||||
{
|
||||
NtWaitForKeyedEvent(nullptr, &m_value, false, nullptr);
|
||||
}
|
||||
#else
|
||||
if (!m_value.sub_fetch(c_one))
|
||||
{
|
||||
// Return immediately if locked
|
||||
return;
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
// Load new value, try to acquire c_sig
|
||||
const s64 value = m_value.fetch_op([](s64& value)
|
||||
{
|
||||
if (value <= c_one - c_sig)
|
||||
{
|
||||
value += c_sig;
|
||||
}
|
||||
});
|
||||
|
||||
if (value <= c_one - c_sig)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
futex((int*)&m_value.raw() + IS_LE_MACHINE, FUTEX_WAIT_BITSET_PRIVATE, int(value >> 32), nullptr, nullptr, u32(c_sig >> 32));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void shared_mutex::imp_lock(s64 _old)
|
||||
{
|
||||
verify("shared_mutex overflow" HERE), _old <= c_max;
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
busy_wait();
|
||||
|
||||
const s64 value = m_value.load();
|
||||
|
||||
if (value == c_one && m_value.compare_and_swap_test(c_one, 0))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
imp_wait(m_value.load());
|
||||
}
|
||||
|
||||
void shared_mutex::imp_unlock(s64 _old)
|
||||
{
|
||||
verify("shared_mutex overflow" HERE), _old + c_one <= c_max;
|
||||
|
||||
// 1) Notify the next writer if necessary
|
||||
// 2) Notify all readers otherwise if necessary
|
||||
#ifdef _WIN32
|
||||
if (_old + c_one <= 0)
|
||||
{
|
||||
NtReleaseKeyedEvent(nullptr, &m_value, false, nullptr);
|
||||
}
|
||||
else if (s64 count = -_old / c_min * 0)
|
||||
{
|
||||
// Disabled code
|
||||
while (count--)
|
||||
{
|
||||
NtReleaseKeyedEvent(nullptr, (int*)&m_value + 1, false, nullptr);
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (_old + c_one <= 0)
|
||||
{
|
||||
m_value -= c_sig;
|
||||
|
||||
futex((int*)&m_value.raw() + IS_LE_MACHINE, FUTEX_WAKE_BITSET_PRIVATE, 1, nullptr, nullptr, u32(c_sig >> 32));
|
||||
}
|
||||
else if (false)
|
||||
{
|
||||
// Disabled code
|
||||
futex((int*)&m_value.raw() + IS_LE_MACHINE, FUTEX_WAKE_BITSET_PRIVATE, INT_MAX, nullptr, nullptr, INT_MIN);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void shared_mutex::imp_lock_upgrade()
|
||||
{
|
||||
unlock_shared();
|
||||
lock();
|
||||
}
|
||||
|
||||
void shared_mutex::imp_lock_degrade()
|
||||
{
|
||||
unlock();
|
||||
lock_shared();
|
||||
}
|
||||
|
||||
bool shared_mutex::try_lock_shared()
|
||||
{
|
||||
// Conditional decrement
|
||||
return m_value.fetch_op([](s64& value) { if (value >= c_min) value -= c_min; }) >= c_min;
|
||||
}
|
||||
|
||||
bool shared_mutex::try_lock()
|
||||
{
|
||||
// Conditional decrement (TODO: obtain c_sig)
|
||||
return m_value.compare_and_swap_test(c_one, 0);
|
||||
}
|
||||
|
||||
bool shared_mutex::try_lock_upgrade()
|
||||
{
|
||||
// TODO
|
||||
return m_value.compare_and_swap_test(c_one - c_min, 0);
|
||||
}
|
||||
|
||||
bool shared_mutex::try_lock_degrade()
|
||||
{
|
||||
// TODO
|
||||
return m_value.compare_and_swap_test(0, c_one - c_min);
|
||||
}
|
||||
@@ -1,173 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "Atomic.h"
|
||||
|
||||
// Shared mutex.
|
||||
class shared_mutex final
|
||||
{
|
||||
enum : s64
|
||||
{
|
||||
c_one = 1ull << 31, // Fixed-point 1.0 value (one writer)
|
||||
c_min = 0x00000001, // Fixed-point 1.0/max_readers value
|
||||
c_sig = 1ull << 62,
|
||||
c_max = c_one
|
||||
};
|
||||
|
||||
atomic_t<s64> m_value{c_one}; // Semaphore-alike counter
|
||||
|
||||
void imp_lock_shared(s64 _old);
|
||||
void imp_unlock_shared(s64 _old);
|
||||
void imp_wait(s64 _old);
|
||||
void imp_lock(s64 _old);
|
||||
void imp_unlock(s64 _old);
|
||||
|
||||
void imp_lock_upgrade();
|
||||
void imp_lock_degrade();
|
||||
|
||||
public:
|
||||
constexpr shared_mutex() = default;
|
||||
|
||||
bool try_lock_shared();
|
||||
|
||||
void lock_shared()
|
||||
{
|
||||
const s64 value = m_value.load();
|
||||
|
||||
// Fast path: decrement if positive
|
||||
if (UNLIKELY(value < c_min || value > c_one || !m_value.compare_and_swap_test(value, value - c_min)))
|
||||
{
|
||||
imp_lock_shared(value);
|
||||
}
|
||||
}
|
||||
|
||||
void unlock_shared()
|
||||
{
|
||||
// Unconditional increment
|
||||
const s64 value = m_value.fetch_add(c_min);
|
||||
|
||||
if (value < 0 || value > c_one - c_min)
|
||||
{
|
||||
imp_unlock_shared(value);
|
||||
}
|
||||
}
|
||||
|
||||
bool try_lock();
|
||||
|
||||
void lock()
|
||||
{
|
||||
// Try to lock
|
||||
const s64 value = m_value.compare_and_swap(c_one, 0);
|
||||
|
||||
if (value != c_one)
|
||||
{
|
||||
imp_lock(value);
|
||||
}
|
||||
}
|
||||
|
||||
void unlock()
|
||||
{
|
||||
// Unconditional increment
|
||||
const s64 value = m_value.fetch_add(c_one);
|
||||
|
||||
if (value != 0)
|
||||
{
|
||||
imp_unlock(value);
|
||||
}
|
||||
}
|
||||
|
||||
bool try_lock_upgrade();
|
||||
|
||||
void lock_upgrade()
|
||||
{
|
||||
if (!m_value.compare_and_swap_test(c_one - c_min, 0))
|
||||
{
|
||||
imp_lock_upgrade();
|
||||
}
|
||||
}
|
||||
|
||||
bool try_lock_degrade();
|
||||
|
||||
void lock_degrade()
|
||||
{
|
||||
if (!m_value.compare_and_swap_test(0, c_one - c_min))
|
||||
{
|
||||
imp_lock_degrade();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Simplified shared (reader) lock implementation.
|
||||
class reader_lock final
|
||||
{
|
||||
shared_mutex& m_mutex;
|
||||
bool m_upgraded = false;
|
||||
|
||||
void lock()
|
||||
{
|
||||
m_upgraded ? m_mutex.lock() : m_mutex.lock_shared();
|
||||
}
|
||||
|
||||
void unlock()
|
||||
{
|
||||
m_upgraded ? m_mutex.unlock() : m_mutex.unlock_shared();
|
||||
}
|
||||
|
||||
friend class cond_variable;
|
||||
|
||||
public:
|
||||
reader_lock(const reader_lock&) = delete;
|
||||
|
||||
explicit reader_lock(shared_mutex& mutex)
|
||||
: m_mutex(mutex)
|
||||
{
|
||||
lock();
|
||||
}
|
||||
|
||||
// One-way lock upgrade
|
||||
void upgrade()
|
||||
{
|
||||
if (!m_upgraded)
|
||||
{
|
||||
m_mutex.lock_upgrade();
|
||||
m_upgraded = true;
|
||||
}
|
||||
}
|
||||
|
||||
~reader_lock()
|
||||
{
|
||||
unlock();
|
||||
}
|
||||
};
|
||||
|
||||
// Simplified exclusive (writer) lock implementation.
|
||||
class writer_lock final
|
||||
{
|
||||
shared_mutex& m_mutex;
|
||||
|
||||
void lock()
|
||||
{
|
||||
m_mutex.lock();
|
||||
}
|
||||
|
||||
void unlock()
|
||||
{
|
||||
m_mutex.unlock();
|
||||
}
|
||||
|
||||
friend class cond_variable;
|
||||
|
||||
public:
|
||||
writer_lock(const writer_lock&) = delete;
|
||||
|
||||
explicit writer_lock(shared_mutex& mutex)
|
||||
: m_mutex(mutex)
|
||||
{
|
||||
lock();
|
||||
}
|
||||
|
||||
~writer_lock()
|
||||
{
|
||||
unlock();
|
||||
}
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,5 @@
|
||||
#include "stdafx.h"
|
||||
#include "restore_new.h"
|
||||
#include "Utilities/rXml.h"
|
||||
#include "define_new_memleakdetect.h"
|
||||
|
||||
rXmlNode::rXmlNode() : handle()
|
||||
{
|
||||
|
||||
@@ -1,124 +0,0 @@
|
||||
#ifndef MAPBOX_UTIL_RECURSIVE_WRAPPER_HPP
|
||||
#define MAPBOX_UTIL_RECURSIVE_WRAPPER_HPP
|
||||
|
||||
// Based on variant/recursive_wrapper.hpp from boost.
|
||||
//
|
||||
// Original license:
|
||||
//
|
||||
// Copyright (c) 2002-2003
|
||||
// Eric Friedman, Itay Maman
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See
|
||||
// accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include <cassert>
|
||||
#include <utility>
|
||||
|
||||
//namespace mapbox {
|
||||
//namespace util {
|
||||
namespace std {
|
||||
|
||||
template <typename T>
|
||||
class recursive_wrapper
|
||||
{
|
||||
|
||||
T* p_;
|
||||
|
||||
void assign(T const& rhs)
|
||||
{
|
||||
this->get() = rhs;
|
||||
}
|
||||
|
||||
public:
|
||||
using type = T;
|
||||
|
||||
/**
|
||||
* Default constructor default initializes the internally stored value.
|
||||
* For POD types this means nothing is done and the storage is
|
||||
* uninitialized.
|
||||
*
|
||||
* @throws std::bad_alloc if there is insufficient memory for an object
|
||||
* of type T.
|
||||
* @throws any exception thrown by the default constructur of T.
|
||||
*/
|
||||
recursive_wrapper()
|
||||
: p_(new T){}
|
||||
|
||||
~recursive_wrapper() noexcept { delete p_; }
|
||||
|
||||
recursive_wrapper(recursive_wrapper const& operand)
|
||||
: p_(new T(operand.get())) {}
|
||||
|
||||
recursive_wrapper(T const& operand)
|
||||
: p_(new T(operand)) {}
|
||||
|
||||
recursive_wrapper(recursive_wrapper&& operand)
|
||||
: p_(new T(std::move(operand.get()))) {}
|
||||
|
||||
recursive_wrapper(T&& operand)
|
||||
: p_(new T(std::move(operand))) {}
|
||||
|
||||
inline recursive_wrapper& operator=(recursive_wrapper const& rhs)
|
||||
{
|
||||
assign(rhs.get());
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline recursive_wrapper& operator=(T const& rhs)
|
||||
{
|
||||
assign(rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline void swap(recursive_wrapper& operand) noexcept
|
||||
{
|
||||
T* temp = operand.p_;
|
||||
operand.p_ = p_;
|
||||
p_ = temp;
|
||||
}
|
||||
|
||||
recursive_wrapper& operator=(recursive_wrapper&& rhs) noexcept
|
||||
{
|
||||
swap(rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
recursive_wrapper& operator=(T&& rhs)
|
||||
{
|
||||
get() = std::move(rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
T& get()
|
||||
{
|
||||
assert(p_);
|
||||
return *get_pointer();
|
||||
}
|
||||
|
||||
T const& get() const
|
||||
{
|
||||
assert(p_);
|
||||
return *get_pointer();
|
||||
}
|
||||
|
||||
T* get_pointer() { return p_; }
|
||||
|
||||
const T* get_pointer() const { return p_; }
|
||||
|
||||
operator T const&() const { return this->get(); }
|
||||
|
||||
operator T&() { return this->get(); }
|
||||
|
||||
}; // class recursive_wrapper
|
||||
|
||||
template <typename T>
|
||||
inline void swap(recursive_wrapper<T>& lhs, recursive_wrapper<T>& rhs) noexcept
|
||||
{
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
}
|
||||
//} // namespace util
|
||||
//} // namespace mapbox
|
||||
|
||||
#endif // MAPBOX_UTIL_RECURSIVE_WRAPPER_HPP
|
||||
@@ -1,103 +0,0 @@
|
||||
#include "sema.h"
|
||||
#include "sync.h"
|
||||
|
||||
void semaphore_base::imp_wait()
|
||||
{
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
busy_wait();
|
||||
|
||||
const s32 value = m_value.load();
|
||||
|
||||
if (value > 0 && m_value.compare_and_swap_test(value, value - 1))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
const s32 value = m_value.fetch_sub(1);
|
||||
|
||||
if (value <= 0)
|
||||
{
|
||||
NtWaitForKeyedEvent(nullptr, &m_value, false, nullptr);
|
||||
}
|
||||
#else
|
||||
while (true)
|
||||
{
|
||||
// Try hard way
|
||||
const s32 value = m_value.op_fetch([](s32& value)
|
||||
{
|
||||
// Use sign bit to acknowledge waiter presence
|
||||
if (value && value > INT32_MIN)
|
||||
{
|
||||
value--;
|
||||
|
||||
if (value < 0)
|
||||
{
|
||||
// Remove sign bit
|
||||
value -= INT32_MIN;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set sign bit
|
||||
value = INT32_MIN;
|
||||
}
|
||||
});
|
||||
|
||||
if (value >= 0)
|
||||
{
|
||||
// Signal other waiter to wake up or to restore sign bit
|
||||
futex(&m_value.raw(), FUTEX_WAKE_PRIVATE, 1, nullptr, nullptr, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
futex(&m_value.raw(), FUTEX_WAIT_PRIVATE, value, nullptr, nullptr, 0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void semaphore_base::imp_post(s32 _old)
|
||||
{
|
||||
verify("semaphore_base: overflow" HERE), _old < 0;
|
||||
|
||||
#ifdef _WIN32
|
||||
NtReleaseKeyedEvent(nullptr, &m_value, false, nullptr);
|
||||
#else
|
||||
futex(&m_value.raw(), FUTEX_WAKE_PRIVATE, 1, nullptr, nullptr, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool semaphore_base::try_wait()
|
||||
{
|
||||
// Conditional decrement
|
||||
const s32 value = m_value.fetch_op([](s32& value)
|
||||
{
|
||||
if (value > 0)
|
||||
{
|
||||
value -= 1;
|
||||
}
|
||||
});
|
||||
|
||||
return value > 0;
|
||||
}
|
||||
|
||||
bool semaphore_base::try_post(s32 _max)
|
||||
{
|
||||
// Conditional increment
|
||||
const s32 value = m_value.fetch_op([&](s32& value)
|
||||
{
|
||||
if (value < _max)
|
||||
{
|
||||
value += 1;
|
||||
}
|
||||
});
|
||||
|
||||
if (value < 0)
|
||||
{
|
||||
imp_post(value);
|
||||
}
|
||||
|
||||
return value < _max;
|
||||
}
|
||||
@@ -1,146 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "Atomic.h"
|
||||
|
||||
// Lightweight semaphore helper class
|
||||
class semaphore_base
|
||||
{
|
||||
// Semaphore value
|
||||
atomic_t<s32> m_value;
|
||||
|
||||
void imp_wait();
|
||||
|
||||
void imp_post(s32 _old);
|
||||
|
||||
friend class semaphore_lock;
|
||||
|
||||
protected:
|
||||
explicit constexpr semaphore_base(s32 value)
|
||||
: m_value{value}
|
||||
{
|
||||
}
|
||||
|
||||
void wait()
|
||||
{
|
||||
// Load value
|
||||
const s32 value = m_value.load();
|
||||
|
||||
// Conditional decrement
|
||||
if (UNLIKELY(value <= 0 || !m_value.compare_and_swap_test(value, value - 1)))
|
||||
{
|
||||
imp_wait();
|
||||
}
|
||||
}
|
||||
|
||||
bool try_wait();
|
||||
|
||||
void post(s32 _max)
|
||||
{
|
||||
// Unconditional increment
|
||||
const s32 value = m_value.fetch_add(1);
|
||||
|
||||
if (UNLIKELY(value < 0 || value >= _max))
|
||||
{
|
||||
imp_post(value);
|
||||
}
|
||||
}
|
||||
|
||||
bool try_post(s32 _max);
|
||||
|
||||
public:
|
||||
// Get current semaphore value
|
||||
s32 get() const
|
||||
{
|
||||
// Load value
|
||||
const s32 value = m_value;
|
||||
|
||||
// Return only positive value
|
||||
return value < 0 ? 0 : value;
|
||||
}
|
||||
};
|
||||
|
||||
// Lightweight semaphore template (default arguments define binary semaphore and Def == Max)
|
||||
template <s32 Max = 1, s32 Def = Max>
|
||||
class semaphore final : public semaphore_base
|
||||
{
|
||||
static_assert(Max >= 0, "semaphore<>: Max is out of bounds");
|
||||
static_assert(Def >= 0, "semaphore<>: Def is out of bounds");
|
||||
static_assert(Def <= Max, "semaphore<>: Def is too big");
|
||||
|
||||
using base = semaphore_base;
|
||||
|
||||
public:
|
||||
// Default constructor (recommended)
|
||||
constexpr semaphore()
|
||||
: base{Def}
|
||||
{
|
||||
}
|
||||
|
||||
// Explicit value constructor (not recommended)
|
||||
explicit constexpr semaphore(s32 value)
|
||||
: base{value}
|
||||
{
|
||||
}
|
||||
|
||||
// Obtain a semaphore
|
||||
void wait()
|
||||
{
|
||||
return base::wait();
|
||||
}
|
||||
|
||||
// Try to obtain a semaphore
|
||||
explicit_bool_t try_wait()
|
||||
{
|
||||
return base::try_wait();
|
||||
}
|
||||
|
||||
// Return a semaphore
|
||||
void post()
|
||||
{
|
||||
return base::post(Max);
|
||||
}
|
||||
|
||||
// Try to return a semaphore
|
||||
explicit_bool_t try_post()
|
||||
{
|
||||
return base::try_post(Max);
|
||||
}
|
||||
|
||||
// Get max semaphore value
|
||||
static constexpr s32 size()
|
||||
{
|
||||
return Max;
|
||||
}
|
||||
};
|
||||
|
||||
class semaphore_lock
|
||||
{
|
||||
semaphore_base& m_base;
|
||||
|
||||
void lock()
|
||||
{
|
||||
m_base.wait();
|
||||
}
|
||||
|
||||
void unlock()
|
||||
{
|
||||
m_base.post(INT32_MAX);
|
||||
}
|
||||
|
||||
friend class cond_variable;
|
||||
|
||||
public:
|
||||
explicit semaphore_lock(const semaphore_lock&) = delete;
|
||||
|
||||
semaphore_lock(semaphore_base& sema)
|
||||
: m_base(sema)
|
||||
{
|
||||
lock();
|
||||
}
|
||||
|
||||
~semaphore_lock()
|
||||
{
|
||||
unlock();
|
||||
}
|
||||
};
|
||||
+165
-124
@@ -3,146 +3,187 @@
|
||||
/* For internal use. Don't include. */
|
||||
|
||||
#include "types.h"
|
||||
#include "Macro.h"
|
||||
#include "Atomic.h"
|
||||
#include "dynamic_library.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include <Windows.h>
|
||||
#include <time.h>
|
||||
#elif __linux__
|
||||
#include <errno.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <linux/futex.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#else
|
||||
#endif
|
||||
#include <ctime>
|
||||
#include <chrono>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <unordered_map>
|
||||
|
||||
#ifdef _WIN32
|
||||
DYNAMIC_IMPORT("ntdll.dll", NtSetTimerResolution, NTSTATUS(ULONG DesiredResolution, BOOLEAN SetResolution, PULONG CurrentResolution));
|
||||
DYNAMIC_IMPORT("ntdll.dll", NtWaitForKeyedEvent, NTSTATUS(HANDLE Handle, PVOID Key, BOOLEAN Alertable, PLARGE_INTEGER Timeout));
|
||||
DYNAMIC_IMPORT("ntdll.dll", NtReleaseKeyedEvent, NTSTATUS(HANDLE Handle, PVOID Key, BOOLEAN Alertable, PLARGE_INTEGER Timeout));
|
||||
DYNAMIC_IMPORT("ntdll.dll", NtDelayExecution, NTSTATUS(BOOLEAN Alertable, PLARGE_INTEGER DelayInterval));
|
||||
#endif
|
||||
#define DYNAMIC_IMPORT(handle, name) do { name = reinterpret_cast<decltype(name)>(GetProcAddress(handle, #name)); } while (0)
|
||||
|
||||
#ifndef __linux__
|
||||
enum
|
||||
static NTSTATUS(*NtSetTimerResolution)(ULONG DesiredResolution, BOOLEAN SetResolution, PULONG CurrentResolution);
|
||||
static NTSTATUS(*NtWaitForKeyedEvent)(HANDLE Handle, PVOID Key, BOOLEAN Alertable, PLARGE_INTEGER Timeout);
|
||||
static NTSTATUS(*NtReleaseKeyedEvent)(HANDLE Handle, PVOID Key, BOOLEAN Alertable, PLARGE_INTEGER Timeout);
|
||||
|
||||
namespace util
|
||||
{
|
||||
FUTEX_PRIVATE_FLAG = 0,
|
||||
FUTEX_WAIT = 0,
|
||||
FUTEX_WAIT_PRIVATE = FUTEX_WAIT,
|
||||
FUTEX_WAKE = 1,
|
||||
FUTEX_WAKE_PRIVATE = FUTEX_WAKE,
|
||||
FUTEX_BITSET = 2,
|
||||
FUTEX_WAIT_BITSET = FUTEX_WAIT | FUTEX_BITSET,
|
||||
FUTEX_WAIT_BITSET_PRIVATE = FUTEX_WAIT_BITSET,
|
||||
FUTEX_WAKE_BITSET = FUTEX_WAKE | FUTEX_BITSET,
|
||||
FUTEX_WAKE_BITSET_PRIVATE = FUTEX_WAKE_BITSET,
|
||||
};
|
||||
#endif
|
||||
|
||||
inline int futex(int* uaddr, int futex_op, int val, const timespec* timeout, int* uaddr2, int val3)
|
||||
{
|
||||
#ifdef __linux__
|
||||
return syscall(SYS_futex, uaddr, futex_op, val, timeout, uaddr, val3);
|
||||
#else
|
||||
static struct futex_map
|
||||
static const bool keyed_init = []
|
||||
{
|
||||
struct waiter
|
||||
const auto handle = LoadLibraryA("ntdll.dll");
|
||||
DYNAMIC_IMPORT(handle, NtSetTimerResolution);
|
||||
DYNAMIC_IMPORT(handle, NtWaitForKeyedEvent);
|
||||
DYNAMIC_IMPORT(handle, NtReleaseKeyedEvent);
|
||||
FreeLibrary(handle);
|
||||
|
||||
ULONG res = 100;
|
||||
NtSetTimerResolution(100, TRUE, &res);
|
||||
|
||||
return NtWaitForKeyedEvent && NtReleaseKeyedEvent;
|
||||
}();
|
||||
|
||||
// Wait for specified condition. func() acknowledges success by value modification.
|
||||
template<typename F>
|
||||
inline void keyed_wait(atomic_t<u32>& key, F&& func)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
int val;
|
||||
uint mask;
|
||||
std::condition_variable cv;
|
||||
};
|
||||
u32 read = key.load();
|
||||
u32 copy = read;
|
||||
|
||||
std::mutex mutex;
|
||||
std::unordered_multimap<int*, waiter*, pointer_hash<int>> map;
|
||||
|
||||
int operator()(int* uaddr, int futex_op, int val, const timespec* timeout, int*, uint val3)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
|
||||
switch (futex_op)
|
||||
while (func(read), read != copy)
|
||||
{
|
||||
case FUTEX_WAIT:
|
||||
{
|
||||
val3 = -1;
|
||||
// Fallthrough
|
||||
}
|
||||
case FUTEX_WAIT_BITSET:
|
||||
{
|
||||
if (*(volatile int*)uaddr != val)
|
||||
read = key.compare_and_swap(copy, read);
|
||||
|
||||
if (copy == read)
|
||||
{
|
||||
errno = EAGAIN;
|
||||
return -1;
|
||||
}
|
||||
|
||||
waiter rec;
|
||||
rec.val = val;
|
||||
rec.mask = val3;
|
||||
const auto& ref = *map.emplace(uaddr, &rec);
|
||||
|
||||
int res = 0;
|
||||
|
||||
if (!timeout)
|
||||
{
|
||||
rec.cv.wait(lock, [&] { return !rec.mask; });
|
||||
}
|
||||
else if (futex_op == FUTEX_WAIT)
|
||||
{
|
||||
const auto nsec = std::chrono::nanoseconds(timeout->tv_nsec + timeout->tv_sec * 1000000000ull);
|
||||
|
||||
if (!rec.cv.wait_for(lock, nsec, [&] { return !rec.mask; }))
|
||||
{
|
||||
res = -1;
|
||||
errno = ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO
|
||||
return;
|
||||
}
|
||||
|
||||
map.erase(std::find(map.find(uaddr), map.end(), ref));
|
||||
return res;
|
||||
copy = read;
|
||||
}
|
||||
|
||||
case FUTEX_WAKE:
|
||||
{
|
||||
val3 = -1;
|
||||
// Fallthrough
|
||||
}
|
||||
case FUTEX_WAKE_BITSET:
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
for (auto range = map.equal_range(uaddr); val && range.first != range.second; range.first++)
|
||||
{
|
||||
auto& entry = *range.first->second;
|
||||
|
||||
if (entry.mask & val3)
|
||||
{
|
||||
entry.cv.notify_one();
|
||||
entry.mask = 0;
|
||||
res++;
|
||||
val--;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
NtWaitForKeyedEvent(NULL, &key, FALSE, NULL);
|
||||
}
|
||||
} g_futex;
|
||||
}
|
||||
|
||||
return g_futex(uaddr, futex_op, val, timeout, uaddr2, val3);
|
||||
#endif
|
||||
// Try to wake up a thread.
|
||||
inline bool keyed_post(atomic_t<u32>& key, u32 acknowledged_value)
|
||||
{
|
||||
LARGE_INTEGER timeout;
|
||||
timeout.QuadPart = -50;
|
||||
|
||||
while (UNLIKELY(NtReleaseKeyedEvent(NULL, &key, FALSE, &timeout) != ERROR_SUCCESS))
|
||||
{
|
||||
if (key.load() != acknowledged_value)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct native_rwlock
|
||||
{
|
||||
SRWLOCK rwlock = SRWLOCK_INIT;
|
||||
|
||||
constexpr native_rwlock() = default;
|
||||
|
||||
native_rwlock(const native_rwlock&) = delete;
|
||||
|
||||
void lock()
|
||||
{
|
||||
AcquireSRWLockExclusive(&rwlock);
|
||||
}
|
||||
|
||||
bool try_lock()
|
||||
{
|
||||
return TryAcquireSRWLockExclusive(&rwlock) != 0;
|
||||
}
|
||||
|
||||
void unlock()
|
||||
{
|
||||
ReleaseSRWLockExclusive(&rwlock);
|
||||
}
|
||||
|
||||
void lock_shared()
|
||||
{
|
||||
AcquireSRWLockShared(&rwlock);
|
||||
}
|
||||
|
||||
bool try_lock_shared()
|
||||
{
|
||||
return TryAcquireSRWLockShared(&rwlock) != 0;
|
||||
}
|
||||
|
||||
void unlock_shared()
|
||||
{
|
||||
ReleaseSRWLockShared(&rwlock);
|
||||
}
|
||||
};
|
||||
|
||||
struct native_cond
|
||||
{
|
||||
CONDITION_VARIABLE cond = CONDITION_VARIABLE_INIT;
|
||||
|
||||
constexpr native_cond() = default;
|
||||
|
||||
native_cond(const native_cond&) = delete;
|
||||
|
||||
void notify_one()
|
||||
{
|
||||
WakeConditionVariable(&cond);
|
||||
}
|
||||
|
||||
void notify_all()
|
||||
{
|
||||
WakeAllConditionVariable(&cond);
|
||||
}
|
||||
|
||||
void wait(native_rwlock& rwlock)
|
||||
{
|
||||
SleepConditionVariableSRW(&cond, &rwlock.rwlock, INFINITE, 0);
|
||||
}
|
||||
|
||||
void wait_shared(native_rwlock& rwlock)
|
||||
{
|
||||
SleepConditionVariableSRW(&cond, &rwlock.rwlock, INFINITE, CONDITION_VARIABLE_LOCKMODE_SHARED);
|
||||
}
|
||||
};
|
||||
|
||||
class exclusive_lock
|
||||
{
|
||||
native_rwlock& m_rwlock;
|
||||
|
||||
public:
|
||||
exclusive_lock(native_rwlock& rwlock)
|
||||
: m_rwlock(rwlock)
|
||||
{
|
||||
m_rwlock.lock();
|
||||
}
|
||||
|
||||
~exclusive_lock()
|
||||
{
|
||||
m_rwlock.unlock();
|
||||
}
|
||||
};
|
||||
|
||||
class shared_lock
|
||||
{
|
||||
native_rwlock& m_rwlock;
|
||||
|
||||
public:
|
||||
shared_lock(native_rwlock& rwlock)
|
||||
: m_rwlock(rwlock)
|
||||
{
|
||||
m_rwlock.lock_shared();
|
||||
}
|
||||
|
||||
~shared_lock()
|
||||
{
|
||||
m_rwlock.unlock_shared();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
namespace util
|
||||
{
|
||||
struct native_rwlock;
|
||||
struct native_cond;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
CHECK_SIZE_ALIGN(util::native_rwlock, sizeof(void*), alignof(void*));
|
||||
CHECK_SIZE_ALIGN(util::native_cond, sizeof(void*), alignof(void*));
|
||||
|
||||
+170
-681
File diff suppressed because it is too large
Load Diff
@@ -1,963 +0,0 @@
|
||||
#ifndef MAPBOX_UTIL_VARIANT_HPP
|
||||
#define MAPBOX_UTIL_VARIANT_HPP
|
||||
|
||||
#include <cassert>
|
||||
#include <cstddef> // size_t
|
||||
#include <new> // operator new
|
||||
#include <stdexcept> // runtime_error
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <typeinfo>
|
||||
#include <utility>
|
||||
|
||||
#include "recursive_wrapper.hpp"
|
||||
|
||||
// clang-format off
|
||||
// [[deprecated]] is only available in C++14, use this for the time being
|
||||
#if __cplusplus <= 201103L
|
||||
# ifdef __GNUC__
|
||||
# define MAPBOX_VARIANT_DEPRECATED __attribute__((deprecated))
|
||||
# elif defined(_MSC_VER)
|
||||
# define MAPBOX_VARIANT_DEPRECATED __declspec(deprecated)
|
||||
# else
|
||||
# define MAPBOX_VARIANT_DEPRECATED
|
||||
# endif
|
||||
#else
|
||||
# define MAPBOX_VARIANT_DEPRECATED [[deprecated]]
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// https://msdn.microsoft.com/en-us/library/bw1hbe6y.aspx
|
||||
# ifdef NDEBUG
|
||||
# define VARIANT_INLINE __forceinline
|
||||
# else
|
||||
# define VARIANT_INLINE //__declspec(noinline)
|
||||
# endif
|
||||
#else
|
||||
# ifdef NDEBUG
|
||||
# define VARIANT_INLINE //inline __attribute__((always_inline))
|
||||
# else
|
||||
# define VARIANT_INLINE __attribute__((noinline))
|
||||
# endif
|
||||
#endif
|
||||
// clang-format on
|
||||
|
||||
// Exceptions
|
||||
#if defined( __EXCEPTIONS) || defined( _MSC_VER)
|
||||
#define HAS_EXCEPTIONS
|
||||
#endif
|
||||
|
||||
#define VARIANT_MAJOR_VERSION 1
|
||||
#define VARIANT_MINOR_VERSION 1
|
||||
#define VARIANT_PATCH_VERSION 0
|
||||
|
||||
#define VARIANT_VERSION (VARIANT_MAJOR_VERSION * 100000) + (VARIANT_MINOR_VERSION * 100) + (VARIANT_PATCH_VERSION)
|
||||
|
||||
//namespace mapbox {
|
||||
// namespace util {
|
||||
namespace std {
|
||||
|
||||
// XXX This should derive from std::logic_error instead of std::runtime_error.
|
||||
// See https://github.com/mapbox/variant/issues/48 for details.
|
||||
class bad_variant_access : public std::runtime_error
|
||||
{
|
||||
|
||||
public:
|
||||
explicit bad_variant_access(const std::string& what_arg)
|
||||
: runtime_error(what_arg) {}
|
||||
|
||||
explicit bad_variant_access(const char* what_arg)
|
||||
: runtime_error(what_arg) {}
|
||||
|
||||
}; // class bad_variant_access
|
||||
|
||||
namespace detail {
|
||||
|
||||
static constexpr std::size_t invalid_value = std::size_t(-1);
|
||||
|
||||
template <typename T, typename... Types>
|
||||
struct direct_type;
|
||||
|
||||
template <typename T, typename First, typename... Types>
|
||||
struct direct_type<T, First, Types...>
|
||||
{
|
||||
static constexpr std::size_t index = std::is_same<T, First>::value
|
||||
? sizeof...(Types)
|
||||
: direct_type<T, Types...>::index;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct direct_type<T>
|
||||
{
|
||||
static constexpr std::size_t index = invalid_value;
|
||||
};
|
||||
|
||||
#if __cpp_lib_logical_traits >= 201510L
|
||||
|
||||
using std::disjunction;
|
||||
|
||||
#else
|
||||
|
||||
template <typename...>
|
||||
struct disjunction : std::false_type {};
|
||||
|
||||
template <typename B1>
|
||||
struct disjunction<B1> : B1 {};
|
||||
|
||||
template <typename B1, typename B2>
|
||||
struct disjunction<B1, B2> : std::conditional<B1::value, B1, B2>::type {};
|
||||
|
||||
template <typename B1, typename... Bs>
|
||||
struct disjunction<B1, Bs...> : std::conditional<B1::value, B1, disjunction<Bs...>>::type {};
|
||||
|
||||
#endif
|
||||
|
||||
template <typename T, typename... Types>
|
||||
struct convertible_type;
|
||||
|
||||
template <typename T, typename First, typename... Types>
|
||||
struct convertible_type<T, First, Types...>
|
||||
{
|
||||
static constexpr std::size_t index = std::is_convertible<T, First>::value
|
||||
? disjunction<std::is_convertible<T, Types>...>::value ? invalid_value : sizeof...(Types)
|
||||
: convertible_type<T, Types...>::index;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct convertible_type<T>
|
||||
{
|
||||
static constexpr std::size_t index = invalid_value;
|
||||
};
|
||||
|
||||
template <typename T, typename... Types>
|
||||
struct value_traits
|
||||
{
|
||||
using value_type = typename std::remove_const<typename std::remove_reference<T>::type>::type;
|
||||
static constexpr std::size_t direct_index = direct_type<value_type, Types...>::index;
|
||||
static constexpr bool is_direct = direct_index != invalid_value;
|
||||
static constexpr std::size_t index = is_direct ? direct_index : convertible_type<value_type, Types...>::index;
|
||||
static constexpr bool is_valid = index != invalid_value;
|
||||
static constexpr std::size_t tindex = is_valid ? sizeof...(Types)-index : 0;
|
||||
using target_type = typename std::tuple_element<tindex, std::tuple<void, Types...>>::type;
|
||||
};
|
||||
|
||||
template <typename T, typename R = void>
|
||||
struct enable_if_type
|
||||
{
|
||||
using type = R;
|
||||
};
|
||||
|
||||
template <typename F, typename V, typename Enable = void>
|
||||
struct result_of_unary_visit
|
||||
{
|
||||
using type = typename std::result_of<F(V&)>::type;
|
||||
};
|
||||
|
||||
template <typename F, typename V>
|
||||
struct result_of_unary_visit<F, V, typename enable_if_type<typename F::result_type>::type>
|
||||
{
|
||||
using type = typename F::result_type;
|
||||
};
|
||||
|
||||
template <typename F, typename V, typename Enable = void>
|
||||
struct result_of_binary_visit
|
||||
{
|
||||
using type = typename std::result_of<F(V&, V&)>::type;
|
||||
};
|
||||
|
||||
template <typename F, typename V>
|
||||
struct result_of_binary_visit<F, V, typename enable_if_type<typename F::result_type>::type>
|
||||
{
|
||||
using type = typename F::result_type;
|
||||
};
|
||||
|
||||
template <std::size_t arg1, std::size_t... others>
|
||||
struct static_max;
|
||||
|
||||
template <std::size_t arg>
|
||||
struct static_max<arg>
|
||||
{
|
||||
static const std::size_t value = arg;
|
||||
};
|
||||
|
||||
template <std::size_t arg1, std::size_t arg2, std::size_t... others>
|
||||
struct static_max<arg1, arg2, others...>
|
||||
{
|
||||
static const std::size_t value = arg1 >= arg2 ? static_max<arg1, others...>::value : static_max<arg2, others...>::value;
|
||||
};
|
||||
|
||||
template <typename... Types>
|
||||
struct variant_helper;
|
||||
|
||||
template <typename T, typename... Types>
|
||||
struct variant_helper<T, Types...>
|
||||
{
|
||||
VARIANT_INLINE static void destroy(const std::size_t type_index, void* data)
|
||||
{
|
||||
if (type_index == sizeof...(Types))
|
||||
{
|
||||
reinterpret_cast<T*>(data)->~T();
|
||||
}
|
||||
else
|
||||
{
|
||||
variant_helper<Types...>::destroy(type_index, data);
|
||||
}
|
||||
}
|
||||
|
||||
VARIANT_INLINE static void move(const std::size_t old_type_index, void* old_value, void* new_value)
|
||||
{
|
||||
if (old_type_index == sizeof...(Types))
|
||||
{
|
||||
new (new_value) T(std::move(*reinterpret_cast<T*>(old_value)));
|
||||
}
|
||||
else
|
||||
{
|
||||
variant_helper<Types...>::move(old_type_index, old_value, new_value);
|
||||
}
|
||||
}
|
||||
|
||||
VARIANT_INLINE static void copy(const std::size_t old_type_index, const void* old_value, void* new_value)
|
||||
{
|
||||
if (old_type_index == sizeof...(Types))
|
||||
{
|
||||
new (new_value) T(*reinterpret_cast<const T*>(old_value));
|
||||
}
|
||||
else
|
||||
{
|
||||
variant_helper<Types...>::copy(old_type_index, old_value, new_value);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct variant_helper<>
|
||||
{
|
||||
VARIANT_INLINE static void destroy(const std::size_t, void*) {}
|
||||
VARIANT_INLINE static void move(const std::size_t, void*, void*) {}
|
||||
VARIANT_INLINE static void copy(const std::size_t, const void*, void*) {}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct unwrapper
|
||||
{
|
||||
static T const& apply_const(T const& obj) { return obj; }
|
||||
static T& apply(T& obj) { return obj; }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct unwrapper<recursive_wrapper<T>>
|
||||
{
|
||||
static auto apply_const(recursive_wrapper<T> const& obj)
|
||||
-> typename recursive_wrapper<T>::type const&
|
||||
{
|
||||
return obj.get();
|
||||
}
|
||||
static auto apply(recursive_wrapper<T>& obj)
|
||||
-> typename recursive_wrapper<T>::type&
|
||||
{
|
||||
return obj.get();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct unwrapper<std::reference_wrapper<T>>
|
||||
{
|
||||
static auto apply_const(std::reference_wrapper<T> const& obj)
|
||||
-> typename std::reference_wrapper<T>::type const&
|
||||
{
|
||||
return obj.get();
|
||||
}
|
||||
static auto apply(std::reference_wrapper<T>& obj)
|
||||
-> typename std::reference_wrapper<T>::type&
|
||||
{
|
||||
return obj.get();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename F, typename V, typename R, typename... Types>
|
||||
struct dispatcher;
|
||||
|
||||
template <typename F, typename V, typename R, typename T, typename... Types>
|
||||
struct dispatcher<F, V, R, T, Types...>
|
||||
{
|
||||
VARIANT_INLINE static R apply_const(V const& v, F&& f)
|
||||
{
|
||||
if (v.template is<T>())
|
||||
{
|
||||
return f(unwrapper<T>::apply_const(v.template get_unchecked<T>()));
|
||||
}
|
||||
else
|
||||
{
|
||||
return dispatcher<F, V, R, Types...>::apply_const(v, std::forward<F>(f));
|
||||
}
|
||||
}
|
||||
|
||||
VARIANT_INLINE static R apply(V& v, F&& f)
|
||||
{
|
||||
if (v.template is<T>())
|
||||
{
|
||||
return f(unwrapper<T>::apply(v.template get_unchecked<T>()));
|
||||
}
|
||||
else
|
||||
{
|
||||
return dispatcher<F, V, R, Types...>::apply(v, std::forward<F>(f));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename F, typename V, typename R, typename T>
|
||||
struct dispatcher<F, V, R, T>
|
||||
{
|
||||
VARIANT_INLINE static R apply_const(V const& v, F&& f)
|
||||
{
|
||||
return f(unwrapper<T>::apply_const(v.template get_unchecked<T>()));
|
||||
}
|
||||
|
||||
VARIANT_INLINE static R apply(V& v, F&& f)
|
||||
{
|
||||
return f(unwrapper<T>::apply(v.template get_unchecked<T>()));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename F, typename V, typename R, typename T, typename... Types>
|
||||
struct binary_dispatcher_rhs;
|
||||
|
||||
template <typename F, typename V, typename R, typename T0, typename T1, typename... Types>
|
||||
struct binary_dispatcher_rhs<F, V, R, T0, T1, Types...>
|
||||
{
|
||||
VARIANT_INLINE static R apply_const(V const& lhs, V const& rhs, F&& f)
|
||||
{
|
||||
if (rhs.template is<T1>()) // call binary functor
|
||||
{
|
||||
return f(unwrapper<T0>::apply_const(lhs.template get_unchecked<T0>()),
|
||||
unwrapper<T1>::apply_const(rhs.template get_unchecked<T1>()));
|
||||
}
|
||||
else
|
||||
{
|
||||
return binary_dispatcher_rhs<F, V, R, T0, Types...>::apply_const(lhs, rhs, std::forward<F>(f));
|
||||
}
|
||||
}
|
||||
|
||||
VARIANT_INLINE static R apply(V& lhs, V& rhs, F&& f)
|
||||
{
|
||||
if (rhs.template is<T1>()) // call binary functor
|
||||
{
|
||||
return f(unwrapper<T0>::apply(lhs.template get_unchecked<T0>()),
|
||||
unwrapper<T1>::apply(rhs.template get_unchecked<T1>()));
|
||||
}
|
||||
else
|
||||
{
|
||||
return binary_dispatcher_rhs<F, V, R, T0, Types...>::apply(lhs, rhs, std::forward<F>(f));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename F, typename V, typename R, typename T0, typename T1>
|
||||
struct binary_dispatcher_rhs<F, V, R, T0, T1>
|
||||
{
|
||||
VARIANT_INLINE static R apply_const(V const& lhs, V const& rhs, F&& f)
|
||||
{
|
||||
return f(unwrapper<T0>::apply_const(lhs.template get_unchecked<T0>()),
|
||||
unwrapper<T1>::apply_const(rhs.template get_unchecked<T1>()));
|
||||
}
|
||||
|
||||
VARIANT_INLINE static R apply(V& lhs, V& rhs, F&& f)
|
||||
{
|
||||
return f(unwrapper<T0>::apply(lhs.template get_unchecked<T0>()),
|
||||
unwrapper<T1>::apply(rhs.template get_unchecked<T1>()));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename F, typename V, typename R, typename T, typename... Types>
|
||||
struct binary_dispatcher_lhs;
|
||||
|
||||
template <typename F, typename V, typename R, typename T0, typename T1, typename... Types>
|
||||
struct binary_dispatcher_lhs<F, V, R, T0, T1, Types...>
|
||||
{
|
||||
VARIANT_INLINE static R apply_const(V const& lhs, V const& rhs, F&& f)
|
||||
{
|
||||
if (lhs.template is<T1>()) // call binary functor
|
||||
{
|
||||
return f(unwrapper<T1>::apply_const(lhs.template get_unchecked<T1>()),
|
||||
unwrapper<T0>::apply_const(rhs.template get_unchecked<T0>()));
|
||||
}
|
||||
else
|
||||
{
|
||||
return binary_dispatcher_lhs<F, V, R, T0, Types...>::apply_const(lhs, rhs, std::forward<F>(f));
|
||||
}
|
||||
}
|
||||
|
||||
VARIANT_INLINE static R apply(V& lhs, V& rhs, F&& f)
|
||||
{
|
||||
if (lhs.template is<T1>()) // call binary functor
|
||||
{
|
||||
return f(unwrapper<T1>::apply(lhs.template get_unchecked<T1>()),
|
||||
unwrapper<T0>::apply(rhs.template get_unchecked<T0>()));
|
||||
}
|
||||
else
|
||||
{
|
||||
return binary_dispatcher_lhs<F, V, R, T0, Types...>::apply(lhs, rhs, std::forward<F>(f));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename F, typename V, typename R, typename T0, typename T1>
|
||||
struct binary_dispatcher_lhs<F, V, R, T0, T1>
|
||||
{
|
||||
VARIANT_INLINE static R apply_const(V const& lhs, V const& rhs, F&& f)
|
||||
{
|
||||
return f(unwrapper<T1>::apply_const(lhs.template get_unchecked<T1>()),
|
||||
unwrapper<T0>::apply_const(rhs.template get_unchecked<T0>()));
|
||||
}
|
||||
|
||||
VARIANT_INLINE static R apply(V& lhs, V& rhs, F&& f)
|
||||
{
|
||||
return f(unwrapper<T1>::apply(lhs.template get_unchecked<T1>()),
|
||||
unwrapper<T0>::apply(rhs.template get_unchecked<T0>()));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename F, typename V, typename R, typename... Types>
|
||||
struct binary_dispatcher;
|
||||
|
||||
template <typename F, typename V, typename R, typename T, typename... Types>
|
||||
struct binary_dispatcher<F, V, R, T, Types...>
|
||||
{
|
||||
VARIANT_INLINE static R apply_const(V const& v0, V const& v1, F&& f)
|
||||
{
|
||||
if (v0.template is<T>())
|
||||
{
|
||||
if (v1.template is<T>())
|
||||
{
|
||||
return f(unwrapper<T>::apply_const(v0.template get_unchecked<T>()),
|
||||
unwrapper<T>::apply_const(v1.template get_unchecked<T>())); // call binary functor
|
||||
}
|
||||
else
|
||||
{
|
||||
return binary_dispatcher_rhs<F, V, R, T, Types...>::apply_const(v0, v1, std::forward<F>(f));
|
||||
}
|
||||
}
|
||||
else if (v1.template is<T>())
|
||||
{
|
||||
return binary_dispatcher_lhs<F, V, R, T, Types...>::apply_const(v0, v1, std::forward<F>(f));
|
||||
}
|
||||
return binary_dispatcher<F, V, R, Types...>::apply_const(v0, v1, std::forward<F>(f));
|
||||
}
|
||||
|
||||
VARIANT_INLINE static R apply(V& v0, V& v1, F&& f)
|
||||
{
|
||||
if (v0.template is<T>())
|
||||
{
|
||||
if (v1.template is<T>())
|
||||
{
|
||||
return f(unwrapper<T>::apply(v0.template get_unchecked<T>()),
|
||||
unwrapper<T>::apply(v1.template get_unchecked<T>())); // call binary functor
|
||||
}
|
||||
else
|
||||
{
|
||||
return binary_dispatcher_rhs<F, V, R, T, Types...>::apply(v0, v1, std::forward<F>(f));
|
||||
}
|
||||
}
|
||||
else if (v1.template is<T>())
|
||||
{
|
||||
return binary_dispatcher_lhs<F, V, R, T, Types...>::apply(v0, v1, std::forward<F>(f));
|
||||
}
|
||||
return binary_dispatcher<F, V, R, Types...>::apply(v0, v1, std::forward<F>(f));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename F, typename V, typename R, typename T>
|
||||
struct binary_dispatcher<F, V, R, T>
|
||||
{
|
||||
VARIANT_INLINE static R apply_const(V const& v0, V const& v1, F&& f)
|
||||
{
|
||||
return f(unwrapper<T>::apply_const(v0.template get_unchecked<T>()),
|
||||
unwrapper<T>::apply_const(v1.template get_unchecked<T>())); // call binary functor
|
||||
}
|
||||
|
||||
VARIANT_INLINE static R apply(V& v0, V& v1, F&& f)
|
||||
{
|
||||
return f(unwrapper<T>::apply(v0.template get_unchecked<T>()),
|
||||
unwrapper<T>::apply(v1.template get_unchecked<T>())); // call binary functor
|
||||
}
|
||||
};
|
||||
|
||||
// comparator functors
|
||||
struct equal_comp
|
||||
{
|
||||
template <typename T>
|
||||
bool operator()(T const& lhs, T const& rhs) const
|
||||
{
|
||||
return lhs == rhs;
|
||||
}
|
||||
};
|
||||
|
||||
struct less_comp
|
||||
{
|
||||
template <typename T>
|
||||
bool operator()(T const& lhs, T const& rhs) const
|
||||
{
|
||||
return lhs < rhs;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Variant, typename Comp>
|
||||
class comparer
|
||||
{
|
||||
public:
|
||||
explicit comparer(Variant const& lhs) noexcept
|
||||
: lhs_(lhs) {}
|
||||
comparer& operator=(comparer const&) = delete;
|
||||
// visitor
|
||||
template <typename T>
|
||||
bool operator()(T const& rhs_content) const
|
||||
{
|
||||
T const& lhs_content = lhs_.template get_unchecked<T>();
|
||||
return Comp()(lhs_content, rhs_content);
|
||||
}
|
||||
|
||||
private:
|
||||
Variant const& lhs_;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
struct no_init
|
||||
{
|
||||
};
|
||||
|
||||
template <typename... Types>
|
||||
class variant
|
||||
{
|
||||
static_assert(sizeof...(Types) > 0, "Template parameter type list of variant can not be empty");
|
||||
static_assert(!detail::disjunction<std::is_reference<Types>...>::value, "Variant can not hold reference types. Maybe use std::reference_wrapper?");
|
||||
|
||||
private:
|
||||
static const std::size_t data_size = detail::static_max<sizeof(Types)...>::value;
|
||||
static const std::size_t data_align = detail::static_max<alignof(Types)...>::value;
|
||||
|
||||
using first_type = typename std::tuple_element<0, std::tuple<Types...>>::type;
|
||||
using data_type = typename std::aligned_storage<data_size, data_align>::type;
|
||||
using helper_type = detail::variant_helper<Types...>;
|
||||
|
||||
std::size_t type_index;
|
||||
data_type data;
|
||||
|
||||
public:
|
||||
VARIANT_INLINE variant() noexcept(std::is_nothrow_default_constructible<first_type>::value)
|
||||
: type_index(sizeof...(Types)-1)
|
||||
{
|
||||
static_assert(std::is_default_constructible<first_type>::value, "First type in variant must be default constructible to allow default construction of variant");
|
||||
new (&data) first_type();
|
||||
}
|
||||
|
||||
VARIANT_INLINE variant(no_init) noexcept
|
||||
: type_index(detail::invalid_value) {}
|
||||
|
||||
// http://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers
|
||||
template <typename T, typename Traits = detail::value_traits<T, Types...>,
|
||||
typename Enable = typename std::enable_if<Traits::is_valid>::type>
|
||||
VARIANT_INLINE variant(T&& val) noexcept(std::is_nothrow_constructible<typename Traits::target_type, T&&>::value)
|
||||
: type_index(Traits::index)
|
||||
{
|
||||
new (&data) typename Traits::target_type(std::forward<T>(val));
|
||||
}
|
||||
|
||||
VARIANT_INLINE variant(variant<Types...> const& old)
|
||||
: type_index(old.type_index)
|
||||
{
|
||||
helper_type::copy(old.type_index, &old.data, &data);
|
||||
}
|
||||
|
||||
VARIANT_INLINE variant(variant<Types...>&& old) noexcept(std::is_nothrow_move_constructible<std::tuple<Types...>>::value)
|
||||
: type_index(old.type_index)
|
||||
{
|
||||
helper_type::move(old.type_index, &old.data, &data);
|
||||
}
|
||||
|
||||
private:
|
||||
VARIANT_INLINE void copy_assign(variant<Types...> const& rhs)
|
||||
{
|
||||
helper_type::destroy(type_index, &data);
|
||||
type_index = detail::invalid_value;
|
||||
helper_type::copy(rhs.type_index, &rhs.data, &data);
|
||||
type_index = rhs.type_index;
|
||||
}
|
||||
|
||||
VARIANT_INLINE void move_assign(variant<Types...>&& rhs)
|
||||
{
|
||||
helper_type::destroy(type_index, &data);
|
||||
type_index = detail::invalid_value;
|
||||
helper_type::move(rhs.type_index, &rhs.data, &data);
|
||||
type_index = rhs.type_index;
|
||||
}
|
||||
|
||||
public:
|
||||
VARIANT_INLINE variant<Types...>& operator=(variant<Types...>&& other)
|
||||
{
|
||||
move_assign(std::move(other));
|
||||
return *this;
|
||||
}
|
||||
|
||||
VARIANT_INLINE variant<Types...>& operator=(variant<Types...> const& other)
|
||||
{
|
||||
copy_assign(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// conversions
|
||||
// move-assign
|
||||
template <typename T>
|
||||
VARIANT_INLINE variant<Types...>& operator=(T&& rhs) noexcept
|
||||
{
|
||||
variant<Types...> temp(std::forward<T>(rhs));
|
||||
move_assign(std::move(temp));
|
||||
return *this;
|
||||
}
|
||||
|
||||
// copy-assign
|
||||
template <typename T>
|
||||
VARIANT_INLINE variant<Types...>& operator=(T const& rhs)
|
||||
{
|
||||
variant<Types...> temp(rhs);
|
||||
copy_assign(temp);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T, typename std::enable_if<
|
||||
(detail::direct_type<T, Types...>::index != detail::invalid_value)>::type* = nullptr>
|
||||
VARIANT_INLINE bool is() const
|
||||
{
|
||||
return type_index == detail::direct_type<T, Types...>::index;
|
||||
}
|
||||
|
||||
template <typename T, typename std::enable_if<
|
||||
(detail::direct_type<recursive_wrapper<T>, Types...>::index != detail::invalid_value)>::type* = nullptr>
|
||||
VARIANT_INLINE bool is() const
|
||||
{
|
||||
return type_index == detail::direct_type<recursive_wrapper<T>, Types...>::index;
|
||||
}
|
||||
|
||||
VARIANT_INLINE bool valid() const
|
||||
{
|
||||
return type_index != detail::invalid_value;
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
VARIANT_INLINE void set(Args&&... args)
|
||||
{
|
||||
helper_type::destroy(type_index, &data);
|
||||
type_index = detail::invalid_value;
|
||||
new (&data) T(std::forward<Args>(args)...);
|
||||
type_index = detail::direct_type<T, Types...>::index;
|
||||
}
|
||||
|
||||
// get_unchecked<T>()
|
||||
template <typename T, typename std::enable_if<
|
||||
(detail::direct_type<T, Types...>::index != detail::invalid_value)>::type* = nullptr>
|
||||
VARIANT_INLINE T& get_unchecked()
|
||||
{
|
||||
return *reinterpret_cast<T*>(&data);
|
||||
}
|
||||
|
||||
#ifdef HAS_EXCEPTIONS
|
||||
// get<T>()
|
||||
template <typename T, typename std::enable_if<
|
||||
(detail::direct_type<T, Types...>::index != detail::invalid_value)>::type* = nullptr>
|
||||
VARIANT_INLINE T& get()
|
||||
{
|
||||
if (type_index == detail::direct_type<T, Types...>::index)
|
||||
{
|
||||
return *reinterpret_cast<T*>(&data);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw bad_variant_access("in get<T>()");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename T, typename std::enable_if<
|
||||
(detail::direct_type<T, Types...>::index != detail::invalid_value)>::type* = nullptr>
|
||||
VARIANT_INLINE T const& get_unchecked() const
|
||||
{
|
||||
return *reinterpret_cast<T const*>(&data);
|
||||
}
|
||||
|
||||
#ifdef HAS_EXCEPTIONS
|
||||
template <typename T, typename std::enable_if<
|
||||
(detail::direct_type<T, Types...>::index != detail::invalid_value)>::type* = nullptr>
|
||||
VARIANT_INLINE T const& get() const
|
||||
{
|
||||
if (type_index == detail::direct_type<T, Types...>::index)
|
||||
{
|
||||
return *reinterpret_cast<T const*>(&data);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw bad_variant_access("in get<T>()");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// get_unchecked<T>() - T stored as recursive_wrapper<T>
|
||||
template <typename T, typename std::enable_if<
|
||||
(detail::direct_type<recursive_wrapper<T>, Types...>::index != detail::invalid_value)>::type* = nullptr>
|
||||
VARIANT_INLINE T& get_unchecked()
|
||||
{
|
||||
return (*reinterpret_cast<recursive_wrapper<T>*>(&data)).get();
|
||||
}
|
||||
|
||||
#ifdef HAS_EXCEPTIONS
|
||||
// get<T>() - T stored as recursive_wrapper<T>
|
||||
template <typename T, typename std::enable_if<
|
||||
(detail::direct_type<recursive_wrapper<T>, Types...>::index != detail::invalid_value)>::type* = nullptr>
|
||||
VARIANT_INLINE T& get()
|
||||
{
|
||||
if (type_index == detail::direct_type<recursive_wrapper<T>, Types...>::index)
|
||||
{
|
||||
return (*reinterpret_cast<recursive_wrapper<T>*>(&data)).get();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw bad_variant_access("in get<T>()");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename T, typename std::enable_if<
|
||||
(detail::direct_type<recursive_wrapper<T>, Types...>::index != detail::invalid_value)>::type* = nullptr>
|
||||
VARIANT_INLINE T const& get_unchecked() const
|
||||
{
|
||||
return (*reinterpret_cast<recursive_wrapper<T> const*>(&data)).get();
|
||||
}
|
||||
|
||||
#ifdef HAS_EXCEPTIONS
|
||||
template <typename T, typename std::enable_if<
|
||||
(detail::direct_type<recursive_wrapper<T>, Types...>::index != detail::invalid_value)>::type* = nullptr>
|
||||
VARIANT_INLINE T const& get() const
|
||||
{
|
||||
if (type_index == detail::direct_type<recursive_wrapper<T>, Types...>::index)
|
||||
{
|
||||
return (*reinterpret_cast<recursive_wrapper<T> const*>(&data)).get();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw bad_variant_access("in get<T>()");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// get_unchecked<T>() - T stored as std::reference_wrapper<T>
|
||||
template <typename T, typename std::enable_if<
|
||||
(detail::direct_type<std::reference_wrapper<T>, Types...>::index != detail::invalid_value)>::type* = nullptr>
|
||||
VARIANT_INLINE T& get_unchecked()
|
||||
{
|
||||
return (*reinterpret_cast<std::reference_wrapper<T>*>(&data)).get();
|
||||
}
|
||||
|
||||
#ifdef HAS_EXCEPTIONS
|
||||
// get<T>() - T stored as std::reference_wrapper<T>
|
||||
template <typename T, typename std::enable_if<
|
||||
(detail::direct_type<std::reference_wrapper<T>, Types...>::index != detail::invalid_value)>::type* = nullptr>
|
||||
VARIANT_INLINE T& get()
|
||||
{
|
||||
if (type_index == detail::direct_type<std::reference_wrapper<T>, Types...>::index)
|
||||
{
|
||||
return (*reinterpret_cast<std::reference_wrapper<T>*>(&data)).get();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw bad_variant_access("in get<T>()");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename T, typename std::enable_if<
|
||||
(detail::direct_type<std::reference_wrapper<T const>, Types...>::index != detail::invalid_value)>::type* = nullptr>
|
||||
VARIANT_INLINE T const& get_unchecked() const
|
||||
{
|
||||
return (*reinterpret_cast<std::reference_wrapper<T const> const*>(&data)).get();
|
||||
}
|
||||
|
||||
#ifdef HAS_EXCEPTIONS
|
||||
template <typename T, typename std::enable_if<
|
||||
(detail::direct_type<std::reference_wrapper<T const>, Types...>::index != detail::invalid_value)>::type* = nullptr>
|
||||
VARIANT_INLINE T const& get() const
|
||||
{
|
||||
if (type_index == detail::direct_type<std::reference_wrapper<T const>, Types...>::index)
|
||||
{
|
||||
return (*reinterpret_cast<std::reference_wrapper<T const> const*>(&data)).get();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw bad_variant_access("in get<T>()");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// This function is deprecated because it returns an internal index field.
|
||||
// Use which() instead.
|
||||
MAPBOX_VARIANT_DEPRECATED VARIANT_INLINE std::size_t get_type_index() const
|
||||
{
|
||||
return type_index;
|
||||
}
|
||||
|
||||
VARIANT_INLINE int which() const noexcept
|
||||
{
|
||||
return static_cast<int>(sizeof...(Types)-type_index - 1);
|
||||
}
|
||||
|
||||
template <typename T, typename std::enable_if<
|
||||
(detail::direct_type<T, Types...>::index != detail::invalid_value)>::type* = nullptr>
|
||||
VARIANT_INLINE static constexpr int which() noexcept
|
||||
{
|
||||
return static_cast<int>(sizeof...(Types)-detail::direct_type<T, Types...>::index - 1);
|
||||
}
|
||||
|
||||
// visitor
|
||||
// unary
|
||||
template <typename F, typename V, typename R = typename detail::result_of_unary_visit<F, first_type>::type>
|
||||
auto VARIANT_INLINE static visit(V const& v, F&& f)
|
||||
-> decltype(detail::dispatcher<F, V, R, Types...>::apply_const(v, std::forward<F>(f)))
|
||||
{
|
||||
return detail::dispatcher<F, V, R, Types...>::apply_const(v, std::forward<F>(f));
|
||||
}
|
||||
// non-const
|
||||
template <typename F, typename V, typename R = typename detail::result_of_unary_visit<F, first_type>::type>
|
||||
auto VARIANT_INLINE static visit(V& v, F&& f)
|
||||
-> decltype(detail::dispatcher<F, V, R, Types...>::apply(v, std::forward<F>(f)))
|
||||
{
|
||||
return detail::dispatcher<F, V, R, Types...>::apply(v, std::forward<F>(f));
|
||||
}
|
||||
|
||||
// binary
|
||||
// const
|
||||
template <typename F, typename V, typename R = typename detail::result_of_binary_visit<F, first_type>::type>
|
||||
auto VARIANT_INLINE static binary_visit(V const& v0, V const& v1, F&& f)
|
||||
-> decltype(detail::binary_dispatcher<F, V, R, Types...>::apply_const(v0, v1, std::forward<F>(f)))
|
||||
{
|
||||
return detail::binary_dispatcher<F, V, R, Types...>::apply_const(v0, v1, std::forward<F>(f));
|
||||
}
|
||||
// non-const
|
||||
template <typename F, typename V, typename R = typename detail::result_of_binary_visit<F, first_type>::type>
|
||||
auto VARIANT_INLINE static binary_visit(V& v0, V& v1, F&& f)
|
||||
-> decltype(detail::binary_dispatcher<F, V, R, Types...>::apply(v0, v1, std::forward<F>(f)))
|
||||
{
|
||||
return detail::binary_dispatcher<F, V, R, Types...>::apply(v0, v1, std::forward<F>(f));
|
||||
}
|
||||
|
||||
~variant() noexcept // no-throw destructor
|
||||
{
|
||||
helper_type::destroy(type_index, &data);
|
||||
}
|
||||
|
||||
// comparison operators
|
||||
// equality
|
||||
VARIANT_INLINE bool operator==(variant const& rhs) const
|
||||
{
|
||||
assert(valid() && rhs.valid());
|
||||
if (this->which() != rhs.which())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
detail::comparer<variant, detail::equal_comp> visitor(*this);
|
||||
return visit(rhs, visitor);
|
||||
}
|
||||
|
||||
VARIANT_INLINE bool operator!=(variant const& rhs) const
|
||||
{
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
// less than
|
||||
VARIANT_INLINE bool operator<(variant const& rhs) const
|
||||
{
|
||||
assert(valid() && rhs.valid());
|
||||
if (this->which() != rhs.which())
|
||||
{
|
||||
return this->which() < rhs.which();
|
||||
}
|
||||
detail::comparer<variant, detail::less_comp> visitor(*this);
|
||||
return visit(rhs, visitor);
|
||||
}
|
||||
VARIANT_INLINE bool operator>(variant const& rhs) const
|
||||
{
|
||||
return rhs < *this;
|
||||
}
|
||||
VARIANT_INLINE bool operator<=(variant const& rhs) const
|
||||
{
|
||||
return !(*this > rhs);
|
||||
}
|
||||
VARIANT_INLINE bool operator>=(variant const& rhs) const
|
||||
{
|
||||
return !(*this < rhs);
|
||||
}
|
||||
};
|
||||
|
||||
// unary visitor interface
|
||||
// const
|
||||
template <typename F, typename V>
|
||||
auto VARIANT_INLINE apply_visitor(F&& f, V const& v) -> decltype(V::visit(v, std::forward<F>(f)))
|
||||
{
|
||||
return V::visit(v, std::forward<F>(f));
|
||||
}
|
||||
|
||||
// non-const
|
||||
template <typename F, typename V>
|
||||
auto VARIANT_INLINE apply_visitor(F&& f, V& v) -> decltype(V::visit(v, std::forward<F>(f)))
|
||||
{
|
||||
return V::visit(v, std::forward<F>(f));
|
||||
}
|
||||
|
||||
// binary visitor interface
|
||||
// const
|
||||
template <typename F, typename V>
|
||||
auto VARIANT_INLINE apply_visitor(F&& f, V const& v0, V const& v1) -> decltype(V::binary_visit(v0, v1, std::forward<F>(f)))
|
||||
{
|
||||
return V::binary_visit(v0, v1, std::forward<F>(f));
|
||||
}
|
||||
|
||||
// non-const
|
||||
template <typename F, typename V>
|
||||
auto VARIANT_INLINE apply_visitor(F&& f, V& v0, V& v1) -> decltype(V::binary_visit(v0, v1, std::forward<F>(f)))
|
||||
{
|
||||
return V::binary_visit(v0, v1, std::forward<F>(f));
|
||||
}
|
||||
|
||||
// getter interface
|
||||
|
||||
#ifdef HAS_EXCEPTIONS
|
||||
template <typename ResultType, typename T>
|
||||
auto get(T& var)->decltype(var.template get<ResultType>())
|
||||
{
|
||||
return var.template get<ResultType>();
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename ResultType, typename T>
|
||||
ResultType& get_unchecked(T& var)
|
||||
{
|
||||
return var.template get_unchecked<ResultType>();
|
||||
}
|
||||
|
||||
#ifdef HAS_EXCEPTIONS
|
||||
template <typename ResultType, typename T>
|
||||
auto get(T const& var)->decltype(var.template get<ResultType>())
|
||||
{
|
||||
return var.template get<ResultType>();
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename ResultType, typename T>
|
||||
ResultType const& get_unchecked(T const& var)
|
||||
{
|
||||
return var.template get_unchecked<ResultType>();
|
||||
}
|
||||
}
|
||||
// } // namespace util
|
||||
//} // namespace mapbox
|
||||
|
||||
#endif // MAPBOX_UTIL_VARIANT_HPP
|
||||
+17
-3
@@ -14,12 +14,26 @@ namespace utils
|
||||
case version_type::release: return "Release";
|
||||
}
|
||||
|
||||
return "Unknown";
|
||||
throw;
|
||||
}
|
||||
|
||||
uint version::to_hex() const
|
||||
version::version(std::uint8_t hi, std::uint8_t mid, std::uint8_t lo)
|
||||
: m_hi(hi)
|
||||
, m_mid(mid)
|
||||
, m_lo(lo)
|
||||
{
|
||||
return (m_hi << 24) | (m_mid << 16) | (m_lo << 8) | ((uint(m_type) & 0xf) << 4) | (m_type_index & 0xf);
|
||||
}
|
||||
|
||||
version& version::type(version_type type, std::uint8_t type_index)
|
||||
{
|
||||
m_type = type;
|
||||
m_type_index = type_index;
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::uint16_t version::to_hex() const
|
||||
{
|
||||
return (m_hi << 24) | (m_mid << 16) | (m_lo << 8) | ((std::uint8_t(m_type) & 0xf) << 4) | (m_type_index & 0xf);
|
||||
}
|
||||
|
||||
std::string version::to_string() const
|
||||
|
||||
+21
-22
@@ -1,11 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
|
||||
namespace utils
|
||||
{
|
||||
enum class version_type : uint
|
||||
enum class version_type : std::uint8_t
|
||||
{
|
||||
pre_alpha,
|
||||
alpha,
|
||||
@@ -18,35 +17,29 @@ namespace utils
|
||||
|
||||
class version
|
||||
{
|
||||
uint m_hi;
|
||||
uint m_mid;
|
||||
uint m_lo;
|
||||
std::uint8_t m_hi;
|
||||
std::uint8_t m_mid;
|
||||
std::uint8_t m_lo;
|
||||
version_type m_type = version_type::release;
|
||||
uint m_type_index = 1;
|
||||
const char* m_postfix;
|
||||
std::uint8_t m_type_index = 1;
|
||||
std::string m_postfix;
|
||||
|
||||
public:
|
||||
constexpr version(uint hi, uint mid, uint lo, version_type type, uint type_index, const char* postfix)
|
||||
: m_hi(hi)
|
||||
, m_mid(mid)
|
||||
, m_lo(lo)
|
||||
, m_type(type)
|
||||
, m_type_index(type_index)
|
||||
, m_postfix(postfix)
|
||||
{
|
||||
}
|
||||
version(std::uint8_t hi, std::uint8_t mid, std::uint8_t lo = 0);
|
||||
|
||||
uint hi() const
|
||||
version& type(version_type type, std::uint8_t type_index = 1);
|
||||
|
||||
std::uint8_t hi() const
|
||||
{
|
||||
return m_hi;
|
||||
}
|
||||
|
||||
uint mid() const
|
||||
std::uint8_t mid() const
|
||||
{
|
||||
return m_mid;
|
||||
}
|
||||
|
||||
uint lo() const
|
||||
std::uint8_t lo() const
|
||||
{
|
||||
return m_lo;
|
||||
}
|
||||
@@ -61,12 +54,18 @@ namespace utils
|
||||
return m_postfix;
|
||||
}
|
||||
|
||||
uint type_index() const
|
||||
version& postfix(const std::string& value)
|
||||
{
|
||||
m_postfix = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::uint8_t type_index() const
|
||||
{
|
||||
return m_type_index;
|
||||
}
|
||||
|
||||
uint to_hex() const;
|
||||
std::uint16_t to_hex() const;
|
||||
std::string to_string() const;
|
||||
};
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+1
-1
Submodule Utilities/yaml-cpp updated: 017626a6ce...bc71686835
@@ -19,7 +19,7 @@
|
||||
<PropertyGroup Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
@@ -54,7 +54,6 @@
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\emitterutils.cpp">
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\exceptions.cpp" />
|
||||
<ClCompile Include="yaml-cpp\src\exp.cpp">
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\memory.cpp">
|
||||
|
||||
@@ -7,88 +7,85 @@
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="yaml-cpp\src\binary.cpp">
|
||||
<ClCompile Include="src\binary.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\convert.cpp">
|
||||
<ClCompile Include="src\convert.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\directives.cpp">
|
||||
<ClCompile Include="src\directives.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\emit.cpp">
|
||||
<ClCompile Include="src\emit.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\emitfromevents.cpp">
|
||||
<ClCompile Include="src\emitfromevents.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\emitter.cpp">
|
||||
<ClCompile Include="src\emitter.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\emitterstate.cpp">
|
||||
<ClCompile Include="src\emitterstate.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\emitterutils.cpp">
|
||||
<ClCompile Include="src\emitterutils.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\exp.cpp">
|
||||
<ClCompile Include="src\exp.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\memory.cpp">
|
||||
<ClCompile Include="src\memory.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\node.cpp">
|
||||
<ClCompile Include="src\node.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\nodebuilder.cpp">
|
||||
<ClCompile Include="src\node_data.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\nodeevents.cpp">
|
||||
<ClCompile Include="src\nodebuilder.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\node_data.cpp">
|
||||
<ClCompile Include="src\nodeevents.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\null.cpp">
|
||||
<ClCompile Include="src\null.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\ostream_wrapper.cpp">
|
||||
<ClCompile Include="src\ostream_wrapper.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\parse.cpp">
|
||||
<ClCompile Include="src\parse.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\parser.cpp">
|
||||
<ClCompile Include="src\parser.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\regex_yaml.cpp">
|
||||
<ClCompile Include="src\regex_yaml.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\scanner.cpp">
|
||||
<ClCompile Include="src\scanner.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\scanscalar.cpp">
|
||||
<ClCompile Include="src\scanscalar.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\scantag.cpp">
|
||||
<ClCompile Include="src\scantag.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\scantoken.cpp">
|
||||
<ClCompile Include="src\scantoken.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\simplekey.cpp">
|
||||
<ClCompile Include="src\simplekey.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\singledocparser.cpp">
|
||||
<ClCompile Include="src\singledocparser.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\stream.cpp">
|
||||
<ClCompile Include="src\stream.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\tag.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\exceptions.cpp">
|
||||
<ClCompile Include="src\tag.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup />
|
||||
</Project>
|
||||
@@ -1,6 +1,8 @@
|
||||
if(WIN32 AND NOT VULKAN_PREBUILT)
|
||||
if(APPLE)
|
||||
else()
|
||||
set(BUILD_TESTS OFF CACHE BOOL "Build tests" FORCE)
|
||||
set(BUILD_DEMOS OFF CACHE BOOL "Build demos" FORCE)
|
||||
# TravisCI break build with layers and vkjson
|
||||
set(BUILD_LAYERS OFF CACHE BOOL "Build demos" FORCE)
|
||||
set(BUILD_VKJSON OFF CACHE BOOL "Build demos" FORCE)
|
||||
add_subdirectory( Vulkan-LoaderAndValidationLayers )
|
||||
|
||||
Submodule Vulkan/Vulkan-LoaderAndValidationLayers updated: 115665a3b5...1affe90f0e
@@ -19,12 +19,12 @@
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Makefile</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Makefile</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
@@ -39,21 +39,24 @@
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<NMakeBuildCommandLine>cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DBUILD_TESTS=OFF -DBUILD_DEMOS=OFF -DBUILD_LAYERS=OFF -DBUILD_VKJSON=OFF ../Vulkan-LoaderAndValidationLayers
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:build /p:Configuration=Release /m</NMakeBuildCommandLine>
|
||||
<NMakeCleanCommandLine>cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DBUILD_TESTS=OFF -DBUILD_DEMOS=OFF -DBUILD_LAYERS=OFF -DBUILD_VKJSON=OFF ../Vulkan-LoaderAndValidationLayers
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:clean /p:Configuration=Release /m</NMakeCleanCommandLine>
|
||||
<NMakeReBuildCommandLine>cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DBUILD_TESTS=OFF -DBUILD_DEMOS=OFF -DBUILD_LAYERS=OFF -DBUILD_VKJSON=OFF ../Vulkan-LoaderAndValidationLayers
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:rebuild /p:Configuration=Release /m
|
||||
<NMakeBuildCommandLine>cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DBUILD_TESTS=OFF -DBUILD_DEMOS=OFF ../Vulkan-LoaderAndValidationLayers
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:build /p:Configuration=Release
|
||||
</NMakeBuildCommandLine>
|
||||
<NMakeCleanCommandLine>cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DBUILD_TESTS=OFF -DBUILD_DEMOS=OFF ../Vulkan-LoaderAndValidationLayers
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:clean /p:Configuration=Release</NMakeCleanCommandLine>
|
||||
<NMakeReBuildCommandLine>cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DBUILD_TESTS=OFF -DBUILD_DEMOS=OFF ../Vulkan-LoaderAndValidationLayers
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:rebuild /p:Configuration=Release
|
||||
</NMakeReBuildCommandLine>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<NMakeBuildCommandLine>cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DBUILD_TESTS=OFF -DBUILD_DEMOS=OFF -DBUILD_LAYERS=OFF -DBUILD_VKJSON=OFF ../Vulkan-LoaderAndValidationLayers
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:build /p:Configuration=Debug /m</NMakeBuildCommandLine>
|
||||
<NMakeReBuildCommandLine>cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DBUILD_TESTS=OFF -DBUILD_DEMOS=OFF -DBUILD_LAYERS=OFF -DBUILD_VKJSON=OFF ../Vulkan-LoaderAndValidationLayers
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:rebuild /p:Configuration=Debug /m</NMakeReBuildCommandLine>
|
||||
<NMakeCleanCommandLine>cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DBUILD_TESTS=OFF -DBUILD_DEMOS=OFF -DBUILD_LAYERS=OFF -DBUILD_VKJSON=OFF ../Vulkan-LoaderAndValidationLayers
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:clean /p:Configuration=Debug /m</NMakeCleanCommandLine>
|
||||
<NMakeBuildCommandLine>cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DBUILD_TESTS=OFF -DBUILD_DEMOS=OFF ../Vulkan-LoaderAndValidationLayers
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:build /p:Configuration=Debug
|
||||
</NMakeBuildCommandLine>
|
||||
<NMakeReBuildCommandLine>cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DBUILD_TESTS=OFF -DBUILD_DEMOS=OFF ../Vulkan-LoaderAndValidationLayers
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:rebuild /p:Configuration=Debug
|
||||
</NMakeReBuildCommandLine>
|
||||
<NMakeCleanCommandLine>cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DBUILD_TESTS=OFF -DBUILD_DEMOS=OFF ../Vulkan-LoaderAndValidationLayers
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:clean /p:Configuration=Debug</NMakeCleanCommandLine>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
+1
-1
Submodule Vulkan/glslang updated: 0e07119ae2...3c5b1e6b31
@@ -19,12 +19,12 @@
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Makefile</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Makefile</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
@@ -39,25 +39,25 @@
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<NMakeBuildCommandLine>cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" ../glslang
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:build /p:Configuration=Release /m</NMakeBuildCommandLine>
|
||||
<NMakeReBuildCommandLine>cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" ../glslang
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:rebuild /p:Configuration=Release /m</NMakeReBuildCommandLine>
|
||||
<NMakeCleanCommandLine>cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" ../glslang
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:clean /p:Configuration=Release /m</NMakeCleanCommandLine>
|
||||
<NMakeBuildCommandLine>cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" ../glslang
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:build /p:Configuration=Release</NMakeBuildCommandLine>
|
||||
<NMakeReBuildCommandLine>cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" ../glslang
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:rebuild /p:Configuration=Release</NMakeReBuildCommandLine>
|
||||
<NMakeCleanCommandLine>cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" ../glslang
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:clean /p:Configuration=Release</NMakeCleanCommandLine>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<NMakeBuildCommandLine>
|
||||
cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" ../glslang
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:build /p:Configuration=Debug /m
|
||||
cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" ../glslang
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:build /p:Configuration=Debug
|
||||
</NMakeBuildCommandLine>
|
||||
<NMakeReBuildCommandLine>
|
||||
cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" ../glslang
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:rebuild /p:Configuration=Debug /m
|
||||
cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" ../glslang
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:rebuild /p:Configuration=Debug
|
||||
</NMakeReBuildCommandLine>
|
||||
<NMakeCleanCommandLine>
|
||||
cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" ../glslang
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:clean /p:Configuration=Debug /m
|
||||
cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" ../glslang
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:clean /p:Configuration=Debug
|
||||
</NMakeCleanCommandLine>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup>
|
||||
|
||||
+18
-25
@@ -1,41 +1,34 @@
|
||||
version: '0.0.2-{build}'
|
||||
|
||||
os: Visual Studio 2017
|
||||
version: 1.0.{build}
|
||||
os: Visual Studio 2015
|
||||
configuration:
|
||||
# - Release
|
||||
- ReleaseLLVM
|
||||
platform: x64
|
||||
clone_folder: C:\rpcs3
|
||||
clone_folder: c:\rpcs3
|
||||
clone_depth: 3
|
||||
test: off
|
||||
|
||||
configuration:
|
||||
# Release
|
||||
- ReleaseLLVM
|
||||
|
||||
before_build:
|
||||
- ps: $env:Date="$(git show -s --date=short --format='%ad')"
|
||||
- git submodule update --init 3rdparty/ffmpeg 3rdparty/pugixml asmjit 3rdparty/GSL 3rdparty/libpng Vulkan/glslang Vulkan/Vulkan-LoaderAndValidationLayers Utilities/yaml-cpp 3rdparty/cereal 3rdparty/zlib 3rdparty/hidapi
|
||||
# until git for win 2.5 release with commit checkout
|
||||
- git submodule update --init 3rdparty/ffmpeg 3rdparty/pugixml asmjit 3rdparty/GSL 3rdparty/libpng Vulkan/glslang Vulkan/Vulkan-LoaderAndValidationLayers Utilities/yaml-cpp rsx_program_decompiler
|
||||
- 7z x wxWidgets.7z -aos -oC:\rpcs3\wxWidgets > null
|
||||
- 7z x zlib.7z -aos -oC:\rpcs3\ > null
|
||||
- 7z x vulkan.7z -aos -oC:\rpcs3\Vulkan > null
|
||||
- if %configuration%==Release (cmake -G "Visual Studio 15 Win64" -DZLIB_ROOT=C:/rpcs3/zlib/ -DVULKAN_PREBUILT=ON)
|
||||
else (7z x llvmlibs.7z -aos -oC:\rpcs3 > null && cmake -G "Visual Studio 15 Win64" -DLLVM_DIR=C:/rpcs3/llvm_build/lib/cmake/llvm -DZLIB_ROOT=C:/rpcs3/zlib/ -DVULKAN_PREBUILT=ON)
|
||||
- if %configuration%==Release (cmake -G "Visual Studio 14 Win64" -DZLIB_ROOT=C:/rpcs3/zlib/)
|
||||
else (7z x llvmlibs.7z -aos -oC:\rpcs3 > null && cmake -G "Visual Studio 14 Win64" -DLLVM_DIR=C:/rpcs3/llvm_build/share/llvm/cmake -DZLIB_ROOT=C:/rpcs3/zlib/)
|
||||
|
||||
build_script:
|
||||
- cmake --build . --config Release -- /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
|
||||
|
||||
install:
|
||||
- if not exist llvmlibs.7z appveyor DownloadFile "https://drive.google.com/uc?export=download&id=0B8A6NaxhQAGRY2k3Q2Yya05lcm8" -FileName llvmlibs.7z
|
||||
- if not exist vulkan.7z appveyor DownloadFile "https://drive.google.com/uc?export=download&id=0B8A6NaxhQAGRaHYxV3VOS2d0a0U" -FileName vulkan.7z
|
||||
- if not exist zlib.7z appveyor DownloadFile "https://drive.google.com/uc?export=download&id=0B-HVE3xvheVFX05lRFdnZlh5aUU" -FileName zlib.7z
|
||||
- set QTDIR=C:\Qt\5.9\msvc2017_64
|
||||
- ps: Start-FileDownload 'https://402331b94f8e4b87ae2ef4677347f7956cf3861f.googledrive.com/host/0B6v_qtb9hkicfmt0NG0wTTRtUmF4X3VTQk5Oc2JidEVKVnUteDA1dXdrYlNsVW9kREpsSHc/wxWidgets.7z'
|
||||
# - ps: Start-FileDownload 'https://402331b94f8e4b87ae2ef4677347f7956cf3861f.googledrive.com/host/0B6v_qtb9hkicfmt0NG0wTTRtUmF4X3VTQk5Oc2JidEVKVnUteDA1dXdrYlNsVW9kREpsSHc/llvmlibs.7z'
|
||||
- ps: Start-FileDownload 'https://drive.google.com/uc?export=download&id=0B-98fOyaZKJ5YWVnb29JZXFQWkU' -FileName llvmlibs.7z
|
||||
- ps: Start-FileDownload 'https://402331b94f8e4b87ae2ef4677347f7956cf3861f.googledrive.com/host/0B6v_qtb9hkicfmt0NG0wTTRtUmF4X3VTQk5Oc2JidEVKVnUteDA1dXdrYlNsVW9kREpsSHc/zlib.7z'
|
||||
- set WXWIN=C:\rpcs3\wxWidgets
|
||||
- set OPENALDIR=C:\rpcs3\3rdparty\OpenAL
|
||||
- set PATH=C:\Program Files (x86)\MSBuild\15.0\Bin;%QTDIR%;%PATH%
|
||||
- set PATH=C:\Program Files (x86)\MSBuild\14.0\Bin;C:\wxWidgets;%PATH%
|
||||
- set COMMIT_SHA=%APPVEYOR_REPO_COMMIT:~0,8%
|
||||
|
||||
artifacts:
|
||||
- path: bin
|
||||
name: 'rpcs3-v0.0.2-$(Date)-$(COMMIT_SHA)_win64'
|
||||
type: zip
|
||||
|
||||
cache:
|
||||
- llvmlibs.7z -> appveyor.yml
|
||||
- vulkan.7z -> appveyor.yml
|
||||
- zlib.7z -> appveyor.yml
|
||||
name: rpcs3-$(configuration)-$(COMMIT_SHA)
|
||||
|
||||
+1
-1
Submodule asmjit updated: 1370fe6a26...b0dad1af25
+22
-29
@@ -23,53 +23,47 @@
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\arch.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\assembler.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\codebuilder.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\codecompiler.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\codeemitter.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\codeholder.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\codegen.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\compiler.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\constpool.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\containers.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\context.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\cpuinfo.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\func.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\cputicks.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\error.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\globals.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\inst.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\logging.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\osutils.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\intutil.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\logger.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\operand.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\regalloc.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\runtime.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\string.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\vmem.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\zone.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\x86\x86assembler.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\x86\x86compiler.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\x86\x86context.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\x86\x86cpuinfo.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\x86\x86inst.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\x86\x86instimpl.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\x86\x86internal.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\x86\x86logging.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\x86\x86operand.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\x86\x86operand_regs.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\x86\x86regalloc.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\x86\x86scheduler.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\arch.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\assembler.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\codebuilder.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\codecompiler.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\codeemitter.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\codeholder.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\codegen.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\compiler.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\constpool.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\containers.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\context_p.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\cpuinfo.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\func.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\logging.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\cputicks.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\error.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\globals.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\inst.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\intutil.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\lock.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\logger.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\operand.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\osutils.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\regalloc_p.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\runtime.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\string.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\vectypes.h" />
|
||||
@@ -77,12 +71,11 @@
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\zone.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\x86\x86assembler.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\x86\x86compiler.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\x86\x86context_p.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\x86\x86cpuinfo.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\x86\x86inst.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\x86\x86instimpl_p.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\x86\x86internal_p.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\x86\x86logging_p.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\x86\x86operand.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\x86\x86regalloc_p.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\x86\x86scheduler_p.h" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{AC40FF01-426E-4838-A317-66354CEFAE88}</ProjectGuid>
|
||||
@@ -93,7 +86,7 @@
|
||||
<PropertyGroup Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
|
||||
@@ -1,65 +1,58 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\arch.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\assembler.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\codebuilder.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\codecompiler.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\codeemitter.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\codeholder.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\codegen.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\compiler.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\constpool.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\containers.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\context.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\cpuinfo.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\func.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\cputicks.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\error.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\globals.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\inst.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\logging.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\osutils.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\intutil.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\logger.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\operand.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\regalloc.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\runtime.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\string.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\vmem.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\base\zone.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\x86\x86assembler.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\x86\x86compiler.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\x86\x86context.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\x86\x86cpuinfo.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\x86\x86inst.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\x86\x86instimpl.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\x86\x86internal.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\x86\x86logging.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\x86\x86operand.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\x86\x86operand_regs.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\x86\x86regalloc.cpp" />
|
||||
<ClCompile Include="..\asmjit\src\asmjit\x86\x86scheduler.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\arch.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\x86\x86assembler.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\x86\x86compiler.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\x86\x86context_p.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\x86\x86cpuinfo.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\x86\x86inst.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\x86\x86operand.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\x86\x86scheduler_p.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\assembler.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\codebuilder.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\codecompiler.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\codeemitter.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\codeholder.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\codegen.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\compiler.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\constpool.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\containers.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\context_p.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\cpuinfo.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\func.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\logging.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\cputicks.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\error.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\globals.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\inst.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\intutil.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\lock.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\logger.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\operand.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\osutils.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\regalloc_p.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\runtime.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\string.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\vectypes.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\vmem.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\base\zone.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\x86\x86assembler.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\x86\x86compiler.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\x86\x86inst.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\x86\x86instimpl_p.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\x86\x86internal_p.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\x86\x86logging_p.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\x86\x86operand.h" />
|
||||
<ClInclude Include="..\asmjit\src\asmjit\x86\x86regalloc_p.h" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup />
|
||||
</Project>
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -2,3 +2,5 @@
|
||||
*
|
||||
# Except this file
|
||||
!.gitignore
|
||||
!cache
|
||||
!cache/README.txt
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
This directory is used for cache functionality, so don't store any data here.
|
||||
@@ -0,0 +1 @@
|
||||
This directory is used for temporary gamedata functionality, so don't store any data here.
|
||||
@@ -1,4 +1,4 @@
|
||||
# Ignore everything in this directory
|
||||
*
|
||||
# Except this file
|
||||
!.gitignore
|
||||
!.gitignore
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user