mirror of
https://github.com/xmrig/xmrig.git
synced 2025-12-06 23:52:38 -05:00
Compare commits
156 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
19f0476efb | ||
|
|
edf7885172 | ||
|
|
6cd7f3e053 | ||
|
|
f1ae81c6ae | ||
|
|
8cbf90d35b | ||
|
|
48eaf11026 | ||
|
|
75f18c9b31 | ||
|
|
302fe70f6b | ||
|
|
a2a0defeef | ||
|
|
e2ea11ffeb | ||
|
|
d8f9501ac8 | ||
|
|
12a1365b5d | ||
|
|
8f3a2a63ba | ||
|
|
f7f07ce42c | ||
|
|
c1d99bfa09 | ||
|
|
be8245fc92 | ||
|
|
926871cbe1 | ||
|
|
ee677ef5c9 | ||
|
|
c10ec90b60 | ||
|
|
0d3c2752c9 | ||
|
|
eaa44a1547 | ||
|
|
89454c6d30 | ||
|
|
d3f2184fcc | ||
|
|
19da03c9b7 | ||
|
|
aa284c6a3a | ||
|
|
6379d1f90e | ||
|
|
8737af0f6f | ||
|
|
9a1e867da2 | ||
|
|
be979d35c7 | ||
|
|
971abe536c | ||
|
|
5ceacbbfd0 | ||
|
|
c18a0152dd | ||
|
|
3bbe3fa481 | ||
|
|
4d6b384c16 | ||
|
|
e4283d5f53 | ||
|
|
3b6cfd9c4f | ||
|
|
a076f739e3 | ||
|
|
4cdea633bf | ||
|
|
e3727f01b8 | ||
|
|
837bd1a43c | ||
|
|
eb36d2beef | ||
|
|
f08887180d | ||
|
|
95d3293f4b | ||
|
|
44054ac7eb | ||
|
|
057fbf7608 | ||
|
|
a64ff6b7c7 | ||
|
|
673c366f77 | ||
|
|
c8c0abdb00 | ||
|
|
0a183a59c0 | ||
|
|
3f7cf2ac18 | ||
|
|
d97b5a7552 | ||
|
|
cbd0c45c2b | ||
|
|
e5fd83554b | ||
|
|
c9e17780e7 | ||
|
|
5df1686810 | ||
|
|
1e3e8ff8ee | ||
|
|
d4750239ea | ||
|
|
99e9073993 | ||
|
|
51690ebad6 | ||
|
|
5ac908c027 | ||
|
|
f1a24b7ddd | ||
|
|
f977b31331 | ||
|
|
80b980c9d3 | ||
|
|
ddb4f9be76 | ||
|
|
a87d9d31e2 | ||
|
|
6c26e04fbe | ||
|
|
eb95d0339e | ||
|
|
e295a938f8 | ||
|
|
bccffa63a4 | ||
|
|
ca6ca4cb67 | ||
|
|
3910cf9e69 | ||
|
|
5d274777f6 | ||
|
|
dad1fdb505 | ||
|
|
565d36d9e4 | ||
|
|
e32e22474a | ||
|
|
905713f1ca | ||
|
|
5f314edb2f | ||
|
|
315d74c319 | ||
|
|
6b7b3511ce | ||
|
|
50bdaba526 | ||
|
|
4914fefb1f | ||
|
|
03cd56ed73 | ||
|
|
79c96418c7 | ||
|
|
36c1cb23e0 | ||
|
|
6dba0635f1 | ||
|
|
99b58580e9 | ||
|
|
027a6f8ae2 | ||
|
|
a3daaf09f5 | ||
|
|
da8b87b007 | ||
|
|
87b4d97798 | ||
|
|
6860450147 | ||
|
|
b0de5aefb1 | ||
|
|
d2e2f5f800 | ||
|
|
3088f915f9 | ||
|
|
4c5421b2bf | ||
|
|
6dd281b508 | ||
|
|
599958c982 | ||
|
|
328f985e07 | ||
|
|
7fc7b976bf | ||
|
|
36b1523194 | ||
|
|
5155139e9a | ||
|
|
a152d6be42 | ||
|
|
ccebf6bb20 | ||
|
|
5b4648339a | ||
|
|
7727014eea | ||
|
|
8c45e3226d | ||
|
|
75403ee275 | ||
|
|
c4db1435b2 | ||
|
|
f3ea3c5227 | ||
|
|
722e468bd9 | ||
|
|
9569772e7e | ||
|
|
144f9c4409 | ||
|
|
2ecece7b3d | ||
|
|
677d287135 | ||
|
|
62eb66486d | ||
|
|
da03d74ade | ||
|
|
9fcc542676 | ||
|
|
581d004568 | ||
|
|
4f7186cb0e | ||
|
|
65fa1d9bf3 | ||
|
|
f85efd163c | ||
|
|
eb8cf3ee5a | ||
|
|
793a2454ad | ||
|
|
4a74ce3242 | ||
|
|
87a54766eb | ||
|
|
22a69f70da | ||
|
|
3fbf2ac3d4 | ||
|
|
0a2fe5caa7 | ||
|
|
17795e3d7b | ||
|
|
1fdc8631e3 | ||
|
|
858463ceba | ||
|
|
a4550f55ea | ||
|
|
d9b6f46a6a | ||
|
|
4bac3e7695 | ||
|
|
59bd6d4187 | ||
|
|
166c011d37 | ||
|
|
1f55c6eb02 | ||
|
|
c2bdae70fe | ||
|
|
1289942567 | ||
|
|
44dcded866 | ||
|
|
8deb247b3e | ||
|
|
a705ab775b | ||
|
|
bfd5a81937 | ||
|
|
c710ee5fb5 | ||
|
|
a8466a139c | ||
|
|
ba47219185 | ||
|
|
cf54c85b76 | ||
|
|
fa5b872782 | ||
|
|
3ee0cd8c51 | ||
|
|
7bdeba4d08 | ||
|
|
116fb3d3f9 | ||
|
|
54a17a75ab | ||
|
|
5f0f2506e8 | ||
|
|
31e896feef | ||
|
|
8bfd7bcf05 | ||
|
|
ebf259fa7c |
47
CHANGELOG.md
47
CHANGELOG.md
@@ -1,3 +1,50 @@
|
||||
# v6.6.0
|
||||
- Online benchmark protocol upgraded to v2, validation not compatible with previous versions.
|
||||
- Single thread benchmark now is cheat-resistant, not possible speedup it with multiple threads.
|
||||
- RandomX dataset is now always initialized with static seed, to prevent time cheat by report slow dataset initialization.
|
||||
- Zero delay online submission, to make time validation much more precise and strict.
|
||||
- DNS cache for online benchmark to prevent unexpected delays.
|
||||
|
||||
# v6.5.3
|
||||
- [#1946](https://github.com/xmrig/xmrig/pull/1946) Fixed MSR mod names in JSON API (v6.5.2 affected).
|
||||
|
||||
# v6.5.2
|
||||
- [#1935](https://github.com/xmrig/xmrig/pull/1935) Separate MSR mod for Zen/Zen2 and Zen3.
|
||||
- [#1937](https://github.com/xmrig/xmrig/issues/1937) Print path to existing WinRing0 service without verbose option.
|
||||
- [#1939](https://github.com/xmrig/xmrig/pull/1939) Fixed build with gcc 4.8.
|
||||
- [#1941](https://github.com/xmrig/xmrig/pull/1941) Added CPUID info to JSON report.
|
||||
- [#1941](https://github.com/xmrig/xmrig/pull/1942) Fixed alignment modification in memory pool.
|
||||
- [#1944](https://github.com/xmrig/xmrig/pull/1944) Updated `randomx_boost.sh` with new MSR mod.
|
||||
- Added `250K` and `500K` offline benchmarks.
|
||||
|
||||
# v6.5.1
|
||||
- [#1932](https://github.com/xmrig/xmrig/pull/1932) New MSR mod for Ryzen, up to +3.5% on Zen2 and +1-2% on Zen3.
|
||||
- [#1918](https://github.com/xmrig/xmrig/issues/1918) Fixed 1GB huge pages support on ARMv8.
|
||||
- [#1926](https://github.com/xmrig/xmrig/pull/1926) Fixed compilation on ARMv8 with GCC 9.3.0.
|
||||
- [#1929](https://github.com/xmrig/xmrig/issues/1929) Fixed build without HTTP.
|
||||
|
||||
# v6.5.0
|
||||
- **Added [online benchmark](https://xmrig.com/benchmark) mode for sharing results.**
|
||||
- Added new command line options: `--submit`, ` --verify=ID`, ` --seed=SEED`, `--hash=HASH`.
|
||||
- [#1912](https://github.com/xmrig/xmrig/pull/1912) Fixed MSR kernel module warning with new Linux kernels.
|
||||
- [#1925](https://github.com/xmrig/xmrig/pull/1925) Add checking for config files in user home directory.
|
||||
- Added vendor to ARM CPUs name and added `"arch"` field to API.
|
||||
- Removed legacy CUDA plugin API.
|
||||
|
||||
# v6.4.0
|
||||
- [#1862](https://github.com/xmrig/xmrig/pull/1862) **RandomX: removed `rx/loki` algorithm.**
|
||||
- [#1890](https://github.com/xmrig/xmrig/pull/1890) **Added `argon2/chukwav2` algorithm.**
|
||||
- [#1895](https://github.com/xmrig/xmrig/pull/1895) [#1897](https://github.com/xmrig/xmrig/pull/1897) **Added [benchmark and stress test](https://github.com/xmrig/xmrig/blob/dev/doc/BENCHMARK.md).**
|
||||
- [#1864](https://github.com/xmrig/xmrig/pull/1864) RandomX: improved software AES performance.
|
||||
- [#1870](https://github.com/xmrig/xmrig/pull/1870) RandomX: fixed unexpected resume due to disconnect during dataset init.
|
||||
- [#1872](https://github.com/xmrig/xmrig/pull/1872) RandomX: fixed `randomx_create_vm` call.
|
||||
- [#1875](https://github.com/xmrig/xmrig/pull/1875) RandomX: fixed crash on x86.
|
||||
- [#1876](https://github.com/xmrig/xmrig/pull/1876) RandomX: added `huge-pages-jit` config parameter.
|
||||
- [#1881](https://github.com/xmrig/xmrig/pull/1881) Fixed possible race condition in hashrate counting code.
|
||||
- [#1882](https://github.com/xmrig/xmrig/pull/1882) [#1886](https://github.com/xmrig/xmrig/pull/1886) [#1887](https://github.com/xmrig/xmrig/pull/1887) [#1893](https://github.com/xmrig/xmrig/pull/1893) General code improvements.
|
||||
- [#1885](https://github.com/xmrig/xmrig/pull/1885) Added more precise hashrate calculation.
|
||||
- [#1889](https://github.com/xmrig/xmrig/pull/1889) Fixed libuv performance issue on Linux.
|
||||
|
||||
# v6.3.5
|
||||
- [#1845](https://github.com/xmrig/xmrig/pull/1845) [#1861](https://github.com/xmrig/xmrig/pull/1861) Fixed ARM build and added CMake option `WITH_SSE4_1`.
|
||||
- [#1846](https://github.com/xmrig/xmrig/pull/1846) KawPow: fixed OpenCL memory leak.
|
||||
|
||||
@@ -25,6 +25,7 @@ option(WITH_STRICT_CACHE "Enable strict checks for OpenCL cache" ON)
|
||||
option(WITH_INTERLEAVE_DEBUG_LOG "Enable debug log for threads interleave" OFF)
|
||||
option(WITH_PROFILING "Enable profiling for developers" OFF)
|
||||
option(WITH_SSE4_1 "Enable SSE 4.1 for Blake2" ON)
|
||||
option(WITH_BENCHMARK "Enable builtin RandomX benchmark and stress test" ON)
|
||||
|
||||
option(BUILD_STATIC "Build static binary" OFF)
|
||||
option(ARM_TARGET "Force use specific ARM target 8 or 7" 0)
|
||||
@@ -208,8 +209,9 @@ add_executable(${CMAKE_PROJECT_NAME} ${HEADERS} ${SOURCES} ${SOURCES_OS} ${SOURC
|
||||
target_link_libraries(${CMAKE_PROJECT_NAME} ${XMRIG_ASM_LIBRARY} ${OPENSSL_LIBRARIES} ${UV_LIBRARIES} ${EXTRA_LIBS} ${CPUID_LIB} ${ARGON2_LIBRARY} ${ETHASH_LIBRARY})
|
||||
|
||||
if (WIN32)
|
||||
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_SOURCE_DIR}/bin/WinRing0/WinRing0x64.sys" $<TARGET_FILE_DIR:${CMAKE_PROJECT_NAME}>)
|
||||
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_SOURCE_DIR}/bin/WinRing0/WinRing0x64.sys" $<TARGET_FILE_DIR:${CMAKE_PROJECT_NAME}>)
|
||||
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_SOURCE_DIR}/scripts/benchmark_1M.cmd" $<TARGET_FILE_DIR:${CMAKE_PROJECT_NAME}>)
|
||||
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_SOURCE_DIR}/scripts/benchmark_10M.cmd" $<TARGET_FILE_DIR:${CMAKE_PROJECT_NAME}>)
|
||||
endif()
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES Clang AND CMAKE_BUILD_TYPE STREQUAL Release)
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
[](https://github.com/xmrig/xmrig/stargazers)
|
||||
[](https://github.com/xmrig/xmrig/network)
|
||||
|
||||
XMRig is a high performance, open source, cross platform RandomX, KawPow, CryptoNight and AstroBWT unified CPU/GPU miner. Official binaries are available for Windows, Linux, macOS and FreeBSD.
|
||||
XMRig is a high performance, open source, cross platform RandomX, KawPow, CryptoNight and AstroBWT unified CPU/GPU miner and [RandomX benchmark](https://xmrig.com/benchmark). Official binaries are available for Windows, Linux, macOS and FreeBSD.
|
||||
|
||||
## Mining backends
|
||||
- **CPU** (x64/ARMv8)
|
||||
|
||||
@@ -64,8 +64,8 @@ elseif (CMAKE_CXX_COMPILER_ID MATCHES MSVC)
|
||||
set(CMAKE_C_FLAGS_RELEASE "/MT /O2 /Oi /DNDEBUG /GL")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "/MT /O2 /Oi /DNDEBUG /GL")
|
||||
|
||||
set(CMAKE_C_FLAGS_RELWITHDEBINFO "/Ob1 /GL")
|
||||
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "/Ob1 /GL")
|
||||
set(CMAKE_C_FLAGS_RELWITHDEBINFO "/Ob1 /Zi /DRELWITHDEBINFO")
|
||||
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "/Ob1 /Zi /DRELWITHDEBINFO")
|
||||
|
||||
add_definitions(/D_CRT_SECURE_NO_WARNINGS)
|
||||
add_definitions(/D_CRT_NONSTDC_NO_WARNINGS)
|
||||
|
||||
@@ -62,6 +62,10 @@ if (WITH_RANDOMX)
|
||||
)
|
||||
# cheat because cmake and ccache hate each other
|
||||
set_property(SOURCE src/crypto/randomx/jit_compiler_a64_static.S PROPERTY LANGUAGE C)
|
||||
else()
|
||||
list(APPEND SOURCES_CRYPTO
|
||||
src/crypto/randomx/jit_compiler_fallback.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
if (WITH_SSE4_1)
|
||||
|
||||
29
doc/BENCHMARK.md
Normal file
29
doc/BENCHMARK.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# Embedded benchmark
|
||||
|
||||
You can run with XMRig with the following commands:
|
||||
```
|
||||
xmrig --bench=1M
|
||||
xmrig --bench=10M
|
||||
xmrig --bench=1M -a rx/wow
|
||||
xmrig --bench=10M -a rx/wow
|
||||
```
|
||||
This will run between 1 and 10 million RandomX hashes, depending on `bench` parameter, and print the time it took. First two commands use Monero variant (2 MB per thread, best for Zen2/Zen3 CPUs), second two commands use Wownero variant (1 MB per thread, useful for Intel and 1st gen Zen/Zen+ CPUs).
|
||||
|
||||
Checksum of all the hashes will be also printed to check stability of your hardware: if it's green then it's correct, if it's red then there was hardware error during computation. No Internet connection is required for the benchmark.
|
||||
|
||||
Double check that you see `Huge pages 100%` both for dataset and for all threads, and also check for `msr register values ... has been set successfully` - without this result will be far from the best. Running as administrator is required for MSR and huge pages to be set up properly.
|
||||
|
||||

|
||||
|
||||
### Benchmark with custom config
|
||||
|
||||
You can run benchmark with any configuration you want. Just start without command line parameteres, use regular config.json and add `"benchmark":"1M",` on the next line after pool url.
|
||||
|
||||
# Stress test
|
||||
|
||||
You can also run continuous stress-test that is as close to the real RandomX mining as possible and doesn't require any configuration:
|
||||
```
|
||||
xmrig --stress
|
||||
xmrig --stress -a rx/wow
|
||||
```
|
||||
This will require Internet connection and will run indefinitely.
|
||||
3
scripts/benchmark_10M.cmd
Normal file
3
scripts/benchmark_10M.cmd
Normal file
@@ -0,0 +1,3 @@
|
||||
@echo off
|
||||
xmrig.exe --bench=10M --submit
|
||||
pause
|
||||
3
scripts/benchmark_1M.cmd
Normal file
3
scripts/benchmark_1M.cmd
Normal file
@@ -0,0 +1,3 @@
|
||||
@echo off
|
||||
xmrig.exe --bench=1M --submit
|
||||
pause
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash -e
|
||||
|
||||
OPENSSL_VERSION="1.1.1g"
|
||||
OPENSSL_VERSION="1.1.1h"
|
||||
|
||||
mkdir -p deps
|
||||
mkdir -p deps/include
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash -e
|
||||
|
||||
UV_VERSION="1.38.1"
|
||||
UV_VERSION="1.40.0"
|
||||
|
||||
mkdir -p deps
|
||||
mkdir -p deps/include
|
||||
|
||||
@@ -49,7 +49,6 @@ function rx()
|
||||
'../cn/algorithm.cl',
|
||||
'randomx_constants_monero.h',
|
||||
'randomx_constants_wow.h',
|
||||
'randomx_constants_loki.h',
|
||||
'randomx_constants_arqma.h',
|
||||
'randomx_constants_keva.h',
|
||||
'aes.cl',
|
||||
|
||||
@@ -4,12 +4,22 @@ modprobe msr
|
||||
|
||||
if cat /proc/cpuinfo | grep "AMD Ryzen" > /dev/null;
|
||||
then
|
||||
echo "Detected Ryzen"
|
||||
wrmsr -a 0xc0011022 0x510000
|
||||
wrmsr -a 0xc001102b 0x1808cc16
|
||||
wrmsr -a 0xc0011020 0
|
||||
wrmsr -a 0xc0011021 0x40
|
||||
echo "MSR register values for Ryzen applied"
|
||||
if cat /proc/cpuinfo | grep "cpu family[[:space:]]:[[:space:]]25" > /dev/null;
|
||||
then
|
||||
echo "Detected Ryzen (Zen3)"
|
||||
wrmsr -a 0xc0011020 0x4480000000000
|
||||
wrmsr -a 0xc0011021 0x1c000200000040
|
||||
wrmsr -a 0xc0011022 0xc000000401500000
|
||||
wrmsr -a 0xc001102b 0x2000cc14
|
||||
echo "MSR register values for Ryzen (Zen3) applied"
|
||||
else
|
||||
echo "Detected Ryzen (Zen1/Zen2)"
|
||||
wrmsr -a 0xc0011020 0
|
||||
wrmsr -a 0xc0011021 0x40
|
||||
wrmsr -a 0xc0011022 0x1510000
|
||||
wrmsr -a 0xc001102b 0x2000cc16
|
||||
echo "MSR register values for Ryzen (Zen1/Zen2) applied"
|
||||
fi
|
||||
elif cat /proc/cpuinfo | grep "Intel" > /dev/null;
|
||||
then
|
||||
echo "Detected Intel"
|
||||
|
||||
27
src/3rdparty/fmt/LICENSE.rst
vendored
Normal file
27
src/3rdparty/fmt/LICENSE.rst
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
Copyright (c) 2012 - present, Victor Zverovich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
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 AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
--- Optional exception to the license ---
|
||||
|
||||
As an exception, if, as a result of your compiling your source code, portions
|
||||
of this Software are embedded into a machine-executable object form of such
|
||||
source code, you may redistribute such embedded portions in such object form
|
||||
without including the above copyright and permission notices.
|
||||
505
src/3rdparty/fmt/README.rst
vendored
Normal file
505
src/3rdparty/fmt/README.rst
vendored
Normal file
@@ -0,0 +1,505 @@
|
||||
{fmt}
|
||||
=====
|
||||
|
||||
.. image:: https://travis-ci.org/fmtlib/fmt.png?branch=master
|
||||
:target: https://travis-ci.org/fmtlib/fmt
|
||||
|
||||
.. image:: https://ci.appveyor.com/api/projects/status/ehjkiefde6gucy1v
|
||||
:target: https://ci.appveyor.com/project/vitaut/fmt
|
||||
|
||||
.. image:: https://oss-fuzz-build-logs.storage.googleapis.com/badges/libfmt.svg
|
||||
:alt: fmt is continuously fuzzed at oss-fuzz
|
||||
:target: https://bugs.chromium.org/p/oss-fuzz/issues/list?\
|
||||
colspec=ID%20Type%20Component%20Status%20Proj%20Reported%20Owner%20\
|
||||
Summary&q=proj%3Dlibfmt&can=1
|
||||
|
||||
.. image:: https://img.shields.io/badge/stackoverflow-fmt-blue.svg
|
||||
:alt: Ask questions at StackOverflow with the tag fmt
|
||||
:target: https://stackoverflow.com/questions/tagged/fmt
|
||||
|
||||
**{fmt}** is an open-source formatting library providing a fast and safe
|
||||
alternative to C stdio and C++ iostreams.
|
||||
|
||||
If you like this project, please consider donating to BYSOL,
|
||||
an initiative to help victims of political repressions in Belarus:
|
||||
https://www.facebook.com/donate/759400044849707/108388587646909/.
|
||||
|
||||
`Documentation <https://fmt.dev>`__
|
||||
|
||||
Q&A: ask questions on `StackOverflow with the tag fmt
|
||||
<https://stackoverflow.com/questions/tagged/fmt>`_.
|
||||
|
||||
Try {fmt} in `Compiler Explorer <https://godbolt.org/z/Eq5763>`_.
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
* Simple `format API <https://fmt.dev/latest/api.html>`_ with positional arguments
|
||||
for localization
|
||||
* Implementation of `C++20 std::format
|
||||
<https://en.cppreference.com/w/cpp/utility/format>`__
|
||||
* `Format string syntax <https://fmt.dev/latest/syntax.html>`_ similar to Python's
|
||||
`format <https://docs.python.org/3/library/stdtypes.html#str.format>`_
|
||||
* Fast IEEE 754 floating-point formatter with correct rounding, shortness and
|
||||
round-trip guarantees.
|
||||
* Safe `printf implementation
|
||||
<https://fmt.dev/latest/api.html#printf-formatting>`_ including the POSIX
|
||||
extension for positional arguments
|
||||
* Extensibility: `support for user-defined types
|
||||
<https://fmt.dev/latest/api.html#formatting-user-defined-types>`_
|
||||
* High performance: faster than common standard library implementations of
|
||||
``(s)printf``, iostreams, ``to_string`` and ``to_chars``, see `Speed tests`_
|
||||
and `Converting a hundred million integers to strings per second
|
||||
<http://www.zverovich.net/2020/06/13/fast-int-to-string-revisited.html>`_
|
||||
* Small code size both in terms of source code with the minimum configuration
|
||||
consisting of just three files, ``core.h``, ``format.h`` and ``format-inl.h``,
|
||||
and compiled code; see `Compile time and code bloat`_
|
||||
* Reliability: the library has an extensive set of `tests
|
||||
<https://github.com/fmtlib/fmt/tree/master/test>`_ and is `continuously fuzzed
|
||||
<https://bugs.chromium.org/p/oss-fuzz/issues/list?colspec=ID%20Type%20
|
||||
Component%20Status%20Proj%20Reported%20Owner%20Summary&q=proj%3Dlibfmt&can=1>`_
|
||||
* Safety: the library is fully type safe, errors in format strings can be
|
||||
reported at compile time, automatic memory management prevents buffer overflow
|
||||
errors
|
||||
* Ease of use: small self-contained code base, no external dependencies,
|
||||
permissive MIT `license
|
||||
<https://github.com/fmtlib/fmt/blob/master/LICENSE.rst>`_
|
||||
* `Portability <https://fmt.dev/latest/index.html#portability>`_ with
|
||||
consistent output across platforms and support for older compilers
|
||||
* Clean warning-free codebase even on high warning levels such as
|
||||
``-Wall -Wextra -pedantic``
|
||||
* Locale-independence by default
|
||||
* Optional header-only configuration enabled with the ``FMT_HEADER_ONLY`` macro
|
||||
|
||||
See the `documentation <https://fmt.dev>`_ for more details.
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
**Print to stdout** (`run <https://godbolt.org/z/Tevcjh>`_)
|
||||
|
||||
.. code:: c++
|
||||
|
||||
#include <fmt/core.h>
|
||||
|
||||
int main() {
|
||||
fmt::print("Hello, world!\n");
|
||||
}
|
||||
|
||||
**Format a string** (`run <https://godbolt.org/z/oK8h33>`_)
|
||||
|
||||
.. code:: c++
|
||||
|
||||
std::string s = fmt::format("The answer is {}.", 42);
|
||||
// s == "The answer is 42."
|
||||
|
||||
**Format a string using positional arguments** (`run <https://godbolt.org/z/Yn7Txe>`_)
|
||||
|
||||
.. code:: c++
|
||||
|
||||
std::string s = fmt::format("I'd rather be {1} than {0}.", "right", "happy");
|
||||
// s == "I'd rather be happy than right."
|
||||
|
||||
**Print chrono durations** (`run <https://godbolt.org/z/K8s4Mc>`_)
|
||||
|
||||
.. code:: c++
|
||||
|
||||
#include <fmt/chrono.h>
|
||||
|
||||
int main() {
|
||||
using namespace std::literals::chrono_literals;
|
||||
fmt::print("Default format: {} {}\n", 42s, 100ms);
|
||||
fmt::print("strftime-like format: {:%H:%M:%S}\n", 3h + 15min + 30s);
|
||||
}
|
||||
|
||||
Output::
|
||||
|
||||
Default format: 42s 100ms
|
||||
strftime-like format: 03:15:30
|
||||
|
||||
**Print a container** (`run <https://godbolt.org/z/MjsY7c>`_)
|
||||
|
||||
.. code:: c++
|
||||
|
||||
#include <vector>
|
||||
#include <fmt/ranges.h>
|
||||
|
||||
int main() {
|
||||
std::vector<int> v = {1, 2, 3};
|
||||
fmt::print("{}\n", v);
|
||||
}
|
||||
|
||||
Output::
|
||||
|
||||
{1, 2, 3}
|
||||
|
||||
**Check a format string at compile time**
|
||||
|
||||
.. code:: c++
|
||||
|
||||
std::string s = fmt::format(FMT_STRING("{:d}"), "don't panic");
|
||||
|
||||
This gives a compile-time error because ``d`` is an invalid format specifier for
|
||||
a string.
|
||||
|
||||
**Write a file from a single thread**
|
||||
|
||||
.. code:: c++
|
||||
|
||||
#include <fmt/os.h>
|
||||
|
||||
int main() {
|
||||
auto out = fmt::output_file("guide.txt");
|
||||
out.print("Don't {}", "Panic");
|
||||
}
|
||||
|
||||
This can be `5 to 9 times faster than fprintf
|
||||
<http://www.zverovich.net/2020/08/04/optimal-file-buffer-size.html>`_.
|
||||
|
||||
**Print with colors and text styles**
|
||||
|
||||
.. code:: c++
|
||||
|
||||
#include <fmt/color.h>
|
||||
|
||||
int main() {
|
||||
fmt::print(fg(fmt::color::crimson) | fmt::emphasis::bold,
|
||||
"Hello, {}!\n", "world");
|
||||
fmt::print(fg(fmt::color::floral_white) | bg(fmt::color::slate_gray) |
|
||||
fmt::emphasis::underline, "Hello, {}!\n", "мир");
|
||||
fmt::print(fg(fmt::color::steel_blue) | fmt::emphasis::italic,
|
||||
"Hello, {}!\n", "世界");
|
||||
}
|
||||
|
||||
Output on a modern terminal:
|
||||
|
||||
.. image:: https://user-images.githubusercontent.com/
|
||||
576385/88485597-d312f600-cf2b-11ea-9cbe-61f535a86e28.png
|
||||
|
||||
Benchmarks
|
||||
----------
|
||||
|
||||
Speed tests
|
||||
~~~~~~~~~~~
|
||||
|
||||
================= ============= ===========
|
||||
Library Method Run Time, s
|
||||
================= ============= ===========
|
||||
libc printf 1.04
|
||||
libc++ std::ostream 3.05
|
||||
{fmt} 6.1.1 fmt::print 0.75
|
||||
Boost Format 1.67 boost::format 7.24
|
||||
Folly Format folly::format 2.23
|
||||
================= ============= ===========
|
||||
|
||||
{fmt} is the fastest of the benchmarked methods, ~35% faster than ``printf``.
|
||||
|
||||
The above results were generated by building ``tinyformat_test.cpp`` on macOS
|
||||
10.14.6 with ``clang++ -O3 -DNDEBUG -DSPEED_TEST -DHAVE_FORMAT``, and taking the
|
||||
best of three runs. In the test, the format string ``"%0.10f:%04d:%+g:%s:%p:%c:%%\n"``
|
||||
or equivalent is filled 2,000,000 times with output sent to ``/dev/null``; for
|
||||
further details refer to the `source
|
||||
<https://github.com/fmtlib/format-benchmark/blob/master/tinyformat_test.cpp>`_.
|
||||
|
||||
{fmt} is up to 10x faster than ``std::ostringstream`` and ``sprintf`` on
|
||||
floating-point formatting (`dtoa-benchmark <https://github.com/fmtlib/dtoa-benchmark>`_)
|
||||
and faster than `double-conversion <https://github.com/google/double-conversion>`_:
|
||||
|
||||
.. image:: https://user-images.githubusercontent.com/576385/
|
||||
69767160-cdaca400-112f-11ea-9fc5-347c9f83caad.png
|
||||
:target: https://fmt.dev/unknown_mac64_clang10.0.html
|
||||
|
||||
Compile time and code bloat
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The script `bloat-test.py
|
||||
<https://github.com/fmtlib/format-benchmark/blob/master/bloat-test.py>`_
|
||||
from `format-benchmark <https://github.com/fmtlib/format-benchmark>`_
|
||||
tests compile time and code bloat for nontrivial projects.
|
||||
It generates 100 translation units and uses ``printf()`` or its alternative
|
||||
five times in each to simulate a medium sized project. The resulting
|
||||
executable size and compile time (Apple LLVM version 8.1.0 (clang-802.0.42),
|
||||
macOS Sierra, best of three) is shown in the following tables.
|
||||
|
||||
**Optimized build (-O3)**
|
||||
|
||||
============= =============== ==================== ==================
|
||||
Method Compile Time, s Executable size, KiB Stripped size, KiB
|
||||
============= =============== ==================== ==================
|
||||
printf 2.6 29 26
|
||||
printf+string 16.4 29 26
|
||||
iostreams 31.1 59 55
|
||||
{fmt} 19.0 37 34
|
||||
Boost Format 91.9 226 203
|
||||
Folly Format 115.7 101 88
|
||||
============= =============== ==================== ==================
|
||||
|
||||
As you can see, {fmt} has 60% less overhead in terms of resulting binary code
|
||||
size compared to iostreams and comes pretty close to ``printf``. Boost Format
|
||||
and Folly Format have the largest overheads.
|
||||
|
||||
``printf+string`` is the same as ``printf`` but with extra ``<string>``
|
||||
include to measure the overhead of the latter.
|
||||
|
||||
**Non-optimized build**
|
||||
|
||||
============= =============== ==================== ==================
|
||||
Method Compile Time, s Executable size, KiB Stripped size, KiB
|
||||
============= =============== ==================== ==================
|
||||
printf 2.2 33 30
|
||||
printf+string 16.0 33 30
|
||||
iostreams 28.3 56 52
|
||||
{fmt} 18.2 59 50
|
||||
Boost Format 54.1 365 303
|
||||
Folly Format 79.9 445 430
|
||||
============= =============== ==================== ==================
|
||||
|
||||
``libc``, ``lib(std)c++`` and ``libfmt`` are all linked as shared libraries to
|
||||
compare formatting function overhead only. Boost Format is a
|
||||
header-only library so it doesn't provide any linkage options.
|
||||
|
||||
Running the tests
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Please refer to `Building the library`__ for the instructions on how to build
|
||||
the library and run the unit tests.
|
||||
|
||||
__ https://fmt.dev/latest/usage.html#building-the-library
|
||||
|
||||
Benchmarks reside in a separate repository,
|
||||
`format-benchmarks <https://github.com/fmtlib/format-benchmark>`_,
|
||||
so to run the benchmarks you first need to clone this repository and
|
||||
generate Makefiles with CMake::
|
||||
|
||||
$ git clone --recursive https://github.com/fmtlib/format-benchmark.git
|
||||
$ cd format-benchmark
|
||||
$ cmake .
|
||||
|
||||
Then you can run the speed test::
|
||||
|
||||
$ make speed-test
|
||||
|
||||
or the bloat test::
|
||||
|
||||
$ make bloat-test
|
||||
|
||||
Projects using this library
|
||||
---------------------------
|
||||
|
||||
* `0 A.D. <https://play0ad.com/>`_: A free, open-source, cross-platform
|
||||
real-time strategy game
|
||||
|
||||
* `AMPL/MP <https://github.com/ampl/mp>`_:
|
||||
An open-source library for mathematical programming
|
||||
|
||||
* `Aseprite <https://github.com/aseprite/aseprite>`_:
|
||||
Animated sprite editor & pixel art tool
|
||||
|
||||
* `AvioBook <https://www.aviobook.aero/en>`_: A comprehensive aircraft
|
||||
operations suite
|
||||
|
||||
* `Celestia <https://celestia.space/>`_: Real-time 3D visualization of space
|
||||
|
||||
* `Ceph <https://ceph.com/>`_: A scalable distributed storage system
|
||||
|
||||
* `ccache <https://ccache.dev/>`_: A compiler cache
|
||||
|
||||
* `ClickHouse <https://github.com/ClickHouse/ClickHouse>`_: analytical database
|
||||
management system
|
||||
|
||||
* `CUAUV <http://cuauv.org/>`_: Cornell University's autonomous underwater
|
||||
vehicle
|
||||
|
||||
* `Drake <https://drake.mit.edu/>`_: A planning, control, and analysis toolbox
|
||||
for nonlinear dynamical systems (MIT)
|
||||
|
||||
* `Envoy <https://lyft.github.io/envoy/>`_: C++ L7 proxy and communication bus
|
||||
(Lyft)
|
||||
|
||||
* `FiveM <https://fivem.net/>`_: a modification framework for GTA V
|
||||
|
||||
* `Folly <https://github.com/facebook/folly>`_: Facebook open-source library
|
||||
|
||||
* `HarpyWar/pvpgn <https://github.com/pvpgn/pvpgn-server>`_:
|
||||
Player vs Player Gaming Network with tweaks
|
||||
|
||||
* `KBEngine <https://github.com/kbengine/kbengine>`_: An open-source MMOG server
|
||||
engine
|
||||
|
||||
* `Keypirinha <https://keypirinha.com/>`_: A semantic launcher for Windows
|
||||
|
||||
* `Kodi <https://kodi.tv/>`_ (formerly xbmc): Home theater software
|
||||
|
||||
* `Knuth <https://kth.cash/>`_: High-performance Bitcoin full-node
|
||||
|
||||
* `Microsoft Verona <https://github.com/microsoft/verona>`_:
|
||||
Research programming language for concurrent ownership
|
||||
|
||||
* `MongoDB <https://mongodb.com/>`_: Distributed document database
|
||||
|
||||
* `MongoDB Smasher <https://github.com/duckie/mongo_smasher>`_: A small tool to
|
||||
generate randomized datasets
|
||||
|
||||
* `OpenSpace <https://openspaceproject.com/>`_: An open-source
|
||||
astrovisualization framework
|
||||
|
||||
* `PenUltima Online (POL) <https://www.polserver.com/>`_:
|
||||
An MMO server, compatible with most Ultima Online clients
|
||||
|
||||
* `PyTorch <https://github.com/pytorch/pytorch>`_: An open-source machine
|
||||
learning library
|
||||
|
||||
* `quasardb <https://www.quasardb.net/>`_: A distributed, high-performance,
|
||||
associative database
|
||||
|
||||
* `Quill <https://github.com/odygrd/quill>`_: Asynchronous low-latency logging library
|
||||
|
||||
* `QKW <https://github.com/ravijanjam/qkw>`_: Generalizing aliasing to simplify
|
||||
navigation, and executing complex multi-line terminal command sequences
|
||||
|
||||
* `readpe <https://bitbucket.org/sys_dev/readpe>`_: Read Portable Executable
|
||||
|
||||
* `redis-cerberus <https://github.com/HunanTV/redis-cerberus>`_: A Redis cluster
|
||||
proxy
|
||||
|
||||
* `redpanda <https://vectorized.io/redpanda>`_: A 10x faster Kafka® replacement
|
||||
for mission critical systems written in C++
|
||||
|
||||
* `rpclib <http://rpclib.net/>`_: A modern C++ msgpack-RPC server and client
|
||||
library
|
||||
|
||||
* `Salesforce Analytics Cloud
|
||||
<https://www.salesforce.com/analytics-cloud/overview/>`_:
|
||||
Business intelligence software
|
||||
|
||||
* `Scylla <https://www.scylladb.com/>`_: A Cassandra-compatible NoSQL data store
|
||||
that can handle 1 million transactions per second on a single server
|
||||
|
||||
* `Seastar <http://www.seastar-project.org/>`_: An advanced, open-source C++
|
||||
framework for high-performance server applications on modern hardware
|
||||
|
||||
* `spdlog <https://github.com/gabime/spdlog>`_: Super fast C++ logging library
|
||||
|
||||
* `Stellar <https://www.stellar.org/>`_: Financial platform
|
||||
|
||||
* `Touch Surgery <https://www.touchsurgery.com/>`_: Surgery simulator
|
||||
|
||||
* `TrinityCore <https://github.com/TrinityCore/TrinityCore>`_: Open-source
|
||||
MMORPG framework
|
||||
|
||||
* `Windows Terminal <https://github.com/microsoft/terminal>`_: The new Windows
|
||||
Terminal
|
||||
|
||||
`More... <https://github.com/search?q=fmtlib&type=Code>`_
|
||||
|
||||
If you are aware of other projects using this library, please let me know
|
||||
by `email <mailto:victor.zverovich@gmail.com>`_ or by submitting an
|
||||
`issue <https://github.com/fmtlib/fmt/issues>`_.
|
||||
|
||||
Motivation
|
||||
----------
|
||||
|
||||
So why yet another formatting library?
|
||||
|
||||
There are plenty of methods for doing this task, from standard ones like
|
||||
the printf family of function and iostreams to Boost Format and FastFormat
|
||||
libraries. The reason for creating a new library is that every existing
|
||||
solution that I found either had serious issues or didn't provide
|
||||
all the features I needed.
|
||||
|
||||
printf
|
||||
~~~~~~
|
||||
|
||||
The good thing about ``printf`` is that it is pretty fast and readily available
|
||||
being a part of the C standard library. The main drawback is that it
|
||||
doesn't support user-defined types. ``printf`` also has safety issues although
|
||||
they are somewhat mitigated with `__attribute__ ((format (printf, ...))
|
||||
<https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html>`_ in GCC.
|
||||
There is a POSIX extension that adds positional arguments required for
|
||||
`i18n <https://en.wikipedia.org/wiki/Internationalization_and_localization>`_
|
||||
to ``printf`` but it is not a part of C99 and may not be available on some
|
||||
platforms.
|
||||
|
||||
iostreams
|
||||
~~~~~~~~~
|
||||
|
||||
The main issue with iostreams is best illustrated with an example:
|
||||
|
||||
.. code:: c++
|
||||
|
||||
std::cout << std::setprecision(2) << std::fixed << 1.23456 << "\n";
|
||||
|
||||
which is a lot of typing compared to printf:
|
||||
|
||||
.. code:: c++
|
||||
|
||||
printf("%.2f\n", 1.23456);
|
||||
|
||||
Matthew Wilson, the author of FastFormat, called this "chevron hell". iostreams
|
||||
don't support positional arguments by design.
|
||||
|
||||
The good part is that iostreams support user-defined types and are safe although
|
||||
error handling is awkward.
|
||||
|
||||
Boost Format
|
||||
~~~~~~~~~~~~
|
||||
|
||||
This is a very powerful library which supports both ``printf``-like format
|
||||
strings and positional arguments. Its main drawback is performance. According to
|
||||
various, benchmarks it is much slower than other methods considered here. Boost
|
||||
Format also has excessive build times and severe code bloat issues (see
|
||||
`Benchmarks`_).
|
||||
|
||||
FastFormat
|
||||
~~~~~~~~~~
|
||||
|
||||
This is an interesting library which is fast, safe and has positional arguments.
|
||||
However, it has significant limitations, citing its author:
|
||||
|
||||
Three features that have no hope of being accommodated within the
|
||||
current design are:
|
||||
|
||||
* Leading zeros (or any other non-space padding)
|
||||
* Octal/hexadecimal encoding
|
||||
* Runtime width/alignment specification
|
||||
|
||||
It is also quite big and has a heavy dependency, STLSoft, which might be too
|
||||
restrictive for using it in some projects.
|
||||
|
||||
Boost Spirit.Karma
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This is not really a formatting library but I decided to include it here for
|
||||
completeness. As iostreams, it suffers from the problem of mixing verbatim text
|
||||
with arguments. The library is pretty fast, but slower on integer formatting
|
||||
than ``fmt::format_to`` with format string compilation on Karma's own benchmark,
|
||||
see `Converting a hundred million integers to strings per second
|
||||
<http://www.zverovich.net/2020/06/13/fast-int-to-string-revisited.html>`_.
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
{fmt} is distributed under the MIT `license
|
||||
<https://github.com/fmtlib/fmt/blob/master/LICENSE.rst>`_.
|
||||
|
||||
Documentation License
|
||||
---------------------
|
||||
|
||||
The `Format String Syntax <https://fmt.dev/latest/syntax.html>`_
|
||||
section in the documentation is based on the one from Python `string module
|
||||
documentation <https://docs.python.org/3/library/string.html#module-string>`_.
|
||||
For this reason the documentation is distributed under the Python Software
|
||||
Foundation license available in `doc/python-license.txt
|
||||
<https://raw.github.com/fmtlib/fmt/master/doc/python-license.txt>`_.
|
||||
It only applies if you distribute the documentation of {fmt}.
|
||||
|
||||
Maintainers
|
||||
-----------
|
||||
|
||||
The {fmt} library is maintained by Victor Zverovich (`vitaut
|
||||
<https://github.com/vitaut>`_) and Jonathan Müller (`foonathan
|
||||
<https://github.com/foonathan>`_) with contributions from many other people.
|
||||
See `Contributors <https://github.com/fmtlib/fmt/graphs/contributors>`_ and
|
||||
`Releases <https://github.com/fmtlib/fmt/releases>`_ for some of the names.
|
||||
Let us know if your contribution is not listed or mentioned incorrectly and
|
||||
we'll make it right.
|
||||
1118
src/3rdparty/fmt/chrono.h
vendored
Normal file
1118
src/3rdparty/fmt/chrono.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
602
src/3rdparty/fmt/color.h
vendored
Normal file
602
src/3rdparty/fmt/color.h
vendored
Normal file
@@ -0,0 +1,602 @@
|
||||
// Formatting library for C++ - color support
|
||||
//
|
||||
// Copyright (c) 2018 - present, Victor Zverovich and fmt contributors
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_COLOR_H_
|
||||
#define FMT_COLOR_H_
|
||||
|
||||
#include "format.h"
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
|
||||
enum class color : uint32_t {
|
||||
alice_blue = 0xF0F8FF, // rgb(240,248,255)
|
||||
antique_white = 0xFAEBD7, // rgb(250,235,215)
|
||||
aqua = 0x00FFFF, // rgb(0,255,255)
|
||||
aquamarine = 0x7FFFD4, // rgb(127,255,212)
|
||||
azure = 0xF0FFFF, // rgb(240,255,255)
|
||||
beige = 0xF5F5DC, // rgb(245,245,220)
|
||||
bisque = 0xFFE4C4, // rgb(255,228,196)
|
||||
black = 0x000000, // rgb(0,0,0)
|
||||
blanched_almond = 0xFFEBCD, // rgb(255,235,205)
|
||||
blue = 0x0000FF, // rgb(0,0,255)
|
||||
blue_violet = 0x8A2BE2, // rgb(138,43,226)
|
||||
brown = 0xA52A2A, // rgb(165,42,42)
|
||||
burly_wood = 0xDEB887, // rgb(222,184,135)
|
||||
cadet_blue = 0x5F9EA0, // rgb(95,158,160)
|
||||
chartreuse = 0x7FFF00, // rgb(127,255,0)
|
||||
chocolate = 0xD2691E, // rgb(210,105,30)
|
||||
coral = 0xFF7F50, // rgb(255,127,80)
|
||||
cornflower_blue = 0x6495ED, // rgb(100,149,237)
|
||||
cornsilk = 0xFFF8DC, // rgb(255,248,220)
|
||||
crimson = 0xDC143C, // rgb(220,20,60)
|
||||
cyan = 0x00FFFF, // rgb(0,255,255)
|
||||
dark_blue = 0x00008B, // rgb(0,0,139)
|
||||
dark_cyan = 0x008B8B, // rgb(0,139,139)
|
||||
dark_golden_rod = 0xB8860B, // rgb(184,134,11)
|
||||
dark_gray = 0xA9A9A9, // rgb(169,169,169)
|
||||
dark_green = 0x006400, // rgb(0,100,0)
|
||||
dark_khaki = 0xBDB76B, // rgb(189,183,107)
|
||||
dark_magenta = 0x8B008B, // rgb(139,0,139)
|
||||
dark_olive_green = 0x556B2F, // rgb(85,107,47)
|
||||
dark_orange = 0xFF8C00, // rgb(255,140,0)
|
||||
dark_orchid = 0x9932CC, // rgb(153,50,204)
|
||||
dark_red = 0x8B0000, // rgb(139,0,0)
|
||||
dark_salmon = 0xE9967A, // rgb(233,150,122)
|
||||
dark_sea_green = 0x8FBC8F, // rgb(143,188,143)
|
||||
dark_slate_blue = 0x483D8B, // rgb(72,61,139)
|
||||
dark_slate_gray = 0x2F4F4F, // rgb(47,79,79)
|
||||
dark_turquoise = 0x00CED1, // rgb(0,206,209)
|
||||
dark_violet = 0x9400D3, // rgb(148,0,211)
|
||||
deep_pink = 0xFF1493, // rgb(255,20,147)
|
||||
deep_sky_blue = 0x00BFFF, // rgb(0,191,255)
|
||||
dim_gray = 0x696969, // rgb(105,105,105)
|
||||
dodger_blue = 0x1E90FF, // rgb(30,144,255)
|
||||
fire_brick = 0xB22222, // rgb(178,34,34)
|
||||
floral_white = 0xFFFAF0, // rgb(255,250,240)
|
||||
forest_green = 0x228B22, // rgb(34,139,34)
|
||||
fuchsia = 0xFF00FF, // rgb(255,0,255)
|
||||
gainsboro = 0xDCDCDC, // rgb(220,220,220)
|
||||
ghost_white = 0xF8F8FF, // rgb(248,248,255)
|
||||
gold = 0xFFD700, // rgb(255,215,0)
|
||||
golden_rod = 0xDAA520, // rgb(218,165,32)
|
||||
gray = 0x808080, // rgb(128,128,128)
|
||||
green = 0x008000, // rgb(0,128,0)
|
||||
green_yellow = 0xADFF2F, // rgb(173,255,47)
|
||||
honey_dew = 0xF0FFF0, // rgb(240,255,240)
|
||||
hot_pink = 0xFF69B4, // rgb(255,105,180)
|
||||
indian_red = 0xCD5C5C, // rgb(205,92,92)
|
||||
indigo = 0x4B0082, // rgb(75,0,130)
|
||||
ivory = 0xFFFFF0, // rgb(255,255,240)
|
||||
khaki = 0xF0E68C, // rgb(240,230,140)
|
||||
lavender = 0xE6E6FA, // rgb(230,230,250)
|
||||
lavender_blush = 0xFFF0F5, // rgb(255,240,245)
|
||||
lawn_green = 0x7CFC00, // rgb(124,252,0)
|
||||
lemon_chiffon = 0xFFFACD, // rgb(255,250,205)
|
||||
light_blue = 0xADD8E6, // rgb(173,216,230)
|
||||
light_coral = 0xF08080, // rgb(240,128,128)
|
||||
light_cyan = 0xE0FFFF, // rgb(224,255,255)
|
||||
light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210)
|
||||
light_gray = 0xD3D3D3, // rgb(211,211,211)
|
||||
light_green = 0x90EE90, // rgb(144,238,144)
|
||||
light_pink = 0xFFB6C1, // rgb(255,182,193)
|
||||
light_salmon = 0xFFA07A, // rgb(255,160,122)
|
||||
light_sea_green = 0x20B2AA, // rgb(32,178,170)
|
||||
light_sky_blue = 0x87CEFA, // rgb(135,206,250)
|
||||
light_slate_gray = 0x778899, // rgb(119,136,153)
|
||||
light_steel_blue = 0xB0C4DE, // rgb(176,196,222)
|
||||
light_yellow = 0xFFFFE0, // rgb(255,255,224)
|
||||
lime = 0x00FF00, // rgb(0,255,0)
|
||||
lime_green = 0x32CD32, // rgb(50,205,50)
|
||||
linen = 0xFAF0E6, // rgb(250,240,230)
|
||||
magenta = 0xFF00FF, // rgb(255,0,255)
|
||||
maroon = 0x800000, // rgb(128,0,0)
|
||||
medium_aquamarine = 0x66CDAA, // rgb(102,205,170)
|
||||
medium_blue = 0x0000CD, // rgb(0,0,205)
|
||||
medium_orchid = 0xBA55D3, // rgb(186,85,211)
|
||||
medium_purple = 0x9370DB, // rgb(147,112,219)
|
||||
medium_sea_green = 0x3CB371, // rgb(60,179,113)
|
||||
medium_slate_blue = 0x7B68EE, // rgb(123,104,238)
|
||||
medium_spring_green = 0x00FA9A, // rgb(0,250,154)
|
||||
medium_turquoise = 0x48D1CC, // rgb(72,209,204)
|
||||
medium_violet_red = 0xC71585, // rgb(199,21,133)
|
||||
midnight_blue = 0x191970, // rgb(25,25,112)
|
||||
mint_cream = 0xF5FFFA, // rgb(245,255,250)
|
||||
misty_rose = 0xFFE4E1, // rgb(255,228,225)
|
||||
moccasin = 0xFFE4B5, // rgb(255,228,181)
|
||||
navajo_white = 0xFFDEAD, // rgb(255,222,173)
|
||||
navy = 0x000080, // rgb(0,0,128)
|
||||
old_lace = 0xFDF5E6, // rgb(253,245,230)
|
||||
olive = 0x808000, // rgb(128,128,0)
|
||||
olive_drab = 0x6B8E23, // rgb(107,142,35)
|
||||
orange = 0xFFA500, // rgb(255,165,0)
|
||||
orange_red = 0xFF4500, // rgb(255,69,0)
|
||||
orchid = 0xDA70D6, // rgb(218,112,214)
|
||||
pale_golden_rod = 0xEEE8AA, // rgb(238,232,170)
|
||||
pale_green = 0x98FB98, // rgb(152,251,152)
|
||||
pale_turquoise = 0xAFEEEE, // rgb(175,238,238)
|
||||
pale_violet_red = 0xDB7093, // rgb(219,112,147)
|
||||
papaya_whip = 0xFFEFD5, // rgb(255,239,213)
|
||||
peach_puff = 0xFFDAB9, // rgb(255,218,185)
|
||||
peru = 0xCD853F, // rgb(205,133,63)
|
||||
pink = 0xFFC0CB, // rgb(255,192,203)
|
||||
plum = 0xDDA0DD, // rgb(221,160,221)
|
||||
powder_blue = 0xB0E0E6, // rgb(176,224,230)
|
||||
purple = 0x800080, // rgb(128,0,128)
|
||||
rebecca_purple = 0x663399, // rgb(102,51,153)
|
||||
red = 0xFF0000, // rgb(255,0,0)
|
||||
rosy_brown = 0xBC8F8F, // rgb(188,143,143)
|
||||
royal_blue = 0x4169E1, // rgb(65,105,225)
|
||||
saddle_brown = 0x8B4513, // rgb(139,69,19)
|
||||
salmon = 0xFA8072, // rgb(250,128,114)
|
||||
sandy_brown = 0xF4A460, // rgb(244,164,96)
|
||||
sea_green = 0x2E8B57, // rgb(46,139,87)
|
||||
sea_shell = 0xFFF5EE, // rgb(255,245,238)
|
||||
sienna = 0xA0522D, // rgb(160,82,45)
|
||||
silver = 0xC0C0C0, // rgb(192,192,192)
|
||||
sky_blue = 0x87CEEB, // rgb(135,206,235)
|
||||
slate_blue = 0x6A5ACD, // rgb(106,90,205)
|
||||
slate_gray = 0x708090, // rgb(112,128,144)
|
||||
snow = 0xFFFAFA, // rgb(255,250,250)
|
||||
spring_green = 0x00FF7F, // rgb(0,255,127)
|
||||
steel_blue = 0x4682B4, // rgb(70,130,180)
|
||||
tan = 0xD2B48C, // rgb(210,180,140)
|
||||
teal = 0x008080, // rgb(0,128,128)
|
||||
thistle = 0xD8BFD8, // rgb(216,191,216)
|
||||
tomato = 0xFF6347, // rgb(255,99,71)
|
||||
turquoise = 0x40E0D0, // rgb(64,224,208)
|
||||
violet = 0xEE82EE, // rgb(238,130,238)
|
||||
wheat = 0xF5DEB3, // rgb(245,222,179)
|
||||
white = 0xFFFFFF, // rgb(255,255,255)
|
||||
white_smoke = 0xF5F5F5, // rgb(245,245,245)
|
||||
yellow = 0xFFFF00, // rgb(255,255,0)
|
||||
yellow_green = 0x9ACD32 // rgb(154,205,50)
|
||||
}; // enum class color
|
||||
|
||||
enum class terminal_color : uint8_t {
|
||||
black = 30,
|
||||
red,
|
||||
green,
|
||||
yellow,
|
||||
blue,
|
||||
magenta,
|
||||
cyan,
|
||||
white,
|
||||
bright_black = 90,
|
||||
bright_red,
|
||||
bright_green,
|
||||
bright_yellow,
|
||||
bright_blue,
|
||||
bright_magenta,
|
||||
bright_cyan,
|
||||
bright_white
|
||||
};
|
||||
|
||||
enum class emphasis : uint8_t {
|
||||
bold = 1,
|
||||
italic = 1 << 1,
|
||||
underline = 1 << 2,
|
||||
strikethrough = 1 << 3
|
||||
};
|
||||
|
||||
// rgb is a struct for red, green and blue colors.
|
||||
// Using the name "rgb" makes some editors show the color in a tooltip.
|
||||
struct rgb {
|
||||
FMT_CONSTEXPR rgb() : r(0), g(0), b(0) {}
|
||||
FMT_CONSTEXPR rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {}
|
||||
FMT_CONSTEXPR rgb(uint32_t hex)
|
||||
: r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {}
|
||||
FMT_CONSTEXPR rgb(color hex)
|
||||
: r((uint32_t(hex) >> 16) & 0xFF),
|
||||
g((uint32_t(hex) >> 8) & 0xFF),
|
||||
b(uint32_t(hex) & 0xFF) {}
|
||||
uint8_t r;
|
||||
uint8_t g;
|
||||
uint8_t b;
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
// color is a struct of either a rgb color or a terminal color.
|
||||
struct color_type {
|
||||
FMT_CONSTEXPR color_type() FMT_NOEXCEPT : is_rgb(), value{} {}
|
||||
FMT_CONSTEXPR color_type(color rgb_color) FMT_NOEXCEPT : is_rgb(true),
|
||||
value{} {
|
||||
value.rgb_color = static_cast<uint32_t>(rgb_color);
|
||||
}
|
||||
FMT_CONSTEXPR color_type(rgb rgb_color) FMT_NOEXCEPT : is_rgb(true), value{} {
|
||||
value.rgb_color = (static_cast<uint32_t>(rgb_color.r) << 16) |
|
||||
(static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b;
|
||||
}
|
||||
FMT_CONSTEXPR color_type(terminal_color term_color) FMT_NOEXCEPT : is_rgb(),
|
||||
value{} {
|
||||
value.term_color = static_cast<uint8_t>(term_color);
|
||||
}
|
||||
bool is_rgb;
|
||||
union color_union {
|
||||
uint8_t term_color;
|
||||
uint32_t rgb_color;
|
||||
} value;
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
// Experimental text formatting support.
|
||||
class text_style {
|
||||
public:
|
||||
FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT
|
||||
: set_foreground_color(),
|
||||
set_background_color(),
|
||||
ems(em) {}
|
||||
|
||||
FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) {
|
||||
if (!set_foreground_color) {
|
||||
set_foreground_color = rhs.set_foreground_color;
|
||||
foreground_color = rhs.foreground_color;
|
||||
} else if (rhs.set_foreground_color) {
|
||||
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
|
||||
FMT_THROW(format_error("can't OR a terminal color"));
|
||||
foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color;
|
||||
}
|
||||
|
||||
if (!set_background_color) {
|
||||
set_background_color = rhs.set_background_color;
|
||||
background_color = rhs.background_color;
|
||||
} else if (rhs.set_background_color) {
|
||||
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
|
||||
FMT_THROW(format_error("can't OR a terminal color"));
|
||||
background_color.value.rgb_color |= rhs.background_color.value.rgb_color;
|
||||
}
|
||||
|
||||
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) |
|
||||
static_cast<uint8_t>(rhs.ems));
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend FMT_CONSTEXPR text_style operator|(text_style lhs,
|
||||
const text_style& rhs) {
|
||||
return lhs |= rhs;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR text_style& operator&=(const text_style& rhs) {
|
||||
if (!set_foreground_color) {
|
||||
set_foreground_color = rhs.set_foreground_color;
|
||||
foreground_color = rhs.foreground_color;
|
||||
} else if (rhs.set_foreground_color) {
|
||||
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
|
||||
FMT_THROW(format_error("can't AND a terminal color"));
|
||||
foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color;
|
||||
}
|
||||
|
||||
if (!set_background_color) {
|
||||
set_background_color = rhs.set_background_color;
|
||||
background_color = rhs.background_color;
|
||||
} else if (rhs.set_background_color) {
|
||||
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
|
||||
FMT_THROW(format_error("can't AND a terminal color"));
|
||||
background_color.value.rgb_color &= rhs.background_color.value.rgb_color;
|
||||
}
|
||||
|
||||
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) &
|
||||
static_cast<uint8_t>(rhs.ems));
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend FMT_CONSTEXPR text_style operator&(text_style lhs,
|
||||
const text_style& rhs) {
|
||||
return lhs &= rhs;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR bool has_foreground() const FMT_NOEXCEPT {
|
||||
return set_foreground_color;
|
||||
}
|
||||
FMT_CONSTEXPR bool has_background() const FMT_NOEXCEPT {
|
||||
return set_background_color;
|
||||
}
|
||||
FMT_CONSTEXPR bool has_emphasis() const FMT_NOEXCEPT {
|
||||
return static_cast<uint8_t>(ems) != 0;
|
||||
}
|
||||
FMT_CONSTEXPR detail::color_type get_foreground() const FMT_NOEXCEPT {
|
||||
FMT_ASSERT(has_foreground(), "no foreground specified for this style");
|
||||
return foreground_color;
|
||||
}
|
||||
FMT_CONSTEXPR detail::color_type get_background() const FMT_NOEXCEPT {
|
||||
FMT_ASSERT(has_background(), "no background specified for this style");
|
||||
return background_color;
|
||||
}
|
||||
FMT_CONSTEXPR emphasis get_emphasis() const FMT_NOEXCEPT {
|
||||
FMT_ASSERT(has_emphasis(), "no emphasis specified for this style");
|
||||
return ems;
|
||||
}
|
||||
|
||||
private:
|
||||
FMT_CONSTEXPR text_style(bool is_foreground,
|
||||
detail::color_type text_color) FMT_NOEXCEPT
|
||||
: set_foreground_color(),
|
||||
set_background_color(),
|
||||
ems() {
|
||||
if (is_foreground) {
|
||||
foreground_color = text_color;
|
||||
set_foreground_color = true;
|
||||
} else {
|
||||
background_color = text_color;
|
||||
set_background_color = true;
|
||||
}
|
||||
}
|
||||
|
||||
friend FMT_CONSTEXPR_DECL text_style fg(detail::color_type foreground)
|
||||
FMT_NOEXCEPT;
|
||||
friend FMT_CONSTEXPR_DECL text_style bg(detail::color_type background)
|
||||
FMT_NOEXCEPT;
|
||||
|
||||
detail::color_type foreground_color;
|
||||
detail::color_type background_color;
|
||||
bool set_foreground_color;
|
||||
bool set_background_color;
|
||||
emphasis ems;
|
||||
};
|
||||
|
||||
FMT_CONSTEXPR text_style fg(detail::color_type foreground) FMT_NOEXCEPT {
|
||||
return text_style(/*is_foreground=*/true, foreground);
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR text_style bg(detail::color_type background) FMT_NOEXCEPT {
|
||||
return text_style(/*is_foreground=*/false, background);
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR text_style operator|(emphasis lhs, emphasis rhs) FMT_NOEXCEPT {
|
||||
return text_style(lhs) | rhs;
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename Char> struct ansi_color_escape {
|
||||
FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color,
|
||||
const char* esc) FMT_NOEXCEPT {
|
||||
// If we have a terminal color, we need to output another escape code
|
||||
// sequence.
|
||||
if (!text_color.is_rgb) {
|
||||
bool is_background = esc == detail::data::background_color;
|
||||
uint32_t value = text_color.value.term_color;
|
||||
// Background ASCII codes are the same as the foreground ones but with
|
||||
// 10 more.
|
||||
if (is_background) value += 10u;
|
||||
|
||||
size_t index = 0;
|
||||
buffer[index++] = static_cast<Char>('\x1b');
|
||||
buffer[index++] = static_cast<Char>('[');
|
||||
|
||||
if (value >= 100u) {
|
||||
buffer[index++] = static_cast<Char>('1');
|
||||
value %= 100u;
|
||||
}
|
||||
buffer[index++] = static_cast<Char>('0' + value / 10u);
|
||||
buffer[index++] = static_cast<Char>('0' + value % 10u);
|
||||
|
||||
buffer[index++] = static_cast<Char>('m');
|
||||
buffer[index++] = static_cast<Char>('\0');
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 7; i++) {
|
||||
buffer[i] = static_cast<Char>(esc[i]);
|
||||
}
|
||||
rgb color(text_color.value.rgb_color);
|
||||
to_esc(color.r, buffer + 7, ';');
|
||||
to_esc(color.g, buffer + 11, ';');
|
||||
to_esc(color.b, buffer + 15, 'm');
|
||||
buffer[19] = static_cast<Char>(0);
|
||||
}
|
||||
FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT {
|
||||
uint8_t em_codes[4] = {};
|
||||
uint8_t em_bits = static_cast<uint8_t>(em);
|
||||
if (em_bits & static_cast<uint8_t>(emphasis::bold)) em_codes[0] = 1;
|
||||
if (em_bits & static_cast<uint8_t>(emphasis::italic)) em_codes[1] = 3;
|
||||
if (em_bits & static_cast<uint8_t>(emphasis::underline)) em_codes[2] = 4;
|
||||
if (em_bits & static_cast<uint8_t>(emphasis::strikethrough))
|
||||
em_codes[3] = 9;
|
||||
|
||||
size_t index = 0;
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
if (!em_codes[i]) continue;
|
||||
buffer[index++] = static_cast<Char>('\x1b');
|
||||
buffer[index++] = static_cast<Char>('[');
|
||||
buffer[index++] = static_cast<Char>('0' + em_codes[i]);
|
||||
buffer[index++] = static_cast<Char>('m');
|
||||
}
|
||||
buffer[index++] = static_cast<Char>(0);
|
||||
}
|
||||
FMT_CONSTEXPR operator const Char*() const FMT_NOEXCEPT { return buffer; }
|
||||
|
||||
FMT_CONSTEXPR const Char* begin() const FMT_NOEXCEPT { return buffer; }
|
||||
FMT_CONSTEXPR const Char* end() const FMT_NOEXCEPT {
|
||||
return buffer + std::char_traits<Char>::length(buffer);
|
||||
}
|
||||
|
||||
private:
|
||||
Char buffer[7u + 3u * 4u + 1u];
|
||||
|
||||
static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out,
|
||||
char delimiter) FMT_NOEXCEPT {
|
||||
out[0] = static_cast<Char>('0' + c / 100);
|
||||
out[1] = static_cast<Char>('0' + c / 10 % 10);
|
||||
out[2] = static_cast<Char>('0' + c % 10);
|
||||
out[3] = static_cast<Char>(delimiter);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char>
|
||||
FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(
|
||||
detail::color_type foreground) FMT_NOEXCEPT {
|
||||
return ansi_color_escape<Char>(foreground, detail::data::foreground_color);
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
FMT_CONSTEXPR ansi_color_escape<Char> make_background_color(
|
||||
detail::color_type background) FMT_NOEXCEPT {
|
||||
return ansi_color_escape<Char>(background, detail::data::background_color);
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
FMT_CONSTEXPR ansi_color_escape<Char> make_emphasis(emphasis em) FMT_NOEXCEPT {
|
||||
return ansi_color_escape<Char>(em);
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
inline void fputs(const Char* chars, FILE* stream) FMT_NOEXCEPT {
|
||||
std::fputs(chars, stream);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) FMT_NOEXCEPT {
|
||||
std::fputws(chars, stream);
|
||||
}
|
||||
|
||||
template <typename Char> inline void reset_color(FILE* stream) FMT_NOEXCEPT {
|
||||
fputs(detail::data::reset_color, stream);
|
||||
}
|
||||
|
||||
template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT {
|
||||
fputs(detail::data::wreset_color, stream);
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
inline void reset_color(buffer<Char>& buffer) FMT_NOEXCEPT {
|
||||
const char* begin = data::reset_color;
|
||||
const char* end = begin + sizeof(data::reset_color) - 1;
|
||||
buffer.append(begin, end);
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
void vformat_to(buffer<Char>& buf, const text_style& ts,
|
||||
basic_string_view<Char> format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||
bool has_style = false;
|
||||
if (ts.has_emphasis()) {
|
||||
has_style = true;
|
||||
auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis());
|
||||
buf.append(emphasis.begin(), emphasis.end());
|
||||
}
|
||||
if (ts.has_foreground()) {
|
||||
has_style = true;
|
||||
auto foreground = detail::make_foreground_color<Char>(ts.get_foreground());
|
||||
buf.append(foreground.begin(), foreground.end());
|
||||
}
|
||||
if (ts.has_background()) {
|
||||
has_style = true;
|
||||
auto background = detail::make_background_color<Char>(ts.get_background());
|
||||
buf.append(background.begin(), background.end());
|
||||
}
|
||||
detail::vformat_to(buf, format_str, args);
|
||||
if (has_style) detail::reset_color<Char>(buf);
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
template <typename S, typename Char = char_t<S>>
|
||||
void vprint(std::FILE* f, const text_style& ts, const S& format,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||
basic_memory_buffer<Char> buf;
|
||||
detail::vformat_to(buf, ts, to_string_view(format), args);
|
||||
buf.push_back(Char(0));
|
||||
detail::fputs(buf.data(), f);
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Formats a string and prints it to the specified file stream using ANSI
|
||||
escape sequences to specify text formatting.
|
||||
|
||||
**Example**::
|
||||
|
||||
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
|
||||
"Elapsed time: {0:.2f} seconds", 1.23);
|
||||
\endrst
|
||||
*/
|
||||
template <typename S, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_string<S>::value)>
|
||||
void print(std::FILE* f, const text_style& ts, const S& format_str,
|
||||
const Args&... args) {
|
||||
vprint(f, ts, format_str,
|
||||
fmt::make_args_checked<Args...>(format_str, args...));
|
||||
}
|
||||
|
||||
/**
|
||||
Formats a string and prints it to stdout using ANSI escape sequences to
|
||||
specify text formatting.
|
||||
Example:
|
||||
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
|
||||
"Elapsed time: {0:.2f} seconds", 1.23);
|
||||
*/
|
||||
template <typename S, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_string<S>::value)>
|
||||
void print(const text_style& ts, const S& format_str, const Args&... args) {
|
||||
return print(stdout, ts, format_str, args...);
|
||||
}
|
||||
|
||||
template <typename S, typename Char = char_t<S>>
|
||||
inline std::basic_string<Char> vformat(
|
||||
const text_style& ts, const S& format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||
basic_memory_buffer<Char> buf;
|
||||
detail::vformat_to(buf, ts, to_string_view(format_str), args);
|
||||
return fmt::to_string(buf);
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Formats arguments and returns the result as a string using ANSI
|
||||
escape sequences to specify text formatting.
|
||||
|
||||
**Example**::
|
||||
|
||||
#include <fmt/color.h>
|
||||
std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red),
|
||||
"The answer is {}", 42);
|
||||
\endrst
|
||||
*/
|
||||
template <typename S, typename... Args, typename Char = char_t<S>>
|
||||
inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
|
||||
const Args&... args) {
|
||||
return vformat(ts, to_string_view(format_str),
|
||||
fmt::make_args_checked<Args...>(format_str, args...));
|
||||
}
|
||||
|
||||
/**
|
||||
Formats a string with the given text_style and writes the output to ``out``.
|
||||
*/
|
||||
template <typename OutputIt, typename Char,
|
||||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt>::value)>
|
||||
OutputIt vformat_to(
|
||||
OutputIt out, const text_style& ts, basic_string_view<Char> format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||
decltype(detail::get_buffer<Char>(out)) buf(detail::get_buffer_init(out));
|
||||
detail::vformat_to(buf, ts, format_str, args);
|
||||
return detail::get_iterator(buf);
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Formats arguments with the given text_style, writes the result to the output
|
||||
iterator ``out`` and returns the iterator past the end of the output range.
|
||||
|
||||
**Example**::
|
||||
|
||||
std::vector<char> out;
|
||||
fmt::format_to(std::back_inserter(out),
|
||||
fmt::emphasis::bold | fg(fmt::color::red), "{}", 42);
|
||||
\endrst
|
||||
*/
|
||||
template <typename OutputIt, typename S, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt>::value&&
|
||||
detail::is_string<S>::value)>
|
||||
inline OutputIt format_to(OutputIt out, const text_style& ts,
|
||||
const S& format_str, Args&&... args) {
|
||||
return vformat_to(out, ts, to_string_view(format_str),
|
||||
fmt::make_args_checked<Args...>(format_str, args...));
|
||||
}
|
||||
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_COLOR_H_
|
||||
699
src/3rdparty/fmt/compile.h
vendored
Normal file
699
src/3rdparty/fmt/compile.h
vendored
Normal file
@@ -0,0 +1,699 @@
|
||||
// Formatting library for C++ - experimental format string compilation
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich and fmt contributors
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_COMPILE_H_
|
||||
#define FMT_COMPILE_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "format.h"
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
namespace detail {
|
||||
|
||||
// A compile-time string which is compiled into fast formatting code.
|
||||
class compiled_string {};
|
||||
|
||||
template <typename S>
|
||||
struct is_compiled_string : std::is_base_of<compiled_string, S> {};
|
||||
|
||||
/**
|
||||
\rst
|
||||
Converts a string literal *s* into a format string that will be parsed at
|
||||
compile time and converted into efficient formatting code. Requires C++17
|
||||
``constexpr if`` compiler support.
|
||||
|
||||
**Example**::
|
||||
|
||||
// Converts 42 into std::string using the most efficient method and no
|
||||
// runtime format string processing.
|
||||
std::string s = fmt::format(FMT_COMPILE("{}"), 42);
|
||||
\endrst
|
||||
*/
|
||||
#define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::detail::compiled_string)
|
||||
|
||||
template <typename T, typename... Tail>
|
||||
const T& first(const T& value, const Tail&...) {
|
||||
return value;
|
||||
}
|
||||
|
||||
// Part of a compiled format string. It can be either literal text or a
|
||||
// replacement field.
|
||||
template <typename Char> struct format_part {
|
||||
enum class kind { arg_index, arg_name, text, replacement };
|
||||
|
||||
struct replacement {
|
||||
arg_ref<Char> arg_id;
|
||||
dynamic_format_specs<Char> specs;
|
||||
};
|
||||
|
||||
kind part_kind;
|
||||
union value {
|
||||
int arg_index;
|
||||
basic_string_view<Char> str;
|
||||
replacement repl;
|
||||
|
||||
FMT_CONSTEXPR value(int index = 0) : arg_index(index) {}
|
||||
FMT_CONSTEXPR value(basic_string_view<Char> s) : str(s) {}
|
||||
FMT_CONSTEXPR value(replacement r) : repl(r) {}
|
||||
} val;
|
||||
// Position past the end of the argument id.
|
||||
const Char* arg_id_end = nullptr;
|
||||
|
||||
FMT_CONSTEXPR format_part(kind k = kind::arg_index, value v = {})
|
||||
: part_kind(k), val(v) {}
|
||||
|
||||
static FMT_CONSTEXPR format_part make_arg_index(int index) {
|
||||
return format_part(kind::arg_index, index);
|
||||
}
|
||||
static FMT_CONSTEXPR format_part make_arg_name(basic_string_view<Char> name) {
|
||||
return format_part(kind::arg_name, name);
|
||||
}
|
||||
static FMT_CONSTEXPR format_part make_text(basic_string_view<Char> text) {
|
||||
return format_part(kind::text, text);
|
||||
}
|
||||
static FMT_CONSTEXPR format_part make_replacement(replacement repl) {
|
||||
return format_part(kind::replacement, repl);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char> struct part_counter {
|
||||
unsigned num_parts = 0;
|
||||
|
||||
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
|
||||
if (begin != end) ++num_parts;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR int on_arg_id() { return ++num_parts, 0; }
|
||||
FMT_CONSTEXPR int on_arg_id(int) { return ++num_parts, 0; }
|
||||
FMT_CONSTEXPR int on_arg_id(basic_string_view<Char>) {
|
||||
return ++num_parts, 0;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void on_replacement_field(int, const Char*) {}
|
||||
|
||||
FMT_CONSTEXPR const Char* on_format_specs(int, const Char* begin,
|
||||
const Char* end) {
|
||||
// Find the matching brace.
|
||||
unsigned brace_counter = 0;
|
||||
for (; begin != end; ++begin) {
|
||||
if (*begin == '{') {
|
||||
++brace_counter;
|
||||
} else if (*begin == '}') {
|
||||
if (brace_counter == 0u) break;
|
||||
--brace_counter;
|
||||
}
|
||||
}
|
||||
return begin;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void on_error(const char*) {}
|
||||
};
|
||||
|
||||
// Counts the number of parts in a format string.
|
||||
template <typename Char>
|
||||
FMT_CONSTEXPR unsigned count_parts(basic_string_view<Char> format_str) {
|
||||
part_counter<Char> counter;
|
||||
parse_format_string<true>(format_str, counter);
|
||||
return counter.num_parts;
|
||||
}
|
||||
|
||||
template <typename Char, typename PartHandler>
|
||||
class format_string_compiler : public error_handler {
|
||||
private:
|
||||
using part = format_part<Char>;
|
||||
|
||||
PartHandler handler_;
|
||||
part part_;
|
||||
basic_string_view<Char> format_str_;
|
||||
basic_format_parse_context<Char> parse_context_;
|
||||
|
||||
public:
|
||||
FMT_CONSTEXPR format_string_compiler(basic_string_view<Char> format_str,
|
||||
PartHandler handler)
|
||||
: handler_(handler),
|
||||
format_str_(format_str),
|
||||
parse_context_(format_str) {}
|
||||
|
||||
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
|
||||
if (begin != end)
|
||||
handler_(part::make_text({begin, to_unsigned(end - begin)}));
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR int on_arg_id() {
|
||||
part_ = part::make_arg_index(parse_context_.next_arg_id());
|
||||
return 0;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR int on_arg_id(int id) {
|
||||
parse_context_.check_arg_id(id);
|
||||
part_ = part::make_arg_index(id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR int on_arg_id(basic_string_view<Char> id) {
|
||||
part_ = part::make_arg_name(id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void on_replacement_field(int, const Char* ptr) {
|
||||
part_.arg_id_end = ptr;
|
||||
handler_(part_);
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR const Char* on_format_specs(int, const Char* begin,
|
||||
const Char* end) {
|
||||
auto repl = typename part::replacement();
|
||||
dynamic_specs_handler<basic_format_parse_context<Char>> handler(
|
||||
repl.specs, parse_context_);
|
||||
auto it = parse_format_specs(begin, end, handler);
|
||||
if (*it != '}') on_error("missing '}' in format string");
|
||||
repl.arg_id = part_.part_kind == part::kind::arg_index
|
||||
? arg_ref<Char>(part_.val.arg_index)
|
||||
: arg_ref<Char>(part_.val.str);
|
||||
auto part = part::make_replacement(repl);
|
||||
part.arg_id_end = begin;
|
||||
handler_(part);
|
||||
return it;
|
||||
}
|
||||
};
|
||||
|
||||
// Compiles a format string and invokes handler(part) for each parsed part.
|
||||
template <bool IS_CONSTEXPR, typename Char, typename PartHandler>
|
||||
FMT_CONSTEXPR void compile_format_string(basic_string_view<Char> format_str,
|
||||
PartHandler handler) {
|
||||
parse_format_string<IS_CONSTEXPR>(
|
||||
format_str,
|
||||
format_string_compiler<Char, PartHandler>(format_str, handler));
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename Context, typename Id>
|
||||
void format_arg(
|
||||
basic_format_parse_context<typename Context::char_type>& parse_ctx,
|
||||
Context& ctx, Id arg_id) {
|
||||
ctx.advance_to(visit_format_arg(
|
||||
arg_formatter<OutputIt, typename Context::char_type>(ctx, &parse_ctx),
|
||||
ctx.arg(arg_id)));
|
||||
}
|
||||
|
||||
// vformat_to is defined in a subnamespace to prevent ADL.
|
||||
namespace cf {
|
||||
template <typename Context, typename OutputIt, typename CompiledFormat>
|
||||
auto vformat_to(OutputIt out, CompiledFormat& cf,
|
||||
basic_format_args<Context> args) -> typename Context::iterator {
|
||||
using char_type = typename Context::char_type;
|
||||
basic_format_parse_context<char_type> parse_ctx(
|
||||
to_string_view(cf.format_str_));
|
||||
Context ctx(out, args);
|
||||
|
||||
const auto& parts = cf.parts();
|
||||
for (auto part_it = std::begin(parts); part_it != std::end(parts);
|
||||
++part_it) {
|
||||
const auto& part = *part_it;
|
||||
const auto& value = part.val;
|
||||
|
||||
using format_part_t = format_part<char_type>;
|
||||
switch (part.part_kind) {
|
||||
case format_part_t::kind::text: {
|
||||
const auto text = value.str;
|
||||
auto output = ctx.out();
|
||||
auto&& it = reserve(output, text.size());
|
||||
it = std::copy_n(text.begin(), text.size(), it);
|
||||
ctx.advance_to(output);
|
||||
break;
|
||||
}
|
||||
|
||||
case format_part_t::kind::arg_index:
|
||||
advance_to(parse_ctx, part.arg_id_end);
|
||||
detail::format_arg<OutputIt>(parse_ctx, ctx, value.arg_index);
|
||||
break;
|
||||
|
||||
case format_part_t::kind::arg_name:
|
||||
advance_to(parse_ctx, part.arg_id_end);
|
||||
detail::format_arg<OutputIt>(parse_ctx, ctx, value.str);
|
||||
break;
|
||||
|
||||
case format_part_t::kind::replacement: {
|
||||
const auto& arg_id_value = value.repl.arg_id.val;
|
||||
const auto arg = value.repl.arg_id.kind == arg_id_kind::index
|
||||
? ctx.arg(arg_id_value.index)
|
||||
: ctx.arg(arg_id_value.name);
|
||||
|
||||
auto specs = value.repl.specs;
|
||||
|
||||
handle_dynamic_spec<width_checker>(specs.width, specs.width_ref, ctx);
|
||||
handle_dynamic_spec<precision_checker>(specs.precision,
|
||||
specs.precision_ref, ctx);
|
||||
|
||||
error_handler h;
|
||||
numeric_specs_checker<error_handler> checker(h, arg.type());
|
||||
if (specs.align == align::numeric) checker.require_numeric_argument();
|
||||
if (specs.sign != sign::none) checker.check_sign();
|
||||
if (specs.alt) checker.require_numeric_argument();
|
||||
if (specs.precision >= 0) checker.check_precision();
|
||||
|
||||
advance_to(parse_ctx, part.arg_id_end);
|
||||
ctx.advance_to(
|
||||
visit_format_arg(arg_formatter<OutputIt, typename Context::char_type>(
|
||||
ctx, nullptr, &specs),
|
||||
arg));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ctx.out();
|
||||
}
|
||||
} // namespace cf
|
||||
|
||||
struct basic_compiled_format {};
|
||||
|
||||
template <typename S, typename = void>
|
||||
struct compiled_format_base : basic_compiled_format {
|
||||
using char_type = char_t<S>;
|
||||
using parts_container = std::vector<detail::format_part<char_type>>;
|
||||
|
||||
parts_container compiled_parts;
|
||||
|
||||
explicit compiled_format_base(basic_string_view<char_type> format_str) {
|
||||
compile_format_string<false>(format_str,
|
||||
[this](const format_part<char_type>& part) {
|
||||
compiled_parts.push_back(part);
|
||||
});
|
||||
}
|
||||
|
||||
const parts_container& parts() const { return compiled_parts; }
|
||||
};
|
||||
|
||||
template <typename Char, unsigned N> struct format_part_array {
|
||||
format_part<Char> data[N] = {};
|
||||
FMT_CONSTEXPR format_part_array() = default;
|
||||
};
|
||||
|
||||
template <typename Char, unsigned N>
|
||||
FMT_CONSTEXPR format_part_array<Char, N> compile_to_parts(
|
||||
basic_string_view<Char> format_str) {
|
||||
format_part_array<Char, N> parts;
|
||||
unsigned counter = 0;
|
||||
// This is not a lambda for compatibility with older compilers.
|
||||
struct {
|
||||
format_part<Char>* parts;
|
||||
unsigned* counter;
|
||||
FMT_CONSTEXPR void operator()(const format_part<Char>& part) {
|
||||
parts[(*counter)++] = part;
|
||||
}
|
||||
} collector{parts.data, &counter};
|
||||
compile_format_string<true>(format_str, collector);
|
||||
if (counter < N) {
|
||||
parts.data[counter] =
|
||||
format_part<Char>::make_text(basic_string_view<Char>());
|
||||
}
|
||||
return parts;
|
||||
}
|
||||
|
||||
template <typename T> constexpr const T& constexpr_max(const T& a, const T& b) {
|
||||
return (a < b) ? b : a;
|
||||
}
|
||||
|
||||
template <typename S>
|
||||
struct compiled_format_base<S, enable_if_t<is_compile_string<S>::value>>
|
||||
: basic_compiled_format {
|
||||
using char_type = char_t<S>;
|
||||
|
||||
FMT_CONSTEXPR explicit compiled_format_base(basic_string_view<char_type>) {}
|
||||
|
||||
// Workaround for old compilers. Format string compilation will not be
|
||||
// performed there anyway.
|
||||
#if FMT_USE_CONSTEXPR
|
||||
static FMT_CONSTEXPR_DECL const unsigned num_format_parts =
|
||||
constexpr_max(count_parts(to_string_view(S())), 1u);
|
||||
#else
|
||||
static const unsigned num_format_parts = 1;
|
||||
#endif
|
||||
|
||||
using parts_container = format_part<char_type>[num_format_parts];
|
||||
|
||||
const parts_container& parts() const {
|
||||
static FMT_CONSTEXPR_DECL const auto compiled_parts =
|
||||
compile_to_parts<char_type, num_format_parts>(
|
||||
detail::to_string_view(S()));
|
||||
return compiled_parts.data;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename S, typename... Args>
|
||||
class compiled_format : private compiled_format_base<S> {
|
||||
public:
|
||||
using typename compiled_format_base<S>::char_type;
|
||||
|
||||
private:
|
||||
basic_string_view<char_type> format_str_;
|
||||
|
||||
template <typename Context, typename OutputIt, typename CompiledFormat>
|
||||
friend auto cf::vformat_to(OutputIt out, CompiledFormat& cf,
|
||||
basic_format_args<Context> args) ->
|
||||
typename Context::iterator;
|
||||
|
||||
public:
|
||||
compiled_format() = delete;
|
||||
explicit constexpr compiled_format(basic_string_view<char_type> format_str)
|
||||
: compiled_format_base<S>(format_str), format_str_(format_str) {}
|
||||
};
|
||||
|
||||
#ifdef __cpp_if_constexpr
|
||||
template <typename... Args> struct type_list {};
|
||||
|
||||
// Returns a reference to the argument at index N from [first, rest...].
|
||||
template <int N, typename T, typename... Args>
|
||||
constexpr const auto& get([[maybe_unused]] const T& first,
|
||||
[[maybe_unused]] const Args&... rest) {
|
||||
static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
|
||||
if constexpr (N == 0)
|
||||
return first;
|
||||
else
|
||||
return get<N - 1>(rest...);
|
||||
}
|
||||
|
||||
template <int N, typename> struct get_type_impl;
|
||||
|
||||
template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> {
|
||||
using type = remove_cvref_t<decltype(get<N>(std::declval<Args>()...))>;
|
||||
};
|
||||
|
||||
template <int N, typename T>
|
||||
using get_type = typename get_type_impl<N, T>::type;
|
||||
|
||||
template <typename T> struct is_compiled_format : std::false_type {};
|
||||
|
||||
template <typename Char> struct text {
|
||||
basic_string_view<Char> data;
|
||||
using char_type = Char;
|
||||
|
||||
template <typename OutputIt, typename... Args>
|
||||
OutputIt format(OutputIt out, const Args&...) const {
|
||||
return write<Char>(out, data);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char>
|
||||
struct is_compiled_format<text<Char>> : std::true_type {};
|
||||
|
||||
template <typename Char>
|
||||
constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
|
||||
size_t size) {
|
||||
return {{&s[pos], size}};
|
||||
}
|
||||
|
||||
template <typename Char> struct code_unit {
|
||||
Char value;
|
||||
using char_type = Char;
|
||||
|
||||
template <typename OutputIt, typename... Args>
|
||||
OutputIt format(OutputIt out, const Args&...) const {
|
||||
return write<Char>(out, value);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char>
|
||||
struct is_compiled_format<code_unit<Char>> : std::true_type {};
|
||||
|
||||
// A replacement field that refers to argument N.
|
||||
template <typename Char, typename T, int N> struct field {
|
||||
using char_type = Char;
|
||||
|
||||
template <typename OutputIt, typename... Args>
|
||||
OutputIt format(OutputIt out, const Args&... args) const {
|
||||
// This ensures that the argument type is convertile to `const T&`.
|
||||
const T& arg = get<N>(args...);
|
||||
return write<Char>(out, arg);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char, typename T, int N>
|
||||
struct is_compiled_format<field<Char, T, N>> : std::true_type {};
|
||||
|
||||
// A replacement field that refers to argument N and has format specifiers.
|
||||
template <typename Char, typename T, int N> struct spec_field {
|
||||
using char_type = Char;
|
||||
mutable formatter<T, Char> fmt;
|
||||
|
||||
template <typename OutputIt, typename... Args>
|
||||
OutputIt format(OutputIt out, const Args&... args) const {
|
||||
// This ensures that the argument type is convertile to `const T&`.
|
||||
const T& arg = get<N>(args...);
|
||||
const auto& vargs =
|
||||
make_format_args<basic_format_context<OutputIt, Char>>(args...);
|
||||
basic_format_context<OutputIt, Char> ctx(out, vargs);
|
||||
return fmt.format(arg, ctx);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char, typename T, int N>
|
||||
struct is_compiled_format<spec_field<Char, T, N>> : std::true_type {};
|
||||
|
||||
template <typename L, typename R> struct concat {
|
||||
L lhs;
|
||||
R rhs;
|
||||
using char_type = typename L::char_type;
|
||||
|
||||
template <typename OutputIt, typename... Args>
|
||||
OutputIt format(OutputIt out, const Args&... args) const {
|
||||
out = lhs.format(out, args...);
|
||||
return rhs.format(out, args...);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename L, typename R>
|
||||
struct is_compiled_format<concat<L, R>> : std::true_type {};
|
||||
|
||||
template <typename L, typename R>
|
||||
constexpr concat<L, R> make_concat(L lhs, R rhs) {
|
||||
return {lhs, rhs};
|
||||
}
|
||||
|
||||
struct unknown_format {};
|
||||
|
||||
template <typename Char>
|
||||
constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) {
|
||||
for (size_t size = str.size(); pos != size; ++pos) {
|
||||
if (str[pos] == '{' || str[pos] == '}') break;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
template <typename Args, size_t POS, int ID, typename S>
|
||||
constexpr auto compile_format_string(S format_str);
|
||||
|
||||
template <typename Args, size_t POS, int ID, typename T, typename S>
|
||||
constexpr auto parse_tail(T head, S format_str) {
|
||||
if constexpr (POS !=
|
||||
basic_string_view<typename S::char_type>(format_str).size()) {
|
||||
constexpr auto tail = compile_format_string<Args, POS, ID>(format_str);
|
||||
if constexpr (std::is_same<remove_cvref_t<decltype(tail)>,
|
||||
unknown_format>())
|
||||
return tail;
|
||||
else
|
||||
return make_concat(head, tail);
|
||||
} else {
|
||||
return head;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename Char> struct parse_specs_result {
|
||||
formatter<T, Char> fmt;
|
||||
size_t end;
|
||||
int next_arg_id;
|
||||
};
|
||||
|
||||
template <typename T, typename Char>
|
||||
constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
|
||||
size_t pos, int arg_id) {
|
||||
str.remove_prefix(pos);
|
||||
auto ctx = basic_format_parse_context<Char>(str, {}, arg_id + 1);
|
||||
auto f = formatter<T, Char>();
|
||||
auto end = f.parse(ctx);
|
||||
return {f, pos + (end - str.data()) + 1, ctx.next_arg_id()};
|
||||
}
|
||||
|
||||
// Compiles a non-empty format string and returns the compiled representation
|
||||
// or unknown_format() on unrecognized input.
|
||||
template <typename Args, size_t POS, int ID, typename S>
|
||||
constexpr auto compile_format_string(S format_str) {
|
||||
using char_type = typename S::char_type;
|
||||
constexpr basic_string_view<char_type> str = format_str;
|
||||
if constexpr (str[POS] == '{') {
|
||||
if (POS + 1 == str.size())
|
||||
throw format_error("unmatched '{' in format string");
|
||||
if constexpr (str[POS + 1] == '{') {
|
||||
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
|
||||
} else if constexpr (str[POS + 1] == '}') {
|
||||
using type = get_type<ID, Args>;
|
||||
return parse_tail<Args, POS + 2, ID + 1>(field<char_type, type, ID>(),
|
||||
format_str);
|
||||
} else if constexpr (str[POS + 1] == ':') {
|
||||
using type = get_type<ID, Args>;
|
||||
constexpr auto result = parse_specs<type>(str, POS + 2, ID);
|
||||
return parse_tail<Args, result.end, result.next_arg_id>(
|
||||
spec_field<char_type, type, ID>{result.fmt}, format_str);
|
||||
} else {
|
||||
return unknown_format();
|
||||
}
|
||||
} else if constexpr (str[POS] == '}') {
|
||||
if (POS + 1 == str.size())
|
||||
throw format_error("unmatched '}' in format string");
|
||||
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
|
||||
} else {
|
||||
constexpr auto end = parse_text(str, POS + 1);
|
||||
if constexpr (end - POS > 1) {
|
||||
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS),
|
||||
format_str);
|
||||
} else {
|
||||
return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]},
|
||||
format_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... Args, typename S,
|
||||
FMT_ENABLE_IF(is_compile_string<S>::value ||
|
||||
detail::is_compiled_string<S>::value)>
|
||||
constexpr auto compile(S format_str) {
|
||||
constexpr basic_string_view<typename S::char_type> str = format_str;
|
||||
if constexpr (str.size() == 0) {
|
||||
return detail::make_text(str, 0, 0);
|
||||
} else {
|
||||
constexpr auto result =
|
||||
detail::compile_format_string<detail::type_list<Args...>, 0, 0>(
|
||||
format_str);
|
||||
if constexpr (std::is_same<remove_cvref_t<decltype(result)>,
|
||||
detail::unknown_format>()) {
|
||||
return detail::compiled_format<S, Args...>(to_string_view(format_str));
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
template <typename... Args, typename S,
|
||||
FMT_ENABLE_IF(is_compile_string<S>::value)>
|
||||
constexpr auto compile(S format_str) -> detail::compiled_format<S, Args...> {
|
||||
return detail::compiled_format<S, Args...>(to_string_view(format_str));
|
||||
}
|
||||
#endif // __cpp_if_constexpr
|
||||
|
||||
// Compiles the format string which must be a string literal.
|
||||
template <typename... Args, typename Char, size_t N>
|
||||
auto compile(const Char (&format_str)[N])
|
||||
-> detail::compiled_format<const Char*, Args...> {
|
||||
return detail::compiled_format<const Char*, Args...>(
|
||||
basic_string_view<Char>(format_str, N - 1));
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
// DEPRECATED! use FMT_COMPILE instead.
|
||||
template <typename... Args>
|
||||
FMT_DEPRECATED auto compile(const Args&... args)
|
||||
-> decltype(detail::compile(args...)) {
|
||||
return detail::compile(args...);
|
||||
}
|
||||
|
||||
#if FMT_USE_CONSTEXPR
|
||||
# ifdef __cpp_if_constexpr
|
||||
|
||||
template <typename CompiledFormat, typename... Args,
|
||||
typename Char = typename CompiledFormat::char_type,
|
||||
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
|
||||
FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf,
|
||||
const Args&... args) {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
cf.format(detail::buffer_appender<Char>(buffer), args...);
|
||||
return to_string(buffer);
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename CompiledFormat, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
|
||||
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
|
||||
const Args&... args) {
|
||||
return cf.format(out, args...);
|
||||
}
|
||||
# endif // __cpp_if_constexpr
|
||||
#endif // FMT_USE_CONSTEXPR
|
||||
|
||||
template <typename CompiledFormat, typename... Args,
|
||||
typename Char = typename CompiledFormat::char_type,
|
||||
FMT_ENABLE_IF(std::is_base_of<detail::basic_compiled_format,
|
||||
CompiledFormat>::value)>
|
||||
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
using context = buffer_context<Char>;
|
||||
detail::cf::vformat_to<context>(detail::buffer_appender<Char>(buffer), cf,
|
||||
make_format_args<context>(args...));
|
||||
return to_string(buffer);
|
||||
}
|
||||
|
||||
template <typename S, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||
FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
|
||||
Args&&... args) {
|
||||
#ifdef __cpp_if_constexpr
|
||||
if constexpr (std::is_same<typename S::char_type, char>::value) {
|
||||
constexpr basic_string_view<typename S::char_type> str = S();
|
||||
if (str.size() == 2 && str[0] == '{' && str[1] == '}')
|
||||
return fmt::to_string(detail::first(args...));
|
||||
}
|
||||
#endif
|
||||
constexpr auto compiled = detail::compile<Args...>(S());
|
||||
return format(compiled, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename CompiledFormat, typename... Args,
|
||||
FMT_ENABLE_IF(std::is_base_of<detail::basic_compiled_format,
|
||||
CompiledFormat>::value)>
|
||||
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
|
||||
const Args&... args) {
|
||||
using char_type = typename CompiledFormat::char_type;
|
||||
using context = format_context_t<OutputIt, char_type>;
|
||||
return detail::cf::vformat_to<context>(out, cf,
|
||||
make_format_args<context>(args...));
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename S, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||
OutputIt format_to(OutputIt out, const S&, const Args&... args) {
|
||||
constexpr auto compiled = detail::compile<Args...>(S());
|
||||
return format_to(out, compiled, args...);
|
||||
}
|
||||
|
||||
template <
|
||||
typename OutputIt, typename CompiledFormat, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt>::value&& std::is_base_of<
|
||||
detail::basic_compiled_format, CompiledFormat>::value)>
|
||||
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
|
||||
const CompiledFormat& cf,
|
||||
const Args&... args) {
|
||||
auto it =
|
||||
format_to(detail::truncating_iterator<OutputIt>(out, n), cf, args...);
|
||||
return {it.base(), it.count()};
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename S, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n, const S&,
|
||||
const Args&... args) {
|
||||
constexpr auto compiled = detail::compile<Args...>(S());
|
||||
auto it = format_to(detail::truncating_iterator<OutputIt>(out, n), compiled,
|
||||
args...);
|
||||
return {it.base(), it.count()};
|
||||
}
|
||||
|
||||
template <typename CompiledFormat, typename... Args>
|
||||
size_t formatted_size(const CompiledFormat& cf, const Args&... args) {
|
||||
return format_to(detail::counting_iterator(), cf, args...).count();
|
||||
}
|
||||
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_COMPILE_H_
|
||||
2129
src/3rdparty/fmt/core.h
vendored
Normal file
2129
src/3rdparty/fmt/core.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2801
src/3rdparty/fmt/format-inl.h
vendored
Normal file
2801
src/3rdparty/fmt/format-inl.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
69
src/3rdparty/fmt/format.cc
vendored
Normal file
69
src/3rdparty/fmt/format.cc
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
// Formatting library for C++
|
||||
//
|
||||
// Copyright (c) 2012 - 2016, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#include "3rdparty/fmt/format-inl.h"
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
namespace detail {
|
||||
|
||||
template <typename T>
|
||||
int format_float(char* buf, std::size_t size, const char* format, int precision,
|
||||
T value) {
|
||||
#ifdef FMT_FUZZ
|
||||
if (precision > 100000)
|
||||
throw std::runtime_error(
|
||||
"fuzz mode - avoid large allocation inside snprintf");
|
||||
#endif
|
||||
// Suppress the warning about nonliteral format string.
|
||||
int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF;
|
||||
return precision < 0 ? snprintf_ptr(buf, size, format, value)
|
||||
: snprintf_ptr(buf, size, format, precision, value);
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
template struct FMT_INSTANTIATION_DEF_API detail::basic_data<void>;
|
||||
|
||||
// Workaround a bug in MSVC2013 that prevents instantiation of format_float.
|
||||
int (*instantiate_format_float)(double, int, detail::float_specs,
|
||||
detail::buffer<char>&) = detail::format_float;
|
||||
|
||||
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
|
||||
template FMT_API detail::locale_ref::locale_ref(const std::locale& loc);
|
||||
template FMT_API std::locale detail::locale_ref::get<std::locale>() const;
|
||||
#endif
|
||||
|
||||
// Explicit instantiations for char.
|
||||
|
||||
template FMT_API std::string detail::grouping_impl<char>(locale_ref);
|
||||
template FMT_API char detail::thousands_sep_impl(locale_ref);
|
||||
template FMT_API char detail::decimal_point_impl(locale_ref);
|
||||
|
||||
template FMT_API void detail::buffer<char>::append(const char*, const char*);
|
||||
|
||||
template FMT_API FMT_BUFFER_CONTEXT(char)::iterator detail::vformat_to(
|
||||
detail::buffer<char>&, string_view,
|
||||
basic_format_args<FMT_BUFFER_CONTEXT(char)>);
|
||||
|
||||
template FMT_API int detail::snprintf_float(double, int, detail::float_specs,
|
||||
detail::buffer<char>&);
|
||||
template FMT_API int detail::snprintf_float(long double, int,
|
||||
detail::float_specs,
|
||||
detail::buffer<char>&);
|
||||
template FMT_API int detail::format_float(double, int, detail::float_specs,
|
||||
detail::buffer<char>&);
|
||||
template FMT_API int detail::format_float(long double, int, detail::float_specs,
|
||||
detail::buffer<char>&);
|
||||
|
||||
// Explicit instantiations for wchar_t.
|
||||
|
||||
template FMT_API std::string detail::grouping_impl<wchar_t>(locale_ref);
|
||||
template FMT_API wchar_t detail::thousands_sep_impl(locale_ref);
|
||||
template FMT_API wchar_t detail::decimal_point_impl(locale_ref);
|
||||
|
||||
template FMT_API void detail::buffer<wchar_t>::append(const wchar_t*,
|
||||
const wchar_t*);
|
||||
FMT_END_NAMESPACE
|
||||
3869
src/3rdparty/fmt/format.h
vendored
Normal file
3869
src/3rdparty/fmt/format.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
78
src/3rdparty/fmt/locale.h
vendored
Normal file
78
src/3rdparty/fmt/locale.h
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
// Formatting library for C++ - std::locale support
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_LOCALE_H_
|
||||
#define FMT_LOCALE_H_
|
||||
|
||||
#include <locale>
|
||||
|
||||
#include "format.h"
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
|
||||
namespace detail {
|
||||
template <typename Char>
|
||||
typename buffer_context<Char>::iterator vformat_to(
|
||||
const std::locale& loc, buffer<Char>& buf,
|
||||
basic_string_view<Char> format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||
using af = arg_formatter<typename buffer_context<Char>::iterator, Char>;
|
||||
return vformat_to<af>(buffer_appender<Char>(buf), to_string_view(format_str),
|
||||
args, detail::locale_ref(loc));
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
std::basic_string<Char> vformat(
|
||||
const std::locale& loc, basic_string_view<Char> format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
detail::vformat_to(loc, buffer, format_str, args);
|
||||
return fmt::to_string(buffer);
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
template <typename S, typename Char = char_t<S>>
|
||||
inline std::basic_string<Char> vformat(
|
||||
const std::locale& loc, const S& format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||
return detail::vformat(loc, to_string_view(format_str), args);
|
||||
}
|
||||
|
||||
template <typename S, typename... Args, typename Char = char_t<S>>
|
||||
inline std::basic_string<Char> format(const std::locale& loc,
|
||||
const S& format_str, Args&&... args) {
|
||||
return detail::vformat(
|
||||
loc, to_string_view(format_str),
|
||||
fmt::make_args_checked<Args...>(format_str, args...));
|
||||
}
|
||||
|
||||
template <typename S, typename OutputIt, typename... Args,
|
||||
typename Char = enable_if_t<
|
||||
detail::is_output_iterator<OutputIt>::value, char_t<S>>>
|
||||
inline OutputIt vformat_to(
|
||||
OutputIt out, const std::locale& loc, const S& format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||
decltype(detail::get_buffer<Char>(out)) buf(detail::get_buffer_init(out));
|
||||
using af =
|
||||
detail::arg_formatter<typename buffer_context<Char>::iterator, Char>;
|
||||
vformat_to<af>(detail::buffer_appender<Char>(buf), to_string_view(format_str),
|
||||
args, detail::locale_ref(loc));
|
||||
return detail::get_iterator(buf);
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename S, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt>::value&&
|
||||
detail::is_string<S>::value)>
|
||||
inline OutputIt format_to(OutputIt out, const std::locale& loc,
|
||||
const S& format_str, Args&&... args) {
|
||||
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
|
||||
return vformat_to(out, loc, to_string_view(format_str), vargs);
|
||||
}
|
||||
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_LOCALE_H_
|
||||
322
src/3rdparty/fmt/os.cc
vendored
Normal file
322
src/3rdparty/fmt/os.cc
vendored
Normal file
@@ -0,0 +1,322 @@
|
||||
// Formatting library for C++ - optional OS-specific functionality
|
||||
//
|
||||
// Copyright (c) 2012 - 2016, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
// Disable bogus MSVC warnings.
|
||||
#if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER)
|
||||
# define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#include "fmt/os.h"
|
||||
|
||||
#include <climits>
|
||||
|
||||
#if FMT_USE_FCNTL
|
||||
# include <sys/stat.h>
|
||||
# include <sys/types.h>
|
||||
|
||||
# ifndef _WIN32
|
||||
# include <unistd.h>
|
||||
# else
|
||||
# ifndef WIN32_LEAN_AND_MEAN
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# endif
|
||||
# include <io.h>
|
||||
# include <windows.h>
|
||||
|
||||
# define O_CREAT _O_CREAT
|
||||
# define O_TRUNC _O_TRUNC
|
||||
|
||||
# ifndef S_IRUSR
|
||||
# define S_IRUSR _S_IREAD
|
||||
# endif
|
||||
|
||||
# ifndef S_IWUSR
|
||||
# define S_IWUSR _S_IWRITE
|
||||
# endif
|
||||
|
||||
# ifdef __MINGW32__
|
||||
# define _SH_DENYNO 0x40
|
||||
# endif
|
||||
# endif // _WIN32
|
||||
#endif // FMT_USE_FCNTL
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <windows.h>
|
||||
#endif
|
||||
|
||||
#ifdef fileno
|
||||
# undef fileno
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
#ifdef _WIN32
|
||||
// Return type of read and write functions.
|
||||
using RWResult = int;
|
||||
|
||||
// On Windows the count argument to read and write is unsigned, so convert
|
||||
// it from size_t preventing integer overflow.
|
||||
inline unsigned convert_rwcount(std::size_t count) {
|
||||
return count <= UINT_MAX ? static_cast<unsigned>(count) : UINT_MAX;
|
||||
}
|
||||
#else
|
||||
// Return type of read and write functions.
|
||||
using RWResult = ssize_t;
|
||||
|
||||
inline std::size_t convert_rwcount(std::size_t count) { return count; }
|
||||
#endif
|
||||
} // namespace
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
|
||||
#ifdef _WIN32
|
||||
detail::utf16_to_utf8::utf16_to_utf8(wstring_view s) {
|
||||
if (int error_code = convert(s)) {
|
||||
FMT_THROW(windows_error(error_code,
|
||||
"cannot convert string from UTF-16 to UTF-8"));
|
||||
}
|
||||
}
|
||||
|
||||
int detail::utf16_to_utf8::convert(wstring_view s) {
|
||||
if (s.size() > INT_MAX) return ERROR_INVALID_PARAMETER;
|
||||
int s_size = static_cast<int>(s.size());
|
||||
if (s_size == 0) {
|
||||
// WideCharToMultiByte does not support zero length, handle separately.
|
||||
buffer_.resize(1);
|
||||
buffer_[0] = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, nullptr, 0,
|
||||
nullptr, nullptr);
|
||||
if (length == 0) return GetLastError();
|
||||
buffer_.resize(length + 1);
|
||||
length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, &buffer_[0],
|
||||
length, nullptr, nullptr);
|
||||
if (length == 0) return GetLastError();
|
||||
buffer_[length] = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void windows_error::init(int err_code, string_view format_str,
|
||||
format_args args) {
|
||||
error_code_ = err_code;
|
||||
memory_buffer buffer;
|
||||
detail::format_windows_error(buffer, err_code, vformat(format_str, args));
|
||||
std::runtime_error& base = *this;
|
||||
base = std::runtime_error(to_string(buffer));
|
||||
}
|
||||
|
||||
void detail::format_windows_error(detail::buffer<char>& out, int error_code,
|
||||
string_view message) FMT_NOEXCEPT {
|
||||
FMT_TRY {
|
||||
wmemory_buffer buf;
|
||||
buf.resize(inline_buffer_size);
|
||||
for (;;) {
|
||||
wchar_t* system_message = &buf[0];
|
||||
int result = FormatMessageW(
|
||||
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr,
|
||||
error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), system_message,
|
||||
static_cast<uint32_t>(buf.size()), nullptr);
|
||||
if (result != 0) {
|
||||
utf16_to_utf8 utf8_message;
|
||||
if (utf8_message.convert(system_message) == ERROR_SUCCESS) {
|
||||
format_to(buffer_appender<char>(out), "{}: {}", message,
|
||||
utf8_message);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
|
||||
break; // Can't get error message, report error code instead.
|
||||
buf.resize(buf.size() * 2);
|
||||
}
|
||||
}
|
||||
FMT_CATCH(...) {}
|
||||
format_error_code(out, error_code, message);
|
||||
}
|
||||
|
||||
void report_windows_error(int error_code,
|
||||
fmt::string_view message) FMT_NOEXCEPT {
|
||||
report_error(detail::format_windows_error, error_code, message);
|
||||
}
|
||||
#endif // _WIN32
|
||||
|
||||
buffered_file::~buffered_file() FMT_NOEXCEPT {
|
||||
if (file_ && FMT_SYSTEM(fclose(file_)) != 0)
|
||||
report_system_error(errno, "cannot close file");
|
||||
}
|
||||
|
||||
buffered_file::buffered_file(cstring_view filename, cstring_view mode) {
|
||||
FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())),
|
||||
nullptr);
|
||||
if (!file_)
|
||||
FMT_THROW(system_error(errno, "cannot open file {}", filename.c_str()));
|
||||
}
|
||||
|
||||
void buffered_file::close() {
|
||||
if (!file_) return;
|
||||
int result = FMT_SYSTEM(fclose(file_));
|
||||
file_ = nullptr;
|
||||
if (result != 0) FMT_THROW(system_error(errno, "cannot close file"));
|
||||
}
|
||||
|
||||
// A macro used to prevent expansion of fileno on broken versions of MinGW.
|
||||
#define FMT_ARGS
|
||||
|
||||
int buffered_file::fileno() const {
|
||||
int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_));
|
||||
if (fd == -1) FMT_THROW(system_error(errno, "cannot get file descriptor"));
|
||||
return fd;
|
||||
}
|
||||
|
||||
#if FMT_USE_FCNTL
|
||||
file::file(cstring_view path, int oflag) {
|
||||
int mode = S_IRUSR | S_IWUSR;
|
||||
# if defined(_WIN32) && !defined(__MINGW32__)
|
||||
fd_ = -1;
|
||||
FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode));
|
||||
# else
|
||||
FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, mode)));
|
||||
# endif
|
||||
if (fd_ == -1)
|
||||
FMT_THROW(system_error(errno, "cannot open file {}", path.c_str()));
|
||||
}
|
||||
|
||||
file::~file() FMT_NOEXCEPT {
|
||||
// Don't retry close in case of EINTR!
|
||||
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
|
||||
if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0)
|
||||
report_system_error(errno, "cannot close file");
|
||||
}
|
||||
|
||||
void file::close() {
|
||||
if (fd_ == -1) return;
|
||||
// Don't retry close in case of EINTR!
|
||||
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
|
||||
int result = FMT_POSIX_CALL(close(fd_));
|
||||
fd_ = -1;
|
||||
if (result != 0) FMT_THROW(system_error(errno, "cannot close file"));
|
||||
}
|
||||
|
||||
long long file::size() const {
|
||||
# ifdef _WIN32
|
||||
// Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT
|
||||
// is less than 0x0500 as is the case with some default MinGW builds.
|
||||
// Both functions support large file sizes.
|
||||
DWORD size_upper = 0;
|
||||
HANDLE handle = reinterpret_cast<HANDLE>(_get_osfhandle(fd_));
|
||||
DWORD size_lower = FMT_SYSTEM(GetFileSize(handle, &size_upper));
|
||||
if (size_lower == INVALID_FILE_SIZE) {
|
||||
DWORD error = GetLastError();
|
||||
if (error != NO_ERROR)
|
||||
FMT_THROW(windows_error(GetLastError(), "cannot get file size"));
|
||||
}
|
||||
unsigned long long long_size = size_upper;
|
||||
return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower;
|
||||
# else
|
||||
using Stat = struct stat;
|
||||
Stat file_stat = Stat();
|
||||
if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1)
|
||||
FMT_THROW(system_error(errno, "cannot get file attributes"));
|
||||
static_assert(sizeof(long long) >= sizeof(file_stat.st_size),
|
||||
"return type of file::size is not large enough");
|
||||
return file_stat.st_size;
|
||||
# endif
|
||||
}
|
||||
|
||||
std::size_t file::read(void* buffer, std::size_t count) {
|
||||
RWResult result = 0;
|
||||
FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count))));
|
||||
if (result < 0) FMT_THROW(system_error(errno, "cannot read from file"));
|
||||
return detail::to_unsigned(result);
|
||||
}
|
||||
|
||||
std::size_t file::write(const void* buffer, std::size_t count) {
|
||||
RWResult result = 0;
|
||||
FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count))));
|
||||
if (result < 0) FMT_THROW(system_error(errno, "cannot write to file"));
|
||||
return detail::to_unsigned(result);
|
||||
}
|
||||
|
||||
file file::dup(int fd) {
|
||||
// Don't retry as dup doesn't return EINTR.
|
||||
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html
|
||||
int new_fd = FMT_POSIX_CALL(dup(fd));
|
||||
if (new_fd == -1)
|
||||
FMT_THROW(system_error(errno, "cannot duplicate file descriptor {}", fd));
|
||||
return file(new_fd);
|
||||
}
|
||||
|
||||
void file::dup2(int fd) {
|
||||
int result = 0;
|
||||
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
|
||||
if (result == -1) {
|
||||
FMT_THROW(system_error(errno, "cannot duplicate file descriptor {} to {}",
|
||||
fd_, fd));
|
||||
}
|
||||
}
|
||||
|
||||
void file::dup2(int fd, error_code& ec) FMT_NOEXCEPT {
|
||||
int result = 0;
|
||||
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
|
||||
if (result == -1) ec = error_code(errno);
|
||||
}
|
||||
|
||||
void file::pipe(file& read_end, file& write_end) {
|
||||
// Close the descriptors first to make sure that assignments don't throw
|
||||
// and there are no leaks.
|
||||
read_end.close();
|
||||
write_end.close();
|
||||
int fds[2] = {};
|
||||
# ifdef _WIN32
|
||||
// Make the default pipe capacity same as on Linux 2.6.11+.
|
||||
enum { DEFAULT_CAPACITY = 65536 };
|
||||
int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY));
|
||||
# else
|
||||
// Don't retry as the pipe function doesn't return EINTR.
|
||||
// http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html
|
||||
int result = FMT_POSIX_CALL(pipe(fds));
|
||||
# endif
|
||||
if (result != 0) FMT_THROW(system_error(errno, "cannot create pipe"));
|
||||
// The following assignments don't throw because read_fd and write_fd
|
||||
// are closed.
|
||||
read_end = file(fds[0]);
|
||||
write_end = file(fds[1]);
|
||||
}
|
||||
|
||||
buffered_file file::fdopen(const char* mode) {
|
||||
// Don't retry as fdopen doesn't return EINTR.
|
||||
# if defined(__MINGW32__) && defined(_POSIX_)
|
||||
FILE* f = ::fdopen(fd_, mode);
|
||||
# else
|
||||
FILE* f = FMT_POSIX_CALL(fdopen(fd_, mode));
|
||||
# endif
|
||||
if (!f)
|
||||
FMT_THROW(
|
||||
system_error(errno, "cannot associate stream with file descriptor"));
|
||||
buffered_file bf(f);
|
||||
fd_ = -1;
|
||||
return bf;
|
||||
}
|
||||
|
||||
long getpagesize() {
|
||||
# ifdef _WIN32
|
||||
SYSTEM_INFO si;
|
||||
GetSystemInfo(&si);
|
||||
return si.dwPageSize;
|
||||
# else
|
||||
long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE));
|
||||
if (size < 0) FMT_THROW(system_error(errno, "cannot get memory page size"));
|
||||
return size;
|
||||
# endif
|
||||
}
|
||||
|
||||
void ostream::grow(size_t) {
|
||||
if (this->size() == this->capacity()) flush();
|
||||
}
|
||||
#endif // FMT_USE_FCNTL
|
||||
FMT_END_NAMESPACE
|
||||
480
src/3rdparty/fmt/os.h
vendored
Normal file
480
src/3rdparty/fmt/os.h
vendored
Normal file
@@ -0,0 +1,480 @@
|
||||
// Formatting library for C++ - optional OS-specific functionality
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_OS_H_
|
||||
#define FMT_OS_H_
|
||||
|
||||
#if defined(__MINGW32__) || defined(__CYGWIN__)
|
||||
// Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/.
|
||||
# undef __STRICT_ANSI__
|
||||
#endif
|
||||
|
||||
#include <cerrno>
|
||||
#include <clocale> // for locale_t
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <cstdlib> // for strtod_l
|
||||
|
||||
#if defined __APPLE__ || defined(__FreeBSD__)
|
||||
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
|
||||
#endif
|
||||
|
||||
#include "format.h"
|
||||
|
||||
// UWP doesn't provide _pipe.
|
||||
#if FMT_HAS_INCLUDE("winapifamily.h")
|
||||
# include <winapifamily.h>
|
||||
#endif
|
||||
#if (FMT_HAS_INCLUDE(<fcntl.h>) || defined(__APPLE__) || \
|
||||
defined(__linux__)) && \
|
||||
(!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
|
||||
# include <fcntl.h> // for O_RDONLY
|
||||
# define FMT_USE_FCNTL 1
|
||||
#else
|
||||
# define FMT_USE_FCNTL 0
|
||||
#endif
|
||||
|
||||
#ifndef FMT_POSIX
|
||||
# if defined(_WIN32) && !defined(__MINGW32__)
|
||||
// Fix warnings about deprecated symbols.
|
||||
# define FMT_POSIX(call) _##call
|
||||
# else
|
||||
# define FMT_POSIX(call) call
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// Calls to system functions are wrapped in FMT_SYSTEM for testability.
|
||||
#ifdef FMT_SYSTEM
|
||||
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
|
||||
#else
|
||||
# define FMT_SYSTEM(call) ::call
|
||||
# ifdef _WIN32
|
||||
// Fix warnings about deprecated symbols.
|
||||
# define FMT_POSIX_CALL(call) ::_##call
|
||||
# else
|
||||
# define FMT_POSIX_CALL(call) ::call
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// Retries the expression while it evaluates to error_result and errno
|
||||
// equals to EINTR.
|
||||
#ifndef _WIN32
|
||||
# define FMT_RETRY_VAL(result, expression, error_result) \
|
||||
do { \
|
||||
(result) = (expression); \
|
||||
} while ((result) == (error_result) && errno == EINTR)
|
||||
#else
|
||||
# define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
|
||||
#endif
|
||||
|
||||
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
|
||||
/**
|
||||
\rst
|
||||
A reference to a null-terminated string. It can be constructed from a C
|
||||
string or ``std::string``.
|
||||
|
||||
You can use one of the following type aliases for common character types:
|
||||
|
||||
+---------------+-----------------------------+
|
||||
| Type | Definition |
|
||||
+===============+=============================+
|
||||
| cstring_view | basic_cstring_view<char> |
|
||||
+---------------+-----------------------------+
|
||||
| wcstring_view | basic_cstring_view<wchar_t> |
|
||||
+---------------+-----------------------------+
|
||||
|
||||
This class is most useful as a parameter type to allow passing
|
||||
different types of strings to a function, for example::
|
||||
|
||||
template <typename... Args>
|
||||
std::string format(cstring_view format_str, const Args & ... args);
|
||||
|
||||
format("{}", 42);
|
||||
format(std::string("{}"), 42);
|
||||
\endrst
|
||||
*/
|
||||
template <typename Char> class basic_cstring_view {
|
||||
private:
|
||||
const Char* data_;
|
||||
|
||||
public:
|
||||
/** Constructs a string reference object from a C string. */
|
||||
basic_cstring_view(const Char* s) : data_(s) {}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Constructs a string reference from an ``std::string`` object.
|
||||
\endrst
|
||||
*/
|
||||
basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {}
|
||||
|
||||
/** Returns the pointer to a C string. */
|
||||
const Char* c_str() const { return data_; }
|
||||
};
|
||||
|
||||
using cstring_view = basic_cstring_view<char>;
|
||||
using wcstring_view = basic_cstring_view<wchar_t>;
|
||||
|
||||
// An error code.
|
||||
class error_code {
|
||||
private:
|
||||
int value_;
|
||||
|
||||
public:
|
||||
explicit error_code(int value = 0) FMT_NOEXCEPT : value_(value) {}
|
||||
|
||||
int get() const FMT_NOEXCEPT { return value_; }
|
||||
};
|
||||
|
||||
#ifdef _WIN32
|
||||
namespace detail {
|
||||
// A converter from UTF-16 to UTF-8.
|
||||
// It is only provided for Windows since other systems support UTF-8 natively.
|
||||
class utf16_to_utf8 {
|
||||
private:
|
||||
memory_buffer buffer_;
|
||||
|
||||
public:
|
||||
utf16_to_utf8() {}
|
||||
FMT_API explicit utf16_to_utf8(wstring_view s);
|
||||
operator string_view() const { return string_view(&buffer_[0], size()); }
|
||||
size_t size() const { return buffer_.size() - 1; }
|
||||
const char* c_str() const { return &buffer_[0]; }
|
||||
std::string str() const { return std::string(&buffer_[0], size()); }
|
||||
|
||||
// Performs conversion returning a system error code instead of
|
||||
// throwing exception on conversion error. This method may still throw
|
||||
// in case of memory allocation error.
|
||||
FMT_API int convert(wstring_view s);
|
||||
};
|
||||
|
||||
FMT_API void format_windows_error(buffer<char>& out, int error_code,
|
||||
string_view message) FMT_NOEXCEPT;
|
||||
} // namespace detail
|
||||
|
||||
/** A Windows error. */
|
||||
class windows_error : public system_error {
|
||||
private:
|
||||
FMT_API void init(int error_code, string_view format_str, format_args args);
|
||||
|
||||
public:
|
||||
/**
|
||||
\rst
|
||||
Constructs a :class:`fmt::windows_error` object with the description
|
||||
of the form
|
||||
|
||||
.. parsed-literal::
|
||||
*<message>*: *<system-message>*
|
||||
|
||||
where *<message>* is the formatted message and *<system-message>* is the
|
||||
system message corresponding to the error code.
|
||||
*error_code* is a Windows error code as given by ``GetLastError``.
|
||||
If *error_code* is not a valid error code such as -1, the system message
|
||||
will look like "error -1".
|
||||
|
||||
**Example**::
|
||||
|
||||
// This throws a windows_error with the description
|
||||
// cannot open file 'madeup': The system cannot find the file specified.
|
||||
// or similar (system message may vary).
|
||||
const char *filename = "madeup";
|
||||
LPOFSTRUCT of = LPOFSTRUCT();
|
||||
HFILE file = OpenFile(filename, &of, OF_READ);
|
||||
if (file == HFILE_ERROR) {
|
||||
throw fmt::windows_error(GetLastError(),
|
||||
"cannot open file '{}'", filename);
|
||||
}
|
||||
\endrst
|
||||
*/
|
||||
template <typename... Args>
|
||||
windows_error(int error_code, string_view message, const Args&... args) {
|
||||
init(error_code, message, make_format_args(args...));
|
||||
}
|
||||
};
|
||||
|
||||
// Reports a Windows error without throwing an exception.
|
||||
// Can be used to report errors from destructors.
|
||||
FMT_API void report_windows_error(int error_code,
|
||||
string_view message) FMT_NOEXCEPT;
|
||||
#endif // _WIN32
|
||||
|
||||
// A buffered file.
|
||||
class buffered_file {
|
||||
private:
|
||||
FILE* file_;
|
||||
|
||||
friend class file;
|
||||
|
||||
explicit buffered_file(FILE* f) : file_(f) {}
|
||||
|
||||
public:
|
||||
buffered_file(const buffered_file&) = delete;
|
||||
void operator=(const buffered_file&) = delete;
|
||||
|
||||
// Constructs a buffered_file object which doesn't represent any file.
|
||||
buffered_file() FMT_NOEXCEPT : file_(nullptr) {}
|
||||
|
||||
// Destroys the object closing the file it represents if any.
|
||||
FMT_API ~buffered_file() FMT_NOEXCEPT;
|
||||
|
||||
public:
|
||||
buffered_file(buffered_file&& other) FMT_NOEXCEPT : file_(other.file_) {
|
||||
other.file_ = nullptr;
|
||||
}
|
||||
|
||||
buffered_file& operator=(buffered_file&& other) {
|
||||
close();
|
||||
file_ = other.file_;
|
||||
other.file_ = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Opens a file.
|
||||
FMT_API buffered_file(cstring_view filename, cstring_view mode);
|
||||
|
||||
// Closes the file.
|
||||
FMT_API void close();
|
||||
|
||||
// Returns the pointer to a FILE object representing this file.
|
||||
FILE* get() const FMT_NOEXCEPT { return file_; }
|
||||
|
||||
// We place parentheses around fileno to workaround a bug in some versions
|
||||
// of MinGW that define fileno as a macro.
|
||||
FMT_API int(fileno)() const;
|
||||
|
||||
void vprint(string_view format_str, format_args args) {
|
||||
fmt::vprint(file_, format_str, args);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline void print(string_view format_str, const Args&... args) {
|
||||
vprint(format_str, make_format_args(args...));
|
||||
}
|
||||
};
|
||||
|
||||
#if FMT_USE_FCNTL
|
||||
// A file. Closed file is represented by a file object with descriptor -1.
|
||||
// Methods that are not declared with FMT_NOEXCEPT may throw
|
||||
// fmt::system_error in case of failure. Note that some errors such as
|
||||
// closing the file multiple times will cause a crash on Windows rather
|
||||
// than an exception. You can get standard behavior by overriding the
|
||||
// invalid parameter handler with _set_invalid_parameter_handler.
|
||||
class file {
|
||||
private:
|
||||
int fd_; // File descriptor.
|
||||
|
||||
// Constructs a file object with a given descriptor.
|
||||
explicit file(int fd) : fd_(fd) {}
|
||||
|
||||
public:
|
||||
// Possible values for the oflag argument to the constructor.
|
||||
enum {
|
||||
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
|
||||
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
|
||||
RDWR = FMT_POSIX(O_RDWR), // Open for reading and writing.
|
||||
CREATE = FMT_POSIX(O_CREAT), // Create if the file doesn't exist.
|
||||
APPEND = FMT_POSIX(O_APPEND) // Open in append mode.
|
||||
};
|
||||
|
||||
// Constructs a file object which doesn't represent any file.
|
||||
file() FMT_NOEXCEPT : fd_(-1) {}
|
||||
|
||||
// Opens a file and constructs a file object representing this file.
|
||||
FMT_API file(cstring_view path, int oflag);
|
||||
|
||||
public:
|
||||
file(const file&) = delete;
|
||||
void operator=(const file&) = delete;
|
||||
|
||||
file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; }
|
||||
|
||||
file& operator=(file&& other) FMT_NOEXCEPT {
|
||||
close();
|
||||
fd_ = other.fd_;
|
||||
other.fd_ = -1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Destroys the object closing the file it represents if any.
|
||||
FMT_API ~file() FMT_NOEXCEPT;
|
||||
|
||||
// Returns the file descriptor.
|
||||
int descriptor() const FMT_NOEXCEPT { return fd_; }
|
||||
|
||||
// Closes the file.
|
||||
FMT_API void close();
|
||||
|
||||
// Returns the file size. The size has signed type for consistency with
|
||||
// stat::st_size.
|
||||
FMT_API long long size() const;
|
||||
|
||||
// Attempts to read count bytes from the file into the specified buffer.
|
||||
FMT_API size_t read(void* buffer, size_t count);
|
||||
|
||||
// Attempts to write count bytes from the specified buffer to the file.
|
||||
FMT_API size_t write(const void* buffer, size_t count);
|
||||
|
||||
// Duplicates a file descriptor with the dup function and returns
|
||||
// the duplicate as a file object.
|
||||
FMT_API static file dup(int fd);
|
||||
|
||||
// Makes fd be the copy of this file descriptor, closing fd first if
|
||||
// necessary.
|
||||
FMT_API void dup2(int fd);
|
||||
|
||||
// Makes fd be the copy of this file descriptor, closing fd first if
|
||||
// necessary.
|
||||
FMT_API void dup2(int fd, error_code& ec) FMT_NOEXCEPT;
|
||||
|
||||
// Creates a pipe setting up read_end and write_end file objects for reading
|
||||
// and writing respectively.
|
||||
FMT_API static void pipe(file& read_end, file& write_end);
|
||||
|
||||
// Creates a buffered_file object associated with this file and detaches
|
||||
// this file object from the file.
|
||||
FMT_API buffered_file fdopen(const char* mode);
|
||||
};
|
||||
|
||||
// Returns the memory page size.
|
||||
long getpagesize();
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct buffer_size {
|
||||
size_t value = 0;
|
||||
buffer_size operator=(size_t val) const {
|
||||
auto bs = buffer_size();
|
||||
bs.value = val;
|
||||
return bs;
|
||||
}
|
||||
};
|
||||
|
||||
struct ostream_params {
|
||||
int oflag = file::WRONLY | file::CREATE;
|
||||
size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768;
|
||||
|
||||
ostream_params() {}
|
||||
|
||||
template <typename... T>
|
||||
ostream_params(T... params, int oflag) : ostream_params(params...) {
|
||||
this->oflag = oflag;
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
ostream_params(T... params, detail::buffer_size bs)
|
||||
: ostream_params(params...) {
|
||||
this->buffer_size = bs.value;
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
static constexpr detail::buffer_size buffer_size;
|
||||
|
||||
// A fast output stream which is not thread-safe.
|
||||
class ostream : private detail::buffer<char> {
|
||||
private:
|
||||
file file_;
|
||||
|
||||
void flush() {
|
||||
if (size() == 0) return;
|
||||
file_.write(data(), size());
|
||||
clear();
|
||||
}
|
||||
|
||||
void grow(size_t) final;
|
||||
|
||||
ostream(cstring_view path, const detail::ostream_params& params)
|
||||
: file_(path, params.oflag) {
|
||||
set(new char[params.buffer_size], params.buffer_size);
|
||||
}
|
||||
|
||||
public:
|
||||
ostream(ostream&& other)
|
||||
: detail::buffer<char>(other.data(), other.size(), other.capacity()),
|
||||
file_(std::move(other.file_)) {
|
||||
other.set(nullptr, 0);
|
||||
}
|
||||
~ostream() {
|
||||
flush();
|
||||
delete[] data();
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
friend ostream output_file(cstring_view path, T... params);
|
||||
|
||||
void close() {
|
||||
flush();
|
||||
file_.close();
|
||||
}
|
||||
|
||||
template <typename S, typename... Args>
|
||||
void print(const S& format_str, const Args&... args) {
|
||||
format_to(detail::buffer_appender<char>(*this), format_str, args...);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
Opens a file for writing. Supported parameters passed in `params`:
|
||||
* ``<integer>``: Output flags (``file::WRONLY | file::CREATE`` by default)
|
||||
* ``buffer_size=<integer>``: Output buffer size
|
||||
*/
|
||||
template <typename... T>
|
||||
inline ostream output_file(cstring_view path, T... params) {
|
||||
return {path, detail::ostream_params(params...)};
|
||||
}
|
||||
#endif // FMT_USE_FCNTL
|
||||
|
||||
#ifdef FMT_LOCALE
|
||||
// A "C" numeric locale.
|
||||
class locale {
|
||||
private:
|
||||
# ifdef _WIN32
|
||||
using locale_t = _locale_t;
|
||||
|
||||
static void freelocale(locale_t loc) { _free_locale(loc); }
|
||||
|
||||
static double strtod_l(const char* nptr, char** endptr, _locale_t loc) {
|
||||
return _strtod_l(nptr, endptr, loc);
|
||||
}
|
||||
# endif
|
||||
|
||||
locale_t locale_;
|
||||
|
||||
public:
|
||||
using type = locale_t;
|
||||
locale(const locale&) = delete;
|
||||
void operator=(const locale&) = delete;
|
||||
|
||||
locale() {
|
||||
# ifndef _WIN32
|
||||
locale_ = FMT_SYSTEM(newlocale(LC_NUMERIC_MASK, "C", nullptr));
|
||||
# else
|
||||
locale_ = _create_locale(LC_NUMERIC, "C");
|
||||
# endif
|
||||
if (!locale_) FMT_THROW(system_error(errno, "cannot create locale"));
|
||||
}
|
||||
~locale() { freelocale(locale_); }
|
||||
|
||||
type get() const { return locale_; }
|
||||
|
||||
// Converts string to floating-point number and advances str past the end
|
||||
// of the parsed input.
|
||||
double strtod(const char*& str) const {
|
||||
char* end = nullptr;
|
||||
double result = strtod_l(str, &end, locale_);
|
||||
str = end;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
using Locale FMT_DEPRECATED_ALIAS = locale;
|
||||
#endif // FMT_LOCALE
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_OS_H_
|
||||
177
src/3rdparty/fmt/ostream.h
vendored
Normal file
177
src/3rdparty/fmt/ostream.h
vendored
Normal file
@@ -0,0 +1,177 @@
|
||||
// Formatting library for C++ - std::ostream support
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_OSTREAM_H_
|
||||
#define FMT_OSTREAM_H_
|
||||
|
||||
#include <ostream>
|
||||
|
||||
#include "format.h"
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
|
||||
template <typename Char> class basic_printf_parse_context;
|
||||
template <typename OutputIt, typename Char> class basic_printf_context;
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <class Char> class formatbuf : public std::basic_streambuf<Char> {
|
||||
private:
|
||||
using int_type = typename std::basic_streambuf<Char>::int_type;
|
||||
using traits_type = typename std::basic_streambuf<Char>::traits_type;
|
||||
|
||||
buffer<Char>& buffer_;
|
||||
|
||||
public:
|
||||
formatbuf(buffer<Char>& buf) : buffer_(buf) {}
|
||||
|
||||
protected:
|
||||
// The put-area is actually always empty. This makes the implementation
|
||||
// simpler and has the advantage that the streambuf and the buffer are always
|
||||
// in sync and sputc never writes into uninitialized memory. The obvious
|
||||
// disadvantage is that each call to sputc always results in a (virtual) call
|
||||
// to overflow. There is no disadvantage here for sputn since this always
|
||||
// results in a call to xsputn.
|
||||
|
||||
int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE {
|
||||
if (!traits_type::eq_int_type(ch, traits_type::eof()))
|
||||
buffer_.push_back(static_cast<Char>(ch));
|
||||
return ch;
|
||||
}
|
||||
|
||||
std::streamsize xsputn(const Char* s, std::streamsize count) FMT_OVERRIDE {
|
||||
buffer_.append(s, s + count);
|
||||
return count;
|
||||
}
|
||||
};
|
||||
|
||||
struct converter {
|
||||
template <typename T, FMT_ENABLE_IF(is_integral<T>::value)> converter(T);
|
||||
};
|
||||
|
||||
template <typename Char> struct test_stream : std::basic_ostream<Char> {
|
||||
private:
|
||||
void_t<> operator<<(converter);
|
||||
};
|
||||
|
||||
// Hide insertion operators for built-in types.
|
||||
template <typename Char, typename Traits>
|
||||
void_t<> operator<<(std::basic_ostream<Char, Traits>&, Char);
|
||||
template <typename Char, typename Traits>
|
||||
void_t<> operator<<(std::basic_ostream<Char, Traits>&, char);
|
||||
template <typename Traits>
|
||||
void_t<> operator<<(std::basic_ostream<char, Traits>&, char);
|
||||
template <typename Traits>
|
||||
void_t<> operator<<(std::basic_ostream<char, Traits>&, signed char);
|
||||
template <typename Traits>
|
||||
void_t<> operator<<(std::basic_ostream<char, Traits>&, unsigned char);
|
||||
|
||||
// Checks if T has a user-defined operator<< (e.g. not a member of
|
||||
// std::ostream).
|
||||
template <typename T, typename Char> class is_streamable {
|
||||
private:
|
||||
template <typename U>
|
||||
static bool_constant<!std::is_same<decltype(std::declval<test_stream<Char>&>()
|
||||
<< std::declval<U>()),
|
||||
void_t<>>::value>
|
||||
test(int);
|
||||
|
||||
template <typename> static std::false_type test(...);
|
||||
|
||||
using result = decltype(test<T>(0));
|
||||
|
||||
public:
|
||||
static const bool value = result::value;
|
||||
};
|
||||
|
||||
// Write the content of buf to os.
|
||||
template <typename Char>
|
||||
void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
|
||||
const Char* buf_data = buf.data();
|
||||
using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
|
||||
unsigned_streamsize size = buf.size();
|
||||
unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>());
|
||||
do {
|
||||
unsigned_streamsize n = size <= max_size ? size : max_size;
|
||||
os.write(buf_data, static_cast<std::streamsize>(n));
|
||||
buf_data += n;
|
||||
size -= n;
|
||||
} while (size != 0);
|
||||
}
|
||||
|
||||
template <typename Char, typename T>
|
||||
void format_value(buffer<Char>& buf, const T& value,
|
||||
locale_ref loc = locale_ref()) {
|
||||
formatbuf<Char> format_buf(buf);
|
||||
std::basic_ostream<Char> output(&format_buf);
|
||||
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
|
||||
if (loc) output.imbue(loc.get<std::locale>());
|
||||
#endif
|
||||
output << value;
|
||||
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
|
||||
buf.try_resize(buf.size());
|
||||
}
|
||||
|
||||
// Formats an object of type T that has an overloaded ostream operator<<.
|
||||
template <typename T, typename Char>
|
||||
struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
|
||||
: private formatter<basic_string_view<Char>, Char> {
|
||||
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
|
||||
-> decltype(ctx.begin()) {
|
||||
return formatter<basic_string_view<Char>, Char>::parse(ctx);
|
||||
}
|
||||
template <typename ParseCtx,
|
||||
FMT_ENABLE_IF(std::is_same<
|
||||
ParseCtx, basic_printf_parse_context<Char>>::value)>
|
||||
auto parse(ParseCtx& ctx) -> decltype(ctx.begin()) {
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template <typename OutputIt>
|
||||
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx)
|
||||
-> OutputIt {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
format_value(buffer, value, ctx.locale());
|
||||
basic_string_view<Char> str(buffer.data(), buffer.size());
|
||||
return formatter<basic_string_view<Char>, Char>::format(str, ctx);
|
||||
}
|
||||
template <typename OutputIt>
|
||||
auto format(const T& value, basic_printf_context<OutputIt, Char>& ctx)
|
||||
-> OutputIt {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
format_value(buffer, value, ctx.locale());
|
||||
return std::copy(buffer.begin(), buffer.end(), ctx.out());
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
template <typename Char>
|
||||
void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
detail::vformat_to(buffer, format_str, args);
|
||||
detail::write_buffer(os, buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Prints formatted data to the stream *os*.
|
||||
|
||||
**Example**::
|
||||
|
||||
fmt::print(cerr, "Don't {}!", "panic");
|
||||
\endrst
|
||||
*/
|
||||
template <typename S, typename... Args,
|
||||
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
|
||||
void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) {
|
||||
vprint(os, to_string_view(format_str),
|
||||
fmt::make_args_checked<Args...>(format_str, args...));
|
||||
}
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_OSTREAM_H_
|
||||
2
src/3rdparty/fmt/posix.h
vendored
Normal file
2
src/3rdparty/fmt/posix.h
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
#include "os.h"
|
||||
#warning "fmt/posix.h is deprecated; use fmt/os.h instead"
|
||||
751
src/3rdparty/fmt/printf.h
vendored
Normal file
751
src/3rdparty/fmt/printf.h
vendored
Normal file
@@ -0,0 +1,751 @@
|
||||
// Formatting library for C++ - legacy printf implementation
|
||||
//
|
||||
// Copyright (c) 2012 - 2016, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_PRINTF_H_
|
||||
#define FMT_PRINTF_H_
|
||||
|
||||
#include <algorithm> // std::max
|
||||
#include <limits> // std::numeric_limits
|
||||
|
||||
#include "ostream.h"
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
namespace detail {
|
||||
|
||||
// Checks if a value fits in int - used to avoid warnings about comparing
|
||||
// signed and unsigned integers.
|
||||
template <bool IsSigned> struct int_checker {
|
||||
template <typename T> static bool fits_in_int(T value) {
|
||||
unsigned max = max_value<int>();
|
||||
return value <= max;
|
||||
}
|
||||
static bool fits_in_int(bool) { return true; }
|
||||
};
|
||||
|
||||
template <> struct int_checker<true> {
|
||||
template <typename T> static bool fits_in_int(T value) {
|
||||
return value >= (std::numeric_limits<int>::min)() &&
|
||||
value <= max_value<int>();
|
||||
}
|
||||
static bool fits_in_int(int) { return true; }
|
||||
};
|
||||
|
||||
class printf_precision_handler {
|
||||
public:
|
||||
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
||||
int operator()(T value) {
|
||||
if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
|
||||
FMT_THROW(format_error("number is too big"));
|
||||
return (std::max)(static_cast<int>(value), 0);
|
||||
}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
|
||||
int operator()(T) {
|
||||
FMT_THROW(format_error("precision is not integer"));
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
// An argument visitor that returns true iff arg is a zero integer.
|
||||
class is_zero_int {
|
||||
public:
|
||||
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
||||
bool operator()(T value) {
|
||||
return value == 0;
|
||||
}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
|
||||
bool operator()(T) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T> struct make_unsigned_or_bool : std::make_unsigned<T> {};
|
||||
|
||||
template <> struct make_unsigned_or_bool<bool> { using type = bool; };
|
||||
|
||||
template <typename T, typename Context> class arg_converter {
|
||||
private:
|
||||
using char_type = typename Context::char_type;
|
||||
|
||||
basic_format_arg<Context>& arg_;
|
||||
char_type type_;
|
||||
|
||||
public:
|
||||
arg_converter(basic_format_arg<Context>& arg, char_type type)
|
||||
: arg_(arg), type_(type) {}
|
||||
|
||||
void operator()(bool value) {
|
||||
if (type_ != 's') operator()<bool>(value);
|
||||
}
|
||||
|
||||
template <typename U, FMT_ENABLE_IF(std::is_integral<U>::value)>
|
||||
void operator()(U value) {
|
||||
bool is_signed = type_ == 'd' || type_ == 'i';
|
||||
using target_type = conditional_t<std::is_same<T, void>::value, U, T>;
|
||||
if (const_check(sizeof(target_type) <= sizeof(int))) {
|
||||
// Extra casts are used to silence warnings.
|
||||
if (is_signed) {
|
||||
arg_ = detail::make_arg<Context>(
|
||||
static_cast<int>(static_cast<target_type>(value)));
|
||||
} else {
|
||||
using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
|
||||
arg_ = detail::make_arg<Context>(
|
||||
static_cast<unsigned>(static_cast<unsigned_type>(value)));
|
||||
}
|
||||
} else {
|
||||
if (is_signed) {
|
||||
// glibc's printf doesn't sign extend arguments of smaller types:
|
||||
// std::printf("%lld", -42); // prints "4294967254"
|
||||
// but we don't have to do the same because it's a UB.
|
||||
arg_ = detail::make_arg<Context>(static_cast<long long>(value));
|
||||
} else {
|
||||
arg_ = detail::make_arg<Context>(
|
||||
static_cast<typename make_unsigned_or_bool<U>::type>(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename U, FMT_ENABLE_IF(!std::is_integral<U>::value)>
|
||||
void operator()(U) {} // No conversion needed for non-integral types.
|
||||
};
|
||||
|
||||
// Converts an integer argument to T for printf, if T is an integral type.
|
||||
// If T is void, the argument is converted to corresponding signed or unsigned
|
||||
// type depending on the type specifier: 'd' and 'i' - signed, other -
|
||||
// unsigned).
|
||||
template <typename T, typename Context, typename Char>
|
||||
void convert_arg(basic_format_arg<Context>& arg, Char type) {
|
||||
visit_format_arg(arg_converter<T, Context>(arg, type), arg);
|
||||
}
|
||||
|
||||
// Converts an integer argument to char for printf.
|
||||
template <typename Context> class char_converter {
|
||||
private:
|
||||
basic_format_arg<Context>& arg_;
|
||||
|
||||
public:
|
||||
explicit char_converter(basic_format_arg<Context>& arg) : arg_(arg) {}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
||||
void operator()(T value) {
|
||||
arg_ = detail::make_arg<Context>(
|
||||
static_cast<typename Context::char_type>(value));
|
||||
}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
|
||||
void operator()(T) {} // No conversion needed for non-integral types.
|
||||
};
|
||||
|
||||
// An argument visitor that return a pointer to a C string if argument is a
|
||||
// string or null otherwise.
|
||||
template <typename Char> struct get_cstring {
|
||||
template <typename T> const Char* operator()(T) { return nullptr; }
|
||||
const Char* operator()(const Char* s) { return s; }
|
||||
};
|
||||
|
||||
// Checks if an argument is a valid printf width specifier and sets
|
||||
// left alignment if it is negative.
|
||||
template <typename Char> class printf_width_handler {
|
||||
private:
|
||||
using format_specs = basic_format_specs<Char>;
|
||||
|
||||
format_specs& specs_;
|
||||
|
||||
public:
|
||||
explicit printf_width_handler(format_specs& specs) : specs_(specs) {}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
||||
unsigned operator()(T value) {
|
||||
auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
|
||||
if (detail::is_negative(value)) {
|
||||
specs_.align = align::left;
|
||||
width = 0 - width;
|
||||
}
|
||||
unsigned int_max = max_value<int>();
|
||||
if (width > int_max) FMT_THROW(format_error("number is too big"));
|
||||
return static_cast<unsigned>(width);
|
||||
}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
|
||||
unsigned operator()(T) {
|
||||
FMT_THROW(format_error("width is not integer"));
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char, typename Context>
|
||||
void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
|
||||
basic_format_args<Context> args) {
|
||||
Context(buffer_appender<Char>(buf), format, args).format();
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
// For printing into memory_buffer.
|
||||
template <typename Char, typename Context>
|
||||
FMT_DEPRECATED void printf(detail::buffer<Char>& buf,
|
||||
basic_string_view<Char> format,
|
||||
basic_format_args<Context> args) {
|
||||
return detail::vprintf(buf, format, args);
|
||||
}
|
||||
using detail::vprintf;
|
||||
|
||||
template <typename Char>
|
||||
class basic_printf_parse_context : public basic_format_parse_context<Char> {
|
||||
using basic_format_parse_context<Char>::basic_format_parse_context;
|
||||
};
|
||||
template <typename OutputIt, typename Char> class basic_printf_context;
|
||||
|
||||
/**
|
||||
\rst
|
||||
The ``printf`` argument formatter.
|
||||
\endrst
|
||||
*/
|
||||
template <typename OutputIt, typename Char>
|
||||
class printf_arg_formatter : public detail::arg_formatter_base<OutputIt, Char> {
|
||||
public:
|
||||
using iterator = OutputIt;
|
||||
|
||||
private:
|
||||
using char_type = Char;
|
||||
using base = detail::arg_formatter_base<OutputIt, Char>;
|
||||
using context_type = basic_printf_context<OutputIt, Char>;
|
||||
|
||||
context_type& context_;
|
||||
|
||||
void write_null_pointer(char) {
|
||||
this->specs()->type = 0;
|
||||
this->write("(nil)");
|
||||
}
|
||||
|
||||
void write_null_pointer(wchar_t) {
|
||||
this->specs()->type = 0;
|
||||
this->write(L"(nil)");
|
||||
}
|
||||
|
||||
public:
|
||||
using format_specs = typename base::format_specs;
|
||||
|
||||
/**
|
||||
\rst
|
||||
Constructs an argument formatter object.
|
||||
*buffer* is a reference to the output buffer and *specs* contains format
|
||||
specifier information for standard argument types.
|
||||
\endrst
|
||||
*/
|
||||
printf_arg_formatter(iterator iter, format_specs& specs, context_type& ctx)
|
||||
: base(iter, &specs, detail::locale_ref()), context_(ctx) {}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(fmt::detail::is_integral<T>::value)>
|
||||
iterator operator()(T value) {
|
||||
// MSVC2013 fails to compile separate overloads for bool and char_type so
|
||||
// use std::is_same instead.
|
||||
if (std::is_same<T, bool>::value) {
|
||||
format_specs& fmt_specs = *this->specs();
|
||||
if (fmt_specs.type != 's') return base::operator()(value ? 1 : 0);
|
||||
fmt_specs.type = 0;
|
||||
this->write(value != 0);
|
||||
} else if (std::is_same<T, char_type>::value) {
|
||||
format_specs& fmt_specs = *this->specs();
|
||||
if (fmt_specs.type && fmt_specs.type != 'c')
|
||||
return (*this)(static_cast<int>(value));
|
||||
fmt_specs.sign = sign::none;
|
||||
fmt_specs.alt = false;
|
||||
fmt_specs.fill[0] = ' '; // Ignore '0' flag for char types.
|
||||
// align::numeric needs to be overwritten here since the '0' flag is
|
||||
// ignored for non-numeric types
|
||||
if (fmt_specs.align == align::none || fmt_specs.align == align::numeric)
|
||||
fmt_specs.align = align::right;
|
||||
return base::operator()(value);
|
||||
} else {
|
||||
return base::operator()(value);
|
||||
}
|
||||
return this->out();
|
||||
}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
|
||||
iterator operator()(T value) {
|
||||
return base::operator()(value);
|
||||
}
|
||||
|
||||
/** Formats a null-terminated C string. */
|
||||
iterator operator()(const char* value) {
|
||||
if (value)
|
||||
base::operator()(value);
|
||||
else if (this->specs()->type == 'p')
|
||||
write_null_pointer(char_type());
|
||||
else
|
||||
this->write("(null)");
|
||||
return this->out();
|
||||
}
|
||||
|
||||
/** Formats a null-terminated wide C string. */
|
||||
iterator operator()(const wchar_t* value) {
|
||||
if (value)
|
||||
base::operator()(value);
|
||||
else if (this->specs()->type == 'p')
|
||||
write_null_pointer(char_type());
|
||||
else
|
||||
this->write(L"(null)");
|
||||
return this->out();
|
||||
}
|
||||
|
||||
iterator operator()(basic_string_view<char_type> value) {
|
||||
return base::operator()(value);
|
||||
}
|
||||
|
||||
iterator operator()(monostate value) { return base::operator()(value); }
|
||||
|
||||
/** Formats a pointer. */
|
||||
iterator operator()(const void* value) {
|
||||
if (value) return base::operator()(value);
|
||||
this->specs()->type = 0;
|
||||
write_null_pointer(char_type());
|
||||
return this->out();
|
||||
}
|
||||
|
||||
/** Formats an argument of a custom (user-defined) type. */
|
||||
iterator operator()(typename basic_format_arg<context_type>::handle handle) {
|
||||
handle.format(context_.parse_context(), context_);
|
||||
return this->out();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T> struct printf_formatter {
|
||||
printf_formatter() = delete;
|
||||
|
||||
template <typename ParseContext>
|
||||
auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(const T& value, FormatContext& ctx) -> decltype(ctx.out()) {
|
||||
detail::format_value(detail::get_container(ctx.out()), value);
|
||||
return ctx.out();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
This template formats data and writes the output through an output iterator.
|
||||
*/
|
||||
template <typename OutputIt, typename Char> class basic_printf_context {
|
||||
public:
|
||||
/** The character type for the output. */
|
||||
using char_type = Char;
|
||||
using iterator = OutputIt;
|
||||
using format_arg = basic_format_arg<basic_printf_context>;
|
||||
using parse_context_type = basic_printf_parse_context<Char>;
|
||||
template <typename T> using formatter_type = printf_formatter<T>;
|
||||
|
||||
private:
|
||||
using format_specs = basic_format_specs<char_type>;
|
||||
|
||||
OutputIt out_;
|
||||
basic_format_args<basic_printf_context> args_;
|
||||
parse_context_type parse_ctx_;
|
||||
|
||||
static void parse_flags(format_specs& specs, const Char*& it,
|
||||
const Char* end);
|
||||
|
||||
// Returns the argument with specified index or, if arg_index is -1, the next
|
||||
// argument.
|
||||
format_arg get_arg(int arg_index = -1);
|
||||
|
||||
// Parses argument index, flags and width and returns the argument index.
|
||||
int parse_header(const Char*& it, const Char* end, format_specs& specs);
|
||||
|
||||
public:
|
||||
/**
|
||||
\rst
|
||||
Constructs a ``printf_context`` object. References to the arguments are
|
||||
stored in the context object so make sure they have appropriate lifetimes.
|
||||
\endrst
|
||||
*/
|
||||
basic_printf_context(OutputIt out, basic_string_view<char_type> format_str,
|
||||
basic_format_args<basic_printf_context> args)
|
||||
: out_(out), args_(args), parse_ctx_(format_str) {}
|
||||
|
||||
OutputIt out() { return out_; }
|
||||
void advance_to(OutputIt it) { out_ = it; }
|
||||
|
||||
detail::locale_ref locale() { return {}; }
|
||||
|
||||
format_arg arg(int id) const { return args_.get(id); }
|
||||
|
||||
parse_context_type& parse_context() { return parse_ctx_; }
|
||||
|
||||
FMT_CONSTEXPR void on_error(const char* message) {
|
||||
parse_ctx_.on_error(message);
|
||||
}
|
||||
|
||||
/** Formats stored arguments and writes the output to the range. */
|
||||
template <typename ArgFormatter = printf_arg_formatter<OutputIt, Char>>
|
||||
OutputIt format();
|
||||
};
|
||||
|
||||
template <typename OutputIt, typename Char>
|
||||
void basic_printf_context<OutputIt, Char>::parse_flags(format_specs& specs,
|
||||
const Char*& it,
|
||||
const Char* end) {
|
||||
for (; it != end; ++it) {
|
||||
switch (*it) {
|
||||
case '-':
|
||||
specs.align = align::left;
|
||||
break;
|
||||
case '+':
|
||||
specs.sign = sign::plus;
|
||||
break;
|
||||
case '0':
|
||||
specs.fill[0] = '0';
|
||||
break;
|
||||
case ' ':
|
||||
if (specs.sign != sign::plus) {
|
||||
specs.sign = sign::space;
|
||||
}
|
||||
break;
|
||||
case '#':
|
||||
specs.alt = true;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename Char>
|
||||
typename basic_printf_context<OutputIt, Char>::format_arg
|
||||
basic_printf_context<OutputIt, Char>::get_arg(int arg_index) {
|
||||
if (arg_index < 0)
|
||||
arg_index = parse_ctx_.next_arg_id();
|
||||
else
|
||||
parse_ctx_.check_arg_id(--arg_index);
|
||||
return detail::get_arg(*this, arg_index);
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename Char>
|
||||
int basic_printf_context<OutputIt, Char>::parse_header(const Char*& it,
|
||||
const Char* end,
|
||||
format_specs& specs) {
|
||||
int arg_index = -1;
|
||||
char_type c = *it;
|
||||
if (c >= '0' && c <= '9') {
|
||||
// Parse an argument index (if followed by '$') or a width possibly
|
||||
// preceded with '0' flag(s).
|
||||
detail::error_handler eh;
|
||||
int value = parse_nonnegative_int(it, end, eh);
|
||||
if (it != end && *it == '$') { // value is an argument index
|
||||
++it;
|
||||
arg_index = value;
|
||||
} else {
|
||||
if (c == '0') specs.fill[0] = '0';
|
||||
if (value != 0) {
|
||||
// Nonzero value means that we parsed width and don't need to
|
||||
// parse it or flags again, so return now.
|
||||
specs.width = value;
|
||||
return arg_index;
|
||||
}
|
||||
}
|
||||
}
|
||||
parse_flags(specs, it, end);
|
||||
// Parse width.
|
||||
if (it != end) {
|
||||
if (*it >= '0' && *it <= '9') {
|
||||
detail::error_handler eh;
|
||||
specs.width = parse_nonnegative_int(it, end, eh);
|
||||
} else if (*it == '*') {
|
||||
++it;
|
||||
specs.width = static_cast<int>(visit_format_arg(
|
||||
detail::printf_width_handler<char_type>(specs), get_arg()));
|
||||
}
|
||||
}
|
||||
return arg_index;
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename Char>
|
||||
template <typename ArgFormatter>
|
||||
OutputIt basic_printf_context<OutputIt, Char>::format() {
|
||||
auto out = this->out();
|
||||
const Char* start = parse_ctx_.begin();
|
||||
const Char* end = parse_ctx_.end();
|
||||
auto it = start;
|
||||
while (it != end) {
|
||||
char_type c = *it++;
|
||||
if (c != '%') continue;
|
||||
if (it != end && *it == c) {
|
||||
out = std::copy(start, it, out);
|
||||
start = ++it;
|
||||
continue;
|
||||
}
|
||||
out = std::copy(start, it - 1, out);
|
||||
|
||||
format_specs specs;
|
||||
specs.align = align::right;
|
||||
|
||||
// Parse argument index, flags and width.
|
||||
int arg_index = parse_header(it, end, specs);
|
||||
if (arg_index == 0) on_error("argument not found");
|
||||
|
||||
// Parse precision.
|
||||
if (it != end && *it == '.') {
|
||||
++it;
|
||||
c = it != end ? *it : 0;
|
||||
if ('0' <= c && c <= '9') {
|
||||
detail::error_handler eh;
|
||||
specs.precision = parse_nonnegative_int(it, end, eh);
|
||||
} else if (c == '*') {
|
||||
++it;
|
||||
specs.precision = static_cast<int>(
|
||||
visit_format_arg(detail::printf_precision_handler(), get_arg()));
|
||||
} else {
|
||||
specs.precision = 0;
|
||||
}
|
||||
}
|
||||
|
||||
format_arg arg = get_arg(arg_index);
|
||||
// For d, i, o, u, x, and X conversion specifiers, if a precision is
|
||||
// specified, the '0' flag is ignored
|
||||
if (specs.precision >= 0 && arg.is_integral())
|
||||
specs.fill[0] =
|
||||
' '; // Ignore '0' flag for non-numeric types or if '-' present.
|
||||
if (specs.precision >= 0 && arg.type() == detail::type::cstring_type) {
|
||||
auto str = visit_format_arg(detail::get_cstring<Char>(), arg);
|
||||
auto str_end = str + specs.precision;
|
||||
auto nul = std::find(str, str_end, Char());
|
||||
arg = detail::make_arg<basic_printf_context>(basic_string_view<Char>(
|
||||
str,
|
||||
detail::to_unsigned(nul != str_end ? nul - str : specs.precision)));
|
||||
}
|
||||
if (specs.alt && visit_format_arg(detail::is_zero_int(), arg))
|
||||
specs.alt = false;
|
||||
if (specs.fill[0] == '0') {
|
||||
if (arg.is_arithmetic() && specs.align != align::left)
|
||||
specs.align = align::numeric;
|
||||
else
|
||||
specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types or if '-'
|
||||
// flag is also present.
|
||||
}
|
||||
|
||||
// Parse length and convert the argument to the required type.
|
||||
c = it != end ? *it++ : 0;
|
||||
char_type t = it != end ? *it : 0;
|
||||
using detail::convert_arg;
|
||||
switch (c) {
|
||||
case 'h':
|
||||
if (t == 'h') {
|
||||
++it;
|
||||
t = it != end ? *it : 0;
|
||||
convert_arg<signed char>(arg, t);
|
||||
} else {
|
||||
convert_arg<short>(arg, t);
|
||||
}
|
||||
break;
|
||||
case 'l':
|
||||
if (t == 'l') {
|
||||
++it;
|
||||
t = it != end ? *it : 0;
|
||||
convert_arg<long long>(arg, t);
|
||||
} else {
|
||||
convert_arg<long>(arg, t);
|
||||
}
|
||||
break;
|
||||
case 'j':
|
||||
convert_arg<intmax_t>(arg, t);
|
||||
break;
|
||||
case 'z':
|
||||
convert_arg<size_t>(arg, t);
|
||||
break;
|
||||
case 't':
|
||||
convert_arg<std::ptrdiff_t>(arg, t);
|
||||
break;
|
||||
case 'L':
|
||||
// printf produces garbage when 'L' is omitted for long double, no
|
||||
// need to do the same.
|
||||
break;
|
||||
default:
|
||||
--it;
|
||||
convert_arg<void>(arg, c);
|
||||
}
|
||||
|
||||
// Parse type.
|
||||
if (it == end) FMT_THROW(format_error("invalid format string"));
|
||||
specs.type = static_cast<char>(*it++);
|
||||
if (arg.is_integral()) {
|
||||
// Normalize type.
|
||||
switch (specs.type) {
|
||||
case 'i':
|
||||
case 'u':
|
||||
specs.type = 'd';
|
||||
break;
|
||||
case 'c':
|
||||
visit_format_arg(detail::char_converter<basic_printf_context>(arg),
|
||||
arg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
start = it;
|
||||
|
||||
// Format argument.
|
||||
out = visit_format_arg(ArgFormatter(out, specs, *this), arg);
|
||||
}
|
||||
return std::copy(start, it, out);
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
using basic_printf_context_t =
|
||||
basic_printf_context<detail::buffer_appender<Char>, Char>;
|
||||
|
||||
using printf_context = basic_printf_context_t<char>;
|
||||
using wprintf_context = basic_printf_context_t<wchar_t>;
|
||||
|
||||
using printf_args = basic_format_args<printf_context>;
|
||||
using wprintf_args = basic_format_args<wprintf_context>;
|
||||
|
||||
/**
|
||||
\rst
|
||||
Constructs an `~fmt::format_arg_store` object that contains references to
|
||||
arguments and can be implicitly converted to `~fmt::printf_args`.
|
||||
\endrst
|
||||
*/
|
||||
template <typename... Args>
|
||||
inline format_arg_store<printf_context, Args...> make_printf_args(
|
||||
const Args&... args) {
|
||||
return {args...};
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Constructs an `~fmt::format_arg_store` object that contains references to
|
||||
arguments and can be implicitly converted to `~fmt::wprintf_args`.
|
||||
\endrst
|
||||
*/
|
||||
template <typename... Args>
|
||||
inline format_arg_store<wprintf_context, Args...> make_wprintf_args(
|
||||
const Args&... args) {
|
||||
return {args...};
|
||||
}
|
||||
|
||||
template <typename S, typename Char = char_t<S>>
|
||||
inline std::basic_string<Char> vsprintf(
|
||||
const S& format,
|
||||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
vprintf(buffer, to_string_view(format), args);
|
||||
return to_string(buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Formats arguments and returns the result as a string.
|
||||
|
||||
**Example**::
|
||||
|
||||
std::string message = fmt::sprintf("The answer is %d", 42);
|
||||
\endrst
|
||||
*/
|
||||
template <typename S, typename... Args,
|
||||
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
|
||||
inline std::basic_string<Char> sprintf(const S& format, const Args&... args) {
|
||||
using context = basic_printf_context_t<Char>;
|
||||
return vsprintf(to_string_view(format), make_format_args<context>(args...));
|
||||
}
|
||||
|
||||
template <typename S, typename Char = char_t<S>>
|
||||
inline int vfprintf(
|
||||
std::FILE* f, const S& format,
|
||||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
vprintf(buffer, to_string_view(format), args);
|
||||
size_t size = buffer.size();
|
||||
return std::fwrite(buffer.data(), sizeof(Char), size, f) < size
|
||||
? -1
|
||||
: static_cast<int>(size);
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Prints formatted data to the file *f*.
|
||||
|
||||
**Example**::
|
||||
|
||||
fmt::fprintf(stderr, "Don't %s!", "panic");
|
||||
\endrst
|
||||
*/
|
||||
template <typename S, typename... Args,
|
||||
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
|
||||
inline int fprintf(std::FILE* f, const S& format, const Args&... args) {
|
||||
using context = basic_printf_context_t<Char>;
|
||||
return vfprintf(f, to_string_view(format),
|
||||
make_format_args<context>(args...));
|
||||
}
|
||||
|
||||
template <typename S, typename Char = char_t<S>>
|
||||
inline int vprintf(
|
||||
const S& format,
|
||||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
|
||||
return vfprintf(stdout, to_string_view(format), args);
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Prints formatted data to ``stdout``.
|
||||
|
||||
**Example**::
|
||||
|
||||
fmt::printf("Elapsed time: %.2f seconds", 1.23);
|
||||
\endrst
|
||||
*/
|
||||
template <typename S, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_string<S>::value)>
|
||||
inline int printf(const S& format_str, const Args&... args) {
|
||||
using context = basic_printf_context_t<char_t<S>>;
|
||||
return vprintf(to_string_view(format_str),
|
||||
make_format_args<context>(args...));
|
||||
}
|
||||
|
||||
template <typename S, typename Char = char_t<S>>
|
||||
inline int vfprintf(
|
||||
std::basic_ostream<Char>& os, const S& format,
|
||||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
vprintf(buffer, to_string_view(format), args);
|
||||
detail::write_buffer(os, buffer);
|
||||
return static_cast<int>(buffer.size());
|
||||
}
|
||||
|
||||
/** Formats arguments and writes the output to the range. */
|
||||
template <typename ArgFormatter, typename Char,
|
||||
typename Context =
|
||||
basic_printf_context<typename ArgFormatter::iterator, Char>>
|
||||
typename ArgFormatter::iterator vprintf(
|
||||
detail::buffer<Char>& out, basic_string_view<Char> format_str,
|
||||
basic_format_args<type_identity_t<Context>> args) {
|
||||
typename ArgFormatter::iterator iter(out);
|
||||
Context(iter, format_str, args).template format<ArgFormatter>();
|
||||
return iter;
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Prints formatted data to the stream *os*.
|
||||
|
||||
**Example**::
|
||||
|
||||
fmt::fprintf(cerr, "Don't %s!", "panic");
|
||||
\endrst
|
||||
*/
|
||||
template <typename S, typename... Args, typename Char = char_t<S>>
|
||||
inline int fprintf(std::basic_ostream<Char>& os, const S& format_str,
|
||||
const Args&... args) {
|
||||
using context = basic_printf_context_t<Char>;
|
||||
return vfprintf(os, to_string_view(format_str),
|
||||
make_format_args<context>(args...));
|
||||
}
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_PRINTF_H_
|
||||
393
src/3rdparty/fmt/ranges.h
vendored
Normal file
393
src/3rdparty/fmt/ranges.h
vendored
Normal file
@@ -0,0 +1,393 @@
|
||||
// Formatting library for C++ - experimental range support
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
//
|
||||
// Copyright (c) 2018 - present, Remotion (Igor Schulz)
|
||||
// All Rights Reserved
|
||||
// {fmt} support for ranges, containers and types tuple interface.
|
||||
|
||||
#ifndef FMT_RANGES_H_
|
||||
#define FMT_RANGES_H_
|
||||
|
||||
#include <initializer_list>
|
||||
#include <type_traits>
|
||||
|
||||
#include "format.h"
|
||||
|
||||
// output only up to N items from the range.
|
||||
#ifndef FMT_RANGE_OUTPUT_LENGTH_LIMIT
|
||||
# define FMT_RANGE_OUTPUT_LENGTH_LIMIT 256
|
||||
#endif
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
|
||||
template <typename Char> struct formatting_base {
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return ctx.begin();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char, typename Enable = void>
|
||||
struct formatting_range : formatting_base<Char> {
|
||||
static FMT_CONSTEXPR_DECL const size_t range_length_limit =
|
||||
FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the
|
||||
// range.
|
||||
Char prefix;
|
||||
Char delimiter;
|
||||
Char postfix;
|
||||
formatting_range() : prefix('{'), delimiter(','), postfix('}') {}
|
||||
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
|
||||
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
|
||||
};
|
||||
|
||||
template <typename Char, typename Enable = void>
|
||||
struct formatting_tuple : formatting_base<Char> {
|
||||
Char prefix;
|
||||
Char delimiter;
|
||||
Char postfix;
|
||||
formatting_tuple() : prefix('('), delimiter(','), postfix(')') {}
|
||||
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
|
||||
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename RangeT, typename OutputIterator>
|
||||
OutputIterator copy(const RangeT& range, OutputIterator out) {
|
||||
for (auto it = range.begin(), end = range.end(); it != end; ++it)
|
||||
*out++ = *it;
|
||||
return out;
|
||||
}
|
||||
|
||||
template <typename OutputIterator>
|
||||
OutputIterator copy(const char* str, OutputIterator out) {
|
||||
while (*str) *out++ = *str++;
|
||||
return out;
|
||||
}
|
||||
|
||||
template <typename OutputIterator>
|
||||
OutputIterator copy(char ch, OutputIterator out) {
|
||||
*out++ = ch;
|
||||
return out;
|
||||
}
|
||||
|
||||
/// Return true value if T has std::string interface, like std::string_view.
|
||||
template <typename T> class is_like_std_string {
|
||||
template <typename U>
|
||||
static auto check(U* p)
|
||||
-> decltype((void)p->find('a'), p->length(), (void)p->data(), int());
|
||||
template <typename> static void check(...);
|
||||
|
||||
public:
|
||||
static FMT_CONSTEXPR_DECL const bool value =
|
||||
is_string<T>::value || !std::is_void<decltype(check<T>(nullptr))>::value;
|
||||
};
|
||||
|
||||
template <typename Char>
|
||||
struct is_like_std_string<fmt::basic_string_view<Char>> : std::true_type {};
|
||||
|
||||
template <typename... Ts> struct conditional_helper {};
|
||||
|
||||
template <typename T, typename _ = void> struct is_range_ : std::false_type {};
|
||||
|
||||
#if !FMT_MSC_VER || FMT_MSC_VER > 1800
|
||||
template <typename T>
|
||||
struct is_range_<
|
||||
T, conditional_t<false,
|
||||
conditional_helper<decltype(std::declval<T>().begin()),
|
||||
decltype(std::declval<T>().end())>,
|
||||
void>> : std::true_type {};
|
||||
#endif
|
||||
|
||||
/// tuple_size and tuple_element check.
|
||||
template <typename T> class is_tuple_like_ {
|
||||
template <typename U>
|
||||
static auto check(U* p) -> decltype(std::tuple_size<U>::value, int());
|
||||
template <typename> static void check(...);
|
||||
|
||||
public:
|
||||
static FMT_CONSTEXPR_DECL const bool value =
|
||||
!std::is_void<decltype(check<T>(nullptr))>::value;
|
||||
};
|
||||
|
||||
// Check for integer_sequence
|
||||
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900
|
||||
template <typename T, T... N>
|
||||
using integer_sequence = std::integer_sequence<T, N...>;
|
||||
template <size_t... N> using index_sequence = std::index_sequence<N...>;
|
||||
template <size_t N> using make_index_sequence = std::make_index_sequence<N>;
|
||||
#else
|
||||
template <typename T, T... N> struct integer_sequence {
|
||||
using value_type = T;
|
||||
|
||||
static FMT_CONSTEXPR size_t size() { return sizeof...(N); }
|
||||
};
|
||||
|
||||
template <size_t... N> using index_sequence = integer_sequence<size_t, N...>;
|
||||
|
||||
template <typename T, size_t N, T... Ns>
|
||||
struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {};
|
||||
template <typename T, T... Ns>
|
||||
struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {};
|
||||
|
||||
template <size_t N>
|
||||
using make_index_sequence = make_integer_sequence<size_t, N>;
|
||||
#endif
|
||||
|
||||
template <class Tuple, class F, size_t... Is>
|
||||
void for_each(index_sequence<Is...>, Tuple&& tup, F&& f) FMT_NOEXCEPT {
|
||||
using std::get;
|
||||
// using free function get<I>(T) now.
|
||||
const int _[] = {0, ((void)f(get<Is>(tup)), 0)...};
|
||||
(void)_; // blocks warnings
|
||||
}
|
||||
|
||||
template <class T>
|
||||
FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value> get_indexes(
|
||||
T const&) {
|
||||
return {};
|
||||
}
|
||||
|
||||
template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) {
|
||||
const auto indexes = get_indexes(tup);
|
||||
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
|
||||
}
|
||||
|
||||
template <typename Range>
|
||||
using value_type = remove_cvref_t<decltype(*std::declval<Range>().begin())>;
|
||||
|
||||
template <typename Arg, FMT_ENABLE_IF(!is_like_std_string<
|
||||
typename std::decay<Arg>::type>::value)>
|
||||
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
|
||||
return add_space ? " {}" : "{}";
|
||||
}
|
||||
|
||||
template <typename Arg, FMT_ENABLE_IF(is_like_std_string<
|
||||
typename std::decay<Arg>::type>::value)>
|
||||
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
|
||||
return add_space ? " \"{}\"" : "\"{}\"";
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char*) {
|
||||
return add_space ? " \"{}\"" : "\"{}\"";
|
||||
}
|
||||
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t*) {
|
||||
return add_space ? L" \"{}\"" : L"\"{}\"";
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char) {
|
||||
return add_space ? " '{}'" : "'{}'";
|
||||
}
|
||||
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) {
|
||||
return add_space ? L" '{}'" : L"'{}'";
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
template <typename T> struct is_tuple_like {
|
||||
static FMT_CONSTEXPR_DECL const bool value =
|
||||
detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value;
|
||||
};
|
||||
|
||||
template <typename TupleT, typename Char>
|
||||
struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
|
||||
private:
|
||||
// C++11 generic lambda for format()
|
||||
template <typename FormatContext> struct format_each {
|
||||
template <typename T> void operator()(const T& v) {
|
||||
if (i > 0) {
|
||||
if (formatting.add_prepostfix_space) {
|
||||
*out++ = ' ';
|
||||
}
|
||||
out = detail::copy(formatting.delimiter, out);
|
||||
}
|
||||
out = format_to(out,
|
||||
detail::format_str_quoted(
|
||||
(formatting.add_delimiter_spaces && i > 0), v),
|
||||
v);
|
||||
++i;
|
||||
}
|
||||
|
||||
formatting_tuple<Char>& formatting;
|
||||
size_t& i;
|
||||
typename std::add_lvalue_reference<decltype(
|
||||
std::declval<FormatContext>().out())>::type out;
|
||||
};
|
||||
|
||||
public:
|
||||
formatting_tuple<Char> formatting;
|
||||
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return formatting.parse(ctx);
|
||||
}
|
||||
|
||||
template <typename FormatContext = format_context>
|
||||
auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) {
|
||||
auto out = ctx.out();
|
||||
size_t i = 0;
|
||||
detail::copy(formatting.prefix, out);
|
||||
|
||||
detail::for_each(values, format_each<FormatContext>{formatting, i, out});
|
||||
if (formatting.add_prepostfix_space) {
|
||||
*out++ = ' ';
|
||||
}
|
||||
detail::copy(formatting.postfix, out);
|
||||
|
||||
return ctx.out();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename Char> struct is_range {
|
||||
static FMT_CONSTEXPR_DECL const bool value =
|
||||
detail::is_range_<T>::value && !detail::is_like_std_string<T>::value &&
|
||||
!std::is_convertible<T, std::basic_string<Char>>::value &&
|
||||
!std::is_constructible<detail::std_string_view<Char>, T>::value;
|
||||
};
|
||||
|
||||
template <typename T, typename Char>
|
||||
struct formatter<
|
||||
T, Char,
|
||||
enable_if_t<fmt::is_range<T, Char>::value
|
||||
// Workaround a bug in MSVC 2017 and earlier.
|
||||
#if !FMT_MSC_VER || FMT_MSC_VER >= 1927
|
||||
&& has_formatter<detail::value_type<T>, format_context>::value
|
||||
#endif
|
||||
>> {
|
||||
formatting_range<Char> formatting;
|
||||
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return formatting.parse(ctx);
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
typename FormatContext::iterator format(const T& values, FormatContext& ctx) {
|
||||
auto out = detail::copy(formatting.prefix, ctx.out());
|
||||
size_t i = 0;
|
||||
auto it = values.begin();
|
||||
auto end = values.end();
|
||||
for (; it != end; ++it) {
|
||||
if (i > 0) {
|
||||
if (formatting.add_prepostfix_space) *out++ = ' ';
|
||||
out = detail::copy(formatting.delimiter, out);
|
||||
}
|
||||
out = format_to(out,
|
||||
detail::format_str_quoted(
|
||||
(formatting.add_delimiter_spaces && i > 0), *it),
|
||||
*it);
|
||||
if (++i > formatting.range_length_limit) {
|
||||
out = format_to(out, " ... <other elements>");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (formatting.add_prepostfix_space) *out++ = ' ';
|
||||
return detail::copy(formatting.postfix, out);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char, typename... T> struct tuple_arg_join : detail::view {
|
||||
const std::tuple<T...>& tuple;
|
||||
basic_string_view<Char> sep;
|
||||
|
||||
tuple_arg_join(const std::tuple<T...>& t, basic_string_view<Char> s)
|
||||
: tuple{t}, sep{s} {}
|
||||
};
|
||||
|
||||
template <typename Char, typename... T>
|
||||
struct formatter<tuple_arg_join<Char, T...>, Char> {
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
typename FormatContext::iterator format(
|
||||
const tuple_arg_join<Char, T...>& value, FormatContext& ctx) {
|
||||
return format(value, ctx, detail::make_index_sequence<sizeof...(T)>{});
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename FormatContext, size_t... N>
|
||||
typename FormatContext::iterator format(
|
||||
const tuple_arg_join<Char, T...>& value, FormatContext& ctx,
|
||||
detail::index_sequence<N...>) {
|
||||
return format_args(value, ctx, std::get<N>(value.tuple)...);
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
typename FormatContext::iterator format_args(
|
||||
const tuple_arg_join<Char, T...>&, FormatContext& ctx) {
|
||||
// NOTE: for compilers that support C++17, this empty function instantiation
|
||||
// can be replaced with a constexpr branch in the variadic overload.
|
||||
return ctx.out();
|
||||
}
|
||||
|
||||
template <typename FormatContext, typename Arg, typename... Args>
|
||||
typename FormatContext::iterator format_args(
|
||||
const tuple_arg_join<Char, T...>& value, FormatContext& ctx,
|
||||
const Arg& arg, const Args&... args) {
|
||||
using base = formatter<typename std::decay<Arg>::type, Char>;
|
||||
auto out = ctx.out();
|
||||
out = base{}.format(arg, ctx);
|
||||
if (sizeof...(Args) > 0) {
|
||||
out = std::copy(value.sep.begin(), value.sep.end(), out);
|
||||
ctx.advance_to(out);
|
||||
return format_args(value, ctx, args...);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
\rst
|
||||
Returns an object that formats `tuple` with elements separated by `sep`.
|
||||
|
||||
**Example**::
|
||||
|
||||
std::tuple<int, char> t = {1, 'a'};
|
||||
fmt::print("{}", fmt::join(t, ", "));
|
||||
// Output: "1, a"
|
||||
\endrst
|
||||
*/
|
||||
template <typename... T>
|
||||
FMT_CONSTEXPR tuple_arg_join<char, T...> join(const std::tuple<T...>& tuple,
|
||||
string_view sep) {
|
||||
return {tuple, sep};
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...>& tuple,
|
||||
wstring_view sep) {
|
||||
return {tuple, sep};
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Returns an object that formats `initializer_list` with elements separated by
|
||||
`sep`.
|
||||
|
||||
**Example**::
|
||||
|
||||
fmt::print("{}", fmt::join({1, 2, 3}, ", "));
|
||||
// Output: "1, 2, 3"
|
||||
\endrst
|
||||
*/
|
||||
template <typename T>
|
||||
arg_join<const T*, const T*, char> join(std::initializer_list<T> list,
|
||||
string_view sep) {
|
||||
return join(std::begin(list), std::end(list), sep);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
arg_join<const T*, const T*, wchar_t> join(std::initializer_list<T> list,
|
||||
wstring_view sep) {
|
||||
return join(std::begin(list), std::end(list), sep);
|
||||
}
|
||||
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_RANGES_H_
|
||||
@@ -48,13 +48,13 @@ inline static const char *format(double h, char *buf, size_t size)
|
||||
|
||||
|
||||
xmrig::Hashrate::Hashrate(size_t threads) :
|
||||
m_threads(threads)
|
||||
m_threads(threads + 1)
|
||||
{
|
||||
m_counts = new uint64_t*[threads];
|
||||
m_timestamps = new uint64_t*[threads];
|
||||
m_top = new uint32_t[threads];
|
||||
m_counts = new uint64_t*[m_threads];
|
||||
m_timestamps = new uint64_t*[m_threads];
|
||||
m_top = new uint32_t[m_threads];
|
||||
|
||||
for (size_t i = 0; i < threads; i++) {
|
||||
for (size_t i = 0; i < m_threads; i++) {
|
||||
m_counts[i] = new uint64_t[kBucketSize]();
|
||||
m_timestamps[i] = new uint64_t[kBucketSize]();
|
||||
m_top[i] = 0;
|
||||
@@ -77,17 +77,8 @@ xmrig::Hashrate::~Hashrate()
|
||||
|
||||
double xmrig::Hashrate::calc(size_t ms) const
|
||||
{
|
||||
double result = 0.0;
|
||||
double data;
|
||||
|
||||
for (size_t i = 0; i < m_threads; ++i) {
|
||||
data = calc(i, ms);
|
||||
if (std::isnormal(data)) {
|
||||
result += data;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
const double data = calc(0, ms);
|
||||
return std::isnormal(data) ? data : 0.0;
|
||||
}
|
||||
|
||||
|
||||
@@ -102,7 +93,7 @@ double xmrig::Hashrate::calc(size_t threadId, size_t ms) const
|
||||
uint64_t earliestStamp = 0;
|
||||
bool haveFullSet = false;
|
||||
|
||||
const uint64_t timeStampLimit = xmrig::Chrono::highResolutionMSecs() - ms;
|
||||
const uint64_t timeStampLimit = xmrig::Chrono::steadyMSecs() - ms;
|
||||
uint64_t* timestamps = m_timestamps[threadId];
|
||||
uint64_t* counts = m_counts[threadId];
|
||||
|
||||
@@ -183,9 +174,9 @@ rapidjson::Value xmrig::Hashrate::toJSON(size_t threadId, rapidjson::Document &d
|
||||
auto &allocator = doc.GetAllocator();
|
||||
|
||||
Value out(kArrayType);
|
||||
out.PushBack(normalize(calc(threadId, ShortInterval)), allocator);
|
||||
out.PushBack(normalize(calc(threadId, MediumInterval)), allocator);
|
||||
out.PushBack(normalize(calc(threadId, LargeInterval)), allocator);
|
||||
out.PushBack(normalize(calc(threadId + 1, ShortInterval)), allocator);
|
||||
out.PushBack(normalize(calc(threadId + 1, MediumInterval)), allocator);
|
||||
out.PushBack(normalize(calc(threadId + 1, LargeInterval)), allocator);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
63
src/backend/common/HashrateInterpolator.cpp
Normal file
63
src/backend/common/HashrateInterpolator.cpp
Normal file
@@ -0,0 +1,63 @@
|
||||
/* XMRig
|
||||
* Copyright 2010 Jeff Garzik <jgarzik@pobox.com>
|
||||
* Copyright 2012-2014 pooler <pooler@litecoinpool.org>
|
||||
* Copyright 2014 Lucas Jones <https://github.com/lucasjones>
|
||||
* Copyright 2014-2016 Wolf9466 <https://github.com/OhGodAPet>
|
||||
* Copyright 2016 Jay D Dee <jayddee246@gmail.com>
|
||||
* Copyright 2017-2018 XMR-Stak <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
|
||||
* Copyright 2018-2020 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright 2016-2020 XMRig <https://github.com/xmrig>, <support@xmrig.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "backend/common/HashrateInterpolator.h"
|
||||
|
||||
|
||||
uint64_t xmrig::HashrateInterpolator::interpolate(uint64_t timeStamp) const
|
||||
{
|
||||
timeStamp -= LagMS;
|
||||
|
||||
std::lock_guard<std::mutex> l(m_lock);
|
||||
|
||||
const size_t N = m_data.size();
|
||||
|
||||
if (N < 2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < N - 1; ++i) {
|
||||
const auto& a = m_data[i];
|
||||
const auto& b = m_data[i + 1];
|
||||
|
||||
if (a.second <= timeStamp && timeStamp <= b.second) {
|
||||
return a.first + static_cast<int64_t>(b.first - a.first) * (timeStamp - a.second) / (b.second - a.second);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void xmrig::HashrateInterpolator::addDataPoint(uint64_t count, uint64_t timeStamp)
|
||||
{
|
||||
std::lock_guard<std::mutex> l(m_lock);
|
||||
|
||||
// Clean up old data
|
||||
while (!m_data.empty() && (timeStamp - m_data.front().second > LagMS * 2)) {
|
||||
m_data.pop_front();
|
||||
}
|
||||
|
||||
m_data.emplace_back(count, timeStamp);
|
||||
}
|
||||
57
src/backend/common/HashrateInterpolator.h
Normal file
57
src/backend/common/HashrateInterpolator.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/* XMRig
|
||||
* Copyright 2010 Jeff Garzik <jgarzik@pobox.com>
|
||||
* Copyright 2012-2014 pooler <pooler@litecoinpool.org>
|
||||
* Copyright 2014 Lucas Jones <https://github.com/lucasjones>
|
||||
* Copyright 2014-2016 Wolf9466 <https://github.com/OhGodAPet>
|
||||
* Copyright 2016 Jay D Dee <jayddee246@gmail.com>
|
||||
* Copyright 2017-2018 XMR-Stak <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
|
||||
* Copyright 2018-2020 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright 2016-2020 XMRig <https://github.com/xmrig>, <support@xmrig.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef XMRIG_HASHRATE_INTERPOLATOR_H
|
||||
#define XMRIG_HASHRATE_INTERPOLATOR_H
|
||||
|
||||
|
||||
#include <mutex>
|
||||
#include <deque>
|
||||
#include <utility>
|
||||
|
||||
|
||||
namespace xmrig {
|
||||
|
||||
|
||||
class HashrateInterpolator
|
||||
{
|
||||
public:
|
||||
enum {
|
||||
LagMS = 4000,
|
||||
};
|
||||
|
||||
uint64_t interpolate(uint64_t timeStamp) const;
|
||||
void addDataPoint(uint64_t count, uint64_t timeStamp);
|
||||
|
||||
private:
|
||||
// Buffer of hashrate counters, used for linear interpolation of past data
|
||||
mutable std::mutex m_lock;
|
||||
std::deque<std::pair<uint64_t, uint64_t>> m_data;
|
||||
};
|
||||
|
||||
|
||||
} // namespace xmrig
|
||||
|
||||
|
||||
#endif /* XMRIG_HASHRATE_INTERPOLATOR_H */
|
||||
@@ -6,8 +6,8 @@
|
||||
* Copyright 2016 Jay D Dee <jayddee246@gmail.com>
|
||||
* Copyright 2017-2018 XMR-Stak <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
|
||||
* Copyright 2018 Lee Clagett <https://github.com/vtnerd>
|
||||
* Copyright 2018-2019 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright 2016-2019 XMRig <https://github.com/xmrig>, <support@xmrig.com>
|
||||
* Copyright 2018-2020 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright 2016-2020 XMRig <https://github.com/xmrig>, <support@xmrig.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -32,9 +32,7 @@
|
||||
|
||||
xmrig::Worker::Worker(size_t id, int64_t affinity, int priority) :
|
||||
m_affinity(affinity),
|
||||
m_id(id),
|
||||
m_hashCount(0),
|
||||
m_timestamp(0)
|
||||
m_id(id)
|
||||
{
|
||||
m_node = VirtualMemory::bindToNUMANode(affinity);
|
||||
|
||||
@@ -45,6 +43,23 @@ xmrig::Worker::Worker(size_t id, int64_t affinity, int priority) :
|
||||
|
||||
void xmrig::Worker::storeStats()
|
||||
{
|
||||
m_hashCount.store(m_count, std::memory_order_relaxed);
|
||||
m_timestamp.store(Chrono::highResolutionMSecs(), std::memory_order_relaxed);
|
||||
// Get index which is unused now
|
||||
const uint32_t index = m_index.load(std::memory_order_relaxed) ^ 1;
|
||||
|
||||
// Fill in the data for that index
|
||||
m_hashCount[index] = m_count;
|
||||
m_timestamp[index] = Chrono::steadyMSecs();
|
||||
|
||||
// Switch to that index
|
||||
// All data will be in memory by the time it completes thanks to std::memory_order_seq_cst
|
||||
m_index.fetch_xor(1, std::memory_order_seq_cst);
|
||||
}
|
||||
|
||||
|
||||
void xmrig::Worker::getHashrateData(uint64_t& hashCount, uint64_t& timeStamp) const
|
||||
{
|
||||
const uint32_t index = m_index.load(std::memory_order_relaxed);
|
||||
|
||||
hashCount = m_hashCount[index];
|
||||
timeStamp = m_timestamp[index];
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
* Copyright 2016 Jay D Dee <jayddee246@gmail.com>
|
||||
* Copyright 2017-2018 XMR-Stak <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
|
||||
* Copyright 2018 Lee Clagett <https://github.com/vtnerd>
|
||||
* Copyright 2018-2019 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright 2016-2019 XMRig <https://github.com/xmrig>, <support@xmrig.com>
|
||||
* Copyright 2018-2020 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright 2016-2020 XMRig <https://github.com/xmrig>, <support@xmrig.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -28,7 +28,6 @@
|
||||
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
|
||||
|
||||
#include "backend/common/interfaces/IWorker.h"
|
||||
@@ -44,19 +43,21 @@ public:
|
||||
|
||||
inline const VirtualMemory *memory() const override { return nullptr; }
|
||||
inline size_t id() const override { return m_id; }
|
||||
inline uint64_t hashCount() const override { return m_hashCount.load(std::memory_order_relaxed); }
|
||||
inline uint64_t timestamp() const override { return m_timestamp.load(std::memory_order_relaxed); }
|
||||
inline uint64_t rawHashes() const override { return m_count; }
|
||||
inline void jobEarlyNotification(const Job&) override {}
|
||||
|
||||
void getHashrateData(uint64_t& hashCount, uint64_t& timeStamp) const override;
|
||||
|
||||
protected:
|
||||
void storeStats();
|
||||
|
||||
const int64_t m_affinity;
|
||||
const size_t m_id;
|
||||
std::atomic<uint64_t> m_hashCount;
|
||||
std::atomic<uint64_t> m_timestamp;
|
||||
uint32_t m_node = 0;
|
||||
uint64_t m_count = 0;
|
||||
std::atomic<uint32_t> m_index = {};
|
||||
uint32_t m_node = 0;
|
||||
uint64_t m_count = 0;
|
||||
uint64_t m_hashCount[2] = {};
|
||||
uint64_t m_timestamp[2] = {};
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ public:
|
||||
{
|
||||
m_rounds[index()]++;
|
||||
|
||||
if ((m_rounds[index()] % rounds) == 0) {
|
||||
if ((m_rounds[index()] & (rounds - 1)) == 0) {
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
if (!Nonce::next(index(), nonce(i), rounds * roundSize, nonceMask())) {
|
||||
return false;
|
||||
@@ -130,7 +130,7 @@ inline bool xmrig::WorkerJob<1>::nextRound(uint32_t rounds, uint32_t roundSize)
|
||||
|
||||
uint32_t* n = nonce();
|
||||
|
||||
if ((m_rounds[index()] % rounds) == 0) {
|
||||
if ((m_rounds[index()] & (rounds - 1)) == 0) {
|
||||
if (!Nonce::next(index(), n, rounds * roundSize, nonceMask())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
* Copyright 2016 Jay D Dee <jayddee246@gmail.com>
|
||||
* Copyright 2017-2018 XMR-Stak <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
|
||||
* Copyright 2018 Lee Clagett <https://github.com/vtnerd>
|
||||
* Copyright 2018-2019 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright 2016-2019 XMRig <https://github.com/xmrig>, <support@xmrig.com>
|
||||
* Copyright 2018-2020 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright 2016-2020 XMRig <https://github.com/xmrig>, <support@xmrig.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -29,7 +29,11 @@
|
||||
#include "backend/common/Workers.h"
|
||||
#include "backend/cpu/CpuWorker.h"
|
||||
#include "base/io/log/Log.h"
|
||||
#include "base/io/log/Tags.h"
|
||||
#include "base/net/stratum/Pool.h"
|
||||
#include "base/tools/Chrono.h"
|
||||
#include "base/tools/Object.h"
|
||||
#include "core/Miner.h"
|
||||
|
||||
|
||||
#ifdef XMRIG_FEATURE_OPENCL
|
||||
@@ -42,6 +46,11 @@
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef XMRIG_FEATURE_BENCHMARK
|
||||
# include "backend/common/benchmark/Benchmark.h"
|
||||
#endif
|
||||
|
||||
|
||||
namespace xmrig {
|
||||
|
||||
|
||||
@@ -51,17 +60,12 @@ public:
|
||||
XMRIG_DISABLE_COPY_MOVE(WorkersPrivate)
|
||||
|
||||
|
||||
WorkersPrivate() = default;
|
||||
WorkersPrivate() = default;
|
||||
~WorkersPrivate() = default;
|
||||
|
||||
|
||||
inline ~WorkersPrivate()
|
||||
{
|
||||
delete hashrate;
|
||||
}
|
||||
|
||||
|
||||
Hashrate *hashrate = nullptr;
|
||||
IBackend *backend = nullptr;
|
||||
IBackend *backend = nullptr;
|
||||
std::shared_ptr<Benchmark> benchmark;
|
||||
std::shared_ptr<Hashrate> hashrate;
|
||||
};
|
||||
|
||||
|
||||
@@ -83,10 +87,64 @@ xmrig::Workers<T>::~Workers()
|
||||
}
|
||||
|
||||
|
||||
template<class T>
|
||||
static void getHashrateData(xmrig::IWorker* worker, uint64_t& hashCount, uint64_t& timeStamp)
|
||||
{
|
||||
worker->getHashrateData(hashCount, timeStamp);
|
||||
}
|
||||
|
||||
|
||||
template<>
|
||||
void getHashrateData<xmrig::CpuLaunchData>(xmrig::IWorker* worker, uint64_t& hashCount, uint64_t&)
|
||||
{
|
||||
hashCount = worker->rawHashes();
|
||||
}
|
||||
|
||||
|
||||
template<class T>
|
||||
bool xmrig::Workers<T>::tick(uint64_t)
|
||||
{
|
||||
if (!d_ptr->hashrate) {
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t ts = Chrono::steadyMSecs();
|
||||
bool totalAvailable = true;
|
||||
uint64_t totalHashCount = 0;
|
||||
|
||||
for (Thread<T> *handle : m_workers) {
|
||||
IWorker *worker = handle->worker();
|
||||
if (worker) {
|
||||
uint64_t hashCount;
|
||||
getHashrateData<T>(worker, hashCount, ts);
|
||||
d_ptr->hashrate->add(handle->id() + 1, hashCount, ts);
|
||||
|
||||
const uint64_t n = worker->rawHashes();
|
||||
if (n == 0) {
|
||||
totalAvailable = false;
|
||||
}
|
||||
totalHashCount += n;
|
||||
}
|
||||
}
|
||||
|
||||
if (totalAvailable) {
|
||||
d_ptr->hashrate->add(0, totalHashCount, Chrono::steadyMSecs());
|
||||
}
|
||||
|
||||
# ifdef XMRIG_FEATURE_BENCHMARK
|
||||
if (d_ptr->benchmark && d_ptr->benchmark->finish(totalHashCount)) {
|
||||
return false;
|
||||
}
|
||||
# endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
template<class T>
|
||||
const xmrig::Hashrate *xmrig::Workers<T>::hashrate() const
|
||||
{
|
||||
return d_ptr->hashrate;
|
||||
return d_ptr->hashrate.get();
|
||||
}
|
||||
|
||||
|
||||
@@ -97,27 +155,6 @@ void xmrig::Workers<T>::setBackend(IBackend *backend)
|
||||
}
|
||||
|
||||
|
||||
template<class T>
|
||||
void xmrig::Workers<T>::start(const std::vector<T> &data)
|
||||
{
|
||||
for (const T &item : data) {
|
||||
m_workers.push_back(new Thread<T>(d_ptr->backend, m_workers.size(), item));
|
||||
}
|
||||
|
||||
d_ptr->hashrate = new Hashrate(m_workers.size());
|
||||
Nonce::touch(T::backend());
|
||||
|
||||
for (Thread<T> *worker : m_workers) {
|
||||
worker->start(Workers<T>::onReady);
|
||||
|
||||
// This sleep is important for optimal caching!
|
||||
// Threads must allocate scratchpads in order so that adjacent cores will use adjacent scratchpads
|
||||
// Sub-optimal caching can result in up to 0.5% hashrate penalty
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<class T>
|
||||
void xmrig::Workers<T>::stop()
|
||||
{
|
||||
@@ -130,26 +167,24 @@ void xmrig::Workers<T>::stop()
|
||||
m_workers.clear();
|
||||
Nonce::touch(T::backend());
|
||||
|
||||
delete d_ptr->hashrate;
|
||||
d_ptr->hashrate = nullptr;
|
||||
d_ptr->hashrate.reset();
|
||||
}
|
||||
|
||||
|
||||
#ifdef XMRIG_FEATURE_BENCHMARK
|
||||
template<class T>
|
||||
void xmrig::Workers<T>::tick(uint64_t)
|
||||
void xmrig::Workers<T>::start(const std::vector<T> &data, const std::shared_ptr<Benchmark> &benchmark)
|
||||
{
|
||||
if (!d_ptr->hashrate) {
|
||||
return;
|
||||
if (!benchmark) {
|
||||
return start(data, true);
|
||||
}
|
||||
|
||||
for (Thread<T> *handle : m_workers) {
|
||||
if (!handle->worker()) {
|
||||
continue;
|
||||
}
|
||||
start(data, false);
|
||||
|
||||
d_ptr->hashrate->add(handle->id(), handle->worker()->hashCount(), handle->worker()->timestamp());
|
||||
}
|
||||
d_ptr->benchmark = benchmark;
|
||||
d_ptr->benchmark->start();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
template<class T>
|
||||
@@ -183,6 +218,29 @@ void xmrig::Workers<T>::onReady(void *arg)
|
||||
}
|
||||
|
||||
|
||||
template<class T>
|
||||
void xmrig::Workers<T>::start(const std::vector<T> &data, bool sleep)
|
||||
{
|
||||
for (const auto &item : data) {
|
||||
m_workers.push_back(new Thread<T>(d_ptr->backend, m_workers.size(), item));
|
||||
}
|
||||
|
||||
d_ptr->hashrate = std::make_shared<Hashrate>(m_workers.size());
|
||||
Nonce::touch(T::backend());
|
||||
|
||||
for (auto worker : m_workers) {
|
||||
worker->start(Workers<T>::onReady);
|
||||
|
||||
// This sleep is important for optimal caching!
|
||||
// Threads must allocate scratchpads in order so that adjacent cores will use adjacent scratchpads
|
||||
// Sub-optimal caching can result in up to 0.5% hashrate penalty
|
||||
if (sleep) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
namespace xmrig {
|
||||
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
* Copyright 2016 Jay D Dee <jayddee246@gmail.com>
|
||||
* Copyright 2017-2018 XMR-Stak <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
|
||||
* Copyright 2018 Lee Clagett <https://github.com/vtnerd>
|
||||
* Copyright 2018-2019 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright 2016-2019 XMRig <https://github.com/xmrig>, <support@xmrig.com>
|
||||
* Copyright 2018-2020 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright 2016-2020 XMRig <https://github.com/xmrig>, <support@xmrig.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -29,7 +29,6 @@
|
||||
|
||||
#include "backend/common/Thread.h"
|
||||
#include "backend/cpu/CpuLaunchData.h"
|
||||
#include "base/tools/Object.h"
|
||||
|
||||
|
||||
#ifdef XMRIG_FEATURE_OPENCL
|
||||
@@ -48,6 +47,7 @@ namespace xmrig {
|
||||
class Hashrate;
|
||||
class WorkersPrivate;
|
||||
class Job;
|
||||
class Benchmark;
|
||||
|
||||
|
||||
template<class T>
|
||||
@@ -59,17 +59,24 @@ public:
|
||||
Workers();
|
||||
~Workers();
|
||||
|
||||
inline void start(const std::vector<T> &data) { start(data, true); }
|
||||
|
||||
bool tick(uint64_t ticks);
|
||||
const Hashrate *hashrate() const;
|
||||
void setBackend(IBackend *backend);
|
||||
void start(const std::vector<T> &data);
|
||||
void stop();
|
||||
void tick(uint64_t ticks);
|
||||
void jobEarlyNotification(const Job&);
|
||||
void setBackend(IBackend *backend);
|
||||
void stop();
|
||||
|
||||
# ifdef XMRIG_FEATURE_BENCHMARK
|
||||
void start(const std::vector<T> &data, const std::shared_ptr<Benchmark> &benchmark);
|
||||
# endif
|
||||
|
||||
private:
|
||||
static IWorker *create(Thread<T> *handle);
|
||||
static void onReady(void *arg);
|
||||
|
||||
void start(const std::vector<T> &data, bool sleep);
|
||||
|
||||
std::vector<Thread<T> *> m_workers;
|
||||
WorkersPrivate *d_ptr;
|
||||
};
|
||||
|
||||
112
src/backend/common/benchmark/BenchState.cpp
Normal file
112
src/backend/common/benchmark/BenchState.cpp
Normal file
@@ -0,0 +1,112 @@
|
||||
/* XMRig
|
||||
* Copyright (c) 2018-2020 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2016-2020 XMRig <https://github.com/xmrig>, <support@xmrig.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "backend/common/benchmark/BenchState.h"
|
||||
#include "backend/common/benchmark/BenchState_test.h"
|
||||
#include "backend/common/interfaces/IBenchListener.h"
|
||||
#include "base/io/Async.h"
|
||||
#include "base/tools/Chrono.h"
|
||||
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
|
||||
namespace xmrig {
|
||||
|
||||
|
||||
static bool done = false;
|
||||
static std::mutex mutex;
|
||||
static std::shared_ptr<Async> async;
|
||||
static uint32_t remaining = 0;
|
||||
static uint64_t doneTime = 0;
|
||||
static uint64_t result = 0;
|
||||
static uint64_t topDiff = 0;
|
||||
|
||||
|
||||
IBenchListener *BenchState::m_listener = nullptr;
|
||||
uint32_t BenchState::m_size = 0;
|
||||
|
||||
|
||||
} // namespace xmrig
|
||||
|
||||
|
||||
|
||||
bool xmrig::BenchState::isDone()
|
||||
{
|
||||
return xmrig::done;
|
||||
}
|
||||
|
||||
|
||||
uint64_t xmrig::BenchState::referenceHash(const Algorithm &algo, uint32_t size, uint32_t threads)
|
||||
{
|
||||
uint64_t hash = 0;
|
||||
|
||||
try {
|
||||
const auto &h = (threads == 1) ? hashCheck1T : hashCheck;
|
||||
hash = h.at(algo).at(size);
|
||||
} catch (const std::exception &ex) {}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
|
||||
uint64_t xmrig::BenchState::start(size_t threads, const IBackend *backend)
|
||||
{
|
||||
assert(m_listener != nullptr);
|
||||
|
||||
remaining = static_cast<uint32_t>(threads);
|
||||
|
||||
async = std::make_shared<Async>([] {
|
||||
m_listener->onBenchDone(result, topDiff, doneTime);
|
||||
async.reset();
|
||||
xmrig::done = true;
|
||||
});
|
||||
|
||||
const uint64_t ts = Chrono::steadyMSecs();
|
||||
m_listener->onBenchReady(ts, remaining, backend);
|
||||
|
||||
return ts;
|
||||
}
|
||||
|
||||
|
||||
void xmrig::BenchState::destroy()
|
||||
{
|
||||
xmrig::done = true;
|
||||
async.reset();
|
||||
}
|
||||
|
||||
|
||||
void xmrig::BenchState::done(uint64_t data, uint64_t diff, uint64_t ts)
|
||||
{
|
||||
assert(async && remaining > 0);
|
||||
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
|
||||
result ^= data;
|
||||
doneTime = std::max(doneTime, ts);
|
||||
topDiff = std::max(topDiff, diff);
|
||||
--remaining;
|
||||
|
||||
if (remaining == 0) {
|
||||
async->send();
|
||||
}
|
||||
}
|
||||
57
src/backend/common/benchmark/BenchState.h
Normal file
57
src/backend/common/benchmark/BenchState.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/* XMRig
|
||||
* Copyright (c) 2018-2020 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2016-2020 XMRig <https://github.com/xmrig>, <support@xmrig.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef XMRIG_BENCHSTATE_H
|
||||
#define XMRIG_BENCHSTATE_H
|
||||
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
|
||||
namespace xmrig {
|
||||
|
||||
|
||||
class Algorithm;
|
||||
class IBackend;
|
||||
class IBenchListener;
|
||||
|
||||
|
||||
class BenchState
|
||||
{
|
||||
public:
|
||||
static bool isDone();
|
||||
static uint64_t referenceHash(const Algorithm &algo, uint32_t size, uint32_t threads);
|
||||
static uint64_t start(size_t threads, const IBackend *backend);
|
||||
static void destroy();
|
||||
static void done(uint64_t data, uint64_t diff, uint64_t ts);
|
||||
|
||||
inline static uint32_t size() { return m_size; }
|
||||
inline static void setListener(IBenchListener *listener) { m_listener = listener; }
|
||||
inline static void setSize(uint32_t size) { m_size = size; }
|
||||
|
||||
private:
|
||||
static IBenchListener *m_listener;
|
||||
static uint32_t m_size;
|
||||
};
|
||||
|
||||
|
||||
} // namespace xmrig
|
||||
|
||||
|
||||
#endif /* XMRIG_BENCHSTATE_H */
|
||||
116
src/backend/common/benchmark/BenchState_test.h
Normal file
116
src/backend/common/benchmark/BenchState_test.h
Normal file
@@ -0,0 +1,116 @@
|
||||
/* XMRig
|
||||
* Copyright (c) 2018-2020 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2016-2020 XMRig <https://github.com/xmrig>, <support@xmrig.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef XMRIG_BENCHSTATE_TEST_H
|
||||
#define XMRIG_BENCHSTATE_TEST_H
|
||||
|
||||
|
||||
#include "base/crypto/Algorithm.h"
|
||||
|
||||
|
||||
#include <map>
|
||||
|
||||
|
||||
namespace xmrig {
|
||||
|
||||
|
||||
static const std::map<int, std::map<uint32_t, uint64_t> > hashCheck = {
|
||||
{ Algorithm::RX_0, {
|
||||
# ifndef NDEBUG
|
||||
{ 10000U, 0x4A597463865ACF0EULL },
|
||||
{ 20000U, 0xC82B490C757DA738ULL },
|
||||
# endif
|
||||
{ 250000U, 0x7D6054757BB08A63ULL },
|
||||
{ 500000U, 0x96607546DE1F5ECCULL },
|
||||
{ 1000000U, 0x898B6E0431C28A6BULL },
|
||||
{ 2000000U, 0xEE9468F8B40926BCULL },
|
||||
{ 3000000U, 0xC2BC5D11724813C0ULL },
|
||||
{ 4000000U, 0x3A2C7B285B87F941ULL },
|
||||
{ 5000000U, 0x3B5BD2C3A16B450EULL },
|
||||
{ 6000000U, 0x5CD0602F20C5C7C4ULL },
|
||||
{ 7000000U, 0x101DE939474B6812ULL },
|
||||
{ 8000000U, 0x52B765A1B156C6ECULL },
|
||||
{ 9000000U, 0x323935102AB6B45CULL },
|
||||
{ 10000000U, 0xB5231262E2792B26ULL }
|
||||
}},
|
||||
{ Algorithm::RX_WOW, {
|
||||
# ifndef NDEBUG
|
||||
{ 10000U, 0x6B0918757100B338ULL },
|
||||
{ 20000U, 0x0B55785C1837F41BULL },
|
||||
# endif
|
||||
{ 250000U, 0xC7F712C9603E2603ULL },
|
||||
{ 500000U, 0x21A0E5AAE6DA7D8DULL },
|
||||
{ 1000000U, 0x0F3E5400B39EA96AULL },
|
||||
{ 2000000U, 0x85944CCFA2752D1FULL },
|
||||
{ 3000000U, 0x64AFFCAE991811BAULL },
|
||||
{ 4000000U, 0x3E4D0B836D3B13BAULL },
|
||||
{ 5000000U, 0xEB7417D621271166ULL },
|
||||
{ 6000000U, 0x97FFE10C0949FFA5ULL },
|
||||
{ 7000000U, 0x84CAC0F8879A4BA1ULL },
|
||||
{ 8000000U, 0xA1B79F031DA2459FULL },
|
||||
{ 9000000U, 0x9B65226DA873E65DULL },
|
||||
{ 10000000U, 0x0F9E00C5A511C200ULL }
|
||||
}}
|
||||
};
|
||||
|
||||
|
||||
static const std::map<int, std::map<uint32_t, uint64_t> > hashCheck1T = {
|
||||
{ Algorithm::RX_0, {
|
||||
# ifndef NDEBUG
|
||||
{ 10000U, 0xADFC3A66F79BFE7FULL },
|
||||
{ 20000U, 0x8ED578A60D55C0DBULL },
|
||||
# endif
|
||||
{ 250000U, 0x90A15B799486F3EBULL },
|
||||
{ 500000U, 0xAA83118FEE570F9AULL },
|
||||
{ 1000000U, 0x3DF47B0A427C93D9ULL },
|
||||
{ 2000000U, 0xED4D639B0AEB85C6ULL },
|
||||
{ 3000000U, 0x2D4F9B4275A713C3ULL },
|
||||
{ 4000000U, 0xA9EBE4888377F8D3ULL },
|
||||
{ 5000000U, 0xB92F81851E180454ULL },
|
||||
{ 6000000U, 0xFB9F98F63C2F1B7DULL },
|
||||
{ 7000000U, 0x2CC3D7A779D5AB35ULL },
|
||||
{ 8000000U, 0x2EEF833EA462F4B1ULL },
|
||||
{ 9000000U, 0xC6D39EF59213A07CULL },
|
||||
{ 10000000U, 0x95E6BAE68DD779CDULL }
|
||||
}},
|
||||
{ Algorithm::RX_WOW, {
|
||||
# ifndef NDEBUG
|
||||
{ 10000U, 0x9EC1B9B8C8C7F082ULL },
|
||||
{ 20000U, 0xF1DA44FA2A20D730ULL },
|
||||
# endif
|
||||
{ 250000U, 0x7B409F096C863207ULL },
|
||||
{ 500000U, 0x70B7B80D15654216ULL },
|
||||
{ 1000000U, 0x31301CC550306A59ULL },
|
||||
{ 2000000U, 0x92F65E9E31116361ULL },
|
||||
{ 3000000U, 0x7FE8DF6F43BA5285ULL },
|
||||
{ 4000000U, 0xD6CDA54FE4D9BBF7ULL },
|
||||
{ 5000000U, 0x73AF673E1A38E2B4ULL },
|
||||
{ 6000000U, 0x81FDC5C4B45D84E4ULL },
|
||||
{ 7000000U, 0xAA08CA57666DC874ULL },
|
||||
{ 8000000U, 0x9DCEFB833FC875BCULL },
|
||||
{ 9000000U, 0x862F051352CFCA1FULL },
|
||||
{ 10000000U, 0xC403F220189E8430ULL }
|
||||
}}
|
||||
};
|
||||
|
||||
|
||||
} // namespace xmrig
|
||||
|
||||
|
||||
|
||||
#endif /* XMRIG_BENCHSTATE_TEST_H */
|
||||
61
src/backend/common/benchmark/Benchmark.cpp
Normal file
61
src/backend/common/benchmark/Benchmark.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
/* XMRig
|
||||
* Copyright (c) 2018-2020 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2016-2020 XMRig <https://github.com/xmrig>, <support@xmrig.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "backend/common/benchmark/Benchmark.h"
|
||||
#include "backend/common/benchmark/BenchState.h"
|
||||
#include "base/io/log/Log.h"
|
||||
#include "base/io/log/Tags.h"
|
||||
#include "base/tools/Chrono.h"
|
||||
|
||||
|
||||
#include <cinttypes>
|
||||
|
||||
|
||||
xmrig::Benchmark::Benchmark(size_t workers, const IBackend *backend) :
|
||||
m_backend(backend),
|
||||
m_workers(workers)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
bool xmrig::Benchmark::finish(uint64_t totalHashCount)
|
||||
{
|
||||
m_current = totalHashCount;
|
||||
|
||||
return BenchState::isDone();
|
||||
}
|
||||
|
||||
|
||||
void xmrig::Benchmark::start()
|
||||
{
|
||||
m_startTime = BenchState::start(m_workers, m_backend);
|
||||
}
|
||||
|
||||
|
||||
void xmrig::Benchmark::printProgress() const
|
||||
{
|
||||
if (!m_startTime || !m_current) {
|
||||
return;
|
||||
}
|
||||
|
||||
const double dt = static_cast<double>(Chrono::steadyMSecs() - m_startTime) / 1000.0;
|
||||
const double percent = static_cast<double>(m_current) / BenchState::size() * 100.0;
|
||||
|
||||
LOG_NOTICE("%s " MAGENTA_BOLD("%5.2f%% ") CYAN_BOLD("%" PRIu64) CYAN("/%u") BLACK_BOLD(" (%.3fs)"), Tags::bench(), percent, m_current, BenchState::size(), dt);
|
||||
}
|
||||
55
src/backend/common/benchmark/Benchmark.h
Normal file
55
src/backend/common/benchmark/Benchmark.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/* XMRig
|
||||
* Copyright (c) 2018-2020 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2016-2020 XMRig <https://github.com/xmrig>, <support@xmrig.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef XMRIG_BENCHMARK_H
|
||||
#define XMRIG_BENCHMARK_H
|
||||
|
||||
|
||||
#include "base/tools/Object.h"
|
||||
|
||||
|
||||
namespace xmrig {
|
||||
|
||||
|
||||
class IBackend;
|
||||
|
||||
|
||||
class Benchmark
|
||||
{
|
||||
public:
|
||||
XMRIG_DISABLE_COPY_MOVE_DEFAULT(Benchmark)
|
||||
|
||||
Benchmark(size_t workers, const IBackend *backend);
|
||||
~Benchmark() = default;
|
||||
|
||||
bool finish(uint64_t totalHashCount);
|
||||
void printProgress() const;
|
||||
void start();
|
||||
|
||||
private:
|
||||
const IBackend *m_backend;
|
||||
const size_t m_workers;
|
||||
uint64_t m_current = 0;
|
||||
uint64_t m_startTime = 0;
|
||||
};
|
||||
|
||||
|
||||
} // namespace xmrig
|
||||
|
||||
|
||||
#endif /* XMRIG_BENCHMARK_H */
|
||||
@@ -1,5 +1,6 @@
|
||||
set(HEADERS_BACKEND_COMMON
|
||||
src/backend/common/Hashrate.h
|
||||
src/backend/common/HashrateInterpolator.h
|
||||
src/backend/common/Tags.h
|
||||
src/backend/common/interfaces/IBackend.h
|
||||
src/backend/common/interfaces/IRxListener.h
|
||||
@@ -15,7 +16,22 @@ set(HEADERS_BACKEND_COMMON
|
||||
|
||||
set(SOURCES_BACKEND_COMMON
|
||||
src/backend/common/Hashrate.cpp
|
||||
src/backend/common/HashrateInterpolator.cpp
|
||||
src/backend/common/Threads.cpp
|
||||
src/backend/common/Worker.cpp
|
||||
src/backend/common/Workers.cpp
|
||||
)
|
||||
|
||||
if (WITH_RANDOMX AND WITH_BENCHMARK)
|
||||
list(APPEND HEADERS_BACKEND_COMMON
|
||||
src/backend/common/benchmark/Benchmark.h
|
||||
src/backend/common/benchmark/BenchState_test.h
|
||||
src/backend/common/benchmark/BenchState.h
|
||||
src/backend/common/interfaces/IBenchListener.h
|
||||
)
|
||||
|
||||
list(APPEND SOURCES_BACKEND_COMMON
|
||||
src/backend/common/benchmark/Benchmark.cpp
|
||||
src/backend/common/benchmark/BenchState.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -36,6 +36,7 @@ namespace xmrig {
|
||||
|
||||
|
||||
class Algorithm;
|
||||
class Benchmark;
|
||||
class Hashrate;
|
||||
class IApiRequest;
|
||||
class IWorker;
|
||||
@@ -60,12 +61,17 @@ public:
|
||||
virtual void setJob(const Job &job) = 0;
|
||||
virtual void start(IWorker *worker, bool ready) = 0;
|
||||
virtual void stop() = 0;
|
||||
virtual void tick(uint64_t ticks) = 0;
|
||||
virtual bool tick(uint64_t ticks) = 0;
|
||||
|
||||
# ifdef XMRIG_FEATURE_API
|
||||
virtual rapidjson::Value toJSON(rapidjson::Document &doc) const = 0;
|
||||
virtual void handleRequest(IApiRequest &request) = 0;
|
||||
# endif
|
||||
|
||||
# ifdef XMRIG_FEATURE_BENCHMARK
|
||||
virtual Benchmark *benchmark() const = 0;
|
||||
virtual void printBenchProgress() const = 0;
|
||||
# endif
|
||||
};
|
||||
|
||||
|
||||
|
||||
48
src/backend/common/interfaces/IBenchListener.h
Normal file
48
src/backend/common/interfaces/IBenchListener.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/* XMRig
|
||||
* Copyright (c) 2018-2020 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2016-2020 XMRig <https://github.com/xmrig>, <support@xmrig.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef XMRIG_IBENCHLISTENER_H
|
||||
#define XMRIG_IBENCHLISTENER_H
|
||||
|
||||
|
||||
#include "base/tools/Object.h"
|
||||
|
||||
|
||||
namespace xmrig {
|
||||
|
||||
|
||||
class IBackend;
|
||||
|
||||
|
||||
class IBenchListener
|
||||
{
|
||||
public:
|
||||
XMRIG_DISABLE_COPY_MOVE(IBenchListener)
|
||||
|
||||
IBenchListener() = default;
|
||||
virtual ~IBenchListener() = default;
|
||||
|
||||
virtual void onBenchDone(uint64_t result, uint64_t diff, uint64_t ts) = 0;
|
||||
virtual void onBenchReady(uint64_t ts, uint32_t threads, const IBackend *backend) = 0;
|
||||
};
|
||||
|
||||
|
||||
} /* namespace xmrig */
|
||||
|
||||
|
||||
#endif // XMRIG_IBENCHLISTENER_H
|
||||
@@ -5,8 +5,8 @@
|
||||
* Copyright 2014-2016 Wolf9466 <https://github.com/OhGodAPet>
|
||||
* Copyright 2016 Jay D Dee <jayddee246@gmail.com>
|
||||
* Copyright 2017-2018 XMR-Stak <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
|
||||
* Copyright 2018-2019 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright 2016-2019 XMRig <https://github.com/xmrig>, <support@xmrig.com>
|
||||
* Copyright 2018-2020 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright 2016-2020 XMRig <https://github.com/xmrig>, <support@xmrig.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -26,6 +26,9 @@
|
||||
#define XMRIG_IWORKER_H
|
||||
|
||||
|
||||
#include "base/tools/Object.h"
|
||||
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
@@ -40,16 +43,19 @@ class Job;
|
||||
class IWorker
|
||||
{
|
||||
public:
|
||||
virtual ~IWorker() = default;
|
||||
XMRIG_DISABLE_COPY_MOVE(IWorker)
|
||||
|
||||
virtual bool selfTest() = 0;
|
||||
virtual const VirtualMemory *memory() const = 0;
|
||||
virtual size_t id() const = 0;
|
||||
virtual size_t intensity() const = 0;
|
||||
virtual uint64_t hashCount() const = 0;
|
||||
virtual uint64_t timestamp() const = 0;
|
||||
virtual void start() = 0;
|
||||
virtual void jobEarlyNotification(const Job&) = 0;
|
||||
IWorker() = default;
|
||||
virtual ~IWorker() = default;
|
||||
|
||||
virtual bool selfTest() = 0;
|
||||
virtual const VirtualMemory *memory() const = 0;
|
||||
virtual size_t id() const = 0;
|
||||
virtual size_t intensity() const = 0;
|
||||
virtual uint64_t rawHashes() const = 0;
|
||||
virtual void getHashrateData(uint64_t&, uint64_t&) const = 0;
|
||||
virtual void start() = 0;
|
||||
virtual void jobEarlyNotification(const Job&) = 0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -55,6 +55,12 @@
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef XMRIG_FEATURE_BENCHMARK
|
||||
# include "backend/common/benchmark/Benchmark.h"
|
||||
# include "backend/common/benchmark/BenchState.h"
|
||||
#endif
|
||||
|
||||
|
||||
namespace xmrig {
|
||||
|
||||
|
||||
@@ -133,10 +139,7 @@ private:
|
||||
class CpuBackendPrivate
|
||||
{
|
||||
public:
|
||||
inline CpuBackendPrivate(Controller *controller) :
|
||||
controller(controller)
|
||||
{
|
||||
}
|
||||
inline CpuBackendPrivate(Controller *controller) : controller(controller) {}
|
||||
|
||||
|
||||
inline void start()
|
||||
@@ -150,7 +153,12 @@ public:
|
||||
);
|
||||
|
||||
status.start(threads, algo.l3());
|
||||
|
||||
# ifdef XMRIG_FEATURE_BENCHMARK
|
||||
workers.start(threads, benchmark);
|
||||
# else
|
||||
workers.start(threads);
|
||||
# endif
|
||||
}
|
||||
|
||||
|
||||
@@ -199,6 +207,10 @@ public:
|
||||
std::vector<CpuLaunchData> threads;
|
||||
String profileName;
|
||||
Workers<CpuLaunchData> workers;
|
||||
|
||||
# ifdef XMRIG_FEATURE_BENCHMARK
|
||||
std::shared_ptr<Benchmark> benchmark;
|
||||
# endif
|
||||
};
|
||||
|
||||
|
||||
@@ -304,9 +316,9 @@ void xmrig::CpuBackend::printHashrate(bool details)
|
||||
Log::print("| %8zu | %8" PRId64 " | %7s | %7s | %7s |",
|
||||
i,
|
||||
data.affinity,
|
||||
Hashrate::format(hashrate()->calc(i, Hashrate::ShortInterval), num, sizeof num / 3),
|
||||
Hashrate::format(hashrate()->calc(i, Hashrate::MediumInterval), num + 8, sizeof num / 3),
|
||||
Hashrate::format(hashrate()->calc(i, Hashrate::LargeInterval), num + 8 * 2, sizeof num / 3)
|
||||
Hashrate::format(hashrate()->calc(i + 1, Hashrate::ShortInterval), num, sizeof num / 3),
|
||||
Hashrate::format(hashrate()->calc(i + 1, Hashrate::MediumInterval), num + 8, sizeof num / 3),
|
||||
Hashrate::format(hashrate()->calc(i + 1, Hashrate::LargeInterval), num + 8 * 2, sizeof num / 3)
|
||||
);
|
||||
|
||||
i++;
|
||||
@@ -333,9 +345,15 @@ void xmrig::CpuBackend::setJob(const Job &job)
|
||||
return stop();
|
||||
}
|
||||
|
||||
const CpuConfig &cpu = d_ptr->controller->config()->cpu();
|
||||
const auto &cpu = d_ptr->controller->config()->cpu();
|
||||
|
||||
std::vector<CpuLaunchData> threads = cpu.get(d_ptr->controller->miner(), job.algorithm());
|
||||
# ifdef XMRIG_FEATURE_BENCHMARK
|
||||
const uint32_t benchSize = BenchState::size();
|
||||
# else
|
||||
constexpr uint32_t benchSize = 0;
|
||||
# endif
|
||||
|
||||
auto threads = cpu.get(d_ptr->controller->miner(), job.algorithm(), benchSize);
|
||||
if (!d_ptr->threads.empty() && d_ptr->threads.size() == threads.size() && std::equal(d_ptr->threads.begin(), d_ptr->threads.end(), threads.begin())) {
|
||||
return;
|
||||
}
|
||||
@@ -351,6 +369,12 @@ void xmrig::CpuBackend::setJob(const Job &job)
|
||||
|
||||
stop();
|
||||
|
||||
# ifdef XMRIG_FEATURE_BENCHMARK
|
||||
if (benchSize) {
|
||||
d_ptr->benchmark = std::make_shared<Benchmark>(threads.size(), this);
|
||||
}
|
||||
# endif
|
||||
|
||||
d_ptr->threads = std::move(threads);
|
||||
d_ptr->start();
|
||||
}
|
||||
@@ -387,9 +411,9 @@ void xmrig::CpuBackend::stop()
|
||||
}
|
||||
|
||||
|
||||
void xmrig::CpuBackend::tick(uint64_t ticks)
|
||||
bool xmrig::CpuBackend::tick(uint64_t ticks)
|
||||
{
|
||||
d_ptr->workers.tick(ticks);
|
||||
return d_ptr->workers.tick(ticks);
|
||||
}
|
||||
|
||||
|
||||
@@ -407,6 +431,7 @@ rapidjson::Value xmrig::CpuBackend::toJSON(rapidjson::Document &doc) const
|
||||
out.AddMember("profile", profileName().toJSON(), allocator);
|
||||
out.AddMember("hw-aes", cpu.isHwAES(), allocator);
|
||||
out.AddMember("priority", cpu.priority(), allocator);
|
||||
out.AddMember("msr", Rx::isMSR(), allocator);
|
||||
|
||||
# ifdef XMRIG_FEATURE_ASM
|
||||
const Assembly assembly = Cpu::assembly(cpu.assembly());
|
||||
@@ -459,3 +484,19 @@ void xmrig::CpuBackend::handleRequest(IApiRequest &request)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef XMRIG_FEATURE_BENCHMARK
|
||||
xmrig::Benchmark *xmrig::CpuBackend::benchmark() const
|
||||
{
|
||||
return d_ptr->benchmark.get();
|
||||
}
|
||||
|
||||
|
||||
void xmrig::CpuBackend::printBenchProgress() const
|
||||
{
|
||||
if (d_ptr->benchmark) {
|
||||
d_ptr->benchmark->printProgress();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -63,13 +63,18 @@ protected:
|
||||
void setJob(const Job &job) override;
|
||||
void start(IWorker *worker, bool ready) override;
|
||||
void stop() override;
|
||||
void tick(uint64_t ticks) override;
|
||||
bool tick(uint64_t ticks) override;
|
||||
|
||||
# ifdef XMRIG_FEATURE_API
|
||||
rapidjson::Value toJSON(rapidjson::Document &doc) const override;
|
||||
void handleRequest(IApiRequest &request) override;
|
||||
# endif
|
||||
|
||||
# ifdef XMRIG_FEATURE_BENCHMARK
|
||||
Benchmark *benchmark() const override;
|
||||
void printBenchProgress() const override;
|
||||
# endif
|
||||
|
||||
private:
|
||||
CpuBackendPrivate *d_ptr;
|
||||
};
|
||||
|
||||
@@ -34,25 +34,27 @@
|
||||
|
||||
namespace xmrig {
|
||||
|
||||
static const char *kEnabled = "enabled";
|
||||
static const char *kHugePages = "huge-pages";
|
||||
static const char *kHwAes = "hw-aes";
|
||||
static const char *kMaxThreadsHint = "max-threads-hint";
|
||||
static const char *kMemoryPool = "memory-pool";
|
||||
static const char *kPriority = "priority";
|
||||
static const char *kYield = "yield";
|
||||
const char *CpuConfig::kEnabled = "enabled";
|
||||
const char *CpuConfig::kField = "cpu";
|
||||
const char *CpuConfig::kHugePages = "huge-pages";
|
||||
const char *CpuConfig::kHugePagesJit = "huge-pages-jit";
|
||||
const char *CpuConfig::kHwAes = "hw-aes";
|
||||
const char *CpuConfig::kMaxThreadsHint = "max-threads-hint";
|
||||
const char *CpuConfig::kMemoryPool = "memory-pool";
|
||||
const char *CpuConfig::kPriority = "priority";
|
||||
const char *CpuConfig::kYield = "yield";
|
||||
|
||||
#ifdef XMRIG_FEATURE_ASM
|
||||
static const char *kAsm = "asm";
|
||||
const char *CpuConfig::kAsm = "asm";
|
||||
#endif
|
||||
|
||||
#ifdef XMRIG_ALGO_ARGON2
|
||||
static const char *kArgon2Impl = "argon2-impl";
|
||||
const char *CpuConfig::kArgon2Impl = "argon2-impl";
|
||||
#endif
|
||||
|
||||
#ifdef XMRIG_ALGO_ASTROBWT
|
||||
static const char* kAstroBWTMaxSize = "astrobwt-max-size";
|
||||
static const char* kAstroBWTAVX2 = "astrobwt-avx2";
|
||||
const char *CpuConfig::kAstroBWTMaxSize = "astrobwt-max-size";
|
||||
const char *CpuConfig::kAstroBWTAVX2 = "astrobwt-avx2";
|
||||
#endif
|
||||
|
||||
|
||||
@@ -76,6 +78,7 @@ rapidjson::Value xmrig::CpuConfig::toJSON(rapidjson::Document &doc) const
|
||||
|
||||
obj.AddMember(StringRef(kEnabled), m_enabled, allocator);
|
||||
obj.AddMember(StringRef(kHugePages), m_hugePages, allocator);
|
||||
obj.AddMember(StringRef(kHugePagesJit), m_hugePagesJit, allocator);
|
||||
obj.AddMember(StringRef(kHwAes), m_aes == AES_AUTO ? Value(kNullType) : Value(m_aes == AES_HW), allocator);
|
||||
obj.AddMember(StringRef(kPriority), priority() != -1 ? Value(priority()) : Value(kNullType), allocator);
|
||||
obj.AddMember(StringRef(kMemoryPool), m_memoryPool < 1 ? Value(m_memoryPool < 0) : Value(m_memoryPool), allocator);
|
||||
@@ -110,19 +113,20 @@ size_t xmrig::CpuConfig::memPoolSize() const
|
||||
}
|
||||
|
||||
|
||||
std::vector<xmrig::CpuLaunchData> xmrig::CpuConfig::get(const Miner *miner, const Algorithm &algorithm) const
|
||||
std::vector<xmrig::CpuLaunchData> xmrig::CpuConfig::get(const Miner *miner, const Algorithm &algorithm, uint32_t benchSize) const
|
||||
{
|
||||
std::vector<CpuLaunchData> out;
|
||||
const CpuThreads &threads = m_threads.get(algorithm);
|
||||
const auto &threads = m_threads.get(algorithm);
|
||||
|
||||
if (threads.isEmpty()) {
|
||||
return out;
|
||||
}
|
||||
|
||||
out.reserve(threads.count());
|
||||
const size_t count = threads.count();
|
||||
out.reserve(count);
|
||||
|
||||
for (const CpuThread &thread : threads.data()) {
|
||||
out.emplace_back(miner, algorithm, *this, thread);
|
||||
for (const auto &thread : threads.data()) {
|
||||
out.emplace_back(miner, algorithm, *this, thread, benchSize, count);
|
||||
}
|
||||
|
||||
return out;
|
||||
@@ -132,10 +136,11 @@ std::vector<xmrig::CpuLaunchData> xmrig::CpuConfig::get(const Miner *miner, cons
|
||||
void xmrig::CpuConfig::read(const rapidjson::Value &value)
|
||||
{
|
||||
if (value.IsObject()) {
|
||||
m_enabled = Json::getBool(value, kEnabled, m_enabled);
|
||||
m_hugePages = Json::getBool(value, kHugePages, m_hugePages);
|
||||
m_limit = Json::getUint(value, kMaxThreadsHint, m_limit);
|
||||
m_yield = Json::getBool(value, kYield, m_yield);
|
||||
m_enabled = Json::getBool(value, kEnabled, m_enabled);
|
||||
m_hugePages = Json::getBool(value, kHugePages, m_hugePages);
|
||||
m_hugePagesJit = Json::getBool(value, kHugePagesJit, m_hugePagesJit);
|
||||
m_limit = Json::getUint(value, kMaxThreadsHint, m_limit);
|
||||
m_yield = Json::getBool(value, kYield, m_yield);
|
||||
|
||||
setAesMode(Json::getValue(value, kHwAes));
|
||||
setPriority(Json::getInt(value, kPriority, -1));
|
||||
|
||||
@@ -44,16 +44,40 @@ public:
|
||||
AES_SOFT
|
||||
};
|
||||
|
||||
static const char *kEnabled;
|
||||
static const char *kField;
|
||||
static const char *kHugePages;
|
||||
static const char *kHugePagesJit;
|
||||
static const char *kHwAes;
|
||||
static const char *kMaxThreadsHint;
|
||||
static const char *kMemoryPool;
|
||||
static const char *kPriority;
|
||||
static const char *kYield;
|
||||
|
||||
# ifdef XMRIG_FEATURE_ASM
|
||||
static const char *kAsm;
|
||||
# endif
|
||||
|
||||
# ifdef XMRIG_ALGO_ARGON2
|
||||
static const char *kArgon2Impl;
|
||||
# endif
|
||||
|
||||
# ifdef XMRIG_ALGO_ASTROBWT
|
||||
static const char *kAstroBWTMaxSize;
|
||||
static const char *kAstroBWTAVX2;
|
||||
# endif
|
||||
|
||||
CpuConfig() = default;
|
||||
|
||||
bool isHwAES() const;
|
||||
rapidjson::Value toJSON(rapidjson::Document &doc) const;
|
||||
size_t memPoolSize() const;
|
||||
std::vector<CpuLaunchData> get(const Miner *miner, const Algorithm &algorithm) const;
|
||||
std::vector<CpuLaunchData> get(const Miner *miner, const Algorithm &algorithm, uint32_t benchSize) const;
|
||||
void read(const rapidjson::Value &value);
|
||||
|
||||
inline bool isEnabled() const { return m_enabled; }
|
||||
inline bool isHugePages() const { return m_hugePages; }
|
||||
inline bool isHugePagesJit() const { return m_hugePagesJit; }
|
||||
inline bool isShouldSave() const { return m_shouldSave; }
|
||||
inline bool isYield() const { return m_yield; }
|
||||
inline const Assembly &assembly() const { return m_assembly; }
|
||||
@@ -76,6 +100,7 @@ private:
|
||||
bool m_astrobwtAVX2 = false;
|
||||
bool m_enabled = true;
|
||||
bool m_hugePages = true;
|
||||
bool m_hugePagesJit = false;
|
||||
bool m_shouldSave = false;
|
||||
bool m_yield = true;
|
||||
int m_astrobwtMaxSize = 550;
|
||||
|
||||
@@ -145,7 +145,7 @@ size_t inline generate<Algorithm::RANDOM_X>(Threads<CpuThreads> &threads, uint32
|
||||
template<>
|
||||
size_t inline generate<Algorithm::ARGON2>(Threads<CpuThreads> &threads, uint32_t limit)
|
||||
{
|
||||
return generate("argon2", threads, Algorithm::AR2_CHUKWA, limit);
|
||||
return generate("argon2", threads, Algorithm::AR2_CHUKWA_V2, limit);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
xmrig::CpuLaunchData::CpuLaunchData(const Miner *miner, const Algorithm &algorithm, const CpuConfig &config, const CpuThread &thread) :
|
||||
xmrig::CpuLaunchData::CpuLaunchData(const Miner *miner, const Algorithm &algorithm, const CpuConfig &config, const CpuThread &thread, uint32_t benchSize, size_t threads) :
|
||||
algorithm(algorithm),
|
||||
assembly(config.assembly()),
|
||||
astrobwtAVX2(config.astrobwtAVX2()),
|
||||
@@ -43,6 +43,8 @@ xmrig::CpuLaunchData::CpuLaunchData(const Miner *miner, const Algorithm &algorit
|
||||
priority(config.priority()),
|
||||
affinity(thread.affinity()),
|
||||
miner(miner),
|
||||
threads(threads),
|
||||
benchSize(benchSize),
|
||||
intensity(std::min<uint32_t>(thread.intensity(), algorithm.maxIntensity()))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -44,12 +44,12 @@ class Miner;
|
||||
class CpuLaunchData
|
||||
{
|
||||
public:
|
||||
CpuLaunchData(const Miner *miner, const Algorithm &algorithm, const CpuConfig &config, const CpuThread &thread);
|
||||
CpuLaunchData(const Miner *miner, const Algorithm &algorithm, const CpuConfig &config, const CpuThread &thread, uint32_t benchSize, size_t threads);
|
||||
|
||||
bool isEqual(const CpuLaunchData &other) const;
|
||||
CnHash::AlgoVariant av() const;
|
||||
|
||||
inline constexpr static Nonce::Backend backend() { return Nonce::CPU; }
|
||||
inline constexpr static Nonce::Backend backend() { return Nonce::CPU; }
|
||||
|
||||
inline bool operator!=(const CpuLaunchData &other) const { return !isEqual(other); }
|
||||
inline bool operator==(const CpuLaunchData &other) const { return isEqual(other); }
|
||||
@@ -66,6 +66,8 @@ public:
|
||||
const int priority;
|
||||
const int64_t affinity;
|
||||
const Miner *miner;
|
||||
const size_t threads;
|
||||
const uint32_t benchSize;
|
||||
const uint32_t intensity;
|
||||
};
|
||||
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
|
||||
|
||||
#include "backend/cpu/CpuWorker.h"
|
||||
#include "base/tools/Chrono.h"
|
||||
#include "core/config/Config.h"
|
||||
#include "core/Miner.h"
|
||||
#include "crypto/cn/CnCtx.h"
|
||||
#include "crypto/cn/CryptoNight_test.h"
|
||||
@@ -51,15 +53,20 @@
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef XMRIG_FEATURE_BENCHMARK
|
||||
# include "backend/common/benchmark/BenchState.h"
|
||||
#endif
|
||||
|
||||
|
||||
namespace xmrig {
|
||||
|
||||
static constexpr uint32_t kReserveCount = 32768;
|
||||
|
||||
|
||||
template<size_t N>
|
||||
inline bool nextRound(WorkerJob<N> &job)
|
||||
inline bool nextRound(WorkerJob<N> &job, uint32_t benchSize)
|
||||
{
|
||||
if (!job.nextRound(kReserveCount, 1)) {
|
||||
if (!job.nextRound(benchSize ? 1 : kReserveCount, 1)) {
|
||||
JobResults::done(job.currentJob());
|
||||
|
||||
return false;
|
||||
@@ -84,6 +91,8 @@ xmrig::CpuWorker<N>::CpuWorker(size_t id, const CpuLaunchData &data) :
|
||||
m_av(data.av()),
|
||||
m_astrobwtMaxSize(data.astrobwtMaxSize * 1000),
|
||||
m_miner(data.miner),
|
||||
m_threads(data.threads),
|
||||
m_benchSize(data.benchSize),
|
||||
m_ctx()
|
||||
{
|
||||
m_memory = new VirtualMemory(m_algorithm.l3() * N, data.hugePages, false, true, m_node);
|
||||
@@ -180,6 +189,7 @@ bool xmrig::CpuWorker<N>::selfTest()
|
||||
# ifdef XMRIG_ALGO_ARGON2
|
||||
if (m_algorithm.family() == Algorithm::ARGON2) {
|
||||
return verify(Algorithm::AR2_CHUKWA, argon2_chukwa_test_out) &&
|
||||
verify(Algorithm::AR2_CHUKWA_V2, argon2_chukwa_v2_test_out) &&
|
||||
verify(Algorithm::AR2_WRKZ, argon2_wrkz_test_out);
|
||||
}
|
||||
# endif
|
||||
@@ -211,23 +221,12 @@ void xmrig::CpuWorker<N>::start()
|
||||
consumeJob();
|
||||
}
|
||||
|
||||
uint64_t storeStatsMask = 7;
|
||||
|
||||
# ifdef XMRIG_ALGO_RANDOMX
|
||||
bool first = true;
|
||||
uint64_t tempHash[8] = {};
|
||||
|
||||
// RandomX is faster, we don't need to store stats so often
|
||||
if (m_job.currentJob().algorithm().family() == Algorithm::RANDOM_X) {
|
||||
storeStatsMask = 63;
|
||||
}
|
||||
alignas(16) uint64_t tempHash[8] = {};
|
||||
# endif
|
||||
|
||||
while (!Nonce::isOutdated(Nonce::CPU, m_job.sequence())) {
|
||||
if ((m_count & storeStatsMask) == 0) {
|
||||
storeStats();
|
||||
}
|
||||
|
||||
const Job &job = m_job.currentJob();
|
||||
|
||||
if (job.algorithm().l3() != m_algorithm.l3()) {
|
||||
@@ -239,6 +238,19 @@ void xmrig::CpuWorker<N>::start()
|
||||
current_job_nonces[i] = *m_job.nonce(i);
|
||||
}
|
||||
|
||||
# ifdef XMRIG_FEATURE_BENCHMARK
|
||||
if (m_benchSize) {
|
||||
if (current_job_nonces[0] >= m_benchSize) {
|
||||
return BenchState::done(m_benchData, m_benchDiff, Chrono::steadyMSecs());;
|
||||
}
|
||||
|
||||
// Make each hash dependent on the previous one in single thread benchmark to prevent cheating with multiple threads
|
||||
if (m_threads == 1) {
|
||||
*(uint64_t*)(m_job.blob()) ^= m_benchData;
|
||||
}
|
||||
}
|
||||
# endif
|
||||
|
||||
bool valid = true;
|
||||
|
||||
# ifdef XMRIG_ALGO_RANDOMX
|
||||
@@ -248,7 +260,7 @@ void xmrig::CpuWorker<N>::start()
|
||||
randomx_calculate_hash_first(m_vm, tempHash, m_job.blob(), job.size());
|
||||
}
|
||||
|
||||
if (!nextRound(m_job)) {
|
||||
if (!nextRound(m_job, m_benchSize)) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -268,14 +280,25 @@ void xmrig::CpuWorker<N>::start()
|
||||
fn(job.algorithm())(m_job.blob(), job.size(), m_hash, m_ctx, job.height());
|
||||
}
|
||||
|
||||
if (!nextRound(m_job)) {
|
||||
if (!nextRound(m_job, m_benchSize)) {
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
if (valid) {
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
if (*reinterpret_cast<uint64_t*>(m_hash + (i * 32) + 24) < job.target()) {
|
||||
const uint64_t value = *reinterpret_cast<uint64_t*>(m_hash + (i * 32) + 24);
|
||||
|
||||
# ifdef XMRIG_FEATURE_BENCHMARK
|
||||
if (m_benchSize) {
|
||||
if (current_job_nonces[i] < m_benchSize) {
|
||||
m_benchData ^= value;
|
||||
m_benchDiff = std::max(m_benchDiff, Job::toDiff(value));
|
||||
}
|
||||
}
|
||||
else
|
||||
# endif
|
||||
if (value < job.target()) {
|
||||
JobResults::submit(job, current_job_nonces[i], m_hash + (i * 32));
|
||||
}
|
||||
}
|
||||
@@ -372,7 +395,11 @@ void xmrig::CpuWorker<N>::consumeJob()
|
||||
return;
|
||||
}
|
||||
|
||||
m_job.add(m_miner->job(), kReserveCount, Nonce::CPU);
|
||||
m_job.add(m_miner->job(), m_benchSize ? 1 : kReserveCount, Nonce::CPU);
|
||||
|
||||
# ifdef XMRIG_FEATURE_BENCHMARK
|
||||
m_benchData = 0;
|
||||
# endif
|
||||
|
||||
# ifdef XMRIG_ALGO_RANDOMX
|
||||
if (m_job.currentJob().algorithm().family() == Algorithm::RANDOM_X) {
|
||||
|
||||
@@ -73,6 +73,7 @@ private:
|
||||
void allocateCnCtx();
|
||||
void consumeJob();
|
||||
|
||||
alignas(16) uint8_t m_hash[N * 32]{ 0 };
|
||||
const Algorithm m_algorithm;
|
||||
const Assembly m_assembly;
|
||||
const bool m_astrobwtAVX2;
|
||||
@@ -81,14 +82,20 @@ private:
|
||||
const CnHash::AlgoVariant m_av;
|
||||
const int m_astrobwtMaxSize;
|
||||
const Miner *m_miner;
|
||||
const size_t m_threads;
|
||||
const uint32_t m_benchSize;
|
||||
cryptonight_ctx *m_ctx[N];
|
||||
uint8_t m_hash[N * 32]{ 0 };
|
||||
VirtualMemory *m_memory = nullptr;
|
||||
WorkerJob<N> m_job;
|
||||
|
||||
# ifdef XMRIG_ALGO_RANDOMX
|
||||
randomx_vm *m_vm = nullptr;
|
||||
# endif
|
||||
|
||||
# ifdef XMRIG_FEATURE_BENCHMARK
|
||||
uint64_t m_benchData = 0;
|
||||
uint64_t m_benchDiff = 0;
|
||||
# endif
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ if (WITH_HWLOC)
|
||||
src/backend/cpu/platform/HwlocCpuInfo.h
|
||||
)
|
||||
elseif (WITH_LIBCPUID)
|
||||
message(WARNING, "libcpuid support is deprecated and will be removed in future versions.")
|
||||
set(WITH_HWLOC OFF)
|
||||
|
||||
add_subdirectory(src/3rdparty/libcpuid)
|
||||
|
||||
@@ -48,12 +48,15 @@ public:
|
||||
|
||||
enum MsrMod : uint32_t {
|
||||
MSR_MOD_NONE,
|
||||
MSR_MOD_RYZEN,
|
||||
MSR_MOD_RYZEN_17H,
|
||||
MSR_MOD_RYZEN_19H,
|
||||
MSR_MOD_INTEL,
|
||||
MSR_MOD_CUSTOM,
|
||||
MSR_MOD_MAX
|
||||
};
|
||||
|
||||
# define MSR_NAMES_LIST "none", "ryzen_17h", "ryzen_19h", "intel", "custom"
|
||||
|
||||
enum Flag : uint32_t {
|
||||
FLAG_AES,
|
||||
FLAG_AVX2,
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
/* XMRig
|
||||
* Copyright 2010 Jeff Garzik <jgarzik@pobox.com>
|
||||
* Copyright 2012-2014 pooler <pooler@litecoinpool.org>
|
||||
* Copyright 2014 Lucas Jones <https://github.com/lucasjones>
|
||||
* Copyright 2014-2016 Wolf9466 <https://github.com/OhGodAPet>
|
||||
* Copyright 2016 Jay D Dee <jayddee246@gmail.com>
|
||||
* Copyright 2017-2019 XMR-Stak <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
|
||||
* Copyright 2018-2020 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright 2016-2020 XMRig <support@xmrig.com>
|
||||
* Copyright (c) 2017-2019 XMR-Stak <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
|
||||
* Copyright (c) 2018-2020 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2016-2020 XMRig <support@xmrig.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -57,8 +52,16 @@
|
||||
namespace xmrig {
|
||||
|
||||
|
||||
static const std::array<const char *, ICpuInfo::FLAG_MAX> flagNames = { "aes", "avx2", "avx512f", "bmi2", "osxsave", "pdpe1gb", "sse2", "ssse3", "sse4.1", "xop", "popcnt", "cat_l3" };
|
||||
static const std::array<const char *, ICpuInfo::MSR_MOD_MAX> msrNames = { "none", "ryzen", "intel", "custom" };
|
||||
constexpr size_t kCpuFlagsSize = 12;
|
||||
static const std::array<const char *, kCpuFlagsSize> flagNames = { "aes", "avx2", "avx512f", "bmi2", "osxsave", "pdpe1gb", "sse2", "ssse3", "sse4.1", "xop", "popcnt", "cat_l3" };
|
||||
static_assert(kCpuFlagsSize == ICpuInfo::FLAG_MAX, "kCpuFlagsSize and FLAG_MAX mismatch");
|
||||
|
||||
|
||||
#ifdef XMRIG_FEATURE_MSR
|
||||
constexpr size_t kMsrArraySize = 5;
|
||||
static const std::array<const char *, kMsrArraySize> msrNames = { MSR_NAMES_LIST };
|
||||
static_assert(kMsrArraySize == ICpuInfo::MSR_MOD_MAX, "kMsrArraySize and MSR_MOD_MAX mismatch");
|
||||
#endif
|
||||
|
||||
|
||||
static inline void cpuid(uint32_t level, int32_t output[4])
|
||||
@@ -194,15 +197,32 @@ xmrig::BasicCpuInfo::BasicCpuInfo() :
|
||||
memcpy(vendor + 4, &data[3], 4);
|
||||
memcpy(vendor + 8, &data[2], 4);
|
||||
|
||||
cpuid(PROCESSOR_INFO, data);
|
||||
|
||||
m_procInfo = data[EAX_Reg];
|
||||
m_family = get_masked(m_procInfo, 12, 8) + get_masked(m_procInfo, 28, 20);
|
||||
m_model = (get_masked(m_procInfo, 20, 16) << 4) | get_masked(m_procInfo, 8, 4);
|
||||
m_stepping = get_masked(m_procInfo, 4, 0);
|
||||
|
||||
if (memcmp(vendor, "AuthenticAMD", 12) == 0) {
|
||||
m_vendor = VENDOR_AMD;
|
||||
|
||||
cpuid(PROCESSOR_INFO, data);
|
||||
const int32_t family = get_masked(data[EAX_Reg], 12, 8) + get_masked(data[EAX_Reg], 28, 20);
|
||||
|
||||
if (family >= 23) {
|
||||
if (m_family >= 0x17) {
|
||||
m_assembly = Assembly::RYZEN;
|
||||
m_msrMod = MSR_MOD_RYZEN;
|
||||
|
||||
switch (m_family) {
|
||||
case 0x17:
|
||||
m_msrMod = MSR_MOD_RYZEN_17H;
|
||||
break;
|
||||
|
||||
case 0x19:
|
||||
m_msrMod = MSR_MOD_RYZEN_19H;
|
||||
break;
|
||||
|
||||
default:
|
||||
m_msrMod = MSR_MOD_NONE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
m_assembly = Assembly::BULLDOZER;
|
||||
@@ -225,7 +245,6 @@ xmrig::BasicCpuInfo::BasicCpuInfo() :
|
||||
unsigned int reserved2 : 4;
|
||||
} processor_info;
|
||||
|
||||
cpuid(1, data);
|
||||
memcpy(&processor_info, data, sizeof(processor_info));
|
||||
|
||||
// Intel JCC erratum mitigation
|
||||
@@ -319,6 +338,10 @@ rapidjson::Value xmrig::BasicCpuInfo::toJSON(rapidjson::Document &doc) const
|
||||
Value out(kObjectType);
|
||||
|
||||
out.AddMember("brand", StringRef(brand()), allocator);
|
||||
out.AddMember("family", m_family, allocator);
|
||||
out.AddMember("model", m_model, allocator);
|
||||
out.AddMember("stepping", m_stepping, allocator);
|
||||
out.AddMember("proc_info", m_procInfo, allocator);
|
||||
out.AddMember("aes", hasAES(), allocator);
|
||||
out.AddMember("avx2", hasAVX2(), allocator);
|
||||
out.AddMember("x64", isX64(), allocator);
|
||||
@@ -329,7 +352,12 @@ rapidjson::Value xmrig::BasicCpuInfo::toJSON(rapidjson::Document &doc) const
|
||||
out.AddMember("packages", static_cast<uint64_t>(packages()), allocator);
|
||||
out.AddMember("nodes", static_cast<uint64_t>(nodes()), allocator);
|
||||
out.AddMember("backend", StringRef(backend()), allocator);
|
||||
|
||||
# ifdef XMRIG_FEATURE_MSR
|
||||
out.AddMember("msr", StringRef(msrNames[msrMod()]), allocator);
|
||||
# else
|
||||
out.AddMember("msr", "none", allocator);
|
||||
# endif
|
||||
|
||||
# ifdef XMRIG_FEATURE_ASM
|
||||
out.AddMember("assembly", StringRef(Assembly(assembly()).toString()), allocator);
|
||||
@@ -337,6 +365,12 @@ rapidjson::Value xmrig::BasicCpuInfo::toJSON(rapidjson::Document &doc) const
|
||||
out.AddMember("assembly", "none", allocator);
|
||||
# endif
|
||||
|
||||
# if defined(__x86_64__) || defined(_M_AMD64)
|
||||
out.AddMember("arch", "x86_64", allocator);
|
||||
# else
|
||||
out.AddMember("arch", "x86", allocator);
|
||||
# endif
|
||||
|
||||
Value flags(kArrayType);
|
||||
|
||||
for (size_t i = 0; i < flagNames.size(); ++i) {
|
||||
|
||||
@@ -70,6 +70,10 @@ protected:
|
||||
bool m_jccErratum = false;
|
||||
|
||||
private:
|
||||
uint32_t m_procInfo = 0;
|
||||
uint32_t m_family = 0;
|
||||
uint32_t m_model = 0;
|
||||
uint32_t m_stepping = 0;
|
||||
Assembly m_assembly = Assembly::NONE;
|
||||
MsrMod m_msrMod = MSR_MOD_NONE;
|
||||
std::bitset<FLAG_MAX> m_flags;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* XMRig
|
||||
* Copyright 2018-2020 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright 2016-2020 XMRig <support@xmrig.com>
|
||||
* Copyright (c) 2018-2020 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2016-2020 XMRig <support@xmrig.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <thread>
|
||||
|
||||
|
||||
@@ -66,6 +67,8 @@ xmrig::BasicCpuInfo::BasicCpuInfo() :
|
||||
if (!name.isNull()) {
|
||||
strncpy(m_brand, name, sizeof(m_brand) - 1);
|
||||
}
|
||||
|
||||
m_flags.set(FLAG_PDPE1GB, std::ifstream("/sys/kernel/mm/hugepages/hugepages-1048576kB/nr_hugepages").good());
|
||||
# endif
|
||||
}
|
||||
|
||||
@@ -103,6 +106,12 @@ rapidjson::Value xmrig::BasicCpuInfo::toJSON(rapidjson::Document &doc) const
|
||||
out.AddMember("msr", "none", allocator);
|
||||
out.AddMember("assembly", "none", allocator);
|
||||
|
||||
# ifdef XMRIG_ARMv8
|
||||
out.AddMember("arch", "aarch64", allocator);
|
||||
# else
|
||||
out.AddMember("arch", "aarch32", allocator);
|
||||
# endif
|
||||
|
||||
Value flags(kArrayType);
|
||||
|
||||
if (hasAES()) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* XMRig
|
||||
* Copyright 2018 Riku Voipio <riku.voipio@iki.fi>
|
||||
* Copyright 2018-2020 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright 2016-2020 XMRig <support@xmrig.com>
|
||||
* Copyright (c) 2018 Riku Voipio <riku.voipio@iki.fi>
|
||||
* Copyright (c) 2018-2020 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2016-2020 XMRig <support@xmrig.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
|
||||
#include "base/tools/String.h"
|
||||
#include "3rdparty/fmt/core.h"
|
||||
|
||||
|
||||
#include <cstdio>
|
||||
@@ -289,7 +290,8 @@ static bool arm_cpu_decode(lscpu_desc *desc)
|
||||
|
||||
for (size_t i = 0; impl.parts[i].id != -1; ++i) {
|
||||
if (impl.parts[i].id == model) {
|
||||
desc->model = impl.parts[i].name;
|
||||
desc->vendor = impl.name;
|
||||
desc->model = impl.parts[i].name;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -304,7 +306,7 @@ String cpu_name_arm()
|
||||
{
|
||||
lscpu_desc desc;
|
||||
if (read_basicinfo(&desc) && arm_cpu_decode(&desc)) {
|
||||
return desc.model;
|
||||
return fmt::format("{} {}", desc.vendor, desc.model).c_str();
|
||||
}
|
||||
|
||||
return {};
|
||||
|
||||
@@ -409,9 +409,9 @@ void xmrig::CudaBackend::printHashrate(bool details)
|
||||
Log::print("| %8zu | %8" PRId64 " | %8s | %8s | %8s |" CYAN_BOLD(" #%u") YELLOW(" %s") GREEN(" %s"),
|
||||
i,
|
||||
data.thread.affinity(),
|
||||
Hashrate::format(hashrate()->calc(i, Hashrate::ShortInterval) * scale, num, sizeof num / 3),
|
||||
Hashrate::format(hashrate()->calc(i, Hashrate::MediumInterval) * scale, num + 16, sizeof num / 3),
|
||||
Hashrate::format(hashrate()->calc(i, Hashrate::LargeInterval) * scale, num + 16 * 2, sizeof num / 3),
|
||||
Hashrate::format(hashrate()->calc(i + 1, Hashrate::ShortInterval) * scale, num, sizeof num / 3),
|
||||
Hashrate::format(hashrate()->calc(i + 1, Hashrate::MediumInterval) * scale, num + 16, sizeof num / 3),
|
||||
Hashrate::format(hashrate()->calc(i + 1, Hashrate::LargeInterval) * scale, num + 16 * 2, sizeof num / 3),
|
||||
data.device.index(),
|
||||
data.device.topology().toString().data(),
|
||||
data.device.name().data()
|
||||
@@ -421,9 +421,9 @@ void xmrig::CudaBackend::printHashrate(bool details)
|
||||
}
|
||||
|
||||
Log::print(WHITE_BOLD_S "| - | - | %8s | %8s | %8s |",
|
||||
Hashrate::format(hashrate()->calc(Hashrate::ShortInterval) * scale, num, sizeof num / 3),
|
||||
Hashrate::format(hashrate()->calc(Hashrate::MediumInterval) * scale, num + 16, sizeof num / 3),
|
||||
Hashrate::format(hashrate()->calc(Hashrate::LargeInterval) * scale, num + 16 * 2, sizeof num / 3)
|
||||
Hashrate::format(hashrate_short * scale, num, sizeof num / 3),
|
||||
Hashrate::format(hashrate_medium * scale, num + 16, sizeof num / 3),
|
||||
Hashrate::format(hashrate_large * scale, num + 16 * 2, sizeof num / 3)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -501,9 +501,9 @@ void xmrig::CudaBackend::stop()
|
||||
}
|
||||
|
||||
|
||||
void xmrig::CudaBackend::tick(uint64_t ticks)
|
||||
bool xmrig::CudaBackend::tick(uint64_t ticks)
|
||||
{
|
||||
d_ptr->workers.tick(ticks);
|
||||
return d_ptr->workers.tick(ticks);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -63,13 +63,18 @@ protected:
|
||||
void setJob(const Job &job) override;
|
||||
void start(IWorker *worker, bool ready) override;
|
||||
void stop() override;
|
||||
void tick(uint64_t ticks) override;
|
||||
bool tick(uint64_t ticks) override;
|
||||
|
||||
# ifdef XMRIG_FEATURE_API
|
||||
rapidjson::Value toJSON(rapidjson::Document &doc) const override;
|
||||
void handleRequest(IApiRequest &request) override;
|
||||
# endif
|
||||
|
||||
# ifdef XMRIG_FEATURE_BENCHMARK
|
||||
inline Benchmark *benchmark() const override { return nullptr; }
|
||||
inline void printBenchProgress() const override {}
|
||||
# endif
|
||||
|
||||
private:
|
||||
CudaBackendPrivate *d_ptr;
|
||||
};
|
||||
|
||||
@@ -30,9 +30,9 @@
|
||||
|
||||
xmrig::CudaLaunchData::CudaLaunchData(const Miner *miner, const Algorithm &algorithm, const CudaThread &thread, const CudaDevice &device) :
|
||||
algorithm(algorithm),
|
||||
miner(miner),
|
||||
device(device),
|
||||
thread(thread)
|
||||
thread(thread),
|
||||
miner(miner)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -54,9 +54,10 @@ public:
|
||||
static const char *tag();
|
||||
|
||||
const Algorithm algorithm;
|
||||
const Miner *miner;
|
||||
const CudaDevice &device;
|
||||
const CudaThread thread;
|
||||
const Miner *miner;
|
||||
const uint32_t benchSize = 0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -62,7 +62,6 @@ std::atomic<bool> CudaWorker::ready;
|
||||
|
||||
|
||||
static inline bool isReady() { return !Nonce::isPaused() && CudaWorker::ready; }
|
||||
static inline uint32_t roundSize(uint32_t intensity) { return kReserveCount / intensity + 1; }
|
||||
|
||||
|
||||
} // namespace xmrig
|
||||
@@ -120,6 +119,12 @@ xmrig::CudaWorker::~CudaWorker()
|
||||
}
|
||||
|
||||
|
||||
uint64_t xmrig::CudaWorker::rawHashes() const
|
||||
{
|
||||
return m_hashrateData.interpolate(Chrono::steadyMSecs());
|
||||
}
|
||||
|
||||
|
||||
void xmrig::CudaWorker::jobEarlyNotification(const Job& job)
|
||||
{
|
||||
if (m_runner) {
|
||||
@@ -170,8 +175,7 @@ void xmrig::CudaWorker::start()
|
||||
JobResults::submit(m_job.currentJob(), foundNonce, foundCount, m_deviceIndex);
|
||||
}
|
||||
|
||||
const size_t batch_size = intensity();
|
||||
if (!Nonce::isOutdated(Nonce::CUDA, m_job.sequence()) && !m_job.nextRound(roundSize(batch_size), batch_size)) {
|
||||
if (!Nonce::isOutdated(Nonce::CUDA, m_job.sequence()) && !m_job.nextRound(1, intensity())) {
|
||||
JobResults::done(m_job.currentJob());
|
||||
}
|
||||
|
||||
@@ -192,8 +196,7 @@ bool xmrig::CudaWorker::consumeJob()
|
||||
return false;
|
||||
}
|
||||
|
||||
const size_t batch_size = intensity();
|
||||
m_job.add(m_miner->job(), roundSize(batch_size) * batch_size, Nonce::CUDA);
|
||||
m_job.add(m_miner->job(), intensity(), Nonce::CUDA);
|
||||
|
||||
return m_runner->set(m_job.currentJob(), m_job.blob());
|
||||
}
|
||||
@@ -207,5 +210,8 @@ void xmrig::CudaWorker::storeStats()
|
||||
|
||||
m_count += m_runner ? m_runner->processedHashes() : 0;
|
||||
|
||||
const uint64_t timeStamp = Chrono::steadyMSecs();
|
||||
m_hashrateData.addDataPoint(m_count, timeStamp);
|
||||
|
||||
Worker::storeStats();
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#define XMRIG_CUDAWORKER_H
|
||||
|
||||
|
||||
#include "backend/common/HashrateInterpolator.h"
|
||||
#include "backend/common/Worker.h"
|
||||
#include "backend/common/WorkerJob.h"
|
||||
#include "backend/cuda/CudaLaunchData.h"
|
||||
@@ -49,6 +50,7 @@ public:
|
||||
|
||||
~CudaWorker() override;
|
||||
|
||||
uint64_t rawHashes() const override;
|
||||
void jobEarlyNotification(const Job&) override;
|
||||
|
||||
static std::atomic<bool> ready;
|
||||
@@ -67,6 +69,8 @@ private:
|
||||
ICudaRunner *m_runner = nullptr;
|
||||
WorkerJob<1> m_job;
|
||||
uint32_t m_deviceIndex;
|
||||
|
||||
HashrateInterpolator m_hashrateData;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -61,7 +61,6 @@ static const char *kAstroBWTHash = "astroBWTHash";
|
||||
static const char *kAstroBWTPrepare = "astroBWTPrepare";
|
||||
static const char *kCnHash = "cnHash";
|
||||
static const char *kDeviceCount = "deviceCount";
|
||||
static const char *kDeviceInfo = "deviceInfo";
|
||||
static const char *kDeviceInfo_v2 = "deviceInfo_v2";
|
||||
static const char *kDeviceInit = "deviceInit";
|
||||
static const char *kDeviceInt = "deviceInt";
|
||||
@@ -77,7 +76,6 @@ static const char *kRxPrepare = "rxPrepare";
|
||||
static const char *kKawPowHash = "kawPowHash";
|
||||
static const char *kKawPowPrepare_v2 = "kawPowPrepare_v2";
|
||||
static const char *kKawPowStopHash = "kawPowStopHash";
|
||||
static const char *kSetJob = "setJob";
|
||||
static const char *kSetJob_v2 = "setJob_v2";
|
||||
static const char *kVersion = "version";
|
||||
|
||||
@@ -87,7 +85,6 @@ using astroBWTHash_t = bool (*)(nvid_ctx *, u
|
||||
using astroBWTPrepare_t = bool (*)(nvid_ctx *, uint32_t);
|
||||
using cnHash_t = bool (*)(nvid_ctx *, uint32_t, uint64_t, uint64_t, uint32_t *, uint32_t *);
|
||||
using deviceCount_t = uint32_t (*)();
|
||||
using deviceInfo_t = int32_t (*)(nvid_ctx *, int32_t, int32_t, int32_t, int32_t);
|
||||
using deviceInfo_v2_t = bool (*)(nvid_ctx *, int32_t, int32_t, const char *, int32_t);
|
||||
using deviceInit_t = bool (*)(nvid_ctx *);
|
||||
using deviceInt_t = int32_t (*)(nvid_ctx *, CudaLib::DeviceProperty);
|
||||
@@ -103,7 +100,6 @@ using rxPrepare_t = bool (*)(nvid_ctx *, c
|
||||
using kawPowHash_t = bool (*)(nvid_ctx *, uint8_t*, uint64_t, uint32_t *, uint32_t *, uint32_t *);
|
||||
using kawPowPrepare_v2_t = bool (*)(nvid_ctx *, const void *, size_t, const void *, size_t, uint32_t, const uint64_t*);
|
||||
using kawPowStopHash_t = bool (*)(nvid_ctx *);
|
||||
using setJob_t = bool (*)(nvid_ctx *, const void *, size_t, int32_t);
|
||||
using setJob_v2_t = bool (*)(nvid_ctx *, const void *, size_t, const char *);
|
||||
using version_t = uint32_t (*)(Version);
|
||||
|
||||
@@ -113,7 +109,6 @@ static astroBWTHash_t pAstroBWTHash = nullptr;
|
||||
static astroBWTPrepare_t pAstroBWTPrepare = nullptr;
|
||||
static cnHash_t pCnHash = nullptr;
|
||||
static deviceCount_t pDeviceCount = nullptr;
|
||||
static deviceInfo_t pDeviceInfo = nullptr;
|
||||
static deviceInfo_v2_t pDeviceInfo_v2 = nullptr;
|
||||
static deviceInit_t pDeviceInit = nullptr;
|
||||
static deviceInt_t pDeviceInt = nullptr;
|
||||
@@ -129,7 +124,6 @@ static rxPrepare_t pRxPrepare = nullptr;
|
||||
static kawPowHash_t pKawPowHash = nullptr;
|
||||
static kawPowPrepare_v2_t pKawPowPrepare_v2 = nullptr;
|
||||
static kawPowStopHash_t pKawPowStopHash = nullptr;
|
||||
static setJob_t pSetJob = nullptr;
|
||||
static setJob_v2_t pSetJob_v2 = nullptr;
|
||||
static version_t pVersion = nullptr;
|
||||
|
||||
@@ -205,11 +199,7 @@ bool xmrig::CudaLib::deviceInfo(nvid_ctx *ctx, int32_t blocks, int32_t threads,
|
||||
{
|
||||
const Algorithm algo = RxAlgo::id(algorithm);
|
||||
|
||||
if (pDeviceInfo_v2) {
|
||||
return pDeviceInfo_v2(ctx, blocks, threads, algo.isValid() ? algo.shortName() : nullptr, dataset_host);
|
||||
}
|
||||
|
||||
return pDeviceInfo(ctx, blocks, threads, algo, dataset_host) == 0;
|
||||
return pDeviceInfo_v2(ctx, blocks, threads, algo.isValid() ? algo.shortName() : nullptr, dataset_host);
|
||||
}
|
||||
|
||||
|
||||
@@ -252,11 +242,8 @@ bool xmrig::CudaLib::kawPowStopHash(nvid_ctx *ctx) noexcept
|
||||
bool xmrig::CudaLib::setJob(nvid_ctx *ctx, const void *data, size_t size, const Algorithm &algorithm) noexcept
|
||||
{
|
||||
const Algorithm algo = RxAlgo::id(algorithm);
|
||||
if (pSetJob_v2) {
|
||||
return pSetJob_v2(ctx, data, size, algo.shortName());
|
||||
}
|
||||
|
||||
return pSetJob(ctx, data, size, algo);
|
||||
return pSetJob_v2(ctx, data, size, algo.shortName());
|
||||
}
|
||||
|
||||
|
||||
@@ -421,16 +408,8 @@ void xmrig::CudaLib::load()
|
||||
DLSYM(KawPowHash);
|
||||
DLSYM(KawPowPrepare_v2);
|
||||
DLSYM(KawPowStopHash);
|
||||
|
||||
uv_dlsym(&cudaLib, kDeviceInfo_v2, reinterpret_cast<void**>(&pDeviceInfo_v2));
|
||||
if (!pDeviceInfo_v2) {
|
||||
DLSYM(DeviceInfo);
|
||||
}
|
||||
|
||||
uv_dlsym(&cudaLib, kSetJob_v2, reinterpret_cast<void**>(&pSetJob_v2));
|
||||
if (!pSetJob_v2) {
|
||||
DLSYM(SetJob);
|
||||
}
|
||||
DLSYM(DeviceInfo_v2);
|
||||
DLSYM(SetJob_v2);
|
||||
|
||||
pInit();
|
||||
}
|
||||
|
||||
@@ -385,9 +385,9 @@ void xmrig::OclBackend::printHashrate(bool details)
|
||||
Log::print("| %8zu | %8" PRId64 " | %8s | %8s | %8s |" CYAN_BOLD(" #%u") YELLOW(" %s") " %s",
|
||||
i,
|
||||
data.affinity,
|
||||
Hashrate::format(hashrate()->calc(i, Hashrate::ShortInterval) * scale, num, sizeof num / 3),
|
||||
Hashrate::format(hashrate()->calc(i, Hashrate::MediumInterval) * scale, num + 16, sizeof num / 3),
|
||||
Hashrate::format(hashrate()->calc(i, Hashrate::LargeInterval) * scale, num + 16 * 2, sizeof num / 3),
|
||||
Hashrate::format(hashrate()->calc(i + 1, Hashrate::ShortInterval) * scale, num, sizeof num / 3),
|
||||
Hashrate::format(hashrate()->calc(i + 1, Hashrate::MediumInterval) * scale, num + 16, sizeof num / 3),
|
||||
Hashrate::format(hashrate()->calc(i + 1, Hashrate::LargeInterval) * scale, num + 16 * 2, sizeof num / 3),
|
||||
data.device.index(),
|
||||
data.device.topology().toString().data(),
|
||||
data.device.printableName().data()
|
||||
@@ -397,9 +397,9 @@ void xmrig::OclBackend::printHashrate(bool details)
|
||||
}
|
||||
|
||||
Log::print(WHITE_BOLD_S "| - | - | %8s | %8s | %8s |",
|
||||
Hashrate::format(hashrate()->calc(Hashrate::ShortInterval) * scale, num, sizeof num / 3),
|
||||
Hashrate::format(hashrate()->calc(Hashrate::MediumInterval) * scale, num + 16, sizeof num / 3),
|
||||
Hashrate::format(hashrate()->calc(Hashrate::LargeInterval) * scale, num + 16 * 2, sizeof num / 3)
|
||||
Hashrate::format(hashrate_short * scale, num, sizeof num / 3),
|
||||
Hashrate::format(hashrate_medium * scale, num + 16, sizeof num / 3),
|
||||
Hashrate::format(hashrate_large * scale, num + 16 * 2, sizeof num / 3)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -485,9 +485,9 @@ void xmrig::OclBackend::stop()
|
||||
}
|
||||
|
||||
|
||||
void xmrig::OclBackend::tick(uint64_t ticks)
|
||||
bool xmrig::OclBackend::tick(uint64_t ticks)
|
||||
{
|
||||
d_ptr->workers.tick(ticks);
|
||||
return d_ptr->workers.tick(ticks);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -63,13 +63,18 @@ protected:
|
||||
void setJob(const Job &job) override;
|
||||
void start(IWorker *worker, bool ready) override;
|
||||
void stop() override;
|
||||
void tick(uint64_t ticks) override;
|
||||
bool tick(uint64_t ticks) override;
|
||||
|
||||
# ifdef XMRIG_FEATURE_API
|
||||
rapidjson::Value toJSON(rapidjson::Document &doc) const override;
|
||||
void handleRequest(IApiRequest &request) override;
|
||||
# endif
|
||||
|
||||
# ifdef XMRIG_FEATURE_BENCHMARK
|
||||
inline Benchmark *benchmark() const override { return nullptr; }
|
||||
inline void printBenchProgress() const override {}
|
||||
# endif
|
||||
|
||||
private:
|
||||
OclBackendPrivate *d_ptr;
|
||||
};
|
||||
|
||||
@@ -67,6 +67,7 @@ public:
|
||||
const OclDevice device;
|
||||
const OclPlatform platform;
|
||||
const OclThread thread;
|
||||
const uint32_t benchSize = 0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -56,12 +56,10 @@
|
||||
namespace xmrig {
|
||||
|
||||
|
||||
static constexpr uint32_t kReserveCount = 32768;
|
||||
std::atomic<bool> OclWorker::ready;
|
||||
|
||||
|
||||
static inline bool isReady() { return !Nonce::isPaused() && OclWorker::ready; }
|
||||
static inline uint32_t roundSize(uint32_t intensity) { return kReserveCount / intensity + 1; }
|
||||
|
||||
|
||||
static inline void printError(size_t id, const char *error)
|
||||
@@ -78,7 +76,6 @@ xmrig::OclWorker::OclWorker(size_t id, const OclLaunchData &data) :
|
||||
Worker(id, data.affinity, -1),
|
||||
m_algorithm(data.algorithm),
|
||||
m_miner(data.miner),
|
||||
m_intensity(data.thread.intensity()),
|
||||
m_sharedData(OclSharedState::get(data.device.index())),
|
||||
m_deviceIndex(data.device.index())
|
||||
{
|
||||
@@ -140,6 +137,12 @@ xmrig::OclWorker::~OclWorker()
|
||||
}
|
||||
|
||||
|
||||
uint64_t xmrig::OclWorker::rawHashes() const
|
||||
{
|
||||
return m_hashrateData.interpolate(Chrono::steadyMSecs());
|
||||
}
|
||||
|
||||
|
||||
void xmrig::OclWorker::jobEarlyNotification(const Job& job)
|
||||
{
|
||||
if (m_runner) {
|
||||
@@ -164,8 +167,6 @@ void xmrig::OclWorker::start()
|
||||
{
|
||||
cl_uint results[0x100];
|
||||
|
||||
const uint32_t runnerRoundSize = m_runner->roundSize();
|
||||
|
||||
while (Nonce::sequence(Nonce::OPENCL) > 0) {
|
||||
if (!isReady()) {
|
||||
m_sharedData.setResumeCounter(0);
|
||||
@@ -204,7 +205,7 @@ void xmrig::OclWorker::start()
|
||||
JobResults::submit(m_job.currentJob(), results, results[0xFF], m_deviceIndex);
|
||||
}
|
||||
|
||||
if (!Nonce::isOutdated(Nonce::OPENCL, m_job.sequence()) && !m_job.nextRound(roundSize(runnerRoundSize), runnerRoundSize)) {
|
||||
if (!Nonce::isOutdated(Nonce::OPENCL, m_job.sequence()) && !m_job.nextRound(1, intensity())) {
|
||||
JobResults::done(m_job.currentJob());
|
||||
}
|
||||
|
||||
@@ -225,7 +226,7 @@ bool xmrig::OclWorker::consumeJob()
|
||||
return false;
|
||||
}
|
||||
|
||||
m_job.add(m_miner->job(), roundSize(m_intensity) * m_intensity, Nonce::OPENCL);
|
||||
m_job.add(m_miner->job(), intensity(), Nonce::OPENCL);
|
||||
|
||||
try {
|
||||
m_runner->set(m_job.currentJob(), m_job.blob());
|
||||
@@ -247,8 +248,11 @@ void xmrig::OclWorker::storeStats(uint64_t t)
|
||||
}
|
||||
|
||||
m_count += m_runner->processedHashes();
|
||||
const uint64_t timeStamp = Chrono::steadyMSecs();
|
||||
|
||||
m_sharedData.setRunTime(Chrono::steadyMSecs() - t);
|
||||
m_hashrateData.addDataPoint(m_count, timeStamp);
|
||||
|
||||
m_sharedData.setRunTime(timeStamp - t);
|
||||
|
||||
Worker::storeStats();
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#define XMRIG_OCLWORKER_H
|
||||
|
||||
|
||||
#include "backend/common/HashrateInterpolator.h"
|
||||
#include "backend/common/Worker.h"
|
||||
#include "backend/common/WorkerJob.h"
|
||||
#include "backend/opencl/OclLaunchData.h"
|
||||
@@ -50,6 +51,7 @@ public:
|
||||
|
||||
~OclWorker() override;
|
||||
|
||||
uint64_t rawHashes() const override;
|
||||
void jobEarlyNotification(const Job&) override;
|
||||
|
||||
static std::atomic<bool> ready;
|
||||
@@ -65,11 +67,12 @@ private:
|
||||
|
||||
const Algorithm m_algorithm;
|
||||
const Miner *m_miner;
|
||||
const uint32_t m_intensity;
|
||||
IOclRunner *m_runner = nullptr;
|
||||
OclSharedData &m_sharedData;
|
||||
WorkerJob<1> m_job;
|
||||
uint32_t m_deviceIndex;
|
||||
|
||||
HashrateInterpolator m_hashrateData;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -19,11 +19,11 @@
|
||||
#define ALGO_CN_CCX 18
|
||||
#define ALGO_RX_0 19
|
||||
#define ALGO_RX_WOW 20
|
||||
#define ALGO_RX_LOKI 21
|
||||
#define ALGO_RX_ARQMA 22
|
||||
#define ALGO_RX_SFX 23
|
||||
#define ALGO_RX_KEVA 24
|
||||
#define ALGO_AR2_CHUKWA 25
|
||||
#define ALGO_RX_ARQMA 21
|
||||
#define ALGO_RX_SFX 22
|
||||
#define ALGO_RX_KEVA 23
|
||||
#define ALGO_AR2_CHUKWA 24
|
||||
#define ALGO_AR2_CHUKWA_V2 25
|
||||
#define ALGO_AR2_WRKZ 26
|
||||
#define ALGO_ASTROBWT_DERO 27
|
||||
#define ALGO_KAWPOW_RVN 28
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,8 +4,6 @@
|
||||
#include "randomx_constants_monero.h"
|
||||
#elif (ALGO == ALGO_RX_WOW)
|
||||
#include "randomx_constants_wow.h"
|
||||
#elif (ALGO == ALGO_RX_LOKI)
|
||||
#include "randomx_constants_loki.h"
|
||||
#elif (ALGO == ALGO_RX_ARQMA)
|
||||
#include "randomx_constants_arqma.h"
|
||||
#elif (ALGO == ALGO_RX_KEVA)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,96 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2019 SChernykh
|
||||
|
||||
This file is part of RandomX OpenCL.
|
||||
|
||||
RandomX OpenCL is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RandomX OpenCL is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RandomX OpenCL. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//Dataset base size in bytes. Must be a power of 2.
|
||||
#define RANDOMX_DATASET_BASE_SIZE 2147483648
|
||||
|
||||
//Dataset extra size. Must be divisible by 64.
|
||||
#define RANDOMX_DATASET_EXTRA_SIZE 33554368
|
||||
|
||||
//Scratchpad L3 size in bytes. Must be a power of 2.
|
||||
#define RANDOMX_SCRATCHPAD_L3 2097152
|
||||
|
||||
//Scratchpad L2 size in bytes. Must be a power of two and less than or equal to RANDOMX_SCRATCHPAD_L3.
|
||||
#define RANDOMX_SCRATCHPAD_L2 262144
|
||||
|
||||
//Scratchpad L1 size in bytes. Must be a power of two (minimum 64) and less than or equal to RANDOMX_SCRATCHPAD_L2.
|
||||
#define RANDOMX_SCRATCHPAD_L1 16384
|
||||
|
||||
//Jump condition mask size in bits.
|
||||
#define RANDOMX_JUMP_BITS 8
|
||||
|
||||
//Jump condition mask offset in bits. The sum of RANDOMX_JUMP_BITS and RANDOMX_JUMP_OFFSET must not exceed 16.
|
||||
#define RANDOMX_JUMP_OFFSET 8
|
||||
|
||||
//Integer instructions
|
||||
#define RANDOMX_FREQ_IADD_RS 25
|
||||
#define RANDOMX_FREQ_IADD_M 7
|
||||
#define RANDOMX_FREQ_ISUB_R 16
|
||||
#define RANDOMX_FREQ_ISUB_M 7
|
||||
#define RANDOMX_FREQ_IMUL_R 16
|
||||
#define RANDOMX_FREQ_IMUL_M 4
|
||||
#define RANDOMX_FREQ_IMULH_R 4
|
||||
#define RANDOMX_FREQ_IMULH_M 1
|
||||
#define RANDOMX_FREQ_ISMULH_R 4
|
||||
#define RANDOMX_FREQ_ISMULH_M 1
|
||||
#define RANDOMX_FREQ_IMUL_RCP 8
|
||||
#define RANDOMX_FREQ_INEG_R 2
|
||||
#define RANDOMX_FREQ_IXOR_R 15
|
||||
#define RANDOMX_FREQ_IXOR_M 5
|
||||
#define RANDOMX_FREQ_IROR_R 8
|
||||
#define RANDOMX_FREQ_IROL_R 2
|
||||
#define RANDOMX_FREQ_ISWAP_R 4
|
||||
|
||||
//Floating point instructions
|
||||
#define RANDOMX_FREQ_FSWAP_R 4
|
||||
#define RANDOMX_FREQ_FADD_R 16
|
||||
#define RANDOMX_FREQ_FADD_M 5
|
||||
#define RANDOMX_FREQ_FSUB_R 16
|
||||
#define RANDOMX_FREQ_FSUB_M 5
|
||||
#define RANDOMX_FREQ_FSCAL_R 6
|
||||
#define RANDOMX_FREQ_FMUL_R 32
|
||||
#define RANDOMX_FREQ_FDIV_M 4
|
||||
#define RANDOMX_FREQ_FSQRT_R 6
|
||||
|
||||
//Control instructions
|
||||
#define RANDOMX_FREQ_CBRANCH 16
|
||||
#define RANDOMX_FREQ_CFROUND 1
|
||||
|
||||
//Store instruction
|
||||
#define RANDOMX_FREQ_ISTORE 16
|
||||
|
||||
//No-op instruction
|
||||
#define RANDOMX_FREQ_NOP 0
|
||||
|
||||
#define RANDOMX_DATASET_ITEM_SIZE 64
|
||||
|
||||
#define RANDOMX_PROGRAM_SIZE 320
|
||||
|
||||
#define HASH_SIZE 64
|
||||
#define ENTROPY_SIZE (128 + RANDOMX_PROGRAM_SIZE * 8)
|
||||
#define REGISTERS_SIZE 256
|
||||
#define IMM_BUF_SIZE (RANDOMX_PROGRAM_SIZE * 4 - REGISTERS_SIZE)
|
||||
#define IMM_INDEX_COUNT ((IMM_BUF_SIZE / 4) - 2)
|
||||
#define VM_STATE_SIZE (REGISTERS_SIZE + IMM_BUF_SIZE + RANDOMX_PROGRAM_SIZE * 4)
|
||||
#define ROUNDING_MODE (RANDOMX_FREQ_CFROUND ? -1 : 0)
|
||||
|
||||
// Scratchpad L1/L2/L3 bits
|
||||
#define LOC_L1 (32 - 14)
|
||||
#define LOC_L2 (32 - 18)
|
||||
#define LOC_L3 (32 - 21)
|
||||
@@ -1,9 +1,11 @@
|
||||
set(HEADERS_BASE
|
||||
src/3rdparty/fmt/format.cc
|
||||
src/base/api/interfaces/IApiListener.h
|
||||
src/base/crypto/Algorithm.h
|
||||
src/base/crypto/Coin.h
|
||||
src/base/crypto/keccak.h
|
||||
src/base/crypto/sha3.h
|
||||
src/base/io/Async.h
|
||||
src/base/io/Console.h
|
||||
src/base/io/Env.h
|
||||
src/base/io/json/Json.h
|
||||
@@ -21,6 +23,7 @@ set(HEADERS_BASE
|
||||
src/base/kernel/config/BaseTransform.h
|
||||
src/base/kernel/config/Title.h
|
||||
src/base/kernel/Entry.h
|
||||
src/base/kernel/interfaces/IAsyncListener.h
|
||||
src/base/kernel/interfaces/IBaseListener.h
|
||||
src/base/kernel/interfaces/IClient.h
|
||||
src/base/kernel/interfaces/IClientListener.h
|
||||
@@ -73,6 +76,7 @@ set(SOURCES_BASE
|
||||
src/base/crypto/Coin.cpp
|
||||
src/base/crypto/keccak.cpp
|
||||
src/base/crypto/sha3.cpp
|
||||
src/base/io/Async.cpp
|
||||
src/base/io/Console.cpp
|
||||
src/base/io/Env.cpp
|
||||
src/base/io/json/Json.cpp
|
||||
@@ -119,16 +123,19 @@ if (WIN32)
|
||||
set(SOURCES_OS
|
||||
src/base/io/json/Json_win.cpp
|
||||
src/base/kernel/Platform_win.cpp
|
||||
src/base/kernel/Process_win.cpp
|
||||
)
|
||||
elseif (APPLE)
|
||||
set(SOURCES_OS
|
||||
src/base/io/json/Json_unix.cpp
|
||||
src/base/kernel/Platform_mac.cpp
|
||||
src/base/kernel/Process_unix.cpp
|
||||
)
|
||||
else()
|
||||
set(SOURCES_OS
|
||||
src/base/io/json/Json_unix.cpp
|
||||
src/base/kernel/Platform_unix.cpp
|
||||
src/base/kernel/Process_unix.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -226,11 +233,23 @@ endif()
|
||||
if (WITH_PROFILING)
|
||||
add_definitions(/DXMRIG_FEATURE_PROFILING)
|
||||
|
||||
list(APPEND HEADERS_BASE src/base/tools/Profiler.h)
|
||||
list(APPEND SOURCES_BASE src/base/tools/Profiler.cpp)
|
||||
endif()
|
||||
|
||||
|
||||
if (WITH_RANDOMX AND WITH_BENCHMARK)
|
||||
add_definitions(/DXMRIG_FEATURE_BENCHMARK)
|
||||
|
||||
list(APPEND HEADERS_BASE
|
||||
src/base/tools/Profiler.h
|
||||
src/base/net/stratum/benchmark/BenchClient.h
|
||||
src/base/net/stratum/benchmark/BenchConfig.h
|
||||
)
|
||||
|
||||
list(APPEND SOURCES_BASE
|
||||
src/base/tools/Profiler.cpp
|
||||
src/base/net/stratum/benchmark/BenchClient.cpp
|
||||
src/base/net/stratum/benchmark/BenchConfig.cpp
|
||||
)
|
||||
else()
|
||||
remove_definitions(/DXMRIG_FEATURE_BENCHMARK)
|
||||
endif()
|
||||
|
||||
@@ -105,8 +105,6 @@ static AlgoName const algorithm_names[] = {
|
||||
{ "RandomX", "rx", Algorithm::RX_0 },
|
||||
{ "randomx/wow", "rx/wow", Algorithm::RX_WOW },
|
||||
{ "RandomWOW", nullptr, Algorithm::RX_WOW },
|
||||
{ "randomx/loki", "rx/loki", Algorithm::RX_LOKI },
|
||||
{ "RandomXL", nullptr, Algorithm::RX_LOKI },
|
||||
{ "randomx/arq", "rx/arq", Algorithm::RX_ARQ },
|
||||
{ "RandomARQ", nullptr, Algorithm::RX_ARQ },
|
||||
{ "randomx/sfx", "rx/sfx", Algorithm::RX_SFX },
|
||||
@@ -117,6 +115,8 @@ static AlgoName const algorithm_names[] = {
|
||||
# ifdef XMRIG_ALGO_ARGON2
|
||||
{ "argon2/chukwa", nullptr, Algorithm::AR2_CHUKWA },
|
||||
{ "chukwa", nullptr, Algorithm::AR2_CHUKWA },
|
||||
{ "argon2/chukwav2", nullptr, Algorithm::AR2_CHUKWA_V2 },
|
||||
{ "chukwav2", nullptr, Algorithm::AR2_CHUKWA_V2 },
|
||||
{ "argon2/wrkz", nullptr, Algorithm::AR2_WRKZ },
|
||||
# endif
|
||||
# ifdef XMRIG_ALGO_ASTROBWT
|
||||
@@ -160,7 +160,6 @@ size_t xmrig::Algorithm::l2() const
|
||||
# ifdef XMRIG_ALGO_RANDOMX
|
||||
switch (m_id) {
|
||||
case RX_0:
|
||||
case RX_LOKI:
|
||||
case RX_SFX:
|
||||
return 0x40000;
|
||||
|
||||
@@ -208,7 +207,6 @@ size_t xmrig::Algorithm::l3() const
|
||||
if (f == RANDOM_X) {
|
||||
switch (m_id) {
|
||||
case RX_0:
|
||||
case RX_LOKI:
|
||||
case RX_SFX:
|
||||
return oneMiB * 2;
|
||||
|
||||
@@ -231,6 +229,9 @@ size_t xmrig::Algorithm::l3() const
|
||||
case AR2_CHUKWA:
|
||||
return oneMiB / 2;
|
||||
|
||||
case AR2_CHUKWA_V2:
|
||||
return oneMiB;
|
||||
|
||||
case AR2_WRKZ:
|
||||
return oneMiB / 4;
|
||||
|
||||
@@ -331,7 +332,6 @@ xmrig::Algorithm::Family xmrig::Algorithm::family(Id id)
|
||||
# ifdef XMRIG_ALGO_RANDOMX
|
||||
case RX_0:
|
||||
case RX_WOW:
|
||||
case RX_LOKI:
|
||||
case RX_ARQ:
|
||||
case RX_SFX:
|
||||
case RX_KEVA:
|
||||
@@ -340,6 +340,7 @@ xmrig::Algorithm::Family xmrig::Algorithm::family(Id id)
|
||||
|
||||
# ifdef XMRIG_ALGO_ARGON2
|
||||
case AR2_CHUKWA:
|
||||
case AR2_CHUKWA_V2:
|
||||
case AR2_WRKZ:
|
||||
return ARGON2;
|
||||
# endif
|
||||
|
||||
@@ -66,11 +66,11 @@ public:
|
||||
CN_CCX, // "cn/ccx" Conceal (CCX)
|
||||
RX_0, // "rx/0" RandomX (reference configuration).
|
||||
RX_WOW, // "rx/wow" RandomWOW (Wownero).
|
||||
RX_LOKI, // "rx/loki" RandomXL (Loki).
|
||||
RX_ARQ, // "rx/arq" RandomARQ (Arqma).
|
||||
RX_SFX, // "rx/sfx" RandomSFX (Safex Cash).
|
||||
RX_KEVA, // "rx/keva" RandomKEVA (Keva).
|
||||
AR2_CHUKWA, // "argon2/chukwa" Argon2id (Chukwa).
|
||||
AR2_CHUKWA_V2, // "argon2/chukwav2" Argon2id (Chukwa v2).
|
||||
AR2_WRKZ, // "argon2/wrkz" Argon2id (WRKZ)
|
||||
ASTROBWT_DERO, // "astrobwt" AstroBWT (Dero)
|
||||
KAWPOW_RVN, // "kawpow/rvn" KawPow (RVN)
|
||||
|
||||
162
src/base/io/Async.cpp
Normal file
162
src/base/io/Async.cpp
Normal file
@@ -0,0 +1,162 @@
|
||||
/* XMRig
|
||||
* Copyright (c) 2015-2020 libuv project contributors.
|
||||
* Copyright (c) 2020 cohcho <https://github.com/cohcho>
|
||||
* Copyright (c) 2018-2020 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2016-2020 XMRig <https://github.com/xmrig>, <support@xmrig.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "base/io/Async.h"
|
||||
#include "base/kernel/interfaces/IAsyncListener.h"
|
||||
#include "base/tools/Handle.h"
|
||||
|
||||
|
||||
// since 2019.05.16, Version 1.29.0 (Stable) https://github.com/xmrig/xmrig/pull/1889
|
||||
#if (UV_VERSION_MAJOR >= 1) && (UV_VERSION_MINOR >= 29) && defined(__linux__)
|
||||
#include <sys/eventfd.h>
|
||||
#include <sys/poll.h>
|
||||
#include <unistd.h>
|
||||
#include <cstdlib>
|
||||
|
||||
|
||||
namespace xmrig {
|
||||
|
||||
|
||||
struct uv_async_t: uv_poll_t
|
||||
{
|
||||
using uv_async_cb = void (*)(uv_async_t *);
|
||||
~uv_async_t();
|
||||
int m_fd = -1;
|
||||
uv_async_cb m_cb = nullptr;
|
||||
};
|
||||
|
||||
|
||||
using uv_async_cb = uv_async_t::uv_async_cb;
|
||||
|
||||
|
||||
uv_async_t::~uv_async_t()
|
||||
{
|
||||
close(m_fd);
|
||||
}
|
||||
|
||||
|
||||
static void on_schedule(uv_poll_t *handle, int, int)
|
||||
{
|
||||
static uint64_t val;
|
||||
auto async = reinterpret_cast<uv_async_t *>(handle);
|
||||
for (;;) {
|
||||
int r = read(async->m_fd, &val, sizeof(val));
|
||||
|
||||
if (r == sizeof(val))
|
||||
continue;
|
||||
|
||||
if (r != -1)
|
||||
break;
|
||||
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK)
|
||||
break;
|
||||
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
|
||||
abort();
|
||||
}
|
||||
if (async->m_cb) {
|
||||
(*async->m_cb)(async);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int uv_async_init(uv_loop_t *loop, uv_async_t *async, uv_async_cb cb)
|
||||
{
|
||||
int fd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
|
||||
if (fd < 0) {
|
||||
return uv_translate_sys_error(errno);
|
||||
}
|
||||
uv_poll_init(loop, (uv_poll_t *)async, fd);
|
||||
uv_poll_start((uv_poll_t *)async, POLLIN, on_schedule);
|
||||
async->m_cb = cb;
|
||||
async->m_fd = fd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int uv_async_send(uv_async_t *async)
|
||||
{
|
||||
static const uint64_t val = 1;
|
||||
int r;
|
||||
do {
|
||||
r = write(async->m_fd, &val, sizeof(val));
|
||||
}
|
||||
while (r == -1 && errno == EINTR);
|
||||
if (r == sizeof(val) || (r == 1 && (errno == EAGAIN || errno == EWOULDBLOCK))) {
|
||||
return 0;
|
||||
}
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace xmrig
|
||||
#endif
|
||||
|
||||
|
||||
namespace xmrig {
|
||||
|
||||
|
||||
class AsyncPrivate
|
||||
{
|
||||
public:
|
||||
Async::Callback callback;
|
||||
IAsyncListener *listener = nullptr;
|
||||
uv_async_t *async = nullptr;
|
||||
};
|
||||
|
||||
|
||||
} // namespace xmrig
|
||||
|
||||
|
||||
xmrig::Async::Async(Callback callback) : d_ptr(new AsyncPrivate())
|
||||
{
|
||||
d_ptr->callback = std::move(callback);
|
||||
d_ptr->async = new uv_async_t;
|
||||
d_ptr->async->data = this;
|
||||
|
||||
uv_async_init(uv_default_loop(), d_ptr->async, [](uv_async_t *handle) { static_cast<Async *>(handle->data)->d_ptr->callback(); });
|
||||
}
|
||||
|
||||
|
||||
xmrig::Async::Async(IAsyncListener *listener) : d_ptr(new AsyncPrivate())
|
||||
{
|
||||
d_ptr->listener = listener;
|
||||
d_ptr->async = new uv_async_t;
|
||||
d_ptr->async->data = this;
|
||||
|
||||
uv_async_init(uv_default_loop(), d_ptr->async, [](uv_async_t *handle) { static_cast<Async *>(handle->data)->d_ptr->listener->onAsync(); });
|
||||
}
|
||||
|
||||
|
||||
xmrig::Async::~Async()
|
||||
{
|
||||
Handle::close(d_ptr->async);
|
||||
|
||||
delete d_ptr;
|
||||
}
|
||||
|
||||
|
||||
void xmrig::Async::send()
|
||||
{
|
||||
uv_async_send(d_ptr->async);
|
||||
}
|
||||
59
src/base/io/Async.h
Normal file
59
src/base/io/Async.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/* XMRig
|
||||
* Copyright (c) 2015-2020 libuv project contributors.
|
||||
* Copyright (c) 2020 cohcho <https://github.com/cohcho>
|
||||
* Copyright (c) 2018-2020 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2016-2020 XMRig <https://github.com/xmrig>, <support@xmrig.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef XMRIG_ASYNC_H
|
||||
#define XMRIG_ASYNC_H
|
||||
|
||||
|
||||
#include "base/tools/Object.h"
|
||||
|
||||
|
||||
#include <functional>
|
||||
|
||||
|
||||
namespace xmrig {
|
||||
|
||||
|
||||
class AsyncPrivate;
|
||||
class IAsyncListener;
|
||||
|
||||
|
||||
class Async
|
||||
{
|
||||
public:
|
||||
XMRIG_DISABLE_COPY_MOVE_DEFAULT(Async)
|
||||
|
||||
using Callback = std::function<void()>;
|
||||
|
||||
Async(Callback callback);
|
||||
Async(IAsyncListener *listener);
|
||||
~Async();
|
||||
|
||||
void send();
|
||||
|
||||
private:
|
||||
AsyncPrivate *d_ptr;
|
||||
};
|
||||
|
||||
|
||||
} // namespace xmrig
|
||||
|
||||
|
||||
#endif /* XMRIG_ASYNC_H */
|
||||
@@ -103,6 +103,7 @@ private:
|
||||
#define WHITE_S CSI "0;37m" // another name for LT.GRAY
|
||||
#define WHITE_BOLD_S CSI "1;37m" // actually white
|
||||
|
||||
#define RED_BG_BOLD_S CSI "41;1m"
|
||||
#define GREEN_BG_BOLD_S CSI "42;1m"
|
||||
#define YELLOW_BG_BOLD_S CSI "43;1m"
|
||||
#define BLUE_BG_S CSI "44m"
|
||||
@@ -130,6 +131,7 @@ private:
|
||||
#define WHITE(x) WHITE_S x CLEAR
|
||||
#define WHITE_BOLD(x) WHITE_BOLD_S x CLEAR
|
||||
|
||||
#define RED_BG_BOLD(x) RED_BG_BOLD_S x CLEAR
|
||||
#define GREEN_BG_BOLD(x) GREEN_BG_BOLD_S x CLEAR
|
||||
#define YELLOW_BG_BOLD(x) YELLOW_BG_BOLD_S x CLEAR
|
||||
#define BLUE_BG(x) BLUE_BG_S x CLEAR
|
||||
|
||||
@@ -70,6 +70,16 @@ const char *xmrig::Tags::randomx()
|
||||
return tag;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef XMRIG_FEATURE_BENCHMARK
|
||||
const char *xmrig::Tags::bench()
|
||||
{
|
||||
static const char *tag = GREEN_BG_BOLD(WHITE_BOLD_S " bench ");
|
||||
|
||||
return tag;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
@@ -40,6 +40,9 @@ public:
|
||||
# ifdef XMRIG_ALGO_RANDOMX
|
||||
static const char *randomx();
|
||||
# endif
|
||||
# ifdef XMRIG_FEATURE_BENCHMARK
|
||||
static const char *bench();
|
||||
# endif
|
||||
# endif
|
||||
|
||||
# ifdef XMRIG_PROXY_PROJECT
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
#include "base/net/tools/NetBuffer.h"
|
||||
#include "core/config/Config.h"
|
||||
#include "core/config/ConfigTransform.h"
|
||||
#include "version.h"
|
||||
|
||||
|
||||
#ifdef HAVE_SYSLOG_H
|
||||
@@ -132,7 +133,16 @@ private:
|
||||
}
|
||||
|
||||
chain.addFile(Process::location(Process::DataLocation, "config.json"));
|
||||
|
||||
if (read(chain, config)) {
|
||||
return config.release();
|
||||
}
|
||||
|
||||
chain.addFile(Process::location(Process::HomeLocation, "." APP_ID ".json"));
|
||||
if (read(chain, config)) {
|
||||
return config.release();
|
||||
}
|
||||
|
||||
chain.addFile(Process::location(Process::HomeLocation, ".config" XMRIG_DIR_SEPARATOR APP_ID ".json"));
|
||||
if (read(chain, config)) {
|
||||
return config.release();
|
||||
}
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
/* XMRig
|
||||
* Copyright 2010 Jeff Garzik <jgarzik@pobox.com>
|
||||
* Copyright 2012-2014 pooler <pooler@litecoinpool.org>
|
||||
* Copyright 2014 Lucas Jones <https://github.com/lucasjones>
|
||||
* Copyright 2014-2016 Wolf9466 <https://github.com/OhGodAPet>
|
||||
* Copyright 2016 Jay D Dee <jayddee246@gmail.com>
|
||||
* Copyright 2017-2018 XMR-Stak <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
|
||||
* Copyright 2018-2020 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright 2016-2020 XMRig <https://github.com/xmrig>, <support@xmrig.com>
|
||||
* Copyright (c) 2018-2020 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2016-2020 XMRig <https://github.com/xmrig>, <support@xmrig.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -29,7 +23,21 @@
|
||||
|
||||
|
||||
#include "base/kernel/Process.h"
|
||||
#include "3rdparty/fmt/core.h"
|
||||
#include "base/tools/Chrono.h"
|
||||
#include "version.h"
|
||||
|
||||
|
||||
#ifdef XMRIG_OS_WIN
|
||||
# ifdef _MSC_VER
|
||||
# include <direct.h>
|
||||
# define MKDIR(path) _mkdir(path.c_str());
|
||||
# else
|
||||
# define MKDIR(path) mkdir((path).c_str());
|
||||
# endif
|
||||
#else
|
||||
# define MKDIR(path) mkdir(path.c_str(), 0700);
|
||||
#endif
|
||||
|
||||
|
||||
namespace xmrig {
|
||||
@@ -73,7 +81,7 @@ static std::string getPath(Process::Location location)
|
||||
}
|
||||
|
||||
const auto path = std::string(pathBuf, size);
|
||||
const auto pos = path.rfind(Process::kDirSeparator);
|
||||
const auto pos = path.rfind(*XMRIG_DIR_SEPARATOR);
|
||||
|
||||
if (pos != std::string::npos) {
|
||||
return path.substr(0, pos);
|
||||
@@ -116,15 +124,17 @@ xmrig::Process::Process(int argc, char **argv) :
|
||||
srand(static_cast<unsigned int>(Chrono::currentMSecsSinceEpoch() ^ reinterpret_cast<uintptr_t>(this)));
|
||||
|
||||
setDataDir(m_arguments.value("--data-dir", "-d"));
|
||||
}
|
||||
|
||||
# ifdef XMRIG_SHARED_DATADIR
|
||||
if (dataDir.empty()) {
|
||||
dataDir = fmt::format("{}" XMRIG_DIR_SEPARATOR ".xmrig" XMRIG_DIR_SEPARATOR, location(HomeLocation));
|
||||
MKDIR(dataDir);
|
||||
|
||||
int xmrig::Process::pid()
|
||||
{
|
||||
# if UV_VERSION_HEX >= 0x011200
|
||||
return uv_os_getpid();
|
||||
# else
|
||||
return 0;
|
||||
dataDir += APP_KIND;
|
||||
MKDIR(dataDir);
|
||||
|
||||
uv_chdir(dataDir.c_str());
|
||||
}
|
||||
# endif
|
||||
}
|
||||
|
||||
@@ -154,5 +164,5 @@ xmrig::String xmrig::Process::location(Location location, const char *fileName)
|
||||
return path.c_str();
|
||||
}
|
||||
|
||||
return (path + kDirSeparator + fileName).c_str();
|
||||
return fmt::format("{}" XMRIG_DIR_SEPARATOR "{}", path, fileName).c_str();
|
||||
}
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
/* XMRig
|
||||
* Copyright 2010 Jeff Garzik <jgarzik@pobox.com>
|
||||
* Copyright 2012-2014 pooler <pooler@litecoinpool.org>
|
||||
* Copyright 2014 Lucas Jones <https://github.com/lucasjones>
|
||||
* Copyright 2014-2016 Wolf9466 <https://github.com/OhGodAPet>
|
||||
* Copyright 2016 Jay D Dee <jayddee246@gmail.com>
|
||||
* Copyright 2017-2018 XMR-Stak <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
|
||||
* Copyright 2018-2020 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright 2016-2020 XMRig <https://github.com/xmrig>, <support@xmrig.com>
|
||||
* Copyright (c) 2018-2020 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2016-2020 XMRig <https://github.com/xmrig>, <support@xmrig.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -29,6 +23,13 @@
|
||||
#include "base/tools/Arguments.h"
|
||||
|
||||
|
||||
#ifdef WIN32
|
||||
# define XMRIG_DIR_SEPARATOR "\\"
|
||||
#else
|
||||
# define XMRIG_DIR_SEPARATOR "/"
|
||||
#endif
|
||||
|
||||
|
||||
namespace xmrig {
|
||||
|
||||
|
||||
@@ -43,12 +44,6 @@ public:
|
||||
TempLocation
|
||||
};
|
||||
|
||||
# ifdef WIN32
|
||||
constexpr const static char kDirSeparator = '\\';
|
||||
# else
|
||||
constexpr const static char kDirSeparator = '/';
|
||||
# endif
|
||||
|
||||
Process(int argc, char **argv);
|
||||
|
||||
static int pid();
|
||||
|
||||
34
src/base/kernel/Process_unix.cpp
Normal file
34
src/base/kernel/Process_unix.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
/* XMRig
|
||||
* Copyright (c) 2018-2020 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2016-2020 XMRig <https://github.com/xmrig>, <support@xmrig.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include <uv.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
#include "base/kernel/Process.h"
|
||||
|
||||
|
||||
int xmrig::Process::pid()
|
||||
{
|
||||
# if UV_VERSION_HEX >= 0x011200
|
||||
return uv_os_getpid();
|
||||
# else
|
||||
return getpid();
|
||||
# endif
|
||||
}
|
||||
33
src/base/kernel/Process_win.cpp
Normal file
33
src/base/kernel/Process_win.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
/* XMRig
|
||||
* Copyright (c) 2018-2020 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2016-2020 XMRig <https://github.com/xmrig>, <support@xmrig.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include <uv.h>
|
||||
|
||||
|
||||
#include "base/kernel/Process.h"
|
||||
|
||||
|
||||
int xmrig::Process::pid()
|
||||
{
|
||||
# if UV_VERSION_HEX >= 0x011200
|
||||
return uv_os_getpid();
|
||||
# else
|
||||
return GetCurrentProcessId();
|
||||
# endif
|
||||
}
|
||||
@@ -151,7 +151,8 @@ void xmrig::BaseTransform::transform(rapidjson::Document &doc, int key, const ch
|
||||
}
|
||||
break;
|
||||
|
||||
case IConfig::UrlKey: /* --url */
|
||||
case IConfig::UrlKey: /* --url */
|
||||
case IConfig::StressKey: /* --stress */
|
||||
{
|
||||
if (!doc.HasMember(Pools::kPools)) {
|
||||
doc.AddMember(rapidjson::StringRef(Pools::kPools), rapidjson::kArrayType, doc.GetAllocator());
|
||||
@@ -162,7 +163,20 @@ void xmrig::BaseTransform::transform(rapidjson::Document &doc, int key, const ch
|
||||
array.PushBack(rapidjson::kObjectType, doc.GetAllocator());
|
||||
}
|
||||
|
||||
set(doc, array[array.Size() - 1], Pool::kUrl, arg);
|
||||
# ifdef XMRIG_FEATURE_BENCHMARK
|
||||
if (key != IConfig::UrlKey) {
|
||||
set(doc, array[array.Size() - 1], Pool::kUrl,
|
||||
# ifdef XMRIG_FEATURE_TLS
|
||||
"stratum+ssl://randomx.xmrig.com:443"
|
||||
# else
|
||||
"randomx.xmrig.com:3333"
|
||||
# endif
|
||||
);
|
||||
} else
|
||||
# endif
|
||||
{
|
||||
set(doc, array[array.Size() - 1], Pool::kUrl, arg);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
47
src/base/kernel/interfaces/IAsyncListener.h
Normal file
47
src/base/kernel/interfaces/IAsyncListener.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/* XMRig
|
||||
* Copyright 2018-2020 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright 2016-2020 XMRig <https://github.com/xmrig>, <support@xmrig.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef XMRIG_IASYNCLISTENER_H
|
||||
#define XMRIG_IASYNCLISTENER_H
|
||||
|
||||
|
||||
#include "base/tools/Object.h"
|
||||
|
||||
|
||||
namespace xmrig {
|
||||
|
||||
|
||||
class Async;
|
||||
|
||||
|
||||
class IAsyncListener
|
||||
{
|
||||
public:
|
||||
XMRIG_DISABLE_COPY_MOVE(IAsyncListener)
|
||||
|
||||
IAsyncListener() = default;
|
||||
virtual ~IAsyncListener() = default;
|
||||
|
||||
virtual void onAsync() = 0;
|
||||
};
|
||||
|
||||
|
||||
} /* namespace xmrig */
|
||||
|
||||
|
||||
#endif // XMRIG_IASYNCLISTENER_H
|
||||
@@ -77,6 +77,13 @@ public:
|
||||
TitleKey = 1037,
|
||||
NoTitleKey = 1038,
|
||||
PauseOnBatteryKey = 1041,
|
||||
StressKey = 1042,
|
||||
BenchKey = 1043,
|
||||
BenchSubmitKey = 1044,
|
||||
BenchVerifyKey = 1045,
|
||||
BenchSeedKey = 1046,
|
||||
BenchHashKey = 1047,
|
||||
BenchTokenKey = 1048,
|
||||
|
||||
// xmrig common
|
||||
CPUPriorityKey = 1021,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* XMRig
|
||||
* Copyright 2018-2020 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright 2016-2020 XMRig <https://github.com/xmrig>, <support@xmrig.com>
|
||||
* Copyright (c) 2014-2019 heapwolf <https://github.com/heapwolf>
|
||||
* Copyright (c) 2018-2020 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2016-2020 XMRig <https://github.com/xmrig>, <support@xmrig.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -18,6 +19,14 @@
|
||||
|
||||
|
||||
#include "base/net/http/HttpData.h"
|
||||
#include "3rdparty/http-parser/http_parser.h"
|
||||
#include "3rdparty/rapidjson/document.h"
|
||||
#include "3rdparty/rapidjson/error/en.h"
|
||||
#include "base/io/json/Json.h"
|
||||
|
||||
|
||||
#include <uv.h>
|
||||
#include <stdexcept>
|
||||
|
||||
|
||||
namespace xmrig {
|
||||
@@ -42,3 +51,46 @@ bool xmrig::HttpData::isJSON() const
|
||||
|
||||
return type == kApplicationJson || type == kTextPlain;
|
||||
}
|
||||
|
||||
|
||||
const char *xmrig::HttpData::methodName() const
|
||||
{
|
||||
return http_method_str(static_cast<http_method>(method));
|
||||
}
|
||||
|
||||
|
||||
const char *xmrig::HttpData::statusName() const
|
||||
{
|
||||
if (status < 0) {
|
||||
return uv_strerror(status);
|
||||
}
|
||||
|
||||
return http_status_str(static_cast<http_status>(status));
|
||||
}
|
||||
|
||||
|
||||
rapidjson::Document xmrig::HttpData::json() const
|
||||
{
|
||||
if (status < 0) {
|
||||
throw std::runtime_error(statusName());
|
||||
}
|
||||
|
||||
if (!isJSON()) {
|
||||
throw std::runtime_error("the response is not a valid JSON response");
|
||||
}
|
||||
|
||||
using namespace rapidjson;
|
||||
Document doc;
|
||||
if (doc.Parse(body.c_str()).HasParseError()) {
|
||||
throw std::runtime_error(GetParseError_En(doc.GetParseError()));
|
||||
}
|
||||
|
||||
if (doc.IsObject() && !doc.ObjectEmpty()) {
|
||||
const char *error = Json::getString(doc, "error");
|
||||
if (error) {
|
||||
throw std::runtime_error(error);
|
||||
}
|
||||
}
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
/* XMRig
|
||||
* Copyright 2010 Jeff Garzik <jgarzik@pobox.com>
|
||||
* Copyright 2012-2014 pooler <pooler@litecoinpool.org>
|
||||
* Copyright 2014 Lucas Jones <https://github.com/lucasjones>
|
||||
* Copyright 2014-2016 Wolf9466 <https://github.com/OhGodAPet>
|
||||
* Copyright 2016 Jay D Dee <jayddee246@gmail.com>
|
||||
* Copyright 2017-2018 XMR-Stak <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
|
||||
* Copyright 2014-2019 heapwolf <https://github.com/heapwolf>
|
||||
* Copyright 2018-2020 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright 2016-2020 XMRig <https://github.com/xmrig>, <support@xmrig.com>
|
||||
* Copyright (c) 2014-2019 heapwolf <https://github.com/heapwolf>
|
||||
* Copyright (c) 2018-2020 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright (c) 2016-2020 XMRig <https://github.com/xmrig>, <support@xmrig.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -28,6 +22,7 @@
|
||||
#define XMRIG_HTTPDATA_H
|
||||
|
||||
|
||||
#include "3rdparty/rapidjson/document.h"
|
||||
#include "base/tools/Object.h"
|
||||
|
||||
|
||||
@@ -63,6 +58,9 @@ public:
|
||||
virtual void write(std::string &&data, bool close) = 0;
|
||||
|
||||
bool isJSON() const;
|
||||
const char *methodName() const;
|
||||
const char *statusName() const;
|
||||
rapidjson::Document json() const;
|
||||
|
||||
int method = 0;
|
||||
int status = 0;
|
||||
@@ -70,6 +68,7 @@ public:
|
||||
std::map<const std::string, const std::string> headers;
|
||||
std::string body;
|
||||
std::string url;
|
||||
uint64_t rpcId = 0;
|
||||
|
||||
private:
|
||||
const uint64_t m_id;
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "base/kernel/interfaces/IClientListener.h"
|
||||
#include "base/net/http/Fetch.h"
|
||||
#include "base/net/http/HttpData.h"
|
||||
#include "base/net/http/HttpListener.h"
|
||||
#include "base/net/stratum/SubmitResult.h"
|
||||
#include "base/tools/Buffer.h"
|
||||
#include "base/tools/Timer.h"
|
||||
|
||||
@@ -27,8 +27,8 @@
|
||||
#define XMRIG_DAEMONCLIENT_H
|
||||
|
||||
|
||||
#include "base/kernel/interfaces/IHttpListener.h"
|
||||
#include "base/kernel/interfaces/ITimerListener.h"
|
||||
#include "base/net/http/HttpListener.h"
|
||||
#include "base/net/stratum/BaseClient.h"
|
||||
#include "base/tools/Object.h"
|
||||
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
* Copyright 2016 Jay D Dee <jayddee246@gmail.com>
|
||||
* Copyright 2017-2018 XMR-Stak <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
|
||||
* Copyright 2018 Lee Clagett <https://github.com/vtnerd>
|
||||
* Copyright 2018 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright 2019 Howard Chu <https://github.com/hyc>
|
||||
* Copyright 2016-2019 XMRig <https://github.com/xmrig>, <support@xmrig.com>
|
||||
* Copyright 2018-2020 SChernykh <https://github.com/SChernykh>
|
||||
* Copyright 2016-2020 XMRig <https://github.com/xmrig>, <support@xmrig.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -82,7 +82,7 @@ public:
|
||||
inline uint32_t backend() const { return m_backend; }
|
||||
inline uint64_t diff() const { return m_diff; }
|
||||
inline uint64_t height() const { return m_height; }
|
||||
inline uint64_t nonceMask() const { return isNicehash() ? 0xFFFFFFULL : (nonceSize() == sizeof(uint64_t) ? (-1ull >> (extraNonce().size() * 4)): 0xFFFFFFFFULL); }
|
||||
inline uint64_t nonceMask() const { return isNicehash() ? 0xFFFFFFULL : (nonceSize() == sizeof(uint64_t) ? (-1ULL >> (extraNonce().size() * 4)): 0xFFFFFFFFULL); }
|
||||
inline uint64_t target() const { return m_target; }
|
||||
inline uint8_t *blob() { return m_blob; }
|
||||
inline uint8_t fixedByte() const { return *(m_blob + 42); }
|
||||
@@ -98,18 +98,18 @@ public:
|
||||
inline void setPoolWallet(const String &poolWallet) { m_poolWallet = poolWallet; }
|
||||
|
||||
# ifdef XMRIG_PROXY_PROJECT
|
||||
inline char *rawBlob() { return m_rawBlob; }
|
||||
inline const char *rawBlob() const { return m_rawBlob; }
|
||||
inline const char *rawTarget() const { return m_rawTarget; }
|
||||
inline const String &rawSeedHash() const { return m_rawSeedHash; }
|
||||
inline char *rawBlob() { return m_rawBlob; }
|
||||
inline const char *rawBlob() const { return m_rawBlob; }
|
||||
inline const char *rawTarget() const { return m_rawTarget; }
|
||||
inline const String &rawSeedHash() const { return m_rawSeedHash; }
|
||||
# endif
|
||||
|
||||
static inline uint64_t toDiff(uint64_t target) { return target ? (0xFFFFFFFFFFFFFFFFULL / target) : 0; }
|
||||
static inline uint64_t toDiff(uint64_t target) { return target ? (0xFFFFFFFFFFFFFFFFULL / target) : 0; }
|
||||
|
||||
inline bool operator!=(const Job &other) const { return !isEqual(other); }
|
||||
inline bool operator==(const Job &other) const { return isEqual(other); }
|
||||
inline Job &operator=(const Job &other) { copy(other); return *this; }
|
||||
inline Job &operator=(Job &&other) noexcept { move(std::move(other)); return *this; }
|
||||
inline bool operator!=(const Job &other) const { return !isEqual(other); }
|
||||
inline bool operator==(const Job &other) const { return isEqual(other); }
|
||||
inline Job &operator=(const Job &other) { copy(other); return *this; }
|
||||
inline Job &operator=(Job &&other) noexcept { move(std::move(other)); return *this; }
|
||||
|
||||
private:
|
||||
void copy(const Job &other);
|
||||
|
||||
@@ -50,6 +50,17 @@
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef XMRIG_FEATURE_BENCHMARK
|
||||
# include "base/net/stratum/benchmark/BenchClient.h"
|
||||
# include "base/net/stratum/benchmark/BenchConfig.h"
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# define strcasecmp _stricmp
|
||||
#endif
|
||||
|
||||
|
||||
namespace xmrig {
|
||||
|
||||
|
||||
@@ -72,9 +83,7 @@ const char *Pool::kSOCKS5 = "socks5";
|
||||
const char *Pool::kTls = "tls";
|
||||
const char *Pool::kUrl = "url";
|
||||
const char *Pool::kUser = "user";
|
||||
|
||||
|
||||
const char *Pool::kNicehashHost = "nicehash.com";
|
||||
const char *Pool::kNicehashHost = "nicehash.com";
|
||||
|
||||
|
||||
}
|
||||
@@ -125,23 +134,42 @@ xmrig::Pool::Pool(const rapidjson::Value &object) :
|
||||
m_flags.set(FLAG_NICEHASH, Json::getBool(object, kNicehash) || m_url.host().contains(kNicehashHost));
|
||||
m_flags.set(FLAG_TLS, Json::getBool(object, kTls) || m_url.isTLS());
|
||||
|
||||
setKeepAlive(Json::getValue(object, kKeepalive));
|
||||
|
||||
if (m_daemon.isValid()) {
|
||||
m_mode = MODE_SELF_SELECT;
|
||||
}
|
||||
else if (Json::getBool(object, kDaemon)) {
|
||||
m_mode = MODE_DAEMON;
|
||||
}
|
||||
|
||||
const rapidjson::Value &keepalive = Json::getValue(object, kKeepalive);
|
||||
if (keepalive.IsInt()) {
|
||||
setKeepAlive(keepalive.GetInt());
|
||||
}
|
||||
else if (keepalive.IsBool()) {
|
||||
setKeepAlive(keepalive.GetBool());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef XMRIG_FEATURE_BENCHMARK
|
||||
xmrig::Pool::Pool(const std::shared_ptr<BenchConfig> &benchmark) :
|
||||
m_mode(MODE_BENCHMARK),
|
||||
m_flags(1 << FLAG_ENABLED),
|
||||
m_url(BenchConfig::kBenchmark),
|
||||
m_benchmark(benchmark)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
xmrig::BenchConfig *xmrig::Pool::benchmark() const
|
||||
{
|
||||
assert(m_mode == MODE_BENCHMARK && m_benchmark);
|
||||
|
||||
return m_benchmark.get();
|
||||
}
|
||||
|
||||
|
||||
uint32_t xmrig::Pool::benchSize() const
|
||||
{
|
||||
return benchmark()->size();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
bool xmrig::Pool::isEnabled() const
|
||||
{
|
||||
# ifndef XMRIG_FEATURE_TLS
|
||||
@@ -217,6 +245,11 @@ xmrig::IClient *xmrig::Pool::createClient(int id, IClientListener *listener) con
|
||||
client = new AutoClient(id, Platform::userAgent(), listener);
|
||||
}
|
||||
# endif
|
||||
# ifdef XMRIG_FEATURE_BENCHMARK
|
||||
else if (m_mode == MODE_BENCHMARK) {
|
||||
client = new BenchClient(m_benchmark, listener);
|
||||
}
|
||||
# endif
|
||||
|
||||
assert(client != nullptr);
|
||||
|
||||
@@ -307,3 +340,14 @@ void xmrig::Pool::print() const
|
||||
LOG_DEBUG ("keepAlive: %d", m_keepAlive);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void xmrig::Pool::setKeepAlive(const rapidjson::Value &value)
|
||||
{
|
||||
if (value.IsInt()) {
|
||||
setKeepAlive(value.GetInt());
|
||||
}
|
||||
else if (value.IsBool()) {
|
||||
setKeepAlive(value.GetBool());
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user