From b8fe729b5210980f0318248504131cccb968adaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Sun, 1 Sep 2019 08:02:09 +0200 Subject: [PATCH] XMRigCC 2.0 (#263) # 2.0.0 **Thx to @xmrig and @SChernykh awesome work!** * Full Rebase on XMRig 3.1.1 * randomX/wow/XL * NUMA support * flexible multi algorithm configuration * unlimited switching between incompatible algorithms at runtime * Argon2, UPX2 (Nice hashrate improvement) and CN-Conceal support integrated like in previous version * 5-10% Hashrate improvement on ARMv8 CPUs when mining CN based algos compared to stock xmrig * Fully compatible to XMRigCCServer 1.9.5 no server upgrade needed! **New XMRigCCServer will be released soon with new features** --- .gitignore | 6 +- .travis.yml | 3 +- CHANGELOG.md | 11 + CMakeLists.txt | 349 +- Dockerfile | 11 +- README.md | 312 +- appveyor.yml | 2 +- cmake/FindHWLOC.cmake | 25 + cmake/FindMHD.cmake | 39 - cmake/FindUV.cmake | 19 +- cmake/OpenSSL.cmake | 31 + cmake/argon2.cmake | 18 + cmake/asm.cmake | 217 +- cmake/cn-gpu.cmake | 25 + cmake/cpu.cmake | 48 +- cmake/flags.cmake | 30 +- cmake/randomx.cmake | 57 + config.json | 44 - doc/ALGORITHMS.md | 53 + doc/API.md | 66 + doc/CPU.md | 96 + doc/api/1/config.json | 63 + doc/api/1/summary.json | 73 + doc/api/1/threads.json | 65 + doc/screenshot.png | Bin 0 -> 230847 bytes doc/screenshot_dashboard.png | Bin 0 -> 252242 bytes doc/screenshot_server.png | Bin 0 -> 294804 bytes doc/topology/AMD_FX_8320_windows_2_0_4.xml | 86 + ...AMD_Opteron_6272_x4_N8_linux_2_0_4_LXC.xml | 234 + .../AMD_Opteron_6278_x2_UMA_windows_2_0_4.xml | 294 + .../AMD_Opteron_6348_x4_N8_linux_1_11_2.xml | 550 + .../AMD_Opteron_6380_x4_N8_linux_1_11_5.xml | 670 + .../AMD_Ryzen_7_2700X_windows_2_0_4.xml | 105 + .../AMD_Ryzen_7_3700X_windows_2_0_4.xml | 104 + ...yzen_Threadripper_2950X_N2_linux_2_0_4.xml | 226 + ...en_Threadripper_2950X_UMA_linux_1_11_9.xml | 328 + .../Intel_Core_i7-3770_linux_2_0_4.xml | 87 + .../Intel_Core_i7-6700_linux_2_0_4.xml | 88 + .../Intel_Core_i7-6700_windows_2_0_4.xml | 61 + .../Intel_Core_i7-7660U_windows_2_0_4.xml | 41 + ...tel_Xeon_E5-4650_0_x4_N4_windows_2_0_4.xml | 477 + .../Intel_Xeon_E5620_x2_UMA_windows_2_0_4.xml | 111 + ...Intel_Xeon_E7-4870_x4_N4_windows_2_0_4.xml | 541 + ...ntel_Xeon_Gold_6146_x2_UMA_linux_2_0_4.xml | 246 + ...el_Xeon_Silver_4114_x2_N2_linux_1_11_9.xml | 403 + ...tel_Xeon_Silver_4114_x2_N2_linux_2_0_4.xml | 263 + res/app.rc | 51 +- .../IWorker.h => 3rdparty/argon2.h} | 25 +- src/3rdparty/argon2/.gitattributes | 2 - src/3rdparty/argon2/.gitignore | 70 - src/3rdparty/argon2/.travis.yml | 23 - src/3rdparty/argon2/CMakeLists.txt | 240 +- .../argon2/arch/x86_64/lib/argon2-arch.c | 3 + .../argon2/arch/x86_64/lib/argon2-avx2.c | 10 +- .../argon2/arch/x86_64/lib/argon2-avx512f.c | 10 +- .../argon2/arch/x86_64/lib/argon2-sse2.c | 10 +- .../argon2/arch/x86_64/lib/argon2-ssse3.c | 10 +- .../arch/x86_64/lib/argon2-template-128.h | 6 +- .../argon2/arch/x86_64/lib/argon2-xop.c | 10 +- .../argon2/arch/x86_64/lib/cpu-flags.c | 129 + .../argon2/arch/x86_64/lib/cpu-flags.h | 12 + .../arch/x86_64/src/test-feature-avx2.c | 8 + .../arch/x86_64/src/test-feature-avx512f.c | 8 + .../arch/x86_64/src/test-feature-sse2.c | 8 + .../arch/x86_64/src/test-feature-ssse3.c | 8 + .../argon2/arch/x86_64/src/test-feature-xop.c | 8 + src/3rdparty/argon2/configure.ac | 108 - src/3rdparty/argon2/include/argon2.h | 60 +- src/3rdparty/argon2/lib/argon2.c | 74 +- src/3rdparty/argon2/lib/core.c | 100 +- src/3rdparty/argon2/lib/impl-select.c | 57 +- src/3rdparty/argon2/lib/thread.c | 36 - src/3rdparty/argon2/lib/thread.h | 47 - .../argon2/m4/ax_check_compile_flag.m4 | 74 - src/3rdparty/argon2/m4/ax_pthread.m4 | 485 - src/3rdparty/argon2/qmake/arch/arch.pro | 3 - .../argon2/qmake/arch/generic/generic.pro | 1 - .../x86_64/libargon2-avx2/libargon2-avx2.pro | 23 - .../libargon2-avx512f/libargon2-avx512f.pro | 23 - .../x86_64/libargon2-sse2/libargon2-sse2.pro | 24 - .../libargon2-ssse3/libargon2-ssse3.pro | 24 - .../x86_64/libargon2-xop/libargon2-xop.pro | 24 - .../argon2/qmake/arch/x86_64/x86_64.pro | 8 - .../qmake/argon2-bench2/argon2-bench2.pro | 19 - .../qmake/argon2-genkat/argon2-genkat.pro | 16 - .../argon2/qmake/argon2-test/argon2-test.pro | 16 - src/3rdparty/argon2/qmake/argon2.pro | 9 - src/3rdparty/argon2/qmake/argon2/argon2.pro | 18 - .../argon2/qmake/libargon2/libargon2.pro | 119 - .../metacentrum/start-all-benchmarks.sh | 12 - .../scripts/metacentrum/start-benchmark.sh | 75 - src/3rdparty/argon2/scripts/run-benchmark.sh | 40 - src/3rdparty/argon2/src/bench2.c | 179 - src/3rdparty/argon2/src/genkat.c | 90 - src/3rdparty/argon2/src/run.c | 315 - src/3rdparty/argon2/src/timing.h | 41 - src/3rdparty/argon2/tests/test.c | 239 - src/3rdparty/cpp-httplib/example/Makefile | 28 - src/3rdparty/cpp-httplib/example/benchmark.cc | 33 - src/3rdparty/cpp-httplib/example/client.cc | 31 - .../cpp-httplib/example/client.vcxproj | 88 - src/3rdparty/cpp-httplib/example/example.sln | 31 - src/3rdparty/cpp-httplib/example/hello.cc | 22 - .../cpp-httplib/example/server.vcxproj | 88 - src/3rdparty/cpp-httplib/example/simplesvr.cc | 105 - src/3rdparty/cpp-httplib/test/Makefile | 17 - .../cpp-httplib/test/gtest/gtest-all.cc | 9118 -------- src/3rdparty/cpp-httplib/test/gtest/gtest.h | 19537 ---------------- .../cpp-httplib/test/gtest/gtest_main.cc | 39 - src/3rdparty/cpp-httplib/test/test.cc | 378 - src/3rdparty/cpp-httplib/test/test.sln | 28 - src/3rdparty/cpp-httplib/test/test.vcxproj | 157 - .../test/test.xcodeproj/project.pbxproj | 248 - .../contents.xcworkspacedata | 7 - .../cpp-httplib/test/www/dir/index.html | 8 - .../cpp-httplib/test/www/dir/test.html | 1 - src/3rdparty/http-parser/AUTHORS | 68 + src/3rdparty/http-parser/LICENSE-MIT | 19 + src/3rdparty/http-parser/README.md | 246 + src/3rdparty/http-parser/http_parser.c | 2501 ++ src/3rdparty/http-parser/http_parser.h | 439 + src/3rdparty/hwloc/AUTHORS | 44 + src/3rdparty/hwloc/CMakeLists.txt | 38 + src/3rdparty/hwloc/COPYING | 39 + src/3rdparty/hwloc/NEWS | 1599 ++ src/3rdparty/hwloc/README | 85 + src/3rdparty/hwloc/VERSION | 47 + src/3rdparty/hwloc/include/hwloc.h | 2270 ++ .../hwloc/include/hwloc/autogen/config.h | 59 + src/3rdparty/hwloc/include/hwloc/bitmap.h | 467 + src/3rdparty/hwloc/include/hwloc/cuda.h | 220 + src/3rdparty/hwloc/include/hwloc/cudart.h | 177 + src/3rdparty/hwloc/include/hwloc/deprecated.h | 206 + src/3rdparty/hwloc/include/hwloc/diff.h | 289 + src/3rdparty/hwloc/include/hwloc/distances.h | 271 + src/3rdparty/hwloc/include/hwloc/export.h | 278 + src/3rdparty/hwloc/include/hwloc/gl.h | 135 + .../hwloc/include/hwloc/glibc-sched.h | 125 + src/3rdparty/hwloc/include/hwloc/helper.h | 1160 + src/3rdparty/hwloc/include/hwloc/inlines.h | 146 + src/3rdparty/hwloc/include/hwloc/intel-mic.h | 134 + .../hwloc/include/hwloc/linux-libnuma.h | 273 + src/3rdparty/hwloc/include/hwloc/linux.h | 79 + src/3rdparty/hwloc/include/hwloc/nvml.h | 181 + src/3rdparty/hwloc/include/hwloc/opencl.h | 206 + .../hwloc/include/hwloc/openfabrics-verbs.h | 150 + src/3rdparty/hwloc/include/hwloc/plugins.h | 542 + src/3rdparty/hwloc/include/hwloc/rename.h | 765 + src/3rdparty/hwloc/include/hwloc/shmem.h | 137 + .../hwloc/include/private/autogen/config.h | 672 + .../hwloc/include/private/components.h | 43 + .../hwloc/include/private/cpuid-x86.h | 86 + src/3rdparty/hwloc/include/private/debug.h | 83 + .../include/private/internal-components.h | 41 + src/3rdparty/hwloc/include/private/misc.h | 583 + src/3rdparty/hwloc/include/private/netloc.h | 578 + src/3rdparty/hwloc/include/private/private.h | 417 + .../hwloc/include/private/solaris-chiptype.h | 43 + src/3rdparty/hwloc/include/private/xml.h | 108 + src/3rdparty/hwloc/src/base64.c | 309 + src/3rdparty/hwloc/src/bind.c | 922 + src/3rdparty/hwloc/src/bitmap.c | 1676 ++ src/3rdparty/hwloc/src/components.c | 785 + src/3rdparty/hwloc/src/diff.c | 492 + src/3rdparty/hwloc/src/distances.c | 920 + src/3rdparty/hwloc/src/misc.c | 166 + src/3rdparty/hwloc/src/pci-common.c | 941 + src/3rdparty/hwloc/src/shmem.c | 287 + src/3rdparty/hwloc/src/static-components.h | 15 + src/3rdparty/hwloc/src/topology-noos.c | 65 + src/3rdparty/hwloc/src/topology-synthetic.c | 1521 ++ src/3rdparty/hwloc/src/topology-windows.c | 1189 + src/3rdparty/hwloc/src/topology-x86.c | 1583 ++ .../hwloc/src/topology-xml-nolibxml.c | 919 + src/3rdparty/hwloc/src/topology-xml.c | 2886 +++ src/3rdparty/hwloc/src/topology.c | 4484 ++++ src/3rdparty/hwloc/src/traversal.c | 616 + src/3rdparty/libcpuid/asm-bits.c | 1661 +- src/3rdparty/libcpuid/asm-bits.h | 124 +- src/3rdparty/libcpuid/cpuid_main.c | 56 +- src/3rdparty/libcpuid/libcpuid.h | 39 +- src/3rdparty/libcpuid/libcpuid_internal.h | 5 +- src/3rdparty/libcpuid/libcpuid_types.h | 28 +- src/3rdparty/libcpuid/recog_amd.c | 4 + src/3rdparty/libcpuid/recog_intel.c | 3 +- src/3rdparty/rapidjson/allocators.h | 17 +- src/3rdparty/rapidjson/cursorstreamwrapper.h | 78 + src/3rdparty/rapidjson/document.h | 265 +- src/3rdparty/rapidjson/encodedstream.h | 2 +- src/3rdparty/rapidjson/encodings.h | 86 +- src/3rdparty/rapidjson/error/error.h | 10 +- src/3rdparty/rapidjson/filereadstream.h | 4 +- src/3rdparty/rapidjson/filewritestream.h | 4 +- src/3rdparty/rapidjson/internal/biginteger.h | 4 +- src/3rdparty/rapidjson/internal/diyfp.h | 37 +- src/3rdparty/rapidjson/internal/dtoa.h | 8 +- src/3rdparty/rapidjson/internal/ieee754.h | 4 +- src/3rdparty/rapidjson/internal/itoa.h | 82 +- src/3rdparty/rapidjson/internal/meta.h | 9 +- src/3rdparty/rapidjson/internal/regex.h | 319 +- src/3rdparty/rapidjson/internal/stack.h | 10 +- src/3rdparty/rapidjson/internal/strfunc.h | 14 + src/3rdparty/rapidjson/internal/strtod.h | 111 +- src/3rdparty/rapidjson/istreamwrapper.h | 87 +- src/3rdparty/rapidjson/license.txt | 57 + src/3rdparty/rapidjson/pointer.h | 92 +- src/3rdparty/rapidjson/prettywriter.h | 72 +- src/3rdparty/rapidjson/rapidjson.h | 110 +- src/3rdparty/rapidjson/reader.h | 601 +- src/3rdparty/rapidjson/schema.h | 717 +- src/3rdparty/rapidjson/stream.h | 52 +- src/3rdparty/rapidjson/stringbuffer.h | 4 + src/3rdparty/rapidjson/writer.h | 160 +- src/App.cpp | 289 +- src/App.h | 73 +- src/App_unix.cpp | 55 +- src/App_win.cpp | 13 +- src/AsmOptimization.h | 97 - src/Cpu.cpp | 249 - src/Cpu.h | 62 - src/CpuImpl.h | 70 - src/Cpu_cpuid.cpp | 102 - src/Cpu_stub.cpp | 118 - src/Cpu_unix.cpp | 83 - src/Embedded_config.h | 68 - src/Mem.cpp | 110 - src/Mem.h | 113 - src/Mem_unix.cpp | 105 - src/Options.cpp | 1434 -- src/Options.h | 225 - src/Platform_mac.cpp | 109 - src/PowVariant.h | 232 - src/Summary.cpp | 252 +- src/Summary.h | 24 +- src/api/ApiState.cpp | 260 - src/api/Httpd.cpp | 116 - src/backend/backend.cmake | 13 + src/{workers => backend/common}/Hashrate.cpp | 124 +- src/{workers => backend/common}/Hashrate.h | 39 +- src/backend/common/Thread.h | 67 + src/backend/common/Threads.cpp | 155 + src/backend/common/Threads.h | 67 + src/{workers => backend/common}/Worker.cpp | 48 +- src/backend/common/Worker.h | 65 + src/backend/common/WorkerJob.h | 143 + src/backend/common/Workers.cpp | 193 + src/backend/common/Workers.h | 73 + src/backend/common/common.cmake | 18 + src/backend/common/interfaces/IBackend.h | 71 + src/backend/common/interfaces/IThread.h | 77 + src/backend/common/interfaces/IWorker.h | 56 + src/backend/cpu/Cpu.cpp | 104 + src/{Platform.h => backend/cpu/Cpu.h} | 35 +- src/backend/cpu/CpuBackend.cpp | 384 + src/backend/cpu/CpuBackend.h | 77 + src/backend/cpu/CpuConfig.cpp | 223 + src/backend/cpu/CpuConfig.h | 83 + src/backend/cpu/CpuLaunchData.cpp | 67 + src/backend/cpu/CpuLaunchData.h | 71 + .../cpu/CpuThread.cpp} | 61 +- src/backend/cpu/CpuThread.h | 62 + src/backend/cpu/CpuThreads.cpp | 146 + src/backend/cpu/CpuThreads.h | 71 + src/backend/cpu/CpuWorker.cpp | 334 + src/backend/cpu/CpuWorker.h | 98 + src/backend/cpu/cpu.cmake | 79 + src/backend/cpu/interfaces/ICpuInfo.h | 66 + src/backend/cpu/platform/AdvancedCpuInfo.cpp | 163 + src/backend/cpu/platform/AdvancedCpuInfo.h | 73 + src/backend/cpu/platform/BasicCpuInfo.cpp | 237 + src/backend/cpu/platform/BasicCpuInfo.h | 69 + .../cpu/platform/BasicCpuInfo_arm.cpp} | 78 +- src/backend/cpu/platform/HwlocCpuInfo.cpp | 343 + src/backend/cpu/platform/HwlocCpuInfo.h | 81 + src/base/api/Api.cpp | 208 + src/base/api/Api.h | 81 + src/base/api/Httpd.cpp | 193 + src/base/api/Httpd.h | 70 + .../api/interfaces/IApiListener.h} | 26 +- src/base/api/interfaces/IApiRequest.h | 93 + .../api/requests/ApiRequest.cpp} | 26 +- src/base/api/requests/ApiRequest.h | 75 + src/base/api/requests/HttpApiRequest.cpp | 178 + src/base/api/requests/HttpApiRequest.h | 70 + src/base/base.cmake | 162 + .../cc/interfaces/IClientStatusListener.h} | 30 +- .../cc/interfaces/ICommandListener.h} | 29 +- src/{ => base/io}/Console.cpp | 48 +- src/{ => base/io}/Console.h | 22 +- src/base/io/Watcher.cpp | 88 + src/base/io/Watcher.h | 71 + src/base/io/json/Json.cpp | 139 + src/base/io/json/Json.h | 79 + src/base/io/json/JsonChain.cpp | 213 + src/base/io/json/JsonChain.h | 76 + .../io/json/JsonRequest.cpp} | 27 +- src/base/io/json/JsonRequest.h | 45 + src/base/io/json/Json_unix.cpp | 64 + src/base/io/json/Json_win.cpp | 126 + src/base/io/log/Log.cpp | 248 + src/base/io/log/Log.h | 144 + src/base/io/log/backends/ConsoleLog.cpp | 97 + src/base/io/log/backends/ConsoleLog.h | 60 + src/{log => base/io/log/backends}/FileLog.cpp | 66 +- src/base/io/log/backends/FileLog.h | 58 + src/base/io/log/backends/RemoteLog.cpp | 82 + src/{log => base/io/log/backends}/RemoteLog.h | 29 +- .../io/log/backends/SysLog.cpp} | 32 +- src/base/io/log/backends/SysLog.h | 50 + src/base/kernel/Base.cpp | 381 + src/base/kernel/Base.h | 77 + src/base/kernel/Entry.cpp | 169 + src/base/kernel/Entry.h | 53 + src/{ => base/kernel}/Platform.cpp | 53 +- src/{net/JobId.h => base/kernel/Platform.h} | 70 +- .../kernel/Platform_mac.cpp} | 56 +- src/base/kernel/Platform_unix.cpp | 162 + src/{ => base/kernel}/Platform_win.cpp | 58 +- src/base/kernel/Process.cpp | 102 + src/base/kernel/Process.h | 64 + src/base/kernel/Signals.cpp | 74 + src/base/kernel/Signals.h | 64 + src/base/kernel/config/BaseConfig.cpp | 170 + src/base/kernel/config/BaseConfig.h | 110 + src/base/kernel/config/BaseTransform.cpp | 319 + src/base/kernel/config/BaseTransform.h | 127 + src/base/kernel/interfaces/IBaseListener.h | 47 + src/base/kernel/interfaces/IClient.h | 85 + src/base/kernel/interfaces/IClientListener.h | 61 + src/base/kernel/interfaces/IConfig.h | 160 + src/base/kernel/interfaces/IConfigListener.h | 47 + src/base/kernel/interfaces/IConfigTransform.h | 53 + .../kernel}/interfaces/IConsoleListener.h | 19 +- .../kernel/interfaces/IDnsListener.h} | 34 +- src/base/kernel/interfaces/IHttpListener.h | 48 + src/base/kernel/interfaces/IJsonReader.h | 56 + .../kernel/interfaces/ILineListener.h} | 30 +- src/base/kernel/interfaces/ILogBackend.h | 49 + .../kernel/interfaces/ISignalListener.h} | 34 +- src/base/kernel/interfaces/IStrategy.h | 59 + .../kernel/interfaces/IStrategyListener.h | 59 + .../kernel/interfaces/ITcpServerListener.h} | 35 +- .../kernel/interfaces/ITimerListener.h} | 35 +- src/base/kernel/interfaces/IWatcherListener.h | 47 + src/base/net/dns/Dns.cpp | 163 + src/base/net/dns/Dns.h | 81 + src/base/net/dns/DnsRecord.cpp | 66 + src/base/net/dns/DnsRecord.h | 66 + src/base/net/http/Http.cpp | 99 + src/base/net/http/Http.h | 73 + src/base/net/http/HttpApiResponse.cpp | 88 + src/base/net/http/HttpApiResponse.h | 57 + src/base/net/http/HttpClient.cpp | 224 + src/base/net/http/HttpClient.h | 74 + src/base/net/http/HttpContext.cpp | 228 + src/base/net/http/HttpContext.h | 86 + src/base/net/http/HttpData.h | 60 + src/base/net/http/HttpResponse.cpp | 153 + src/base/net/http/HttpResponse.h | 61 + src/base/net/http/HttpServer.cpp | 83 + src/base/net/http/HttpServer.h | 62 + src/base/net/http/HttpsClient.cpp | 208 + src/base/net/http/HttpsClient.h | 77 + src/base/net/stratum/BaseClient.cpp | 63 + src/base/net/stratum/BaseClient.h | 97 + src/base/net/stratum/Client.cpp | 925 + src/base/net/stratum/Client.h | 151 + src/base/net/stratum/DaemonClient.cpp | 379 + src/base/net/stratum/DaemonClient.h | 83 + src/base/net/stratum/Job.cpp | 186 + src/base/net/stratum/Job.h | 121 + src/base/net/stratum/Pool.cpp | 339 + src/base/net/stratum/Pool.h | 126 + src/base/net/stratum/Pools.cpp | 190 + src/base/net/stratum/Pools.h | 87 + src/base/net/stratum/SubmitResult.h | 72 + src/base/net/stratum/Tls.cpp | 192 + .../ApiState.h => base/net/stratum/Tls.h} | 62 +- .../stratum/strategies/FailoverStrategy.cpp | 210 + .../net/stratum/strategies/FailoverStrategy.h | 86 + .../stratum/strategies/SinglePoolStrategy.cpp | 144 + .../stratum/strategies/SinglePoolStrategy.h | 75 + src/base/net/tools/RecvBuf.h | 99 + src/base/net/tools/Storage.h | 97 + src/base/net/tools/TcpServer.cpp | 102 + src/base/net/tools/TcpServer.h | 64 + src/base/tools/Arguments.cpp | 76 + src/base/tools/Arguments.h | 61 + src/{log/SysLog.h => base/tools/Baton.h} | 25 +- src/base/tools/Buffer.cpp | 201 + src/base/tools/Buffer.h | 91 + src/base/tools/Chrono.h | 68 + src/base/tools/Handle.h | 92 + src/base/tools/String.cpp | 239 + src/base/tools/String.h | 107 + src/base/tools/Timer.cpp | 91 + src/{workers/Handle.h => base/tools/Timer.h} | 53 +- src/cc/CCClient.cpp | 336 +- src/cc/CCClient.h | 69 +- src/cc/CCClientConfig.cpp | 122 + src/cc/CCClientConfig.h | 73 + src/cc/CCServer.cpp | 9 +- src/cc/CCServer.h | 9 +- src/cc/ClientStatus.cpp | 39 +- src/cc/ClientStatus.h | 15 +- src/cc/ControlCommand.cpp | 4 +- src/cc/ControlCommand.h | 2 +- src/cc/GPUInfo.cpp | 2 +- src/cc/GPUInfo.h | 3 +- src/cc/Httpd.cpp | 11 +- src/cc/Httpd.h | 11 +- src/cc/Service.cpp | 11 +- src/cc/Service.h | 11 +- src/cc/Summary.cpp | 12 +- src/cc/XMRigCC.cpp | 9 +- src/cc/XMRigd.cpp | 9 +- src/config.json | 96 +- src/core/Controller.cpp | 104 + .../IStrategyListener.h => core/Controller.h} | 44 +- src/core/Miner.cpp | 548 + src/core/Miner.h | 84 + src/core/config/Config.cpp | 137 + src/core/config/Config.h | 74 + src/core/config/ConfigTransform.cpp | 190 + src/core/config/ConfigTransform.h | 57 + src/core/config/Config_default.h | 106 + src/core/config/Config_platform.h | 104 + src/core/config/usage.h | 149 + src/crypto/Argon2_test.h | 49 - src/crypto/CryptoNight.h | 80 - src/crypto/CryptoNight_arm.h | 5534 ----- src/crypto/CryptoNight_test.h | 300 - src/crypto/CryptoNight_x86.h | 6198 ----- src/crypto/HashSelector.cpp | 965 - src/{Cpu_mac.cpp => crypto/argon2/Hash.h} | 55 +- .../argon2/Impl.cpp} | 42 +- .../ILogBackend.h => crypto/argon2/Impl.h} | 36 +- src/crypto/asm/cn_main_loop.S | 534 - .../asm/cnv1_main_loop_rto_sandybridge.inc | 75 - ...nv1_main_loop_rto_soft_aes_sandybridge.inc | 167 - .../asm/cnv1_main_loop_sandybridge.inc.in | 74 - ...cnv1_main_loop_soft_aes_sandybridge.inc.in | 166 - ...cnv2_main_loop_soft_aes_sandybridge.inc.in | 271 - src/crypto/asm/win/CryptonightR_template.S | 1595 -- src/crypto/asm/win/cn_main_loop.asm | 287 - src/crypto/asm/win/cn_main_loop_win_gcc.S | 248 - .../win/cnv1_main_loop_rto_sandybridge.inc | 71 - ...nv1_main_loop_rto_soft_aes_sandybridge.inc | 163 - .../asm/win/cnv1_main_loop_sandybridge.inc.in | 70 - ...cnv1_main_loop_soft_aes_sandybridge.inc.in | 162 - ...cnv2_main_loop_soft_aes_sandybridge.inc.in | 267 - src/crypto/c_keccak.h | 26 - src/crypto/cn/CnAlgo.h | 258 + src/crypto/cn/CnCtx.cpp | 60 + src/crypto/cn/CnCtx.h | 52 + src/crypto/cn/CnHash.cpp | 312 + src/crypto/cn/CnHash.h | 78 + src/crypto/cn/CryptoNight.h | 64 + src/crypto/cn/CryptoNight_arm.h | 980 + src/crypto/cn/CryptoNight_monero.h | 207 + src/crypto/cn/CryptoNight_test.h | 433 + src/crypto/cn/CryptoNight_x86.h | 1663 ++ src/crypto/{ => cn}/SSE2NEON.h | 6 - .../asm/CryptonightR_soft_aes_template.inc | 2 + .../CryptonightR_soft_aes_template_win.inc} | 48 +- .../{ => cn}/asm/CryptonightR_template.S | 4 + .../win => cn/asm}/CryptonightR_template.asm | 0 .../{ => cn}/asm/CryptonightR_template.h | 0 .../{ => cn}/asm/CryptonightR_template.inc | 41 +- .../asm/CryptonightR_template_win.inc} | 191 +- .../asm/CryptonightWOW_soft_aes_template.inc | 2 + .../CryptonightWOW_soft_aes_template_win.inc} | 52 +- .../{ => cn}/asm/CryptonightWOW_template.inc | 5 + .../asm/CryptonightWOW_template_win.inc} | 171 +- .../cnv2_double_main_loop_sandybridge.inc} | 59 +- .../asm/cn2/cnv2_main_loop_bulldozer.inc} | 28 +- .../asm/cn2/cnv2_main_loop_ivybridge.inc} | 32 +- .../asm/cn2/cnv2_main_loop_ryzen.inc} | 28 +- .../asm/cn2/cnv2_rwz_double_main_loop.inc} | 61 +- .../asm/cn2/cnv2_rwz_main_loop.inc} | 32 +- src/crypto/cn/asm/cn_main_loop.S | 77 + src/crypto/cn/asm/cn_main_loop.asm | 52 + .../CryptonightR_soft_aes_template_win.inc | 2 + .../cn/asm/win64/CryptonightR_template.asm | 1585 ++ .../asm/win64}/CryptonightR_template_win.inc | 41 +- .../CryptonightWOW_soft_aes_template_win.inc | 2 + .../win64}/CryptonightWOW_template_win.inc | 5 + .../cnv2_double_main_loop_sandybridge.inc} | 143 +- .../win64/cn2/cnv2_main_loop_bulldozer.inc} | 28 +- .../win64/cn2/cnv2_main_loop_ivybridge.inc} | 68 +- .../asm/win64/cn2/cnv2_main_loop_ryzen.inc} | 64 +- .../win64/cn2/cnv2_rwz_double_main_loop.inc} | 57 +- .../asm/win64/cn2/cnv2_rwz_main_loop.inc} | 26 +- src/crypto/cn/asm/win64/cn_main_loop.S | 45 + src/crypto/cn/asm/win64/cn_main_loop.asm | 52 + src/crypto/{ => cn}/c_blake256.c | 0 src/crypto/{ => cn}/c_blake256.h | 0 src/crypto/{ => cn}/c_groestl.c | 0 src/crypto/{ => cn}/c_groestl.h | 0 src/crypto/{ => cn}/c_jh.c | 0 src/crypto/{ => cn}/c_jh.h | 0 src/crypto/{ => cn}/c_skein.c | 0 src/crypto/{ => cn}/c_skein.h | 0 src/crypto/cn/gpu/cn_gpu_arm.cpp | 240 + src/crypto/cn/gpu/cn_gpu_avx.cpp | 211 + src/crypto/cn/gpu/cn_gpu_ssse3.cpp | 212 + src/crypto/{ => cn}/groestl_tables.h | 0 src/crypto/{ => cn}/hash.h | 0 .../r/CryptonightR_gen.cpp} | 52 +- src/crypto/cn/r/variant4_random_math.h | 453 + src/crypto/{ => cn}/skein_port.h | 0 src/crypto/{ => cn}/soft_aes.h | 20 +- src/crypto/common/Algorithm.cpp | 332 + src/crypto/common/Algorithm.h | 136 + src/crypto/common/Assembly.cpp | 107 + src/crypto/common/Assembly.h | 75 + src/crypto/common/Nonce.cpp | 100 + src/crypto/common/Nonce.h | 70 + src/crypto/common/VirtualMemory.cpp | 85 + src/crypto/common/VirtualMemory.h | 86 + src/crypto/common/VirtualMemory_unix.cpp | 143 + .../common/VirtualMemory_win.cpp} | 137 +- src/crypto/{c_keccak.c => common/keccak.cpp} | 52 +- src/crypto/common/keccak.h | 55 + .../common/portable/mm_malloc.h} | 50 +- src/crypto/randomx/aes_hash.cpp | 214 + src/crypto/randomx/aes_hash.hpp | 40 + src/crypto/randomx/allocator.cpp | 60 + src/crypto/randomx/allocator.hpp | 46 + src/crypto/randomx/argon2.h | 229 + src/crypto/randomx/argon2_core.c | 502 + src/crypto/randomx/argon2_core.h | 254 + src/crypto/randomx/argon2_ref.c | 214 + .../randomx/asm/program_epilogue_linux.inc | 10 + .../randomx/asm/program_epilogue_store.inc | 19 + .../randomx/asm/program_epilogue_win64.inc | 24 + src/crypto/randomx/asm/program_loop_load.inc | 32 + src/crypto/randomx/asm/program_loop_store.inc | 19 + .../randomx/asm/program_prologue_linux.inc | 34 + .../randomx/asm/program_prologue_win64.inc | 47 + .../randomx/asm/program_read_dataset.inc | 17 + .../asm/program_read_dataset_sshash_fin.inc | 10 + .../asm/program_read_dataset_sshash_init.inc | 17 + .../randomx/asm/program_sshash_constants.inc | 24 + .../randomx/asm/program_sshash_load.inc | 8 + .../randomx/asm/program_sshash_prefetch.inc | 4 + .../randomx/asm/program_xmm_constants.inc | 6 + src/crypto/randomx/asm/randomx_reciprocal.inc | 7 + src/crypto/randomx/blake2/blake2-impl.h | 76 + src/crypto/randomx/blake2/blake2.h | 105 + src/crypto/randomx/blake2/blake2b.c | 409 + src/crypto/randomx/blake2/blamka-round-ref.h | 73 + src/crypto/randomx/blake2/endian.h | 107 + src/crypto/randomx/blake2_generator.cpp | 62 + src/crypto/randomx/blake2_generator.hpp | 46 + src/crypto/randomx/bytecode_machine.cpp | 482 + src/crypto/randomx/bytecode_machine.hpp | 288 + src/crypto/randomx/common.hpp | 173 + src/crypto/randomx/configuration.h | 47 + src/crypto/randomx/dataset.cpp | 189 + src/crypto/randomx/dataset.hpp | 80 + src/crypto/randomx/instruction.hpp | 102 + src/crypto/randomx/instructions_portable.cpp | 193 + src/crypto/randomx/intrin_portable.h | 605 + src/crypto/randomx/jit_compiler.hpp | 37 + src/crypto/randomx/jit_compiler_a64.hpp | 73 + src/crypto/randomx/jit_compiler_fallback.hpp | 73 + src/crypto/randomx/jit_compiler_x86.cpp | 930 + src/crypto/randomx/jit_compiler_x86.hpp | 140 + src/crypto/randomx/jit_compiler_x86_static.S | 212 + .../randomx/jit_compiler_x86_static.asm | 199 + .../randomx/jit_compiler_x86_static.hpp | 48 + src/crypto/randomx/program.hpp | 60 + src/crypto/randomx/randomx.cpp | 449 + src/crypto/randomx/randomx.h | 332 + src/crypto/randomx/reciprocal.c | 80 + src/crypto/randomx/reciprocal.h | 48 + src/crypto/randomx/soft_aes.cpp | 364 + src/crypto/randomx/soft_aes.h | 46 + src/crypto/randomx/superscalar.cpp | 891 + src/crypto/randomx/superscalar.hpp | 60 + src/crypto/randomx/superscalar_program.hpp | 73 + src/crypto/randomx/virtual_machine.cpp | 129 + src/crypto/randomx/virtual_machine.hpp | 90 + src/crypto/randomx/virtual_memory.cpp | 58 + src/crypto/randomx/virtual_memory.hpp | 35 + src/crypto/randomx/vm_compiled.cpp | 58 + src/crypto/randomx/vm_compiled.hpp | 74 + src/crypto/randomx/vm_compiled_light.cpp | 52 + src/crypto/randomx/vm_compiled_light.hpp | 65 + src/crypto/randomx/vm_interpreted.cpp | 123 + src/crypto/randomx/vm_interpreted.hpp | 78 + src/crypto/randomx/vm_interpreted_light.cpp | 53 + src/crypto/randomx/vm_interpreted_light.hpp | 63 + src/crypto/rx/Rx.cpp | 323 + .../IClientListener.h => crypto/rx/Rx.h} | 39 +- src/crypto/rx/RxAlgo.cpp | 49 + src/crypto/{HashSelector.h => rx/RxAlgo.h} | 37 +- src/crypto/rx/RxCache.cpp | 83 + src/crypto/rx/RxCache.h | 73 + src/crypto/rx/RxConfig.cpp | 63 + src/{api/Httpd.h => crypto/rx/RxConfig.h} | 40 +- src/crypto/rx/RxDataset.cpp | 114 + src/crypto/rx/RxDataset.h | 71 + src/crypto/rx/RxVm.cpp | 59 + src/{workers/Worker.h => crypto/rx/RxVm.h} | 48 +- src/crypto/variant4_random_math.h | 447 - src/default_miner_config.json | 43 - src/donate.h | 25 +- src/log/ConsoleLog.cpp | 156 - src/log/Log.cpp | 89 - src/log/Log.h | 108 - src/log/RemoteLog.cpp | 103 - src/net/BoostConnection.h | 164 - src/net/BoostTcpConnection.cpp | 72 - src/net/BoostTlsConnection.cpp | 83 - src/net/Client.cpp | 699 - src/net/Client.h | 136 - src/net/Connection.cpp | 101 - src/net/Connection.h | 81 - src/net/Job.cpp | 218 - src/net/Job.h | 85 - src/net/JobResult.h | 77 +- src/net/JobResults.cpp | 134 + src/{log/ConsoleLog.h => net/JobResults.h} | 41 +- src/net/Network.cpp | 318 +- src/net/Network.h | 85 +- src/{api => net}/NetworkState.cpp | 59 +- src/{api => net}/NetworkState.h | 37 +- src/net/Url.cpp | 198 - src/net/Url.h | 73 - src/net/interfaces/IJobResultListener.h | 48 + src/net/strategies/DonateStrategy.cpp | 419 +- src/net/strategies/DonateStrategy.h | 99 +- src/net/strategies/FailoverStrategy.cpp | 148 - src/net/strategies/FailoverStrategy.h | 69 - src/net/strategies/SinglePoolStrategy.cpp | 102 - src/net/strategies/SinglePoolStrategy.h | 63 - src/version.h | 59 +- src/win_dirent.h | 1224 - src/workers/MultiWorker.cpp | 209 - src/workers/MultiWorker.h | 39 - src/workers/Workers.cpp | 198 - src/workers/Workers.h | 82 - src/xmrig.cpp | 23 +- 645 files changed, 85475 insertions(+), 63443 deletions(-) create mode 100644 cmake/FindHWLOC.cmake delete mode 100644 cmake/FindMHD.cmake create mode 100644 cmake/OpenSSL.cmake create mode 100644 cmake/argon2.cmake create mode 100644 cmake/cn-gpu.cmake create mode 100644 cmake/randomx.cmake delete mode 100644 config.json create mode 100644 doc/ALGORITHMS.md create mode 100644 doc/API.md create mode 100644 doc/CPU.md create mode 100644 doc/api/1/config.json create mode 100644 doc/api/1/summary.json create mode 100644 doc/api/1/threads.json create mode 100644 doc/screenshot.png create mode 100644 doc/screenshot_dashboard.png create mode 100644 doc/screenshot_server.png create mode 100644 doc/topology/AMD_FX_8320_windows_2_0_4.xml create mode 100644 doc/topology/AMD_Opteron_6272_x4_N8_linux_2_0_4_LXC.xml create mode 100644 doc/topology/AMD_Opteron_6278_x2_UMA_windows_2_0_4.xml create mode 100644 doc/topology/AMD_Opteron_6348_x4_N8_linux_1_11_2.xml create mode 100644 doc/topology/AMD_Opteron_6380_x4_N8_linux_1_11_5.xml create mode 100644 doc/topology/AMD_Ryzen_7_2700X_windows_2_0_4.xml create mode 100644 doc/topology/AMD_Ryzen_7_3700X_windows_2_0_4.xml create mode 100644 doc/topology/AMD_Ryzen_Threadripper_2950X_N2_linux_2_0_4.xml create mode 100644 doc/topology/AMD_Ryzen_Threadripper_2950X_UMA_linux_1_11_9.xml create mode 100644 doc/topology/Intel_Core_i7-3770_linux_2_0_4.xml create mode 100644 doc/topology/Intel_Core_i7-6700_linux_2_0_4.xml create mode 100644 doc/topology/Intel_Core_i7-6700_windows_2_0_4.xml create mode 100644 doc/topology/Intel_Core_i7-7660U_windows_2_0_4.xml create mode 100644 doc/topology/Intel_Xeon_E5-4650_0_x4_N4_windows_2_0_4.xml create mode 100644 doc/topology/Intel_Xeon_E5620_x2_UMA_windows_2_0_4.xml create mode 100644 doc/topology/Intel_Xeon_E7-4870_x4_N4_windows_2_0_4.xml create mode 100644 doc/topology/Intel_Xeon_Gold_6146_x2_UMA_linux_2_0_4.xml create mode 100644 doc/topology/Intel_Xeon_Silver_4114_x2_N2_linux_1_11_9.xml create mode 100644 doc/topology/Intel_Xeon_Silver_4114_x2_N2_linux_2_0_4.xml rename src/{interfaces/IWorker.h => 3rdparty/argon2.h} (71%) delete mode 100644 src/3rdparty/argon2/.gitattributes delete mode 100644 src/3rdparty/argon2/.gitignore delete mode 100644 src/3rdparty/argon2/.travis.yml create mode 100644 src/3rdparty/argon2/arch/x86_64/lib/cpu-flags.c create mode 100644 src/3rdparty/argon2/arch/x86_64/lib/cpu-flags.h create mode 100644 src/3rdparty/argon2/arch/x86_64/src/test-feature-avx2.c create mode 100644 src/3rdparty/argon2/arch/x86_64/src/test-feature-avx512f.c create mode 100644 src/3rdparty/argon2/arch/x86_64/src/test-feature-sse2.c create mode 100644 src/3rdparty/argon2/arch/x86_64/src/test-feature-ssse3.c create mode 100644 src/3rdparty/argon2/arch/x86_64/src/test-feature-xop.c delete mode 100644 src/3rdparty/argon2/configure.ac delete mode 100644 src/3rdparty/argon2/lib/thread.c delete mode 100644 src/3rdparty/argon2/lib/thread.h delete mode 100644 src/3rdparty/argon2/m4/ax_check_compile_flag.m4 delete mode 100644 src/3rdparty/argon2/m4/ax_pthread.m4 delete mode 100644 src/3rdparty/argon2/qmake/arch/arch.pro delete mode 100644 src/3rdparty/argon2/qmake/arch/generic/generic.pro delete mode 100644 src/3rdparty/argon2/qmake/arch/x86_64/libargon2-avx2/libargon2-avx2.pro delete mode 100644 src/3rdparty/argon2/qmake/arch/x86_64/libargon2-avx512f/libargon2-avx512f.pro delete mode 100644 src/3rdparty/argon2/qmake/arch/x86_64/libargon2-sse2/libargon2-sse2.pro delete mode 100644 src/3rdparty/argon2/qmake/arch/x86_64/libargon2-ssse3/libargon2-ssse3.pro delete mode 100644 src/3rdparty/argon2/qmake/arch/x86_64/libargon2-xop/libargon2-xop.pro delete mode 100644 src/3rdparty/argon2/qmake/arch/x86_64/x86_64.pro delete mode 100644 src/3rdparty/argon2/qmake/argon2-bench2/argon2-bench2.pro delete mode 100644 src/3rdparty/argon2/qmake/argon2-genkat/argon2-genkat.pro delete mode 100644 src/3rdparty/argon2/qmake/argon2-test/argon2-test.pro delete mode 100644 src/3rdparty/argon2/qmake/argon2.pro delete mode 100644 src/3rdparty/argon2/qmake/argon2/argon2.pro delete mode 100644 src/3rdparty/argon2/qmake/libargon2/libargon2.pro delete mode 100644 src/3rdparty/argon2/scripts/metacentrum/start-all-benchmarks.sh delete mode 100644 src/3rdparty/argon2/scripts/metacentrum/start-benchmark.sh delete mode 100644 src/3rdparty/argon2/scripts/run-benchmark.sh delete mode 100644 src/3rdparty/argon2/src/bench2.c delete mode 100644 src/3rdparty/argon2/src/genkat.c delete mode 100644 src/3rdparty/argon2/src/run.c delete mode 100644 src/3rdparty/argon2/src/timing.h delete mode 100644 src/3rdparty/argon2/tests/test.c delete mode 100644 src/3rdparty/cpp-httplib/example/Makefile delete mode 100644 src/3rdparty/cpp-httplib/example/benchmark.cc delete mode 100644 src/3rdparty/cpp-httplib/example/client.cc delete mode 100644 src/3rdparty/cpp-httplib/example/client.vcxproj delete mode 100644 src/3rdparty/cpp-httplib/example/example.sln delete mode 100644 src/3rdparty/cpp-httplib/example/hello.cc delete mode 100644 src/3rdparty/cpp-httplib/example/server.vcxproj delete mode 100644 src/3rdparty/cpp-httplib/example/simplesvr.cc delete mode 100644 src/3rdparty/cpp-httplib/test/Makefile delete mode 100644 src/3rdparty/cpp-httplib/test/gtest/gtest-all.cc delete mode 100644 src/3rdparty/cpp-httplib/test/gtest/gtest.h delete mode 100644 src/3rdparty/cpp-httplib/test/gtest/gtest_main.cc delete mode 100644 src/3rdparty/cpp-httplib/test/test.cc delete mode 100644 src/3rdparty/cpp-httplib/test/test.sln delete mode 100644 src/3rdparty/cpp-httplib/test/test.vcxproj delete mode 100644 src/3rdparty/cpp-httplib/test/test.xcodeproj/project.pbxproj delete mode 100644 src/3rdparty/cpp-httplib/test/test.xcodeproj/project.xcworkspace/contents.xcworkspacedata delete mode 100644 src/3rdparty/cpp-httplib/test/www/dir/index.html delete mode 100644 src/3rdparty/cpp-httplib/test/www/dir/test.html create mode 100644 src/3rdparty/http-parser/AUTHORS create mode 100644 src/3rdparty/http-parser/LICENSE-MIT create mode 100644 src/3rdparty/http-parser/README.md create mode 100644 src/3rdparty/http-parser/http_parser.c create mode 100644 src/3rdparty/http-parser/http_parser.h create mode 100644 src/3rdparty/hwloc/AUTHORS create mode 100644 src/3rdparty/hwloc/CMakeLists.txt create mode 100644 src/3rdparty/hwloc/COPYING create mode 100644 src/3rdparty/hwloc/NEWS create mode 100644 src/3rdparty/hwloc/README create mode 100644 src/3rdparty/hwloc/VERSION create mode 100644 src/3rdparty/hwloc/include/hwloc.h create mode 100644 src/3rdparty/hwloc/include/hwloc/autogen/config.h create mode 100644 src/3rdparty/hwloc/include/hwloc/bitmap.h create mode 100644 src/3rdparty/hwloc/include/hwloc/cuda.h create mode 100644 src/3rdparty/hwloc/include/hwloc/cudart.h create mode 100644 src/3rdparty/hwloc/include/hwloc/deprecated.h create mode 100644 src/3rdparty/hwloc/include/hwloc/diff.h create mode 100644 src/3rdparty/hwloc/include/hwloc/distances.h create mode 100644 src/3rdparty/hwloc/include/hwloc/export.h create mode 100644 src/3rdparty/hwloc/include/hwloc/gl.h create mode 100644 src/3rdparty/hwloc/include/hwloc/glibc-sched.h create mode 100644 src/3rdparty/hwloc/include/hwloc/helper.h create mode 100644 src/3rdparty/hwloc/include/hwloc/inlines.h create mode 100644 src/3rdparty/hwloc/include/hwloc/intel-mic.h create mode 100644 src/3rdparty/hwloc/include/hwloc/linux-libnuma.h create mode 100644 src/3rdparty/hwloc/include/hwloc/linux.h create mode 100644 src/3rdparty/hwloc/include/hwloc/nvml.h create mode 100644 src/3rdparty/hwloc/include/hwloc/opencl.h create mode 100644 src/3rdparty/hwloc/include/hwloc/openfabrics-verbs.h create mode 100644 src/3rdparty/hwloc/include/hwloc/plugins.h create mode 100644 src/3rdparty/hwloc/include/hwloc/rename.h create mode 100644 src/3rdparty/hwloc/include/hwloc/shmem.h create mode 100644 src/3rdparty/hwloc/include/private/autogen/config.h create mode 100644 src/3rdparty/hwloc/include/private/components.h create mode 100644 src/3rdparty/hwloc/include/private/cpuid-x86.h create mode 100644 src/3rdparty/hwloc/include/private/debug.h create mode 100644 src/3rdparty/hwloc/include/private/internal-components.h create mode 100644 src/3rdparty/hwloc/include/private/misc.h create mode 100644 src/3rdparty/hwloc/include/private/netloc.h create mode 100644 src/3rdparty/hwloc/include/private/private.h create mode 100644 src/3rdparty/hwloc/include/private/solaris-chiptype.h create mode 100644 src/3rdparty/hwloc/include/private/xml.h create mode 100644 src/3rdparty/hwloc/src/base64.c create mode 100644 src/3rdparty/hwloc/src/bind.c create mode 100644 src/3rdparty/hwloc/src/bitmap.c create mode 100644 src/3rdparty/hwloc/src/components.c create mode 100644 src/3rdparty/hwloc/src/diff.c create mode 100644 src/3rdparty/hwloc/src/distances.c create mode 100644 src/3rdparty/hwloc/src/misc.c create mode 100644 src/3rdparty/hwloc/src/pci-common.c create mode 100644 src/3rdparty/hwloc/src/shmem.c create mode 100644 src/3rdparty/hwloc/src/static-components.h create mode 100644 src/3rdparty/hwloc/src/topology-noos.c create mode 100644 src/3rdparty/hwloc/src/topology-synthetic.c create mode 100644 src/3rdparty/hwloc/src/topology-windows.c create mode 100644 src/3rdparty/hwloc/src/topology-x86.c create mode 100644 src/3rdparty/hwloc/src/topology-xml-nolibxml.c create mode 100644 src/3rdparty/hwloc/src/topology-xml.c create mode 100644 src/3rdparty/hwloc/src/topology.c create mode 100644 src/3rdparty/hwloc/src/traversal.c create mode 100644 src/3rdparty/rapidjson/cursorstreamwrapper.h create mode 100644 src/3rdparty/rapidjson/license.txt delete mode 100644 src/AsmOptimization.h delete mode 100644 src/Cpu.cpp delete mode 100644 src/Cpu.h delete mode 100644 src/CpuImpl.h delete mode 100644 src/Cpu_cpuid.cpp delete mode 100644 src/Cpu_stub.cpp delete mode 100644 src/Cpu_unix.cpp delete mode 100644 src/Embedded_config.h delete mode 100644 src/Mem.cpp delete mode 100644 src/Mem.h delete mode 100644 src/Mem_unix.cpp delete mode 100644 src/Options.cpp delete mode 100644 src/Options.h delete mode 100644 src/Platform_mac.cpp delete mode 100644 src/PowVariant.h delete mode 100644 src/api/ApiState.cpp delete mode 100644 src/api/Httpd.cpp create mode 100644 src/backend/backend.cmake rename src/{workers => backend/common}/Hashrate.cpp (52%) rename src/{workers => backend/common}/Hashrate.h (69%) create mode 100644 src/backend/common/Thread.h create mode 100644 src/backend/common/Threads.cpp create mode 100644 src/backend/common/Threads.h rename src/{workers => backend/common}/Worker.cpp (56%) create mode 100644 src/backend/common/Worker.h create mode 100644 src/backend/common/WorkerJob.h create mode 100644 src/backend/common/Workers.cpp create mode 100644 src/backend/common/Workers.h create mode 100644 src/backend/common/common.cmake create mode 100644 src/backend/common/interfaces/IBackend.h create mode 100644 src/backend/common/interfaces/IThread.h create mode 100644 src/backend/common/interfaces/IWorker.h create mode 100644 src/backend/cpu/Cpu.cpp rename src/{Platform.h => backend/cpu/Cpu.h} (61%) create mode 100644 src/backend/cpu/CpuBackend.cpp create mode 100644 src/backend/cpu/CpuBackend.h create mode 100644 src/backend/cpu/CpuConfig.cpp create mode 100644 src/backend/cpu/CpuConfig.h create mode 100644 src/backend/cpu/CpuLaunchData.cpp create mode 100644 src/backend/cpu/CpuLaunchData.h rename src/{Cpu_win.cpp => backend/cpu/CpuThread.cpp} (51%) create mode 100644 src/backend/cpu/CpuThread.h create mode 100644 src/backend/cpu/CpuThreads.cpp create mode 100644 src/backend/cpu/CpuThreads.h create mode 100644 src/backend/cpu/CpuWorker.cpp create mode 100644 src/backend/cpu/CpuWorker.h create mode 100644 src/backend/cpu/cpu.cmake create mode 100644 src/backend/cpu/interfaces/ICpuInfo.h create mode 100644 src/backend/cpu/platform/AdvancedCpuInfo.cpp create mode 100644 src/backend/cpu/platform/AdvancedCpuInfo.h create mode 100644 src/backend/cpu/platform/BasicCpuInfo.cpp create mode 100644 src/backend/cpu/platform/BasicCpuInfo.h rename src/{api/Api.cpp => backend/cpu/platform/BasicCpuInfo_arm.cpp} (51%) create mode 100644 src/backend/cpu/platform/HwlocCpuInfo.cpp create mode 100644 src/backend/cpu/platform/HwlocCpuInfo.h create mode 100644 src/base/api/Api.cpp create mode 100644 src/base/api/Api.h create mode 100644 src/base/api/Httpd.cpp create mode 100644 src/base/api/Httpd.h rename src/{interfaces/IJobResultListener.h => base/api/interfaces/IApiListener.h} (73%) create mode 100644 src/base/api/interfaces/IApiRequest.h rename src/{log/SysLog.cpp => base/api/requests/ApiRequest.cpp} (69%) create mode 100644 src/base/api/requests/ApiRequest.h create mode 100644 src/base/api/requests/HttpApiRequest.cpp create mode 100644 src/base/api/requests/HttpApiRequest.h create mode 100644 src/base/base.cmake rename src/{net/BoostTlsConnection.h => base/cc/interfaces/IClientStatusListener.h} (58%) rename src/{net/BoostTcpConnection.h => base/cc/interfaces/ICommandListener.h} (61%) rename src/{ => base/io}/Console.cpp (56%) rename src/{ => base/io}/Console.h (76%) create mode 100644 src/base/io/Watcher.cpp create mode 100644 src/base/io/Watcher.h create mode 100644 src/base/io/json/Json.cpp create mode 100644 src/base/io/json/Json.h create mode 100644 src/base/io/json/JsonChain.cpp create mode 100644 src/base/io/json/JsonChain.h rename src/{Cpu_arm.cpp => base/io/json/JsonRequest.cpp} (59%) create mode 100644 src/base/io/json/JsonRequest.h create mode 100644 src/base/io/json/Json_unix.cpp create mode 100644 src/base/io/json/Json_win.cpp create mode 100644 src/base/io/log/Log.cpp create mode 100644 src/base/io/log/Log.h create mode 100644 src/base/io/log/backends/ConsoleLog.cpp create mode 100644 src/base/io/log/backends/ConsoleLog.h rename src/{log => base/io/log/backends}/FileLog.cpp (53%) create mode 100644 src/base/io/log/backends/FileLog.h create mode 100644 src/base/io/log/backends/RemoteLog.cpp rename src/{log => base/io/log/backends}/RemoteLog.h (68%) rename src/{workers/Handle.cpp => base/io/log/backends/SysLog.cpp} (58%) create mode 100644 src/base/io/log/backends/SysLog.h create mode 100644 src/base/kernel/Base.cpp create mode 100644 src/base/kernel/Base.h create mode 100644 src/base/kernel/Entry.cpp create mode 100644 src/base/kernel/Entry.h rename src/{ => base/kernel}/Platform.cpp (58%) rename src/{net/JobId.h => base/kernel/Platform.h} (51%) rename src/{Platform_unix.cpp => base/kernel/Platform_mac.cpp} (68%) create mode 100644 src/base/kernel/Platform_unix.cpp rename src/{ => base/kernel}/Platform_win.cpp (74%) create mode 100644 src/base/kernel/Process.cpp create mode 100644 src/base/kernel/Process.h create mode 100644 src/base/kernel/Signals.cpp create mode 100644 src/base/kernel/Signals.h create mode 100644 src/base/kernel/config/BaseConfig.cpp create mode 100644 src/base/kernel/config/BaseConfig.h create mode 100644 src/base/kernel/config/BaseTransform.cpp create mode 100644 src/base/kernel/config/BaseTransform.h create mode 100644 src/base/kernel/interfaces/IBaseListener.h create mode 100644 src/base/kernel/interfaces/IClient.h create mode 100644 src/base/kernel/interfaces/IClientListener.h create mode 100644 src/base/kernel/interfaces/IConfig.h create mode 100644 src/base/kernel/interfaces/IConfigListener.h create mode 100644 src/base/kernel/interfaces/IConfigTransform.h rename src/{ => base/kernel}/interfaces/IConsoleListener.h (71%) rename src/{log/FileLog.h => base/kernel/interfaces/IDnsListener.h} (66%) create mode 100644 src/base/kernel/interfaces/IHttpListener.h create mode 100644 src/base/kernel/interfaces/IJsonReader.h rename src/{interfaces/IStrategy.h => base/kernel/interfaces/ILineListener.h} (65%) create mode 100644 src/base/kernel/interfaces/ILogBackend.h rename src/{api/Api.h => base/kernel/interfaces/ISignalListener.h} (66%) create mode 100644 src/base/kernel/interfaces/IStrategy.h create mode 100644 src/base/kernel/interfaces/IStrategyListener.h rename src/{crypto/Argon2.h => base/kernel/interfaces/ITcpServerListener.h} (62%) rename src/{net/SubmitResult.h => base/kernel/interfaces/ITimerListener.h} (66%) create mode 100644 src/base/kernel/interfaces/IWatcherListener.h create mode 100644 src/base/net/dns/Dns.cpp create mode 100644 src/base/net/dns/Dns.h create mode 100644 src/base/net/dns/DnsRecord.cpp create mode 100644 src/base/net/dns/DnsRecord.h create mode 100644 src/base/net/http/Http.cpp create mode 100644 src/base/net/http/Http.h create mode 100644 src/base/net/http/HttpApiResponse.cpp create mode 100644 src/base/net/http/HttpApiResponse.h create mode 100644 src/base/net/http/HttpClient.cpp create mode 100644 src/base/net/http/HttpClient.h create mode 100644 src/base/net/http/HttpContext.cpp create mode 100644 src/base/net/http/HttpContext.h create mode 100644 src/base/net/http/HttpData.h create mode 100644 src/base/net/http/HttpResponse.cpp create mode 100644 src/base/net/http/HttpResponse.h create mode 100644 src/base/net/http/HttpServer.cpp create mode 100644 src/base/net/http/HttpServer.h create mode 100644 src/base/net/http/HttpsClient.cpp create mode 100644 src/base/net/http/HttpsClient.h create mode 100644 src/base/net/stratum/BaseClient.cpp create mode 100644 src/base/net/stratum/BaseClient.h create mode 100644 src/base/net/stratum/Client.cpp create mode 100644 src/base/net/stratum/Client.h create mode 100644 src/base/net/stratum/DaemonClient.cpp create mode 100644 src/base/net/stratum/DaemonClient.h create mode 100644 src/base/net/stratum/Job.cpp create mode 100644 src/base/net/stratum/Job.h create mode 100644 src/base/net/stratum/Pool.cpp create mode 100644 src/base/net/stratum/Pool.h create mode 100644 src/base/net/stratum/Pools.cpp create mode 100644 src/base/net/stratum/Pools.h create mode 100644 src/base/net/stratum/SubmitResult.h create mode 100644 src/base/net/stratum/Tls.cpp rename src/{api/ApiState.h => base/net/stratum/Tls.h} (51%) create mode 100644 src/base/net/stratum/strategies/FailoverStrategy.cpp create mode 100644 src/base/net/stratum/strategies/FailoverStrategy.h create mode 100644 src/base/net/stratum/strategies/SinglePoolStrategy.cpp create mode 100644 src/base/net/stratum/strategies/SinglePoolStrategy.h create mode 100644 src/base/net/tools/RecvBuf.h create mode 100644 src/base/net/tools/Storage.h create mode 100644 src/base/net/tools/TcpServer.cpp create mode 100644 src/base/net/tools/TcpServer.h create mode 100644 src/base/tools/Arguments.cpp create mode 100644 src/base/tools/Arguments.h rename src/{log/SysLog.h => base/tools/Baton.h} (68%) create mode 100644 src/base/tools/Buffer.cpp create mode 100644 src/base/tools/Buffer.h create mode 100644 src/base/tools/Chrono.h create mode 100644 src/base/tools/Handle.h create mode 100644 src/base/tools/String.cpp create mode 100644 src/base/tools/String.h create mode 100644 src/base/tools/Timer.cpp rename src/{workers/Handle.h => base/tools/Timer.h} (55%) create mode 100644 src/cc/CCClientConfig.cpp create mode 100644 src/cc/CCClientConfig.h create mode 100644 src/core/Controller.cpp rename src/{interfaces/IStrategyListener.h => core/Controller.h} (56%) create mode 100644 src/core/Miner.cpp create mode 100644 src/core/Miner.h create mode 100644 src/core/config/Config.cpp create mode 100644 src/core/config/Config.h create mode 100644 src/core/config/ConfigTransform.cpp create mode 100644 src/core/config/ConfigTransform.h create mode 100644 src/core/config/Config_default.h create mode 100644 src/core/config/Config_platform.h create mode 100644 src/core/config/usage.h delete mode 100644 src/crypto/Argon2_test.h delete mode 100644 src/crypto/CryptoNight.h delete mode 100644 src/crypto/CryptoNight_arm.h delete mode 100644 src/crypto/CryptoNight_test.h delete mode 100644 src/crypto/CryptoNight_x86.h delete mode 100644 src/crypto/HashSelector.cpp rename src/{Cpu_mac.cpp => crypto/argon2/Hash.h} (51%) rename src/{net/SubmitResult.cpp => crypto/argon2/Impl.cpp} (54%) rename src/{interfaces/ILogBackend.h => crypto/argon2/Impl.h} (66%) delete mode 100644 src/crypto/asm/cn_main_loop.S delete mode 100644 src/crypto/asm/cnv1_main_loop_rto_sandybridge.inc delete mode 100644 src/crypto/asm/cnv1_main_loop_rto_soft_aes_sandybridge.inc delete mode 100644 src/crypto/asm/cnv1_main_loop_sandybridge.inc.in delete mode 100644 src/crypto/asm/cnv1_main_loop_soft_aes_sandybridge.inc.in delete mode 100644 src/crypto/asm/cnv2_main_loop_soft_aes_sandybridge.inc.in delete mode 100644 src/crypto/asm/win/CryptonightR_template.S delete mode 100644 src/crypto/asm/win/cn_main_loop.asm delete mode 100644 src/crypto/asm/win/cn_main_loop_win_gcc.S delete mode 100644 src/crypto/asm/win/cnv1_main_loop_rto_sandybridge.inc delete mode 100644 src/crypto/asm/win/cnv1_main_loop_rto_soft_aes_sandybridge.inc delete mode 100644 src/crypto/asm/win/cnv1_main_loop_sandybridge.inc.in delete mode 100644 src/crypto/asm/win/cnv1_main_loop_soft_aes_sandybridge.inc.in delete mode 100644 src/crypto/asm/win/cnv2_main_loop_soft_aes_sandybridge.inc.in delete mode 100644 src/crypto/c_keccak.h create mode 100644 src/crypto/cn/CnAlgo.h create mode 100644 src/crypto/cn/CnCtx.cpp create mode 100644 src/crypto/cn/CnCtx.h create mode 100644 src/crypto/cn/CnHash.cpp create mode 100644 src/crypto/cn/CnHash.h create mode 100644 src/crypto/cn/CryptoNight.h create mode 100644 src/crypto/cn/CryptoNight_arm.h create mode 100644 src/crypto/cn/CryptoNight_monero.h create mode 100644 src/crypto/cn/CryptoNight_test.h create mode 100644 src/crypto/cn/CryptoNight_x86.h rename src/crypto/{ => cn}/SSE2NEON.h (99%) rename src/crypto/{ => cn}/asm/CryptonightR_soft_aes_template.inc (99%) rename src/crypto/{asm/win/CryptonightR_soft_aes_template.inc => cn/asm/CryptonightR_soft_aes_template_win.inc} (86%) rename src/crypto/{ => cn}/asm/CryptonightR_template.S (99%) rename src/crypto/{asm/win => cn/asm}/CryptonightR_template.asm (100%) rename src/crypto/{ => cn}/asm/CryptonightR_template.h (100%) rename src/crypto/{ => cn}/asm/CryptonightR_template.inc (97%) rename src/crypto/{asm/win/CryptonightR_template.inc => cn/asm/CryptonightR_template_win.inc} (79%) rename src/crypto/{ => cn}/asm/CryptonightWOW_soft_aes_template.inc (99%) rename src/crypto/{asm/win/CryptonightWOW_soft_aes_template.inc => cn/asm/CryptonightWOW_soft_aes_template_win.inc} (85%) rename src/crypto/{ => cn}/asm/CryptonightWOW_template.inc (99%) rename src/crypto/{asm/win/CryptonightWOW_template.inc => cn/asm/CryptonightWOW_template_win.inc} (79%) rename src/crypto/{asm/win/cnv2_double_main_loop_sandybridge.inc.in => cn/asm/cn2/cnv2_double_main_loop_sandybridge.inc} (89%) rename src/crypto/{asm/cnv2_main_loop_bulldozer.inc.in => cn/asm/cn2/cnv2_main_loop_bulldozer.inc} (89%) rename src/crypto/{asm/win/cnv2_main_loop_ivybridge.inc.in => cn/asm/cn2/cnv2_main_loop_ivybridge.inc} (89%) rename src/crypto/{asm/win/cnv2_main_loop_ryzen.inc.in => cn/asm/cn2/cnv2_main_loop_ryzen.inc} (90%) rename src/crypto/{asm/cnv2_double_main_loop_rwz_all.inc.in => cn/asm/cn2/cnv2_rwz_double_main_loop.inc} (90%) rename src/crypto/{asm/cnv2_main_loop_rwz_all.inc.in => cn/asm/cn2/cnv2_rwz_main_loop.inc} (89%) create mode 100644 src/crypto/cn/asm/cn_main_loop.S create mode 100644 src/crypto/cn/asm/cn_main_loop.asm rename src/crypto/{asm/win => cn/asm/win64}/CryptonightR_soft_aes_template_win.inc (99%) create mode 100644 src/crypto/cn/asm/win64/CryptonightR_template.asm rename src/crypto/{asm/win => cn/asm/win64}/CryptonightR_template_win.inc (97%) rename src/crypto/{asm/win => cn/asm/win64}/CryptonightWOW_soft_aes_template_win.inc (99%) rename src/crypto/{asm/win => cn/asm/win64}/CryptonightWOW_template_win.inc (99%) rename src/crypto/{asm/cnv2_double_main_loop_sandybridge.inc.in => cn/asm/win64/cn2/cnv2_double_main_loop_sandybridge.inc} (79%) rename src/crypto/{asm/win/cnv2_main_loop_bulldozer.inc.in => cn/asm/win64/cn2/cnv2_main_loop_bulldozer.inc} (89%) rename src/crypto/{asm/cnv2_main_loop_ivybridge.inc.in => cn/asm/win64/cn2/cnv2_main_loop_ivybridge.inc} (80%) rename src/crypto/{asm/cnv2_main_loop_ryzen.inc.in => cn/asm/win64/cn2/cnv2_main_loop_ryzen.inc} (81%) rename src/crypto/{asm/win/cnv2_double_main_loop_rwz_all.inc.in => cn/asm/win64/cn2/cnv2_rwz_double_main_loop.inc} (91%) rename src/crypto/{asm/win/cnv2_main_loop_rwz_all.inc.in => cn/asm/win64/cn2/cnv2_rwz_main_loop.inc} (91%) create mode 100644 src/crypto/cn/asm/win64/cn_main_loop.S create mode 100644 src/crypto/cn/asm/win64/cn_main_loop.asm rename src/crypto/{ => cn}/c_blake256.c (100%) rename src/crypto/{ => cn}/c_blake256.h (100%) rename src/crypto/{ => cn}/c_groestl.c (100%) rename src/crypto/{ => cn}/c_groestl.h (100%) rename src/crypto/{ => cn}/c_jh.c (100%) rename src/crypto/{ => cn}/c_jh.h (100%) rename src/crypto/{ => cn}/c_skein.c (100%) rename src/crypto/{ => cn}/c_skein.h (100%) create mode 100644 src/crypto/cn/gpu/cn_gpu_arm.cpp create mode 100644 src/crypto/cn/gpu/cn_gpu_avx.cpp create mode 100644 src/crypto/cn/gpu/cn_gpu_ssse3.cpp rename src/crypto/{ => cn}/groestl_tables.h (100%) rename src/crypto/{ => cn}/hash.h (100%) rename src/crypto/{CryptoNightR_gen.cpp => cn/r/CryptonightR_gen.cpp} (85%) create mode 100644 src/crypto/cn/r/variant4_random_math.h rename src/crypto/{ => cn}/skein_port.h (100%) rename src/crypto/{ => cn}/soft_aes.h (88%) create mode 100644 src/crypto/common/Algorithm.cpp create mode 100644 src/crypto/common/Algorithm.h create mode 100644 src/crypto/common/Assembly.cpp create mode 100644 src/crypto/common/Assembly.h create mode 100644 src/crypto/common/Nonce.cpp create mode 100644 src/crypto/common/Nonce.h create mode 100644 src/crypto/common/VirtualMemory.cpp create mode 100644 src/crypto/common/VirtualMemory.h create mode 100644 src/crypto/common/VirtualMemory_unix.cpp rename src/{Mem_win.cpp => crypto/common/VirtualMemory_win.cpp} (59%) rename src/crypto/{c_keccak.c => common/keccak.cpp} (74%) create mode 100644 src/crypto/common/keccak.h rename src/{3rdparty/aligned_malloc.h => crypto/common/portable/mm_malloc.h} (57%) create mode 100644 src/crypto/randomx/aes_hash.cpp create mode 100644 src/crypto/randomx/aes_hash.hpp create mode 100644 src/crypto/randomx/allocator.cpp create mode 100644 src/crypto/randomx/allocator.hpp create mode 100644 src/crypto/randomx/argon2.h create mode 100644 src/crypto/randomx/argon2_core.c create mode 100644 src/crypto/randomx/argon2_core.h create mode 100644 src/crypto/randomx/argon2_ref.c create mode 100644 src/crypto/randomx/asm/program_epilogue_linux.inc create mode 100644 src/crypto/randomx/asm/program_epilogue_store.inc create mode 100644 src/crypto/randomx/asm/program_epilogue_win64.inc create mode 100644 src/crypto/randomx/asm/program_loop_load.inc create mode 100644 src/crypto/randomx/asm/program_loop_store.inc create mode 100644 src/crypto/randomx/asm/program_prologue_linux.inc create mode 100644 src/crypto/randomx/asm/program_prologue_win64.inc create mode 100644 src/crypto/randomx/asm/program_read_dataset.inc create mode 100644 src/crypto/randomx/asm/program_read_dataset_sshash_fin.inc create mode 100644 src/crypto/randomx/asm/program_read_dataset_sshash_init.inc create mode 100644 src/crypto/randomx/asm/program_sshash_constants.inc create mode 100644 src/crypto/randomx/asm/program_sshash_load.inc create mode 100644 src/crypto/randomx/asm/program_sshash_prefetch.inc create mode 100644 src/crypto/randomx/asm/program_xmm_constants.inc create mode 100644 src/crypto/randomx/asm/randomx_reciprocal.inc create mode 100644 src/crypto/randomx/blake2/blake2-impl.h create mode 100644 src/crypto/randomx/blake2/blake2.h create mode 100644 src/crypto/randomx/blake2/blake2b.c create mode 100644 src/crypto/randomx/blake2/blamka-round-ref.h create mode 100644 src/crypto/randomx/blake2/endian.h create mode 100644 src/crypto/randomx/blake2_generator.cpp create mode 100644 src/crypto/randomx/blake2_generator.hpp create mode 100644 src/crypto/randomx/bytecode_machine.cpp create mode 100644 src/crypto/randomx/bytecode_machine.hpp create mode 100644 src/crypto/randomx/common.hpp create mode 100644 src/crypto/randomx/configuration.h create mode 100644 src/crypto/randomx/dataset.cpp create mode 100644 src/crypto/randomx/dataset.hpp create mode 100644 src/crypto/randomx/instruction.hpp create mode 100644 src/crypto/randomx/instructions_portable.cpp create mode 100644 src/crypto/randomx/intrin_portable.h create mode 100644 src/crypto/randomx/jit_compiler.hpp create mode 100644 src/crypto/randomx/jit_compiler_a64.hpp create mode 100644 src/crypto/randomx/jit_compiler_fallback.hpp create mode 100644 src/crypto/randomx/jit_compiler_x86.cpp create mode 100644 src/crypto/randomx/jit_compiler_x86.hpp create mode 100644 src/crypto/randomx/jit_compiler_x86_static.S create mode 100644 src/crypto/randomx/jit_compiler_x86_static.asm create mode 100644 src/crypto/randomx/jit_compiler_x86_static.hpp create mode 100644 src/crypto/randomx/program.hpp create mode 100644 src/crypto/randomx/randomx.cpp create mode 100644 src/crypto/randomx/randomx.h create mode 100644 src/crypto/randomx/reciprocal.c create mode 100644 src/crypto/randomx/reciprocal.h create mode 100644 src/crypto/randomx/soft_aes.cpp create mode 100644 src/crypto/randomx/soft_aes.h create mode 100644 src/crypto/randomx/superscalar.cpp create mode 100644 src/crypto/randomx/superscalar.hpp create mode 100644 src/crypto/randomx/superscalar_program.hpp create mode 100644 src/crypto/randomx/virtual_machine.cpp create mode 100644 src/crypto/randomx/virtual_machine.hpp create mode 100644 src/crypto/randomx/virtual_memory.cpp create mode 100644 src/crypto/randomx/virtual_memory.hpp create mode 100644 src/crypto/randomx/vm_compiled.cpp create mode 100644 src/crypto/randomx/vm_compiled.hpp create mode 100644 src/crypto/randomx/vm_compiled_light.cpp create mode 100644 src/crypto/randomx/vm_compiled_light.hpp create mode 100644 src/crypto/randomx/vm_interpreted.cpp create mode 100644 src/crypto/randomx/vm_interpreted.hpp create mode 100644 src/crypto/randomx/vm_interpreted_light.cpp create mode 100644 src/crypto/randomx/vm_interpreted_light.hpp create mode 100644 src/crypto/rx/Rx.cpp rename src/{interfaces/IClientListener.h => crypto/rx/Rx.h} (55%) create mode 100644 src/crypto/rx/RxAlgo.cpp rename src/crypto/{HashSelector.h => rx/RxAlgo.h} (63%) create mode 100644 src/crypto/rx/RxCache.cpp create mode 100644 src/crypto/rx/RxCache.h create mode 100644 src/crypto/rx/RxConfig.cpp rename src/{api/Httpd.h => crypto/rx/RxConfig.h} (59%) create mode 100644 src/crypto/rx/RxDataset.cpp create mode 100644 src/crypto/rx/RxDataset.h create mode 100644 src/crypto/rx/RxVm.cpp rename src/{workers/Worker.h => crypto/rx/RxVm.h} (58%) delete mode 100644 src/crypto/variant4_random_math.h delete mode 100644 src/default_miner_config.json delete mode 100644 src/log/ConsoleLog.cpp delete mode 100644 src/log/Log.cpp delete mode 100644 src/log/Log.h delete mode 100644 src/log/RemoteLog.cpp delete mode 100644 src/net/BoostConnection.h delete mode 100644 src/net/BoostTcpConnection.cpp delete mode 100644 src/net/BoostTlsConnection.cpp delete mode 100644 src/net/Client.cpp delete mode 100644 src/net/Client.h delete mode 100644 src/net/Connection.cpp delete mode 100644 src/net/Connection.h delete mode 100644 src/net/Job.cpp delete mode 100644 src/net/Job.h create mode 100644 src/net/JobResults.cpp rename src/{log/ConsoleLog.h => net/JobResults.h} (61%) rename src/{api => net}/NetworkState.cpp (56%) rename src/{api => net}/NetworkState.h (65%) delete mode 100644 src/net/Url.cpp delete mode 100644 src/net/Url.h create mode 100644 src/net/interfaces/IJobResultListener.h delete mode 100644 src/net/strategies/FailoverStrategy.cpp delete mode 100644 src/net/strategies/FailoverStrategy.h delete mode 100644 src/net/strategies/SinglePoolStrategy.cpp delete mode 100644 src/net/strategies/SinglePoolStrategy.h delete mode 100644 src/win_dirent.h delete mode 100644 src/workers/MultiWorker.cpp delete mode 100644 src/workers/MultiWorker.h delete mode 100644 src/workers/Workers.cpp delete mode 100644 src/workers/Workers.h diff --git a/.gitignore b/.gitignore index 9265eadc..6145c899 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,6 @@ -/test/CMakeFiles -/test/*/CMakeFiles /build /CMakeLists.txt.user /.idea -/CMakeFiles /src/3rdparty /cmake-build-* CMakeCache.txt @@ -14,3 +11,6 @@ src/crypto/asm/*.inc src/crypto/asm/win/*.inc src/3rdparty/ test/ +*CMakeFiles* +*.a +*.cbp diff --git a/.travis.yml b/.travis.yml index fa5e813d..5ac4fae1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,8 +13,7 @@ before_install: script: - brew install gcc libuv libmicrohttpd - - brew upgrade boost - - cmake . -DUV_LIBRARY=/usr/local/lib/libuv.a -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl -DOPENSSL_SSL_LIBRARY=/usr/local/opt/openssl/lib/libssl.a -DOPENSSL_CRYPTO_LIBRARY=/usr/local/opt/openssl/lib/libcrypto.a -DBOOST_ROOT=/usr/local/lib + - cmake . -DUV_LIBRARY=/usr/local/lib/libuv.a -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl -DOPENSSL_SSL_LIBRARY=/usr/local/opt/openssl/lib/libssl.a -DOPENSSL_CRYPTO_LIBRARY=/usr/local/opt/openssl/lib/libcrypto.a - make - cp ./src/config.json . - ./xmrigDaemon --version diff --git a/CHANGELOG.md b/CHANGELOG.md index 80760706..6046eeb4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +# 2.0.0 +**Thx to @xmrig and @SChernykh awesome work!** +* Full Rebase on XMRig 3.1.1 + * randomX/wow/XL + * NUMA support + * flexible multi algorithm configuration + * unlimited switching between incompatible algorithms at runtime +* Argon2, UPX2 (Nice hashrate improvement) and CN-Conceal support integrated like in previous version +* 5-10% Hashrate improvement on ARMv8 CPUs when mining CN based algos compared to stock xmrig +* Fully compatible to XMRigCCServer 1.9.5 no server upgrade needed! +**New XMRigCCServer will be released soon with new features** # 1.9.5 - Integrated CN-Conceal algo (algo: "cryptonight", variant: "conceal" or variant: "ccx") #259 - Integrated Argon2-512 algo "chukwa" for upcoming trtl fork (algo: "argon2-512", variant: "auto" or "chukwa") #258 diff --git a/CMakeLists.txt b/CMakeLists.txt index 72d4065e..8803e0eb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,112 +1,160 @@ cmake_minimum_required(VERSION 2.8) project(xmrig) -if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) - set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE) -endif(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) +#set(CMAKE_BUILD_TYPE "Debug") -option(WITH_LIBCPUID "Use Libcpuid" ON) -option(WITH_HTTPD "HTTP REST API" OFF) -option(WITH_CC_CLIENT "CC Client" ON) -option(WITH_CC_SERVER "CC Server" ON) -option(WITH_TLS "TLS support" ON) -option(WITH_ASM "ASM optimizations" ON) -option(BUILD_STATIC "Build static binary" OFF) -set(Boost_USE_STATIC_RUNTIME ON) -set(Boost_USE_STATIC_LIBS ON) +find_program(CCACHE_PROGRAM ccache) +if(CCACHE_PROGRAM) + message(STATUS "-- XMRigCC: Found ccache package... Activating...") + set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_PROGRAM}") +endif() + +option(WITH_LIBCPUID "Enable libcpuid support" ON) +option(WITH_HWLOC "Enable hwloc support" ON) +option(WITH_CN_LITE "Enable CryptoNight-Lite algorithms family" ON) +option(WITH_CN_HEAVY "Enable CryptoNight-Heavy algorithms family" ON) +option(WITH_CN_PICO "Enable CryptoNight-Pico algorithm" ON) +option(WITH_CN_GPU "Enable CryptoNight-GPU algorithm" OFF) +option(WITH_RANDOMX "Enable RandomX algorithms family" ON) +option(WITH_ARGON2 "Enable Argon2 algorithms family" ON) +option(WITH_HTTP "Enable HTTP protocol support (client/server)" ON) +option(WITH_CN_EXTREMELITE "CryptoNight-Extremelite support" ON) +option(WITH_DEBUG_LOG "Enable debug log output" OFF) +option(WITH_TLS "Enable OpenSSL support" ON) +option(WITH_ASM "Enable ASM PoW implementations" ON) +option(WITH_EMBEDDED_CONFIG "Enable internal embedded JSON config" OFF) +option(WITH_CC_CLIENT "CC Client" ON) +option(WITH_CC_SERVER "CC Server" OFF) + +option(BUILD_STATIC "Build static binary" OFF) +option(ARM_TARGET "Force use specific ARM target 8 or 7" 0) +option(HWLOC_DEBUG "Enable hwloc debug helpers and log" OFF) + + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake") if(NOT MINER_EXECUTABLE_NAME) -set(MINER_EXECUTABLE_NAME "xmrigMiner" CACHE STRING "Miner executable file name") + set(MINER_EXECUTABLE_NAME "xmrigMiner" CACHE STRING "Miner executable file name") endif(NOT MINER_EXECUTABLE_NAME) if(NOT DAEMON_EXECUTABLE_NAME) -set(DAEMON_EXECUTABLE_NAME "xmrigDaemon" CACHE STRING "Daemon executable file name") + set(DAEMON_EXECUTABLE_NAME "xmrigDaemon" CACHE STRING "Daemon executable file name") endif(NOT DAEMON_EXECUTABLE_NAME) include (CheckIncludeFile) include (cmake/cpu.cmake) +include (src/base/base.cmake) +include (src/backend/backend.cmake) + + +set(HEADERS + "${HEADERS_BASE}" + "${HEADERS_BASE_HTTP}" + "${HEADERS_BACKEND}" + src/App.h + src/core/config/Config_default.h + src/core/config/Config_platform.h + src/core/config/Config.h + src/core/config/ConfigTransform.h + src/core/config/usage.h + src/core/Controller.h + src/core/Miner.h + src/net/interfaces/IJobResultListener.h + src/net/JobResult.h + src/net/JobResults.h + src/net/Network.h + src/net/NetworkState.h + src/net/strategies/DonateStrategy.h + src/Summary.h + src/version.h + ) + +set(HEADERS_CRYPTO + src/crypto/cn/asm/CryptonightR_template.h + src/crypto/cn/c_blake256.h + src/crypto/cn/c_groestl.h + src/crypto/cn/c_jh.h + src/crypto/cn/c_skein.h + src/crypto/cn/CnAlgo.h + src/crypto/cn/CnCtx.h + src/crypto/cn/CnHash.h + src/crypto/cn/CryptoNight_monero.h + src/crypto/cn/CryptoNight_test.h + src/crypto/cn/CryptoNight.h + src/crypto/cn/groestl_tables.h + src/crypto/cn/hash.h + src/crypto/cn/skein_port.h + src/crypto/cn/soft_aes.h + src/crypto/common/Algorithm.h + src/crypto/common/keccak.h + src/crypto/common/Nonce.h + src/crypto/common/portable/mm_malloc.h + src/crypto/common/VirtualMemory.h + ) + +if (XMRIG_ARM) + set(HEADERS_CRYPTO "${HEADERS_CRYPTO}" src/crypto/cn/CryptoNight_arm.h) +else() + set(HEADERS_CRYPTO "${HEADERS_CRYPTO}" src/crypto/cn/CryptoNight_x86.h) +endif() set(SOURCES - src/api/NetworkState.cpp + "${SOURCES_BASE}" + "${SOURCES_BASE_HTTP}" + "${SOURCES_BACKEND}" src/App.cpp - src/net/BoostTcpConnection.cpp - src/net/Client.cpp - src/net/Connection.cpp - src/net/Job.cpp + src/core/config/Config.cpp + src/core/config/ConfigTransform.cpp + src/core/Controller.cpp + src/core/Miner.cpp + src/net/JobResults.cpp src/net/Network.cpp + src/net/NetworkState.cpp src/net/strategies/DonateStrategy.cpp - src/net/strategies/FailoverStrategy.cpp - src/net/strategies/SinglePoolStrategy.cpp - src/net/SubmitResult.cpp src/Summary.cpp - src/workers/MultiWorker.cpp - src/workers/Handle.cpp - src/workers/Hashrate.cpp - src/workers/Worker.cpp - src/workers/Workers.cpp src/xmrig.cpp ) set(SOURCES_CRYPTO - src/crypto/c_keccak.c - src/crypto/c_groestl.c - src/crypto/c_blake256.c - src/crypto/c_jh.c - src/crypto/c_skein.c - src/crypto/HashSelector.cpp - src/crypto/CryptoNightR_gen.cpp - ) - -set(SOURCES_COMMON - src/Console.cpp - src/Mem.cpp - src/net/Url.cpp - src/Options.cpp - src/log/ConsoleLog.cpp - src/log/FileLog.cpp - src/log/RemoteLog.cpp - src/log/Log.cpp - src/Platform.cpp - src/Cpu.cpp - + src/crypto/cn/c_blake256.c + src/crypto/cn/c_groestl.c + src/crypto/cn/c_jh.c + src/crypto/cn/c_skein.c + src/crypto/cn/CnCtx.cpp + src/crypto/cn/CnHash.cpp + src/crypto/common/Algorithm.cpp + src/crypto/common/keccak.cpp + src/crypto/common/Nonce.cpp + src/crypto/common/VirtualMemory.cpp ) if (WIN32) set(SOURCES_OS + "${SOURCES_OS}" res/app.rc - src/api/Api.cpp - src/api/ApiState.cpp src/App_win.cpp - src/Cpu_win.cpp - src/Mem_win.cpp - src/Platform_win.cpp + src/crypto/common/VirtualMemory_win.cpp ) add_definitions(/DWIN32) - set(EXTRA_LIBS ws2_32 psapi iphlpapi userenv crypt32) + set(EXTRA_LIBS ws2_32 psapi iphlpapi userenv) elseif (APPLE) set(SOURCES_OS + "${SOURCES_OS}" src/App_unix.cpp - src/Cpu_mac.cpp - src/Mem_unix.cpp - src/Platform_mac.cpp + src/crypto/common/VirtualMemory_unix.cpp ) else() set(SOURCES_OS - src/api/Api.cpp - src/api/ApiState.cpp + "${SOURCES_OS}" src/App_unix.cpp - src/Cpu_unix.cpp - src/Mem_unix.cpp - src/Platform_unix.cpp + src/crypto/common/VirtualMemory_unix.cpp ) - set(EXTRA_LIBS pthread rt) - if (CMAKE_SYSTEM_NAME STREQUAL FreeBSD) - set(EXTRA_LIBS ${EXTRA_LIBS} kvm) + set(EXTRA_LIBS kvm pthread) else() - set(EXTRA_LIBS ${EXTRA_LIBS} dl) + set(EXTRA_LIBS pthread rt dl) endif() endif() @@ -120,76 +168,41 @@ endif() add_definitions(/D__STDC_FORMAT_MACROS) add_definitions(/DUNICODE) add_definitions(/DMINER_EXECUTABLE_NAME=${MINER_EXECUTABLE_NAME}) -#add_definitions(/DAPP_DEBUG) - -add_compile_options(-fexceptions) - -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake") find_package(UV REQUIRED) -if (WIN32) - add_definitions(-DBOOST_ALL_NO_LIB) -endif(WIN32) - -find_package(Boost 1.62.0 COMPONENTS system REQUIRED) - include(cmake/flags.cmake) +include(cmake/randomx.cmake) +include(cmake/argon2.cmake) +include(cmake/OpenSSL.cmake) +include(cmake/asm.cmake) +include(cmake/cn-gpu.cmake) -if (WITH_TLS) - find_package(OpenSSL) - - add_definitions(/DCPPHTTPLIB_OPENSSL_SUPPORT) - - if (OPENSSL_FOUND) - include_directories(${OPENSSL_INCLUDE_DIR}) - set(SOURCES_SSL_TLS src/net/BoostTlsConnection.cpp) - else() - message(FATAL_ERROR "OpenSSL NOT found: use `-DWITH_TLS=OFF` to build without TLS support") - endif(OPENSSL_FOUND) -else() - add_definitions(/DXMRIG_NO_TLS) -endif(WITH_TLS) - -if (WITH_LIBCPUID) - add_subdirectory(src/3rdparty/libcpuid) - - include_directories(src/3rdparty/libcpuid) - set(CPUID_LIB cpuid) - set(SOURCES_CPUID src/Cpu_cpuid.cpp) -else() - add_definitions(/DXMRIG_NO_LIBCPUID) - - if (XMRIG_ARM) - set(SOURCES_CPUID src/Cpu_arm.cpp) - else() - set(SOURCES_CPUID src/Cpu_stub.cpp) - endif(XMRIG_ARM) -endif(WITH_LIBCPUID) - -CHECK_INCLUDE_FILE (syslog.h HAVE_SYSLOG_H) -if (HAVE_SYSLOG_H) - add_definitions(/DHAVE_SYSLOG_H) - set(SOURCES_SYSLOG src/log/SysLog.h src/log/SysLog.cpp) +if (WITH_CN_LITE) + add_definitions(/DXMRIG_ALGO_CN_LITE) endif() -if (WITH_HTTPD) - find_package(MHD) +if (WITH_CN_HEAVY) + add_definitions(/DXMRIG_ALGO_CN_HEAVY) +endif() - if (MHD_FOUND) - include_directories(${MHD_INCLUDE_DIRS}) - set(HTTPD_SOURCES src/api/Httpd.h src/api/Httpd.cpp) - else() - message(FATAL_ERROR "microhttpd NOT found: use `-DWITH_HTTPD=OFF` to build without http deamon support") - endif(MHD_FOUND) -else() - add_definitions(/DXMRIG_NO_HTTPD) - add_definitions(/DXMRIG_NO_API) -endif(WITH_HTTPD) +if (WITH_CN_PICO) + add_definitions(/DXMRIG_ALGO_CN_PICO) +endif() + +if (WITH_CN_EXTREMELITE) + add_definitions(/DXMRIG_ALGO_CN_EXTREMELITE) +endif() + +if (WITH_ARGON2) + add_definitions(/DXMRIG_ALGO_ARGON2) +endif() + +if (WITH_EMBEDDED_CONFIG) + add_definitions(/DXMRIG_FEATURE_EMBEDDED_CONFIG) +endif() if (WITH_CC_SERVER) - find_package(MHD) - if (MHD_FOUND) include_directories(${MHD_INCLUDE_DIRS}) else() @@ -203,86 +216,44 @@ if (WITH_CC_SERVER) src/cc/Httpd.cpp src/cc/XMRigCC.cpp ) -endif(WITH_CC_SERVER) + add_definitions("/DXMRIG_FEATURE_CC_SERVER") +endif() if (WITH_CC_CLIENT) set(SOURCES_CC_CLIENT + src/cc/CCClientConfig.cpp src/cc/CCClient.cpp) -endif(WITH_CC_CLIENT) + add_definitions("/DXMRIG_FEATURE_CC_CLIENT") + + if (WITH_TLS) + add_definitions(/DCPPHTTPLIB_OPENSSL_SUPPORT) + endif() + +endif() if (WITH_CC_SERVER OR WITH_CC_CLIENT) set(SOURCES_CC_COMMON src/cc/ControlCommand.cpp src/cc/ClientStatus.cpp src/cc/GPUInfo.cpp) -else() - add_definitions(/DXMRIG_NO_CC) -endif(WITH_CC_SERVER OR WITH_CC_CLIENT) - -if (WITH_ASM AND NOT XMRIG_ARM AND CMAKE_SIZEOF_VOID_P EQUAL 8) - include(cmake/asm.cmake) -else() - add_definitions(/DXMRIG_NO_ASM) -endif(WITH_ASM AND NOT XMRIG_ARM AND CMAKE_SIZEOF_VOID_P EQUAL 8) - -add_subdirectory(src/3rdparty/argon2) -set(ARGON2_LIBRARY argon2) - -if (BUILD_STATIC) - set(CMAKE_EXE_LINKER_FLAGS " -static") -endif(BUILD_STATIC) +endif() include_directories(src) include_directories(src/3rdparty) include_directories(${UV_INCLUDE_DIR}) -include_directories(${Boost_INCLUDE_DIRS}) -add_library(xmrig_common STATIC ${SOURCES_COMMON}) -add_library(xmrig_os_dependencies STATIC ${SOURCES_OS} ${SOURCES_SYSLOG}) -add_library(xmrig_cpuid STATIC ${SOURCES_CPUID}) +if (BUILD_STATIC) + set(CMAKE_EXE_LINKER_FLAGS " -static") +endif() -if (WITH_TLS) - add_library(xmrig_tls STATIC ${SOURCES_SSL_TLS}) -endif (WITH_TLS) +if (WITH_DEBUG_LOG) + add_definitions(/DAPP_DEBUG) +endif() -if (WITH_CC_SERVER OR WITH_CC_CLIENT) - add_library(xmrig_cc_common STATIC ${SOURCES_CC_COMMON}) -endif (WITH_CC_SERVER OR WITH_CC_CLIENT) - -add_executable(xmrigMiner ${SOURCES} ${SOURCES_CRYPTO} ${HTTPD_SOURCES} ${SOURCES_CC_CLIENT} res/app.rc) -set_target_properties(xmrigMiner PROPERTIES OUTPUT_NAME ${MINER_EXECUTABLE_NAME}) - -target_link_libraries(xmrigMiner xmrig_common xmrig_os_dependencies xmrig_cpuid ${Boost_LIBRARIES} - ${UV_LIBRARIES} ${MHD_LIBRARY} ${EXTRA_LIBS} ${CPUID_LIB} argon2) - -if (WITH_CC_CLIENT) - target_link_libraries(xmrigMiner xmrig_cc_common) -endif(WITH_CC_CLIENT) - -if (WITH_TLS) - target_link_libraries(xmrigMiner xmrig_tls ${OPENSSL_LIBRARIES} ${EXTRA_LIBS}) -endif(WITH_TLS) - -if (WITH_ASM AND NOT XMRIG_ARM AND CMAKE_SIZEOF_VOID_P EQUAL 8) - target_link_libraries(xmrigMiner xmrig_asm) -endif(WITH_ASM AND NOT XMRIG_ARM AND CMAKE_SIZEOF_VOID_P EQUAL 8) +add_executable(${CMAKE_PROJECT_NAME} ${HEADERS} ${SOURCES} ${SOURCES_OS} ${SOURCES_CPUID} ${HEADERS_CRYPTO} ${SOURCES_CRYPTO} ${SOURCES_SYSLOG} ${TLS_SOURCES} ${XMRIG_ASM_SOURCES} ${CN_GPU_SOURCES} ${SOURCES_CC_CLIENT} ${SOURCES_CC_COMMON}) +target_link_libraries(${CMAKE_PROJECT_NAME} ${XMRIG_ASM_LIBRARY} ${OPENSSL_LIBRARIES} ${UV_LIBRARIES} ${EXTRA_LIBS} ${CPUID_LIB} ${ARGON2_LIBRARY}) add_executable(xmrigDaemon src/cc/XMRigd.cpp res/app.rc) + set_target_properties(xmrigDaemon PROPERTIES OUTPUT_NAME ${DAEMON_EXECUTABLE_NAME}) - -if (WITH_CC_SERVER AND MHD_FOUND) - add_library(xmrig_common_cc STATIC ${SOURCES_COMMON}) - add_executable(xmrigCCServer ${SOURCES_CC_SERVER} res/app.rc) - target_link_libraries(xmrigCCServer - xmrig_common_cc xmrig_os_dependencies xmrig_cpuid xmrig_cc_common - ${UV_LIBRARIES} ${MHD_LIBRARY} ${EXTRA_LIBS} ${CPUID_LIB}) - - if (WITH_TLS) - target_link_libraries(xmrigCCServer xmrig_tls ${OPENSSL_LIBRARIES} ${EXTRA_LIBS}) - endif (WITH_TLS) - - set_target_properties(xmrig_common_cc PROPERTIES COMPILE_FLAGS "-DXMRIG_CC_SERVER ${SHARED_FLAGS}") - set_target_properties(xmrigCCServer PROPERTIES COMPILE_FLAGS "-DXMRIG_CC_SERVER ${SHARED_FLAGS}") -endif(WITH_CC_SERVER AND MHD_FOUND) - -add_subdirectory(test EXCLUDE_FROM_ALL) +set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES OUTPUT_NAME ${MINER_EXECUTABLE_NAME}) diff --git a/Dockerfile b/Dockerfile index 59ceab4f..92385e0a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,20 +1,13 @@ FROM ubuntu:latest RUN apt-get update && \ - apt install git build-essential libbz2-dev cmake libuv1-dev libssl-dev wget gcc g++ -y && \ + apt install git build-essential libbz2-dev cmake libuv1-dev libssl-dev libhwloc-dev wget gcc g++ -y && \ apt clean && \ rm -rf /var/lib/apt/lists/* -RUN wget https://dl.bintray.com/boostorg/release/1.66.0/source/boost_1_66_0.tar.gz \ - && tar xfz boost_1_66_0.tar.gz \ - && cd boost_1_66_0 \ - && ./bootstrap.sh --with-libraries=system \ - && ./b2 link=static runtime-link=static install \ - && cd .. && rm -rf boost_1_66_0 && rm boost_1_66_0.tar.gz && ldconfig - RUN git clone https://github.com/Bendr0id/xmrigCC.git && \ cd xmrigCC && \ - cmake . -DWITH_CC_SERVER=OFF -DWITH_HTTPD=OFF && \ + cmake . -DWITH_CC_SERVER=OFF && \ make COPY Dockerfile /Dockerfile diff --git a/README.md b/README.md index f58bf59f..e0b221ae 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,9 @@ # XMRigCC +XMRig is a high performance RandomX, CryptoNight and Argon2 CPU miner, with official support for Windows. + :bulb: **This is the CPU variant of XMRigCC, if you're looking for the AMD GPU (OpenCL) variant [click here](https://github.com/Bendr0id/xmrigCC-amd/).** -:warning: **Confused by all the forks? Check the [Coin Configuration](https://github.com/Bendr0id/xmrigCC/wiki/Coin-configurations) guide.** - - [![Windows Build status](https://ci.appveyor.com/api/projects/status/l8v7cuuy320a4tpd?svg=true)](https://ci.appveyor.com/project/Bendr0id/xmrigcc) [![Docker Build status](https://img.shields.io/docker/build/bendr0id/xmrigcc.svg)](https://hub.docker.com/r/bendr0id/xmrigcc/) [![GitHub release](https://img.shields.io/github/release/bendr0id/xmrigCC/all.svg)](https://github.com/bendr0id/xmrigCC/releases) @@ -16,164 +15,83 @@ ### About XMRigCC -XMRigCC is a fork of [XMRig](https://github.com/xmrig/xmrig) which adds the ability to remote control your XMRig instances via a Webfrontend and REST api. -This fork is based on XMRig and adds a "Command and Control" (C&C) server, a daemon to reload XMRigCCMiner on config changes and modifications in XMRig to send the current status to the C&C Server. -The modified version can also handle commands like "update config", "start/stop mining" or "restart/shutdown" which can be send from the C&C-Server. - -**AND MANY MORE** +XMRigCC is a [XMRig](https://github.com/xmrig/xmrig) fork which adds remote control and monitoring functions to XMRigCC miners. It lets you control your miners via a Dashboard or the REST api. +XMRigCC has a "Command and Control" (C&C) server part, a daemon to keep the XMRigCC miner alive and modifications to send the current status to the C&C Server. +The modified version can handle commands like "update config", "start/stop mining" or "restart/shutdown/reboot" which can be send from the C&C-Server Dashboard. +Assign config templates to multiple miners with a single click and let them switch configs without connecting to each of them. +Watch your miners logs with the simple remote Log viewer and monitor you miners. When the hashrate drops or one of your miners went offline you can get a notification via +PushOver or Telegram automatically so that you dont need to watch your miners all day. Full Windows/Linux compatible, and you can mix Linux and Windows miner on one XMRigCCServer. ## Additional features of XMRigCC (on top of XMRig) Check the [Coin Configuration](https://github.com/Bendr0id/xmrigCC/wiki/Coin-configurations) guide -* **NEW Support of Argon2-512 chukwa (TRTL) variant (algo: "argon2-512", variant "auto" or "chukwa")** -* **NEW Support of Argon2-256 wrkz variant (algo: "argon2-256", variant "auto" or "wrkz")** -* **NEW Support of Crytptonight Conceal variant (algo: "cryptonight", variant "conceal" or "ccx")** -* **NEW Support of Crytptonight-Extremelite (UPX 2) variant (algo: "cryptonight-extremelite", variant "auto" (autodetect) or "upx2")** -* **Support of Crytptonight R (XMR) variant (algo: "cryptonight", variant "auto" (autodetect) or "r")** -* **Support of Crytptonight WOW (Wownero) variant (algo: "cryptonight", variant "wow")** -* **Support of Crytptonight Reverse Waltz (Graft) variant (algo: "cryptonight", variant "rwz" (autodetect) or variant "graft")** -* **Support of Crytptonight Double/Heavyx (XCash) variant (algo: "cryptonight", variant "double")** -* **Support of Crytptonight Zelerius variant (algo: "cryptonight", variant "zls")** -* **Support of Crytptonight RTO/HOSP variant (algo: "cryptonight", variant "rto" or variant "hosp")** -* **Support of Crytptonight-Ultralite TRTL/Turtle variant (algo: "cryptonight-ultralite", variant "auto")** -* **Support of Crytptonight-Lite UPX/uPlexa variant (algo: "cryptonight-lite", variant "upx")** -* **Support of Crytptonight XTL v5/v9 PoW changes aka CN-FastV2 (algo: "cryptonight", variant: "xtl" (autodetect), "xtlv9" (force v9))** -* **Support of Crytptonight XFH/SWAP variant aka CN-Heavy-Fast** -* **Support of Crytptonight v8 PoW changes aka CNV2 (XMR fork on Block 1685555)** -* **Support of Crytptonight-Heavy BitTube (TUBE) v4 variant (fork on Block 110000)** -* **Support of Crytptonight Masari (MSR) v7 variant (use variant "msr" to be ready for the fork, with autodetect)** -* **Support of Crytptonight-Heavy Haven Protocol (XHV) v3 variant (use variant "xhv")** -* **Support of Crytptonight Stellite (XTL) v4 variant** -* **Support of Crytptonight Alloy (XAO) variant** -* **Support of Crytptonight-Lite IPBC/TUBE variant** -* **Support of Crytptonight-Heavy (Loki, Ryo, ...)** -* **Support of Crytptonight v7 PoW changes aka CNV1** -* **Support of Crytptonight-Lite v7 PoW changes aka CN-LiteV1** -* Full SSL/TLS support for the whole communication: [Howto](https://github.com/Bendr0id/xmrigCC/wiki/tls) - - XMRigCCServer Dashboard <-> Browser - - XMRigCCServer <-> XMRigMiner - - XMRigMiner <-> Pool +* **Support of UPX2 variant (algo: "cn-extremelite/upx2")** +* **Support of CN-Conceal variant (algo: "cn/conceal")** +* **Better performance for ARMv8 CPUs** +* Full SSL/TLS support +* NUMA support * Command and control server * CC Dashboard with: * statistics of all connected miners * remote control miners (start/stop/restart/shutdown) * remote configuration changes of miners - * simple config editor for miner / mass editor for multiple miners - * monitoring / offline notification -* Daemon around the miner to restart and apply config changes -* High optimized mining code ([Benchmarks](#benchmarks)) -* Working CPU affinity for NUMA Cores or CPU's with lots of cores -* Multihash support (Double, Triple, Quadruple, Quituple) -* Configuration of multihash per thread -* Smarter automatic CPU configuration -* It's still open source software :D + * simple config editor for miner / config templates + * monitoring / offline notification push notifications via Pushover and Telegram +* Daemon to restart the miner -**[Find Help/Howto](https://github.com/Bendr0id/xmrigCC/wiki/)** +**XMRigCC Miner** - -**XMRigCC Daemon(miner)** - -![Screenshot of XMRig Daemon (miner)](https://i.imgur.com/gYq1QSP.png) + **XMRigCC Server** -![Screenshot of XMRigCC Server](https://i.imgur.com/iS1RzgO.png) + **XMRigCC Dashboard** -![Screenshot of XMRigCC Dashboard](https://imgur.com/UrdTHpM.png) + #### Table of contents * [Download](#download) -* [Wiki/Building/Howto](https://github.com/Bendr0id/xmrigCC/wiki/) * [Usage](#usage) -* [Multihash factor](#multihash-multihash-factor) -* [Multihash thread Mask](#multihash-thread-mask-only-for-multihash-factor--1) +* [Wiki/Building/Howto](https://github.com/Bendr0id/xmrigCC/wiki/) * [Common Issues](#common-issues) -* [Optimizations](#cpu-mining-performance) -* [Benchmarks](#benchmarks) * [Donations](#donations) * [Contacts](#contact) ## Download * Binary releases: https://github.com/Bendr0id/xmrigCC/releases * Git tree: https://github.com/Bendr0id/xmrigCC.git - * Clone with `git clone https://github.com/Bendr0id/xmrigCC.git` :hammer: [Build instructions](https://github.com/Bendr0id/xmrigCC/wiki/Build-Debian%5CUbuntu). + * Clone with `git clone https://github.com/Bendr0id/xmrigCC.git` :hammer: [Build instructions](https://github.com/xmrig/xmrig/wiki/Build. ## Usage -### Basic example xmrigCCServer +### Basic example XMRigCCServer ``` xmrigCCServer --cc-port=3344 --cc-user=admin --cc-pass=pass --cc-access-token=SECRET_TOKEN_TO_ACCESS_CC_SERVER ``` -### Options xmrigCCServer +### Options XMRigCCServer ``` - --cc-user=USERNAME CC Server admin user - --cc-pass=PASSWORD CC Server admin pass - --cc-access-token=T CC Server access token for CC Client - --cc-port=N CC Server - --cc-use-tls enable tls encryption for CC communication - --cc-cert-file=FILE when tls is turned on, use this to point to the right cert file (default: server.pem) - --cc-key-file=FILE when tls is turned on, use this to point to the right key file (default: server.key) - --cc-client-config-folder=FOLDER Folder contains the client config files - --cc-custom-dashboard=FILE loads a custom dashboard and serve it to '/' - --cc-client-log-lines-history=N maximum lines of log history kept per miner (default: 100) - --no-color disable colored output - -S, --syslog use system log for output messages - -B, --background run the miner in the background - -c, --config=FILE load a JSON-format configuration file - -l, --log-file=FILE log all output to a file - -h, --help display this help and exit - -V, --version output version information and exit -``` - -Also you can use configuration via config file, default **[config_cc.json](https://github.com/Bendr0id/xmrigCC/wiki/Config-XMRigCCServer)**. You can load multiple config files and combine it with command line options. - - -### Basic example xmrigDaemon -``` -xmrigDaemon -o pool.minemonero.pro:5555 -u YOUR_WALLET -p x -k --cc-url=IP_OF_CC_SERVER:PORT --cc-access-token=SECRET_TOKEN_TO_ACCESS_CC_SERVER --cc-worker-id=OPTIONAL_WORKER_NAME -``` - -### Options xmrigDaemon -``` - -a, --algo=ALGO cryptonight (default), cryptonight-lite or cryptonight-heavy - -o, --url=URL URL of mining server - -O, --userpass=U:P username:password pair for mining server - -u, --user=USERNAME username for mining server - -p, --pass=PASSWORD password for mining server - -t, --threads=N number of miner threads - -A, --aesni=N selection of AES-NI mode (0 auto, 1 on, 2 off) - -k, --keepalive send keepalived for prevent timeout (need pool support) - -r, --retries=N number of times to retry before switch to backup server (default: 5) - -R, --retry-pause=N time to pause between retries (default: 5) - --pow-variant=V specificy the PoW variat to use: -> auto (default), 0 (v0), 1 (v1, aka monerov7, aeonv7), tube (ipbc), alloy, xtl (including autodetect for v5), msr, xhv, rto - for further help see: https://github.com/Bendr0id/xmrigCC/wiki/Coin-configurations - --multihash-factor=N number of hash blocks to process at a time (don't set or 0 enables automatic selection of optimal number of hash blocks) - --multihash-thread-mask=MASK limits multihash to given threads (mask), (default: all threads) - --cpu-affinity set process affinity to CPU core(s), mask 0x3 for cores 0 and 1 - --cpu-priority set process priority (0 idle, 2 normal to 5 highest) - --no-huge-pages disable huge pages support - --donate-level=N donate level, default 5% (5 minutes in 100 minutes) - --user-agent set custom user-agent string for pool - --max-cpu-usage=N maximum CPU usage for automatic threads mode (default 75) - --safe safe adjust threads and av settings for current CPU - --nicehash enable nicehash/xmrig-proxy support - --use-tls enable tls on pool communication - --print-time=N print hashrate report every N seconds - --api-port=N port for the miner API - --api-access-token=T access token for API - --api-worker-id=ID custom worker-id for API - --cc-url=URL url of the CC Server + --cc-user=USERNAME CC Server admin user + --cc-pass=PASSWORD CC Server admin pass + --cc-access-token=T CC Server access token for CC Client + --cc-port=N CC Server port --cc-use-tls enable tls encryption for CC communication - --cc-access-token=T access token for CC Server - --cc-worker-id=ID custom worker-id for CC Server - --cc-update-interval-s=N status update interval in seconds (default: 10 min: 1) - --cc-use-remote-logging enable remote logging on CC Server - --cc-upload-config-on-startup upload current miner config to CC Server on startup + --cc-cert-file=FILE when tls is turned on, use this to point to the right cert file (default: server.pem) + --cc-key-file=FILE when tls is turned on, use this to point to the right key file (default: server.key) + --cc-client-log-lines-history=N maximum lines of log history kept per miner (default: 100) + --cc-client-config-folder=FOLDER Folder contains the client config files + --cc-pushover-user-key your user key for pushover notifications + --cc-pushover-api-token api token/keytoken of the application for pushover notifications + --cc-telegram-bot-token your bot token for telegram notifications + --cc-telegram-chat-id your chat-id for telegram notifications + --cc-push-miner-offline-info push notification for offline miners and recover push + --cc-push-miner-zero-hash-info push notification when miner reports 0 hashrate and recover push + --cc-push-periodic-mining-status push periodic status notification (every hour) + --cc-custom-dashboard=FILE loads a custom dashboard and serve it to '/' --no-color disable colored output -S, --syslog use system log for output messages -B, --background run the miner in the background @@ -183,46 +101,73 @@ xmrigDaemon -o pool.minemonero.pro:5555 -u YOUR_WALLET -p x -k --cc-url=IP_OF_CC -V, --version output version information and exit ``` -Also you can use configuration via config file, default **[config.json](https://github.com/Bendr0id/xmrigCC/wiki/Config-XMRigDaemon)**. You can load multiple config files and combine it with command line options. - -## Multihash (multihash-factor) -With this option it is possible to increase the number of hashblocks calculated by a single thread in each round. -Selecting multihash-factors greater than 1 increases the L3 cache demands relative to the multihash-factor. -E.g. at multihash-factor 2, each Cryptonight thread requires 4MB and each Cryptonight-lite thread requires 2 MB of L3 cache. -With multihash-factor 3, they need 6MB or 3MB respectively. - -Setting multihash-factor to 0 will allow automatic detection of the optimal value. -Xmrig will then try to utilize as much of the L3 cache as possible for the selected number of threads. -If the threads parameter has been set to auto, Xmrig will detect the optimal number of threads first. -After that it finds the greatest possible multihash-factor. - -### Multihash for low power operation -Depending the CPU and its L3 caches, it can make sense to replace multiple single hash threads with single multi-hash counterparts. -This change might come at the price of a minor drop in effective hash-rate, yet it will also reduce heat production and power consumption of the used CPU. - -### Multihash for optimal CPU exploitation -In certain environments (e.g. vServer) the system running xmrig can have access to relatively large amounts of L3 cache, but may has access to only a few CPU cores. -In such cases, running xmrig with higher multihash-factors can lead to improvements. - - -## Multihash thread Mask (only for multihash-factor > 1) -With this option you can limit multihash to the given threads (mask). -This can significantly improve your hashrate by using unused l3 cache. -The default is to run the configured multihash-factor on all threads. - +### Basic example xmrigDaemon +``` +xmrigDaemon -o pool.minemonero.pro:5555 -u YOUR_WALLET -p x -k --cc-url=IP_OF_CC_SERVER:PORT --cc-access-token=SECRET_TOKEN_TO_ACCESS_CC_SERVER --cc-worker-id=OPTIONAL_WORKER_NAME ``` -{ -... -"multihash-factor":2, -"multihash-thread-mask":"0x5", // in binary -> 0101 -"threads": 4, - -... -} -``` -This will limit multihash mode (multihash-factor = 2) to thread 0 and thread 2, thread 1 and thread 3 will run in single hashmode. +### Options xmrigDaemon +``` + -a, --algo=ALGO specify the algorithm to use + cn/r, cn/2, cn/1, cn/0, cn/double, cn/half, cn/fast, + cn/rwz, cn/zls, cn/xao, cn/rto, cn/conceal, + cn-lite/1, + cn-heavy/xhv, cn-heavy/tube, cn-heavy/0, + cn-pico + cn-extremelite + argon2/chukwa, argon2/wrkz + rx/wow, rx/loki + -o, --url=URL URL of mining server + -O, --userpass=U:P username:password pair for mining server + -u, --user=USERNAME username for mining server + -p, --pass=PASSWORD password for mining server + --rig-id=ID rig identifier for pool-side statistics (needs pool support) + -t, --threads=N number of miner threads + -v, --av=N algorithm variation, 0 auto select + -k, --keepalive send keepalived packet for prevent timeout (needs pool support) + --nicehash enable nicehash.com support + --tls enable SSL/TLS support (needs pool support) + --tls-fingerprint=F pool TLS certificate fingerprint, if set enable strict certificate pinning + --daemon use daemon RPC instead of pool for solo mining + --daemon-poll-interval=N daemon poll interval in milliseconds (default: 1000) + -r, --retries=N number of times to retry before switch to backup server (default: 5) + -R, --retry-pause=N time to pause between retries (default: 5) + --cpu-affinity set process affinity to CPU core(s), mask 0x3 for cores 0 and 1 + --cpu-priority set process priority (0 idle, 2 normal to 5 highest) + --no-huge-pages disable huge pages support + --no-color disable colored output + --donate-level=N donate level, default 5% (5 minutes in 100 minutes) + --user-agent set custom user-agent string for pool + -B, --background run the miner in the background + -c, --config=FILE load a JSON-format configuration file + -l, --log-file=FILE log all output to a file + -S, --syslog use system log for output messages + --asm=ASM ASM optimizations, possible values: auto, none, intel, ryzen, bulldozer. + --print-time=N print hashrate report every N seconds + --api-worker-id=ID custom worker-id for API + --api-id=ID custom instance ID for API + --http-enabled enable HTTP API + --http-host=HOST bind host for HTTP API (default: 127.0.0.1) + --http-port=N bind port for HTTP API + --http-access-token=T access token for HTTP API + --http-no-restricted enable full remote access to HTTP API (only if access token set) + --randomx-init=N threads count to initialize RandomX dataset + --randomx-no-numa disable NUMA support for RandomX + --export-topology export hwloc topology to a XML file and exit + --cc-disabled disable CC Client feature + --cc-url=URL url of the CC Server + --cc-access-token=T access token for CC Server + --cc-worker-id=ID custom worker-id for CC Server + --cc-update-interval-s=N status update interval in seconds (default: 10 min: 1) + --cc-use-tls enable tls encryption for CC communication + --cc-use-remote-logging enable remote logging on CC Server + --cc-upload-config-on-start upload current miner config to CC Server on startup + --cc-reboot-cmd=CMD command/bat to execute to Reboot miner machine + --dry-run test configuration and exit + -h, --help display this help and exit + -V, --version output version information and exit +``` ## Common Issues @@ -239,62 +184,19 @@ This will limit multihash mode (multihash-factor = 2) to thread 0 and thread 2, ### HUGE PAGES unavailable (Windows) -* Run XMRig as Administrator. -* Since version 0.8.0 XMRig automatically enables SeLockMemoryPrivilege for current user, but reboot or sign out still required. [Manual instruction](https://msdn.microsoft.com/en-gb/library/ms190730.aspx). +* Run XMRigDaemon as Administrator. +* On Windows it automatically enables SeLockMemoryPrivilege for current user, but reboot or sign out still required. [Manual instruction](https://msdn.microsoft.com/en-gb/library/ms190730.aspx). ### HUGE PAGES unavailable (Linux) * Before starting XMRigDaemon set huge pages `sudo sysctl -w vm.nr_hugepages=128` - -## Other information -* No HTTP support, only stratum protocol support. - - -### CPU mining performance -Please note performance is highly dependent on system load. -The numbers above are obtained on an idle system. -Tasks heavily using a processor cache, such as video playback, can greatly degrade hashrate. -Optimal number of threads depends on the size of the L3 cache of a processor, 1 thread requires 4 MB (Cryptonight-Heavy), 2 MB (Cryptonight) or 1MB (Cryptonigh-Lite) of cache. - -### Maximum performance checklist -* Idle operating system. -* Do not exceed optimal thread count. -* Use modern CPUs with AES-NI instruction set. -* Try setup optimal cpu affinity. -* Try decreasing number of threads while increasing multihash-factor. - Allocate unused cores and L3 cache with the help of multihash-thread-mask. -* Enable fast memory (Large/Huge pages). - -## Benchmarks - -Here are some result reported by users. Feel free to share your results, i'll add them. - -### XMRigCC with max optimizations: - - * AMD Ryzen 1950x - - AEON: ~5300 h/s (XMRig Stock: ~4900 h/s) - XMR: ~1320 h/s (XMRig Stock: ~1200 h/s) - - * AMD Ryzen 1600 - - AEON: ~2065 h/s (XMRig Stock: ~1800 h/s) - XMR: ~565 h/s (XMRig Stock: ~460 h/s) - - * 4x Intel XEON e7-4820 - - AEON: ~2500 h/s (XMRig Stock ~2200h/s) - - * 2x Intel XEON 2x e5-2670 - - AEON: ~3300 h/s (XMRig Stock ~2500h/s) ## Donations * Default donation 5% (5 minutes in 100 minutes) can be reduced to 1% via command line option `--donate-level`. -##### BenDroid (xmrigCC): +##### BenDroid (XMRigCC): XMR: `4BEn3sSa2SsHBcwa9dNdKnGvvbyHPABr2JzoY7omn7DA2hPv84pVFvwDrcwMCWgz3dQVcrkw3gE9aTC9Mi5HxzkfF9ev1eH` AEON: `Wmtm4S2cQ8uEBBAVjvbiaVAPv2d6gA1mAUmBmjna4VF7VixLxLRUYag5cvsym3WnuzdJ9zvhQ3Xwa8gWxPDPRfcQ3AUkYra3W` diff --git a/appveyor.yml b/appveyor.yml index 107144fa..b8b98ad6 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -25,7 +25,7 @@ build_script: - mkdir build - cd build - set CMAKE_PREFIX_PATH=c:\xmrigCC-deps\msvc2017\libuv\x64\;c:\xmrigCC-deps\msvc2017\libmicrohttpd\x64\;c:\xmrigCC-deps\msvc2017\openssl\x64\ - - cmake -G "Visual Studio 15 2017 Win64" -T v141,host=x64 .. -DBOOST_ROOT=C:\Libraries\boost_1_66_0 + - cmake -G "Visual Studio 15 2017 Win64" -T v141,host=x64 .. - msbuild xmrig.sln /p:Configuration=Release after_build: diff --git a/cmake/FindHWLOC.cmake b/cmake/FindHWLOC.cmake new file mode 100644 index 00000000..55309d3e --- /dev/null +++ b/cmake/FindHWLOC.cmake @@ -0,0 +1,25 @@ +find_path( + HWLOC_INCLUDE_DIR + NAMES hwloc.h + PATHS "${XMRIG_DEPS}" ENV "XMRIG_DEPS" + PATH_SUFFIXES "include" + NO_DEFAULT_PATH +) + +find_path(HWLOC_INCLUDE_DIR NAMES hwloc.h) + +find_library( + HWLOC_LIBRARY + NAMES hwloc.a hwloc libhwloc + PATHS "${XMRIG_DEPS}" ENV "XMRIG_DEPS" + PATH_SUFFIXES "lib" + NO_DEFAULT_PATH +) + +find_library(HWLOC_LIBRARY NAMES hwloc.a hwloc libhwloc) + +set(HWLOC_LIBRARIES ${HWLOC_LIBRARY}) +set(HWLOC_INCLUDE_DIRS ${HWLOC_INCLUDE_DIR}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(HWLOC DEFAULT_MSG HWLOC_LIBRARY HWLOC_INCLUDE_DIR) diff --git a/cmake/FindMHD.cmake b/cmake/FindMHD.cmake deleted file mode 100644 index f6665892..00000000 --- a/cmake/FindMHD.cmake +++ /dev/null @@ -1,39 +0,0 @@ -# - Try to find MHD -# Once done this will define -# -# MHD_FOUND - system has MHD -# MHD_INCLUDE_DIRS - the MHD include directory -# MHD_LIBRARY - Link these to use MHD - -find_path( - MHD_INCLUDE_DIR - NAMES microhttpd.h - DOC "microhttpd include dir" -) - -find_library( - MHD_LIBRARY - NAMES microhttpd microhttpd-10 libmicrohttpd libmicrohttpd-dll - DOC "microhttpd library" -) - -set(MHD_INCLUDE_DIRS ${MHD_INCLUDE_DIR}) -set(MHD_LIBRARIES ${MHD_LIBRARY}) - -# debug library on windows -# same naming convention as in qt (appending debug library with d) -# boost is using the same "hack" as us with "optimized" and "debug" -# official MHD project actually uses _d suffix -if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL MSVC) - find_library( - MHD_LIBRARY_DEBUG - NAMES microhttpd_d microhttpd-10_d libmicrohttpd_d libmicrohttpd-dll_d - DOC "mhd debug library" - ) - set(MHD_LIBRARIES optimized ${MHD_LIBRARIES} debug ${MHD_LIBRARY_DEBUG}) -endif() - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(mhd DEFAULT_MSG MHD_INCLUDE_DIR MHD_LIBRARY) -mark_as_advanced(MHD_INCLUDE_DIR MHD_LIBRARY) - diff --git a/cmake/FindUV.cmake b/cmake/FindUV.cmake index e3c22d28..ba59d1d3 100644 --- a/cmake/FindUV.cmake +++ b/cmake/FindUV.cmake @@ -1,5 +1,22 @@ +find_path( + UV_INCLUDE_DIR + NAMES uv.h + PATHS "${XMRIG_DEPS}" ENV "XMRIG_DEPS" + PATH_SUFFIXES "include" + NO_DEFAULT_PATH +) + find_path(UV_INCLUDE_DIR NAMES uv.h) -find_library(UV_LIBRARY NAMES uv libuv) + +find_library( + UV_LIBRARY + NAMES libuv.a uv libuv + PATHS "${XMRIG_DEPS}" ENV "XMRIG_DEPS" + PATH_SUFFIXES "lib" + NO_DEFAULT_PATH +) + +find_library(UV_LIBRARY NAMES libuv.a uv libuv) set(UV_LIBRARIES ${UV_LIBRARY}) set(UV_INCLUDE_DIRS ${UV_INCLUDE_DIR}) diff --git a/cmake/OpenSSL.cmake b/cmake/OpenSSL.cmake new file mode 100644 index 00000000..ea46081c --- /dev/null +++ b/cmake/OpenSSL.cmake @@ -0,0 +1,31 @@ +if (WITH_TLS) + set(OPENSSL_ROOT_DIR ${XMRIG_DEPS}) + + if (WIN32) + set(OPENSSL_USE_STATIC_LIBS TRUE) + set(OPENSSL_MSVC_STATIC_RT TRUE) + + set(EXTRA_LIBS ${EXTRA_LIBS} Crypt32) + endif() + + find_package(OpenSSL) + + if (OPENSSL_FOUND) + set(TLS_SOURCES src/base/net/stratum/Tls.h src/base/net/stratum/Tls.cpp) + include_directories(${OPENSSL_INCLUDE_DIR}) + + if (WITH_HTTP) + set(TLS_SOURCES ${TLS_SOURCES} src/base/net/http/HttpsClient.h src/base/net/http/HttpsClient.cpp) + endif() + else() + message(FATAL_ERROR "OpenSSL NOT found: use `-DWITH_TLS=OFF` to build without TLS support") + endif() + + add_definitions(/DXMRIG_FEATURE_TLS) +else() + set(TLS_SOURCES "") + set(OPENSSL_LIBRARIES "") + remove_definitions(/DXMRIG_FEATURE_TLS) + + set(CMAKE_PROJECT_NAME "${CMAKE_PROJECT_NAME}-notls") +endif() diff --git a/cmake/argon2.cmake b/cmake/argon2.cmake new file mode 100644 index 00000000..e82cd389 --- /dev/null +++ b/cmake/argon2.cmake @@ -0,0 +1,18 @@ +if (WITH_ARGON2) + add_definitions(/DXMRIG_ALGO_ARGON2) + + list(APPEND HEADERS_CRYPTO + src/crypto/argon2/Hash.h + src/crypto/argon2/Impl.h + ) + + list(APPEND SOURCES_CRYPTO + src/crypto/argon2/Impl.cpp + ) + + add_subdirectory(src/3rdparty/argon2) + set(ARGON2_LIBRARY argon2) +else() + remove_definitions(/DXMRIG_ALGO_ARGON2) + set(ARGON2_LIBRARY "") +endif() diff --git a/cmake/asm.cmake b/cmake/asm.cmake index 8b11e2ee..e445defd 100644 --- a/cmake/asm.cmake +++ b/cmake/asm.cmake @@ -1,179 +1,52 @@ -message("Generating ASM files") +if (WITH_ASM AND NOT XMRIG_ARM AND CMAKE_SIZEOF_VOID_P EQUAL 8) + set(XMRIG_ASM_LIBRARY "xmrig-asm") -# CN v1 original -set(ALGO "original") -set(ITERATIONS "524288") #0x80000 -set(MASK "2097136") #0x1FFFF0 + if (CMAKE_C_COMPILER_ID MATCHES MSVC) + enable_language(ASM_MASM) -configure_file("src/crypto/asm/cnv1_main_loop_sandybridge.inc.in" "src/crypto/asm/cnv1_main_loop_sandybridge.inc") -configure_file("src/crypto/asm/cnv1_main_loop_soft_aes_sandybridge.inc.in" "src/crypto/asm/cnv1_main_loop_soft_aes_sandybridge.inc") + if (MSVC_TOOLSET_VERSION GREATER_EQUAL 141) + set(XMRIG_ASM_FILES + "src/crypto/cn/asm/cn_main_loop.asm" + "src/crypto/cn/asm/CryptonightR_template.asm" + ) + else() + set(XMRIG_ASM_FILES + "src/crypto/cn/asm/win64/cn_main_loop.asm" + "src/crypto/cn/asm/win64/CryptonightR_template.asm" + ) + endif() -configure_file("src/crypto/asm/win/cnv1_main_loop_sandybridge.inc.in" "src/crypto/asm/win/cnv1_main_loop_sandybridge.inc") -configure_file("src/crypto/asm/win/cnv1_main_loop_soft_aes_sandybridge.inc.in" "src/crypto/asm/win/cnv1_main_loop_soft_aes_sandybridge.inc") - -# CN v2 ORIGINAL -set(ALGO "originalv2") -set(ITERATIONS "524288") #0x80000 -set(MASK "2097136") #0x1FFFF0 - -configure_file("src/crypto/asm/cnv2_main_loop_ivybridge.inc.in" "src/crypto/asm/cnv2_main_loop_ivybridge.inc") -configure_file("src/crypto/asm/cnv2_main_loop_bulldozer.inc.in" "src/crypto/asm/cnv2_main_loop_bulldozer.inc") -configure_file("src/crypto/asm/cnv2_main_loop_ryzen.inc.in" "src/crypto/asm/cnv2_main_loop_ryzen.inc") -configure_file("src/crypto/asm/cnv2_double_main_loop_sandybridge.inc.in" "src/crypto/asm/cnv2_double_main_loop_sandybridge.inc") -configure_file("src/crypto/asm/cnv2_main_loop_soft_aes_sandybridge.inc.in" "src/crypto/asm/cnv2_main_loop_soft_aes_sandybridge.inc") - -configure_file("src/crypto/asm/win/cnv2_main_loop_ivybridge.inc.in" "src/crypto/asm/win/cnv2_main_loop_ivybridge.inc") -configure_file("src/crypto/asm/win/cnv2_main_loop_bulldozer.inc.in" "src/crypto/asm/win/cnv2_main_loop_bulldozer.inc") -configure_file("src/crypto/asm/win/cnv2_main_loop_ryzen.inc.in" "src/crypto/asm/win/cnv2_main_loop_ryzen.inc") -configure_file("src/crypto/asm/win/cnv2_double_main_loop_sandybridge.inc.in" "src/crypto/asm/win/cnv2_double_main_loop_sandybridge.inc") -configure_file("src/crypto/asm/win/cnv2_main_loop_soft_aes_sandybridge.inc.in" "src/crypto/asm/win/cnv2_main_loop_soft_aes_sandybridge.inc") - -# CN v1 FAST -set(ALGO "fast") -set(ITERATIONS "262144") #0x40000 -set(MASK "2097136") #0x1FFFF0 - -configure_file("src/crypto/asm/cnv1_main_loop_sandybridge.inc.in" "src/crypto/asm/cnv1_main_loop_fast_sandybridge.inc") -configure_file("src/crypto/asm/cnv1_main_loop_soft_aes_sandybridge.inc.in" "src/crypto/asm/cnv1_main_loop_fast_soft_aes_sandybridge.inc") - -configure_file("src/crypto/asm/win/cnv1_main_loop_sandybridge.inc.in" "src/crypto/asm/win/cnv1_main_loop_fast_sandybridge.inc") -configure_file("src/crypto/asm/win/cnv1_main_loop_soft_aes_sandybridge.inc.in" "src/crypto/asm/win/cnv1_main_loop_fast_soft_aes_sandybridge.inc") - -# CN v2 FAST -set(ALGO "fastv2") -set(ITERATIONS "262144") #0x40000 -set(MASK "2097136") #0x1FFFF0 - -configure_file("src/crypto/asm/cnv2_main_loop_ivybridge.inc.in" "src/crypto/asm/cnv2_main_loop_fastv2_ivybridge.inc") -configure_file("src/crypto/asm/cnv2_main_loop_bulldozer.inc.in" "src/crypto/asm/cnv2_main_loop_fastv2_bulldozer.inc") -configure_file("src/crypto/asm/cnv2_main_loop_ryzen.inc.in" "src/crypto/asm/cnv2_main_loop_fastv2_ryzen.inc") -configure_file("src/crypto/asm/cnv2_double_main_loop_sandybridge.inc.in" "src/crypto/asm/cnv2_double_main_loop_fastv2_sandybridge.inc") -configure_file("src/crypto/asm/cnv2_main_loop_soft_aes_sandybridge.inc.in" "src/crypto/asm/cnv2_main_loop_fastv2_soft_aes_sandybridge.inc") - -configure_file("src/crypto/asm/win/cnv2_main_loop_ivybridge.inc.in" "src/crypto/asm/win/cnv2_main_loop_fastv2_ivybridge.inc") -configure_file("src/crypto/asm/win/cnv2_main_loop_bulldozer.inc.in" "src/crypto/asm/win/cnv2_main_loop_fastv2_bulldozer.inc") -configure_file("src/crypto/asm/win/cnv2_main_loop_ryzen.inc.in" "src/crypto/asm/win/cnv2_main_loop_fastv2_ryzen.inc") -configure_file("src/crypto/asm/win/cnv2_double_main_loop_sandybridge.inc.in" "src/crypto/asm/win/cnv2_double_main_loop_fastv2_sandybridge.inc") -configure_file("src/crypto/asm/win/cnv2_main_loop_soft_aes_sandybridge.inc.in" "src/crypto/asm/win/cnv2_main_loop_fastv2_soft_aes_sandybridge.inc") - -# CN XCASH -set(ALGO "xcash") -set(ITERATIONS "1048576") #0x100000 -set(MASK "2097136") #0x1FFFF0 - -configure_file("src/crypto/asm/cnv2_main_loop_ivybridge.inc.in" "src/crypto/asm/cnv2_main_loop_xcash_ivybridge.inc") -configure_file("src/crypto/asm/cnv2_main_loop_bulldozer.inc.in" "src/crypto/asm/cnv2_main_loop_xcash_bulldozer.inc") -configure_file("src/crypto/asm/cnv2_main_loop_ryzen.inc.in" "src/crypto/asm/cnv2_main_loop_xcash_ryzen.inc") -configure_file("src/crypto/asm/cnv2_double_main_loop_sandybridge.inc.in" "src/crypto/asm/cnv2_double_main_loop_xcash_sandybridge.inc") -configure_file("src/crypto/asm/cnv2_main_loop_soft_aes_sandybridge.inc.in" "src/crypto/asm/cnv2_main_loop_xcash_soft_aes_sandybridge.inc") - -configure_file("src/crypto/asm/win/cnv2_main_loop_ivybridge.inc.in" "src/crypto/asm/win/cnv2_main_loop_xcash_ivybridge.inc") -configure_file("src/crypto/asm/win/cnv2_main_loop_bulldozer.inc.in" "src/crypto/asm/win/cnv2_main_loop_xcash_bulldozer.inc") -configure_file("src/crypto/asm/win/cnv2_main_loop_ryzen.inc.in" "src/crypto/asm/win/cnv2_main_loop_xcash_ryzen.inc") -configure_file("src/crypto/asm/win/cnv2_double_main_loop_sandybridge.inc.in" "src/crypto/asm/win/cnv2_double_main_loop_xcash_sandybridge.inc") -configure_file("src/crypto/asm/win/cnv2_main_loop_soft_aes_sandybridge.inc.in" "src/crypto/asm/win/cnv2_main_loop_xcash_soft_aes_sandybridge.inc") - -# CN ZELERIUS -set(ALGO "zelerius") -set(ITERATIONS "393216") #0x60000 -set(MASK "2097136") #0x1FFFF0 - -configure_file("src/crypto/asm/cnv2_main_loop_ivybridge.inc.in" "src/crypto/asm/cnv2_main_loop_zelerius_ivybridge.inc") -configure_file("src/crypto/asm/cnv2_main_loop_bulldozer.inc.in" "src/crypto/asm/cnv2_main_loop_zelerius_bulldozer.inc") -configure_file("src/crypto/asm/cnv2_main_loop_ryzen.inc.in" "src/crypto/asm/cnv2_main_loop_zelerius_ryzen.inc") -configure_file("src/crypto/asm/cnv2_double_main_loop_sandybridge.inc.in" "src/crypto/asm/cnv2_double_main_loop_zelerius_sandybridge.inc") -configure_file("src/crypto/asm/cnv2_main_loop_soft_aes_sandybridge.inc.in" "src/crypto/asm/cnv2_main_loop_zelerius_soft_aes_sandybridge.inc") - -configure_file("src/crypto/asm/win/cnv2_main_loop_ivybridge.inc.in" "src/crypto/asm/win/cnv2_main_loop_zelerius_ivybridge.inc") -configure_file("src/crypto/asm/win/cnv2_main_loop_bulldozer.inc.in" "src/crypto/asm/win/cnv2_main_loop_zelerius_bulldozer.inc") -configure_file("src/crypto/asm/win/cnv2_main_loop_ryzen.inc.in" "src/crypto/asm/win/cnv2_main_loop_zelerius_ryzen.inc") -configure_file("src/crypto/asm/win/cnv2_double_main_loop_sandybridge.inc.in" "src/crypto/asm/win/cnv2_double_main_loop_zelerius_sandybridge.inc") -configure_file("src/crypto/asm/win/cnv2_main_loop_soft_aes_sandybridge.inc.in" "src/crypto/asm/win/cnv2_main_loop_zelerius_soft_aes_sandybridge.inc") - -# CN LITE - -set(ALGO "lite") -set(ITERATIONS "262144") #0x40000 -set(MASK "1048560") #0xFFFF0 - -configure_file("src/crypto/asm/cnv1_main_loop_sandybridge.inc.in" "src/crypto/asm/cnv1_main_loop_lite_sandybridge.inc") -configure_file("src/crypto/asm/cnv1_main_loop_soft_aes_sandybridge.inc.in" "src/crypto/asm/cnv1_main_loop_lite_soft_aes_sandybridge.inc") - -configure_file("src/crypto/asm/win/cnv1_main_loop_sandybridge.inc.in" "src/crypto/asm/win/cnv1_main_loop_lite_sandybridge.inc") -configure_file("src/crypto/asm/win/cnv1_main_loop_soft_aes_sandybridge.inc.in" "src/crypto/asm/win/cnv1_main_loop_lite_soft_aes_sandybridge.inc") - -# CN UPX - -set(ALGO "upx") -set(ITERATIONS "131072") #0x20000 -set(MASK "1048560") #0xFFFF0 - -configure_file("src/crypto/asm/cnv1_main_loop_sandybridge.inc.in" "src/crypto/asm/cnv1_main_loop_upx_sandybridge.inc") -configure_file("src/crypto/asm/cnv1_main_loop_soft_aes_sandybridge.inc.in" "src/crypto/asm/cnv1_main_loop_upx_soft_aes_sandybridge.inc") - -configure_file("src/crypto/asm/win/cnv1_main_loop_sandybridge.inc.in" "src/crypto/asm/win/cnv1_main_loop_upx_sandybridge.inc") -configure_file("src/crypto/asm/win/cnv1_main_loop_soft_aes_sandybridge.inc.in" "src/crypto/asm/win/cnv1_main_loop_upx_soft_aes_sandybridge.inc") - -# CN V2 ULTRALITE -set(ALGO "ultralite") -set(ITERATIONS "65536") #0x10000 -set(MASK "131056") #0x1FFF0 - -configure_file("src/crypto/asm/cnv2_main_loop_ivybridge.inc.in" "src/crypto/asm/cnv2_main_loop_ultralite_ivybridge.inc") -configure_file("src/crypto/asm/cnv2_main_loop_bulldozer.inc.in" "src/crypto/asm/cnv2_main_loop_ultralite_bulldozer.inc") -configure_file("src/crypto/asm/cnv2_main_loop_ryzen.inc.in" "src/crypto/asm/cnv2_main_loop_ultralite_ryzen.inc") -configure_file("src/crypto/asm/cnv2_double_main_loop_sandybridge.inc.in" "src/crypto/asm/cnv2_double_main_loop_ultralite_sandybridge.inc") -configure_file("src/crypto/asm/cnv2_main_loop_soft_aes_sandybridge.inc.in" "src/crypto/asm/cnv2_main_loop_ultralite_soft_aes_sandybridge.inc") - -configure_file("src/crypto/asm/win/cnv2_main_loop_ivybridge.inc.in" "src/crypto/asm/win/cnv2_main_loop_ultralite_ivybridge.inc") -configure_file("src/crypto/asm/win/cnv2_main_loop_bulldozer.inc.in" "src/crypto/asm/win/cnv2_main_loop_ultralite_bulldozer.inc") -configure_file("src/crypto/asm/win/cnv2_main_loop_ryzen.inc.in" "src/crypto/asm/win/cnv2_main_loop_ultralite_ryzen.inc") -configure_file("src/crypto/asm/win/cnv2_double_main_loop_sandybridge.inc.in" "src/crypto/asm/win/cnv2_double_main_loop_ultralite_sandybridge.inc") -configure_file("src/crypto/asm/win/cnv2_main_loop_soft_aes_sandybridge.inc.in" "src/crypto/asm/win/cnv2_main_loop_ultralite_soft_aes_sandybridge.inc") - -# CN V2 RWZ -set(ALGO "original") -set(ITERATIONS "393216") #0x60000 -set(MASK "2097136") #0x1FFFF0 - -configure_file("src/crypto/asm/cnv2_main_loop_rwz_all.inc.in" "src/crypto/asm/cnv2_main_loop_rwz_original_all.inc") -configure_file("src/crypto/asm/cnv2_double_main_loop_rwz_all.inc.in" "src/crypto/asm/cnv2_double_main_loop_rwz_original_all.inc") - -configure_file("src/crypto/asm/win/cnv2_main_loop_rwz_all.inc.in" "src/crypto/asm/win/cnv2_main_loop_rwz_original_all.inc") -configure_file("src/crypto/asm/win/cnv2_double_main_loop_rwz_all.inc.in" "src/crypto/asm/win/cnv2_double_main_loop_rwz_original_all.inc") - - -# CN V2 UPX2 -set(ALGO "upx2") -set(ITERATIONS "16384") #0x4000 -set(MASK "131056") #0x1FFF0 - -configure_file("src/crypto/asm/cnv2_main_loop_rwz_all.inc.in" "src/crypto/asm/cnv2_main_loop_rwz_upx2_all.inc") -configure_file("src/crypto/asm/cnv2_double_main_loop_rwz_all.inc.in" "src/crypto/asm/cnv2_double_main_loop_rwz_upx2_all.inc") - -configure_file("src/crypto/asm/win/cnv2_main_loop_rwz_all.inc.in" "src/crypto/asm/win/cnv2_main_loop_rwz_upx2_all.inc") -configure_file("src/crypto/asm/win/cnv2_double_main_loop_rwz_all.inc.in" "src/crypto/asm/win/cnv2_double_main_loop_rwz_upx2_all.inc") - -if (CMAKE_C_COMPILER_ID MATCHES MSVC) - enable_language(ASM_MASM) - set(XMRIG_ASM_FILE "src/crypto/asm/win/cn_main_loop.asm" - "src/crypto/asm/win/CryptonightR_template.asm") - set_property(SOURCE ${XMRIG_ASM_FILE} PROPERTY ASM_MASM) - include_directories(${CMAKE_BINARY_DIR}/src/crypto/asm/win) -else() - enable_language(ASM) - - if (WIN32 AND CMAKE_C_COMPILER_ID MATCHES GNU) - set(XMRIG_ASM_FILE "src/crypto/asm/win/cn_main_loop_win_gcc.S" - "src/crypto/asm/win/CryptonightR_template.S") + set_property(SOURCE ${XMRIG_ASM_FILES} PROPERTY ASM_MASM) else() - set(XMRIG_ASM_FILE "src/crypto/asm/cn_main_loop.S" - "src/crypto/asm/CryptonightR_template.S") + enable_language(ASM) + + if (WIN32 AND CMAKE_C_COMPILER_ID MATCHES GNU) + set(XMRIG_ASM_FILES + "src/crypto/cn/asm/win64/cn_main_loop.S" + "src/crypto/cn/asm/CryptonightR_template.S" + ) + else() + set(XMRIG_ASM_FILES + "src/crypto/cn/asm/cn_main_loop.S" + "src/crypto/cn/asm/CryptonightR_template.S" + ) + endif() + + set_property(SOURCE ${XMRIG_ASM_FILES} PROPERTY C) endif() - set_property(SOURCE ${XMRIG_ASM_FILE} PROPERTY C) - include_directories(${CMAKE_BINARY_DIR}/src/crypto/asm/) -endif() + add_library(${XMRIG_ASM_LIBRARY} STATIC ${XMRIG_ASM_FILES}) + set(XMRIG_ASM_SOURCES + src/crypto/common/Assembly.h + src/crypto/common/Assembly.cpp + src/crypto/cn/r/CryptonightR_gen.cpp + ) + set_property(TARGET ${XMRIG_ASM_LIBRARY} PROPERTY LINKER_LANGUAGE C) -add_library(xmrig_asm STATIC ${XMRIG_ASM_FILE}) -set_property(TARGET xmrig_asm PROPERTY LINKER_LANGUAGE C) \ No newline at end of file + add_definitions(/DXMRIG_FEATURE_ASM) +else() + set(XMRIG_ASM_SOURCES "") + set(XMRIG_ASM_LIBRARY "") + + remove_definitions(/DXMRIG_FEATURE_ASM) +endif() diff --git a/cmake/cn-gpu.cmake b/cmake/cn-gpu.cmake new file mode 100644 index 00000000..7aa489e6 --- /dev/null +++ b/cmake/cn-gpu.cmake @@ -0,0 +1,25 @@ +if (WITH_CN_GPU AND CMAKE_SIZEOF_VOID_P EQUAL 8) + + if (XMRIG_ARM) + set(CN_GPU_SOURCES src/crypto/cn/gpu/cn_gpu_arm.cpp) + + if (CMAKE_CXX_COMPILER_ID MATCHES GNU OR CMAKE_CXX_COMPILER_ID MATCHES Clang) + set_source_files_properties(src/crypto/cn/gpu/cn_gpu_arm.cpp PROPERTIES COMPILE_FLAGS "-O3") + endif() + else() + set(CN_GPU_SOURCES src/crypto/cn/gpu/cn_gpu_avx.cpp src/crypto/cn/gpu/cn_gpu_ssse3.cpp) + + if (CMAKE_CXX_COMPILER_ID MATCHES GNU OR CMAKE_CXX_COMPILER_ID MATCHES Clang) + set_source_files_properties(src/crypto/cn/gpu/cn_gpu_avx.cpp PROPERTIES COMPILE_FLAGS "-O3 -mavx2") + set_source_files_properties(src/crypto/cn/gpu/cn_gpu_ssse3.cpp PROPERTIES COMPILE_FLAGS "-O3") + elseif (CMAKE_CXX_COMPILER_ID MATCHES MSVC) + set_source_files_properties(src/crypto/cn/gpu/cn_gpu_avx.cpp PROPERTIES COMPILE_FLAGS "/arch:AVX") + endif() + endif() + + add_definitions(/DXMRIG_ALGO_CN_GPU) +else() + set(CN_GPU_SOURCES "") + + remove_definitions(/DXMRIG_ALGO_CN_GPU) +endif() diff --git a/cmake/cpu.cmake b/cmake/cpu.cmake index 96e61e2b..2fdebad8 100644 --- a/cmake/cpu.cmake +++ b/cmake/cpu.cmake @@ -7,19 +7,37 @@ if (CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86_64|AMD64)$") add_definitions(/DRAPIDJSON_SSE2) endif() - -if (CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64)$") - set(XMRIG_ARM ON) - set(XMRIG_ARMv8 ON) - set(WITH_LIBCPUID OFF) - - add_definitions(/DXMRIG_ARM) - add_definitions(/DXMRIG_ARMv8) -elseif (CMAKE_SYSTEM_PROCESSOR MATCHES "^(armv7|armv7f|armv7s|armv7k|armv7-a|armv7l)$") - set(XMRIG_ARM ON) - set(XMRIG_ARMv7 ON) - set(WITH_LIBCPUID OFF) - - add_definitions(/DXMRIG_ARM) - add_definitions(/DXMRIG_ARMv7) +if (NOT ARM_TARGET) + if (CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64|arm64|armv8-a)$") + set(ARM_TARGET 8) + elseif (CMAKE_SYSTEM_PROCESSOR MATCHES "^(armv7|armv7f|armv7s|armv7k|armv7-a|armv7l)$") + set(ARM_TARGET 7) + endif() +endif() + +if (ARM_TARGET AND ARM_TARGET GREATER 6) + set(XMRIG_ARM ON) + set(WITH_LIBCPUID OFF) + add_definitions(/DXMRIG_ARM) + + message(STATUS "Use ARM_TARGET=${ARM_TARGET} (${CMAKE_SYSTEM_PROCESSOR})") + + include(CheckCXXCompilerFlag) + + if (ARM_TARGET EQUAL 8) + set(XMRIG_ARMv8 ON) + add_definitions(/DXMRIG_ARMv8) + + CHECK_CXX_COMPILER_FLAG(-march=armv8-a+crypto XMRIG_ARM_CRYPTO) + + if (XMRIG_ARM_CRYPTO) + add_definitions(/DXMRIG_ARM_CRYPTO) + set(ARM8_CXX_FLAGS "-march=armv8-a+crypto") + else() + set(ARM8_CXX_FLAGS "-march=armv8-a") + endif() + elseif (ARM_TARGET EQUAL 7) + set(XMRIG_ARMv7 ON) + add_definitions(/DXMRIG_ARMv7) + endif() endif() diff --git a/cmake/flags.cmake b/cmake/flags.cmake index ceabcdc6..bc441dd0 100644 --- a/cmake/flags.cmake +++ b/cmake/flags.cmake @@ -6,6 +6,10 @@ if ("${CMAKE_BUILD_TYPE}" STREQUAL "") set(CMAKE_BUILD_TYPE Release) endif() +if (CMAKE_BUILD_TYPE STREQUAL "Release") + add_definitions(/DNDEBUG) +endif() + include(CheckSymbolExists) if (CMAKE_CXX_COMPILER_ID MATCHES GNU) @@ -13,23 +17,28 @@ if (CMAKE_CXX_COMPILER_ID MATCHES GNU) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wno-strict-aliasing") set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -Ofast") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fno-exceptions -fno-rtti -Wno-class-memaccess") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fexceptions -fno-rtti -Wno-class-memaccess") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Ofast -s") if (XMRIG_ARMv8) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=armv8-a+crypto") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=armv8-a+crypto -flax-vector-conversions") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${ARM8_CXX_FLAGS}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${ARM8_CXX_FLAGS} -flax-vector-conversions") elseif (XMRIG_ARMv7) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpu=neon") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfpu=neon -flax-vector-conversions") else() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -maes") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -maes") + add_definitions(/DHAVE_ROTR) endif() if (WIN32) - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static") + if (CMAKE_SIZEOF_VOID_P EQUAL 8) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static") + else() + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static -Wl,--large-address-aware") + endif() else() set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libgcc -static-libstdc++") endif() @@ -56,12 +65,12 @@ elseif (CMAKE_CXX_COMPILER_ID MATCHES Clang) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -Ofast -funroll-loops -fmerge-all-constants") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fno-exceptions -fno-rtti -Wno-missing-braces") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fexceptions -fno-rtti -Wno-missing-braces") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Ofast -funroll-loops -fmerge-all-constants") if (XMRIG_ARMv8) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=armv8-a+crypto") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=armv8-a+crypto") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${ARM8_CXX_FLAGS}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${ARM8_CXX_FLAGS}") elseif (XMRIG_ARMv7) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpu=neon -march=${CMAKE_SYSTEM_PROCESSOR}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfpu=neon -march=${CMAKE_SYSTEM_PROCESSOR}") @@ -76,3 +85,10 @@ elseif (CMAKE_CXX_COMPILER_ID MATCHES Clang) endif() endif() + +if (NOT WIN32) + check_symbol_exists("__builtin___clear_cache" "stdlib.h" HAVE_BUILTIN_CLEAR_CACHE) + if (HAVE_BUILTIN_CLEAR_CACHE) + add_definitions(/DHAVE_BUILTIN_CLEAR_CACHE) + endif() +endif() diff --git a/cmake/randomx.cmake b/cmake/randomx.cmake new file mode 100644 index 00000000..23f66b29 --- /dev/null +++ b/cmake/randomx.cmake @@ -0,0 +1,57 @@ +if (WITH_RANDOMX) + add_definitions(/DXMRIG_ALGO_RANDOMX) + + list(APPEND HEADERS_CRYPTO + src/crypto/rx/Rx.h + src/crypto/rx/RxAlgo.h + src/crypto/rx/RxCache.h + src/crypto/rx/RxConfig.h + src/crypto/rx/RxDataset.h + src/crypto/rx/RxVm.h + ) + + list(APPEND SOURCES_CRYPTO + src/crypto/randomx/aes_hash.cpp + src/crypto/randomx/allocator.cpp + src/crypto/randomx/argon2_core.c + src/crypto/randomx/argon2_ref.c + src/crypto/randomx/blake2_generator.cpp + src/crypto/randomx/blake2/blake2b.c + src/crypto/randomx/bytecode_machine.cpp + src/crypto/randomx/dataset.cpp + src/crypto/randomx/instructions_portable.cpp + src/crypto/randomx/randomx.cpp + src/crypto/randomx/reciprocal.c + src/crypto/randomx/soft_aes.cpp + src/crypto/randomx/superscalar.cpp + src/crypto/randomx/virtual_machine.cpp + src/crypto/randomx/virtual_memory.cpp + src/crypto/randomx/vm_compiled_light.cpp + src/crypto/randomx/vm_compiled.cpp + src/crypto/randomx/vm_interpreted_light.cpp + src/crypto/randomx/vm_interpreted.cpp + src/crypto/rx/Rx.cpp + src/crypto/rx/RxAlgo.cpp + src/crypto/rx/RxCache.cpp + src/crypto/rx/RxConfig.cpp + src/crypto/rx/RxDataset.cpp + src/crypto/rx/RxVm.cpp + ) + + if (CMAKE_C_COMPILER_ID MATCHES MSVC) + enable_language(ASM_MASM) + list(APPEND SOURCES_CRYPTO + src/crypto/randomx/jit_compiler_x86_static.asm + src/crypto/randomx/jit_compiler_x86.cpp + ) + elseif (NOT XMRIG_ARM AND CMAKE_SIZEOF_VOID_P EQUAL 8) + list(APPEND SOURCES_CRYPTO + src/crypto/randomx/jit_compiler_x86_static.S + src/crypto/randomx/jit_compiler_x86.cpp + ) + # cheat because cmake and ccache hate each other + set_property(SOURCE src/crypto/randomx/jit_compiler_x86_static.S PROPERTY LANGUAGE C) + endif() +else() + remove_definitions(/DXMRIG_ALGO_RANDOMX) +endif() diff --git a/config.json b/config.json deleted file mode 100644 index 035bc4a3..00000000 --- a/config.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "algo": "cryptonight", // cryptonight (default), cryptonight-lite, cryptonight-ultralite, cryptonight-extremelite or cryptonight-heavy - "aesni": 0, // selection of AES-NI mode (0 auto, 1 on, 2 off) - "threads": 0, // number of miner threads (not set or 0 enables automatic selection of optimal thread count) - "multihash-factor": 0, // number of hash blocks to process at a time (not set or 0 enables automatic selection of optimal number of hash blocks) - "multihash-thread-mask" : null, // for multihash-factors>0 only, limits multihash to given threads (mask), mask "0x3" means run multihash on thread 0 and 1 only (default: all threads) - "pow-variant" : "auto", // specificy the PoW variat to use: -> auto (default), '0', '1', '2', 'ipbc', 'xao', 'xtl', 'rto', 'xfh', 'upx', 'turtle', 'hosp', 'r', 'wow', 'double (xcash)', 'zls' (zelerius), 'rwz' (graft), 'upx2' - // for further help see: https://github.com/Bendr0id/xmrigCC/wiki/Coin-configurations - "asm-optimization" : "auto", // specificy the ASM optimization to use: -> auto (default), intel, ryzen, bulldozer, off - "background": false, // true to run the miner in the background (Windows only, for *nix plase use screen/tmux or systemd service instead) - "colors": true, // false to disable colored output - "cpu-affinity": null, // set process affinity to CPU core(s), mask "0x3" for cores 0 and 1 - "cpu-priority": null, // set process priority (0 idle, 2 normal to 5 highest) - "donate-level": 5, // donate level, mininum 1% - "log-file": null, // log all output to a file, example: "c:/some/path/xmrig.log" - "max-cpu-usage": 100, // maximum CPU usage for automatic mode, usually limiting factor is CPU cache not this option. - "print-time": 60, // print hashrate report every N seconds - "retries": 5, // number of times to retry before switch to backup server - "retry-pause": 5, // time to pause between retries - "safe": false, // true to safe adjust threads and av settings for current CPU - "syslog": false, // use system log for output messages - "reboot-cmd" : "", // command to execute to reboot the OS - "force-pow-variant" : false, // force pow variant, dont parse pow/variant from pool job - "skip-self-check" : false, // skip the self check on startup - "pools": [ - { - "url": "donate2.graef.in:80", // URL of mining server - "user": "YOUR_WALLET_ID", // username for mining server - "pass": "x", // password for mining server - "use-tls" : false, // enable tls for pool communication (need pool support) - "keepalive": true, // send keepalived for prevent timeout (need pool support) - "nicehash": false // enable nicehash/xmrig-proxy support - } - ], - "cc-client": { - "url": "localhost:3344", // url of the CC Server (ip:port) - "use-tls" : false, // enable tls for CC communication (needs to be enabled on CC Server too) - "access-token": "mySecret", // access token for CC Server (has to be the same in config_cc.json) - "worker-id": null, // custom worker-id for CC Server (otherwise hostname is used) - "update-interval-s": 10, // status update interval in seconds (default: 10 min: 1) - "use-remote-logging" : true, // enable remote logging on CC Server - "upload-config-on-startup" : true // upload current miner config to CC Server on startup - } -} diff --git a/doc/ALGORITHMS.md b/doc/ALGORITHMS.md new file mode 100644 index 00000000..005342ce --- /dev/null +++ b/doc/ALGORITHMS.md @@ -0,0 +1,53 @@ +# Algorithms + +Since version 2 mining [algorithm](#algorithm-names) should specified for each pool separately (`algo` option), earlier versions was use one global `algo` option and per pool `variant/powVariant` option (this option was removed in v2). If your pool support [mining algorithm negotiation](https://github.com/xmrig/xmrig-proxy/issues/168) you may not specify this option at all. + +#### Example +```json +{ + "pools": [ + { + "url": "...", + "algo": "cn/r", + ... + } + ], + ... +} +``` + +#### Pools with mining algorithm negotiation support. + + * [www.hashvault.pro](https://www.hashvault.pro/) + * [moneroocean.stream](https://moneroocean.stream) + + ## Algorithm names + +| Name | Memory | Version | Notes | +|------|--------|---------|-------| +| `rx/test` | 2 MB | 2.0.0+ | RandomX (reference configuration). | +| `rx/0` | 2 MB | 2.0.0+ | RandomX (reference configuration), reserved for future use. | +| `rx/wow` | 1 MB | 2.0.0+ | RandomWOW. | +| `rx/loki` | 2 MB | 2.0.0+ | RandomXL. | +| `cn/conceal` | 2 MB | 1.9.5+ | CryptoNight variant 1 (modified). | +| `argon2/chukwa` | 512 KB | 1.9.5+ | Argon2id (Chukwa). | +| `argon2/wrkz` | 256 KB | 1.9.5+ | Argon2id (WRKZ). | +| `cn-extremelite/upx2` | 128 KB | <1.9.5 | CryptoNight-Extremelite variant UPX2 with reversed shuffle. | +| `cn/fast` | 2 MB | <1.9.5+ | CryptoNight variant 1 with half iterations. | +| `cn/rwz` | 2 MB | <1.9.5+ | CryptoNight variant 2 with 3/4 iterations and reversed shuffle operation. | +| `cn/zls` | 2 MB | <1.9.5+ | CryptoNight variant 2 with 3/4 iterations. | +| `cn/double` | 2 MB | <1.9.5+ | CryptoNight variant 2 with double iterations. | +| `cn/r` | 2 MB | <1.9.5+ | CryptoNightR (Monero's variant 4). | +| `cn/wow` | 2 MB | <1.9.5+ | CryptoNightR (Wownero). | +| `cn-pico` | 256 KB | <1.9.5+ | CryptoNight-Pico. (Turtle) | +| `cn/half` | 2 MB | <1.9.5+ | CryptoNight variant 2 with half iterations. | +| `cn/2` | 2 MB | <1.9.5+ | CryptoNight variant 2. | +| `cn/xao` | 2 MB | <1.9.5+ | CryptoNight variant 0 (modified). | +| `cn/rto` | 2 MB | <1.9.5+ | CryptoNight variant 1 (modified). | +| `cn-heavy/tube` | 4 MB | <1.9.5+ | CryptoNight-Heavy (modified). | +| `cn-heavy/xhv` | 4 MB | <1.9.5+ | CryptoNight-Heavy (modified). | +| `cn-heavy/0` | 4 MB | <1.9.5+ | CryptoNight-Heavy. | +| `cn/1` | 2 MB | <1.9.5+ | CryptoNight variant 1. | +| `cn-lite/1` | 1 MB | <1.9.5+ | CryptoNight-Lite variant 1. | +| `cn-lite/0` | 1 MB | <1.9.5+ | CryptoNight-Lite variant 0. | +| `cn/0` | 2 MB | <1.9.5+ | CryptoNight (original). | diff --git a/doc/API.md b/doc/API.md new file mode 100644 index 00000000..2cd0fbbe --- /dev/null +++ b/doc/API.md @@ -0,0 +1,66 @@ +# HTTP API + +If you want use HTTP API you need enable it (`"enabled": true,`) then choice `port` and optionaly `host`. API not available if miner built without HTTP support (`-DWITH_HTTP=OFF`). + +Offical HTTP client for API: http://workers.xmrig.info/ + +Example configuration: + +```json +"api": { + "id": null, + "worker-id": null, +}, +"http": { + "enabled": false, + "host": "127.0.0.1", + "port": 0, + "access-token": null, + "restricted": true +} +``` + +#### Global API options +* **id** Miner ID, if not set created automatically. +* **worker-id** Optional worker name, if not set will be detected automatically. + +#### HTTP API options, +* **enabled** Enable (`true`) or disable (`false`) HTTP API. +* **host** Host for incoming connections `http://:`, to allow connections from all interfaces use `0.0.0.0` (IPv4) or `::` (IPv4+IPv6). +* **port** Port for incoming connections `http://:`, zero port is valid option and means random port. +* **access-token** [Bearer](https://gist.github.com/xmrig/c75fdd1f8e0f3bac05500be2ab718f8e#file-api-html-L54) access token to secure access to API. Miner support this token only via `Authorization` header. +* **restricted** Use `false` to allow remote configuration. + +If you prefer use command line options instead of config file, you can use options: `--api-id`, `--api-worker-id`, `--http-enabled`, `--http-host`, `--http-access-token`, `--http-port`, `--http-no-restricted`. + +Versions before 2.15 was use another options for API https://github.com/xmrig/xmrig/issues/1007 + +## Endpoints + +### GET /1/summary + +Get miner summary information. [Example](api/1/summary.json). + +### GET /1/threads + +Get detailed information about miner threads. [Example](api/1/threads.json). + + +## Restricted endpoints + +All API endpoints below allow access to sensitive information and remote configure miner. You should set `access-token` and allow unrestricted access (`"restricted": false`). + +### GET /1/config + +Get current miner configuration. [Example](api/1/config.json). + + +### PUT /1/config + +Update current miner configuration. Common use case, get current configuration, make changes, and upload it to miner. + +Curl example: + +``` +curl -v --data-binary @config.json -X PUT -H "Content-Type: application/json" -H "Authorization: Bearer SECRET" http://127.0.0.1:44444/1/config +``` diff --git a/doc/CPU.md b/doc/CPU.md new file mode 100644 index 00000000..4d1f0858 --- /dev/null +++ b/doc/CPU.md @@ -0,0 +1,96 @@ +# CPU backend + +All CPU related settings contains in one `cpu` object in config file, CPU backend allow specify multiple profiles and allow switch between them without restrictions by pool request or config change. Default auto-configuration create reasonable minimum of profiles which cover all supported algorithms. + +### Example + +Example below demonstrate all primary ideas of flexible profiles configuration: + +* `"rx/wow"` Exact match to algorithm `rx/wow`, defined 4 threads without CPU affinity. +* `"cn"` Default failback profile for all `cn/*` algorithms, defined 2 threads with CPU affinity, another failback profiles is `cn-lite`, `cn-heavy` and `rx`. +* `"cn-lite"` Default failback profile for all `cn-lite/*` algorithms, defined 2 double threads with CPU affinity. +* `"cn-pico"` Alternative short object format. +* `"custom-profile"` Custom user defined profile. +* `"*"` Failback profile for all unhandled by other profiles algorithms. +* `"cn/r"` Exact match, alias to profile `custom-profile`. +* `"cn/0"` Exact match, disabled algorithm. + +```json +{ + "cpu": { + "enabled": true, + "huge-pages": true, + "hw-aes": null, + "priority": null, + "asm": true, + "rx/wow": [-1, -1, -1, -1], + "cn": [ + [1, 0], + [1, 2] + ], + "cn-lite": [ + [2, 0], + [2, 2] + ], + "cn-pico": { + "intensity": 2, + "threads": 8, + "affinity": -1 + }, + "custom-profile": [0, 2], + "*": [-1], + "cn/r": "custom-profile", + "cn/0": false + } +} +``` + +## Threads definition +Threads can be defined in 3 formats. + +#### Array format +```json +[ + [1, 0], + [1, 2], + [1, -1], + [2, -1] +] +``` +Each line represent one thread, first element is intensity, this option was known as `low_power_mode`, possible values is range from 1 to 5, second element is CPU affinity, special value `-1` means no affinity. + +#### Short array format +```json +[-1, -1, -1, -1] +``` +Each number represent one thread and means CPU affinity, this is default format for algorithm with maximum intensity 1, currently it all RandomX variants and cryptonight-gpu. + +#### Short object format +```json +{ + "intensity": 2, + "threads": 8, + "affinity": -1 +} +``` +Internal format, but can be user defined. + +## Shared options + +#### `enabled` +Enable (`true`) or disable (`false`) CPU backend, by default `true`. + +#### `huge-pages` +Enable (`true`) or disable (`false`) huge pages support, by default `true`. + +#### `hw-aes` +Force enable (`true`) or disable (`false`) hardware AES support. Default value `null` means miner autodetect this feature. Usually don't need change this option, this option useful for some rare cases when miner can't detect hardware AES, but it available. If you force enable this option, but your hardware not support it, miner will crash. + +#### `priority` +Mining threads priority, value from `1` (lowest priority) to `5` (highest possible priority). Default value `null` means miner don't change threads priority at all. + +#### `asm` +Enable/configure or disable ASM optimizations. Possible values: `true`, `false`, `"intel"`, `"ryzen"`, `"bulldozer"`. + +#### `argon2-impl` (since v3.1.0) +Allow override automatically detected Argon2 implementation, this option added mostly for debug purposes, default value `null` means autodetect. Other possible values: `"x86_64"`, `"SSE2"`, `"SSSE3"`, `"XOP"`, `"AVX2"`, `"AVX-512F"`. Manual selection has no safe guards, if you CPU not support required instuctions, miner will crash. diff --git a/doc/api/1/config.json b/doc/api/1/config.json new file mode 100644 index 00000000..2c74cfba --- /dev/null +++ b/doc/api/1/config.json @@ -0,0 +1,63 @@ +{ + "algo": "cryptonight", + "api": { + "port": 44444, + "access-token": "TOKEN", + "worker-id": null, + "ipv6": false, + "restricted": false + }, + "av": 1, + "background": false, + "colors": true, + "cpu-affinity": null, + "cpu-priority": null, + "donate-level": 5, + "huge-pages": true, + "hw-aes": null, + "log-file": null, + "max-cpu-usage": 75, + "pools": [ + { + "url": "pool.monero.hashvault.pro:3333", + "user": "48edfHu7V9Z84YzzMa6fUueoELZ9ZRXq9VetWzYGzKt52XU5xvqgzYnDK9URnRoJMk1j8nLwEVsaSWJ4fhdUyZijBGUicoD", + "pass": "x", + "keepalive": false, + "nicehash": false, + "variant": -1 + }, + { + "url": "pool.supportxmr.com:3333", + "user": "48edfHu7V9Z84YzzMa6fUueoELZ9ZRXq9VetWzYGzKt52XU5xvqgzYnDK9URnRoJMk1j8nLwEVsaSWJ4fhdUyZijBGUicoD", + "pass": "x", + "keepalive": false, + "nicehash": false, + "variant": -1 + } + ], + "print-time": 60, + "retries": 5, + "retry-pause": 5, + "safe": false, + "threads": [ + { + "low_power_mode": 1, + "affine_to_cpu": 0 + }, + { + "low_power_mode": 1, + "affine_to_cpu": 1 + }, + { + "low_power_mode": 1, + "affine_to_cpu": 2 + }, + { + "low_power_mode": 1, + "affine_to_cpu": 3 + } + ], + "user-agent": null, + "syslog": false, + "watch": false +} \ No newline at end of file diff --git a/doc/api/1/summary.json b/doc/api/1/summary.json new file mode 100644 index 00000000..ed3cd128 --- /dev/null +++ b/doc/api/1/summary.json @@ -0,0 +1,73 @@ +{ + "id": "92f3104f9a2ee78c", + "worker_id": "Ubuntu-1604-xenial-64-minimal", + "version": "2.6.0-beta3", + "kind": "cpu", + "ua": "XMRig/2.6.0-beta3 (Linux x86_64) libuv/1.8.0 gcc/5.4.0", + "cpu": { + "brand": "Intel(R) Core(TM) i7-4770 CPU @ 3.40GHz", + "aes": true, + "x64": true, + "sockets": 1 + }, + "algo": "cryptonight", + "hugepages": true, + "donate_level": 5, + "hashrate": { + "total": [ + 296.24, + 296.23, + 295.97 + ], + "highest": 296.5, + "threads": [ + [ + 73.39, + 73.39, + 73.28 + ], + [ + 74.72, + 74.72, + 74.71 + ], + [ + 74.72, + 74.72, + 74.71 + ], + [ + 73.39, + 73.39, + 73.27 + ] + ] + }, + "results": { + "diff_current": 9990, + "shares_good": 30, + "shares_total": 30, + "avg_time": 31, + "hashes_total": 311833, + "best": [ + 278199, + 181923, + 103717, + 96632, + 56154, + 51580, + 45667, + 33159, + 29581, + 29514 + ], + "error_log": [] + }, + "connection": { + "pool": "pool.monero.hashvault.pro:3333", + "uptime": 953, + "ping": 35, + "failures": 0, + "error_log": [] + } +} \ No newline at end of file diff --git a/doc/api/1/threads.json b/doc/api/1/threads.json new file mode 100644 index 00000000..e536883d --- /dev/null +++ b/doc/api/1/threads.json @@ -0,0 +1,65 @@ +{ + "hugepages": [ + 4, + 4 + ], + "memory": 8388608, + "threads": [ + { + "type": "cpu", + "algo": "cryptonight", + "av": 1, + "low_power_mode": 1, + "affine_to_cpu": 0, + "priority": -1, + "soft_aes": false, + "hashrate": [ + 73.39, + 73.4, + 73.28 + ] + }, + { + "type": "cpu", + "algo": "cryptonight", + "av": 1, + "low_power_mode": 1, + "affine_to_cpu": 1, + "priority": -1, + "soft_aes": false, + "hashrate": [ + 74.72, + 74.72, + 74.7 + ] + }, + { + "type": "cpu", + "algo": "cryptonight", + "av": 1, + "low_power_mode": 1, + "affine_to_cpu": 2, + "priority": -1, + "soft_aes": false, + "hashrate": [ + 74.71, + 74.72, + 74.7 + ] + }, + { + "type": "cpu", + "algo": "cryptonight", + "av": 1, + "low_power_mode": 1, + "affine_to_cpu": 3, + "priority": -1, + "soft_aes": false, + "hashrate": [ + 73.39, + 73.4, + 73.28 + ] + } + ] +} \ No newline at end of file diff --git a/doc/screenshot.png b/doc/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..be1ebcf69d457f0a3ab3404a2a59c1a60ed40fe2 GIT binary patch literal 230847 zcmb4~1yEee*6%kEAV?rMK@&)D3Blb7?(XjH4ncyuySuwfaCdhd++}bc=iPJe{l2eW zy;rZRrg~=2uGzDDukN*a{r)RRTJq-ycpP{D06vHa3&;Whv^4-get!@7b|e#Z2KTLl zvldpd0{|N0zg`f|dEAa~2Vw0+Bm`l%-xDISpgmU+mH@zKKtzC7-g)Ub%}8DT=X~e* zN=w@W?}0GN$78b3!EHLQZdiA)9VaE(8UAK9!U2BqeE#_U9pwLS*Y(O(3lhoG#6+88v^=Bkjew^Q z4mx~K7&b9@9`J$W8HbXPLxCcY?LR-g=>-=K62ZVLk1L5S>5>xTrlQTAw)0;^%&wf}O&VD}n zAgjIJisd=eW=8~YYG56 z9KEh8v`Ek_?n+M&JoV4&EiR|;A+!-geaHvMylykkSMs6}caJ_~k7CC}wKWd-ws~gm zR9qsHeK(krbkepmL7*&;HME;Tx6u*Aq8XOmNhU2UZ`QuX9R5QR>B(gB5_LJm^mU^N zY2s{M#Jng#bXx2zeVo(f=VYfY(KHgw_HScWhJ17t11Cu%!j1xYPZKz;MwCT~h1+du zdf^YsEJr*FRsyn*?c04fb`4fvHjFxaZY<;#&@+SpjsKEco27ezUZ z5pa2y-0o+>xnD`@k$gB=8Bxw02u0#L@Vavx3h=LrY?Nsb#MclfPff7ZneN`Z$M&wy zVA8i{$AQoeRmrRVTJrfqWW^Wv09s)V)!{OgHR~efi#ikY?LIh*98o&lqF}r(YAf6A zsC-ql&kCeg;a7YKr|LdkVP%&bhP+-wp1)C-6-H{Y)RH9TmLv4QK}AbFLL-VGhS-u2 zMckjP|Lbd*8`Y(;9$(dBkP$OyI5kl>$=}uqN7jn`D^FTgK3y&_iAn(ufz8&yh7S1r zD@nSfl!3hh;==D#kJSF4(BE3E@2J7f(kqwwhP`QJ zAorws|9lg=+?ApD$%*z%WV-K}!Egi=Aeb1fT6eIa2sY?kfk4>o=Sm6ue|orlXJD=URd6!_nZFPOEAyy$7}g&a?gt-E5P zk5Sn6e;qd!Z%U@gbKDyp`M2FUgfh2Z_^7CKyXA4n%w=RsQaUjvGYXpqi+QPJ6`l`Y zYv8^%_U5(-ItbRb{l@*UjKX|NszNt6^TCp?`*eb+2uXoFYh2p*B}G$mMWwik^yvb% zodFU|Zs)--ShDyPyzSL#ZpLD_{aTiy7JU8#Q6{|Dc)_)Q_*O~PbwP4b$p^RWw zHbTx~fNO2LJ-ybh6+F~&#Zc#8c+)@%+3qx7R%zOkB9HD#$`cgE7}B%l#76}#Q6Z?sY!5x|4;&9@j`!?Rfo#Y1_U(z$H) z7Nho+=Xux=t$DgPqJ=Yw@o{_7Vz6)3v|6gNqQs`nM@f2|ezaz!V=6(w+qoRd75D5$s-(vDw;2Cz1^WeGm~ne})3A5c zbDF*R&oVXZhozOz{)+Q)Etri5lgd$!+@Cibe-AXh267>}S|T`Bz}=+UNoY#VHSSOT zaxk-+0s!@bEChGFrb9n5HDv!-0Qjx~wjI3ngP*ZkEP0t>zbYHB%)ZZ+qhr_79a4UM zT$H8T*z+88)BDa)WXi%s#7^)v4r;>3P+}c5Qic@$sac8y%er*w&=veq6hCZkoU*kc zxAFEi{(AbNeDC9TZ06cb_03pLi4La%a>QO6!33+J=^c4uSP?8KnYoKp={y*?!&(W+ ziVIf~hFF+AJhvdqb`!dR&goQL+g-0TD(6J_TVI?olYR@Ha7DD2L(_@84)e7BX;ViKVR*aR&7%7RS+fM_OSdjd>(eTcW&-r(bbXG63 zAQdA{MebQL%wC7&`zu{1jUIh&mc~cfVx0&PG|o|u_!qV#!wSM29Mn<1t$Hz7*9H_G z6OeyXf@NPv`>>CMr^~HzG9MX(sm}Z#IW6OP-KY7BaWCQ0pw#a!5suta<+G4BB8HN_ zFqscu16NLxgCKy9^F6(CsP=q{dHR|mNu13KHoVjk9WO)_S$H8|!|=tA)s8^wE=hx? z$>JUdxP&~H0L8mnldXnTEheY#N2W;%DqHa zMR#~n#uPE4G)pLoEjSDm|MtGe+nF#jGTrirvvy6o3!#^&pL`8Bn7H+Jb(>FV^czQ6 zAcjhpc}(Xp?$j9%1g5kDl*z7SQuxZf?C zm7HrnmA0278-jOr_btO~6RE{U{%ouqUOvpmd(s!vO#}Je41J8!)R^bXx`9~lb(2Ki zS0o~O{9t~5g&s&!0x4BnQ5}vh_i|hbo!i|_rENuYL-{6$+SrmDSvny9=3C+P0QGgh z)3$W!;pNMdXajtSuVUY*R2&K6ARP5OfXAyy{EX><2DPznPj*uD0}iL@f!@Xd6bZG{ zX-}@&Hvl$dn1*%4m;#Sgw%RUF{QlcldE=?-;lc}kHnyarAQlF`S%{2oI}n~!apC*2 z>C$oQ41yE95&OnnXcclBsk60K0`zEA)mh#{3F#t{;msJg18MUymySbviCRHBQhfMC zKd37sE7wv;7Ss{8RkcN-jeYa}U!@Oi_m_Ju9&RsX3Aib;amz)BA$u(ka8#-dKQhw| zjkOUhOHhbYT>?c|89a8LGEsrojj2vbJG|8XaEI0A>u?Y(;a3gzu$yREWt6s)%X=d$ za`u!oSFnIG7lL;es^>-8N>agzuB9kxd$FaRxXwb>Pw+0`WZY8JAxg$MvjK ziahp^qHmucaUMG`WeU?QYzz&_Vd$(I)ju{n*r5?=PmRBzNV`a44--5P!l$$@`R`5D z)V!?i^+aYMH61t9xp+JwigQZLl}0k*59EB>9VDDOOPKkAjF1RouHpX};^Ihzi&GNY zx!E?HC@w_e`%B1ORC+fs@}SOXrw z!ZLMrd^a4UrO4h;Yk5@Fy?pgiyRKBZ#+t-pb8amahKYUuX;oB0NWJmWG@f5hFCyGA zl|2e)4a9?MpJG}e@r&(YFv{DV-50zv|-|XkrD|$Rx$J8Lr<`jI(RcPSbPaHOU2|jMsS) zZaW!{S*++)77#$2pXNMPEp&|~M*t-`=|Xejs!20OGSF0?C8rG~SbM@VV!JYH!<6Ky zdQVto#-pK{=BJooi_O%MN9A^_Vm0U?U?ZDyq`N4lt2Z?yV-|$G#F=TJGo4j%v^Kqp zcRLWn4rgC`pU{FEnr-vB@~71%3rnxhpAtQvbwLDV)V)CH<o9W0Nwm1$^1|gK23TFF zO|*pVDY4hzt4=mPEf!r(CTk4~t4S@rg4^7j2)RRo+NFQ@s1J<1YBf4e>$<_=m_>~241@chu5DdO2=t_s_o=lK2XU)+HhO1UjMx! z5}H``r&Iuu<5R8ntpCj?;udA+K!}9l`ik-K;v@c=PX@HCsup;DMDlC3pE9BoIHa~z zl4dPb$|NMAnY;MHqaa3efQ0zd<@5|W6CU+cP0iGB>q|9G>X{jp0sc(a)S7QR@gZM+ zP`gbCm(ZxIrhSVT@2a(T!eT$8de zU}I&P`U%YD_Q_Q#U;whHs##qzgHRl(2r;uDh#~HdcN;9V#(n&m?i&r83u$PRW72yl50;b*8{qukOkqI(@hTQQ$Delluz;xZ8? zle3Xeqf%#|%;vXSayWsv(QyaQT&UrGlmb3tt1hy$SVI8ciKSZYthX;6ymrkD5x&kw ze+CpuViW=lMMDe_VR~k#Fp0_L1(1H&(lO^kB;@^Gp>~@`-Upqor_JmDmKJg#dd$YQ8yFV4^BJ7?f}hJV^9{N2a&qF_DJ|)zGY5& z&}wZw^%6RYseZs*CBs|Xzs14ipuDGET-@|1Z#~mNX8LRw^jN{YF>ui0wX$46QLf(~ zAeL_32N~$B`IsmTP|3+xMWhR1eBc}HTz+nLMVoP6=|b7gCGwjdGt{DWRwCTIef|Sl zoKO<7^Snlfnz zOHr1=q`uQeix|h{(jTZqK{KqlyK+po3D~UJ3pFXrdMBV@QeAIJ(Z#?ZVaBG2qjDe( zurRXc?X0R?_&*pf!N9m9K@TNFdA5^E`OmF_eORXYR3G{n2{ovjb$$}9QA3DOYrHv( zh3l4}#lxwAq*5(3R2;QFplnxr*{S#3A;7gMS%y$nSV3}% z5)(L3J-i&u{Ksu*3B=&Tv%CsD{OYI)J2^P!(W;Q5$QWtSNwkJaGa<_}s>a!_>Vjq1MT6BHs+b{WasiR$N&IElrZ2oc40$zH=<6>P zgeX?nSi1YYE;?X?$N>cg$h8{C@^+yYm9g?M6|fW{#`{>f^AkC))uWAY(lW%ti(sfg zWjAj3qQl>`HFBi#n*jj%5x(e<8E_#A4gvHObCd|VH6aW6pd{PR)6Qn>=MLQP`C}?w zh85%SN}UO$4|ZkQ9@x+!OGTwdUCl<{BF-`qb?M&(D^4BdB+vvQ_2W*{KV5i- z-TTf$^(|n^pRVS~%UYGbLo6t>VnEaIdb@r8h|oZx5F zO&i6%I`YD-azI^EQF3JE zbL6VPmB{UTEcEikMAwRUESSHC+cD=ge)Z61#F4Z5fINC7n(hSk7gIh!V#ThushmG9 zjZD~PB06A=moIcwpbQ0$NEpRZK?Wr?`UWhh541NY(Rs)&P1^zhQ^BzT4Ghoiscc(A z*~wf=W^r~Z*Y=ILapd|KD@wpRj)XDe^zFEbE}i1~Iv(>`ZZ?G5wTgi)4ve=@ervH% z5KfY{3BaTjrTHTG6(H-XFXK!aBoft~%$F;ukI#<2JL-4{Up&v<+G{K=#e@1BE-!Yi zC^(yvS*`y2*`rP3E%Z3{x~tn`YaHWjNtw(HUu-dT%Eim1dUXB-WifDWF3jA|t`+v(D=t@vPu!Ur@$e66+0$i0@YpMTkn4}tNWmryl`gUXQ- z_~NWM_uIH;=zGWYDoQyox}^r;5jl*^Ncm?r_=w7R0s$a4G}+Gr?q6FEVV=#J#)73) z7>9TwZB!|*vmZ372E!d!YR;zZBeBcG1^xo`@FJ6!HfK&#Qc>ZjhpuilHc&2> zkq%=uE!PG|!C=CvYb!vC|L}U^QQ& zDEhrRP{$qj%&w@Xa)n5=82hr(JO(`PntGpePmQ@QBhtr@JZQdd#jYGOW6cKTjqeUD z^LmF|5}{SjwRn`kvQI(5G9Khnrq|DGT=%3hd?@8TxT5+BBr3?(mJ>|P?J~s|$)d*f z#*GHFJsQpB`~sb#{<-7E4@K5bYgUL?2)7^gA1mv1-8?9fqCAx7gfwMb%EUmPp|wL^ zbDd`Y$wZuTtX6Ko!~3WExGkaxlORr^C-kt_x0*v8--}MUs;`g}IWy(+ndfyx)T}4Q zG~A-0(GrG1WJq&4!TjE*W~EP&fyvn zXV^9xO*VuA-$p^WvxsG#^|o0PH{vDx@#-Q##5Vj_T#Z-(4O;_NIZKA`kpuud)uWNr zZhpB5>3;cRwL+5_9u_R4jmB%XZV;34?EFoVrs|0PRZWVZYsg5-lyey}_I{s`Nz!5O z%f3m5Tfua5U~yH~5O_hPBGx;{sC+dv?E)JD&(ERKtV`rV$xSWc)!Otk0X(pmZG-&0Vcz8T%*$zFlVShA4U2?M0t&b>aa zH8`1_2zh{5)>H7Klva;X)&~X`^5=puN}~(<(dW$p9z0GrGQL%Hj)#BMXm{Ye%nowkW zON<9r;KLU;L}I`<)Y<+J@>YP`WZe-G`rD#WrSGUN{DQBL4ijsPa!m6#%!hjEr4(z= zI?b3fD{QO1mrTlPz+Dvv@D73ZdXbnj z@{PAtit*=!Pf#RPh!yPBwZh8vRC!Q`C_lUCuDXuT$aEq?E`cmU4nW zoCEPOA{(M}Wna1x0s#olK5K4eQ$lP^rS1pR|4fYD@|SQ<`%Kl~qQ&D=Q$lNN8jPpW zvfPDar72ICH5*hMFouB^L5R7$khx{_f7E#NZr8F_3Yk6l%V(V0%jsYNp7~klN1g5D z^@JU-*~8gc3MuR$GMip_hU{!Pm*6>Q7h+?ri( zox6E?-oNzd59e!giaolYj3CU6XgIJH;4={)Z&Il7!>Mg&Ef_t!e_b$w+AuUG#}6gE z?Oi24t@-sCG=TBlhg(uu=(UFl+so~e)jllR8xG{-F#fjGa{KU`bC%s)u7OGPK>I@B8mHHD}mR?=xSB_GWWq?~~xJ zsGtofWTU&}H#ohKyL_rDhI?&))oboH5_h~$bdJ-5Wo*t_1fT5o$qbf*nn zJuDRKceV1OVtKWG5g&o~(TWpyMt4=m)(12(XQQ*}qpFK*+VZuVC>V6yz0`=+X@R|{2j+_a?&#KuBg)aYwem3=qa!B>E5vo zrm`>EtC=q~_32)C8I(%Jad}M_$m8ZoOHStx0S@)_-))SZD_SXJ;`3J5?4C@*oQ8V{ zwVHJI7F#BZ?(;-{%M93|c9jz<)90IY#5KTIlDuI>>)SjRC?Tu&&atst`Bb!`A0Ew? z=b=a*7!x9&{?P*TGVL&7N0Mr=r|0zuuk?C&i?z*jR~G#|TN&-)5m_;8Xu$9Ujj7$V z^AFV}nAGtXmL4C>+GD^-BDBQ&>>PCjaCxT2#1zOE(Ur&!%GRW(wLLy2=D=Dpvk-OK z7$4E|i0UvsuEdbK=wE;Rw?5z%viA2v-`A$Eg3Dgh@sx${on?g0O&VXD#B60#G@u44 zvy!tpuccKbJvFy_p-)1K83r-b?$d>bhQ|jNxCI2{XSDtLxcb{KkkcCW_{QU1Uo{v% zR~<~~t=|QVUA0W)_%oSc{+*k=!)z!vY0Odc|(FRAGjD-g65z@%uV6H966j>&@GH zurBPaz+d_B|P=Cx$gzDqDGy zuCsUM{P8+szAf&2n1C^vs(&qIje&JE*O^vMiIBha300Db@Ev=2wK>+oT+I9%HT{2e zFCCNK?C80gn8c*;CPEj^GP=U>GOMpjjpbL!p6i10i$7*A0zf0&={@b=vMTR;K7_bl zoLW6w?)i2drYy3Q86K;wDn*1XvfE82*~jn4@Z5d1PL^10($OYnI_+e>f|6Z$ld1e2 z_`iQAK~j`Vs$kuyST0)*&rNM%OhTRiSavbhEz-Z|<+eYoW*NWNOwCa}BX{m<&wsO@ zU}NUH6B`5uox~hd1_#sSP$XdKlL&A&+JZ*Xa;uYf+~vSuh+*taP4vG0sZJdU&Bcna z)I@TRmghUIwt6o-J~rFUuWEuA$Dn+DTZmTi?ANRJ$i$a!knl{XPFC@dJXLS9w>cSz zqK@U+gv)Kx0=MjmX9`mUfnbCmSY~1Ms|887=}uZrK*W0S>IUF#wPrGso0XRG2?0H6@(<}V^17r9L+c%(j4%+cv;0Rdz|qXW z;c7Uu^U5LUZ&)mk}t?cGf{vR2Q^D-qn;exjz0{P7NpSE8$Cn6Q z_}nNYK$1^yK4wqR=R}BC7NvR5yNJnKdRoriEHY_{U-*Ql zbX8wo{dm!wDRqR}R=ZX6BBo*HX6EL;nhc)%@IL)Z&~&1fWuF0P)r=yq-l*a`!%~#;F#e6T;wyh~REj6_1>vrw=>?8$8>@HNrn2GD3bOTnQQ@lTw(b6N5AoON0 z-6T3B@cXQGrCE%WTFi{X(DJT*Izty)(#1{Uj7H6*=d2mjMIqHrp+9^My>~^YmZ~Zn z^FHDCv6j=8KhC5(zFh(Y@gw`eLbz(f#&um2?{0MZ`ZGsX61KdG)K~_Rt4gOR&N7qL z`s66n=5>>EHE)(qq5k4v^)X^jDY)bNcz#;qzI!mt@B0A4?4J*s?fj$0>Nv+<=Yq2( z(dsejV&EsPX@!iJNH{LV8eKFwi&R3FT|RALaaoA*@^$oe8qjFR630<#|6u_Iu)#L^ zg}u-SGVl9>~Sa_SWJ-OAWU zRxsgYKkvB3pHs%Hbh~m4Pq^-x%q1erO zfp3EGcL%sHh4<^%bh{nPnNl;cTW)Dw(Z5&RisMv1@!q7{shZ~Y??gg%Wi*eN(Uv`5E&coxT#P654x@0+N5ni^3ktLDcn6DY_lh0+G|};3Z>9PP?gYl zt}Ef92x4*0w;o`T;)Oa=esOQG8t=HV)sqvYOxgVJG`F(Tf>GCx9+{3jAU`6NP#$uF zP#wqFAl&zAp{iUhVQq3}qx&Z&>L;W-*Fx40=4*A(n%l?)-vQ(0raI;^$r^fmHn*O5 z+?FY|k%gM(3I^Nx6_1h3!8J=JHkPy9Hr4t zw%FNyy8mP1OAJiYB98S?wJxD1TjiNGC25LEyCvthuQ9E;}7NF#Lr+gS_7nYw@;O>{7wXG=cTp^G1b@{4xt5cNbtgC0S9KfqW~|H06=#bD$0(-L-9i?C_? z8kkl6q0^jZwm70<^Qx+kNlmT`OylNN*UHgLO)XM;;S$Ejv6GIw2O$)&;}B_Vsvy!Q zRq2i#O8ew3CwC1gG)n`N7Pk*+95)Y_Q8BNfRA$E1FLZhyzo-sB5&kXTe4&)SX;Luf zRlc~Q6ofX6?j!2-Ka|BZHWS(AQ`rgI>hbe3ZD4P(iTIQ5ieA|iaoS(}D+syuLe$@6 zd$J?r<^D($sTogqaXjGg00d!)?{=NyurtfvX4u2XupyAK#Nst~eD>O!GU}bXTbj{f z(LK%6dVIExMgM7I!Nsj%M^R$I0_MJUlc@f@FNm_qVJY>$lGY(M0^S|M8W3J+G{U!R z7}mLo&1jmXMP8+W;CyZdTAzfzYbGy#(HtalD!YJ0tg+TVZFqkRIy%QT$RYTmpsPQ3LHsi~&C^m*--2RWN(XmX_=y!*gQD{bwh$ zWQHF6t~4-@U$P3|0tTD+Iu4gr)~Oze$pm}p4w~j(TadDU&uwKe%q18jpW*)JF&i_q zB;3|xbIA-q+|F8E=%iT$*l#v08poY;oXT8cg^?QHtlJiNki+yuVJ9&~+gjWBtv@+1 z?*f^K&kmqwrk@E8KkPqsyeu@vu|~~D+`qo`U%-A6)!mi~uO_2zC!HM#}$3bM+7svuHX}w!-)>lo9*^B~}?j*JGuyGGg0*_j);m3|hGQ0K16zmJC zigu1~ocZBqL;Qz#tL@$K>$Fe+3yQiuqqU4!N)`RXtLv)(L7olu3r62Vt4gcUKa2KV z1&>K2jLmFtt-x<;vQ{Ti%xYq0tx?Q&G9|dlgf=(CNmUZ2`nSfU7?!8*Hyq7R*pHz! z2PAN;vbE;Suiz)-T*-Gbo3DA@(qa*=EF9|feiM2j)s`jT6fdzRww_Gu4A{js<|i!> zN@if`jq#qgG0hx>d1yocytj`zd(xrA`}|}6T(WI`@2_gd7kraT?K<7lk8CgYY+wCG z{`AAV$8VAHr8UiHMd$9ARZrC{3!Xbix0|^gD~1j--*B<{uUd8W6dRP!IS^{)uO3Hp>kXFA`0loj2uQ#dQ&nAIIZTQx zSkZmg5xxF1a|SdIQIK*?Ie72^WW)qE68bMFSClLJTD)&`Sc+1L@cTj0Cvku3#Mr#pGUQow#|dd9 zThSfNVFXON3zbnhBqN%2XHhFFc9(tLF$ii`lxT%^>3FJKebb9gkA4`ig!&XwwTp>m z%LPl3M#QfWm8HUpt6b>!_usI+r>0!?%v;95hXdZ;#u)jyVDJSxR7DwB8yD&Oppg^@ zSw))o&L;a@6v9xT@8fBLS2$ZNiT?@`5>m70=KgISvB#W$aEeRCmDceXEbte01cb>@ z<+0KzQEgDPsS*;lXeSBvZ4tc%mQ?HT?Y>5dq5w2e3P*$q3A5;d+WzRtX1HRAZi^l9biVYo>4TtTkN=4s?6sz22|BS(%9;kQOwX7W-kaGL<4g8n zoxj~Ktw)QLub-|?S|H@rrG2N|xBP3jNBdbTZhdd)4~Gc_l3ZnNA_Sjq!8c5KHX@a; zn+X}OCejX~0`d?rOFeD;Xm8RV2~h)Jzc0T}L6XkG+)x~%a*^C5m^M>~gz<|=7=+CU zUYVIIFjZgAm*3U1?Iwxe4pCSQ)DBFLRYye<}sNprTsyii=}HbvhhOPjA#52P^gZ z2`1=Cq8a_9lNIEKr$+Qb&4SbnVH#5}4Tmpz6%vx}K#Y*r&KzA`r7nK?@!5kpA|J1_ z7z>3~9%pWAUAJ^RUhw%JTkZ1lr@hC6L``OLazLAB`qv076Hat zp3|q(9cSN!Ix+pZf@E+*S(sud$>e8 zR;aAIa;&&B2k5ce4hk62R5tzcT2%sgM9qERm*LCYLE`Prhq^fP`TeO1zBffftIG-~ z{-GiLzDh2)lei3acoKTMIWYy>X0#5M%aVx`zXN7ykse_a+^xyUuO_+8QxC`ZC#Jc; zk_8SZ3KjCI_XO+*aj4eUBXw?3Fd z@)jR)RWx-3zWaYdv#W~&ebJn*?HMRlTZqKa$VdhUUS(s*>Csejm( z_oZvZxGf{A?LVF$pChh%$GVj~=Osd|2TyYLhVh3t9=E+ZBB#YR&X+jn|t{Z;tWCg%werJ#i@zlQI&<87Ip0T30AZEzz%Wvimmynl`sB zb5VbNH{)d$t_mW*HEk4=g|#o{ti-p zBSLF#ag;vPil-c$>^{)Q0%iWan9`erO@ro8MWUZ)QIQt$-Rc6@#Sva4V?-KA_HsI@O z|7rs2?|?wcSW}a?2IpdxvZGU~h@LOB#!+c15MlFDMbRnvhkgFl<#ep32QP`Q_q(Wz zrL6KsmIIpG^_%cFpml5ZyIdfJc-A+N%zI$cU8MI_%pb$))#e>W-(>W;$vbbd_Qp#r zKFwK9kH+I6-3*tPhV2k?npi_2&1E`|^IVS#3QWM}&y66LoL38WylkO#$*`R!%rmRm z%J2RPscsvcjf>$XDnMIh;-bTJ9pYz_>!a&|ocW8E8>B3k;VT@P)7RTN>>cVGW+$f~}e90{ydx2-C#7{vEwxX|paZo$Ya^n^)&Fhi*;`cf?J zZFGi)NfW>?=c85)j7?${ZwXlPw_uMMj1}!|5aES0<#9dq{0oKr-aynqg}1My5;wqe z(XC)(8d0`*KXA_!kQU@ZqJ{=`Ya)di%UOn`^Ht9Jn&z>Pk6%y5hBu1@;p4Jdh}rf# ztFt`v@ygqya?(|wLjLzeHvacS{`lVSRmVB4L0}iOXQ+`OkxYE9pXF z6~@)grQUK%I3(UEkEEBY%?8x9nk)P4Y3Ag$>H`IMzoqYEcJn#B?4M_fj_3rbfQj1} z&-Hx^$Rdak!;=UD3VoR{7>gCw-l%x!^0f0KG>obp(yQH7Oahfda%aw|T-WR2#yZ|h znD=~a^(U#J!CG9-QPUgJz*LRh&3U9ECf~OYm~HHr{<&rOrCM~I(cOmYFD`@eYH#M* z(4DO(#&-D;=%bR;jLsmyas*EmT}4dADRfMkKF>bvK2x!X_l1Rt)pJtaX~Tl?%T!`Goa}8JR(@H<=Z16C&d7OJ9`_}~sZqM36ztk}FnZjONe5VF)LyE7 zasMHBu_UF;QEx@OqvE*u`p5sWxT4Or^zyZy`plw3TJV|GcOz&}=V^n5dlE!cIsm}(+X6NMf2l04?w52@ z#z#;3jg6#!mEzZ?U$OHG1``8cG}P2^;S^lpblxgyGc1)E50xA|CS0juB(bOadxa7x zB;h)2MqYci&Fc*NiVuG19?@{Ud0iHjeOs{~XnNh2>1-bK^?{Q!$r)8wbDw@d*RKsL zNgC6La4QU3XV`x%c^&sjs@?slfos$rk6v^+q;)Lm@$mvwDVZcue>k6laPN<0WRO>c z8}DD4vY2S1i=^i0WohedNWE%o;`Thb@HA=@s=%q1W({@`%p*^`ZMJBw9Vu<(&=&m> zcTX#(sYxp-sUwV+{Fj~kc@g}|n~;Wu08UJ+82%W@HO4_<0g;{J`neVNej+6|*HBW^ zfAJ7AughwJatDk=an=+S@Z;0dO6y2qj$ASv;C)p6V58{i3Ch7JGb6!>-3=W+xW8w; zSa!P%isJh67f{h;mh<$q?2AHK`Vh^pGTNG)o>4WfcFlXGR21cJL7yPvNo#9&RpW!n zGIb_QX34qd?rv$RDS2Hrh~#r9intJy4i*X#MS7`{%1L&TXvAFe;ustm9u-4UpOwM# z;2~e1FLu$IGq;+AGGN37>MRX& zY8UR8J8MF}7{uIul&K-7A#-F!+h??=KsqoS&d%MESJ~B8cMo#yJL*Q6chE3?8?sw_}0>Y9A$`kU3+**}a30}Cm>&T3h1Y9Qz10(*?bi^rv*&8I} zxt!;2dNcAbw3MLhP*C0PWbmgFzNlzSEkdNxLUz#|gMd=-hME58_=<~JpWd+NkyX#z zqS$W9au@4y^^8eRU{rILHw@Z2lv*AnX0-zKTIgJ!h$V~;(xecmEiuXe44gJqI+A$% z7_;Qgm>zI=ZRJaDQ)gH4@U<3uKlqJaq))pR)QMD(E?f^)m3)Bw*V>wqtKdU7XW8~;RfR!q{n8^CBcKu_2D=ab=RcG7K zaMitwujnv*#Cr@?3dsYoUqe zezsZB@4%|4uqoC`TW)=9S1+8;U45&N!mmvwu~2cpSM8B<|3?cj*P4Q%W-dQi(1D+4 z&PGWXJK3Y}?5aeFR7sP371^7sJ4<=PjSG!7H9iZ{`I z@Ivjx#K;2{He=N1gC_kpCp4~j!Ssjc&Q&~<6qM=yTKdCg^V!N8Vk85d+~px7dnJTl zY|PhPlQrPzsGRYUpz_~kMph3G>hlsZrvERpg+nz)Tvkr$L~T8tQ$=**$R)igl)fcF0Ow`TkW3F;{o~Ed1%0LE0V11l4|d!*JAUzPIwYI`2=xK!<6-_5{}BzGlp> z{BbncOV3;NBS@izplwwFqMwkw{n-04oj25M%I+Y2-KhC+`piCrTV~xoqt{JETs5bJ zpVviwGe)lWAc?A>3i!4$zg)tz;-II8_9wuUSFJ?wCk_OVUHWinOgKd2-nO@0ittJ5 zEw~R!nn6bdp2|tqa{S#pd!-P%tPJa-RP z9}kw|HEE;w9CE*I{)TT)R_a`}?zBHOe4sFGr8+FElr-uslJ|(^wdyr42n`%B*ywmn z98{RUD|@tOUTaE7JV5XXk;~4J1^ns-0icwFljKsx@TN9&+Wc_r1Jq6WnMz~C0k=tH zN*P1}73J+_9@HaE-@td^JjI@1ts*C;Mr|C){oR7c=HOM#TVY=P`C+jLJ6{P0OnXuP z`309oa*Byub?v}%0WtxyB!$@mp8UGmP5xh%!bqCz7GeWx;d+&yz~K)kl6nx4>(jnc zj@MB*uFh=MmRx23i?p`@imPkdMK_)RK@yx0EI{yJ!QI{6A-D#22@o8DyE}usy99T4 zcXtMuL*DQGPMx}S?_am-?3&uOchyX-J$rTc>h9<1UR3@hvo?JOckRyatfvicb|?5r zQS;4P`0G|xw4?rC5i5SjPnfF0wgO&hE!^dEu@|0;Y6nE>B9=WXOb8LYMI2ug%ClGV z5VYQ-SI}l}wg(7mj9mSICEwXiFLei%4xFiy32crJN4LH2mLnhe=(t)n`Gg))Y|fzJ9d zdSeZF>3t2wjkg|AUW&}ah6P0%iNVplnXhzzcnXVj93Ak`S0cr(P(0QBxKG1W);AwX=ql213~*Cn`!lfu}z z#oj-EIA7DrDI0)!$>fIW8f~hJE>_viUMl%<+j>2xDRUZLlF2lz*%TitBn6d{=xORA zVQ}NaKczsRC4j>9kNbK)hJaD=KLM5FL@%;S$OK2k{FxDVDfK1B?~wtiA5L^YORqPj z>&NTD@5SLLM$>BD;WhsBFq2j0OP$@*o%cOosHx5JteRZk;U7H@oxkvM#VWe3>{@GV z4S>?=mztd`$`j4^dQ=;W|BL@A-?;Cym<~_BZT1MHMH-%Q5f0v3Z`KndMWpVk^Q`A> zOULD+lNkHbNhIvyTQTaNFv3x9uvK`<>pXS9VUiCH&U0qb$|$Ky9efk=c9i>-!JVm@`D&Ey}Dkq3m^| zx6naRSI225Y+(w)jpc+3_j1C@^|+xDtl|Gav8#;{5-gv%9BmB|6P`}IX-@rAG}mH% zjD#7HeR?>WxByI`e_Sj9x}!IsH_lC|FXNH6O@yNA$%D-24I89?)l*bd-V+sQw=tG< zs%|bySrsED4nTntz|JLu{^L2Iq+r?Ars{0e-3Iox9Bej6)Y%#9KD#=Qe&%|5qW>d% z)U5`e=3d3B*U!+mBi|C;kdKLpJ9Y>bD|k~tD}A-4%{H=-LQo<2_luLJ6KJ3JS%7N) z{3#d3fX8I-SMHwoy=$t8cIU3$SeooP!9K64smaE}(1_EVi$h_`8WO!OJ(Lf_>EO*` zjEUIto&C0Zn)~9fTNI4+mQGgts!dpZ!)|T0_1b=(hkL1KWcr%y!av7qL8(98t`VDU zV#WMG%NcD!%4=#5mkK*!Avd8ij!Wz>y@BO+28JVb}oA5FkJaMueg*LLru z-W5D)z?u|;VH#W%jWDaZtEBMBxrv4}&EiFtN8aJxJ2W7XG2+EzYT30uC@-xP4v^DU zXTMc{+S89)P5nt7dEx1s+@8?#gD2gUN8e99JeVirrGNadj5kYojj*8z*B+f(*TRJobE>Kc7w) zG=~AI82Bk;Fu>92$pEyidXc&#I|>~sOAL%2_wyq28k#ZpQ%f&wAteuES=$s`!Uufa zVHot9ch?`DuIO`1R5QM!LaDm&v=6x(a>FUt&&d+Btt-L7Y1n~>-?5BwJc8Bh?_V6I z)N#Dp|+xNP?Mm3qqg3uS5@r$$gt2j zK*B$M`3bQ{0(~-&EY_HCpw%^?!2bfz+{(yW=U4G)La}rQ+d}x-$%1>P5Ebw$t8bpu zh98%x2{#$6Lxcd2Mb)Bu@I%BY{b@@VHTTBp4Goi5IKyk@F%8uEq{8G#^P1Rej}e;0 z6`FsLsadt5;E)DJp|;>NT0+Nw_1Z{>#N zqoya|&hebb_SatJ^z!@`H_t|eY4B@&a6a4uWpz!Ww8i5(%y{y1p4AvnXV(<#uo)_| zZn2S{v5Yk}N&d9-RJx|aD_o(s_AnV^vU;}8WF~W;ObtFIAScc*#5C+L7cX8of3c!{ z8>{}qg_(|?Sbn@*Q_qJ?{N0;NeaD1NCW2P(PZi&(q2U1F*W~i_*#u) zLqq4YChONA!!95UIN#aXaSYedP*)k1ClUyV+i5kd!fU0o>u3dclLSOVtwZ_9tEn>p zH3E6o_xGr6LW*5}FcyX;-T%m|v!)G{tuu(gy{Wq9@VFinL5`e4MAUI4VaH6yH9xe? z2(LtYpkn7;m@+0y)=!RCSlw_nA56^afu#+FWhFwFydnw9hf^8whHq_nz#vWrg$5`e z7@d#@CKN$*p6uDT?2KBobqOSG)7+1i(7?3Lo_UB?f$LD;eMsi!E;c>AB|i&u{pI^# z`pFjg^<~zVzY5`!yohrx_x>zSZP}eyop(jhk`?rpg{JTY(4SEh5G@ZVX((OZa|r1iYOoxB%NtL zv9m0KnRx>FiPf^UZX*|OKG*Z4^}%)m4$M=X`j{>-tNt+Tv}5aNwE2>aq{)H{=fVU{ z?2qiUGQ^UWi2qb+j-hR|+21A0 z=HG9Lp5bX?f2rOa*K&u*NExW9@l|B7QL=S(#kzna+Pr)G5-eRaHT>DaQW^T<@@7=) z&O*#^(2?pRRy>or;4>LHFO4;({Jj3)1xZH^*#B}zdRsc!lGamqv~EoNE-aD37&2pH z9nYczw}nw@KGdvDlXZDJcUBh8H2dwqe7D!ap)-eTt>Oy+=zp#ojYdeo&ff)x+jL!d zj7)uoGhUv1_HO?zVQKl4h57J&(`On@qC>gpzLH(_B9`uDb8S=)eFMlTD#xvc#q`d4j3$|Dj9`6}86B+^CoYrtafS|q z%9|1{k-Cy;wAMN$E}=|;-O@r{Om@}56)^}lIU=8l{ph8;Q`ybGu#!&;WR!EW>1t~w ze5jJEc+JkMow{vw(&kb5{efWCw{Z*e6E^)yl?bJS=dYMbT!7uEcu4hWr)sXiz8hbH%Tc42OnAGIy4b3Tv=I54y20Y6+wPa6;5;c@adaSn|@tPpr@wX(Zu~LUCRSXy}dhv z2_1ZQ{zS404#ae8SNf85Y;v?+kJ70Oax(YPYjrQN3${C9SAS&hJeNx+T2S(*%L9MW z1~VsB!-rPf&5fSui!z&FN4Wm*{CIXXycup`?10x`dL(+GZ<9e;j#}dQ3NN;&jf-^b z5vH-8RBJkVTHJ?RN+4nhk<=17s(#E;qWa8dCM-+_=PF%U*l<6Sl459n7kuOC)%!#& z0RY9+<(WX(v;?uG?QXCX9mT7$RPBTy(dE`6#hZq@+A6Sdh#7_4@<0Qmm~3oIOdjeY z$&1H~7Zq|TiVrd)8Rsb4%`O6>oMQm~U|i3OOD@OL!YNij;{z99w)_1euh)p{i#jOT zi?~no$V8fx#GJ`#A#h_fyzZ)^dZD7jwXMQ9adC8R^`0lkNHOuR6yh8j&-6Z3 z@13N(hrF)CR-@roU!!2hGlZU5&Ult+ce%-({s|5DiYgSp_=K8TRm(k{x8Aw5+(V`X1t2=X74F zQ(gclc^^~;IhiDPGR>)vR@8&($O>hQ3Mg)H{0n&NIvc4Ib5NFdR;?tBx$? z72qVgIY(ex=Fm}JTrs*Y9CtO5!8cF2OIurPy{eUoR|5A}1>4_#GyP=8p2YrfzDlNS zl;-=~@{yC1y)kf~eZ2h0evYg@7eTuZl9T-0y4UL#XVckvP4bp4kPcw{l{_0ZS%|P)XS)i(DS)q2DMkKXnbI;G+5? zgb+EL67QhcSc`>FIa6+SN}uu9JUNuasjN%AcKj4+>8d01E?*v+C=X7!GId8t@q_yv z64nI|DvO)GH7^SBiy?t(9l7ZX2jvvr5t-zwgl($Ny$~a3DRKPSDz*3o@O_5H0*+6} zb*dOYV_UlxRTMVtq*V_xQo@L71r%5U0p5efTD_9{9D;A-mhzNQ{6af{4o);9`agR#0Wy`2Bqze z2b;aW%7^MF2Zx_7`ge@>>;PzbZ(V)+#)@s)Atw`Cp>=MfgOW@HTH157J!)Wm$j<(? zn5DE)!Ac?`VoFhO#r^iW5cIvLOjJ!1O3u(A40nnm8M_dxX9L~gg28N>*wVpUH5rQl zd~QD;!k4s0RE3nmFdLrN$LU_DYd!fRRaTdEcwlz5ZexhJ#_Y~Nu$-Tyk_hl{U1-Gz z*Ob(RDG*^zE@o}$p)=cqSK(SMGq3v&gXzm!SH4{I49SM&$AQNIzk#vA;R;0FG9HY(M@Tl7u2)pHzv?&xfDurbwGa$hvp0Vqw;;{bCunD$`TxCKDE9{@!Sf-6o@Oo8e{{J0g6Y`{kzOznR-#P zao%Zq!5}LXy8168GTd#4DTxr56ME`vOWx+0l2*Lr_Eo8uLClRgVsz zMXRsR^>PKWmOo-dd#F#j!p=P%_W~_|BdTFB; z(pqu$K(G)V8b+k2=dFrmYBYVrwzWky1C~AJ&|>@kQ*o|-j&2le+yAcA=)9qq5V_{F z(p~>*_kuBoMm6+IHX9dLvRUN?M`r^cuSOL;`N;R13Xre`v=RBNd)IJ5DUODt{!%*Z z)Rwj%B^i&ntTqjYv?d$(ZS@Cd>hE5HduN9SCh6GFUSZigoK6Dm_dJ6QrinDOmB5`F^-LR7v>SxG zebL`q)%e;B!ax@1Y~xXwvdKw`fn#=tJZ7vNvP|?oKiu$k9+uy&{VAkp3dp6rL7jf2fI+6WfY}^6<$%Kr)j?}l#FOek4M|S>2r4a=EvP9Q#N1K znvC#OM@4jR@z$;$@3z)rhEe(x#VOm`fZ!V??RX5qA$$AwF*9axU~c))KK5rR@r8qc zVh=0jYEt&YKF;y%Zn%U9m07w`B@J#9!qvEokH%KD)rHm^Z%8hp-5jhdL?nh3sY0p! zSK5&F`{KxTCU^4p9XZh0C7#GR1J$5xe+`Qrr~CYkwpA<2HZ01%o7I?GZK-aY+0+~1 zCg27l)|gA*_MHcEcPv&d{bWj#ZZDGopFGnkAfIlLc|9K8Pltuogr$0ftCY@c?-}PC zea|Jc!Yu^=v|F`LMaA4d9C@!}WA64qwaH=OqAZ{{;X~`Lg#^-kRBfu6(5yWFk4ug-1YQo$XY7~LCA@%K1;l<4wQ9_nExs)GX3dL9%Ph;@xS7DYaFNX4+Nqof8@PpE~?vWi7$tuf23xxkirdh+X?k%#7!h z&G`Lb@K0(-lpwN<&1F&-XFG^Jqh5V0BAjwy2^)yi7bp_sm6aW+3}dJC=4FXr1Q@T< zo7Mj5>dx}l5clJ4rf*EbDF;DXQ4!;1YL-9@=3Rg1$kHfg;#6nmW?zU&x2~r9wMugS zq~7F_l5_c|Cy(886HUfAY{qJ+FY;e75Ca4>u0AM!iK6~{Nn*ZWQ#~k2D4ke)D`csY zUhubxXC`!scqWs>A51=0phuugF~e zD+Dwo`m6o+W&^+I5YcZyu1KcU$@XSgKZmDL1?=CkOM#Im+`k&^vN82M$5gBQ>u@r4 zslPo5lfg(b6mF{$8}n1kFe#iYZp;$_}%5S#_J!5U^x_Ub|{ z8^(P^#j=;G)01Ok9A?9r7m%|uM{8Db0}<24V{jU1cVx{-27*Hp zJCuhEpml}xY{Jt={kD4LF}q{WAr%5vo^V&7?; zY!br(h4k`&1>e!C~9%tu0*)y z6bIv%CHn`js={X62JMx~C-zbmGg8wkob-!Oz!XG(^OsqHF;1E;bh0_K7^_d_L7o<; z!sxD%khGzhLKb1x_K)8s)92Z^D19II^cIJ?VoT*MW(ETHoN6Cc;bPU`!Xfv8Am=M{ z@Eg-h#qy^La}8B|8Y~xdL8BS5*9y%4>=E$lki0s$YeL)L&AT73#wX2fJ8hKqWR1$4 za|g^x6{nJBw_O@BNx_ODtC0D^UoZ@b^P@B`)c_5&RCx(#UbI_5;Nm4@+nru?A`sEP z8C;P3`kNc@A8(JTS&>|_*|C^DY!=-MTu1`jr~cs>Ui_e&JBiVI=A9FY>8h}G4hw%s z=r@0z=**>FI`ClVs@u{UiOtXw^!Ks;J{x07kInY9emXQV>_7MazfTZZTLaVSTp_jk zlbKavWK7&B)+vYUPNYhOyp)9IoNy^2Ddjxj1+xQTOs%1MqYdzJb}xOvpGz+sU{bAsCKBb06t66=HUD=l%U?1e{;d@9vIuT}xE+bUm5S)@B9^pgAPA8XbJq zgqM=HDDc8ywcS}PdSS8OksrhbQHLN+Zk4CAxttpq&uoQ$Mjs^LUQUghWn>t_r;-e{ zn`F|!6DPSN{N{QYl>QR&-jAI2@EVm9MWk2?QY7|`j(PF`v=aD*AFO$6kwPt9O0#2^ zQI^I{CKpBE%H7&>yNot_W(IyMxZ{NqCa*}~DO0^?8?bntSRB{WYOhL2EkJFbQF(Mr#^FG#WpF-Pq+~CsSF_En zv6=-YznN9@@!IjXlbik83#$9#R~FrmA9?gI3nTl(AmodwB2DFF~-Gi1?R?>;99(P{}XM>Xz{*Mz0cocW1!LU6nZqA2h&PRyMXMF35=a1#C z8wyC!0hE?80)_|J7l+HL>G;c~rJ><)s`1{ekDC`K8Rhey%_U~;iM4lDd#8Hhl88A+ zjNBzI+t^mTgk)u*xd!KAJCZro6r*1xrkPI5(;C6j-V=UKV%~Q7{S_m^BXtexMd7WJ z5l~bXDM^k@Yif{uZgkLV`dU)cJ@4b-%+LI_DidT5bbwTqDh?V*uZ$tLaKP|Un`duk z`+^Ik4;de}li6KbZ|xJoR^*k5vpZ|1$`hRmuFQJw&QBnAC=rl#hm!oGH(mbFZd^ij zr#dq6H0AW81H&rHK5_1vgziX7b!kubGQDxha}MZD$T}4dOR9n=^D4K$(cxPkdt1`K z`BOJFSxi-a+{r9bd~6D!Rl8a|ll8`0`&sjAoPJ&x_Fx8phUh`SR4}D2wT2IELLc;d zXHT_B$^8-3!949xD=_V?o^_A12+=?Js4x{g#rdt<>PosgzG!)P66DRcy$UMPgGi$V-(7)d*tu@?_RH5GZBMRTSSjHmGsi~w1-^t{(q4nX6?UbD zyJY#iK#jyasd9Tgvw=Zwq<4?nxpn$|l>wS$$J&O0v z-glgEW^tu$2V;LqF_HJ#fb5ERULq_KQP%!sWE6|NT@rC=tARS$Y~M3i?~AJ8_+ATw z|NY*FuOB87%9J^jatq9mF#sY}dV<9fVItKrTa38L_0 zmHWzVHHb#kzp3iAy3ruJl#w*LL{#iI3yjv|Xk&SKi$3hUeL8AWOGCAAvmP%L1|GyP z3PaMw`w-(Zyo4{awJ4I{>hq7i;f_f|8)2y@@=KjSSP>fIn_pn3naS=saUoMjnZwo% zpodn>U<63Wda<=$rTx$mx#OWX{xhP7cl>9-<>epl4B=TCN`kc;zqdTW2)vpX)os0d zsdzUcD#z>2sz**Z?pb7wdCPkA`9Po`bQ>u zIeXR`f2I8o*+4dh)6{Vyz%eq#}HQ(_Wb4>WZBAff&;8e_w_Yj zjEhZPPTC>z(Ue-5H*F!sqI^RJXsIc4AhlwQ#P%)52uvR7w}JLrIdLD3f*$OfHMdWb zZxsAqVXAYt=Vx)uzPj*8ZX~6ID1{8Wtew1%a76JXaSi0r(0MEXpDX1Z4hRLx%FFlD zcdJE?P>n`J2};uX_-dfzO9`g`C6`sStgSN|v~4m;9J)`fqho8kXg}%UZfOfEkNlbX z@B8J1I3<5<&i|~wZ{e<04Xi#M`vWyi4K){PR_4sf08lt!YU##Q$6?)VVRI5v{`*gE z=|XOEJq1#SQJ(Qk?QKKYv=oZZ&9#N06H}rpc9AO*goqHTBAx%_yN9(*XSWF@CdWIx znYnl<9nWWpkb@t>k$*h8;NRhLxR?sL^sL{?V8NOyTf6aZ8J`Q14(z3KIVg+b?ljD3 zt`bZ(o(&zfCGGZ39?oZ=t}#56DT{seay>-ecvG*#@@i_Z6z^2<=G8HyDlvi$%8L^K;^M;l!-76)69fP)->B(w{=jxDHa3YRTOZ0h#?T|5t z>s!-?AQF$8-qSdYAimx8!!Ow5Rf)9r2@stN9GEZh1DGl-uH5F6MUz_wn|3MJlin_$ zAO1hk+gjHLe{bo}y_4)29B`WB%NbSi85_S^r}qW>YSY?(DnYzR>mC?B2_5bpk&{pV z_GSfTZ&Y5W7dP5Sw(FXlF^xMebtH?aSbNht3T|$BcKH6Lm6euvcdom;sg`VBx$M^( zFRoJP>(bFD1JdH+QgM{Y!YA!W3X(kp#QD9kIQSUhYB}s zF0?sYB$`Be;WHpRd zb_;Gb@o7&e9bXoEpYkbY5BU!3y+$0aBWFP}bQHYEFwT@b|BGPM_n&(%qbU0KS|iyz zWEyP;-PX90No0c55<|(d*=WM}HhF-l(d`PkI=%sI%6V070-9+Y4@aMzw67Rhcm8}` z0`F=z5)g+=**K$juyaIZ-HUfWxv4Ptr39m7S)+5ZYBTzmA)`>lDT)A~p`w<#bp!XH z(Pli|?V0xOrMM~hA7}V$md|fop~1`wIaypmke#(|@Lcjs8|@Y5P8^1h7~b$5eXK+P z?3dEQuig^wI+)lAsC2%*r@s8@xnI7~wTsL6AC8uKY&2esakFkbB(Al)$`-S_U^9JJTPy>gaqKu;^)Y4CPh98P5zmk=vhNM2XhfDo>?%?2W0zA5nZ9?HtQ2T&hQ{N09;D*tDgejd$VV49>{V zkcbTe82`TeM1OEJNngv7$yh48{qFSuse5}mr|ZN3qC^i=jtJ}vnP7ma@QL`~{ctEc$b5A(X@{Dj}r`SSifF!~9_4kw<0!%t+5mK|Ef3Je>;OrPKS21q(PBEw}zjNca&8jm{L^ z?;VvJw?L_`Oyp=^+337mOikqq80eQPOOClyCuNXG;$X19{b1|1s}q&>iBq6N1(73Ks6}{C>Z(imGyR zCt<^uscU52DUbx-9X1b`&(3yMmgOlbXH$XNLDJh+-SCc8*ynl9G8w`2)~#aTFN-gNxG^T~&MqgRu9QgLg9;I9%;k~(=gCY6WAQcL1@mj> zUDEWlG6`uHuw#s?n&{skViWT6k^yhW)q1PW#H%%^v@^rM*bdMFNxLWK*=kP7;}oHnfkhHdtjS-~N1SbA2& zn4Ty|2JmG)s9D|RVU`p6Ql^7&9f& z^3!trXVU}JvJiyicjrv1>iy{@it7>5RfM;H~Iol+#f5i4=2|^ zo%l608`B331_o>2{s>C|K0(JzH^)hdn^6{x9?y6sm^8pwMrwAXUfK*pUooS2 z|LGEmRTpHwo7!?L2(!nM%UFf8Q<`T%U0NP{^JPrVVLVF&va>;S*p`5BU(db^_qBNw zLdf|Hb~k>TAp6XMtV-G!@KqEIl%CM+sTF5az18w0jd+CA>}B}e`MTKT&u0*;6p|-( zkHz(%UykakyC!z+?UwezlQ(I!$K%fCwFv0_q7*DJZW0G^d*y<-$~l?8F%;*jYV2O%ADP5JSmKx zmTr&x1gEW}!5_xxq`B*rI#NSJ(acX3q_X?n?B_{biS{da|8(&0Duo8D|3;YJG;V85 z%YE2PJ!T}ep&A=8V}wahYG@9uOBb8MEw+QqKN)`CS{kj{pXWrU_h^E+<<||$X(!u* z?^&x!@-4*~>KyQ0e_Y^wVpG<#TqRu$k4?TNWB<{9rp4$rzFkVYItZg$G!zygKB^L@ zf+xk5*CJj{Jt`a8e7)3|DB#x^RnpMp@%`RHm#{6Wxa?~pa@^sx1%m3>$8U!*7EKfs z(JUiZLqoqDydFz!#u939zZtHI4kZkyanul`yid~*hVl_x@zn11KeSh@BIH^V?G0ak zYdAGWIF|=<%?Va12HAWX@b+}L++%HQFD5^*kH+V1n#>L6b$$^sC{rn5y@+K!7@Apl zt2(A_%CSXRN<}%Lm>F*T{X4-jfj{6OF5_gDW8$Q=vuoNwp7ZI&VMXXa;`)!w{$HP- z9fKZ12Ls)u5=r#gpTqB%tc?u!|C3OA%_>qPbUX>)Kxe5IYMJIn#nCQ6Z(SZ2*D5~4 z0kL+!1{I`bCP(Z=QPYDa67kN0U$@8-H$-r{u1mlRoyzfEYd`9^fi~=Bv3&^K9a~6N zyJG44u^n>5v=}u^xYyRm84l8ZTlwj!ownQ={YS9o-Y{?C*jyXvW2MIyH8Co?%G64l z&G1SFDdE$^?HuPyJF>3Jw%-E&mb9!?RK@;X=V>Fi@U|~MQAj3(y>a;QG4cbemqz`q ztsNZj=e5g?M|*wV;e0kbz5%jY(SQFE6rkoJ5jzVD%*#c>9cZhHxWm19h4~xA366*F zdHzK}R?hR}3v{(Ovr2QVaSbpz&i1wAiaQypW>#{p1WLuaSqcr?acRnj6kSFiSY$!8 z4Nv^w$In0|ZZ)ZNjwhpV-!BwrW8UM?#f7r&Z!n%wwW#pH+E2h<$WOKrl+#TmM;wir*)x*H5Y5)7+`{EOpAAY;E9r9Yy zP)5*6Hg&&hPY-`+3;0gn#f?eaM%0G4BdMNP!_}hy^4UybuZ}w~^7xDt)#C*uXm@&0 z#Jh7Y#GrTKe0E!5RK#;P`10(%E$P?YkNU3S3-hIn7n-~crEa8G9`&MeC3`MGAB0|02~8ajN$ zOpZqJx-cwh^UCv3S0cZIjeFBKI~ou5H4oC{WfNOQ_a?8tiVY2xmp7-LB{o&(t5bm9 zL{{E2{D>RR{(b3zeUo_MVLyExSyF|-f2Ero!#pKHzz9G7O^_9KO1d@xbm)BI$onR$ zcSL5K8O>@V`IsA{+0L1V;IjiFZJTd@fFmqGm*l~@Tmn(_pjY=bRd-l;xjWy7u9?^a z+bOy{Jf8cQymOW1k-DC4_Esq>+9b6|?yeev+>$xSsG}6fVTA(})88?EvD>Vr4#5C3 zy~hypd}HE){fM(LGX$;Mvy}E#6~3c(Eiiq8+vXAr??L+V`_6ePE8NV-`3xTLliXG( ztpO{&GgiZNXn|o)uj-Wa^H1*Q$M)$vc5D%E_bpWl$uF}M|K_gU?2;o#JY=LVh7{{s zqw8#fsS2`hxfQE+=iSYk^YcEk71~JSPbPY9catGZbA3f2CFe3CJ7K5@^am2WO}k%? zliPrrr^VTodTBl(ZG^jOkTm-5!l{DVu(O^8SPd);RnR;Bc&zT5)=CS5!6BQrA}tV#Qo0(k^5MVQ^8xYQ2SksH zlPdoYdA-(*6qMud70FOb>W7PRxlSF_ML6aLQznhDBOqBhe7OWou(cOgSRXR;@BcJB zMj&X(Sb8v^3XOlOu#%=nG$0NKKLj$b@_5W%StTOue1PYgl9efBxLmK|#-^r35BP=J z8zvBn-p84^DU%k$Yk!%X z{$Ad+=W9Z|F@_LKylz-TMAKae*OLmE!mNy+Bu4W+qR{5@w=YCQ6^3J-W9?Tvl}PR; zfnosFs3+G^jH3GbY?dGO_Hsi~enAH|!IObIqmwM(e~fz;MHsu45ef5wJrf)C(&P|t z0!2>|_(p{>ct+$cF`uTlZgTlEmA=|*qxQ+Z7>08$1fb)qBPEmo&Yzbb-5ZUqebO#u zo1jQMt7NsA;eJyI9#8V_!s3^ce0@F}`TC$NI!)HSbJ}@Yl1gWLn{)3`Jz5f3Ip6i@ zSs6E(8%v^*A-ZY#0Fyh0^5KuirN?*e^v0>UkJ5G*GxYt=;Mv7hM2Fd!+l(W`$A+9J zYnzgVZMw-m!nLLc(`b}ts(<41*O&jQw{j;3EopnNA=*Or`*rK>GaU88U=|$6v{Qf# zodlN8Tp{z$4km_Alm?|ga0kzgmZT*E6qdEL@FT^=r#)DkOeQu88p|%WG*`*<=zr&B zhtl&je!}6#<@-(s5M`gD3u@I_n~95b+FCqYSkS4?*~lk2-3>H7TmNaUus8{5f)Mat zH$VRAjcf`PO#igrLdPD6uzo`(Ox3$CT-~e&s2)BpQ)E!V!U7_^tk&zo#D+rr=k?nA=tx>f80$HrdW#eY(}YY{`zH2HgQQc9XLBGP`m;X|^4I33SlxR-D3 zeaJXIfND5SbfJaQE3qs&6OO)dy+~t_3FFUWR2xtiyq?<nJAQ^_hH-6!tS2>4ec z6XotD(T>v3=+oB+p=mkY_iLK2XKOjh5uw+j3^^qaorYfNe{$$hiid>W+uv{zqn!1g zeYtE2SZX|1n8<@>I-;JpVH?6=2xU^f84cD@D&|Xj+KZ+}y_c_78O}NX{6TX`4Da2A z-tn`C)myo;*KO87p?8WlBc}%ZNRh|Qa_{`dS{;s`Zb%EZ!a5dfFRDmzg#Wc^k7r=; zE1GAmKap$f%7^h$8C!e7r!(Z$_B4;=s^e%JL0%a@}|^%F4Dz z7u2pxSJF+yNO;a2M(${kT6TJMsMF6-GeRX)kH|i8lMkm5l9ShL z9$r)@Clgh)(YG@71@DuxdNs;kpHQzCS5)dJz1OsWH+3`W;Kxa8bxUIChpxIr*#KY-*84h`w)r@lM zri|C$V&0h1Z+x(?OZ-;;qA||=rZ~HY&=__a#fV5naCU`qvUqFuT`kE^pL!W13+Bdp z*^dp08oA1A4|H8voc|5k$3IN?JmJ?1Q`(eFfA39Qy6a6rZ~aC4RGL|b#ElbOFY}3U zBj;?4Vmm{@=!)3G=< z`Tg8TV(qC`M2jG*tmm;WL^{b^DfAyw#lH&o--YJ?<~B=(WS5?b@RUJ6#DC|Sb#<&E zE<8&R0=zei?4^U+ytJ1@WRifKHf`NBo&DyKvclZFo?9CW>T&b^&**LjZ-#Z1=+WE) z1_MO!)E5Tf6K~cbR<5m~L6XZHCNG^HZ5IUwbLlz#qSpuOTSPfK#Xp~hdyd*6Zo9_$ z5ln_q1}yhEC*{sf5YziEQo`ZY#Mc&_+(?faXc}H_2a>vL%+SCaj^WiRheTSK{K8sA z-Vpo-yAvfy@|oiQ0|-{ODqI|U{bB>{$kUJ0l;)`h5z+jQZI&w~w^%j%`&|q8hVO1N zzNT-rYhC?h9m+fN1edlZJ$+EB397ZJCkm|E1_c+lA6G1l4)T8}!4%Tx${S=`O&$^R z_&$28H4#pwJ=9Kn6XklRXYm1%{M&1&`$b3b-$^Kp4=4~alLma=-KhOub#!0A&HXPI zz^F#m2Mq`cNSW>|6MiR^!oWGn{3@<7?xs!DQ|F|8uJ?yX3wX6EOZ$10TsQHA7NgKY z?5^HkQ?h^AK!2s0I7rlTP{pkFl~8rTZk4zwEyJspsccbu_>`fmgy`|AX%L@s3%fG! zE2)#VkVg-%0w8IS|EF z&BIbg+)JD|5_a>zT(gH;H+_vXsB0(){OP!gs<*%p_%@JMf-e?ANU)_F&Cj(XdEwr;jlN-%Usmi&gg&gl$~ftc&h;@!i}*{A!H zlj@mBE;bFP+fe$^NGYz^yy<8KhMXubOswz5O?ul={Nl`zFpxjfAL9+tSSm z3IN<)_hT1lQ%e&PUlY4cAs>!>(ODk+lf$~c?wzDqFd;&>pK4p-u1|iG4Oerx(fQ^R z$2o6DwVDcdnBr1I(WZSBQ+CkTdv_~9 zb{noi(J|H`DI30x+x5l8f_s8Z(!yS;lNWs}r}0!SHyl?b?&Ih&9hU#le5<8o^ud}U zIgV>e1vBf|}dK{m@jKzaBKLj8%FR$MidwV*K);F}} z>Z;C*-{5U`+%je8DoA`__oDb;TO?ro8KaQW%Qd7=UGISA>?VD3xi!WsxjF;CXeOO>ai7qF$ce2L5{Ys~RctnKLtPui)YLcmW^x2k5dEG3DkDBeZ&- z2CtOX@sDkw#4$-AuPCj_SGlbHtk48Z3?+pMSl{sy&XdQbBh`;?`b+i^eyizICmj$H z&-`}ILEnwqe$~=OwTp5}v>xl}zM-{nm8u)o)4+)I>%pGdEs}UGsscU?9c{M&*YkK2 zvb%URcDUPpO!CQ{C3HPYgtk^YGu?^SnTNaM9o-l^`MlXY+t2(=bx#c*6_VNl>qQ%e z1!G*brd`+JKa}6y3Qrr(ZsKl4AipbpeC|I8#Ft*t-!NOq4{UJqOyv`= zUK>j=y2EZ99r7uU{F z6?M2@HhtgYR@{lfZ|mtUQYZ%3UAC$HSaO zIFp&!eC9r?e>H!tdS(EX;Q<}Ct~>jG3!np0Q3)@e=AuLZ;K!UZD=q#p$vPsvjrQfP zg$>QJ?YE^bwzy9hv#4$?$wNMKaR)9yX57w>^e8a$!uY>Pd&{V})@@sKV!;v!!Gb5a zyL-?Cch}$$Ah=s_cXxMpcXxO9!rkH4UTf{M&%Nih``T--epR(;sT$CrCTHJ z!JjPqiObTJBFrIyFGzLS?Y6enIVABxVWZ-2y6&f;%cK!MTs$^Weg)5e_-DWw&nS>D zzv6G_eTVMIcZ+pyPCq+%-5R56nL<6)8fWB6mtk7#L~{TsJSPU3J!V4nW}aO^I8C@b zPD)sM+aV!Fc(g-&UqM_5@r}3SRL(UoyI!>WjH*jn`&jFT7o0v)S(r5hP4FV4!^7F0 zJ^hAUJ>5FP%>?xs1fs{WvBB`~MVC19Hoa%%SU5G;XRe2XOlJ<~iOgiKqp@yDO;<)i8axJS zWAYZgn0Kjc%pdF$zbP6{$n+AC)@S4m*;pFRz4sk>QLkUUB12vg`O8BlED^;UOSTVC&cRK> z@9h@VqGdCh2Fm+y+PF7E>$TJ93sO@ViJx!i{G}5c_fs+VUsH6Mjl7y@ejKl^;IMLi zn9F*60{L+<1nTNSbh>ZlYygPzTPIwPuPCARF^UcMY5ydFC7oviCdSD?xQL^5K!RK^D(|u-P&{+L^?aV8ccesmG|rI`o0j{ZWq}OA?5yNsb`s}z0@sC zuo`Svy*c+dKc`XFZX#)C;vfms5EBue721dGV#M#;0r1T z3~Q=^AUWy1>-T>zRu@*tXo~i|Z?G+eU+RRDzbHW+rD2-r9vy4Ed4ZpqKHfap8oO5U zrYM2PY@PR2{P>lM>gfj;GS)`J*IL=)XD`GV795k!ll3sXX`WxoA^)OL|J>qrbG?HL zy<)vlaM^!*qMxmL5e)`ABGeOg?px^99r3C$;;P2{|Kp}E7f+fu7oet zx~9s)BP3N6?V(lYI_$gCW$kU<)kk#IR1wbJ`AIzRf#w&uA~HuuRNs2|k&Ypbkpm)Q z79D*>k0(v(mMEiT5ZrO3DXIQt_>#0)f26#NjOQQ3B4UGX9kLRey(&Egh_~AC5D(v* z<29~WUm4eak6{=5d9TVqo|eAjDm2MGrLN+;^f!c`m2jT)qtrUAnywPqcxbFm?NpM> znCc=#_=`o%iR=K-g#aTxKx5L!z}^!HGm);zbhoT$000;k_DCO`sbpF^`5^){zLp0E zGL!cx|MrM($(tNSNXj9U7@V|n2GLrbElzs}iWO5J+w*iSA$Ve=>V3l5)d`p(DM+oN z=3p&;E;sI;UJ?*a$a`T3d4Lyb^AE7XNU(^{SKRddSKar&*8A(NkHD%U?DQ(s5i4F$ zS!z`JKdCdz75Oux&eO$@l|*^WGGQ`om8&(S9P@hL-FF1OG6 zgCD6L4+nuJNT;u`x6evYqIE4!gvpH6Ruu)`-+SZ``tHB7M`Cg>wT-T@1Xs7OVdcd@ zhiJ59H2V;e%-^OC6vVRYa2uVrKWoBPvhMZs^0awmc7h*oJ~>+{VSz{3TL4pS~^^qy8Owjufd>zm2D`!eKgV=v*51??eO+72(a6 zH`BV+n|E`eG9f7-Vwl~I`8nDC+n+k9C(g8)pwuP%!wa~r>?Pb_vT_4U`fWy z^iKv|nAUxk{Xjyc)$iu=Q=W%<)?3{_7Wx_dm~UT1puS1Z&)Ig2&%@4ez*4o>Z28;t z)@EtUGqk7T$`=Rl>e`*ruCLCwf9UoqpnY}Xq;vFX?N6Yo?^DRtkJ^gTbXzSJCm*xm zN?CN%AnJdmlD^SaT@)4bjj}YC-T%H+k=;Mt*S@bi|JEPz$_EKl44OT@qk*4~%(@ca z`(;_FXBvH5U9XX%>89a!dkCX_I$Feo;$?Mrg(Axj&;8OOvGHwG3?r}hD&>TA?>!(4 z{~1g-oLEeTK><+}UrfiX^o*D)B8U7B=kQkVU)LWVJ-`5H?q1nZWMUaCE9qSa!r+Xr zA;u~m*u$sfAtaAJ>oR$I^(6SM`lB4cOp^n_KeKHdObhK{tv6xx_jWk zolf5n90I91a-nBcD`-J`bUnF@Edamt{+Ot($-OrZtahH-F%1nfsLh25Khs`mb+sPX zx)Rud_C(VxZ0ConSe#@)KvHqCRd9PE`II$Zz?l8ERI9Hr4!;~_`<8gYk09m7yOkY^ zxqUfoTaw5Aw0Sry-{hIapX!|`{OSQj+(hptajtmWKBgw*h z&`reUrP2zXc7AM33Sj9G>Ph4Os3K(9RFR9+ zj>M9c<(c;xUb{WGt2|^A5Rdi6E#4NZrkoPo+27meEn2ybmR3%i6cUg{1B5Sf6zw*y z&XE!SnYI6a;G6&aHZxuTOIu-X+~8UtrQ5V?FMoBoqwHxsBI#z?_^gX)usB-a^y6{3 z$DfH|?%yH4#bnxWzf8hNkK{T_5z#rf`RG2?YacHN6|}Vw)0M(CqyBy6YtHi!x}wdC zRY(I@yg+iZ2@3L~*L91u#Sn03QMh6%Wla5^)eju5?8KP?#fqrObAo;02Wws3Y9&+> zZs+rvd|@l2rFe3)>Ynqz=H?7us0X)S(nl9lyV`6$=q2;Yv^i#2pEmXh&cqP!b&jUm z^1|(1+24NKmnJ1Seo(J*l#@5P-FzT8k@&fO`&HoL{?>~TnK!@Ac{LxyNer)K@wwvi z(`o7Jr$??4_JbWlw@XN?dCb)YnMqIG<)OTNV;lJ)1x!L|uB&l7j1%tBQV)89m?C+C zU4z3=8va2E*LP~H@M!94(;Ati`%1bCkv|O{s7exkPY^ux_a)1L`&&#TTC}q5wN)0< z4XqTHwwqgRoj7v9{tW}PF2cj0Do2(S2$T{2N-Oh$x{@=uVgO`F#|Wn{t6Y6I*ZU#; zJ(2Ldp=-5|_lGAd()T7S4FsoXD0&DJ zhH2*BoJ%%J^*J-^2S`IT!eS>KA;ap-=GhwLWlZMO3`WnKGtJ(5)jeFwV>v!N zif(!wX$;6*Q~r929`^)h*Y*bQhS#?2 z;VYS8fRYK1NYVMRyfS>-t`%6Wu+KR6A4k1chi`>#1jNH{y+z+WC2-to-EP;2d(V;D z@KDV@z2r{eIBjGj05g~o1FOo(bzD>8(!gv@X!&J~I=%7N(a^!{Jbq$gX3Z(n` z%nHHFJwYPUZ&KVv(yyg$u6n1Nf$r_(Gmm1Vp3HjthJ4N3TW!573!ukejUCGzYJ)x# zt6zv_M7`^Jv3Y<-{ZW5*f+vZs;`xyfB_UZrwS*ERX5!wIc2NbFq+_~i$+hFQ3yRGx zH~k~+OGu*?8TTtr_QH>DDD&6a;aW_%w#Q#V#I+JJnsHkwm*55Y@Eiu78rXWdmio7~ zVu{WnX-?+If=T1!PK3F-lnGd3w-il9i{~^xonw%Wk8~to17#Fu+ymq{oIAA#x-hB$ zpu6pLKYW+Y)HMdDU_FhiFGm@=dAIg8q^-oD&*EwDQ$lSRCn5>W&X*6)sn2PY zuG+eFMWE{2=vCOe?cGy}_R%DV1G?(EEZc`&lYMZl4wc8JV_CUO(F$Knm%vb{ZsVJx zo~Q)xpnLr|0GLoprH0}Rz*|akf`bSs>8DM<1nk*s3}A_Be*rLMyLtF@ubi z(AA%H(g`WDzd?D`9sfdS@0eLjB-`kW#yQYX>&&D>7YnmaK0#ke98!sQ71qUPo@2Zw?$9xqI{TORudEAn^O3$m)E_c4% zl;CPm?slvrWu&TY{FBG~YQb1RPsXEK+y3J?iN#oSRQD#yW(Tar_Gl0OC#`P+2;cpk zV5KEC!QRlb4-|667DY&%Q3EYDHix=k68 z-4EhfZPn&?FDBy4a7)*qpS8^c{9ckC18){=%x}N@FCmm_a%ANp0BCpT`{O4@HV=Hy zn*FG4qu_E6KI@0r3dJ*;>O`$3ciHdDn%SgnwSs@_6Haf|1mxj0AEc`d&#axbRw1Lg z^J!U`irpvk>lOV6h4b@>*8_HejvmFWH7y;Zv(+VGV>!Ez@^6`}G;^e7Z<#`f{HkhU z@OG9KnoO|x&T4h3Ki%x)x)PVEGo3>DY){rJA0>l~=B1QaJ|j~0%+iTBGbc5zjXeyu zL9Nm(N!2yJe%mS>cHZtI-sY^osNskLVbAFpm_on+Uv6WZYw|a(vQ-63%zbA=Px|QC z6(TGu&_xucn(|k<^c;021P=!sOF1WFD7JSy8HdE~zoEspmeAl$M zMIEk%Qw0ym?9q_Xs9jE;kGbEHqH|1ooIIIK20>Yo^pO$y|7vQqDv&Ha6nFCr^YB9QIX|Ww@+_2UF7;h=6`kgaYsHeDv~T& zS(7XB?Yi_S{?58&?90so+s~zt2;uB}^B~&(x5U48>`V_`0WYc+ZH;@+3ymEex_4Pq z@yVA$_;N0ad$TgD;?lHv#7c3b;Ep6gvo*o>{t=K_;ei2Vfxj0<3K9g$LTSn?wNdZ> zJR&mhb*@X2Pvo7JF2O7*zx=}Fysx(@K+@1mf@w0oq`?9T0@lta!Nxl-Y!}PPQs*^z ztm6s-f{ShsyR9U^N7$SKCchD8N+OR{PRvsCLx zqa20O$GjX10ZEC@Ndm+v#gIGyGB;-g+cAg*oivy5+ z6&CuWNRI=TOL$HU`N3LI5qb!J#>V7%BN;jE269S>B@J>)ho_+cnYuVZk4l@kkFy^h zLQ!)~#WpyW$|Mo3@@HvHc;vG}R1&2*RBsYV7&04|6yYdJ8d?{j=0i2qgqIPHDzQC#5T2PzAH8FVv5unW2(mo4*wjIjgXxs*36*A= z6o>$BF|$2?bcpsR_qFkO)>^E_p(W6R4J-&eC5?krAzg!hqmzugmilg4tS@sVew^*R zj(|({WSH214Ega?i;vezu0~n$TgMR%C=TUR(Z8;X-qXw*H!kIMIIqVmbixi+%H~U} z-BQsjGtVdZ8LGkf72)xABvKlh3`64Oxtuy}-?QBPC6A5cVpg<0y_JLv0ID(!zV5eO zN^w_OPr{jNC7SC$2einP>g0R{j*AD$YGQapvGLfk+xAE;- z+P|#5arhh~GG%K=S@7qGvFyC>(_THMv1J){?N$(2jbVJXWd~0BS<@|F%i@W`N1GeX zUS8pHsdWyXTJs&Xt%FHL)X)^)v9qGhJMhkOq$hnKRt@oN4E9(}Y zt;%iVuS$9(#z2zTpS{hNlnEd#gdEKipvp5bVSC05A&Qydg~?MW;4U!k#bN9I7C{>FyiBXsEre4Pu#K1D*rH&R0ieYf5|O&gWUk z*l9WZY#7UkfGtVAJMDK`AYiPGWx-HLS*&2*fb{4{sPM505jBdIL=j)ks+sgpNjth9 zm1>$&QrtU!#1wI`BJKIIrXtGkl%fN}Y`0ephM|<~xb#eo`-jAHS_{|mh(>N$!>hvn zq!dAe{fCSnN}ZLv!su@1=_lfXFo0)NQ_wNCJOKC@PtEEUGmU9Z;5?NYpt^(hZl|L? zPM0tTgInqKWGaTGD+@&=tvBkiIEMUN>CNXr(Fl+qo%o7n&l(hIpL2>QAHZ5+_iB&i zb-6~p-}6}cvCx3d)2TWaAu`=d`(3#G6U}0QO+?pp^>WI*aZz-dKamd&tsB;t^V^%9 zZ)KaJo~BpZr+ZM}anL^jUxsojmg|q+yO@k)p<*MFyIt-Tp3ycbA_2cQ#3mntuaaHD z-gG+3>ygOM6U+Vz>kqUOWHRoZC6%zv3{`EfDmZDIGQ`PnINJosvdF)DxQC}gcs*3Q zXB5#lF``11L7Ozf(`@4vfc1K>iDfkNvN)zFV#+Kz?T7IEV)2C}kpbCS9m#e>{#W10 z)jXkIvxNOmegcOZiTf9{+jb^=V}IZ)OouQg4`qRKA+tnkKAS@+E(JGgJ;r+&Av@t% zEuO(7Ny-qnosFI0hC0+~(HTXaq+ItHfg(W~O)S`s34tGW9@Qa&1V^Oi!gKcAUJ+Yd zh{nK2Ic~jAENIgOr2%=sFKR!$_(%}us3U-8uuGBC5%9g%XZB;ngew&$HUj%cxKdmQ zPvfA4eBR++bdh}SH^-aWtyqOi^ujka1rMm9YzJPWC2qP7_j_vi^l5Og8zTS7q#F?c zekTRM1csBK=}}f7*1X52GP`N>CVaRUg*_Ix6L4}JA>ujFG5F?f1rH-e7%Ssk+tkv8 zDK5+K-)lX8U&CTxDlH};KX~PmN?mA=$imM8pVcL)4H9MsoA~sMgH*D zdG_)I7L{QowH{fwc_T-cL$XpE?Qd`k$0*HrFv-^R%t^|j2cYvIthVe7r;qJgcTma3 ze*)Bg84ke~sG8Bl4w3_MSxPw6=MF?nTEpRW9`ox_mFJiGo>idwOChrg^PfLZw|L!G z3*D2riu-9BRUXp^$mj{NgdrjX+(r$PP3u|=lljsXCRs%L|E&cGf&7wtsfMVf;uZ*| zCVQg#xv`jgANP;zr6B+rRzq)lC+G*hmx}j#bVQ@kO1$1OlofZV+&84!-?32JDv@I% z1M61Zgf;D-TzF?b8CBX41sM+$h4a0vKx7#Xr(;TE8}}9$>`;l5x7@3J{(|OEsxkcg z%gc4|z5)MxjDjMF{164@GNC}e?10hR#O4m`4Ngs|yAZTc2AYrT4#^A-dlh9gEveMG z0{D`MJ&@lS+feN`_vt7}VLi@&C6d#8*{ruyayoe^ZOC#iJ=nN|K7)pOlmbxtsyu@mtvGFFE-m+%3P10ZlQvOFe@Z7`dAd#{D)PMafO|2=8l(*J{Q}4Kmbcxs=vU zO8DiCN1wvq!*(4~LUE&v=4Y6yyYnheyLOol!2@sx5+}?;yC)W!Sp8U1A7$_f8tu=G zGP1T_!&TWVV~S^mIJ@-rG~mr`Np92t;N|2Pue~5W=*^B}S7cRcE>24|*SLCgNrLhh z8ZorMkbICu`SdcZiZ>n3qawIig4sS_guDZqK&;7PX zz14W=D}flLW52#Ie&f5NZcb_qF`)3H3V6QS&eZman>)$K?|SmncZ2?x{z_k37*h0| z`+4=UR2lVZMlE?VS;~DfsW4xcFmT{rKH`%d76g?TW%P_|K-_l&G@y{2<^_2)`)`=a zQcGa-Y+_YCmBN5X$VAI!2U<^8n9^KJHwvDlB?8ADhL%3!?S~ z^;$wN23?%C5Ps0Hgr@3*-CSk~f})h+x0C)=ko+h4Yibdk~Yod?E7?|NMnk0-M4MG1)E_u1i(LBW8VlG$2bh z&0%zy7B0(g$`rygZ1rcsv(-8o{G}U$J2T7`tqr6lB!BY+H|sDY;M435QNwVFAtSii z{dVmILEB6`NkK@H(}hm%B^?`?c)O*@IIGk*Rq;)jWEW?~U@r>ks$?VqlT5ej%_wHipa1V;y9`Rn+1WGS=?OPpY zTS3UCGGSM7v!c?{K&=Ek6!kOZI%B=3^T2E-%Gu^U@^43@i6m)2RvAUnxKxdOyv@rj!dY8&Mql)?Z zRA{T$lpKRrqypJ-Z+m)Zb0`+s=<(KA+{eYlB96ZhlB0lk@u~5{oBq-RNq*IR8V{c; zV-G0Ns+=kU(^_ZTlI6Gla57mH1rrWza8S>=D5ad^X*jA=XZFeRh-NkW(^0Cf*pH5Z zUc@GGaye`B^}zRkSXsYiGy5ec8!8l zT?3FI&uy07tfClgd~-lax)`|-B|)CLgYEW{$Nte99stBAHJ6DRHsLQMtUyr_%VQ>~ z+B)E_u2$|bC3OXG>Jrn$C>XH&Of@CreUws%fF@5yu{aBLlw)zbtsH?uM6@2#`bHH} znzN~M?^~*)#x=fZp*IG|#CSpgx)Y}uC+xr%c*Lw*Tuc5ClU=G$GW-|5w`t5m$_6_% zg`k5O&R%^CRzZGfY)Q~!mnbJV%atmu2Z$j3_GiY|3e(+IftKT?t`!MAi&q)-T{QP+Y7RHsS@75`1k=%Pe<;w!+G>SUiA=h~ z@TGCy()=~w2m~MB;D8R&@V_ri)1%9I0Pn4CN}9K)!&ewy=$#KbA3ng7_T zmh7VFZtD7+_JU2Mmo!y z{IsxfH-Hb66#gmXMXb}aKde-u@$hoT_gv*Z-Uw@Dv=6ONIzt|QpXsp=kAv?N@@C&% zcO%}{Kx&aN(|8s{uO}arvoW(z#Jl9kK-(_SZyHohkfz0BaO}VqWSaCHL2wT_{bITB z35zmROZ%&IOU+?6JXLp^YN5>kXQJ)qzeM6J^tDMb6MO#sG)C^!v55+;ecyfX?CaEG z*9?0_#3Ae_mwika_pNn*+=IUH#wn`PO+ZeIXtp(LmPtS4q7rcZ&BA2H3+Z8IykQ54 zGQ0;Ml*Hj60G%AyWP1$WPzoSxY;w!*!E$!D&OKk-<;PZ~c|K~4A~Nk}O>ZJYpc7}- z{2|=!-})5+xcZGJJ{IT-wCY zbul#V8ek#Y<(!hR8t+=Q`|>%?OUecdV-5wc4FwUMAcBtgcBX3bsL`Y zXP-@PJ1&<=q;`7seIQNW3&KCwIvAbt%;z?Cbgk#d(iSDuVx2(atR-MBS?8izJ&{P9 zY8mPOfs!dIz2>L^_Rn@?E%gr}m~8d+uT@b_c-$x#^uTRQE`|a&>eK7*iX9}#vye1d z58t}L*iOq{bsg=I*$;1C>(Q9AAfw~L`i?EcqN0vi#OZQM!SinAg1 zxVPO&6ujK5`wQ^|zkU${aNH)9RZ(;r7A*ULxKBUdC_qm zg@bg{pZ=NI@9Zs3O{hproh-(-@0&eRTR*=x>_|HO_FU*%9-sAt!$){4WRl6VDD~!S z`W+>n<>4wW^WR84e{K7J2H3e(LPMB!4-KIAqSel=Q@!aZg(m>WQH?4~6``^Q;;QY1 z^#@~z{w%#wT%Nk(kOM>1=fU#fJGJYv!vv|1gcBi= zv$AT20>%z*nz7!`f{y5p*s9NMOv0JeGL%(ek~UfC3gH*e?8Ksl)G`=CFKgmTsRqAa zHh$H~CA!#rV_J)btfkV9aCRF#=*t3mPawUxw0C6m9={ea01TJb)E z{c9XJd_{kxyF6H0oSF5ON_f)gN71urMRYZKv2s)uwMqId92hU$vcfVJ#0)G2|K(~41&wnGM?sdXPn6#og4qp($Z+CFv3B^RF~ZDdQv<%K-i zp^0r*kMAF?(!?8KCjXWB3RaM)YF*vI1U9&%nt@kzah91l+t&{U@AjjnL;Gmo7za{F zlkQXHMP2P!)s=7F>6w~kj_&{J8^xUrfRM7=|*!|&Idzn=s;F4JMlcxwzw^4;D@5dYA*I@ZlJYvPDwFT~KuzoTs`Eqn!ts%!Pv zTOMwGjJ6l^ji&Qx<cl{0`b)L)+*mR*6`FFxsooKeN`_Uq|a6{FAH+! z+~K&7FrfO|ma}W)Q+=q$hQ*}enjIfkPIj`?5QX5{W$W)q!w$C0`sv2Sn{1rt!{p&X zMS|y0d{5g)Wi2l4QtH_UJZ?YJl`QsRILQt-L$jN2{f9XO?LIdBUrDWbfrA;Vot{n2 zWX|MCP|1Or=qot7uzH{Zee0|4P6_1As##8^jX-jPbj6k6*Zk;0#LcXc36bp_1>UA! zhQ8UaWNQ+5*pquhf!T6rnpK8o5_x-rUY~S}qmW~eM^z%w!F>4qpdzicb)zjEQwN%E zs;&2hW*$0DS-?S2MimdVvMb0(fA_c<-{Ro$Vi2=BI%+1~G=5;J>}Ym%X&0U}_FyfB zGyP||+5Mh}S^m!X5#sNCR)U-xPX(kGM@E6`_z6~Zf*Z#O+?SMB*2>)`QZ7@O%q(O< zKKufgp)ETG((Q6i8got`74W1PqXRzPovvvKK_t8V>dp)q;(8ErGg>}D7c>c!Sbb`50;1?+DAnQo(H?8%dD^OJY*Md0x*Mg8kaVP1@7muh}^=w0022UDt zN=D|I*?661Yzr5uG~qN~YTnQFb%yb|prRVJio6?WC(USB4w#As9iuV8;WpT+2am$-!j zDKxZX-kX)H!QGOu4oEaT?KR`3$2$mw(%m4v-ZI|RQY3NbeilY>J3IifzDfZDWKg$4 zaoM8@ELBYt^|4|o;pL0c_3yYnb$^%x6T7?)vsDg7bUX&T(_aVj84ZY~%f^^wSy$sv z*LkabOE}MFT+SP?qFJTf95#FZC|Al8lT`jXJ~K6?OIjbzi_EFY(kkVu2RYiFUABgc z2X3aht{I|2y0qVZ!-FB=P>Q9)0#AabM1X&|B>BGalAq&sIZrdyNs-0m&^-2K!!%1) zQ`3X${WX;0RDpp^KdEGL4Qq8(21HZ6l|}qYo&y&=%tW@jo_YPN8GdDXHo@OVVV6(W zG(1e%xx3XSRG&?xckAZ>F>Q+>RHyGJiiygCb|Nny^Cgy{SvMgLrls(Z${nTJI(wG| zze7y+3kl7SAm}h<|3qkHt!wFX4^2D*0F7^LT|-O#>(LH`eY5MzLk0#roX!n1xC1g? zMK?%7m{!-#tzc2w0TmMNM8EFxTv7<60bv;Lvr(}Y!TSV`zL$|lWQ_mU4Q>;1aNO+-8K>8vMjRBbYEc}R#uLX>j-sjY4=vW?UkCmUr*Kkque zesl1+FZTJk8@sK1xegz|+)pQLu9flCJSRYd|Lh>HmfOsadT^pBw-6b=F98vGMS`KJUqlNmmI& zX;NFe#cpcGofNs2V#p8mWzIdaV|5oMu}&Lgq_l<6Ik$7qP6xJQ$)BCSrLpx3inW!Q zAUyZyvk;5JIBcGrT_d$jcX!EtAjqR;gLGEF9NFYvR?K?-uNc=xizMmzwXK1h>DTb2 zW)mg;;MZ2{QkNKutEK$xigkws*3#8FOY8Tw zlF^Bg<9{gh^o%6ESuf|2NHRyg;%z~A3bhb~v!j`lvMiPZn*LY;)-#9rM^wz)w#z!OsAPyKU z@m_4!yMH19tYwc;d6}nO$ZnT?6xo=o6tJFxvRg`D(^*hh%y);^qu+9x$;AjBUIJv> zNfpLSIci!-#}z&BY96c{t{`$k5#WK326780%XavZU5;T+d<#j2@^xdMHEVfyJ01k~ z2MC%~?yzuvIa!}kAuK9k;6*Mk;vC%tl*iJYg-=j&g~+}%c@ zGd$g{_fKO*cVo3LkM={)Oo?D6UTkGW>(Y5Q=WdoMcAUQ$+gE)S|(CG7xGmq2j6EgsO zwd;lr(*v6~G&zS=fn}hs{b7=jExzy&k`mV!cX}+Euye0MtuW-(Loc6i0y8nEZ7+im zHAF3)P8%$us0<^TN$JtJ&~uXh81Mg$EJWD(=h0%ALsF zb#0Xir_L?i3+ed6(_zmzQvTsufo^9ZfuDgQ&GJSNx)@}rapre^MQv>Pb_gh!dgR~r zZ1HdpgqSYx03TAtv-nkZtfJ5K15Gb~A_^GTq;c%<$5D8 zrx+o90NniAaHh=Q-z~DA}j$1XjsSmY0 zGwud{BQ$0eX*xUIp9*i=dl*fZ+!at%^L|D*K4Uw5o9kI8XgLjao`9mDlAG6;FPajG z<)WOZYgNKsVQwuvF65yzqop3?V{~ZZLUL2H$sf1l7_+pG`M&$>Yp+D9JF(xhCA&_~l$jcy~U$|)OLq#_a z2}fQTm;jlfRfKK_Qu0u4A2S3yN=`9e%4wRfcovou0KoMlnMagL+>^GZ#ox&aedal? zvzt^1*s^bWUxbBxETX?3vmzDup)HkDrBphW56`9|u`v9mC%*sg_GSYwbV`{F_%~x> zdb291+10H=Q?42eL6dZD$AG+jT_)XcK}XG>Xjks8l@pljEjTI)ee&*K`%~lC);rCa zFiB;jvQti{7^8$FsA^1B$(b@EQ7jlC)GPYTQyQtz=gmPDn{nK5PFpiYOsnDz3wxZX z>h%Iqo)+WNz%K{W1bHm6vgm>S?dokera#lv%$S^~2kF$9YWYgS7MUzNGH1%6AtPx28s$J^D^j^6um`Lwv=fp|(pvcCdCHrxGH#TGM~kpe^2dfK}$re1;Y zimM-ogowdvG*AuQJc1Z8%IpV^qa)D#;s~ZD^)^Un#{K}sS@4>^mViI0Ht=JRT^*|BHq&ncr=*|={9?sV1<1c zv7h*P5ovh-_I896uvtNa9r@BXS&%WRcb%I0XcZ=+ZL0Q^#h^hOP2MMypPm^f9c^Li z^|Pa1`UI*TJ6C0>e%F%)TAy=e_VD)T@K1UGiY8lXg>U3Z@4|}ZQxx}h&ENvQk)ySc zZ2Q(B8k_b5#-Aev9sWEmXtMG7;DEbMk0>p>ac?aY=WQn&%7jT76O}SI1OUdzmc2zGD8^}Jto_u# zF{{x)Wa#Hrfi_hCMK2h+Dz~>8w#f)KJ;Ib4^YcBwaQ>4|y(+@PkheG4kCtW*a}BoS zpK$Lt*uA_CS90t^QembWmM*J^ls!M5Px7$wnlCEuvFL4M+|(zR?FK=vrPgN@#}I;& z!Nv8UOwP;<>A)b0&Cu&B(jq8i`Rj2hJjZW;osXiTa~8a}86ZF6mkz7rgfmE(zxG$F z!c#A?a*G8lmd9d@w4a`wcXUn|7Lw`?^bVGbde=39s~W;+6d#`4W*Im#fAyVlGo`hD zw!Rtj_OW$>M_GQdPrf|-VuQVKe16Vm=G+w=86|)&4t!1@<8h5=4)YrHpB@-9f zRTPN*_~cMD7uwt^qxV_tl3VOuCWCIiT8SzB+D4hEvpLf-9rtV`Go`W19BCSVGZ&M` zr)aG{onE($XRWA;$#`gXa~Uib8o;^(d(3f8@DH7?OU`3WBCrxsD2lcSS#lR)P(%oLV8>Vqt>gV22mae)Q zI2%)PHiYPwE>xF7X+1siqg~VtAkXNPGH;f&;e`Dt%=ZC?DfIWMaDw{t0`X#@i#_6p%QNuz4k5W`{VRGAKd;O~7=uPkY8v&K* zR#MwH@h-azm-{DCadiTfxz*gemJhA_v#ugGrf>4=AJfZ-p6=|78X#v+;|CB>x)xw0 zG%@IFAJ;(x;{IJ!aGsq?AOBAnydCYY8V%iF%I)Ce1alOp#M?{h^W=XUV!v$dag&7- zc^en|^orx~3D|J-UCyxCILfK)a=g{57i(JB49OMNua4JKJjz?vK`^L#ssGjjP}U@E zt=v<&#D%<1Yq^qL$cZ zQdc{_f9+izCc8Ukp%Z$&<&F(QOL=td5yheF+l@a|k+iz)o-WQ0c{vq9DFj=r);-ok z#pCA;iq|+RY0Jjox$KUkqdrNlB=)tHaVk(WSK3~Wu*DOu_3F$enUT%N7uQ#(}h0LLLeZe&W%Fd@2)#(^i(}o+*`$q%% zNp?BwG!ikbwhpS&yNg9aI+Y^2A|ASpp6x;Ieq!14$^)o$=8wi*L-d;9jfjP%TI(H1 zrvm-m)8^UyKN-8vN9S7TRrgRT84R?ZmpSpN4CKqCoAuwu7wNEh$ksV)TZVs`=!2`f znvIVkeY66sEN53z-m#vXuNCJmNc;o*6?wtKfWKq3oXukhHzssAg`3x5`rDXNpa28k z9zuXl0PU%d{iOTfm-`=J?0;Zc@Vg0|ObF?k=o=d>9@U2ZVJI0EmwWY6{tjkml zOqwi=Dx<3pCMZ~MMVvB!3$LSN{&*vz`QmCZ{&P={1jZeCA=voqG%|hjkpWfl+=&x~ zFB!ToYbJ*lMnf@s2E0;1a6HbJecN(BZ6Toi&**JuI>ttM%yWfk#z&oSJ?_e@F}n%k zD;!gC1db_gjK>vVuAE7KNI*n*DB?+Iker_Q3dxP+zg{E-aOWb;{c=}v`q9mV88gyj zlwlCfUzy7?if(Fcf$oP#p%llK!B10j#as_I_K+!(a*K)2^$3LgH=sy~g){($POGvI z8iT;t>D5~e3mxsk8YzvIXd-*C#PIMXx=31o&F%<`I_9k%>MRG*BuP2WjMF4u=_ce& zHs0e)gU7Z$-;dRID4ILRhTawf;Gzq<>W~gzZ9mZdo0KSz>%>BcIr8U~7nSiv4nqBDf&i#wz7d9(rAlh@ z9QZUM+v75jrlH=CJ#L>@h3zqK27RJSMQDweC9JwLThCVKUpv*@R3k$8{NK)K&`WaEw>5g;|ldp?+OlNsH2pj})G$6z*DWVKr9iSWuh&PWX#5 zK3}5t81nORczN^X=?kZMXx80zpqpA_qC6-i$Db|ibctY>)#WG)-%w*O)2Z5FZp0x2 z&%acSYtesl<-q`SDGqA43;O+#T!ZB~FrT#wXQM35t6d(wF7JCrHKH_2(@B5JQ>tbk zy~J^89IBP77->~Z{;F13^d77D|I4&}d0*hUdP)tVdg}uN?YgDR=et;P8L151^#@@AUyFMiu;{ zm;+96W|$t>a1r}tZ14Z!f#5Rd(`CLqChYz|fKBW9hQU1ErwZ=#EUl2Tu92f~Fgu7% zM9AM~({mpthj2Zp&ZnsoO$`8b?Cl*1kbXqNK?olpG9+?7@peS}Ko*b1&kKeB8$$~z zbI!cmuwo)LPkB7;3Wk9OJgcseju&L%OopYxKQF7OpeNoO>?X8WdN{gfH4VJO8)EJlhBmXiF@hN6xR zEss%^hQsY#vp-$Cwakh-GIkr*o+3dmKFpALI$CNbd2?p6XdiSl`vcy{g6&H_fAdn< zY9A^?>>J|^f$8*5^Q}h$u^JJ@sH)_ho7YcF6!J#_sK?Fe(waAESutBIuM@Q%{U8K6 z?q>U{qD&>3uVtt;^8a0c&mm7mCDhO*Dor{svt>9(qdtuWsCSuiY$GVR3nwojCzI^i zEIq*vC5N!0@;>3QD*Ro61m5k*{;yzOuX~J1atA3l;I>rQy@^gI-$Bp%@Y8r`|CJX> zZyxV!`*V%SoA!mT9b0bpiQh|&yCGsB@6phhq$uH)Nxl@SVW`RM^@|HyxHk2=>OpZdzEr9!Rcw0vNZu#$|S4= zh#}~w?kD^($!dyn*wuI97_GjHc6PwTJXVhV1Z4eBm3+>SP^?tEvV*b7e`(ScQW;Fz z=d!agg~eeoklu$w>eR}@22+O6fi;LvYiol(1xR`RLJQC#PI;LBSs8DV0uTV8-$_eE z7VOxO`yA)m9y~u$)}^%GoZH9Klx!h%F1pOQ#DE4QN5u@aDL@bz6M4M+b}Dv5p>S3W?ulshAGQ`ypvY&A0U24K=a1=7BXhw*XLT4NHTp4VaUfCH(cE<~U zNAX|4l~fFSO)Kc;QXMwTNFrf(kJZ?5Irh%N6v9!`?}OP;KKn!C%U4Rskok3H%_;FplLXZ;x zgK&(4ZxfP%!fh!RG;9Hx3-HdA{s540(eyZ1(t<+(czle=vR3&vgG6Hq$f?VO=ps|3 z67vQ9RKLbwdR?7ae{o9U+j^>#FUYT?Y#YFVU{`6*E(=GIPWYDRD9V0NONTJ?l~5eR zpLrVFi*u_^>-5RQ;r>nf`lbF~bj|~i;qT98NyoP=3kDMgP*kz;q3^_thkFMx`<>ja zMO8`i(@=GOhuj_mYl^hKt~~Nmsf|cC+o~eg2yZ0dme0`zKNJK2;pb<&J^ouV1Z6S2 zPDdU(J61jDq#hjGs^t5kz5I-A9tFSfS*?rF(HF`S5ULV2#_Jy1irldWBEob~f}wL; zbH7!s5&imK^v~gNV+D5~{5v^LV&d_c!ag!?PY$-q=)^UUDpKS>j6)Lv0&%K7>HzFL z`fKjXOkHDvHkb&mAD9ve)MXmwNL{qZCu8>STF!sD$>zr31d=k!jcpB$eCW73__NDW zocqa0bjuj2|2OgpG*iI9U+s?bY-wNbYth@k(Vi$5&?g}i@fW-{54oc}G;u;+oF8!i z?Lql&BleT-|A=s5KoBlT$g{boAtxu$${ou1aCfr}KWh2)k9^%)Ug7AIfm(LQdh@#u zwx8ccE60C_hzeu_#SBkblNmH{1)Cs58u)zcWI2FIy_Q3S zJRaWOP|L${=gTDlvrlTg7WO~k!9(Pkz%Tcsm&M{u8ZMYk$Y-WahUxzuESXn0WNm&I zNGl&0>ecG)I0y%z!;FCfSd}?&kJg8I_+_$HfV$>nJRTp0)c$ig&wL>x}KW+h>-+y+o99pm$n> ztubNC)p0-!DqgYQPk@P_?SAUzNq=ma8yR)}h-~P>TGMi(B0}nPmuX0dnNv2(aubW| zRePar!n`euc8w6V!f?2a0Q8dU8L8YUA?v+FY8+uuhU#+_qeoH9?glz7>xZc|L3ipF zK0Y7GqK6c!r)Hm^BQlLbCep7yCHyT-`D&E5w*{)kNU`88FEb8U|0uOJ#hTNjv1|P! z&QV5MN?_UUHKPxW-XD*c&hbzcM-$0FfTY(*nmax%?iaLfv;Zlj15F;g-q3I0H?#!& zv>$)eX@XL4cbw{zhSq)-0;G}3O8%tLO{O>){7vB?{UpPiS1|fexCiu=ko2e+{W+#~ z9s`O)a^A%c>C+4Kt^Jc*-8(+;mKyFw8=+#H7&i5%*g^_dyh)k}Q%Ql|NqSMFl$-7AWpr_MHJb*L0-@CcRG0aG7+f8Ou z&V73tdz$JqF;*#F20w=Of>$w33_||Nz)xdBPSIrszW#Sl5ofpOt#`&i9DqEE1pwog zXDJs2isIxIJue#gaTos!+9{smZwklsLpHCy1PndmIu1d`wpyxhEMpU{>t{Qr%t*i^ z+=rdXrLBy9fJByk^FK=BnK923$>YCnH^aM*_;C$2F07OxCe`sAHkjtK`FZLwd`MJ2 zTkmylYnSRc%P_)qUS8*i9r*D|Vh41R)&6wFIQ&;iYB-pZ{o#ms7mwBkFgVKL(#oaI zVqGF)6p1%$wvNwZr*8N6Ql8Sa#8{7lKF_Zg2(iPXsi|Mg#xv;KL^YSsT*J9_{Az%l z?1_IaY=J#(kjz+_Q~Yg)k>vvAflFRQjPzsTpQ5&H)95@58))nP{NDU@v9qp@S~dj; zfFfy;d5le+aS#*ze%oXZZj8pS5{TrUbaqcx^QAX$ZK^^u0M_ zgUNf{B>~%(GqpIr?c5nRz*fKtGGEINg1pZet^+rR*>Zf@p(30KERh`*-rinQj(tb! zxXuZeSt@+&R|-P1ygcXfbEbSL!9Y#*wDeBD{k?<^kH8oXvbEZ(%`;x2$a<31+NR0? z>sQsj_b=9wv;)f>AH%*=X21cctF^|CudM8Ld{U3OHw`_$%9W4P?xOOGGGN9ihX zvaBO%M3bT)3h#JY8nz4?x2Lbu<>)>h^{o0OEK9~2TQ#}C7+*iTz%Y$!HfK?lh*6Kl z`v0?D(0~2@WME)$0CQ5`FWl}Lk-eDc9?m5Qa+f8XDq@sug00k{_|d{>MOGnnY?$^? zRsG_=BQaY(oQsHx#-%7Pvpr>Vi-6)aW%B}in;wzBB5bw(v}8${3U2~A>p%6d9@x(T z)w_-X{9+){-fGMf>8xS;u4Qhmw@lHvK+7|!$b{|S&|~t&fZWSj#WtgEFZC7>YktTH?yY5P^xuWp~~{xW1=GG+v;ePghOwmrVG3r>r-7eEZl)x zqRso_R8*}&ZO)abn>4ieBFTlemF|^MN4Czl1oqmeo-Z|N?9o+(LCG<@ngO{a~J z&a+lcNG`=83FC-2X2)ya)oRTx`qQc`U4PtKnusT7)LE|P-$m}0KC_&}=ELQ%tkaNj z$qDd$kh;O5zonhit!+Fyy7Ym z_c4-IpS`k@z1=8l&+8ew$DLY_aoLO13RMItQ&?6h8Hn9;Dmt4EZnekDMB3{}D#!0{ zvkutkg_9^rY{8IO1gL&;5SB2ADiex)b$@+{f*eIEV7b){%h?9{(e(g6rj7Wd#tsz> z0K~HLxb)ccCK!uBzi@OyiN?XG49XJ`(saamRJ9=$kVpLMzOKrBov{^u>|S~Q(F)b> zvL973PUAL|E~U^;rsZoejX&ae)KcTd;%+(iMm_v{YNRfM31!95?)3FEDKuVxzVkIz zPw;MvR#=dLmVk3J52(3Ja_n?$mF^4VlG{1@o3i!6-C-h59p?D1(r5o#jNSXH9d4aO zJ}xHp#-WXbE;}9yCH?t&$_3k6s#CjxZjezv^KItKG5X-O%K1xa;N0iXgcor1dP?cU z>C5f0Zjd^j?~F~%D`Xs$V;!kCxp3KBBO&>VSR71zjE~!UWdZ%&=qa&gMTfT4{A%DBA zI3{d@c=AwVeTRlhcP6qbv%J~+ivQ^{Ch`Vzh!SByjjX-+TnQgY#!s!i7Njaggx>k( zxYLCP!0)ozB&$MTJVk07(YTUZF*U>cewJ=i8V3%1|0%8`J&qa&9lK_%uW7^M9-y3k zL@H^~hT%R8s+{(a=`72{xllUCiY3E%a-!a7aU8N2Sk|-cqolPWBN~%&XLNtd9ARKl z8nJ48do(l=f(*D1(hshW4GNBrSRId4KxU)oENQgGFIRPnbzaZ^jO{~PjI-}#)Gs3f zv6vA_nN^YwS&}EM*{MnKS)~_mo9$6xgXhr!f z3Llem^UYQ~1k6_B=Ukj2xpX5>3;Vqj2_`^-=a5Td2cMfq>nirQ63qlH8*lNJgvELE zx-)mqjWOnj|ETwVC^$vQf#_5IKYvtsCj4qHx)V%5q?P8{r#^LxS#h^>zaEX(viyc1 z6X9^9zD|bm9>*sRbxoEs{)(NTY9_O7F!!0(dlw!lq%(sXd)nLyJ~V%i*E&>fVgcD2 zi+aw3Q$oCa{kG;Th)WRx-uR7>Vx_Zt$tbes1)LFPODY!uc zyxqVqhYw$3Rjk5pm;M`{y2?oAvA3Em>UN z&Q28#L1(Z5t{=xb-Q&M6&&UK;5T;uNJVjx}tk;S)J=n!y-4I zMAr?SzK+hU@Q0uGUM8Cnx~An^^WpYxe=0MR!}wav7BlYf7JF9Y!)2WCf$=Tx9j#N` zaQ#8M!>#sdJV$^gViHda{pC9dl+E;;1{qheRhNq~b)WVta~{KchT$@oYFq=a5a*HJ z;{}iJkvk?n6z|OWbH!0%yL<>kmiGJtn0Uyq*>P?)J`jLs>Y^(%|9%tElFL0f_;Zld zC+nyZ^Op$WK#z`o^+(yJM5fn%nQDm%Uu@R2)rWr7hE}AsMeLw%mFhV@$1gX+uiym{ zrP*I9(i!}hGPes#(Tddu02DyAaztLE5ENjeCD-T92BumSs z)@FkHNKH;#YE#XfoGyzI*U?AHkMI&DuG6o$1r^CS zgZ=E_Zvd<6^Nf^y@1CTN@*8Vqoi^#BU;);Ukc_!Jl+r7~p17)~K)Tp#N1teAHs>F9 z=}ljn_Li$Tt||+EU<8rAW(cL=;wp5#7dub zDJUvv_Q&<7FaZVHv1`fsT?OoH@Et8b!2f-Fqltji>Qq%<`~?XXgH>kJWwFYO)#|Xh+cd&8F30=p%?)6r>H`XH*y4RGC|9!oRGmT5wCrMO@Tz zzs+TiN(w~4i^kBWwss+-E0r09$sdAx9Cx=>P&!3d)hh`7w1N4)052AER@8X}|nP2OQm|n6G zQI(AVBJL|>wH^2PUwAa46B@6u>hzoKKxgt)G6x%&Q~^kv%Dz^Lj^5iLQ3S&^a~%2y zyM02Jy_HGZGp9)xmu7koKHVE3O^4C|ks}8?JFWL0;msyY4$G@fdxEuRm(R(hw%KgV ztQHbSL)@md5C7Hz#Fer>#?%>a=gWIUW_hoR-@ke7-MA@ocjON$x^FErF?@Pz$`~W{ z8;oqZM(YrAI@4^CFH`yp>k~rjZsc=Qi&N$CAXr7zLJ8{KP}Eb6%| zm?jmYCeSG2)sF^nAEKCI70x`r|GR1nE~T(pQ#D*71?$N z;0`+9l+t4{J+U3N{Csdh5m)ghf+PXdPzHyGIAovPE?C@OYr8rZG(GV)zJsN;P%J;P zodwc*o6#>jt6|gHhJR?5fx(BGMBd;07$DILogQ?Zyx4cpnsv8;&t$4EaIZf012tAW z6dQzd-rzE~3WXw;^{p?|@^#Mpa8##CKle7#-`W|EtC8?1KlN`n2_U{+yPex=ch3QX z8nCqmL=&+R={{~afVg#epqV)v{EEksD;ojYSm7aWe-bVYC-?>Q<51&&E1Q>WA~h!jlcij@GMF)6=C(iLTP9M8w`SZi z?s_D=L|P_@$vh*92cEgCc}_B>4cWc zmLP@l0rcWIgACw_kaTaoEWVgb6z!zU`Z?Uq0|YJYqieOgI$YH3pRIoXV#{~bj;2x8 z9N733g`@^3@?Ada%$NCBbdS;@MaYOGAQYR`JtG*lYQ(m*>N7Lr{NggwF9}SlV99Vy zqw7FuGe{nxRPuIy1@-QNpHG2V}hRhn|rn0y8Y zOxOv?ek<$VwS)U>F`eG*k~<}QotI^#i`t9^GvbP4L>vZh_zV-#5A(KZMZ5s?$Oy*u zrkBNuXmy7Ka{U@3SMRGYR+-EEdw;H(H_$vTkKJgW>e^k3K<|#S$$A;?6EA~yNNLS4 zZJJJY9bE}r^tV6N(|Eb`4QX2a<~8u4_N@M{O}|3jRzN_qe3X3_6Ck3D60ahyxSbNC zyF3n`pA33URfOIXY|p)jK%oX;n3@TW(G(V4ZWE?76@P~zL-TrnvyHB~hup9cx>bwc ze!<7NR^7IWPLOW29nnL@*xPv3@mV!`BRtR+P#g&Yv~+~ z!%y3V!TYd6oqrLy;-ud4#`p}+FLLu|y?sFpCTLP$BrzYtDpnwlib2B(su+|4^R5Kx z%yunRGd8-&{tkJM+@BH0iSIlfhNJ_-cpQZ@Ai?65vdeNEJjRL^()8lU13ja10P0-a z+WuW48e)HE0y&!^^e0B~5PD_-v0iAfp1Ru9%7^@l!dfBG(_R#^4AY=8KEs&}I!x%Q z&h}D#a@Io!vBNA%g8!&DLMS-ft9Fk(t$zKN74IL!tPL^n*QOROHg9xON}A{W39ml( z{?V0`M@J{6O||9m7}Os^``q+x@9rf6rm+^KsS6%0DNzXpTFS_MKRq^0L}!HQU#r`W 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%0D#@T=$~Dgs1zfka(iI@ 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 s((x-) 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 Ang9R* literal 0 HcmV?d00001 diff --git a/doc/screenshot_server.png b/doc/screenshot_server.png new file mode 100644 index 0000000000000000000000000000000000000000..90718d97e8ff92d4c3de24554fc9b6eff467550f GIT binary patch literal 294804 zcmc$_Wl$Z_)~?&QOA=fI1b26bV8Pwp-Q7cQ2<~pd7akmfySpsh-JQ$+>h4pgZq+$| zZ&i1-{OPs2=XmEBV?J-Vf}8{jA|4_D04P$DVoCr2{SE+-rSOm+cYZt*t$rL393(ZJ z0pPRCe|CscmBch-j7)K}ZQK0@g{ z4{gLmEL+DCXj#yfoC5+0g5nl~Irmc5zW9mA=QGGs{`W_}E66i5^L;aSGj}%=2>76{ z|Nn1W??_UNDN+M%9egMAqmMaqh?jyQ8^-N-?h`0Ek*4o1(r*$||D;yhV+hOZ${4X< zXDpe&oo%>Wq%KTBp;jLF^H9J4^sm)%&>9nP+B-?B6Zfa&M0T#+8bxQrX>xqeqgU3- z!1a^n%&y(PSz@JJKyi3b&r*aIe#sK3{agUE?RU%LRx!Q|@m?kKSs49YAQLZKyl1ne zDcQ;Op>+5r+C(?`EOR|*qL(O>`PhS$mPU9Ig~whyZ{a z`Ni@2p@o3r_U2#pofr!M1lTdg#tWglBM^#{dzviZJK`e`pS;K%azZI{aBZBKcGA5BNh}ViWKR`Ul_gGtdu_XV_r;f z0<9xgQYnon_on1pQ2U&t>ywfkO6^)h9bnFpsM zJ<>OMB|WrZzFU=v)DCWa9`h7YTD#_n#nIJWRu;0dPcjHK4y#W4dJJJ>G!M5c_0QZu zm~wgZ>;l$xwK6<_AG>A$ujW(ZU6K=$s~Sd^#LG0^9he&Xv*LzRN%nY{?1+9R+pCn{ ztv_vT#mq8TUrQ(9sE|`UDF=5|Z5u1<%rtblobdz|gE6;b7##CS2kXt(97EOn zr26<5m#ETYTPa-r2&UddAeaGvK_TAB=xz3OYObQ_QO*xa5P!19kzS^Xh5KZ_^flDJ zeMW^gz>#miv7jIAaAW)HWMK%Huj6?#>znPZ(Sx_@sFmNdINbij<*=@oOx4L;Y!2At zs(J+K*w>F^bOHOip$k)ep&Zvj*PXis5XJp17rB~UvdWJWX&u8jnI?#2* zui-`KLC;y{?Wf3o!cXG1wzPZlHjNsvU0R^&(%-QY4HijBE{^s}^nWa7kWdW|H$!_;fDhF7zO+57I057mZVSAP*xUB%d5B)g21Kc+Gsfb5c#n-`Lm zLGLYjDx7Bot9~70936(E&@D0O)+wc@0)P!(1XC+lv+>ZTWQoEHtwzfp>p;kPxKk#J zH!XL3_Tu)=Foe!ls;phtyGz^ciE0pg0*=`)AfWc@>e^np>X64_bk=jm&7aw(y9gN> z80=VRG3JHz?cJ@a(I3}+voT%C&dAryt!VREd58qtzgq2tN;^r+TfOCpI%{iBlN&<9 zM!!3a$`^cpeE{GAy<4IKi0K2J&c_&R~gqWbUiiF3XPEaWKfz9%zCh-VAq z^dXAI`1)H$kI67GX*%E4@+wScrqyY_=Wa`9y8T%<1bS`f``V%zA-k4m zFaT>+iLGN9+&v}m#7T+#c&Y%!rIlIpYahRw2{NYy+KxR98xWNcMq&3MPFaj>t1}wU z&B`;OuJT^_tRw*te%33cuSIei4UU%0#nsMe`}D^r>8TJ0NI6nvX(H*ICeCZ;-j~uu zMj5w6hP(!{v-`YrRx_kR$w6uzO5m$XeG1K2X5Aptsl+wcy=Nx}v$tCE!~19qn}3u3 zB|20pa2zJ=0!r4o)%7)$=39Su;O5%tXLQpRS`Ovs8&CHLbQZvkM-ER1p?Qa7-ubOf z{{-_d=BYZsg_^-Dtsq8!4IFa?56>!y5jWeX{4{Qur-OlbtJ`lRwBW-|o9u<2%KEE= zdHbE-IKfzgwmZK-DTp5=HY~5Px%Mw~5%LjszTcy8K~5JJV~j}U3GNXN>qH_G>%Y9qX+EjD+T!?hlzJu$avE34vuOs=Y1 z4LA`+(1Poxa3a#uP>AWT6Cmvn*P8Z-B}AnJNCn)QQ0(-==s8CTTAg|&)VF@CSw zbRWVqDB9Va=b}jF6j3f zgw(XBqaOTsWea87r#Dx|-{)@U_K%L5k$$UTd1FVzwp@0&Ap}t0q<(YofE4|Zr3UT4 zkX`Lv>$kXC$DK+qNnd&*}8rj4*5rw zAby*=tS8tV5764!RvpTU5n#CAZ+9vR*j`^XQc%yU&pu6G@+8C_LSb|*Eiuy0fp`d8 z-G-+)y?N@7EoQeF{6kI2Z9P8bRQzy&CFk%5yaMW)Z$%LqN?AslM1i#6wp;ps z%_dU#?f`0sE8byaK6Y%+Q4WEOAm%TANSlF&>t%+D+uNRt9KCc5DAVo+A;4>F7hI=D z6)9+flqfYf&MY@Emwm0}{9CEnQfV}>={;L-{r%Vbk7mFi-gzI+sY1S`(cYrd)!6V| zYAJj!QlfhBhl)w@aDr@DeHXdwV0KF8{+)nGUm z^3p<{F=zZk*+9K}wr~4x_1T!$e0X86T8N*=ckl$T$^ymGH+qs%8#*;#({bw`I)Vn$ zjyonw06?KVFy{4`)YoVIt{@`cnUzds0#f1o785n4U~@4!{MHv=5%QqF91Rw|!pURw zFX>P~KtsK&!Ib;rbXKg;>n6Twrqks~&+gl)2fep@-AX%SB~`s=DB#chG5!a+gQwVj zjP_M#1SP79!`K|PleM}P8vu*9H(lHG{6!pZr|pNw6S;EbSjiYnHM|4&x_DYVnc?w^ z8Z2pjhY6JJ2=b8Vv*U1pK@xSGq2XbX;>t}HUvCI3$MZxpgfSXr4RCRsmZUD{uQ^~j zSigB(eL^QKJ_$;z|_3_v73jMDUC^>_iSd=KSZTpq)n;>v_+1?*g%L)HwNRcWuT@QoW`G zrD%e|$BFxS5XRL{@tgR5Mv)hoGwp|b#(E{R8UY8{cgI78J#pm$n-|Vu@u`WqKC^K> zQttQiB3sIoq#ne6;<>q<8Pt~bLR;(SB}}joi)9J6+!EoVAA=F5@rvcu&%8IiWG>#L zx6(E5_b63-yYcVmIRg7)N{atXbW_f=<`dP{bXfP)_sVs_Zp_9BL3^^SIAu`;ux}u8 z5JM%x8kgZ1N=SG1?-fw^+88y*^+}boWl>1e(EbQzQ=>>k3Y^?73c*ZAO9-}86~mj2 zmC^IV>vQ&=ArMfPHa@{|k3M&KUnjarz32fHaLN8cv+ol9vDHmYwzsYTdD2nZ;^kQE z9z0c`{a*V}Ra)ToSVbs+&kM5k#g_RL`kj0~Sm11OG|j(_RvOk+t$H8St*hXT9C+@hM?vn$(U>C{DUAy|%ul5U|!G)NgtlExFmc#GB9%Z2v^e)c?l|WdmdVm(8s<7_)ZXyYIV07hoYAX-0tP@Uot2NSjo2 z&zP8MJbpCtni?xz_$O;SrkdPEWd0?DTMDNkP1oDYy0>F2^VZgmrsx=O8M zeC@L3Am@)fS?HVVr;N-_du_E}hk|oEnJ?DXT0vi<9)8X<&Ufm)a;yZBx&3ZTGvB6z z1tuj-^`H0!DYY46A1`L!^Kq0Ipapf78VNg9tE=XpQOzKMK_2M(6w*m?XgL2JmH3xL z6&P6REu^3D0rYgU2Ee!9Va-4hHmh1(nyHqH2zb0h1p3GD;i)kBt69Y48eERN&U(;7 zzBXLB?mYmYd%5jO*OBmlmpyzNN!cGpGwG%%2|VWYTq+cB!?3IDaI;Zz7(+dN#Q%el zp=Nf!YI-`m?%1bPsxC-0W)WL0-E8kr(@STNey$8B%(gf8$#Ytx1V*k8LbWe-mJJpv z`axw#*J5fld&`@ELYUn5+)aWm+w;$ORIy8ldA){LtO$mG7knvnpa6&SR77nuJpe-V zMFntB6!f;^IL4rJV~5uL>rS4^JkwcxQOql7eCODQ>_yAQtI8&Q(A1#=dWdNvC9n<< z<+IhsY)ES?NUn>KveOk<{$df}LUO35yiz+uSazr4CO2YO=okCCD<}`yF?+~XwXdWr z&&to&w9Z|;m2OKE^Cdz78aGw0yXrF5!KfEizLf)L^7x$Jxa$uRH{kovuAZu(7C;%c zqb8v;b$Y&zWfu0CcQQvd8C3C!1molFl~58z{_H%5W>ei8NR(%nTGPLj`Nk_EcNH+T z+qGoB;K)`*SPPk z=S}waZ)QrRNbzK_>|v^FAUwF!Uuoh=!_f+MmWmr~_r&*K*!mI_?oU!cb8}&^!++cL zDPzl$Le~q0ko92yQ?U9PBh^|araof^&S>5Je<}{;LM@WhN~-32PKf!6-QTvM9cZ)y z+HuDL6A}&fW2$<51jSTMly);Uf%Gtv?{|Y$$EIKlqW6nF#0;rnTd|!+bY~vCyZ|;YtN>Q+{?QoLv zs?l5c_I3IQO(q)8lC7iSoz}w`xYfFu zA>y3Rqp#w@In3G`@IV*#3}3)>K{pL;^NugHtbmgh^{qv&HHeN5D2Q<@>+%bnS+#eCL1rPv}O z4LArdUhec|gWpcQ-G6_C@K0#)Ks|U4CTyuop;RK zuz?~VVUPd~@|1Vpfoj|YfBwhkNS@>^H>7%(ZK3&`AwfNQch)Ym<66zKnAiYVhf>N! z(DDxQpCixq!5%9Wa|W2)aHT#B@g6EpRL`|``QA)Hwxqq^+Xf}nvKEDEgy~a5GJ zf$4F+^r?_Dc`YVhzul@J6MmTp$#|}Cw2U;u?vlR)D}&K>)r(`zOV4-9l<*u+Cx^|R z_UGZ)`Kh*bX50C0hav^ivOC5K=xWPWwa?H$`peZzlhy<9Q7#0Q`+A~b4c7Y-V?LQ} z8GptBmMi7x6OQ0^*nEbf%vcKj8r-!y$913l8lFQCp_qm!_ir-ldDCHp8bvwIvKQum z^+z0xc+dOZpEi~I^Lt!+p3I2~*U$wZKn$y7&kg=tju#Qqyn8$xUTfv7{zb671?KH#|l1H0}JDcnZ?ZD!DAOqcwj!H8URrSmf??g)C8XgnEGbEg?m#Ww421kzj z+a?BGJ*Y13lKQ7=wcqoj3Iz~wwltw%ZRchp#hZa;yg)!$N5^_{HMOSQM2uAVSI5ef zDlH8cCLk$I^H+iIV%rHHmW`#e%=H!=yd;HB>;665cISNtSIyj6QR%O^)J)jD!>29; zDcwxx^N&7hjvm^c@nP^nVa&n75hJV)6}XLpvfu@UFrzGHYcSmtNnGJzx2GgAf)j!7 z{>pxMi_MkKaRwEMyK$Z^7P3O=bS+ca>zGD?@JVA1X2@FGF3)pf*K)#rR9+O_Oj=FeTfef3we)w=d}&jpJrZyGTbg?xL(W%Y50 z`i#8GE&a}yLl;?c@Ea9N*x8Jt(ZOPlBEReh0s z^f=Wcim%y%M7Apo8x;;v6@^6tWZfA$xJ_DPL3L%%nijo!VfYV1JJTW#oX4Q(l+Ho| zJ33?AYnt?RM`mk%ER0n8i}t@Y!h{^@EvNHOegIr?DQ0s9zUm3~>%Fy(IS9@01832= ztxB5k0A=m@&H_>z`4x`gf^*%L$`Fg#6Q~|Hk`>GX}KA*v`MljKN#WSqpyh)zx%vI%o+>TntL|$jt3hP+cLiCVFGKD?dGF>Z*6vb^r z@#3mXYYe4|qDN^gb=&zTw(Jg{OgOlY^qTJiRLiqw5g@HZktBr<(58&YRL7S=atA{= znwl9YAHl`AlYPYKX|S$!?D`m_M_j%2#esf{19?mR{uDP4Fl_vg!e2>M;oI)1QN0^W zgX+qTmrK{IYQi2b*#{5xna5+!9RMyYw-#I%Qz2wRqn3!wj0pQa6yP=LKMF7yWa^J~ zlV!22W-9e?lL^gKnf>Ch2?J~lCF|L7b)LLGtaYa5S<2#Y1X4 zRv>NvO9VIY-D>st3ZH%Js#k;&6@*ZBK@fnZZsg@VtIQAVW`;tbXJ|j%)YMLIIzBB% zQ3p!NBSL(o{P9hIS65@K^OfDFef-5p-byAdJ#t zK39ZStOPZ(myglVwxeqx(kMUi6DI7-SBQb3=yMyHYbu3M^D$h4#~x$lYtcLdCSbSA zeHRM2(1D4;07iDOqe+Str#36D)`Y)9C5B)i6O&<*Wcu)IJ7tHLvc|owo@HY2ho$po zHG?;<=6=Oic|~1_!2`#d4VK#l`ArhBdWkn{FN+gUV7c@+yA{=Fyx*?cCG3GdGf3^N zlsS+FpH=KJRAIWq2Q-D(j zk~2RyFL_}B;eQa3`uqnwTXfna%26Z#0fXHM`ed&;bKTzrkm)tKK^x<9lOMlX_VvlF zc&FjcadOCle{%d4_W2Sbz>dknKo^y%w0polIfGt)0iQpw5}rAn(oJ`;@)4|nymb5M zxUjlsq|p9gdbYQKIU!R3K@;2P;Ue1nc-pruZZXqx3KFU60_qDwwWl68_BO>JKv!)+ z`AAuXnzY2IkZUudt;FifwI6XPmZUlM-Ij>ogf`baZZ-Kr z_cUA&ueVzD@{O{uk9tej1{Gn^9IMd`KIav43xosJ`ipbv_GAjhi8-DlCj8v>E;o6q z^C+!o#Dcr!o0X@&-(PFFw>;l%&ts>`ehY-?ghNoprA1jYMMFsyDnSQeIyHA=B9By` zEfRBk$i-3@xD)yKTjs~g_xK~~-^_isHpiQ#QTw_DOXtGp#d>MiZ0Ed|!Kr0IbLFPA z;2apmTXut}4&y7f7etIi(EoSrtanpexvbvGWHM5jThW-{Xs%JvL-vUUA`?GchbuRF zl>7Q)ZG8Xuni=>?i;o&4&0`Y{OGSRuKzJ4{RKn+^aLIg*|CSqcd=82I?@3$@rSd^< zuWP4VPS4!?CIz0Q{*Ypw{J`}-wh+wt2;SWY%FKO=%uq(*898w``Dn7B0-t555d(&1 z_SJo%F&<4Bb!T@Dl5oVrI8q=A16(OnUNdZu{WomoHJhC!2Fxp6@TP)}&+PeEz+THn zeR-Id*5g)e;v;GwEB4>yM2Ji7<9|Ky2`OnwLjmJs4B84}*%PU6JMd}Fqv$~S#3;k_ z@1dz(5J0dzU3(H!nj3FXUiWXjvsI@J?b!L_Kj!z=LkGUt`(lLdJ~{=n(ntgfiw%vM z!cWtn=%4XIxM=;C1^CKjC@j1=gT+~U&ieOtTALFO8E<0FsGdcsKdmez9`=uqQQ0K&5{;n;saGuMGSs zV~?e_L)1%Fxp~=Vb7!=1&3#_t!kGtoH0l!opbS&vdEFe~L)ERlx(<9LKc;6MtuvNN zh#}XU<0R%=wwqQ6&uY4j#hHyH*PEmW^Q zL;?b47_w0248GYe*gxe~&0`$s!R3^LM2O}5-}WD`v8CS`RwL*ZI{KRJ?etI|JrDON zLsDxAFHTQ5lK3TO%-Ru`SX%94$(vMg5HfBLOxgqkr6Ob|jfGZuZuyXjU@|;s8}oph z8vE_xSA*B`_RcjWCwv&x`_Tk-FY=c#wwIJyR2N)*?9J7-qCly8j4sCpO%_cRzf&TD zTj|POc)YvM-j0r9sV!RpUo*=)yy@+ahwIHlgqAeuQq`gnJX+K(1v95>;lGV%IUCg{MknE)fTuv ze=GLS0nJyEO)&eAJpDA^sn}EfiwgCXH72dJh{zg|8CHn&2&#bF|}MTy|JM+6ttGN?zQD0rb-p zZ5C9@w=h4Zq(mZ8)u2NF^R*Oo`WkiUu@kIZ1<*dw0Z8S4dB2M|J~}5v^Wfg7L{gU% z1@me);w>&D!TQdIZ_AJD7Q5yk1ZP@Q{B3?#nAN{IP+WD{>wZaDx_ilnrTBTl>~xvk{R_1b!#khh z>@uZzhH*m+I@H$~6}3lhGd7``BNO(Fhbvc9*xXUN(dY%m#-5I}b+G^@jk}jawZ#z- zD1|fS=?eHgcA-0=CxG@;+jz-kMM^1KK^cv$r*?m2^_JtG{JQ4b`K2qB#yFP;8l(D- z4z#hfa(zNijR*0y%#+bM6wz|rFYA6Ke-L5>Nd@Pp?C8o;dMy%}6ZlWO=7vY={hv^f z*6l;kmMYQYS*@L`WyD-3>ddSGcW;iG`a&++u1SqQ=zpdh_Nk{=F|O0CB?XjXI`0_5 z1EF9BzHhy=>fvp%o~_n%-5#)xe30rt?x^)*%0$-2KN?mROu)sXDnqGO@rIPeUm~ay zw|9~;X*CP67zik>YxDzSJFpx>j#{Rd)5;Kr1KwnvqNb@@78^X_5vTPI>7zVHcwE68 zr@o9>m%vK03n|-isIL`auGbgcey>ENVxNMD0UfX(i$sMdrk<%h;g^J(vp>It2YnN- zq3sjCkR;!pj~C#aE;Vlwu5{YWUwH0cQme7p&gq@zWuLi78NIk2sF#S3r&G7!ADLH#KdJm1xp$7#BIF25=E#VeGW}aU{AVgp=x8Ix4W!D9x zN>VN`xX^261njJdsiecoGX@^YG&0$%ZsGyy;4)Xu$dTHjdJYtgYShT5$;RSmw8Y5! zNk~RCARx%ZRv1(Gjip}MHa5OMs(Ilai^KqP%3fKc3$GFj>T79*3%!Qo>#v3pG=qN# zCGQ>zVbqc1ubp^{V{!e<>cW)6m1{Dd$T^F$zZP#;TgO$lbt%phIB@IS_TTvNi|b24 zxv1d%>vjpOhMIsEiDQZAHJ!^f*Y8k14%jxU1tpqvljDB2tC3Y3gX5i*oN%D0xjOhS zIf9YA$Y-V!%%zki?p8~+HBIKH^JMC}Kj_xDI9XV-AT}BgmMX7qgQ8#oruaEN*6S;4 zBC=zi2KrA16#PXm@F~Mr5aA{)sE|VSgTr}YS=^mBP~O(m3zEnRqTH5B{F*0ZNavLC zX)yH#AK8`p=gxEjR!^tVogro<)Y2Oqk1_fdZa{^@jE_;Yqz?%A(Jadn5w6ru4IR_^ zrwZpLE&}_pY`_a-jd{RogNT0p3`X|oJMvgq9YSL(j(TmKC+0uL*f~Y5iNX zC~TNq*0*9l|J=R5BS0|Eo%fdd+u#c>{}h?-5dlOnw%Q&Uk2DJmO>w308 z6~pkC!SvMX&kIL(ByUb}w?kcY%KgWrwAtm*K%G`ZBS_2)OUJvJfmu)e>P^GvzUe0P zRp;efP)(;N?}@yiqfC}w7@f{fl=KLC{X#myB6d6giVfFt?I+yRp9-iZfwwevr&_(1 ze~p-ExpiNk{T}42Q98Fd?)Ha`>k-qHS0=2EbXv_wC)>wX-2$1ZVS5QX*2*Q6=4-C6 zQZ9Kae}j#}wrvCv4ezFkd}iPf`uP-h>qKZv>y?pQiXy~AO>|F+A80rPCY|{!%wn;e zfZFPP{#iin==v7WQ5gVN+*JlavKq!(R`n2$tl>ZS=z52j9R1r38ZZpOwKCZyb3rEq zDyY(Z2C3d2MER|X2VLu*8($K$7G|4~O-~uD_Uz{|O;L1HOUx2est)>NwwF*`-ZpZ&ZnD&d(ygS zIHndPTZ_rV2WDlykDe=N<1~Kz+{VklH8%^rW=UOMm(vje(&TiHArB~LOH50`Tv(l6 z*2{imU}(;L@Hoza&I-$Bs;d(3x|iV4vf;u?&)v~z<+PZpDtJ8glnJhjf@4aa8+4q_ zPG|=c3b@?$*Zrl7o}6><4GMYl`I&?^u0O$ntYImzaT8fCD9@If0Ss2moaFS+l{i;d zX1Fu)@sh^9Iv}$rCKzb(Yvv#QlbJe;7L!GR#WapSv;A8|w~fB5p+7C6fhDMB-J^dz zY!_J;7RZ2iSHZ~ltli%XiXZ&vAsQN?b){6 zMTQkIRcv<#BX|d2)PMFZZJ{3evhxHy`2>^sw!Z|Ij!Fmv?_$`! zPh>1Q-{H_=LGWi@Zvy`@K@FIXrhN6#y3wPdgK zXmB*>8_9u5Uo&*vczD?8X9P_^f(Cf_m5UfB8&2wE%{Wb4a&PpCMyA~JT#I7kH|Zg{ z-)U(OKqS2FPd`3?ap+qyrnu^gq1CrXp*r?q&HiQNLZ8zj0v)!0sH_bz3hwsZ`}5Z1 zZ2uF`iI~dP$+)XOIMVptX_(NquazcFIpC>#jgUMA!lR6lUv~jjrz*JVwIz)OKqniKnM?v897Q0Sz+&yWau*C+)$S z#U1FiERtg3k-P*JL*5C--1%LE4RE+Ve6@`{wmZN?uwRIA1NiXHvoiyu5tD zcGZP-S_}2kA=Dz^qNts0u+t1hBj0Kuve^?{>}HbYC^0w>-x3$=X&R`u)H0WX^V?5r z;F+L9Dri=4@s1+4k>XjXRMVH3I*`5nj%}M=Jz>tR%1H?B(bFaL2gU8QiIGDr8KlHE;*)!jN$O5bX3^l0e#rcK$q)Q_}~;WU{O70$$x30NDBJiW6QjMb6h$sf*$d} zW>i;Q^?GUa!4%zvtWn1E#5y^iv~v{Rf0UG?J0|-MmGr|!-;TG4)ty?=>USJ~^0PNb zo~aA2bk`iV3ase4rh6ifM}+$f$#0*qqYs6RN;ee(C_;8SNSsRf6*@I%!0jN7<_v;+r4KGJ%o4Z@$`JXr(_=3>bWhw!I+mA~VC4#h% zKT@t~EOr;JQGb;~xJ>wGcXW(RJyz1EN9ne9p0p37Yj27#2o*p?nX`kt-FHaAai-(| zNMQTuN<1_kIaUMnF*l|i-{@l61OU|#Jd**HQh$j*`)HfvnkYEVEK0^ zjq_;PCksnba2t!Y_J$UT|K3B>RA0Dwy?kmaef2Kp#<8(Q)0c!E3u~U#oWSl>>mo>RlLvwW`0hxv=U0Htgs$-HG@F_KJDs4*wXBt2 zo|^J+EXV6f_Z~0kv)Ixt7)(v`#&<)eSX%H;HjMK_&5I$XhUUxjt}njL zv;EOfk4;MR_-%u1yB^cy4pElt0ez^I59B$u%L|)4SPpyEs%13=gw2%PR4`C|r%hdX zA?RF?S~NvPN?QLU-~mNRy8oQg6g0zk=`_BC$z&jIM{;51cb8kq-q5_+AN9GvdP(^E za|zNZR?kBf7(2(S+y}9uuFuy{Ks%h+&#WOXX10~-xtNf0aNKw0;DyA<6g__US&Z*zeC#49UI;v62i}g=#dS3Yx5T0gV zeoH}b+Fj*4EP}kZMp*K!s-$V)`c|um0C#4&g8|vV@nZEecvF)t8g%F;bXWVm;|6mCAYAh1t$@ z6fGlrGfjo^Q9?md3fi{`u$IE?@7}{^=U)RhJS9IE3|y^zPufTOzQmua9dqts&=KM_ z8<^{9uoO(yU9sI(_JkL}^}fX^WJ#7?<;CJC8tklwe>a!jHW@K2;udjls==f`JakAEAj7Y`sXZqjUDvN2? zL%U<2W{SJ%+r|WE2%r4u%)xC)n&N_g29Mck0b(fc`%w*&CLLh#cx$|mqHZc87fb=I zp=dMym?a#ZqvBB@f8K#pK3SGvf6jMTzp7g~1>^QZCvYFC6&U0bY&{=r5JvmrG`sxo zrEq`WKWmlKLA^fw)H9L+oTD@M_KJHIX1!$5i`FT3$v|@$F>nm$hTCido=XYmwyIRi z%D{Vm8SakuK1+0&_2mkvspuav?|8QxXb!cX*~M$uD;SLOb+UU7f*>3?iJ4!s0bv^4{ ztx=1#D2=(91FH&`hlSzl?{a=FAAmfxFuG|W6d&1E#z5y?`g~LmV~ZXsdb8F!ihVXZ z*u@+CzGHLbpQ^xj?lbf7bC0=cmB-(KkZDW59B)^IPEWz-uP$%;yXX-4n^A2yTs%u{ z-B!?kJ&2l{n2WUOa$u{jmR0Ev^&t}9x!9(!Wy$m{gP>(1ZU{<51 z&xfgCLuN5IGAaXf9VyMc(W8aE%ghc(D}SjjG+rL_+ID;HH`>-lf9EJpMq=>aVa(Ql zQs9z*n^_3ww0fYQ@9l&MohSg93g!+XlLvOCZH^yq54Y$6gP8>7a(1^M08oj9?d0VHFXdVDkVWI+PczT%SIy0FbMoxM=-7TZuIe+TS8jmUrt`1|7i2j^}{QQ z7u3`UoNiSFboC(YZ1`h*JiR~e13r24EWDL@c_+PKdf&3n00b53c~OZCO{+LaL>y_k zoEIETDKyoC9`wbBVGJcVmb{Nf!(W#-1g+kJe# z|NHh{n4We|d$`L&sv!B1+gyLirkImVgjPgob!>Qob8xhozwG1oG=VYyuczuqe`%Zs z8$s1at*RcHQ&!fLr`k0PrsUiL0E5wk*{h0PPBg|GE=TS%01DW}{CoQ>9P=B4&Hw1_ zU@PhJw{7QnlWdv9NOcuK(_uIf5b%HY4(%{q42*3b~ zV6A~OALYrmSTv|RtTP)Zge9P`Iw#bEi8fYSV;WV-Ha_+XhFs>X zG^H#^I+Q}N&(Y(?^tsO(wjeEcHxP*U(|SD^PKpNrOu?>*x^{&uTR+^yT%zf%s2sj5 zTBR39NKgb$ zLXE@B0?p62SNe1GNgjU~rQ$*Y!^0yJowCiv$6d_I`?4#qeIS?=7BVFd`jz_41|f*! zU^g5#|H{^AyqR_V-k)II#F$bFy$;)#zVjwO_A60X3CU%Xt36#AuVy5aVA<40i}`uK z)8u|UsU2{DG2_)m()2IlThLXO5pm7dY4>Z035nu=$IzQ_&g_%xbIaLJ5~_P~VH2(p zAYgOhy-{tzjfE49#%Vs%x8%p^WluUboH_x5@nbSk$7|^AAM3qE-@cacg=bq|b02NT zrat`s$1vfPw}+FVW5wo*-VV1OHCtcnfh@i+J?Sj9E0V~jH*qB#1l~o|&jIuv$p(`p zW&bA+!|Gr37Yp%>#x}F|7T%Xe+M>co>m}|k^)Mk~+|Qr80cUUV*qF}AFtDZgh5PAa z3*~X>7=K3aQqVVXM~|xeL?}wNhYaP^vDg+Or(LOh5sQzPU8Ed*6Z}kk+%;V<_I3+W zyS1=YLzD@(mcm0}X-c|m>vX(sCG_MyJO2Um-;pB5oiq6)u9Km~w)J&n56f>10_f6O z<^dh>+8mBcs$8h6L} zw1Vx>Kw@uy{7`T4s5J+cC@#g`*1W9nFpaU1@;TvB11k(vS5*ldk^Lcc!=?SXtiHeB z|CflDyGDh0GlG5^{O$bd!^4!Y{*w{{-fABT01YP&fB;k`0e~Mu5H!_09w-4{ydtA zs6E?Az4R>BUetC}^++K6{_gl%p4cP}0L#MDC#)kpw~rzbLr9Dsj^B8BN>6MYGPGJH zcoYA!@0m4vX2k6=RIyRHq#0geOVoa;dk>-`|9>r}TM51*$JodE)qWYLU7vU*S4Qg` zv7ms1f?CGc?3{$klmQC?JGJ7gsoT(q3H~u^ZEyTx4+kBMKiz=j*H6w6(koRJq^u*e zIhRAsDx`wss)52050E5~nNB1Ro5PK#50zf74eRbqUp^#{B)1(Skb#to>)+38&0*(3 z3x5Pi-_fcinc?&fplg`5d|`F#NluTWG?p+#yS$n(1EZp9zJIRBH~xdjY2bhgC&Uh{ ziq~w_sS>3vg-aP?JZptrscGnZ8UJC#<>wXUyD#G?=o6>BtWvUSBXPC|AK5c_gjR1^ zOK!EoQ-lftOR=|^pwZEao!P3DwP8jkK@*?RcFxprx>*nIg{FV`5QniGiF{eYZT*4s z+4R5fq}3um1466op8q&_&83Fkc(m)0>zl7tRR+hljLEMOJsy;PZ?KpKG}sPVT(@NX z)f8R|XHWJ0@02x5@C7eb^`9r>&L`|^=PdF&(k@RgOr-kIUIBR`H`Q$P_ZlR^@Jc!u zAOI7_>C5tj&FCC$4bKnWs8uhg*OaxR^qKyq(nFl}P_c&4sADr3h8Yvq&U) zT2at`?X6l6KtQ4IIjRzLOW5xXNo5x4`TPb>I3Z$Ur>&ZPj|((IOc+Eg0QAx6J}%jc z;3gFS0*brprxtW9TXD@kBkr&L;AQ1JJ9z0G@pj$@TmFmL#l2G^V?`B|@w1nGsdcn(h*H@a1E~I&}lb;M@va&GU zydzYgg%zFgwfBfjZjTzGtKVqjJFhwOcx#5Eo$%^Z$WUzdu`sDsYj;Cnh@n>806J~? zNlwI&6_cOIxaRqs?C`)5XQMc`yl)E<@U%-BDucKlG$x+ig|>khkk1fo}Y^O4*WP-;UcS)G!BRU->ElcbxmY;iNSKgfeuvSfT>;q zCR;ing$x4%823)S{w7)34g7XA)eJFs`p2uhp6wk3v7w1`>NMXiN9A-GG@z-n<~ZM| zV_?jR8Z8^a2py0~>7H6j5+g@6f9h4-pJlZ5VUmjm)w$GJO!IMHk^lj^g0z&V8x?IO z3z`53*J$4u#>S_Av#SuA9_Xqo_qyh_bFCQFcFF4rLN@m*el*HrHDZtfq+6?1@Ck0ujz9OHs@V@jl}WW3a**J++ctJqCS+IC$o*YK z!CwQm1)A2k@*+S9nz1qa{UvSw3^j)^n-h_Oy_U;9SSJ<{jIg;dXR;R1T<2!%t^eY? z?45fG2s4kS>?*}(skyJ|o;@pU_j%fWy%u2;4d2%Pk?J4kF8VJEpbr7VbuJ=Tj|f<# zB#R(IENUS0kkRAf$!0hL=(SfnWC}^ezkCz$v5@|&7B7@OR>z$8R!4#OxOc9HQ9cI5 zMQ6u3*@VUAX8~Gb;kIjiABOU@paa@Y|E7x)nL`{ved;HJ<#>|M*#8Y0x$J{Ta_1OZ ze5~E^?Q9Ty_A^Ao@4+dLA!)4lE%U9lJ({>jlxgaYShVQLDMJGDTHm@3TpnY)HSDdx zCsIF}f;a0UyYtGIU4J(ouXS@&8)MTVx>N@RgVX09`aH!W#YphA@u7#&_%)c42Rn)K zsY1_qZxHlDyCev4c1b+f^ zsjDX=6j35T3K7KNLUs-R$P&J9(u;!vzJhip0lES|H?1|pD#`ho+B+*x75;x_x<6ed8!%BW<0FcLNte1p}^+$Dj~oq5U`W8rYHe_6?7l;_?9cwNwm#! z1g~ih-p~le#=Kl=SN;-Ag z?X9?6M@uEw8^?!XZJ-HlD*DHW@fwfQY2#h}8s$SfB^YH%zgKv)w2N{Qg@jfit~g9KTJ%vnxEX$`sjl_CyfBWGc2_?=h!xHhHL4* zPCHR^4c@;ayN?hBt-G>=qxo!uE~ovq^O=+|IRgL%J+oF^V4c9 zFp^8IFc#q#W9vOEp^v|PNiHz)E%VWy1a#5iA53|oZ{{(&Unx4^p9+p#rIvxD4r6Hg z-P^Zj4cLJARLsP)3=IWSmDNW#3cV*E=f0^aqnB7~&5()qRPHnugluYA;4pT)IBkUZ z@pOHn$AUzFqgQHM3wuWD%PmP1b*zTN%TQJ;`p{@ey-HM9f7=2l4`zVVpQ9XFW1_?t z8Chbz&i}U3D+4t&-){fBjdhJ2%eH6u6NNXsdgJpz$uyB168MA01F{NF$M@;42ulSI zSwxe%oj$a;;(yy^p)psNPucw31f7AYiNkR!OfA^!p?6ed%!wiggmM|2oF3vRONEkQ z*Sua4kSv0ne+1^2dSU#BMoKLX+0t zt;trYDnzhuPJ)}KZ4F_&xVwQ>kRI!%0vc(u@UAUiQWUN4eN96Q_21&vcuHyZbKK2S z>qbIOUspZjcNy%;BPXhdj^kul#ps6wkULqgS}BkF{Tg#9iQQXVm@fFQ>2S0N9FY@$AZX`#|52uCKNh{ zcKLDNb^X>wN01pZHXath*R7VvJkDfUHD$$p+<*^QS=la;y+K;zDXwa9>$$yyn3v&P z`b-iTh5JfDpr#KNfBe`X^t4s4(4LY|k$GEcn}XayB>hrY|s0aWtt&n@A%e;2=rKHzy}IpWYmU z;dNW0dTyTlZhZZ=B;BW{GPzlK-KVqbStB5*F=M1jJ`p=BT*oeyoy$yY>&Q}6u`n(c zyd4pE1+)60dK)!sn?q*2(8`}XeIH=={~Hd z@{F+dq=W#7e|{ZF-fK4SMT${K8PifvPD43(S~0qLwBN*P$Gr8lBDdhN1iLIn@6?HB#L(GV!lNGXUIdCt7y$pOfgi?#TW8%mepR z7V~~+jp`b)+DsF(w1{nAeV%N-8@gf?CDW&pcFs3tv$S4!P>aPe7NfrMynP&!-$7>t)Uk@9S%deo}Wdv z20o_7)ar%f#`*)>c<26CpiJ_@B0rz6!+?O*o{Uj$to^=Oi!K7}r?*BMb7hv{vDS%i zqYsi-Q!>U~`|op^|0THpmf!zhFHR&UdREtq+Vc!*_S#n#9cdM z7B(XPdVK%<{5u-cpmJm)s;ZS0P5fS*mY`-(jnDXd@Ux4ha$^Nf77iXN=Hu+*)x{}j zZ1-qR4mO%_uE?=*Zs__iN;Ty&uuE@BrJ)75BukOolq ztdae&lcZP{H`2hG>iqiTJKX!Z5eE>hrKN6dEY|a4@qnKtNLkyOzGD4{>E%AAECqGv z_A&K{>BC)tkk9Xf|JRFf##;(%19r2E3#jgQ@MG_B^FyI0^hEjkBIB?7MSfz4Lw>3M zhyZRdGO*If3QPqp6E8CBoow<{ubKn^uvMArn*JI@TkRbm3WyDmQw-UBNd0i!o)G~U zE*Xz*_HdooJ30osr!+7@0Z{I_-}xDsnFW~FtRyf#0RSWByOT!)j{^gbzfVo1@s7|& z)ccKRR&ztZ?}fk(N9_MRU{bSdl2J7__A%~wc(*5A6gK7E^D{LzzCGRVp2dZ*+usW| zAjDRmSJZ>6JMKJvdI)*svvWz)34TVM|5z5@|KQp~!#VeMv}M=37f!O^k))8w!m+%K zvyu4Et@+7CqQ6wWTAW9SD%NRe_h11b05*#Lu8Z>LlQbFykO$sb&VSJs&==AicZR~6 zFwqZ`y9GKApGo-oM~d~!tFrJ0cryJNInO)JSMt@9*^(u?TK(S48XSZD`n?uwZ5xjU z!W0M&_`ldhmagVDp@e+ia)!0;YgWQFiNd*S8G@sm3c z<6!r*nXOWbiO*;b{&u7R%XP9A0>er5$3K)L^=d zBS{y^tUVmvQs89LB87-X7Jy!j;h z&1?^O8{G-(z49km0Ia=(ODB7!c_>MJ__-T@IrTTy5`D9i9Sx+UYK>(P$ri<&0C6nu zqMLFTfA17tZ|U{7ePuM|=*l(utd*lV35C|Frf*P$*#Z|dPyv(eFXyd(PNuFIU8m}| zE>H8^_2y9r$>=q`l$0ygp0p|kLJ<}8tB;u+UzT1?-pYELq^5Y)v+tf~*{*Ydc51E8 z)e_xm92nmcpte0)iWf8Z8(8i=o8H#kRo$Wd60>$ey_Jbou0B6vEbik zF@Mm322>_0xuW1X+pI>|_6ODz?Ma(O`oBDEi3xc#yJFEBz$=x_`w_q2@bs)I|Cz zGFJs&-K-s)uPGX)gv4$wKwQeyzcP#qoi+cNm45bGJ{-945urkcsWP{Rm?k)lBa4882L?4mj8QCqV6p8xeT zqux+{{d@Aa-S}}3Mb*C5)GqGX{M!>@p44d=z33dRdcCnq3^jnMe6B)HM`!A9DJrBQ zu7;&bwOU3~U$iCdVmWLPLM2@s;vGu?U}1}~?u^C%B@F2#R#d%Fr@0gn0MPY`hlUpF zF!;CV0^^azpN`N=Jc?d;ODHtZSFxewl}sIlG|*NH3t#x;k>U4$&Dbn;m=VATp?ZOa zya{OWl$zMfafo|>+i?Q8IjAi^rn^S*iHp@cwWTQ|WPZr}L9kh_mWNVn_0vPe!a;@l zR!-ar3FZ;geH^tM&y~|fhtR*=(Ui~!AjJ6E>wZ3ve#j8WFT0QM`R4EVVFm+Zy75~} zFAU!643}=$rS)gH!$tN|XA^YRwwbPkHViC)igDcuzFHy2 z&sXy7<2UbDh5vvO96@ZxH zq%GeqF|CywI)HT#jinLEnL2_RA7ExwI%a5I@cOD!N^^IT8eo+5ym*WtxLt$4e~hMB z#82x($baqodRd8121cH(edm^LbBRC;`IaceGYUCtk_FI*G+E!CCSa`1)(ZGH=l;F* z$&61%i<78*e{7twOJS-KZ*z_Ulu~{+ytzlopw(==Hi9_q&utM~XE&1<2H2r{xN#@) zi3UDB-)-$kIgYwHUYu&n1S!ML$ac83qkVId@SBpaHp7X1a=bGXzJ4R^Xn4dzpcz zu_h1zD(TA3v#9275zP`|xnNwx6KtAS{_ek#U$N*6t8_1=uNLndf0MpPjCQRbBO~MMzINXHF0BlN0hsCyX{U5Yv(B+H16VjX=aGJq{w$@2?7=xc zF7s;fAj+|%GN{%s^17ofXqgt6ZPl%Ugr4C2*qTL$m|3JWxVwIT{qCp&1Nf}7@<5lP zLYw*ixbg&8%0G;r#rW!#T=-=rvAGzcW#CavP0hA1JjzDG-F7DTw1pjO!^Cu?Ve9uB zY{}8a>|x5lH`8w=!gW$yTxO5qlW1dL&0D$^hfol<;s>r>! zH#ALv@TOa3r3(f=>X+i4fKTkF>hsSY3mw{sHb5a%eC+%xS;9YW;zJAe8`X{kfg8)~ zjB#I};Zog|99&H;wW@Smo%SoDtqQ@e;n2xn`0DBG3M(xrY%g3*L5h*;x7A9%j*iMF z&687tcoiJ2zC-VD%#{b-J5qSEf?|H&l(*^@f0i^zC;%KH`wKo)mWf?$2Qe91R|MpL zusKlilb`(`rK_Dl8#?%r!kdjj0-1gOoco<~ENGbGA&_W#89$9`*K|O#2pF4F80(w2 zCNJ-hhRoKG&Qu{_y?y1!@w}%L0BP<|odwj89JdE!5op74Ib43P1D~CzpGKI!tnIt{ zJKj!`aTetE1?VHfYc8yHEG9Je)6N9UXKy*leG>A-=VPGekdK7u=^;+>wp6Y1n-5D8 z>&}%)#mPz?gJ3eVQP((hW-X#k8YeW?r1@n8Fts)>v(I*X+qOpc+jOj0gGn_9Hs)jg zCnkOrEQaGH#DGn}3P&&ZT zs%5wk2w2%3j!D2-c;T=3(L&_j{&s$ltdAq=^f<-1g73lnbUaH+>H*r$+cWLBd>@yODRD4h|q|z1D}iU!5t` z3Pv1DW|IjwM%Z|T1uf|eUB z$r|cGdKaEmK`R09!@3`Q8^e4S9++wOTPJfmDv&AH$B(y;us&u0K%ygb`_!-VXN4(8 zXiV5loi0$;{o0hvqE}-K3bE0h7dt{PFPb0q-@ImqM~;m3o-(I;!DjY(lK96zw=q8$ z3TTo*?RWaym0+@hoV4thMD^h}oc2Wp#<=QlP>L`V2!Iv&HdLl`Xu#^PP?jd9AfH?OgXvu6M48BU=D8iU~ zc#H+D`=!H$&+|YF0hSnQr6bL_I7nG_<5<+>=)UZgiYOKtCAUt z06`vTX1j+^taa|T>wl5BL&d(&my@$smgmz^fn!NFcjcY!222bv8nlb0Oj^ly1hfXr( z!QBNog%bWf+;ohx2p<=eMWWWtD__e5O&a<4_noOKW<)u7m984)k~HtC?&~P&(S%49 zQh|2-ob}nTr94UD$%Sw~6|7pIKqZy6fu@2RrZabZmw}ST0~MQX*Sjbt?Tz*_Q}`FN zk~UMU-eR6t20F1f%+A>Gk$OIL&M%e(=&wzE{3^Tk1u@J4-+Knxl1+XwBC^e9GO{1I z!Hm~hVZp*d078PROld%H>Lio0zz1JXyXC|sGZ!{U=CGkK7|6EEtjd8FiS9wki>?>U zUkT(%g~Q=KPMtNLE@bl}aOgjT7q?{O+dRU^&85u}qC+kj?cL=o2vp0N?q5NMb1t_X zU1~D7?1O@eg9@M2QlB*QyXY|zZXXpDQs&3cZLpJs^vJEHI-SwGr6zuRZIcfZ`5;J* zB#wP84fJk@1JWwuA7undk0t!x%~AN8Y0oC#n0u247y5VgO!`S7-ZYjJtXv|s{j5&J zeyt{Re#qPaycZI2XVn$k^IuryHa`AG-#wBvI)t;fEv8oXHfAbBU*vL3X;2H2PcOE0 z^!W#_j-G?1mI#Iw1pg>6x9Bx*b~(vWtES1*5-?O*lP4TMBciiux;%_FQPe)f*>l|@ zYMip09gLR5$haQ0YLtKCKbnV7OLN&juC>=vh;_&SC7EwoUPfzK5H7PeoO+X@-%uOO zd>@r7F&^FDu6)oy7&Uy?tHo6aia>^C`Ay)sA>FuI<#zFDjevbS=X*d?H(%Jf#f4@C zw)Y6N*s|fPTG`|Acd6-nhZfXO+*ob?j;8G*#1vn2 z03gyZ4naROo;&g%{1AlQ0U?z>Nvu??Bb_NL-HfG|c8`gc{oq-X^Zk^R<07a==z5Io z`kpvBVpDtlST0Jxn35aW&lvXYYhLrgt=<{U6kaVG-Vq9;LJNOY$Mg3zVXUrkm5b=+ zo$_vd0>#Fs>aW)UdUgx+=uN8_#3>!=1~JXZqnesJmV=Z^p*V4akt=BY?8&psvV=#> zuFR@=)fnO+c*C_f47w=6MIDF4{+g#}WSkwW*loF#gV;APbISApxi0H0kt(i}xeOl? zV9L6y9Zh$=Yq)rcg4B}w_{w2+e-=WThEj!rdRQCK6I7ND1Fw&Fz6;koT@6-AH zC#gn<5`SNeqM^+eL*sJ zcFV&pfQe;_`dMt&tLG2*LyqoK8xa}=KUzIc%+`jk7o&{3XSCfADg6LW{FR@$C~zw*yE){};?j+TyyZuFF4 zHD=gk3XzWH@$uR@Bf4NFdnV)KOB!^>HukE+*2+4h*6mu$o%@>SVqL}+F2vYnJ4n(> zw0#I%3pCsqsItF?1z`(!rH^#+xkv4UB+;a(Y|G28^Y=Qrsu#W_XnQ)a2HJttQuLvR z%)Vh%sLTH;u-XUH^UqW0ZH=%!jhc6cr#%f4P{#G9 zRLu8k`H-1bJ=CcnF*;8q%Mc!^53Qhhyu^yU`GKCCN{vx6=>OpF{jgG(eFAHLQ)T1O zd5aX~oTVdf$hs(Ab^@0+U0i(9oZe~FRpmA?Kh z(oFzJI<7f-J$3k;rd9eg+r0^~G{`g*aXF2Xu=zAu@|$rn7c2AQdE=!2!syh6#oEPZ zOz$e2b&m-`%IV8QIw|wM=4B)qo^|vctRTd8uN^50wJxLnx^(XnVuD-`y3dL;=!ICF z&WZYgb)pBcW$4aEqmhzVZK73P&_JP?bIZiPdnYhp<1g#FY)t&+MW=C~r_z|*5=9p0 zf)UU~{iB2&Lsk66b(ya(Ax-5b(Zg>?{A~0$4(Sbd7WaSx@I+ zbah?u;lwb9bS12ls|eX_o6e3z)-9Z2v!X6oTD}^S+4kgldP{U_ciG7Nsh2mA?$ocl z^l7x>wwum8)rt;X3G<}xG4xbG{`k;JW)?vpk>qMH%13FyEu4_7Tf+R zl|(*mo2t1Rdf`0$45~Wcie)&Q+52+^34NZW-ENk`p5cWGM?3h;eEE_`@h;awnqyiZa-eOP1ug6yo4m5`fDVlQ8# z7rt!h%x0<`@6lkT`K0;}+gjb2LuFe-DaDwgt4H$m-TAmK_1=ZfMO7K7!}3xd85#hX z0zNEnh+q;>Dp)07_}Fc)#%o%Y7WW@ctT>fpS}fa_`ve-o3VlIKd{NnR9}wi zX!k#s5VTrNCUe`u9a2>Mfg`=()lYV~&o1gfsP8`_xY?nX>GS&^k6Ud&Y)s3 zboXKfimk+HUJ~pdv!6@O(!n1$<2d#E{hc)r6IdDz)pOFJL&$@^m~g@Z%-jcxf$)nI zgckBNy!Za%#VW8u1ms)An!!^mqBwz&w>Cz$PjG@2U{co_t8&sZwc5>CoiAlz`k!O0 zO+aW{PBWa)SM^`t)^}|v>-pr*(z^xc%btFF&PVYO0%7C+%M*qq1o-=v*36c9SS|KT}#_&w`7Vy8Vlood$x1?hVguo z+K#=_SF^&J-hD|u6Q&@Ua*N9Em(oA{YC9F4M7s8haVy@b)EJ)<2yYF2-SPBdGcL?A z)A@=S#4uzB<$oQ>pJ@tf@Hh%rQi&P-)qqyBXbn$HGB+F5Oy1L!%Fnska%{!KX0>g) z@Riuuj7UNiN~1;|^i=O0n76F z8V*wa%^i0kyjtAqonN*y^b15nBa>{{&jpN@=Pf;pA7bzGHLY(8hWnSJ+zhdIXh1Eps-k!1LpHo{4;+fDZnx41PtN~@by zsg6r62pnSlXd?tB$Ou%FRolnTy!!yhr3FE@870R60H(jLRQ#|3eqtv}`ru4?#5GrV zp!uL$sGk^_z4>VHeFOa?e~x9LNpg?{OQq5w6p7R5xaX>oxV|)|VyHf^W?XAh{x@&F z^7J7(IS!wMx>wem(zfw!slOzY&!*K67Kryl3?zy_x zPK48~G!6%>*o_~j<+|^7?s#D-NQy2p)>M|sv?yTk6o6>k;bBZ$uoI8VYbSy(9??`! zI0Dr<$U>oQK|+hJ+ufnjnl#9U`}XcwJQ&0v^Ug@+(s7NuJF}j19CAWak8UT7T@fP- zERBnhZJdg=1bujn97wX}+)e-2RQVM!m@;g-<+r!GACo^dvA$dE7pH)S3lw7bWi!0U zl_rY>6BhOg+=r~{5kI% zpRz_rUamTNKI3(H5Ii5d`&bY(>kwisNbvhY1;LbYyH-gy&s0^Vr#@KO&Q?g*OUBK! zKKBFQl2-QQi*wpFfH0Y74^Q4(d0#)y-t}i!sz{(<#gM8Y@_oX0bF@jQ*cj=7zyc1E46xpuDk_a1|oJb z>Pg``*oxpy8fvmX=nY>$@o^_34hMfwn*|c4O}r zl`~6D!Sl~|&fffokuGb-ZkO(#{FA%(ynM2>RW!F`_fJP9H3YSe^FI#EqAZFuYr8?5 z&}w64s#Bvk!$}KZ98=$1%NX0N)?cP0B)@U%cuDdfh%dAM@FwHAeYQ$D(+wPeEwPut zZooBmC~qA_4|KOTtgO;vjILG&o;MVPDJT_}?!7&eVbD3t@LJ zNA%^_I{YfRWnfOnbJPpDyPynjI}Q2|T(&ZbyXSabr1!m=JkF^WYxUNNOCJy;Wqk`@@IU(ht{{y8ihAWCm8O>0^(xX-bwx(2MC*E~^pw{fSV-@A;D$sN*>M zcf>FUJyC;htc!!Y~+1FQl-*n;ul0NG=V>|4EyxbX(}iM$4=s6nx+DF z)89M4vs&LCq28M@^-{%{Q0g@_3LXYwzwDXw|xUU}6+ zgC0tNEM5}u-~N**pR2=Q&Fx64#8>Z2EFw3LgS zSw7Ii{Mo}g{2H&_Z{6lFc4^=}{M3<)EpFCbAEoBaq;9GDUP`5HCC2j4PALKZIeMI% zIfx{pde1byvz>eR3kfO!@#{`oAQ`=2+u0WXiT;OkEnnG)%!%_brIw1t#6{=+Vx!9@ zsh~txF~jHUCJ_e%^yFI&_Z*4|e4X-Q488~s%eLEYY3QVjCN>PkqWs zWg64p;^1TC8LMCkjpoJMOA?(8j6T{Q&K{;%?0faMUw}y#XG^&}9?J}r z(Np6e6Ne5p6|QgVUM#eKIfy)>e%PD`ErPlRi)C8tOc(?VOtN(@ zz@TUWo0O5|yqqrLO9upufhwE{$A4+(DWa2S001p5ytKvHmp{kJOKXL@x{OM>$M%Xu zmD<}|MwunAPwFI7zm{m|_WAZ&?O&@aU}S*o>ODE$0# zl^?fqdQQ2rlAqV8^ahN`ChEq@IsaZ&Seo-=>fcJ0Hv+B>AAA3KPZL}jFL;^oL`k_R zQNCK7my~U`)YJedmHF@(y-B!QUEzUx`uN+ZmHPy`k)Z%gXO`QY4UdccXKX6?9VL}J z**?_^B~U8uO1@;R8Q~iDb(%O123}(pO-<#LTv)Wr2BApUXlPJlxvJDA0lPPT@GPzo zx$PLxcj9?@hzOs0%nB_abAef2Y)oH1YqY~uOp&m@RDIYU@8K-A5zRkcYCZDUOs&-C zzBdOG#QMnnwAXjGSZN(>r!=^5{W_eS;^gpvTvK~KG7&~i$Q!IRa{g8(<+|1IbQRyv z>i+K8wpZ2n7eA6lU2IyzCEbxJ=(!iPUkEy9 zNy_K?VU1GKpu9`gig8qcnA8W7i-R&OahEw@F!8BQI^B-{>(V!+7qD3Sn zX0={qt?1!xUcUFO#=61X@&`_bb%ARMkP5R8L&F89=eNNM-hyz3&I;+fT!d93-DW*b zuk(OkWMr&LcslFK0LL;D)m106C)geIUV~r^*~N4pGsyGvk?6tOJFC88wcUJ} zxt%;-%1v zn#~E9^!m8;E4uD}@AOsi2T{b(4@dv zQ+Si&uAZztAKcCwh_OSm^O}iMz`|k9efCWam!~Tn2kT-#D|MSaR5Gsgsei>StIbe4 zMY>hr2fYmg`c_;(4PauLxQ9Zs?yu_ZTsl0@c?{q>m>NU%w`6r0b9CUwbF-VR5tOKK zr!Ho5P&HbZQnO3IPd@jcgbqk8r)_SB=c!)3m#m7rU=eKbuO(n9DmfnJ%mI4k4j5Tz z5Zsz+({FN=@%iVBnEyGO#W2X>dSqdu;^Hegg$JN``{syJm2n;pOm*$McID2|<|2=c za6Lp?QB1ZqH}=hL2hBkM>ast4In`H=r(5Zrs|3ZALep6_t7WkGkJW==8cqar$?tY{ z&9=`n%2LZ_L}TT561I2+o`--kDYYX?;dEqr!2G$Q8#=PEkT4kZWD9f4u01 zL^YmWYDdk%D{oh)2*MdgqGi96if!&rHR8IzaNSPbxz~*aQ{FF}H6W>XQX*AQ`GY*F zv4cZDqLH3-STbIVi96A`B(J`FbLqO0owJ;KB}{iqZY0{6B7S?rBZ3x$@cruH8`)60 zSt5RZ)C|gJ`rZrLDp{~vY?fc0iOUYX{`{nQeVFI|uaq|AjwF@zSxOoA&B{Q~}B!lwM&-5*|QZtoLRESZ*L4D*6Q`+kAjkF2)w+Y z4NiS|FhM7MKFX2W-J$#Om7Y)7NWXLP&u21ioe+ccyXE!WNXttmc7sm@$ytJYowBA$ z)r*IG(@Sw0v>x~Ws83y%-%BsZQN9WUl|Rp=RCMq*9Cu9h++g-}jDi5r+RoRgjHD*Q0xpaf>^Hui+IEkopn1X_<=r0Pz1xg;cwQgKSgP7aZh%>D;eo-t8nNx{R@E7G}34Bd|BGaDK^r6yzgc7_-q3(oAfRR4eo_!g&G-^t{k88QC-)hVx8d)YL_ zG{7MHx`WjxH(}&#Kj152mW0;wDYK)xbL-)WcObXD{pz~pw6f&=riXpjQJF(nPhaF+ zo6E5t3n13-V3+!o%kospUuL-adanB}oh^_0(??H&+mm{4Ka!)r!Bz*-MHZkD$;J9?>nUuTy_<)%*U^A)w@ba3Z^hW_iAy==SA>xtc-t8kLvULdP_7;Hby=5sfo0)#JBm(Oij#Qi4K za%m0*fcU}ZlzMHmJ*jWsG{72iRuKBC!8Ag2{XVsGj*k5$%(z>S&u7OMH|M`S;1`k) zU$ako$dWWI9V5Yw4_LxW=Lgw-@9Y9938)WuPsg6;<3|W*f7=z}a@XJgxRi=yC!rra zUH+|mXODs3hVkXWUdE~087mJ+bt%^O_M4=Dbh^l_)#H`;dJ2}RB=Xe-u85_;}a&9iTUt0ewy)e=*p1dTtd{Sbq$@Avzs~P zWV_b+nFq6xKA8sC(>CrDPN`3Fb^N7O1kAU-?meE#v8%Y*!uxHTf}yb$MiFH;7d-&d zFA29HeF`drYTCSul};HQkm_|{pgYm58mlP58RNGA7MZ?-p%Y4xBP)fgmUHK!-aYxXG(h=Z=npM+ik!kyb>nL$w3mD)Gyf2{;d&97QBsx5Tko2y3jz zDl1Sq-oB?q;u^49`&9@+C0_q_O6iq?xn5%LVe27Q7ENX@--nPeeJNkn2pSkvR=*)S zCY3#@UYR7fXmF=ACZ%sh;rpf%ANfkBb$b;r*UL+|ucUnK6I-|ZIQd^m3UpraHMJxm zDrUdMpHko*$JnShO`3fxmCFvfzA{y=813N^zRZ33cX2s{DD<$NgN3QFt}X$>P|HiX zP4)x&lZX@vTO%6=DIl*@IKmEAHzDz2rX*=}S*|3ji=zCCKrJ7C--AsBzRuVOy-me` zF7^=)2d*$rPR*dV%P#)x{6_q0l9+WuIwimV0&UVQJAapLtTj+TFc0GjTB*_-zIcEG;NcPJSzG>iXm5dSYL z?jPEIqvFn=LlS!C+7LupAxZjY*yQE`0Q{0yL0J6pI{ffjCpq&FfJtONnDkv@$o}Do zKZHMgOh=@I4jDofnTHdZZ<$_<6PafP=)o^(TwxVgLYE`kpBy!+Pz_Y0(u_6JN;bp5 zht1T+t7!cK0U3f)B({i^j^+zAL>-ju5C4+Joo zYbC3Vpr5=Oa>tr+o5g*{AEricYY+QxXKf$DSmjr;-o?52PxjmUMq=P>z=PA;Bg(t2*VBXjZDo9-+j+=bq$QU4VmI+9>oy zE3EQ#oQrCNosRm~t_aC8ERX$ToFiIDXdW^Qb*4<;#4;Uq1`;A4J6*X`HKu7$1vH=c37pm#|2gvO)}V^`30CuDqX9-v*ZzC%@-Ly+Uwex(G<3$j3`~jK7Fbc?%ngnPj6@s=nA{Jb1l7zP_@O$Y5e;PbY*f zs+366Ag#hC&MHENroyU%CMvNXPb!TwB-WM}KjvOU9hsMSkseqZF*2|ckhd614_3{5 zOKnTjvfh=Im4ShQZ@K$6Wzq!z0LdNEOv|t|Y*w*VBqla)#t*R}nAKQ|rZ^fVCKe73 z)Ce)spAoe`0|V516T)3lzaXc0)9xwgvG9N--|J<~}!eGZ3zo@tk-v@dMav2(_IOM0lUPd=Z5@R_r z2K@=p2-?}?=z8I#q!lw>3)iB}`u<3f@}mi+6k(nMd|fr(MItd_26^=%Pz=SjtrW=O zuccdqf@dFyiGKoi48%v>IX^~UDV0OIP+SDi^Wyn3^o|cHQx7bx%RkE-XLV0#aZ0BC zHoX=6X>PLO^A=90e$ZHDw8ij7-uChVpY0Gd!00Z!1|oqqG!ZPcj0I*?`d%Bm#(Ox?3>FfRbEMj=qj&*181m9DGV<{5b(X3Pz%P!)*_W3_14`+b;@`KBm|@O9^nE1Z`z1MY1l>q zuF{WU>hv#)nhaPL=BCm~3ix#6H0Y}g^h4NG(mimYBDl_}{cuV@cBy2I$N}+`1lSOe zLf_8uNJA{snNfh2J@_yH@Z!8p=@2SFNK)xwYJaMkR!PnjZtf%#V2#B6|gNRb{#Y^Z$fFbOzK^+4`u zsz~DykysOMBqjVlI3u8u8Is4vi`elAnRWpb6gCq~dU1SR(%1~M_&fp`IunAN5!h!S zK@DMBMh+#qX`zQwX%m~G!1SQY&4Ra|o2bJE)Uwoy36q3a2FB-cDYmtx?k8#+hA=4x zU{GzR`0CEpzv0<`s%^+qQ)~Alldy9-TX*w1eWwiO1^~t{{Q2AY%VYnJZ_0S{{}OZc zj!Sjj)q1h+Y_IUjLIMQP#I`-2oB@6brP!3%hng`Z@0Tq(%)4q5HE-fU07_c2G#g(@ z5CO!NT-JA&Yknyly7x)|096n_3y|HoweV34sf==v5;L<$ZZV6gp#AdvdXF%4HB&wv z9Wx#s)0B{b`umniC+B*aT_9hUzAbiBPOC|+@fk@83cuBf;L=3?sgMS)$I;dkHDYQi zvV5#0LH>R*#!17`Y_r38=V>D3ho1~4pXF1cq67HwCDD-TjNu)+O{yrljR|AYZH zH-zl1YSP!qNxWvW*4Qxt{A|`|vz1Ot)UlsV*TQodai`|;r!~H9<={1sNkpw~^8F3n zl5RMj^n`%>tcQ*RSNqlUK#Q=qEb!{7-@BLajMGRGQ&Ex~7yiBl!WWT~&-6NL+Bj?T;XleWr&s#`(4ei_$Jp4>7hN*7uTm+wN|;`S`yuFZ^QIXTm~oHZw- z-9b+izD^*!&2oI%I0Yj$*jPOzw>LM#-4G<34Z<-`b`?f1IlaF$x6S!Sz7VR%Eb3Hu z$Ez_JL`UyCOSAxDL|<20bb!!rY!8IU-M0h6#cvoQMFDCfX2!LeK)6za$5K9pB5dPN z2#6KBfvC`$=ZTP^0F@r&#E0|XlV7?cZoW8SkP~%u%2^^O<%wScn8Ue~`>{=8_n3x_ zq7wxglx_Iwn*PlNfQ@FJQ~G&r?Rvu4hhGjDpkkc=K?WpA2ip&HlEKV0HALwSdEWFIzJ^o>V14XPPFZwiui66$XScq$-M$sZVYBYJXfD zhWo?yX~0aFo2>{={@4dC=92SPPsV-X8l47r3B9NCeFE=`M>+_=%6G#xy9ZJDhw08W z<9Q3r-?(Jvt0|xP=L!bTN>;n*SeOh8@c}|ZCFAV^)X2Dqc3s9EuKvSW>kbFmGEaeM zXbD_@622q=GgA@P6>>JX`mN5Mauc5O=zGIOP}o4Tt3duaM$Mo}x^J+>z4_&bB?~gH z=@x<~JtZ!8MqknA>iXKr1cwuzVJhFhL{^D16FckJY1mt7w>@*hyPdo89C*5)-9pIN z6COTgsABuA)hN7N?7b26{B{G^1;3o^?-%()QVZ+Jfb*A3q2>8s|1kr7pF52A1pi#d zchqav#(|d;HxRz0Vi695%vH)Jr9qiJ6QM$(G(rZWU{lRmr_3}=bUl4EQb>*j>b%UZ z0dLtGGkBHM-KA`xGR9c>FBrP^x2-L%i>Wl;t1>8NPK8aWnj_|xavmd0Nr7%353_Cc zV5G;95Kp50G?;U3tr7h$mDTKDC8%D6pa#oAi?{p2tf{4%Pmk z7MqJAC11Dt1jxm~B_aD6K`|)&e?yDnw0Qj_jazZcrv*K}({aijc#RE?kDbzVd+V_4 zu}zQ`? zRZO1i(miXfvfE?!&gAP?sr#Dtd0U^C?%1{G%ftX9|(@tBIkFzU%ETJZMCNHSKXnhOATP1U(DcjuqE_U1?0!?paFEhsQqwc zP6!(Ko`e1m?5%kfFB!vsrr?Lt@6Fk_LUhPm^XfP1`UVb>jk*Sa05<<_I7}QIJS*2n zf18yu0y@H4etFd4;FUCKZRHFeNm(E4m-AOn;knH+qTiM zcWj#-+qToO?WAMd{_^|3-+S)P^{HoF?Rr+#s$FxgHEYc=$9P`NBT@!C#%A&^r)uqZ zc>LCl>ur82m#zWbV=08G@^-oT2Fy7o4O+8&DZ&sSigkOuz+n-foeb?TmoHwkv(LMq zMj;#@)Xi_5Y6c&nmKok13}dKyw!6N;iUENl;jWxJKd83s|Iypp$~x#sq@CKbC|4Nj ziY!f!bH4EvEey}c;xFeq$iA-qzyJV#ZLhcC0!nc}Awe=L1Wia#{OQ=~dHk_f{?!P< z7-LuC1i#dk}enh{f*>{W};FW_qW5W z0};~qoQqwi*@iwttB^ocI3XwATTeGkTooLq3JTD`c7vEO%uUjq(K26*DT_&?^Sd>Q z`i^?=e&a#HP$_s!dYCv>A4Sy_Lq?ZtMe><1mxL3OCXa^-m9SPv(JP;tZmXWr8%NdA zbXN}OFG!t37Fyap9+Z2sBkL7b>XP?@7$j>~v9SuX36J?P008+^$$K&E^h!QkG8F~= zG_a+5KlW!(tiQGBos#I#l8b3RlOAqhU$Ad#ALv=)+G;OVs;L}{qS&hg4F=>7rv@ua-J#@|jc27! zEcOE*Z**mDGq)$OkuixSY|?Js%~v)Om5nO%J5FXcZK{zC)-Cg~dzu0K_m5_FQ8>{! zYv2%sdo2?DYjfjM^%P!MIJ>o=T3C*OBM~hJyJhU zDt%tUpG*O--#b6LRnt@J(zt*1_#2Pi@tP~QS}S{cPLaW(*K4#eJKaxf-Nx}azFcbB zO3qU1!0?W^&bR9JZy!!#3){}7zz}(k*_*OK28I`2+qLEGL9v>*+`kTacmANGV)BKX zls{>AyU8MYo4-_oA#VLWq6!^0`7AibMpVG#WUK*EMiT3Cc#g6{=ascxGhPbfE^}Mb z|Ex|obmxo5;m~$<&bw%63!D;j!_Lw_E(7n#ycR(#d7`ypGnMk9c)mt=TLk1)j;G|U z#5#m)#g;*&OAac~R|e)UueSO5D7hc_f~AyoFx*6YV5ch^VO$ZEgY%6B>S`rakU6StwZ$k%*{NcopW}(V9giM~7AM>PZL%yZ3 zTWGw1v9+e7LuIq-@lrrxxes^7(JsLG3Kwx0_W>03#eYJ zh&N7Miqx;r(fd)=PZemt?4}F6*&Ml@tMp4v9Ey zbiPB%fS|qnx45y7-7>S6Gub@Gv20J{wWWR-B~E`YZw;oy7_0n$Sf8D-tS9%+l#+Ga zP=Qh3sr|`yd)|&t#f5Yb?Tv++7Yh*)kP%bI3ork~(R~90i;)@yhJP7>Z}op*V{C8U zoVMqGmpcxRYPTRTMiYrl-C+i#BGiwxxDmWP;VJbDAa$fP&1F~n9&4^_Xfr(b-|N|7 zh#zt+S}&IpNxfjjYZ{$={6FwJui`hI|BLX%bho-ps-&Qz;pK--U*|aEJedB5-f=Kl z&)*3G#-($E`uv#H!W!zC3hw`^4EyE#9U%Foke0%|J7O6})v^#(Mo81()9W%4GYx6w zlskrrgzZtOE-^1*3}oHjY0rR*fKWv!{VH@q&lxxoheA4Jf|PsVq6=2;%S5+M7($JxzIxT_IspDZ?AzyEZ3n0yGaHNWPvtCY zAw0Tx38s2B4sig$<~kU8()*HLv(2FYcD_K35X!^R?x;PQa&s5hudUax}ET= z-KN)t-H5$VgZF0EXI4V^0CI1*@;?}0>i=MXnH4cF?Y;>zSL=DY*w?Cj?$n@H8zXw9 z@&sr|mAMtFwRyya3R@9(X=mIwj^*J2k1LSTzUD{H)1U`7y zzLtp{UbsJ<)k9|BV<)`p_k0T&rN7)4{|}Y8Dg9tL`ZR+DM-&+=ZKfR1J!lLNvy$RY z%gDqSukYcNne^>N3kLNn!nu{OrM8km(?G8 z8jy=Cld(G3>$F+7tR=trnj^v9eU;9nqccn2FCfh9(bK;+kN2ska4^4ScSlHO8B>oE z4hJsm|LC3@Bn%=!?(MYQ)(d7lw4%q$a@Br>0(i@2qL&Xlb(N;YM02#iyRdzqh~TsJ1L12SM|!l1D~+zd5h4 zxINi{fBV~aw%q>hpa!O;m=@7Fv%Qt76y5t(7||wc{l&-b)OWM{{F?11aC~CWiJRc< zE<7q#P%?lkMGUP-VPsG#22xa!<_XjCr7wu;w~loj^FQ5~Sy-_g(v0Dy7G zqVbd}qftn$OdgE7tAeeVPi^P{QPT$lWHc2mFmP0%qfW&v9MWhNfu&Z6+Hzgc{cb|6 zLPt@T+_f>3HOK&yd&%sxTq~Yarft%a7J4`u!m1}-gEDLaY9!uNbt~w{&Y)B3A$}5i zE#q%D-aO_cp3{LsuV&JrKqWE3nAU*eC#gQyQA3Oo6g5ty2vaLa`75cdvYzuPJwlNt z)%R}^6Z@aX*$y{5UlAtQcu2*dYPmmD39zNeyG`@CC0teu?#(!B z3;ebU+S~Z_ZjYb`-eR;RBveP*osN>@7{)dl-P|=y``gY!lB#j6tuKGH*?iQMWMx)J5Yq!v(NYce z_jSs4QR+LHW!JbZ4?e3xfvmfCOD+{}9bD_;JhhH;f^3W@Iev zr?BEifZhnHR6~aC7 zv0mQ6kD{AhBq#_`AwEh&KIq`S{aQa)<>UW4@Pxkl@l6?-NU_+e*#8tVq?0R_JVomH3BPff>9yL?whiNzs(xtDHeM%ld6+p|gQJ$++E z7NQ;yr((7Vjaj4q9Tj*$AzsdERn2-dDlFzB&zy&HNEk&_JYmb1%xG=Au|T?)gIn9{ zd4{TsSZ^z4fUzMx3?l)5Cf{jf*y-hOiYgh1F;q54(0Mj-Hia+>q+$>d&_g6K*7#J* zsiM@o9@DJUCh)XKk%^`7zWaH<9dGcwNVQ7xJT~}^EmeHWK>!;nLBvDUp)Dw+Ej1(* zQ^Pqx28m6Ub+}OstANT258y|HY`rvuHP1GqCDNF89#)ol(e}dm2MWn}3o-~QqZk<; zidc)1ah^Nh^6zRwQ7m8& z+D__KYKP{$9d3+V`fvM%^Gfp;`GyL9V1QW5(GSp0=u`1q>A>)ssdg8G*)XuPmM`GV zep*hCZJm$JMy8CEa5tyB3GVJ(5IZLv;YS!z+*U`#?GJyX8SFQTdwoyo9+!5en_xSk zyxH$6R+ep9fZrTt2W2DQ&mO~NMo2^g-rtJn*Y>gA%WY4$>oBxIY>3}NY)9lXxrbhh z;b3mG$BRRm^JIV|k2z1DZU*2ldzQ;5Y}DefJGO$_q(_2U_RC+GJFN7A6r39I8hEk@ zq89T*K=NVA%5L*(wU0j;kHRxoWU@DxvaW<*FB`WAl63`=%7=lJ#~wuKGzJ&kV}N`W zdaM2wn8_xTS zBVS|BH?s@;w)F*|7{R?-;o}2kO$L^Od0!#y?My8bKrlNy2I{;VG9(xs-Hf6LU1~M@ zbD%c;gCr+L`fKf4z{=}Oei)zqnqiJtuC;;)cd?8*RQ+6~u>xH;QAMZw<1cr2;iz=C z6FoMdH&3VTGHQaigG}#Lqu1<}kdlTXOB~Wd zN<7GKqS0vrL2h&7iCQDn^Hg)>rH^2l!?#6&@L57xu;1a|-|skppH_0$ZaAbOrjDDt z0dXhi9hL0ZtbDy#qzoI)H!mp|y&Cb(FEuN$6a)=FKMtc++6RAB;qqMa7U>F(p0V4j zgnI%H2wCOFI-gfySB-n%5L`ok;u?`^!j^E5hAxZ=ESP8BSX*QzzUKqjqsvdCr_viX zS0J5C*hrLJKefAv+W3KrcV16QvuSW%wbHL z=WzQbEKX@WJNFagT*_@oL+%)4yze!Im@1Z_t}ER6K*zi!MX1N!g!9(S7CLL6Iv zgPS+GBPj-oyZrX7cRzscd*HSgi+I;|E4?_j;rv)AIPo+cA^`quzP}%td90w>Fd@x5 zubq7ws+#N^SpT8U&3m7i=cOE_@6^ff^G_z;t;9d4z3vAu58~;$CZ5}|jNa;+ljZBc zyATLt3;k*$@go{Bc4?IQNzylUzE(SD!c?^OQp5I-GN?pO&I{9Ymz!<+Ua4~x=8TvTbk}CR zri`Fb{>A>%t>Y%|eU1DYfn|^2OKjsn_ZJ_mb}6vqsl7$Wb|*OA*wsL`Z;%10F#ryV zw6Nk2b2^J{^U|q^dXlYbO=Q|(_>kWmYB24j zG>T91Gk_V9A7*FDNi811*znyR@ zuNBTxY6U`#9M(C?1=+g&4}^esM)88OVCiTCKCecE3h)qjx8g2xs!x8OGKsB5^;m;y zS(e>~>3sK$i8eyF!$#(*EZ;a-gF%aKkhJAMEY-*iCXP}qIPdo83&P))ll8Fj+2qBY zmZz{pyo5GA?&F62Ndv*8OcjFzb&Dg05686DOFFFgo^`DxgD0WgS9b#Zo(Q@=+Op(V zpViA>ukX(J?`{-QRPrm1zNF=`Yi&pCufOf5BS(Z-flCA>`olGEAIGq80Bjy}dOR1q zq1QyDt{tUn^9tg<&m(WAC3zk(Cv`ynE2T(A8(I>#yBOQqqv>`^wVUq+x7ag$Gy*8g z45CpAK5on7x=&ql>eVY{Q=`;FvwqAaW?ZMkdvQs=1O+k&@8f%K=fIEYZI6tEDhFum zH6Mi>ifZchp?6?WXXhW$QZ2fD`Vt#G=KeF&>%CeB2kvi)L*admG)qv0nXtfL9VSEB z?T=Jdey#}KJ$I>KT3p*%JT4zjbk%|ofva*fZndeTGKw2OeU4N{Y4>$V`C6%nbvn z-wEodCdCq_jyp_;OjjmGd6c_)A@d!xaCRZ2DBZMge8zXnx6C-yX_($tHAbyGaRzZkuA~dAwSj*;zZU^JU^`<9)sI`8jZQn?C-OsAsxg zhVPTF<$AVNtaa#59J#k`(9J+%^sh7)zh+v)lmbO>EAIsLsYQ$PaOQ!#!!@h*PhRyt zvPOa8-iClyRHP~hKn1!SUng0V2==K~WP~f%b<=_nu9fX>bb#%ft3Odc`nS9nxJgpKA+d!Xk;U3KWq8GKjy0n z)M7%9{;hE_7)!~J{zT>1=_u~|`QyN+wH$sVuNQ~nxhX!Xdb_TK=p3IWzN4y{!WBvD zX2>kpOVNPx#iO1|pX27Qy0yKo*ig7~#PFd!g2{2eM8u0uHm93j_8$=#wq5aOXltIm zGZ<8w{!L5zeX}MRLyxul1h{&0UC6MUKeAfVbfd%#-cY?Scc%l~x;-y1*xu1fFNuc6d5^E*;d(p4?~ zTO<$nP`xL~ z{t!@~T;DCr)4_W%#Tr5Zh#7Nn{q8NZ?H5q6ZO;rN!%#~?@8N0prQC(uxfx228%2<{ z*7Ee&gLJ1P%r#IgEyJHoI6pXbjQJ8ut7j#X7JXm82zG1p`S990tT{NAKQZiZHr#T} z@?sTQ%75cncH7TM90G*9b(Kbem1aLp*w_!+vo3M2zBKV63^Ms$K5M)b6B#=PE4?`O zp6Vu7{Zbr_Uh`1FhIRXvedng^7$Ln#Bc``j@Z8lCYjJ7Ja@DeGrxo&3HPR{o3g7eC zROs}xZ}Hw2a~?L{*svP96z$_+7c7;q=zw<7N^EQV?WlR`RJEd>9pafufHm0gR>xjn zK;Wx4H|`Xf-Aa6^5*u5Sl!SzZx6@O@!s2(6m5@IO2nbN?`Stbn<>l?|?d9d=)!oa> z!?V4kP1WUp#}0pWwW_XeMSFYuty5h`d%IfI>EYq!<<7~;!NtYi(b3V>)zNsMub`lT zfkKm2A2IQN?SIE&IRpeGTPX1UeQ(9+faI-ea~5&rvbHDcer z6%?BO#l_9k)?S}3l9G^;S}&9;7KlfWjEsbxYKbxlh%S{PV}BpWihR7hbZlg;T1b?k zsg#HW1O%6tmv(j=!<(C%0|WEp2IfDkdf-qN1YU>%iy?cI%}|O;#Ejn)3fnOyb1HBQI8jh6V?R)I!0-!@|PGhJ{6i zg+al>S7TuPyE@yu*ty)%Tbx`RY?3+{o03b$!^gvmDuKBrbkQmUcDDu>o&CAC;kH`U z=hWX%p^9`$tj5=2s;##3O*FpGEqY_vkes4XDQCA51FjW}zokZ5UNCZ257!-CqLjN2 zADO1tf!5kLU)P;J_=D)Bz>LCkh<=N55dkDcg;d_PVzq($uXpE0$y%yO5ATQJO@a9< zv>X2!tLZMnojcH2oV^|+GN&RMR$=u_VDaHc8z)}9!FFLWe&_E2gY z{7449lnQMO!p*+qKG%L}|1(8M=epdw1Nljlu&P$l-R{!s7H}h4v^F!_wqz))?SZ#K zp=_x%q9I2=R+qoMtVC#n1y(pB&L5eq_F{$n2ZMmzv~uafSir>J%o2`R+x`1y3#Io$ z_L?6a63ZT;_B}KzWoT{TsV&~iW1Q|2tN9%bXNgAEfc}z;q^g@~M=2?E560@NKa13z zeX*Qq?RsI@vVh+o&VvQ-S+w)poDP$H9ZVM&?RNA`NlJ-Lm#BKa&^a85MhujPDO9lY zSNQter9$&3%jl!hTH(6GP_f0jC6gX1eM{BFmwG0D(p!>h)S45){3=HSD5V}_XzaV_ z#4LHq=>~R-%wKlVxeO;biP<(t#OJBc=dp+97d-dG%I4*xDoi~+GeV7z=-beeI~vTf zIp$&iH^ubf?AxZ4;4o?>JM6iS_usbtwgE$CX4cD-)0>_8PF|IxQv=))dGZuCGcf1A3aN06 zBZ`|8Ge8=lEtsqF8mGCt1L<3mRj0GZ4tI!=`uxkpDDg;;jDTW(NFX)^Y_eya0Uks8 zwy@kHl=F~6wTnn$9_s^N=|oVX31oK_OFkJ6a!VMbaZ3c_6qfnyIMH%YTV|#}1M})T zPZp%tK5S$Xr~q_7BIh>WHY%f#X8jl zB{nhTG&P!{%rtOl6f%iY08;#(oPPFRXP$k}>M%i%8WDaT&qbi z4-z)t-OwW8`{O;#RR^bjYwLI4O8*fzs*&#cj*bo(Kn08wY!O6ZkT0@p@+QqXF`#4Y zu{rpFDQgjK00JNin8wF));7m>GrV|#A_n)g=cvm@Wm@98!)XOK%mo1}_J?F}M61!W zr8A`%q{n?26BX$-?%~}w4gw&}_#x*vJ^1mz9as%xu_C8)<;&d#c+TlaQqU(7%hc)v zF+qqkuBdE#fy|kb*r?#q&e_vY0A;kQ`P!5Z$E^vi$H*q0c;q1&JWCm4oOu}na*i*? z4qsd{i8vymfFPK_U*qz)>Ylt_KIePO=cqp_V3n`i+9@M&PY zPtSWj-kT}i4gl_LkgW=~scLhm_hegPuNXn6};`f)TGnhx{Z z8Mbt`^-i6XTj?f|W3$$Zw$vxE7=GkOh58?ly|x50iRUi>@(^omshW#-TkWp%RA3}@ z&DL>>Gc?L`+}on9-uxd^UX4Uoo=af!LsMGDqUt6uR00vUn+`=$B}WdUEb~s7Y`uOr za}p?raSe8DBO0+N5}724EfXbJ_wX|q*c0p$WcGh?phZ-Z!=K(b4@XGl08wy~CiELA zX878eQHkw4O~$8$C8?;Uj|FZI<&AMbC24+$5)#>{CpqrE5F}rZg~(&1$JR35mlzZX z2o%yCN`QQvWP%BI5VNc`8(Pt<#R5(Qbc~oX8$6rrEL(A%5;<4(|NqVV)8&3}vQj=; zZ|AnxMB-P`jdiNO6Vqcif^aNxx(v2La3+2GTPsiNxs z0?Ge(=Wplv)Zfz|J*E)q_eD>8Q$JgwKRuI?H?p6zMDip9{YPR zf3Vk7hK;P;h*b52#8f3Gq&pSnlBC6AeM$sSwhk^nJ7rGUuH@F;2V2qkQ9|yvRQ9&$217P!_ky*NW)W0q2{|+NEZlwks zn(q5%pu_=0F4gVrT~%19kdnbzkJ1hAX&^%e2@W_}!|j~kI)Ul}Q>dv4VUgGnSo3HU zNd0T9Lc=7hPBQFy<7IzU3u!_9{D@Zm>4gzN6=(Lwq6`KP7~vFxAj#f%_2P_jp*HK4 zQwGYR`1wWOSJFcG`6>R~^PRNH${~rE4EYPy?+k`C&|{f#7TU}m1wiVDLiV!^;?mM3 zUNZlG1`BPE3$;UnLiYXz5E_$E5>Fr-wfjnr=SAZE{p%h9<=ia>`MI>=K&^*G}XS>7>nWx{TrqeJiflY1fCB;Mt%M=#?uQ7;a_rOgj4q2eb{6m&g zIssgNk4!S{6s>?olt>gskg3=kFqHLaKV9?n^PJmuYVXmRRu1ltLp>(!m4kuy?tVFb zZeRe8OtxFh{nw9*(_T^-m)it2`&b{5&z*==oWXd1)$u%ryFhZ+R+nc+i`yQeKyc_Q zjG(K1S6!pqwvOPNkN$%$J%Pc$d=lg8-zzF%mL6+$dPyHi;=SF+#SpA~eS{Ruf4_3o z=apsRo;=NNw8Az07xfNVtE9dLroOxIu!bM zyMOGy=0Sj+sx%h^J9?K2FfQ7j|EcOpmXR<%OocD%x7-rTju@6@3L~jCMazbr1XmgF zyeu*Y1E*Hztmmz8ri6!UV-nQ}yKcTKwN#2GeCu1QvYrZ_YEj#1O5f8MT-6>?dnYO> zpaSBM!6nD=@GdUS_Z{0n9u6Al6Uak#${$|9xfD#2L zV;OpPhB;P=T? zLt`h3se_zj|IOldGh(9^M<;xs>9UJ8i0SXU8I!8F!uOI79?<{UPcYG^+B;)^h_w8c znMb*}hn>y8p0Bx;{BnB6&EVz6UA02{;os7=2qfF&)C?1Z_50;Geq*iKC45%P21Dt0 z^8@;Lv6?pnRdjDSy5i9~&Xsb>DfDLB`y>_IK&HDIQssf{EB%-GdYx%C{TL0;{9@%h zSJ!hFQld_v!5obKvRz|Nr!EI;vdY^?q!fEA|&2&Cq53!-E_Jdd}Rd6>tk@M zT`^g#^cN65H(K+u>k-#~8)KdoZYI(!?D}xP?0q~$%4eUEtreuf%nyl-Gk&UcGu}Cw_ zE-EGtHQ?S~{b7r248Fv`4L-BA4T22Qx8{8m+@NkyLQhU}zfazPS$*=jl}L7#7} zgJ-U&=b`zq(^3K7u>&6<)iu4w{14Jv;RKdtWF1eb3&I=}5{MrNl^P$^nYARL^JN=( z!y|uz#E+-Aker7TPA}hKGtvxaz07X{#@422T^Kf0nU5< z;$Pvge$#~q_)*+g#dD=kKxHJ0h-lcU#T+E#Dmu-KJi4kES9e>I0`mh3IMl7JU&>Lmv1+s!_1<{-UTh?E?ajEU4{dy{Fy9m;S`S*9J-uc{eW7U!?6@=9`nyR`(H=VC;6h7Ng%<_xC_;% z4W_yRZwz`6fZET1Q@M>a|4reg&upqg$g*y}8lUU>{z~4Mf~vJ__tTTPzCf){EMFb^ z^2`7vm8MBqsHgtNIza!xIQ~J-=JQ=vff>O@^7>c3gQN4t8TH;SXznrC;|R00{sb(h+oCou0~l zlOO?9?QK4NvWL0G(@!N22wtsUE&5N%^fxF|qfRs#sv`2@K7we5=yUN#y5SML&_v^|DnX#5}ivij_pcb*KijK+TU z6$TM5L3DwxysP~H;fwWIh)82~a=cF?(PhauwMcIY%|KttGuF)u;jMu2o*s{XnRJR`(I=2 zjX8V9fLsr6c4EZx*Edxo#MY*>xqj-qnGg}pbN_pvGX&EP*W*v(Bc@3GAzS?$pW&e# z#;%HbR=w$QD^C0K^7huVURt{;Qv#jFllb{iKK@4}>ik?d35rx=8qx#uWki>?aT%D= z%4>(Y{SyYJZ>@20lKo>H$ulzI8Chl|g{EVQu$jD2C0L~=L<9~xvKiPLW5aIfxy^Za zmicJ?Du?UK8<(Emd-}xU^t^6#7(FPXff9I7A{lJNHihYLy{jO&9!tx7JwMCLHSx|^ zJJtqdnR9Orp~3B-(4>^DlmnUg+ypl}E@heL#j?F_~~rwASZi&DKht9=xAvnhhUq^xgx73{V8Zv8R;572Ktw+rxH#uKZAU4 zq`a?RO2^LKqK_pkpM*n~7!PqeU~INluI7uf9}zr06+)pRB6=9SN#aiNOE4->!RT%B zG?{rz+XwizlNg#cw1Z&`QWWkdQ$;r9=tZP2429C}-0nnyVcJoeXTNf5T(X{vQm-d* zxv{ctiW10*(|?_rIm$VdmuP6-azAXPM<=eoSpJf#h16^i3%3G0@|`CBg?{>1 zY_ie*AXr?vub>+MfIuWHR2rEbjlV*;lauvg71X2eS@%A%bajyY91jw?^8_bFzoM4r zR;%7MilRxz7mJ}_;xR*~40&r8MbvUV6%tqc9_QXkk^Y)0B%pq3A}D~AbUkMEj;V#| z%yr^lX)l7)G}!^B>4wHO<3fa;0Xz1b(`URkBkXsGG|ga}y)vfFlVC&<71oCmVEQf0 zajK@H1~p=ERzzvmD%M+U$!H|!$W&srQ`GTqOy{>44Ow7N3+6#bg3n3cgr`{L;4GBp5@<+|Sz3=j+&H-(}Kgi$0nH&-~oJjM(2 zm-2p>F~(HwXoss8tKnd@N*s0wkWzh zq8B$SJ?j2cwVbmeFx($7MK55tbMJZe*)RwYj3bjx4lJ((F=r9$)pGPFf*N{DGc#gn zfY4l$Aj*9Rq+`7abD3%Q2v{nF)rrx+C|hF_D8A?@K2I0PGH}Js6x2gl3>-NeXA=_X|H#&|=TFO5+({DU&_DAH=gO?=S}hRyg*q zycOY-@KxrvLu2#DB{3La(bjxbejTklsp}~m6FyxubJ(q?3blQmuQwaGl=qKP<_pb_ zk)I5joic|Uu5CO%w7j@4<2E~r*(=lkxL`kQ@p`_LOM%KW`d0cq^i`YNhX6vSc)qtN->U2WhuRTBkhpvRpWnExjbl zx28w}?#5s`hryVhRcujrPcP(!=n1^d0T^?06GfB898PFR@X3Ad3@KT;qCZw$m~wdC z({jgB@cgCpzor<5Qlllq;{4lNt|OUef2I6+;jx-M?T8nl^iJz(bh{ditt9PHM4i_A zDyzl=FzIGFJ5Gh|WX{X*ehcWeu!O{aJ5V0~m-N?8tVqsfo5`+HVe<ba1%;{5%uZ=Q&cCorjBWt2W> z*aIm(0^cPi7WWCWJ}n-B~04v#do!Z2!~79d)t{ z`RwX!Tc6gK*LoDQkNS+?p8s9K4117v66i|#w#p}V=B@lRM1$}9*%dk;FC^<}V|t05 z*`lYo$^E#vq3D~PwT@Hna@swWuc& z;g8wcZXW$BO?MEzqbj~4u-Rbn_JSE>M_J;%KcJU#hm5K2ai;m`cdK}E0gc3fKF4q4V zKqP^g`gGi2AMHup`vrExjCf02c^utG>okY0>-iS6xmka+0bgB z_#UU&N!DTWo4yPBugSsU-XF8*o+v7tT^)wGBq)MX_Nsb)u3K0e`Lt{ z0JD8u=B~VIOtCIw&?pNbkdm`#kz0TE^itefW7~@+9tc>XBVO?>B6VM?LQ^rqhWA?B z{#$z=4txWaKG=9%Q6xSIEMs^N7k6=C>r^Eo2HKC>kBKz8X;~UEuk(n zZ1Q}5Hi_LK;;!0%RKQ8&pAlD%eii;_#Y;;H2=iO7T$Z5y|98F)a>G9O3%no+9X`)M z@^%vMu#Y*kidB*-g4uU<{H%(tZAcU;Q>r9|{V+N40};~x!t0?lL;=-#J)P*h`f}D} zU*%%e<{7Twz<+(96zc2(U5G63tqk|2SA!gW>Jb1MNfp`poW%%aNr3~|Y(GL)dIYfU zcD|PQX2UE4ge2z#%oA67bl2#6UY}lu@U>;SEOcemk(sJaZv%?H6L+v63h6hQUtvex z_!|UH97nI=im1a)TI{b;FaQ%rF2B>;``EWD<7UeozK<2^o%hx{9}(BD&(3`K$h2bS zd`lQh!2;>^@i=Hq$5oATEFqTR0Kfdt5)yD-*PPeG5n}>39Hh@8q zuz!4DU|=7I&5s{Hg3!PB@2H+1CezCJjDYu1mp={mpKnCevd-2%D=_#tDZ5so;;C|< zO6}}oQ{O%Mue0?6)}--5fCe6e!6YO&A9}AGFmN{Gk_PeX5{J>72c$v zvx$Ml6()DwO}L?!^7K4>_S-H7I54XZF?Kt7zqJdbRtY>N-Qgiq7ef3?*hPuT%A!@( zSz6{Smei)K+UR+hu^0E=YPWOoNSqoghJ7L`aaCp zRW!)+9f6Gf!$4Ai#w4S>J&?YYQl;fV8gk{((!6d@(>w^nm6f%+m1b@K z8-W4=`7JPqL%WI@!WJ!*YANTAFV-dHGpMZMs^mz__VnZif`mV?4jDGH?<)ei&dB#4 zKKZ|B97gExoUVZ>LnBG@H9`UJ3M2M{lkGX7ZG-y5^Ly$Uc_-mKVtEYs@p2ZIL6Z~ziq{0-XrEx42z4Ciizcg1kXvRI@7c3 zBI$t1b!Yw^&rF&)7Ajk`hfp+E2i+v+qT_dRW65fKl}UC*6aEMqlBxOQ)M{wRwhm5p zIVu;iYw5WHJ6iWw7N8V&lo6hyoBM3ZUyKx?a{KdbFwq$J**y4I=U~r5 z4gIq9FWxpx8g*%dNr#sxK+xc)*ElA)u%P*Q=8W$N{m0Tt|6fdQ{-Xa6(Z_{)BXAwD zU*2{eu|+Ghh16@63=i1`#V}!7-{8*(%eCDzxdLcqg4*0sPDp8%72wILPTbHt}e00o>gh^k1$ryGc zEF08nUv{XV=lhc}>?mk6Ua4hwm9$jCZ z3j#CM&>w`6B~h`+_(I=7RT1GkxJ^iJ{q zZ~|r?(a8h@s?K%Pt5K9wk*Ho7fJ0=Dt`G4b&DNtFF#H`{K((UC&O_%h^VngaGkK$Ba*HO}}NcTxEJchg5>DET~_1ZxP`sb<6 zH?;_8ZXLfx)C1yd=dk?kozvNDJ7a!4eZ|*k4P=X|z9#na}2@RUbwqcK6|LPqlZPh#)O)jl7-f+grUoG^S{BVZE{T$R)E| z-ht~_D(LwX6i9~?(s+qeL`P2vXb250_{M6x&KrcCY*@d^^B0}-cIrS^1T_+g3}p)# zB`7SkviG>$R{%N^(S>39y&313MxHO~>d zVM})sxSX&w&f@wa!t9Nht^7S=)%e9E!E>68&u?v`?tZf%Pz2beh>Av9G8q(+_+HGq z9I-AP(;h*cl&}J%-FDck{za(t^T25{Sm(c%{_*OrLVSAc48vpFGrw?nb$M^!j_vLR z#ML=1^(!8kRSH1Q;xZg~Hsi2d`c840e>BcVK#7Y!$cL~wQB%2GM_4RfjCa%PUVW~) zj6u^L7W={72d0@04^@4*>f0O~bbLzES1EN}q!zqEklEw)oRm=|NCv^j@(D0qUH9F& zWfz-R>-1;15-ya4TaPs}_vlkIj!_6N8WG!bnchVp>oEorhDJxuk5ZOnk_><2$^8Pv z@p#wBSsxdgepx~^I{kb5(}OoN_HwvUcGgDU^^@N0bQ)~>qkb>zY;JKlr=V6oB=+R= zx6ff`psBa65+qT2m(0+)I_Ok->JZ(_L7R^uhp6)EE`37~>bxMBaM%MUB$aZKkzd8(c!94DDhv@@g zbK3v>H=aT2q+VCf4IR1C=^@5ko$fu?9NQ;kZG!g!gCzhOI*hdMiHGk19ecvX6{-&Et=$q%C^iArcr5j7*j<#a zL{$85IPk?f#Cw^X6Cav%BU4&+DSZVXa9C` zxCBx%D8xd`{zeEF=LyVWc^$%V(}=>HNkk5%F1g9Ub3H7XhCgo<_9eNy+L15H@>Vuo zgLo9-XKU{X-8|XaqFMC?; zZwK0-Vlpm!kuT6T+y>+^D%U|DCw@naq2i||1M4)srQUOf8$7$NS;u$%B}~7oOh%p4 zLnm3-JUq0-n%|{ZmordAf{88=+~yRt_*BKhJt?bhaSp==X&hh>KI&8+55d-6ixKN1LLR4S3mo+vjlhRKF zWjgwtCQr;|Rh|dOBP()xjV*t_{N`m;rj!1DH8IS&(NMeH=$8T~?ziFmX8#p3tgcfu zBD!w_VDoX^7MRuczAg7I^NvbIX*ZbMnl8JRAX$>addQ)`L{N1 zT8lzt@7tSSSC`Pb)Wy}6X~ve&84*#ofq{Xo-5ca5P;Y*IeqF1B%p@cvEG(ru|LAS} z)YKFUY92u^WcmHOLbKXHFyBi8f@C33_BP_8W-1=?zJY;I(TKPZF#Pz$_T}Pg_xI?^ z%HZJOfq(b17gQKXz%K`Y#l{~xQO zr^jtg{xRWrvOY@PTK(JnHHdm1HPX1+ zv7L$aG5H7o*v?}N-rSH5gI^UFlZaXMyV?Tr?z-bpkgsO8ITkI}hjivM;BvF64?b|8 zi^Qgh=G!kGzP=>tgoF43#2pasl|8@RU`pWvqPo?$i74G;@8|E_pL^`RnzFt)@6TUX ziWTV;&`tRH3A#NV7H7k;jxr$7ior?6cQ=`&612SV2rA{N&?B*7IAkviG$qWg7pd%08LVIF({F6DT11^qR4GONfLwh@{Zd& z%%aO=7D!GPy#aO(F8ZA1Lrw{iDJHF)6UI)nyzQ?8HwG3XSa>iAW4WYZLuG6loA*?>5*s6PsGY~ECV>BQwEibx&d7Oo5EG&4RhZHdWnQeIy zNe5IAz7jD?G!THBV*y|>1b0UrFhD>nYuO_&g!DqOE+xs=0i)4+Av=40l4>;2?PM5c zVnXyhcT7Iev8l~qDbGF}j*g$J9rtP+jjk|jzQk-y(FdD!kY)nbakm3NDscl?lC1Gy zaDW-+jR7rFa1LD8@&B>Dhxe%et9yMXlc)n8oPmID5bz|(N!Ie)n!f4uYrJyT#ha7E z%!r%q7`AN`?WCiI>K8 zl9s9iAPrWUARSf*bQv*UFLZ5NK5%Op2QmdeaTy@3{dpU;#TZDFUTYnwg?i>h6Rm)U z5a5SeTv7m}YIvkcT=#zw#2V;3lngEs;z|S8;(QEMh<44BN2~R?iLh25OhuAcJP>F} zyEszVuSq5m{p1908@Fkz2L;wpow}{Q+8*1ma#mEdw4lL?CbG?&;gX8LZr1~2L3m+Y z=9P7rlAx_3owM>(RRr93Q=%w1K;gBIl$X#juGR`OSHIuVj$tRgKK~Nd>3#@0af7%H^6T`Kv%%k}#{tZ)- zHF1UmC3Ei0bsdD1L-kMU{^u&Uz+qPt|A442($Ck=6wM~sP^7W1brUU+&4-<&k{XAd zETp;BDR#j+$T6u(x7Gq*$9i?3tw}=f^u(xPj*Z53dR)z1iAH@&Os`KCx?NY0pUo!B zHa^6U*+9c=SWBRdDe17Y8Or0=0g*YTL}0p)7aUmLtNu^c8S;+%7W$uk-4pS7L(*#^ z&6xbGa48E2k>Eo{YN)Qzc`lh?8S#~Tx=I5$>@eXBj+R2zvjGko>~{Sst6}CnwWS>t zhI?ia-<2T9qkyfmkQ^|LK#B}YB5xQ{#O?b(eDve{2I)V=Dj#T=84n57fsxX|r|o$u z!l6rNFC{bZpcG%JW)iPqfssP?{y_8_F61L18Xf#0`xGZ&z^gU<(SDSGT?&pr`f;;= zlnomm&Dcd020(?7b%_;i2kvfOcf!_d!r(wbu$*;(x{1?P(9mXt0+4(rjif+Bfz7uH z0R#Y`Eotvk{%6m7-)&@0Aik9m0RWBGbH=sz;)*WD0qC*kltPB0LfHJK3MujpVG%{s z!5+*AZM79au>6814qz0K_H^|FGt?G9!JtHJmZ;4j1^}kh=I3MLS)cdPsM$sXZg;0L zT0Y(hZ+eYvG$Bcyq)I*UgB~%7U`XW2)!@imCLVS0DUi?tWd_7E4a4BcVL8y!qylKd z?#L1$iGWsLFsM{xt({fD!mt$1lfp^+pU(@kW9NMsX95E2yt@{aJh@f4zHY>5Vz@N~%KCQU^ z7(TE+9pH9Pz4v;>#JDG61m-cw`#XA#x0q+c$?43r-!|hzvH~!r%u;e%)2HGf7rwd)02^lYQQ$V5^kA@fv$kS!E zw9p^fF7z_TzlzlvIlcK*hBKhIQ^`ACtZ8L!boxMEZ7)} zikOG;`AKc&=Vv{xC>>2O)^Elw!jK^xgGi+7aE8`$xLmfXW0kN)&b06QFE-;=kIvPcnQ~Kj#y>7oHs6Ru| zTXB2Os_SogQpx7Z9>4H*c9HIR%l4+jYxAf|@N_x#&1Q1`*YZ_C|Gh+s{^vxf51*F4 zyQ#M)vR54VI#+Z<(yND9@fD?cIZj%rWRsnXJ0=Q!YgGlR2^Ycpo3p8*v2*;$Uz#os z;e}4WD8*<-oD`!;lr9fYN!&o--hWTe$Aj0do7R}jK7F$~V7r}1u41$x=I1G@+CIuw zxYiJo(OlH@%=&uiH_6XhG5M~M0JsXmuFZ0Yx@vg^Z%62;I4NO~ORR7{QE%=8M) z?6j=xj`U#zaM9%<;fS|@1@iGq8&+lsEQ}cKNz#m!_ex@~j8j-ZvDH@)J01_7OF4Ul zmR;Yf_Q!^z1_q#9k#;`-4jI!h`Xfc^=q^QXa$9Mssjf=IWvD7Dd*p;*v+4Z01?JyP zqM3_&p;z>UqzM}gD*&MR4xGc!Qyg~nwq+NE;gn_LH)AoD;mjxoffT=s#QxDr zhvWVjeR@IOTJN>MU9xnLgUqjD;=3mfzpY6?!1eaR3(GCfM*vCntb^D25d4a{9k>F5 z1Skv`>r|#H3BL3?)jKhq2U1z1epy;S8~HP*`AgP8GXwNO!~K{p7df+?mDDC!VwBQ~ zmFBmU*I)XG!3kuD^JS`;ZMR)((1&BIS-Bzyh0p&i0>-U@*{Brsm%vg5sD%7dOxeSj z59c;BSA|%r5VM^OQ||hH`Ajr&Js4}dnv$4TiaB4m5CAmcm$F<)Sb$z*qM^dvW z=K95*BY{eYoe5fCus5z4E>YJ?N_qgw8_?bn-8$rp9qfXFdDU@%8v@Q=9Yp*h>R)^x z$4jnKt&CI0jThz|emlx};QXI_RoTo@&DFQRE>qTwDWXx@nRz3P*VrO@{?k={*IdWO|FSZVg zjwzGH^7i>ZUdt606slPOm65G=d&?8);G30cPm?y9=?`Wiq-$`E6;HO>3zJ<@noxjB zaBdq0x4B_+d?T1T(o?z2VbX@UqmqR_ZpX5si8|Y>&E+EZr|O^G!zd13}5uprs9SE|HrT5t;a|rW+Y|F!DNB5Y%b^br;PhFU89V_i!YV=8WdQA zP=MWYcgvkvMMRv+ZhM_XQH|&JEKs=A-eBzcABVr=VrMLM_2laFvkCsvNn%MFEs&El z4SeZ#3HMEwo^q)#xXtkUNqNX`VDurM)rNI~5~s~YwZd}{4rIb9fMfL7?||z>`?WSo zADA)`ZALEoGa_}rQ*HmRp%cU02+rji>+6rlcAexjj=MjGAKrQ!and}=TWqmAtNdnv zGm1jP;CKG0V>&%}zy(+m0TqUV%isJm9YS>5nHV$KKH2dk_Y=u%G@mIq*@T}FLnecI zFk8LD+Hkjhd7Npd%<7o0uOnQb3jc!i#Amd`K`d+I{RH9u=4To!D4@2XBhnTiv)izJSMpRpHW zawp}Th}QD-d<<96=h^#2=Xvmxy*~t)#kYgapX2!1wzhn>dFz z1%~@-I{a~}+g>6p=fg}~0bL~q%cM%$O7*N$la_QYtr(ABc+~D=J(CsQ^<_g8vD+i=^r{lK< z)f_kQDt$L@sg}FRHTyR{kwS;amTmAviI`%T5a7j0je^79BIJ zMXRvZUNa)s%}1)3-!Ny!?lG%>aCHquZHE20uiT~kx)u~fw=;Gye9~^HEs>Rh`X^ZT z-o*h=?wm>>7jC{vH*3{53mVXECGDq+0FY?W3kzT?MN@ZjgH!hloCw4%?HioraQoI+ zF-oG_rz5Hb_oRK&6N0yK|3%q-`~*Y^NWuR^I3?n`7alyn1cLyNv+&jJjtOd-7$t|Z zzLkObZgEfLwEAbeHXLrLjt_>ZeUXmV)gx*ur4wHR(3{>xAZFqi-HLWOD|>-6p2g-i}4l0G%maLcPIc0 z#x^dCNvK2`(s4AaMd}|o`T2RfF+7Dfp<~fh8ck_X0+SA#iICYRYkZdIqDt~LxYJ|B zX4oof9UCrxg(0^P?^P&h3d86AG%BsOXdWD=Y()9t-)&OlS=cQRvs69h!NpdB_Pju-B!F{LLA_Vsyk+x*_XUbdXYe0j|GS1B9Rbl4r{^Jjetwqt*r zsFrDk=%T0Ri*GKx!@Z%QV$X3$hd$nCBS?s^J6&t@19s1BH1|2aE7+Ba>O7AU9F{t* z6rGFmf&)`}pp~vSF5+{q-x^hicIA5k1|TNevqFKUtA)#Er(IFTE?n++kL!!7@d1KK zv#25K=+$e@9^nc<0-hbC1n=LZ&iZc zK0|BPl5wFC-fG$q(N7b{Yt;&eUgIhhLCApi>u7{<0J%KD|FGngxuxe{zZ$5ix#=hu zZ%DK^?01?Uj%r6Lr&qh<@N*U|d))xmm7G1id17*6D1XRO*x@rWLWV3mT1rOdSN=IK z=1d-+oKAB(&WGG40^W99;0hI~7~Ii%FY|x|v9{a(d8uT@xfE*Fh0)v0dMA!>UF?gL zf`IpHNt+X7ezxy3!@T5S4vG*x%}8vM`u_tj#YX$T@zNLn6Ma`7%t0LgdT;md(0yyc zKR3_jRU$Dh^JHk1BUQg5g+hlK7hyJk)`P}ez)U59iu~|V6hjR95)Hg-dT>8@wto6( za0VT^;V1kkQpeAKy*}37`grDj9X8_a#cI9Y2W%4F1plc)(7Gme&i=hG82qOny?iKu z2kU%i33jwK9R`%vF(s3lVVxsA)&&b7KS}L{C1W8wH1U}Gv~%UsX7qu@GD4n21i=Vk zdJ}BIxyWh1@h9PHOw?$%h{u!1XSMxNOa&@h|Evtr1fe$}ibA@pPm>Sdf%Uhu%oIKy z)K}Hx{HS^!8d~4rL)ueXZ*)92Pw~8s**prKXwA%{0?sOuK$MkXe;ew&YeMu3{>~suM+2K zAZULS&VRIO?0nh2Kqggrc=p_R2@~%|wy)=IoJ!w_#sTwy=)9IiP&x&t_)kNaLPlv1 z2SXElSMUdc_Aie&gbRbOd;8$n@9PWcdR*5AnwqPQNN9$z^CsYlU;DO@2z@W-$;aBt z(WHh(tYAH=1HWC`ba_;+rluFVBBJw{Hm<$N9|_kO0}R4BVz3R5caS-qTFGbeY(vBX}eQyOQoe)?w{k(|S; zZ5f$Hh1pimPxM{TL??|EJ1iqN+y2GRLssyHkFo-m_nG^__93kne{!Mf&&y6qTna#> zrM3NY$Z<`P@WzApM8Jk>jRWZ1@6~fw_P*{3csk4iRA(18BB9Ae5Wx&Z&u4*)T(?0? zyMrtRWVJvwZ0*p2mFp#r{r-*s~I6*u23L>o_!5wnNqY0k-?V} z;*k>NYkPcppy`jbH&XCc$`s_|c|S?xT)+g7>GJc3L>1|L{O)LFMJuI#dWsGRVEg{8 zi9$pG60qBjF9X4iX{Ec^ALrEX+eVE0z*qmKcg17p;cz}z@@_C#U*b5wO;5c#>hql5 z+XECEu9V+foE4?U>d}<#o{Z^5f!#`6Oc&-E*GRLq^XRgPmZ~STsQlk}zKOy;dnCr2 zVuLsesbq*(6_s6{ttVFTlTy#X>3N;}i8=97B0M~NXb^9j3%vpENXc&Kn*K`MFRY4Y zz@&~)^OJn2&(cY`b+0=7lYI59k1N<*m{?u~>!o{aJsw$szsV^PxYEg-pnpGfMkw%P z`qgeO9kn&CO3$aSfa)!WTb5Jxi{{UXdp&o+GBUCs2V-L|=; z!Su^KN2_g9Z<|g)0Ag^;Vs+tiM5!PK%=y^K!|zMIIdwCuGUUF`tnR*YG8D!%^n3W* zN`m^lmhtbgV5*ZSP&6R5Q>fW+890$3%x`X9Ata@q@#(!QO4vd?_-gNR@7QGHBBiHl zt0AeRnCIjK=-xZ6)_cAKcYTHw=2#^5emSR@|Cr&v2iXLb!mB-jDeBXh;zW< zOhVxlQ^Z=JO>f~zG8J;Maud-0_h3Ov|kSjipJp120w^{Gz zUw&JlW@z_!f?!DvJHE^47{M*tLiGPA=?Nhr-Pxao=J}+bUw$o?oL&%1-C65$kCl`c z#6*x#+<$}uq-!Pu|K=}+qg~D__s^Iz~uXtXnLn8Uis4#-)7LR=>(j|UPbc4 zSLJE3T5&9DT`>z&7ImGA;xzGYtg_*1WEZTB+ESJa<=HP>R@x}@HIVaQKN2e8fY0-{ zzf0)4L{>iK3QQo+%8CyN9^o}`&y=s=RkY3yUpLbn1Mmjj)@m->3>)oC|Jnzr=e0n+ zRef1Ab1&@k1WIgD>khcI24pk~uQ+W%=` zx@JCS`~xiXVOfMwQGG`F*w9v*4xbyJyTj_=zx6itz5tjQXu7Ywr%US8C`Zopy0ovv z!>vilWXdmQ@mM~j0T4|jlC(oudjnWQY0VFqJEv1GMT0VGzGD!<1aujixYd`t30b1x zUbmIg&AP-w4`@kJ@M75rPyVPc{@B#^ZORxdWoj5pW9%)$4BR{M)tEMKf>Z4Rj)f`L z-`XVVOW!jvy$Uqvjyd|03-pu0RbYgWPNvv>Zc@i{{23VrDNn^pXRqfJ%fy# zO93GvUf}JkVo_1?Hwkiope_J)_Oti17uv|maCL(a-&JLM_|Is82CIdc(UQT5$J)87 z@2n2j%Acd;Qb7apbMS9>RfqbbDR+*;09@fMt6s7}2Zo z^3LlsV8pKBtdnctZ7nWZo6mVW+CRq(^XE6BV7I=PBikogarD(A)Qt%-mtyObN?bYy_xO0h1r=HI=fR5g0p%$CB?)6c<58Hu85jQKya z1n%Gt00H^OsoT2+AkK2y^$b$(wGg5R0s>nE5S{u7ROO&w4CKC01dKf}&}$pd#bulu ziTD?y(jbv#qcUI-Ahg;!9wjO&8OoT{40!wc(k6jkA$aFZz`o7pR!iU z)GDa#K+C5>1v+~_jvH-Ha^>6Js=HPJfOs5yUs~@~?!drUG!s#K3TLAY`#&{0kJ4Ss z>6PTz6S98V0T~OGTY*ftPG?$>WZ5(|*xS>nv;IEQbS8VK=+d$vhS!1YQ0_a1jCqfl zGyi?Yhl2MvCj8l7WTvtw>Gb`teEv(|o?f867C$sGVBK8Fx?8@$_4#B)sEbk>&hPP> zshGp4g5z8Q_G=j|+7BL4?Hdg*LR?%0S4kxemygRdqw{%2#EDDz30vb8n*F7%V1F#Y zU9w5=4FPG(V2l~IqJnZ6W3Ne!XJq;N@R|@+%wR(sud9))P=-e-~&KxH{oIB=`RfV}wswsE6@&0dp_#GrA~8eR1cf!z?s5-#es*YY8pk=9U3p>g_^2 zHQahK@K`bSC&StJJ#;3*NZ7?39!a3>*4|3hms_32Zq6?%9{_x-b(se)T1DGdCZ z0E^6b_vhG^uK<#hlg79MH_NHAP$Wg~yVdeM^5V^P8+l;$ZYk&B&G*z9`fw%lDBaks z)vogF_IVN6vFrl8#3qw2hkVa8WYM%oPD*+@1{i5gP5#&BnDVk)s{$tnB7s+V+V8H_ z@@bTX=y+^*jB#Fp*0I2M3r&Y(qPFgB#v2M?0jCyB;^H!zmVsX3Rnzj^bGf_S8Y(M8 zFx#tZn~GYeXvon6+w%bM4q@&kcIz#VJ%f%%^Zdmur-`)fAlH}2+yQyU#-bYY2k8D3 zW2i|s%>PbXK~ZU6Aez@*1LuH=>S9Bx*O`G171G;ZrSe=b#MP*Qw?T$XAGO$scdIXW z^z?Mg-FR3&Y*xSb@2o?I{nO|1vr&Qj`E#vM-?_K8-Ow0s`SBmnVvJlIxDR4Tz3;ZC zam?-cP|!iuyKRklTyIE&{Pq4Tc2b)#=<{Y;NC!b|y7w2@IsUU?*FAXun@p#XXvw^f zc%>nw>eK@c50@ehvqysO#Xr>%ZQjSzpt^>2scd}*Y<|gyL`QV-@SGoRMcwBVnbK#9 zXWSEAsn?It@((g~+B1#}6jVHlt$K|#agwhAKL~xqBQE$Emx?Vg*eik&@FGnlv`)_c zG&VkH^&z6Rwb#r-`-E_-OXce$|@v{rU5(n47^LH5D~smEFc$K72qwhj}7%`btU z0o|^fZ_?~*)!FS&7yZZ40cjJ^g@zeYS{^hj zbgNBAEjG?ZJej=`tEazILY3(AU{Tx59^a$Hl@s}pxxXGJ8Hpc>=iArG{ASzSC2y}D z)8X=X&C93cw?9~hZzkmE!20^rIOTp}*OHCv+v$or$j_ma=W3w+0QJ`nNC>UoqObv_ zG#a3e3W>~&H-_*4lrIh=UqZm$zf-KO>D|UO@s|TaxgLT|U)w1L0NK7B$b=pU+3*W|2mB ziyo`batz+Z`PmIdG_6@||8_-vQgMnL-DR^fSl(HDm6L9|`QzQuD#{rX4Ai`a1hx|S z?W&6&_2fZi+&^0OI5Qw~LZPB>Fz~zFe%J9U(_S~B5MG^KF78V8qi8*~yPb%XV>Prsrdo5zN14hA|nNL)mRh*d6I1c=5O?~Ip8m6^km%Q%i<*mzi~kkT$_Pn$rQk`M?uGEuK~NxM z?@}Q<86FOai3EY@pLhPtpxyerx3_crPr)8b-;-U7ySx00p+#dvp_jy^*Y)tY)oDD2 z4Dn9YXxHzb^1HCy_0X0`lIBcX$nD9F>fnJ`5t{c4G+&($%JjDk5L-EfPcP?!=?9u_(U0d%?)TO|n_X^nj+k6i8O|DCVl08ggI8m) zK6^G3f}a+D6`mNU>uDsXxE%ZmBjrrz6`LWa1D09Ddezm?u5spTj3(~3eMnvc38^0k z>wQ7db=iEXugq7m65SsS*F>#Mo0^EqmXTomNikA17~cAw!U^9^ube+)sNj{^ALxH6 zMMqCR;_2zGD(v7!$5vLX`&{*oko-S$bL?sUZm4kfJD=&` z)?3*wPQccZ-$?A@0WYQ5Oh;<--Q*ee^5(*dN+Y(96b69Kzit1!-^48*%B|1-rZ|hY z>?GlbWfi(QF1M{iIgcg~fh#9#b72FE;M?v9DN4p<3#Ed=blCk@nHf9X>^ehfCGjMT z$=Xe*_Olh|l#$gn;7^mdmXDl_&9X_H5i7qG=k&)@2BHrtQ!7nA{R~9N!K6>2uKsIj zIBW;`)Gz(})JKN?Et7^c@Ucj9LFXzQksmzm(FX(;SUCpO^LjiXR9N4+@PA z8;CX#fTu*Mk4y>;KnM~Ed8*MFqwQ`X)Xt&y{m2sfx5n_-g{j>%nkX8qEQ@jpXdCv) zNse4XTH>30CG&h~IO(QPQ{&_SQ82w-!)vRdk?l5XJ{Q*KYPJ7SGZ`R|W~ro&V};zpFT zT4^T0DXoTlW8`D@$wKXYlV&FrU}Pd1`J_W}NRw$Zt7ixF$~^C8%2s74R|l#Xwf|By~-j3mh1Ou zy*;b}W*Kai4_+BCOutXI1{B4BVV7$25;6+_2bo&IqLkpyciUs@f1BFuVfNaT|h+VL|!8jG-$EYW;0)~&YQfQ zMpUiTLqm_xNcm4IsgfSQ#&qhzvJoi_XPqfC@bZb8E;Ix^^jxm7-H)4s`MQ8j#0|&F zl0r6~CF!|VPZ!kCG~FVQo;b8Ud%&SHuPD}|e0#`0vMd{P2QZ1ggeZL%v!+Y3Njy9e?o{Nq-50k}t6`ojH;$mOA0s`8o3KRg!*$MLl=5?i_N0R~GA275b zAgC)yOPC3|GkxL1|K1ejlj1LC2!@7bA(2HFCE;6P$G9Ec^(j_GAP=*zRh0VqYK)Al zii>6#g6=?O(|sS67GV-3(r}*Y3=bJQ>3;=X^C%djONGS|?W^?{s%SjQ&Al1TPggn}Uhd|T$5`PhcgB=!H*MCJ$d;-Vu=Q!FI|)jljQR9?bv6L7+QA;53& zAcxHFhe(rq=vz&7W4dA%i*CGF9huHXWG@KGhkefz94IbjG4{h1RA*#8Ni8as34Wb% z_=Gkyf?8}0##O~EPT3XhSO2yU!spuj-|_tKKN0W_L_61kynLfa`yrEv`9q>G0#mk+ ztX7KxJF4HfR)zulhlFtgYTp=VB4&b16~n%8&@GBHPj<$IlgQ^I0o^YovQObKzWq1B zvwYh#%2T%46bOmA>X6@}cok2iIA}5QO{wrhe%8{~ss3)xQh++Edvoq3l1d_zcuEjt zSV+b}S@2k6E^IRrfF>FbyM1HSk2~QFCEIN0c_KSHqq^aVODFy3g_Vl_`s98t!1s*Q7e%qm56v>qen%XaKDW9-{I|J#E6%>2x(fE-+gsklLWt2JHMo=@sH;J+BOPHI=k%N4L{bDfJr;F)x1sVJdM0 zufO3WYp#{fuX*%ut*IHzDf=Y@lFWE|C5hT}R|!3}8t=lW>pltej7eRdp2tJ^qOO@^ zWx(wPx0^)YR15=1j;8Fj%6X2?y)V9!AiZ6Ibw<$Ns-~KcLONP}*!COQ@hV?v+8^am zQ0O`4LI&MS{;1Pj7kJ5x{lVxCP{)Wsi<14<{<3|a@oOc>u%jqHe;jv`{YeldX^bwn zDM5xFuFKkil@2G49K))ML7@iEa7x8dum0B#M8WZ+5syS>ZMl^Kw1Vc}BWvNh#Q7B?8fcb$;g6ju7Lv!FuqO_LarERdzlHpSi&F z)BZfS~vwXKXaX0q5<4p5?V!C32Y*&N0V0yZ^za7d(=R$PWh zkV$IJgq23=X&EPEWBFUp1{M9xwSPaeXeDOzBPQ!4g_GHs;2eWG_cn#4JH@pOleXO| zRI!S11|B<~U)iPFmmk=Mt?6g(jaxn-(0uafQcW_@;j!&U;SFHh(a1h3FYFm}#<2+L z62NOTe_CSz9SKUs={V&p7R~?KUr%6_Jjob)I#~P@Ul?rTubXl_zYj{FSDLGFSo;3G z<7tYbxAU~2lyDZrH*(|q*4OCI=VdS28SOtmVepC!@6YCQ6L#%d(eUP>EpTpWbvoqPs~j?fqwQ+`?v=>!|JRL$!?T_pD*~{F0V60T;*DEsJWz(_X$$ zkM@_BRM~_e#h8>UwOLFLm&QcnLS`xigJp{?#^$+~#DXgiXqkS9Njl1?E<;?_7;h1ht?ir8kY|x5U=81+N% z+3IiTZ(H6|ocas!H=$>Hzci<>{|G2w|(66&2n z)|}^4_p^)r&H%tce^rjqEd}IQu3|To0$YFNw>bC81pt&O)@j(5`1O=qwRtf^cFp&2 z^-E#CnGBUGCLXIUtg#%NwAQWe4nm?vvsAyv#HL@ZcSxn|gOF{Rg>7|X40vQOZ;uWs zc65HKDvX+uyb1w*ZC5Uq^KW#g;TNQehO>OixzAP`Z5qw$524>*vpMV=e5@hIH$6%2 zef8{9vVp*N+tKV~dOUqw#WI)4Pu7)EYNbty4dF>Hv<pnY6yRrAVNcNDlkI@KG6ugS@!L7 z0Jz;j_JFcY7EgKVK%7s*v#b2TLb!hf0N~P3T#Liwf4I&?k;8cO!5KMcJj84%XQ5Xb z){9t8@z`G)$rIOk|KhDOA*YbUvNVUyZKQ)VItY!R>5h}^?>Cz1Ie6<=e{Ge2(!VFp z3;<9>PLlV0zN@gClQ*ndlwzRz`1OZ+$0w6$z&9CLqi9wIgFr>JP*$;se1M5OqzFe@ z>ov{q@JSNSewc`?F9sWKv&Q%-*(g#uT(u%jzZVw+5J3ACc>U!>bAhoUMG%R+Shmq7 zb*UC9jRaDVak8%~o}SMNN2Q(Cg=!cIbted2!wb44`rUVVX`Lc+Q}I!8ow@Htn%_^I z8XKl4nI6lmY{o6lH5GVTK71na~bc>k3L4Y&x@?T1>%MBvDz@L&pa}Vt4c(u^FRcqxqk1 zhq9ACRqi~dn4qqwFX43fIG$4m=r;-=^PKmuwYw`Qk(|?nLrwCIc%7!d%Y-gNiZgnu zxEkf_wj*!sK0&%64k*}g3}*VgLszT-Wr0@oRCslt4#G4fb^bd`vm8&;>jZ{?Foh()aP`L{g!JIYC4}fz|hPl z%SeX$(MMJ|Cga(EM5^SSQYJyh9+ZpedGrGg5P+G0u*Az(8YC%SIt)+Mmf?;eadAOj z_Mr?u9L^etE$_p7Ez2tG-)#*nwpn%*28&-Tt3LsxLFT4oOscBH6h4j1x-| z0Kn|(?DQb=<2j$J09rulH*nWh-!b^$S78}5fF|4FP4FnRbRstpMFt>}QlTL=vKt;3 z#i>S5Ux6?vZ>8v#04b~}`J+XeN@N1}P;{o_%OiaZ=VZlkGuf$0RI(>0C%0ZmJ&+ZX zF*TOpzLSqF_j&KcFT^J4_pJ|i#4De|x{nZPrwARR2cf_DYsfqi?EQDE)kM_wr+#jf)=vCNO(D+V>ETRkauWjAzPXFn?W$(-s$uy@$fZB@KrsfdlUa~D4>l{& z`BCYGI4Q+PEpqwkwD@8(M@R+J;6PJ#Qyjq$ZhaSR(~R@7v$u4+Ge!7uKV?0IOxeFB!)92QW7Cq2a60|S~`9Yr}Q_c zNczKWtC&i?TN*;0Cxf*7sEA;k5CONd&W5P#!epl^^yVLzhg`V2&C)X0ab#q0S&le#b!z8OfH}x-bWjWK>7*_VVdtWOS?knzWZ1{kj zSlgDRPEZvcJsAg|j_C?X3*U%ltg}^#Qz`3hP=bcsSpNaPr>h(tVX z6+4}eHq5e|)!x2cq~-_QZ>{e{i>;RW<$u=MJ4BsJ5^s!QZV$!2JNuP9@e2wb`8K;L z2>UWV->rFB={C<`*u1AXa!a3V935XGDK#OVE*YN;>lH_oK9%~uW4_=&9&e_{MM4J8 zK<@`H!3O;(e82n@p^^m_?+26FLwrjfrArq0E>+Kk9cTamN7)f_eP5JP*^t;3Z{zQ$ z(krP-zzi)B4V0HstLOcmSjnTE+Gr#z6}T1bH9pOvY-SzrZItua=kwb_*@8WG?35KB z{tnTtADyGk6lTflWg8AJrH|pgWsrDY`CN~ALqsZRRY-*ZOjl)iHdA|YeukJ3Lp?WO zB@8SjJz08rbQqa)K#}ap@pv>T?kfq)pBK10JCrOAL;p7(H?zG6bU)>vR`ls{y08=f zt9>Q#=69rVY8VVv^e%$cfYwDCkl;(f_~|>PVsaKEAP#Hc7m-zokgyV6k6np`4kOP((rAAt*ET)Mr&41O*UDhtSHU zg(sS1o~QWqOoA`}2X${58&}h&>$V*;W9*n2W2TrPcFY_z#}qR&Gcz+YGrP@>nVFfH z`E=fyZ);}5Xrz5~q*Lnt(`v1%Rb5@Hs-Ek)Z`KmJACcp&GeO#y(Sl!qk0&*#gUT6g z%63Do&~t&Zr_sP#9iU;<7%7mAG6Q(0p~VEyEq&}iav>Epmz=qwt5lJ$%bA&bO^eof z_1Fxi?4G#Snz|`wqI--A=%#?d;=mHpG~Uubvc*LOUrM9IYGg>}(Ld=BvP&z*Mu8v# zhmoV9j!7n4y41|35+*eCXFTx~h5^6;!U3PG)rfmL7^{n(RneG*3FF|8(W3x|bp5x| zO4SSi1ws;tj;^&xb2!t&9{E@#MrbZi z++>e_Z%z!=isF=U9S210Pu^NP_tN+!v(3#22>wM;l!AtxQd0)8*9#?IFphREz6+I- zd|ZYTKCo%H>z?@3`D$@=w&cM6`!1{M-+FgWY(T@< z1m%284$$(oT|+hMjjJiO&d{{)WQ_MNR`6z)q1g+ z;(IzaC%@k4^$oSKK$?};M@JT|FA3*F6^b_VA8jpu+xw|O57UP8nPPG0?xzizfiH05 zY*Z%O_c2+0%8hpS2qmA7caojB8=8CAB@T~FS+d1Cs%@Si-yXtE^afP1O?_QkuU@vu z?a>LH`Fzo`SI z8WzgT9jN9_aKQK=n|M#Gj5O{bZ5#THcxZJ|pfR6dfliuyPDa%qB}YCd5LrzY5-+7i zqT19%s16$+W6UA$1ZMC~{p98i=aL6~R1XL1h zRAk`y^ea^Oe`~I<$H*(azqc>9-@dQH;rYhyvfM|k$bpz#!+f+l&u=`ogp&4zq#dMk z%xq-+dw)Ia{&$7GdFsRSsHBU-cKhMAEkLo_V(x|IVOmm@KK`ezqfuqQPMacEt=UQ9 zIm$%29Hwm4smj4t9KTn3F_E7KkGL3fm^6+8dOorxB~_~joAG(XkmSS0=;S*7kgOOf z5gr~sjApuyoR1BKRRLv%RsFQ+)dYrrk|ZX0`5p{2Q!3t1%cqmAPcvq`I%`!+6VP!# zA^92WILN-wL{_BlE;wFD8eoyvx~lO!C4@yuDdoA>c-?$a8lTQ>PK(;z zfGG9f{hukvs%~Z|&yoD577|RNO10&EPB`Zut#EzH}s)pnBT1K&$}5sTvs7`>Z^ zWp_O=ofOp7qsmh=G-E+t8~s^z#zIfmkJooKR*%EF3)ag+ii&uS_f_0mziGA}t{BqT zMRm-W8+);EvuGY!1@m_Mfd@K_KyD{ZkDcZILM;vH4s-2k6@tl)xp`mEoERPeOU0t! zL>FcF!PQQC>^@$hm`535h0eL3KFTGniX;p=M)aXx9xQdRuHw9_1|Ggl>-qUEHEYQP zQo-qWVawOcE~)OE25NU4segNI;<Q%g2PLj!J|kE8p?6zu|-I z`SRmJsWfF~jb0^B04unrf~IdwN8b1TjOXWh*J*`3%$Iun_ufC+7AJvEKOO2(QT_Y0 z!DTB_rn>%DUUBd8zsD)87Lq`}Z6eu@+c@Co`mJB^rD2l8Ixe9hZzl5*9c zRRB?AvZ{V>vF|#T!P@J5+_kb*8&(O{mU3TAQZSCT&wah$T!rY*EAyFIc*7pjxb{YR z(KSRSkcv@{18vdK(ZZI0L=s8T^NAUl2`Hs{vKXqfjB^IomRU7g1bg_o40*RhkEKmh z=}VV|h$>Db6K0OZg zuKY|f*rSP|&F9MJcDt~pM{iAE@hg?_6GkCbD1b~T`zK92HUL2E52lw;mPlPXSO-fZ z6NEwpj?(YhZF1^RkTsCK^P-eAd7UubK%$h-;`IINLm#O$1FH&C-?bE7QI!nsvTrHv zXsIvSM!!61gqWI8-d#>ND(pQ*b(-7a$SH!ic$ z?^_T^2zj#F|K#2t?z?MhSP%;)LODbzP!c49_&MMc@=0ZV&OC!g%DqI}jvC{$TN?;} z1<2joU-aDvVX+@S-A(3vhh_*NxZ0`7YJJe~kTngA4ixP0%=`(CXzGsf1J6}Xu!?_9fvT!0w#Js9fnztZK}=lv1ng!8vr7`k zZ2UV5bp-UEV!1eKJx%O0#J53ta=kYzKgMi}uYRIlYL-D{YB%DWja5fw6FZu7lN}>v z32onf;fMnRn6KEM?9z0&U&or(grXCwOlOj_e^W?zx3Bxb z)~~^`TbMTa38Z6-sHa#E%Ikf%kE5@pMLby*P*cN*n;#K@U7Ra4<}|8o(Ss)kz#swW zhyIGXft)l7pq3j@^_)YlmDV>8*qt)}Q@tK~h;2TZzdwrahg|c^ zcDo=&@;Sb*)P4eE%uZ=)H}w=tZKe!Dz$qI|F+Mhzu^^h3Iy5(v+-w)nbL3=Mq)?Gs zZ(-W>5^kD6S-DVKXp`?X-6MQ5HLi_R z{YLp@2xh8X7YN;YSt?}oe}xmq>+6J=w0C6AdZ+w2xnFAXaF>ikc)dQ*k}~O0Hd(iU zj8}jqLbD+$Jgm6oVq{D`4$Uk-M5~uitoYu`M^?u{(wnAI6)JD%^+5wTr5^MTO5z%0 z{PPRz_OM=QLwS0^#wd_64JRzr&*X2!I?Sxvmsi!QdQ^=<=}+iSZ|{dMpP>$ZS~P7Q zR^}jy204A{q+#Z=55G}T}XCG(j&y>eHk8MyWvC9B<UF7fCT}OB4KGzFi*p2!^(;IISv!1N4lmJg{@W`^nI0S})ffSUKQt z{rAB^J1RD>KcR3VT~l*&G9RG8jA=(?&VLOKW(NPa!9h8`e+LJ_H~$kjh?E_HJa`%* z=z4aVKw@*#=>fjPK6E+3S2lDslpe%%Fn+wf+7I!bmggJ^f==T>&?(sk4ni;7y>3MJ zwD&mk(OJ0|=$rzmH}t@b%I^BmNqF-8wKHiha5O!yRi-0kt@-v*5@ZM8X3Rj~p|@W+ zgHrP(QK^xpw*$0pXL)H_Gm8E~Qa)1sc^uNP@qCMp@1xM`WElDVkaI8o%ZUs+vmC5^ znN0Y`<&MwqqB*yBX}U6u`S&!YPwsI|Qj5>wSVLq&KZJGg(w%NE`(Gv$YB9%&$J~&g zO4at_GxU?Ro0Pv{L{;v?+ni9Z%!k{1VLp->xQDCrX{%r2!GDqlTobxHI!BEjvTzU< zVai2%BDQ(46i4i2JSlRJ>#?L2qv%2g_(UfqguuWKp?woJCLO@0O}G{0G>4(LOGF;4 zp`vHmTfotx3t-xI4McPiVDbi|tZUI-BoZlkSS|g-Ie;NjvRX$oE%PBMk5M2;lMo+k z*!x`2ef*Jvq+n;rrbkhJb}|?5)05nVp-e+vpb$bWQIfATAiH@vaVc zHC%`Q#xafHpQ$FtH5gfh=C3zLd92g`?+&LsISyP1gWx25pbriM%Zd~uvB z+`y%YY`;2h`F`?Ph}?Jjh1%j14XHrFCy)oXfoe<~c9z7;HS@!$22=WCAJ*M?#L~qkEz;MOKDQSx8I}hV^vpeM1JX|2x-Tr;W^p%#-Hom@1N*OK)oD$ z7=3s=*|_SZw6lK9z0I6>{WczO`;b5kq+4^k5(FK0rf5=(NxI>+c^&;p%ygBN<0vk) zxjZq|mPYCBjcTV6ISP?rWo*^Ni{QU#{LGp_?dCLyRoMnyl3qod8Y0COb~^HGy__`n7&g1-TM? zWnQ#gBWf+lO)|dU>wzXpFx6W+tY6l^3JKAcLs@Kh&GGeEzbUph%+OX%9u(GA#0{J^ z;OmMdkbjckMgJ2QAbkWYJZq{JtJ&nu>^4(m>JcrOrx%8#>6NJTlZNgONGWvK2NQkS zWh%>7K8!rX_UKB_5-(&m5I^a7T>gK@`(-lz|B?6GU=9e1<{9*bK5ey{P@=JO@=}ZB z-KO}<(M!)0SvGMgIh82kuw>RH*F^HFg71KK@zwvVpMy-|{_-ANlV`sfntXyb1V6ij zfg^OIjm*B3Cq)s8qmc=Q3X}K}E!ug^$+y(|VNDH^fN@xX7AF!Vav%n$4X5Q@whIH_ zRZ?%fQiUB`jl&>x6C5@CLFtTE88g45_Owwi^y*MSGwZ8@)z}hnE5S3lER)FBcOWTAF{E zxpheA&IloP8G3qpX24h4Q92COYHuf)5Je?QSn%j?ORD#s3M-`vaFDuaVgfLV@u^$W zF+&*m-6MKH*5B9t&j|6LkbeCV10}0EC}=JFxLKR?FBZVkh;30-FPzHZOCv$TYxbBY zC{b;6EXO?ftrjO`(39-}3fBOLN4Ho$>$=1siYI+Pf)|k=E5XprnS}`HKf_y@PxZX# zlljN^AL2=u?q|sc6-I%@hOK6HCy4!eyzr%baX9TbQ?df3gt3r*;;wR|^nS_qA zx(BMR#E2jJ(k-EuTg;X2V4^6}*s~Dg5xGq&av-f7s74g@c zY9+m)LFCBTiGUG}Qlj+>@R!gmQ0$)atG-13gGE&{o8z!r2~d=W@u*BeGsFGGq#!qI z*6k~n3L~-7vNMLooADl=EeLEum=V;!gMK{$H6&-+qrK)gW>dy8tOyiKJ=oUe(I;y?E5 zbOOQj$7}(&v;ZlSvSOKUV~GZ&ok!UQ4v!6ig{IW7>~I+tez`w=CQA1iK}wy3jLKR4 zAdhoi_1iFL8eTfYe^GWI^bIJqcuX$<@S{=ELItsh)Fi0CS|~wuT7e)wDwsEnKU;6s z$dD)JYy4B1WojX?Yb&u-nN=u_ZK>)<|Bt1rQXBnKl{BKV#VRC8Wg}pCp9MY;HjnQ=n+`B+TQFbmR|VGjK2qnwEGw22P{rTM}&lV{DAZ*tbCSrXlAGr zoZBYSObA2wUeeR-jElB^tbTS%z5C_R%#2Lr(LJ~au~IFw!Ww6E$Os02(f4Nr`Cb2j z(F>=RaEL}13eM(iQW_6K`xm~5a{2xb*{2GT;HH$(FENr;wv|@E2pNbpY)CU}y^1q> zVqi~O)`~Q#7i|h%avrFA)7|I`VDv%xAx6u-675_MkBtiP3thv`11QDBE~b9=GFP1D znbO6mo#2SApHTEAhD>@_kmx_$(gVsO2vfxoPzR!$chby>l(i&r*s&mW$^o7q`n8BkN!jAgMPmM168_&9 zN>D#L%jS5@7X!dPCbEyPgh1#rI4W5kQ-WNU^vJ2IlA(F-hr%05zL;<{ncZYL4g_cl zEP~3%f)L`eFv)tCWQ=J~wI#ia+8X4nxH&@-CL+DYwh)3$2>+U7*cL$siz*%}788t- zfcz_CqYuOalA(o(fG!p+Og8iyEXE`*QEo^%ADF+=$m7URcRTAu5IBs=<`q6P%=6;G zrnk)w$f4XIb{UHgbQV zOvX$l*p-#W-JTZN_o|fc?II0}U zI6>CBn&_ByFJa|pqjw$J1doYY|KkK zZA-u8s>+v2O>+I1ZM|4=j&J~s_Zlk08VvTBluuWhS4{$1K_#v0LMfLI9U z?CvB{S}O`l7>T-;!|70#o{?Op1`(4)A`s%y5YwVcY|Ttw5`O}pQ^C6#e=EV0y)_R( zE4|C?+hE=leoW>T9Cy@czLx`k1Ui|8sL> z+O=eyHj|bwMu?a%5p0YqNlin8oxQECP08`aMG;?>xT|||noFRDmt}i+EN`-g&IF~# zr_Aq>`$Gh8Q^KP_+ocj%RJId#%ZImaggj$3(wK^liiqU$X7d1~J7}3hoW3B#`0vuR zL%M{|OZmXa_m{7{Rr%%hjI>ANDQ7oOKPDCLU#8;N1+^%wzF{V%D{4~E(hbYFV7nQ- zjcRY9z5j(l0-_&TaTy)%a%dm2iuMxXKOc|{W@tX>Q}Wl9Z+T#U{fqV=SHao&ofcDQccX^N{28!;$&l1*w@ zs9E}l$}2{>bU&i#sJC7W#kB<(4pi{WDT(`~9MF%$c(^c|j@`DYvCp}M6Ey4!XGm0N zjCGLehc2?E+)dP!5Q{@Eyf@M;RE%+us$`bxZPe~;JKyDq`@5}}$a5ERIK&^NMgM#kTIFJI|N6f{2b{Fs4|n%8X&X;-WRK7)i-Q8;jbHVRtsv$Kt+eddDXAQm=1 znGEHiOPxJ|Wxvp1Ki#cwUoH2OosT$odFX-&29ZB%&)22hKRzfYwiNYW3R+RpzNX@% z!zn(4yEuqZH-X=S_$+lorFq(X#jr%mq(lH1QFb&9$7kF}DI#!2DB%(+)_`w4zEBJM zvy$ASYL;WS5yyhQNUlHdRM_H*!?y9>`_5ksonA>ozaRmK0D1`Vu?mUue|*R;&XQ|R ze82$$G0u5^s!a~T64DaB3yOhUxTgZRP}l;)kI7p29M%x+kOFB(q&?HqUROB_W)IWz z+BvG^3!X5W{gxW%4g=yabgCj{m@5rA=jt_~rY~;h1{)Ep=p=xc9@Z8=Q&)$ITzAH` z+LSfe!{{$JF2r}abqDw8c=0ncm)ox7Y-5T`Rpcu6@t-|$+MJOlaK0vW!@(TBoz(Lc zV9DaG)KZdvKSr5uy~yUzikFN(BPnuy{ai%xG@7uOLdaONFgn2Vz`=w z@~W5TKIY=<1HF8X^4q;`bD8v4*C)Dfv`v|A+n4dJ)3`5<^r~sm^bIAq6iP!nP+;JE z_Lm00(D*Xwd%{E$t%Nmo7SGLsUHt=vBP(CTLA$E;Og2~H6aF8WF(9qOi+7g61?t5a zpvOmt8lc);V|Bevqv-YdX}{C$uh!wy{feiFq?P@zX%&^$i!}YbHS2Q=u^g0xK106rmR#e81z^B@yoH`Y8M{w7 z-HaL;ybZVG8mX7H{DGhZlFho-=+L*?a&@;iCJ;fA8Me3FerJbo^GcH&~sj61`7%*Tn~37}Z^lk9KbE`MEKGotEzD zb-6H{uXmWag1cCuBV4BMl`mvIMg1&R0*ZiGBgHUn0(cqgS=Rb3BYkTd*jw6K$hDi^2u4A6xSdbZ(o)h}PG(ZH^=L|6gOB<#pX-#E{1C39Bt@Y{;hLhYXN{|v^ zQewM!1b@@sLF9UA46~YFaeu6Ei}EFKrFuufWjpqBDaUr<55-{+8wY@lLQ_ciGrk_l zvPqb@gbT2>dYfsaov00yyw>T*SDU0n8Txj?B*tANG2U9dYaq%YUvP6gXlNLv;%KeR zyS=Knw^Lg?SjMfiUqJCDwbmVmv?ex8od|j)Yj1F0gdLYQ_#Ao4`7L}^(Z;@@3aeN) zKz!AW5lO+bWJ9oLixR0Cf-Dlvx`2Kxu*!N3V>&hy{yVw3t8%OfD0z8Nrl-V@woiQy zSSZmelWNAT@>Eg3C!!bRY<65@w7sAs2LNQa?+<ro-N0x@HHo(C75BBo5({R97~^fl3Tm8yNCyTW5eew_BnmN?cH}_gV8t`Mnc0i z-UyBo9nG9mh{y8SHcdIMv|$?otEk-HfcT=?)LE1#+5zK*rhRCiZQ)04=5-6a^r)&Z zIde0!h2+aKX@`wtyR^#n_4WZ55TK?K28V^Pt(nX)a8lp zc*~JLwSrmQl<%7EIe;RkD*uCHGYa;Fjyykhwt8QdR2Vza&aw(#dW!Hm2SPj2t0&Pk zJRn$2u9kqL0|j_nUwh){ zzPmMIyTu}=q9_qITx9urk$U0t+{nj(4ux($(!D0jngxsICaAq*pSU(PL z)hRuX-wqHV0RVb&m;ACSFt(6M8#mdnm2baau8$bk$SF%BIf8M^zjhDHxq*j}$Y0{G!hi(0Hs*6JW^762ZgLineMnEVFLxUEi#*pF zcGFK9z&XrMfWlCn^h^%hzDCx!7v`~&d4T<2Ukd_PEqZ+ZwgZo{aVs_wjVq z!9h;DCam7r?Zt}7rdJD9hfql3qt_60pE~_q(_H z95*}xh2z;$n591@CXK)}we)|6%~bxQ3ZLYEg3bQ#RQR?8etkDx<}h99d*53Q*%&kA ze=OTiA`3?0{&F5VsD$*DUfOla$m_$JQ+tCQ4E|f4ZXoc$oiBa>)kU9KLdI6 z6tU|p?1+E1mkFKktb-;7-1|&J_COoRK9+>|o~V_w(XjR-h8{n*Xv4T!ZYm*VW7N#f zW`DV05ErRs`q$Ai@i~X*+1rZJJ)I9VTL{ihqHJYSTY>1x*2m;U0K2HxnyvAN73~i^ z^CsW5PQ!A-Sw`Lwkf0yeFpjcCF4Ro*Vy-hnlZ5J<)|l0lnR&N3)IzCck^8;WlK$0^ zOYbrSx4eri!;i7F$B}#J5B-bQ-aPiq`*QmOr1OqV38q&@-rLVa#1KO887M@zXhM_% zMD>&0{@RHCNKnv>a~SACEOdo9xVWp;HvBK4_b)qS8I=-)OJtWv9M@GiRq9Dks zj=Pm1bJ(g`B=|8Z?{u+BzpjP5P^5in-9-@Z1}e0^CdOB&a?-VVcn_|+u?CeQ-qn`z zx~fESz)CZk(R8jKBUd4e(DS7t3D;LBpv#I{kn4}mMljdMW7zpznp4$>+TLpzFf(L= z)s7Q4q1o**$wsCzQkz&w$OGT@BL8y&t{t;3h?JA+sCSqn|3s;J-p2a{cXQGxyfB8s znOyap@iUmB04jzLDkO#?&u5UujD!~*L)?BDJ%^-8ZfA1gxPAMqfNt^{HhlAx*mrq( z%dqtP-|Tlw)*hOQS9)|{$%&FGqkE}B6>Qc@My-JkRr&BlW(!(E>TdR58aZbC)lu(= z>6Vf`U~{n2jQA=Izu&>>HXis>I7cvvTrG$JI5B2`FV7R zIh%mQ`{t#idE4W%PZ! zBvq`0oQvGrHh(*RA%9rBaO|{1BgO}Y5XC85g-zuP8)PlJk~Lghp5uR-FPBhG1G=Ae z!Fzr`>b9+7R}uQ+93Q>K4yzZZt%xquv(_6yvNzF3 zr>-Yconr4tVuH4K){sUxTf~+k1UEeq!)l z0;WutgHZtVKcy0%uRzEt>gKP*OW8%t3)C7hOc0O&wMpBWd9>c#!q0gx&@grp)Q}0< zsjK#5jDL$k_T=2$$B(ZxOi-n#XoDaLXo%<{N6z_DT>i7h@j?B}E!&d#_kIpJ7*DWEF z}VndI+7G60ygr1=>>NoM04tadd*Vj`UPg`bVu=ZnzVO-4Wd>PXV&F&vSVeUeS>kZ7BKE)@4 zwat(-ON-+LdT!H8WFY^{@6i?MS_Nxzg{RAw&tXcP?)M7hu<-D)0=22E31#@s#9eZR zTXTuxRPa$~?5l-D2GeG)J=8H0V7m*(o=X?OuFwf#2U*MgIt1K>vXkAI3?FkBE~q5$ zv{dl-l2EscXdR_@D-mmL?~ZNBcD}=6Bl%(~$SM${97MKv)}%@)M{2%0dnMZVgd1O7 zWeQAl&Z#Jwh-<3%2J{TB_JTsfA$C_UUrJ_hZ6wHttAHyF2MmC^$z z4rdfO3cMUa+x>uw_${J|3w(+^M}RJ&)jwKkJQes0!Npv?&9BZ_X3&Va7o;#zCwvxl z33oyVWZy1zUsXggRBk-A3Hm`HsuJ*;_Y8$(xK^|=uzVNa$p#r*t06d)y)lG;yRS}n zoPD=Wga80U<~#I9f(Ta{t@`Kc@(yHvH(E)RN9R*SRch?KABqfACbN0&Z%nYjNl;I` zyZ!RA+5O0e70c(U1B=TW@`kZooh*7BeUy%}*Ov2Cu$+KClQE^MtAD#cyA1sk z-d@0)1_HFK_QM!+gX*-5y=9TJih|Dg%R7@UEBOkImaI2kr!Wc5NH26KqMD{Ait>z%Gr(}M{@ng#1lN$9p|d2nQID_8lKa+x_p%*@AItI>&@7M>TP!vvavYb?tqAn z3-|s@mjHAyW_3=|%L2ds18wB-g8neyvuV!{Lv1=Ri30Fwv2RCG`BvUlK4Uf!;e|gY zXXeiQ8izNA?fD<6TRLFz*E>A5{BfWmzI|d;iiDN58uuK%ZVuR}Uo-hle3<5pgk@_2 zw0~L54w4krdUZc~JPV_DXHQ+a4sA*s?kftB5SLR0owKVdQ@|p@lO>2x+&MMAI(1o5 zjdU#kf;HTf2M^K!NJ+ao3C|aqTfP@&w}t|rn1yKxxG$>a*PMTbXq2%SGf^SceOEa9f%*^mhH@W^sfYf3vMce zXzi{(vd~HcY4!>A>SxEPe=5KC?mLvnQ!^;~Gtp}xSA{MsslB?nZ&Z>b*j{v6E}_GB z>6>mvL%8$Vzedl$uVN8?<27}?uxdGEGMMoQpn2Nt%iOqsXQP1bN}pn#^(TTgJO>R7 zHoITgiO&pogxPKFN*?#E zZ}(jx2xWGZVzlo!uKfOdVjW3d_Yu-ztxbqUPgzZy6zvq{kOt+ln#$q4f58GZ(RRjSe^WY6 zAk-ETmy7g9S2b%fiG}mMt&woHU)JX2a7g6?v$fY4G)~m3A$F)fMWHdS?h9H?kMM7e zg2su0c;9B$at+efQjLb6S7L&H_^YW~QB zM=Ce3=B$fp_!m?q0{mkCKMh5LmMPX`tv#)uyowT)5zOA+iaR}rxChSM>3`Qs`6GmN z@(?}rU5y}T7PTvJ1+t#AS38jw#dqYXtWuSUMgSDy1L~hPO+~39a@`=iQu2)yioxPl;da^>@2@Vgd@MK7a2{M zvX8yUTQ0^bu8C&!+}K62{EG#Utob?_k8t*?Pmx+WgusqF8Z^6X1HL#4u@oNAU@8HJ zA!L4vh0yP5@*`@|=XdT6G~_H7H66|xy`Pz_gyqVfl_6zqr>gP2%NgG zO?>`+Z@RLOK}M_nSRb-XdV$z$u>U71%)?gh^rB3s^>~vbVWHTT3ygP;x1qtyCkBgqeX-beV&;&a4_cU<1RPFja;*S>zh$iwPT4ZP)& zml#X@*Ix76Bqvsrtm5n5Weeb(;*XA-qju3E_ZiCV`}#!ogme_5qORaz7*1s)N;Aib z;D_hAIzq%B!)EVE-l3_a6==*820!8v$c_qhP39=jN;B z>KBgoWmhxgcCADD|Mo9q&R$DC!n|P}RH_;;-|(O%93Id6>Ng}_fm0@8yq3!(Y_XAZp__0~(+5s3O^;Y@xl*bjx3|sH{VeqqRP|z2r_0dqNrZ(4SP3+{wX5}%HbE)~ zR>y|beXUcLsez1&@PD=W(C)bS?+&vi3Y9MLa7g1e(zF!YaD9Kw7@)VKL2!jrPwWPD zPjGRyeQN{RF1xupzc{}Bze-rf>_CGkMCoSt3+ZBW49 z5(r}BAR(3E{>{pHJ|L;6=&y7gh_<7}va~!WI48(l2Kh2y-(Owb-5?iZU}Iz8;bBOT zQc+P=D-?i)_H+mdtr!?&wVW9*Z*Om}@9#n1gNK2ELHMV@{ZkMSewH1coa9wxB{lu! z`#?4PRma1H23bA-^?F|C1HpWJpt8QMota5MaE@>|v(Co{;_5)j)|~HZ~3p4t7otpjts~`=`cV82R0|f5id6mfI%KD3t|*stF2GIUwTUY5zy6 zPf6daG(e?HxkJ6-(p0HTsYJ8IR!mGxPR?4OK;Xk$q7%%g(?<|Y$&~%~^lMb*#SR@$ zwPo#T$?ICpJ3%3R>|;G%+xhNUl2zC)oQvN4NE?#|RA3yQ6|1!qzt*aphm&M#g?q^) zP~gsSMJ4r}XyMr}iJ;|*r>t5yG3)6#%S==`%51Nq6Ovpyk&?!s2&&DF)t1m*qVNMC zm|5ZVzVl<68>D>4_%hdF(E*CICpV>?R}_h8H$O0!WeexTV|93d&aT#q2hO<+o200y z1)v*9-YW*n`5fO&Tq~t2sfH$`+TAP{lA#<4!^ka2p(;6T_XKT~1w({NqW21TTRiRW z4ih^Sc>Qpi{8)=`de9#>F#hb-ruUt+Q8r#8;d#_tZnbme%fSjbH)OU_o{Kozlze@d zJL|RM3Y>r2i^8}WMFf$>2$H66E8>ZnK>&d=YczoThaP>5>F zj#AT8i+R}_jdis~7_TRiS6LGOMk21bpXqlBZSq2bpAX0X!gVdxxP5knnsdsujyp)) zzF-w(MbQCL_%Wtj|5jfu8v&Opk75q(x#+l@k*BbCdT~WiHtK?4*rIYH5#6YsRLUPm z6M{dIQWhT}ss=t2r|pA29*VkA^?LJu;;ttCo)%<0k7%~3Y4jX|as}(1<*(*}p5lxUu}K87;< zCe$Z35bU{r(p`)LwdmnRs2)IY^s zm*Bokl(%L)>+8nc(Wf85BN@`>78PblshuRkV0E$TKwOZ78b%=s`?x2k%d|I2Pr69w zF+a5gZ}9P2!3V9`=HJF|llt3bJm*kZJVYvz++h+6^@$?*oG#?7R4%Lm01^|3$mUJr zUz6#!b^HzkP6U|F3hRf#sqjIZLK>}2;VCUV~Yyq;UgQRl~< zkYUaC9-+!8)dz$sk*z44(=?&*PcV7V|8Ozu#Q3hQmE%h)hz7!*?58VIy+NvI(`5W+ z(>^|WK#Vvmv+y@l{9KmwIa`5V&lI|(qja6r24?2wTdb4%t^}!rpXlaThh^BD%Y~Em zLbRoH@WjH5xC$qyYyIi@P9ggk%-d9dlo`SAZTwIKAEGBG+}WuJq^fns++Y9z&_1oP z2ZN}q9_PY#y;tRLZwvXupppo?lT?3o_~{=k3xi3+6$|0xyu|RkPNxf$x$u)_Oh}Qw zgH==b!DErZkPDbfnkMk|#c+reTOTNvq;EHls}Ix+EFJT#(Kf&_8O&wFxvtf*cpjt_MV)I4YOq27jQbDW(BDHeVteAKWDbdY|2}-IY z@A~+Im~5Xn>|fV@U{ifR{yR4Q=}gdm4l?E~6h+XF&X9~;++sSI3KLSSgRm01-sHMe&Oe%r`z$Fh@PvPJXyrzv@irkm{pPUmK7^9>E@P zL|JTCvQSg-YRD|w?5Qd1*$BG!JRy=EbL0N@zkT{8XdalmJ1i^sY-c*i<){4N!p|m4 zF(MferOlUZBvRK(B@7*O?C}}DBO#uYHlf4Kf!oyNBNcNr#%gcFcK7s`ih`3%>2jZAX{`|g25lt4*4ts4>joS(rHJEW~-%U{3 z^Gh!}1c~@FKP&W)U}1!}Jp^dE_?%JWUG8gl*d3|iSl{ye$7G8cuE1qm29i}ABpr`y z8C%Dkaw_g4$JuL;4q*K`{YmzHTvJ~kr7>2*uq50@;VU{;gNyRQurk2=^@kQ!4#Iaw z)9N-gOUe0^2Qir;!tpZw&7)k%Dj!PQy(laX3QT%U0v39bc4a?heuA-VDXkzfc1QMk zh1yq=(|d>Kh!YetgXJfJwXdZ3r$WHY+m{evR?)%dq(qQ=B4`2f@alrJ88SyNm;dHv}hh@^zPcE`FbY%<@xIVE;we9zm@lA_q>s4zcu=efPg!=*tsmXwu)B&`zrSUBWl=y3 zmrj#DA_6@xx;O-DX~UqaI!gd@hO6U-QR;IU4qMlwlPbm}3lq&rhFW)NF{FuEm1;FE zbuLbOx8#jkWA8V+_0dV99IvhHpzZWS??T_tICvz=*sn8eYAoO}~?P4!q>&h9C@z|d$+^YYU(q(xq=v_i* z;R_m(Gwk7riY4}K47#i?U6el74o>9pu+UiN9Xb+T!U>Mlaolu65h*h-D-mSg_3w)n zai(gM(pps%0@A|dXt88Rv0TAKhra{>>Y$B)nnD=U07L*NV?P{KNJ>z5|CehGBVYuf z#UR3{9)M$7$T@BaEe#eh>n3Sa%*OtcGRZJQU7AR!BBbyW=omkbcmN}pN|<<@VAh<0 z5di4k7f}@OIVS)G#BiR+en$VY*M+c?vBCPx(x!1jEg{)wZ%Co8kzWJwfdL>0putf2 zach2&UMRGsugJ7}3<#~aK`i}nxahozbo|ZSE?-0!f&uWBT)|9=jH*<>o(Son-Lb6gifJX1iZ0$9wyMip|JAIAA{k-ISqDsd`kdr>Q(S+Uhifqs6Bv&W(e9z5s#ui`fgZ@P397o}M ztS9&LVc;KD5y*{F#ASy=Im12kJID_Y$y7&mVzjRdIS6tkD-1JjNt%2fwu3(L2q_+7 zH<@o(B28h3bDM3XDME&S&5w7n3fU?t*GtW4Q@q? zJH?8-I~3RAF2%igaVeDI?(XjH4#g?%?iO4Egp)q+`yH7%-;tR!XJ^l3@=vmpSu0t& z*L7dN1MRe|C)wm|My*iH^)GGcE!&~bJz?mqxMa}73I?v;OXq1$x>Sf7c2{r8MzTgF zNI7xrtlak8RX>0Gqe8FLupfZp21X9}r4L$`VlF(WpZ7!5n@a5Xl3d=V&f^BsSqLE# zB0zu-Xo66W<6Sgo(HG4Es<+(W8Uc8b36f8_5JKMvKpcE$O)q274##(en@jv8>9$pC z--l{Sl&lLQ!Fd`-S&pm#yU5PEmqqYO&;47$T(qlL`R!>yQ2PhFz1MH{M>Vv=qfmfm zgE*1jBtp6JGs!q%;@Q68FDPs9)l7fJH2xxRU&;$U`b_2qPWp!l6kyCN8eXNZ-q2_- z&u26{1s#ocrSPL8Zj&C7DGFc7ldlWvf887+aXHFif0#5(cfHrf0t6)0k4x1Cznyw< zbs;5qjWP}A_J3g8|HT(r^#c}Q^B|82F*jyKH1eaJA1#-b;zGfp@yrb>E{s6c?#xu@ z30sHyrEyOf_vGG(iakWoNl_H=Z8F|3m~QKyrwA8}`?Wc{e%r)1?S<7$F(__D`Gu*P zqLhy~42ejlHjd*2Ft_@d&IO{<~wC3)osEO;6vh ze?qR5&-NF|yX{8sg2<&KD=-lG_snL2fEU%5p{VUgENb#wFp<@#I9m3MB8l==vcgpQ*sdj!aepg{XT1 zo)1Hc7z%iXbDkW29F0nIBV~}KhWJlj+!_gWz9aWG%Q3@eX1D@3g}?}Zq}zv#+V~?q z6%&EXVbElU<`B$~>=|HsEoMA1PyP7F(q7{R`yBH-E$CebQl|C&iIf^wZeq7OQSxtS z;ufcv83>GBL42^CFJ9qt;MN z3mrQTTxejwK8$&`orlB$C$coZvV-&^6QjkSFflSec3j!&j#pz}+~#`hO?)~7`1i;_=gR90tQ zG4fZew(DJ0eQ!=^F;bA3P8q$~66A03)OS%1dU4WuJ*Q~`vBX3xk1{dSdA+>q?(b_F zPT)l6O!K=R_&Fc8k--!hN|?%>6h~4=5S-9B+j2T@(r2L@zyz=;_0q< z6esR4_0{doBca+E0!f7fBz&O&;|F6g!||O;491LzFkdlx})nw$<1rcW-FU^*p=(COZA|?t%A$)p6>Z8V-bsgy~v%p!fg! z>{i=OZ{gw`bw$VME(>9d@)_k9+J}TkgG#mgG`FpSyaF2;EmB)d2YZ{2u-K91&7TRT zYu*-%p#ia6mLo?w#DBf+@<#aJbRH=YLS9<*zEjZEur7v5Kd& z3axiSiX7yqRwAar1A8FIN?A)8YS%8 zl_+G}o&kg+QA2SI`cSDcGTztV{sn*}>jmFfCg3&@93U^^zLB=rp7CJ7XvV3?Lm?_I z>A_=5&%yMu5KYNl-NdkN8_8TJfcZuqQo2B9lbt56moYAoG{uB=J7h|@V~nM&Cr)?^ z$h)fUgBv_mQ|O|wpOI#>c<^1!uL8y6JDtXs6`L~;?dwld8Btb_UN4RYTJU7T+xsq9 zA{DQGgs4;2nHpKMrX$m$8d@izjYW08x+`gCE#`>Jyy^KP26wq>B#+VY!$)qxID>`jJx+?%)3US<+M^h-OYjKd3eXR#;Ox)N?{6xF zTn9jgzjPnY+HS|Hqp3@H7zsUC^ay9pJL)2)IZ-dx&F%b(P62V1aO`HQ(9wg$= zP;>A4QiDU2>M(&h@ZEN~-Kkb0kLIkeF}{<3ne$|MD#WYKb>PG!2=#HRfvRQ>nE<5( zSL8#P-gnS;ISC7gT-Mngp}@Dkx-s>)VtK63fmlzy0_p9wQO9cq++NK$a>yMa;F5y} zDPuKY%JFg9f?;Lfp$+gXnG)%gv1yAjSgX(NRiO& zN=rUkxh$^Eabr+1ZJD|-`DkzC4jWd7D}znIX?7;tUpXqXXVUY`muMA60C_BQ*x7$Cw6{R@{ik(-wSU2~LNj;4NU+1K1 z_}ql#<)Z^apI?GT$Z{DS48_n)k&`!h#XHk~iK=N?@pu_8be0-Dc74lX4YDYtDgMs{ z`+0 zSv<#gU3p|cD!m-Pfd5mifi--0A*%bF-Eo@kkB{NwJ@-TpoKMerwji=5$eh|642Ur8 zu$R|tlXvxzc(Nk24Kjk>IlYvVv1RuAq-V|~M(+Q3H+vb9V0qbT1R(ohLx}R5Dz^Oz zEd*25M1edK6BPSwb}kADYvAic{3w&)5eWseXhzdx$?%~Hj=#0SB88BecbDnb0BxwG zpjY9r0qq5@T~_F!Vro+gRtg&m5oXqO-5jt6Uq!^PQg(e!fRD&x#1b^-laxP6h2yBo z@p+b~HziozeDgZ(BO2GL;GSwf9s)PG+jlxnei~(LT)uQf58h+-+bOd3TiyU6-8Aaf zL(Ic%Q-6w!raxY4eUt5+2`$H#TGwm~3u&kCwag;>^h`tDx6n3*_O&eH<UbO)SsW8u?Vh4yxq`|w%8&`W7h>p9K_jht_!Y%RAAPrZ~|D2nVd`ZES@p}iI# zI^l~Ia|^8lilskN-Fw|t=7$pqetRAz2KOCFxbWVv;r*KuB2J?WZQ`n+zmHV@T^u_#&s z)+Pd#V#C$;)A+!T=Zc;G8)f-KtuoyV<}GaH_DYD?h$MzN^%F9iKd!>FRkwQ`N~a|| zOd^2qr7wzSI+i$-JMOOR!-gMh!lHdGpNwmo_+%x^fTpQBzan zw=J(w000`hmz!>6*_ndAuf5i5peg;^oBk>4h9ift@mIXf8%;YA8&RvnzAyw=!!8}3 zOHfnGDd_3>FK`^{$#|}EZHbgxE1wxh%hk0oMhJA~FnWbHnc>w->9t$rgX^Z<@HJ<5 zDB%k=!TFhFzkI`TVVy(J`A!zB`N*cr(MD$H?p4)?jfJ@WL{6yw&A#kjNzdnl)WjPx zMx6&G2JhG7w)iV|H=5kaT_VAP(Ll91qiC|UuWv)`hEc`DzN3auVQpP(B!tb@nqt>W zV2QAaN=d)>YDgjwg5qf+bsIWp^COpQG?Gl>5QY&pvlb3Z)Y4I7)v2kS zV|8WQz@pkZvU!bPtS$xqk7y>VsTITjU;+Lom}bIBjhCCv!eOh2xLa)%&zr)8`U@mH zH%cH*?#ci9w)x_(zb`lF*0Duc4cS^eYPYb5h32onK(mg=ZOu5U0B45#LZ^@cTyJd| zQz(e0`@b;FTi9`FwE$QRi}h*^r-{+MmrpDfThRo3)^-t51zd>ZDL9)aJe%`X{Eds;27rPF(V8NU5l)nk+e|UWCGki)O49hJ4kr zmIR^lC-Ta?@o1WDyqW4hlJRus0stw0%6WQ$jsUZm*i`KOc3;K~q4Yihp=#jw z<4&QXTlWhNWLisU=-Gv2l`dJ?y@@DDiCQ$?qI!vYQ&B}od-hBqB}#roY7@WZrZ3G` z&)=xy;?9L;QX@B>CLkgroUPuamZc|%k!&J9nZIhjw-@?N-k(d! zz*^3J%h4mcF(TOnbihWPL4Q2+KEKfbQoVewCcln4@1-pVJQt<^5+$DQgaRQtEDy&ayLptujYi+^ zjnfX#3I87wpIZ{*eG4D)wxVCFol_a~Wg0p&iUR}lSwlVt(?N(lZb<8t3ci{Gc~TcK z25lsYwwg-v3yyJWhx0&BFRaZwrA!~ECg7t1x8DB2N7 z6>Ed8=&nI)=eov<@^{Dv+-!tSRRtU2?M}(eHY7_e=p9S=wBh5GhEj+WU&wpcFEZ0q zTBj^sBOUP7E#{{sctq4c%iXXW3VJ1Gj`+E#{_`n*73rOZ4;W}H6!yywK2^H0C27$x z+Yo5>d8)I2(bN|b@VX4Pco`n_`;$-gzeh>6L_Q(9*;n7ZgwtR^)9?Lx^0U3PZc)Oa z>Yi2joG2GjU@+VP5h{Ytx1SbA@RoqD8@Ujelre^sduM-8scUf~B`vSeNyX8ylkr#k z0vJz<*jrnvhFdt38fv>MYq8*Lu~oz?u8zSXD(Cs}HZn+ctl>R-*@*aA#2Ol_Y$|dP zk`_rGM8tDPC(PkDN163lo0GV9E7p;a7=CM4{|_52){;RcQIwM;XewHKjL1Wo$dW!0 zjV*)Yi!5U0OJ4~cekcczWb>tLO?)lGknQSv8Chmln@T!xRm4zuk|0>`gy5vF!#j^Q z9NYI7$A+6aq0+>hjfQbBRRm=L)(tvxzn^kzQ{NyZ4O{R!xXQ+;r)D^Nc9dCFILiMs zCDD8(O$bjTgRVwQsnl5YmcPYj1gp8j%}>2iNSp-|f3sVHfsji`R>bb$fudb9(Hm6` zT0A}Gf<2BC4<9uwvZH>OR-~P}h<-bVMBi30l+5`>6_Tx`r9eQvX-~0&eVNr(@`cSZ zzxJfyqgnA{7#l5V44sh3r`C&M&T;8mU#i%}Mb%tnlrhjf6Im*iiI)~_a^nehltzL= zmQC;Kc9_b)SmMbBx&cAf&P%OOk8i&m=eqgAX+(Iw z2N$V87#I@r#h@;TmC>LA^Jm481%f-)--1212ruBdhg$&MhAEsY~nI;vM_fdR|B%O1|NR*Iil1(@_Sn z?kbp!Y${%tbmxV)?J0o@Jx;)vek8c^l-aYsKUOxdLIxYc zRu!*Rqyx{1>zl!7K*Jtp@hBMROsG`)k>C;@OBOTF^?t|IOU_2|8^uoITm>n$-}#4A z1&tz1>+?QnYMbA$ZYAnOT8N#F-DDkfZJN&qe!)Gz_1)|=Yl{=C^L83C5r28=jk`F{ zu3EZZhhrg74v#_FY!A5JTGtSj&xidP6{lGk& zRn_urN8{^wbg+L6a8097^>Sm0iSHZK4+SRT_<-rqXtLSWoR?jg5@|)?8xx+;FT3^{ z6>74~<=eH*p5c8!Do6USbPOu7Z?+W%Uh55NTg}l$JS>Lqv1^7-;KMH}bd^Z&s@U9U zQiw#w`&a@STs_5RR_Zg94fr1zVKi?S*@NA?D67L7ADkCYsRyz{{*=@XCnlz;SWErn z4%=a#5lP%cfj3dvT=(CmdpNnjz50F#B?kYHG0P(3>G+r}Ko20dyKs%LU0bqW8^kFQ z^1p`a!tE_ME6v^9l$BVp`Byi-$%^RD4QMd0L*7=YLAHABBY{ z>x~!BS}cV%GdEQUx`xAdjED20wKwYNG6I8Woxs*G?lecOFSbsl` zqCpuczwmN(B>0Bj{^Yv8iEZYe)uZ{@e!an)e2zxv`K)GuFL?q?`_S*{tKXU#3u8QN zDVWXXc1Cd_#c2U(?!J8rUnPi1d)`q2qVn9_^H z*Uf0J^Sou+sKiU)P!*fAzMgI;Kdi{A!IY6mpN|0!@v|4u}R`olFB>(f#;X@_}#+%fVQ z+H01sE?edOV*v<64a9G`Z6lAD{l5WD?P#HjagnWW%Q_a(AAgWMahlmNRv2C1%1h_i z8XTSQyIznde!u)<(tMZpAKnyz^x637T5ZcrDyQR~iYdqWs%Gir1Dz&1hr z6(1%fPM50B5cO12@9-f+86iCX*qs1Pj^(uIGYGxPw+f;rb(Pjw?J%SOMA9Y64JCdZ zJhgAZFpk6&_#`#31pLb}G+DIA?c?Gzzq@>f>OmGW?Nqv$XEY7sZaIw7DKZ>ASOi|} z>5wk5=)cpRC`wiLFEVs`rZ`m5o0AfCvR}e1o_(T6$A2!mxX>YrIuA}E;&Pe&iwnt7 z+o<;3j!AtXK=$T&FGnRjthC|g)iXG~9y!;XF3+X4q6HF%q z(46<$+61DRj?McFFad@f8<4B`UpsxBmb2{OLNp+H#P>F5P`?Op!r)mxI9h;+r(&^# zWW(mwbu^QjQxI(YJjtL9O)%pyaTo5HzE`egxd`k|LO1u=(?)x&E_iu5$Vu8Av?OR}Ilm&Um1@yb9k@?E0|y#=WXIhhbqO29YvI(Bn3~;qH`v;ENX%HJ+w~X1zWfGik#>Nom}?Y|50(vG?(grP@9v(S{wG!W;NW6!Z}05v;^JcH^~cK-2=t5q ztB(BP!+)I+1hrC~j=XnkdU|ASe0*+TU}JJ}G6@VpM_nNFD1;$(ve?Pr2&k#asbQ(; z{1-``)8j8%)SH_N$$FlttsP5Z&@#cm#l;1&rc$PAtRiD2qkT|2rcth52Jv4m z{8w^x)xSIXhEmlcIh^}zNTBr9=>o(!S)o~m1$j*@EG+DQ2QlP?9FT{&ntY6mjJ%&& z|MU9e0~-qq3qvcTqjlegkkK9<9-7_VL3)#vTX9G7*eqJy9Bw@yFQcTZ-)=7}GI!(i zGYj=bQqW3^bhgZh*uGE3F`&mcAj@IPg)p*QjNTViNVnW2osQRCIMr%v+(|DzUTc$T z^XNRNgDJwTO^%BI$6&SHd2{j22P09Pc!}qCI!+?%nhL~6vuJu zJ*%3K!@vJuBCviRWd3bZ{ZT@7KeE~veo9n$r}!nP_x?H(Rp`O)X>IaQ;l1Yd%!-b+ z>CoPijqACyIbIor6{2c*_i%7=>{dY7ac0Jxs-hZzW+VBk7o+2Q4w}2uPt*SzpOg0E zalM)i^;{TN9@#^8&pj62&G5%AI0L#(KRMxt@8cluW?%seu5hMSC^kJ5V$Gu3<6zex zX6B@W?snfFN+UDvJ?nASUKD1WP`xJ=_sTc*juVxZ+u1*r`!1^Zful`AO#c8_+~mn9Q-a@P3A<+D`}g)A*iafQ3XSbn z86h~RAC?6KYF~_trfqM36fnHZxP+j2#K}L3`=;AHf5bs}vEX1TB<_Tisc{XHNDX?N z!^&5Ul9zJR3_D&d+Yn1WcFxdwHy^|xa>RHQ1RgfpT?q9?-j2@yb)x#MC7J z7;(rfE9FY*3IkgKUxF0G)Ea@PJ(&z$NckRsQr3>j>g+Fz+WsEK}qPY`G+etdc&79w)^5XPG*-3jW^i zx4e$Y|EB*e0!rB*UhkjL)?V(iJU}M`Z~$^^xGkv!^z7`@x;*ow>!k@9gin+fTWx#; zM=H}Z!lAcMEO-!pj}PMs4NgbvyPI1QjIpPfQsAE;$=8V5@@>^VWGHQo+GaaFG=rL9 z>>_9gxB^IR(ADzRDE|d?T46z%S$)Aexlw`;Eh?$vuI#m-PxcQLUDl1b5P6>o#8mky zKnGr-?o-nW%Nj303P14Ifiq_w{zF7+p_Kv2(=yAkYsk1C8$c6U3=mlBE@>2nhDaeg z0d0mRGLoLxdp>Mrm(L^l4wVY6@@{<3@?VHwzW(;&YsIezp8||MsV$CiL+WVgWo>)E{IXtzTTLPpt(Zc8y;jtlx z!`%36SF6dSgzT4x%8=rw=SXEy?xGvf67@%J`_8LATWSc!{SE~kWa+TOWaGuL7Ou8} zq_wx@Gedpy>C1PD`?7_`bcQZ%e1^Hp5%BGz58bcM)uEv%$hf6KivI%piMR11tAt-# zRsm-urs;*g@kLkJT}2ON;S?85>ba>X;^(Pmt;`0S=U$T@1qt0dErAW)gl9-vIS1WP zCc`|z^m(yr|4L1GhOd3%8~d8*$Mo)*51Bhnxw2u0COtMxRuW~?HVsB@`TSxS_-gb; zszs{hiE<(F9XH|4hGF^&`k4RqwxPp})^aSAPuH3qQdpp#X^|&%QwGPL)#@yaB>%n)W%s#9&vY2GysV}Dtd&|)A)K5OwGD670XIGP#wg0S( zO7b8-Q$-%hR-|68E>|R1HoNK$tConYrft^q-;^jK=6jBbNte<8pho{2AKj(w?NOc9 z5B~j5g$RI#3Y=dfZ%D(W3j2&h9^AI}m4fDT7!)EbcD~jfGIYLwDk5DzhJ{uQv^eZ7 zOs)=ZWb6uqmd>+`g z7+zlt2fP2>CCp>2)F5JV9xIrA!3gHRB?zd}3}%D=fE^;69L`?xjg34KV-bUMukwS_ z6HUo3M%MMXexa<+p#`*>f)^_}EFz^B8}s6BaIXP>n3VS|&F7taV(!+vCffgt_~);M$@5I;kPPo;4I4EcY9y%0G4 zonvsq67!Q42CS?cRUx5kkvtj@za%}ri?B7c)aRG=@yV*d3K3>-bBILz_+{9C0A~KgbEBPtUVZx*JVeq2)M| zK(*jFJtMP>GH;K`7hefK}guR{v(s3qF z^D#(>%O7=I?=)4TE7cz^7cNSR-fT5lrMr{-$K%G)4HSPQ=;_msEBazIoQ&Wa7D5Rr zxFw*!7B{y-P>|C%uMTbM9-fY3cOKZNVp+sEW1?A;n_@K&!)_+}q?COjy%pUtz7sx9 zsbNsTe_F0wGys$k9eHrDD3zF8RVCzBs9F|GJwO~@RNO;V2BwgW|Kd}*L@vUKZqXuE z%J;ddm`zt+P42c=DiQ7^H?Dd*2Z#RfGna;pmCe)>Pz+Ob^dgr z;F1%03;`adVTD))S&W5BakT^%kpPh`osOU5$Mds0q9qC$#PJ_74#{z7?s-Paptr%T z={c7Od3oe|!%d+UZ@(VI`=#L#6WNbS3@Skf{Mv{NTT;FyRRZo8eG!f8kk#Dz{u4(A zP2)Y!IFazLzWRSHI7PQtDi-$kMh_0}VZHwuE5eBW*bshpdokf(y9{F+=kWWMzqQ@O zd^JpgIed>+M9f`tNsZI{PU93;QHx)7aoz9jvX>Fo^-mK&dp%_T-+;7fSsL~t8E;3U z))Xu;4o`0O(cTfj(3-F^i>1cNdg=6h* z=$}2r?9Ssux>J)q+RrlISEz+aRJON8m>ulYd`#y0b1n}Aq+E&Eyk4HaXMjk!X_0hw zK3DNQ%iO6y>WxG@2!mhU0`0Xs0wq$iKob>;1J+LLfgAfj0cv`yun64u#rF6_#i}Rn zM!i-yq5-fahqp>(IlA)hw=+{AL|C)ti#S_F&S(1v!xJZO7gAUdc<+2swu$k$xLWHY zER>9Rg6@~EsiHClCkTkpKiw4#VO^^^hP4&f02Ol%CyEJfHm^Z6v^qSW!tXe8kU)5> zWlQO+1Na>4v&JP>7E!LcfXa;LN*C0Qa$KnIr>JcFFsyE2P^Oi6afEf5{p}y{7VHD` z{P3JAn4P%&;Ij-W7sT-uTP^2TXXQhzIX)HU$6AnZ^N_55xk?#pYxJ7h!K0rY<|+j* zb-hdptmchgNo6%tsY1kC_FDSA=&Swq?`BP&V*7S$C8ze;%zawEQ5Iq~IR)WI^~a;7npRQA zoa_3ZiVblQv~)dscyIBRejvY%Mty|Yp7HjCj$e@I@~vp$b_#aIuYD~xKix_A)phvv zeEa;j;)}C1e?zG8&oJtz594>2)G5k2>rUj<5*B2$otE#Ce=k0HaXnqi6sr)tzx6>s zhge71ve3jSb`+)Ha))Kje;f1s*M}NjL>tvRXpBVcAdKjX{hsl8Ne?*Cms%F#08AI+ zP>Sf!4M**MKVdjI;{;8S>u(q!nfLdFcmq>rQ%Yx<&DouU6*vAN<&?ifA1V~u{PwS$ zHoj5ZteuTKbj17Ih?lP}HD`{s`B^^vF`2|e6)6D#M1_4VqN>vTbCu785gGu-3)N3Q zAKjJSipC$Z5u71J?eqPruemgjtCuR!HVbXCY0g0 zH9KG@w%_X>beUfIK)C#x(#nA>wy_UaQ^w>N6r$K=O+@=6d)>S2HUMzx1pW(|8%Q{- z;AT^~FJtY|IWYnm<};uoS*;jqD1bIyhaIf^a!s|?T+#1&JNbywzjVW}mTq)nGYnb_ z=Xf7sCD}UFd-ftfJCxUxz6@#4eNse(03I{U*nfy%v18=&@~VXn?18SWijROgqJgAV$Gk(`I5{B8B&9`- zJJfza!g_)v&vZ&N9C)4iisEy)jlR7_T-uU|fhoTgZ~G_Pw~23&24{pajCHh{UcRdR zBAtXTz7*vD{q)h_W;YdBnE`dF2-nP;-Zg?^w3`e-d1Rg9XouZ2$RJ@BM|czqy{v+TS1CeIT{e{&i@ zrHZ6#Uf5{?%7Zi{mWg9=IAziCaDT6>@dsUwiV)>>A0QXr0=@n8Z;c*C#B zzA12?onihD7JzqvRFRtC?B{s@3fG^5&Z9)Q{t$nqq)z|GkWc+37K+NSv8{SxhZtW+tYXUIp2QHF1rdwR0>ryf#0M z@*4lsZnu#JH>H2AyUE0z4^&LbnyRnNLTq54{Z=WoAfgmbo$ONMn-Orj{z`93=zPz` z{srF0*<17aSDu(Dd(x%#gl#{4T#*jhD)4y0o|q z)Deoltzrb|b#A*UsQZTj;-mBz80O^gNN&l9!F&vM*u61jD~;Mcr_ zSKzDGDI=otHu*#O}h54+d=A)`dwPbNJE2z#H6L$_sS!neV?0 zl$r935nI(PJuZz2$FYNprYY5$82k3iQ$Xs2zBe;PL9Kdn&h_i6qdh$UpyT5110Kb! zk|y)YREu>JaaL*d?|{RLzd`1>v^yiRlj5lj`fytJwBNP#T%UiAx8$y9voji9h!Kj( zs_nySFN+&QIs`-^C1LEk5B|^s(B6N5_K6QvQIoepCHYHiu+n-Ve5*iiuwg*XAvAY< z_I0zYqYW~|f2cH992-DG({E3tjNYx8$sVLH*r_8ul3H)7B@0I?tDu&CWr!5~p$8EO z&nYqMzyrRNst1V{QDUqJ@CFF72mLq|f&rQt zSMeL7Wj9em%gD@fHe!%RY9T%HPu^C3b{~5e_TT=+HC^1dMLR!apXk)?T>W%?S zoDpT65%W=T1$~`2n=K(*y-9Mr1gr_wLqwCi4|fqInx_m|rdK6+DuL#u<~`X0HRq?=&RzrgPLK6PP?Oz69Zw2# zGgFFEIVR2$R2tI6AdYunRL_Uh`_`Ez@HU)0F7Z8c`I`F%3Ucn#PM)ONG^V>fUS{go zx4S_Hc?WtZz;~xH(>E~T2@NLy;r59+7b(z?%T)54@Cnva&#x*X1OaTDOX)een^WVt zLQ?^})1g_^k}V|eb^qyqpt>1jrtT|ip+n!v&kFa>iO+7;bGGNd;YR4y?lYKWb7AGS zVSrYr{oMJEF{{TbV+?fX1v_)j-juavkMgc}6E-!8* zy?B0{|MSeCv=|}MvfVKyJtE1hC+NAAw__>qw#NOTzKCx1sbm9LP z(DSK2Vb3n}BSHmIyujqM)0WrS4Zpx6z@ZfE>O$OTinMzhK(;KhJpCgh z;)oTAn7-oH$jrr;w9TB_%k$lSI>rm$$osv`1;Lmf5)7bWl^{B z)cxM%qsSk<)fV6`LUY`fZi2GGTU|9VfK?;W^}HmGlDjC)Q$~-5skzszbciR9?{+)@ z6J3G~DD3D}Zu+zA>lS<#;g+pI{gj$TAFAfp%!Hhn}RJDyb}qhlN{`R_+&#;7@SGQEofdYho!*~IYk+n5z(io z7Yy~`25bqMGS9elr8uw{n5xj`7HuN4vE$^FpakgK%5ho)@w#UF>8ltPh;0~gG_Dca zQtwCoim@se5_aA&7${gz<45|oROjiD%7J~Eb|&Bt+QZ=j^xmJrEvyYiovbyTh7T#T zSO3Sci6f5&yxMBk{8p!B z*}}3v<_hHzju)3>_{Llsin12MxDQ_Myl6jnIIndegJ)_@36@+SN6cH{49K@hY5RLd z6Fltye$Aq_vf?>1zX>fUl69>pM9j0ZXE^>Z)31)fQmXfYlv%kGD1Y6qrjBIjA{z7K~SEW|>)hraqH zN&qK?LME}pq^@q0qqY`K03hSt&q>haZ}3T1vVT3b{Jx5I4x<=iR3T}9%kHeWHv9{V zkkgz!oO?wVV%B~z4#)hTp6S+2!|BC$H|=-b_^Ycs@_n~M5f$(WxkRqt{ng!}QOU&m zj|5;NrNfD#-yGA9VjjehsyE(7R4m!8y|s7rTF%b5NEM`)z<6(_rqcDNfV8}3sMYy7+jl|xLzL*3YUN=I-bU00 z>pY}BLPX^ndf2b#$7EXuo5GK`MylUM&vrz7S}seqWZ(P*=Invx7rpqOEcXe04$`#j0HkLUX3Jm6Ni?D2>>7tn;AW8T5HW4173rH(S_}DKgSMY&O zyGYeUePx9J3UG|Y^Jx41f^&<;trSVb{n%JO{UImv zoKof6eX;!H5BHB0J7tP?v#LB|o-eCH_Dg;)M+Vt9BiRfm!++y;gBzjqgXH0$@eU5w zB>k%Pnj+0s7^-f-StGjrO9NQ}_4Toc6Ex^ay071jfsv2djKH;WvPr#V~`x zWx_}rzGw!-zgO*0oo@txMmk8`K3;v{s_bsx^Q1S9OUCKGeB(iv%-<~WgX%qjecAtH zMOT8LwCjT38rAF~uV9UjY9g9{S`GX3?`lmb&XNR+Dtknnp2|<=f#FC4?{fDc3o>Tk z-uoLj<_+rC6cwp+t*^dc?xw=}mMojz4}S|o#VZx5{?w;9+5#blh@(-{*F`hV7ZkxD zz}r9D_Fo3mA$}i?Na#^|A7?rhNQq>O&Ti)+dT=Wt&o1b`T!|CW8tK7e_^CEn!ZO{}f$t*Ip3gwC z_f=&LJ^dw{c+17N{@Hd5ync^k7@vYQ)d{@;6;!u<>nbI9#)5DTO~7jAv5Qgctax~T@zKklY9&TP zlgknn+%kH6bZzkizDORF!tE8XC~Eth9XB`mGwD4}n=ZP1DU8+p5B2NpUZ&#L(cRq~ z*QjNE0Ji_-%TaHc(Z2A?(82o$QGBN|K-zqFeXti~ce=CezIjLhq*id)xR@4zM=$V@ zyI>kq;Umw5{>S~o^`1c#ax+jPDf>9<1|*8^q6i9QH%2&rD~O#M$`lp-9Oyi9>$St6 z0H4GA=?_CFBu!?3oV1&)s=N9^<@W zuu`~m2>P(ky~sisCebC)dZ#PFbp|-n@}0ky38en8u>3b+q$QfrCEoO)#`c7HN90$% zagW)IrR_JcT?)=Z#}SKGgr||pi8TTC$<9GwfG>y~y@tM!dC$D~m`A= ztxd*bI-XYA_yBTcWm;I|$KKl7{-DUhM(8ofkgbHm+dnd$oJ?I-xXg?{JF3R^zZJV@ zB@ko+zLBs(B91Y!`(eeTeVvT}A#CP$NwA`oo*K%r_t^ul=~4JZ@ZI-#%RQ|?y5!hd z8m*ZZ9+=|-{VI{Nr(q+90V>iEPIgfjXyay7v`p#+pTb%(Na!8A5n68}CnA8AB~?c` zzip~cUuz4U--zoSQF;j#Asv^PXoSdxtRKstbe#OK~VN-8M@yg!w-FL=2kUsP25 z^-Do6KpL<)r*et6t$18bEyAPfqj(tg79n+M)N&EyGBP}sHSfIE+Pc(6hxFV-QLu`2 zL|ff$(fLCZHn3M^WQ-72T(k(7#cOp*}{mGSNqog4LH|uR3@FZ~~S`*Ai&?-mdiA}z1P(EximuL~yI8s67&dZ%GxY)lw zbepY{HPB4GK00}cAsq;PmtRMB=(p4jRB&kgRL7se_4n|WnyC9BQ;=)oE4YC?;EqiY`cg`)or8 z{L6x&Hm!ZYbcyK;$l{WqXO6q4u{a+ zx3NKxd(KQ@D`D@r0Ji5}ba(zb0fCi@`|$SAHpsM`zo}b=mU6yWs@1|@46LEgn;Cxx zK&>+fN`@e_s}OW#C!@0hhOv#2KM(Rv)WfSb!}f5da$I~Oh2v}pta8dIKF7Om9v(`j zV~zoT0+i3o3r;PD*Cj39DH066eIvIDHHXEg3@jS+$I(u6-2tXHSdh*?9o*ZNsgFOd zaXPI!W$HPNpEX(Oi$NOYdT_Iw+OF9w8-{%eh|i&&-1ns*5zzM1sJ<*fZ&a%*<-7tt7RxifE@PDIQZjoi2TM!wDX*Z*w(0 z_giz;7vt=h^}OJx=lpIGk+Ush@MFsO5Yg>@R3Vg|y6!Zy(LLH{VczWqU^810l602+ z3xNthNr1vI2i$Dl9ZfB31}L1A`2@ROGpmSG_}W0#n}NO43*3pwg@Ww7q$h*qXygkv zP#Jk&?HZ7BVM}Jgng^J(XMBUjF_gwM6gGr4t5<#x_w&Qa+m@uyN>yy^Pt+Cdi(~Xh z95;S9TQT_eYn-bTj_}p?>Nt8T2Dc^>N8dgH4b2$>VKW}a(WMl11iw?AcUCgAyxq?< zQ{bSXo^jeKho59zY0*C9Fy3AAAMcG#KutUR?C``LA`wi}YkyczpG+0u-F{xA?vRuY zL>|qR_rF%9tR?C%{9hm4AQ@w^CMt-j4F{*!9gymhXnput=9z^vPZZ zao27w-(^0(|F%_f=g>0U1%1YIGouP|%dJX#{$y&+7SFoEFLFDdKcy%ra=j2>i}uz? z@1poGwi=(-1BrK~n)-G5mu0F#9D-SocY>^7n<%3#F z62wY6`u)l(rF+nQxjjF5viVGx59G72_)8uSvA4HfTW$=Xmj5r*y>(C>Z?~wqk>Kv` z?(Xg$oZt{FNFc%8A-Dwh0KwhegS$HfcefxLrt|ymx#v4I=hnS7Q#Do7)qm}xn%!@& z-LF0CS=AyLL~AzAFXe6-)d(h0Zn9?;;vQGO9wj{LL^^_i+U;R1hBj9+9MAS^!XK@=W+9gm*Qx=C)vbpTxwXEg0Bwn{(K=dn7x#`uV~+UUKOj53fqw8jKpaB z`%3PLx9*A+%XEy{?{(w(`SCYY+S`0KdMb9WOAv3B&+nLk*OIL1q=m93VTakn&!;a| z4|T^uR^xjy8dckiuU~iZVf=52op54Agl*T^!wmdH2J7you5wxpa&C4|2s!Z3=LL{I zpC=n{A7W3xB(Od=l{!B+k!XZ$ULBH?LAkJcE{pCgDyJytPDudCNoZP@|yV5>)Oak3=Jl7wx6$v*dy+=1I=HX`0= zGKe%ZI(BE5(bwB^(=5nwgQPD7S^Od9V!iKkHjOLKI4^l2OeT)-=kp)4U3Q{|)<>oJ z@6|pOp__T}8D)V_0@OQMYT%VfsM91^B(XwtX$^w|K0cCLb_B62U(g}w*B|RSD8A>1 zK2ti`UIwdguS*ERY?o$IOfrPe#+1sT(h4D$|B6$hKE-Jz0`*zAHm%4u}YWAL3Y@4XTBQI{oX#Lq+ZHm&vnm!!dCAF=I07L`5Fb= z8kkI10Uq)7)&o}CbDfO`RgFDAQeG?P~)T0}g!`;-a zahs#EM49n7?N?qX+rTMjT}8LWi(bxeQxaFvk=f(dFxq7s8MS61p7#Th_%RKhlLkxc z>{~@S4>OulA55WX6~DBe5zCW=UBVA;Th044Xj2PFs&OM#m@O8f@S`{UG8@e5*vF=x!8iJB-wfaTWbxj6f>+p6G!P@2 z;o`BvtgOr0w$BzEfgJi;^E`G2fWa8idTAN=WWJN6l=^>CU509wluyuj=kn6YB)$=Q zXXms!$Z4HWxTD5ul{$=7nk^x#)mb;dZxJxa__qAH$e6rIDt*Ciie(|4URrMtPUhCY zvz)xUzQ4OegewMkS`u^|;51@p5~5&Y65`_GqT(yIQ8n@J`peIDb&(?b`Mq@m2Vk-l z{l_-@ev*MCv{(WFW%C^^+(a03Do@o4B8mX9Y;5dbgtw^)_2I9r_68NO&?1x)7;y9P z@lpMAHruHFy>eGhRaI;ge0#glk>wJqhM-1@4tf*_%wURq2qZ*K1Hfb;I^>K+j0?)HtY zt_}_kPEN9Ri;}Sc2OS%kKf27O(7NV_5AOdQA3l8eB=-sK86FuCaVK0AxN4Zqd~aW$ zgruZIV31@KqO?@-x6n{cWQ5y?o4c#)`||B0wdH(A>Ia3SH3n^GCl_ul*|%w0&Ge#3 zeZ95~_({wB6tE9iSEZ(uj`nCI;9b!1G3C!p3Ru>4uX}Ar%K(M^dAge!5`(2Cb2~!6 z7HeUZoHK?6?9Ugv1ku?3k~bl!^Huk#x3$`G`#^%_b)OC(JTBor?j0*yG+RWf>^Jxw zaX#%K!0DFy?IvSNlG3*2Psu>NKa1`9;L|Hvtc)b?X~!*Jood0@q`T|G%LX;(%F|2} zgzk-WIKf_gZnjXk&^Hpb^o?I}ZfzG$#tNPVnkiV8qHQ&mRis{&=%f*pGeD|GJJ_h#wXEZIpZ3#(+{DkYg&TJX6-&kqR>K#KdyCEz4-v!p`%42; z++tXW*M2OIm5uCDSM*n!1zTxMyJ%Fh_$i_~xv|zxoD!DeGOmt7h%GqxP+~jDCN=3M z=W%Gk-(YxGZ+obn4(Ub9zPP*?dFGb#(X#vA?DD|a4?-eP^63W78p2Qx_m-^g6J@bj z4O$hzh2X{k-S+!x7?0Jxl0roltu^hX!{j*0=ptv|c8TB1r(KHUT#k{u20uWXY;_NYUrGy*hH zGq}1@7I8FZ2pVTRGd-9mdh?g$WRZy`3C_E^`7aWeomTqOUJOw0(K>k>Htx%2tB7wK zYH~+Ip+KM~5sE;vPvq|EwS(}#b`i`@rU~89dr+_#$;|?bZXIW^(WvCfKni8^NA-FY z+GVpGp#ZIlHI<*DL-y`QE{;q*Fxc@OKL7K|9=>nebMV7v0_Dd1kPiP-*xD*;Wz)~h7MK!_1ZpG?AWn!++J z4M*rHexpHl@__sC6;euV4W|L-6xZEAlj-@L3aHWoknl>Gf)K`Pjib>1);SnGO)1Q} z?UV`IT1o|s1QTrYb@82UC6uUeUa1;GQO)nG{-B4VdE>Dq9W_v?Q#zPP8i+fB?n>WlojUHfSm{If5ZeP7LO zqIhiCrN>|BMEW=W408D)%tw7xI2mX(3~&_dht=ed3ISwrgVafH1+G*AzI z*mIXc44T8Ft9);fuA!!cvznc~56d=-+vF2m4NA`%IvLslvzl0IG=!Xt zN56mm2Md75uDI|y6}wkT;^cI3=zcV%Cm8&ZBr2GV63RAzLBkGIcbanNxv=UB;XmjL zKX8zCCo%Y;Y*tMmE5=p2=+Gmd?HvfOYs826X!f7h2W}06>V;QrL9kUm?s6dz;FgDF z#wf^s)fg|(*@S~@eMgg%Uk{`*nE6l4+0^^zKE z4JJx%XNCR4hHxbB#m}9nE#%Jy7E^Pn&3OxFnQ&9!UC- ztY1=aS&<5BGcY0etjtN=3jQ=yx2Gd_y<^n%Qt+C=OnToBDfWJd=O9!t-~YnM|gpFsrkYW1$>wW$0T-^NoRC)^13j~s&2C- zP^y?eJoFRTJ^m}>OZlXClf{As9=T-UFqF?65G?i51R#G25VZJZUgC1XQoXUzEZ4!p z*nUHvh7W|rE3^L=d&V%sItV5ZEdHE75PF=!Yve~`i)%*Mas|L+sn(2_gV34}(0 z2IVyoQ6TXVLV^aaZ~(h6=*wx!>TK9+q^v0N4IbqWIx#MYddOd#jCHRlMy ze)5=yn5l4=de|GE4oBtWTs2wiS5^FD>#*qK7>*!s72(&^ph7j~j z%KcQu@9)CIpm&NVmba|$quCb7gXwt63CxgE+1lot|3<~X-Z*cpamhg#RBS&UlBJ;~ zs5&lK#c6x;{PF>G8Yq@7F<^(p#2IKnlQ4X!VLm2HLutYBJ#qT#7qn>hA{Ml`->*O_ zxJDcG=K4;TWV{UE(Lg&7aT;TNw~iuP7LHdIm<-q*nJ)V3cLft~;!57gL3|ns%0(xG zV0Mg9itdQ5O>Fw^3UEthEFI!#f@_HkD zeka3=fZ1C9f+v9|(M^xakH^dNL5MI5UmFU1rA;!XBVBXa9w?fcrP4Ay z8C~ZWD}2Vv>nmI};CdxLyJ33r@w{Bw)TsEFxf1u;hovi*Rci+g{>G!WK7SITRc^=7Ke^;3yeH$ct=_Y7MQe*1I%$_?nYK7i70>T#8aI{FdFzhTT&M z3pLl7`Jl+qxhiwAU`e?mdfShf-vLeDTxoB4V%rt0&g3zEE&!vm?f;D6K>a%m;-MGO z_1hrkF6b1l>YT~|<@9`WoPY51GG^&?bU6(k(sLnf0(%;_y1c=p<=5p0WioZiog&=9 zDjgD0gjyu#Wmr%JIk~3{`w>4qT#c@6V`z!*&z=nOzHyO#_q}WOFLUNVZ z+PuB79(~B^0+L~yK4>Lo0mJ|KpC|!5)_tpNx)CJ$*Df-=sGs*&R4l6Q?*%3$N-+=mW+V*`=)OyuX?QDizKIH(Scl2WWtVpO~VImQkG6i z&sA!2eAUaTv!c8e+#G|u6JFlybqcR&g0@#F=0TY(9olfmiNwXUT3*Z2gCy{?HmUnd zdv}T6U{4<|iN|JbU|4Qr6FdnKp^83#XS#~jnh$y%J?MlR%W$2`%d$N{ zB`+en>t)~U@rJ81d~JyzED}utu^T;5m1VFPV^3hq9s4lTC;<}lNALANOziOwz)e(~ z!ioX?Ahw6-rn1JIq0R60Up@<_|6QO2(^*%KzcCyq)e32ZXvbqIz}g^hVFxJjD!7{N zSRlpf;Vkb6A<#h3mjzR76pDU^cQeB^p}v1)Io-h$fnlR3ko%A8MKtN7PRDLeudv zB;?Vpx6j@iofG4-JZFtG@sOb6wXN7$*ntJrb>VuKuM#ne<0{~RN|AMrdW|;x14Ix= zwfjJS;`5=}mfC4$!*c?nMUs!?hV8w0PV5fu#5+(XVVCDXeLfY)zkT9>^I5yA$z`H( zRSyLEyuBwu_-=fxkc^3zbJY`xOU>!B7$rh+R@<=A!mI6j<}NHC++9=G)V|1U%@E}- z{o_RMI*fxjn4Ev`e!;g!Xum}4r`u7hbr+KPZ`x5*2Lf&V`)ixA&ZKE;0-ZO4XhGL= z1D`9q)^0yPstU%6RD#nKz_;75h&t<4Hu2)*-ZzoxY!One?E~|QGs|A7Y~$|$NIUm*6f}&$Kj%JtAK9$orM^A4wC)FtGRA0%jQ5C1ZW{kYcqmS z@Gv2c+m9;3L)F^;b%oqrSKP8Ph99kg`RdP3fh0`R6Zvbgj3)0pa=y8H-kzSCt@>j5 zjc-la=j?Q0eg^J5A!M3C$NMpx_5|7Jd<^%3-fmmzb(z;mR9UMz(4Zevz8LTBtBuVn z3R{RI!s3SI11B&-(8#1HkU%&W;|9CT5@MijVvasv{CjJWKZ}8T31~gOy2o@wzRPeH zh-Vpa;#1C<>!}~Gp1CpN8NZo$kBVZ4eBy?ZHbwHM zkA^V=DO6-HHI!Y2XwVRZAljen*SwSi%z&?+kom=)7O9_T=i>mp6*rl~?b)^(H}^qd z!|RtJbrIEa!i=EJUV)iHnEUYg4JCLfo&uZYw zt$5KSf$t{o>1*ev->R)QFAn`DS8bGcnkw2c^ZFTPqw&V`%u$aYXA`dV;uA?>L29riC(&{}TlQ6GIo`DmF380(3WsOOkgh5-esETd6YEoP$Q!$P{vZMaTKSfO>DaTr z8G9F9yw>TZcn}qIf@;YDYjket#a5<1Xhd2)G5W`2tr2@xkoTzNwo%Wtl`Li6e){M4 zr?m+%>k=a6PIq23YNqHRSPeU#0%648+!24Kph(E-Ial5*J6Do}P$;p!#S`F0#YW_U zGYuHNWD`NIV?hC%0PE1`0;eW6zQ>*9J^P6KJefG$^p|txn-ia+VZ?ygN=Gwb->HHny+em+jdZpK^Xy*M$4MY>Tvd7HWcYTX%JeoaFh9tj3YI6w@M z4K^NyXxvXxUo3gohFz9v3=%>N5dhog;_XYXv+C(jkpB0>PsVu(<}NTOpTrn4Lt`b08m(og)F?xNt z1K?f^3*Y^^=M)raW$;wD@^pG!FGsUWmOx3(&doR_>=od>HEGa$UWoenVL0##7k4a3F*A+p$5|0aZIoibt`@-VG<6qplhpqHF z_=3HQ%#N3}I}bC6iiwX$v6F8zMi*xc-WCx4LtdKJz_PCE$xjpHZIcv{urwx^kKb0z zscD++aJnj47LSt2dN4+K=DHkJ;}|F0)5qEolcncMj*8pdc4A(c&4$l{y;{W^PP8fA z4Cf(0zw7(DSfN1_1}u^$V_#L@;o_zNbaqBGfMg!FsGe5o7eSdkND$r6=}Xq`bzm9P1olrP`MiBp`~wNyWr#4nqA7nbxU zTyxtQWI-nj4_~h;aq*=jz7K;0cv4#()u)cCyzG^17e_Bys4aOac*#3qVEzqVG#b?x zS29&n#iLXxnuQRjkTL}d9{Nvb z8QQFl6~q5O6s$`|6tKl4yLYdRfbT)h500?9-_{ zw5{Cp7a^RqKQ4W;+H=6PHy;hs^JVvaoyOS`PLCBH@k|-AiCO`>y>_XIBBi@@P32Ed z<@mjM-gxf#w$`=|Bsn*p4_1x3j29q5zqb>g3&9Oj$`syfm3}x2 zon4QU_AfMuAMv(iyj|<(KYjNx;FEr7RaD`mbpSTb`NsqC1Y`Nl(%?XyqW-b zkcjXV4U@BO|3*@=GsCOu*E86+>*9ivl5dl!e^8D78CH|4MSLd{*$^E1GA=wlJQeFF zdTDre`+2m>Fe!lX+x%UpUrxt-L^dM&L-CBnj|^2D0qYJJ^lBZ_Un)3ZBsQ~2slRJ= zUs_vpt$|$TMEA!-$U?Md&mLA~zS4(^2 z^@d1`vTjkChJd_L#VpHWU%1$)8x|+-X0?TDMv2NQ=uxwfr`1%3n(;?`W2VywSF}3% zWg*@B_&C;O59D8x_tQ)?ny7|;e2lWuuH2ADlDQLpJk1y;${p{?{lQ~agP2e$`ibXo z?vK&Qcb9Aq%5^l+jO6rcc~VfEU(VN#j_iuJlWO8JDZ(Ch193MxyYBhwSIu~_r){{T zB1xjb57In6j`o&)G&HPS90i3k&*)BGf&tj~il&h0cz-4PufwUtt~b>_&PY-;aKBCy z%

XIsczCtw8S5f5Wsg&fMkmD|W)E=&siFhxOqXU{z6_3|ExXx+Q+T9$PnLbj^O2 zbvWn?%DCT<*c9ab-14*YYEnlu_Vr|`U2qH^;bH79OB8$+K>+&wbQgGhwmoorrsvYX zU4+MV_JbsYedbCzN{yZ_MtwJOc+u_6fJd8sgVlMW|U8W;d znvmkKToCBs{kOmWWuS_R+lZ(;1a9$Iby;3tGF=xz()+>+A6Y7nCQ)Y%lAdRA%3oB5 zi++3EF|MqwyNN03c<1gbu#@4*XWu*6kxZ=)s`l8a$yCHGf~}s|JiZZivv1S3R4C?R zb+}r_!oODA?Q~V9&9S0$;JOf;b|UwVWnd0&)x{B@R}Er2U4X4gct`PVY#CoC>Qz9%%F2FCQbz?;1We(X zcy~9nF5ZkS&$9Qa4YnSe3~==X@egil9K|`t0`A))e>xURAAZ}TKfb;CaFO>7h_Nwz zy%~xX@SZDpNHL!LBbws^sISky6{FL|T#(z%_71L7CpCofkgp#Igvkc2=El3 zg&IZypi<4t>y2!UqAb^qCnr4{qbmU?osijLM3xNe1yTCl>2_IL^=VP+j3bDgukm!g zU(x0K_tD3R&#~(eN|2s|D*>+q5L(N4w8iHc#>9 zng^J`GeiEBSw)VKjG2Q#Jay+T>L-$70CmxnK`Z5H9(cGs#U{iuU1 z7sL#!GfmbhA&Y!{=Yoaz$>yJCIt$A7w`11BPg`YuoHld6wZpnkfN!j}mA=xv25hxi z|BDNCD@PZ_-FvrXpWf}8Ro@WGF$Kg6HA=Gf$H38jjt@GDajN!ol&e*FPhp2ym_b#O zHgDtTK9E#d3I#AN5);PL7ziOm3Ys4cUrJ;ZVNsA)t`~#X;Wf~Hf5)Zu%X&@GpL1iu z*Il#N-LP9)%nEdmg;=kn^s9{Vqt=Eg3Z0D|P^qYH@LPxfrXJpn+GX4%vcLUoCi=(u zjItt?<`Whz*oz_jalHb3J&HTYb+qQV(!$|Z%_TZ_AGF^|JNRIbWA{q$@MgE7Dfw^E zUsvZdH4oeHHL5<-7``C;XcDmZZV+nDb}@k%T7~Q05)+`TNgd&u439_qIU5OvBjbaw zN9CfJX-B~aV^}jJo->eapY=EEi5kMxpUcTOsaPh^aAuV3d>BYnhtj)t$4HE@V$hIe znd`zMTfIQ`EQiU^`7d&%oAj}hRodHEfn#7c@u5KT1lLRi?8gAOujC{ zo_ZIDq(YQ}s6^bRy;ImlUe13?_pCC$8(la;{5!|#r1d{JPE2^vE-}fzLi6p1oEfu@ zE#7p6b9?CapRy7U6x7j81_jPoAo-PMo`yB$sr9Fh3*4l9oT%7NF4L=u{MCim2D^39 zSX~|}bvefFe(8QZ{x(gzE<u`0CF5jS33F}d*T zWtoMs)NRtZ66dD#$IL7T-Avyo6vXqX7>SXVoZ-Nz(zM9y{|R`>iaC#K^jQz{4I%P7 z#3S;HwN3slHZv`W!aStd$nsIe1s@rNROCG$&ZvUB}u8{X!vx;cWs+LKo z+>y!G;#LMKv2UBqwbv+0RAl}r2{*&Fye0KK; zh}rE=?M>~*8gTBV>I^`K`jSC7Jz)F`!^mgDBFehaQf`0AM9Y_}8oqp4vSq}*VJ7s` z7`nVs|G>e`OSyGmg##8KM8?L)YvWygU+0~K`#d&n*$5+U%X%U)zToi5+3I!QC8q%b z)1O@xD+DbCf{>GfEEvFUcel^s)WD;EdQHVFh+cgE9Ip~YGq8e(vjm4d)^4&C6qWPC zWW*t2KthGo4~UmE&Wqop!IWHSy!WgGEq?2bq7%e~f^O=gpLe#64hjL~YG{;=VrEYB zQ~DL;yXo^Cyp+v5IWlO?r;Tu^23OW>fE)(>*qyEnKSLRFnA?dY zmkdSH!{Yonv*plsXXvY89Tb-=-r;^-k4G@~V$A*V?aa}$;p#~S1DMIz8woECI9w0^U!vh1jkURZ0Etx@UF2{(2vWRW$5w4!f2ae-a^~p3^pj;5CoLqptd$n zvE>@$|2;wJjVx8slP@SBs@KlZsANn6*B5L`U?*p*p4@=N`Ex2X)ZJ)k?frs*0-MOM zFfz0r0j3n2M~hI48>UKcBbyK3lOfS9Cl-Uq^Xs!xOnYGxH)mSfhdz>^%HBRhBlgKb zkc}a6r9@{u5I{f#1YCLS3l`6Y+Nq!a{^|dHE%QoebkGl-F@fcU*lA(R6S}M2mpRGi z(KmxeMKH#xOvs}HCz|mv+vsVrcQkA5Pimd+PA3wIYe4fN?j6d%F_y2S|Anz^{(s9@ zQkU-hFBwZg3V^W$b+8LU2_$Yn<5+4KPxn!0Ni3AsDScXM`+2Wz?tb^;fo_9?wTsey z?x^Jcd_0R3B=EX;PRx^zNbM7Eva?|;;iTq^c3ub;uy#YX_}{+cs2}5MRe;k+(OG-Mn%%i^pPa9 z-%GwN`m?|B=*S-h#W2U2aEec24J2w+*R+Om|NUXvQ#`DfD(O3lkkg3WHYWnBV7y3>h85Bqxi_nn3|+>?(y| zWeL4_a-WBsY5C%XKl5E~_l&E`xF6a>acsp9WMLs~BM|s)%QQNCJ8Uvg!YuHe zS&iH0hHbdQb{koNiVeCCUxFCQCuv}t2mpq6oo39LwElk&O=kaL!-)SIB*#FI(6_>j z(n0yqMGixP82{pW$o*+*2{`$j8GpauG4Iy zce==`LJ%hYWb(khkwoBCBjv=&cg-|t2THACc52`Lkvhf*eO&ah)~`&V$?t@H9w*_4 zKhj*sp~YL2F{iad)X8<$TVb+h`NH`qdk!p>Lu&O^O>qMUIuFJE*pD?y*oigI%b-HE~Vpt=9c=RR=w&eFKAEudzi%IxcF&`5j`nMnivi z4L+Q2-LGH^X6$F683$QS)_|d|W0f}TR;&YX_-Am8Sm4d7E zVHw<=`uG($-A*JfZb!ZD#$P0wsA`Px;ovhdNGu7$>wNOM^~-hd`cK@W76RHFy$X?| z`9_<8*WAb*W3#Je?5+P`0fMp6*+1RNs_0lr>&n3=dwtp8^{H5l5q58wm^iE9o#nloRT3TrL8O=qwdNE8`^@)c=;ufN{zx}2 z5_0GIlpHhRkmchZp0ovkKILO4X-`__oh zS5B><{ShdR#Fr7JqWNaC+-gi9dVw)yigOqqJOr z{Yq8Be~Ucc_V|AIXCeE{=x>KTkb6jyRWurTZlC`1{cBIefga~RaZtxTgh%5aPs6po zF$yiyfxc!JOg+yupCDI;_0r?-O14%-b{zrE=$j?vISag5q;Naqd z^!Ej+Nid)@E$t!CH8tIuFO}A;GRl{UCg8TwtWp!E$kS`MykryZ?E|R8GOe9qd=eZS zf`3jN91;=|qYAx-FdKk$oG;S>I2BJRsfOw))k56c57UICP#?m%&uy8siF_@IhP&wR)1GNmuNXh{gw|Z;2%+m}29przj`qLjE_~;#dm^&%01Ipmw3=KL45o!JSojXq zwH-XLq)jcPbjA;*vntHd#UkD9%J1^uhRrzh#Q!%8BO}KD9~nmJs$eWb-o4}NrrT;~ zCk%dEir;geonll{>UQeXe}y4B&-!Lzm5Xn#d=#n^a!`v^PaZp6SUr`QI8#~JJ^huNdlSzP!maC zb5YN5)y}VEQO{mX9gxD`69qNU0Ku z*!Q;Izea=`6;z`=@A5RIR~nU4ir5P2S)1ko8j`&SCD-F#-<5#+Z?gkIf`XjRO?FWcA)t$*zbGF^w)F-t zl`2p=XJa^+GM=qsu_>5xDWUN9D~q4&G)XI?(tNt za~}yXt6bI=3H+vGcIA?XXN^eD^GD7DYu{0MoY-#)_Pj?7M(e3({lNsB@3YmC?#lOp zyi4Ec<&qlcP;Jvdt2T?yrg(7rlJD|Yw#uLzZ=PBePLhN zOB6)iIETjYXV+aNRv48+rAsX-Oa zk8Ihacx>;moGQb;8g=n+yJjfZms^&c&4)IS$N!U3Y*&n10zwN(PF-fI(O9b5sH>a6 zh+QI#U4G^$?h}P=&wV6btXZH~{aMNbGEfgX;*Iyh*|kaEy#20v zxiL+1q#Pvl%$89Xp}0JjOwTPX+g&%t(Ne*Oe-x!{V8r6%&EX){jEin3z+z4TPaN<$ zjzj66IR6(i3H1*!2y7QV64QxBG_xo-W zY{N~vn53lv7}zwJU1o?*5KmYR&L1+0J{TTS4d}tuFT}k4MkT-D+PBYeAd>vKoT-9$i8J*Gg;VeL!cr`SOzt}| zkqpsyszWtP>YvN?TYQ8g{=n-l+}-ShNW_h{7=Wje@(-u?W|-G zCS(#L7;b{QI5clT6)2yhVzp+T@FutIq*Kcm;+U(;EMRb2T2TIrK%yaT>38ILIg>o! zvibNngR3TEr@ub@-9iscc~B`Q*<73bM6Tf3lvclWnm^XN{m-XF^F!-Ols`AWi4GRn zYFc%{vl|Y!&Xd;eUSyPbC|DTTNQ~u)yD+TGNwae_w=cdm`RIIiOr##snXDu+V5^QP zW4>dAT#YiSe_rdrZRw{TS)0?ZDeJU9GqWrJrjZh8BsOgsQ++jCu{x11$Aon!c&?Ij zc6uQu$P%+>z!+}$D4&J3wXIhhy*p4l0h&A|0qH=CZrAahe=$#QAP@V6E5EXz1ndF0XdcX6QtxhmF)hI#THAp;W{Zx@?mf4ofzrT+d!Ca1oC zczAexe7v=V{`(>)EfKBFUT^UWZ6^nsR(h0y;~zao*W6kyi%0&d=sND%5?>BZZ9 zr-Aq0@e+J|!!eKj^IfNNnWYnzle4RHT*BUgs8Q~#KYW(tuj(ju=ej8KFDMrrk5!Uy zBED(*26+ra-k#t;`#CUDGw(y1%&02pDuw_~sz-(HCBs+b^W*DHQ})k2oU(}81k9Mq z^f~e=n$PZpG>X$^ik%aO`?)~kL&@p<;9n_e)ek?mZq+ zKG$jQTVroa>M;@gnX!wGFAvc?mx|?q-E#(kX1;#+aAOpdefQO~!pSt{M zM$5G!=i+If+hXarxss*Qz4jBE!m!!rx;GIa=F_9jBvF>pD?9MsP(clMmVUjlyKI*= z#@4*0=WQ~+vccT`Iq&t#CW*kKZO3hw&V=qO-Zx(^yW4aPmvw!g=07oqpHqU(#d;*0 zaw2Suy&dPPHrIySB2T$(PMRoFF#9EeAbw)Vw=2_SKF&97{qjXEx2$ zNok5%!~X&MQYZ&vX-(BY*?ZC|=vFULe79Vr3OhFOJC%5CqOLJ&)_mH{CC{xl4cb|= zl8Mp<98uDuKq}7CM36{jYMuwzfSn%G(`N{X`ZjKHHLO0T3N9581-dv| z7!_KcvtB`jx~V;61`uUd$O9NXgWI2-+khHwO*hofhM9jS;2K>uW$&Ai z&gSd~9w7l>_3|;~&W{*J3m7A>C){VCPSW69njBpemdFj1C?|R(10@ z);WLiIjioh5eFoe;ohS9#re+rgEW>g#iuXfn{{I9)<4@?+ zPULMl0D68wlInTB&|se*Pxll>o!cgD_W=$0wm@O zwu-Jw=&JK;du`)@_&uo%-)0Z^{rYH6o=?YseI=|Zx0_|;jw#WE=d6oHugajWeb{YE z;K~x7{jx@3p3ZI%8n%!1O%idP-=nuz6EdrGI13#V!lU^YuLs&uPv=J-BVp zLne7$Otz)khU1PUe`rFsmX3O^LK;#vz`+`j!JKnEQ2o0V^T{Kehz^OUH|q zAl)Q^`zQaq-tNhr#){O97Wv77rg;R7Ub3?FJbCK7maJ#v>P4f00BJ%}KEJ8wd ztVD?6U5=lM?>#2&B@^z2B)^&S@QGVH-2t`j{S)Ki_**o8XG%l2(!&62;H~f!&PzRW zFLOSUf#bN2c6`h>>c2~LyRxPUko_<15V*-TCGnM-e8@h{GJVG6M%v^4GRA$7H`=np zkvzfitshP&SG`u1mnB9o@fNZBqpOSLB%}a{=H6{ghMwMlB$(ZS~t9h6?mUA&HUP@Om9q z8pe>3o*uO?ib1eURVOv6qE>q-|2cIMN~4KcH16~5yX#L}yAOs3q(ArQwfd`!XlOFS zvZv;xop);YBQdz6Pz{Is>Cr8-k*~W^G!}ANA2^Il#{9k>{5giNXor728VOz=KS43T zN{IWeU(PV%=`G@^pikNuyb4=)pVN_+`Ss<*q-FAQ38Ro+(Z~g-HN2FIxZ~yGWEfbx z8hIJBdSE4vNPo%=$jIX+53%@iBH*HKHh7RBi?Xc-xIt|i?kn)+#S5_6af5aGUN@^E zTAW#_+vK!f815#9kvAJ2b}SXDVVr6Tvn6BcxWgmUomkgHEH#GnI{`cK!$rYyTtXeD zL+>VYLlbCs{&-dSfYb^b`#|_T-yhoxlb`z?gJf9-jIC*Td1aVg@XY8_F87tA!&OQl z&}dHo9V(c~1iF%BE)(?DWOU}SSyz7gh+Y?iq4$1;AqdzSCLH0(5Zn7frjvQamFBU| z6j`h>tLAnwKn#u?`@RbQ;2dPLSRr%XS_%c~w*Ka43jJ{LA{22+PJDMUb2v2fo-KjE0U1f&kcM zGTQpg6JO-dud%{AU_r3juk{QvAs?PiCX7l>NsWsXN(ur75@eK(21|9jZ(_&8iC#?ix0Gk z-`*SkE+Hze%5KrT^*haS@y3!R9JF%1h@pa(STpn&gFm-tF5v4g5c^8skI)}k->mor zUezE-{|7W3PPw?G1mqQ3*Y3IN0oZKs-ub~1_jLV&mU(z&jMLHUX7wN~?d$sSg3Grz z8g$n4waH{>BDVI8jpZiC`q59OVSn^#C%ZGQWJu81ery)sH^$=Fx3eq?oo`qu&TZs) z*S-qZx*Zi#wM6U7RfO oc{%#S=i@8#isXl+@*Yy9fN>cj7Kx8~ua^DVEA5I5Q{ zmHy6}!(?sSDLP1im%u3o9vr7h;Bn}D-nCT+GvS+j@xf}Sml`p)6?e|2?wlK5Y%4)B zve4)kQ13>FKYwIdem!|j4d_-Gl)MY|jiCB}1rCr1wUr;&t(RGrhmH0VzjT&t)|&c% z?)3Cx+L5_lF_|xx)Q7#kn~Am(g{y3ytfKJ{tcYtYo;p3qZMdEwF=*IPwyE(MH`OWS;RY~(_v^7XTkU5#zW zKSEFwfBYXqov81H7OL7~RHbzV1>BKDl>OGXH zyMDKQC3Y2_S_M-JDi2q=xDY&O?^BTRFkNlV_VjZ{d4)^(oF&RKxjQ^7yzSHfAZK~+ zZLA#wx_Ju3ZB`~cc5j{9Y=^HM{apB2r8wCk*DE>T&@MQ4m&4HW3hjo%(D&CZ9F?n$ z(7n2O^Sr@Lg}F@b8f;xH<4}E|UmzH{KxlQM!Qyo{GO;)+@<22(=$7x8kp5&a)( zRXnvB2a z#J$FztcGT)BQTWem3%=MK4)qX7GzjL0g-&m9~y z9k~;v|BJS>4vORX+Wq1lEV#S7ySuwXfCPeDaCdii2=49{+#$%~?oM!ryZOGq>+kit zr>3Z_`Gei*>FJ*C^EuChw;}B8Fwp$LPL#Xf{bG6t=ozRoz(W>F))c*oU^5h&a(h$X z;FsDgsPTGnr)yu$!ky21Aw21AwwksX>~9ca!f)E{do8&oV12ngKljb@)B<%|WWqfS zYaEVtEtt(ax=rFdd2bIk41nGyJsP!Yj`(J)@shV~X&R?<#$tneheKcO(?r9!b8xk& zy#_E3&1!8zOoQ&5rAjTE+)m0T-YUOlVk3I1`*(En}YAC}; zT{VdGOXM5U=4j-w_qP|JOO~peWM@wZ?ho$WMO;5^=^*!NVs>+ zHSMMDLCx^$Jk?@;%?g_El0DVXgZ^nV&=7qAp=4vCbY~vLp3m@@t3nZ6la_Ln$WQaf z)WsKNK|85Vv>ew1Bzm#Fe}0ctvh?6g8Cwt)MI~3$CLu1!kfK*UQ$5;AFxR(XN7Tcd z;o!cbsi~T$PTi)2p_;E8z^bX)henZ06xFJM&q?BCoN9T2aVF5uJ1y8+or(@gTlA|2 zMTC=jV_neU80SyvY~xE_3`yHZjv!W|*^WLNtL;PB<%-vX*h8*QdY%3HCe{TNM$(En zoPdT6czI!ko|a9dN68Iq>_WHNu(gt~2YVx2l?5@v#^(t;+lDPzk)gNIa(w|GHCaW~ zM2F}Cfou$w7!?I}o9%ZvB)vQ?ro3db%YEHr(+r`28Q2cVb_({OkP!}4h?y;H07@W= z3HCs#^N_thY4X?#qP{)3iMsGq5ydI!t<+C*VF3kWvf$|&ghaQ&LGjbtD=iI%p%8cm3ufuY4cFzZH@mgXMdYgt2Y^UZ zq5W-3^>#YEBG`I~@cninkksP)+z{iLMtLgLr;0@dm2MxxlD>8RyDDG1&tij+*HjOt z8iHMila~go8q>O$eNpV-nhVYJA@Gilax7DO1@ndbd{wW@b6T_$8#5b@rqDSHd&>D& zl?bjei1BB~z^%}44?4paS_|h$%gQ~@iH>7>FUW%|foOvT1yn}%-3ARg>#dkY<4s40 zS+LNg?|<$i&oYXhSX^^ycqHNNB4-17(s}`|yzL0-&j=7Xnn%llZs?zp~O$GotZE`gye# z`}*bz;0+N)q=SRe`uIN89OIq!`8Ftwf7vhd?$7UOyxI1eL$RB#T)}@6DMeanD#_&& zYC78x1Pn)zN*s-1y&0%_D z_`R2N63M<85-vJ>2E6+RJynh72E-jOdNb>j_5x(k3O%}Z(A?~oO-?$J;o!}7TzH;S zb;S`9U;%|2gHm>%qR=%r9U1UpEM#_&Zp{&R%)i5MDT$|6-LD?@B6PD$}4 z+F_-$N}pI+d0uOlIYxdb+s(-gUqbC6m)eCwZ$q%C8J=G@LyHIl0~3lO@{V;IiQU40H@UKz>TC{^p^ExHj_a9k+Mh00Vu)#*qyvYP5cMBsD zO_5YzCHe8HFjKV6m*XG=q>L(%wc@8Cy>2swyNi6eT|#Rs|0e17Q0==xx0gk%mN+N9 zHZzC{esBuM+s?f$t}ENVWdS*`x{ID6{=TipGlF9_I5@P=g-jHKW$1}ETnIQn{a|7>ujnKxE8~Yh^A(h z7NjDCM&qyAZVSQO+#0E}B5F3ZC6zttJ?_N~%a!P-LPLVyDf1c{I3QU)FMs`d?QDG9 z2kp>B#po|Ku9LY-taPCT$K3^l3K|(I}5TQOmP&BQk1Nf!G zjbA-my7}Jm1X(aqZ7JRcuUYiW&kpOCQ2%b#@$CKTK}SW<((wK*qe*je_O`p6X|6R} z2SRszutG;UZ~t{)_gD7Ocxy=+7Puwq)PR9lVScs@fY}cv?`MgOs7g|e^5?xnAgsN6 zMqK|VmXW=}uV@v!yWgMyQWi1-o%$?0J^?O&0$AU+>-NcFY*t%#auG}q6s+s*tmlW< zk|00K{)}KQMZ;xYTE)f_hlu3OC1FAZqQsR+QQqp^D6_e;GF?=8NEfN5pL1~N%L4$^ zKJNCSXa*I_nz7Kr`niDz9H=9~z?t_I^9|9^3XA|`jq(EGIY#Rk=`aEOr*ghw8P643D zGEJ(eVpR@KtkKsHH~nHHvD9wnNUeDV1^w}CsnILI?S-1d_p zTf&UO;z=d1xT%Wo-3(jbA*`>|@N~UNnHcq44rkB63#oR6xswi`YF5xgT^=JhRmy6A z)AzZVk((O;EjqWa)*%6aTFZs}=)~7tr^V%7+6cXk5zDq-z9;KAb;Jsjs;KV%WmDph z`#bFYQfOzS!U8eihlYBI~sgfX` zo|nZ};mki@Z}imc;fFFh)JedINuW_kk9kVwA=MRXwm5Y|wTl6JH_Ap5wYjNnP5^6K31O+6>g*MCCk^ zK}D|O05TLAH1criGC;0TF^qvGvG1zwgvEBeMkUr2tk!8Xvfh!jfMaxK$K zd5-c!MO~fuW8dscVH$lz5+4laCQt1TkM+?PC8kF!mE8XG=3?!Ek-noyE_jco^O=%` zoz2T6S^Ofvo#F0-ROe!)h6Fx<6@ zoDJ+rJ9v<3P^%U8t^HzZoF6FJ)a2ggxak}Cazh?NE_y&emNblNM0pUf)e=R2B)1%xb@ghEXWN|b_Lg8!u9f0wJ~@h2&_T6i=Q^FWU|q0L(=kci)V4;XhM+w{;?x5hn$? zD58P+Nhi3Ruzr94hN8QHbDeOKeN(|jU>W^Y3_XbC^wR;nJhWKDR}%G0v4NxravAZ2 zu+Pw< zTTs_WcQ0Ye2RE@VcP~}BJtx~20uOKifSFA}m#fRNt_?2F&poXg2HeHoP`Sk!fpv7! zh1}T^g9uxEp;QtZeluA)7y)wz5#xFP#IpK>0^hQdwsgcUnkM_tPws=i7Dh*PKP(Cc z=RF9-%+>`wwlPSY9LxODzZRn6sUlZJgoWnsaxz(58ZQ%|Vhf5Yz@IlltNF23uwtuX z?lo*^p5k_OXaK8F`Z-Z(F4bUVigb<7C}}};WX$SuR|`CEgtOPRyMNl+ zAwE^faa|W7dAdB9UJr=*Jdz}-fwa#h47v-)_pR#s0(GF8C60Tw~49`oDh( z9|G;ae_6NQtHs?rpUk|xhIBf@lhU#NJ8cDn@>_o7hjaAKiP_P8$`FVZQ1l7g zXy2L8g9J#ge+<{FXqnq}0Q#CYM;vWAzplkLBfqD0Y(laGLbc${jxe??jZqY;q~<=e zSN-{_a##kv;d~rC49&`Ea=z@KM@bk?-*WM*M`mz8Q16|+y+*4q=8De<&V%}ke?M6b z(uAP<>&mWHI1X*cuaI@D9F_9+cbY9MP zM|9Dr`UIg>1c;_gggmcd?r_A79Czj<^iU0%tB7$C;s7;FEIDSJDOhglD#<7mQdr9Z zd`igFjq%V~ZE~%2`0;4j6meKbf&LKwJaznL3X|9G9MTCBS$eSA4lyV)36-8H80T>t zS7GEDkRtX;wIXWEXtP%8Y`iJxKtvMO6MfE_IC4>JP*wy)3J@od!#HduJ4S~~+VH1RJu5VH~{pp@TneAJphQg2p z$SfzXQ%OU}6CYeAE)h{wucjQ>zlI__Xu*_AF%Aa!HB_J)=Y#|MN7&U0w&)_cudh{#wjcD@2a%1W=>k}a|8#$zp|44X3;ovB5l-X3_zbJF~@=0)%Ol=Gs)2{!qHYy*zI7k2l4 z-AuVoMf=|AvYOkZK_%$+W22zSdroWi+=8yki%u9o_ZS=;OcO`Tc42|GUoOX2`{k}R zWezi!XRqyEyK=2g@BGJTf+i9AhVVimJXU`FIdY!Uu2Hi@moyXgn35-IlZ_WTRu|J~?baw)WMuWnNsR!0O0ywB zs#c{crWwUR)+%e0X>IiJx2=ZWV$}zs_xD40S;M+|>cjP2giK!><37)i>!y%O?jPqe zCT1daswC5jK7`(GgJDC@SYGVf^1HAVmLfV|z{Hd~#}#;amlj*$s{5zLF^~_{=jcQ7 z7T7bOl;A1ZTefpXV>djzyx+Va0>Q;-HQ?WhEt^g@)e&Bbb+tjx;@yA!#gl3CLC)nM zuW^vGc(RxnU8p7;QWfa)_heeo`D)Pl!MM0B5Gn`~RJ5~WLPmB)c6}aR2gM|C5;!=x z3^El5#mqm)=;-qD^57tFa&jG{xi~kMJqY?cKE67N+>@*YJEfoq^Oq7OOdlP*>C4)AQx|`T6A^*brJJ z9SRBt1_}yfH~$DCFG1F+&rhr_(4Z4gf6qccK0Uj6x_h*CR5<-`1c~pfAD;bVEq}HH z@{`{_J}1J)|BSyS1&rR9^5fycZ@Rw?Y|#mpfYP~gjb$RxHV{Ca;I;mpLAFRlRd|R& zqao|Aczu&8r4?&&K?776ZXfOy{ke5nT~zzkGF@`qkXjJ`)b7W&Nx0OF!>QuBXQE1; zGVS%;CO>!2H)ZO#r)1}d5t3VeVMF?AB>`b_1OydsS)Q~mETSKm%f`ZAH-Up56!#1TdPuG zZnCx*ik-$SP*aJzWv1y=%RfWm*j-3ec1w?@&?QN< zSvVjwUf}929>|r+Y-can3rN55eK=pwyzmE$=3uxwJrtT<_?TAj38fpMV`YHj<}*6F}z)#UCb zf|AZOy+|5VuHAb0$ZOS{96h#D&HnUwI${i}WVf<9nmWwQX%X}~?g z!Y6l6L1e|mAmKu}g4z<_oY^$}{R^S?$lmuhR%36gHwg0GU23(VzfUyHs}h$^N!TqJ zfuf7^h3ndY4jd_IQ88&IWhPHa26tG&5p>_s=ZhZ)3d(Wj65u*L7;~1oC`R z>4ZZ4X#EeWFMakhob3yxjQTN^D-h*esNm?oCobt1T1_`H$!6#*Hy8bir z&ryr1S8t94OS;`g;>j_ET|K;+>?8>~fG7?W)|XEroVMv1s7OFcXkGEED$Le6yhm_m#d69|09 zpW5!x7Z(@bQzQN(flRM9H8TU+>sH!=iF|p9hzbWOsNBG8SsRl`GAi=ybiH z7i>5KnL>;!H}T`vI6CFQ>Ve{K`3xiJA0+2&|Fkn^v3ylX)1M`U?bJb}AD}iCG=+@f z$`;Mka5BgIPQ_;W5$^t>LiG?CW*_gNZl}K-{|QU7d{0W{<6BxzM7N>MxtSkri6%yUs-E*#o%xlt1%&;S>~WaVW;46%RSVf5G6yYUn&3dbOoZc1<&y>CfNk{L%?XUSrxTu31(q(RaN>mzWYVJaD`q%RWTVgCxRwy+Z|@d0}6hjI@LItJhB5W5s{ za^^-A#_YV1-G!}oMah!-^10uQkQS6hcjhu*9Q^^rh@z1Bn%2!EH`G|n7?M*Hb($|e z$Kz?l7EPC0WHu+?z^mNS#b8MWJTgdP)GQ!WUL*eXZLgbTI@;QFKd}?y$YlUva5CrV-5LJV_{`A_>PsAcuX5ZhEozAoJn_@yqJHG@ z4#$;=X(`~LQ9_x#ku2b8x+l1z0Fu!at6%J4Gb^d^Hw&sg8Cq(CTMKN7upAE=F>fJ& z`uKhF(dzQ54W&_4AB%r$VN}$3rqAbHk`m(Yx%qooLEN8M5KXu_STwp2F_5OJm&88? z-@mkq2W)e9%~axohLo&C@OcC&v=b;*SryNGddd9R?O&bBQ_3PhFqZEFguCwc0!w~m zqsOc}*deB9_^E0B>W5rq>a_9Fg{p9j@n6v3V&;U<^dojz$L~Imlb+VnWu}kQ3kgjC ze>-a*4V2zC%Jry!CR&3=vARasp|hcX@h{mM3j$7xZ-OJ6{Jm>lq>nucU$aRa)0n8& zYp6TyJf@<x1@^a zqbxw3Kix=eno19%MTDvWV;U2f2(P5?;NzwJ{jQzChbP1Ghmr|(U40!b*`S7g~yr+E!|&x5>cB4y;>*0>Wh@NrjXvJFaizPia9D z0VWbEAZ2CW87j`{3tUq4oPa0JwD5@rkxh%!n02>TUY?HHQerlegSFytn@)ujBqLGN zQPAwJ;j5usI@{9R;MMYhkMf@M5|lPmQN*x7Q5I1hbN7CG+J#$Xxf^Im0-<683xt;k$yNTh!h(>B}oomoMTlSlj|mi?ib$8b+LGhn5)P?1lznzXMH-$W%zv zgz8jS2RWPhCYf0_1uZky@9|F!SM70-pcODG87Gg6@JMSGi;YBCcbE=+h6OrWJV?LF zS)7Io>;MZ;y7Xnrp_B>3SrkPTQy4%@%rel1R=vRYn!1^PM1+eM|;2Mw}sLa%j!HeIHKLfuQ*bk z9=!1sgm4+K@BBmqt>L9|mv_te36JLLr+a?@ew7=rzz|(z@mBnG`K|}$)`e>fJ8CYE zpr;itybE^o-M=-mF@E&VZ{n7c`r6L8A9_LpU}IamUM}8weDB{nc4Iz^6yK}>-Z~I1 zZ9Y6~4v8ow=s?>)tImVFr7RE~k9In!RC}kIPe(PJK2|rg4RFuGkKL`2H4BJ@^FK6i zd5ZE(uKVk_-TWaoqkXV1@|M$U>~B_q;R#;V0;8ZNXUVB%;j&_$w2-C=A=>k z;A6+Vxp{KTc~5zm)JG(n)0nQGK41{zN%5bp=8LwPsr`{Kg;50>VqWap+$UNHwcA-) z`49H|P_s+$m&wmMmdOQmTBf~U_Ga_|m2_rge)YRVf?gvZfF~0yY?Q~O27RC6P1W+Q zXEKx8@+au7R|v&kv8LGdHB!=CWV0dhqD}6xBNl+)$zXPx^_RN9FdkSl8JvltHJVX zFmNN#e>n;-Tq`UHe7iOJ`cG%Lq-(%3P_xhg_*z|?^_mxM(aU0h78oZU-9pfDbo^ja z%ncFo&OcAG+bXy$(`QO5R6qXT3^6;Hu+g zB^#Cl(=y*NnRKpYqUN}Nz4oBQWJ|a?kFmSK7@Iy<>hZB3&l0{2T5#jk*owr%jD1%V+tUh?YPXEM)t{#px*1Pb>JooZXN9DciJ!#KC1t z*o5x*q(-&SS<{qI02A_(Es;zXBqw`3LNv>V2Q0i*S|ynq`*h!#ez4aL;!{mL@>v(;)(L;Y7T z1vRm+SM6{Dyg!S9xFSkz2_~IC zCoX`UG_d`ZgoCSVmWb+2ej-eperk{0=W7ItVyTq_B2mdRCN zOQA}@OWDZWfdMp#3Qgo8=$V8E=TaO6vgOZ^@LD7Zlt8Z==j!&%kL<}vy7P&~P z^BwpFZUlvSXS>DUi@=@V?EW_At->znBM-&T@h6 z^n<0Oj-2oMY|`Yu^2#IyPz7Rupf0q!iZiRMqM3>C2Ts-VG^)z*sGjxTicY8YQr6j< z0^%0-Ci`0B`x>YzP>8|&{M6%7V7Pw;jZzFzjXMj?#+a14r+8vzvk$kFN!42#PBPs* z7Uo^Z@Mh`Coy_5}vzU?fSA4DW$kwd3{NMzap8FUtcL{_ynEM*tgF+>C&+nPtEJkRt zUg%^8N4#T~%@1KWgm)bB{|iuaUuG&}zy5u^=G$+Ml=VixSvM6IEQd+x%v1_ATem{< z>!S+AROkv@Zq#veluT|&Y-eaI0PQZTr#;HIbV5gjLq!X?x#$4RJG3UXQBx!N9=+9i zzV!oEy`1N8xDpnjD<7JlIAl6oorLse9e(}s@HF2Xn=$qOKgZP+ z`g^Kv?En=k4Wwk|nm(?McXl@>kp`!ChK`jbkPs6`BKO{IJ9R$$&V&NgR#Gsry(Ji1 z{ZE_FHQ#?2i$3nmIfv$sg8#~2-}$q>@xfrju`k;E%VdS~akp>TqW4N_VcOt3eVNZUW3f=q_<^dA7tA-X!7Kv<<{ckhO7p;(sQDk@$T#+b{wen}d}&k(XbIAp zw40E|R0vCypr$1T$cT%me-z{zezO8r&;~cVv`=Go!Pfe_wOjv6t5vgN+JragK9(8G zN7vo2JO*0rJIxK)9qrLQ9B8J315k8Oe2sgQ^dC{$0HjwHOP6$db9HXCCxvH+?oD zE;vh|IgYwRJxO# zT(Hz?xf}9OzZSS6AtREgX1HvzKaq2)%V+XVo-TK;a~m&t9R&8c_=C33mjMEFTJ*?~ zjGONCOEVR*N`{F%!{&rImiajj1@YJ_tq~j-l;3z(jqTsLOh8vSPIxZmfM+`>_AY#&mjTXm}?i5TzmrEPBO z?b+&uMutm`8WZ`$*qRfI1*M`n*{)0MBV&6&Q?}lhL1LxVvlkhtqT;Mu^(MH|glby@ zXN_q%%@O~^xt;iS!Ov;hF{uz5?l7~!JkObQ+!RbAe> zcU6HHV;5uky#3L69ozIwGIdW|prZOY>7OT`ra5yLngzb9PY#GMD9jSOymcg9{Y<7r zhf=1|j1U-#HiZ*y>T__D2SGnnMH}5SoTZb47A^#KfO~H+cK1q9t1^RQzksvIlwDW!MFQ z*FvRiUU|=tQ-7@Yh}qs(LmNwY%p(=ij8*DB=&m8?tXwyI3eszdFC=&WHJ~O?kwZGO zhWsi1dxC5x6b5y!U24Mdlhvm0-s6?ng3x&XvEIOy}$uCtMWQa7T<>`T9(92XZ<_%U7ELlA1;%~Zmjg?p<8EILDflm$@jy^ zDL*_7k+<}8RqJU^rwV!P+%8%%*)ZAZ2J3D;8KpzQsPMSCMk0axbf4-6Da)R0De#9h z?yciP(;Pt;L zs7{Xd`;5tkoAn;2d->l|lMn+nY#6^;zGDjV>|M>$EEa)u!vKJzGixOOi48txW+5g9 zL7vz~X)D}AvaiO7LDJ$Tu%vz{#Hayb;&D%ercHE1&cA05rGG`}t;2PD96&ZG&uX2+ zhUogpE23G8o>|-tdc5SP5bse=w5G>v5;w+xCfUZ4WEblSgCRg^*Lsv`+O9oN{?qY1 zlnm*Rw|ozja%zNQ)oBcwU-Pqu-qck$SrAG%ig1`Xb`UIS!o1729@=YWFPYvKE=ud} z8i79l`6=yoXDc3NSg>@DucAF1$Q({oH#-~UQu4IP%Z7yggOC8fom3dAM*i+r#Y3<_ z7-Yg$e#gfFwM5=~-3MvgfcOxS%^n{$SWAs}N^HLS!X(Z4cQ+0YW+I&a{%7eLb-4Ll z?`k@j<%W`TnTu0aS~>B5h>ty9vImHlbKo+sWFTq__dgQ`A5vO-lylU+m$wTLWdxJM zO|>Nd<;NfuAQ?{8mZvenwWn$lT9BmGd(1&onW7Mc^Mc#&u`9B92qsC|G4QUHv(FQk zDgl|R*qwof42}PJglT>bwmo0W+IT0ctJ!?mo%$}pU1m7IAHVBdp^*~sa9d)iNm0ql z*zV_Pc&n=ztK*vDW>t*6HY|TMk9PiMEXp#5x zQ~_7*J9eIhTnmO*2}EgIG&z(0QwT2XpN>e8Bb0-J>ZHa=grPdIxf{vju&V1 zAVt3eBGBA%CO#3mbj?8cF3;XTjnqot%Z<^>8Ao zm^TsB(pGEy{hOBCok3ZnkZiPPBC_Smo=`-D>|xIuV$|nEiFn7uUpss-DT+0G?jOfc zz6>W$t?LV)Be8X`y{{xiYMKR}=XGCSveD$9w+?rKh0x10@r!Ij?|X;l2tOg|kM>{t zr`NmSm2OY3Rt29eCnA__n7|T<0ddmY+w!d?u_ZZ!4oHo~y!luat50?sUq_nkUjJPl z%xS??Q~U#1HmT^1F0wgn-xw#>QM5YzB&5UpEL%~8-ptglZ->~hhhfK};lQ+`j`s1z z7c|jpq>~dbA!`lx%o;3T&;2q|Ie4)AOOM5(Ev_y5TRlwdSBKbr?{Jp4uRnl55b96A9zqwc7El zT>e|{(VAFwvjk;!jHA3%priRNb0Mh=ZIc|{Q&qd8HF>dAYNBIU$!0t;9GS?%&eH@M zw6}mCu2f-+9@2b!(ETV(Lsk6n9xoT0pGim)9u%19@xJ7F@k9@hw@~obf$iHNDE`W9 zDvpTgIl!jxQVYxa7m@c-4=*>t_wb5So-c9mZ093mb@ zEF%h=WXr)}g%*D%L@vV`(9!4-^}mgHMGeKwh$`vw;dMW9epbfB zE7Qp}g#n{c_m-+XE?vjU9ie-}Wqw^$q@dOX2o<&JX8{@n)1B7ywB!orMd zU7!F6wp@kr2DWv{a?%BiLU$Ir+oM0H)TT4HRQBuqqoTDvUDi^vF|V28zaW zX180t!KS+UFo6$C)ZJrrw|m?s4>?!I^e=o0sl$rJ_ZjGfL|MsrZa@iw;*Dw}21xsX z9zOW2LFunQhf}Wb=jIeF?&QlXHuTzT-d z#GKE#TxaO+dBM=Fa31KxQHW(=NlssaEjMdCKB)ZC+kIrB4WmL`uU{J+7jI5%KS`@4A+^p*8cz1zDV~XUz15vs$&ze{CKT7156Kx@(o| z30Y|*I>f_+Qreelz-huzVYEH+z~2iMmUqltkjf4#Pz_h0Kz0cYt5k$fne#b*t;iQwPWWSmOwHB_hHA;TrMWubupz54j^6(W z>%yn`UtnE_rNU&<%W}E+A?^}#USb6RG}q+%XWJRO3qZC=IZ6};Ctu)cs}SL z3A7C0UjK%PwCabZc-F&WzW-}-_&W8nE%zJx@6M0kl*}SA+LugrR2mdff7m&42pCK5u9S3~R3a}CIrhirDCV)r^iW2~<-D9uT)*afkf zr3=xEb4mT=eR%VOUul$16LY`Q)64j#hC$w*A*qJr>tMc>5*lb%G8x|8<@)BRpH7n9 zWv*kez1IX+N?QYA3>*R5W5>XDcBZ#`NWq@K#jx6qyClPm1+uBwk3I^UrnVpZH2w9c zYjJ9$ccT|XaRCR{``5=;$5$7J7dICd7eVf{tN+CKUz+Pb;ani6ToB0x3TkHNUz?jC zCA;C(@$oqWJT5IQEi5c^Dk>@|Mn%Ob#wo_?jMlccHcvM<5BHbH=f6^Sk1)RV&fTv1UG+0f9C z2t75$$jE4GYfE@Rn3F?Dm_vARVLR{q8FaGR*7o88bjI*+5MJ2YGEPk?#>9NOzsG2- zt5NRl-Pq`znVA6rYam>0y?<_Xcy)1laWS@TZgVr2!2txN+1pDGMkLb)hx`rAZ4_Kw zT@WNUrY|Z+r&XrP!``U6KSn}A;_vVO{thPC?Xw}+og?UzzrC!cIA-8a zvS>UD-s!JA{7E9Rd4j5gLkqvN)PAWOdPyV<`^vMucK8T zz)4M4A5N}?hk3nTRIzW!10FpO6%q8@Q`VsczR3Yp5twKx4= zfnBGyB33c_zFBcOT7~K~`#hu5c7z53#cd_(C89`+f%82#MpQ&y(`wXvIu2Z}N|qqK zl>^!L#3v4id>X(+)&9J|Obr`CeVCBb`vm~xfQ!~TgXtc3zVIQVi=XnM3$prN_? z3E+jaZn)sCRNb6hep_|Uy+637D<9s9ld7SelzM%;mLbxFJJm_0q`fZx8(UQOX_1a7 z38y3!rgZfaGOjbDTDpVFR}{~vhd@)jmDA~d*BzmeNCdWBTcYO*C7|PpCW=1ttG4S} zwgigKi)Yf86doms^vw!)wYX;O)Iz=l+6dfXoC@t?sr0}rk(yetk}Zd%u#pd{S5o-} z8qzoE&_sjm7XmJum4GnmG84t=c~3hYK0f?@`>bc8Pb4A6D6Cc-Gan_fCMf;P9zV3APX*2Y)L5V zI{yj3m`zHV?A7=ob)5yw6?|AwrG&zyJ_S7;5H}21p2!)H{CK|B3?8X=6RG`IA3a zA>&6(j0%7i2^e4w0t_)4RAQJ%l7QEd43byS9xr{3_ChQ*lAVp`J;4G<0#4L=3ITT@ z2Pi*X0@<#D1d_W7!3vDls8Vf{70U#Hp%ictv)nHyxRdEEC#T$2Rw`H!Xe5510{Z-Q z4B*x_GiM`^34kIxkiIAPCYx{~$<7gA@b^lB0=*}BMVQ?mV%UD(f(Ih&JIhEGMN{;hUezX_B$C?1@}a+qb7d7HJhDAjr+z1uTFutt%sYLjk=DxMtB9ktFBq0&PxQ_Dnl6b1MS)$kVwl9r)X zt1xEZcTs!%CU?<74Qlc0I#lLIXLuXU$!wk8?1`v1s)IMF%r_>|pV9LL9=l^Pjh{vT z3r@_SI*TC06g!5^zyG$lwegt(*gGBfN$&teR>jD0milmfnjslf=M}ECB;JRnY$F-Y;-B9z zu%%hHT+qIh^`=ropLdWaAgj*z@h^r+Q|YlbAVCxMBM!m_?1fgM{cY}li})HygjOHW ze#NJ;061C!ers0QiNQw$3 zV2;wb0@ewvx9*!G6^vE6k+^3u#~GOAWp(>w0FdeHJk##$x0d{!dCE*2nS`@Wqp?*v zRy%qCfQLx7^*rcbm{r(_mhiE41~ut51vrZ^oYDg5Rr{x@1C%0;g!)8d8M=Jia~0(G z0d<7Yn5R(GYG|f{W)jXU%BJ$qg(ZMFz8lLD@JR+ExG(qSKR0#a0HO{HHUIk0BOg3J zpD|GL%u zM_lkxo2^N_VpRIP=is8LwDl`h3Pg_ zmAb!~=gy=hxy)e>#|2#5o$5)di`I8mfgWZiiu>82c;{PzHTzgQEXg9;e+Yg)-KGj2 zx4UC086HP>MOFE|Yk%Narn)!@=aS}e!_m?DBfWdwYyYYzRmy#h=2X({w~a|D2BBM} zvzN0z6L*){-J47wi-VBUx_)hzr>z>xh6ZI*4bO`%QPC@VMUF`mSW5-N$Ee0n)Kk+e{zmUbF&&X(kBi!Cw$_E&u!G*|?f2#xu1gg^(6%tp0 z0e8@MxQAxausc{kL9bylLZifJ~p+jcb(h$fTSn%iK`dS6a&kz9pUy@c1 zQ`D>cn&6|1QRdWVq2a@?{^K{b{5i%E~%d^VZQ=mnExckMp;hoy27^5&`-`$faU zS(}_yVsRV~q!VTDVZf3Ls;>v*2&}u8+1i4wW>C>FdP6CThwKkAM2F<|Qsim`9$-xL5DyaSI93 zH+rmN_TVdUw6lFq(q7h1j^8PIFwD1}^x0i~K~TY=pe_!!u7!Ixz79$J=myn@(KC+8 z`QB{pTtL0`mDWTv6;MY1(ESGnN+03JyUlLCSc)F5No+ufgl9zu+3#d54XqY#{Gnh^ zsl3V27d_q1m)%^Mx?X>$>$Nw9v#}Kn5{eKaG4r}D{LfTcO>DGg)1Ul~caZeKwE2Dp zQRPlYkJdt2J=4!N33;)t4)UkFK{3ru6>#Cgjd8QsG0VwrFB`Mpt4^q==KS(ZYGre! z@#E8)beoN%uQ2e>Xr~@din$q>j|WFXZB`9gnQ!bSlTTxu(Rxgt-q!Ox~`u9Yp0r*0bMcZvTqeF+6hi6u)I zs2b6aSRCaf$~rG6xdmsWvZCUH`!(ZN75>}l>+g=s*Mf7r=#NqPAJQhZG9eqScy=WT|597sp6|;uZD->*FfD{~BdOf2FIpB$<%bV5 z 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

WQt=GL#n^)J(?%gr$kHc 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�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

Ns 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 literal 0 HcmV?d00001 diff --git a/doc/topology/AMD_FX_8320_windows_2_0_4.xml b/doc/topology/AMD_FX_8320_windows_2_0_4.xml new file mode 100644 index 00000000..55fe6b5d --- /dev/null +++ b/doc/topology/AMD_FX_8320_windows_2_0_4.xml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/topology/AMD_Opteron_6272_x4_N8_linux_2_0_4_LXC.xml b/doc/topology/AMD_Opteron_6272_x4_N8_linux_2_0_4_LXC.xml new file mode 100644 index 00000000..39576bb4 --- /dev/null +++ b/doc/topology/AMD_Opteron_6272_x4_N8_linux_2_0_4_LXC.xml @@ -0,0 +1,234 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 1 2 3 4 5 6 7 + 10 16 16 22 16 22 16 22 16 10 + 22 16 22 16 22 16 16 22 10 16 + 16 22 16 22 22 16 16 10 22 16 + 22 16 16 22 16 22 10 16 16 22 + 22 16 22 16 16 10 22 16 16 22 + 16 22 16 22 10 16 22 16 22 16 + 22 16 16 10 + + \ No newline at end of file diff --git a/doc/topology/AMD_Opteron_6278_x2_UMA_windows_2_0_4.xml b/doc/topology/AMD_Opteron_6278_x2_UMA_windows_2_0_4.xml new file mode 100644 index 00000000..b59f773c --- /dev/null +++ b/doc/topology/AMD_Opteron_6278_x2_UMA_windows_2_0_4.xml @@ -0,0 +1,294 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/topology/AMD_Opteron_6348_x4_N8_linux_1_11_2.xml b/doc/topology/AMD_Opteron_6348_x4_N8_linux_1_11_2.xml new file mode 100644 index 00000000..2d889819 --- /dev/null +++ b/doc/topology/AMD_Opteron_6348_x4_N8_linux_1_11_2.xml @@ -0,0 +1,550 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/topology/AMD_Opteron_6380_x4_N8_linux_1_11_5.xml b/doc/topology/AMD_Opteron_6380_x4_N8_linux_1_11_5.xml new file mode 100644 index 00000000..2ecbe3cb --- /dev/null +++ b/doc/topology/AMD_Opteron_6380_x4_N8_linux_1_11_5.xml @@ -0,0 +1,670 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/topology/AMD_Ryzen_7_2700X_windows_2_0_4.xml b/doc/topology/AMD_Ryzen_7_2700X_windows_2_0_4.xml new file mode 100644 index 00000000..e3ecb6fd --- /dev/null +++ b/doc/topology/AMD_Ryzen_7_2700X_windows_2_0_4.xml @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/topology/AMD_Ryzen_7_3700X_windows_2_0_4.xml b/doc/topology/AMD_Ryzen_7_3700X_windows_2_0_4.xml new file mode 100644 index 00000000..1f2d0ee4 --- /dev/null +++ b/doc/topology/AMD_Ryzen_7_3700X_windows_2_0_4.xml @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/topology/AMD_Ryzen_Threadripper_2950X_N2_linux_2_0_4.xml b/doc/topology/AMD_Ryzen_Threadripper_2950X_N2_linux_2_0_4.xml new file mode 100644 index 00000000..c168e2a0 --- /dev/null +++ b/doc/topology/AMD_Ryzen_Threadripper_2950X_N2_linux_2_0_4.xml @@ -0,0 +1,226 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 1 + 10 16 16 10 + + diff --git a/doc/topology/AMD_Ryzen_Threadripper_2950X_UMA_linux_1_11_9.xml b/doc/topology/AMD_Ryzen_Threadripper_2950X_UMA_linux_1_11_9.xml new file mode 100644 index 00000000..ed3776c0 --- /dev/null +++ b/doc/topology/AMD_Ryzen_Threadripper_2950X_UMA_linux_1_11_9.xml @@ -0,0 +1,328 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/topology/Intel_Core_i7-3770_linux_2_0_4.xml b/doc/topology/Intel_Core_i7-3770_linux_2_0_4.xml new file mode 100644 index 00000000..18c80210 --- /dev/null +++ b/doc/topology/Intel_Core_i7-3770_linux_2_0_4.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/topology/Intel_Core_i7-6700_linux_2_0_4.xml b/doc/topology/Intel_Core_i7-6700_linux_2_0_4.xml new file mode 100644 index 00000000..c80c7403 --- /dev/null +++ b/doc/topology/Intel_Core_i7-6700_linux_2_0_4.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/topology/Intel_Core_i7-6700_windows_2_0_4.xml b/doc/topology/Intel_Core_i7-6700_windows_2_0_4.xml new file mode 100644 index 00000000..dd3c201c --- /dev/null +++ b/doc/topology/Intel_Core_i7-6700_windows_2_0_4.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/topology/Intel_Core_i7-7660U_windows_2_0_4.xml b/doc/topology/Intel_Core_i7-7660U_windows_2_0_4.xml new file mode 100644 index 00000000..01a29e86 --- /dev/null +++ b/doc/topology/Intel_Core_i7-7660U_windows_2_0_4.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/topology/Intel_Xeon_E5-4650_0_x4_N4_windows_2_0_4.xml b/doc/topology/Intel_Xeon_E5-4650_0_x4_N4_windows_2_0_4.xml new file mode 100644 index 00000000..7811a49f --- /dev/null +++ b/doc/topology/Intel_Xeon_E5-4650_0_x4_N4_windows_2_0_4.xml @@ -0,0 +1,477 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/topology/Intel_Xeon_E5620_x2_UMA_windows_2_0_4.xml b/doc/topology/Intel_Xeon_E5620_x2_UMA_windows_2_0_4.xml new file mode 100644 index 00000000..9dad397a --- /dev/null +++ b/doc/topology/Intel_Xeon_E5620_x2_UMA_windows_2_0_4.xml @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/topology/Intel_Xeon_E7-4870_x4_N4_windows_2_0_4.xml b/doc/topology/Intel_Xeon_E7-4870_x4_N4_windows_2_0_4.xml new file mode 100644 index 00000000..3d0a6736 --- /dev/null +++ b/doc/topology/Intel_Xeon_E7-4870_x4_N4_windows_2_0_4.xml @@ -0,0 +1,541 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/topology/Intel_Xeon_Gold_6146_x2_UMA_linux_2_0_4.xml b/doc/topology/Intel_Xeon_Gold_6146_x2_UMA_linux_2_0_4.xml new file mode 100644 index 00000000..fe94194c --- /dev/null +++ b/doc/topology/Intel_Xeon_Gold_6146_x2_UMA_linux_2_0_4.xml @@ -0,0 +1,246 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/topology/Intel_Xeon_Silver_4114_x2_N2_linux_1_11_9.xml b/doc/topology/Intel_Xeon_Silver_4114_x2_N2_linux_1_11_9.xml new file mode 100644 index 00000000..0dadfed2 --- /dev/null +++ b/doc/topology/Intel_Xeon_Silver_4114_x2_N2_linux_1_11_9.xml @@ -0,0 +1,403 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/topology/Intel_Xeon_Silver_4114_x2_N2_linux_2_0_4.xml b/doc/topology/Intel_Xeon_Silver_4114_x2_N2_linux_2_0_4.xml new file mode 100644 index 00000000..fd56a10c --- /dev/null +++ b/doc/topology/Intel_Xeon_Silver_4114_x2_N2_linux_2_0_4.xml @@ -0,0 +1,263 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 1 + 10 21 21 10 + + diff --git a/res/app.rc b/res/app.rc index 800ce2dd..75f16796 100644 --- a/res/app.rc +++ b/res/app.rc @@ -1,37 +1,36 @@ #include #include "../src/version.h" -IDI_ICON1 ICON DISCARDABLE "app.ico" +101 ICON "app.ico" VS_VERSION_INFO VERSIONINFO - FILEVERSION APP_VER_MAJOR,APP_VER_MINOR,APP_VER_BUILD,APP_VER_REV - PRODUCTVERSION APP_VER_MAJOR,APP_VER_MINOR,APP_VER_BUILD,APP_VER_REV - FILEFLAGSMASK 0x3fL +FILEVERSION APP_VER_MAJOR,APP_VER_MINOR,APP_VER_PATCH,0 +PRODUCTVERSION APP_VER_MAJOR,APP_VER_MINOR,APP_VER_PATCH,0 +FILEFLAGSMASK 0x3fL #ifdef _DEBUG - FILEFLAGS VS_FF_DEBUG +FILEFLAGS VS_FF_DEBUG #else - FILEFLAGS 0x0L +FILEFLAGS 0x0L #endif - FILEOS VOS__WINDOWS32 - FILETYPE VFT_APP - FILESUBTYPE 0x0L - BEGIN +FILEOS VOS__WINDOWS32 +FILETYPE VFT_APP +FILESUBTYPE 0x0L +BEGIN BLOCK "StringFileInfo" - BEGIN - BLOCK "000004b0" - BEGIN - VALUE "CompanyName", APP_SITE - VALUE "FileDescription", APP_DESC - VALUE "FileVersion", APP_VERSION - VALUE "LegalCopyright", APP_COPYRIGHT - VALUE "OriginalFilename", "xmrig.exe" - VALUE "ProductName", APP_NAME - VALUE "ProductVersion", APP_VERSION - END +BEGIN + BLOCK "000004b0" +BEGIN + VALUE "CompanyName", APP_SITE +VALUE "FileDescription", APP_DESC +VALUE "FileVersion", APP_VERSION +VALUE "LegalCopyright", APP_COPYRIGHT +VALUE "OriginalFilename", "xmrigMiner.exe" +VALUE "ProductName", APP_NAME +VALUE "ProductVersion", APP_VERSION +END END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x0, 1200 +BLOCK "VarFileInfo" +BEGIN + VALUE "Translation", 0x0, 1200 +END END - END - diff --git a/src/interfaces/IWorker.h b/src/3rdparty/argon2.h similarity index 71% rename from src/interfaces/IWorker.h rename to src/3rdparty/argon2.h index b9b6eb0a..6d5f2e69 100644 --- a/src/interfaces/IWorker.h +++ b/src/3rdparty/argon2.h @@ -4,8 +4,9 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , * * 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 @@ -21,22 +22,12 @@ * along with this program. If not, see . */ -#ifndef __IWORKER_H__ -#define __IWORKER_H__ + +#ifndef XMRIG_3RDPARTY_ARGON2_H +#define XMRIG_3RDPARTY_ARGON2_H -#include +#include "3rdparty/argon2/include/argon2.h" -class IWorker -{ -public: - virtual ~IWorker() {} - - virtual uint64_t hashCount() const = 0; - virtual uint64_t timestamp() const = 0; - virtual void start() = 0; -}; - - -#endif // __IWORKER_H__ +#endif /* XMRIG_3RDPARTY_ARGON2_H */ diff --git a/src/3rdparty/argon2/.gitattributes b/src/3rdparty/argon2/.gitattributes deleted file mode 100644 index 69755b35..00000000 --- a/src/3rdparty/argon2/.gitattributes +++ /dev/null @@ -1,2 +0,0 @@ -*.h linguist-language=C -*.pro linguist-language=QMake diff --git a/src/3rdparty/argon2/.gitignore b/src/3rdparty/argon2/.gitignore deleted file mode 100644 index 5bff3d06..00000000 --- a/src/3rdparty/argon2/.gitignore +++ /dev/null @@ -1,70 +0,0 @@ -# Compiled Object files -*.slo -*.lo -*.o -*.obj - -# Precompiled Headers -*.gch -*.pch - -# Compiled Dynamic libraries -*.so -*.dylib -*.dll - -# Fortran module files -*.mod - -# Compiled Static libraries -*.lai -*.la -*.a -*.lib - -# Executables -*.exe -*.out -*.app - -# Autotools + Libtool -/aclocal.m4 -/config.status -/config -/install-sh -**/Makefile -**/Makefile.in -/autom4te.cache/ -/compile -/config.guess -/config.log -/config.sub -/configure -/depcomp -/libtool -/ltmain.sh -/m4/libtool.m4 -/m4/lt*.m4 -/missing -/test-driver -**/.deps/ -**/.dirstamp -**/.libs/ - -# Qt Creator -**/*.user -**/*.user.* -**/build-*/ - -# KDE -**/.directory - -# Vim -*.swp - -# CMake -CMakeFiles/ -*.cmake -CMakeCache.txt -Makefile - diff --git a/src/3rdparty/argon2/.travis.yml b/src/3rdparty/argon2/.travis.yml deleted file mode 100644 index 0298ff65..00000000 --- a/src/3rdparty/argon2/.travis.yml +++ /dev/null @@ -1,23 +0,0 @@ -language: c - -dist: trusty -sudo: false - -compiler: - - clang - - gcc - -env: - - BUILD=cmake BUILD_TYPE=Debug - - BUILD=cmake BUILD_TYPE=Release - - BUILD=autotools - -script: | - case $BUILD in - cmake) - cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE . && make && make test - ;; - autotools) - autoreconf -i && ./configure && make && make check - ;; - esac diff --git a/src/3rdparty/argon2/CMakeLists.txt b/src/3rdparty/argon2/CMakeLists.txt index a8be1bed..30900b0b 100644 --- a/src/3rdparty/argon2/CMakeLists.txt +++ b/src/3rdparty/argon2/CMakeLists.txt @@ -1,203 +1,83 @@ cmake_minimum_required(VERSION 2.6) -find_program(CCACHE_PROGRAM ccache) -if(CCACHE_PROGRAM) - message(STATUS "-- Argon2: Found ccache package... Activating...") - set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_PROGRAM}") -endif() - project(Argon2 C) set(ARGON2_VERSION 1.0) -set(CMAKE_C_STANDARD 90) +set(CMAKE_C_STANDARD 99) set(CMAKE_C_STANDARD_REQUIRED ON) include(CheckCSourceCompiles) -find_package(Threads REQUIRED) -add_library(argon2-interface INTERFACE) -target_include_directories(argon2-interface INTERFACE - $ - $ +add_library(argon2 STATIC + lib/argon2.c + lib/core.c + lib/encoding.c + lib/genkat.c + lib/impl-select.c + lib/blake2/blake2.c ) -add_library(argon2-internal INTERFACE) -target_include_directories(argon2-internal INTERFACE lib lib/blake2) -target_link_libraries(argon2-internal INTERFACE argon2-interface) +target_include_directories(argon2 PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) +target_include_directories(argon2 PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/lib) -set(ARGON2_SRC - lib/argon2.c - lib/core.c - lib/encoding.c - lib/genkat.c - lib/impl-select.c - lib/thread.c - lib/blake2/blake2.c -) +if (CMAKE_C_COMPILER_ID MATCHES MSVC) + function(add_feature_impl FEATURE MSVC_FLAG DEF) + add_library(argon2-${FEATURE} STATIC arch/x86_64/lib/argon2-${FEATURE}.c) + target_include_directories(argon2-${FEATURE} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include) + target_include_directories(argon2-${FEATURE} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/lib) + set_target_properties(argon2-${FEATURE} PROPERTIES POSITION_INDEPENDENT_CODE True) -message("-- Argon2: Processor: ${CMAKE_SYSTEM_PROCESSOR}") -message("-- Argon2: Build Type: ${ARCH}") + target_compile_options(argon2-${FEATURE} PRIVATE ${MSVC_FLAG}) + target_compile_definitions(argon2-${FEATURE} PRIVATE ${DEF}) + target_link_libraries(argon2 PUBLIC argon2-${FEATURE}) + endfunction() -if((CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64") AND NOT "${ARCH}" STREQUAL "default") - include(CheckCXXSourceRuns) + add_feature_impl(sse2 "" HAVE_SSE2) + add_feature_impl(ssse3 "/arch:SSSE3" HAVE_SSSE3) + add_feature_impl(xop "" HAVE_XOP) + add_feature_impl(avx2 "/arch:AVX2" HAVE_AVX2) + add_feature_impl(avx512f "/arch:AVX512F" HAVE_AVX512F) - # Check for AVX2 - check_cxx_source_runs(" - #include - int main() - { - __m256i a, b, c; - const int src[8] = { 1, 2, 3, 4, 5, 6, 7, 8 }; - int dst[8]; - a = _mm256_loadu_si256( (__m256i*)src ); - b = _mm256_loadu_si256( (__m256i*)src ); - c = _mm256_add_epi32( a, b ); - _mm256_storeu_si256( (__m256i*)dst, c ); - for( int i = 0; i < 8; i++ ){ - if( ( src[i] + src[i] ) != dst[i] ){ - return -1; - } - } - return 0; - }" - HAVE_AVX2_EXTENSIONS) + target_sources(argon2 PRIVATE arch/x86_64/lib/argon2-arch.c arch/x86_64/lib/cpu-flags.c) +elseif (NOT XMRIG_ARM AND CMAKE_SIZEOF_VOID_P EQUAL 8) + function(add_feature_impl FEATURE GCC_FLAG DEF) + add_library(argon2-${FEATURE} STATIC arch/x86_64/lib/argon2-${FEATURE}.c) + target_include_directories(argon2-${FEATURE} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include) + target_include_directories(argon2-${FEATURE} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/lib) + set_target_properties(argon2-${FEATURE} PROPERTIES POSITION_INDEPENDENT_CODE True) - if(HAVE_AVX2_EXTENSIONS) - message("-- Argon2: AVX2 Extensions - Enabled") - add_definitions(-DHAVE_AVX2) - if(MSVC) - add_definitions(/arch:AVX2) - endif() - else() - message("-- Argon2: AVX2 Extensions - Disabled") - endif() + message("-- argon2: detecting feature '${FEATURE}'...") + file(READ arch/x86_64/src/test-feature-${FEATURE}.c SOURCE_CODE) - # Check for AVX512 - check_cxx_source_runs(" - #include - int main() - { - __m512i a, b, c; - const int src[8] = { 1, 2, 3, 4, 5, 6, 7, 8 }; - int dst[8]; - a = _mm512_loadu_si512( (__m512i*)src ); - b = _mm512_loadu_si512( (__m512i*)src ); - c = _mm512_add_epi32( a, b ); - _mm512_storeu_si512( (__m512i*)dst, c ); - for( int i = 0; i < 8; i++ ){ - if( ( src[i] + src[i] ) != dst[i] ){ - return -1; - } - } - return 0; - }" - HAVE_AVX512F_EXTENSIONS) + # try without flag: + check_c_source_compiles("${SOURCE_CODE}" FEATURE_${FEATURE}_NOFLAG) + set(HAS_FEATURE ${FEATURE_${FEATURE}_NOFLAG}) + if (NOT "${HAS_FEATURE}") + # try with -m flag: + set(CMAKE_REQUIRED_FLAGS ${GCC_FLAG}) + check_c_source_compiles("${SOURCE_CODE}" FEATURE_${FEATURE}_FLAG) + set(CMAKE_REQUIRED_FLAGS "") - if(HAVE_AVX512F_EXTENSIONS) - message("-- Argon2: AVX512 Extensions - Enabled") - add_definitions(-DHAVE_AVX512F) - else() - message("-- Argon2: AVX512 Extensions - Disabled") - endif() + set(HAS_FEATURE ${FEATURE_${FEATURE}_FLAG}) + if (${HAS_FEATURE}) + target_compile_options(argon2-${FEATURE} PRIVATE ${GCC_FLAG}) + endif() + endif() - # Check for SSE2 - check_cxx_source_runs(" - #include - int main() - { - __m128d a, b; - double vals[2] = {0}; - a = _mm_loadu_pd(vals); - b = _mm_add_pd(a,a); - _mm_storeu_pd(vals,b); - return 0; - }" - HAVE_SSE2_EXTENSIONS) + if (${HAS_FEATURE}) + message("-- argon2: feature '${FEATURE}' detected!") + target_compile_definitions(argon2-${FEATURE} PRIVATE ${DEF}) + endif() - if(HAVE_SSE2_EXTENSIONS) - message("-- Argon2: SSE2 Extensions - Enabled") - add_definitions(-DHAVE_SSE2) - if(MSVC) - add_definitions(/arch:SSE2) - endif() - else() - message("-- Argon2: SSE2 Extensions - Disabled") - endif() + target_link_libraries(argon2 PUBLIC argon2-${FEATURE}) + endfunction() - # Check for SSE3 - check_cxx_source_runs(" - #include - int main() - { - __m128d a, b; - double vals[2] = {0}; - a = _mm_loadu_pd(vals); - b = _mm_hadd_pd(a,a); - _mm_storeu_pd(vals, b); - return 0; - }" - HAVE_SSE3_EXTENSIONS) + add_feature_impl(sse2 -msse2 HAVE_SSE2) + add_feature_impl(ssse3 -mssse3 HAVE_SSSE3) + add_feature_impl(xop -mxop HAVE_XOP) + add_feature_impl(avx2 -mavx2 HAVE_AVX2) + add_feature_impl(avx512f -mavx512f HAVE_AVX512F) - if(HAVE_SSE3_EXTENSIONS) - message("-- Argon2: SSE3 Extensions - Enabled") - add_definitions(-DHAVE_SSE3) - if(MSVC) - add_definitions(/arch:SSE3) - endif() - else() - message("-- Argon2: SSE3 Extensions - Disabled") - endif() - - # Check for XOP - check_cxx_source_runs(" - #include - int main() - { - __m128i a, b, c; - const int src[8] = { 1, 2, 3, 4, 5, 6, 7, 8 }; - a = _mm_load_si128( (__m128i*)src ); - b = _mm_load_si128( (__m128i*)src ); - c = _mm_roti_epi64(*a, b); - return 0; - }" - HAVE_XOP_EXTENSIONS) - - if(HAVE_XOP_EXTENSIONS) - message("-- Argon2: XOP Extensions - Enabled") - add_definitions(-DHAVE_XOP) - else() - message("-- Argon2: XOP Extensions - Disabled") - endif() - - list(APPEND ARGON2_SRC - arch/x86_64/lib/argon2-sse2.c - arch/x86_64/lib/argon2-ssse3.c - arch/x86_64/lib/argon2-xop.c - arch/x86_64/lib/argon2-avx2.c - arch/x86_64/lib/argon2-avx512f.c - arch/x86_64/lib/argon2-arch.c - ) + target_sources(argon2 PRIVATE arch/x86_64/lib/argon2-arch.c arch/x86_64/lib/cpu-flags.c) else() - list(APPEND ARGON2_SRC - arch/generic/lib/argon2-arch.c - ) + target_sources(argon2 PRIVATE arch/generic/lib/argon2-arch.c) endif() - -add_library(argon2 STATIC ${ARGON2_SRC}) - -target_compile_definitions(argon2 - PUBLIC "A2_VISCTL" -) - -target_link_libraries(argon2 - PUBLIC argon2-interface ${CMAKE_THREAD_LIBS_INIT} - PRIVATE argon2-internal -) - -set_property(TARGET argon2 PROPERTY C_STANDARD 90) -set_property(TARGET argon2 PROPERTY VERSION ${Upstream_VERSION}) -set_property(TARGET argon2 PROPERTY SOVERSION 1) -set_property(TARGET argon2 PROPERTY INTERFACE_ARGON2_MAJOR_VERSION 1) -set_property(TARGET argon2 APPEND PROPERTY - COMPATIBLE_INTERFACE_STRING ARGON2_MAJOR_VERSION -) - diff --git a/src/3rdparty/argon2/arch/x86_64/lib/argon2-arch.c b/src/3rdparty/argon2/arch/x86_64/lib/argon2-arch.c index 1d54b657..4486e395 100644 --- a/src/3rdparty/argon2/arch/x86_64/lib/argon2-arch.c +++ b/src/3rdparty/argon2/arch/x86_64/lib/argon2-arch.c @@ -4,6 +4,7 @@ #include "impl-select.h" +#include "cpu-flags.h" #include "argon2-sse2.h" #include "argon2-ssse3.h" #include "argon2-xop.h" @@ -33,6 +34,8 @@ void argon2_get_impl_list(argon2_impl_list *list) { "AVX-512F", check_avx512f, fill_segment_avx512f }, }; + cpu_flags_get(); + list->count = sizeof(IMPLS) / sizeof(IMPLS[0]); list->entries = IMPLS; } diff --git a/src/3rdparty/argon2/arch/x86_64/lib/argon2-avx2.c b/src/3rdparty/argon2/arch/x86_64/lib/argon2-avx2.c index 5dc41979..09ef421a 100644 --- a/src/3rdparty/argon2/arch/x86_64/lib/argon2-avx2.c +++ b/src/3rdparty/argon2/arch/x86_64/lib/argon2-avx2.c @@ -3,7 +3,13 @@ #ifdef HAVE_AVX2 #include -#include +#ifdef __GNUC__ +# include +#else +# include +#endif + +#include "cpu-flags.h" #define r16 (_mm256_setr_epi8( \ 2, 3, 4, 5, 6, 7, 0, 1, \ @@ -323,7 +329,7 @@ void fill_segment_avx2(const argon2_instance_t *instance, int check_avx2(void) { - return 1; + return cpu_flags_have_avx2(); } #else diff --git a/src/3rdparty/argon2/arch/x86_64/lib/argon2-avx512f.c b/src/3rdparty/argon2/arch/x86_64/lib/argon2-avx512f.c index f6de135b..79fd2dda 100644 --- a/src/3rdparty/argon2/arch/x86_64/lib/argon2-avx512f.c +++ b/src/3rdparty/argon2/arch/x86_64/lib/argon2-avx512f.c @@ -4,7 +4,13 @@ #include #include -#include +#ifdef __GNUC__ +# include +#else +# include +#endif + +#include "cpu-flags.h" #define ror64(x, n) _mm512_ror_epi64((x), (n)) @@ -308,7 +314,7 @@ void fill_segment_avx512f(const argon2_instance_t *instance, int check_avx512f(void) { - return 1; + return cpu_flags_have_avx512f(); } #else diff --git a/src/3rdparty/argon2/arch/x86_64/lib/argon2-sse2.c b/src/3rdparty/argon2/arch/x86_64/lib/argon2-sse2.c index 60ffb7bb..5eccdf07 100644 --- a/src/3rdparty/argon2/arch/x86_64/lib/argon2-sse2.c +++ b/src/3rdparty/argon2/arch/x86_64/lib/argon2-sse2.c @@ -1,7 +1,13 @@ #include "argon2-sse2.h" #ifdef HAVE_SSE2 -#include +#ifdef __GNUC__ +# include +#else +# include +#endif + +#include "cpu-flags.h" #define ror64_16(x) \ _mm_shufflehi_epi16( \ @@ -104,7 +110,7 @@ void fill_segment_sse2(const argon2_instance_t *instance, int check_sse2(void) { - return 1; + return cpu_flags_have_sse2(); } #else diff --git a/src/3rdparty/argon2/arch/x86_64/lib/argon2-ssse3.c b/src/3rdparty/argon2/arch/x86_64/lib/argon2-ssse3.c index 7098ab22..86e9b3c9 100644 --- a/src/3rdparty/argon2/arch/x86_64/lib/argon2-ssse3.c +++ b/src/3rdparty/argon2/arch/x86_64/lib/argon2-ssse3.c @@ -3,7 +3,13 @@ #ifdef HAVE_SSSE3 #include -#include +#ifdef __GNUC__ +# include +#else +# include +#endif + +#include "cpu-flags.h" #define r16 (_mm_setr_epi8( \ 2, 3, 4, 5, 6, 7, 0, 1, \ @@ -116,7 +122,7 @@ void fill_segment_ssse3(const argon2_instance_t *instance, int check_ssse3(void) { - return 1; + return cpu_flags_have_ssse3(); } #else diff --git a/src/3rdparty/argon2/arch/x86_64/lib/argon2-template-128.h b/src/3rdparty/argon2/arch/x86_64/lib/argon2-template-128.h index 3062ec00..4fb04c1f 100644 --- a/src/3rdparty/argon2/arch/x86_64/lib/argon2-template-128.h +++ b/src/3rdparty/argon2/arch/x86_64/lib/argon2-template-128.h @@ -1,6 +1,10 @@ #include -#include +#ifdef __GNUC__ +# include +#else +# include +#endif #include "core.h" diff --git a/src/3rdparty/argon2/arch/x86_64/lib/argon2-xop.c b/src/3rdparty/argon2/arch/x86_64/lib/argon2-xop.c index a7f6e399..0e877bce 100644 --- a/src/3rdparty/argon2/arch/x86_64/lib/argon2-xop.c +++ b/src/3rdparty/argon2/arch/x86_64/lib/argon2-xop.c @@ -3,7 +3,13 @@ #ifdef HAVE_XOP #include -#include +#ifdef __GNUC__ +# include +#else +# include +#endif + +#include "cpu-flags.h" #define ror64(x, c) _mm_roti_epi64((x), -(c)) @@ -104,7 +110,7 @@ void fill_segment_xop(const argon2_instance_t *instance, int check_xop(void) { - return 1; + return cpu_flags_have_xop(); } #else diff --git a/src/3rdparty/argon2/arch/x86_64/lib/cpu-flags.c b/src/3rdparty/argon2/arch/x86_64/lib/cpu-flags.c new file mode 100644 index 00000000..cd13cb01 --- /dev/null +++ b/src/3rdparty/argon2/arch/x86_64/lib/cpu-flags.c @@ -0,0 +1,129 @@ +#include +#include + + +#include "cpu-flags.h" + +#include + +#ifdef _MSC_VER +# include +#else +# include +#endif + +#ifndef bit_OSXSAVE +# define bit_OSXSAVE (1 << 27) +#endif + +#ifndef bit_SSE2 +# define bit_SSE2 (1 << 26) +#endif + +#ifndef bit_SSSE3 +# define bit_SSSE3 (1 << 9) +#endif + +#ifndef bit_AVX2 +# define bit_AVX2 (1 << 5) +#endif + +#ifndef bit_AVX512F +# define bit_AVX512F (1 << 16) +#endif + +#ifndef bit_XOP +# define bit_XOP (1 << 11) +#endif + +#define PROCESSOR_INFO (1) +#define EXTENDED_FEATURES (7) + +#define EAX_Reg (0) +#define EBX_Reg (1) +#define ECX_Reg (2) +#define EDX_Reg (3) + + +enum { + X86_64_FEATURE_SSE2 = (1 << 0), + X86_64_FEATURE_SSSE3 = (1 << 1), + X86_64_FEATURE_XOP = (1 << 2), + X86_64_FEATURE_AVX2 = (1 << 3), + X86_64_FEATURE_AVX512F = (1 << 4), +}; + +static unsigned int cpu_flags; + + +static inline void cpuid(uint32_t level, int32_t output[4]) +{ +# ifdef _MSC_VER + __cpuid(output, (int) level); +# else + __cpuid_count(level, 0, output[0], output[1], output[2], output[3]); +# endif +} + + +static bool has_feature(uint32_t level, uint32_t reg, int32_t bit) +{ + int32_t cpu_info[4] = { 0 }; + cpuid(level, cpu_info); + + return (cpu_info[reg] & bit) != 0; +} + + +void cpu_flags_get(void) +{ + if (has_feature(PROCESSOR_INFO, EDX_Reg, bit_SSE2)) { + cpu_flags |= X86_64_FEATURE_SSE2; + } + + if (has_feature(PROCESSOR_INFO, ECX_Reg, bit_SSSE3)) { + cpu_flags |= X86_64_FEATURE_SSSE3; + } + + if (!has_feature(PROCESSOR_INFO, ECX_Reg, bit_OSXSAVE)) { + return; + } + + if (has_feature(EXTENDED_FEATURES, EBX_Reg, bit_AVX2)) { + cpu_flags |= X86_64_FEATURE_AVX2; + } + + if (has_feature(EXTENDED_FEATURES, EBX_Reg, bit_AVX512F)) { + cpu_flags |= X86_64_FEATURE_AVX512F; + } + + if (has_feature(0x80000001, ECX_Reg, bit_XOP)) { + cpu_flags |= X86_64_FEATURE_XOP; + } +} + +int cpu_flags_have_sse2(void) +{ + return cpu_flags & X86_64_FEATURE_SSE2; +} + +int cpu_flags_have_ssse3(void) +{ + return cpu_flags & X86_64_FEATURE_SSSE3; +} + +int cpu_flags_have_xop(void) +{ + return cpu_flags & X86_64_FEATURE_XOP; +} + +int cpu_flags_have_avx2(void) +{ + return cpu_flags & X86_64_FEATURE_AVX2; +} + +int cpu_flags_have_avx512f(void) +{ + return cpu_flags & X86_64_FEATURE_AVX512F; +} + diff --git a/src/3rdparty/argon2/arch/x86_64/lib/cpu-flags.h b/src/3rdparty/argon2/arch/x86_64/lib/cpu-flags.h new file mode 100644 index 00000000..b546a8ba --- /dev/null +++ b/src/3rdparty/argon2/arch/x86_64/lib/cpu-flags.h @@ -0,0 +1,12 @@ +#ifndef ARGON2_CPU_FLAGS_H +#define ARGON2_CPU_FLAGS_H + +void cpu_flags_get(void); + +int cpu_flags_have_sse2(void); +int cpu_flags_have_ssse3(void); +int cpu_flags_have_xop(void); +int cpu_flags_have_avx2(void); +int cpu_flags_have_avx512f(void); + +#endif // ARGON2_CPU_FLAGS_H diff --git a/src/3rdparty/argon2/arch/x86_64/src/test-feature-avx2.c b/src/3rdparty/argon2/arch/x86_64/src/test-feature-avx2.c new file mode 100644 index 00000000..01e5b156 --- /dev/null +++ b/src/3rdparty/argon2/arch/x86_64/src/test-feature-avx2.c @@ -0,0 +1,8 @@ +#include + +void function_avx2(__m256i *dst, const __m256i *a, const __m256i *b) +{ + *dst = _mm256_xor_si256(*a, *b); +} + +int main(void) { return 0; } diff --git a/src/3rdparty/argon2/arch/x86_64/src/test-feature-avx512f.c b/src/3rdparty/argon2/arch/x86_64/src/test-feature-avx512f.c new file mode 100644 index 00000000..372eb256 --- /dev/null +++ b/src/3rdparty/argon2/arch/x86_64/src/test-feature-avx512f.c @@ -0,0 +1,8 @@ +#include + +void function_avx512f(__m512i *dst, const __m512i *a) +{ + *dst = _mm512_ror_epi64(*a, 57); +} + +int main(void) { return 0; } diff --git a/src/3rdparty/argon2/arch/x86_64/src/test-feature-sse2.c b/src/3rdparty/argon2/arch/x86_64/src/test-feature-sse2.c new file mode 100644 index 00000000..4a3b70fd --- /dev/null +++ b/src/3rdparty/argon2/arch/x86_64/src/test-feature-sse2.c @@ -0,0 +1,8 @@ +#include + +void function_sse2(__m128i *dst, const __m128i *a, const __m128i *b) +{ + *dst = _mm_xor_si128(*a, *b); +} + +int main(void) { return 0; } diff --git a/src/3rdparty/argon2/arch/x86_64/src/test-feature-ssse3.c b/src/3rdparty/argon2/arch/x86_64/src/test-feature-ssse3.c new file mode 100644 index 00000000..db2b3551 --- /dev/null +++ b/src/3rdparty/argon2/arch/x86_64/src/test-feature-ssse3.c @@ -0,0 +1,8 @@ +#include + +void function_ssse3(__m128i *dst, const __m128i *a, const __m128i *b) +{ + *dst = _mm_shuffle_epi8(*a, *b); +} + +int main(void) { return 0; } diff --git a/src/3rdparty/argon2/arch/x86_64/src/test-feature-xop.c b/src/3rdparty/argon2/arch/x86_64/src/test-feature-xop.c new file mode 100644 index 00000000..5897fe14 --- /dev/null +++ b/src/3rdparty/argon2/arch/x86_64/src/test-feature-xop.c @@ -0,0 +1,8 @@ +#include + +void function_xop(__m128i *dst, const __m128i *a, int b) +{ + *dst = _mm_roti_epi64(*a, b); +} + +int main(void) { return 0; } diff --git a/src/3rdparty/argon2/configure.ac b/src/3rdparty/argon2/configure.ac deleted file mode 100644 index 81607a97..00000000 --- a/src/3rdparty/argon2/configure.ac +++ /dev/null @@ -1,108 +0,0 @@ -dnl --------------------------------------------------------------------- -dnl Copyright (C) 2015, Ondrej Mosnacek -dnl -dnl This program is free software: you can redistribute it and/or -dnl modify it under the terms of the GNU General Public License -dnl as published by the Free Software Foundation: either version 2 -dnl of the License, or (at your option) any later version. -dnl -dnl This program is distributed in the hope that it will be useful, -dnl but WITHOUT ANY WARRANTY; without even the implied warranty of -dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -dnl GNU General Public License for more details. -dnl -dnl You should have received a copy of the GNU General Public License -dnl along with this program. If not, see . -dnl --------------------------------------------------------------------- - -AC_CONFIG_MACRO_DIR([m4]) - -AC_INIT([argon2], [0.1], []) -LT_INIT -AM_INIT_AUTOMAKE([foreign subdir-objects]) -AM_SILENT_RULES([yes]) - -AC_PROG_CC -AC_PROG_CC_C89 -AM_PROG_AS -AX_PTHREAD - -AC_CANONICAL_HOST - -AS_CASE([$host_cpu], - dnl [i?86], [ARCH=i386], - [x86_64], [ARCH=x86_64], - [ARCH=generic - AC_MSG_WARN("No code for architecture $host_cpu; using generic implementation")] -) -AC_SUBST([ARCH]) - -AM_CONDITIONAL([ARCH_X86_64], [test "$ARCH" = 'x86_64']) -AM_CONDITIONAL([ARCH_GENERIC], [test "$ARCH" = 'generic']) - -# AX_CHECK_COMPILER_FEATURE(NAME, FLAG, TEST_SOURCE) -# -------------------------- -AC_DEFUN([AX_CHECK_COMPILER_FEATURE], [{ - AX_CHECK_COMPILE_FLAG([-m$2], [HAVE_FLAG=1], [HAVE_FLAG=0]) - HAVE_FEATURE=0 - AS_IF([test "$HAVE_FLAG" = '1'], [{ - AC_MSG_CHECKING("whether C compiler supports $1 with -m$2...") - - CFLAGS_BACKUP="$CFLAGS" - CFLAGS="-m$2" - - AC_COMPILE_IFELSE([AC_LANG_SOURCE([$3])], [HAVE_FEATURE=1]) - - CFLAGS="$CFLAGS_BACKUP" - AS_IF([test "$HAVE_FEATURE" = '1'], [RESULT='yes'], [RESULT='no']) - AC_MSG_RESULT([$RESULT]) - - }]) - HAVE_$1=HAVE_FEATURE - AM_CONDITIONAL([HAVE_$1], [test "$HAVE_FEATURE" = '1']) -}]) - -AX_CHECK_COMPILER_FEATURE([SSE2], [sse2], [[ -#include - -void function_sse2(__m128i *dst, const __m128i *a, const __m128i *b) -{ - *dst = _mm_xor_si128(*a, *b); -} -]]) -AX_CHECK_COMPILER_FEATURE([SSSE3], [ssse3], [[ -#include - -void function_ssse3(__m128i *dst, const __m128i *a, const __m128i *b) -{ - *dst = _mm_shuffle_epi8(*a, *b); -} -]]) -AX_CHECK_COMPILER_FEATURE([XOP], [xop], [[ -#include - -void function_xop(__m128i *dst, const __m128i *a, int b) -{ - *dst = _mm_roti_epi64(*a, b); -} -]]) -AX_CHECK_COMPILER_FEATURE([AVX2], [avx2], [[ -#include - -void function_avx2(__m256i *dst, const __m256i *a, const __m256i *b) -{ - *dst = _mm256_xor_si256(*a, *b); -} -]]) -AX_CHECK_COMPILER_FEATURE([AVX512F], [avx512f], [[ -#include - -void function_avx512f(__m512i *dst, const __m512i *a) -{ - *dst = _mm512_ror_epi64(*a, 57); -} -]]) - -AC_CONFIG_FILES([Makefile]) - -AC_OUTPUT diff --git a/src/3rdparty/argon2/include/argon2.h b/src/3rdparty/argon2/include/argon2.h index d3bf8458..7759a885 100644 --- a/src/3rdparty/argon2/include/argon2.h +++ b/src/3rdparty/argon2/include/argon2.h @@ -19,30 +19,7 @@ #include /* Symbols visibility control */ -#if defined(_WIN32) || defined(__CYGWIN__) - #if defined(A2_VISCTL) - #if defined(_MSC_VER) - #define ARGON2_PUBLIC __declspec(dllexport) - #else - #define ARGON2_PUBLIC __attribute__ ((dllexport)) - #endif - #else - #if defined(_MSC_VER) - #define ARGON2_PUBLIC __declspec(dllimport) - #else - #define ARGON2_PUBLIC /*__attribute__ ((dllimport))*/ - #endif - #endif - #define ARGON2_LOCAL -#else - #if defined(A2_VISCTL) - #define ARGON2_PUBLIC __attribute__ ((visibility ("default"))) - #define ARGON2_LOCAL __attribute__ ((visibility ("hidden"))) - #else - #define ARGON2_PUBLIC - #define ARGON2_LOCAL - #endif -#endif +#define ARGON2_PUBLIC #if defined(__cplusplus) extern "C" { @@ -255,7 +232,7 @@ ARGON2_PUBLIC const char *argon2_type2string(argon2_type type, int uppercase); * @param context Pointer to the Argon2 internal structure * @return Error code if smth is wrong, ARGON2_OK otherwise */ -ARGON2_PUBLIC int argon2_ctx(argon2_context *context, argon2_type type, void *memory, size_t memory_size); +ARGON2_PUBLIC int argon2_ctx(argon2_context *context, argon2_type type); /** * Hashes a password with Argon2i, producing an encoded hash @@ -278,8 +255,7 @@ ARGON2_PUBLIC int argon2i_hash_encoded(const uint32_t t_cost, const void *pwd, const size_t pwdlen, const void *salt, const size_t saltlen, const size_t hashlen, char *encoded, - const size_t encodedlen, void *memory, - size_t memory_size); + const size_t encodedlen); /** * Hashes a password with Argon2i, producing a raw hash by allocating memory at @@ -300,8 +276,7 @@ ARGON2_PUBLIC int argon2i_hash_raw(const uint32_t t_cost, const uint32_t m_cost, const uint32_t parallelism, const void *pwd, const size_t pwdlen, const void *salt, const size_t saltlen, void *hash, - const size_t hashlen, void *memory, - size_t memory_size); + const size_t hashlen); ARGON2_PUBLIC int argon2d_hash_encoded(const uint32_t t_cost, const uint32_t m_cost, @@ -309,16 +284,14 @@ ARGON2_PUBLIC int argon2d_hash_encoded(const uint32_t t_cost, const void *pwd, const size_t pwdlen, const void *salt, const size_t saltlen, const size_t hashlen, char *encoded, - const size_t encodedlen, void *memory, - size_t memory_size); + const size_t encodedlen); ARGON2_PUBLIC int argon2d_hash_raw(const uint32_t t_cost, const uint32_t m_cost, const uint32_t parallelism, const void *pwd, const size_t pwdlen, const void *salt, const size_t saltlen, void *hash, - const size_t hashlen, void *memory, - size_t memory_size); + const size_t hashlen); ARGON2_PUBLIC int argon2id_hash_encoded(const uint32_t t_cost, const uint32_t m_cost, @@ -326,16 +299,22 @@ ARGON2_PUBLIC int argon2id_hash_encoded(const uint32_t t_cost, const void *pwd, const size_t pwdlen, const void *salt, const size_t saltlen, const size_t hashlen, char *encoded, - const size_t encodedlen, void *memory, - size_t memory_size); + const size_t encodedlen); ARGON2_PUBLIC int argon2id_hash_raw(const uint32_t t_cost, const uint32_t m_cost, const uint32_t parallelism, const void *pwd, const size_t pwdlen, const void *salt, const size_t saltlen, void *hash, - const size_t hashlen, void *memory, - size_t memory_size); + const size_t hashlen); + +ARGON2_PUBLIC int argon2id_hash_raw_ex(const uint32_t t_cost, + const uint32_t m_cost, + const uint32_t parallelism, const void *pwd, + const size_t pwdlen, const void *salt, + const size_t saltlen, void *hash, + const size_t hashlen, + void *memory); /* generic function underlying the above ones */ ARGON2_PUBLIC int argon2_hash(const uint32_t t_cost, const uint32_t m_cost, @@ -344,8 +323,7 @@ ARGON2_PUBLIC int argon2_hash(const uint32_t t_cost, const uint32_t m_cost, const size_t saltlen, void *hash, const size_t hashlen, char *encoded, const size_t encodedlen, argon2_type type, - const uint32_t version, void *memory, - size_t memory_size); + const uint32_t version); /** * Verifies a password against an encoded string @@ -460,7 +438,9 @@ ARGON2_PUBLIC size_t argon2_encodedlen(uint32_t t_cost, uint32_t m_cost, * @param prefix What to print before each line; NULL is equivalent to empty * string */ -ARGON2_PUBLIC void argon2_select_impl(FILE *out, const char *prefix); +ARGON2_PUBLIC void argon2_select_impl(); +ARGON2_PUBLIC const char *argon2_get_impl_name(); +ARGON2_PUBLIC int argon2_select_impl_by_name(const char *name); /* signals support for passing preallocated memory: */ #define ARGON2_PREALLOCATED_MEMORY diff --git a/src/3rdparty/argon2/lib/argon2.c b/src/3rdparty/argon2/lib/argon2.c index 4027690e..d4d038a9 100644 --- a/src/3rdparty/argon2/lib/argon2.c +++ b/src/3rdparty/argon2/lib/argon2.c @@ -116,8 +116,8 @@ int argon2_ctx_mem(argon2_context *context, argon2_type type, void *memory, return ARGON2_OK; } -int argon2_ctx(argon2_context *context, argon2_type type, void *memory, size_t memory_size) { - return argon2_ctx_mem(context, type, memory, memory_size); +int argon2_ctx(argon2_context *context, argon2_type type) { + return argon2_ctx_mem(context, type, NULL, 0); } int argon2_hash(const uint32_t t_cost, const uint32_t m_cost, @@ -125,7 +125,7 @@ int argon2_hash(const uint32_t t_cost, const uint32_t m_cost, const size_t pwdlen, const void *salt, const size_t saltlen, void *hash, const size_t hashlen, char *encoded, const size_t encodedlen, argon2_type type, - const uint32_t version, void *memory, size_t memory_size){ + const uint32_t version){ argon2_context context; int result; @@ -171,7 +171,7 @@ int argon2_hash(const uint32_t t_cost, const uint32_t m_cost, context.flags = ARGON2_DEFAULT_FLAGS; context.version = version; - result = argon2_ctx(&context, type, memory, memory_size); + result = argon2_ctx(&context, type); if (result != ARGON2_OK) { clear_internal_memory(out, hashlen); @@ -203,66 +203,88 @@ int argon2i_hash_encoded(const uint32_t t_cost, const uint32_t m_cost, const uint32_t parallelism, const void *pwd, const size_t pwdlen, const void *salt, const size_t saltlen, const size_t hashlen, - char *encoded, const size_t encodedlen, - void *memory, size_t memory_size) { + char *encoded, const size_t encodedlen) { return argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen, NULL, hashlen, encoded, encodedlen, Argon2_i, - ARGON2_VERSION_NUMBER, memory, memory_size); + ARGON2_VERSION_NUMBER); } int argon2i_hash_raw(const uint32_t t_cost, const uint32_t m_cost, const uint32_t parallelism, const void *pwd, const size_t pwdlen, const void *salt, - const size_t saltlen, void *hash, const size_t hashlen, - void *memory, size_t memory_size) { + const size_t saltlen, void *hash, const size_t hashlen) { return argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen, - hash, hashlen, NULL, 0, Argon2_i, ARGON2_VERSION_NUMBER, memory, memory_size); + hash, hashlen, NULL, 0, Argon2_i, ARGON2_VERSION_NUMBER); } int argon2d_hash_encoded(const uint32_t t_cost, const uint32_t m_cost, const uint32_t parallelism, const void *pwd, const size_t pwdlen, const void *salt, const size_t saltlen, const size_t hashlen, - char *encoded, const size_t encodedlen, - void *memory, size_t memory_size) { + char *encoded, const size_t encodedlen) { return argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen, NULL, hashlen, encoded, encodedlen, Argon2_d, - ARGON2_VERSION_NUMBER, memory, memory_size); + ARGON2_VERSION_NUMBER); } int argon2d_hash_raw(const uint32_t t_cost, const uint32_t m_cost, const uint32_t parallelism, const void *pwd, const size_t pwdlen, const void *salt, - const size_t saltlen, void *hash, const size_t hashlen, - void *memory, size_t memory_size) { + const size_t saltlen, void *hash, const size_t hashlen) { return argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen, - hash, hashlen, NULL, 0, Argon2_d, ARGON2_VERSION_NUMBER, memory, memory_size); + hash, hashlen, NULL, 0, Argon2_d, ARGON2_VERSION_NUMBER); } int argon2id_hash_encoded(const uint32_t t_cost, const uint32_t m_cost, const uint32_t parallelism, const void *pwd, const size_t pwdlen, const void *salt, const size_t saltlen, const size_t hashlen, - char *encoded, const size_t encodedlen, - void *memory, size_t memory_size) { + char *encoded, const size_t encodedlen) { return argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen, NULL, hashlen, encoded, encodedlen, Argon2_id, - ARGON2_VERSION_NUMBER, memory, memory_size); + ARGON2_VERSION_NUMBER); } int argon2id_hash_raw(const uint32_t t_cost, const uint32_t m_cost, const uint32_t parallelism, const void *pwd, const size_t pwdlen, const void *salt, - const size_t saltlen, void *hash, const size_t hashlen, - void *memory, size_t memory_size) { + const size_t saltlen, void *hash, const size_t hashlen) { return argon2_hash(t_cost, m_cost, parallelism, pwd, pwdlen, salt, saltlen, hash, hashlen, NULL, 0, Argon2_id, - ARGON2_VERSION_NUMBER, memory, memory_size); + ARGON2_VERSION_NUMBER); +} + +int argon2id_hash_raw_ex(const uint32_t t_cost, const uint32_t m_cost, + const uint32_t parallelism, const void *pwd, + const size_t pwdlen, const void *salt, + const size_t saltlen, void *hash, const size_t hashlen, void *memory) { + argon2_context context; + + context.out = (uint8_t *)hash; + context.outlen = (uint32_t)hashlen; + context.pwd = CONST_CAST(uint8_t *)pwd; + context.pwdlen = (uint32_t)pwdlen; + context.salt = CONST_CAST(uint8_t *)salt; + context.saltlen = (uint32_t)saltlen; + context.secret = NULL; + context.secretlen = 0; + context.ad = NULL; + context.adlen = 0; + context.t_cost = t_cost; + context.m_cost = m_cost; + context.lanes = parallelism; + context.threads = parallelism; + context.allocate_cbk = NULL; + context.free_cbk = NULL; + context.flags = ARGON2_DEFAULT_FLAGS; + context.version = ARGON2_VERSION_NUMBER; + + return argon2_ctx_mem(&context, Argon2_id, memory, m_cost * 1024); } static int argon2_compare(const uint8_t *b1, const uint8_t *b2, size_t len) { @@ -357,20 +379,20 @@ int argon2id_verify(const char *encoded, const void *pwd, const size_t pwdlen) { } int argon2d_ctx(argon2_context *context) { - return argon2_ctx(context, Argon2_d, NULL, 0); + return argon2_ctx(context, Argon2_d); } int argon2i_ctx(argon2_context *context) { - return argon2_ctx(context, Argon2_i, NULL, 0); + return argon2_ctx(context, Argon2_i); } int argon2id_ctx(argon2_context *context) { - return argon2_ctx(context, Argon2_id, NULL, 0); + return argon2_ctx(context, Argon2_id); } int argon2_verify_ctx(argon2_context *context, const char *hash, argon2_type type) { - int ret = argon2_ctx(context, type, NULL, 0); + int ret = argon2_ctx(context, type); if (ret != ARGON2_OK) { return ret; } diff --git a/src/3rdparty/argon2/lib/core.c b/src/3rdparty/argon2/lib/core.c index 3498e37c..5d130c04 100644 --- a/src/3rdparty/argon2/lib/core.c +++ b/src/3rdparty/argon2/lib/core.c @@ -27,7 +27,6 @@ #include #include "core.h" -#include "thread.h" #include "blake2/blake2.h" #include "blake2/blake2-impl.h" @@ -89,8 +88,6 @@ int allocate_memory(const argon2_context *context, return ARGON2_OK; } - return ARGON2_MEMORY_ALLOCATION_ERROR; - /* 1. Check for multiplication overflow */ if (blocks != 0 && memory_size / ARGON2_BLOCK_SIZE != blocks) { return ARGON2_MEMORY_ALLOCATION_ERROR; @@ -142,7 +139,7 @@ void NOT_OPTIMIZED secure_wipe_memory(void *v, size_t n) { } /* Memory clear flag defaults to true. */ -int FLAG_clear_internal_memory = 1; +int FLAG_clear_internal_memory = 0; void clear_internal_memory(void *v, size_t n) { if (FLAG_clear_internal_memory && v) { secure_wipe_memory(v, n); @@ -252,18 +249,6 @@ uint32_t index_alpha(const argon2_instance_t *instance, return absolute_position; } -#ifdef _WIN32 -static unsigned __stdcall fill_segment_thr(void *thread_data) -#else -static void *fill_segment_thr(void *thread_data) -#endif -{ - argon2_thread_data *my_data = thread_data; - fill_segment(my_data->instance_ptr, my_data->pos); - argon2_thread_exit(); - return 0; -} - /* Single-threaded version for p=1 case */ static int fill_memory_blocks_st(argon2_instance_t *instance) { uint32_t r, s, l; @@ -283,93 +268,12 @@ static int fill_memory_blocks_st(argon2_instance_t *instance) { return ARGON2_OK; } -/* Multi-threaded version for p > 1 case */ -static int fill_memory_blocks_mt(argon2_instance_t *instance) { - uint32_t r, s; - argon2_thread_handle_t *thread = NULL; - argon2_thread_data *thr_data = NULL; - int rc = ARGON2_OK; - - /* 1. Allocating space for threads */ - thread = calloc(instance->lanes, sizeof(argon2_thread_handle_t)); - if (thread == NULL) { - rc = ARGON2_MEMORY_ALLOCATION_ERROR; - goto fail; - } - - thr_data = calloc(instance->lanes, sizeof(argon2_thread_data)); - if (thr_data == NULL) { - rc = ARGON2_MEMORY_ALLOCATION_ERROR; - goto fail; - } - - for (r = 0; r < instance->passes; ++r) { - for (s = 0; s < ARGON2_SYNC_POINTS; ++s) { - uint32_t l; - - /* 2. Calling threads */ - for (l = 0; l < instance->lanes; ++l) { - argon2_position_t position; - - /* 2.1 Join a thread if limit is exceeded */ - if (l >= instance->threads) { - if (argon2_thread_join(thread[l - instance->threads])) { - rc = ARGON2_THREAD_FAIL; - goto fail; - } - } - - /* 2.2 Create thread */ - position.pass = r; - position.lane = l; - position.slice = (uint8_t)s; - position.index = 0; - thr_data[l].instance_ptr = - instance; /* preparing the thread input */ - memcpy(&(thr_data[l].pos), &position, - sizeof(argon2_position_t)); - if (argon2_thread_create(&thread[l], &fill_segment_thr, - (void *)&thr_data[l])) { - rc = ARGON2_THREAD_FAIL; - goto fail; - } - - /* fill_segment(instance, position); */ - /*Non-thread equivalent of the lines above */ - } - - /* 3. Joining remaining threads */ - for (l = instance->lanes - instance->threads; l < instance->lanes; - ++l) { - if (argon2_thread_join(thread[l])) { - rc = ARGON2_THREAD_FAIL; - goto fail; - } - } - } - - if (instance->print_internals) { - internal_kat(instance, r); /* Print all memory blocks */ - } - } - -fail: - if (thread != NULL) { - free(thread); - } - if (thr_data != NULL) { - free(thr_data); - } - return rc; -} - int fill_memory_blocks(argon2_instance_t *instance) { if (instance == NULL || instance->lanes == 0) { return ARGON2_INCORRECT_PARAMETER; } - return instance->threads == 1 ? - fill_memory_blocks_st(instance) : fill_memory_blocks_mt(instance); + return fill_memory_blocks_st(instance); } int validate_inputs(const argon2_context *context) { diff --git a/src/3rdparty/argon2/lib/impl-select.c b/src/3rdparty/argon2/lib/impl-select.c index 84c62aec..d618010c 100644 --- a/src/3rdparty/argon2/lib/impl-select.c +++ b/src/3rdparty/argon2/lib/impl-select.c @@ -5,18 +5,11 @@ #include "argon2.h" -#define log_maybe(file, ...) \ - do { \ - if (file) { \ - fprintf(file, __VA_ARGS__); \ - } \ - } while((void)0, 0) - -#define BENCH_SAMPLES 512 +#define BENCH_SAMPLES 1024 #define BENCH_MEM_BLOCKS 512 static argon2_impl selected_argon_impl = { - "(default)", NULL, fill_segment_default + "default", NULL, fill_segment_default }; /* the benchmark routine is not thread-safe, so we can use a global var here: */ @@ -60,32 +53,24 @@ static uint64_t benchmark_impl(const argon2_impl *impl) { return bench; } -static void select_impl(FILE *out, const char *prefix) +void argon2_select_impl() { argon2_impl_list impls; unsigned int i; const argon2_impl *best_impl = NULL; uint64_t best_bench = UINT_MAX; - log_maybe(out, "%sSelecting best fill_segment implementation...\n", prefix); - argon2_get_impl_list(&impls); for (i = 0; i < impls.count; i++) { const argon2_impl *impl = &impls.entries[i]; uint64_t bench; - log_maybe(out, "%s%s: Checking availability... ", prefix, impl->name); if (impl->check != NULL && !impl->check()) { - log_maybe(out, "FAILED!\n"); continue; } - log_maybe(out, "OK!\n"); - log_maybe(out, "%s%s: Benchmarking...\n", prefix, impl->name); bench = benchmark_impl(impl); - log_maybe(out, "%s%s: Benchmark result: %llu\n", prefix, impl->name, - (unsigned long long)bench); if (bench < best_bench) { best_bench = bench; @@ -94,15 +79,7 @@ static void select_impl(FILE *out, const char *prefix) } if (best_impl != NULL) { - log_maybe(out, - "%sBest implementation: '%s' (bench %llu)\n", prefix, - best_impl->name, (unsigned long long)best_bench); - selected_argon_impl = *best_impl; - } else { - log_maybe(out, - "%sNo optimized implementation available, using default!\n", - prefix); } } @@ -111,10 +88,28 @@ void fill_segment(const argon2_instance_t *instance, argon2_position_t position) selected_argon_impl.fill_segment(instance, position); } -void argon2_select_impl(FILE *out, const char *prefix) +const char *argon2_get_impl_name() { - if (prefix == NULL) { - prefix = ""; - } - select_impl(out, prefix); + return selected_argon_impl.name; +} + + +int argon2_select_impl_by_name(const char *name) +{ + argon2_impl_list impls; + unsigned int i; + + argon2_get_impl_list(&impls); + + for (i = 0; i < impls.count; i++) { + const argon2_impl *impl = &impls.entries[i]; + + if (strcmp(impl->name, name) == 0) { + selected_argon_impl = *impl; + + return 1; + } + } + + return 0; } diff --git a/src/3rdparty/argon2/lib/thread.c b/src/3rdparty/argon2/lib/thread.c deleted file mode 100644 index 412261f1..00000000 --- a/src/3rdparty/argon2/lib/thread.c +++ /dev/null @@ -1,36 +0,0 @@ -#include "thread.h" -#if defined(_WIN32) -#include -#endif - -int argon2_thread_create(argon2_thread_handle_t *handle, - argon2_thread_func_t func, void *args) { - if (NULL == handle || func == NULL) { - return -1; - } -#if defined(_WIN32) - *handle = _beginthreadex(NULL, 0, func, args, 0, NULL); - return *handle != 0 ? 0 : -1; -#else - return pthread_create(handle, NULL, func, args); -#endif -} - -int argon2_thread_join(argon2_thread_handle_t handle) { -#if defined(_WIN32) - if (WaitForSingleObject((HANDLE)handle, INFINITE) == WAIT_OBJECT_0) { - return CloseHandle((HANDLE)handle) != 0 ? 0 : -1; - } - return -1; -#else - return pthread_join(handle, NULL); -#endif -} - -void argon2_thread_exit(void) { -#if defined(_WIN32) - _endthreadex(0); -#else - pthread_exit(NULL); -#endif -} diff --git a/src/3rdparty/argon2/lib/thread.h b/src/3rdparty/argon2/lib/thread.h deleted file mode 100644 index f1ef5191..00000000 --- a/src/3rdparty/argon2/lib/thread.h +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef ARGON2_THREAD_H -#define ARGON2_THREAD_H -/* - Here we implement an abstraction layer for the simpĺe requirements - of the Argon2 code. We only require 3 primitives---thread creation, - joining, and termination---so full emulation of the pthreads API - is unwarranted. Currently we wrap pthreads and Win32 threads. - - The API defines 2 types: the function pointer type, - argon2_thread_func_t, - and the type of the thread handle---argon2_thread_handle_t. -*/ -#if defined(_WIN32) -#include -#include -typedef unsigned(__stdcall *argon2_thread_func_t)(void *); -typedef uintptr_t argon2_thread_handle_t; -#else -#include -typedef void *(*argon2_thread_func_t)(void *); -typedef pthread_t argon2_thread_handle_t; -#endif - -/* Creates a thread - * @param handle pointer to a thread handle, which is the output of this - * function. Must not be NULL. - * @param func A function pointer for the thread's entry point. Must not be - * NULL. - * @param args Pointer that is passed as an argument to @func. May be NULL. - * @return 0 if @handle and @func are valid pointers and a thread is successfuly - * created. - */ -int argon2_thread_create(argon2_thread_handle_t *handle, - argon2_thread_func_t func, void *args); - -/* Waits for a thread to terminate - * @param handle Handle to a thread created with argon2_thread_create. - * @return 0 if @handle is a valid handle, and joining completed successfully. -*/ -int argon2_thread_join(argon2_thread_handle_t handle); - -/* Terminate the current thread. Must be run inside a thread created by - * argon2_thread_create. -*/ -void argon2_thread_exit(void); - -#endif diff --git a/src/3rdparty/argon2/m4/ax_check_compile_flag.m4 b/src/3rdparty/argon2/m4/ax_check_compile_flag.m4 deleted file mode 100644 index ca363971..00000000 --- a/src/3rdparty/argon2/m4/ax_check_compile_flag.m4 +++ /dev/null @@ -1,74 +0,0 @@ -# =========================================================================== -# http://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html -# =========================================================================== -# -# SYNOPSIS -# -# AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT]) -# -# DESCRIPTION -# -# Check whether the given FLAG works with the current language's compiler -# or gives an error. (Warnings, however, are ignored) -# -# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on -# success/failure. -# -# If EXTRA-FLAGS is defined, it is added to the current language's default -# flags (e.g. CFLAGS) when the check is done. The check is thus made with -# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to -# force the compiler to issue an error when a bad flag is given. -# -# INPUT gives an alternative input source to AC_COMPILE_IFELSE. -# -# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this -# macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG. -# -# LICENSE -# -# Copyright (c) 2008 Guido U. Draheim -# Copyright (c) 2011 Maarten Bosmans -# -# 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 . -# -# As a special exception, the respective Autoconf Macro's copyright owner -# gives unlimited permission to copy, distribute and modify the configure -# scripts that are the output of Autoconf when processing the Macro. You -# need not follow the terms of the GNU General Public License when using -# or distributing such scripts, even though portions of the text of the -# Macro appear in them. The GNU General Public License (GPL) does govern -# all other use of the material that constitutes the Autoconf Macro. -# -# This special exception to the GPL applies to versions of the Autoconf -# Macro released by the Autoconf Archive. When you make and distribute a -# modified version of the Autoconf Macro, you may extend this special -# exception to the GPL to apply to your modified version as well. - -#serial 4 - -AC_DEFUN([AX_CHECK_COMPILE_FLAG], -[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF -AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl -AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [ - ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS - _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1" - AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], - [AS_VAR_SET(CACHEVAR,[yes])], - [AS_VAR_SET(CACHEVAR,[no])]) - _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags]) -AS_VAR_IF(CACHEVAR,yes, - [m4_default([$2], :)], - [m4_default([$3], :)]) -AS_VAR_POPDEF([CACHEVAR])dnl -])dnl AX_CHECK_COMPILE_FLAGS diff --git a/src/3rdparty/argon2/m4/ax_pthread.m4 b/src/3rdparty/argon2/m4/ax_pthread.m4 deleted file mode 100644 index 4c4051ea..00000000 --- a/src/3rdparty/argon2/m4/ax_pthread.m4 +++ /dev/null @@ -1,485 +0,0 @@ -# =========================================================================== -# http://www.gnu.org/software/autoconf-archive/ax_pthread.html -# =========================================================================== -# -# SYNOPSIS -# -# AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) -# -# DESCRIPTION -# -# This macro figures out how to build C programs using POSIX threads. It -# sets the PTHREAD_LIBS output variable to the threads library and linker -# flags, and the PTHREAD_CFLAGS output variable to any special C compiler -# flags that are needed. (The user can also force certain compiler -# flags/libs to be tested by setting these environment variables.) -# -# Also sets PTHREAD_CC to any special C compiler that is needed for -# multi-threaded programs (defaults to the value of CC otherwise). (This -# is necessary on AIX to use the special cc_r compiler alias.) -# -# NOTE: You are assumed to not only compile your program with these flags, -# but also to link with them as well. For example, you might link with -# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS -# -# If you are only building threaded programs, you may wish to use these -# variables in your default LIBS, CFLAGS, and CC: -# -# LIBS="$PTHREAD_LIBS $LIBS" -# CFLAGS="$CFLAGS $PTHREAD_CFLAGS" -# CC="$PTHREAD_CC" -# -# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant -# has a nonstandard name, this macro defines PTHREAD_CREATE_JOINABLE to -# that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX). -# -# Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the -# PTHREAD_PRIO_INHERIT symbol is defined when compiling with -# PTHREAD_CFLAGS. -# -# ACTION-IF-FOUND is a list of shell commands to run if a threads library -# is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it -# is not found. If ACTION-IF-FOUND is not specified, the default action -# will define HAVE_PTHREAD. -# -# Please let the authors know if this macro fails on any platform, or if -# you have any other suggestions or comments. This macro was based on work -# by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help -# from M. Frigo), as well as ac_pthread and hb_pthread macros posted by -# Alejandro Forero Cuervo to the autoconf macro repository. We are also -# grateful for the helpful feedback of numerous users. -# -# Updated for Autoconf 2.68 by Daniel Richard G. -# -# LICENSE -# -# Copyright (c) 2008 Steven G. Johnson -# Copyright (c) 2011 Daniel Richard G. -# -# 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 . -# -# As a special exception, the respective Autoconf Macro's copyright owner -# gives unlimited permission to copy, distribute and modify the configure -# scripts that are the output of Autoconf when processing the Macro. You -# need not follow the terms of the GNU General Public License when using -# or distributing such scripts, even though portions of the text of the -# Macro appear in them. The GNU General Public License (GPL) does govern -# all other use of the material that constitutes the Autoconf Macro. -# -# This special exception to the GPL applies to versions of the Autoconf -# Macro released by the Autoconf Archive. When you make and distribute a -# modified version of the Autoconf Macro, you may extend this special -# exception to the GPL to apply to your modified version as well. - -#serial 23 - -AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD]) -AC_DEFUN([AX_PTHREAD], [ -AC_REQUIRE([AC_CANONICAL_HOST]) -AC_REQUIRE([AC_PROG_CC]) -AC_REQUIRE([AC_PROG_SED]) -AC_LANG_PUSH([C]) -ax_pthread_ok=no - -# We used to check for pthread.h first, but this fails if pthread.h -# requires special compiler flags (e.g. on Tru64 or Sequent). -# It gets checked for in the link test anyway. - -# First of all, check if the user has set any of the PTHREAD_LIBS, -# etcetera environment variables, and if threads linking works using -# them: -if test "x$PTHREAD_CFLAGS$PTHREAD_LIBS" != "x"; then - ax_pthread_save_CC="$CC" - ax_pthread_save_CFLAGS="$CFLAGS" - ax_pthread_save_LIBS="$LIBS" - AS_IF([test "x$PTHREAD_CC" != "x"], [CC="$PTHREAD_CC"]) - CFLAGS="$CFLAGS $PTHREAD_CFLAGS" - LIBS="$PTHREAD_LIBS $LIBS" - AC_MSG_CHECKING([for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS]) - AC_LINK_IFELSE([AC_LANG_CALL([], [pthread_join])], [ax_pthread_ok=yes]) - AC_MSG_RESULT([$ax_pthread_ok]) - if test "x$ax_pthread_ok" = "xno"; then - PTHREAD_LIBS="" - PTHREAD_CFLAGS="" - fi - CC="$ax_pthread_save_CC" - CFLAGS="$ax_pthread_save_CFLAGS" - LIBS="$ax_pthread_save_LIBS" -fi - -# We must check for the threads library under a number of different -# names; the ordering is very important because some systems -# (e.g. DEC) have both -lpthread and -lpthreads, where one of the -# libraries is broken (non-POSIX). - -# Create a list of thread flags to try. Items starting with a "-" are -# C compiler flags, and other items are library names, except for "none" -# which indicates that we try without any flags at all, and "pthread-config" -# which is a program returning the flags for the Pth emulation library. - -ax_pthread_flags="pthreads none -Kthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" - -# The ordering *is* (sometimes) important. Some notes on the -# individual items follow: - -# pthreads: AIX (must check this before -lpthread) -# none: in case threads are in libc; should be tried before -Kthread and -# other compiler flags to prevent continual compiler warnings -# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) -# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads), Tru64 -# (Note: HP C rejects this with "bad form for `-t' option") -# -pthreads: Solaris/gcc (Note: HP C also rejects) -# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it -# doesn't hurt to check since this sometimes defines pthreads and -# -D_REENTRANT too), HP C (must be checked before -lpthread, which -# is present but should not be used directly; and before -mthreads, -# because the compiler interprets this as "-mt" + "-hreads") -# -mthreads: Mingw32/gcc, Lynx/gcc -# pthread: Linux, etcetera -# --thread-safe: KAI C++ -# pthread-config: use pthread-config program (for GNU Pth library) - -case $host_os in - - freebsd*) - - # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) - # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) - - ax_pthread_flags="-kthread lthread $ax_pthread_flags" - ;; - - hpux*) - - # From the cc(1) man page: "[-mt] Sets various -D flags to enable - # multi-threading and also sets -lpthread." - - ax_pthread_flags="-mt -pthread pthread $ax_pthread_flags" - ;; - - openedition*) - - # IBM z/OS requires a feature-test macro to be defined in order to - # enable POSIX threads at all, so give the user a hint if this is - # not set. (We don't define these ourselves, as they can affect - # other portions of the system API in unpredictable ways.) - - AC_EGREP_CPP([AX_PTHREAD_ZOS_MISSING], - [ -# if !defined(_OPEN_THREADS) && !defined(_UNIX03_THREADS) - AX_PTHREAD_ZOS_MISSING -# endif - ], - [AC_MSG_WARN([IBM z/OS requires -D_OPEN_THREADS or -D_UNIX03_THREADS to enable pthreads support.])]) - ;; - - solaris*) - - # On Solaris (at least, for some versions), libc contains stubbed - # (non-functional) versions of the pthreads routines, so link-based - # tests will erroneously succeed. (N.B.: The stubs are missing - # pthread_cleanup_push, or rather a function called by this macro, - # so we could check for that, but who knows whether they'll stub - # that too in a future libc.) So we'll check first for the - # standard Solaris way of linking pthreads (-mt -lpthread). - - ax_pthread_flags="-mt,pthread pthread $ax_pthread_flags" - ;; -esac - -# GCC generally uses -pthread, or -pthreads on some platforms (e.g. SPARC) - -AS_IF([test "x$GCC" = "xyes"], - [ax_pthread_flags="-pthread -pthreads $ax_pthread_flags"]) - -# The presence of a feature test macro requesting re-entrant function -# definitions is, on some systems, a strong hint that pthreads support is -# correctly enabled - -case $host_os in - darwin* | hpux* | linux* | osf* | solaris*) - ax_pthread_check_macro="_REENTRANT" - ;; - - aix*) - ax_pthread_check_macro="_THREAD_SAFE" - ;; - - *) - ax_pthread_check_macro="--" - ;; -esac -AS_IF([test "x$ax_pthread_check_macro" = "x--"], - [ax_pthread_check_cond=0], - [ax_pthread_check_cond="!defined($ax_pthread_check_macro)"]) - -# Are we compiling with Clang? - -AC_CACHE_CHECK([whether $CC is Clang], - [ax_cv_PTHREAD_CLANG], - [ax_cv_PTHREAD_CLANG=no - # Note that Autoconf sets GCC=yes for Clang as well as GCC - if test "x$GCC" = "xyes"; then - AC_EGREP_CPP([AX_PTHREAD_CC_IS_CLANG], - [/* Note: Clang 2.7 lacks __clang_[a-z]+__ */ -# if defined(__clang__) && defined(__llvm__) - AX_PTHREAD_CC_IS_CLANG -# endif - ], - [ax_cv_PTHREAD_CLANG=yes]) - fi - ]) -ax_pthread_clang="$ax_cv_PTHREAD_CLANG" - -ax_pthread_clang_warning=no - -# Clang needs special handling, because older versions handle the -pthread -# option in a rather... idiosyncratic way - -if test "x$ax_pthread_clang" = "xyes"; then - - # Clang takes -pthread; it has never supported any other flag - - # (Note 1: This will need to be revisited if a system that Clang - # supports has POSIX threads in a separate library. This tends not - # to be the way of modern systems, but it's conceivable.) - - # (Note 2: On some systems, notably Darwin, -pthread is not needed - # to get POSIX threads support; the API is always present and - # active. We could reasonably leave PTHREAD_CFLAGS empty. But - # -pthread does define _REENTRANT, and while the Darwin headers - # ignore this macro, third-party headers might not.) - - PTHREAD_CFLAGS="-pthread" - PTHREAD_LIBS= - - ax_pthread_ok=yes - - # However, older versions of Clang make a point of warning the user - # that, in an invocation where only linking and no compilation is - # taking place, the -pthread option has no effect ("argument unused - # during compilation"). They expect -pthread to be passed in only - # when source code is being compiled. - # - # Problem is, this is at odds with the way Automake and most other - # C build frameworks function, which is that the same flags used in - # compilation (CFLAGS) are also used in linking. Many systems - # supported by AX_PTHREAD require exactly this for POSIX threads - # support, and in fact it is often not straightforward to specify a - # flag that is used only in the compilation phase and not in - # linking. Such a scenario is extremely rare in practice. - # - # Even though use of the -pthread flag in linking would only print - # a warning, this can be a nuisance for well-run software projects - # that build with -Werror. So if the active version of Clang has - # this misfeature, we search for an option to squash it. - - AC_CACHE_CHECK([whether Clang needs flag to prevent "argument unused" warning when linking with -pthread], - [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG], - [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG=unknown - # Create an alternate version of $ac_link that compiles and - # links in two steps (.c -> .o, .o -> exe) instead of one - # (.c -> exe), because the warning occurs only in the second - # step - ax_pthread_save_ac_link="$ac_link" - ax_pthread_sed='s/conftest\.\$ac_ext/conftest.$ac_objext/g' - ax_pthread_link_step=`$as_echo "$ac_link" | sed "$ax_pthread_sed"` - ax_pthread_2step_ac_link="($ac_compile) && (echo ==== >&5) && ($ax_pthread_link_step)" - ax_pthread_save_CFLAGS="$CFLAGS" - for ax_pthread_try in '' -Qunused-arguments -Wno-unused-command-line-argument unknown; do - AS_IF([test "x$ax_pthread_try" = "xunknown"], [break]) - CFLAGS="-Werror -Wunknown-warning-option $ax_pthread_try -pthread $ax_pthread_save_CFLAGS" - ac_link="$ax_pthread_save_ac_link" - AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])], - [ac_link="$ax_pthread_2step_ac_link" - AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])], - [break]) - ]) - done - ac_link="$ax_pthread_save_ac_link" - CFLAGS="$ax_pthread_save_CFLAGS" - AS_IF([test "x$ax_pthread_try" = "x"], [ax_pthread_try=no]) - ax_cv_PTHREAD_CLANG_NO_WARN_FLAG="$ax_pthread_try" - ]) - - case "$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" in - no | unknown) ;; - *) PTHREAD_CFLAGS="$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG $PTHREAD_CFLAGS" ;; - esac - -fi # $ax_pthread_clang = yes - -if test "x$ax_pthread_ok" = "xno"; then -for ax_pthread_try_flag in $ax_pthread_flags; do - - case $ax_pthread_try_flag in - none) - AC_MSG_CHECKING([whether pthreads work without any flags]) - ;; - - -mt,pthread) - AC_MSG_CHECKING([whether pthreads work with -mt -lpthread]) - PTHREAD_CFLAGS="-mt" - PTHREAD_LIBS="-lpthread" - ;; - - -*) - AC_MSG_CHECKING([whether pthreads work with $ax_pthread_try_flag]) - PTHREAD_CFLAGS="$ax_pthread_try_flag" - ;; - - pthread-config) - AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no]) - AS_IF([test "x$ax_pthread_config" = "xno"], [continue]) - PTHREAD_CFLAGS="`pthread-config --cflags`" - PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" - ;; - - *) - AC_MSG_CHECKING([for the pthreads library -l$ax_pthread_try_flag]) - PTHREAD_LIBS="-l$ax_pthread_try_flag" - ;; - esac - - ax_pthread_save_CFLAGS="$CFLAGS" - ax_pthread_save_LIBS="$LIBS" - CFLAGS="$CFLAGS $PTHREAD_CFLAGS" - LIBS="$PTHREAD_LIBS $LIBS" - - # Check for various functions. We must include pthread.h, - # since some functions may be macros. (On the Sequent, we - # need a special flag -Kthread to make this header compile.) - # We check for pthread_join because it is in -lpthread on IRIX - # while pthread_create is in libc. We check for pthread_attr_init - # due to DEC craziness with -lpthreads. We check for - # pthread_cleanup_push because it is one of the few pthread - # functions on Solaris that doesn't have a non-functional libc stub. - # We try pthread_create on general principles. - - AC_LINK_IFELSE([AC_LANG_PROGRAM([#include -# if $ax_pthread_check_cond -# error "$ax_pthread_check_macro must be defined" -# endif - static void routine(void *a) { a = 0; } - static void *start_routine(void *a) { return a; }], - [pthread_t th; pthread_attr_t attr; - pthread_create(&th, 0, start_routine, 0); - pthread_join(th, 0); - pthread_attr_init(&attr); - pthread_cleanup_push(routine, 0); - pthread_cleanup_pop(0) /* ; */])], - [ax_pthread_ok=yes], - []) - - CFLAGS="$ax_pthread_save_CFLAGS" - LIBS="$ax_pthread_save_LIBS" - - AC_MSG_RESULT([$ax_pthread_ok]) - AS_IF([test "x$ax_pthread_ok" = "xyes"], [break]) - - PTHREAD_LIBS="" - PTHREAD_CFLAGS="" -done -fi - -# Various other checks: -if test "x$ax_pthread_ok" = "xyes"; then - ax_pthread_save_CFLAGS="$CFLAGS" - ax_pthread_save_LIBS="$LIBS" - CFLAGS="$CFLAGS $PTHREAD_CFLAGS" - LIBS="$PTHREAD_LIBS $LIBS" - - # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. - AC_CACHE_CHECK([for joinable pthread attribute], - [ax_cv_PTHREAD_JOINABLE_ATTR], - [ax_cv_PTHREAD_JOINABLE_ATTR=unknown - for ax_pthread_attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do - AC_LINK_IFELSE([AC_LANG_PROGRAM([#include ], - [int attr = $ax_pthread_attr; return attr /* ; */])], - [ax_cv_PTHREAD_JOINABLE_ATTR=$ax_pthread_attr; break], - []) - done - ]) - AS_IF([test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xunknown" && \ - test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xPTHREAD_CREATE_JOINABLE" && \ - test "x$ax_pthread_joinable_attr_defined" != "xyes"], - [AC_DEFINE_UNQUOTED([PTHREAD_CREATE_JOINABLE], - [$ax_cv_PTHREAD_JOINABLE_ATTR], - [Define to necessary symbol if this constant - uses a non-standard name on your system.]) - ax_pthread_joinable_attr_defined=yes - ]) - - AC_CACHE_CHECK([whether more special flags are required for pthreads], - [ax_cv_PTHREAD_SPECIAL_FLAGS], - [ax_cv_PTHREAD_SPECIAL_FLAGS=no - case $host_os in - solaris*) - ax_cv_PTHREAD_SPECIAL_FLAGS="-D_POSIX_PTHREAD_SEMANTICS" - ;; - esac - ]) - AS_IF([test "x$ax_cv_PTHREAD_SPECIAL_FLAGS" != "xno" && \ - test "x$ax_pthread_special_flags_added" != "xyes"], - [PTHREAD_CFLAGS="$ax_cv_PTHREAD_SPECIAL_FLAGS $PTHREAD_CFLAGS" - ax_pthread_special_flags_added=yes]) - - AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT], - [ax_cv_PTHREAD_PRIO_INHERIT], - [AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], - [[int i = PTHREAD_PRIO_INHERIT;]])], - [ax_cv_PTHREAD_PRIO_INHERIT=yes], - [ax_cv_PTHREAD_PRIO_INHERIT=no]) - ]) - AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes" && \ - test "x$ax_pthread_prio_inherit_defined" != "xyes"], - [AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], [1], [Have PTHREAD_PRIO_INHERIT.]) - ax_pthread_prio_inherit_defined=yes - ]) - - CFLAGS="$ax_pthread_save_CFLAGS" - LIBS="$ax_pthread_save_LIBS" - - # More AIX lossage: compile with *_r variant - if test "x$GCC" != "xyes"; then - case $host_os in - aix*) - AS_CASE(["x/$CC"], - [x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6], - [#handle absolute path differently from PATH based program lookup - AS_CASE(["x$CC"], - [x/*], - [AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])], - [AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])]) - ;; - esac - fi -fi - -test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" - -AC_SUBST([PTHREAD_LIBS]) -AC_SUBST([PTHREAD_CFLAGS]) -AC_SUBST([PTHREAD_CC]) - -# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: -if test "x$ax_pthread_ok" = "xyes"; then - ifelse([$1],,[AC_DEFINE([HAVE_PTHREAD],[1],[Define if you have POSIX threads libraries and header files.])],[$1]) - : -else - ax_pthread_ok=no - $2 -fi -AC_LANG_POP -])dnl AX_PTHREAD diff --git a/src/3rdparty/argon2/qmake/arch/arch.pro b/src/3rdparty/argon2/qmake/arch/arch.pro deleted file mode 100644 index b1a83150..00000000 --- a/src/3rdparty/argon2/qmake/arch/arch.pro +++ /dev/null @@ -1,3 +0,0 @@ -TEMPLATE = subdirs - -SUBDIRS += $$ARCH diff --git a/src/3rdparty/argon2/qmake/arch/generic/generic.pro b/src/3rdparty/argon2/qmake/arch/generic/generic.pro deleted file mode 100644 index 96710850..00000000 --- a/src/3rdparty/argon2/qmake/arch/generic/generic.pro +++ /dev/null @@ -1 +0,0 @@ -TEMPLATE = subdirs diff --git a/src/3rdparty/argon2/qmake/arch/x86_64/libargon2-avx2/libargon2-avx2.pro b/src/3rdparty/argon2/qmake/arch/x86_64/libargon2-avx2/libargon2-avx2.pro deleted file mode 100644 index 449dc508..00000000 --- a/src/3rdparty/argon2/qmake/arch/x86_64/libargon2-avx2/libargon2-avx2.pro +++ /dev/null @@ -1,23 +0,0 @@ -QT -= core gui - -TARGET = argon2-avx2 -TEMPLATE = lib -CONFIG += staticlib - -ARGON2_ROOT = ../../../.. - -INCLUDEPATH += \ - $$ARGON2_ROOT/include \ - $$ARGON2_ROOT/lib \ - $$ARGON2_ROOT/arch/$$ARCH/lib - -USE_AVX2 { - DEFINES += HAVE_AVX2 - QMAKE_CFLAGS += -mavx2 -} - -SOURCES += \ - $$ARGON2_ROOT/arch/x86_64/lib/argon2-avx2.c - -HEADERS += \ - $$ARGON2_ROOT/arch/x86_64/lib/argon2-avx2.h diff --git a/src/3rdparty/argon2/qmake/arch/x86_64/libargon2-avx512f/libargon2-avx512f.pro b/src/3rdparty/argon2/qmake/arch/x86_64/libargon2-avx512f/libargon2-avx512f.pro deleted file mode 100644 index a4a32e06..00000000 --- a/src/3rdparty/argon2/qmake/arch/x86_64/libargon2-avx512f/libargon2-avx512f.pro +++ /dev/null @@ -1,23 +0,0 @@ -QT -= core gui - -TARGET = argon2-avx512f -TEMPLATE = lib -CONFIG += staticlib - -ARGON2_ROOT = ../../../.. - -INCLUDEPATH += \ - $$ARGON2_ROOT/include \ - $$ARGON2_ROOT/lib \ - $$ARGON2_ROOT/arch/$$ARCH/lib - -USE_AVX512F { - DEFINES += HAVE_AVX512F - QMAKE_CFLAGS += -mavx512f -} - -SOURCES += \ - $$ARGON2_ROOT/arch/x86_64/lib/argon2-avx512f.c - -HEADERS += \ - $$ARGON2_ROOT/arch/x86_64/lib/argon2-avx512f.h diff --git a/src/3rdparty/argon2/qmake/arch/x86_64/libargon2-sse2/libargon2-sse2.pro b/src/3rdparty/argon2/qmake/arch/x86_64/libargon2-sse2/libargon2-sse2.pro deleted file mode 100644 index 49e7deee..00000000 --- a/src/3rdparty/argon2/qmake/arch/x86_64/libargon2-sse2/libargon2-sse2.pro +++ /dev/null @@ -1,24 +0,0 @@ -QT -= core gui - -TARGET = argon2-sse2 -TEMPLATE = lib -CONFIG += staticlib - -ARGON2_ROOT = ../../../.. - -INCLUDEPATH += \ - $$ARGON2_ROOT/include \ - $$ARGON2_ROOT/lib \ - $$ARGON2_ROOT/arch/$$ARCH/lib - -USE_SSE2 | USE_SSSE3 | USE_XOP | USE_AVX2 { - DEFINES += HAVE_SSE2 - QMAKE_CFLAGS += -msse2 -} - -SOURCES += \ - $$ARGON2_ROOT/arch/x86_64/lib/argon2-sse2.c - -HEADERS += \ - $$ARGON2_ROOT/arch/x86_64/lib/argon2-sse2.h \ - $$ARGON2_ROOT/arch/x86_64/lib/argon2-template-128.h diff --git a/src/3rdparty/argon2/qmake/arch/x86_64/libargon2-ssse3/libargon2-ssse3.pro b/src/3rdparty/argon2/qmake/arch/x86_64/libargon2-ssse3/libargon2-ssse3.pro deleted file mode 100644 index 53ebe6e5..00000000 --- a/src/3rdparty/argon2/qmake/arch/x86_64/libargon2-ssse3/libargon2-ssse3.pro +++ /dev/null @@ -1,24 +0,0 @@ -QT -= core gui - -TARGET = argon2-ssse3 -TEMPLATE = lib -CONFIG += staticlib - -ARGON2_ROOT = ../../../.. - -INCLUDEPATH += \ - $$ARGON2_ROOT/include \ - $$ARGON2_ROOT/lib \ - $$ARGON2_ROOT/arch/$$ARCH/lib - -USE_SSSE3 | USE_XOP | USE_AVX2 { - DEFINES += HAVE_SSSE3 - QMAKE_CFLAGS += -mssse3 -} - -SOURCES += \ - $$ARGON2_ROOT/arch/x86_64/lib/argon2-ssse3.c - -HEADERS += \ - $$ARGON2_ROOT/arch/x86_64/lib/argon2-ssse3.h \ - $$ARGON2_ROOT/arch/x86_64/lib/argon2-template-128.h diff --git a/src/3rdparty/argon2/qmake/arch/x86_64/libargon2-xop/libargon2-xop.pro b/src/3rdparty/argon2/qmake/arch/x86_64/libargon2-xop/libargon2-xop.pro deleted file mode 100644 index 85517dce..00000000 --- a/src/3rdparty/argon2/qmake/arch/x86_64/libargon2-xop/libargon2-xop.pro +++ /dev/null @@ -1,24 +0,0 @@ -QT -= core gui - -TARGET = argon2-xop -TEMPLATE = lib -CONFIG += staticlib - -ARGON2_ROOT = ../../../.. - -INCLUDEPATH += \ - $$ARGON2_ROOT/include \ - $$ARGON2_ROOT/lib \ - $$ARGON2_ROOT/arch/$$ARCH/lib - -USE_XOP { - DEFINES += HAVE_XOP - QMAKE_CFLAGS += -mxop -} - -SOURCES += \ - $$ARGON2_ROOT/arch/x86_64/lib/argon2-xop.c - -HEADERS += \ - $$ARGON2_ROOT/arch/x86_64/lib/argon2-xop.h \ - $$ARGON2_ROOT/arch/x86_64/lib/argon2-template-128.h diff --git a/src/3rdparty/argon2/qmake/arch/x86_64/x86_64.pro b/src/3rdparty/argon2/qmake/arch/x86_64/x86_64.pro deleted file mode 100644 index b3cfe029..00000000 --- a/src/3rdparty/argon2/qmake/arch/x86_64/x86_64.pro +++ /dev/null @@ -1,8 +0,0 @@ -TEMPLATE = subdirs - -SUBDIRS += \ - libargon2-sse2 \ - libargon2-ssse3 \ - libargon2-xop \ - libargon2-avx2 \ - libargon2-avx512f diff --git a/src/3rdparty/argon2/qmake/argon2-bench2/argon2-bench2.pro b/src/3rdparty/argon2/qmake/argon2-bench2/argon2-bench2.pro deleted file mode 100644 index 1e858d42..00000000 --- a/src/3rdparty/argon2/qmake/argon2-bench2/argon2-bench2.pro +++ /dev/null @@ -1,19 +0,0 @@ -TEMPLATE = app -CONFIG += console c++11 -CONFIG -= app_bundle -CONFIG -= qt - -ARGON2_ROOT = ../.. - -SOURCES += \ - $$ARGON2_ROOT/src/bench2.c - -HEADERS += \ - $$ARGON2_ROOT/src/timing.h - -win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../libargon2/release/ -largon2 -else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../libargon2/debug/ -largon2 -else:unix: LIBS += -L$$OUT_PWD/../libargon2/ -largon2 - -INCLUDEPATH += $$PWD/../../include -DEPENDPATH += $$PWD/../../include diff --git a/src/3rdparty/argon2/qmake/argon2-genkat/argon2-genkat.pro b/src/3rdparty/argon2/qmake/argon2-genkat/argon2-genkat.pro deleted file mode 100644 index c397efec..00000000 --- a/src/3rdparty/argon2/qmake/argon2-genkat/argon2-genkat.pro +++ /dev/null @@ -1,16 +0,0 @@ -TEMPLATE = app -CONFIG += console c++11 -CONFIG -= app_bundle -CONFIG -= qt - -ARGON2_ROOT = ../.. - -SOURCES += \ - $$ARGON2_ROOT/src/genkat.c - -win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../libargon2/release/ -largon2 -else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../libargon2/debug/ -largon2 -else:unix: LIBS += -L$$OUT_PWD/../libargon2/ -largon2 - -INCLUDEPATH += $$PWD/../../include -DEPENDPATH += $$PWD/../../include diff --git a/src/3rdparty/argon2/qmake/argon2-test/argon2-test.pro b/src/3rdparty/argon2/qmake/argon2-test/argon2-test.pro deleted file mode 100644 index e0d23c45..00000000 --- a/src/3rdparty/argon2/qmake/argon2-test/argon2-test.pro +++ /dev/null @@ -1,16 +0,0 @@ -TEMPLATE = app -CONFIG += console c++11 -CONFIG -= app_bundle -CONFIG -= qt - -ARGON2_ROOT = ../.. - -SOURCES += \ - $$ARGON2_ROOT/tests/test.c - -win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../libargon2/release/ -largon2 -else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../libargon2/debug/ -largon2 -else:unix: LIBS += -L$$OUT_PWD/../libargon2/ -largon2 - -INCLUDEPATH += $$PWD/../../include -DEPENDPATH += $$PWD/../../include diff --git a/src/3rdparty/argon2/qmake/argon2.pro b/src/3rdparty/argon2/qmake/argon2.pro deleted file mode 100644 index 0949d39c..00000000 --- a/src/3rdparty/argon2/qmake/argon2.pro +++ /dev/null @@ -1,9 +0,0 @@ -TEMPLATE = subdirs - -SUBDIRS += \ - arch \ - libargon2 \ - argon2 \ - argon2-genkat \ - argon2-bench2 \ - argon2-test diff --git a/src/3rdparty/argon2/qmake/argon2/argon2.pro b/src/3rdparty/argon2/qmake/argon2/argon2.pro deleted file mode 100644 index ff8c2049..00000000 --- a/src/3rdparty/argon2/qmake/argon2/argon2.pro +++ /dev/null @@ -1,18 +0,0 @@ -TEMPLATE = app -CONFIG += console c++11 -CONFIG -= app_bundle -CONFIG -= qt - -ARGON2_ROOT = ../.. - -SOURCES += \ - $$ARGON2_ROOT/src/run.c - -win32: DEFINES += argon2_EXPORT - -win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../libargon2/release/ -largon2 -else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../libargon2/debug/ -largon2 -else:unix: LIBS += -L$$OUT_PWD/../libargon2/ -largon2 - -INCLUDEPATH += $$PWD/../../include $$PWD/../../lib -DEPENDPATH += $$PWD/../../include $$PWD/../../lib diff --git a/src/3rdparty/argon2/qmake/libargon2/libargon2.pro b/src/3rdparty/argon2/qmake/libargon2/libargon2.pro deleted file mode 100644 index 77846168..00000000 --- a/src/3rdparty/argon2/qmake/libargon2/libargon2.pro +++ /dev/null @@ -1,119 +0,0 @@ -#------------------------------------------------- -# -# Project created by QtCreator 2016-08-08T17:43:00 -# -#------------------------------------------------- - -QT -= core gui - -TARGET = argon2 -TEMPLATE = lib - -ARGON2_ROOT = ../.. - -INCLUDEPATH += \ - $$ARGON2_ROOT/include \ - $$ARGON2_ROOT/lib - -SOURCES += \ - $$ARGON2_ROOT/lib/argon2.c \ - $$ARGON2_ROOT/lib/core.c \ - $$ARGON2_ROOT/lib/encoding.c \ - $$ARGON2_ROOT/lib/genkat.c \ - $$ARGON2_ROOT/lib/impl-select.c \ - $$ARGON2_ROOT/lib/thread.c \ - $$ARGON2_ROOT/lib/blake2/blake2.c - -HEADERS += \ - $$ARGON2_ROOT/include/argon2.h \ - $$ARGON2_ROOT/lib/argon2-template-64.h \ - $$ARGON2_ROOT/lib/core.h \ - $$ARGON2_ROOT/lib/encoding.h \ - $$ARGON2_ROOT/lib/genkat.h \ - $$ARGON2_ROOT/lib/impl-select.h \ - $$ARGON2_ROOT/lib/thread.h \ - $$ARGON2_ROOT/lib/blake2/blake2.h \ - $$ARGON2_ROOT/lib/blake2/blake2-impl.h - -equals(ARCH, x86_64) { - SOURCES += \ - $$ARGON2_ROOT/arch/$$ARCH/lib/cpu-flags.c \ - $$ARGON2_ROOT/arch/$$ARCH/lib/argon2-arch.c - - HEADERS += \ - $$ARGON2_ROOT/arch/$$ARCH/lib/cpu-flags.h - - # libargon2-sse2.a: - win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../arch/x86_64/libargon2-sse2/release/ -largon2-sse2 - else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../arch/x86_64/libargon2-sse2/debug/ -largon2-sse2 - else:unix: LIBS += -L$$OUT_PWD/../arch/x86_64/libargon2-sse2/ -largon2-sse2 - - DEPENDPATH += $$PWD/../arch/x86_64/libargon2-sse2 - - win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../arch/x86_64/libargon2-sse2/release/libargon2-sse2.a - else:win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../arch/x86_64/libargon2-sse2/debug/libargon2-sse2.a - else:win32:!win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../arch/x86_64/libargon2-sse2/release/argon2-sse2.lib - else:win32:!win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../arch/x86_64/libargon2-sse2/debug/argon2-sse2.lib - else:unix: PRE_TARGETDEPS += $$OUT_PWD/../arch/x86_64/libargon2-sse2/libargon2-sse2.a - - # libargon2-ssse3.a: - win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../arch/x86_64/libargon2-ssse3/release/ -largon2-ssse3 - else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../arch/x86_64/libargon2-ssse3/debug/ -largon2-ssse3 - else:unix: LIBS += -L$$OUT_PWD/../arch/x86_64/libargon2-ssse3/ -largon2-ssse3 - - DEPENDPATH += $$PWD/../arch/x86_64/libargon2-ssse3 - - win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../arch/x86_64/libargon2-ssse3/release/libargon2-ssse3.a - else:win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../arch/x86_64/libargon2-ssse3/debug/libargon2-ssse3.a - else:win32:!win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../arch/x86_64/libargon2-ssse3/release/argon2-ssse3.lib - else:win32:!win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../arch/x86_64/libargon2-ssse3/debug/argon2-ssse3.lib - else:unix: PRE_TARGETDEPS += $$OUT_PWD/../arch/x86_64/libargon2-ssse3/libargon2-ssse3.a - - # libargon2-xop.a: - win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../arch/x86_64/libargon2-xop/release/ -largon2-xop - else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../arch/x86_64/libargon2-xop/debug/ -largon2-xop - else:unix: LIBS += -L$$OUT_PWD/../arch/x86_64/libargon2-xop/ -largon2-xop - - DEPENDPATH += $$PWD/../arch/x86_64/libargon2-xop - - win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../arch/x86_64/libargon2-xop/release/libargon2-xop.a - else:win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../arch/x86_64/libargon2-xop/debug/libargon2-xop.a - else:win32:!win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../arch/x86_64/libargon2-xop/release/argon2-xop.lib - else:win32:!win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../arch/x86_64/libargon2-xop/debug/argon2-xop.lib - else:unix: PRE_TARGETDEPS += $$OUT_PWD/../arch/x86_64/libargon2-xop/libargon2-xop.a - - # libargon2-avx2.a: - win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../arch/x86_64/libargon2-avx2/release/ -largon2-avx2 - else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../arch/x86_64/libargon2-avx2/debug/ -largon2-avx2 - else:unix: LIBS += -L$$OUT_PWD/../arch/x86_64/libargon2-avx2/ -largon2-avx2 - - DEPENDPATH += $$PWD/../arch/x86_64/libargon2-avx2 - - win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../arch/x86_64/libargon2-avx2/release/libargon2-avx2.a - else:win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../arch/x86_64/libargon2-avx2/debug/libargon2-avx2.a - else:win32:!win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../arch/x86_64/libargon2-avx2/release/argon2-avx2.lib - else:win32:!win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../arch/x86_64/libargon2-avx2/debug/argon2-avx2.lib - else:unix: PRE_TARGETDEPS += $$OUT_PWD/../arch/x86_64/libargon2-avx2/libargon2-avx2.a - - # libargon2-avx512f.a: - win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../arch/x86_64/libargon2-avx512f/release/ -largon2-avx512f - else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../arch/x86_64/libargon2-avx512f/debug/ -largon2-avx512f - else:unix: LIBS += -L$$OUT_PWD/../arch/x86_64/libargon2-avx512f/ -largon2-avx512f - - DEPENDPATH += $$PWD/../arch/x86_64/libargon2-avx512f - - win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../arch/x86_64/libargon2-avx512f/release/libargon2-avx512f.a - else:win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../arch/x86_64/libargon2-avx512f/debug/libargon2-avx512f.a - else:win32:!win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../arch/x86_64/libargon2-avx512f/release/argon2-avx512f.lib - else:win32:!win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../arch/x86_64/libargon2-avx512f/debug/argon2-avx512f.lib - else:unix: PRE_TARGETDEPS += $$OUT_PWD/../arch/x86_64/libargon2-avx512f/libargon2-avx512f.a -} -equals(ARCH, generic) { - SOURCES += \ - $$ARGON2_ROOT/arch/$$ARCH/lib/argon2-arch.c -} - -unix { - target.path = /usr/lib - INSTALLS += target -} diff --git a/src/3rdparty/argon2/scripts/metacentrum/start-all-benchmarks.sh b/src/3rdparty/argon2/scripts/metacentrum/start-all-benchmarks.sh deleted file mode 100644 index 47e925c6..00000000 --- a/src/3rdparty/argon2/scripts/metacentrum/start-all-benchmarks.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash - -dirname="$(dirname "$0")" - -cd "$dirname" || exit 1 - -./start-benchmark.sh luna -./start-benchmark.sh lex '' '' '' '' '' backfill -./start-benchmark.sh mandos -./start-benchmark.sh zubat -PBS_SERVER=wagap.cerit-sc.cz \ - ./start-benchmark.sh zapat '' '' '' '' '' default@wagap.cerit-sc.cz diff --git a/src/3rdparty/argon2/scripts/metacentrum/start-benchmark.sh b/src/3rdparty/argon2/scripts/metacentrum/start-benchmark.sh deleted file mode 100644 index beedf748..00000000 --- a/src/3rdparty/argon2/scripts/metacentrum/start-benchmark.sh +++ /dev/null @@ -1,75 +0,0 @@ -#!/bin/bash - -machine="$1" -max_t_cost="$2" -max_m_cost="$3" -max_lanes="$4" -branch="$5" -duration="$6" -queue="$7" -run_tests="$8" - -if [ -z "$machine" ]; then - echo "ERROR: Machine must be specified!" 1>&2 - exit 1 -fi - -if [ -z "$max_t_cost" ]; then - max_t_cost=16 -fi - -if [ -z "$max_m_cost" ]; then - max_m_cost=$((8 * 1024 * 1024)) -fi - -if [ -z "$max_lanes" ]; then - max_lanes=16 -fi - -if [ -z "$branch" ]; then - branch='master' -fi - -if [ -z "$duration" ]; then - duration=2h -fi - -REPO_URL='https://github.com/WOnder93/argon2.git' - -dest_dir="$(pwd)" - -task_file="$(mktemp)" - -cat >$task_file <"$dest_dir/\$PBS_JOBID/benchmark-$machine-$branch.csv" -EOF - -qsub "$task_file" - -rm -f "$task_file" diff --git a/src/3rdparty/argon2/scripts/run-benchmark.sh b/src/3rdparty/argon2/scripts/run-benchmark.sh deleted file mode 100644 index f023b8e8..00000000 --- a/src/3rdparty/argon2/scripts/run-benchmark.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/bash - -max_t_cost="$1" -max_m_cost="$2" -max_lanes="$3" - -if [ -z "$max_t_cost" ]; then - echo "ERROR: Maximum time cost must be specified!" 1>&2 - exit 1 -fi - -if [ -z "$max_m_cost" ]; then - echo "ERROR: Maximum memory cost must be specified!" 1>&2 - exit 1 -fi - -if [ -z "$max_lanes" ]; then - echo "ERROR: Maximum number of lanes must be specified!" 1>&2 - exit 1 -fi - -dirname="$(dirname "$0")" - -cd "$dirname/.." || exit 1 - -echo "t_cost,m_cost,lanes,ms_i,ms_d,ms_id" -stdbuf -oL ./argon2-bench2 $max_t_cost $max_m_cost $max_lanes | -stdbuf -oL tail -n +2 | -while read line; do - print_comma=0 - for x in $line; do - if [ $print_comma -eq 1 ]; then - echo -n "," - else - print_comma=1 - fi - echo -n "$x" - done - echo -done diff --git a/src/3rdparty/argon2/src/bench2.c b/src/3rdparty/argon2/src/bench2.c deleted file mode 100644 index 0d7d7fca..00000000 --- a/src/3rdparty/argon2/src/bench2.c +++ /dev/null @@ -1,179 +0,0 @@ -#include -#include -#include -#include -#include - -#include "argon2.h" - -#include "timing.h" - -#define ARGON2_BLOCK_SIZE 1024 - -#define BENCH_MAX_T_COST 16 -#define BENCH_MAX_M_COST (1024 * 1024) -#define BENCH_MAX_THREADS 8 -#define BENCH_MIN_PASSES (1024 * 1024) -#define BENCH_MAX_SAMPLES 128 - -#define BENCH_OUTLEN 16 -#define BENCH_INLEN 16 - -static double pick_min(const double *samples, size_t count) -{ - size_t i; - double min = INFINITY; - for (i = 0; i < count; i++) { - if (samples[i] < min) { - min = samples[i]; - } - } - return min; -} - -static int benchmark(void *memory, size_t memory_size, - uint32_t t_cost, uint32_t m_cost, uint32_t p) -{ - static const unsigned char PASSWORD[BENCH_OUTLEN] = { 0 }; - static const unsigned char SALT[BENCH_INLEN] = { 1 }; - - unsigned char out[BENCH_OUTLEN]; - struct timestamp start, end; - double ms_d[BENCH_MAX_SAMPLES]; - double ms_i[BENCH_MAX_SAMPLES]; - double ms_id[BENCH_MAX_SAMPLES]; - - double ms_d_final, ms_i_final, ms_id_final; - unsigned int i, bench_samples; - argon2_context ctx; - - int res; - - ctx.out = out; - ctx.outlen = sizeof(out); - ctx.pwd = (uint8_t *)PASSWORD; - ctx.pwdlen = sizeof(PASSWORD); - ctx.salt = (uint8_t *)SALT; - ctx.saltlen = sizeof(SALT); - ctx.secret = NULL; - ctx.secretlen = 0; - ctx.ad = NULL; - ctx.adlen = 0; - ctx.t_cost = t_cost; - ctx.m_cost = m_cost; - ctx.lanes = ctx.threads = p; - ctx.version = ARGON2_VERSION_NUMBER; - ctx.allocate_cbk = NULL; - ctx.free_cbk = NULL; - ctx.flags = ARGON2_DEFAULT_FLAGS; - - bench_samples = (BENCH_MIN_PASSES * p) / (t_cost * m_cost); - bench_samples += (BENCH_MIN_PASSES * p) % (t_cost * m_cost) != 0; - - if (bench_samples > BENCH_MAX_SAMPLES) { - bench_samples = BENCH_MAX_SAMPLES; - } - for (i = 0; i < bench_samples; i++) { - timestamp_store(&start); - res = argon2_ctx_mem(&ctx, Argon2_d, memory, memory_size); - timestamp_store(&end); - if (res != ARGON2_OK) { - return res; - } - - ms_d[i] = timestamp_span_ms(&start, &end); - } - - for (i = 0; i < bench_samples; i++) { - timestamp_store(&start); - res = argon2_ctx_mem(&ctx, Argon2_i, memory, memory_size); - timestamp_store(&end); - if (res != ARGON2_OK) { - return res; - } - - ms_i[i] = timestamp_span_ms(&start, &end); - } - - for (i = 0; i < bench_samples; i++) { - timestamp_store(&start); - res = argon2_ctx_mem(&ctx, Argon2_id, memory, memory_size); - timestamp_store(&end); - if (res != ARGON2_OK) { - return res; - } - - ms_id[i] = timestamp_span_ms(&start, &end); - } - - ms_d_final = pick_min(ms_d, bench_samples); - ms_i_final = pick_min(ms_i, bench_samples); - ms_id_final = pick_min(ms_id, bench_samples); - - printf("%8lu%16lu%8lu%16.6lf%16.6lf%16.6lf\n", - (unsigned long)t_cost, (unsigned long)m_cost, (unsigned long)p, - ms_d_final, ms_i_final, ms_id_final); - return 0; -} - -int main(int argc, const char * const *argv) -{ - uint32_t max_t_cost = BENCH_MAX_T_COST; - uint32_t max_m_cost = BENCH_MAX_M_COST; - uint32_t max_p = BENCH_MAX_THREADS; - uint32_t t_cost, m_cost, p; - char *end; - int res; - - if (argc >= 2) { - max_t_cost = strtoul(argv[1], &end, 10); - if (end == argv[1]) { - fprintf(stderr, "ERROR: Invalid number format!\n"); - return 1; - } - } - - if (argc >= 3) { - max_m_cost = strtoul(argv[2], &end, 10); - if (end == argv[2]) { - fprintf(stderr, "ERROR: Invalid number format!\n"); - return 1; - } - } - - if (argc >= 4) { - max_p = strtoul(argv[3], &end, 10); - if (end == argv[3]) { - fprintf(stderr, "ERROR: Invalid number format!\n"); - return 1; - } - } - - argon2_select_impl(stderr, "[libargon2] "); - - size_t memory_size = (size_t)max_m_cost * (size_t)ARGON2_BLOCK_SIZE; - void *memory = malloc(memory_size); - if (memory == NULL) { - fprintf(stderr, "ERROR: Memory allocation failed!\n"); - return 1; - } - /* make sure the whole memory gets mapped to physical pages: */ - memset(memory, 0xAB, memory_size); - - printf("%8s%16s%8s%16s%16s%16s\n", "t_cost", "m_cost", "threads", - "Argon2d (ms)", "Argon2i (ms)", "Argon2id (ms)"); - for (t_cost = 1; t_cost <= max_t_cost; t_cost *= 2) { - uint32_t min_m_cost = max_p * ARGON2_SYNC_POINTS * 2; - for (m_cost = min_m_cost; m_cost <= max_m_cost; m_cost *= 2) { - for (p = 1; p <= max_p; p *= 2) { - res = benchmark(memory, memory_size, t_cost, m_cost, p); - if (res != 0) { - free(memory); - return res; - } - } - } - } - free(memory); - return 0; -} diff --git a/src/3rdparty/argon2/src/genkat.c b/src/3rdparty/argon2/src/genkat.c deleted file mode 100644 index 7295c985..00000000 --- a/src/3rdparty/argon2/src/genkat.c +++ /dev/null @@ -1,90 +0,0 @@ -#include -#include -#include - -#include "argon2.h" - -static void fatal(const char *error) { - fprintf(stderr, "Error: %s\n", error); - exit(1); -} - -static void generate_testvectors(argon2_type type, const uint32_t version) { -#define TEST_OUTLEN 32 -#define TEST_PWDLEN 32 -#define TEST_SALTLEN 16 -#define TEST_SECRETLEN 8 -#define TEST_ADLEN 12 - argon2_context context; - - unsigned char out[TEST_OUTLEN]; - unsigned char pwd[TEST_PWDLEN]; - unsigned char salt[TEST_SALTLEN]; - unsigned char secret[TEST_SECRETLEN]; - unsigned char ad[TEST_ADLEN]; - const allocate_fptr myown_allocator = NULL; - const deallocate_fptr myown_deallocator = NULL; - - unsigned t_cost = 3; - unsigned m_cost = 32; - unsigned lanes = 4; - - memset(pwd, 1, TEST_OUTLEN); - memset(salt, 2, TEST_SALTLEN); - memset(secret, 3, TEST_SECRETLEN); - memset(ad, 4, TEST_ADLEN); - - context.out = out; - context.outlen = TEST_OUTLEN; - context.version = version; - context.pwd = pwd; - context.pwdlen = TEST_PWDLEN; - context.salt = salt; - context.saltlen = TEST_SALTLEN; - context.secret = secret; - context.secretlen = TEST_SECRETLEN; - context.ad = ad; - context.adlen = TEST_ADLEN; - context.t_cost = t_cost; - context.m_cost = m_cost; - context.lanes = lanes; - context.threads = lanes; - context.allocate_cbk = myown_allocator; - context.free_cbk = myown_deallocator; - context.flags = ARGON2_DEFAULT_FLAGS | ARGON2_FLAG_GENKAT; - -#undef TEST_OUTLEN -#undef TEST_PWDLEN -#undef TEST_SALTLEN -#undef TEST_SECRETLEN -#undef TEST_ADLEN - - argon2_ctx(&context, type); -} - -int main(int argc, char *argv[]) { - /* Get and check Argon2 type */ - const char *type_str = (argc > 1) ? argv[1] : "i"; - argon2_type type = Argon2_i; - uint32_t version = ARGON2_VERSION_NUMBER; - if (!strcmp(type_str, "d")) { - type = Argon2_d; - } else if (!strcmp(type_str, "i")) { - type = Argon2_i; - } else if (!strcmp(type_str, "id")) { - type = Argon2_id; - } else { - fatal("wrong Argon2 type"); - } - - /* Get and check Argon2 version number */ - if(argc > 2) { - version = strtoul(argv[2], NULL, 10); - } - if (ARGON2_VERSION_10 != version && ARGON2_VERSION_NUMBER != version) { - fatal("wrong Argon2 version number"); - } - - generate_testvectors(type, version); - return ARGON2_OK; -} diff --git a/src/3rdparty/argon2/src/run.c b/src/3rdparty/argon2/src/run.c deleted file mode 100644 index 9588a632..00000000 --- a/src/3rdparty/argon2/src/run.c +++ /dev/null @@ -1,315 +0,0 @@ -/* - * Argon2 source code package - * - * Written by Daniel Dinu and Dmitry Khovratovich, 2015 - * - * This work is licensed under a Creative Commons CC0 1.0 License/Waiver. - * - * You should have received a copy of the CC0 Public Domain Dedication along - * with - * this software. If not, see - * . - */ - -#define _GNU_SOURCE 1 - -#include -#include -#include -#include -#include -#include - -#include "argon2.h" -#include "core.h" - -#define T_COST_DEF 3 -#define LOG_M_COST_DEF 12 /* 2^12 = 4 MiB */ -#define LANES_DEF 1 -#define THREADS_DEF 1 -#define OUTLEN_DEF 32 -#define MAX_PASS_LEN 128 - -#define UNUSED_PARAMETER(x) (void)(x) - -static void usage(const char *cmd) { - printf("Usage: %s [-h] salt [-i|-d|-id] [-t iterations] [-m memory] " - "[-p parallelism] [-l hash length] [-e|-r] [-v (10|13)]\n", - cmd); - printf("\tPassword is read from stdin\n"); - printf("Parameters:\n"); - printf("\tsalt\t\tThe salt to use, at least 8 characters\n"); - printf("\t-i\t\tUse Argon2i (this is the default)\n"); - printf("\t-d\t\tUse Argon2d instead of Argon2i\n"); - printf("\t-id\t\tUse Argon2id instead of Argon2i\n"); - printf("\t-t N\t\tSets the number of iterations to N (default = %d)\n", - T_COST_DEF); - printf("\t-m N\t\tSets the memory usage of 2^N KiB (default %d)\n", - LOG_M_COST_DEF); - printf("\t-p N\t\tSets parallelism to N threads (default %d)\n", - THREADS_DEF); - printf("\t-l N\t\tSets hash output length to N bytes (default %d)\n", - OUTLEN_DEF); - printf("\t-e\t\tOutput only encoded hash\n"); - printf("\t-r\t\tOutput only the raw bytes of the hash\n"); - printf("\t-v (10|13)\tArgon2 version (defaults to the most recent version, " - "currently %x)\n", ARGON2_VERSION_NUMBER); - printf("\t-h\t\tPrint %s usage\n", cmd); -} - -static void fatal(const char *error) { - fprintf(stderr, "Error: %s\n", error); - exit(1); -} - -static void print_hex(uint8_t *bytes, size_t bytes_len) { - size_t i; - for (i = 0; i < bytes_len; ++i) { - printf("%02x", bytes[i]); - } - printf("\n"); -} - -/* -Runs Argon2 with certain inputs and parameters, inputs not cleared. Prints the -Base64-encoded hash string -@out output array with at least 32 bytes allocated -@pwd NULL-terminated string, presumably from argv[] -@salt salt array -@t_cost number of iterations -@m_cost amount of requested memory in KB -@lanes amount of requested parallelism -@threads actual parallelism -@type Argon2 type we want to run -@encoded_only display only the encoded hash -@raw_only display only the hexadecimal of the hash -@version Argon2 version -*/ -static void run(uint32_t outlen, char *pwd, char *salt, uint32_t t_cost, - uint32_t m_cost, uint32_t lanes, uint32_t threads, - argon2_type type, int encoded_only, int raw_only, - uint32_t version) { - clock_t start_time, stop_time; - size_t pwdlen, saltlen, encodedlen; - int result; - unsigned char * out = NULL; - char * encoded = NULL; - - start_time = clock(); - - if (!pwd) { - fatal("password missing"); - } - - if (!salt) { - clear_internal_memory(pwd, strlen(pwd)); - fatal("salt missing"); - } - - pwdlen = strlen(pwd); - saltlen = strlen(salt); - if(UINT32_MAX < saltlen) { - fatal("salt is too long"); - } - - UNUSED_PARAMETER(lanes); - - out = malloc(outlen + 1); - if (!out) { - clear_internal_memory(pwd, strlen(pwd)); - fatal("could not allocate memory for output"); - } - - encodedlen = argon2_encodedlen(t_cost, m_cost, lanes, (uint32_t)saltlen, outlen, type); - encoded = malloc(encodedlen + 1); - if (!encoded) { - clear_internal_memory(pwd, strlen(pwd)); - fatal("could not allocate memory for hash"); - } - - result = argon2_hash(t_cost, m_cost, threads, pwd, pwdlen, salt, saltlen, - out, outlen, encoded, encodedlen, type, version); - if (result != ARGON2_OK) - fatal(argon2_error_message(result)); - - stop_time = clock(); - - if (encoded_only) - puts(encoded); - - if (raw_only) - print_hex(out, outlen); - - if (encoded_only || raw_only) { - free(out); - free(encoded); - return; - } - - printf("Hash:\t\t"); - print_hex(out, outlen); - free(out); - - printf("Encoded:\t%s\n", encoded); - - printf("%2.3f seconds\n", - ((double)stop_time - start_time) / (CLOCKS_PER_SEC)); - - result = argon2_verify(encoded, pwd, pwdlen, type); - if (result != ARGON2_OK) - fatal(argon2_error_message(result)); - printf("Verification ok\n"); - free(encoded); -} - -int main(int argc, char *argv[]) { - uint32_t outlen = OUTLEN_DEF; - uint32_t m_cost = 1 << LOG_M_COST_DEF; - uint32_t t_cost = T_COST_DEF; - uint32_t lanes = LANES_DEF; - uint32_t threads = THREADS_DEF; - argon2_type type = Argon2_i; /* Argon2i is the default type */ - int types_specified = 0; - int encoded_only = 0; - int raw_only = 0; - uint32_t version = ARGON2_VERSION_NUMBER; - int i; - size_t n; - char pwd[MAX_PASS_LEN], *salt; - - if (argc < 2) { - usage(argv[0]); - return ARGON2_MISSING_ARGS; - } else if (argc >= 2 && strcmp(argv[1], "-h") == 0) { - usage(argv[0]); - return 1; - } - - argon2_select_impl(stderr, "[libargon2] "); - - /* get password from stdin */ - n = fread(pwd, 1, sizeof pwd - 1, stdin); - if(n < 1) { - fatal("no password read"); - } - if(n == MAX_PASS_LEN-1) { - fatal("Provided password longer than supported in command line utility"); - } - - pwd[n] = '\0'; - if (pwd[n - 1] == '\n') { - pwd[n - 1] = '\0'; - } - - salt = argv[1]; - - /* parse options */ - for (i = 2; i < argc; i++) { - const char *a = argv[i]; - unsigned long input = 0; - if (!strcmp(a, "-h")) { - usage(argv[0]); - return 1; - } else if (!strcmp(a, "-m")) { - if (i < argc - 1) { - i++; - input = strtoul(argv[i], NULL, 10); - if (input == 0 || input == ULONG_MAX || - input > ARGON2_MAX_MEMORY_BITS) { - fatal("bad numeric input for -m"); - } - m_cost = ARGON2_MIN(UINT64_C(1) << input, UINT32_C(0xFFFFFFFF)); - if (m_cost > ARGON2_MAX_MEMORY) { - fatal("m_cost overflow"); - } - continue; - } else { - fatal("missing -m argument"); - } - } else if (!strcmp(a, "-t")) { - if (i < argc - 1) { - i++; - input = strtoul(argv[i], NULL, 10); - if (input == 0 || input == ULONG_MAX || - input > ARGON2_MAX_TIME) { - fatal("bad numeric input for -t"); - } - t_cost = input; - continue; - } else { - fatal("missing -t argument"); - } - } else if (!strcmp(a, "-p")) { - if (i < argc - 1) { - i++; - input = strtoul(argv[i], NULL, 10); - if (input == 0 || input == ULONG_MAX || - input > ARGON2_MAX_THREADS || input > ARGON2_MAX_LANES) { - fatal("bad numeric input for -p"); - } - threads = input; - lanes = threads; - continue; - } else { - fatal("missing -p argument"); - } - } else if (!strcmp(a, "-l")) { - if (i < argc - 1) { - i++; - input = strtoul(argv[i], NULL, 10); - outlen = input; - continue; - } else { - fatal("missing -l argument"); - } - } else if (!strcmp(a, "-i")) { - type = Argon2_i; - ++types_specified; - } else if (!strcmp(a, "-d")) { - type = Argon2_d; - ++types_specified; - } else if (!strcmp(a, "-id")) { - type = Argon2_id; - ++types_specified; - } else if (!strcmp(a, "-e")) { - encoded_only = 1; - } else if (!strcmp(a, "-r")) { - raw_only = 1; - } else if (!strcmp(a, "-v")) { - if (i < argc - 1) { - i++; - if (!strcmp(argv[i], "10")) { - version = ARGON2_VERSION_10; - } else if (!strcmp(argv[i], "13")) { - version = ARGON2_VERSION_13; - } else { - fatal("invalid Argon2 version"); - } - } else { - fatal("missing -v argument"); - } - } else { - fatal("unknown argument"); - } - } - - if (types_specified > 1) { - fatal("cannot specify multiple Argon2 types"); - } - - if(encoded_only && raw_only) - fatal("cannot provide both -e and -r"); - - if(!encoded_only && !raw_only) { - printf("Type:\t\t%s\n", argon2_type2string(type, 1)); - printf("Iterations:\t%" PRIu32 " \n", t_cost); - printf("Memory:\t\t%" PRIu32 " KiB\n", m_cost); - printf("Parallelism:\t%" PRIu32 " \n", lanes); - } - - run(outlen, pwd, salt, t_cost, m_cost, lanes, threads, type, - encoded_only, raw_only, version); - - return ARGON2_OK; -} - diff --git a/src/3rdparty/argon2/src/timing.h b/src/3rdparty/argon2/src/timing.h deleted file mode 100644 index 0e39a1f3..00000000 --- a/src/3rdparty/argon2/src/timing.h +++ /dev/null @@ -1,41 +0,0 @@ -#include - -#ifdef _POSIX_SOURCE -#include - -struct timestamp { - struct timespec time; -}; - -static inline void timestamp_store(struct timestamp *out) -{ - clock_gettime(CLOCK_MONOTONIC, &out->time); -} - -static inline double timestamp_span_ms(const struct timestamp *start, - const struct timestamp *end) -{ - double res = 0.0; - res += (end->time.tv_sec - start->time.tv_sec) * 1000.0; - res += (end->time.tv_nsec - start->time.tv_nsec) / 1000000.0; - return res; -} -#else -#include - -struct timestamp { - clock_t time; -}; - -static inline void timestamp_store(struct timestamp *out) -{ - out->time = clock(); -} - -static inline double timestamp_span_ms(const struct timestamp *start, - const struct timestamp *end) -{ - double res = (end->time - start->time) * 1000; - return res / CLOCKS_PER_SEC; -} -#endif diff --git a/src/3rdparty/argon2/tests/test.c b/src/3rdparty/argon2/tests/test.c deleted file mode 100644 index 1fe9f0e2..00000000 --- a/src/3rdparty/argon2/tests/test.c +++ /dev/null @@ -1,239 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include "argon2.h" - -#define OUT_LEN 32 -#define ENCODED_LEN 108 - -/* Test harness will assert: - * argon2_hash() returns ARGON2_OK - * HEX output matches expected - * encoded output matches expected - * argon2_verify() correctly verifies value - */ - -void hashtest(uint32_t version, uint32_t t, uint32_t m, uint32_t p, char *pwd, - char *salt, char *hexref, char *mcfref) { - unsigned char out[OUT_LEN]; - unsigned char hex_out[OUT_LEN * 2 + 4]; - char encoded[ENCODED_LEN]; - int ret, i; - - printf("Hash test: $v=%d t=%d, m=%d, p=%d, pass=%s, salt=%s: ", version, - t, m, p, pwd, salt); - - ret = argon2_hash(t, 1 << m, p, pwd, strlen(pwd), salt, strlen(salt), out, - OUT_LEN, encoded, ENCODED_LEN, Argon2_i, version); - assert(ret == ARGON2_OK); - - for (i = 0; i < OUT_LEN; ++i) - sprintf((char *)(hex_out + i * 2), "%02x", out[i]); - - assert(memcmp(hex_out, hexref, OUT_LEN * 2) == 0); - - if (ARGON2_VERSION_NUMBER == version) { - assert(memcmp(encoded, mcfref, strlen(mcfref)) == 0); - } - - ret = argon2_verify(encoded, pwd, strlen(pwd), Argon2_i); - assert(ret == ARGON2_OK); - ret = argon2_verify(mcfref, pwd, strlen(pwd), Argon2_i); - assert(ret == ARGON2_OK); - - printf("PASS\n"); -} - -int main() { - int ret; - unsigned char out[OUT_LEN]; - char const *msg; - int version; - - argon2_select_impl(stderr, "[libargon2] "); - - version = ARGON2_VERSION_10; - printf("Test Argon2i version number: %02x\n", version); - - /* Multiple test cases for various input values */ - hashtest(version, 2, 16, 1, "password", "somesalt", - "f6c4db4a54e2a370627aff3db6176b94a2a209a62c8e36152711802f7b30c694", - "$argon2i$m=65536,t=2,p=1$c29tZXNhbHQ" - "$9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ"); -#ifdef TEST_LARGE_RAM - hashtest(version, 2, 20, 1, "password", "somesalt", - "9690ec55d28d3ed32562f2e73ea62b02b018757643a2ae6e79528459de8106e9", - "$argon2i$m=1048576,t=2,p=1$c29tZXNhbHQ" - "$lpDsVdKNPtMlYvLnPqYrArAYdXZDoq5ueVKEWd6BBuk"); -#endif - hashtest(version, 2, 18, 1, "password", "somesalt", - "3e689aaa3d28a77cf2bc72a51ac53166761751182f1ee292e3f677a7da4c2467", - "$argon2i$m=262144,t=2,p=1$c29tZXNhbHQ" - "$Pmiaqj0op3zyvHKlGsUxZnYXURgvHuKS4/Z3p9pMJGc"); - hashtest(version, 2, 8, 1, "password", "somesalt", - "fd4dd83d762c49bdeaf57c47bdcd0c2f1babf863fdeb490df63ede9975fccf06", - "$argon2i$m=256,t=2,p=1$c29tZXNhbHQ" - "$/U3YPXYsSb3q9XxHvc0MLxur+GP960kN9j7emXX8zwY"); - hashtest(version, 2, 8, 2, "password", "somesalt", - "b6c11560a6a9d61eac706b79a2f97d68b4463aa3ad87e00c07e2b01e90c564fb", - "$argon2i$m=256,t=2,p=2$c29tZXNhbHQ" - "$tsEVYKap1h6scGt5ovl9aLRGOqOth+AMB+KwHpDFZPs"); - hashtest(version, 1, 16, 1, "password", "somesalt", - "81630552b8f3b1f48cdb1992c4c678643d490b2b5eb4ff6c4b3438b5621724b2", - "$argon2i$m=65536,t=1,p=1$c29tZXNhbHQ" - "$gWMFUrjzsfSM2xmSxMZ4ZD1JCytetP9sSzQ4tWIXJLI"); - hashtest(version, 4, 16, 1, "password", "somesalt", - "f212f01615e6eb5d74734dc3ef40ade2d51d052468d8c69440a3a1f2c1c2847b", - "$argon2i$m=65536,t=4,p=1$c29tZXNhbHQ" - "$8hLwFhXm6110c03D70Ct4tUdBSRo2MaUQKOh8sHChHs"); - hashtest(version, 2, 16, 1, "differentpassword", "somesalt", - "e9c902074b6754531a3a0be519e5baf404b30ce69b3f01ac3bf21229960109a3", - "$argon2i$m=65536,t=2,p=1$c29tZXNhbHQ" - "$6ckCB0tnVFMaOgvlGeW69ASzDOabPwGsO/ISKZYBCaM"); - hashtest(version, 2, 16, 1, "password", "diffsalt", - "79a103b90fe8aef8570cb31fc8b22259778916f8336b7bdac3892569d4f1c497", - "$argon2i$m=65536,t=2,p=1$ZGlmZnNhbHQ" - "$eaEDuQ/orvhXDLMfyLIiWXeJFvgza3vaw4kladTxxJc"); - - /* Error state tests */ - - /* Handle an invalid encoding correctly (it is missing a $) */ - ret = argon2_verify("$argon2i$m=65536,t=2,p=1c29tZXNhbHQ" - "$9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ", - "password", strlen("password"), Argon2_i); - assert(ret == ARGON2_DECODING_FAIL); - printf("Recognise an invalid encoding: PASS\n"); - - /* Handle an invalid encoding correctly (it is missing a $) */ - ret = argon2_verify("$argon2i$m=65536,t=2,p=1$c29tZXNhbHQ" - "9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ", - "password", strlen("password"), Argon2_i); - assert(ret == ARGON2_DECODING_FAIL); - printf("Recognise an invalid encoding: PASS\n"); - - /* Handle an invalid encoding correctly (salt is too short) */ - ret = argon2_verify("$argon2i$m=65536,t=2,p=1$" - "$9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ", - "password", strlen("password"), Argon2_i); - assert(ret == ARGON2_SALT_TOO_SHORT); - printf("Recognise an invalid salt in encoding: PASS\n"); - - /* Handle an mismatching hash (the encoded password is "passwore") */ - ret = argon2_verify("$argon2i$m=65536,t=2,p=1$c29tZXNhbHQ" - "$b2G3seW+uPzerwQQC+/E1K50CLLO7YXy0JRcaTuswRo", - "password", strlen("password"), Argon2_i); - assert(ret == ARGON2_VERIFY_MISMATCH); - printf("Verify with mismatched password: PASS\n"); - - msg = argon2_error_message(ARGON2_DECODING_FAIL); - assert(strcmp(msg, "Decoding failed") == 0); - printf("Decode an error message: PASS\n"); - - printf("\n"); - - version = ARGON2_VERSION_NUMBER; - printf("Test Argon2i version number: %02x\n", version); - - /* Multiple test cases for various input values */ - hashtest(version, 2, 16, 1, "password", "somesalt", - "c1628832147d9720c5bd1cfd61367078729f6dfb6f8fea9ff98158e0d7816ed0", - "$argon2i$v=19$m=65536,t=2,p=1$c29tZXNhbHQ" - "$wWKIMhR9lyDFvRz9YTZweHKfbftvj+qf+YFY4NeBbtA"); -#ifdef TEST_LARGE_RAM - hashtest(version, 2, 20, 1, "password", "somesalt", - "d1587aca0922c3b5d6a83edab31bee3c4ebaef342ed6127a55d19b2351ad1f41", - "$argon2i$v=19$m=1048576,t=2,p=1$c29tZXNhbHQ" - "$0Vh6ygkiw7XWqD7asxvuPE667zQu1hJ6VdGbI1GtH0E"); -#endif - hashtest(version, 2, 18, 1, "password", "somesalt", - "296dbae80b807cdceaad44ae741b506f14db0959267b183b118f9b24229bc7cb", - "$argon2i$v=19$m=262144,t=2,p=1$c29tZXNhbHQ" - "$KW266AuAfNzqrUSudBtQbxTbCVkmexg7EY+bJCKbx8s"); - hashtest(version, 2, 8, 1, "password", "somesalt", - "89e9029f4637b295beb027056a7336c414fadd43f6b208645281cb214a56452f", - "$argon2i$v=19$m=256,t=2,p=1$c29tZXNhbHQ" - "$iekCn0Y3spW+sCcFanM2xBT63UP2sghkUoHLIUpWRS8"); - hashtest(version, 2, 8, 2, "password", "somesalt", - "4ff5ce2769a1d7f4c8a491df09d41a9fbe90e5eb02155a13e4c01e20cd4eab61", - "$argon2i$v=19$m=256,t=2,p=2$c29tZXNhbHQ" - "$T/XOJ2mh1/TIpJHfCdQan76Q5esCFVoT5MAeIM1Oq2E"); - hashtest(version, 1, 16, 1, "password", "somesalt", - "d168075c4d985e13ebeae560cf8b94c3b5d8a16c51916b6f4ac2da3ac11bbecf", - "$argon2i$v=19$m=65536,t=1,p=1$c29tZXNhbHQ" - "$0WgHXE2YXhPr6uVgz4uUw7XYoWxRkWtvSsLaOsEbvs8"); - hashtest(version, 4, 16, 1, "password", "somesalt", - "aaa953d58af3706ce3df1aefd4a64a84e31d7f54175231f1285259f88174ce5b", - "$argon2i$v=19$m=65536,t=4,p=1$c29tZXNhbHQ" - "$qqlT1YrzcGzj3xrv1KZKhOMdf1QXUjHxKFJZ+IF0zls"); - hashtest(version, 2, 16, 1, "differentpassword", "somesalt", - "14ae8da01afea8700c2358dcef7c5358d9021282bd88663a4562f59fb74d22ee", - "$argon2i$v=19$m=65536,t=2,p=1$c29tZXNhbHQ" - "$FK6NoBr+qHAMI1jc73xTWNkCEoK9iGY6RWL1n7dNIu4"); - hashtest(version, 2, 16, 1, "password", "diffsalt", - "b0357cccfbef91f3860b0dba447b2348cbefecadaf990abfe9cc40726c521271", - "$argon2i$v=19$m=65536,t=2,p=1$ZGlmZnNhbHQ" - "$sDV8zPvvkfOGCw26RHsjSMvv7K2vmQq/6cxAcmxSEnE"); - - /* Error state tests */ - - /* Handle an invalid encoding correctly (it is missing a $) */ - ret = argon2_verify("$argon2i$v=19$m=65536,t=2,p=1c29tZXNhbHQ" - "$wWKIMhR9lyDFvRz9YTZweHKfbftvj+qf+YFY4NeBbtA", - "password", strlen("password"), Argon2_i); - assert(ret == ARGON2_DECODING_FAIL); - printf("Recognise an invalid encoding: PASS\n"); - - /* Handle an invalid encoding correctly (it is missing a $) */ - ret = argon2_verify("$argon2i$v=19$m=65536,t=2,p=1$c29tZXNhbHQ" - "wWKIMhR9lyDFvRz9YTZweHKfbftvj+qf+YFY4NeBbtA", - "password", strlen("password"), Argon2_i); - assert(ret == ARGON2_DECODING_FAIL); - printf("Recognise an invalid encoding: PASS\n"); - - /* Handle an invalid encoding correctly (salt is too short) */ - ret = argon2_verify("$argon2i$v=19$m=65536,t=2,p=1$" - "$9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ", - "password", strlen("password"), Argon2_i); - assert(ret == ARGON2_SALT_TOO_SHORT); - printf("Recognise an invalid salt in encoding: PASS\n"); - - /* Handle an mismatching hash (the encoded password is "passwore") */ - ret = argon2_verify("$argon2i$v=19$m=65536,t=2,p=1$c29tZXNhbHQ" - "$8iIuixkI73Js3G1uMbezQXD0b8LG4SXGsOwoQkdAQIM", - "password", strlen("password"), Argon2_i); - assert(ret == ARGON2_VERIFY_MISMATCH); - printf("Verify with mismatched password: PASS\n"); - - msg = argon2_error_message(ARGON2_DECODING_FAIL); - assert(strcmp(msg, "Decoding failed") == 0); - printf("Decode an error message: PASS\n"); - - /* Common error state tests */ - - printf("\n"); - printf("Common error state tests\n"); - - ret = argon2_hash(2, 1, 1, "password", strlen("password"), - "diffsalt", strlen("diffsalt"), - out, OUT_LEN, NULL, 0, Argon2_i, version); - assert(ret == ARGON2_MEMORY_TOO_LITTLE); - printf("Fail on invalid memory: PASS\n"); - - ret = argon2_hash(2, 1 << 12, 1, NULL, strlen("password"), - "diffsalt", strlen("diffsalt"), - out, OUT_LEN, NULL, 0, Argon2_i, version); - assert(ret == ARGON2_PWD_PTR_MISMATCH); - printf("Fail on invalid null pointer: PASS\n"); - - ret = argon2_hash(2, 1 << 12, 1, "password", strlen("password"), "s", 1, - out, OUT_LEN, NULL, 0, Argon2_i, version); - assert(ret == ARGON2_SALT_TOO_SHORT); - printf("Fail on salt too short: PASS\n"); - - return 0; -} diff --git a/src/3rdparty/cpp-httplib/example/Makefile b/src/3rdparty/cpp-httplib/example/Makefile deleted file mode 100644 index 575e0fae..00000000 --- a/src/3rdparty/cpp-httplib/example/Makefile +++ /dev/null @@ -1,28 +0,0 @@ - -CC = clang++ -CFLAGS = -std=c++14 -I.. -#OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I/usr/local/opt/openssl/include -L/usr/local/opt/openssl/lib -lssl -lcrypto - -all: server client hello simplesvr benchmark - -server : server.cc ../httplib.h Makefile - $(CC) -o server $(CFLAGS) server.cc $(OPENSSL_SUPPORT) - -client : client.cc ../httplib.h Makefile - $(CC) -o client $(CFLAGS) client.cc $(OPENSSL_SUPPORT) - -hello : hello.cc ../httplib.h Makefile - $(CC) -o hello $(CFLAGS) hello.cc $(OPENSSL_SUPPORT) - -simplesvr : simplesvr.cc ../httplib.h Makefile - $(CC) -o simplesvr $(CFLAGS) simplesvr.cc $(OPENSSL_SUPPORT) - -benchmark : benchmark.cc ../httplib.h Makefile - $(CC) -o benchmark $(CFLAGS) benchmark.cc $(OPENSSL_SUPPORT) - -pem: - openssl genrsa 2048 > key.pem - openssl req -new -key key.pem | openssl x509 -days 3650 -req -signkey key.pem > cert.pem - -clean: - rm server client hello simplesvr *.pem diff --git a/src/3rdparty/cpp-httplib/example/benchmark.cc b/src/3rdparty/cpp-httplib/example/benchmark.cc deleted file mode 100644 index 7e3c3dd7..00000000 --- a/src/3rdparty/cpp-httplib/example/benchmark.cc +++ /dev/null @@ -1,33 +0,0 @@ -#include -#include -#include - -using namespace std; - -struct StopWatch { - StopWatch(const string& label) : label_(label) { - start_ = chrono::system_clock::now(); - } - ~StopWatch() { - auto end = chrono::system_clock::now(); - auto diff = end - start_; - auto count = chrono::duration_cast(diff).count(); - cout << label_ << ": " << count << " millisec." << endl; - } - string label_; - chrono::system_clock::time_point start_; -}; - -int main(int argc, char* argv[]) { - string body(1024 * 5, 'a'); - - httplib::Client cli("httpbin.org", 80); - - for (int i = 0; i < 3; i++) { - StopWatch sw(to_string(i).c_str()); - auto res = cli.post("/post", body, "application/octet-stream"); - assert(res->status == 200); - } - - return 0; -} diff --git a/src/3rdparty/cpp-httplib/example/client.cc b/src/3rdparty/cpp-httplib/example/client.cc deleted file mode 100644 index 3bd1641c..00000000 --- a/src/3rdparty/cpp-httplib/example/client.cc +++ /dev/null @@ -1,31 +0,0 @@ -// -// client.cc -// -// Copyright (c) 2012 Yuji Hirose. All rights reserved. -// The Boost Software License 1.0 -// - -#include -#include - -using namespace std; - -int main(void) -{ -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT - httplib::SSLClient cli("localhost", 8080); -#else - httplib::Client cli("localhost", 8080); -#endif - - auto res = cli.get("/hi"); - if (res) { - cout << res->status << endl; - cout << res->get_header_value("Content-Type") << endl; - cout << res->body << endl; - } - - return 0; -} - -// vim: et ts=4 sw=4 cin cino={1s ff=unix diff --git a/src/3rdparty/cpp-httplib/example/client.vcxproj b/src/3rdparty/cpp-httplib/example/client.vcxproj deleted file mode 100644 index 51d06c1c..00000000 --- a/src/3rdparty/cpp-httplib/example/client.vcxproj +++ /dev/null @@ -1,88 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - {6DB1FC63-B153-4279-92B7-D8A11AF285D6} - Win32Proj - client - - - - Application - true - Unicode - v140 - - - Application - false - true - Unicode - v140 - - - - - - - - - - - - - true - - - false - - - - - - Level3 - Disabled - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - .. - - - Console - true - Ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) - - - - - Level3 - - - MaxSpeed - true - true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - .. - - - Console - true - true - true - Ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) - - - - - - - - - \ No newline at end of file diff --git a/src/3rdparty/cpp-httplib/example/example.sln b/src/3rdparty/cpp-httplib/example/example.sln deleted file mode 100644 index e11aa85e..00000000 --- a/src/3rdparty/cpp-httplib/example/example.sln +++ /dev/null @@ -1,31 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "server", "server.vcxproj", "{864CD288-050A-4C8B-9BEF-3048BD876C5B}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "client", "client.vcxproj", "{6DB1FC63-B153-4279-92B7-D8A11AF285D6}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{280E605F-0CB8-4336-8D9F-CE50A9472AE2}" - ProjectSection(SolutionItems) = preProject - ..\README.md = ..\README.md - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {864CD288-050A-4C8B-9BEF-3048BD876C5B}.Debug|Win32.ActiveCfg = Debug|Win32 - {864CD288-050A-4C8B-9BEF-3048BD876C5B}.Debug|Win32.Build.0 = Debug|Win32 - {864CD288-050A-4C8B-9BEF-3048BD876C5B}.Release|Win32.ActiveCfg = Release|Win32 - {864CD288-050A-4C8B-9BEF-3048BD876C5B}.Release|Win32.Build.0 = Release|Win32 - {6DB1FC63-B153-4279-92B7-D8A11AF285D6}.Debug|Win32.ActiveCfg = Debug|Win32 - {6DB1FC63-B153-4279-92B7-D8A11AF285D6}.Debug|Win32.Build.0 = Debug|Win32 - {6DB1FC63-B153-4279-92B7-D8A11AF285D6}.Release|Win32.ActiveCfg = Release|Win32 - {6DB1FC63-B153-4279-92B7-D8A11AF285D6}.Release|Win32.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/src/3rdparty/cpp-httplib/example/hello.cc b/src/3rdparty/cpp-httplib/example/hello.cc deleted file mode 100644 index de6f4fbf..00000000 --- a/src/3rdparty/cpp-httplib/example/hello.cc +++ /dev/null @@ -1,22 +0,0 @@ -// -// hello.cc -// -// Copyright (c) 2012 Yuji Hirose. All rights reserved. -// The Boost Software License 1.0 -// - -#include -using namespace httplib; - -int main(void) -{ - Server svr; - - svr.get("/hi", [](const auto& req, auto& res) { - res.set_content("Hello World!", "text/plain"); - }); - - svr.listen("localhost", 1234); -} - -// vim: et ts=4 sw=4 cin cino={1s ff=unix diff --git a/src/3rdparty/cpp-httplib/example/server.vcxproj b/src/3rdparty/cpp-httplib/example/server.vcxproj deleted file mode 100644 index f4e71812..00000000 --- a/src/3rdparty/cpp-httplib/example/server.vcxproj +++ /dev/null @@ -1,88 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - {864CD288-050A-4C8B-9BEF-3048BD876C5B} - Win32Proj - sample - - - - Application - true - Unicode - v140 - - - Application - false - true - Unicode - v140 - - - - - - - - - - - - - true - - - false - - - - - - Level3 - Disabled - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - .. - - - Console - true - Ws2_32.lib;%(AdditionalDependencies) - - - - - Level3 - - - MaxSpeed - true - true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - .. - - - Console - true - true - true - Ws2_32.lib;%(AdditionalDependencies) - - - - - - - - - \ No newline at end of file diff --git a/src/3rdparty/cpp-httplib/example/simplesvr.cc b/src/3rdparty/cpp-httplib/example/simplesvr.cc deleted file mode 100644 index 1c9d5a2f..00000000 --- a/src/3rdparty/cpp-httplib/example/simplesvr.cc +++ /dev/null @@ -1,105 +0,0 @@ -// -// simplesvr.cc -// -// Copyright (c) 2013 Yuji Hirose. All rights reserved. -// The Boost Software License 1.0 -// - -#include -#include -#include - -#define SERVER_CERT_FILE "./cert.pem" -#define SERVER_PRIVATE_KEY_FILE "./key.pem" - -using namespace httplib; -using namespace std; - -string dump_headers(const MultiMap& headers) -{ - string s; - char buf[BUFSIZ]; - - for (const auto& x: headers) { - snprintf(buf, sizeof(buf), "%s: %s\n", x.first.c_str(), x.second.c_str()); - s += buf; - } - - return s; -} - -string log(const Request& req, const Response& res) -{ - string s; - char buf[BUFSIZ]; - - s += "================================\n"; - - snprintf(buf, sizeof(buf), "%s %s", req.method.c_str(), req.path.c_str()); - s += buf; - - string query; - for (auto it = req.params.begin(); it != req.params.end(); ++it) { - const auto& x = *it; - snprintf(buf, sizeof(buf), "%c%s=%s", - (it == req.params.begin()) ? '?' : '&', x.first.c_str(), x.second.c_str()); - query += buf; - } - snprintf(buf, sizeof(buf), "%s\n", query.c_str()); - s += buf; - - s += dump_headers(req.headers); - - s += "--------------------------------\n"; - - snprintf(buf, sizeof(buf), "%d\n", res.status); - s += buf; - s += dump_headers(res.headers); - - return s; -} - -int main(int argc, const char** argv) -{ - if (argc > 1 && string("--help") == argv[1]) { - cout << "usage: simplesvr [PORT] [DIR]" << endl; - return 1; - } - -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT - SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE); -#else - Server svr; -#endif - - svr.set_error_handler([](const auto& req, auto& res) { - const char* fmt = "

Error Status: %d

"; - char buf[BUFSIZ]; - snprintf(buf, sizeof(buf), fmt, res.status); - res.set_content(buf, "text/html"); - }); - - svr.set_logger([](const auto& req, const auto& res) { - cout << log(req, res); - }); - - auto port = 80; - if (argc > 1) { - port = atoi(argv[1]); - } - - auto base_dir = "./"; - if (argc > 2) { - base_dir = argv[2]; - } - - svr.set_base_dir(base_dir); - - cout << "The server started at port " << port << "..."; - - svr.listen("localhost", port); - - return 0; -} - -// vim: et ts=4 sw=4 cin cino={1s ff=unix diff --git a/src/3rdparty/cpp-httplib/test/Makefile b/src/3rdparty/cpp-httplib/test/Makefile deleted file mode 100644 index 76809f33..00000000 --- a/src/3rdparty/cpp-httplib/test/Makefile +++ /dev/null @@ -1,17 +0,0 @@ - -CC = clang++ -CFLAGS = -std=c++14 -DGTEST_USE_OWN_TR1_TUPLE -I.. -I. -#OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I/usr/local/opt/openssl/include -L/usr/local/opt/openssl/lib -lssl -lcrypto - -all : test - ./test - -test : test.cc ../httplib.h Makefile - $(CC) -o test $(CFLAGS) test.cc gtest/gtest-all.cc gtest/gtest_main.cc $(OPENSSL_SUPPORT) - -pem: - openssl genrsa 2048 > key.pem - openssl req -new -key key.pem | openssl x509 -days 3650 -req -signkey key.pem > cert.pem - -clean: - rm test *.pem diff --git a/src/3rdparty/cpp-httplib/test/gtest/gtest-all.cc b/src/3rdparty/cpp-httplib/test/gtest/gtest-all.cc deleted file mode 100644 index 5ced66a9..00000000 --- a/src/3rdparty/cpp-httplib/test/gtest/gtest-all.cc +++ /dev/null @@ -1,9118 +0,0 @@ -// Copyright 2008, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Author: mheule@google.com (Markus Heule) -// -// Google C++ Testing Framework (Google Test) -// -// Sometimes it's desirable to build Google Test by compiling a single file. -// This file serves this purpose. - -// This line ensures that gtest.h can be compiled on its own, even -// when it's fused. -#include "gtest/gtest.h" - -// The following lines pull in the real gtest *.cc files. -// Copyright 2005, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Author: wan@google.com (Zhanyong Wan) -// -// The Google C++ Testing Framework (Google Test) - -// Copyright 2007, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Author: wan@google.com (Zhanyong Wan) -// -// Utilities for testing Google Test itself and code that uses Google Test -// (e.g. frameworks built on top of Google Test). - -#ifndef GTEST_INCLUDE_GTEST_GTEST_SPI_H_ -#define GTEST_INCLUDE_GTEST_GTEST_SPI_H_ - - -namespace testing { - -// This helper class can be used to mock out Google Test failure reporting -// so that we can test Google Test or code that builds on Google Test. -// -// An object of this class appends a TestPartResult object to the -// TestPartResultArray object given in the constructor whenever a Google Test -// failure is reported. It can either intercept only failures that are -// generated in the same thread that created this object or it can intercept -// all generated failures. The scope of this mock object can be controlled with -// the second argument to the two arguments constructor. -class GTEST_API_ ScopedFakeTestPartResultReporter - : public TestPartResultReporterInterface { - public: - // The two possible mocking modes of this object. - enum InterceptMode { - INTERCEPT_ONLY_CURRENT_THREAD, // Intercepts only thread local failures. - INTERCEPT_ALL_THREADS // Intercepts all failures. - }; - - // The c'tor sets this object as the test part result reporter used - // by Google Test. The 'result' parameter specifies where to report the - // results. This reporter will only catch failures generated in the current - // thread. DEPRECATED - explicit ScopedFakeTestPartResultReporter(TestPartResultArray* result); - - // Same as above, but you can choose the interception scope of this object. - ScopedFakeTestPartResultReporter(InterceptMode intercept_mode, - TestPartResultArray* result); - - // The d'tor restores the previous test part result reporter. - virtual ~ScopedFakeTestPartResultReporter(); - - // Appends the TestPartResult object to the TestPartResultArray - // received in the constructor. - // - // This method is from the TestPartResultReporterInterface - // interface. - virtual void ReportTestPartResult(const TestPartResult& result); - private: - void Init(); - - const InterceptMode intercept_mode_; - TestPartResultReporterInterface* old_reporter_; - TestPartResultArray* const result_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedFakeTestPartResultReporter); -}; - -namespace internal { - -// A helper class for implementing EXPECT_FATAL_FAILURE() and -// EXPECT_NONFATAL_FAILURE(). Its destructor verifies that the given -// TestPartResultArray contains exactly one failure that has the given -// type and contains the given substring. If that's not the case, a -// non-fatal failure will be generated. -class GTEST_API_ SingleFailureChecker { - public: - // The constructor remembers the arguments. - SingleFailureChecker(const TestPartResultArray* results, - TestPartResult::Type type, - const string& substr); - ~SingleFailureChecker(); - private: - const TestPartResultArray* const results_; - const TestPartResult::Type type_; - const string substr_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(SingleFailureChecker); -}; - -} // namespace internal - -} // namespace testing - -// A set of macros for testing Google Test assertions or code that's expected -// to generate Google Test fatal failures. It verifies that the given -// statement will cause exactly one fatal Google Test failure with 'substr' -// being part of the failure message. -// -// There are two different versions of this macro. EXPECT_FATAL_FAILURE only -// affects and considers failures generated in the current thread and -// EXPECT_FATAL_FAILURE_ON_ALL_THREADS does the same but for all threads. -// -// The verification of the assertion is done correctly even when the statement -// throws an exception or aborts the current function. -// -// Known restrictions: -// - 'statement' cannot reference local non-static variables or -// non-static members of the current object. -// - 'statement' cannot return a value. -// - You cannot stream a failure message to this macro. -// -// Note that even though the implementations of the following two -// macros are much alike, we cannot refactor them to use a common -// helper macro, due to some peculiarity in how the preprocessor -// works. The AcceptsMacroThatExpandsToUnprotectedComma test in -// gtest_unittest.cc will fail to compile if we do that. -#define EXPECT_FATAL_FAILURE(statement, substr) \ - do { \ - class GTestExpectFatalFailureHelper {\ - public:\ - static void Execute() { statement; }\ - };\ - ::testing::TestPartResultArray gtest_failures;\ - ::testing::internal::SingleFailureChecker gtest_checker(\ - >est_failures, ::testing::TestPartResult::kFatalFailure, (substr));\ - {\ - ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ - ::testing::ScopedFakeTestPartResultReporter:: \ - INTERCEPT_ONLY_CURRENT_THREAD, >est_failures);\ - GTestExpectFatalFailureHelper::Execute();\ - }\ - } while (::testing::internal::AlwaysFalse()) - -#define EXPECT_FATAL_FAILURE_ON_ALL_THREADS(statement, substr) \ - do { \ - class GTestExpectFatalFailureHelper {\ - public:\ - static void Execute() { statement; }\ - };\ - ::testing::TestPartResultArray gtest_failures;\ - ::testing::internal::SingleFailureChecker gtest_checker(\ - >est_failures, ::testing::TestPartResult::kFatalFailure, (substr));\ - {\ - ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ - ::testing::ScopedFakeTestPartResultReporter:: \ - INTERCEPT_ALL_THREADS, >est_failures);\ - GTestExpectFatalFailureHelper::Execute();\ - }\ - } while (::testing::internal::AlwaysFalse()) - -// A macro for testing Google Test assertions or code that's expected to -// generate Google Test non-fatal failures. It asserts that the given -// statement will cause exactly one non-fatal Google Test failure with 'substr' -// being part of the failure message. -// -// There are two different versions of this macro. EXPECT_NONFATAL_FAILURE only -// affects and considers failures generated in the current thread and -// EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS does the same but for all threads. -// -// 'statement' is allowed to reference local variables and members of -// the current object. -// -// The verification of the assertion is done correctly even when the statement -// throws an exception or aborts the current function. -// -// Known restrictions: -// - You cannot stream a failure message to this macro. -// -// Note that even though the implementations of the following two -// macros are much alike, we cannot refactor them to use a common -// helper macro, due to some peculiarity in how the preprocessor -// works. If we do that, the code won't compile when the user gives -// EXPECT_NONFATAL_FAILURE() a statement that contains a macro that -// expands to code containing an unprotected comma. The -// AcceptsMacroThatExpandsToUnprotectedComma test in gtest_unittest.cc -// catches that. -// -// For the same reason, we have to write -// if (::testing::internal::AlwaysTrue()) { statement; } -// instead of -// GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) -// to avoid an MSVC warning on unreachable code. -#define EXPECT_NONFATAL_FAILURE(statement, substr) \ - do {\ - ::testing::TestPartResultArray gtest_failures;\ - ::testing::internal::SingleFailureChecker gtest_checker(\ - >est_failures, ::testing::TestPartResult::kNonFatalFailure, \ - (substr));\ - {\ - ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ - ::testing::ScopedFakeTestPartResultReporter:: \ - INTERCEPT_ONLY_CURRENT_THREAD, >est_failures);\ - if (::testing::internal::AlwaysTrue()) { statement; }\ - }\ - } while (::testing::internal::AlwaysFalse()) - -#define EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS(statement, substr) \ - do {\ - ::testing::TestPartResultArray gtest_failures;\ - ::testing::internal::SingleFailureChecker gtest_checker(\ - >est_failures, ::testing::TestPartResult::kNonFatalFailure, \ - (substr));\ - {\ - ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ - ::testing::ScopedFakeTestPartResultReporter::INTERCEPT_ALL_THREADS,\ - >est_failures);\ - if (::testing::internal::AlwaysTrue()) { statement; }\ - }\ - } while (::testing::internal::AlwaysFalse()) - -#endif // GTEST_INCLUDE_GTEST_GTEST_SPI_H_ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include // NOLINT -#include -#include - -#if GTEST_OS_LINUX - -// TODO(kenton@google.com): Use autoconf to detect availability of -// gettimeofday(). -# define GTEST_HAS_GETTIMEOFDAY_ 1 - -# include // NOLINT -# include // NOLINT -# include // NOLINT -// Declares vsnprintf(). This header is not available on Windows. -# include // NOLINT -# include // NOLINT -# include // NOLINT -# include // NOLINT -# include - -#elif GTEST_OS_SYMBIAN -# define GTEST_HAS_GETTIMEOFDAY_ 1 -# include // NOLINT - -#elif GTEST_OS_ZOS -# define GTEST_HAS_GETTIMEOFDAY_ 1 -# include // NOLINT - -// On z/OS we additionally need strings.h for strcasecmp. -# include // NOLINT - -#elif GTEST_OS_WINDOWS_MOBILE // We are on Windows CE. - -# include // NOLINT - -#elif GTEST_OS_WINDOWS // We are on Windows proper. - -# include // NOLINT -# include // NOLINT -# include // NOLINT -# include // NOLINT - -# if GTEST_OS_WINDOWS_MINGW -// MinGW has gettimeofday() but not _ftime64(). -// TODO(kenton@google.com): Use autoconf to detect availability of -// gettimeofday(). -// TODO(kenton@google.com): There are other ways to get the time on -// Windows, like GetTickCount() or GetSystemTimeAsFileTime(). MinGW -// supports these. consider using them instead. -# define GTEST_HAS_GETTIMEOFDAY_ 1 -# include // NOLINT -# endif // GTEST_OS_WINDOWS_MINGW - -// cpplint thinks that the header is already included, so we want to -// silence it. -# include // NOLINT - -#else - -// Assume other platforms have gettimeofday(). -// TODO(kenton@google.com): Use autoconf to detect availability of -// gettimeofday(). -# define GTEST_HAS_GETTIMEOFDAY_ 1 - -// cpplint thinks that the header is already included, so we want to -// silence it. -# include // NOLINT -# include // NOLINT - -#endif // GTEST_OS_LINUX - -#if GTEST_HAS_EXCEPTIONS -# include -#endif - -#if GTEST_CAN_STREAM_RESULTS_ -# include // NOLINT -# include // NOLINT -#endif - -// Indicates that this translation unit is part of Google Test's -// implementation. It must come before gtest-internal-inl.h is -// included, or there will be a compiler error. This trick is to -// prevent a user from accidentally including gtest-internal-inl.h in -// his code. -#define GTEST_IMPLEMENTATION_ 1 -// Copyright 2005, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// Utility functions and classes used by the Google C++ testing framework. -// -// Author: wan@google.com (Zhanyong Wan) -// -// This file contains purely Google Test's internal implementation. Please -// DO NOT #INCLUDE IT IN A USER PROGRAM. - -#ifndef GTEST_SRC_GTEST_INTERNAL_INL_H_ -#define GTEST_SRC_GTEST_INTERNAL_INL_H_ - -// GTEST_IMPLEMENTATION_ is defined to 1 iff the current translation unit is -// part of Google Test's implementation; otherwise it's undefined. -#if !GTEST_IMPLEMENTATION_ -// A user is trying to include this from his code - just say no. -# error "gtest-internal-inl.h is part of Google Test's internal implementation." -# error "It must not be included except by Google Test itself." -#endif // GTEST_IMPLEMENTATION_ - -#ifndef _WIN32_WCE -# include -#endif // !_WIN32_WCE -#include -#include // For strtoll/_strtoul64/malloc/free. -#include // For memmove. - -#include -#include -#include - - -#if GTEST_OS_WINDOWS -# include // NOLINT -#endif // GTEST_OS_WINDOWS - - -namespace testing { - -// Declares the flags. -// -// We don't want the users to modify this flag in the code, but want -// Google Test's own unit tests to be able to access it. Therefore we -// declare it here as opposed to in gtest.h. -GTEST_DECLARE_bool_(death_test_use_fork); - -namespace internal { - -// The value of GetTestTypeId() as seen from within the Google Test -// library. This is solely for testing GetTestTypeId(). -GTEST_API_ extern const TypeId kTestTypeIdInGoogleTest; - -// Names of the flags (needed for parsing Google Test flags). -const char kAlsoRunDisabledTestsFlag[] = "also_run_disabled_tests"; -const char kBreakOnFailureFlag[] = "break_on_failure"; -const char kCatchExceptionsFlag[] = "catch_exceptions"; -const char kColorFlag[] = "color"; -const char kFilterFlag[] = "filter"; -const char kListTestsFlag[] = "list_tests"; -const char kOutputFlag[] = "output"; -const char kPrintTimeFlag[] = "print_time"; -const char kRandomSeedFlag[] = "random_seed"; -const char kRepeatFlag[] = "repeat"; -const char kShuffleFlag[] = "shuffle"; -const char kStackTraceDepthFlag[] = "stack_trace_depth"; -const char kStreamResultToFlag[] = "stream_result_to"; -const char kThrowOnFailureFlag[] = "throw_on_failure"; - -// A valid random seed must be in [1, kMaxRandomSeed]. -const int kMaxRandomSeed = 99999; - -// g_help_flag is true iff the --help flag or an equivalent form is -// specified on the command line. -GTEST_API_ extern bool g_help_flag; - -// Returns the current time in milliseconds. -GTEST_API_ TimeInMillis GetTimeInMillis(); - -// Returns true iff Google Test should use colors in the output. -GTEST_API_ bool ShouldUseColor(bool stdout_is_tty); - -// Formats the given time in milliseconds as seconds. -GTEST_API_ std::string FormatTimeInMillisAsSeconds(TimeInMillis ms); - -// Parses a string for an Int32 flag, in the form of "--flag=value". -// -// On success, stores the value of the flag in *value, and returns -// true. On failure, returns false without changing *value. -GTEST_API_ bool ParseInt32Flag( - const char* str, const char* flag, Int32* value); - -// Returns a random seed in range [1, kMaxRandomSeed] based on the -// given --gtest_random_seed flag value. -inline int GetRandomSeedFromFlag(Int32 random_seed_flag) { - const unsigned int raw_seed = (random_seed_flag == 0) ? - static_cast(GetTimeInMillis()) : - static_cast(random_seed_flag); - - // Normalizes the actual seed to range [1, kMaxRandomSeed] such that - // it's easy to type. - const int normalized_seed = - static_cast((raw_seed - 1U) % - static_cast(kMaxRandomSeed)) + 1; - return normalized_seed; -} - -// Returns the first valid random seed after 'seed'. The behavior is -// undefined if 'seed' is invalid. The seed after kMaxRandomSeed is -// considered to be 1. -inline int GetNextRandomSeed(int seed) { - GTEST_CHECK_(1 <= seed && seed <= kMaxRandomSeed) - << "Invalid random seed " << seed << " - must be in [1, " - << kMaxRandomSeed << "]."; - const int next_seed = seed + 1; - return (next_seed > kMaxRandomSeed) ? 1 : next_seed; -} - -// This class saves the values of all Google Test flags in its c'tor, and -// restores them in its d'tor. -class GTestFlagSaver { - public: - // The c'tor. - GTestFlagSaver() { - also_run_disabled_tests_ = GTEST_FLAG(also_run_disabled_tests); - break_on_failure_ = GTEST_FLAG(break_on_failure); - catch_exceptions_ = GTEST_FLAG(catch_exceptions); - color_ = GTEST_FLAG(color); - death_test_style_ = GTEST_FLAG(death_test_style); - death_test_use_fork_ = GTEST_FLAG(death_test_use_fork); - filter_ = GTEST_FLAG(filter); - internal_run_death_test_ = GTEST_FLAG(internal_run_death_test); - list_tests_ = GTEST_FLAG(list_tests); - output_ = GTEST_FLAG(output); - print_time_ = GTEST_FLAG(print_time); - random_seed_ = GTEST_FLAG(random_seed); - repeat_ = GTEST_FLAG(repeat); - shuffle_ = GTEST_FLAG(shuffle); - stack_trace_depth_ = GTEST_FLAG(stack_trace_depth); - stream_result_to_ = GTEST_FLAG(stream_result_to); - throw_on_failure_ = GTEST_FLAG(throw_on_failure); - } - - // The d'tor is not virtual. DO NOT INHERIT FROM THIS CLASS. - ~GTestFlagSaver() { - GTEST_FLAG(also_run_disabled_tests) = also_run_disabled_tests_; - GTEST_FLAG(break_on_failure) = break_on_failure_; - GTEST_FLAG(catch_exceptions) = catch_exceptions_; - GTEST_FLAG(color) = color_; - GTEST_FLAG(death_test_style) = death_test_style_; - GTEST_FLAG(death_test_use_fork) = death_test_use_fork_; - GTEST_FLAG(filter) = filter_; - GTEST_FLAG(internal_run_death_test) = internal_run_death_test_; - GTEST_FLAG(list_tests) = list_tests_; - GTEST_FLAG(output) = output_; - GTEST_FLAG(print_time) = print_time_; - GTEST_FLAG(random_seed) = random_seed_; - GTEST_FLAG(repeat) = repeat_; - GTEST_FLAG(shuffle) = shuffle_; - GTEST_FLAG(stack_trace_depth) = stack_trace_depth_; - GTEST_FLAG(stream_result_to) = stream_result_to_; - GTEST_FLAG(throw_on_failure) = throw_on_failure_; - } - private: - // Fields for saving the original values of flags. - bool also_run_disabled_tests_; - bool break_on_failure_; - bool catch_exceptions_; - String color_; - String death_test_style_; - bool death_test_use_fork_; - String filter_; - String internal_run_death_test_; - bool list_tests_; - String output_; - bool print_time_; - bool pretty_; - internal::Int32 random_seed_; - internal::Int32 repeat_; - bool shuffle_; - internal::Int32 stack_trace_depth_; - String stream_result_to_; - bool throw_on_failure_; -} GTEST_ATTRIBUTE_UNUSED_; - -// Converts a Unicode code point to a narrow string in UTF-8 encoding. -// code_point parameter is of type UInt32 because wchar_t may not be -// wide enough to contain a code point. -// The output buffer str must containt at least 32 characters. -// The function returns the address of the output buffer. -// If the code_point is not a valid Unicode code point -// (i.e. outside of Unicode range U+0 to U+10FFFF) it will be output -// as '(Invalid Unicode 0xXXXXXXXX)'. -GTEST_API_ char* CodePointToUtf8(UInt32 code_point, char* str); - -// Converts a wide string to a narrow string in UTF-8 encoding. -// The wide string is assumed to have the following encoding: -// UTF-16 if sizeof(wchar_t) == 2 (on Windows, Cygwin, Symbian OS) -// UTF-32 if sizeof(wchar_t) == 4 (on Linux) -// Parameter str points to a null-terminated wide string. -// Parameter num_chars may additionally limit the number -// of wchar_t characters processed. -1 is used when the entire string -// should be processed. -// If the string contains code points that are not valid Unicode code points -// (i.e. outside of Unicode range U+0 to U+10FFFF) they will be output -// as '(Invalid Unicode 0xXXXXXXXX)'. If the string is in UTF16 encoding -// and contains invalid UTF-16 surrogate pairs, values in those pairs -// will be encoded as individual Unicode characters from Basic Normal Plane. -GTEST_API_ String WideStringToUtf8(const wchar_t* str, int num_chars); - -// Reads the GTEST_SHARD_STATUS_FILE environment variable, and creates the file -// if the variable is present. If a file already exists at this location, this -// function will write over it. If the variable is present, but the file cannot -// be created, prints an error and exits. -void WriteToShardStatusFileIfNeeded(); - -// Checks whether sharding is enabled by examining the relevant -// environment variable values. If the variables are present, -// but inconsistent (e.g., shard_index >= total_shards), prints -// an error and exits. If in_subprocess_for_death_test, sharding is -// disabled because it must only be applied to the original test -// process. Otherwise, we could filter out death tests we intended to execute. -GTEST_API_ bool ShouldShard(const char* total_shards_str, - const char* shard_index_str, - bool in_subprocess_for_death_test); - -// Parses the environment variable var as an Int32. If it is unset, -// returns default_val. If it is not an Int32, prints an error and -// and aborts. -GTEST_API_ Int32 Int32FromEnvOrDie(const char* env_var, Int32 default_val); - -// Given the total number of shards, the shard index, and the test id, -// returns true iff the test should be run on this shard. The test id is -// some arbitrary but unique non-negative integer assigned to each test -// method. Assumes that 0 <= shard_index < total_shards. -GTEST_API_ bool ShouldRunTestOnShard( - int total_shards, int shard_index, int test_id); - -// STL container utilities. - -// Returns the number of elements in the given container that satisfy -// the given predicate. -template -inline int CountIf(const Container& c, Predicate predicate) { - // Implemented as an explicit loop since std::count_if() in libCstd on - // Solaris has a non-standard signature. - int count = 0; - for (typename Container::const_iterator it = c.begin(); it != c.end(); ++it) { - if (predicate(*it)) - ++count; - } - return count; -} - -// Applies a function/functor to each element in the container. -template -void ForEach(const Container& c, Functor functor) { - std::for_each(c.begin(), c.end(), functor); -} - -// Returns the i-th element of the vector, or default_value if i is not -// in range [0, v.size()). -template -inline E GetElementOr(const std::vector& v, int i, E default_value) { - return (i < 0 || i >= static_cast(v.size())) ? default_value : v[i]; -} - -// Performs an in-place shuffle of a range of the vector's elements. -// 'begin' and 'end' are element indices as an STL-style range; -// i.e. [begin, end) are shuffled, where 'end' == size() means to -// shuffle to the end of the vector. -template -void ShuffleRange(internal::Random* random, int begin, int end, - std::vector* v) { - const int size = static_cast(v->size()); - GTEST_CHECK_(0 <= begin && begin <= size) - << "Invalid shuffle range start " << begin << ": must be in range [0, " - << size << "]."; - GTEST_CHECK_(begin <= end && end <= size) - << "Invalid shuffle range finish " << end << ": must be in range [" - << begin << ", " << size << "]."; - - // Fisher-Yates shuffle, from - // http://en.wikipedia.org/wiki/Fisher-Yates_shuffle - for (int range_width = end - begin; range_width >= 2; range_width--) { - const int last_in_range = begin + range_width - 1; - const int selected = begin + random->Generate(range_width); - std::swap((*v)[selected], (*v)[last_in_range]); - } -} - -// Performs an in-place shuffle of the vector's elements. -template -inline void Shuffle(internal::Random* random, std::vector* v) { - ShuffleRange(random, 0, static_cast(v->size()), v); -} - -// A function for deleting an object. Handy for being used as a -// functor. -template -static void Delete(T* x) { - delete x; -} - -// A predicate that checks the key of a TestProperty against a known key. -// -// TestPropertyKeyIs is copyable. -class TestPropertyKeyIs { - public: - // Constructor. - // - // TestPropertyKeyIs has NO default constructor. - explicit TestPropertyKeyIs(const char* key) - : key_(key) {} - - // Returns true iff the test name of test property matches on key_. - bool operator()(const TestProperty& test_property) const { - return String(test_property.key()).Compare(key_) == 0; - } - - private: - String key_; -}; - -// Class UnitTestOptions. -// -// This class contains functions for processing options the user -// specifies when running the tests. It has only static members. -// -// In most cases, the user can specify an option using either an -// environment variable or a command line flag. E.g. you can set the -// test filter using either GTEST_FILTER or --gtest_filter. If both -// the variable and the flag are present, the latter overrides the -// former. -class GTEST_API_ UnitTestOptions { - public: - // Functions for processing the gtest_output flag. - - // Returns the output format, or "" for normal printed output. - static String GetOutputFormat(); - - // Returns the absolute path of the requested output file, or the - // default (test_detail.xml in the original working directory) if - // none was explicitly specified. - static String GetAbsolutePathToOutputFile(); - - // Functions for processing the gtest_filter flag. - - // Returns true iff the wildcard pattern matches the string. The - // first ':' or '\0' character in pattern marks the end of it. - // - // This recursive algorithm isn't very efficient, but is clear and - // works well enough for matching test names, which are short. - static bool PatternMatchesString(const char *pattern, const char *str); - - // Returns true iff the user-specified filter matches the test case - // name and the test name. - static bool FilterMatchesTest(const String &test_case_name, - const String &test_name); - -#if GTEST_OS_WINDOWS - // Function for supporting the gtest_catch_exception flag. - - // Returns EXCEPTION_EXECUTE_HANDLER if Google Test should handle the - // given SEH exception, or EXCEPTION_CONTINUE_SEARCH otherwise. - // This function is useful as an __except condition. - static int GTestShouldProcessSEH(DWORD exception_code); -#endif // GTEST_OS_WINDOWS - - // Returns true if "name" matches the ':' separated list of glob-style - // filters in "filter". - static bool MatchesFilter(const String& name, const char* filter); -}; - -// Returns the current application's name, removing directory path if that -// is present. Used by UnitTestOptions::GetOutputFile. -GTEST_API_ FilePath GetCurrentExecutableName(); - -// The role interface for getting the OS stack trace as a string. -class OsStackTraceGetterInterface { - public: - OsStackTraceGetterInterface() {} - virtual ~OsStackTraceGetterInterface() {} - - // Returns the current OS stack trace as a String. Parameters: - // - // max_depth - the maximum number of stack frames to be included - // in the trace. - // skip_count - the number of top frames to be skipped; doesn't count - // against max_depth. - virtual String CurrentStackTrace(int max_depth, int skip_count) = 0; - - // UponLeavingGTest() should be called immediately before Google Test calls - // user code. It saves some information about the current stack that - // CurrentStackTrace() will use to find and hide Google Test stack frames. - virtual void UponLeavingGTest() = 0; - - private: - GTEST_DISALLOW_COPY_AND_ASSIGN_(OsStackTraceGetterInterface); -}; - -// A working implementation of the OsStackTraceGetterInterface interface. -class OsStackTraceGetter : public OsStackTraceGetterInterface { - public: - OsStackTraceGetter() : caller_frame_(NULL) {} - virtual String CurrentStackTrace(int max_depth, int skip_count); - virtual void UponLeavingGTest(); - - // This string is inserted in place of stack frames that are part of - // Google Test's implementation. - static const char* const kElidedFramesMarker; - - private: - Mutex mutex_; // protects all internal state - - // We save the stack frame below the frame that calls user code. - // We do this because the address of the frame immediately below - // the user code changes between the call to UponLeavingGTest() - // and any calls to CurrentStackTrace() from within the user code. - void* caller_frame_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(OsStackTraceGetter); -}; - -// Information about a Google Test trace point. -struct TraceInfo { - const char* file; - int line; - String message; -}; - -// This is the default global test part result reporter used in UnitTestImpl. -// This class should only be used by UnitTestImpl. -class DefaultGlobalTestPartResultReporter - : public TestPartResultReporterInterface { - public: - explicit DefaultGlobalTestPartResultReporter(UnitTestImpl* unit_test); - // Implements the TestPartResultReporterInterface. Reports the test part - // result in the current test. - virtual void ReportTestPartResult(const TestPartResult& result); - - private: - UnitTestImpl* const unit_test_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(DefaultGlobalTestPartResultReporter); -}; - -// This is the default per thread test part result reporter used in -// UnitTestImpl. This class should only be used by UnitTestImpl. -class DefaultPerThreadTestPartResultReporter - : public TestPartResultReporterInterface { - public: - explicit DefaultPerThreadTestPartResultReporter(UnitTestImpl* unit_test); - // Implements the TestPartResultReporterInterface. The implementation just - // delegates to the current global test part result reporter of *unit_test_. - virtual void ReportTestPartResult(const TestPartResult& result); - - private: - UnitTestImpl* const unit_test_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(DefaultPerThreadTestPartResultReporter); -}; - -// The private implementation of the UnitTest class. We don't protect -// the methods under a mutex, as this class is not accessible by a -// user and the UnitTest class that delegates work to this class does -// proper locking. -class GTEST_API_ UnitTestImpl { - public: - explicit UnitTestImpl(UnitTest* parent); - virtual ~UnitTestImpl(); - - // There are two different ways to register your own TestPartResultReporter. - // You can register your own repoter to listen either only for test results - // from the current thread or for results from all threads. - // By default, each per-thread test result repoter just passes a new - // TestPartResult to the global test result reporter, which registers the - // test part result for the currently running test. - - // Returns the global test part result reporter. - TestPartResultReporterInterface* GetGlobalTestPartResultReporter(); - - // Sets the global test part result reporter. - void SetGlobalTestPartResultReporter( - TestPartResultReporterInterface* reporter); - - // Returns the test part result reporter for the current thread. - TestPartResultReporterInterface* GetTestPartResultReporterForCurrentThread(); - - // Sets the test part result reporter for the current thread. - void SetTestPartResultReporterForCurrentThread( - TestPartResultReporterInterface* reporter); - - // Gets the number of successful test cases. - int successful_test_case_count() const; - - // Gets the number of failed test cases. - int failed_test_case_count() const; - - // Gets the number of all test cases. - int total_test_case_count() const; - - // Gets the number of all test cases that contain at least one test - // that should run. - int test_case_to_run_count() const; - - // Gets the number of successful tests. - int successful_test_count() const; - - // Gets the number of failed tests. - int failed_test_count() const; - - // Gets the number of disabled tests. - int disabled_test_count() const; - - // Gets the number of all tests. - int total_test_count() const; - - // Gets the number of tests that should run. - int test_to_run_count() const; - - // Gets the elapsed time, in milliseconds. - TimeInMillis elapsed_time() const { return elapsed_time_; } - - // Returns true iff the unit test passed (i.e. all test cases passed). - bool Passed() const { return !Failed(); } - - // Returns true iff the unit test failed (i.e. some test case failed - // or something outside of all tests failed). - bool Failed() const { - return failed_test_case_count() > 0 || ad_hoc_test_result()->Failed(); - } - - // Gets the i-th test case among all the test cases. i can range from 0 to - // total_test_case_count() - 1. If i is not in that range, returns NULL. - const TestCase* GetTestCase(int i) const { - const int index = GetElementOr(test_case_indices_, i, -1); - return index < 0 ? NULL : test_cases_[i]; - } - - // Gets the i-th test case among all the test cases. i can range from 0 to - // total_test_case_count() - 1. If i is not in that range, returns NULL. - TestCase* GetMutableTestCase(int i) { - const int index = GetElementOr(test_case_indices_, i, -1); - return index < 0 ? NULL : test_cases_[index]; - } - - // Provides access to the event listener list. - TestEventListeners* listeners() { return &listeners_; } - - // Returns the TestResult for the test that's currently running, or - // the TestResult for the ad hoc test if no test is running. - TestResult* current_test_result(); - - // Returns the TestResult for the ad hoc test. - const TestResult* ad_hoc_test_result() const { return &ad_hoc_test_result_; } - - // Sets the OS stack trace getter. - // - // Does nothing if the input and the current OS stack trace getter - // are the same; otherwise, deletes the old getter and makes the - // input the current getter. - void set_os_stack_trace_getter(OsStackTraceGetterInterface* getter); - - // Returns the current OS stack trace getter if it is not NULL; - // otherwise, creates an OsStackTraceGetter, makes it the current - // getter, and returns it. - OsStackTraceGetterInterface* os_stack_trace_getter(); - - // Returns the current OS stack trace as a String. - // - // The maximum number of stack frames to be included is specified by - // the gtest_stack_trace_depth flag. The skip_count parameter - // specifies the number of top frames to be skipped, which doesn't - // count against the number of frames to be included. - // - // For example, if Foo() calls Bar(), which in turn calls - // CurrentOsStackTraceExceptTop(1), Foo() will be included in the - // trace but Bar() and CurrentOsStackTraceExceptTop() won't. - String CurrentOsStackTraceExceptTop(int skip_count); - - // Finds and returns a TestCase with the given name. If one doesn't - // exist, creates one and returns it. - // - // Arguments: - // - // test_case_name: name of the test case - // type_param: the name of the test's type parameter, or NULL if - // this is not a typed or a type-parameterized test. - // set_up_tc: pointer to the function that sets up the test case - // tear_down_tc: pointer to the function that tears down the test case - TestCase* GetTestCase(const char* test_case_name, - const char* type_param, - Test::SetUpTestCaseFunc set_up_tc, - Test::TearDownTestCaseFunc tear_down_tc); - - // Adds a TestInfo to the unit test. - // - // Arguments: - // - // set_up_tc: pointer to the function that sets up the test case - // tear_down_tc: pointer to the function that tears down the test case - // test_info: the TestInfo object - void AddTestInfo(Test::SetUpTestCaseFunc set_up_tc, - Test::TearDownTestCaseFunc tear_down_tc, - TestInfo* test_info) { - // In order to support thread-safe death tests, we need to - // remember the original working directory when the test program - // was first invoked. We cannot do this in RUN_ALL_TESTS(), as - // the user may have changed the current directory before calling - // RUN_ALL_TESTS(). Therefore we capture the current directory in - // AddTestInfo(), which is called to register a TEST or TEST_F - // before main() is reached. - if (original_working_dir_.IsEmpty()) { - original_working_dir_.Set(FilePath::GetCurrentDir()); - GTEST_CHECK_(!original_working_dir_.IsEmpty()) - << "Failed to get the current working directory."; - } - - GetTestCase(test_info->test_case_name(), - test_info->type_param(), - set_up_tc, - tear_down_tc)->AddTestInfo(test_info); - } - -#if GTEST_HAS_PARAM_TEST - // Returns ParameterizedTestCaseRegistry object used to keep track of - // value-parameterized tests and instantiate and register them. - internal::ParameterizedTestCaseRegistry& parameterized_test_registry() { - return parameterized_test_registry_; - } -#endif // GTEST_HAS_PARAM_TEST - - // Sets the TestCase object for the test that's currently running. - void set_current_test_case(TestCase* a_current_test_case) { - current_test_case_ = a_current_test_case; - } - - // Sets the TestInfo object for the test that's currently running. If - // current_test_info is NULL, the assertion results will be stored in - // ad_hoc_test_result_. - void set_current_test_info(TestInfo* a_current_test_info) { - current_test_info_ = a_current_test_info; - } - - // Registers all parameterized tests defined using TEST_P and - // INSTANTIATE_TEST_CASE_P, creating regular tests for each test/parameter - // combination. This method can be called more then once; it has guards - // protecting from registering the tests more then once. If - // value-parameterized tests are disabled, RegisterParameterizedTests is - // present but does nothing. - void RegisterParameterizedTests(); - - // Runs all tests in this UnitTest object, prints the result, and - // returns true if all tests are successful. If any exception is - // thrown during a test, this test is considered to be failed, but - // the rest of the tests will still be run. - bool RunAllTests(); - - // Clears the results of all tests, except the ad hoc tests. - void ClearNonAdHocTestResult() { - ForEach(test_cases_, TestCase::ClearTestCaseResult); - } - - // Clears the results of ad-hoc test assertions. - void ClearAdHocTestResult() { - ad_hoc_test_result_.Clear(); - } - - enum ReactionToSharding { - HONOR_SHARDING_PROTOCOL, - IGNORE_SHARDING_PROTOCOL - }; - - // Matches the full name of each test against the user-specified - // filter to decide whether the test should run, then records the - // result in each TestCase and TestInfo object. - // If shard_tests == HONOR_SHARDING_PROTOCOL, further filters tests - // based on sharding variables in the environment. - // Returns the number of tests that should run. - int FilterTests(ReactionToSharding shard_tests); - - // Prints the names of the tests matching the user-specified filter flag. - void ListTestsMatchingFilter(); - - const TestCase* current_test_case() const { return current_test_case_; } - TestInfo* current_test_info() { return current_test_info_; } - const TestInfo* current_test_info() const { return current_test_info_; } - - // Returns the vector of environments that need to be set-up/torn-down - // before/after the tests are run. - std::vector& environments() { return environments_; } - - // Getters for the per-thread Google Test trace stack. - std::vector& gtest_trace_stack() { - return *(gtest_trace_stack_.pointer()); - } - const std::vector& gtest_trace_stack() const { - return gtest_trace_stack_.get(); - } - -#if GTEST_HAS_DEATH_TEST - void InitDeathTestSubprocessControlInfo() { - internal_run_death_test_flag_.reset(ParseInternalRunDeathTestFlag()); - } - // Returns a pointer to the parsed --gtest_internal_run_death_test - // flag, or NULL if that flag was not specified. - // This information is useful only in a death test child process. - // Must not be called before a call to InitGoogleTest. - const InternalRunDeathTestFlag* internal_run_death_test_flag() const { - return internal_run_death_test_flag_.get(); - } - - // Returns a pointer to the current death test factory. - internal::DeathTestFactory* death_test_factory() { - return death_test_factory_.get(); - } - - void SuppressTestEventsIfInSubprocess(); - - friend class ReplaceDeathTestFactory; -#endif // GTEST_HAS_DEATH_TEST - - // Initializes the event listener performing XML output as specified by - // UnitTestOptions. Must not be called before InitGoogleTest. - void ConfigureXmlOutput(); - -#if GTEST_CAN_STREAM_RESULTS_ - // Initializes the event listener for streaming test results to a socket. - // Must not be called before InitGoogleTest. - void ConfigureStreamingOutput(); -#endif - - // Performs initialization dependent upon flag values obtained in - // ParseGoogleTestFlagsOnly. Is called from InitGoogleTest after the call to - // ParseGoogleTestFlagsOnly. In case a user neglects to call InitGoogleTest - // this function is also called from RunAllTests. Since this function can be - // called more than once, it has to be idempotent. - void PostFlagParsingInit(); - - // Gets the random seed used at the start of the current test iteration. - int random_seed() const { return random_seed_; } - - // Gets the random number generator. - internal::Random* random() { return &random_; } - - // Shuffles all test cases, and the tests within each test case, - // making sure that death tests are still run first. - void ShuffleTests(); - - // Restores the test cases and tests to their order before the first shuffle. - void UnshuffleTests(); - - // Returns the value of GTEST_FLAG(catch_exceptions) at the moment - // UnitTest::Run() starts. - bool catch_exceptions() const { return catch_exceptions_; } - - private: - friend class ::testing::UnitTest; - - // Used by UnitTest::Run() to capture the state of - // GTEST_FLAG(catch_exceptions) at the moment it starts. - void set_catch_exceptions(bool value) { catch_exceptions_ = value; } - - // The UnitTest object that owns this implementation object. - UnitTest* const parent_; - - // The working directory when the first TEST() or TEST_F() was - // executed. - internal::FilePath original_working_dir_; - - // The default test part result reporters. - DefaultGlobalTestPartResultReporter default_global_test_part_result_reporter_; - DefaultPerThreadTestPartResultReporter - default_per_thread_test_part_result_reporter_; - - // Points to (but doesn't own) the global test part result reporter. - TestPartResultReporterInterface* global_test_part_result_repoter_; - - // Protects read and write access to global_test_part_result_reporter_. - internal::Mutex global_test_part_result_reporter_mutex_; - - // Points to (but doesn't own) the per-thread test part result reporter. - internal::ThreadLocal - per_thread_test_part_result_reporter_; - - // The vector of environments that need to be set-up/torn-down - // before/after the tests are run. - std::vector environments_; - - // The vector of TestCases in their original order. It owns the - // elements in the vector. - std::vector test_cases_; - - // Provides a level of indirection for the test case list to allow - // easy shuffling and restoring the test case order. The i-th - // element of this vector is the index of the i-th test case in the - // shuffled order. - std::vector test_case_indices_; - -#if GTEST_HAS_PARAM_TEST - // ParameterizedTestRegistry object used to register value-parameterized - // tests. - internal::ParameterizedTestCaseRegistry parameterized_test_registry_; - - // Indicates whether RegisterParameterizedTests() has been called already. - bool parameterized_tests_registered_; -#endif // GTEST_HAS_PARAM_TEST - - // Index of the last death test case registered. Initially -1. - int last_death_test_case_; - - // This points to the TestCase for the currently running test. It - // changes as Google Test goes through one test case after another. - // When no test is running, this is set to NULL and Google Test - // stores assertion results in ad_hoc_test_result_. Initially NULL. - TestCase* current_test_case_; - - // This points to the TestInfo for the currently running test. It - // changes as Google Test goes through one test after another. When - // no test is running, this is set to NULL and Google Test stores - // assertion results in ad_hoc_test_result_. Initially NULL. - TestInfo* current_test_info_; - - // Normally, a user only writes assertions inside a TEST or TEST_F, - // or inside a function called by a TEST or TEST_F. Since Google - // Test keeps track of which test is current running, it can - // associate such an assertion with the test it belongs to. - // - // If an assertion is encountered when no TEST or TEST_F is running, - // Google Test attributes the assertion result to an imaginary "ad hoc" - // test, and records the result in ad_hoc_test_result_. - TestResult ad_hoc_test_result_; - - // The list of event listeners that can be used to track events inside - // Google Test. - TestEventListeners listeners_; - - // The OS stack trace getter. Will be deleted when the UnitTest - // object is destructed. By default, an OsStackTraceGetter is used, - // but the user can set this field to use a custom getter if that is - // desired. - OsStackTraceGetterInterface* os_stack_trace_getter_; - - // True iff PostFlagParsingInit() has been called. - bool post_flag_parse_init_performed_; - - // The random number seed used at the beginning of the test run. - int random_seed_; - - // Our random number generator. - internal::Random random_; - - // How long the test took to run, in milliseconds. - TimeInMillis elapsed_time_; - -#if GTEST_HAS_DEATH_TEST - // The decomposed components of the gtest_internal_run_death_test flag, - // parsed when RUN_ALL_TESTS is called. - internal::scoped_ptr internal_run_death_test_flag_; - internal::scoped_ptr death_test_factory_; -#endif // GTEST_HAS_DEATH_TEST - - // A per-thread stack of traces created by the SCOPED_TRACE() macro. - internal::ThreadLocal > gtest_trace_stack_; - - // The value of GTEST_FLAG(catch_exceptions) at the moment RunAllTests() - // starts. - bool catch_exceptions_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(UnitTestImpl); -}; // class UnitTestImpl - -// Convenience function for accessing the global UnitTest -// implementation object. -inline UnitTestImpl* GetUnitTestImpl() { - return UnitTest::GetInstance()->impl(); -} - -#if GTEST_USES_SIMPLE_RE - -// Internal helper functions for implementing the simple regular -// expression matcher. -GTEST_API_ bool IsInSet(char ch, const char* str); -GTEST_API_ bool IsAsciiDigit(char ch); -GTEST_API_ bool IsAsciiPunct(char ch); -GTEST_API_ bool IsRepeat(char ch); -GTEST_API_ bool IsAsciiWhiteSpace(char ch); -GTEST_API_ bool IsAsciiWordChar(char ch); -GTEST_API_ bool IsValidEscape(char ch); -GTEST_API_ bool AtomMatchesChar(bool escaped, char pattern, char ch); -GTEST_API_ bool ValidateRegex(const char* regex); -GTEST_API_ bool MatchRegexAtHead(const char* regex, const char* str); -GTEST_API_ bool MatchRepetitionAndRegexAtHead( - bool escaped, char ch, char repeat, const char* regex, const char* str); -GTEST_API_ bool MatchRegexAnywhere(const char* regex, const char* str); - -#endif // GTEST_USES_SIMPLE_RE - -// Parses the command line for Google Test flags, without initializing -// other parts of Google Test. -GTEST_API_ void ParseGoogleTestFlagsOnly(int* argc, char** argv); -GTEST_API_ void ParseGoogleTestFlagsOnly(int* argc, wchar_t** argv); - -#if GTEST_HAS_DEATH_TEST - -// Returns the message describing the last system error, regardless of the -// platform. -GTEST_API_ String GetLastErrnoDescription(); - -# if GTEST_OS_WINDOWS -// Provides leak-safe Windows kernel handle ownership. -class AutoHandle { - public: - AutoHandle() : handle_(INVALID_HANDLE_VALUE) {} - explicit AutoHandle(HANDLE handle) : handle_(handle) {} - - ~AutoHandle() { Reset(); } - - HANDLE Get() const { return handle_; } - void Reset() { Reset(INVALID_HANDLE_VALUE); } - void Reset(HANDLE handle) { - if (handle != handle_) { - if (handle_ != INVALID_HANDLE_VALUE) - ::CloseHandle(handle_); - handle_ = handle; - } - } - - private: - HANDLE handle_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(AutoHandle); -}; -# endif // GTEST_OS_WINDOWS - -// Attempts to parse a string into a positive integer pointed to by the -// number parameter. Returns true if that is possible. -// GTEST_HAS_DEATH_TEST implies that we have ::std::string, so we can use -// it here. -template -bool ParseNaturalNumber(const ::std::string& str, Integer* number) { - // Fail fast if the given string does not begin with a digit; - // this bypasses strtoXXX's "optional leading whitespace and plus - // or minus sign" semantics, which are undesirable here. - if (str.empty() || !IsDigit(str[0])) { - return false; - } - errno = 0; - - char* end; - // BiggestConvertible is the largest integer type that system-provided - // string-to-number conversion routines can return. - -# if GTEST_OS_WINDOWS && !defined(__GNUC__) - - // MSVC and C++ Builder define __int64 instead of the standard long long. - typedef unsigned __int64 BiggestConvertible; - const BiggestConvertible parsed = _strtoui64(str.c_str(), &end, 10); - -# else - - typedef unsigned long long BiggestConvertible; // NOLINT - const BiggestConvertible parsed = strtoull(str.c_str(), &end, 10); - -# endif // GTEST_OS_WINDOWS && !defined(__GNUC__) - - const bool parse_success = *end == '\0' && errno == 0; - - // TODO(vladl@google.com): Convert this to compile time assertion when it is - // available. - GTEST_CHECK_(sizeof(Integer) <= sizeof(parsed)); - - const Integer result = static_cast(parsed); - if (parse_success && static_cast(result) == parsed) { - *number = result; - return true; - } - return false; -} -#endif // GTEST_HAS_DEATH_TEST - -// TestResult contains some private methods that should be hidden from -// Google Test user but are required for testing. This class allow our tests -// to access them. -// -// This class is supplied only for the purpose of testing Google Test's own -// constructs. Do not use it in user tests, either directly or indirectly. -class TestResultAccessor { - public: - static void RecordProperty(TestResult* test_result, - const TestProperty& property) { - test_result->RecordProperty(property); - } - - static void ClearTestPartResults(TestResult* test_result) { - test_result->ClearTestPartResults(); - } - - static const std::vector& test_part_results( - const TestResult& test_result) { - return test_result.test_part_results(); - } -}; - -} // namespace internal -} // namespace testing - -#endif // GTEST_SRC_GTEST_INTERNAL_INL_H_ -#undef GTEST_IMPLEMENTATION_ - -#if GTEST_OS_WINDOWS -# define vsnprintf _vsnprintf -#endif // GTEST_OS_WINDOWS - -namespace testing { - -using internal::CountIf; -using internal::ForEach; -using internal::GetElementOr; -using internal::Shuffle; - -// Constants. - -// A test whose test case name or test name matches this filter is -// disabled and not run. -static const char kDisableTestFilter[] = "DISABLED_*:*/DISABLED_*"; - -// A test case whose name matches this filter is considered a death -// test case and will be run before test cases whose name doesn't -// match this filter. -static const char kDeathTestCaseFilter[] = "*DeathTest:*DeathTest/*"; - -// A test filter that matches everything. -static const char kUniversalFilter[] = "*"; - -// The default output file for XML output. -static const char kDefaultOutputFile[] = "test_detail.xml"; - -// The environment variable name for the test shard index. -static const char kTestShardIndex[] = "GTEST_SHARD_INDEX"; -// The environment variable name for the total number of test shards. -static const char kTestTotalShards[] = "GTEST_TOTAL_SHARDS"; -// The environment variable name for the test shard status file. -static const char kTestShardStatusFile[] = "GTEST_SHARD_STATUS_FILE"; - -namespace internal { - -// The text used in failure messages to indicate the start of the -// stack trace. -const char kStackTraceMarker[] = "\nStack trace:\n"; - -// g_help_flag is true iff the --help flag or an equivalent form is -// specified on the command line. -bool g_help_flag = false; - -} // namespace internal - -GTEST_DEFINE_bool_( - also_run_disabled_tests, - internal::BoolFromGTestEnv("also_run_disabled_tests", false), - "Run disabled tests too, in addition to the tests normally being run."); - -GTEST_DEFINE_bool_( - break_on_failure, - internal::BoolFromGTestEnv("break_on_failure", false), - "True iff a failed assertion should be a debugger break-point."); - -GTEST_DEFINE_bool_( - catch_exceptions, - internal::BoolFromGTestEnv("catch_exceptions", true), - "True iff " GTEST_NAME_ - " should catch exceptions and treat them as test failures."); - -GTEST_DEFINE_string_( - color, - internal::StringFromGTestEnv("color", "auto"), - "Whether to use colors in the output. Valid values: yes, no, " - "and auto. 'auto' means to use colors if the output is " - "being sent to a terminal and the TERM environment variable " - "is set to xterm, xterm-color, xterm-256color, linux or cygwin."); - -GTEST_DEFINE_string_( - filter, - internal::StringFromGTestEnv("filter", kUniversalFilter), - "A colon-separated list of glob (not regex) patterns " - "for filtering the tests to run, optionally followed by a " - "'-' and a : separated list of negative patterns (tests to " - "exclude). A test is run if it matches one of the positive " - "patterns and does not match any of the negative patterns."); - -GTEST_DEFINE_bool_(list_tests, false, - "List all tests without running them."); - -GTEST_DEFINE_string_( - output, - internal::StringFromGTestEnv("output", ""), - "A format (currently must be \"xml\"), optionally followed " - "by a colon and an output file name or directory. A directory " - "is indicated by a trailing pathname separator. " - "Examples: \"xml:filename.xml\", \"xml::directoryname/\". " - "If a directory is specified, output files will be created " - "within that directory, with file-names based on the test " - "executable's name and, if necessary, made unique by adding " - "digits."); - -GTEST_DEFINE_bool_( - print_time, - internal::BoolFromGTestEnv("print_time", true), - "True iff " GTEST_NAME_ - " should display elapsed time in text output."); - -GTEST_DEFINE_int32_( - random_seed, - internal::Int32FromGTestEnv("random_seed", 0), - "Random number seed to use when shuffling test orders. Must be in range " - "[1, 99999], or 0 to use a seed based on the current time."); - -GTEST_DEFINE_int32_( - repeat, - internal::Int32FromGTestEnv("repeat", 1), - "How many times to repeat each test. Specify a negative number " - "for repeating forever. Useful for shaking out flaky tests."); - -GTEST_DEFINE_bool_( - show_internal_stack_frames, false, - "True iff " GTEST_NAME_ " should include internal stack frames when " - "printing test failure stack traces."); - -GTEST_DEFINE_bool_( - shuffle, - internal::BoolFromGTestEnv("shuffle", false), - "True iff " GTEST_NAME_ - " should randomize tests' order on every run."); - -GTEST_DEFINE_int32_( - stack_trace_depth, - internal::Int32FromGTestEnv("stack_trace_depth", kMaxStackTraceDepth), - "The maximum number of stack frames to print when an " - "assertion fails. The valid range is 0 through 100, inclusive."); - -GTEST_DEFINE_string_( - stream_result_to, - internal::StringFromGTestEnv("stream_result_to", ""), - "This flag specifies the host name and the port number on which to stream " - "test results. Example: \"localhost:555\". The flag is effective only on " - "Linux."); - -GTEST_DEFINE_bool_( - throw_on_failure, - internal::BoolFromGTestEnv("throw_on_failure", false), - "When this flag is specified, a failed assertion will throw an exception " - "if exceptions are enabled or exit the program with a non-zero code " - "otherwise."); - -namespace internal { - -// Generates a random number from [0, range), using a Linear -// Congruential Generator (LCG). Crashes if 'range' is 0 or greater -// than kMaxRange. -UInt32 Random::Generate(UInt32 range) { - // These constants are the same as are used in glibc's rand(3). - state_ = (1103515245U*state_ + 12345U) % kMaxRange; - - GTEST_CHECK_(range > 0) - << "Cannot generate a number in the range [0, 0)."; - GTEST_CHECK_(range <= kMaxRange) - << "Generation of a number in [0, " << range << ") was requested, " - << "but this can only generate numbers in [0, " << kMaxRange << ")."; - - // Converting via modulus introduces a bit of downward bias, but - // it's simple, and a linear congruential generator isn't too good - // to begin with. - return state_ % range; -} - -// GTestIsInitialized() returns true iff the user has initialized -// Google Test. Useful for catching the user mistake of not initializing -// Google Test before calling RUN_ALL_TESTS(). -// -// A user must call testing::InitGoogleTest() to initialize Google -// Test. g_init_gtest_count is set to the number of times -// InitGoogleTest() has been called. We don't protect this variable -// under a mutex as it is only accessed in the main thread. -int g_init_gtest_count = 0; -static bool GTestIsInitialized() { return g_init_gtest_count != 0; } - -// Iterates over a vector of TestCases, keeping a running sum of the -// results of calling a given int-returning method on each. -// Returns the sum. -static int SumOverTestCaseList(const std::vector& case_list, - int (TestCase::*method)() const) { - int sum = 0; - for (size_t i = 0; i < case_list.size(); i++) { - sum += (case_list[i]->*method)(); - } - return sum; -} - -// Returns true iff the test case passed. -static bool TestCasePassed(const TestCase* test_case) { - return test_case->should_run() && test_case->Passed(); -} - -// Returns true iff the test case failed. -static bool TestCaseFailed(const TestCase* test_case) { - return test_case->should_run() && test_case->Failed(); -} - -// Returns true iff test_case contains at least one test that should -// run. -static bool ShouldRunTestCase(const TestCase* test_case) { - return test_case->should_run(); -} - -// AssertHelper constructor. -AssertHelper::AssertHelper(TestPartResult::Type type, - const char* file, - int line, - const char* message) - : data_(new AssertHelperData(type, file, line, message)) { -} - -AssertHelper::~AssertHelper() { - delete data_; -} - -// Message assignment, for assertion streaming support. -void AssertHelper::operator=(const Message& message) const { - UnitTest::GetInstance()-> - AddTestPartResult(data_->type, data_->file, data_->line, - AppendUserMessage(data_->message, message), - UnitTest::GetInstance()->impl() - ->CurrentOsStackTraceExceptTop(1) - // Skips the stack frame for this function itself. - ); // NOLINT -} - -// Mutex for linked pointers. -GTEST_DEFINE_STATIC_MUTEX_(g_linked_ptr_mutex); - -// Application pathname gotten in InitGoogleTest. -String g_executable_path; - -// Returns the current application's name, removing directory path if that -// is present. -FilePath GetCurrentExecutableName() { - FilePath result; - -#if GTEST_OS_WINDOWS - result.Set(FilePath(g_executable_path).RemoveExtension("exe")); -#else - result.Set(FilePath(g_executable_path)); -#endif // GTEST_OS_WINDOWS - - return result.RemoveDirectoryName(); -} - -// Functions for processing the gtest_output flag. - -// Returns the output format, or "" for normal printed output. -String UnitTestOptions::GetOutputFormat() { - const char* const gtest_output_flag = GTEST_FLAG(output).c_str(); - if (gtest_output_flag == NULL) return String(""); - - const char* const colon = strchr(gtest_output_flag, ':'); - return (colon == NULL) ? - String(gtest_output_flag) : - String(gtest_output_flag, colon - gtest_output_flag); -} - -// Returns the name of the requested output file, or the default if none -// was explicitly specified. -String UnitTestOptions::GetAbsolutePathToOutputFile() { - const char* const gtest_output_flag = GTEST_FLAG(output).c_str(); - if (gtest_output_flag == NULL) - return String(""); - - const char* const colon = strchr(gtest_output_flag, ':'); - if (colon == NULL) - return String(internal::FilePath::ConcatPaths( - internal::FilePath( - UnitTest::GetInstance()->original_working_dir()), - internal::FilePath(kDefaultOutputFile)).ToString() ); - - internal::FilePath output_name(colon + 1); - if (!output_name.IsAbsolutePath()) - // TODO(wan@google.com): on Windows \some\path is not an absolute - // path (as its meaning depends on the current drive), yet the - // following logic for turning it into an absolute path is wrong. - // Fix it. - output_name = internal::FilePath::ConcatPaths( - internal::FilePath(UnitTest::GetInstance()->original_working_dir()), - internal::FilePath(colon + 1)); - - if (!output_name.IsDirectory()) - return output_name.ToString(); - - internal::FilePath result(internal::FilePath::GenerateUniqueFileName( - output_name, internal::GetCurrentExecutableName(), - GetOutputFormat().c_str())); - return result.ToString(); -} - -// Returns true iff the wildcard pattern matches the string. The -// first ':' or '\0' character in pattern marks the end of it. -// -// This recursive algorithm isn't very efficient, but is clear and -// works well enough for matching test names, which are short. -bool UnitTestOptions::PatternMatchesString(const char *pattern, - const char *str) { - switch (*pattern) { - case '\0': - case ':': // Either ':' or '\0' marks the end of the pattern. - return *str == '\0'; - case '?': // Matches any single character. - return *str != '\0' && PatternMatchesString(pattern + 1, str + 1); - case '*': // Matches any string (possibly empty) of characters. - return (*str != '\0' && PatternMatchesString(pattern, str + 1)) || - PatternMatchesString(pattern + 1, str); - default: // Non-special character. Matches itself. - return *pattern == *str && - PatternMatchesString(pattern + 1, str + 1); - } -} - -bool UnitTestOptions::MatchesFilter(const String& name, const char* filter) { - const char *cur_pattern = filter; - for (;;) { - if (PatternMatchesString(cur_pattern, name.c_str())) { - return true; - } - - // Finds the next pattern in the filter. - cur_pattern = strchr(cur_pattern, ':'); - - // Returns if no more pattern can be found. - if (cur_pattern == NULL) { - return false; - } - - // Skips the pattern separater (the ':' character). - cur_pattern++; - } -} - -// TODO(keithray): move String function implementations to gtest-string.cc. - -// Returns true iff the user-specified filter matches the test case -// name and the test name. -bool UnitTestOptions::FilterMatchesTest(const String &test_case_name, - const String &test_name) { - const String& full_name = String::Format("%s.%s", - test_case_name.c_str(), - test_name.c_str()); - - // Split --gtest_filter at '-', if there is one, to separate into - // positive filter and negative filter portions - const char* const p = GTEST_FLAG(filter).c_str(); - const char* const dash = strchr(p, '-'); - String positive; - String negative; - if (dash == NULL) { - positive = GTEST_FLAG(filter).c_str(); // Whole string is a positive filter - negative = String(""); - } else { - positive = String(p, dash - p); // Everything up to the dash - negative = String(dash+1); // Everything after the dash - if (positive.empty()) { - // Treat '-test1' as the same as '*-test1' - positive = kUniversalFilter; - } - } - - // A filter is a colon-separated list of patterns. It matches a - // test if any pattern in it matches the test. - return (MatchesFilter(full_name, positive.c_str()) && - !MatchesFilter(full_name, negative.c_str())); -} - -#if GTEST_HAS_SEH -// Returns EXCEPTION_EXECUTE_HANDLER if Google Test should handle the -// given SEH exception, or EXCEPTION_CONTINUE_SEARCH otherwise. -// This function is useful as an __except condition. -int UnitTestOptions::GTestShouldProcessSEH(DWORD exception_code) { - // Google Test should handle a SEH exception if: - // 1. the user wants it to, AND - // 2. this is not a breakpoint exception, AND - // 3. this is not a C++ exception (VC++ implements them via SEH, - // apparently). - // - // SEH exception code for C++ exceptions. - // (see http://support.microsoft.com/kb/185294 for more information). - const DWORD kCxxExceptionCode = 0xe06d7363; - - bool should_handle = true; - - if (!GTEST_FLAG(catch_exceptions)) - should_handle = false; - else if (exception_code == EXCEPTION_BREAKPOINT) - should_handle = false; - else if (exception_code == kCxxExceptionCode) - should_handle = false; - - return should_handle ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH; -} -#endif // GTEST_HAS_SEH - -} // namespace internal - -// The c'tor sets this object as the test part result reporter used by -// Google Test. The 'result' parameter specifies where to report the -// results. Intercepts only failures from the current thread. -ScopedFakeTestPartResultReporter::ScopedFakeTestPartResultReporter( - TestPartResultArray* result) - : intercept_mode_(INTERCEPT_ONLY_CURRENT_THREAD), - result_(result) { - Init(); -} - -// The c'tor sets this object as the test part result reporter used by -// Google Test. The 'result' parameter specifies where to report the -// results. -ScopedFakeTestPartResultReporter::ScopedFakeTestPartResultReporter( - InterceptMode intercept_mode, TestPartResultArray* result) - : intercept_mode_(intercept_mode), - result_(result) { - Init(); -} - -void ScopedFakeTestPartResultReporter::Init() { - internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); - if (intercept_mode_ == INTERCEPT_ALL_THREADS) { - old_reporter_ = impl->GetGlobalTestPartResultReporter(); - impl->SetGlobalTestPartResultReporter(this); - } else { - old_reporter_ = impl->GetTestPartResultReporterForCurrentThread(); - impl->SetTestPartResultReporterForCurrentThread(this); - } -} - -// The d'tor restores the test part result reporter used by Google Test -// before. -ScopedFakeTestPartResultReporter::~ScopedFakeTestPartResultReporter() { - internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); - if (intercept_mode_ == INTERCEPT_ALL_THREADS) { - impl->SetGlobalTestPartResultReporter(old_reporter_); - } else { - impl->SetTestPartResultReporterForCurrentThread(old_reporter_); - } -} - -// Increments the test part result count and remembers the result. -// This method is from the TestPartResultReporterInterface interface. -void ScopedFakeTestPartResultReporter::ReportTestPartResult( - const TestPartResult& result) { - result_->Append(result); -} - -namespace internal { - -// Returns the type ID of ::testing::Test. We should always call this -// instead of GetTypeId< ::testing::Test>() to get the type ID of -// testing::Test. This is to work around a suspected linker bug when -// using Google Test as a framework on Mac OS X. The bug causes -// GetTypeId< ::testing::Test>() to return different values depending -// on whether the call is from the Google Test framework itself or -// from user test code. GetTestTypeId() is guaranteed to always -// return the same value, as it always calls GetTypeId<>() from the -// gtest.cc, which is within the Google Test framework. -TypeId GetTestTypeId() { - return GetTypeId(); -} - -// The value of GetTestTypeId() as seen from within the Google Test -// library. This is solely for testing GetTestTypeId(). -extern const TypeId kTestTypeIdInGoogleTest = GetTestTypeId(); - -// This predicate-formatter checks that 'results' contains a test part -// failure of the given type and that the failure message contains the -// given substring. -AssertionResult HasOneFailure(const char* /* results_expr */, - const char* /* type_expr */, - const char* /* substr_expr */, - const TestPartResultArray& results, - TestPartResult::Type type, - const string& substr) { - const String expected(type == TestPartResult::kFatalFailure ? - "1 fatal failure" : - "1 non-fatal failure"); - Message msg; - if (results.size() != 1) { - msg << "Expected: " << expected << "\n" - << " Actual: " << results.size() << " failures"; - for (int i = 0; i < results.size(); i++) { - msg << "\n" << results.GetTestPartResult(i); - } - return AssertionFailure() << msg; - } - - const TestPartResult& r = results.GetTestPartResult(0); - if (r.type() != type) { - return AssertionFailure() << "Expected: " << expected << "\n" - << " Actual:\n" - << r; - } - - if (strstr(r.message(), substr.c_str()) == NULL) { - return AssertionFailure() << "Expected: " << expected << " containing \"" - << substr << "\"\n" - << " Actual:\n" - << r; - } - - return AssertionSuccess(); -} - -// The constructor of SingleFailureChecker remembers where to look up -// test part results, what type of failure we expect, and what -// substring the failure message should contain. -SingleFailureChecker:: SingleFailureChecker( - const TestPartResultArray* results, - TestPartResult::Type type, - const string& substr) - : results_(results), - type_(type), - substr_(substr) {} - -// The destructor of SingleFailureChecker verifies that the given -// TestPartResultArray contains exactly one failure that has the given -// type and contains the given substring. If that's not the case, a -// non-fatal failure will be generated. -SingleFailureChecker::~SingleFailureChecker() { - EXPECT_PRED_FORMAT3(HasOneFailure, *results_, type_, substr_); -} - -DefaultGlobalTestPartResultReporter::DefaultGlobalTestPartResultReporter( - UnitTestImpl* unit_test) : unit_test_(unit_test) {} - -void DefaultGlobalTestPartResultReporter::ReportTestPartResult( - const TestPartResult& result) { - unit_test_->current_test_result()->AddTestPartResult(result); - unit_test_->listeners()->repeater()->OnTestPartResult(result); -} - -DefaultPerThreadTestPartResultReporter::DefaultPerThreadTestPartResultReporter( - UnitTestImpl* unit_test) : unit_test_(unit_test) {} - -void DefaultPerThreadTestPartResultReporter::ReportTestPartResult( - const TestPartResult& result) { - unit_test_->GetGlobalTestPartResultReporter()->ReportTestPartResult(result); -} - -// Returns the global test part result reporter. -TestPartResultReporterInterface* -UnitTestImpl::GetGlobalTestPartResultReporter() { - internal::MutexLock lock(&global_test_part_result_reporter_mutex_); - return global_test_part_result_repoter_; -} - -// Sets the global test part result reporter. -void UnitTestImpl::SetGlobalTestPartResultReporter( - TestPartResultReporterInterface* reporter) { - internal::MutexLock lock(&global_test_part_result_reporter_mutex_); - global_test_part_result_repoter_ = reporter; -} - -// Returns the test part result reporter for the current thread. -TestPartResultReporterInterface* -UnitTestImpl::GetTestPartResultReporterForCurrentThread() { - return per_thread_test_part_result_reporter_.get(); -} - -// Sets the test part result reporter for the current thread. -void UnitTestImpl::SetTestPartResultReporterForCurrentThread( - TestPartResultReporterInterface* reporter) { - per_thread_test_part_result_reporter_.set(reporter); -} - -// Gets the number of successful test cases. -int UnitTestImpl::successful_test_case_count() const { - return CountIf(test_cases_, TestCasePassed); -} - -// Gets the number of failed test cases. -int UnitTestImpl::failed_test_case_count() const { - return CountIf(test_cases_, TestCaseFailed); -} - -// Gets the number of all test cases. -int UnitTestImpl::total_test_case_count() const { - return static_cast(test_cases_.size()); -} - -// Gets the number of all test cases that contain at least one test -// that should run. -int UnitTestImpl::test_case_to_run_count() const { - return CountIf(test_cases_, ShouldRunTestCase); -} - -// Gets the number of successful tests. -int UnitTestImpl::successful_test_count() const { - return SumOverTestCaseList(test_cases_, &TestCase::successful_test_count); -} - -// Gets the number of failed tests. -int UnitTestImpl::failed_test_count() const { - return SumOverTestCaseList(test_cases_, &TestCase::failed_test_count); -} - -// Gets the number of disabled tests. -int UnitTestImpl::disabled_test_count() const { - return SumOverTestCaseList(test_cases_, &TestCase::disabled_test_count); -} - -// Gets the number of all tests. -int UnitTestImpl::total_test_count() const { - return SumOverTestCaseList(test_cases_, &TestCase::total_test_count); -} - -// Gets the number of tests that should run. -int UnitTestImpl::test_to_run_count() const { - return SumOverTestCaseList(test_cases_, &TestCase::test_to_run_count); -} - -// Returns the current OS stack trace as a String. -// -// The maximum number of stack frames to be included is specified by -// the gtest_stack_trace_depth flag. The skip_count parameter -// specifies the number of top frames to be skipped, which doesn't -// count against the number of frames to be included. -// -// For example, if Foo() calls Bar(), which in turn calls -// CurrentOsStackTraceExceptTop(1), Foo() will be included in the -// trace but Bar() and CurrentOsStackTraceExceptTop() won't. -String UnitTestImpl::CurrentOsStackTraceExceptTop(int skip_count) { - (void)skip_count; - return String(""); -} - -// Returns the current time in milliseconds. -TimeInMillis GetTimeInMillis() { -#if GTEST_OS_WINDOWS_MOBILE || defined(__BORLANDC__) - // Difference between 1970-01-01 and 1601-01-01 in milliseconds. - // http://analogous.blogspot.com/2005/04/epoch.html - const TimeInMillis kJavaEpochToWinFileTimeDelta = - static_cast(116444736UL) * 100000UL; - const DWORD kTenthMicrosInMilliSecond = 10000; - - SYSTEMTIME now_systime; - FILETIME now_filetime; - ULARGE_INTEGER now_int64; - // TODO(kenton@google.com): Shouldn't this just use - // GetSystemTimeAsFileTime()? - GetSystemTime(&now_systime); - if (SystemTimeToFileTime(&now_systime, &now_filetime)) { - now_int64.LowPart = now_filetime.dwLowDateTime; - now_int64.HighPart = now_filetime.dwHighDateTime; - now_int64.QuadPart = (now_int64.QuadPart / kTenthMicrosInMilliSecond) - - kJavaEpochToWinFileTimeDelta; - return now_int64.QuadPart; - } - return 0; -#elif GTEST_OS_WINDOWS && !GTEST_HAS_GETTIMEOFDAY_ - __timeb64 now; - -# ifdef _MSC_VER - - // MSVC 8 deprecates _ftime64(), so we want to suppress warning 4996 - // (deprecated function) there. - // TODO(kenton@google.com): Use GetTickCount()? Or use - // SystemTimeToFileTime() -# pragma warning(push) // Saves the current warning state. -# pragma warning(disable:4996) // Temporarily disables warning 4996. - _ftime64(&now); -# pragma warning(pop) // Restores the warning state. -# else - - _ftime64(&now); - -# endif // _MSC_VER - - return static_cast(now.time) * 1000 + now.millitm; -#elif GTEST_HAS_GETTIMEOFDAY_ - struct timeval now; - gettimeofday(&now, NULL); - return static_cast(now.tv_sec) * 1000 + now.tv_usec / 1000; -#else -# error "Don't know how to get the current time on your system." -#endif -} - -// Utilities - -// class String - -// Returns the input enclosed in double quotes if it's not NULL; -// otherwise returns "(null)". For example, "\"Hello\"" is returned -// for input "Hello". -// -// This is useful for printing a C string in the syntax of a literal. -// -// Known issue: escape sequences are not handled yet. -String String::ShowCStringQuoted(const char* c_str) { - return c_str ? String::Format("\"%s\"", c_str) : String("(null)"); -} - -// Copies at most length characters from str into a newly-allocated -// piece of memory of size length+1. The memory is allocated with new[]. -// A terminating null byte is written to the memory, and a pointer to it -// is returned. If str is NULL, NULL is returned. -static char* CloneString(const char* str, size_t length) { - if (str == NULL) { - return NULL; - } else { - char* const clone = new char[length + 1]; - posix::StrNCpy(clone, str, length); - clone[length] = '\0'; - return clone; - } -} - -// Clones a 0-terminated C string, allocating memory using new. The -// caller is responsible for deleting[] the return value. Returns the -// cloned string, or NULL if the input is NULL. -const char * String::CloneCString(const char* c_str) { - return (c_str == NULL) ? - NULL : CloneString(c_str, strlen(c_str)); -} - -#if GTEST_OS_WINDOWS_MOBILE -// Creates a UTF-16 wide string from the given ANSI string, allocating -// memory using new. The caller is responsible for deleting the return -// value using delete[]. Returns the wide string, or NULL if the -// input is NULL. -LPCWSTR String::AnsiToUtf16(const char* ansi) { - if (!ansi) return NULL; - const int length = strlen(ansi); - const int unicode_length = - MultiByteToWideChar(CP_ACP, 0, ansi, length, - NULL, 0); - WCHAR* unicode = new WCHAR[unicode_length + 1]; - MultiByteToWideChar(CP_ACP, 0, ansi, length, - unicode, unicode_length); - unicode[unicode_length] = 0; - return unicode; -} - -// Creates an ANSI string from the given wide string, allocating -// memory using new. The caller is responsible for deleting the return -// value using delete[]. Returns the ANSI string, or NULL if the -// input is NULL. -const char* String::Utf16ToAnsi(LPCWSTR utf16_str) { - if (!utf16_str) return NULL; - const int ansi_length = - WideCharToMultiByte(CP_ACP, 0, utf16_str, -1, - NULL, 0, NULL, NULL); - char* ansi = new char[ansi_length + 1]; - WideCharToMultiByte(CP_ACP, 0, utf16_str, -1, - ansi, ansi_length, NULL, NULL); - ansi[ansi_length] = 0; - return ansi; -} - -#endif // GTEST_OS_WINDOWS_MOBILE - -// Compares two C strings. Returns true iff they have the same content. -// -// Unlike strcmp(), this function can handle NULL argument(s). A NULL -// C string is considered different to any non-NULL C string, -// including the empty string. -bool String::CStringEquals(const char * lhs, const char * rhs) { - if ( lhs == NULL ) return rhs == NULL; - - if ( rhs == NULL ) return false; - - return strcmp(lhs, rhs) == 0; -} - -#if GTEST_HAS_STD_WSTRING || GTEST_HAS_GLOBAL_WSTRING - -// Converts an array of wide chars to a narrow string using the UTF-8 -// encoding, and streams the result to the given Message object. -static void StreamWideCharsToMessage(const wchar_t* wstr, size_t length, - Message* msg) { - // TODO(wan): consider allowing a testing::String object to - // contain '\0'. This will make it behave more like std::string, - // and will allow ToUtf8String() to return the correct encoding - // for '\0' s.t. we can get rid of the conditional here (and in - // several other places). - for (size_t i = 0; i != length; ) { // NOLINT - if (wstr[i] != L'\0') { - *msg << WideStringToUtf8(wstr + i, static_cast(length - i)); - while (i != length && wstr[i] != L'\0') - i++; - } else { - *msg << '\0'; - i++; - } - } -} - -#endif // GTEST_HAS_STD_WSTRING || GTEST_HAS_GLOBAL_WSTRING - -} // namespace internal - -#if GTEST_HAS_STD_WSTRING -// Converts the given wide string to a narrow string using the UTF-8 -// encoding, and streams the result to this Message object. -Message& Message::operator <<(const ::std::wstring& wstr) { - internal::StreamWideCharsToMessage(wstr.c_str(), wstr.length(), this); - return *this; -} -#endif // GTEST_HAS_STD_WSTRING - -#if GTEST_HAS_GLOBAL_WSTRING -// Converts the given wide string to a narrow string using the UTF-8 -// encoding, and streams the result to this Message object. -Message& Message::operator <<(const ::wstring& wstr) { - internal::StreamWideCharsToMessage(wstr.c_str(), wstr.length(), this); - return *this; -} -#endif // GTEST_HAS_GLOBAL_WSTRING - -// AssertionResult constructors. -// Used in EXPECT_TRUE/FALSE(assertion_result). -AssertionResult::AssertionResult(const AssertionResult& other) - : success_(other.success_), - message_(other.message_.get() != NULL ? - new ::std::string(*other.message_) : - static_cast< ::std::string*>(NULL)) { -} - -// Returns the assertion's negation. Used with EXPECT/ASSERT_FALSE. -AssertionResult AssertionResult::operator!() const { - AssertionResult negation(!success_); - if (message_.get() != NULL) - negation << *message_; - return negation; -} - -// Makes a successful assertion result. -AssertionResult AssertionSuccess() { - return AssertionResult(true); -} - -// Makes a failed assertion result. -AssertionResult AssertionFailure() { - return AssertionResult(false); -} - -// Makes a failed assertion result with the given failure message. -// Deprecated; use AssertionFailure() << message. -AssertionResult AssertionFailure(const Message& message) { - return AssertionFailure() << message; -} - -namespace internal { - -// Constructs and returns the message for an equality assertion -// (e.g. ASSERT_EQ, EXPECT_STREQ, etc) failure. -// -// The first four parameters are the expressions used in the assertion -// and their values, as strings. For example, for ASSERT_EQ(foo, bar) -// where foo is 5 and bar is 6, we have: -// -// expected_expression: "foo" -// actual_expression: "bar" -// expected_value: "5" -// actual_value: "6" -// -// The ignoring_case parameter is true iff the assertion is a -// *_STRCASEEQ*. When it's true, the string " (ignoring case)" will -// be inserted into the message. -AssertionResult EqFailure(const char* expected_expression, - const char* actual_expression, - const String& expected_value, - const String& actual_value, - bool ignoring_case) { - Message msg; - msg << "Value of: " << actual_expression; - if (actual_value != actual_expression) { - msg << "\n Actual: " << actual_value; - } - - msg << "\nExpected: " << expected_expression; - if (ignoring_case) { - msg << " (ignoring case)"; - } - if (expected_value != expected_expression) { - msg << "\nWhich is: " << expected_value; - } - - return AssertionFailure() << msg; -} - -// Constructs a failure message for Boolean assertions such as EXPECT_TRUE. -String GetBoolAssertionFailureMessage(const AssertionResult& assertion_result, - const char* expression_text, - const char* actual_predicate_value, - const char* expected_predicate_value) { - const char* actual_message = assertion_result.message(); - Message msg; - msg << "Value of: " << expression_text - << "\n Actual: " << actual_predicate_value; - if (actual_message[0] != '\0') - msg << " (" << actual_message << ")"; - msg << "\nExpected: " << expected_predicate_value; - return msg.GetString(); -} - -// Helper function for implementing ASSERT_NEAR. -AssertionResult DoubleNearPredFormat(const char* expr1, - const char* expr2, - const char* abs_error_expr, - double val1, - double val2, - double abs_error) { - const double diff = fabs(val1 - val2); - if (diff <= abs_error) return AssertionSuccess(); - - // TODO(wan): do not print the value of an expression if it's - // already a literal. - return AssertionFailure() - << "The difference between " << expr1 << " and " << expr2 - << " is " << diff << ", which exceeds " << abs_error_expr << ", where\n" - << expr1 << " evaluates to " << val1 << ",\n" - << expr2 << " evaluates to " << val2 << ", and\n" - << abs_error_expr << " evaluates to " << abs_error << "."; -} - - -// Helper template for implementing FloatLE() and DoubleLE(). -template -AssertionResult FloatingPointLE(const char* expr1, - const char* expr2, - RawType val1, - RawType val2) { - // Returns success if val1 is less than val2, - if (val1 < val2) { - return AssertionSuccess(); - } - - // or if val1 is almost equal to val2. - const FloatingPoint lhs(val1), rhs(val2); - if (lhs.AlmostEquals(rhs)) { - return AssertionSuccess(); - } - - // Note that the above two checks will both fail if either val1 or - // val2 is NaN, as the IEEE floating-point standard requires that - // any predicate involving a NaN must return false. - - ::std::stringstream val1_ss; - val1_ss << std::setprecision(std::numeric_limits::digits10 + 2) - << val1; - - ::std::stringstream val2_ss; - val2_ss << std::setprecision(std::numeric_limits::digits10 + 2) - << val2; - - return AssertionFailure() - << "Expected: (" << expr1 << ") <= (" << expr2 << ")\n" - << " Actual: " << StringStreamToString(&val1_ss) << " vs " - << StringStreamToString(&val2_ss); -} - -} // namespace internal - -// Asserts that val1 is less than, or almost equal to, val2. Fails -// otherwise. In particular, it fails if either val1 or val2 is NaN. -AssertionResult FloatLE(const char* expr1, const char* expr2, - float val1, float val2) { - return internal::FloatingPointLE(expr1, expr2, val1, val2); -} - -// Asserts that val1 is less than, or almost equal to, val2. Fails -// otherwise. In particular, it fails if either val1 or val2 is NaN. -AssertionResult DoubleLE(const char* expr1, const char* expr2, - double val1, double val2) { - return internal::FloatingPointLE(expr1, expr2, val1, val2); -} - -namespace internal { - -// The helper function for {ASSERT|EXPECT}_EQ with int or enum -// arguments. -AssertionResult CmpHelperEQ(const char* expected_expression, - const char* actual_expression, - BiggestInt expected, - BiggestInt actual) { - if (expected == actual) { - return AssertionSuccess(); - } - - return EqFailure(expected_expression, - actual_expression, - FormatForComparisonFailureMessage(expected, actual), - FormatForComparisonFailureMessage(actual, expected), - false); -} - -// A macro for implementing the helper functions needed to implement -// ASSERT_?? and EXPECT_?? with integer or enum arguments. It is here -// just to avoid copy-and-paste of similar code. -#define GTEST_IMPL_CMP_HELPER_(op_name, op)\ -AssertionResult CmpHelper##op_name(const char* expr1, const char* expr2, \ - BiggestInt val1, BiggestInt val2) {\ - if (val1 op val2) {\ - return AssertionSuccess();\ - } else {\ - return AssertionFailure() \ - << "Expected: (" << expr1 << ") " #op " (" << expr2\ - << "), actual: " << FormatForComparisonFailureMessage(val1, val2)\ - << " vs " << FormatForComparisonFailureMessage(val2, val1);\ - }\ -} - -// Implements the helper function for {ASSERT|EXPECT}_NE with int or -// enum arguments. -GTEST_IMPL_CMP_HELPER_(NE, !=) -// Implements the helper function for {ASSERT|EXPECT}_LE with int or -// enum arguments. -GTEST_IMPL_CMP_HELPER_(LE, <=) -// Implements the helper function for {ASSERT|EXPECT}_LT with int or -// enum arguments. -GTEST_IMPL_CMP_HELPER_(LT, < ) -// Implements the helper function for {ASSERT|EXPECT}_GE with int or -// enum arguments. -GTEST_IMPL_CMP_HELPER_(GE, >=) -// Implements the helper function for {ASSERT|EXPECT}_GT with int or -// enum arguments. -GTEST_IMPL_CMP_HELPER_(GT, > ) - -#undef GTEST_IMPL_CMP_HELPER_ - -// The helper function for {ASSERT|EXPECT}_STREQ. -AssertionResult CmpHelperSTREQ(const char* expected_expression, - const char* actual_expression, - const char* expected, - const char* actual) { - if (String::CStringEquals(expected, actual)) { - return AssertionSuccess(); - } - - return EqFailure(expected_expression, - actual_expression, - String::ShowCStringQuoted(expected), - String::ShowCStringQuoted(actual), - false); -} - -// The helper function for {ASSERT|EXPECT}_STRCASEEQ. -AssertionResult CmpHelperSTRCASEEQ(const char* expected_expression, - const char* actual_expression, - const char* expected, - const char* actual) { - if (String::CaseInsensitiveCStringEquals(expected, actual)) { - return AssertionSuccess(); - } - - return EqFailure(expected_expression, - actual_expression, - String::ShowCStringQuoted(expected), - String::ShowCStringQuoted(actual), - true); -} - -// The helper function for {ASSERT|EXPECT}_STRNE. -AssertionResult CmpHelperSTRNE(const char* s1_expression, - const char* s2_expression, - const char* s1, - const char* s2) { - if (!String::CStringEquals(s1, s2)) { - return AssertionSuccess(); - } else { - return AssertionFailure() << "Expected: (" << s1_expression << ") != (" - << s2_expression << "), actual: \"" - << s1 << "\" vs \"" << s2 << "\""; - } -} - -// The helper function for {ASSERT|EXPECT}_STRCASENE. -AssertionResult CmpHelperSTRCASENE(const char* s1_expression, - const char* s2_expression, - const char* s1, - const char* s2) { - if (!String::CaseInsensitiveCStringEquals(s1, s2)) { - return AssertionSuccess(); - } else { - return AssertionFailure() - << "Expected: (" << s1_expression << ") != (" - << s2_expression << ") (ignoring case), actual: \"" - << s1 << "\" vs \"" << s2 << "\""; - } -} - -} // namespace internal - -namespace { - -// Helper functions for implementing IsSubString() and IsNotSubstring(). - -// This group of overloaded functions return true iff needle is a -// substring of haystack. NULL is considered a substring of itself -// only. - -bool IsSubstringPred(const char* needle, const char* haystack) { - if (needle == NULL || haystack == NULL) - return needle == haystack; - - return strstr(haystack, needle) != NULL; -} - -bool IsSubstringPred(const wchar_t* needle, const wchar_t* haystack) { - if (needle == NULL || haystack == NULL) - return needle == haystack; - - return wcsstr(haystack, needle) != NULL; -} - -// StringType here can be either ::std::string or ::std::wstring. -template -bool IsSubstringPred(const StringType& needle, - const StringType& haystack) { - return haystack.find(needle) != StringType::npos; -} - -// This function implements either IsSubstring() or IsNotSubstring(), -// depending on the value of the expected_to_be_substring parameter. -// StringType here can be const char*, const wchar_t*, ::std::string, -// or ::std::wstring. -template -AssertionResult IsSubstringImpl( - bool expected_to_be_substring, - const char* needle_expr, const char* haystack_expr, - const StringType& needle, const StringType& haystack) { - if (IsSubstringPred(needle, haystack) == expected_to_be_substring) - return AssertionSuccess(); - - const bool is_wide_string = sizeof(needle[0]) > 1; - const char* const begin_string_quote = is_wide_string ? "L\"" : "\""; - return AssertionFailure() - << "Value of: " << needle_expr << "\n" - << " Actual: " << begin_string_quote << needle << "\"\n" - << "Expected: " << (expected_to_be_substring ? "" : "not ") - << "a substring of " << haystack_expr << "\n" - << "Which is: " << begin_string_quote << haystack << "\""; -} - -} // namespace - -// IsSubstring() and IsNotSubstring() check whether needle is a -// substring of haystack (NULL is considered a substring of itself -// only), and return an appropriate error message when they fail. - -AssertionResult IsSubstring( - const char* needle_expr, const char* haystack_expr, - const char* needle, const char* haystack) { - return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); -} - -AssertionResult IsSubstring( - const char* needle_expr, const char* haystack_expr, - const wchar_t* needle, const wchar_t* haystack) { - return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); -} - -AssertionResult IsNotSubstring( - const char* needle_expr, const char* haystack_expr, - const char* needle, const char* haystack) { - return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); -} - -AssertionResult IsNotSubstring( - const char* needle_expr, const char* haystack_expr, - const wchar_t* needle, const wchar_t* haystack) { - return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); -} - -AssertionResult IsSubstring( - const char* needle_expr, const char* haystack_expr, - const ::std::string& needle, const ::std::string& haystack) { - return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); -} - -AssertionResult IsNotSubstring( - const char* needle_expr, const char* haystack_expr, - const ::std::string& needle, const ::std::string& haystack) { - return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); -} - -#if GTEST_HAS_STD_WSTRING -AssertionResult IsSubstring( - const char* needle_expr, const char* haystack_expr, - const ::std::wstring& needle, const ::std::wstring& haystack) { - return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); -} - -AssertionResult IsNotSubstring( - const char* needle_expr, const char* haystack_expr, - const ::std::wstring& needle, const ::std::wstring& haystack) { - return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); -} -#endif // GTEST_HAS_STD_WSTRING - -namespace internal { - -#if GTEST_OS_WINDOWS - -namespace { - -// Helper function for IsHRESULT{SuccessFailure} predicates -AssertionResult HRESULTFailureHelper(const char* expr, - const char* expected, - long hr) { // NOLINT -# if GTEST_OS_WINDOWS_MOBILE - - // Windows CE doesn't support FormatMessage. - const char error_text[] = ""; - -# else - - // Looks up the human-readable system message for the HRESULT code - // and since we're not passing any params to FormatMessage, we don't - // want inserts expanded. - const DWORD kFlags = FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS; - const DWORD kBufSize = 4096; // String::Format can't exceed this length. - // Gets the system's human readable message string for this HRESULT. - char error_text[kBufSize] = { '\0' }; - DWORD message_length = ::FormatMessageA(kFlags, - 0, // no source, we're asking system - hr, // the error - 0, // no line width restrictions - error_text, // output buffer - kBufSize, // buf size - NULL); // no arguments for inserts - // Trims tailing white space (FormatMessage leaves a trailing cr-lf) - for (; message_length && IsSpace(error_text[message_length - 1]); - --message_length) { - error_text[message_length - 1] = '\0'; - } - -# endif // GTEST_OS_WINDOWS_MOBILE - - const String error_hex(String::Format("0x%08X ", hr)); - return ::testing::AssertionFailure() - << "Expected: " << expr << " " << expected << ".\n" - << " Actual: " << error_hex << error_text << "\n"; -} - -} // namespace - -AssertionResult IsHRESULTSuccess(const char* expr, long hr) { // NOLINT - if (SUCCEEDED(hr)) { - return AssertionSuccess(); - } - return HRESULTFailureHelper(expr, "succeeds", hr); -} - -AssertionResult IsHRESULTFailure(const char* expr, long hr) { // NOLINT - if (FAILED(hr)) { - return AssertionSuccess(); - } - return HRESULTFailureHelper(expr, "fails", hr); -} - -#endif // GTEST_OS_WINDOWS - -// Utility functions for encoding Unicode text (wide strings) in -// UTF-8. - -// A Unicode code-point can have upto 21 bits, and is encoded in UTF-8 -// like this: -// -// Code-point length Encoding -// 0 - 7 bits 0xxxxxxx -// 8 - 11 bits 110xxxxx 10xxxxxx -// 12 - 16 bits 1110xxxx 10xxxxxx 10xxxxxx -// 17 - 21 bits 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - -// The maximum code-point a one-byte UTF-8 sequence can represent. -const UInt32 kMaxCodePoint1 = (static_cast(1) << 7) - 1; - -// The maximum code-point a two-byte UTF-8 sequence can represent. -const UInt32 kMaxCodePoint2 = (static_cast(1) << (5 + 6)) - 1; - -// The maximum code-point a three-byte UTF-8 sequence can represent. -const UInt32 kMaxCodePoint3 = (static_cast(1) << (4 + 2*6)) - 1; - -// The maximum code-point a four-byte UTF-8 sequence can represent. -const UInt32 kMaxCodePoint4 = (static_cast(1) << (3 + 3*6)) - 1; - -// Chops off the n lowest bits from a bit pattern. Returns the n -// lowest bits. As a side effect, the original bit pattern will be -// shifted to the right by n bits. -inline UInt32 ChopLowBits(UInt32* bits, int n) { - const UInt32 low_bits = *bits & ((static_cast(1) << n) - 1); - *bits >>= n; - return low_bits; -} - -// Converts a Unicode code point to a narrow string in UTF-8 encoding. -// code_point parameter is of type UInt32 because wchar_t may not be -// wide enough to contain a code point. -// The output buffer str must containt at least 32 characters. -// The function returns the address of the output buffer. -// If the code_point is not a valid Unicode code point -// (i.e. outside of Unicode range U+0 to U+10FFFF) it will be output -// as '(Invalid Unicode 0xXXXXXXXX)'. -char* CodePointToUtf8(UInt32 code_point, char* str) { - if (code_point <= kMaxCodePoint1) { - str[1] = '\0'; - str[0] = static_cast(code_point); // 0xxxxxxx - } else if (code_point <= kMaxCodePoint2) { - str[2] = '\0'; - str[1] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx - str[0] = static_cast(0xC0 | code_point); // 110xxxxx - } else if (code_point <= kMaxCodePoint3) { - str[3] = '\0'; - str[2] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx - str[1] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx - str[0] = static_cast(0xE0 | code_point); // 1110xxxx - } else if (code_point <= kMaxCodePoint4) { - str[4] = '\0'; - str[3] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx - str[2] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx - str[1] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx - str[0] = static_cast(0xF0 | code_point); // 11110xxx - } else { - // The longest string String::Format can produce when invoked - // with these parameters is 28 character long (not including - // the terminating nul character). We are asking for 32 character - // buffer just in case. This is also enough for strncpy to - // null-terminate the destination string. - posix::StrNCpy( - str, String::Format("(Invalid Unicode 0x%X)", code_point).c_str(), 32); - str[31] = '\0'; // Makes sure no change in the format to strncpy leaves - // the result unterminated. - } - return str; -} - -// The following two functions only make sense if the the system -// uses UTF-16 for wide string encoding. All supported systems -// with 16 bit wchar_t (Windows, Cygwin, Symbian OS) do use UTF-16. - -// Determines if the arguments constitute UTF-16 surrogate pair -// and thus should be combined into a single Unicode code point -// using CreateCodePointFromUtf16SurrogatePair. -inline bool IsUtf16SurrogatePair(wchar_t first, wchar_t second) { - return sizeof(wchar_t) == 2 && - (first & 0xFC00) == 0xD800 && (second & 0xFC00) == 0xDC00; -} - -// Creates a Unicode code point from UTF16 surrogate pair. -inline UInt32 CreateCodePointFromUtf16SurrogatePair(wchar_t first, - wchar_t second) { - const UInt32 mask = (1 << 10) - 1; - return (sizeof(wchar_t) == 2) ? - (((first & mask) << 10) | (second & mask)) + 0x10000 : - // This function should not be called when the condition is - // false, but we provide a sensible default in case it is. - static_cast(first); -} - -// Converts a wide string to a narrow string in UTF-8 encoding. -// The wide string is assumed to have the following encoding: -// UTF-16 if sizeof(wchar_t) == 2 (on Windows, Cygwin, Symbian OS) -// UTF-32 if sizeof(wchar_t) == 4 (on Linux) -// Parameter str points to a null-terminated wide string. -// Parameter num_chars may additionally limit the number -// of wchar_t characters processed. -1 is used when the entire string -// should be processed. -// If the string contains code points that are not valid Unicode code points -// (i.e. outside of Unicode range U+0 to U+10FFFF) they will be output -// as '(Invalid Unicode 0xXXXXXXXX)'. If the string is in UTF16 encoding -// and contains invalid UTF-16 surrogate pairs, values in those pairs -// will be encoded as individual Unicode characters from Basic Normal Plane. -String WideStringToUtf8(const wchar_t* str, int num_chars) { - if (num_chars == -1) - num_chars = static_cast(wcslen(str)); - - ::std::stringstream stream; - for (int i = 0; i < num_chars; ++i) { - UInt32 unicode_code_point; - - if (str[i] == L'\0') { - break; - } else if (i + 1 < num_chars && IsUtf16SurrogatePair(str[i], str[i + 1])) { - unicode_code_point = CreateCodePointFromUtf16SurrogatePair(str[i], - str[i + 1]); - i++; - } else { - unicode_code_point = static_cast(str[i]); - } - - char buffer[32]; // CodePointToUtf8 requires a buffer this big. - stream << CodePointToUtf8(unicode_code_point, buffer); - } - return StringStreamToString(&stream); -} - -// Converts a wide C string to a String using the UTF-8 encoding. -// NULL will be converted to "(null)". -String String::ShowWideCString(const wchar_t * wide_c_str) { - if (wide_c_str == NULL) return String("(null)"); - - return String(internal::WideStringToUtf8(wide_c_str, -1).c_str()); -} - -// Similar to ShowWideCString(), except that this function encloses -// the converted string in double quotes. -String String::ShowWideCStringQuoted(const wchar_t* wide_c_str) { - if (wide_c_str == NULL) return String("(null)"); - - return String::Format("L\"%s\"", - String::ShowWideCString(wide_c_str).c_str()); -} - -// Compares two wide C strings. Returns true iff they have the same -// content. -// -// Unlike wcscmp(), this function can handle NULL argument(s). A NULL -// C string is considered different to any non-NULL C string, -// including the empty string. -bool String::WideCStringEquals(const wchar_t * lhs, const wchar_t * rhs) { - if (lhs == NULL) return rhs == NULL; - - if (rhs == NULL) return false; - - return wcscmp(lhs, rhs) == 0; -} - -// Helper function for *_STREQ on wide strings. -AssertionResult CmpHelperSTREQ(const char* expected_expression, - const char* actual_expression, - const wchar_t* expected, - const wchar_t* actual) { - if (String::WideCStringEquals(expected, actual)) { - return AssertionSuccess(); - } - - return EqFailure(expected_expression, - actual_expression, - String::ShowWideCStringQuoted(expected), - String::ShowWideCStringQuoted(actual), - false); -} - -// Helper function for *_STRNE on wide strings. -AssertionResult CmpHelperSTRNE(const char* s1_expression, - const char* s2_expression, - const wchar_t* s1, - const wchar_t* s2) { - if (!String::WideCStringEquals(s1, s2)) { - return AssertionSuccess(); - } - - return AssertionFailure() << "Expected: (" << s1_expression << ") != (" - << s2_expression << "), actual: " - << String::ShowWideCStringQuoted(s1) - << " vs " << String::ShowWideCStringQuoted(s2); -} - -// Compares two C strings, ignoring case. Returns true iff they have -// the same content. -// -// Unlike strcasecmp(), this function can handle NULL argument(s). A -// NULL C string is considered different to any non-NULL C string, -// including the empty string. -bool String::CaseInsensitiveCStringEquals(const char * lhs, const char * rhs) { - if (lhs == NULL) - return rhs == NULL; - if (rhs == NULL) - return false; - return posix::StrCaseCmp(lhs, rhs) == 0; -} - - // Compares two wide C strings, ignoring case. Returns true iff they - // have the same content. - // - // Unlike wcscasecmp(), this function can handle NULL argument(s). - // A NULL C string is considered different to any non-NULL wide C string, - // including the empty string. - // NB: The implementations on different platforms slightly differ. - // On windows, this method uses _wcsicmp which compares according to LC_CTYPE - // environment variable. On GNU platform this method uses wcscasecmp - // which compares according to LC_CTYPE category of the current locale. - // On MacOS X, it uses towlower, which also uses LC_CTYPE category of the - // current locale. -bool String::CaseInsensitiveWideCStringEquals(const wchar_t* lhs, - const wchar_t* rhs) { - if (lhs == NULL) return rhs == NULL; - - if (rhs == NULL) return false; - -#if GTEST_OS_WINDOWS - return _wcsicmp(lhs, rhs) == 0; -#elif GTEST_OS_LINUX && !GTEST_OS_LINUX_ANDROID - return wcscasecmp(lhs, rhs) == 0; -#else - // Android, Mac OS X and Cygwin don't define wcscasecmp. - // Other unknown OSes may not define it either. - wint_t left, right; - do { - left = towlower(*lhs++); - right = towlower(*rhs++); - } while (left && left == right); - return left == right; -#endif // OS selector -} - -// Compares this with another String. -// Returns < 0 if this is less than rhs, 0 if this is equal to rhs, or > 0 -// if this is greater than rhs. -int String::Compare(const String & rhs) const { - const char* const lhs_c_str = c_str(); - const char* const rhs_c_str = rhs.c_str(); - - if (lhs_c_str == NULL) { - return rhs_c_str == NULL ? 0 : -1; // NULL < anything except NULL - } else if (rhs_c_str == NULL) { - return 1; - } - - const size_t shorter_str_len = - length() <= rhs.length() ? length() : rhs.length(); - for (size_t i = 0; i != shorter_str_len; i++) { - if (lhs_c_str[i] < rhs_c_str[i]) { - return -1; - } else if (lhs_c_str[i] > rhs_c_str[i]) { - return 1; - } - } - return (length() < rhs.length()) ? -1 : - (length() > rhs.length()) ? 1 : 0; -} - -// Returns true iff this String ends with the given suffix. *Any* -// String is considered to end with a NULL or empty suffix. -bool String::EndsWith(const char* suffix) const { - if (suffix == NULL || CStringEquals(suffix, "")) return true; - - if (c_str() == NULL) return false; - - const size_t this_len = strlen(c_str()); - const size_t suffix_len = strlen(suffix); - return (this_len >= suffix_len) && - CStringEquals(c_str() + this_len - suffix_len, suffix); -} - -// Returns true iff this String ends with the given suffix, ignoring case. -// Any String is considered to end with a NULL or empty suffix. -bool String::EndsWithCaseInsensitive(const char* suffix) const { - if (suffix == NULL || CStringEquals(suffix, "")) return true; - - if (c_str() == NULL) return false; - - const size_t this_len = strlen(c_str()); - const size_t suffix_len = strlen(suffix); - return (this_len >= suffix_len) && - CaseInsensitiveCStringEquals(c_str() + this_len - suffix_len, suffix); -} - -// Formats a list of arguments to a String, using the same format -// spec string as for printf. -// -// We do not use the StringPrintf class as it is not universally -// available. -// -// The result is limited to 4096 characters (including the tailing 0). -// If 4096 characters are not enough to format the input, or if -// there's an error, "" is -// returned. -String String::Format(const char * format, ...) { - va_list args; - va_start(args, format); - - char buffer[4096]; - const int kBufferSize = sizeof(buffer)/sizeof(buffer[0]); - - // MSVC 8 deprecates vsnprintf(), so we want to suppress warning - // 4996 (deprecated function) there. -#ifdef _MSC_VER // We are using MSVC. -# pragma warning(push) // Saves the current warning state. -# pragma warning(disable:4996) // Temporarily disables warning 4996. - - const int size = vsnprintf(buffer, kBufferSize, format, args); - -# pragma warning(pop) // Restores the warning state. -#else // We are not using MSVC. - const int size = vsnprintf(buffer, kBufferSize, format, args); -#endif // _MSC_VER - va_end(args); - - // vsnprintf()'s behavior is not portable. When the buffer is not - // big enough, it returns a negative value in MSVC, and returns the - // needed buffer size on Linux. When there is an output error, it - // always returns a negative value. For simplicity, we lump the two - // error cases together. - if (size < 0 || size >= kBufferSize) { - return String(""); - } else { - return String(buffer, size); - } -} - -// Converts the buffer in a stringstream to a String, converting NUL -// bytes to "\\0" along the way. -String StringStreamToString(::std::stringstream* ss) { - const ::std::string& str = ss->str(); - const char* const start = str.c_str(); - const char* const end = start + str.length(); - - // We need to use a helper stringstream to do this transformation - // because String doesn't support push_back(). - ::std::stringstream helper; - for (const char* ch = start; ch != end; ++ch) { - if (*ch == '\0') { - helper << "\\0"; // Replaces NUL with "\\0"; - } else { - helper.put(*ch); - } - } - - return String(helper.str().c_str()); -} - -// Appends the user-supplied message to the Google-Test-generated message. -String AppendUserMessage(const String& gtest_msg, - const Message& user_msg) { - // Appends the user message if it's non-empty. - const String user_msg_string = user_msg.GetString(); - if (user_msg_string.empty()) { - return gtest_msg; - } - - Message msg; - msg << gtest_msg << "\n" << user_msg_string; - - return msg.GetString(); -} - -} // namespace internal - -// class TestResult - -// Creates an empty TestResult. -TestResult::TestResult() - : death_test_count_(0), - elapsed_time_(0) { -} - -// D'tor. -TestResult::~TestResult() { -} - -// Returns the i-th test part result among all the results. i can -// range from 0 to total_part_count() - 1. If i is not in that range, -// aborts the program. -const TestPartResult& TestResult::GetTestPartResult(int i) const { - if (i < 0 || i >= total_part_count()) - internal::posix::Abort(); - return test_part_results_.at(i); -} - -// Returns the i-th test property. i can range from 0 to -// test_property_count() - 1. If i is not in that range, aborts the -// program. -const TestProperty& TestResult::GetTestProperty(int i) const { - if (i < 0 || i >= test_property_count()) - internal::posix::Abort(); - return test_properties_.at(i); -} - -// Clears the test part results. -void TestResult::ClearTestPartResults() { - test_part_results_.clear(); -} - -// Adds a test part result to the list. -void TestResult::AddTestPartResult(const TestPartResult& test_part_result) { - test_part_results_.push_back(test_part_result); -} - -// Adds a test property to the list. If a property with the same key as the -// supplied property is already represented, the value of this test_property -// replaces the old value for that key. -void TestResult::RecordProperty(const TestProperty& test_property) { - if (!ValidateTestProperty(test_property)) { - return; - } - internal::MutexLock lock(&test_properites_mutex_); - const std::vector::iterator property_with_matching_key = - std::find_if(test_properties_.begin(), test_properties_.end(), - internal::TestPropertyKeyIs(test_property.key())); - if (property_with_matching_key == test_properties_.end()) { - test_properties_.push_back(test_property); - return; - } - property_with_matching_key->SetValue(test_property.value()); -} - -// Adds a failure if the key is a reserved attribute of Google Test -// testcase tags. Returns true if the property is valid. -bool TestResult::ValidateTestProperty(const TestProperty& test_property) { - internal::String key(test_property.key()); - if (key == "name" || key == "status" || key == "time" || key == "classname") { - ADD_FAILURE() - << "Reserved key used in RecordProperty(): " - << key - << " ('name', 'status', 'time', and 'classname' are reserved by " - << GTEST_NAME_ << ")"; - return false; - } - return true; -} - -// Clears the object. -void TestResult::Clear() { - test_part_results_.clear(); - test_properties_.clear(); - death_test_count_ = 0; - elapsed_time_ = 0; -} - -// Returns true iff the test failed. -bool TestResult::Failed() const { - for (int i = 0; i < total_part_count(); ++i) { - if (GetTestPartResult(i).failed()) - return true; - } - return false; -} - -// Returns true iff the test part fatally failed. -static bool TestPartFatallyFailed(const TestPartResult& result) { - return result.fatally_failed(); -} - -// Returns true iff the test fatally failed. -bool TestResult::HasFatalFailure() const { - return CountIf(test_part_results_, TestPartFatallyFailed) > 0; -} - -// Returns true iff the test part non-fatally failed. -static bool TestPartNonfatallyFailed(const TestPartResult& result) { - return result.nonfatally_failed(); -} - -// Returns true iff the test has a non-fatal failure. -bool TestResult::HasNonfatalFailure() const { - return CountIf(test_part_results_, TestPartNonfatallyFailed) > 0; -} - -// Gets the number of all test parts. This is the sum of the number -// of successful test parts and the number of failed test parts. -int TestResult::total_part_count() const { - return static_cast(test_part_results_.size()); -} - -// Returns the number of the test properties. -int TestResult::test_property_count() const { - return static_cast(test_properties_.size()); -} - -// class Test - -// Creates a Test object. - -// The c'tor saves the values of all Google Test flags. -Test::Test() - : gtest_flag_saver_(new internal::GTestFlagSaver) { -} - -// The d'tor restores the values of all Google Test flags. -Test::~Test() { - delete gtest_flag_saver_; -} - -// Sets up the test fixture. -// -// A sub-class may override this. -void Test::SetUp() { -} - -// Tears down the test fixture. -// -// A sub-class may override this. -void Test::TearDown() { -} - -// Allows user supplied key value pairs to be recorded for later output. -void Test::RecordProperty(const char* key, const char* value) { - UnitTest::GetInstance()->RecordPropertyForCurrentTest(key, value); -} - -// Allows user supplied key value pairs to be recorded for later output. -void Test::RecordProperty(const char* key, int value) { - Message value_message; - value_message << value; - RecordProperty(key, value_message.GetString().c_str()); -} - -namespace internal { - -void ReportFailureInUnknownLocation(TestPartResult::Type result_type, - const String& message) { - // This function is a friend of UnitTest and as such has access to - // AddTestPartResult. - UnitTest::GetInstance()->AddTestPartResult( - result_type, - NULL, // No info about the source file where the exception occurred. - -1, // We have no info on which line caused the exception. - message, - String()); // No stack trace, either. -} - -} // namespace internal - -// Google Test requires all tests in the same test case to use the same test -// fixture class. This function checks if the current test has the -// same fixture class as the first test in the current test case. If -// yes, it returns true; otherwise it generates a Google Test failure and -// returns false. -bool Test::HasSameFixtureClass() { - internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); - const TestCase* const test_case = impl->current_test_case(); - - // Info about the first test in the current test case. - const TestInfo* const first_test_info = test_case->test_info_list()[0]; - const internal::TypeId first_fixture_id = first_test_info->fixture_class_id_; - const char* const first_test_name = first_test_info->name(); - - // Info about the current test. - const TestInfo* const this_test_info = impl->current_test_info(); - const internal::TypeId this_fixture_id = this_test_info->fixture_class_id_; - const char* const this_test_name = this_test_info->name(); - - if (this_fixture_id != first_fixture_id) { - // Is the first test defined using TEST? - const bool first_is_TEST = first_fixture_id == internal::GetTestTypeId(); - // Is this test defined using TEST? - const bool this_is_TEST = this_fixture_id == internal::GetTestTypeId(); - - if (first_is_TEST || this_is_TEST) { - // The user mixed TEST and TEST_F in this test case - we'll tell - // him/her how to fix it. - - // Gets the name of the TEST and the name of the TEST_F. Note - // that first_is_TEST and this_is_TEST cannot both be true, as - // the fixture IDs are different for the two tests. - const char* const TEST_name = - first_is_TEST ? first_test_name : this_test_name; - const char* const TEST_F_name = - first_is_TEST ? this_test_name : first_test_name; - - ADD_FAILURE() - << "All tests in the same test case must use the same test fixture\n" - << "class, so mixing TEST_F and TEST in the same test case is\n" - << "illegal. In test case " << this_test_info->test_case_name() - << ",\n" - << "test " << TEST_F_name << " is defined using TEST_F but\n" - << "test " << TEST_name << " is defined using TEST. You probably\n" - << "want to change the TEST to TEST_F or move it to another test\n" - << "case."; - } else { - // The user defined two fixture classes with the same name in - // two namespaces - we'll tell him/her how to fix it. - ADD_FAILURE() - << "All tests in the same test case must use the same test fixture\n" - << "class. However, in test case " - << this_test_info->test_case_name() << ",\n" - << "you defined test " << first_test_name - << " and test " << this_test_name << "\n" - << "using two different test fixture classes. This can happen if\n" - << "the two classes are from different namespaces or translation\n" - << "units and have the same name. You should probably rename one\n" - << "of the classes to put the tests into different test cases."; - } - return false; - } - - return true; -} - -#if GTEST_HAS_SEH - -// Adds an "exception thrown" fatal failure to the current test. This -// function returns its result via an output parameter pointer because VC++ -// prohibits creation of objects with destructors on stack in functions -// using __try (see error C2712). -static internal::String* FormatSehExceptionMessage(DWORD exception_code, - const char* location) { - Message message; - message << "SEH exception with code 0x" << std::setbase(16) << - exception_code << std::setbase(10) << " thrown in " << location << "."; - - return new internal::String(message.GetString()); -} - -#endif // GTEST_HAS_SEH - -#if GTEST_HAS_EXCEPTIONS - -// Adds an "exception thrown" fatal failure to the current test. -static internal::String FormatCxxExceptionMessage(const char* description, - const char* location) { - Message message; - if (description != NULL) { - message << "C++ exception with description \"" << description << "\""; - } else { - message << "Unknown C++ exception"; - } - message << " thrown in " << location << "."; - - return message.GetString(); -} - -static internal::String PrintTestPartResultToString( - const TestPartResult& test_part_result); - -// A failed Google Test assertion will throw an exception of this type when -// GTEST_FLAG(throw_on_failure) is true (if exceptions are enabled). We -// derive it from std::runtime_error, which is for errors presumably -// detectable only at run time. Since std::runtime_error inherits from -// std::exception, many testing frameworks know how to extract and print the -// message inside it. -class GoogleTestFailureException : public ::std::runtime_error { - public: - explicit GoogleTestFailureException(const TestPartResult& failure) - : ::std::runtime_error(PrintTestPartResultToString(failure).c_str()) {} -}; -#endif // GTEST_HAS_EXCEPTIONS - -namespace internal { -// We put these helper functions in the internal namespace as IBM's xlC -// compiler rejects the code if they were declared static. - -// Runs the given method and handles SEH exceptions it throws, when -// SEH is supported; returns the 0-value for type Result in case of an -// SEH exception. (Microsoft compilers cannot handle SEH and C++ -// exceptions in the same function. Therefore, we provide a separate -// wrapper function for handling SEH exceptions.) -template -Result HandleSehExceptionsInMethodIfSupported( - T* object, Result (T::*method)(), const char* location) { -#if GTEST_HAS_SEH - __try { - return (object->*method)(); - } __except (internal::UnitTestOptions::GTestShouldProcessSEH( // NOLINT - GetExceptionCode())) { - // We create the exception message on the heap because VC++ prohibits - // creation of objects with destructors on stack in functions using __try - // (see error C2712). - internal::String* exception_message = FormatSehExceptionMessage( - GetExceptionCode(), location); - internal::ReportFailureInUnknownLocation(TestPartResult::kFatalFailure, - *exception_message); - delete exception_message; - return static_cast(0); - } -#else - (void)location; - return (object->*method)(); -#endif // GTEST_HAS_SEH -} - -// Runs the given method and catches and reports C++ and/or SEH-style -// exceptions, if they are supported; returns the 0-value for type -// Result in case of an SEH exception. -template -Result HandleExceptionsInMethodIfSupported( - T* object, Result (T::*method)(), const char* location) { - // NOTE: The user code can affect the way in which Google Test handles - // exceptions by setting GTEST_FLAG(catch_exceptions), but only before - // RUN_ALL_TESTS() starts. It is technically possible to check the flag - // after the exception is caught and either report or re-throw the - // exception based on the flag's value: - // - // try { - // // Perform the test method. - // } catch (...) { - // if (GTEST_FLAG(catch_exceptions)) - // // Report the exception as failure. - // else - // throw; // Re-throws the original exception. - // } - // - // However, the purpose of this flag is to allow the program to drop into - // the debugger when the exception is thrown. On most platforms, once the - // control enters the catch block, the exception origin information is - // lost and the debugger will stop the program at the point of the - // re-throw in this function -- instead of at the point of the original - // throw statement in the code under test. For this reason, we perform - // the check early, sacrificing the ability to affect Google Test's - // exception handling in the method where the exception is thrown. - if (internal::GetUnitTestImpl()->catch_exceptions()) { -#if GTEST_HAS_EXCEPTIONS - try { - return HandleSehExceptionsInMethodIfSupported(object, method, location); - } catch (const GoogleTestFailureException&) { // NOLINT - // This exception doesn't originate in code under test. It makes no - // sense to report it as a test failure. - throw; - } catch (const std::exception& e) { // NOLINT - internal::ReportFailureInUnknownLocation( - TestPartResult::kFatalFailure, - FormatCxxExceptionMessage(e.what(), location)); - } catch (...) { // NOLINT - internal::ReportFailureInUnknownLocation( - TestPartResult::kFatalFailure, - FormatCxxExceptionMessage(NULL, location)); - } - return static_cast(0); -#else - return HandleSehExceptionsInMethodIfSupported(object, method, location); -#endif // GTEST_HAS_EXCEPTIONS - } else { - return (object->*method)(); - } -} - -} // namespace internal - -// Runs the test and updates the test result. -void Test::Run() { - if (!HasSameFixtureClass()) return; - - internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); - impl->os_stack_trace_getter()->UponLeavingGTest(); - internal::HandleExceptionsInMethodIfSupported(this, &Test::SetUp, "SetUp()"); - // We will run the test only if SetUp() was successful. - if (!HasFatalFailure()) { - impl->os_stack_trace_getter()->UponLeavingGTest(); - internal::HandleExceptionsInMethodIfSupported( - this, &Test::TestBody, "the test body"); - } - - // However, we want to clean up as much as possible. Hence we will - // always call TearDown(), even if SetUp() or the test body has - // failed. - impl->os_stack_trace_getter()->UponLeavingGTest(); - internal::HandleExceptionsInMethodIfSupported( - this, &Test::TearDown, "TearDown()"); -} - -// Returns true iff the current test has a fatal failure. -bool Test::HasFatalFailure() { - return internal::GetUnitTestImpl()->current_test_result()->HasFatalFailure(); -} - -// Returns true iff the current test has a non-fatal failure. -bool Test::HasNonfatalFailure() { - return internal::GetUnitTestImpl()->current_test_result()-> - HasNonfatalFailure(); -} - -// class TestInfo - -// Constructs a TestInfo object. It assumes ownership of the test factory -// object. -// TODO(vladl@google.com): Make a_test_case_name and a_name const string&'s -// to signify they cannot be NULLs. -TestInfo::TestInfo(const char* a_test_case_name, - const char* a_name, - const char* a_type_param, - const char* a_value_param, - internal::TypeId fixture_class_id, - internal::TestFactoryBase* factory) - : test_case_name_(a_test_case_name), - name_(a_name), - type_param_(a_type_param ? new std::string(a_type_param) : NULL), - value_param_(a_value_param ? new std::string(a_value_param) : NULL), - fixture_class_id_(fixture_class_id), - should_run_(false), - is_disabled_(false), - matches_filter_(false), - factory_(factory), - result_() {} - -// Destructs a TestInfo object. -TestInfo::~TestInfo() { delete factory_; } - -namespace internal { - -// Creates a new TestInfo object and registers it with Google Test; -// returns the created object. -// -// Arguments: -// -// test_case_name: name of the test case -// name: name of the test -// type_param: the name of the test's type parameter, or NULL if -// this is not a typed or a type-parameterized test. -// value_param: text representation of the test's value parameter, -// or NULL if this is not a value-parameterized test. -// fixture_class_id: ID of the test fixture class -// set_up_tc: pointer to the function that sets up the test case -// tear_down_tc: pointer to the function that tears down the test case -// factory: pointer to the factory that creates a test object. -// The newly created TestInfo instance will assume -// ownership of the factory object. -TestInfo* MakeAndRegisterTestInfo( - const char* test_case_name, const char* name, - const char* type_param, - const char* value_param, - TypeId fixture_class_id, - SetUpTestCaseFunc set_up_tc, - TearDownTestCaseFunc tear_down_tc, - TestFactoryBase* factory) { - TestInfo* const test_info = - new TestInfo(test_case_name, name, type_param, value_param, - fixture_class_id, factory); - GetUnitTestImpl()->AddTestInfo(set_up_tc, tear_down_tc, test_info); - return test_info; -} - -#if GTEST_HAS_PARAM_TEST -void ReportInvalidTestCaseType(const char* test_case_name, - const char* file, int line) { - Message errors; - errors - << "Attempted redefinition of test case " << test_case_name << ".\n" - << "All tests in the same test case must use the same test fixture\n" - << "class. However, in test case " << test_case_name << ", you tried\n" - << "to define a test using a fixture class different from the one\n" - << "used earlier. This can happen if the two fixture classes are\n" - << "from different namespaces and have the same name. You should\n" - << "probably rename one of the classes to put the tests into different\n" - << "test cases."; - - fprintf(stderr, "%s %s", FormatFileLocation(file, line).c_str(), - errors.GetString().c_str()); -} -#endif // GTEST_HAS_PARAM_TEST - -} // namespace internal - -namespace { - -// A predicate that checks the test name of a TestInfo against a known -// value. -// -// This is used for implementation of the TestCase class only. We put -// it in the anonymous namespace to prevent polluting the outer -// namespace. -// -// TestNameIs is copyable. -class TestNameIs { - public: - // Constructor. - // - // TestNameIs has NO default constructor. - explicit TestNameIs(const char* name) - : name_(name) {} - - // Returns true iff the test name of test_info matches name_. - bool operator()(const TestInfo * test_info) const { - return test_info && internal::String(test_info->name()).Compare(name_) == 0; - } - - private: - internal::String name_; -}; - -} // namespace - -namespace internal { - -// This method expands all parameterized tests registered with macros TEST_P -// and INSTANTIATE_TEST_CASE_P into regular tests and registers those. -// This will be done just once during the program runtime. -void UnitTestImpl::RegisterParameterizedTests() { -#if GTEST_HAS_PARAM_TEST - if (!parameterized_tests_registered_) { - parameterized_test_registry_.RegisterTests(); - parameterized_tests_registered_ = true; - } -#endif -} - -} // namespace internal - -// Creates the test object, runs it, records its result, and then -// deletes it. -void TestInfo::Run() { - if (!should_run_) return; - - // Tells UnitTest where to store test result. - internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); - impl->set_current_test_info(this); - - TestEventListener* repeater = UnitTest::GetInstance()->listeners().repeater(); - - // Notifies the unit test event listeners that a test is about to start. - repeater->OnTestStart(*this); - - const TimeInMillis start = internal::GetTimeInMillis(); - - impl->os_stack_trace_getter()->UponLeavingGTest(); - - // Creates the test object. - Test* const test = internal::HandleExceptionsInMethodIfSupported( - factory_, &internal::TestFactoryBase::CreateTest, - "the test fixture's constructor"); - - // Runs the test only if the test object was created and its - // constructor didn't generate a fatal failure. - if ((test != NULL) && !Test::HasFatalFailure()) { - // This doesn't throw as all user code that can throw are wrapped into - // exception handling code. - test->Run(); - } - - // Deletes the test object. - impl->os_stack_trace_getter()->UponLeavingGTest(); - internal::HandleExceptionsInMethodIfSupported( - test, &Test::DeleteSelf_, "the test fixture's destructor"); - - result_.set_elapsed_time(internal::GetTimeInMillis() - start); - - // Notifies the unit test event listener that a test has just finished. - repeater->OnTestEnd(*this); - - // Tells UnitTest to stop associating assertion results to this - // test. - impl->set_current_test_info(NULL); -} - -// class TestCase - -// Gets the number of successful tests in this test case. -int TestCase::successful_test_count() const { - return CountIf(test_info_list_, TestPassed); -} - -// Gets the number of failed tests in this test case. -int TestCase::failed_test_count() const { - return CountIf(test_info_list_, TestFailed); -} - -int TestCase::disabled_test_count() const { - return CountIf(test_info_list_, TestDisabled); -} - -// Get the number of tests in this test case that should run. -int TestCase::test_to_run_count() const { - return CountIf(test_info_list_, ShouldRunTest); -} - -// Gets the number of all tests. -int TestCase::total_test_count() const { - return static_cast(test_info_list_.size()); -} - -// Creates a TestCase with the given name. -// -// Arguments: -// -// name: name of the test case -// a_type_param: the name of the test case's type parameter, or NULL if -// this is not a typed or a type-parameterized test case. -// set_up_tc: pointer to the function that sets up the test case -// tear_down_tc: pointer to the function that tears down the test case -TestCase::TestCase(const char* a_name, const char* a_type_param, - Test::SetUpTestCaseFunc set_up_tc, - Test::TearDownTestCaseFunc tear_down_tc) - : name_(a_name), - type_param_(a_type_param ? new std::string(a_type_param) : NULL), - set_up_tc_(set_up_tc), - tear_down_tc_(tear_down_tc), - should_run_(false), - elapsed_time_(0) { -} - -// Destructor of TestCase. -TestCase::~TestCase() { - // Deletes every Test in the collection. - ForEach(test_info_list_, internal::Delete); -} - -// Returns the i-th test among all the tests. i can range from 0 to -// total_test_count() - 1. If i is not in that range, returns NULL. -const TestInfo* TestCase::GetTestInfo(int i) const { - const int index = GetElementOr(test_indices_, i, -1); - return index < 0 ? NULL : test_info_list_[index]; -} - -// Returns the i-th test among all the tests. i can range from 0 to -// total_test_count() - 1. If i is not in that range, returns NULL. -TestInfo* TestCase::GetMutableTestInfo(int i) { - const int index = GetElementOr(test_indices_, i, -1); - return index < 0 ? NULL : test_info_list_[index]; -} - -// Adds a test to this test case. Will delete the test upon -// destruction of the TestCase object. -void TestCase::AddTestInfo(TestInfo * test_info) { - test_info_list_.push_back(test_info); - test_indices_.push_back(static_cast(test_indices_.size())); -} - -// Runs every test in this TestCase. -void TestCase::Run() { - if (!should_run_) return; - - internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); - impl->set_current_test_case(this); - - TestEventListener* repeater = UnitTest::GetInstance()->listeners().repeater(); - - repeater->OnTestCaseStart(*this); - impl->os_stack_trace_getter()->UponLeavingGTest(); - internal::HandleExceptionsInMethodIfSupported( - this, &TestCase::RunSetUpTestCase, "SetUpTestCase()"); - - const internal::TimeInMillis start = internal::GetTimeInMillis(); - for (int i = 0; i < total_test_count(); i++) { - GetMutableTestInfo(i)->Run(); - } - elapsed_time_ = internal::GetTimeInMillis() - start; - - impl->os_stack_trace_getter()->UponLeavingGTest(); - internal::HandleExceptionsInMethodIfSupported( - this, &TestCase::RunTearDownTestCase, "TearDownTestCase()"); - - repeater->OnTestCaseEnd(*this); - impl->set_current_test_case(NULL); -} - -// Clears the results of all tests in this test case. -void TestCase::ClearResult() { - ForEach(test_info_list_, TestInfo::ClearTestResult); -} - -// Shuffles the tests in this test case. -void TestCase::ShuffleTests(internal::Random* random) { - Shuffle(random, &test_indices_); -} - -// Restores the test order to before the first shuffle. -void TestCase::UnshuffleTests() { - for (size_t i = 0; i < test_indices_.size(); i++) { - test_indices_[i] = static_cast(i); - } -} - -// Formats a countable noun. Depending on its quantity, either the -// singular form or the plural form is used. e.g. -// -// FormatCountableNoun(1, "formula", "formuli") returns "1 formula". -// FormatCountableNoun(5, "book", "books") returns "5 books". -static internal::String FormatCountableNoun(int count, - const char * singular_form, - const char * plural_form) { - return internal::String::Format("%d %s", count, - count == 1 ? singular_form : plural_form); -} - -// Formats the count of tests. -static internal::String FormatTestCount(int test_count) { - return FormatCountableNoun(test_count, "test", "tests"); -} - -// Formats the count of test cases. -static internal::String FormatTestCaseCount(int test_case_count) { - return FormatCountableNoun(test_case_count, "test case", "test cases"); -} - -// Converts a TestPartResult::Type enum to human-friendly string -// representation. Both kNonFatalFailure and kFatalFailure are translated -// to "Failure", as the user usually doesn't care about the difference -// between the two when viewing the test result. -static const char * TestPartResultTypeToString(TestPartResult::Type type) { - switch (type) { - case TestPartResult::kSuccess: - return "Success"; - - case TestPartResult::kNonFatalFailure: - case TestPartResult::kFatalFailure: -#ifdef _MSC_VER - return "error: "; -#else - return "Failure\n"; -#endif - default: - return "Unknown result type"; - } -} - -// Prints a TestPartResult to a String. -static internal::String PrintTestPartResultToString( - const TestPartResult& test_part_result) { - return (Message() - << internal::FormatFileLocation(test_part_result.file_name(), - test_part_result.line_number()) - << " " << TestPartResultTypeToString(test_part_result.type()) - << test_part_result.message()).GetString(); -} - -// Prints a TestPartResult. -static void PrintTestPartResult(const TestPartResult& test_part_result) { - const internal::String& result = - PrintTestPartResultToString(test_part_result); - printf("%s\n", result.c_str()); - fflush(stdout); - // If the test program runs in Visual Studio or a debugger, the - // following statements add the test part result message to the Output - // window such that the user can double-click on it to jump to the - // corresponding source code location; otherwise they do nothing. -#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE - // We don't call OutputDebugString*() on Windows Mobile, as printing - // to stdout is done by OutputDebugString() there already - we don't - // want the same message printed twice. - ::OutputDebugStringA(result.c_str()); - ::OutputDebugStringA("\n"); -#endif -} - -// class PrettyUnitTestResultPrinter - -namespace internal { - -enum GTestColor { - COLOR_DEFAULT, - COLOR_RED, - COLOR_GREEN, - COLOR_YELLOW -}; - -#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE - -// Returns the character attribute for the given color. -WORD GetColorAttribute(GTestColor color) { - switch (color) { - case COLOR_RED: return FOREGROUND_RED; - case COLOR_GREEN: return FOREGROUND_GREEN; - case COLOR_YELLOW: return FOREGROUND_RED | FOREGROUND_GREEN; - default: return 0; - } -} - -#else - -// Returns the ANSI color code for the given color. COLOR_DEFAULT is -// an invalid input. -const char* GetAnsiColorCode(GTestColor color) { - switch (color) { - case COLOR_RED: return "1"; - case COLOR_GREEN: return "2"; - case COLOR_YELLOW: return "3"; - default: return NULL; - }; -} - -#endif // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE - -// Returns true iff Google Test should use colors in the output. -bool ShouldUseColor(bool stdout_is_tty) { - const char* const gtest_color = GTEST_FLAG(color).c_str(); - - if (String::CaseInsensitiveCStringEquals(gtest_color, "auto")) { -#if GTEST_OS_WINDOWS - // On Windows the TERM variable is usually not set, but the - // console there does support colors. - return stdout_is_tty; -#else - // On non-Windows platforms, we rely on the TERM variable. - const char* const term = posix::GetEnv("TERM"); - const bool term_supports_color = - String::CStringEquals(term, "xterm") || - String::CStringEquals(term, "xterm-color") || - String::CStringEquals(term, "xterm-256color") || - String::CStringEquals(term, "screen") || - String::CStringEquals(term, "linux") || - String::CStringEquals(term, "cygwin"); - return stdout_is_tty && term_supports_color; -#endif // GTEST_OS_WINDOWS - } - - return String::CaseInsensitiveCStringEquals(gtest_color, "yes") || - String::CaseInsensitiveCStringEquals(gtest_color, "true") || - String::CaseInsensitiveCStringEquals(gtest_color, "t") || - String::CStringEquals(gtest_color, "1"); - // We take "yes", "true", "t", and "1" as meaning "yes". If the - // value is neither one of these nor "auto", we treat it as "no" to - // be conservative. -} - -// Helpers for printing colored strings to stdout. Note that on Windows, we -// cannot simply emit special characters and have the terminal change colors. -// This routine must actually emit the characters rather than return a string -// that would be colored when printed, as can be done on Linux. -void ColoredPrintf(GTestColor color, const char* fmt, ...) { - va_list args; - va_start(args, fmt); - -#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_SYMBIAN || GTEST_OS_ZOS - const bool use_color = false; -#else - static const bool in_color_mode = - ShouldUseColor(posix::IsATTY(posix::FileNo(stdout)) != 0); - const bool use_color = in_color_mode && (color != COLOR_DEFAULT); -#endif // GTEST_OS_WINDOWS_MOBILE || GTEST_OS_SYMBIAN || GTEST_OS_ZOS - // The '!= 0' comparison is necessary to satisfy MSVC 7.1. - - if (!use_color) { - vprintf(fmt, args); - va_end(args); - return; - } - -#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE - const HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); - - // Gets the current text color. - CONSOLE_SCREEN_BUFFER_INFO buffer_info; - GetConsoleScreenBufferInfo(stdout_handle, &buffer_info); - const WORD old_color_attrs = buffer_info.wAttributes; - - // We need to flush the stream buffers into the console before each - // SetConsoleTextAttribute call lest it affect the text that is already - // printed but has not yet reached the console. - fflush(stdout); - SetConsoleTextAttribute(stdout_handle, - GetColorAttribute(color) | FOREGROUND_INTENSITY); - vprintf(fmt, args); - - fflush(stdout); - // Restores the text color. - SetConsoleTextAttribute(stdout_handle, old_color_attrs); -#else - printf("\033[0;3%sm", GetAnsiColorCode(color)); - vprintf(fmt, args); - printf("\033[m"); // Resets the terminal to default. -#endif // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE - va_end(args); -} - -void PrintFullTestCommentIfPresent(const TestInfo& test_info) { - const char* const type_param = test_info.type_param(); - const char* const value_param = test_info.value_param(); - - if (type_param != NULL || value_param != NULL) { - printf(", where "); - if (type_param != NULL) { - printf("TypeParam = %s", type_param); - if (value_param != NULL) - printf(" and "); - } - if (value_param != NULL) { - printf("GetParam() = %s", value_param); - } - } -} - -// This class implements the TestEventListener interface. -// -// Class PrettyUnitTestResultPrinter is copyable. -class PrettyUnitTestResultPrinter : public TestEventListener { - public: - PrettyUnitTestResultPrinter() {} - static void PrintTestName(const char * test_case, const char * test) { - printf("%s.%s", test_case, test); - } - - // The following methods override what's in the TestEventListener class. - virtual void OnTestProgramStart(const UnitTest& /*unit_test*/) {} - virtual void OnTestIterationStart(const UnitTest& unit_test, int iteration); - virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test); - virtual void OnEnvironmentsSetUpEnd(const UnitTest& /*unit_test*/) {} - virtual void OnTestCaseStart(const TestCase& test_case); - virtual void OnTestStart(const TestInfo& test_info); - virtual void OnTestPartResult(const TestPartResult& result); - virtual void OnTestEnd(const TestInfo& test_info); - virtual void OnTestCaseEnd(const TestCase& test_case); - virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test); - virtual void OnEnvironmentsTearDownEnd(const UnitTest& /*unit_test*/) {} - virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration); - virtual void OnTestProgramEnd(const UnitTest& /*unit_test*/) {} - - private: - static void PrintFailedTests(const UnitTest& unit_test); - - internal::String test_case_name_; -}; - - // Fired before each iteration of tests starts. -void PrettyUnitTestResultPrinter::OnTestIterationStart( - const UnitTest& unit_test, int iteration) { - if (GTEST_FLAG(repeat) != 1) - printf("\nRepeating all tests (iteration %d) . . .\n\n", iteration + 1); - - const char* const filter = GTEST_FLAG(filter).c_str(); - - // Prints the filter if it's not *. This reminds the user that some - // tests may be skipped. - if (!internal::String::CStringEquals(filter, kUniversalFilter)) { - ColoredPrintf(COLOR_YELLOW, - "Note: %s filter = %s\n", GTEST_NAME_, filter); - } - - if (internal::ShouldShard(kTestTotalShards, kTestShardIndex, false)) { - const Int32 shard_index = Int32FromEnvOrDie(kTestShardIndex, -1); - ColoredPrintf(COLOR_YELLOW, - "Note: This is test shard %d of %s.\n", - static_cast(shard_index) + 1, - internal::posix::GetEnv(kTestTotalShards)); - } - - if (GTEST_FLAG(shuffle)) { - ColoredPrintf(COLOR_YELLOW, - "Note: Randomizing tests' orders with a seed of %d .\n", - unit_test.random_seed()); - } - - ColoredPrintf(COLOR_GREEN, "[==========] "); - printf("Running %s from %s.\n", - FormatTestCount(unit_test.test_to_run_count()).c_str(), - FormatTestCaseCount(unit_test.test_case_to_run_count()).c_str()); - fflush(stdout); -} - -void PrettyUnitTestResultPrinter::OnEnvironmentsSetUpStart( - const UnitTest& /*unit_test*/) { - ColoredPrintf(COLOR_GREEN, "[----------] "); - printf("Global test environment set-up.\n"); - fflush(stdout); -} - -void PrettyUnitTestResultPrinter::OnTestCaseStart(const TestCase& test_case) { - test_case_name_ = test_case.name(); - const internal::String counts = - FormatCountableNoun(test_case.test_to_run_count(), "test", "tests"); - ColoredPrintf(COLOR_GREEN, "[----------] "); - printf("%s from %s", counts.c_str(), test_case_name_.c_str()); - if (test_case.type_param() == NULL) { - printf("\n"); - } else { - printf(", where TypeParam = %s\n", test_case.type_param()); - } - fflush(stdout); -} - -void PrettyUnitTestResultPrinter::OnTestStart(const TestInfo& test_info) { - ColoredPrintf(COLOR_GREEN, "[ RUN ] "); - PrintTestName(test_case_name_.c_str(), test_info.name()); - printf("\n"); - fflush(stdout); -} - -// Called after an assertion failure. -void PrettyUnitTestResultPrinter::OnTestPartResult( - const TestPartResult& result) { - // If the test part succeeded, we don't need to do anything. - if (result.type() == TestPartResult::kSuccess) - return; - - // Print failure message from the assertion (e.g. expected this and got that). - PrintTestPartResult(result); - fflush(stdout); -} - -void PrettyUnitTestResultPrinter::OnTestEnd(const TestInfo& test_info) { - if (test_info.result()->Passed()) { - ColoredPrintf(COLOR_GREEN, "[ OK ] "); - } else { - ColoredPrintf(COLOR_RED, "[ FAILED ] "); - } - PrintTestName(test_case_name_.c_str(), test_info.name()); - if (test_info.result()->Failed()) - PrintFullTestCommentIfPresent(test_info); - - if (GTEST_FLAG(print_time)) { - printf(" (%s ms)\n", internal::StreamableToString( - test_info.result()->elapsed_time()).c_str()); - } else { - printf("\n"); - } - fflush(stdout); -} - -void PrettyUnitTestResultPrinter::OnTestCaseEnd(const TestCase& test_case) { - if (!GTEST_FLAG(print_time)) return; - - test_case_name_ = test_case.name(); - const internal::String counts = - FormatCountableNoun(test_case.test_to_run_count(), "test", "tests"); - ColoredPrintf(COLOR_GREEN, "[----------] "); - printf("%s from %s (%s ms total)\n\n", - counts.c_str(), test_case_name_.c_str(), - internal::StreamableToString(test_case.elapsed_time()).c_str()); - fflush(stdout); -} - -void PrettyUnitTestResultPrinter::OnEnvironmentsTearDownStart( - const UnitTest& /*unit_test*/) { - ColoredPrintf(COLOR_GREEN, "[----------] "); - printf("Global test environment tear-down\n"); - fflush(stdout); -} - -// Internal helper for printing the list of failed tests. -void PrettyUnitTestResultPrinter::PrintFailedTests(const UnitTest& unit_test) { - const int failed_test_count = unit_test.failed_test_count(); - if (failed_test_count == 0) { - return; - } - - for (int i = 0; i < unit_test.total_test_case_count(); ++i) { - const TestCase& test_case = *unit_test.GetTestCase(i); - if (!test_case.should_run() || (test_case.failed_test_count() == 0)) { - continue; - } - for (int j = 0; j < test_case.total_test_count(); ++j) { - const TestInfo& test_info = *test_case.GetTestInfo(j); - if (!test_info.should_run() || test_info.result()->Passed()) { - continue; - } - ColoredPrintf(COLOR_RED, "[ FAILED ] "); - printf("%s.%s", test_case.name(), test_info.name()); - PrintFullTestCommentIfPresent(test_info); - printf("\n"); - } - } -} - -void PrettyUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test, - int /*iteration*/) { - ColoredPrintf(COLOR_GREEN, "[==========] "); - printf("%s from %s ran.", - FormatTestCount(unit_test.test_to_run_count()).c_str(), - FormatTestCaseCount(unit_test.test_case_to_run_count()).c_str()); - if (GTEST_FLAG(print_time)) { - printf(" (%s ms total)", - internal::StreamableToString(unit_test.elapsed_time()).c_str()); - } - printf("\n"); - ColoredPrintf(COLOR_GREEN, "[ PASSED ] "); - printf("%s.\n", FormatTestCount(unit_test.successful_test_count()).c_str()); - - int num_failures = unit_test.failed_test_count(); - if (!unit_test.Passed()) { - const int failed_test_count = unit_test.failed_test_count(); - ColoredPrintf(COLOR_RED, "[ FAILED ] "); - printf("%s, listed below:\n", FormatTestCount(failed_test_count).c_str()); - PrintFailedTests(unit_test); - printf("\n%2d FAILED %s\n", num_failures, - num_failures == 1 ? "TEST" : "TESTS"); - } - - int num_disabled = unit_test.disabled_test_count(); - if (num_disabled && !GTEST_FLAG(also_run_disabled_tests)) { - if (!num_failures) { - printf("\n"); // Add a spacer if no FAILURE banner is displayed. - } - ColoredPrintf(COLOR_YELLOW, - " YOU HAVE %d DISABLED %s\n\n", - num_disabled, - num_disabled == 1 ? "TEST" : "TESTS"); - } - // Ensure that Google Test output is printed before, e.g., heapchecker output. - fflush(stdout); -} - -// End PrettyUnitTestResultPrinter - -// class TestEventRepeater -// -// This class forwards events to other event listeners. -class TestEventRepeater : public TestEventListener { - public: - TestEventRepeater() : forwarding_enabled_(true) {} - virtual ~TestEventRepeater(); - void Append(TestEventListener *listener); - TestEventListener* Release(TestEventListener* listener); - - // Controls whether events will be forwarded to listeners_. Set to false - // in death test child processes. - bool forwarding_enabled() const { return forwarding_enabled_; } - void set_forwarding_enabled(bool enable) { forwarding_enabled_ = enable; } - - virtual void OnTestProgramStart(const UnitTest& unit_test); - virtual void OnTestIterationStart(const UnitTest& unit_test, int iteration); - virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test); - virtual void OnEnvironmentsSetUpEnd(const UnitTest& unit_test); - virtual void OnTestCaseStart(const TestCase& test_case); - virtual void OnTestStart(const TestInfo& test_info); - virtual void OnTestPartResult(const TestPartResult& result); - virtual void OnTestEnd(const TestInfo& test_info); - virtual void OnTestCaseEnd(const TestCase& test_case); - virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test); - virtual void OnEnvironmentsTearDownEnd(const UnitTest& unit_test); - virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration); - virtual void OnTestProgramEnd(const UnitTest& unit_test); - - private: - // Controls whether events will be forwarded to listeners_. Set to false - // in death test child processes. - bool forwarding_enabled_; - // The list of listeners that receive events. - std::vector listeners_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(TestEventRepeater); -}; - -TestEventRepeater::~TestEventRepeater() { - ForEach(listeners_, Delete); -} - -void TestEventRepeater::Append(TestEventListener *listener) { - listeners_.push_back(listener); -} - -// TODO(vladl@google.com): Factor the search functionality into Vector::Find. -TestEventListener* TestEventRepeater::Release(TestEventListener *listener) { - for (size_t i = 0; i < listeners_.size(); ++i) { - if (listeners_[i] == listener) { - listeners_.erase(listeners_.begin() + i); - return listener; - } - } - - return NULL; -} - -// Since most methods are very similar, use macros to reduce boilerplate. -// This defines a member that forwards the call to all listeners. -#define GTEST_REPEATER_METHOD_(Name, Type) \ -void TestEventRepeater::Name(const Type& parameter) { \ - if (forwarding_enabled_) { \ - for (size_t i = 0; i < listeners_.size(); i++) { \ - listeners_[i]->Name(parameter); \ - } \ - } \ -} -// This defines a member that forwards the call to all listeners in reverse -// order. -#define GTEST_REVERSE_REPEATER_METHOD_(Name, Type) \ -void TestEventRepeater::Name(const Type& parameter) { \ - if (forwarding_enabled_) { \ - for (int i = static_cast(listeners_.size()) - 1; i >= 0; i--) { \ - listeners_[i]->Name(parameter); \ - } \ - } \ -} - -GTEST_REPEATER_METHOD_(OnTestProgramStart, UnitTest) -GTEST_REPEATER_METHOD_(OnEnvironmentsSetUpStart, UnitTest) -GTEST_REPEATER_METHOD_(OnTestCaseStart, TestCase) -GTEST_REPEATER_METHOD_(OnTestStart, TestInfo) -GTEST_REPEATER_METHOD_(OnTestPartResult, TestPartResult) -GTEST_REPEATER_METHOD_(OnEnvironmentsTearDownStart, UnitTest) -GTEST_REVERSE_REPEATER_METHOD_(OnEnvironmentsSetUpEnd, UnitTest) -GTEST_REVERSE_REPEATER_METHOD_(OnEnvironmentsTearDownEnd, UnitTest) -GTEST_REVERSE_REPEATER_METHOD_(OnTestEnd, TestInfo) -GTEST_REVERSE_REPEATER_METHOD_(OnTestCaseEnd, TestCase) -GTEST_REVERSE_REPEATER_METHOD_(OnTestProgramEnd, UnitTest) - -#undef GTEST_REPEATER_METHOD_ -#undef GTEST_REVERSE_REPEATER_METHOD_ - -void TestEventRepeater::OnTestIterationStart(const UnitTest& unit_test, - int iteration) { - if (forwarding_enabled_) { - for (size_t i = 0; i < listeners_.size(); i++) { - listeners_[i]->OnTestIterationStart(unit_test, iteration); - } - } -} - -void TestEventRepeater::OnTestIterationEnd(const UnitTest& unit_test, - int iteration) { - if (forwarding_enabled_) { - for (int i = static_cast(listeners_.size()) - 1; i >= 0; i--) { - listeners_[i]->OnTestIterationEnd(unit_test, iteration); - } - } -} - -// End TestEventRepeater - -// This class generates an XML output file. -class XmlUnitTestResultPrinter : public EmptyTestEventListener { - public: - explicit XmlUnitTestResultPrinter(const char* output_file); - - virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration); - - private: - // Is c a whitespace character that is normalized to a space character - // when it appears in an XML attribute value? - static bool IsNormalizableWhitespace(char c) { - return c == 0x9 || c == 0xA || c == 0xD; - } - - // May c appear in a well-formed XML document? - static bool IsValidXmlCharacter(char c) { - return IsNormalizableWhitespace(c) || c >= 0x20; - } - - // Returns an XML-escaped copy of the input string str. If - // is_attribute is true, the text is meant to appear as an attribute - // value, and normalizable whitespace is preserved by replacing it - // with character references. - static String EscapeXml(const char* str, bool is_attribute); - - // Returns the given string with all characters invalid in XML removed. - static string RemoveInvalidXmlCharacters(const string& str); - - // Convenience wrapper around EscapeXml when str is an attribute value. - static String EscapeXmlAttribute(const char* str) { - return EscapeXml(str, true); - } - - // Convenience wrapper around EscapeXml when str is not an attribute value. - static String EscapeXmlText(const char* str) { return EscapeXml(str, false); } - - // Streams an XML CDATA section, escaping invalid CDATA sequences as needed. - static void OutputXmlCDataSection(::std::ostream* stream, const char* data); - - // Streams an XML representation of a TestInfo object. - static void OutputXmlTestInfo(::std::ostream* stream, - const char* test_case_name, - const TestInfo& test_info); - - // Prints an XML representation of a TestCase object - static void PrintXmlTestCase(FILE* out, const TestCase& test_case); - - // Prints an XML summary of unit_test to output stream out. - static void PrintXmlUnitTest(FILE* out, const UnitTest& unit_test); - - // Produces a string representing the test properties in a result as space - // delimited XML attributes based on the property key="value" pairs. - // When the String is not empty, it includes a space at the beginning, - // to delimit this attribute from prior attributes. - static String TestPropertiesAsXmlAttributes(const TestResult& result); - - // The output file. - const String output_file_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(XmlUnitTestResultPrinter); -}; - -// Creates a new XmlUnitTestResultPrinter. -XmlUnitTestResultPrinter::XmlUnitTestResultPrinter(const char* output_file) - : output_file_(output_file) { - if (output_file_.c_str() == NULL || output_file_.empty()) { - fprintf(stderr, "XML output file may not be null\n"); - fflush(stderr); - exit(EXIT_FAILURE); - } -} - -// Called after the unit test ends. -void XmlUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test, - int /*iteration*/) { - FILE* xmlout = NULL; - FilePath output_file(output_file_); - FilePath output_dir(output_file.RemoveFileName()); - - if (output_dir.CreateDirectoriesRecursively()) { - xmlout = posix::FOpen(output_file_.c_str(), "w"); - } - if (xmlout == NULL) { - // TODO(wan): report the reason of the failure. - // - // We don't do it for now as: - // - // 1. There is no urgent need for it. - // 2. It's a bit involved to make the errno variable thread-safe on - // all three operating systems (Linux, Windows, and Mac OS). - // 3. To interpret the meaning of errno in a thread-safe way, - // we need the strerror_r() function, which is not available on - // Windows. - fprintf(stderr, - "Unable to open file \"%s\"\n", - output_file_.c_str()); - fflush(stderr); - exit(EXIT_FAILURE); - } - PrintXmlUnitTest(xmlout, unit_test); - fclose(xmlout); -} - -// Returns an XML-escaped copy of the input string str. If is_attribute -// is true, the text is meant to appear as an attribute value, and -// normalizable whitespace is preserved by replacing it with character -// references. -// -// Invalid XML characters in str, if any, are stripped from the output. -// It is expected that most, if not all, of the text processed by this -// module will consist of ordinary English text. -// If this module is ever modified to produce version 1.1 XML output, -// most invalid characters can be retained using character references. -// TODO(wan): It might be nice to have a minimally invasive, human-readable -// escaping scheme for invalid characters, rather than dropping them. -String XmlUnitTestResultPrinter::EscapeXml(const char* str, bool is_attribute) { - Message m; - - if (str != NULL) { - for (const char* src = str; *src; ++src) { - switch (*src) { - case '<': - m << "<"; - break; - case '>': - m << ">"; - break; - case '&': - m << "&"; - break; - case '\'': - if (is_attribute) - m << "'"; - else - m << '\''; - break; - case '"': - if (is_attribute) - m << """; - else - m << '"'; - break; - default: - if (IsValidXmlCharacter(*src)) { - if (is_attribute && IsNormalizableWhitespace(*src)) - m << String::Format("&#x%02X;", unsigned(*src)); - else - m << *src; - } - break; - } - } - } - - return m.GetString(); -} - -// Returns the given string with all characters invalid in XML removed. -// Currently invalid characters are dropped from the string. An -// alternative is to replace them with certain characters such as . or ?. -string XmlUnitTestResultPrinter::RemoveInvalidXmlCharacters(const string& str) { - string output; - output.reserve(str.size()); - for (string::const_iterator it = str.begin(); it != str.end(); ++it) - if (IsValidXmlCharacter(*it)) - output.push_back(*it); - - return output; -} - -// The following routines generate an XML representation of a UnitTest -// object. -// -// This is how Google Test concepts map to the DTD: -// -// <-- corresponds to a UnitTest object -// <-- corresponds to a TestCase object -// <-- corresponds to a TestInfo object -// ... -// ... -// ... -// <-- individual assertion failures -// -// -// - -// Formats the given time in milliseconds as seconds. -std::string FormatTimeInMillisAsSeconds(TimeInMillis ms) { - ::std::stringstream ss; - ss << ms/1000.0; - return ss.str(); -} - -// Streams an XML CDATA section, escaping invalid CDATA sequences as needed. -void XmlUnitTestResultPrinter::OutputXmlCDataSection(::std::ostream* stream, - const char* data) { - const char* segment = data; - *stream << ""); - if (next_segment != NULL) { - stream->write( - segment, static_cast(next_segment - segment)); - *stream << "]]>]]>"); - } else { - *stream << segment; - break; - } - } - *stream << "]]>"; -} - -// Prints an XML representation of a TestInfo object. -// TODO(wan): There is also value in printing properties with the plain printer. -void XmlUnitTestResultPrinter::OutputXmlTestInfo(::std::ostream* stream, - const char* test_case_name, - const TestInfo& test_info) { - const TestResult& result = *test_info.result(); - *stream << " \n"; - *stream << " "; - const string location = internal::FormatCompilerIndependentFileLocation( - part.file_name(), part.line_number()); - const string message = location + "\n" + part.message(); - OutputXmlCDataSection(stream, - RemoveInvalidXmlCharacters(message).c_str()); - *stream << "\n"; - } - } - - if (failures == 0) - *stream << " />\n"; - else - *stream << " \n"; -} - -// Prints an XML representation of a TestCase object -void XmlUnitTestResultPrinter::PrintXmlTestCase(FILE* out, - const TestCase& test_case) { - fprintf(out, - " \n", - FormatTimeInMillisAsSeconds(test_case.elapsed_time()).c_str()); - for (int i = 0; i < test_case.total_test_count(); ++i) { - ::std::stringstream stream; - OutputXmlTestInfo(&stream, test_case.name(), *test_case.GetTestInfo(i)); - fprintf(out, "%s", StringStreamToString(&stream).c_str()); - } - fprintf(out, " \n"); -} - -// Prints an XML summary of unit_test to output stream out. -void XmlUnitTestResultPrinter::PrintXmlUnitTest(FILE* out, - const UnitTest& unit_test) { - fprintf(out, "\n"); - fprintf(out, - "\n"); - for (int i = 0; i < unit_test.total_test_case_count(); ++i) - PrintXmlTestCase(out, *unit_test.GetTestCase(i)); - fprintf(out, "\n"); -} - -// Produces a string representing the test properties in a result as space -// delimited XML attributes based on the property key="value" pairs. -String XmlUnitTestResultPrinter::TestPropertiesAsXmlAttributes( - const TestResult& result) { - Message attributes; - for (int i = 0; i < result.test_property_count(); ++i) { - const TestProperty& property = result.GetTestProperty(i); - attributes << " " << property.key() << "=" - << "\"" << EscapeXmlAttribute(property.value()) << "\""; - } - return attributes.GetString(); -} - -// End XmlUnitTestResultPrinter - -#if GTEST_CAN_STREAM_RESULTS_ - -// Streams test results to the given port on the given host machine. -class StreamingListener : public EmptyTestEventListener { - public: - // Escapes '=', '&', '%', and '\n' characters in str as "%xx". - static string UrlEncode(const char* str); - - StreamingListener(const string& host, const string& port) - : sockfd_(-1), host_name_(host), port_num_(port) { - MakeConnection(); - Send("gtest_streaming_protocol_version=1.0\n"); - } - - virtual ~StreamingListener() { - if (sockfd_ != -1) - CloseConnection(); - } - - void OnTestProgramStart(const UnitTest& /* unit_test */) { - Send("event=TestProgramStart\n"); - } - - void OnTestProgramEnd(const UnitTest& unit_test) { - // Note that Google Test current only report elapsed time for each - // test iteration, not for the entire test program. - Send(String::Format("event=TestProgramEnd&passed=%d\n", - unit_test.Passed())); - - // Notify the streaming server to stop. - CloseConnection(); - } - - void OnTestIterationStart(const UnitTest& /* unit_test */, int iteration) { - Send(String::Format("event=TestIterationStart&iteration=%d\n", - iteration)); - } - - void OnTestIterationEnd(const UnitTest& unit_test, int /* iteration */) { - Send(String::Format("event=TestIterationEnd&passed=%d&elapsed_time=%sms\n", - unit_test.Passed(), - StreamableToString(unit_test.elapsed_time()).c_str())); - } - - void OnTestCaseStart(const TestCase& test_case) { - Send(String::Format("event=TestCaseStart&name=%s\n", test_case.name())); - } - - void OnTestCaseEnd(const TestCase& test_case) { - Send(String::Format("event=TestCaseEnd&passed=%d&elapsed_time=%sms\n", - test_case.Passed(), - StreamableToString(test_case.elapsed_time()).c_str())); - } - - void OnTestStart(const TestInfo& test_info) { - Send(String::Format("event=TestStart&name=%s\n", test_info.name())); - } - - void OnTestEnd(const TestInfo& test_info) { - Send(String::Format( - "event=TestEnd&passed=%d&elapsed_time=%sms\n", - (test_info.result())->Passed(), - StreamableToString((test_info.result())->elapsed_time()).c_str())); - } - - void OnTestPartResult(const TestPartResult& test_part_result) { - const char* file_name = test_part_result.file_name(); - if (file_name == NULL) - file_name = ""; - Send(String::Format("event=TestPartResult&file=%s&line=%d&message=", - UrlEncode(file_name).c_str(), - test_part_result.line_number())); - Send(UrlEncode(test_part_result.message()) + "\n"); - } - - private: - // Creates a client socket and connects to the server. - void MakeConnection(); - - // Closes the socket. - void CloseConnection() { - GTEST_CHECK_(sockfd_ != -1) - << "CloseConnection() can be called only when there is a connection."; - - close(sockfd_); - sockfd_ = -1; - } - - // Sends a string to the socket. - void Send(const string& message) { - GTEST_CHECK_(sockfd_ != -1) - << "Send() can be called only when there is a connection."; - - const int len = static_cast(message.length()); - if (write(sockfd_, message.c_str(), len) != len) { - GTEST_LOG_(WARNING) - << "stream_result_to: failed to stream to " - << host_name_ << ":" << port_num_; - } - } - - int sockfd_; // socket file descriptor - const string host_name_; - const string port_num_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(StreamingListener); -}; // class StreamingListener - -// Checks if str contains '=', '&', '%' or '\n' characters. If yes, -// replaces them by "%xx" where xx is their hexadecimal value. For -// example, replaces "=" with "%3D". This algorithm is O(strlen(str)) -// in both time and space -- important as the input str may contain an -// arbitrarily long test failure message and stack trace. -string StreamingListener::UrlEncode(const char* str) { - string result; - result.reserve(strlen(str) + 1); - for (char ch = *str; ch != '\0'; ch = *++str) { - switch (ch) { - case '%': - case '=': - case '&': - case '\n': - result.append(String::Format("%%%02x", static_cast(ch))); - break; - default: - result.push_back(ch); - break; - } - } - return result; -} - -void StreamingListener::MakeConnection() { - GTEST_CHECK_(sockfd_ == -1) - << "MakeConnection() can't be called when there is already a connection."; - - addrinfo hints; - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; // To allow both IPv4 and IPv6 addresses. - hints.ai_socktype = SOCK_STREAM; - addrinfo* servinfo = NULL; - - // Use the getaddrinfo() to get a linked list of IP addresses for - // the given host name. - const int error_num = getaddrinfo( - host_name_.c_str(), port_num_.c_str(), &hints, &servinfo); - if (error_num != 0) { - GTEST_LOG_(WARNING) << "stream_result_to: getaddrinfo() failed: " - << gai_strerror(error_num); - } - - // Loop through all the results and connect to the first we can. - for (addrinfo* cur_addr = servinfo; sockfd_ == -1 && cur_addr != NULL; - cur_addr = cur_addr->ai_next) { - sockfd_ = socket( - cur_addr->ai_family, cur_addr->ai_socktype, cur_addr->ai_protocol); - if (sockfd_ != -1) { - // Connect the client socket to the server socket. - if (connect(sockfd_, cur_addr->ai_addr, cur_addr->ai_addrlen) == -1) { - close(sockfd_); - sockfd_ = -1; - } - } - } - - freeaddrinfo(servinfo); // all done with this structure - - if (sockfd_ == -1) { - GTEST_LOG_(WARNING) << "stream_result_to: failed to connect to " - << host_name_ << ":" << port_num_; - } -} - -// End of class Streaming Listener -#endif // GTEST_CAN_STREAM_RESULTS__ - -// Class ScopedTrace - -// Pushes the given source file location and message onto a per-thread -// trace stack maintained by Google Test. -// L < UnitTest::mutex_ -ScopedTrace::ScopedTrace(const char* file, int line, const Message& message) { - TraceInfo trace; - trace.file = file; - trace.line = line; - trace.message = message.GetString(); - - UnitTest::GetInstance()->PushGTestTrace(trace); -} - -// Pops the info pushed by the c'tor. -// L < UnitTest::mutex_ -ScopedTrace::~ScopedTrace() { - UnitTest::GetInstance()->PopGTestTrace(); -} - - -// class OsStackTraceGetter - -// Returns the current OS stack trace as a String. Parameters: -// -// max_depth - the maximum number of stack frames to be included -// in the trace. -// skip_count - the number of top frames to be skipped; doesn't count -// against max_depth. -// -// L < mutex_ -// We use "L < mutex_" to denote that the function may acquire mutex_. -String OsStackTraceGetter::CurrentStackTrace(int, int) { - return String(""); -} - -// L < mutex_ -void OsStackTraceGetter::UponLeavingGTest() { -} - -const char* const -OsStackTraceGetter::kElidedFramesMarker = - "... " GTEST_NAME_ " internal frames ..."; - -} // namespace internal - -// class TestEventListeners - -TestEventListeners::TestEventListeners() - : repeater_(new internal::TestEventRepeater()), - default_result_printer_(NULL), - default_xml_generator_(NULL) { -} - -TestEventListeners::~TestEventListeners() { delete repeater_; } - -// Returns the standard listener responsible for the default console -// output. Can be removed from the listeners list to shut down default -// console output. Note that removing this object from the listener list -// with Release transfers its ownership to the user. -void TestEventListeners::Append(TestEventListener* listener) { - repeater_->Append(listener); -} - -// Removes the given event listener from the list and returns it. It then -// becomes the caller's responsibility to delete the listener. Returns -// NULL if the listener is not found in the list. -TestEventListener* TestEventListeners::Release(TestEventListener* listener) { - if (listener == default_result_printer_) - default_result_printer_ = NULL; - else if (listener == default_xml_generator_) - default_xml_generator_ = NULL; - return repeater_->Release(listener); -} - -// Returns repeater that broadcasts the TestEventListener events to all -// subscribers. -TestEventListener* TestEventListeners::repeater() { return repeater_; } - -// Sets the default_result_printer attribute to the provided listener. -// The listener is also added to the listener list and previous -// default_result_printer is removed from it and deleted. The listener can -// also be NULL in which case it will not be added to the list. Does -// nothing if the previous and the current listener objects are the same. -void TestEventListeners::SetDefaultResultPrinter(TestEventListener* listener) { - if (default_result_printer_ != listener) { - // It is an error to pass this method a listener that is already in the - // list. - delete Release(default_result_printer_); - default_result_printer_ = listener; - if (listener != NULL) - Append(listener); - } -} - -// Sets the default_xml_generator attribute to the provided listener. The -// listener is also added to the listener list and previous -// default_xml_generator is removed from it and deleted. The listener can -// also be NULL in which case it will not be added to the list. Does -// nothing if the previous and the current listener objects are the same. -void TestEventListeners::SetDefaultXmlGenerator(TestEventListener* listener) { - if (default_xml_generator_ != listener) { - // It is an error to pass this method a listener that is already in the - // list. - delete Release(default_xml_generator_); - default_xml_generator_ = listener; - if (listener != NULL) - Append(listener); - } -} - -// Controls whether events will be forwarded by the repeater to the -// listeners in the list. -bool TestEventListeners::EventForwardingEnabled() const { - return repeater_->forwarding_enabled(); -} - -void TestEventListeners::SuppressEventForwarding() { - repeater_->set_forwarding_enabled(false); -} - -// class UnitTest - -// Gets the singleton UnitTest object. The first time this method is -// called, a UnitTest object is constructed and returned. Consecutive -// calls will return the same object. -// -// We don't protect this under mutex_ as a user is not supposed to -// call this before main() starts, from which point on the return -// value will never change. -UnitTest * UnitTest::GetInstance() { - // When compiled with MSVC 7.1 in optimized mode, destroying the - // UnitTest object upon exiting the program messes up the exit code, - // causing successful tests to appear failed. We have to use a - // different implementation in this case to bypass the compiler bug. - // This implementation makes the compiler happy, at the cost of - // leaking the UnitTest object. - - // CodeGear C++Builder insists on a public destructor for the - // default implementation. Use this implementation to keep good OO - // design with private destructor. - -#if (_MSC_VER == 1310 && !defined(_DEBUG)) || defined(__BORLANDC__) - static UnitTest* const instance = new UnitTest; - return instance; -#else - static UnitTest instance; - return &instance; -#endif // (_MSC_VER == 1310 && !defined(_DEBUG)) || defined(__BORLANDC__) -} - -// Gets the number of successful test cases. -int UnitTest::successful_test_case_count() const { - return impl()->successful_test_case_count(); -} - -// Gets the number of failed test cases. -int UnitTest::failed_test_case_count() const { - return impl()->failed_test_case_count(); -} - -// Gets the number of all test cases. -int UnitTest::total_test_case_count() const { - return impl()->total_test_case_count(); -} - -// Gets the number of all test cases that contain at least one test -// that should run. -int UnitTest::test_case_to_run_count() const { - return impl()->test_case_to_run_count(); -} - -// Gets the number of successful tests. -int UnitTest::successful_test_count() const { - return impl()->successful_test_count(); -} - -// Gets the number of failed tests. -int UnitTest::failed_test_count() const { return impl()->failed_test_count(); } - -// Gets the number of disabled tests. -int UnitTest::disabled_test_count() const { - return impl()->disabled_test_count(); -} - -// Gets the number of all tests. -int UnitTest::total_test_count() const { return impl()->total_test_count(); } - -// Gets the number of tests that should run. -int UnitTest::test_to_run_count() const { return impl()->test_to_run_count(); } - -// Gets the elapsed time, in milliseconds. -internal::TimeInMillis UnitTest::elapsed_time() const { - return impl()->elapsed_time(); -} - -// Returns true iff the unit test passed (i.e. all test cases passed). -bool UnitTest::Passed() const { return impl()->Passed(); } - -// Returns true iff the unit test failed (i.e. some test case failed -// or something outside of all tests failed). -bool UnitTest::Failed() const { return impl()->Failed(); } - -// Gets the i-th test case among all the test cases. i can range from 0 to -// total_test_case_count() - 1. If i is not in that range, returns NULL. -const TestCase* UnitTest::GetTestCase(int i) const { - return impl()->GetTestCase(i); -} - -// Gets the i-th test case among all the test cases. i can range from 0 to -// total_test_case_count() - 1. If i is not in that range, returns NULL. -TestCase* UnitTest::GetMutableTestCase(int i) { - return impl()->GetMutableTestCase(i); -} - -// Returns the list of event listeners that can be used to track events -// inside Google Test. -TestEventListeners& UnitTest::listeners() { - return *impl()->listeners(); -} - -// Registers and returns a global test environment. When a test -// program is run, all global test environments will be set-up in the -// order they were registered. After all tests in the program have -// finished, all global test environments will be torn-down in the -// *reverse* order they were registered. -// -// The UnitTest object takes ownership of the given environment. -// -// We don't protect this under mutex_, as we only support calling it -// from the main thread. -Environment* UnitTest::AddEnvironment(Environment* env) { - if (env == NULL) { - return NULL; - } - - impl_->environments().push_back(env); - return env; -} - -// Adds a TestPartResult to the current TestResult object. All Google Test -// assertion macros (e.g. ASSERT_TRUE, EXPECT_EQ, etc) eventually call -// this to report their results. The user code should use the -// assertion macros instead of calling this directly. -// L < mutex_ -void UnitTest::AddTestPartResult(TestPartResult::Type result_type, - const char* file_name, - int line_number, - const internal::String& message, - const internal::String& os_stack_trace) { - Message msg; - msg << message; - - internal::MutexLock lock(&mutex_); - if (impl_->gtest_trace_stack().size() > 0) { - msg << "\n" << GTEST_NAME_ << " trace:"; - - for (int i = static_cast(impl_->gtest_trace_stack().size()); - i > 0; --i) { - const internal::TraceInfo& trace = impl_->gtest_trace_stack()[i - 1]; - msg << "\n" << internal::FormatFileLocation(trace.file, trace.line) - << " " << trace.message; - } - } - - if (os_stack_trace.c_str() != NULL && !os_stack_trace.empty()) { - msg << internal::kStackTraceMarker << os_stack_trace; - } - - const TestPartResult result = - TestPartResult(result_type, file_name, line_number, - msg.GetString().c_str()); - impl_->GetTestPartResultReporterForCurrentThread()-> - ReportTestPartResult(result); - - if (result_type != TestPartResult::kSuccess) { - // gtest_break_on_failure takes precedence over - // gtest_throw_on_failure. This allows a user to set the latter - // in the code (perhaps in order to use Google Test assertions - // with another testing framework) and specify the former on the - // command line for debugging. - if (GTEST_FLAG(break_on_failure)) { -#if GTEST_OS_WINDOWS - // Using DebugBreak on Windows allows gtest to still break into a debugger - // when a failure happens and both the --gtest_break_on_failure and - // the --gtest_catch_exceptions flags are specified. - DebugBreak(); -#else - // Dereference NULL through a volatile pointer to prevent the compiler - // from removing. We use this rather than abort() or __builtin_trap() for - // portability: Symbian doesn't implement abort() well, and some debuggers - // don't correctly trap abort(). - *static_cast(NULL) = 1; -#endif // GTEST_OS_WINDOWS - } else if (GTEST_FLAG(throw_on_failure)) { -#if GTEST_HAS_EXCEPTIONS - throw GoogleTestFailureException(result); -#else - // We cannot call abort() as it generates a pop-up in debug mode - // that cannot be suppressed in VC 7.1 or below. - exit(1); -#endif - } - } -} - -// Creates and adds a property to the current TestResult. If a property matching -// the supplied value already exists, updates its value instead. -void UnitTest::RecordPropertyForCurrentTest(const char* key, - const char* value) { - const TestProperty test_property(key, value); - impl_->current_test_result()->RecordProperty(test_property); -} - -// Runs all tests in this UnitTest object and prints the result. -// Returns 0 if successful, or 1 otherwise. -// -// We don't protect this under mutex_, as we only support calling it -// from the main thread. -int UnitTest::Run() { - // Captures the value of GTEST_FLAG(catch_exceptions). This value will be - // used for the duration of the program. - impl()->set_catch_exceptions(GTEST_FLAG(catch_exceptions)); - -#if GTEST_HAS_SEH - const bool in_death_test_child_process = - internal::GTEST_FLAG(internal_run_death_test).length() > 0; - - // Either the user wants Google Test to catch exceptions thrown by the - // tests or this is executing in the context of death test child - // process. In either case the user does not want to see pop-up dialogs - // about crashes - they are expected. - if (impl()->catch_exceptions() || in_death_test_child_process) { - -# if !GTEST_OS_WINDOWS_MOBILE - // SetErrorMode doesn't exist on CE. - SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOALIGNMENTFAULTEXCEPT | - SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX); -# endif // !GTEST_OS_WINDOWS_MOBILE - -# if (defined(_MSC_VER) || GTEST_OS_WINDOWS_MINGW) && !GTEST_OS_WINDOWS_MOBILE - // Death test children can be terminated with _abort(). On Windows, - // _abort() can show a dialog with a warning message. This forces the - // abort message to go to stderr instead. - _set_error_mode(_OUT_TO_STDERR); -# endif - -# if _MSC_VER >= 1400 && !GTEST_OS_WINDOWS_MOBILE - // In the debug version, Visual Studio pops up a separate dialog - // offering a choice to debug the aborted program. We need to suppress - // this dialog or it will pop up for every EXPECT/ASSERT_DEATH statement - // executed. Google Test will notify the user of any unexpected - // failure via stderr. - // - // VC++ doesn't define _set_abort_behavior() prior to the version 8.0. - // Users of prior VC versions shall suffer the agony and pain of - // clicking through the countless debug dialogs. - // TODO(vladl@google.com): find a way to suppress the abort dialog() in the - // debug mode when compiled with VC 7.1 or lower. - if (!GTEST_FLAG(break_on_failure)) - _set_abort_behavior( - 0x0, // Clear the following flags: - _WRITE_ABORT_MSG | _CALL_REPORTFAULT); // pop-up window, core dump. -# endif - - } -#endif // GTEST_HAS_SEH - - return internal::HandleExceptionsInMethodIfSupported( - impl(), - &internal::UnitTestImpl::RunAllTests, - "auxiliary test code (environments or event listeners)") ? 0 : 1; -} - -// Returns the working directory when the first TEST() or TEST_F() was -// executed. -const char* UnitTest::original_working_dir() const { - return impl_->original_working_dir_.c_str(); -} - -// Returns the TestCase object for the test that's currently running, -// or NULL if no test is running. -// L < mutex_ -const TestCase* UnitTest::current_test_case() const { - internal::MutexLock lock(&mutex_); - return impl_->current_test_case(); -} - -// Returns the TestInfo object for the test that's currently running, -// or NULL if no test is running. -// L < mutex_ -const TestInfo* UnitTest::current_test_info() const { - internal::MutexLock lock(&mutex_); - return impl_->current_test_info(); -} - -// Returns the random seed used at the start of the current test run. -int UnitTest::random_seed() const { return impl_->random_seed(); } - -#if GTEST_HAS_PARAM_TEST -// Returns ParameterizedTestCaseRegistry object used to keep track of -// value-parameterized tests and instantiate and register them. -// L < mutex_ -internal::ParameterizedTestCaseRegistry& - UnitTest::parameterized_test_registry() { - return impl_->parameterized_test_registry(); -} -#endif // GTEST_HAS_PARAM_TEST - -// Creates an empty UnitTest. -UnitTest::UnitTest() { - impl_ = new internal::UnitTestImpl(this); -} - -// Destructor of UnitTest. -UnitTest::~UnitTest() { - delete impl_; -} - -// Pushes a trace defined by SCOPED_TRACE() on to the per-thread -// Google Test trace stack. -// L < mutex_ -void UnitTest::PushGTestTrace(const internal::TraceInfo& trace) { - internal::MutexLock lock(&mutex_); - impl_->gtest_trace_stack().push_back(trace); -} - -// Pops a trace from the per-thread Google Test trace stack. -// L < mutex_ -void UnitTest::PopGTestTrace() { - internal::MutexLock lock(&mutex_); - impl_->gtest_trace_stack().pop_back(); -} - -namespace internal { - -UnitTestImpl::UnitTestImpl(UnitTest* parent) - : parent_(parent), -#ifdef _MSC_VER -# pragma warning(push) // Saves the current warning state. -# pragma warning(disable:4355) // Temporarily disables warning 4355 - // (using this in initializer). - default_global_test_part_result_reporter_(this), - default_per_thread_test_part_result_reporter_(this), -# pragma warning(pop) // Restores the warning state again. -#else - default_global_test_part_result_reporter_(this), - default_per_thread_test_part_result_reporter_(this), -#endif // _MSC_VER - global_test_part_result_repoter_( - &default_global_test_part_result_reporter_), - per_thread_test_part_result_reporter_( - &default_per_thread_test_part_result_reporter_), -#if GTEST_HAS_PARAM_TEST - parameterized_test_registry_(), - parameterized_tests_registered_(false), -#endif // GTEST_HAS_PARAM_TEST - last_death_test_case_(-1), - current_test_case_(NULL), - current_test_info_(NULL), - ad_hoc_test_result_(), - os_stack_trace_getter_(NULL), - post_flag_parse_init_performed_(false), - random_seed_(0), // Will be overridden by the flag before first use. - random_(0), // Will be reseeded before first use. - elapsed_time_(0), -#if GTEST_HAS_DEATH_TEST - internal_run_death_test_flag_(NULL), - death_test_factory_(new DefaultDeathTestFactory), -#endif - // Will be overridden by the flag before first use. - catch_exceptions_(false) { - listeners()->SetDefaultResultPrinter(new PrettyUnitTestResultPrinter); -} - -UnitTestImpl::~UnitTestImpl() { - // Deletes every TestCase. - ForEach(test_cases_, internal::Delete); - - // Deletes every Environment. - ForEach(environments_, internal::Delete); - - delete os_stack_trace_getter_; -} - -#if GTEST_HAS_DEATH_TEST -// Disables event forwarding if the control is currently in a death test -// subprocess. Must not be called before InitGoogleTest. -void UnitTestImpl::SuppressTestEventsIfInSubprocess() { - if (internal_run_death_test_flag_.get() != NULL) - listeners()->SuppressEventForwarding(); -} -#endif // GTEST_HAS_DEATH_TEST - -// Initializes event listeners performing XML output as specified by -// UnitTestOptions. Must not be called before InitGoogleTest. -void UnitTestImpl::ConfigureXmlOutput() { - const String& output_format = UnitTestOptions::GetOutputFormat(); - if (output_format == "xml") { - listeners()->SetDefaultXmlGenerator(new XmlUnitTestResultPrinter( - UnitTestOptions::GetAbsolutePathToOutputFile().c_str())); - } else if (output_format != "") { - printf("WARNING: unrecognized output format \"%s\" ignored.\n", - output_format.c_str()); - fflush(stdout); - } -} - -#if GTEST_CAN_STREAM_RESULTS_ -// Initializes event listeners for streaming test results in String form. -// Must not be called before InitGoogleTest. -void UnitTestImpl::ConfigureStreamingOutput() { - const string& target = GTEST_FLAG(stream_result_to); - if (!target.empty()) { - const size_t pos = target.find(':'); - if (pos != string::npos) { - listeners()->Append(new StreamingListener(target.substr(0, pos), - target.substr(pos+1))); - } else { - printf("WARNING: unrecognized streaming target \"%s\" ignored.\n", - target.c_str()); - fflush(stdout); - } - } -} -#endif // GTEST_CAN_STREAM_RESULTS_ - -// Performs initialization dependent upon flag values obtained in -// ParseGoogleTestFlagsOnly. Is called from InitGoogleTest after the call to -// ParseGoogleTestFlagsOnly. In case a user neglects to call InitGoogleTest -// this function is also called from RunAllTests. Since this function can be -// called more than once, it has to be idempotent. -void UnitTestImpl::PostFlagParsingInit() { - // Ensures that this function does not execute more than once. - if (!post_flag_parse_init_performed_) { - post_flag_parse_init_performed_ = true; - -#if GTEST_HAS_DEATH_TEST - InitDeathTestSubprocessControlInfo(); - SuppressTestEventsIfInSubprocess(); -#endif // GTEST_HAS_DEATH_TEST - - // Registers parameterized tests. This makes parameterized tests - // available to the UnitTest reflection API without running - // RUN_ALL_TESTS. - RegisterParameterizedTests(); - - // Configures listeners for XML output. This makes it possible for users - // to shut down the default XML output before invoking RUN_ALL_TESTS. - ConfigureXmlOutput(); - -#if GTEST_CAN_STREAM_RESULTS_ - // Configures listeners for streaming test results to the specified server. - ConfigureStreamingOutput(); -#endif // GTEST_CAN_STREAM_RESULTS_ - } -} - -// A predicate that checks the name of a TestCase against a known -// value. -// -// This is used for implementation of the UnitTest class only. We put -// it in the anonymous namespace to prevent polluting the outer -// namespace. -// -// TestCaseNameIs is copyable. -class TestCaseNameIs { - public: - // Constructor. - explicit TestCaseNameIs(const String& name) - : name_(name) {} - - // Returns true iff the name of test_case matches name_. - bool operator()(const TestCase* test_case) const { - return test_case != NULL && strcmp(test_case->name(), name_.c_str()) == 0; - } - - private: - String name_; -}; - -// Finds and returns a TestCase with the given name. If one doesn't -// exist, creates one and returns it. It's the CALLER'S -// RESPONSIBILITY to ensure that this function is only called WHEN THE -// TESTS ARE NOT SHUFFLED. -// -// Arguments: -// -// test_case_name: name of the test case -// type_param: the name of the test case's type parameter, or NULL if -// this is not a typed or a type-parameterized test case. -// set_up_tc: pointer to the function that sets up the test case -// tear_down_tc: pointer to the function that tears down the test case -TestCase* UnitTestImpl::GetTestCase(const char* test_case_name, - const char* type_param, - Test::SetUpTestCaseFunc set_up_tc, - Test::TearDownTestCaseFunc tear_down_tc) { - // Can we find a TestCase with the given name? - const std::vector::const_iterator test_case = - std::find_if(test_cases_.begin(), test_cases_.end(), - TestCaseNameIs(test_case_name)); - - if (test_case != test_cases_.end()) - return *test_case; - - // No. Let's create one. - TestCase* const new_test_case = - new TestCase(test_case_name, type_param, set_up_tc, tear_down_tc); - - // Is this a death test case? - if (internal::UnitTestOptions::MatchesFilter(String(test_case_name), - kDeathTestCaseFilter)) { - // Yes. Inserts the test case after the last death test case - // defined so far. This only works when the test cases haven't - // been shuffled. Otherwise we may end up running a death test - // after a non-death test. - ++last_death_test_case_; - test_cases_.insert(test_cases_.begin() + last_death_test_case_, - new_test_case); - } else { - // No. Appends to the end of the list. - test_cases_.push_back(new_test_case); - } - - test_case_indices_.push_back(static_cast(test_case_indices_.size())); - return new_test_case; -} - -// Helpers for setting up / tearing down the given environment. They -// are for use in the ForEach() function. -static void SetUpEnvironment(Environment* env) { env->SetUp(); } -static void TearDownEnvironment(Environment* env) { env->TearDown(); } - -// Runs all tests in this UnitTest object, prints the result, and -// returns true if all tests are successful. If any exception is -// thrown during a test, the test is considered to be failed, but the -// rest of the tests will still be run. -// -// When parameterized tests are enabled, it expands and registers -// parameterized tests first in RegisterParameterizedTests(). -// All other functions called from RunAllTests() may safely assume that -// parameterized tests are ready to be counted and run. -bool UnitTestImpl::RunAllTests() { - // Makes sure InitGoogleTest() was called. - if (!GTestIsInitialized()) { - printf("%s", - "\nThis test program did NOT call ::testing::InitGoogleTest " - "before calling RUN_ALL_TESTS(). Please fix it.\n"); - return false; - } - - // Do not run any test if the --help flag was specified. - if (g_help_flag) - return true; - - // Repeats the call to the post-flag parsing initialization in case the - // user didn't call InitGoogleTest. - PostFlagParsingInit(); - - // Even if sharding is not on, test runners may want to use the - // GTEST_SHARD_STATUS_FILE to query whether the test supports the sharding - // protocol. - internal::WriteToShardStatusFileIfNeeded(); - - // True iff we are in a subprocess for running a thread-safe-style - // death test. - bool in_subprocess_for_death_test = false; - -#if GTEST_HAS_DEATH_TEST - in_subprocess_for_death_test = (internal_run_death_test_flag_.get() != NULL); -#endif // GTEST_HAS_DEATH_TEST - - const bool should_shard = ShouldShard(kTestTotalShards, kTestShardIndex, - in_subprocess_for_death_test); - - // Compares the full test names with the filter to decide which - // tests to run. - const bool has_tests_to_run = FilterTests(should_shard - ? HONOR_SHARDING_PROTOCOL - : IGNORE_SHARDING_PROTOCOL) > 0; - - // Lists the tests and exits if the --gtest_list_tests flag was specified. - if (GTEST_FLAG(list_tests)) { - // This must be called *after* FilterTests() has been called. - ListTestsMatchingFilter(); - return true; - } - - random_seed_ = GTEST_FLAG(shuffle) ? - GetRandomSeedFromFlag(GTEST_FLAG(random_seed)) : 0; - - // True iff at least one test has failed. - bool failed = false; - - TestEventListener* repeater = listeners()->repeater(); - - repeater->OnTestProgramStart(*parent_); - - // How many times to repeat the tests? We don't want to repeat them - // when we are inside the subprocess of a death test. - const int repeat = in_subprocess_for_death_test ? 1 : GTEST_FLAG(repeat); - // Repeats forever if the repeat count is negative. - const bool forever = repeat < 0; - for (int i = 0; forever || i != repeat; i++) { - // We want to preserve failures generated by ad-hoc test - // assertions executed before RUN_ALL_TESTS(). - ClearNonAdHocTestResult(); - - const TimeInMillis start = GetTimeInMillis(); - - // Shuffles test cases and tests if requested. - if (has_tests_to_run && GTEST_FLAG(shuffle)) { - random()->Reseed(random_seed_); - // This should be done before calling OnTestIterationStart(), - // such that a test event listener can see the actual test order - // in the event. - ShuffleTests(); - } - - // Tells the unit test event listeners that the tests are about to start. - repeater->OnTestIterationStart(*parent_, i); - - // Runs each test case if there is at least one test to run. - if (has_tests_to_run) { - // Sets up all environments beforehand. - repeater->OnEnvironmentsSetUpStart(*parent_); - ForEach(environments_, SetUpEnvironment); - repeater->OnEnvironmentsSetUpEnd(*parent_); - - // Runs the tests only if there was no fatal failure during global - // set-up. - if (!Test::HasFatalFailure()) { - for (int test_index = 0; test_index < total_test_case_count(); - test_index++) { - GetMutableTestCase(test_index)->Run(); - } - } - - // Tears down all environments in reverse order afterwards. - repeater->OnEnvironmentsTearDownStart(*parent_); - std::for_each(environments_.rbegin(), environments_.rend(), - TearDownEnvironment); - repeater->OnEnvironmentsTearDownEnd(*parent_); - } - - elapsed_time_ = GetTimeInMillis() - start; - - // Tells the unit test event listener that the tests have just finished. - repeater->OnTestIterationEnd(*parent_, i); - - // Gets the result and clears it. - if (!Passed()) { - failed = true; - } - - // Restores the original test order after the iteration. This - // allows the user to quickly repro a failure that happens in the - // N-th iteration without repeating the first (N - 1) iterations. - // This is not enclosed in "if (GTEST_FLAG(shuffle)) { ... }", in - // case the user somehow changes the value of the flag somewhere - // (it's always safe to unshuffle the tests). - UnshuffleTests(); - - if (GTEST_FLAG(shuffle)) { - // Picks a new random seed for each iteration. - random_seed_ = GetNextRandomSeed(random_seed_); - } - } - - repeater->OnTestProgramEnd(*parent_); - - return !failed; -} - -// Reads the GTEST_SHARD_STATUS_FILE environment variable, and creates the file -// if the variable is present. If a file already exists at this location, this -// function will write over it. If the variable is present, but the file cannot -// be created, prints an error and exits. -void WriteToShardStatusFileIfNeeded() { - const char* const test_shard_file = posix::GetEnv(kTestShardStatusFile); - if (test_shard_file != NULL) { - FILE* const file = posix::FOpen(test_shard_file, "w"); - if (file == NULL) { - ColoredPrintf(COLOR_RED, - "Could not write to the test shard status file \"%s\" " - "specified by the %s environment variable.\n", - test_shard_file, kTestShardStatusFile); - fflush(stdout); - exit(EXIT_FAILURE); - } - fclose(file); - } -} - -// Checks whether sharding is enabled by examining the relevant -// environment variable values. If the variables are present, -// but inconsistent (i.e., shard_index >= total_shards), prints -// an error and exits. If in_subprocess_for_death_test, sharding is -// disabled because it must only be applied to the original test -// process. Otherwise, we could filter out death tests we intended to execute. -bool ShouldShard(const char* total_shards_env, - const char* shard_index_env, - bool in_subprocess_for_death_test) { - if (in_subprocess_for_death_test) { - return false; - } - - const Int32 total_shards = Int32FromEnvOrDie(total_shards_env, -1); - const Int32 shard_index = Int32FromEnvOrDie(shard_index_env, -1); - - if (total_shards == -1 && shard_index == -1) { - return false; - } else if (total_shards == -1 && shard_index != -1) { - const Message msg = Message() - << "Invalid environment variables: you have " - << kTestShardIndex << " = " << shard_index - << ", but have left " << kTestTotalShards << " unset.\n"; - ColoredPrintf(COLOR_RED, msg.GetString().c_str()); - fflush(stdout); - exit(EXIT_FAILURE); - } else if (total_shards != -1 && shard_index == -1) { - const Message msg = Message() - << "Invalid environment variables: you have " - << kTestTotalShards << " = " << total_shards - << ", but have left " << kTestShardIndex << " unset.\n"; - ColoredPrintf(COLOR_RED, msg.GetString().c_str()); - fflush(stdout); - exit(EXIT_FAILURE); - } else if (shard_index < 0 || shard_index >= total_shards) { - const Message msg = Message() - << "Invalid environment variables: we require 0 <= " - << kTestShardIndex << " < " << kTestTotalShards - << ", but you have " << kTestShardIndex << "=" << shard_index - << ", " << kTestTotalShards << "=" << total_shards << ".\n"; - ColoredPrintf(COLOR_RED, msg.GetString().c_str()); - fflush(stdout); - exit(EXIT_FAILURE); - } - - return total_shards > 1; -} - -// Parses the environment variable var as an Int32. If it is unset, -// returns default_val. If it is not an Int32, prints an error -// and aborts. -Int32 Int32FromEnvOrDie(const char* var, Int32 default_val) { - const char* str_val = posix::GetEnv(var); - if (str_val == NULL) { - return default_val; - } - - Int32 result; - if (!ParseInt32(Message() << "The value of environment variable " << var, - str_val, &result)) { - exit(EXIT_FAILURE); - } - return result; -} - -// Given the total number of shards, the shard index, and the test id, -// returns true iff the test should be run on this shard. The test id is -// some arbitrary but unique non-negative integer assigned to each test -// method. Assumes that 0 <= shard_index < total_shards. -bool ShouldRunTestOnShard(int total_shards, int shard_index, int test_id) { - return (test_id % total_shards) == shard_index; -} - -// Compares the name of each test with the user-specified filter to -// decide whether the test should be run, then records the result in -// each TestCase and TestInfo object. -// If shard_tests == true, further filters tests based on sharding -// variables in the environment - see -// http://code.google.com/p/googletest/wiki/GoogleTestAdvancedGuide. -// Returns the number of tests that should run. -int UnitTestImpl::FilterTests(ReactionToSharding shard_tests) { - const Int32 total_shards = shard_tests == HONOR_SHARDING_PROTOCOL ? - Int32FromEnvOrDie(kTestTotalShards, -1) : -1; - const Int32 shard_index = shard_tests == HONOR_SHARDING_PROTOCOL ? - Int32FromEnvOrDie(kTestShardIndex, -1) : -1; - - // num_runnable_tests are the number of tests that will - // run across all shards (i.e., match filter and are not disabled). - // num_selected_tests are the number of tests to be run on - // this shard. - int num_runnable_tests = 0; - int num_selected_tests = 0; - for (size_t i = 0; i < test_cases_.size(); i++) { - TestCase* const test_case = test_cases_[i]; - const String &test_case_name = test_case->name(); - test_case->set_should_run(false); - - for (size_t j = 0; j < test_case->test_info_list().size(); j++) { - TestInfo* const test_info = test_case->test_info_list()[j]; - const String test_name(test_info->name()); - // A test is disabled if test case name or test name matches - // kDisableTestFilter. - const bool is_disabled = - internal::UnitTestOptions::MatchesFilter(test_case_name, - kDisableTestFilter) || - internal::UnitTestOptions::MatchesFilter(test_name, - kDisableTestFilter); - test_info->is_disabled_ = is_disabled; - - const bool matches_filter = - internal::UnitTestOptions::FilterMatchesTest(test_case_name, - test_name); - test_info->matches_filter_ = matches_filter; - - const bool is_runnable = - (GTEST_FLAG(also_run_disabled_tests) || !is_disabled) && - matches_filter; - - const bool is_selected = is_runnable && - (shard_tests == IGNORE_SHARDING_PROTOCOL || - ShouldRunTestOnShard(total_shards, shard_index, - num_runnable_tests)); - - num_runnable_tests += is_runnable; - num_selected_tests += is_selected; - - test_info->should_run_ = is_selected; - test_case->set_should_run(test_case->should_run() || is_selected); - } - } - return num_selected_tests; -} - -// Prints the names of the tests matching the user-specified filter flag. -void UnitTestImpl::ListTestsMatchingFilter() { - for (size_t i = 0; i < test_cases_.size(); i++) { - const TestCase* const test_case = test_cases_[i]; - bool printed_test_case_name = false; - - for (size_t j = 0; j < test_case->test_info_list().size(); j++) { - const TestInfo* const test_info = - test_case->test_info_list()[j]; - if (test_info->matches_filter_) { - if (!printed_test_case_name) { - printed_test_case_name = true; - printf("%s.\n", test_case->name()); - } - printf(" %s\n", test_info->name()); - } - } - } - fflush(stdout); -} - -// Sets the OS stack trace getter. -// -// Does nothing if the input and the current OS stack trace getter are -// the same; otherwise, deletes the old getter and makes the input the -// current getter. -void UnitTestImpl::set_os_stack_trace_getter( - OsStackTraceGetterInterface* getter) { - if (os_stack_trace_getter_ != getter) { - delete os_stack_trace_getter_; - os_stack_trace_getter_ = getter; - } -} - -// Returns the current OS stack trace getter if it is not NULL; -// otherwise, creates an OsStackTraceGetter, makes it the current -// getter, and returns it. -OsStackTraceGetterInterface* UnitTestImpl::os_stack_trace_getter() { - if (os_stack_trace_getter_ == NULL) { - os_stack_trace_getter_ = new OsStackTraceGetter; - } - - return os_stack_trace_getter_; -} - -// Returns the TestResult for the test that's currently running, or -// the TestResult for the ad hoc test if no test is running. -TestResult* UnitTestImpl::current_test_result() { - return current_test_info_ ? - &(current_test_info_->result_) : &ad_hoc_test_result_; -} - -// Shuffles all test cases, and the tests within each test case, -// making sure that death tests are still run first. -void UnitTestImpl::ShuffleTests() { - // Shuffles the death test cases. - ShuffleRange(random(), 0, last_death_test_case_ + 1, &test_case_indices_); - - // Shuffles the non-death test cases. - ShuffleRange(random(), last_death_test_case_ + 1, - static_cast(test_cases_.size()), &test_case_indices_); - - // Shuffles the tests inside each test case. - for (size_t i = 0; i < test_cases_.size(); i++) { - test_cases_[i]->ShuffleTests(random()); - } -} - -// Restores the test cases and tests to their order before the first shuffle. -void UnitTestImpl::UnshuffleTests() { - for (size_t i = 0; i < test_cases_.size(); i++) { - // Unshuffles the tests in each test case. - test_cases_[i]->UnshuffleTests(); - // Resets the index of each test case. - test_case_indices_[i] = static_cast(i); - } -} - -// Returns the current OS stack trace as a String. -// -// The maximum number of stack frames to be included is specified by -// the gtest_stack_trace_depth flag. The skip_count parameter -// specifies the number of top frames to be skipped, which doesn't -// count against the number of frames to be included. -// -// For example, if Foo() calls Bar(), which in turn calls -// GetCurrentOsStackTraceExceptTop(..., 1), Foo() will be included in -// the trace but Bar() and GetCurrentOsStackTraceExceptTop() won't. -String GetCurrentOsStackTraceExceptTop(UnitTest* /*unit_test*/, - int skip_count) { - // We pass skip_count + 1 to skip this wrapper function in addition - // to what the user really wants to skip. - return GetUnitTestImpl()->CurrentOsStackTraceExceptTop(skip_count + 1); -} - -// Used by the GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_ macro to -// suppress unreachable code warnings. -namespace { -class ClassUniqueToAlwaysTrue {}; -} - -bool IsTrue(bool condition) { return condition; } - -bool AlwaysTrue() { -#if GTEST_HAS_EXCEPTIONS - // This condition is always false so AlwaysTrue() never actually throws, - // but it makes the compiler think that it may throw. - if (IsTrue(false)) - throw ClassUniqueToAlwaysTrue(); -#endif // GTEST_HAS_EXCEPTIONS - return true; -} - -// If *pstr starts with the given prefix, modifies *pstr to be right -// past the prefix and returns true; otherwise leaves *pstr unchanged -// and returns false. None of pstr, *pstr, and prefix can be NULL. -bool SkipPrefix(const char* prefix, const char** pstr) { - const size_t prefix_len = strlen(prefix); - if (strncmp(*pstr, prefix, prefix_len) == 0) { - *pstr += prefix_len; - return true; - } - return false; -} - -// Parses a string as a command line flag. The string should have -// the format "--flag=value". When def_optional is true, the "=value" -// part can be omitted. -// -// Returns the value of the flag, or NULL if the parsing failed. -const char* ParseFlagValue(const char* str, - const char* flag, - bool def_optional) { - // str and flag must not be NULL. - if (str == NULL || flag == NULL) return NULL; - - // The flag must start with "--" followed by GTEST_FLAG_PREFIX_. - const String flag_str = String::Format("--%s%s", GTEST_FLAG_PREFIX_, flag); - const size_t flag_len = flag_str.length(); - if (strncmp(str, flag_str.c_str(), flag_len) != 0) return NULL; - - // Skips the flag name. - const char* flag_end = str + flag_len; - - // When def_optional is true, it's OK to not have a "=value" part. - if (def_optional && (flag_end[0] == '\0')) { - return flag_end; - } - - // If def_optional is true and there are more characters after the - // flag name, or if def_optional is false, there must be a '=' after - // the flag name. - if (flag_end[0] != '=') return NULL; - - // Returns the string after "=". - return flag_end + 1; -} - -// Parses a string for a bool flag, in the form of either -// "--flag=value" or "--flag". -// -// In the former case, the value is taken as true as long as it does -// not start with '0', 'f', or 'F'. -// -// In the latter case, the value is taken as true. -// -// On success, stores the value of the flag in *value, and returns -// true. On failure, returns false without changing *value. -bool ParseBoolFlag(const char* str, const char* flag, bool* value) { - // Gets the value of the flag as a string. - const char* const value_str = ParseFlagValue(str, flag, true); - - // Aborts if the parsing failed. - if (value_str == NULL) return false; - - // Converts the string value to a bool. - *value = !(*value_str == '0' || *value_str == 'f' || *value_str == 'F'); - return true; -} - -// Parses a string for an Int32 flag, in the form of -// "--flag=value". -// -// On success, stores the value of the flag in *value, and returns -// true. On failure, returns false without changing *value. -bool ParseInt32Flag(const char* str, const char* flag, Int32* value) { - // Gets the value of the flag as a string. - const char* const value_str = ParseFlagValue(str, flag, false); - - // Aborts if the parsing failed. - if (value_str == NULL) return false; - - // Sets *value to the value of the flag. - return ParseInt32(Message() << "The value of flag --" << flag, - value_str, value); -} - -// Parses a string for a string flag, in the form of -// "--flag=value". -// -// On success, stores the value of the flag in *value, and returns -// true. On failure, returns false without changing *value. -bool ParseStringFlag(const char* str, const char* flag, String* value) { - // Gets the value of the flag as a string. - const char* const value_str = ParseFlagValue(str, flag, false); - - // Aborts if the parsing failed. - if (value_str == NULL) return false; - - // Sets *value to the value of the flag. - *value = value_str; - return true; -} - -// Determines whether a string has a prefix that Google Test uses for its -// flags, i.e., starts with GTEST_FLAG_PREFIX_ or GTEST_FLAG_PREFIX_DASH_. -// If Google Test detects that a command line flag has its prefix but is not -// recognized, it will print its help message. Flags starting with -// GTEST_INTERNAL_PREFIX_ followed by "internal_" are considered Google Test -// internal flags and do not trigger the help message. -static bool HasGoogleTestFlagPrefix(const char* str) { - return (SkipPrefix("--", &str) || - SkipPrefix("-", &str) || - SkipPrefix("/", &str)) && - !SkipPrefix(GTEST_FLAG_PREFIX_ "internal_", &str) && - (SkipPrefix(GTEST_FLAG_PREFIX_, &str) || - SkipPrefix(GTEST_FLAG_PREFIX_DASH_, &str)); -} - -// Prints a string containing code-encoded text. The following escape -// sequences can be used in the string to control the text color: -// -// @@ prints a single '@' character. -// @R changes the color to red. -// @G changes the color to green. -// @Y changes the color to yellow. -// @D changes to the default terminal text color. -// -// TODO(wan@google.com): Write tests for this once we add stdout -// capturing to Google Test. -static void PrintColorEncoded(const char* str) { - GTestColor color = COLOR_DEFAULT; // The current color. - - // Conceptually, we split the string into segments divided by escape - // sequences. Then we print one segment at a time. At the end of - // each iteration, the str pointer advances to the beginning of the - // next segment. - for (;;) { - const char* p = strchr(str, '@'); - if (p == NULL) { - ColoredPrintf(color, "%s", str); - return; - } - - ColoredPrintf(color, "%s", String(str, p - str).c_str()); - - const char ch = p[1]; - str = p + 2; - if (ch == '@') { - ColoredPrintf(color, "@"); - } else if (ch == 'D') { - color = COLOR_DEFAULT; - } else if (ch == 'R') { - color = COLOR_RED; - } else if (ch == 'G') { - color = COLOR_GREEN; - } else if (ch == 'Y') { - color = COLOR_YELLOW; - } else { - --str; - } - } -} - -static const char kColorEncodedHelpMessage[] = -"This program contains tests written using " GTEST_NAME_ ". You can use the\n" -"following command line flags to control its behavior:\n" -"\n" -"Test Selection:\n" -" @G--" GTEST_FLAG_PREFIX_ "list_tests@D\n" -" List the names of all tests instead of running them. The name of\n" -" TEST(Foo, Bar) is \"Foo.Bar\".\n" -" @G--" GTEST_FLAG_PREFIX_ "filter=@YPOSTIVE_PATTERNS" - "[@G-@YNEGATIVE_PATTERNS]@D\n" -" Run only the tests whose name matches one of the positive patterns but\n" -" none of the negative patterns. '?' matches any single character; '*'\n" -" matches any substring; ':' separates two patterns.\n" -" @G--" GTEST_FLAG_PREFIX_ "also_run_disabled_tests@D\n" -" Run all disabled tests too.\n" -"\n" -"Test Execution:\n" -" @G--" GTEST_FLAG_PREFIX_ "repeat=@Y[COUNT]@D\n" -" Run the tests repeatedly; use a negative count to repeat forever.\n" -" @G--" GTEST_FLAG_PREFIX_ "shuffle@D\n" -" Randomize tests' orders on every iteration.\n" -" @G--" GTEST_FLAG_PREFIX_ "random_seed=@Y[NUMBER]@D\n" -" Random number seed to use for shuffling test orders (between 1 and\n" -" 99999, or 0 to use a seed based on the current time).\n" -"\n" -"Test Output:\n" -" @G--" GTEST_FLAG_PREFIX_ "color=@Y(@Gyes@Y|@Gno@Y|@Gauto@Y)@D\n" -" Enable/disable colored output. The default is @Gauto@D.\n" -" -@G-" GTEST_FLAG_PREFIX_ "print_time=0@D\n" -" Don't print the elapsed time of each test.\n" -" @G--" GTEST_FLAG_PREFIX_ "output=xml@Y[@G:@YDIRECTORY_PATH@G" - GTEST_PATH_SEP_ "@Y|@G:@YFILE_PATH]@D\n" -" Generate an XML report in the given directory or with the given file\n" -" name. @YFILE_PATH@D defaults to @Gtest_details.xml@D.\n" -#if GTEST_CAN_STREAM_RESULTS_ -" @G--" GTEST_FLAG_PREFIX_ "stream_result_to=@YHOST@G:@YPORT@D\n" -" Stream test results to the given server.\n" -#endif // GTEST_CAN_STREAM_RESULTS_ -"\n" -"Assertion Behavior:\n" -#if GTEST_HAS_DEATH_TEST && !GTEST_OS_WINDOWS -" @G--" GTEST_FLAG_PREFIX_ "death_test_style=@Y(@Gfast@Y|@Gthreadsafe@Y)@D\n" -" Set the default death test style.\n" -#endif // GTEST_HAS_DEATH_TEST && !GTEST_OS_WINDOWS -" @G--" GTEST_FLAG_PREFIX_ "break_on_failure@D\n" -" Turn assertion failures into debugger break-points.\n" -" @G--" GTEST_FLAG_PREFIX_ "throw_on_failure@D\n" -" Turn assertion failures into C++ exceptions.\n" -" @G--" GTEST_FLAG_PREFIX_ "catch_exceptions=0@D\n" -" Do not report exceptions as test failures. Instead, allow them\n" -" to crash the program or throw a pop-up (on Windows).\n" -"\n" -"Except for @G--" GTEST_FLAG_PREFIX_ "list_tests@D, you can alternatively set " - "the corresponding\n" -"environment variable of a flag (all letters in upper-case). For example, to\n" -"disable colored text output, you can either specify @G--" GTEST_FLAG_PREFIX_ - "color=no@D or set\n" -"the @G" GTEST_FLAG_PREFIX_UPPER_ "COLOR@D environment variable to @Gno@D.\n" -"\n" -"For more information, please read the " GTEST_NAME_ " documentation at\n" -"@G" GTEST_PROJECT_URL_ "@D. If you find a bug in " GTEST_NAME_ "\n" -"(not one in your own code or tests), please report it to\n" -"@G<" GTEST_DEV_EMAIL_ ">@D.\n"; - -// Parses the command line for Google Test flags, without initializing -// other parts of Google Test. The type parameter CharType can be -// instantiated to either char or wchar_t. -template -void ParseGoogleTestFlagsOnlyImpl(int* argc, CharType** argv) { - for (int i = 1; i < *argc; i++) { - const String arg_string = StreamableToString(argv[i]); - const char* const arg = arg_string.c_str(); - - using internal::ParseBoolFlag; - using internal::ParseInt32Flag; - using internal::ParseStringFlag; - - // Do we see a Google Test flag? - if (ParseBoolFlag(arg, kAlsoRunDisabledTestsFlag, - >EST_FLAG(also_run_disabled_tests)) || - ParseBoolFlag(arg, kBreakOnFailureFlag, - >EST_FLAG(break_on_failure)) || - ParseBoolFlag(arg, kCatchExceptionsFlag, - >EST_FLAG(catch_exceptions)) || - ParseStringFlag(arg, kColorFlag, >EST_FLAG(color)) || - ParseStringFlag(arg, kDeathTestStyleFlag, - >EST_FLAG(death_test_style)) || - ParseBoolFlag(arg, kDeathTestUseFork, - >EST_FLAG(death_test_use_fork)) || - ParseStringFlag(arg, kFilterFlag, >EST_FLAG(filter)) || - ParseStringFlag(arg, kInternalRunDeathTestFlag, - >EST_FLAG(internal_run_death_test)) || - ParseBoolFlag(arg, kListTestsFlag, >EST_FLAG(list_tests)) || - ParseStringFlag(arg, kOutputFlag, >EST_FLAG(output)) || - ParseBoolFlag(arg, kPrintTimeFlag, >EST_FLAG(print_time)) || - ParseInt32Flag(arg, kRandomSeedFlag, >EST_FLAG(random_seed)) || - ParseInt32Flag(arg, kRepeatFlag, >EST_FLAG(repeat)) || - ParseBoolFlag(arg, kShuffleFlag, >EST_FLAG(shuffle)) || - ParseInt32Flag(arg, kStackTraceDepthFlag, - >EST_FLAG(stack_trace_depth)) || - ParseStringFlag(arg, kStreamResultToFlag, - >EST_FLAG(stream_result_to)) || - ParseBoolFlag(arg, kThrowOnFailureFlag, - >EST_FLAG(throw_on_failure)) - ) { - // Yes. Shift the remainder of the argv list left by one. Note - // that argv has (*argc + 1) elements, the last one always being - // NULL. The following loop moves the trailing NULL element as - // well. - for (int j = i; j != *argc; j++) { - argv[j] = argv[j + 1]; - } - - // Decrements the argument count. - (*argc)--; - - // We also need to decrement the iterator as we just removed - // an element. - i--; - } else if (arg_string == "--help" || arg_string == "-h" || - arg_string == "-?" || arg_string == "/?" || - HasGoogleTestFlagPrefix(arg)) { - // Both help flag and unrecognized Google Test flags (excluding - // internal ones) trigger help display. - g_help_flag = true; - } - } - - if (g_help_flag) { - // We print the help here instead of in RUN_ALL_TESTS(), as the - // latter may not be called at all if the user is using Google - // Test with another testing framework. - PrintColorEncoded(kColorEncodedHelpMessage); - } -} - -// Parses the command line for Google Test flags, without initializing -// other parts of Google Test. -void ParseGoogleTestFlagsOnly(int* argc, char** argv) { - ParseGoogleTestFlagsOnlyImpl(argc, argv); -} -void ParseGoogleTestFlagsOnly(int* argc, wchar_t** argv) { - ParseGoogleTestFlagsOnlyImpl(argc, argv); -} - -// The internal implementation of InitGoogleTest(). -// -// The type parameter CharType can be instantiated to either char or -// wchar_t. -template -void InitGoogleTestImpl(int* argc, CharType** argv) { - g_init_gtest_count++; - - // We don't want to run the initialization code twice. - if (g_init_gtest_count != 1) return; - - if (*argc <= 0) return; - - internal::g_executable_path = internal::StreamableToString(argv[0]); - -#if GTEST_HAS_DEATH_TEST - - g_argvs.clear(); - for (int i = 0; i != *argc; i++) { - g_argvs.push_back(StreamableToString(argv[i])); - } - -#endif // GTEST_HAS_DEATH_TEST - - ParseGoogleTestFlagsOnly(argc, argv); - GetUnitTestImpl()->PostFlagParsingInit(); -} - -} // namespace internal - -// Initializes Google Test. This must be called before calling -// RUN_ALL_TESTS(). In particular, it parses a command line for the -// flags that Google Test recognizes. Whenever a Google Test flag is -// seen, it is removed from argv, and *argc is decremented. -// -// No value is returned. Instead, the Google Test flag variables are -// updated. -// -// Calling the function for the second time has no user-visible effect. -void InitGoogleTest(int* argc, char** argv) { - internal::InitGoogleTestImpl(argc, argv); -} - -// This overloaded version can be used in Windows programs compiled in -// UNICODE mode. -void InitGoogleTest(int* argc, wchar_t** argv) { - internal::InitGoogleTestImpl(argc, argv); -} - -} // namespace testing -// Copyright 2005, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Author: wan@google.com (Zhanyong Wan), vladl@google.com (Vlad Losev) -// -// This file implements death tests. - - -#if GTEST_HAS_DEATH_TEST - -# if GTEST_OS_MAC -# include -# endif // GTEST_OS_MAC - -# include -# include -# include -# include - -# if GTEST_OS_WINDOWS -# include -# else -# include -# include -# endif // GTEST_OS_WINDOWS - -#endif // GTEST_HAS_DEATH_TEST - - -// Indicates that this translation unit is part of Google Test's -// implementation. It must come before gtest-internal-inl.h is -// included, or there will be a compiler error. This trick is to -// prevent a user from accidentally including gtest-internal-inl.h in -// his code. -#define GTEST_IMPLEMENTATION_ 1 -#undef GTEST_IMPLEMENTATION_ - -namespace testing { - -// Constants. - -// The default death test style. -static const char kDefaultDeathTestStyle[] = "fast"; - -GTEST_DEFINE_string_( - death_test_style, - internal::StringFromGTestEnv("death_test_style", kDefaultDeathTestStyle), - "Indicates how to run a death test in a forked child process: " - "\"threadsafe\" (child process re-executes the test binary " - "from the beginning, running only the specific death test) or " - "\"fast\" (child process runs the death test immediately " - "after forking)."); - -GTEST_DEFINE_bool_( - death_test_use_fork, - internal::BoolFromGTestEnv("death_test_use_fork", false), - "Instructs to use fork()/_exit() instead of clone() in death tests. " - "Ignored and always uses fork() on POSIX systems where clone() is not " - "implemented. Useful when running under valgrind or similar tools if " - "those do not support clone(). Valgrind 3.3.1 will just fail if " - "it sees an unsupported combination of clone() flags. " - "It is not recommended to use this flag w/o valgrind though it will " - "work in 99% of the cases. Once valgrind is fixed, this flag will " - "most likely be removed."); - -namespace internal { -GTEST_DEFINE_string_( - internal_run_death_test, "", - "Indicates the file, line number, temporal index of " - "the single death test to run, and a file descriptor to " - "which a success code may be sent, all separated by " - "colons. This flag is specified if and only if the current " - "process is a sub-process launched for running a thread-safe " - "death test. FOR INTERNAL USE ONLY."); -} // namespace internal - -#if GTEST_HAS_DEATH_TEST - -// ExitedWithCode constructor. -ExitedWithCode::ExitedWithCode(int exit_code) : exit_code_(exit_code) { -} - -// ExitedWithCode function-call operator. -bool ExitedWithCode::operator()(int exit_status) const { -# if GTEST_OS_WINDOWS - - return exit_status == exit_code_; - -# else - - return WIFEXITED(exit_status) && WEXITSTATUS(exit_status) == exit_code_; - -# endif // GTEST_OS_WINDOWS -} - -# if !GTEST_OS_WINDOWS -// KilledBySignal constructor. -KilledBySignal::KilledBySignal(int signum) : signum_(signum) { -} - -// KilledBySignal function-call operator. -bool KilledBySignal::operator()(int exit_status) const { - return WIFSIGNALED(exit_status) && WTERMSIG(exit_status) == signum_; -} -# endif // !GTEST_OS_WINDOWS - -namespace internal { - -// Utilities needed for death tests. - -// Generates a textual description of a given exit code, in the format -// specified by wait(2). -static String ExitSummary(int exit_code) { - Message m; - -# if GTEST_OS_WINDOWS - - m << "Exited with exit status " << exit_code; - -# else - - if (WIFEXITED(exit_code)) { - m << "Exited with exit status " << WEXITSTATUS(exit_code); - } else if (WIFSIGNALED(exit_code)) { - m << "Terminated by signal " << WTERMSIG(exit_code); - } -# ifdef WCOREDUMP - if (WCOREDUMP(exit_code)) { - m << " (core dumped)"; - } -# endif -# endif // GTEST_OS_WINDOWS - - return m.GetString(); -} - -// Returns true if exit_status describes a process that was terminated -// by a signal, or exited normally with a nonzero exit code. -bool ExitedUnsuccessfully(int exit_status) { - return !ExitedWithCode(0)(exit_status); -} - -# if !GTEST_OS_WINDOWS -// Generates a textual failure message when a death test finds more than -// one thread running, or cannot determine the number of threads, prior -// to executing the given statement. It is the responsibility of the -// caller not to pass a thread_count of 1. -static String DeathTestThreadWarning(size_t thread_count) { - Message msg; - msg << "Death tests use fork(), which is unsafe particularly" - << " in a threaded context. For this test, " << GTEST_NAME_ << " "; - if (thread_count == 0) - msg << "couldn't detect the number of threads."; - else - msg << "detected " << thread_count << " threads."; - return msg.GetString(); -} -# endif // !GTEST_OS_WINDOWS - -// Flag characters for reporting a death test that did not die. -static const char kDeathTestLived = 'L'; -static const char kDeathTestReturned = 'R'; -static const char kDeathTestThrew = 'T'; -static const char kDeathTestInternalError = 'I'; - -// An enumeration describing all of the possible ways that a death test can -// conclude. DIED means that the process died while executing the test -// code; LIVED means that process lived beyond the end of the test code; -// RETURNED means that the test statement attempted to execute a return -// statement, which is not allowed; THREW means that the test statement -// returned control by throwing an exception. IN_PROGRESS means the test -// has not yet concluded. -// TODO(vladl@google.com): Unify names and possibly values for -// AbortReason, DeathTestOutcome, and flag characters above. -enum DeathTestOutcome { IN_PROGRESS, DIED, LIVED, RETURNED, THREW }; - -// Routine for aborting the program which is safe to call from an -// exec-style death test child process, in which case the error -// message is propagated back to the parent process. Otherwise, the -// message is simply printed to stderr. In either case, the program -// then exits with status 1. -void DeathTestAbort(const String& message) { - // On a POSIX system, this function may be called from a threadsafe-style - // death test child process, which operates on a very small stack. Use - // the heap for any additional non-minuscule memory requirements. - const InternalRunDeathTestFlag* const flag = - GetUnitTestImpl()->internal_run_death_test_flag(); - if (flag != NULL) { - FILE* parent = posix::FDOpen(flag->write_fd(), "w"); - fputc(kDeathTestInternalError, parent); - fprintf(parent, "%s", message.c_str()); - fflush(parent); - _exit(1); - } else { - fprintf(stderr, "%s", message.c_str()); - fflush(stderr); - posix::Abort(); - } -} - -// A replacement for CHECK that calls DeathTestAbort if the assertion -// fails. -# define GTEST_DEATH_TEST_CHECK_(expression) \ - do { \ - if (!::testing::internal::IsTrue(expression)) { \ - DeathTestAbort(::testing::internal::String::Format( \ - "CHECK failed: File %s, line %d: %s", \ - __FILE__, __LINE__, #expression)); \ - } \ - } while (::testing::internal::AlwaysFalse()) - -// This macro is similar to GTEST_DEATH_TEST_CHECK_, but it is meant for -// evaluating any system call that fulfills two conditions: it must return -// -1 on failure, and set errno to EINTR when it is interrupted and -// should be tried again. The macro expands to a loop that repeatedly -// evaluates the expression as long as it evaluates to -1 and sets -// errno to EINTR. If the expression evaluates to -1 but errno is -// something other than EINTR, DeathTestAbort is called. -# define GTEST_DEATH_TEST_CHECK_SYSCALL_(expression) \ - do { \ - int gtest_retval; \ - do { \ - gtest_retval = (expression); \ - } while (gtest_retval == -1 && errno == EINTR); \ - if (gtest_retval == -1) { \ - DeathTestAbort(::testing::internal::String::Format( \ - "CHECK failed: File %s, line %d: %s != -1", \ - __FILE__, __LINE__, #expression)); \ - } \ - } while (::testing::internal::AlwaysFalse()) - -// Returns the message describing the last system error in errno. -String GetLastErrnoDescription() { - return String(errno == 0 ? "" : posix::StrError(errno)); -} - -// This is called from a death test parent process to read a failure -// message from the death test child process and log it with the FATAL -// severity. On Windows, the message is read from a pipe handle. On other -// platforms, it is read from a file descriptor. -static void FailFromInternalError(int fd) { - Message error; - char buffer[256]; - int num_read; - - do { - while ((num_read = posix::Read(fd, buffer, 255)) > 0) { - buffer[num_read] = '\0'; - error << buffer; - } - } while (num_read == -1 && errno == EINTR); - - if (num_read == 0) { - GTEST_LOG_(FATAL) << error.GetString(); - } else { - const int last_error = errno; - GTEST_LOG_(FATAL) << "Error while reading death test internal: " - << GetLastErrnoDescription() << " [" << last_error << "]"; - } -} - -// Death test constructor. Increments the running death test count -// for the current test. -DeathTest::DeathTest() { - TestInfo* const info = GetUnitTestImpl()->current_test_info(); - if (info == NULL) { - DeathTestAbort("Cannot run a death test outside of a TEST or " - "TEST_F construct"); - } -} - -// Creates and returns a death test by dispatching to the current -// death test factory. -bool DeathTest::Create(const char* statement, const RE* regex, - const char* file, int line, DeathTest** test) { - return GetUnitTestImpl()->death_test_factory()->Create( - statement, regex, file, line, test); -} - -const char* DeathTest::LastMessage() { - return last_death_test_message_.c_str(); -} - -void DeathTest::set_last_death_test_message(const String& message) { - last_death_test_message_ = message; -} - -String DeathTest::last_death_test_message_; - -// Provides cross platform implementation for some death functionality. -class DeathTestImpl : public DeathTest { - protected: - DeathTestImpl(const char* a_statement, const RE* a_regex) - : statement_(a_statement), - regex_(a_regex), - spawned_(false), - status_(-1), - outcome_(IN_PROGRESS), - read_fd_(-1), - write_fd_(-1) {} - - // read_fd_ is expected to be closed and cleared by a derived class. - ~DeathTestImpl() { GTEST_DEATH_TEST_CHECK_(read_fd_ == -1); } - - void Abort(AbortReason reason); - virtual bool Passed(bool status_ok); - - const char* statement() const { return statement_; } - const RE* regex() const { return regex_; } - bool spawned() const { return spawned_; } - void set_spawned(bool is_spawned) { spawned_ = is_spawned; } - int status() const { return status_; } - void set_status(int a_status) { status_ = a_status; } - DeathTestOutcome outcome() const { return outcome_; } - void set_outcome(DeathTestOutcome an_outcome) { outcome_ = an_outcome; } - int read_fd() const { return read_fd_; } - void set_read_fd(int fd) { read_fd_ = fd; } - int write_fd() const { return write_fd_; } - void set_write_fd(int fd) { write_fd_ = fd; } - - // Called in the parent process only. Reads the result code of the death - // test child process via a pipe, interprets it to set the outcome_ - // member, and closes read_fd_. Outputs diagnostics and terminates in - // case of unexpected codes. - void ReadAndInterpretStatusByte(); - - private: - // The textual content of the code this object is testing. This class - // doesn't own this string and should not attempt to delete it. - const char* const statement_; - // The regular expression which test output must match. DeathTestImpl - // doesn't own this object and should not attempt to delete it. - const RE* const regex_; - // True if the death test child process has been successfully spawned. - bool spawned_; - // The exit status of the child process. - int status_; - // How the death test concluded. - DeathTestOutcome outcome_; - // Descriptor to the read end of the pipe to the child process. It is - // always -1 in the child process. The child keeps its write end of the - // pipe in write_fd_. - int read_fd_; - // Descriptor to the child's write end of the pipe to the parent process. - // It is always -1 in the parent process. The parent keeps its end of the - // pipe in read_fd_. - int write_fd_; -}; - -// Called in the parent process only. Reads the result code of the death -// test child process via a pipe, interprets it to set the outcome_ -// member, and closes read_fd_. Outputs diagnostics and terminates in -// case of unexpected codes. -void DeathTestImpl::ReadAndInterpretStatusByte() { - char flag; - int bytes_read; - - // The read() here blocks until data is available (signifying the - // failure of the death test) or until the pipe is closed (signifying - // its success), so it's okay to call this in the parent before - // the child process has exited. - do { - bytes_read = posix::Read(read_fd(), &flag, 1); - } while (bytes_read == -1 && errno == EINTR); - - if (bytes_read == 0) { - set_outcome(DIED); - } else if (bytes_read == 1) { - switch (flag) { - case kDeathTestReturned: - set_outcome(RETURNED); - break; - case kDeathTestThrew: - set_outcome(THREW); - break; - case kDeathTestLived: - set_outcome(LIVED); - break; - case kDeathTestInternalError: - FailFromInternalError(read_fd()); // Does not return. - break; - default: - GTEST_LOG_(FATAL) << "Death test child process reported " - << "unexpected status byte (" - << static_cast(flag) << ")"; - } - } else { - GTEST_LOG_(FATAL) << "Read from death test child process failed: " - << GetLastErrnoDescription(); - } - GTEST_DEATH_TEST_CHECK_SYSCALL_(posix::Close(read_fd())); - set_read_fd(-1); -} - -// Signals that the death test code which should have exited, didn't. -// Should be called only in a death test child process. -// Writes a status byte to the child's status file descriptor, then -// calls _exit(1). -void DeathTestImpl::Abort(AbortReason reason) { - // The parent process considers the death test to be a failure if - // it finds any data in our pipe. So, here we write a single flag byte - // to the pipe, then exit. - const char status_ch = - reason == TEST_DID_NOT_DIE ? kDeathTestLived : - reason == TEST_THREW_EXCEPTION ? kDeathTestThrew : kDeathTestReturned; - - GTEST_DEATH_TEST_CHECK_SYSCALL_(posix::Write(write_fd(), &status_ch, 1)); - // We are leaking the descriptor here because on some platforms (i.e., - // when built as Windows DLL), destructors of global objects will still - // run after calling _exit(). On such systems, write_fd_ will be - // indirectly closed from the destructor of UnitTestImpl, causing double - // close if it is also closed here. On debug configurations, double close - // may assert. As there are no in-process buffers to flush here, we are - // relying on the OS to close the descriptor after the process terminates - // when the destructors are not run. - _exit(1); // Exits w/o any normal exit hooks (we were supposed to crash) -} - -// Returns an indented copy of stderr output for a death test. -// This makes distinguishing death test output lines from regular log lines -// much easier. -static ::std::string FormatDeathTestOutput(const ::std::string& output) { - ::std::string ret; - for (size_t at = 0; ; ) { - const size_t line_end = output.find('\n', at); - ret += "[ DEATH ] "; - if (line_end == ::std::string::npos) { - ret += output.substr(at); - break; - } - ret += output.substr(at, line_end + 1 - at); - at = line_end + 1; - } - return ret; -} - -// Assesses the success or failure of a death test, using both private -// members which have previously been set, and one argument: -// -// Private data members: -// outcome: An enumeration describing how the death test -// concluded: DIED, LIVED, THREW, or RETURNED. The death test -// fails in the latter three cases. -// status: The exit status of the child process. On *nix, it is in the -// in the format specified by wait(2). On Windows, this is the -// value supplied to the ExitProcess() API or a numeric code -// of the exception that terminated the program. -// regex: A regular expression object to be applied to -// the test's captured standard error output; the death test -// fails if it does not match. -// -// Argument: -// status_ok: true if exit_status is acceptable in the context of -// this particular death test, which fails if it is false -// -// Returns true iff all of the above conditions are met. Otherwise, the -// first failing condition, in the order given above, is the one that is -// reported. Also sets the last death test message string. -bool DeathTestImpl::Passed(bool status_ok) { - if (!spawned()) - return false; - - const String error_message = GetCapturedStderr(); - - bool success = false; - Message buffer; - - buffer << "Death test: " << statement() << "\n"; - switch (outcome()) { - case LIVED: - buffer << " Result: failed to die.\n" - << " Error msg:\n" << FormatDeathTestOutput(error_message); - break; - case THREW: - buffer << " Result: threw an exception.\n" - << " Error msg:\n" << FormatDeathTestOutput(error_message); - break; - case RETURNED: - buffer << " Result: illegal return in test statement.\n" - << " Error msg:\n" << FormatDeathTestOutput(error_message); - break; - case DIED: - if (status_ok) { - const bool matched = RE::PartialMatch(error_message.c_str(), *regex()); - if (matched) { - success = true; - } else { - buffer << " Result: died but not with expected error.\n" - << " Expected: " << regex()->pattern() << "\n" - << "Actual msg:\n" << FormatDeathTestOutput(error_message); - } - } else { - buffer << " Result: died but not with expected exit code:\n" - << " " << ExitSummary(status()) << "\n" - << "Actual msg:\n" << FormatDeathTestOutput(error_message); - } - break; - case IN_PROGRESS: - default: - GTEST_LOG_(FATAL) - << "DeathTest::Passed somehow called before conclusion of test"; - } - - DeathTest::set_last_death_test_message(buffer.GetString()); - return success; -} - -# if GTEST_OS_WINDOWS -// WindowsDeathTest implements death tests on Windows. Due to the -// specifics of starting new processes on Windows, death tests there are -// always threadsafe, and Google Test considers the -// --gtest_death_test_style=fast setting to be equivalent to -// --gtest_death_test_style=threadsafe there. -// -// A few implementation notes: Like the Linux version, the Windows -// implementation uses pipes for child-to-parent communication. But due to -// the specifics of pipes on Windows, some extra steps are required: -// -// 1. The parent creates a communication pipe and stores handles to both -// ends of it. -// 2. The parent starts the child and provides it with the information -// necessary to acquire the handle to the write end of the pipe. -// 3. The child acquires the write end of the pipe and signals the parent -// using a Windows event. -// 4. Now the parent can release the write end of the pipe on its side. If -// this is done before step 3, the object's reference count goes down to -// 0 and it is destroyed, preventing the child from acquiring it. The -// parent now has to release it, or read operations on the read end of -// the pipe will not return when the child terminates. -// 5. The parent reads child's output through the pipe (outcome code and -// any possible error messages) from the pipe, and its stderr and then -// determines whether to fail the test. -// -// Note: to distinguish Win32 API calls from the local method and function -// calls, the former are explicitly resolved in the global namespace. -// -class WindowsDeathTest : public DeathTestImpl { - public: - WindowsDeathTest(const char* a_statement, - const RE* a_regex, - const char* file, - int line) - : DeathTestImpl(a_statement, a_regex), file_(file), line_(line) {} - - // All of these virtual functions are inherited from DeathTest. - virtual int Wait(); - virtual TestRole AssumeRole(); - - private: - // The name of the file in which the death test is located. - const char* const file_; - // The line number on which the death test is located. - const int line_; - // Handle to the write end of the pipe to the child process. - AutoHandle write_handle_; - // Child process handle. - AutoHandle child_handle_; - // Event the child process uses to signal the parent that it has - // acquired the handle to the write end of the pipe. After seeing this - // event the parent can release its own handles to make sure its - // ReadFile() calls return when the child terminates. - AutoHandle event_handle_; -}; - -// Waits for the child in a death test to exit, returning its exit -// status, or 0 if no child process exists. As a side effect, sets the -// outcome data member. -int WindowsDeathTest::Wait() { - if (!spawned()) - return 0; - - // Wait until the child either signals that it has acquired the write end - // of the pipe or it dies. - const HANDLE wait_handles[2] = { child_handle_.Get(), event_handle_.Get() }; - switch (::WaitForMultipleObjects(2, - wait_handles, - FALSE, // Waits for any of the handles. - INFINITE)) { - case WAIT_OBJECT_0: - case WAIT_OBJECT_0 + 1: - break; - default: - GTEST_DEATH_TEST_CHECK_(false); // Should not get here. - } - - // The child has acquired the write end of the pipe or exited. - // We release the handle on our side and continue. - write_handle_.Reset(); - event_handle_.Reset(); - - ReadAndInterpretStatusByte(); - - // Waits for the child process to exit if it haven't already. This - // returns immediately if the child has already exited, regardless of - // whether previous calls to WaitForMultipleObjects synchronized on this - // handle or not. - GTEST_DEATH_TEST_CHECK_( - WAIT_OBJECT_0 == ::WaitForSingleObject(child_handle_.Get(), - INFINITE)); - DWORD status_code; - GTEST_DEATH_TEST_CHECK_( - ::GetExitCodeProcess(child_handle_.Get(), &status_code) != FALSE); - child_handle_.Reset(); - set_status(static_cast(status_code)); - return status(); -} - -// The AssumeRole process for a Windows death test. It creates a child -// process with the same executable as the current process to run the -// death test. The child process is given the --gtest_filter and -// --gtest_internal_run_death_test flags such that it knows to run the -// current death test only. -DeathTest::TestRole WindowsDeathTest::AssumeRole() { - const UnitTestImpl* const impl = GetUnitTestImpl(); - const InternalRunDeathTestFlag* const flag = - impl->internal_run_death_test_flag(); - const TestInfo* const info = impl->current_test_info(); - const int death_test_index = info->result()->death_test_count(); - - if (flag != NULL) { - // ParseInternalRunDeathTestFlag() has performed all the necessary - // processing. - set_write_fd(flag->write_fd()); - return EXECUTE_TEST; - } - - // WindowsDeathTest uses an anonymous pipe to communicate results of - // a death test. - SECURITY_ATTRIBUTES handles_are_inheritable = { - sizeof(SECURITY_ATTRIBUTES), NULL, TRUE }; - HANDLE read_handle, write_handle; - GTEST_DEATH_TEST_CHECK_( - ::CreatePipe(&read_handle, &write_handle, &handles_are_inheritable, - 0) // Default buffer size. - != FALSE); - set_read_fd(::_open_osfhandle(reinterpret_cast(read_handle), - O_RDONLY)); - write_handle_.Reset(write_handle); - event_handle_.Reset(::CreateEvent( - &handles_are_inheritable, - TRUE, // The event will automatically reset to non-signaled state. - FALSE, // The initial state is non-signalled. - NULL)); // The even is unnamed. - GTEST_DEATH_TEST_CHECK_(event_handle_.Get() != NULL); - const String filter_flag = String::Format("--%s%s=%s.%s", - GTEST_FLAG_PREFIX_, kFilterFlag, - info->test_case_name(), - info->name()); - const String internal_flag = String::Format( - "--%s%s=%s|%d|%d|%u|%Iu|%Iu", - GTEST_FLAG_PREFIX_, - kInternalRunDeathTestFlag, - file_, line_, - death_test_index, - static_cast(::GetCurrentProcessId()), - // size_t has the same with as pointers on both 32-bit and 64-bit - // Windows platforms. - // See http://msdn.microsoft.com/en-us/library/tcxf1dw6.aspx. - reinterpret_cast(write_handle), - reinterpret_cast(event_handle_.Get())); - - char executable_path[_MAX_PATH + 1]; // NOLINT - GTEST_DEATH_TEST_CHECK_( - _MAX_PATH + 1 != ::GetModuleFileNameA(NULL, - executable_path, - _MAX_PATH)); - - String command_line = String::Format("%s %s \"%s\"", - ::GetCommandLineA(), - filter_flag.c_str(), - internal_flag.c_str()); - - DeathTest::set_last_death_test_message(""); - - CaptureStderr(); - // Flush the log buffers since the log streams are shared with the child. - FlushInfoLog(); - - // The child process will share the standard handles with the parent. - STARTUPINFOA startup_info; - memset(&startup_info, 0, sizeof(STARTUPINFO)); - startup_info.dwFlags = STARTF_USESTDHANDLES; - startup_info.hStdInput = ::GetStdHandle(STD_INPUT_HANDLE); - startup_info.hStdOutput = ::GetStdHandle(STD_OUTPUT_HANDLE); - startup_info.hStdError = ::GetStdHandle(STD_ERROR_HANDLE); - - PROCESS_INFORMATION process_info; - GTEST_DEATH_TEST_CHECK_(::CreateProcessA( - executable_path, - const_cast(command_line.c_str()), - NULL, // Retuned process handle is not inheritable. - NULL, // Retuned thread handle is not inheritable. - TRUE, // Child inherits all inheritable handles (for write_handle_). - 0x0, // Default creation flags. - NULL, // Inherit the parent's environment. - UnitTest::GetInstance()->original_working_dir(), - &startup_info, - &process_info) != FALSE); - child_handle_.Reset(process_info.hProcess); - ::CloseHandle(process_info.hThread); - set_spawned(true); - return OVERSEE_TEST; -} -# else // We are not on Windows. - -// ForkingDeathTest provides implementations for most of the abstract -// methods of the DeathTest interface. Only the AssumeRole method is -// left undefined. -class ForkingDeathTest : public DeathTestImpl { - public: - ForkingDeathTest(const char* statement, const RE* regex); - - // All of these virtual functions are inherited from DeathTest. - virtual int Wait(); - - protected: - void set_child_pid(pid_t child_pid) { child_pid_ = child_pid; } - - private: - // PID of child process during death test; 0 in the child process itself. - pid_t child_pid_; -}; - -// Constructs a ForkingDeathTest. -ForkingDeathTest::ForkingDeathTest(const char* a_statement, const RE* a_regex) - : DeathTestImpl(a_statement, a_regex), - child_pid_(-1) {} - -// Waits for the child in a death test to exit, returning its exit -// status, or 0 if no child process exists. As a side effect, sets the -// outcome data member. -int ForkingDeathTest::Wait() { - if (!spawned()) - return 0; - - ReadAndInterpretStatusByte(); - - int status_value; - GTEST_DEATH_TEST_CHECK_SYSCALL_(waitpid(child_pid_, &status_value, 0)); - set_status(status_value); - return status_value; -} - -// A concrete death test class that forks, then immediately runs the test -// in the child process. -class NoExecDeathTest : public ForkingDeathTest { - public: - NoExecDeathTest(const char* a_statement, const RE* a_regex) : - ForkingDeathTest(a_statement, a_regex) { } - virtual TestRole AssumeRole(); -}; - -// The AssumeRole process for a fork-and-run death test. It implements a -// straightforward fork, with a simple pipe to transmit the status byte. -DeathTest::TestRole NoExecDeathTest::AssumeRole() { - const size_t thread_count = GetThreadCount(); - if (thread_count != 1) { - GTEST_LOG_(WARNING) << DeathTestThreadWarning(thread_count); - } - - int pipe_fd[2]; - GTEST_DEATH_TEST_CHECK_(pipe(pipe_fd) != -1); - - DeathTest::set_last_death_test_message(""); - CaptureStderr(); - // When we fork the process below, the log file buffers are copied, but the - // file descriptors are shared. We flush all log files here so that closing - // the file descriptors in the child process doesn't throw off the - // synchronization between descriptors and buffers in the parent process. - // This is as close to the fork as possible to avoid a race condition in case - // there are multiple threads running before the death test, and another - // thread writes to the log file. - FlushInfoLog(); - - const pid_t child_pid = fork(); - GTEST_DEATH_TEST_CHECK_(child_pid != -1); - set_child_pid(child_pid); - if (child_pid == 0) { - GTEST_DEATH_TEST_CHECK_SYSCALL_(close(pipe_fd[0])); - set_write_fd(pipe_fd[1]); - // Redirects all logging to stderr in the child process to prevent - // concurrent writes to the log files. We capture stderr in the parent - // process and append the child process' output to a log. - LogToStderr(); - // Event forwarding to the listeners of event listener API mush be shut - // down in death test subprocesses. - GetUnitTestImpl()->listeners()->SuppressEventForwarding(); - return EXECUTE_TEST; - } else { - GTEST_DEATH_TEST_CHECK_SYSCALL_(close(pipe_fd[1])); - set_read_fd(pipe_fd[0]); - set_spawned(true); - return OVERSEE_TEST; - } -} - -// A concrete death test class that forks and re-executes the main -// program from the beginning, with command-line flags set that cause -// only this specific death test to be run. -class ExecDeathTest : public ForkingDeathTest { - public: - ExecDeathTest(const char* a_statement, const RE* a_regex, - const char* file, int line) : - ForkingDeathTest(a_statement, a_regex), file_(file), line_(line) { } - virtual TestRole AssumeRole(); - private: - // The name of the file in which the death test is located. - const char* const file_; - // The line number on which the death test is located. - const int line_; -}; - -// Utility class for accumulating command-line arguments. -class Arguments { - public: - Arguments() { - args_.push_back(NULL); - } - - ~Arguments() { - for (std::vector::iterator i = args_.begin(); i != args_.end(); - ++i) { - free(*i); - } - } - void AddArgument(const char* argument) { - args_.insert(args_.end() - 1, posix::StrDup(argument)); - } - - template - void AddArguments(const ::std::vector& arguments) { - for (typename ::std::vector::const_iterator i = arguments.begin(); - i != arguments.end(); - ++i) { - args_.insert(args_.end() - 1, posix::StrDup(i->c_str())); - } - } - char* const* Argv() { - return &args_[0]; - } - private: - std::vector args_; -}; - -// A struct that encompasses the arguments to the child process of a -// threadsafe-style death test process. -struct ExecDeathTestArgs { - char* const* argv; // Command-line arguments for the child's call to exec - int close_fd; // File descriptor to close; the read end of a pipe -}; - -# if GTEST_OS_MAC -inline char** GetEnviron() { - // When Google Test is built as a framework on MacOS X, the environ variable - // is unavailable. Apple's documentation (man environ) recommends using - // _NSGetEnviron() instead. - return *_NSGetEnviron(); -} -# else -// Some POSIX platforms expect you to declare environ. extern "C" makes -// it reside in the global namespace. -extern "C" char** environ; -inline char** GetEnviron() { return environ; } -# endif // GTEST_OS_MAC - -// The main function for a threadsafe-style death test child process. -// This function is called in a clone()-ed process and thus must avoid -// any potentially unsafe operations like malloc or libc functions. -static int ExecDeathTestChildMain(void* child_arg) { - ExecDeathTestArgs* const args = static_cast(child_arg); - GTEST_DEATH_TEST_CHECK_SYSCALL_(close(args->close_fd)); - - // We need to execute the test program in the same environment where - // it was originally invoked. Therefore we change to the original - // working directory first. - const char* const original_dir = - UnitTest::GetInstance()->original_working_dir(); - // We can safely call chdir() as it's a direct system call. - if (chdir(original_dir) != 0) { - DeathTestAbort(String::Format("chdir(\"%s\") failed: %s", - original_dir, - GetLastErrnoDescription().c_str())); - return EXIT_FAILURE; - } - - // We can safely call execve() as it's a direct system call. We - // cannot use execvp() as it's a libc function and thus potentially - // unsafe. Since execve() doesn't search the PATH, the user must - // invoke the test program via a valid path that contains at least - // one path separator. - execve(args->argv[0], args->argv, GetEnviron()); - DeathTestAbort(String::Format("execve(%s, ...) in %s failed: %s", - args->argv[0], - original_dir, - GetLastErrnoDescription().c_str())); - return EXIT_FAILURE; -} - -// Two utility routines that together determine the direction the stack -// grows. -// This could be accomplished more elegantly by a single recursive -// function, but we want to guard against the unlikely possibility of -// a smart compiler optimizing the recursion away. -// -// GTEST_NO_INLINE_ is required to prevent GCC 4.6 from inlining -// StackLowerThanAddress into StackGrowsDown, which then doesn't give -// correct answer. -bool StackLowerThanAddress(const void* ptr) GTEST_NO_INLINE_; -bool StackLowerThanAddress(const void* ptr) { - int dummy; - return &dummy < ptr; -} - -bool StackGrowsDown() { - int dummy; - return StackLowerThanAddress(&dummy); -} - -// A threadsafe implementation of fork(2) for threadsafe-style death tests -// that uses clone(2). It dies with an error message if anything goes -// wrong. -static pid_t ExecDeathTestFork(char* const* argv, int close_fd) { - ExecDeathTestArgs args = { argv, close_fd }; - pid_t child_pid = -1; - -# if GTEST_HAS_CLONE - const bool use_fork = GTEST_FLAG(death_test_use_fork); - - if (!use_fork) { - static const bool stack_grows_down = StackGrowsDown(); - const size_t stack_size = getpagesize(); - // MMAP_ANONYMOUS is not defined on Mac, so we use MAP_ANON instead. - void* const stack = mmap(NULL, stack_size, PROT_READ | PROT_WRITE, - MAP_ANON | MAP_PRIVATE, -1, 0); - GTEST_DEATH_TEST_CHECK_(stack != MAP_FAILED); - void* const stack_top = - static_cast(stack) + (stack_grows_down ? stack_size : 0); - - child_pid = clone(&ExecDeathTestChildMain, stack_top, SIGCHLD, &args); - - GTEST_DEATH_TEST_CHECK_(munmap(stack, stack_size) != -1); - } -# else - const bool use_fork = true; -# endif // GTEST_HAS_CLONE - - if (use_fork && (child_pid = fork()) == 0) { - ExecDeathTestChildMain(&args); - _exit(0); - } - - GTEST_DEATH_TEST_CHECK_(child_pid != -1); - return child_pid; -} - -// The AssumeRole process for a fork-and-exec death test. It re-executes the -// main program from the beginning, setting the --gtest_filter -// and --gtest_internal_run_death_test flags to cause only the current -// death test to be re-run. -DeathTest::TestRole ExecDeathTest::AssumeRole() { - const UnitTestImpl* const impl = GetUnitTestImpl(); - const InternalRunDeathTestFlag* const flag = - impl->internal_run_death_test_flag(); - const TestInfo* const info = impl->current_test_info(); - const int death_test_index = info->result()->death_test_count(); - - if (flag != NULL) { - set_write_fd(flag->write_fd()); - return EXECUTE_TEST; - } - - int pipe_fd[2]; - GTEST_DEATH_TEST_CHECK_(pipe(pipe_fd) != -1); - // Clear the close-on-exec flag on the write end of the pipe, lest - // it be closed when the child process does an exec: - GTEST_DEATH_TEST_CHECK_(fcntl(pipe_fd[1], F_SETFD, 0) != -1); - - const String filter_flag = - String::Format("--%s%s=%s.%s", - GTEST_FLAG_PREFIX_, kFilterFlag, - info->test_case_name(), info->name()); - const String internal_flag = - String::Format("--%s%s=%s|%d|%d|%d", - GTEST_FLAG_PREFIX_, kInternalRunDeathTestFlag, - file_, line_, death_test_index, pipe_fd[1]); - Arguments args; - args.AddArguments(GetArgvs()); - args.AddArgument(filter_flag.c_str()); - args.AddArgument(internal_flag.c_str()); - - DeathTest::set_last_death_test_message(""); - - CaptureStderr(); - // See the comment in NoExecDeathTest::AssumeRole for why the next line - // is necessary. - FlushInfoLog(); - - const pid_t child_pid = ExecDeathTestFork(args.Argv(), pipe_fd[0]); - GTEST_DEATH_TEST_CHECK_SYSCALL_(close(pipe_fd[1])); - set_child_pid(child_pid); - set_read_fd(pipe_fd[0]); - set_spawned(true); - return OVERSEE_TEST; -} - -# endif // !GTEST_OS_WINDOWS - -// Creates a concrete DeathTest-derived class that depends on the -// --gtest_death_test_style flag, and sets the pointer pointed to -// by the "test" argument to its address. If the test should be -// skipped, sets that pointer to NULL. Returns true, unless the -// flag is set to an invalid value. -bool DefaultDeathTestFactory::Create(const char* statement, const RE* regex, - const char* file, int line, - DeathTest** test) { - UnitTestImpl* const impl = GetUnitTestImpl(); - const InternalRunDeathTestFlag* const flag = - impl->internal_run_death_test_flag(); - const int death_test_index = impl->current_test_info() - ->increment_death_test_count(); - - if (flag != NULL) { - if (death_test_index > flag->index()) { - DeathTest::set_last_death_test_message(String::Format( - "Death test count (%d) somehow exceeded expected maximum (%d)", - death_test_index, flag->index())); - return false; - } - - if (!(flag->file() == file && flag->line() == line && - flag->index() == death_test_index)) { - *test = NULL; - return true; - } - } - -# if GTEST_OS_WINDOWS - - if (GTEST_FLAG(death_test_style) == "threadsafe" || - GTEST_FLAG(death_test_style) == "fast") { - *test = new WindowsDeathTest(statement, regex, file, line); - } - -# else - - if (GTEST_FLAG(death_test_style) == "threadsafe") { - *test = new ExecDeathTest(statement, regex, file, line); - } else if (GTEST_FLAG(death_test_style) == "fast") { - *test = new NoExecDeathTest(statement, regex); - } - -# endif // GTEST_OS_WINDOWS - - else { // NOLINT - this is more readable than unbalanced brackets inside #if. - DeathTest::set_last_death_test_message(String::Format( - "Unknown death test style \"%s\" encountered", - GTEST_FLAG(death_test_style).c_str())); - return false; - } - - return true; -} - -// Splits a given string on a given delimiter, populating a given -// vector with the fields. GTEST_HAS_DEATH_TEST implies that we have -// ::std::string, so we can use it here. -static void SplitString(const ::std::string& str, char delimiter, - ::std::vector< ::std::string>* dest) { - ::std::vector< ::std::string> parsed; - ::std::string::size_type pos = 0; - while (::testing::internal::AlwaysTrue()) { - const ::std::string::size_type colon = str.find(delimiter, pos); - if (colon == ::std::string::npos) { - parsed.push_back(str.substr(pos)); - break; - } else { - parsed.push_back(str.substr(pos, colon - pos)); - pos = colon + 1; - } - } - dest->swap(parsed); -} - -# if GTEST_OS_WINDOWS -// Recreates the pipe and event handles from the provided parameters, -// signals the event, and returns a file descriptor wrapped around the pipe -// handle. This function is called in the child process only. -int GetStatusFileDescriptor(unsigned int parent_process_id, - size_t write_handle_as_size_t, - size_t event_handle_as_size_t) { - AutoHandle parent_process_handle(::OpenProcess(PROCESS_DUP_HANDLE, - FALSE, // Non-inheritable. - parent_process_id)); - if (parent_process_handle.Get() == INVALID_HANDLE_VALUE) { - DeathTestAbort(String::Format("Unable to open parent process %u", - parent_process_id)); - } - - // TODO(vladl@google.com): Replace the following check with a - // compile-time assertion when available. - GTEST_CHECK_(sizeof(HANDLE) <= sizeof(size_t)); - - const HANDLE write_handle = - reinterpret_cast(write_handle_as_size_t); - HANDLE dup_write_handle; - - // The newly initialized handle is accessible only in in the parent - // process. To obtain one accessible within the child, we need to use - // DuplicateHandle. - if (!::DuplicateHandle(parent_process_handle.Get(), write_handle, - ::GetCurrentProcess(), &dup_write_handle, - 0x0, // Requested privileges ignored since - // DUPLICATE_SAME_ACCESS is used. - FALSE, // Request non-inheritable handler. - DUPLICATE_SAME_ACCESS)) { - DeathTestAbort(String::Format( - "Unable to duplicate the pipe handle %Iu from the parent process %u", - write_handle_as_size_t, parent_process_id)); - } - - const HANDLE event_handle = reinterpret_cast(event_handle_as_size_t); - HANDLE dup_event_handle; - - if (!::DuplicateHandle(parent_process_handle.Get(), event_handle, - ::GetCurrentProcess(), &dup_event_handle, - 0x0, - FALSE, - DUPLICATE_SAME_ACCESS)) { - DeathTestAbort(String::Format( - "Unable to duplicate the event handle %Iu from the parent process %u", - event_handle_as_size_t, parent_process_id)); - } - - const int write_fd = - ::_open_osfhandle(reinterpret_cast(dup_write_handle), O_APPEND); - if (write_fd == -1) { - DeathTestAbort(String::Format( - "Unable to convert pipe handle %Iu to a file descriptor", - write_handle_as_size_t)); - } - - // Signals the parent that the write end of the pipe has been acquired - // so the parent can release its own write end. - ::SetEvent(dup_event_handle); - - return write_fd; -} -# endif // GTEST_OS_WINDOWS - -// Returns a newly created InternalRunDeathTestFlag object with fields -// initialized from the GTEST_FLAG(internal_run_death_test) flag if -// the flag is specified; otherwise returns NULL. -InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag() { - if (GTEST_FLAG(internal_run_death_test) == "") return NULL; - - // GTEST_HAS_DEATH_TEST implies that we have ::std::string, so we - // can use it here. - int line = -1; - int index = -1; - ::std::vector< ::std::string> fields; - SplitString(GTEST_FLAG(internal_run_death_test).c_str(), '|', &fields); - int write_fd = -1; - -# if GTEST_OS_WINDOWS - - unsigned int parent_process_id = 0; - size_t write_handle_as_size_t = 0; - size_t event_handle_as_size_t = 0; - - if (fields.size() != 6 - || !ParseNaturalNumber(fields[1], &line) - || !ParseNaturalNumber(fields[2], &index) - || !ParseNaturalNumber(fields[3], &parent_process_id) - || !ParseNaturalNumber(fields[4], &write_handle_as_size_t) - || !ParseNaturalNumber(fields[5], &event_handle_as_size_t)) { - DeathTestAbort(String::Format( - "Bad --gtest_internal_run_death_test flag: %s", - GTEST_FLAG(internal_run_death_test).c_str())); - } - write_fd = GetStatusFileDescriptor(parent_process_id, - write_handle_as_size_t, - event_handle_as_size_t); -# else - - if (fields.size() != 4 - || !ParseNaturalNumber(fields[1], &line) - || !ParseNaturalNumber(fields[2], &index) - || !ParseNaturalNumber(fields[3], &write_fd)) { - DeathTestAbort(String::Format( - "Bad --gtest_internal_run_death_test flag: %s", - GTEST_FLAG(internal_run_death_test).c_str())); - } - -# endif // GTEST_OS_WINDOWS - - return new InternalRunDeathTestFlag(fields[0], line, index, write_fd); -} - -} // namespace internal - -#endif // GTEST_HAS_DEATH_TEST - -} // namespace testing -// Copyright 2008, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Authors: keith.ray@gmail.com (Keith Ray) - - -#include - -#if GTEST_OS_WINDOWS_MOBILE -# include -#elif GTEST_OS_WINDOWS -# include -# include -#elif GTEST_OS_SYMBIAN || GTEST_OS_NACL -// Symbian OpenC and NaCl have PATH_MAX in sys/syslimits.h -# include -#else -# include -# include // Some Linux distributions define PATH_MAX here. -#endif // GTEST_OS_WINDOWS_MOBILE - -#if GTEST_OS_WINDOWS -# define GTEST_PATH_MAX_ _MAX_PATH -#elif defined(PATH_MAX) -# define GTEST_PATH_MAX_ PATH_MAX -#elif defined(_XOPEN_PATH_MAX) -# define GTEST_PATH_MAX_ _XOPEN_PATH_MAX -#else -# define GTEST_PATH_MAX_ _POSIX_PATH_MAX -#endif // GTEST_OS_WINDOWS - - -namespace testing { -namespace internal { - -#if GTEST_OS_WINDOWS -// On Windows, '\\' is the standard path separator, but many tools and the -// Windows API also accept '/' as an alternate path separator. Unless otherwise -// noted, a file path can contain either kind of path separators, or a mixture -// of them. -const char kPathSeparator = '\\'; -const char kAlternatePathSeparator = '/'; -const char kPathSeparatorString[] = "\\"; -const char kAlternatePathSeparatorString[] = "/"; -# if GTEST_OS_WINDOWS_MOBILE -// Windows CE doesn't have a current directory. You should not use -// the current directory in tests on Windows CE, but this at least -// provides a reasonable fallback. -const char kCurrentDirectoryString[] = "\\"; -// Windows CE doesn't define INVALID_FILE_ATTRIBUTES -const DWORD kInvalidFileAttributes = 0xffffffff; -# else -const char kCurrentDirectoryString[] = ".\\"; -# endif // GTEST_OS_WINDOWS_MOBILE -#else -const char kPathSeparator = '/'; -const char kPathSeparatorString[] = "/"; -const char kCurrentDirectoryString[] = "./"; -#endif // GTEST_OS_WINDOWS - -// Returns whether the given character is a valid path separator. -static bool IsPathSeparator(char c) { -#if GTEST_HAS_ALT_PATH_SEP_ - return (c == kPathSeparator) || (c == kAlternatePathSeparator); -#else - return c == kPathSeparator; -#endif -} - -// Returns the current working directory, or "" if unsuccessful. -FilePath FilePath::GetCurrentDir() { -#if GTEST_OS_WINDOWS_MOBILE - // Windows CE doesn't have a current directory, so we just return - // something reasonable. - return FilePath(kCurrentDirectoryString); -#elif GTEST_OS_WINDOWS - char cwd[GTEST_PATH_MAX_ + 1] = { '\0' }; - return FilePath(_getcwd(cwd, sizeof(cwd)) == NULL ? "" : cwd); -#else - char cwd[GTEST_PATH_MAX_ + 1] = { '\0' }; - return FilePath(getcwd(cwd, sizeof(cwd)) == NULL ? "" : cwd); -#endif // GTEST_OS_WINDOWS_MOBILE -} - -// Returns a copy of the FilePath with the case-insensitive extension removed. -// Example: FilePath("dir/file.exe").RemoveExtension("EXE") returns -// FilePath("dir/file"). If a case-insensitive extension is not -// found, returns a copy of the original FilePath. -FilePath FilePath::RemoveExtension(const char* extension) const { - String dot_extension(String::Format(".%s", extension)); - if (pathname_.EndsWithCaseInsensitive(dot_extension.c_str())) { - return FilePath(String(pathname_.c_str(), pathname_.length() - 4)); - } - return *this; -} - -// Returns a pointer to the last occurence of a valid path separator in -// the FilePath. On Windows, for example, both '/' and '\' are valid path -// separators. Returns NULL if no path separator was found. -const char* FilePath::FindLastPathSeparator() const { - const char* const last_sep = strrchr(c_str(), kPathSeparator); -#if GTEST_HAS_ALT_PATH_SEP_ - const char* const last_alt_sep = strrchr(c_str(), kAlternatePathSeparator); - // Comparing two pointers of which only one is NULL is undefined. - if (last_alt_sep != NULL && - (last_sep == NULL || last_alt_sep > last_sep)) { - return last_alt_sep; - } -#endif - return last_sep; -} - -// Returns a copy of the FilePath with the directory part removed. -// Example: FilePath("path/to/file").RemoveDirectoryName() returns -// FilePath("file"). If there is no directory part ("just_a_file"), it returns -// the FilePath unmodified. If there is no file part ("just_a_dir/") it -// returns an empty FilePath (""). -// On Windows platform, '\' is the path separator, otherwise it is '/'. -FilePath FilePath::RemoveDirectoryName() const { - const char* const last_sep = FindLastPathSeparator(); - return last_sep ? FilePath(String(last_sep + 1)) : *this; -} - -// RemoveFileName returns the directory path with the filename removed. -// Example: FilePath("path/to/file").RemoveFileName() returns "path/to/". -// If the FilePath is "a_file" or "/a_file", RemoveFileName returns -// FilePath("./") or, on Windows, FilePath(".\\"). If the filepath does -// not have a file, like "just/a/dir/", it returns the FilePath unmodified. -// On Windows platform, '\' is the path separator, otherwise it is '/'. -FilePath FilePath::RemoveFileName() const { - const char* const last_sep = FindLastPathSeparator(); - String dir; - if (last_sep) { - dir = String(c_str(), last_sep + 1 - c_str()); - } else { - dir = kCurrentDirectoryString; - } - return FilePath(dir); -} - -// Helper functions for naming files in a directory for xml output. - -// Given directory = "dir", base_name = "test", number = 0, -// extension = "xml", returns "dir/test.xml". If number is greater -// than zero (e.g., 12), returns "dir/test_12.xml". -// On Windows platform, uses \ as the separator rather than /. -FilePath FilePath::MakeFileName(const FilePath& directory, - const FilePath& base_name, - int number, - const char* extension) { - String file; - if (number == 0) { - file = String::Format("%s.%s", base_name.c_str(), extension); - } else { - file = String::Format("%s_%d.%s", base_name.c_str(), number, extension); - } - return ConcatPaths(directory, FilePath(file)); -} - -// Given directory = "dir", relative_path = "test.xml", returns "dir/test.xml". -// On Windows, uses \ as the separator rather than /. -FilePath FilePath::ConcatPaths(const FilePath& directory, - const FilePath& relative_path) { - if (directory.IsEmpty()) - return relative_path; - const FilePath dir(directory.RemoveTrailingPathSeparator()); - return FilePath(String::Format("%s%c%s", dir.c_str(), kPathSeparator, - relative_path.c_str())); -} - -// Returns true if pathname describes something findable in the file-system, -// either a file, directory, or whatever. -bool FilePath::FileOrDirectoryExists() const { -#if GTEST_OS_WINDOWS_MOBILE - LPCWSTR unicode = String::AnsiToUtf16(pathname_.c_str()); - const DWORD attributes = GetFileAttributes(unicode); - delete [] unicode; - return attributes != kInvalidFileAttributes; -#else - posix::StatStruct file_stat; - return posix::Stat(pathname_.c_str(), &file_stat) == 0; -#endif // GTEST_OS_WINDOWS_MOBILE -} - -// Returns true if pathname describes a directory in the file-system -// that exists. -bool FilePath::DirectoryExists() const { - bool result = false; -#if GTEST_OS_WINDOWS - // Don't strip off trailing separator if path is a root directory on - // Windows (like "C:\\"). - const FilePath& path(IsRootDirectory() ? *this : - RemoveTrailingPathSeparator()); -#else - const FilePath& path(*this); -#endif - -#if GTEST_OS_WINDOWS_MOBILE - LPCWSTR unicode = String::AnsiToUtf16(path.c_str()); - const DWORD attributes = GetFileAttributes(unicode); - delete [] unicode; - if ((attributes != kInvalidFileAttributes) && - (attributes & FILE_ATTRIBUTE_DIRECTORY)) { - result = true; - } -#else - posix::StatStruct file_stat; - result = posix::Stat(path.c_str(), &file_stat) == 0 && - posix::IsDir(file_stat); -#endif // GTEST_OS_WINDOWS_MOBILE - - return result; -} - -// Returns true if pathname describes a root directory. (Windows has one -// root directory per disk drive.) -bool FilePath::IsRootDirectory() const { -#if GTEST_OS_WINDOWS - // TODO(wan@google.com): on Windows a network share like - // \\server\share can be a root directory, although it cannot be the - // current directory. Handle this properly. - return pathname_.length() == 3 && IsAbsolutePath(); -#else - return pathname_.length() == 1 && IsPathSeparator(pathname_.c_str()[0]); -#endif -} - -// Returns true if pathname describes an absolute path. -bool FilePath::IsAbsolutePath() const { - const char* const name = pathname_.c_str(); -#if GTEST_OS_WINDOWS - return pathname_.length() >= 3 && - ((name[0] >= 'a' && name[0] <= 'z') || - (name[0] >= 'A' && name[0] <= 'Z')) && - name[1] == ':' && - IsPathSeparator(name[2]); -#else - return IsPathSeparator(name[0]); -#endif -} - -// Returns a pathname for a file that does not currently exist. The pathname -// will be directory/base_name.extension or -// directory/base_name_.extension if directory/base_name.extension -// already exists. The number will be incremented until a pathname is found -// that does not already exist. -// Examples: 'dir/foo_test.xml' or 'dir/foo_test_1.xml'. -// There could be a race condition if two or more processes are calling this -// function at the same time -- they could both pick the same filename. -FilePath FilePath::GenerateUniqueFileName(const FilePath& directory, - const FilePath& base_name, - const char* extension) { - FilePath full_pathname; - int number = 0; - do { - full_pathname.Set(MakeFileName(directory, base_name, number++, extension)); - } while (full_pathname.FileOrDirectoryExists()); - return full_pathname; -} - -// Returns true if FilePath ends with a path separator, which indicates that -// it is intended to represent a directory. Returns false otherwise. -// This does NOT check that a directory (or file) actually exists. -bool FilePath::IsDirectory() const { - return !pathname_.empty() && - IsPathSeparator(pathname_.c_str()[pathname_.length() - 1]); -} - -// Create directories so that path exists. Returns true if successful or if -// the directories already exist; returns false if unable to create directories -// for any reason. -bool FilePath::CreateDirectoriesRecursively() const { - if (!this->IsDirectory()) { - return false; - } - - if (pathname_.length() == 0 || this->DirectoryExists()) { - return true; - } - - const FilePath parent(this->RemoveTrailingPathSeparator().RemoveFileName()); - return parent.CreateDirectoriesRecursively() && this->CreateFolder(); -} - -// Create the directory so that path exists. Returns true if successful or -// if the directory already exists; returns false if unable to create the -// directory for any reason, including if the parent directory does not -// exist. Not named "CreateDirectory" because that's a macro on Windows. -bool FilePath::CreateFolder() const { -#if GTEST_OS_WINDOWS_MOBILE - FilePath removed_sep(this->RemoveTrailingPathSeparator()); - LPCWSTR unicode = String::AnsiToUtf16(removed_sep.c_str()); - int result = CreateDirectory(unicode, NULL) ? 0 : -1; - delete [] unicode; -#elif GTEST_OS_WINDOWS - int result = _mkdir(pathname_.c_str()); -#else - int result = mkdir(pathname_.c_str(), 0777); -#endif // GTEST_OS_WINDOWS_MOBILE - - if (result == -1) { - return this->DirectoryExists(); // An error is OK if the directory exists. - } - return true; // No error. -} - -// If input name has a trailing separator character, remove it and return the -// name, otherwise return the name string unmodified. -// On Windows platform, uses \ as the separator, other platforms use /. -FilePath FilePath::RemoveTrailingPathSeparator() const { - return IsDirectory() - ? FilePath(String(pathname_.c_str(), pathname_.length() - 1)) - : *this; -} - -// Removes any redundant separators that might be in the pathname. -// For example, "bar///foo" becomes "bar/foo". Does not eliminate other -// redundancies that might be in a pathname involving "." or "..". -// TODO(wan@google.com): handle Windows network shares (e.g. \\server\share). -void FilePath::Normalize() { - if (pathname_.c_str() == NULL) { - pathname_ = ""; - return; - } - const char* src = pathname_.c_str(); - char* const dest = new char[pathname_.length() + 1]; - char* dest_ptr = dest; - memset(dest_ptr, 0, pathname_.length() + 1); - - while (*src != '\0') { - *dest_ptr = *src; - if (!IsPathSeparator(*src)) { - src++; - } else { -#if GTEST_HAS_ALT_PATH_SEP_ - if (*dest_ptr == kAlternatePathSeparator) { - *dest_ptr = kPathSeparator; - } -#endif - while (IsPathSeparator(*src)) - src++; - } - dest_ptr++; - } - *dest_ptr = '\0'; - pathname_ = dest; - delete[] dest; -} - -} // namespace internal -} // namespace testing -// Copyright 2008, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Author: wan@google.com (Zhanyong Wan) - - -#include -#include -#include -#include - -#if GTEST_OS_WINDOWS_MOBILE -# include // For TerminateProcess() -#elif GTEST_OS_WINDOWS -# include -# include -#else -# include -#endif // GTEST_OS_WINDOWS_MOBILE - -#if GTEST_OS_MAC -# include -# include -# include -#endif // GTEST_OS_MAC - - -// Indicates that this translation unit is part of Google Test's -// implementation. It must come before gtest-internal-inl.h is -// included, or there will be a compiler error. This trick is to -// prevent a user from accidentally including gtest-internal-inl.h in -// his code. -#define GTEST_IMPLEMENTATION_ 1 -#undef GTEST_IMPLEMENTATION_ - -namespace testing { -namespace internal { - -#if defined(_MSC_VER) || defined(__BORLANDC__) -// MSVC and C++Builder do not provide a definition of STDERR_FILENO. -const int kStdOutFileno = 1; -const int kStdErrFileno = 2; -#else -const int kStdOutFileno = STDOUT_FILENO; -const int kStdErrFileno = STDERR_FILENO; -#endif // _MSC_VER - -#if GTEST_OS_MAC - -// Returns the number of threads running in the process, or 0 to indicate that -// we cannot detect it. -size_t GetThreadCount() { - const task_t task = mach_task_self(); - mach_msg_type_number_t thread_count; - thread_act_array_t thread_list; - const kern_return_t status = task_threads(task, &thread_list, &thread_count); - if (status == KERN_SUCCESS) { - // task_threads allocates resources in thread_list and we need to free them - // to avoid leaks. - vm_deallocate(task, - reinterpret_cast(thread_list), - sizeof(thread_t) * thread_count); - return static_cast(thread_count); - } else { - return 0; - } -} - -#else - -size_t GetThreadCount() { - // There's no portable way to detect the number of threads, so we just - // return 0 to indicate that we cannot detect it. - return 0; -} - -#endif // GTEST_OS_MAC - -#if GTEST_USES_POSIX_RE - -// Implements RE. Currently only needed for death tests. - -RE::~RE() { - if (is_valid_) { - // regfree'ing an invalid regex might crash because the content - // of the regex is undefined. Since the regex's are essentially - // the same, one cannot be valid (or invalid) without the other - // being so too. - regfree(&partial_regex_); - regfree(&full_regex_); - } - free(const_cast(pattern_)); -} - -// Returns true iff regular expression re matches the entire str. -bool RE::FullMatch(const char* str, const RE& re) { - if (!re.is_valid_) return false; - - regmatch_t match; - return regexec(&re.full_regex_, str, 1, &match, 0) == 0; -} - -// Returns true iff regular expression re matches a substring of str -// (including str itself). -bool RE::PartialMatch(const char* str, const RE& re) { - if (!re.is_valid_) return false; - - regmatch_t match; - return regexec(&re.partial_regex_, str, 1, &match, 0) == 0; -} - -// Initializes an RE from its string representation. -void RE::Init(const char* regex) { - pattern_ = posix::StrDup(regex); - - // Reserves enough bytes to hold the regular expression used for a - // full match. - const size_t full_regex_len = strlen(regex) + 10; - char* const full_pattern = new char[full_regex_len]; - - snprintf(full_pattern, full_regex_len, "^(%s)$", regex); - is_valid_ = regcomp(&full_regex_, full_pattern, REG_EXTENDED) == 0; - // We want to call regcomp(&partial_regex_, ...) even if the - // previous expression returns false. Otherwise partial_regex_ may - // not be properly initialized can may cause trouble when it's - // freed. - // - // Some implementation of POSIX regex (e.g. on at least some - // versions of Cygwin) doesn't accept the empty string as a valid - // regex. We change it to an equivalent form "()" to be safe. - if (is_valid_) { - const char* const partial_regex = (*regex == '\0') ? "()" : regex; - is_valid_ = regcomp(&partial_regex_, partial_regex, REG_EXTENDED) == 0; - } - EXPECT_TRUE(is_valid_) - << "Regular expression \"" << regex - << "\" is not a valid POSIX Extended regular expression."; - - delete[] full_pattern; -} - -#elif GTEST_USES_SIMPLE_RE - -// Returns true iff ch appears anywhere in str (excluding the -// terminating '\0' character). -bool IsInSet(char ch, const char* str) { - return ch != '\0' && strchr(str, ch) != NULL; -} - -// Returns true iff ch belongs to the given classification. Unlike -// similar functions in , these aren't affected by the -// current locale. -bool IsAsciiDigit(char ch) { return '0' <= ch && ch <= '9'; } -bool IsAsciiPunct(char ch) { - return IsInSet(ch, "^-!\"#$%&'()*+,./:;<=>?@[\\]_`{|}~"); -} -bool IsRepeat(char ch) { return IsInSet(ch, "?*+"); } -bool IsAsciiWhiteSpace(char ch) { return IsInSet(ch, " \f\n\r\t\v"); } -bool IsAsciiWordChar(char ch) { - return ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') || - ('0' <= ch && ch <= '9') || ch == '_'; -} - -// Returns true iff "\\c" is a supported escape sequence. -bool IsValidEscape(char c) { - return (IsAsciiPunct(c) || IsInSet(c, "dDfnrsStvwW")); -} - -// Returns true iff the given atom (specified by escaped and pattern) -// matches ch. The result is undefined if the atom is invalid. -bool AtomMatchesChar(bool escaped, char pattern_char, char ch) { - if (escaped) { // "\\p" where p is pattern_char. - switch (pattern_char) { - case 'd': return IsAsciiDigit(ch); - case 'D': return !IsAsciiDigit(ch); - case 'f': return ch == '\f'; - case 'n': return ch == '\n'; - case 'r': return ch == '\r'; - case 's': return IsAsciiWhiteSpace(ch); - case 'S': return !IsAsciiWhiteSpace(ch); - case 't': return ch == '\t'; - case 'v': return ch == '\v'; - case 'w': return IsAsciiWordChar(ch); - case 'W': return !IsAsciiWordChar(ch); - } - return IsAsciiPunct(pattern_char) && pattern_char == ch; - } - - return (pattern_char == '.' && ch != '\n') || pattern_char == ch; -} - -// Helper function used by ValidateRegex() to format error messages. -String FormatRegexSyntaxError(const char* regex, int index) { - return (Message() << "Syntax error at index " << index - << " in simple regular expression \"" << regex << "\": ").GetString(); -} - -// Generates non-fatal failures and returns false if regex is invalid; -// otherwise returns true. -bool ValidateRegex(const char* regex) { - if (regex == NULL) { - // TODO(wan@google.com): fix the source file location in the - // assertion failures to match where the regex is used in user - // code. - ADD_FAILURE() << "NULL is not a valid simple regular expression."; - return false; - } - - bool is_valid = true; - - // True iff ?, *, or + can follow the previous atom. - bool prev_repeatable = false; - for (int i = 0; regex[i]; i++) { - if (regex[i] == '\\') { // An escape sequence - i++; - if (regex[i] == '\0') { - ADD_FAILURE() << FormatRegexSyntaxError(regex, i - 1) - << "'\\' cannot appear at the end."; - return false; - } - - if (!IsValidEscape(regex[i])) { - ADD_FAILURE() << FormatRegexSyntaxError(regex, i - 1) - << "invalid escape sequence \"\\" << regex[i] << "\"."; - is_valid = false; - } - prev_repeatable = true; - } else { // Not an escape sequence. - const char ch = regex[i]; - - if (ch == '^' && i > 0) { - ADD_FAILURE() << FormatRegexSyntaxError(regex, i) - << "'^' can only appear at the beginning."; - is_valid = false; - } else if (ch == '$' && regex[i + 1] != '\0') { - ADD_FAILURE() << FormatRegexSyntaxError(regex, i) - << "'$' can only appear at the end."; - is_valid = false; - } else if (IsInSet(ch, "()[]{}|")) { - ADD_FAILURE() << FormatRegexSyntaxError(regex, i) - << "'" << ch << "' is unsupported."; - is_valid = false; - } else if (IsRepeat(ch) && !prev_repeatable) { - ADD_FAILURE() << FormatRegexSyntaxError(regex, i) - << "'" << ch << "' can only follow a repeatable token."; - is_valid = false; - } - - prev_repeatable = !IsInSet(ch, "^$?*+"); - } - } - - return is_valid; -} - -// Matches a repeated regex atom followed by a valid simple regular -// expression. The regex atom is defined as c if escaped is false, -// or \c otherwise. repeat is the repetition meta character (?, *, -// or +). The behavior is undefined if str contains too many -// characters to be indexable by size_t, in which case the test will -// probably time out anyway. We are fine with this limitation as -// std::string has it too. -bool MatchRepetitionAndRegexAtHead( - bool escaped, char c, char repeat, const char* regex, - const char* str) { - const size_t min_count = (repeat == '+') ? 1 : 0; - const size_t max_count = (repeat == '?') ? 1 : - static_cast(-1) - 1; - // We cannot call numeric_limits::max() as it conflicts with the - // max() macro on Windows. - - for (size_t i = 0; i <= max_count; ++i) { - // We know that the atom matches each of the first i characters in str. - if (i >= min_count && MatchRegexAtHead(regex, str + i)) { - // We have enough matches at the head, and the tail matches too. - // Since we only care about *whether* the pattern matches str - // (as opposed to *how* it matches), there is no need to find a - // greedy match. - return true; - } - if (str[i] == '\0' || !AtomMatchesChar(escaped, c, str[i])) - return false; - } - return false; -} - -// Returns true iff regex matches a prefix of str. regex must be a -// valid simple regular expression and not start with "^", or the -// result is undefined. -bool MatchRegexAtHead(const char* regex, const char* str) { - if (*regex == '\0') // An empty regex matches a prefix of anything. - return true; - - // "$" only matches the end of a string. Note that regex being - // valid guarantees that there's nothing after "$" in it. - if (*regex == '$') - return *str == '\0'; - - // Is the first thing in regex an escape sequence? - const bool escaped = *regex == '\\'; - if (escaped) - ++regex; - if (IsRepeat(regex[1])) { - // MatchRepetitionAndRegexAtHead() calls MatchRegexAtHead(), so - // here's an indirect recursion. It terminates as the regex gets - // shorter in each recursion. - return MatchRepetitionAndRegexAtHead( - escaped, regex[0], regex[1], regex + 2, str); - } else { - // regex isn't empty, isn't "$", and doesn't start with a - // repetition. We match the first atom of regex with the first - // character of str and recurse. - return (*str != '\0') && AtomMatchesChar(escaped, *regex, *str) && - MatchRegexAtHead(regex + 1, str + 1); - } -} - -// Returns true iff regex matches any substring of str. regex must be -// a valid simple regular expression, or the result is undefined. -// -// The algorithm is recursive, but the recursion depth doesn't exceed -// the regex length, so we won't need to worry about running out of -// stack space normally. In rare cases the time complexity can be -// exponential with respect to the regex length + the string length, -// but usually it's must faster (often close to linear). -bool MatchRegexAnywhere(const char* regex, const char* str) { - if (regex == NULL || str == NULL) - return false; - - if (*regex == '^') - return MatchRegexAtHead(regex + 1, str); - - // A successful match can be anywhere in str. - do { - if (MatchRegexAtHead(regex, str)) - return true; - } while (*str++ != '\0'); - return false; -} - -// Implements the RE class. - -RE::~RE() { - free(const_cast(pattern_)); - free(const_cast(full_pattern_)); -} - -// Returns true iff regular expression re matches the entire str. -bool RE::FullMatch(const char* str, const RE& re) { - return re.is_valid_ && MatchRegexAnywhere(re.full_pattern_, str); -} - -// Returns true iff regular expression re matches a substring of str -// (including str itself). -bool RE::PartialMatch(const char* str, const RE& re) { - return re.is_valid_ && MatchRegexAnywhere(re.pattern_, str); -} - -// Initializes an RE from its string representation. -void RE::Init(const char* regex) { - pattern_ = full_pattern_ = NULL; - if (regex != NULL) { - pattern_ = posix::StrDup(regex); - } - - is_valid_ = ValidateRegex(regex); - if (!is_valid_) { - // No need to calculate the full pattern when the regex is invalid. - return; - } - - const size_t len = strlen(regex); - // Reserves enough bytes to hold the regular expression used for a - // full match: we need space to prepend a '^', append a '$', and - // terminate the string with '\0'. - char* buffer = static_cast(malloc(len + 3)); - full_pattern_ = buffer; - - if (*regex != '^') - *buffer++ = '^'; // Makes sure full_pattern_ starts with '^'. - - // We don't use snprintf or strncpy, as they trigger a warning when - // compiled with VC++ 8.0. - memcpy(buffer, regex, len); - buffer += len; - - if (len == 0 || regex[len - 1] != '$') - *buffer++ = '$'; // Makes sure full_pattern_ ends with '$'. - - *buffer = '\0'; -} - -#endif // GTEST_USES_POSIX_RE - -const char kUnknownFile[] = "unknown file"; - -// Formats a source file path and a line number as they would appear -// in an error message from the compiler used to compile this code. -GTEST_API_ ::std::string FormatFileLocation(const char* file, int line) { - const char* const file_name = file == NULL ? kUnknownFile : file; - - if (line < 0) { - return String::Format("%s:", file_name).c_str(); - } -#ifdef _MSC_VER - return String::Format("%s(%d):", file_name, line).c_str(); -#else - return String::Format("%s:%d:", file_name, line).c_str(); -#endif // _MSC_VER -} - -// Formats a file location for compiler-independent XML output. -// Although this function is not platform dependent, we put it next to -// FormatFileLocation in order to contrast the two functions. -// Note that FormatCompilerIndependentFileLocation() does NOT append colon -// to the file location it produces, unlike FormatFileLocation(). -GTEST_API_ ::std::string FormatCompilerIndependentFileLocation( - const char* file, int line) { - const char* const file_name = file == NULL ? kUnknownFile : file; - - if (line < 0) - return file_name; - else - return String::Format("%s:%d", file_name, line).c_str(); -} - - -GTestLog::GTestLog(GTestLogSeverity severity, const char* file, int line) - : severity_(severity) { - const char* const marker = - severity == GTEST_INFO ? "[ INFO ]" : - severity == GTEST_WARNING ? "[WARNING]" : - severity == GTEST_ERROR ? "[ ERROR ]" : "[ FATAL ]"; - GetStream() << ::std::endl << marker << " " - << FormatFileLocation(file, line).c_str() << ": "; -} - -// Flushes the buffers and, if severity is GTEST_FATAL, aborts the program. -GTestLog::~GTestLog() { - GetStream() << ::std::endl; - if (severity_ == GTEST_FATAL) { - fflush(stderr); - posix::Abort(); - } -} -// Disable Microsoft deprecation warnings for POSIX functions called from -// this class (creat, dup, dup2, and close) -#ifdef _MSC_VER -# pragma warning(push) -# pragma warning(disable: 4996) -#endif // _MSC_VER - -#if GTEST_HAS_STREAM_REDIRECTION - -// Object that captures an output stream (stdout/stderr). -class CapturedStream { - public: - // The ctor redirects the stream to a temporary file. - CapturedStream(int fd) : fd_(fd), uncaptured_fd_(dup(fd)) { - -# if GTEST_OS_WINDOWS - char temp_dir_path[MAX_PATH + 1] = { '\0' }; // NOLINT - char temp_file_path[MAX_PATH + 1] = { '\0' }; // NOLINT - - ::GetTempPathA(sizeof(temp_dir_path), temp_dir_path); - const UINT success = ::GetTempFileNameA(temp_dir_path, - "gtest_redir", - 0, // Generate unique file name. - temp_file_path); - GTEST_CHECK_(success != 0) - << "Unable to create a temporary file in " << temp_dir_path; - const int captured_fd = creat(temp_file_path, _S_IREAD | _S_IWRITE); - GTEST_CHECK_(captured_fd != -1) << "Unable to open temporary file " - << temp_file_path; - filename_ = temp_file_path; -# else - // There's no guarantee that a test has write access to the - // current directory, so we create the temporary file in the /tmp - // directory instead. - char name_template[] = "/tmp/captured_stream.XXXXXX"; - const int captured_fd = mkstemp(name_template); - filename_ = name_template; -# endif // GTEST_OS_WINDOWS - fflush(NULL); - dup2(captured_fd, fd_); - close(captured_fd); - } - - ~CapturedStream() { - remove(filename_.c_str()); - } - - String GetCapturedString() { - if (uncaptured_fd_ != -1) { - // Restores the original stream. - fflush(NULL); - dup2(uncaptured_fd_, fd_); - close(uncaptured_fd_); - uncaptured_fd_ = -1; - } - - FILE* const file = posix::FOpen(filename_.c_str(), "r"); - const String content = ReadEntireFile(file); - posix::FClose(file); - return content; - } - - private: - // Reads the entire content of a file as a String. - static String ReadEntireFile(FILE* file); - - // Returns the size (in bytes) of a file. - static size_t GetFileSize(FILE* file); - - const int fd_; // A stream to capture. - int uncaptured_fd_; - // Name of the temporary file holding the stderr output. - ::std::string filename_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(CapturedStream); -}; - -// Returns the size (in bytes) of a file. -size_t CapturedStream::GetFileSize(FILE* file) { - fseek(file, 0, SEEK_END); - return static_cast(ftell(file)); -} - -// Reads the entire content of a file as a string. -String CapturedStream::ReadEntireFile(FILE* file) { - const size_t file_size = GetFileSize(file); - char* const buffer = new char[file_size]; - - size_t bytes_last_read = 0; // # of bytes read in the last fread() - size_t bytes_read = 0; // # of bytes read so far - - fseek(file, 0, SEEK_SET); - - // Keeps reading the file until we cannot read further or the - // pre-determined file size is reached. - do { - bytes_last_read = fread(buffer+bytes_read, 1, file_size-bytes_read, file); - bytes_read += bytes_last_read; - } while (bytes_last_read > 0 && bytes_read < file_size); - - const String content(buffer, bytes_read); - delete[] buffer; - - return content; -} - -# ifdef _MSC_VER -# pragma warning(pop) -# endif // _MSC_VER - -static CapturedStream* g_captured_stderr = NULL; -static CapturedStream* g_captured_stdout = NULL; - -// Starts capturing an output stream (stdout/stderr). -void CaptureStream(int fd, const char* stream_name, CapturedStream** stream) { - if (*stream != NULL) { - GTEST_LOG_(FATAL) << "Only one " << stream_name - << " capturer can exist at a time."; - } - *stream = new CapturedStream(fd); -} - -// Stops capturing the output stream and returns the captured string. -String GetCapturedStream(CapturedStream** captured_stream) { - const String content = (*captured_stream)->GetCapturedString(); - - delete *captured_stream; - *captured_stream = NULL; - - return content; -} - -// Starts capturing stdout. -void CaptureStdout() { - CaptureStream(kStdOutFileno, "stdout", &g_captured_stdout); -} - -// Starts capturing stderr. -void CaptureStderr() { - CaptureStream(kStdErrFileno, "stderr", &g_captured_stderr); -} - -// Stops capturing stdout and returns the captured string. -String GetCapturedStdout() { return GetCapturedStream(&g_captured_stdout); } - -// Stops capturing stderr and returns the captured string. -String GetCapturedStderr() { return GetCapturedStream(&g_captured_stderr); } - -#endif // GTEST_HAS_STREAM_REDIRECTION - -#if GTEST_HAS_DEATH_TEST - -// A copy of all command line arguments. Set by InitGoogleTest(). -::std::vector g_argvs; - -// Returns the command line as a vector of strings. -const ::std::vector& GetArgvs() { return g_argvs; } - -#endif // GTEST_HAS_DEATH_TEST - -#if GTEST_OS_WINDOWS_MOBILE -namespace posix { -void Abort() { - DebugBreak(); - TerminateProcess(GetCurrentProcess(), 1); -} -} // namespace posix -#endif // GTEST_OS_WINDOWS_MOBILE - -// Returns the name of the environment variable corresponding to the -// given flag. For example, FlagToEnvVar("foo") will return -// "GTEST_FOO" in the open-source version. -static String FlagToEnvVar(const char* flag) { - const String full_flag = - (Message() << GTEST_FLAG_PREFIX_ << flag).GetString(); - - Message env_var; - for (size_t i = 0; i != full_flag.length(); i++) { - env_var << ToUpper(full_flag.c_str()[i]); - } - - return env_var.GetString(); -} - -// Parses 'str' for a 32-bit signed integer. If successful, writes -// the result to *value and returns true; otherwise leaves *value -// unchanged and returns false. -bool ParseInt32(const Message& src_text, const char* str, Int32* value) { - // Parses the environment variable as a decimal integer. - char* end = NULL; - const long long_value = strtol(str, &end, 10); // NOLINT - - // Has strtol() consumed all characters in the string? - if (*end != '\0') { - // No - an invalid character was encountered. - Message msg; - msg << "WARNING: " << src_text - << " is expected to be a 32-bit integer, but actually" - << " has value \"" << str << "\".\n"; - printf("%s", msg.GetString().c_str()); - fflush(stdout); - return false; - } - - // Is the parsed value in the range of an Int32? - const Int32 result = static_cast(long_value); - if (long_value == LONG_MAX || long_value == LONG_MIN || - // The parsed value overflows as a long. (strtol() returns - // LONG_MAX or LONG_MIN when the input overflows.) - result != long_value - // The parsed value overflows as an Int32. - ) { - Message msg; - msg << "WARNING: " << src_text - << " is expected to be a 32-bit integer, but actually" - << " has value " << str << ", which overflows.\n"; - printf("%s", msg.GetString().c_str()); - fflush(stdout); - return false; - } - - *value = result; - return true; -} - -// Reads and returns the Boolean environment variable corresponding to -// the given flag; if it's not set, returns default_value. -// -// The value is considered true iff it's not "0". -bool BoolFromGTestEnv(const char* flag, bool default_value) { - const String env_var = FlagToEnvVar(flag); - const char* const string_value = posix::GetEnv(env_var.c_str()); - return string_value == NULL ? - default_value : strcmp(string_value, "0") != 0; -} - -// Reads and returns a 32-bit integer stored in the environment -// variable corresponding to the given flag; if it isn't set or -// doesn't represent a valid 32-bit integer, returns default_value. -Int32 Int32FromGTestEnv(const char* flag, Int32 default_value) { - const String env_var = FlagToEnvVar(flag); - const char* const string_value = posix::GetEnv(env_var.c_str()); - if (string_value == NULL) { - // The environment variable is not set. - return default_value; - } - - Int32 result = default_value; - if (!ParseInt32(Message() << "Environment variable " << env_var, - string_value, &result)) { - printf("The default value %s is used.\n", - (Message() << default_value).GetString().c_str()); - fflush(stdout); - return default_value; - } - - return result; -} - -// Reads and returns the string environment variable corresponding to -// the given flag; if it's not set, returns default_value. -const char* StringFromGTestEnv(const char* flag, const char* default_value) { - const String env_var = FlagToEnvVar(flag); - const char* const value = posix::GetEnv(env_var.c_str()); - return value == NULL ? default_value : value; -} - -} // namespace internal -} // namespace testing -// Copyright 2007, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Author: wan@google.com (Zhanyong Wan) - -// Google Test - The Google C++ Testing Framework -// -// This file implements a universal value printer that can print a -// value of any type T: -// -// void ::testing::internal::UniversalPrinter::Print(value, ostream_ptr); -// -// It uses the << operator when possible, and prints the bytes in the -// object otherwise. A user can override its behavior for a class -// type Foo by defining either operator<<(::std::ostream&, const Foo&) -// or void PrintTo(const Foo&, ::std::ostream*) in the namespace that -// defines Foo. - -#include -#include -#include // NOLINT -#include - -namespace testing { - -namespace { - -using ::std::ostream; - -#if GTEST_OS_WINDOWS_MOBILE // Windows CE does not define _snprintf_s. -# define snprintf _snprintf -#elif _MSC_VER >= 1400 // VC 8.0 and later deprecate snprintf and _snprintf. -# define snprintf _snprintf_s -#elif _MSC_VER -# define snprintf _snprintf -#endif // GTEST_OS_WINDOWS_MOBILE - -// Prints a segment of bytes in the given object. -void PrintByteSegmentInObjectTo(const unsigned char* obj_bytes, size_t start, - size_t count, ostream* os) { - char text[5] = ""; - for (size_t i = 0; i != count; i++) { - const size_t j = start + i; - if (i != 0) { - // Organizes the bytes into groups of 2 for easy parsing by - // human. - if ((j % 2) == 0) - *os << ' '; - else - *os << '-'; - } - snprintf(text, sizeof(text), "%02X", obj_bytes[j]); - *os << text; - } -} - -// Prints the bytes in the given value to the given ostream. -void PrintBytesInObjectToImpl(const unsigned char* obj_bytes, size_t count, - ostream* os) { - // Tells the user how big the object is. - *os << count << "-byte object <"; - - const size_t kThreshold = 132; - const size_t kChunkSize = 64; - // If the object size is bigger than kThreshold, we'll have to omit - // some details by printing only the first and the last kChunkSize - // bytes. - // TODO(wan): let the user control the threshold using a flag. - if (count < kThreshold) { - PrintByteSegmentInObjectTo(obj_bytes, 0, count, os); - } else { - PrintByteSegmentInObjectTo(obj_bytes, 0, kChunkSize, os); - *os << " ... "; - // Rounds up to 2-byte boundary. - const size_t resume_pos = (count - kChunkSize + 1)/2*2; - PrintByteSegmentInObjectTo(obj_bytes, resume_pos, count - resume_pos, os); - } - *os << ">"; -} - -} // namespace - -namespace internal2 { - -// Delegates to PrintBytesInObjectToImpl() to print the bytes in the -// given object. The delegation simplifies the implementation, which -// uses the << operator and thus is easier done outside of the -// ::testing::internal namespace, which contains a << operator that -// sometimes conflicts with the one in STL. -void PrintBytesInObjectTo(const unsigned char* obj_bytes, size_t count, - ostream* os) { - PrintBytesInObjectToImpl(obj_bytes, count, os); -} - -} // namespace internal2 - -namespace internal { - -// Depending on the value of a char (or wchar_t), we print it in one -// of three formats: -// - as is if it's a printable ASCII (e.g. 'a', '2', ' '), -// - as a hexidecimal escape sequence (e.g. '\x7F'), or -// - as a special escape sequence (e.g. '\r', '\n'). -enum CharFormat { - kAsIs, - kHexEscape, - kSpecialEscape -}; - -// Returns true if c is a printable ASCII character. We test the -// value of c directly instead of calling isprint(), which is buggy on -// Windows Mobile. -inline bool IsPrintableAscii(wchar_t c) { - return 0x20 <= c && c <= 0x7E; -} - -// Prints a wide or narrow char c as a character literal without the -// quotes, escaping it when necessary; returns how c was formatted. -// The template argument UnsignedChar is the unsigned version of Char, -// which is the type of c. -template -static CharFormat PrintAsCharLiteralTo(Char c, ostream* os) { - switch (static_cast(c)) { - case L'\0': - *os << "\\0"; - break; - case L'\'': - *os << "\\'"; - break; - case L'\\': - *os << "\\\\"; - break; - case L'\a': - *os << "\\a"; - break; - case L'\b': - *os << "\\b"; - break; - case L'\f': - *os << "\\f"; - break; - case L'\n': - *os << "\\n"; - break; - case L'\r': - *os << "\\r"; - break; - case L'\t': - *os << "\\t"; - break; - case L'\v': - *os << "\\v"; - break; - default: - if (IsPrintableAscii(c)) { - *os << static_cast(c); - return kAsIs; - } else { - *os << String::Format("\\x%X", static_cast(c)); - return kHexEscape; - } - } - return kSpecialEscape; -} - -// Prints a char c as if it's part of a string literal, escaping it when -// necessary; returns how c was formatted. -static CharFormat PrintAsWideStringLiteralTo(wchar_t c, ostream* os) { - switch (c) { - case L'\'': - *os << "'"; - return kAsIs; - case L'"': - *os << "\\\""; - return kSpecialEscape; - default: - return PrintAsCharLiteralTo(c, os); - } -} - -// Prints a char c as if it's part of a string literal, escaping it when -// necessary; returns how c was formatted. -static CharFormat PrintAsNarrowStringLiteralTo(char c, ostream* os) { - return PrintAsWideStringLiteralTo(static_cast(c), os); -} - -// Prints a wide or narrow character c and its code. '\0' is printed -// as "'\\0'", other unprintable characters are also properly escaped -// using the standard C++ escape sequence. The template argument -// UnsignedChar is the unsigned version of Char, which is the type of c. -template -void PrintCharAndCodeTo(Char c, ostream* os) { - // First, print c as a literal in the most readable form we can find. - *os << ((sizeof(c) > 1) ? "L'" : "'"); - const CharFormat format = PrintAsCharLiteralTo(c, os); - *os << "'"; - - // To aid user debugging, we also print c's code in decimal, unless - // it's 0 (in which case c was printed as '\\0', making the code - // obvious). - if (c == 0) - return; - *os << " (" << String::Format("%d", c).c_str(); - - // For more convenience, we print c's code again in hexidecimal, - // unless c was already printed in the form '\x##' or the code is in - // [1, 9]. - if (format == kHexEscape || (1 <= c && c <= 9)) { - // Do nothing. - } else { - *os << String::Format(", 0x%X", - static_cast(c)).c_str(); - } - *os << ")"; -} - -void PrintTo(unsigned char c, ::std::ostream* os) { - PrintCharAndCodeTo(c, os); -} -void PrintTo(signed char c, ::std::ostream* os) { - PrintCharAndCodeTo(c, os); -} - -// Prints a wchar_t as a symbol if it is printable or as its internal -// code otherwise and also as its code. L'\0' is printed as "L'\\0'". -void PrintTo(wchar_t wc, ostream* os) { - PrintCharAndCodeTo(wc, os); -} - -// Prints the given array of characters to the ostream. -// The array starts at *begin, the length is len, it may include '\0' characters -// and may not be null-terminated. -static void PrintCharsAsStringTo(const char* begin, size_t len, ostream* os) { - *os << "\""; - bool is_previous_hex = false; - for (size_t index = 0; index < len; ++index) { - const char cur = begin[index]; - if (is_previous_hex && IsXDigit(cur)) { - // Previous character is of '\x..' form and this character can be - // interpreted as another hexadecimal digit in its number. Break string to - // disambiguate. - *os << "\" \""; - } - is_previous_hex = PrintAsNarrowStringLiteralTo(cur, os) == kHexEscape; - } - *os << "\""; -} - -// Prints a (const) char array of 'len' elements, starting at address 'begin'. -void UniversalPrintArray(const char* begin, size_t len, ostream* os) { - PrintCharsAsStringTo(begin, len, os); -} - -// Prints the given array of wide characters to the ostream. -// The array starts at *begin, the length is len, it may include L'\0' -// characters and may not be null-terminated. -static void PrintWideCharsAsStringTo(const wchar_t* begin, size_t len, - ostream* os) { - *os << "L\""; - bool is_previous_hex = false; - for (size_t index = 0; index < len; ++index) { - const wchar_t cur = begin[index]; - if (is_previous_hex && isascii(cur) && IsXDigit(static_cast(cur))) { - // Previous character is of '\x..' form and this character can be - // interpreted as another hexadecimal digit in its number. Break string to - // disambiguate. - *os << "\" L\""; - } - is_previous_hex = PrintAsWideStringLiteralTo(cur, os) == kHexEscape; - } - *os << "\""; -} - -// Prints the given C string to the ostream. -void PrintTo(const char* s, ostream* os) { - if (s == NULL) { - *os << "NULL"; - } else { - *os << ImplicitCast_(s) << " pointing to "; - PrintCharsAsStringTo(s, strlen(s), os); - } -} - -// MSVC compiler can be configured to define whar_t as a typedef -// of unsigned short. Defining an overload for const wchar_t* in that case -// would cause pointers to unsigned shorts be printed as wide strings, -// possibly accessing more memory than intended and causing invalid -// memory accesses. MSVC defines _NATIVE_WCHAR_T_DEFINED symbol when -// wchar_t is implemented as a native type. -#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) -// Prints the given wide C string to the ostream. -void PrintTo(const wchar_t* s, ostream* os) { - if (s == NULL) { - *os << "NULL"; - } else { - *os << ImplicitCast_(s) << " pointing to "; - PrintWideCharsAsStringTo(s, wcslen(s), os); - } -} -#endif // wchar_t is native - -// Prints a ::string object. -#if GTEST_HAS_GLOBAL_STRING -void PrintStringTo(const ::string& s, ostream* os) { - PrintCharsAsStringTo(s.data(), s.size(), os); -} -#endif // GTEST_HAS_GLOBAL_STRING - -void PrintStringTo(const ::std::string& s, ostream* os) { - PrintCharsAsStringTo(s.data(), s.size(), os); -} - -// Prints a ::wstring object. -#if GTEST_HAS_GLOBAL_WSTRING -void PrintWideStringTo(const ::wstring& s, ostream* os) { - PrintWideCharsAsStringTo(s.data(), s.size(), os); -} -#endif // GTEST_HAS_GLOBAL_WSTRING - -#if GTEST_HAS_STD_WSTRING -void PrintWideStringTo(const ::std::wstring& s, ostream* os) { - PrintWideCharsAsStringTo(s.data(), s.size(), os); -} -#endif // GTEST_HAS_STD_WSTRING - -} // namespace internal - -} // namespace testing -// Copyright 2008, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Author: mheule@google.com (Markus Heule) -// -// The Google C++ Testing Framework (Google Test) - - -// Indicates that this translation unit is part of Google Test's -// implementation. It must come before gtest-internal-inl.h is -// included, or there will be a compiler error. This trick is to -// prevent a user from accidentally including gtest-internal-inl.h in -// his code. -#define GTEST_IMPLEMENTATION_ 1 -#undef GTEST_IMPLEMENTATION_ - -namespace testing { - -using internal::GetUnitTestImpl; - -// Gets the summary of the failure message by omitting the stack trace -// in it. -internal::String TestPartResult::ExtractSummary(const char* message) { - const char* const stack_trace = strstr(message, internal::kStackTraceMarker); - return stack_trace == NULL ? internal::String(message) : - internal::String(message, stack_trace - message); -} - -// Prints a TestPartResult object. -std::ostream& operator<<(std::ostream& os, const TestPartResult& result) { - return os - << result.file_name() << ":" << result.line_number() << ": " - << (result.type() == TestPartResult::kSuccess ? "Success" : - result.type() == TestPartResult::kFatalFailure ? "Fatal failure" : - "Non-fatal failure") << ":\n" - << result.message() << std::endl; -} - -// Appends a TestPartResult to the array. -void TestPartResultArray::Append(const TestPartResult& result) { - array_.push_back(result); -} - -// Returns the TestPartResult at the given index (0-based). -const TestPartResult& TestPartResultArray::GetTestPartResult(int index) const { - if (index < 0 || index >= size()) { - printf("\nInvalid index (%d) into TestPartResultArray.\n", index); - internal::posix::Abort(); - } - - return array_[index]; -} - -// Returns the number of TestPartResult objects in the array. -int TestPartResultArray::size() const { - return static_cast(array_.size()); -} - -namespace internal { - -HasNewFatalFailureHelper::HasNewFatalFailureHelper() - : has_new_fatal_failure_(false), - original_reporter_(GetUnitTestImpl()-> - GetTestPartResultReporterForCurrentThread()) { - GetUnitTestImpl()->SetTestPartResultReporterForCurrentThread(this); -} - -HasNewFatalFailureHelper::~HasNewFatalFailureHelper() { - GetUnitTestImpl()->SetTestPartResultReporterForCurrentThread( - original_reporter_); -} - -void HasNewFatalFailureHelper::ReportTestPartResult( - const TestPartResult& result) { - if (result.fatally_failed()) - has_new_fatal_failure_ = true; - original_reporter_->ReportTestPartResult(result); -} - -} // namespace internal - -} // namespace testing -// Copyright 2008 Google Inc. -// All Rights Reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Author: wan@google.com (Zhanyong Wan) - - -namespace testing { -namespace internal { - -#if GTEST_HAS_TYPED_TEST_P - -// Skips to the first non-space char in str. Returns an empty string if str -// contains only whitespace characters. -static const char* SkipSpaces(const char* str) { - while (IsSpace(*str)) - str++; - return str; -} - -// Verifies that registered_tests match the test names in -// defined_test_names_; returns registered_tests if successful, or -// aborts the program otherwise. -const char* TypedTestCasePState::VerifyRegisteredTestNames( - const char* file, int line, const char* registered_tests) { - typedef ::std::set::const_iterator DefinedTestIter; - registered_ = true; - - // Skip initial whitespace in registered_tests since some - // preprocessors prefix stringizied literals with whitespace. - registered_tests = SkipSpaces(registered_tests); - - Message errors; - ::std::set tests; - for (const char* names = registered_tests; names != NULL; - names = SkipComma(names)) { - const String name = GetPrefixUntilComma(names); - if (tests.count(name) != 0) { - errors << "Test " << name << " is listed more than once.\n"; - continue; - } - - bool found = false; - for (DefinedTestIter it = defined_test_names_.begin(); - it != defined_test_names_.end(); - ++it) { - if (name == *it) { - found = true; - break; - } - } - - if (found) { - tests.insert(name); - } else { - errors << "No test named " << name - << " can be found in this test case.\n"; - } - } - - for (DefinedTestIter it = defined_test_names_.begin(); - it != defined_test_names_.end(); - ++it) { - if (tests.count(*it) == 0) { - errors << "You forgot to list test " << *it << ".\n"; - } - } - - const String& errors_str = errors.GetString(); - if (errors_str != "") { - fprintf(stderr, "%s %s", FormatFileLocation(file, line).c_str(), - errors_str.c_str()); - fflush(stderr); - posix::Abort(); - } - - return registered_tests; -} - -#endif // GTEST_HAS_TYPED_TEST_P - -} // namespace internal -} // namespace testing diff --git a/src/3rdparty/cpp-httplib/test/gtest/gtest.h b/src/3rdparty/cpp-httplib/test/gtest/gtest.h deleted file mode 100644 index 3143bd67..00000000 --- a/src/3rdparty/cpp-httplib/test/gtest/gtest.h +++ /dev/null @@ -1,19537 +0,0 @@ -// Copyright 2005, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Author: wan@google.com (Zhanyong Wan) -// -// The Google C++ Testing Framework (Google Test) -// -// This header file defines the public API for Google Test. It should be -// included by any test program that uses Google Test. -// -// IMPORTANT NOTE: Due to limitation of the C++ language, we have to -// leave some internal implementation details in this header file. -// They are clearly marked by comments like this: -// -// // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. -// -// Such code is NOT meant to be used by a user directly, and is subject -// to CHANGE WITHOUT NOTICE. Therefore DO NOT DEPEND ON IT in a user -// program! -// -// Acknowledgment: Google Test borrowed the idea of automatic test -// registration from Barthelemy Dagenais' (barthelemy@prologique.com) -// easyUnit framework. - -#ifndef GTEST_INCLUDE_GTEST_GTEST_H_ -#define GTEST_INCLUDE_GTEST_GTEST_H_ - -#include -#include - -// Copyright 2005, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee) -// -// The Google C++ Testing Framework (Google Test) -// -// This header file declares functions and macros used internally by -// Google Test. They are subject to change without notice. - -#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ -#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ - -// Copyright 2005, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Authors: wan@google.com (Zhanyong Wan) -// -// Low-level types and utilities for porting Google Test to various -// platforms. They are subject to change without notice. DO NOT USE -// THEM IN USER CODE. - -#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ -#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ - -// The user can define the following macros in the build script to -// control Google Test's behavior. If the user doesn't define a macro -// in this list, Google Test will define it. -// -// GTEST_HAS_CLONE - Define it to 1/0 to indicate that clone(2) -// is/isn't available. -// GTEST_HAS_EXCEPTIONS - Define it to 1/0 to indicate that exceptions -// are enabled. -// GTEST_HAS_GLOBAL_STRING - Define it to 1/0 to indicate that ::string -// is/isn't available (some systems define -// ::string, which is different to std::string). -// GTEST_HAS_GLOBAL_WSTRING - Define it to 1/0 to indicate that ::string -// is/isn't available (some systems define -// ::wstring, which is different to std::wstring). -// GTEST_HAS_POSIX_RE - Define it to 1/0 to indicate that POSIX regular -// expressions are/aren't available. -// GTEST_HAS_PTHREAD - Define it to 1/0 to indicate that -// is/isn't available. -// GTEST_HAS_RTTI - Define it to 1/0 to indicate that RTTI is/isn't -// enabled. -// GTEST_HAS_STD_WSTRING - Define it to 1/0 to indicate that -// std::wstring does/doesn't work (Google Test can -// be used where std::wstring is unavailable). -// GTEST_HAS_TR1_TUPLE - Define it to 1/0 to indicate tr1::tuple -// is/isn't available. -// GTEST_HAS_SEH - Define it to 1/0 to indicate whether the -// compiler supports Microsoft's "Structured -// Exception Handling". -// GTEST_HAS_STREAM_REDIRECTION -// - Define it to 1/0 to indicate whether the -// platform supports I/O stream redirection using -// dup() and dup2(). -// GTEST_USE_OWN_TR1_TUPLE - Define it to 1/0 to indicate whether Google -// Test's own tr1 tuple implementation should be -// used. Unused when the user sets -// GTEST_HAS_TR1_TUPLE to 0. -// GTEST_LINKED_AS_SHARED_LIBRARY -// - Define to 1 when compiling tests that use -// Google Test as a shared library (known as -// DLL on Windows). -// GTEST_CREATE_SHARED_LIBRARY -// - Define to 1 when compiling Google Test itself -// as a shared library. - -// This header defines the following utilities: -// -// Macros indicating the current platform (defined to 1 if compiled on -// the given platform; otherwise undefined): -// GTEST_OS_AIX - IBM AIX -// GTEST_OS_CYGWIN - Cygwin -// GTEST_OS_HPUX - HP-UX -// GTEST_OS_LINUX - Linux -// GTEST_OS_LINUX_ANDROID - Google Android -// GTEST_OS_MAC - Mac OS X -// GTEST_OS_NACL - Google Native Client (NaCl) -// GTEST_OS_SOLARIS - Sun Solaris -// GTEST_OS_SYMBIAN - Symbian -// GTEST_OS_WINDOWS - Windows (Desktop, MinGW, or Mobile) -// GTEST_OS_WINDOWS_DESKTOP - Windows Desktop -// GTEST_OS_WINDOWS_MINGW - MinGW -// GTEST_OS_WINDOWS_MOBILE - Windows Mobile -// GTEST_OS_ZOS - z/OS -// -// Among the platforms, Cygwin, Linux, Max OS X, and Windows have the -// most stable support. Since core members of the Google Test project -// don't have access to other platforms, support for them may be less -// stable. If you notice any problems on your platform, please notify -// googletestframework@googlegroups.com (patches for fixing them are -// even more welcome!). -// -// Note that it is possible that none of the GTEST_OS_* macros are defined. -// -// Macros indicating available Google Test features (defined to 1 if -// the corresponding feature is supported; otherwise undefined): -// GTEST_HAS_COMBINE - the Combine() function (for value-parameterized -// tests) -// GTEST_HAS_DEATH_TEST - death tests -// GTEST_HAS_PARAM_TEST - value-parameterized tests -// GTEST_HAS_TYPED_TEST - typed tests -// GTEST_HAS_TYPED_TEST_P - type-parameterized tests -// GTEST_USES_POSIX_RE - enhanced POSIX regex is used. Do not confuse with -// GTEST_HAS_POSIX_RE (see above) which users can -// define themselves. -// GTEST_USES_SIMPLE_RE - our own simple regex is used; -// the above two are mutually exclusive. -// GTEST_CAN_COMPARE_NULL - accepts untyped NULL in EXPECT_EQ(). -// -// Macros for basic C++ coding: -// GTEST_AMBIGUOUS_ELSE_BLOCKER_ - for disabling a gcc warning. -// GTEST_ATTRIBUTE_UNUSED_ - declares that a class' instances or a -// variable don't have to be used. -// GTEST_DISALLOW_ASSIGN_ - disables operator=. -// GTEST_DISALLOW_COPY_AND_ASSIGN_ - disables copy ctor and operator=. -// GTEST_MUST_USE_RESULT_ - declares that a function's result must be used. -// -// Synchronization: -// Mutex, MutexLock, ThreadLocal, GetThreadCount() -// - synchronization primitives. -// GTEST_IS_THREADSAFE - defined to 1 to indicate that the above -// synchronization primitives have real implementations -// and Google Test is thread-safe; or 0 otherwise. -// -// Template meta programming: -// is_pointer - as in TR1; needed on Symbian and IBM XL C/C++ only. -// IteratorTraits - partial implementation of std::iterator_traits, which -// is not available in libCstd when compiled with Sun C++. -// -// Smart pointers: -// scoped_ptr - as in TR2. -// -// Regular expressions: -// RE - a simple regular expression class using the POSIX -// Extended Regular Expression syntax on UNIX-like -// platforms, or a reduced regular exception syntax on -// other platforms, including Windows. -// -// Logging: -// GTEST_LOG_() - logs messages at the specified severity level. -// LogToStderr() - directs all log messages to stderr. -// FlushInfoLog() - flushes informational log messages. -// -// Stdout and stderr capturing: -// CaptureStdout() - starts capturing stdout. -// GetCapturedStdout() - stops capturing stdout and returns the captured -// string. -// CaptureStderr() - starts capturing stderr. -// GetCapturedStderr() - stops capturing stderr and returns the captured -// string. -// -// Integer types: -// TypeWithSize - maps an integer to a int type. -// Int32, UInt32, Int64, UInt64, TimeInMillis -// - integers of known sizes. -// BiggestInt - the biggest signed integer type. -// -// Command-line utilities: -// GTEST_FLAG() - references a flag. -// GTEST_DECLARE_*() - declares a flag. -// GTEST_DEFINE_*() - defines a flag. -// GetArgvs() - returns the command line as a vector of strings. -// -// Environment variable utilities: -// GetEnv() - gets the value of an environment variable. -// BoolFromGTestEnv() - parses a bool environment variable. -// Int32FromGTestEnv() - parses an Int32 environment variable. -// StringFromGTestEnv() - parses a string environment variable. - -#include // for isspace, etc -#include // for ptrdiff_t -#include -#include -#include -#ifndef _WIN32_WCE -# include -# include -#endif // !_WIN32_WCE - -#include // NOLINT -#include // NOLINT -#include // NOLINT - -#define GTEST_DEV_EMAIL_ "googletestframework@@googlegroups.com" -#define GTEST_FLAG_PREFIX_ "gtest_" -#define GTEST_FLAG_PREFIX_DASH_ "gtest-" -#define GTEST_FLAG_PREFIX_UPPER_ "GTEST_" -#define GTEST_NAME_ "Google Test" -#define GTEST_PROJECT_URL_ "http://code.google.com/p/googletest/" - -// Determines the version of gcc that is used to compile this. -#ifdef __GNUC__ -// 40302 means version 4.3.2. -# define GTEST_GCC_VER_ \ - (__GNUC__*10000 + __GNUC_MINOR__*100 + __GNUC_PATCHLEVEL__) -#endif // __GNUC__ - -// Determines the platform on which Google Test is compiled. -#ifdef __CYGWIN__ -# define GTEST_OS_CYGWIN 1 -#elif defined __SYMBIAN32__ -# define GTEST_OS_SYMBIAN 1 -#elif defined _WIN32 -# define GTEST_OS_WINDOWS 1 -# ifdef _WIN32_WCE -# define GTEST_OS_WINDOWS_MOBILE 1 -# elif defined(__MINGW__) || defined(__MINGW32__) -# define GTEST_OS_WINDOWS_MINGW 1 -# else -# define GTEST_OS_WINDOWS_DESKTOP 1 -# endif // _WIN32_WCE -#elif defined __APPLE__ -# define GTEST_OS_MAC 1 -#elif defined __linux__ -# define GTEST_OS_LINUX 1 -# ifdef ANDROID -# define GTEST_OS_LINUX_ANDROID 1 -# endif // ANDROID -#elif defined __MVS__ -# define GTEST_OS_ZOS 1 -#elif defined(__sun) && defined(__SVR4) -# define GTEST_OS_SOLARIS 1 -#elif defined(_AIX) -# define GTEST_OS_AIX 1 -#elif defined(__hpux) -# define GTEST_OS_HPUX 1 -#elif defined __native_client__ -# define GTEST_OS_NACL 1 -#endif // __CYGWIN__ - -// Brings in definitions for functions used in the testing::internal::posix -// namespace (read, write, close, chdir, isatty, stat). We do not currently -// use them on Windows Mobile. -#if !GTEST_OS_WINDOWS -// This assumes that non-Windows OSes provide unistd.h. For OSes where this -// is not the case, we need to include headers that provide the functions -// mentioned above. -# include -# if !GTEST_OS_NACL -// TODO(vladl@google.com): Remove this condition when Native Client SDK adds -// strings.h (tracked in -// http://code.google.com/p/nativeclient/issues/detail?id=1175). -# include // Native Client doesn't provide strings.h. -# endif -#elif !GTEST_OS_WINDOWS_MOBILE -# include -# include -#endif - -// Defines this to true iff Google Test can use POSIX regular expressions. -#ifndef GTEST_HAS_POSIX_RE -# define GTEST_HAS_POSIX_RE (!GTEST_OS_WINDOWS) -#endif - -#if GTEST_HAS_POSIX_RE - -// On some platforms, needs someone to define size_t, and -// won't compile otherwise. We can #include it here as we already -// included , which is guaranteed to define size_t through -// . -# include // NOLINT - -# define GTEST_USES_POSIX_RE 1 - -#elif GTEST_OS_WINDOWS - -// is not available on Windows. Use our own simple regex -// implementation instead. -# define GTEST_USES_SIMPLE_RE 1 - -#else - -// may not be available on this platform. Use our own -// simple regex implementation instead. -# define GTEST_USES_SIMPLE_RE 1 - -#endif // GTEST_HAS_POSIX_RE - -#ifndef GTEST_HAS_EXCEPTIONS -// The user didn't tell us whether exceptions are enabled, so we need -// to figure it out. -# if defined(_MSC_VER) || defined(__BORLANDC__) -// MSVC's and C++Builder's implementations of the STL use the _HAS_EXCEPTIONS -// macro to enable exceptions, so we'll do the same. -// Assumes that exceptions are enabled by default. -# ifndef _HAS_EXCEPTIONS -# define _HAS_EXCEPTIONS 1 -# endif // _HAS_EXCEPTIONS -# define GTEST_HAS_EXCEPTIONS _HAS_EXCEPTIONS -# elif defined(__GNUC__) && __EXCEPTIONS -// gcc defines __EXCEPTIONS to 1 iff exceptions are enabled. -# define GTEST_HAS_EXCEPTIONS 1 -# elif defined(__SUNPRO_CC) -// Sun Pro CC supports exceptions. However, there is no compile-time way of -// detecting whether they are enabled or not. Therefore, we assume that -// they are enabled unless the user tells us otherwise. -# define GTEST_HAS_EXCEPTIONS 1 -# elif defined(__IBMCPP__) && __EXCEPTIONS -// xlC defines __EXCEPTIONS to 1 iff exceptions are enabled. -# define GTEST_HAS_EXCEPTIONS 1 -# elif defined(__HP_aCC) -// Exception handling is in effect by default in HP aCC compiler. It has to -// be turned of by +noeh compiler option if desired. -# define GTEST_HAS_EXCEPTIONS 1 -# else -// For other compilers, we assume exceptions are disabled to be -// conservative. -# define GTEST_HAS_EXCEPTIONS 0 -# endif // defined(_MSC_VER) || defined(__BORLANDC__) -#endif // GTEST_HAS_EXCEPTIONS - -#if !defined(GTEST_HAS_STD_STRING) -// Even though we don't use this macro any longer, we keep it in case -// some clients still depend on it. -# define GTEST_HAS_STD_STRING 1 -#elif !GTEST_HAS_STD_STRING -// The user told us that ::std::string isn't available. -# error "Google Test cannot be used where ::std::string isn't available." -#endif // !defined(GTEST_HAS_STD_STRING) - -#ifndef GTEST_HAS_GLOBAL_STRING -// The user didn't tell us whether ::string is available, so we need -// to figure it out. - -# define GTEST_HAS_GLOBAL_STRING 0 - -#endif // GTEST_HAS_GLOBAL_STRING - -#ifndef GTEST_HAS_STD_WSTRING -// The user didn't tell us whether ::std::wstring is available, so we need -// to figure it out. -// TODO(wan@google.com): uses autoconf to detect whether ::std::wstring -// is available. - -// Cygwin 1.7 and below doesn't support ::std::wstring. -// Solaris' libc++ doesn't support it either. Android has -// no support for it at least as recent as Froyo (2.2). -# define GTEST_HAS_STD_WSTRING \ - (!(GTEST_OS_LINUX_ANDROID || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS)) - -#endif // GTEST_HAS_STD_WSTRING - -#ifndef GTEST_HAS_GLOBAL_WSTRING -// The user didn't tell us whether ::wstring is available, so we need -// to figure it out. -# define GTEST_HAS_GLOBAL_WSTRING \ - (GTEST_HAS_STD_WSTRING && GTEST_HAS_GLOBAL_STRING) -#endif // GTEST_HAS_GLOBAL_WSTRING - -// Determines whether RTTI is available. -#ifndef GTEST_HAS_RTTI -// The user didn't tell us whether RTTI is enabled, so we need to -// figure it out. - -# ifdef _MSC_VER - -# ifdef _CPPRTTI // MSVC defines this macro iff RTTI is enabled. -# define GTEST_HAS_RTTI 1 -# else -# define GTEST_HAS_RTTI 0 -# endif - -// Starting with version 4.3.2, gcc defines __GXX_RTTI iff RTTI is enabled. -# elif defined(__GNUC__) && (GTEST_GCC_VER_ >= 40302) - -# ifdef __GXX_RTTI -# define GTEST_HAS_RTTI 1 -# else -# define GTEST_HAS_RTTI 0 -# endif // __GXX_RTTI - -// Starting with version 9.0 IBM Visual Age defines __RTTI_ALL__ to 1 if -// both the typeid and dynamic_cast features are present. -# elif defined(__IBMCPP__) && (__IBMCPP__ >= 900) - -# ifdef __RTTI_ALL__ -# define GTEST_HAS_RTTI 1 -# else -# define GTEST_HAS_RTTI 0 -# endif - -# else - -// For all other compilers, we assume RTTI is enabled. -# define GTEST_HAS_RTTI 1 - -# endif // _MSC_VER - -#endif // GTEST_HAS_RTTI - -// It's this header's responsibility to #include when RTTI -// is enabled. -#if GTEST_HAS_RTTI -# include -#endif - -// Determines whether Google Test can use the pthreads library. -#ifndef GTEST_HAS_PTHREAD -// The user didn't tell us explicitly, so we assume pthreads support is -// available on Linux and Mac. -// -// To disable threading support in Google Test, add -DGTEST_HAS_PTHREAD=0 -// to your compiler flags. -# define GTEST_HAS_PTHREAD (GTEST_OS_LINUX || GTEST_OS_MAC || GTEST_OS_HPUX) -#endif // GTEST_HAS_PTHREAD - -#if GTEST_HAS_PTHREAD -// gtest-port.h guarantees to #include when GTEST_HAS_PTHREAD is -// true. -# include // NOLINT - -// For timespec and nanosleep, used below. -# include // NOLINT -#endif - -// Determines whether Google Test can use tr1/tuple. You can define -// this macro to 0 to prevent Google Test from using tuple (any -// feature depending on tuple with be disabled in this mode). -#ifndef GTEST_HAS_TR1_TUPLE -// The user didn't tell us not to do it, so we assume it's OK. -# define GTEST_HAS_TR1_TUPLE 1 -#endif // GTEST_HAS_TR1_TUPLE - -// Determines whether Google Test's own tr1 tuple implementation -// should be used. -#ifndef GTEST_USE_OWN_TR1_TUPLE -// The user didn't tell us, so we need to figure it out. - -// We use our own TR1 tuple if we aren't sure the user has an -// implementation of it already. At this time, GCC 4.0.0+ and MSVC -// 2010 are the only mainstream compilers that come with a TR1 tuple -// implementation. NVIDIA's CUDA NVCC compiler pretends to be GCC by -// defining __GNUC__ and friends, but cannot compile GCC's tuple -// implementation. MSVC 2008 (9.0) provides TR1 tuple in a 323 MB -// Feature Pack download, which we cannot assume the user has. -# if (defined(__GNUC__) && !defined(__CUDACC__) && (GTEST_GCC_VER_ >= 40000)) \ - || _MSC_VER >= 1600 -# define GTEST_USE_OWN_TR1_TUPLE 0 -# else -# define GTEST_USE_OWN_TR1_TUPLE 1 -# endif - -#endif // GTEST_USE_OWN_TR1_TUPLE - -// To avoid conditional compilation everywhere, we make it -// gtest-port.h's responsibility to #include the header implementing -// tr1/tuple. -#if GTEST_HAS_TR1_TUPLE - -# if GTEST_USE_OWN_TR1_TUPLE -// This file was GENERATED by a script. DO NOT EDIT BY HAND!!! - -// Copyright 2009 Google Inc. -// All Rights Reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Author: wan@google.com (Zhanyong Wan) - -// Implements a subset of TR1 tuple needed by Google Test and Google Mock. - -#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_ -#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_ - -#include // For ::std::pair. - -// The compiler used in Symbian has a bug that prevents us from declaring the -// tuple template as a friend (it complains that tuple is redefined). This -// hack bypasses the bug by declaring the members that should otherwise be -// private as public. -// Sun Studio versions < 12 also have the above bug. -#if defined(__SYMBIAN32__) || (defined(__SUNPRO_CC) && __SUNPRO_CC < 0x590) -# define GTEST_DECLARE_TUPLE_AS_FRIEND_ public: -#else -# define GTEST_DECLARE_TUPLE_AS_FRIEND_ \ - template friend class tuple; \ - private: -#endif - -// GTEST_n_TUPLE_(T) is the type of an n-tuple. -#define GTEST_0_TUPLE_(T) tuple<> -#define GTEST_1_TUPLE_(T) tuple -#define GTEST_2_TUPLE_(T) tuple -#define GTEST_3_TUPLE_(T) tuple -#define GTEST_4_TUPLE_(T) tuple -#define GTEST_5_TUPLE_(T) tuple -#define GTEST_6_TUPLE_(T) tuple -#define GTEST_7_TUPLE_(T) tuple -#define GTEST_8_TUPLE_(T) tuple -#define GTEST_9_TUPLE_(T) tuple -#define GTEST_10_TUPLE_(T) tuple - -// GTEST_n_TYPENAMES_(T) declares a list of n typenames. -#define GTEST_0_TYPENAMES_(T) -#define GTEST_1_TYPENAMES_(T) typename T##0 -#define GTEST_2_TYPENAMES_(T) typename T##0, typename T##1 -#define GTEST_3_TYPENAMES_(T) typename T##0, typename T##1, typename T##2 -#define GTEST_4_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ - typename T##3 -#define GTEST_5_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ - typename T##3, typename T##4 -#define GTEST_6_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ - typename T##3, typename T##4, typename T##5 -#define GTEST_7_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ - typename T##3, typename T##4, typename T##5, typename T##6 -#define GTEST_8_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ - typename T##3, typename T##4, typename T##5, typename T##6, typename T##7 -#define GTEST_9_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ - typename T##3, typename T##4, typename T##5, typename T##6, \ - typename T##7, typename T##8 -#define GTEST_10_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ - typename T##3, typename T##4, typename T##5, typename T##6, \ - typename T##7, typename T##8, typename T##9 - -// In theory, defining stuff in the ::std namespace is undefined -// behavior. We can do this as we are playing the role of a standard -// library vendor. -namespace std { -namespace tr1 { - -template -class tuple; - -// Anything in namespace gtest_internal is Google Test's INTERNAL -// IMPLEMENTATION DETAIL and MUST NOT BE USED DIRECTLY in user code. -namespace gtest_internal { - -// ByRef::type is T if T is a reference; otherwise it's const T&. -template -struct ByRef { typedef const T& type; }; // NOLINT -template -struct ByRef { typedef T& type; }; // NOLINT - -// A handy wrapper for ByRef. -#define GTEST_BY_REF_(T) typename ::std::tr1::gtest_internal::ByRef::type - -// AddRef::type is T if T is a reference; otherwise it's T&. This -// is the same as tr1::add_reference::type. -template -struct AddRef { typedef T& type; }; // NOLINT -template -struct AddRef { typedef T& type; }; // NOLINT - -// A handy wrapper for AddRef. -#define GTEST_ADD_REF_(T) typename ::std::tr1::gtest_internal::AddRef::type - -// A helper for implementing get(). -template class Get; - -// A helper for implementing tuple_element. kIndexValid is true -// iff k < the number of fields in tuple type T. -template -struct TupleElement; - -template -struct TupleElement { typedef T0 type; }; - -template -struct TupleElement { typedef T1 type; }; - -template -struct TupleElement { typedef T2 type; }; - -template -struct TupleElement { typedef T3 type; }; - -template -struct TupleElement { typedef T4 type; }; - -template -struct TupleElement { typedef T5 type; }; - -template -struct TupleElement { typedef T6 type; }; - -template -struct TupleElement { typedef T7 type; }; - -template -struct TupleElement { typedef T8 type; }; - -template -struct TupleElement { typedef T9 type; }; - -} // namespace gtest_internal - -template <> -class tuple<> { - public: - tuple() {} - tuple(const tuple& /* t */) {} - tuple& operator=(const tuple& /* t */) { return *this; } -}; - -template -class GTEST_1_TUPLE_(T) { - public: - template friend class gtest_internal::Get; - - tuple() : f0_() {} - - explicit tuple(GTEST_BY_REF_(T0) f0) : f0_(f0) {} - - tuple(const tuple& t) : f0_(t.f0_) {} - - template - tuple(const GTEST_1_TUPLE_(U)& t) : f0_(t.f0_) {} - - tuple& operator=(const tuple& t) { return CopyFrom(t); } - - template - tuple& operator=(const GTEST_1_TUPLE_(U)& t) { - return CopyFrom(t); - } - - GTEST_DECLARE_TUPLE_AS_FRIEND_ - - template - tuple& CopyFrom(const GTEST_1_TUPLE_(U)& t) { - f0_ = t.f0_; - return *this; - } - - T0 f0_; -}; - -template -class GTEST_2_TUPLE_(T) { - public: - template friend class gtest_internal::Get; - - tuple() : f0_(), f1_() {} - - explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1) : f0_(f0), - f1_(f1) {} - - tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_) {} - - template - tuple(const GTEST_2_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_) {} - template - tuple(const ::std::pair& p) : f0_(p.first), f1_(p.second) {} - - tuple& operator=(const tuple& t) { return CopyFrom(t); } - - template - tuple& operator=(const GTEST_2_TUPLE_(U)& t) { - return CopyFrom(t); - } - template - tuple& operator=(const ::std::pair& p) { - f0_ = p.first; - f1_ = p.second; - return *this; - } - - GTEST_DECLARE_TUPLE_AS_FRIEND_ - - template - tuple& CopyFrom(const GTEST_2_TUPLE_(U)& t) { - f0_ = t.f0_; - f1_ = t.f1_; - return *this; - } - - T0 f0_; - T1 f1_; -}; - -template -class GTEST_3_TUPLE_(T) { - public: - template friend class gtest_internal::Get; - - tuple() : f0_(), f1_(), f2_() {} - - explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, - GTEST_BY_REF_(T2) f2) : f0_(f0), f1_(f1), f2_(f2) {} - - tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_) {} - - template - tuple(const GTEST_3_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_) {} - - tuple& operator=(const tuple& t) { return CopyFrom(t); } - - template - tuple& operator=(const GTEST_3_TUPLE_(U)& t) { - return CopyFrom(t); - } - - GTEST_DECLARE_TUPLE_AS_FRIEND_ - - template - tuple& CopyFrom(const GTEST_3_TUPLE_(U)& t) { - f0_ = t.f0_; - f1_ = t.f1_; - f2_ = t.f2_; - return *this; - } - - T0 f0_; - T1 f1_; - T2 f2_; -}; - -template -class GTEST_4_TUPLE_(T) { - public: - template friend class gtest_internal::Get; - - tuple() : f0_(), f1_(), f2_(), f3_() {} - - explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, - GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3) : f0_(f0), f1_(f1), f2_(f2), - f3_(f3) {} - - tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_) {} - - template - tuple(const GTEST_4_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), - f3_(t.f3_) {} - - tuple& operator=(const tuple& t) { return CopyFrom(t); } - - template - tuple& operator=(const GTEST_4_TUPLE_(U)& t) { - return CopyFrom(t); - } - - GTEST_DECLARE_TUPLE_AS_FRIEND_ - - template - tuple& CopyFrom(const GTEST_4_TUPLE_(U)& t) { - f0_ = t.f0_; - f1_ = t.f1_; - f2_ = t.f2_; - f3_ = t.f3_; - return *this; - } - - T0 f0_; - T1 f1_; - T2 f2_; - T3 f3_; -}; - -template -class GTEST_5_TUPLE_(T) { - public: - template friend class gtest_internal::Get; - - tuple() : f0_(), f1_(), f2_(), f3_(), f4_() {} - - explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, - GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, - GTEST_BY_REF_(T4) f4) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4) {} - - tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_), - f4_(t.f4_) {} - - template - tuple(const GTEST_5_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), - f3_(t.f3_), f4_(t.f4_) {} - - tuple& operator=(const tuple& t) { return CopyFrom(t); } - - template - tuple& operator=(const GTEST_5_TUPLE_(U)& t) { - return CopyFrom(t); - } - - GTEST_DECLARE_TUPLE_AS_FRIEND_ - - template - tuple& CopyFrom(const GTEST_5_TUPLE_(U)& t) { - f0_ = t.f0_; - f1_ = t.f1_; - f2_ = t.f2_; - f3_ = t.f3_; - f4_ = t.f4_; - return *this; - } - - T0 f0_; - T1 f1_; - T2 f2_; - T3 f3_; - T4 f4_; -}; - -template -class GTEST_6_TUPLE_(T) { - public: - template friend class gtest_internal::Get; - - tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_() {} - - explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, - GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4, - GTEST_BY_REF_(T5) f5) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4), - f5_(f5) {} - - tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_), - f4_(t.f4_), f5_(t.f5_) {} - - template - tuple(const GTEST_6_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), - f3_(t.f3_), f4_(t.f4_), f5_(t.f5_) {} - - tuple& operator=(const tuple& t) { return CopyFrom(t); } - - template - tuple& operator=(const GTEST_6_TUPLE_(U)& t) { - return CopyFrom(t); - } - - GTEST_DECLARE_TUPLE_AS_FRIEND_ - - template - tuple& CopyFrom(const GTEST_6_TUPLE_(U)& t) { - f0_ = t.f0_; - f1_ = t.f1_; - f2_ = t.f2_; - f3_ = t.f3_; - f4_ = t.f4_; - f5_ = t.f5_; - return *this; - } - - T0 f0_; - T1 f1_; - T2 f2_; - T3 f3_; - T4 f4_; - T5 f5_; -}; - -template -class GTEST_7_TUPLE_(T) { - public: - template friend class gtest_internal::Get; - - tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_() {} - - explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, - GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4, - GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6) : f0_(f0), f1_(f1), f2_(f2), - f3_(f3), f4_(f4), f5_(f5), f6_(f6) {} - - tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_), - f4_(t.f4_), f5_(t.f5_), f6_(t.f6_) {} - - template - tuple(const GTEST_7_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), - f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_) {} - - tuple& operator=(const tuple& t) { return CopyFrom(t); } - - template - tuple& operator=(const GTEST_7_TUPLE_(U)& t) { - return CopyFrom(t); - } - - GTEST_DECLARE_TUPLE_AS_FRIEND_ - - template - tuple& CopyFrom(const GTEST_7_TUPLE_(U)& t) { - f0_ = t.f0_; - f1_ = t.f1_; - f2_ = t.f2_; - f3_ = t.f3_; - f4_ = t.f4_; - f5_ = t.f5_; - f6_ = t.f6_; - return *this; - } - - T0 f0_; - T1 f1_; - T2 f2_; - T3 f3_; - T4 f4_; - T5 f5_; - T6 f6_; -}; - -template -class GTEST_8_TUPLE_(T) { - public: - template friend class gtest_internal::Get; - - tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_(), f7_() {} - - explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, - GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4, - GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6, - GTEST_BY_REF_(T7) f7) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4), - f5_(f5), f6_(f6), f7_(f7) {} - - tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_), - f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_) {} - - template - tuple(const GTEST_8_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), - f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_) {} - - tuple& operator=(const tuple& t) { return CopyFrom(t); } - - template - tuple& operator=(const GTEST_8_TUPLE_(U)& t) { - return CopyFrom(t); - } - - GTEST_DECLARE_TUPLE_AS_FRIEND_ - - template - tuple& CopyFrom(const GTEST_8_TUPLE_(U)& t) { - f0_ = t.f0_; - f1_ = t.f1_; - f2_ = t.f2_; - f3_ = t.f3_; - f4_ = t.f4_; - f5_ = t.f5_; - f6_ = t.f6_; - f7_ = t.f7_; - return *this; - } - - T0 f0_; - T1 f1_; - T2 f2_; - T3 f3_; - T4 f4_; - T5 f5_; - T6 f6_; - T7 f7_; -}; - -template -class GTEST_9_TUPLE_(T) { - public: - template friend class gtest_internal::Get; - - tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_(), f7_(), f8_() {} - - explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, - GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4, - GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6, GTEST_BY_REF_(T7) f7, - GTEST_BY_REF_(T8) f8) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4), - f5_(f5), f6_(f6), f7_(f7), f8_(f8) {} - - tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_), - f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_) {} - - template - tuple(const GTEST_9_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), - f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_) {} - - tuple& operator=(const tuple& t) { return CopyFrom(t); } - - template - tuple& operator=(const GTEST_9_TUPLE_(U)& t) { - return CopyFrom(t); - } - - GTEST_DECLARE_TUPLE_AS_FRIEND_ - - template - tuple& CopyFrom(const GTEST_9_TUPLE_(U)& t) { - f0_ = t.f0_; - f1_ = t.f1_; - f2_ = t.f2_; - f3_ = t.f3_; - f4_ = t.f4_; - f5_ = t.f5_; - f6_ = t.f6_; - f7_ = t.f7_; - f8_ = t.f8_; - return *this; - } - - T0 f0_; - T1 f1_; - T2 f2_; - T3 f3_; - T4 f4_; - T5 f5_; - T6 f6_; - T7 f7_; - T8 f8_; -}; - -template -class tuple { - public: - template friend class gtest_internal::Get; - - tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_(), f7_(), f8_(), - f9_() {} - - explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, - GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4, - GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6, GTEST_BY_REF_(T7) f7, - GTEST_BY_REF_(T8) f8, GTEST_BY_REF_(T9) f9) : f0_(f0), f1_(f1), f2_(f2), - f3_(f3), f4_(f4), f5_(f5), f6_(f6), f7_(f7), f8_(f8), f9_(f9) {} - - tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_), - f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_), f9_(t.f9_) {} - - template - tuple(const GTEST_10_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), - f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_), - f9_(t.f9_) {} - - tuple& operator=(const tuple& t) { return CopyFrom(t); } - - template - tuple& operator=(const GTEST_10_TUPLE_(U)& t) { - return CopyFrom(t); - } - - GTEST_DECLARE_TUPLE_AS_FRIEND_ - - template - tuple& CopyFrom(const GTEST_10_TUPLE_(U)& t) { - f0_ = t.f0_; - f1_ = t.f1_; - f2_ = t.f2_; - f3_ = t.f3_; - f4_ = t.f4_; - f5_ = t.f5_; - f6_ = t.f6_; - f7_ = t.f7_; - f8_ = t.f8_; - f9_ = t.f9_; - return *this; - } - - T0 f0_; - T1 f1_; - T2 f2_; - T3 f3_; - T4 f4_; - T5 f5_; - T6 f6_; - T7 f7_; - T8 f8_; - T9 f9_; -}; - -// 6.1.3.2 Tuple creation functions. - -// Known limitations: we don't support passing an -// std::tr1::reference_wrapper to make_tuple(). And we don't -// implement tie(). - -inline tuple<> make_tuple() { return tuple<>(); } - -template -inline GTEST_1_TUPLE_(T) make_tuple(const T0& f0) { - return GTEST_1_TUPLE_(T)(f0); -} - -template -inline GTEST_2_TUPLE_(T) make_tuple(const T0& f0, const T1& f1) { - return GTEST_2_TUPLE_(T)(f0, f1); -} - -template -inline GTEST_3_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2) { - return GTEST_3_TUPLE_(T)(f0, f1, f2); -} - -template -inline GTEST_4_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, - const T3& f3) { - return GTEST_4_TUPLE_(T)(f0, f1, f2, f3); -} - -template -inline GTEST_5_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, - const T3& f3, const T4& f4) { - return GTEST_5_TUPLE_(T)(f0, f1, f2, f3, f4); -} - -template -inline GTEST_6_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, - const T3& f3, const T4& f4, const T5& f5) { - return GTEST_6_TUPLE_(T)(f0, f1, f2, f3, f4, f5); -} - -template -inline GTEST_7_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, - const T3& f3, const T4& f4, const T5& f5, const T6& f6) { - return GTEST_7_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6); -} - -template -inline GTEST_8_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, - const T3& f3, const T4& f4, const T5& f5, const T6& f6, const T7& f7) { - return GTEST_8_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6, f7); -} - -template -inline GTEST_9_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, - const T3& f3, const T4& f4, const T5& f5, const T6& f6, const T7& f7, - const T8& f8) { - return GTEST_9_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6, f7, f8); -} - -template -inline GTEST_10_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, - const T3& f3, const T4& f4, const T5& f5, const T6& f6, const T7& f7, - const T8& f8, const T9& f9) { - return GTEST_10_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6, f7, f8, f9); -} - -// 6.1.3.3 Tuple helper classes. - -template struct tuple_size; - -template -struct tuple_size { static const int value = 0; }; - -template -struct tuple_size { static const int value = 1; }; - -template -struct tuple_size { static const int value = 2; }; - -template -struct tuple_size { static const int value = 3; }; - -template -struct tuple_size { static const int value = 4; }; - -template -struct tuple_size { static const int value = 5; }; - -template -struct tuple_size { static const int value = 6; }; - -template -struct tuple_size { static const int value = 7; }; - -template -struct tuple_size { static const int value = 8; }; - -template -struct tuple_size { static const int value = 9; }; - -template -struct tuple_size { static const int value = 10; }; - -template -struct tuple_element { - typedef typename gtest_internal::TupleElement< - k < (tuple_size::value), k, Tuple>::type type; -}; - -#define GTEST_TUPLE_ELEMENT_(k, Tuple) typename tuple_element::type - -// 6.1.3.4 Element access. - -namespace gtest_internal { - -template <> -class Get<0> { - public: - template - static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(0, Tuple)) - Field(Tuple& t) { return t.f0_; } // NOLINT - - template - static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(0, Tuple)) - ConstField(const Tuple& t) { return t.f0_; } -}; - -template <> -class Get<1> { - public: - template - static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(1, Tuple)) - Field(Tuple& t) { return t.f1_; } // NOLINT - - template - static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(1, Tuple)) - ConstField(const Tuple& t) { return t.f1_; } -}; - -template <> -class Get<2> { - public: - template - static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(2, Tuple)) - Field(Tuple& t) { return t.f2_; } // NOLINT - - template - static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(2, Tuple)) - ConstField(const Tuple& t) { return t.f2_; } -}; - -template <> -class Get<3> { - public: - template - static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(3, Tuple)) - Field(Tuple& t) { return t.f3_; } // NOLINT - - template - static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(3, Tuple)) - ConstField(const Tuple& t) { return t.f3_; } -}; - -template <> -class Get<4> { - public: - template - static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(4, Tuple)) - Field(Tuple& t) { return t.f4_; } // NOLINT - - template - static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(4, Tuple)) - ConstField(const Tuple& t) { return t.f4_; } -}; - -template <> -class Get<5> { - public: - template - static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(5, Tuple)) - Field(Tuple& t) { return t.f5_; } // NOLINT - - template - static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(5, Tuple)) - ConstField(const Tuple& t) { return t.f5_; } -}; - -template <> -class Get<6> { - public: - template - static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(6, Tuple)) - Field(Tuple& t) { return t.f6_; } // NOLINT - - template - static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(6, Tuple)) - ConstField(const Tuple& t) { return t.f6_; } -}; - -template <> -class Get<7> { - public: - template - static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(7, Tuple)) - Field(Tuple& t) { return t.f7_; } // NOLINT - - template - static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(7, Tuple)) - ConstField(const Tuple& t) { return t.f7_; } -}; - -template <> -class Get<8> { - public: - template - static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(8, Tuple)) - Field(Tuple& t) { return t.f8_; } // NOLINT - - template - static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(8, Tuple)) - ConstField(const Tuple& t) { return t.f8_; } -}; - -template <> -class Get<9> { - public: - template - static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(9, Tuple)) - Field(Tuple& t) { return t.f9_; } // NOLINT - - template - static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(9, Tuple)) - ConstField(const Tuple& t) { return t.f9_; } -}; - -} // namespace gtest_internal - -template -GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(k, GTEST_10_TUPLE_(T))) -get(GTEST_10_TUPLE_(T)& t) { - return gtest_internal::Get::Field(t); -} - -template -GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(k, GTEST_10_TUPLE_(T))) -get(const GTEST_10_TUPLE_(T)& t) { - return gtest_internal::Get::ConstField(t); -} - -// 6.1.3.5 Relational operators - -// We only implement == and !=, as we don't have a need for the rest yet. - -namespace gtest_internal { - -// SameSizeTuplePrefixComparator::Eq(t1, t2) returns true if the -// first k fields of t1 equals the first k fields of t2. -// SameSizeTuplePrefixComparator(k1, k2) would be a compiler error if -// k1 != k2. -template -struct SameSizeTuplePrefixComparator; - -template <> -struct SameSizeTuplePrefixComparator<0, 0> { - template - static bool Eq(const Tuple1& /* t1 */, const Tuple2& /* t2 */) { - return true; - } -}; - -template -struct SameSizeTuplePrefixComparator { - template - static bool Eq(const Tuple1& t1, const Tuple2& t2) { - return SameSizeTuplePrefixComparator::Eq(t1, t2) && - ::std::tr1::get(t1) == ::std::tr1::get(t2); - } -}; - -} // namespace gtest_internal - -template -inline bool operator==(const GTEST_10_TUPLE_(T)& t, - const GTEST_10_TUPLE_(U)& u) { - return gtest_internal::SameSizeTuplePrefixComparator< - tuple_size::value, - tuple_size::value>::Eq(t, u); -} - -template -inline bool operator!=(const GTEST_10_TUPLE_(T)& t, - const GTEST_10_TUPLE_(U)& u) { return !(t == u); } - -// 6.1.4 Pairs. -// Unimplemented. - -} // namespace tr1 -} // namespace std - -#undef GTEST_0_TUPLE_ -#undef GTEST_1_TUPLE_ -#undef GTEST_2_TUPLE_ -#undef GTEST_3_TUPLE_ -#undef GTEST_4_TUPLE_ -#undef GTEST_5_TUPLE_ -#undef GTEST_6_TUPLE_ -#undef GTEST_7_TUPLE_ -#undef GTEST_8_TUPLE_ -#undef GTEST_9_TUPLE_ -#undef GTEST_10_TUPLE_ - -#undef GTEST_0_TYPENAMES_ -#undef GTEST_1_TYPENAMES_ -#undef GTEST_2_TYPENAMES_ -#undef GTEST_3_TYPENAMES_ -#undef GTEST_4_TYPENAMES_ -#undef GTEST_5_TYPENAMES_ -#undef GTEST_6_TYPENAMES_ -#undef GTEST_7_TYPENAMES_ -#undef GTEST_8_TYPENAMES_ -#undef GTEST_9_TYPENAMES_ -#undef GTEST_10_TYPENAMES_ - -#undef GTEST_DECLARE_TUPLE_AS_FRIEND_ -#undef GTEST_BY_REF_ -#undef GTEST_ADD_REF_ -#undef GTEST_TUPLE_ELEMENT_ - -#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_ -# elif GTEST_OS_SYMBIAN - -// On Symbian, BOOST_HAS_TR1_TUPLE causes Boost's TR1 tuple library to -// use STLport's tuple implementation, which unfortunately doesn't -// work as the copy of STLport distributed with Symbian is incomplete. -// By making sure BOOST_HAS_TR1_TUPLE is undefined, we force Boost to -// use its own tuple implementation. -# ifdef BOOST_HAS_TR1_TUPLE -# undef BOOST_HAS_TR1_TUPLE -# endif // BOOST_HAS_TR1_TUPLE - -// This prevents , which defines -// BOOST_HAS_TR1_TUPLE, from being #included by Boost's . -# define BOOST_TR1_DETAIL_CONFIG_HPP_INCLUDED -# include - -# elif defined(__GNUC__) && (GTEST_GCC_VER_ >= 40000) -// GCC 4.0+ implements tr1/tuple in the header. This does -// not conform to the TR1 spec, which requires the header to be . - -# if !GTEST_HAS_RTTI && GTEST_GCC_VER_ < 40302 -// Until version 4.3.2, gcc has a bug that causes , -// which is #included by , to not compile when RTTI is -// disabled. _TR1_FUNCTIONAL is the header guard for -// . Hence the following #define is a hack to prevent -// from being included. -# define _TR1_FUNCTIONAL 1 -# include -# undef _TR1_FUNCTIONAL // Allows the user to #include - // if he chooses to. -# else -# include // NOLINT -# endif // !GTEST_HAS_RTTI && GTEST_GCC_VER_ < 40302 - -# else -// If the compiler is not GCC 4.0+, we assume the user is using a -// spec-conforming TR1 implementation. -# include // NOLINT -# endif // GTEST_USE_OWN_TR1_TUPLE - -#endif // GTEST_HAS_TR1_TUPLE - -// Determines whether clone(2) is supported. -// Usually it will only be available on Linux, excluding -// Linux on the Itanium architecture. -// Also see http://linux.die.net/man/2/clone. -#ifndef GTEST_HAS_CLONE -// The user didn't tell us, so we need to figure it out. - -# if GTEST_OS_LINUX && !defined(__ia64__) -# define GTEST_HAS_CLONE 1 -# else -# define GTEST_HAS_CLONE 0 -# endif // GTEST_OS_LINUX && !defined(__ia64__) - -#endif // GTEST_HAS_CLONE - -// Determines whether to support stream redirection. This is used to test -// output correctness and to implement death tests. -#ifndef GTEST_HAS_STREAM_REDIRECTION -// By default, we assume that stream redirection is supported on all -// platforms except known mobile ones. -# if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_SYMBIAN -# define GTEST_HAS_STREAM_REDIRECTION 0 -# else -# define GTEST_HAS_STREAM_REDIRECTION 1 -# endif // !GTEST_OS_WINDOWS_MOBILE && !GTEST_OS_SYMBIAN -#endif // GTEST_HAS_STREAM_REDIRECTION - -// Determines whether to support death tests. -// Google Test does not support death tests for VC 7.1 and earlier as -// abort() in a VC 7.1 application compiled as GUI in debug config -// pops up a dialog window that cannot be suppressed programmatically. -#if (GTEST_OS_LINUX || GTEST_OS_MAC || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS || \ - (GTEST_OS_WINDOWS_DESKTOP && _MSC_VER >= 1400) || \ - GTEST_OS_WINDOWS_MINGW || GTEST_OS_AIX || GTEST_OS_HPUX) -# define GTEST_HAS_DEATH_TEST 1 -# include // NOLINT -#endif - -// We don't support MSVC 7.1 with exceptions disabled now. Therefore -// all the compilers we care about are adequate for supporting -// value-parameterized tests. -#define GTEST_HAS_PARAM_TEST 1 - -// Determines whether to support type-driven tests. - -// Typed tests need and variadic macros, which GCC, VC++ 8.0, -// Sun Pro CC, IBM Visual Age, and HP aCC support. -#if defined(__GNUC__) || (_MSC_VER >= 1400) || defined(__SUNPRO_CC) || \ - defined(__IBMCPP__) || defined(__HP_aCC) -# define GTEST_HAS_TYPED_TEST 1 -# define GTEST_HAS_TYPED_TEST_P 1 -#endif - -// Determines whether to support Combine(). This only makes sense when -// value-parameterized tests are enabled. The implementation doesn't -// work on Sun Studio since it doesn't understand templated conversion -// operators. -#if GTEST_HAS_PARAM_TEST && GTEST_HAS_TR1_TUPLE && !defined(__SUNPRO_CC) -# define GTEST_HAS_COMBINE 1 -#endif - -// Determines whether the system compiler uses UTF-16 for encoding wide strings. -#define GTEST_WIDE_STRING_USES_UTF16_ \ - (GTEST_OS_WINDOWS || GTEST_OS_CYGWIN || GTEST_OS_SYMBIAN || GTEST_OS_AIX) - -// Determines whether test results can be streamed to a socket. -#if GTEST_OS_LINUX -# define GTEST_CAN_STREAM_RESULTS_ 1 -#endif - -// Defines some utility macros. - -// The GNU compiler emits a warning if nested "if" statements are followed by -// an "else" statement and braces are not used to explicitly disambiguate the -// "else" binding. This leads to problems with code like: -// -// if (gate) -// ASSERT_*(condition) << "Some message"; -// -// The "switch (0) case 0:" idiom is used to suppress this. -#ifdef __INTEL_COMPILER -# define GTEST_AMBIGUOUS_ELSE_BLOCKER_ -#else -# define GTEST_AMBIGUOUS_ELSE_BLOCKER_ switch (0) case 0: default: // NOLINT -#endif - -// Use this annotation at the end of a struct/class definition to -// prevent the compiler from optimizing away instances that are never -// used. This is useful when all interesting logic happens inside the -// c'tor and / or d'tor. Example: -// -// struct Foo { -// Foo() { ... } -// } GTEST_ATTRIBUTE_UNUSED_; -// -// Also use it after a variable or parameter declaration to tell the -// compiler the variable/parameter does not have to be used. -#if defined(__GNUC__) && !defined(COMPILER_ICC) -# define GTEST_ATTRIBUTE_UNUSED_ __attribute__ ((unused)) -#else -# define GTEST_ATTRIBUTE_UNUSED_ -#endif - -// A macro to disallow operator= -// This should be used in the private: declarations for a class. -#define GTEST_DISALLOW_ASSIGN_(type)\ - void operator=(type const &) - -// A macro to disallow copy constructor and operator= -// This should be used in the private: declarations for a class. -#define GTEST_DISALLOW_COPY_AND_ASSIGN_(type)\ - type(type const &);\ - GTEST_DISALLOW_ASSIGN_(type) - -// Tell the compiler to warn about unused return values for functions declared -// with this macro. The macro should be used on function declarations -// following the argument list: -// -// Sprocket* AllocateSprocket() GTEST_MUST_USE_RESULT_; -#if defined(__GNUC__) && (GTEST_GCC_VER_ >= 30400) && !defined(COMPILER_ICC) -# define GTEST_MUST_USE_RESULT_ __attribute__ ((warn_unused_result)) -#else -# define GTEST_MUST_USE_RESULT_ -#endif // __GNUC__ && (GTEST_GCC_VER_ >= 30400) && !COMPILER_ICC - -// Determine whether the compiler supports Microsoft's Structured Exception -// Handling. This is supported by several Windows compilers but generally -// does not exist on any other system. -#ifndef GTEST_HAS_SEH -// The user didn't tell us, so we need to figure it out. - -# if defined(_MSC_VER) || defined(__BORLANDC__) -// These two compilers are known to support SEH. -# define GTEST_HAS_SEH 1 -# else -// Assume no SEH. -# define GTEST_HAS_SEH 0 -# endif - -#endif // GTEST_HAS_SEH - -#ifdef _MSC_VER - -# if GTEST_LINKED_AS_SHARED_LIBRARY -# define GTEST_API_ __declspec(dllimport) -# elif GTEST_CREATE_SHARED_LIBRARY -# define GTEST_API_ __declspec(dllexport) -# endif - -#endif // _MSC_VER - -#ifndef GTEST_API_ -# define GTEST_API_ -#endif - -#ifdef __GNUC__ -// Ask the compiler to never inline a given function. -# define GTEST_NO_INLINE_ __attribute__((noinline)) -#else -# define GTEST_NO_INLINE_ -#endif - -namespace testing { - -class Message; - -namespace internal { - -class String; - -// The GTEST_COMPILE_ASSERT_ macro can be used to verify that a compile time -// expression is true. For example, you could use it to verify the -// size of a static array: -// -// GTEST_COMPILE_ASSERT_(ARRAYSIZE(content_type_names) == CONTENT_NUM_TYPES, -// content_type_names_incorrect_size); -// -// or to make sure a struct is smaller than a certain size: -// -// GTEST_COMPILE_ASSERT_(sizeof(foo) < 128, foo_too_large); -// -// The second argument to the macro is the name of the variable. If -// the expression is false, most compilers will issue a warning/error -// containing the name of the variable. - -template -struct CompileAssert { -}; - -#define GTEST_COMPILE_ASSERT_(expr, msg) \ - typedef ::testing::internal::CompileAssert<(bool(expr))> \ - msg[bool(expr) ? 1 : -1] - -// Implementation details of GTEST_COMPILE_ASSERT_: -// -// - GTEST_COMPILE_ASSERT_ works by defining an array type that has -1 -// elements (and thus is invalid) when the expression is false. -// -// - The simpler definition -// -// #define GTEST_COMPILE_ASSERT_(expr, msg) typedef char msg[(expr) ? 1 : -1] -// -// does not work, as gcc supports variable-length arrays whose sizes -// are determined at run-time (this is gcc's extension and not part -// of the C++ standard). As a result, gcc fails to reject the -// following code with the simple definition: -// -// int foo; -// GTEST_COMPILE_ASSERT_(foo, msg); // not supposed to compile as foo is -// // not a compile-time constant. -// -// - By using the type CompileAssert<(bool(expr))>, we ensures that -// expr is a compile-time constant. (Template arguments must be -// determined at compile-time.) -// -// - The outter parentheses in CompileAssert<(bool(expr))> are necessary -// to work around a bug in gcc 3.4.4 and 4.0.1. If we had written -// -// CompileAssert -// -// instead, these compilers will refuse to compile -// -// GTEST_COMPILE_ASSERT_(5 > 0, some_message); -// -// (They seem to think the ">" in "5 > 0" marks the end of the -// template argument list.) -// -// - The array size is (bool(expr) ? 1 : -1), instead of simply -// -// ((expr) ? 1 : -1). -// -// This is to avoid running into a bug in MS VC 7.1, which -// causes ((0.0) ? 1 : -1) to incorrectly evaluate to 1. - -// StaticAssertTypeEqHelper is used by StaticAssertTypeEq defined in gtest.h. -// -// This template is declared, but intentionally undefined. -template -struct StaticAssertTypeEqHelper; - -template -struct StaticAssertTypeEqHelper {}; - -#if GTEST_HAS_GLOBAL_STRING -typedef ::string string; -#else -typedef ::std::string string; -#endif // GTEST_HAS_GLOBAL_STRING - -#if GTEST_HAS_GLOBAL_WSTRING -typedef ::wstring wstring; -#elif GTEST_HAS_STD_WSTRING -typedef ::std::wstring wstring; -#endif // GTEST_HAS_GLOBAL_WSTRING - -// A helper for suppressing warnings on constant condition. It just -// returns 'condition'. -GTEST_API_ bool IsTrue(bool condition); - -// Defines scoped_ptr. - -// This implementation of scoped_ptr is PARTIAL - it only contains -// enough stuff to satisfy Google Test's need. -template -class scoped_ptr { - public: - typedef T element_type; - - explicit scoped_ptr(T* p = NULL) : ptr_(p) {} - ~scoped_ptr() { reset(); } - - T& operator*() const { return *ptr_; } - T* operator->() const { return ptr_; } - T* get() const { return ptr_; } - - T* release() { - T* const ptr = ptr_; - ptr_ = NULL; - return ptr; - } - - void reset(T* p = NULL) { - if (p != ptr_) { - if (IsTrue(sizeof(T) > 0)) { // Makes sure T is a complete type. - delete ptr_; - } - ptr_ = p; - } - } - private: - T* ptr_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(scoped_ptr); -}; - -// Defines RE. - -// A simple C++ wrapper for . It uses the POSIX Extended -// Regular Expression syntax. -class GTEST_API_ RE { - public: - // A copy constructor is required by the Standard to initialize object - // references from r-values. - RE(const RE& other) { Init(other.pattern()); } - - // Constructs an RE from a string. - RE(const ::std::string& regex) { Init(regex.c_str()); } // NOLINT - -#if GTEST_HAS_GLOBAL_STRING - - RE(const ::string& regex) { Init(regex.c_str()); } // NOLINT - -#endif // GTEST_HAS_GLOBAL_STRING - - RE(const char* regex) { Init(regex); } // NOLINT - ~RE(); - - // Returns the string representation of the regex. - const char* pattern() const { return pattern_; } - - // FullMatch(str, re) returns true iff regular expression re matches - // the entire str. - // PartialMatch(str, re) returns true iff regular expression re - // matches a substring of str (including str itself). - // - // TODO(wan@google.com): make FullMatch() and PartialMatch() work - // when str contains NUL characters. - static bool FullMatch(const ::std::string& str, const RE& re) { - return FullMatch(str.c_str(), re); - } - static bool PartialMatch(const ::std::string& str, const RE& re) { - return PartialMatch(str.c_str(), re); - } - -#if GTEST_HAS_GLOBAL_STRING - - static bool FullMatch(const ::string& str, const RE& re) { - return FullMatch(str.c_str(), re); - } - static bool PartialMatch(const ::string& str, const RE& re) { - return PartialMatch(str.c_str(), re); - } - -#endif // GTEST_HAS_GLOBAL_STRING - - static bool FullMatch(const char* str, const RE& re); - static bool PartialMatch(const char* str, const RE& re); - - private: - void Init(const char* regex); - - // We use a const char* instead of a string, as Google Test may be used - // where string is not available. We also do not use Google Test's own - // String type here, in order to simplify dependencies between the - // files. - const char* pattern_; - bool is_valid_; - -#if GTEST_USES_POSIX_RE - - regex_t full_regex_; // For FullMatch(). - regex_t partial_regex_; // For PartialMatch(). - -#else // GTEST_USES_SIMPLE_RE - - const char* full_pattern_; // For FullMatch(); - -#endif - - GTEST_DISALLOW_ASSIGN_(RE); -}; - -// Formats a source file path and a line number as they would appear -// in an error message from the compiler used to compile this code. -GTEST_API_ ::std::string FormatFileLocation(const char* file, int line); - -// Formats a file location for compiler-independent XML output. -// Although this function is not platform dependent, we put it next to -// FormatFileLocation in order to contrast the two functions. -GTEST_API_ ::std::string FormatCompilerIndependentFileLocation(const char* file, - int line); - -// Defines logging utilities: -// GTEST_LOG_(severity) - logs messages at the specified severity level. The -// message itself is streamed into the macro. -// LogToStderr() - directs all log messages to stderr. -// FlushInfoLog() - flushes informational log messages. - -enum GTestLogSeverity { - GTEST_INFO, - GTEST_WARNING, - GTEST_ERROR, - GTEST_FATAL -}; - -// Formats log entry severity, provides a stream object for streaming the -// log message, and terminates the message with a newline when going out of -// scope. -class GTEST_API_ GTestLog { - public: - GTestLog(GTestLogSeverity severity, const char* file, int line); - - // Flushes the buffers and, if severity is GTEST_FATAL, aborts the program. - ~GTestLog(); - - ::std::ostream& GetStream() { return ::std::cerr; } - - private: - const GTestLogSeverity severity_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(GTestLog); -}; - -#define GTEST_LOG_(severity) \ - ::testing::internal::GTestLog(::testing::internal::GTEST_##severity, \ - __FILE__, __LINE__).GetStream() - -inline void LogToStderr() {} -inline void FlushInfoLog() { fflush(NULL); } - -// INTERNAL IMPLEMENTATION - DO NOT USE. -// -// GTEST_CHECK_ is an all-mode assert. It aborts the program if the condition -// is not satisfied. -// Synopsys: -// GTEST_CHECK_(boolean_condition); -// or -// GTEST_CHECK_(boolean_condition) << "Additional message"; -// -// This checks the condition and if the condition is not satisfied -// it prints message about the condition violation, including the -// condition itself, plus additional message streamed into it, if any, -// and then it aborts the program. It aborts the program irrespective of -// whether it is built in the debug mode or not. -#define GTEST_CHECK_(condition) \ - GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ - if (::testing::internal::IsTrue(condition)) \ - ; \ - else \ - GTEST_LOG_(FATAL) << "Condition " #condition " failed. " - -// An all-mode assert to verify that the given POSIX-style function -// call returns 0 (indicating success). Known limitation: this -// doesn't expand to a balanced 'if' statement, so enclose the macro -// in {} if you need to use it as the only statement in an 'if' -// branch. -#define GTEST_CHECK_POSIX_SUCCESS_(posix_call) \ - if (const int gtest_error = (posix_call)) \ - GTEST_LOG_(FATAL) << #posix_call << "failed with error " \ - << gtest_error - -// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. -// -// Use ImplicitCast_ as a safe version of static_cast for upcasting in -// the type hierarchy (e.g. casting a Foo* to a SuperclassOfFoo* or a -// const Foo*). When you use ImplicitCast_, the compiler checks that -// the cast is safe. Such explicit ImplicitCast_s are necessary in -// surprisingly many situations where C++ demands an exact type match -// instead of an argument type convertable to a target type. -// -// The syntax for using ImplicitCast_ is the same as for static_cast: -// -// ImplicitCast_(expr) -// -// ImplicitCast_ would have been part of the C++ standard library, -// but the proposal was submitted too late. It will probably make -// its way into the language in the future. -// -// This relatively ugly name is intentional. It prevents clashes with -// similar functions users may have (e.g., implicit_cast). The internal -// namespace alone is not enough because the function can be found by ADL. -template -inline To ImplicitCast_(To x) { return x; } - -// When you upcast (that is, cast a pointer from type Foo to type -// SuperclassOfFoo), it's fine to use ImplicitCast_<>, since upcasts -// always succeed. When you downcast (that is, cast a pointer from -// type Foo to type SubclassOfFoo), static_cast<> isn't safe, because -// how do you know the pointer is really of type SubclassOfFoo? It -// could be a bare Foo, or of type DifferentSubclassOfFoo. Thus, -// when you downcast, you should use this macro. In debug mode, we -// use dynamic_cast<> to double-check the downcast is legal (we die -// if it's not). In normal mode, we do the efficient static_cast<> -// instead. Thus, it's important to test in debug mode to make sure -// the cast is legal! -// This is the only place in the code we should use dynamic_cast<>. -// In particular, you SHOULDN'T be using dynamic_cast<> in order to -// do RTTI (eg code like this: -// if (dynamic_cast(foo)) HandleASubclass1Object(foo); -// if (dynamic_cast(foo)) HandleASubclass2Object(foo); -// You should design the code some other way not to need this. -// -// This relatively ugly name is intentional. It prevents clashes with -// similar functions users may have (e.g., down_cast). The internal -// namespace alone is not enough because the function can be found by ADL. -template // use like this: DownCast_(foo); -inline To DownCast_(From* f) { // so we only accept pointers - // Ensures that To is a sub-type of From *. This test is here only - // for compile-time type checking, and has no overhead in an - // optimized build at run-time, as it will be optimized away - // completely. - if (false) { - const To to = NULL; - ::testing::internal::ImplicitCast_(to); - } - -#if GTEST_HAS_RTTI - // RTTI: debug mode only! - GTEST_CHECK_(f == NULL || dynamic_cast(f) != NULL); -#endif - return static_cast(f); -} - -// Downcasts the pointer of type Base to Derived. -// Derived must be a subclass of Base. The parameter MUST -// point to a class of type Derived, not any subclass of it. -// When RTTI is available, the function performs a runtime -// check to enforce this. -template -Derived* CheckedDowncastToActualType(Base* base) { -#if GTEST_HAS_RTTI - GTEST_CHECK_(typeid(*base) == typeid(Derived)); - return dynamic_cast(base); // NOLINT -#else - return static_cast(base); // Poor man's downcast. -#endif -} - -#if GTEST_HAS_STREAM_REDIRECTION - -// Defines the stderr capturer: -// CaptureStdout - starts capturing stdout. -// GetCapturedStdout - stops capturing stdout and returns the captured string. -// CaptureStderr - starts capturing stderr. -// GetCapturedStderr - stops capturing stderr and returns the captured string. -// -GTEST_API_ void CaptureStdout(); -GTEST_API_ String GetCapturedStdout(); -GTEST_API_ void CaptureStderr(); -GTEST_API_ String GetCapturedStderr(); - -#endif // GTEST_HAS_STREAM_REDIRECTION - - -#if GTEST_HAS_DEATH_TEST - -// A copy of all command line arguments. Set by InitGoogleTest(). -extern ::std::vector g_argvs; - -// GTEST_HAS_DEATH_TEST implies we have ::std::string. -const ::std::vector& GetArgvs(); - -#endif // GTEST_HAS_DEATH_TEST - -// Defines synchronization primitives. - -#if GTEST_HAS_PTHREAD - -// Sleeps for (roughly) n milli-seconds. This function is only for -// testing Google Test's own constructs. Don't use it in user tests, -// either directly or indirectly. -inline void SleepMilliseconds(int n) { - const timespec time = { - 0, // 0 seconds. - n * 1000L * 1000L, // And n ms. - }; - nanosleep(&time, NULL); -} - -// Allows a controller thread to pause execution of newly created -// threads until notified. Instances of this class must be created -// and destroyed in the controller thread. -// -// This class is only for testing Google Test's own constructs. Do not -// use it in user tests, either directly or indirectly. -class Notification { - public: - Notification() : notified_(false) {} - - // Notifies all threads created with this notification to start. Must - // be called from the controller thread. - void Notify() { notified_ = true; } - - // Blocks until the controller thread notifies. Must be called from a test - // thread. - void WaitForNotification() { - while(!notified_) { - SleepMilliseconds(10); - } - } - - private: - volatile bool notified_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(Notification); -}; - -// As a C-function, ThreadFuncWithCLinkage cannot be templated itself. -// Consequently, it cannot select a correct instantiation of ThreadWithParam -// in order to call its Run(). Introducing ThreadWithParamBase as a -// non-templated base class for ThreadWithParam allows us to bypass this -// problem. -class ThreadWithParamBase { - public: - virtual ~ThreadWithParamBase() {} - virtual void Run() = 0; -}; - -// pthread_create() accepts a pointer to a function type with the C linkage. -// According to the Standard (7.5/1), function types with different linkages -// are different even if they are otherwise identical. Some compilers (for -// example, SunStudio) treat them as different types. Since class methods -// cannot be defined with C-linkage we need to define a free C-function to -// pass into pthread_create(). -extern "C" inline void* ThreadFuncWithCLinkage(void* thread) { - static_cast(thread)->Run(); - return NULL; -} - -// Helper class for testing Google Test's multi-threading constructs. -// To use it, write: -// -// void ThreadFunc(int param) { /* Do things with param */ } -// Notification thread_can_start; -// ... -// // The thread_can_start parameter is optional; you can supply NULL. -// ThreadWithParam thread(&ThreadFunc, 5, &thread_can_start); -// thread_can_start.Notify(); -// -// These classes are only for testing Google Test's own constructs. Do -// not use them in user tests, either directly or indirectly. -template -class ThreadWithParam : public ThreadWithParamBase { - public: - typedef void (*UserThreadFunc)(T); - - ThreadWithParam( - UserThreadFunc func, T param, Notification* thread_can_start) - : func_(func), - param_(param), - thread_can_start_(thread_can_start), - finished_(false) { - ThreadWithParamBase* const base = this; - // The thread can be created only after all fields except thread_ - // have been initialized. - GTEST_CHECK_POSIX_SUCCESS_( - pthread_create(&thread_, 0, &ThreadFuncWithCLinkage, base)); - } - ~ThreadWithParam() { Join(); } - - void Join() { - if (!finished_) { - GTEST_CHECK_POSIX_SUCCESS_(pthread_join(thread_, 0)); - finished_ = true; - } - } - - virtual void Run() { - if (thread_can_start_ != NULL) - thread_can_start_->WaitForNotification(); - func_(param_); - } - - private: - const UserThreadFunc func_; // User-supplied thread function. - const T param_; // User-supplied parameter to the thread function. - // When non-NULL, used to block execution until the controller thread - // notifies. - Notification* const thread_can_start_; - bool finished_; // true iff we know that the thread function has finished. - pthread_t thread_; // The native thread object. - - GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadWithParam); -}; - -// MutexBase and Mutex implement mutex on pthreads-based platforms. They -// are used in conjunction with class MutexLock: -// -// Mutex mutex; -// ... -// MutexLock lock(&mutex); // Acquires the mutex and releases it at the end -// // of the current scope. -// -// MutexBase implements behavior for both statically and dynamically -// allocated mutexes. Do not use MutexBase directly. Instead, write -// the following to define a static mutex: -// -// GTEST_DEFINE_STATIC_MUTEX_(g_some_mutex); -// -// You can forward declare a static mutex like this: -// -// GTEST_DECLARE_STATIC_MUTEX_(g_some_mutex); -// -// To create a dynamic mutex, just define an object of type Mutex. -class MutexBase { - public: - // Acquires this mutex. - void Lock() { - GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_lock(&mutex_)); - owner_ = pthread_self(); - } - - // Releases this mutex. - void Unlock() { - // We don't protect writing to owner_ here, as it's the caller's - // responsibility to ensure that the current thread holds the - // mutex when this is called. - owner_ = 0; - GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_unlock(&mutex_)); - } - - // Does nothing if the current thread holds the mutex. Otherwise, crashes - // with high probability. - void AssertHeld() const { - GTEST_CHECK_(owner_ == pthread_self()) - << "The current thread is not holding the mutex @" << this; - } - - // A static mutex may be used before main() is entered. It may even - // be used before the dynamic initialization stage. Therefore we - // must be able to initialize a static mutex object at link time. - // This means MutexBase has to be a POD and its member variables - // have to be public. - public: - pthread_mutex_t mutex_; // The underlying pthread mutex. - pthread_t owner_; // The thread holding the mutex; 0 means no one holds it. -}; - -// Forward-declares a static mutex. -# define GTEST_DECLARE_STATIC_MUTEX_(mutex) \ - extern ::testing::internal::MutexBase mutex - -// Defines and statically (i.e. at link time) initializes a static mutex. -# define GTEST_DEFINE_STATIC_MUTEX_(mutex) \ - ::testing::internal::MutexBase mutex = { PTHREAD_MUTEX_INITIALIZER, 0 } - -// The Mutex class can only be used for mutexes created at runtime. It -// shares its API with MutexBase otherwise. -class Mutex : public MutexBase { - public: - Mutex() { - GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_init(&mutex_, NULL)); - owner_ = 0; - } - ~Mutex() { - GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_destroy(&mutex_)); - } - - private: - GTEST_DISALLOW_COPY_AND_ASSIGN_(Mutex); -}; - -// We cannot name this class MutexLock as the ctor declaration would -// conflict with a macro named MutexLock, which is defined on some -// platforms. Hence the typedef trick below. -class GTestMutexLock { - public: - explicit GTestMutexLock(MutexBase* mutex) - : mutex_(mutex) { mutex_->Lock(); } - - ~GTestMutexLock() { mutex_->Unlock(); } - - private: - MutexBase* const mutex_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(GTestMutexLock); -}; - -typedef GTestMutexLock MutexLock; - -// Helpers for ThreadLocal. - -// pthread_key_create() requires DeleteThreadLocalValue() to have -// C-linkage. Therefore it cannot be templatized to access -// ThreadLocal. Hence the need for class -// ThreadLocalValueHolderBase. -class ThreadLocalValueHolderBase { - public: - virtual ~ThreadLocalValueHolderBase() {} -}; - -// Called by pthread to delete thread-local data stored by -// pthread_setspecific(). -extern "C" inline void DeleteThreadLocalValue(void* value_holder) { - delete static_cast(value_holder); -} - -// Implements thread-local storage on pthreads-based systems. -// -// // Thread 1 -// ThreadLocal tl(100); // 100 is the default value for each thread. -// -// // Thread 2 -// tl.set(150); // Changes the value for thread 2 only. -// EXPECT_EQ(150, tl.get()); -// -// // Thread 1 -// EXPECT_EQ(100, tl.get()); // In thread 1, tl has the original value. -// tl.set(200); -// EXPECT_EQ(200, tl.get()); -// -// The template type argument T must have a public copy constructor. -// In addition, the default ThreadLocal constructor requires T to have -// a public default constructor. -// -// An object managed for a thread by a ThreadLocal instance is deleted -// when the thread exits. Or, if the ThreadLocal instance dies in -// that thread, when the ThreadLocal dies. It's the user's -// responsibility to ensure that all other threads using a ThreadLocal -// have exited when it dies, or the per-thread objects for those -// threads will not be deleted. -// -// Google Test only uses global ThreadLocal objects. That means they -// will die after main() has returned. Therefore, no per-thread -// object managed by Google Test will be leaked as long as all threads -// using Google Test have exited when main() returns. -template -class ThreadLocal { - public: - ThreadLocal() : key_(CreateKey()), - default_() {} - explicit ThreadLocal(const T& value) : key_(CreateKey()), - default_(value) {} - - ~ThreadLocal() { - // Destroys the managed object for the current thread, if any. - DeleteThreadLocalValue(pthread_getspecific(key_)); - - // Releases resources associated with the key. This will *not* - // delete managed objects for other threads. - GTEST_CHECK_POSIX_SUCCESS_(pthread_key_delete(key_)); - } - - T* pointer() { return GetOrCreateValue(); } - const T* pointer() const { return GetOrCreateValue(); } - const T& get() const { return *pointer(); } - void set(const T& value) { *pointer() = value; } - - private: - // Holds a value of type T. - class ValueHolder : public ThreadLocalValueHolderBase { - public: - explicit ValueHolder(const T& value) : value_(value) {} - - T* pointer() { return &value_; } - - private: - T value_; - GTEST_DISALLOW_COPY_AND_ASSIGN_(ValueHolder); - }; - - static pthread_key_t CreateKey() { - pthread_key_t key; - // When a thread exits, DeleteThreadLocalValue() will be called on - // the object managed for that thread. - GTEST_CHECK_POSIX_SUCCESS_( - pthread_key_create(&key, &DeleteThreadLocalValue)); - return key; - } - - T* GetOrCreateValue() const { - ThreadLocalValueHolderBase* const holder = - static_cast(pthread_getspecific(key_)); - if (holder != NULL) { - return CheckedDowncastToActualType(holder)->pointer(); - } - - ValueHolder* const new_holder = new ValueHolder(default_); - ThreadLocalValueHolderBase* const holder_base = new_holder; - GTEST_CHECK_POSIX_SUCCESS_(pthread_setspecific(key_, holder_base)); - return new_holder->pointer(); - } - - // A key pthreads uses for looking up per-thread values. - const pthread_key_t key_; - const T default_; // The default value for each thread. - - GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadLocal); -}; - -# define GTEST_IS_THREADSAFE 1 - -#else // GTEST_HAS_PTHREAD - -// A dummy implementation of synchronization primitives (mutex, lock, -// and thread-local variable). Necessary for compiling Google Test where -// mutex is not supported - using Google Test in multiple threads is not -// supported on such platforms. - -class Mutex { - public: - Mutex() {} - void AssertHeld() const {} -}; - -# define GTEST_DECLARE_STATIC_MUTEX_(mutex) \ - extern ::testing::internal::Mutex mutex - -# define GTEST_DEFINE_STATIC_MUTEX_(mutex) ::testing::internal::Mutex mutex - -class GTestMutexLock { - public: - explicit GTestMutexLock(Mutex*) {} // NOLINT -}; - -typedef GTestMutexLock MutexLock; - -template -class ThreadLocal { - public: - ThreadLocal() : value_() {} - explicit ThreadLocal(const T& value) : value_(value) {} - T* pointer() { return &value_; } - const T* pointer() const { return &value_; } - const T& get() const { return value_; } - void set(const T& value) { value_ = value; } - private: - T value_; -}; - -// The above synchronization primitives have dummy implementations. -// Therefore Google Test is not thread-safe. -# define GTEST_IS_THREADSAFE 0 - -#endif // GTEST_HAS_PTHREAD - -// Returns the number of threads running in the process, or 0 to indicate that -// we cannot detect it. -GTEST_API_ size_t GetThreadCount(); - -// Passing non-POD classes through ellipsis (...) crashes the ARM -// compiler and generates a warning in Sun Studio. The Nokia Symbian -// and the IBM XL C/C++ compiler try to instantiate a copy constructor -// for objects passed through ellipsis (...), failing for uncopyable -// objects. We define this to ensure that only POD is passed through -// ellipsis on these systems. -#if defined(__SYMBIAN32__) || defined(__IBMCPP__) || defined(__SUNPRO_CC) -// We lose support for NULL detection where the compiler doesn't like -// passing non-POD classes through ellipsis (...). -# define GTEST_ELLIPSIS_NEEDS_POD_ 1 -#else -# define GTEST_CAN_COMPARE_NULL 1 -#endif - -// The Nokia Symbian and IBM XL C/C++ compilers cannot decide between -// const T& and const T* in a function template. These compilers -// _can_ decide between class template specializations for T and T*, -// so a tr1::type_traits-like is_pointer works. -#if defined(__SYMBIAN32__) || defined(__IBMCPP__) -# define GTEST_NEEDS_IS_POINTER_ 1 -#endif - -template -struct bool_constant { - typedef bool_constant type; - static const bool value = bool_value; -}; -template const bool bool_constant::value; - -typedef bool_constant false_type; -typedef bool_constant true_type; - -template -struct is_pointer : public false_type {}; - -template -struct is_pointer : public true_type {}; - -template -struct IteratorTraits { - typedef typename Iterator::value_type value_type; -}; - -template -struct IteratorTraits { - typedef T value_type; -}; - -template -struct IteratorTraits { - typedef T value_type; -}; - -#if GTEST_OS_WINDOWS -# define GTEST_PATH_SEP_ "\\" -# define GTEST_HAS_ALT_PATH_SEP_ 1 -// The biggest signed integer type the compiler supports. -typedef __int64 BiggestInt; -#else -# define GTEST_PATH_SEP_ "/" -# define GTEST_HAS_ALT_PATH_SEP_ 0 -typedef long long BiggestInt; // NOLINT -#endif // GTEST_OS_WINDOWS - -// Utilities for char. - -// isspace(int ch) and friends accept an unsigned char or EOF. char -// may be signed, depending on the compiler (or compiler flags). -// Therefore we need to cast a char to unsigned char before calling -// isspace(), etc. - -inline bool IsAlpha(char ch) { - return isalpha(static_cast(ch)) != 0; -} -inline bool IsAlNum(char ch) { - return isalnum(static_cast(ch)) != 0; -} -inline bool IsDigit(char ch) { - return isdigit(static_cast(ch)) != 0; -} -inline bool IsLower(char ch) { - return islower(static_cast(ch)) != 0; -} -inline bool IsSpace(char ch) { - return isspace(static_cast(ch)) != 0; -} -inline bool IsUpper(char ch) { - return isupper(static_cast(ch)) != 0; -} -inline bool IsXDigit(char ch) { - return isxdigit(static_cast(ch)) != 0; -} - -inline char ToLower(char ch) { - return static_cast(tolower(static_cast(ch))); -} -inline char ToUpper(char ch) { - return static_cast(toupper(static_cast(ch))); -} - -// The testing::internal::posix namespace holds wrappers for common -// POSIX functions. These wrappers hide the differences between -// Windows/MSVC and POSIX systems. Since some compilers define these -// standard functions as macros, the wrapper cannot have the same name -// as the wrapped function. - -namespace posix { - -// Functions with a different name on Windows. - -#if GTEST_OS_WINDOWS - -typedef struct _stat StatStruct; - -# ifdef __BORLANDC__ -inline int IsATTY(int fd) { return isatty(fd); } -inline int StrCaseCmp(const char* s1, const char* s2) { - return stricmp(s1, s2); -} -inline char* StrDup(const char* src) { return strdup(src); } -# else // !__BORLANDC__ -# if GTEST_OS_WINDOWS_MOBILE -inline int IsATTY(int /* fd */) { return 0; } -# else -inline int IsATTY(int fd) { return _isatty(fd); } -# endif // GTEST_OS_WINDOWS_MOBILE -inline int StrCaseCmp(const char* s1, const char* s2) { - return _stricmp(s1, s2); -} -inline char* StrDup(const char* src) { return _strdup(src); } -# endif // __BORLANDC__ - -# if GTEST_OS_WINDOWS_MOBILE -inline int FileNo(FILE* file) { return reinterpret_cast(_fileno(file)); } -// Stat(), RmDir(), and IsDir() are not needed on Windows CE at this -// time and thus not defined there. -# else -inline int FileNo(FILE* file) { return _fileno(file); } -inline int Stat(const char* path, StatStruct* buf) { return _stat(path, buf); } -inline int RmDir(const char* dir) { return _rmdir(dir); } -inline bool IsDir(const StatStruct& st) { - return (_S_IFDIR & st.st_mode) != 0; -} -# endif // GTEST_OS_WINDOWS_MOBILE - -#else - -typedef struct stat StatStruct; - -inline int FileNo(FILE* file) { return fileno(file); } -inline int IsATTY(int fd) { return isatty(fd); } -inline int Stat(const char* path, StatStruct* buf) { return stat(path, buf); } -inline int StrCaseCmp(const char* s1, const char* s2) { - return strcasecmp(s1, s2); -} -inline char* StrDup(const char* src) { return strdup(src); } -inline int RmDir(const char* dir) { return rmdir(dir); } -inline bool IsDir(const StatStruct& st) { return S_ISDIR(st.st_mode); } - -#endif // GTEST_OS_WINDOWS - -// Functions deprecated by MSVC 8.0. - -#ifdef _MSC_VER -// Temporarily disable warning 4996 (deprecated function). -# pragma warning(push) -# pragma warning(disable:4996) -#endif - -inline const char* StrNCpy(char* dest, const char* src, size_t n) { - return strncpy(dest, src, n); -} - -// ChDir(), FReopen(), FDOpen(), Read(), Write(), Close(), and -// StrError() aren't needed on Windows CE at this time and thus not -// defined there. - -#if !GTEST_OS_WINDOWS_MOBILE -inline int ChDir(const char* dir) { return chdir(dir); } -#endif -inline FILE* FOpen(const char* path, const char* mode) { - return fopen(path, mode); -} -#if !GTEST_OS_WINDOWS_MOBILE -inline FILE *FReopen(const char* path, const char* mode, FILE* stream) { - return freopen(path, mode, stream); -} -inline FILE* FDOpen(int fd, const char* mode) { return fdopen(fd, mode); } -#endif -inline int FClose(FILE* fp) { return fclose(fp); } -#if !GTEST_OS_WINDOWS_MOBILE -inline int Read(int fd, void* buf, unsigned int count) { - return static_cast(read(fd, buf, count)); -} -inline int Write(int fd, const void* buf, unsigned int count) { - return static_cast(write(fd, buf, count)); -} -inline int Close(int fd) { return close(fd); } -inline const char* StrError(int errnum) { return strerror(errnum); } -#endif -inline const char* GetEnv(const char* name) { -#if GTEST_OS_WINDOWS_MOBILE - // We are on Windows CE, which has no environment variables. - return NULL; -#elif defined(__BORLANDC__) || defined(__SunOS_5_8) || defined(__SunOS_5_9) - // Environment variables which we programmatically clear will be set to the - // empty string rather than unset (NULL). Handle that case. - const char* const env = getenv(name); - return (env != NULL && env[0] != '\0') ? env : NULL; -#else - return getenv(name); -#endif -} - -#ifdef _MSC_VER -# pragma warning(pop) // Restores the warning state. -#endif - -#if GTEST_OS_WINDOWS_MOBILE -// Windows CE has no C library. The abort() function is used in -// several places in Google Test. This implementation provides a reasonable -// imitation of standard behaviour. -void Abort(); -#else -inline void Abort() { abort(); } -#endif // GTEST_OS_WINDOWS_MOBILE - -} // namespace posix - -// The maximum number a BiggestInt can represent. This definition -// works no matter BiggestInt is represented in one's complement or -// two's complement. -// -// We cannot rely on numeric_limits in STL, as __int64 and long long -// are not part of standard C++ and numeric_limits doesn't need to be -// defined for them. -const BiggestInt kMaxBiggestInt = - ~(static_cast(1) << (8*sizeof(BiggestInt) - 1)); - -// This template class serves as a compile-time function from size to -// type. It maps a size in bytes to a primitive type with that -// size. e.g. -// -// TypeWithSize<4>::UInt -// -// is typedef-ed to be unsigned int (unsigned integer made up of 4 -// bytes). -// -// Such functionality should belong to STL, but I cannot find it -// there. -// -// Google Test uses this class in the implementation of floating-point -// comparison. -// -// For now it only handles UInt (unsigned int) as that's all Google Test -// needs. Other types can be easily added in the future if need -// arises. -template -class TypeWithSize { - public: - // This prevents the user from using TypeWithSize with incorrect - // values of N. - typedef void UInt; -}; - -// The specialization for size 4. -template <> -class TypeWithSize<4> { - public: - // unsigned int has size 4 in both gcc and MSVC. - // - // As base/basictypes.h doesn't compile on Windows, we cannot use - // uint32, uint64, and etc here. - typedef int Int; - typedef unsigned int UInt; -}; - -// The specialization for size 8. -template <> -class TypeWithSize<8> { - public: - -#if GTEST_OS_WINDOWS - typedef __int64 Int; - typedef unsigned __int64 UInt; -#else - typedef long long Int; // NOLINT - typedef unsigned long long UInt; // NOLINT -#endif // GTEST_OS_WINDOWS -}; - -// Integer types of known sizes. -typedef TypeWithSize<4>::Int Int32; -typedef TypeWithSize<4>::UInt UInt32; -typedef TypeWithSize<8>::Int Int64; -typedef TypeWithSize<8>::UInt UInt64; -typedef TypeWithSize<8>::Int TimeInMillis; // Represents time in milliseconds. - -// Utilities for command line flags and environment variables. - -// Macro for referencing flags. -#define GTEST_FLAG(name) FLAGS_gtest_##name - -// Macros for declaring flags. -#define GTEST_DECLARE_bool_(name) GTEST_API_ extern bool GTEST_FLAG(name) -#define GTEST_DECLARE_int32_(name) \ - GTEST_API_ extern ::testing::internal::Int32 GTEST_FLAG(name) -#define GTEST_DECLARE_string_(name) \ - GTEST_API_ extern ::testing::internal::String GTEST_FLAG(name) - -// Macros for defining flags. -#define GTEST_DEFINE_bool_(name, default_val, doc) \ - GTEST_API_ bool GTEST_FLAG(name) = (default_val) -#define GTEST_DEFINE_int32_(name, default_val, doc) \ - GTEST_API_ ::testing::internal::Int32 GTEST_FLAG(name) = (default_val) -#define GTEST_DEFINE_string_(name, default_val, doc) \ - GTEST_API_ ::testing::internal::String GTEST_FLAG(name) = (default_val) - -// Parses 'str' for a 32-bit signed integer. If successful, writes the result -// to *value and returns true; otherwise leaves *value unchanged and returns -// false. -// TODO(chandlerc): Find a better way to refactor flag and environment parsing -// out of both gtest-port.cc and gtest.cc to avoid exporting this utility -// function. -bool ParseInt32(const Message& src_text, const char* str, Int32* value); - -// Parses a bool/Int32/string from the environment variable -// corresponding to the given Google Test flag. -bool BoolFromGTestEnv(const char* flag, bool default_val); -GTEST_API_ Int32 Int32FromGTestEnv(const char* flag, Int32 default_val); -const char* StringFromGTestEnv(const char* flag, const char* default_val); - -} // namespace internal -} // namespace testing - -#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ - -#if GTEST_OS_LINUX -# include -# include -# include -# include -#endif // GTEST_OS_LINUX - -#include -#include -#include -#include -#include - -// Copyright 2005, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee) -// -// The Google C++ Testing Framework (Google Test) -// -// This header file declares the String class and functions used internally by -// Google Test. They are subject to change without notice. They should not used -// by code external to Google Test. -// -// This header file is #included by . -// It should not be #included by other files. - -#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ -#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ - -#ifdef __BORLANDC__ -// string.h is not guaranteed to provide strcpy on C++ Builder. -# include -#endif - -#include - -#include - -namespace testing { -namespace internal { - -// String - a UTF-8 string class. -// -// For historic reasons, we don't use std::string. -// -// TODO(wan@google.com): replace this class with std::string or -// implement it in terms of the latter. -// -// Note that String can represent both NULL and the empty string, -// while std::string cannot represent NULL. -// -// NULL and the empty string are considered different. NULL is less -// than anything (including the empty string) except itself. -// -// This class only provides minimum functionality necessary for -// implementing Google Test. We do not intend to implement a full-fledged -// string class here. -// -// Since the purpose of this class is to provide a substitute for -// std::string on platforms where it cannot be used, we define a copy -// constructor and assignment operators such that we don't need -// conditional compilation in a lot of places. -// -// In order to make the representation efficient, the d'tor of String -// is not virtual. Therefore DO NOT INHERIT FROM String. -class GTEST_API_ String { - public: - // Static utility methods - - // Returns the input enclosed in double quotes if it's not NULL; - // otherwise returns "(null)". For example, "\"Hello\"" is returned - // for input "Hello". - // - // This is useful for printing a C string in the syntax of a literal. - // - // Known issue: escape sequences are not handled yet. - static String ShowCStringQuoted(const char* c_str); - - // Clones a 0-terminated C string, allocating memory using new. The - // caller is responsible for deleting the return value using - // delete[]. Returns the cloned string, or NULL if the input is - // NULL. - // - // This is different from strdup() in string.h, which allocates - // memory using malloc(). - static const char* CloneCString(const char* c_str); - -#if GTEST_OS_WINDOWS_MOBILE - // Windows CE does not have the 'ANSI' versions of Win32 APIs. To be - // able to pass strings to Win32 APIs on CE we need to convert them - // to 'Unicode', UTF-16. - - // Creates a UTF-16 wide string from the given ANSI string, allocating - // memory using new. The caller is responsible for deleting the return - // value using delete[]. Returns the wide string, or NULL if the - // input is NULL. - // - // The wide string is created using the ANSI codepage (CP_ACP) to - // match the behaviour of the ANSI versions of Win32 calls and the - // C runtime. - static LPCWSTR AnsiToUtf16(const char* c_str); - - // Creates an ANSI string from the given wide string, allocating - // memory using new. The caller is responsible for deleting the return - // value using delete[]. Returns the ANSI string, or NULL if the - // input is NULL. - // - // The returned string is created using the ANSI codepage (CP_ACP) to - // match the behaviour of the ANSI versions of Win32 calls and the - // C runtime. - static const char* Utf16ToAnsi(LPCWSTR utf16_str); -#endif - - // Compares two C strings. Returns true iff they have the same content. - // - // Unlike strcmp(), this function can handle NULL argument(s). A - // NULL C string is considered different to any non-NULL C string, - // including the empty string. - static bool CStringEquals(const char* lhs, const char* rhs); - - // Converts a wide C string to a String using the UTF-8 encoding. - // NULL will be converted to "(null)". If an error occurred during - // the conversion, "(failed to convert from wide string)" is - // returned. - static String ShowWideCString(const wchar_t* wide_c_str); - - // Similar to ShowWideCString(), except that this function encloses - // the converted string in double quotes. - static String ShowWideCStringQuoted(const wchar_t* wide_c_str); - - // Compares two wide C strings. Returns true iff they have the same - // content. - // - // Unlike wcscmp(), this function can handle NULL argument(s). A - // NULL C string is considered different to any non-NULL C string, - // including the empty string. - static bool WideCStringEquals(const wchar_t* lhs, const wchar_t* rhs); - - // Compares two C strings, ignoring case. Returns true iff they - // have the same content. - // - // Unlike strcasecmp(), this function can handle NULL argument(s). - // A NULL C string is considered different to any non-NULL C string, - // including the empty string. - static bool CaseInsensitiveCStringEquals(const char* lhs, - const char* rhs); - - // Compares two wide C strings, ignoring case. Returns true iff they - // have the same content. - // - // Unlike wcscasecmp(), this function can handle NULL argument(s). - // A NULL C string is considered different to any non-NULL wide C string, - // including the empty string. - // NB: The implementations on different platforms slightly differ. - // On windows, this method uses _wcsicmp which compares according to LC_CTYPE - // environment variable. On GNU platform this method uses wcscasecmp - // which compares according to LC_CTYPE category of the current locale. - // On MacOS X, it uses towlower, which also uses LC_CTYPE category of the - // current locale. - static bool CaseInsensitiveWideCStringEquals(const wchar_t* lhs, - const wchar_t* rhs); - - // Formats a list of arguments to a String, using the same format - // spec string as for printf. - // - // We do not use the StringPrintf class as it is not universally - // available. - // - // The result is limited to 4096 characters (including the tailing - // 0). If 4096 characters are not enough to format the input, - // "" is returned. - static String Format(const char* format, ...); - - // C'tors - - // The default c'tor constructs a NULL string. - String() : c_str_(NULL), length_(0) {} - - // Constructs a String by cloning a 0-terminated C string. - String(const char* a_c_str) { // NOLINT - if (a_c_str == NULL) { - c_str_ = NULL; - length_ = 0; - } else { - ConstructNonNull(a_c_str, strlen(a_c_str)); - } - } - - // Constructs a String by copying a given number of chars from a - // buffer. E.g. String("hello", 3) creates the string "hel", - // String("a\0bcd", 4) creates "a\0bc", String(NULL, 0) creates "", - // and String(NULL, 1) results in access violation. - String(const char* buffer, size_t a_length) { - ConstructNonNull(buffer, a_length); - } - - // The copy c'tor creates a new copy of the string. The two - // String objects do not share content. - String(const String& str) : c_str_(NULL), length_(0) { *this = str; } - - // D'tor. String is intended to be a final class, so the d'tor - // doesn't need to be virtual. - ~String() { delete[] c_str_; } - - // Allows a String to be implicitly converted to an ::std::string or - // ::string, and vice versa. Converting a String containing a NULL - // pointer to ::std::string or ::string is undefined behavior. - // Converting a ::std::string or ::string containing an embedded NUL - // character to a String will result in the prefix up to the first - // NUL character. - String(const ::std::string& str) { - ConstructNonNull(str.c_str(), str.length()); - } - - operator ::std::string() const { return ::std::string(c_str(), length()); } - -#if GTEST_HAS_GLOBAL_STRING - String(const ::string& str) { - ConstructNonNull(str.c_str(), str.length()); - } - - operator ::string() const { return ::string(c_str(), length()); } -#endif // GTEST_HAS_GLOBAL_STRING - - // Returns true iff this is an empty string (i.e. ""). - bool empty() const { return (c_str() != NULL) && (length() == 0); } - - // Compares this with another String. - // Returns < 0 if this is less than rhs, 0 if this is equal to rhs, or > 0 - // if this is greater than rhs. - int Compare(const String& rhs) const; - - // Returns true iff this String equals the given C string. A NULL - // string and a non-NULL string are considered not equal. - bool operator==(const char* a_c_str) const { return Compare(a_c_str) == 0; } - - // Returns true iff this String is less than the given String. A - // NULL string is considered less than "". - bool operator<(const String& rhs) const { return Compare(rhs) < 0; } - - // Returns true iff this String doesn't equal the given C string. A NULL - // string and a non-NULL string are considered not equal. - bool operator!=(const char* a_c_str) const { return !(*this == a_c_str); } - - // Returns true iff this String ends with the given suffix. *Any* - // String is considered to end with a NULL or empty suffix. - bool EndsWith(const char* suffix) const; - - // Returns true iff this String ends with the given suffix, not considering - // case. Any String is considered to end with a NULL or empty suffix. - bool EndsWithCaseInsensitive(const char* suffix) const; - - // Returns the length of the encapsulated string, or 0 if the - // string is NULL. - size_t length() const { return length_; } - - // Gets the 0-terminated C string this String object represents. - // The String object still owns the string. Therefore the caller - // should NOT delete the return value. - const char* c_str() const { return c_str_; } - - // Assigns a C string to this object. Self-assignment works. - const String& operator=(const char* a_c_str) { - return *this = String(a_c_str); - } - - // Assigns a String object to this object. Self-assignment works. - const String& operator=(const String& rhs) { - if (this != &rhs) { - delete[] c_str_; - if (rhs.c_str() == NULL) { - c_str_ = NULL; - length_ = 0; - } else { - ConstructNonNull(rhs.c_str(), rhs.length()); - } - } - - return *this; - } - - private: - // Constructs a non-NULL String from the given content. This - // function can only be called when c_str_ has not been allocated. - // ConstructNonNull(NULL, 0) results in an empty string (""). - // ConstructNonNull(NULL, non_zero) is undefined behavior. - void ConstructNonNull(const char* buffer, size_t a_length) { - char* const str = new char[a_length + 1]; - memcpy(str, buffer, a_length); - str[a_length] = '\0'; - c_str_ = str; - length_ = a_length; - } - - const char* c_str_; - size_t length_; -}; // class String - -// Streams a String to an ostream. Each '\0' character in the String -// is replaced with "\\0". -inline ::std::ostream& operator<<(::std::ostream& os, const String& str) { - if (str.c_str() == NULL) { - os << "(null)"; - } else { - const char* const c_str = str.c_str(); - for (size_t i = 0; i != str.length(); i++) { - if (c_str[i] == '\0') { - os << "\\0"; - } else { - os << c_str[i]; - } - } - } - return os; -} - -// Gets the content of the stringstream's buffer as a String. Each '\0' -// character in the buffer is replaced with "\\0". -GTEST_API_ String StringStreamToString(::std::stringstream* stream); - -// Converts a streamable value to a String. A NULL pointer is -// converted to "(null)". When the input value is a ::string, -// ::std::string, ::wstring, or ::std::wstring object, each NUL -// character in it is replaced with "\\0". - -// Declared here but defined in gtest.h, so that it has access -// to the definition of the Message class, required by the ARM -// compiler. -template -String StreamableToString(const T& streamable); - -} // namespace internal -} // namespace testing - -#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ -// Copyright 2008, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Author: keith.ray@gmail.com (Keith Ray) -// -// Google Test filepath utilities -// -// This header file declares classes and functions used internally by -// Google Test. They are subject to change without notice. -// -// This file is #included in . -// Do not include this header file separately! - -#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ -#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ - - -namespace testing { -namespace internal { - -// FilePath - a class for file and directory pathname manipulation which -// handles platform-specific conventions (like the pathname separator). -// Used for helper functions for naming files in a directory for xml output. -// Except for Set methods, all methods are const or static, which provides an -// "immutable value object" -- useful for peace of mind. -// A FilePath with a value ending in a path separator ("like/this/") represents -// a directory, otherwise it is assumed to represent a file. In either case, -// it may or may not represent an actual file or directory in the file system. -// Names are NOT checked for syntax correctness -- no checking for illegal -// characters, malformed paths, etc. - -class GTEST_API_ FilePath { - public: - FilePath() : pathname_("") { } - FilePath(const FilePath& rhs) : pathname_(rhs.pathname_) { } - - explicit FilePath(const char* pathname) : pathname_(pathname) { - Normalize(); - } - - explicit FilePath(const String& pathname) : pathname_(pathname) { - Normalize(); - } - - FilePath& operator=(const FilePath& rhs) { - Set(rhs); - return *this; - } - - void Set(const FilePath& rhs) { - pathname_ = rhs.pathname_; - } - - String ToString() const { return pathname_; } - const char* c_str() const { return pathname_.c_str(); } - - // Returns the current working directory, or "" if unsuccessful. - static FilePath GetCurrentDir(); - - // Given directory = "dir", base_name = "test", number = 0, - // extension = "xml", returns "dir/test.xml". If number is greater - // than zero (e.g., 12), returns "dir/test_12.xml". - // On Windows platform, uses \ as the separator rather than /. - static FilePath MakeFileName(const FilePath& directory, - const FilePath& base_name, - int number, - const char* extension); - - // Given directory = "dir", relative_path = "test.xml", - // returns "dir/test.xml". - // On Windows, uses \ as the separator rather than /. - static FilePath ConcatPaths(const FilePath& directory, - const FilePath& relative_path); - - // Returns a pathname for a file that does not currently exist. The pathname - // will be directory/base_name.extension or - // directory/base_name_.extension if directory/base_name.extension - // already exists. The number will be incremented until a pathname is found - // that does not already exist. - // Examples: 'dir/foo_test.xml' or 'dir/foo_test_1.xml'. - // There could be a race condition if two or more processes are calling this - // function at the same time -- they could both pick the same filename. - static FilePath GenerateUniqueFileName(const FilePath& directory, - const FilePath& base_name, - const char* extension); - - // Returns true iff the path is NULL or "". - bool IsEmpty() const { return c_str() == NULL || *c_str() == '\0'; } - - // If input name has a trailing separator character, removes it and returns - // the name, otherwise return the name string unmodified. - // On Windows platform, uses \ as the separator, other platforms use /. - FilePath RemoveTrailingPathSeparator() const; - - // Returns a copy of the FilePath with the directory part removed. - // Example: FilePath("path/to/file").RemoveDirectoryName() returns - // FilePath("file"). If there is no directory part ("just_a_file"), it returns - // the FilePath unmodified. If there is no file part ("just_a_dir/") it - // returns an empty FilePath (""). - // On Windows platform, '\' is the path separator, otherwise it is '/'. - FilePath RemoveDirectoryName() const; - - // RemoveFileName returns the directory path with the filename removed. - // Example: FilePath("path/to/file").RemoveFileName() returns "path/to/". - // If the FilePath is "a_file" or "/a_file", RemoveFileName returns - // FilePath("./") or, on Windows, FilePath(".\\"). If the filepath does - // not have a file, like "just/a/dir/", it returns the FilePath unmodified. - // On Windows platform, '\' is the path separator, otherwise it is '/'. - FilePath RemoveFileName() const; - - // Returns a copy of the FilePath with the case-insensitive extension removed. - // Example: FilePath("dir/file.exe").RemoveExtension("EXE") returns - // FilePath("dir/file"). If a case-insensitive extension is not - // found, returns a copy of the original FilePath. - FilePath RemoveExtension(const char* extension) const; - - // Creates directories so that path exists. Returns true if successful or if - // the directories already exist; returns false if unable to create - // directories for any reason. Will also return false if the FilePath does - // not represent a directory (that is, it doesn't end with a path separator). - bool CreateDirectoriesRecursively() const; - - // Create the directory so that path exists. Returns true if successful or - // if the directory already exists; returns false if unable to create the - // directory for any reason, including if the parent directory does not - // exist. Not named "CreateDirectory" because that's a macro on Windows. - bool CreateFolder() const; - - // Returns true if FilePath describes something in the file-system, - // either a file, directory, or whatever, and that something exists. - bool FileOrDirectoryExists() const; - - // Returns true if pathname describes a directory in the file-system - // that exists. - bool DirectoryExists() const; - - // Returns true if FilePath ends with a path separator, which indicates that - // it is intended to represent a directory. Returns false otherwise. - // This does NOT check that a directory (or file) actually exists. - bool IsDirectory() const; - - // Returns true if pathname describes a root directory. (Windows has one - // root directory per disk drive.) - bool IsRootDirectory() const; - - // Returns true if pathname describes an absolute path. - bool IsAbsolutePath() const; - - private: - // Replaces multiple consecutive separators with a single separator. - // For example, "bar///foo" becomes "bar/foo". Does not eliminate other - // redundancies that might be in a pathname involving "." or "..". - // - // A pathname with multiple consecutive separators may occur either through - // user error or as a result of some scripts or APIs that generate a pathname - // with a trailing separator. On other platforms the same API or script - // may NOT generate a pathname with a trailing "/". Then elsewhere that - // pathname may have another "/" and pathname components added to it, - // without checking for the separator already being there. - // The script language and operating system may allow paths like "foo//bar" - // but some of the functions in FilePath will not handle that correctly. In - // particular, RemoveTrailingPathSeparator() only removes one separator, and - // it is called in CreateDirectoriesRecursively() assuming that it will change - // a pathname from directory syntax (trailing separator) to filename syntax. - // - // On Windows this method also replaces the alternate path separator '/' with - // the primary path separator '\\', so that for example "bar\\/\\foo" becomes - // "bar\\foo". - - void Normalize(); - - // Returns a pointer to the last occurence of a valid path separator in - // the FilePath. On Windows, for example, both '/' and '\' are valid path - // separators. Returns NULL if no path separator was found. - const char* FindLastPathSeparator() const; - - String pathname_; -}; // class FilePath - -} // namespace internal -} // namespace testing - -#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ -// This file was GENERATED by command: -// pump.py gtest-type-util.h.pump -// DO NOT EDIT BY HAND!!! - -// Copyright 2008 Google Inc. -// All Rights Reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Author: wan@google.com (Zhanyong Wan) - -// Type utilities needed for implementing typed and type-parameterized -// tests. This file is generated by a SCRIPT. DO NOT EDIT BY HAND! -// -// Currently we support at most 50 types in a list, and at most 50 -// type-parameterized tests in one type-parameterized test case. -// Please contact googletestframework@googlegroups.com if you need -// more. - -#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ -#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ - - -// #ifdef __GNUC__ is too general here. It is possible to use gcc without using -// libstdc++ (which is where cxxabi.h comes from). -# ifdef __GLIBCXX__ -# include -# elif defined(__HP_aCC) -# include -# endif // __GLIBCXX__ - -namespace testing { -namespace internal { - -// GetTypeName() returns a human-readable name of type T. -// NB: This function is also used in Google Mock, so don't move it inside of -// the typed-test-only section below. -template -String GetTypeName() { -# if GTEST_HAS_RTTI - - const char* const name = typeid(T).name(); -# if defined(__GLIBCXX__) || defined(__HP_aCC) - int status = 0; - // gcc's implementation of typeid(T).name() mangles the type name, - // so we have to demangle it. -# ifdef __GLIBCXX__ - using abi::__cxa_demangle; -# endif // __GLIBCXX__ - char* const readable_name = __cxa_demangle(name, 0, 0, &status); - const String name_str(status == 0 ? readable_name : name); - free(readable_name); - return name_str; -# else - return name; -# endif // __GLIBCXX__ || __HP_aCC - -# else - - return ""; - -# endif // GTEST_HAS_RTTI -} - -#if GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P - -// AssertyTypeEq::type is defined iff T1 and T2 are the same -// type. This can be used as a compile-time assertion to ensure that -// two types are equal. - -template -struct AssertTypeEq; - -template -struct AssertTypeEq { - typedef bool type; -}; - -// A unique type used as the default value for the arguments of class -// template Types. This allows us to simulate variadic templates -// (e.g. Types, Type, and etc), which C++ doesn't -// support directly. -struct None {}; - -// The following family of struct and struct templates are used to -// represent type lists. In particular, TypesN -// represents a type list with N types (T1, T2, ..., and TN) in it. -// Except for Types0, every struct in the family has two member types: -// Head for the first type in the list, and Tail for the rest of the -// list. - -// The empty type list. -struct Types0 {}; - -// Type lists of length 1, 2, 3, and so on. - -template -struct Types1 { - typedef T1 Head; - typedef Types0 Tail; -}; -template -struct Types2 { - typedef T1 Head; - typedef Types1 Tail; -}; - -template -struct Types3 { - typedef T1 Head; - typedef Types2 Tail; -}; - -template -struct Types4 { - typedef T1 Head; - typedef Types3 Tail; -}; - -template -struct Types5 { - typedef T1 Head; - typedef Types4 Tail; -}; - -template -struct Types6 { - typedef T1 Head; - typedef Types5 Tail; -}; - -template -struct Types7 { - typedef T1 Head; - typedef Types6 Tail; -}; - -template -struct Types8 { - typedef T1 Head; - typedef Types7 Tail; -}; - -template -struct Types9 { - typedef T1 Head; - typedef Types8 Tail; -}; - -template -struct Types10 { - typedef T1 Head; - typedef Types9 Tail; -}; - -template -struct Types11 { - typedef T1 Head; - typedef Types10 Tail; -}; - -template -struct Types12 { - typedef T1 Head; - typedef Types11 Tail; -}; - -template -struct Types13 { - typedef T1 Head; - typedef Types12 Tail; -}; - -template -struct Types14 { - typedef T1 Head; - typedef Types13 Tail; -}; - -template -struct Types15 { - typedef T1 Head; - typedef Types14 Tail; -}; - -template -struct Types16 { - typedef T1 Head; - typedef Types15 Tail; -}; - -template -struct Types17 { - typedef T1 Head; - typedef Types16 Tail; -}; - -template -struct Types18 { - typedef T1 Head; - typedef Types17 Tail; -}; - -template -struct Types19 { - typedef T1 Head; - typedef Types18 Tail; -}; - -template -struct Types20 { - typedef T1 Head; - typedef Types19 Tail; -}; - -template -struct Types21 { - typedef T1 Head; - typedef Types20 Tail; -}; - -template -struct Types22 { - typedef T1 Head; - typedef Types21 Tail; -}; - -template -struct Types23 { - typedef T1 Head; - typedef Types22 Tail; -}; - -template -struct Types24 { - typedef T1 Head; - typedef Types23 Tail; -}; - -template -struct Types25 { - typedef T1 Head; - typedef Types24 Tail; -}; - -template -struct Types26 { - typedef T1 Head; - typedef Types25 Tail; -}; - -template -struct Types27 { - typedef T1 Head; - typedef Types26 Tail; -}; - -template -struct Types28 { - typedef T1 Head; - typedef Types27 Tail; -}; - -template -struct Types29 { - typedef T1 Head; - typedef Types28 Tail; -}; - -template -struct Types30 { - typedef T1 Head; - typedef Types29 Tail; -}; - -template -struct Types31 { - typedef T1 Head; - typedef Types30 Tail; -}; - -template -struct Types32 { - typedef T1 Head; - typedef Types31 Tail; -}; - -template -struct Types33 { - typedef T1 Head; - typedef Types32 Tail; -}; - -template -struct Types34 { - typedef T1 Head; - typedef Types33 Tail; -}; - -template -struct Types35 { - typedef T1 Head; - typedef Types34 Tail; -}; - -template -struct Types36 { - typedef T1 Head; - typedef Types35 Tail; -}; - -template -struct Types37 { - typedef T1 Head; - typedef Types36 Tail; -}; - -template -struct Types38 { - typedef T1 Head; - typedef Types37 Tail; -}; - -template -struct Types39 { - typedef T1 Head; - typedef Types38 Tail; -}; - -template -struct Types40 { - typedef T1 Head; - typedef Types39 Tail; -}; - -template -struct Types41 { - typedef T1 Head; - typedef Types40 Tail; -}; - -template -struct Types42 { - typedef T1 Head; - typedef Types41 Tail; -}; - -template -struct Types43 { - typedef T1 Head; - typedef Types42 Tail; -}; - -template -struct Types44 { - typedef T1 Head; - typedef Types43 Tail; -}; - -template -struct Types45 { - typedef T1 Head; - typedef Types44 Tail; -}; - -template -struct Types46 { - typedef T1 Head; - typedef Types45 Tail; -}; - -template -struct Types47 { - typedef T1 Head; - typedef Types46 Tail; -}; - -template -struct Types48 { - typedef T1 Head; - typedef Types47 Tail; -}; - -template -struct Types49 { - typedef T1 Head; - typedef Types48 Tail; -}; - -template -struct Types50 { - typedef T1 Head; - typedef Types49 Tail; -}; - - -} // namespace internal - -// We don't want to require the users to write TypesN<...> directly, -// as that would require them to count the length. Types<...> is much -// easier to write, but generates horrible messages when there is a -// compiler error, as gcc insists on printing out each template -// argument, even if it has the default value (this means Types -// will appear as Types in the compiler -// errors). -// -// Our solution is to combine the best part of the two approaches: a -// user would write Types, and Google Test will translate -// that to TypesN internally to make error messages -// readable. The translation is done by the 'type' member of the -// Types template. -template -struct Types { - typedef internal::Types50 type; -}; - -template <> -struct Types { - typedef internal::Types0 type; -}; -template -struct Types { - typedef internal::Types1 type; -}; -template -struct Types { - typedef internal::Types2 type; -}; -template -struct Types { - typedef internal::Types3 type; -}; -template -struct Types { - typedef internal::Types4 type; -}; -template -struct Types { - typedef internal::Types5 type; -}; -template -struct Types { - typedef internal::Types6 type; -}; -template -struct Types { - typedef internal::Types7 type; -}; -template -struct Types { - typedef internal::Types8 type; -}; -template -struct Types { - typedef internal::Types9 type; -}; -template -struct Types { - typedef internal::Types10 type; -}; -template -struct Types { - typedef internal::Types11 type; -}; -template -struct Types { - typedef internal::Types12 type; -}; -template -struct Types { - typedef internal::Types13 type; -}; -template -struct Types { - typedef internal::Types14 type; -}; -template -struct Types { - typedef internal::Types15 type; -}; -template -struct Types { - typedef internal::Types16 type; -}; -template -struct Types { - typedef internal::Types17 type; -}; -template -struct Types { - typedef internal::Types18 type; -}; -template -struct Types { - typedef internal::Types19 type; -}; -template -struct Types { - typedef internal::Types20 type; -}; -template -struct Types { - typedef internal::Types21 type; -}; -template -struct Types { - typedef internal::Types22 type; -}; -template -struct Types { - typedef internal::Types23 type; -}; -template -struct Types { - typedef internal::Types24 type; -}; -template -struct Types { - typedef internal::Types25 type; -}; -template -struct Types { - typedef internal::Types26 type; -}; -template -struct Types { - typedef internal::Types27 type; -}; -template -struct Types { - typedef internal::Types28 type; -}; -template -struct Types { - typedef internal::Types29 type; -}; -template -struct Types { - typedef internal::Types30 type; -}; -template -struct Types { - typedef internal::Types31 type; -}; -template -struct Types { - typedef internal::Types32 type; -}; -template -struct Types { - typedef internal::Types33 type; -}; -template -struct Types { - typedef internal::Types34 type; -}; -template -struct Types { - typedef internal::Types35 type; -}; -template -struct Types { - typedef internal::Types36 type; -}; -template -struct Types { - typedef internal::Types37 type; -}; -template -struct Types { - typedef internal::Types38 type; -}; -template -struct Types { - typedef internal::Types39 type; -}; -template -struct Types { - typedef internal::Types40 type; -}; -template -struct Types { - typedef internal::Types41 type; -}; -template -struct Types { - typedef internal::Types42 type; -}; -template -struct Types { - typedef internal::Types43 type; -}; -template -struct Types { - typedef internal::Types44 type; -}; -template -struct Types { - typedef internal::Types45 type; -}; -template -struct Types { - typedef internal::Types46 type; -}; -template -struct Types { - typedef internal::Types47 type; -}; -template -struct Types { - typedef internal::Types48 type; -}; -template -struct Types { - typedef internal::Types49 type; -}; - -namespace internal { - -# define GTEST_TEMPLATE_ template class - -// The template "selector" struct TemplateSel is used to -// represent Tmpl, which must be a class template with one type -// parameter, as a type. TemplateSel::Bind::type is defined -// as the type Tmpl. This allows us to actually instantiate the -// template "selected" by TemplateSel. -// -// This trick is necessary for simulating typedef for class templates, -// which C++ doesn't support directly. -template -struct TemplateSel { - template - struct Bind { - typedef Tmpl type; - }; -}; - -# define GTEST_BIND_(TmplSel, T) \ - TmplSel::template Bind::type - -// A unique struct template used as the default value for the -// arguments of class template Templates. This allows us to simulate -// variadic templates (e.g. Templates, Templates, -// and etc), which C++ doesn't support directly. -template -struct NoneT {}; - -// The following family of struct and struct templates are used to -// represent template lists. In particular, TemplatesN represents a list of N templates (T1, T2, ..., and TN). Except -// for Templates0, every struct in the family has two member types: -// Head for the selector of the first template in the list, and Tail -// for the rest of the list. - -// The empty template list. -struct Templates0 {}; - -// Template lists of length 1, 2, 3, and so on. - -template -struct Templates1 { - typedef TemplateSel Head; - typedef Templates0 Tail; -}; -template -struct Templates2 { - typedef TemplateSel Head; - typedef Templates1 Tail; -}; - -template -struct Templates3 { - typedef TemplateSel Head; - typedef Templates2 Tail; -}; - -template -struct Templates4 { - typedef TemplateSel Head; - typedef Templates3 Tail; -}; - -template -struct Templates5 { - typedef TemplateSel Head; - typedef Templates4 Tail; -}; - -template -struct Templates6 { - typedef TemplateSel Head; - typedef Templates5 Tail; -}; - -template -struct Templates7 { - typedef TemplateSel Head; - typedef Templates6 Tail; -}; - -template -struct Templates8 { - typedef TemplateSel Head; - typedef Templates7 Tail; -}; - -template -struct Templates9 { - typedef TemplateSel Head; - typedef Templates8 Tail; -}; - -template -struct Templates10 { - typedef TemplateSel Head; - typedef Templates9 Tail; -}; - -template -struct Templates11 { - typedef TemplateSel Head; - typedef Templates10 Tail; -}; - -template -struct Templates12 { - typedef TemplateSel Head; - typedef Templates11 Tail; -}; - -template -struct Templates13 { - typedef TemplateSel Head; - typedef Templates12 Tail; -}; - -template -struct Templates14 { - typedef TemplateSel Head; - typedef Templates13 Tail; -}; - -template -struct Templates15 { - typedef TemplateSel Head; - typedef Templates14 Tail; -}; - -template -struct Templates16 { - typedef TemplateSel Head; - typedef Templates15 Tail; -}; - -template -struct Templates17 { - typedef TemplateSel Head; - typedef Templates16 Tail; -}; - -template -struct Templates18 { - typedef TemplateSel Head; - typedef Templates17 Tail; -}; - -template -struct Templates19 { - typedef TemplateSel Head; - typedef Templates18 Tail; -}; - -template -struct Templates20 { - typedef TemplateSel Head; - typedef Templates19 Tail; -}; - -template -struct Templates21 { - typedef TemplateSel Head; - typedef Templates20 Tail; -}; - -template -struct Templates22 { - typedef TemplateSel Head; - typedef Templates21 Tail; -}; - -template -struct Templates23 { - typedef TemplateSel Head; - typedef Templates22 Tail; -}; - -template -struct Templates24 { - typedef TemplateSel Head; - typedef Templates23 Tail; -}; - -template -struct Templates25 { - typedef TemplateSel Head; - typedef Templates24 Tail; -}; - -template -struct Templates26 { - typedef TemplateSel Head; - typedef Templates25 Tail; -}; - -template -struct Templates27 { - typedef TemplateSel Head; - typedef Templates26 Tail; -}; - -template -struct Templates28 { - typedef TemplateSel Head; - typedef Templates27 Tail; -}; - -template -struct Templates29 { - typedef TemplateSel Head; - typedef Templates28 Tail; -}; - -template -struct Templates30 { - typedef TemplateSel Head; - typedef Templates29 Tail; -}; - -template -struct Templates31 { - typedef TemplateSel Head; - typedef Templates30 Tail; -}; - -template -struct Templates32 { - typedef TemplateSel Head; - typedef Templates31 Tail; -}; - -template -struct Templates33 { - typedef TemplateSel Head; - typedef Templates32 Tail; -}; - -template -struct Templates34 { - typedef TemplateSel Head; - typedef Templates33 Tail; -}; - -template -struct Templates35 { - typedef TemplateSel Head; - typedef Templates34 Tail; -}; - -template -struct Templates36 { - typedef TemplateSel Head; - typedef Templates35 Tail; -}; - -template -struct Templates37 { - typedef TemplateSel Head; - typedef Templates36 Tail; -}; - -template -struct Templates38 { - typedef TemplateSel Head; - typedef Templates37 Tail; -}; - -template -struct Templates39 { - typedef TemplateSel Head; - typedef Templates38 Tail; -}; - -template -struct Templates40 { - typedef TemplateSel Head; - typedef Templates39 Tail; -}; - -template -struct Templates41 { - typedef TemplateSel Head; - typedef Templates40 Tail; -}; - -template -struct Templates42 { - typedef TemplateSel Head; - typedef Templates41 Tail; -}; - -template -struct Templates43 { - typedef TemplateSel Head; - typedef Templates42 Tail; -}; - -template -struct Templates44 { - typedef TemplateSel Head; - typedef Templates43 Tail; -}; - -template -struct Templates45 { - typedef TemplateSel Head; - typedef Templates44 Tail; -}; - -template -struct Templates46 { - typedef TemplateSel Head; - typedef Templates45 Tail; -}; - -template -struct Templates47 { - typedef TemplateSel Head; - typedef Templates46 Tail; -}; - -template -struct Templates48 { - typedef TemplateSel Head; - typedef Templates47 Tail; -}; - -template -struct Templates49 { - typedef TemplateSel Head; - typedef Templates48 Tail; -}; - -template -struct Templates50 { - typedef TemplateSel Head; - typedef Templates49 Tail; -}; - - -// We don't want to require the users to write TemplatesN<...> directly, -// as that would require them to count the length. Templates<...> is much -// easier to write, but generates horrible messages when there is a -// compiler error, as gcc insists on printing out each template -// argument, even if it has the default value (this means Templates -// will appear as Templates in the compiler -// errors). -// -// Our solution is to combine the best part of the two approaches: a -// user would write Templates, and Google Test will translate -// that to TemplatesN internally to make error messages -// readable. The translation is done by the 'type' member of the -// Templates template. -template -struct Templates { - typedef Templates50 type; -}; - -template <> -struct Templates { - typedef Templates0 type; -}; -template -struct Templates { - typedef Templates1 type; -}; -template -struct Templates { - typedef Templates2 type; -}; -template -struct Templates { - typedef Templates3 type; -}; -template -struct Templates { - typedef Templates4 type; -}; -template -struct Templates { - typedef Templates5 type; -}; -template -struct Templates { - typedef Templates6 type; -}; -template -struct Templates { - typedef Templates7 type; -}; -template -struct Templates { - typedef Templates8 type; -}; -template -struct Templates { - typedef Templates9 type; -}; -template -struct Templates { - typedef Templates10 type; -}; -template -struct Templates { - typedef Templates11 type; -}; -template -struct Templates { - typedef Templates12 type; -}; -template -struct Templates { - typedef Templates13 type; -}; -template -struct Templates { - typedef Templates14 type; -}; -template -struct Templates { - typedef Templates15 type; -}; -template -struct Templates { - typedef Templates16 type; -}; -template -struct Templates { - typedef Templates17 type; -}; -template -struct Templates { - typedef Templates18 type; -}; -template -struct Templates { - typedef Templates19 type; -}; -template -struct Templates { - typedef Templates20 type; -}; -template -struct Templates { - typedef Templates21 type; -}; -template -struct Templates { - typedef Templates22 type; -}; -template -struct Templates { - typedef Templates23 type; -}; -template -struct Templates { - typedef Templates24 type; -}; -template -struct Templates { - typedef Templates25 type; -}; -template -struct Templates { - typedef Templates26 type; -}; -template -struct Templates { - typedef Templates27 type; -}; -template -struct Templates { - typedef Templates28 type; -}; -template -struct Templates { - typedef Templates29 type; -}; -template -struct Templates { - typedef Templates30 type; -}; -template -struct Templates { - typedef Templates31 type; -}; -template -struct Templates { - typedef Templates32 type; -}; -template -struct Templates { - typedef Templates33 type; -}; -template -struct Templates { - typedef Templates34 type; -}; -template -struct Templates { - typedef Templates35 type; -}; -template -struct Templates { - typedef Templates36 type; -}; -template -struct Templates { - typedef Templates37 type; -}; -template -struct Templates { - typedef Templates38 type; -}; -template -struct Templates { - typedef Templates39 type; -}; -template -struct Templates { - typedef Templates40 type; -}; -template -struct Templates { - typedef Templates41 type; -}; -template -struct Templates { - typedef Templates42 type; -}; -template -struct Templates { - typedef Templates43 type; -}; -template -struct Templates { - typedef Templates44 type; -}; -template -struct Templates { - typedef Templates45 type; -}; -template -struct Templates { - typedef Templates46 type; -}; -template -struct Templates { - typedef Templates47 type; -}; -template -struct Templates { - typedef Templates48 type; -}; -template -struct Templates { - typedef Templates49 type; -}; - -// The TypeList template makes it possible to use either a single type -// or a Types<...> list in TYPED_TEST_CASE() and -// INSTANTIATE_TYPED_TEST_CASE_P(). - -template -struct TypeList { typedef Types1 type; }; - -template -struct TypeList > { - typedef typename Types::type type; -}; - -#endif // GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P - -} // namespace internal -} // namespace testing - -#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ - -// Due to C++ preprocessor weirdness, we need double indirection to -// concatenate two tokens when one of them is __LINE__. Writing -// -// foo ## __LINE__ -// -// will result in the token foo__LINE__, instead of foo followed by -// the current line number. For more details, see -// http://www.parashift.com/c++-faq-lite/misc-technical-issues.html#faq-39.6 -#define GTEST_CONCAT_TOKEN_(foo, bar) GTEST_CONCAT_TOKEN_IMPL_(foo, bar) -#define GTEST_CONCAT_TOKEN_IMPL_(foo, bar) foo ## bar - -// Google Test defines the testing::Message class to allow construction of -// test messages via the << operator. The idea is that anything -// streamable to std::ostream can be streamed to a testing::Message. -// This allows a user to use his own types in Google Test assertions by -// overloading the << operator. -// -// util/gtl/stl_logging-inl.h overloads << for STL containers. These -// overloads cannot be defined in the std namespace, as that will be -// undefined behavior. Therefore, they are defined in the global -// namespace instead. -// -// C++'s symbol lookup rule (i.e. Koenig lookup) says that these -// overloads are visible in either the std namespace or the global -// namespace, but not other namespaces, including the testing -// namespace which Google Test's Message class is in. -// -// To allow STL containers (and other types that has a << operator -// defined in the global namespace) to be used in Google Test assertions, -// testing::Message must access the custom << operator from the global -// namespace. Hence this helper function. -// -// Note: Jeffrey Yasskin suggested an alternative fix by "using -// ::operator<<;" in the definition of Message's operator<<. That fix -// doesn't require a helper function, but unfortunately doesn't -// compile with MSVC. -template -inline void GTestStreamToHelper(std::ostream* os, const T& val) { - *os << val; -} - -class ProtocolMessage; -namespace proto2 { class Message; } - -namespace testing { - -// Forward declarations. - -class AssertionResult; // Result of an assertion. -class Message; // Represents a failure message. -class Test; // Represents a test. -class TestInfo; // Information about a test. -class TestPartResult; // Result of a test part. -class UnitTest; // A collection of test cases. - -template -::std::string PrintToString(const T& value); - -namespace internal { - -struct TraceInfo; // Information about a trace point. -class ScopedTrace; // Implements scoped trace. -class TestInfoImpl; // Opaque implementation of TestInfo -class UnitTestImpl; // Opaque implementation of UnitTest - -// How many times InitGoogleTest() has been called. -extern int g_init_gtest_count; - -// The text used in failure messages to indicate the start of the -// stack trace. -GTEST_API_ extern const char kStackTraceMarker[]; - -// A secret type that Google Test users don't know about. It has no -// definition on purpose. Therefore it's impossible to create a -// Secret object, which is what we want. -class Secret; - -// Two overloaded helpers for checking at compile time whether an -// expression is a null pointer literal (i.e. NULL or any 0-valued -// compile-time integral constant). Their return values have -// different sizes, so we can use sizeof() to test which version is -// picked by the compiler. These helpers have no implementations, as -// we only need their signatures. -// -// Given IsNullLiteralHelper(x), the compiler will pick the first -// version if x can be implicitly converted to Secret*, and pick the -// second version otherwise. Since Secret is a secret and incomplete -// type, the only expression a user can write that has type Secret* is -// a null pointer literal. Therefore, we know that x is a null -// pointer literal if and only if the first version is picked by the -// compiler. -char IsNullLiteralHelper(Secret* p); -char (&IsNullLiteralHelper(...))[2]; // NOLINT - -// A compile-time bool constant that is true if and only if x is a -// null pointer literal (i.e. NULL or any 0-valued compile-time -// integral constant). -#ifdef GTEST_ELLIPSIS_NEEDS_POD_ -// We lose support for NULL detection where the compiler doesn't like -// passing non-POD classes through ellipsis (...). -# define GTEST_IS_NULL_LITERAL_(x) false -#else -# define GTEST_IS_NULL_LITERAL_(x) \ - (sizeof(::testing::internal::IsNullLiteralHelper(x)) == 1) -#endif // GTEST_ELLIPSIS_NEEDS_POD_ - -// Appends the user-supplied message to the Google-Test-generated message. -GTEST_API_ String AppendUserMessage(const String& gtest_msg, - const Message& user_msg); - -// A helper class for creating scoped traces in user programs. -class GTEST_API_ ScopedTrace { - public: - // The c'tor pushes the given source file location and message onto - // a trace stack maintained by Google Test. - ScopedTrace(const char* file, int line, const Message& message); - - // The d'tor pops the info pushed by the c'tor. - // - // Note that the d'tor is not virtual in order to be efficient. - // Don't inherit from ScopedTrace! - ~ScopedTrace(); - - private: - GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedTrace); -} GTEST_ATTRIBUTE_UNUSED_; // A ScopedTrace object does its job in its - // c'tor and d'tor. Therefore it doesn't - // need to be used otherwise. - -// Converts a streamable value to a String. A NULL pointer is -// converted to "(null)". When the input value is a ::string, -// ::std::string, ::wstring, or ::std::wstring object, each NUL -// character in it is replaced with "\\0". -// Declared here but defined in gtest.h, so that it has access -// to the definition of the Message class, required by the ARM -// compiler. -template -String StreamableToString(const T& streamable); - -// The Symbian compiler has a bug that prevents it from selecting the -// correct overload of FormatForComparisonFailureMessage (see below) -// unless we pass the first argument by reference. If we do that, -// however, Visual Age C++ 10.1 generates a compiler error. Therefore -// we only apply the work-around for Symbian. -#if defined(__SYMBIAN32__) -# define GTEST_CREF_WORKAROUND_ const& -#else -# define GTEST_CREF_WORKAROUND_ -#endif - -// When this operand is a const char* or char*, if the other operand -// is a ::std::string or ::string, we print this operand as a C string -// rather than a pointer (we do the same for wide strings); otherwise -// we print it as a pointer to be safe. - -// This internal macro is used to avoid duplicated code. -#define GTEST_FORMAT_IMPL_(operand2_type, operand1_printer)\ -inline String FormatForComparisonFailureMessage(\ - operand2_type::value_type* GTEST_CREF_WORKAROUND_ str, \ - const operand2_type& /*operand2*/) {\ - return operand1_printer(str);\ -}\ -inline String FormatForComparisonFailureMessage(\ - const operand2_type::value_type* GTEST_CREF_WORKAROUND_ str, \ - const operand2_type& /*operand2*/) {\ - return operand1_printer(str);\ -} - -GTEST_FORMAT_IMPL_(::std::string, String::ShowCStringQuoted) -#if GTEST_HAS_STD_WSTRING -GTEST_FORMAT_IMPL_(::std::wstring, String::ShowWideCStringQuoted) -#endif // GTEST_HAS_STD_WSTRING - -#if GTEST_HAS_GLOBAL_STRING -GTEST_FORMAT_IMPL_(::string, String::ShowCStringQuoted) -#endif // GTEST_HAS_GLOBAL_STRING -#if GTEST_HAS_GLOBAL_WSTRING -GTEST_FORMAT_IMPL_(::wstring, String::ShowWideCStringQuoted) -#endif // GTEST_HAS_GLOBAL_WSTRING - -#undef GTEST_FORMAT_IMPL_ - -// The next four overloads handle the case where the operand being -// printed is a char/wchar_t pointer and the other operand is not a -// string/wstring object. In such cases, we just print the operand as -// a pointer to be safe. -#define GTEST_FORMAT_CHAR_PTR_IMPL_(CharType) \ - template \ - String FormatForComparisonFailureMessage(CharType* GTEST_CREF_WORKAROUND_ p, \ - const T&) { \ - return PrintToString(static_cast(p)); \ - } - -GTEST_FORMAT_CHAR_PTR_IMPL_(char) -GTEST_FORMAT_CHAR_PTR_IMPL_(const char) -GTEST_FORMAT_CHAR_PTR_IMPL_(wchar_t) -GTEST_FORMAT_CHAR_PTR_IMPL_(const wchar_t) - -#undef GTEST_FORMAT_CHAR_PTR_IMPL_ - -// Constructs and returns the message for an equality assertion -// (e.g. ASSERT_EQ, EXPECT_STREQ, etc) failure. -// -// The first four parameters are the expressions used in the assertion -// and their values, as strings. For example, for ASSERT_EQ(foo, bar) -// where foo is 5 and bar is 6, we have: -// -// expected_expression: "foo" -// actual_expression: "bar" -// expected_value: "5" -// actual_value: "6" -// -// The ignoring_case parameter is true iff the assertion is a -// *_STRCASEEQ*. When it's true, the string " (ignoring case)" will -// be inserted into the message. -GTEST_API_ AssertionResult EqFailure(const char* expected_expression, - const char* actual_expression, - const String& expected_value, - const String& actual_value, - bool ignoring_case); - -// Constructs a failure message for Boolean assertions such as EXPECT_TRUE. -GTEST_API_ String GetBoolAssertionFailureMessage( - const AssertionResult& assertion_result, - const char* expression_text, - const char* actual_predicate_value, - const char* expected_predicate_value); - -// This template class represents an IEEE floating-point number -// (either single-precision or double-precision, depending on the -// template parameters). -// -// The purpose of this class is to do more sophisticated number -// comparison. (Due to round-off error, etc, it's very unlikely that -// two floating-points will be equal exactly. Hence a naive -// comparison by the == operation often doesn't work.) -// -// Format of IEEE floating-point: -// -// The most-significant bit being the leftmost, an IEEE -// floating-point looks like -// -// sign_bit exponent_bits fraction_bits -// -// Here, sign_bit is a single bit that designates the sign of the -// number. -// -// For float, there are 8 exponent bits and 23 fraction bits. -// -// For double, there are 11 exponent bits and 52 fraction bits. -// -// More details can be found at -// http://en.wikipedia.org/wiki/IEEE_floating-point_standard. -// -// Template parameter: -// -// RawType: the raw floating-point type (either float or double) -template -class FloatingPoint { - public: - // Defines the unsigned integer type that has the same size as the - // floating point number. - typedef typename TypeWithSize::UInt Bits; - - // Constants. - - // # of bits in a number. - static const size_t kBitCount = 8*sizeof(RawType); - - // # of fraction bits in a number. - static const size_t kFractionBitCount = - std::numeric_limits::digits - 1; - - // # of exponent bits in a number. - static const size_t kExponentBitCount = kBitCount - 1 - kFractionBitCount; - - // The mask for the sign bit. - static const Bits kSignBitMask = static_cast(1) << (kBitCount - 1); - - // The mask for the fraction bits. - static const Bits kFractionBitMask = - ~static_cast(0) >> (kExponentBitCount + 1); - - // The mask for the exponent bits. - static const Bits kExponentBitMask = ~(kSignBitMask | kFractionBitMask); - - // How many ULP's (Units in the Last Place) we want to tolerate when - // comparing two numbers. The larger the value, the more error we - // allow. A 0 value means that two numbers must be exactly the same - // to be considered equal. - // - // The maximum error of a single floating-point operation is 0.5 - // units in the last place. On Intel CPU's, all floating-point - // calculations are done with 80-bit precision, while double has 64 - // bits. Therefore, 4 should be enough for ordinary use. - // - // See the following article for more details on ULP: - // http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm. - static const size_t kMaxUlps = 4; - - // Constructs a FloatingPoint from a raw floating-point number. - // - // On an Intel CPU, passing a non-normalized NAN (Not a Number) - // around may change its bits, although the new value is guaranteed - // to be also a NAN. Therefore, don't expect this constructor to - // preserve the bits in x when x is a NAN. - explicit FloatingPoint(const RawType& x) { u_.value_ = x; } - - // Static methods - - // Reinterprets a bit pattern as a floating-point number. - // - // This function is needed to test the AlmostEquals() method. - static RawType ReinterpretBits(const Bits bits) { - FloatingPoint fp(0); - fp.u_.bits_ = bits; - return fp.u_.value_; - } - - // Returns the floating-point number that represent positive infinity. - static RawType Infinity() { - return ReinterpretBits(kExponentBitMask); - } - - // Non-static methods - - // Returns the bits that represents this number. - const Bits &bits() const { return u_.bits_; } - - // Returns the exponent bits of this number. - Bits exponent_bits() const { return kExponentBitMask & u_.bits_; } - - // Returns the fraction bits of this number. - Bits fraction_bits() const { return kFractionBitMask & u_.bits_; } - - // Returns the sign bit of this number. - Bits sign_bit() const { return kSignBitMask & u_.bits_; } - - // Returns true iff this is NAN (not a number). - bool is_nan() const { - // It's a NAN if the exponent bits are all ones and the fraction - // bits are not entirely zeros. - return (exponent_bits() == kExponentBitMask) && (fraction_bits() != 0); - } - - // Returns true iff this number is at most kMaxUlps ULP's away from - // rhs. In particular, this function: - // - // - returns false if either number is (or both are) NAN. - // - treats really large numbers as almost equal to infinity. - // - thinks +0.0 and -0.0 are 0 DLP's apart. - bool AlmostEquals(const FloatingPoint& rhs) const { - // The IEEE standard says that any comparison operation involving - // a NAN must return false. - if (is_nan() || rhs.is_nan()) return false; - - return DistanceBetweenSignAndMagnitudeNumbers(u_.bits_, rhs.u_.bits_) - <= kMaxUlps; - } - - private: - // The data type used to store the actual floating-point number. - union FloatingPointUnion { - RawType value_; // The raw floating-point number. - Bits bits_; // The bits that represent the number. - }; - - // Converts an integer from the sign-and-magnitude representation to - // the biased representation. More precisely, let N be 2 to the - // power of (kBitCount - 1), an integer x is represented by the - // unsigned number x + N. - // - // For instance, - // - // -N + 1 (the most negative number representable using - // sign-and-magnitude) is represented by 1; - // 0 is represented by N; and - // N - 1 (the biggest number representable using - // sign-and-magnitude) is represented by 2N - 1. - // - // Read http://en.wikipedia.org/wiki/Signed_number_representations - // for more details on signed number representations. - static Bits SignAndMagnitudeToBiased(const Bits &sam) { - if (kSignBitMask & sam) { - // sam represents a negative number. - return ~sam + 1; - } else { - // sam represents a positive number. - return kSignBitMask | sam; - } - } - - // Given two numbers in the sign-and-magnitude representation, - // returns the distance between them as an unsigned number. - static Bits DistanceBetweenSignAndMagnitudeNumbers(const Bits &sam1, - const Bits &sam2) { - const Bits biased1 = SignAndMagnitudeToBiased(sam1); - const Bits biased2 = SignAndMagnitudeToBiased(sam2); - return (biased1 >= biased2) ? (biased1 - biased2) : (biased2 - biased1); - } - - FloatingPointUnion u_; -}; - -// Typedefs the instances of the FloatingPoint template class that we -// care to use. -typedef FloatingPoint Float; -typedef FloatingPoint Double; - -// In order to catch the mistake of putting tests that use different -// test fixture classes in the same test case, we need to assign -// unique IDs to fixture classes and compare them. The TypeId type is -// used to hold such IDs. The user should treat TypeId as an opaque -// type: the only operation allowed on TypeId values is to compare -// them for equality using the == operator. -typedef const void* TypeId; - -template -class TypeIdHelper { - public: - // dummy_ must not have a const type. Otherwise an overly eager - // compiler (e.g. MSVC 7.1 & 8.0) may try to merge - // TypeIdHelper::dummy_ for different Ts as an "optimization". - static bool dummy_; -}; - -template -bool TypeIdHelper::dummy_ = false; - -// GetTypeId() returns the ID of type T. Different values will be -// returned for different types. Calling the function twice with the -// same type argument is guaranteed to return the same ID. -template -TypeId GetTypeId() { - // The compiler is required to allocate a different - // TypeIdHelper::dummy_ variable for each T used to instantiate - // the template. Therefore, the address of dummy_ is guaranteed to - // be unique. - return &(TypeIdHelper::dummy_); -} - -// Returns the type ID of ::testing::Test. Always call this instead -// of GetTypeId< ::testing::Test>() to get the type ID of -// ::testing::Test, as the latter may give the wrong result due to a -// suspected linker bug when compiling Google Test as a Mac OS X -// framework. -GTEST_API_ TypeId GetTestTypeId(); - -// Defines the abstract factory interface that creates instances -// of a Test object. -class TestFactoryBase { - public: - virtual ~TestFactoryBase() {} - - // Creates a test instance to run. The instance is both created and destroyed - // within TestInfoImpl::Run() - virtual Test* CreateTest() = 0; - - protected: - TestFactoryBase() {} - - private: - GTEST_DISALLOW_COPY_AND_ASSIGN_(TestFactoryBase); -}; - -// This class provides implementation of TeastFactoryBase interface. -// It is used in TEST and TEST_F macros. -template -class TestFactoryImpl : public TestFactoryBase { - public: - virtual Test* CreateTest() { return new TestClass; } -}; - -#if GTEST_OS_WINDOWS - -// Predicate-formatters for implementing the HRESULT checking macros -// {ASSERT|EXPECT}_HRESULT_{SUCCEEDED|FAILED} -// We pass a long instead of HRESULT to avoid causing an -// include dependency for the HRESULT type. -GTEST_API_ AssertionResult IsHRESULTSuccess(const char* expr, - long hr); // NOLINT -GTEST_API_ AssertionResult IsHRESULTFailure(const char* expr, - long hr); // NOLINT - -#endif // GTEST_OS_WINDOWS - -// Types of SetUpTestCase() and TearDownTestCase() functions. -typedef void (*SetUpTestCaseFunc)(); -typedef void (*TearDownTestCaseFunc)(); - -// Creates a new TestInfo object and registers it with Google Test; -// returns the created object. -// -// Arguments: -// -// test_case_name: name of the test case -// name: name of the test -// type_param the name of the test's type parameter, or NULL if -// this is not a typed or a type-parameterized test. -// value_param text representation of the test's value parameter, -// or NULL if this is not a type-parameterized test. -// fixture_class_id: ID of the test fixture class -// set_up_tc: pointer to the function that sets up the test case -// tear_down_tc: pointer to the function that tears down the test case -// factory: pointer to the factory that creates a test object. -// The newly created TestInfo instance will assume -// ownership of the factory object. -GTEST_API_ TestInfo* MakeAndRegisterTestInfo( - const char* test_case_name, const char* name, - const char* type_param, - const char* value_param, - TypeId fixture_class_id, - SetUpTestCaseFunc set_up_tc, - TearDownTestCaseFunc tear_down_tc, - TestFactoryBase* factory); - -// If *pstr starts with the given prefix, modifies *pstr to be right -// past the prefix and returns true; otherwise leaves *pstr unchanged -// and returns false. None of pstr, *pstr, and prefix can be NULL. -GTEST_API_ bool SkipPrefix(const char* prefix, const char** pstr); - -#if GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P - -// State of the definition of a type-parameterized test case. -class GTEST_API_ TypedTestCasePState { - public: - TypedTestCasePState() : registered_(false) {} - - // Adds the given test name to defined_test_names_ and return true - // if the test case hasn't been registered; otherwise aborts the - // program. - bool AddTestName(const char* file, int line, const char* case_name, - const char* test_name) { - if (registered_) { - fprintf(stderr, "%s Test %s must be defined before " - "REGISTER_TYPED_TEST_CASE_P(%s, ...).\n", - FormatFileLocation(file, line).c_str(), test_name, case_name); - fflush(stderr); - posix::Abort(); - } - defined_test_names_.insert(test_name); - return true; - } - - // Verifies that registered_tests match the test names in - // defined_test_names_; returns registered_tests if successful, or - // aborts the program otherwise. - const char* VerifyRegisteredTestNames( - const char* file, int line, const char* registered_tests); - - private: - bool registered_; - ::std::set defined_test_names_; -}; - -// Skips to the first non-space char after the first comma in 'str'; -// returns NULL if no comma is found in 'str'. -inline const char* SkipComma(const char* str) { - const char* comma = strchr(str, ','); - if (comma == NULL) { - return NULL; - } - while (IsSpace(*(++comma))) {} - return comma; -} - -// Returns the prefix of 'str' before the first comma in it; returns -// the entire string if it contains no comma. -inline String GetPrefixUntilComma(const char* str) { - const char* comma = strchr(str, ','); - return comma == NULL ? String(str) : String(str, comma - str); -} - -// TypeParameterizedTest::Register() -// registers a list of type-parameterized tests with Google Test. The -// return value is insignificant - we just need to return something -// such that we can call this function in a namespace scope. -// -// Implementation note: The GTEST_TEMPLATE_ macro declares a template -// template parameter. It's defined in gtest-type-util.h. -template -class TypeParameterizedTest { - public: - // 'index' is the index of the test in the type list 'Types' - // specified in INSTANTIATE_TYPED_TEST_CASE_P(Prefix, TestCase, - // Types). Valid values for 'index' are [0, N - 1] where N is the - // length of Types. - static bool Register(const char* prefix, const char* case_name, - const char* test_names, int index) { - typedef typename Types::Head Type; - typedef Fixture FixtureClass; - typedef typename GTEST_BIND_(TestSel, Type) TestClass; - - // First, registers the first type-parameterized test in the type - // list. - MakeAndRegisterTestInfo( - String::Format("%s%s%s/%d", prefix, prefix[0] == '\0' ? "" : "/", - case_name, index).c_str(), - GetPrefixUntilComma(test_names).c_str(), - GetTypeName().c_str(), - NULL, // No value parameter. - GetTypeId(), - TestClass::SetUpTestCase, - TestClass::TearDownTestCase, - new TestFactoryImpl); - - // Next, recurses (at compile time) with the tail of the type list. - return TypeParameterizedTest - ::Register(prefix, case_name, test_names, index + 1); - } -}; - -// The base case for the compile time recursion. -template -class TypeParameterizedTest { - public: - static bool Register(const char* /*prefix*/, const char* /*case_name*/, - const char* /*test_names*/, int /*index*/) { - return true; - } -}; - -// TypeParameterizedTestCase::Register() -// registers *all combinations* of 'Tests' and 'Types' with Google -// Test. The return value is insignificant - we just need to return -// something such that we can call this function in a namespace scope. -template -class TypeParameterizedTestCase { - public: - static bool Register(const char* prefix, const char* case_name, - const char* test_names) { - typedef typename Tests::Head Head; - - // First, register the first test in 'Test' for each type in 'Types'. - TypeParameterizedTest::Register( - prefix, case_name, test_names, 0); - - // Next, recurses (at compile time) with the tail of the test list. - return TypeParameterizedTestCase - ::Register(prefix, case_name, SkipComma(test_names)); - } -}; - -// The base case for the compile time recursion. -template -class TypeParameterizedTestCase { - public: - static bool Register(const char* /*prefix*/, const char* /*case_name*/, - const char* /*test_names*/) { - return true; - } -}; - -#endif // GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P - -// Returns the current OS stack trace as a String. -// -// The maximum number of stack frames to be included is specified by -// the gtest_stack_trace_depth flag. The skip_count parameter -// specifies the number of top frames to be skipped, which doesn't -// count against the number of frames to be included. -// -// For example, if Foo() calls Bar(), which in turn calls -// GetCurrentOsStackTraceExceptTop(..., 1), Foo() will be included in -// the trace but Bar() and GetCurrentOsStackTraceExceptTop() won't. -GTEST_API_ String GetCurrentOsStackTraceExceptTop(UnitTest* unit_test, - int skip_count); - -// Helpers for suppressing warnings on unreachable code or constant -// condition. - -// Always returns true. -GTEST_API_ bool AlwaysTrue(); - -// Always returns false. -inline bool AlwaysFalse() { return !AlwaysTrue(); } - -// Helper for suppressing false warning from Clang on a const char* -// variable declared in a conditional expression always being NULL in -// the else branch. -struct GTEST_API_ ConstCharPtr { - ConstCharPtr(const char* str) : value(str) {} - operator bool() const { return true; } - const char* value; -}; - -// A simple Linear Congruential Generator for generating random -// numbers with a uniform distribution. Unlike rand() and srand(), it -// doesn't use global state (and therefore can't interfere with user -// code). Unlike rand_r(), it's portable. An LCG isn't very random, -// but it's good enough for our purposes. -class GTEST_API_ Random { - public: - static const UInt32 kMaxRange = 1u << 31; - - explicit Random(UInt32 seed) : state_(seed) {} - - void Reseed(UInt32 seed) { state_ = seed; } - - // Generates a random number from [0, range). Crashes if 'range' is - // 0 or greater than kMaxRange. - UInt32 Generate(UInt32 range); - - private: - UInt32 state_; - GTEST_DISALLOW_COPY_AND_ASSIGN_(Random); -}; - -// Defining a variable of type CompileAssertTypesEqual will cause a -// compiler error iff T1 and T2 are different types. -template -struct CompileAssertTypesEqual; - -template -struct CompileAssertTypesEqual { -}; - -// Removes the reference from a type if it is a reference type, -// otherwise leaves it unchanged. This is the same as -// tr1::remove_reference, which is not widely available yet. -template -struct RemoveReference { typedef T type; }; // NOLINT -template -struct RemoveReference { typedef T type; }; // NOLINT - -// A handy wrapper around RemoveReference that works when the argument -// T depends on template parameters. -#define GTEST_REMOVE_REFERENCE_(T) \ - typename ::testing::internal::RemoveReference::type - -// Removes const from a type if it is a const type, otherwise leaves -// it unchanged. This is the same as tr1::remove_const, which is not -// widely available yet. -template -struct RemoveConst { typedef T type; }; // NOLINT -template -struct RemoveConst { typedef T type; }; // NOLINT - -// MSVC 8.0, Sun C++, and IBM XL C++ have a bug which causes the above -// definition to fail to remove the const in 'const int[3]' and 'const -// char[3][4]'. The following specialization works around the bug. -// However, it causes trouble with GCC and thus needs to be -// conditionally compiled. -#if defined(_MSC_VER) || defined(__SUNPRO_CC) || defined(__IBMCPP__) -template -struct RemoveConst { - typedef typename RemoveConst::type type[N]; -}; -#endif - -// A handy wrapper around RemoveConst that works when the argument -// T depends on template parameters. -#define GTEST_REMOVE_CONST_(T) \ - typename ::testing::internal::RemoveConst::type - -// Turns const U&, U&, const U, and U all into U. -#define GTEST_REMOVE_REFERENCE_AND_CONST_(T) \ - GTEST_REMOVE_CONST_(GTEST_REMOVE_REFERENCE_(T)) - -// Adds reference to a type if it is not a reference type, -// otherwise leaves it unchanged. This is the same as -// tr1::add_reference, which is not widely available yet. -template -struct AddReference { typedef T& type; }; // NOLINT -template -struct AddReference { typedef T& type; }; // NOLINT - -// A handy wrapper around AddReference that works when the argument T -// depends on template parameters. -#define GTEST_ADD_REFERENCE_(T) \ - typename ::testing::internal::AddReference::type - -// Adds a reference to const on top of T as necessary. For example, -// it transforms -// -// char ==> const char& -// const char ==> const char& -// char& ==> const char& -// const char& ==> const char& -// -// The argument T must depend on some template parameters. -#define GTEST_REFERENCE_TO_CONST_(T) \ - GTEST_ADD_REFERENCE_(const GTEST_REMOVE_REFERENCE_(T)) - -// ImplicitlyConvertible::value is a compile-time bool -// constant that's true iff type From can be implicitly converted to -// type To. -template -class ImplicitlyConvertible { - private: - // We need the following helper functions only for their types. - // They have no implementations. - - // MakeFrom() is an expression whose type is From. We cannot simply - // use From(), as the type From may not have a public default - // constructor. - static From MakeFrom(); - - // These two functions are overloaded. Given an expression - // Helper(x), the compiler will pick the first version if x can be - // implicitly converted to type To; otherwise it will pick the - // second version. - // - // The first version returns a value of size 1, and the second - // version returns a value of size 2. Therefore, by checking the - // size of Helper(x), which can be done at compile time, we can tell - // which version of Helper() is used, and hence whether x can be - // implicitly converted to type To. - static char Helper(To); - static char (&Helper(...))[2]; // NOLINT - - // We have to put the 'public' section after the 'private' section, - // or MSVC refuses to compile the code. - public: - // MSVC warns about implicitly converting from double to int for - // possible loss of data, so we need to temporarily disable the - // warning. -#ifdef _MSC_VER -# pragma warning(push) // Saves the current warning state. -# pragma warning(disable:4244) // Temporarily disables warning 4244. - - static const bool value = - sizeof(Helper(ImplicitlyConvertible::MakeFrom())) == 1; -# pragma warning(pop) // Restores the warning state. -#elif defined(__BORLANDC__) - // C++Builder cannot use member overload resolution during template - // instantiation. The simplest workaround is to use its C++0x type traits - // functions (C++Builder 2009 and above only). - static const bool value = __is_convertible(From, To); -#else - static const bool value = - sizeof(Helper(ImplicitlyConvertible::MakeFrom())) == 1; -#endif // _MSV_VER -}; -template -const bool ImplicitlyConvertible::value; - -// IsAProtocolMessage::value is a compile-time bool constant that's -// true iff T is type ProtocolMessage, proto2::Message, or a subclass -// of those. -template -struct IsAProtocolMessage - : public bool_constant< - ImplicitlyConvertible::value || - ImplicitlyConvertible::value> { -}; - -// When the compiler sees expression IsContainerTest(0), if C is an -// STL-style container class, the first overload of IsContainerTest -// will be viable (since both C::iterator* and C::const_iterator* are -// valid types and NULL can be implicitly converted to them). It will -// be picked over the second overload as 'int' is a perfect match for -// the type of argument 0. If C::iterator or C::const_iterator is not -// a valid type, the first overload is not viable, and the second -// overload will be picked. Therefore, we can determine whether C is -// a container class by checking the type of IsContainerTest(0). -// The value of the expression is insignificant. -// -// Note that we look for both C::iterator and C::const_iterator. The -// reason is that C++ injects the name of a class as a member of the -// class itself (e.g. you can refer to class iterator as either -// 'iterator' or 'iterator::iterator'). If we look for C::iterator -// only, for example, we would mistakenly think that a class named -// iterator is an STL container. -// -// Also note that the simpler approach of overloading -// IsContainerTest(typename C::const_iterator*) and -// IsContainerTest(...) doesn't work with Visual Age C++ and Sun C++. -typedef int IsContainer; -template -IsContainer IsContainerTest(int /* dummy */, - typename C::iterator* /* it */ = NULL, - typename C::const_iterator* /* const_it */ = NULL) { - return 0; -} - -typedef char IsNotContainer; -template -IsNotContainer IsContainerTest(long /* dummy */) { return '\0'; } - -// EnableIf::type is void when 'Cond' is true, and -// undefined when 'Cond' is false. To use SFINAE to make a function -// overload only apply when a particular expression is true, add -// "typename EnableIf::type* = 0" as the last parameter. -template struct EnableIf; -template<> struct EnableIf { typedef void type; }; // NOLINT - -// Utilities for native arrays. - -// ArrayEq() compares two k-dimensional native arrays using the -// elements' operator==, where k can be any integer >= 0. When k is -// 0, ArrayEq() degenerates into comparing a single pair of values. - -template -bool ArrayEq(const T* lhs, size_t size, const U* rhs); - -// This generic version is used when k is 0. -template -inline bool ArrayEq(const T& lhs, const U& rhs) { return lhs == rhs; } - -// This overload is used when k >= 1. -template -inline bool ArrayEq(const T(&lhs)[N], const U(&rhs)[N]) { - return internal::ArrayEq(lhs, N, rhs); -} - -// This helper reduces code bloat. If we instead put its logic inside -// the previous ArrayEq() function, arrays with different sizes would -// lead to different copies of the template code. -template -bool ArrayEq(const T* lhs, size_t size, const U* rhs) { - for (size_t i = 0; i != size; i++) { - if (!internal::ArrayEq(lhs[i], rhs[i])) - return false; - } - return true; -} - -// Finds the first element in the iterator range [begin, end) that -// equals elem. Element may be a native array type itself. -template -Iter ArrayAwareFind(Iter begin, Iter end, const Element& elem) { - for (Iter it = begin; it != end; ++it) { - if (internal::ArrayEq(*it, elem)) - return it; - } - return end; -} - -// CopyArray() copies a k-dimensional native array using the elements' -// operator=, where k can be any integer >= 0. When k is 0, -// CopyArray() degenerates into copying a single value. - -template -void CopyArray(const T* from, size_t size, U* to); - -// This generic version is used when k is 0. -template -inline void CopyArray(const T& from, U* to) { *to = from; } - -// This overload is used when k >= 1. -template -inline void CopyArray(const T(&from)[N], U(*to)[N]) { - internal::CopyArray(from, N, *to); -} - -// This helper reduces code bloat. If we instead put its logic inside -// the previous CopyArray() function, arrays with different sizes -// would lead to different copies of the template code. -template -void CopyArray(const T* from, size_t size, U* to) { - for (size_t i = 0; i != size; i++) { - internal::CopyArray(from[i], to + i); - } -} - -// The relation between an NativeArray object (see below) and the -// native array it represents. -enum RelationToSource { - kReference, // The NativeArray references the native array. - kCopy // The NativeArray makes a copy of the native array and - // owns the copy. -}; - -// Adapts a native array to a read-only STL-style container. Instead -// of the complete STL container concept, this adaptor only implements -// members useful for Google Mock's container matchers. New members -// should be added as needed. To simplify the implementation, we only -// support Element being a raw type (i.e. having no top-level const or -// reference modifier). It's the client's responsibility to satisfy -// this requirement. Element can be an array type itself (hence -// multi-dimensional arrays are supported). -template -class NativeArray { - public: - // STL-style container typedefs. - typedef Element value_type; - typedef Element* iterator; - typedef const Element* const_iterator; - - // Constructs from a native array. - NativeArray(const Element* array, size_t count, RelationToSource relation) { - Init(array, count, relation); - } - - // Copy constructor. - NativeArray(const NativeArray& rhs) { - Init(rhs.array_, rhs.size_, rhs.relation_to_source_); - } - - ~NativeArray() { - // Ensures that the user doesn't instantiate NativeArray with a - // const or reference type. - static_cast(StaticAssertTypeEqHelper()); - if (relation_to_source_ == kCopy) - delete[] array_; - } - - // STL-style container methods. - size_t size() const { return size_; } - const_iterator begin() const { return array_; } - const_iterator end() const { return array_ + size_; } - bool operator==(const NativeArray& rhs) const { - return size() == rhs.size() && - ArrayEq(begin(), size(), rhs.begin()); - } - - private: - // Initializes this object; makes a copy of the input array if - // 'relation' is kCopy. - void Init(const Element* array, size_t a_size, RelationToSource relation) { - if (relation == kReference) { - array_ = array; - } else { - Element* const copy = new Element[a_size]; - CopyArray(array, a_size, copy); - array_ = copy; - } - size_ = a_size; - relation_to_source_ = relation; - } - - const Element* array_; - size_t size_; - RelationToSource relation_to_source_; - - GTEST_DISALLOW_ASSIGN_(NativeArray); -}; - -} // namespace internal -} // namespace testing - -#define GTEST_MESSAGE_AT_(file, line, message, result_type) \ - ::testing::internal::AssertHelper(result_type, file, line, message) \ - = ::testing::Message() - -#define GTEST_MESSAGE_(message, result_type) \ - GTEST_MESSAGE_AT_(__FILE__, __LINE__, message, result_type) - -#define GTEST_FATAL_FAILURE_(message) \ - return GTEST_MESSAGE_(message, ::testing::TestPartResult::kFatalFailure) - -#define GTEST_NONFATAL_FAILURE_(message) \ - GTEST_MESSAGE_(message, ::testing::TestPartResult::kNonFatalFailure) - -#define GTEST_SUCCESS_(message) \ - GTEST_MESSAGE_(message, ::testing::TestPartResult::kSuccess) - -// Suppresses MSVC warnings 4072 (unreachable code) for the code following -// statement if it returns or throws (or doesn't return or throw in some -// situations). -#define GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) \ - if (::testing::internal::AlwaysTrue()) { statement; } - -#define GTEST_TEST_THROW_(statement, expected_exception, fail) \ - GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ - if (::testing::internal::ConstCharPtr gtest_msg = "") { \ - bool gtest_caught_expected = false; \ - try { \ - GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ - } \ - catch (expected_exception const&) { \ - gtest_caught_expected = true; \ - } \ - catch (...) { \ - gtest_msg.value = \ - "Expected: " #statement " throws an exception of type " \ - #expected_exception ".\n Actual: it throws a different type."; \ - goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ - } \ - if (!gtest_caught_expected) { \ - gtest_msg.value = \ - "Expected: " #statement " throws an exception of type " \ - #expected_exception ".\n Actual: it throws nothing."; \ - goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ - } \ - } else \ - GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__): \ - fail(gtest_msg.value) - -#define GTEST_TEST_NO_THROW_(statement, fail) \ - GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ - if (::testing::internal::AlwaysTrue()) { \ - try { \ - GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ - } \ - catch (...) { \ - goto GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__); \ - } \ - } else \ - GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__): \ - fail("Expected: " #statement " doesn't throw an exception.\n" \ - " Actual: it throws.") - -#define GTEST_TEST_ANY_THROW_(statement, fail) \ - GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ - if (::testing::internal::AlwaysTrue()) { \ - bool gtest_caught_any = false; \ - try { \ - GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ - } \ - catch (...) { \ - gtest_caught_any = true; \ - } \ - if (!gtest_caught_any) { \ - goto GTEST_CONCAT_TOKEN_(gtest_label_testanythrow_, __LINE__); \ - } \ - } else \ - GTEST_CONCAT_TOKEN_(gtest_label_testanythrow_, __LINE__): \ - fail("Expected: " #statement " throws an exception.\n" \ - " Actual: it doesn't.") - - -// Implements Boolean test assertions such as EXPECT_TRUE. expression can be -// either a boolean expression or an AssertionResult. text is a textual -// represenation of expression as it was passed into the EXPECT_TRUE. -#define GTEST_TEST_BOOLEAN_(expression, text, actual, expected, fail) \ - GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ - if (const ::testing::AssertionResult gtest_ar_ = \ - ::testing::AssertionResult(expression)) \ - ; \ - else \ - fail(::testing::internal::GetBoolAssertionFailureMessage(\ - gtest_ar_, text, #actual, #expected).c_str()) - -#define GTEST_TEST_NO_FATAL_FAILURE_(statement, fail) \ - GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ - if (::testing::internal::AlwaysTrue()) { \ - ::testing::internal::HasNewFatalFailureHelper gtest_fatal_failure_checker; \ - GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ - if (gtest_fatal_failure_checker.has_new_fatal_failure()) { \ - goto GTEST_CONCAT_TOKEN_(gtest_label_testnofatal_, __LINE__); \ - } \ - } else \ - GTEST_CONCAT_TOKEN_(gtest_label_testnofatal_, __LINE__): \ - fail("Expected: " #statement " doesn't generate new fatal " \ - "failures in the current thread.\n" \ - " Actual: it does.") - -// Expands to the name of the class that implements the given test. -#define GTEST_TEST_CLASS_NAME_(test_case_name, test_name) \ - test_case_name##_##test_name##_Test - -// Helper macro for defining tests. -#define GTEST_TEST_(test_case_name, test_name, parent_class, parent_id)\ -class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) : public parent_class {\ - public:\ - GTEST_TEST_CLASS_NAME_(test_case_name, test_name)() {}\ - private:\ - virtual void TestBody();\ - static ::testing::TestInfo* const test_info_ GTEST_ATTRIBUTE_UNUSED_;\ - GTEST_DISALLOW_COPY_AND_ASSIGN_(\ - GTEST_TEST_CLASS_NAME_(test_case_name, test_name));\ -};\ -\ -::testing::TestInfo* const GTEST_TEST_CLASS_NAME_(test_case_name, test_name)\ - ::test_info_ =\ - ::testing::internal::MakeAndRegisterTestInfo(\ - #test_case_name, #test_name, NULL, NULL, \ - (parent_id), \ - parent_class::SetUpTestCase, \ - parent_class::TearDownTestCase, \ - new ::testing::internal::TestFactoryImpl<\ - GTEST_TEST_CLASS_NAME_(test_case_name, test_name)>);\ -void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody() - -#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ -// Copyright 2005, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Author: wan@google.com (Zhanyong Wan) -// -// The Google C++ Testing Framework (Google Test) -// -// This header file defines the public API for death tests. It is -// #included by gtest.h so a user doesn't need to include this -// directly. - -#ifndef GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ -#define GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ - -// Copyright 2005, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee) -// -// The Google C++ Testing Framework (Google Test) -// -// This header file defines internal utilities needed for implementing -// death tests. They are subject to change without notice. - -#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ -#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ - - -#include - -namespace testing { -namespace internal { - -GTEST_DECLARE_string_(internal_run_death_test); - -// Names of the flags (needed for parsing Google Test flags). -const char kDeathTestStyleFlag[] = "death_test_style"; -const char kDeathTestUseFork[] = "death_test_use_fork"; -const char kInternalRunDeathTestFlag[] = "internal_run_death_test"; - -#if GTEST_HAS_DEATH_TEST - -// DeathTest is a class that hides much of the complexity of the -// GTEST_DEATH_TEST_ macro. It is abstract; its static Create method -// returns a concrete class that depends on the prevailing death test -// style, as defined by the --gtest_death_test_style and/or -// --gtest_internal_run_death_test flags. - -// In describing the results of death tests, these terms are used with -// the corresponding definitions: -// -// exit status: The integer exit information in the format specified -// by wait(2) -// exit code: The integer code passed to exit(3), _exit(2), or -// returned from main() -class GTEST_API_ DeathTest { - public: - // Create returns false if there was an error determining the - // appropriate action to take for the current death test; for example, - // if the gtest_death_test_style flag is set to an invalid value. - // The LastMessage method will return a more detailed message in that - // case. Otherwise, the DeathTest pointer pointed to by the "test" - // argument is set. If the death test should be skipped, the pointer - // is set to NULL; otherwise, it is set to the address of a new concrete - // DeathTest object that controls the execution of the current test. - static bool Create(const char* statement, const RE* regex, - const char* file, int line, DeathTest** test); - DeathTest(); - virtual ~DeathTest() { } - - // A helper class that aborts a death test when it's deleted. - class ReturnSentinel { - public: - explicit ReturnSentinel(DeathTest* test) : test_(test) { } - ~ReturnSentinel() { test_->Abort(TEST_ENCOUNTERED_RETURN_STATEMENT); } - private: - DeathTest* const test_; - GTEST_DISALLOW_COPY_AND_ASSIGN_(ReturnSentinel); - } GTEST_ATTRIBUTE_UNUSED_; - - // An enumeration of possible roles that may be taken when a death - // test is encountered. EXECUTE means that the death test logic should - // be executed immediately. OVERSEE means that the program should prepare - // the appropriate environment for a child process to execute the death - // test, then wait for it to complete. - enum TestRole { OVERSEE_TEST, EXECUTE_TEST }; - - // An enumeration of the three reasons that a test might be aborted. - enum AbortReason { - TEST_ENCOUNTERED_RETURN_STATEMENT, - TEST_THREW_EXCEPTION, - TEST_DID_NOT_DIE - }; - - // Assumes one of the above roles. - virtual TestRole AssumeRole() = 0; - - // Waits for the death test to finish and returns its status. - virtual int Wait() = 0; - - // Returns true if the death test passed; that is, the test process - // exited during the test, its exit status matches a user-supplied - // predicate, and its stderr output matches a user-supplied regular - // expression. - // The user-supplied predicate may be a macro expression rather - // than a function pointer or functor, or else Wait and Passed could - // be combined. - virtual bool Passed(bool exit_status_ok) = 0; - - // Signals that the death test did not die as expected. - virtual void Abort(AbortReason reason) = 0; - - // Returns a human-readable outcome message regarding the outcome of - // the last death test. - static const char* LastMessage(); - - static void set_last_death_test_message(const String& message); - - private: - // A string containing a description of the outcome of the last death test. - static String last_death_test_message_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(DeathTest); -}; - -// Factory interface for death tests. May be mocked out for testing. -class DeathTestFactory { - public: - virtual ~DeathTestFactory() { } - virtual bool Create(const char* statement, const RE* regex, - const char* file, int line, DeathTest** test) = 0; -}; - -// A concrete DeathTestFactory implementation for normal use. -class DefaultDeathTestFactory : public DeathTestFactory { - public: - virtual bool Create(const char* statement, const RE* regex, - const char* file, int line, DeathTest** test); -}; - -// Returns true if exit_status describes a process that was terminated -// by a signal, or exited normally with a nonzero exit code. -GTEST_API_ bool ExitedUnsuccessfully(int exit_status); - -// Traps C++ exceptions escaping statement and reports them as test -// failures. Note that trapping SEH exceptions is not implemented here. -# if GTEST_HAS_EXCEPTIONS -# define GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, death_test) \ - try { \ - GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ - } catch (const ::std::exception& gtest_exception) { \ - fprintf(\ - stderr, \ - "\n%s: Caught std::exception-derived exception escaping the " \ - "death test statement. Exception message: %s\n", \ - ::testing::internal::FormatFileLocation(__FILE__, __LINE__).c_str(), \ - gtest_exception.what()); \ - fflush(stderr); \ - death_test->Abort(::testing::internal::DeathTest::TEST_THREW_EXCEPTION); \ - } catch (...) { \ - death_test->Abort(::testing::internal::DeathTest::TEST_THREW_EXCEPTION); \ - } - -# else -# define GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, death_test) \ - GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) - -# endif - -// This macro is for implementing ASSERT_DEATH*, EXPECT_DEATH*, -// ASSERT_EXIT*, and EXPECT_EXIT*. -# define GTEST_DEATH_TEST_(statement, predicate, regex, fail) \ - GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ - if (::testing::internal::AlwaysTrue()) { \ - const ::testing::internal::RE& gtest_regex = (regex); \ - ::testing::internal::DeathTest* gtest_dt; \ - if (!::testing::internal::DeathTest::Create(#statement, >est_regex, \ - __FILE__, __LINE__, >est_dt)) { \ - goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \ - } \ - if (gtest_dt != NULL) { \ - ::testing::internal::scoped_ptr< ::testing::internal::DeathTest> \ - gtest_dt_ptr(gtest_dt); \ - switch (gtest_dt->AssumeRole()) { \ - case ::testing::internal::DeathTest::OVERSEE_TEST: \ - if (!gtest_dt->Passed(predicate(gtest_dt->Wait()))) { \ - goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \ - } \ - break; \ - case ::testing::internal::DeathTest::EXECUTE_TEST: { \ - ::testing::internal::DeathTest::ReturnSentinel \ - gtest_sentinel(gtest_dt); \ - GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, gtest_dt); \ - gtest_dt->Abort(::testing::internal::DeathTest::TEST_DID_NOT_DIE); \ - break; \ - } \ - default: \ - break; \ - } \ - } \ - } else \ - GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__): \ - fail(::testing::internal::DeathTest::LastMessage()) -// The symbol "fail" here expands to something into which a message -// can be streamed. - -// A class representing the parsed contents of the -// --gtest_internal_run_death_test flag, as it existed when -// RUN_ALL_TESTS was called. -class InternalRunDeathTestFlag { - public: - InternalRunDeathTestFlag(const String& a_file, - int a_line, - int an_index, - int a_write_fd) - : file_(a_file), line_(a_line), index_(an_index), - write_fd_(a_write_fd) {} - - ~InternalRunDeathTestFlag() { - if (write_fd_ >= 0) - posix::Close(write_fd_); - } - - String file() const { return file_; } - int line() const { return line_; } - int index() const { return index_; } - int write_fd() const { return write_fd_; } - - private: - String file_; - int line_; - int index_; - int write_fd_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(InternalRunDeathTestFlag); -}; - -// Returns a newly created InternalRunDeathTestFlag object with fields -// initialized from the GTEST_FLAG(internal_run_death_test) flag if -// the flag is specified; otherwise returns NULL. -InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag(); - -#else // GTEST_HAS_DEATH_TEST - -// This macro is used for implementing macros such as -// EXPECT_DEATH_IF_SUPPORTED and ASSERT_DEATH_IF_SUPPORTED on systems where -// death tests are not supported. Those macros must compile on such systems -// iff EXPECT_DEATH and ASSERT_DEATH compile with the same parameters on -// systems that support death tests. This allows one to write such a macro -// on a system that does not support death tests and be sure that it will -// compile on a death-test supporting system. -// -// Parameters: -// statement - A statement that a macro such as EXPECT_DEATH would test -// for program termination. This macro has to make sure this -// statement is compiled but not executed, to ensure that -// EXPECT_DEATH_IF_SUPPORTED compiles with a certain -// parameter iff EXPECT_DEATH compiles with it. -// regex - A regex that a macro such as EXPECT_DEATH would use to test -// the output of statement. This parameter has to be -// compiled but not evaluated by this macro, to ensure that -// this macro only accepts expressions that a macro such as -// EXPECT_DEATH would accept. -// terminator - Must be an empty statement for EXPECT_DEATH_IF_SUPPORTED -// and a return statement for ASSERT_DEATH_IF_SUPPORTED. -// This ensures that ASSERT_DEATH_IF_SUPPORTED will not -// compile inside functions where ASSERT_DEATH doesn't -// compile. -// -// The branch that has an always false condition is used to ensure that -// statement and regex are compiled (and thus syntactically correct) but -// never executed. The unreachable code macro protects the terminator -// statement from generating an 'unreachable code' warning in case -// statement unconditionally returns or throws. The Message constructor at -// the end allows the syntax of streaming additional messages into the -// macro, for compilational compatibility with EXPECT_DEATH/ASSERT_DEATH. -# define GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, terminator) \ - GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ - if (::testing::internal::AlwaysTrue()) { \ - GTEST_LOG_(WARNING) \ - << "Death tests are not supported on this platform.\n" \ - << "Statement '" #statement "' cannot be verified."; \ - } else if (::testing::internal::AlwaysFalse()) { \ - ::testing::internal::RE::PartialMatch(".*", (regex)); \ - GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ - terminator; \ - } else \ - ::testing::Message() - -#endif // GTEST_HAS_DEATH_TEST - -} // namespace internal -} // namespace testing - -#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ - -namespace testing { - -// This flag controls the style of death tests. Valid values are "threadsafe", -// meaning that the death test child process will re-execute the test binary -// from the start, running only a single death test, or "fast", -// meaning that the child process will execute the test logic immediately -// after forking. -GTEST_DECLARE_string_(death_test_style); - -#if GTEST_HAS_DEATH_TEST - -// The following macros are useful for writing death tests. - -// Here's what happens when an ASSERT_DEATH* or EXPECT_DEATH* is -// executed: -// -// 1. It generates a warning if there is more than one active -// thread. This is because it's safe to fork() or clone() only -// when there is a single thread. -// -// 2. The parent process clone()s a sub-process and runs the death -// test in it; the sub-process exits with code 0 at the end of the -// death test, if it hasn't exited already. -// -// 3. The parent process waits for the sub-process to terminate. -// -// 4. The parent process checks the exit code and error message of -// the sub-process. -// -// Examples: -// -// ASSERT_DEATH(server.SendMessage(56, "Hello"), "Invalid port number"); -// for (int i = 0; i < 5; i++) { -// EXPECT_DEATH(server.ProcessRequest(i), -// "Invalid request .* in ProcessRequest()") -// << "Failed to die on request " << i); -// } -// -// ASSERT_EXIT(server.ExitNow(), ::testing::ExitedWithCode(0), "Exiting"); -// -// bool KilledBySIGHUP(int exit_code) { -// return WIFSIGNALED(exit_code) && WTERMSIG(exit_code) == SIGHUP; -// } -// -// ASSERT_EXIT(client.HangUpServer(), KilledBySIGHUP, "Hanging up!"); -// -// On the regular expressions used in death tests: -// -// On POSIX-compliant systems (*nix), we use the library, -// which uses the POSIX extended regex syntax. -// -// On other platforms (e.g. Windows), we only support a simple regex -// syntax implemented as part of Google Test. This limited -// implementation should be enough most of the time when writing -// death tests; though it lacks many features you can find in PCRE -// or POSIX extended regex syntax. For example, we don't support -// union ("x|y"), grouping ("(xy)"), brackets ("[xy]"), and -// repetition count ("x{5,7}"), among others. -// -// Below is the syntax that we do support. We chose it to be a -// subset of both PCRE and POSIX extended regex, so it's easy to -// learn wherever you come from. In the following: 'A' denotes a -// literal character, period (.), or a single \\ escape sequence; -// 'x' and 'y' denote regular expressions; 'm' and 'n' are for -// natural numbers. -// -// c matches any literal character c -// \\d matches any decimal digit -// \\D matches any character that's not a decimal digit -// \\f matches \f -// \\n matches \n -// \\r matches \r -// \\s matches any ASCII whitespace, including \n -// \\S matches any character that's not a whitespace -// \\t matches \t -// \\v matches \v -// \\w matches any letter, _, or decimal digit -// \\W matches any character that \\w doesn't match -// \\c matches any literal character c, which must be a punctuation -// . matches any single character except \n -// A? matches 0 or 1 occurrences of A -// A* matches 0 or many occurrences of A -// A+ matches 1 or many occurrences of A -// ^ matches the beginning of a string (not that of each line) -// $ matches the end of a string (not that of each line) -// xy matches x followed by y -// -// If you accidentally use PCRE or POSIX extended regex features -// not implemented by us, you will get a run-time failure. In that -// case, please try to rewrite your regular expression within the -// above syntax. -// -// This implementation is *not* meant to be as highly tuned or robust -// as a compiled regex library, but should perform well enough for a -// death test, which already incurs significant overhead by launching -// a child process. -// -// Known caveats: -// -// A "threadsafe" style death test obtains the path to the test -// program from argv[0] and re-executes it in the sub-process. For -// simplicity, the current implementation doesn't search the PATH -// when launching the sub-process. This means that the user must -// invoke the test program via a path that contains at least one -// path separator (e.g. path/to/foo_test and -// /absolute/path/to/bar_test are fine, but foo_test is not). This -// is rarely a problem as people usually don't put the test binary -// directory in PATH. -// -// TODO(wan@google.com): make thread-safe death tests search the PATH. - -// Asserts that a given statement causes the program to exit, with an -// integer exit status that satisfies predicate, and emitting error output -// that matches regex. -# define ASSERT_EXIT(statement, predicate, regex) \ - GTEST_DEATH_TEST_(statement, predicate, regex, GTEST_FATAL_FAILURE_) - -// Like ASSERT_EXIT, but continues on to successive tests in the -// test case, if any: -# define EXPECT_EXIT(statement, predicate, regex) \ - GTEST_DEATH_TEST_(statement, predicate, regex, GTEST_NONFATAL_FAILURE_) - -// Asserts that a given statement causes the program to exit, either by -// explicitly exiting with a nonzero exit code or being killed by a -// signal, and emitting error output that matches regex. -# define ASSERT_DEATH(statement, regex) \ - ASSERT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, regex) - -// Like ASSERT_DEATH, but continues on to successive tests in the -// test case, if any: -# define EXPECT_DEATH(statement, regex) \ - EXPECT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, regex) - -// Two predicate classes that can be used in {ASSERT,EXPECT}_EXIT*: - -// Tests that an exit code describes a normal exit with a given exit code. -class GTEST_API_ ExitedWithCode { - public: - explicit ExitedWithCode(int exit_code); - bool operator()(int exit_status) const; - private: - // No implementation - assignment is unsupported. - void operator=(const ExitedWithCode& other); - - const int exit_code_; -}; - -# if !GTEST_OS_WINDOWS -// Tests that an exit code describes an exit due to termination by a -// given signal. -class GTEST_API_ KilledBySignal { - public: - explicit KilledBySignal(int signum); - bool operator()(int exit_status) const; - private: - const int signum_; -}; -# endif // !GTEST_OS_WINDOWS - -// EXPECT_DEBUG_DEATH asserts that the given statements die in debug mode. -// The death testing framework causes this to have interesting semantics, -// since the sideeffects of the call are only visible in opt mode, and not -// in debug mode. -// -// In practice, this can be used to test functions that utilize the -// LOG(DFATAL) macro using the following style: -// -// int DieInDebugOr12(int* sideeffect) { -// if (sideeffect) { -// *sideeffect = 12; -// } -// LOG(DFATAL) << "death"; -// return 12; -// } -// -// TEST(TestCase, TestDieOr12WorksInDgbAndOpt) { -// int sideeffect = 0; -// // Only asserts in dbg. -// EXPECT_DEBUG_DEATH(DieInDebugOr12(&sideeffect), "death"); -// -// #ifdef NDEBUG -// // opt-mode has sideeffect visible. -// EXPECT_EQ(12, sideeffect); -// #else -// // dbg-mode no visible sideeffect. -// EXPECT_EQ(0, sideeffect); -// #endif -// } -// -// This will assert that DieInDebugReturn12InOpt() crashes in debug -// mode, usually due to a DCHECK or LOG(DFATAL), but returns the -// appropriate fallback value (12 in this case) in opt mode. If you -// need to test that a function has appropriate side-effects in opt -// mode, include assertions against the side-effects. A general -// pattern for this is: -// -// EXPECT_DEBUG_DEATH({ -// // Side-effects here will have an effect after this statement in -// // opt mode, but none in debug mode. -// EXPECT_EQ(12, DieInDebugOr12(&sideeffect)); -// }, "death"); -// -# ifdef NDEBUG - -# define EXPECT_DEBUG_DEATH(statement, regex) \ - do { statement; } while (::testing::internal::AlwaysFalse()) - -# define ASSERT_DEBUG_DEATH(statement, regex) \ - do { statement; } while (::testing::internal::AlwaysFalse()) - -# else - -# define EXPECT_DEBUG_DEATH(statement, regex) \ - EXPECT_DEATH(statement, regex) - -# define ASSERT_DEBUG_DEATH(statement, regex) \ - ASSERT_DEATH(statement, regex) - -# endif // NDEBUG for EXPECT_DEBUG_DEATH -#endif // GTEST_HAS_DEATH_TEST - -// EXPECT_DEATH_IF_SUPPORTED(statement, regex) and -// ASSERT_DEATH_IF_SUPPORTED(statement, regex) expand to real death tests if -// death tests are supported; otherwise they just issue a warning. This is -// useful when you are combining death test assertions with normal test -// assertions in one test. -#if GTEST_HAS_DEATH_TEST -# define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \ - EXPECT_DEATH(statement, regex) -# define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \ - ASSERT_DEATH(statement, regex) -#else -# define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \ - GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, ) -# define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \ - GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, return) -#endif - -} // namespace testing - -#endif // GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ -// Copyright 2005, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Author: wan@google.com (Zhanyong Wan) -// -// The Google C++ Testing Framework (Google Test) -// -// This header file defines the Message class. -// -// IMPORTANT NOTE: Due to limitation of the C++ language, we have to -// leave some internal implementation details in this header file. -// They are clearly marked by comments like this: -// -// // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. -// -// Such code is NOT meant to be used by a user directly, and is subject -// to CHANGE WITHOUT NOTICE. Therefore DO NOT DEPEND ON IT in a user -// program! - -#ifndef GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ -#define GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ - -#include - - -namespace testing { - -// The Message class works like an ostream repeater. -// -// Typical usage: -// -// 1. You stream a bunch of values to a Message object. -// It will remember the text in a stringstream. -// 2. Then you stream the Message object to an ostream. -// This causes the text in the Message to be streamed -// to the ostream. -// -// For example; -// -// testing::Message foo; -// foo << 1 << " != " << 2; -// std::cout << foo; -// -// will print "1 != 2". -// -// Message is not intended to be inherited from. In particular, its -// destructor is not virtual. -// -// Note that stringstream behaves differently in gcc and in MSVC. You -// can stream a NULL char pointer to it in the former, but not in the -// latter (it causes an access violation if you do). The Message -// class hides this difference by treating a NULL char pointer as -// "(null)". -class GTEST_API_ Message { - private: - // The type of basic IO manipulators (endl, ends, and flush) for - // narrow streams. - typedef std::ostream& (*BasicNarrowIoManip)(std::ostream&); - - public: - // Constructs an empty Message. - // We allocate the stringstream separately because otherwise each use of - // ASSERT/EXPECT in a procedure adds over 200 bytes to the procedure's - // stack frame leading to huge stack frames in some cases; gcc does not reuse - // the stack space. - Message() : ss_(new ::std::stringstream) { - // By default, we want there to be enough precision when printing - // a double to a Message. - *ss_ << std::setprecision(std::numeric_limits::digits10 + 2); - } - - // Copy constructor. - Message(const Message& msg) : ss_(new ::std::stringstream) { // NOLINT - *ss_ << msg.GetString(); - } - - // Constructs a Message from a C-string. - explicit Message(const char* str) : ss_(new ::std::stringstream) { - *ss_ << str; - } - -#if GTEST_OS_SYMBIAN - // Streams a value (either a pointer or not) to this object. - template - inline Message& operator <<(const T& value) { - StreamHelper(typename internal::is_pointer::type(), value); - return *this; - } -#else - // Streams a non-pointer value to this object. - template - inline Message& operator <<(const T& val) { - ::GTestStreamToHelper(ss_.get(), val); - return *this; - } - - // Streams a pointer value to this object. - // - // This function is an overload of the previous one. When you - // stream a pointer to a Message, this definition will be used as it - // is more specialized. (The C++ Standard, section - // [temp.func.order].) If you stream a non-pointer, then the - // previous definition will be used. - // - // The reason for this overload is that streaming a NULL pointer to - // ostream is undefined behavior. Depending on the compiler, you - // may get "0", "(nil)", "(null)", or an access violation. To - // ensure consistent result across compilers, we always treat NULL - // as "(null)". - template - inline Message& operator <<(T* const& pointer) { // NOLINT - if (pointer == NULL) { - *ss_ << "(null)"; - } else { - ::GTestStreamToHelper(ss_.get(), pointer); - } - return *this; - } -#endif // GTEST_OS_SYMBIAN - - // Since the basic IO manipulators are overloaded for both narrow - // and wide streams, we have to provide this specialized definition - // of operator <<, even though its body is the same as the - // templatized version above. Without this definition, streaming - // endl or other basic IO manipulators to Message will confuse the - // compiler. - Message& operator <<(BasicNarrowIoManip val) { - *ss_ << val; - return *this; - } - - // Instead of 1/0, we want to see true/false for bool values. - Message& operator <<(bool b) { - return *this << (b ? "true" : "false"); - } - - // These two overloads allow streaming a wide C string to a Message - // using the UTF-8 encoding. - Message& operator <<(const wchar_t* wide_c_str) { - return *this << internal::String::ShowWideCString(wide_c_str); - } - Message& operator <<(wchar_t* wide_c_str) { - return *this << internal::String::ShowWideCString(wide_c_str); - } - -#if GTEST_HAS_STD_WSTRING - // Converts the given wide string to a narrow string using the UTF-8 - // encoding, and streams the result to this Message object. - Message& operator <<(const ::std::wstring& wstr); -#endif // GTEST_HAS_STD_WSTRING - -#if GTEST_HAS_GLOBAL_WSTRING - // Converts the given wide string to a narrow string using the UTF-8 - // encoding, and streams the result to this Message object. - Message& operator <<(const ::wstring& wstr); -#endif // GTEST_HAS_GLOBAL_WSTRING - - // Gets the text streamed to this object so far as a String. - // Each '\0' character in the buffer is replaced with "\\0". - // - // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. - internal::String GetString() const { - return internal::StringStreamToString(ss_.get()); - } - - private: - -#if GTEST_OS_SYMBIAN - // These are needed as the Nokia Symbian Compiler cannot decide between - // const T& and const T* in a function template. The Nokia compiler _can_ - // decide between class template specializations for T and T*, so a - // tr1::type_traits-like is_pointer works, and we can overload on that. - template - inline void StreamHelper(internal::true_type /*dummy*/, T* pointer) { - if (pointer == NULL) { - *ss_ << "(null)"; - } else { - ::GTestStreamToHelper(ss_.get(), pointer); - } - } - template - inline void StreamHelper(internal::false_type /*dummy*/, const T& value) { - ::GTestStreamToHelper(ss_.get(), value); - } -#endif // GTEST_OS_SYMBIAN - - // We'll hold the text streamed to this object here. - const internal::scoped_ptr< ::std::stringstream> ss_; - - // We declare (but don't implement) this to prevent the compiler - // from implementing the assignment operator. - void operator=(const Message&); -}; - -// Streams a Message to an ostream. -inline std::ostream& operator <<(std::ostream& os, const Message& sb) { - return os << sb.GetString(); -} - -} // namespace testing - -#endif // GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ -// This file was GENERATED by command: -// pump.py gtest-param-test.h.pump -// DO NOT EDIT BY HAND!!! - -// Copyright 2008, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Authors: vladl@google.com (Vlad Losev) -// -// Macros and functions for implementing parameterized tests -// in Google C++ Testing Framework (Google Test) -// -// This file is generated by a SCRIPT. DO NOT EDIT BY HAND! -// -#ifndef GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_ -#define GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_ - - -// Value-parameterized tests allow you to test your code with different -// parameters without writing multiple copies of the same test. -// -// Here is how you use value-parameterized tests: - -#if 0 - -// To write value-parameterized tests, first you should define a fixture -// class. It is usually derived from testing::TestWithParam (see below for -// another inheritance scheme that's sometimes useful in more complicated -// class hierarchies), where the type of your parameter values. -// TestWithParam is itself derived from testing::Test. T can be any -// copyable type. If it's a raw pointer, you are responsible for managing the -// lifespan of the pointed values. - -class FooTest : public ::testing::TestWithParam { - // You can implement all the usual class fixture members here. -}; - -// Then, use the TEST_P macro to define as many parameterized tests -// for this fixture as you want. The _P suffix is for "parameterized" -// or "pattern", whichever you prefer to think. - -TEST_P(FooTest, DoesBlah) { - // Inside a test, access the test parameter with the GetParam() method - // of the TestWithParam class: - EXPECT_TRUE(foo.Blah(GetParam())); - ... -} - -TEST_P(FooTest, HasBlahBlah) { - ... -} - -// Finally, you can use INSTANTIATE_TEST_CASE_P to instantiate the test -// case with any set of parameters you want. Google Test defines a number -// of functions for generating test parameters. They return what we call -// (surprise!) parameter generators. Here is a summary of them, which -// are all in the testing namespace: -// -// -// Range(begin, end [, step]) - Yields values {begin, begin+step, -// begin+step+step, ...}. The values do not -// include end. step defaults to 1. -// Values(v1, v2, ..., vN) - Yields values {v1, v2, ..., vN}. -// ValuesIn(container) - Yields values from a C-style array, an STL -// ValuesIn(begin,end) container, or an iterator range [begin, end). -// Bool() - Yields sequence {false, true}. -// Combine(g1, g2, ..., gN) - Yields all combinations (the Cartesian product -// for the math savvy) of the values generated -// by the N generators. -// -// For more details, see comments at the definitions of these functions below -// in this file. -// -// The following statement will instantiate tests from the FooTest test case -// each with parameter values "meeny", "miny", and "moe". - -INSTANTIATE_TEST_CASE_P(InstantiationName, - FooTest, - Values("meeny", "miny", "moe")); - -// To distinguish different instances of the pattern, (yes, you -// can instantiate it more then once) the first argument to the -// INSTANTIATE_TEST_CASE_P macro is a prefix that will be added to the -// actual test case name. Remember to pick unique prefixes for different -// instantiations. The tests from the instantiation above will have -// these names: -// -// * InstantiationName/FooTest.DoesBlah/0 for "meeny" -// * InstantiationName/FooTest.DoesBlah/1 for "miny" -// * InstantiationName/FooTest.DoesBlah/2 for "moe" -// * InstantiationName/FooTest.HasBlahBlah/0 for "meeny" -// * InstantiationName/FooTest.HasBlahBlah/1 for "miny" -// * InstantiationName/FooTest.HasBlahBlah/2 for "moe" -// -// You can use these names in --gtest_filter. -// -// This statement will instantiate all tests from FooTest again, each -// with parameter values "cat" and "dog": - -const char* pets[] = {"cat", "dog"}; -INSTANTIATE_TEST_CASE_P(AnotherInstantiationName, FooTest, ValuesIn(pets)); - -// The tests from the instantiation above will have these names: -// -// * AnotherInstantiationName/FooTest.DoesBlah/0 for "cat" -// * AnotherInstantiationName/FooTest.DoesBlah/1 for "dog" -// * AnotherInstantiationName/FooTest.HasBlahBlah/0 for "cat" -// * AnotherInstantiationName/FooTest.HasBlahBlah/1 for "dog" -// -// Please note that INSTANTIATE_TEST_CASE_P will instantiate all tests -// in the given test case, whether their definitions come before or -// AFTER the INSTANTIATE_TEST_CASE_P statement. -// -// Please also note that generator expressions (including parameters to the -// generators) are evaluated in InitGoogleTest(), after main() has started. -// This allows the user on one hand, to adjust generator parameters in order -// to dynamically determine a set of tests to run and on the other hand, -// give the user a chance to inspect the generated tests with Google Test -// reflection API before RUN_ALL_TESTS() is executed. -// -// You can see samples/sample7_unittest.cc and samples/sample8_unittest.cc -// for more examples. -// -// In the future, we plan to publish the API for defining new parameter -// generators. But for now this interface remains part of the internal -// implementation and is subject to change. -// -// -// A parameterized test fixture must be derived from testing::Test and from -// testing::WithParamInterface, where T is the type of the parameter -// values. Inheriting from TestWithParam satisfies that requirement because -// TestWithParam inherits from both Test and WithParamInterface. In more -// complicated hierarchies, however, it is occasionally useful to inherit -// separately from Test and WithParamInterface. For example: - -class BaseTest : public ::testing::Test { - // You can inherit all the usual members for a non-parameterized test - // fixture here. -}; - -class DerivedTest : public BaseTest, public ::testing::WithParamInterface { - // The usual test fixture members go here too. -}; - -TEST_F(BaseTest, HasFoo) { - // This is an ordinary non-parameterized test. -} - -TEST_P(DerivedTest, DoesBlah) { - // GetParam works just the same here as if you inherit from TestWithParam. - EXPECT_TRUE(foo.Blah(GetParam())); -} - -#endif // 0 - - -#if !GTEST_OS_SYMBIAN -# include -#endif - -// scripts/fuse_gtest.py depends on gtest's own header being #included -// *unconditionally*. Therefore these #includes cannot be moved -// inside #if GTEST_HAS_PARAM_TEST. -// Copyright 2008 Google Inc. -// All Rights Reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Author: vladl@google.com (Vlad Losev) - -// Type and function utilities for implementing parameterized tests. - -#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_ -#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_ - -#include -#include -#include - -// scripts/fuse_gtest.py depends on gtest's own header being #included -// *unconditionally*. Therefore these #includes cannot be moved -// inside #if GTEST_HAS_PARAM_TEST. -// Copyright 2003 Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Authors: Dan Egnor (egnor@google.com) -// -// A "smart" pointer type with reference tracking. Every pointer to a -// particular object is kept on a circular linked list. When the last pointer -// to an object is destroyed or reassigned, the object is deleted. -// -// Used properly, this deletes the object when the last reference goes away. -// There are several caveats: -// - Like all reference counting schemes, cycles lead to leaks. -// - Each smart pointer is actually two pointers (8 bytes instead of 4). -// - Every time a pointer is assigned, the entire list of pointers to that -// object is traversed. This class is therefore NOT SUITABLE when there -// will often be more than two or three pointers to a particular object. -// - References are only tracked as long as linked_ptr<> objects are copied. -// If a linked_ptr<> is converted to a raw pointer and back, BAD THINGS -// will happen (double deletion). -// -// A good use of this class is storing object references in STL containers. -// You can safely put linked_ptr<> in a vector<>. -// Other uses may not be as good. -// -// Note: If you use an incomplete type with linked_ptr<>, the class -// *containing* linked_ptr<> must have a constructor and destructor (even -// if they do nothing!). -// -// Bill Gibbons suggested we use something like this. -// -// Thread Safety: -// Unlike other linked_ptr implementations, in this implementation -// a linked_ptr object is thread-safe in the sense that: -// - it's safe to copy linked_ptr objects concurrently, -// - it's safe to copy *from* a linked_ptr and read its underlying -// raw pointer (e.g. via get()) concurrently, and -// - it's safe to write to two linked_ptrs that point to the same -// shared object concurrently. -// TODO(wan@google.com): rename this to safe_linked_ptr to avoid -// confusion with normal linked_ptr. - -#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_LINKED_PTR_H_ -#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_LINKED_PTR_H_ - -#include -#include - - -namespace testing { -namespace internal { - -// Protects copying of all linked_ptr objects. -GTEST_API_ GTEST_DECLARE_STATIC_MUTEX_(g_linked_ptr_mutex); - -// This is used internally by all instances of linked_ptr<>. It needs to be -// a non-template class because different types of linked_ptr<> can refer to -// the same object (linked_ptr(obj) vs linked_ptr(obj)). -// So, it needs to be possible for different types of linked_ptr to participate -// in the same circular linked list, so we need a single class type here. -// -// DO NOT USE THIS CLASS DIRECTLY YOURSELF. Use linked_ptr. -class linked_ptr_internal { - public: - // Create a new circle that includes only this instance. - void join_new() { - next_ = this; - } - - // Many linked_ptr operations may change p.link_ for some linked_ptr - // variable p in the same circle as this object. Therefore we need - // to prevent two such operations from occurring concurrently. - // - // Note that different types of linked_ptr objects can coexist in a - // circle (e.g. linked_ptr, linked_ptr, and - // linked_ptr). Therefore we must use a single mutex to - // protect all linked_ptr objects. This can create serious - // contention in production code, but is acceptable in a testing - // framework. - - // Join an existing circle. - // L < g_linked_ptr_mutex - void join(linked_ptr_internal const* ptr) { - MutexLock lock(&g_linked_ptr_mutex); - - linked_ptr_internal const* p = ptr; - while (p->next_ != ptr) p = p->next_; - p->next_ = this; - next_ = ptr; - } - - // Leave whatever circle we're part of. Returns true if we were the - // last member of the circle. Once this is done, you can join() another. - // L < g_linked_ptr_mutex - bool depart() { - MutexLock lock(&g_linked_ptr_mutex); - - if (next_ == this) return true; - linked_ptr_internal const* p = next_; - while (p->next_ != this) p = p->next_; - p->next_ = next_; - return false; - } - - private: - mutable linked_ptr_internal const* next_; -}; - -template -class linked_ptr { - public: - typedef T element_type; - - // Take over ownership of a raw pointer. This should happen as soon as - // possible after the object is created. - explicit linked_ptr(T* ptr = NULL) { capture(ptr); } - ~linked_ptr() { depart(); } - - // Copy an existing linked_ptr<>, adding ourselves to the list of references. - template linked_ptr(linked_ptr const& ptr) { copy(&ptr); } - linked_ptr(linked_ptr const& ptr) { // NOLINT - assert(&ptr != this); - copy(&ptr); - } - - // Assignment releases the old value and acquires the new. - template linked_ptr& operator=(linked_ptr const& ptr) { - depart(); - copy(&ptr); - return *this; - } - - linked_ptr& operator=(linked_ptr const& ptr) { - if (&ptr != this) { - depart(); - copy(&ptr); - } - return *this; - } - - // Smart pointer members. - void reset(T* ptr = NULL) { - depart(); - capture(ptr); - } - T* get() const { return value_; } - T* operator->() const { return value_; } - T& operator*() const { return *value_; } - - bool operator==(T* p) const { return value_ == p; } - bool operator!=(T* p) const { return value_ != p; } - template - bool operator==(linked_ptr const& ptr) const { - return value_ == ptr.get(); - } - template - bool operator!=(linked_ptr const& ptr) const { - return value_ != ptr.get(); - } - - private: - template - friend class linked_ptr; - - T* value_; - linked_ptr_internal link_; - - void depart() { - if (link_.depart()) delete value_; - } - - void capture(T* ptr) { - value_ = ptr; - link_.join_new(); - } - - template void copy(linked_ptr const* ptr) { - value_ = ptr->get(); - if (value_) - link_.join(&ptr->link_); - else - link_.join_new(); - } -}; - -template inline -bool operator==(T* ptr, const linked_ptr& x) { - return ptr == x.get(); -} - -template inline -bool operator!=(T* ptr, const linked_ptr& x) { - return ptr != x.get(); -} - -// A function to convert T* into linked_ptr -// Doing e.g. make_linked_ptr(new FooBarBaz(arg)) is a shorter notation -// for linked_ptr >(new FooBarBaz(arg)) -template -linked_ptr make_linked_ptr(T* ptr) { - return linked_ptr(ptr); -} - -} // namespace internal -} // namespace testing - -#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_LINKED_PTR_H_ -// Copyright 2007, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Author: wan@google.com (Zhanyong Wan) - -// Google Test - The Google C++ Testing Framework -// -// This file implements a universal value printer that can print a -// value of any type T: -// -// void ::testing::internal::UniversalPrinter::Print(value, ostream_ptr); -// -// A user can teach this function how to print a class type T by -// defining either operator<<() or PrintTo() in the namespace that -// defines T. More specifically, the FIRST defined function in the -// following list will be used (assuming T is defined in namespace -// foo): -// -// 1. foo::PrintTo(const T&, ostream*) -// 2. operator<<(ostream&, const T&) defined in either foo or the -// global namespace. -// -// If none of the above is defined, it will print the debug string of -// the value if it is a protocol buffer, or print the raw bytes in the -// value otherwise. -// -// To aid debugging: when T is a reference type, the address of the -// value is also printed; when T is a (const) char pointer, both the -// pointer value and the NUL-terminated string it points to are -// printed. -// -// We also provide some convenient wrappers: -// -// // Prints a value to a string. For a (const or not) char -// // pointer, the NUL-terminated string (but not the pointer) is -// // printed. -// std::string ::testing::PrintToString(const T& value); -// -// // Prints a value tersely: for a reference type, the referenced -// // value (but not the address) is printed; for a (const or not) char -// // pointer, the NUL-terminated string (but not the pointer) is -// // printed. -// void ::testing::internal::UniversalTersePrint(const T& value, ostream*); -// -// // Prints value using the type inferred by the compiler. The difference -// // from UniversalTersePrint() is that this function prints both the -// // pointer and the NUL-terminated string for a (const or not) char pointer. -// void ::testing::internal::UniversalPrint(const T& value, ostream*); -// -// // Prints the fields of a tuple tersely to a string vector, one -// // element for each field. Tuple support must be enabled in -// // gtest-port.h. -// std::vector UniversalTersePrintTupleFieldsToStrings( -// const Tuple& value); -// -// Known limitation: -// -// The print primitives print the elements of an STL-style container -// using the compiler-inferred type of *iter where iter is a -// const_iterator of the container. When const_iterator is an input -// iterator but not a forward iterator, this inferred type may not -// match value_type, and the print output may be incorrect. In -// practice, this is rarely a problem as for most containers -// const_iterator is a forward iterator. We'll fix this if there's an -// actual need for it. Note that this fix cannot rely on value_type -// being defined as many user-defined container types don't have -// value_type. - -#ifndef GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_ -#define GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_ - -#include // NOLINT -#include -#include -#include -#include - -namespace testing { - -// Definitions in the 'internal' and 'internal2' name spaces are -// subject to change without notice. DO NOT USE THEM IN USER CODE! -namespace internal2 { - -// Prints the given number of bytes in the given object to the given -// ostream. -GTEST_API_ void PrintBytesInObjectTo(const unsigned char* obj_bytes, - size_t count, - ::std::ostream* os); - -// For selecting which printer to use when a given type has neither << -// nor PrintTo(). -enum TypeKind { - kProtobuf, // a protobuf type - kConvertibleToInteger, // a type implicitly convertible to BiggestInt - // (e.g. a named or unnamed enum type) - kOtherType // anything else -}; - -// TypeWithoutFormatter::PrintValue(value, os) is called -// by the universal printer to print a value of type T when neither -// operator<< nor PrintTo() is defined for T, where kTypeKind is the -// "kind" of T as defined by enum TypeKind. -template -class TypeWithoutFormatter { - public: - // This default version is called when kTypeKind is kOtherType. - static void PrintValue(const T& value, ::std::ostream* os) { - PrintBytesInObjectTo(reinterpret_cast(&value), - sizeof(value), os); - } -}; - -// We print a protobuf using its ShortDebugString() when the string -// doesn't exceed this many characters; otherwise we print it using -// DebugString() for better readability. -const size_t kProtobufOneLinerMaxLength = 50; - -template -class TypeWithoutFormatter { - public: - static void PrintValue(const T& value, ::std::ostream* os) { - const ::testing::internal::string short_str = value.ShortDebugString(); - const ::testing::internal::string pretty_str = - short_str.length() <= kProtobufOneLinerMaxLength ? - short_str : ("\n" + value.DebugString()); - *os << ("<" + pretty_str + ">"); - } -}; - -template -class TypeWithoutFormatter { - public: - // Since T has no << operator or PrintTo() but can be implicitly - // converted to BiggestInt, we print it as a BiggestInt. - // - // Most likely T is an enum type (either named or unnamed), in which - // case printing it as an integer is the desired behavior. In case - // T is not an enum, printing it as an integer is the best we can do - // given that it has no user-defined printer. - static void PrintValue(const T& value, ::std::ostream* os) { - const internal::BiggestInt kBigInt = value; - *os << kBigInt; - } -}; - -// Prints the given value to the given ostream. If the value is a -// protocol message, its debug string is printed; if it's an enum or -// of a type implicitly convertible to BiggestInt, it's printed as an -// integer; otherwise the bytes in the value are printed. This is -// what UniversalPrinter::Print() does when it knows nothing about -// type T and T has neither << operator nor PrintTo(). -// -// A user can override this behavior for a class type Foo by defining -// a << operator in the namespace where Foo is defined. -// -// We put this operator in namespace 'internal2' instead of 'internal' -// to simplify the implementation, as much code in 'internal' needs to -// use << in STL, which would conflict with our own << were it defined -// in 'internal'. -// -// Note that this operator<< takes a generic std::basic_ostream type instead of the more restricted std::ostream. If -// we define it to take an std::ostream instead, we'll get an -// "ambiguous overloads" compiler error when trying to print a type -// Foo that supports streaming to std::basic_ostream, as the compiler cannot tell whether -// operator<<(std::ostream&, const T&) or -// operator<<(std::basic_stream, const Foo&) is more -// specific. -template -::std::basic_ostream& operator<<( - ::std::basic_ostream& os, const T& x) { - TypeWithoutFormatter::value ? kProtobuf : - internal::ImplicitlyConvertible::value ? - kConvertibleToInteger : kOtherType)>::PrintValue(x, &os); - return os; -} - -} // namespace internal2 -} // namespace testing - -// This namespace MUST NOT BE NESTED IN ::testing, or the name look-up -// magic needed for implementing UniversalPrinter won't work. -namespace testing_internal { - -// Used to print a value that is not an STL-style container when the -// user doesn't define PrintTo() for it. -template -void DefaultPrintNonContainerTo(const T& value, ::std::ostream* os) { - // With the following statement, during unqualified name lookup, - // testing::internal2::operator<< appears as if it was declared in - // the nearest enclosing namespace that contains both - // ::testing_internal and ::testing::internal2, i.e. the global - // namespace. For more details, refer to the C++ Standard section - // 7.3.4-1 [namespace.udir]. This allows us to fall back onto - // testing::internal2::operator<< in case T doesn't come with a << - // operator. - // - // We cannot write 'using ::testing::internal2::operator<<;', which - // gcc 3.3 fails to compile due to a compiler bug. - using namespace ::testing::internal2; // NOLINT - - // Assuming T is defined in namespace foo, in the next statement, - // the compiler will consider all of: - // - // 1. foo::operator<< (thanks to Koenig look-up), - // 2. ::operator<< (as the current namespace is enclosed in ::), - // 3. testing::internal2::operator<< (thanks to the using statement above). - // - // The operator<< whose type matches T best will be picked. - // - // We deliberately allow #2 to be a candidate, as sometimes it's - // impossible to define #1 (e.g. when foo is ::std, defining - // anything in it is undefined behavior unless you are a compiler - // vendor.). - *os << value; -} - -} // namespace testing_internal - -namespace testing { -namespace internal { - -// UniversalPrinter::Print(value, ostream_ptr) prints the given -// value to the given ostream. The caller must ensure that -// 'ostream_ptr' is not NULL, or the behavior is undefined. -// -// We define UniversalPrinter as a class template (as opposed to a -// function template), as we need to partially specialize it for -// reference types, which cannot be done with function templates. -template -class UniversalPrinter; - -template -void UniversalPrint(const T& value, ::std::ostream* os); - -// Used to print an STL-style container when the user doesn't define -// a PrintTo() for it. -template -void DefaultPrintTo(IsContainer /* dummy */, - false_type /* is not a pointer */, - const C& container, ::std::ostream* os) { - const size_t kMaxCount = 32; // The maximum number of elements to print. - *os << '{'; - size_t count = 0; - for (typename C::const_iterator it = container.begin(); - it != container.end(); ++it, ++count) { - if (count > 0) { - *os << ','; - if (count == kMaxCount) { // Enough has been printed. - *os << " ..."; - break; - } - } - *os << ' '; - // We cannot call PrintTo(*it, os) here as PrintTo() doesn't - // handle *it being a native array. - internal::UniversalPrint(*it, os); - } - - if (count > 0) { - *os << ' '; - } - *os << '}'; -} - -// Used to print a pointer that is neither a char pointer nor a member -// pointer, when the user doesn't define PrintTo() for it. (A member -// variable pointer or member function pointer doesn't really point to -// a location in the address space. Their representation is -// implementation-defined. Therefore they will be printed as raw -// bytes.) -template -void DefaultPrintTo(IsNotContainer /* dummy */, - true_type /* is a pointer */, - T* p, ::std::ostream* os) { - if (p == NULL) { - *os << "NULL"; - } else { - // C++ doesn't allow casting from a function pointer to any object - // pointer. - // - // IsTrue() silences warnings: "Condition is always true", - // "unreachable code". - if (IsTrue(ImplicitlyConvertible::value)) { - // T is not a function type. We just call << to print p, - // relying on ADL to pick up user-defined << for their pointer - // types, if any. - *os << p; - } else { - // T is a function type, so '*os << p' doesn't do what we want - // (it just prints p as bool). We want to print p as a const - // void*. However, we cannot cast it to const void* directly, - // even using reinterpret_cast, as earlier versions of gcc - // (e.g. 3.4.5) cannot compile the cast when p is a function - // pointer. Casting to UInt64 first solves the problem. - *os << reinterpret_cast( - reinterpret_cast(p)); - } - } -} - -// Used to print a non-container, non-pointer value when the user -// doesn't define PrintTo() for it. -template -void DefaultPrintTo(IsNotContainer /* dummy */, - false_type /* is not a pointer */, - const T& value, ::std::ostream* os) { - ::testing_internal::DefaultPrintNonContainerTo(value, os); -} - -// Prints the given value using the << operator if it has one; -// otherwise prints the bytes in it. This is what -// UniversalPrinter::Print() does when PrintTo() is not specialized -// or overloaded for type T. -// -// A user can override this behavior for a class type Foo by defining -// an overload of PrintTo() in the namespace where Foo is defined. We -// give the user this option as sometimes defining a << operator for -// Foo is not desirable (e.g. the coding style may prevent doing it, -// or there is already a << operator but it doesn't do what the user -// wants). -template -void PrintTo(const T& value, ::std::ostream* os) { - // DefaultPrintTo() is overloaded. The type of its first two - // arguments determine which version will be picked. If T is an - // STL-style container, the version for container will be called; if - // T is a pointer, the pointer version will be called; otherwise the - // generic version will be called. - // - // Note that we check for container types here, prior to we check - // for protocol message types in our operator<<. The rationale is: - // - // For protocol messages, we want to give people a chance to - // override Google Mock's format by defining a PrintTo() or - // operator<<. For STL containers, other formats can be - // incompatible with Google Mock's format for the container - // elements; therefore we check for container types here to ensure - // that our format is used. - // - // The second argument of DefaultPrintTo() is needed to bypass a bug - // in Symbian's C++ compiler that prevents it from picking the right - // overload between: - // - // PrintTo(const T& x, ...); - // PrintTo(T* x, ...); - DefaultPrintTo(IsContainerTest(0), is_pointer(), value, os); -} - -// The following list of PrintTo() overloads tells -// UniversalPrinter::Print() how to print standard types (built-in -// types, strings, plain arrays, and pointers). - -// Overloads for various char types. -GTEST_API_ void PrintTo(unsigned char c, ::std::ostream* os); -GTEST_API_ void PrintTo(signed char c, ::std::ostream* os); -inline void PrintTo(char c, ::std::ostream* os) { - // When printing a plain char, we always treat it as unsigned. This - // way, the output won't be affected by whether the compiler thinks - // char is signed or not. - PrintTo(static_cast(c), os); -} - -// Overloads for other simple built-in types. -inline void PrintTo(bool x, ::std::ostream* os) { - *os << (x ? "true" : "false"); -} - -// Overload for wchar_t type. -// Prints a wchar_t as a symbol if it is printable or as its internal -// code otherwise and also as its decimal code (except for L'\0'). -// The L'\0' char is printed as "L'\\0'". The decimal code is printed -// as signed integer when wchar_t is implemented by the compiler -// as a signed type and is printed as an unsigned integer when wchar_t -// is implemented as an unsigned type. -GTEST_API_ void PrintTo(wchar_t wc, ::std::ostream* os); - -// Overloads for C strings. -GTEST_API_ void PrintTo(const char* s, ::std::ostream* os); -inline void PrintTo(char* s, ::std::ostream* os) { - PrintTo(ImplicitCast_(s), os); -} - -// signed/unsigned char is often used for representing binary data, so -// we print pointers to it as void* to be safe. -inline void PrintTo(const signed char* s, ::std::ostream* os) { - PrintTo(ImplicitCast_(s), os); -} -inline void PrintTo(signed char* s, ::std::ostream* os) { - PrintTo(ImplicitCast_(s), os); -} -inline void PrintTo(const unsigned char* s, ::std::ostream* os) { - PrintTo(ImplicitCast_(s), os); -} -inline void PrintTo(unsigned char* s, ::std::ostream* os) { - PrintTo(ImplicitCast_(s), os); -} - -// MSVC can be configured to define wchar_t as a typedef of unsigned -// short. It defines _NATIVE_WCHAR_T_DEFINED when wchar_t is a native -// type. When wchar_t is a typedef, defining an overload for const -// wchar_t* would cause unsigned short* be printed as a wide string, -// possibly causing invalid memory accesses. -#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) -// Overloads for wide C strings -GTEST_API_ void PrintTo(const wchar_t* s, ::std::ostream* os); -inline void PrintTo(wchar_t* s, ::std::ostream* os) { - PrintTo(ImplicitCast_(s), os); -} -#endif - -// Overload for C arrays. Multi-dimensional arrays are printed -// properly. - -// Prints the given number of elements in an array, without printing -// the curly braces. -template -void PrintRawArrayTo(const T a[], size_t count, ::std::ostream* os) { - UniversalPrint(a[0], os); - for (size_t i = 1; i != count; i++) { - *os << ", "; - UniversalPrint(a[i], os); - } -} - -// Overloads for ::string and ::std::string. -#if GTEST_HAS_GLOBAL_STRING -GTEST_API_ void PrintStringTo(const ::string&s, ::std::ostream* os); -inline void PrintTo(const ::string& s, ::std::ostream* os) { - PrintStringTo(s, os); -} -#endif // GTEST_HAS_GLOBAL_STRING - -GTEST_API_ void PrintStringTo(const ::std::string&s, ::std::ostream* os); -inline void PrintTo(const ::std::string& s, ::std::ostream* os) { - PrintStringTo(s, os); -} - -// Overloads for ::wstring and ::std::wstring. -#if GTEST_HAS_GLOBAL_WSTRING -GTEST_API_ void PrintWideStringTo(const ::wstring&s, ::std::ostream* os); -inline void PrintTo(const ::wstring& s, ::std::ostream* os) { - PrintWideStringTo(s, os); -} -#endif // GTEST_HAS_GLOBAL_WSTRING - -#if GTEST_HAS_STD_WSTRING -GTEST_API_ void PrintWideStringTo(const ::std::wstring&s, ::std::ostream* os); -inline void PrintTo(const ::std::wstring& s, ::std::ostream* os) { - PrintWideStringTo(s, os); -} -#endif // GTEST_HAS_STD_WSTRING - -#if GTEST_HAS_TR1_TUPLE -// Overload for ::std::tr1::tuple. Needed for printing function arguments, -// which are packed as tuples. - -// Helper function for printing a tuple. T must be instantiated with -// a tuple type. -template -void PrintTupleTo(const T& t, ::std::ostream* os); - -// Overloaded PrintTo() for tuples of various arities. We support -// tuples of up-to 10 fields. The following implementation works -// regardless of whether tr1::tuple is implemented using the -// non-standard variadic template feature or not. - -inline void PrintTo(const ::std::tr1::tuple<>& t, ::std::ostream* os) { - PrintTupleTo(t, os); -} - -template -void PrintTo(const ::std::tr1::tuple& t, ::std::ostream* os) { - PrintTupleTo(t, os); -} - -template -void PrintTo(const ::std::tr1::tuple& t, ::std::ostream* os) { - PrintTupleTo(t, os); -} - -template -void PrintTo(const ::std::tr1::tuple& t, ::std::ostream* os) { - PrintTupleTo(t, os); -} - -template -void PrintTo(const ::std::tr1::tuple& t, ::std::ostream* os) { - PrintTupleTo(t, os); -} - -template -void PrintTo(const ::std::tr1::tuple& t, - ::std::ostream* os) { - PrintTupleTo(t, os); -} - -template -void PrintTo(const ::std::tr1::tuple& t, - ::std::ostream* os) { - PrintTupleTo(t, os); -} - -template -void PrintTo(const ::std::tr1::tuple& t, - ::std::ostream* os) { - PrintTupleTo(t, os); -} - -template -void PrintTo(const ::std::tr1::tuple& t, - ::std::ostream* os) { - PrintTupleTo(t, os); -} - -template -void PrintTo(const ::std::tr1::tuple& t, - ::std::ostream* os) { - PrintTupleTo(t, os); -} - -template -void PrintTo( - const ::std::tr1::tuple& t, - ::std::ostream* os) { - PrintTupleTo(t, os); -} -#endif // GTEST_HAS_TR1_TUPLE - -// Overload for std::pair. -template -void PrintTo(const ::std::pair& value, ::std::ostream* os) { - *os << '('; - // We cannot use UniversalPrint(value.first, os) here, as T1 may be - // a reference type. The same for printing value.second. - UniversalPrinter::Print(value.first, os); - *os << ", "; - UniversalPrinter::Print(value.second, os); - *os << ')'; -} - -// Implements printing a non-reference type T by letting the compiler -// pick the right overload of PrintTo() for T. -template -class UniversalPrinter { - public: - // MSVC warns about adding const to a function type, so we want to - // disable the warning. -#ifdef _MSC_VER -# pragma warning(push) // Saves the current warning state. -# pragma warning(disable:4180) // Temporarily disables warning 4180. -#endif // _MSC_VER - - // Note: we deliberately don't call this PrintTo(), as that name - // conflicts with ::testing::internal::PrintTo in the body of the - // function. - static void Print(const T& value, ::std::ostream* os) { - // By default, ::testing::internal::PrintTo() is used for printing - // the value. - // - // Thanks to Koenig look-up, if T is a class and has its own - // PrintTo() function defined in its namespace, that function will - // be visible here. Since it is more specific than the generic ones - // in ::testing::internal, it will be picked by the compiler in the - // following statement - exactly what we want. - PrintTo(value, os); - } - -#ifdef _MSC_VER -# pragma warning(pop) // Restores the warning state. -#endif // _MSC_VER -}; - -// UniversalPrintArray(begin, len, os) prints an array of 'len' -// elements, starting at address 'begin'. -template -void UniversalPrintArray(const T* begin, size_t len, ::std::ostream* os) { - if (len == 0) { - *os << "{}"; - } else { - *os << "{ "; - const size_t kThreshold = 18; - const size_t kChunkSize = 8; - // If the array has more than kThreshold elements, we'll have to - // omit some details by printing only the first and the last - // kChunkSize elements. - // TODO(wan@google.com): let the user control the threshold using a flag. - if (len <= kThreshold) { - PrintRawArrayTo(begin, len, os); - } else { - PrintRawArrayTo(begin, kChunkSize, os); - *os << ", ..., "; - PrintRawArrayTo(begin + len - kChunkSize, kChunkSize, os); - } - *os << " }"; - } -} -// This overload prints a (const) char array compactly. -GTEST_API_ void UniversalPrintArray(const char* begin, - size_t len, - ::std::ostream* os); - -// Implements printing an array type T[N]. -template -class UniversalPrinter { - public: - // Prints the given array, omitting some elements when there are too - // many. - static void Print(const T (&a)[N], ::std::ostream* os) { - UniversalPrintArray(a, N, os); - } -}; - -// Implements printing a reference type T&. -template -class UniversalPrinter { - public: - // MSVC warns about adding const to a function type, so we want to - // disable the warning. -#ifdef _MSC_VER -# pragma warning(push) // Saves the current warning state. -# pragma warning(disable:4180) // Temporarily disables warning 4180. -#endif // _MSC_VER - - static void Print(const T& value, ::std::ostream* os) { - // Prints the address of the value. We use reinterpret_cast here - // as static_cast doesn't compile when T is a function type. - *os << "@" << reinterpret_cast(&value) << " "; - - // Then prints the value itself. - UniversalPrint(value, os); - } - -#ifdef _MSC_VER -# pragma warning(pop) // Restores the warning state. -#endif // _MSC_VER -}; - -// Prints a value tersely: for a reference type, the referenced value -// (but not the address) is printed; for a (const) char pointer, the -// NUL-terminated string (but not the pointer) is printed. -template -void UniversalTersePrint(const T& value, ::std::ostream* os) { - UniversalPrint(value, os); -} -inline void UniversalTersePrint(const char* str, ::std::ostream* os) { - if (str == NULL) { - *os << "NULL"; - } else { - UniversalPrint(string(str), os); - } -} -inline void UniversalTersePrint(char* str, ::std::ostream* os) { - UniversalTersePrint(static_cast(str), os); -} - -// Prints a value using the type inferred by the compiler. The -// difference between this and UniversalTersePrint() is that for a -// (const) char pointer, this prints both the pointer and the -// NUL-terminated string. -template -void UniversalPrint(const T& value, ::std::ostream* os) { - UniversalPrinter::Print(value, os); -} - -#if GTEST_HAS_TR1_TUPLE -typedef ::std::vector Strings; - -// This helper template allows PrintTo() for tuples and -// UniversalTersePrintTupleFieldsToStrings() to be defined by -// induction on the number of tuple fields. The idea is that -// TuplePrefixPrinter::PrintPrefixTo(t, os) prints the first N -// fields in tuple t, and can be defined in terms of -// TuplePrefixPrinter. - -// The inductive case. -template -struct TuplePrefixPrinter { - // Prints the first N fields of a tuple. - template - static void PrintPrefixTo(const Tuple& t, ::std::ostream* os) { - TuplePrefixPrinter::PrintPrefixTo(t, os); - *os << ", "; - UniversalPrinter::type> - ::Print(::std::tr1::get(t), os); - } - - // Tersely prints the first N fields of a tuple to a string vector, - // one element for each field. - template - static void TersePrintPrefixToStrings(const Tuple& t, Strings* strings) { - TuplePrefixPrinter::TersePrintPrefixToStrings(t, strings); - ::std::stringstream ss; - UniversalTersePrint(::std::tr1::get(t), &ss); - strings->push_back(ss.str()); - } -}; - -// Base cases. -template <> -struct TuplePrefixPrinter<0> { - template - static void PrintPrefixTo(const Tuple&, ::std::ostream*) {} - - template - static void TersePrintPrefixToStrings(const Tuple&, Strings*) {} -}; -// We have to specialize the entire TuplePrefixPrinter<> class -// template here, even though the definition of -// TersePrintPrefixToStrings() is the same as the generic version, as -// Embarcadero (formerly CodeGear, formerly Borland) C++ doesn't -// support specializing a method template of a class template. -template <> -struct TuplePrefixPrinter<1> { - template - static void PrintPrefixTo(const Tuple& t, ::std::ostream* os) { - UniversalPrinter::type>:: - Print(::std::tr1::get<0>(t), os); - } - - template - static void TersePrintPrefixToStrings(const Tuple& t, Strings* strings) { - ::std::stringstream ss; - UniversalTersePrint(::std::tr1::get<0>(t), &ss); - strings->push_back(ss.str()); - } -}; - -// Helper function for printing a tuple. T must be instantiated with -// a tuple type. -template -void PrintTupleTo(const T& t, ::std::ostream* os) { - *os << "("; - TuplePrefixPrinter< ::std::tr1::tuple_size::value>:: - PrintPrefixTo(t, os); - *os << ")"; -} - -// Prints the fields of a tuple tersely to a string vector, one -// element for each field. See the comment before -// UniversalTersePrint() for how we define "tersely". -template -Strings UniversalTersePrintTupleFieldsToStrings(const Tuple& value) { - Strings result; - TuplePrefixPrinter< ::std::tr1::tuple_size::value>:: - TersePrintPrefixToStrings(value, &result); - return result; -} -#endif // GTEST_HAS_TR1_TUPLE - -} // namespace internal - -template -::std::string PrintToString(const T& value) { - ::std::stringstream ss; - internal::UniversalTersePrint(value, &ss); - return ss.str(); -} - -} // namespace testing - -#endif // GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_ - -#if GTEST_HAS_PARAM_TEST - -namespace testing { -namespace internal { - -// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. -// -// Outputs a message explaining invalid registration of different -// fixture class for the same test case. This may happen when -// TEST_P macro is used to define two tests with the same name -// but in different namespaces. -GTEST_API_ void ReportInvalidTestCaseType(const char* test_case_name, - const char* file, int line); - -template class ParamGeneratorInterface; -template class ParamGenerator; - -// Interface for iterating over elements provided by an implementation -// of ParamGeneratorInterface. -template -class ParamIteratorInterface { - public: - virtual ~ParamIteratorInterface() {} - // A pointer to the base generator instance. - // Used only for the purposes of iterator comparison - // to make sure that two iterators belong to the same generator. - virtual const ParamGeneratorInterface* BaseGenerator() const = 0; - // Advances iterator to point to the next element - // provided by the generator. The caller is responsible - // for not calling Advance() on an iterator equal to - // BaseGenerator()->End(). - virtual void Advance() = 0; - // Clones the iterator object. Used for implementing copy semantics - // of ParamIterator. - virtual ParamIteratorInterface* Clone() const = 0; - // Dereferences the current iterator and provides (read-only) access - // to the pointed value. It is the caller's responsibility not to call - // Current() on an iterator equal to BaseGenerator()->End(). - // Used for implementing ParamGenerator::operator*(). - virtual const T* Current() const = 0; - // Determines whether the given iterator and other point to the same - // element in the sequence generated by the generator. - // Used for implementing ParamGenerator::operator==(). - virtual bool Equals(const ParamIteratorInterface& other) const = 0; -}; - -// Class iterating over elements provided by an implementation of -// ParamGeneratorInterface. It wraps ParamIteratorInterface -// and implements the const forward iterator concept. -template -class ParamIterator { - public: - typedef T value_type; - typedef const T& reference; - typedef ptrdiff_t difference_type; - - // ParamIterator assumes ownership of the impl_ pointer. - ParamIterator(const ParamIterator& other) : impl_(other.impl_->Clone()) {} - ParamIterator& operator=(const ParamIterator& other) { - if (this != &other) - impl_.reset(other.impl_->Clone()); - return *this; - } - - const T& operator*() const { return *impl_->Current(); } - const T* operator->() const { return impl_->Current(); } - // Prefix version of operator++. - ParamIterator& operator++() { - impl_->Advance(); - return *this; - } - // Postfix version of operator++. - ParamIterator operator++(int /*unused*/) { - ParamIteratorInterface* clone = impl_->Clone(); - impl_->Advance(); - return ParamIterator(clone); - } - bool operator==(const ParamIterator& other) const { - return impl_.get() == other.impl_.get() || impl_->Equals(*other.impl_); - } - bool operator!=(const ParamIterator& other) const { - return !(*this == other); - } - - private: - friend class ParamGenerator; - explicit ParamIterator(ParamIteratorInterface* impl) : impl_(impl) {} - scoped_ptr > impl_; -}; - -// ParamGeneratorInterface is the binary interface to access generators -// defined in other translation units. -template -class ParamGeneratorInterface { - public: - typedef T ParamType; - - virtual ~ParamGeneratorInterface() {} - - // Generator interface definition - virtual ParamIteratorInterface* Begin() const = 0; - virtual ParamIteratorInterface* End() const = 0; -}; - -// Wraps ParamGeneratorInterface and provides general generator syntax -// compatible with the STL Container concept. -// This class implements copy initialization semantics and the contained -// ParamGeneratorInterface instance is shared among all copies -// of the original object. This is possible because that instance is immutable. -template -class ParamGenerator { - public: - typedef ParamIterator iterator; - - explicit ParamGenerator(ParamGeneratorInterface* impl) : impl_(impl) {} - ParamGenerator(const ParamGenerator& other) : impl_(other.impl_) {} - - ParamGenerator& operator=(const ParamGenerator& other) { - impl_ = other.impl_; - return *this; - } - - iterator begin() const { return iterator(impl_->Begin()); } - iterator end() const { return iterator(impl_->End()); } - - private: - linked_ptr > impl_; -}; - -// Generates values from a range of two comparable values. Can be used to -// generate sequences of user-defined types that implement operator+() and -// operator<(). -// This class is used in the Range() function. -template -class RangeGenerator : public ParamGeneratorInterface { - public: - RangeGenerator(T begin, T end, IncrementT step) - : begin_(begin), end_(end), - step_(step), end_index_(CalculateEndIndex(begin, end, step)) {} - virtual ~RangeGenerator() {} - - virtual ParamIteratorInterface* Begin() const { - return new Iterator(this, begin_, 0, step_); - } - virtual ParamIteratorInterface* End() const { - return new Iterator(this, end_, end_index_, step_); - } - - private: - class Iterator : public ParamIteratorInterface { - public: - Iterator(const ParamGeneratorInterface* base, T value, int index, - IncrementT step) - : base_(base), value_(value), index_(index), step_(step) {} - virtual ~Iterator() {} - - virtual const ParamGeneratorInterface* BaseGenerator() const { - return base_; - } - virtual void Advance() { - value_ = value_ + step_; - index_++; - } - virtual ParamIteratorInterface* Clone() const { - return new Iterator(*this); - } - virtual const T* Current() const { return &value_; } - virtual bool Equals(const ParamIteratorInterface& other) const { - // Having the same base generator guarantees that the other - // iterator is of the same type and we can downcast. - GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) - << "The program attempted to compare iterators " - << "from different generators." << std::endl; - const int other_index = - CheckedDowncastToActualType(&other)->index_; - return index_ == other_index; - } - - private: - Iterator(const Iterator& other) - : ParamIteratorInterface(), - base_(other.base_), value_(other.value_), index_(other.index_), - step_(other.step_) {} - - // No implementation - assignment is unsupported. - void operator=(const Iterator& other); - - const ParamGeneratorInterface* const base_; - T value_; - int index_; - const IncrementT step_; - }; // class RangeGenerator::Iterator - - static int CalculateEndIndex(const T& begin, - const T& end, - const IncrementT& step) { - int end_index = 0; - for (T i = begin; i < end; i = i + step) - end_index++; - return end_index; - } - - // No implementation - assignment is unsupported. - void operator=(const RangeGenerator& other); - - const T begin_; - const T end_; - const IncrementT step_; - // The index for the end() iterator. All the elements in the generated - // sequence are indexed (0-based) to aid iterator comparison. - const int end_index_; -}; // class RangeGenerator - - -// Generates values from a pair of STL-style iterators. Used in the -// ValuesIn() function. The elements are copied from the source range -// since the source can be located on the stack, and the generator -// is likely to persist beyond that stack frame. -template -class ValuesInIteratorRangeGenerator : public ParamGeneratorInterface { - public: - template - ValuesInIteratorRangeGenerator(ForwardIterator begin, ForwardIterator end) - : container_(begin, end) {} - virtual ~ValuesInIteratorRangeGenerator() {} - - virtual ParamIteratorInterface* Begin() const { - return new Iterator(this, container_.begin()); - } - virtual ParamIteratorInterface* End() const { - return new Iterator(this, container_.end()); - } - - private: - typedef typename ::std::vector ContainerType; - - class Iterator : public ParamIteratorInterface { - public: - Iterator(const ParamGeneratorInterface* base, - typename ContainerType::const_iterator iterator) - : base_(base), iterator_(iterator) {} - virtual ~Iterator() {} - - virtual const ParamGeneratorInterface* BaseGenerator() const { - return base_; - } - virtual void Advance() { - ++iterator_; - value_.reset(); - } - virtual ParamIteratorInterface* Clone() const { - return new Iterator(*this); - } - // We need to use cached value referenced by iterator_ because *iterator_ - // can return a temporary object (and of type other then T), so just - // having "return &*iterator_;" doesn't work. - // value_ is updated here and not in Advance() because Advance() - // can advance iterator_ beyond the end of the range, and we cannot - // detect that fact. The client code, on the other hand, is - // responsible for not calling Current() on an out-of-range iterator. - virtual const T* Current() const { - if (value_.get() == NULL) - value_.reset(new T(*iterator_)); - return value_.get(); - } - virtual bool Equals(const ParamIteratorInterface& other) const { - // Having the same base generator guarantees that the other - // iterator is of the same type and we can downcast. - GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) - << "The program attempted to compare iterators " - << "from different generators." << std::endl; - return iterator_ == - CheckedDowncastToActualType(&other)->iterator_; - } - - private: - Iterator(const Iterator& other) - // The explicit constructor call suppresses a false warning - // emitted by gcc when supplied with the -Wextra option. - : ParamIteratorInterface(), - base_(other.base_), - iterator_(other.iterator_) {} - - const ParamGeneratorInterface* const base_; - typename ContainerType::const_iterator iterator_; - // A cached value of *iterator_. We keep it here to allow access by - // pointer in the wrapping iterator's operator->(). - // value_ needs to be mutable to be accessed in Current(). - // Use of scoped_ptr helps manage cached value's lifetime, - // which is bound by the lifespan of the iterator itself. - mutable scoped_ptr value_; - }; // class ValuesInIteratorRangeGenerator::Iterator - - // No implementation - assignment is unsupported. - void operator=(const ValuesInIteratorRangeGenerator& other); - - const ContainerType container_; -}; // class ValuesInIteratorRangeGenerator - -// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. -// -// Stores a parameter value and later creates tests parameterized with that -// value. -template -class ParameterizedTestFactory : public TestFactoryBase { - public: - typedef typename TestClass::ParamType ParamType; - explicit ParameterizedTestFactory(ParamType parameter) : - parameter_(parameter) {} - virtual Test* CreateTest() { - TestClass::SetParam(¶meter_); - return new TestClass(); - } - - private: - const ParamType parameter_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestFactory); -}; - -// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. -// -// TestMetaFactoryBase is a base class for meta-factories that create -// test factories for passing into MakeAndRegisterTestInfo function. -template -class TestMetaFactoryBase { - public: - virtual ~TestMetaFactoryBase() {} - - virtual TestFactoryBase* CreateTestFactory(ParamType parameter) = 0; -}; - -// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. -// -// TestMetaFactory creates test factories for passing into -// MakeAndRegisterTestInfo function. Since MakeAndRegisterTestInfo receives -// ownership of test factory pointer, same factory object cannot be passed -// into that method twice. But ParameterizedTestCaseInfo is going to call -// it for each Test/Parameter value combination. Thus it needs meta factory -// creator class. -template -class TestMetaFactory - : public TestMetaFactoryBase { - public: - typedef typename TestCase::ParamType ParamType; - - TestMetaFactory() {} - - virtual TestFactoryBase* CreateTestFactory(ParamType parameter) { - return new ParameterizedTestFactory(parameter); - } - - private: - GTEST_DISALLOW_COPY_AND_ASSIGN_(TestMetaFactory); -}; - -// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. -// -// ParameterizedTestCaseInfoBase is a generic interface -// to ParameterizedTestCaseInfo classes. ParameterizedTestCaseInfoBase -// accumulates test information provided by TEST_P macro invocations -// and generators provided by INSTANTIATE_TEST_CASE_P macro invocations -// and uses that information to register all resulting test instances -// in RegisterTests method. The ParameterizeTestCaseRegistry class holds -// a collection of pointers to the ParameterizedTestCaseInfo objects -// and calls RegisterTests() on each of them when asked. -class ParameterizedTestCaseInfoBase { - public: - virtual ~ParameterizedTestCaseInfoBase() {} - - // Base part of test case name for display purposes. - virtual const string& GetTestCaseName() const = 0; - // Test case id to verify identity. - virtual TypeId GetTestCaseTypeId() const = 0; - // UnitTest class invokes this method to register tests in this - // test case right before running them in RUN_ALL_TESTS macro. - // This method should not be called more then once on any single - // instance of a ParameterizedTestCaseInfoBase derived class. - virtual void RegisterTests() = 0; - - protected: - ParameterizedTestCaseInfoBase() {} - - private: - GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseInfoBase); -}; - -// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. -// -// ParameterizedTestCaseInfo accumulates tests obtained from TEST_P -// macro invocations for a particular test case and generators -// obtained from INSTANTIATE_TEST_CASE_P macro invocations for that -// test case. It registers tests with all values generated by all -// generators when asked. -template -class ParameterizedTestCaseInfo : public ParameterizedTestCaseInfoBase { - public: - // ParamType and GeneratorCreationFunc are private types but are required - // for declarations of public methods AddTestPattern() and - // AddTestCaseInstantiation(). - typedef typename TestCase::ParamType ParamType; - // A function that returns an instance of appropriate generator type. - typedef ParamGenerator(GeneratorCreationFunc)(); - - explicit ParameterizedTestCaseInfo(const char* name) - : test_case_name_(name) {} - - // Test case base name for display purposes. - virtual const string& GetTestCaseName() const { return test_case_name_; } - // Test case id to verify identity. - virtual TypeId GetTestCaseTypeId() const { return GetTypeId(); } - // TEST_P macro uses AddTestPattern() to record information - // about a single test in a LocalTestInfo structure. - // test_case_name is the base name of the test case (without invocation - // prefix). test_base_name is the name of an individual test without - // parameter index. For the test SequenceA/FooTest.DoBar/1 FooTest is - // test case base name and DoBar is test base name. - void AddTestPattern(const char* test_case_name, - const char* test_base_name, - TestMetaFactoryBase* meta_factory) { - tests_.push_back(linked_ptr(new TestInfo(test_case_name, - test_base_name, - meta_factory))); - } - // INSTANTIATE_TEST_CASE_P macro uses AddGenerator() to record information - // about a generator. - int AddTestCaseInstantiation(const string& instantiation_name, - GeneratorCreationFunc* func, - const char* /* file */, - int /* line */) { - instantiations_.push_back(::std::make_pair(instantiation_name, func)); - return 0; // Return value used only to run this method in namespace scope. - } - // UnitTest class invokes this method to register tests in this test case - // test cases right before running tests in RUN_ALL_TESTS macro. - // This method should not be called more then once on any single - // instance of a ParameterizedTestCaseInfoBase derived class. - // UnitTest has a guard to prevent from calling this method more then once. - virtual void RegisterTests() { - for (typename TestInfoContainer::iterator test_it = tests_.begin(); - test_it != tests_.end(); ++test_it) { - linked_ptr test_info = *test_it; - for (typename InstantiationContainer::iterator gen_it = - instantiations_.begin(); gen_it != instantiations_.end(); - ++gen_it) { - const string& instantiation_name = gen_it->first; - ParamGenerator generator((*gen_it->second)()); - - Message test_case_name_stream; - if ( !instantiation_name.empty() ) - test_case_name_stream << instantiation_name << "/"; - test_case_name_stream << test_info->test_case_base_name; - - int i = 0; - for (typename ParamGenerator::iterator param_it = - generator.begin(); - param_it != generator.end(); ++param_it, ++i) { - Message test_name_stream; - test_name_stream << test_info->test_base_name << "/" << i; - MakeAndRegisterTestInfo( - test_case_name_stream.GetString().c_str(), - test_name_stream.GetString().c_str(), - NULL, // No type parameter. - PrintToString(*param_it).c_str(), - GetTestCaseTypeId(), - TestCase::SetUpTestCase, - TestCase::TearDownTestCase, - test_info->test_meta_factory->CreateTestFactory(*param_it)); - } // for param_it - } // for gen_it - } // for test_it - } // RegisterTests - - private: - // LocalTestInfo structure keeps information about a single test registered - // with TEST_P macro. - struct TestInfo { - TestInfo(const char* a_test_case_base_name, - const char* a_test_base_name, - TestMetaFactoryBase* a_test_meta_factory) : - test_case_base_name(a_test_case_base_name), - test_base_name(a_test_base_name), - test_meta_factory(a_test_meta_factory) {} - - const string test_case_base_name; - const string test_base_name; - const scoped_ptr > test_meta_factory; - }; - typedef ::std::vector > TestInfoContainer; - // Keeps pairs of - // received from INSTANTIATE_TEST_CASE_P macros. - typedef ::std::vector > - InstantiationContainer; - - const string test_case_name_; - TestInfoContainer tests_; - InstantiationContainer instantiations_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseInfo); -}; // class ParameterizedTestCaseInfo - -// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. -// -// ParameterizedTestCaseRegistry contains a map of ParameterizedTestCaseInfoBase -// classes accessed by test case names. TEST_P and INSTANTIATE_TEST_CASE_P -// macros use it to locate their corresponding ParameterizedTestCaseInfo -// descriptors. -class ParameterizedTestCaseRegistry { - public: - ParameterizedTestCaseRegistry() {} - ~ParameterizedTestCaseRegistry() { - for (TestCaseInfoContainer::iterator it = test_case_infos_.begin(); - it != test_case_infos_.end(); ++it) { - delete *it; - } - } - - // Looks up or creates and returns a structure containing information about - // tests and instantiations of a particular test case. - template - ParameterizedTestCaseInfo* GetTestCasePatternHolder( - const char* test_case_name, - const char* file, - int line) { - ParameterizedTestCaseInfo* typed_test_info = NULL; - for (TestCaseInfoContainer::iterator it = test_case_infos_.begin(); - it != test_case_infos_.end(); ++it) { - if ((*it)->GetTestCaseName() == test_case_name) { - if ((*it)->GetTestCaseTypeId() != GetTypeId()) { - // Complain about incorrect usage of Google Test facilities - // and terminate the program since we cannot guaranty correct - // test case setup and tear-down in this case. - ReportInvalidTestCaseType(test_case_name, file, line); - posix::Abort(); - } else { - // At this point we are sure that the object we found is of the same - // type we are looking for, so we downcast it to that type - // without further checks. - typed_test_info = CheckedDowncastToActualType< - ParameterizedTestCaseInfo >(*it); - } - break; - } - } - if (typed_test_info == NULL) { - typed_test_info = new ParameterizedTestCaseInfo(test_case_name); - test_case_infos_.push_back(typed_test_info); - } - return typed_test_info; - } - void RegisterTests() { - for (TestCaseInfoContainer::iterator it = test_case_infos_.begin(); - it != test_case_infos_.end(); ++it) { - (*it)->RegisterTests(); - } - } - - private: - typedef ::std::vector TestCaseInfoContainer; - - TestCaseInfoContainer test_case_infos_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseRegistry); -}; - -} // namespace internal -} // namespace testing - -#endif // GTEST_HAS_PARAM_TEST - -#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_ -// This file was GENERATED by command: -// pump.py gtest-param-util-generated.h.pump -// DO NOT EDIT BY HAND!!! - -// Copyright 2008 Google Inc. -// All Rights Reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Author: vladl@google.com (Vlad Losev) - -// Type and function utilities for implementing parameterized tests. -// This file is generated by a SCRIPT. DO NOT EDIT BY HAND! -// -// Currently Google Test supports at most 50 arguments in Values, -// and at most 10 arguments in Combine. Please contact -// googletestframework@googlegroups.com if you need more. -// Please note that the number of arguments to Combine is limited -// by the maximum arity of the implementation of tr1::tuple which is -// currently set at 10. - -#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_ -#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_ - -// scripts/fuse_gtest.py depends on gtest's own header being #included -// *unconditionally*. Therefore these #includes cannot be moved -// inside #if GTEST_HAS_PARAM_TEST. - -#if GTEST_HAS_PARAM_TEST - -namespace testing { - -// Forward declarations of ValuesIn(), which is implemented in -// include/gtest/gtest-param-test.h. -template -internal::ParamGenerator< - typename ::testing::internal::IteratorTraits::value_type> -ValuesIn(ForwardIterator begin, ForwardIterator end); - -template -internal::ParamGenerator ValuesIn(const T (&array)[N]); - -template -internal::ParamGenerator ValuesIn( - const Container& container); - -namespace internal { - -// Used in the Values() function to provide polymorphic capabilities. -template -class ValueArray1 { - public: - explicit ValueArray1(T1 v1) : v1_(v1) {} - - template - operator ParamGenerator() const { return ValuesIn(&v1_, &v1_ + 1); } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray1& other); - - const T1 v1_; -}; - -template -class ValueArray2 { - public: - ValueArray2(T1 v1, T2 v2) : v1_(v1), v2_(v2) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray2& other); - - const T1 v1_; - const T2 v2_; -}; - -template -class ValueArray3 { - public: - ValueArray3(T1 v1, T2 v2, T3 v3) : v1_(v1), v2_(v2), v3_(v3) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray3& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; -}; - -template -class ValueArray4 { - public: - ValueArray4(T1 v1, T2 v2, T3 v3, T4 v4) : v1_(v1), v2_(v2), v3_(v3), - v4_(v4) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray4& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; -}; - -template -class ValueArray5 { - public: - ValueArray5(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5) : v1_(v1), v2_(v2), v3_(v3), - v4_(v4), v5_(v5) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray5& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; -}; - -template -class ValueArray6 { - public: - ValueArray6(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6) : v1_(v1), v2_(v2), - v3_(v3), v4_(v4), v5_(v5), v6_(v6) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray6& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; -}; - -template -class ValueArray7 { - public: - ValueArray7(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7) : v1_(v1), - v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray7& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; -}; - -template -class ValueArray8 { - public: - ValueArray8(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, - T8 v8) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), - v8_(v8) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray8& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; -}; - -template -class ValueArray9 { - public: - ValueArray9(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, - T9 v9) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), - v8_(v8), v9_(v9) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray9& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; -}; - -template -class ValueArray10 { - public: - ValueArray10(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), - v8_(v8), v9_(v9), v10_(v10) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray10& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; -}; - -template -class ValueArray11 { - public: - ValueArray11(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), - v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray11& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; -}; - -template -class ValueArray12 { - public: - ValueArray12(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), - v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray12& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; -}; - -template -class ValueArray13 { - public: - ValueArray13(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), - v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), - v12_(v12), v13_(v13) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray13& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; -}; - -template -class ValueArray14 { - public: - ValueArray14(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14) : v1_(v1), v2_(v2), v3_(v3), - v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), - v11_(v11), v12_(v12), v13_(v13), v14_(v14) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray14& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; -}; - -template -class ValueArray15 { - public: - ValueArray15(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15) : v1_(v1), v2_(v2), - v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), - v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray15& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; -}; - -template -class ValueArray16 { - public: - ValueArray16(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16) : v1_(v1), - v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), - v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), - v16_(v16) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray16& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; -}; - -template -class ValueArray17 { - public: - ValueArray17(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, - T17 v17) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), - v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), - v15_(v15), v16_(v16), v17_(v17) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray17& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; -}; - -template -class ValueArray18 { - public: - ValueArray18(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), - v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), - v15_(v15), v16_(v16), v17_(v17), v18_(v18) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray18& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; -}; - -template -class ValueArray19 { - public: - ValueArray19(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), - v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), - v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray19& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; -}; - -template -class ValueArray20 { - public: - ValueArray20(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), - v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), - v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), - v19_(v19), v20_(v20) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray20& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; -}; - -template -class ValueArray21 { - public: - ValueArray21(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), - v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), - v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), - v18_(v18), v19_(v19), v20_(v20), v21_(v21) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray21& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; -}; - -template -class ValueArray22 { - public: - ValueArray22(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22) : v1_(v1), v2_(v2), v3_(v3), - v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), - v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), - v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray22& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; -}; - -template -class ValueArray23 { - public: - ValueArray23(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23) : v1_(v1), v2_(v2), - v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), - v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), - v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), - v23_(v23) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, - v23_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray23& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; -}; - -template -class ValueArray24 { - public: - ValueArray24(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24) : v1_(v1), - v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), - v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), - v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), - v22_(v22), v23_(v23), v24_(v24) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray24& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; -}; - -template -class ValueArray25 { - public: - ValueArray25(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, - T25 v25) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), - v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), - v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), - v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray25& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; -}; - -template -class ValueArray26 { - public: - ValueArray26(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), - v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), - v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), - v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray26& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; - const T26 v26_; -}; - -template -class ValueArray27 { - public: - ValueArray27(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), - v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), - v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), - v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), - v26_(v26), v27_(v27) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray27& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; - const T26 v26_; - const T27 v27_; -}; - -template -class ValueArray28 { - public: - ValueArray28(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), - v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), - v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), - v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), - v25_(v25), v26_(v26), v27_(v27), v28_(v28) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray28& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; - const T26 v26_; - const T27 v27_; - const T28 v28_; -}; - -template -class ValueArray29 { - public: - ValueArray29(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), - v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), - v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), - v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), - v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray29& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; - const T26 v26_; - const T27 v27_; - const T28 v28_; - const T29 v29_; -}; - -template -class ValueArray30 { - public: - ValueArray30(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29, T30 v30) : v1_(v1), v2_(v2), v3_(v3), - v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), - v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), - v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), - v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), - v29_(v29), v30_(v30) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray30& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; - const T26 v26_; - const T27 v27_; - const T28 v28_; - const T29 v29_; - const T30 v30_; -}; - -template -class ValueArray31 { - public: - ValueArray31(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31) : v1_(v1), v2_(v2), - v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), - v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), - v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), - v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), - v29_(v29), v30_(v30), v31_(v31) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray31& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; - const T26 v26_; - const T27 v27_; - const T28 v28_; - const T29 v29_; - const T30 v30_; - const T31 v31_; -}; - -template -class ValueArray32 { - public: - ValueArray32(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32) : v1_(v1), - v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), - v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), - v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), - v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), - v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray32& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; - const T26 v26_; - const T27 v27_; - const T28 v28_; - const T29 v29_; - const T30 v30_; - const T31 v31_; - const T32 v32_; -}; - -template -class ValueArray33 { - public: - ValueArray33(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, - T33 v33) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), - v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), - v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), - v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), - v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), - v33_(v33) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray33& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; - const T26 v26_; - const T27 v27_; - const T28 v28_; - const T29 v29_; - const T30 v30_; - const T31 v31_; - const T32 v32_; - const T33 v33_; -}; - -template -class ValueArray34 { - public: - ValueArray34(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, - T34 v34) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), - v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), - v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), - v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), - v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), - v33_(v33), v34_(v34) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray34& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; - const T26 v26_; - const T27 v27_; - const T28 v28_; - const T29 v29_; - const T30 v30_; - const T31 v31_; - const T32 v32_; - const T33 v33_; - const T34 v34_; -}; - -template -class ValueArray35 { - public: - ValueArray35(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, - T34 v34, T35 v35) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), - v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), - v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), - v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), - v26_(v26), v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), - v32_(v32), v33_(v33), v34_(v34), v35_(v35) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, - v35_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray35& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; - const T26 v26_; - const T27 v27_; - const T28 v28_; - const T29 v29_; - const T30 v30_; - const T31 v31_; - const T32 v32_; - const T33 v33_; - const T34 v34_; - const T35 v35_; -}; - -template -class ValueArray36 { - public: - ValueArray36(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, - T34 v34, T35 v35, T36 v36) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), - v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), - v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), - v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), - v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29), v30_(v30), - v31_(v31), v32_(v32), v33_(v33), v34_(v34), v35_(v35), v36_(v36) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray36& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; - const T26 v26_; - const T27 v27_; - const T28 v28_; - const T29 v29_; - const T30 v30_; - const T31 v31_; - const T32 v32_; - const T33 v33_; - const T34 v34_; - const T35 v35_; - const T36 v36_; -}; - -template -class ValueArray37 { - public: - ValueArray37(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, - T34 v34, T35 v35, T36 v36, T37 v37) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), - v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), - v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), - v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), - v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29), - v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), v35_(v35), - v36_(v36), v37_(v37) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray37& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; - const T26 v26_; - const T27 v27_; - const T28 v28_; - const T29 v29_; - const T30 v30_; - const T31 v31_; - const T32 v32_; - const T33 v33_; - const T34 v34_; - const T35 v35_; - const T36 v36_; - const T37 v37_; -}; - -template -class ValueArray38 { - public: - ValueArray38(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, - T34 v34, T35 v35, T36 v36, T37 v37, T38 v38) : v1_(v1), v2_(v2), v3_(v3), - v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), - v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), - v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), - v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), - v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), - v35_(v35), v36_(v36), v37_(v37), v38_(v38) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray38& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; - const T26 v26_; - const T27 v27_; - const T28 v28_; - const T29 v29_; - const T30 v30_; - const T31 v31_; - const T32 v32_; - const T33 v33_; - const T34 v34_; - const T35 v35_; - const T36 v36_; - const T37 v37_; - const T38 v38_; -}; - -template -class ValueArray39 { - public: - ValueArray39(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, - T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39) : v1_(v1), v2_(v2), - v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), - v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), - v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), - v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), - v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), - v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_, v39_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray39& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; - const T26 v26_; - const T27 v27_; - const T28 v28_; - const T29 v29_; - const T30 v30_; - const T31 v31_; - const T32 v32_; - const T33 v33_; - const T34 v34_; - const T35 v35_; - const T36 v36_; - const T37 v37_; - const T38 v38_; - const T39 v39_; -}; - -template -class ValueArray40 { - public: - ValueArray40(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, - T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40) : v1_(v1), - v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), - v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), - v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), - v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), - v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), - v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39), - v40_(v40) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_, v39_, v40_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray40& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; - const T26 v26_; - const T27 v27_; - const T28 v28_; - const T29 v29_; - const T30 v30_; - const T31 v31_; - const T32 v32_; - const T33 v33_; - const T34 v34_; - const T35 v35_; - const T36 v36_; - const T37 v37_; - const T38 v38_; - const T39 v39_; - const T40 v40_; -}; - -template -class ValueArray41 { - public: - ValueArray41(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, - T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, - T41 v41) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), - v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), - v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), - v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), - v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), - v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), - v39_(v39), v40_(v40), v41_(v41) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_, v39_, v40_, v41_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray41& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; - const T26 v26_; - const T27 v27_; - const T28 v28_; - const T29 v29_; - const T30 v30_; - const T31 v31_; - const T32 v32_; - const T33 v33_; - const T34 v34_; - const T35 v35_; - const T36 v36_; - const T37 v37_; - const T38 v38_; - const T39 v39_; - const T40 v40_; - const T41 v41_; -}; - -template -class ValueArray42 { - public: - ValueArray42(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, - T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, - T42 v42) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), - v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), - v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), - v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), - v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), - v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), - v39_(v39), v40_(v40), v41_(v41), v42_(v42) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_, v39_, v40_, v41_, v42_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray42& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; - const T26 v26_; - const T27 v27_; - const T28 v28_; - const T29 v29_; - const T30 v30_; - const T31 v31_; - const T32 v32_; - const T33 v33_; - const T34 v34_; - const T35 v35_; - const T36 v36_; - const T37 v37_; - const T38 v38_; - const T39 v39_; - const T40 v40_; - const T41 v41_; - const T42 v42_; -}; - -template -class ValueArray43 { - public: - ValueArray43(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, - T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, - T42 v42, T43 v43) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), - v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), - v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), - v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), - v26_(v26), v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), - v32_(v32), v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), - v38_(v38), v39_(v39), v40_(v40), v41_(v41), v42_(v42), v43_(v43) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_, v39_, v40_, v41_, v42_, v43_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray43& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; - const T26 v26_; - const T27 v27_; - const T28 v28_; - const T29 v29_; - const T30 v30_; - const T31 v31_; - const T32 v32_; - const T33 v33_; - const T34 v34_; - const T35 v35_; - const T36 v36_; - const T37 v37_; - const T38 v38_; - const T39 v39_; - const T40 v40_; - const T41 v41_; - const T42 v42_; - const T43 v43_; -}; - -template -class ValueArray44 { - public: - ValueArray44(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, - T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, - T42 v42, T43 v43, T44 v44) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), - v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), - v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), - v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), - v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29), v30_(v30), - v31_(v31), v32_(v32), v33_(v33), v34_(v34), v35_(v35), v36_(v36), - v37_(v37), v38_(v38), v39_(v39), v40_(v40), v41_(v41), v42_(v42), - v43_(v43), v44_(v44) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_, v39_, v40_, v41_, v42_, v43_, v44_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray44& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; - const T26 v26_; - const T27 v27_; - const T28 v28_; - const T29 v29_; - const T30 v30_; - const T31 v31_; - const T32 v32_; - const T33 v33_; - const T34 v34_; - const T35 v35_; - const T36 v36_; - const T37 v37_; - const T38 v38_; - const T39 v39_; - const T40 v40_; - const T41 v41_; - const T42 v42_; - const T43 v43_; - const T44 v44_; -}; - -template -class ValueArray45 { - public: - ValueArray45(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, - T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, - T42 v42, T43 v43, T44 v44, T45 v45) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), - v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), - v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), - v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), - v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29), - v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), v35_(v35), - v36_(v36), v37_(v37), v38_(v38), v39_(v39), v40_(v40), v41_(v41), - v42_(v42), v43_(v43), v44_(v44), v45_(v45) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_, v39_, v40_, v41_, v42_, v43_, v44_, v45_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray45& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; - const T26 v26_; - const T27 v27_; - const T28 v28_; - const T29 v29_; - const T30 v30_; - const T31 v31_; - const T32 v32_; - const T33 v33_; - const T34 v34_; - const T35 v35_; - const T36 v36_; - const T37 v37_; - const T38 v38_; - const T39 v39_; - const T40 v40_; - const T41 v41_; - const T42 v42_; - const T43 v43_; - const T44 v44_; - const T45 v45_; -}; - -template -class ValueArray46 { - public: - ValueArray46(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, - T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, - T42 v42, T43 v43, T44 v44, T45 v45, T46 v46) : v1_(v1), v2_(v2), v3_(v3), - v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), - v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), - v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), - v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), - v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), - v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39), v40_(v40), - v41_(v41), v42_(v42), v43_(v43), v44_(v44), v45_(v45), v46_(v46) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_, v39_, v40_, v41_, v42_, v43_, v44_, v45_, v46_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray46& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; - const T26 v26_; - const T27 v27_; - const T28 v28_; - const T29 v29_; - const T30 v30_; - const T31 v31_; - const T32 v32_; - const T33 v33_; - const T34 v34_; - const T35 v35_; - const T36 v36_; - const T37 v37_; - const T38 v38_; - const T39 v39_; - const T40 v40_; - const T41 v41_; - const T42 v42_; - const T43 v43_; - const T44 v44_; - const T45 v45_; - const T46 v46_; -}; - -template -class ValueArray47 { - public: - ValueArray47(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, - T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, - T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47) : v1_(v1), v2_(v2), - v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), - v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), - v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), - v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), - v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), - v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39), v40_(v40), - v41_(v41), v42_(v42), v43_(v43), v44_(v44), v45_(v45), v46_(v46), - v47_(v47) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_, v39_, v40_, v41_, v42_, v43_, v44_, v45_, v46_, - v47_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray47& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; - const T26 v26_; - const T27 v27_; - const T28 v28_; - const T29 v29_; - const T30 v30_; - const T31 v31_; - const T32 v32_; - const T33 v33_; - const T34 v34_; - const T35 v35_; - const T36 v36_; - const T37 v37_; - const T38 v38_; - const T39 v39_; - const T40 v40_; - const T41 v41_; - const T42 v42_; - const T43 v43_; - const T44 v44_; - const T45 v45_; - const T46 v46_; - const T47 v47_; -}; - -template -class ValueArray48 { - public: - ValueArray48(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, - T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, - T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47, T48 v48) : v1_(v1), - v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), - v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), - v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), - v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), - v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), - v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39), - v40_(v40), v41_(v41), v42_(v42), v43_(v43), v44_(v44), v45_(v45), - v46_(v46), v47_(v47), v48_(v48) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_, v39_, v40_, v41_, v42_, v43_, v44_, v45_, v46_, v47_, - v48_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray48& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; - const T26 v26_; - const T27 v27_; - const T28 v28_; - const T29 v29_; - const T30 v30_; - const T31 v31_; - const T32 v32_; - const T33 v33_; - const T34 v34_; - const T35 v35_; - const T36 v36_; - const T37 v37_; - const T38 v38_; - const T39 v39_; - const T40 v40_; - const T41 v41_; - const T42 v42_; - const T43 v43_; - const T44 v44_; - const T45 v45_; - const T46 v46_; - const T47 v47_; - const T48 v48_; -}; - -template -class ValueArray49 { - public: - ValueArray49(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, - T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, - T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47, T48 v48, - T49 v49) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), - v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), - v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), - v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), - v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), - v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), - v39_(v39), v40_(v40), v41_(v41), v42_(v42), v43_(v43), v44_(v44), - v45_(v45), v46_(v46), v47_(v47), v48_(v48), v49_(v49) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_, v39_, v40_, v41_, v42_, v43_, v44_, v45_, v46_, v47_, - v48_, v49_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray49& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; - const T26 v26_; - const T27 v27_; - const T28 v28_; - const T29 v29_; - const T30 v30_; - const T31 v31_; - const T32 v32_; - const T33 v33_; - const T34 v34_; - const T35 v35_; - const T36 v36_; - const T37 v37_; - const T38 v38_; - const T39 v39_; - const T40 v40_; - const T41 v41_; - const T42 v42_; - const T43 v43_; - const T44 v44_; - const T45 v45_; - const T46 v46_; - const T47 v47_; - const T48 v48_; - const T49 v49_; -}; - -template -class ValueArray50 { - public: - ValueArray50(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, - T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, - T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47, T48 v48, T49 v49, - T50 v50) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), - v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), - v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), - v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), - v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), - v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), - v39_(v39), v40_(v40), v41_(v41), v42_(v42), v43_(v43), v44_(v44), - v45_(v45), v46_(v46), v47_(v47), v48_(v48), v49_(v49), v50_(v50) {} - - template - operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_, v39_, v40_, v41_, v42_, v43_, v44_, v45_, v46_, v47_, - v48_, v49_, v50_}; - return ValuesIn(array); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const ValueArray50& other); - - const T1 v1_; - const T2 v2_; - const T3 v3_; - const T4 v4_; - const T5 v5_; - const T6 v6_; - const T7 v7_; - const T8 v8_; - const T9 v9_; - const T10 v10_; - const T11 v11_; - const T12 v12_; - const T13 v13_; - const T14 v14_; - const T15 v15_; - const T16 v16_; - const T17 v17_; - const T18 v18_; - const T19 v19_; - const T20 v20_; - const T21 v21_; - const T22 v22_; - const T23 v23_; - const T24 v24_; - const T25 v25_; - const T26 v26_; - const T27 v27_; - const T28 v28_; - const T29 v29_; - const T30 v30_; - const T31 v31_; - const T32 v32_; - const T33 v33_; - const T34 v34_; - const T35 v35_; - const T36 v36_; - const T37 v37_; - const T38 v38_; - const T39 v39_; - const T40 v40_; - const T41 v41_; - const T42 v42_; - const T43 v43_; - const T44 v44_; - const T45 v45_; - const T46 v46_; - const T47 v47_; - const T48 v48_; - const T49 v49_; - const T50 v50_; -}; - -# if GTEST_HAS_COMBINE -// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. -// -// Generates values from the Cartesian product of values produced -// by the argument generators. -// -template -class CartesianProductGenerator2 - : public ParamGeneratorInterface< ::std::tr1::tuple > { - public: - typedef ::std::tr1::tuple ParamType; - - CartesianProductGenerator2(const ParamGenerator& g1, - const ParamGenerator& g2) - : g1_(g1), g2_(g2) {} - virtual ~CartesianProductGenerator2() {} - - virtual ParamIteratorInterface* Begin() const { - return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin()); - } - virtual ParamIteratorInterface* End() const { - return new Iterator(this, g1_, g1_.end(), g2_, g2_.end()); - } - - private: - class Iterator : public ParamIteratorInterface { - public: - Iterator(const ParamGeneratorInterface* base, - const ParamGenerator& g1, - const typename ParamGenerator::iterator& current1, - const ParamGenerator& g2, - const typename ParamGenerator::iterator& current2) - : base_(base), - begin1_(g1.begin()), end1_(g1.end()), current1_(current1), - begin2_(g2.begin()), end2_(g2.end()), current2_(current2) { - ComputeCurrentValue(); - } - virtual ~Iterator() {} - - virtual const ParamGeneratorInterface* BaseGenerator() const { - return base_; - } - // Advance should not be called on beyond-of-range iterators - // so no component iterators must be beyond end of range, either. - virtual void Advance() { - assert(!AtEnd()); - ++current2_; - if (current2_ == end2_) { - current2_ = begin2_; - ++current1_; - } - ComputeCurrentValue(); - } - virtual ParamIteratorInterface* Clone() const { - return new Iterator(*this); - } - virtual const ParamType* Current() const { return ¤t_value_; } - virtual bool Equals(const ParamIteratorInterface& other) const { - // Having the same base generator guarantees that the other - // iterator is of the same type and we can downcast. - GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) - << "The program attempted to compare iterators " - << "from different generators." << std::endl; - const Iterator* typed_other = - CheckedDowncastToActualType(&other); - // We must report iterators equal if they both point beyond their - // respective ranges. That can happen in a variety of fashions, - // so we have to consult AtEnd(). - return (AtEnd() && typed_other->AtEnd()) || - ( - current1_ == typed_other->current1_ && - current2_ == typed_other->current2_); - } - - private: - Iterator(const Iterator& other) - : base_(other.base_), - begin1_(other.begin1_), - end1_(other.end1_), - current1_(other.current1_), - begin2_(other.begin2_), - end2_(other.end2_), - current2_(other.current2_) { - ComputeCurrentValue(); - } - - void ComputeCurrentValue() { - if (!AtEnd()) - current_value_ = ParamType(*current1_, *current2_); - } - bool AtEnd() const { - // We must report iterator past the end of the range when either of the - // component iterators has reached the end of its range. - return - current1_ == end1_ || - current2_ == end2_; - } - - // No implementation - assignment is unsupported. - void operator=(const Iterator& other); - - const ParamGeneratorInterface* const base_; - // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. - // current[i]_ is the actual traversing iterator. - const typename ParamGenerator::iterator begin1_; - const typename ParamGenerator::iterator end1_; - typename ParamGenerator::iterator current1_; - const typename ParamGenerator::iterator begin2_; - const typename ParamGenerator::iterator end2_; - typename ParamGenerator::iterator current2_; - ParamType current_value_; - }; // class CartesianProductGenerator2::Iterator - - // No implementation - assignment is unsupported. - void operator=(const CartesianProductGenerator2& other); - - const ParamGenerator g1_; - const ParamGenerator g2_; -}; // class CartesianProductGenerator2 - - -template -class CartesianProductGenerator3 - : public ParamGeneratorInterface< ::std::tr1::tuple > { - public: - typedef ::std::tr1::tuple ParamType; - - CartesianProductGenerator3(const ParamGenerator& g1, - const ParamGenerator& g2, const ParamGenerator& g3) - : g1_(g1), g2_(g2), g3_(g3) {} - virtual ~CartesianProductGenerator3() {} - - virtual ParamIteratorInterface* Begin() const { - return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, - g3_.begin()); - } - virtual ParamIteratorInterface* End() const { - return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end()); - } - - private: - class Iterator : public ParamIteratorInterface { - public: - Iterator(const ParamGeneratorInterface* base, - const ParamGenerator& g1, - const typename ParamGenerator::iterator& current1, - const ParamGenerator& g2, - const typename ParamGenerator::iterator& current2, - const ParamGenerator& g3, - const typename ParamGenerator::iterator& current3) - : base_(base), - begin1_(g1.begin()), end1_(g1.end()), current1_(current1), - begin2_(g2.begin()), end2_(g2.end()), current2_(current2), - begin3_(g3.begin()), end3_(g3.end()), current3_(current3) { - ComputeCurrentValue(); - } - virtual ~Iterator() {} - - virtual const ParamGeneratorInterface* BaseGenerator() const { - return base_; - } - // Advance should not be called on beyond-of-range iterators - // so no component iterators must be beyond end of range, either. - virtual void Advance() { - assert(!AtEnd()); - ++current3_; - if (current3_ == end3_) { - current3_ = begin3_; - ++current2_; - } - if (current2_ == end2_) { - current2_ = begin2_; - ++current1_; - } - ComputeCurrentValue(); - } - virtual ParamIteratorInterface* Clone() const { - return new Iterator(*this); - } - virtual const ParamType* Current() const { return ¤t_value_; } - virtual bool Equals(const ParamIteratorInterface& other) const { - // Having the same base generator guarantees that the other - // iterator is of the same type and we can downcast. - GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) - << "The program attempted to compare iterators " - << "from different generators." << std::endl; - const Iterator* typed_other = - CheckedDowncastToActualType(&other); - // We must report iterators equal if they both point beyond their - // respective ranges. That can happen in a variety of fashions, - // so we have to consult AtEnd(). - return (AtEnd() && typed_other->AtEnd()) || - ( - current1_ == typed_other->current1_ && - current2_ == typed_other->current2_ && - current3_ == typed_other->current3_); - } - - private: - Iterator(const Iterator& other) - : base_(other.base_), - begin1_(other.begin1_), - end1_(other.end1_), - current1_(other.current1_), - begin2_(other.begin2_), - end2_(other.end2_), - current2_(other.current2_), - begin3_(other.begin3_), - end3_(other.end3_), - current3_(other.current3_) { - ComputeCurrentValue(); - } - - void ComputeCurrentValue() { - if (!AtEnd()) - current_value_ = ParamType(*current1_, *current2_, *current3_); - } - bool AtEnd() const { - // We must report iterator past the end of the range when either of the - // component iterators has reached the end of its range. - return - current1_ == end1_ || - current2_ == end2_ || - current3_ == end3_; - } - - // No implementation - assignment is unsupported. - void operator=(const Iterator& other); - - const ParamGeneratorInterface* const base_; - // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. - // current[i]_ is the actual traversing iterator. - const typename ParamGenerator::iterator begin1_; - const typename ParamGenerator::iterator end1_; - typename ParamGenerator::iterator current1_; - const typename ParamGenerator::iterator begin2_; - const typename ParamGenerator::iterator end2_; - typename ParamGenerator::iterator current2_; - const typename ParamGenerator::iterator begin3_; - const typename ParamGenerator::iterator end3_; - typename ParamGenerator::iterator current3_; - ParamType current_value_; - }; // class CartesianProductGenerator3::Iterator - - // No implementation - assignment is unsupported. - void operator=(const CartesianProductGenerator3& other); - - const ParamGenerator g1_; - const ParamGenerator g2_; - const ParamGenerator g3_; -}; // class CartesianProductGenerator3 - - -template -class CartesianProductGenerator4 - : public ParamGeneratorInterface< ::std::tr1::tuple > { - public: - typedef ::std::tr1::tuple ParamType; - - CartesianProductGenerator4(const ParamGenerator& g1, - const ParamGenerator& g2, const ParamGenerator& g3, - const ParamGenerator& g4) - : g1_(g1), g2_(g2), g3_(g3), g4_(g4) {} - virtual ~CartesianProductGenerator4() {} - - virtual ParamIteratorInterface* Begin() const { - return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, - g3_.begin(), g4_, g4_.begin()); - } - virtual ParamIteratorInterface* End() const { - return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), - g4_, g4_.end()); - } - - private: - class Iterator : public ParamIteratorInterface { - public: - Iterator(const ParamGeneratorInterface* base, - const ParamGenerator& g1, - const typename ParamGenerator::iterator& current1, - const ParamGenerator& g2, - const typename ParamGenerator::iterator& current2, - const ParamGenerator& g3, - const typename ParamGenerator::iterator& current3, - const ParamGenerator& g4, - const typename ParamGenerator::iterator& current4) - : base_(base), - begin1_(g1.begin()), end1_(g1.end()), current1_(current1), - begin2_(g2.begin()), end2_(g2.end()), current2_(current2), - begin3_(g3.begin()), end3_(g3.end()), current3_(current3), - begin4_(g4.begin()), end4_(g4.end()), current4_(current4) { - ComputeCurrentValue(); - } - virtual ~Iterator() {} - - virtual const ParamGeneratorInterface* BaseGenerator() const { - return base_; - } - // Advance should not be called on beyond-of-range iterators - // so no component iterators must be beyond end of range, either. - virtual void Advance() { - assert(!AtEnd()); - ++current4_; - if (current4_ == end4_) { - current4_ = begin4_; - ++current3_; - } - if (current3_ == end3_) { - current3_ = begin3_; - ++current2_; - } - if (current2_ == end2_) { - current2_ = begin2_; - ++current1_; - } - ComputeCurrentValue(); - } - virtual ParamIteratorInterface* Clone() const { - return new Iterator(*this); - } - virtual const ParamType* Current() const { return ¤t_value_; } - virtual bool Equals(const ParamIteratorInterface& other) const { - // Having the same base generator guarantees that the other - // iterator is of the same type and we can downcast. - GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) - << "The program attempted to compare iterators " - << "from different generators." << std::endl; - const Iterator* typed_other = - CheckedDowncastToActualType(&other); - // We must report iterators equal if they both point beyond their - // respective ranges. That can happen in a variety of fashions, - // so we have to consult AtEnd(). - return (AtEnd() && typed_other->AtEnd()) || - ( - current1_ == typed_other->current1_ && - current2_ == typed_other->current2_ && - current3_ == typed_other->current3_ && - current4_ == typed_other->current4_); - } - - private: - Iterator(const Iterator& other) - : base_(other.base_), - begin1_(other.begin1_), - end1_(other.end1_), - current1_(other.current1_), - begin2_(other.begin2_), - end2_(other.end2_), - current2_(other.current2_), - begin3_(other.begin3_), - end3_(other.end3_), - current3_(other.current3_), - begin4_(other.begin4_), - end4_(other.end4_), - current4_(other.current4_) { - ComputeCurrentValue(); - } - - void ComputeCurrentValue() { - if (!AtEnd()) - current_value_ = ParamType(*current1_, *current2_, *current3_, - *current4_); - } - bool AtEnd() const { - // We must report iterator past the end of the range when either of the - // component iterators has reached the end of its range. - return - current1_ == end1_ || - current2_ == end2_ || - current3_ == end3_ || - current4_ == end4_; - } - - // No implementation - assignment is unsupported. - void operator=(const Iterator& other); - - const ParamGeneratorInterface* const base_; - // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. - // current[i]_ is the actual traversing iterator. - const typename ParamGenerator::iterator begin1_; - const typename ParamGenerator::iterator end1_; - typename ParamGenerator::iterator current1_; - const typename ParamGenerator::iterator begin2_; - const typename ParamGenerator::iterator end2_; - typename ParamGenerator::iterator current2_; - const typename ParamGenerator::iterator begin3_; - const typename ParamGenerator::iterator end3_; - typename ParamGenerator::iterator current3_; - const typename ParamGenerator::iterator begin4_; - const typename ParamGenerator::iterator end4_; - typename ParamGenerator::iterator current4_; - ParamType current_value_; - }; // class CartesianProductGenerator4::Iterator - - // No implementation - assignment is unsupported. - void operator=(const CartesianProductGenerator4& other); - - const ParamGenerator g1_; - const ParamGenerator g2_; - const ParamGenerator g3_; - const ParamGenerator g4_; -}; // class CartesianProductGenerator4 - - -template -class CartesianProductGenerator5 - : public ParamGeneratorInterface< ::std::tr1::tuple > { - public: - typedef ::std::tr1::tuple ParamType; - - CartesianProductGenerator5(const ParamGenerator& g1, - const ParamGenerator& g2, const ParamGenerator& g3, - const ParamGenerator& g4, const ParamGenerator& g5) - : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5) {} - virtual ~CartesianProductGenerator5() {} - - virtual ParamIteratorInterface* Begin() const { - return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, - g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin()); - } - virtual ParamIteratorInterface* End() const { - return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), - g4_, g4_.end(), g5_, g5_.end()); - } - - private: - class Iterator : public ParamIteratorInterface { - public: - Iterator(const ParamGeneratorInterface* base, - const ParamGenerator& g1, - const typename ParamGenerator::iterator& current1, - const ParamGenerator& g2, - const typename ParamGenerator::iterator& current2, - const ParamGenerator& g3, - const typename ParamGenerator::iterator& current3, - const ParamGenerator& g4, - const typename ParamGenerator::iterator& current4, - const ParamGenerator& g5, - const typename ParamGenerator::iterator& current5) - : base_(base), - begin1_(g1.begin()), end1_(g1.end()), current1_(current1), - begin2_(g2.begin()), end2_(g2.end()), current2_(current2), - begin3_(g3.begin()), end3_(g3.end()), current3_(current3), - begin4_(g4.begin()), end4_(g4.end()), current4_(current4), - begin5_(g5.begin()), end5_(g5.end()), current5_(current5) { - ComputeCurrentValue(); - } - virtual ~Iterator() {} - - virtual const ParamGeneratorInterface* BaseGenerator() const { - return base_; - } - // Advance should not be called on beyond-of-range iterators - // so no component iterators must be beyond end of range, either. - virtual void Advance() { - assert(!AtEnd()); - ++current5_; - if (current5_ == end5_) { - current5_ = begin5_; - ++current4_; - } - if (current4_ == end4_) { - current4_ = begin4_; - ++current3_; - } - if (current3_ == end3_) { - current3_ = begin3_; - ++current2_; - } - if (current2_ == end2_) { - current2_ = begin2_; - ++current1_; - } - ComputeCurrentValue(); - } - virtual ParamIteratorInterface* Clone() const { - return new Iterator(*this); - } - virtual const ParamType* Current() const { return ¤t_value_; } - virtual bool Equals(const ParamIteratorInterface& other) const { - // Having the same base generator guarantees that the other - // iterator is of the same type and we can downcast. - GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) - << "The program attempted to compare iterators " - << "from different generators." << std::endl; - const Iterator* typed_other = - CheckedDowncastToActualType(&other); - // We must report iterators equal if they both point beyond their - // respective ranges. That can happen in a variety of fashions, - // so we have to consult AtEnd(). - return (AtEnd() && typed_other->AtEnd()) || - ( - current1_ == typed_other->current1_ && - current2_ == typed_other->current2_ && - current3_ == typed_other->current3_ && - current4_ == typed_other->current4_ && - current5_ == typed_other->current5_); - } - - private: - Iterator(const Iterator& other) - : base_(other.base_), - begin1_(other.begin1_), - end1_(other.end1_), - current1_(other.current1_), - begin2_(other.begin2_), - end2_(other.end2_), - current2_(other.current2_), - begin3_(other.begin3_), - end3_(other.end3_), - current3_(other.current3_), - begin4_(other.begin4_), - end4_(other.end4_), - current4_(other.current4_), - begin5_(other.begin5_), - end5_(other.end5_), - current5_(other.current5_) { - ComputeCurrentValue(); - } - - void ComputeCurrentValue() { - if (!AtEnd()) - current_value_ = ParamType(*current1_, *current2_, *current3_, - *current4_, *current5_); - } - bool AtEnd() const { - // We must report iterator past the end of the range when either of the - // component iterators has reached the end of its range. - return - current1_ == end1_ || - current2_ == end2_ || - current3_ == end3_ || - current4_ == end4_ || - current5_ == end5_; - } - - // No implementation - assignment is unsupported. - void operator=(const Iterator& other); - - const ParamGeneratorInterface* const base_; - // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. - // current[i]_ is the actual traversing iterator. - const typename ParamGenerator::iterator begin1_; - const typename ParamGenerator::iterator end1_; - typename ParamGenerator::iterator current1_; - const typename ParamGenerator::iterator begin2_; - const typename ParamGenerator::iterator end2_; - typename ParamGenerator::iterator current2_; - const typename ParamGenerator::iterator begin3_; - const typename ParamGenerator::iterator end3_; - typename ParamGenerator::iterator current3_; - const typename ParamGenerator::iterator begin4_; - const typename ParamGenerator::iterator end4_; - typename ParamGenerator::iterator current4_; - const typename ParamGenerator::iterator begin5_; - const typename ParamGenerator::iterator end5_; - typename ParamGenerator::iterator current5_; - ParamType current_value_; - }; // class CartesianProductGenerator5::Iterator - - // No implementation - assignment is unsupported. - void operator=(const CartesianProductGenerator5& other); - - const ParamGenerator g1_; - const ParamGenerator g2_; - const ParamGenerator g3_; - const ParamGenerator g4_; - const ParamGenerator g5_; -}; // class CartesianProductGenerator5 - - -template -class CartesianProductGenerator6 - : public ParamGeneratorInterface< ::std::tr1::tuple > { - public: - typedef ::std::tr1::tuple ParamType; - - CartesianProductGenerator6(const ParamGenerator& g1, - const ParamGenerator& g2, const ParamGenerator& g3, - const ParamGenerator& g4, const ParamGenerator& g5, - const ParamGenerator& g6) - : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6) {} - virtual ~CartesianProductGenerator6() {} - - virtual ParamIteratorInterface* Begin() const { - return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, - g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin()); - } - virtual ParamIteratorInterface* End() const { - return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), - g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end()); - } - - private: - class Iterator : public ParamIteratorInterface { - public: - Iterator(const ParamGeneratorInterface* base, - const ParamGenerator& g1, - const typename ParamGenerator::iterator& current1, - const ParamGenerator& g2, - const typename ParamGenerator::iterator& current2, - const ParamGenerator& g3, - const typename ParamGenerator::iterator& current3, - const ParamGenerator& g4, - const typename ParamGenerator::iterator& current4, - const ParamGenerator& g5, - const typename ParamGenerator::iterator& current5, - const ParamGenerator& g6, - const typename ParamGenerator::iterator& current6) - : base_(base), - begin1_(g1.begin()), end1_(g1.end()), current1_(current1), - begin2_(g2.begin()), end2_(g2.end()), current2_(current2), - begin3_(g3.begin()), end3_(g3.end()), current3_(current3), - begin4_(g4.begin()), end4_(g4.end()), current4_(current4), - begin5_(g5.begin()), end5_(g5.end()), current5_(current5), - begin6_(g6.begin()), end6_(g6.end()), current6_(current6) { - ComputeCurrentValue(); - } - virtual ~Iterator() {} - - virtual const ParamGeneratorInterface* BaseGenerator() const { - return base_; - } - // Advance should not be called on beyond-of-range iterators - // so no component iterators must be beyond end of range, either. - virtual void Advance() { - assert(!AtEnd()); - ++current6_; - if (current6_ == end6_) { - current6_ = begin6_; - ++current5_; - } - if (current5_ == end5_) { - current5_ = begin5_; - ++current4_; - } - if (current4_ == end4_) { - current4_ = begin4_; - ++current3_; - } - if (current3_ == end3_) { - current3_ = begin3_; - ++current2_; - } - if (current2_ == end2_) { - current2_ = begin2_; - ++current1_; - } - ComputeCurrentValue(); - } - virtual ParamIteratorInterface* Clone() const { - return new Iterator(*this); - } - virtual const ParamType* Current() const { return ¤t_value_; } - virtual bool Equals(const ParamIteratorInterface& other) const { - // Having the same base generator guarantees that the other - // iterator is of the same type and we can downcast. - GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) - << "The program attempted to compare iterators " - << "from different generators." << std::endl; - const Iterator* typed_other = - CheckedDowncastToActualType(&other); - // We must report iterators equal if they both point beyond their - // respective ranges. That can happen in a variety of fashions, - // so we have to consult AtEnd(). - return (AtEnd() && typed_other->AtEnd()) || - ( - current1_ == typed_other->current1_ && - current2_ == typed_other->current2_ && - current3_ == typed_other->current3_ && - current4_ == typed_other->current4_ && - current5_ == typed_other->current5_ && - current6_ == typed_other->current6_); - } - - private: - Iterator(const Iterator& other) - : base_(other.base_), - begin1_(other.begin1_), - end1_(other.end1_), - current1_(other.current1_), - begin2_(other.begin2_), - end2_(other.end2_), - current2_(other.current2_), - begin3_(other.begin3_), - end3_(other.end3_), - current3_(other.current3_), - begin4_(other.begin4_), - end4_(other.end4_), - current4_(other.current4_), - begin5_(other.begin5_), - end5_(other.end5_), - current5_(other.current5_), - begin6_(other.begin6_), - end6_(other.end6_), - current6_(other.current6_) { - ComputeCurrentValue(); - } - - void ComputeCurrentValue() { - if (!AtEnd()) - current_value_ = ParamType(*current1_, *current2_, *current3_, - *current4_, *current5_, *current6_); - } - bool AtEnd() const { - // We must report iterator past the end of the range when either of the - // component iterators has reached the end of its range. - return - current1_ == end1_ || - current2_ == end2_ || - current3_ == end3_ || - current4_ == end4_ || - current5_ == end5_ || - current6_ == end6_; - } - - // No implementation - assignment is unsupported. - void operator=(const Iterator& other); - - const ParamGeneratorInterface* const base_; - // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. - // current[i]_ is the actual traversing iterator. - const typename ParamGenerator::iterator begin1_; - const typename ParamGenerator::iterator end1_; - typename ParamGenerator::iterator current1_; - const typename ParamGenerator::iterator begin2_; - const typename ParamGenerator::iterator end2_; - typename ParamGenerator::iterator current2_; - const typename ParamGenerator::iterator begin3_; - const typename ParamGenerator::iterator end3_; - typename ParamGenerator::iterator current3_; - const typename ParamGenerator::iterator begin4_; - const typename ParamGenerator::iterator end4_; - typename ParamGenerator::iterator current4_; - const typename ParamGenerator::iterator begin5_; - const typename ParamGenerator::iterator end5_; - typename ParamGenerator::iterator current5_; - const typename ParamGenerator::iterator begin6_; - const typename ParamGenerator::iterator end6_; - typename ParamGenerator::iterator current6_; - ParamType current_value_; - }; // class CartesianProductGenerator6::Iterator - - // No implementation - assignment is unsupported. - void operator=(const CartesianProductGenerator6& other); - - const ParamGenerator g1_; - const ParamGenerator g2_; - const ParamGenerator g3_; - const ParamGenerator g4_; - const ParamGenerator g5_; - const ParamGenerator g6_; -}; // class CartesianProductGenerator6 - - -template -class CartesianProductGenerator7 - : public ParamGeneratorInterface< ::std::tr1::tuple > { - public: - typedef ::std::tr1::tuple ParamType; - - CartesianProductGenerator7(const ParamGenerator& g1, - const ParamGenerator& g2, const ParamGenerator& g3, - const ParamGenerator& g4, const ParamGenerator& g5, - const ParamGenerator& g6, const ParamGenerator& g7) - : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7) {} - virtual ~CartesianProductGenerator7() {} - - virtual ParamIteratorInterface* Begin() const { - return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, - g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin(), g7_, - g7_.begin()); - } - virtual ParamIteratorInterface* End() const { - return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), - g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end(), g7_, g7_.end()); - } - - private: - class Iterator : public ParamIteratorInterface { - public: - Iterator(const ParamGeneratorInterface* base, - const ParamGenerator& g1, - const typename ParamGenerator::iterator& current1, - const ParamGenerator& g2, - const typename ParamGenerator::iterator& current2, - const ParamGenerator& g3, - const typename ParamGenerator::iterator& current3, - const ParamGenerator& g4, - const typename ParamGenerator::iterator& current4, - const ParamGenerator& g5, - const typename ParamGenerator::iterator& current5, - const ParamGenerator& g6, - const typename ParamGenerator::iterator& current6, - const ParamGenerator& g7, - const typename ParamGenerator::iterator& current7) - : base_(base), - begin1_(g1.begin()), end1_(g1.end()), current1_(current1), - begin2_(g2.begin()), end2_(g2.end()), current2_(current2), - begin3_(g3.begin()), end3_(g3.end()), current3_(current3), - begin4_(g4.begin()), end4_(g4.end()), current4_(current4), - begin5_(g5.begin()), end5_(g5.end()), current5_(current5), - begin6_(g6.begin()), end6_(g6.end()), current6_(current6), - begin7_(g7.begin()), end7_(g7.end()), current7_(current7) { - ComputeCurrentValue(); - } - virtual ~Iterator() {} - - virtual const ParamGeneratorInterface* BaseGenerator() const { - return base_; - } - // Advance should not be called on beyond-of-range iterators - // so no component iterators must be beyond end of range, either. - virtual void Advance() { - assert(!AtEnd()); - ++current7_; - if (current7_ == end7_) { - current7_ = begin7_; - ++current6_; - } - if (current6_ == end6_) { - current6_ = begin6_; - ++current5_; - } - if (current5_ == end5_) { - current5_ = begin5_; - ++current4_; - } - if (current4_ == end4_) { - current4_ = begin4_; - ++current3_; - } - if (current3_ == end3_) { - current3_ = begin3_; - ++current2_; - } - if (current2_ == end2_) { - current2_ = begin2_; - ++current1_; - } - ComputeCurrentValue(); - } - virtual ParamIteratorInterface* Clone() const { - return new Iterator(*this); - } - virtual const ParamType* Current() const { return ¤t_value_; } - virtual bool Equals(const ParamIteratorInterface& other) const { - // Having the same base generator guarantees that the other - // iterator is of the same type and we can downcast. - GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) - << "The program attempted to compare iterators " - << "from different generators." << std::endl; - const Iterator* typed_other = - CheckedDowncastToActualType(&other); - // We must report iterators equal if they both point beyond their - // respective ranges. That can happen in a variety of fashions, - // so we have to consult AtEnd(). - return (AtEnd() && typed_other->AtEnd()) || - ( - current1_ == typed_other->current1_ && - current2_ == typed_other->current2_ && - current3_ == typed_other->current3_ && - current4_ == typed_other->current4_ && - current5_ == typed_other->current5_ && - current6_ == typed_other->current6_ && - current7_ == typed_other->current7_); - } - - private: - Iterator(const Iterator& other) - : base_(other.base_), - begin1_(other.begin1_), - end1_(other.end1_), - current1_(other.current1_), - begin2_(other.begin2_), - end2_(other.end2_), - current2_(other.current2_), - begin3_(other.begin3_), - end3_(other.end3_), - current3_(other.current3_), - begin4_(other.begin4_), - end4_(other.end4_), - current4_(other.current4_), - begin5_(other.begin5_), - end5_(other.end5_), - current5_(other.current5_), - begin6_(other.begin6_), - end6_(other.end6_), - current6_(other.current6_), - begin7_(other.begin7_), - end7_(other.end7_), - current7_(other.current7_) { - ComputeCurrentValue(); - } - - void ComputeCurrentValue() { - if (!AtEnd()) - current_value_ = ParamType(*current1_, *current2_, *current3_, - *current4_, *current5_, *current6_, *current7_); - } - bool AtEnd() const { - // We must report iterator past the end of the range when either of the - // component iterators has reached the end of its range. - return - current1_ == end1_ || - current2_ == end2_ || - current3_ == end3_ || - current4_ == end4_ || - current5_ == end5_ || - current6_ == end6_ || - current7_ == end7_; - } - - // No implementation - assignment is unsupported. - void operator=(const Iterator& other); - - const ParamGeneratorInterface* const base_; - // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. - // current[i]_ is the actual traversing iterator. - const typename ParamGenerator::iterator begin1_; - const typename ParamGenerator::iterator end1_; - typename ParamGenerator::iterator current1_; - const typename ParamGenerator::iterator begin2_; - const typename ParamGenerator::iterator end2_; - typename ParamGenerator::iterator current2_; - const typename ParamGenerator::iterator begin3_; - const typename ParamGenerator::iterator end3_; - typename ParamGenerator::iterator current3_; - const typename ParamGenerator::iterator begin4_; - const typename ParamGenerator::iterator end4_; - typename ParamGenerator::iterator current4_; - const typename ParamGenerator::iterator begin5_; - const typename ParamGenerator::iterator end5_; - typename ParamGenerator::iterator current5_; - const typename ParamGenerator::iterator begin6_; - const typename ParamGenerator::iterator end6_; - typename ParamGenerator::iterator current6_; - const typename ParamGenerator::iterator begin7_; - const typename ParamGenerator::iterator end7_; - typename ParamGenerator::iterator current7_; - ParamType current_value_; - }; // class CartesianProductGenerator7::Iterator - - // No implementation - assignment is unsupported. - void operator=(const CartesianProductGenerator7& other); - - const ParamGenerator g1_; - const ParamGenerator g2_; - const ParamGenerator g3_; - const ParamGenerator g4_; - const ParamGenerator g5_; - const ParamGenerator g6_; - const ParamGenerator g7_; -}; // class CartesianProductGenerator7 - - -template -class CartesianProductGenerator8 - : public ParamGeneratorInterface< ::std::tr1::tuple > { - public: - typedef ::std::tr1::tuple ParamType; - - CartesianProductGenerator8(const ParamGenerator& g1, - const ParamGenerator& g2, const ParamGenerator& g3, - const ParamGenerator& g4, const ParamGenerator& g5, - const ParamGenerator& g6, const ParamGenerator& g7, - const ParamGenerator& g8) - : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), - g8_(g8) {} - virtual ~CartesianProductGenerator8() {} - - virtual ParamIteratorInterface* Begin() const { - return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, - g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin(), g7_, - g7_.begin(), g8_, g8_.begin()); - } - virtual ParamIteratorInterface* End() const { - return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), - g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end(), g7_, g7_.end(), g8_, - g8_.end()); - } - - private: - class Iterator : public ParamIteratorInterface { - public: - Iterator(const ParamGeneratorInterface* base, - const ParamGenerator& g1, - const typename ParamGenerator::iterator& current1, - const ParamGenerator& g2, - const typename ParamGenerator::iterator& current2, - const ParamGenerator& g3, - const typename ParamGenerator::iterator& current3, - const ParamGenerator& g4, - const typename ParamGenerator::iterator& current4, - const ParamGenerator& g5, - const typename ParamGenerator::iterator& current5, - const ParamGenerator& g6, - const typename ParamGenerator::iterator& current6, - const ParamGenerator& g7, - const typename ParamGenerator::iterator& current7, - const ParamGenerator& g8, - const typename ParamGenerator::iterator& current8) - : base_(base), - begin1_(g1.begin()), end1_(g1.end()), current1_(current1), - begin2_(g2.begin()), end2_(g2.end()), current2_(current2), - begin3_(g3.begin()), end3_(g3.end()), current3_(current3), - begin4_(g4.begin()), end4_(g4.end()), current4_(current4), - begin5_(g5.begin()), end5_(g5.end()), current5_(current5), - begin6_(g6.begin()), end6_(g6.end()), current6_(current6), - begin7_(g7.begin()), end7_(g7.end()), current7_(current7), - begin8_(g8.begin()), end8_(g8.end()), current8_(current8) { - ComputeCurrentValue(); - } - virtual ~Iterator() {} - - virtual const ParamGeneratorInterface* BaseGenerator() const { - return base_; - } - // Advance should not be called on beyond-of-range iterators - // so no component iterators must be beyond end of range, either. - virtual void Advance() { - assert(!AtEnd()); - ++current8_; - if (current8_ == end8_) { - current8_ = begin8_; - ++current7_; - } - if (current7_ == end7_) { - current7_ = begin7_; - ++current6_; - } - if (current6_ == end6_) { - current6_ = begin6_; - ++current5_; - } - if (current5_ == end5_) { - current5_ = begin5_; - ++current4_; - } - if (current4_ == end4_) { - current4_ = begin4_; - ++current3_; - } - if (current3_ == end3_) { - current3_ = begin3_; - ++current2_; - } - if (current2_ == end2_) { - current2_ = begin2_; - ++current1_; - } - ComputeCurrentValue(); - } - virtual ParamIteratorInterface* Clone() const { - return new Iterator(*this); - } - virtual const ParamType* Current() const { return ¤t_value_; } - virtual bool Equals(const ParamIteratorInterface& other) const { - // Having the same base generator guarantees that the other - // iterator is of the same type and we can downcast. - GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) - << "The program attempted to compare iterators " - << "from different generators." << std::endl; - const Iterator* typed_other = - CheckedDowncastToActualType(&other); - // We must report iterators equal if they both point beyond their - // respective ranges. That can happen in a variety of fashions, - // so we have to consult AtEnd(). - return (AtEnd() && typed_other->AtEnd()) || - ( - current1_ == typed_other->current1_ && - current2_ == typed_other->current2_ && - current3_ == typed_other->current3_ && - current4_ == typed_other->current4_ && - current5_ == typed_other->current5_ && - current6_ == typed_other->current6_ && - current7_ == typed_other->current7_ && - current8_ == typed_other->current8_); - } - - private: - Iterator(const Iterator& other) - : base_(other.base_), - begin1_(other.begin1_), - end1_(other.end1_), - current1_(other.current1_), - begin2_(other.begin2_), - end2_(other.end2_), - current2_(other.current2_), - begin3_(other.begin3_), - end3_(other.end3_), - current3_(other.current3_), - begin4_(other.begin4_), - end4_(other.end4_), - current4_(other.current4_), - begin5_(other.begin5_), - end5_(other.end5_), - current5_(other.current5_), - begin6_(other.begin6_), - end6_(other.end6_), - current6_(other.current6_), - begin7_(other.begin7_), - end7_(other.end7_), - current7_(other.current7_), - begin8_(other.begin8_), - end8_(other.end8_), - current8_(other.current8_) { - ComputeCurrentValue(); - } - - void ComputeCurrentValue() { - if (!AtEnd()) - current_value_ = ParamType(*current1_, *current2_, *current3_, - *current4_, *current5_, *current6_, *current7_, *current8_); - } - bool AtEnd() const { - // We must report iterator past the end of the range when either of the - // component iterators has reached the end of its range. - return - current1_ == end1_ || - current2_ == end2_ || - current3_ == end3_ || - current4_ == end4_ || - current5_ == end5_ || - current6_ == end6_ || - current7_ == end7_ || - current8_ == end8_; - } - - // No implementation - assignment is unsupported. - void operator=(const Iterator& other); - - const ParamGeneratorInterface* const base_; - // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. - // current[i]_ is the actual traversing iterator. - const typename ParamGenerator::iterator begin1_; - const typename ParamGenerator::iterator end1_; - typename ParamGenerator::iterator current1_; - const typename ParamGenerator::iterator begin2_; - const typename ParamGenerator::iterator end2_; - typename ParamGenerator::iterator current2_; - const typename ParamGenerator::iterator begin3_; - const typename ParamGenerator::iterator end3_; - typename ParamGenerator::iterator current3_; - const typename ParamGenerator::iterator begin4_; - const typename ParamGenerator::iterator end4_; - typename ParamGenerator::iterator current4_; - const typename ParamGenerator::iterator begin5_; - const typename ParamGenerator::iterator end5_; - typename ParamGenerator::iterator current5_; - const typename ParamGenerator::iterator begin6_; - const typename ParamGenerator::iterator end6_; - typename ParamGenerator::iterator current6_; - const typename ParamGenerator::iterator begin7_; - const typename ParamGenerator::iterator end7_; - typename ParamGenerator::iterator current7_; - const typename ParamGenerator::iterator begin8_; - const typename ParamGenerator::iterator end8_; - typename ParamGenerator::iterator current8_; - ParamType current_value_; - }; // class CartesianProductGenerator8::Iterator - - // No implementation - assignment is unsupported. - void operator=(const CartesianProductGenerator8& other); - - const ParamGenerator g1_; - const ParamGenerator g2_; - const ParamGenerator g3_; - const ParamGenerator g4_; - const ParamGenerator g5_; - const ParamGenerator g6_; - const ParamGenerator g7_; - const ParamGenerator g8_; -}; // class CartesianProductGenerator8 - - -template -class CartesianProductGenerator9 - : public ParamGeneratorInterface< ::std::tr1::tuple > { - public: - typedef ::std::tr1::tuple ParamType; - - CartesianProductGenerator9(const ParamGenerator& g1, - const ParamGenerator& g2, const ParamGenerator& g3, - const ParamGenerator& g4, const ParamGenerator& g5, - const ParamGenerator& g6, const ParamGenerator& g7, - const ParamGenerator& g8, const ParamGenerator& g9) - : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), g8_(g8), - g9_(g9) {} - virtual ~CartesianProductGenerator9() {} - - virtual ParamIteratorInterface* Begin() const { - return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, - g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin(), g7_, - g7_.begin(), g8_, g8_.begin(), g9_, g9_.begin()); - } - virtual ParamIteratorInterface* End() const { - return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), - g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end(), g7_, g7_.end(), g8_, - g8_.end(), g9_, g9_.end()); - } - - private: - class Iterator : public ParamIteratorInterface { - public: - Iterator(const ParamGeneratorInterface* base, - const ParamGenerator& g1, - const typename ParamGenerator::iterator& current1, - const ParamGenerator& g2, - const typename ParamGenerator::iterator& current2, - const ParamGenerator& g3, - const typename ParamGenerator::iterator& current3, - const ParamGenerator& g4, - const typename ParamGenerator::iterator& current4, - const ParamGenerator& g5, - const typename ParamGenerator::iterator& current5, - const ParamGenerator& g6, - const typename ParamGenerator::iterator& current6, - const ParamGenerator& g7, - const typename ParamGenerator::iterator& current7, - const ParamGenerator& g8, - const typename ParamGenerator::iterator& current8, - const ParamGenerator& g9, - const typename ParamGenerator::iterator& current9) - : base_(base), - begin1_(g1.begin()), end1_(g1.end()), current1_(current1), - begin2_(g2.begin()), end2_(g2.end()), current2_(current2), - begin3_(g3.begin()), end3_(g3.end()), current3_(current3), - begin4_(g4.begin()), end4_(g4.end()), current4_(current4), - begin5_(g5.begin()), end5_(g5.end()), current5_(current5), - begin6_(g6.begin()), end6_(g6.end()), current6_(current6), - begin7_(g7.begin()), end7_(g7.end()), current7_(current7), - begin8_(g8.begin()), end8_(g8.end()), current8_(current8), - begin9_(g9.begin()), end9_(g9.end()), current9_(current9) { - ComputeCurrentValue(); - } - virtual ~Iterator() {} - - virtual const ParamGeneratorInterface* BaseGenerator() const { - return base_; - } - // Advance should not be called on beyond-of-range iterators - // so no component iterators must be beyond end of range, either. - virtual void Advance() { - assert(!AtEnd()); - ++current9_; - if (current9_ == end9_) { - current9_ = begin9_; - ++current8_; - } - if (current8_ == end8_) { - current8_ = begin8_; - ++current7_; - } - if (current7_ == end7_) { - current7_ = begin7_; - ++current6_; - } - if (current6_ == end6_) { - current6_ = begin6_; - ++current5_; - } - if (current5_ == end5_) { - current5_ = begin5_; - ++current4_; - } - if (current4_ == end4_) { - current4_ = begin4_; - ++current3_; - } - if (current3_ == end3_) { - current3_ = begin3_; - ++current2_; - } - if (current2_ == end2_) { - current2_ = begin2_; - ++current1_; - } - ComputeCurrentValue(); - } - virtual ParamIteratorInterface* Clone() const { - return new Iterator(*this); - } - virtual const ParamType* Current() const { return ¤t_value_; } - virtual bool Equals(const ParamIteratorInterface& other) const { - // Having the same base generator guarantees that the other - // iterator is of the same type and we can downcast. - GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) - << "The program attempted to compare iterators " - << "from different generators." << std::endl; - const Iterator* typed_other = - CheckedDowncastToActualType(&other); - // We must report iterators equal if they both point beyond their - // respective ranges. That can happen in a variety of fashions, - // so we have to consult AtEnd(). - return (AtEnd() && typed_other->AtEnd()) || - ( - current1_ == typed_other->current1_ && - current2_ == typed_other->current2_ && - current3_ == typed_other->current3_ && - current4_ == typed_other->current4_ && - current5_ == typed_other->current5_ && - current6_ == typed_other->current6_ && - current7_ == typed_other->current7_ && - current8_ == typed_other->current8_ && - current9_ == typed_other->current9_); - } - - private: - Iterator(const Iterator& other) - : base_(other.base_), - begin1_(other.begin1_), - end1_(other.end1_), - current1_(other.current1_), - begin2_(other.begin2_), - end2_(other.end2_), - current2_(other.current2_), - begin3_(other.begin3_), - end3_(other.end3_), - current3_(other.current3_), - begin4_(other.begin4_), - end4_(other.end4_), - current4_(other.current4_), - begin5_(other.begin5_), - end5_(other.end5_), - current5_(other.current5_), - begin6_(other.begin6_), - end6_(other.end6_), - current6_(other.current6_), - begin7_(other.begin7_), - end7_(other.end7_), - current7_(other.current7_), - begin8_(other.begin8_), - end8_(other.end8_), - current8_(other.current8_), - begin9_(other.begin9_), - end9_(other.end9_), - current9_(other.current9_) { - ComputeCurrentValue(); - } - - void ComputeCurrentValue() { - if (!AtEnd()) - current_value_ = ParamType(*current1_, *current2_, *current3_, - *current4_, *current5_, *current6_, *current7_, *current8_, - *current9_); - } - bool AtEnd() const { - // We must report iterator past the end of the range when either of the - // component iterators has reached the end of its range. - return - current1_ == end1_ || - current2_ == end2_ || - current3_ == end3_ || - current4_ == end4_ || - current5_ == end5_ || - current6_ == end6_ || - current7_ == end7_ || - current8_ == end8_ || - current9_ == end9_; - } - - // No implementation - assignment is unsupported. - void operator=(const Iterator& other); - - const ParamGeneratorInterface* const base_; - // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. - // current[i]_ is the actual traversing iterator. - const typename ParamGenerator::iterator begin1_; - const typename ParamGenerator::iterator end1_; - typename ParamGenerator::iterator current1_; - const typename ParamGenerator::iterator begin2_; - const typename ParamGenerator::iterator end2_; - typename ParamGenerator::iterator current2_; - const typename ParamGenerator::iterator begin3_; - const typename ParamGenerator::iterator end3_; - typename ParamGenerator::iterator current3_; - const typename ParamGenerator::iterator begin4_; - const typename ParamGenerator::iterator end4_; - typename ParamGenerator::iterator current4_; - const typename ParamGenerator::iterator begin5_; - const typename ParamGenerator::iterator end5_; - typename ParamGenerator::iterator current5_; - const typename ParamGenerator::iterator begin6_; - const typename ParamGenerator::iterator end6_; - typename ParamGenerator::iterator current6_; - const typename ParamGenerator::iterator begin7_; - const typename ParamGenerator::iterator end7_; - typename ParamGenerator::iterator current7_; - const typename ParamGenerator::iterator begin8_; - const typename ParamGenerator::iterator end8_; - typename ParamGenerator::iterator current8_; - const typename ParamGenerator::iterator begin9_; - const typename ParamGenerator::iterator end9_; - typename ParamGenerator::iterator current9_; - ParamType current_value_; - }; // class CartesianProductGenerator9::Iterator - - // No implementation - assignment is unsupported. - void operator=(const CartesianProductGenerator9& other); - - const ParamGenerator g1_; - const ParamGenerator g2_; - const ParamGenerator g3_; - const ParamGenerator g4_; - const ParamGenerator g5_; - const ParamGenerator g6_; - const ParamGenerator g7_; - const ParamGenerator g8_; - const ParamGenerator g9_; -}; // class CartesianProductGenerator9 - - -template -class CartesianProductGenerator10 - : public ParamGeneratorInterface< ::std::tr1::tuple > { - public: - typedef ::std::tr1::tuple ParamType; - - CartesianProductGenerator10(const ParamGenerator& g1, - const ParamGenerator& g2, const ParamGenerator& g3, - const ParamGenerator& g4, const ParamGenerator& g5, - const ParamGenerator& g6, const ParamGenerator& g7, - const ParamGenerator& g8, const ParamGenerator& g9, - const ParamGenerator& g10) - : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), g8_(g8), - g9_(g9), g10_(g10) {} - virtual ~CartesianProductGenerator10() {} - - virtual ParamIteratorInterface* Begin() const { - return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, - g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin(), g7_, - g7_.begin(), g8_, g8_.begin(), g9_, g9_.begin(), g10_, g10_.begin()); - } - virtual ParamIteratorInterface* End() const { - return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), - g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end(), g7_, g7_.end(), g8_, - g8_.end(), g9_, g9_.end(), g10_, g10_.end()); - } - - private: - class Iterator : public ParamIteratorInterface { - public: - Iterator(const ParamGeneratorInterface* base, - const ParamGenerator& g1, - const typename ParamGenerator::iterator& current1, - const ParamGenerator& g2, - const typename ParamGenerator::iterator& current2, - const ParamGenerator& g3, - const typename ParamGenerator::iterator& current3, - const ParamGenerator& g4, - const typename ParamGenerator::iterator& current4, - const ParamGenerator& g5, - const typename ParamGenerator::iterator& current5, - const ParamGenerator& g6, - const typename ParamGenerator::iterator& current6, - const ParamGenerator& g7, - const typename ParamGenerator::iterator& current7, - const ParamGenerator& g8, - const typename ParamGenerator::iterator& current8, - const ParamGenerator& g9, - const typename ParamGenerator::iterator& current9, - const ParamGenerator& g10, - const typename ParamGenerator::iterator& current10) - : base_(base), - begin1_(g1.begin()), end1_(g1.end()), current1_(current1), - begin2_(g2.begin()), end2_(g2.end()), current2_(current2), - begin3_(g3.begin()), end3_(g3.end()), current3_(current3), - begin4_(g4.begin()), end4_(g4.end()), current4_(current4), - begin5_(g5.begin()), end5_(g5.end()), current5_(current5), - begin6_(g6.begin()), end6_(g6.end()), current6_(current6), - begin7_(g7.begin()), end7_(g7.end()), current7_(current7), - begin8_(g8.begin()), end8_(g8.end()), current8_(current8), - begin9_(g9.begin()), end9_(g9.end()), current9_(current9), - begin10_(g10.begin()), end10_(g10.end()), current10_(current10) { - ComputeCurrentValue(); - } - virtual ~Iterator() {} - - virtual const ParamGeneratorInterface* BaseGenerator() const { - return base_; - } - // Advance should not be called on beyond-of-range iterators - // so no component iterators must be beyond end of range, either. - virtual void Advance() { - assert(!AtEnd()); - ++current10_; - if (current10_ == end10_) { - current10_ = begin10_; - ++current9_; - } - if (current9_ == end9_) { - current9_ = begin9_; - ++current8_; - } - if (current8_ == end8_) { - current8_ = begin8_; - ++current7_; - } - if (current7_ == end7_) { - current7_ = begin7_; - ++current6_; - } - if (current6_ == end6_) { - current6_ = begin6_; - ++current5_; - } - if (current5_ == end5_) { - current5_ = begin5_; - ++current4_; - } - if (current4_ == end4_) { - current4_ = begin4_; - ++current3_; - } - if (current3_ == end3_) { - current3_ = begin3_; - ++current2_; - } - if (current2_ == end2_) { - current2_ = begin2_; - ++current1_; - } - ComputeCurrentValue(); - } - virtual ParamIteratorInterface* Clone() const { - return new Iterator(*this); - } - virtual const ParamType* Current() const { return ¤t_value_; } - virtual bool Equals(const ParamIteratorInterface& other) const { - // Having the same base generator guarantees that the other - // iterator is of the same type and we can downcast. - GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) - << "The program attempted to compare iterators " - << "from different generators." << std::endl; - const Iterator* typed_other = - CheckedDowncastToActualType(&other); - // We must report iterators equal if they both point beyond their - // respective ranges. That can happen in a variety of fashions, - // so we have to consult AtEnd(). - return (AtEnd() && typed_other->AtEnd()) || - ( - current1_ == typed_other->current1_ && - current2_ == typed_other->current2_ && - current3_ == typed_other->current3_ && - current4_ == typed_other->current4_ && - current5_ == typed_other->current5_ && - current6_ == typed_other->current6_ && - current7_ == typed_other->current7_ && - current8_ == typed_other->current8_ && - current9_ == typed_other->current9_ && - current10_ == typed_other->current10_); - } - - private: - Iterator(const Iterator& other) - : base_(other.base_), - begin1_(other.begin1_), - end1_(other.end1_), - current1_(other.current1_), - begin2_(other.begin2_), - end2_(other.end2_), - current2_(other.current2_), - begin3_(other.begin3_), - end3_(other.end3_), - current3_(other.current3_), - begin4_(other.begin4_), - end4_(other.end4_), - current4_(other.current4_), - begin5_(other.begin5_), - end5_(other.end5_), - current5_(other.current5_), - begin6_(other.begin6_), - end6_(other.end6_), - current6_(other.current6_), - begin7_(other.begin7_), - end7_(other.end7_), - current7_(other.current7_), - begin8_(other.begin8_), - end8_(other.end8_), - current8_(other.current8_), - begin9_(other.begin9_), - end9_(other.end9_), - current9_(other.current9_), - begin10_(other.begin10_), - end10_(other.end10_), - current10_(other.current10_) { - ComputeCurrentValue(); - } - - void ComputeCurrentValue() { - if (!AtEnd()) - current_value_ = ParamType(*current1_, *current2_, *current3_, - *current4_, *current5_, *current6_, *current7_, *current8_, - *current9_, *current10_); - } - bool AtEnd() const { - // We must report iterator past the end of the range when either of the - // component iterators has reached the end of its range. - return - current1_ == end1_ || - current2_ == end2_ || - current3_ == end3_ || - current4_ == end4_ || - current5_ == end5_ || - current6_ == end6_ || - current7_ == end7_ || - current8_ == end8_ || - current9_ == end9_ || - current10_ == end10_; - } - - // No implementation - assignment is unsupported. - void operator=(const Iterator& other); - - const ParamGeneratorInterface* const base_; - // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. - // current[i]_ is the actual traversing iterator. - const typename ParamGenerator::iterator begin1_; - const typename ParamGenerator::iterator end1_; - typename ParamGenerator::iterator current1_; - const typename ParamGenerator::iterator begin2_; - const typename ParamGenerator::iterator end2_; - typename ParamGenerator::iterator current2_; - const typename ParamGenerator::iterator begin3_; - const typename ParamGenerator::iterator end3_; - typename ParamGenerator::iterator current3_; - const typename ParamGenerator::iterator begin4_; - const typename ParamGenerator::iterator end4_; - typename ParamGenerator::iterator current4_; - const typename ParamGenerator::iterator begin5_; - const typename ParamGenerator::iterator end5_; - typename ParamGenerator::iterator current5_; - const typename ParamGenerator::iterator begin6_; - const typename ParamGenerator::iterator end6_; - typename ParamGenerator::iterator current6_; - const typename ParamGenerator::iterator begin7_; - const typename ParamGenerator::iterator end7_; - typename ParamGenerator::iterator current7_; - const typename ParamGenerator::iterator begin8_; - const typename ParamGenerator::iterator end8_; - typename ParamGenerator::iterator current8_; - const typename ParamGenerator::iterator begin9_; - const typename ParamGenerator::iterator end9_; - typename ParamGenerator::iterator current9_; - const typename ParamGenerator::iterator begin10_; - const typename ParamGenerator::iterator end10_; - typename ParamGenerator::iterator current10_; - ParamType current_value_; - }; // class CartesianProductGenerator10::Iterator - - // No implementation - assignment is unsupported. - void operator=(const CartesianProductGenerator10& other); - - const ParamGenerator g1_; - const ParamGenerator g2_; - const ParamGenerator g3_; - const ParamGenerator g4_; - const ParamGenerator g5_; - const ParamGenerator g6_; - const ParamGenerator g7_; - const ParamGenerator g8_; - const ParamGenerator g9_; - const ParamGenerator g10_; -}; // class CartesianProductGenerator10 - - -// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. -// -// Helper classes providing Combine() with polymorphic features. They allow -// casting CartesianProductGeneratorN to ParamGenerator if T is -// convertible to U. -// -template -class CartesianProductHolder2 { - public: -CartesianProductHolder2(const Generator1& g1, const Generator2& g2) - : g1_(g1), g2_(g2) {} - template - operator ParamGenerator< ::std::tr1::tuple >() const { - return ParamGenerator< ::std::tr1::tuple >( - new CartesianProductGenerator2( - static_cast >(g1_), - static_cast >(g2_))); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const CartesianProductHolder2& other); - - const Generator1 g1_; - const Generator2 g2_; -}; // class CartesianProductHolder2 - -template -class CartesianProductHolder3 { - public: -CartesianProductHolder3(const Generator1& g1, const Generator2& g2, - const Generator3& g3) - : g1_(g1), g2_(g2), g3_(g3) {} - template - operator ParamGenerator< ::std::tr1::tuple >() const { - return ParamGenerator< ::std::tr1::tuple >( - new CartesianProductGenerator3( - static_cast >(g1_), - static_cast >(g2_), - static_cast >(g3_))); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const CartesianProductHolder3& other); - - const Generator1 g1_; - const Generator2 g2_; - const Generator3 g3_; -}; // class CartesianProductHolder3 - -template -class CartesianProductHolder4 { - public: -CartesianProductHolder4(const Generator1& g1, const Generator2& g2, - const Generator3& g3, const Generator4& g4) - : g1_(g1), g2_(g2), g3_(g3), g4_(g4) {} - template - operator ParamGenerator< ::std::tr1::tuple >() const { - return ParamGenerator< ::std::tr1::tuple >( - new CartesianProductGenerator4( - static_cast >(g1_), - static_cast >(g2_), - static_cast >(g3_), - static_cast >(g4_))); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const CartesianProductHolder4& other); - - const Generator1 g1_; - const Generator2 g2_; - const Generator3 g3_; - const Generator4 g4_; -}; // class CartesianProductHolder4 - -template -class CartesianProductHolder5 { - public: -CartesianProductHolder5(const Generator1& g1, const Generator2& g2, - const Generator3& g3, const Generator4& g4, const Generator5& g5) - : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5) {} - template - operator ParamGenerator< ::std::tr1::tuple >() const { - return ParamGenerator< ::std::tr1::tuple >( - new CartesianProductGenerator5( - static_cast >(g1_), - static_cast >(g2_), - static_cast >(g3_), - static_cast >(g4_), - static_cast >(g5_))); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const CartesianProductHolder5& other); - - const Generator1 g1_; - const Generator2 g2_; - const Generator3 g3_; - const Generator4 g4_; - const Generator5 g5_; -}; // class CartesianProductHolder5 - -template -class CartesianProductHolder6 { - public: -CartesianProductHolder6(const Generator1& g1, const Generator2& g2, - const Generator3& g3, const Generator4& g4, const Generator5& g5, - const Generator6& g6) - : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6) {} - template - operator ParamGenerator< ::std::tr1::tuple >() const { - return ParamGenerator< ::std::tr1::tuple >( - new CartesianProductGenerator6( - static_cast >(g1_), - static_cast >(g2_), - static_cast >(g3_), - static_cast >(g4_), - static_cast >(g5_), - static_cast >(g6_))); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const CartesianProductHolder6& other); - - const Generator1 g1_; - const Generator2 g2_; - const Generator3 g3_; - const Generator4 g4_; - const Generator5 g5_; - const Generator6 g6_; -}; // class CartesianProductHolder6 - -template -class CartesianProductHolder7 { - public: -CartesianProductHolder7(const Generator1& g1, const Generator2& g2, - const Generator3& g3, const Generator4& g4, const Generator5& g5, - const Generator6& g6, const Generator7& g7) - : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7) {} - template - operator ParamGenerator< ::std::tr1::tuple >() const { - return ParamGenerator< ::std::tr1::tuple >( - new CartesianProductGenerator7( - static_cast >(g1_), - static_cast >(g2_), - static_cast >(g3_), - static_cast >(g4_), - static_cast >(g5_), - static_cast >(g6_), - static_cast >(g7_))); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const CartesianProductHolder7& other); - - const Generator1 g1_; - const Generator2 g2_; - const Generator3 g3_; - const Generator4 g4_; - const Generator5 g5_; - const Generator6 g6_; - const Generator7 g7_; -}; // class CartesianProductHolder7 - -template -class CartesianProductHolder8 { - public: -CartesianProductHolder8(const Generator1& g1, const Generator2& g2, - const Generator3& g3, const Generator4& g4, const Generator5& g5, - const Generator6& g6, const Generator7& g7, const Generator8& g8) - : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), - g8_(g8) {} - template - operator ParamGenerator< ::std::tr1::tuple >() const { - return ParamGenerator< ::std::tr1::tuple >( - new CartesianProductGenerator8( - static_cast >(g1_), - static_cast >(g2_), - static_cast >(g3_), - static_cast >(g4_), - static_cast >(g5_), - static_cast >(g6_), - static_cast >(g7_), - static_cast >(g8_))); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const CartesianProductHolder8& other); - - const Generator1 g1_; - const Generator2 g2_; - const Generator3 g3_; - const Generator4 g4_; - const Generator5 g5_; - const Generator6 g6_; - const Generator7 g7_; - const Generator8 g8_; -}; // class CartesianProductHolder8 - -template -class CartesianProductHolder9 { - public: -CartesianProductHolder9(const Generator1& g1, const Generator2& g2, - const Generator3& g3, const Generator4& g4, const Generator5& g5, - const Generator6& g6, const Generator7& g7, const Generator8& g8, - const Generator9& g9) - : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), g8_(g8), - g9_(g9) {} - template - operator ParamGenerator< ::std::tr1::tuple >() const { - return ParamGenerator< ::std::tr1::tuple >( - new CartesianProductGenerator9( - static_cast >(g1_), - static_cast >(g2_), - static_cast >(g3_), - static_cast >(g4_), - static_cast >(g5_), - static_cast >(g6_), - static_cast >(g7_), - static_cast >(g8_), - static_cast >(g9_))); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const CartesianProductHolder9& other); - - const Generator1 g1_; - const Generator2 g2_; - const Generator3 g3_; - const Generator4 g4_; - const Generator5 g5_; - const Generator6 g6_; - const Generator7 g7_; - const Generator8 g8_; - const Generator9 g9_; -}; // class CartesianProductHolder9 - -template -class CartesianProductHolder10 { - public: -CartesianProductHolder10(const Generator1& g1, const Generator2& g2, - const Generator3& g3, const Generator4& g4, const Generator5& g5, - const Generator6& g6, const Generator7& g7, const Generator8& g8, - const Generator9& g9, const Generator10& g10) - : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), g8_(g8), - g9_(g9), g10_(g10) {} - template - operator ParamGenerator< ::std::tr1::tuple >() const { - return ParamGenerator< ::std::tr1::tuple >( - new CartesianProductGenerator10( - static_cast >(g1_), - static_cast >(g2_), - static_cast >(g3_), - static_cast >(g4_), - static_cast >(g5_), - static_cast >(g6_), - static_cast >(g7_), - static_cast >(g8_), - static_cast >(g9_), - static_cast >(g10_))); - } - - private: - // No implementation - assignment is unsupported. - void operator=(const CartesianProductHolder10& other); - - const Generator1 g1_; - const Generator2 g2_; - const Generator3 g3_; - const Generator4 g4_; - const Generator5 g5_; - const Generator6 g6_; - const Generator7 g7_; - const Generator8 g8_; - const Generator9 g9_; - const Generator10 g10_; -}; // class CartesianProductHolder10 - -# endif // GTEST_HAS_COMBINE - -} // namespace internal -} // namespace testing - -#endif // GTEST_HAS_PARAM_TEST - -#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_ - -#if GTEST_HAS_PARAM_TEST - -namespace testing { - -// Functions producing parameter generators. -// -// Google Test uses these generators to produce parameters for value- -// parameterized tests. When a parameterized test case is instantiated -// with a particular generator, Google Test creates and runs tests -// for each element in the sequence produced by the generator. -// -// In the following sample, tests from test case FooTest are instantiated -// each three times with parameter values 3, 5, and 8: -// -// class FooTest : public TestWithParam { ... }; -// -// TEST_P(FooTest, TestThis) { -// } -// TEST_P(FooTest, TestThat) { -// } -// INSTANTIATE_TEST_CASE_P(TestSequence, FooTest, Values(3, 5, 8)); -// - -// Range() returns generators providing sequences of values in a range. -// -// Synopsis: -// Range(start, end) -// - returns a generator producing a sequence of values {start, start+1, -// start+2, ..., }. -// Range(start, end, step) -// - returns a generator producing a sequence of values {start, start+step, -// start+step+step, ..., }. -// Notes: -// * The generated sequences never include end. For example, Range(1, 5) -// returns a generator producing a sequence {1, 2, 3, 4}. Range(1, 9, 2) -// returns a generator producing {1, 3, 5, 7}. -// * start and end must have the same type. That type may be any integral or -// floating-point type or a user defined type satisfying these conditions: -// * It must be assignable (have operator=() defined). -// * It must have operator+() (operator+(int-compatible type) for -// two-operand version). -// * It must have operator<() defined. -// Elements in the resulting sequences will also have that type. -// * Condition start < end must be satisfied in order for resulting sequences -// to contain any elements. -// -template -internal::ParamGenerator Range(T start, T end, IncrementT step) { - return internal::ParamGenerator( - new internal::RangeGenerator(start, end, step)); -} - -template -internal::ParamGenerator Range(T start, T end) { - return Range(start, end, 1); -} - -// ValuesIn() function allows generation of tests with parameters coming from -// a container. -// -// Synopsis: -// ValuesIn(const T (&array)[N]) -// - returns a generator producing sequences with elements from -// a C-style array. -// ValuesIn(const Container& container) -// - returns a generator producing sequences with elements from -// an STL-style container. -// ValuesIn(Iterator begin, Iterator end) -// - returns a generator producing sequences with elements from -// a range [begin, end) defined by a pair of STL-style iterators. These -// iterators can also be plain C pointers. -// -// Please note that ValuesIn copies the values from the containers -// passed in and keeps them to generate tests in RUN_ALL_TESTS(). -// -// Examples: -// -// This instantiates tests from test case StringTest -// each with C-string values of "foo", "bar", and "baz": -// -// const char* strings[] = {"foo", "bar", "baz"}; -// INSTANTIATE_TEST_CASE_P(StringSequence, SrtingTest, ValuesIn(strings)); -// -// This instantiates tests from test case StlStringTest -// each with STL strings with values "a" and "b": -// -// ::std::vector< ::std::string> GetParameterStrings() { -// ::std::vector< ::std::string> v; -// v.push_back("a"); -// v.push_back("b"); -// return v; -// } -// -// INSTANTIATE_TEST_CASE_P(CharSequence, -// StlStringTest, -// ValuesIn(GetParameterStrings())); -// -// -// This will also instantiate tests from CharTest -// each with parameter values 'a' and 'b': -// -// ::std::list GetParameterChars() { -// ::std::list list; -// list.push_back('a'); -// list.push_back('b'); -// return list; -// } -// ::std::list l = GetParameterChars(); -// INSTANTIATE_TEST_CASE_P(CharSequence2, -// CharTest, -// ValuesIn(l.begin(), l.end())); -// -template -internal::ParamGenerator< - typename ::testing::internal::IteratorTraits::value_type> -ValuesIn(ForwardIterator begin, ForwardIterator end) { - typedef typename ::testing::internal::IteratorTraits - ::value_type ParamType; - return internal::ParamGenerator( - new internal::ValuesInIteratorRangeGenerator(begin, end)); -} - -template -internal::ParamGenerator ValuesIn(const T (&array)[N]) { - return ValuesIn(array, array + N); -} - -template -internal::ParamGenerator ValuesIn( - const Container& container) { - return ValuesIn(container.begin(), container.end()); -} - -// Values() allows generating tests from explicitly specified list of -// parameters. -// -// Synopsis: -// Values(T v1, T v2, ..., T vN) -// - returns a generator producing sequences with elements v1, v2, ..., vN. -// -// For example, this instantiates tests from test case BarTest each -// with values "one", "two", and "three": -// -// INSTANTIATE_TEST_CASE_P(NumSequence, BarTest, Values("one", "two", "three")); -// -// This instantiates tests from test case BazTest each with values 1, 2, 3.5. -// The exact type of values will depend on the type of parameter in BazTest. -// -// INSTANTIATE_TEST_CASE_P(FloatingNumbers, BazTest, Values(1, 2, 3.5)); -// -// Currently, Values() supports from 1 to 50 parameters. -// -template -internal::ValueArray1 Values(T1 v1) { - return internal::ValueArray1(v1); -} - -template -internal::ValueArray2 Values(T1 v1, T2 v2) { - return internal::ValueArray2(v1, v2); -} - -template -internal::ValueArray3 Values(T1 v1, T2 v2, T3 v3) { - return internal::ValueArray3(v1, v2, v3); -} - -template -internal::ValueArray4 Values(T1 v1, T2 v2, T3 v3, T4 v4) { - return internal::ValueArray4(v1, v2, v3, v4); -} - -template -internal::ValueArray5 Values(T1 v1, T2 v2, T3 v3, T4 v4, - T5 v5) { - return internal::ValueArray5(v1, v2, v3, v4, v5); -} - -template -internal::ValueArray6 Values(T1 v1, T2 v2, T3 v3, - T4 v4, T5 v5, T6 v6) { - return internal::ValueArray6(v1, v2, v3, v4, v5, v6); -} - -template -internal::ValueArray7 Values(T1 v1, T2 v2, T3 v3, - T4 v4, T5 v5, T6 v6, T7 v7) { - return internal::ValueArray7(v1, v2, v3, v4, v5, - v6, v7); -} - -template -internal::ValueArray8 Values(T1 v1, T2 v2, - T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8) { - return internal::ValueArray8(v1, v2, v3, v4, - v5, v6, v7, v8); -} - -template -internal::ValueArray9 Values(T1 v1, T2 v2, - T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9) { - return internal::ValueArray9(v1, v2, v3, - v4, v5, v6, v7, v8, v9); -} - -template -internal::ValueArray10 Values(T1 v1, - T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10) { - return internal::ValueArray10(v1, - v2, v3, v4, v5, v6, v7, v8, v9, v10); -} - -template -internal::ValueArray11 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11) { - return internal::ValueArray11(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11); -} - -template -internal::ValueArray12 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12) { - return internal::ValueArray12(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12); -} - -template -internal::ValueArray13 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13) { - return internal::ValueArray13(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13); -} - -template -internal::ValueArray14 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14) { - return internal::ValueArray14(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, - v14); -} - -template -internal::ValueArray15 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, - T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15) { - return internal::ValueArray15(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, - v13, v14, v15); -} - -template -internal::ValueArray16 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, - T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, - T16 v16) { - return internal::ValueArray16(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, - v12, v13, v14, v15, v16); -} - -template -internal::ValueArray17 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, - T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, - T16 v16, T17 v17) { - return internal::ValueArray17(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, - v11, v12, v13, v14, v15, v16, v17); -} - -template -internal::ValueArray18 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, - T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, - T16 v16, T17 v17, T18 v18) { - return internal::ValueArray18(v1, v2, v3, v4, v5, v6, v7, v8, v9, - v10, v11, v12, v13, v14, v15, v16, v17, v18); -} - -template -internal::ValueArray19 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, - T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, - T15 v15, T16 v16, T17 v17, T18 v18, T19 v19) { - return internal::ValueArray19(v1, v2, v3, v4, v5, v6, v7, v8, - v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19); -} - -template -internal::ValueArray20 Values(T1 v1, T2 v2, T3 v3, T4 v4, - T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, - T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20) { - return internal::ValueArray20(v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20); -} - -template -internal::ValueArray21 Values(T1 v1, T2 v2, T3 v3, T4 v4, - T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, - T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21) { - return internal::ValueArray21(v1, v2, v3, v4, v5, v6, - v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21); -} - -template -internal::ValueArray22 Values(T1 v1, T2 v2, T3 v3, - T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, - T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, - T21 v21, T22 v22) { - return internal::ValueArray22(v1, v2, v3, v4, - v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, - v20, v21, v22); -} - -template -internal::ValueArray23 Values(T1 v1, T2 v2, - T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, - T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, - T21 v21, T22 v22, T23 v23) { - return internal::ValueArray23(v1, v2, v3, - v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, - v20, v21, v22, v23); -} - -template -internal::ValueArray24 Values(T1 v1, T2 v2, - T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, - T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, - T21 v21, T22 v22, T23 v23, T24 v24) { - return internal::ValueArray24(v1, v2, - v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, - v19, v20, v21, v22, v23, v24); -} - -template -internal::ValueArray25 Values(T1 v1, - T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, - T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, - T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25) { - return internal::ValueArray25(v1, - v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, - v18, v19, v20, v21, v22, v23, v24, v25); -} - -template -internal::ValueArray26 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26) { - return internal::ValueArray26(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, - v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26); -} - -template -internal::ValueArray27 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27) { - return internal::ValueArray27(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, - v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27); -} - -template -internal::ValueArray28 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28) { - return internal::ValueArray28(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, - v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, - v28); -} - -template -internal::ValueArray29 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29) { - return internal::ValueArray29(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, - v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, - v27, v28, v29); -} - -template -internal::ValueArray30 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, - T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, - T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, - T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30) { - return internal::ValueArray30(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, - v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, - v26, v27, v28, v29, v30); -} - -template -internal::ValueArray31 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, - T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, - T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, - T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31) { - return internal::ValueArray31(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, - v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, - v25, v26, v27, v28, v29, v30, v31); -} - -template -internal::ValueArray32 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, - T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, - T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, - T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, - T32 v32) { - return internal::ValueArray32(v1, v2, v3, v4, v5, v6, v7, v8, v9, - v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, - v24, v25, v26, v27, v28, v29, v30, v31, v32); -} - -template -internal::ValueArray33 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, - T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, - T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, - T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, - T32 v32, T33 v33) { - return internal::ValueArray33(v1, v2, v3, v4, v5, v6, v7, v8, - v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, - v24, v25, v26, v27, v28, v29, v30, v31, v32, v33); -} - -template -internal::ValueArray34 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, - T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, - T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, - T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, - T31 v31, T32 v32, T33 v33, T34 v34) { - return internal::ValueArray34(v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, - v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34); -} - -template -internal::ValueArray35 Values(T1 v1, T2 v2, T3 v3, T4 v4, - T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, - T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, - T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, - T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35) { - return internal::ValueArray35(v1, v2, v3, v4, v5, v6, - v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, - v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35); -} - -template -internal::ValueArray36 Values(T1 v1, T2 v2, T3 v3, T4 v4, - T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, - T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, - T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, - T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36) { - return internal::ValueArray36(v1, v2, v3, v4, - v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, - v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, - v34, v35, v36); -} - -template -internal::ValueArray37 Values(T1 v1, T2 v2, T3 v3, - T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, - T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, - T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, - T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, - T37 v37) { - return internal::ValueArray37(v1, v2, v3, - v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, - v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, - v34, v35, v36, v37); -} - -template -internal::ValueArray38 Values(T1 v1, T2 v2, - T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, - T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, - T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, - T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, - T37 v37, T38 v38) { - return internal::ValueArray38(v1, v2, - v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, - v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, - v33, v34, v35, v36, v37, v38); -} - -template -internal::ValueArray39 Values(T1 v1, T2 v2, - T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, - T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, - T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, - T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, - T37 v37, T38 v38, T39 v39) { - return internal::ValueArray39(v1, - v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, - v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, - v32, v33, v34, v35, v36, v37, v38, v39); -} - -template -internal::ValueArray40 Values(T1 v1, - T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, - T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, - T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, - T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, - T36 v36, T37 v37, T38 v38, T39 v39, T40 v40) { - return internal::ValueArray40(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, - v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, - v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40); -} - -template -internal::ValueArray41 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, - T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41) { - return internal::ValueArray41(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, - v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, - v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41); -} - -template -internal::ValueArray42 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, - T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, - T42 v42) { - return internal::ValueArray42(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, - v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, - v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, - v42); -} - -template -internal::ValueArray43 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, - T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, - T42 v42, T43 v43) { - return internal::ValueArray43(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, - v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, - v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, - v41, v42, v43); -} - -template -internal::ValueArray44 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, - T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, - T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, - T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, - T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, - T42 v42, T43 v43, T44 v44) { - return internal::ValueArray44(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, - v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, - v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, - v40, v41, v42, v43, v44); -} - -template -internal::ValueArray45 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, - T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, - T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, - T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, - T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, - T41 v41, T42 v42, T43 v43, T44 v44, T45 v45) { - return internal::ValueArray45(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, - v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, - v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, - v39, v40, v41, v42, v43, v44, v45); -} - -template -internal::ValueArray46 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, - T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, - T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, - T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, - T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, - T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, T46 v46) { - return internal::ValueArray46(v1, v2, v3, v4, v5, v6, v7, v8, v9, - v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, - v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, - v38, v39, v40, v41, v42, v43, v44, v45, v46); -} - -template -internal::ValueArray47 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, - T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, - T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, - T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, - T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, - T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47) { - return internal::ValueArray47(v1, v2, v3, v4, v5, v6, v7, v8, - v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, - v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, - v38, v39, v40, v41, v42, v43, v44, v45, v46, v47); -} - -template -internal::ValueArray48 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, - T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, - T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, - T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, - T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, - T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47, - T48 v48) { - return internal::ValueArray48(v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, - v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, - v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48); -} - -template -internal::ValueArray49 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, - T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, - T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, - T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, - T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, - T39 v39, T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, - T47 v47, T48 v48, T49 v49) { - return internal::ValueArray49(v1, v2, v3, v4, v5, v6, - v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, - v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, - v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49); -} - -template -internal::ValueArray50 Values(T1 v1, T2 v2, T3 v3, T4 v4, - T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, - T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, - T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, - T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, - T38 v38, T39 v39, T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, - T46 v46, T47 v47, T48 v48, T49 v49, T50 v50) { - return internal::ValueArray50(v1, v2, v3, v4, - v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, - v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, - v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, - v48, v49, v50); -} - -// Bool() allows generating tests with parameters in a set of (false, true). -// -// Synopsis: -// Bool() -// - returns a generator producing sequences with elements {false, true}. -// -// It is useful when testing code that depends on Boolean flags. Combinations -// of multiple flags can be tested when several Bool()'s are combined using -// Combine() function. -// -// In the following example all tests in the test case FlagDependentTest -// will be instantiated twice with parameters false and true. -// -// class FlagDependentTest : public testing::TestWithParam { -// virtual void SetUp() { -// external_flag = GetParam(); -// } -// } -// INSTANTIATE_TEST_CASE_P(BoolSequence, FlagDependentTest, Bool()); -// -inline internal::ParamGenerator Bool() { - return Values(false, true); -} - -# if GTEST_HAS_COMBINE -// Combine() allows the user to combine two or more sequences to produce -// values of a Cartesian product of those sequences' elements. -// -// Synopsis: -// Combine(gen1, gen2, ..., genN) -// - returns a generator producing sequences with elements coming from -// the Cartesian product of elements from the sequences generated by -// gen1, gen2, ..., genN. The sequence elements will have a type of -// tuple where T1, T2, ..., TN are the types -// of elements from sequences produces by gen1, gen2, ..., genN. -// -// Combine can have up to 10 arguments. This number is currently limited -// by the maximum number of elements in the tuple implementation used by Google -// Test. -// -// Example: -// -// This will instantiate tests in test case AnimalTest each one with -// the parameter values tuple("cat", BLACK), tuple("cat", WHITE), -// tuple("dog", BLACK), and tuple("dog", WHITE): -// -// enum Color { BLACK, GRAY, WHITE }; -// class AnimalTest -// : public testing::TestWithParam > {...}; -// -// TEST_P(AnimalTest, AnimalLooksNice) {...} -// -// INSTANTIATE_TEST_CASE_P(AnimalVariations, AnimalTest, -// Combine(Values("cat", "dog"), -// Values(BLACK, WHITE))); -// -// This will instantiate tests in FlagDependentTest with all variations of two -// Boolean flags: -// -// class FlagDependentTest -// : public testing::TestWithParam > { -// virtual void SetUp() { -// // Assigns external_flag_1 and external_flag_2 values from the tuple. -// tie(external_flag_1, external_flag_2) = GetParam(); -// } -// }; -// -// TEST_P(FlagDependentTest, TestFeature1) { -// // Test your code using external_flag_1 and external_flag_2 here. -// } -// INSTANTIATE_TEST_CASE_P(TwoBoolSequence, FlagDependentTest, -// Combine(Bool(), Bool())); -// -template -internal::CartesianProductHolder2 Combine( - const Generator1& g1, const Generator2& g2) { - return internal::CartesianProductHolder2( - g1, g2); -} - -template -internal::CartesianProductHolder3 Combine( - const Generator1& g1, const Generator2& g2, const Generator3& g3) { - return internal::CartesianProductHolder3( - g1, g2, g3); -} - -template -internal::CartesianProductHolder4 Combine( - const Generator1& g1, const Generator2& g2, const Generator3& g3, - const Generator4& g4) { - return internal::CartesianProductHolder4( - g1, g2, g3, g4); -} - -template -internal::CartesianProductHolder5 Combine( - const Generator1& g1, const Generator2& g2, const Generator3& g3, - const Generator4& g4, const Generator5& g5) { - return internal::CartesianProductHolder5( - g1, g2, g3, g4, g5); -} - -template -internal::CartesianProductHolder6 Combine( - const Generator1& g1, const Generator2& g2, const Generator3& g3, - const Generator4& g4, const Generator5& g5, const Generator6& g6) { - return internal::CartesianProductHolder6( - g1, g2, g3, g4, g5, g6); -} - -template -internal::CartesianProductHolder7 Combine( - const Generator1& g1, const Generator2& g2, const Generator3& g3, - const Generator4& g4, const Generator5& g5, const Generator6& g6, - const Generator7& g7) { - return internal::CartesianProductHolder7( - g1, g2, g3, g4, g5, g6, g7); -} - -template -internal::CartesianProductHolder8 Combine( - const Generator1& g1, const Generator2& g2, const Generator3& g3, - const Generator4& g4, const Generator5& g5, const Generator6& g6, - const Generator7& g7, const Generator8& g8) { - return internal::CartesianProductHolder8( - g1, g2, g3, g4, g5, g6, g7, g8); -} - -template -internal::CartesianProductHolder9 Combine( - const Generator1& g1, const Generator2& g2, const Generator3& g3, - const Generator4& g4, const Generator5& g5, const Generator6& g6, - const Generator7& g7, const Generator8& g8, const Generator9& g9) { - return internal::CartesianProductHolder9( - g1, g2, g3, g4, g5, g6, g7, g8, g9); -} - -template -internal::CartesianProductHolder10 Combine( - const Generator1& g1, const Generator2& g2, const Generator3& g3, - const Generator4& g4, const Generator5& g5, const Generator6& g6, - const Generator7& g7, const Generator8& g8, const Generator9& g9, - const Generator10& g10) { - return internal::CartesianProductHolder10( - g1, g2, g3, g4, g5, g6, g7, g8, g9, g10); -} -# endif // GTEST_HAS_COMBINE - - - -# define TEST_P(test_case_name, test_name) \ - class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) \ - : public test_case_name { \ - public: \ - GTEST_TEST_CLASS_NAME_(test_case_name, test_name)() {} \ - virtual void TestBody(); \ - private: \ - static int AddToRegistry() { \ - ::testing::UnitTest::GetInstance()->parameterized_test_registry(). \ - GetTestCasePatternHolder(\ - #test_case_name, __FILE__, __LINE__)->AddTestPattern(\ - #test_case_name, \ - #test_name, \ - new ::testing::internal::TestMetaFactory< \ - GTEST_TEST_CLASS_NAME_(test_case_name, test_name)>()); \ - return 0; \ - } \ - static int gtest_registering_dummy_; \ - GTEST_DISALLOW_COPY_AND_ASSIGN_(\ - GTEST_TEST_CLASS_NAME_(test_case_name, test_name)); \ - }; \ - int GTEST_TEST_CLASS_NAME_(test_case_name, \ - test_name)::gtest_registering_dummy_ = \ - GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::AddToRegistry(); \ - void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody() - -# define INSTANTIATE_TEST_CASE_P(prefix, test_case_name, generator) \ - ::testing::internal::ParamGenerator \ - gtest_##prefix##test_case_name##_EvalGenerator_() { return generator; } \ - int gtest_##prefix##test_case_name##_dummy_ = \ - ::testing::UnitTest::GetInstance()->parameterized_test_registry(). \ - GetTestCasePatternHolder(\ - #test_case_name, __FILE__, __LINE__)->AddTestCaseInstantiation(\ - #prefix, \ - >est_##prefix##test_case_name##_EvalGenerator_, \ - __FILE__, __LINE__) - -} // namespace testing - -#endif // GTEST_HAS_PARAM_TEST - -#endif // GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_ -// Copyright 2006, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Author: wan@google.com (Zhanyong Wan) -// -// Google C++ Testing Framework definitions useful in production code. - -#ifndef GTEST_INCLUDE_GTEST_GTEST_PROD_H_ -#define GTEST_INCLUDE_GTEST_GTEST_PROD_H_ - -// When you need to test the private or protected members of a class, -// use the FRIEND_TEST macro to declare your tests as friends of the -// class. For example: -// -// class MyClass { -// private: -// void MyMethod(); -// FRIEND_TEST(MyClassTest, MyMethod); -// }; -// -// class MyClassTest : public testing::Test { -// // ... -// }; -// -// TEST_F(MyClassTest, MyMethod) { -// // Can call MyClass::MyMethod() here. -// } - -#define FRIEND_TEST(test_case_name, test_name)\ -friend class test_case_name##_##test_name##_Test - -#endif // GTEST_INCLUDE_GTEST_GTEST_PROD_H_ -// Copyright 2008, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Author: mheule@google.com (Markus Heule) -// - -#ifndef GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_ -#define GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_ - -#include -#include - -namespace testing { - -// A copyable object representing the result of a test part (i.e. an -// assertion or an explicit FAIL(), ADD_FAILURE(), or SUCCESS()). -// -// Don't inherit from TestPartResult as its destructor is not virtual. -class GTEST_API_ TestPartResult { - public: - // The possible outcomes of a test part (i.e. an assertion or an - // explicit SUCCEED(), FAIL(), or ADD_FAILURE()). - enum Type { - kSuccess, // Succeeded. - kNonFatalFailure, // Failed but the test can continue. - kFatalFailure // Failed and the test should be terminated. - }; - - // C'tor. TestPartResult does NOT have a default constructor. - // Always use this constructor (with parameters) to create a - // TestPartResult object. - TestPartResult(Type a_type, - const char* a_file_name, - int a_line_number, - const char* a_message) - : type_(a_type), - file_name_(a_file_name), - line_number_(a_line_number), - summary_(ExtractSummary(a_message)), - message_(a_message) { - } - - // Gets the outcome of the test part. - Type type() const { return type_; } - - // Gets the name of the source file where the test part took place, or - // NULL if it's unknown. - const char* file_name() const { return file_name_.c_str(); } - - // Gets the line in the source file where the test part took place, - // or -1 if it's unknown. - int line_number() const { return line_number_; } - - // Gets the summary of the failure message. - const char* summary() const { return summary_.c_str(); } - - // Gets the message associated with the test part. - const char* message() const { return message_.c_str(); } - - // Returns true iff the test part passed. - bool passed() const { return type_ == kSuccess; } - - // Returns true iff the test part failed. - bool failed() const { return type_ != kSuccess; } - - // Returns true iff the test part non-fatally failed. - bool nonfatally_failed() const { return type_ == kNonFatalFailure; } - - // Returns true iff the test part fatally failed. - bool fatally_failed() const { return type_ == kFatalFailure; } - private: - Type type_; - - // Gets the summary of the failure message by omitting the stack - // trace in it. - static internal::String ExtractSummary(const char* message); - - // The name of the source file where the test part took place, or - // NULL if the source file is unknown. - internal::String file_name_; - // The line in the source file where the test part took place, or -1 - // if the line number is unknown. - int line_number_; - internal::String summary_; // The test failure summary. - internal::String message_; // The test failure message. -}; - -// Prints a TestPartResult object. -std::ostream& operator<<(std::ostream& os, const TestPartResult& result); - -// An array of TestPartResult objects. -// -// Don't inherit from TestPartResultArray as its destructor is not -// virtual. -class GTEST_API_ TestPartResultArray { - public: - TestPartResultArray() {} - - // Appends the given TestPartResult to the array. - void Append(const TestPartResult& result); - - // Returns the TestPartResult at the given index (0-based). - const TestPartResult& GetTestPartResult(int index) const; - - // Returns the number of TestPartResult objects in the array. - int size() const; - - private: - std::vector array_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(TestPartResultArray); -}; - -// This interface knows how to report a test part result. -class TestPartResultReporterInterface { - public: - virtual ~TestPartResultReporterInterface() {} - - virtual void ReportTestPartResult(const TestPartResult& result) = 0; -}; - -namespace internal { - -// This helper class is used by {ASSERT|EXPECT}_NO_FATAL_FAILURE to check if a -// statement generates new fatal failures. To do so it registers itself as the -// current test part result reporter. Besides checking if fatal failures were -// reported, it only delegates the reporting to the former result reporter. -// The original result reporter is restored in the destructor. -// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. -class GTEST_API_ HasNewFatalFailureHelper - : public TestPartResultReporterInterface { - public: - HasNewFatalFailureHelper(); - virtual ~HasNewFatalFailureHelper(); - virtual void ReportTestPartResult(const TestPartResult& result); - bool has_new_fatal_failure() const { return has_new_fatal_failure_; } - private: - bool has_new_fatal_failure_; - TestPartResultReporterInterface* original_reporter_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(HasNewFatalFailureHelper); -}; - -} // namespace internal - -} // namespace testing - -#endif // GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_ -// Copyright 2008 Google Inc. -// All Rights Reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Author: wan@google.com (Zhanyong Wan) - -#ifndef GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_ -#define GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_ - -// This header implements typed tests and type-parameterized tests. - -// Typed (aka type-driven) tests repeat the same test for types in a -// list. You must know which types you want to test with when writing -// typed tests. Here's how you do it: - -#if 0 - -// First, define a fixture class template. It should be parameterized -// by a type. Remember to derive it from testing::Test. -template -class FooTest : public testing::Test { - public: - ... - typedef std::list List; - static T shared_; - T value_; -}; - -// Next, associate a list of types with the test case, which will be -// repeated for each type in the list. The typedef is necessary for -// the macro to parse correctly. -typedef testing::Types MyTypes; -TYPED_TEST_CASE(FooTest, MyTypes); - -// If the type list contains only one type, you can write that type -// directly without Types<...>: -// TYPED_TEST_CASE(FooTest, int); - -// Then, use TYPED_TEST() instead of TEST_F() to define as many typed -// tests for this test case as you want. -TYPED_TEST(FooTest, DoesBlah) { - // Inside a test, refer to TypeParam to get the type parameter. - // Since we are inside a derived class template, C++ requires use to - // visit the members of FooTest via 'this'. - TypeParam n = this->value_; - - // To visit static members of the fixture, add the TestFixture:: - // prefix. - n += TestFixture::shared_; - - // To refer to typedefs in the fixture, add the "typename - // TestFixture::" prefix. - typename TestFixture::List values; - values.push_back(n); - ... -} - -TYPED_TEST(FooTest, HasPropertyA) { ... } - -#endif // 0 - -// Type-parameterized tests are abstract test patterns parameterized -// by a type. Compared with typed tests, type-parameterized tests -// allow you to define the test pattern without knowing what the type -// parameters are. The defined pattern can be instantiated with -// different types any number of times, in any number of translation -// units. -// -// If you are designing an interface or concept, you can define a -// suite of type-parameterized tests to verify properties that any -// valid implementation of the interface/concept should have. Then, -// each implementation can easily instantiate the test suite to verify -// that it conforms to the requirements, without having to write -// similar tests repeatedly. Here's an example: - -#if 0 - -// First, define a fixture class template. It should be parameterized -// by a type. Remember to derive it from testing::Test. -template -class FooTest : public testing::Test { - ... -}; - -// Next, declare that you will define a type-parameterized test case -// (the _P suffix is for "parameterized" or "pattern", whichever you -// prefer): -TYPED_TEST_CASE_P(FooTest); - -// Then, use TYPED_TEST_P() to define as many type-parameterized tests -// for this type-parameterized test case as you want. -TYPED_TEST_P(FooTest, DoesBlah) { - // Inside a test, refer to TypeParam to get the type parameter. - TypeParam n = 0; - ... -} - -TYPED_TEST_P(FooTest, HasPropertyA) { ... } - -// Now the tricky part: you need to register all test patterns before -// you can instantiate them. The first argument of the macro is the -// test case name; the rest are the names of the tests in this test -// case. -REGISTER_TYPED_TEST_CASE_P(FooTest, - DoesBlah, HasPropertyA); - -// Finally, you are free to instantiate the pattern with the types you -// want. If you put the above code in a header file, you can #include -// it in multiple C++ source files and instantiate it multiple times. -// -// To distinguish different instances of the pattern, the first -// argument to the INSTANTIATE_* macro is a prefix that will be added -// to the actual test case name. Remember to pick unique prefixes for -// different instances. -typedef testing::Types MyTypes; -INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, MyTypes); - -// If the type list contains only one type, you can write that type -// directly without Types<...>: -// INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, int); - -#endif // 0 - - -// Implements typed tests. - -#if GTEST_HAS_TYPED_TEST - -// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. -// -// Expands to the name of the typedef for the type parameters of the -// given test case. -# define GTEST_TYPE_PARAMS_(TestCaseName) gtest_type_params_##TestCaseName##_ - -// The 'Types' template argument below must have spaces around it -// since some compilers may choke on '>>' when passing a template -// instance (e.g. Types) -# define TYPED_TEST_CASE(CaseName, Types) \ - typedef ::testing::internal::TypeList< Types >::type \ - GTEST_TYPE_PARAMS_(CaseName) - -# define TYPED_TEST(CaseName, TestName) \ - template \ - class GTEST_TEST_CLASS_NAME_(CaseName, TestName) \ - : public CaseName { \ - private: \ - typedef CaseName TestFixture; \ - typedef gtest_TypeParam_ TypeParam; \ - virtual void TestBody(); \ - }; \ - bool gtest_##CaseName##_##TestName##_registered_ GTEST_ATTRIBUTE_UNUSED_ = \ - ::testing::internal::TypeParameterizedTest< \ - CaseName, \ - ::testing::internal::TemplateSel< \ - GTEST_TEST_CLASS_NAME_(CaseName, TestName)>, \ - GTEST_TYPE_PARAMS_(CaseName)>::Register(\ - "", #CaseName, #TestName, 0); \ - template \ - void GTEST_TEST_CLASS_NAME_(CaseName, TestName)::TestBody() - -#endif // GTEST_HAS_TYPED_TEST - -// Implements type-parameterized tests. - -#if GTEST_HAS_TYPED_TEST_P - -// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. -// -// Expands to the namespace name that the type-parameterized tests for -// the given type-parameterized test case are defined in. The exact -// name of the namespace is subject to change without notice. -# define GTEST_CASE_NAMESPACE_(TestCaseName) \ - gtest_case_##TestCaseName##_ - -// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. -// -// Expands to the name of the variable used to remember the names of -// the defined tests in the given test case. -# define GTEST_TYPED_TEST_CASE_P_STATE_(TestCaseName) \ - gtest_typed_test_case_p_state_##TestCaseName##_ - -// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE DIRECTLY. -// -// Expands to the name of the variable used to remember the names of -// the registered tests in the given test case. -# define GTEST_REGISTERED_TEST_NAMES_(TestCaseName) \ - gtest_registered_test_names_##TestCaseName##_ - -// The variables defined in the type-parameterized test macros are -// static as typically these macros are used in a .h file that can be -// #included in multiple translation units linked together. -# define TYPED_TEST_CASE_P(CaseName) \ - static ::testing::internal::TypedTestCasePState \ - GTEST_TYPED_TEST_CASE_P_STATE_(CaseName) - -# define TYPED_TEST_P(CaseName, TestName) \ - namespace GTEST_CASE_NAMESPACE_(CaseName) { \ - template \ - class TestName : public CaseName { \ - private: \ - typedef CaseName TestFixture; \ - typedef gtest_TypeParam_ TypeParam; \ - virtual void TestBody(); \ - }; \ - static bool gtest_##TestName##_defined_ GTEST_ATTRIBUTE_UNUSED_ = \ - GTEST_TYPED_TEST_CASE_P_STATE_(CaseName).AddTestName(\ - __FILE__, __LINE__, #CaseName, #TestName); \ - } \ - template \ - void GTEST_CASE_NAMESPACE_(CaseName)::TestName::TestBody() - -# define REGISTER_TYPED_TEST_CASE_P(CaseName, ...) \ - namespace GTEST_CASE_NAMESPACE_(CaseName) { \ - typedef ::testing::internal::Templates<__VA_ARGS__>::type gtest_AllTests_; \ - } \ - static const char* const GTEST_REGISTERED_TEST_NAMES_(CaseName) = \ - GTEST_TYPED_TEST_CASE_P_STATE_(CaseName).VerifyRegisteredTestNames(\ - __FILE__, __LINE__, #__VA_ARGS__) - -// The 'Types' template argument below must have spaces around it -// since some compilers may choke on '>>' when passing a template -// instance (e.g. Types) -# define INSTANTIATE_TYPED_TEST_CASE_P(Prefix, CaseName, Types) \ - bool gtest_##Prefix##_##CaseName GTEST_ATTRIBUTE_UNUSED_ = \ - ::testing::internal::TypeParameterizedTestCase::type>::Register(\ - #Prefix, #CaseName, GTEST_REGISTERED_TEST_NAMES_(CaseName)) - -#endif // GTEST_HAS_TYPED_TEST_P - -#endif // GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_ - -// Depending on the platform, different string classes are available. -// On Linux, in addition to ::std::string, Google also makes use of -// class ::string, which has the same interface as ::std::string, but -// has a different implementation. -// -// The user can define GTEST_HAS_GLOBAL_STRING to 1 to indicate that -// ::string is available AND is a distinct type to ::std::string, or -// define it to 0 to indicate otherwise. -// -// If the user's ::std::string and ::string are the same class due to -// aliasing, he should define GTEST_HAS_GLOBAL_STRING to 0. -// -// If the user doesn't define GTEST_HAS_GLOBAL_STRING, it is defined -// heuristically. - -namespace testing { - -// Declares the flags. - -// This flag temporary enables the disabled tests. -GTEST_DECLARE_bool_(also_run_disabled_tests); - -// This flag brings the debugger on an assertion failure. -GTEST_DECLARE_bool_(break_on_failure); - -// This flag controls whether Google Test catches all test-thrown exceptions -// and logs them as failures. -GTEST_DECLARE_bool_(catch_exceptions); - -// This flag enables using colors in terminal output. Available values are -// "yes" to enable colors, "no" (disable colors), or "auto" (the default) -// to let Google Test decide. -GTEST_DECLARE_string_(color); - -// This flag sets up the filter to select by name using a glob pattern -// the tests to run. If the filter is not given all tests are executed. -GTEST_DECLARE_string_(filter); - -// This flag causes the Google Test to list tests. None of the tests listed -// are actually run if the flag is provided. -GTEST_DECLARE_bool_(list_tests); - -// This flag controls whether Google Test emits a detailed XML report to a file -// in addition to its normal textual output. -GTEST_DECLARE_string_(output); - -// This flags control whether Google Test prints the elapsed time for each -// test. -GTEST_DECLARE_bool_(print_time); - -// This flag specifies the random number seed. -GTEST_DECLARE_int32_(random_seed); - -// This flag sets how many times the tests are repeated. The default value -// is 1. If the value is -1 the tests are repeating forever. -GTEST_DECLARE_int32_(repeat); - -// This flag controls whether Google Test includes Google Test internal -// stack frames in failure stack traces. -GTEST_DECLARE_bool_(show_internal_stack_frames); - -// When this flag is specified, tests' order is randomized on every iteration. -GTEST_DECLARE_bool_(shuffle); - -// This flag specifies the maximum number of stack frames to be -// printed in a failure message. -GTEST_DECLARE_int32_(stack_trace_depth); - -// When this flag is specified, a failed assertion will throw an -// exception if exceptions are enabled, or exit the program with a -// non-zero code otherwise. -GTEST_DECLARE_bool_(throw_on_failure); - -// When this flag is set with a "host:port" string, on supported -// platforms test results are streamed to the specified port on -// the specified host machine. -GTEST_DECLARE_string_(stream_result_to); - -// The upper limit for valid stack trace depths. -const int kMaxStackTraceDepth = 100; - -namespace internal { - -class AssertHelper; -class DefaultGlobalTestPartResultReporter; -class ExecDeathTest; -class NoExecDeathTest; -class FinalSuccessChecker; -class GTestFlagSaver; -class TestResultAccessor; -class TestEventListenersAccessor; -class TestEventRepeater; -class WindowsDeathTest; -class UnitTestImpl* GetUnitTestImpl(); -void ReportFailureInUnknownLocation(TestPartResult::Type result_type, - const String& message); - -// Converts a streamable value to a String. A NULL pointer is -// converted to "(null)". When the input value is a ::string, -// ::std::string, ::wstring, or ::std::wstring object, each NUL -// character in it is replaced with "\\0". -// Declared in gtest-internal.h but defined here, so that it has access -// to the definition of the Message class, required by the ARM -// compiler. -template -String StreamableToString(const T& streamable) { - return (Message() << streamable).GetString(); -} - -} // namespace internal - -// The friend relationship of some of these classes is cyclic. -// If we don't forward declare them the compiler might confuse the classes -// in friendship clauses with same named classes on the scope. -class Test; -class TestCase; -class TestInfo; -class UnitTest; - -// A class for indicating whether an assertion was successful. When -// the assertion wasn't successful, the AssertionResult object -// remembers a non-empty message that describes how it failed. -// -// To create an instance of this class, use one of the factory functions -// (AssertionSuccess() and AssertionFailure()). -// -// This class is useful for two purposes: -// 1. Defining predicate functions to be used with Boolean test assertions -// EXPECT_TRUE/EXPECT_FALSE and their ASSERT_ counterparts -// 2. Defining predicate-format functions to be -// used with predicate assertions (ASSERT_PRED_FORMAT*, etc). -// -// For example, if you define IsEven predicate: -// -// testing::AssertionResult IsEven(int n) { -// if ((n % 2) == 0) -// return testing::AssertionSuccess(); -// else -// return testing::AssertionFailure() << n << " is odd"; -// } -// -// Then the failed expectation EXPECT_TRUE(IsEven(Fib(5))) -// will print the message -// -// Value of: IsEven(Fib(5)) -// Actual: false (5 is odd) -// Expected: true -// -// instead of a more opaque -// -// Value of: IsEven(Fib(5)) -// Actual: false -// Expected: true -// -// in case IsEven is a simple Boolean predicate. -// -// If you expect your predicate to be reused and want to support informative -// messages in EXPECT_FALSE and ASSERT_FALSE (negative assertions show up -// about half as often as positive ones in our tests), supply messages for -// both success and failure cases: -// -// testing::AssertionResult IsEven(int n) { -// if ((n % 2) == 0) -// return testing::AssertionSuccess() << n << " is even"; -// else -// return testing::AssertionFailure() << n << " is odd"; -// } -// -// Then a statement EXPECT_FALSE(IsEven(Fib(6))) will print -// -// Value of: IsEven(Fib(6)) -// Actual: true (8 is even) -// Expected: false -// -// NB: Predicates that support negative Boolean assertions have reduced -// performance in positive ones so be careful not to use them in tests -// that have lots (tens of thousands) of positive Boolean assertions. -// -// To use this class with EXPECT_PRED_FORMAT assertions such as: -// -// // Verifies that Foo() returns an even number. -// EXPECT_PRED_FORMAT1(IsEven, Foo()); -// -// you need to define: -// -// testing::AssertionResult IsEven(const char* expr, int n) { -// if ((n % 2) == 0) -// return testing::AssertionSuccess(); -// else -// return testing::AssertionFailure() -// << "Expected: " << expr << " is even\n Actual: it's " << n; -// } -// -// If Foo() returns 5, you will see the following message: -// -// Expected: Foo() is even -// Actual: it's 5 -// -class GTEST_API_ AssertionResult { - public: - // Copy constructor. - // Used in EXPECT_TRUE/FALSE(assertion_result). - AssertionResult(const AssertionResult& other); - // Used in the EXPECT_TRUE/FALSE(bool_expression). - explicit AssertionResult(bool success) : success_(success) {} - - // Returns true iff the assertion succeeded. - operator bool() const { return success_; } // NOLINT - - // Returns the assertion's negation. Used with EXPECT/ASSERT_FALSE. - AssertionResult operator!() const; - - // Returns the text streamed into this AssertionResult. Test assertions - // use it when they fail (i.e., the predicate's outcome doesn't match the - // assertion's expectation). When nothing has been streamed into the - // object, returns an empty string. - const char* message() const { - return message_.get() != NULL ? message_->c_str() : ""; - } - // TODO(vladl@google.com): Remove this after making sure no clients use it. - // Deprecated; please use message() instead. - const char* failure_message() const { return message(); } - - // Streams a custom failure message into this object. - template AssertionResult& operator<<(const T& value) { - AppendMessage(Message() << value); - return *this; - } - - // Allows streaming basic output manipulators such as endl or flush into - // this object. - AssertionResult& operator<<( - ::std::ostream& (*basic_manipulator)(::std::ostream& stream)) { - AppendMessage(Message() << basic_manipulator); - return *this; - } - - private: - // Appends the contents of message to message_. - void AppendMessage(const Message& a_message) { - if (message_.get() == NULL) - message_.reset(new ::std::string); - message_->append(a_message.GetString().c_str()); - } - - // Stores result of the assertion predicate. - bool success_; - // Stores the message describing the condition in case the expectation - // construct is not satisfied with the predicate's outcome. - // Referenced via a pointer to avoid taking too much stack frame space - // with test assertions. - internal::scoped_ptr< ::std::string> message_; - - GTEST_DISALLOW_ASSIGN_(AssertionResult); -}; - -// Makes a successful assertion result. -GTEST_API_ AssertionResult AssertionSuccess(); - -// Makes a failed assertion result. -GTEST_API_ AssertionResult AssertionFailure(); - -// Makes a failed assertion result with the given failure message. -// Deprecated; use AssertionFailure() << msg. -GTEST_API_ AssertionResult AssertionFailure(const Message& msg); - -// The abstract class that all tests inherit from. -// -// In Google Test, a unit test program contains one or many TestCases, and -// each TestCase contains one or many Tests. -// -// When you define a test using the TEST macro, you don't need to -// explicitly derive from Test - the TEST macro automatically does -// this for you. -// -// The only time you derive from Test is when defining a test fixture -// to be used a TEST_F. For example: -// -// class FooTest : public testing::Test { -// protected: -// virtual void SetUp() { ... } -// virtual void TearDown() { ... } -// ... -// }; -// -// TEST_F(FooTest, Bar) { ... } -// TEST_F(FooTest, Baz) { ... } -// -// Test is not copyable. -class GTEST_API_ Test { - public: - friend class TestInfo; - - // Defines types for pointers to functions that set up and tear down - // a test case. - typedef internal::SetUpTestCaseFunc SetUpTestCaseFunc; - typedef internal::TearDownTestCaseFunc TearDownTestCaseFunc; - - // The d'tor is virtual as we intend to inherit from Test. - virtual ~Test(); - - // Sets up the stuff shared by all tests in this test case. - // - // Google Test will call Foo::SetUpTestCase() before running the first - // test in test case Foo. Hence a sub-class can define its own - // SetUpTestCase() method to shadow the one defined in the super - // class. - static void SetUpTestCase() {} - - // Tears down the stuff shared by all tests in this test case. - // - // Google Test will call Foo::TearDownTestCase() after running the last - // test in test case Foo. Hence a sub-class can define its own - // TearDownTestCase() method to shadow the one defined in the super - // class. - static void TearDownTestCase() {} - - // Returns true iff the current test has a fatal failure. - static bool HasFatalFailure(); - - // Returns true iff the current test has a non-fatal failure. - static bool HasNonfatalFailure(); - - // Returns true iff the current test has a (either fatal or - // non-fatal) failure. - static bool HasFailure() { return HasFatalFailure() || HasNonfatalFailure(); } - - // Logs a property for the current test. Only the last value for a given - // key is remembered. - // These are public static so they can be called from utility functions - // that are not members of the test fixture. - // The arguments are const char* instead strings, as Google Test is used - // on platforms where string doesn't compile. - // - // Note that a driving consideration for these RecordProperty methods - // was to produce xml output suited to the Greenspan charting utility, - // which at present will only chart values that fit in a 32-bit int. It - // is the user's responsibility to restrict their values to 32-bit ints - // if they intend them to be used with Greenspan. - static void RecordProperty(const char* key, const char* value); - static void RecordProperty(const char* key, int value); - - protected: - // Creates a Test object. - Test(); - - // Sets up the test fixture. - virtual void SetUp(); - - // Tears down the test fixture. - virtual void TearDown(); - - private: - // Returns true iff the current test has the same fixture class as - // the first test in the current test case. - static bool HasSameFixtureClass(); - - // Runs the test after the test fixture has been set up. - // - // A sub-class must implement this to define the test logic. - // - // DO NOT OVERRIDE THIS FUNCTION DIRECTLY IN A USER PROGRAM. - // Instead, use the TEST or TEST_F macro. - virtual void TestBody() = 0; - - // Sets up, executes, and tears down the test. - void Run(); - - // Deletes self. We deliberately pick an unusual name for this - // internal method to avoid clashing with names used in user TESTs. - void DeleteSelf_() { delete this; } - - // Uses a GTestFlagSaver to save and restore all Google Test flags. - const internal::GTestFlagSaver* const gtest_flag_saver_; - - // Often a user mis-spells SetUp() as Setup() and spends a long time - // wondering why it is never called by Google Test. The declaration of - // the following method is solely for catching such an error at - // compile time: - // - // - The return type is deliberately chosen to be not void, so it - // will be a conflict if a user declares void Setup() in his test - // fixture. - // - // - This method is private, so it will be another compiler error - // if a user calls it from his test fixture. - // - // DO NOT OVERRIDE THIS FUNCTION. - // - // If you see an error about overriding the following function or - // about it being private, you have mis-spelled SetUp() as Setup(). - struct Setup_should_be_spelled_SetUp {}; - virtual Setup_should_be_spelled_SetUp* Setup() { return NULL; } - - // We disallow copying Tests. - GTEST_DISALLOW_COPY_AND_ASSIGN_(Test); -}; - -typedef internal::TimeInMillis TimeInMillis; - -// A copyable object representing a user specified test property which can be -// output as a key/value string pair. -// -// Don't inherit from TestProperty as its destructor is not virtual. -class TestProperty { - public: - // C'tor. TestProperty does NOT have a default constructor. - // Always use this constructor (with parameters) to create a - // TestProperty object. - TestProperty(const char* a_key, const char* a_value) : - key_(a_key), value_(a_value) { - } - - // Gets the user supplied key. - const char* key() const { - return key_.c_str(); - } - - // Gets the user supplied value. - const char* value() const { - return value_.c_str(); - } - - // Sets a new value, overriding the one supplied in the constructor. - void SetValue(const char* new_value) { - value_ = new_value; - } - - private: - // The key supplied by the user. - internal::String key_; - // The value supplied by the user. - internal::String value_; -}; - -// The result of a single Test. This includes a list of -// TestPartResults, a list of TestProperties, a count of how many -// death tests there are in the Test, and how much time it took to run -// the Test. -// -// TestResult is not copyable. -class GTEST_API_ TestResult { - public: - // Creates an empty TestResult. - TestResult(); - - // D'tor. Do not inherit from TestResult. - ~TestResult(); - - // Gets the number of all test parts. This is the sum of the number - // of successful test parts and the number of failed test parts. - int total_part_count() const; - - // Returns the number of the test properties. - int test_property_count() const; - - // Returns true iff the test passed (i.e. no test part failed). - bool Passed() const { return !Failed(); } - - // Returns true iff the test failed. - bool Failed() const; - - // Returns true iff the test fatally failed. - bool HasFatalFailure() const; - - // Returns true iff the test has a non-fatal failure. - bool HasNonfatalFailure() const; - - // Returns the elapsed time, in milliseconds. - TimeInMillis elapsed_time() const { return elapsed_time_; } - - // Returns the i-th test part result among all the results. i can range - // from 0 to test_property_count() - 1. If i is not in that range, aborts - // the program. - const TestPartResult& GetTestPartResult(int i) const; - - // Returns the i-th test property. i can range from 0 to - // test_property_count() - 1. If i is not in that range, aborts the - // program. - const TestProperty& GetTestProperty(int i) const; - - private: - friend class TestInfo; - friend class UnitTest; - friend class internal::DefaultGlobalTestPartResultReporter; - friend class internal::ExecDeathTest; - friend class internal::TestResultAccessor; - friend class internal::UnitTestImpl; - friend class internal::WindowsDeathTest; - - // Gets the vector of TestPartResults. - const std::vector& test_part_results() const { - return test_part_results_; - } - - // Gets the vector of TestProperties. - const std::vector& test_properties() const { - return test_properties_; - } - - // Sets the elapsed time. - void set_elapsed_time(TimeInMillis elapsed) { elapsed_time_ = elapsed; } - - // Adds a test property to the list. The property is validated and may add - // a non-fatal failure if invalid (e.g., if it conflicts with reserved - // key names). If a property is already recorded for the same key, the - // value will be updated, rather than storing multiple values for the same - // key. - void RecordProperty(const TestProperty& test_property); - - // Adds a failure if the key is a reserved attribute of Google Test - // testcase tags. Returns true if the property is valid. - // TODO(russr): Validate attribute names are legal and human readable. - static bool ValidateTestProperty(const TestProperty& test_property); - - // Adds a test part result to the list. - void AddTestPartResult(const TestPartResult& test_part_result); - - // Returns the death test count. - int death_test_count() const { return death_test_count_; } - - // Increments the death test count, returning the new count. - int increment_death_test_count() { return ++death_test_count_; } - - // Clears the test part results. - void ClearTestPartResults(); - - // Clears the object. - void Clear(); - - // Protects mutable state of the property vector and of owned - // properties, whose values may be updated. - internal::Mutex test_properites_mutex_; - - // The vector of TestPartResults - std::vector test_part_results_; - // The vector of TestProperties - std::vector test_properties_; - // Running count of death tests. - int death_test_count_; - // The elapsed time, in milliseconds. - TimeInMillis elapsed_time_; - - // We disallow copying TestResult. - GTEST_DISALLOW_COPY_AND_ASSIGN_(TestResult); -}; // class TestResult - -// A TestInfo object stores the following information about a test: -// -// Test case name -// Test name -// Whether the test should be run -// A function pointer that creates the test object when invoked -// Test result -// -// The constructor of TestInfo registers itself with the UnitTest -// singleton such that the RUN_ALL_TESTS() macro knows which tests to -// run. -class GTEST_API_ TestInfo { - public: - // Destructs a TestInfo object. This function is not virtual, so - // don't inherit from TestInfo. - ~TestInfo(); - - // Returns the test case name. - const char* test_case_name() const { return test_case_name_.c_str(); } - - // Returns the test name. - const char* name() const { return name_.c_str(); } - - // Returns the name of the parameter type, or NULL if this is not a typed - // or a type-parameterized test. - const char* type_param() const { - if (type_param_.get() != NULL) - return type_param_->c_str(); - return NULL; - } - - // Returns the text representation of the value parameter, or NULL if this - // is not a value-parameterized test. - const char* value_param() const { - if (value_param_.get() != NULL) - return value_param_->c_str(); - return NULL; - } - - // Returns true if this test should run, that is if the test is not disabled - // (or it is disabled but the also_run_disabled_tests flag has been specified) - // and its full name matches the user-specified filter. - // - // Google Test allows the user to filter the tests by their full names. - // The full name of a test Bar in test case Foo is defined as - // "Foo.Bar". Only the tests that match the filter will run. - // - // A filter is a colon-separated list of glob (not regex) patterns, - // optionally followed by a '-' and a colon-separated list of - // negative patterns (tests to exclude). A test is run if it - // matches one of the positive patterns and does not match any of - // the negative patterns. - // - // For example, *A*:Foo.* is a filter that matches any string that - // contains the character 'A' or starts with "Foo.". - bool should_run() const { return should_run_; } - - // Returns the result of the test. - const TestResult* result() const { return &result_; } - - private: - -#if GTEST_HAS_DEATH_TEST - friend class internal::DefaultDeathTestFactory; -#endif // GTEST_HAS_DEATH_TEST - friend class Test; - friend class TestCase; - friend class internal::UnitTestImpl; - friend TestInfo* internal::MakeAndRegisterTestInfo( - const char* test_case_name, const char* name, - const char* type_param, - const char* value_param, - internal::TypeId fixture_class_id, - Test::SetUpTestCaseFunc set_up_tc, - Test::TearDownTestCaseFunc tear_down_tc, - internal::TestFactoryBase* factory); - - // Constructs a TestInfo object. The newly constructed instance assumes - // ownership of the factory object. - TestInfo(const char* test_case_name, const char* name, - const char* a_type_param, - const char* a_value_param, - internal::TypeId fixture_class_id, - internal::TestFactoryBase* factory); - - // Increments the number of death tests encountered in this test so - // far. - int increment_death_test_count() { - return result_.increment_death_test_count(); - } - - // Creates the test object, runs it, records its result, and then - // deletes it. - void Run(); - - static void ClearTestResult(TestInfo* test_info) { - test_info->result_.Clear(); - } - - // These fields are immutable properties of the test. - const std::string test_case_name_; // Test case name - const std::string name_; // Test name - // Name of the parameter type, or NULL if this is not a typed or a - // type-parameterized test. - const internal::scoped_ptr type_param_; - // Text representation of the value parameter, or NULL if this is not a - // value-parameterized test. - const internal::scoped_ptr value_param_; - const internal::TypeId fixture_class_id_; // ID of the test fixture class - bool should_run_; // True iff this test should run - bool is_disabled_; // True iff this test is disabled - bool matches_filter_; // True if this test matches the - // user-specified filter. - internal::TestFactoryBase* const factory_; // The factory that creates - // the test object - - // This field is mutable and needs to be reset before running the - // test for the second time. - TestResult result_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(TestInfo); -}; - -// A test case, which consists of a vector of TestInfos. -// -// TestCase is not copyable. -class GTEST_API_ TestCase { - public: - // Creates a TestCase with the given name. - // - // TestCase does NOT have a default constructor. Always use this - // constructor to create a TestCase object. - // - // Arguments: - // - // name: name of the test case - // a_type_param: the name of the test's type parameter, or NULL if - // this is not a type-parameterized test. - // set_up_tc: pointer to the function that sets up the test case - // tear_down_tc: pointer to the function that tears down the test case - TestCase(const char* name, const char* a_type_param, - Test::SetUpTestCaseFunc set_up_tc, - Test::TearDownTestCaseFunc tear_down_tc); - - // Destructor of TestCase. - virtual ~TestCase(); - - // Gets the name of the TestCase. - const char* name() const { return name_.c_str(); } - - // Returns the name of the parameter type, or NULL if this is not a - // type-parameterized test case. - const char* type_param() const { - if (type_param_.get() != NULL) - return type_param_->c_str(); - return NULL; - } - - // Returns true if any test in this test case should run. - bool should_run() const { return should_run_; } - - // Gets the number of successful tests in this test case. - int successful_test_count() const; - - // Gets the number of failed tests in this test case. - int failed_test_count() const; - - // Gets the number of disabled tests in this test case. - int disabled_test_count() const; - - // Get the number of tests in this test case that should run. - int test_to_run_count() const; - - // Gets the number of all tests in this test case. - int total_test_count() const; - - // Returns true iff the test case passed. - bool Passed() const { return !Failed(); } - - // Returns true iff the test case failed. - bool Failed() const { return failed_test_count() > 0; } - - // Returns the elapsed time, in milliseconds. - TimeInMillis elapsed_time() const { return elapsed_time_; } - - // Returns the i-th test among all the tests. i can range from 0 to - // total_test_count() - 1. If i is not in that range, returns NULL. - const TestInfo* GetTestInfo(int i) const; - - private: - friend class Test; - friend class internal::UnitTestImpl; - - // Gets the (mutable) vector of TestInfos in this TestCase. - std::vector& test_info_list() { return test_info_list_; } - - // Gets the (immutable) vector of TestInfos in this TestCase. - const std::vector& test_info_list() const { - return test_info_list_; - } - - // Returns the i-th test among all the tests. i can range from 0 to - // total_test_count() - 1. If i is not in that range, returns NULL. - TestInfo* GetMutableTestInfo(int i); - - // Sets the should_run member. - void set_should_run(bool should) { should_run_ = should; } - - // Adds a TestInfo to this test case. Will delete the TestInfo upon - // destruction of the TestCase object. - void AddTestInfo(TestInfo * test_info); - - // Clears the results of all tests in this test case. - void ClearResult(); - - // Clears the results of all tests in the given test case. - static void ClearTestCaseResult(TestCase* test_case) { - test_case->ClearResult(); - } - - // Runs every test in this TestCase. - void Run(); - - // Runs SetUpTestCase() for this TestCase. This wrapper is needed - // for catching exceptions thrown from SetUpTestCase(). - void RunSetUpTestCase() { (*set_up_tc_)(); } - - // Runs TearDownTestCase() for this TestCase. This wrapper is - // needed for catching exceptions thrown from TearDownTestCase(). - void RunTearDownTestCase() { (*tear_down_tc_)(); } - - // Returns true iff test passed. - static bool TestPassed(const TestInfo* test_info) { - return test_info->should_run() && test_info->result()->Passed(); - } - - // Returns true iff test failed. - static bool TestFailed(const TestInfo* test_info) { - return test_info->should_run() && test_info->result()->Failed(); - } - - // Returns true iff test is disabled. - static bool TestDisabled(const TestInfo* test_info) { - return test_info->is_disabled_; - } - - // Returns true if the given test should run. - static bool ShouldRunTest(const TestInfo* test_info) { - return test_info->should_run(); - } - - // Shuffles the tests in this test case. - void ShuffleTests(internal::Random* random); - - // Restores the test order to before the first shuffle. - void UnshuffleTests(); - - // Name of the test case. - internal::String name_; - // Name of the parameter type, or NULL if this is not a typed or a - // type-parameterized test. - const internal::scoped_ptr type_param_; - // The vector of TestInfos in their original order. It owns the - // elements in the vector. - std::vector test_info_list_; - // Provides a level of indirection for the test list to allow easy - // shuffling and restoring the test order. The i-th element in this - // vector is the index of the i-th test in the shuffled test list. - std::vector test_indices_; - // Pointer to the function that sets up the test case. - Test::SetUpTestCaseFunc set_up_tc_; - // Pointer to the function that tears down the test case. - Test::TearDownTestCaseFunc tear_down_tc_; - // True iff any test in this test case should run. - bool should_run_; - // Elapsed time, in milliseconds. - TimeInMillis elapsed_time_; - - // We disallow copying TestCases. - GTEST_DISALLOW_COPY_AND_ASSIGN_(TestCase); -}; - -// An Environment object is capable of setting up and tearing down an -// environment. The user should subclass this to define his own -// environment(s). -// -// An Environment object does the set-up and tear-down in virtual -// methods SetUp() and TearDown() instead of the constructor and the -// destructor, as: -// -// 1. You cannot safely throw from a destructor. This is a problem -// as in some cases Google Test is used where exceptions are enabled, and -// we may want to implement ASSERT_* using exceptions where they are -// available. -// 2. You cannot use ASSERT_* directly in a constructor or -// destructor. -class Environment { - public: - // The d'tor is virtual as we need to subclass Environment. - virtual ~Environment() {} - - // Override this to define how to set up the environment. - virtual void SetUp() {} - - // Override this to define how to tear down the environment. - virtual void TearDown() {} - private: - // If you see an error about overriding the following function or - // about it being private, you have mis-spelled SetUp() as Setup(). - struct Setup_should_be_spelled_SetUp {}; - virtual Setup_should_be_spelled_SetUp* Setup() { return NULL; } -}; - -// The interface for tracing execution of tests. The methods are organized in -// the order the corresponding events are fired. -class TestEventListener { - public: - virtual ~TestEventListener() {} - - // Fired before any test activity starts. - virtual void OnTestProgramStart(const UnitTest& unit_test) = 0; - - // Fired before each iteration of tests starts. There may be more than - // one iteration if GTEST_FLAG(repeat) is set. iteration is the iteration - // index, starting from 0. - virtual void OnTestIterationStart(const UnitTest& unit_test, - int iteration) = 0; - - // Fired before environment set-up for each iteration of tests starts. - virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test) = 0; - - // Fired after environment set-up for each iteration of tests ends. - virtual void OnEnvironmentsSetUpEnd(const UnitTest& unit_test) = 0; - - // Fired before the test case starts. - virtual void OnTestCaseStart(const TestCase& test_case) = 0; - - // Fired before the test starts. - virtual void OnTestStart(const TestInfo& test_info) = 0; - - // Fired after a failed assertion or a SUCCEED() invocation. - virtual void OnTestPartResult(const TestPartResult& test_part_result) = 0; - - // Fired after the test ends. - virtual void OnTestEnd(const TestInfo& test_info) = 0; - - // Fired after the test case ends. - virtual void OnTestCaseEnd(const TestCase& test_case) = 0; - - // Fired before environment tear-down for each iteration of tests starts. - virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test) = 0; - - // Fired after environment tear-down for each iteration of tests ends. - virtual void OnEnvironmentsTearDownEnd(const UnitTest& unit_test) = 0; - - // Fired after each iteration of tests finishes. - virtual void OnTestIterationEnd(const UnitTest& unit_test, - int iteration) = 0; - - // Fired after all test activities have ended. - virtual void OnTestProgramEnd(const UnitTest& unit_test) = 0; -}; - -// The convenience class for users who need to override just one or two -// methods and are not concerned that a possible change to a signature of -// the methods they override will not be caught during the build. For -// comments about each method please see the definition of TestEventListener -// above. -class EmptyTestEventListener : public TestEventListener { - public: - virtual void OnTestProgramStart(const UnitTest& /*unit_test*/) {} - virtual void OnTestIterationStart(const UnitTest& /*unit_test*/, - int /*iteration*/) {} - virtual void OnEnvironmentsSetUpStart(const UnitTest& /*unit_test*/) {} - virtual void OnEnvironmentsSetUpEnd(const UnitTest& /*unit_test*/) {} - virtual void OnTestCaseStart(const TestCase& /*test_case*/) {} - virtual void OnTestStart(const TestInfo& /*test_info*/) {} - virtual void OnTestPartResult(const TestPartResult& /*test_part_result*/) {} - virtual void OnTestEnd(const TestInfo& /*test_info*/) {} - virtual void OnTestCaseEnd(const TestCase& /*test_case*/) {} - virtual void OnEnvironmentsTearDownStart(const UnitTest& /*unit_test*/) {} - virtual void OnEnvironmentsTearDownEnd(const UnitTest& /*unit_test*/) {} - virtual void OnTestIterationEnd(const UnitTest& /*unit_test*/, - int /*iteration*/) {} - virtual void OnTestProgramEnd(const UnitTest& /*unit_test*/) {} -}; - -// TestEventListeners lets users add listeners to track events in Google Test. -class GTEST_API_ TestEventListeners { - public: - TestEventListeners(); - ~TestEventListeners(); - - // Appends an event listener to the end of the list. Google Test assumes - // the ownership of the listener (i.e. it will delete the listener when - // the test program finishes). - void Append(TestEventListener* listener); - - // Removes the given event listener from the list and returns it. It then - // becomes the caller's responsibility to delete the listener. Returns - // NULL if the listener is not found in the list. - TestEventListener* Release(TestEventListener* listener); - - // Returns the standard listener responsible for the default console - // output. Can be removed from the listeners list to shut down default - // console output. Note that removing this object from the listener list - // with Release transfers its ownership to the caller and makes this - // function return NULL the next time. - TestEventListener* default_result_printer() const { - return default_result_printer_; - } - - // Returns the standard listener responsible for the default XML output - // controlled by the --gtest_output=xml flag. Can be removed from the - // listeners list by users who want to shut down the default XML output - // controlled by this flag and substitute it with custom one. Note that - // removing this object from the listener list with Release transfers its - // ownership to the caller and makes this function return NULL the next - // time. - TestEventListener* default_xml_generator() const { - return default_xml_generator_; - } - - private: - friend class TestCase; - friend class TestInfo; - friend class internal::DefaultGlobalTestPartResultReporter; - friend class internal::NoExecDeathTest; - friend class internal::TestEventListenersAccessor; - friend class internal::UnitTestImpl; - - // Returns repeater that broadcasts the TestEventListener events to all - // subscribers. - TestEventListener* repeater(); - - // Sets the default_result_printer attribute to the provided listener. - // The listener is also added to the listener list and previous - // default_result_printer is removed from it and deleted. The listener can - // also be NULL in which case it will not be added to the list. Does - // nothing if the previous and the current listener objects are the same. - void SetDefaultResultPrinter(TestEventListener* listener); - - // Sets the default_xml_generator attribute to the provided listener. The - // listener is also added to the listener list and previous - // default_xml_generator is removed from it and deleted. The listener can - // also be NULL in which case it will not be added to the list. Does - // nothing if the previous and the current listener objects are the same. - void SetDefaultXmlGenerator(TestEventListener* listener); - - // Controls whether events will be forwarded by the repeater to the - // listeners in the list. - bool EventForwardingEnabled() const; - void SuppressEventForwarding(); - - // The actual list of listeners. - internal::TestEventRepeater* repeater_; - // Listener responsible for the standard result output. - TestEventListener* default_result_printer_; - // Listener responsible for the creation of the XML output file. - TestEventListener* default_xml_generator_; - - // We disallow copying TestEventListeners. - GTEST_DISALLOW_COPY_AND_ASSIGN_(TestEventListeners); -}; - -// A UnitTest consists of a vector of TestCases. -// -// This is a singleton class. The only instance of UnitTest is -// created when UnitTest::GetInstance() is first called. This -// instance is never deleted. -// -// UnitTest is not copyable. -// -// This class is thread-safe as long as the methods are called -// according to their specification. -class GTEST_API_ UnitTest { - public: - // Gets the singleton UnitTest object. The first time this method - // is called, a UnitTest object is constructed and returned. - // Consecutive calls will return the same object. - static UnitTest* GetInstance(); - - // Runs all tests in this UnitTest object and prints the result. - // Returns 0 if successful, or 1 otherwise. - // - // This method can only be called from the main thread. - // - // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. - int Run() GTEST_MUST_USE_RESULT_; - - // Returns the working directory when the first TEST() or TEST_F() - // was executed. The UnitTest object owns the string. - const char* original_working_dir() const; - - // Returns the TestCase object for the test that's currently running, - // or NULL if no test is running. - const TestCase* current_test_case() const; - - // Returns the TestInfo object for the test that's currently running, - // or NULL if no test is running. - const TestInfo* current_test_info() const; - - // Returns the random seed used at the start of the current test run. - int random_seed() const; - -#if GTEST_HAS_PARAM_TEST - // Returns the ParameterizedTestCaseRegistry object used to keep track of - // value-parameterized tests and instantiate and register them. - // - // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. - internal::ParameterizedTestCaseRegistry& parameterized_test_registry(); -#endif // GTEST_HAS_PARAM_TEST - - // Gets the number of successful test cases. - int successful_test_case_count() const; - - // Gets the number of failed test cases. - int failed_test_case_count() const; - - // Gets the number of all test cases. - int total_test_case_count() const; - - // Gets the number of all test cases that contain at least one test - // that should run. - int test_case_to_run_count() const; - - // Gets the number of successful tests. - int successful_test_count() const; - - // Gets the number of failed tests. - int failed_test_count() const; - - // Gets the number of disabled tests. - int disabled_test_count() const; - - // Gets the number of all tests. - int total_test_count() const; - - // Gets the number of tests that should run. - int test_to_run_count() const; - - // Gets the elapsed time, in milliseconds. - TimeInMillis elapsed_time() const; - - // Returns true iff the unit test passed (i.e. all test cases passed). - bool Passed() const; - - // Returns true iff the unit test failed (i.e. some test case failed - // or something outside of all tests failed). - bool Failed() const; - - // Gets the i-th test case among all the test cases. i can range from 0 to - // total_test_case_count() - 1. If i is not in that range, returns NULL. - const TestCase* GetTestCase(int i) const; - - // Returns the list of event listeners that can be used to track events - // inside Google Test. - TestEventListeners& listeners(); - - private: - // Registers and returns a global test environment. When a test - // program is run, all global test environments will be set-up in - // the order they were registered. After all tests in the program - // have finished, all global test environments will be torn-down in - // the *reverse* order they were registered. - // - // The UnitTest object takes ownership of the given environment. - // - // This method can only be called from the main thread. - Environment* AddEnvironment(Environment* env); - - // Adds a TestPartResult to the current TestResult object. All - // Google Test assertion macros (e.g. ASSERT_TRUE, EXPECT_EQ, etc) - // eventually call this to report their results. The user code - // should use the assertion macros instead of calling this directly. - void AddTestPartResult(TestPartResult::Type result_type, - const char* file_name, - int line_number, - const internal::String& message, - const internal::String& os_stack_trace); - - // Adds a TestProperty to the current TestResult object. If the result already - // contains a property with the same key, the value will be updated. - void RecordPropertyForCurrentTest(const char* key, const char* value); - - // Gets the i-th test case among all the test cases. i can range from 0 to - // total_test_case_count() - 1. If i is not in that range, returns NULL. - TestCase* GetMutableTestCase(int i); - - // Accessors for the implementation object. - internal::UnitTestImpl* impl() { return impl_; } - const internal::UnitTestImpl* impl() const { return impl_; } - - // These classes and funcions are friends as they need to access private - // members of UnitTest. - friend class Test; - friend class internal::AssertHelper; - friend class internal::ScopedTrace; - friend Environment* AddGlobalTestEnvironment(Environment* env); - friend internal::UnitTestImpl* internal::GetUnitTestImpl(); - friend void internal::ReportFailureInUnknownLocation( - TestPartResult::Type result_type, - const internal::String& message); - - // Creates an empty UnitTest. - UnitTest(); - - // D'tor - virtual ~UnitTest(); - - // Pushes a trace defined by SCOPED_TRACE() on to the per-thread - // Google Test trace stack. - void PushGTestTrace(const internal::TraceInfo& trace); - - // Pops a trace from the per-thread Google Test trace stack. - void PopGTestTrace(); - - // Protects mutable state in *impl_. This is mutable as some const - // methods need to lock it too. - mutable internal::Mutex mutex_; - - // Opaque implementation object. This field is never changed once - // the object is constructed. We don't mark it as const here, as - // doing so will cause a warning in the constructor of UnitTest. - // Mutable state in *impl_ is protected by mutex_. - internal::UnitTestImpl* impl_; - - // We disallow copying UnitTest. - GTEST_DISALLOW_COPY_AND_ASSIGN_(UnitTest); -}; - -// A convenient wrapper for adding an environment for the test -// program. -// -// You should call this before RUN_ALL_TESTS() is called, probably in -// main(). If you use gtest_main, you need to call this before main() -// starts for it to take effect. For example, you can define a global -// variable like this: -// -// testing::Environment* const foo_env = -// testing::AddGlobalTestEnvironment(new FooEnvironment); -// -// However, we strongly recommend you to write your own main() and -// call AddGlobalTestEnvironment() there, as relying on initialization -// of global variables makes the code harder to read and may cause -// problems when you register multiple environments from different -// translation units and the environments have dependencies among them -// (remember that the compiler doesn't guarantee the order in which -// global variables from different translation units are initialized). -inline Environment* AddGlobalTestEnvironment(Environment* env) { - return UnitTest::GetInstance()->AddEnvironment(env); -} - -// Initializes Google Test. This must be called before calling -// RUN_ALL_TESTS(). In particular, it parses a command line for the -// flags that Google Test recognizes. Whenever a Google Test flag is -// seen, it is removed from argv, and *argc is decremented. -// -// No value is returned. Instead, the Google Test flag variables are -// updated. -// -// Calling the function for the second time has no user-visible effect. -GTEST_API_ void InitGoogleTest(int* argc, char** argv); - -// This overloaded version can be used in Windows programs compiled in -// UNICODE mode. -GTEST_API_ void InitGoogleTest(int* argc, wchar_t** argv); - -namespace internal { - -// Formats a comparison assertion (e.g. ASSERT_EQ, EXPECT_LT, and etc) -// operand to be used in a failure message. The type (but not value) -// of the other operand may affect the format. This allows us to -// print a char* as a raw pointer when it is compared against another -// char*, and print it as a C string when it is compared against an -// std::string object, for example. -// -// The default implementation ignores the type of the other operand. -// Some specialized versions are used to handle formatting wide or -// narrow C strings. -// -// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. -template -String FormatForComparisonFailureMessage(const T1& value, - const T2& /* other_operand */) { - // C++Builder compiles this incorrectly if the namespace isn't explicitly - // given. - return ::testing::PrintToString(value); -} - -// The helper function for {ASSERT|EXPECT}_EQ. -template -AssertionResult CmpHelperEQ(const char* expected_expression, - const char* actual_expression, - const T1& expected, - const T2& actual) { -#ifdef _MSC_VER -# pragma warning(push) // Saves the current warning state. -# pragma warning(disable:4389) // Temporarily disables warning on - // signed/unsigned mismatch. -#endif - - if (expected == actual) { - return AssertionSuccess(); - } - -#ifdef _MSC_VER -# pragma warning(pop) // Restores the warning state. -#endif - - return EqFailure(expected_expression, - actual_expression, - FormatForComparisonFailureMessage(expected, actual), - FormatForComparisonFailureMessage(actual, expected), - false); -} - -// With this overloaded version, we allow anonymous enums to be used -// in {ASSERT|EXPECT}_EQ when compiled with gcc 4, as anonymous enums -// can be implicitly cast to BiggestInt. -GTEST_API_ AssertionResult CmpHelperEQ(const char* expected_expression, - const char* actual_expression, - BiggestInt expected, - BiggestInt actual); - -// The helper class for {ASSERT|EXPECT}_EQ. The template argument -// lhs_is_null_literal is true iff the first argument to ASSERT_EQ() -// is a null pointer literal. The following default implementation is -// for lhs_is_null_literal being false. -template -class EqHelper { - public: - // This templatized version is for the general case. - template - static AssertionResult Compare(const char* expected_expression, - const char* actual_expression, - const T1& expected, - const T2& actual) { - return CmpHelperEQ(expected_expression, actual_expression, expected, - actual); - } - - // With this overloaded version, we allow anonymous enums to be used - // in {ASSERT|EXPECT}_EQ when compiled with gcc 4, as anonymous - // enums can be implicitly cast to BiggestInt. - // - // Even though its body looks the same as the above version, we - // cannot merge the two, as it will make anonymous enums unhappy. - static AssertionResult Compare(const char* expected_expression, - const char* actual_expression, - BiggestInt expected, - BiggestInt actual) { - return CmpHelperEQ(expected_expression, actual_expression, expected, - actual); - } -}; - -// This specialization is used when the first argument to ASSERT_EQ() -// is a null pointer literal, like NULL, false, or 0. -template <> -class EqHelper { - public: - // We define two overloaded versions of Compare(). The first - // version will be picked when the second argument to ASSERT_EQ() is - // NOT a pointer, e.g. ASSERT_EQ(0, AnIntFunction()) or - // EXPECT_EQ(false, a_bool). - template - static AssertionResult Compare( - const char* expected_expression, - const char* actual_expression, - const T1& expected, - const T2& actual, - // The following line prevents this overload from being considered if T2 - // is not a pointer type. We need this because ASSERT_EQ(NULL, my_ptr) - // expands to Compare("", "", NULL, my_ptr), which requires a conversion - // to match the Secret* in the other overload, which would otherwise make - // this template match better. - typename EnableIf::value>::type* = 0) { - return CmpHelperEQ(expected_expression, actual_expression, expected, - actual); - } - - // This version will be picked when the second argument to ASSERT_EQ() is a - // pointer, e.g. ASSERT_EQ(NULL, a_pointer). - template - static AssertionResult Compare( - const char* expected_expression, - const char* actual_expression, - // We used to have a second template parameter instead of Secret*. That - // template parameter would deduce to 'long', making this a better match - // than the first overload even without the first overload's EnableIf. - // Unfortunately, gcc with -Wconversion-null warns when "passing NULL to - // non-pointer argument" (even a deduced integral argument), so the old - // implementation caused warnings in user code. - Secret* /* expected (NULL) */, - T* actual) { - // We already know that 'expected' is a null pointer. - return CmpHelperEQ(expected_expression, actual_expression, - static_cast(NULL), actual); - } -}; - -// A macro for implementing the helper functions needed to implement -// ASSERT_?? and EXPECT_??. It is here just to avoid copy-and-paste -// of similar code. -// -// For each templatized helper function, we also define an overloaded -// version for BiggestInt in order to reduce code bloat and allow -// anonymous enums to be used with {ASSERT|EXPECT}_?? when compiled -// with gcc 4. -// -// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. -#define GTEST_IMPL_CMP_HELPER_(op_name, op)\ -template \ -AssertionResult CmpHelper##op_name(const char* expr1, const char* expr2, \ - const T1& val1, const T2& val2) {\ - if (val1 op val2) {\ - return AssertionSuccess();\ - } else {\ - return AssertionFailure() \ - << "Expected: (" << expr1 << ") " #op " (" << expr2\ - << "), actual: " << FormatForComparisonFailureMessage(val1, val2)\ - << " vs " << FormatForComparisonFailureMessage(val2, val1);\ - }\ -}\ -GTEST_API_ AssertionResult CmpHelper##op_name(\ - const char* expr1, const char* expr2, BiggestInt val1, BiggestInt val2) - -// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. - -// Implements the helper function for {ASSERT|EXPECT}_NE -GTEST_IMPL_CMP_HELPER_(NE, !=); -// Implements the helper function for {ASSERT|EXPECT}_LE -GTEST_IMPL_CMP_HELPER_(LE, <=); -// Implements the helper function for {ASSERT|EXPECT}_LT -GTEST_IMPL_CMP_HELPER_(LT, < ); -// Implements the helper function for {ASSERT|EXPECT}_GE -GTEST_IMPL_CMP_HELPER_(GE, >=); -// Implements the helper function for {ASSERT|EXPECT}_GT -GTEST_IMPL_CMP_HELPER_(GT, > ); - -#undef GTEST_IMPL_CMP_HELPER_ - -// The helper function for {ASSERT|EXPECT}_STREQ. -// -// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. -GTEST_API_ AssertionResult CmpHelperSTREQ(const char* expected_expression, - const char* actual_expression, - const char* expected, - const char* actual); - -// The helper function for {ASSERT|EXPECT}_STRCASEEQ. -// -// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. -GTEST_API_ AssertionResult CmpHelperSTRCASEEQ(const char* expected_expression, - const char* actual_expression, - const char* expected, - const char* actual); - -// The helper function for {ASSERT|EXPECT}_STRNE. -// -// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. -GTEST_API_ AssertionResult CmpHelperSTRNE(const char* s1_expression, - const char* s2_expression, - const char* s1, - const char* s2); - -// The helper function for {ASSERT|EXPECT}_STRCASENE. -// -// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. -GTEST_API_ AssertionResult CmpHelperSTRCASENE(const char* s1_expression, - const char* s2_expression, - const char* s1, - const char* s2); - - -// Helper function for *_STREQ on wide strings. -// -// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. -GTEST_API_ AssertionResult CmpHelperSTREQ(const char* expected_expression, - const char* actual_expression, - const wchar_t* expected, - const wchar_t* actual); - -// Helper function for *_STRNE on wide strings. -// -// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. -GTEST_API_ AssertionResult CmpHelperSTRNE(const char* s1_expression, - const char* s2_expression, - const wchar_t* s1, - const wchar_t* s2); - -} // namespace internal - -// IsSubstring() and IsNotSubstring() are intended to be used as the -// first argument to {EXPECT,ASSERT}_PRED_FORMAT2(), not by -// themselves. They check whether needle is a substring of haystack -// (NULL is considered a substring of itself only), and return an -// appropriate error message when they fail. -// -// The {needle,haystack}_expr arguments are the stringified -// expressions that generated the two real arguments. -GTEST_API_ AssertionResult IsSubstring( - const char* needle_expr, const char* haystack_expr, - const char* needle, const char* haystack); -GTEST_API_ AssertionResult IsSubstring( - const char* needle_expr, const char* haystack_expr, - const wchar_t* needle, const wchar_t* haystack); -GTEST_API_ AssertionResult IsNotSubstring( - const char* needle_expr, const char* haystack_expr, - const char* needle, const char* haystack); -GTEST_API_ AssertionResult IsNotSubstring( - const char* needle_expr, const char* haystack_expr, - const wchar_t* needle, const wchar_t* haystack); -GTEST_API_ AssertionResult IsSubstring( - const char* needle_expr, const char* haystack_expr, - const ::std::string& needle, const ::std::string& haystack); -GTEST_API_ AssertionResult IsNotSubstring( - const char* needle_expr, const char* haystack_expr, - const ::std::string& needle, const ::std::string& haystack); - -#if GTEST_HAS_STD_WSTRING -GTEST_API_ AssertionResult IsSubstring( - const char* needle_expr, const char* haystack_expr, - const ::std::wstring& needle, const ::std::wstring& haystack); -GTEST_API_ AssertionResult IsNotSubstring( - const char* needle_expr, const char* haystack_expr, - const ::std::wstring& needle, const ::std::wstring& haystack); -#endif // GTEST_HAS_STD_WSTRING - -namespace internal { - -// Helper template function for comparing floating-points. -// -// Template parameter: -// -// RawType: the raw floating-point type (either float or double) -// -// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. -template -AssertionResult CmpHelperFloatingPointEQ(const char* expected_expression, - const char* actual_expression, - RawType expected, - RawType actual) { - const FloatingPoint lhs(expected), rhs(actual); - - if (lhs.AlmostEquals(rhs)) { - return AssertionSuccess(); - } - - ::std::stringstream expected_ss; - expected_ss << std::setprecision(std::numeric_limits::digits10 + 2) - << expected; - - ::std::stringstream actual_ss; - actual_ss << std::setprecision(std::numeric_limits::digits10 + 2) - << actual; - - return EqFailure(expected_expression, - actual_expression, - StringStreamToString(&expected_ss), - StringStreamToString(&actual_ss), - false); -} - -// Helper function for implementing ASSERT_NEAR. -// -// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. -GTEST_API_ AssertionResult DoubleNearPredFormat(const char* expr1, - const char* expr2, - const char* abs_error_expr, - double val1, - double val2, - double abs_error); - -// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. -// A class that enables one to stream messages to assertion macros -class GTEST_API_ AssertHelper { - public: - // Constructor. - AssertHelper(TestPartResult::Type type, - const char* file, - int line, - const char* message); - ~AssertHelper(); - - // Message assignment is a semantic trick to enable assertion - // streaming; see the GTEST_MESSAGE_ macro below. - void operator=(const Message& message) const; - - private: - // We put our data in a struct so that the size of the AssertHelper class can - // be as small as possible. This is important because gcc is incapable of - // re-using stack space even for temporary variables, so every EXPECT_EQ - // reserves stack space for another AssertHelper. - struct AssertHelperData { - AssertHelperData(TestPartResult::Type t, - const char* srcfile, - int line_num, - const char* msg) - : type(t), file(srcfile), line(line_num), message(msg) { } - - TestPartResult::Type const type; - const char* const file; - int const line; - String const message; - - private: - GTEST_DISALLOW_COPY_AND_ASSIGN_(AssertHelperData); - }; - - AssertHelperData* const data_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(AssertHelper); -}; - -} // namespace internal - -#if GTEST_HAS_PARAM_TEST -// The pure interface class that all value-parameterized tests inherit from. -// A value-parameterized class must inherit from both ::testing::Test and -// ::testing::WithParamInterface. In most cases that just means inheriting -// from ::testing::TestWithParam, but more complicated test hierarchies -// may need to inherit from Test and WithParamInterface at different levels. -// -// This interface has support for accessing the test parameter value via -// the GetParam() method. -// -// Use it with one of the parameter generator defining functions, like Range(), -// Values(), ValuesIn(), Bool(), and Combine(). -// -// class FooTest : public ::testing::TestWithParam { -// protected: -// FooTest() { -// // Can use GetParam() here. -// } -// virtual ~FooTest() { -// // Can use GetParam() here. -// } -// virtual void SetUp() { -// // Can use GetParam() here. -// } -// virtual void TearDown { -// // Can use GetParam() here. -// } -// }; -// TEST_P(FooTest, DoesBar) { -// // Can use GetParam() method here. -// Foo foo; -// ASSERT_TRUE(foo.DoesBar(GetParam())); -// } -// INSTANTIATE_TEST_CASE_P(OneToTenRange, FooTest, ::testing::Range(1, 10)); - -template -class WithParamInterface { - public: - typedef T ParamType; - virtual ~WithParamInterface() {} - - // The current parameter value. Is also available in the test fixture's - // constructor. This member function is non-static, even though it only - // references static data, to reduce the opportunity for incorrect uses - // like writing 'WithParamInterface::GetParam()' for a test that - // uses a fixture whose parameter type is int. - const ParamType& GetParam() const { return *parameter_; } - - private: - // Sets parameter value. The caller is responsible for making sure the value - // remains alive and unchanged throughout the current test. - static void SetParam(const ParamType* parameter) { - parameter_ = parameter; - } - - // Static value used for accessing parameter during a test lifetime. - static const ParamType* parameter_; - - // TestClass must be a subclass of WithParamInterface and Test. - template friend class internal::ParameterizedTestFactory; -}; - -template -const T* WithParamInterface::parameter_ = NULL; - -// Most value-parameterized classes can ignore the existence of -// WithParamInterface, and can just inherit from ::testing::TestWithParam. - -template -class TestWithParam : public Test, public WithParamInterface { -}; - -#endif // GTEST_HAS_PARAM_TEST - -// Macros for indicating success/failure in test code. - -// ADD_FAILURE unconditionally adds a failure to the current test. -// SUCCEED generates a success - it doesn't automatically make the -// current test successful, as a test is only successful when it has -// no failure. -// -// EXPECT_* verifies that a certain condition is satisfied. If not, -// it behaves like ADD_FAILURE. In particular: -// -// EXPECT_TRUE verifies that a Boolean condition is true. -// EXPECT_FALSE verifies that a Boolean condition is false. -// -// FAIL and ASSERT_* are similar to ADD_FAILURE and EXPECT_*, except -// that they will also abort the current function on failure. People -// usually want the fail-fast behavior of FAIL and ASSERT_*, but those -// writing data-driven tests often find themselves using ADD_FAILURE -// and EXPECT_* more. -// -// Examples: -// -// EXPECT_TRUE(server.StatusIsOK()); -// ASSERT_FALSE(server.HasPendingRequest(port)) -// << "There are still pending requests " << "on port " << port; - -// Generates a nonfatal failure with a generic message. -#define ADD_FAILURE() GTEST_NONFATAL_FAILURE_("Failed") - -// Generates a nonfatal failure at the given source file location with -// a generic message. -#define ADD_FAILURE_AT(file, line) \ - GTEST_MESSAGE_AT_(file, line, "Failed", \ - ::testing::TestPartResult::kNonFatalFailure) - -// Generates a fatal failure with a generic message. -#define GTEST_FAIL() GTEST_FATAL_FAILURE_("Failed") - -// Define this macro to 1 to omit the definition of FAIL(), which is a -// generic name and clashes with some other libraries. -#if !GTEST_DONT_DEFINE_FAIL -# define FAIL() GTEST_FAIL() -#endif - -// Generates a success with a generic message. -#define GTEST_SUCCEED() GTEST_SUCCESS_("Succeeded") - -// Define this macro to 1 to omit the definition of SUCCEED(), which -// is a generic name and clashes with some other libraries. -#if !GTEST_DONT_DEFINE_SUCCEED -# define SUCCEED() GTEST_SUCCEED() -#endif - -// Macros for testing exceptions. -// -// * {ASSERT|EXPECT}_THROW(statement, expected_exception): -// Tests that the statement throws the expected exception. -// * {ASSERT|EXPECT}_NO_THROW(statement): -// Tests that the statement doesn't throw any exception. -// * {ASSERT|EXPECT}_ANY_THROW(statement): -// Tests that the statement throws an exception. - -#define EXPECT_THROW(statement, expected_exception) \ - GTEST_TEST_THROW_(statement, expected_exception, GTEST_NONFATAL_FAILURE_) -#define EXPECT_NO_THROW(statement) \ - GTEST_TEST_NO_THROW_(statement, GTEST_NONFATAL_FAILURE_) -#define EXPECT_ANY_THROW(statement) \ - GTEST_TEST_ANY_THROW_(statement, GTEST_NONFATAL_FAILURE_) -#define ASSERT_THROW(statement, expected_exception) \ - GTEST_TEST_THROW_(statement, expected_exception, GTEST_FATAL_FAILURE_) -#define ASSERT_NO_THROW(statement) \ - GTEST_TEST_NO_THROW_(statement, GTEST_FATAL_FAILURE_) -#define ASSERT_ANY_THROW(statement) \ - GTEST_TEST_ANY_THROW_(statement, GTEST_FATAL_FAILURE_) - -// Boolean assertions. Condition can be either a Boolean expression or an -// AssertionResult. For more information on how to use AssertionResult with -// these macros see comments on that class. -#define EXPECT_TRUE(condition) \ - GTEST_TEST_BOOLEAN_(condition, #condition, false, true, \ - GTEST_NONFATAL_FAILURE_) -#define EXPECT_FALSE(condition) \ - GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \ - GTEST_NONFATAL_FAILURE_) -#define ASSERT_TRUE(condition) \ - GTEST_TEST_BOOLEAN_(condition, #condition, false, true, \ - GTEST_FATAL_FAILURE_) -#define ASSERT_FALSE(condition) \ - GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \ - GTEST_FATAL_FAILURE_) - -// Includes the auto-generated header that implements a family of -// generic predicate assertion macros. -// Copyright 2006, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// This file is AUTOMATICALLY GENERATED on 09/24/2010 by command -// 'gen_gtest_pred_impl.py 5'. DO NOT EDIT BY HAND! -// -// Implements a family of generic predicate assertion macros. - -#ifndef GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ -#define GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ - -// Makes sure this header is not included before gtest.h. -#ifndef GTEST_INCLUDE_GTEST_GTEST_H_ -# error Do not include gtest_pred_impl.h directly. Include gtest.h instead. -#endif // GTEST_INCLUDE_GTEST_GTEST_H_ - -// This header implements a family of generic predicate assertion -// macros: -// -// ASSERT_PRED_FORMAT1(pred_format, v1) -// ASSERT_PRED_FORMAT2(pred_format, v1, v2) -// ... -// -// where pred_format is a function or functor that takes n (in the -// case of ASSERT_PRED_FORMATn) values and their source expression -// text, and returns a testing::AssertionResult. See the definition -// of ASSERT_EQ in gtest.h for an example. -// -// If you don't care about formatting, you can use the more -// restrictive version: -// -// ASSERT_PRED1(pred, v1) -// ASSERT_PRED2(pred, v1, v2) -// ... -// -// where pred is an n-ary function or functor that returns bool, -// and the values v1, v2, ..., must support the << operator for -// streaming to std::ostream. -// -// We also define the EXPECT_* variations. -// -// For now we only support predicates whose arity is at most 5. -// Please email googletestframework@googlegroups.com if you need -// support for higher arities. - -// GTEST_ASSERT_ is the basic statement to which all of the assertions -// in this file reduce. Don't use this in your code. - -#define GTEST_ASSERT_(expression, on_failure) \ - GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ - if (const ::testing::AssertionResult gtest_ar = (expression)) \ - ; \ - else \ - on_failure(gtest_ar.failure_message()) - - -// Helper function for implementing {EXPECT|ASSERT}_PRED1. Don't use -// this in your code. -template -AssertionResult AssertPred1Helper(const char* pred_text, - const char* e1, - Pred pred, - const T1& v1) { - if (pred(v1)) return AssertionSuccess(); - - return AssertionFailure() << pred_text << "(" - << e1 << ") evaluates to false, where" - << "\n" << e1 << " evaluates to " << v1; -} - -// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT1. -// Don't use this in your code. -#define GTEST_PRED_FORMAT1_(pred_format, v1, on_failure)\ - GTEST_ASSERT_(pred_format(#v1, v1),\ - on_failure) - -// Internal macro for implementing {EXPECT|ASSERT}_PRED1. Don't use -// this in your code. -#define GTEST_PRED1_(pred, v1, on_failure)\ - GTEST_ASSERT_(::testing::AssertPred1Helper(#pred, \ - #v1, \ - pred, \ - v1), on_failure) - -// Unary predicate assertion macros. -#define EXPECT_PRED_FORMAT1(pred_format, v1) \ - GTEST_PRED_FORMAT1_(pred_format, v1, GTEST_NONFATAL_FAILURE_) -#define EXPECT_PRED1(pred, v1) \ - GTEST_PRED1_(pred, v1, GTEST_NONFATAL_FAILURE_) -#define ASSERT_PRED_FORMAT1(pred_format, v1) \ - GTEST_PRED_FORMAT1_(pred_format, v1, GTEST_FATAL_FAILURE_) -#define ASSERT_PRED1(pred, v1) \ - GTEST_PRED1_(pred, v1, GTEST_FATAL_FAILURE_) - - - -// Helper function for implementing {EXPECT|ASSERT}_PRED2. Don't use -// this in your code. -template -AssertionResult AssertPred2Helper(const char* pred_text, - const char* e1, - const char* e2, - Pred pred, - const T1& v1, - const T2& v2) { - if (pred(v1, v2)) return AssertionSuccess(); - - return AssertionFailure() << pred_text << "(" - << e1 << ", " - << e2 << ") evaluates to false, where" - << "\n" << e1 << " evaluates to " << v1 - << "\n" << e2 << " evaluates to " << v2; -} - -// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT2. -// Don't use this in your code. -#define GTEST_PRED_FORMAT2_(pred_format, v1, v2, on_failure)\ - GTEST_ASSERT_(pred_format(#v1, #v2, v1, v2),\ - on_failure) - -// Internal macro for implementing {EXPECT|ASSERT}_PRED2. Don't use -// this in your code. -#define GTEST_PRED2_(pred, v1, v2, on_failure)\ - GTEST_ASSERT_(::testing::AssertPred2Helper(#pred, \ - #v1, \ - #v2, \ - pred, \ - v1, \ - v2), on_failure) - -// Binary predicate assertion macros. -#define EXPECT_PRED_FORMAT2(pred_format, v1, v2) \ - GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_NONFATAL_FAILURE_) -#define EXPECT_PRED2(pred, v1, v2) \ - GTEST_PRED2_(pred, v1, v2, GTEST_NONFATAL_FAILURE_) -#define ASSERT_PRED_FORMAT2(pred_format, v1, v2) \ - GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_FATAL_FAILURE_) -#define ASSERT_PRED2(pred, v1, v2) \ - GTEST_PRED2_(pred, v1, v2, GTEST_FATAL_FAILURE_) - - - -// Helper function for implementing {EXPECT|ASSERT}_PRED3. Don't use -// this in your code. -template -AssertionResult AssertPred3Helper(const char* pred_text, - const char* e1, - const char* e2, - const char* e3, - Pred pred, - const T1& v1, - const T2& v2, - const T3& v3) { - if (pred(v1, v2, v3)) return AssertionSuccess(); - - return AssertionFailure() << pred_text << "(" - << e1 << ", " - << e2 << ", " - << e3 << ") evaluates to false, where" - << "\n" << e1 << " evaluates to " << v1 - << "\n" << e2 << " evaluates to " << v2 - << "\n" << e3 << " evaluates to " << v3; -} - -// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT3. -// Don't use this in your code. -#define GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, on_failure)\ - GTEST_ASSERT_(pred_format(#v1, #v2, #v3, v1, v2, v3),\ - on_failure) - -// Internal macro for implementing {EXPECT|ASSERT}_PRED3. Don't use -// this in your code. -#define GTEST_PRED3_(pred, v1, v2, v3, on_failure)\ - GTEST_ASSERT_(::testing::AssertPred3Helper(#pred, \ - #v1, \ - #v2, \ - #v3, \ - pred, \ - v1, \ - v2, \ - v3), on_failure) - -// Ternary predicate assertion macros. -#define EXPECT_PRED_FORMAT3(pred_format, v1, v2, v3) \ - GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, GTEST_NONFATAL_FAILURE_) -#define EXPECT_PRED3(pred, v1, v2, v3) \ - GTEST_PRED3_(pred, v1, v2, v3, GTEST_NONFATAL_FAILURE_) -#define ASSERT_PRED_FORMAT3(pred_format, v1, v2, v3) \ - GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, GTEST_FATAL_FAILURE_) -#define ASSERT_PRED3(pred, v1, v2, v3) \ - GTEST_PRED3_(pred, v1, v2, v3, GTEST_FATAL_FAILURE_) - - - -// Helper function for implementing {EXPECT|ASSERT}_PRED4. Don't use -// this in your code. -template -AssertionResult AssertPred4Helper(const char* pred_text, - const char* e1, - const char* e2, - const char* e3, - const char* e4, - Pred pred, - const T1& v1, - const T2& v2, - const T3& v3, - const T4& v4) { - if (pred(v1, v2, v3, v4)) return AssertionSuccess(); - - return AssertionFailure() << pred_text << "(" - << e1 << ", " - << e2 << ", " - << e3 << ", " - << e4 << ") evaluates to false, where" - << "\n" << e1 << " evaluates to " << v1 - << "\n" << e2 << " evaluates to " << v2 - << "\n" << e3 << " evaluates to " << v3 - << "\n" << e4 << " evaluates to " << v4; -} - -// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT4. -// Don't use this in your code. -#define GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, on_failure)\ - GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, v1, v2, v3, v4),\ - on_failure) - -// Internal macro for implementing {EXPECT|ASSERT}_PRED4. Don't use -// this in your code. -#define GTEST_PRED4_(pred, v1, v2, v3, v4, on_failure)\ - GTEST_ASSERT_(::testing::AssertPred4Helper(#pred, \ - #v1, \ - #v2, \ - #v3, \ - #v4, \ - pred, \ - v1, \ - v2, \ - v3, \ - v4), on_failure) - -// 4-ary predicate assertion macros. -#define EXPECT_PRED_FORMAT4(pred_format, v1, v2, v3, v4) \ - GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, GTEST_NONFATAL_FAILURE_) -#define EXPECT_PRED4(pred, v1, v2, v3, v4) \ - GTEST_PRED4_(pred, v1, v2, v3, v4, GTEST_NONFATAL_FAILURE_) -#define ASSERT_PRED_FORMAT4(pred_format, v1, v2, v3, v4) \ - GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, GTEST_FATAL_FAILURE_) -#define ASSERT_PRED4(pred, v1, v2, v3, v4) \ - GTEST_PRED4_(pred, v1, v2, v3, v4, GTEST_FATAL_FAILURE_) - - - -// Helper function for implementing {EXPECT|ASSERT}_PRED5. Don't use -// this in your code. -template -AssertionResult AssertPred5Helper(const char* pred_text, - const char* e1, - const char* e2, - const char* e3, - const char* e4, - const char* e5, - Pred pred, - const T1& v1, - const T2& v2, - const T3& v3, - const T4& v4, - const T5& v5) { - if (pred(v1, v2, v3, v4, v5)) return AssertionSuccess(); - - return AssertionFailure() << pred_text << "(" - << e1 << ", " - << e2 << ", " - << e3 << ", " - << e4 << ", " - << e5 << ") evaluates to false, where" - << "\n" << e1 << " evaluates to " << v1 - << "\n" << e2 << " evaluates to " << v2 - << "\n" << e3 << " evaluates to " << v3 - << "\n" << e4 << " evaluates to " << v4 - << "\n" << e5 << " evaluates to " << v5; -} - -// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT5. -// Don't use this in your code. -#define GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, on_failure)\ - GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, #v5, v1, v2, v3, v4, v5),\ - on_failure) - -// Internal macro for implementing {EXPECT|ASSERT}_PRED5. Don't use -// this in your code. -#define GTEST_PRED5_(pred, v1, v2, v3, v4, v5, on_failure)\ - GTEST_ASSERT_(::testing::AssertPred5Helper(#pred, \ - #v1, \ - #v2, \ - #v3, \ - #v4, \ - #v5, \ - pred, \ - v1, \ - v2, \ - v3, \ - v4, \ - v5), on_failure) - -// 5-ary predicate assertion macros. -#define EXPECT_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5) \ - GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, GTEST_NONFATAL_FAILURE_) -#define EXPECT_PRED5(pred, v1, v2, v3, v4, v5) \ - GTEST_PRED5_(pred, v1, v2, v3, v4, v5, GTEST_NONFATAL_FAILURE_) -#define ASSERT_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5) \ - GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, GTEST_FATAL_FAILURE_) -#define ASSERT_PRED5(pred, v1, v2, v3, v4, v5) \ - GTEST_PRED5_(pred, v1, v2, v3, v4, v5, GTEST_FATAL_FAILURE_) - - - -#endif // GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ - -// Macros for testing equalities and inequalities. -// -// * {ASSERT|EXPECT}_EQ(expected, actual): Tests that expected == actual -// * {ASSERT|EXPECT}_NE(v1, v2): Tests that v1 != v2 -// * {ASSERT|EXPECT}_LT(v1, v2): Tests that v1 < v2 -// * {ASSERT|EXPECT}_LE(v1, v2): Tests that v1 <= v2 -// * {ASSERT|EXPECT}_GT(v1, v2): Tests that v1 > v2 -// * {ASSERT|EXPECT}_GE(v1, v2): Tests that v1 >= v2 -// -// When they are not, Google Test prints both the tested expressions and -// their actual values. The values must be compatible built-in types, -// or you will get a compiler error. By "compatible" we mean that the -// values can be compared by the respective operator. -// -// Note: -// -// 1. It is possible to make a user-defined type work with -// {ASSERT|EXPECT}_??(), but that requires overloading the -// comparison operators and is thus discouraged by the Google C++ -// Usage Guide. Therefore, you are advised to use the -// {ASSERT|EXPECT}_TRUE() macro to assert that two objects are -// equal. -// -// 2. The {ASSERT|EXPECT}_??() macros do pointer comparisons on -// pointers (in particular, C strings). Therefore, if you use it -// with two C strings, you are testing how their locations in memory -// are related, not how their content is related. To compare two C -// strings by content, use {ASSERT|EXPECT}_STR*(). -// -// 3. {ASSERT|EXPECT}_EQ(expected, actual) is preferred to -// {ASSERT|EXPECT}_TRUE(expected == actual), as the former tells you -// what the actual value is when it fails, and similarly for the -// other comparisons. -// -// 4. Do not depend on the order in which {ASSERT|EXPECT}_??() -// evaluate their arguments, which is undefined. -// -// 5. These macros evaluate their arguments exactly once. -// -// Examples: -// -// EXPECT_NE(5, Foo()); -// EXPECT_EQ(NULL, a_pointer); -// ASSERT_LT(i, array_size); -// ASSERT_GT(records.size(), 0) << "There is no record left."; - -#define EXPECT_EQ(expected, actual) \ - EXPECT_PRED_FORMAT2(::testing::internal:: \ - EqHelper::Compare, \ - expected, actual) -#define EXPECT_NE(expected, actual) \ - EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperNE, expected, actual) -#define EXPECT_LE(val1, val2) \ - EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperLE, val1, val2) -#define EXPECT_LT(val1, val2) \ - EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperLT, val1, val2) -#define EXPECT_GE(val1, val2) \ - EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperGE, val1, val2) -#define EXPECT_GT(val1, val2) \ - EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperGT, val1, val2) - -#define GTEST_ASSERT_EQ(expected, actual) \ - ASSERT_PRED_FORMAT2(::testing::internal:: \ - EqHelper::Compare, \ - expected, actual) -#define GTEST_ASSERT_NE(val1, val2) \ - ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperNE, val1, val2) -#define GTEST_ASSERT_LE(val1, val2) \ - ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperLE, val1, val2) -#define GTEST_ASSERT_LT(val1, val2) \ - ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperLT, val1, val2) -#define GTEST_ASSERT_GE(val1, val2) \ - ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperGE, val1, val2) -#define GTEST_ASSERT_GT(val1, val2) \ - ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperGT, val1, val2) - -// Define macro GTEST_DONT_DEFINE_ASSERT_XY to 1 to omit the definition of -// ASSERT_XY(), which clashes with some users' own code. - -#if !GTEST_DONT_DEFINE_ASSERT_EQ -# define ASSERT_EQ(val1, val2) GTEST_ASSERT_EQ(val1, val2) -#endif - -#if !GTEST_DONT_DEFINE_ASSERT_NE -# define ASSERT_NE(val1, val2) GTEST_ASSERT_NE(val1, val2) -#endif - -#if !GTEST_DONT_DEFINE_ASSERT_LE -# define ASSERT_LE(val1, val2) GTEST_ASSERT_LE(val1, val2) -#endif - -#if !GTEST_DONT_DEFINE_ASSERT_LT -# define ASSERT_LT(val1, val2) GTEST_ASSERT_LT(val1, val2) -#endif - -#if !GTEST_DONT_DEFINE_ASSERT_GE -# define ASSERT_GE(val1, val2) GTEST_ASSERT_GE(val1, val2) -#endif - -#if !GTEST_DONT_DEFINE_ASSERT_GT -# define ASSERT_GT(val1, val2) GTEST_ASSERT_GT(val1, val2) -#endif - -// C String Comparisons. All tests treat NULL and any non-NULL string -// as different. Two NULLs are equal. -// -// * {ASSERT|EXPECT}_STREQ(s1, s2): Tests that s1 == s2 -// * {ASSERT|EXPECT}_STRNE(s1, s2): Tests that s1 != s2 -// * {ASSERT|EXPECT}_STRCASEEQ(s1, s2): Tests that s1 == s2, ignoring case -// * {ASSERT|EXPECT}_STRCASENE(s1, s2): Tests that s1 != s2, ignoring case -// -// For wide or narrow string objects, you can use the -// {ASSERT|EXPECT}_??() macros. -// -// Don't depend on the order in which the arguments are evaluated, -// which is undefined. -// -// These macros evaluate their arguments exactly once. - -#define EXPECT_STREQ(expected, actual) \ - EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTREQ, expected, actual) -#define EXPECT_STRNE(s1, s2) \ - EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRNE, s1, s2) -#define EXPECT_STRCASEEQ(expected, actual) \ - EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASEEQ, expected, actual) -#define EXPECT_STRCASENE(s1, s2)\ - EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASENE, s1, s2) - -#define ASSERT_STREQ(expected, actual) \ - ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTREQ, expected, actual) -#define ASSERT_STRNE(s1, s2) \ - ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRNE, s1, s2) -#define ASSERT_STRCASEEQ(expected, actual) \ - ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASEEQ, expected, actual) -#define ASSERT_STRCASENE(s1, s2)\ - ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASENE, s1, s2) - -// Macros for comparing floating-point numbers. -// -// * {ASSERT|EXPECT}_FLOAT_EQ(expected, actual): -// Tests that two float values are almost equal. -// * {ASSERT|EXPECT}_DOUBLE_EQ(expected, actual): -// Tests that two double values are almost equal. -// * {ASSERT|EXPECT}_NEAR(v1, v2, abs_error): -// Tests that v1 and v2 are within the given distance to each other. -// -// Google Test uses ULP-based comparison to automatically pick a default -// error bound that is appropriate for the operands. See the -// FloatingPoint template class in gtest-internal.h if you are -// interested in the implementation details. - -#define EXPECT_FLOAT_EQ(expected, actual)\ - EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ - expected, actual) - -#define EXPECT_DOUBLE_EQ(expected, actual)\ - EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ - expected, actual) - -#define ASSERT_FLOAT_EQ(expected, actual)\ - ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ - expected, actual) - -#define ASSERT_DOUBLE_EQ(expected, actual)\ - ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ - expected, actual) - -#define EXPECT_NEAR(val1, val2, abs_error)\ - EXPECT_PRED_FORMAT3(::testing::internal::DoubleNearPredFormat, \ - val1, val2, abs_error) - -#define ASSERT_NEAR(val1, val2, abs_error)\ - ASSERT_PRED_FORMAT3(::testing::internal::DoubleNearPredFormat, \ - val1, val2, abs_error) - -// These predicate format functions work on floating-point values, and -// can be used in {ASSERT|EXPECT}_PRED_FORMAT2*(), e.g. -// -// EXPECT_PRED_FORMAT2(testing::DoubleLE, Foo(), 5.0); - -// Asserts that val1 is less than, or almost equal to, val2. Fails -// otherwise. In particular, it fails if either val1 or val2 is NaN. -GTEST_API_ AssertionResult FloatLE(const char* expr1, const char* expr2, - float val1, float val2); -GTEST_API_ AssertionResult DoubleLE(const char* expr1, const char* expr2, - double val1, double val2); - - -#if GTEST_OS_WINDOWS - -// Macros that test for HRESULT failure and success, these are only useful -// on Windows, and rely on Windows SDK macros and APIs to compile. -// -// * {ASSERT|EXPECT}_HRESULT_{SUCCEEDED|FAILED}(expr) -// -// When expr unexpectedly fails or succeeds, Google Test prints the -// expected result and the actual result with both a human-readable -// string representation of the error, if available, as well as the -// hex result code. -# define EXPECT_HRESULT_SUCCEEDED(expr) \ - EXPECT_PRED_FORMAT1(::testing::internal::IsHRESULTSuccess, (expr)) - -# define ASSERT_HRESULT_SUCCEEDED(expr) \ - ASSERT_PRED_FORMAT1(::testing::internal::IsHRESULTSuccess, (expr)) - -# define EXPECT_HRESULT_FAILED(expr) \ - EXPECT_PRED_FORMAT1(::testing::internal::IsHRESULTFailure, (expr)) - -# define ASSERT_HRESULT_FAILED(expr) \ - ASSERT_PRED_FORMAT1(::testing::internal::IsHRESULTFailure, (expr)) - -#endif // GTEST_OS_WINDOWS - -// Macros that execute statement and check that it doesn't generate new fatal -// failures in the current thread. -// -// * {ASSERT|EXPECT}_NO_FATAL_FAILURE(statement); -// -// Examples: -// -// EXPECT_NO_FATAL_FAILURE(Process()); -// ASSERT_NO_FATAL_FAILURE(Process()) << "Process() failed"; -// -#define ASSERT_NO_FATAL_FAILURE(statement) \ - GTEST_TEST_NO_FATAL_FAILURE_(statement, GTEST_FATAL_FAILURE_) -#define EXPECT_NO_FATAL_FAILURE(statement) \ - GTEST_TEST_NO_FATAL_FAILURE_(statement, GTEST_NONFATAL_FAILURE_) - -// Causes a trace (including the source file path, the current line -// number, and the given message) to be included in every test failure -// message generated by code in the current scope. The effect is -// undone when the control leaves the current scope. -// -// The message argument can be anything streamable to std::ostream. -// -// In the implementation, we include the current line number as part -// of the dummy variable name, thus allowing multiple SCOPED_TRACE()s -// to appear in the same block - as long as they are on different -// lines. -#define SCOPED_TRACE(message) \ - ::testing::internal::ScopedTrace GTEST_CONCAT_TOKEN_(gtest_trace_, __LINE__)(\ - __FILE__, __LINE__, ::testing::Message() << (message)) - -// Compile-time assertion for type equality. -// StaticAssertTypeEq() compiles iff type1 and type2 are -// the same type. The value it returns is not interesting. -// -// Instead of making StaticAssertTypeEq a class template, we make it a -// function template that invokes a helper class template. This -// prevents a user from misusing StaticAssertTypeEq by -// defining objects of that type. -// -// CAVEAT: -// -// When used inside a method of a class template, -// StaticAssertTypeEq() is effective ONLY IF the method is -// instantiated. For example, given: -// -// template class Foo { -// public: -// void Bar() { testing::StaticAssertTypeEq(); } -// }; -// -// the code: -// -// void Test1() { Foo foo; } -// -// will NOT generate a compiler error, as Foo::Bar() is never -// actually instantiated. Instead, you need: -// -// void Test2() { Foo foo; foo.Bar(); } -// -// to cause a compiler error. -template -bool StaticAssertTypeEq() { - (void)internal::StaticAssertTypeEqHelper(); - return true; -} - -// Defines a test. -// -// The first parameter is the name of the test case, and the second -// parameter is the name of the test within the test case. -// -// The convention is to end the test case name with "Test". For -// example, a test case for the Foo class can be named FooTest. -// -// The user should put his test code between braces after using this -// macro. Example: -// -// TEST(FooTest, InitializesCorrectly) { -// Foo foo; -// EXPECT_TRUE(foo.StatusIsOK()); -// } - -// Note that we call GetTestTypeId() instead of GetTypeId< -// ::testing::Test>() here to get the type ID of testing::Test. This -// is to work around a suspected linker bug when using Google Test as -// a framework on Mac OS X. The bug causes GetTypeId< -// ::testing::Test>() to return different values depending on whether -// the call is from the Google Test framework itself or from user test -// code. GetTestTypeId() is guaranteed to always return the same -// value, as it always calls GetTypeId<>() from the Google Test -// framework. -#define GTEST_TEST(test_case_name, test_name)\ - GTEST_TEST_(test_case_name, test_name, \ - ::testing::Test, ::testing::internal::GetTestTypeId()) - -// Define this macro to 1 to omit the definition of TEST(), which -// is a generic name and clashes with some other libraries. -#if !GTEST_DONT_DEFINE_TEST -# define TEST(test_case_name, test_name) GTEST_TEST(test_case_name, test_name) -#endif - -// Defines a test that uses a test fixture. -// -// The first parameter is the name of the test fixture class, which -// also doubles as the test case name. The second parameter is the -// name of the test within the test case. -// -// A test fixture class must be declared earlier. The user should put -// his test code between braces after using this macro. Example: -// -// class FooTest : public testing::Test { -// protected: -// virtual void SetUp() { b_.AddElement(3); } -// -// Foo a_; -// Foo b_; -// }; -// -// TEST_F(FooTest, InitializesCorrectly) { -// EXPECT_TRUE(a_.StatusIsOK()); -// } -// -// TEST_F(FooTest, ReturnsElementCountCorrectly) { -// EXPECT_EQ(0, a_.size()); -// EXPECT_EQ(1, b_.size()); -// } - -#define TEST_F(test_fixture, test_name)\ - GTEST_TEST_(test_fixture, test_name, test_fixture, \ - ::testing::internal::GetTypeId()) - -// Use this macro in main() to run all tests. It returns 0 if all -// tests are successful, or 1 otherwise. -// -// RUN_ALL_TESTS() should be invoked after the command line has been -// parsed by InitGoogleTest(). - -#define RUN_ALL_TESTS()\ - (::testing::UnitTest::GetInstance()->Run()) - -} // namespace testing - -#endif // GTEST_INCLUDE_GTEST_GTEST_H_ diff --git a/src/3rdparty/cpp-httplib/test/gtest/gtest_main.cc b/src/3rdparty/cpp-httplib/test/gtest/gtest_main.cc deleted file mode 100644 index a09bbe0c..00000000 --- a/src/3rdparty/cpp-httplib/test/gtest/gtest_main.cc +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2006, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include - -#include "gtest/gtest.h" - -GTEST_API_ int main(int argc, char **argv) { - std::cout << "Running main() from gtest_main.cc\n"; - - testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/src/3rdparty/cpp-httplib/test/test.cc b/src/3rdparty/cpp-httplib/test/test.cc deleted file mode 100644 index 98f09542..00000000 --- a/src/3rdparty/cpp-httplib/test/test.cc +++ /dev/null @@ -1,378 +0,0 @@ - -#include -#include -#include -#include - -#define SERVER_CERT_FILE "./cert.pem" -#define SERVER_PRIVATE_KEY_FILE "./key.pem" - -#ifdef _WIN32 -#include -#define msleep(n) ::Sleep(n) -#else -#define msleep(n) ::usleep(n * 1000) -#endif - -using namespace std; -using namespace httplib; - -const char* HOST = "localhost"; -const int PORT = 1234; - -#ifdef _WIN32 -TEST(StartupTest, WSAStartup) -{ - WSADATA wsaData; - int ret = WSAStartup(0x0002, &wsaData); - ASSERT_EQ(0, ret); -} -#endif - -TEST(SplitTest, ParseQueryString) -{ - string s = "key1=val1&key2=val2&key3=val3"; - map dic; - - detail::split(s.c_str(), s.c_str() + s.size(), '&', [&](const char* b, const char* e) { - string key, val; - detail::split(b, e, '=', [&](const char* b, const char* e) { - if (key.empty()) { - key.assign(b, e); - } else { - val.assign(b, e); - } - }); - dic[key] = val; - }); - - EXPECT_EQ("val1", dic["key1"]); - EXPECT_EQ("val2", dic["key2"]); - EXPECT_EQ("val3", dic["key3"]); -} - -TEST(ParseQueryTest, ParseQueryString) -{ - string s = "key1=val1&key2=val2&key3=val3"; - map dic; - - detail::parse_query_text(s, dic); - - EXPECT_EQ("val1", dic["key1"]); - EXPECT_EQ("val2", dic["key2"]); - EXPECT_EQ("val3", dic["key3"]); -} - -TEST(SocketTest, OpenClose) -{ - socket_t sock = detail::create_server_socket(HOST, PORT, 0); - ASSERT_NE(-1, sock); - - auto ret = detail::close_socket(sock); - EXPECT_EQ(0, ret); -} - -TEST(SocketTest, OpenCloseWithAI_PASSIVE) -{ - socket_t sock = detail::create_server_socket(nullptr, PORT, AI_PASSIVE); - ASSERT_NE(-1, sock); - - auto ret = detail::close_socket(sock); - EXPECT_EQ(0, ret); -} - -TEST(GetHeaderValueTest, DefaultValue) -{ - MultiMap map = {{"Dummy","Dummy"}}; - auto val = detail::get_header_value(map, "Content-Type", "text/plain"); - ASSERT_STREQ("text/plain", val); -} - -TEST(GetHeaderValueTest, DefaultValueInt) -{ - MultiMap map = {{"Dummy","Dummy"}}; - auto val = detail::get_header_value_int(map, "Content-Length", 100); - EXPECT_EQ(100, val); -} - -TEST(GetHeaderValueTest, RegularValue) -{ - MultiMap map = {{"Content-Type", "text/html"}, {"Dummy", "Dummy"}}; - auto val = detail::get_header_value(map, "Content-Type", "text/plain"); - ASSERT_STREQ("text/html", val); -} - -TEST(GetHeaderValueTest, RegularValueInt) -{ - MultiMap map = {{"Content-Length", "100"}, {"Dummy", "Dummy"}}; - auto val = detail::get_header_value_int(map, "Content-Length", 0); - EXPECT_EQ(100, val); -} - -class ServerTest : public ::testing::Test { -protected: - ServerTest() - : cli_(HOST, PORT) -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT - , svr_(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE) -#endif - , up_(false) {} - - virtual void SetUp() { - svr_.set_base_dir("./www"); - - svr_.get("/hi", [&](const Request& req, Response& res) { - res.set_content("Hello World!", "text/plain"); - }); - - svr_.get("/", [&](const Request& req, Response& res) { - res.set_redirect("/hi"); - }); - - svr_.post("/person", [&](const Request& req, Response& res) { - if (req.has_param("name") && req.has_param("note")) { - persons_[req.params.at("name")] = req.params.at("note"); - } else { - res.status = 400; - } - }); - - svr_.get("/person/(.*)", [&](const Request& req, Response& res) { - string name = req.matches[1]; - if (persons_.find(name) != persons_.end()) { - auto note = persons_[name]; - res.set_content(note, "text/plain"); - } else { - res.status = 404; - } - }); - - svr_.get("/stop", [&](const Request& req, Response& res) { - svr_.stop(); - }); - - persons_["john"] = "programmer"; - - f_ = async([&](){ - up_ = true; - svr_.listen(HOST, PORT); - }); - - while (!up_) { - msleep(1); - } - } - - virtual void TearDown() { - //svr_.stop(); // NOTE: This causes dead lock on Windows. - cli_.get("/stop"); - f_.get(); - } - - map persons_; -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT - SSLClient cli_; - SSLServer svr_; -#else - Client cli_; - Server svr_; -#endif - future f_; - bool up_; -}; - -TEST_F(ServerTest, GetMethod200) -{ - auto res = cli_.get("/hi"); - ASSERT_TRUE(res != nullptr); - EXPECT_EQ(200, res->status); - EXPECT_EQ("text/plain", res->get_header_value("Content-Type")); - EXPECT_EQ("Hello World!", res->body); -} - -TEST_F(ServerTest, GetMethod302) -{ - auto res = cli_.get("/"); - ASSERT_TRUE(res != nullptr); - EXPECT_EQ(302, res->status); - EXPECT_EQ("/hi", res->get_header_value("Location")); -} - -TEST_F(ServerTest, GetMethod404) -{ - auto res = cli_.get("/invalid"); - ASSERT_TRUE(res != nullptr); - EXPECT_EQ(404, res->status); -} - -TEST_F(ServerTest, HeadMethod200) -{ - auto res = cli_.head("/hi"); - ASSERT_TRUE(res != nullptr); - EXPECT_EQ(200, res->status); - EXPECT_EQ("text/plain", res->get_header_value("Content-Type")); - EXPECT_EQ("", res->body); -} - -TEST_F(ServerTest, HeadMethod404) -{ - auto res = cli_.head("/invalid"); - ASSERT_TRUE(res != nullptr); - EXPECT_EQ(404, res->status); - EXPECT_EQ("", res->body); -} - -TEST_F(ServerTest, GetMethodPersonJohn) -{ - auto res = cli_.get("/person/john"); - ASSERT_TRUE(res != nullptr); - EXPECT_EQ(200, res->status); - EXPECT_EQ("text/plain", res->get_header_value("Content-Type")); - EXPECT_EQ("programmer", res->body); -} - -TEST_F(ServerTest, PostMethod1) -{ - auto res = cli_.get("/person/john1"); - ASSERT_TRUE(res != nullptr); - ASSERT_EQ(404, res->status); - - res = cli_.post("/person", "name=john1¬e=coder", "application/x-www-form-urlencoded"); - ASSERT_TRUE(res != nullptr); - ASSERT_EQ(200, res->status); - - res = cli_.get("/person/john1"); - ASSERT_TRUE(res != nullptr); - ASSERT_EQ(200, res->status); - ASSERT_EQ("text/plain", res->get_header_value("Content-Type")); - ASSERT_EQ("coder", res->body); -} - -TEST_F(ServerTest, PostMethod2) -{ - auto res = cli_.get("/person/john2"); - ASSERT_TRUE(res != nullptr); - ASSERT_EQ(404, res->status); - - Map params; - params["name"] = "john2"; - params["note"] = "coder"; - - res = cli_.post("/person", params); - ASSERT_TRUE(res != nullptr); - ASSERT_EQ(200, res->status); - - res = cli_.get("/person/john2"); - ASSERT_TRUE(res != nullptr); - ASSERT_EQ(200, res->status); - ASSERT_EQ("text/plain", res->get_header_value("Content-Type")); - ASSERT_EQ("coder", res->body); -} - -TEST_F(ServerTest, GetMethodDir) -{ - auto res = cli_.get("/dir/"); - ASSERT_TRUE(res != nullptr); - EXPECT_EQ(200, res->status); - EXPECT_EQ("text/html", res->get_header_value("Content-Type")); - - auto body = R"( - - - - Test - hi - - -)"; - EXPECT_EQ(body, res->body); -} - -TEST_F(ServerTest, GetMethodDirTest) -{ - auto res = cli_.get("/dir/test.html"); - ASSERT_TRUE(res != nullptr); - EXPECT_EQ(200, res->status); - EXPECT_EQ("text/html", res->get_header_value("Content-Type")); - EXPECT_EQ("test.html", res->body); -} - -TEST_F(ServerTest, InvalidBaseDir) -{ - EXPECT_EQ(false, svr_.set_base_dir("invalid_dir")); - EXPECT_EQ(true, svr_.set_base_dir(".")); -} - -class ServerTestWithAI_PASSIVE : public ::testing::Test { -protected: - ServerTestWithAI_PASSIVE() - : cli_(HOST, PORT) -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT - , svr_(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE) -#endif - , up_(false) {} - - virtual void SetUp() { - svr_.get("/hi", [&](const Request& req, Response& res) { - res.set_content("Hello World!", "text/plain"); - }); - - svr_.get("/stop", [&](const Request& req, Response& res) { - svr_.stop(); - }); - - f_ = async([&](){ - up_ = true; - svr_.listen(nullptr, PORT, AI_PASSIVE); - }); - - while (!up_) { - msleep(1); - } - } - - virtual void TearDown() { - //svr_.stop(); // NOTE: This causes dead lock on Windows. - cli_.get("/stop"); - f_.get(); - } - -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT - SSLClient cli_; - SSLServer svr_; -#else - Client cli_; - Server svr_; -#endif - future f_; - bool up_; -}; - -TEST_F(ServerTestWithAI_PASSIVE, GetMethod200) -{ - auto res = cli_.get("/hi"); - ASSERT_TRUE(res != nullptr); - EXPECT_EQ(200, res->status); - EXPECT_EQ("text/plain", res->get_header_value("Content-Type")); - EXPECT_EQ("Hello World!", res->body); -} - -#ifdef CPPHTTPLIB_OPENSSL_SUPPORT -TEST(SSLClientTest, ServerNameIndication) -{ - SSLClient cli("httpbin.org", 443); - auto res = cli.get("/get"); - ASSERT_TRUE(res != nullptr); - ASSERT_EQ(200, res->status); -} -#endif - -#ifdef _WIN32 -TEST(CleanupTest, WSACleanup) -{ - int ret = WSACleanup(); - ASSERT_EQ(0, ret); -} -#endif - -// vim: et ts=4 sw=4 cin cino={1s ff=unix diff --git a/src/3rdparty/cpp-httplib/test/test.sln b/src/3rdparty/cpp-httplib/test/test.sln deleted file mode 100644 index 8377dd7f..00000000 --- a/src/3rdparty/cpp-httplib/test/test.sln +++ /dev/null @@ -1,28 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Express 2013 for Windows Desktop -VisualStudioVersion = 12.0.20617.1 PREVIEW -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test", "test.vcxproj", "{6B3E6769-052D-4BC0-9D2C-E9127C3DBB26}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Debug|x64 = Debug|x64 - Release|Win32 = Release|Win32 - Release|x64 = Release|x64 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {6B3E6769-052D-4BC0-9D2C-E9127C3DBB26}.Debug|Win32.ActiveCfg = Debug|Win32 - {6B3E6769-052D-4BC0-9D2C-E9127C3DBB26}.Debug|Win32.Build.0 = Debug|Win32 - {6B3E6769-052D-4BC0-9D2C-E9127C3DBB26}.Debug|x64.ActiveCfg = Debug|x64 - {6B3E6769-052D-4BC0-9D2C-E9127C3DBB26}.Debug|x64.Build.0 = Debug|x64 - {6B3E6769-052D-4BC0-9D2C-E9127C3DBB26}.Release|Win32.ActiveCfg = Release|Win32 - {6B3E6769-052D-4BC0-9D2C-E9127C3DBB26}.Release|Win32.Build.0 = Release|Win32 - {6B3E6769-052D-4BC0-9D2C-E9127C3DBB26}.Release|x64.ActiveCfg = Release|x64 - {6B3E6769-052D-4BC0-9D2C-E9127C3DBB26}.Release|x64.Build.0 = Release|x64 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/src/3rdparty/cpp-httplib/test/test.vcxproj b/src/3rdparty/cpp-httplib/test/test.vcxproj deleted file mode 100644 index b19fd4c3..00000000 --- a/src/3rdparty/cpp-httplib/test/test.vcxproj +++ /dev/null @@ -1,157 +0,0 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - {6B3E6769-052D-4BC0-9D2C-E9127C3DBB26} - Win32Proj - test - - - - Application - true - v140 - Unicode - - - Application - true - v140 - Unicode - - - Application - false - v140 - true - Unicode - - - Application - false - v140 - true - Unicode - - - - - - - - - - - - - - - - - - - true - - - true - - - false - - - false - - - - - - Level3 - Disabled - WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) - ./;../ - - - Console - true - Ws2_32.lib;%(AdditionalDependencies) - - - - - - - Level3 - Disabled - WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) - ./;../ - - - Console - true - Ws2_32.lib;%(AdditionalDependencies) - - - - - Level3 - - - MaxSpeed - true - true - WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) - ./;../ - - - Console - true - true - true - Ws2_32.lib;%(AdditionalDependencies) - - - - - Level3 - - - MaxSpeed - true - true - WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) - ./;../ - - - Console - true - true - true - Ws2_32.lib;%(AdditionalDependencies) - - - - - - - - - - - \ No newline at end of file diff --git a/src/3rdparty/cpp-httplib/test/test.xcodeproj/project.pbxproj b/src/3rdparty/cpp-httplib/test/test.xcodeproj/project.pbxproj deleted file mode 100644 index abff30ef..00000000 --- a/src/3rdparty/cpp-httplib/test/test.xcodeproj/project.pbxproj +++ /dev/null @@ -1,248 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - BAA4BF2517280236003EF6AD /* test.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAA4BF2417280236003EF6AD /* test.cc */; }; - BAF46809172813BB0069D928 /* gtest-all.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAF46806172813BB0069D928 /* gtest-all.cc */; }; - BAF4680A172813BB0069D928 /* gtest_main.cc in Sources */ = {isa = PBXBuildFile; fileRef = BAF46808172813BB0069D928 /* gtest_main.cc */; }; -/* End PBXBuildFile section */ - -/* Begin PBXCopyFilesBuildPhase section */ - BAE5F9C51727F08F001D0075 /* CopyFiles */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = /usr/share/man/man1/; - dstSubfolderSpec = 0; - files = ( - ); - runOnlyForDeploymentPostprocessing = 1; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - BAA4BF2417280236003EF6AD /* test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = test.cc; sourceTree = SOURCE_ROOT; }; - BAE5F9C71727F08F001D0075 /* test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = test; sourceTree = BUILT_PRODUCTS_DIR; }; - BAF46806172813BB0069D928 /* gtest-all.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "gtest-all.cc"; sourceTree = ""; }; - BAF46807172813BB0069D928 /* gtest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = gtest.h; sourceTree = ""; }; - BAF46808172813BB0069D928 /* gtest_main.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = gtest_main.cc; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - BAE5F9C41727F08F001D0075 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - BAE5F9BE1727F08F001D0075 = { - isa = PBXGroup; - children = ( - BAE5F9C91727F08F001D0075 /* test */, - BAE5F9C81727F08F001D0075 /* Products */, - ); - sourceTree = ""; - }; - BAE5F9C81727F08F001D0075 /* Products */ = { - isa = PBXGroup; - children = ( - BAE5F9C71727F08F001D0075 /* test */, - ); - name = Products; - sourceTree = ""; - }; - BAE5F9C91727F08F001D0075 /* test */ = { - isa = PBXGroup; - children = ( - BAF46805172813BB0069D928 /* gtest */, - BAA4BF2417280236003EF6AD /* test.cc */, - ); - path = test; - sourceTree = ""; - }; - BAF46805172813BB0069D928 /* gtest */ = { - isa = PBXGroup; - children = ( - BAF46806172813BB0069D928 /* gtest-all.cc */, - BAF46807172813BB0069D928 /* gtest.h */, - BAF46808172813BB0069D928 /* gtest_main.cc */, - ); - path = gtest; - sourceTree = SOURCE_ROOT; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - BAE5F9C61727F08F001D0075 /* test */ = { - isa = PBXNativeTarget; - buildConfigurationList = BAE5F9D01727F08F001D0075 /* Build configuration list for PBXNativeTarget "test" */; - buildPhases = ( - BAE5F9C31727F08F001D0075 /* Sources */, - BAE5F9C41727F08F001D0075 /* Frameworks */, - BAE5F9C51727F08F001D0075 /* CopyFiles */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = test; - productName = test; - productReference = BAE5F9C71727F08F001D0075 /* test */; - productType = "com.apple.product-type.tool"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - BAE5F9BF1727F08F001D0075 /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 0460; - ORGANIZATIONNAME = "Yuji Hirose"; - }; - buildConfigurationList = BAE5F9C21727F08F001D0075 /* Build configuration list for PBXProject "test" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; - hasScannedForEncodings = 0; - knownRegions = ( - en, - ); - mainGroup = BAE5F9BE1727F08F001D0075; - productRefGroup = BAE5F9C81727F08F001D0075 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - BAE5F9C61727F08F001D0075 /* test */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXSourcesBuildPhase section */ - BAE5F9C31727F08F001D0075 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - BAA4BF2517280236003EF6AD /* test.cc in Sources */, - BAF46809172813BB0069D928 /* gtest-all.cc in Sources */, - BAF4680A172813BB0069D928 /* gtest_main.cc in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin XCBuildConfiguration section */ - BAE5F9CE1727F08F001D0075 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ARCHS = "$(ARCHS_STANDARD_64_BIT)"; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_OBJC_EXCEPTIONS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - GTEST_USE_OWN_TR1_TUPLE, - "DEBUG=1", - "$(inherited)", - ); - GCC_SYMBOLS_PRIVATE_EXTERN = NO; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - HEADER_SEARCH_PATHS = ( - "../**", - "./**", - ); - MACOSX_DEPLOYMENT_TARGET = 10.7; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = macosx; - USER_HEADER_SEARCH_PATHS = ""; - }; - name = Debug; - }; - BAE5F9CF1727F08F001D0075 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ARCHS = "$(ARCHS_STANDARD_64_BIT)"; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = YES; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_ENABLE_OBJC_EXCEPTIONS = YES; - GCC_PREPROCESSOR_DEFINITIONS = GTEST_USE_OWN_TR1_TUPLE; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - HEADER_SEARCH_PATHS = ( - "../**", - "./**", - ); - MACOSX_DEPLOYMENT_TARGET = 10.7; - SDKROOT = macosx; - USER_HEADER_SEARCH_PATHS = ""; - }; - name = Release; - }; - BAE5F9D11727F08F001D0075 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Debug; - }; - BAE5F9D21727F08F001D0075 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - BAE5F9C21727F08F001D0075 /* Build configuration list for PBXProject "test" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - BAE5F9CE1727F08F001D0075 /* Debug */, - BAE5F9CF1727F08F001D0075 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - BAE5F9D01727F08F001D0075 /* Build configuration list for PBXNativeTarget "test" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - BAE5F9D11727F08F001D0075 /* Debug */, - BAE5F9D21727F08F001D0075 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = BAE5F9BF1727F08F001D0075 /* Project object */; -} diff --git a/src/3rdparty/cpp-httplib/test/test.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/src/3rdparty/cpp-httplib/test/test.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index d683bc92..00000000 --- a/src/3rdparty/cpp-httplib/test/test.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/src/3rdparty/cpp-httplib/test/www/dir/index.html b/src/3rdparty/cpp-httplib/test/www/dir/index.html deleted file mode 100644 index be3c05f7..00000000 --- a/src/3rdparty/cpp-httplib/test/www/dir/index.html +++ /dev/null @@ -1,8 +0,0 @@ - - - - - Test - hi - - diff --git a/src/3rdparty/cpp-httplib/test/www/dir/test.html b/src/3rdparty/cpp-httplib/test/www/dir/test.html deleted file mode 100644 index 6d70cd0e..00000000 --- a/src/3rdparty/cpp-httplib/test/www/dir/test.html +++ /dev/null @@ -1 +0,0 @@ -test.html \ No newline at end of file diff --git a/src/3rdparty/http-parser/AUTHORS b/src/3rdparty/http-parser/AUTHORS new file mode 100644 index 00000000..5323b685 --- /dev/null +++ b/src/3rdparty/http-parser/AUTHORS @@ -0,0 +1,68 @@ +# Authors ordered by first contribution. +Ryan Dahl +Jeremy Hinegardner +Sergey Shepelev +Joe Damato +tomika +Phoenix Sol +Cliff Frey +Ewen Cheslack-Postava +Santiago Gala +Tim Becker +Jeff Terrace +Ben Noordhuis +Nathan Rajlich +Mark Nottingham +Aman Gupta +Tim Becker +Sean Cunningham +Peter Griess +Salman Haq +Cliff Frey +Jon Kolb +Fouad Mardini +Paul Querna +Felix Geisendörfer +koichik +Andre Caron +Ivo Raisr +James McLaughlin +David Gwynne +Thomas LE ROUX +Randy Rizun +Andre Louis Caron +Simon Zimmermann +Erik Dubbelboer +Martell Malone +Bertrand Paquet +BogDan Vatra +Peter Faiman +Corey Richardson +Tóth Tamás +Cam Swords +Chris Dickinson +Uli Köhler +Charlie Somerville +Patrik Stutz +Fedor Indutny +runner +Alexis Campailla +David Wragg +Vinnie Falco +Alex Butum +Rex Feng +Alex Kocharin +Mark Koopman +Helge Heß +Alexis La Goutte +George Miroshnykov +Maciej Małecki +Marc O'Morain +Jeff Pinner +Timothy J Fontaine +Akagi201 +Romain Giraud +Jay Satiro +Arne Steen +Kjell Schubert +Olivier Mengué diff --git a/src/3rdparty/http-parser/LICENSE-MIT b/src/3rdparty/http-parser/LICENSE-MIT new file mode 100644 index 00000000..1ec0ab4e --- /dev/null +++ b/src/3rdparty/http-parser/LICENSE-MIT @@ -0,0 +1,19 @@ +Copyright Joyent, Inc. and other Node contributors. + +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. diff --git a/src/3rdparty/http-parser/README.md b/src/3rdparty/http-parser/README.md new file mode 100644 index 00000000..b265d717 --- /dev/null +++ b/src/3rdparty/http-parser/README.md @@ -0,0 +1,246 @@ +HTTP Parser +=========== + +[![Build Status](https://api.travis-ci.org/nodejs/http-parser.svg?branch=master)](https://travis-ci.org/nodejs/http-parser) + +This is a parser for HTTP messages written in C. It parses both requests and +responses. The parser is designed to be used in performance HTTP +applications. It does not make any syscalls nor allocations, it does not +buffer data, it can be interrupted at anytime. Depending on your +architecture, it only requires about 40 bytes of data per message +stream (in a web server that is per connection). + +Features: + + * No dependencies + * Handles persistent streams (keep-alive). + * Decodes chunked encoding. + * Upgrade support + * Defends against buffer overflow attacks. + +The parser extracts the following information from HTTP messages: + + * Header fields and values + * Content-Length + * Request method + * Response status code + * Transfer-Encoding + * HTTP version + * Request URL + * Message body + + +Usage +----- + +One `http_parser` object is used per TCP connection. Initialize the struct +using `http_parser_init()` and set the callbacks. That might look something +like this for a request parser: +```c +http_parser_settings settings; +settings.on_url = my_url_callback; +settings.on_header_field = my_header_field_callback; +/* ... */ + +http_parser *parser = malloc(sizeof(http_parser)); +http_parser_init(parser, HTTP_REQUEST); +parser->data = my_socket; +``` + +When data is received on the socket execute the parser and check for errors. + +```c +size_t len = 80*1024, nparsed; +char buf[len]; +ssize_t recved; + +recved = recv(fd, buf, len, 0); + +if (recved < 0) { + /* Handle error. */ +} + +/* Start up / continue the parser. + * Note we pass recved==0 to signal that EOF has been received. + */ +nparsed = http_parser_execute(parser, &settings, buf, recved); + +if (parser->upgrade) { + /* handle new protocol */ +} else if (nparsed != recved) { + /* Handle error. Usually just close the connection. */ +} +``` + +`http_parser` needs to know where the end of the stream is. For example, sometimes +servers send responses without Content-Length and expect the client to +consume input (for the body) until EOF. To tell `http_parser` about EOF, give +`0` as the fourth parameter to `http_parser_execute()`. Callbacks and errors +can still be encountered during an EOF, so one must still be prepared +to receive them. + +Scalar valued message information such as `status_code`, `method`, and the +HTTP version are stored in the parser structure. This data is only +temporally stored in `http_parser` and gets reset on each new message. If +this information is needed later, copy it out of the structure during the +`headers_complete` callback. + +The parser decodes the transfer-encoding for both requests and responses +transparently. That is, a chunked encoding is decoded before being sent to +the on_body callback. + + +The Special Problem of Upgrade +------------------------------ + +`http_parser` supports upgrading the connection to a different protocol. An +increasingly common example of this is the WebSocket protocol which sends +a request like + + GET /demo HTTP/1.1 + Upgrade: WebSocket + Connection: Upgrade + Host: example.com + Origin: http://example.com + WebSocket-Protocol: sample + +followed by non-HTTP data. + +(See [RFC6455](https://tools.ietf.org/html/rfc6455) for more information the +WebSocket protocol.) + +To support this, the parser will treat this as a normal HTTP message without a +body, issuing both on_headers_complete and on_message_complete callbacks. However +http_parser_execute() will stop parsing at the end of the headers and return. + +The user is expected to check if `parser->upgrade` has been set to 1 after +`http_parser_execute()` returns. Non-HTTP data begins at the buffer supplied +offset by the return value of `http_parser_execute()`. + + +Callbacks +--------- + +During the `http_parser_execute()` call, the callbacks set in +`http_parser_settings` will be executed. The parser maintains state and +never looks behind, so buffering the data is not necessary. If you need to +save certain data for later usage, you can do that from the callbacks. + +There are two types of callbacks: + +* notification `typedef int (*http_cb) (http_parser*);` + Callbacks: on_message_begin, on_headers_complete, on_message_complete. +* data `typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);` + Callbacks: (requests only) on_url, + (common) on_header_field, on_header_value, on_body; + +Callbacks must return 0 on success. Returning a non-zero value indicates +error to the parser, making it exit immediately. + +For cases where it is necessary to pass local information to/from a callback, +the `http_parser` object's `data` field can be used. +An example of such a case is when using threads to handle a socket connection, +parse a request, and then give a response over that socket. By instantiation +of a thread-local struct containing relevant data (e.g. accepted socket, +allocated memory for callbacks to write into, etc), a parser's callbacks are +able to communicate data between the scope of the thread and the scope of the +callback in a threadsafe manner. This allows `http_parser` to be used in +multi-threaded contexts. + +Example: +```c + typedef struct { + socket_t sock; + void* buffer; + int buf_len; + } custom_data_t; + + +int my_url_callback(http_parser* parser, const char *at, size_t length) { + /* access to thread local custom_data_t struct. + Use this access save parsed data for later use into thread local + buffer, or communicate over socket + */ + parser->data; + ... + return 0; +} + +... + +void http_parser_thread(socket_t sock) { + int nparsed = 0; + /* allocate memory for user data */ + custom_data_t *my_data = malloc(sizeof(custom_data_t)); + + /* some information for use by callbacks. + * achieves thread -> callback information flow */ + my_data->sock = sock; + + /* instantiate a thread-local parser */ + http_parser *parser = malloc(sizeof(http_parser)); + http_parser_init(parser, HTTP_REQUEST); /* initialise parser */ + /* this custom data reference is accessible through the reference to the + parser supplied to callback functions */ + parser->data = my_data; + + http_parser_settings settings; /* set up callbacks */ + settings.on_url = my_url_callback; + + /* execute parser */ + nparsed = http_parser_execute(parser, &settings, buf, recved); + + ... + /* parsed information copied from callback. + can now perform action on data copied into thread-local memory from callbacks. + achieves callback -> thread information flow */ + my_data->buffer; + ... +} + +``` + +In case you parse HTTP message in chunks (i.e. `read()` request line +from socket, parse, read half headers, parse, etc) your data callbacks +may be called more than once. `http_parser` guarantees that data pointer is only +valid for the lifetime of callback. You can also `read()` into a heap allocated +buffer to avoid copying memory around if this fits your application. + +Reading headers may be a tricky task if you read/parse headers partially. +Basically, you need to remember whether last header callback was field or value +and apply the following logic: + + (on_header_field and on_header_value shortened to on_h_*) + ------------------------ ------------ -------------------------------------------- + | State (prev. callback) | Callback | Description/action | + ------------------------ ------------ -------------------------------------------- + | nothing (first call) | on_h_field | Allocate new buffer and copy callback data | + | | | into it | + ------------------------ ------------ -------------------------------------------- + | value | on_h_field | New header started. | + | | | Copy current name,value buffers to headers | + | | | list and allocate new buffer for new name | + ------------------------ ------------ -------------------------------------------- + | field | on_h_field | Previous name continues. Reallocate name | + | | | buffer and append callback data to it | + ------------------------ ------------ -------------------------------------------- + | field | on_h_value | Value for current header started. Allocate | + | | | new buffer and copy callback data to it | + ------------------------ ------------ -------------------------------------------- + | value | on_h_value | Value continues. Reallocate value buffer | + | | | and append callback data to it | + ------------------------ ------------ -------------------------------------------- + + +Parsing URLs +------------ + +A simplistic zero-copy URL parser is provided as `http_parser_parse_url()`. +Users of this library may wish to use it to parse URLs constructed from +consecutive `on_url` callbacks. + +See examples of reading in headers: + +* [partial example](http://gist.github.com/155877) in C +* [from http-parser tests](http://github.com/joyent/http-parser/blob/37a0ff8/test.c#L403) in C +* [from Node library](http://github.com/joyent/node/blob/842eaf4/src/http.js#L284) in Javascript diff --git a/src/3rdparty/http-parser/http_parser.c b/src/3rdparty/http-parser/http_parser.c new file mode 100644 index 00000000..e2fc5d2e --- /dev/null +++ b/src/3rdparty/http-parser/http_parser.c @@ -0,0 +1,2501 @@ +/* Copyright Joyent, Inc. and other Node contributors. + * + * 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. + */ +#include "http_parser.h" +#include +#include +#include +#include +#include + +static uint32_t max_header_size = HTTP_MAX_HEADER_SIZE; + +#ifndef ULLONG_MAX +# define ULLONG_MAX ((uint64_t) -1) /* 2^64-1 */ +#endif + +#ifndef MIN +# define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +#ifndef ARRAY_SIZE +# define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) +#endif + +#ifndef BIT_AT +# define BIT_AT(a, i) \ + (!!((unsigned int) (a)[(unsigned int) (i) >> 3] & \ + (1 << ((unsigned int) (i) & 7)))) +#endif + +#ifndef ELEM_AT +# define ELEM_AT(a, i, v) ((unsigned int) (i) < ARRAY_SIZE(a) ? (a)[(i)] : (v)) +#endif + +#define SET_ERRNO(e) \ +do { \ + parser->nread = nread; \ + parser->http_errno = (e); \ +} while(0) + +#define CURRENT_STATE() p_state +#define UPDATE_STATE(V) p_state = (enum state) (V); +#define RETURN(V) \ +do { \ + parser->nread = nread; \ + parser->state = CURRENT_STATE(); \ + return (V); \ +} while (0); +#define REEXECUTE() \ + goto reexecute; \ + + +#ifdef __GNUC__ +# define LIKELY(X) __builtin_expect(!!(X), 1) +# define UNLIKELY(X) __builtin_expect(!!(X), 0) +#else +# define LIKELY(X) (X) +# define UNLIKELY(X) (X) +#endif + + +/* Run the notify callback FOR, returning ER if it fails */ +#define CALLBACK_NOTIFY_(FOR, ER) \ +do { \ + assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \ + \ + if (LIKELY(settings->on_##FOR)) { \ + parser->state = CURRENT_STATE(); \ + if (UNLIKELY(0 != settings->on_##FOR(parser))) { \ + SET_ERRNO(HPE_CB_##FOR); \ + } \ + UPDATE_STATE(parser->state); \ + \ + /* We either errored above or got paused; get out */ \ + if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) { \ + return (ER); \ + } \ + } \ +} while (0) + +/* Run the notify callback FOR and consume the current byte */ +#define CALLBACK_NOTIFY(FOR) CALLBACK_NOTIFY_(FOR, p - data + 1) + +/* Run the notify callback FOR and don't consume the current byte */ +#define CALLBACK_NOTIFY_NOADVANCE(FOR) CALLBACK_NOTIFY_(FOR, p - data) + +/* Run data callback FOR with LEN bytes, returning ER if it fails */ +#define CALLBACK_DATA_(FOR, LEN, ER) \ +do { \ + assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \ + \ + if (FOR##_mark) { \ + if (LIKELY(settings->on_##FOR)) { \ + parser->state = CURRENT_STATE(); \ + if (UNLIKELY(0 != \ + settings->on_##FOR(parser, FOR##_mark, (LEN)))) { \ + SET_ERRNO(HPE_CB_##FOR); \ + } \ + UPDATE_STATE(parser->state); \ + \ + /* We either errored above or got paused; get out */ \ + if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) { \ + return (ER); \ + } \ + } \ + FOR##_mark = NULL; \ + } \ +} while (0) + +/* Run the data callback FOR and consume the current byte */ +#define CALLBACK_DATA(FOR) \ + CALLBACK_DATA_(FOR, p - FOR##_mark, p - data + 1) + +/* Run the data callback FOR and don't consume the current byte */ +#define CALLBACK_DATA_NOADVANCE(FOR) \ + CALLBACK_DATA_(FOR, p - FOR##_mark, p - data) + +/* Set the mark FOR; non-destructive if mark is already set */ +#define MARK(FOR) \ +do { \ + if (!FOR##_mark) { \ + FOR##_mark = p; \ + } \ +} while (0) + +/* Don't allow the total size of the HTTP headers (including the status + * line) to exceed max_header_size. This check is here to protect + * embedders against denial-of-service attacks where the attacker feeds + * us a never-ending header that the embedder keeps buffering. + * + * This check is arguably the responsibility of embedders but we're doing + * it on the embedder's behalf because most won't bother and this way we + * make the web a little safer. max_header_size is still far bigger + * than any reasonable request or response so this should never affect + * day-to-day operation. + */ +#define COUNT_HEADER_SIZE(V) \ +do { \ + nread += (uint32_t)(V); \ + if (UNLIKELY(nread > max_header_size)) { \ + SET_ERRNO(HPE_HEADER_OVERFLOW); \ + goto error; \ + } \ +} while (0) + + +#define PROXY_CONNECTION "proxy-connection" +#define CONNECTION "connection" +#define CONTENT_LENGTH "content-length" +#define TRANSFER_ENCODING "transfer-encoding" +#define UPGRADE "upgrade" +#define CHUNKED "chunked" +#define KEEP_ALIVE "keep-alive" +#define CLOSE "close" + + +static const char *method_strings[] = + { +#define XX(num, name, string) #string, + HTTP_METHOD_MAP(XX) +#undef XX + }; + + +/* Tokens as defined by rfc 2616. Also lowercases them. + * token = 1* + * separators = "(" | ")" | "<" | ">" | "@" + * | "," | ";" | ":" | "\" | <"> + * | "/" | "[" | "]" | "?" | "=" + * | "{" | "}" | SP | HT + */ +static const char tokens[256] = { +/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */ + 0, 0, 0, 0, 0, 0, 0, 0, +/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */ + 0, 0, 0, 0, 0, 0, 0, 0, +/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */ + 0, 0, 0, 0, 0, 0, 0, 0, +/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */ + 0, 0, 0, 0, 0, 0, 0, 0, +/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */ + ' ', '!', 0, '#', '$', '%', '&', '\'', +/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */ + 0, 0, '*', '+', 0, '-', '.', 0, +/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */ + '0', '1', '2', '3', '4', '5', '6', '7', +/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */ + '8', '9', 0, 0, 0, 0, 0, 0, +/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */ + 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', +/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */ + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', +/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */ + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', +/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */ + 'x', 'y', 'z', 0, 0, 0, '^', '_', +/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */ + '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', +/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */ + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', +/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */ + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', +/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */ + 'x', 'y', 'z', 0, '|', 0, '~', 0 }; + + +static const int8_t unhex[256] = + {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + , 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1 + ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + }; + + +#if HTTP_PARSER_STRICT +# define T(v) 0 +#else +# define T(v) v +#endif + + +static const uint8_t normal_url_char[32] = { +/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */ + 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, +/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */ + 0 | T(2) | 0 | 0 | T(16) | 0 | 0 | 0, +/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */ + 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, +/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */ + 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, +/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */ + 0 | 2 | 4 | 0 | 16 | 32 | 64 | 128, +/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, +/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, }; + +#undef T + +enum state + { s_dead = 1 /* important that this is > 0 */ + + , s_start_req_or_res + , s_res_or_resp_H + , s_start_res + , s_res_H + , s_res_HT + , s_res_HTT + , s_res_HTTP + , s_res_http_major + , s_res_http_dot + , s_res_http_minor + , s_res_http_end + , s_res_first_status_code + , s_res_status_code + , s_res_status_start + , s_res_status + , s_res_line_almost_done + + , s_start_req + + , s_req_method + , s_req_spaces_before_url + , s_req_schema + , s_req_schema_slash + , s_req_schema_slash_slash + , s_req_server_start + , s_req_server + , s_req_server_with_at + , s_req_path + , s_req_query_string_start + , s_req_query_string + , s_req_fragment_start + , s_req_fragment + , s_req_http_start + , s_req_http_H + , s_req_http_HT + , s_req_http_HTT + , s_req_http_HTTP + , s_req_http_I + , s_req_http_IC + , s_req_http_major + , s_req_http_dot + , s_req_http_minor + , s_req_http_end + , s_req_line_almost_done + + , s_header_field_start + , s_header_field + , s_header_value_discard_ws + , s_header_value_discard_ws_almost_done + , s_header_value_discard_lws + , s_header_value_start + , s_header_value + , s_header_value_lws + + , s_header_almost_done + + , s_chunk_size_start + , s_chunk_size + , s_chunk_parameters + , s_chunk_size_almost_done + + , s_headers_almost_done + , s_headers_done + + /* Important: 's_headers_done' must be the last 'header' state. All + * states beyond this must be 'body' states. It is used for overflow + * checking. See the PARSING_HEADER() macro. + */ + + , s_chunk_data + , s_chunk_data_almost_done + , s_chunk_data_done + + , s_body_identity + , s_body_identity_eof + + , s_message_done + }; + + +#define PARSING_HEADER(state) (state <= s_headers_done) + + +enum header_states + { h_general = 0 + , h_C + , h_CO + , h_CON + + , h_matching_connection + , h_matching_proxy_connection + , h_matching_content_length + , h_matching_transfer_encoding + , h_matching_upgrade + + , h_connection + , h_content_length + , h_content_length_num + , h_content_length_ws + , h_transfer_encoding + , h_upgrade + + , h_matching_transfer_encoding_chunked + , h_matching_connection_token_start + , h_matching_connection_keep_alive + , h_matching_connection_close + , h_matching_connection_upgrade + , h_matching_connection_token + + , h_transfer_encoding_chunked + , h_connection_keep_alive + , h_connection_close + , h_connection_upgrade + }; + +enum http_host_state + { + s_http_host_dead = 1 + , s_http_userinfo_start + , s_http_userinfo + , s_http_host_start + , s_http_host_v6_start + , s_http_host + , s_http_host_v6 + , s_http_host_v6_end + , s_http_host_v6_zone_start + , s_http_host_v6_zone + , s_http_host_port_start + , s_http_host_port +}; + +/* Macros for character classes; depends on strict-mode */ +#define CR '\r' +#define LF '\n' +#define LOWER(c) (unsigned char)(c | 0x20) +#define IS_ALPHA(c) (LOWER(c) >= 'a' && LOWER(c) <= 'z') +#define IS_NUM(c) ((c) >= '0' && (c) <= '9') +#define IS_ALPHANUM(c) (IS_ALPHA(c) || IS_NUM(c)) +#define IS_HEX(c) (IS_NUM(c) || (LOWER(c) >= 'a' && LOWER(c) <= 'f')) +#define IS_MARK(c) ((c) == '-' || (c) == '_' || (c) == '.' || \ + (c) == '!' || (c) == '~' || (c) == '*' || (c) == '\'' || (c) == '(' || \ + (c) == ')') +#define IS_USERINFO_CHAR(c) (IS_ALPHANUM(c) || IS_MARK(c) || (c) == '%' || \ + (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \ + (c) == '$' || (c) == ',') + +#define STRICT_TOKEN(c) ((c == ' ') ? 0 : tokens[(unsigned char)c]) + +#if HTTP_PARSER_STRICT +#define TOKEN(c) STRICT_TOKEN(c) +#define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c)) +#define IS_HOST_CHAR(c) (IS_ALPHANUM(c) || (c) == '.' || (c) == '-') +#else +#define TOKEN(c) tokens[(unsigned char)c] +#define IS_URL_CHAR(c) \ + (BIT_AT(normal_url_char, (unsigned char)c) || ((c) & 0x80)) +#define IS_HOST_CHAR(c) \ + (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_') +#endif + +/** + * Verify that a char is a valid visible (printable) US-ASCII + * character or %x80-FF + **/ +#define IS_HEADER_CHAR(ch) \ + (ch == CR || ch == LF || ch == 9 || ((unsigned char)ch > 31 && ch != 127)) + +#define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res) + + +#if HTTP_PARSER_STRICT +# define STRICT_CHECK(cond) \ +do { \ + if (cond) { \ + SET_ERRNO(HPE_STRICT); \ + goto error; \ + } \ +} while (0) +# define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead) +#else +# define STRICT_CHECK(cond) +# define NEW_MESSAGE() start_state +#endif + + +/* Map errno values to strings for human-readable output */ +#define HTTP_STRERROR_GEN(n, s) { "HPE_" #n, s }, +static struct { + const char *name; + const char *description; +} http_strerror_tab[] = { + HTTP_ERRNO_MAP(HTTP_STRERROR_GEN) +}; +#undef HTTP_STRERROR_GEN + +int http_message_needs_eof(const http_parser *parser); + +/* Our URL parser. + * + * This is designed to be shared by http_parser_execute() for URL validation, + * hence it has a state transition + byte-for-byte interface. In addition, it + * is meant to be embedded in http_parser_parse_url(), which does the dirty + * work of turning state transitions URL components for its API. + * + * This function should only be invoked with non-space characters. It is + * assumed that the caller cares about (and can detect) the transition between + * URL and non-URL states by looking for these. + */ +static enum state +parse_url_char(enum state s, const char ch) +{ + if (ch == ' ' || ch == '\r' || ch == '\n') { + return s_dead; + } + +#if HTTP_PARSER_STRICT + if (ch == '\t' || ch == '\f') { + return s_dead; + } +#endif + + switch (s) { + case s_req_spaces_before_url: + /* Proxied requests are followed by scheme of an absolute URI (alpha). + * All methods except CONNECT are followed by '/' or '*'. + */ + + if (ch == '/' || ch == '*') { + return s_req_path; + } + + if (IS_ALPHA(ch)) { + return s_req_schema; + } + + break; + + case s_req_schema: + if (IS_ALPHA(ch)) { + return s; + } + + if (ch == ':') { + return s_req_schema_slash; + } + + break; + + case s_req_schema_slash: + if (ch == '/') { + return s_req_schema_slash_slash; + } + + break; + + case s_req_schema_slash_slash: + if (ch == '/') { + return s_req_server_start; + } + + break; + + case s_req_server_with_at: + if (ch == '@') { + return s_dead; + } + + /* fall through */ + case s_req_server_start: + case s_req_server: + if (ch == '/') { + return s_req_path; + } + + if (ch == '?') { + return s_req_query_string_start; + } + + if (ch == '@') { + return s_req_server_with_at; + } + + if (IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') { + return s_req_server; + } + + break; + + case s_req_path: + if (IS_URL_CHAR(ch)) { + return s; + } + + switch (ch) { + case '?': + return s_req_query_string_start; + + case '#': + return s_req_fragment_start; + } + + break; + + case s_req_query_string_start: + case s_req_query_string: + if (IS_URL_CHAR(ch)) { + return s_req_query_string; + } + + switch (ch) { + case '?': + /* allow extra '?' in query string */ + return s_req_query_string; + + case '#': + return s_req_fragment_start; + } + + break; + + case s_req_fragment_start: + if (IS_URL_CHAR(ch)) { + return s_req_fragment; + } + + switch (ch) { + case '?': + return s_req_fragment; + + case '#': + return s; + } + + break; + + case s_req_fragment: + if (IS_URL_CHAR(ch)) { + return s; + } + + switch (ch) { + case '?': + case '#': + return s; + } + + break; + + default: + break; + } + + /* We should never fall out of the switch above unless there's an error */ + return s_dead; +} + +size_t http_parser_execute (http_parser *parser, + const http_parser_settings *settings, + const char *data, + size_t len) +{ + char c, ch; + int8_t unhex_val; + const char *p = data; + const char *header_field_mark = 0; + const char *header_value_mark = 0; + const char *url_mark = 0; + const char *body_mark = 0; + const char *status_mark = 0; + enum state p_state = (enum state) parser->state; + const unsigned int lenient = parser->lenient_http_headers; + uint32_t nread = parser->nread; + + /* We're in an error state. Don't bother doing anything. */ + if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { + return 0; + } + + if (len == 0) { + switch (CURRENT_STATE()) { + case s_body_identity_eof: + /* Use of CALLBACK_NOTIFY() here would erroneously return 1 byte read if + * we got paused. + */ + CALLBACK_NOTIFY_NOADVANCE(message_complete); + return 0; + + case s_dead: + case s_start_req_or_res: + case s_start_res: + case s_start_req: + return 0; + + default: + SET_ERRNO(HPE_INVALID_EOF_STATE); + return 1; + } + } + + + if (CURRENT_STATE() == s_header_field) + header_field_mark = data; + if (CURRENT_STATE() == s_header_value) + header_value_mark = data; + switch (CURRENT_STATE()) { + case s_req_path: + case s_req_schema: + case s_req_schema_slash: + case s_req_schema_slash_slash: + case s_req_server_start: + case s_req_server: + case s_req_server_with_at: + case s_req_query_string_start: + case s_req_query_string: + case s_req_fragment_start: + case s_req_fragment: + url_mark = data; + break; + case s_res_status: + status_mark = data; + break; + default: + break; + } + + for (p=data; p != data + len; p++) { + ch = *p; + + if (PARSING_HEADER(CURRENT_STATE())) + COUNT_HEADER_SIZE(1); + +reexecute: + switch (CURRENT_STATE()) { + + case s_dead: + /* this state is used after a 'Connection: close' message + * the parser will error out if it reads another message + */ + if (LIKELY(ch == CR || ch == LF)) + break; + + SET_ERRNO(HPE_CLOSED_CONNECTION); + goto error; + + case s_start_req_or_res: + { + if (ch == CR || ch == LF) + break; + parser->flags = 0; + parser->content_length = ULLONG_MAX; + + if (ch == 'H') { + UPDATE_STATE(s_res_or_resp_H); + + CALLBACK_NOTIFY(message_begin); + } else { + parser->type = HTTP_REQUEST; + UPDATE_STATE(s_start_req); + REEXECUTE(); + } + + break; + } + + case s_res_or_resp_H: + if (ch == 'T') { + parser->type = HTTP_RESPONSE; + UPDATE_STATE(s_res_HT); + } else { + if (UNLIKELY(ch != 'E')) { + SET_ERRNO(HPE_INVALID_CONSTANT); + goto error; + } + + parser->type = HTTP_REQUEST; + parser->method = HTTP_HEAD; + parser->index = 2; + UPDATE_STATE(s_req_method); + } + break; + + case s_start_res: + { + if (ch == CR || ch == LF) + break; + parser->flags = 0; + parser->content_length = ULLONG_MAX; + + if (ch == 'H') { + UPDATE_STATE(s_res_H); + } else { + SET_ERRNO(HPE_INVALID_CONSTANT); + goto error; + } + + CALLBACK_NOTIFY(message_begin); + break; + } + + case s_res_H: + STRICT_CHECK(ch != 'T'); + UPDATE_STATE(s_res_HT); + break; + + case s_res_HT: + STRICT_CHECK(ch != 'T'); + UPDATE_STATE(s_res_HTT); + break; + + case s_res_HTT: + STRICT_CHECK(ch != 'P'); + UPDATE_STATE(s_res_HTTP); + break; + + case s_res_HTTP: + STRICT_CHECK(ch != '/'); + UPDATE_STATE(s_res_http_major); + break; + + case s_res_http_major: + if (UNLIKELY(!IS_NUM(ch))) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + parser->http_major = ch - '0'; + UPDATE_STATE(s_res_http_dot); + break; + + case s_res_http_dot: + { + if (UNLIKELY(ch != '.')) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + UPDATE_STATE(s_res_http_minor); + break; + } + + case s_res_http_minor: + if (UNLIKELY(!IS_NUM(ch))) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + parser->http_minor = ch - '0'; + UPDATE_STATE(s_res_http_end); + break; + + case s_res_http_end: + { + if (UNLIKELY(ch != ' ')) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + UPDATE_STATE(s_res_first_status_code); + break; + } + + case s_res_first_status_code: + { + if (!IS_NUM(ch)) { + if (ch == ' ') { + break; + } + + SET_ERRNO(HPE_INVALID_STATUS); + goto error; + } + parser->status_code = ch - '0'; + UPDATE_STATE(s_res_status_code); + break; + } + + case s_res_status_code: + { + if (!IS_NUM(ch)) { + switch (ch) { + case ' ': + UPDATE_STATE(s_res_status_start); + break; + case CR: + case LF: + UPDATE_STATE(s_res_status_start); + REEXECUTE(); + break; + default: + SET_ERRNO(HPE_INVALID_STATUS); + goto error; + } + break; + } + + parser->status_code *= 10; + parser->status_code += ch - '0'; + + if (UNLIKELY(parser->status_code > 999)) { + SET_ERRNO(HPE_INVALID_STATUS); + goto error; + } + + break; + } + + case s_res_status_start: + { + MARK(status); + UPDATE_STATE(s_res_status); + parser->index = 0; + + if (ch == CR || ch == LF) + REEXECUTE(); + + break; + } + + case s_res_status: + if (ch == CR) { + UPDATE_STATE(s_res_line_almost_done); + CALLBACK_DATA(status); + break; + } + + if (ch == LF) { + UPDATE_STATE(s_header_field_start); + CALLBACK_DATA(status); + break; + } + + break; + + case s_res_line_almost_done: + STRICT_CHECK(ch != LF); + UPDATE_STATE(s_header_field_start); + break; + + case s_start_req: + { + if (ch == CR || ch == LF) + break; + parser->flags = 0; + parser->content_length = ULLONG_MAX; + + if (UNLIKELY(!IS_ALPHA(ch))) { + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + + parser->method = (enum http_method) 0; + parser->index = 1; + switch (ch) { + case 'A': parser->method = HTTP_ACL; break; + case 'B': parser->method = HTTP_BIND; break; + case 'C': parser->method = HTTP_CONNECT; /* or COPY, CHECKOUT */ break; + case 'D': parser->method = HTTP_DELETE; break; + case 'G': parser->method = HTTP_GET; break; + case 'H': parser->method = HTTP_HEAD; break; + case 'L': parser->method = HTTP_LOCK; /* or LINK */ break; + case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH, MKCALENDAR */ break; + case 'N': parser->method = HTTP_NOTIFY; break; + case 'O': parser->method = HTTP_OPTIONS; break; + case 'P': parser->method = HTTP_POST; + /* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */ + break; + case 'R': parser->method = HTTP_REPORT; /* or REBIND */ break; + case 'S': parser->method = HTTP_SUBSCRIBE; /* or SEARCH, SOURCE */ break; + case 'T': parser->method = HTTP_TRACE; break; + case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE, UNBIND, UNLINK */ break; + default: + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + UPDATE_STATE(s_req_method); + + CALLBACK_NOTIFY(message_begin); + + break; + } + + case s_req_method: + { + const char *matcher; + if (UNLIKELY(ch == '\0')) { + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + + matcher = method_strings[parser->method]; + if (ch == ' ' && matcher[parser->index] == '\0') { + UPDATE_STATE(s_req_spaces_before_url); + } else if (ch == matcher[parser->index]) { + ; /* nada */ + } else if ((ch >= 'A' && ch <= 'Z') || ch == '-') { + + switch (parser->method << 16 | parser->index << 8 | ch) { +#define XX(meth, pos, ch, new_meth) \ + case (HTTP_##meth << 16 | pos << 8 | ch): \ + parser->method = HTTP_##new_meth; break; + + XX(POST, 1, 'U', PUT) + XX(POST, 1, 'A', PATCH) + XX(POST, 1, 'R', PROPFIND) + XX(PUT, 2, 'R', PURGE) + XX(CONNECT, 1, 'H', CHECKOUT) + XX(CONNECT, 2, 'P', COPY) + XX(MKCOL, 1, 'O', MOVE) + XX(MKCOL, 1, 'E', MERGE) + XX(MKCOL, 1, '-', MSEARCH) + XX(MKCOL, 2, 'A', MKACTIVITY) + XX(MKCOL, 3, 'A', MKCALENDAR) + XX(SUBSCRIBE, 1, 'E', SEARCH) + XX(SUBSCRIBE, 1, 'O', SOURCE) + XX(REPORT, 2, 'B', REBIND) + XX(PROPFIND, 4, 'P', PROPPATCH) + XX(LOCK, 1, 'I', LINK) + XX(UNLOCK, 2, 'S', UNSUBSCRIBE) + XX(UNLOCK, 2, 'B', UNBIND) + XX(UNLOCK, 3, 'I', UNLINK) +#undef XX + default: + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + } else { + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + + ++parser->index; + break; + } + + case s_req_spaces_before_url: + { + if (ch == ' ') break; + + MARK(url); + if (parser->method == HTTP_CONNECT) { + UPDATE_STATE(s_req_server_start); + } + + UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch)); + if (UNLIKELY(CURRENT_STATE() == s_dead)) { + SET_ERRNO(HPE_INVALID_URL); + goto error; + } + + break; + } + + case s_req_schema: + case s_req_schema_slash: + case s_req_schema_slash_slash: + case s_req_server_start: + { + switch (ch) { + /* No whitespace allowed here */ + case ' ': + case CR: + case LF: + SET_ERRNO(HPE_INVALID_URL); + goto error; + default: + UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch)); + if (UNLIKELY(CURRENT_STATE() == s_dead)) { + SET_ERRNO(HPE_INVALID_URL); + goto error; + } + } + + break; + } + + case s_req_server: + case s_req_server_with_at: + case s_req_path: + case s_req_query_string_start: + case s_req_query_string: + case s_req_fragment_start: + case s_req_fragment: + { + switch (ch) { + case ' ': + UPDATE_STATE(s_req_http_start); + CALLBACK_DATA(url); + break; + case CR: + case LF: + parser->http_major = 0; + parser->http_minor = 9; + UPDATE_STATE((ch == CR) ? + s_req_line_almost_done : + s_header_field_start); + CALLBACK_DATA(url); + break; + default: + UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch)); + if (UNLIKELY(CURRENT_STATE() == s_dead)) { + SET_ERRNO(HPE_INVALID_URL); + goto error; + } + } + break; + } + + case s_req_http_start: + switch (ch) { + case ' ': + break; + case 'H': + UPDATE_STATE(s_req_http_H); + break; + case 'I': + if (parser->method == HTTP_SOURCE) { + UPDATE_STATE(s_req_http_I); + break; + } + /* fall through */ + default: + SET_ERRNO(HPE_INVALID_CONSTANT); + goto error; + } + break; + + case s_req_http_H: + STRICT_CHECK(ch != 'T'); + UPDATE_STATE(s_req_http_HT); + break; + + case s_req_http_HT: + STRICT_CHECK(ch != 'T'); + UPDATE_STATE(s_req_http_HTT); + break; + + case s_req_http_HTT: + STRICT_CHECK(ch != 'P'); + UPDATE_STATE(s_req_http_HTTP); + break; + + case s_req_http_I: + STRICT_CHECK(ch != 'C'); + UPDATE_STATE(s_req_http_IC); + break; + + case s_req_http_IC: + STRICT_CHECK(ch != 'E'); + UPDATE_STATE(s_req_http_HTTP); /* Treat "ICE" as "HTTP". */ + break; + + case s_req_http_HTTP: + STRICT_CHECK(ch != '/'); + UPDATE_STATE(s_req_http_major); + break; + + case s_req_http_major: + if (UNLIKELY(!IS_NUM(ch))) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + parser->http_major = ch - '0'; + UPDATE_STATE(s_req_http_dot); + break; + + case s_req_http_dot: + { + if (UNLIKELY(ch != '.')) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + UPDATE_STATE(s_req_http_minor); + break; + } + + case s_req_http_minor: + if (UNLIKELY(!IS_NUM(ch))) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + parser->http_minor = ch - '0'; + UPDATE_STATE(s_req_http_end); + break; + + case s_req_http_end: + { + if (ch == CR) { + UPDATE_STATE(s_req_line_almost_done); + break; + } + + if (ch == LF) { + UPDATE_STATE(s_header_field_start); + break; + } + + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + break; + } + + /* end of request line */ + case s_req_line_almost_done: + { + if (UNLIKELY(ch != LF)) { + SET_ERRNO(HPE_LF_EXPECTED); + goto error; + } + + UPDATE_STATE(s_header_field_start); + break; + } + + case s_header_field_start: + { + if (ch == CR) { + UPDATE_STATE(s_headers_almost_done); + break; + } + + if (ch == LF) { + /* they might be just sending \n instead of \r\n so this would be + * the second \n to denote the end of headers*/ + UPDATE_STATE(s_headers_almost_done); + REEXECUTE(); + } + + c = TOKEN(ch); + + if (UNLIKELY(!c)) { + SET_ERRNO(HPE_INVALID_HEADER_TOKEN); + goto error; + } + + MARK(header_field); + + parser->index = 0; + UPDATE_STATE(s_header_field); + + switch (c) { + case 'c': + parser->header_state = h_C; + break; + + case 'p': + parser->header_state = h_matching_proxy_connection; + break; + + case 't': + parser->header_state = h_matching_transfer_encoding; + break; + + case 'u': + parser->header_state = h_matching_upgrade; + break; + + default: + parser->header_state = h_general; + break; + } + break; + } + + case s_header_field: + { + const char* start = p; + for (; p != data + len; p++) { + ch = *p; + c = TOKEN(ch); + + if (!c) + break; + + switch (parser->header_state) { + case h_general: { + size_t limit = data + len - p; + limit = MIN(limit, max_header_size); + while (p+1 < data + limit && TOKEN(p[1])) { + p++; + } + break; + } + + case h_C: + parser->index++; + parser->header_state = (c == 'o' ? h_CO : h_general); + break; + + case h_CO: + parser->index++; + parser->header_state = (c == 'n' ? h_CON : h_general); + break; + + case h_CON: + parser->index++; + switch (c) { + case 'n': + parser->header_state = h_matching_connection; + break; + case 't': + parser->header_state = h_matching_content_length; + break; + default: + parser->header_state = h_general; + break; + } + break; + + /* connection */ + + case h_matching_connection: + parser->index++; + if (parser->index > sizeof(CONNECTION)-1 + || c != CONNECTION[parser->index]) { + parser->header_state = h_general; + } else if (parser->index == sizeof(CONNECTION)-2) { + parser->header_state = h_connection; + } + break; + + /* proxy-connection */ + + case h_matching_proxy_connection: + parser->index++; + if (parser->index > sizeof(PROXY_CONNECTION)-1 + || c != PROXY_CONNECTION[parser->index]) { + parser->header_state = h_general; + } else if (parser->index == sizeof(PROXY_CONNECTION)-2) { + parser->header_state = h_connection; + } + break; + + /* content-length */ + + case h_matching_content_length: + parser->index++; + if (parser->index > sizeof(CONTENT_LENGTH)-1 + || c != CONTENT_LENGTH[parser->index]) { + parser->header_state = h_general; + } else if (parser->index == sizeof(CONTENT_LENGTH)-2) { + parser->header_state = h_content_length; + } + break; + + /* transfer-encoding */ + + case h_matching_transfer_encoding: + parser->index++; + if (parser->index > sizeof(TRANSFER_ENCODING)-1 + || c != TRANSFER_ENCODING[parser->index]) { + parser->header_state = h_general; + } else if (parser->index == sizeof(TRANSFER_ENCODING)-2) { + parser->header_state = h_transfer_encoding; + } + break; + + /* upgrade */ + + case h_matching_upgrade: + parser->index++; + if (parser->index > sizeof(UPGRADE)-1 + || c != UPGRADE[parser->index]) { + parser->header_state = h_general; + } else if (parser->index == sizeof(UPGRADE)-2) { + parser->header_state = h_upgrade; + } + break; + + case h_connection: + case h_content_length: + case h_transfer_encoding: + case h_upgrade: + if (ch != ' ') parser->header_state = h_general; + break; + + default: + assert(0 && "Unknown header_state"); + break; + } + } + + if (p == data + len) { + --p; + COUNT_HEADER_SIZE(p - start); + break; + } + + COUNT_HEADER_SIZE(p - start); + + if (ch == ':') { + UPDATE_STATE(s_header_value_discard_ws); + CALLBACK_DATA(header_field); + break; + } + + SET_ERRNO(HPE_INVALID_HEADER_TOKEN); + goto error; + } + + case s_header_value_discard_ws: + if (ch == ' ' || ch == '\t') break; + + if (ch == CR) { + UPDATE_STATE(s_header_value_discard_ws_almost_done); + break; + } + + if (ch == LF) { + UPDATE_STATE(s_header_value_discard_lws); + break; + } + + /* fall through */ + + case s_header_value_start: + { + MARK(header_value); + + UPDATE_STATE(s_header_value); + parser->index = 0; + + c = LOWER(ch); + + switch (parser->header_state) { + case h_upgrade: + parser->flags |= F_UPGRADE; + parser->header_state = h_general; + break; + + case h_transfer_encoding: + /* looking for 'Transfer-Encoding: chunked' */ + if ('c' == c) { + parser->header_state = h_matching_transfer_encoding_chunked; + } else { + parser->header_state = h_general; + } + break; + + case h_content_length: + if (UNLIKELY(!IS_NUM(ch))) { + SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); + goto error; + } + + if (parser->flags & F_CONTENTLENGTH) { + SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH); + goto error; + } + + parser->flags |= F_CONTENTLENGTH; + parser->content_length = ch - '0'; + parser->header_state = h_content_length_num; + break; + + /* when obsolete line folding is encountered for content length + * continue to the s_header_value state */ + case h_content_length_ws: + break; + + case h_connection: + /* looking for 'Connection: keep-alive' */ + if (c == 'k') { + parser->header_state = h_matching_connection_keep_alive; + /* looking for 'Connection: close' */ + } else if (c == 'c') { + parser->header_state = h_matching_connection_close; + } else if (c == 'u') { + parser->header_state = h_matching_connection_upgrade; + } else { + parser->header_state = h_matching_connection_token; + } + break; + + /* Multi-value `Connection` header */ + case h_matching_connection_token_start: + break; + + default: + parser->header_state = h_general; + break; + } + break; + } + + case s_header_value: + { + const char* start = p; + enum header_states h_state = (enum header_states) parser->header_state; + for (; p != data + len; p++) { + ch = *p; + if (ch == CR) { + UPDATE_STATE(s_header_almost_done); + parser->header_state = h_state; + CALLBACK_DATA(header_value); + break; + } + + if (ch == LF) { + UPDATE_STATE(s_header_almost_done); + COUNT_HEADER_SIZE(p - start); + parser->header_state = h_state; + CALLBACK_DATA_NOADVANCE(header_value); + REEXECUTE(); + } + + if (!lenient && !IS_HEADER_CHAR(ch)) { + SET_ERRNO(HPE_INVALID_HEADER_TOKEN); + goto error; + } + + c = LOWER(ch); + + switch (h_state) { + case h_general: + { + const char* p_cr; + const char* p_lf; + size_t limit = data + len - p; + + limit = MIN(limit, max_header_size); + + p_cr = (const char*) memchr(p, CR, limit); + p_lf = (const char*) memchr(p, LF, limit); + if (p_cr != NULL) { + if (p_lf != NULL && p_cr >= p_lf) + p = p_lf; + else + p = p_cr; + } else if (UNLIKELY(p_lf != NULL)) { + p = p_lf; + } else { + p = data + len; + } + --p; + break; + } + + case h_connection: + case h_transfer_encoding: + assert(0 && "Shouldn't get here."); + break; + + case h_content_length: + if (ch == ' ') break; + h_state = h_content_length_num; + /* fall through */ + + case h_content_length_num: + { + uint64_t t; + + if (ch == ' ') { + h_state = h_content_length_ws; + break; + } + + if (UNLIKELY(!IS_NUM(ch))) { + SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); + parser->header_state = h_state; + goto error; + } + + t = parser->content_length; + t *= 10; + t += ch - '0'; + + /* Overflow? Test against a conservative limit for simplicity. */ + if (UNLIKELY((ULLONG_MAX - 10) / 10 < parser->content_length)) { + SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); + parser->header_state = h_state; + goto error; + } + + parser->content_length = t; + break; + } + + case h_content_length_ws: + if (ch == ' ') break; + SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); + parser->header_state = h_state; + goto error; + + /* Transfer-Encoding: chunked */ + case h_matching_transfer_encoding_chunked: + parser->index++; + if (parser->index > sizeof(CHUNKED)-1 + || c != CHUNKED[parser->index]) { + h_state = h_general; + } else if (parser->index == sizeof(CHUNKED)-2) { + h_state = h_transfer_encoding_chunked; + } + break; + + case h_matching_connection_token_start: + /* looking for 'Connection: keep-alive' */ + if (c == 'k') { + h_state = h_matching_connection_keep_alive; + /* looking for 'Connection: close' */ + } else if (c == 'c') { + h_state = h_matching_connection_close; + } else if (c == 'u') { + h_state = h_matching_connection_upgrade; + } else if (STRICT_TOKEN(c)) { + h_state = h_matching_connection_token; + } else if (c == ' ' || c == '\t') { + /* Skip lws */ + } else { + h_state = h_general; + } + break; + + /* looking for 'Connection: keep-alive' */ + case h_matching_connection_keep_alive: + parser->index++; + if (parser->index > sizeof(KEEP_ALIVE)-1 + || c != KEEP_ALIVE[parser->index]) { + h_state = h_matching_connection_token; + } else if (parser->index == sizeof(KEEP_ALIVE)-2) { + h_state = h_connection_keep_alive; + } + break; + + /* looking for 'Connection: close' */ + case h_matching_connection_close: + parser->index++; + if (parser->index > sizeof(CLOSE)-1 || c != CLOSE[parser->index]) { + h_state = h_matching_connection_token; + } else if (parser->index == sizeof(CLOSE)-2) { + h_state = h_connection_close; + } + break; + + /* looking for 'Connection: upgrade' */ + case h_matching_connection_upgrade: + parser->index++; + if (parser->index > sizeof(UPGRADE) - 1 || + c != UPGRADE[parser->index]) { + h_state = h_matching_connection_token; + } else if (parser->index == sizeof(UPGRADE)-2) { + h_state = h_connection_upgrade; + } + break; + + case h_matching_connection_token: + if (ch == ',') { + h_state = h_matching_connection_token_start; + parser->index = 0; + } + break; + + case h_transfer_encoding_chunked: + if (ch != ' ') h_state = h_general; + break; + + case h_connection_keep_alive: + case h_connection_close: + case h_connection_upgrade: + if (ch == ',') { + if (h_state == h_connection_keep_alive) { + parser->flags |= F_CONNECTION_KEEP_ALIVE; + } else if (h_state == h_connection_close) { + parser->flags |= F_CONNECTION_CLOSE; + } else if (h_state == h_connection_upgrade) { + parser->flags |= F_CONNECTION_UPGRADE; + } + h_state = h_matching_connection_token_start; + parser->index = 0; + } else if (ch != ' ') { + h_state = h_matching_connection_token; + } + break; + + default: + UPDATE_STATE(s_header_value); + h_state = h_general; + break; + } + } + parser->header_state = h_state; + + if (p == data + len) + --p; + + COUNT_HEADER_SIZE(p - start); + break; + } + + case s_header_almost_done: + { + if (UNLIKELY(ch != LF)) { + SET_ERRNO(HPE_LF_EXPECTED); + goto error; + } + + UPDATE_STATE(s_header_value_lws); + break; + } + + case s_header_value_lws: + { + if (ch == ' ' || ch == '\t') { + if (parser->header_state == h_content_length_num) { + /* treat obsolete line folding as space */ + parser->header_state = h_content_length_ws; + } + UPDATE_STATE(s_header_value_start); + REEXECUTE(); + } + + /* finished the header */ + switch (parser->header_state) { + case h_connection_keep_alive: + parser->flags |= F_CONNECTION_KEEP_ALIVE; + break; + case h_connection_close: + parser->flags |= F_CONNECTION_CLOSE; + break; + case h_transfer_encoding_chunked: + parser->flags |= F_CHUNKED; + break; + case h_connection_upgrade: + parser->flags |= F_CONNECTION_UPGRADE; + break; + default: + break; + } + + UPDATE_STATE(s_header_field_start); + REEXECUTE(); + } + + case s_header_value_discard_ws_almost_done: + { + STRICT_CHECK(ch != LF); + UPDATE_STATE(s_header_value_discard_lws); + break; + } + + case s_header_value_discard_lws: + { + if (ch == ' ' || ch == '\t') { + UPDATE_STATE(s_header_value_discard_ws); + break; + } else { + switch (parser->header_state) { + case h_connection_keep_alive: + parser->flags |= F_CONNECTION_KEEP_ALIVE; + break; + case h_connection_close: + parser->flags |= F_CONNECTION_CLOSE; + break; + case h_connection_upgrade: + parser->flags |= F_CONNECTION_UPGRADE; + break; + case h_transfer_encoding_chunked: + parser->flags |= F_CHUNKED; + break; + case h_content_length: + /* do not allow empty content length */ + SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); + goto error; + break; + default: + break; + } + + /* header value was empty */ + MARK(header_value); + UPDATE_STATE(s_header_field_start); + CALLBACK_DATA_NOADVANCE(header_value); + REEXECUTE(); + } + } + + case s_headers_almost_done: + { + STRICT_CHECK(ch != LF); + + if (parser->flags & F_TRAILING) { + /* End of a chunked request */ + UPDATE_STATE(s_message_done); + CALLBACK_NOTIFY_NOADVANCE(chunk_complete); + REEXECUTE(); + } + + /* Cannot use chunked encoding and a content-length header together + per the HTTP specification. */ + if ((parser->flags & F_CHUNKED) && + (parser->flags & F_CONTENTLENGTH)) { + SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH); + goto error; + } + + UPDATE_STATE(s_headers_done); + + /* Set this here so that on_headers_complete() callbacks can see it */ + if ((parser->flags & F_UPGRADE) && + (parser->flags & F_CONNECTION_UPGRADE)) { + /* For responses, "Upgrade: foo" and "Connection: upgrade" are + * mandatory only when it is a 101 Switching Protocols response, + * otherwise it is purely informational, to announce support. + */ + parser->upgrade = + (parser->type == HTTP_REQUEST || parser->status_code == 101); + } else { + parser->upgrade = (parser->method == HTTP_CONNECT); + } + + /* Here we call the headers_complete callback. This is somewhat + * different than other callbacks because if the user returns 1, we + * will interpret that as saying that this message has no body. This + * is needed for the annoying case of recieving a response to a HEAD + * request. + * + * We'd like to use CALLBACK_NOTIFY_NOADVANCE() here but we cannot, so + * we have to simulate it by handling a change in errno below. + */ + if (settings->on_headers_complete) { + switch (settings->on_headers_complete(parser)) { + case 0: + break; + + case 2: + parser->upgrade = 1; + + /* fall through */ + case 1: + parser->flags |= F_SKIPBODY; + break; + + default: + SET_ERRNO(HPE_CB_headers_complete); + RETURN(p - data); /* Error */ + } + } + + if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { + RETURN(p - data); + } + + REEXECUTE(); + } + + case s_headers_done: + { + int hasBody; + STRICT_CHECK(ch != LF); + + parser->nread = 0; + nread = 0; + + hasBody = parser->flags & F_CHUNKED || + (parser->content_length > 0 && parser->content_length != ULLONG_MAX); + if (parser->upgrade && (parser->method == HTTP_CONNECT || + (parser->flags & F_SKIPBODY) || !hasBody)) { + /* Exit, the rest of the message is in a different protocol. */ + UPDATE_STATE(NEW_MESSAGE()); + CALLBACK_NOTIFY(message_complete); + RETURN((p - data) + 1); + } + + if (parser->flags & F_SKIPBODY) { + UPDATE_STATE(NEW_MESSAGE()); + CALLBACK_NOTIFY(message_complete); + } else if (parser->flags & F_CHUNKED) { + /* chunked encoding - ignore Content-Length header */ + UPDATE_STATE(s_chunk_size_start); + } else { + if (parser->content_length == 0) { + /* Content-Length header given but zero: Content-Length: 0\r\n */ + UPDATE_STATE(NEW_MESSAGE()); + CALLBACK_NOTIFY(message_complete); + } else if (parser->content_length != ULLONG_MAX) { + /* Content-Length header given and non-zero */ + UPDATE_STATE(s_body_identity); + } else { + if (!http_message_needs_eof(parser)) { + /* Assume content-length 0 - read the next */ + UPDATE_STATE(NEW_MESSAGE()); + CALLBACK_NOTIFY(message_complete); + } else { + /* Read body until EOF */ + UPDATE_STATE(s_body_identity_eof); + } + } + } + + break; + } + + case s_body_identity: + { + uint64_t to_read = MIN(parser->content_length, + (uint64_t) ((data + len) - p)); + + assert(parser->content_length != 0 + && parser->content_length != ULLONG_MAX); + + /* The difference between advancing content_length and p is because + * the latter will automaticaly advance on the next loop iteration. + * Further, if content_length ends up at 0, we want to see the last + * byte again for our message complete callback. + */ + MARK(body); + parser->content_length -= to_read; + p += to_read - 1; + + if (parser->content_length == 0) { + UPDATE_STATE(s_message_done); + + /* Mimic CALLBACK_DATA_NOADVANCE() but with one extra byte. + * + * The alternative to doing this is to wait for the next byte to + * trigger the data callback, just as in every other case. The + * problem with this is that this makes it difficult for the test + * harness to distinguish between complete-on-EOF and + * complete-on-length. It's not clear that this distinction is + * important for applications, but let's keep it for now. + */ + CALLBACK_DATA_(body, p - body_mark + 1, p - data); + REEXECUTE(); + } + + break; + } + + /* read until EOF */ + case s_body_identity_eof: + MARK(body); + p = data + len - 1; + + break; + + case s_message_done: + UPDATE_STATE(NEW_MESSAGE()); + CALLBACK_NOTIFY(message_complete); + if (parser->upgrade) { + /* Exit, the rest of the message is in a different protocol. */ + RETURN((p - data) + 1); + } + break; + + case s_chunk_size_start: + { + assert(nread == 1); + assert(parser->flags & F_CHUNKED); + + unhex_val = unhex[(unsigned char)ch]; + if (UNLIKELY(unhex_val == -1)) { + SET_ERRNO(HPE_INVALID_CHUNK_SIZE); + goto error; + } + + parser->content_length = unhex_val; + UPDATE_STATE(s_chunk_size); + break; + } + + case s_chunk_size: + { + uint64_t t; + + assert(parser->flags & F_CHUNKED); + + if (ch == CR) { + UPDATE_STATE(s_chunk_size_almost_done); + break; + } + + unhex_val = unhex[(unsigned char)ch]; + + if (unhex_val == -1) { + if (ch == ';' || ch == ' ') { + UPDATE_STATE(s_chunk_parameters); + break; + } + + SET_ERRNO(HPE_INVALID_CHUNK_SIZE); + goto error; + } + + t = parser->content_length; + t *= 16; + t += unhex_val; + + /* Overflow? Test against a conservative limit for simplicity. */ + if (UNLIKELY((ULLONG_MAX - 16) / 16 < parser->content_length)) { + SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); + goto error; + } + + parser->content_length = t; + break; + } + + case s_chunk_parameters: + { + assert(parser->flags & F_CHUNKED); + /* just ignore this shit. TODO check for overflow */ + if (ch == CR) { + UPDATE_STATE(s_chunk_size_almost_done); + break; + } + break; + } + + case s_chunk_size_almost_done: + { + assert(parser->flags & F_CHUNKED); + STRICT_CHECK(ch != LF); + + parser->nread = 0; + nread = 0; + + if (parser->content_length == 0) { + parser->flags |= F_TRAILING; + UPDATE_STATE(s_header_field_start); + } else { + UPDATE_STATE(s_chunk_data); + } + CALLBACK_NOTIFY(chunk_header); + break; + } + + case s_chunk_data: + { + uint64_t to_read = MIN(parser->content_length, + (uint64_t) ((data + len) - p)); + + assert(parser->flags & F_CHUNKED); + assert(parser->content_length != 0 + && parser->content_length != ULLONG_MAX); + + /* See the explanation in s_body_identity for why the content + * length and data pointers are managed this way. + */ + MARK(body); + parser->content_length -= to_read; + p += to_read - 1; + + if (parser->content_length == 0) { + UPDATE_STATE(s_chunk_data_almost_done); + } + + break; + } + + case s_chunk_data_almost_done: + assert(parser->flags & F_CHUNKED); + assert(parser->content_length == 0); + STRICT_CHECK(ch != CR); + UPDATE_STATE(s_chunk_data_done); + CALLBACK_DATA(body); + break; + + case s_chunk_data_done: + assert(parser->flags & F_CHUNKED); + STRICT_CHECK(ch != LF); + parser->nread = 0; + nread = 0; + UPDATE_STATE(s_chunk_size_start); + CALLBACK_NOTIFY(chunk_complete); + break; + + default: + assert(0 && "unhandled state"); + SET_ERRNO(HPE_INVALID_INTERNAL_STATE); + goto error; + } + } + + /* Run callbacks for any marks that we have leftover after we ran out of + * bytes. There should be at most one of these set, so it's OK to invoke + * them in series (unset marks will not result in callbacks). + * + * We use the NOADVANCE() variety of callbacks here because 'p' has already + * overflowed 'data' and this allows us to correct for the off-by-one that + * we'd otherwise have (since CALLBACK_DATA() is meant to be run with a 'p' + * value that's in-bounds). + */ + + assert(((header_field_mark ? 1 : 0) + + (header_value_mark ? 1 : 0) + + (url_mark ? 1 : 0) + + (body_mark ? 1 : 0) + + (status_mark ? 1 : 0)) <= 1); + + CALLBACK_DATA_NOADVANCE(header_field); + CALLBACK_DATA_NOADVANCE(header_value); + CALLBACK_DATA_NOADVANCE(url); + CALLBACK_DATA_NOADVANCE(body); + CALLBACK_DATA_NOADVANCE(status); + + RETURN(len); + +error: + if (HTTP_PARSER_ERRNO(parser) == HPE_OK) { + SET_ERRNO(HPE_UNKNOWN); + } + + RETURN(p - data); +} + + +/* Does the parser need to see an EOF to find the end of the message? */ +int +http_message_needs_eof (const http_parser *parser) +{ + if (parser->type == HTTP_REQUEST) { + return 0; + } + + /* See RFC 2616 section 4.4 */ + if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */ + parser->status_code == 204 || /* No Content */ + parser->status_code == 304 || /* Not Modified */ + parser->flags & F_SKIPBODY) { /* response to a HEAD request */ + return 0; + } + + if ((parser->flags & F_CHUNKED) || parser->content_length != ULLONG_MAX) { + return 0; + } + + return 1; +} + + +int +http_should_keep_alive (const http_parser *parser) +{ + if (parser->http_major > 0 && parser->http_minor > 0) { + /* HTTP/1.1 */ + if (parser->flags & F_CONNECTION_CLOSE) { + return 0; + } + } else { + /* HTTP/1.0 or earlier */ + if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) { + return 0; + } + } + + return !http_message_needs_eof(parser); +} + + +const char * +http_method_str (enum http_method m) +{ + return ELEM_AT(method_strings, m, ""); +} + +const char * +http_status_str (enum http_status s) +{ + switch (s) { +#define XX(num, name, string) case HTTP_STATUS_##name: return #string; + HTTP_STATUS_MAP(XX) +#undef XX + default: return ""; + } +} + +void +http_parser_init (http_parser *parser, enum http_parser_type t) +{ + void *data = parser->data; /* preserve application data */ + memset(parser, 0, sizeof(*parser)); + parser->data = data; + parser->type = t; + parser->state = (t == HTTP_REQUEST ? s_start_req : (t == HTTP_RESPONSE ? s_start_res : s_start_req_or_res)); + parser->http_errno = HPE_OK; +} + +void +http_parser_settings_init(http_parser_settings *settings) +{ + memset(settings, 0, sizeof(*settings)); +} + +const char * +http_errno_name(enum http_errno err) { + assert(((size_t) err) < ARRAY_SIZE(http_strerror_tab)); + return http_strerror_tab[err].name; +} + +const char * +http_errno_description(enum http_errno err) { + assert(((size_t) err) < ARRAY_SIZE(http_strerror_tab)); + return http_strerror_tab[err].description; +} + +static enum http_host_state +http_parse_host_char(enum http_host_state s, const char ch) { + switch(s) { + case s_http_userinfo: + case s_http_userinfo_start: + if (ch == '@') { + return s_http_host_start; + } + + if (IS_USERINFO_CHAR(ch)) { + return s_http_userinfo; + } + break; + + case s_http_host_start: + if (ch == '[') { + return s_http_host_v6_start; + } + + if (IS_HOST_CHAR(ch)) { + return s_http_host; + } + + break; + + case s_http_host: + if (IS_HOST_CHAR(ch)) { + return s_http_host; + } + + /* fall through */ + case s_http_host_v6_end: + if (ch == ':') { + return s_http_host_port_start; + } + + break; + + case s_http_host_v6: + if (ch == ']') { + return s_http_host_v6_end; + } + + /* fall through */ + case s_http_host_v6_start: + if (IS_HEX(ch) || ch == ':' || ch == '.') { + return s_http_host_v6; + } + + if (s == s_http_host_v6 && ch == '%') { + return s_http_host_v6_zone_start; + } + break; + + case s_http_host_v6_zone: + if (ch == ']') { + return s_http_host_v6_end; + } + + /* fall through */ + case s_http_host_v6_zone_start: + /* RFC 6874 Zone ID consists of 1*( unreserved / pct-encoded) */ + if (IS_ALPHANUM(ch) || ch == '%' || ch == '.' || ch == '-' || ch == '_' || + ch == '~') { + return s_http_host_v6_zone; + } + break; + + case s_http_host_port: + case s_http_host_port_start: + if (IS_NUM(ch)) { + return s_http_host_port; + } + + break; + + default: + break; + } + return s_http_host_dead; +} + +static int +http_parse_host(const char * buf, struct http_parser_url *u, int found_at) { + enum http_host_state s; + + const char *p; + size_t buflen = u->field_data[UF_HOST].off + u->field_data[UF_HOST].len; + + assert(u->field_set & (1 << UF_HOST)); + + u->field_data[UF_HOST].len = 0; + + s = found_at ? s_http_userinfo_start : s_http_host_start; + + for (p = buf + u->field_data[UF_HOST].off; p < buf + buflen; p++) { + enum http_host_state new_s = http_parse_host_char(s, *p); + + if (new_s == s_http_host_dead) { + return 1; + } + + switch(new_s) { + case s_http_host: + if (s != s_http_host) { + u->field_data[UF_HOST].off = (uint16_t)(p - buf); + } + u->field_data[UF_HOST].len++; + break; + + case s_http_host_v6: + if (s != s_http_host_v6) { + u->field_data[UF_HOST].off = (uint16_t)(p - buf); + } + u->field_data[UF_HOST].len++; + break; + + case s_http_host_v6_zone_start: + case s_http_host_v6_zone: + u->field_data[UF_HOST].len++; + break; + + case s_http_host_port: + if (s != s_http_host_port) { + u->field_data[UF_PORT].off = (uint16_t)(p - buf); + u->field_data[UF_PORT].len = 0; + u->field_set |= (1 << UF_PORT); + } + u->field_data[UF_PORT].len++; + break; + + case s_http_userinfo: + if (s != s_http_userinfo) { + u->field_data[UF_USERINFO].off = (uint16_t)(p - buf); + u->field_data[UF_USERINFO].len = 0; + u->field_set |= (1 << UF_USERINFO); + } + u->field_data[UF_USERINFO].len++; + break; + + default: + break; + } + s = new_s; + } + + /* Make sure we don't end somewhere unexpected */ + switch (s) { + case s_http_host_start: + case s_http_host_v6_start: + case s_http_host_v6: + case s_http_host_v6_zone_start: + case s_http_host_v6_zone: + case s_http_host_port_start: + case s_http_userinfo: + case s_http_userinfo_start: + return 1; + default: + break; + } + + return 0; +} + +void +http_parser_url_init(struct http_parser_url *u) { + memset(u, 0, sizeof(*u)); +} + +int +http_parser_parse_url(const char *buf, size_t buflen, int is_connect, + struct http_parser_url *u) +{ + enum state s; + const char *p; + enum http_parser_url_fields uf, old_uf; + int found_at = 0; + + if (buflen == 0) { + return 1; + } + + u->port = u->field_set = 0; + s = is_connect ? s_req_server_start : s_req_spaces_before_url; + old_uf = UF_MAX; + + for (p = buf; p < buf + buflen; p++) { + s = parse_url_char(s, *p); + + /* Figure out the next field that we're operating on */ + switch (s) { + case s_dead: + return 1; + + /* Skip delimeters */ + case s_req_schema_slash: + case s_req_schema_slash_slash: + case s_req_server_start: + case s_req_query_string_start: + case s_req_fragment_start: + continue; + + case s_req_schema: + uf = UF_SCHEMA; + break; + + case s_req_server_with_at: + found_at = 1; + + /* fall through */ + case s_req_server: + uf = UF_HOST; + break; + + case s_req_path: + uf = UF_PATH; + break; + + case s_req_query_string: + uf = UF_QUERY; + break; + + case s_req_fragment: + uf = UF_FRAGMENT; + break; + + default: + assert(!"Unexpected state"); + return 1; + } + + /* Nothing's changed; soldier on */ + if (uf == old_uf) { + u->field_data[uf].len++; + continue; + } + + u->field_data[uf].off = (uint16_t)(p - buf); + u->field_data[uf].len = 1; + + u->field_set |= (1 << uf); + old_uf = uf; + } + + /* host must be present if there is a schema */ + /* parsing http:///toto will fail */ + if ((u->field_set & (1 << UF_SCHEMA)) && + (u->field_set & (1 << UF_HOST)) == 0) { + return 1; + } + + if (u->field_set & (1 << UF_HOST)) { + if (http_parse_host(buf, u, found_at) != 0) { + return 1; + } + } + + /* CONNECT requests can only contain "hostname:port" */ + if (is_connect && u->field_set != ((1 << UF_HOST)|(1 << UF_PORT))) { + return 1; + } + + if (u->field_set & (1 << UF_PORT)) { + uint16_t off; + uint16_t len; + const char* p; + const char* end; + unsigned long v; + + off = u->field_data[UF_PORT].off; + len = u->field_data[UF_PORT].len; + end = buf + off + len; + + /* NOTE: The characters are already validated and are in the [0-9] range */ + assert(off + len <= buflen && "Port number overflow"); + v = 0; + for (p = buf + off; p < end; p++) { + v *= 10; + v += *p - '0'; + + /* Ports have a max value of 2^16 */ + if (v > 0xffff) { + return 1; + } + } + + u->port = (uint16_t) v; + } + + return 0; +} + +void +http_parser_pause(http_parser *parser, int paused) { + /* Users should only be pausing/unpausing a parser that is not in an error + * state. In non-debug builds, there's not much that we can do about this + * other than ignore it. + */ + if (HTTP_PARSER_ERRNO(parser) == HPE_OK || + HTTP_PARSER_ERRNO(parser) == HPE_PAUSED) { + uint32_t nread = parser->nread; /* used by the SET_ERRNO macro */ + SET_ERRNO((paused) ? HPE_PAUSED : HPE_OK); + } else { + assert(0 && "Attempting to pause parser in error state"); + } +} + +int +http_body_is_final(const struct http_parser *parser) { + return parser->state == s_message_done; +} + +unsigned long +http_parser_version(void) { + return HTTP_PARSER_VERSION_MAJOR * 0x10000 | + HTTP_PARSER_VERSION_MINOR * 0x00100 | + HTTP_PARSER_VERSION_PATCH * 0x00001; +} + +void +http_parser_set_max_header_size(uint32_t size) { + max_header_size = size; +} diff --git a/src/3rdparty/http-parser/http_parser.h b/src/3rdparty/http-parser/http_parser.h new file mode 100644 index 00000000..880ed278 --- /dev/null +++ b/src/3rdparty/http-parser/http_parser.h @@ -0,0 +1,439 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * 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. + */ +#ifndef http_parser_h +#define http_parser_h +#ifdef __cplusplus +extern "C" { +#endif + +/* Also update SONAME in the Makefile whenever you change these. */ +#define HTTP_PARSER_VERSION_MAJOR 2 +#define HTTP_PARSER_VERSION_MINOR 9 +#define HTTP_PARSER_VERSION_PATCH 0 + +#include +#if defined(_WIN32) && !defined(__MINGW32__) && \ + (!defined(_MSC_VER) || _MSC_VER<1600) && !defined(__WINE__) +#include +typedef __int8 int8_t; +typedef unsigned __int8 uint8_t; +typedef __int16 int16_t; +typedef unsigned __int16 uint16_t; +typedef __int32 int32_t; +typedef unsigned __int32 uint32_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +#else +#include +#endif + +/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run + * faster + */ +#ifndef HTTP_PARSER_STRICT +# define HTTP_PARSER_STRICT 1 +#endif + +/* Maximium header size allowed. If the macro is not defined + * before including this header then the default is used. To + * change the maximum header size, define the macro in the build + * environment (e.g. -DHTTP_MAX_HEADER_SIZE=). To remove + * the effective limit on the size of the header, define the macro + * to a very large number (e.g. -DHTTP_MAX_HEADER_SIZE=0x7fffffff) + */ +#ifndef HTTP_MAX_HEADER_SIZE +# define HTTP_MAX_HEADER_SIZE (80*1024) +#endif + +typedef struct http_parser http_parser; +typedef struct http_parser_settings http_parser_settings; + + +/* Callbacks should return non-zero to indicate an error. The parser will + * then halt execution. + * + * The one exception is on_headers_complete. In a HTTP_RESPONSE parser + * returning '1' from on_headers_complete will tell the parser that it + * should not expect a body. This is used when receiving a response to a + * HEAD request which may contain 'Content-Length' or 'Transfer-Encoding: + * chunked' headers that indicate the presence of a body. + * + * Returning `2` from on_headers_complete will tell parser that it should not + * expect neither a body nor any futher responses on this connection. This is + * useful for handling responses to a CONNECT request which may not contain + * `Upgrade` or `Connection: upgrade` headers. + * + * http_data_cb does not return data chunks. It will be called arbitrarily + * many times for each string. E.G. you might get 10 callbacks for "on_url" + * each providing just a few characters more data. + */ +typedef int (*http_data_cb) (http_parser*, const char *at, size_t length); +typedef int (*http_cb) (http_parser*); + + +/* Status Codes */ +#define HTTP_STATUS_MAP(XX) \ + XX(100, CONTINUE, Continue) \ + XX(101, SWITCHING_PROTOCOLS, Switching Protocols) \ + XX(102, PROCESSING, Processing) \ + XX(200, OK, OK) \ + XX(201, CREATED, Created) \ + XX(202, ACCEPTED, Accepted) \ + XX(203, NON_AUTHORITATIVE_INFORMATION, Non-Authoritative Information) \ + XX(204, NO_CONTENT, No Content) \ + XX(205, RESET_CONTENT, Reset Content) \ + XX(206, PARTIAL_CONTENT, Partial Content) \ + XX(207, MULTI_STATUS, Multi-Status) \ + XX(208, ALREADY_REPORTED, Already Reported) \ + XX(226, IM_USED, IM Used) \ + XX(300, MULTIPLE_CHOICES, Multiple Choices) \ + XX(301, MOVED_PERMANENTLY, Moved Permanently) \ + XX(302, FOUND, Found) \ + XX(303, SEE_OTHER, See Other) \ + XX(304, NOT_MODIFIED, Not Modified) \ + XX(305, USE_PROXY, Use Proxy) \ + XX(307, TEMPORARY_REDIRECT, Temporary Redirect) \ + XX(308, PERMANENT_REDIRECT, Permanent Redirect) \ + XX(400, BAD_REQUEST, Bad Request) \ + XX(401, UNAUTHORIZED, Unauthorized) \ + XX(402, PAYMENT_REQUIRED, Payment Required) \ + XX(403, FORBIDDEN, Forbidden) \ + XX(404, NOT_FOUND, Not Found) \ + XX(405, METHOD_NOT_ALLOWED, Method Not Allowed) \ + XX(406, NOT_ACCEPTABLE, Not Acceptable) \ + XX(407, PROXY_AUTHENTICATION_REQUIRED, Proxy Authentication Required) \ + XX(408, REQUEST_TIMEOUT, Request Timeout) \ + XX(409, CONFLICT, Conflict) \ + XX(410, GONE, Gone) \ + XX(411, LENGTH_REQUIRED, Length Required) \ + XX(412, PRECONDITION_FAILED, Precondition Failed) \ + XX(413, PAYLOAD_TOO_LARGE, Payload Too Large) \ + XX(414, URI_TOO_LONG, URI Too Long) \ + XX(415, UNSUPPORTED_MEDIA_TYPE, Unsupported Media Type) \ + XX(416, RANGE_NOT_SATISFIABLE, Range Not Satisfiable) \ + XX(417, EXPECTATION_FAILED, Expectation Failed) \ + XX(421, MISDIRECTED_REQUEST, Misdirected Request) \ + XX(422, UNPROCESSABLE_ENTITY, Unprocessable Entity) \ + XX(423, LOCKED, Locked) \ + XX(424, FAILED_DEPENDENCY, Failed Dependency) \ + XX(426, UPGRADE_REQUIRED, Upgrade Required) \ + XX(428, PRECONDITION_REQUIRED, Precondition Required) \ + XX(429, TOO_MANY_REQUESTS, Too Many Requests) \ + XX(431, REQUEST_HEADER_FIELDS_TOO_LARGE, Request Header Fields Too Large) \ + XX(451, UNAVAILABLE_FOR_LEGAL_REASONS, Unavailable For Legal Reasons) \ + XX(500, INTERNAL_SERVER_ERROR, Internal Server Error) \ + XX(501, NOT_IMPLEMENTED, Not Implemented) \ + XX(502, BAD_GATEWAY, Bad Gateway) \ + XX(503, SERVICE_UNAVAILABLE, Service Unavailable) \ + XX(504, GATEWAY_TIMEOUT, Gateway Timeout) \ + XX(505, HTTP_VERSION_NOT_SUPPORTED, HTTP Version Not Supported) \ + XX(506, VARIANT_ALSO_NEGOTIATES, Variant Also Negotiates) \ + XX(507, INSUFFICIENT_STORAGE, Insufficient Storage) \ + XX(508, LOOP_DETECTED, Loop Detected) \ + XX(510, NOT_EXTENDED, Not Extended) \ + XX(511, NETWORK_AUTHENTICATION_REQUIRED, Network Authentication Required) \ + +enum http_status + { +#define XX(num, name, string) HTTP_STATUS_##name = num, + HTTP_STATUS_MAP(XX) +#undef XX + }; + + +/* Request Methods */ +#define HTTP_METHOD_MAP(XX) \ + XX(0, DELETE, DELETE) \ + XX(1, GET, GET) \ + XX(2, HEAD, HEAD) \ + XX(3, POST, POST) \ + XX(4, PUT, PUT) \ + /* pathological */ \ + XX(5, CONNECT, CONNECT) \ + XX(6, OPTIONS, OPTIONS) \ + XX(7, TRACE, TRACE) \ + /* WebDAV */ \ + XX(8, COPY, COPY) \ + XX(9, LOCK, LOCK) \ + XX(10, MKCOL, MKCOL) \ + XX(11, MOVE, MOVE) \ + XX(12, PROPFIND, PROPFIND) \ + XX(13, PROPPATCH, PROPPATCH) \ + XX(14, SEARCH, SEARCH) \ + XX(15, UNLOCK, UNLOCK) \ + XX(16, BIND, BIND) \ + XX(17, REBIND, REBIND) \ + XX(18, UNBIND, UNBIND) \ + XX(19, ACL, ACL) \ + /* subversion */ \ + XX(20, REPORT, REPORT) \ + XX(21, MKACTIVITY, MKACTIVITY) \ + XX(22, CHECKOUT, CHECKOUT) \ + XX(23, MERGE, MERGE) \ + /* upnp */ \ + XX(24, MSEARCH, M-SEARCH) \ + XX(25, NOTIFY, NOTIFY) \ + XX(26, SUBSCRIBE, SUBSCRIBE) \ + XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \ + /* RFC-5789 */ \ + XX(28, PATCH, PATCH) \ + XX(29, PURGE, PURGE) \ + /* CalDAV */ \ + XX(30, MKCALENDAR, MKCALENDAR) \ + /* RFC-2068, section 19.6.1.2 */ \ + XX(31, LINK, LINK) \ + XX(32, UNLINK, UNLINK) \ + /* icecast */ \ + XX(33, SOURCE, SOURCE) \ + +enum http_method + { +#define XX(num, name, string) HTTP_##name = num, + HTTP_METHOD_MAP(XX) +#undef XX + }; + + +enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH }; + + +/* Flag values for http_parser.flags field */ +enum flags + { F_CHUNKED = 1 << 0 + , F_CONNECTION_KEEP_ALIVE = 1 << 1 + , F_CONNECTION_CLOSE = 1 << 2 + , F_CONNECTION_UPGRADE = 1 << 3 + , F_TRAILING = 1 << 4 + , F_UPGRADE = 1 << 5 + , F_SKIPBODY = 1 << 6 + , F_CONTENTLENGTH = 1 << 7 + }; + + +/* Map for errno-related constants + * + * The provided argument should be a macro that takes 2 arguments. + */ +#define HTTP_ERRNO_MAP(XX) \ + /* No error */ \ + XX(OK, "success") \ + \ + /* Callback-related errors */ \ + XX(CB_message_begin, "the on_message_begin callback failed") \ + XX(CB_url, "the on_url callback failed") \ + XX(CB_header_field, "the on_header_field callback failed") \ + XX(CB_header_value, "the on_header_value callback failed") \ + XX(CB_headers_complete, "the on_headers_complete callback failed") \ + XX(CB_body, "the on_body callback failed") \ + XX(CB_message_complete, "the on_message_complete callback failed") \ + XX(CB_status, "the on_status callback failed") \ + XX(CB_chunk_header, "the on_chunk_header callback failed") \ + XX(CB_chunk_complete, "the on_chunk_complete callback failed") \ + \ + /* Parsing-related errors */ \ + XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \ + XX(HEADER_OVERFLOW, \ + "too many header bytes seen; overflow detected") \ + XX(CLOSED_CONNECTION, \ + "data received after completed connection: close message") \ + XX(INVALID_VERSION, "invalid HTTP version") \ + XX(INVALID_STATUS, "invalid HTTP status code") \ + XX(INVALID_METHOD, "invalid HTTP method") \ + XX(INVALID_URL, "invalid URL") \ + XX(INVALID_HOST, "invalid host") \ + XX(INVALID_PORT, "invalid port") \ + XX(INVALID_PATH, "invalid path") \ + XX(INVALID_QUERY_STRING, "invalid query string") \ + XX(INVALID_FRAGMENT, "invalid fragment") \ + XX(LF_EXPECTED, "LF character expected") \ + XX(INVALID_HEADER_TOKEN, "invalid character in header") \ + XX(INVALID_CONTENT_LENGTH, \ + "invalid character in content-length header") \ + XX(UNEXPECTED_CONTENT_LENGTH, \ + "unexpected content-length header") \ + XX(INVALID_CHUNK_SIZE, \ + "invalid character in chunk size header") \ + XX(INVALID_CONSTANT, "invalid constant string") \ + XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\ + XX(STRICT, "strict mode assertion failed") \ + XX(PAUSED, "parser is paused") \ + XX(UNKNOWN, "an unknown error occurred") + + +/* Define HPE_* values for each errno value above */ +#define HTTP_ERRNO_GEN(n, s) HPE_##n, +enum http_errno { + HTTP_ERRNO_MAP(HTTP_ERRNO_GEN) +}; +#undef HTTP_ERRNO_GEN + + +/* Get an http_errno value from an http_parser */ +#define HTTP_PARSER_ERRNO(p) ((enum http_errno) (p)->http_errno) + + +struct http_parser { + /** PRIVATE **/ + unsigned int type : 2; /* enum http_parser_type */ + unsigned int flags : 8; /* F_* values from 'flags' enum; semi-public */ + unsigned int state : 7; /* enum state from http_parser.c */ + unsigned int header_state : 7; /* enum header_state from http_parser.c */ + unsigned int index : 7; /* index into current matcher */ + unsigned int lenient_http_headers : 1; + + uint32_t nread; /* # bytes read in various scenarios */ + uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */ + + /** READ-ONLY **/ + unsigned short http_major; + unsigned short http_minor; + unsigned int status_code : 16; /* responses only */ + unsigned int method : 8; /* requests only */ + unsigned int http_errno : 7; + + /* 1 = Upgrade header was present and the parser has exited because of that. + * 0 = No upgrade header present. + * Should be checked when http_parser_execute() returns in addition to + * error checking. + */ + unsigned int upgrade : 1; + + /** PUBLIC **/ + void *data; /* A pointer to get hook to the "connection" or "socket" object */ +}; + + +struct http_parser_settings { + http_cb on_message_begin; + http_data_cb on_url; + http_data_cb on_status; + http_data_cb on_header_field; + http_data_cb on_header_value; + http_cb on_headers_complete; + http_data_cb on_body; + http_cb on_message_complete; + /* When on_chunk_header is called, the current chunk length is stored + * in parser->content_length. + */ + http_cb on_chunk_header; + http_cb on_chunk_complete; +}; + + +enum http_parser_url_fields + { UF_SCHEMA = 0 + , UF_HOST = 1 + , UF_PORT = 2 + , UF_PATH = 3 + , UF_QUERY = 4 + , UF_FRAGMENT = 5 + , UF_USERINFO = 6 + , UF_MAX = 7 + }; + + +/* Result structure for http_parser_parse_url(). + * + * Callers should index into field_data[] with UF_* values iff field_set + * has the relevant (1 << UF_*) bit set. As a courtesy to clients (and + * because we probably have padding left over), we convert any port to + * a uint16_t. + */ +struct http_parser_url { + uint16_t field_set; /* Bitmask of (1 << UF_*) values */ + uint16_t port; /* Converted UF_PORT string */ + + struct { + uint16_t off; /* Offset into buffer in which field starts */ + uint16_t len; /* Length of run in buffer */ + } field_data[UF_MAX]; +}; + + +/* Returns the library version. Bits 16-23 contain the major version number, + * bits 8-15 the minor version number and bits 0-7 the patch level. + * Usage example: + * + * unsigned long version = http_parser_version(); + * unsigned major = (version >> 16) & 255; + * unsigned minor = (version >> 8) & 255; + * unsigned patch = version & 255; + * printf("http_parser v%u.%u.%u\n", major, minor, patch); + */ +unsigned long http_parser_version(void); + +void http_parser_init(http_parser *parser, enum http_parser_type type); + + +/* Initialize http_parser_settings members to 0 + */ +void http_parser_settings_init(http_parser_settings *settings); + + +/* Executes the parser. Returns number of parsed bytes. Sets + * `parser->http_errno` on error. */ +size_t http_parser_execute(http_parser *parser, + const http_parser_settings *settings, + const char *data, + size_t len); + + +/* If http_should_keep_alive() in the on_headers_complete or + * on_message_complete callback returns 0, then this should be + * the last message on the connection. + * If you are the server, respond with the "Connection: close" header. + * If you are the client, close the connection. + */ +int http_should_keep_alive(const http_parser *parser); + +/* Returns a string version of the HTTP method. */ +const char *http_method_str(enum http_method m); + +/* Returns a string version of the HTTP status code. */ +const char *http_status_str(enum http_status s); + +/* Return a string name of the given error */ +const char *http_errno_name(enum http_errno err); + +/* Return a string description of the given error */ +const char *http_errno_description(enum http_errno err); + +/* Initialize all http_parser_url members to 0 */ +void http_parser_url_init(struct http_parser_url *u); + +/* Parse a URL; return nonzero on failure */ +int http_parser_parse_url(const char *buf, size_t buflen, + int is_connect, + struct http_parser_url *u); + +/* Pause or un-pause the parser; a nonzero value pauses */ +void http_parser_pause(http_parser *parser, int paused); + +/* Checks if this is the final chunk of the body. */ +int http_body_is_final(const http_parser *parser); + +/* Change the maximum header size provided at compile time. */ +void http_parser_set_max_header_size(uint32_t size); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/3rdparty/hwloc/AUTHORS b/src/3rdparty/hwloc/AUTHORS new file mode 100644 index 00000000..7187a723 --- /dev/null +++ b/src/3rdparty/hwloc/AUTHORS @@ -0,0 +1,44 @@ +hwloc Authors +============= + +The following cumulative list contains the names of most individuals +who have committed code to the hwloc repository +(either directly or through a third party). + +Name Affiliation(s) +--------------------------- -------------------- +Grzegorz Andrejczuk Intel +Cédric Augonnet University of Bordeaux +Guillaume Beauchamp Inria +Ahmad Boissetri Binzagr Inria +Cyril Bordage Inria +Nicholas Buroker UWL +Christopher M. Cantalupo Intel +Jérôme Clet-Ortega University of Bordeaux +Ludovic Courtès Inria +Clément Foyer Inria +Nathalie Furmento CNRS +Bryon Gloden +Brice Goglin Inria +Gilles Gouaillardet RIST +Joshua Hursey UWL +Alexey Kardashevskiy IBM +Rob Latham ANL +Douglas MacFarland UWL +Marc Marí BSC +Jonathan L Peyton Intel +Piotr Luc Intel +Antoine Rougier intern from University of Bordeaux +Jeff Squyres Cisco +Samuel Thibault University of Bordeaux +Jean-Yves VET DDN +Benjamin Worpitz +Jeff Zhao Zhaoxin + +Affiliaion abbreviations: +------------------------- +ANL = Argonne National Lab +BSC = Barcelona Supercomputing Center +Cisco = Cisco Systems, Inc. +CNRS = Centre national de la recherche scientifique (France) +UWL = University of Wisconsin-La Crosse diff --git a/src/3rdparty/hwloc/CMakeLists.txt b/src/3rdparty/hwloc/CMakeLists.txt new file mode 100644 index 00000000..431c11eb --- /dev/null +++ b/src/3rdparty/hwloc/CMakeLists.txt @@ -0,0 +1,38 @@ +cmake_minimum_required (VERSION 2.8) +project (hwloc C) + +include_directories(include) +include_directories(src) + +add_definitions(/D_CRT_SECURE_NO_WARNINGS) +set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /MT") + +set(HEADERS + include/hwloc.h + src/static-components.h + ) + +set(SOURCES + src/base64.c + src/bind.c + src/bitmap.c + src/components.c + src/diff.c + src/distances.c + src/misc.c + src/pci-common.c + src/shmem.c + src/topology.c + src/topology-noos.c + src/topology-synthetic.c + src/topology-windows.c + src/topology-x86.c + src/topology-xml.c + src/topology-xml-nolibxml.c + src/traversal.c + ) + +add_library(hwloc STATIC + ${HEADERS} + ${SOURCES} + ) diff --git a/src/3rdparty/hwloc/COPYING b/src/3rdparty/hwloc/COPYING new file mode 100644 index 00000000..e77516e1 --- /dev/null +++ b/src/3rdparty/hwloc/COPYING @@ -0,0 +1,39 @@ +Copyright © 2004-2006 The Trustees of Indiana University and Indiana University Research and Technology Corporation. All rights reserved. +Copyright © 2004-2005 The University of Tennessee and The University of Tennessee Research Foundation. All rights reserved. +Copyright © 2004-2005 High Performance Computing Center Stuttgart, University of Stuttgart. All rights reserved. +Copyright © 2004-2005 The Regents of the University of California. All rights reserved. +Copyright © 2009 CNRS +Copyright © 2009-2016 Inria. All rights reserved. +Copyright © 2009-2015 Université Bordeaux +Copyright © 2009-2015 Cisco Systems, Inc. All rights reserved. +Copyright © 2009-2012 Oracle and/or its affiliates. All rights reserved. +Copyright © 2010 IBM +Copyright © 2010 Jirka Hladky +Copyright © 2012 Aleksej Saushev, The NetBSD Foundation +Copyright © 2012 Blue Brain Project, EPFL. All rights reserved. +Copyright © 2013-2014 University of Wisconsin-La Crosse. All rights reserved. +Copyright © 2015 Research Organization for Information Science and Technology (RIST). All rights reserved. +Copyright © 2015-2016 Intel, Inc. All rights reserved. +See COPYING in top-level directory. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/3rdparty/hwloc/NEWS b/src/3rdparty/hwloc/NEWS new file mode 100644 index 00000000..664c8d55 --- /dev/null +++ b/src/3rdparty/hwloc/NEWS @@ -0,0 +1,1599 @@ +Copyright © 2009 CNRS +Copyright © 2009-2019 Inria. All rights reserved. +Copyright © 2009-2013 Université Bordeaux +Copyright © 2009-2011 Cisco Systems, Inc. All rights reserved. + +$COPYRIGHT$ + +Additional copyrights may follow + +$HEADER$ + +=========================================================================== + +This file contains the main features as well as overviews of specific +bug fixes (and other actions) for each version of hwloc since version +0.9 (as initially released as "libtopology", then re-branded to "hwloc" +in v0.9.1). + + +Version 2.0.4 (also included in 1.11.13 when appropriate) +------------- +* Add support for Linux 5.3 new sysfs cpu topology files with Die information. +* Add support for Intel v2 Extended Topology Enumeration in the x86 backend. +* Tiles, Modules and Dies are exposed as Groups for now. + + HWLOC_DONT_MERGE_DIE_GROUPS=1 may be set in the environment to prevent + Die groups from being automatically merged with identical parent or children. +* Ignore NUMA node information from AMD topoext in the x86 backend, + unless HWLOC_X86_TOPOEXT_NUMANODES=1 is set in the environment. +* Group objects have a new "dont_merge" attribute to prevent them from + being automatically merged with identical parent or children. + + +Version 2.0.3 (also included in 1.11.12 when appropriate) +------------- +* Fix build on Cygwin, thanks to Marco Atzeri for the patches. +* Fix a corner case of hwloc_topology_restrict() where children would + become out-of-order. +* Fix the return length of export_xmlbuffer() functions to always + include the ending \0. +* Fix lstopo --children-order argument parsing. + + +Version 2.0.2 (also included in 1.11.11 when appropriate) +------------- +* Add support for Hygon Dhyana processors in the x86 backend, + thanks to Pu Wen for the patch. +* Fix symbol renaming to also rename internal components, + thanks to Evan Ramos for the patch. +* Fix build on HP-UX, thanks to Richard Lloyd for reporting the issues. +* Detect PCI link speed without being root on Linux >= 4.13. +* Add HWLOC_VERSION* macros to the public headers, + thanks to Gilles Gouaillardet for the suggestion. + + +Version 2.0.1 (also included in 1.11.10 when relevant) +------------- +* Bump the library soname to 15:0:0 to avoid conflicts with hwloc 1.11.x + releases. The hwloc 2.0.0 soname was buggy (12:0:0), applications will + have to be recompiled. +* Serialize pciaccess discovery to fix concurrent topology loads in + multiple threads. +* Fix hwloc-dump-hwdata to only process SMBIOS information that correspond + to the KNL and KNM configuration. +* Add a heuristic for guessing KNL/KNM memory and cluster modes when + hwloc-dump-hwdata could not run as root earlier. +* Add --no-text lstopo option to remove text from some boxes in the + graphical output. Mostly useful for removing Group labels. +* Some minor fixes to memory binding. + + +Version 2.0.0 +------------- +*** The ABI of the library has changed. *** + For instance some hwloc_obj fields were reordered, added or removed, see below. + + HWLOC_API_VERSION and hwloc_get_api_version() now give 0x00020000. + + See "How do I handle ABI breaks and API upgrades ?" in the FAQ + and "Upgrading to hwloc 2.0 API" in the documentation. +* Major API changes + + Memory, I/O and Misc objects are now stored in dedicated children lists, + not in the usual children list that is now only used for CPU-side objects. + - hwloc_get_next_child() may still be used to iterate over these 4 lists + of children at once. + - hwloc_obj_type_is_normal(), _memory() and _io() may be used to check + the kind of a given object type. + + Topologies always have at least one NUMA object. On non-NUMA machines, + a single NUMA object is added to describe the entire machine memory. + The NUMA level cannot be ignored anymore. + + The NUMA level is special since NUMA nodes are not in the main hierarchy + of objects anymore. Its depth is a fake negative depth that should not be + compared with normal levels. + - If all memory objects are attached to parents at the same depth, + it may be retrieved with hwloc_get_memory_parents_depth(). + + The HWLOC_OBJ_CACHE type is replaced with 8 types HWLOC_OBJ_L[1-5]CACHE + and HWLOC_OBJ_L[1-3]ICACHE that remove the need to disambiguate levels + when looking for caches with _by_type() functions. + - New hwloc_obj_type_is_{,d,i}cache() functions may be used to check whether + a given type is a cache. + + Reworked ignoring/filtering API + - Replace hwloc_topology_ignore*() functions with hwloc_topology_set_type_filter() + and hwloc_topology_set_all_types_filter(). + . Contrary to hwloc_topology_ignore_{type,all}_keep_structure() which + removed individual objects, HWLOC_TYPE_FILTER_KEEP_STRUCTURE only removes + entire levels (so that topology do not become too asymmetric). + - Remove HWLOC_TOPOLOGY_FLAG_ICACHES in favor of hwloc_topology_set_icache_types_filter() + with HWLOC_TYPE_FILTER_KEEP_ALL. + - Remove HWLOC_TOPOLOGY_FLAG_IO_DEVICES, _IO_BRIDGES and _WHOLE_IO in favor of + hwloc_topology_set_io_types_filter() with HWLOC_TYPE_FILTER_KEEP_ALL or + HWLOC_TYPE_FILTER_KEEP_IMPORTANT. + + The distance API has been completely reworked. It is now described + in hwloc/distances.h. + + Return values + - Most functions in hwloc/bitmap.h now return an int that may be negative + in case of failure to realloc/extend the internal storage of a bitmap. + - hwloc_obj_add_info() also returns an int in case allocations fail. +* Minor API changes + + Object attributes + - obj->memory is removed. + . local_memory and page_types attributes are now in obj->attr->numanode + . total_memory moves obj->total_memory. + - Objects do not have allowed_cpuset and allowed_nodeset anymore. + They are only available for the entire topology using + hwloc_topology_get_allowed_cpuset() and hwloc_topology_get_allowed_nodeset(). + - Objects now have a "subtype" field that supersedes former "Type" and + "CoProcType" info attributes. + + Object and level depths are now signed ints. + + Object string printing and parsing + - hwloc_type_sscanf() deprecates the old hwloc_obj_type_sscanf(). + - hwloc_type_sscanf_as_depth() is added to convert a type name into + a level depth. + - hwloc_obj_cpuset_snprintf() is deprecated in favor of hwloc_bitmap_snprintf(). + + Misc objects + - Replace hwloc_topology_insert_misc_object_by_cpuset() with + hwloc_topology_insert_group_object() to precisely specify the location + of an additional hierarchy level in the topology. + - Misc objects have their own level and depth to iterate over all of them. + - Misc objects may now only be inserted as a leaf object with + hwloc_topology_insert_misc_object() which deprecates + hwloc_topology_insert_misc_object_by_parent(). + + hwloc_topology_restrict() doesn't remove objects that contain memory + by default anymore. + - The list of existing restrict flags was modified. + + The discovery support array now contains some NUMA specific bits. + + XML export functions take an additional flags argument, + for instance for exporting XMLs that are compatible with hwloc 1.x. + + Functions diff_load_xml*(), diff_export_xml*() and diff_destroy() in + hwloc/diff.h do not need a topology as first parameter anymore. + + hwloc_parse_cpumap_file () superseded by hwloc_linux_read_path_as_cpumask() + in hwloc/linux.h. + + HWLOC_MEMBIND_DEFAULT and HWLOC_MEMBIND_FIRSTTOUCH were clarified. +* New APIs and Features + + Add hwloc/shmem.h for sharing topologies between processes running on + the same machine (for reducing the memory footprint). + + Add the experimental netloc subproject. It is disabled by default + and can be enabled with --enable-netloc. + It currently brings command-line tools to gather and visualize the + topology of InfiniBand fabrics, and an API to convert such topologies + into Scotch architectures for process mapping. + See the documentation for details. +* Removed APIs and features + + Remove the online_cpuset from struct hwloc_obj. Offline PUs get unknown + topologies on Linux nowadays, and wrong topology on Solaris. Other OS + do not support them. And one cannot do much about them anyway. Just keep + them in complete_cpuset. + + Remove the now-unused "System" object type HWLOC_OBJ_SYSTEM, + defined to MACHINE for backward compatibility. + + The almost-unused "os_level" attribute has been removed from the + hwloc_obj structure. + + Remove the custom interface for assembling the topologies of different + nodes as well as the hwloc-assembler tools. + + hwloc_topology_set_fsroot() is removed, the environment variable + HWLOC_FSROOT may be used for the same remote testing/debugging purpose. + + Remove the deprecated hwloc_obj_snprintf(), hwloc_obj_type_of_string(), + hwloc_distribute[v](). + * Remove Myrinet Express interoperability (hwloc/myriexpress.h). + + Remove Kerrighed support from the Linux backend. + + Remove Tru64 (OSF/1) support. + - Remove HWLOC_MEMBIND_REPLICATE which wasn't available anywhere else. +* Backend improvements + + Linux + - OS devices do not have to be attached through PCI anymore, + for instance enabling the discovery of NVDIMM block devices. + - Remove the dependency on libnuma. + - Add a SectorSize attribute to block OS devices. + + Mac OS X + - Fix detection of cores and hyperthreads. + - Add CPUVendor, Model, ... attributes. + + Windows + - Add get_area_memlocation(). +* Tools + + lstopo and hwloc-info have a new --filter option matching the new filtering API. + + lstopo can be given --children-order=plain to force a basic displaying + of memory and normal children together below their parent. + + hwloc-distances was removed and replaced with lstopo --distances. +* Misc + + Exports + - Exporting to synthetic now ignores I/O and Misc objects. + + PCI discovery + - Separate OS device discovery from PCI discovery. Only the latter is disabled + with --disable-pci at configure time. Both may be disabled with --disable-io. + - The `linuxpci' component is now renamed into `linuxio'. + - The old `libpci' component name from hwloc 1.6 is not supported anymore, + only the `pci' name from hwloc 1.7 is now recognized. + - The HWLOC_PCI___LOCALCPUS environment variables are superseded + with a single HWLOC_PCI_LOCALITY where bus ranges may be specified. + - Do not set PCI devices and bridges name automatically. Vendor and device + names are already in info attributes. + + Components and discovery + - Add HWLOC_SYNTHETIC environment variable to enforce a synthetic topology + as if hwloc_topology_set_synthetic() had been called. + - HWLOC_COMPONENTS doesn't support xml or synthetic component attributes + anymore, they should be passed in HWLOC_XMLFILE or HWLOC_SYNTHETIC instead. + - HWLOC_COMPONENTS takes precedence over other environment variables + for selecting components. + + hwloc now requires a C99 compliant compiler. + + +Version 1.11.9 +-------------- +* Add support for Zhaoxin ZX-C and ZX-D processors in the x86 backend, + thanks to Jeff Zhao for the patch. +* Fix AMD Epyc 24-core L3 cache locality in the x86 backend. +* Don't crash in the x86 backend when the CPUID vendor string is unknown. +* Fix the missing pu discovery support bit on some OS. +* Fix the management of the lstopoStyle info attribute for custom colors. +* Add verbose warnings when failing to load hwloc v2.0+ XMLs. + + +Version 1.11.8 +-------------- +* Multiple Solaris improvements, thanks to Maureen Chew for the help: + + Detect caches on Sparc. + + Properly detect allowed/disallowed PUs and NUMA nodes with processor sets. + + Add hwloc_get_last_cpu_location() support for the current thread. +* Add support for CUDA compute capability 7.0 and fix support for 6.[12]. +* Tools improvements + + Fix search for objects by physical index in command-line tools. + + Add missing "cpubind:get_thisthread_last_cpu_location" in the output + of hwloc-info --support. + + Add --pid and --name to specify target processes in hwloc-ps. + + Display thread names in lstopo and hwloc-ps on Linux. +* Doc improvements + + Add a FAQ entry about building on Windows. + + Install missing sub-manpage for hwloc_obj_add_info() and + hwloc_obj_get_info_by_name(). + + +Version 1.11.7 +-------------- +* Fix hwloc-bind --membind for CPU-less NUMA nodes (again). + Thanks to Gilles Gouaillardet for reporting the issue. +* Fix a memory leak on IBM S/390 platforms running Linux. +* Fix a memory leak when forcing the x86 backend first on amd64/topoext + platforms running Linux. +* Command-line tools now support "hbm" instead "numanode" for filtering + only high-bandwidth memory nodes when selecting locations. + + hwloc-bind also support --hbm and --no-hbm for filtering only or + no HBM nodes. + Thanks to Nicolas Denoyelle for the suggestion. +* Add --children and --descendants to hwloc-info for listing object + children or object descendants of a specific type. +* Add --no-index, --index, --no-attrs, --attrs to disable/enable display + of index numbers or attributes in the graphical lstopo output. +* Try to gather hwloc-dump-hwdata output from all possible locations + in hwloc-gather-topology. +* Updates to the documentation of locations in hwloc(7) and + command-line tools manpages. + + +Version 1.11.6 +-------------- +* Make the Linux discovery about twice faster, especially on the CPU side, + by trying to avoid sysfs file accesses as much as possible. +* Add support for AMD Family 17h processors (Zen) SMT cores in the Linux + and x86 backends. +* Add the HWLOC_TOPOLOGY_FLAG_THISSYSTEM_ALLOWED_RESOURCES flag (and the + HWLOC_THISSYSTEM_ALLOWED_RESOURCES environment variable) for reading the + set of allowed resources from the local operating system even if the + topology was loaded from XML or synthetic. +* Fix hwloc_bitmap_set/clr_range() for infinite ranges that do not + overlap currently defined ranges in the bitmap. +* Don't reset the lstopo zoom scale when moving the X11 window. +* lstopo now has --flags for manually setting topology flags. +* hwloc_get_depth_type() returns HWLOC_TYPE_DEPTH_UNKNOWN for Misc objects. + + +Version 1.11.5 +-------------- +* Add support for Knights Mill Xeon Phi, thanks to Piotr Luc for the patch. +* Reenable distance gathering on Solaris, disabled by mistake since v1.0. + Thanks to TU Wien for the help. +* Fix hwloc_get_*obj*_inside_cpuset() functions to ignore objects with + empty CPU sets, for instance, CPU-less NUMA nodes such as KNL MCDRAM. + Thanks to Nicolas Denoyelle for the report. +* Fix XML import of multiple distance matrices. +* Add a FAQ entry about "hwloc is only a structural model, it ignores + performance models, memory bandwidth, etc.?" + + +Version 1.11.4 +-------------- +* Add MemoryMode and ClusterMode attributes in the Machine object on KNL. + Add doc/examples/get-knl-modes.c for an example of retrieving them. + Thanks to Grzegorz Andrejczuk. +* Fix Linux build with -m32 with respect to libudev. + Thanks to Paul Hargrove for reporting the issue. +* Fix build with Visual Studio 2015, thanks to Eloi Gaudry for reporting + the issue and providing the patch. +* Don't forget to display OS device children in the graphical lstopo. +* Fix a memory leak on Solaris, thanks to Bryon Gloden for the patch. +* Properly handle realloc() failures, thanks to Bryon Gloden for reporting + the issue. +* Fix lstopo crash in ascii/fig/windows outputs when some objects have a + lstopoStyle info attribute. + + +Version 1.11.3 +-------------- +* Bug fixes + + Fix a memory leak on Linux S/390 hosts with books. + + Fix /proc/mounts parsing on Linux by using mntent.h. + Thanks to Nathan Hjelm for reporting the issue. + + Fix a x86 infinite loop on VMware due to the x2APIC feature being + advertised without actually being fully supported. + Thanks to Jianjun Wen for reporting the problem and testing the patch. + + Fix the return value of hwloc_alloc() on mmap() failure. + Thanks to Hugo Brunie for reporting the issue. + + Fix the return value of command-line tools in some error cases. + + Do not break individual thread bindings during x86 backend discovery in a + multithreaded process. Thanks to Farouk Mansouri for the report. + + Fix hwloc-bind --membind for CPU-less NUMA nodes. + + Fix some corner cases in the XML export/import of application userdata. +* API Improvements + + Add HWLOC_MEMBIND_BYNODESET flag so that membind() functions accept + either cpusets or nodesets. + + Add hwloc_get_area_memlocation() to check where pages are actually + allocated. Only implemented on Linux for now. + - There's no _nodeset() variant, but the new flag HWLOC_MEMBIND_BYNODESET + is supported. + + Make hwloc_obj_type_sscanf() parse back everything that may be outputted + by hwloc_obj_type_snprintf(). +* Detection Improvements + + Allow the x86 backend to add missing cache levels, so that it completes + what the Solaris backend lacks. + Thanks to Ryan Zezeski for reporting the issue. + + Do not filter-out FibreChannel PCI adapters by default anymore. + Thanks to Matt Muggeridge for the report. + + Add support for CUDA compute capability 6.x. +* Tools + + Add --support to hwloc-info to list supported features, just like with + hwloc_topology_get_support(). + - Also add --objects and --topology to explicitly switch between the + default modes. + + Add --tid to let hwloc-bind operate on individual threads on Linux. + + Add --nodeset to let hwloc-bind report memory binding as NUMA node sets. + + hwloc-annotate and lstopo don't drop application userdata from XMLs anymore. + - Add --cu to hwloc-annotate to drop these application userdata. + + Make the hwloc-dump-hwdata dump directory configurable through configure + options such as --runstatedir or --localstatedir. +* Misc Improvements + + Add systemd service template contrib/systemd/hwloc-dump-hwdata.service + for launching hwloc-dump-hwdata at boot on Linux. + Thanks to Grzegorz Andrejczuk. + + Add HWLOC_PLUGINS_BLACKLIST environment variable to prevent some plugins + from being loaded. Thanks to Alexandre Denis for the suggestion. + + Small improvements for various Windows build systems, + thanks to Jonathan L Peyton and Marco Atzeri. + + +Version 1.11.2 +-------------- +* Improve support for Intel Knights Landing Xeon Phi on Linux: + + Group local NUMA nodes of normal memory (DDR) and high-bandwidth memory + (MCDRAM) together through "Cluster" groups so that the local MCDRAM is + easy to find. + - See "How do I find the local MCDRAM NUMA node on Intel Knights + Landing Xeon Phi?" in the documentation. + - For uniformity across all KNL configurations, always have a NUMA node + object even if the host is UMA. + + Fix the detection of the memory-side cache: + - Add the hwloc-dump-hwdata superuser utility to dump SMBIOS information + into /var/run/hwloc/ as root during boot, and load this dumped + information from the hwloc library at runtime. + - See "Why do I need hwloc-dump-hwdata for caches on Intel Knights + Landing Xeon Phi?" in the documentation. + Thanks to Grzegorz Andrejczuk for the patches and for the help. +* The x86 and linux backends may now be combined for discovering CPUs + through x86 CPUID and memory from the Linux kernel. + This is useful for working around buggy CPU information reported by Linux + (for instance the AMD Bulldozer/Piledriver bug below). + Combination is enabled by passing HWLOC_COMPONENTS=x86 in the environment. +* Fix L3 cache sharing on AMD Opteron 63xx (Piledriver) and 62xx (Bulldozer) + in the x86 backend. Thanks to many users who helped. +* Fix the overzealous L3 cache sharing fix added to the x86 backend in 1.11.1 + for AMD Opteron 61xx (Magny-Cours) processors. +* The x86 backend may now add the info attribute Inclusive=0 or 1 to caches + it discovers, or to caches discovered by other backends earlier. + Thanks to Guillaume Beauchamp for the patch. +* Fix the management on alloc_membind() allocation failures on AIX, HP-UX + and OSF/Tru64. +* Fix spurious failures to load with ENOMEM on AIX in case of Misc objects + below PUs. +* lstopo improvements in X11 and Windows graphical mode: + + Add + - f 1 shortcuts to manually zoom-in, zoom-out, reset the scale, + or fit the entire window. + + Display all keyboard shortcuts in the console. +* Debug messages may be disabled at runtime by passing HWLOC_DEBUG_VERBOSE=0 + in the environment when --enable-debug was passed to configure. +* Add a FAQ entry "What are these Group objects in my topology?". + + +Version 1.11.1 +-------------- +* Detection fixes + + Hardwire the topology of Fujitsu K-computer, FX10, FX100 servers to + workaround buggy Linux kernels. + Thanks to Takahiro Kawashima and Gilles Gouaillardet. + + Fix L3 cache information on AMD Opteron 61xx Magny-Cours processors + in the x86 backend. Thanks to Guillaume Beauchamp for the patch. + + Detect block devices directly attached to PCI without a controller, + for instance NVMe disks. Thanks to Barry M. Tannenbaum. + + Add the PCISlot attribute to all PCI functions instead of only the + first one. +* Miscellaneous internal fixes + + Ignore PCI bridges that could fail assertions by reporting buggy + secondary-subordinate bus numbers + Thanks to George Bosilca for reporting the issue. + + Fix an overzealous assertion when inserting an intermediate Group object + while Groups are totally ignored. + + Fix a memory leak on Linux on AMD processors with dual-core compute units. + Thanks to Bob Benner. + + Fix a memory leak on failure to load a xml diff file. + + Fix some segfaults when inputting an invalid synthetic description. + + Fix a segfault when plugins fail to find core symbols. + Thanks to Guy Streeter. +* Many fixes and improvements in the Windows backend: + + Fix the discovery of more than 32 processors and multiple processor + groups. Thanks to Barry M. Tannenbaum for the help. + + Add thread binding set support in case of multiple process groups. + + Add thread binding get support. + + Add get_last_cpu_location() support for the current thread. + + Disable the unsupported process binding in case of multiple processor + groups. + + Fix/update the Visual Studio support under contrib/windows. + Thanks to Eloi Gaudry for the help. +* Tools fixes + + Fix a segfault when displaying logical indexes in the graphical lstopo. + Thanks to Guillaume Mercier for reporting the issue. + + Fix lstopo linking with X11 libraries, for instance on Mac OS X. + Thanks to Scott Atchley and Pierre Ramet for reporting the issue. + + hwloc-annotate, hwloc-diff and hwloc-patch do not drop unavailable + resources from the output anymore and those may be annotated as well. + + Command-line tools may now import XML from the standard input with -i -.xml + + Add missing documentation for the hwloc-info --no-icaches option. + + +Version 1.11.0 +-------------- +* API + + Socket objects are renamed into Package to align with the terminology + used by processor vendors. The old HWLOC_OBJ_SOCKET type and "Socket" + name are still supported for backward compatibility. + + HWLOC_OBJ_NODE is replaced with HWLOC_OBJ_NUMANODE for clarification. + HWLOC_OBJ_NODE is still supported for backward compatibility. + "Node" and "NUMANode" strings are supported as in earlier releases. +* Detection improvements + + Add support for Intel Knights Landing Xeon Phi. + Thanks to Grzegorz Andrejczuk and Lukasz Anaczkowski. + + Add Vendor, Model, Revision, SerialNumber, Type and LinuxDeviceID + info attributes to Block OS devices on Linux. Thanks to Vineet Pedaballe + for the help. + - Add --disable-libudev to avoid dependency on the libudev library. + + Add "MemoryModule" Misc objects with information about DIMMs, on Linux + when privileged and when I/O is enabled. + Thanks to Vineet Pedaballe for the help. + + Add a PCISlot attribute to PCI devices on Linux when supported to + identify the physical PCI slot where the board is plugged. + + Add CPUStepping info attribute on x86 processors, + thanks to Thomas Röhl for the suggestion. + + Ignore the device-tree on non-Power architectures to avoid buggy + detection on ARM. Thanks to Orion Poplawski for reporting the issue. + + Work-around buggy Xeon E5v3 BIOS reporting invalid PCI-NUMA affinity + for the PCI links on the second processor. + + Add support for CUDA compute capability 5.x, thanks Benjamin Worpitz. + + Many fixes to the x86 backend + - Add L1i and fix L2/L3 type on old AMD processors without topoext support. + - Fix Intel CPU family and model numbers when basic family isn't 6 or 15. + - Fix package IDs on recent AMD processors. + - Fix misc issues due to incomplete APIC IDs on x2APIC processors. + - Avoid buggy discovery on old SGI Altix UVs with non-unique APIC IDs. + + Gather total machine memory on NetBSD. +* Tools + + lstopo + - Collapse identical PCI devices unless --no-collapse is given. + This avoids gigantic outputs when a PCI device contains dozens of + identical virtual functions. + - The ASCII art output is now called "ascii", for instance in + "lstopo -.ascii". + The former "txt" extension is retained for backward compatibility. + - Automatically scales graphical box width to the inner text in Cairo, + ASCII and Windows outputs. + - Add --rect to lstopo to force rectangular layout even for NUMA nodes. + - Add --restrict-flags to configure the behavior of --restrict. + - Objects may have a "Type" info attribute to specify a better type name + and display it in lstopo. + - Really export all verbose information to the given output file. + + hwloc-annotate + - May now operate on all types of objects, including I/O. + - May now insert Misc objects in the topology. + - Do not drop instruction caches and I/O devices from the output anymore. + + Fix lstopo path in hwloc-gather-topology after install. +* Misc + + Fix hwloc/cudart.h for machines with multiple PCI domains, + thanks to Imre Kerr for reporting the problem. + + Fix PCI Bridge-specific depth attribute. + + Fix hwloc_bitmap_intersect() for two infinite bitmaps. + + Fix some corner cases in the building of levels on large NUMA machines + with non-uniform NUMA groups and I/Os. + + Improve the performance of object insertion by cpuset for large + topologies. + + Prefix verbose XML import errors with the source name. + + Improve pkg-config checks and error messages. + + Fix excluding after a component with an argument in the HWLOC_COMPONENTS + environment variable. +* Documentation + + Fix the recommended way in documentation and examples to allocate memory + on some node, it should use HWLOC_MEMBIND_BIND. + Thanks to Nicolas Bouzat for reporting the issue. + + Add a "Miscellaneous objects" section in the documentation. + + Add a FAQ entry "What happens to my topology if I disable symmetric + multithreading, hyper-threading, etc. ?" to the documentation. + + +Version 1.10.1 +-------------- +* Actually remove disallowed NUMA nodes from nodesets when the whole-system + flag isn't enabled. +* Fix the gathering of PCI domains. Thanks to James Custer for reporting + the issue and providing a patch. +* Fix the merging of identical parent and child in presence of Misc objects. + Thanks to Dave Love for reporting the issue. +* Fix some misordering of children when merging with ignore_keep_structure() + in partially allowed topologies. +* Fix an overzealous assertion in the debug code when running on a single-PU + host with I/O. Thanks to Thomas Van Doren for reporting the issue. +* Don't forget to setup NUMA node object nodesets in x86 backend (for BSDs) + and OSF/Tru64 backend. +* Fix cpuid-x86 build error with gcc -O3 on x86-32. Thanks to Thomas Van Doren + for reporting the issue. +* Fix support for future very large caches in the x86 backend. +* Fix vendor/device names for SR-IOV PCI devices on Linux. +* Fix an unlikely crash in case of buggy hierarchical distance matrix. +* Fix PU os_index on some AIX releases. Thanks to Hendryk Bockelmann and + Erik Schnetter for helping debugging. +* Fix hwloc_bitmap_isincluded() in case of infinite sets. +* Change hwloc-ls.desktop into a lstopo.desktop and only install it if + lstopo is built with Cairo/X11 support. It cannot work with a non-graphical + lstopo or hwloc-ls. +* Add support for the renaming of Socket into Package in future releases. +* Add support for the replacement of HWLOC_OBJ_NODE with HWLOC_OBJ_NUMANODE + in future releases. +* Clarify the documentation of distance matrices in hwloc.h and in the manpage + of the hwloc-distances. Thanks to Dave Love for the suggestion. +* Improve some error messages by displaying more information about the + hwloc library in use. +* Document how to deal with the ABI break when upgrading to the upcoming 2.0 + See "How do I handle ABI breaks and API upgrades ?" in the FAQ. + + +Version 1.10.0 +-------------- +* API + + Add hwloc_topology_export_synthetic() to export a topology to a + synthetic string without using lstopo. See the Synthetic topologies + section in the documentation. + + Add hwloc_topology_set/get_userdata() to let the application save + a private pointer in the topology whenever it needs a way to find + its own object corresponding to a topology. + + Add hwloc_get_numanode_obj_by_os_index() and document that this function + as well as hwloc_get_pu_obj_by_os_index() are good at converting + nodesets and cpusets into objects. + + hwloc_distrib() does not ignore any objects anymore when there are + too many of them. They get merged with others instead. + Thanks to Tim Creech for reporting the issue. +* Tools + + hwloc-bind --get now executes the command after displaying + the binding instead of ignoring the command entirely. + Thanks to John Donners for the suggestion. + + Clarify that memory sizes shown in lstopo are local by default + unless specified (total memory added in the root object). +* Synthetic topologies + + Synthetic topology descriptions may now specify attributes such as + memory sizes and OS indexes. See the Synthetic topologies section + in the documentation. + + lstopo now exports in this fully-detailed format by default. + The new option --export-synthetic-flags may be used to revert + back the old format. +* Documentation + + Add the doc/examples/ subdirectory with several real-life examples, + including the already existing hwloc-hello.C for basics. + Thanks to Rob Aulwes for the suggestion. + + Improve the documentation of CPU and memory binding in the API. + + Add a FAQ entry about operating system errors, especially on AMD + platforms with buggy cache information. + + Add a FAQ entry about loading many topologies in a single program. +* Misc + + Work around buggy Linux kernels reporting 2 sockets instead + 1 socket with 2 NUMA nodes for each Xeon E5 v3 (Haswell) processor. + + pciutils/libpci support is now removed since libpciaccess works + well and there's also a Linux-specific PCI backend. For the record, + pciutils was GPL and therefore disabled by default since v1.6.2. + + Add --disable-cpuid configure flag to work around buggy processor + simulators reporting invalid CPUID information. + Thanks for Andrew Friedley for reporting the issue. + + Fix a racy use of libltdl when manipulating multiple topologies in + different threads. + Thanks to Andra Hugo for reporting the issue and testing patches. + + Fix some build failures in private/misc.h. + Thanks to Pavan Balaji and Ralph Castain for the reports. + + Fix failures to detect X11/Xutil.h on some Solaris platforms. + Thanks to Siegmar Gross for reporting the failure. + + The plugin ABI has changed, this release will not load plugins + built against previous hwloc releases. + + +Version 1.9.1 +------------- +* Fix a crash when the PCI locality is invalid. Attach to the root object + instead. Thanks to Nicolas Denoyelle for reporting the issue. +* Fix -f in lstopo manpage. Thanks to Jirka Hladky for reporting the issue. +* Fix hwloc_obj_type_sscanf() and others when strncasecmp() is not properly + available. Thanks to Nick Papior Andersen for reporting the problem. +* Mark Linux file descriptors as close-on-exec to avoid leaks on exec. +* Fix some minor memory leaks. + + +Version 1.9.0 +------------- +* API + + Add hwloc_obj_type_sscanf() to extend hwloc_obj_type_of_string() with + type-specific attributes such as Cache/Group depth and Cache type. + hwloc_obj_type_of_string() is moved to hwloc/deprecated.h. + + Add hwloc_linux_get_tid_last_cpu_location() for retrieving the + last CPU where a Linux thread given by TID ran. + + Add hwloc_distrib() to extend the old hwloc_distribute[v]() functions. + hwloc_distribute[v]() is moved to hwloc/deprecated.h. + + Don't mix total and local memory when displaying verbose object attributes + with hwloc_obj_attr_snprintf() or in lstopo. +* Backends + + Add CPUVendor, CPUModelNumber and CPUFamilyNumber info attributes for + x86, ia64 and Xeon Phi sockets on Linux, to extend the x86-specific + support added in v1.8.1. Requested by Ralph Castain. + + Add many CPU- and Platform-related info attributes on ARM and POWER + platforms, in the Machine and Socket objects. + + Add CUDA info attributes describing the number of multiprocessors and + cores and the size of the global, shared and L2 cache memories in CUDA + OS devices. + + Add OpenCL info attributes describing the number of compute units and + the global memory size in OpenCL OS devices. + + The synthetic backend now accepts extended types such as L2Cache, L1i or + Group3. lstopo also exports synthetic strings using these extended types. +* Tools + + lstopo + - Do not overwrite output files by default anymore. + Pass -f or --force to enforce it. + - Display OpenCL, CUDA and Xeon Phi numbers of cores and memory sizes + in the graphical output. + - Fix export to stdout when specifying a Cairo-based output type + with --of. + + hwloc-ps + - Add -e or --get-last-cpu-location to report where processes/threads + run instead of where they are bound. + - Report locations as likely-more-useful objects such as Cores or Sockets + instead of Caches when possible. + + hwloc-bind + - Fix failure on Windows when not using --pid. + - Add -e as a synonym to --get-last-cpu-location. + + hwloc-distrib + - Add --reverse to distribute using last objects first and singlify + into last bits first. Thanks to Jirka Hladky for the suggestion. + + hwloc-info + - Report unified caches when looking for data or instruction cache + ancestor objects. +* Misc + + Add experimental Visual Studio support under contrib/windows. + Thanks to Eloi Gaudry for his help and for providing the first draft. + + Fix some overzealous assertions and warnings about the ordering of + objects on a level with respect to cpusets. The ordering is only + guaranteed for complete cpusets (based on the first bit in sets). + + Fix some memory leaks when importing xml diffs and when exporting a + "too complex" entry. + + +Version 1.8.1 +------------- +* Fix the cpuid code on Windows 64bits so that the x86 backend gets + enabled as expected and can populate CPU information. + Thanks to Robin Scher for reporting the problem. +* Add CPUVendor/CPUModelNumber/CPUFamilyNumber attributes when running + on x86 architecture. Thanks to Ralph Castain for the suggestion. +* Work around buggy BIOS reporting duplicate NUMA nodes on Linux. + Thanks to Jeff Becker for reporting the problem and testing the patch. +* Add a name to the lstopo graphical window. Thanks to Michael Prokop + for reporting the issue. + + +Version 1.8.0 +------------- +* New components + + Add the "linuxpci" component that always works on Linux even when + libpciaccess and libpci aren't available (and even with a modified + file-system root). By default the old "pci" component runs first + because "linuxpci" lacks device names (obj->name is always NULL). +* API + + Add the topology difference API in hwloc/diff.h for manipulating + many similar topologies. + + Add hwloc_topology_dup() for duplicating an entire topology. + + hwloc.h and hwloc/helper.h have been reorganized to clarify the + documentation sections. The actual inline code has moved out of hwloc.h + into the new hwloc/inlines.h. + + Deprecated functions are now in hwloc/deprecated.h, and not in the + official documentation anymore. +* Tools + + Add hwloc-diff and hwloc-patch tools together with the new diff API. + + Add hwloc-compress-dir to (de)compress an entire directory of XML files + using hwloc-diff and hwloc-patch. + + Object colors in the graphical output of lstopo may be changed by adding + a "lstopoStyle" info attribute. See CUSTOM COLORS in the lstopo(1) manpage + for details. Thanks to Jirka Hladky for discussing the idea. + + hwloc-gather-topology may now gather I/O-related files on Linux when + --io is given. Only the linuxpci component supports discovering I/O + objects from these extended tarballs. + + hwloc-annotate now supports --ri to remove/replace info attributes with + a given name. + + hwloc-info supports "root" and "all" special locations for dumping + information about the root object. + + lstopo now supports --append-legend to append custom lines of text + to the legend in the graphical output. Thanks to Jirka Hladky for + discussing the idea. + + hwloc-calc and friends have a more robust parsing of locations given + on the command-line and they report useful error messages about it. + + Add --whole-system to hwloc-bind, hwloc-calc, hwloc-distances and + hwloc-distrib, and add --restrict to hwloc-bind for uniformity among + tools. +* Misc + + Calling hwloc_topology_load() or hwloc_topology_set_*() on an already + loaded topology now returns an error (deprecated since release 1.6.1). + + Fix the initialisation of cpusets and nodesets in Group objects added + when inserting PCI hostbridges. + + Never merge Group objects that were added explicitly by the user with + hwloc_custom_insert_group_object_by_parent(). + + Add a sanity check during dynamic plugin loading to prevent some + crashes when hwloc is dynamically loaded by another plugin mechanisms. + + Add --with-hwloc-plugins-path to specify the install/load directories + of plugins. + + Add the MICSerialNumber info attribute to the root object when running + hwloc inside a Xeon Phi to match the same attribute in the MIC OS device + when running in the host. + + +Version 1.7.2 +------------- +* Do not create invalid block OS devices on very old Linux kernel such + as RHEL4 2.6.9. +* Fix PCI subvendor/device IDs. +* Fix the management of Misc objects inserted by parent. + Thanks to Jirka Hladky for reporting the problem. +* Add a PortState into attribute to OpenFabrics OS devices. +* Add a MICSerialNumber info attribute to Xeon PHI/MIC OS devices. +* Improve verbose error messages when failing to load from XML. + + +Version 1.7.1 +------------- +* Fix a failed assertion in the distance grouping code when loading a XML + file that already contains some groups. + Thanks to Laercio Lima Pilla for reporting the problem. +* Remove unexpected Group objects when loading XML topologies with I/O + objects and NUMA distances. + Thanks to Elena Elkina for reporting the problem and testing patches. +* Fix PCI link speed discovery when using libpciaccess. +* Fix invalid libpciaccess virtual function device/vendor IDs when using + SR-IOV PCI devices on Linux. +* Fix GL component build with old NVCtrl releases. + Thanks to Jirka Hladky for reporting the problem. +* Fix embedding breakage caused by libltdl. + Thanks to Pavan Balaji for reporting the problem. +* Always use the system-wide libltdl instead of shipping one inside hwloc. +* Document issues when enabling plugins while embedding hwloc in another + project, in the documentation section Embedding hwloc in Other Software. +* Add a FAQ entry "How to get useful topology information on NetBSD?" + in the documentation. +* Somes fixes in the renaming code for embedding. +* Miscellaneous minor build fixes. + + +Version 1.7.0 +------------- +* New operating system backends + + Add BlueGene/Q compute node kernel (CNK) support. See the FAQ in the + documentation for details. Thanks to Jeff Hammond, Christopher Samuel + and Erik Schnetter for their help. + + Add NetBSD support, thanks to Aleksej Saushev. +* New I/O device discovery + + Add co-processor OS devices such as "mic0" for Intel Xeon Phi (MIC) + on Linux. Thanks to Jerome Vienne for helping. + + Add co-processor OS devices such as "cuda0" for NVIDIA CUDA-capable GPUs. + + Add co-processor OS devices such as "opencl0d0" for OpenCL GPU devices + on the AMD OpenCL implementation. + + Add GPU OS devices such as ":0.0" for NVIDIA X11 displays. + + Add GPU OS devices such as "nvml0" for NVIDIA GPUs. + Thanks to Marwan Abdellah and Stefan Eilemann for helping. + These new OS devices have some string info attributes such as CoProcType, + GPUModel, etc. to better identify them. + See the I/O Devices and Attributes documentation sections for details. +* New components + + Add the "opencl", "cuda", "nvml" and "gl" components for I/O device + discovery. + + "nvml" also improves the discovery of NVIDIA GPU PCIe link speed. + All of these new components may be built as plugins. They may also be + disabled entirely by passing --disable-opencl/cuda/nvml/gl to configure. + See the I/O Devices, Components and Plugins, and FAQ documentation + sections for details. +* API + + Add hwloc_topology_get_flags(). + + Add hwloc/plugins.h for building external plugins. + See the Adding new discovery components and plugins section. +* Interoperability + + Add hwloc/opencl.h, hwloc/nvml.h, hwloc/gl.h and hwloc/intel-mic.h + to retrieve the locality of OS devices that correspond to AMD OpenCL + GPU devices or indexes, to NVML devices or indexes, to NVIDIA X11 + displays, or to Intel Xeon Phi (MIC) device indexes. + + Add new helpers in hwloc/cuda.h and hwloc/cudart.h to convert + between CUDA devices or indexes and hwloc OS devices. + + Add hwloc_ibv_get_device_osdev() and clarify the requirements + of the OpenFabrics Verbs helpers in hwloc/openfabrics-verbs.h. +* Tools + + hwloc-info is not only a synonym of lstopo -s anymore, it also + dumps information about objects given on the command-line. +* Documentation + + Add a section "Existing components and plugins". + + Add a list of common OS devices in section "Software devices". + + Add a new FAQ entry "Why is lstopo slow?" about lstopo slowness + issues because of GPUs. + + Clarify the documentation of inline helpers in hwloc/myriexpress.h + and hwloc/openfabrics-verbs.h. +* Misc + + Improve cache detection on AIX. + + The HWLOC_COMPONENTS variable now excludes the components whose + names are prefixed with '-'. + + lstopo --ignore PU now works when displaying the topology in + graphical and textual mode (not when exporting to XML). + + Make sure I/O options always appear in lstopo usage, not only when + using pciutils/libpci. + + Remove some unneeded Linux specific includes from some interoperability + headers. + + Fix some inconsistencies in hwloc-distrib and hwloc-assembler-remote + manpages. Thanks to Guy Streeter for the report. + + Fix a memory leak on AIX when getting memory binding. + + Fix many small memory leaks on Linux. + + The `libpci' component is now called `pci' but the old name is still + accepted in the HWLOC_COMPONENTS variable for backward compatibility. + + +Version 1.6.2 +------------- +* Use libpciaccess instead of pciutils/libpci by default for I/O discovery. + pciutils/libpci is only used if --enable-libpci is given to configure + because its GPL license may taint hwloc. See the Installation section + in the documentation for details. +* Fix get_cpubind on Solaris when bound to a single PU with + processor_bind(). Thanks to Eugene Loh for reporting the problem + and providing a patch. + + +Version 1.6.1 +------------- +* Fix some crash or buggy detection in the x86 backend when Linux + cgroups/cpusets restrict the available CPUs. +* Fix the pkg-config output with --libs --static. + Thanks to Erik Schnetter for reporting one of the problems. +* Fix the output of hwloc-calc -H --hierarchical when using logical + indexes in the output. +* Calling hwloc_topology_load() multiple times on the same topology + is officially deprecated. hwloc will warn in such cases. +* Add some documentation about existing plugins/components, package + dependencies, and I/O devices specification on the command-line. + + +Version 1.6.0 +------------- +* Major changes + + Reorganize the backend infrastructure to support dynamic selection + of components and dynamic loading of plugins. For details, see the + new documentation section Components and plugins. + - The HWLOC_COMPONENTS variable lets one replace the default discovery + components. + - Dynamic loading of plugins may be enabled with --enable-plugins + (except on AIX and Windows). It will build libxml2 and libpci + support as separated modules. This helps reducing the dependencies + of the core hwloc library when distributed as a binary package. +* Backends + + Add CPUModel detection on Darwin and x86/FreeBSD. + Thanks to Robin Scher for providing ways to implement this. + + The x86 backend now adds CPUModel info attributes to socket objects + created by other backends that do not natively support this attribute. + + Fix detection on FreeBSD in case of cpuset restriction. Thanks to + Sebastian Kuzminsky for reporting the problem. +* XML + + Add hwloc_topology_set_userdata_import/export_callback(), + hwloc_export_obj_userdata() and _userdata_base64() to let + applications specify how to save/restore the custom data they placed + in the userdata private pointer field of hwloc objects. +* Tools + + Add hwloc-annotate program to add string info attributes to XML + topologies. + + Add --pid-cmd to hwloc-ps to append the output of a command to each + PID line. May be used for showing Open MPI process ranks, see the + hwloc-ps(1) manpage for details. + + hwloc-bind now exits with an error if binding fails; the executable + is not launched unless binding suceeeded or --force was given. + + Add --quiet to hwloc-calc and hwloc-bind to hide non-fatal error + messages. + + Fix command-line pid support in windows tools. + + All programs accept --verbose as a synonym to -v. +* Misc + + Fix some DIR descriptor leaks on Linux. + + Fix I/O device lists when some were filtered out after a XML import. + + Fix the removal of I/O objects when importing a I/O-enabled XML topology + without any I/O topology flag. + + When merging objects with HWLOC_IGNORE_TYPE_KEEP_STRUCTURE or + lstopo --merge, compare object types before deciding which one of two + identical object to remove (e.g. keep sockets in favor of caches). + + Add some GUID- and LID-related info attributes to OpenFabrics + OS devices. + + Only add CPUType socket attributes on Solaris/Sparc. Other cases + don't report reliable information (Solaris/x86), and a replacement + is available as the Architecture string info in the Machine object. + + Add missing Backend string info on Solaris in most cases. + + Document object attributes and string infos in a new Attributes + section in the documentation. + + Add a section about Synthetic topologies in the documentation. + + +Version 1.5.2 (some of these changes are in v1.6.2 but not in v1.6) +------------- +* Use libpciaccess instead of pciutils/libpci by default for I/O discovery. + pciutils/libpci is only used if --enable-libpci is given to configure + because its GPL license may taint hwloc. See the Installation section + in the documentation for details. +* Fix get_cpubind on Solaris when bound to a single PU with + processor_bind(). Thanks to Eugene Loh for reporting the problem + and providing a patch. +* Fix some DIR descriptor leaks on Linux. +* Fix I/O device lists when some were filtered out after a XML import. +* Add missing Backend string info on Solaris in most cases. +* Fix the removal of I/O objects when importing a I/O-enabled XML topology + without any I/O topology flag. +* Fix the output of hwloc-calc -H --hierarchical when using logical + indexes in the output. +* Fix the pkg-config output with --libs --static. + Thanks to Erik Schnetter for reporting one of the problems. + + +Version 1.5.1 +------------- +* Fix block OS device detection on Linux kernel 3.3 and later. + Thanks to Guy Streeter for reporting the problem and testing the fix. +* Fix the cpuid code in the x86 backend (for FreeBSD). Thanks to + Sebastian Kuzminsky for reporting problems and testing patches. +* Fix 64bit detection on FreeBSD. +* Fix some corner cases in the management of the thissystem flag with + respect to topology flags and environment variables. +* Fix some corner cases in command-line parsing checks in hwloc-distrib + and hwloc-distances. +* Make sure we do not miss some block OS devices on old Linux kernels + when a single PCI device has multiple IDE hosts/devices behind it. +* Do not disable I/O devices or instruction caches in hwloc-assembler output. + + +Version 1.5.0 +------------- +* Backends + + Do not limit the number of processors to 1024 on Solaris anymore. + + Gather total machine memory on FreeBSD. Thanks to Cyril Roelandt. + + XML topology files do not depend on the locale anymore. Float numbers + such as NUMA distances or PCI link speeds now always use a dot as a + decimal separator. + + Add instruction caches detection on Linux, AIX, Windows and Darwin. + + Add get_last_cpu_location() support for the current thread on AIX. + + Support binding on AIX when threads or processes were bound with + bindprocessor(). Thanks to Hendryk Bockelmann for reporting the issue + and testing patches, and to Farid Parpia for explaining the binding + interfaces. + + Improve AMD topology detection in the x86 backend (for FreeBSD) using + the topoext feature. +* API + + Increase HWLOC_API_VERSION to 0x00010500 so that API changes may be + detected at build-time. + + Add a cache type attribute describind Data, Instruction and Unified + caches. Caches with different types but same depth (for instance L1d + and L1i) are placed on different levels. + + Add hwloc_get_cache_type_depth() to retrieve the hwloc level depth of + of the given cache depth and type, for instance L1i or L2. + It helps disambiguating the case where hwloc_get_type_depth() returns + HWLOC_TYPE_DEPTH_MULTIPLE. + + Instruction caches are ignored unless HWLOC_TOPOLOGY_FLAG_ICACHES is + passed to hwloc_topology_set_flags() before load. + + Add hwloc_ibv_get_device_osdev_by_name() OpenFabrics helper in + openfabrics-verbs.h to find the hwloc OS device object corresponding to + an OpenFabrics device. +* Tools + + Add lstopo-no-graphics, a lstopo built without graphical support to + avoid dependencies on external libraries such as Cairo and X11. When + supported, graphical outputs are only available in the original lstopo + program. + - Packagers splitting lstopo and lstopo-no-graphics into different + packages are advised to use the alternatives system so that lstopo + points to the best available binary. + + Instruction caches are enabled in lstopo by default. Use --no-icaches + to disable them. + + Add -t/--threads to show threads in hwloc-ps. +* Removal of obsolete components + + Remove the old cpuset interface (hwloc/cpuset.h) which is deprecated and + superseded by the bitmap API (hwloc/bitmap.h) since v1.1. + hwloc_cpuset and nodeset types are still defined, but all hwloc_cpuset_* + compatibility wrappers are now gone. + + Remove Linux libnuma conversion helpers for the deprecated and + broken nodemask_t interface. + + Remove support for "Proc" type name, it was superseded by "PU" in v1.0. + + Remove hwloc-mask symlinks, it was replaced by hwloc-calc in v1.0. +* Misc + + Fix PCIe 3.0 link speed computation. + + Non-printable characters are dropped from strings during XML export. + + Fix importing of escaped characters with the minimalistic XML backend. + + Assert hwloc_is_thissystem() in several I/O related helpers. + + Fix some memory leaks in the x86 backend for FreeBSD. + + Minor fixes to ease native builds on Windows. + + Limit the number of retries when operating on all threads within a + process on Linux if the list of threads is heavily getting modified. + + +Version 1.4.3 +------------- +* This release is only meant to fix the pciutils license issue when upgrading + to hwloc v1.5 or later is not possible. It contains several other minor + fixes but ignores many of them that are only in v1.5 or later. +* Use libpciaccess instead of pciutils/libpci by default for I/O discovery. + pciutils/libpci is only used if --enable-libpci is given to configure + because its GPL license may taint hwloc. See the Installation section + in the documentation for details. +* Fix PCIe 3.0 link speed computation. +* Fix importing of escaped characters with the minimalistic XML backend. +* Fix a memory leak in the x86 backend. + + +Version 1.4.2 +------------- +* Fix build on Solaris 9 and earlier when fabsf() is not a compiler + built-in. Thanks to Igor Galić for reporting the problem. +* Fix support for more than 32 processors on Windows. Thanks to Hartmut + Kaiser for reporting the problem. +* Fix process-wide binding and cpulocation routines on Linux when some + threads disappear in the meantime. Thanks to Vlad Roubtsov for reporting + the issue. +* Make installed scripts executable. Thanks to Jirka Hladky for reporting + the problem. +* Fix libtool revision management when building for Windows. This fix was + also released as hwloc v1.4.1.1 Windows builds. Thanks to Hartmut Kaiser + for reporting the problem. +* Fix the __hwloc_inline keyword in public headers when compiling with a + C++ compiler. +* Add Port info attribute to network OS devices inside OpenFabrics PCI + devices so as to identify which interface corresponds to which port. +* Document requirements for interoperability helpers: I/O devices discovery + is required for some of them; the topology must match the current host + for most of them. + + +Version 1.4.1 +------------- +* This release contains all changes from v1.3.2. +* Fix hwloc_alloc_membind, thanks Karl Napf for reporting the issue. +* Fix memory leaks in some get_membind() functions. +* Fix helpers converting from Linux libnuma to hwloc (hwloc/linux-libnuma.h) + in case of out-of-order NUMA node ids. +* Fix some overzealous assertions in the distance grouping code. +* Workaround BIOS reporting empty I/O locality in CUDA and OpenFabrics + helpers on Linux. Thanks to Albert Solernou for reporting the problem. +* Install a valgrind suppressions file hwloc-valgrind.supp (see the FAQ). +* Fix memory binding documentation. Thanks to Karl Napf for reporting the + issues. + + +Version 1.4.0 (does not contain all v1.3.2 changes) +------------- +* Major features + + Add "custom" interface and "assembler" tools to build multi-node + topology. See the Multi-node Topologies section in the documentation + for details. +* Interface improvements + + Add symmetric_subtree object attribute to ease assumptions when consulting + regular symmetric topologies. + + Add a CPUModel and CPUType info attribute to Socket objects on Linux + and Solaris. + + Add hwloc_get_obj_index_inside_cpuset() to retrieve the "logical" index + of an object within a subtree of the topology. + + Add more NVIDIA CUDA helpers in cuda.h and cudart.h to find hwloc objects + corresponding to CUDA devices. +* Discovery improvements + + Add a group object above partial distance matrices to make sure + the matrices are available in the final topology, except when this + new object would contradict the existing hierarchy. + + Grouping by distances now also works when loading from XML. + + Fix some corner cases in object insertion, for instance when dealing + with NUMA nodes without any CPU. +* Backends + + Implement hwloc_get_area_membind() on Linux. + + Honor I/O topology flags when importing from XML. + + Further improve XML-related error checking and reporting. + + Hide synthetic topology error messages unless HWLOC_SYNTHETIC_VERBOSE=1. +* Tools + + Add synthetic exporting of symmetric topologies to lstopo. + + lstopo --horiz and --vert can now be applied to some specific object types. + + lstopo -v -p now displays distance matrices with physical indexes. + + Add hwloc-distances utility to list distances. +* Documentation + + Fix and/or document the behavior of most inline functions in hwloc/helper.h + when the topology contains some I/O or Misc objects. + + Backend documentation enhancements. +* Bug fixes + + Fix missing last bit in hwloc_linux_get_thread_cpubind(). + Thanks to Carolina Gómez-Tostón Gutiérrez for reporting the issue. + + Fix FreeBSD build without cpuid support. + + Fix several Windows build issues. + + Fix inline keyword definition in public headers. + + Fix dependencies in the embedded library. + + Improve visibility support detection. Thanks to Dave Love for providing + the patch. + + Remove references to internal symbols in the tools. + + +Version 1.3.3 +------------- +* This release is only meant to fix the pciutils license issue when upgrading + to hwloc v1.4 or later is not possible. It contains several other minor + fixes but ignores many of them that are only in v1.4 or later. +* Use libpciaccess instead of pciutils/libpci by default for I/O discovery. + pciutils/libpci is only used if --enable-libpci is given to configure + because its GPL license may taint hwloc. See the Installation section + in the documentation for details. + + +Version 1.3.2 +------------- +* Fix missing last bit in hwloc_linux_get_thread_cpubind(). + Thanks to Carolina Gómez-Tostón Gutiérrez for reporting the issue. +* Fix build with -mcmodel=medium. Thanks to Devendar Bureddy for reporting + the issue. +* Fix build with Solaris Studio 12 compiler when XML is disabled. + Thanks to Paul H. Hargrove for reporting the problem. +* Fix installation with old GNU sed, for instance on Red Hat 8. + Thanks to Paul H. Hargrove for reporting the problem. +* Fix PCI locality when Linux cgroups restrict the available CPUs. +* Fix floating point issue when grouping by distance on mips64 architecture. + Thanks to Paul H. Hargrove for reporting the problem. +* Fix conversion from/to Linux libnuma when some NUMA nodes have no memory. +* Fix support for gccfss compilers with broken ffs() support. Thanks to + Paul H. Hargrove for reporting the problem and providing a patch. +* Fix FreeBSD build without cpuid support. +* Fix several Windows build issues. +* Fix inline keyword definition in public headers. +* Fix dependencies in the embedded library. +* Detect when a compiler such as xlc may not report compile errors + properly, causing some configure checks to be wrong. Thanks to + Paul H. Hargrove for reporting the problem and providing a patch. +* Improve visibility support detection. Thanks to Dave Love for providing + the patch. +* Remove references to internal symbols in the tools. +* Fix installation on systems with limited command-line size. + Thanks to Paul H. Hargrove for reporting the problem. +* Further improve XML-related error checking and reporting. + + +Version 1.3.1 +------------- +* Fix pciutils detection with pkg-config when not installed in standard + directories. +* Fix visibility options detection with the Solaris Studio compiler. + Thanks to Igor Galić and Terry Dontje for reporting the problems. +* Fix support for old Linux sched.h headers such as those found + on Red Hat 8. Thanks to Paul H. Hargrove for reporting the problems. +* Fix inline and attribute support for Solaris compilers. Thanks to + Dave Love for reporting the problems. +* Print a short summary at the end of the configure output. Thanks to + Stefan Eilemann for the suggestion. +* Add --disable-libnuma configure option to disable libnuma-based + memory binding support on Linux. Thanks to Rayson Ho for the + suggestion. +* Make hwloc's configure script properly obey $PKG_CONFIG. Thanks to + Nathan Phillip Brink for raising the issue. +* Silence some harmless pciutils warnings, thanks to Paul H. Hargrove + for reporting the problem. +* Fix the documentation with respect to hwloc_pid_t and hwloc_thread_t + being either pid_t and pthread_t on Unix, or HANDLE on Windows. + + +Version 1.3.0 +------------- +* Major features + + Add I/O devices and bridges to the topology using the pciutils + library. Only enabled after setting the relevant flag with + hwloc_topology_set_flags() before hwloc_topology_load(). See the + I/O Devices section in the documentation for details. +* Discovery improvements + + Add associativity to the cache attributes. + + Add support for s390/z11 "books" on Linux. + + Add the HWLOC_GROUPING_ACCURACY environment variable to relax + distance-based grouping constraints. See the Environment Variables + section in the documentation for details about grouping behavior + and configuration. + + Allow user-given distance matrices to remove or replace those + discovered by the OS backend. +* XML improvements + + XML is now always supported: a minimalistic custom import/export + code is used when libxml2 is not available. It is only guaranteed + to read XML files generated by hwloc. + + hwloc_topology_export_xml() and export_xmlbuffer() now return an + integer. + + Add hwloc_free_xmlbuffer() to free the buffer allocated by + hwloc_topology_export_xmlbuffer(). + + Hide XML topology error messages unless HWLOC_XML_VERBOSE=1. +* Minor API updates + + Add hwloc_obj_add_info to customize object info attributes. +* Tools + + lstopo now displays I/O devices by default. Several options are + added to configure the I/O discovery. + + hwloc-calc and hwloc-bind now accept I/O devices as input. + + Add --restrict option to hwloc-calc and hwloc-distribute. + + Add --sep option to change the output field separator in hwloc-calc. + + Add --whole-system option to hwloc-ps. + + +Version 1.2.2 +------------- +* Fix build on AIX 5.2, thanks Utpal Kumar Ray for the report. +* Fix XML import of very large page sizes or counts on 32bits platform, + thanks to Karsten Hopp for the RedHat ticket. +* Fix crash when administrator limitations such as Linux cgroup require + to restrict distance matrices. Thanks to Ake Sandgren for reporting the + problem. +* Fix the removal of objects such as AMD Magny-Cours dual-node sockets + in case of administrator restrictions. +* Improve error reporting and messages in case of wrong synthetic topology + description. +* Several other minor internal fixes and documentation improvements. + + +Version 1.2.1 +------------- +* Improve support of AMD Bulldozer "Compute-Unit" modules by detecting + logical processors with different core IDs on Linux. +* Fix hwloc-ps crash when listing processes from another Linux cpuset. + Thanks to Carl Smith for reporting the problem. +* Fix build on AIX and Solaris. Thanks to Carl Smith and Andreas Kupries + for reporting the problems. +* Fix cache size detection on Darwin. Thanks to Erkcan Özcan for reporting + the problem. +* Make configure fail if --enable-xml or --enable-cairo is given and + proper support cannot be found. Thanks to Andreas Kupries for reporting + the XML problem. +* Fix spurious L1 cache detection on AIX. Thanks to Hendryk Bockelmann + for reporting the problem. +* Fix hwloc_get_last_cpu_location(THREAD) on Linux. Thanks to Gabriele + Fatigati for reporting the problem. +* Fix object distance detection on Solaris. +* Add pthread_self weak symbol to ease static linking. +* Minor documentation fixes. + + +Version 1.2.0 +------------- +* Major features + + Expose latency matrices in the API as an array of distance structures + within objects. Add several helpers to find distances. + + Add hwloc_topology_set_distance_matrix() and environment variables + to provide a matrix of distances between a given set of objects. + + Add hwloc_get_last_cpu_location() and hwloc_get_proc_last_cpu_location() + to retrieve the processors where a process or thread recently ran. + - Add the corresponding --get-last-cpu-location option to hwloc-bind. + + Add hwloc_topology_restrict() to restrict an existing topology to a + given cpuset. + - Add the corresponding --restrict option to lstopo. +* Minor API updates + + Add hwloc_bitmap_list_sscanf/snprintf/asprintf to convert between bitmaps + and strings such as 4-5,7-9,12,15- + + hwloc_bitmap_set/clr_range() now support infinite ranges. + + Clarify the difference between inserting Misc objects by cpuset or by + parent. + + hwloc_insert_misc_object_by_cpuset() now returns NULL in case of error. +* Discovery improvements + + x86 backend (for freebsd): add x2APIC support + + Support standard device-tree phandle, to get better support on e.g. ARM + systems providing it. + + Detect cache size on AIX. Thanks Christopher and IBM. + + Improve grouping to support asymmetric topologies. +* Tools + + Command-line tools now support "all" and "root" special locations + consisting in the entire topology, as well as type names with depth + attributes such as L2 or Group4. + + hwloc-calc improvements: + - Add --number-of/-N option to report the number of objects of a given + type or depth. + - -I is now equivalent to --intersect for listing the indexes of + objects of a given type or depth that intersects the input. + - Add -H to report the output as a hierarchical combination of types + and depths. + + Add --thissystem to lstopo. + + Add lstopo-win, a console-less lstopo variant on Windows. +* Miscellaneous + + Remove C99 usage from code base. + + Rename hwloc-gather-topology.sh into hwloc-gather-topology + + Fix AMD cache discovery on freebsd when there is no L3 cache, thanks + Andriy Gapon for the fix. + + +Version 1.1.2 +------------- +* Fix a segfault in the distance-based grouping code when some objects + are not placed in any group. Thanks to Bernd Kallies for reporting + the problem and providing a patch. +* Fix the command-line parsing of hwloc-bind --mempolicy interleave. + Thanks to Guy Streeter for reporting the problem. +* Stop truncating the output in hwloc_obj_attr_snprintf() and in the + corresponding lstopo output. Thanks to Guy Streeter for reporting the + problem. +* Fix object levels ordering in synthetic topologies. +* Fix potential incoherency between device tree and kernel information, + when SMT is disabled on Power machines. +* Fix and document the behavior of hwloc_topology_set_synthetic() in case + of invalid argument. Thanks to Guy Streeter for reporting the problem. +* Add some verbose error message reporting when it looks like the OS + gives erroneous information. +* Do not include unistd.h and stdint.h in public headers on Windows. +* Move config.h files into their own subdirectories to avoid name + conflicts when AC_CONFIG_HEADERS adds -I's for them. +* Remove the use of declaring variables inside "for" loops. +* Some other minor fixes. +* Many minor documentation fixes. + + +Version 1.1.1 +------------- +* Add hwloc_get_api_version() which returns the version of hwloc used + at runtime. Thanks to Guy Streeter for the suggestion. +* Fix the number of hugepages reported for NUMA nodes on Linux. +* Fix hwloc_bitmap_to_ulong() right after allocating the bitmap. + Thanks to Bernd Kallies for reporting the problem. +* Fix hwloc_bitmap_from_ith_ulong() to properly zero the first ulong. + Thanks to Guy Streeter for reporting the problem. +* Fix hwloc_get_membind_nodeset() on Linux. + Thanks to Bernd Kallies for reporting the problem and providing a patch. +* Fix some file descriptor leaks in the Linux discovery. +* Fix the minimum width of NUMA nodes, caches and the legend in the graphical + lstopo output. Thanks to Jirka Hladky for reporting the problem. +* Various fixes to bitmap conversion from/to taskset-strings. +* Fix and document snprintf functions behavior when the buffer size is too + small or zero. Thanks to Guy Streeter for reporting the problem. +* Fix configure to avoid spurious enabling of the cpuid backend. + Thanks to Tim Anderson for reporting the problem. +* Cleanup error management in hwloc-gather-topology.sh. + Thanks to Jirka Hladky for reporting the problem and providing a patch. +* Add a manpage and usage for hwloc-gather-topology.sh on Linux. + Thanks to Jirka Hladky for providing a patch. +* Memory binding documentation enhancements. + + +Version 1.1.0 +------------- + +* API + + Increase HWLOC_API_VERSION to 0x00010100 so that API changes may be + detected at build-time. + + Add a memory binding interface. + + The cpuset API (hwloc/cpuset.h) is now deprecated. It is replaced by + the bitmap API (hwloc/bitmap.h) which offers the same features with more + generic names since it applies to CPU sets, node sets and more. + Backward compatibility with the cpuset API and ABI is still provided but + it will be removed in a future release. + Old types (hwloc_cpuset_t, ...) are still available as a way to clarify + what kind of hwloc_bitmap_t each API function manipulates. + Upgrading to the new API only requires to replace hwloc_cpuset_ function + calls with the corresponding hwloc_bitmap_ calls, with the following + renaming exceptions: + - hwloc_cpuset_cpu -> hwloc_bitmap_only + - hwloc_cpuset_all_but_cpu -> hwloc_bitmap_allbut + - hwloc_cpuset_from_string -> hwloc_bitmap_sscanf + + Add an `infos' array in each object to store couples of info names and + values. It enables generic storage of things like the old dmi board infos + that were previously stored in machine specific attributes. + + Add linesize cache attribute. +* Features + + Bitmaps (and thus CPU sets and node sets) are dynamically (re-)allocated, + the maximal number of CPUs (HWLOC_NBMAXCPUS) has been removed. + + Improve the distance-based grouping code to better support irregular + distance matrices. + + Add support for device-tree to get cache information (useful on Power + architectures). +* Helpers + + Add NVIDIA CUDA helpers in cuda.h and cudart.h to ease interoperability + with CUDA Runtime and Driver APIs. + + Add Myrinet Express helper in myriexpress.h to ease interoperability. +* Tools + + lstopo now displays physical/OS indexes by default in graphical mode + (use -l to switch back to logical indexes). The textual output still uses + logical by default (use -p to switch to physical indexes). + + lstopo prefixes logical indexes with `L#' and physical indexes with `P#'. + Physical indexes are also printed as `P#N' instead of `phys=N' within + object attributes (in parentheses). + + Add a legend at the bottom of the lstopo graphical output, use --no-legend + to remove it. + + Add hwloc-ps to list process' bindings. + + Add --membind and --mempolicy options to hwloc-bind. + + Improve tools command-line options by adding a generic --input option + (and more) which replaces the old --xml, --synthetic and --fsys-root. + + Cleanup lstopo output configuration by adding --output-format. + + Add --intersect in hwloc-calc, and replace --objects with --largest. + + Add the ability to work on standard input in hwloc-calc. + + Add --from, --to and --at in hwloc-distrib. + + Add taskset-specific functions and command-line tools options to + manipulate CPU set strings in the format of the taskset program. + + Install hwloc-gather-topology.sh on Linux. + + +Version 1.0.3 +------------- + +* Fix support for Linux cpuset when emulated by a cgroup mount point. +* Remove unneeded runtime dependency on libibverbs.so in the library and + all utils programs. +* Fix hwloc_cpuset_to_linux_libnuma_ulongs in case of non-linear OS-indexes + for NUMA nodes. +* lstopo now displays physical/OS indexes by default in graphical mode + (use -l to switch back to logical indexes). The textual output still uses + logical by default (use -p to switch to physical indexes). + + +Version 1.0.2 +------------- + +* Public headers can now be included directly from C++ programs. +* Solaris fix for non-contiguous cpu numbers. Thanks to Rolf vandeVaart for + reporting the issue. +* Darwin 10.4 fix. Thanks to Olivier Cessenat for reporting the issue. +* Revert 1.0.1 patch that ignored sockets with unknown ID values since it + only slightly helped POWER7 machines with old Linux kernels while it + prevents recent kernels from getting the complete POWER7 topology. +* Fix hwloc_get_common_ancestor_obj(). +* Remove arch-specific bits in public headers. +* Some fixes in the lstopo graphical output. +* Various man page clarifications and minor updates. + + +Version 1.0.1 +------------- + +* Various Solaris fixes. Thanks to Yannick Martin for reporting the issue. +* Fix "non-native" builds on x86 platforms (e.g., when building 32 + bit executables with compilers that natively build 64 bit). +* Ignore sockets with unknown ID values (which fixes issues on POWER7 + machines). Thanks to Greg Bauer for reporting the issue. +* Various man page clarifications and minor updates. +* Fixed memory leaks in hwloc_setup_group_from_min_distance_clique(). +* Fix cache type filtering on MS Windows 7. Thanks to Αλέξανδρος + Παπαδογιαννάκ for reporting the issue. +* Fixed warnings when compiling with -DNDEBUG. + + +Version 1.0.0 +------------- + +* The ABI of the library has changed. +* Backend updates + + Add FreeBSD support. + + Add x86 cpuid based backend. + + Add Linux cgroup support to the Linux cpuset code. + + Support binding of entire multithreaded process on Linux. + + Fix and enable Group support in Windows. + + Cleanup XML export/import. +* Objects + + HWLOC_OBJ_PROC is renamed into HWLOC_OBJ_PU for "Processing Unit", + its stringified type name is now "PU". + + Use new HWLOC_OBJ_GROUP objects instead of MISC when grouping + objects according to NUMA distances or arbitrary OS aggregation. + + Rework memory attributes. + + Add different cpusets in each object to specify processors that + are offline, unavailable, ... + + Cleanup the storage of object names and DMI infos. +* Features + + Add support for looking up specific PID topology information. + + Add hwloc_topology_export_xml() to export the topology in a XML file. + + Add hwloc_topology_get_support() to retrieve the supported features + for the current topology context. + + Support non-SYSTEM object as the root of the tree, use MACHINE in + most common cases. + + Add hwloc_get_*cpubind() routines to retrieve the current binding + of processes and threads. +* API + + Add HWLOC_API_VERSION to help detect the currently used API version. + + Add missing ending "e" to *compare* functions. + + Add several routines to emulate PLPA functions. + + Rename and rework the cpuset and/or/xor/not/clear operators to output + their result in a dedicated argument instead of modifying one input. + + Deprecate hwloc_obj_snprintf() in favor of hwloc_obj_type/attr_snprintf(). + + Clarify the use of parent and ancestor in the API, do not use father. + + Replace hwloc_get_system_obj() with hwloc_get_root_obj(). + + Return -1 instead of HWLOC_OBJ_TYPE_MAX in the API since the latter + isn't public. + + Relax constraints in hwloc_obj_type_of_string(). + + Improve displaying of memory sizes. + + Add 0x prefix to cpuset strings. +* Tools + + lstopo now displays logical indexes by default, use --physical to + revert back to OS/physical indexes. + + Add colors in the lstopo graphical outputs to distinguish between online, + offline, reserved, ... objects. + + Extend lstopo to show cpusets, filter objects by type, ... + + Renamed hwloc-mask into hwloc-calc which supports many new options. +* Documentation + + Add a hwloc(7) manpage containing general information. + + Add documentation about how to switch from PLPA to hwloc. + + Cleanup the distributed documentation files. +* Miscellaneous + + Many compilers warning fixes. + + Cleanup the ABI by using the visibility attribute. + + Add project embedding support. + + +Version 0.9.4 (unreleased) +-------------------------- + +* Fix reseting colors to normal in lstopo -.txt output. +* Fix Linux pthread_t binding error report. + + +Version 0.9.3 +------------- + +* Fix autogen.sh to work with Autoconf 2.63. +* Fix various crashes in particular conditions: + - xml files with root attributes + - offline CPUs + - partial sysfs support + - unparseable /proc/cpuinfo + - ignoring NUMA level while Misc level have been generated +* Tweak documentation a bit +* Do not require the pthread library for binding the current thread on Linux +* Do not erroneously consider the sched_setaffinity prototype is the old version + when there is actually none. +* Fix _syscall3 compilation on archs for which we do not have the + sched_setaffinity system call number. +* Fix AIX binding. +* Fix libraries dependencies: now only lstopo depends on libtermcap, fix + binutils-gold link +* Have make check always build and run hwloc-hello.c +* Do not limit size of a cpuset. + + +Version 0.9.2 +------------- + +* Trivial documentation changes. + + +Version 0.9.1 +------------- + +* Re-branded to "hwloc" and moved to the Open MPI project, relicensed under the + BSD license. +* The prefix of all functions and tools is now hwloc, and some public + functions were also renamed for real. +* Group NUMA nodes into Misc objects according to their physical distance + that may be reported by the OS/BIOS. + May be ignored by setting HWLOC_IGNORE_DISTANCES=1 in the environment. +* Ignore offline CPUs on Solaris. +* Improved binding support on AIX. +* Add HP-UX support. +* CPU sets are now allocated/freed dynamically. +* Add command line options to tune the lstopo graphical output, add + semi-graphical textual output +* Extend topobind to support multiple cpusets or objects on the command + line as topomask does. +* Add an Infiniband-specific helper hwloc/openfabrics-verbs.h to retrieve + the physical location of IB devices. + + +Version 0.9 (libtopology) +------------------------- + +* First release. diff --git a/src/3rdparty/hwloc/README b/src/3rdparty/hwloc/README new file mode 100644 index 00000000..5567b4d1 --- /dev/null +++ b/src/3rdparty/hwloc/README @@ -0,0 +1,85 @@ +Introduction + +The Hardware Locality (hwloc) software project aims at easing the process of +discovering hardware resources in parallel architectures. It offers +command-line tools and a C API for consulting these resources, their locality, +attributes, and interconnection. hwloc primarily aims at helping +high-performance computing (HPC) applications, but is also applicable to any +project seeking to exploit code and/or data locality on modern computing +platforms. + +hwloc is actually made of two subprojects distributed together: + + * The original hwloc project for describing the internals of computing nodes. + It is described in details starting at section Hardware Locality (hwloc) + Introduction. + * The network-oriented companion called netloc (Network Locality), described + in details starting with section Network Locality (netloc). + +See also the Related pages tab above for links to other sections. + +Netloc may be disabled, but the original hwloc cannot. Both hwloc and netloc +APIs are documented after these sections. + +Installation + +hwloc (http://www.open-mpi.org/projects/hwloc/) is available under the BSD +license. It is hosted as a sub-project of the overall Open MPI project (http:// +www.open-mpi.org/). Note that hwloc does not require any functionality from +Open MPI -- it is a wholly separate (and much smaller!) project and code base. +It just happens to be hosted as part of the overall Open MPI project. + +Basic Installation + +Installation is the fairly common GNU-based process: + +shell$ ./configure --prefix=... +shell$ make +shell$ make install + +hwloc- and netloc-specific configure options and requirements are documented in +sections hwloc Installation and Netloc Installation respectively. + +Also note that if you install supplemental libraries in non-standard locations, +hwloc's configure script may not be able to find them without some help. You +may need to specify additional CPPFLAGS, LDFLAGS, or PKG_CONFIG_PATH values on +the configure command line. + +For example, if libpciaccess was installed into /opt/pciaccess, hwloc's +configure script may not find it be default. Try adding PKG_CONFIG_PATH to the +./configure command line, like this: + +./configure PKG_CONFIG_PATH=/opt/pciaccess/lib/pkgconfig ... + +Running the "lstopo" tool is a good way to check as a graphical output whether +hwloc properly detected the architecture of your node. Netloc command-line +tools can be used to display the network topology interconnecting your nodes. + +Installing from a Git clone + +Additionally, the code can be directly cloned from Git: + +shell$ git clone https://github.com/open-mpi/hwloc.git +shell$ cd hwloc +shell$ ./autogen.sh + +Note that GNU Autoconf >=2.63, Automake >=1.11 and Libtool >=2.2.6 are required +when building from a Git clone. + +Nightly development snapshots are available on the web site, they can be +configured and built without any need for Git or GNU Autotools. + +Questions and Bugs + +Bugs should be reported in the tracker (https://github.com/open-mpi/hwloc/ +issues). Opening a new issue automatically displays lots of hints about how to +debug and report issues. + +Questions may be sent to the users or developers mailing lists (http:// +www.open-mpi.org/community/lists/hwloc.php). + +There is also a #hwloc IRC channel on Freenode (irc.freenode.net). + + + +See https://www.open-mpi.org/projects/hwloc/doc/ for more hwloc documentation. diff --git a/src/3rdparty/hwloc/VERSION b/src/3rdparty/hwloc/VERSION new file mode 100644 index 00000000..5ebc6bb4 --- /dev/null +++ b/src/3rdparty/hwloc/VERSION @@ -0,0 +1,47 @@ +# This is the VERSION file for hwloc, describing the precise version +# of hwloc in this distribution. The various components of the version +# number below are combined to form a single version number string. + +# major, minor, and release are generally combined in the form +# ... If release is zero, then it is omitted. + +# Please update HWLOC_VERSION* in contrib/windows/hwloc_config.h too. + +major=2 +minor=0 +release=4 + +# greek is used for alpha or beta release tags. If it is non-empty, +# it will be appended to the version number. It does not have to be +# numeric. Common examples include a1 (alpha release 1), b1 (beta +# release 1), sc2005 (Super Computing 2005 release). The only +# requirement is that it must be entirely printable ASCII characters +# and have no white space. + +greek= + +# The date when this release was created + +date="Jun 03, 2019" + +# If snapshot=1, then use the value from snapshot_version as the +# entire hwloc version (i.e., ignore major, minor, release, and +# greek). This is only set to 1 when making snapshot tarballs. +snapshot=0 +snapshot_version=${major}.${minor}.${release}${greek}-git + +# The shared library version of hwloc's public library. This version +# is maintained in accordance with the "Library Interface Versions" +# chapter from the GNU Libtool documentation. Notes: + +# 1. Since version numbers are associated with *releases*, the version +# number maintained on the hwloc git master (and developer branches) +# is always 0:0:0. + +# 2. Version numbers are described in the Libtool current:revision:age +# format. + +libhwloc_so_version=15:3:0 +libnetloc_so_version=0:0:0 + +# Please also update the lines in contrib/windows/libhwloc.vcxproj diff --git a/src/3rdparty/hwloc/include/hwloc.h b/src/3rdparty/hwloc/include/hwloc.h new file mode 100644 index 00000000..ee6da6fd --- /dev/null +++ b/src/3rdparty/hwloc/include/hwloc.h @@ -0,0 +1,2270 @@ +/* + * Copyright © 2009 CNRS + * Copyright © 2009-2019 Inria. All rights reserved. + * Copyright © 2009-2012 Université Bordeaux + * Copyright © 2009-2011 Cisco Systems, Inc. All rights reserved. + * See COPYING in top-level directory. + */ + +/*===================================================================== + * PLEASE GO READ THE DOCUMENTATION! + * ------------------------------------------------ + * $tarball_directory/doc/doxygen-doc/ + * or + * http://www.open-mpi.org/projects/hwloc/doc/ + *===================================================================== + * + * FAIR WARNING: Do NOT expect to be able to figure out all the + * subtleties of hwloc by simply reading function prototypes and + * constant descrptions here in this file. + * + * Hwloc has wonderful documentation in both PDF and HTML formats for + * your reading pleasure. The formal documentation explains a LOT of + * hwloc-specific concepts, provides definitions, and discusses the + * "big picture" for many of the things that you'll find here in this + * header file. + * + * The PDF/HTML documentation was generated via Doxygen; much of what + * you'll see in there is also here in this file. BUT THERE IS A LOT + * THAT IS IN THE PDF/HTML THAT IS ***NOT*** IN hwloc.h! + * + * There are entire paragraph-length descriptions, discussions, and + * pretty prictures to explain subtle corner cases, provide concrete + * examples, etc. + * + * Please, go read the documentation. :-) + * + * Moreover there are several examples of hwloc use under doc/examples + * in the source tree. + * + *=====================================================================*/ + +/** \file + * \brief The hwloc API. + * + * See hwloc/bitmap.h for bitmap specific macros. + * See hwloc/helper.h for high-level topology traversal helpers. + * See hwloc/inlines.h for the actual inline code of some functions below. + * See hwloc/export.h for exporting topologies to XML or to synthetic descriptions. + * See hwloc/distances.h for querying and modifying distances between objects. + * See hwloc/diff.h for manipulating differences between similar topologies. + */ + +#ifndef HWLOC_H +#define HWLOC_H + +#include +#include +#include +#include +#include + +/* + * Symbol transforms + */ +#include + +/* + * Bitmap definitions + */ + +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + +/** \defgroup hwlocality_api_version API version + * @{ + */ + +/** \brief Indicate at build time which hwloc API version is being used. + * + * This number is updated to (X<<16)+(Y<<8)+Z when a new release X.Y.Z + * actually modifies the API. + * + * Users may check for available features at build time using this number + * (see \ref faq_upgrade). + * + * \note This should not be confused with HWLOC_VERSION, the library version. + * Two stable releases of the same series usually have the same ::HWLOC_API_VERSION + * even if their HWLOC_VERSION are different. + */ +#define HWLOC_API_VERSION 0x00020000 + +/** \brief Indicate at runtime which hwloc API version was used at build time. + * + * Should be ::HWLOC_API_VERSION if running on the same version. + */ +HWLOC_DECLSPEC unsigned hwloc_get_api_version(void); + +/** \brief Current component and plugin ABI version (see hwloc/plugins.h) */ +#define HWLOC_COMPONENT_ABI 5 + +/** @} */ + + + +/** \defgroup hwlocality_object_sets Object Sets (hwloc_cpuset_t and hwloc_nodeset_t) + * + * Hwloc uses bitmaps to represent two distinct kinds of object sets: + * CPU sets (::hwloc_cpuset_t) and NUMA node sets (::hwloc_nodeset_t). + * These types are both typedefs to a common back end type + * (::hwloc_bitmap_t), and therefore all the hwloc bitmap functions + * are applicable to both ::hwloc_cpuset_t and ::hwloc_nodeset_t (see + * \ref hwlocality_bitmap). + * + * The rationale for having two different types is that even though + * the actions one wants to perform on these types are the same (e.g., + * enable and disable individual items in the set/mask), they're used + * in very different contexts: one for specifying which processors to + * use and one for specifying which NUMA nodes to use. Hence, the + * name difference is really just to reflect the intent of where the + * type is used. + * + * @{ + */ + +/** \brief A CPU set is a bitmap whose bits are set according to CPU + * physical OS indexes. + * + * It may be consulted and modified with the bitmap API as any + * ::hwloc_bitmap_t (see hwloc/bitmap.h). + * + * Each bit may be converted into a PU object using + * hwloc_get_pu_obj_by_os_index(). + */ +typedef hwloc_bitmap_t hwloc_cpuset_t; +/** \brief A non-modifiable ::hwloc_cpuset_t. */ +typedef hwloc_const_bitmap_t hwloc_const_cpuset_t; + +/** \brief A node set is a bitmap whose bits are set according to NUMA + * memory node physical OS indexes. + * + * It may be consulted and modified with the bitmap API as any + * ::hwloc_bitmap_t (see hwloc/bitmap.h). + * Each bit may be converted into a NUMA node object using + * hwloc_get_numanode_obj_by_os_index(). + * + * When binding memory on a system without any NUMA node, + * the single main memory bank is considered as NUMA node #0. + * + * See also \ref hwlocality_helper_nodeset_convert. + */ +typedef hwloc_bitmap_t hwloc_nodeset_t; +/** \brief A non-modifiable ::hwloc_nodeset_t. + */ +typedef hwloc_const_bitmap_t hwloc_const_nodeset_t; + +/** @} */ + + + +/** \defgroup hwlocality_object_types Object Types + * @{ + */ + +/** \brief Type of topology object. + * + * \note Do not rely on the ordering or completeness of the values as new ones + * may be defined in the future! If you need to compare types, use + * hwloc_compare_types() instead. + */ +#define HWLOC_OBJ_TYPE_MIN HWLOC_OBJ_MACHINE /**< \private Sentinel value */ +typedef enum { + HWLOC_OBJ_MACHINE, /**< \brief Machine. + * A set of processors and memory with cache + * coherency. + * + * This type is always used for the root object of a topology, + * and never used anywhere else. + * Hence its parent is always \c NULL. + */ + + HWLOC_OBJ_PACKAGE, /**< \brief Physical package. + * The physical package that usually gets inserted + * into a socket on the motherboard. + * A processor package usually contains multiple cores. + */ + HWLOC_OBJ_CORE, /**< \brief Core. + * A computation unit (may be shared by several + * logical processors). + */ + HWLOC_OBJ_PU, /**< \brief Processing Unit, or (Logical) Processor. + * An execution unit (may share a core with some + * other logical processors, e.g. in the case of + * an SMT core). + * + * This is the smallest object representing CPU resources, + * it cannot have any child except Misc objects. + * + * Objects of this kind are always reported and can + * thus be used as fallback when others are not. + */ + + HWLOC_OBJ_L1CACHE, /**< \brief Level 1 Data (or Unified) Cache. */ + HWLOC_OBJ_L2CACHE, /**< \brief Level 2 Data (or Unified) Cache. */ + HWLOC_OBJ_L3CACHE, /**< \brief Level 3 Data (or Unified) Cache. */ + HWLOC_OBJ_L4CACHE, /**< \brief Level 4 Data (or Unified) Cache. */ + HWLOC_OBJ_L5CACHE, /**< \brief Level 5 Data (or Unified) Cache. */ + + HWLOC_OBJ_L1ICACHE, /**< \brief Level 1 instruction Cache (filtered out by default). */ + HWLOC_OBJ_L2ICACHE, /**< \brief Level 2 instruction Cache (filtered out by default). */ + HWLOC_OBJ_L3ICACHE, /**< \brief Level 3 instruction Cache (filtered out by default). */ + + HWLOC_OBJ_GROUP, /**< \brief Group objects. + * Objects which do not fit in the above but are + * detected by hwloc and are useful to take into + * account for affinity. For instance, some operating systems + * expose their arbitrary processors aggregation this + * way. And hwloc may insert such objects to group + * NUMA nodes according to their distances. + * See also \ref faq_groups. + * + * These objects are removed when they do not bring + * any structure (see ::HWLOC_TYPE_FILTER_KEEP_STRUCTURE). + */ + + HWLOC_OBJ_NUMANODE, /**< \brief NUMA node. + * An object that contains memory that is directly + * and byte-accessible to the host processors. + * It is usually close to some cores (the corresponding objects + * are descendants of the NUMA node object in the hwloc tree). + * + * There is always at least one such object in the topology + * even if the machine is not NUMA. + * + * Memory objects are not listed in the main children list, + * but rather in the dedicated Memory children list. + * + * NUMA nodes have a special depth ::HWLOC_TYPE_DEPTH_NUMANODE + * instead of a normal depth just like other objects in the + * main tree. + */ + + HWLOC_OBJ_BRIDGE, /**< \brief Bridge (filtered out by default). + * Any bridge that connects the host or an I/O bus, + * to another I/O bus. + * They are not added to the topology unless I/O discovery + * is enabled with hwloc_topology_set_flags(). + * I/O objects are not listed in the main children list, + * but rather in the dedicated io children list. + * I/O objects have NULL CPU and node sets. + */ + HWLOC_OBJ_PCI_DEVICE, /**< \brief PCI device (filtered out by default). + * They are not added to the topology unless I/O discovery + * is enabled with hwloc_topology_set_flags(). + * I/O objects are not listed in the main children list, + * but rather in the dedicated io children list. + * I/O objects have NULL CPU and node sets. + */ + HWLOC_OBJ_OS_DEVICE, /**< \brief Operating system device (filtered out by default). + * They are not added to the topology unless I/O discovery + * is enabled with hwloc_topology_set_flags(). + * I/O objects are not listed in the main children list, + * but rather in the dedicated io children list. + * I/O objects have NULL CPU and node sets. + */ + + HWLOC_OBJ_MISC, /**< \brief Miscellaneous objects (filtered out by default). + * Objects without particular meaning, that can e.g. be + * added by the application for its own use, or by hwloc + * for miscellaneous objects such as MemoryModule (DIMMs). + * These objects are not listed in the main children list, + * but rather in the dedicated misc children list. + * Misc objects may only have Misc objects as children, + * and those are in the dedicated misc children list as well. + * Misc objects have NULL CPU and node sets. + */ + + HWLOC_OBJ_TYPE_MAX /**< \private Sentinel value */ +} hwloc_obj_type_t; + +/** \brief Cache type. */ +typedef enum hwloc_obj_cache_type_e { + HWLOC_OBJ_CACHE_UNIFIED, /**< \brief Unified cache. */ + HWLOC_OBJ_CACHE_DATA, /**< \brief Data cache. */ + HWLOC_OBJ_CACHE_INSTRUCTION /**< \brief Instruction cache (filtered out by default). */ +} hwloc_obj_cache_type_t; + +/** \brief Type of one side (upstream or downstream) of an I/O bridge. */ +typedef enum hwloc_obj_bridge_type_e { + HWLOC_OBJ_BRIDGE_HOST, /**< \brief Host-side of a bridge, only possible upstream. */ + HWLOC_OBJ_BRIDGE_PCI /**< \brief PCI-side of a bridge. */ +} hwloc_obj_bridge_type_t; + +/** \brief Type of a OS device. */ +typedef enum hwloc_obj_osdev_type_e { + HWLOC_OBJ_OSDEV_BLOCK, /**< \brief Operating system block device. + * For instance "sda" on Linux. */ + HWLOC_OBJ_OSDEV_GPU, /**< \brief Operating system GPU device. + * For instance ":0.0" for a GL display, + * "card0" for a Linux DRM device. */ + HWLOC_OBJ_OSDEV_NETWORK, /**< \brief Operating system network device. + * For instance the "eth0" interface on Linux. */ + HWLOC_OBJ_OSDEV_OPENFABRICS, /**< \brief Operating system openfabrics device. + * For instance the "mlx4_0" InfiniBand HCA, + * or "hfi1_0" Omni-Path interface on Linux. */ + HWLOC_OBJ_OSDEV_DMA, /**< \brief Operating system dma engine device. + * For instance the "dma0chan0" DMA channel on Linux. */ + HWLOC_OBJ_OSDEV_COPROC /**< \brief Operating system co-processor device. + * For instance "mic0" for a Xeon Phi (MIC) on Linux, + * "opencl0d0" for a OpenCL device, + * "cuda0" for a CUDA device. */ +} hwloc_obj_osdev_type_t; + +/** \brief Compare the depth of two object types + * + * Types shouldn't be compared as they are, since newer ones may be added in + * the future. This function returns less than, equal to, or greater than zero + * respectively if \p type1 objects usually include \p type2 objects, are the + * same as \p type2 objects, or are included in \p type2 objects. If the types + * can not be compared (because neither is usually contained in the other), + * ::HWLOC_TYPE_UNORDERED is returned. Object types containing CPUs can always + * be compared (usually, a system contains machines which contain nodes which + * contain packages which contain caches, which contain cores, which contain + * processors). + * + * \note ::HWLOC_OBJ_PU will always be the deepest, + * while ::HWLOC_OBJ_MACHINE is always the highest. + * + * \note This does not mean that the actual topology will respect that order: + * e.g. as of today cores may also contain caches, and packages may also contain + * nodes. This is thus just to be seen as a fallback comparison method. + */ +HWLOC_DECLSPEC int hwloc_compare_types (hwloc_obj_type_t type1, hwloc_obj_type_t type2) __hwloc_attribute_const; + +enum hwloc_compare_types_e { + HWLOC_TYPE_UNORDERED = INT_MAX /**< \brief Value returned by hwloc_compare_types() when types can not be compared. \hideinitializer */ +}; + +/** @} */ + + + +/** \defgroup hwlocality_objects Object Structure and Attributes + * @{ + */ + +union hwloc_obj_attr_u; + +/** \brief Structure of a topology object + * + * Applications must not modify any field except \p hwloc_obj.userdata. + */ +struct hwloc_obj { + /* physical information */ + hwloc_obj_type_t type; /**< \brief Type of object */ + char *subtype; /**< \brief Subtype string to better describe the type field. */ + + unsigned os_index; /**< \brief OS-provided physical index number. + * It is not guaranteed unique across the entire machine, + * except for PUs and NUMA nodes. + * Set to HWLOC_UNKNOWN_INDEX if unknown or irrelevant for this object. + */ +#define HWLOC_UNKNOWN_INDEX (unsigned)-1 + + char *name; /**< \brief Object-specific name if any. + * Mostly used for identifying OS devices and Misc objects where + * a name string is more useful than numerical indexes. + */ + + hwloc_uint64_t total_memory; /**< \brief Total memory (in bytes) in NUMA nodes below this object. */ + + union hwloc_obj_attr_u *attr; /**< \brief Object type-specific Attributes, + * may be \c NULL if no attribute value was found */ + + /* global position */ + int depth; /**< \brief Vertical index in the hierarchy. + * + * For normal objects, this is the depth of the horizontal level + * that contains this object and its cousins of the same type. + * If the topology is symmetric, this is equal to the parent depth + * plus one, and also equal to the number of parent/child links + * from the root object to here. + * + * For special objects (NUMA nodes, I/O and Misc) that are not + * in the main tree, this is a special negative value that + * corresponds to their dedicated level, + * see hwloc_get_type_depth() and ::hwloc_get_type_depth_e. + * Those special values can be passed to hwloc functions such + * hwloc_get_nbobjs_by_depth() as usual. + */ + unsigned logical_index; /**< \brief Horizontal index in the whole list of similar objects, + * hence guaranteed unique across the entire machine. + * Could be a "cousin_rank" since it's the rank within the "cousin" list below + * Note that this index may change when restricting the topology + * or when inserting a group. + */ + + /* cousins are all objects of the same type (and depth) across the entire topology */ + struct hwloc_obj *next_cousin; /**< \brief Next object of same type and depth */ + struct hwloc_obj *prev_cousin; /**< \brief Previous object of same type and depth */ + + /* children of the same parent are siblings, even if they may have different type and depth */ + struct hwloc_obj *parent; /**< \brief Parent, \c NULL if root (Machine object) */ + unsigned sibling_rank; /**< \brief Index in parent's \c children[] array. Or the index in parent's Memory, I/O or Misc children list. */ + struct hwloc_obj *next_sibling; /**< \brief Next object below the same parent (inside the same list of children). */ + struct hwloc_obj *prev_sibling; /**< \brief Previous object below the same parent (inside the same list of children). */ + /** @name List and array of normal children below this object (except Memory, I/O and Misc children). */ + /**@{*/ + unsigned arity; /**< \brief Number of normal children. + * Memory, Misc and I/O children are not listed here + * but rather in their dedicated children list. + */ + struct hwloc_obj **children; /**< \brief Normal children, \c children[0 .. arity -1] */ + struct hwloc_obj *first_child; /**< \brief First normal child */ + struct hwloc_obj *last_child; /**< \brief Last normal child */ + /**@}*/ + + int symmetric_subtree; /**< \brief Set if the subtree of normal objects below this object is symmetric, + * which means all normal children and their children have identical subtrees. + * + * Memory, I/O and Misc children are ignored. + * + * If set in the topology root object, lstopo may export the topology + * as a synthetic string. + */ + + /** @name List of Memory children below this object. */ + /**@{*/ + unsigned memory_arity; /**< \brief Number of Memory children. + * These children are listed in \p memory_first_child. + */ + struct hwloc_obj *memory_first_child; /**< \brief First Memory child. + * NUMA nodes are listed here (\p memory_arity and \p memory_first_child) + * instead of in the normal children list. + * See also hwloc_obj_type_is_memory(). + */ + /**@}*/ + + /** @name List of I/O children below this object. */ + /**@{*/ + unsigned io_arity; /**< \brief Number of I/O children. + * These children are listed in \p io_first_child. + */ + struct hwloc_obj *io_first_child; /**< \brief First I/O child. + * Bridges, PCI and OS devices are listed here (\p io_arity and \p io_first_child) + * instead of in the normal children list. + * See also hwloc_obj_type_is_io(). + */ + /**@}*/ + + /** @name List of Misc children below this object. */ + /**@{*/ + unsigned misc_arity; /**< \brief Number of Misc children. + * These children are listed in \p misc_first_child. + */ + struct hwloc_obj *misc_first_child; /**< \brief First Misc child. + * Misc objects are listed here (\p misc_arity and \p misc_first_child) + * instead of in the normal children list. + */ + /**@}*/ + + /* cpusets and nodesets */ + hwloc_cpuset_t cpuset; /**< \brief CPUs covered by this object + * + * This is the set of CPUs for which there are PU objects in the topology + * under this object, i.e. which are known to be physically contained in this + * object and known how (the children path between this object and the PU + * objects). + * + * If the ::HWLOC_TOPOLOGY_FLAG_WHOLE_SYSTEM configuration flag is set, + * some of these CPUs may not be allowed for binding, + * see hwloc_topology_get_allowed_cpuset(). + * + * \note All objects have non-NULL CPU and node sets except Misc and I/O objects. + * + * \note Its value must not be changed, hwloc_bitmap_dup() must be used instead. + */ + hwloc_cpuset_t complete_cpuset; /**< \brief The complete CPU set of logical processors of this object, + * + * This may include not only the same as the cpuset field, but also some CPUs for + * which topology information is unknown or incomplete, some offlines CPUs, and + * the CPUs that are ignored when the ::HWLOC_TOPOLOGY_FLAG_WHOLE_SYSTEM flag + * is not set. + * Thus no corresponding PU object may be found in the topology, because the + * precise position is undefined. It is however known that it would be somewhere + * under this object. + * + * \note Its value must not be changed, hwloc_bitmap_dup() must be used instead. + */ + + hwloc_nodeset_t nodeset; /**< \brief NUMA nodes covered by this object or containing this object + * + * This is the set of NUMA nodes for which there are NUMA node objects in the + * topology under or above this object, i.e. which are known to be physically + * contained in this object or containing it and known how (the children path + * between this object and the NUMA node objects). + * + * In the end, these nodes are those that are close to the current object. + * + * If the ::HWLOC_TOPOLOGY_FLAG_WHOLE_SYSTEM configuration flag is set, + * some of these nodes may not be allowed for allocation, + * see hwloc_topology_get_allowed_nodeset(). + * + * If there are no NUMA nodes in the machine, all the memory is close to this + * object, so only the first bit may be set in \p nodeset. + * + * \note All objects have non-NULL CPU and node sets except Misc and I/O objects. + * + * \note Its value must not be changed, hwloc_bitmap_dup() must be used instead. + */ + hwloc_nodeset_t complete_nodeset; /**< \brief The complete NUMA node set of this object, + * + * This may include not only the same as the nodeset field, but also some NUMA + * nodes for which topology information is unknown or incomplete, some offlines + * nodes, and the nodes that are ignored when the ::HWLOC_TOPOLOGY_FLAG_WHOLE_SYSTEM + * flag is not set. + * Thus no corresponding NUMA node object may be found in the topology, because the + * precise position is undefined. It is however known that it would be + * somewhere under this object. + * + * If there are no NUMA nodes in the machine, all the memory is close to this + * object, so only the first bit is set in \p complete_nodeset. + * + * \note Its value must not be changed, hwloc_bitmap_dup() must be used instead. + */ + + struct hwloc_info_s *infos; /**< \brief Array of stringified info type=name. */ + unsigned infos_count; /**< \brief Size of infos array. */ + + /* misc */ + void *userdata; /**< \brief Application-given private data pointer, + * initialized to \c NULL, use it as you wish. + * See hwloc_topology_set_userdata_export_callback() in hwloc/export.h + * if you wish to export this field to XML. */ + + hwloc_uint64_t gp_index; /**< \brief Global persistent index. + * Generated by hwloc, unique across the topology (contrary to os_index) + * and persistent across topology changes (contrary to logical_index). + * Mostly used internally, but could also be used by application to identify objects. + */ +}; +/** + * \brief Convenience typedef; a pointer to a struct hwloc_obj. + */ +typedef struct hwloc_obj * hwloc_obj_t; + +/** \brief Object type-specific Attributes */ +union hwloc_obj_attr_u { + /** \brief NUMA node-specific Object Attributes */ + struct hwloc_numanode_attr_s { + hwloc_uint64_t local_memory; /**< \brief Local memory (in bytes) */ + unsigned page_types_len; /**< \brief Size of array \p page_types */ + /** \brief Array of local memory page types, \c NULL if no local memory and \p page_types is 0. + * + * The array is sorted by increasing \p size fields. + * It contains \p page_types_len slots. + */ + struct hwloc_memory_page_type_s { + hwloc_uint64_t size; /**< \brief Size of pages */ + hwloc_uint64_t count; /**< \brief Number of pages of this size */ + } * page_types; + } numanode; + + /** \brief Cache-specific Object Attributes */ + struct hwloc_cache_attr_s { + hwloc_uint64_t size; /**< \brief Size of cache in bytes */ + unsigned depth; /**< \brief Depth of cache (e.g., L1, L2, ...etc.) */ + unsigned linesize; /**< \brief Cache-line size in bytes. 0 if unknown */ + int associativity; /**< \brief Ways of associativity, + * -1 if fully associative, 0 if unknown */ + hwloc_obj_cache_type_t type; /**< \brief Cache type */ + } cache; + /** \brief Group-specific Object Attributes */ + struct hwloc_group_attr_s { + unsigned depth; /**< \brief Depth of group object. + * It may change if intermediate Group objects are added. */ + unsigned kind; /**< \brief Internally-used kind of group. */ + unsigned subkind; /**< \brief Internally-used subkind to distinguish different levels of groups with same kind */ + unsigned char dont_merge; /**< \brief Flag preventing groups from being automatically merged with identical parent or children. */ + } group; + /** \brief PCI Device specific Object Attributes */ + struct hwloc_pcidev_attr_s { + unsigned short domain; + unsigned char bus, dev, func; + unsigned short class_id; + unsigned short vendor_id, device_id, subvendor_id, subdevice_id; + unsigned char revision; + float linkspeed; /* in GB/s */ + } pcidev; + /** \brief Bridge specific Object Attribues */ + struct hwloc_bridge_attr_s { + union { + struct hwloc_pcidev_attr_s pci; + } upstream; + hwloc_obj_bridge_type_t upstream_type; + union { + struct { + unsigned short domain; + unsigned char secondary_bus, subordinate_bus; + } pci; + } downstream; + hwloc_obj_bridge_type_t downstream_type; + unsigned depth; + } bridge; + /** \brief OS Device specific Object Attributes */ + struct hwloc_osdev_attr_s { + hwloc_obj_osdev_type_t type; + } osdev; +}; + +/** \brief Object info + * + * \sa hwlocality_info_attr + */ +struct hwloc_info_s { + char *name; /**< \brief Info name */ + char *value; /**< \brief Info value */ +}; + +/** @} */ + + + +/** \defgroup hwlocality_creation Topology Creation and Destruction + * @{ + */ + +struct hwloc_topology; +/** \brief Topology context + * + * To be initialized with hwloc_topology_init() and built with hwloc_topology_load(). + */ +typedef struct hwloc_topology * hwloc_topology_t; + +/** \brief Allocate a topology context. + * + * \param[out] topologyp is assigned a pointer to the new allocated context. + * + * \return 0 on success, -1 on error. + */ +HWLOC_DECLSPEC int hwloc_topology_init (hwloc_topology_t *topologyp); + +/** \brief Build the actual topology + * + * Build the actual topology once initialized with hwloc_topology_init() and + * tuned with \ref hwlocality_configuration and \ref hwlocality_setsource routines. + * No other routine may be called earlier using this topology context. + * + * \param topology is the topology to be loaded with objects. + * + * \return 0 on success, -1 on error. + * + * \note On failure, the topology is reinitialized. It should be either + * destroyed with hwloc_topology_destroy() or configured and loaded again. + * + * \note This function may be called only once per topology. + * + * \note The binding of the current thread or process may temporarily change + * during this call but it will be restored before it returns. + * + * \sa hwlocality_configuration and hwlocality_setsource + */ +HWLOC_DECLSPEC int hwloc_topology_load(hwloc_topology_t topology); + +/** \brief Terminate and free a topology context + * + * \param topology is the topology to be freed + */ +HWLOC_DECLSPEC void hwloc_topology_destroy (hwloc_topology_t topology); + +/** \brief Duplicate a topology. + * + * The entire topology structure as well as its objects + * are duplicated into a new one. + * + * This is useful for keeping a backup while modifying a topology. + * + * \note Object userdata is not duplicated since hwloc does not know what it point to. + * The objects of both old and new topologies will point to the same userdata. + */ +HWLOC_DECLSPEC int hwloc_topology_dup(hwloc_topology_t *newtopology, hwloc_topology_t oldtopology); + +/** \brief Verify that the topology is compatible with the current hwloc library. + * + * This is useful when using the same topology structure (in memory) + * in different libraries that may use different hwloc installations + * (for instance if one library embeds a specific version of hwloc, + * while another library uses a default system-wide hwloc installation). + * + * If all libraries/programs use the same hwloc installation, this function + * always returns success. + * + * \return \c 0 on success. + * + * \return \c -1 with \p errno set to \c EINVAL if incompatible. + * + * \note If sharing between processes with hwloc_shmem_topology_write(), + * the relevant check is already performed inside hwloc_shmem_topology_adopt(). + */ +HWLOC_DECLSPEC int hwloc_topology_abi_check(hwloc_topology_t topology); + +/** \brief Run internal checks on a topology structure + * + * The program aborts if an inconsistency is detected in the given topology. + * + * \param topology is the topology to be checked + * + * \note This routine is only useful to developers. + * + * \note The input topology should have been previously loaded with + * hwloc_topology_load(). + */ +HWLOC_DECLSPEC void hwloc_topology_check(hwloc_topology_t topology); + +/** @} */ + + + +/** \defgroup hwlocality_levels Object levels, depths and types + * @{ + * + * Be sure to see the figure in \ref termsanddefs that shows a + * complete topology tree, including depths, child/sibling/cousin + * relationships, and an example of an asymmetric topology where one + * package has fewer caches than its peers. + */ + +/** \brief Get the depth of the hierarchical tree of objects. + * + * This is the depth of ::HWLOC_OBJ_PU objects plus one. + * + * \note NUMA nodes, I/O and Misc objects are ignored when computing + * the depth of the tree (they are placed on special levels). + */ +HWLOC_DECLSPEC int hwloc_topology_get_depth(hwloc_topology_t __hwloc_restrict topology) __hwloc_attribute_pure; + +/** \brief Returns the depth of objects of type \p type. + * + * If no object of this type is present on the underlying architecture, or if + * the OS doesn't provide this kind of information, the function returns + * ::HWLOC_TYPE_DEPTH_UNKNOWN. + * + * If type is absent but a similar type is acceptable, see also + * hwloc_get_type_or_below_depth() and hwloc_get_type_or_above_depth(). + * + * If ::HWLOC_OBJ_GROUP is given, the function may return ::HWLOC_TYPE_DEPTH_MULTIPLE + * if multiple levels of Groups exist. + * + * If a NUMA node, I/O or Misc object type is given, the function returns a virtual + * value because these objects are stored in special levels that are not CPU-related. + * This virtual depth may be passed to other hwloc functions such as + * hwloc_get_obj_by_depth() but it should not be considered as an actual + * depth by the application. In particular, it should not be compared with + * any other object depth or with the entire topology depth. + * \sa hwloc_get_memory_parents_depth(). + * + * \sa hwloc_type_sscanf_as_depth() for returning the depth of objects + * whose type is given as a string. + */ +HWLOC_DECLSPEC int hwloc_get_type_depth (hwloc_topology_t topology, hwloc_obj_type_t type); + +enum hwloc_get_type_depth_e { + HWLOC_TYPE_DEPTH_UNKNOWN = -1, /**< \brief No object of given type exists in the topology. \hideinitializer */ + HWLOC_TYPE_DEPTH_MULTIPLE = -2, /**< \brief Objects of given type exist at different depth in the topology (only for Groups). \hideinitializer */ + HWLOC_TYPE_DEPTH_NUMANODE = -3, /**< \brief Virtual depth for NUMA nodes. \hideinitializer */ + HWLOC_TYPE_DEPTH_BRIDGE = -4, /**< \brief Virtual depth for bridge object level. \hideinitializer */ + HWLOC_TYPE_DEPTH_PCI_DEVICE = -5, /**< \brief Virtual depth for PCI device object level. \hideinitializer */ + HWLOC_TYPE_DEPTH_OS_DEVICE = -6, /**< \brief Virtual depth for software device object level. \hideinitializer */ + HWLOC_TYPE_DEPTH_MISC = -7 /**< \brief Virtual depth for Misc object. \hideinitializer */ +}; + +/** \brief Return the depth of parents where memory objects are attached. + * + * Memory objects have virtual negative depths because they are not part of + * the main CPU-side hierarchy of objects. This depth should not be compared + * with other level depths. + * + * If all Memory objects are attached to Normal parents at the same depth, + * this parent depth may be compared to other as usual, for instance + * for knowing whether NUMA nodes is attached above or below Packages. + * + * \return The depth of Normal parents of all memory children + * if all these parents have the same depth. For instance the depth of + * the Package level if all NUMA nodes are attached to Package objects. + * + * \return ::HWLOC_TYPE_DEPTH_MULTIPLE if Normal parents of all + * memory children do not have the same depth. For instance if some + * NUMA nodes are attached to Packages while others are attached to + * Groups. + */ +HWLOC_DECLSPEC int hwloc_get_memory_parents_depth (hwloc_topology_t topology); + +/** \brief Returns the depth of objects of type \p type or below + * + * If no object of this type is present on the underlying architecture, the + * function returns the depth of the first "present" object typically found + * inside \p type. + * + * This function is only meaningful for normal object types. + * If a memory, I/O or Misc object type is given, the corresponding virtual + * depth is always returned (see hwloc_get_type_depth()). + * + * May return ::HWLOC_TYPE_DEPTH_MULTIPLE for ::HWLOC_OBJ_GROUP just like + * hwloc_get_type_depth(). + */ +static __hwloc_inline int +hwloc_get_type_or_below_depth (hwloc_topology_t topology, hwloc_obj_type_t type) __hwloc_attribute_pure; + +/** \brief Returns the depth of objects of type \p type or above + * + * If no object of this type is present on the underlying architecture, the + * function returns the depth of the first "present" object typically + * containing \p type. + * + * This function is only meaningful for normal object types. + * If a memory, I/O or Misc object type is given, the corresponding virtual + * depth is always returned (see hwloc_get_type_depth()). + * + * May return ::HWLOC_TYPE_DEPTH_MULTIPLE for ::HWLOC_OBJ_GROUP just like + * hwloc_get_type_depth(). + */ +static __hwloc_inline int +hwloc_get_type_or_above_depth (hwloc_topology_t topology, hwloc_obj_type_t type) __hwloc_attribute_pure; + +/** \brief Returns the type of objects at depth \p depth. + * + * \p depth should between 0 and hwloc_topology_get_depth()-1. + * + * \return (hwloc_obj_type_t)-1 if depth \p depth does not exist. + */ +HWLOC_DECLSPEC hwloc_obj_type_t hwloc_get_depth_type (hwloc_topology_t topology, int depth) __hwloc_attribute_pure; + +/** \brief Returns the width of level at depth \p depth. + */ +HWLOC_DECLSPEC unsigned hwloc_get_nbobjs_by_depth (hwloc_topology_t topology, int depth) __hwloc_attribute_pure; + +/** \brief Returns the width of level type \p type + * + * If no object for that type exists, 0 is returned. + * If there are several levels with objects of that type, -1 is returned. + */ +static __hwloc_inline int +hwloc_get_nbobjs_by_type (hwloc_topology_t topology, hwloc_obj_type_t type) __hwloc_attribute_pure; + +/** \brief Returns the top-object of the topology-tree. + * + * Its type is ::HWLOC_OBJ_MACHINE. + */ +static __hwloc_inline hwloc_obj_t +hwloc_get_root_obj (hwloc_topology_t topology) __hwloc_attribute_pure; + +/** \brief Returns the topology object at logical index \p idx from depth \p depth */ +HWLOC_DECLSPEC hwloc_obj_t hwloc_get_obj_by_depth (hwloc_topology_t topology, int depth, unsigned idx) __hwloc_attribute_pure; + +/** \brief Returns the topology object at logical index \p idx with type \p type + * + * If no object for that type exists, \c NULL is returned. + * If there are several levels with objects of that type (::HWLOC_OBJ_GROUP), + * \c NULL is returned and the caller may fallback to hwloc_get_obj_by_depth(). + */ +static __hwloc_inline hwloc_obj_t +hwloc_get_obj_by_type (hwloc_topology_t topology, hwloc_obj_type_t type, unsigned idx) __hwloc_attribute_pure; + +/** \brief Returns the next object at depth \p depth. + * + * If \p prev is \c NULL, return the first object at depth \p depth. + */ +static __hwloc_inline hwloc_obj_t +hwloc_get_next_obj_by_depth (hwloc_topology_t topology, int depth, hwloc_obj_t prev); + +/** \brief Returns the next object of type \p type. + * + * If \p prev is \c NULL, return the first object at type \p type. If + * there are multiple or no depth for given type, return \c NULL and + * let the caller fallback to hwloc_get_next_obj_by_depth(). + */ +static __hwloc_inline hwloc_obj_t +hwloc_get_next_obj_by_type (hwloc_topology_t topology, hwloc_obj_type_t type, + hwloc_obj_t prev); + +/** @} */ + + + +/** \defgroup hwlocality_object_strings Converting between Object Types and Attributes, and Strings + * @{ + */ + +/** \brief Return a constant stringified object type. + * + * This function is the basic way to convert a generic type into a string. + * The output string may be parsed back by hwloc_type_sscanf(). + * + * hwloc_obj_type_snprintf() may return a more precise output for a specific + * object, but it requires the caller to provide the output buffer. + */ +HWLOC_DECLSPEC const char * hwloc_obj_type_string (hwloc_obj_type_t type) __hwloc_attribute_const; + +/** \brief Stringify the type of a given topology object into a human-readable form. + * + * Contrary to hwloc_obj_type_string(), this function includes object-specific + * attributes (such as the Group depth, the Bridge type, or OS device type) + * in the output, and it requires the caller to provide the output buffer. + * + * The output is guaranteed to be the same for all objects of a same topology level. + * + * If \p verbose is 1, longer type names are used, e.g. L1Cache instead of L1. + * + * The output string may be parsed back by hwloc_type_sscanf(). + * + * If \p size is 0, \p string may safely be \c NULL. + * + * \return the number of character that were actually written if not truncating, + * or that would have been written (not including the ending \\0). + */ +HWLOC_DECLSPEC int hwloc_obj_type_snprintf(char * __hwloc_restrict string, size_t size, + hwloc_obj_t obj, + int verbose); + +/** \brief Stringify the attributes of a given topology object into a human-readable form. + * + * Attribute values are separated by \p separator. + * + * Only the major attributes are printed in non-verbose mode. + * + * If \p size is 0, \p string may safely be \c NULL. + * + * \return the number of character that were actually written if not truncating, + * or that would have been written (not including the ending \\0). + */ +HWLOC_DECLSPEC int hwloc_obj_attr_snprintf(char * __hwloc_restrict string, size_t size, + hwloc_obj_t obj, const char * __hwloc_restrict separator, + int verbose); + +/** \brief Return an object type and attributes from a type string. + * + * Convert strings such as "Package" or "L1iCache" into the corresponding types. + * Matching is case-insensitive, and only the first letters are actually + * required to match. + * + * The matched object type is set in \p typep (which cannot be \c NULL). + * + * Type-specific attributes, for instance Cache type, Cache depth, Group depth, + * Bridge type or OS Device type may be returned in \p attrp. + * Attributes that are not specified in the string (for instance "Group" + * without a depth, or "L2Cache" without a cache type) are set to -1. + * + * \p attrp is only filled if not \c NULL and if its size specified in \p attrsize + * is large enough. It should be at least as large as union hwloc_obj_attr_u. + * + * \return 0 if a type was correctly identified, otherwise -1. + * + * \note This function is guaranteed to match any string returned by + * hwloc_obj_type_string() or hwloc_obj_type_snprintf(). + * + * \note This is an extended version of the now deprecated hwloc_obj_type_sscanf(). + */ +HWLOC_DECLSPEC int hwloc_type_sscanf(const char *string, + hwloc_obj_type_t *typep, + union hwloc_obj_attr_u *attrp, size_t attrsize); + +/** \brief Return an object type and its level depth from a type string. + * + * Convert strings such as "Package" or "L1iCache" into the corresponding types + * and return in \p depthp the depth of the corresponding level in the + * topology \p topology. + * + * If no object of this type is present on the underlying architecture, + * ::HWLOC_TYPE_DEPTH_UNKNOWN is returned. + * + * If multiple such levels exist (for instance if giving Group without any depth), + * the function may return ::HWLOC_TYPE_DEPTH_MULTIPLE instead. + * + * The matched object type is set in \p typep if \p typep is non \c NULL. + * + * \note This function is similar to hwloc_type_sscanf() followed + * by hwloc_get_type_depth() but it also automatically disambiguates + * multiple group levels etc. + * + * \note This function is guaranteed to match any string returned by + * hwloc_obj_type_string() or hwloc_obj_type_snprintf(). + */ +HWLOC_DECLSPEC int hwloc_type_sscanf_as_depth(const char *string, + hwloc_obj_type_t *typep, + hwloc_topology_t topology, int *depthp); + +/** @} */ + + + +/** \defgroup hwlocality_info_attr Consulting and Adding Key-Value Info Attributes + * + * @{ + */ + +/** \brief Search the given key name in object infos and return the corresponding value. + * + * If multiple keys match the given name, only the first one is returned. + * + * \return \c NULL if no such key exists. + */ +static __hwloc_inline const char * +hwloc_obj_get_info_by_name(hwloc_obj_t obj, const char *name) __hwloc_attribute_pure; + +/** \brief Add the given info name and value pair to the given object. + * + * The info is appended to the existing info array even if another key + * with the same name already exists. + * + * The input strings are copied before being added in the object infos. + * + * \return \c 0 on success, \c -1 on error. + * + * \note This function may be used to enforce object colors in the lstopo + * graphical output by using "lstopoStyle" as a name and "Background=#rrggbb" + * as a value. See CUSTOM COLORS in the lstopo(1) manpage for details. + * + * \note If \p value contains some non-printable characters, they will + * be dropped when exporting to XML, see hwloc_topology_export_xml() in hwloc/export.h. + */ +HWLOC_DECLSPEC int hwloc_obj_add_info(hwloc_obj_t obj, const char *name, const char *value); + +/** @} */ + + + +/** \defgroup hwlocality_cpubinding CPU binding + * + * Some operating systems only support binding threads or processes to a single PU. + * Others allow binding to larger sets such as entire Cores or Packages or + * even random sets of invididual PUs. In such operating system, the scheduler + * is free to run the task on one of these PU, then migrate it to another PU, etc. + * It is often useful to call hwloc_bitmap_singlify() on the target CPU set before + * passing it to the binding function to avoid these expensive migrations. + * See the documentation of hwloc_bitmap_singlify() for details. + * + * Some operating systems do not provide all hwloc-supported + * mechanisms to bind processes, threads, etc. + * hwloc_topology_get_support() may be used to query about the actual CPU + * binding support in the currently used operating system. + * + * When the requested binding operation is not available and the + * ::HWLOC_CPUBIND_STRICT flag was passed, the function returns -1. + * \p errno is set to \c ENOSYS when it is not possible to bind the requested kind of object + * processes/threads. errno is set to \c EXDEV when the requested cpuset + * can not be enforced (e.g. some systems only allow one CPU, and some + * other systems only allow one NUMA node). + * + * If ::HWLOC_CPUBIND_STRICT was not passed, the function may fail as well, + * or the operating system may use a slightly different operation + * (with side-effects, smaller binding set, etc.) + * when the requested operation is not exactly supported. + * + * The most portable version that should be preferred over the others, + * whenever possible, is the following one which just binds the current program, + * assuming it is single-threaded: + * + * \code + * hwloc_set_cpubind(topology, set, 0), + * \endcode + * + * If the program may be multithreaded, the following one should be preferred + * to only bind the current thread: + * + * \code + * hwloc_set_cpubind(topology, set, HWLOC_CPUBIND_THREAD), + * \endcode + * + * \sa Some example codes are available under doc/examples/ in the source tree. + * + * \note To unbind, just call the binding function with either a full cpuset or + * a cpuset equal to the system cpuset. + * + * \note On some operating systems, CPU binding may have effects on memory binding, see + * ::HWLOC_CPUBIND_NOMEMBIND + * + * \note Running lstopo \--top or hwloc-ps can be a very convenient tool to check + * how binding actually happened. + * @{ + */ + +/** \brief Process/Thread binding flags. + * + * These bit flags can be used to refine the binding policy. + * + * The default (0) is to bind the current process, assumed to be + * single-threaded, in a non-strict way. This is the most portable + * way to bind as all operating systems usually provide it. + * + * \note Not all systems support all kinds of binding. See the + * "Detailed Description" section of \ref hwlocality_cpubinding for a + * description of errors that can occur. + */ +typedef enum { + /** \brief Bind all threads of the current (possibly) multithreaded process. + * \hideinitializer */ + HWLOC_CPUBIND_PROCESS = (1<<0), + + /** \brief Bind current thread of current process. + * \hideinitializer */ + HWLOC_CPUBIND_THREAD = (1<<1), + + /** \brief Request for strict binding from the OS. + * + * By default, when the designated CPUs are all busy while other + * CPUs are idle, operating systems may execute the thread/process + * on those other CPUs instead of the designated CPUs, to let them + * progress anyway. Strict binding means that the thread/process + * will _never_ execute on other cpus than the designated CPUs, even + * when those are busy with other tasks and other CPUs are idle. + * + * \note Depending on the operating system, strict binding may not + * be possible (e.g., the OS does not implement it) or not allowed + * (e.g., for an administrative reasons), and the function will fail + * in that case. + * + * When retrieving the binding of a process, this flag checks + * whether all its threads actually have the same binding. If the + * flag is not given, the binding of each thread will be + * accumulated. + * + * \note This flag is meaningless when retrieving the binding of a + * thread. + * \hideinitializer + */ + HWLOC_CPUBIND_STRICT = (1<<2), + + /** \brief Avoid any effect on memory binding + * + * On some operating systems, some CPU binding function would also + * bind the memory on the corresponding NUMA node. It is often not + * a problem for the application, but if it is, setting this flag + * will make hwloc avoid using OS functions that would also bind + * memory. This will however reduce the support of CPU bindings, + * i.e. potentially return -1 with errno set to ENOSYS in some + * cases. + * + * This flag is only meaningful when used with functions that set + * the CPU binding. It is ignored when used with functions that get + * CPU binding information. + * \hideinitializer + */ + HWLOC_CPUBIND_NOMEMBIND = (1<<3) +} hwloc_cpubind_flags_t; + +/** \brief Bind current process or thread on cpus given in physical bitmap \p set. + * + * \return -1 with errno set to ENOSYS if the action is not supported + * \return -1 with errno set to EXDEV if the binding cannot be enforced + */ +HWLOC_DECLSPEC int hwloc_set_cpubind(hwloc_topology_t topology, hwloc_const_cpuset_t set, int flags); + +/** \brief Get current process or thread binding. + * + * Writes into \p set the physical cpuset which the process or thread (according to \e + * flags) was last bound to. + */ +HWLOC_DECLSPEC int hwloc_get_cpubind(hwloc_topology_t topology, hwloc_cpuset_t set, int flags); + +/** \brief Bind a process \p pid on cpus given in physical bitmap \p set. + * + * \note \p hwloc_pid_t is \p pid_t on Unix platforms, + * and \p HANDLE on native Windows platforms. + * + * \note As a special case on Linux, if a tid (thread ID) is supplied + * instead of a pid (process ID) and ::HWLOC_CPUBIND_THREAD is passed in flags, + * the binding is applied to that specific thread. + * + * \note On non-Linux systems, ::HWLOC_CPUBIND_THREAD can not be used in \p flags. + */ +HWLOC_DECLSPEC int hwloc_set_proc_cpubind(hwloc_topology_t topology, hwloc_pid_t pid, hwloc_const_cpuset_t set, int flags); + +/** \brief Get the current physical binding of process \p pid. + * + * \note \p hwloc_pid_t is \p pid_t on Unix platforms, + * and \p HANDLE on native Windows platforms. + * + * \note As a special case on Linux, if a tid (thread ID) is supplied + * instead of a pid (process ID) and HWLOC_CPUBIND_THREAD is passed in flags, + * the binding for that specific thread is returned. + * + * \note On non-Linux systems, HWLOC_CPUBIND_THREAD can not be used in \p flags. + */ +HWLOC_DECLSPEC int hwloc_get_proc_cpubind(hwloc_topology_t topology, hwloc_pid_t pid, hwloc_cpuset_t set, int flags); + +#ifdef hwloc_thread_t +/** \brief Bind a thread \p thread on cpus given in physical bitmap \p set. + * + * \note \p hwloc_thread_t is \p pthread_t on Unix platforms, + * and \p HANDLE on native Windows platforms. + * + * \note ::HWLOC_CPUBIND_PROCESS can not be used in \p flags. + */ +HWLOC_DECLSPEC int hwloc_set_thread_cpubind(hwloc_topology_t topology, hwloc_thread_t thread, hwloc_const_cpuset_t set, int flags); +#endif + +#ifdef hwloc_thread_t +/** \brief Get the current physical binding of thread \p tid. + * + * \note \p hwloc_thread_t is \p pthread_t on Unix platforms, + * and \p HANDLE on native Windows platforms. + * + * \note ::HWLOC_CPUBIND_PROCESS can not be used in \p flags. + */ +HWLOC_DECLSPEC int hwloc_get_thread_cpubind(hwloc_topology_t topology, hwloc_thread_t thread, hwloc_cpuset_t set, int flags); +#endif + +/** \brief Get the last physical CPU where the current process or thread ran. + * + * The operating system may move some tasks from one processor + * to another at any time according to their binding, + * so this function may return something that is already + * outdated. + * + * \p flags can include either ::HWLOC_CPUBIND_PROCESS or ::HWLOC_CPUBIND_THREAD to + * specify whether the query should be for the whole process (union of all CPUs + * on which all threads are running), or only the current thread. If the + * process is single-threaded, flags can be set to zero to let hwloc use + * whichever method is available on the underlying OS. + */ +HWLOC_DECLSPEC int hwloc_get_last_cpu_location(hwloc_topology_t topology, hwloc_cpuset_t set, int flags); + +/** \brief Get the last physical CPU where a process ran. + * + * The operating system may move some tasks from one processor + * to another at any time according to their binding, + * so this function may return something that is already + * outdated. + * + * \note \p hwloc_pid_t is \p pid_t on Unix platforms, + * and \p HANDLE on native Windows platforms. + * + * \note As a special case on Linux, if a tid (thread ID) is supplied + * instead of a pid (process ID) and ::HWLOC_CPUBIND_THREAD is passed in flags, + * the last CPU location of that specific thread is returned. + * + * \note On non-Linux systems, ::HWLOC_CPUBIND_THREAD can not be used in \p flags. + */ +HWLOC_DECLSPEC int hwloc_get_proc_last_cpu_location(hwloc_topology_t topology, hwloc_pid_t pid, hwloc_cpuset_t set, int flags); + +/** @} */ + + + +/** \defgroup hwlocality_membinding Memory binding + * + * Memory binding can be done three ways: + * + * - explicit memory allocation thanks to hwloc_alloc_membind() and friends: + * the binding will have effect on the memory allocated by these functions. + * - implicit memory binding through binding policy: hwloc_set_membind() and + * friends only define the current policy of the process, which will be + * applied to the subsequent calls to malloc() and friends. + * - migration of existing memory ranges, thanks to hwloc_set_area_membind() + * and friends, which move already-allocated data. + * + * Not all operating systems support all three ways. + * hwloc_topology_get_support() may be used to query about the actual memory + * binding support in the currently used operating system. + * + * When the requested binding operation is not available and the + * ::HWLOC_MEMBIND_STRICT flag was passed, the function returns -1. + * \p errno will be set to \c ENOSYS when the system does support + * the specified action or policy + * (e.g., some systems only allow binding memory on a per-thread + * basis, whereas other systems only allow binding memory for all + * threads in a process). + * \p errno will be set to EXDEV when the requested set can not be enforced + * (e.g., some systems only allow binding memory to a single NUMA node). + * + * If ::HWLOC_MEMBIND_STRICT was not passed, the function may fail as well, + * or the operating system may use a slightly different operation + * (with side-effects, smaller binding set, etc.) + * when the requested operation is not exactly supported. + * + * The most portable form that should be preferred over the others + * whenever possible is as follows. + * It allocates some memory hopefully bound to the specified set. + * To do so, hwloc will possibly have to change the current memory + * binding policy in order to actually get the memory bound, if the OS + * does not provide any other way to simply allocate bound memory + * without changing the policy for all allocations. That is the + * difference with hwloc_alloc_membind(), which will never change the + * current memory binding policy. + * + * \code + * hwloc_alloc_membind_policy(topology, size, set, + * HWLOC_MEMBIND_BIND, 0); + * \endcode + * + * Each hwloc memory binding function takes a bitmap argument that + * is a CPU set by default, or a NUMA memory node set if the flag + * ::HWLOC_MEMBIND_BYNODESET is specified. + * See \ref hwlocality_object_sets and \ref hwlocality_bitmap for a + * discussion of CPU sets and NUMA memory node sets. + * It is also possible to convert between CPU set and node set using + * hwloc_cpuset_to_nodeset() or hwloc_cpuset_from_nodeset(). + * + * Memory binding by CPU set cannot work for CPU-less NUMA memory nodes. + * Binding by nodeset should therefore be preferred whenever possible. + * + * \sa Some example codes are available under doc/examples/ in the source tree. + * + * \note On some operating systems, memory binding affects the CPU + * binding; see ::HWLOC_MEMBIND_NOCPUBIND + * @{ + */ + +/** \brief Memory binding policy. + * + * These constants can be used to choose the binding policy. Only one policy can + * be used at a time (i.e., the values cannot be OR'ed together). + * + * Not all systems support all kinds of binding. + * hwloc_topology_get_support() may be used to query about the actual memory + * binding policy support in the currently used operating system. + * See the "Detailed Description" section of \ref hwlocality_membinding + * for a description of errors that can occur. + */ +typedef enum { + /** \brief Reset the memory allocation policy to the system default. + * Depending on the operating system, this may correspond to + * ::HWLOC_MEMBIND_FIRSTTOUCH (Linux), + * or ::HWLOC_MEMBIND_BIND (AIX, HP-UX, Solaris, Windows). + * This policy is never returned by get membind functions. + * The nodeset argument is ignored. + * \hideinitializer */ + HWLOC_MEMBIND_DEFAULT = 0, + + /** \brief Allocate each memory page individually on the local NUMA + * node of the thread that touches it. + * + * The given nodeset should usually be hwloc_topology_get_topology_nodeset() + * so that the touching thread may run and allocate on any node in the system. + * + * On AIX, if the nodeset is smaller, pages are allocated locally (if the local + * node is in the nodeset) or from a random non-local node (otherwise). + * \hideinitializer */ + HWLOC_MEMBIND_FIRSTTOUCH = 1, + + /** \brief Allocate memory on the specified nodes. + * \hideinitializer */ + HWLOC_MEMBIND_BIND = 2, + + /** \brief Allocate memory on the given nodes in an interleaved + * / round-robin manner. The precise layout of the memory across + * multiple NUMA nodes is OS/system specific. Interleaving can be + * useful when threads distributed across the specified NUMA nodes + * will all be accessing the whole memory range concurrently, since + * the interleave will then balance the memory references. + * \hideinitializer */ + HWLOC_MEMBIND_INTERLEAVE = 3, + + /** \brief For each page bound with this policy, by next time + * it is touched (and next time only), it is moved from its current + * location to the local NUMA node of the thread where the memory + * reference occurred (if it needs to be moved at all). + * \hideinitializer */ + HWLOC_MEMBIND_NEXTTOUCH = 4, + + /** \brief Returned by get_membind() functions when multiple + * threads or parts of a memory area have differing memory binding + * policies. + * Also returned when binding is unknown because binding hooks are empty + * when the topology is loaded from XML without HWLOC_THISSYSTEM=1, etc. + * \hideinitializer */ + HWLOC_MEMBIND_MIXED = -1 +} hwloc_membind_policy_t; + +/** \brief Memory binding flags. + * + * These flags can be used to refine the binding policy. + * All flags can be logically OR'ed together with the exception of + * ::HWLOC_MEMBIND_PROCESS and ::HWLOC_MEMBIND_THREAD; + * these two flags are mutually exclusive. + * + * Not all systems support all kinds of binding. + * hwloc_topology_get_support() may be used to query about the actual memory + * binding support in the currently used operating system. + * See the "Detailed Description" section of \ref hwlocality_membinding + * for a description of errors that can occur. + */ +typedef enum { + /** \brief Set policy for all threads of the specified (possibly + * multithreaded) process. This flag is mutually exclusive with + * ::HWLOC_MEMBIND_THREAD. + * \hideinitializer */ + HWLOC_MEMBIND_PROCESS = (1<<0), + + /** \brief Set policy for a specific thread of the current process. + * This flag is mutually exclusive with ::HWLOC_MEMBIND_PROCESS. + * \hideinitializer */ + HWLOC_MEMBIND_THREAD = (1<<1), + + /** Request strict binding from the OS. The function will fail if + * the binding can not be guaranteed / completely enforced. + * + * This flag has slightly different meanings depending on which + * function it is used with. + * \hideinitializer */ + HWLOC_MEMBIND_STRICT = (1<<2), + + /** \brief Migrate existing allocated memory. If the memory cannot + * be migrated and the ::HWLOC_MEMBIND_STRICT flag is passed, an error + * will be returned. + * \hideinitializer */ + HWLOC_MEMBIND_MIGRATE = (1<<3), + + /** \brief Avoid any effect on CPU binding. + * + * On some operating systems, some underlying memory binding + * functions also bind the application to the corresponding CPU(s). + * Using this flag will cause hwloc to avoid using OS functions that + * could potentially affect CPU bindings. Note, however, that using + * NOCPUBIND may reduce hwloc's overall memory binding + * support. Specifically: some of hwloc's memory binding functions + * may fail with errno set to ENOSYS when used with NOCPUBIND. + * \hideinitializer + */ + HWLOC_MEMBIND_NOCPUBIND = (1<<4), + + /** \brief Consider the bitmap argument as a nodeset. + * + * The bitmap argument is considered a nodeset if this flag is given, + * or a cpuset otherwise by default. + * + * Memory binding by CPU set cannot work for CPU-less NUMA memory nodes. + * Binding by nodeset should therefore be preferred whenever possible. + * \hideinitializer + */ + HWLOC_MEMBIND_BYNODESET = (1<<5) +} hwloc_membind_flags_t; + +/** \brief Set the default memory binding policy of the current + * process or thread to prefer the NUMA node(s) specified by \p set + * + * If neither ::HWLOC_MEMBIND_PROCESS nor ::HWLOC_MEMBIND_THREAD is + * specified, the current process is assumed to be single-threaded. + * This is the most portable form as it permits hwloc to use either + * process-based OS functions or thread-based OS functions, depending + * on which are available. + * + * If ::HWLOC_MEMBIND_BYNODESET is specified, set is considered a nodeset. + * Otherwise it's a cpuset. + * + * \return -1 with errno set to ENOSYS if the action is not supported + * \return -1 with errno set to EXDEV if the binding cannot be enforced + */ +HWLOC_DECLSPEC int hwloc_set_membind(hwloc_topology_t topology, hwloc_const_bitmap_t set, hwloc_membind_policy_t policy, int flags); + +/** \brief Query the default memory binding policy and physical locality of the + * current process or thread. + * + * This function has two output parameters: \p set and \p policy. + * The values returned in these parameters depend on both the \p flags + * passed in and the current memory binding policies and nodesets in + * the queried target. + * + * Passing the ::HWLOC_MEMBIND_PROCESS flag specifies that the query + * target is the current policies and nodesets for all the threads in + * the current process. Passing ::HWLOC_MEMBIND_THREAD specifies that + * the query target is the current policy and nodeset for only the + * thread invoking this function. + * + * If neither of these flags are passed (which is the most portable + * method), the process is assumed to be single threaded. This allows + * hwloc to use either process-based OS functions or thread-based OS + * functions, depending on which are available. + * + * ::HWLOC_MEMBIND_STRICT is only meaningful when ::HWLOC_MEMBIND_PROCESS + * is also specified. In this case, hwloc will check the default + * memory policies and nodesets for all threads in the process. If + * they are not identical, -1 is returned and errno is set to EXDEV. + * If they are identical, the values are returned in \p set and \p + * policy. + * + * Otherwise, if ::HWLOC_MEMBIND_PROCESS is specified (and + * ::HWLOC_MEMBIND_STRICT is \em not specified), the default set + * from each thread is logically OR'ed together. + * If all threads' default policies are the same, \p policy is set to + * that policy. If they are different, \p policy is set to + * ::HWLOC_MEMBIND_MIXED. + * + * In the ::HWLOC_MEMBIND_THREAD case (or when neither + * ::HWLOC_MEMBIND_PROCESS or ::HWLOC_MEMBIND_THREAD is specified), there + * is only one set and policy; they are returned in \p set and + * \p policy, respectively. + * + * If ::HWLOC_MEMBIND_BYNODESET is specified, set is considered a nodeset. + * Otherwise it's a cpuset. + * + * If any other flags are specified, -1 is returned and errno is set + * to EINVAL. + */ +HWLOC_DECLSPEC int hwloc_get_membind(hwloc_topology_t topology, hwloc_bitmap_t set, hwloc_membind_policy_t * policy, int flags); + +/** \brief Set the default memory binding policy of the specified + * process to prefer the NUMA node(s) specified by \p set + * + * If ::HWLOC_MEMBIND_BYNODESET is specified, set is considered a nodeset. + * Otherwise it's a cpuset. + * + * \return -1 with errno set to ENOSYS if the action is not supported + * \return -1 with errno set to EXDEV if the binding cannot be enforced + * + * \note \p hwloc_pid_t is \p pid_t on Unix platforms, + * and \p HANDLE on native Windows platforms. + */ +HWLOC_DECLSPEC int hwloc_set_proc_membind(hwloc_topology_t topology, hwloc_pid_t pid, hwloc_const_bitmap_t set, hwloc_membind_policy_t policy, int flags); + +/** \brief Query the default memory binding policy and physical locality of the + * specified process. + * + * This function has two output parameters: \p set and \p policy. + * The values returned in these parameters depend on both the \p flags + * passed in and the current memory binding policies and nodesets in + * the queried target. + * + * Passing the ::HWLOC_MEMBIND_PROCESS flag specifies that the query + * target is the current policies and nodesets for all the threads in + * the specified process. If ::HWLOC_MEMBIND_PROCESS is not specified + * (which is the most portable method), the process is assumed to be + * single threaded. This allows hwloc to use either process-based OS + * functions or thread-based OS functions, depending on which are + * available. + * + * Note that it does not make sense to pass ::HWLOC_MEMBIND_THREAD to + * this function. + * + * If ::HWLOC_MEMBIND_STRICT is specified, hwloc will check the default + * memory policies and nodesets for all threads in the specified + * process. If they are not identical, -1 is returned and errno is + * set to EXDEV. If they are identical, the values are returned in \p + * set and \p policy. + * + * Otherwise, \p set is set to the logical OR of all threads' + * default set. If all threads' default policies + * are the same, \p policy is set to that policy. If they are + * different, \p policy is set to ::HWLOC_MEMBIND_MIXED. + * + * If ::HWLOC_MEMBIND_BYNODESET is specified, set is considered a nodeset. + * Otherwise it's a cpuset. + * + * If any other flags are specified, -1 is returned and errno is set + * to EINVAL. + * + * \note \p hwloc_pid_t is \p pid_t on Unix platforms, + * and \p HANDLE on native Windows platforms. + */ +HWLOC_DECLSPEC int hwloc_get_proc_membind(hwloc_topology_t topology, hwloc_pid_t pid, hwloc_bitmap_t set, hwloc_membind_policy_t * policy, int flags); + +/** \brief Bind the already-allocated memory identified by (addr, len) + * to the NUMA node(s) specified by \p set. + * + * If ::HWLOC_MEMBIND_BYNODESET is specified, set is considered a nodeset. + * Otherwise it's a cpuset. + * + * \return 0 if \p len is 0. + * \return -1 with errno set to ENOSYS if the action is not supported + * \return -1 with errno set to EXDEV if the binding cannot be enforced + */ +HWLOC_DECLSPEC int hwloc_set_area_membind(hwloc_topology_t topology, const void *addr, size_t len, hwloc_const_bitmap_t set, hwloc_membind_policy_t policy, int flags); + +/** \brief Query the CPUs near the physical NUMA node(s) and binding policy of + * the memory identified by (\p addr, \p len ). + * + * This function has two output parameters: \p set and \p policy. + * The values returned in these parameters depend on both the \p flags + * passed in and the memory binding policies and nodesets of the pages + * in the address range. + * + * If ::HWLOC_MEMBIND_STRICT is specified, the target pages are first + * checked to see if they all have the same memory binding policy and + * nodeset. If they do not, -1 is returned and errno is set to EXDEV. + * If they are identical across all pages, the set and policy are + * returned in \p set and \p policy, respectively. + * + * If ::HWLOC_MEMBIND_STRICT is not specified, the union of all NUMA + * node(s) containing pages in the address range is calculated. + * If all pages in the target have the same policy, it is returned in + * \p policy. Otherwise, \p policy is set to ::HWLOC_MEMBIND_MIXED. + * + * If ::HWLOC_MEMBIND_BYNODESET is specified, set is considered a nodeset. + * Otherwise it's a cpuset. + * + * If any other flags are specified, -1 is returned and errno is set + * to EINVAL. + * + * If \p len is 0, -1 is returned and errno is set to EINVAL. + */ +HWLOC_DECLSPEC int hwloc_get_area_membind(hwloc_topology_t topology, const void *addr, size_t len, hwloc_bitmap_t set, hwloc_membind_policy_t * policy, int flags); + +/** \brief Get the NUMA nodes where memory identified by (\p addr, \p len ) is physically allocated. + * + * Fills \p set according to the NUMA nodes where the memory area pages + * are physically allocated. If no page is actually allocated yet, + * \p set may be empty. + * + * If pages spread to multiple nodes, it is not specified whether they spread + * equitably, or whether most of them are on a single node, etc. + * + * The operating system may move memory pages from one processor + * to another at any time according to their binding, + * so this function may return something that is already + * outdated. + * + * If ::HWLOC_MEMBIND_BYNODESET is specified in \p flags, set is + * considered a nodeset. Otherwise it's a cpuset. + * + * If \p len is 0, \p set is emptied. + */ +HWLOC_DECLSPEC int hwloc_get_area_memlocation(hwloc_topology_t topology, const void *addr, size_t len, hwloc_bitmap_t set, int flags); + +/** \brief Allocate some memory + * + * This is equivalent to malloc(), except that it tries to allocate + * page-aligned memory from the OS. + * + * \note The allocated memory should be freed with hwloc_free(). + */ +HWLOC_DECLSPEC void *hwloc_alloc(hwloc_topology_t topology, size_t len); + +/** \brief Allocate some memory on NUMA memory nodes specified by \p set + * + * \return NULL with errno set to ENOSYS if the action is not supported + * and ::HWLOC_MEMBIND_STRICT is given + * \return NULL with errno set to EXDEV if the binding cannot be enforced + * and ::HWLOC_MEMBIND_STRICT is given + * \return NULL with errno set to ENOMEM if the memory allocation failed + * even before trying to bind. + * + * If ::HWLOC_MEMBIND_BYNODESET is specified, set is considered a nodeset. + * Otherwise it's a cpuset. + * + * \note The allocated memory should be freed with hwloc_free(). + */ +HWLOC_DECLSPEC void *hwloc_alloc_membind(hwloc_topology_t topology, size_t len, hwloc_const_bitmap_t set, hwloc_membind_policy_t policy, int flags) __hwloc_attribute_malloc; + +/** \brief Allocate some memory on NUMA memory nodes specified by \p set + * + * This is similar to hwloc_alloc_membind_nodeset() except that it is allowed to change + * the current memory binding policy, thus providing more binding support, at + * the expense of changing the current state. + * + * If ::HWLOC_MEMBIND_BYNODESET is specified, set is considered a nodeset. + * Otherwise it's a cpuset. + */ +static __hwloc_inline void * +hwloc_alloc_membind_policy(hwloc_topology_t topology, size_t len, hwloc_const_bitmap_t set, hwloc_membind_policy_t policy, int flags) __hwloc_attribute_malloc; + +/** \brief Free memory that was previously allocated by hwloc_alloc() + * or hwloc_alloc_membind(). + */ +HWLOC_DECLSPEC int hwloc_free(hwloc_topology_t topology, void *addr, size_t len); + +/** @} */ + + + +/** \defgroup hwlocality_setsource Changing the Source of Topology Discovery + * + * If none of the functions below is called, the default is to detect all the objects + * of the machine that the caller is allowed to access. + * + * This default behavior may also be modified through environment variables + * if the application did not modify it already. + * Setting HWLOC_XMLFILE in the environment enforces the discovery from a XML + * file as if hwloc_topology_set_xml() had been called. + * Setting HWLOC_SYNTHETIC enforces a synthetic topology as if + * hwloc_topology_set_synthetic() had been called. + * + * Finally, HWLOC_THISSYSTEM enforces the return value of + * hwloc_topology_is_thissystem(). + * + * @{ + */ + +/** \brief Change which process the topology is viewed from. + * + * On some systems, processes may have different views of the machine, for + * instance the set of allowed CPUs. By default, hwloc exposes the view from + * the current process. Calling hwloc_topology_set_pid() permits to make it + * expose the topology of the machine from the point of view of another + * process. + * + * \note \p hwloc_pid_t is \p pid_t on Unix platforms, + * and \p HANDLE on native Windows platforms. + * + * \note -1 is returned and errno is set to ENOSYS on platforms that do not + * support this feature. + */ +HWLOC_DECLSPEC int hwloc_topology_set_pid(hwloc_topology_t __hwloc_restrict topology, hwloc_pid_t pid); + +/** \brief Enable synthetic topology. + * + * Gather topology information from the given \p description, + * a space-separated string of describing + * the object type and arity at each level. + * All types may be omitted (space-separated string of numbers) so that + * hwloc chooses all types according to usual topologies. + * See also the \ref synthetic. + * + * Setting the environment variable HWLOC_SYNTHETIC + * may also result in this behavior. + * + * If \p description was properly parsed and describes a valid topology + * configuration, this function returns 0. + * Otherwise -1 is returned and errno is set to EINVAL. + * + * Note that this function does not actually load topology + * information; it just tells hwloc where to load it from. You'll + * still need to invoke hwloc_topology_load() to actually load the + * topology information. + * + * \note For convenience, this backend provides empty binding hooks which just + * return success. + * + * \note On success, the synthetic component replaces the previously enabled + * component (if any), but the topology is not actually modified until + * hwloc_topology_load(). + */ +HWLOC_DECLSPEC int hwloc_topology_set_synthetic(hwloc_topology_t __hwloc_restrict topology, const char * __hwloc_restrict description); + +/** \brief Enable XML-file based topology. + * + * Gather topology information from the XML file given at \p xmlpath. + * Setting the environment variable HWLOC_XMLFILE may also result in this behavior. + * This file may have been generated earlier with hwloc_topology_export_xml() in hwloc/export.h, + * or lstopo file.xml. + * + * Note that this function does not actually load topology + * information; it just tells hwloc where to load it from. You'll + * still need to invoke hwloc_topology_load() to actually load the + * topology information. + * + * \return -1 with errno set to EINVAL on failure to read the XML file. + * + * \note See also hwloc_topology_set_userdata_import_callback() + * for importing application-specific object userdata. + * + * \note For convenience, this backend provides empty binding hooks which just + * return success. To have hwloc still actually call OS-specific hooks, the + * ::HWLOC_TOPOLOGY_FLAG_IS_THISSYSTEM has to be set to assert that the loaded + * file is really the underlying system. + * + * \note On success, the XML component replaces the previously enabled + * component (if any), but the topology is not actually modified until + * hwloc_topology_load(). + */ +HWLOC_DECLSPEC int hwloc_topology_set_xml(hwloc_topology_t __hwloc_restrict topology, const char * __hwloc_restrict xmlpath); + +/** \brief Enable XML based topology using a memory buffer (instead of + * a file, as with hwloc_topology_set_xml()). + * + * Gather topology information from the XML memory buffer given at \p + * buffer and of length \p size. This buffer may have been filled + * earlier with hwloc_topology_export_xmlbuffer() in hwloc/export.h. + * + * Note that this function does not actually load topology + * information; it just tells hwloc where to load it from. You'll + * still need to invoke hwloc_topology_load() to actually load the + * topology information. + * + * \return -1 with errno set to EINVAL on failure to read the XML buffer. + * + * \note See also hwloc_topology_set_userdata_import_callback() + * for importing application-specific object userdata. + * + * \note For convenience, this backend provides empty binding hooks which just + * return success. To have hwloc still actually call OS-specific hooks, the + * ::HWLOC_TOPOLOGY_FLAG_IS_THISSYSTEM has to be set to assert that the loaded + * file is really the underlying system. + * + * \note On success, the XML component replaces the previously enabled + * component (if any), but the topology is not actually modified until + * hwloc_topology_load(). + */ +HWLOC_DECLSPEC int hwloc_topology_set_xmlbuffer(hwloc_topology_t __hwloc_restrict topology, const char * __hwloc_restrict buffer, int size); + +/** @} */ + + + +/** \defgroup hwlocality_configuration Topology Detection Configuration and Query + * + * Several functions can optionally be called between hwloc_topology_init() and + * hwloc_topology_load() to configure how the detection should be performed, + * e.g. to ignore some objects types, define a synthetic topology, etc. + * + * @{ + */ + +/** \brief Flags to be set onto a topology context before load. + * + * Flags should be given to hwloc_topology_set_flags(). + * They may also be returned by hwloc_topology_get_flags(). + */ +enum hwloc_topology_flags_e { + /** \brief Detect the whole system, ignore reservations. + * + * Gather all resources, even if some were disabled by the administrator. + * For instance, ignore Linux Cgroup/Cpusets and gather all processors and memory nodes. + * + * When this flag is not set, PUs and NUMA nodes that are disallowed are not added to the topology. + * Parent objects (package, core, cache, etc.) are added only if some of their children are allowed. + * + * When this flag is set, the actual sets of allowed PUs and NUMA nodes are given + * by hwloc_topology_get_allowed_cpuset() and hwloc_topology_get_allowed_nodeset(). + * They may be smaller than the root object cpuset and nodeset. + * + * When this flag is not set, all existing PUs and NUMA nodes in the topology + * are allowed. hwloc_topology_get_allowed_cpuset() and hwloc_topology_get_allowed_nodeset() + * are equal to the root object cpuset and nodeset. + * + * If the current topology is exported to XML and reimported later, this flag + * should be set again in the reimported topology so that disallowed resources + * are reimported as well. + * \hideinitializer + */ + HWLOC_TOPOLOGY_FLAG_WHOLE_SYSTEM = (1UL<<0), + + /** \brief Assume that the selected backend provides the topology for the + * system on which we are running. + * + * This forces hwloc_topology_is_thissystem() to return 1, i.e. makes hwloc assume that + * the selected backend provides the topology for the system on which we are running, + * even if it is not the OS-specific backend but the XML backend for instance. + * This means making the binding functions actually call the OS-specific + * system calls and really do binding, while the XML backend would otherwise + * provide empty hooks just returning success. + * + * Setting the environment variable HWLOC_THISSYSTEM may also result in the + * same behavior. + * + * This can be used for efficiency reasons to first detect the topology once, + * save it to an XML file, and quickly reload it later through the XML + * backend, but still having binding functions actually do bind. + * \hideinitializer + */ + HWLOC_TOPOLOGY_FLAG_IS_THISSYSTEM = (1UL<<1), + + /** \brief Get the set of allowed resources from the local operating system even if the topology was loaded from XML or synthetic description. + * + * If the topology was loaded from XML or from a synthetic string, + * restrict it by applying the current process restrictions such as + * Linux Cgroup/Cpuset. + * + * This is useful when the topology is not loaded directly from + * the local machine (e.g. for performance reason) and it comes + * with all resources, while the running process is restricted + * to only parts of the machine. + * + * This flag is ignored unless ::HWLOC_TOPOLOGY_FLAG_IS_THISSYSTEM is + * also set since the loaded topology must match the underlying machine + * where restrictions will be gathered from. + * + * Setting the environment variable HWLOC_THISSYSTEM_ALLOWED_RESOURCES + * would result in the same behavior. + * \hideinitializer + */ + HWLOC_TOPOLOGY_FLAG_THISSYSTEM_ALLOWED_RESOURCES = (1UL<<2) +}; + +/** \brief Set OR'ed flags to non-yet-loaded topology. + * + * Set a OR'ed set of ::hwloc_topology_flags_e onto a topology that was not yet loaded. + * + * If this function is called multiple times, the last invokation will erase + * and replace the set of flags that was previously set. + * + * The flags set in a topology may be retrieved with hwloc_topology_get_flags() + */ +HWLOC_DECLSPEC int hwloc_topology_set_flags (hwloc_topology_t topology, unsigned long flags); + +/** \brief Get OR'ed flags of a topology. + * + * Get the OR'ed set of ::hwloc_topology_flags_e of a topology. + * + * \return the flags previously set with hwloc_topology_set_flags(). + */ +HWLOC_DECLSPEC unsigned long hwloc_topology_get_flags (hwloc_topology_t topology); + +/** \brief Does the topology context come from this system? + * + * \return 1 if this topology context was built using the system + * running this program. + * \return 0 instead (for instance if using another file-system root, + * a XML topology file, or a synthetic topology). + */ +HWLOC_DECLSPEC int hwloc_topology_is_thissystem(hwloc_topology_t __hwloc_restrict topology) __hwloc_attribute_pure; + +/** \brief Flags describing actual discovery support for this topology. */ +struct hwloc_topology_discovery_support { + /** \brief Detecting the number of PU objects is supported. */ + unsigned char pu; + /** \brief Detecting the number of NUMA nodes is supported. */ + unsigned char numa; + /** \brief Detecting the amount of memory in NUMA nodes is supported. */ + unsigned char numa_memory; +}; + +/** \brief Flags describing actual PU binding support for this topology. + * + * A flag may be set even if the feature isn't supported in all cases + * (e.g. binding to random sets of non-contiguous objects). + */ +struct hwloc_topology_cpubind_support { + /** Binding the whole current process is supported. */ + unsigned char set_thisproc_cpubind; + /** Getting the binding of the whole current process is supported. */ + unsigned char get_thisproc_cpubind; + /** Binding a whole given process is supported. */ + unsigned char set_proc_cpubind; + /** Getting the binding of a whole given process is supported. */ + unsigned char get_proc_cpubind; + /** Binding the current thread only is supported. */ + unsigned char set_thisthread_cpubind; + /** Getting the binding of the current thread only is supported. */ + unsigned char get_thisthread_cpubind; + /** Binding a given thread only is supported. */ + unsigned char set_thread_cpubind; + /** Getting the binding of a given thread only is supported. */ + unsigned char get_thread_cpubind; + /** Getting the last processors where the whole current process ran is supported */ + unsigned char get_thisproc_last_cpu_location; + /** Getting the last processors where a whole process ran is supported */ + unsigned char get_proc_last_cpu_location; + /** Getting the last processors where the current thread ran is supported */ + unsigned char get_thisthread_last_cpu_location; +}; + +/** \brief Flags describing actual memory binding support for this topology. + * + * A flag may be set even if the feature isn't supported in all cases + * (e.g. binding to random sets of non-contiguous objects). + */ +struct hwloc_topology_membind_support { + /** Binding the whole current process is supported. */ + unsigned char set_thisproc_membind; + /** Getting the binding of the whole current process is supported. */ + unsigned char get_thisproc_membind; + /** Binding a whole given process is supported. */ + unsigned char set_proc_membind; + /** Getting the binding of a whole given process is supported. */ + unsigned char get_proc_membind; + /** Binding the current thread only is supported. */ + unsigned char set_thisthread_membind; + /** Getting the binding of the current thread only is supported. */ + unsigned char get_thisthread_membind; + /** Binding a given memory area is supported. */ + unsigned char set_area_membind; + /** Getting the binding of a given memory area is supported. */ + unsigned char get_area_membind; + /** Allocating a bound memory area is supported. */ + unsigned char alloc_membind; + /** First-touch policy is supported. */ + unsigned char firsttouch_membind; + /** Bind policy is supported. */ + unsigned char bind_membind; + /** Interleave policy is supported. */ + unsigned char interleave_membind; + /** Next-touch migration policy is supported. */ + unsigned char nexttouch_membind; + /** Migration flags is supported. */ + unsigned char migrate_membind; + /** Getting the last NUMA nodes where a memory area was allocated is supported */ + unsigned char get_area_memlocation; +}; + +/** \brief Set of flags describing actual support for this topology. + * + * This is retrieved with hwloc_topology_get_support() and will be valid until + * the topology object is destroyed. Note: the values are correct only after + * discovery. + */ +struct hwloc_topology_support { + struct hwloc_topology_discovery_support *discovery; + struct hwloc_topology_cpubind_support *cpubind; + struct hwloc_topology_membind_support *membind; +}; + +/** \brief Retrieve the topology support. + * + * Each flag indicates whether a feature is supported. + * If set to 0, the feature is not supported. + * If set to 1, the feature is supported, but the corresponding + * call may still fail in some corner cases. + * + * These features are also listed by hwloc-info \--support + */ +HWLOC_DECLSPEC const struct hwloc_topology_support *hwloc_topology_get_support(hwloc_topology_t __hwloc_restrict topology); + +/** \brief Type filtering flags. + * + * By default, most objects are kept (::HWLOC_TYPE_FILTER_KEEP_ALL). + * Instruction caches, I/O and Misc objects are ignored by default (::HWLOC_TYPE_FILTER_KEEP_NONE). + * Group levels are ignored unless they bring structure (::HWLOC_TYPE_FILTER_KEEP_STRUCTURE). + * + * Note that group objects are also ignored individually (without the entire level) + * when they do not bring structure. + */ +enum hwloc_type_filter_e { + /** \brief Keep all objects of this type. + * + * Cannot be set for ::HWLOC_OBJ_GROUP (groups are designed only to add more structure to the topology). + * \hideinitializer + */ + HWLOC_TYPE_FILTER_KEEP_ALL = 0, + + /** \brief Ignore all objects of this type. + * + * The bottom-level type ::HWLOC_OBJ_PU, the ::HWLOC_OBJ_NUMANODE type, and + * the top-level type ::HWLOC_OBJ_MACHINE may not be ignored. + * \hideinitializer + */ + HWLOC_TYPE_FILTER_KEEP_NONE = 1, + + /** \brief Only ignore objects if their entire level does not bring any structure. + * + * Keep the entire level of objects if at least one of these objects adds + * structure to the topology. An object brings structure when it has multiple + * children and it is not the only child of its parent. + * + * If all objects in the level are the only child of their parent, and if none + * of them has multiple children, the entire level is removed. + * + * Cannot be set for I/O and Misc objects since the topology structure does not matter there. + * \hideinitializer + */ + HWLOC_TYPE_FILTER_KEEP_STRUCTURE = 2, + + /** \brief Only keep likely-important objects of the given type. + * + * It is only useful for I/O object types. + * For ::HWLOC_OBJ_PCI_DEVICE and ::HWLOC_OBJ_OS_DEVICE, it means that only objects + * of major/common kinds are kept (storage, network, OpenFabrics, Intel MICs, CUDA, + * OpenCL, NVML, and displays). + * Also, only OS devices directly attached on PCI (e.g. no USB) are reported. + * For ::HWLOC_OBJ_BRIDGE, it means that bridges are kept only if they have children. + * + * This flag equivalent to ::HWLOC_TYPE_FILTER_KEEP_ALL for Normal, Memory and Misc types + * since they are likely important. + * \hideinitializer + */ + HWLOC_TYPE_FILTER_KEEP_IMPORTANT = 3 +}; + +/** \brief Set the filtering for the given object type. + */ +HWLOC_DECLSPEC int hwloc_topology_set_type_filter(hwloc_topology_t topology, hwloc_obj_type_t type, enum hwloc_type_filter_e filter); + +/** \brief Get the current filtering for the given object type. + */ +HWLOC_DECLSPEC int hwloc_topology_get_type_filter(hwloc_topology_t topology, hwloc_obj_type_t type, enum hwloc_type_filter_e *filter); + +/** \brief Set the filtering for all object types. + * + * If some types do not support this filtering, they are silently ignored. + */ +HWLOC_DECLSPEC int hwloc_topology_set_all_types_filter(hwloc_topology_t topology, enum hwloc_type_filter_e filter); + +/** \brief Set the filtering for all cache object types. + */ +HWLOC_DECLSPEC int hwloc_topology_set_cache_types_filter(hwloc_topology_t topology, enum hwloc_type_filter_e filter); + +/** \brief Set the filtering for all instruction cache object types. + */ +HWLOC_DECLSPEC int hwloc_topology_set_icache_types_filter(hwloc_topology_t topology, enum hwloc_type_filter_e filter); + +/** \brief Set the filtering for all I/O object types. + */ +HWLOC_DECLSPEC int hwloc_topology_set_io_types_filter(hwloc_topology_t topology, enum hwloc_type_filter_e filter); + +/** \brief Set the topology-specific userdata pointer. + * + * Each topology may store one application-given private data pointer. + * It is initialized to \c NULL. + * hwloc will never modify it. + * + * Use it as you wish, after hwloc_topology_init() and until hwloc_topolog_destroy(). + * + * This pointer is not exported to XML. + */ +HWLOC_DECLSPEC void hwloc_topology_set_userdata(hwloc_topology_t topology, const void *userdata); + +/** \brief Retrieve the topology-specific userdata pointer. + * + * Retrieve the application-given private data pointer that was + * previously set with hwloc_topology_set_userdata(). + */ +HWLOC_DECLSPEC void * hwloc_topology_get_userdata(hwloc_topology_t topology); + +/** @} */ + + + +/** \defgroup hwlocality_tinker Modifying a loaded Topology + * @{ + */ + +/** \brief Flags to be given to hwloc_topology_restrict(). */ +enum hwloc_restrict_flags_e { + /** \brief Remove all objects that became CPU-less. + * By default, only objects that contain no PU and no memory are removed. + * \hideinitializer + */ + HWLOC_RESTRICT_FLAG_REMOVE_CPULESS = (1UL<<0), + + /** \brief Move Misc objects to ancestors if their parents are removed during restriction. + * If this flag is not set, Misc objects are removed when their parents are removed. + * \hideinitializer + */ + HWLOC_RESTRICT_FLAG_ADAPT_MISC = (1UL<<1), + + /** \brief Move I/O objects to ancestors if their parents are removed during restriction. + * If this flag is not set, I/O devices and bridges are removed when their parents are removed. + * \hideinitializer + */ + HWLOC_RESTRICT_FLAG_ADAPT_IO = (1UL<<2) +}; + +/** \brief Restrict the topology to the given CPU set. + * + * Topology \p topology is modified so as to remove all objects that + * are not included (or partially included) in the CPU set \p cpuset. + * All objects CPU and node sets are restricted accordingly. + * + * \p flags is a OR'ed set of ::hwloc_restrict_flags_e. + * + * \note This call may not be reverted by restricting back to a larger + * cpuset. Once dropped during restriction, objects may not be brought + * back, except by loading another topology with hwloc_topology_load(). + * + * \return 0 on success. + * + * \return -1 with errno set to EINVAL if the input cpuset is invalid. + * The topology is not modified in this case. + * + * \return -1 with errno set to ENOMEM on failure to allocate internal data. + * The topology is reinitialized in this case. It should be either + * destroyed with hwloc_topology_destroy() or configured and loaded again. + */ +HWLOC_DECLSPEC int hwloc_topology_restrict(hwloc_topology_t __hwloc_restrict topology, hwloc_const_cpuset_t cpuset, unsigned long flags); + +/** \brief Add a MISC object as a leaf of the topology + * + * A new MISC object will be created and inserted into the topology at the + * position given by parent. It is appended to the list of existing Misc children, + * without ever adding any intermediate hierarchy level. This is useful for + * annotating the topology without actually changing the hierarchy. + * + * \p name is supposed to be unique across all Misc objects in the topology. + * It will be duplicated to setup the new object attributes. + * + * The new leaf object will not have any \p cpuset. + * + * \return the newly-created object + * + * \return \c NULL on error. + * + * \return \c NULL if Misc objects are filtered-out of the topology (::HWLOC_TYPE_FILTER_KEEP_NONE). + * + * \note If \p name contains some non-printable characters, they will + * be dropped when exporting to XML, see hwloc_topology_export_xml() in hwloc/export.h. + */ +HWLOC_DECLSPEC hwloc_obj_t hwloc_topology_insert_misc_object(hwloc_topology_t topology, hwloc_obj_t parent, const char *name); + +/** \brief Allocate a Group object to insert later with hwloc_topology_insert_group_object(). + * + * This function returns a new Group object. + * The caller should (at least) initialize its sets before inserting the object. + * See hwloc_topology_insert_group_object(). + * + * The \p subtype object attribute may be set to display something else + * than "Group" as the type name for this object in lstopo. + * Custom name/value info pairs may be added with hwloc_obj_add_info() after + * insertion. + * + * The \p kind group attribute should be 0. The \p subkind group attribute may + * be set to identify multiple Groups of the same level. + * + * It is recommended not to set any other object attribute before insertion, + * since the Group may get discarded during insertion. + * + * The object will be destroyed if passed to hwloc_topology_insert_group_object() + * without any set defined. + */ +HWLOC_DECLSPEC hwloc_obj_t hwloc_topology_alloc_group_object(hwloc_topology_t topology); + +/** \brief Add more structure to the topology by adding an intermediate Group + * + * The caller should first allocate a new Group object with hwloc_topology_alloc_group_object(). + * Then it must setup at least one of its CPU or node sets to specify + * the final location of the Group in the topology. + * Then the object can be passed to this function for actual insertion in the topology. + * + * The group \p dont_merge attribute may be set to prevent the core from + * ever merging this object with another object hierarchically-identical. + * + * Either the cpuset or nodeset field (or both, if compatible) must be set + * to a non-empty bitmap. The complete_cpuset or complete_nodeset may be set + * instead if inserting with respect to the complete topology + * (including disallowed, offline or unknown objects). + * + * It grouping several objects, hwloc_obj_add_other_obj_sets() is an easy way + * to build the Group sets iteratively. + * + * These sets cannot be larger than the current topology, or they would get + * restricted silently. + * + * The core will setup the other sets after actual insertion. + * + * \return The inserted object if it was properly inserted. + * + * \return An existing object if the Group was discarded because the topology already + * contained an object at the same location (the Group did not add any locality information). + * Any name/info key pair set before inserting is appended to the existing object. + * + * \return \c NULL if the insertion failed because of conflicting sets in topology tree. + * + * \return \c NULL if Group objects are filtered-out of the topology (::HWLOC_TYPE_FILTER_KEEP_NONE). + * + * \return \c NULL if the object was discarded because no set was initialized in the Group + * before insert, or all of them were empty. + */ +HWLOC_DECLSPEC hwloc_obj_t hwloc_topology_insert_group_object(hwloc_topology_t topology, hwloc_obj_t group); + +/** \brief Setup object cpusets/nodesets by OR'ing another object's sets. + * + * For each defined cpuset or nodeset in \p src, allocate the corresponding set + * in \p dst and add \p src to it by OR'ing sets. + * + * This function is convenient between hwloc_topology_alloc_group_object() + * and hwloc_topology_insert_group_object(). It builds the sets of the new Group + * that will be inserted as a new intermediate parent of several objects. + */ +HWLOC_DECLSPEC int hwloc_obj_add_other_obj_sets(hwloc_obj_t dst, hwloc_obj_t src); + +/** @} */ + + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + +/* high-level helpers */ +#include + +/* inline code of some functions above */ +#include + +/* exporting to XML or synthetic */ +#include + +/* distances */ +#include + +/* topology diffs */ +#include + +/* deprecated headers */ +#include + +#endif /* HWLOC_H */ diff --git a/src/3rdparty/hwloc/include/hwloc/autogen/config.h b/src/3rdparty/hwloc/include/hwloc/autogen/config.h new file mode 100644 index 00000000..14d4481d --- /dev/null +++ b/src/3rdparty/hwloc/include/hwloc/autogen/config.h @@ -0,0 +1,59 @@ +/* + * Copyright © 2009 CNRS + * Copyright © 2009-2018 Inria. All rights reserved. + * Copyright © 2009-2012 Université Bordeaux + * Copyright © 2009-2011 Cisco Systems, Inc. All rights reserved. + * See COPYING in top-level directory. + */ + +/* The configuration file */ + +#ifndef HWLOC_CONFIG_H +#define HWLOC_CONFIG_H + +#define HWLOC_VERSION "2.0.4" +#define HWLOC_VERSION_MAJOR 2 +#define HWLOC_VERSION_MINOR 0 +#define HWLOC_VERSION_RELEASE 4 +#define HWLOC_VERSION_GREEK "" + +#define __hwloc_restrict +#define __hwloc_inline __inline + +#define __hwloc_attribute_unused +#define __hwloc_attribute_malloc +#define __hwloc_attribute_const +#define __hwloc_attribute_pure +#define __hwloc_attribute_deprecated +#define __hwloc_attribute_may_alias +#define __hwloc_attribute_warn_unused_result + +/* Defined to 1 if you have the `windows.h' header. */ +#define HWLOC_HAVE_WINDOWS_H 1 +#define hwloc_pid_t HANDLE +#define hwloc_thread_t HANDLE + +#include +#include +typedef DWORDLONG hwloc_uint64_t; + +#if defined( _USRDLL ) /* dynamic linkage */ +#if defined( DECLSPEC_EXPORTS ) +#define HWLOC_DECLSPEC __declspec(dllexport) +#else +#define HWLOC_DECLSPEC __declspec(dllimport) +#endif +#else /* static linkage */ +#define HWLOC_DECLSPEC +#endif + +/* Whether we need to re-define all the hwloc public symbols or not */ +#define HWLOC_SYM_TRANSFORM 0 + +/* The hwloc symbol prefix */ +#define HWLOC_SYM_PREFIX hwloc_ + +/* The hwloc symbol prefix in all caps */ +#define HWLOC_SYM_PREFIX_CAPS HWLOC_ + +#endif /* HWLOC_CONFIG_H */ diff --git a/src/3rdparty/hwloc/include/hwloc/bitmap.h b/src/3rdparty/hwloc/include/hwloc/bitmap.h new file mode 100644 index 00000000..bae623c8 --- /dev/null +++ b/src/3rdparty/hwloc/include/hwloc/bitmap.h @@ -0,0 +1,467 @@ +/* + * Copyright © 2009 CNRS + * Copyright © 2009-2018 Inria. All rights reserved. + * Copyright © 2009-2012 Université Bordeaux + * Copyright © 2009-2011 Cisco Systems, Inc. All rights reserved. + * See COPYING in top-level directory. + */ + +/** \file + * \brief The bitmap API, for use in hwloc itself. + */ + +#ifndef HWLOC_BITMAP_H +#define HWLOC_BITMAP_H + +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + +/** \defgroup hwlocality_bitmap The bitmap API + * + * The ::hwloc_bitmap_t type represents a set of integers (positive or null). + * A bitmap may be of infinite size (all bits are set after some point). + * A bitmap may even be full if all bits are set. + * + * Bitmaps are used by hwloc for sets of OS processors + * (which may actually be hardware threads) as by ::hwloc_cpuset_t + * (a typedef for ::hwloc_bitmap_t), or sets of NUMA memory nodes + * as ::hwloc_nodeset_t (also a typedef for ::hwloc_bitmap_t). + * Those are used for cpuset and nodeset fields in the ::hwloc_obj structure, + * see \ref hwlocality_object_sets. + * + * Both CPU and node sets are always indexed by OS physical number. + * However users should usually not build CPU and node sets manually + * (e.g. with hwloc_bitmap_set()). + * One should rather use existing object sets and combine them with + * hwloc_bitmap_or(), etc. + * For instance, binding the current thread on a pair of cores may be performed with: + * \code + * hwloc_obj_t core1 = ... , core2 = ... ; + * hwloc_bitmap_t set = hwloc_bitmap_alloc(); + * hwloc_bitmap_or(set, core1->cpuset, core2->cpuset); + * hwloc_set_cpubind(topology, set, HWLOC_CPUBIND_THREAD); + * hwloc_bitmap_free(set); + * \endcode + * + * \note Most functions below return an int that may be negative in case of + * error. The usual error case would be an internal failure to realloc/extend + * the storage of the bitmap (\p errno would be set to \c ENOMEM). + * + * \note Several examples of using the bitmap API are available under the + * doc/examples/ directory in the source tree. + * Regression tests such as tests/hwloc/hwloc_bitmap*.c also make intensive use + * of this API. + * @{ + */ + + +/** \brief + * Set of bits represented as an opaque pointer to an internal bitmap. + */ +typedef struct hwloc_bitmap_s * hwloc_bitmap_t; +/** \brief a non-modifiable ::hwloc_bitmap_t */ +typedef const struct hwloc_bitmap_s * hwloc_const_bitmap_t; + + +/* + * Bitmap allocation, freeing and copying. + */ + +/** \brief Allocate a new empty bitmap. + * + * \returns A valid bitmap or \c NULL. + * + * The bitmap should be freed by a corresponding call to + * hwloc_bitmap_free(). + */ +HWLOC_DECLSPEC hwloc_bitmap_t hwloc_bitmap_alloc(void) __hwloc_attribute_malloc; + +/** \brief Allocate a new full bitmap. */ +HWLOC_DECLSPEC hwloc_bitmap_t hwloc_bitmap_alloc_full(void) __hwloc_attribute_malloc; + +/** \brief Free bitmap \p bitmap. + * + * If \p bitmap is \c NULL, no operation is performed. + */ +HWLOC_DECLSPEC void hwloc_bitmap_free(hwloc_bitmap_t bitmap); + +/** \brief Duplicate bitmap \p bitmap by allocating a new bitmap and copying \p bitmap contents. + * + * If \p bitmap is \c NULL, \c NULL is returned. + */ +HWLOC_DECLSPEC hwloc_bitmap_t hwloc_bitmap_dup(hwloc_const_bitmap_t bitmap) __hwloc_attribute_malloc; + +/** \brief Copy the contents of bitmap \p src into the already allocated bitmap \p dst */ +HWLOC_DECLSPEC int hwloc_bitmap_copy(hwloc_bitmap_t dst, hwloc_const_bitmap_t src); + + +/* + * Bitmap/String Conversion + */ + +/** \brief Stringify a bitmap. + * + * Up to \p buflen characters may be written in buffer \p buf. + * + * If \p buflen is 0, \p buf may safely be \c NULL. + * + * \return the number of character that were actually written if not truncating, + * or that would have been written (not including the ending \\0). + */ +HWLOC_DECLSPEC int hwloc_bitmap_snprintf(char * __hwloc_restrict buf, size_t buflen, hwloc_const_bitmap_t bitmap); + +/** \brief Stringify a bitmap into a newly allocated string. + * + * \return -1 on error. + */ +HWLOC_DECLSPEC int hwloc_bitmap_asprintf(char ** strp, hwloc_const_bitmap_t bitmap); + +/** \brief Parse a bitmap string and stores it in bitmap \p bitmap. + */ +HWLOC_DECLSPEC int hwloc_bitmap_sscanf(hwloc_bitmap_t bitmap, const char * __hwloc_restrict string); + +/** \brief Stringify a bitmap in the list format. + * + * Lists are comma-separated indexes or ranges. + * Ranges are dash separated indexes. + * The last range may not have an ending indexes if the bitmap is infinitely set. + * + * Up to \p buflen characters may be written in buffer \p buf. + * + * If \p buflen is 0, \p buf may safely be \c NULL. + * + * \return the number of character that were actually written if not truncating, + * or that would have been written (not including the ending \\0). + */ +HWLOC_DECLSPEC int hwloc_bitmap_list_snprintf(char * __hwloc_restrict buf, size_t buflen, hwloc_const_bitmap_t bitmap); + +/** \brief Stringify a bitmap into a newly allocated list string. + * + * \return -1 on error. + */ +HWLOC_DECLSPEC int hwloc_bitmap_list_asprintf(char ** strp, hwloc_const_bitmap_t bitmap); + +/** \brief Parse a list string and stores it in bitmap \p bitmap. + */ +HWLOC_DECLSPEC int hwloc_bitmap_list_sscanf(hwloc_bitmap_t bitmap, const char * __hwloc_restrict string); + +/** \brief Stringify a bitmap in the taskset-specific format. + * + * The taskset command manipulates bitmap strings that contain a single + * (possible very long) hexadecimal number starting with 0x. + * + * Up to \p buflen characters may be written in buffer \p buf. + * + * If \p buflen is 0, \p buf may safely be \c NULL. + * + * \return the number of character that were actually written if not truncating, + * or that would have been written (not including the ending \\0). + */ +HWLOC_DECLSPEC int hwloc_bitmap_taskset_snprintf(char * __hwloc_restrict buf, size_t buflen, hwloc_const_bitmap_t bitmap); + +/** \brief Stringify a bitmap into a newly allocated taskset-specific string. + * + * \return -1 on error. + */ +HWLOC_DECLSPEC int hwloc_bitmap_taskset_asprintf(char ** strp, hwloc_const_bitmap_t bitmap); + +/** \brief Parse a taskset-specific bitmap string and stores it in bitmap \p bitmap. + */ +HWLOC_DECLSPEC int hwloc_bitmap_taskset_sscanf(hwloc_bitmap_t bitmap, const char * __hwloc_restrict string); + + +/* + * Building bitmaps. + */ + +/** \brief Empty the bitmap \p bitmap */ +HWLOC_DECLSPEC void hwloc_bitmap_zero(hwloc_bitmap_t bitmap); + +/** \brief Fill bitmap \p bitmap with all possible indexes (even if those objects don't exist or are otherwise unavailable) */ +HWLOC_DECLSPEC void hwloc_bitmap_fill(hwloc_bitmap_t bitmap); + +/** \brief Empty the bitmap \p bitmap and add bit \p id */ +HWLOC_DECLSPEC int hwloc_bitmap_only(hwloc_bitmap_t bitmap, unsigned id); + +/** \brief Fill the bitmap \p and clear the index \p id */ +HWLOC_DECLSPEC int hwloc_bitmap_allbut(hwloc_bitmap_t bitmap, unsigned id); + +/** \brief Setup bitmap \p bitmap from unsigned long \p mask */ +HWLOC_DECLSPEC int hwloc_bitmap_from_ulong(hwloc_bitmap_t bitmap, unsigned long mask); + +/** \brief Setup bitmap \p bitmap from unsigned long \p mask used as \p i -th subset */ +HWLOC_DECLSPEC int hwloc_bitmap_from_ith_ulong(hwloc_bitmap_t bitmap, unsigned i, unsigned long mask); + + +/* + * Modifying bitmaps. + */ + +/** \brief Add index \p id in bitmap \p bitmap */ +HWLOC_DECLSPEC int hwloc_bitmap_set(hwloc_bitmap_t bitmap, unsigned id); + +/** \brief Add indexes from \p begin to \p end in bitmap \p bitmap. + * + * If \p end is \c -1, the range is infinite. + */ +HWLOC_DECLSPEC int hwloc_bitmap_set_range(hwloc_bitmap_t bitmap, unsigned begin, int end); + +/** \brief Replace \p i -th subset of bitmap \p bitmap with unsigned long \p mask */ +HWLOC_DECLSPEC int hwloc_bitmap_set_ith_ulong(hwloc_bitmap_t bitmap, unsigned i, unsigned long mask); + +/** \brief Remove index \p id from bitmap \p bitmap */ +HWLOC_DECLSPEC int hwloc_bitmap_clr(hwloc_bitmap_t bitmap, unsigned id); + +/** \brief Remove indexes from \p begin to \p end in bitmap \p bitmap. + * + * If \p end is \c -1, the range is infinite. + */ +HWLOC_DECLSPEC int hwloc_bitmap_clr_range(hwloc_bitmap_t bitmap, unsigned begin, int end); + +/** \brief Keep a single index among those set in bitmap \p bitmap + * + * May be useful before binding so that the process does not + * have a chance of migrating between multiple logical CPUs + * in the original mask. + * Instead of running the task on any PU inside the given CPU set, + * the operating system scheduler will be forced to run it on a single + * of these PUs. + * It avoids a migration overhead and cache-line ping-pongs between PUs. + * + * \note This function is NOT meant to distribute multiple processes + * within a single CPU set. It always return the same single bit when + * called multiple times on the same input set. hwloc_distrib() may + * be used for generating CPU sets to distribute multiple tasks below + * a single multi-PU object. + * + * \note This function cannot be applied to an object set directly. It + * should be applied to a copy (which may be obtained with hwloc_bitmap_dup()). + */ +HWLOC_DECLSPEC int hwloc_bitmap_singlify(hwloc_bitmap_t bitmap); + + +/* + * Consulting bitmaps. + */ + +/** \brief Convert the beginning part of bitmap \p bitmap into unsigned long \p mask */ +HWLOC_DECLSPEC unsigned long hwloc_bitmap_to_ulong(hwloc_const_bitmap_t bitmap) __hwloc_attribute_pure; + +/** \brief Convert the \p i -th subset of bitmap \p bitmap into unsigned long mask */ +HWLOC_DECLSPEC unsigned long hwloc_bitmap_to_ith_ulong(hwloc_const_bitmap_t bitmap, unsigned i) __hwloc_attribute_pure; + +/** \brief Test whether index \p id is part of bitmap \p bitmap. + * + * \return 1 if the bit at index \p id is set in bitmap \p bitmap, 0 otherwise. + */ +HWLOC_DECLSPEC int hwloc_bitmap_isset(hwloc_const_bitmap_t bitmap, unsigned id) __hwloc_attribute_pure; + +/** \brief Test whether bitmap \p bitmap is empty + * + * \return 1 if bitmap is empty, 0 otherwise. + */ +HWLOC_DECLSPEC int hwloc_bitmap_iszero(hwloc_const_bitmap_t bitmap) __hwloc_attribute_pure; + +/** \brief Test whether bitmap \p bitmap is completely full + * + * \return 1 if bitmap is full, 0 otherwise. + * + * \note A full bitmap is always infinitely set. + */ +HWLOC_DECLSPEC int hwloc_bitmap_isfull(hwloc_const_bitmap_t bitmap) __hwloc_attribute_pure; + +/** \brief Compute the first index (least significant bit) in bitmap \p bitmap + * + * \return -1 if no index is set in \p bitmap. + */ +HWLOC_DECLSPEC int hwloc_bitmap_first(hwloc_const_bitmap_t bitmap) __hwloc_attribute_pure; + +/** \brief Compute the next index in bitmap \p bitmap which is after index \p prev + * + * If \p prev is -1, the first index is returned. + * + * \return -1 if no index with higher index is set in \p bitmap. + */ +HWLOC_DECLSPEC int hwloc_bitmap_next(hwloc_const_bitmap_t bitmap, int prev) __hwloc_attribute_pure; + +/** \brief Compute the last index (most significant bit) in bitmap \p bitmap + * + * \return -1 if no index is set in \p bitmap, or if \p bitmap is infinitely set. + */ +HWLOC_DECLSPEC int hwloc_bitmap_last(hwloc_const_bitmap_t bitmap) __hwloc_attribute_pure; + +/** \brief Compute the "weight" of bitmap \p bitmap (i.e., number of + * indexes that are in the bitmap). + * + * \return the number of indexes that are in the bitmap. + * + * \return -1 if \p bitmap is infinitely set. + */ +HWLOC_DECLSPEC int hwloc_bitmap_weight(hwloc_const_bitmap_t bitmap) __hwloc_attribute_pure; + +/** \brief Compute the first unset index (least significant bit) in bitmap \p bitmap + * + * \return -1 if no index is unset in \p bitmap. + */ +HWLOC_DECLSPEC int hwloc_bitmap_first_unset(hwloc_const_bitmap_t bitmap) __hwloc_attribute_pure; + +/** \brief Compute the next unset index in bitmap \p bitmap which is after index \p prev + * + * If \p prev is -1, the first unset index is returned. + * + * \return -1 if no index with higher index is unset in \p bitmap. + */ +HWLOC_DECLSPEC int hwloc_bitmap_next_unset(hwloc_const_bitmap_t bitmap, int prev) __hwloc_attribute_pure; + +/** \brief Compute the last unset index (most significant bit) in bitmap \p bitmap + * + * \return -1 if no index is unset in \p bitmap, or if \p bitmap is infinitely set. + */ +HWLOC_DECLSPEC int hwloc_bitmap_last_unset(hwloc_const_bitmap_t bitmap) __hwloc_attribute_pure; + +/** \brief Loop macro iterating on bitmap \p bitmap + * + * The loop must start with hwloc_bitmap_foreach_begin() and end + * with hwloc_bitmap_foreach_end() followed by a terminating ';'. + * + * \p index is the loop variable; it should be an unsigned int. The + * first iteration will set \p index to the lowest index in the bitmap. + * Successive iterations will iterate through, in order, all remaining + * indexes set in the bitmap. To be specific: each iteration will return a + * value for \p index such that hwloc_bitmap_isset(bitmap, index) is true. + * + * The assert prevents the loop from being infinite if the bitmap is infinitely set. + * + * \hideinitializer + */ +#define hwloc_bitmap_foreach_begin(id, bitmap) \ +do { \ + assert(hwloc_bitmap_weight(bitmap) != -1); \ + for (id = hwloc_bitmap_first(bitmap); \ + (unsigned) id != (unsigned) -1; \ + id = hwloc_bitmap_next(bitmap, id)) { + +/** \brief End of loop macro iterating on a bitmap. + * + * Needs a terminating ';'. + * + * \sa hwloc_bitmap_foreach_begin() + * \hideinitializer + */ +#define hwloc_bitmap_foreach_end() \ + } \ +} while (0) + + +/* + * Combining bitmaps. + */ + +/** \brief Or bitmaps \p bitmap1 and \p bitmap2 and store the result in bitmap \p res + * + * \p res can be the same as \p bitmap1 or \p bitmap2 + */ +HWLOC_DECLSPEC int hwloc_bitmap_or (hwloc_bitmap_t res, hwloc_const_bitmap_t bitmap1, hwloc_const_bitmap_t bitmap2); + +/** \brief And bitmaps \p bitmap1 and \p bitmap2 and store the result in bitmap \p res + * + * \p res can be the same as \p bitmap1 or \p bitmap2 + */ +HWLOC_DECLSPEC int hwloc_bitmap_and (hwloc_bitmap_t res, hwloc_const_bitmap_t bitmap1, hwloc_const_bitmap_t bitmap2); + +/** \brief And bitmap \p bitmap1 and the negation of \p bitmap2 and store the result in bitmap \p res + * + * \p res can be the same as \p bitmap1 or \p bitmap2 + */ +HWLOC_DECLSPEC int hwloc_bitmap_andnot (hwloc_bitmap_t res, hwloc_const_bitmap_t bitmap1, hwloc_const_bitmap_t bitmap2); + +/** \brief Xor bitmaps \p bitmap1 and \p bitmap2 and store the result in bitmap \p res + * + * \p res can be the same as \p bitmap1 or \p bitmap2 + */ +HWLOC_DECLSPEC int hwloc_bitmap_xor (hwloc_bitmap_t res, hwloc_const_bitmap_t bitmap1, hwloc_const_bitmap_t bitmap2); + +/** \brief Negate bitmap \p bitmap and store the result in bitmap \p res + * + * \p res can be the same as \p bitmap + */ +HWLOC_DECLSPEC int hwloc_bitmap_not (hwloc_bitmap_t res, hwloc_const_bitmap_t bitmap); + + +/* + * Comparing bitmaps. + */ + +/** \brief Test whether bitmaps \p bitmap1 and \p bitmap2 intersects. + * + * \return 1 if bitmaps intersect, 0 otherwise. + */ +HWLOC_DECLSPEC int hwloc_bitmap_intersects (hwloc_const_bitmap_t bitmap1, hwloc_const_bitmap_t bitmap2) __hwloc_attribute_pure; + +/** \brief Test whether bitmap \p sub_bitmap is part of bitmap \p super_bitmap. + * + * \return 1 if \p sub_bitmap is included in \p super_bitmap, 0 otherwise. + * + * \note The empty bitmap is considered included in any other bitmap. + */ +HWLOC_DECLSPEC int hwloc_bitmap_isincluded (hwloc_const_bitmap_t sub_bitmap, hwloc_const_bitmap_t super_bitmap) __hwloc_attribute_pure; + +/** \brief Test whether bitmap \p bitmap1 is equal to bitmap \p bitmap2. + * + * \return 1 if bitmaps are equal, 0 otherwise. + */ +HWLOC_DECLSPEC int hwloc_bitmap_isequal (hwloc_const_bitmap_t bitmap1, hwloc_const_bitmap_t bitmap2) __hwloc_attribute_pure; + +/** \brief Compare bitmaps \p bitmap1 and \p bitmap2 using their lowest index. + * + * A bitmap is considered smaller if its least significant bit is smaller. + * The empty bitmap is considered higher than anything (because its least significant bit does not exist). + * + * \return -1 if \p bitmap1 is considered smaller than \p bitmap2. + * \return 1 if \p bitmap1 is considered larger than \p bitmap2. + * + * For instance comparing binary bitmaps 0011 and 0110 returns -1 + * (hence 0011 is considered smaller than 0110) + * because least significant bit of 0011 (0001) is smaller than least significant bit of 0110 (0010). + * Comparing 01001 and 00110 would also return -1 for the same reason. + * + * \return 0 if bitmaps are considered equal, even if they are not strictly equal. + * They just need to have the same least significant bit. + * For instance, comparing binary bitmaps 0010 and 0110 returns 0 because they have the same least significant bit. + */ +HWLOC_DECLSPEC int hwloc_bitmap_compare_first(hwloc_const_bitmap_t bitmap1, hwloc_const_bitmap_t bitmap2) __hwloc_attribute_pure; + +/** \brief Compare bitmaps \p bitmap1 and \p bitmap2 in lexicographic order. + * + * Lexicographic comparison of bitmaps, starting for their highest indexes. + * Compare last indexes first, then second, etc. + * The empty bitmap is considered lower than anything. + * + * \return -1 if \p bitmap1 is considered smaller than \p bitmap2. + * \return 1 if \p bitmap1 is considered larger than \p bitmap2. + * \return 0 if bitmaps are equal (contrary to hwloc_bitmap_compare_first()). + * + * For instance comparing binary bitmaps 0011 and 0110 returns -1 + * (hence 0011 is considered smaller than 0110). + * Comparing 00101 and 01010 returns -1 too. + * + * \note This is different from the non-existing hwloc_bitmap_compare_last() + * which would only compare the highest index of each bitmap. + */ +HWLOC_DECLSPEC int hwloc_bitmap_compare(hwloc_const_bitmap_t bitmap1, hwloc_const_bitmap_t bitmap2) __hwloc_attribute_pure; + +/** @} */ + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + +#endif /* HWLOC_BITMAP_H */ diff --git a/src/3rdparty/hwloc/include/hwloc/cuda.h b/src/3rdparty/hwloc/include/hwloc/cuda.h new file mode 100644 index 00000000..77c8473e --- /dev/null +++ b/src/3rdparty/hwloc/include/hwloc/cuda.h @@ -0,0 +1,220 @@ +/* + * Copyright © 2010-2017 Inria. All rights reserved. + * Copyright © 2010-2011 Université Bordeaux + * Copyright © 2011 Cisco Systems, Inc. All rights reserved. + * See COPYING in top-level directory. + */ + +/** \file + * \brief Macros to help interaction between hwloc and the CUDA Driver API. + * + * Applications that use both hwloc and the CUDA Driver API may want to + * include this file so as to get topology information for CUDA devices. + * + */ + +#ifndef HWLOC_CUDA_H +#define HWLOC_CUDA_H + +#include +#include +#include +#ifdef HWLOC_LINUX_SYS +#include +#endif + +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + +/** \defgroup hwlocality_cuda Interoperability with the CUDA Driver API + * + * This interface offers ways to retrieve topology information about + * CUDA devices when using the CUDA Driver API. + * + * @{ + */ + +/** \brief Return the domain, bus and device IDs of the CUDA device \p cudevice. + * + * Device \p cudevice must match the local machine. + */ +static __hwloc_inline int +hwloc_cuda_get_device_pci_ids(hwloc_topology_t topology __hwloc_attribute_unused, + CUdevice cudevice, int *domain, int *bus, int *dev) +{ + CUresult cres; + +#if CUDA_VERSION >= 4000 + cres = cuDeviceGetAttribute(domain, CU_DEVICE_ATTRIBUTE_PCI_DOMAIN_ID, cudevice); + if (cres != CUDA_SUCCESS) { + errno = ENOSYS; + return -1; + } +#else + *domain = 0; +#endif + cres = cuDeviceGetAttribute(bus, CU_DEVICE_ATTRIBUTE_PCI_BUS_ID, cudevice); + if (cres != CUDA_SUCCESS) { + errno = ENOSYS; + return -1; + } + cres = cuDeviceGetAttribute(dev, CU_DEVICE_ATTRIBUTE_PCI_DEVICE_ID, cudevice); + if (cres != CUDA_SUCCESS) { + errno = ENOSYS; + return -1; + } + + return 0; +} + +/** \brief Get the CPU set of logical processors that are physically + * close to device \p cudevice. + * + * Return the CPU set describing the locality of the CUDA device \p cudevice. + * + * Topology \p topology and device \p cudevice must match the local machine. + * I/O devices detection and the CUDA component are not needed in the topology. + * + * The function only returns the locality of the device. + * If more information about the device is needed, OS objects should + * be used instead, see hwloc_cuda_get_device_osdev() + * and hwloc_cuda_get_device_osdev_by_index(). + * + * This function is currently only implemented in a meaningful way for + * Linux; other systems will simply get a full cpuset. + */ +static __hwloc_inline int +hwloc_cuda_get_device_cpuset(hwloc_topology_t topology __hwloc_attribute_unused, + CUdevice cudevice, hwloc_cpuset_t set) +{ +#ifdef HWLOC_LINUX_SYS + /* If we're on Linux, use the sysfs mechanism to get the local cpus */ +#define HWLOC_CUDA_DEVICE_SYSFS_PATH_MAX 128 + char path[HWLOC_CUDA_DEVICE_SYSFS_PATH_MAX]; + int domainid, busid, deviceid; + + if (hwloc_cuda_get_device_pci_ids(topology, cudevice, &domainid, &busid, &deviceid)) + return -1; + + if (!hwloc_topology_is_thissystem(topology)) { + errno = EINVAL; + return -1; + } + + sprintf(path, "/sys/bus/pci/devices/%04x:%02x:%02x.0/local_cpus", domainid, busid, deviceid); + if (hwloc_linux_read_path_as_cpumask(path, set) < 0 + || hwloc_bitmap_iszero(set)) + hwloc_bitmap_copy(set, hwloc_topology_get_complete_cpuset(topology)); +#else + /* Non-Linux systems simply get a full cpuset */ + hwloc_bitmap_copy(set, hwloc_topology_get_complete_cpuset(topology)); +#endif + return 0; +} + +/** \brief Get the hwloc PCI device object corresponding to the + * CUDA device \p cudevice. + * + * Return the PCI device object describing the CUDA device \p cudevice. + * Return NULL if there is none. + * + * Topology \p topology and device \p cudevice must match the local machine. + * I/O devices detection must be enabled in topology \p topology. + * The CUDA component is not needed in the topology. + */ +static __hwloc_inline hwloc_obj_t +hwloc_cuda_get_device_pcidev(hwloc_topology_t topology, CUdevice cudevice) +{ + int domain, bus, dev; + + if (hwloc_cuda_get_device_pci_ids(topology, cudevice, &domain, &bus, &dev)) + return NULL; + + return hwloc_get_pcidev_by_busid(topology, domain, bus, dev, 0); +} + +/** \brief Get the hwloc OS device object corresponding to CUDA device \p cudevice. + * + * Return the hwloc OS device object that describes the given + * CUDA device \p cudevice. Return NULL if there is none. + * + * Topology \p topology and device \p cudevice must match the local machine. + * I/O devices detection and the CUDA component must be enabled in the topology. + * If not, the locality of the object may still be found using + * hwloc_cuda_get_device_cpuset(). + * + * \note This function cannot work if PCI devices are filtered out. + * + * \note The corresponding hwloc PCI device may be found by looking + * at the result parent pointer (unless PCI devices are filtered out). + */ +static __hwloc_inline hwloc_obj_t +hwloc_cuda_get_device_osdev(hwloc_topology_t topology, CUdevice cudevice) +{ + hwloc_obj_t osdev = NULL; + int domain, bus, dev; + + if (hwloc_cuda_get_device_pci_ids(topology, cudevice, &domain, &bus, &dev)) + return NULL; + + osdev = NULL; + while ((osdev = hwloc_get_next_osdev(topology, osdev)) != NULL) { + hwloc_obj_t pcidev = osdev->parent; + if (strncmp(osdev->name, "cuda", 4)) + continue; + if (pcidev + && pcidev->type == HWLOC_OBJ_PCI_DEVICE + && (int) pcidev->attr->pcidev.domain == domain + && (int) pcidev->attr->pcidev.bus == bus + && (int) pcidev->attr->pcidev.dev == dev + && pcidev->attr->pcidev.func == 0) + return osdev; + /* if PCI are filtered out, we need a info attr to match on */ + } + + return NULL; +} + +/** \brief Get the hwloc OS device object corresponding to the + * CUDA device whose index is \p idx. + * + * Return the OS device object describing the CUDA device whose + * index is \p idx. Return NULL if there is none. + * + * The topology \p topology does not necessarily have to match the current + * machine. For instance the topology may be an XML import of a remote host. + * I/O devices detection and the CUDA component must be enabled in the topology. + * + * \note The corresponding PCI device object can be obtained by looking + * at the OS device parent object (unless PCI devices are filtered out). + * + * \note This function is identical to hwloc_cudart_get_device_osdev_by_index(). + */ +static __hwloc_inline hwloc_obj_t +hwloc_cuda_get_device_osdev_by_index(hwloc_topology_t topology, unsigned idx) +{ + hwloc_obj_t osdev = NULL; + while ((osdev = hwloc_get_next_osdev(topology, osdev)) != NULL) { + if (HWLOC_OBJ_OSDEV_COPROC == osdev->attr->osdev.type + && osdev->name + && !strncmp("cuda", osdev->name, 4) + && atoi(osdev->name + 4) == (int) idx) + return osdev; + } + return NULL; +} + +/** @} */ + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + +#endif /* HWLOC_CUDA_H */ diff --git a/src/3rdparty/hwloc/include/hwloc/cudart.h b/src/3rdparty/hwloc/include/hwloc/cudart.h new file mode 100644 index 00000000..63c7f59c --- /dev/null +++ b/src/3rdparty/hwloc/include/hwloc/cudart.h @@ -0,0 +1,177 @@ +/* + * Copyright © 2010-2017 Inria. All rights reserved. + * Copyright © 2010-2011 Université Bordeaux + * Copyright © 2011 Cisco Systems, Inc. All rights reserved. + * See COPYING in top-level directory. + */ + +/** \file + * \brief Macros to help interaction between hwloc and the CUDA Runtime API. + * + * Applications that use both hwloc and the CUDA Runtime API may want to + * include this file so as to get topology information for CUDA devices. + * + */ + +#ifndef HWLOC_CUDART_H +#define HWLOC_CUDART_H + +#include +#include +#include +#ifdef HWLOC_LINUX_SYS +#include +#endif + +#include /* for CUDA_VERSION */ +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + +/** \defgroup hwlocality_cudart Interoperability with the CUDA Runtime API + * + * This interface offers ways to retrieve topology information about + * CUDA devices when using the CUDA Runtime API. + * + * @{ + */ + +/** \brief Return the domain, bus and device IDs of the CUDA device whose index is \p idx. + * + * Device index \p idx must match the local machine. + */ +static __hwloc_inline int +hwloc_cudart_get_device_pci_ids(hwloc_topology_t topology __hwloc_attribute_unused, + int idx, int *domain, int *bus, int *dev) +{ + cudaError_t cerr; + struct cudaDeviceProp prop; + + cerr = cudaGetDeviceProperties(&prop, idx); + if (cerr) { + errno = ENOSYS; + return -1; + } + +#if CUDA_VERSION >= 4000 + *domain = prop.pciDomainID; +#else + *domain = 0; +#endif + + *bus = prop.pciBusID; + *dev = prop.pciDeviceID; + + return 0; +} + +/** \brief Get the CPU set of logical processors that are physically + * close to device \p idx. + * + * Return the CPU set describing the locality of the CUDA device + * whose index is \p idx. + * + * Topology \p topology and device \p idx must match the local machine. + * I/O devices detection and the CUDA component are not needed in the topology. + * + * The function only returns the locality of the device. + * If more information about the device is needed, OS objects should + * be used instead, see hwloc_cudart_get_device_osdev_by_index(). + * + * This function is currently only implemented in a meaningful way for + * Linux; other systems will simply get a full cpuset. + */ +static __hwloc_inline int +hwloc_cudart_get_device_cpuset(hwloc_topology_t topology __hwloc_attribute_unused, + int idx, hwloc_cpuset_t set) +{ +#ifdef HWLOC_LINUX_SYS + /* If we're on Linux, use the sysfs mechanism to get the local cpus */ +#define HWLOC_CUDART_DEVICE_SYSFS_PATH_MAX 128 + char path[HWLOC_CUDART_DEVICE_SYSFS_PATH_MAX]; + int domain, bus, dev; + + if (hwloc_cudart_get_device_pci_ids(topology, idx, &domain, &bus, &dev)) + return -1; + + if (!hwloc_topology_is_thissystem(topology)) { + errno = EINVAL; + return -1; + } + + sprintf(path, "/sys/bus/pci/devices/%04x:%02x:%02x.0/local_cpus", (unsigned) domain, (unsigned) bus, (unsigned) dev); + if (hwloc_linux_read_path_as_cpumask(path, set) < 0 + || hwloc_bitmap_iszero(set)) + hwloc_bitmap_copy(set, hwloc_topology_get_complete_cpuset(topology)); +#else + /* Non-Linux systems simply get a full cpuset */ + hwloc_bitmap_copy(set, hwloc_topology_get_complete_cpuset(topology)); +#endif + return 0; +} + +/** \brief Get the hwloc PCI device object corresponding to the + * CUDA device whose index is \p idx. + * + * Return the PCI device object describing the CUDA device whose + * index is \p idx. Return NULL if there is none. + * + * Topology \p topology and device \p idx must match the local machine. + * I/O devices detection must be enabled in topology \p topology. + * The CUDA component is not needed in the topology. + */ +static __hwloc_inline hwloc_obj_t +hwloc_cudart_get_device_pcidev(hwloc_topology_t topology, int idx) +{ + int domain, bus, dev; + + if (hwloc_cudart_get_device_pci_ids(topology, idx, &domain, &bus, &dev)) + return NULL; + + return hwloc_get_pcidev_by_busid(topology, domain, bus, dev, 0); +} + +/** \brief Get the hwloc OS device object corresponding to the + * CUDA device whose index is \p idx. + * + * Return the OS device object describing the CUDA device whose + * index is \p idx. Return NULL if there is none. + * + * The topology \p topology does not necessarily have to match the current + * machine. For instance the topology may be an XML import of a remote host. + * I/O devices detection and the CUDA component must be enabled in the topology. + * If not, the locality of the object may still be found using + * hwloc_cudart_get_device_cpuset(). + * + * \note The corresponding PCI device object can be obtained by looking + * at the OS device parent object (unless PCI devices are filtered out). + * + * \note This function is identical to hwloc_cuda_get_device_osdev_by_index(). + */ +static __hwloc_inline hwloc_obj_t +hwloc_cudart_get_device_osdev_by_index(hwloc_topology_t topology, unsigned idx) +{ + hwloc_obj_t osdev = NULL; + while ((osdev = hwloc_get_next_osdev(topology, osdev)) != NULL) { + if (HWLOC_OBJ_OSDEV_COPROC == osdev->attr->osdev.type + && osdev->name + && !strncmp("cuda", osdev->name, 4) + && atoi(osdev->name + 4) == (int) idx) + return osdev; + } + return NULL; +} + +/** @} */ + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + +#endif /* HWLOC_CUDART_H */ diff --git a/src/3rdparty/hwloc/include/hwloc/deprecated.h b/src/3rdparty/hwloc/include/hwloc/deprecated.h new file mode 100644 index 00000000..8f3b1459 --- /dev/null +++ b/src/3rdparty/hwloc/include/hwloc/deprecated.h @@ -0,0 +1,206 @@ +/* + * Copyright © 2009 CNRS + * Copyright © 2009-2017 Inria. All rights reserved. + * Copyright © 2009-2012 Université Bordeaux + * Copyright © 2009-2010 Cisco Systems, Inc. All rights reserved. + * See COPYING in top-level directory. + */ + +/** + * This file contains the inline code of functions declared in hwloc.h + */ + +#ifndef HWLOC_DEPRECATED_H +#define HWLOC_DEPRECATED_H + +#ifndef HWLOC_H +#error Please include the main hwloc.h instead +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* backward compat with v1.11 before System removal */ +#define HWLOC_OBJ_SYSTEM HWLOC_OBJ_MACHINE +/* backward compat with v1.10 before Socket->Package renaming */ +#define HWLOC_OBJ_SOCKET HWLOC_OBJ_PACKAGE +/* backward compat with v1.10 before Node->NUMANode clarification */ +#define HWLOC_OBJ_NODE HWLOC_OBJ_NUMANODE + +/** \brief Insert a misc object by parent. + * + * Identical to hwloc_topology_insert_misc_object(). + */ +static __hwloc_inline hwloc_obj_t +hwloc_topology_insert_misc_object_by_parent(hwloc_topology_t topology, hwloc_obj_t parent, const char *name) __hwloc_attribute_deprecated; +static __hwloc_inline hwloc_obj_t +hwloc_topology_insert_misc_object_by_parent(hwloc_topology_t topology, hwloc_obj_t parent, const char *name) +{ + return hwloc_topology_insert_misc_object(topology, parent, name); +} + +/** \brief Stringify the cpuset containing a set of objects. + * + * If \p size is 0, \p string may safely be \c NULL. + * + * \return the number of character that were actually written if not truncating, + * or that would have been written (not including the ending \\0). + */ +static __hwloc_inline int +hwloc_obj_cpuset_snprintf(char *str, size_t size, size_t nobj, struct hwloc_obj * const *objs) __hwloc_attribute_deprecated; +static __hwloc_inline int +hwloc_obj_cpuset_snprintf(char *str, size_t size, size_t nobj, struct hwloc_obj * const *objs) +{ + hwloc_bitmap_t set = hwloc_bitmap_alloc(); + int res; + unsigned i; + + hwloc_bitmap_zero(set); + for(i=0; icpuset) + hwloc_bitmap_or(set, set, objs[i]->cpuset); + + res = hwloc_bitmap_snprintf(str, size, set); + hwloc_bitmap_free(set); + return res; +} + +/** \brief Convert a type string into a type and some attributes. + * + * Deprecated by hwloc_type_sscanf() + */ +static __hwloc_inline int +hwloc_obj_type_sscanf(const char *string, hwloc_obj_type_t *typep, int *depthattrp, void *typeattrp, size_t typeattrsize) __hwloc_attribute_deprecated; +static __hwloc_inline int +hwloc_obj_type_sscanf(const char *string, hwloc_obj_type_t *typep, int *depthattrp, void *typeattrp, size_t typeattrsize) +{ + union hwloc_obj_attr_u attr; + int err = hwloc_type_sscanf(string, typep, &attr, sizeof(attr)); + if (err < 0) + return err; + if (hwloc_obj_type_is_cache(*typep)) { + if (depthattrp) + *depthattrp = (int) attr.cache.depth; + if (typeattrp && typeattrsize >= sizeof(hwloc_obj_cache_type_t)) + memcpy(typeattrp, &attr.cache.type, sizeof(hwloc_obj_cache_type_t)); + } else if (*typep == HWLOC_OBJ_GROUP) { + if (depthattrp) + *depthattrp = (int) attr.group.depth; + } + return 0; +} + +/** \brief Set the default memory binding policy of the current + * process or thread to prefer the NUMA node(s) specified by physical \p nodeset + */ +static __hwloc_inline int +hwloc_set_membind_nodeset(hwloc_topology_t topology, hwloc_const_nodeset_t nodeset, hwloc_membind_policy_t policy, int flags) __hwloc_attribute_deprecated; +static __hwloc_inline int +hwloc_set_membind_nodeset(hwloc_topology_t topology, hwloc_const_nodeset_t nodeset, hwloc_membind_policy_t policy, int flags) +{ + return hwloc_set_membind(topology, nodeset, policy, flags | HWLOC_MEMBIND_BYNODESET); +} + +/** \brief Query the default memory binding policy and physical locality of the + * current process or thread. + */ +static __hwloc_inline int +hwloc_get_membind_nodeset(hwloc_topology_t topology, hwloc_nodeset_t nodeset, hwloc_membind_policy_t * policy, int flags) __hwloc_attribute_deprecated; +static __hwloc_inline int +hwloc_get_membind_nodeset(hwloc_topology_t topology, hwloc_nodeset_t nodeset, hwloc_membind_policy_t * policy, int flags) +{ + return hwloc_get_membind(topology, nodeset, policy, flags | HWLOC_MEMBIND_BYNODESET); +} + +/** \brief Set the default memory binding policy of the specified + * process to prefer the NUMA node(s) specified by physical \p nodeset + */ +static __hwloc_inline int +hwloc_set_proc_membind_nodeset(hwloc_topology_t topology, hwloc_pid_t pid, hwloc_const_nodeset_t nodeset, hwloc_membind_policy_t policy, int flags) __hwloc_attribute_deprecated; +static __hwloc_inline int +hwloc_set_proc_membind_nodeset(hwloc_topology_t topology, hwloc_pid_t pid, hwloc_const_nodeset_t nodeset, hwloc_membind_policy_t policy, int flags) +{ + return hwloc_set_proc_membind(topology, pid, nodeset, policy, flags | HWLOC_MEMBIND_BYNODESET); +} + +/** \brief Query the default memory binding policy and physical locality of the + * specified process. + */ +static __hwloc_inline int +hwloc_get_proc_membind_nodeset(hwloc_topology_t topology, hwloc_pid_t pid, hwloc_nodeset_t nodeset, hwloc_membind_policy_t * policy, int flags) __hwloc_attribute_deprecated; +static __hwloc_inline int +hwloc_get_proc_membind_nodeset(hwloc_topology_t topology, hwloc_pid_t pid, hwloc_nodeset_t nodeset, hwloc_membind_policy_t * policy, int flags) +{ + return hwloc_get_proc_membind(topology, pid, nodeset, policy, flags | HWLOC_MEMBIND_BYNODESET); +} + +/** \brief Bind the already-allocated memory identified by (addr, len) + * to the NUMA node(s) in physical \p nodeset. + */ +static __hwloc_inline int +hwloc_set_area_membind_nodeset(hwloc_topology_t topology, const void *addr, size_t len, hwloc_const_nodeset_t nodeset, hwloc_membind_policy_t policy, int flags) __hwloc_attribute_deprecated; +static __hwloc_inline int +hwloc_set_area_membind_nodeset(hwloc_topology_t topology, const void *addr, size_t len, hwloc_const_nodeset_t nodeset, hwloc_membind_policy_t policy, int flags) +{ + return hwloc_set_area_membind(topology, addr, len, nodeset, policy, flags | HWLOC_MEMBIND_BYNODESET); +} + +/** \brief Query the physical NUMA node(s) and binding policy of the memory + * identified by (\p addr, \p len ). + */ +static __hwloc_inline int +hwloc_get_area_membind_nodeset(hwloc_topology_t topology, const void *addr, size_t len, hwloc_nodeset_t nodeset, hwloc_membind_policy_t * policy, int flags) __hwloc_attribute_deprecated; +static __hwloc_inline int +hwloc_get_area_membind_nodeset(hwloc_topology_t topology, const void *addr, size_t len, hwloc_nodeset_t nodeset, hwloc_membind_policy_t * policy, int flags) +{ + return hwloc_get_area_membind(topology, addr, len, nodeset, policy, flags | HWLOC_MEMBIND_BYNODESET); +} + +/** \brief Allocate some memory on the given physical nodeset \p nodeset + */ +static __hwloc_inline void * +hwloc_alloc_membind_nodeset(hwloc_topology_t topology, size_t len, hwloc_const_nodeset_t nodeset, hwloc_membind_policy_t policy, int flags) __hwloc_attribute_malloc __hwloc_attribute_deprecated; +static __hwloc_inline void * +hwloc_alloc_membind_nodeset(hwloc_topology_t topology, size_t len, hwloc_const_nodeset_t nodeset, hwloc_membind_policy_t policy, int flags) +{ + return hwloc_alloc_membind(topology, len, nodeset, policy, flags | HWLOC_MEMBIND_BYNODESET); +} + +/** \brief Allocate some memory on the given nodeset \p nodeset. + */ +static __hwloc_inline void * +hwloc_alloc_membind_policy_nodeset(hwloc_topology_t topology, size_t len, hwloc_const_nodeset_t nodeset, hwloc_membind_policy_t policy, int flags) __hwloc_attribute_malloc __hwloc_attribute_deprecated; +static __hwloc_inline void * +hwloc_alloc_membind_policy_nodeset(hwloc_topology_t topology, size_t len, hwloc_const_nodeset_t nodeset, hwloc_membind_policy_t policy, int flags) +{ + return hwloc_alloc_membind_policy(topology, len, nodeset, policy, flags | HWLOC_MEMBIND_BYNODESET); +} + +/** \brief Convert a CPU set into a NUMA node set and handle non-NUMA cases + */ +static __hwloc_inline void +hwloc_cpuset_to_nodeset_strict(hwloc_topology_t topology, hwloc_const_cpuset_t _cpuset, hwloc_nodeset_t nodeset) __hwloc_attribute_deprecated; +static __hwloc_inline void +hwloc_cpuset_to_nodeset_strict(hwloc_topology_t topology, hwloc_const_cpuset_t _cpuset, hwloc_nodeset_t nodeset) +{ + hwloc_cpuset_to_nodeset(topology, _cpuset, nodeset); +} + +/** \brief Convert a NUMA node set into a CPU set and handle non-NUMA cases + */ +static __hwloc_inline void +hwloc_cpuset_from_nodeset_strict(hwloc_topology_t topology, hwloc_cpuset_t _cpuset, hwloc_const_nodeset_t nodeset) __hwloc_attribute_deprecated; +static __hwloc_inline void +hwloc_cpuset_from_nodeset_strict(hwloc_topology_t topology, hwloc_cpuset_t _cpuset, hwloc_const_nodeset_t nodeset) +{ + hwloc_cpuset_from_nodeset(topology, _cpuset, nodeset); +} + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + +#endif /* HWLOC_DEPRECATED_H */ diff --git a/src/3rdparty/hwloc/include/hwloc/diff.h b/src/3rdparty/hwloc/include/hwloc/diff.h new file mode 100644 index 00000000..79f2df3d --- /dev/null +++ b/src/3rdparty/hwloc/include/hwloc/diff.h @@ -0,0 +1,289 @@ +/* + * Copyright © 2013-2018 Inria. All rights reserved. + * See COPYING in top-level directory. + */ + +/** \file + * \brief Topology differences. + */ + +#ifndef HWLOC_DIFF_H +#define HWLOC_DIFF_H + +#ifndef HWLOC_H +#error Please include the main hwloc.h instead +#endif + + +#ifdef __cplusplus +extern "C" { +#elif 0 +} +#endif + + +/** \defgroup hwlocality_diff Topology differences + * + * Applications that manipulate many similar topologies, for instance + * one for each node of a homogeneous cluster, may want to compress + * topologies to reduce the memory footprint. + * + * This file offers a way to manipulate the difference between topologies + * and export/import it to/from XML. + * Compression may therefore be achieved by storing one topology + * entirely while the others are only described by their differences + * with the former. + * The actual topology can be reconstructed when actually needed by + * applying the precomputed difference to the reference topology. + * + * This interface targets very similar nodes. + * Only very simple differences between topologies are actually + * supported, for instance a change in the memory size, the name + * of the object, or some info attribute. + * More complex differences such as adding or removing objects cannot + * be represented in the difference structures and therefore return + * errors. + * Differences between object sets or topology-wide allowed sets, + * cannot be represented either. + * + * It means that there is no need to apply the difference when + * looking at the tree organization (how many levels, how many + * objects per level, what kind of objects, CPU and node sets, etc) + * and when binding to objects. + * However the difference must be applied when looking at object + * attributes such as the name, the memory size or info attributes. + * + * @{ + */ + + +/** \brief Type of one object attribute difference. + */ +typedef enum hwloc_topology_diff_obj_attr_type_e { + /** \brief The object local memory is modified. + * The union is a hwloc_topology_diff_obj_attr_u::hwloc_topology_diff_obj_attr_uint64_s + * (and the index field is ignored). + */ + HWLOC_TOPOLOGY_DIFF_OBJ_ATTR_SIZE, + + /** \brief The object name is modified. + * The union is a hwloc_topology_diff_obj_attr_u::hwloc_topology_diff_obj_attr_string_s + * (and the name field is ignored). + */ + + HWLOC_TOPOLOGY_DIFF_OBJ_ATTR_NAME, + /** \brief the value of an info attribute is modified. + * The union is a hwloc_topology_diff_obj_attr_u::hwloc_topology_diff_obj_attr_string_s. + */ + HWLOC_TOPOLOGY_DIFF_OBJ_ATTR_INFO +} hwloc_topology_diff_obj_attr_type_t; + +/** \brief One object attribute difference. + */ +union hwloc_topology_diff_obj_attr_u { + struct hwloc_topology_diff_obj_attr_generic_s { + /* each part of the union must start with these */ + hwloc_topology_diff_obj_attr_type_t type; + } generic; + + /** \brief Integer attribute modification with an optional index. */ + struct hwloc_topology_diff_obj_attr_uint64_s { + /* used for storing integer attributes */ + hwloc_topology_diff_obj_attr_type_t type; + hwloc_uint64_t index; /* not used for SIZE */ + hwloc_uint64_t oldvalue; + hwloc_uint64_t newvalue; + } uint64; + + /** \brief String attribute modification with an optional name */ + struct hwloc_topology_diff_obj_attr_string_s { + /* used for storing name and info pairs */ + hwloc_topology_diff_obj_attr_type_t type; + char *name; /* not used for NAME */ + char *oldvalue; + char *newvalue; + } string; +}; + + +/** \brief Type of one element of a difference list. + */ +typedef enum hwloc_topology_diff_type_e { + /** \brief An object attribute was changed. + * The union is a hwloc_topology_diff_obj_attr_u::hwloc_topology_diff_obj_attr_s. + */ + HWLOC_TOPOLOGY_DIFF_OBJ_ATTR, + + /** \brief The difference is too complex, + * it cannot be represented. The difference below + * this object has not been checked. + * hwloc_topology_diff_build() will return 1. + * + * The union is a hwloc_topology_diff_obj_attr_u::hwloc_topology_diff_too_complex_s. + */ + HWLOC_TOPOLOGY_DIFF_TOO_COMPLEX +} hwloc_topology_diff_type_t; + +/** \brief One element of a difference list between two topologies. + */ +typedef union hwloc_topology_diff_u { + struct hwloc_topology_diff_generic_s { + /* each part of the union must start with these */ + hwloc_topology_diff_type_t type; + union hwloc_topology_diff_u * next; /* pointer to the next element of the list, or NULL */ + } generic; + + /* A difference in an object attribute. */ + struct hwloc_topology_diff_obj_attr_s { + hwloc_topology_diff_type_t type; /* must be ::HWLOC_TOPOLOGY_DIFF_OBJ_ATTR */ + union hwloc_topology_diff_u * next; + /* List of attribute differences for a single object */ + int obj_depth; + unsigned obj_index; + union hwloc_topology_diff_obj_attr_u diff; + } obj_attr; + + /* A difference that is too complex. */ + struct hwloc_topology_diff_too_complex_s { + hwloc_topology_diff_type_t type; /* must be ::HWLOC_TOPOLOGY_DIFF_TOO_COMPLEX */ + union hwloc_topology_diff_u * next; + /* Where we had to stop computing the diff in the first topology */ + int obj_depth; + unsigned obj_index; + } too_complex; +} * hwloc_topology_diff_t; + + +/** \brief Compute the difference between 2 topologies. + * + * The difference is stored as a list of ::hwloc_topology_diff_t entries + * starting at \p diff. + * It is computed by doing a depth-first traversal of both topology trees + * simultaneously. + * + * If the difference between 2 objects is too complex to be represented + * (for instance if some objects have different types, or different numbers + * of children), a special diff entry of type ::HWLOC_TOPOLOGY_DIFF_TOO_COMPLEX + * is queued. + * The computation of the diff does not continue below these objects. + * So each such diff entry means that the difference between two subtrees + * could not be computed. + * + * \return 0 if the difference can be represented properly. + * + * \return 0 with \p diff pointing to NULL if there is no difference + * between the topologies. + * + * \return 1 if the difference is too complex (see above). Some entries in + * the list will be of type ::HWLOC_TOPOLOGY_DIFF_TOO_COMPLEX. + * + * \return -1 on any other error. + * + * \note \p flags is currently not used. It should be 0. + * + * \note The output diff has to be freed with hwloc_topology_diff_destroy(). + * + * \note The output diff can only be exported to XML or passed to + * hwloc_topology_diff_apply() if 0 was returned, i.e. if no entry of type + * ::HWLOC_TOPOLOGY_DIFF_TOO_COMPLEX is listed. + * + * \note The output diff may be modified by removing some entries from + * the list. The removed entries should be freed by passing them to + * to hwloc_topology_diff_destroy() (possible as another list). +*/ +HWLOC_DECLSPEC int hwloc_topology_diff_build(hwloc_topology_t topology, hwloc_topology_t newtopology, unsigned long flags, hwloc_topology_diff_t *diff); + +/** \brief Flags to be given to hwloc_topology_diff_apply(). + */ +enum hwloc_topology_diff_apply_flags_e { + /** \brief Apply topology diff in reverse direction. + * \hideinitializer + */ + HWLOC_TOPOLOGY_DIFF_APPLY_REVERSE = (1UL<<0) +}; + +/** \brief Apply a topology diff to an existing topology. + * + * \p flags is an OR'ed set of ::hwloc_topology_diff_apply_flags_e. + * + * The new topology is modified in place. hwloc_topology_dup() + * may be used to duplicate it before patching. + * + * If the difference cannot be applied entirely, all previous applied + * elements are unapplied before returning. + * + * \return 0 on success. + * + * \return -N if applying the difference failed while trying + * to apply the N-th part of the difference. For instance -1 + * is returned if the very first difference element could not + * be applied. + */ +HWLOC_DECLSPEC int hwloc_topology_diff_apply(hwloc_topology_t topology, hwloc_topology_diff_t diff, unsigned long flags); + +/** \brief Destroy a list of topology differences. + */ +HWLOC_DECLSPEC int hwloc_topology_diff_destroy(hwloc_topology_diff_t diff); + +/** \brief Load a list of topology differences from a XML file. + * + * If not \c NULL, \p refname will be filled with the identifier + * string of the reference topology for the difference file, + * if any was specified in the XML file. + * This identifier is usually the name of the other XML file + * that contains the reference topology. + * + * \note the pointer returned in refname should later be freed + * by the caller. + */ +HWLOC_DECLSPEC int hwloc_topology_diff_load_xml(const char *xmlpath, hwloc_topology_diff_t *diff, char **refname); + +/** \brief Export a list of topology differences to a XML file. + * + * If not \c NULL, \p refname defines an identifier string + * for the reference topology which was used as a base when + * computing this difference. + * This identifier is usually the name of the other XML file + * that contains the reference topology. + * This attribute is given back when reading the diff from XML. + */ +HWLOC_DECLSPEC int hwloc_topology_diff_export_xml(hwloc_topology_diff_t diff, const char *refname, const char *xmlpath); + +/** \brief Load a list of topology differences from a XML buffer. + * + * If not \c NULL, \p refname will be filled with the identifier + * string of the reference topology for the difference file, + * if any was specified in the XML file. + * This identifier is usually the name of the other XML file + * that contains the reference topology. + * + * \note the pointer returned in refname should later be freed + * by the caller. + */ +HWLOC_DECLSPEC int hwloc_topology_diff_load_xmlbuffer(const char *xmlbuffer, int buflen, hwloc_topology_diff_t *diff, char **refname); + +/** \brief Export a list of topology differences to a XML buffer. + * + * If not \c NULL, \p refname defines an identifier string + * for the reference topology which was used as a base when + * computing this difference. + * This identifier is usually the name of the other XML file + * that contains the reference topology. + * This attribute is given back when reading the diff from XML. + * + * The returned buffer ends with a \0 that is included in the returned + * length. + * + * \note The XML buffer should later be freed with hwloc_free_xmlbuffer(). + */ +HWLOC_DECLSPEC int hwloc_topology_diff_export_xmlbuffer(hwloc_topology_diff_t diff, const char *refname, char **xmlbuffer, int *buflen); + +/** @} */ + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + +#endif /* HWLOC_DIFF_H */ diff --git a/src/3rdparty/hwloc/include/hwloc/distances.h b/src/3rdparty/hwloc/include/hwloc/distances.h new file mode 100644 index 00000000..d523f29f --- /dev/null +++ b/src/3rdparty/hwloc/include/hwloc/distances.h @@ -0,0 +1,271 @@ +/* + * Copyright © 2010-2019 Inria. All rights reserved. + * See COPYING in top-level directory. + */ + +/** \file + * \brief Object distances. + */ + +#ifndef HWLOC_DISTANCES_H +#define HWLOC_DISTANCES_H + +#ifndef HWLOC_H +#error Please include the main hwloc.h instead +#endif + + +#ifdef __cplusplus +extern "C" { +#elif 0 +} +#endif + + +/** \defgroup hwlocality_distances_get Retrieve distances between objects + * @{ + */ + +/** \brief Matrix of distances between a set of objects. + * + * This matrix often contains latencies between NUMA nodes + * (as reported in the System Locality Distance Information Table (SLIT) + * in the ACPI specification), which may or may not be physically accurate. + * It corresponds to the latency for accessing the memory of one node + * from a core in another node. + * The corresponding kind is ::HWLOC_DISTANCES_KIND_FROM_OS | ::HWLOC_DISTANCES_KIND_FROM_USER. + * + * The matrix may also contain bandwidths between random sets of objects, + * possibly provided by the user, as specified in the \p kind attribute. + */ +struct hwloc_distances_s { + unsigned nbobjs; /**< \brief Number of objects described by the distance matrix. */ + hwloc_obj_t *objs; /**< \brief Array of objects described by the distance matrix. + * These objects are not in any particular order, + * see hwloc_distances_obj_index() and hwloc_distances_obj_pair_values() + * for easy ways to find objects in this array and their corresponding values. + */ + unsigned long kind; /**< \brief OR'ed set of ::hwloc_distances_kind_e. */ + hwloc_uint64_t *values; /**< \brief Matrix of distances between objects, stored as a one-dimension array. + * + * Distance from i-th to j-th object is stored in slot i*nbobjs+j. + * The meaning of the value depends on the \p kind attribute. + */ +}; + +/** \brief Kinds of distance matrices. + * + * The \p kind attribute of struct hwloc_distances_s is a OR'ed set + * of kinds. + * + * A kind of format HWLOC_DISTANCES_KIND_FROM_* specifies where the + * distance information comes from, if known. + * + * A kind of format HWLOC_DISTANCES_KIND_MEANS_* specifies whether + * values are latencies or bandwidths, if applicable. + */ +enum hwloc_distances_kind_e { + /** \brief These distances were obtained from the operating system or hardware. + * \hideinitializer + */ + HWLOC_DISTANCES_KIND_FROM_OS = (1UL<<0), + /** \brief These distances were provided by the user. + * \hideinitializer + */ + HWLOC_DISTANCES_KIND_FROM_USER = (1UL<<1), + + /** \brief Distance values are similar to latencies between objects. + * Values are smaller for closer objects, hence minimal on the diagonal + * of the matrix (distance between an object and itself). + * It could also be the number of network hops between objects, etc. + * \hideinitializer + */ + HWLOC_DISTANCES_KIND_MEANS_LATENCY = (1UL<<2), + /** \brief Distance values are similar to bandwidths between objects. + * Values are higher for closer objects, hence maximal on the diagonal + * of the matrix (distance between an object and itself). + * Such values are currently ignored for distance-based grouping. + * \hideinitializer + */ + HWLOC_DISTANCES_KIND_MEANS_BANDWIDTH = (1UL<<3) +}; + +/** \brief Retrieve distance matrices. + * + * Retrieve distance matrices from the topology into the \p distances array. + * + * \p flags is currently unused, should be \c 0. + * + * \p kind serves as a filter. If \c 0, all distance matrices are returned. + * If it contains some HWLOC_DISTANCES_KIND_FROM_*, only distance matrices + * whose kind matches one of these are returned. + * If it contains some HWLOC_DISTANCES_KIND_MEANS_*, only distance matrices + * whose kind matches one of these are returned. + * + * On input, \p nr points to the number of distance matrices that may be stored + * in \p distances. + * On output, \p nr points to the number of distance matrices that were actually + * found, even if some of them couldn't be stored in \p distances. + * Distance matrices that couldn't be stored are ignored, but the function still + * returns success (\c 0). The caller may find out by comparing the value pointed + * by \p nr before and after the function call. + * + * Each distance matrix returned in the \p distances array should be released + * by the caller using hwloc_distances_release(). + */ +HWLOC_DECLSPEC int +hwloc_distances_get(hwloc_topology_t topology, + unsigned *nr, struct hwloc_distances_s **distances, + unsigned long kind, unsigned long flags); + +/** \brief Retrieve distance matrices for object at a specific depth in the topology. + * + * Identical to hwloc_distances_get() with the additional \p depth filter. + */ +HWLOC_DECLSPEC int +hwloc_distances_get_by_depth(hwloc_topology_t topology, int depth, + unsigned *nr, struct hwloc_distances_s **distances, + unsigned long kind, unsigned long flags); + +/** \brief Retrieve distance matrices for object of a specific type. + * + * Identical to hwloc_distances_get() with the additional \p type filter. + */ +static __hwloc_inline int +hwloc_distances_get_by_type(hwloc_topology_t topology, hwloc_obj_type_t type, + unsigned *nr, struct hwloc_distances_s **distances, + unsigned long kind, unsigned long flags) +{ + int depth = hwloc_get_type_depth(topology, type); + if (depth == HWLOC_TYPE_DEPTH_UNKNOWN || depth == HWLOC_TYPE_DEPTH_MULTIPLE) { + *nr = 0; + return 0; + } + return hwloc_distances_get_by_depth(topology, depth, nr, distances, kind, flags); +} + +/** \brief Release a distance matrix structure previously returned by hwloc_distances_get(). */ +HWLOC_DECLSPEC void +hwloc_distances_release(hwloc_topology_t topology, struct hwloc_distances_s *distances); + +/** @} */ + + + +/** \defgroup hwlocality_distances_consult Helpers for consulting distance matrices + * @{ + */ + +/** \brief Find the index of an object in a distances structure. + * + * \return -1 if object \p obj is not involved in structure \p distances. + */ +static __hwloc_inline int +hwloc_distances_obj_index(struct hwloc_distances_s *distances, hwloc_obj_t obj) +{ + unsigned i; + for(i=0; inbobjs; i++) + if (distances->objs[i] == obj) + return (int)i; + return -1; +} + +/** \brief Find the values between two objects in a distance matrices. + * + * The distance from \p obj1 to \p obj2 is stored in the value pointed by + * \p value1to2 and reciprocally. + * + * \return -1 if object \p obj1 or \p obj2 is not involved in structure \p distances. + */ +static __hwloc_inline int +hwloc_distances_obj_pair_values(struct hwloc_distances_s *distances, + hwloc_obj_t obj1, hwloc_obj_t obj2, + hwloc_uint64_t *value1to2, hwloc_uint64_t *value2to1) +{ + int i1 = hwloc_distances_obj_index(distances, obj1); + int i2 = hwloc_distances_obj_index(distances, obj2); + if (i1 < 0 || i2 < 0) + return -1; + *value1to2 = distances->values[i1 * distances->nbobjs + i2]; + *value2to1 = distances->values[i2 * distances->nbobjs + i1]; + return 0; +} + +/** @} */ + + + +/** \defgroup hwlocality_distances_add Add or remove distances between objects + * @{ + */ + +/** \brief Flags for adding a new distances to a topology. */ +enum hwloc_distances_add_flag_e { + /** \brief Try to group objects based on the newly provided distance information. + * \hideinitializer + */ + HWLOC_DISTANCES_ADD_FLAG_GROUP = (1UL<<0), + /** \brief If grouping, consider the distance values as inaccurate and relax the + * comparisons during the grouping algorithms. The actual accuracy may be modified + * through the HWLOC_GROUPING_ACCURACY environment variable (see \ref envvar). + * \hideinitializer + */ + HWLOC_DISTANCES_ADD_FLAG_GROUP_INACCURATE = (1UL<<1) +}; + +/** \brief Provide a new distance matrix. + * + * Provide the matrix of distances between a set of objects given by \p nbobjs + * and the \p objs array. \p nbobjs must be at least 2. + * The distances are stored as a one-dimension array in \p values. + * The distance from object i to object j is in slot i*nbobjs+j. + * + * \p kind specifies the kind of distance as a OR'ed set of ::hwloc_distances_kind_e. + * + * \p flags configures the behavior of the function using an optional OR'ed set of + * ::hwloc_distances_add_flag_e. + * + * Objects must be of the same type. They cannot be of type Group. + */ +HWLOC_DECLSPEC int hwloc_distances_add(hwloc_topology_t topology, + unsigned nbobjs, hwloc_obj_t *objs, hwloc_uint64_t *values, + unsigned long kind, unsigned long flags); + +/** \brief Remove all distance matrices from a topology. + * + * Remove all distance matrices, either provided by the user or + * gathered through the OS. + * + * If these distances were used to group objects, these additional + *Group objects are not removed from the topology. + */ +HWLOC_DECLSPEC int hwloc_distances_remove(hwloc_topology_t topology); + +/** \brief Remove distance matrices for objects at a specific depth in the topology. + * + * Identical to hwloc_distances_remove() but only applies to one level of the topology. + */ +HWLOC_DECLSPEC int hwloc_distances_remove_by_depth(hwloc_topology_t topology, int depth); + +/** \brief Remove distance matrices for objects of a specific type in the topology. + * + * Identical to hwloc_distances_remove() but only applies to one level of the topology. + */ +static __hwloc_inline int +hwloc_distances_remove_by_type(hwloc_topology_t topology, hwloc_obj_type_t type) +{ + int depth = hwloc_get_type_depth(topology, type); + if (depth == HWLOC_TYPE_DEPTH_UNKNOWN || depth == HWLOC_TYPE_DEPTH_MULTIPLE) + return 0; + return hwloc_distances_remove_by_depth(topology, depth); +} + +/** @} */ + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + +#endif /* HWLOC_DISTANCES_H */ diff --git a/src/3rdparty/hwloc/include/hwloc/export.h b/src/3rdparty/hwloc/include/hwloc/export.h new file mode 100644 index 00000000..b178b77e --- /dev/null +++ b/src/3rdparty/hwloc/include/hwloc/export.h @@ -0,0 +1,278 @@ +/* + * Copyright © 2009-2018 Inria. All rights reserved. + * Copyright © 2009-2012 Université Bordeaux + * Copyright © 2009-2011 Cisco Systems, Inc. All rights reserved. + * See COPYING in top-level directory. + */ + +/** \file + * \brief Exporting Topologies to XML or to Synthetic strings. + */ + +#ifndef HWLOC_EXPORT_H +#define HWLOC_EXPORT_H + +#ifndef HWLOC_H +#error Please include the main hwloc.h instead +#endif + + +#ifdef __cplusplus +extern "C" { +#elif 0 +} +#endif + + +/** \defgroup hwlocality_xmlexport Exporting Topologies to XML + * @{ + */ + +/** \brief Flags for exporting XML topologies. + * + * Flags to be given as a OR'ed set to hwloc_topology_export_xml(). + */ +enum hwloc_topology_export_xml_flags_e { + /** \brief Export XML that is loadable by hwloc v1.x. + * However, the export may miss some details about the topology. + * \hideinitializer + */ + HWLOC_TOPOLOGY_EXPORT_XML_FLAG_V1 = (1UL<<0) +}; + +/** \brief Export the topology into an XML file. + * + * This file may be loaded later through hwloc_topology_set_xml(). + * + * By default, the latest export format is used, which means older hwloc + * releases (e.g. v1.x) will not be able to import it. + * Exporting to v1.x specific XML format is possible using flag + * ::HWLOC_TOPOLOGY_EXPORT_XML_FLAG_V1 but it may miss some details + * about the topology. + * If there is any chance that the exported file may ever be imported + * back by a process using hwloc 1.x, one should consider detecting + * it at runtime and using the corresponding export format. + * + * \p flags is a OR'ed set of ::hwloc_topology_export_xml_flags_e. + * + * \return -1 if a failure occured. + * + * \note See also hwloc_topology_set_userdata_export_callback() + * for exporting application-specific object userdata. + * + * \note The topology-specific userdata pointer is ignored when exporting to XML. + * + * \note Only printable characters may be exported to XML string attributes. + * Any other character, especially any non-ASCII character, will be silently + * dropped. + * + * \note If \p name is "-", the XML output is sent to the standard output. + */ +HWLOC_DECLSPEC int hwloc_topology_export_xml(hwloc_topology_t topology, const char *xmlpath, unsigned long flags); + +/** \brief Export the topology into a newly-allocated XML memory buffer. + * + * \p xmlbuffer is allocated by the callee and should be freed with + * hwloc_free_xmlbuffer() later in the caller. + * + * This memory buffer may be loaded later through hwloc_topology_set_xmlbuffer(). + * + * By default, the latest export format is used, which means older hwloc + * releases (e.g. v1.x) will not be able to import it. + * Exporting to v1.x specific XML format is possible using flag + * ::HWLOC_TOPOLOGY_EXPORT_XML_FLAG_V1 but it may miss some details + * about the topology. + * If there is any chance that the exported buffer may ever be imported + * back by a process using hwloc 1.x, one should consider detecting + * it at runtime and using the corresponding export format. + * + * The returned buffer ends with a \0 that is included in the returned + * length. + * + * \p flags is a OR'ed set of ::hwloc_topology_export_xml_flags_e. + * + * \return -1 if a failure occured. + * + * \note See also hwloc_topology_set_userdata_export_callback() + * for exporting application-specific object userdata. + * + * \note The topology-specific userdata pointer is ignored when exporting to XML. + * + * \note Only printable characters may be exported to XML string attributes. + * Any other character, especially any non-ASCII character, will be silently + * dropped. + */ +HWLOC_DECLSPEC int hwloc_topology_export_xmlbuffer(hwloc_topology_t topology, char **xmlbuffer, int *buflen, unsigned long flags); + +/** \brief Free a buffer allocated by hwloc_topology_export_xmlbuffer() */ +HWLOC_DECLSPEC void hwloc_free_xmlbuffer(hwloc_topology_t topology, char *xmlbuffer); + +/** \brief Set the application-specific callback for exporting object userdata + * + * The object userdata pointer is not exported to XML by default because hwloc + * does not know what it contains. + * + * This function lets applications set \p export_cb to a callback function + * that converts this opaque userdata into an exportable string. + * + * \p export_cb is invoked during XML export for each object whose + * \p userdata pointer is not \c NULL. + * The callback should use hwloc_export_obj_userdata() or + * hwloc_export_obj_userdata_base64() to actually export + * something to XML (possibly multiple times per object). + * + * \p export_cb may be set to \c NULL if userdata should not be exported to XML. + * + * \note The topology-specific userdata pointer is ignored when exporting to XML. + */ +HWLOC_DECLSPEC void hwloc_topology_set_userdata_export_callback(hwloc_topology_t topology, + void (*export_cb)(void *reserved, hwloc_topology_t topology, hwloc_obj_t obj)); + +/** \brief Export some object userdata to XML + * + * This function may only be called from within the export() callback passed + * to hwloc_topology_set_userdata_export_callback(). + * It may be invoked one of multiple times to export some userdata to XML. + * The \p buffer content of length \p length is stored with optional name + * \p name. + * + * When importing this XML file, the import() callback (if set) will be + * called exactly as many times as hwloc_export_obj_userdata() was called + * during export(). It will receive the corresponding \p name, \p buffer + * and \p length arguments. + * + * \p reserved, \p topology and \p obj must be the first three parameters + * that were given to the export callback. + * + * Only printable characters may be exported to XML string attributes. + * If a non-printable character is passed in \p name or \p buffer, + * the function returns -1 with errno set to EINVAL. + * + * If exporting binary data, the application should first encode into + * printable characters only (or use hwloc_export_obj_userdata_base64()). + * It should also take care of portability issues if the export may + * be reimported on a different architecture. + */ +HWLOC_DECLSPEC int hwloc_export_obj_userdata(void *reserved, hwloc_topology_t topology, hwloc_obj_t obj, const char *name, const void *buffer, size_t length); + +/** \brief Encode and export some object userdata to XML + * + * This function is similar to hwloc_export_obj_userdata() but it encodes + * the input buffer into printable characters before exporting. + * On import, decoding is automatically performed before the data is given + * to the import() callback if any. + * + * This function may only be called from within the export() callback passed + * to hwloc_topology_set_userdata_export_callback(). + * + * The function does not take care of portability issues if the export + * may be reimported on a different architecture. + */ +HWLOC_DECLSPEC int hwloc_export_obj_userdata_base64(void *reserved, hwloc_topology_t topology, hwloc_obj_t obj, const char *name, const void *buffer, size_t length); + +/** \brief Set the application-specific callback for importing userdata + * + * On XML import, userdata is ignored by default because hwloc does not know + * how to store it in memory. + * + * This function lets applications set \p import_cb to a callback function + * that will get the XML-stored userdata and store it in the object as expected + * by the application. + * + * \p import_cb is called during hwloc_topology_load() as many times as + * hwloc_export_obj_userdata() was called during export. The topology + * is not entirely setup yet. Object attributes are ready to consult, + * but links between objects are not. + * + * \p import_cb may be \c NULL if userdata should be ignored during import. + * + * \note \p buffer contains \p length characters followed by a null byte ('\0'). + * + * \note This function should be called before hwloc_topology_load(). + * + * \note The topology-specific userdata pointer is ignored when importing from XML. + */ +HWLOC_DECLSPEC void hwloc_topology_set_userdata_import_callback(hwloc_topology_t topology, + void (*import_cb)(hwloc_topology_t topology, hwloc_obj_t obj, const char *name, const void *buffer, size_t length)); + +/** @} */ + + +/** \defgroup hwlocality_syntheticexport Exporting Topologies to Synthetic + * @{ + */ + +/** \brief Flags for exporting synthetic topologies. + * + * Flags to be given as a OR'ed set to hwloc_topology_export_synthetic(). + */ +enum hwloc_topology_export_synthetic_flags_e { + /** \brief Export extended types such as L2dcache as basic types such as Cache. + * + * This is required if loading the synthetic description with hwloc < 1.9. + * \hideinitializer + */ + HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_NO_EXTENDED_TYPES = (1UL<<0), + + /** \brief Do not export level attributes. + * + * Ignore level attributes such as memory/cache sizes or PU indexes. + * This is required if loading the synthetic description with hwloc < 1.10. + * \hideinitializer + */ + HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_NO_ATTRS = (1UL<<1), + + /** \brief Export the memory hierarchy as expected in hwloc 1.x. + * + * Instead of attaching memory children to levels, export single NUMA node child + * as normal intermediate levels, when possible. + * This is required if loading the synthetic description with hwloc 1.x. + * However this may fail if some objects have multiple local NUMA nodes. + * \hideinitializer + */ + HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_V1 = (1UL<<2), + + /** \brief Do not export memory information. + * + * Only export the actual hierarchy of normal CPU-side objects and ignore + * where memory is attached. + * This is useful for when the hierarchy of CPUs is what really matters, + * but it behaves as if there was a single machine-wide NUMA node. + * \hideinitializer + */ + HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_IGNORE_MEMORY = (1UL<<3) +}; + +/** \brief Export the topology as a synthetic string. + * + * At most \p buflen characters will be written in \p buffer, + * including the terminating \0. + * + * This exported string may be given back to hwloc_topology_set_synthetic(). + * + * \p flags is a OR'ed set of ::hwloc_topology_export_synthetic_flags_e. + * + * \return The number of characters that were written, + * not including the terminating \0. + * + * \return -1 if the topology could not be exported, + * for instance if it is not symmetric. + * + * \note I/O and Misc children are ignored, the synthetic string only + * describes normal children. + * + * \note A 1024-byte buffer should be large enough for exporting + * topologies in the vast majority of cases. + */ + HWLOC_DECLSPEC int hwloc_topology_export_synthetic(hwloc_topology_t topology, char *buffer, size_t buflen, unsigned long flags); + +/** @} */ + + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + +#endif /* HWLOC_EXPORT_H */ diff --git a/src/3rdparty/hwloc/include/hwloc/gl.h b/src/3rdparty/hwloc/include/hwloc/gl.h new file mode 100644 index 00000000..3e643fa9 --- /dev/null +++ b/src/3rdparty/hwloc/include/hwloc/gl.h @@ -0,0 +1,135 @@ +/* + * Copyright © 2012 Blue Brain Project, EPFL. All rights reserved. + * Copyright © 2012-2013 Inria. All rights reserved. + * See COPYING in top-level directory. + */ + +/** \file + * \brief Macros to help interaction between hwloc and OpenGL displays. + * + * Applications that use both hwloc and OpenGL may want to include + * this file so as to get topology information for OpenGL displays. + */ + +#ifndef HWLOC_GL_H +#define HWLOC_GL_H + +#include + +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + +/** \defgroup hwlocality_gl Interoperability with OpenGL displays + * + * This interface offers ways to retrieve topology information about + * OpenGL displays. + * + * Only the NVIDIA display locality information is currently available, + * using the NV-CONTROL X11 extension and the NVCtrl library. + * + * @{ + */ + +/** \brief Get the hwloc OS device object corresponding to the + * OpenGL display given by port and device index. + * + * Return the OS device object describing the OpenGL display + * whose port (server) is \p port and device (screen) is \p device. + * Return NULL if there is none. + * + * The topology \p topology does not necessarily have to match the current + * machine. For instance the topology may be an XML import of a remote host. + * I/O devices detection and the GL component must be enabled in the topology. + * + * \note The corresponding PCI device object can be obtained by looking + * at the OS device parent object (unless PCI devices are filtered out). + */ +static __hwloc_inline hwloc_obj_t +hwloc_gl_get_display_osdev_by_port_device(hwloc_topology_t topology, + unsigned port, unsigned device) +{ + unsigned x = (unsigned) -1, y = (unsigned) -1; + hwloc_obj_t osdev = NULL; + while ((osdev = hwloc_get_next_osdev(topology, osdev)) != NULL) { + if (HWLOC_OBJ_OSDEV_GPU == osdev->attr->osdev.type + && osdev->name + && sscanf(osdev->name, ":%u.%u", &x, &y) == 2 + && port == x && device == y) + return osdev; + } + errno = EINVAL; + return NULL; +} + +/** \brief Get the hwloc OS device object corresponding to the + * OpenGL display given by name. + * + * Return the OS device object describing the OpenGL display + * whose name is \p name, built as ":port.device" such as ":0.0" . + * Return NULL if there is none. + * + * The topology \p topology does not necessarily have to match the current + * machine. For instance the topology may be an XML import of a remote host. + * I/O devices detection and the GL component must be enabled in the topology. + * + * \note The corresponding PCI device object can be obtained by looking + * at the OS device parent object (unless PCI devices are filtered out). + */ +static __hwloc_inline hwloc_obj_t +hwloc_gl_get_display_osdev_by_name(hwloc_topology_t topology, + const char *name) +{ + hwloc_obj_t osdev = NULL; + while ((osdev = hwloc_get_next_osdev(topology, osdev)) != NULL) { + if (HWLOC_OBJ_OSDEV_GPU == osdev->attr->osdev.type + && osdev->name + && !strcmp(name, osdev->name)) + return osdev; + } + errno = EINVAL; + return NULL; +} + +/** \brief Get the OpenGL display port and device corresponding + * to the given hwloc OS object. + * + * Return the OpenGL display port (server) in \p port and device (screen) + * in \p screen that correspond to the given hwloc OS device object. + * Return \c -1 if there is none. + * + * The topology \p topology does not necessarily have to match the current + * machine. For instance the topology may be an XML import of a remote host. + * I/O devices detection and the GL component must be enabled in the topology. + */ +static __hwloc_inline int +hwloc_gl_get_display_by_osdev(hwloc_topology_t topology __hwloc_attribute_unused, + hwloc_obj_t osdev, + unsigned *port, unsigned *device) +{ + unsigned x = -1, y = -1; + if (HWLOC_OBJ_OSDEV_GPU == osdev->attr->osdev.type + && sscanf(osdev->name, ":%u.%u", &x, &y) == 2) { + *port = x; + *device = y; + return 0; + } + errno = EINVAL; + return -1; +} + +/** @} */ + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + +#endif /* HWLOC_GL_H */ + diff --git a/src/3rdparty/hwloc/include/hwloc/glibc-sched.h b/src/3rdparty/hwloc/include/hwloc/glibc-sched.h new file mode 100644 index 00000000..1f9ba7cd --- /dev/null +++ b/src/3rdparty/hwloc/include/hwloc/glibc-sched.h @@ -0,0 +1,125 @@ +/* + * Copyright © 2009 CNRS + * Copyright © 2009-2013 inria. All rights reserved. + * Copyright © 2009-2011 Université Bordeaux + * Copyright © 2011 Cisco Systems, Inc. All rights reserved. + * See COPYING in top-level directory. + */ + +/** \file + * \brief Macros to help interaction between hwloc and glibc scheduling routines. + * + * Applications that use both hwloc and glibc scheduling routines such as + * sched_getaffinity() or pthread_attr_setaffinity_np() may want to include + * this file so as to ease conversion between their respective types. + */ + +#ifndef HWLOC_GLIBC_SCHED_H +#define HWLOC_GLIBC_SCHED_H + +#include +#include +#include + +#if !defined _GNU_SOURCE || !defined _SCHED_H || (!defined CPU_SETSIZE && !defined sched_priority) +#error Please make sure to include sched.h before including glibc-sched.h, and define _GNU_SOURCE before any inclusion of sched.h +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + + +#ifdef HWLOC_HAVE_CPU_SET + + +/** \defgroup hwlocality_glibc_sched Interoperability with glibc sched affinity + * + * This interface offers ways to convert between hwloc cpusets and glibc cpusets + * such as those manipulated by sched_getaffinity() or pthread_attr_setaffinity_np(). + * + * \note Topology \p topology must match the current machine. + * + * @{ + */ + + +/** \brief Convert hwloc CPU set \p toposet into glibc sched affinity CPU set \p schedset + * + * This function may be used before calling sched_setaffinity or any other function + * that takes a cpu_set_t as input parameter. + * + * \p schedsetsize should be sizeof(cpu_set_t) unless \p schedset was dynamically allocated with CPU_ALLOC + */ +static __hwloc_inline int +hwloc_cpuset_to_glibc_sched_affinity(hwloc_topology_t topology __hwloc_attribute_unused, hwloc_const_cpuset_t hwlocset, + cpu_set_t *schedset, size_t schedsetsize) +{ +#ifdef CPU_ZERO_S + unsigned cpu; + CPU_ZERO_S(schedsetsize, schedset); + hwloc_bitmap_foreach_begin(cpu, hwlocset) + CPU_SET_S(cpu, schedsetsize, schedset); + hwloc_bitmap_foreach_end(); +#else /* !CPU_ZERO_S */ + unsigned cpu; + CPU_ZERO(schedset); + assert(schedsetsize == sizeof(cpu_set_t)); + hwloc_bitmap_foreach_begin(cpu, hwlocset) + CPU_SET(cpu, schedset); + hwloc_bitmap_foreach_end(); +#endif /* !CPU_ZERO_S */ + return 0; +} + +/** \brief Convert glibc sched affinity CPU set \p schedset into hwloc CPU set + * + * This function may be used before calling sched_setaffinity or any other function + * that takes a cpu_set_t as input parameter. + * + * \p schedsetsize should be sizeof(cpu_set_t) unless \p schedset was dynamically allocated with CPU_ALLOC + */ +static __hwloc_inline int +hwloc_cpuset_from_glibc_sched_affinity(hwloc_topology_t topology __hwloc_attribute_unused, hwloc_cpuset_t hwlocset, + const cpu_set_t *schedset, size_t schedsetsize) +{ + int cpu; +#ifdef CPU_ZERO_S + int count; +#endif + hwloc_bitmap_zero(hwlocset); +#ifdef CPU_ZERO_S + count = CPU_COUNT_S(schedsetsize, schedset); + cpu = 0; + while (count) { + if (CPU_ISSET_S(cpu, schedsetsize, schedset)) { + hwloc_bitmap_set(hwlocset, cpu); + count--; + } + cpu++; + } +#else /* !CPU_ZERO_S */ + /* sched.h does not support dynamic cpu_set_t (introduced in glibc 2.7), + * assume we have a very old interface without CPU_COUNT (added in 2.6) + */ + assert(schedsetsize == sizeof(cpu_set_t)); + for(cpu=0; cpu +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + +/** \defgroup hwlocality_helper_find_inside Finding Objects inside a CPU set + * @{ + */ + +/** \brief Get the first largest object included in the given cpuset \p set. + * + * \return the first object that is included in \p set and whose parent is not. + * + * This is convenient for iterating over all largest objects within a CPU set + * by doing a loop getting the first largest object and clearing its CPU set + * from the remaining CPU set. + */ +static __hwloc_inline hwloc_obj_t +hwloc_get_first_largest_obj_inside_cpuset(hwloc_topology_t topology, hwloc_const_cpuset_t set) +{ + hwloc_obj_t obj = hwloc_get_root_obj(topology); + if (!hwloc_bitmap_intersects(obj->cpuset, set)) + return NULL; + while (!hwloc_bitmap_isincluded(obj->cpuset, set)) { + /* while the object intersects without being included, look at its children */ + hwloc_obj_t child = obj->first_child; + while (child) { + if (hwloc_bitmap_intersects(child->cpuset, set)) + break; + child = child->next_sibling; + } + if (!child) + /* no child intersects, return their father */ + return obj; + /* found one intersecting child, look at its children */ + obj = child; + } + /* obj is included, return it */ + return obj; +} + +/** \brief Get the set of largest objects covering exactly a given cpuset \p set + * + * \return the number of objects returned in \p objs. + */ +HWLOC_DECLSPEC int hwloc_get_largest_objs_inside_cpuset (hwloc_topology_t topology, hwloc_const_cpuset_t set, + hwloc_obj_t * __hwloc_restrict objs, int max); + +/** \brief Return the next object at depth \p depth included in CPU set \p set. + * + * If \p prev is \c NULL, return the first object at depth \p depth + * included in \p set. The next invokation should pass the previous + * return value in \p prev so as to obtain the next object in \p set. + * + * \note Objects with empty CPU sets are ignored + * (otherwise they would be considered included in any given set). + * + * \note This function cannot work if objects at the given depth do + * not have CPU sets (I/O or Misc objects). + */ +static __hwloc_inline hwloc_obj_t +hwloc_get_next_obj_inside_cpuset_by_depth (hwloc_topology_t topology, hwloc_const_cpuset_t set, + int depth, hwloc_obj_t prev) +{ + hwloc_obj_t next = hwloc_get_next_obj_by_depth(topology, depth, prev); + if (!next) + return NULL; + while (next && (hwloc_bitmap_iszero(next->cpuset) || !hwloc_bitmap_isincluded(next->cpuset, set))) + next = next->next_cousin; + return next; +} + +/** \brief Return the next object of type \p type included in CPU set \p set. + * + * If there are multiple or no depth for given type, return \c NULL + * and let the caller fallback to + * hwloc_get_next_obj_inside_cpuset_by_depth(). + * + * \note Objects with empty CPU sets are ignored + * (otherwise they would be considered included in any given set). + * + * \note This function cannot work if objects of the given type do + * not have CPU sets (I/O or Misc objects). + */ +static __hwloc_inline hwloc_obj_t +hwloc_get_next_obj_inside_cpuset_by_type (hwloc_topology_t topology, hwloc_const_cpuset_t set, + hwloc_obj_type_t type, hwloc_obj_t prev) +{ + int depth = hwloc_get_type_depth(topology, type); + if (depth == HWLOC_TYPE_DEPTH_UNKNOWN || depth == HWLOC_TYPE_DEPTH_MULTIPLE) + return NULL; + return hwloc_get_next_obj_inside_cpuset_by_depth(topology, set, depth, prev); +} + +/** \brief Return the (logically) \p idx -th object at depth \p depth included in CPU set \p set. + * + * \note Objects with empty CPU sets are ignored + * (otherwise they would be considered included in any given set). + * + * \note This function cannot work if objects at the given depth do + * not have CPU sets (I/O or Misc objects). + */ +static __hwloc_inline hwloc_obj_t +hwloc_get_obj_inside_cpuset_by_depth (hwloc_topology_t topology, hwloc_const_cpuset_t set, + int depth, unsigned idx) __hwloc_attribute_pure; +static __hwloc_inline hwloc_obj_t +hwloc_get_obj_inside_cpuset_by_depth (hwloc_topology_t topology, hwloc_const_cpuset_t set, + int depth, unsigned idx) +{ + hwloc_obj_t obj = hwloc_get_obj_by_depth (topology, depth, 0); + unsigned count = 0; + if (!obj) + return NULL; + while (obj) { + if (!hwloc_bitmap_iszero(obj->cpuset) && hwloc_bitmap_isincluded(obj->cpuset, set)) { + if (count == idx) + return obj; + count++; + } + obj = obj->next_cousin; + } + return NULL; +} + +/** \brief Return the \p idx -th object of type \p type included in CPU set \p set. + * + * If there are multiple or no depth for given type, return \c NULL + * and let the caller fallback to + * hwloc_get_obj_inside_cpuset_by_depth(). + * + * \note Objects with empty CPU sets are ignored + * (otherwise they would be considered included in any given set). + * + * \note This function cannot work if objects of the given type do + * not have CPU sets (I/O or Misc objects). + */ +static __hwloc_inline hwloc_obj_t +hwloc_get_obj_inside_cpuset_by_type (hwloc_topology_t topology, hwloc_const_cpuset_t set, + hwloc_obj_type_t type, unsigned idx) __hwloc_attribute_pure; +static __hwloc_inline hwloc_obj_t +hwloc_get_obj_inside_cpuset_by_type (hwloc_topology_t topology, hwloc_const_cpuset_t set, + hwloc_obj_type_t type, unsigned idx) +{ + int depth = hwloc_get_type_depth(topology, type); + if (depth == HWLOC_TYPE_DEPTH_UNKNOWN || depth == HWLOC_TYPE_DEPTH_MULTIPLE) + return NULL; + return hwloc_get_obj_inside_cpuset_by_depth(topology, set, depth, idx); +} + +/** \brief Return the number of objects at depth \p depth included in CPU set \p set. + * + * \note Objects with empty CPU sets are ignored + * (otherwise they would be considered included in any given set). + * + * \note This function cannot work if objects at the given depth do + * not have CPU sets (I/O or Misc objects). + */ +static __hwloc_inline unsigned +hwloc_get_nbobjs_inside_cpuset_by_depth (hwloc_topology_t topology, hwloc_const_cpuset_t set, + int depth) __hwloc_attribute_pure; +static __hwloc_inline unsigned +hwloc_get_nbobjs_inside_cpuset_by_depth (hwloc_topology_t topology, hwloc_const_cpuset_t set, + int depth) +{ + hwloc_obj_t obj = hwloc_get_obj_by_depth (topology, depth, 0); + unsigned count = 0; + if (!obj) + return 0; + while (obj) { + if (!hwloc_bitmap_iszero(obj->cpuset) && hwloc_bitmap_isincluded(obj->cpuset, set)) + count++; + obj = obj->next_cousin; + } + return count; +} + +/** \brief Return the number of objects of type \p type included in CPU set \p set. + * + * If no object for that type exists inside CPU set \p set, 0 is + * returned. If there are several levels with objects of that type + * inside CPU set \p set, -1 is returned. + * + * \note Objects with empty CPU sets are ignored + * (otherwise they would be considered included in any given set). + * + * \note This function cannot work if objects of the given type do + * not have CPU sets (I/O objects). + */ +static __hwloc_inline int +hwloc_get_nbobjs_inside_cpuset_by_type (hwloc_topology_t topology, hwloc_const_cpuset_t set, + hwloc_obj_type_t type) __hwloc_attribute_pure; +static __hwloc_inline int +hwloc_get_nbobjs_inside_cpuset_by_type (hwloc_topology_t topology, hwloc_const_cpuset_t set, + hwloc_obj_type_t type) +{ + int depth = hwloc_get_type_depth(topology, type); + if (depth == HWLOC_TYPE_DEPTH_UNKNOWN) + return 0; + if (depth == HWLOC_TYPE_DEPTH_MULTIPLE) + return -1; /* FIXME: agregate nbobjs from different levels? */ + return (int) hwloc_get_nbobjs_inside_cpuset_by_depth(topology, set, depth); +} + +/** \brief Return the logical index among the objects included in CPU set \p set. + * + * Consult all objects in the same level as \p obj and inside CPU set \p set + * in the logical order, and return the index of \p obj within them. + * If \p set covers the entire topology, this is the logical index of \p obj. + * Otherwise, this is similar to a logical index within the part of the topology + * defined by CPU set \p set. + * + * \note Objects with empty CPU sets are ignored + * (otherwise they would be considered included in any given set). + * + * \note This function cannot work if obj does not have CPU sets (I/O objects). + */ +static __hwloc_inline int +hwloc_get_obj_index_inside_cpuset (hwloc_topology_t topology __hwloc_attribute_unused, hwloc_const_cpuset_t set, + hwloc_obj_t obj) __hwloc_attribute_pure; +static __hwloc_inline int +hwloc_get_obj_index_inside_cpuset (hwloc_topology_t topology __hwloc_attribute_unused, hwloc_const_cpuset_t set, + hwloc_obj_t obj) +{ + int idx = 0; + if (!hwloc_bitmap_isincluded(obj->cpuset, set)) + return -1; + /* count how many objects are inside the cpuset on the way from us to the beginning of the level */ + while ((obj = obj->prev_cousin) != NULL) + if (!hwloc_bitmap_iszero(obj->cpuset) && hwloc_bitmap_isincluded(obj->cpuset, set)) + idx++; + return idx; +} + +/** @} */ + + + +/** \defgroup hwlocality_helper_find_covering Finding Objects covering at least CPU set + * @{ + */ + +/** \brief Get the child covering at least CPU set \p set. + * + * \return \c NULL if no child matches or if \p set is empty. + * + * \note This function cannot work if parent does not have a CPU set (I/O or Misc objects). + */ +static __hwloc_inline hwloc_obj_t +hwloc_get_child_covering_cpuset (hwloc_topology_t topology __hwloc_attribute_unused, hwloc_const_cpuset_t set, + hwloc_obj_t parent) __hwloc_attribute_pure; +static __hwloc_inline hwloc_obj_t +hwloc_get_child_covering_cpuset (hwloc_topology_t topology __hwloc_attribute_unused, hwloc_const_cpuset_t set, + hwloc_obj_t parent) +{ + hwloc_obj_t child; + if (hwloc_bitmap_iszero(set)) + return NULL; + child = parent->first_child; + while (child) { + if (child->cpuset && hwloc_bitmap_isincluded(set, child->cpuset)) + return child; + child = child->next_sibling; + } + return NULL; +} + +/** \brief Get the lowest object covering at least CPU set \p set + * + * \return \c NULL if no object matches or if \p set is empty. + */ +static __hwloc_inline hwloc_obj_t +hwloc_get_obj_covering_cpuset (hwloc_topology_t topology, hwloc_const_cpuset_t set) __hwloc_attribute_pure; +static __hwloc_inline hwloc_obj_t +hwloc_get_obj_covering_cpuset (hwloc_topology_t topology, hwloc_const_cpuset_t set) +{ + struct hwloc_obj *current = hwloc_get_root_obj(topology); + if (hwloc_bitmap_iszero(set) || !hwloc_bitmap_isincluded(set, current->cpuset)) + return NULL; + while (1) { + hwloc_obj_t child = hwloc_get_child_covering_cpuset(topology, set, current); + if (!child) + return current; + current = child; + } +} + +/** \brief Iterate through same-depth objects covering at least CPU set \p set + * + * If object \p prev is \c NULL, return the first object at depth \p + * depth covering at least part of CPU set \p set. The next + * invokation should pass the previous return value in \p prev so as + * to obtain the next object covering at least another part of \p set. + * + * \note This function cannot work if objects at the given depth do + * not have CPU sets (I/O or Misc objects). + */ +static __hwloc_inline hwloc_obj_t +hwloc_get_next_obj_covering_cpuset_by_depth(hwloc_topology_t topology, hwloc_const_cpuset_t set, + int depth, hwloc_obj_t prev) +{ + hwloc_obj_t next = hwloc_get_next_obj_by_depth(topology, depth, prev); + if (!next) + return NULL; + while (next && !hwloc_bitmap_intersects(set, next->cpuset)) + next = next->next_cousin; + return next; +} + +/** \brief Iterate through same-type objects covering at least CPU set \p set + * + * If object \p prev is \c NULL, return the first object of type \p + * type covering at least part of CPU set \p set. The next invokation + * should pass the previous return value in \p prev so as to obtain + * the next object of type \p type covering at least another part of + * \p set. + * + * If there are no or multiple depths for type \p type, \c NULL is returned. + * The caller may fallback to hwloc_get_next_obj_covering_cpuset_by_depth() + * for each depth. + * + * \note This function cannot work if objects of the given type do + * not have CPU sets (I/O or Misc objects). + */ +static __hwloc_inline hwloc_obj_t +hwloc_get_next_obj_covering_cpuset_by_type(hwloc_topology_t topology, hwloc_const_cpuset_t set, + hwloc_obj_type_t type, hwloc_obj_t prev) +{ + int depth = hwloc_get_type_depth(topology, type); + if (depth == HWLOC_TYPE_DEPTH_UNKNOWN || depth == HWLOC_TYPE_DEPTH_MULTIPLE) + return NULL; + return hwloc_get_next_obj_covering_cpuset_by_depth(topology, set, depth, prev); +} + +/** @} */ + + + +/** \defgroup hwlocality_helper_ancestors Looking at Ancestor and Child Objects + * @{ + * + * Be sure to see the figure in \ref termsanddefs that shows a + * complete topology tree, including depths, child/sibling/cousin + * relationships, and an example of an asymmetric topology where one + * package has fewer caches than its peers. + */ + +/** \brief Returns the ancestor object of \p obj at depth \p depth. + * + * \note \p depth should not be the depth of PU or NUMA objects + * since they are ancestors of no objects (except Misc or I/O). + * This function rather expects an intermediate level depth, + * such as the depth of Packages, Cores, or Caches. + */ +static __hwloc_inline hwloc_obj_t +hwloc_get_ancestor_obj_by_depth (hwloc_topology_t topology __hwloc_attribute_unused, int depth, hwloc_obj_t obj) __hwloc_attribute_pure; +static __hwloc_inline hwloc_obj_t +hwloc_get_ancestor_obj_by_depth (hwloc_topology_t topology __hwloc_attribute_unused, int depth, hwloc_obj_t obj) +{ + hwloc_obj_t ancestor = obj; + if (obj->depth < depth) + return NULL; + while (ancestor && ancestor->depth > depth) + ancestor = ancestor->parent; + return ancestor; +} + +/** \brief Returns the ancestor object of \p obj with type \p type. + * + * \note \p type should not be ::HWLOC_OBJ_PU or ::HWLOC_OBJ_NUMANODE + * since these objects are ancestors of no objects (except Misc or I/O). + * This function rather expects an intermediate object type, + * such as ::HWLOC_OBJ_PACKAGE, ::HWLOC_OBJ_CORE, etc. + */ +static __hwloc_inline hwloc_obj_t +hwloc_get_ancestor_obj_by_type (hwloc_topology_t topology __hwloc_attribute_unused, hwloc_obj_type_t type, hwloc_obj_t obj) __hwloc_attribute_pure; +static __hwloc_inline hwloc_obj_t +hwloc_get_ancestor_obj_by_type (hwloc_topology_t topology __hwloc_attribute_unused, hwloc_obj_type_t type, hwloc_obj_t obj) +{ + hwloc_obj_t ancestor = obj->parent; + while (ancestor && ancestor->type != type) + ancestor = ancestor->parent; + return ancestor; +} + +/** \brief Returns the common parent object to objects \p obj1 and \p obj2 */ +static __hwloc_inline hwloc_obj_t +hwloc_get_common_ancestor_obj (hwloc_topology_t topology __hwloc_attribute_unused, hwloc_obj_t obj1, hwloc_obj_t obj2) __hwloc_attribute_pure; +static __hwloc_inline hwloc_obj_t +hwloc_get_common_ancestor_obj (hwloc_topology_t topology __hwloc_attribute_unused, hwloc_obj_t obj1, hwloc_obj_t obj2) +{ + /* the loop isn't so easy since intermediate ancestors may have + * different depth, causing us to alternate between using obj1->parent + * and obj2->parent. Also, even if at some point we find ancestors of + * of the same depth, their ancestors may have different depth again. + */ + while (obj1 != obj2) { + while (obj1->depth > obj2->depth) + obj1 = obj1->parent; + while (obj2->depth > obj1->depth) + obj2 = obj2->parent; + if (obj1 != obj2 && obj1->depth == obj2->depth) { + obj1 = obj1->parent; + obj2 = obj2->parent; + } + } + return obj1; +} + +/** \brief Returns true if \p obj is inside the subtree beginning with ancestor object \p subtree_root. + * + * \note This function cannot work if \p obj and \p subtree_root objects do + * not have CPU sets (I/O or Misc objects). + */ +static __hwloc_inline int +hwloc_obj_is_in_subtree (hwloc_topology_t topology __hwloc_attribute_unused, hwloc_obj_t obj, hwloc_obj_t subtree_root) __hwloc_attribute_pure; +static __hwloc_inline int +hwloc_obj_is_in_subtree (hwloc_topology_t topology __hwloc_attribute_unused, hwloc_obj_t obj, hwloc_obj_t subtree_root) +{ + return obj->cpuset && subtree_root->cpuset && hwloc_bitmap_isincluded(obj->cpuset, subtree_root->cpuset); +} + +/** \brief Return the next child. + * + * Return the next child among the normal children list, + * then among the memory children list, then among the I/O + * children list, then among the Misc children list. + * + * If \p prev is \c NULL, return the first child. + * + * Return \c NULL when there is no next child. + */ +static __hwloc_inline hwloc_obj_t +hwloc_get_next_child (hwloc_topology_t topology __hwloc_attribute_unused, hwloc_obj_t parent, hwloc_obj_t prev) +{ + hwloc_obj_t obj; + int state = 0; + if (prev) { + if (prev->type == HWLOC_OBJ_MISC) + state = 3; + else if (prev->type == HWLOC_OBJ_BRIDGE || prev->type == HWLOC_OBJ_PCI_DEVICE || prev->type == HWLOC_OBJ_OS_DEVICE) + state = 2; + else if (prev->type == HWLOC_OBJ_NUMANODE) + state = 1; + obj = prev->next_sibling; + } else { + obj = parent->first_child; + } + if (!obj && state == 0) { + obj = parent->memory_first_child; + state = 1; + } + if (!obj && state == 1) { + obj = parent->io_first_child; + state = 2; + } + if (!obj && state == 2) { + obj = parent->misc_first_child; + state = 3; + } + return obj; +} + +/** @} */ + + + +/** \defgroup hwlocality_helper_types Kinds of object Type + * @{ + * + * Each object type is + * either Normal (i.e. hwloc_obj_type_is_normal() returns 1), + * or Memory (i.e. hwloc_obj_type_is_memory() returns 1) + * or I/O (i.e. hwloc_obj_type_is_io() returns 1) + * or Misc (i.e. equal to ::HWLOC_OBJ_MISC). + * It cannot be of more than one of these kinds. + */ + +/** \brief Check whether an object type is Normal. + * + * Normal objects are objects of the main CPU hierarchy + * (Machine, Package, Core, PU, CPU caches, etc.), + * but they are not NUMA nodes, I/O devices or Misc objects. + * + * They are attached to parent as Normal children, + * not as Memory, I/O or Misc children. + * + * \return 1 if an object of type \p type is a Normal object, 0 otherwise. + */ +HWLOC_DECLSPEC int +hwloc_obj_type_is_normal(hwloc_obj_type_t type); + +/** \brief Check whether an object type is I/O. + * + * I/O objects are objects attached to their parents + * in the I/O children list. + * This current includes Bridges, PCI and OS devices. + * + * \return 1 if an object of type \p type is a I/O object, 0 otherwise. + */ +HWLOC_DECLSPEC int +hwloc_obj_type_is_io(hwloc_obj_type_t type); + +/** \brief Check whether an object type is Memory. + * + * Memory objects are objects attached to their parents + * in the Memory children list. + * This current only includes NUMA nodes. + * + * \return 1 if an object of type \p type is a Memory object, 0 otherwise. + */ +HWLOC_DECLSPEC int +hwloc_obj_type_is_memory(hwloc_obj_type_t type); + +/** \brief Check whether an object type is a Cache (Data, Unified or Instruction). + * + * \return 1 if an object of type \p type is a Cache, 0 otherwise. + */ +HWLOC_DECLSPEC int +hwloc_obj_type_is_cache(hwloc_obj_type_t type); + +/** \brief Check whether an object type is a Data or Unified Cache. + * + * \return 1 if an object of type \p type is a Data or Unified Cache, 0 otherwise. + */ +HWLOC_DECLSPEC int +hwloc_obj_type_is_dcache(hwloc_obj_type_t type); + +/** \brief Check whether an object type is a Instruction Cache, + * + * \return 1 if an object of type \p type is a Instruction Cache, 0 otherwise. + */ +HWLOC_DECLSPEC int +hwloc_obj_type_is_icache(hwloc_obj_type_t type); + +/** @} */ + + + +/** \defgroup hwlocality_helper_find_cache Looking at Cache Objects + * @{ + */ + +/** \brief Find the depth of cache objects matching cache level and type. + * + * Return the depth of the topology level that contains cache objects + * whose attributes match \p cachelevel and \p cachetype. + + * This function is identical to calling hwloc_get_type_depth() with the + * corresponding type such as ::HWLOC_OBJ_L1ICACHE, except that it may + * also return a Unified cache when looking for an instruction cache. + * + * If no cache level matches, ::HWLOC_TYPE_DEPTH_UNKNOWN is returned. + * + * If \p cachetype is ::HWLOC_OBJ_CACHE_UNIFIED, the depth of the + * unique matching unified cache level is returned. + * + * If \p cachetype is ::HWLOC_OBJ_CACHE_DATA or ::HWLOC_OBJ_CACHE_INSTRUCTION, + * either a matching cache, or a unified cache is returned. + * + * If \p cachetype is \c -1, it is ignored and multiple levels may + * match. The function returns either the depth of a uniquely matching + * level or ::HWLOC_TYPE_DEPTH_MULTIPLE. + */ +static __hwloc_inline int +hwloc_get_cache_type_depth (hwloc_topology_t topology, + unsigned cachelevel, hwloc_obj_cache_type_t cachetype) +{ + int depth; + int found = HWLOC_TYPE_DEPTH_UNKNOWN; + for (depth=0; ; depth++) { + hwloc_obj_t obj = hwloc_get_obj_by_depth(topology, depth, 0); + if (!obj) + break; + if (!hwloc_obj_type_is_dcache(obj->type) || obj->attr->cache.depth != cachelevel) + /* doesn't match, try next depth */ + continue; + if (cachetype == (hwloc_obj_cache_type_t) -1) { + if (found != HWLOC_TYPE_DEPTH_UNKNOWN) { + /* second match, return MULTIPLE */ + return HWLOC_TYPE_DEPTH_MULTIPLE; + } + /* first match, mark it as found */ + found = depth; + continue; + } + if (obj->attr->cache.type == cachetype || obj->attr->cache.type == HWLOC_OBJ_CACHE_UNIFIED) + /* exact match (either unified is alone, or we match instruction or data), return immediately */ + return depth; + } + /* went to the bottom, return what we found */ + return found; +} + +/** \brief Get the first data (or unified) cache covering a cpuset \p set + * + * \return \c NULL if no cache matches. + */ +static __hwloc_inline hwloc_obj_t +hwloc_get_cache_covering_cpuset (hwloc_topology_t topology, hwloc_const_cpuset_t set) __hwloc_attribute_pure; +static __hwloc_inline hwloc_obj_t +hwloc_get_cache_covering_cpuset (hwloc_topology_t topology, hwloc_const_cpuset_t set) +{ + hwloc_obj_t current = hwloc_get_obj_covering_cpuset(topology, set); + while (current) { + if (hwloc_obj_type_is_dcache(current->type)) + return current; + current = current->parent; + } + return NULL; +} + +/** \brief Get the first data (or unified) cache shared between an object and somebody else. + * + * \return \c NULL if no cache matches or if an invalid object is given. + */ +static __hwloc_inline hwloc_obj_t +hwloc_get_shared_cache_covering_obj (hwloc_topology_t topology __hwloc_attribute_unused, hwloc_obj_t obj) __hwloc_attribute_pure; +static __hwloc_inline hwloc_obj_t +hwloc_get_shared_cache_covering_obj (hwloc_topology_t topology __hwloc_attribute_unused, hwloc_obj_t obj) +{ + hwloc_obj_t current = obj->parent; + if (!obj->cpuset) + return NULL; + while (current) { + if (!hwloc_bitmap_isequal(current->cpuset, obj->cpuset) + && hwloc_obj_type_is_dcache(current->type)) + return current; + current = current->parent; + } + return NULL; +} + +/** @} */ + + + +/** \defgroup hwlocality_helper_find_misc Finding objects, miscellaneous helpers + * @{ + * + * Be sure to see the figure in \ref termsanddefs that shows a + * complete topology tree, including depths, child/sibling/cousin + * relationships, and an example of an asymmetric topology where one + * package has fewer caches than its peers. + */ + +/** \brief Returns the object of type ::HWLOC_OBJ_PU with \p os_index. + * + * This function is useful for converting a CPU set into the PU + * objects it contains. + * When retrieving the current binding (e.g. with hwloc_get_cpubind()), + * one may iterate over the bits of the resulting CPU set with + * hwloc_bitmap_foreach_begin(), and find the corresponding PUs + * with this function. + */ +static __hwloc_inline hwloc_obj_t +hwloc_get_pu_obj_by_os_index(hwloc_topology_t topology, unsigned os_index) __hwloc_attribute_pure; +static __hwloc_inline hwloc_obj_t +hwloc_get_pu_obj_by_os_index(hwloc_topology_t topology, unsigned os_index) +{ + hwloc_obj_t obj = NULL; + while ((obj = hwloc_get_next_obj_by_type(topology, HWLOC_OBJ_PU, obj)) != NULL) + if (obj->os_index == os_index) + return obj; + return NULL; +} + +/** \brief Returns the object of type ::HWLOC_OBJ_NUMANODE with \p os_index. + * + * This function is useful for converting a nodeset into the NUMA node + * objects it contains. + * When retrieving the current binding (e.g. with hwloc_get_membind() with HWLOC_MEMBIND_BYNODESET), + * one may iterate over the bits of the resulting nodeset with + * hwloc_bitmap_foreach_begin(), and find the corresponding NUMA nodes + * with this function. + */ +static __hwloc_inline hwloc_obj_t +hwloc_get_numanode_obj_by_os_index(hwloc_topology_t topology, unsigned os_index) __hwloc_attribute_pure; +static __hwloc_inline hwloc_obj_t +hwloc_get_numanode_obj_by_os_index(hwloc_topology_t topology, unsigned os_index) +{ + hwloc_obj_t obj = NULL; + while ((obj = hwloc_get_next_obj_by_type(topology, HWLOC_OBJ_NUMANODE, obj)) != NULL) + if (obj->os_index == os_index) + return obj; + return NULL; +} + +/** \brief Do a depth-first traversal of the topology to find and sort + * + * all objects that are at the same depth than \p src. + * Report in \p objs up to \p max physically closest ones to \p src. + * + * \return the number of objects returned in \p objs. + * + * \return 0 if \p src is an I/O object. + * + * \note This function requires the \p src object to have a CPU set. + */ +/* TODO: rather provide an iterator? Provide a way to know how much should be allocated? By returning the total number of objects instead? */ +HWLOC_DECLSPEC unsigned hwloc_get_closest_objs (hwloc_topology_t topology, hwloc_obj_t src, hwloc_obj_t * __hwloc_restrict objs, unsigned max); + +/** \brief Find an object below another object, both specified by types and indexes. + * + * Start from the top system object and find object of type \p type1 + * and logical index \p idx1. Then look below this object and find another + * object of type \p type2 and logical index \p idx2. Indexes are specified + * within the parent, not withing the entire system. + * + * For instance, if type1 is PACKAGE, idx1 is 2, type2 is CORE and idx2 + * is 3, return the fourth core object below the third package. + * + * \note This function requires these objects to have a CPU set. + */ +static __hwloc_inline hwloc_obj_t +hwloc_get_obj_below_by_type (hwloc_topology_t topology, + hwloc_obj_type_t type1, unsigned idx1, + hwloc_obj_type_t type2, unsigned idx2) __hwloc_attribute_pure; +static __hwloc_inline hwloc_obj_t +hwloc_get_obj_below_by_type (hwloc_topology_t topology, + hwloc_obj_type_t type1, unsigned idx1, + hwloc_obj_type_t type2, unsigned idx2) +{ + hwloc_obj_t obj; + obj = hwloc_get_obj_by_type (topology, type1, idx1); + if (!obj) + return NULL; + return hwloc_get_obj_inside_cpuset_by_type(topology, obj->cpuset, type2, idx2); +} + +/** \brief Find an object below a chain of objects specified by types and indexes. + * + * This is a generalized version of hwloc_get_obj_below_by_type(). + * + * Arrays \p typev and \p idxv must contain \p nr types and indexes. + * + * Start from the top system object and walk the arrays \p typev and \p idxv. + * For each type and logical index couple in the arrays, look under the previously found + * object to find the index-th object of the given type. + * Indexes are specified within the parent, not withing the entire system. + * + * For instance, if nr is 3, typev contains NODE, PACKAGE and CORE, + * and idxv contains 0, 1 and 2, return the third core object below + * the second package below the first NUMA node. + * + * \note This function requires all these objects and the root object + * to have a CPU set. + */ +static __hwloc_inline hwloc_obj_t +hwloc_get_obj_below_array_by_type (hwloc_topology_t topology, int nr, hwloc_obj_type_t *typev, unsigned *idxv) __hwloc_attribute_pure; +static __hwloc_inline hwloc_obj_t +hwloc_get_obj_below_array_by_type (hwloc_topology_t topology, int nr, hwloc_obj_type_t *typev, unsigned *idxv) +{ + hwloc_obj_t obj = hwloc_get_root_obj(topology); + int i; + for(i=0; icpuset, typev[i], idxv[i]); + } + return obj; +} + +/** @} */ + + + +/** \defgroup hwlocality_helper_distribute Distributing items over a topology + * @{ + */ + +/** \brief Flags to be given to hwloc_distrib(). + */ +enum hwloc_distrib_flags_e { + /** \brief Distrib in reverse order, starting from the last objects. + * \hideinitializer + */ + HWLOC_DISTRIB_FLAG_REVERSE = (1UL<<0) +}; + +/** \brief Distribute \p n items over the topology under \p roots + * + * Array \p set will be filled with \p n cpusets recursively distributed + * linearly over the topology under objects \p roots, down to depth \p until + * (which can be INT_MAX to distribute down to the finest level). + * + * \p n_roots is usually 1 and \p roots only contains the topology root object + * so as to distribute over the entire topology. + * + * This is typically useful when an application wants to distribute \p n + * threads over a machine, giving each of them as much private cache as + * possible and keeping them locally in number order. + * + * The caller may typically want to also call hwloc_bitmap_singlify() + * before binding a thread so that it does not move at all. + * + * \p flags should be 0 or a OR'ed set of ::hwloc_distrib_flags_e. + * + * \note This function requires the \p roots objects to have a CPU set. + * + * \note This function replaces the now deprecated hwloc_distribute() + * and hwloc_distributev() functions. + */ +static __hwloc_inline int +hwloc_distrib(hwloc_topology_t topology, + hwloc_obj_t *roots, unsigned n_roots, + hwloc_cpuset_t *set, + unsigned n, + int until, unsigned long flags) +{ + unsigned i; + unsigned tot_weight; + unsigned given, givenweight; + hwloc_cpuset_t *cpusetp = set; + + if (flags & ~HWLOC_DISTRIB_FLAG_REVERSE) { + errno = EINVAL; + return -1; + } + + tot_weight = 0; + for (i = 0; i < n_roots; i++) + tot_weight += (unsigned) hwloc_bitmap_weight(roots[i]->cpuset); + + for (i = 0, given = 0, givenweight = 0; i < n_roots; i++) { + unsigned chunk, weight; + hwloc_obj_t root = roots[flags & HWLOC_DISTRIB_FLAG_REVERSE ? n_roots-1-i : i]; + hwloc_cpuset_t cpuset = root->cpuset; + if (root->type == HWLOC_OBJ_NUMANODE) + /* NUMANodes have same cpuset as their parent, but we need normal objects below */ + root = root->parent; + weight = (unsigned) hwloc_bitmap_weight(cpuset); + if (!weight) + continue; + /* Give to root a chunk proportional to its weight. + * If previous chunks got rounded-up, we may get a bit less. */ + chunk = (( (givenweight+weight) * n + tot_weight-1) / tot_weight) + - (( givenweight * n + tot_weight-1) / tot_weight); + if (!root->arity || chunk <= 1 || root->depth >= until) { + /* We can't split any more, put everything there. */ + if (chunk) { + /* Fill cpusets with ours */ + unsigned j; + for (j=0; j < chunk; j++) + cpusetp[j] = hwloc_bitmap_dup(cpuset); + } else { + /* We got no chunk, just merge our cpuset to a previous one + * (the first chunk cannot be empty) + * so that this root doesn't get ignored. + */ + assert(given); + hwloc_bitmap_or(cpusetp[-1], cpusetp[-1], cpuset); + } + } else { + /* Still more to distribute, recurse into children */ + hwloc_distrib(topology, root->children, root->arity, cpusetp, chunk, until, flags); + } + cpusetp += chunk; + given += chunk; + givenweight += weight; + } + + return 0; +} + +/** @} */ + + + +/** \defgroup hwlocality_helper_topology_sets CPU and node sets of entire topologies + * @{ + */ + +/** \brief Get complete CPU set + * + * \return the complete CPU set of logical processors of the system. + * + * \note The returned cpuset is not newly allocated and should thus not be + * changed or freed; hwloc_bitmap_dup() must be used to obtain a local copy. + * + * \note This is equivalent to retrieving the root object complete CPU-set. + */ +HWLOC_DECLSPEC hwloc_const_cpuset_t +hwloc_topology_get_complete_cpuset(hwloc_topology_t topology) __hwloc_attribute_pure; + +/** \brief Get topology CPU set + * + * \return the CPU set of logical processors of the system for which hwloc + * provides topology information. This is equivalent to the cpuset of the + * system object. + * + * \note The returned cpuset is not newly allocated and should thus not be + * changed or freed; hwloc_bitmap_dup() must be used to obtain a local copy. + * + * \note This is equivalent to retrieving the root object complete CPU-set. + */ +HWLOC_DECLSPEC hwloc_const_cpuset_t +hwloc_topology_get_topology_cpuset(hwloc_topology_t topology) __hwloc_attribute_pure; + +/** \brief Get allowed CPU set + * + * \return the CPU set of allowed logical processors of the system. + * + * \note If the topology flag ::HWLOC_TOPOLOGY_FLAG_WHOLE_SYSTEM was not set, + * this is identical to hwloc_topology_get_topology_cpuset(), which means + * all PUs are allowed. + * + * \note If ::HWLOC_TOPOLOGY_FLAG_WHOLE_SYSTEM was set, applying + * hwloc_bitmap_intersects() on the result of this function and on an object + * cpuset checks whether there are allowed PUs inside that object. + * Applying hwloc_bitmap_and() returns the list of these allowed PUs. + * + * \note The returned cpuset is not newly allocated and should thus not be + * changed or freed, hwloc_bitmap_dup() must be used to obtain a local copy. + */ +HWLOC_DECLSPEC hwloc_const_cpuset_t +hwloc_topology_get_allowed_cpuset(hwloc_topology_t topology) __hwloc_attribute_pure; + +/** \brief Get complete node set + * + * \return the complete node set of memory of the system. + * + * \note The returned nodeset is not newly allocated and should thus not be + * changed or freed; hwloc_bitmap_dup() must be used to obtain a local copy. + * + * \note This is equivalent to retrieving the root object complete CPU-set. + */ +HWLOC_DECLSPEC hwloc_const_nodeset_t +hwloc_topology_get_complete_nodeset(hwloc_topology_t topology) __hwloc_attribute_pure; + +/** \brief Get topology node set + * + * \return the node set of memory of the system for which hwloc + * provides topology information. This is equivalent to the nodeset of the + * system object. + * + * \note The returned nodeset is not newly allocated and should thus not be + * changed or freed; hwloc_bitmap_dup() must be used to obtain a local copy. + * + * \note This is equivalent to retrieving the root object complete CPU-set. + */ +HWLOC_DECLSPEC hwloc_const_nodeset_t +hwloc_topology_get_topology_nodeset(hwloc_topology_t topology) __hwloc_attribute_pure; + +/** \brief Get allowed node set + * + * \return the node set of allowed memory of the system. + * + * \note If the topology flag ::HWLOC_TOPOLOGY_FLAG_WHOLE_SYSTEM was not set, + * this is identical to hwloc_topology_get_topology_nodeset(), which means + * all NUMA nodes are allowed. + * + * \note If ::HWLOC_TOPOLOGY_FLAG_WHOLE_SYSTEM was set, applying + * hwloc_bitmap_intersects() on the result of this function and on an object + * nodeset checks whether there are allowed NUMA nodes inside that object. + * Applying hwloc_bitmap_and() returns the list of these allowed NUMA nodes. + * + * \note The returned nodeset is not newly allocated and should thus not be + * changed or freed, hwloc_bitmap_dup() must be used to obtain a local copy. + */ +HWLOC_DECLSPEC hwloc_const_nodeset_t +hwloc_topology_get_allowed_nodeset(hwloc_topology_t topology) __hwloc_attribute_pure; + +/** @} */ + + + +/** \defgroup hwlocality_helper_nodeset_convert Converting between CPU sets and node sets + * + * @{ + */ + +/** \brief Convert a CPU set into a NUMA node set and handle non-NUMA cases + * + * If some NUMA nodes have no CPUs at all, this function never sets their + * indexes in the output node set, even if a full CPU set is given in input. + * + * If the topology contains no NUMA nodes, the machine is considered + * as a single memory node, and the following behavior is used: + * If \p cpuset is empty, \p nodeset will be emptied as well. + * Otherwise \p nodeset will be entirely filled. + */ +static __hwloc_inline int +hwloc_cpuset_to_nodeset(hwloc_topology_t topology, hwloc_const_cpuset_t _cpuset, hwloc_nodeset_t nodeset) +{ + int depth = hwloc_get_type_depth(topology, HWLOC_OBJ_NUMANODE); + hwloc_obj_t obj = NULL; + assert(depth != HWLOC_TYPE_DEPTH_UNKNOWN); + hwloc_bitmap_zero(nodeset); + while ((obj = hwloc_get_next_obj_covering_cpuset_by_depth(topology, _cpuset, depth, obj)) != NULL) + if (hwloc_bitmap_set(nodeset, obj->os_index) < 0) + return -1; + return 0; +} + +/** \brief Convert a NUMA node set into a CPU set and handle non-NUMA cases + * + * If the topology contains no NUMA nodes, the machine is considered + * as a single memory node, and the following behavior is used: + * If \p nodeset is empty, \p cpuset will be emptied as well. + * Otherwise \p cpuset will be entirely filled. + * This is useful for manipulating memory binding sets. + */ +static __hwloc_inline int +hwloc_cpuset_from_nodeset(hwloc_topology_t topology, hwloc_cpuset_t _cpuset, hwloc_const_nodeset_t nodeset) +{ + int depth = hwloc_get_type_depth(topology, HWLOC_OBJ_NUMANODE); + hwloc_obj_t obj = NULL; + assert(depth != HWLOC_TYPE_DEPTH_UNKNOWN); + hwloc_bitmap_zero(_cpuset); + while ((obj = hwloc_get_next_obj_by_depth(topology, depth, obj)) != NULL) { + if (hwloc_bitmap_isset(nodeset, obj->os_index)) + /* no need to check obj->cpuset because objects in levels always have a cpuset */ + if (hwloc_bitmap_or(_cpuset, _cpuset, obj->cpuset) < 0) + return -1; + } + return 0; +} + +/** @} */ + + + +/** \defgroup hwlocality_advanced_io Finding I/O objects + * @{ + */ + +/** \brief Get the first non-I/O ancestor object. + * + * Given the I/O object \p ioobj, find the smallest non-I/O ancestor + * object. This object (normal or memory) may then be used for binding + * because it has non-NULL CPU and node sets + * and because its locality is the same as \p ioobj. + * + * \note The resulting object is usually a normal object but it could also + * be a memory object (e.g. NUMA node) in future platforms if I/O objects + * ever get attached to memory instead of CPUs. + */ +static __hwloc_inline hwloc_obj_t +hwloc_get_non_io_ancestor_obj(hwloc_topology_t topology __hwloc_attribute_unused, + hwloc_obj_t ioobj) +{ + hwloc_obj_t obj = ioobj; + while (obj && !obj->cpuset) { + obj = obj->parent; + } + return obj; +} + +/** \brief Get the next PCI device in the system. + * + * \return the first PCI device if \p prev is \c NULL. + */ +static __hwloc_inline hwloc_obj_t +hwloc_get_next_pcidev(hwloc_topology_t topology, hwloc_obj_t prev) +{ + return hwloc_get_next_obj_by_type(topology, HWLOC_OBJ_PCI_DEVICE, prev); +} + +/** \brief Find the PCI device object matching the PCI bus id + * given domain, bus device and function PCI bus id. + */ +static __hwloc_inline hwloc_obj_t +hwloc_get_pcidev_by_busid(hwloc_topology_t topology, + unsigned domain, unsigned bus, unsigned dev, unsigned func) +{ + hwloc_obj_t obj = NULL; + while ((obj = hwloc_get_next_pcidev(topology, obj)) != NULL) { + if (obj->attr->pcidev.domain == domain + && obj->attr->pcidev.bus == bus + && obj->attr->pcidev.dev == dev + && obj->attr->pcidev.func == func) + return obj; + } + return NULL; +} + +/** \brief Find the PCI device object matching the PCI bus id + * given as a string xxxx:yy:zz.t or yy:zz.t. + */ +static __hwloc_inline hwloc_obj_t +hwloc_get_pcidev_by_busidstring(hwloc_topology_t topology, const char *busid) +{ + unsigned domain = 0; /* default */ + unsigned bus, dev, func; + + if (sscanf(busid, "%x:%x.%x", &bus, &dev, &func) != 3 + && sscanf(busid, "%x:%x:%x.%x", &domain, &bus, &dev, &func) != 4) { + errno = EINVAL; + return NULL; + } + + return hwloc_get_pcidev_by_busid(topology, domain, bus, dev, func); +} + +/** \brief Get the next OS device in the system. + * + * \return the first OS device if \p prev is \c NULL. + */ +static __hwloc_inline hwloc_obj_t +hwloc_get_next_osdev(hwloc_topology_t topology, hwloc_obj_t prev) +{ + return hwloc_get_next_obj_by_type(topology, HWLOC_OBJ_OS_DEVICE, prev); +} + +/** \brief Get the next bridge in the system. + * + * \return the first bridge if \p prev is \c NULL. + */ +static __hwloc_inline hwloc_obj_t +hwloc_get_next_bridge(hwloc_topology_t topology, hwloc_obj_t prev) +{ + return hwloc_get_next_obj_by_type(topology, HWLOC_OBJ_BRIDGE, prev); +} + +/* \brief Checks whether a given bridge covers a given PCI bus. + */ +static __hwloc_inline int +hwloc_bridge_covers_pcibus(hwloc_obj_t bridge, + unsigned domain, unsigned bus) +{ + return bridge->type == HWLOC_OBJ_BRIDGE + && bridge->attr->bridge.downstream_type == HWLOC_OBJ_BRIDGE_PCI + && bridge->attr->bridge.downstream.pci.domain == domain + && bridge->attr->bridge.downstream.pci.secondary_bus <= bus + && bridge->attr->bridge.downstream.pci.subordinate_bus >= bus; +} + +/** @} */ + + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + +#endif /* HWLOC_HELPER_H */ diff --git a/src/3rdparty/hwloc/include/hwloc/inlines.h b/src/3rdparty/hwloc/include/hwloc/inlines.h new file mode 100644 index 00000000..494209ea --- /dev/null +++ b/src/3rdparty/hwloc/include/hwloc/inlines.h @@ -0,0 +1,146 @@ +/* + * Copyright © 2009 CNRS + * Copyright © 2009-2018 Inria. All rights reserved. + * Copyright © 2009-2012 Université Bordeaux + * Copyright © 2009-2010 Cisco Systems, Inc. All rights reserved. + * See COPYING in top-level directory. + */ + +/** + * This file contains the inline code of functions declared in hwloc.h + */ + +#ifndef HWLOC_INLINES_H +#define HWLOC_INLINES_H + +#ifndef HWLOC_H +#error Please include the main hwloc.h instead +#endif + +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif + +static __hwloc_inline int +hwloc_get_type_or_below_depth (hwloc_topology_t topology, hwloc_obj_type_t type) +{ + int depth = hwloc_get_type_depth(topology, type); + + if (depth != HWLOC_TYPE_DEPTH_UNKNOWN) + return depth; + + /* find the highest existing level with type order >= */ + for(depth = hwloc_get_type_depth(topology, HWLOC_OBJ_PU); ; depth--) + if (hwloc_compare_types(hwloc_get_depth_type(topology, depth), type) < 0) + return depth+1; + + /* Shouldn't ever happen, as there is always a Machine level with lower order and known depth. */ + /* abort(); */ +} + +static __hwloc_inline int +hwloc_get_type_or_above_depth (hwloc_topology_t topology, hwloc_obj_type_t type) +{ + int depth = hwloc_get_type_depth(topology, type); + + if (depth != HWLOC_TYPE_DEPTH_UNKNOWN) + return depth; + + /* find the lowest existing level with type order <= */ + for(depth = 0; ; depth++) + if (hwloc_compare_types(hwloc_get_depth_type(topology, depth), type) > 0) + return depth-1; + + /* Shouldn't ever happen, as there is always a PU level with higher order and known depth. */ + /* abort(); */ +} + +static __hwloc_inline int +hwloc_get_nbobjs_by_type (hwloc_topology_t topology, hwloc_obj_type_t type) +{ + int depth = hwloc_get_type_depth(topology, type); + if (depth == HWLOC_TYPE_DEPTH_UNKNOWN) + return 0; + if (depth == HWLOC_TYPE_DEPTH_MULTIPLE) + return -1; /* FIXME: agregate nbobjs from different levels? */ + return (int) hwloc_get_nbobjs_by_depth(topology, depth); +} + +static __hwloc_inline hwloc_obj_t +hwloc_get_obj_by_type (hwloc_topology_t topology, hwloc_obj_type_t type, unsigned idx) +{ + int depth = hwloc_get_type_depth(topology, type); + if (depth == HWLOC_TYPE_DEPTH_UNKNOWN) + return NULL; + if (depth == HWLOC_TYPE_DEPTH_MULTIPLE) + return NULL; + return hwloc_get_obj_by_depth(topology, depth, idx); +} + +static __hwloc_inline hwloc_obj_t +hwloc_get_next_obj_by_depth (hwloc_topology_t topology, int depth, hwloc_obj_t prev) +{ + if (!prev) + return hwloc_get_obj_by_depth (topology, depth, 0); + if (prev->depth != depth) + return NULL; + return prev->next_cousin; +} + +static __hwloc_inline hwloc_obj_t +hwloc_get_next_obj_by_type (hwloc_topology_t topology, hwloc_obj_type_t type, + hwloc_obj_t prev) +{ + int depth = hwloc_get_type_depth(topology, type); + if (depth == HWLOC_TYPE_DEPTH_UNKNOWN || depth == HWLOC_TYPE_DEPTH_MULTIPLE) + return NULL; + return hwloc_get_next_obj_by_depth (topology, depth, prev); +} + +static __hwloc_inline hwloc_obj_t +hwloc_get_root_obj (hwloc_topology_t topology) +{ + return hwloc_get_obj_by_depth (topology, 0, 0); +} + +static __hwloc_inline const char * +hwloc_obj_get_info_by_name(hwloc_obj_t obj, const char *name) +{ + unsigned i; + for(i=0; iinfos_count; i++) { + struct hwloc_info_s *info = &obj->infos[i]; + if (!strcmp(info->name, name)) + return info->value; + } + return NULL; +} + +static __hwloc_inline void * +hwloc_alloc_membind_policy(hwloc_topology_t topology, size_t len, hwloc_const_cpuset_t set, hwloc_membind_policy_t policy, int flags) +{ + void *p = hwloc_alloc_membind(topology, len, set, policy, flags); + if (p) + return p; + + if (hwloc_set_membind(topology, set, policy, flags) < 0) + /* hwloc_set_membind() takes care of ignoring errors if non-STRICT */ + return NULL; + + p = hwloc_alloc(topology, len); + if (p && policy != HWLOC_MEMBIND_FIRSTTOUCH) + /* Enforce the binding by touching the data */ + memset(p, 0, len); + return p; +} + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + +#endif /* HWLOC_INLINES_H */ diff --git a/src/3rdparty/hwloc/include/hwloc/intel-mic.h b/src/3rdparty/hwloc/include/hwloc/intel-mic.h new file mode 100644 index 00000000..6f6f9d1b --- /dev/null +++ b/src/3rdparty/hwloc/include/hwloc/intel-mic.h @@ -0,0 +1,134 @@ +/* + * Copyright © 2013-2016 Inria. All rights reserved. + * See COPYING in top-level directory. + */ + +/** \file + * \brief Macros to help interaction between hwloc and Intel Xeon Phi (MIC). + * + * Applications that use both hwloc and Intel Xeon Phi (MIC) may want to + * include this file so as to get topology information for MIC devices. + */ + +#ifndef HWLOC_INTEL_MIC_H +#define HWLOC_INTEL_MIC_H + +#include +#include +#include +#ifdef HWLOC_LINUX_SYS +#include +#include +#include +#endif + +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + +/** \defgroup hwlocality_intel_mic Interoperability with Intel Xeon Phi (MIC) + * + * This interface offers ways to retrieve topology information about + * Intel Xeon Phi (MIC) devices. + * + * @{ + */ + +/** \brief Get the CPU set of logical processors that are physically + * close to MIC device whose index is \p idx. + * + * Return the CPU set describing the locality of the MIC device whose index is \p idx. + * + * Topology \p topology and device index \p idx must match the local machine. + * I/O devices detection is not needed in the topology. + * + * The function only returns the locality of the device. + * If more information about the device is needed, OS objects should + * be used instead, see hwloc_intel_mic_get_device_osdev_by_index(). + * + * This function is currently only implemented in a meaningful way for + * Linux; other systems will simply get a full cpuset. + */ +static __hwloc_inline int +hwloc_intel_mic_get_device_cpuset(hwloc_topology_t topology __hwloc_attribute_unused, + int idx __hwloc_attribute_unused, + hwloc_cpuset_t set) +{ +#ifdef HWLOC_LINUX_SYS + /* If we're on Linux, use the sysfs mechanism to get the local cpus */ +#define HWLOC_INTEL_MIC_DEVICE_SYSFS_PATH_MAX 128 + char path[HWLOC_INTEL_MIC_DEVICE_SYSFS_PATH_MAX]; + DIR *sysdir = NULL; + struct dirent *dirent; + unsigned pcibus, pcidev, pcifunc; + + if (!hwloc_topology_is_thissystem(topology)) { + errno = EINVAL; + return -1; + } + + sprintf(path, "/sys/class/mic/mic%d", idx); + sysdir = opendir(path); + if (!sysdir) + return -1; + + while ((dirent = readdir(sysdir)) != NULL) { + if (sscanf(dirent->d_name, "pci_%02x:%02x.%02x", &pcibus, &pcidev, &pcifunc) == 3) { + sprintf(path, "/sys/class/mic/mic%d/pci_%02x:%02x.%02x/local_cpus", idx, pcibus, pcidev, pcifunc); + if (hwloc_linux_read_path_as_cpumask(path, set) < 0 + || hwloc_bitmap_iszero(set)) + hwloc_bitmap_copy(set, hwloc_topology_get_complete_cpuset(topology)); + break; + } + } + + closedir(sysdir); +#else + /* Non-Linux systems simply get a full cpuset */ + hwloc_bitmap_copy(set, hwloc_topology_get_complete_cpuset(topology)); +#endif + return 0; +} + +/** \brief Get the hwloc OS device object corresponding to the + * MIC device for the given index. + * + * Return the OS device object describing the MIC device whose index is \p idx. + * Return NULL if there is none. + * + * The topology \p topology does not necessarily have to match the current + * machine. For instance the topology may be an XML import of a remote host. + * I/O devices detection must be enabled in the topology. + * + * \note The corresponding PCI device object can be obtained by looking + * at the OS device parent object. + */ +static __hwloc_inline hwloc_obj_t +hwloc_intel_mic_get_device_osdev_by_index(hwloc_topology_t topology, + unsigned idx) +{ + hwloc_obj_t osdev = NULL; + while ((osdev = hwloc_get_next_osdev(topology, osdev)) != NULL) { + if (HWLOC_OBJ_OSDEV_COPROC == osdev->attr->osdev.type + && osdev->name + && !strncmp("mic", osdev->name, 3) + && atoi(osdev->name + 3) == (int) idx) + return osdev; + } + return NULL; +} + +/** @} */ + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + +#endif /* HWLOC_INTEL_MIC_H */ diff --git a/src/3rdparty/hwloc/include/hwloc/linux-libnuma.h b/src/3rdparty/hwloc/include/hwloc/linux-libnuma.h new file mode 100644 index 00000000..7cea4166 --- /dev/null +++ b/src/3rdparty/hwloc/include/hwloc/linux-libnuma.h @@ -0,0 +1,273 @@ +/* + * Copyright © 2009 CNRS + * Copyright © 2009-2017 Inria. All rights reserved. + * Copyright © 2009-2010, 2012 Université Bordeaux + * See COPYING in top-level directory. + */ + +/** \file + * \brief Macros to help interaction between hwloc and Linux libnuma. + * + * Applications that use both Linux libnuma and hwloc may want to + * include this file so as to ease conversion between their respective types. +*/ + +#ifndef HWLOC_LINUX_LIBNUMA_H +#define HWLOC_LINUX_LIBNUMA_H + +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + +/** \defgroup hwlocality_linux_libnuma_ulongs Interoperability with Linux libnuma unsigned long masks + * + * This interface helps converting between Linux libnuma unsigned long masks + * and hwloc cpusets and nodesets. + * + * \note Topology \p topology must match the current machine. + * + * \note The behavior of libnuma is undefined if the kernel is not NUMA-aware. + * (when CONFIG_NUMA is not set in the kernel configuration). + * This helper and libnuma may thus not be strictly compatible in this case, + * which may be detected by checking whether numa_available() returns -1. + * + * @{ + */ + + +/** \brief Convert hwloc CPU set \p cpuset into the array of unsigned long \p mask + * + * \p mask is the array of unsigned long that will be filled. + * \p maxnode contains the maximal node number that may be stored in \p mask. + * \p maxnode will be set to the maximal node number that was found, plus one. + * + * This function may be used before calling set_mempolicy, mbind, migrate_pages + * or any other function that takes an array of unsigned long and a maximal + * node number as input parameter. + */ +static __hwloc_inline int +hwloc_cpuset_to_linux_libnuma_ulongs(hwloc_topology_t topology, hwloc_const_cpuset_t cpuset, + unsigned long *mask, unsigned long *maxnode) +{ + int depth = hwloc_get_type_depth(topology, HWLOC_OBJ_NUMANODE); + unsigned long outmaxnode = -1; + hwloc_obj_t node = NULL; + + /* round-up to the next ulong and clear all bytes */ + *maxnode = (*maxnode + 8*sizeof(*mask) - 1) & ~(8*sizeof(*mask) - 1); + memset(mask, 0, *maxnode/8); + + while ((node = hwloc_get_next_obj_covering_cpuset_by_depth(topology, cpuset, depth, node)) != NULL) { + if (node->os_index >= *maxnode) + continue; + mask[node->os_index/sizeof(*mask)/8] |= 1UL << (node->os_index % (sizeof(*mask)*8)); + if (outmaxnode == (unsigned long) -1 || outmaxnode < node->os_index) + outmaxnode = node->os_index; + } + + *maxnode = outmaxnode+1; + return 0; +} + +/** \brief Convert hwloc NUMA node set \p nodeset into the array of unsigned long \p mask + * + * \p mask is the array of unsigned long that will be filled. + * \p maxnode contains the maximal node number that may be stored in \p mask. + * \p maxnode will be set to the maximal node number that was found, plus one. + * + * This function may be used before calling set_mempolicy, mbind, migrate_pages + * or any other function that takes an array of unsigned long and a maximal + * node number as input parameter. + */ +static __hwloc_inline int +hwloc_nodeset_to_linux_libnuma_ulongs(hwloc_topology_t topology, hwloc_const_nodeset_t nodeset, + unsigned long *mask, unsigned long *maxnode) +{ + int depth = hwloc_get_type_depth(topology, HWLOC_OBJ_NUMANODE); + unsigned long outmaxnode = -1; + hwloc_obj_t node = NULL; + + /* round-up to the next ulong and clear all bytes */ + *maxnode = (*maxnode + 8*sizeof(*mask) - 1) & ~(8*sizeof(*mask) - 1); + memset(mask, 0, *maxnode/8); + + while ((node = hwloc_get_next_obj_by_depth(topology, depth, node)) != NULL) { + if (node->os_index >= *maxnode) + continue; + if (!hwloc_bitmap_isset(nodeset, node->os_index)) + continue; + mask[node->os_index/sizeof(*mask)/8] |= 1UL << (node->os_index % (sizeof(*mask)*8)); + if (outmaxnode == (unsigned long) -1 || outmaxnode < node->os_index) + outmaxnode = node->os_index; + } + + *maxnode = outmaxnode+1; + return 0; +} + +/** \brief Convert the array of unsigned long \p mask into hwloc CPU set + * + * \p mask is a array of unsigned long that will be read. + * \p maxnode contains the maximal node number that may be read in \p mask. + * + * This function may be used after calling get_mempolicy or any other function + * that takes an array of unsigned long as output parameter (and possibly + * a maximal node number as input parameter). + */ +static __hwloc_inline int +hwloc_cpuset_from_linux_libnuma_ulongs(hwloc_topology_t topology, hwloc_cpuset_t cpuset, + const unsigned long *mask, unsigned long maxnode) +{ + int depth = hwloc_get_type_depth(topology, HWLOC_OBJ_NUMANODE); + hwloc_obj_t node = NULL; + hwloc_bitmap_zero(cpuset); + while ((node = hwloc_get_next_obj_by_depth(topology, depth, node)) != NULL) + if (node->os_index < maxnode + && (mask[node->os_index/sizeof(*mask)/8] & (1UL << (node->os_index % (sizeof(*mask)*8))))) + hwloc_bitmap_or(cpuset, cpuset, node->cpuset); + return 0; +} + +/** \brief Convert the array of unsigned long \p mask into hwloc NUMA node set + * + * \p mask is a array of unsigned long that will be read. + * \p maxnode contains the maximal node number that may be read in \p mask. + * + * This function may be used after calling get_mempolicy or any other function + * that takes an array of unsigned long as output parameter (and possibly + * a maximal node number as input parameter). + */ +static __hwloc_inline int +hwloc_nodeset_from_linux_libnuma_ulongs(hwloc_topology_t topology, hwloc_nodeset_t nodeset, + const unsigned long *mask, unsigned long maxnode) +{ + int depth = hwloc_get_type_depth(topology, HWLOC_OBJ_NUMANODE); + hwloc_obj_t node = NULL; + hwloc_bitmap_zero(nodeset); + while ((node = hwloc_get_next_obj_by_depth(topology, depth, node)) != NULL) + if (node->os_index < maxnode + && (mask[node->os_index/sizeof(*mask)/8] & (1UL << (node->os_index % (sizeof(*mask)*8))))) + hwloc_bitmap_set(nodeset, node->os_index); + return 0; +} + +/** @} */ + + + +/** \defgroup hwlocality_linux_libnuma_bitmask Interoperability with Linux libnuma bitmask + * + * This interface helps converting between Linux libnuma bitmasks + * and hwloc cpusets and nodesets. + * + * \note Topology \p topology must match the current machine. + * + * \note The behavior of libnuma is undefined if the kernel is not NUMA-aware. + * (when CONFIG_NUMA is not set in the kernel configuration). + * This helper and libnuma may thus not be strictly compatible in this case, + * which may be detected by checking whether numa_available() returns -1. + * + * @{ + */ + + +/** \brief Convert hwloc CPU set \p cpuset into the returned libnuma bitmask + * + * The returned bitmask should later be freed with numa_bitmask_free. + * + * This function may be used before calling many numa_ functions + * that use a struct bitmask as an input parameter. + * + * \return newly allocated struct bitmask. + */ +static __hwloc_inline struct bitmask * +hwloc_cpuset_to_linux_libnuma_bitmask(hwloc_topology_t topology, hwloc_const_cpuset_t cpuset) __hwloc_attribute_malloc; +static __hwloc_inline struct bitmask * +hwloc_cpuset_to_linux_libnuma_bitmask(hwloc_topology_t topology, hwloc_const_cpuset_t cpuset) +{ + int depth = hwloc_get_type_depth(topology, HWLOC_OBJ_NUMANODE); + hwloc_obj_t node = NULL; + struct bitmask *bitmask = numa_allocate_cpumask(); + if (!bitmask) + return NULL; + while ((node = hwloc_get_next_obj_covering_cpuset_by_depth(topology, cpuset, depth, node)) != NULL) + if (node->attr->numanode.local_memory) + numa_bitmask_setbit(bitmask, node->os_index); + return bitmask; +} + +/** \brief Convert hwloc NUMA node set \p nodeset into the returned libnuma bitmask + * + * The returned bitmask should later be freed with numa_bitmask_free. + * + * This function may be used before calling many numa_ functions + * that use a struct bitmask as an input parameter. + * + * \return newly allocated struct bitmask. + */ +static __hwloc_inline struct bitmask * +hwloc_nodeset_to_linux_libnuma_bitmask(hwloc_topology_t topology, hwloc_const_nodeset_t nodeset) __hwloc_attribute_malloc; +static __hwloc_inline struct bitmask * +hwloc_nodeset_to_linux_libnuma_bitmask(hwloc_topology_t topology, hwloc_const_nodeset_t nodeset) +{ + int depth = hwloc_get_type_depth(topology, HWLOC_OBJ_NUMANODE); + hwloc_obj_t node = NULL; + struct bitmask *bitmask = numa_allocate_cpumask(); + if (!bitmask) + return NULL; + while ((node = hwloc_get_next_obj_by_depth(topology, depth, node)) != NULL) + if (hwloc_bitmap_isset(nodeset, node->os_index) && node->attr->numanode.local_memory) + numa_bitmask_setbit(bitmask, node->os_index); + return bitmask; +} + +/** \brief Convert libnuma bitmask \p bitmask into hwloc CPU set \p cpuset + * + * This function may be used after calling many numa_ functions + * that use a struct bitmask as an output parameter. + */ +static __hwloc_inline int +hwloc_cpuset_from_linux_libnuma_bitmask(hwloc_topology_t topology, hwloc_cpuset_t cpuset, + const struct bitmask *bitmask) +{ + int depth = hwloc_get_type_depth(topology, HWLOC_OBJ_NUMANODE); + hwloc_obj_t node = NULL; + hwloc_bitmap_zero(cpuset); + while ((node = hwloc_get_next_obj_by_depth(topology, depth, node)) != NULL) + if (numa_bitmask_isbitset(bitmask, node->os_index)) + hwloc_bitmap_or(cpuset, cpuset, node->cpuset); + return 0; +} + +/** \brief Convert libnuma bitmask \p bitmask into hwloc NUMA node set \p nodeset + * + * This function may be used after calling many numa_ functions + * that use a struct bitmask as an output parameter. + */ +static __hwloc_inline int +hwloc_nodeset_from_linux_libnuma_bitmask(hwloc_topology_t topology, hwloc_nodeset_t nodeset, + const struct bitmask *bitmask) +{ + int depth = hwloc_get_type_depth(topology, HWLOC_OBJ_NUMANODE); + hwloc_obj_t node = NULL; + hwloc_bitmap_zero(nodeset); + while ((node = hwloc_get_next_obj_by_depth(topology, depth, node)) != NULL) + if (numa_bitmask_isbitset(bitmask, node->os_index)) + hwloc_bitmap_set(nodeset, node->os_index); + return 0; +} + +/** @} */ + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + +#endif /* HWLOC_LINUX_NUMA_H */ diff --git a/src/3rdparty/hwloc/include/hwloc/linux.h b/src/3rdparty/hwloc/include/hwloc/linux.h new file mode 100644 index 00000000..c409e1c2 --- /dev/null +++ b/src/3rdparty/hwloc/include/hwloc/linux.h @@ -0,0 +1,79 @@ +/* + * Copyright © 2009 CNRS + * Copyright © 2009-2016 Inria. All rights reserved. + * Copyright © 2009-2011 Université Bordeaux + * See COPYING in top-level directory. + */ + +/** \file + * \brief Macros to help interaction between hwloc and Linux. + * + * Applications that use hwloc on Linux may want to include this file + * if using some low-level Linux features. + */ + +#ifndef HWLOC_LINUX_H +#define HWLOC_LINUX_H + +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + +/** \defgroup hwlocality_linux Linux-specific helpers + * + * This includes helpers for manipulating Linux kernel cpumap files, and hwloc + * equivalents of the Linux sched_setaffinity and sched_getaffinity system calls. + * + * @{ + */ + +/** \brief Bind a thread \p tid on cpus given in cpuset \p set + * + * The behavior is exactly the same as the Linux sched_setaffinity system call, + * but uses a hwloc cpuset. + * + * \note This is equivalent to calling hwloc_set_proc_cpubind() with + * HWLOC_CPUBIND_THREAD as flags. + */ +HWLOC_DECLSPEC int hwloc_linux_set_tid_cpubind(hwloc_topology_t topology, pid_t tid, hwloc_const_cpuset_t set); + +/** \brief Get the current binding of thread \p tid + * + * The behavior is exactly the same as the Linux sched_getaffinity system call, + * but uses a hwloc cpuset. + * + * \note This is equivalent to calling hwloc_get_proc_cpubind() with + * ::HWLOC_CPUBIND_THREAD as flags. + */ +HWLOC_DECLSPEC int hwloc_linux_get_tid_cpubind(hwloc_topology_t topology, pid_t tid, hwloc_cpuset_t set); + +/** \brief Get the last physical CPU where thread \p tid ran. + * + * \note This is equivalent to calling hwloc_get_proc_last_cpu_location() with + * ::HWLOC_CPUBIND_THREAD as flags. + */ +HWLOC_DECLSPEC int hwloc_linux_get_tid_last_cpu_location(hwloc_topology_t topology, pid_t tid, hwloc_bitmap_t set); + +/** \brief Convert a linux kernel cpumask file \p path into a hwloc bitmap \p set. + * + * Might be used when reading CPU set from sysfs attributes such as topology + * and caches for processors, or local_cpus for devices. + * + * \note This function ignores the HWLOC_FSROOT environment variable. + */ +HWLOC_DECLSPEC int hwloc_linux_read_path_as_cpumask(const char *path, hwloc_bitmap_t set); + +/** @} */ + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + +#endif /* HWLOC_LINUX_H */ diff --git a/src/3rdparty/hwloc/include/hwloc/nvml.h b/src/3rdparty/hwloc/include/hwloc/nvml.h new file mode 100644 index 00000000..19710866 --- /dev/null +++ b/src/3rdparty/hwloc/include/hwloc/nvml.h @@ -0,0 +1,181 @@ +/* + * Copyright © 2012-2016 Inria. All rights reserved. + * See COPYING in top-level directory. + */ + +/** \file + * \brief Macros to help interaction between hwloc and the NVIDIA Management Library. + * + * Applications that use both hwloc and the NVIDIA Management Library may want to + * include this file so as to get topology information for NVML devices. + */ + +#ifndef HWLOC_NVML_H +#define HWLOC_NVML_H + +#include +#include +#include +#ifdef HWLOC_LINUX_SYS +#include +#endif + +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + +/** \defgroup hwlocality_nvml Interoperability with the NVIDIA Management Library + * + * This interface offers ways to retrieve topology information about + * devices managed by the NVIDIA Management Library (NVML). + * + * @{ + */ + +/** \brief Get the CPU set of logical processors that are physically + * close to NVML device \p device. + * + * Return the CPU set describing the locality of the NVML device \p device. + * + * Topology \p topology and device \p device must match the local machine. + * I/O devices detection and the NVML component are not needed in the topology. + * + * The function only returns the locality of the device. + * If more information about the device is needed, OS objects should + * be used instead, see hwloc_nvml_get_device_osdev() + * and hwloc_nvml_get_device_osdev_by_index(). + * + * This function is currently only implemented in a meaningful way for + * Linux; other systems will simply get a full cpuset. + */ +static __hwloc_inline int +hwloc_nvml_get_device_cpuset(hwloc_topology_t topology __hwloc_attribute_unused, + nvmlDevice_t device, hwloc_cpuset_t set) +{ +#ifdef HWLOC_LINUX_SYS + /* If we're on Linux, use the sysfs mechanism to get the local cpus */ +#define HWLOC_NVML_DEVICE_SYSFS_PATH_MAX 128 + char path[HWLOC_NVML_DEVICE_SYSFS_PATH_MAX]; + nvmlReturn_t nvres; + nvmlPciInfo_t pci; + + if (!hwloc_topology_is_thissystem(topology)) { + errno = EINVAL; + return -1; + } + + nvres = nvmlDeviceGetPciInfo(device, &pci); + if (NVML_SUCCESS != nvres) { + errno = EINVAL; + return -1; + } + + sprintf(path, "/sys/bus/pci/devices/%04x:%02x:%02x.0/local_cpus", pci.domain, pci.bus, pci.device); + if (hwloc_linux_read_path_as_cpumask(path, set) < 0 + || hwloc_bitmap_iszero(set)) + hwloc_bitmap_copy(set, hwloc_topology_get_complete_cpuset(topology)); +#else + /* Non-Linux systems simply get a full cpuset */ + hwloc_bitmap_copy(set, hwloc_topology_get_complete_cpuset(topology)); +#endif + return 0; +} + +/** \brief Get the hwloc OS device object corresponding to the + * NVML device whose index is \p idx. + * + * Return the OS device object describing the NVML device whose + * index is \p idx. Returns NULL if there is none. + * + * The topology \p topology does not necessarily have to match the current + * machine. For instance the topology may be an XML import of a remote host. + * I/O devices detection and the NVML component must be enabled in the topology. + * + * \note The corresponding PCI device object can be obtained by looking + * at the OS device parent object (unless PCI devices are filtered out). + */ +static __hwloc_inline hwloc_obj_t +hwloc_nvml_get_device_osdev_by_index(hwloc_topology_t topology, unsigned idx) +{ + hwloc_obj_t osdev = NULL; + while ((osdev = hwloc_get_next_osdev(topology, osdev)) != NULL) { + if (HWLOC_OBJ_OSDEV_GPU == osdev->attr->osdev.type + && osdev->name + && !strncmp("nvml", osdev->name, 4) + && atoi(osdev->name + 4) == (int) idx) + return osdev; + } + return NULL; +} + +/** \brief Get the hwloc OS device object corresponding to NVML device \p device. + * + * Return the hwloc OS device object that describes the given + * NVML device \p device. Return NULL if there is none. + * + * Topology \p topology and device \p device must match the local machine. + * I/O devices detection and the NVML component must be enabled in the topology. + * If not, the locality of the object may still be found using + * hwloc_nvml_get_device_cpuset(). + * + * \note The corresponding hwloc PCI device may be found by looking + * at the result parent pointer (unless PCI devices are filtered out). + */ +static __hwloc_inline hwloc_obj_t +hwloc_nvml_get_device_osdev(hwloc_topology_t topology, nvmlDevice_t device) +{ + hwloc_obj_t osdev; + nvmlReturn_t nvres; + nvmlPciInfo_t pci; + char uuid[64]; + + if (!hwloc_topology_is_thissystem(topology)) { + errno = EINVAL; + return NULL; + } + + nvres = nvmlDeviceGetPciInfo(device, &pci); + if (NVML_SUCCESS != nvres) + return NULL; + + nvres = nvmlDeviceGetUUID(device, uuid, sizeof(uuid)); + if (NVML_SUCCESS != nvres) + uuid[0] = '\0'; + + osdev = NULL; + while ((osdev = hwloc_get_next_osdev(topology, osdev)) != NULL) { + hwloc_obj_t pcidev = osdev->parent; + const char *info; + + if (strncmp(osdev->name, "nvml", 4)) + continue; + + if (pcidev + && pcidev->type == HWLOC_OBJ_PCI_DEVICE + && pcidev->attr->pcidev.domain == pci.domain + && pcidev->attr->pcidev.bus == pci.bus + && pcidev->attr->pcidev.dev == pci.device + && pcidev->attr->pcidev.func == 0) + return osdev; + + info = hwloc_obj_get_info_by_name(osdev, "NVIDIAUUID"); + if (info && !strcmp(info, uuid)) + return osdev; + } + + return NULL; +} + +/** @} */ + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + +#endif /* HWLOC_NVML_H */ diff --git a/src/3rdparty/hwloc/include/hwloc/opencl.h b/src/3rdparty/hwloc/include/hwloc/opencl.h new file mode 100644 index 00000000..058968d7 --- /dev/null +++ b/src/3rdparty/hwloc/include/hwloc/opencl.h @@ -0,0 +1,206 @@ +/* + * Copyright © 2012-2018 Inria. All rights reserved. + * Copyright © 2013, 2018 Université Bordeaux. All right reserved. + * See COPYING in top-level directory. + */ + +/** \file + * \brief Macros to help interaction between hwloc and the OpenCL interface. + * + * Applications that use both hwloc and OpenCL may want to + * include this file so as to get topology information for OpenCL devices. + */ + +#ifndef HWLOC_OPENCL_H +#define HWLOC_OPENCL_H + +#include +#include +#include +#ifdef HWLOC_LINUX_SYS +#include +#endif + +#ifdef __APPLE__ +#include +#include +#else +#include +#include +#endif + +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + +/** \defgroup hwlocality_opencl Interoperability with OpenCL + * + * This interface offers ways to retrieve topology information about + * OpenCL devices. + * + * Only the AMD OpenCL interface currently offers useful locality information + * about its devices. + * + * @{ + */ + +/** \brief Get the CPU set of logical processors that are physically + * close to OpenCL device \p device. + * + * Return the CPU set describing the locality of the OpenCL device \p device. + * + * Topology \p topology and device \p device must match the local machine. + * I/O devices detection and the OpenCL component are not needed in the topology. + * + * The function only returns the locality of the device. + * If more information about the device is needed, OS objects should + * be used instead, see hwloc_opencl_get_device_osdev() + * and hwloc_opencl_get_device_osdev_by_index(). + * + * This function is currently only implemented in a meaningful way for + * Linux with the AMD OpenCL implementation; other systems will simply + * get a full cpuset. + */ +static __hwloc_inline int +hwloc_opencl_get_device_cpuset(hwloc_topology_t topology __hwloc_attribute_unused, + cl_device_id device __hwloc_attribute_unused, + hwloc_cpuset_t set) +{ +#if (defined HWLOC_LINUX_SYS) && (defined CL_DEVICE_TOPOLOGY_AMD) + /* If we're on Linux + AMD OpenCL, use the AMD extension + the sysfs mechanism to get the local cpus */ +#define HWLOC_OPENCL_DEVICE_SYSFS_PATH_MAX 128 + char path[HWLOC_OPENCL_DEVICE_SYSFS_PATH_MAX]; + cl_device_topology_amd amdtopo; + cl_int clret; + + if (!hwloc_topology_is_thissystem(topology)) { + errno = EINVAL; + return -1; + } + + clret = clGetDeviceInfo(device, CL_DEVICE_TOPOLOGY_AMD, sizeof(amdtopo), &amdtopo, NULL); + if (CL_SUCCESS != clret) { + hwloc_bitmap_copy(set, hwloc_topology_get_complete_cpuset(topology)); + return 0; + } + if (CL_DEVICE_TOPOLOGY_TYPE_PCIE_AMD != amdtopo.raw.type) { + hwloc_bitmap_copy(set, hwloc_topology_get_complete_cpuset(topology)); + return 0; + } + + sprintf(path, "/sys/bus/pci/devices/0000:%02x:%02x.%01x/local_cpus", + (unsigned) amdtopo.pcie.bus, (unsigned) amdtopo.pcie.device, (unsigned) amdtopo.pcie.function); + if (hwloc_linux_read_path_as_cpumask(path, set) < 0 + || hwloc_bitmap_iszero(set)) + hwloc_bitmap_copy(set, hwloc_topology_get_complete_cpuset(topology)); +#else + /* Non-Linux + AMD OpenCL systems simply get a full cpuset */ + hwloc_bitmap_copy(set, hwloc_topology_get_complete_cpuset(topology)); +#endif + return 0; +} + +/** \brief Get the hwloc OS device object corresponding to the + * OpenCL device for the given indexes. + * + * Return the OS device object describing the OpenCL device + * whose platform index is \p platform_index, + * and whose device index within this platform if \p device_index. + * Return NULL if there is none. + * + * The topology \p topology does not necessarily have to match the current + * machine. For instance the topology may be an XML import of a remote host. + * I/O devices detection and the OpenCL component must be enabled in the topology. + * + * \note The corresponding PCI device object can be obtained by looking + * at the OS device parent object (unless PCI devices are filtered out). + */ +static __hwloc_inline hwloc_obj_t +hwloc_opencl_get_device_osdev_by_index(hwloc_topology_t topology, + unsigned platform_index, unsigned device_index) +{ + unsigned x = (unsigned) -1, y = (unsigned) -1; + hwloc_obj_t osdev = NULL; + while ((osdev = hwloc_get_next_osdev(topology, osdev)) != NULL) { + if (HWLOC_OBJ_OSDEV_COPROC == osdev->attr->osdev.type + && osdev->name + && sscanf(osdev->name, "opencl%ud%u", &x, &y) == 2 + && platform_index == x && device_index == y) + return osdev; + } + return NULL; +} + +/** \brief Get the hwloc OS device object corresponding to OpenCL device \p deviceX. + * + * Use OpenCL device attributes to find the corresponding hwloc OS device object. + * Return NULL if there is none or if useful attributes are not available. + * + * This function currently only works on AMD OpenCL devices that support + * the CL_DEVICE_TOPOLOGY_AMD extension. hwloc_opencl_get_device_osdev_by_index() + * should be preferred whenever possible, i.e. when platform and device index + * are known. + * + * Topology \p topology and device \p device must match the local machine. + * I/O devices detection and the OpenCL component must be enabled in the topology. + * If not, the locality of the object may still be found using + * hwloc_opencl_get_device_cpuset(). + * + * \note This function cannot work if PCI devices are filtered out. + * + * \note The corresponding hwloc PCI device may be found by looking + * at the result parent pointer (unless PCI devices are filtered out). + */ +static __hwloc_inline hwloc_obj_t +hwloc_opencl_get_device_osdev(hwloc_topology_t topology __hwloc_attribute_unused, + cl_device_id device __hwloc_attribute_unused) +{ +#ifdef CL_DEVICE_TOPOLOGY_AMD + hwloc_obj_t osdev; + cl_device_topology_amd amdtopo; + cl_int clret; + + clret = clGetDeviceInfo(device, CL_DEVICE_TOPOLOGY_AMD, sizeof(amdtopo), &amdtopo, NULL); + if (CL_SUCCESS != clret) { + errno = EINVAL; + return NULL; + } + if (CL_DEVICE_TOPOLOGY_TYPE_PCIE_AMD != amdtopo.raw.type) { + errno = EINVAL; + return NULL; + } + + osdev = NULL; + while ((osdev = hwloc_get_next_osdev(topology, osdev)) != NULL) { + hwloc_obj_t pcidev = osdev->parent; + if (strncmp(osdev->name, "opencl", 6)) + continue; + if (pcidev + && pcidev->type == HWLOC_OBJ_PCI_DEVICE + && pcidev->attr->pcidev.domain == 0 + && pcidev->attr->pcidev.bus == amdtopo.pcie.bus + && pcidev->attr->pcidev.dev == amdtopo.pcie.device + && pcidev->attr->pcidev.func == amdtopo.pcie.function) + return osdev; + /* if PCI are filtered out, we need a info attr to match on */ + } + + return NULL; +#else + return NULL; +#endif +} + +/** @} */ + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + +#endif /* HWLOC_OPENCL_H */ diff --git a/src/3rdparty/hwloc/include/hwloc/openfabrics-verbs.h b/src/3rdparty/hwloc/include/hwloc/openfabrics-verbs.h new file mode 100644 index 00000000..174ab4a5 --- /dev/null +++ b/src/3rdparty/hwloc/include/hwloc/openfabrics-verbs.h @@ -0,0 +1,150 @@ +/* + * Copyright © 2009 CNRS + * Copyright © 2009-2016 Inria. All rights reserved. + * Copyright © 2009-2010 Université Bordeaux + * Copyright © 2009-2011 Cisco Systems, Inc. All rights reserved. + * See COPYING in top-level directory. + */ + +/** \file + * \brief Macros to help interaction between hwloc and OpenFabrics + * verbs. + * + * Applications that use both hwloc and OpenFabrics verbs may want to + * include this file so as to get topology information for OpenFabrics + * hardware (InfiniBand, etc). + * + */ + +#ifndef HWLOC_OPENFABRICS_VERBS_H +#define HWLOC_OPENFABRICS_VERBS_H + +#include +#include +#ifdef HWLOC_LINUX_SYS +#include +#endif + +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + +/** \defgroup hwlocality_openfabrics Interoperability with OpenFabrics + * + * This interface offers ways to retrieve topology information about + * OpenFabrics devices (InfiniBand, Omni-Path, usNIC, etc). + * + * @{ + */ + +/** \brief Get the CPU set of logical processors that are physically + * close to device \p ibdev. + * + * Return the CPU set describing the locality of the OpenFabrics + * device \p ibdev (InfiniBand, etc). + * + * Topology \p topology and device \p ibdev must match the local machine. + * I/O devices detection is not needed in the topology. + * + * The function only returns the locality of the device. + * If more information about the device is needed, OS objects should + * be used instead, see hwloc_ibv_get_device_osdev() + * and hwloc_ibv_get_device_osdev_by_name(). + * + * This function is currently only implemented in a meaningful way for + * Linux; other systems will simply get a full cpuset. + */ +static __hwloc_inline int +hwloc_ibv_get_device_cpuset(hwloc_topology_t topology __hwloc_attribute_unused, + struct ibv_device *ibdev, hwloc_cpuset_t set) +{ +#ifdef HWLOC_LINUX_SYS + /* If we're on Linux, use the verbs-provided sysfs mechanism to + get the local cpus */ +#define HWLOC_OPENFABRICS_VERBS_SYSFS_PATH_MAX 128 + char path[HWLOC_OPENFABRICS_VERBS_SYSFS_PATH_MAX]; + + if (!hwloc_topology_is_thissystem(topology)) { + errno = EINVAL; + return -1; + } + + sprintf(path, "/sys/class/infiniband/%s/device/local_cpus", + ibv_get_device_name(ibdev)); + if (hwloc_linux_read_path_as_cpumask(path, set) < 0 + || hwloc_bitmap_iszero(set)) + hwloc_bitmap_copy(set, hwloc_topology_get_complete_cpuset(topology)); +#else + /* Non-Linux systems simply get a full cpuset */ + hwloc_bitmap_copy(set, hwloc_topology_get_complete_cpuset(topology)); +#endif + return 0; +} + +/** \brief Get the hwloc OS device object corresponding to the OpenFabrics + * device named \p ibname. + * + * Return the OS device object describing the OpenFabrics device + * (InfiniBand, Omni-Path, usNIC, etc) whose name is \p ibname + * (mlx5_0, hfi1_0, usnic_0, qib0, etc). + * Returns NULL if there is none. + * The name \p ibname is usually obtained from ibv_get_device_name(). + * + * The topology \p topology does not necessarily have to match the current + * machine. For instance the topology may be an XML import of a remote host. + * I/O devices detection must be enabled in the topology. + * + * \note The corresponding PCI device object can be obtained by looking + * at the OS device parent object. + */ +static __hwloc_inline hwloc_obj_t +hwloc_ibv_get_device_osdev_by_name(hwloc_topology_t topology, + const char *ibname) +{ + hwloc_obj_t osdev = NULL; + while ((osdev = hwloc_get_next_osdev(topology, osdev)) != NULL) { + if (HWLOC_OBJ_OSDEV_OPENFABRICS == osdev->attr->osdev.type + && osdev->name && !strcmp(ibname, osdev->name)) + return osdev; + } + return NULL; +} + +/** \brief Get the hwloc OS device object corresponding to the OpenFabrics + * device \p ibdev. + * + * Return the OS device object describing the OpenFabrics device \p ibdev + * (InfiniBand, etc). Returns NULL if there is none. + * + * Topology \p topology and device \p ibdev must match the local machine. + * I/O devices detection must be enabled in the topology. + * If not, the locality of the object may still be found using + * hwloc_ibv_get_device_cpuset(). + * + * \note The corresponding PCI device object can be obtained by looking + * at the OS device parent object. + */ +static __hwloc_inline hwloc_obj_t +hwloc_ibv_get_device_osdev(hwloc_topology_t topology, + struct ibv_device *ibdev) +{ + if (!hwloc_topology_is_thissystem(topology)) { + errno = EINVAL; + return NULL; + } + return hwloc_ibv_get_device_osdev_by_name(topology, ibv_get_device_name(ibdev)); +} + +/** @} */ + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + +#endif /* HWLOC_OPENFABRICS_VERBS_H */ diff --git a/src/3rdparty/hwloc/include/hwloc/plugins.h b/src/3rdparty/hwloc/include/hwloc/plugins.h new file mode 100644 index 00000000..cb22000d --- /dev/null +++ b/src/3rdparty/hwloc/include/hwloc/plugins.h @@ -0,0 +1,542 @@ +/* + * Copyright © 2013-2017 Inria. All rights reserved. + * Copyright © 2016 Cisco Systems, Inc. All rights reserved. + * See COPYING in top-level directory. + */ + +#ifndef HWLOC_PLUGINS_H +#define HWLOC_PLUGINS_H + +/** \file + * \brief Public interface for building hwloc plugins. + */ + +struct hwloc_backend; + +#include +#ifdef HWLOC_INSIDE_PLUGIN +/* needed for hwloc_plugin_check_namespace() */ +#include +#endif + + + +/** \defgroup hwlocality_disc_components Components and Plugins: Discovery components + * @{ + */ + +/** \brief Discovery component type */ +typedef enum hwloc_disc_component_type_e { + /** \brief CPU-only discovery through the OS, or generic no-OS support. + * \hideinitializer */ + HWLOC_DISC_COMPONENT_TYPE_CPU = (1<<0), + + /** \brief xml or synthetic, + * platform-specific components such as bgq. + * Anything the discovers CPU and everything else. + * No misc backend is expected to complement a global component. + * \hideinitializer */ + HWLOC_DISC_COMPONENT_TYPE_GLOBAL = (1<<1), + + /** \brief OpenCL, Cuda, etc. + * \hideinitializer */ + HWLOC_DISC_COMPONENT_TYPE_MISC = (1<<2) +} hwloc_disc_component_type_t; + +/** \brief Discovery component structure + * + * This is the major kind of components, taking care of the discovery. + * They are registered by generic components, either statically-built or as plugins. + */ +struct hwloc_disc_component { + /** \brief Discovery component type */ + hwloc_disc_component_type_t type; + + /** \brief Name. + * If this component is built as a plugin, this name does not have to match the plugin filename. + */ + const char *name; + + /** \brief Component types to exclude, as an OR'ed set of ::hwloc_disc_component_type_e. + * + * For a GLOBAL component, this usually includes all other types (~0). + * + * Other components only exclude types that may bring conflicting + * topology information. MISC components should likely not be excluded + * since they usually bring non-primary additional information. + */ + unsigned excludes; + + /** \brief Instantiate callback to create a backend from the component. + * Parameters data1, data2, data3 are NULL except for components + * that have special enabling routines such as hwloc_topology_set_xml(). */ + struct hwloc_backend * (*instantiate)(struct hwloc_disc_component *component, const void *data1, const void *data2, const void *data3); + + /** \brief Component priority. + * Used to sort topology->components, higher priority first. + * Also used to decide between two components with the same name. + * + * Usual values are + * 50 for native OS (or platform) components, + * 45 for x86, + * 40 for no-OS fallback, + * 30 for global components (xml, synthetic), + * 20 for pci, + * 10 for other misc components (opencl etc.). + */ + unsigned priority; + + /** \brief Enabled by default. + * If unset, if will be disabled unless explicitly requested. + */ + unsigned enabled_by_default; + + /** \private Used internally to list components by priority on topology->components + * (the component structure is usually read-only, + * the core copies it before using this field for queueing) + */ + struct hwloc_disc_component * next; +}; + +/** @} */ + + + + +/** \defgroup hwlocality_disc_backends Components and Plugins: Discovery backends + * @{ + */ + +/** \brief Discovery backend structure + * + * A backend is the instantiation of a discovery component. + * When a component gets enabled for a topology, + * its instantiate() callback creates a backend. + * + * hwloc_backend_alloc() initializes all fields to default values + * that the component may change (except "component" and "next") + * before enabling the backend with hwloc_backend_enable(). + */ +struct hwloc_backend { + /** \private Reserved for the core, set by hwloc_backend_alloc() */ + struct hwloc_disc_component * component; + /** \private Reserved for the core, set by hwloc_backend_enable() */ + struct hwloc_topology * topology; + /** \private Reserved for the core. Set to 1 if forced through envvar, 0 otherwise. */ + int envvar_forced; + /** \private Reserved for the core. Used internally to list backends topology->backends. */ + struct hwloc_backend * next; + + /** \brief Backend flags, currently always 0. */ + unsigned long flags; + + /** \brief Backend-specific 'is_thissystem' property. + * Set to 0 or 1 if the backend should enforce the thissystem flag when it gets enabled. + * Set to -1 if the backend doesn't care (default). */ + int is_thissystem; + + /** \brief Backend private data, or NULL if none. */ + void * private_data; + /** \brief Callback for freeing the private_data. + * May be NULL. + */ + void (*disable)(struct hwloc_backend *backend); + + /** \brief Main discovery callback. + * returns -1 on error, either because it couldn't add its objects ot the existing topology, + * or because of an actual discovery/gathering failure. + * May be NULL. + */ + int (*discover)(struct hwloc_backend *backend); + + /** \brief Callback used by the PCI backend to retrieve the locality of a PCI object from the OS/cpu backend. + * May be NULL. */ + int (*get_pci_busid_cpuset)(struct hwloc_backend *backend, struct hwloc_pcidev_attr_s *busid, hwloc_bitmap_t cpuset); +}; + +/** \brief Allocate a backend structure, set good default values, initialize backend->component and topology, etc. + * The caller will then modify whatever needed, and call hwloc_backend_enable(). + */ +HWLOC_DECLSPEC struct hwloc_backend * hwloc_backend_alloc(struct hwloc_disc_component *component); + +/** \brief Enable a previously allocated and setup backend. */ +HWLOC_DECLSPEC int hwloc_backend_enable(struct hwloc_topology *topology, struct hwloc_backend *backend); + +/** @} */ + + + + +/** \defgroup hwlocality_generic_components Components and Plugins: Generic components + * @{ + */ + +/** \brief Generic component type */ +typedef enum hwloc_component_type_e { + /** \brief The data field must point to a struct hwloc_disc_component. */ + HWLOC_COMPONENT_TYPE_DISC, + + /** \brief The data field must point to a struct hwloc_xml_component. */ + HWLOC_COMPONENT_TYPE_XML +} hwloc_component_type_t; + +/** \brief Generic component structure + * + * Generic components structure, either statically listed by configure in static-components.h + * or dynamically loaded as a plugin. + */ +struct hwloc_component { + /** \brief Component ABI version, set to ::HWLOC_COMPONENT_ABI */ + unsigned abi; + + /** \brief Process-wide component initialization callback. + * + * This optional callback is called when the component is registered + * to the hwloc core (after loading the plugin). + * + * When the component is built as a plugin, this callback + * should call hwloc_check_plugin_namespace() + * and return an negative error code on error. + * + * \p flags is always 0 for now. + * + * \return 0 on success, or a negative code on error. + * + * \note If the component uses ltdl for loading its own plugins, + * it should load/unload them only in init() and finalize(), + * to avoid race conditions with hwloc's use of ltdl. + */ + int (*init)(unsigned long flags); + + /** \brief Process-wide component termination callback. + * + * This optional callback is called after unregistering the component + * from the hwloc core (before unloading the plugin). + * + * \p flags is always 0 for now. + * + * \note If the component uses ltdl for loading its own plugins, + * it should load/unload them only in init() and finalize(), + * to avoid race conditions with hwloc's use of ltdl. + */ + void (*finalize)(unsigned long flags); + + /** \brief Component type */ + hwloc_component_type_t type; + + /** \brief Component flags, unused for now */ + unsigned long flags; + + /** \brief Component data, pointing to a struct hwloc_disc_component or struct hwloc_xml_component. */ + void * data; +}; + +/** @} */ + + + + +/** \defgroup hwlocality_components_core_funcs Components and Plugins: Core functions to be used by components + * @{ + */ + +/** \brief Add an object to the topology. + * + * It is sorted along the tree of other objects according to the inclusion of + * cpusets, to eventually be added as a child of the smallest object including + * this object. + * + * If the cpuset is empty, the type of the object (and maybe some attributes) + * must be enough to find where to insert the object. This is especially true + * for NUMA nodes with memory and no CPUs. + * + * The given object should not have children. + * + * This shall only be called before levels are built. + * + * In case of error, hwloc_report_os_error() is called. + * + * The caller should check whether the object type is filtered-out before calling this function. + * + * The topology cpuset/nodesets will be enlarged to include the object sets. + * + * Returns the object on success. + * Returns NULL and frees obj on error. + * Returns another object and frees obj if it was merged with an identical pre-existing object. + */ +HWLOC_DECLSPEC struct hwloc_obj *hwloc_insert_object_by_cpuset(struct hwloc_topology *topology, hwloc_obj_t obj); + +/** \brief Type of error callbacks during object insertion */ +typedef void (*hwloc_report_error_t)(const char * msg, int line); +/** \brief Report an insertion error from a backend */ +HWLOC_DECLSPEC void hwloc_report_os_error(const char * msg, int line); +/** \brief Check whether insertion errors are hidden */ +HWLOC_DECLSPEC int hwloc_hide_errors(void); + +/** \brief Add an object to the topology and specify which error callback to use. + * + * This function is similar to hwloc_insert_object_by_cpuset() but it allows specifying + * where to start insertion from (if \p root is NULL, the topology root object is used), + * and specifying the error callback. + */ +HWLOC_DECLSPEC struct hwloc_obj *hwloc__insert_object_by_cpuset(struct hwloc_topology *topology, hwloc_obj_t root, hwloc_obj_t obj, hwloc_report_error_t report_error); + +/** \brief Insert an object somewhere in the topology. + * + * It is added as the last child of the given parent. + * The cpuset is completely ignored, so strange objects such as I/O devices should + * preferably be inserted with this. + * + * When used for "normal" children with cpusets (when importing from XML + * when duplicating a topology), the caller should make sure that: + * - children are inserted in order, + * - children cpusets do not intersect. + * + * The given object may have normal, I/O or Misc children, as long as they are in order as well. + * These children must have valid parent and next_sibling pointers. + * + * The caller should check whether the object type is filtered-out before calling this function. + */ +HWLOC_DECLSPEC void hwloc_insert_object_by_parent(struct hwloc_topology *topology, hwloc_obj_t parent, hwloc_obj_t obj); + +/** \brief Allocate and initialize an object of the given type and physical index. + * + * If \p os_index is unknown or irrelevant, use \c HWLOC_UNKNOWN_INDEX. + */ +HWLOC_DECLSPEC hwloc_obj_t hwloc_alloc_setup_object(hwloc_topology_t topology, hwloc_obj_type_t type, unsigned os_index); + +/** \brief Setup object cpusets/nodesets by OR'ing its children. + * + * Used when adding an object late in the topology. + * Will update the new object by OR'ing all its new children sets. + * + * Used when PCI backend adds a hostbridge parent, when distances + * add a new Group, etc. + */ +HWLOC_DECLSPEC int hwloc_obj_add_children_sets(hwloc_obj_t obj); + +/** \brief Request a reconnection of children and levels in the topology. + * + * May be used by backends during discovery if they need arrays or lists + * of object within levels or children to be fully connected. + * + * \p flags is currently unused, must 0. + */ +HWLOC_DECLSPEC int hwloc_topology_reconnect(hwloc_topology_t topology, unsigned long flags __hwloc_attribute_unused); + +/** \brief Make sure that plugins can lookup core symbols. + * + * This is a sanity check to avoid lazy-lookup failures when libhwloc + * is loaded within a plugin, and later tries to load its own plugins. + * This may fail (and abort the program) if libhwloc symbols are in a + * private namespace. + * + * \return 0 on success. + * \return -1 if the plugin cannot be successfully loaded. The caller + * plugin init() callback should return a negative error code as well. + * + * Plugins should call this function in their init() callback to avoid + * later crashes if lazy symbol resolution is used by the upper layer that + * loaded hwloc (e.g. OpenCL implementations using dlopen with RTLD_LAZY). + * + * \note The build system must define HWLOC_INSIDE_PLUGIN if and only if + * building the caller as a plugin. + * + * \note This function should remain inline so plugins can call it even + * when they cannot find libhwloc symbols. + */ +static __hwloc_inline int +hwloc_plugin_check_namespace(const char *pluginname __hwloc_attribute_unused, const char *symbol __hwloc_attribute_unused) +{ +#ifdef HWLOC_INSIDE_PLUGIN + lt_dlhandle handle; + void *sym; + handle = lt_dlopen(NULL); + if (!handle) + /* cannot check, assume things will work */ + return 0; + sym = lt_dlsym(handle, symbol); + lt_dlclose(handle); + if (!sym) { + static int verboseenv_checked = 0; + static int verboseenv_value = 0; + if (!verboseenv_checked) { + const char *verboseenv = getenv("HWLOC_PLUGINS_VERBOSE"); + verboseenv_value = verboseenv ? atoi(verboseenv) : 0; + verboseenv_checked = 1; + } + if (verboseenv_value) + fprintf(stderr, "Plugin `%s' disabling itself because it cannot find the `%s' core symbol.\n", + pluginname, symbol); + return -1; + } +#endif /* HWLOC_INSIDE_PLUGIN */ + return 0; +} + +/** @} */ + + + + +/** \defgroup hwlocality_components_filtering Components and Plugins: Filtering objects + * @{ + */ + +/** \brief Check whether the given PCI device classid is important. + * + * \return 1 if important, 0 otherwise. + */ +static __hwloc_inline int +hwloc_filter_check_pcidev_subtype_important(unsigned classid) +{ + unsigned baseclass = classid >> 8; + return (baseclass == 0x03 /* PCI_BASE_CLASS_DISPLAY */ + || baseclass == 0x02 /* PCI_BASE_CLASS_NETWORK */ + || baseclass == 0x01 /* PCI_BASE_CLASS_STORAGE */ + || baseclass == 0x0b /* PCI_BASE_CLASS_PROCESSOR */ + || classid == 0x0c04 /* PCI_CLASS_SERIAL_FIBER */ + || classid == 0x0c06 /* PCI_CLASS_SERIAL_INFINIBAND */ + || baseclass == 0x12 /* Processing Accelerators */); +} + +/** \brief Check whether the given OS device subtype is important. + * + * \return 1 if important, 0 otherwise. + */ +static __hwloc_inline int +hwloc_filter_check_osdev_subtype_important(hwloc_obj_osdev_type_t subtype) +{ + return (subtype != HWLOC_OBJ_OSDEV_DMA); +} + +/** \brief Check whether a non-I/O object type should be filtered-out. + * + * Cannot be used for I/O objects. + * + * \return 1 if the object type should be kept, 0 otherwise. + */ +static __hwloc_inline int +hwloc_filter_check_keep_object_type(hwloc_topology_t topology, hwloc_obj_type_t type) +{ + enum hwloc_type_filter_e filter = HWLOC_TYPE_FILTER_KEEP_NONE; + hwloc_topology_get_type_filter(topology, type, &filter); + assert(filter != HWLOC_TYPE_FILTER_KEEP_IMPORTANT); /* IMPORTANT only used for I/O */ + return filter == HWLOC_TYPE_FILTER_KEEP_NONE ? 0 : 1; +} + +/** \brief Check whether the given object should be filtered-out. + * + * \return 1 if the object type should be kept, 0 otherwise. + */ +static __hwloc_inline int +hwloc_filter_check_keep_object(hwloc_topology_t topology, hwloc_obj_t obj) +{ + hwloc_obj_type_t type = obj->type; + enum hwloc_type_filter_e filter = HWLOC_TYPE_FILTER_KEEP_NONE; + hwloc_topology_get_type_filter(topology, type, &filter); + if (filter == HWLOC_TYPE_FILTER_KEEP_NONE) + return 0; + if (filter == HWLOC_TYPE_FILTER_KEEP_IMPORTANT) { + if (type == HWLOC_OBJ_PCI_DEVICE) + return hwloc_filter_check_pcidev_subtype_important(obj->attr->pcidev.class_id); + if (type == HWLOC_OBJ_OS_DEVICE) + return hwloc_filter_check_osdev_subtype_important(obj->attr->osdev.type); + } + return 1; +} + +/** @} */ + + + + +/** \defgroup hwlocality_components_pcidisc Components and Plugins: helpers for PCI discovery + * @{ + */ + +/** \brief Return the offset of the given capability in the PCI config space buffer + * + * This function requires a 256-bytes config space. Unknown/unavailable bytes should be set to 0xff. + */ +HWLOC_DECLSPEC unsigned hwloc_pcidisc_find_cap(const unsigned char *config, unsigned cap); + +/** \brief Fill linkspeed by reading the PCI config space where PCI_CAP_ID_EXP is at position offset. + * + * Needs 20 bytes of EXP capability block starting at offset in the config space + * for registers up to link status. + */ +HWLOC_DECLSPEC int hwloc_pcidisc_find_linkspeed(const unsigned char *config, unsigned offset, float *linkspeed); + +/** \brief Return the hwloc object type (PCI device or Bridge) for the given class and configuration space. + * + * This function requires 16 bytes of common configuration header at the beginning of config. + */ +HWLOC_DECLSPEC hwloc_obj_type_t hwloc_pcidisc_check_bridge_type(unsigned device_class, const unsigned char *config); + +/** \brief Fills the attributes of the given PCI bridge using the given PCI config space. + * + * This function requires 32 bytes of common configuration header at the beginning of config. + * + * Returns -1 and destroys /p obj if bridge fields are invalid. + */ +HWLOC_DECLSPEC int hwloc_pcidisc_setup_bridge_attr(hwloc_obj_t obj, const unsigned char *config); + +/** \brief Insert a PCI object in the given PCI tree by looking at PCI bus IDs. + * + * If \p treep points to \c NULL, the new object is inserted there. + */ +HWLOC_DECLSPEC void hwloc_pcidisc_tree_insert_by_busid(struct hwloc_obj **treep, struct hwloc_obj *obj); + +/** \brief Add some hostbridges on top of the given tree of PCI objects and attach them to the topology. + * + * For now, they will be attached to the root object. The core will move them to their actual PCI + * locality using hwloc_pci_belowroot_apply_locality() at the end of the discovery. + * + * In the meantime, other backends lookup PCI objects or localities (for instance to attach OS devices) + * by using hwloc_pcidisc_find_by_busid() or hwloc_pcidisc_find_busid_parent(). + */ +HWLOC_DECLSPEC int hwloc_pcidisc_tree_attach(struct hwloc_topology *topology, struct hwloc_obj *tree); + +/** @} */ + + + + +/** \defgroup hwlocality_components_pcifind Components and Plugins: finding PCI objects during other discoveries + * @{ + */ + +/** \brief Find the PCI object that matches the bus ID. + * + * To be used after a PCI backend added PCI devices with hwloc_pcidisc_tree_attach() + * and before the core moves them to their actual location with hwloc_pci_belowroot_apply_locality(). + * + * If no exactly matching object is found, return the container bridge if any, or NULL. + * + * On failure, it may be possible to find the PCI locality (instead of the PCI device) + * by calling hwloc_pcidisc_find_busid_parent(). + * + * \note This is semantically identical to hwloc_get_pcidev_by_busid() which only works + * after the topology is fully loaded. + */ +HWLOC_DECLSPEC struct hwloc_obj * hwloc_pcidisc_find_by_busid(struct hwloc_topology *topology, unsigned domain, unsigned bus, unsigned dev, unsigned func); + +/** \brief Find the normal parent of a PCI bus ID. + * + * Look at PCI affinity to find out where the given PCI bus ID should be attached. + * + * This function should be used to attach an I/O device directly under a normal + * (non-I/O) object, instead of below a PCI object. + * It is usually used by backends when hwloc_pcidisc_find_by_busid() failed + * to find the hwloc object corresponding to this bus ID, for instance because + * PCI discovery is not supported on this platform. + */ +HWLOC_DECLSPEC struct hwloc_obj * hwloc_pcidisc_find_busid_parent(struct hwloc_topology *topology, unsigned domain, unsigned bus, unsigned dev, unsigned func); + +/** @} */ + + + + +#endif /* HWLOC_PLUGINS_H */ diff --git a/src/3rdparty/hwloc/include/hwloc/rename.h b/src/3rdparty/hwloc/include/hwloc/rename.h new file mode 100644 index 00000000..7cef1b2e --- /dev/null +++ b/src/3rdparty/hwloc/include/hwloc/rename.h @@ -0,0 +1,765 @@ +/* + * Copyright © 2009-2011 Cisco Systems, Inc. All rights reserved. + * Copyright © 2010-2018 Inria. All rights reserved. + * See COPYING in top-level directory. + */ + +#ifndef HWLOC_RENAME_H +#define HWLOC_RENAME_H + +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Only enact these defines if we're actually renaming the symbols + (i.e., avoid trying to have no-op defines if we're *not* + renaming). */ + +#if HWLOC_SYM_TRANSFORM + +/* Use a preprocessor two-step in order to get the prefixing right. + Make 2 macros: HWLOC_NAME and HWLOC_NAME_CAPS for renaming + things. */ + +#define HWLOC_MUNGE_NAME(a, b) HWLOC_MUNGE_NAME2(a, b) +#define HWLOC_MUNGE_NAME2(a, b) a ## b +#define HWLOC_NAME(name) HWLOC_MUNGE_NAME(HWLOC_SYM_PREFIX, hwloc_ ## name) +#define HWLOC_NAME_CAPS(name) HWLOC_MUNGE_NAME(HWLOC_SYM_PREFIX_CAPS, hwloc_ ## name) + +/* Now define all the "real" names to be the prefixed names. This + allows us to use the real names throughout the code base (i.e., + "hwloc_"); the preprocessor will adjust to have the prefixed + name under the covers. */ + +/* Names from hwloc.h */ + +#define hwloc_get_api_version HWLOC_NAME(get_api_version) + +#define hwloc_topology HWLOC_NAME(topology) +#define hwloc_topology_t HWLOC_NAME(topology_t) + +#define hwloc_cpuset_t HWLOC_NAME(cpuset_t) +#define hwloc_const_cpuset_t HWLOC_NAME(const_cpuset_t) +#define hwloc_nodeset_t HWLOC_NAME(nodeset_t) +#define hwloc_const_nodeset_t HWLOC_NAME(const_nodeset_t) + +#define HWLOC_OBJ_MACHINE HWLOC_NAME_CAPS(OBJ_MACHINE) +#define HWLOC_OBJ_NUMANODE HWLOC_NAME_CAPS(OBJ_NUMANODE) +#define HWLOC_OBJ_PACKAGE HWLOC_NAME_CAPS(OBJ_PACKAGE) +#define HWLOC_OBJ_CORE HWLOC_NAME_CAPS(OBJ_CORE) +#define HWLOC_OBJ_PU HWLOC_NAME_CAPS(OBJ_PU) +#define HWLOC_OBJ_L1CACHE HWLOC_NAME_CAPS(OBJ_L1CACHE) +#define HWLOC_OBJ_L2CACHE HWLOC_NAME_CAPS(OBJ_L2CACHE) +#define HWLOC_OBJ_L3CACHE HWLOC_NAME_CAPS(OBJ_L3CACHE) +#define HWLOC_OBJ_L4CACHE HWLOC_NAME_CAPS(OBJ_L4CACHE) +#define HWLOC_OBJ_L5CACHE HWLOC_NAME_CAPS(OBJ_L5CACHE) +#define HWLOC_OBJ_L1ICACHE HWLOC_NAME_CAPS(OBJ_L1ICACHE) +#define HWLOC_OBJ_L2ICACHE HWLOC_NAME_CAPS(OBJ_L2ICACHE) +#define HWLOC_OBJ_L3ICACHE HWLOC_NAME_CAPS(OBJ_L3ICACHE) +#define HWLOC_OBJ_MISC HWLOC_NAME_CAPS(OBJ_MISC) +#define HWLOC_OBJ_GROUP HWLOC_NAME_CAPS(OBJ_GROUP) +#define HWLOC_OBJ_BRIDGE HWLOC_NAME_CAPS(OBJ_BRIDGE) +#define HWLOC_OBJ_PCI_DEVICE HWLOC_NAME_CAPS(OBJ_PCI_DEVICE) +#define HWLOC_OBJ_OS_DEVICE HWLOC_NAME_CAPS(OBJ_OS_DEVICE) +#define HWLOC_OBJ_TYPE_MAX HWLOC_NAME_CAPS(OBJ_TYPE_MAX) +#define hwloc_obj_type_t HWLOC_NAME(obj_type_t) + +#define hwloc_obj_cache_type_e HWLOC_NAME(obj_cache_type_e) +#define hwloc_obj_cache_type_t HWLOC_NAME(obj_cache_type_t) +#define HWLOC_OBJ_CACHE_UNIFIED HWLOC_NAME_CAPS(OBJ_CACHE_UNIFIED) +#define HWLOC_OBJ_CACHE_DATA HWLOC_NAME_CAPS(OBJ_CACHE_DATA) +#define HWLOC_OBJ_CACHE_INSTRUCTION HWLOC_NAME_CAPS(OBJ_CACHE_INSTRUCTION) + +#define hwloc_obj_bridge_type_e HWLOC_NAME(obj_bridge_type_e) +#define hwloc_obj_bridge_type_t HWLOC_NAME(obj_bridge_type_t) +#define HWLOC_OBJ_BRIDGE_HOST HWLOC_NAME_CAPS(OBJ_BRIDGE_HOST) +#define HWLOC_OBJ_BRIDGE_PCI HWLOC_NAME_CAPS(OBJ_BRIDGE_PCI) + +#define hwloc_obj_osdev_type_e HWLOC_NAME(obj_osdev_type_e) +#define hwloc_obj_osdev_type_t HWLOC_NAME(obj_osdev_type_t) +#define HWLOC_OBJ_OSDEV_BLOCK HWLOC_NAME_CAPS(OBJ_OSDEV_BLOCK) +#define HWLOC_OBJ_OSDEV_GPU HWLOC_NAME_CAPS(OBJ_OSDEV_GPU) +#define HWLOC_OBJ_OSDEV_NETWORK HWLOC_NAME_CAPS(OBJ_OSDEV_NETWORK) +#define HWLOC_OBJ_OSDEV_OPENFABRICS HWLOC_NAME_CAPS(OBJ_OSDEV_OPENFABRICS) +#define HWLOC_OBJ_OSDEV_DMA HWLOC_NAME_CAPS(OBJ_OSDEV_DMA) +#define HWLOC_OBJ_OSDEV_COPROC HWLOC_NAME_CAPS(OBJ_OSDEV_COPROC) + +#define hwloc_compare_types HWLOC_NAME(compare_types) + +#define hwloc_compare_types_e HWLOC_NAME(compare_types_e) +#define HWLOC_TYPE_UNORDERED HWLOC_NAME_CAPS(TYPE_UNORDERED) + +#define hwloc_obj HWLOC_NAME(obj) +#define hwloc_obj_t HWLOC_NAME(obj_t) + +#define hwloc_info_s HWLOC_NAME(info_s) + +#define hwloc_obj_attr_u HWLOC_NAME(obj_attr_u) +#define hwloc_numanode_attr_s HWLOC_NAME(numanode_attr_s) +#define hwloc_memory_page_type_s HWLOC_NAME(memory_page_type_s) +#define hwloc_cache_attr_s HWLOC_NAME(cache_attr_s) +#define hwloc_group_attr_s HWLOC_NAME(group_attr_s) +#define hwloc_pcidev_attr_s HWLOC_NAME(pcidev_attr_s) +#define hwloc_bridge_attr_s HWLOC_NAME(bridge_attr_s) +#define hwloc_osdev_attr_s HWLOC_NAME(osdev_attr_s) + +#define hwloc_topology_init HWLOC_NAME(topology_init) +#define hwloc_topology_load HWLOC_NAME(topology_load) +#define hwloc_topology_destroy HWLOC_NAME(topology_destroy) +#define hwloc_topology_dup HWLOC_NAME(topology_dup) +#define hwloc_topology_abi_check HWLOC_NAME(topology_abi_check) +#define hwloc_topology_check HWLOC_NAME(topology_check) + +#define hwloc_topology_flags_e HWLOC_NAME(topology_flags_e) + +#define HWLOC_TOPOLOGY_FLAG_WHOLE_SYSTEM HWLOC_NAME_CAPS(TOPOLOGY_FLAG_WHOLE_SYSTEM) +#define HWLOC_TOPOLOGY_FLAG_IS_THISSYSTEM HWLOC_NAME_CAPS(TOPOLOGY_FLAG_IS_THISSYSTEM) +#define HWLOC_TOPOLOGY_FLAG_THISSYSTEM_ALLOWED_RESOURCES HWLOC_NAME_CAPS(TOPOLOGY_FLAG_THISSYSTEM_ALLOWED_RESOURCES) + +#define hwloc_topology_set_pid HWLOC_NAME(topology_set_pid) +#define hwloc_topology_set_synthetic HWLOC_NAME(topology_set_synthetic) +#define hwloc_topology_set_xml HWLOC_NAME(topology_set_xml) +#define hwloc_topology_set_xmlbuffer HWLOC_NAME(topology_set_xmlbuffer) + +#define hwloc_topology_set_flags HWLOC_NAME(topology_set_flags) +#define hwloc_topology_is_thissystem HWLOC_NAME(topology_is_thissystem) +#define hwloc_topology_get_flags HWLOC_NAME(topology_get_flags) +#define hwloc_topology_discovery_support HWLOC_NAME(topology_discovery_support) +#define hwloc_topology_cpubind_support HWLOC_NAME(topology_cpubind_support) +#define hwloc_topology_membind_support HWLOC_NAME(topology_membind_support) +#define hwloc_topology_support HWLOC_NAME(topology_support) +#define hwloc_topology_get_support HWLOC_NAME(topology_get_support) + +#define hwloc_type_filter_e HWLOC_NAME(type_filter_e) +#define HWLOC_TYPE_FILTER_KEEP_ALL HWLOC_NAME_CAPS(TYPE_FILTER_KEEP_ALL) +#define HWLOC_TYPE_FILTER_KEEP_NONE HWLOC_NAME_CAPS(TYPE_FILTER_KEEP_NONE) +#define HWLOC_TYPE_FILTER_KEEP_STRUCTURE HWLOC_NAME_CAPS(TYPE_FILTER_KEEP_STRUCTURE) +#define HWLOC_TYPE_FILTER_KEEP_IMPORTANT HWLOC_NAME_CAPS(TYPE_FILTER_KEEP_IMPORTANT) +#define hwloc_topology_set_type_filter HWLOC_NAME(topology_set_type_filter) +#define hwloc_topology_get_type_filter HWLOC_NAME(topology_get_type_filter) +#define hwloc_topology_set_all_types_filter HWLOC_NAME(topology_set_all_types_filter) +#define hwloc_topology_set_cache_types_filter HWLOC_NAME(topology_set_cache_types_filter) +#define hwloc_topology_set_icache_types_filter HWLOC_NAME(topology_set_icache_types_filter) +#define hwloc_topology_set_io_types_filter HWLOC_NAME(topology_set_io_types_filter) + +#define hwloc_topology_set_userdata HWLOC_NAME(topology_set_userdata) +#define hwloc_topology_get_userdata HWLOC_NAME(topology_get_userdata) + +#define hwloc_restrict_flags_e HWLOC_NAME(restrict_flags_e) +#define HWLOC_RESTRICT_FLAG_REMOVE_CPULESS HWLOC_NAME_CAPS(RESTRICT_FLAG_REMOVE_CPULESS) +#define HWLOC_RESTRICT_FLAG_ADAPT_MISC HWLOC_NAME_CAPS(RESTRICT_FLAG_ADAPT_MISC) +#define HWLOC_RESTRICT_FLAG_ADAPT_IO HWLOC_NAME_CAPS(RESTRICT_FLAG_ADAPT_IO) +#define hwloc_topology_restrict HWLOC_NAME(topology_restrict) + +#define hwloc_topology_insert_misc_object HWLOC_NAME(topology_insert_misc_object) +#define hwloc_topology_alloc_group_object HWLOC_NAME(topology_alloc_group_object) +#define hwloc_topology_insert_group_object HWLOC_NAME(topology_insert_group_object) +#define hwloc_obj_add_other_obj_sets HWLOC_NAME(obj_add_other_obj_sets) + +#define hwloc_topology_get_depth HWLOC_NAME(topology_get_depth) +#define hwloc_get_type_depth HWLOC_NAME(get_type_depth) +#define hwloc_get_memory_parents_depth HWLOC_NAME(get_memory_parents_depth) + +#define hwloc_get_type_depth_e HWLOC_NAME(get_type_depth_e) +#define HWLOC_TYPE_DEPTH_UNKNOWN HWLOC_NAME_CAPS(TYPE_DEPTH_UNKNOWN) +#define HWLOC_TYPE_DEPTH_MULTIPLE HWLOC_NAME_CAPS(TYPE_DEPTH_MULTIPLE) +#define HWLOC_TYPE_DEPTH_BRIDGE HWLOC_NAME_CAPS(TYPE_DEPTH_BRIDGE) +#define HWLOC_TYPE_DEPTH_PCI_DEVICE HWLOC_NAME_CAPS(TYPE_DEPTH_PCI_DEVICE) +#define HWLOC_TYPE_DEPTH_OS_DEVICE HWLOC_NAME_CAPS(TYPE_DEPTH_OS_DEVICE) +#define HWLOC_TYPE_DEPTH_MISC HWLOC_NAME_CAPS(TYPE_DEPTH_MISC) +#define HWLOC_TYPE_DEPTH_NUMANODE HWLOC_NAME_CAPS(TYPE_DEPTH_NUMANODE) + +#define hwloc_get_depth_type HWLOC_NAME(get_depth_type) +#define hwloc_get_nbobjs_by_depth HWLOC_NAME(get_nbobjs_by_depth) +#define hwloc_get_nbobjs_by_type HWLOC_NAME(get_nbobjs_by_type) + +#define hwloc_get_obj_by_depth HWLOC_NAME(get_obj_by_depth ) +#define hwloc_get_obj_by_type HWLOC_NAME(get_obj_by_type ) + +#define hwloc_obj_type_string HWLOC_NAME(obj_type_string ) +#define hwloc_obj_type_snprintf HWLOC_NAME(obj_type_snprintf ) +#define hwloc_obj_attr_snprintf HWLOC_NAME(obj_attr_snprintf ) +#define hwloc_type_sscanf HWLOC_NAME(type_sscanf) +#define hwloc_type_sscanf_as_depth HWLOC_NAME(type_sscanf_as_depth) + +#define hwloc_obj_get_info_by_name HWLOC_NAME(obj_get_info_by_name) +#define hwloc_obj_add_info HWLOC_NAME(obj_add_info) + +#define HWLOC_CPUBIND_PROCESS HWLOC_NAME_CAPS(CPUBIND_PROCESS) +#define HWLOC_CPUBIND_THREAD HWLOC_NAME_CAPS(CPUBIND_THREAD) +#define HWLOC_CPUBIND_STRICT HWLOC_NAME_CAPS(CPUBIND_STRICT) +#define HWLOC_CPUBIND_NOMEMBIND HWLOC_NAME_CAPS(CPUBIND_NOMEMBIND) + +#define hwloc_cpubind_flags_t HWLOC_NAME(cpubind_flags_t) + +#define hwloc_set_cpubind HWLOC_NAME(set_cpubind) +#define hwloc_get_cpubind HWLOC_NAME(get_cpubind) +#define hwloc_set_proc_cpubind HWLOC_NAME(set_proc_cpubind) +#define hwloc_get_proc_cpubind HWLOC_NAME(get_proc_cpubind) +#define hwloc_set_thread_cpubind HWLOC_NAME(set_thread_cpubind) +#define hwloc_get_thread_cpubind HWLOC_NAME(get_thread_cpubind) + +#define hwloc_get_last_cpu_location HWLOC_NAME(get_last_cpu_location) +#define hwloc_get_proc_last_cpu_location HWLOC_NAME(get_proc_last_cpu_location) + +#define HWLOC_MEMBIND_DEFAULT HWLOC_NAME_CAPS(MEMBIND_DEFAULT) +#define HWLOC_MEMBIND_FIRSTTOUCH HWLOC_NAME_CAPS(MEMBIND_FIRSTTOUCH) +#define HWLOC_MEMBIND_BIND HWLOC_NAME_CAPS(MEMBIND_BIND) +#define HWLOC_MEMBIND_INTERLEAVE HWLOC_NAME_CAPS(MEMBIND_INTERLEAVE) +#define HWLOC_MEMBIND_NEXTTOUCH HWLOC_NAME_CAPS(MEMBIND_NEXTTOUCH) +#define HWLOC_MEMBIND_MIXED HWLOC_NAME_CAPS(MEMBIND_MIXED) + +#define hwloc_membind_policy_t HWLOC_NAME(membind_policy_t) + +#define HWLOC_MEMBIND_PROCESS HWLOC_NAME_CAPS(MEMBIND_PROCESS) +#define HWLOC_MEMBIND_THREAD HWLOC_NAME_CAPS(MEMBIND_THREAD) +#define HWLOC_MEMBIND_STRICT HWLOC_NAME_CAPS(MEMBIND_STRICT) +#define HWLOC_MEMBIND_MIGRATE HWLOC_NAME_CAPS(MEMBIND_MIGRATE) +#define HWLOC_MEMBIND_NOCPUBIND HWLOC_NAME_CAPS(MEMBIND_NOCPUBIND) +#define HWLOC_MEMBIND_BYNODESET HWLOC_NAME_CAPS(MEMBIND_BYNODESET) + +#define hwloc_membind_flags_t HWLOC_NAME(membind_flags_t) + +#define hwloc_set_membind HWLOC_NAME(set_membind) +#define hwloc_get_membind HWLOC_NAME(get_membind) +#define hwloc_set_proc_membind HWLOC_NAME(set_proc_membind) +#define hwloc_get_proc_membind HWLOC_NAME(get_proc_membind) +#define hwloc_set_area_membind HWLOC_NAME(set_area_membind) +#define hwloc_get_area_membind HWLOC_NAME(get_area_membind) +#define hwloc_get_area_memlocation HWLOC_NAME(get_area_memlocation) +#define hwloc_alloc_membind HWLOC_NAME(alloc_membind) +#define hwloc_alloc HWLOC_NAME(alloc) +#define hwloc_free HWLOC_NAME(free) + +#define hwloc_get_non_io_ancestor_obj HWLOC_NAME(get_non_io_ancestor_obj) +#define hwloc_get_next_pcidev HWLOC_NAME(get_next_pcidev) +#define hwloc_get_pcidev_by_busid HWLOC_NAME(get_pcidev_by_busid) +#define hwloc_get_pcidev_by_busidstring HWLOC_NAME(get_pcidev_by_busidstring) +#define hwloc_get_next_osdev HWLOC_NAME(get_next_osdev) +#define hwloc_get_next_bridge HWLOC_NAME(get_next_bridge) +#define hwloc_bridge_covers_pcibus HWLOC_NAME(bridge_covers_pcibus) + +/* hwloc/bitmap.h */ + +#define hwloc_bitmap_s HWLOC_NAME(bitmap_s) +#define hwloc_bitmap_t HWLOC_NAME(bitmap_t) +#define hwloc_const_bitmap_t HWLOC_NAME(const_bitmap_t) + +#define hwloc_bitmap_alloc HWLOC_NAME(bitmap_alloc) +#define hwloc_bitmap_alloc_full HWLOC_NAME(bitmap_alloc_full) +#define hwloc_bitmap_free HWLOC_NAME(bitmap_free) +#define hwloc_bitmap_dup HWLOC_NAME(bitmap_dup) +#define hwloc_bitmap_copy HWLOC_NAME(bitmap_copy) +#define hwloc_bitmap_snprintf HWLOC_NAME(bitmap_snprintf) +#define hwloc_bitmap_asprintf HWLOC_NAME(bitmap_asprintf) +#define hwloc_bitmap_sscanf HWLOC_NAME(bitmap_sscanf) +#define hwloc_bitmap_list_snprintf HWLOC_NAME(bitmap_list_snprintf) +#define hwloc_bitmap_list_asprintf HWLOC_NAME(bitmap_list_asprintf) +#define hwloc_bitmap_list_sscanf HWLOC_NAME(bitmap_list_sscanf) +#define hwloc_bitmap_taskset_snprintf HWLOC_NAME(bitmap_taskset_snprintf) +#define hwloc_bitmap_taskset_asprintf HWLOC_NAME(bitmap_taskset_asprintf) +#define hwloc_bitmap_taskset_sscanf HWLOC_NAME(bitmap_taskset_sscanf) +#define hwloc_bitmap_zero HWLOC_NAME(bitmap_zero) +#define hwloc_bitmap_fill HWLOC_NAME(bitmap_fill) +#define hwloc_bitmap_from_ulong HWLOC_NAME(bitmap_from_ulong) + +#define hwloc_bitmap_from_ith_ulong HWLOC_NAME(bitmap_from_ith_ulong) +#define hwloc_bitmap_to_ulong HWLOC_NAME(bitmap_to_ulong) +#define hwloc_bitmap_to_ith_ulong HWLOC_NAME(bitmap_to_ith_ulong) +#define hwloc_bitmap_only HWLOC_NAME(bitmap_only) +#define hwloc_bitmap_allbut HWLOC_NAME(bitmap_allbut) +#define hwloc_bitmap_set HWLOC_NAME(bitmap_set) +#define hwloc_bitmap_set_range HWLOC_NAME(bitmap_set_range) +#define hwloc_bitmap_set_ith_ulong HWLOC_NAME(bitmap_set_ith_ulong) +#define hwloc_bitmap_clr HWLOC_NAME(bitmap_clr) +#define hwloc_bitmap_clr_range HWLOC_NAME(bitmap_clr_range) +#define hwloc_bitmap_isset HWLOC_NAME(bitmap_isset) +#define hwloc_bitmap_iszero HWLOC_NAME(bitmap_iszero) +#define hwloc_bitmap_isfull HWLOC_NAME(bitmap_isfull) +#define hwloc_bitmap_isequal HWLOC_NAME(bitmap_isequal) +#define hwloc_bitmap_intersects HWLOC_NAME(bitmap_intersects) +#define hwloc_bitmap_isincluded HWLOC_NAME(bitmap_isincluded) +#define hwloc_bitmap_or HWLOC_NAME(bitmap_or) +#define hwloc_bitmap_and HWLOC_NAME(bitmap_and) +#define hwloc_bitmap_andnot HWLOC_NAME(bitmap_andnot) +#define hwloc_bitmap_xor HWLOC_NAME(bitmap_xor) +#define hwloc_bitmap_not HWLOC_NAME(bitmap_not) +#define hwloc_bitmap_first HWLOC_NAME(bitmap_first) +#define hwloc_bitmap_last HWLOC_NAME(bitmap_last) +#define hwloc_bitmap_next HWLOC_NAME(bitmap_next) +#define hwloc_bitmap_first_unset HWLOC_NAME(bitmap_first_unset) +#define hwloc_bitmap_last_unset HWLOC_NAME(bitmap_last_unset) +#define hwloc_bitmap_next_unset HWLOC_NAME(bitmap_next_unset) +#define hwloc_bitmap_singlify HWLOC_NAME(bitmap_singlify) +#define hwloc_bitmap_compare_first HWLOC_NAME(bitmap_compare_first) +#define hwloc_bitmap_compare HWLOC_NAME(bitmap_compare) +#define hwloc_bitmap_weight HWLOC_NAME(bitmap_weight) + +/* hwloc/helper.h */ + +#define hwloc_get_type_or_below_depth HWLOC_NAME(get_type_or_below_depth) +#define hwloc_get_type_or_above_depth HWLOC_NAME(get_type_or_above_depth) +#define hwloc_get_root_obj HWLOC_NAME(get_root_obj) +#define hwloc_get_ancestor_obj_by_depth HWLOC_NAME(get_ancestor_obj_by_depth) +#define hwloc_get_ancestor_obj_by_type HWLOC_NAME(get_ancestor_obj_by_type) +#define hwloc_get_next_obj_by_depth HWLOC_NAME(get_next_obj_by_depth) +#define hwloc_get_next_obj_by_type HWLOC_NAME(get_next_obj_by_type) +#define hwloc_get_pu_obj_by_os_index HWLOC_NAME(get_pu_obj_by_os_index) +#define hwloc_get_numanode_obj_by_os_index HWLOC_NAME(get_numanode_obj_by_os_index) +#define hwloc_get_next_child HWLOC_NAME(get_next_child) +#define hwloc_get_common_ancestor_obj HWLOC_NAME(get_common_ancestor_obj) +#define hwloc_obj_is_in_subtree HWLOC_NAME(obj_is_in_subtree) +#define hwloc_get_first_largest_obj_inside_cpuset HWLOC_NAME(get_first_largest_obj_inside_cpuset) +#define hwloc_get_largest_objs_inside_cpuset HWLOC_NAME(get_largest_objs_inside_cpuset) +#define hwloc_get_next_obj_inside_cpuset_by_depth HWLOC_NAME(get_next_obj_inside_cpuset_by_depth) +#define hwloc_get_next_obj_inside_cpuset_by_type HWLOC_NAME(get_next_obj_inside_cpuset_by_type) +#define hwloc_get_obj_inside_cpuset_by_depth HWLOC_NAME(get_obj_inside_cpuset_by_depth) +#define hwloc_get_obj_inside_cpuset_by_type HWLOC_NAME(get_obj_inside_cpuset_by_type) +#define hwloc_get_nbobjs_inside_cpuset_by_depth HWLOC_NAME(get_nbobjs_inside_cpuset_by_depth) +#define hwloc_get_nbobjs_inside_cpuset_by_type HWLOC_NAME(get_nbobjs_inside_cpuset_by_type) +#define hwloc_get_obj_index_inside_cpuset HWLOC_NAME(get_obj_index_inside_cpuset) +#define hwloc_get_child_covering_cpuset HWLOC_NAME(get_child_covering_cpuset) +#define hwloc_get_obj_covering_cpuset HWLOC_NAME(get_obj_covering_cpuset) +#define hwloc_get_next_obj_covering_cpuset_by_depth HWLOC_NAME(get_next_obj_covering_cpuset_by_depth) +#define hwloc_get_next_obj_covering_cpuset_by_type HWLOC_NAME(get_next_obj_covering_cpuset_by_type) +#define hwloc_obj_type_is_normal HWLOC_NAME(obj_type_is_normal) +#define hwloc_obj_type_is_memory HWLOC_NAME(obj_type_is_memory) +#define hwloc_obj_type_is_io HWLOC_NAME(obj_type_is_io) +#define hwloc_obj_type_is_cache HWLOC_NAME(obj_type_is_cache) +#define hwloc_obj_type_is_dcache HWLOC_NAME(obj_type_is_dcache) +#define hwloc_obj_type_is_icache HWLOC_NAME(obj_type_is_icache) +#define hwloc_get_cache_type_depth HWLOC_NAME(get_cache_type_depth) +#define hwloc_get_cache_covering_cpuset HWLOC_NAME(get_cache_covering_cpuset) +#define hwloc_get_shared_cache_covering_obj HWLOC_NAME(get_shared_cache_covering_obj) +#define hwloc_get_closest_objs HWLOC_NAME(get_closest_objs) +#define hwloc_get_obj_below_by_type HWLOC_NAME(get_obj_below_by_type) +#define hwloc_get_obj_below_array_by_type HWLOC_NAME(get_obj_below_array_by_type) +#define hwloc_distrib_flags_e HWLOC_NAME(distrib_flags_e) +#define HWLOC_DISTRIB_FLAG_REVERSE HWLOC_NAME_CAPS(DISTRIB_FLAG_REVERSE) +#define hwloc_distrib HWLOC_NAME(distrib) +#define hwloc_alloc_membind_policy HWLOC_NAME(alloc_membind_policy) +#define hwloc_alloc_membind_policy_nodeset HWLOC_NAME(alloc_membind_policy_nodeset) +#define hwloc_topology_get_complete_cpuset HWLOC_NAME(topology_get_complete_cpuset) +#define hwloc_topology_get_topology_cpuset HWLOC_NAME(topology_get_topology_cpuset) +#define hwloc_topology_get_allowed_cpuset HWLOC_NAME(topology_get_allowed_cpuset) +#define hwloc_topology_get_complete_nodeset HWLOC_NAME(topology_get_complete_nodeset) +#define hwloc_topology_get_topology_nodeset HWLOC_NAME(topology_get_topology_nodeset) +#define hwloc_topology_get_allowed_nodeset HWLOC_NAME(topology_get_allowed_nodeset) +#define hwloc_cpuset_to_nodeset HWLOC_NAME(cpuset_to_nodeset) +#define hwloc_cpuset_from_nodeset HWLOC_NAME(cpuset_from_nodeset) + +/* export.h */ + +#define hwloc_topology_export_xml_flags_e HWLOC_NAME(topology_export_xml_flags_e) +#define HWLOC_TOPOLOGY_EXPORT_XML_FLAG_V1 HWLOC_NAME_CAPS(TOPOLOGY_EXPORT_XML_FLAG_V1) +#define hwloc_topology_export_xml HWLOC_NAME(topology_export_xml) +#define hwloc_topology_export_xmlbuffer HWLOC_NAME(topology_export_xmlbuffer) +#define hwloc_free_xmlbuffer HWLOC_NAME(free_xmlbuffer) +#define hwloc_topology_set_userdata_export_callback HWLOC_NAME(topology_set_userdata_export_callback) +#define hwloc_export_obj_userdata HWLOC_NAME(export_obj_userdata) +#define hwloc_export_obj_userdata_base64 HWLOC_NAME(export_obj_userdata_base64) +#define hwloc_topology_set_userdata_import_callback HWLOC_NAME(topology_set_userdata_import_callback) + +#define hwloc_topology_export_synthetic_flags_e HWLOC_NAME(topology_export_synthetic_flags_e) +#define HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_NO_EXTENDED_TYPES HWLOC_NAME_CAPS(TOPOLOGY_EXPORT_SYNTHETIC_FLAG_NO_EXTENDED_TYPES) +#define HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_NO_ATTRS HWLOC_NAME_CAPS(TOPOLOGY_EXPORT_SYNTHETIC_FLAG_NO_ATTRS) +#define HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_V1 HWLOC_NAME_CAPS(TOPOLOGY_EXPORT_SYNTHETIC_FLAG_V1) +#define HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_IGNORE_MEMORY HWLOC_NAME_CAPS(TOPOLOGY_EXPORT_SYNTHETIC_FLAG_IGNORE_MEMORY) +#define hwloc_topology_export_synthetic HWLOC_NAME(topology_export_synthetic) + +/* distances.h */ + +#define hwloc_distances_s HWLOC_NAME(distances_s) + +#define hwloc_distances_kind_e HWLOC_NAME(distances_kind_e) +#define HWLOC_DISTANCES_KIND_FROM_OS HWLOC_NAME_CAPS(DISTANCES_KIND_FROM_OS) +#define HWLOC_DISTANCES_KIND_FROM_USER HWLOC_NAME_CAPS(DISTANCES_KIND_FROM_USER) +#define HWLOC_DISTANCES_KIND_MEANS_LATENCY HWLOC_NAME_CAPS(DISTANCES_KIND_MEANS_LATENCY) +#define HWLOC_DISTANCES_KIND_MEANS_BANDWIDTH HWLOC_NAME_CAPS(DISTANCES_KIND_MEANS_BANDWIDTH) + +#define hwloc_distances_get HWLOC_NAME(distances_get) +#define hwloc_distances_get_by_depth HWLOC_NAME(distances_get_by_depth) +#define hwloc_distances_get_by_type HWLOC_NAME(distances_get_by_type) +#define hwloc_distances_release HWLOC_NAME(distances_release) +#define hwloc_distances_obj_index HWLOC_NAME(distances_obj_index) +#define hwloc_distances_obj_pair_values HWLOC_NAME(distances_pair_values) + +#define hwloc_distances_add_flag_e HWLOC_NAME(distances_add_flag_e) +#define HWLOC_DISTANCES_ADD_FLAG_GROUP HWLOC_NAME_CAPS(DISTANCES_ADD_FLAG_GROUP) +#define HWLOC_DISTANCES_ADD_FLAG_GROUP_INACCURATE HWLOC_NAME_CAPS(DISTANCES_ADD_FLAG_GROUP_INACCURATE) + +#define hwloc_distances_add HWLOC_NAME(distances_add) +#define hwloc_distances_remove HWLOC_NAME(distances_remove) +#define hwloc_distances_remove_by_depth HWLOC_NAME(distances_remove_by_depth) +#define hwloc_distances_remove_by_type HWLOC_NAME(distances_remove_by_type) + +/* diff.h */ + +#define hwloc_topology_diff_obj_attr_type_e HWLOC_NAME(topology_diff_obj_attr_type_e) +#define hwloc_topology_diff_obj_attr_type_t HWLOC_NAME(topology_diff_obj_attr_type_t) +#define HWLOC_TOPOLOGY_DIFF_OBJ_ATTR_SIZE HWLOC_NAME_CAPS(TOPOLOGY_DIFF_OBJ_ATTR_SIZE) +#define HWLOC_TOPOLOGY_DIFF_OBJ_ATTR_NAME HWLOC_NAME_CAPS(TOPOLOGY_DIFF_OBJ_ATTR_NAME) +#define HWLOC_TOPOLOGY_DIFF_OBJ_ATTR_INFO HWLOC_NAME_CAPS(TOPOLOGY_DIFF_OBJ_ATTR_INFO) +#define hwloc_topology_diff_obj_attr_u HWLOC_NAME(topology_diff_obj_attr_u) +#define hwloc_topology_diff_obj_attr_generic_s HWLOC_NAME(topology_diff_obj_attr_generic_s) +#define hwloc_topology_diff_obj_attr_uint64_s HWLOC_NAME(topology_diff_obj_attr_uint64_s) +#define hwloc_topology_diff_obj_attr_string_s HWLOC_NAME(topology_diff_obj_attr_string_s) +#define hwloc_topology_diff_type_e HWLOC_NAME(topology_diff_type_e) +#define hwloc_topology_diff_type_t HWLOC_NAME(topology_diff_type_t) +#define HWLOC_TOPOLOGY_DIFF_OBJ_ATTR HWLOC_NAME_CAPS(TOPOLOGY_DIFF_OBJ_ATTR) +#define HWLOC_TOPOLOGY_DIFF_TOO_COMPLEX HWLOC_NAME_CAPS(TOPOLOGY_DIFF_TOO_COMPLEX) +#define hwloc_topology_diff_u HWLOC_NAME(topology_diff_u) +#define hwloc_topology_diff_t HWLOC_NAME(topology_diff_t) +#define hwloc_topology_diff_generic_s HWLOC_NAME(topology_diff_generic_s) +#define hwloc_topology_diff_obj_attr_s HWLOC_NAME(topology_diff_obj_attr_s) +#define hwloc_topology_diff_too_complex_s HWLOC_NAME(topology_diff_too_complex_s) +#define hwloc_topology_diff_build HWLOC_NAME(topology_diff_build) +#define hwloc_topology_diff_apply_flags_e HWLOC_NAME(topology_diff_apply_flags_e) +#define HWLOC_TOPOLOGY_DIFF_APPLY_REVERSE HWLOC_NAME_CAPS(TOPOLOGY_DIFF_APPLY_REVERSE) +#define hwloc_topology_diff_apply HWLOC_NAME(topology_diff_apply) +#define hwloc_topology_diff_destroy HWLOC_NAME(topology_diff_destroy) +#define hwloc_topology_diff_load_xml HWLOC_NAME(topology_diff_load_xml) +#define hwloc_topology_diff_export_xml HWLOC_NAME(topology_diff_export_xml) +#define hwloc_topology_diff_load_xmlbuffer HWLOC_NAME(topology_diff_load_xmlbuffer) +#define hwloc_topology_diff_export_xmlbuffer HWLOC_NAME(topology_diff_export_xmlbuffer) + +/* shmem.h */ + +#define hwloc_shmem_topology_get_length HWLOC_NAME(shmem_topology_get_length) +#define hwloc_shmem_topology_write HWLOC_NAME(shmem_topology_write) +#define hwloc_shmem_topology_adopt HWLOC_NAME(shmem_topology_adopt) + +/* glibc-sched.h */ + +#define hwloc_cpuset_to_glibc_sched_affinity HWLOC_NAME(cpuset_to_glibc_sched_affinity) +#define hwloc_cpuset_from_glibc_sched_affinity HWLOC_NAME(cpuset_from_glibc_sched_affinity) + +/* linux-libnuma.h */ + +#define hwloc_cpuset_to_linux_libnuma_ulongs HWLOC_NAME(cpuset_to_linux_libnuma_ulongs) +#define hwloc_nodeset_to_linux_libnuma_ulongs HWLOC_NAME(nodeset_to_linux_libnuma_ulongs) +#define hwloc_cpuset_from_linux_libnuma_ulongs HWLOC_NAME(cpuset_from_linux_libnuma_ulongs) +#define hwloc_nodeset_from_linux_libnuma_ulongs HWLOC_NAME(nodeset_from_linux_libnuma_ulongs) +#define hwloc_cpuset_to_linux_libnuma_bitmask HWLOC_NAME(cpuset_to_linux_libnuma_bitmask) +#define hwloc_nodeset_to_linux_libnuma_bitmask HWLOC_NAME(nodeset_to_linux_libnuma_bitmask) +#define hwloc_cpuset_from_linux_libnuma_bitmask HWLOC_NAME(cpuset_from_linux_libnuma_bitmask) +#define hwloc_nodeset_from_linux_libnuma_bitmask HWLOC_NAME(nodeset_from_linux_libnuma_bitmask) + +/* linux.h */ + +#define hwloc_linux_set_tid_cpubind HWLOC_NAME(linux_set_tid_cpubind) +#define hwloc_linux_get_tid_cpubind HWLOC_NAME(linux_get_tid_cpubind) +#define hwloc_linux_get_tid_last_cpu_location HWLOC_NAME(linux_get_tid_last_cpu_location) +#define hwloc_linux_read_path_as_cpumask HWLOC_NAME(linux_read_file_cpumask) + +/* openfabrics-verbs.h */ + +#define hwloc_ibv_get_device_cpuset HWLOC_NAME(ibv_get_device_cpuset) +#define hwloc_ibv_get_device_osdev HWLOC_NAME(ibv_get_device_osdev) +#define hwloc_ibv_get_device_osdev_by_name HWLOC_NAME(ibv_get_device_osdev_by_name) + +/* intel-mic.h */ + +#define hwloc_intel_mic_get_device_cpuset HWLOC_NAME(intel_mic_get_device_cpuset) +#define hwloc_intel_mic_get_device_osdev_by_index HWLOC_NAME(intel_mic_get_device_osdev_by_index) + +/* opencl.h */ + +#define hwloc_opencl_get_device_cpuset HWLOC_NAME(opencl_get_device_cpuset) +#define hwloc_opencl_get_device_osdev HWLOC_NAME(opencl_get_device_osdev) +#define hwloc_opencl_get_device_osdev_by_index HWLOC_NAME(opencl_get_device_osdev_by_index) + +/* cuda.h */ + +#define hwloc_cuda_get_device_pci_ids HWLOC_NAME(cuda_get_device_pci_ids) +#define hwloc_cuda_get_device_cpuset HWLOC_NAME(cuda_get_device_cpuset) +#define hwloc_cuda_get_device_pcidev HWLOC_NAME(cuda_get_device_pcidev) +#define hwloc_cuda_get_device_osdev HWLOC_NAME(cuda_get_device_osdev) +#define hwloc_cuda_get_device_osdev_by_index HWLOC_NAME(cuda_get_device_osdev_by_index) + +/* cudart.h */ + +#define hwloc_cudart_get_device_pci_ids HWLOC_NAME(cudart_get_device_pci_ids) +#define hwloc_cudart_get_device_cpuset HWLOC_NAME(cudart_get_device_cpuset) +#define hwloc_cudart_get_device_pcidev HWLOC_NAME(cudart_get_device_pcidev) +#define hwloc_cudart_get_device_osdev_by_index HWLOC_NAME(cudart_get_device_osdev_by_index) + +/* nvml.h */ + +#define hwloc_nvml_get_device_cpuset HWLOC_NAME(nvml_get_device_cpuset) +#define hwloc_nvml_get_device_osdev HWLOC_NAME(nvml_get_device_osdev) +#define hwloc_nvml_get_device_osdev_by_index HWLOC_NAME(nvml_get_device_osdev_by_index) + +/* gl.h */ + +#define hwloc_gl_get_display_osdev_by_port_device HWLOC_NAME(gl_get_display_osdev_by_port_device) +#define hwloc_gl_get_display_osdev_by_name HWLOC_NAME(gl_get_display_osdev_by_name) +#define hwloc_gl_get_display_by_osdev HWLOC_NAME(gl_get_display_by_osdev) + +/* hwloc/plugins.h */ + +#define hwloc_disc_component_type_e HWLOC_NAME(disc_component_type_e) +#define HWLOC_DISC_COMPONENT_TYPE_CPU HWLOC_NAME_CAPS(DISC_COMPONENT_TYPE_CPU) +#define HWLOC_DISC_COMPONENT_TYPE_GLOBAL HWLOC_NAME_CAPS(DISC_COMPONENT_TYPE_GLOBAL) +#define HWLOC_DISC_COMPONENT_TYPE_MISC HWLOC_NAME_CAPS(DISC_COMPONENT_TYPE_MISC) +#define hwloc_disc_component_type_t HWLOC_NAME(disc_component_type_t) +#define hwloc_disc_component HWLOC_NAME(disc_component) + +#define hwloc_backend HWLOC_NAME(backend) + +#define hwloc_backend_alloc HWLOC_NAME(backend_alloc) +#define hwloc_backend_enable HWLOC_NAME(backend_enable) + +#define hwloc_component_type_e HWLOC_NAME(component_type_e) +#define HWLOC_COMPONENT_TYPE_DISC HWLOC_NAME_CAPS(COMPONENT_TYPE_DISC) +#define HWLOC_COMPONENT_TYPE_XML HWLOC_NAME_CAPS(COMPONENT_TYPE_XML) +#define hwloc_component_type_t HWLOC_NAME(component_type_t) +#define hwloc_component HWLOC_NAME(component) + +#define hwloc_plugin_check_namespace HWLOC_NAME(plugin_check_namespace) + +#define hwloc_insert_object_by_cpuset HWLOC_NAME(insert_object_by_cpuset) +#define hwloc_report_error_t HWLOC_NAME(report_error_t) +#define hwloc_report_os_error HWLOC_NAME(report_os_error) +#define hwloc_hide_errors HWLOC_NAME(hide_errors) +#define hwloc__insert_object_by_cpuset HWLOC_NAME(_insert_object_by_cpuset) +#define hwloc_insert_object_by_parent HWLOC_NAME(insert_object_by_parent) +#define hwloc_alloc_setup_object HWLOC_NAME(alloc_setup_object) +#define hwloc_obj_add_children_sets HWLOC_NAME(add_children_sets) +#define hwloc_topology_reconnect HWLOC_NAME(topology_reconnect) + +#define hwloc_filter_check_pcidev_subtype_important HWLOC_NAME(filter_check_pcidev_subtype_important) +#define hwloc_filter_check_osdev_subtype_important HWLOC_NAME(filter_check_osdev_subtype_important) +#define hwloc_filter_check_keep_object_type HWLOC_NAME(filter_check_keep_object_type) +#define hwloc_filter_check_keep_object HWLOC_NAME(filter_check_keep_object) + +#define hwloc_pcidisc_find_cap HWLOC_NAME(pcidisc_find_cap) +#define hwloc_pcidisc_find_linkspeed HWLOC_NAME(pcidisc_find_linkspeed) +#define hwloc_pcidisc_check_bridge_type HWLOC_NAME(pcidisc_check_bridge_type) +#define hwloc_pcidisc_setup_bridge_attr HWLOC_NAME(pcidisc_setup_bridge_attr) +#define hwloc_pcidisc_tree_insert_by_busid HWLOC_NAME(pcidisc_tree_insert_by_busid) +#define hwloc_pcidisc_tree_attach HWLOC_NAME(pcidisc_tree_attach) + +#define hwloc_pcidisc_find_by_busid HWLOC_NAME(pcidisc_find_by_busid) +#define hwloc_pcidisc_find_busid_parent HWLOC_NAME(pcidisc_find_busid_parent) + +/* hwloc/deprecated.h */ + +#define hwloc_topology_insert_misc_object_by_parent HWLOC_NAME(topology_insert_misc_object_by_parent) +#define hwloc_obj_cpuset_snprintf HWLOC_NAME(obj_cpuset_snprintf) +#define hwloc_obj_type_sscanf HWLOC_NAME(obj_type_sscanf) + +#define hwloc_set_membind_nodeset HWLOC_NAME(set_membind_nodeset) +#define hwloc_get_membind_nodeset HWLOC_NAME(get_membind_nodeset) +#define hwloc_set_proc_membind_nodeset HWLOC_NAME(set_proc_membind_nodeset) +#define hwloc_get_proc_membind_nodeset HWLOC_NAME(get_proc_membind_nodeset) +#define hwloc_set_area_membind_nodeset HWLOC_NAME(set_area_membind_nodeset) +#define hwloc_get_area_membind_nodeset HWLOC_NAME(get_area_membind_nodeset) +#define hwloc_alloc_membind_nodeset HWLOC_NAME(alloc_membind_nodeset) + +#define hwloc_cpuset_to_nodeset_strict HWLOC_NAME(cpuset_to_nodeset_strict) +#define hwloc_cpuset_from_nodeset_strict HWLOC_NAME(cpuset_from_nodeset_strict) + +/* private/debug.h */ + +#define hwloc_debug_enabled HWLOC_NAME(debug_enabled) +#define hwloc_debug HWLOC_NAME(debug) + +/* private/misc.h */ + +#define hwloc_snprintf HWLOC_NAME(snprintf) +#define hwloc_namecoloncmp HWLOC_NAME(namecoloncmp) +#define hwloc_ffsl_manual HWLOC_NAME(ffsl_manual) +#define hwloc_ffs32 HWLOC_NAME(ffs32) +#define hwloc_ffsl_from_ffs32 HWLOC_NAME(ffsl_from_ffs32) +#define hwloc_flsl_manual HWLOC_NAME(flsl_manual) +#define hwloc_fls32 HWLOC_NAME(fls32) +#define hwloc_flsl_from_fls32 HWLOC_NAME(flsl_from_fls32) +#define hwloc_weight_long HWLOC_NAME(weight_long) +#define hwloc_strncasecmp HWLOC_NAME(strncasecmp) + +#define hwloc_bitmap_compare_inclusion HWLOC_NAME(bitmap_compare_inclusion) + +#define hwloc_pci_class_string HWLOC_NAME(pci_class_string) +#define hwloc_linux_pci_link_speed_from_string HWLOC_NAME(linux_pci_link_speed_from_string) + +#define hwloc_cache_type_by_depth_type HWLOC_NAME(cache_type_by_depth_type) +#define hwloc__obj_type_is_normal HWLOC_NAME(_obj_type_is_normal) +#define hwloc__obj_type_is_memory HWLOC_NAME(_obj_type_is_memory) +#define hwloc__obj_type_is_io HWLOC_NAME(_obj_type_is_io) +#define hwloc__obj_type_is_special HWLOC_NAME(_obj_type_is_special) + +#define hwloc__obj_type_is_cache HWLOC_NAME(_obj_type_is_cache) +#define hwloc__obj_type_is_dcache HWLOC_NAME(_obj_type_is_dcache) +#define hwloc__obj_type_is_icache HWLOC_NAME(_obj_type_is_icache) + +/* private/cpuid-x86.h */ + +#define hwloc_have_x86_cpuid HWLOC_NAME(have_x86_cpuid) +#define hwloc_x86_cpuid HWLOC_NAME(x86_cpuid) + +/* private/xml.h */ + +#define hwloc__xml_verbose HWLOC_NAME(_xml_verbose) + +#define hwloc__xml_import_state_s HWLOC_NAME(_xml_import_state_s) +#define hwloc__xml_import_state_t HWLOC_NAME(_xml_import_state_t) +#define hwloc__xml_import_diff HWLOC_NAME(_xml_import_diff) +#define hwloc_xml_backend_data_s HWLOC_NAME(xml_backend_data_s) +#define hwloc__xml_export_state_s HWLOC_NAME(_xml_export_state_s) +#define hwloc__xml_export_state_t HWLOC_NAME(_xml_export_state_t) +#define hwloc__xml_export_data_s HWLOC_NAME(_xml_export_data_s) +#define hwloc__xml_export_topology HWLOC_NAME(_xml_export_topology) +#define hwloc__xml_export_diff HWLOC_NAME(_xml_export_diff) + +#define hwloc_xml_callbacks HWLOC_NAME(xml_callbacks) +#define hwloc_xml_component HWLOC_NAME(xml_component) +#define hwloc_xml_callbacks_register HWLOC_NAME(xml_callbacks_register) +#define hwloc_xml_callbacks_reset HWLOC_NAME(xml_callbacks_reset) + +#define hwloc__xml_imported_v1distances_s HWLOC_NAME(_xml_imported_v1distances_s) + +/* private/components.h */ + +#define hwloc_disc_component_force_enable HWLOC_NAME(disc_component_force_enable) +#define hwloc_disc_components_enable_others HWLOC_NAME(disc_components_instantiate_others) + +#define hwloc_backends_is_thissystem HWLOC_NAME(backends_is_thissystem) +#define hwloc_backends_find_callbacks HWLOC_NAME(backends_find_callbacks) + +#define hwloc_backends_init HWLOC_NAME(backends_init) +#define hwloc_backends_disable_all HWLOC_NAME(backends_disable_all) + +#define hwloc_components_init HWLOC_NAME(components_init) +#define hwloc_components_fini HWLOC_NAME(components_fini) + +/* private/internal-private.h */ + +#define hwloc_xml_component HWLOC_NAME(xml_component) +#define hwloc_synthetic_component HWLOC_NAME(synthetic_component) + +#define hwloc_aix_component HWLOC_NAME(aix_component) +#define hwloc_bgq_component HWLOC_NAME(bgq_component) +#define hwloc_darwin_component HWLOC_NAME(darwin_component) +#define hwloc_freebsd_component HWLOC_NAME(freebsd_component) +#define hwloc_hpux_component HWLOC_NAME(hpux_component) +#define hwloc_linux_component HWLOC_NAME(linux_component) +#define hwloc_netbsd_component HWLOC_NAME(netbsd_component) +#define hwloc_noos_component HWLOC_NAME(noos_component) +#define hwloc_solaris_component HWLOC_NAME(solaris_component) +#define hwloc_windows_component HWLOC_NAME(windows_component) +#define hwloc_x86_component HWLOC_NAME(x86_component) + +#define hwloc_cuda_component HWLOC_NAME(cuda_component) +#define hwloc_gl_component HWLOC_NAME(gl_component) +#define hwloc_linuxio_component HWLOC_NAME(linuxio_component) +#define hwloc_nvml_component HWLOC_NAME(nvml_component) +#define hwloc_opencl_component HWLOC_NAME(opencl_component) +#define hwloc_pci_component HWLOC_NAME(pci_component) + +#define hwloc_xml_libxml_component HWLOC_NAME(xml_libxml_component) +#define hwloc_xml_nolibxml_component HWLOC_NAME(xml_nolibxml_component) + +/* private/private.h */ + +#define hwloc_special_level_s HWLOC_NAME(special_level_s) + +#define hwloc_pci_forced_locality_s HWLOC_NAME(pci_forced_locality_s) + +#define hwloc_alloc_root_sets HWLOC_NAME(alloc_root_sets) +#define hwloc_setup_pu_level HWLOC_NAME(setup_pu_level) +#define hwloc_get_sysctlbyname HWLOC_NAME(get_sysctlbyname) +#define hwloc_get_sysctl HWLOC_NAME(get_sysctl) +#define hwloc_fallback_nbprocessors HWLOC_NAME(fallback_nbprocessors) + +#define hwloc__object_cpusets_compare_first HWLOC_NAME(_object_cpusets_compare_first) +#define hwloc__reorder_children HWLOC_NAME(_reorder_children) + +#define hwloc_topology_setup_defaults HWLOC_NAME(topology_setup_defaults) +#define hwloc_topology_clear HWLOC_NAME(topology_clear) + +#define hwloc__attach_memory_object HWLOC_NAME(insert_memory_object) + +#define hwloc_pci_discovery_init HWLOC_NAME(pci_discovery_init) +#define hwloc_pci_discovery_prepare HWLOC_NAME(pci_discovery_prepare) +#define hwloc_pci_discovery_exit HWLOC_NAME(pci_discovery_exit) +#define hwloc_find_insert_io_parent_by_complete_cpuset HWLOC_NAME(hwloc_find_insert_io_parent_by_complete_cpuset) +#define hwloc_pci_belowroot_apply_locality HWLOC_NAME(pci_belowroot_apply_locality) + +#define hwloc__add_info HWLOC_NAME(_add_info) +#define hwloc__add_info_nodup HWLOC_NAME(_add_info_nodup) +#define hwloc__move_infos HWLOC_NAME(_move_infos) +#define hwloc__free_infos HWLOC_NAME(_free_infos) + +#define hwloc_binding_hooks HWLOC_NAME(binding_hooks) +#define hwloc_set_native_binding_hooks HWLOC_NAME(set_native_binding_hooks) +#define hwloc_set_binding_hooks HWLOC_NAME(set_binding_hooks) + +#define hwloc_set_linuxfs_hooks HWLOC_NAME(set_linuxfs_hooks) +#define hwloc_set_bgq_hooks HWLOC_NAME(set_bgq_hooks) +#define hwloc_set_solaris_hooks HWLOC_NAME(set_solaris_hooks) +#define hwloc_set_aix_hooks HWLOC_NAME(set_aix_hooks) +#define hwloc_set_windows_hooks HWLOC_NAME(set_windows_hooks) +#define hwloc_set_darwin_hooks HWLOC_NAME(set_darwin_hooks) +#define hwloc_set_freebsd_hooks HWLOC_NAME(set_freebsd_hooks) +#define hwloc_set_netbsd_hooks HWLOC_NAME(set_netbsd_hooks) +#define hwloc_set_hpux_hooks HWLOC_NAME(set_hpux_hooks) + +#define hwloc_look_hardwired_fujitsu_k HWLOC_NAME(look_hardwired_fujitsu_k) +#define hwloc_look_hardwired_fujitsu_fx10 HWLOC_NAME(look_hardwired_fujitsu_fx10) +#define hwloc_look_hardwired_fujitsu_fx100 HWLOC_NAME(look_hardwired_fujitsu_fx100) + +#define hwloc_add_uname_info HWLOC_NAME(add_uname_info) +#define hwloc_free_unlinked_object HWLOC_NAME(free_unlinked_object) +#define hwloc_free_object_and_children HWLOC_NAME(free_object_and_children) +#define hwloc_free_object_siblings_and_children HWLOC_NAME(free_object_siblings_and_children) + +#define hwloc_alloc_heap HWLOC_NAME(alloc_heap) +#define hwloc_alloc_mmap HWLOC_NAME(alloc_mmap) +#define hwloc_free_heap HWLOC_NAME(free_heap) +#define hwloc_free_mmap HWLOC_NAME(free_mmap) +#define hwloc_alloc_or_fail HWLOC_NAME(alloc_or_fail) + +#define hwloc_internal_distances_s HWLOC_NAME(internal_distances_s) +#define hwloc_internal_distances_init HWLOC_NAME(internal_distances_init) +#define hwloc_internal_distances_prepare HWLOC_NAME(internal_distances_prepare) +#define hwloc_internal_distances_dup HWLOC_NAME(internal_distances_dup) +#define hwloc_internal_distances_refresh HWLOC_NAME(internal_distances_refresh) +#define hwloc_internal_distances_destroy HWLOC_NAME(internal_distances_destroy) + +#define hwloc_internal_distances_add HWLOC_NAME(internal_distances_add) +#define hwloc_internal_distances_add_by_index HWLOC_NAME(internal_distances_add_by_index) +#define hwloc_internal_distances_invalidate_cached_objs HWLOC_NAME(hwloc_internal_distances_invalidate_cached_objs) + +#define hwloc_encode_to_base64 HWLOC_NAME(encode_to_base64) +#define hwloc_decode_from_base64 HWLOC_NAME(decode_from_base64) + +#define hwloc_progname HWLOC_NAME(progname) + +#define hwloc__topology_disadopt HWLOC_NAME(_topology_disadopt) +#define hwloc__topology_dup HWLOC_NAME(_topology_dup) + +#define hwloc_tma HWLOC_NAME(tma) +#define hwloc_tma_malloc HWLOC_NAME(tma_malloc) +#define hwloc_tma_calloc HWLOC_NAME(tma_calloc) +#define hwloc_tma_strdup HWLOC_NAME(tma_strdup) +#define hwloc_bitmap_tma_dup HWLOC_NAME(bitmap_tma_dup) + +/* private/solaris-chiptype.h */ + +#define hwloc_solaris_chip_info_s HWLOC_NAME(solaris_chip_info_s) +#define hwloc_solaris_get_chip_info HWLOC_NAME(solaris_get_chip_info) + +#endif /* HWLOC_SYM_TRANSFORM */ + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + +#endif /* HWLOC_RENAME_H */ diff --git a/src/3rdparty/hwloc/include/hwloc/shmem.h b/src/3rdparty/hwloc/include/hwloc/shmem.h new file mode 100644 index 00000000..22249463 --- /dev/null +++ b/src/3rdparty/hwloc/include/hwloc/shmem.h @@ -0,0 +1,137 @@ +/* + * Copyright © 2013-2018 Inria. All rights reserved. + * See COPYING in top-level directory. + */ + +/** \file + * \brief Sharing topologies between processes + */ + +#ifndef HWLOC_SHMEM_H +#define HWLOC_SHMEM_H + +#include + +#ifdef __cplusplus +extern "C" { +#elif 0 +} +#endif + + +/** \defgroup hwlocality_shmem Sharing topologies between processes + * + * These functions are used to share a topology between processes by + * duplicating it into a file-backed shared-memory buffer. + * + * The master process must first get the required shared-memory size + * for storing this topology with hwloc_shmem_topology_get_length(). + * + * Then it must find a virtual memory area of that size that is available + * in all processes (identical virtual addresses in all processes). + * On Linux, this can be done by comparing holes found in /proc/\/maps + * for each process. + * + * Once found, it must open a destination file for storing the buffer, + * and pass it to hwloc_shmem_topology_write() together with + * virtual memory address and length obtained above. + * + * Other processes may then adopt this shared topology by opening the + * same file and passing it to hwloc_shmem_topology_adopt() with the + * exact same virtual memory address and length. + * + * @{ + */ + +/** \brief Get the required shared memory length for storing a topology. + * + * This length (in bytes) must be used in hwloc_shmem_topology_write() + * and hwloc_shmem_topology_adopt() later. + * + * \note Flags \p flags are currently unused, must be 0. + */ +HWLOC_DECLSPEC int hwloc_shmem_topology_get_length(hwloc_topology_t topology, + size_t *lengthp, + unsigned long flags); + +/** \brief Duplicate a topology to a shared memory file. + * + * Temporarily map a file in virtual memory and duplicate the + * topology \p topology by allocating duplicates in there. + * + * The segment of the file pointed by descriptor \p fd, + * starting at offset \p fileoffset, and of length \p length (in bytes), + * will be temporarily mapped at virtual address \p mmap_address + * during the duplication. + * + * The mapping length \p length must have been previously obtained with + * hwloc_shmem_topology_get_length() + * and the topology must not have been modified in the meantime. + * + * \note Flags \p flags are currently unused, must be 0. + * + * \note The object userdata pointer is duplicated but the pointed buffer + * is not. However the caller may also allocate it manually in shared memory + * to share it as well. + * + * \return -1 with errno set to EBUSY if the virtual memory mapping defined + * by \p mmap_address and \p length isn't available in the process. + * \return -1 with errno set to EINVAL if \p fileoffset, \p mmap_address + * or \p length aren't page-aligned. + */ +HWLOC_DECLSPEC int hwloc_shmem_topology_write(hwloc_topology_t topology, + int fd, hwloc_uint64_t fileoffset, + void *mmap_address, size_t length, + unsigned long flags); + +/** \brief Adopt a shared memory topology stored in a file. + * + * Map a file in virtual memory and adopt the topology that was previously + * stored there with hwloc_shmem_topology_write(). + * + * The returned adopted topology in \p topologyp can be used just like any + * topology. And it must be destroyed with hwloc_topology_destroy() as usual. + * + * However the topology is read-only. + * For instance, it cannot be modified with hwloc_topology_restrict() + * and object userdata pointers cannot be changed. + * + * The segment of the file pointed by descriptor \p fd, + * starting at offset \p fileoffset, and of length \p length (in bytes), + * will be mapped at virtual address \p mmap_address. + * + * The file pointed by descriptor \p fd, the offset \p fileoffset, + * the requested mapping virtual address \p mmap_address and the length \p length + * must be identical to what was given to hwloc_shmem_topology_write() earlier. + * + * \note Flags \p flags are currently unused, must be 0. + * + * \note The object userdata pointer should not be used unless the process + * that created the shared topology also placed userdata-pointed buffers + * in shared memory. + * + * \note This function takes care of calling hwloc_topology_abi_check(). + * + * \return -1 with errno set to EBUSY if the virtual memory mapping defined + * by \p mmap_address and \p length isn't available in the process. + * + * \return -1 with errno set to EINVAL if \p fileoffset, \p mmap_address + * or \p length aren't page-aligned, or do not match what was given to + * hwloc_shmem_topology_write() earlier. + * + * \return -1 with errno set to EINVAL if the layout of the topology structure + * is different between the writer process and the adopter process. + */ +HWLOC_DECLSPEC int hwloc_shmem_topology_adopt(hwloc_topology_t *topologyp, + int fd, hwloc_uint64_t fileoffset, + void *mmap_address, size_t length, + unsigned long flags); +/** @} */ + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + +#endif /* HWLOC_SHMEM_H */ diff --git a/src/3rdparty/hwloc/include/private/autogen/config.h b/src/3rdparty/hwloc/include/private/autogen/config.h new file mode 100644 index 00000000..a97bdfea --- /dev/null +++ b/src/3rdparty/hwloc/include/private/autogen/config.h @@ -0,0 +1,672 @@ +/* + * Copyright © 2009, 2011, 2012 CNRS. All rights reserved. + * Copyright © 2009-2018 Inria. All rights reserved. + * Copyright © 2009, 2011, 2012, 2015 Université Bordeaux. All rights reserved. + * Copyright © 2009 Cisco Systems, Inc. All rights reserved. + * $COPYRIGHT$ + * + * Additional copyrights may follow + * + * $HEADER$ + */ + +#ifndef HWLOC_CONFIGURE_H +#define HWLOC_CONFIGURE_H + +#define DECLSPEC_EXPORTS + +#define HWLOC_HAVE_MSVC_CPUIDEX 1 + +/* Define to 1 if the system has the type `CACHE_DESCRIPTOR'. */ +#define HAVE_CACHE_DESCRIPTOR 0 + +/* Define to 1 if the system has the type `CACHE_RELATIONSHIP'. */ +#define HAVE_CACHE_RELATIONSHIP 0 + +/* Define to 1 if you have the `clz' function. */ +/* #undef HAVE_CLZ */ + +/* Define to 1 if you have the `clzl' function. */ +/* #undef HAVE_CLZL */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_CL_CL_EXT_H */ + +/* Define to 1 if you have the `cpuset_setaffinity' function. */ +/* #undef HAVE_CPUSET_SETAFFINITY */ + +/* Define to 1 if you have the `cpuset_setid' function. */ +/* #undef HAVE_CPUSET_SETID */ + +/* Define to 1 if we have -lcuda */ +/* #undef HAVE_CUDA */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_CUDA_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_CUDA_RUNTIME_API_H */ + +/* Define to 1 if you have the declaration of `CL_DEVICE_TOPOLOGY_AMD', and to + 0 if you don't. */ +/* #undef HAVE_DECL_CL_DEVICE_TOPOLOGY_AMD */ + +/* Define to 1 if you have the declaration of `CTL_HW', and to 0 if you don't. + */ +/* #undef HAVE_DECL_CTL_HW */ + +/* Define to 1 if you have the declaration of `fabsf', and to 0 if you don't. + */ +#define HAVE_DECL_FABSF 1 + +/* Define to 1 if you have the declaration of `modff', and to 0 if you don't. + */ +#define HAVE_DECL_MODFF 1 + +/* Define to 1 if you have the declaration of `HW_NCPU', and to 0 if you + don't. */ +/* #undef HAVE_DECL_HW_NCPU */ + +/* Define to 1 if you have the declaration of + `nvmlDeviceGetMaxPcieLinkGeneration', and to 0 if you don't. */ +/* #undef HAVE_DECL_NVMLDEVICEGETMAXPCIELINKGENERATION */ + +/* Define to 1 if you have the declaration of `pthread_getaffinity_np', and to + 0 if you don't. */ +#define HAVE_DECL_PTHREAD_GETAFFINITY_NP 0 + +/* Define to 1 if you have the declaration of `pthread_setaffinity_np', and to + 0 if you don't. */ +#define HAVE_DECL_PTHREAD_SETAFFINITY_NP 0 + +/* Define to 1 if you have the declaration of `strtoull', and to 0 if you + don't. */ +#define HAVE_DECL_STRTOULL 0 + +/* Define to 1 if you have the declaration of `strcasecmp', and to 0 if you + don't. */ +/* #undef HWLOC_HAVE_DECL_STRCASECMP */ + +/* Define to 1 if you have the declaration of `snprintf', and to 0 if you + don't. */ +#define HAVE_DECL_SNPRINTF 0 + +/* Define to 1 if you have the declaration of `_strdup', and to 0 if you + don't. */ +#define HAVE_DECL__STRDUP 1 + +/* Define to 1 if you have the declaration of `_putenv', and to 0 if you + don't. */ +#define HAVE_DECL__PUTENV 1 + +/* Define to 1 if you have the declaration of `_SC_LARGE_PAGESIZE', and to 0 + if you don't. */ +#define HAVE_DECL__SC_LARGE_PAGESIZE 0 + +/* Define to 1 if you have the declaration of `_SC_NPROCESSORS_CONF', and to 0 + if you don't. */ +#define HAVE_DECL__SC_NPROCESSORS_CONF 0 + +/* Define to 1 if you have the declaration of `_SC_NPROCESSORS_ONLN', and to 0 + if you don't. */ +#define HAVE_DECL__SC_NPROCESSORS_ONLN 0 + +/* Define to 1 if you have the declaration of `_SC_NPROC_CONF', and to 0 if + you don't. */ +#define HAVE_DECL__SC_NPROC_CONF 0 + +/* Define to 1 if you have the declaration of `_SC_NPROC_ONLN', and to 0 if + you don't. */ +#define HAVE_DECL__SC_NPROC_ONLN 0 + +/* Define to 1 if you have the declaration of `_SC_PAGESIZE', and to 0 if you + don't. */ +#define HAVE_DECL__SC_PAGESIZE 0 + +/* Define to 1 if you have the declaration of `_SC_PAGE_SIZE', and to 0 if you + don't. */ +#define HAVE_DECL__SC_PAGE_SIZE 0 + +/* Define to 1 if you have the header file. */ +/* #define HAVE_DIRENT_H 1 */ +#undef HAVE_DIRENT_H + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_DLFCN_H */ + +/* Define to 1 if you have the `ffs' function. */ +/* #undef HAVE_FFS */ + +/* Define to 1 if you have the `ffsl' function. */ +/* #undef HAVE_FFSL */ + +/* Define to 1 if you have the `fls' function. */ +/* #undef HAVE_FLS */ + +/* Define to 1 if you have the `flsl' function. */ +/* #undef HAVE_FLSL */ + +/* Define to 1 if you have the `getpagesize' function. */ +#define HAVE_GETPAGESIZE 1 + +/* Define to 1 if the system has the type `GROUP_AFFINITY'. */ +#define HAVE_GROUP_AFFINITY 1 + +/* Define to 1 if the system has the type `GROUP_RELATIONSHIP'. */ +#define HAVE_GROUP_RELATIONSHIP 1 + +/* Define to 1 if you have the `host_info' function. */ +/* #undef HAVE_HOST_INFO */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_INFINIBAND_VERBS_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if the system has the type `KAFFINITY'. */ +#define HAVE_KAFFINITY 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_KSTAT_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_LANGINFO_H */ + +/* Define to 1 if we have -lgdi32 */ +#define HAVE_LIBGDI32 1 + +/* Define to 1 if we have -libverbs */ +/* #undef HAVE_LIBIBVERBS */ + +/* Define to 1 if we have -lkstat */ +/* #undef HAVE_LIBKSTAT */ + +/* Define to 1 if we have -llgrp */ +/* #undef HAVE_LIBLGRP */ + +/* Define to 1 if you have the header file. */ +#define HAVE_LOCALE_H 1 + +/* Define to 1 if the system has the type `LOGICAL_PROCESSOR_RELATIONSHIP'. */ +#define HAVE_LOGICAL_PROCESSOR_RELATIONSHIP 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_MACH_MACH_HOST_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_MACH_MACH_INIT_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_MALLOC_H 1 + +/* Define to 1 if you have the `memalign' function. */ +/* #undef HAVE_MEMALIGN */ + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `nl_langinfo' function. */ +/* #undef HAVE_NL_LANGINFO */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_NUMAIF_H */ + +/* Define to 1 if the system has the type `NUMA_NODE_RELATIONSHIP'. */ +#define HAVE_NUMA_NODE_RELATIONSHIP 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_NVCTRL_NVCTRL_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_NVML_H */ + +/* Define to 1 if you have the `openat' function. */ +/* #undef HAVE_OPENAT */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_PICL_H */ + +/* Define to 1 if you have the `posix_memalign' function. */ +/* #undef HAVE_POSIX_MEMALIGN */ + +/* Define to 1 if the system has the type `PROCESSOR_CACHE_TYPE'. */ +#define HAVE_PROCESSOR_CACHE_TYPE 1 + +/* Define to 1 if the system has the type `PROCESSOR_GROUP_INFO'. */ +#define HAVE_PROCESSOR_GROUP_INFO 1 + +/* Define to 1 if the system has the type `PROCESSOR_RELATIONSHIP'. */ +#define HAVE_PROCESSOR_RELATIONSHIP 1 + +/* Define to 1 if the system has the type `PSAPI_WORKING_SET_EX_BLOCK'. */ +/* #undef HAVE_PSAPI_WORKING_SET_EX_BLOCK */ + +/* Define to 1 if the system has the type `PSAPI_WORKING_SET_EX_INFORMATION'. + */ +/* #undef HAVE_PSAPI_WORKING_SET_EX_INFORMATION */ + +/* Define to 1 if the system has the type `PROCESSOR_NUMBER'. */ +#define HAVE_PROCESSOR_NUMBER 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_PTHREAD_NP_H */ + +/* Define to 1 if the system has the type `pthread_t'. */ +/* #undef HAVE_PTHREAD_T */ +#undef HAVE_PTHREAD_T + +/* Define to 1 if you have the `putwc' function. */ +#define HAVE_PUTWC 1 + +/* Define to 1 if the system has the type `RelationProcessorPackage'. */ +/* #undef HAVE_RELATIONPROCESSORPACKAGE */ + +/* Define to 1 if you have the `setlocale' function. */ +#define HAVE_SETLOCALE 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strftime' function. */ +#define HAVE_STRFTIME 1 + +/* Define to 1 if you have the header file. */ +/* #define HAVE_STRINGS_H 1*/ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `strncasecmp' function. */ +#define HAVE_STRNCASECMP 1 + +/* Define to '1' if sysctl is present and usable */ +/* #undef HAVE_SYSCTL */ + +/* Define to '1' if sysctlbyname is present and usable */ +/* #undef HAVE_SYSCTLBYNAME */ + +/* Define to 1 if the system has the type + `SYSTEM_LOGICAL_PROCESSOR_INFORMATION'. */ +#define HAVE_SYSTEM_LOGICAL_PROCESSOR_INFORMATION 1 + +/* Define to 1 if the system has the type + `SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX'. */ +#define HAVE_SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_CPUSET_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_LGRP_USER_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_MMAN_H */ + +/* Define to 1 if you have the header file. */ +/* #define HAVE_SYS_PARAM_H 1 */ +#undef HAVE_SYS_PARAM_H + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_SYSCTL_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_UTSNAME_H */ + +/* Define to 1 if you have the `uname' function. */ +/* #undef HAVE_UNAME */ + +/* Define to 1 if you have the header file. */ +/* #define HAVE_UNISTD_H 1 */ +#undef HAVE_UNISTD_H + +/* Define to 1 if you have the `uselocale' function. */ +/* #undef HAVE_USELOCALE */ + +/* Define to 1 if the system has the type `wchar_t'. */ +#define HAVE_WCHAR_T 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_X11_KEYSYM_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_X11_XLIB_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_X11_XUTIL_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_XLOCALE_H */ + +/* Define to 1 on AIX */ +/* #undef HWLOC_AIX_SYS */ + +/* Define to 1 on BlueGene/Q */ +/* #undef HWLOC_BGQ_SYS */ + +/* Whether C compiler supports symbol visibility or not */ +#define HWLOC_C_HAVE_VISIBILITY 0 + +/* Define to 1 on Darwin */ +/* #undef HWLOC_DARWIN_SYS */ + +/* Whether we are in debugging mode or not */ +/* #undef HWLOC_DEBUG */ + +/* Define to 1 on *FREEBSD */ +/* #undef HWLOC_FREEBSD_SYS */ + +/* Whether your compiler has __attribute__ or not */ +/* #define HWLOC_HAVE_ATTRIBUTE 1 */ +#undef HWLOC_HAVE_ATTRIBUTE + +/* Whether your compiler has __attribute__ aligned or not */ +/* #define HWLOC_HAVE_ATTRIBUTE_ALIGNED 1 */ + +/* Whether your compiler has __attribute__ always_inline or not */ +/* #define HWLOC_HAVE_ATTRIBUTE_ALWAYS_INLINE 1 */ + +/* Whether your compiler has __attribute__ cold or not */ +/* #define HWLOC_HAVE_ATTRIBUTE_COLD 1 */ + +/* Whether your compiler has __attribute__ const or not */ +/* #define HWLOC_HAVE_ATTRIBUTE_CONST 1 */ + +/* Whether your compiler has __attribute__ deprecated or not */ +/* #define HWLOC_HAVE_ATTRIBUTE_DEPRECATED 1 */ + +/* Whether your compiler has __attribute__ format or not */ +/* #define HWLOC_HAVE_ATTRIBUTE_FORMAT 1 */ + +/* Whether your compiler has __attribute__ hot or not */ +/* #define HWLOC_HAVE_ATTRIBUTE_HOT 1 */ + +/* Whether your compiler has __attribute__ malloc or not */ +/* #define HWLOC_HAVE_ATTRIBUTE_MALLOC 1 */ + +/* Whether your compiler has __attribute__ may_alias or not */ +/* #define HWLOC_HAVE_ATTRIBUTE_MAY_ALIAS 1 */ + +/* Whether your compiler has __attribute__ nonnull or not */ +/* #define HWLOC_HAVE_ATTRIBUTE_NONNULL 1 */ + +/* Whether your compiler has __attribute__ noreturn or not */ +/* #define HWLOC_HAVE_ATTRIBUTE_NORETURN 1 */ + +/* Whether your compiler has __attribute__ no_instrument_function or not */ +/* #define HWLOC_HAVE_ATTRIBUTE_NO_INSTRUMENT_FUNCTION 1 */ + +/* Whether your compiler has __attribute__ packed or not */ +/* #define HWLOC_HAVE_ATTRIBUTE_PACKED 1 */ + +/* Whether your compiler has __attribute__ pure or not */ +/* #define HWLOC_HAVE_ATTRIBUTE_PURE 1 */ + +/* Whether your compiler has __attribute__ sentinel or not */ +/* #define HWLOC_HAVE_ATTRIBUTE_SENTINEL 1 */ + +/* Whether your compiler has __attribute__ unused or not */ +/* #define HWLOC_HAVE_ATTRIBUTE_UNUSED 1 */ + +/* Whether your compiler has __attribute__ warn unused result or not */ +/* #define HWLOC_HAVE_ATTRIBUTE_WARN_UNUSED_RESULT 1 */ + +/* Whether your compiler has __attribute__ weak alias or not */ +/* #define HWLOC_HAVE_ATTRIBUTE_WEAK_ALIAS 1 */ + +/* Define to 1 if your `ffs' function is known to be broken. */ +/* #undef HWLOC_HAVE_BROKEN_FFS */ + +/* Define to 1 if you have the `cairo' library. */ +/* #undef HWLOC_HAVE_CAIRO */ + +/* Define to 1 if you have the `clz' function. */ +/* #undef HWLOC_HAVE_CLZ */ + +/* Define to 1 if you have the `clzl' function. */ +/* #undef HWLOC_HAVE_CLZL */ + +/* Define to 1 if you have cpuid */ +/* #undef HWLOC_HAVE_CPUID */ + +/* Define to 1 if the CPU_SET macro works */ +/* #undef HWLOC_HAVE_CPU_SET */ + +/* Define to 1 if the CPU_SET_S macro works */ +/* #undef HWLOC_HAVE_CPU_SET_S */ + +/* Define to 1 if you have the `cudart' SDK. */ +/* #undef HWLOC_HAVE_CUDART */ + +/* Define to 1 if function `clz' is declared by system headers */ +/* #undef HWLOC_HAVE_DECL_CLZ */ + +/* Define to 1 if function `clzl' is declared by system headers */ +/* #undef HWLOC_HAVE_DECL_CLZL */ + +/* Define to 1 if function `ffs' is declared by system headers */ +/* #undef HWLOC_HAVE_DECL_FFS */ + +/* Define to 1 if function `ffsl' is declared by system headers */ +/* #undef HWLOC_HAVE_DECL_FFSL */ + +/* Define to 1 if function `fls' is declared by system headers */ +/* #undef HWLOC_HAVE_DECL_FLS */ + +/* Define to 1 if function `flsl' is declared by system headers */ +/* #undef HWLOC_HAVE_DECL_FLSL */ + +/* Define to 1 if you have the `ffs' function. */ +/* #undef HWLOC_HAVE_FFS */ + +/* Define to 1 if you have the `ffsl' function. */ +/* #undef HWLOC_HAVE_FFSL */ + +/* Define to 1 if you have the `fls' function. */ +/* #undef HWLOC_HAVE_FLS */ + +/* Define to 1 if you have the `flsl' function. */ +/* #undef HWLOC_HAVE_FLSL */ + +/* Define to 1 if you have the GL module components. */ +/* #undef HWLOC_HAVE_GL */ + +/* Define to 1 if you have a library providing the termcap interface */ +/* #undef HWLOC_HAVE_LIBTERMCAP */ + +/* Define to 1 if you have the `libxml2' library. */ +/* #undef HWLOC_HAVE_LIBXML2 */ + +/* Define to 1 if building the Linux PCI component */ +/* #undef HWLOC_HAVE_LINUXPCI */ + +/* Define to 1 if you have the `NVML' library. */ +/* #undef HWLOC_HAVE_NVML */ + +/* Define to 1 if glibc provides the old prototype (without length) of + sched_setaffinity() */ +/* #undef HWLOC_HAVE_OLD_SCHED_SETAFFINITY */ + +/* Define to 1 if you have the `OpenCL' library. */ +/* #undef HWLOC_HAVE_OPENCL */ + +/* Define to 1 if the hwloc library should support dynamically-loaded plugins + */ +/* #undef HWLOC_HAVE_PLUGINS */ + +/* `Define to 1 if you have pthread_getthrds_np' */ +/* #undef HWLOC_HAVE_PTHREAD_GETTHRDS_NP */ + +/* Define to 1 if pthread mutexes are available */ +/* #undef HWLOC_HAVE_PTHREAD_MUTEX */ + +/* Define to 1 if glibc provides a prototype of sched_setaffinity() */ +#define HWLOC_HAVE_SCHED_SETAFFINITY 1 + +/* Define to 1 if you have the header file. */ +#define HWLOC_HAVE_STDINT_H 1 + +/* Define to 1 if you have the `windows.h' header. */ +#define HWLOC_HAVE_WINDOWS_H 1 + +/* Define to 1 if X11 headers including Xutil.h and keysym.h are available. */ +/* #undef HWLOC_HAVE_X11_KEYSYM */ + +/* Define to 1 if function `syscall' is available */ +/* #undef HWLOC_HAVE_SYSCALL */ + +/* Define to 1 on HP-UX */ +/* #undef HWLOC_HPUX_SYS */ + +/* Define to 1 on Linux */ +/* #undef HWLOC_LINUX_SYS */ + +/* Define to 1 on *NETBSD */ +/* #undef HWLOC_NETBSD_SYS */ + +/* The size of `unsigned int', as computed by sizeof */ +#define HWLOC_SIZEOF_UNSIGNED_INT 4 + +/* The size of `unsigned long', as computed by sizeof */ +#define HWLOC_SIZEOF_UNSIGNED_LONG 4 + +/* Define to 1 on Solaris */ +/* #undef HWLOC_SOLARIS_SYS */ + +/* The hwloc symbol prefix */ +#define HWLOC_SYM_PREFIX hwloc_ + +/* The hwloc symbol prefix in all caps */ +#define HWLOC_SYM_PREFIX_CAPS HWLOC_ + +/* Whether we need to re-define all the hwloc public symbols or not */ +#define HWLOC_SYM_TRANSFORM 0 + +/* Define to 1 on unsupported systems */ +/* #undef HWLOC_UNSUPPORTED_SYS */ + +/* Define to 1 if ncurses works, preferred over curses */ +/* #undef HWLOC_USE_NCURSES */ + +/* Define to 1 on WINDOWS */ +#define HWLOC_WIN_SYS 1 + +/* Define to 1 on x86_32 */ +/* #undef HWLOC_X86_32_ARCH */ + +/* Define to 1 on x86_64 */ +#define HWLOC_X86_64_ARCH 1 + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#define LT_OBJDIR ".libs/" + +/* Name of package */ +#define PACKAGE "hwloc" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "http://www.open-mpi.org/projects/hwloc/" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "hwloc" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "hwloc" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "hwloc" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION HWLOC_VERSION + +/* The size of `unsigned int', as computed by sizeof. */ +#define SIZEOF_UNSIGNED_INT 4 + +/* The size of `unsigned long', as computed by sizeof. */ +#define SIZEOF_UNSIGNED_LONG 4 + +/* The size of `void *', as computed by sizeof. */ +#define SIZEOF_VOID_P 8 + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Enable extensions on HP-UX. */ +#ifndef _HPUX_SOURCE +# define _HPUX_SOURCE 1 +#endif + + +/* Enable extensions on AIX 3, Interix. */ +/* +#ifndef _ALL_SOURCE +# define _ALL_SOURCE 1 +#endif +*/ + +/* Enable GNU extensions on systems that have them. */ +/* +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif +*/ +/* Enable threading extensions on Solaris. */ +/* +#ifndef _POSIX_PTHREAD_SEMANTICS +# define _POSIX_PTHREAD_SEMANTICS 1 +#endif +*/ +/* Enable extensions on HP NonStop. */ +/* +#ifndef _TANDEM_SOURCE +# define _TANDEM_SOURCE 1 +#endif +*/ +/* Enable general extensions on Solaris. */ +/* +#ifndef __EXTENSIONS__ +# define __EXTENSIONS__ 1 +#endif +*/ + + +/* Version number of package */ +#define VERSION HWLOC_VERSION + +/* Define to 1 if the X Window System is missing or not being used. */ +#define X_DISPLAY_MISSING 1 + +/* Define to 1 if on MINIX. */ +/* #undef _MINIX */ + +/* Define to 2 if the system does not provide POSIX.1 features except with + this defined. */ +/* #undef _POSIX_1_SOURCE */ + +/* Define to 1 if you need to in order for `stat' and other things to work. */ +/* #undef _POSIX_SOURCE */ + +/* Define this to the process ID type */ +#define hwloc_pid_t HANDLE + +/* Define this to either strncasecmp or strncmp */ +#define hwloc_strncasecmp strncasecmp + +/* Define this to the thread ID type */ +#define hwloc_thread_t HANDLE + + +#endif /* HWLOC_CONFIGURE_H */ diff --git a/src/3rdparty/hwloc/include/private/components.h b/src/3rdparty/hwloc/include/private/components.h new file mode 100644 index 00000000..8525bbe4 --- /dev/null +++ b/src/3rdparty/hwloc/include/private/components.h @@ -0,0 +1,43 @@ +/* + * Copyright © 2012-2015 Inria. All rights reserved. + * See COPYING in top-level directory. + */ + + +#ifdef HWLOC_INSIDE_PLUGIN +/* + * these declarations are internal only, they are not available to plugins + * (many functions below are internal static symbols). + */ +#error This file should not be used in plugins +#endif + + +#ifndef PRIVATE_COMPONENTS_H +#define PRIVATE_COMPONENTS_H 1 + +#include + +struct hwloc_topology; + +extern int hwloc_disc_component_force_enable(struct hwloc_topology *topology, + int envvar_forced, /* 1 if forced through envvar, 0 if forced through API */ + int type, const char *name, + const void *data1, const void *data2, const void *data3); +extern void hwloc_disc_components_enable_others(struct hwloc_topology *topology); + +/* Compute the topology is_thissystem flag and find some callbacks based on enabled backends */ +extern void hwloc_backends_is_thissystem(struct hwloc_topology *topology); +extern void hwloc_backends_find_callbacks(struct hwloc_topology *topology); + +/* Initialize the list of backends used by a topology */ +extern void hwloc_backends_init(struct hwloc_topology *topology); +/* Disable and destroy all backends used by a topology */ +extern void hwloc_backends_disable_all(struct hwloc_topology *topology); + +/* Used by the core to setup/destroy the list of components */ +extern void hwloc_components_init(void); /* increases components refcount, should be called exactly once per topology (during init) */ +extern void hwloc_components_fini(void); /* decreases components refcount, should be called exactly once per topology (during destroy) */ + +#endif /* PRIVATE_COMPONENTS_H */ + diff --git a/src/3rdparty/hwloc/include/private/cpuid-x86.h b/src/3rdparty/hwloc/include/private/cpuid-x86.h new file mode 100644 index 00000000..2758afe0 --- /dev/null +++ b/src/3rdparty/hwloc/include/private/cpuid-x86.h @@ -0,0 +1,86 @@ +/* + * Copyright © 2010-2012, 2014 Université Bordeaux + * Copyright © 2010 Cisco Systems, Inc. All rights reserved. + * Copyright © 2014 Inria. All rights reserved. + * + * See COPYING in top-level directory. + */ + +/* Internals for x86's cpuid. */ + +#ifndef HWLOC_PRIVATE_CPUID_X86_H +#define HWLOC_PRIVATE_CPUID_X86_H + +#if (defined HWLOC_X86_32_ARCH) && (!defined HWLOC_HAVE_MSVC_CPUIDEX) +static __hwloc_inline int hwloc_have_x86_cpuid(void) +{ + int ret; + unsigned tmp, tmp2; + __asm__( + "mov $0,%0\n\t" /* Not supported a priori */ + + "pushfl \n\t" /* Save flags */ + + "pushfl \n\t" \ + "pop %1 \n\t" /* Get flags */ \ + +#define TRY_TOGGLE \ + "xor $0x00200000,%1\n\t" /* Try to toggle ID */ \ + "mov %1,%2\n\t" /* Save expected value */ \ + "push %1 \n\t" \ + "popfl \n\t" /* Try to toggle */ \ + "pushfl \n\t" \ + "pop %1 \n\t" \ + "cmp %1,%2\n\t" /* Compare with expected value */ \ + "jnz 0f\n\t" /* Unexpected, failure */ \ + + TRY_TOGGLE /* Try to set/clear */ + TRY_TOGGLE /* Try to clear/set */ + + "mov $1,%0\n\t" /* Passed the test! */ + + "0: \n\t" + "popfl \n\t" /* Restore flags */ + + : "=r" (ret), "=&r" (tmp), "=&r" (tmp2)); + return ret; +} +#endif /* !defined HWLOC_X86_32_ARCH && !defined HWLOC_HAVE_MSVC_CPUIDEX*/ +#if (defined HWLOC_X86_64_ARCH) || (defined HWLOC_HAVE_MSVC_CPUIDEX) +static __hwloc_inline int hwloc_have_x86_cpuid(void) { return 1; } +#endif /* HWLOC_X86_64_ARCH */ + +static __hwloc_inline void hwloc_x86_cpuid(unsigned *eax, unsigned *ebx, unsigned *ecx, unsigned *edx) +{ +#ifdef HWLOC_HAVE_MSVC_CPUIDEX + int regs[4]; + __cpuidex(regs, *eax, *ecx); + *eax = regs[0]; + *ebx = regs[1]; + *ecx = regs[2]; + *edx = regs[3]; +#else /* HWLOC_HAVE_MSVC_CPUIDEX */ + /* Note: gcc might want to use bx or the stack for %1 addressing, so we can't + * use them :/ */ +#ifdef HWLOC_X86_64_ARCH + hwloc_uint64_t sav_rbx; + __asm__( + "mov %%rbx,%2\n\t" + "cpuid\n\t" + "xchg %2,%%rbx\n\t" + "movl %k2,%1\n\t" + : "+a" (*eax), "=m" (*ebx), "=&r"(sav_rbx), + "+c" (*ecx), "=&d" (*edx)); +#elif defined(HWLOC_X86_32_ARCH) + __asm__( + "mov %%ebx,%1\n\t" + "cpuid\n\t" + "xchg %%ebx,%1\n\t" + : "+a" (*eax), "=&SD" (*ebx), "+c" (*ecx), "=&d" (*edx)); +#else +#error unknown architecture +#endif +#endif /* HWLOC_HAVE_MSVC_CPUIDEX */ +} + +#endif /* HWLOC_PRIVATE_X86_CPUID_H */ diff --git a/src/3rdparty/hwloc/include/private/debug.h b/src/3rdparty/hwloc/include/private/debug.h new file mode 100644 index 00000000..74b697db --- /dev/null +++ b/src/3rdparty/hwloc/include/private/debug.h @@ -0,0 +1,83 @@ +/* + * Copyright © 2009 CNRS + * Copyright © 2009-2017 Inria. All rights reserved. + * Copyright © 2009, 2011 Université Bordeaux + * Copyright © 2011 Cisco Systems, Inc. All rights reserved. + * See COPYING in top-level directory. + */ + +/* The configuration file */ + +#ifndef HWLOC_DEBUG_H +#define HWLOC_DEBUG_H + +#include +#include + +#ifdef HWLOC_DEBUG +#include +#include +#endif + +/* Compile-time assertion */ +#define HWLOC_BUILD_ASSERT(condition) ((void)sizeof(char[1 - 2*!(condition)])) + +#ifdef HWLOC_DEBUG +static __hwloc_inline int hwloc_debug_enabled(void) +{ + static int checked = 0; + static int enabled = 1; + if (!checked) { + const char *env = getenv("HWLOC_DEBUG_VERBOSE"); + if (env) + enabled = atoi(env); + if (enabled) + fprintf(stderr, "hwloc verbose debug enabled, may be disabled with HWLOC_DEBUG_VERBOSE=0 in the environment.\n"); + checked = 1; + } + return enabled; +} +#endif + +static __hwloc_inline void hwloc_debug(const char *s __hwloc_attribute_unused, ...) __hwloc_attribute_format(printf, 1, 2); +static __hwloc_inline void hwloc_debug(const char *s __hwloc_attribute_unused, ...) +{ +#ifdef HWLOC_DEBUG + if (hwloc_debug_enabled()) { + va_list ap; + va_start(ap, s); + vfprintf(stderr, s, ap); + va_end(ap); + } +#endif +} + +#ifdef HWLOC_DEBUG +#define hwloc_debug_bitmap(fmt, bitmap) do { \ +if (hwloc_debug_enabled()) { \ + char *s; \ + hwloc_bitmap_asprintf(&s, bitmap); \ + fprintf(stderr, fmt, s); \ + free(s); \ +} } while (0) +#define hwloc_debug_1arg_bitmap(fmt, arg1, bitmap) do { \ +if (hwloc_debug_enabled()) { \ + char *s; \ + hwloc_bitmap_asprintf(&s, bitmap); \ + fprintf(stderr, fmt, arg1, s); \ + free(s); \ +} } while (0) +#define hwloc_debug_2args_bitmap(fmt, arg1, arg2, bitmap) do { \ +if (hwloc_debug_enabled()) { \ + char *s; \ + hwloc_bitmap_asprintf(&s, bitmap); \ + fprintf(stderr, fmt, arg1, arg2, s); \ + free(s); \ +} } while (0) +#else +#define hwloc_debug_bitmap(s, bitmap) do { } while(0) +#define hwloc_debug_1arg_bitmap(s, arg1, bitmap) do { } while(0) +#define hwloc_debug_2args_bitmap(s, arg1, arg2, bitmap) do { } while(0) +#endif + +#endif /* HWLOC_DEBUG_H */ diff --git a/src/3rdparty/hwloc/include/private/internal-components.h b/src/3rdparty/hwloc/include/private/internal-components.h new file mode 100644 index 00000000..b138a0eb --- /dev/null +++ b/src/3rdparty/hwloc/include/private/internal-components.h @@ -0,0 +1,41 @@ +/* + * Copyright © 2018 Inria. All rights reserved. + * + * See COPYING in top-level directory. + */ + +/* List of components defined inside hwloc */ + +#ifndef PRIVATE_INTERNAL_COMPONENTS_H +#define PRIVATE_INTERNAL_COMPONENTS_H + +/* global discovery */ +HWLOC_DECLSPEC extern const struct hwloc_component hwloc_xml_component; +HWLOC_DECLSPEC extern const struct hwloc_component hwloc_synthetic_component; + +/* CPU discovery */ +HWLOC_DECLSPEC extern const struct hwloc_component hwloc_aix_component; +HWLOC_DECLSPEC extern const struct hwloc_component hwloc_bgq_component; +HWLOC_DECLSPEC extern const struct hwloc_component hwloc_darwin_component; +HWLOC_DECLSPEC extern const struct hwloc_component hwloc_freebsd_component; +HWLOC_DECLSPEC extern const struct hwloc_component hwloc_hpux_component; +HWLOC_DECLSPEC extern const struct hwloc_component hwloc_linux_component; +HWLOC_DECLSPEC extern const struct hwloc_component hwloc_netbsd_component; +HWLOC_DECLSPEC extern const struct hwloc_component hwloc_noos_component; +HWLOC_DECLSPEC extern const struct hwloc_component hwloc_solaris_component; +HWLOC_DECLSPEC extern const struct hwloc_component hwloc_windows_component; +HWLOC_DECLSPEC extern const struct hwloc_component hwloc_x86_component; + +/* I/O discovery */ +HWLOC_DECLSPEC extern const struct hwloc_component hwloc_cuda_component; +HWLOC_DECLSPEC extern const struct hwloc_component hwloc_gl_component; +HWLOC_DECLSPEC extern const struct hwloc_component hwloc_linuxio_component; +HWLOC_DECLSPEC extern const struct hwloc_component hwloc_nvml_component; +HWLOC_DECLSPEC extern const struct hwloc_component hwloc_opencl_component; +HWLOC_DECLSPEC extern const struct hwloc_component hwloc_pci_component; + +/* XML backend */ +HWLOC_DECLSPEC extern const struct hwloc_component hwloc_xml_nolibxml_component; +HWLOC_DECLSPEC extern const struct hwloc_component hwloc_xml_libxml_component; + +#endif /* PRIVATE_INTERNAL_COMPONENTS_H */ diff --git a/src/3rdparty/hwloc/include/private/misc.h b/src/3rdparty/hwloc/include/private/misc.h new file mode 100644 index 00000000..66608bc7 --- /dev/null +++ b/src/3rdparty/hwloc/include/private/misc.h @@ -0,0 +1,583 @@ +/* + * Copyright © 2009 CNRS + * Copyright © 2009-2018 Inria. All rights reserved. + * Copyright © 2009-2012 Université Bordeaux + * Copyright © 2011 Cisco Systems, Inc. All rights reserved. + * See COPYING in top-level directory. + */ + +/* Misc macros and inlines. */ + +#ifndef HWLOC_PRIVATE_MISC_H +#define HWLOC_PRIVATE_MISC_H + +#include +#include +#include + +#ifdef HWLOC_HAVE_DECL_STRNCASECMP +#ifdef HAVE_STRINGS_H +#include +#endif +#else +#ifdef HAVE_CTYPE_H +#include +#endif +#endif + +#define HWLOC_BITS_PER_LONG (HWLOC_SIZEOF_UNSIGNED_LONG * 8) +#define HWLOC_BITS_PER_INT (HWLOC_SIZEOF_UNSIGNED_INT * 8) + +#if (HWLOC_BITS_PER_LONG != 32) && (HWLOC_BITS_PER_LONG != 64) +#error "unknown size for unsigned long." +#endif + +#if (HWLOC_BITS_PER_INT != 16) && (HWLOC_BITS_PER_INT != 32) && (HWLOC_BITS_PER_INT != 64) +#error "unknown size for unsigned int." +#endif + +/* internal-use-only value for when we don't know the type or don't have any value */ +#define HWLOC_OBJ_TYPE_NONE ((hwloc_obj_type_t) -1) + +/** + * ffsl helpers. + */ + +#if defined(HWLOC_HAVE_BROKEN_FFS) + +/* System has a broken ffs(). + * We must check the before __GNUC__ or HWLOC_HAVE_FFSL + */ +# define HWLOC_NO_FFS + +#elif defined(__GNUC__) + +# if (__GNUC__ >= 4) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4)) + /* Starting from 3.4, gcc has a long variant. */ +# define hwloc_ffsl(x) __builtin_ffsl(x) +# else +# define hwloc_ffs(x) __builtin_ffs(x) +# define HWLOC_NEED_FFSL +# endif + +#elif defined(HWLOC_HAVE_FFSL) + +# ifndef HWLOC_HAVE_DECL_FFSL +extern int ffsl(long) __hwloc_attribute_const; +# endif + +# define hwloc_ffsl(x) ffsl(x) + +#elif defined(HWLOC_HAVE_FFS) + +# ifndef HWLOC_HAVE_DECL_FFS +extern int ffs(int) __hwloc_attribute_const; +# endif + +# define hwloc_ffs(x) ffs(x) +# define HWLOC_NEED_FFSL + +#else /* no ffs implementation */ + +# define HWLOC_NO_FFS + +#endif + +#ifdef HWLOC_NO_FFS + +/* no ffs or it is known to be broken */ +static __hwloc_inline int +hwloc_ffsl_manual(unsigned long x) __hwloc_attribute_const; +static __hwloc_inline int +hwloc_ffsl_manual(unsigned long x) +{ + int i; + + if (!x) + return 0; + + i = 1; +#if HWLOC_BITS_PER_LONG >= 64 + if (!(x & 0xfffffffful)) { + x >>= 32; + i += 32; + } +#endif + if (!(x & 0xffffu)) { + x >>= 16; + i += 16; + } + if (!(x & 0xff)) { + x >>= 8; + i += 8; + } + if (!(x & 0xf)) { + x >>= 4; + i += 4; + } + if (!(x & 0x3)) { + x >>= 2; + i += 2; + } + if (!(x & 0x1)) { + x >>= 1; + i += 1; + } + + return i; +} +/* always define hwloc_ffsl as a macro, to avoid renaming breakage */ +#define hwloc_ffsl hwloc_ffsl_manual + +#elif defined(HWLOC_NEED_FFSL) + +/* We only have an int ffs(int) implementation, build a long one. */ + +/* First make it 32 bits if it was only 16. */ +static __hwloc_inline int +hwloc_ffs32(unsigned long x) __hwloc_attribute_const; +static __hwloc_inline int +hwloc_ffs32(unsigned long x) +{ +#if HWLOC_BITS_PER_INT == 16 + int low_ffs, hi_ffs; + + low_ffs = hwloc_ffs(x & 0xfffful); + if (low_ffs) + return low_ffs; + + hi_ffs = hwloc_ffs(x >> 16); + if (hi_ffs) + return hi_ffs + 16; + + return 0; +#else + return hwloc_ffs(x); +#endif +} + +/* Then make it 64 bit if longs are. */ +static __hwloc_inline int +hwloc_ffsl_from_ffs32(unsigned long x) __hwloc_attribute_const; +static __hwloc_inline int +hwloc_ffsl_from_ffs32(unsigned long x) +{ +#if HWLOC_BITS_PER_LONG == 64 + int low_ffs, hi_ffs; + + low_ffs = hwloc_ffs32(x & 0xfffffffful); + if (low_ffs) + return low_ffs; + + hi_ffs = hwloc_ffs32(x >> 32); + if (hi_ffs) + return hi_ffs + 32; + + return 0; +#else + return hwloc_ffs32(x); +#endif +} +/* always define hwloc_ffsl as a macro, to avoid renaming breakage */ +#define hwloc_ffsl hwloc_ffsl_from_ffs32 + +#endif + +/** + * flsl helpers. + */ +#ifdef __GNUC_____ + +# if (__GNUC__ >= 4) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4)) +# define hwloc_flsl(x) ((x) ? (8*sizeof(long) - __builtin_clzl(x)) : 0) +# else +# define hwloc_fls(x) ((x) ? (8*sizeof(int) - __builtin_clz(x)) : 0) +# define HWLOC_NEED_FLSL +# endif + +#elif defined(HWLOC_HAVE_FLSL) + +# ifndef HWLOC_HAVE_DECL_FLSL +extern int flsl(long) __hwloc_attribute_const; +# endif + +# define hwloc_flsl(x) flsl(x) + +#elif defined(HWLOC_HAVE_CLZL) + +# ifndef HWLOC_HAVE_DECL_CLZL +extern int clzl(long) __hwloc_attribute_const; +# endif + +# define hwloc_flsl(x) ((x) ? (8*sizeof(long) - clzl(x)) : 0) + +#elif defined(HWLOC_HAVE_FLS) + +# ifndef HWLOC_HAVE_DECL_FLS +extern int fls(int) __hwloc_attribute_const; +# endif + +# define hwloc_fls(x) fls(x) +# define HWLOC_NEED_FLSL + +#elif defined(HWLOC_HAVE_CLZ) + +# ifndef HWLOC_HAVE_DECL_CLZ +extern int clz(int) __hwloc_attribute_const; +# endif + +# define hwloc_fls(x) ((x) ? (8*sizeof(int) - clz(x)) : 0) +# define HWLOC_NEED_FLSL + +#else /* no fls implementation */ + +static __hwloc_inline int +hwloc_flsl_manual(unsigned long x) __hwloc_attribute_const; +static __hwloc_inline int +hwloc_flsl_manual(unsigned long x) +{ + int i = 0; + + if (!x) + return 0; + + i = 1; +#if HWLOC_BITS_PER_LONG >= 64 + if ((x & 0xffffffff00000000ul)) { + x >>= 32; + i += 32; + } +#endif + if ((x & 0xffff0000u)) { + x >>= 16; + i += 16; + } + if ((x & 0xff00)) { + x >>= 8; + i += 8; + } + if ((x & 0xf0)) { + x >>= 4; + i += 4; + } + if ((x & 0xc)) { + x >>= 2; + i += 2; + } + if ((x & 0x2)) { + x >>= 1; + i += 1; + } + + return i; +} +/* always define hwloc_flsl as a macro, to avoid renaming breakage */ +#define hwloc_flsl hwloc_flsl_manual + +#endif + +#ifdef HWLOC_NEED_FLSL + +/* We only have an int fls(int) implementation, build a long one. */ + +/* First make it 32 bits if it was only 16. */ +static __hwloc_inline int +hwloc_fls32(unsigned long x) __hwloc_attribute_const; +static __hwloc_inline int +hwloc_fls32(unsigned long x) +{ +#if HWLOC_BITS_PER_INT == 16 + int low_fls, hi_fls; + + hi_fls = hwloc_fls(x >> 16); + if (hi_fls) + return hi_fls + 16; + + low_fls = hwloc_fls(x & 0xfffful); + if (low_fls) + return low_fls; + + return 0; +#else + return hwloc_fls(x); +#endif +} + +/* Then make it 64 bit if longs are. */ +static __hwloc_inline int +hwloc_flsl_from_fls32(unsigned long x) __hwloc_attribute_const; +static __hwloc_inline int +hwloc_flsl_from_fls32(unsigned long x) +{ +#if HWLOC_BITS_PER_LONG == 64 + int low_fls, hi_fls; + + hi_fls = hwloc_fls32(x >> 32); + if (hi_fls) + return hi_fls + 32; + + low_fls = hwloc_fls32(x & 0xfffffffful); + if (low_fls) + return low_fls; + + return 0; +#else + return hwloc_fls32(x); +#endif +} +/* always define hwloc_flsl as a macro, to avoid renaming breakage */ +#define hwloc_flsl hwloc_flsl_from_fls32 + +#endif + +static __hwloc_inline int +hwloc_weight_long(unsigned long w) __hwloc_attribute_const; +static __hwloc_inline int +hwloc_weight_long(unsigned long w) +{ +#if HWLOC_BITS_PER_LONG == 32 +#if (__GNUC__ >= 4) || ((__GNUC__ == 3) && (__GNUC_MINOR__) >= 4) + return __builtin_popcount(w); +#else + unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555); + res = (res & 0x33333333) + ((res >> 2) & 0x33333333); + res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F); + res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF); + return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF); +#endif +#else /* HWLOC_BITS_PER_LONG == 32 */ +#if (__GNUC__ >= 4) || ((__GNUC__ == 3) && (__GNUC_MINOR__) >= 4) + return __builtin_popcountll(w); +#else + unsigned long res; + res = (w & 0x5555555555555555ul) + ((w >> 1) & 0x5555555555555555ul); + res = (res & 0x3333333333333333ul) + ((res >> 2) & 0x3333333333333333ul); + res = (res & 0x0F0F0F0F0F0F0F0Ful) + ((res >> 4) & 0x0F0F0F0F0F0F0F0Ful); + res = (res & 0x00FF00FF00FF00FFul) + ((res >> 8) & 0x00FF00FF00FF00FFul); + res = (res & 0x0000FFFF0000FFFFul) + ((res >> 16) & 0x0000FFFF0000FFFFul); + return (res & 0x00000000FFFFFFFFul) + ((res >> 32) & 0x00000000FFFFFFFFul); +#endif +#endif /* HWLOC_BITS_PER_LONG == 64 */ +} + +#if !HAVE_DECL_STRTOULL && defined(HAVE_STRTOULL) +unsigned long long int strtoull(const char *nptr, char **endptr, int base); +#endif + +static __hwloc_inline int hwloc_strncasecmp(const char *s1, const char *s2, size_t n) +{ +#ifdef HWLOC_HAVE_DECL_STRNCASECMP + return strncasecmp(s1, s2, n); +#else + while (n) { + char c1 = tolower(*s1), c2 = tolower(*s2); + if (!c1 || !c2 || c1 != c2) + return c1-c2; + n--; s1++; s2++; + } + return 0; +#endif +} + +static __hwloc_inline hwloc_obj_type_t hwloc_cache_type_by_depth_type(unsigned depth, hwloc_obj_cache_type_t type) +{ + if (type == HWLOC_OBJ_CACHE_INSTRUCTION) { + if (depth >= 1 && depth <= 3) + return HWLOC_OBJ_L1ICACHE + depth-1; + else + return HWLOC_OBJ_TYPE_NONE; + } else { + if (depth >= 1 && depth <= 5) + return HWLOC_OBJ_L1CACHE + depth-1; + else + return HWLOC_OBJ_TYPE_NONE; + } +} + +#define HWLOC_BITMAP_EQUAL 0 /* Bitmaps are equal */ +#define HWLOC_BITMAP_INCLUDED 1 /* First bitmap included in second */ +#define HWLOC_BITMAP_CONTAINS 2 /* First bitmap contains second */ +#define HWLOC_BITMAP_INTERSECTS 3 /* Bitmaps intersect without any inclusion */ +#define HWLOC_BITMAP_DIFFERENT 4 /* Bitmaps do not intersect */ + +/* Compare bitmaps \p bitmap1 and \p bitmap2 from an inclusion point of view. */ +HWLOC_DECLSPEC int hwloc_bitmap_compare_inclusion(hwloc_const_bitmap_t bitmap1, hwloc_const_bitmap_t bitmap2) __hwloc_attribute_pure; + +/* Return a stringified PCI class. */ +HWLOC_DECLSPEC extern const char * hwloc_pci_class_string(unsigned short class_id); + +/* Parse a PCI link speed (GT/s) string from Linux sysfs */ +#ifdef HWLOC_LINUX_SYS +#include /* for atof() */ +static __hwloc_inline float +hwloc_linux_pci_link_speed_from_string(const char *string) +{ + /* don't parse Gen1 with atof() since it expects a localized string + * while the kernel sysfs files aren't. + */ + if (!strncmp(string, "2.5 ", 4)) + /* "2.5 GT/s" is Gen1 with 8/10 encoding */ + return 2.5 * .8; + + /* also hardwire Gen2 since it also has a specific encoding */ + if (!strncmp(string, "5 ", 2)) + /* "5 GT/s" is Gen2 with 8/10 encoding */ + return 5 * .8; + + /* handle Gen3+ in a generic way */ + return atof(string) * 128./130; /* Gen3+ encoding is 128/130 */ +} +#endif + +/* Traverse children of a parent */ +#define for_each_child(child, parent) for(child = parent->first_child; child; child = child->next_sibling) +#define for_each_memory_child(child, parent) for(child = parent->memory_first_child; child; child = child->next_sibling) +#define for_each_io_child(child, parent) for(child = parent->io_first_child; child; child = child->next_sibling) +#define for_each_misc_child(child, parent) for(child = parent->misc_first_child; child; child = child->next_sibling) + +/* Any object attached to normal children */ +static __hwloc_inline int hwloc__obj_type_is_normal (hwloc_obj_type_t type) +{ + /* type contiguity is asserted in topology_check() */ + return type <= HWLOC_OBJ_GROUP; +} + +/* Any object attached to memory children, currently only NUMA nodes */ +static __hwloc_inline int hwloc__obj_type_is_memory (hwloc_obj_type_t type) +{ + /* type contiguity is asserted in topology_check() */ + return type == HWLOC_OBJ_NUMANODE; +} + +/* I/O or Misc object, without cpusets or nodesets. */ +static __hwloc_inline int hwloc__obj_type_is_special (hwloc_obj_type_t type) +{ + /* type contiguity is asserted in topology_check() */ + return type >= HWLOC_OBJ_BRIDGE && type <= HWLOC_OBJ_MISC; +} + +/* Any object attached to io children */ +static __hwloc_inline int hwloc__obj_type_is_io (hwloc_obj_type_t type) +{ + /* type contiguity is asserted in topology_check() */ + return type >= HWLOC_OBJ_BRIDGE && type <= HWLOC_OBJ_OS_DEVICE; +} + +static __hwloc_inline int +hwloc__obj_type_is_cache(hwloc_obj_type_t type) +{ + /* type contiguity is asserted in topology_check() */ + return (type >= HWLOC_OBJ_L1CACHE && type <= HWLOC_OBJ_L3ICACHE); +} + +static __hwloc_inline int +hwloc__obj_type_is_dcache(hwloc_obj_type_t type) +{ + /* type contiguity is asserted in topology_check() */ + return (type >= HWLOC_OBJ_L1CACHE && type <= HWLOC_OBJ_L5CACHE); +} + +/** \brief Check whether an object is a Instruction Cache. */ +static __hwloc_inline int +hwloc__obj_type_is_icache(hwloc_obj_type_t type) +{ + /* type contiguity is asserted in topology_check() */ + return (type >= HWLOC_OBJ_L1ICACHE && type <= HWLOC_OBJ_L3ICACHE); +} + +#ifdef HAVE_USELOCALE +#include "locale.h" +#ifdef HAVE_XLOCALE_H +#include "xlocale.h" +#endif +#define hwloc_localeswitch_declare locale_t __old_locale = (locale_t)0, __new_locale +#define hwloc_localeswitch_init() do { \ + __new_locale = newlocale(LC_ALL_MASK, "C", (locale_t)0); \ + if (__new_locale != (locale_t)0) \ + __old_locale = uselocale(__new_locale); \ +} while (0) +#define hwloc_localeswitch_fini() do { \ + if (__new_locale != (locale_t)0) { \ + uselocale(__old_locale); \ + freelocale(__new_locale); \ + } \ +} while(0) +#else /* HAVE_USELOCALE */ +#if __HWLOC_HAVE_ATTRIBUTE_UNUSED +#define hwloc_localeswitch_declare int __dummy_nolocale __hwloc_attribute_unused +#define hwloc_localeswitch_init() +#else +#define hwloc_localeswitch_declare int __dummy_nolocale +#define hwloc_localeswitch_init() (void)__dummy_nolocale +#endif +#define hwloc_localeswitch_fini() +#endif /* HAVE_USELOCALE */ + +#if !HAVE_DECL_FABSF +#define fabsf(f) fabs((double)(f)) +#endif + +#if !HAVE_DECL_MODFF +#define modff(x,iptr) (float)modf((double)x,(double *)iptr) +#endif + +#if HAVE_DECL__SC_PAGE_SIZE +#define hwloc_getpagesize() sysconf(_SC_PAGE_SIZE) +#elif HAVE_DECL__SC_PAGESIZE +#define hwloc_getpagesize() sysconf(_SC_PAGESIZE) +#elif defined HAVE_GETPAGESIZE +#define hwloc_getpagesize() getpagesize() +#else +#undef hwloc_getpagesize +#endif + +#if HWLOC_HAVE_ATTRIBUTE_FORMAT +# define __hwloc_attribute_format(type, str, arg) __attribute__((__format__(type, str, arg))) +#else +# define __hwloc_attribute_format(type, str, arg) +#endif + +#define hwloc_memory_size_printf_value(_size, _verbose) \ + ((_size) < (10ULL<<20) || (_verbose) ? (((_size)>>9)+1)>>1 : (_size) < (10ULL<<30) ? (((_size)>>19)+1)>>1 : (_size) < (10ULL<<40) ? (((_size)>>29)+1)>>1 : (((_size)>>39)+1)>>1) +#define hwloc_memory_size_printf_unit(_size, _verbose) \ + ((_size) < (10ULL<<20) || (_verbose) ? "KB" : (_size) < (10ULL<<30) ? "MB" : (_size) < (10ULL<<40) ? "GB" : "TB") + +#ifdef HWLOC_WIN_SYS +# ifndef HAVE_SSIZE_T +typedef SSIZE_T ssize_t; +# endif +# if !HAVE_DECL_STRTOULL && !defined(HAVE_STRTOULL) +# define strtoull _strtoui64 +# endif +# ifndef S_ISREG +# define S_ISREG(m) ((m) & S_IFREG) +# endif +# ifndef S_ISDIR +# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +# endif +# ifndef S_IRWXU +# define S_IRWXU 00700 +# endif +# ifndef HWLOC_HAVE_DECL_STRCASECMP +# define strcasecmp _stricmp +# endif +# if !HAVE_DECL_SNPRINTF +# define snprintf _snprintf +# endif +# if HAVE_DECL__STRDUP +# define strdup _strdup +# endif +# if HAVE_DECL__PUTENV +# define putenv _putenv +# endif +#endif + +#if defined HWLOC_WIN_SYS && !defined __MINGW32__ && !defined(__CYGWIN__) +/* MSVC doesn't support C99 variable-length array */ +#include +#define HWLOC_VLA(_type, _name, _nb) _type *_name = (_type*) _alloca((_nb)*sizeof(_type)) +#else +#define HWLOC_VLA(_type, _name, _nb) _type _name[_nb] +#endif + +#endif /* HWLOC_PRIVATE_MISC_H */ diff --git a/src/3rdparty/hwloc/include/private/netloc.h b/src/3rdparty/hwloc/include/private/netloc.h new file mode 100644 index 00000000..c070c54c --- /dev/null +++ b/src/3rdparty/hwloc/include/private/netloc.h @@ -0,0 +1,578 @@ +/* + * Copyright © 2014 Cisco Systems, Inc. All rights reserved. + * Copyright © 2013-2014 University of Wisconsin-La Crosse. + * All rights reserved. + * Copyright © 2015-2017 Inria. All rights reserved. + * + * $COPYRIGHT$ + * + * Additional copyrights may follow + * See COPYING in top-level directory. + * + * $HEADER$ + */ + +#ifndef _NETLOC_PRIVATE_H_ +#define _NETLOC_PRIVATE_H_ + +#include +#include +#include +#include +#include + +#define NETLOCFILE_VERSION 1 + +#ifdef NETLOC_SCOTCH +#include +#include +#define NETLOC_int SCOTCH_Num +#else +#define NETLOC_int int +#endif + +/* + * "Import" a few things from hwloc + */ +#define __netloc_attribute_unused __hwloc_attribute_unused +#define __netloc_attribute_malloc __hwloc_attribute_malloc +#define __netloc_attribute_const __hwloc_attribute_const +#define __netloc_attribute_pure __hwloc_attribute_pure +#define __netloc_attribute_deprecated __hwloc_attribute_deprecated +#define __netloc_attribute_may_alias __hwloc_attribute_may_alias +#define NETLOC_DECLSPEC HWLOC_DECLSPEC + + +/********************************************************************** + * Types + **********************************************************************/ + +/** + * Definitions for Comparators + * \sa These are the return values from the following functions: + * netloc_network_compare, netloc_dt_edge_t_compare, netloc_dt_node_t_compare + */ +typedef enum { + NETLOC_CMP_SAME = 0, /**< Compared as the Same */ + NETLOC_CMP_SIMILAR = -1, /**< Compared as Similar, but not the Same */ + NETLOC_CMP_DIFF = -2 /**< Compared as Different */ +} netloc_compare_type_t; + +/** + * Enumerated type for the various types of supported networks + */ +typedef enum { + NETLOC_NETWORK_TYPE_ETHERNET = 1, /**< Ethernet network */ + NETLOC_NETWORK_TYPE_INFINIBAND = 2, /**< InfiniBand network */ + NETLOC_NETWORK_TYPE_INVALID = 3 /**< Invalid network */ +} netloc_network_type_t; + +/** + * Enumerated type for the various types of supported topologies + */ +typedef enum { + NETLOC_TOPOLOGY_TYPE_INVALID = -1, /**< Invalid */ + NETLOC_TOPOLOGY_TYPE_TREE = 1, /**< Tree */ +} netloc_topology_type_t; + +/** + * Enumerated type for the various types of nodes + */ +typedef enum { + NETLOC_NODE_TYPE_HOST = 0, /**< Host (a.k.a., network addressable endpoint - e.g., MAC Address) node */ + NETLOC_NODE_TYPE_SWITCH = 1, /**< Switch node */ + NETLOC_NODE_TYPE_INVALID = 2 /**< Invalid node */ +} netloc_node_type_t; + +typedef enum { + NETLOC_ARCH_TREE = 0, /* Fat tree */ +} netloc_arch_type_t; + + +/* Pre declarations to avoid inter dependency problems */ +/** \cond IGNORE */ +struct netloc_topology_t; +typedef struct netloc_topology_t netloc_topology_t; +struct netloc_node_t; +typedef struct netloc_node_t netloc_node_t; +struct netloc_edge_t; +typedef struct netloc_edge_t netloc_edge_t; +struct netloc_physical_link_t; +typedef struct netloc_physical_link_t netloc_physical_link_t; +struct netloc_path_t; +typedef struct netloc_path_t netloc_path_t; + +struct netloc_arch_tree_t; +typedef struct netloc_arch_tree_t netloc_arch_tree_t; +struct netloc_arch_node_t; +typedef struct netloc_arch_node_t netloc_arch_node_t; +struct netloc_arch_node_slot_t; +typedef struct netloc_arch_node_slot_t netloc_arch_node_slot_t; +struct netloc_arch_t; +typedef struct netloc_arch_t netloc_arch_t; +/** \endcond */ + +/** + * \struct netloc_topology_t + * \brief Netloc Topology Context + * + * An opaque data structure used to reference a network topology. + * + * \note Must be initialized with \ref netloc_topology_construct() + */ +struct netloc_topology_t { + /** Topology path */ + char *topopath; + /** Subnet ID */ + char *subnet_id; + + /** Node List */ + netloc_node_t *nodes; /* Hash table of nodes by physical_id */ + netloc_node_t *nodesByHostname; /* Hash table of nodes by hostname */ + + netloc_physical_link_t *physical_links; /* Hash table with physcial links */ + + /** Partition List */ + UT_array *partitions; + + /** Hwloc topology List */ + char *hwlocpath; + UT_array *topos; + hwloc_topology_t *hwloc_topos; + + /** Type of the graph */ + netloc_topology_type_t type; +}; + +/** + * \brief Netloc Node Type + * + * Represents the concept of a node (a.k.a., vertex, endpoint) within a network + * graph. This could be a server or a network switch. The \ref node_type parameter + * will distinguish the exact type of node this represents in the graph. + */ +struct netloc_node_t { + UT_hash_handle hh; /* makes this structure hashable with physical_id */ + UT_hash_handle hh2; /* makes this structure hashable with hostname */ + + /** Physical ID of the node */ + char physical_id[20]; + + /** Logical ID of the node (if any) */ + int logical_id; + + /** Type of the node */ + netloc_node_type_t type; + + /* Pointer to physical_links */ + UT_array *physical_links; + + /** Description information from discovery (if any) */ + char *description; + + /** + * Application-given private data pointer. + * Initialized to NULL, and not used by the netloc library. + */ + void * userdata; + + /** Outgoing edges from this node */ + netloc_edge_t *edges; + + UT_array *subnodes; /* the group of nodes for the virtual nodes */ + + netloc_path_t *paths; + + char *hostname; + + UT_array *partitions; /* index in the list from the topology */ + + hwloc_topology_t hwlocTopo; + int hwlocTopoIdx; +}; + +/** + * \brief Netloc Edge Type + * + * Represents the concept of a directed edge within a network graph. + * + * \note We do not point to the netloc_node_t structure directly to + * simplify the representation, and allow the information to more easily + * be entered into the data store without circular references. + * \todo JJH Is the note above still true? + */ +struct netloc_edge_t { + UT_hash_handle hh; /* makes this structure hashable */ + + netloc_node_t *dest; + + int id; + + /** Pointers to the parent node */ + netloc_node_t *node; + + /* Pointer to physical_links */ + UT_array *physical_links; + + /** total gbits of the links */ + float total_gbits; + + UT_array *partitions; /* index in the list from the topology */ + + UT_array *subnode_edges; /* for edges going to virtual nodes */ + + struct netloc_edge_t *other_way; + + /** + * Application-given private data pointer. + * Initialized to NULL, and not used by the netloc library. + */ + void * userdata; +}; + + +struct netloc_physical_link_t { + UT_hash_handle hh; /* makes this structure hashable */ + + int id; // TODO long long + netloc_node_t *src; + netloc_node_t *dest; + int ports[2]; + char *width; + char *speed; + + netloc_edge_t *edge; + + int other_way_id; + struct netloc_physical_link_t *other_way; + + UT_array *partitions; /* index in the list from the topology */ + + /** gbits of the link from speed and width */ + float gbits; + + /** Description information from discovery (if any) */ + char *description; +}; + +struct netloc_path_t { + UT_hash_handle hh; /* makes this structure hashable */ + char dest_id[20]; + UT_array *links; +}; + + +/********************************************************************** + * Architecture structures + **********************************************************************/ +struct netloc_arch_tree_t { + NETLOC_int num_levels; + NETLOC_int *degrees; + NETLOC_int *cost; +}; + +struct netloc_arch_node_t { + UT_hash_handle hh; /* makes this structure hashable */ + char *name; /* Hash key */ + netloc_node_t *node; /* Corresponding node */ + int idx_in_topo; /* idx with ghost hosts to have complete topo */ + int num_slots; /* it is not the real number of slots but the maximum slot idx */ + int *slot_idx; /* corresponding idx in slot_tree */ + int *slot_os_idx; /* corresponding os index for each leaf in tree */ + netloc_arch_tree_t *slot_tree; /* Tree built from hwloc */ + int num_current_slots; /* Number of PUs */ + NETLOC_int *current_slots; /* indices in the complete tree */ + int *slot_ranks; /* corresponding MPI rank for each leaf in tree */ +}; + +struct netloc_arch_node_slot_t { + netloc_arch_node_t *node; + int slot; +}; + +struct netloc_arch_t { + netloc_topology_t *topology; + int has_slots; /* if slots are included in the architecture */ + netloc_arch_type_t type; + union { + netloc_arch_tree_t *node_tree; + netloc_arch_tree_t *global_tree; + } arch; + netloc_arch_node_t *nodes_by_name; + netloc_arch_node_slot_t *node_slot_by_idx; /* node_slot by index in complete topo */ + NETLOC_int num_current_hosts; /* if has_slots, host is a slot, else host is a node */ + NETLOC_int *current_hosts; /* indices in the complete topology */ +}; + +/********************************************************************** + * Topology Functions + **********************************************************************/ +/** + * Allocate a topology handle. + * + * User is responsible for calling \ref netloc_detach on the topology handle. + * The network parameter information is deep copied into the topology handle, so the + * user may destruct the network handle after calling this function and/or reuse + * the network handle. + * + * \returns NETLOC_SUCCESS on success + * \returns NETLOC_ERROR upon an error. + */ +netloc_topology_t *netloc_topology_construct(char *path); + +/** + * Destruct a topology handle + * + * \param topology A valid pointer to a \ref netloc_topology_t handle created + * from a prior call to \ref netloc_topology_construct. + * + * \returns NETLOC_SUCCESS on success + * \returns NETLOC_ERROR upon an error. + */ +int netloc_topology_destruct(netloc_topology_t *topology); + +int netloc_topology_find_partition_idx(netloc_topology_t *topology, char *partition_name); + +int netloc_topology_read_hwloc(netloc_topology_t *topology, int num_nodes, + netloc_node_t **node_list); + +#define netloc_topology_iter_partitions(topology,partition) \ + for ((partition) = (char **)utarray_front(topology->partitions); \ + (partition) != NULL; \ + (partition) = (char **)utarray_next(topology->partitions, partition)) + +#define netloc_topology_iter_hwloctopos(topology,hwloctopo) \ + for ((hwloctopo) = (char **)utarray_front(topology->topos); \ + (hwloctopo) != NULL; \ + (hwloctopo) = (char **)utarray_next(topology->topos, hwloctopo)) + +#define netloc_topology_find_node(topology,node_id,node) \ + HASH_FIND_STR(topology->nodes, node_id, node) + +#define netloc_topology_iter_nodes(topology,node,_tmp) \ + HASH_ITER(hh, topology->nodes, node, _tmp) + +#define netloc_topology_num_nodes(topology) \ + HASH_COUNT(topology->nodes) + +/*************************************************/ + + +/** + * Constructor for netloc_node_t + * + * User is responsible for calling the destructor on the handle. + * + * Returns + * A newly allocated pointer to the network information. + */ +netloc_node_t *netloc_node_construct(void); + +/** + * Destructor for netloc_node_t + * + * \param node A valid node handle + * + * Returns + * NETLOC_SUCCESS on success + * NETLOC_ERROR on error + */ +int netloc_node_destruct(netloc_node_t *node); + +char *netloc_node_pretty_print(netloc_node_t* node); + +#define netloc_node_get_num_subnodes(node) \ + utarray_len((node)->subnodes) + +#define netloc_node_get_subnode(node,i) \ + (*(netloc_node_t **)utarray_eltptr((node)->subnodes, (i))) + +#define netloc_node_get_num_edges(node) \ + utarray_len((node)->edges) + +#define netloc_node_get_edge(node,i) \ + (*(netloc_edge_t **)utarray_eltptr((node)->edges, (i))) + +#define netloc_node_iter_edges(node,edge,_tmp) \ + HASH_ITER(hh, node->edges, edge, _tmp) + +#define netloc_node_iter_paths(node,path,_tmp) \ + HASH_ITER(hh, node->paths, path, _tmp) + +#define netloc_node_is_host(node) \ + (node->type == NETLOC_NODE_TYPE_HOST) + +#define netloc_node_is_switch(node) \ + (node->type == NETLOC_NODE_TYPE_SWITCH) + +#define netloc_node_iter_paths(node, path,_tmp) \ + HASH_ITER(hh, node->paths, path, _tmp) + +int netloc_node_is_in_partition(netloc_node_t *node, int partition); + +/*************************************************/ + + +/** + * Constructor for netloc_edge_t + * + * User is responsible for calling the destructor on the handle. + * + * Returns + * A newly allocated pointer to the edge information. + */ +netloc_edge_t *netloc_edge_construct(void); + +/** + * Destructor for netloc_edge_t + * + * \param edge A valid edge handle + * + * Returns + * NETLOC_SUCCESS on success + * NETLOC_ERROR on error + */ +int netloc_edge_destruct(netloc_edge_t *edge); + +char * netloc_edge_pretty_print(netloc_edge_t* edge); + +void netloc_edge_reset_uid(void); + +int netloc_edge_is_in_partition(netloc_edge_t *edge, int partition); + +#define netloc_edge_get_num_links(edge) \ + utarray_len((edge)->physical_links) + +#define netloc_edge_get_link(edge,i) \ + (*(netloc_physical_link_t **)utarray_eltptr((edge)->physical_links, (i))) + +#define netloc_edge_get_num_subedges(edge) \ + utarray_len((edge)->subnode_edges) + +#define netloc_edge_get_subedge(edge,i) \ + (*(netloc_edge_t **)utarray_eltptr((edge)->subnode_edges, (i))) + +/*************************************************/ + + +/** + * Constructor for netloc_physical_link_t + * + * User is responsible for calling the destructor on the handle. + * + * Returns + * A newly allocated pointer to the physical link information. + */ +netloc_physical_link_t * netloc_physical_link_construct(void); + +/** + * Destructor for netloc_physical_link_t + * + * Returns + * NETLOC_SUCCESS on success + * NETLOC_ERROR on error + */ +int netloc_physical_link_destruct(netloc_physical_link_t *link); + +char * netloc_link_pretty_print(netloc_physical_link_t* link); + +/*************************************************/ + + +netloc_path_t *netloc_path_construct(void); +int netloc_path_destruct(netloc_path_t *path); + + +/********************************************************************** + * Architecture functions + **********************************************************************/ + +netloc_arch_t * netloc_arch_construct(void); + +int netloc_arch_destruct(netloc_arch_t *arch); + +int netloc_arch_build(netloc_arch_t *arch, int add_slots); + +int netloc_arch_set_current_resources(netloc_arch_t *arch); + +int netloc_arch_set_global_resources(netloc_arch_t *arch); + +int netloc_arch_node_get_hwloc_info(netloc_arch_node_t *arch); + +void netloc_arch_tree_complete(netloc_arch_tree_t *tree, UT_array **down_degrees_by_level, + int num_hosts, int **parch_idx); + +NETLOC_int netloc_arch_tree_num_leaves(netloc_arch_tree_t *tree); + + +/********************************************************************** + * Access functions of various elements of the topology + **********************************************************************/ + +#define netloc_get_num_partitions(object) \ + utarray_len((object)->partitions) + +#define netloc_get_partition(object,i) \ + (*(int *)utarray_eltptr((object)->partitions, (i))) + + +#define netloc_path_iter_links(path,link) \ + for ((link) = (netloc_physical_link_t **)utarray_front(path->links); \ + (link) != NULL; \ + (link) = (netloc_physical_link_t **)utarray_next(path->links, link)) + +/********************************************************************** + * Misc functions + **********************************************************************/ + +/** + * Decode the network type + * + * \param net_type A valid member of the \ref netloc_network_type_t type + * + * \returns NULL if the type is invalid + * \returns A string for that \ref netloc_network_type_t type + */ +static inline const char * netloc_network_type_decode(netloc_network_type_t net_type) { + if( NETLOC_NETWORK_TYPE_ETHERNET == net_type ) { + return "ETH"; + } + else if( NETLOC_NETWORK_TYPE_INFINIBAND == net_type ) { + return "IB"; + } + else { + return NULL; + } +} + +/** + * Decode the node type + * + * \param node_type A valid member of the \ref netloc_node_type_t type + * + * \returns NULL if the type is invalid + * \returns A string for that \ref netloc_node_type_t type + */ +static inline const char * netloc_node_type_decode(netloc_node_type_t node_type) { + if( NETLOC_NODE_TYPE_SWITCH == node_type ) { + return "SW"; + } + else if( NETLOC_NODE_TYPE_HOST == node_type ) { + return "CA"; + } + else { + return NULL; + } +} + +ssize_t netloc_line_get(char **lineptr, size_t *n, FILE *stream); + +char *netloc_line_get_next_token(char **string, char c); + +int netloc_build_comm_mat(char *filename, int *pn, double ***pmat); + +#define STRDUP_IF_NOT_NULL(str) (NULL == str ? NULL : strdup(str)) +#define STR_EMPTY_IF_NULL(str) (NULL == str ? "" : str) + + +#endif // _NETLOC_PRIVATE_H_ diff --git a/src/3rdparty/hwloc/include/private/private.h b/src/3rdparty/hwloc/include/private/private.h new file mode 100644 index 00000000..8e3964ab --- /dev/null +++ b/src/3rdparty/hwloc/include/private/private.h @@ -0,0 +1,417 @@ +/* + * Copyright © 2009 CNRS + * Copyright © 2009-2019 Inria. All rights reserved. + * Copyright © 2009-2012 Université Bordeaux + * Copyright © 2009-2011 Cisco Systems, Inc. All rights reserved. + * + * See COPYING in top-level directory. + */ + +/* Internal types and helpers. */ + + +#ifdef HWLOC_INSIDE_PLUGIN +/* + * these declarations are internal only, they are not available to plugins + * (many functions below are internal static symbols). + */ +#error This file should not be used in plugins +#endif + + +#ifndef HWLOC_PRIVATE_H +#define HWLOC_PRIVATE_H + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_STDINT_H +#include +#endif +#ifdef HAVE_SYS_UTSNAME_H +#include +#endif +#include + +#define HWLOC_TOPOLOGY_ABI 0x20000 /* version of the layout of struct topology */ + +/***************************************************** + * WARNING: + * changes below in this structure (and its children) + * should cause a bump of HWLOC_TOPOLOGY_ABI. + *****************************************************/ + +struct hwloc_topology { + unsigned topology_abi; + + unsigned nb_levels; /* Number of horizontal levels */ + unsigned nb_levels_allocated; /* Number of levels allocated and zeroed in level_nbobjects and levels below */ + unsigned *level_nbobjects; /* Number of objects on each horizontal level */ + struct hwloc_obj ***levels; /* Direct access to levels, levels[l = 0 .. nblevels-1][0..level_nbobjects[l]] */ + unsigned long flags; + int type_depth[HWLOC_OBJ_TYPE_MAX]; + enum hwloc_type_filter_e type_filter[HWLOC_OBJ_TYPE_MAX]; + int is_thissystem; + int is_loaded; + int modified; /* >0 if objects were added/removed recently, which means a reconnect is needed */ + hwloc_pid_t pid; /* Process ID the topology is view from, 0 for self */ + void *userdata; + uint64_t next_gp_index; + + void *adopted_shmem_addr; + size_t adopted_shmem_length; + +#define HWLOC_NR_SLEVELS 5 +#define HWLOC_SLEVEL_NUMANODE 0 +#define HWLOC_SLEVEL_BRIDGE 1 +#define HWLOC_SLEVEL_PCIDEV 2 +#define HWLOC_SLEVEL_OSDEV 3 +#define HWLOC_SLEVEL_MISC 4 + /* order must match negative depth, it's asserted in setup_defaults() */ +#define HWLOC_SLEVEL_FROM_DEPTH(x) (HWLOC_TYPE_DEPTH_NUMANODE-(x)) +#define HWLOC_SLEVEL_TO_DEPTH(x) (HWLOC_TYPE_DEPTH_NUMANODE-(x)) + struct hwloc_special_level_s { + unsigned nbobjs; + struct hwloc_obj **objs; + struct hwloc_obj *first, *last; /* Temporarily used while listing object before building the objs array */ + } slevels[HWLOC_NR_SLEVELS]; + + hwloc_bitmap_t allowed_cpuset; + hwloc_bitmap_t allowed_nodeset; + + struct hwloc_binding_hooks { + int (*set_thisproc_cpubind)(hwloc_topology_t topology, hwloc_const_cpuset_t set, int flags); + int (*get_thisproc_cpubind)(hwloc_topology_t topology, hwloc_cpuset_t set, int flags); + int (*set_thisthread_cpubind)(hwloc_topology_t topology, hwloc_const_cpuset_t set, int flags); + int (*get_thisthread_cpubind)(hwloc_topology_t topology, hwloc_cpuset_t set, int flags); + int (*set_proc_cpubind)(hwloc_topology_t topology, hwloc_pid_t pid, hwloc_const_cpuset_t set, int flags); + int (*get_proc_cpubind)(hwloc_topology_t topology, hwloc_pid_t pid, hwloc_cpuset_t set, int flags); +#ifdef hwloc_thread_t + int (*set_thread_cpubind)(hwloc_topology_t topology, hwloc_thread_t tid, hwloc_const_cpuset_t set, int flags); + int (*get_thread_cpubind)(hwloc_topology_t topology, hwloc_thread_t tid, hwloc_cpuset_t set, int flags); +#endif + + int (*get_thisproc_last_cpu_location)(hwloc_topology_t topology, hwloc_cpuset_t set, int flags); + int (*get_thisthread_last_cpu_location)(hwloc_topology_t topology, hwloc_cpuset_t set, int flags); + int (*get_proc_last_cpu_location)(hwloc_topology_t topology, hwloc_pid_t pid, hwloc_cpuset_t set, int flags); + + int (*set_thisproc_membind)(hwloc_topology_t topology, hwloc_const_nodeset_t nodeset, hwloc_membind_policy_t policy, int flags); + int (*get_thisproc_membind)(hwloc_topology_t topology, hwloc_nodeset_t nodeset, hwloc_membind_policy_t * policy, int flags); + int (*set_thisthread_membind)(hwloc_topology_t topology, hwloc_const_nodeset_t nodeset, hwloc_membind_policy_t policy, int flags); + int (*get_thisthread_membind)(hwloc_topology_t topology, hwloc_nodeset_t nodeset, hwloc_membind_policy_t * policy, int flags); + int (*set_proc_membind)(hwloc_topology_t topology, hwloc_pid_t pid, hwloc_const_nodeset_t nodeset, hwloc_membind_policy_t policy, int flags); + int (*get_proc_membind)(hwloc_topology_t topology, hwloc_pid_t pid, hwloc_nodeset_t nodeset, hwloc_membind_policy_t * policy, int flags); + int (*set_area_membind)(hwloc_topology_t topology, const void *addr, size_t len, hwloc_const_nodeset_t nodeset, hwloc_membind_policy_t policy, int flags); + int (*get_area_membind)(hwloc_topology_t topology, const void *addr, size_t len, hwloc_nodeset_t nodeset, hwloc_membind_policy_t * policy, int flags); + int (*get_area_memlocation)(hwloc_topology_t topology, const void *addr, size_t len, hwloc_nodeset_t nodeset, int flags); + /* This has to return the same kind of pointer as alloc_membind, so that free_membind can be used on it */ + void *(*alloc)(hwloc_topology_t topology, size_t len); + /* alloc_membind has to always succeed if !(flags & HWLOC_MEMBIND_STRICT). + * see hwloc_alloc_or_fail which is convenient for that. */ + void *(*alloc_membind)(hwloc_topology_t topology, size_t len, hwloc_const_nodeset_t nodeset, hwloc_membind_policy_t policy, int flags); + int (*free_membind)(hwloc_topology_t topology, void *addr, size_t len); + + int (*get_allowed_resources)(hwloc_topology_t topology); + } binding_hooks; + + struct hwloc_topology_support support; + + void (*userdata_export_cb)(void *reserved, struct hwloc_topology *topology, struct hwloc_obj *obj); + void (*userdata_import_cb)(struct hwloc_topology *topology, struct hwloc_obj *obj, const char *name, const void *buffer, size_t length); + int userdata_not_decoded; + + struct hwloc_internal_distances_s { + hwloc_obj_type_t type; + /* add union hwloc_obj_attr_u if we ever support groups */ + unsigned nbobjs; + uint64_t *indexes; /* array of OS or GP indexes before we can convert them into objs. */ + uint64_t *values; /* distance matrices, ordered according to the above indexes/objs array. + * distance from i to j is stored in slot i*nbnodes+j. + */ + unsigned long kind; + + /* objects are currently stored in physical_index order */ + hwloc_obj_t *objs; /* array of objects */ + int objs_are_valid; /* set to 1 if the array objs is still valid, 0 if needs refresh */ + + unsigned id; /* to match the container id field of public distances structure */ + struct hwloc_internal_distances_s *prev, *next; + } *first_dist, *last_dist; + unsigned next_dist_id; + + int grouping; + int grouping_verbose; + unsigned grouping_nbaccuracies; + float grouping_accuracies[5]; + unsigned grouping_next_subkind; + + /* list of enabled backends. */ + struct hwloc_backend * backends; + struct hwloc_backend * get_pci_busid_cpuset_backend; + unsigned backend_excludes; + + /* memory allocator for topology objects */ + struct hwloc_tma * tma; + +/***************************************************** + * WARNING: + * changes above in this structure (and its children) + * should cause a bump of HWLOC_TOPOLOGY_ABI. + *****************************************************/ + + /* + * temporary variables during discovery + */ + + /* machine-wide memory. + * temporarily stored there by OSes that only provide this without NUMA information, + * and actually used later by the core. + */ + struct hwloc_numanode_attr_s machine_memory; + + /* pci stuff */ + int need_pci_belowroot_apply_locality; + int pci_has_forced_locality; + unsigned pci_forced_locality_nr; + struct hwloc_pci_forced_locality_s { + unsigned domain; + unsigned bus_first, bus_last; + hwloc_bitmap_t cpuset; + } * pci_forced_locality; + +}; + +extern void hwloc_alloc_root_sets(hwloc_obj_t root); +extern void hwloc_setup_pu_level(struct hwloc_topology *topology, unsigned nb_pus); +extern int hwloc_get_sysctlbyname(const char *name, int64_t *n); +extern int hwloc_get_sysctl(int name[], unsigned namelen, int *n); +extern int hwloc_fallback_nbprocessors(struct hwloc_topology *topology); + +extern int hwloc__object_cpusets_compare_first(hwloc_obj_t obj1, hwloc_obj_t obj2); +extern void hwloc__reorder_children(hwloc_obj_t parent); + +extern void hwloc_topology_setup_defaults(struct hwloc_topology *topology); +extern void hwloc_topology_clear(struct hwloc_topology *topology); + +/* insert memory object as memory child of normal parent */ +extern struct hwloc_obj * hwloc__attach_memory_object(struct hwloc_topology *topology, hwloc_obj_t parent, + hwloc_obj_t obj, + hwloc_report_error_t report_error); + +extern void hwloc_pci_discovery_init(struct hwloc_topology *topology); +extern void hwloc_pci_discovery_prepare(struct hwloc_topology *topology); +extern void hwloc_pci_discovery_exit(struct hwloc_topology *topology); + +/* Look for an object matching complete cpuset exactly, or insert one. + * Return NULL on failure. + * Return a good fallback (object above) on failure to insert. + */ +extern hwloc_obj_t hwloc_find_insert_io_parent_by_complete_cpuset(struct hwloc_topology *topology, hwloc_cpuset_t cpuset); + +/* Move PCI objects currently attached to the root object ot their actual location. + * Called by the core at the end of hwloc_topology_load(). + * Prior to this call, all PCI objects may be found below the root object. + * After this call and a reconnect of levels, all PCI objects are available through levels. + */ +extern int hwloc_pci_belowroot_apply_locality(struct hwloc_topology *topology); + +extern int hwloc__add_info(struct hwloc_info_s **infosp, unsigned *countp, const char *name, const char *value); +extern int hwloc__add_info_nodup(struct hwloc_info_s **infosp, unsigned *countp, const char *name, const char *value, int replace); +extern int hwloc__move_infos(struct hwloc_info_s **dst_infosp, unsigned *dst_countp, struct hwloc_info_s **src_infosp, unsigned *src_countp); +extern void hwloc__free_infos(struct hwloc_info_s *infos, unsigned count); + +/* set native OS binding hooks */ +extern void hwloc_set_native_binding_hooks(struct hwloc_binding_hooks *hooks, struct hwloc_topology_support *support); +/* set either native OS binding hooks (if thissystem), or dummy ones */ +extern void hwloc_set_binding_hooks(struct hwloc_topology *topology); + +#if defined(HWLOC_LINUX_SYS) +extern void hwloc_set_linuxfs_hooks(struct hwloc_binding_hooks *binding_hooks, struct hwloc_topology_support *support); +#endif /* HWLOC_LINUX_SYS */ + +#if defined(HWLOC_BGQ_SYS) +extern void hwloc_set_bgq_hooks(struct hwloc_binding_hooks *binding_hooks, struct hwloc_topology_support *support); +#endif /* HWLOC_BGQ_SYS */ + +#ifdef HWLOC_SOLARIS_SYS +extern void hwloc_set_solaris_hooks(struct hwloc_binding_hooks *binding_hooks, struct hwloc_topology_support *support); +#endif /* HWLOC_SOLARIS_SYS */ + +#ifdef HWLOC_AIX_SYS +extern void hwloc_set_aix_hooks(struct hwloc_binding_hooks *binding_hooks, struct hwloc_topology_support *support); +#endif /* HWLOC_AIX_SYS */ + +#ifdef HWLOC_WIN_SYS +extern void hwloc_set_windows_hooks(struct hwloc_binding_hooks *binding_hooks, struct hwloc_topology_support *support); +#endif /* HWLOC_WIN_SYS */ + +#ifdef HWLOC_DARWIN_SYS +extern void hwloc_set_darwin_hooks(struct hwloc_binding_hooks *binding_hooks, struct hwloc_topology_support *support); +#endif /* HWLOC_DARWIN_SYS */ + +#ifdef HWLOC_FREEBSD_SYS +extern void hwloc_set_freebsd_hooks(struct hwloc_binding_hooks *binding_hooks, struct hwloc_topology_support *support); +#endif /* HWLOC_FREEBSD_SYS */ + +#ifdef HWLOC_NETBSD_SYS +extern void hwloc_set_netbsd_hooks(struct hwloc_binding_hooks *binding_hooks, struct hwloc_topology_support *support); +#endif /* HWLOC_NETBSD_SYS */ + +#ifdef HWLOC_HPUX_SYS +extern void hwloc_set_hpux_hooks(struct hwloc_binding_hooks *binding_hooks, struct hwloc_topology_support *support); +#endif /* HWLOC_HPUX_SYS */ + +extern int hwloc_look_hardwired_fujitsu_k(struct hwloc_topology *topology); +extern int hwloc_look_hardwired_fujitsu_fx10(struct hwloc_topology *topology); +extern int hwloc_look_hardwired_fujitsu_fx100(struct hwloc_topology *topology); + +/* Insert uname-specific names/values in the object infos array. + * If cached_uname isn't NULL, it is used as a struct utsname instead of recalling uname. + * Any field that starts with \0 is ignored. + */ +extern void hwloc_add_uname_info(struct hwloc_topology *topology, void *cached_uname); + +/* Free obj and its attributes assuming it's not linked to a parent and doesn't have any child */ +extern void hwloc_free_unlinked_object(hwloc_obj_t obj); + +/* Free obj and its children, assuming it's not linked to a parent */ +extern void hwloc_free_object_and_children(hwloc_obj_t obj); + +/* Free obj, its next siblings, and their children, assuming they're not linked to a parent */ +extern void hwloc_free_object_siblings_and_children(hwloc_obj_t obj); + +/* This can be used for the alloc field to get allocated data that can be freed by free() */ +void *hwloc_alloc_heap(hwloc_topology_t topology, size_t len); + +/* This can be used for the alloc field to get allocated data that can be freed by munmap() */ +void *hwloc_alloc_mmap(hwloc_topology_t topology, size_t len); + +/* This can be used for the free_membind field to free data using free() */ +int hwloc_free_heap(hwloc_topology_t topology, void *addr, size_t len); + +/* This can be used for the free_membind field to free data using munmap() */ +int hwloc_free_mmap(hwloc_topology_t topology, void *addr, size_t len); + +/* Allocates unbound memory or fail, depending on whether STRICT is requested + * or not */ +static __hwloc_inline void * +hwloc_alloc_or_fail(hwloc_topology_t topology, size_t len, int flags) +{ + if (flags & HWLOC_MEMBIND_STRICT) + return NULL; + return hwloc_alloc(topology, len); +} + +extern void hwloc_internal_distances_init(hwloc_topology_t topology); +extern void hwloc_internal_distances_prepare(hwloc_topology_t topology); +extern void hwloc_internal_distances_destroy(hwloc_topology_t topology); +extern int hwloc_internal_distances_dup(hwloc_topology_t new, hwloc_topology_t old); +extern void hwloc_internal_distances_refresh(hwloc_topology_t topology); +extern int hwloc_internal_distances_add(hwloc_topology_t topology, unsigned nbobjs, hwloc_obj_t *objs, uint64_t *values, unsigned long kind, unsigned long flags); +extern int hwloc_internal_distances_add_by_index(hwloc_topology_t topology, hwloc_obj_type_t type, unsigned nbobjs, uint64_t *indexes, uint64_t *values, unsigned long kind, unsigned long flags); +extern void hwloc_internal_distances_invalidate_cached_objs(hwloc_topology_t topology); + +/* encode src buffer into target buffer. + * targsize must be at least 4*((srclength+2)/3)+1. + * target will be 0-terminated. + */ +extern int hwloc_encode_to_base64(const char *src, size_t srclength, char *target, size_t targsize); +/* decode src buffer into target buffer. + * src is 0-terminated. + * targsize must be at least srclength*3/4+1 (srclength not including \0) + * but only srclength*3/4 characters will be meaningful + * (the next one may be partially written during decoding, but it should be ignored). + */ +extern int hwloc_decode_from_base64(char const *src, char *target, size_t targsize); + +/* Check whether needle matches the beginning of haystack, at least n, and up + * to a colon or \0 */ +extern int hwloc_namecoloncmp(const char *haystack, const char *needle, size_t n); + +/* On some systems, snprintf returns the size of written data, not the actually + * required size. hwloc_snprintf always report the actually required size. */ +extern int hwloc_snprintf(char *str, size_t size, const char *format, ...) __hwloc_attribute_format(printf, 3, 4); + +/* Return the name of the currently running program, if supported. + * If not NULL, must be freed by the caller. + */ +extern char * hwloc_progname(struct hwloc_topology *topology); + +/* obj->attr->group.kind internal values. + * the core will keep the smallest ones when merging two groups, + * that's why user-given kinds are first. + */ +/* first, user-given groups, should remain as long as possible */ +#define HWLOC_GROUP_KIND_USER 0 /* user-given, user may use subkind too */ +#define HWLOC_GROUP_KIND_SYNTHETIC 10 /* subkind is group depth within synthetic description */ +/* then, hardware-specific groups */ +#define HWLOC_GROUP_KIND_INTEL_KNL_SUBNUMA_CLUSTER 100 /* no subkind */ +#define HWLOC_GROUP_KIND_INTEL_EXTTOPOENUM_UNKNOWN 101 /* subkind is unknown level */ +#define HWLOC_GROUP_KIND_INTEL_MODULE 102 /* no subkind */ +#define HWLOC_GROUP_KIND_INTEL_TILE 103 /* no subkind */ +#define HWLOC_GROUP_KIND_INTEL_DIE 104 /* no subkind */ +#define HWLOC_GROUP_KIND_S390_BOOK 110 /* no subkind */ +#define HWLOC_GROUP_KIND_AMD_COMPUTE_UNIT 120 /* no subkind */ +/* then, OS-specific groups */ +#define HWLOC_GROUP_KIND_SOLARIS_PG_HW_PERF 200 /* subkind is group width */ +#define HWLOC_GROUP_KIND_AIX_SDL_UNKNOWN 210 /* subkind is SDL level */ +#define HWLOC_GROUP_KIND_WINDOWS_PROCESSOR_GROUP 220 /* no subkind */ +#define HWLOC_GROUP_KIND_WINDOWS_RELATIONSHIP_UNKNOWN 221 /* no subkind */ +/* distance groups */ +#define HWLOC_GROUP_KIND_DISTANCE 900 /* subkind is round of adding these groups during distance based grouping */ +/* finally, hwloc-specific groups required to insert something else, should disappear as soon as possible */ +#define HWLOC_GROUP_KIND_IO 1000 /* no subkind */ +#define HWLOC_GROUP_KIND_MEMORY 1001 /* no subkind */ + +/* memory allocator for topology objects */ +struct hwloc_tma { + void * (*malloc)(struct hwloc_tma *, size_t); + void *data; + int dontfree; /* when set, free() or realloc() cannot be used, and tma->malloc() cannot fail */ +}; + +static __hwloc_inline void * +hwloc_tma_malloc(struct hwloc_tma *tma, + size_t size) +{ + if (tma) { + return tma->malloc(tma, size); + } else { + return malloc(size); + } +} + +static __hwloc_inline void * +hwloc_tma_calloc(struct hwloc_tma *tma, + size_t size) +{ + char *ptr = hwloc_tma_malloc(tma, size); + if (ptr) + memset(ptr, 0, size); + return ptr; +} + +static __hwloc_inline char * +hwloc_tma_strdup(struct hwloc_tma *tma, + const char *src) +{ + size_t len = strlen(src); + char *ptr = hwloc_tma_malloc(tma, len+1); + if (ptr) + memcpy(ptr, src, len+1); + return ptr; +} + +/* bitmap allocator to be used inside hwloc */ +extern hwloc_bitmap_t hwloc_bitmap_tma_dup(struct hwloc_tma *tma, hwloc_const_bitmap_t old); + +extern int hwloc__topology_dup(hwloc_topology_t *newp, hwloc_topology_t old, struct hwloc_tma *tma); +extern void hwloc__topology_disadopt(hwloc_topology_t topology); + +#endif /* HWLOC_PRIVATE_H */ diff --git a/src/3rdparty/hwloc/include/private/solaris-chiptype.h b/src/3rdparty/hwloc/include/private/solaris-chiptype.h new file mode 100644 index 00000000..4ad2130a --- /dev/null +++ b/src/3rdparty/hwloc/include/private/solaris-chiptype.h @@ -0,0 +1,43 @@ +/* + * Copyright © 2009-2010 Oracle and/or its affiliates. All rights reserved. + * + * Copyright © 2017 Inria. All rights reserved. + * $COPYRIGHT$ + * + * Additional copyrights may follow + * + * $HEADER$ + */ + + +#ifdef HWLOC_INSIDE_PLUGIN +/* + * these declarations are internal only, they are not available to plugins + * (functions below are internal static symbols). + */ +#error This file should not be used in plugins +#endif + + +#ifndef HWLOC_PRIVATE_SOLARIS_CHIPTYPE_H +#define HWLOC_PRIVATE_SOLARIS_CHIPTYPE_H + +struct hwloc_solaris_chip_info_s { + char *model; + char *type; + /* L1i, L1d, L2, L3 */ +#define HWLOC_SOLARIS_CHIP_INFO_L1I 0 +#define HWLOC_SOLARIS_CHIP_INFO_L1D 1 +#define HWLOC_SOLARIS_CHIP_INFO_L2I 2 +#define HWLOC_SOLARIS_CHIP_INFO_L2D 3 +#define HWLOC_SOLARIS_CHIP_INFO_L3 4 + long cache_size[5]; /* cleared to -1 if we don't want of that cache */ + unsigned cache_linesize[5]; + unsigned cache_associativity[5]; + int l2_unified; +}; + +/* fills the structure with 0 on error */ +extern void hwloc_solaris_get_chip_info(struct hwloc_solaris_chip_info_s *info); + +#endif /* HWLOC_PRIVATE_SOLARIS_CHIPTYPE_H */ diff --git a/src/3rdparty/hwloc/include/private/xml.h b/src/3rdparty/hwloc/include/private/xml.h new file mode 100644 index 00000000..7c73384d --- /dev/null +++ b/src/3rdparty/hwloc/include/private/xml.h @@ -0,0 +1,108 @@ +/* + * Copyright © 2009-2019 Inria. All rights reserved. + * See COPYING in top-level directory. + */ + +#ifndef PRIVATE_XML_H +#define PRIVATE_XML_H 1 + +#include + +#include + +HWLOC_DECLSPEC int hwloc__xml_verbose(void); + +/************** + * XML import * + **************/ + +typedef struct hwloc__xml_import_state_s { + struct hwloc__xml_import_state_s *parent; + + /* globals shared because the entire stack of states during import */ + struct hwloc_xml_backend_data_s *global; + + /* opaque data used to store backend-specific data. + * statically allocated to allow stack-allocation by the common code without knowing actual backend needs. + */ + char data[32]; +} * hwloc__xml_import_state_t; + +struct hwloc__xml_imported_v1distances_s { + unsigned long kind; + unsigned nbobjs; + float *floats; + struct hwloc__xml_imported_v1distances_s *prev, *next; +}; + +HWLOC_DECLSPEC int hwloc__xml_import_diff(hwloc__xml_import_state_t state, hwloc_topology_diff_t *firstdiffp); + +struct hwloc_xml_backend_data_s { + /* xml backend parameters */ + int (*look_init)(struct hwloc_xml_backend_data_s *bdata, struct hwloc__xml_import_state_s *state); + void (*look_done)(struct hwloc_xml_backend_data_s *bdata, int result); + void (*backend_exit)(struct hwloc_xml_backend_data_s *bdata); + int (*next_attr)(struct hwloc__xml_import_state_s * state, char **namep, char **valuep); + int (*find_child)(struct hwloc__xml_import_state_s * state, struct hwloc__xml_import_state_s * childstate, char **tagp); + int (*close_tag)(struct hwloc__xml_import_state_s * state); /* look for an explicit closing tag */ + void (*close_child)(struct hwloc__xml_import_state_s * state); + int (*get_content)(struct hwloc__xml_import_state_s * state, char **beginp, size_t expected_length); /* return 0 on empty content (and sets beginp to empty string), 1 on actual content, -1 on error or unexpected content length */ + void (*close_content)(struct hwloc__xml_import_state_s * state); + char * msgprefix; + void *data; /* libxml2 doc, or nolibxml buffer */ + unsigned version_major, version_minor; + unsigned nbnumanodes; + hwloc_obj_t first_numanode, last_numanode; /* temporary cousin-list for handling v1distances */ + struct hwloc__xml_imported_v1distances_s *first_v1dist, *last_v1dist; + int dont_merge_die_groups; +}; + +/************** + * XML export * + **************/ + +typedef struct hwloc__xml_export_state_s { + struct hwloc__xml_export_state_s *parent; + + void (*new_child)(struct hwloc__xml_export_state_s *parentstate, struct hwloc__xml_export_state_s *state, const char *name); + void (*new_prop)(struct hwloc__xml_export_state_s *state, const char *name, const char *value); + void (*add_content)(struct hwloc__xml_export_state_s *state, const char *buffer, size_t length); + void (*end_object)(struct hwloc__xml_export_state_s *state, const char *name); + + struct hwloc__xml_export_data_s { + hwloc_obj_t v1_memory_group; /* if we need to insert intermediate group above memory children when exporting to v1 */ + } *global; + + /* opaque data used to store backend-specific data. + * statically allocated to allow stack-allocation by the common code without knowing actual backend needs. + */ + char data[40]; +} * hwloc__xml_export_state_t; + +HWLOC_DECLSPEC void hwloc__xml_export_topology(hwloc__xml_export_state_t parentstate, hwloc_topology_t topology, unsigned long flags); + +HWLOC_DECLSPEC void hwloc__xml_export_diff(hwloc__xml_export_state_t parentstate, hwloc_topology_diff_t diff); + +/****************** + * XML components * + ******************/ + +struct hwloc_xml_callbacks { + int (*backend_init)(struct hwloc_xml_backend_data_s *bdata, const char *xmlpath, const char *xmlbuffer, int xmlbuflen); + int (*export_file)(struct hwloc_topology *topology, struct hwloc__xml_export_data_s *edata, const char *filename, unsigned long flags); + int (*export_buffer)(struct hwloc_topology *topology, struct hwloc__xml_export_data_s *edata, char **xmlbuffer, int *buflen, unsigned long flags); + void (*free_buffer)(void *xmlbuffer); + int (*import_diff)(struct hwloc__xml_import_state_s *state, const char *xmlpath, const char *xmlbuffer, int xmlbuflen, hwloc_topology_diff_t *diff, char **refnamep); + int (*export_diff_file)(union hwloc_topology_diff_u *diff, const char *refname, const char *filename); + int (*export_diff_buffer)(union hwloc_topology_diff_u *diff, const char *refname, char **xmlbuffer, int *buflen); +}; + +struct hwloc_xml_component { + struct hwloc_xml_callbacks *nolibxml_callbacks; + struct hwloc_xml_callbacks *libxml_callbacks; +}; + +HWLOC_DECLSPEC void hwloc_xml_callbacks_register(struct hwloc_xml_component *component); +HWLOC_DECLSPEC void hwloc_xml_callbacks_reset(void); + +#endif /* PRIVATE_XML_H */ diff --git a/src/3rdparty/hwloc/src/base64.c b/src/3rdparty/hwloc/src/base64.c new file mode 100644 index 00000000..7b3e1210 --- /dev/null +++ b/src/3rdparty/hwloc/src/base64.c @@ -0,0 +1,309 @@ +/* + * Copyright © 2012-2018 Inria. All rights reserved. + * See COPYING in top-level directory. + * + * Modifications after import: + * - removed all #if + * - updated prototypes + * - updated #include + */ + +/* include hwloc's config before anything else + * so that extensions and features are properly enabled + */ +#include + +/* $OpenBSD: base64.c,v 1.5 2006/10/21 09:55:03 otto Exp $ */ + +/* + * Copyright (c) 1996 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* + * Portions Copyright (c) 1995 by International Business Machines, Inc. + * + * International Business Machines, Inc. (hereinafter called IBM) grants + * permission under its copyrights to use, copy, modify, and distribute this + * Software with or without fee, provided that the above copyright notice and + * all paragraphs of this notice appear in all copies, and that the name of IBM + * not be used in connection with the marketing of any product incorporating + * the Software or modifications thereof, without specific, written prior + * permission. + * + * To the extent it has a right to do so, IBM grants an immunity from suit + * under its patents, if any, for the use, sale or manufacture of products to + * the extent that such products are used for performing Domain Name System + * dynamic updates in TCP/IP networks by means of the Software. No immunity is + * granted for any product per se or for any other function of any product. + * + * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, + * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN + * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +/* OPENBSD ORIGINAL: lib/libc/net/base64.c */ + +static const char Base64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static const char Pad64 = '='; + +/* (From RFC1521 and draft-ietf-dnssec-secext-03.txt) + The following encoding technique is taken from RFC 1521 by Borenstein + and Freed. It is reproduced here in a slightly edited form for + convenience. + + A 65-character subset of US-ASCII is used, enabling 6 bits to be + represented per printable character. (The extra 65th character, "=", + is used to signify a special processing function.) + + The encoding process represents 24-bit groups of input bits as output + strings of 4 encoded characters. Proceeding from left to right, a + 24-bit input group is formed by concatenating 3 8-bit input groups. + These 24 bits are then treated as 4 concatenated 6-bit groups, each + of which is translated into a single digit in the base64 alphabet. + + Each 6-bit group is used as an index into an array of 64 printable + characters. The character referenced by the index is placed in the + output string. + + Table 1: The Base64 Alphabet + + Value Encoding Value Encoding Value Encoding Value Encoding + 0 A 17 R 34 i 51 z + 1 B 18 S 35 j 52 0 + 2 C 19 T 36 k 53 1 + 3 D 20 U 37 l 54 2 + 4 E 21 V 38 m 55 3 + 5 F 22 W 39 n 56 4 + 6 G 23 X 40 o 57 5 + 7 H 24 Y 41 p 58 6 + 8 I 25 Z 42 q 59 7 + 9 J 26 a 43 r 60 8 + 10 K 27 b 44 s 61 9 + 11 L 28 c 45 t 62 + + 12 M 29 d 46 u 63 / + 13 N 30 e 47 v + 14 O 31 f 48 w (pad) = + 15 P 32 g 49 x + 16 Q 33 h 50 y + + Special processing is performed if fewer than 24 bits are available + at the end of the data being encoded. A full encoding quantum is + always completed at the end of a quantity. When fewer than 24 input + bits are available in an input group, zero bits are added (on the + right) to form an integral number of 6-bit groups. Padding at the + end of the data is performed using the '=' character. + + Since all base64 input is an integral number of octets, only the + ------------------------------------------------- + following cases can arise: + + (1) the final quantum of encoding input is an integral + multiple of 24 bits; here, the final unit of encoded + output will be an integral multiple of 4 characters + with no "=" padding, + (2) the final quantum of encoding input is exactly 8 bits; + here, the final unit of encoded output will be two + characters followed by two "=" padding characters, or + (3) the final quantum of encoding input is exactly 16 bits; + here, the final unit of encoded output will be three + characters followed by one "=" padding character. + */ + +#include +#include +#include + +int +hwloc_encode_to_base64(const char *src, size_t srclength, char *target, size_t targsize) +{ + size_t datalength = 0; + unsigned char input[3]; + unsigned char output[4]; + unsigned int i; + + while (2 < srclength) { + input[0] = *src++; + input[1] = *src++; + input[2] = *src++; + srclength -= 3; + + output[0] = input[0] >> 2; + output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); + output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); + output[3] = input[2] & 0x3f; + + if (datalength + 4 > targsize) + return (-1); + target[datalength++] = Base64[output[0]]; + target[datalength++] = Base64[output[1]]; + target[datalength++] = Base64[output[2]]; + target[datalength++] = Base64[output[3]]; + } + + /* Now we worry about padding. */ + if (0 != srclength) { + /* Get what's left. */ + input[0] = input[1] = input[2] = '\0'; + for (i = 0; i < srclength; i++) + input[i] = *src++; + + output[0] = input[0] >> 2; + output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); + output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); + + if (datalength + 4 > targsize) + return (-1); + target[datalength++] = Base64[output[0]]; + target[datalength++] = Base64[output[1]]; + if (srclength == 1) + target[datalength++] = Pad64; + else + target[datalength++] = Base64[output[2]]; + target[datalength++] = Pad64; + } + if (datalength >= targsize) + return (-1); + target[datalength] = '\0'; /* Returned value doesn't count \0. */ + return (int)(datalength); +} + +/* skips all whitespace anywhere. + converts characters, four at a time, starting at (or after) + src from base - 64 numbers into three 8 bit bytes in the target area. + it returns the number of data bytes stored at the target, or -1 on error. + */ + +int +hwloc_decode_from_base64(char const *src, char *target, size_t targsize) +{ + unsigned int tarindex, state; + int ch; + char *pos; + + state = 0; + tarindex = 0; + + while ((ch = *src++) != '\0') { + if (isspace(ch)) /* Skip whitespace anywhere. */ + continue; + + if (ch == Pad64) + break; + + pos = strchr(Base64, ch); + if (pos == 0) /* A non-base64 character. */ + return (-1); + + switch (state) { + case 0: + if (target) { + if (tarindex >= targsize) + return (-1); + target[tarindex] = (char)(pos - Base64) << 2; + } + state = 1; + break; + case 1: + if (target) { + if (tarindex + 1 >= targsize) + return (-1); + target[tarindex] |= (pos - Base64) >> 4; + target[tarindex+1] = ((pos - Base64) & 0x0f) + << 4 ; + } + tarindex++; + state = 2; + break; + case 2: + if (target) { + if (tarindex + 1 >= targsize) + return (-1); + target[tarindex] |= (pos - Base64) >> 2; + target[tarindex+1] = ((pos - Base64) & 0x03) + << 6; + } + tarindex++; + state = 3; + break; + case 3: + if (target) { + if (tarindex >= targsize) + return (-1); + target[tarindex] |= (pos - Base64); + } + tarindex++; + state = 0; + break; + } + } + + /* + * We are done decoding Base-64 chars. Let's see if we ended + * on a byte boundary, and/or with erroneous trailing characters. + */ + + if (ch == Pad64) { /* We got a pad char. */ + ch = *src++; /* Skip it, get next. */ + switch (state) { + case 0: /* Invalid = in first position */ + case 1: /* Invalid = in second position */ + return (-1); + + case 2: /* Valid, means one byte of info */ + /* Skip any number of spaces. */ + for (; ch != '\0'; ch = *src++) + if (!isspace(ch)) + break; + /* Make sure there is another trailing = sign. */ + if (ch != Pad64) + return (-1); + ch = *src++; /* Skip the = */ + /* Fall through to "single trailing =" case. */ + /* FALLTHROUGH */ + + case 3: /* Valid, means two bytes of info */ + /* + * We know this char is an =. Is there anything but + * whitespace after it? + */ + for (; ch != '\0'; ch = *src++) + if (!isspace(ch)) + return (-1); + + /* + * Now make sure for cases 2 and 3 that the "extra" + * bits that slopped past the last full byte were + * zeros. If we don't check them, they become a + * subliminal channel. + */ + if (target && target[tarindex] != 0) + return (-1); + } + } else { + /* + * We ended by seeing the end of the string. Make sure we + * have no partial bytes lying around. + */ + if (state != 0) + return (-1); + } + + return (tarindex); +} diff --git a/src/3rdparty/hwloc/src/bind.c b/src/3rdparty/hwloc/src/bind.c new file mode 100644 index 00000000..b3457bc7 --- /dev/null +++ b/src/3rdparty/hwloc/src/bind.c @@ -0,0 +1,922 @@ +/* + * Copyright © 2009 CNRS + * Copyright © 2009-2018 Inria. All rights reserved. + * Copyright © 2009-2010, 2012 Université Bordeaux + * Copyright © 2011-2015 Cisco Systems, Inc. All rights reserved. + * See COPYING in top-level directory. + */ + +#include +#include +#include +#include +#ifdef HAVE_SYS_MMAN_H +# include +#endif +/* is only needed if we don't have posix_memalign() */ +#if defined(hwloc_getpagesize) && !defined(HAVE_POSIX_MEMALIGN) && defined(HAVE_MEMALIGN) && defined(HAVE_MALLOC_H) +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include + +/* TODO: HWLOC_GNU_SYS, + * + * We could use glibc's sched_setaffinity generically when it is available + * + * Darwin and OpenBSD don't seem to have binding facilities. + */ + +#define HWLOC_CPUBIND_ALLFLAGS (HWLOC_CPUBIND_PROCESS|HWLOC_CPUBIND_THREAD|HWLOC_CPUBIND_STRICT|HWLOC_CPUBIND_NOMEMBIND) + +static hwloc_const_bitmap_t +hwloc_fix_cpubind(hwloc_topology_t topology, hwloc_const_bitmap_t set) +{ + hwloc_const_bitmap_t topology_set = hwloc_topology_get_topology_cpuset(topology); + hwloc_const_bitmap_t complete_set = hwloc_topology_get_complete_cpuset(topology); + + if (hwloc_bitmap_iszero(set)) { + errno = EINVAL; + return NULL; + } + + if (!hwloc_bitmap_isincluded(set, complete_set)) { + errno = EINVAL; + return NULL; + } + + if (hwloc_bitmap_isincluded(topology_set, set)) + set = complete_set; + + return set; +} + +int +hwloc_set_cpubind(hwloc_topology_t topology, hwloc_const_bitmap_t set, int flags) +{ + if (flags & ~HWLOC_CPUBIND_ALLFLAGS) { + errno = EINVAL; + return -1; + } + + set = hwloc_fix_cpubind(topology, set); + if (!set) + return -1; + + if (flags & HWLOC_CPUBIND_PROCESS) { + if (topology->binding_hooks.set_thisproc_cpubind) + return topology->binding_hooks.set_thisproc_cpubind(topology, set, flags); + } else if (flags & HWLOC_CPUBIND_THREAD) { + if (topology->binding_hooks.set_thisthread_cpubind) + return topology->binding_hooks.set_thisthread_cpubind(topology, set, flags); + } else { + if (topology->binding_hooks.set_thisproc_cpubind) { + int err = topology->binding_hooks.set_thisproc_cpubind(topology, set, flags); + if (err >= 0 || errno != ENOSYS) + return err; + /* ENOSYS, fallback */ + } + if (topology->binding_hooks.set_thisthread_cpubind) + return topology->binding_hooks.set_thisthread_cpubind(topology, set, flags); + } + + errno = ENOSYS; + return -1; +} + +int +hwloc_get_cpubind(hwloc_topology_t topology, hwloc_bitmap_t set, int flags) +{ + if (flags & ~HWLOC_CPUBIND_ALLFLAGS) { + errno = EINVAL; + return -1; + } + + if (flags & HWLOC_CPUBIND_PROCESS) { + if (topology->binding_hooks.get_thisproc_cpubind) + return topology->binding_hooks.get_thisproc_cpubind(topology, set, flags); + } else if (flags & HWLOC_CPUBIND_THREAD) { + if (topology->binding_hooks.get_thisthread_cpubind) + return topology->binding_hooks.get_thisthread_cpubind(topology, set, flags); + } else { + if (topology->binding_hooks.get_thisproc_cpubind) { + int err = topology->binding_hooks.get_thisproc_cpubind(topology, set, flags); + if (err >= 0 || errno != ENOSYS) + return err; + /* ENOSYS, fallback */ + } + if (topology->binding_hooks.get_thisthread_cpubind) + return topology->binding_hooks.get_thisthread_cpubind(topology, set, flags); + } + + errno = ENOSYS; + return -1; +} + +int +hwloc_set_proc_cpubind(hwloc_topology_t topology, hwloc_pid_t pid, hwloc_const_bitmap_t set, int flags) +{ + if (flags & ~HWLOC_CPUBIND_ALLFLAGS) { + errno = EINVAL; + return -1; + } + + set = hwloc_fix_cpubind(topology, set); + if (!set) + return -1; + + if (topology->binding_hooks.set_proc_cpubind) + return topology->binding_hooks.set_proc_cpubind(topology, pid, set, flags); + + errno = ENOSYS; + return -1; +} + +int +hwloc_get_proc_cpubind(hwloc_topology_t topology, hwloc_pid_t pid, hwloc_bitmap_t set, int flags) +{ + if (flags & ~HWLOC_CPUBIND_ALLFLAGS) { + errno = EINVAL; + return -1; + } + + if (topology->binding_hooks.get_proc_cpubind) + return topology->binding_hooks.get_proc_cpubind(topology, pid, set, flags); + + errno = ENOSYS; + return -1; +} + +#ifdef hwloc_thread_t +int +hwloc_set_thread_cpubind(hwloc_topology_t topology, hwloc_thread_t tid, hwloc_const_bitmap_t set, int flags) +{ + if (flags & ~HWLOC_CPUBIND_ALLFLAGS) { + errno = EINVAL; + return -1; + } + + set = hwloc_fix_cpubind(topology, set); + if (!set) + return -1; + + if (topology->binding_hooks.set_thread_cpubind) + return topology->binding_hooks.set_thread_cpubind(topology, tid, set, flags); + + errno = ENOSYS; + return -1; +} + +int +hwloc_get_thread_cpubind(hwloc_topology_t topology, hwloc_thread_t tid, hwloc_bitmap_t set, int flags) +{ + if (flags & ~HWLOC_CPUBIND_ALLFLAGS) { + errno = EINVAL; + return -1; + } + + if (topology->binding_hooks.get_thread_cpubind) + return topology->binding_hooks.get_thread_cpubind(topology, tid, set, flags); + + errno = ENOSYS; + return -1; +} +#endif + +int +hwloc_get_last_cpu_location(hwloc_topology_t topology, hwloc_bitmap_t set, int flags) +{ + if (flags & ~HWLOC_CPUBIND_ALLFLAGS) { + errno = EINVAL; + return -1; + } + + if (flags & HWLOC_CPUBIND_PROCESS) { + if (topology->binding_hooks.get_thisproc_last_cpu_location) + return topology->binding_hooks.get_thisproc_last_cpu_location(topology, set, flags); + } else if (flags & HWLOC_CPUBIND_THREAD) { + if (topology->binding_hooks.get_thisthread_last_cpu_location) + return topology->binding_hooks.get_thisthread_last_cpu_location(topology, set, flags); + } else { + if (topology->binding_hooks.get_thisproc_last_cpu_location) { + int err = topology->binding_hooks.get_thisproc_last_cpu_location(topology, set, flags); + if (err >= 0 || errno != ENOSYS) + return err; + /* ENOSYS, fallback */ + } + if (topology->binding_hooks.get_thisthread_last_cpu_location) + return topology->binding_hooks.get_thisthread_last_cpu_location(topology, set, flags); + } + + errno = ENOSYS; + return -1; +} + +int +hwloc_get_proc_last_cpu_location(hwloc_topology_t topology, hwloc_pid_t pid, hwloc_bitmap_t set, int flags) +{ + if (flags & ~HWLOC_CPUBIND_ALLFLAGS) { + errno = EINVAL; + return -1; + } + + if (topology->binding_hooks.get_proc_last_cpu_location) + return topology->binding_hooks.get_proc_last_cpu_location(topology, pid, set, flags); + + errno = ENOSYS; + return -1; +} + +#define HWLOC_MEMBIND_ALLFLAGS (HWLOC_MEMBIND_PROCESS|HWLOC_MEMBIND_THREAD|HWLOC_MEMBIND_STRICT|HWLOC_MEMBIND_MIGRATE|HWLOC_MEMBIND_NOCPUBIND|HWLOC_MEMBIND_BYNODESET) + +static hwloc_const_nodeset_t +hwloc_fix_membind(hwloc_topology_t topology, hwloc_const_nodeset_t nodeset) +{ + hwloc_const_bitmap_t topology_nodeset = hwloc_topology_get_topology_nodeset(topology); + hwloc_const_bitmap_t complete_nodeset = hwloc_topology_get_complete_nodeset(topology); + + if (hwloc_bitmap_iszero(nodeset)) { + errno = EINVAL; + return NULL; + } + + if (!hwloc_bitmap_isincluded(nodeset, complete_nodeset)) { + errno = EINVAL; + return NULL; + } + + if (hwloc_bitmap_isincluded(topology_nodeset, nodeset)) + return complete_nodeset; + + return nodeset; +} + +static int +hwloc_fix_membind_cpuset(hwloc_topology_t topology, hwloc_nodeset_t nodeset, hwloc_const_cpuset_t cpuset) +{ + hwloc_const_bitmap_t topology_set = hwloc_topology_get_topology_cpuset(topology); + hwloc_const_bitmap_t complete_set = hwloc_topology_get_complete_cpuset(topology); + hwloc_const_bitmap_t complete_nodeset = hwloc_topology_get_complete_nodeset(topology); + + if (hwloc_bitmap_iszero(cpuset)) { + errno = EINVAL; + return -1; + } + + if (!hwloc_bitmap_isincluded(cpuset, complete_set)) { + errno = EINVAL; + return -1; + } + + if (hwloc_bitmap_isincluded(topology_set, cpuset)) { + hwloc_bitmap_copy(nodeset, complete_nodeset); + return 0; + } + + hwloc_cpuset_to_nodeset(topology, cpuset, nodeset); + return 0; +} + +static __hwloc_inline int hwloc__check_membind_policy(hwloc_membind_policy_t policy) +{ + if (policy == HWLOC_MEMBIND_DEFAULT + || policy == HWLOC_MEMBIND_FIRSTTOUCH + || policy == HWLOC_MEMBIND_BIND + || policy == HWLOC_MEMBIND_INTERLEAVE + || policy == HWLOC_MEMBIND_NEXTTOUCH) + return 0; + return -1; +} + +static int +hwloc_set_membind_by_nodeset(hwloc_topology_t topology, hwloc_const_nodeset_t nodeset, hwloc_membind_policy_t policy, int flags) +{ + if ((flags & ~HWLOC_MEMBIND_ALLFLAGS) || hwloc__check_membind_policy(policy) < 0) { + errno = EINVAL; + return -1; + } + + nodeset = hwloc_fix_membind(topology, nodeset); + if (!nodeset) + return -1; + + if (flags & HWLOC_MEMBIND_PROCESS) { + if (topology->binding_hooks.set_thisproc_membind) + return topology->binding_hooks.set_thisproc_membind(topology, nodeset, policy, flags); + } else if (flags & HWLOC_MEMBIND_THREAD) { + if (topology->binding_hooks.set_thisthread_membind) + return topology->binding_hooks.set_thisthread_membind(topology, nodeset, policy, flags); + } else { + if (topology->binding_hooks.set_thisproc_membind) { + int err = topology->binding_hooks.set_thisproc_membind(topology, nodeset, policy, flags); + if (err >= 0 || errno != ENOSYS) + return err; + /* ENOSYS, fallback */ + } + if (topology->binding_hooks.set_thisthread_membind) + return topology->binding_hooks.set_thisthread_membind(topology, nodeset, policy, flags); + } + + errno = ENOSYS; + return -1; +} + +int +hwloc_set_membind(hwloc_topology_t topology, hwloc_const_bitmap_t set, hwloc_membind_policy_t policy, int flags) +{ + int ret; + + if (flags & HWLOC_MEMBIND_BYNODESET) { + ret = hwloc_set_membind_by_nodeset(topology, set, policy, flags); + } else { + hwloc_nodeset_t nodeset = hwloc_bitmap_alloc(); + if (hwloc_fix_membind_cpuset(topology, nodeset, set)) + ret = -1; + else + ret = hwloc_set_membind_by_nodeset(topology, nodeset, policy, flags); + hwloc_bitmap_free(nodeset); + } + return ret; +} + +static int +hwloc_get_membind_by_nodeset(hwloc_topology_t topology, hwloc_nodeset_t nodeset, hwloc_membind_policy_t * policy, int flags) +{ + if (flags & ~HWLOC_MEMBIND_ALLFLAGS) { + errno = EINVAL; + return -1; + } + + if (flags & HWLOC_MEMBIND_PROCESS) { + if (topology->binding_hooks.get_thisproc_membind) + return topology->binding_hooks.get_thisproc_membind(topology, nodeset, policy, flags); + } else if (flags & HWLOC_MEMBIND_THREAD) { + if (topology->binding_hooks.get_thisthread_membind) + return topology->binding_hooks.get_thisthread_membind(topology, nodeset, policy, flags); + } else { + if (topology->binding_hooks.get_thisproc_membind) { + int err = topology->binding_hooks.get_thisproc_membind(topology, nodeset, policy, flags); + if (err >= 0 || errno != ENOSYS) + return err; + /* ENOSYS, fallback */ + } + if (topology->binding_hooks.get_thisthread_membind) + return topology->binding_hooks.get_thisthread_membind(topology, nodeset, policy, flags); + } + + errno = ENOSYS; + return -1; +} + +int +hwloc_get_membind(hwloc_topology_t topology, hwloc_bitmap_t set, hwloc_membind_policy_t * policy, int flags) +{ + int ret; + + if (flags & HWLOC_MEMBIND_BYNODESET) { + ret = hwloc_get_membind_by_nodeset(topology, set, policy, flags); + } else { + hwloc_nodeset_t nodeset = hwloc_bitmap_alloc(); + ret = hwloc_get_membind_by_nodeset(topology, nodeset, policy, flags); + if (!ret) + hwloc_cpuset_from_nodeset(topology, set, nodeset); + hwloc_bitmap_free(nodeset); + } + + return ret; +} + +static int +hwloc_set_proc_membind_by_nodeset(hwloc_topology_t topology, hwloc_pid_t pid, hwloc_const_nodeset_t nodeset, hwloc_membind_policy_t policy, int flags) +{ + if ((flags & ~HWLOC_MEMBIND_ALLFLAGS) || hwloc__check_membind_policy(policy) < 0) { + errno = EINVAL; + return -1; + } + + nodeset = hwloc_fix_membind(topology, nodeset); + if (!nodeset) + return -1; + + if (topology->binding_hooks.set_proc_membind) + return topology->binding_hooks.set_proc_membind(topology, pid, nodeset, policy, flags); + + errno = ENOSYS; + return -1; +} + + +int +hwloc_set_proc_membind(hwloc_topology_t topology, hwloc_pid_t pid, hwloc_const_bitmap_t set, hwloc_membind_policy_t policy, int flags) +{ + int ret; + + if (flags & HWLOC_MEMBIND_BYNODESET) { + ret = hwloc_set_proc_membind_by_nodeset(topology, pid, set, policy, flags); + } else { + hwloc_nodeset_t nodeset = hwloc_bitmap_alloc(); + if (hwloc_fix_membind_cpuset(topology, nodeset, set)) + ret = -1; + else + ret = hwloc_set_proc_membind_by_nodeset(topology, pid, nodeset, policy, flags); + hwloc_bitmap_free(nodeset); + } + + return ret; +} + +static int +hwloc_get_proc_membind_by_nodeset(hwloc_topology_t topology, hwloc_pid_t pid, hwloc_nodeset_t nodeset, hwloc_membind_policy_t * policy, int flags) +{ + if (flags & ~HWLOC_MEMBIND_ALLFLAGS) { + errno = EINVAL; + return -1; + } + + if (topology->binding_hooks.get_proc_membind) + return topology->binding_hooks.get_proc_membind(topology, pid, nodeset, policy, flags); + + errno = ENOSYS; + return -1; +} + +int +hwloc_get_proc_membind(hwloc_topology_t topology, hwloc_pid_t pid, hwloc_bitmap_t set, hwloc_membind_policy_t * policy, int flags) +{ + int ret; + + if (flags & HWLOC_MEMBIND_BYNODESET) { + ret = hwloc_get_proc_membind_by_nodeset(topology, pid, set, policy, flags); + } else { + hwloc_nodeset_t nodeset = hwloc_bitmap_alloc(); + ret = hwloc_get_proc_membind_by_nodeset(topology, pid, nodeset, policy, flags); + if (!ret) + hwloc_cpuset_from_nodeset(topology, set, nodeset); + hwloc_bitmap_free(nodeset); + } + + return ret; +} + +static int +hwloc_set_area_membind_by_nodeset(hwloc_topology_t topology, const void *addr, size_t len, hwloc_const_nodeset_t nodeset, hwloc_membind_policy_t policy, int flags) +{ + if ((flags & ~HWLOC_MEMBIND_ALLFLAGS) || hwloc__check_membind_policy(policy) < 0) { + errno = EINVAL; + return -1; + } + + if (!len) + /* nothing to do */ + return 0; + + nodeset = hwloc_fix_membind(topology, nodeset); + if (!nodeset) + return -1; + + if (topology->binding_hooks.set_area_membind) + return topology->binding_hooks.set_area_membind(topology, addr, len, nodeset, policy, flags); + + errno = ENOSYS; + return -1; +} + +int +hwloc_set_area_membind(hwloc_topology_t topology, const void *addr, size_t len, hwloc_const_bitmap_t set, hwloc_membind_policy_t policy, int flags) +{ + int ret; + + if (flags & HWLOC_MEMBIND_BYNODESET) { + ret = hwloc_set_area_membind_by_nodeset(topology, addr, len, set, policy, flags); + } else { + hwloc_nodeset_t nodeset = hwloc_bitmap_alloc(); + if (hwloc_fix_membind_cpuset(topology, nodeset, set)) + ret = -1; + else + ret = hwloc_set_area_membind_by_nodeset(topology, addr, len, nodeset, policy, flags); + hwloc_bitmap_free(nodeset); + } + + return ret; +} + +static int +hwloc_get_area_membind_by_nodeset(hwloc_topology_t topology, const void *addr, size_t len, hwloc_nodeset_t nodeset, hwloc_membind_policy_t * policy, int flags) +{ + if (flags & ~HWLOC_MEMBIND_ALLFLAGS) { + errno = EINVAL; + return -1; + } + + if (!len) { + /* nothing to query */ + errno = EINVAL; + return -1; + } + + if (topology->binding_hooks.get_area_membind) + return topology->binding_hooks.get_area_membind(topology, addr, len, nodeset, policy, flags); + + errno = ENOSYS; + return -1; +} + +int +hwloc_get_area_membind(hwloc_topology_t topology, const void *addr, size_t len, hwloc_bitmap_t set, hwloc_membind_policy_t * policy, int flags) +{ + int ret; + + if (flags & HWLOC_MEMBIND_BYNODESET) { + ret = hwloc_get_area_membind_by_nodeset(topology, addr, len, set, policy, flags); + } else { + hwloc_nodeset_t nodeset = hwloc_bitmap_alloc(); + ret = hwloc_get_area_membind_by_nodeset(topology, addr, len, nodeset, policy, flags); + if (!ret) + hwloc_cpuset_from_nodeset(topology, set, nodeset); + hwloc_bitmap_free(nodeset); + } + + return ret; +} + +static int +hwloc_get_area_memlocation_by_nodeset(hwloc_topology_t topology, const void *addr, size_t len, hwloc_nodeset_t nodeset, int flags) +{ + if (flags & ~HWLOC_MEMBIND_ALLFLAGS) { + errno = EINVAL; + return -1; + } + + if (!len) + /* nothing to do */ + return 0; + + if (topology->binding_hooks.get_area_memlocation) + return topology->binding_hooks.get_area_memlocation(topology, addr, len, nodeset, flags); + + errno = ENOSYS; + return -1; +} + +int +hwloc_get_area_memlocation(hwloc_topology_t topology, const void *addr, size_t len, hwloc_cpuset_t set, int flags) +{ + int ret; + + if (flags & HWLOC_MEMBIND_BYNODESET) { + ret = hwloc_get_area_memlocation_by_nodeset(topology, addr, len, set, flags); + } else { + hwloc_nodeset_t nodeset = hwloc_bitmap_alloc(); + ret = hwloc_get_area_memlocation_by_nodeset(topology, addr, len, nodeset, flags); + if (!ret) + hwloc_cpuset_from_nodeset(topology, set, nodeset); + hwloc_bitmap_free(nodeset); + } + + return ret; +} + +void * +hwloc_alloc_heap(hwloc_topology_t topology __hwloc_attribute_unused, size_t len) +{ + void *p = NULL; +#if defined(hwloc_getpagesize) && defined(HAVE_POSIX_MEMALIGN) + errno = posix_memalign(&p, hwloc_getpagesize(), len); + if (errno) + p = NULL; +#elif defined(hwloc_getpagesize) && defined(HAVE_MEMALIGN) + p = memalign(hwloc_getpagesize(), len); +#else + p = malloc(len); +#endif + return p; +} + +#ifdef MAP_ANONYMOUS +void * +hwloc_alloc_mmap(hwloc_topology_t topology __hwloc_attribute_unused, size_t len) +{ + void * buffer = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + return buffer == MAP_FAILED ? NULL : buffer; +} +#endif + +int +hwloc_free_heap(hwloc_topology_t topology __hwloc_attribute_unused, void *addr, size_t len __hwloc_attribute_unused) +{ + free(addr); + return 0; +} + +#ifdef MAP_ANONYMOUS +int +hwloc_free_mmap(hwloc_topology_t topology __hwloc_attribute_unused, void *addr, size_t len) +{ + if (!addr) + return 0; + return munmap(addr, len); +} +#endif + +void * +hwloc_alloc(hwloc_topology_t topology, size_t len) +{ + if (topology->binding_hooks.alloc) + return topology->binding_hooks.alloc(topology, len); + return hwloc_alloc_heap(topology, len); +} + +static void * +hwloc_alloc_membind_by_nodeset(hwloc_topology_t topology, size_t len, hwloc_const_nodeset_t nodeset, hwloc_membind_policy_t policy, int flags) +{ + void *p; + + if ((flags & ~HWLOC_MEMBIND_ALLFLAGS) || hwloc__check_membind_policy(policy) < 0) { + errno = EINVAL; + return NULL; + } + + nodeset = hwloc_fix_membind(topology, nodeset); + if (!nodeset) + goto fallback; + if (flags & HWLOC_MEMBIND_MIGRATE) { + errno = EINVAL; + goto fallback; + } + + if (topology->binding_hooks.alloc_membind) + return topology->binding_hooks.alloc_membind(topology, len, nodeset, policy, flags); + else if (topology->binding_hooks.set_area_membind) { + p = hwloc_alloc(topology, len); + if (!p) + return NULL; + if (topology->binding_hooks.set_area_membind(topology, p, len, nodeset, policy, flags) && flags & HWLOC_MEMBIND_STRICT) { + int error = errno; + free(p); + errno = error; + return NULL; + } + return p; + } else { + errno = ENOSYS; + } + +fallback: + if (flags & HWLOC_MEMBIND_STRICT) + /* Report error */ + return NULL; + /* Never mind, allocate anyway */ + return hwloc_alloc(topology, len); +} + +void * +hwloc_alloc_membind(hwloc_topology_t topology, size_t len, hwloc_const_bitmap_t set, hwloc_membind_policy_t policy, int flags) +{ + void *ret; + + if (flags & HWLOC_MEMBIND_BYNODESET) { + ret = hwloc_alloc_membind_by_nodeset(topology, len, set, policy, flags); + } else { + hwloc_nodeset_t nodeset = hwloc_bitmap_alloc(); + if (hwloc_fix_membind_cpuset(topology, nodeset, set)) { + if (flags & HWLOC_MEMBIND_STRICT) + ret = NULL; + else + ret = hwloc_alloc(topology, len); + } else + ret = hwloc_alloc_membind_by_nodeset(topology, len, nodeset, policy, flags); + hwloc_bitmap_free(nodeset); + } + + return ret; +} + +int +hwloc_free(hwloc_topology_t topology, void *addr, size_t len) +{ + if (topology->binding_hooks.free_membind) + return topology->binding_hooks.free_membind(topology, addr, len); + return hwloc_free_heap(topology, addr, len); +} + +/* + * Empty binding hooks always returning success + */ + +static int dontset_return_complete_cpuset(hwloc_topology_t topology, hwloc_cpuset_t set) +{ + hwloc_bitmap_copy(set, hwloc_topology_get_complete_cpuset(topology)); + return 0; +} + +static int dontset_thisthread_cpubind(hwloc_topology_t topology __hwloc_attribute_unused, hwloc_const_bitmap_t set __hwloc_attribute_unused, int flags __hwloc_attribute_unused) +{ + return 0; +} +static int dontget_thisthread_cpubind(hwloc_topology_t topology __hwloc_attribute_unused, hwloc_bitmap_t set, int flags __hwloc_attribute_unused) +{ + return dontset_return_complete_cpuset(topology, set); +} +static int dontset_thisproc_cpubind(hwloc_topology_t topology __hwloc_attribute_unused, hwloc_const_bitmap_t set __hwloc_attribute_unused, int flags __hwloc_attribute_unused) +{ + return 0; +} +static int dontget_thisproc_cpubind(hwloc_topology_t topology __hwloc_attribute_unused, hwloc_bitmap_t set, int flags __hwloc_attribute_unused) +{ + return dontset_return_complete_cpuset(topology, set); +} +static int dontset_proc_cpubind(hwloc_topology_t topology __hwloc_attribute_unused, hwloc_pid_t pid __hwloc_attribute_unused, hwloc_const_bitmap_t set __hwloc_attribute_unused, int flags __hwloc_attribute_unused) +{ + return 0; +} +static int dontget_proc_cpubind(hwloc_topology_t topology __hwloc_attribute_unused, hwloc_pid_t pid __hwloc_attribute_unused, hwloc_bitmap_t cpuset, int flags __hwloc_attribute_unused) +{ + return dontset_return_complete_cpuset(topology, cpuset); +} +#ifdef hwloc_thread_t +static int dontset_thread_cpubind(hwloc_topology_t topology __hwloc_attribute_unused, hwloc_thread_t tid __hwloc_attribute_unused, hwloc_const_bitmap_t set __hwloc_attribute_unused, int flags __hwloc_attribute_unused) +{ + return 0; +} +static int dontget_thread_cpubind(hwloc_topology_t topology __hwloc_attribute_unused, hwloc_thread_t tid __hwloc_attribute_unused, hwloc_bitmap_t cpuset, int flags __hwloc_attribute_unused) +{ + return dontset_return_complete_cpuset(topology, cpuset); +} +#endif + +static int dontset_return_complete_nodeset(hwloc_topology_t topology, hwloc_nodeset_t set, hwloc_membind_policy_t *policy) +{ + hwloc_bitmap_copy(set, hwloc_topology_get_complete_nodeset(topology)); + *policy = HWLOC_MEMBIND_MIXED; + return 0; +} + +static int dontset_thisproc_membind(hwloc_topology_t topology __hwloc_attribute_unused, hwloc_const_bitmap_t set __hwloc_attribute_unused, hwloc_membind_policy_t policy __hwloc_attribute_unused, int flags __hwloc_attribute_unused) +{ + return 0; +} +static int dontget_thisproc_membind(hwloc_topology_t topology __hwloc_attribute_unused, hwloc_bitmap_t set, hwloc_membind_policy_t * policy, int flags __hwloc_attribute_unused) +{ + return dontset_return_complete_nodeset(topology, set, policy); +} + +static int dontset_thisthread_membind(hwloc_topology_t topology __hwloc_attribute_unused, hwloc_const_bitmap_t set __hwloc_attribute_unused, hwloc_membind_policy_t policy __hwloc_attribute_unused, int flags __hwloc_attribute_unused) +{ + return 0; +} +static int dontget_thisthread_membind(hwloc_topology_t topology __hwloc_attribute_unused, hwloc_bitmap_t set, hwloc_membind_policy_t * policy, int flags __hwloc_attribute_unused) +{ + return dontset_return_complete_nodeset(topology, set, policy); +} + +static int dontset_proc_membind(hwloc_topology_t topology __hwloc_attribute_unused, hwloc_pid_t pid __hwloc_attribute_unused, hwloc_const_bitmap_t set __hwloc_attribute_unused, hwloc_membind_policy_t policy __hwloc_attribute_unused, int flags __hwloc_attribute_unused) +{ + return 0; +} +static int dontget_proc_membind(hwloc_topology_t topology __hwloc_attribute_unused, hwloc_pid_t pid __hwloc_attribute_unused, hwloc_bitmap_t set, hwloc_membind_policy_t * policy, int flags __hwloc_attribute_unused) +{ + return dontset_return_complete_nodeset(topology, set, policy); +} + +static int dontset_area_membind(hwloc_topology_t topology __hwloc_attribute_unused, const void *addr __hwloc_attribute_unused, size_t size __hwloc_attribute_unused, hwloc_const_bitmap_t set __hwloc_attribute_unused, hwloc_membind_policy_t policy __hwloc_attribute_unused, int flags __hwloc_attribute_unused) +{ + return 0; +} +static int dontget_area_membind(hwloc_topology_t topology __hwloc_attribute_unused, const void *addr __hwloc_attribute_unused, size_t size __hwloc_attribute_unused, hwloc_bitmap_t set, hwloc_membind_policy_t * policy, int flags __hwloc_attribute_unused) +{ + return dontset_return_complete_nodeset(topology, set, policy); +} +static int dontget_area_memlocation(hwloc_topology_t topology __hwloc_attribute_unused, const void *addr __hwloc_attribute_unused, size_t size __hwloc_attribute_unused, hwloc_bitmap_t set, int flags __hwloc_attribute_unused) +{ + hwloc_membind_policy_t policy; + return dontset_return_complete_nodeset(topology, set, &policy); +} + +static void * dontalloc_membind(hwloc_topology_t topology __hwloc_attribute_unused, size_t size __hwloc_attribute_unused, hwloc_const_bitmap_t set __hwloc_attribute_unused, hwloc_membind_policy_t policy __hwloc_attribute_unused, int flags __hwloc_attribute_unused) +{ + return malloc(size); +} +static int dontfree_membind(hwloc_topology_t topology __hwloc_attribute_unused, void *addr __hwloc_attribute_unused, size_t size __hwloc_attribute_unused) +{ + free(addr); + return 0; +} + +static void hwloc_set_dummy_hooks(struct hwloc_binding_hooks *hooks, + struct hwloc_topology_support *support __hwloc_attribute_unused) +{ + hooks->set_thisproc_cpubind = dontset_thisproc_cpubind; + hooks->get_thisproc_cpubind = dontget_thisproc_cpubind; + hooks->set_thisthread_cpubind = dontset_thisthread_cpubind; + hooks->get_thisthread_cpubind = dontget_thisthread_cpubind; + hooks->set_proc_cpubind = dontset_proc_cpubind; + hooks->get_proc_cpubind = dontget_proc_cpubind; +#ifdef hwloc_thread_t + hooks->set_thread_cpubind = dontset_thread_cpubind; + hooks->get_thread_cpubind = dontget_thread_cpubind; +#endif + hooks->get_thisproc_last_cpu_location = dontget_thisproc_cpubind; /* cpubind instead of last_cpu_location is ok */ + hooks->get_thisthread_last_cpu_location = dontget_thisthread_cpubind; /* cpubind instead of last_cpu_location is ok */ + hooks->get_proc_last_cpu_location = dontget_proc_cpubind; /* cpubind instead of last_cpu_location is ok */ + /* TODO: get_thread_last_cpu_location */ + hooks->set_thisproc_membind = dontset_thisproc_membind; + hooks->get_thisproc_membind = dontget_thisproc_membind; + hooks->set_thisthread_membind = dontset_thisthread_membind; + hooks->get_thisthread_membind = dontget_thisthread_membind; + hooks->set_proc_membind = dontset_proc_membind; + hooks->get_proc_membind = dontget_proc_membind; + hooks->set_area_membind = dontset_area_membind; + hooks->get_area_membind = dontget_area_membind; + hooks->get_area_memlocation = dontget_area_memlocation; + hooks->alloc_membind = dontalloc_membind; + hooks->free_membind = dontfree_membind; +} + +void +hwloc_set_native_binding_hooks(struct hwloc_binding_hooks *hooks, struct hwloc_topology_support *support) +{ +# ifdef HWLOC_LINUX_SYS + hwloc_set_linuxfs_hooks(hooks, support); +# endif /* HWLOC_LINUX_SYS */ + +# ifdef HWLOC_BGQ_SYS + hwloc_set_bgq_hooks(hooks, support); +# endif /* HWLOC_BGQ_SYS */ + +# ifdef HWLOC_AIX_SYS + hwloc_set_aix_hooks(hooks, support); +# endif /* HWLOC_AIX_SYS */ + +# ifdef HWLOC_SOLARIS_SYS + hwloc_set_solaris_hooks(hooks, support); +# endif /* HWLOC_SOLARIS_SYS */ + +# ifdef HWLOC_WIN_SYS + hwloc_set_windows_hooks(hooks, support); +# endif /* HWLOC_WIN_SYS */ + +# ifdef HWLOC_DARWIN_SYS + hwloc_set_darwin_hooks(hooks, support); +# endif /* HWLOC_DARWIN_SYS */ + +# ifdef HWLOC_FREEBSD_SYS + hwloc_set_freebsd_hooks(hooks, support); +# endif /* HWLOC_FREEBSD_SYS */ + +# ifdef HWLOC_NETBSD_SYS + hwloc_set_netbsd_hooks(hooks, support); +# endif /* HWLOC_NETBSD_SYS */ + +# ifdef HWLOC_HPUX_SYS + hwloc_set_hpux_hooks(hooks, support); +# endif /* HWLOC_HPUX_SYS */ +} + +/* If the represented system is actually not this system, use dummy binding hooks. */ +void +hwloc_set_binding_hooks(struct hwloc_topology *topology) +{ + if (topology->is_thissystem) { + hwloc_set_native_binding_hooks(&topology->binding_hooks, &topology->support); + /* every hook not set above will return ENOSYS */ + } else { + /* not this system, use dummy binding hooks that do nothing (but don't return ENOSYS) */ + hwloc_set_dummy_hooks(&topology->binding_hooks, &topology->support); + } + + /* if not is_thissystem, set_cpubind is fake + * and get_cpubind returns the whole system cpuset, + * so don't report that set/get_cpubind as supported + */ + if (topology->is_thissystem) { +#define DO(which,kind) \ + if (topology->binding_hooks.kind) \ + topology->support.which##bind->kind = 1; + DO(cpu,set_thisproc_cpubind); + DO(cpu,get_thisproc_cpubind); + DO(cpu,set_proc_cpubind); + DO(cpu,get_proc_cpubind); + DO(cpu,set_thisthread_cpubind); + DO(cpu,get_thisthread_cpubind); +#ifdef hwloc_thread_t + DO(cpu,set_thread_cpubind); + DO(cpu,get_thread_cpubind); +#endif + DO(cpu,get_thisproc_last_cpu_location); + DO(cpu,get_proc_last_cpu_location); + DO(cpu,get_thisthread_last_cpu_location); + DO(mem,set_thisproc_membind); + DO(mem,get_thisproc_membind); + DO(mem,set_thisthread_membind); + DO(mem,get_thisthread_membind); + DO(mem,set_proc_membind); + DO(mem,get_proc_membind); + DO(mem,set_area_membind); + DO(mem,get_area_membind); + DO(mem,get_area_memlocation); + DO(mem,alloc_membind); + } +} diff --git a/src/3rdparty/hwloc/src/bitmap.c b/src/3rdparty/hwloc/src/bitmap.c new file mode 100644 index 00000000..ea1264af --- /dev/null +++ b/src/3rdparty/hwloc/src/bitmap.c @@ -0,0 +1,1676 @@ +/* + * Copyright © 2009 CNRS + * Copyright © 2009-2017 Inria. All rights reserved. + * Copyright © 2009-2011 Université Bordeaux + * Copyright © 2009-2011 Cisco Systems, Inc. All rights reserved. + * See COPYING in top-level directory. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* + * possible improvements: + * - have a way to change the initial allocation size: + * add hwloc_bitmap_set_foo() to changes a global here, + * and make the hwloc core call based on the early number of PUs + * - make HWLOC_BITMAP_PREALLOC_BITS configurable, and detectable + * by parsing /proc/cpuinfo during configure on Linux. + * - preallocate inside the bitmap structure (so that the whole structure is a cacheline for instance) + * and allocate a dedicated array only later when reallocating larger + * - add a bitmap->ulongs_empty_first which guarantees that some first ulongs are empty, + * making tests much faster for big bitmaps since there's no need to look at first ulongs. + * no need for ulongs_empty_first to be exactly the max number of empty ulongs, + * clearing bits that were set earlier isn't very common. + */ + +/* magic number */ +#define HWLOC_BITMAP_MAGIC 0x20091007 + +/* preallocated bits in every bitmap */ +#define HWLOC_BITMAP_PREALLOC_BITS 512 +#define HWLOC_BITMAP_PREALLOC_ULONGS (HWLOC_BITMAP_PREALLOC_BITS/HWLOC_BITS_PER_LONG) + +/* actual opaque type internals */ +struct hwloc_bitmap_s { + unsigned ulongs_count; /* how many ulong bitmasks are valid, >= 1 */ + unsigned ulongs_allocated; /* how many ulong bitmasks are allocated, >= ulongs_count */ + unsigned long *ulongs; + int infinite; /* set to 1 if all bits beyond ulongs are set */ +#ifdef HWLOC_DEBUG + int magic; +#endif +}; + +/* overzealous check in debug-mode, not as powerful as valgrind but still useful */ +#ifdef HWLOC_DEBUG +#define HWLOC__BITMAP_CHECK(set) do { \ + assert((set)->magic == HWLOC_BITMAP_MAGIC); \ + assert((set)->ulongs_count >= 1); \ + assert((set)->ulongs_allocated >= (set)->ulongs_count); \ +} while (0) +#else +#define HWLOC__BITMAP_CHECK(set) +#endif + +/* extract a subset from a set using an index or a cpu */ +#define HWLOC_SUBBITMAP_INDEX(cpu) ((cpu)/(HWLOC_BITS_PER_LONG)) +#define HWLOC_SUBBITMAP_CPU_ULBIT(cpu) ((cpu)%(HWLOC_BITS_PER_LONG)) +/* Read from a bitmap ulong without knowing whether x is valid. + * Writers should make sure that x is valid and modify set->ulongs[x] directly. + */ +#define HWLOC_SUBBITMAP_READULONG(set,x) ((x) < (set)->ulongs_count ? (set)->ulongs[x] : (set)->infinite ? HWLOC_SUBBITMAP_FULL : HWLOC_SUBBITMAP_ZERO) + +/* predefined subset values */ +#define HWLOC_SUBBITMAP_ZERO 0UL +#define HWLOC_SUBBITMAP_FULL (~0UL) +#define HWLOC_SUBBITMAP_ULBIT(bit) (1UL<<(bit)) +#define HWLOC_SUBBITMAP_CPU(cpu) HWLOC_SUBBITMAP_ULBIT(HWLOC_SUBBITMAP_CPU_ULBIT(cpu)) +#define HWLOC_SUBBITMAP_ULBIT_TO(bit) (HWLOC_SUBBITMAP_FULL>>(HWLOC_BITS_PER_LONG-1-(bit))) +#define HWLOC_SUBBITMAP_ULBIT_FROM(bit) (HWLOC_SUBBITMAP_FULL<<(bit)) +#define HWLOC_SUBBITMAP_ULBIT_FROMTO(begin,end) (HWLOC_SUBBITMAP_ULBIT_TO(end) & HWLOC_SUBBITMAP_ULBIT_FROM(begin)) + +struct hwloc_bitmap_s * hwloc_bitmap_alloc(void) +{ + struct hwloc_bitmap_s * set; + + set = malloc(sizeof(struct hwloc_bitmap_s)); + if (!set) + return NULL; + + set->ulongs_count = 1; + set->ulongs_allocated = HWLOC_BITMAP_PREALLOC_ULONGS; + set->ulongs = malloc(HWLOC_BITMAP_PREALLOC_ULONGS * sizeof(unsigned long)); + if (!set->ulongs) { + free(set); + return NULL; + } + + set->ulongs[0] = HWLOC_SUBBITMAP_ZERO; + set->infinite = 0; +#ifdef HWLOC_DEBUG + set->magic = HWLOC_BITMAP_MAGIC; +#endif + return set; +} + +struct hwloc_bitmap_s * hwloc_bitmap_alloc_full(void) +{ + struct hwloc_bitmap_s * set = hwloc_bitmap_alloc(); + if (set) { + set->infinite = 1; + set->ulongs[0] = HWLOC_SUBBITMAP_FULL; + } + return set; +} + +void hwloc_bitmap_free(struct hwloc_bitmap_s * set) +{ + if (!set) + return; + + HWLOC__BITMAP_CHECK(set); +#ifdef HWLOC_DEBUG + set->magic = 0; +#endif + + free(set->ulongs); + free(set); +} + +/* enlarge until it contains at least needed_count ulongs. + */ +static int +hwloc_bitmap_enlarge_by_ulongs(struct hwloc_bitmap_s * set, unsigned needed_count) __hwloc_attribute_warn_unused_result; +static int +hwloc_bitmap_enlarge_by_ulongs(struct hwloc_bitmap_s * set, unsigned needed_count) +{ + unsigned tmp = 1U << hwloc_flsl((unsigned long) needed_count - 1); + if (tmp > set->ulongs_allocated) { + unsigned long *tmpulongs; + tmpulongs = realloc(set->ulongs, tmp * sizeof(unsigned long)); + if (!tmpulongs) + return -1; + set->ulongs = tmpulongs; + set->ulongs_allocated = tmp; + } + return 0; +} + +/* enlarge until it contains at least needed_count ulongs, + * and update new ulongs according to the infinite field. + */ +static int +hwloc_bitmap_realloc_by_ulongs(struct hwloc_bitmap_s * set, unsigned needed_count) __hwloc_attribute_warn_unused_result; +static int +hwloc_bitmap_realloc_by_ulongs(struct hwloc_bitmap_s * set, unsigned needed_count) +{ + unsigned i; + + HWLOC__BITMAP_CHECK(set); + + if (needed_count <= set->ulongs_count) + return 0; + + /* realloc larger if needed */ + if (hwloc_bitmap_enlarge_by_ulongs(set, needed_count) < 0) + return -1; + + /* fill the newly allocated subset depending on the infinite flag */ + for(i=set->ulongs_count; iulongs[i] = set->infinite ? HWLOC_SUBBITMAP_FULL : HWLOC_SUBBITMAP_ZERO; + set->ulongs_count = needed_count; + return 0; +} + +/* realloc until it contains at least cpu+1 bits */ +#define hwloc_bitmap_realloc_by_cpu_index(set, cpu) hwloc_bitmap_realloc_by_ulongs(set, ((cpu)/HWLOC_BITS_PER_LONG)+1) + +/* reset a bitmap to exactely the needed size. + * the caller must reinitialize all ulongs and the infinite flag later. + */ +static int +hwloc_bitmap_reset_by_ulongs(struct hwloc_bitmap_s * set, unsigned needed_count) __hwloc_attribute_warn_unused_result; +static int +hwloc_bitmap_reset_by_ulongs(struct hwloc_bitmap_s * set, unsigned needed_count) +{ + if (hwloc_bitmap_enlarge_by_ulongs(set, needed_count)) + return -1; + set->ulongs_count = needed_count; + return 0; +} + +/* reset until it contains exactly cpu+1 bits (roundup to a ulong). + * the caller must reinitialize all ulongs and the infinite flag later. + */ +#define hwloc_bitmap_reset_by_cpu_index(set, cpu) hwloc_bitmap_reset_by_ulongs(set, ((cpu)/HWLOC_BITS_PER_LONG)+1) + +struct hwloc_bitmap_s * hwloc_bitmap_tma_dup(struct hwloc_tma *tma, const struct hwloc_bitmap_s * old) +{ + struct hwloc_bitmap_s * new; + + if (!old) + return NULL; + + HWLOC__BITMAP_CHECK(old); + + new = hwloc_tma_malloc(tma, sizeof(struct hwloc_bitmap_s)); + if (!new) + return NULL; + + new->ulongs = hwloc_tma_malloc(tma, old->ulongs_allocated * sizeof(unsigned long)); + if (!new->ulongs) { + free(new); + return NULL; + } + new->ulongs_allocated = old->ulongs_allocated; + new->ulongs_count = old->ulongs_count; + memcpy(new->ulongs, old->ulongs, new->ulongs_count * sizeof(unsigned long)); + new->infinite = old->infinite; +#ifdef HWLOC_DEBUG + new->magic = HWLOC_BITMAP_MAGIC; +#endif + return new; +} + +struct hwloc_bitmap_s * hwloc_bitmap_dup(const struct hwloc_bitmap_s * old) +{ + return hwloc_bitmap_tma_dup(NULL, old); +} + +int hwloc_bitmap_copy(struct hwloc_bitmap_s * dst, const struct hwloc_bitmap_s * src) +{ + HWLOC__BITMAP_CHECK(dst); + HWLOC__BITMAP_CHECK(src); + + if (hwloc_bitmap_reset_by_ulongs(dst, src->ulongs_count) < 0) + return -1; + + memcpy(dst->ulongs, src->ulongs, src->ulongs_count * sizeof(unsigned long)); + dst->infinite = src->infinite; + return 0; +} + +/* Strings always use 32bit groups */ +#define HWLOC_PRIxSUBBITMAP "%08lx" +#define HWLOC_BITMAP_SUBSTRING_SIZE 32 +#define HWLOC_BITMAP_SUBSTRING_LENGTH (HWLOC_BITMAP_SUBSTRING_SIZE/4) +#define HWLOC_BITMAP_STRING_PER_LONG (HWLOC_BITS_PER_LONG/HWLOC_BITMAP_SUBSTRING_SIZE) + +int hwloc_bitmap_snprintf(char * __hwloc_restrict buf, size_t buflen, const struct hwloc_bitmap_s * __hwloc_restrict set) +{ + ssize_t size = buflen; + char *tmp = buf; + int res, ret = 0; + int needcomma = 0; + int i; + unsigned long accum = 0; + int accumed = 0; +#if HWLOC_BITS_PER_LONG == HWLOC_BITMAP_SUBSTRING_SIZE + const unsigned long accum_mask = ~0UL; +#else /* HWLOC_BITS_PER_LONG != HWLOC_BITMAP_SUBSTRING_SIZE */ + const unsigned long accum_mask = ((1UL << HWLOC_BITMAP_SUBSTRING_SIZE) - 1) << (HWLOC_BITS_PER_LONG - HWLOC_BITMAP_SUBSTRING_SIZE); +#endif /* HWLOC_BITS_PER_LONG != HWLOC_BITMAP_SUBSTRING_SIZE */ + + HWLOC__BITMAP_CHECK(set); + + /* mark the end in case we do nothing later */ + if (buflen > 0) + tmp[0] = '\0'; + + if (set->infinite) { + res = hwloc_snprintf(tmp, size, "0xf...f"); + needcomma = 1; + if (res < 0) + return -1; + ret += res; + if (res >= size) + res = size>0 ? (int)size - 1 : 0; + tmp += res; + size -= res; + } + + i=(int) set->ulongs_count-1; + + if (set->infinite) { + /* ignore starting FULL since we have 0xf...f already */ + while (i>=0 && set->ulongs[i] == HWLOC_SUBBITMAP_FULL) + i--; + } else { + /* ignore starting ZERO except the last one */ + while (i>=0 && set->ulongs[i] == HWLOC_SUBBITMAP_ZERO) + i--; + } + + while (i>=0 || accumed) { + /* Refill accumulator */ + if (!accumed) { + accum = set->ulongs[i--]; + accumed = HWLOC_BITS_PER_LONG; + } + + if (accum & accum_mask) { + /* print the whole subset if not empty */ + res = hwloc_snprintf(tmp, size, needcomma ? ",0x" HWLOC_PRIxSUBBITMAP : "0x" HWLOC_PRIxSUBBITMAP, + (accum & accum_mask) >> (HWLOC_BITS_PER_LONG - HWLOC_BITMAP_SUBSTRING_SIZE)); + needcomma = 1; + } else if (i == -1 && accumed == HWLOC_BITMAP_SUBSTRING_SIZE) { + /* print a single 0 to mark the last subset */ + res = hwloc_snprintf(tmp, size, needcomma ? ",0x0" : "0x0"); + } else if (needcomma) { + res = hwloc_snprintf(tmp, size, ","); + } else { + res = 0; + } + if (res < 0) + return -1; + ret += res; + +#if HWLOC_BITS_PER_LONG == HWLOC_BITMAP_SUBSTRING_SIZE + accum = 0; + accumed = 0; +#else + accum <<= HWLOC_BITMAP_SUBSTRING_SIZE; + accumed -= HWLOC_BITMAP_SUBSTRING_SIZE; +#endif + + if (res >= size) + res = size>0 ? (int)size - 1 : 0; + + tmp += res; + size -= res; + } + + /* if didn't display anything, display 0x0 */ + if (!ret) { + res = hwloc_snprintf(tmp, size, "0x0"); + if (res < 0) + return -1; + ret += res; + } + + return ret; +} + +int hwloc_bitmap_asprintf(char ** strp, const struct hwloc_bitmap_s * __hwloc_restrict set) +{ + int len; + char *buf; + + HWLOC__BITMAP_CHECK(set); + + len = hwloc_bitmap_snprintf(NULL, 0, set); + buf = malloc(len+1); + if (!buf) + return -1; + *strp = buf; + return hwloc_bitmap_snprintf(buf, len+1, set); +} + +int hwloc_bitmap_sscanf(struct hwloc_bitmap_s *set, const char * __hwloc_restrict string) +{ + const char * current = string; + unsigned long accum = 0; + int count=0; + int infinite = 0; + + /* count how many substrings there are */ + count++; + while ((current = strchr(current+1, ',')) != NULL) + count++; + + current = string; + if (!strncmp("0xf...f", current, 7)) { + current += 7; + if (*current != ',') { + /* special case for infinite/full bitmap */ + hwloc_bitmap_fill(set); + return 0; + } + current++; + infinite = 1; + count--; + } + + if (hwloc_bitmap_reset_by_ulongs(set, (count + HWLOC_BITMAP_STRING_PER_LONG - 1) / HWLOC_BITMAP_STRING_PER_LONG) < 0) + return -1; + set->infinite = 0; + + while (*current != '\0') { + unsigned long val; + char *next; + val = strtoul(current, &next, 16); + + assert(count > 0); + count--; + + accum |= (val << ((count * HWLOC_BITMAP_SUBSTRING_SIZE) % HWLOC_BITS_PER_LONG)); + if (!(count % HWLOC_BITMAP_STRING_PER_LONG)) { + set->ulongs[count / HWLOC_BITMAP_STRING_PER_LONG] = accum; + accum = 0; + } + + if (*next != ',') { + if (*next || count > 0) + goto failed; + else + break; + } + current = (const char*) next+1; + } + + set->infinite = infinite; /* set at the end, to avoid spurious realloc with filled new ulongs */ + + return 0; + + failed: + /* failure to parse */ + hwloc_bitmap_zero(set); + return -1; +} + +int hwloc_bitmap_list_snprintf(char * __hwloc_restrict buf, size_t buflen, const struct hwloc_bitmap_s * __hwloc_restrict set) +{ + int prev = -1; + ssize_t size = buflen; + char *tmp = buf; + int res, ret = 0; + int needcomma = 0; + + HWLOC__BITMAP_CHECK(set); + + /* mark the end in case we do nothing later */ + if (buflen > 0) + tmp[0] = '\0'; + + while (1) { + int begin, end; + + begin = hwloc_bitmap_next(set, prev); + if (begin == -1) + break; + end = hwloc_bitmap_next_unset(set, begin); + + if (end == begin+1) { + res = hwloc_snprintf(tmp, size, needcomma ? ",%d" : "%d", begin); + } else if (end == -1) { + res = hwloc_snprintf(tmp, size, needcomma ? ",%d-" : "%d-", begin); + } else { + res = hwloc_snprintf(tmp, size, needcomma ? ",%d-%d" : "%d-%d", begin, end-1); + } + if (res < 0) + return -1; + ret += res; + + if (res >= size) + res = size>0 ? (int)size - 1 : 0; + + tmp += res; + size -= res; + needcomma = 1; + + if (end == -1) + break; + else + prev = end - 1; + } + + return ret; +} + +int hwloc_bitmap_list_asprintf(char ** strp, const struct hwloc_bitmap_s * __hwloc_restrict set) +{ + int len; + char *buf; + + HWLOC__BITMAP_CHECK(set); + + len = hwloc_bitmap_list_snprintf(NULL, 0, set); + buf = malloc(len+1); + if (!buf) + return -1; + *strp = buf; + return hwloc_bitmap_list_snprintf(buf, len+1, set); +} + +int hwloc_bitmap_list_sscanf(struct hwloc_bitmap_s *set, const char * __hwloc_restrict string) +{ + const char * current = string; + char *next; + long begin = -1, val; + + hwloc_bitmap_zero(set); + + while (*current != '\0') { + + /* ignore empty ranges */ + while (*current == ',' || *current == ' ') + current++; + + val = strtoul(current, &next, 0); + /* make sure we got at least one digit */ + if (next == current) + goto failed; + + if (begin != -1) { + /* finishing a range */ + hwloc_bitmap_set_range(set, begin, val); + begin = -1; + + } else if (*next == '-') { + /* starting a new range */ + if (*(next+1) == '\0') { + /* infinite range */ + hwloc_bitmap_set_range(set, val, -1); + break; + } else { + /* normal range */ + begin = val; + } + + } else if (*next == ',' || *next == ' ' || *next == '\0') { + /* single digit */ + hwloc_bitmap_set(set, val); + } + + if (*next == '\0') + break; + current = next+1; + } + + return 0; + + failed: + /* failure to parse */ + hwloc_bitmap_zero(set); + return -1; +} + +int hwloc_bitmap_taskset_snprintf(char * __hwloc_restrict buf, size_t buflen, const struct hwloc_bitmap_s * __hwloc_restrict set) +{ + ssize_t size = buflen; + char *tmp = buf; + int res, ret = 0; + int started = 0; + int i; + + HWLOC__BITMAP_CHECK(set); + + /* mark the end in case we do nothing later */ + if (buflen > 0) + tmp[0] = '\0'; + + if (set->infinite) { + res = hwloc_snprintf(tmp, size, "0xf...f"); + started = 1; + if (res < 0) + return -1; + ret += res; + if (res >= size) + res = size>0 ? (int)size - 1 : 0; + tmp += res; + size -= res; + } + + i=set->ulongs_count-1; + + if (set->infinite) { + /* ignore starting FULL since we have 0xf...f already */ + while (i>=0 && set->ulongs[i] == HWLOC_SUBBITMAP_FULL) + i--; + } else { + /* ignore starting ZERO except the last one */ + while (i>=1 && set->ulongs[i] == HWLOC_SUBBITMAP_ZERO) + i--; + } + + while (i>=0) { + unsigned long val = set->ulongs[i--]; + if (started) { + /* print the whole subset */ +#if HWLOC_BITS_PER_LONG == 64 + res = hwloc_snprintf(tmp, size, "%016lx", val); +#else + res = hwloc_snprintf(tmp, size, "%08lx", val); +#endif + } else if (val || i == -1) { + res = hwloc_snprintf(tmp, size, "0x%lx", val); + started = 1; + } else { + res = 0; + } + if (res < 0) + return -1; + ret += res; + if (res >= size) + res = size>0 ? (int)size - 1 : 0; + tmp += res; + size -= res; + } + + /* if didn't display anything, display 0x0 */ + if (!ret) { + res = hwloc_snprintf(tmp, size, "0x0"); + if (res < 0) + return -1; + ret += res; + } + + return ret; +} + +int hwloc_bitmap_taskset_asprintf(char ** strp, const struct hwloc_bitmap_s * __hwloc_restrict set) +{ + int len; + char *buf; + + HWLOC__BITMAP_CHECK(set); + + len = hwloc_bitmap_taskset_snprintf(NULL, 0, set); + buf = malloc(len+1); + if (!buf) + return -1; + *strp = buf; + return hwloc_bitmap_taskset_snprintf(buf, len+1, set); +} + +int hwloc_bitmap_taskset_sscanf(struct hwloc_bitmap_s *set, const char * __hwloc_restrict string) +{ + const char * current = string; + int chars; + int count; + int infinite = 0; + + if (!strncmp("0xf...f", current, 7)) { + /* infinite bitmap */ + infinite = 1; + current += 7; + if (*current == '\0') { + /* special case for infinite/full bitmap */ + hwloc_bitmap_fill(set); + return 0; + } + } else { + /* finite bitmap */ + if (!strncmp("0x", current, 2)) + current += 2; + if (*current == '\0') { + /* special case for empty bitmap */ + hwloc_bitmap_zero(set); + return 0; + } + } + /* we know there are other characters now */ + + chars = (int)strlen(current); + count = (chars * 4 + HWLOC_BITS_PER_LONG - 1) / HWLOC_BITS_PER_LONG; + + if (hwloc_bitmap_reset_by_ulongs(set, count) < 0) + return -1; + set->infinite = 0; + + while (*current != '\0') { + int tmpchars; + char ustr[17]; + unsigned long val; + char *next; + + tmpchars = chars % (HWLOC_BITS_PER_LONG/4); + if (!tmpchars) + tmpchars = (HWLOC_BITS_PER_LONG/4); + + memcpy(ustr, current, tmpchars); + ustr[tmpchars] = '\0'; + val = strtoul(ustr, &next, 16); + if (*next != '\0') + goto failed; + + set->ulongs[count-1] = val; + + current += tmpchars; + chars -= tmpchars; + count--; + } + + set->infinite = infinite; /* set at the end, to avoid spurious realloc with filled new ulongs */ + + return 0; + + failed: + /* failure to parse */ + hwloc_bitmap_zero(set); + return -1; +} + +static void hwloc_bitmap__zero(struct hwloc_bitmap_s *set) +{ + unsigned i; + for(i=0; iulongs_count; i++) + set->ulongs[i] = HWLOC_SUBBITMAP_ZERO; + set->infinite = 0; +} + +void hwloc_bitmap_zero(struct hwloc_bitmap_s * set) +{ + HWLOC__BITMAP_CHECK(set); + + HWLOC_BUILD_ASSERT(HWLOC_BITMAP_PREALLOC_ULONGS >= 1); + if (hwloc_bitmap_reset_by_ulongs(set, 1) < 0) { + /* cannot fail since we preallocate some ulongs. + * if we ever preallocate nothing, we'll reset to 0 ulongs. + */ + } + hwloc_bitmap__zero(set); +} + +static void hwloc_bitmap__fill(struct hwloc_bitmap_s * set) +{ + unsigned i; + for(i=0; iulongs_count; i++) + set->ulongs[i] = HWLOC_SUBBITMAP_FULL; + set->infinite = 1; +} + +void hwloc_bitmap_fill(struct hwloc_bitmap_s * set) +{ + HWLOC__BITMAP_CHECK(set); + + HWLOC_BUILD_ASSERT(HWLOC_BITMAP_PREALLOC_ULONGS >= 1); + if (hwloc_bitmap_reset_by_ulongs(set, 1) < 0) { + /* cannot fail since we pre-allocate some ulongs. + * if we ever pre-allocate nothing, we'll reset to 0 ulongs. + */ + } + hwloc_bitmap__fill(set); +} + +int hwloc_bitmap_from_ulong(struct hwloc_bitmap_s *set, unsigned long mask) +{ + HWLOC__BITMAP_CHECK(set); + + HWLOC_BUILD_ASSERT(HWLOC_BITMAP_PREALLOC_ULONGS >= 1); + if (hwloc_bitmap_reset_by_ulongs(set, 1) < 0) { + /* cannot fail since we pre-allocate some ulongs. + * if ever pre-allocate nothing, we may have to return a failure. + */ + } + set->ulongs[0] = mask; /* there's always at least one ulong allocated */ + set->infinite = 0; + return 0; +} + +int hwloc_bitmap_from_ith_ulong(struct hwloc_bitmap_s *set, unsigned i, unsigned long mask) +{ + unsigned j; + + HWLOC__BITMAP_CHECK(set); + + if (hwloc_bitmap_reset_by_ulongs(set, i+1) < 0) + return -1; + + set->ulongs[i] = mask; + for(j=0; julongs[j] = HWLOC_SUBBITMAP_ZERO; + set->infinite = 0; + return 0; +} + +unsigned long hwloc_bitmap_to_ulong(const struct hwloc_bitmap_s *set) +{ + HWLOC__BITMAP_CHECK(set); + + return set->ulongs[0]; /* there's always at least one ulong allocated */ +} + +unsigned long hwloc_bitmap_to_ith_ulong(const struct hwloc_bitmap_s *set, unsigned i) +{ + HWLOC__BITMAP_CHECK(set); + + return HWLOC_SUBBITMAP_READULONG(set, i); +} + +int hwloc_bitmap_only(struct hwloc_bitmap_s * set, unsigned cpu) +{ + unsigned index_ = HWLOC_SUBBITMAP_INDEX(cpu); + + HWLOC__BITMAP_CHECK(set); + + if (hwloc_bitmap_reset_by_cpu_index(set, cpu) < 0) + return -1; + + hwloc_bitmap__zero(set); + set->ulongs[index_] |= HWLOC_SUBBITMAP_CPU(cpu); + return 0; +} + +int hwloc_bitmap_allbut(struct hwloc_bitmap_s * set, unsigned cpu) +{ + unsigned index_ = HWLOC_SUBBITMAP_INDEX(cpu); + + HWLOC__BITMAP_CHECK(set); + + if (hwloc_bitmap_reset_by_cpu_index(set, cpu) < 0) + return -1; + + hwloc_bitmap__fill(set); + set->ulongs[index_] &= ~HWLOC_SUBBITMAP_CPU(cpu); + return 0; +} + +int hwloc_bitmap_set(struct hwloc_bitmap_s * set, unsigned cpu) +{ + unsigned index_ = HWLOC_SUBBITMAP_INDEX(cpu); + + HWLOC__BITMAP_CHECK(set); + + /* nothing to do if setting inside the infinite part of the bitmap */ + if (set->infinite && cpu >= set->ulongs_count * HWLOC_BITS_PER_LONG) + return 0; + + if (hwloc_bitmap_realloc_by_cpu_index(set, cpu) < 0) + return -1; + + set->ulongs[index_] |= HWLOC_SUBBITMAP_CPU(cpu); + return 0; +} + +int hwloc_bitmap_set_range(struct hwloc_bitmap_s * set, unsigned begincpu, int _endcpu) +{ + unsigned i; + unsigned beginset,endset; + unsigned endcpu = (unsigned) _endcpu; + + HWLOC__BITMAP_CHECK(set); + + if (endcpu < begincpu) + return 0; + if (set->infinite && begincpu >= set->ulongs_count * HWLOC_BITS_PER_LONG) + /* setting only in the already-set infinite part, nothing to do */ + return 0; + + if (_endcpu == -1) { + /* infinite range */ + + /* make sure we can play with the ulong that contains begincpu */ + if (hwloc_bitmap_realloc_by_cpu_index(set, begincpu) < 0) + return -1; + + /* update the ulong that contains begincpu */ + beginset = HWLOC_SUBBITMAP_INDEX(begincpu); + set->ulongs[beginset] |= HWLOC_SUBBITMAP_ULBIT_FROM(HWLOC_SUBBITMAP_CPU_ULBIT(begincpu)); + /* set ulongs after begincpu if any already allocated */ + for(i=beginset+1; iulongs_count; i++) + set->ulongs[i] = HWLOC_SUBBITMAP_FULL; + /* mark the infinity as set */ + set->infinite = 1; + } else { + /* finite range */ + + /* ignore the part of the range that overlaps with the already-set infinite part */ + if (set->infinite && endcpu >= set->ulongs_count * HWLOC_BITS_PER_LONG) + endcpu = set->ulongs_count * HWLOC_BITS_PER_LONG - 1; + /* make sure we can play with the ulongs that contain begincpu and endcpu */ + if (hwloc_bitmap_realloc_by_cpu_index(set, endcpu) < 0) + return -1; + + /* update first and last ulongs */ + beginset = HWLOC_SUBBITMAP_INDEX(begincpu); + endset = HWLOC_SUBBITMAP_INDEX(endcpu); + if (beginset == endset) { + set->ulongs[beginset] |= HWLOC_SUBBITMAP_ULBIT_FROMTO(HWLOC_SUBBITMAP_CPU_ULBIT(begincpu), HWLOC_SUBBITMAP_CPU_ULBIT(endcpu)); + } else { + set->ulongs[beginset] |= HWLOC_SUBBITMAP_ULBIT_FROM(HWLOC_SUBBITMAP_CPU_ULBIT(begincpu)); + set->ulongs[endset] |= HWLOC_SUBBITMAP_ULBIT_TO(HWLOC_SUBBITMAP_CPU_ULBIT(endcpu)); + } + /* set ulongs in the middle of the range */ + for(i=beginset+1; iulongs[i] = HWLOC_SUBBITMAP_FULL; + } + + return 0; +} + +int hwloc_bitmap_set_ith_ulong(struct hwloc_bitmap_s *set, unsigned i, unsigned long mask) +{ + HWLOC__BITMAP_CHECK(set); + + if (hwloc_bitmap_realloc_by_ulongs(set, i+1) < 0) + return -1; + + set->ulongs[i] = mask; + return 0; +} + +int hwloc_bitmap_clr(struct hwloc_bitmap_s * set, unsigned cpu) +{ + unsigned index_ = HWLOC_SUBBITMAP_INDEX(cpu); + + HWLOC__BITMAP_CHECK(set); + + /* nothing to do if clearing inside the infinitely-unset part of the bitmap */ + if (!set->infinite && cpu >= set->ulongs_count * HWLOC_BITS_PER_LONG) + return 0; + + if (hwloc_bitmap_realloc_by_cpu_index(set, cpu) < 0) + return -1; + + set->ulongs[index_] &= ~HWLOC_SUBBITMAP_CPU(cpu); + return 0; +} + +int hwloc_bitmap_clr_range(struct hwloc_bitmap_s * set, unsigned begincpu, int _endcpu) +{ + unsigned i; + unsigned beginset,endset; + unsigned endcpu = (unsigned) _endcpu; + + HWLOC__BITMAP_CHECK(set); + + if (endcpu < begincpu) + return 0; + + if (!set->infinite && begincpu >= set->ulongs_count * HWLOC_BITS_PER_LONG) + /* clearing only in the already-unset infinite part, nothing to do */ + return 0; + + if (_endcpu == -1) { + /* infinite range */ + + /* make sure we can play with the ulong that contains begincpu */ + if (hwloc_bitmap_realloc_by_cpu_index(set, begincpu) < 0) + return -1; + + /* update the ulong that contains begincpu */ + beginset = HWLOC_SUBBITMAP_INDEX(begincpu); + set->ulongs[beginset] &= ~HWLOC_SUBBITMAP_ULBIT_FROM(HWLOC_SUBBITMAP_CPU_ULBIT(begincpu)); + /* clear ulong after begincpu if any already allocated */ + for(i=beginset+1; iulongs_count; i++) + set->ulongs[i] = HWLOC_SUBBITMAP_ZERO; + /* mark the infinity as unset */ + set->infinite = 0; + } else { + /* finite range */ + + /* ignore the part of the range that overlaps with the already-unset infinite part */ + if (!set->infinite && endcpu >= set->ulongs_count * HWLOC_BITS_PER_LONG) + endcpu = set->ulongs_count * HWLOC_BITS_PER_LONG - 1; + /* make sure we can play with the ulongs that contain begincpu and endcpu */ + if (hwloc_bitmap_realloc_by_cpu_index(set, endcpu) < 0) + return -1; + + /* update first and last ulongs */ + beginset = HWLOC_SUBBITMAP_INDEX(begincpu); + endset = HWLOC_SUBBITMAP_INDEX(endcpu); + if (beginset == endset) { + set->ulongs[beginset] &= ~HWLOC_SUBBITMAP_ULBIT_FROMTO(HWLOC_SUBBITMAP_CPU_ULBIT(begincpu), HWLOC_SUBBITMAP_CPU_ULBIT(endcpu)); + } else { + set->ulongs[beginset] &= ~HWLOC_SUBBITMAP_ULBIT_FROM(HWLOC_SUBBITMAP_CPU_ULBIT(begincpu)); + set->ulongs[endset] &= ~HWLOC_SUBBITMAP_ULBIT_TO(HWLOC_SUBBITMAP_CPU_ULBIT(endcpu)); + } + /* clear ulongs in the middle of the range */ + for(i=beginset+1; iulongs[i] = HWLOC_SUBBITMAP_ZERO; + } + + return 0; +} + +int hwloc_bitmap_isset(const struct hwloc_bitmap_s * set, unsigned cpu) +{ + unsigned index_ = HWLOC_SUBBITMAP_INDEX(cpu); + + HWLOC__BITMAP_CHECK(set); + + return (HWLOC_SUBBITMAP_READULONG(set, index_) & HWLOC_SUBBITMAP_CPU(cpu)) != 0; +} + +int hwloc_bitmap_iszero(const struct hwloc_bitmap_s *set) +{ + unsigned i; + + HWLOC__BITMAP_CHECK(set); + + if (set->infinite) + return 0; + for(i=0; iulongs_count; i++) + if (set->ulongs[i] != HWLOC_SUBBITMAP_ZERO) + return 0; + return 1; +} + +int hwloc_bitmap_isfull(const struct hwloc_bitmap_s *set) +{ + unsigned i; + + HWLOC__BITMAP_CHECK(set); + + if (!set->infinite) + return 0; + for(i=0; iulongs_count; i++) + if (set->ulongs[i] != HWLOC_SUBBITMAP_FULL) + return 0; + return 1; +} + +int hwloc_bitmap_isequal (const struct hwloc_bitmap_s *set1, const struct hwloc_bitmap_s *set2) +{ + unsigned count1 = set1->ulongs_count; + unsigned count2 = set2->ulongs_count; + unsigned min_count = count1 < count2 ? count1 : count2; + unsigned i; + + HWLOC__BITMAP_CHECK(set1); + HWLOC__BITMAP_CHECK(set2); + + for(i=0; iulongs[i] != set2->ulongs[i]) + return 0; + + if (count1 != count2) { + unsigned long w1 = set1->infinite ? HWLOC_SUBBITMAP_FULL : HWLOC_SUBBITMAP_ZERO; + unsigned long w2 = set2->infinite ? HWLOC_SUBBITMAP_FULL : HWLOC_SUBBITMAP_ZERO; + for(i=min_count; iulongs[i] != w2) + return 0; + } + for(i=min_count; iulongs[i] != w1) + return 0; + } + } + + if (set1->infinite != set2->infinite) + return 0; + + return 1; +} + +int hwloc_bitmap_intersects (const struct hwloc_bitmap_s *set1, const struct hwloc_bitmap_s *set2) +{ + unsigned count1 = set1->ulongs_count; + unsigned count2 = set2->ulongs_count; + unsigned min_count = count1 < count2 ? count1 : count2; + unsigned i; + + HWLOC__BITMAP_CHECK(set1); + HWLOC__BITMAP_CHECK(set2); + + for(i=0; iulongs[i] & set2->ulongs[i]) + return 1; + + if (count1 != count2) { + if (set2->infinite) { + for(i=min_count; iulongs_count; i++) + if (set1->ulongs[i]) + return 1; + } + if (set1->infinite) { + for(i=min_count; iulongs_count; i++) + if (set2->ulongs[i]) + return 1; + } + } + + if (set1->infinite && set2->infinite) + return 1; + + return 0; +} + +int hwloc_bitmap_isincluded (const struct hwloc_bitmap_s *sub_set, const struct hwloc_bitmap_s *super_set) +{ + unsigned super_count = super_set->ulongs_count; + unsigned sub_count = sub_set->ulongs_count; + unsigned min_count = super_count < sub_count ? super_count : sub_count; + unsigned i; + + HWLOC__BITMAP_CHECK(sub_set); + HWLOC__BITMAP_CHECK(super_set); + + for(i=0; iulongs[i] != (super_set->ulongs[i] | sub_set->ulongs[i])) + return 0; + + if (super_count != sub_count) { + if (!super_set->infinite) + for(i=min_count; iulongs[i]) + return 0; + if (sub_set->infinite) + for(i=min_count; iulongs[i] != HWLOC_SUBBITMAP_FULL) + return 0; + } + + if (sub_set->infinite && !super_set->infinite) + return 0; + + return 1; +} + +int hwloc_bitmap_or (struct hwloc_bitmap_s *res, const struct hwloc_bitmap_s *set1, const struct hwloc_bitmap_s *set2) +{ + /* cache counts so that we can reset res even if it's also set1 or set2 */ + unsigned count1 = set1->ulongs_count; + unsigned count2 = set2->ulongs_count; + unsigned max_count = count1 > count2 ? count1 : count2; + unsigned min_count = count1 + count2 - max_count; + unsigned i; + + HWLOC__BITMAP_CHECK(res); + HWLOC__BITMAP_CHECK(set1); + HWLOC__BITMAP_CHECK(set2); + + if (hwloc_bitmap_reset_by_ulongs(res, max_count) < 0) + return -1; + + for(i=0; iulongs[i] = set1->ulongs[i] | set2->ulongs[i]; + + if (count1 != count2) { + if (min_count < count1) { + if (set2->infinite) { + res->ulongs_count = min_count; + } else { + for(i=min_count; iulongs[i] = set1->ulongs[i]; + } + } else { + if (set1->infinite) { + res->ulongs_count = min_count; + } else { + for(i=min_count; iulongs[i] = set2->ulongs[i]; + } + } + } + + res->infinite = set1->infinite || set2->infinite; + return 0; +} + +int hwloc_bitmap_and (struct hwloc_bitmap_s *res, const struct hwloc_bitmap_s *set1, const struct hwloc_bitmap_s *set2) +{ + /* cache counts so that we can reset res even if it's also set1 or set2 */ + unsigned count1 = set1->ulongs_count; + unsigned count2 = set2->ulongs_count; + unsigned max_count = count1 > count2 ? count1 : count2; + unsigned min_count = count1 + count2 - max_count; + unsigned i; + + HWLOC__BITMAP_CHECK(res); + HWLOC__BITMAP_CHECK(set1); + HWLOC__BITMAP_CHECK(set2); + + if (hwloc_bitmap_reset_by_ulongs(res, max_count) < 0) + return -1; + + for(i=0; iulongs[i] = set1->ulongs[i] & set2->ulongs[i]; + + if (count1 != count2) { + if (min_count < count1) { + if (set2->infinite) { + for(i=min_count; iulongs[i] = set1->ulongs[i]; + } else { + res->ulongs_count = min_count; + } + } else { + if (set1->infinite) { + for(i=min_count; iulongs[i] = set2->ulongs[i]; + } else { + res->ulongs_count = min_count; + } + } + } + + res->infinite = set1->infinite && set2->infinite; + return 0; +} + +int hwloc_bitmap_andnot (struct hwloc_bitmap_s *res, const struct hwloc_bitmap_s *set1, const struct hwloc_bitmap_s *set2) +{ + /* cache counts so that we can reset res even if it's also set1 or set2 */ + unsigned count1 = set1->ulongs_count; + unsigned count2 = set2->ulongs_count; + unsigned max_count = count1 > count2 ? count1 : count2; + unsigned min_count = count1 + count2 - max_count; + unsigned i; + + HWLOC__BITMAP_CHECK(res); + HWLOC__BITMAP_CHECK(set1); + HWLOC__BITMAP_CHECK(set2); + + if (hwloc_bitmap_reset_by_ulongs(res, max_count) < 0) + return -1; + + for(i=0; iulongs[i] = set1->ulongs[i] & ~set2->ulongs[i]; + + if (count1 != count2) { + if (min_count < count1) { + if (!set2->infinite) { + for(i=min_count; iulongs[i] = set1->ulongs[i]; + } else { + res->ulongs_count = min_count; + } + } else { + if (set1->infinite) { + for(i=min_count; iulongs[i] = ~set2->ulongs[i]; + } else { + res->ulongs_count = min_count; + } + } + } + + res->infinite = set1->infinite && !set2->infinite; + return 0; +} + +int hwloc_bitmap_xor (struct hwloc_bitmap_s *res, const struct hwloc_bitmap_s *set1, const struct hwloc_bitmap_s *set2) +{ + /* cache counts so that we can reset res even if it's also set1 or set2 */ + unsigned count1 = set1->ulongs_count; + unsigned count2 = set2->ulongs_count; + unsigned max_count = count1 > count2 ? count1 : count2; + unsigned min_count = count1 + count2 - max_count; + unsigned i; + + HWLOC__BITMAP_CHECK(res); + HWLOC__BITMAP_CHECK(set1); + HWLOC__BITMAP_CHECK(set2); + + if (hwloc_bitmap_reset_by_ulongs(res, max_count) < 0) + return -1; + + for(i=0; iulongs[i] = set1->ulongs[i] ^ set2->ulongs[i]; + + if (count1 != count2) { + if (min_count < count1) { + unsigned long w2 = set2->infinite ? HWLOC_SUBBITMAP_FULL : HWLOC_SUBBITMAP_ZERO; + for(i=min_count; iulongs[i] = set1->ulongs[i] ^ w2; + } else { + unsigned long w1 = set1->infinite ? HWLOC_SUBBITMAP_FULL : HWLOC_SUBBITMAP_ZERO; + for(i=min_count; iulongs[i] = set2->ulongs[i] ^ w1; + } + } + + res->infinite = (!set1->infinite) != (!set2->infinite); + return 0; +} + +int hwloc_bitmap_not (struct hwloc_bitmap_s *res, const struct hwloc_bitmap_s *set) +{ + unsigned count = set->ulongs_count; + unsigned i; + + HWLOC__BITMAP_CHECK(res); + HWLOC__BITMAP_CHECK(set); + + if (hwloc_bitmap_reset_by_ulongs(res, count) < 0) + return -1; + + for(i=0; iulongs[i] = ~set->ulongs[i]; + + res->infinite = !set->infinite; + return 0; +} + +int hwloc_bitmap_first(const struct hwloc_bitmap_s * set) +{ + unsigned i; + + HWLOC__BITMAP_CHECK(set); + + for(i=0; iulongs_count; i++) { + /* subsets are unsigned longs, use ffsl */ + unsigned long w = set->ulongs[i]; + if (w) + return hwloc_ffsl(w) - 1 + HWLOC_BITS_PER_LONG*i; + } + + if (set->infinite) + return set->ulongs_count * HWLOC_BITS_PER_LONG; + + return -1; +} + +int hwloc_bitmap_first_unset(const struct hwloc_bitmap_s * set) +{ + unsigned i; + + HWLOC__BITMAP_CHECK(set); + + for(i=0; iulongs_count; i++) { + /* subsets are unsigned longs, use ffsl */ + unsigned long w = ~set->ulongs[i]; + if (w) + return hwloc_ffsl(w) - 1 + HWLOC_BITS_PER_LONG*i; + } + + if (!set->infinite) + return set->ulongs_count * HWLOC_BITS_PER_LONG; + + return -1; +} + +int hwloc_bitmap_last(const struct hwloc_bitmap_s * set) +{ + int i; + + HWLOC__BITMAP_CHECK(set); + + if (set->infinite) + return -1; + + for(i=(int)set->ulongs_count-1; i>=0; i--) { + /* subsets are unsigned longs, use flsl */ + unsigned long w = set->ulongs[i]; + if (w) + return hwloc_flsl(w) - 1 + HWLOC_BITS_PER_LONG*i; + } + + return -1; +} + +int hwloc_bitmap_last_unset(const struct hwloc_bitmap_s * set) +{ + int i; + + HWLOC__BITMAP_CHECK(set); + + if (!set->infinite) + return -1; + + for(i=(int)set->ulongs_count-1; i>=0; i--) { + /* subsets are unsigned longs, use flsl */ + unsigned long w = ~set->ulongs[i]; + if (w) + return hwloc_flsl(w) - 1 + HWLOC_BITS_PER_LONG*i; + } + + return -1; +} + +int hwloc_bitmap_next(const struct hwloc_bitmap_s * set, int prev_cpu) +{ + unsigned i = HWLOC_SUBBITMAP_INDEX(prev_cpu + 1); + + HWLOC__BITMAP_CHECK(set); + + if (i >= set->ulongs_count) { + if (set->infinite) + return prev_cpu + 1; + else + return -1; + } + + for(; iulongs_count; i++) { + /* subsets are unsigned longs, use ffsl */ + unsigned long w = set->ulongs[i]; + + /* if the prev cpu is in the same word as the possible next one, + we need to mask out previous cpus */ + if (prev_cpu >= 0 && HWLOC_SUBBITMAP_INDEX((unsigned) prev_cpu) == i) + w &= ~HWLOC_SUBBITMAP_ULBIT_TO(HWLOC_SUBBITMAP_CPU_ULBIT(prev_cpu)); + + if (w) + return hwloc_ffsl(w) - 1 + HWLOC_BITS_PER_LONG*i; + } + + if (set->infinite) + return set->ulongs_count * HWLOC_BITS_PER_LONG; + + return -1; +} + +int hwloc_bitmap_next_unset(const struct hwloc_bitmap_s * set, int prev_cpu) +{ + unsigned i = HWLOC_SUBBITMAP_INDEX(prev_cpu + 1); + + HWLOC__BITMAP_CHECK(set); + + if (i >= set->ulongs_count) { + if (!set->infinite) + return prev_cpu + 1; + else + return -1; + } + + for(; iulongs_count; i++) { + /* subsets are unsigned longs, use ffsl */ + unsigned long w = ~set->ulongs[i]; + + /* if the prev cpu is in the same word as the possible next one, + we need to mask out previous cpus */ + if (prev_cpu >= 0 && HWLOC_SUBBITMAP_INDEX((unsigned) prev_cpu) == i) + w &= ~HWLOC_SUBBITMAP_ULBIT_TO(HWLOC_SUBBITMAP_CPU_ULBIT(prev_cpu)); + + if (w) + return hwloc_ffsl(w) - 1 + HWLOC_BITS_PER_LONG*i; + } + + if (!set->infinite) + return set->ulongs_count * HWLOC_BITS_PER_LONG; + + return -1; +} + +int hwloc_bitmap_singlify(struct hwloc_bitmap_s * set) +{ + unsigned i; + int found = 0; + + HWLOC__BITMAP_CHECK(set); + + for(i=0; iulongs_count; i++) { + if (found) { + set->ulongs[i] = HWLOC_SUBBITMAP_ZERO; + continue; + } else { + /* subsets are unsigned longs, use ffsl */ + unsigned long w = set->ulongs[i]; + if (w) { + int _ffs = hwloc_ffsl(w); + set->ulongs[i] = HWLOC_SUBBITMAP_CPU(_ffs-1); + found = 1; + } + } + } + + if (set->infinite) { + if (found) { + set->infinite = 0; + } else { + /* set the first non allocated bit */ + unsigned first = set->ulongs_count * HWLOC_BITS_PER_LONG; + set->infinite = 0; /* do not let realloc fill the newly allocated sets */ + return hwloc_bitmap_set(set, first); + } + } + + return 0; +} + +int hwloc_bitmap_compare_first(const struct hwloc_bitmap_s * set1, const struct hwloc_bitmap_s * set2) +{ + unsigned count1 = set1->ulongs_count; + unsigned count2 = set2->ulongs_count; + unsigned max_count = count1 > count2 ? count1 : count2; + unsigned min_count = count1 + count2 - max_count; + unsigned i; + + HWLOC__BITMAP_CHECK(set1); + HWLOC__BITMAP_CHECK(set2); + + for(i=0; iulongs[i]; + unsigned long w2 = set2->ulongs[i]; + if (w1 || w2) { + int _ffs1 = hwloc_ffsl(w1); + int _ffs2 = hwloc_ffsl(w2); + /* if both have a bit set, compare for real */ + if (_ffs1 && _ffs2) + return _ffs1-_ffs2; + /* one is empty, and it is considered higher, so reverse-compare them */ + return _ffs2-_ffs1; + } + } + + if (count1 != count2) { + if (min_count < count2) { + for(i=min_count; iulongs[i]; + if (set1->infinite) + return -!(w2 & 1); + else if (w2) + return 1; + } + } else { + for(i=min_count; iulongs[i]; + if (set2->infinite) + return !(w1 & 1); + else if (w1) + return -1; + } + } + } + + return !!set1->infinite - !!set2->infinite; +} + +int hwloc_bitmap_compare(const struct hwloc_bitmap_s * set1, const struct hwloc_bitmap_s * set2) +{ + unsigned count1 = set1->ulongs_count; + unsigned count2 = set2->ulongs_count; + unsigned max_count = count1 > count2 ? count1 : count2; + unsigned min_count = count1 + count2 - max_count; + int i; + + HWLOC__BITMAP_CHECK(set1); + HWLOC__BITMAP_CHECK(set2); + + if ((!set1->infinite) != (!set2->infinite)) + return !!set1->infinite - !!set2->infinite; + + if (count1 != count2) { + if (min_count < count2) { + unsigned long val1 = set1->infinite ? HWLOC_SUBBITMAP_FULL : HWLOC_SUBBITMAP_ZERO; + for(i=(int)max_count-1; i>=(int) min_count; i--) { + unsigned long val2 = set2->ulongs[i]; + if (val1 == val2) + continue; + return val1 < val2 ? -1 : 1; + } + } else { + unsigned long val2 = set2->infinite ? HWLOC_SUBBITMAP_FULL : HWLOC_SUBBITMAP_ZERO; + for(i=(int)max_count-1; i>=(int) min_count; i--) { + unsigned long val1 = set1->ulongs[i]; + if (val1 == val2) + continue; + return val1 < val2 ? -1 : 1; + } + } + } + + for(i=(int)min_count-1; i>=0; i--) { + unsigned long val1 = set1->ulongs[i]; + unsigned long val2 = set2->ulongs[i]; + if (val1 == val2) + continue; + return val1 < val2 ? -1 : 1; + } + + return 0; +} + +int hwloc_bitmap_weight(const struct hwloc_bitmap_s * set) +{ + int weight = 0; + unsigned i; + + HWLOC__BITMAP_CHECK(set); + + if (set->infinite) + return -1; + + for(i=0; iulongs_count; i++) + weight += hwloc_weight_long(set->ulongs[i]); + return weight; +} + +int hwloc_bitmap_compare_inclusion(const struct hwloc_bitmap_s * set1, const struct hwloc_bitmap_s * set2) +{ + unsigned max_count = set1->ulongs_count > set2->ulongs_count ? set1->ulongs_count : set2->ulongs_count; + int result = HWLOC_BITMAP_EQUAL; /* means empty sets return equal */ + int empty1 = 1; + int empty2 = 1; + unsigned i; + + HWLOC__BITMAP_CHECK(set1); + HWLOC__BITMAP_CHECK(set2); + + for(i=0; iinfinite) { + if (set2->infinite) { + /* set2 infinite only */ + if (result == HWLOC_BITMAP_CONTAINS) { + if (!empty2) + return HWLOC_BITMAP_INTERSECTS; + result = HWLOC_BITMAP_DIFFERENT; + } else if (result == HWLOC_BITMAP_EQUAL) { + result = HWLOC_BITMAP_INCLUDED; + } + /* no change otherwise */ + } + } else if (!set2->infinite) { + /* set1 infinite only */ + if (result == HWLOC_BITMAP_INCLUDED) { + if (!empty1) + return HWLOC_BITMAP_INTERSECTS; + result = HWLOC_BITMAP_DIFFERENT; + } else if (result == HWLOC_BITMAP_EQUAL) { + result = HWLOC_BITMAP_CONTAINS; + } + /* no change otherwise */ + } else { + /* both infinite */ + if (result == HWLOC_BITMAP_DIFFERENT) + return HWLOC_BITMAP_INTERSECTS; + /* equal/contains/included unchanged */ + } + + return result; +} diff --git a/src/3rdparty/hwloc/src/components.c b/src/3rdparty/hwloc/src/components.c new file mode 100644 index 00000000..bd7c00e3 --- /dev/null +++ b/src/3rdparty/hwloc/src/components.c @@ -0,0 +1,785 @@ +/* + * Copyright © 2009-2017 Inria. All rights reserved. + * Copyright © 2012 Université Bordeaux + * See COPYING in top-level directory. + */ + +#include +#include +#include +#include +#include + +#define HWLOC_COMPONENT_STOP_NAME "stop" +#define HWLOC_COMPONENT_EXCLUDE_CHAR '-' +#define HWLOC_COMPONENT_SEPS "," + +/* list of all registered discovery components, sorted by priority, higher priority first. + * noos is last because its priority is 0. + * others' priority is 10. + */ +static struct hwloc_disc_component * hwloc_disc_components = NULL; + +static unsigned hwloc_components_users = 0; /* first one initializes, last ones destroys */ + +static int hwloc_components_verbose = 0; +#ifdef HWLOC_HAVE_PLUGINS +static int hwloc_plugins_verbose = 0; +static const char * hwloc_plugins_blacklist = NULL; +#endif + +/* hwloc_components_mutex serializes: + * - loading/unloading plugins, and modifications of the hwloc_plugins list + * - calls to ltdl, including in hwloc_check_plugin_namespace() + * - registration of components with hwloc_disc_component_register() + * and hwloc_xml_callbacks_register() + */ +#ifdef HWLOC_WIN_SYS +/* Basic mutex on top of InterlockedCompareExchange() on windows, + * Far from perfect, but easy to maintain, and way enough given that this code will never be needed for real. */ +#include +static LONG hwloc_components_mutex = 0; +#define HWLOC_COMPONENTS_LOCK() do { \ + while (InterlockedCompareExchange(&hwloc_components_mutex, 1, 0) != 0) \ + SwitchToThread(); \ +} while (0) +#define HWLOC_COMPONENTS_UNLOCK() do { \ + assert(hwloc_components_mutex == 1); \ + hwloc_components_mutex = 0; \ +} while (0) + +#elif defined HWLOC_HAVE_PTHREAD_MUTEX +/* pthread mutex if available (except on windows) */ +#include +static pthread_mutex_t hwloc_components_mutex = PTHREAD_MUTEX_INITIALIZER; +#define HWLOC_COMPONENTS_LOCK() pthread_mutex_lock(&hwloc_components_mutex) +#define HWLOC_COMPONENTS_UNLOCK() pthread_mutex_unlock(&hwloc_components_mutex) + +#else /* HWLOC_WIN_SYS || HWLOC_HAVE_PTHREAD_MUTEX */ +#error No mutex implementation available +#endif + + +#ifdef HWLOC_HAVE_PLUGINS + +#include + +/* array of pointers to dynamically loaded plugins */ +static struct hwloc__plugin_desc { + char *name; + struct hwloc_component *component; + char *filename; + lt_dlhandle handle; + struct hwloc__plugin_desc *next; +} *hwloc_plugins = NULL; + +static int +hwloc__dlforeach_cb(const char *filename, void *_data __hwloc_attribute_unused) +{ + const char *basename; + lt_dlhandle handle; + struct hwloc_component *component; + struct hwloc__plugin_desc *desc, **prevdesc; + + if (hwloc_plugins_verbose) + fprintf(stderr, "Plugin dlforeach found `%s'\n", filename); + + basename = strrchr(filename, '/'); + if (!basename) + basename = filename; + else + basename++; + + if (hwloc_plugins_blacklist && strstr(hwloc_plugins_blacklist, basename)) { + if (hwloc_plugins_verbose) + fprintf(stderr, "Plugin `%s' is blacklisted in the environment\n", basename); + goto out; + } + + /* dlopen and get the component structure */ + handle = lt_dlopenext(filename); + if (!handle) { + if (hwloc_plugins_verbose) + fprintf(stderr, "Failed to load plugin: %s\n", lt_dlerror()); + goto out; + } + +{ + char componentsymbolname[strlen(basename)+10+1]; + sprintf(componentsymbolname, "%s_component", basename); + component = lt_dlsym(handle, componentsymbolname); + if (!component) { + if (hwloc_plugins_verbose) + fprintf(stderr, "Failed to find component symbol `%s'\n", + componentsymbolname); + goto out_with_handle; + } + if (component->abi != HWLOC_COMPONENT_ABI) { + if (hwloc_plugins_verbose) + fprintf(stderr, "Plugin symbol ABI %u instead of %d\n", + component->abi, HWLOC_COMPONENT_ABI); + goto out_with_handle; + } + if (hwloc_plugins_verbose) + fprintf(stderr, "Plugin contains expected symbol `%s'\n", + componentsymbolname); +} + + if (HWLOC_COMPONENT_TYPE_DISC == component->type) { + if (strncmp(basename, "hwloc_", 6)) { + if (hwloc_plugins_verbose) + fprintf(stderr, "Plugin name `%s' doesn't match its type DISCOVERY\n", basename); + goto out_with_handle; + } + } else if (HWLOC_COMPONENT_TYPE_XML == component->type) { + if (strncmp(basename, "hwloc_xml_", 10)) { + if (hwloc_plugins_verbose) + fprintf(stderr, "Plugin name `%s' doesn't match its type XML\n", basename); + goto out_with_handle; + } + } else { + if (hwloc_plugins_verbose) + fprintf(stderr, "Plugin name `%s' has invalid type %u\n", + basename, (unsigned) component->type); + goto out_with_handle; + } + + /* allocate a plugin_desc and queue it */ + desc = malloc(sizeof(*desc)); + if (!desc) + goto out_with_handle; + desc->name = strdup(basename); + desc->filename = strdup(filename); + desc->component = component; + desc->handle = handle; + desc->next = NULL; + if (hwloc_plugins_verbose) + fprintf(stderr, "Plugin descriptor `%s' ready\n", basename); + + /* append to the list */ + prevdesc = &hwloc_plugins; + while (*prevdesc) + prevdesc = &((*prevdesc)->next); + *prevdesc = desc; + if (hwloc_plugins_verbose) + fprintf(stderr, "Plugin descriptor `%s' queued\n", basename); + return 0; + + out_with_handle: + lt_dlclose(handle); + out: + return 0; +} + +static void +hwloc_plugins_exit(void) +{ + struct hwloc__plugin_desc *desc, *next; + + if (hwloc_plugins_verbose) + fprintf(stderr, "Closing all plugins\n"); + + desc = hwloc_plugins; + while (desc) { + next = desc->next; + lt_dlclose(desc->handle); + free(desc->name); + free(desc->filename); + free(desc); + desc = next; + } + hwloc_plugins = NULL; + + lt_dlexit(); +} + +static int +hwloc_plugins_init(void) +{ + const char *verboseenv; + const char *path = HWLOC_PLUGINS_PATH; + const char *env; + int err; + + verboseenv = getenv("HWLOC_PLUGINS_VERBOSE"); + hwloc_plugins_verbose = verboseenv ? atoi(verboseenv) : 0; + + hwloc_plugins_blacklist = getenv("HWLOC_PLUGINS_BLACKLIST"); + + err = lt_dlinit(); + if (err) + goto out; + + env = getenv("HWLOC_PLUGINS_PATH"); + if (env) + path = env; + + hwloc_plugins = NULL; + + if (hwloc_plugins_verbose) + fprintf(stderr, "Starting plugin dlforeach in %s\n", path); + err = lt_dlforeachfile(path, hwloc__dlforeach_cb, NULL); + if (err) + goto out_with_init; + + return 0; + + out_with_init: + hwloc_plugins_exit(); + out: + return -1; +} + +#endif /* HWLOC_HAVE_PLUGINS */ + +static const char * +hwloc_disc_component_type_string(hwloc_disc_component_type_t type) +{ + switch (type) { + case HWLOC_DISC_COMPONENT_TYPE_CPU: return "cpu"; + case HWLOC_DISC_COMPONENT_TYPE_GLOBAL: return "global"; + case HWLOC_DISC_COMPONENT_TYPE_MISC: return "misc"; + default: return "**unknown**"; + } +} + +static int +hwloc_disc_component_register(struct hwloc_disc_component *component, + const char *filename) +{ + struct hwloc_disc_component **prev; + + /* check that the component name is valid */ + if (!strcmp(component->name, HWLOC_COMPONENT_STOP_NAME)) { + if (hwloc_components_verbose) + fprintf(stderr, "Cannot register discovery component with reserved name `" HWLOC_COMPONENT_STOP_NAME "'\n"); + return -1; + } + if (strchr(component->name, HWLOC_COMPONENT_EXCLUDE_CHAR) + || strcspn(component->name, HWLOC_COMPONENT_SEPS) != strlen(component->name)) { + if (hwloc_components_verbose) + fprintf(stderr, "Cannot register discovery component with name `%s' containing reserved characters `%c" HWLOC_COMPONENT_SEPS "'\n", + component->name, HWLOC_COMPONENT_EXCLUDE_CHAR); + return -1; + } + /* check that the component type is valid */ + switch ((unsigned) component->type) { + case HWLOC_DISC_COMPONENT_TYPE_CPU: + case HWLOC_DISC_COMPONENT_TYPE_GLOBAL: + case HWLOC_DISC_COMPONENT_TYPE_MISC: + break; + default: + fprintf(stderr, "Cannot register discovery component `%s' with unknown type %u\n", + component->name, (unsigned) component->type); + return -1; + } + + prev = &hwloc_disc_components; + while (NULL != *prev) { + if (!strcmp((*prev)->name, component->name)) { + /* if two components have the same name, only keep the highest priority one */ + if ((*prev)->priority < component->priority) { + /* drop the existing component */ + if (hwloc_components_verbose) + fprintf(stderr, "Dropping previously registered discovery component `%s', priority %u lower than new one %u\n", + (*prev)->name, (*prev)->priority, component->priority); + *prev = (*prev)->next; + } else { + /* drop the new one */ + if (hwloc_components_verbose) + fprintf(stderr, "Ignoring new discovery component `%s', priority %u lower than previously registered one %u\n", + component->name, component->priority, (*prev)->priority); + return -1; + } + } + prev = &((*prev)->next); + } + if (hwloc_components_verbose) + fprintf(stderr, "Registered %s discovery component `%s' with priority %u (%s%s)\n", + hwloc_disc_component_type_string(component->type), component->name, component->priority, + filename ? "from plugin " : "statically build", filename ? filename : ""); + + prev = &hwloc_disc_components; + while (NULL != *prev) { + if ((*prev)->priority < component->priority) + break; + prev = &((*prev)->next); + } + component->next = *prev; + *prev = component; + return 0; +} + +#include + +static void (**hwloc_component_finalize_cbs)(unsigned long); +static unsigned hwloc_component_finalize_cb_count; + +void +hwloc_components_init(void) +{ +#ifdef HWLOC_HAVE_PLUGINS + struct hwloc__plugin_desc *desc; +#endif + const char *verboseenv; + unsigned i; + + HWLOC_COMPONENTS_LOCK(); + assert((unsigned) -1 != hwloc_components_users); + if (0 != hwloc_components_users++) { + HWLOC_COMPONENTS_UNLOCK(); + return; + } + + verboseenv = getenv("HWLOC_COMPONENTS_VERBOSE"); + hwloc_components_verbose = verboseenv ? atoi(verboseenv) : 0; + +#ifdef HWLOC_HAVE_PLUGINS + hwloc_plugins_init(); +#endif + + hwloc_component_finalize_cbs = NULL; + hwloc_component_finalize_cb_count = 0; + /* count the max number of finalize callbacks */ + for(i=0; NULL != hwloc_static_components[i]; i++) + hwloc_component_finalize_cb_count++; +#ifdef HWLOC_HAVE_PLUGINS + for(desc = hwloc_plugins; NULL != desc; desc = desc->next) + hwloc_component_finalize_cb_count++; +#endif + if (hwloc_component_finalize_cb_count) { + hwloc_component_finalize_cbs = calloc(hwloc_component_finalize_cb_count, + sizeof(*hwloc_component_finalize_cbs)); + assert(hwloc_component_finalize_cbs); + /* forget that max number and recompute the real one below */ + hwloc_component_finalize_cb_count = 0; + } + + /* hwloc_static_components is created by configure in static-components.h */ + for(i=0; NULL != hwloc_static_components[i]; i++) { + if (hwloc_static_components[i]->flags) { + fprintf(stderr, "Ignoring static component with invalid flags %lx\n", + hwloc_static_components[i]->flags); + continue; + } + + /* initialize the component */ + if (hwloc_static_components[i]->init && hwloc_static_components[i]->init(0) < 0) { + if (hwloc_components_verbose) + fprintf(stderr, "Ignoring static component, failed to initialize\n"); + continue; + } + /* queue ->finalize() callback if any */ + if (hwloc_static_components[i]->finalize) + hwloc_component_finalize_cbs[hwloc_component_finalize_cb_count++] = hwloc_static_components[i]->finalize; + + /* register for real now */ + if (HWLOC_COMPONENT_TYPE_DISC == hwloc_static_components[i]->type) + hwloc_disc_component_register(hwloc_static_components[i]->data, NULL); + else if (HWLOC_COMPONENT_TYPE_XML == hwloc_static_components[i]->type) + hwloc_xml_callbacks_register(hwloc_static_components[i]->data); + else + assert(0); + } + + /* dynamic plugins */ +#ifdef HWLOC_HAVE_PLUGINS + for(desc = hwloc_plugins; NULL != desc; desc = desc->next) { + if (desc->component->flags) { + fprintf(stderr, "Ignoring plugin `%s' component with invalid flags %lx\n", + desc->name, desc->component->flags); + continue; + } + + /* initialize the component */ + if (desc->component->init && desc->component->init(0) < 0) { + if (hwloc_components_verbose) + fprintf(stderr, "Ignoring plugin `%s', failed to initialize\n", desc->name); + continue; + } + /* queue ->finalize() callback if any */ + if (desc->component->finalize) + hwloc_component_finalize_cbs[hwloc_component_finalize_cb_count++] = desc->component->finalize; + + /* register for real now */ + if (HWLOC_COMPONENT_TYPE_DISC == desc->component->type) + hwloc_disc_component_register(desc->component->data, desc->filename); + else if (HWLOC_COMPONENT_TYPE_XML == desc->component->type) + hwloc_xml_callbacks_register(desc->component->data); + else + assert(0); + } +#endif + + HWLOC_COMPONENTS_UNLOCK(); +} + +void +hwloc_backends_init(struct hwloc_topology *topology) +{ + topology->backends = NULL; + topology->backend_excludes = 0; +} + +static struct hwloc_disc_component * +hwloc_disc_component_find(int type /* hwloc_disc_component_type_t or -1 if any */, + const char *name /* name of NULL if any */) +{ + struct hwloc_disc_component *comp = hwloc_disc_components; + while (NULL != comp) { + if ((-1 == type || type == (int) comp->type) + && (NULL == name || !strcmp(name, comp->name))) + return comp; + comp = comp->next; + } + return NULL; +} + +/* used by set_xml(), set_synthetic(), ... environment variables, ... to force the first backend */ +int +hwloc_disc_component_force_enable(struct hwloc_topology *topology, + int envvar_forced, + int type, const char *name, + const void *data1, const void *data2, const void *data3) +{ + struct hwloc_disc_component *comp; + struct hwloc_backend *backend; + + if (topology->is_loaded) { + errno = EBUSY; + return -1; + } + + comp = hwloc_disc_component_find(type, name); + if (!comp) { + errno = ENOSYS; + return -1; + } + + backend = comp->instantiate(comp, data1, data2, data3); + if (backend) { + backend->envvar_forced = envvar_forced; + if (topology->backends) + hwloc_backends_disable_all(topology); + return hwloc_backend_enable(topology, backend); + } else + return -1; +} + +static int +hwloc_disc_component_try_enable(struct hwloc_topology *topology, + struct hwloc_disc_component *comp, + const char *comparg, + int envvar_forced) +{ + struct hwloc_backend *backend; + + if (topology->backend_excludes & comp->type) { + if (hwloc_components_verbose) + /* do not warn if envvar_forced since system-wide HWLOC_COMPONENTS must be silently ignored after set_xml() etc. + */ + fprintf(stderr, "Excluding %s discovery component `%s', conflicts with excludes 0x%x\n", + hwloc_disc_component_type_string(comp->type), comp->name, topology->backend_excludes); + return -1; + } + + backend = comp->instantiate(comp, comparg, NULL, NULL); + if (!backend) { + if (hwloc_components_verbose || envvar_forced) + fprintf(stderr, "Failed to instantiate discovery component `%s'\n", comp->name); + return -1; + } + + backend->envvar_forced = envvar_forced; + return hwloc_backend_enable(topology, backend); +} + +void +hwloc_disc_components_enable_others(struct hwloc_topology *topology) +{ + struct hwloc_disc_component *comp; + struct hwloc_backend *backend; + int tryall = 1; + const char *_env; + char *env; /* we'll to modify the env value, so duplicate it */ + + _env = getenv("HWLOC_COMPONENTS"); + env = _env ? strdup(_env) : NULL; + + /* enable explicitly listed components */ + if (env) { + char *curenv = env; + size_t s; + + while (*curenv) { + s = strcspn(curenv, HWLOC_COMPONENT_SEPS); + if (s) { + char c; + + /* replace linuxpci with linuxio for backward compatibility with pre-v2.0 */ + if (!strncmp(curenv, "linuxpci", 8) && s == 8) { + curenv[5] = 'i'; + curenv[6] = 'o'; + curenv[7] = *HWLOC_COMPONENT_SEPS; + } else if (curenv[0] == HWLOC_COMPONENT_EXCLUDE_CHAR && !strncmp(curenv+1, "linuxpci", 8) && s == 9) { + curenv[6] = 'i'; + curenv[7] = 'o'; + curenv[8] = *HWLOC_COMPONENT_SEPS; + /* skip this name, it's a negated one */ + goto nextname; + } + + if (curenv[0] == HWLOC_COMPONENT_EXCLUDE_CHAR) + goto nextname; + + if (!strncmp(curenv, HWLOC_COMPONENT_STOP_NAME, s)) { + tryall = 0; + break; + } + + /* save the last char and replace with \0 */ + c = curenv[s]; + curenv[s] = '\0'; + + comp = hwloc_disc_component_find(-1, curenv); + if (comp) { + hwloc_disc_component_try_enable(topology, comp, NULL, 1 /* envvar forced */); + } else { + fprintf(stderr, "Cannot find discovery component `%s'\n", curenv); + } + + /* restore chars (the second loop below needs env to be unmodified) */ + curenv[s] = c; + } + +nextname: + curenv += s; + if (*curenv) + /* Skip comma */ + curenv++; + } + } + + /* env is still the same, the above loop didn't modify it */ + + /* now enable remaining components (except the explicitly '-'-listed ones) */ + if (tryall) { + comp = hwloc_disc_components; + while (NULL != comp) { + if (!comp->enabled_by_default) + goto nextcomp; + /* check if this component was explicitly excluded in env */ + if (env) { + char *curenv = env; + while (*curenv) { + size_t s = strcspn(curenv, HWLOC_COMPONENT_SEPS); + if (curenv[0] == HWLOC_COMPONENT_EXCLUDE_CHAR && !strncmp(curenv+1, comp->name, s-1) && strlen(comp->name) == s-1) { + if (hwloc_components_verbose) + fprintf(stderr, "Excluding %s discovery component `%s' because of HWLOC_COMPONENTS environment variable\n", + hwloc_disc_component_type_string(comp->type), comp->name); + goto nextcomp; + } + curenv += s; + if (*curenv) + /* Skip comma */ + curenv++; + } + } + hwloc_disc_component_try_enable(topology, comp, NULL, 0 /* defaults, not envvar forced */); +nextcomp: + comp = comp->next; + } + } + + if (hwloc_components_verbose) { + /* print a summary */ + int first = 1; + backend = topology->backends; + fprintf(stderr, "Final list of enabled discovery components: "); + while (backend != NULL) { + fprintf(stderr, "%s%s", first ? "" : ",", backend->component->name); + backend = backend->next; + first = 0; + } + fprintf(stderr, "\n"); + } + + free(env); +} + +void +hwloc_components_fini(void) +{ + unsigned i; + + HWLOC_COMPONENTS_LOCK(); + assert(0 != hwloc_components_users); + if (0 != --hwloc_components_users) { + HWLOC_COMPONENTS_UNLOCK(); + return; + } + + for(i=0; icomponent = component; + backend->flags = 0; + backend->discover = NULL; + backend->get_pci_busid_cpuset = NULL; + backend->disable = NULL; + backend->is_thissystem = -1; + backend->next = NULL; + backend->envvar_forced = 0; + return backend; +} + +static void +hwloc_backend_disable(struct hwloc_backend *backend) +{ + if (backend->disable) + backend->disable(backend); + free(backend); +} + +int +hwloc_backend_enable(struct hwloc_topology *topology, struct hwloc_backend *backend) +{ + struct hwloc_backend **pprev; + + /* check backend flags */ + if (backend->flags) { + fprintf(stderr, "Cannot enable %s discovery component `%s' with unknown flags %lx\n", + hwloc_disc_component_type_string(backend->component->type), backend->component->name, backend->flags); + return -1; + } + + /* make sure we didn't already enable this backend, we don't want duplicates */ + pprev = &topology->backends; + while (NULL != *pprev) { + if ((*pprev)->component == backend->component) { + if (hwloc_components_verbose) + fprintf(stderr, "Cannot enable %s discovery component `%s' twice\n", + hwloc_disc_component_type_string(backend->component->type), backend->component->name); + hwloc_backend_disable(backend); + errno = EBUSY; + return -1; + } + pprev = &((*pprev)->next); + } + + if (hwloc_components_verbose) + fprintf(stderr, "Enabling %s discovery component `%s'\n", + hwloc_disc_component_type_string(backend->component->type), backend->component->name); + + /* enqueue at the end */ + pprev = &topology->backends; + while (NULL != *pprev) + pprev = &((*pprev)->next); + backend->next = *pprev; + *pprev = backend; + + backend->topology = topology; + topology->backend_excludes |= backend->component->excludes; + return 0; +} + +void +hwloc_backends_is_thissystem(struct hwloc_topology *topology) +{ + struct hwloc_backend *backend; + const char *local_env; + + /* Apply is_thissystem topology flag before we enforce envvar backends. + * If the application changed the backend with set_foo(), + * it may use set_flags() update the is_thissystem flag here. + * If it changes the backend with environment variables below, + * it may use HWLOC_THISSYSTEM envvar below as well. + */ + + topology->is_thissystem = 1; + + /* apply thissystem from normally-given backends (envvar_forced=0, either set_foo() or defaults) */ + backend = topology->backends; + while (backend != NULL) { + if (backend->envvar_forced == 0 && backend->is_thissystem != -1) { + assert(backend->is_thissystem == 0); + topology->is_thissystem = 0; + } + backend = backend->next; + } + + /* override set_foo() with flags */ + if (topology->flags & HWLOC_TOPOLOGY_FLAG_IS_THISSYSTEM) + topology->is_thissystem = 1; + + /* now apply envvar-forced backend (envvar_forced=1) */ + backend = topology->backends; + while (backend != NULL) { + if (backend->envvar_forced == 1 && backend->is_thissystem != -1) { + assert(backend->is_thissystem == 0); + topology->is_thissystem = 0; + } + backend = backend->next; + } + + /* override with envvar-given flag */ + local_env = getenv("HWLOC_THISSYSTEM"); + if (local_env) + topology->is_thissystem = atoi(local_env); +} + +void +hwloc_backends_find_callbacks(struct hwloc_topology *topology) +{ + struct hwloc_backend *backend = topology->backends; + /* use the first backend's get_pci_busid_cpuset callback */ + topology->get_pci_busid_cpuset_backend = NULL; + while (backend != NULL) { + if (backend->get_pci_busid_cpuset) { + topology->get_pci_busid_cpuset_backend = backend; + return; + } + backend = backend->next; + } + return; +} + +void +hwloc_backends_disable_all(struct hwloc_topology *topology) +{ + struct hwloc_backend *backend; + + while (NULL != (backend = topology->backends)) { + struct hwloc_backend *next = backend->next; + if (hwloc_components_verbose) + fprintf(stderr, "Disabling %s discovery component `%s'\n", + hwloc_disc_component_type_string(backend->component->type), backend->component->name); + hwloc_backend_disable(backend); + topology->backends = next; + } + topology->backends = NULL; + topology->backend_excludes = 0; +} diff --git a/src/3rdparty/hwloc/src/diff.c b/src/3rdparty/hwloc/src/diff.c new file mode 100644 index 00000000..00811a7b --- /dev/null +++ b/src/3rdparty/hwloc/src/diff.c @@ -0,0 +1,492 @@ +/* + * Copyright © 2013-2018 Inria. All rights reserved. + * See COPYING in top-level directory. + */ + +#include +#include +#include + +int hwloc_topology_diff_destroy(hwloc_topology_diff_t diff) +{ + hwloc_topology_diff_t next; + while (diff) { + next = diff->generic.next; + switch (diff->generic.type) { + default: + break; + case HWLOC_TOPOLOGY_DIFF_OBJ_ATTR: + switch (diff->obj_attr.diff.generic.type) { + default: + break; + case HWLOC_TOPOLOGY_DIFF_OBJ_ATTR_NAME: + case HWLOC_TOPOLOGY_DIFF_OBJ_ATTR_INFO: + free(diff->obj_attr.diff.string.name); + free(diff->obj_attr.diff.string.oldvalue); + free(diff->obj_attr.diff.string.newvalue); + break; + } + break; + } + free(diff); + diff = next; + } + return 0; +} + +/************************ + * Computing diffs + */ + +static void hwloc_append_diff(hwloc_topology_diff_t newdiff, + hwloc_topology_diff_t *firstdiffp, + hwloc_topology_diff_t *lastdiffp) +{ + if (*firstdiffp) + (*lastdiffp)->generic.next = newdiff; + else + *firstdiffp = newdiff; + *lastdiffp = newdiff; + newdiff->generic.next = NULL; +} + +static int hwloc_append_diff_too_complex(hwloc_obj_t obj1, + hwloc_topology_diff_t *firstdiffp, + hwloc_topology_diff_t *lastdiffp) +{ + hwloc_topology_diff_t newdiff; + newdiff = malloc(sizeof(*newdiff)); + if (!newdiff) + return -1; + + newdiff->too_complex.type = HWLOC_TOPOLOGY_DIFF_TOO_COMPLEX; + newdiff->too_complex.obj_depth = obj1->depth; + newdiff->too_complex.obj_index = obj1->logical_index; + hwloc_append_diff(newdiff, firstdiffp, lastdiffp); + return 0; +} + +static int hwloc_append_diff_obj_attr_string(hwloc_obj_t obj, + hwloc_topology_diff_obj_attr_type_t type, + const char *name, + const char *oldvalue, + const char *newvalue, + hwloc_topology_diff_t *firstdiffp, + hwloc_topology_diff_t *lastdiffp) +{ + hwloc_topology_diff_t newdiff; + newdiff = malloc(sizeof(*newdiff)); + if (!newdiff) + return -1; + + newdiff->obj_attr.type = HWLOC_TOPOLOGY_DIFF_OBJ_ATTR; + newdiff->obj_attr.obj_depth = obj->depth; + newdiff->obj_attr.obj_index = obj->logical_index; + newdiff->obj_attr.diff.string.type = type; + newdiff->obj_attr.diff.string.name = name ? strdup(name) : NULL; + newdiff->obj_attr.diff.string.oldvalue = oldvalue ? strdup(oldvalue) : NULL; + newdiff->obj_attr.diff.string.newvalue = newvalue ? strdup(newvalue) : NULL; + hwloc_append_diff(newdiff, firstdiffp, lastdiffp); + return 0; +} + +static int hwloc_append_diff_obj_attr_uint64(hwloc_obj_t obj, + hwloc_topology_diff_obj_attr_type_t type, + hwloc_uint64_t idx, + hwloc_uint64_t oldvalue, + hwloc_uint64_t newvalue, + hwloc_topology_diff_t *firstdiffp, + hwloc_topology_diff_t *lastdiffp) +{ + hwloc_topology_diff_t newdiff; + newdiff = malloc(sizeof(*newdiff)); + if (!newdiff) + return -1; + + newdiff->obj_attr.type = HWLOC_TOPOLOGY_DIFF_OBJ_ATTR; + newdiff->obj_attr.obj_depth = obj->depth; + newdiff->obj_attr.obj_index = obj->logical_index; + newdiff->obj_attr.diff.uint64.type = type; + newdiff->obj_attr.diff.uint64.index = idx; + newdiff->obj_attr.diff.uint64.oldvalue = oldvalue; + newdiff->obj_attr.diff.uint64.newvalue = newvalue; + hwloc_append_diff(newdiff, firstdiffp, lastdiffp); + return 0; +} + +static int +hwloc_diff_trees(hwloc_topology_t topo1, hwloc_obj_t obj1, + hwloc_topology_t topo2, hwloc_obj_t obj2, + unsigned flags, + hwloc_topology_diff_t *firstdiffp, hwloc_topology_diff_t *lastdiffp) +{ + unsigned i; + int err; + hwloc_obj_t child1, child2; + + if (obj1->depth != obj2->depth) + goto out_too_complex; + + if (obj1->type != obj2->type) + goto out_too_complex; + if ((!obj1->subtype) != (!obj2->subtype) + || (obj1->subtype && strcmp(obj1->subtype, obj2->subtype))) + goto out_too_complex; + + if (obj1->os_index != obj2->os_index) + /* we could allow different os_index for non-PU non-NUMAnode objects + * but it's likely useless anyway */ + goto out_too_complex; + +#define _SETS_DIFFERENT(_set1, _set2) \ + ( ( !(_set1) != !(_set2) ) \ + || ( (_set1) && !hwloc_bitmap_isequal(_set1, _set2) ) ) +#define SETS_DIFFERENT(_set, _obj1, _obj2) _SETS_DIFFERENT((_obj1)->_set, (_obj2)->_set) + if (SETS_DIFFERENT(cpuset, obj1, obj2) + || SETS_DIFFERENT(complete_cpuset, obj1, obj2) + || SETS_DIFFERENT(nodeset, obj1, obj2) + || SETS_DIFFERENT(complete_nodeset, obj1, obj2)) + goto out_too_complex; + + /* no need to check logical_index, sibling_rank, symmetric_subtree, + * the parents did it */ + + /* gp_index don't have to be strictly identical */ + + if ((!obj1->name) != (!obj2->name) + || (obj1->name && strcmp(obj1->name, obj2->name))) { + err = hwloc_append_diff_obj_attr_string(obj1, + HWLOC_TOPOLOGY_DIFF_OBJ_ATTR_NAME, + NULL, + obj1->name, + obj2->name, + firstdiffp, lastdiffp); + if (err < 0) + return err; + } + + /* type-specific attrs */ + switch (obj1->type) { + default: + break; + case HWLOC_OBJ_NUMANODE: + if (obj1->attr->numanode.local_memory != obj2->attr->numanode.local_memory) { + err = hwloc_append_diff_obj_attr_uint64(obj1, + HWLOC_TOPOLOGY_DIFF_OBJ_ATTR_SIZE, + 0, + obj1->attr->numanode.local_memory, + obj2->attr->numanode.local_memory, + firstdiffp, lastdiffp); + if (err < 0) + return err; + } + /* ignore memory page_types */ + break; + case HWLOC_OBJ_L1CACHE: + case HWLOC_OBJ_L2CACHE: + case HWLOC_OBJ_L3CACHE: + case HWLOC_OBJ_L4CACHE: + case HWLOC_OBJ_L5CACHE: + case HWLOC_OBJ_L1ICACHE: + case HWLOC_OBJ_L2ICACHE: + case HWLOC_OBJ_L3ICACHE: + if (memcmp(obj1->attr, obj2->attr, sizeof(obj1->attr->cache))) + goto out_too_complex; + break; + case HWLOC_OBJ_GROUP: + if (memcmp(obj1->attr, obj2->attr, sizeof(obj1->attr->group))) + goto out_too_complex; + break; + case HWLOC_OBJ_PCI_DEVICE: + if (memcmp(obj1->attr, obj2->attr, sizeof(obj1->attr->pcidev))) + goto out_too_complex; + break; + case HWLOC_OBJ_BRIDGE: + if (memcmp(obj1->attr, obj2->attr, sizeof(obj1->attr->bridge))) + goto out_too_complex; + break; + case HWLOC_OBJ_OS_DEVICE: + if (memcmp(obj1->attr, obj2->attr, sizeof(obj1->attr->osdev))) + goto out_too_complex; + break; + } + + /* infos */ + if (obj1->infos_count != obj2->infos_count) + goto out_too_complex; + for(i=0; iinfos_count; i++) { + struct hwloc_info_s *info1 = &obj1->infos[i], *info2 = &obj2->infos[i]; + if (strcmp(info1->name, info2->name)) + goto out_too_complex; + if (strcmp(obj1->infos[i].value, obj2->infos[i].value)) { + err = hwloc_append_diff_obj_attr_string(obj1, + HWLOC_TOPOLOGY_DIFF_OBJ_ATTR_INFO, + info1->name, + info1->value, + info2->value, + firstdiffp, lastdiffp); + if (err < 0) + return err; + } + } + + /* ignore userdata */ + + /* children */ + for(child1 = obj1->first_child, child2 = obj2->first_child; + child1 != NULL && child2 != NULL; + child1 = child1->next_sibling, child2 = child2->next_sibling) { + err = hwloc_diff_trees(topo1, child1, + topo2, child2, + flags, + firstdiffp, lastdiffp); + if (err < 0) + return err; + } + if (child1 || child2) + goto out_too_complex; + + /* memory children */ + for(child1 = obj1->memory_first_child, child2 = obj2->memory_first_child; + child1 != NULL && child2 != NULL; + child1 = child1->next_sibling, child2 = child2->next_sibling) { + err = hwloc_diff_trees(topo1, child1, + topo2, child2, + flags, + firstdiffp, lastdiffp); + if (err < 0) + return err; + } + if (child1 || child2) + goto out_too_complex; + + /* I/O children */ + for(child1 = obj1->io_first_child, child2 = obj2->io_first_child; + child1 != NULL && child2 != NULL; + child1 = child1->next_sibling, child2 = child2->next_sibling) { + err = hwloc_diff_trees(topo1, child1, + topo2, child2, + flags, + firstdiffp, lastdiffp); + if (err < 0) + return err; + } + if (child1 || child2) + goto out_too_complex; + + /* misc children */ + for(child1 = obj1->misc_first_child, child2 = obj2->misc_first_child; + child1 != NULL && child2 != NULL; + child1 = child1->next_sibling, child2 = child2->next_sibling) { + err = hwloc_diff_trees(topo1, child1, + topo2, child2, + flags, + firstdiffp, lastdiffp); + if (err < 0) + return err; + } + if (child1 || child2) + goto out_too_complex; + + return 0; + +out_too_complex: + hwloc_append_diff_too_complex(obj1, firstdiffp, lastdiffp); + return 0; +} + +int hwloc_topology_diff_build(hwloc_topology_t topo1, + hwloc_topology_t topo2, + unsigned long flags, + hwloc_topology_diff_t *diffp) +{ + hwloc_topology_diff_t lastdiff, tmpdiff; + struct hwloc_internal_distances_s *dist1, *dist2; + unsigned i; + int err; + + if (!topo1->is_loaded || !topo2->is_loaded) { + errno = EINVAL; + return -1; + } + + if (flags != 0) { + errno = EINVAL; + return -1; + } + + *diffp = NULL; + err = hwloc_diff_trees(topo1, hwloc_get_root_obj(topo1), + topo2, hwloc_get_root_obj(topo2), + flags, + diffp, &lastdiff); + if (!err) { + tmpdiff = *diffp; + while (tmpdiff) { + if (tmpdiff->generic.type == HWLOC_TOPOLOGY_DIFF_TOO_COMPLEX) { + err = 1; + break; + } + tmpdiff = tmpdiff->generic.next; + } + } + + if (!err) { + if (SETS_DIFFERENT(allowed_cpuset, topo1, topo2) + || SETS_DIFFERENT(allowed_nodeset, topo1, topo2)) { + hwloc_append_diff_too_complex(hwloc_get_root_obj(topo1), diffp, &lastdiff); + err = 1; + } + } + + if (!err) { + /* distances */ + hwloc_internal_distances_refresh(topo1); + hwloc_internal_distances_refresh(topo2); + dist1 = topo1->first_dist; + dist2 = topo2->first_dist; + while (dist1 || dist2) { + if (!!dist1 != !!dist2) { + hwloc_append_diff_too_complex(hwloc_get_root_obj(topo1), diffp, &lastdiff); + err = 1; + break; + } + if (dist1->type != dist2->type + || dist1->nbobjs != dist2->nbobjs + || dist1->kind != dist2->kind + || memcmp(dist1->values, dist2->values, dist1->nbobjs * dist1->nbobjs * sizeof(*dist1->values))) { + hwloc_append_diff_too_complex(hwloc_get_root_obj(topo1), diffp, &lastdiff); + err = 1; + break; + } + for(i=0; inbobjs; i++) + /* gp_index isn't enforced above. so compare logical_index instead, which is enforced. requires distances refresh() above */ + if (dist1->objs[i]->logical_index != dist2->objs[i]->logical_index) { + hwloc_append_diff_too_complex(hwloc_get_root_obj(topo1), diffp, &lastdiff); + err = 1; + break; + } + dist1 = dist1->next; + dist2 = dist2->next; + } + } + + return err; +} + +/******************** + * Applying diffs + */ + +static int +hwloc_apply_diff_one(hwloc_topology_t topology, + hwloc_topology_diff_t diff, + unsigned long flags) +{ + int reverse = !!(flags & HWLOC_TOPOLOGY_DIFF_APPLY_REVERSE); + + switch (diff->generic.type) { + case HWLOC_TOPOLOGY_DIFF_OBJ_ATTR: { + struct hwloc_topology_diff_obj_attr_s *obj_attr = &diff->obj_attr; + hwloc_obj_t obj = hwloc_get_obj_by_depth(topology, obj_attr->obj_depth, obj_attr->obj_index); + if (!obj) + return -1; + + switch (obj_attr->diff.generic.type) { + case HWLOC_TOPOLOGY_DIFF_OBJ_ATTR_SIZE: { + hwloc_obj_t tmpobj; + hwloc_uint64_t oldvalue = reverse ? obj_attr->diff.uint64.newvalue : obj_attr->diff.uint64.oldvalue; + hwloc_uint64_t newvalue = reverse ? obj_attr->diff.uint64.oldvalue : obj_attr->diff.uint64.newvalue; + hwloc_uint64_t valuediff = newvalue - oldvalue; + if (obj->type != HWLOC_OBJ_NUMANODE) + return -1; + if (obj->attr->numanode.local_memory != oldvalue) + return -1; + obj->attr->numanode.local_memory = newvalue; + tmpobj = obj; + while (tmpobj) { + tmpobj->total_memory += valuediff; + tmpobj = tmpobj->parent; + } + break; + } + case HWLOC_TOPOLOGY_DIFF_OBJ_ATTR_NAME: { + const char *oldvalue = reverse ? obj_attr->diff.string.newvalue : obj_attr->diff.string.oldvalue; + const char *newvalue = reverse ? obj_attr->diff.string.oldvalue : obj_attr->diff.string.newvalue; + if (!obj->name || strcmp(obj->name, oldvalue)) + return -1; + free(obj->name); + obj->name = strdup(newvalue); + break; + } + case HWLOC_TOPOLOGY_DIFF_OBJ_ATTR_INFO: { + const char *name = obj_attr->diff.string.name; + const char *oldvalue = reverse ? obj_attr->diff.string.newvalue : obj_attr->diff.string.oldvalue; + const char *newvalue = reverse ? obj_attr->diff.string.oldvalue : obj_attr->diff.string.newvalue; + unsigned i; + int found = 0; + for(i=0; iinfos_count; i++) { + struct hwloc_info_s *info = &obj->infos[i]; + if (!strcmp(info->name, name) + && !strcmp(info->value, oldvalue)) { + free(info->value); + info->value = strdup(newvalue); + found = 1; + break; + } + } + if (!found) + return -1; + break; + } + default: + return -1; + } + + break; + } + default: + return -1; + } + + return 0; +} + +int hwloc_topology_diff_apply(hwloc_topology_t topology, + hwloc_topology_diff_t diff, + unsigned long flags) +{ + hwloc_topology_diff_t tmpdiff, tmpdiff2; + int err, nr; + + if (!topology->is_loaded) { + errno = EINVAL; + return -1; + } + + if (flags & ~HWLOC_TOPOLOGY_DIFF_APPLY_REVERSE) { + errno = EINVAL; + return -1; + } + + tmpdiff = diff; + nr = 0; + while (tmpdiff) { + nr++; + err = hwloc_apply_diff_one(topology, tmpdiff, flags); + if (err < 0) + goto cancel; + tmpdiff = tmpdiff->generic.next; + } + return 0; + +cancel: + tmpdiff2 = tmpdiff; + tmpdiff = diff; + while (tmpdiff != tmpdiff2) { + hwloc_apply_diff_one(topology, tmpdiff, flags ^ HWLOC_TOPOLOGY_DIFF_APPLY_REVERSE); + tmpdiff = tmpdiff->generic.next; + } + errno = EINVAL; + return -nr; /* return the index (starting at 1) of the first element that couldn't be applied */ +} diff --git a/src/3rdparty/hwloc/src/distances.c b/src/3rdparty/hwloc/src/distances.c new file mode 100644 index 00000000..f0b91f01 --- /dev/null +++ b/src/3rdparty/hwloc/src/distances.c @@ -0,0 +1,920 @@ +/* + * Copyright © 2010-2018 Inria. All rights reserved. + * Copyright © 2011-2012 Université Bordeaux + * Copyright © 2011 Cisco Systems, Inc. All rights reserved. + * See COPYING in top-level directory. + */ + +#include +#include +#include +#include +#include + +#include +#include + +/****************************************************** + * Global init, prepare, destroy, dup + */ + +/* called during topology init() */ +void hwloc_internal_distances_init(struct hwloc_topology *topology) +{ + topology->first_dist = topology->last_dist = NULL; + topology->next_dist_id = 0; +} + +/* called at the beginning of load() */ +void hwloc_internal_distances_prepare(struct hwloc_topology *topology) +{ + char *env; + hwloc_localeswitch_declare; + + topology->grouping = 1; + if (topology->type_filter[HWLOC_OBJ_GROUP] == HWLOC_TYPE_FILTER_KEEP_NONE) + topology->grouping = 0; + env = getenv("HWLOC_GROUPING"); + if (env && !atoi(env)) + topology->grouping = 0; + + if (topology->grouping) { + topology->grouping_next_subkind = 0; + + HWLOC_BUILD_ASSERT(sizeof(topology->grouping_accuracies)/sizeof(*topology->grouping_accuracies) == 5); + topology->grouping_accuracies[0] = 0.0f; + topology->grouping_accuracies[1] = 0.01f; + topology->grouping_accuracies[2] = 0.02f; + topology->grouping_accuracies[3] = 0.05f; + topology->grouping_accuracies[4] = 0.1f; + topology->grouping_nbaccuracies = 5; + + hwloc_localeswitch_init(); + env = getenv("HWLOC_GROUPING_ACCURACY"); + if (!env) { + /* only use 0.0 */ + topology->grouping_nbaccuracies = 1; + } else if (strcmp(env, "try")) { + /* use the given value */ + topology->grouping_nbaccuracies = 1; + topology->grouping_accuracies[0] = (float) atof(env); + } /* otherwise try all values */ + hwloc_localeswitch_fini(); + + topology->grouping_verbose = 0; + env = getenv("HWLOC_GROUPING_VERBOSE"); + if (env) + topology->grouping_verbose = atoi(env); + } +} + +static void hwloc_internal_distances_free(struct hwloc_internal_distances_s *dist) +{ + free(dist->indexes); + free(dist->objs); + free(dist->values); + free(dist); +} + +/* called during topology destroy */ +void hwloc_internal_distances_destroy(struct hwloc_topology * topology) +{ + struct hwloc_internal_distances_s *dist, *next = topology->first_dist; + while ((dist = next) != NULL) { + next = dist->next; + hwloc_internal_distances_free(dist); + } + topology->first_dist = topology->last_dist = NULL; +} + +static int hwloc_internal_distances_dup_one(struct hwloc_topology *new, struct hwloc_internal_distances_s *olddist) +{ + struct hwloc_tma *tma = new->tma; + struct hwloc_internal_distances_s *newdist; + unsigned nbobjs = olddist->nbobjs; + + newdist = hwloc_tma_malloc(tma, sizeof(*newdist)); + if (!newdist) + return -1; + + newdist->type = olddist->type; + newdist->nbobjs = nbobjs; + newdist->kind = olddist->kind; + newdist->id = olddist->id; + + newdist->indexes = hwloc_tma_malloc(tma, nbobjs * sizeof(*newdist->indexes)); + newdist->objs = hwloc_tma_calloc(tma, nbobjs * sizeof(*newdist->objs)); + newdist->objs_are_valid = 0; + newdist->values = hwloc_tma_malloc(tma, nbobjs*nbobjs * sizeof(*newdist->values)); + if (!newdist->indexes || !newdist->objs || !newdist->values) { + assert(!tma || !tma->dontfree); /* this tma cannot fail to allocate */ + hwloc_internal_distances_free(newdist); + return -1; + } + + memcpy(newdist->indexes, olddist->indexes, nbobjs * sizeof(*newdist->indexes)); + memcpy(newdist->values, olddist->values, nbobjs*nbobjs * sizeof(*newdist->values)); + + newdist->next = NULL; + newdist->prev = new->last_dist; + if (new->last_dist) + new->last_dist->next = newdist; + else + new->first_dist = newdist; + new->last_dist = newdist; + + return 0; +} + +/* This function may be called with topology->tma set, it cannot free() or realloc() */ +int hwloc_internal_distances_dup(struct hwloc_topology *new, struct hwloc_topology *old) +{ + struct hwloc_internal_distances_s *olddist; + int err; + new->next_dist_id = old->next_dist_id; + for(olddist = old->first_dist; olddist; olddist = olddist->next) { + err = hwloc_internal_distances_dup_one(new, olddist); + if (err < 0) + return err; + } + return 0; +} + +/****************************************************** + * Remove distances from the topology + */ + +int hwloc_distances_remove(hwloc_topology_t topology) +{ + if (!topology->is_loaded) { + errno = EINVAL; + return -1; + } + hwloc_internal_distances_destroy(topology); + return 0; +} + +int hwloc_distances_remove_by_depth(hwloc_topology_t topology, int depth) +{ + struct hwloc_internal_distances_s *dist, *next; + hwloc_obj_type_t type; + + if (!topology->is_loaded) { + errno = EINVAL; + return -1; + } + + /* switch back to types since we don't support groups for now */ + type = hwloc_get_depth_type(topology, depth); + if (type == (hwloc_obj_type_t)-1) { + errno = EINVAL; + return -1; + } + + next = topology->first_dist; + while ((dist = next) != NULL) { + next = dist->next; + if (dist->type == type) { + if (next) + next->prev = dist->prev; + else + topology->last_dist = dist->prev; + if (dist->prev) + dist->prev->next = dist->next; + else + topology->first_dist = dist->next; + hwloc_internal_distances_free(dist); + } + } + + return 0; +} + +/****************************************************** + * Add distances to the topology + */ + +static void +hwloc__groups_by_distances(struct hwloc_topology *topology, unsigned nbobjs, struct hwloc_obj **objs, uint64_t *values, unsigned long kind, unsigned nbaccuracies, float *accuracies, int needcheck); + +/* insert a distance matrix in the topology. + * the caller gives us the distances and objs pointers, we'll free them later. + */ +static int +hwloc_internal_distances__add(hwloc_topology_t topology, + hwloc_obj_type_t type, unsigned nbobjs, hwloc_obj_t *objs, uint64_t *indexes, uint64_t *values, + unsigned long kind) +{ + struct hwloc_internal_distances_s *dist = calloc(1, sizeof(*dist)); + if (!dist) + goto err; + + dist->type = type; + dist->nbobjs = nbobjs; + dist->kind = kind; + + if (!objs) { + assert(indexes); + /* we only have indexes, we'll refresh objs from there */ + dist->indexes = indexes; + dist->objs = calloc(nbobjs, sizeof(hwloc_obj_t)); + if (!dist->objs) + goto err_with_dist; + dist->objs_are_valid = 0; + + } else { + unsigned i; + assert(!indexes); + /* we only have objs, generate the indexes arrays so that we can refresh objs later */ + dist->objs = objs; + dist->objs_are_valid = 1; + dist->indexes = malloc(nbobjs * sizeof(*dist->indexes)); + if (!dist->indexes) + goto err_with_dist; + if (dist->type == HWLOC_OBJ_PU || dist->type == HWLOC_OBJ_NUMANODE) { + for(i=0; iindexes[i] = objs[i]->os_index; + } else { + for(i=0; iindexes[i] = objs[i]->gp_index; + } + } + + dist->values = values; + + dist->id = topology->next_dist_id++; + + if (topology->last_dist) + topology->last_dist->next = dist; + else + topology->first_dist = dist; + dist->prev = topology->last_dist; + dist->next = NULL; + topology->last_dist = dist; + return 0; + + err_with_dist: + free(dist); + err: + free(objs); + free(indexes); + free(values); + return -1; +} + +int hwloc_internal_distances_add_by_index(hwloc_topology_t topology, + hwloc_obj_type_t type, unsigned nbobjs, uint64_t *indexes, uint64_t *values, + unsigned long kind, unsigned long flags) +{ + if (nbobjs < 2) { + errno = EINVAL; + goto err; + } + + /* cannot group without objects, + * and we don't group from XML anyway since the hwloc that generated the XML should have grouped already. + */ + if (flags & HWLOC_DISTANCES_ADD_FLAG_GROUP) { + errno = EINVAL; + goto err; + } + + return hwloc_internal_distances__add(topology, type, nbobjs, NULL, indexes, values, kind); + + err: + free(indexes); + free(values); + return -1; +} + +int hwloc_internal_distances_add(hwloc_topology_t topology, + unsigned nbobjs, hwloc_obj_t *objs, uint64_t *values, + unsigned long kind, unsigned long flags) +{ + if (nbobjs < 2) { + errno = EINVAL; + goto err; + } + + if (topology->grouping && (flags & HWLOC_DISTANCES_ADD_FLAG_GROUP)) { + float full_accuracy = 0.f; + float *accuracies; + unsigned nbaccuracies; + + if (flags & HWLOC_DISTANCES_ADD_FLAG_GROUP_INACCURATE) { + accuracies = topology->grouping_accuracies; + nbaccuracies = topology->grouping_nbaccuracies; + } else { + accuracies = &full_accuracy; + nbaccuracies = 1; + } + + if (topology->grouping_verbose) { + unsigned i, j; + int gp = (objs[0]->type != HWLOC_OBJ_NUMANODE && objs[0]->type != HWLOC_OBJ_PU); + fprintf(stderr, "Trying to group objects using distance matrix:\n"); + fprintf(stderr, "%s", gp ? "gp_index" : "os_index"); + for(j=0; jgp_index : objs[j]->os_index)); + fprintf(stderr, "\n"); + for(i=0; igp_index : objs[i]->os_index)); + for(j=0; jtype, nbobjs, objs, NULL, values, kind); + + err: + free(objs); + free(values); + return -1; +} + +#define HWLOC_DISTANCES_KIND_FROM_ALL (HWLOC_DISTANCES_KIND_FROM_OS|HWLOC_DISTANCES_KIND_FROM_USER) +#define HWLOC_DISTANCES_KIND_MEANS_ALL (HWLOC_DISTANCES_KIND_MEANS_LATENCY|HWLOC_DISTANCES_KIND_MEANS_BANDWIDTH) +#define HWLOC_DISTANCES_KIND_ALL (HWLOC_DISTANCES_KIND_FROM_ALL|HWLOC_DISTANCES_KIND_MEANS_ALL) +#define HWLOC_DISTANCES_ADD_FLAG_ALL (HWLOC_DISTANCES_ADD_FLAG_GROUP|HWLOC_DISTANCES_ADD_FLAG_GROUP_INACCURATE) + +/* The actual function exported to the user + */ +int hwloc_distances_add(hwloc_topology_t topology, + unsigned nbobjs, hwloc_obj_t *objs, hwloc_uint64_t *values, + unsigned long kind, unsigned long flags) +{ + hwloc_obj_type_t type; + unsigned i; + uint64_t *_values; + hwloc_obj_t *_objs; + int err; + + if (nbobjs < 2 || !objs || !values || !topology->is_loaded) { + errno = EINVAL; + return -1; + } + if ((kind & ~HWLOC_DISTANCES_KIND_ALL) + || hwloc_weight_long(kind & HWLOC_DISTANCES_KIND_FROM_ALL) != 1 + || hwloc_weight_long(kind & HWLOC_DISTANCES_KIND_MEANS_ALL) != 1 + || (flags & ~HWLOC_DISTANCES_ADD_FLAG_ALL)) { + errno = EINVAL; + return -1; + } + + /* no strict need to check for duplicates, things shouldn't break */ + + type = objs[0]->type; + if (type == HWLOC_OBJ_GROUP) { + /* not supported yet, would require we save the subkind together with the type. */ + errno = EINVAL; + return -1; + } + + for(i=1; itype != type) { + errno = EINVAL; + return -1; + } + + /* copy the input arrays and give them to the topology */ + _objs = malloc(nbobjs*sizeof(hwloc_obj_t)); + _values = malloc(nbobjs*nbobjs*sizeof(*_values)); + if (!_objs || !_values) + goto out_with_arrays; + + memcpy(_objs, objs, nbobjs*sizeof(hwloc_obj_t)); + memcpy(_values, values, nbobjs*nbobjs*sizeof(*_values)); + err = hwloc_internal_distances_add(topology, nbobjs, _objs, _values, kind, flags); + if (err < 0) + goto out; /* _objs and _values freed in hwloc_internal_distances_add() */ + + /* in case we added some groups, see if we need to reconnect */ + hwloc_topology_reconnect(topology, 0); + + return 0; + + out_with_arrays: + free(_values); + free(_objs); + out: + return -1; +} + +/****************************************************** + * Refresh objects in distances + */ + +static hwloc_obj_t hwloc_find_obj_by_type_and_gp_index(hwloc_topology_t topology, hwloc_obj_type_t type, uint64_t gp_index) +{ + hwloc_obj_t obj = hwloc_get_obj_by_type(topology, type, 0); + while (obj) { + if (obj->gp_index == gp_index) + return obj; + obj = obj->next_cousin; + } + return NULL; +} + +static void +hwloc_internal_distances_restrict(struct hwloc_internal_distances_s *dist, + hwloc_obj_t *objs, + unsigned disappeared) +{ + unsigned nbobjs = dist->nbobjs; + unsigned i, newi; + unsigned j, newj; + + for(i=0, newi=0; ivalues[newi*(nbobjs-disappeared)+newj] = dist->values[i*nbobjs+j]; + newj++; + } + newi++; + } + + for(i=0, newi=0; iindexes[newi] = dist->indexes[i]; + newi++; + } + + dist->nbobjs -= disappeared; +} + +static int +hwloc_internal_distances_refresh_one(hwloc_topology_t topology, + struct hwloc_internal_distances_s *dist) +{ + hwloc_obj_type_t type = dist->type; + unsigned nbobjs = dist->nbobjs; + hwloc_obj_t *objs = dist->objs; + uint64_t *indexes = dist->indexes; + unsigned disappeared = 0; + unsigned i; + + if (dist->objs_are_valid) + return 0; + + for(i=0; iobjs_are_valid = 1; + return 0; +} + +/* This function may be called with topology->tma set, it cannot free() or realloc() */ +void +hwloc_internal_distances_refresh(hwloc_topology_t topology) +{ + struct hwloc_internal_distances_s *dist, *next; + + for(dist = topology->first_dist; dist; dist = next) { + next = dist->next; + + if (hwloc_internal_distances_refresh_one(topology, dist) < 0) { + assert(!topology->tma || !topology->tma->dontfree); /* this tma cannot fail to allocate */ + if (dist->prev) + dist->prev->next = next; + else + topology->first_dist = next; + if (next) + next->prev = dist->prev; + else + topology->last_dist = dist->prev; + hwloc_internal_distances_free(dist); + continue; + } + } +} + +void +hwloc_internal_distances_invalidate_cached_objs(hwloc_topology_t topology) +{ + struct hwloc_internal_distances_s *dist; + for(dist = topology->first_dist; dist; dist = dist->next) + dist->objs_are_valid = 0; +} + +/****************************************************** + * User API for getting distances + */ + +void +hwloc_distances_release(hwloc_topology_t topology __hwloc_attribute_unused, + struct hwloc_distances_s *distances) +{ + free(distances->values); + free(distances->objs); + free(distances); +} + +static struct hwloc_distances_s * +hwloc_distances_get_one(hwloc_topology_t topology __hwloc_attribute_unused, + struct hwloc_internal_distances_s *dist) +{ + struct hwloc_distances_s *distances; + unsigned nbobjs; + + distances = malloc(sizeof(*distances)); + if (!distances) + return NULL; + + nbobjs = distances->nbobjs = dist->nbobjs; + + distances->objs = malloc(nbobjs * sizeof(hwloc_obj_t)); + if (!distances->objs) + goto out; + memcpy(distances->objs, dist->objs, nbobjs * sizeof(hwloc_obj_t)); + + distances->values = malloc(nbobjs * nbobjs * sizeof(*distances->values)); + if (!distances->values) + goto out_with_objs; + memcpy(distances->values, dist->values, nbobjs*nbobjs*sizeof(*distances->values)); + + distances->kind = dist->kind; + return distances; + + out_with_objs: + free(distances->objs); + out: + free(distances); + return NULL; +} + +static int +hwloc__distances_get(hwloc_topology_t topology, + hwloc_obj_type_t type, + unsigned *nrp, struct hwloc_distances_s **distancesp, + unsigned long kind, unsigned long flags __hwloc_attribute_unused) +{ + struct hwloc_internal_distances_s *dist; + unsigned nr = 0, i; + + /* We could return the internal arrays (as const), + * but it would require to prevent removing distances between get() and free(). + * Not performance critical anyway. + */ + + if (flags) { + errno = EINVAL; + return -1; + } + + /* we could refresh only the distances that match, but we won't have many distances anyway, + * so performance is totally negligible. + * + * This is also useful in multithreaded apps that modify the topology. + * They can call any valid hwloc_distances_get() to force a refresh after + * changing the topology, so that future concurrent get() won't cause + * concurrent refresh(). + */ + hwloc_internal_distances_refresh(topology); + + for(dist = topology->first_dist; dist; dist = dist->next) { + unsigned long kind_from = kind & HWLOC_DISTANCES_KIND_FROM_ALL; + unsigned long kind_means = kind & HWLOC_DISTANCES_KIND_MEANS_ALL; + + if (type != HWLOC_OBJ_TYPE_NONE && type != dist->type) + continue; + + if (kind_from && !(kind_from & dist->kind)) + continue; + if (kind_means && !(kind_means & dist->kind)) + continue; + + if (nr < *nrp) { + struct hwloc_distances_s *distances = hwloc_distances_get_one(topology, dist); + if (!distances) + goto error; + distancesp[nr] = distances; + } + nr++; + } + + for(i=nr; i<*nrp; i++) + distancesp[i] = NULL; + *nrp = nr; + return 0; + + error: + for(i=0; iis_loaded) { + errno = EINVAL; + return -1; + } + + return hwloc__distances_get(topology, HWLOC_OBJ_TYPE_NONE, nrp, distancesp, kind, flags); +} + +int +hwloc_distances_get_by_depth(hwloc_topology_t topology, int depth, + unsigned *nrp, struct hwloc_distances_s **distancesp, + unsigned long kind, unsigned long flags) +{ + hwloc_obj_type_t type; + + if (flags || !topology->is_loaded) { + errno = EINVAL; + return -1; + } + + /* switch back to types since we don't support groups for now */ + type = hwloc_get_depth_type(topology, depth); + if (type == (hwloc_obj_type_t)-1) { + errno = EINVAL; + return -1; + } + + return hwloc__distances_get(topology, type, nrp, distancesp, kind, flags); +} + +/****************************************************** + * Grouping objects according to distances + */ + +static void hwloc_report_user_distance_error(const char *msg, int line) +{ + static int reported = 0; + + if (!reported && !hwloc_hide_errors()) { + fprintf(stderr, "****************************************************************************\n"); + fprintf(stderr, "* hwloc %s was given invalid distances by the user.\n", HWLOC_VERSION); + fprintf(stderr, "*\n"); + fprintf(stderr, "* %s\n", msg); + fprintf(stderr, "* Error occurred in topology.c line %d\n", line); + fprintf(stderr, "*\n"); + fprintf(stderr, "* Please make sure that distances given through the programming API\n"); + fprintf(stderr, "* do not contradict any other topology information.\n"); + fprintf(stderr, "* \n"); + fprintf(stderr, "* hwloc will now ignore this invalid topology information and continue.\n"); + fprintf(stderr, "****************************************************************************\n"); + reported = 1; + } +} + +static int hwloc_compare_values(uint64_t a, uint64_t b, float accuracy) +{ + if (accuracy != 0.0f && fabsf((float)a-(float)b) < (float)a * accuracy) + return 0; + return a < b ? -1 : a == b ? 0 : 1; +} + +/* + * Place objects in groups if they are in a transitive graph of minimal values. + * Return how many groups were created, or 0 if some incomplete distance graphs were found. + */ +static unsigned +hwloc__find_groups_by_min_distance(unsigned nbobjs, + uint64_t *_values, + float accuracy, + unsigned *groupids, + int verbose) +{ + uint64_t min_distance = UINT64_MAX; + unsigned groupid = 1; + unsigned i,j,k; + unsigned skipped = 0; + +#define VALUE(i, j) _values[(i) * nbobjs + (j)] + + memset(groupids, 0, nbobjs*sizeof(*groupids)); + + /* find the minimal distance */ + for(i=0; igrouping_verbose; + + if (nbobjs <= 2) + return; + + if (!(kind & HWLOC_DISTANCES_KIND_MEANS_LATENCY)) + /* don't know use to use those for grouping */ + /* TODO hwloc__find_groups_by_max_distance() for bandwidth */ + return; + + for(i=0; itype), accuracies[i]); + if (needcheck && hwloc__check_grouping_matrix(nbobjs, _values, accuracies[i], verbose) < 0) + continue; + nbgroups = hwloc__find_groups_by_min_distance(nbobjs, _values, accuracies[i], groupids, verbose); + if (nbgroups) + break; + } + if (!nbgroups) + return; + + { + HWLOC_VLA(hwloc_obj_t, groupobjs, nbgroups); + HWLOC_VLA(unsigned, groupsizes, nbgroups); + HWLOC_VLA(uint64_t, groupvalues, nbgroups*nbgroups); + unsigned failed = 0; + + /* create new Group objects and record their size */ + memset(&(groupsizes[0]), 0, sizeof(groupsizes[0]) * nbgroups); + for(i=0; icpuset = hwloc_bitmap_alloc(); + group_obj->attr->group.kind = HWLOC_GROUP_KIND_DISTANCE; + group_obj->attr->group.subkind = topology->grouping_next_subkind; + for (j=0; jcpuset); + res_obj = hwloc__insert_object_by_cpuset(topology, NULL, group_obj, + (kind & HWLOC_DISTANCES_KIND_FROM_USER) ? hwloc_report_user_distance_error : hwloc_report_os_error); + /* res_obj may be NULL on failure to insert. */ + if (!res_obj) + failed++; + /* or it may be different from groupobjs if we got groups from XML import before grouping */ + groupobjs[i] = res_obj; + } + topology->grouping_next_subkind++; + + if (failed) + /* don't try to group above if we got a NULL group here, just keep this incomplete level */ + return; + + /* factorize values */ + memset(&(groupvalues[0]), 0, sizeof(groupvalues[0]) * nbgroups * nbgroups); +#undef VALUE +#define VALUE(i, j) _values[(i) * nbobjs + (j)] +#define GROUP_VALUE(i, j) groupvalues[(i) * nbgroups + (j)] + for(i=0; i +#include +#include + +#include +#ifdef HAVE_SYS_UTSNAME_H +#include +#endif +#include +#include +#include +#include +#include + +#ifdef HAVE_PROGRAM_INVOCATION_NAME +#include +extern char *program_invocation_name; +#endif +#ifdef HAVE___PROGNAME +extern char *__progname; +#endif + +int hwloc_snprintf(char *str, size_t size, const char *format, ...) +{ + int ret; + va_list ap; + static char bin; + size_t fakesize; + char *fakestr; + + /* Some systems crash on str == NULL */ + if (!size) { + str = &bin; + size = 1; + } + + va_start(ap, format); + ret = vsnprintf(str, size, format, ap); + va_end(ap); + + if (ret >= 0 && (size_t) ret != size-1) + return ret; + + /* vsnprintf returned size-1 or -1. That could be a system which reports the + * written data and not the actually required room. Try increasing buffer + * size to get the latter. */ + + fakesize = size; + fakestr = NULL; + do { + fakesize *= 2; + free(fakestr); + fakestr = malloc(fakesize); + if (NULL == fakestr) + return -1; + va_start(ap, format); + errno = 0; + ret = vsnprintf(fakestr, fakesize, format, ap); + va_end(ap); + } while ((size_t) ret == fakesize-1 || (ret < 0 && (!errno || errno == ERANGE))); + + if (ret >= 0 && size) { + if (size > (size_t) ret+1) + size = ret+1; + memcpy(str, fakestr, size-1); + str[size-1] = 0; + } + free(fakestr); + + return ret; +} + +int hwloc_namecoloncmp(const char *haystack, const char *needle, size_t n) +{ + size_t i = 0; + while (*haystack && *haystack != ':') { + int ha = *haystack++; + int low_h = tolower(ha); + int ne = *needle++; + int low_n = tolower(ne); + if (low_h != low_n) + return 1; + i++; + } + return i < n; +} + +void hwloc_add_uname_info(struct hwloc_topology *topology __hwloc_attribute_unused, + void *cached_uname __hwloc_attribute_unused) +{ +#ifdef HAVE_UNAME + struct utsname _utsname, *utsname; + + if (hwloc_obj_get_info_by_name(topology->levels[0][0], "OSName")) + /* don't annotate twice */ + return; + + if (cached_uname) + utsname = (struct utsname *) cached_uname; + else { + utsname = &_utsname; + if (uname(utsname) < 0) + return; + } + + if (*utsname->sysname) + hwloc_obj_add_info(topology->levels[0][0], "OSName", utsname->sysname); + if (*utsname->release) + hwloc_obj_add_info(topology->levels[0][0], "OSRelease", utsname->release); + if (*utsname->version) + hwloc_obj_add_info(topology->levels[0][0], "OSVersion", utsname->version); + if (*utsname->nodename) + hwloc_obj_add_info(topology->levels[0][0], "HostName", utsname->nodename); + if (*utsname->machine) + hwloc_obj_add_info(topology->levels[0][0], "Architecture", utsname->machine); +#endif /* HAVE_UNAME */ +} + +char * +hwloc_progname(struct hwloc_topology *topology __hwloc_attribute_unused) +{ +#if HAVE_DECL_GETMODULEFILENAME + char name[256], *local_basename; + unsigned res = GetModuleFileName(NULL, name, sizeof(name)); + if (res == sizeof(name) || !res) + return NULL; + local_basename = strrchr(name, '\\'); + if (!local_basename) + local_basename = name; + else + local_basename++; + return strdup(local_basename); +#else /* !HAVE_GETMODULEFILENAME */ + const char *name, *local_basename; +#if HAVE_DECL_GETPROGNAME + name = getprogname(); /* FreeBSD, NetBSD, some Solaris */ +#elif HAVE_DECL_GETEXECNAME + name = getexecname(); /* Solaris */ +#elif defined HAVE_PROGRAM_INVOCATION_NAME + name = program_invocation_name; /* Glibc. BGQ CNK. */ + /* could use program_invocation_short_name directly, but we have the code to remove the path below anyway */ +#elif defined HAVE___PROGNAME + name = __progname; /* fallback for most unix, used for OpenBSD */ +#else + /* TODO: _NSGetExecutablePath(path, &size) on Darwin */ + /* TODO: AIX, HPUX */ + name = NULL; +#endif + if (!name) + return NULL; + local_basename = strrchr(name, '/'); + if (!local_basename) + local_basename = name; + else + local_basename++; + return strdup(local_basename); +#endif /* !HAVE_GETMODULEFILENAME */ +} diff --git a/src/3rdparty/hwloc/src/pci-common.c b/src/3rdparty/hwloc/src/pci-common.c new file mode 100644 index 00000000..00f08a9e --- /dev/null +++ b/src/3rdparty/hwloc/src/pci-common.c @@ -0,0 +1,941 @@ +/* + * Copyright © 2009-2018 Inria. All rights reserved. + * See COPYING in top-level directory. + */ + +#include +#include +#include +#include +#include +#include + +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include + +#if defined(HWLOC_WIN_SYS) && !defined(__CYGWIN__) +#include +#define open _open +#define read _read +#define close _close +#endif + +static void +hwloc_pci_forced_locality_parse_one(struct hwloc_topology *topology, + const char *string /* must contain a ' ' */, + unsigned *allocated) +{ + unsigned nr = topology->pci_forced_locality_nr; + unsigned domain, bus_first, bus_last, dummy; + hwloc_bitmap_t set; + char *tmp; + + if (sscanf(string, "%x:%x-%x %x", &domain, &bus_first, &bus_last, &dummy) == 4) { + /* fine */ + } else if (sscanf(string, "%x:%x %x", &domain, &bus_first, &dummy) == 3) { + bus_last = bus_first; + } else if (sscanf(string, "%x %x", &domain, &dummy) == 2) { + bus_first = 0; + bus_last = 255; + } else + return; + + tmp = strchr(string, ' '); + if (!tmp) + return; + tmp++; + + set = hwloc_bitmap_alloc(); + hwloc_bitmap_sscanf(set, tmp); + + if (!*allocated) { + topology->pci_forced_locality = malloc(sizeof(*topology->pci_forced_locality)); + if (!topology->pci_forced_locality) + goto out_with_set; /* failed to allocate, ignore this forced locality */ + *allocated = 1; + } else if (nr >= *allocated) { + struct hwloc_pci_forced_locality_s *tmplocs; + tmplocs = realloc(topology->pci_forced_locality, + 2 * *allocated * sizeof(*topology->pci_forced_locality)); + if (!tmplocs) + goto out_with_set; /* failed to allocate, ignore this forced locality */ + topology->pci_forced_locality = tmplocs; + *allocated *= 2; + } + + topology->pci_forced_locality[nr].domain = domain; + topology->pci_forced_locality[nr].bus_first = bus_first; + topology->pci_forced_locality[nr].bus_last = bus_last; + topology->pci_forced_locality[nr].cpuset = set; + topology->pci_forced_locality_nr++; + return; + + out_with_set: + hwloc_bitmap_free(set); + return; +} + +static void +hwloc_pci_forced_locality_parse(struct hwloc_topology *topology, const char *_env) +{ + char *env = strdup(_env); + unsigned allocated = 0; + char *tmp = env; + + while (1) { + size_t len = strcspn(tmp, ";\r\n"); + char *next = NULL; + + if (tmp[len] != '\0') { + tmp[len] = '\0'; + if (tmp[len+1] != '\0') + next = &tmp[len]+1; + } + + hwloc_pci_forced_locality_parse_one(topology, tmp, &allocated); + + if (next) + tmp = next; + else + break; + } + + free(env); +} + +void +hwloc_pci_discovery_init(struct hwloc_topology *topology) +{ + topology->need_pci_belowroot_apply_locality = 0; + + topology->pci_has_forced_locality = 0; + topology->pci_forced_locality_nr = 0; + topology->pci_forced_locality = NULL; +} + +void +hwloc_pci_discovery_prepare(struct hwloc_topology *topology) +{ + char *env; + + env = getenv("HWLOC_PCI_LOCALITY"); + if (env) { + int fd; + + topology->pci_has_forced_locality = 1; + + fd = open(env, O_RDONLY); + if (fd >= 0) { + struct stat st; + char *buffer; + int err = fstat(fd, &st); + if (!err) { + if (st.st_size <= 64*1024) { /* random limit large enough to store multiple cpusets for thousands of PUs */ + buffer = malloc(st.st_size+1); + if (read(fd, buffer, st.st_size) == st.st_size) { + buffer[st.st_size] = '\0'; + hwloc_pci_forced_locality_parse(topology, buffer); + } + free(buffer); + } else { + fprintf(stderr, "Ignoring HWLOC_PCI_LOCALITY file `%s' too large (%lu bytes)\n", + env, (unsigned long) st.st_size); + } + } + close(fd); + } else + hwloc_pci_forced_locality_parse(topology, env); + } +} + +void +hwloc_pci_discovery_exit(struct hwloc_topology *topology __hwloc_attribute_unused) +{ + unsigned i; + for(i=0; ipci_forced_locality_nr; i++) + hwloc_bitmap_free(topology->pci_forced_locality[i].cpuset); + free(topology->pci_forced_locality); + + hwloc_pci_discovery_init(topology); +} + +#ifdef HWLOC_DEBUG +static void +hwloc_pci_traverse_print_cb(void * cbdata __hwloc_attribute_unused, + struct hwloc_obj *pcidev) +{ + char busid[14]; + hwloc_obj_t parent; + + /* indent */ + parent = pcidev->parent; + while (parent) { + hwloc_debug("%s", " "); + parent = parent->parent; + } + + snprintf(busid, sizeof(busid), "%04x:%02x:%02x.%01x", + pcidev->attr->pcidev.domain, pcidev->attr->pcidev.bus, pcidev->attr->pcidev.dev, pcidev->attr->pcidev.func); + + if (pcidev->type == HWLOC_OBJ_BRIDGE) { + if (pcidev->attr->bridge.upstream_type == HWLOC_OBJ_BRIDGE_HOST) + hwloc_debug("HostBridge"); + else + hwloc_debug("%s Bridge [%04x:%04x]", busid, + pcidev->attr->pcidev.vendor_id, pcidev->attr->pcidev.device_id); + hwloc_debug(" to %04x:[%02x:%02x]\n", + pcidev->attr->bridge.downstream.pci.domain, pcidev->attr->bridge.downstream.pci.secondary_bus, pcidev->attr->bridge.downstream.pci.subordinate_bus); + } else + hwloc_debug("%s Device [%04x:%04x (%04x:%04x) rev=%02x class=%04x]\n", busid, + pcidev->attr->pcidev.vendor_id, pcidev->attr->pcidev.device_id, + pcidev->attr->pcidev.subvendor_id, pcidev->attr->pcidev.subdevice_id, + pcidev->attr->pcidev.revision, pcidev->attr->pcidev.class_id); +} + +static void +hwloc_pci_traverse(void * cbdata, struct hwloc_obj *tree, + void (*cb)(void * cbdata, struct hwloc_obj *)) +{ + hwloc_obj_t child; + cb(cbdata, tree); + for_each_io_child(child, tree) { + if (child->type == HWLOC_OBJ_BRIDGE) + hwloc_pci_traverse(cbdata, child, cb); + } +} +#endif /* HWLOC_DEBUG */ + +enum hwloc_pci_busid_comparison_e { + HWLOC_PCI_BUSID_LOWER, + HWLOC_PCI_BUSID_HIGHER, + HWLOC_PCI_BUSID_INCLUDED, + HWLOC_PCI_BUSID_SUPERSET +}; + +static enum hwloc_pci_busid_comparison_e +hwloc_pci_compare_busids(struct hwloc_obj *a, struct hwloc_obj *b) +{ +#ifdef HWLOC_DEBUG + if (a->type == HWLOC_OBJ_BRIDGE) + assert(a->attr->bridge.upstream_type == HWLOC_OBJ_BRIDGE_PCI); + if (b->type == HWLOC_OBJ_BRIDGE) + assert(b->attr->bridge.upstream_type == HWLOC_OBJ_BRIDGE_PCI); +#endif + + if (a->attr->pcidev.domain < b->attr->pcidev.domain) + return HWLOC_PCI_BUSID_LOWER; + if (a->attr->pcidev.domain > b->attr->pcidev.domain) + return HWLOC_PCI_BUSID_HIGHER; + + if (a->type == HWLOC_OBJ_BRIDGE + && b->attr->pcidev.bus >= a->attr->bridge.downstream.pci.secondary_bus + && b->attr->pcidev.bus <= a->attr->bridge.downstream.pci.subordinate_bus) + return HWLOC_PCI_BUSID_SUPERSET; + if (b->type == HWLOC_OBJ_BRIDGE + && a->attr->pcidev.bus >= b->attr->bridge.downstream.pci.secondary_bus + && a->attr->pcidev.bus <= b->attr->bridge.downstream.pci.subordinate_bus) + return HWLOC_PCI_BUSID_INCLUDED; + + if (a->attr->pcidev.bus < b->attr->pcidev.bus) + return HWLOC_PCI_BUSID_LOWER; + if (a->attr->pcidev.bus > b->attr->pcidev.bus) + return HWLOC_PCI_BUSID_HIGHER; + + if (a->attr->pcidev.dev < b->attr->pcidev.dev) + return HWLOC_PCI_BUSID_LOWER; + if (a->attr->pcidev.dev > b->attr->pcidev.dev) + return HWLOC_PCI_BUSID_HIGHER; + + if (a->attr->pcidev.func < b->attr->pcidev.func) + return HWLOC_PCI_BUSID_LOWER; + if (a->attr->pcidev.func > b->attr->pcidev.func) + return HWLOC_PCI_BUSID_HIGHER; + + /* Should never reach here. Abort on both debug builds and + non-debug builds */ + assert(0); + fprintf(stderr, "Bad assertion in hwloc %s:%d (aborting)\n", __FILE__, __LINE__); + exit(1); +} + +static void +hwloc_pci_add_object(struct hwloc_obj *parent, struct hwloc_obj **parent_io_first_child_p, struct hwloc_obj *new) +{ + struct hwloc_obj **curp, **childp; + + curp = parent_io_first_child_p; + while (*curp) { + enum hwloc_pci_busid_comparison_e comp = hwloc_pci_compare_busids(new, *curp); + switch (comp) { + case HWLOC_PCI_BUSID_HIGHER: + /* go further */ + curp = &(*curp)->next_sibling; + continue; + case HWLOC_PCI_BUSID_INCLUDED: + /* insert new below current bridge */ + hwloc_pci_add_object(*curp, &(*curp)->io_first_child, new); + return; + case HWLOC_PCI_BUSID_LOWER: + case HWLOC_PCI_BUSID_SUPERSET: { + /* insert new before current */ + new->next_sibling = *curp; + *curp = new; + new->parent = parent; + if (new->type == HWLOC_OBJ_BRIDGE) { + /* look at remaining siblings and move some below new */ + childp = &new->io_first_child; + curp = &new->next_sibling; + while (*curp) { + hwloc_obj_t cur = *curp; + if (hwloc_pci_compare_busids(new, cur) == HWLOC_PCI_BUSID_LOWER) { + /* this sibling remains under root, after new. */ + if (cur->attr->pcidev.domain > new->attr->pcidev.domain + || cur->attr->pcidev.bus > new->attr->bridge.downstream.pci.subordinate_bus) + /* this sibling is even above new's subordinate bus, no other sibling could go below new */ + return; + curp = &cur->next_sibling; + } else { + /* this sibling goes under new */ + *childp = cur; + *curp = cur->next_sibling; + (*childp)->parent = new; + (*childp)->next_sibling = NULL; + childp = &(*childp)->next_sibling; + } + } + } + return; + } + } + } + /* add to the end of the list if higher than everybody */ + new->parent = parent; + new->next_sibling = NULL; + *curp = new; +} + +void +hwloc_pcidisc_tree_insert_by_busid(struct hwloc_obj **treep, + struct hwloc_obj *obj) +{ + hwloc_pci_add_object(NULL /* no parent on top of tree */, treep, obj); +} + +int +hwloc_pcidisc_tree_attach(struct hwloc_topology *topology, struct hwloc_obj *old_tree) +{ + struct hwloc_obj **next_hb_p; + enum hwloc_type_filter_e bfilter; + + if (!old_tree) + /* found nothing, exit */ + return 0; + +#ifdef HWLOC_DEBUG + hwloc_debug("%s", "\nPCI hierarchy:\n"); + hwloc_pci_traverse(NULL, old_tree, hwloc_pci_traverse_print_cb); + hwloc_debug("%s", "\n"); +#endif + + next_hb_p = &hwloc_get_root_obj(topology)->io_first_child; + while (*next_hb_p) + next_hb_p = &((*next_hb_p)->next_sibling); + + bfilter = topology->type_filter[HWLOC_OBJ_BRIDGE]; + if (bfilter == HWLOC_TYPE_FILTER_KEEP_NONE) { + *next_hb_p = old_tree; + topology->modified = 1; + goto done; + } + + /* + * tree points to all objects connected to any upstream bus in the machine. + * We now create one real hostbridge object per upstream bus. + * It's not actually a PCI device so we have to create it. + */ + while (old_tree) { + /* start a new host bridge */ + struct hwloc_obj *hostbridge = hwloc_alloc_setup_object(topology, HWLOC_OBJ_BRIDGE, HWLOC_UNKNOWN_INDEX); + struct hwloc_obj **dstnextp = &hostbridge->io_first_child; + struct hwloc_obj **srcnextp = &old_tree; + struct hwloc_obj *child = *srcnextp; + unsigned short current_domain = child->attr->pcidev.domain; + unsigned char current_bus = child->attr->pcidev.bus; + unsigned char current_subordinate = current_bus; + + hwloc_debug("Starting new PCI hostbridge %04x:%02x\n", current_domain, current_bus); + + next_child: + /* remove next child from tree */ + *srcnextp = child->next_sibling; + /* append it to hostbridge */ + *dstnextp = child; + child->parent = hostbridge; + child->next_sibling = NULL; + dstnextp = &child->next_sibling; + + /* compute hostbridge secondary/subordinate buses */ + if (child->type == HWLOC_OBJ_BRIDGE + && child->attr->bridge.downstream.pci.subordinate_bus > current_subordinate) + current_subordinate = child->attr->bridge.downstream.pci.subordinate_bus; + + /* use next child if it has the same domains/bus */ + child = *srcnextp; + if (child + && child->attr->pcidev.domain == current_domain + && child->attr->pcidev.bus == current_bus) + goto next_child; + + /* finish setting up this hostbridge */ + hostbridge->attr->bridge.upstream_type = HWLOC_OBJ_BRIDGE_HOST; + hostbridge->attr->bridge.downstream_type = HWLOC_OBJ_BRIDGE_PCI; + hostbridge->attr->bridge.downstream.pci.domain = current_domain; + hostbridge->attr->bridge.downstream.pci.secondary_bus = current_bus; + hostbridge->attr->bridge.downstream.pci.subordinate_bus = current_subordinate; + hwloc_debug("New PCI hostbridge %04x:[%02x-%02x]\n", + current_domain, current_bus, current_subordinate); + + *next_hb_p = hostbridge; + next_hb_p = &hostbridge->next_sibling; + topology->modified = 1; /* needed in case somebody reconnects levels before the core calls hwloc_pci_belowroot_apply_locality() + * or if hwloc_pci_belowroot_apply_locality() keeps hostbridges below root. + */ + } + + done: + topology->need_pci_belowroot_apply_locality = 1; + return 0; +} + +static struct hwloc_obj * +hwloc_pci_fixup_busid_parent(struct hwloc_topology *topology __hwloc_attribute_unused, + struct hwloc_pcidev_attr_s *busid, + struct hwloc_obj *parent) +{ + /* Xeon E5v3 in cluster-on-die mode only have PCI on the first NUMA node of each package. + * but many dual-processor host report the second PCI hierarchy on 2nd NUMA of first package. + */ + if (parent->depth >= 2 + && parent->type == HWLOC_OBJ_NUMANODE + && parent->sibling_rank == 1 && parent->parent->arity == 2 + && parent->parent->type == HWLOC_OBJ_PACKAGE + && parent->parent->sibling_rank == 0 && parent->parent->parent->arity == 2) { + const char *cpumodel = hwloc_obj_get_info_by_name(parent->parent, "CPUModel"); + if (cpumodel && strstr(cpumodel, "Xeon")) { + if (!hwloc_hide_errors()) { + fprintf(stderr, "****************************************************************************\n"); + fprintf(stderr, "* hwloc %s has encountered an incorrect PCI locality information.\n", HWLOC_VERSION); + fprintf(stderr, "* PCI bus %04x:%02x is supposedly close to 2nd NUMA node of 1st package,\n", + busid->domain, busid->bus); + fprintf(stderr, "* however hwloc believes this is impossible on this architecture.\n"); + fprintf(stderr, "* Therefore the PCI bus will be moved to 1st NUMA node of 2nd package.\n"); + fprintf(stderr, "*\n"); + fprintf(stderr, "* If you feel this fixup is wrong, disable it by setting in your environment\n"); + fprintf(stderr, "* HWLOC_PCI_%04x_%02x_LOCALCPUS= (empty value), and report the problem\n", + busid->domain, busid->bus); + fprintf(stderr, "* to the hwloc's user mailing list together with the XML output of lstopo.\n"); + fprintf(stderr, "*\n"); + fprintf(stderr, "* You may silence this message by setting HWLOC_HIDE_ERRORS=1 in your environment.\n"); + fprintf(stderr, "****************************************************************************\n"); + } + return parent->parent->next_sibling->first_child; + } + } + + return parent; +} + +static struct hwloc_obj * +hwloc__pci_find_busid_parent(struct hwloc_topology *topology, struct hwloc_pcidev_attr_s *busid) +{ + hwloc_bitmap_t cpuset = hwloc_bitmap_alloc(); + hwloc_obj_t parent; + int forced = 0; + int noquirks = 0; + unsigned i; + int err; + + /* try to match a forced locality */ + if (topology->pci_has_forced_locality) { + for(i=0; ipci_forced_locality_nr; i++) { + if (busid->domain == topology->pci_forced_locality[i].domain + && busid->bus >= topology->pci_forced_locality[i].bus_first + && busid->bus <= topology->pci_forced_locality[i].bus_last) { + hwloc_bitmap_copy(cpuset, topology->pci_forced_locality[i].cpuset); + forced = 1; + break; + } + } + /* if pci locality was forced, even empty, don't let quirks change what the OS reports */ + noquirks = 1; + } + + /* deprecated force locality variables */ + if (!forced) { + const char *env; + char envname[256]; + /* override the cpuset with the environment if given */ + snprintf(envname, sizeof(envname), "HWLOC_PCI_%04x_%02x_LOCALCPUS", + busid->domain, busid->bus); + env = getenv(envname); + if (env) { + static int reported = 0; + if (!topology->pci_has_forced_locality && !reported) { + fprintf(stderr, "Environment variable %s is deprecated, please use HWLOC_PCI_LOCALITY instead.\n", env); + reported = 1; + } + if (*env) { + /* force the cpuset */ + hwloc_debug("Overriding localcpus using %s in the environment\n", envname); + hwloc_bitmap_sscanf(cpuset, env); + forced = 1; + } + /* if env exists, even empty, don't let quirks change what the OS reports */ + noquirks = 1; + } + } + + if (!forced) { + /* get the cpuset by asking the OS backend. */ + struct hwloc_backend *backend = topology->get_pci_busid_cpuset_backend; + if (backend) + err = backend->get_pci_busid_cpuset(backend, busid, cpuset); + else + err = -1; + if (err < 0) + /* if we got nothing, assume this PCI bus is attached to the top of hierarchy */ + hwloc_bitmap_copy(cpuset, hwloc_topology_get_topology_cpuset(topology)); + } + + hwloc_debug_bitmap("Attaching PCI tree to cpuset %s\n", cpuset); + + parent = hwloc_find_insert_io_parent_by_complete_cpuset(topology, cpuset); + if (parent) { + if (!noquirks) + /* We found a valid parent. Check that the OS didn't report invalid locality */ + parent = hwloc_pci_fixup_busid_parent(topology, busid, parent); + } else { + /* Fallback to root */ + parent = hwloc_get_root_obj(topology); + } + + hwloc_bitmap_free(cpuset); + return parent; +} + +struct hwloc_obj * +hwloc_pcidisc_find_busid_parent(struct hwloc_topology *topology, + unsigned domain, unsigned bus, unsigned dev, unsigned func) +{ + struct hwloc_pcidev_attr_s busid; + busid.domain = domain; + busid.bus = bus; + busid.dev = dev; + busid.func = func; + return hwloc__pci_find_busid_parent(topology, &busid); +} + +int +hwloc_pci_belowroot_apply_locality(struct hwloc_topology *topology) +{ + struct hwloc_obj *root = hwloc_get_root_obj(topology); + struct hwloc_obj **listp, *obj; + + if (!topology->need_pci_belowroot_apply_locality) + return 0; + topology->need_pci_belowroot_apply_locality = 0; + + /* root->io_first_child contains some PCI hierarchies, any maybe some non-PCI things. + * insert the PCI trees according to their PCI-locality. + */ + listp = &root->io_first_child; + while ((obj = *listp) != NULL) { + struct hwloc_pcidev_attr_s *busid; + struct hwloc_obj *parent; + + /* skip non-PCI objects */ + if (obj->type != HWLOC_OBJ_PCI_DEVICE + && !(obj->type == HWLOC_OBJ_BRIDGE && obj->attr->bridge.downstream_type == HWLOC_OBJ_BRIDGE_PCI) + && !(obj->type == HWLOC_OBJ_BRIDGE && obj->attr->bridge.upstream_type == HWLOC_OBJ_BRIDGE_PCI)) { + listp = &obj->next_sibling; + continue; + } + + if (obj->type == HWLOC_OBJ_PCI_DEVICE + || (obj->type == HWLOC_OBJ_BRIDGE + && obj->attr->bridge.upstream_type == HWLOC_OBJ_BRIDGE_PCI)) + busid = &obj->attr->pcidev; + else { + /* hostbridges don't have a PCI busid for looking up locality, use their first child if PCI */ + hwloc_obj_t child = obj->io_first_child; + if (child && (child->type == HWLOC_OBJ_PCI_DEVICE + || (child->type == HWLOC_OBJ_BRIDGE + && child->attr->bridge.upstream_type == HWLOC_OBJ_BRIDGE_PCI))) + busid = &obj->io_first_child->attr->pcidev; + else + continue; + } + + /* attach the object (and children) where it belongs */ + parent = hwloc__pci_find_busid_parent(topology, busid); + if (parent == root) { + /* keep this object here */ + listp = &obj->next_sibling; + } else { + /* dequeue this object */ + *listp = obj->next_sibling; + obj->next_sibling = NULL; + hwloc_insert_object_by_parent(topology, parent, obj); + } + } + + return 0; +} + +static struct hwloc_obj * +hwloc__pci_belowroot_find_by_busid(hwloc_obj_t parent, + unsigned domain, unsigned bus, unsigned dev, unsigned func) +{ + hwloc_obj_t child; + + for_each_io_child(child, parent) { + if (child->type == HWLOC_OBJ_PCI_DEVICE + || (child->type == HWLOC_OBJ_BRIDGE + && child->attr->bridge.upstream_type == HWLOC_OBJ_BRIDGE_PCI)) { + if (child->attr->pcidev.domain == domain + && child->attr->pcidev.bus == bus + && child->attr->pcidev.dev == dev + && child->attr->pcidev.func == func) + /* that's the right bus id */ + return child; + if (child->attr->pcidev.domain > domain + || (child->attr->pcidev.domain == domain + && child->attr->pcidev.bus > bus)) + /* bus id too high, won't find anything later, return parent */ + return parent; + if (child->type == HWLOC_OBJ_BRIDGE + && child->attr->bridge.downstream_type == HWLOC_OBJ_BRIDGE_PCI + && child->attr->bridge.downstream.pci.domain == domain + && child->attr->bridge.downstream.pci.secondary_bus <= bus + && child->attr->bridge.downstream.pci.subordinate_bus >= bus) + /* not the right bus id, but it's included in the bus below that bridge */ + return hwloc__pci_belowroot_find_by_busid(child, domain, bus, dev, func); + + } else if (child->type == HWLOC_OBJ_BRIDGE + && child->attr->bridge.upstream_type != HWLOC_OBJ_BRIDGE_PCI + && child->attr->bridge.downstream_type == HWLOC_OBJ_BRIDGE_PCI + /* non-PCI to PCI bridge, just look at the subordinate bus */ + && child->attr->bridge.downstream.pci.domain == domain + && child->attr->bridge.downstream.pci.secondary_bus <= bus + && child->attr->bridge.downstream.pci.subordinate_bus >= bus) { + /* contains our bus, recurse */ + return hwloc__pci_belowroot_find_by_busid(child, domain, bus, dev, func); + } + } + /* didn't find anything, return parent */ + return parent; +} + +struct hwloc_obj * +hwloc_pcidisc_find_by_busid(struct hwloc_topology *topology, + unsigned domain, unsigned bus, unsigned dev, unsigned func) +{ + hwloc_obj_t root = hwloc_get_root_obj(topology); + hwloc_obj_t parent = hwloc__pci_belowroot_find_by_busid(root, domain, bus, dev, func); + if (parent == root) + return NULL; + else + return parent; +} + +#define HWLOC_PCI_STATUS 0x06 +#define HWLOC_PCI_STATUS_CAP_LIST 0x10 +#define HWLOC_PCI_CAPABILITY_LIST 0x34 +#define HWLOC_PCI_CAP_LIST_ID 0 +#define HWLOC_PCI_CAP_LIST_NEXT 1 + +unsigned +hwloc_pcidisc_find_cap(const unsigned char *config, unsigned cap) +{ + unsigned char seen[256] = { 0 }; + unsigned char ptr; /* unsigned char to make sure we stay within the 256-byte config space */ + + if (!(config[HWLOC_PCI_STATUS] & HWLOC_PCI_STATUS_CAP_LIST)) + return 0; + + for (ptr = config[HWLOC_PCI_CAPABILITY_LIST] & ~3; + ptr; /* exit if next is 0 */ + ptr = config[ptr + HWLOC_PCI_CAP_LIST_NEXT] & ~3) { + unsigned char id; + + /* Looped around! */ + if (seen[ptr]) + break; + seen[ptr] = 1; + + id = config[ptr + HWLOC_PCI_CAP_LIST_ID]; + if (id == cap) + return ptr; + if (id == 0xff) /* exit if id is 0 or 0xff */ + break; + } + return 0; +} + +#define HWLOC_PCI_EXP_LNKSTA 0x12 +#define HWLOC_PCI_EXP_LNKSTA_SPEED 0x000f +#define HWLOC_PCI_EXP_LNKSTA_WIDTH 0x03f0 + +int +hwloc_pcidisc_find_linkspeed(const unsigned char *config, + unsigned offset, float *linkspeed) +{ + unsigned linksta, speed, width; + float lanespeed; + + memcpy(&linksta, &config[offset + HWLOC_PCI_EXP_LNKSTA], 4); + speed = linksta & HWLOC_PCI_EXP_LNKSTA_SPEED; /* PCIe generation */ + width = (linksta & HWLOC_PCI_EXP_LNKSTA_WIDTH) >> 4; /* how many lanes */ + /* PCIe Gen1 = 2.5GT/s signal-rate per lane with 8/10 encoding = 0.25GB/s data-rate per lane + * PCIe Gen2 = 5 GT/s signal-rate per lane with 8/10 encoding = 0.5 GB/s data-rate per lane + * PCIe Gen3 = 8 GT/s signal-rate per lane with 128/130 encoding = 1 GB/s data-rate per lane + * PCIe Gen4 = 16 GT/s signal-rate per lane with 128/130 encoding = 2 GB/s data-rate per lane + */ + + /* lanespeed in Gbit/s */ + if (speed <= 2) + lanespeed = 2.5f * speed * 0.8f; + else + lanespeed = 8.0f * (1<<(speed-3)) * 128/130; /* assume Gen5 will be 32 GT/s and so on */ + + /* linkspeed in GB/s */ + *linkspeed = lanespeed * width / 8; + return 0; +} + +#define HWLOC_PCI_HEADER_TYPE 0x0e +#define HWLOC_PCI_HEADER_TYPE_BRIDGE 1 +#define HWLOC_PCI_CLASS_BRIDGE_PCI 0x0604 + +hwloc_obj_type_t +hwloc_pcidisc_check_bridge_type(unsigned device_class, const unsigned char *config) +{ + unsigned char headertype; + + if (device_class != HWLOC_PCI_CLASS_BRIDGE_PCI) + return HWLOC_OBJ_PCI_DEVICE; + + headertype = config[HWLOC_PCI_HEADER_TYPE] & 0x7f; + return (headertype == HWLOC_PCI_HEADER_TYPE_BRIDGE) + ? HWLOC_OBJ_BRIDGE : HWLOC_OBJ_PCI_DEVICE; +} + +#define HWLOC_PCI_PRIMARY_BUS 0x18 +#define HWLOC_PCI_SECONDARY_BUS 0x19 +#define HWLOC_PCI_SUBORDINATE_BUS 0x1a + +int +hwloc_pcidisc_setup_bridge_attr(hwloc_obj_t obj, + const unsigned char *config) +{ + struct hwloc_bridge_attr_s *battr = &obj->attr->bridge; + struct hwloc_pcidev_attr_s *pattr = &battr->upstream.pci; + + if (config[HWLOC_PCI_PRIMARY_BUS] != pattr->bus) { + /* Sometimes the config space contains 00 instead of the actual primary bus number. + * Always trust the bus ID because it was built by the system which has more information + * to workaround such problems (e.g. ACPI information about PCI parent/children). + */ + hwloc_debug(" %04x:%02x:%02x.%01x bridge with (ignored) invalid PCI_PRIMARY_BUS %02x\n", + pattr->domain, pattr->bus, pattr->dev, pattr->func, config[HWLOC_PCI_PRIMARY_BUS]); + } + + battr->upstream_type = HWLOC_OBJ_BRIDGE_PCI; + battr->downstream_type = HWLOC_OBJ_BRIDGE_PCI; + battr->downstream.pci.domain = pattr->domain; + battr->downstream.pci.secondary_bus = config[HWLOC_PCI_SECONDARY_BUS]; + battr->downstream.pci.subordinate_bus = config[HWLOC_PCI_SUBORDINATE_BUS]; + + if (battr->downstream.pci.secondary_bus <= pattr->bus + || battr->downstream.pci.subordinate_bus <= pattr->bus + || battr->downstream.pci.secondary_bus > battr->downstream.pci.subordinate_bus) { + /* This should catch most cases of invalid bridge information + * (e.g. 00 for secondary and subordinate). + * Ideally we would also check that [secondary-subordinate] is included + * in the parent bridge [secondary+1:subordinate]. But that's hard to do + * because objects may be discovered out of order (especially in the fsroot case). + */ + hwloc_debug(" %04x:%02x:%02x.%01x bridge has invalid secondary-subordinate buses [%02x-%02x]\n", + pattr->domain, pattr->bus, pattr->dev, pattr->func, + battr->downstream.pci.secondary_bus, battr->downstream.pci.subordinate_bus); + hwloc_free_unlinked_object(obj); + return -1; + } + + return 0; +} + +const char * +hwloc_pci_class_string(unsigned short class_id) +{ + /* See https://pci-ids.ucw.cz/read/PD/ */ + switch ((class_id & 0xff00) >> 8) { + case 0x00: + switch (class_id) { + case 0x0001: return "VGA"; + } + break; + case 0x01: + switch (class_id) { + case 0x0100: return "SCSI"; + case 0x0101: return "IDE"; + case 0x0102: return "Floppy"; + case 0x0103: return "IPI"; + case 0x0104: return "RAID"; + case 0x0105: return "ATA"; + case 0x0106: return "SATA"; + case 0x0107: return "SAS"; + case 0x0108: return "NVMExp"; + } + return "Storage"; + case 0x02: + switch (class_id) { + case 0x0200: return "Ethernet"; + case 0x0201: return "TokenRing"; + case 0x0202: return "FDDI"; + case 0x0203: return "ATM"; + case 0x0204: return "ISDN"; + case 0x0205: return "WorldFip"; + case 0x0206: return "PICMG"; + case 0x0207: return "InfiniBand"; + case 0x0208: return "Fabric"; + } + return "Network"; + case 0x03: + switch (class_id) { + case 0x0300: return "VGA"; + case 0x0301: return "XGA"; + case 0x0302: return "3D"; + } + return "Display"; + case 0x04: + switch (class_id) { + case 0x0400: return "MultimediaVideo"; + case 0x0401: return "MultimediaAudio"; + case 0x0402: return "Telephony"; + case 0x0403: return "AudioDevice"; + } + return "Multimedia"; + case 0x05: + switch (class_id) { + case 0x0500: return "RAM"; + case 0x0501: return "Flash"; + } + return "Memory"; + case 0x06: + switch (class_id) { + case 0x0600: return "HostBridge"; + case 0x0601: return "ISABridge"; + case 0x0602: return "EISABridge"; + case 0x0603: return "MicroChannelBridge"; + case 0x0604: return "PCIBridge"; + case 0x0605: return "PCMCIABridge"; + case 0x0606: return "NubusBridge"; + case 0x0607: return "CardBusBridge"; + case 0x0608: return "RACEwayBridge"; + case 0x0609: return "SemiTransparentPCIBridge"; + case 0x060a: return "InfiniBandPCIHostBridge"; + } + return "Bridge"; + case 0x07: + switch (class_id) { + case 0x0700: return "Serial"; + case 0x0701: return "Parallel"; + case 0x0702: return "MultiportSerial"; + case 0x0703: return "Model"; + case 0x0704: return "GPIB"; + case 0x0705: return "SmartCard"; + } + return "Communication"; + case 0x08: + switch (class_id) { + case 0x0800: return "PIC"; + case 0x0801: return "DMA"; + case 0x0802: return "Timer"; + case 0x0803: return "RTC"; + case 0x0804: return "PCIHotPlug"; + case 0x0805: return "SDHost"; + case 0x0806: return "IOMMU"; + } + return "SystemPeripheral"; + case 0x09: + switch (class_id) { + case 0x0900: return "Keyboard"; + case 0x0901: return "DigitizerPen"; + case 0x0902: return "Mouse"; + case 0x0903: return "Scanern"; + case 0x0904: return "Gameport"; + } + return "Input"; + case 0x0a: + return "DockingStation"; + case 0x0b: + switch (class_id) { + case 0x0b00: return "386"; + case 0x0b01: return "486"; + case 0x0b02: return "Pentium"; +/* 0x0b03 and 0x0b04 might be Pentium and P6 ? */ + case 0x0b10: return "Alpha"; + case 0x0b20: return "PowerPC"; + case 0x0b30: return "MIPS"; + case 0x0b40: return "Co-Processor"; + } + return "Processor"; + case 0x0c: + switch (class_id) { + case 0x0c00: return "FireWire"; + case 0x0c01: return "ACCESS"; + case 0x0c02: return "SSA"; + case 0x0c03: return "USB"; + case 0x0c04: return "FibreChannel"; + case 0x0c05: return "SMBus"; + case 0x0c06: return "InfiniBand"; + case 0x0c07: return "IPMI-SMIC"; + case 0x0c08: return "SERCOS"; + case 0x0c09: return "CANBUS"; + } + return "SerialBus"; + case 0x0d: + switch (class_id) { + case 0x0d00: return "IRDA"; + case 0x0d01: return "ConsumerIR"; + case 0x0d10: return "RF"; + case 0x0d11: return "Bluetooth"; + case 0x0d12: return "Broadband"; + case 0x0d20: return "802.1a"; + case 0x0d21: return "802.1b"; + } + return "Wireless"; + case 0x0e: + switch (class_id) { + case 0x0e00: return "I2O"; + } + return "Intelligent"; + case 0x0f: + return "Satellite"; + case 0x10: + return "Encryption"; + case 0x11: + return "SignalProcessing"; + case 0x12: + return "ProcessingAccelerator"; + case 0x13: + return "Instrumentation"; + case 0x40: + return "Co-Processor"; + } + return "Other"; +} diff --git a/src/3rdparty/hwloc/src/shmem.c b/src/3rdparty/hwloc/src/shmem.c new file mode 100644 index 00000000..6c507f52 --- /dev/null +++ b/src/3rdparty/hwloc/src/shmem.c @@ -0,0 +1,287 @@ +/* + * Copyright © 2017-2018 Inria. All rights reserved. + * See COPYING in top-level directory. + */ + +#include +#include +#include +#include + +#ifndef HWLOC_WIN_SYS + +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include + +#define HWLOC_SHMEM_HEADER_VERSION 1 + +struct hwloc_shmem_header { + uint32_t header_version; /* sanity check */ + uint32_t header_length; /* where the actual topology starts in the file/mapping */ + uint64_t mmap_address; /* virtual address to pass to mmap */ + uint64_t mmap_length; /* length to pass to mmap (includes the header) */ +}; + +#define HWLOC_SHMEM_MALLOC_ALIGN 8UL + +static void * +tma_shmem_malloc(struct hwloc_tma * tma, + size_t length) +{ + void *current = tma->data; + tma->data = (char*)tma->data + ((length + HWLOC_SHMEM_MALLOC_ALIGN - 1) & ~(HWLOC_SHMEM_MALLOC_ALIGN - 1)); + return current; + +} + +static void * +tma_get_length_malloc(struct hwloc_tma * tma, + size_t length) +{ + size_t *tma_length = tma->data; + *tma_length += (length + HWLOC_SHMEM_MALLOC_ALIGN - 1) & ~(HWLOC_SHMEM_MALLOC_ALIGN - 1); + return malloc(length); + +} + +int +hwloc_shmem_topology_get_length(hwloc_topology_t topology, + size_t *lengthp, + unsigned long flags) +{ + hwloc_topology_t new; + struct hwloc_tma tma; + size_t length = 0; + unsigned long pagesize = hwloc_getpagesize(); /* round-up to full page for mmap() */ + int err; + + if (flags) { + errno = EINVAL; + return -1; + } + + tma.malloc = tma_get_length_malloc; + tma.dontfree = 0; + tma.data = &length; + + err = hwloc__topology_dup(&new, topology, &tma); + if (err < 0) + return err; + hwloc_topology_destroy(new); + + *lengthp = (sizeof(struct hwloc_shmem_header) + length + pagesize - 1) & ~(pagesize - 1); + return 0; +} + +int +hwloc_shmem_topology_write(hwloc_topology_t topology, + int fd, hwloc_uint64_t fileoffset, + void *mmap_address, size_t length, + unsigned long flags) +{ + hwloc_topology_t new; + struct hwloc_tma tma; + struct hwloc_shmem_header header; + void *mmap_res; + int err; + + if (flags) { + errno = EINVAL; + return -1; + } + + /* refresh old topology distances so that we don't uselessly duplicate invalid distances + * without being able to free() them. + */ + hwloc_internal_distances_refresh(topology); + + header.header_version = HWLOC_SHMEM_HEADER_VERSION; + header.header_length = sizeof(header); + header.mmap_address = (uintptr_t) mmap_address; + header.mmap_length = length; + + err = lseek(fd, fileoffset, SEEK_SET); + if (err < 0) + return -1; + + err = write(fd, &header, sizeof(header)); + if (err != sizeof(header)) + return -1; + + err = ftruncate(fd, fileoffset + length); + if (err < 0) + return -1; + + mmap_res = mmap(mmap_address, length, PROT_READ|PROT_WRITE, MAP_SHARED, fd, fileoffset); + if (mmap_res == MAP_FAILED) + return -1; + if (mmap_res != mmap_address) { + munmap(mmap_res, length); + errno = EBUSY; + return -1; + } + + tma.malloc = tma_shmem_malloc; + tma.dontfree = 1; + tma.data = (char *)mmap_res + sizeof(header); + err = hwloc__topology_dup(&new, topology, &tma); + if (err < 0) + return err; + assert((char*)new == (char*)mmap_address + sizeof(header)); + + assert((char *)mmap_res <= (char *)mmap_address + length); + + /* now refresh the new distances so that adopters can use them without refreshing the R/O shmem mapping */ + hwloc_internal_distances_refresh(new); + + /* topology is saved, release resources now */ + munmap(mmap_address, length); + hwloc_components_fini(); + + return 0; +} + +int +hwloc_shmem_topology_adopt(hwloc_topology_t *topologyp, + int fd, hwloc_uint64_t fileoffset, + void *mmap_address, size_t length, + unsigned long flags) +{ + hwloc_topology_t new, old; + struct hwloc_shmem_header header; + void *mmap_res; + int err; + + if (flags) { + errno = EINVAL; + return -1; + } + + err = lseek(fd, fileoffset, SEEK_SET); + if (err < 0) + return -1; + + err = read(fd, &header, sizeof(header)); + if (err != sizeof(header)) + return -1; + + if (header.header_version != HWLOC_SHMEM_HEADER_VERSION + || header.header_length != sizeof(header) + || header.mmap_address != (uintptr_t) mmap_address + || header.mmap_length != length) { + errno = EINVAL; + return -1; + } + + mmap_res = mmap(mmap_address, length, PROT_READ, MAP_SHARED, fd, fileoffset); + if (mmap_res == MAP_FAILED) + return -1; + if (mmap_res != mmap_address) { + errno = EBUSY; + goto out_with_mmap; + } + + old = (hwloc_topology_t)((char*)mmap_address + sizeof(header)); + if (hwloc_topology_abi_check(old) < 0) { + errno = EINVAL; + goto out_with_mmap; + } + + /* enforced by dup() inside shmem_topology_write() */ + assert(old->is_loaded); + assert(old->backends == NULL); + assert(old->get_pci_busid_cpuset_backend == NULL); + + hwloc_components_init(); + + /* duplicate the topology object so that we ca change use local binding_hooks + * (those are likely not mapped at the same location in both processes). + */ + new = malloc(sizeof(struct hwloc_topology)); + if (!new) + goto out_with_components; + memcpy(new, old, sizeof(*old)); + new->tma = NULL; + new->adopted_shmem_addr = mmap_address; + new->adopted_shmem_length = length; + new->topology_abi = HWLOC_TOPOLOGY_ABI; + /* setting binding hooks will touch support arrays, so duplicate them too. + * could avoid that by requesting a R/W mmap + */ + new->support.discovery = malloc(sizeof(*new->support.discovery)); + new->support.cpubind = malloc(sizeof(*new->support.cpubind)); + new->support.membind = malloc(sizeof(*new->support.membind)); + memcpy(new->support.discovery, old->support.discovery, sizeof(*new->support.discovery)); + memcpy(new->support.cpubind, old->support.cpubind, sizeof(*new->support.cpubind)); + memcpy(new->support.membind, old->support.membind, sizeof(*new->support.membind)); + hwloc_set_binding_hooks(new); + /* clear userdata callbacks pointing to the writer process' functions */ + new->userdata_export_cb = NULL; + new->userdata_import_cb = NULL; + +#ifndef HWLOC_DEBUG + if (getenv("HWLOC_DEBUG_CHECK")) +#endif + hwloc_topology_check(new); + + *topologyp = new; + return 0; + + out_with_components: + hwloc_components_fini(); + out_with_mmap: + munmap(mmap_res, length); + return -1; +} + +void +hwloc__topology_disadopt(hwloc_topology_t topology) +{ + hwloc_components_fini(); + munmap(topology->adopted_shmem_addr, topology->adopted_shmem_length); + free(topology->support.discovery); + free(topology->support.cpubind); + free(topology->support.membind); + free(topology); +} + +#else /* HWLOC_WIN_SYS */ + +int +hwloc_shmem_topology_get_length(hwloc_topology_t topology __hwloc_attribute_unused, + size_t *lengthp __hwloc_attribute_unused, + unsigned long flags __hwloc_attribute_unused) +{ + errno = ENOSYS; + return -1; +} + +int +hwloc_shmem_topology_write(hwloc_topology_t topology __hwloc_attribute_unused, + int fd __hwloc_attribute_unused, hwloc_uint64_t fileoffset __hwloc_attribute_unused, + void *mmap_address __hwloc_attribute_unused, size_t length __hwloc_attribute_unused, + unsigned long flags __hwloc_attribute_unused) +{ + errno = ENOSYS; + return -1; +} + +int +hwloc_shmem_topology_adopt(hwloc_topology_t *topologyp __hwloc_attribute_unused, + int fd __hwloc_attribute_unused, hwloc_uint64_t fileoffset __hwloc_attribute_unused, + void *mmap_address __hwloc_attribute_unused, size_t length __hwloc_attribute_unused, + unsigned long flags __hwloc_attribute_unused) +{ + errno = ENOSYS; + return -1; +} + +void +hwloc__topology_disadopt(hwloc_topology_t topology __hwloc_attribute_unused) +{ +} + +#endif /* HWLOC_WIN_SYS */ diff --git a/src/3rdparty/hwloc/src/static-components.h b/src/3rdparty/hwloc/src/static-components.h new file mode 100644 index 00000000..dac227a6 --- /dev/null +++ b/src/3rdparty/hwloc/src/static-components.h @@ -0,0 +1,15 @@ +HWLOC_DECLSPEC extern const struct hwloc_component hwloc_noos_component; +HWLOC_DECLSPEC extern const struct hwloc_component hwloc_xml_component; +HWLOC_DECLSPEC extern const struct hwloc_component hwloc_synthetic_component; +HWLOC_DECLSPEC extern const struct hwloc_component hwloc_xml_nolibxml_component; +HWLOC_DECLSPEC extern const struct hwloc_component hwloc_windows_component; +HWLOC_DECLSPEC extern const struct hwloc_component hwloc_x86_component; +static const struct hwloc_component * hwloc_static_components[] = { + &hwloc_noos_component, + &hwloc_xml_component, + &hwloc_synthetic_component, + &hwloc_xml_nolibxml_component, + &hwloc_windows_component, + &hwloc_x86_component, + NULL +}; diff --git a/src/3rdparty/hwloc/src/topology-noos.c b/src/3rdparty/hwloc/src/topology-noos.c new file mode 100644 index 00000000..77871eb1 --- /dev/null +++ b/src/3rdparty/hwloc/src/topology-noos.c @@ -0,0 +1,65 @@ +/* + * Copyright © 2009 CNRS + * Copyright © 2009-2017 Inria. All rights reserved. + * Copyright © 2009-2012 Université Bordeaux + * Copyright © 2009-2011 Cisco Systems, Inc. All rights reserved. + * See COPYING in top-level directory. + */ + +#include +#include +#include + +static int +hwloc_look_noos(struct hwloc_backend *backend) +{ + struct hwloc_topology *topology = backend->topology; + int nbprocs; + + if (topology->levels[0][0]->cpuset) + /* somebody discovered things */ + return -1; + + nbprocs = hwloc_fallback_nbprocessors(topology); + if (nbprocs >= 1) + topology->support.discovery->pu = 1; + else + nbprocs = 1; + + hwloc_alloc_root_sets(topology->levels[0][0]); + hwloc_setup_pu_level(topology, nbprocs); + hwloc_add_uname_info(topology, NULL); + return 0; +} + +static struct hwloc_backend * +hwloc_noos_component_instantiate(struct hwloc_disc_component *component, + const void *_data1 __hwloc_attribute_unused, + const void *_data2 __hwloc_attribute_unused, + const void *_data3 __hwloc_attribute_unused) +{ + struct hwloc_backend *backend; + backend = hwloc_backend_alloc(component); + if (!backend) + return NULL; + backend->discover = hwloc_look_noos; + return backend; +} + +static struct hwloc_disc_component hwloc_noos_disc_component = { + HWLOC_DISC_COMPONENT_TYPE_CPU, + "no_os", + HWLOC_DISC_COMPONENT_TYPE_GLOBAL, + hwloc_noos_component_instantiate, + 40, /* lower than native OS component, higher than globals */ + 1, + NULL +}; + +const struct hwloc_component hwloc_noos_component = { + HWLOC_COMPONENT_ABI, + NULL, NULL, + HWLOC_COMPONENT_TYPE_DISC, + 0, + &hwloc_noos_disc_component +}; diff --git a/src/3rdparty/hwloc/src/topology-synthetic.c b/src/3rdparty/hwloc/src/topology-synthetic.c new file mode 100644 index 00000000..1fe334d1 --- /dev/null +++ b/src/3rdparty/hwloc/src/topology-synthetic.c @@ -0,0 +1,1521 @@ +/* + * Copyright © 2009 CNRS + * Copyright © 2009-2019 Inria. All rights reserved. + * Copyright © 2009-2010 Université Bordeaux + * Copyright © 2009-2011 Cisco Systems, Inc. All rights reserved. + * See COPYING in top-level directory. + */ + +#include +#include +#include +#include +#include + +#include +#include +#ifdef HAVE_STRINGS_H +#include +#endif + +struct hwloc_synthetic_attr_s { + hwloc_obj_type_t type; + unsigned depth; /* For caches/groups */ + hwloc_obj_cache_type_t cachetype; /* For caches */ + hwloc_uint64_t memorysize; /* For caches/memory */ +}; + +struct hwloc_synthetic_indexes_s { + /* the indexes= attribute before parsing */ + const char *string; + unsigned long string_length; + /* the array of explicit indexes after parsing */ + unsigned *array; + + /* used while filling the topology */ + unsigned next; /* id of the next object for that level */ +}; + +struct hwloc_synthetic_level_data_s { + unsigned arity; + unsigned long totalwidth; + + struct hwloc_synthetic_attr_s attr; + struct hwloc_synthetic_indexes_s indexes; + + struct hwloc_synthetic_attached_s { + struct hwloc_synthetic_attr_s attr; + + struct hwloc_synthetic_attached_s *next; + } *attached; +}; + +struct hwloc_synthetic_backend_data_s { + /* synthetic backend parameters */ + char *string; + + unsigned long numa_attached_nr; + struct hwloc_synthetic_indexes_s numa_attached_indexes; + +#define HWLOC_SYNTHETIC_MAX_DEPTH 128 + struct hwloc_synthetic_level_data_s level[HWLOC_SYNTHETIC_MAX_DEPTH]; +}; + +struct hwloc_synthetic_intlv_loop_s { + unsigned step; + unsigned nb; + unsigned level_depth; +}; + +static void +hwloc_synthetic_process_indexes(struct hwloc_synthetic_backend_data_s *data, + struct hwloc_synthetic_indexes_s *indexes, + unsigned long total, + int verbose) +{ + const char *attr = indexes->string; + unsigned long length = indexes->string_length; + unsigned *array = NULL; + size_t i; + + if (!attr) + return; + + array = calloc(total, sizeof(*array)); + if (!array) { + if (verbose) + fprintf(stderr, "Failed to allocate synthetic index array of size %lu\n", total); + goto out; + } + + i = strspn(attr, "0123456789,"); + if (i == length) { + /* explicit array of indexes */ + + for(i=0; iarray = array; + + } else { + /* interleaving */ + unsigned nr_loops = 1, cur_loop; + unsigned minstep = total; + unsigned long nbs = 1; + unsigned j, mul; + const char *tmp; + + tmp = attr; + while (tmp) { + tmp = strchr(tmp, ':'); + if (!tmp || tmp >= attr+length) + break; + nr_loops++; + tmp++; + } + + { + /* nr_loops colon-separated fields, but we may need one more at the end */ + HWLOC_VLA(struct hwloc_synthetic_intlv_loop_s, loops, nr_loops+1); + + if (*attr >= '0' && *attr <= '9') { + /* interleaving as x*y:z*t:... */ + unsigned step, nb; + + tmp = attr; + cur_loop = 0; + while (tmp) { + char *tmp2, *tmp3; + step = (unsigned) strtol(tmp, &tmp2, 0); + if (tmp2 == tmp || *tmp2 != '*') { + if (verbose) + fprintf(stderr, "Failed to read synthetic index interleaving loop '%s' without number before '*'\n", tmp); + goto out_with_array; + } + if (!step) { + if (verbose) + fprintf(stderr, "Invalid interleaving loop with step 0 at '%s'\n", tmp); + goto out_with_array; + } + tmp2++; + nb = (unsigned) strtol(tmp2, &tmp3, 0); + if (tmp3 == tmp2 || (*tmp3 && *tmp3 != ':' && *tmp3 != ')' && *tmp3 != ' ')) { + if (verbose) + fprintf(stderr, "Failed to read synthetic index interleaving loop '%s' without number between '*' and ':'\n", tmp); + goto out_with_array; + } + if (!nb) { + if (verbose) + fprintf(stderr, "Invalid interleaving loop with number 0 at '%s'\n", tmp2); + goto out_with_array; + } + loops[cur_loop].step = step; + loops[cur_loop].nb = nb; + if (step < minstep) + minstep = step; + nbs *= nb; + cur_loop++; + if (*tmp3 == ')' || *tmp3 == ' ') + break; + tmp = (const char*) (tmp3+1); + } + + } else { + /* interleaving as type1:type2:... */ + hwloc_obj_type_t type; + union hwloc_obj_attr_u attrs; + int err; + + /* find level depths for each interleaving loop */ + tmp = attr; + cur_loop = 0; + while (tmp) { + err = hwloc_type_sscanf(tmp, &type, &attrs, sizeof(attrs)); + if (err < 0) { + if (verbose) + fprintf(stderr, "Failed to read synthetic index interleaving loop type '%s'\n", tmp); + goto out_with_array; + } + if (type == HWLOC_OBJ_MISC || type == HWLOC_OBJ_BRIDGE || type == HWLOC_OBJ_PCI_DEVICE || type == HWLOC_OBJ_OS_DEVICE) { + if (verbose) + fprintf(stderr, "Misc object type disallowed in synthetic index interleaving loop type '%s'\n", tmp); + goto out_with_array; + } + for(i=0; ; i++) { + if (!data->level[i].arity) { + loops[cur_loop].level_depth = (unsigned)-1; + break; + } + if (type != data->level[i].attr.type) + continue; + if (type == HWLOC_OBJ_GROUP + && attrs.group.depth != (unsigned) -1 + && attrs.group.depth != data->level[i].attr.depth) + continue; + loops[cur_loop].level_depth = (unsigned)i; + break; + } + if (loops[cur_loop].level_depth == (unsigned)-1) { + if (verbose) + fprintf(stderr, "Failed to find level for synthetic index interleaving loop type '%s'\n", + tmp); + goto out_with_array; + } + tmp = strchr(tmp, ':'); + if (!tmp || tmp > attr+length) + break; + tmp++; + cur_loop++; + } + + /* compute actual loop step/nb */ + for(cur_loop=0; cur_loop prevdepth) + prevdepth = loops[i].level_depth; + } + step = total / data->level[mydepth].totalwidth; /* number of objects below us */ + nb = data->level[mydepth].totalwidth / data->level[prevdepth].totalwidth; /* number of us within parent */ + + loops[cur_loop].step = step; + loops[cur_loop].nb = nb; + assert(nb); + assert(step); + if (step < minstep) + minstep = step; + nbs *= nb; + } + } + assert(nbs); + + if (nbs != total) { + /* one loop of total/nbs steps is missing, add it if it's just the smallest one */ + if (minstep == total/nbs) { + loops[nr_loops].step = 1; + loops[nr_loops].nb = total/nbs; + nr_loops++; + } else { + if (verbose) + fprintf(stderr, "Invalid index interleaving total width %lu instead of %lu\n", nbs, total); + goto out_with_array; + } + } + + /* generate the array of indexes */ + mul = 1; + for(i=0; i= total) { + if (verbose) + fprintf(stderr, "Invalid index interleaving generates out-of-range index %u\n", array[j]); + goto out_with_array; + } + if (!array[j] && j) { + if (verbose) + fprintf(stderr, "Invalid index interleaving generates duplicate index values\n"); + goto out_with_array; + } + } + + indexes->array = array; + } + } + + return; + + out_with_array: + free(array); + out: + return; +} + +static hwloc_uint64_t +hwloc_synthetic_parse_memory_attr(const char *attr, const char **endp) +{ + const char *endptr; + hwloc_uint64_t size; + size = strtoull(attr, (char **) &endptr, 0); + if (!hwloc_strncasecmp(endptr, "TB", 2)) { + size <<= 40; + endptr += 2; + } else if (!hwloc_strncasecmp(endptr, "GB", 2)) { + size <<= 30; + endptr += 2; + } else if (!hwloc_strncasecmp(endptr, "MB", 2)) { + size <<= 20; + endptr += 2; + } else if (!hwloc_strncasecmp(endptr, "kB", 2)) { + size <<= 10; + endptr += 2; + } + *endp = endptr; + return size; +} + +static int +hwloc_synthetic_parse_attrs(const char *attrs, const char **next_posp, + struct hwloc_synthetic_attr_s *sattr, + struct hwloc_synthetic_indexes_s *sind, + int verbose) +{ + hwloc_obj_type_t type = sattr->type; + const char *next_pos; + hwloc_uint64_t memorysize = 0; + const char *index_string = NULL; + size_t index_string_length = 0; + + next_pos = (const char *) strchr(attrs, ')'); + if (!next_pos) { + if (verbose) + fprintf(stderr, "Missing attribute closing bracket in synthetic string doesn't have a number of objects at '%s'\n", attrs); + errno = EINVAL; + return -1; + } + + while (')' != *attrs) { + int iscache = hwloc__obj_type_is_cache(type); + + if (iscache && !strncmp("size=", attrs, 5)) { + memorysize = hwloc_synthetic_parse_memory_attr(attrs+5, &attrs); + + } else if (!iscache && !strncmp("memory=", attrs, 7)) { + memorysize = hwloc_synthetic_parse_memory_attr(attrs+7, &attrs); + + } else if (!strncmp("indexes=", attrs, 8)) { + index_string = attrs+8; + attrs += 8; + index_string_length = strcspn(attrs, " )"); + attrs += index_string_length; + + } else { + if (verbose) + fprintf(stderr, "Unknown attribute at '%s'\n", attrs); + errno = EINVAL; + return -1; + } + + if (' ' == *attrs) + attrs++; + else if (')' != *attrs) { + if (verbose) + fprintf(stderr, "Missing parameter separator at '%s'\n", attrs); + errno = EINVAL; + return -1; + } + } + + sattr->memorysize = memorysize; + + if (index_string) { + if (sind->string && verbose) + fprintf(stderr, "Overwriting duplicate indexes attribute with last occurence\n"); + sind->string = index_string; + sind->string_length = (unsigned long)index_string_length; + } + + *next_posp = next_pos+1; + return 0; +} + +/* frees level until arity = 0 */ +static void +hwloc_synthetic_free_levels(struct hwloc_synthetic_backend_data_s *data) +{ + unsigned i; + for(i=0; ilevel[i]; + struct hwloc_synthetic_attached_s **pprev = &curlevel->attached; + while (*pprev) { + struct hwloc_synthetic_attached_s *cur = *pprev; + *pprev = cur->next; + free(cur); + } + free(curlevel->indexes.array); + if (!curlevel->arity) + break; + } + free(data->numa_attached_indexes.array); +} + +/* Read from description a series of integers describing a symmetrical + topology and update the hwloc_synthetic_backend_data_s accordingly. On + success, return zero. */ +static int +hwloc_backend_synthetic_init(struct hwloc_synthetic_backend_data_s *data, + const char *description) +{ + const char *pos, *next_pos; + unsigned long item, count; + unsigned i; + int type_count[HWLOC_OBJ_TYPE_MAX]; + unsigned unset; + int verbose = 0; + const char *env = getenv("HWLOC_SYNTHETIC_VERBOSE"); + int err; + unsigned long totalarity = 1; + + if (env) + verbose = atoi(env); + + data->numa_attached_nr = 0; + data->numa_attached_indexes.array = NULL; + + /* default values before we add root attributes */ + data->level[0].totalwidth = 1; + data->level[0].attr.type = HWLOC_OBJ_MACHINE; + data->level[0].indexes.string = NULL; + data->level[0].indexes.array = NULL; + data->level[0].attr.memorysize = 0; + data->level[0].attached = NULL; + type_count[HWLOC_OBJ_MACHINE] = 1; + if (*description == '(') { + err = hwloc_synthetic_parse_attrs(description+1, &description, &data->level[0].attr, &data->level[0].indexes, verbose); + if (err < 0) + return err; + } + + data->numa_attached_indexes.string = NULL; + data->numa_attached_indexes.array = NULL; + + for (pos = description, count = 1; *pos; pos = next_pos) { + hwloc_obj_type_t type = HWLOC_OBJ_TYPE_NONE; + union hwloc_obj_attr_u attrs; + + /* initialize parent arity to 0 so that the levels are not infinite */ + data->level[count-1].arity = 0; + + while (*pos == ' ') + pos++; + + if (!*pos) + break; + + if (*pos == '[') { + /* attached */ + struct hwloc_synthetic_attached_s *attached, **pprev; + char *attr; + + pos++; + + if (hwloc_type_sscanf(pos, &type, &attrs, sizeof(attrs)) < 0) { + if (verbose) + fprintf(stderr, "Synthetic string with unknown attached object type at '%s'\n", pos); + errno = EINVAL; + goto error; + } + if (type != HWLOC_OBJ_NUMANODE) { + if (verbose) + fprintf(stderr, "Synthetic string with disallowed attached object type at '%s'\n", pos); + errno = EINVAL; + goto error; + } + data->numa_attached_nr += data->level[count-1].totalwidth; + + attached = malloc(sizeof(*attached)); + if (attached) { + attached->attr.type = type; + attached->attr.memorysize = 0; + /* attached->attr.depth and .cachetype unused */ + attached->next = NULL; + pprev = &data->level[count-1].attached; + while (*pprev) + pprev = &((*pprev)->next); + *pprev = attached; + } + + next_pos = strchr(pos, ']'); + if (!next_pos) { + if (verbose) + fprintf(stderr,"Synthetic string doesn't have a closing `]' after attached object type at '%s'\n", pos); + errno = EINVAL; + goto error; + } + + attr = strchr(pos, '('); + if (attr && attr < next_pos && attached) { + const char *dummy; + err = hwloc_synthetic_parse_attrs(attr+1, &dummy, &attached->attr, &data->numa_attached_indexes, verbose); + if (err < 0) + goto error; + } + + next_pos++; + continue; + } + + /* normal level */ + + /* reset defaults */ + data->level[count].indexes.string = NULL; + data->level[count].indexes.array = NULL; + data->level[count].attached = NULL; + + if (*pos < '0' || *pos > '9') { + if (hwloc_type_sscanf(pos, &type, &attrs, sizeof(attrs)) < 0) { + if (!strncmp(pos, "Die", 3) || !strncmp(pos, "Tile", 4) || !strncmp(pos, "Module", 6)) { + type = HWLOC_OBJ_GROUP; + } else { + /* FIXME: allow generic "Cache" string? would require to deal with possibly duplicate cache levels */ + if (verbose) + fprintf(stderr, "Synthetic string with unknown object type at '%s'\n", pos); + errno = EINVAL; + goto error; + } + } + if (type == HWLOC_OBJ_MACHINE || type == HWLOC_OBJ_MISC || type == HWLOC_OBJ_BRIDGE || type == HWLOC_OBJ_PCI_DEVICE || type == HWLOC_OBJ_OS_DEVICE) { + if (verbose) + fprintf(stderr, "Synthetic string with disallowed object type at '%s'\n", pos); + errno = EINVAL; + goto error; + } + + next_pos = strchr(pos, ':'); + if (!next_pos) { + if (verbose) + fprintf(stderr,"Synthetic string doesn't have a `:' after object type at '%s'\n", pos); + errno = EINVAL; + goto error; + } + pos = next_pos + 1; + } + + data->level[count].attr.type = type; + data->level[count].attr.depth = (unsigned) -1; + data->level[count].attr.cachetype = (hwloc_obj_cache_type_t) -1; + if (hwloc__obj_type_is_cache(type)) { + /* these are always initialized */ + data->level[count].attr.depth = attrs.cache.depth; + data->level[count].attr.cachetype = attrs.cache.type; + } else if (type == HWLOC_OBJ_GROUP) { + /* could be -1 but will be set below */ + data->level[count].attr.depth = attrs.group.depth; + } + + /* number of normal children */ + item = strtoul(pos, (char **)&next_pos, 0); + if (next_pos == pos) { + if (verbose) + fprintf(stderr,"Synthetic string doesn't have a number of objects at '%s'\n", pos); + errno = EINVAL; + goto error; + } + if (!item) { + if (verbose) + fprintf(stderr,"Synthetic string with disallow 0 number of objects at '%s'\n", pos); + errno = EINVAL; + goto error; + } + + totalarity *= item; + data->level[count].totalwidth = totalarity; + data->level[count].indexes.string = NULL; + data->level[count].indexes.array = NULL; + data->level[count].attr.memorysize = 0; + if (*next_pos == '(') { + err = hwloc_synthetic_parse_attrs(next_pos+1, &next_pos, &data->level[count].attr, &data->level[count].indexes, verbose); + if (err < 0) + goto error; + } + + if (count + 1 >= HWLOC_SYNTHETIC_MAX_DEPTH) { + if (verbose) + fprintf(stderr,"Too many synthetic levels, max %d\n", HWLOC_SYNTHETIC_MAX_DEPTH); + errno = EINVAL; + goto error; + } + if (item > UINT_MAX) { + if (verbose) + fprintf(stderr,"Too big arity, max %u\n", UINT_MAX); + errno = EINVAL; + goto error; + } + + data->level[count-1].arity = (unsigned)item; + count++; + } + + if (data->level[count-1].attr.type != HWLOC_OBJ_TYPE_NONE && data->level[count-1].attr.type != HWLOC_OBJ_PU) { + if (verbose) + fprintf(stderr, "Synthetic string cannot use non-PU type for last level\n"); + errno = EINVAL; + return -1; + } + data->level[count-1].attr.type = HWLOC_OBJ_PU; + + for(i=HWLOC_OBJ_TYPE_MIN; i0; i--) { + hwloc_obj_type_t type = data->level[i].attr.type; + if (type != HWLOC_OBJ_TYPE_NONE) { + type_count[type]++; + } + } + + /* sanity checks */ + if (!type_count[HWLOC_OBJ_PU]) { + if (verbose) + fprintf(stderr, "Synthetic string missing ending number of PUs\n"); + errno = EINVAL; + return -1; + } else if (type_count[HWLOC_OBJ_PU] > 1) { + if (verbose) + fprintf(stderr, "Synthetic string cannot have several PU levels\n"); + errno = EINVAL; + return -1; + } + if (type_count[HWLOC_OBJ_PACKAGE] > 1) { + if (verbose) + fprintf(stderr, "Synthetic string cannot have several package levels\n"); + errno = EINVAL; + return -1; + } + if (type_count[HWLOC_OBJ_NUMANODE] > 1) { + if (verbose) + fprintf(stderr, "Synthetic string cannot have several NUMA node levels\n"); + errno = EINVAL; + return -1; + } + if (type_count[HWLOC_OBJ_NUMANODE] && data->numa_attached_nr) { + if (verbose) + fprintf(stderr,"Synthetic string cannot have NUMA nodes both as a level and attached\n"); + errno = EINVAL; + return -1; + } + if (type_count[HWLOC_OBJ_CORE] > 1) { + if (verbose) + fprintf(stderr, "Synthetic string cannot have several core levels\n"); + errno = EINVAL; + return -1; + } + + /* deal with missing intermediate levels */ + unset = 0; + for(i=1; ilevel[i].attr.type == HWLOC_OBJ_TYPE_NONE) + unset++; + } + if (unset && unset != count-2) { + if (verbose) + fprintf(stderr, "Synthetic string cannot mix unspecified and specified types for levels\n"); + errno = EINVAL; + return -1; + } + if (unset) { + /* we want in priority: numa, package, core, up to 3 caches, groups */ + unsigned _count = count; + unsigned neednuma = 0; + unsigned needpack = 0; + unsigned needcore = 0; + unsigned needcaches = 0; + unsigned needgroups = 0; + /* 2 levels for machine and PU */ + _count -= 2; + + neednuma = (_count >= 1 && !data->numa_attached_nr); + _count -= neednuma; + + needpack = (_count >= 1); + _count -= needpack; + + needcore = (_count >= 1); + _count -= needcore; + + needcaches = (_count > 4 ? 4 : _count); + _count -= needcaches; + + needgroups = _count; + + /* we place them in order: groups, package, numa, caches, core */ + for(i = 0; i < needgroups; i++) { + unsigned depth = 1 + i; + data->level[depth].attr.type = HWLOC_OBJ_GROUP; + type_count[HWLOC_OBJ_GROUP]++; + } + if (needpack) { + unsigned depth = 1 + needgroups; + data->level[depth].attr.type = HWLOC_OBJ_PACKAGE; + type_count[HWLOC_OBJ_PACKAGE] = 1; + } + if (neednuma) { + unsigned depth = 1 + needgroups + needpack; + data->level[depth].attr.type = HWLOC_OBJ_NUMANODE; + type_count[HWLOC_OBJ_NUMANODE] = 1; + } + if (needcaches) { + /* priority: l2, l1, l3, l1i */ + /* order: l3, l2, l1, l1i */ + unsigned l3depth = 1 + needgroups + needpack + neednuma; + unsigned l2depth = l3depth + (needcaches >= 3); + unsigned l1depth = l2depth + 1; + unsigned l1idepth = l1depth + 1; + if (needcaches >= 3) { + data->level[l3depth].attr.type = HWLOC_OBJ_L3CACHE; + data->level[l3depth].attr.depth = 3; + data->level[l3depth].attr.cachetype = HWLOC_OBJ_CACHE_UNIFIED; + type_count[HWLOC_OBJ_L3CACHE] = 1; + } + data->level[l2depth].attr.type = HWLOC_OBJ_L2CACHE; + data->level[l2depth].attr.depth = 2; + data->level[l2depth].attr.cachetype = HWLOC_OBJ_CACHE_UNIFIED; + type_count[HWLOC_OBJ_L2CACHE] = 1; + if (needcaches >= 2) { + data->level[l1depth].attr.type = HWLOC_OBJ_L1CACHE; + data->level[l1depth].attr.depth = 1; + data->level[l1depth].attr.cachetype = HWLOC_OBJ_CACHE_DATA; + type_count[HWLOC_OBJ_L1CACHE] = 1; + } + if (needcaches >= 4) { + data->level[l1idepth].attr.type = HWLOC_OBJ_L1ICACHE; + data->level[l1idepth].attr.depth = 1; + data->level[l1idepth].attr.cachetype = HWLOC_OBJ_CACHE_INSTRUCTION; + type_count[HWLOC_OBJ_L1ICACHE] = 1; + } + } + if (needcore) { + unsigned depth = 1 + needgroups + needpack + neednuma + needcaches; + data->level[depth].attr.type = HWLOC_OBJ_CORE; + type_count[HWLOC_OBJ_CORE] = 1; + } + } + + /* enforce a NUMA level */ + if (!type_count[HWLOC_OBJ_NUMANODE] && !data->numa_attached_nr) { + /* insert a NUMA level below the automatic machine root */ + if (verbose) + fprintf(stderr, "Inserting a NUMA level with a single object at depth 1\n"); + /* move existing levels by one */ + memmove(&data->level[2], &data->level[1], count*sizeof(struct hwloc_synthetic_level_data_s)); + data->level[1].attr.type = HWLOC_OBJ_NUMANODE; + data->level[1].indexes.string = NULL; + data->level[1].indexes.array = NULL; + data->level[1].attr.memorysize = 0; + data->level[1].totalwidth = data->level[0].totalwidth; + /* update arity to insert a single NUMA node per parent */ + data->level[1].arity = data->level[0].arity; + data->level[0].arity = 1; + count++; + } + + for (i=0; ilevel[i]; + hwloc_obj_type_t type = curlevel->attr.type; + + if (type == HWLOC_OBJ_GROUP) { + if (curlevel->attr.depth == (unsigned)-1) + curlevel->attr.depth = type_count[HWLOC_OBJ_GROUP]--; + + } else if (hwloc__obj_type_is_cache(type)) { + if (!curlevel->attr.memorysize) { + if (1 == curlevel->attr.depth) + /* 32Kb in L1 */ + curlevel->attr.memorysize = 32*1024; + else + /* *4 at each level, starting from 1MB for L2, unified */ + curlevel->attr.memorysize = 256ULL*1024 << (2*curlevel->attr.depth); + } + + } else if (type == HWLOC_OBJ_NUMANODE && !curlevel->attr.memorysize) { + /* 1GB in memory nodes. */ + curlevel->attr.memorysize = 1024*1024*1024; + } + + hwloc_synthetic_process_indexes(data, &data->level[i].indexes, data->level[i].totalwidth, verbose); + } + + hwloc_synthetic_process_indexes(data, &data->numa_attached_indexes, data->numa_attached_nr, verbose); + + data->string = strdup(description); + data->level[count-1].arity = 0; + return 0; + + error: + hwloc_synthetic_free_levels(data); + return -1; +} + +static void +hwloc_synthetic_set_attr(struct hwloc_synthetic_attr_s *sattr, + hwloc_obj_t obj) +{ + switch (obj->type) { + case HWLOC_OBJ_GROUP: + obj->attr->group.kind = HWLOC_GROUP_KIND_SYNTHETIC; + obj->attr->group.subkind = sattr->depth-1; + break; + case HWLOC_OBJ_MACHINE: + break; + case HWLOC_OBJ_NUMANODE: + obj->attr->numanode.local_memory = sattr->memorysize; + obj->attr->numanode.page_types_len = 1; + obj->attr->numanode.page_types = malloc(sizeof(*obj->attr->numanode.page_types)); + memset(obj->attr->numanode.page_types, 0, sizeof(*obj->attr->numanode.page_types)); + obj->attr->numanode.page_types[0].size = 4096; + obj->attr->numanode.page_types[0].count = sattr->memorysize / 4096; + break; + case HWLOC_OBJ_PACKAGE: + break; + case HWLOC_OBJ_L1CACHE: + case HWLOC_OBJ_L2CACHE: + case HWLOC_OBJ_L3CACHE: + case HWLOC_OBJ_L4CACHE: + case HWLOC_OBJ_L5CACHE: + case HWLOC_OBJ_L1ICACHE: + case HWLOC_OBJ_L2ICACHE: + case HWLOC_OBJ_L3ICACHE: + obj->attr->cache.depth = sattr->depth; + obj->attr->cache.linesize = 64; + obj->attr->cache.type = sattr->cachetype; + obj->attr->cache.size = sattr->memorysize; + break; + case HWLOC_OBJ_CORE: + break; + case HWLOC_OBJ_PU: + break; + default: + /* Should never happen */ + assert(0); + break; + } +} + +static unsigned +hwloc_synthetic_next_index(struct hwloc_synthetic_indexes_s *indexes, hwloc_obj_type_t type) +{ + unsigned os_index = indexes->next++; + + if (indexes->array) + os_index = indexes->array[os_index]; + else if (hwloc__obj_type_is_cache(type) || type == HWLOC_OBJ_GROUP) + /* don't enforce useless os_indexes for Caches and Groups */ + os_index = HWLOC_UNKNOWN_INDEX; + + return os_index; +} + +static void +hwloc_synthetic_insert_attached(struct hwloc_topology *topology, + struct hwloc_synthetic_backend_data_s *data, + struct hwloc_synthetic_attached_s *attached, + hwloc_bitmap_t set) +{ + hwloc_obj_t child; + unsigned attached_os_index; + + if (!attached) + return; + + assert(attached->attr.type == HWLOC_OBJ_NUMANODE); + + attached_os_index = hwloc_synthetic_next_index(&data->numa_attached_indexes, HWLOC_OBJ_NUMANODE); + + child = hwloc_alloc_setup_object(topology, attached->attr.type, attached_os_index); + child->cpuset = hwloc_bitmap_dup(set); + + child->nodeset = hwloc_bitmap_alloc(); + hwloc_bitmap_set(child->nodeset, attached_os_index); + + hwloc_synthetic_set_attr(&attached->attr, child); + + hwloc_insert_object_by_cpuset(topology, child); + + hwloc_synthetic_insert_attached(topology, data, attached->next, set); +} + +/* + * Recursively build objects whose cpu start at first_cpu + * - level gives where to look in the type, arity and id arrays + * - the id array is used as a variable to get unique IDs for a given level. + * - generated memory should be added to *memory_kB. + * - generated cpus should be added to parent_cpuset. + * - next cpu number to be used should be returned. + */ +static void +hwloc__look_synthetic(struct hwloc_topology *topology, + struct hwloc_synthetic_backend_data_s *data, + int level, + hwloc_bitmap_t parent_cpuset) +{ + hwloc_obj_t obj; + unsigned i; + struct hwloc_synthetic_level_data_s *curlevel = &data->level[level]; + hwloc_obj_type_t type = curlevel->attr.type; + hwloc_bitmap_t set; + unsigned os_index; + + assert(hwloc__obj_type_is_normal(type) || type == HWLOC_OBJ_NUMANODE); + assert(type != HWLOC_OBJ_MACHINE); + + os_index = hwloc_synthetic_next_index(&curlevel->indexes, type); + + set = hwloc_bitmap_alloc(); + if (!curlevel->arity) { + hwloc_bitmap_set(set, os_index); + } else { + for (i = 0; i < curlevel->arity; i++) + hwloc__look_synthetic(topology, data, level + 1, set); + } + + hwloc_bitmap_or(parent_cpuset, parent_cpuset, set); + + if (hwloc_filter_check_keep_object_type(topology, type)) { + obj = hwloc_alloc_setup_object(topology, type, os_index); + obj->cpuset = hwloc_bitmap_dup(set); + + if (type == HWLOC_OBJ_NUMANODE) { + obj->nodeset = hwloc_bitmap_alloc(); + hwloc_bitmap_set(obj->nodeset, os_index); + } + + hwloc_synthetic_set_attr(&curlevel->attr, obj); + + hwloc_insert_object_by_cpuset(topology, obj); + } + + hwloc_synthetic_insert_attached(topology, data, curlevel->attached, set); + + hwloc_bitmap_free(set); +} + +static int +hwloc_look_synthetic(struct hwloc_backend *backend) +{ + struct hwloc_topology *topology = backend->topology; + struct hwloc_synthetic_backend_data_s *data = backend->private_data; + hwloc_bitmap_t cpuset = hwloc_bitmap_alloc(); + unsigned i; + + assert(!topology->levels[0][0]->cpuset); + + hwloc_alloc_root_sets(topology->levels[0][0]); + + topology->support.discovery->pu = 1; + topology->support.discovery->numa = 1; /* we add a single NUMA node if none is given */ + topology->support.discovery->numa_memory = 1; /* specified or default size */ + + /* start with os_index 0 for each level */ + for (i = 0; data->level[i].arity > 0; i++) + data->level[i].indexes.next = 0; + data->numa_attached_indexes.next = 0; + /* ... including the last one */ + data->level[i].indexes.next = 0; + + /* update first level type according to the synthetic type array */ + topology->levels[0][0]->type = data->level[0].attr.type; + hwloc_synthetic_set_attr(&data->level[0].attr, topology->levels[0][0]); + + for (i = 0; i < data->level[0].arity; i++) + hwloc__look_synthetic(topology, data, 1, cpuset); + + hwloc_synthetic_insert_attached(topology, data, data->level[0].attached, cpuset); + + hwloc_bitmap_free(cpuset); + + hwloc_obj_add_info(topology->levels[0][0], "Backend", "Synthetic"); + hwloc_obj_add_info(topology->levels[0][0], "SyntheticDescription", data->string); + return 0; +} + +static void +hwloc_synthetic_backend_disable(struct hwloc_backend *backend) +{ + struct hwloc_synthetic_backend_data_s *data = backend->private_data; + hwloc_synthetic_free_levels(data); + free(data->string); + free(data); +} + +static struct hwloc_backend * +hwloc_synthetic_component_instantiate(struct hwloc_disc_component *component, + const void *_data1, + const void *_data2 __hwloc_attribute_unused, + const void *_data3 __hwloc_attribute_unused) +{ + struct hwloc_backend *backend; + struct hwloc_synthetic_backend_data_s *data; + int err; + + if (!_data1) { + const char *env = getenv("HWLOC_SYNTHETIC"); + if (env) { + /* 'synthetic' was given in HWLOC_COMPONENTS without a description */ + _data1 = env; + } else { + errno = EINVAL; + goto out; + } + } + + backend = hwloc_backend_alloc(component); + if (!backend) + goto out; + + data = malloc(sizeof(*data)); + if (!data) { + errno = ENOMEM; + goto out_with_backend; + } + + err = hwloc_backend_synthetic_init(data, (const char *) _data1); + if (err < 0) + goto out_with_data; + + backend->private_data = data; + backend->discover = hwloc_look_synthetic; + backend->disable = hwloc_synthetic_backend_disable; + backend->is_thissystem = 0; + + return backend; + + out_with_data: + free(data); + out_with_backend: + free(backend); + out: + return NULL; +} + +static struct hwloc_disc_component hwloc_synthetic_disc_component = { + HWLOC_DISC_COMPONENT_TYPE_GLOBAL, + "synthetic", + ~0, + hwloc_synthetic_component_instantiate, + 30, + 1, + NULL +}; + +const struct hwloc_component hwloc_synthetic_component = { + HWLOC_COMPONENT_ABI, + NULL, NULL, + HWLOC_COMPONENT_TYPE_DISC, + 0, + &hwloc_synthetic_disc_component +}; + +static __hwloc_inline int +hwloc__export_synthetic_update_status(int *ret, char **tmp, ssize_t *tmplen, int res) +{ + if (res < 0) + return -1; + *ret += res; + if (res >= *tmplen) + res = *tmplen>0 ? (int)(*tmplen) - 1 : 0; + *tmp += res; + *tmplen -= res; + return 0; +} + +static __hwloc_inline void +hwloc__export_synthetic_add_char(int *ret, char **tmp, ssize_t *tmplen, char c) +{ + if (*tmplen > 1) { + (*tmp)[0] = c; + (*tmp)[1] = '\0'; + (*tmp)++; + (*tmplen)--; + } + (*ret)++; +} + +static int +hwloc__export_synthetic_indexes(hwloc_obj_t *level, unsigned total, + char *buffer, size_t buflen) +{ + unsigned step = 1; + unsigned nr_loops = 0; + struct hwloc_synthetic_intlv_loop_s *loops = NULL, *tmploops; + hwloc_obj_t cur; + unsigned i, j; + ssize_t tmplen = buflen; + char *tmp = buffer; + int res, ret = 0; + + /* must start with 0 */ + if (level[0]->os_index) + goto exportall; + + while (step != total) { + /* must be a divider of the total */ + if (total % step) + goto exportall; + + /* look for os_index == step */ + for(i=1; ios_index == step) + break; + if (i == total) + goto exportall; + for(j=2; jos_index != step*j) + break; + + nr_loops++; + tmploops = realloc(loops, nr_loops*sizeof(*loops)); + if (!tmploops) + goto exportall; + loops = tmploops; + loops[nr_loops-1].step = i; + loops[nr_loops-1].nb = j; + step *= j; + } + + /* check this interleaving */ + for(i=0; ios_index != ind) + goto exportall; + } + + /* success, print it */ + for(j=0; jos_index, + cur->next_cousin ? "," : ")"); + if (hwloc__export_synthetic_update_status(&ret, &tmp, &tmplen, res) < 0) + return -1; + cur = cur->next_cousin; + } + return ret; +} + +static int +hwloc__export_synthetic_obj_attr(struct hwloc_topology * topology, + hwloc_obj_t obj, + char *buffer, size_t buflen) +{ + const char * separator = " "; + const char * prefix = "("; + char cachesize[64] = ""; + char memsize[64] = ""; + int needindexes = 0; + + if (hwloc__obj_type_is_cache(obj->type) && obj->attr->cache.size) { + snprintf(cachesize, sizeof(cachesize), "%ssize=%llu", + prefix, (unsigned long long) obj->attr->cache.size); + prefix = separator; + } + if (obj->type == HWLOC_OBJ_NUMANODE && obj->attr->numanode.local_memory) { + snprintf(memsize, sizeof(memsize), "%smemory=%llu", + prefix, (unsigned long long) obj->attr->numanode.local_memory); + prefix = separator; + } + if (!obj->logical_index /* only display indexes once per level (not for non-first NUMA children, etc.) */ + && (obj->type == HWLOC_OBJ_PU || obj->type == HWLOC_OBJ_NUMANODE)) { + hwloc_obj_t cur = obj; + while (cur) { + if (cur->os_index != cur->logical_index) { + needindexes = 1; + break; + } + cur = cur->next_cousin; + } + } + if (*cachesize || *memsize || needindexes) { + ssize_t tmplen = buflen; + char *tmp = buffer; + int res, ret = 0; + + res = hwloc_snprintf(tmp, tmplen, "%s%s%s", cachesize, memsize, needindexes ? "" : ")"); + if (hwloc__export_synthetic_update_status(&ret, &tmp, &tmplen, res) < 0) + return -1; + + if (needindexes) { + unsigned total; + hwloc_obj_t *level; + + if (obj->depth < 0) { + assert(obj->depth == HWLOC_TYPE_DEPTH_NUMANODE); + total = topology->slevels[HWLOC_SLEVEL_NUMANODE].nbobjs; + level = topology->slevels[HWLOC_SLEVEL_NUMANODE].objs; + } else { + total = topology->level_nbobjects[obj->depth]; + level = topology->levels[obj->depth]; + } + + res = hwloc_snprintf(tmp, tmplen, "%sindexes=", prefix); + if (hwloc__export_synthetic_update_status(&ret, &tmp, &tmplen, res) < 0) + return -1; + + res = hwloc__export_synthetic_indexes(level, total, tmp, tmplen); + if (hwloc__export_synthetic_update_status(&ret, &tmp, &tmplen, res) < 0) + return -1; + } + return ret; + } else { + return 0; + } +} + +static int +hwloc__export_synthetic_obj(struct hwloc_topology * topology, unsigned long flags, + hwloc_obj_t obj, unsigned arity, + char *buffer, size_t buflen) +{ + char aritys[12] = ""; + ssize_t tmplen = buflen; + char *tmp = buffer; + int res, ret = 0; + + /* :, except for root */ + if (arity != (unsigned)-1) + snprintf(aritys, sizeof(aritys), ":%u", arity); + if (hwloc__obj_type_is_cache(obj->type) + && (flags & HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_NO_EXTENDED_TYPES)) { + /* v1 uses generic "Cache" for non-extended type name */ + res = hwloc_snprintf(tmp, tmplen, "Cache%s", aritys); + + } else if (obj->type == HWLOC_OBJ_PACKAGE + && (flags & (HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_NO_EXTENDED_TYPES + |HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_V1))) { + /* if exporting to v1 or without extended-types, use all-v1-compatible Socket name */ + res = hwloc_snprintf(tmp, tmplen, "Socket%s", aritys); + + } else if (obj->type == HWLOC_OBJ_GROUP /* don't export group depth */ + || flags & HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_NO_EXTENDED_TYPES) { + res = hwloc_snprintf(tmp, tmplen, "%s%s", hwloc_obj_type_string(obj->type), aritys); + } else { + char types[64]; + hwloc_obj_type_snprintf(types, sizeof(types), obj, 1); + res = hwloc_snprintf(tmp, tmplen, "%s%s", types, aritys); + } + if (hwloc__export_synthetic_update_status(&ret, &tmp, &tmplen, res) < 0) + return -1; + + if (!(flags & HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_NO_ATTRS)) { + /* obj attributes */ + res = hwloc__export_synthetic_obj_attr(topology, obj, tmp, tmplen); + if (hwloc__export_synthetic_update_status(&ret, &tmp, &tmplen, res) < 0) + return -1; + } + + return ret; +} + +static int +hwloc__export_synthetic_memory_children(struct hwloc_topology * topology, unsigned long flags, + hwloc_obj_t parent, + char *buffer, size_t buflen, + int needprefix, int verbose) +{ + hwloc_obj_t mchild; + ssize_t tmplen = buflen; + char *tmp = buffer; + int res, ret = 0; + + mchild = parent->memory_first_child; + if (!mchild) + return 0; + + if (flags & HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_V1) { + /* v1: export a single NUMA child */ + if (parent->memory_arity > 1 || mchild->type != HWLOC_OBJ_NUMANODE) { + /* not supported */ + if (verbose) + fprintf(stderr, "Cannot export to synthetic v1 if multiple memory children are attached to the same location.\n"); + errno = EINVAL; + return -1; + } + + if (needprefix) + hwloc__export_synthetic_add_char(&ret, &tmp, &tmplen, ' '); + + res = hwloc__export_synthetic_obj(topology, flags, mchild, 1, tmp, tmplen); + if (hwloc__export_synthetic_update_status(&ret, &tmp, &tmplen, res) < 0) + return -1; + return ret; + } + + while (mchild) { + /* v2: export all NUMA children */ + + assert(mchild->type == HWLOC_OBJ_NUMANODE); /* only NUMA node memory children for now */ + + if (needprefix) + hwloc__export_synthetic_add_char(&ret, &tmp, &tmplen, ' '); + + hwloc__export_synthetic_add_char(&ret, &tmp, &tmplen, '['); + + res = hwloc__export_synthetic_obj(topology, flags, mchild, (unsigned)-1, tmp, tmplen); + if (hwloc__export_synthetic_update_status(&ret, &tmp, &tmplen, res) < 0) + return -1; + + hwloc__export_synthetic_add_char(&ret, &tmp, &tmplen, ']'); + + needprefix = 1; + mchild = mchild->next_sibling; + } + + return ret; +} + +static int +hwloc_check_memory_symmetric(struct hwloc_topology * topology) +{ + hwloc_bitmap_t remaining_nodes; + + remaining_nodes = hwloc_bitmap_dup(hwloc_get_root_obj(topology)->nodeset); + if (!remaining_nodes) + /* assume asymmetric */ + return -1; + + while (!hwloc_bitmap_iszero(remaining_nodes)) { + unsigned idx; + hwloc_obj_t node; + hwloc_obj_t first_parent; + unsigned i; + + idx = hwloc_bitmap_first(remaining_nodes); + node = hwloc_get_numanode_obj_by_os_index(topology, idx); + assert(node); + + first_parent = node->parent; + assert(hwloc__obj_type_is_normal(first_parent->type)); /* only depth-1 memory children for now */ + + /* check whether all object on parent's level have same number of NUMA children */ + for(i=0; idepth); i++) { + hwloc_obj_t parent, mchild; + + parent = hwloc_get_obj_by_depth(topology, first_parent->depth, i); + assert(parent); + + /* must have same memory arity */ + if (parent->memory_arity != first_parent->memory_arity) + goto out_with_bitmap; + + /* clear these NUMA children from remaining_nodes */ + mchild = parent->memory_first_child; + while (mchild) { + assert(mchild->type == HWLOC_OBJ_NUMANODE); /* only NUMA node memory children for now */ + hwloc_bitmap_clr(remaining_nodes, mchild->os_index); /* cannot use parent->nodeset, some normal children may have other NUMA nodes */ + mchild = mchild->next_sibling; + } + } + } + + hwloc_bitmap_free(remaining_nodes); + return 0; + + out_with_bitmap: + hwloc_bitmap_free(remaining_nodes); + return -1; +} + +int +hwloc_topology_export_synthetic(struct hwloc_topology * topology, + char *buffer, size_t buflen, + unsigned long flags) +{ + hwloc_obj_t obj = hwloc_get_root_obj(topology); + ssize_t tmplen = buflen; + char *tmp = buffer; + int res, ret = 0; + unsigned arity; + int needprefix = 0; + int verbose = 0; + const char *env = getenv("HWLOC_SYNTHETIC_VERBOSE"); + + if (env) + verbose = atoi(env); + + if (!topology->is_loaded) { + errno = EINVAL; + return -1; + } + + if (flags & ~(HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_NO_EXTENDED_TYPES + |HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_NO_ATTRS + |HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_V1 + |HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_IGNORE_MEMORY)) { + errno = EINVAL; + return -1; + } + + /* TODO: add a flag to ignore symmetric_subtree and I/Os. + * just assume things are symmetric with the left branches of the tree. + * but the number of objects per level may be wrong, what to do with OS index array in this case? + * only allow ignoring symmetric_subtree if the level width remains OK? + */ + + /* TODO: add a root object by default, with a prefix such as tree= + * so that we can backward-compatibly recognize whether there's a root or not. + * and add a flag to disable it. + */ + + /* TODO: flag to force all indexes, not only for PU and NUMA? */ + + if (!obj->symmetric_subtree) { + if (verbose) + fprintf(stderr, "Cannot export to synthetic unless topology is symmetric (root->symmetric_subtree must be set).\n"); + errno = EINVAL; + return -1; + } + + if (!(flags & HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_IGNORE_MEMORY) + && hwloc_check_memory_symmetric(topology) < 0) { + if (verbose) + fprintf(stderr, "Cannot export to synthetic unless memory is attached symmetrically.\n"); + errno = EINVAL; + return -1; + } + + if (flags & HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_V1) { + /* v1 requires all NUMA at the same level */ + hwloc_obj_t node; + signed pdepth; + + node = hwloc_get_obj_by_type(topology, HWLOC_OBJ_NUMANODE, 0); + assert(hwloc__obj_type_is_normal(node->parent->type)); /* only depth-1 memory children for now */ + pdepth = node->parent->depth; + + while ((node = node->next_cousin) != NULL) { + assert(hwloc__obj_type_is_normal(node->parent->type)); /* only depth-1 memory children for now */ + if (node->parent->depth != pdepth) { + if (verbose) + fprintf(stderr, "Cannot export to synthetic v1 if memory is attached to parents at different depths.\n"); + errno = EINVAL; + return -1; + } + } + } + + /* we're good, start exporting */ + + if (!(flags & HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_NO_ATTRS)) { + /* obj attributes */ + res = hwloc__export_synthetic_obj_attr(topology, obj, tmp, tmplen); + if (res > 0) + needprefix = 1; + if (hwloc__export_synthetic_update_status(&ret, &tmp, &tmplen, res) < 0) + return -1; + } + + if (!(flags & HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_IGNORE_MEMORY)) { + res = hwloc__export_synthetic_memory_children(topology, flags, obj, tmp, tmplen, needprefix, verbose); + if (res > 0) + needprefix = 1; + if (hwloc__export_synthetic_update_status(&ret, &tmp, &tmplen, res) < 0) + return -1; + } + + arity = obj->arity; + while (arity) { + /* for each level */ + obj = obj->first_child; + + if (needprefix) + hwloc__export_synthetic_add_char(&ret, &tmp, &tmplen, ' '); + + res = hwloc__export_synthetic_obj(topology, flags, obj, arity, tmp, tmplen); + if (hwloc__export_synthetic_update_status(&ret, &tmp, &tmplen, res) < 0) + return -1; + + if (!(flags & HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_IGNORE_MEMORY)) { + res = hwloc__export_synthetic_memory_children(topology, flags, obj, tmp, tmplen, 1, verbose); + if (hwloc__export_synthetic_update_status(&ret, &tmp, &tmplen, res) < 0) + return -1; + } + + /* next level */ + needprefix = 1; + arity = obj->arity; + } + + return ret; +} diff --git a/src/3rdparty/hwloc/src/topology-windows.c b/src/3rdparty/hwloc/src/topology-windows.c new file mode 100644 index 00000000..d03645c0 --- /dev/null +++ b/src/3rdparty/hwloc/src/topology-windows.c @@ -0,0 +1,1189 @@ +/* + * Copyright © 2009 CNRS + * Copyright © 2009-2018 Inria. All rights reserved. + * Copyright © 2009-2012 Université Bordeaux + * Copyright © 2011 Cisco Systems, Inc. All rights reserved. + * See COPYING in top-level directory. + */ + +/* To try to get all declarations duplicated below. */ +#define _WIN32_WINNT 0x0601 + +#include +#include +#include +#include + +#include + +#ifndef HAVE_KAFFINITY +typedef ULONG_PTR KAFFINITY, *PKAFFINITY; +#endif + +#ifndef HAVE_PROCESSOR_CACHE_TYPE +typedef enum _PROCESSOR_CACHE_TYPE { + CacheUnified, + CacheInstruction, + CacheData, + CacheTrace +} PROCESSOR_CACHE_TYPE; +#endif + +#ifndef CACHE_FULLY_ASSOCIATIVE +#define CACHE_FULLY_ASSOCIATIVE 0xFF +#endif + +#ifndef MAXIMUM_PROC_PER_GROUP /* missing in MinGW */ +#define MAXIMUM_PROC_PER_GROUP 64 +#endif + +#ifndef HAVE_CACHE_DESCRIPTOR +typedef struct _CACHE_DESCRIPTOR { + BYTE Level; + BYTE Associativity; + WORD LineSize; + DWORD Size; /* in bytes */ + PROCESSOR_CACHE_TYPE Type; +} CACHE_DESCRIPTOR, *PCACHE_DESCRIPTOR; +#endif + +#ifndef HAVE_LOGICAL_PROCESSOR_RELATIONSHIP +typedef enum _LOGICAL_PROCESSOR_RELATIONSHIP { + RelationProcessorCore, + RelationNumaNode, + RelationCache, + RelationProcessorPackage, + RelationGroup, + RelationAll = 0xffff +} LOGICAL_PROCESSOR_RELATIONSHIP; +#else /* HAVE_LOGICAL_PROCESSOR_RELATIONSHIP */ +# ifndef HAVE_RELATIONPROCESSORPACKAGE +# define RelationProcessorPackage 3 +# define RelationGroup 4 +# define RelationAll 0xffff +# endif /* HAVE_RELATIONPROCESSORPACKAGE */ +#endif /* HAVE_LOGICAL_PROCESSOR_RELATIONSHIP */ + +#ifndef HAVE_SYSTEM_LOGICAL_PROCESSOR_INFORMATION +typedef struct _SYSTEM_LOGICAL_PROCESSOR_INFORMATION { + ULONG_PTR ProcessorMask; + LOGICAL_PROCESSOR_RELATIONSHIP Relationship; + _ANONYMOUS_UNION + union { + struct { + BYTE flags; + } ProcessorCore; + struct { + DWORD NodeNumber; + } NumaNode; + CACHE_DESCRIPTOR Cache; + ULONGLONG Reserved[2]; + } DUMMYUNIONNAME; +} SYSTEM_LOGICAL_PROCESSOR_INFORMATION, *PSYSTEM_LOGICAL_PROCESSOR_INFORMATION; +#endif + +/* Extended interface, for group support */ + +#ifndef HAVE_GROUP_AFFINITY +typedef struct _GROUP_AFFINITY { + KAFFINITY Mask; + WORD Group; + WORD Reserved[3]; +} GROUP_AFFINITY, *PGROUP_AFFINITY; +#endif + +#ifndef HAVE_PROCESSOR_RELATIONSHIP +typedef struct _PROCESSOR_RELATIONSHIP { + BYTE Flags; + BYTE Reserved[21]; + WORD GroupCount; + GROUP_AFFINITY GroupMask[ANYSIZE_ARRAY]; +} PROCESSOR_RELATIONSHIP, *PPROCESSOR_RELATIONSHIP; +#endif + +#ifndef HAVE_NUMA_NODE_RELATIONSHIP +typedef struct _NUMA_NODE_RELATIONSHIP { + DWORD NodeNumber; + BYTE Reserved[20]; + GROUP_AFFINITY GroupMask; +} NUMA_NODE_RELATIONSHIP, *PNUMA_NODE_RELATIONSHIP; +#endif + +#ifndef HAVE_CACHE_RELATIONSHIP +typedef struct _CACHE_RELATIONSHIP { + BYTE Level; + BYTE Associativity; + WORD LineSize; + DWORD CacheSize; + PROCESSOR_CACHE_TYPE Type; + BYTE Reserved[20]; + GROUP_AFFINITY GroupMask; +} CACHE_RELATIONSHIP, *PCACHE_RELATIONSHIP; +#endif + +#ifndef HAVE_PROCESSOR_GROUP_INFO +typedef struct _PROCESSOR_GROUP_INFO { + BYTE MaximumProcessorCount; + BYTE ActiveProcessorCount; + BYTE Reserved[38]; + KAFFINITY ActiveProcessorMask; +} PROCESSOR_GROUP_INFO, *PPROCESSOR_GROUP_INFO; +#endif + +#ifndef HAVE_GROUP_RELATIONSHIP +typedef struct _GROUP_RELATIONSHIP { + WORD MaximumGroupCount; + WORD ActiveGroupCount; + ULONGLONG Reserved[2]; + PROCESSOR_GROUP_INFO GroupInfo[ANYSIZE_ARRAY]; +} GROUP_RELATIONSHIP, *PGROUP_RELATIONSHIP; +#endif + +#ifndef HAVE_SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX +typedef struct _SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX { + LOGICAL_PROCESSOR_RELATIONSHIP Relationship; + DWORD Size; + _ANONYMOUS_UNION + union { + PROCESSOR_RELATIONSHIP Processor; + NUMA_NODE_RELATIONSHIP NumaNode; + CACHE_RELATIONSHIP Cache; + GROUP_RELATIONSHIP Group; + /* Odd: no member to tell the cpu mask of the package... */ + } DUMMYUNIONNAME; +} SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX, *PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX; +#endif + +#ifndef HAVE_PSAPI_WORKING_SET_EX_BLOCK +typedef union _PSAPI_WORKING_SET_EX_BLOCK { + ULONG_PTR Flags; + struct { + unsigned Valid :1; + unsigned ShareCount :3; + unsigned Win32Protection :11; + unsigned Shared :1; + unsigned Node :6; + unsigned Locked :1; + unsigned LargePage :1; + }; +} PSAPI_WORKING_SET_EX_BLOCK; +#endif + +#ifndef HAVE_PSAPI_WORKING_SET_EX_INFORMATION +typedef struct _PSAPI_WORKING_SET_EX_INFORMATION { + PVOID VirtualAddress; + PSAPI_WORKING_SET_EX_BLOCK VirtualAttributes; +} PSAPI_WORKING_SET_EX_INFORMATION; +#endif + +#ifndef HAVE_PROCESSOR_NUMBER +typedef struct _PROCESSOR_NUMBER { + WORD Group; + BYTE Number; + BYTE Reserved; +} PROCESSOR_NUMBER, *PPROCESSOR_NUMBER; +#endif + +/* Function pointers */ + +typedef WORD (WINAPI *PFN_GETACTIVEPROCESSORGROUPCOUNT)(void); +static PFN_GETACTIVEPROCESSORGROUPCOUNT GetActiveProcessorGroupCountProc; + +static unsigned long nr_processor_groups = 1; +static unsigned long max_numanode_index = 0; + +typedef WORD (WINAPI *PFN_GETACTIVEPROCESSORCOUNT)(WORD); +static PFN_GETACTIVEPROCESSORCOUNT GetActiveProcessorCountProc; + +typedef DWORD (WINAPI *PFN_GETCURRENTPROCESSORNUMBER)(void); +static PFN_GETCURRENTPROCESSORNUMBER GetCurrentProcessorNumberProc; + +typedef VOID (WINAPI *PFN_GETCURRENTPROCESSORNUMBEREX)(PPROCESSOR_NUMBER); +static PFN_GETCURRENTPROCESSORNUMBEREX GetCurrentProcessorNumberExProc; + +typedef BOOL (WINAPI *PFN_GETLOGICALPROCESSORINFORMATION)(PSYSTEM_LOGICAL_PROCESSOR_INFORMATION Buffer, PDWORD ReturnLength); +static PFN_GETLOGICALPROCESSORINFORMATION GetLogicalProcessorInformationProc; + +typedef BOOL (WINAPI *PFN_GETLOGICALPROCESSORINFORMATIONEX)(LOGICAL_PROCESSOR_RELATIONSHIP relationship, PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX Buffer, PDWORD ReturnLength); +static PFN_GETLOGICALPROCESSORINFORMATIONEX GetLogicalProcessorInformationExProc; + +typedef BOOL (WINAPI *PFN_SETTHREADGROUPAFFINITY)(HANDLE hThread, const GROUP_AFFINITY *GroupAffinity, PGROUP_AFFINITY PreviousGroupAffinity); +static PFN_SETTHREADGROUPAFFINITY SetThreadGroupAffinityProc; + +typedef BOOL (WINAPI *PFN_GETTHREADGROUPAFFINITY)(HANDLE hThread, PGROUP_AFFINITY GroupAffinity); +static PFN_GETTHREADGROUPAFFINITY GetThreadGroupAffinityProc; + +typedef BOOL (WINAPI *PFN_GETNUMAAVAILABLEMEMORYNODE)(UCHAR Node, PULONGLONG AvailableBytes); +static PFN_GETNUMAAVAILABLEMEMORYNODE GetNumaAvailableMemoryNodeProc; + +typedef BOOL (WINAPI *PFN_GETNUMAAVAILABLEMEMORYNODEEX)(USHORT Node, PULONGLONG AvailableBytes); +static PFN_GETNUMAAVAILABLEMEMORYNODEEX GetNumaAvailableMemoryNodeExProc; + +typedef LPVOID (WINAPI *PFN_VIRTUALALLOCEXNUMA)(HANDLE hProcess, LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect, DWORD nndPreferred); +static PFN_VIRTUALALLOCEXNUMA VirtualAllocExNumaProc; + +typedef BOOL (WINAPI *PFN_VIRTUALFREEEX)(HANDLE hProcess, LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType); +static PFN_VIRTUALFREEEX VirtualFreeExProc; + +typedef BOOL (WINAPI *PFN_QUERYWORKINGSETEX)(HANDLE hProcess, PVOID pv, DWORD cb); +static PFN_QUERYWORKINGSETEX QueryWorkingSetExProc; + +static void hwloc_win_get_function_ptrs(void) +{ + HMODULE kernel32; + + kernel32 = LoadLibrary("kernel32.dll"); + if (kernel32) { + GetActiveProcessorGroupCountProc = + (PFN_GETACTIVEPROCESSORGROUPCOUNT) GetProcAddress(kernel32, "GetActiveProcessorGroupCount"); + GetActiveProcessorCountProc = + (PFN_GETACTIVEPROCESSORCOUNT) GetProcAddress(kernel32, "GetActiveProcessorCount"); + GetLogicalProcessorInformationProc = + (PFN_GETLOGICALPROCESSORINFORMATION) GetProcAddress(kernel32, "GetLogicalProcessorInformation"); + GetCurrentProcessorNumberProc = + (PFN_GETCURRENTPROCESSORNUMBER) GetProcAddress(kernel32, "GetCurrentProcessorNumber"); + GetCurrentProcessorNumberExProc = + (PFN_GETCURRENTPROCESSORNUMBEREX) GetProcAddress(kernel32, "GetCurrentProcessorNumberEx"); + SetThreadGroupAffinityProc = + (PFN_SETTHREADGROUPAFFINITY) GetProcAddress(kernel32, "SetThreadGroupAffinity"); + GetThreadGroupAffinityProc = + (PFN_GETTHREADGROUPAFFINITY) GetProcAddress(kernel32, "GetThreadGroupAffinity"); + GetNumaAvailableMemoryNodeProc = + (PFN_GETNUMAAVAILABLEMEMORYNODE) GetProcAddress(kernel32, "GetNumaAvailableMemoryNode"); + GetNumaAvailableMemoryNodeExProc = + (PFN_GETNUMAAVAILABLEMEMORYNODEEX) GetProcAddress(kernel32, "GetNumaAvailableMemoryNodeEx"); + GetLogicalProcessorInformationExProc = + (PFN_GETLOGICALPROCESSORINFORMATIONEX)GetProcAddress(kernel32, "GetLogicalProcessorInformationEx"); + QueryWorkingSetExProc = + (PFN_QUERYWORKINGSETEX) GetProcAddress(kernel32, "K32QueryWorkingSetEx"); + VirtualAllocExNumaProc = + (PFN_VIRTUALALLOCEXNUMA) GetProcAddress(kernel32, "VirtualAllocExNuma"); + VirtualFreeExProc = + (PFN_VIRTUALFREEEX) GetProcAddress(kernel32, "VirtualFreeEx"); + } + + if (GetActiveProcessorGroupCountProc) + nr_processor_groups = GetActiveProcessorGroupCountProc(); + + if (!QueryWorkingSetExProc) { + HMODULE psapi = LoadLibrary("psapi.dll"); + if (psapi) + QueryWorkingSetExProc = (PFN_QUERYWORKINGSETEX) GetProcAddress(psapi, "QueryWorkingSetEx"); + } +} + +/* + * ULONG_PTR and DWORD_PTR are 64/32bits depending on the arch + * while bitmaps use unsigned long (always 32bits) + */ + +static void hwloc_bitmap_from_ULONG_PTR(hwloc_bitmap_t set, ULONG_PTR mask) +{ +#if SIZEOF_VOID_P == 8 + hwloc_bitmap_from_ulong(set, mask & 0xffffffff); + hwloc_bitmap_set_ith_ulong(set, 1, mask >> 32); +#else + hwloc_bitmap_from_ulong(set, mask); +#endif +} + +static void hwloc_bitmap_from_ith_ULONG_PTR(hwloc_bitmap_t set, unsigned i, ULONG_PTR mask) +{ +#if SIZEOF_VOID_P == 8 + hwloc_bitmap_from_ith_ulong(set, 2*i, mask & 0xffffffff); + hwloc_bitmap_set_ith_ulong(set, 2*i+1, mask >> 32); +#else + hwloc_bitmap_from_ith_ulong(set, i, mask); +#endif +} + +static void hwloc_bitmap_set_ith_ULONG_PTR(hwloc_bitmap_t set, unsigned i, ULONG_PTR mask) +{ +#if SIZEOF_VOID_P == 8 + hwloc_bitmap_set_ith_ulong(set, 2*i, mask & 0xffffffff); + hwloc_bitmap_set_ith_ulong(set, 2*i+1, mask >> 32); +#else + hwloc_bitmap_set_ith_ulong(set, i, mask); +#endif +} + +static ULONG_PTR hwloc_bitmap_to_ULONG_PTR(hwloc_const_bitmap_t set) +{ +#if SIZEOF_VOID_P == 8 + ULONG_PTR up = hwloc_bitmap_to_ith_ulong(set, 1); + up <<= 32; + up |= hwloc_bitmap_to_ulong(set); + return up; +#else + return hwloc_bitmap_to_ulong(set); +#endif +} + +static ULONG_PTR hwloc_bitmap_to_ith_ULONG_PTR(hwloc_const_bitmap_t set, unsigned i) +{ +#if SIZEOF_VOID_P == 8 + ULONG_PTR up = hwloc_bitmap_to_ith_ulong(set, 2*i+1); + up <<= 32; + up |= hwloc_bitmap_to_ith_ulong(set, 2*i); + return up; +#else + return hwloc_bitmap_to_ith_ulong(set, i); +#endif +} + +/* convert set into index+mask if all set bits are in the same ULONG. + * otherwise return -1. + */ +static int hwloc_bitmap_to_single_ULONG_PTR(hwloc_const_bitmap_t set, unsigned *index, ULONG_PTR *mask) +{ + unsigned first_ulp, last_ulp; + if (hwloc_bitmap_weight(set) == -1) + return -1; + first_ulp = hwloc_bitmap_first(set) / (sizeof(ULONG_PTR)*8); + last_ulp = hwloc_bitmap_last(set) / (sizeof(ULONG_PTR)*8); + if (first_ulp != last_ulp) + return -1; + *mask = hwloc_bitmap_to_ith_ULONG_PTR(set, first_ulp); + *index = first_ulp; + return 0; +} + +/************************************************************** + * hwloc PU numbering with respect to Windows processor groups + * + * Everywhere below we reserve 64 physical indexes per processor groups because that's + * the maximum (MAXIMUM_PROC_PER_GROUP). Windows may actually use less bits than that + * in some groups (either to avoid splitting NUMA nodes across groups, or because of OS + * tweaks such as "bcdedit /set groupsize 8") but we keep some unused indexes for simplicity. + * That means PU physical indexes and cpusets may be non-contigous. + * That also means hwloc_fallback_nbprocessors() below must return the last PU index + 1 + * instead the actual number of processors. + */ + +/******************** + * last_cpu_location + */ + +static int +hwloc_win_get_thisthread_last_cpu_location(hwloc_topology_t topology __hwloc_attribute_unused, hwloc_cpuset_t set, int flags __hwloc_attribute_unused) +{ + assert(GetCurrentProcessorNumberExProc || (GetCurrentProcessorNumberProc && nr_processor_groups == 1)); + + if (nr_processor_groups > 1 || !GetCurrentProcessorNumberProc) { + PROCESSOR_NUMBER num; + GetCurrentProcessorNumberExProc(&num); + hwloc_bitmap_from_ith_ULONG_PTR(set, num.Group, ((ULONG_PTR)1) << num.Number); + return 0; + } + + hwloc_bitmap_from_ith_ULONG_PTR(set, 0, ((ULONG_PTR)1) << GetCurrentProcessorNumberProc()); + return 0; +} + +/* TODO: hwloc_win_get_thisproc_last_cpu_location() using + * CreateToolhelp32Snapshot(), Thread32First/Next() + * th.th32OwnerProcessID == GetCurrentProcessId() for filtering within process + * OpenThread(THREAD_SET_INFORMATION|THREAD_QUERY_INFORMATION, FALSE, te32.th32ThreadID) to get a handle. + */ + + +/****************************** + * set cpu/membind for threads + */ + +/* TODO: SetThreadIdealProcessor{,Ex} */ + +static int +hwloc_win_set_thread_cpubind(hwloc_topology_t topology __hwloc_attribute_unused, hwloc_thread_t thread, hwloc_const_bitmap_t hwloc_set, int flags) +{ + DWORD_PTR mask; + unsigned group; + + if (flags & HWLOC_CPUBIND_NOMEMBIND) { + errno = ENOSYS; + return -1; + } + + if (hwloc_bitmap_to_single_ULONG_PTR(hwloc_set, &group, &mask) < 0) { + errno = ENOSYS; + return -1; + } + + assert(nr_processor_groups == 1 || SetThreadGroupAffinityProc); + + if (nr_processor_groups > 1) { + GROUP_AFFINITY aff; + memset(&aff, 0, sizeof(aff)); /* we get Invalid Parameter error if Reserved field isn't cleared */ + aff.Group = group; + aff.Mask = mask; + if (!SetThreadGroupAffinityProc(thread, &aff, NULL)) + return -1; + + } else { + /* SetThreadAffinityMask() only changes the mask inside the current processor group */ + /* The resulting binding is always strict */ + if (!SetThreadAffinityMask(thread, mask)) + return -1; + } + return 0; +} + +static int +hwloc_win_set_thisthread_cpubind(hwloc_topology_t topology, hwloc_const_bitmap_t hwloc_set, int flags) +{ + return hwloc_win_set_thread_cpubind(topology, GetCurrentThread(), hwloc_set, flags); +} + +static int +hwloc_win_set_thisthread_membind(hwloc_topology_t topology, hwloc_const_nodeset_t nodeset, hwloc_membind_policy_t policy, int flags) +{ + int ret; + hwloc_const_cpuset_t cpuset; + hwloc_cpuset_t _cpuset = NULL; + + if ((policy != HWLOC_MEMBIND_DEFAULT && policy != HWLOC_MEMBIND_BIND) + || flags & HWLOC_MEMBIND_NOCPUBIND) { + errno = ENOSYS; + return -1; + } + + if (policy == HWLOC_MEMBIND_DEFAULT) { + cpuset = hwloc_topology_get_complete_cpuset(topology); + } else { + cpuset = _cpuset = hwloc_bitmap_alloc(); + hwloc_cpuset_from_nodeset(topology, _cpuset, nodeset); + } + + ret = hwloc_win_set_thisthread_cpubind(topology, cpuset, + (flags & HWLOC_MEMBIND_STRICT) ? HWLOC_CPUBIND_STRICT : 0); + hwloc_bitmap_free(_cpuset); + return ret; +} + + +/****************************** + * get cpu/membind for threads + */ + +static int +hwloc_win_get_thread_cpubind(hwloc_topology_t topology __hwloc_attribute_unused, hwloc_thread_t thread, hwloc_cpuset_t set, int flags __hwloc_attribute_unused) +{ + GROUP_AFFINITY aff; + + assert(GetThreadGroupAffinityProc); + + if (!GetThreadGroupAffinityProc(thread, &aff)) + return -1; + hwloc_bitmap_from_ith_ULONG_PTR(set, aff.Group, aff.Mask); + return 0; +} + +static int +hwloc_win_get_thisthread_cpubind(hwloc_topology_t topology __hwloc_attribute_unused, hwloc_cpuset_t set, int flags __hwloc_attribute_unused) +{ + return hwloc_win_get_thread_cpubind(topology, GetCurrentThread(), set, flags); +} + +static int +hwloc_win_get_thisthread_membind(hwloc_topology_t topology, hwloc_nodeset_t nodeset, hwloc_membind_policy_t * policy, int flags) +{ + int ret; + hwloc_cpuset_t cpuset = hwloc_bitmap_alloc(); + ret = hwloc_win_get_thread_cpubind(topology, GetCurrentThread(), cpuset, flags); + if (!ret) { + *policy = HWLOC_MEMBIND_BIND; + hwloc_cpuset_to_nodeset(topology, cpuset, nodeset); + } + hwloc_bitmap_free(cpuset); + return ret; +} + + +/******************************** + * set cpu/membind for processes + */ + +static int +hwloc_win_set_proc_cpubind(hwloc_topology_t topology __hwloc_attribute_unused, hwloc_pid_t proc, hwloc_const_bitmap_t hwloc_set, int flags) +{ + DWORD_PTR mask; + + assert(nr_processor_groups == 1); + + if (flags & HWLOC_CPUBIND_NOMEMBIND) { + errno = ENOSYS; + return -1; + } + + /* TODO: SetThreadGroupAffinity() for all threads doesn't enforce the whole process affinity, + * maybe because of process-specific resource locality */ + /* TODO: if we are in a single group (check with GetProcessGroupAffinity()), + * SetProcessAffinityMask() changes the binding within that same group. + */ + /* TODO: NtSetInformationProcess() works very well for binding to any mask in a single group, + * but it's an internal routine. + */ + /* TODO: checks whether hwloc-bind.c needs to pass INHERIT_PARENT_AFFINITY to CreateProcess() instead of execvp(). */ + + /* The resulting binding is always strict */ + mask = hwloc_bitmap_to_ULONG_PTR(hwloc_set); + if (!SetProcessAffinityMask(proc, mask)) + return -1; + return 0; +} + +static int +hwloc_win_set_thisproc_cpubind(hwloc_topology_t topology, hwloc_const_bitmap_t hwloc_set, int flags) +{ + return hwloc_win_set_proc_cpubind(topology, GetCurrentProcess(), hwloc_set, flags); +} + +static int +hwloc_win_set_proc_membind(hwloc_topology_t topology, hwloc_pid_t pid, hwloc_const_nodeset_t nodeset, hwloc_membind_policy_t policy, int flags) +{ + int ret; + hwloc_const_cpuset_t cpuset; + hwloc_cpuset_t _cpuset = NULL; + + if ((policy != HWLOC_MEMBIND_DEFAULT && policy != HWLOC_MEMBIND_BIND) + || flags & HWLOC_MEMBIND_NOCPUBIND) { + errno = ENOSYS; + return -1; + } + + if (policy == HWLOC_MEMBIND_DEFAULT) { + cpuset = hwloc_topology_get_complete_cpuset(topology); + } else { + cpuset = _cpuset = hwloc_bitmap_alloc(); + hwloc_cpuset_from_nodeset(topology, _cpuset, nodeset); + } + + ret = hwloc_win_set_proc_cpubind(topology, pid, cpuset, + (flags & HWLOC_MEMBIND_STRICT) ? HWLOC_CPUBIND_STRICT : 0); + hwloc_bitmap_free(_cpuset); + return ret; +} + +static int +hwloc_win_set_thisproc_membind(hwloc_topology_t topology, hwloc_const_nodeset_t nodeset, hwloc_membind_policy_t policy, int flags) +{ + return hwloc_win_set_proc_membind(topology, GetCurrentProcess(), nodeset, policy, flags); +} + + +/******************************** + * get cpu/membind for processes + */ + +static int +hwloc_win_get_proc_cpubind(hwloc_topology_t topology __hwloc_attribute_unused, hwloc_pid_t proc, hwloc_bitmap_t hwloc_set, int flags) +{ + DWORD_PTR proc_mask, sys_mask; + + assert(nr_processor_groups == 1); + + if (flags & HWLOC_CPUBIND_NOMEMBIND) { + errno = ENOSYS; + return -1; + } + + /* TODO: if we are in a single group (check with GetProcessGroupAffinity()), + * GetProcessAffinityMask() gives the mask within that group. + */ + /* TODO: if we are in multiple groups, GetProcessGroupAffinity() gives their IDs, + * but we don't know their masks. + */ + /* TODO: GetThreadGroupAffinity() for all threads can be smaller than the whole process affinity, + * maybe because of process-specific resource locality. + */ + + if (!GetProcessAffinityMask(proc, &proc_mask, &sys_mask)) + return -1; + hwloc_bitmap_from_ULONG_PTR(hwloc_set, proc_mask); + return 0; +} + +static int +hwloc_win_get_proc_membind(hwloc_topology_t topology, hwloc_pid_t pid, hwloc_nodeset_t nodeset, hwloc_membind_policy_t * policy, int flags) +{ + int ret; + hwloc_cpuset_t cpuset = hwloc_bitmap_alloc(); + ret = hwloc_win_get_proc_cpubind(topology, pid, cpuset, + (flags & HWLOC_MEMBIND_STRICT) ? HWLOC_CPUBIND_STRICT : 0); + if (!ret) { + *policy = HWLOC_MEMBIND_BIND; + hwloc_cpuset_to_nodeset(topology, cpuset, nodeset); + } + hwloc_bitmap_free(cpuset); + return ret; +} + +static int +hwloc_win_get_thisproc_cpubind(hwloc_topology_t topology, hwloc_bitmap_t hwloc_cpuset, int flags) +{ + return hwloc_win_get_proc_cpubind(topology, GetCurrentProcess(), hwloc_cpuset, flags); +} + +static int +hwloc_win_get_thisproc_membind(hwloc_topology_t topology, hwloc_nodeset_t nodeset, hwloc_membind_policy_t * policy, int flags) +{ + return hwloc_win_get_proc_membind(topology, GetCurrentProcess(), nodeset, policy, flags); +} + + +/************************ + * membind alloc/free + */ + +static void * +hwloc_win_alloc(hwloc_topology_t topology __hwloc_attribute_unused, size_t len) { + return VirtualAlloc(NULL, len, MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE); +} + +static void * +hwloc_win_alloc_membind(hwloc_topology_t topology __hwloc_attribute_unused, size_t len, hwloc_const_nodeset_t nodeset, hwloc_membind_policy_t policy, int flags) { + int node; + + switch (policy) { + case HWLOC_MEMBIND_DEFAULT: + case HWLOC_MEMBIND_BIND: + break; + default: + errno = ENOSYS; + return hwloc_alloc_or_fail(topology, len, flags); + } + + if (flags & HWLOC_MEMBIND_STRICT) { + errno = ENOSYS; + return NULL; + } + + if (policy == HWLOC_MEMBIND_DEFAULT + || hwloc_bitmap_isequal(nodeset, hwloc_topology_get_complete_nodeset(topology))) + return hwloc_win_alloc(topology, len); + + if (hwloc_bitmap_weight(nodeset) != 1) { + /* Not a single node, can't do this */ + errno = EXDEV; + return hwloc_alloc_or_fail(topology, len, flags); + } + + node = hwloc_bitmap_first(nodeset); + return VirtualAllocExNumaProc(GetCurrentProcess(), NULL, len, MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE, node); +} + +static int +hwloc_win_free_membind(hwloc_topology_t topology __hwloc_attribute_unused, void *addr, size_t len __hwloc_attribute_unused) { + if (!addr) + return 0; + if (!VirtualFreeExProc(GetCurrentProcess(), addr, 0, MEM_RELEASE)) + return -1; + return 0; +} + + +/********************** + * membind for areas + */ + +static int +hwloc_win_get_area_memlocation(hwloc_topology_t topology __hwloc_attribute_unused, const void *addr, size_t len, hwloc_nodeset_t nodeset, int flags __hwloc_attribute_unused) +{ + SYSTEM_INFO SystemInfo; + DWORD page_size; + uintptr_t start; + unsigned nb; + PSAPI_WORKING_SET_EX_INFORMATION *pv; + unsigned i; + + GetSystemInfo(&SystemInfo); + page_size = SystemInfo.dwPageSize; + + start = (((uintptr_t) addr) / page_size) * page_size; + nb = (unsigned)((((uintptr_t) addr + len - start) + page_size - 1) / page_size); + + if (!nb) + nb = 1; + + pv = calloc(nb, sizeof(*pv)); + if (!pv) + return -1; + + for (i = 0; i < nb; i++) + pv[i].VirtualAddress = (void*) (start + i * page_size); + if (!QueryWorkingSetExProc(GetCurrentProcess(), pv, nb * sizeof(*pv))) { + free(pv); + return -1; + } + + for (i = 0; i < nb; i++) { + if (pv[i].VirtualAttributes.Valid) + hwloc_bitmap_set(nodeset, pv[i].VirtualAttributes.Node); + } + + free(pv); + return 0; +} + + +/************************* + * discovery + */ + +static int +hwloc_look_windows(struct hwloc_backend *backend) +{ + struct hwloc_topology *topology = backend->topology; + hwloc_bitmap_t groups_pu_set = NULL; + SYSTEM_INFO SystemInfo; + DWORD length; + int gotnuma = 0; + int gotnumamemory = 0; + + if (topology->levels[0][0]->cpuset) + /* somebody discovered things */ + return -1; + + hwloc_alloc_root_sets(topology->levels[0][0]); + + GetSystemInfo(&SystemInfo); + + if (!GetLogicalProcessorInformationExProc && GetLogicalProcessorInformationProc) { + PSYSTEM_LOGICAL_PROCESSOR_INFORMATION procInfo, tmpprocInfo; + unsigned id; + unsigned i; + struct hwloc_obj *obj; + hwloc_obj_type_t type; + + length = 0; + procInfo = NULL; + + while (1) { + if (GetLogicalProcessorInformationProc(procInfo, &length)) + break; + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) + return -1; + tmpprocInfo = realloc(procInfo, length); + if (!tmpprocInfo) { + free(procInfo); + goto out; + } + procInfo = tmpprocInfo; + } + + assert(!length || procInfo); + + for (i = 0; i < length / sizeof(*procInfo); i++) { + + /* Ignore unknown caches */ + if (procInfo->Relationship == RelationCache + && procInfo->Cache.Type != CacheUnified + && procInfo->Cache.Type != CacheData + && procInfo->Cache.Type != CacheInstruction) + continue; + + id = HWLOC_UNKNOWN_INDEX; + switch (procInfo[i].Relationship) { + case RelationNumaNode: + type = HWLOC_OBJ_NUMANODE; + id = procInfo[i].NumaNode.NodeNumber; + gotnuma++; + if (id > max_numanode_index) + max_numanode_index = id; + break; + case RelationProcessorPackage: + type = HWLOC_OBJ_PACKAGE; + break; + case RelationCache: + type = (procInfo[i].Cache.Type == CacheInstruction ? HWLOC_OBJ_L1ICACHE : HWLOC_OBJ_L1CACHE) + procInfo[i].Cache.Level - 1; + break; + case RelationProcessorCore: + type = HWLOC_OBJ_CORE; + break; + case RelationGroup: + default: + type = HWLOC_OBJ_GROUP; + break; + } + + if (!hwloc_filter_check_keep_object_type(topology, type)) + continue; + + obj = hwloc_alloc_setup_object(topology, type, id); + obj->cpuset = hwloc_bitmap_alloc(); + hwloc_debug("%s#%u mask %llx\n", hwloc_obj_type_string(type), id, (unsigned long long) procInfo[i].ProcessorMask); + /* ProcessorMask is a ULONG_PTR */ + hwloc_bitmap_set_ith_ULONG_PTR(obj->cpuset, 0, procInfo[i].ProcessorMask); + hwloc_debug_2args_bitmap("%s#%u bitmap %s\n", hwloc_obj_type_string(type), id, obj->cpuset); + + switch (type) { + case HWLOC_OBJ_NUMANODE: + { + ULONGLONG avail; + obj->nodeset = hwloc_bitmap_alloc(); + hwloc_bitmap_set(obj->nodeset, id); + if ((GetNumaAvailableMemoryNodeExProc && GetNumaAvailableMemoryNodeExProc(id, &avail)) + || (GetNumaAvailableMemoryNodeProc && GetNumaAvailableMemoryNodeProc(id, &avail))) { + obj->attr->numanode.local_memory = avail; + gotnumamemory++; + } + obj->attr->numanode.page_types_len = 2; + obj->attr->numanode.page_types = malloc(2 * sizeof(*obj->attr->numanode.page_types)); + memset(obj->attr->numanode.page_types, 0, 2 * sizeof(*obj->attr->numanode.page_types)); + obj->attr->numanode.page_types_len = 1; + obj->attr->numanode.page_types[0].size = SystemInfo.dwPageSize; +#if HAVE_DECL__SC_LARGE_PAGESIZE + obj->attr->numanode.page_types_len++; + obj->attr->numanode.page_types[1].size = sysconf(_SC_LARGE_PAGESIZE); +#endif + break; + } + case HWLOC_OBJ_L1CACHE: + case HWLOC_OBJ_L2CACHE: + case HWLOC_OBJ_L3CACHE: + case HWLOC_OBJ_L4CACHE: + case HWLOC_OBJ_L5CACHE: + case HWLOC_OBJ_L1ICACHE: + case HWLOC_OBJ_L2ICACHE: + case HWLOC_OBJ_L3ICACHE: + obj->attr->cache.size = procInfo[i].Cache.Size; + obj->attr->cache.associativity = procInfo[i].Cache.Associativity == CACHE_FULLY_ASSOCIATIVE ? -1 : procInfo[i].Cache.Associativity ; + obj->attr->cache.linesize = procInfo[i].Cache.LineSize; + obj->attr->cache.depth = procInfo[i].Cache.Level; + switch (procInfo->Cache.Type) { + case CacheUnified: + obj->attr->cache.type = HWLOC_OBJ_CACHE_UNIFIED; + break; + case CacheData: + obj->attr->cache.type = HWLOC_OBJ_CACHE_DATA; + break; + case CacheInstruction: + obj->attr->cache.type = HWLOC_OBJ_CACHE_INSTRUCTION; + break; + default: + hwloc_free_unlinked_object(obj); + continue; + } + break; + case HWLOC_OBJ_GROUP: + obj->attr->group.kind = procInfo[i].Relationship == RelationGroup ? HWLOC_GROUP_KIND_WINDOWS_PROCESSOR_GROUP : HWLOC_GROUP_KIND_WINDOWS_RELATIONSHIP_UNKNOWN; + break; + default: + break; + } + hwloc_insert_object_by_cpuset(topology, obj); + } + + free(procInfo); + } + + if (GetLogicalProcessorInformationExProc) { + PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX procInfoTotal, tmpprocInfoTotal, procInfo; + unsigned id; + struct hwloc_obj *obj; + hwloc_obj_type_t type; + + length = 0; + procInfoTotal = NULL; + + while (1) { + if (GetLogicalProcessorInformationExProc(RelationAll, procInfoTotal, &length)) + break; + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) + return -1; + tmpprocInfoTotal = realloc(procInfoTotal, length); + if (!tmpprocInfoTotal) { + free(procInfoTotal); + goto out; + } + procInfoTotal = tmpprocInfoTotal; + } + + for (procInfo = procInfoTotal; + (void*) procInfo < (void*) ((uintptr_t) procInfoTotal + length); + procInfo = (void*) ((uintptr_t) procInfo + procInfo->Size)) { + unsigned num, i; + GROUP_AFFINITY *GroupMask; + + /* Ignore unknown caches */ + if (procInfo->Relationship == RelationCache + && procInfo->Cache.Type != CacheUnified + && procInfo->Cache.Type != CacheData + && procInfo->Cache.Type != CacheInstruction) + continue; + + id = HWLOC_UNKNOWN_INDEX; + switch (procInfo->Relationship) { + case RelationNumaNode: + type = HWLOC_OBJ_NUMANODE; + num = 1; + GroupMask = &procInfo->NumaNode.GroupMask; + id = procInfo->NumaNode.NodeNumber; + gotnuma++; + if (id > max_numanode_index) + max_numanode_index = id; + break; + case RelationProcessorPackage: + type = HWLOC_OBJ_PACKAGE; + num = procInfo->Processor.GroupCount; + GroupMask = procInfo->Processor.GroupMask; + break; + case RelationCache: + type = (procInfo->Cache.Type == CacheInstruction ? HWLOC_OBJ_L1ICACHE : HWLOC_OBJ_L1CACHE) + procInfo->Cache.Level - 1; + num = 1; + GroupMask = &procInfo->Cache.GroupMask; + break; + case RelationProcessorCore: + type = HWLOC_OBJ_CORE; + num = procInfo->Processor.GroupCount; + GroupMask = procInfo->Processor.GroupMask; + break; + case RelationGroup: + /* So strange an interface... */ + for (id = 0; id < procInfo->Group.ActiveGroupCount; id++) { + KAFFINITY mask; + hwloc_bitmap_t set; + + set = hwloc_bitmap_alloc(); + mask = procInfo->Group.GroupInfo[id].ActiveProcessorMask; + hwloc_debug("group %u %d cpus mask %lx\n", id, + procInfo->Group.GroupInfo[id].ActiveProcessorCount, mask); + /* KAFFINITY is ULONG_PTR */ + hwloc_bitmap_set_ith_ULONG_PTR(set, id, mask); + /* FIXME: what if running 32bits on a 64bits windows with 64-processor groups? + * ULONG_PTR is 32bits, so half the group is invisible? + * maybe scale id to id*8/sizeof(ULONG_PTR) so that groups are 64-PU aligned? + */ + hwloc_debug_2args_bitmap("group %u %d bitmap %s\n", id, procInfo->Group.GroupInfo[id].ActiveProcessorCount, set); + + /* save the set of PUs so that we can create them at the end */ + if (!groups_pu_set) + groups_pu_set = hwloc_bitmap_alloc(); + hwloc_bitmap_or(groups_pu_set, groups_pu_set, set); + + if (hwloc_filter_check_keep_object_type(topology, HWLOC_OBJ_GROUP)) { + obj = hwloc_alloc_setup_object(topology, HWLOC_OBJ_GROUP, id); + obj->cpuset = set; + obj->attr->group.kind = HWLOC_GROUP_KIND_WINDOWS_PROCESSOR_GROUP; + hwloc_insert_object_by_cpuset(topology, obj); + } else + hwloc_bitmap_free(set); + } + continue; + default: + /* Don't know how to get the mask. */ + hwloc_debug("unknown relation %d\n", procInfo->Relationship); + continue; + } + + if (!hwloc_filter_check_keep_object_type(topology, type)) + continue; + + obj = hwloc_alloc_setup_object(topology, type, id); + obj->cpuset = hwloc_bitmap_alloc(); + for (i = 0; i < num; i++) { + hwloc_debug("%s#%u %d: mask %d:%lx\n", hwloc_obj_type_string(type), id, i, GroupMask[i].Group, GroupMask[i].Mask); + /* GROUP_AFFINITY.Mask is KAFFINITY, which is ULONG_PTR */ + hwloc_bitmap_set_ith_ULONG_PTR(obj->cpuset, GroupMask[i].Group, GroupMask[i].Mask); + /* FIXME: scale id to id*8/sizeof(ULONG_PTR) as above? */ + } + hwloc_debug_2args_bitmap("%s#%u bitmap %s\n", hwloc_obj_type_string(type), id, obj->cpuset); + switch (type) { + case HWLOC_OBJ_NUMANODE: + { + ULONGLONG avail; + obj->nodeset = hwloc_bitmap_alloc(); + hwloc_bitmap_set(obj->nodeset, id); + if ((GetNumaAvailableMemoryNodeExProc && GetNumaAvailableMemoryNodeExProc(id, &avail)) + || (GetNumaAvailableMemoryNodeProc && GetNumaAvailableMemoryNodeProc(id, &avail))) { + obj->attr->numanode.local_memory = avail; + gotnumamemory++; + } + obj->attr->numanode.page_types = malloc(2 * sizeof(*obj->attr->numanode.page_types)); + memset(obj->attr->numanode.page_types, 0, 2 * sizeof(*obj->attr->numanode.page_types)); + obj->attr->numanode.page_types_len = 1; + obj->attr->numanode.page_types[0].size = SystemInfo.dwPageSize; +#if HAVE_DECL__SC_LARGE_PAGESIZE + obj->attr->numanode.page_types_len++; + obj->attr->numanode.page_types[1].size = sysconf(_SC_LARGE_PAGESIZE); +#endif + break; + } + case HWLOC_OBJ_L1CACHE: + case HWLOC_OBJ_L2CACHE: + case HWLOC_OBJ_L3CACHE: + case HWLOC_OBJ_L4CACHE: + case HWLOC_OBJ_L5CACHE: + case HWLOC_OBJ_L1ICACHE: + case HWLOC_OBJ_L2ICACHE: + case HWLOC_OBJ_L3ICACHE: + obj->attr->cache.size = procInfo->Cache.CacheSize; + obj->attr->cache.associativity = procInfo->Cache.Associativity == CACHE_FULLY_ASSOCIATIVE ? -1 : procInfo->Cache.Associativity ; + obj->attr->cache.linesize = procInfo->Cache.LineSize; + obj->attr->cache.depth = procInfo->Cache.Level; + switch (procInfo->Cache.Type) { + case CacheUnified: + obj->attr->cache.type = HWLOC_OBJ_CACHE_UNIFIED; + break; + case CacheData: + obj->attr->cache.type = HWLOC_OBJ_CACHE_DATA; + break; + case CacheInstruction: + obj->attr->cache.type = HWLOC_OBJ_CACHE_INSTRUCTION; + break; + default: + hwloc_free_unlinked_object(obj); + continue; + } + break; + default: + break; + } + hwloc_insert_object_by_cpuset(topology, obj); + } + free(procInfoTotal); + } + + topology->support.discovery->pu = 1; + topology->support.discovery->numa = gotnuma; + topology->support.discovery->numa_memory = gotnumamemory; + + if (groups_pu_set) { + /* the system supports multiple Groups. + * PU indexes may be discontiguous, especially if Groups contain less than 64 procs. + */ + hwloc_obj_t obj; + unsigned idx; + hwloc_bitmap_foreach_begin(idx, groups_pu_set) { + obj = hwloc_alloc_setup_object(topology, HWLOC_OBJ_PU, idx); + obj->cpuset = hwloc_bitmap_alloc(); + hwloc_bitmap_only(obj->cpuset, idx); + hwloc_debug_1arg_bitmap("cpu %u has cpuset %s\n", + idx, obj->cpuset); + hwloc_insert_object_by_cpuset(topology, obj); + } hwloc_bitmap_foreach_end(); + hwloc_bitmap_free(groups_pu_set); + } else { + /* no processor groups */ + SYSTEM_INFO sysinfo; + hwloc_obj_t obj; + unsigned idx; + GetSystemInfo(&sysinfo); + for(idx=0; idx<32; idx++) + if (sysinfo.dwActiveProcessorMask & (((DWORD_PTR)1)<cpuset = hwloc_bitmap_alloc(); + hwloc_bitmap_only(obj->cpuset, idx); + hwloc_debug_1arg_bitmap("cpu %u has cpuset %s\n", + idx, obj->cpuset); + hwloc_insert_object_by_cpuset(topology, obj); + } + } + + out: + hwloc_obj_add_info(topology->levels[0][0], "Backend", "Windows"); + hwloc_add_uname_info(topology, NULL); + return 0; +} + +void +hwloc_set_windows_hooks(struct hwloc_binding_hooks *hooks, + struct hwloc_topology_support *support) +{ + if (GetCurrentProcessorNumberExProc || (GetCurrentProcessorNumberProc && nr_processor_groups == 1)) + hooks->get_thisthread_last_cpu_location = hwloc_win_get_thisthread_last_cpu_location; + + if (nr_processor_groups == 1) { + hooks->set_proc_cpubind = hwloc_win_set_proc_cpubind; + hooks->get_proc_cpubind = hwloc_win_get_proc_cpubind; + hooks->set_thisproc_cpubind = hwloc_win_set_thisproc_cpubind; + hooks->get_thisproc_cpubind = hwloc_win_get_thisproc_cpubind; + hooks->set_proc_membind = hwloc_win_set_proc_membind; + hooks->get_proc_membind = hwloc_win_get_proc_membind; + hooks->set_thisproc_membind = hwloc_win_set_thisproc_membind; + hooks->get_thisproc_membind = hwloc_win_get_thisproc_membind; + } + if (nr_processor_groups == 1 || SetThreadGroupAffinityProc) { + hooks->set_thread_cpubind = hwloc_win_set_thread_cpubind; + hooks->set_thisthread_cpubind = hwloc_win_set_thisthread_cpubind; + hooks->set_thisthread_membind = hwloc_win_set_thisthread_membind; + } + if (GetThreadGroupAffinityProc) { + hooks->get_thread_cpubind = hwloc_win_get_thread_cpubind; + hooks->get_thisthread_cpubind = hwloc_win_get_thisthread_cpubind; + hooks->get_thisthread_membind = hwloc_win_get_thisthread_membind; + } + + if (VirtualAllocExNumaProc) { + hooks->alloc_membind = hwloc_win_alloc_membind; + hooks->alloc = hwloc_win_alloc; + hooks->free_membind = hwloc_win_free_membind; + support->membind->bind_membind = 1; + } + + if (QueryWorkingSetExProc && max_numanode_index <= 63 /* PSAPI_WORKING_SET_EX_BLOCK.Node is 6 bits only */) + hooks->get_area_memlocation = hwloc_win_get_area_memlocation; +} + +static int hwloc_windows_component_init(unsigned long flags __hwloc_attribute_unused) +{ + hwloc_win_get_function_ptrs(); + return 0; +} + +static void hwloc_windows_component_finalize(unsigned long flags __hwloc_attribute_unused) +{ +} + +static struct hwloc_backend * +hwloc_windows_component_instantiate(struct hwloc_disc_component *component, + const void *_data1 __hwloc_attribute_unused, + const void *_data2 __hwloc_attribute_unused, + const void *_data3 __hwloc_attribute_unused) +{ + struct hwloc_backend *backend; + backend = hwloc_backend_alloc(component); + if (!backend) + return NULL; + backend->discover = hwloc_look_windows; + return backend; +} + +static struct hwloc_disc_component hwloc_windows_disc_component = { + HWLOC_DISC_COMPONENT_TYPE_CPU, + "windows", + HWLOC_DISC_COMPONENT_TYPE_GLOBAL, + hwloc_windows_component_instantiate, + 50, + 1, + NULL +}; + +const struct hwloc_component hwloc_windows_component = { + HWLOC_COMPONENT_ABI, + hwloc_windows_component_init, hwloc_windows_component_finalize, + HWLOC_COMPONENT_TYPE_DISC, + 0, + &hwloc_windows_disc_component +}; + +int +hwloc_fallback_nbprocessors(struct hwloc_topology *topology __hwloc_attribute_unused) { + int n; + SYSTEM_INFO sysinfo; + + /* by default, ignore groups (return only the number in the current group) */ + GetSystemInfo(&sysinfo); + n = sysinfo.dwNumberOfProcessors; /* FIXME could be non-contigous, rather return a mask from dwActiveProcessorMask? */ + + if (nr_processor_groups > 1) { + /* assume n-1 groups are complete, since that's how we store things in cpusets */ + if (GetActiveProcessorCountProc) + n = MAXIMUM_PROC_PER_GROUP*(nr_processor_groups-1) + + GetActiveProcessorCountProc((WORD)nr_processor_groups-1); + else + n = MAXIMUM_PROC_PER_GROUP*nr_processor_groups; + } + + return n; +} diff --git a/src/3rdparty/hwloc/src/topology-x86.c b/src/3rdparty/hwloc/src/topology-x86.c new file mode 100644 index 00000000..4aefdcf1 --- /dev/null +++ b/src/3rdparty/hwloc/src/topology-x86.c @@ -0,0 +1,1583 @@ +/* + * Copyright © 2010-2019 Inria. All rights reserved. + * Copyright © 2010-2013 Université Bordeaux + * Copyright © 2010-2011 Cisco Systems, Inc. All rights reserved. + * See COPYING in top-level directory. + * + * + * This backend is only used when the operating system does not export + * the necessary hardware topology information to user-space applications. + * Currently, only the FreeBSD backend relies on this x86 backend. + * + * Other backends such as Linux have their own way to retrieve various + * pieces of hardware topology information from the operating system + * on various architectures, without having to use this x86-specific code. + */ + +#include +#include +#include +#include +#include + +#include + +#include +#ifdef HAVE_DIRENT_H +#include +#endif +#ifdef HAVE_VALGRIND_VALGRIND_H +#include +#endif + +struct hwloc_x86_backend_data_s { + unsigned nbprocs; + hwloc_bitmap_t apicid_set; + int apicid_unique; + char *src_cpuiddump_path; + int is_knl; +}; + +/************************************ + * Management of cpuid dump as input + */ + +struct cpuiddump { + unsigned nr; + struct cpuiddump_entry { + unsigned inmask; /* which of ine[abcd]x are set on input */ + unsigned ineax; + unsigned inebx; + unsigned inecx; + unsigned inedx; + unsigned outeax; + unsigned outebx; + unsigned outecx; + unsigned outedx; + } *entries; +}; + +static void +cpuiddump_free(struct cpuiddump *cpuiddump) +{ + if (cpuiddump->nr) + free(cpuiddump->entries); + free(cpuiddump); +} + +static struct cpuiddump * +cpuiddump_read(const char *dirpath, unsigned idx) +{ + struct cpuiddump *cpuiddump; + struct cpuiddump_entry *cur; + FILE *file; + char line[128]; + unsigned nr; + + cpuiddump = malloc(sizeof(*cpuiddump)); + if (!cpuiddump) { + fprintf(stderr, "Failed to allocate cpuiddump for PU #%u, ignoring cpuiddump.\n", idx); + goto out; + } + + { + size_t filenamelen = strlen(dirpath) + 15; + HWLOC_VLA(char, filename, filenamelen); + snprintf(filename, filenamelen, "%s/pu%u", dirpath, idx); + file = fopen(filename, "r"); + if (!file) { + fprintf(stderr, "Could not read dumped cpuid file %s, ignoring cpuiddump.\n", filename); + goto out_with_dump; + } + } + + nr = 0; + while (fgets(line, sizeof(line), file)) + nr++; + cpuiddump->entries = malloc(nr * sizeof(struct cpuiddump_entry)); + if (!cpuiddump->entries) { + fprintf(stderr, "Failed to allocate %u cpuiddump entries for PU #%u, ignoring cpuiddump.\n", nr, idx); + goto out_with_file; + } + + fseek(file, 0, SEEK_SET); + cur = &cpuiddump->entries[0]; + nr = 0; + while (fgets(line, sizeof(line), file)) { + if (*line == '#') + continue; + if (sscanf(line, "%x %x %x %x %x => %x %x %x %x", + &cur->inmask, + &cur->ineax, &cur->inebx, &cur->inecx, &cur->inedx, + &cur->outeax, &cur->outebx, &cur->outecx, &cur->outedx) == 9) { + cur++; + nr++; + } + } + + cpuiddump->nr = nr; + fclose(file); + return cpuiddump; + + out_with_file: + fclose(file); + out_with_dump: + free(cpuiddump); + out: + return NULL; +} + +static void +cpuiddump_find_by_input(unsigned *eax, unsigned *ebx, unsigned *ecx, unsigned *edx, struct cpuiddump *cpuiddump) +{ + unsigned i; + + for(i=0; inr; i++) { + struct cpuiddump_entry *entry = &cpuiddump->entries[i]; + if ((entry->inmask & 0x1) && *eax != entry->ineax) + continue; + if ((entry->inmask & 0x2) && *ebx != entry->inebx) + continue; + if ((entry->inmask & 0x4) && *ecx != entry->inecx) + continue; + if ((entry->inmask & 0x8) && *edx != entry->inedx) + continue; + *eax = entry->outeax; + *ebx = entry->outebx; + *ecx = entry->outecx; + *edx = entry->outedx; + return; + } + + fprintf(stderr, "Couldn't find %x,%x,%x,%x in dumped cpuid, returning 0s.\n", + *eax, *ebx, *ecx, *edx); + *eax = 0; + *ebx = 0; + *ecx = 0; + *edx = 0; +} + +static void cpuid_or_from_dump(unsigned *eax, unsigned *ebx, unsigned *ecx, unsigned *edx, struct cpuiddump *src_cpuiddump) +{ + if (src_cpuiddump) { + cpuiddump_find_by_input(eax, ebx, ecx, edx, src_cpuiddump); + } else { + hwloc_x86_cpuid(eax, ebx, ecx, edx); + } +} + +/******************************* + * Core detection routines and structures + */ + +#define has_topoext(features) ((features)[6] & (1 << 22)) +#define has_x2apic(features) ((features)[4] & (1 << 21)) + +struct cacheinfo { + hwloc_obj_cache_type_t type; + unsigned level; + unsigned nbthreads_sharing; + unsigned cacheid; + + unsigned linesize; + unsigned linepart; + int inclusive; + int ways; + unsigned sets; + unsigned long size; +}; + +struct procinfo { + unsigned present; + unsigned apicid; + unsigned packageid; + unsigned dieid; + unsigned nodeid; + unsigned unitid; + unsigned threadid; + unsigned coreid; + unsigned *otherids; + unsigned levels; + unsigned numcaches; + struct cacheinfo *cache; + char cpuvendor[13]; + char cpumodel[3*4*4+1]; + unsigned cpustepping; + unsigned cpumodelnumber; + unsigned cpufamilynumber; +}; + +enum cpuid_type { + intel, + amd, + zhaoxin, + hygon, + unknown +}; + +static void fill_amd_cache(struct procinfo *infos, unsigned level, hwloc_obj_cache_type_t type, unsigned nbthreads_sharing, unsigned cpuid) +{ + struct cacheinfo *cache, *tmpcaches; + unsigned cachenum; + unsigned long size = 0; + + if (level == 1) + size = ((cpuid >> 24)) << 10; + else if (level == 2) + size = ((cpuid >> 16)) << 10; + else if (level == 3) + size = ((cpuid >> 18)) << 19; + if (!size) + return; + + tmpcaches = realloc(infos->cache, (infos->numcaches+1)*sizeof(*infos->cache)); + if (!tmpcaches) + /* failed to allocated, ignore that cache */ + return; + infos->cache = tmpcaches; + cachenum = infos->numcaches++; + + cache = &infos->cache[cachenum]; + + cache->type = type; + cache->level = level; + cache->nbthreads_sharing = nbthreads_sharing; + cache->linesize = cpuid & 0xff; + cache->linepart = 0; + cache->inclusive = 0; /* old AMD (K8-K10) supposed to have exclusive caches */ + + if (level == 1) { + cache->ways = (cpuid >> 16) & 0xff; + if (cache->ways == 0xff) + /* Fully associative */ + cache->ways = -1; + } else { + static const unsigned ways_tab[] = { 0, 1, 2, 0, 4, 0, 8, 0, 16, 0, 32, 48, 64, 96, 128, -1 }; + unsigned ways = (cpuid >> 12) & 0xf; + cache->ways = ways_tab[ways]; + } + cache->size = size; + cache->sets = 0; + + hwloc_debug("cache L%u t%u linesize %u ways %d size %luKB\n", cache->level, cache->nbthreads_sharing, cache->linesize, cache->ways, cache->size >> 10); +} + +static void look_exttopoenum(struct procinfo *infos, unsigned leaf, struct cpuiddump *src_cpuiddump) +{ + unsigned level, apic_nextshift, apic_number, apic_type, apic_id = 0, apic_shift = 0, id; + unsigned threadid __hwloc_attribute_unused = 0; /* shut-up compiler */ + unsigned eax, ebx, ecx = 0, edx; + int apic_packageshift = 0; + + for (level = 0; ; level++) { + ecx = level; + eax = leaf; + cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump); + if (!eax && !ebx) + break; + apic_packageshift = eax & 0x1f; + } + + if (level) { + infos->otherids = malloc(level * sizeof(*infos->otherids)); + if (infos->otherids) { + infos->levels = level; + for (level = 0; ; level++) { + ecx = level; + eax = leaf; + cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump); + if (!eax && !ebx) + break; + apic_nextshift = eax & 0x1f; + apic_number = ebx & 0xffff; + apic_type = (ecx & 0xff00) >> 8; + apic_id = edx; + id = (apic_id >> apic_shift) & ((1 << (apic_packageshift - apic_shift)) - 1); + hwloc_debug("x2APIC %08x %u: nextshift %u num %2u type %u id %2u\n", apic_id, level, apic_nextshift, apic_number, apic_type, id); + infos->apicid = apic_id; + infos->otherids[level] = UINT_MAX; + switch (apic_type) { + case 1: + threadid = id; + /* apic_number is the actual number of threads per core */ + break; + case 2: + infos->coreid = id; + /* apic_number is the actual number of threads per module */ + break; + case 5: + infos->dieid = id; + /* apic_number is the actual number of threads per package */ + break; + default: + hwloc_debug("x2APIC %u: unknown type %u\n", level, apic_type); + infos->otherids[level] = apic_id >> apic_shift; + break; + } + apic_shift = apic_nextshift; + } + infos->apicid = apic_id; + infos->packageid = apic_id >> apic_shift; + hwloc_debug("x2APIC remainder: %u\n", infos->packageid); + hwloc_debug("this is thread %u of core %u\n", threadid, infos->coreid); + } + } +} + +/* Fetch information from the processor itself thanks to cpuid and store it in + * infos for summarize to analyze them globally */ +static void look_proc(struct hwloc_backend *backend, struct procinfo *infos, unsigned highest_cpuid, unsigned highest_ext_cpuid, unsigned *features, enum cpuid_type cpuid_type, struct cpuiddump *src_cpuiddump) +{ + struct hwloc_x86_backend_data_s *data = backend->private_data; + unsigned eax, ebx, ecx = 0, edx; + unsigned cachenum; + struct cacheinfo *cache; + unsigned regs[4]; + unsigned legacy_max_log_proc; /* not valid on Intel processors with > 256 threads, or when cpuid 0x80000008 is supported */ + unsigned legacy_log_proc_id; + unsigned _model, _extendedmodel, _family, _extendedfamily; + + infos->present = 1; + + /* Get apicid, legacy_max_log_proc, packageid, legacy_log_proc_id from cpuid 0x01 */ + eax = 0x01; + cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump); + infos->apicid = ebx >> 24; + if (edx & (1 << 28)) + legacy_max_log_proc = 1 << hwloc_flsl(((ebx >> 16) & 0xff) - 1); + else + legacy_max_log_proc = 1; + hwloc_debug("APIC ID 0x%02x legacy_max_log_proc %u\n", infos->apicid, legacy_max_log_proc); + infos->packageid = infos->apicid / legacy_max_log_proc; + legacy_log_proc_id = infos->apicid % legacy_max_log_proc; + hwloc_debug("phys %u legacy thread %u\n", infos->packageid, legacy_log_proc_id); + + /* Get cpu model/family/stepping numbers from same cpuid */ + _model = (eax>>4) & 0xf; + _extendedmodel = (eax>>16) & 0xf; + _family = (eax>>8) & 0xf; + _extendedfamily = (eax>>20) & 0xff; + if ((cpuid_type == intel || cpuid_type == amd || cpuid_type == hygon) && _family == 0xf) { + infos->cpufamilynumber = _family + _extendedfamily; + } else { + infos->cpufamilynumber = _family; + } + if ((cpuid_type == intel && (_family == 0x6 || _family == 0xf)) + || ((cpuid_type == amd || cpuid_type == hygon) && _family == 0xf) + || (cpuid_type == zhaoxin && (_family == 0x6 || _family == 0x7))) { + infos->cpumodelnumber = _model + (_extendedmodel << 4); + } else { + infos->cpumodelnumber = _model; + } + infos->cpustepping = eax & 0xf; + + if (cpuid_type == intel && infos->cpufamilynumber == 0x6 && + (infos->cpumodelnumber == 0x57 || infos->cpumodelnumber == 0x85)) + data->is_knl = 1; /* KNM is the same as KNL */ + + /* Get cpu vendor string from cpuid 0x00 */ + memset(regs, 0, sizeof(regs)); + regs[0] = 0; + cpuid_or_from_dump(®s[0], ®s[1], ®s[3], ®s[2], src_cpuiddump); + memcpy(infos->cpuvendor, regs+1, 4*3); + /* infos was calloc'ed, already ends with \0 */ + + /* Get cpu model string from cpuid 0x80000002-4 */ + if (highest_ext_cpuid >= 0x80000004) { + memset(regs, 0, sizeof(regs)); + regs[0] = 0x80000002; + cpuid_or_from_dump(®s[0], ®s[1], ®s[2], ®s[3], src_cpuiddump); + memcpy(infos->cpumodel, regs, 4*4); + regs[0] = 0x80000003; + cpuid_or_from_dump(®s[0], ®s[1], ®s[2], ®s[3], src_cpuiddump); + memcpy(infos->cpumodel + 4*4, regs, 4*4); + regs[0] = 0x80000004; + cpuid_or_from_dump(®s[0], ®s[1], ®s[2], ®s[3], src_cpuiddump); + memcpy(infos->cpumodel + 4*4*2, regs, 4*4); + /* infos was calloc'ed, already ends with \0 */ + } + + /* Get core/thread information from cpuid 0x80000008 + * (not supported on Intel) + */ + if (cpuid_type != intel && cpuid_type != zhaoxin && highest_ext_cpuid >= 0x80000008) { + unsigned max_nbcores; + unsigned max_nbthreads; + unsigned coreidsize; + unsigned logprocid; + eax = 0x80000008; + cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump); + coreidsize = (ecx >> 12) & 0xf; + hwloc_debug("core ID size: %u\n", coreidsize); + if (!coreidsize) { + max_nbcores = (ecx & 0xff) + 1; + } else + max_nbcores = 1 << coreidsize; + hwloc_debug("Thus max # of cores: %u\n", max_nbcores); + /* Still no multithreaded AMD */ + max_nbthreads = 1 ; + hwloc_debug("and max # of threads: %u\n", max_nbthreads); + /* legacy_max_log_proc is deprecated, it can be smaller than max_nbcores, + * which is the maximum number of cores that the processor could theoretically support + * (see "Multiple Core Calculation" in the AMD CPUID specification). + * Recompute packageid/threadid/coreid accordingly. + */ + infos->packageid = infos->apicid / max_nbcores; + logprocid = infos->apicid % max_nbcores; + infos->threadid = logprocid % max_nbthreads; + infos->coreid = logprocid / max_nbthreads; + hwloc_debug("this is thread %u of core %u\n", infos->threadid, infos->coreid); + } + + infos->numcaches = 0; + infos->cache = NULL; + + /* Get apicid, nodeid, unitid from cpuid 0x8000001e + * and cache information from cpuid 0x8000001d + * (AMD topology extension) + */ + if (cpuid_type != intel && cpuid_type != zhaoxin && has_topoext(features)) { + unsigned apic_id, node_id, nodes_per_proc; + + /* the code below doesn't want any other cache yet */ + assert(!infos->numcaches); + + eax = 0x8000001e; + cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump); + infos->apicid = apic_id = eax; + + if (infos->cpufamilynumber == 0x16) { + /* ecx is reserved */ + node_id = 0; + nodes_per_proc = 1; + } else { + /* AMD other families or Hygon family 18h */ + node_id = ecx & 0xff; + nodes_per_proc = ((ecx >> 8) & 7) + 1; + } + infos->nodeid = node_id; + if ((infos->cpufamilynumber == 0x15 && nodes_per_proc > 2) + || ((infos->cpufamilynumber == 0x17 || infos->cpufamilynumber == 0x18) && nodes_per_proc > 4)) { + hwloc_debug("warning: undefined nodes_per_proc value %u, assuming it means %u\n", nodes_per_proc, nodes_per_proc); + } + + if (infos->cpufamilynumber <= 0x16) { /* topoext appeared in 0x15 and compute-units were only used in 0x15 and 0x16 */ + unsigned unit_id, cores_per_unit; + infos->unitid = unit_id = ebx & 0xff; + cores_per_unit = ((ebx >> 8) & 0xff) + 1; + hwloc_debug("topoext %08x, %u nodes, node %u, %u cores in unit %u\n", apic_id, nodes_per_proc, node_id, cores_per_unit, unit_id); + /* coreid and unitid are package-wide (core 0-15 and unit 0-7 on 16-core 2-NUMAnode processor). + * The Linux kernel reduces theses to NUMA-node-wide (by applying %core_per_node and %unit_per node respectively). + * It's not clear if we should do this as well. + */ + } else { + unsigned core_id, threads_per_core; + infos->coreid = core_id = ebx & 0xff; + threads_per_core = ((ebx >> 8) & 0xff) + 1; + hwloc_debug("topoext %08x, %u nodes, node %u, %u threads in core %u\n", apic_id, nodes_per_proc, node_id, threads_per_core, core_id); + } + + for (cachenum = 0; ; cachenum++) { + eax = 0x8000001d; + ecx = cachenum; + cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump); + if ((eax & 0x1f) == 0) + break; + infos->numcaches++; + } + + cache = infos->cache = malloc(infos->numcaches * sizeof(*infos->cache)); + if (cache) { + for (cachenum = 0; ; cachenum++) { + unsigned long linesize, linepart, ways, sets; + eax = 0x8000001d; + ecx = cachenum; + cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump); + + if ((eax & 0x1f) == 0) + break; + switch (eax & 0x1f) { + case 1: cache->type = HWLOC_OBJ_CACHE_DATA; break; + case 2: cache->type = HWLOC_OBJ_CACHE_INSTRUCTION; break; + default: cache->type = HWLOC_OBJ_CACHE_UNIFIED; break; + } + + cache->level = (eax >> 5) & 0x7; + /* Note: actually number of cores */ + cache->nbthreads_sharing = ((eax >> 14) & 0xfff) + 1; + + cache->linesize = linesize = (ebx & 0xfff) + 1; + cache->linepart = linepart = ((ebx >> 12) & 0x3ff) + 1; + ways = ((ebx >> 22) & 0x3ff) + 1; + + if (eax & (1 << 9)) + /* Fully associative */ + cache->ways = -1; + else + cache->ways = ways; + cache->sets = sets = ecx + 1; + cache->size = linesize * linepart * ways * sets; + cache->inclusive = edx & 0x2; + + hwloc_debug("cache %u L%u%c t%u linesize %lu linepart %lu ways %lu sets %lu, size %luKB\n", + cachenum, cache->level, + cache->type == HWLOC_OBJ_CACHE_DATA ? 'd' : cache->type == HWLOC_OBJ_CACHE_INSTRUCTION ? 'i' : 'u', + cache->nbthreads_sharing, linesize, linepart, ways, sets, cache->size >> 10); + + cache++; + } + } else { + infos->numcaches = 0; + } + } else { + /* If there's no topoext, + * get cache information from cpuid 0x80000005 and 0x80000006 + * (not supported on Intel) + */ + if (cpuid_type != intel && cpuid_type != zhaoxin && highest_ext_cpuid >= 0x80000005) { + eax = 0x80000005; + cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump); + fill_amd_cache(infos, 1, HWLOC_OBJ_CACHE_DATA, 1, ecx); /* private L1d */ + fill_amd_cache(infos, 1, HWLOC_OBJ_CACHE_INSTRUCTION, 1, edx); /* private L1i */ + } + if (cpuid_type != intel && cpuid_type != zhaoxin && highest_ext_cpuid >= 0x80000006) { + eax = 0x80000006; + cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump); + if (ecx & 0xf000) + /* This is actually supported on Intel but LinePerTag isn't returned in bits 8-11. + * Could be useful if some Intels (at least before Core micro-architecture) + * support this leaf without leaf 0x4. + */ + fill_amd_cache(infos, 2, HWLOC_OBJ_CACHE_UNIFIED, 1, ecx); /* private L2u */ + if (edx & 0xf000) + fill_amd_cache(infos, 3, HWLOC_OBJ_CACHE_UNIFIED, legacy_max_log_proc, edx); /* package-wide L3u */ + } + } + + /* Get thread/core + cache information from cpuid 0x04 + * (not supported on AMD) + */ + if ((cpuid_type != amd && cpuid_type != hygon) && highest_cpuid >= 0x04) { + unsigned max_nbcores; + unsigned max_nbthreads; + unsigned level; + struct cacheinfo *tmpcaches; + unsigned oldnumcaches = infos->numcaches; /* in case we got caches above */ + + for (cachenum = 0; ; cachenum++) { + eax = 0x04; + ecx = cachenum; + cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump); + + hwloc_debug("cache %u type %u\n", cachenum, eax & 0x1f); + if ((eax & 0x1f) == 0) + break; + level = (eax >> 5) & 0x7; + if (data->is_knl && level == 3) + /* KNL reports wrong L3 information (size always 0, cpuset always the entire machine, ignore it */ + break; + infos->numcaches++; + + if (!cachenum) { + /* by the way, get thread/core information from the first cache */ + max_nbcores = ((eax >> 26) & 0x3f) + 1; + max_nbthreads = legacy_max_log_proc / max_nbcores; + hwloc_debug("thus %u threads\n", max_nbthreads); + infos->threadid = legacy_log_proc_id % max_nbthreads; + infos->coreid = legacy_log_proc_id / max_nbthreads; + hwloc_debug("this is thread %u of core %u\n", infos->threadid, infos->coreid); + } + } + + tmpcaches = realloc(infos->cache, infos->numcaches * sizeof(*infos->cache)); + if (!tmpcaches) { + infos->numcaches = oldnumcaches; + } else { + infos->cache = tmpcaches; + cache = &infos->cache[oldnumcaches]; + + for (cachenum = 0; ; cachenum++) { + unsigned long linesize, linepart, ways, sets; + eax = 0x04; + ecx = cachenum; + cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump); + + if ((eax & 0x1f) == 0) + break; + level = (eax >> 5) & 0x7; + if (data->is_knl && level == 3) + /* KNL reports wrong L3 information (size always 0, cpuset always the entire machine, ignore it */ + break; + switch (eax & 0x1f) { + case 1: cache->type = HWLOC_OBJ_CACHE_DATA; break; + case 2: cache->type = HWLOC_OBJ_CACHE_INSTRUCTION; break; + default: cache->type = HWLOC_OBJ_CACHE_UNIFIED; break; + } + + cache->level = level; + cache->nbthreads_sharing = ((eax >> 14) & 0xfff) + 1; + + cache->linesize = linesize = (ebx & 0xfff) + 1; + cache->linepart = linepart = ((ebx >> 12) & 0x3ff) + 1; + ways = ((ebx >> 22) & 0x3ff) + 1; + if (eax & (1 << 9)) + /* Fully associative */ + cache->ways = -1; + else + cache->ways = ways; + cache->sets = sets = ecx + 1; + cache->size = linesize * linepart * ways * sets; + cache->inclusive = edx & 0x2; + + hwloc_debug("cache %u L%u%c t%u linesize %lu linepart %lu ways %lu sets %lu, size %luKB\n", + cachenum, cache->level, + cache->type == HWLOC_OBJ_CACHE_DATA ? 'd' : cache->type == HWLOC_OBJ_CACHE_INSTRUCTION ? 'i' : 'u', + cache->nbthreads_sharing, linesize, linepart, ways, sets, cache->size >> 10); + cache++; + } + } + } + + if ((cpuid_type == intel) && highest_cpuid >= 0x1f) { + /* Get package/die/module/tile/core/thread information from cpuid 0x1f + * (Intel v2 Extended Topology Enumeration) + */ + look_exttopoenum(infos, 0x1f, src_cpuiddump); + + } else if ((cpuid_type == intel || cpuid_type == zhaoxin) && highest_cpuid >= 0x0b && has_x2apic(features)) { + /* Get package/core/thread information from cpuid 0x0b + * (Intel v1 Extended Topology Enumeration) + */ + look_exttopoenum(infos, 0x0b, src_cpuiddump); + } + + /* Now that we have all info, compute cacheids and apply quirks */ + for (cachenum = 0; cachenum < infos->numcaches; cachenum++) { + cache = &infos->cache[cachenum]; + + /* default cacheid value */ + cache->cacheid = infos->apicid / cache->nbthreads_sharing; + + if (cpuid_type == amd) { + /* AMD quirks */ + if (infos->cpufamilynumber == 0x17 + && cache->level == 3 && cache->nbthreads_sharing == 6) { + /* AMD family 0x17 always shares L3 between 8 APIC ids, + * even when only 6 APIC ids are enabled and reported in nbthreads_sharing + * (on 24-core CPUs). + */ + cache->cacheid = infos->apicid / 8; + + } else if (infos->cpufamilynumber== 0x10 && infos->cpumodelnumber == 0x9 + && cache->level == 3 + && (cache->ways == -1 || (cache->ways % 2 == 0)) && cache->nbthreads_sharing >= 8) { + /* Fix AMD family 0x10 model 0x9 (Magny-Cours) with 8 or 12 cores. + * The L3 (and its associativity) is actually split into two halves). + */ + if (cache->nbthreads_sharing == 16) + cache->nbthreads_sharing = 12; /* nbthreads_sharing is a power of 2 but the processor actually has 8 or 12 cores */ + cache->nbthreads_sharing /= 2; + cache->size /= 2; + if (cache->ways != -1) + cache->ways /= 2; + /* AMD Magny-Cours 12-cores processor reserve APIC ids as AAAAAABBBBBB.... + * among first L3 (A), second L3 (B), and unexisting cores (.). + * On multi-socket servers, L3 in non-first sockets may have APIC id ranges + * such as [16-21] that are not aligned on multiple of nbthreads_sharing (6). + * That means, we can't just compare apicid/nbthreads_sharing to identify siblings. + */ + cache->cacheid = (infos->apicid % legacy_max_log_proc) / cache->nbthreads_sharing /* cacheid within the package */ + + 2 * (infos->apicid / legacy_max_log_proc); /* add 2 caches per previous package */ + + } else if (infos->cpufamilynumber == 0x15 + && (infos->cpumodelnumber == 0x1 /* Bulldozer */ || infos->cpumodelnumber == 0x2 /* Piledriver */) + && cache->level == 3 && cache->nbthreads_sharing == 6) { + /* AMD Bulldozer and Piledriver 12-core processors have same APIC ids as Magny-Cours below, + * but we can't merge the checks because the original nbthreads_sharing must be exactly 6 here. + */ + cache->cacheid = (infos->apicid % legacy_max_log_proc) / cache->nbthreads_sharing /* cacheid within the package */ + + 2 * (infos->apicid / legacy_max_log_proc); /* add 2 cache per previous package */ + } + } else if (cpuid_type == hygon) { + if (infos->cpufamilynumber == 0x18 + && cache->level == 3 && cache->nbthreads_sharing == 6) { + /* Hygon family 0x18 always shares L3 between 8 APIC ids, + * even when only 6 APIC ids are enabled and reported in nbthreads_sharing + * (on 24-core CPUs). + */ + cache->cacheid = infos->apicid / 8; + } + } + } + + if (hwloc_bitmap_isset(data->apicid_set, infos->apicid)) + data->apicid_unique = 0; + else + hwloc_bitmap_set(data->apicid_set, infos->apicid); +} + +static void +hwloc_x86_add_cpuinfos(hwloc_obj_t obj, struct procinfo *info, int replace) +{ + char number[12]; + if (info->cpuvendor[0]) + hwloc__add_info_nodup(&obj->infos, &obj->infos_count, "CPUVendor", info->cpuvendor, replace); + snprintf(number, sizeof(number), "%u", info->cpufamilynumber); + hwloc__add_info_nodup(&obj->infos, &obj->infos_count, "CPUFamilyNumber", number, replace); + snprintf(number, sizeof(number), "%u", info->cpumodelnumber); + hwloc__add_info_nodup(&obj->infos, &obj->infos_count, "CPUModelNumber", number, replace); + if (info->cpumodel[0]) { + const char *c = info->cpumodel; + while (*c == ' ') + c++; + hwloc__add_info_nodup(&obj->infos, &obj->infos_count, "CPUModel", c, replace); + } + snprintf(number, sizeof(number), "%u", info->cpustepping); + hwloc__add_info_nodup(&obj->infos, &obj->infos_count, "CPUStepping", number, replace); +} + +/* Analyse information stored in infos, and build/annotate topology levels accordingly */ +static void summarize(struct hwloc_backend *backend, struct procinfo *infos, int fulldiscovery) +{ + struct hwloc_topology *topology = backend->topology; + struct hwloc_x86_backend_data_s *data = backend->private_data; + unsigned nbprocs = data->nbprocs; + hwloc_bitmap_t complete_cpuset = hwloc_bitmap_alloc(); + unsigned i, j, l, level; + int one = -1; + hwloc_bitmap_t remaining_cpuset; + int gotnuma = 0; + + for (i = 0; i < nbprocs; i++) + if (infos[i].present) { + hwloc_bitmap_set(complete_cpuset, i); + one = i; + } + + if (one == -1) { + hwloc_bitmap_free(complete_cpuset); + return; + } + + remaining_cpuset = hwloc_bitmap_alloc(); + + /* Ideally, when fulldiscovery=0, we could add any object that doesn't exist yet. + * But what if the x86 and the native backends disagree because one is buggy? Which one to trust? + * We only add missing caches, and annotate other existing objects for now. + */ + + if (hwloc_filter_check_keep_object_type(topology, HWLOC_OBJ_PACKAGE)) { + /* Look for packages */ + hwloc_obj_t package; + + hwloc_bitmap_copy(remaining_cpuset, complete_cpuset); + while ((i = hwloc_bitmap_first(remaining_cpuset)) != (unsigned) -1) { + if (fulldiscovery) { + unsigned packageid = infos[i].packageid; + hwloc_bitmap_t package_cpuset = hwloc_bitmap_alloc(); + + for (j = i; j < nbprocs; j++) { + if (infos[j].packageid == packageid) { + hwloc_bitmap_set(package_cpuset, j); + hwloc_bitmap_clr(remaining_cpuset, j); + } + } + package = hwloc_alloc_setup_object(topology, HWLOC_OBJ_PACKAGE, packageid); + package->cpuset = package_cpuset; + + hwloc_x86_add_cpuinfos(package, &infos[i], 0); + + hwloc_debug_1arg_bitmap("os package %u has cpuset %s\n", + packageid, package_cpuset); + hwloc_insert_object_by_cpuset(topology, package); + + } else { + /* Annotate packages previously-existing packages */ + hwloc_bitmap_t set = hwloc_bitmap_alloc(); + hwloc_bitmap_set(set, i); + package = hwloc_get_next_obj_covering_cpuset_by_type(topology, set, HWLOC_OBJ_PACKAGE, NULL); + hwloc_bitmap_free(set); + if (package) { + /* Found package above that PU, annotate if no such attribute yet */ + hwloc_x86_add_cpuinfos(package, &infos[i], 1); + hwloc_bitmap_andnot(remaining_cpuset, remaining_cpuset, package->cpuset); + } else { + /* No package, annotate the root object */ + hwloc_x86_add_cpuinfos(hwloc_get_root_obj(topology), &infos[i], 1); + break; + } + } + } + } + + /* Look for Numa nodes inside packages (cannot be filtered-out) */ + if (fulldiscovery && getenv("HWLOC_X86_TOPOEXT_NUMANODES")) { + hwloc_bitmap_t node_cpuset; + hwloc_obj_t node; + + /* FIXME: if there's memory inside the root object, divide it into NUMA nodes? */ + + hwloc_bitmap_copy(remaining_cpuset, complete_cpuset); + while ((i = hwloc_bitmap_first(remaining_cpuset)) != (unsigned) -1) { + unsigned packageid = infos[i].packageid; + unsigned nodeid = infos[i].nodeid; + + if (nodeid == (unsigned)-1) { + hwloc_bitmap_clr(remaining_cpuset, i); + continue; + } + + node_cpuset = hwloc_bitmap_alloc(); + for (j = i; j < nbprocs; j++) { + if (infos[j].nodeid == (unsigned) -1) { + hwloc_bitmap_clr(remaining_cpuset, j); + continue; + } + + if (infos[j].packageid == packageid && infos[j].nodeid == nodeid) { + hwloc_bitmap_set(node_cpuset, j); + hwloc_bitmap_clr(remaining_cpuset, j); + } + } + node = hwloc_alloc_setup_object(topology, HWLOC_OBJ_NUMANODE, nodeid); + node->cpuset = node_cpuset; + node->nodeset = hwloc_bitmap_alloc(); + hwloc_bitmap_set(node->nodeset, nodeid); + hwloc_debug_1arg_bitmap("os node %u has cpuset %s\n", + nodeid, node_cpuset); + hwloc_insert_object_by_cpuset(topology, node); + gotnuma++; + } + } + + if (hwloc_filter_check_keep_object_type(topology, HWLOC_OBJ_GROUP)) { + if (fulldiscovery) { + char *env; + int dont_merge; + hwloc_bitmap_t unit_cpuset, die_cpuset; + hwloc_obj_t unit, die; + + /* Look for Compute units inside packages */ + hwloc_bitmap_copy(remaining_cpuset, complete_cpuset); + while ((i = hwloc_bitmap_first(remaining_cpuset)) != (unsigned) -1) { + unsigned packageid = infos[i].packageid; + unsigned unitid = infos[i].unitid; + + if (unitid == (unsigned)-1) { + hwloc_bitmap_clr(remaining_cpuset, i); + continue; + } + + unit_cpuset = hwloc_bitmap_alloc(); + for (j = i; j < nbprocs; j++) { + if (infos[j].unitid == (unsigned) -1) { + hwloc_bitmap_clr(remaining_cpuset, j); + continue; + } + + if (infos[j].packageid == packageid && infos[j].unitid == unitid) { + hwloc_bitmap_set(unit_cpuset, j); + hwloc_bitmap_clr(remaining_cpuset, j); + } + } + unit = hwloc_alloc_setup_object(topology, HWLOC_OBJ_GROUP, unitid); + unit->cpuset = unit_cpuset; + unit->subtype = strdup("ComputeUnit"); + unit->attr->group.kind = HWLOC_GROUP_KIND_AMD_COMPUTE_UNIT; + hwloc_debug_1arg_bitmap("os unit %u has cpuset %s\n", + unitid, unit_cpuset); + hwloc_insert_object_by_cpuset(topology, unit); + } + + /* Look for Dies inside packages */ + env = getenv("HWLOC_DONT_MERGE_DIE_GROUPS"); + dont_merge = env && atoi(env); + hwloc_bitmap_copy(remaining_cpuset, complete_cpuset); + while ((i = hwloc_bitmap_first(remaining_cpuset)) != (unsigned) -1) { + unsigned packageid = infos[i].packageid; + unsigned dieid = infos[i].dieid; + + if (dieid == (unsigned)-1) { + hwloc_bitmap_clr(remaining_cpuset, i); + continue; + } + + die_cpuset = hwloc_bitmap_alloc(); + for (j = i; j < nbprocs; j++) { + if (infos[j].dieid == (unsigned) -1) { + hwloc_bitmap_clr(remaining_cpuset, j); + continue; + } + + if (infos[j].packageid == packageid && infos[j].dieid == dieid) { + hwloc_bitmap_set(die_cpuset, j); + hwloc_bitmap_clr(remaining_cpuset, j); + } + } + die = hwloc_alloc_setup_object(topology, HWLOC_OBJ_GROUP, dieid); + die->cpuset = die_cpuset; + die->subtype = strdup("Die"); + die->attr->group.kind = HWLOC_GROUP_KIND_INTEL_DIE; + die->attr->group.dont_merge = dont_merge; + hwloc_debug_1arg_bitmap("os die %u has cpuset %s\n", + dieid, die_cpuset); + hwloc_insert_object_by_cpuset(topology, die); + } + + /* Look for unknown objects */ + if (infos[one].otherids) { + for (level = infos[one].levels-1; level <= infos[one].levels-1; level--) { + if (infos[one].otherids[level] != UINT_MAX) { + hwloc_bitmap_t unknown_cpuset; + hwloc_obj_t unknown_obj; + + hwloc_bitmap_copy(remaining_cpuset, complete_cpuset); + while ((i = hwloc_bitmap_first(remaining_cpuset)) != (unsigned) -1) { + unsigned unknownid = infos[i].otherids[level]; + + unknown_cpuset = hwloc_bitmap_alloc(); + for (j = i; j < nbprocs; j++) { + if (infos[j].otherids[level] == unknownid) { + hwloc_bitmap_set(unknown_cpuset, j); + hwloc_bitmap_clr(remaining_cpuset, j); + } + } + unknown_obj = hwloc_alloc_setup_object(topology, HWLOC_OBJ_GROUP, unknownid); + unknown_obj->cpuset = unknown_cpuset; + unknown_obj->attr->group.kind = HWLOC_GROUP_KIND_INTEL_EXTTOPOENUM_UNKNOWN; + unknown_obj->attr->group.subkind = level; + hwloc_debug_2args_bitmap("os unknown%u %u has cpuset %s\n", + level, unknownid, unknown_cpuset); + hwloc_insert_object_by_cpuset(topology, unknown_obj); + } + } + } + } + } + } + + if (hwloc_filter_check_keep_object_type(topology, HWLOC_OBJ_CORE)) { + /* Look for cores */ + if (fulldiscovery) { + hwloc_bitmap_t core_cpuset; + hwloc_obj_t core; + + hwloc_bitmap_copy(remaining_cpuset, complete_cpuset); + while ((i = hwloc_bitmap_first(remaining_cpuset)) != (unsigned) -1) { + unsigned packageid = infos[i].packageid; + unsigned nodeid = infos[i].nodeid; + unsigned coreid = infos[i].coreid; + + if (coreid == (unsigned) -1) { + hwloc_bitmap_clr(remaining_cpuset, i); + continue; + } + + core_cpuset = hwloc_bitmap_alloc(); + for (j = i; j < nbprocs; j++) { + if (infos[j].coreid == (unsigned) -1) { + hwloc_bitmap_clr(remaining_cpuset, j); + continue; + } + + if (infos[j].packageid == packageid && infos[j].nodeid == nodeid && infos[j].coreid == coreid) { + hwloc_bitmap_set(core_cpuset, j); + hwloc_bitmap_clr(remaining_cpuset, j); + } + } + core = hwloc_alloc_setup_object(topology, HWLOC_OBJ_CORE, coreid); + core->cpuset = core_cpuset; + hwloc_debug_1arg_bitmap("os core %u has cpuset %s\n", + coreid, core_cpuset); + hwloc_insert_object_by_cpuset(topology, core); + } + } + } + + /* Look for PUs (cannot be filtered-out) */ + if (fulldiscovery) { + hwloc_debug("%s", "\n\n * CPU cpusets *\n\n"); + for (i=0; icpuset = hwloc_bitmap_alloc(); + hwloc_bitmap_only(obj->cpuset, i); + hwloc_debug_1arg_bitmap("PU %u has cpuset %s\n", i, obj->cpuset); + hwloc_insert_object_by_cpuset(topology, obj); + } + } + + /* Look for caches */ + /* First find max level */ + level = 0; + for (i = 0; i < nbprocs; i++) + for (j = 0; j < infos[i].numcaches; j++) + if (infos[i].cache[j].level > level) + level = infos[i].cache[j].level; + while (level > 0) { + hwloc_obj_cache_type_t type; + HWLOC_BUILD_ASSERT(HWLOC_OBJ_CACHE_DATA == HWLOC_OBJ_CACHE_UNIFIED+1); + HWLOC_BUILD_ASSERT(HWLOC_OBJ_CACHE_INSTRUCTION == HWLOC_OBJ_CACHE_DATA+1); + for (type = HWLOC_OBJ_CACHE_UNIFIED; type <= HWLOC_OBJ_CACHE_INSTRUCTION; type++) { + /* Look for caches of that type at level level */ + hwloc_obj_type_t otype; + hwloc_obj_t cache; + + otype = hwloc_cache_type_by_depth_type(level, type); + if (otype == HWLOC_OBJ_TYPE_NONE) + continue; + if (!hwloc_filter_check_keep_object_type(topology, otype)) + continue; + + hwloc_bitmap_copy(remaining_cpuset, complete_cpuset); + while ((i = hwloc_bitmap_first(remaining_cpuset)) != (unsigned) -1) { + hwloc_bitmap_t puset; + + for (l = 0; l < infos[i].numcaches; l++) { + if (infos[i].cache[l].level == level && infos[i].cache[l].type == type) + break; + } + if (l == infos[i].numcaches) { + /* no cache Llevel of that type in i */ + hwloc_bitmap_clr(remaining_cpuset, i); + continue; + } + + puset = hwloc_bitmap_alloc(); + hwloc_bitmap_set(puset, i); + cache = hwloc_get_next_obj_covering_cpuset_by_type(topology, puset, otype, NULL); + hwloc_bitmap_free(puset); + + if (cache) { + /* Found cache above that PU, annotate if no such attribute yet */ + if (!hwloc_obj_get_info_by_name(cache, "Inclusive")) + hwloc_obj_add_info(cache, "Inclusive", infos[i].cache[l].inclusive ? "1" : "0"); + hwloc_bitmap_andnot(remaining_cpuset, remaining_cpuset, cache->cpuset); + } else { + /* Add the missing cache */ + hwloc_bitmap_t cache_cpuset; + unsigned packageid = infos[i].packageid; + unsigned cacheid = infos[i].cache[l].cacheid; + /* Now look for others sharing it */ + cache_cpuset = hwloc_bitmap_alloc(); + for (j = i; j < nbprocs; j++) { + unsigned l2; + for (l2 = 0; l2 < infos[j].numcaches; l2++) { + if (infos[j].cache[l2].level == level && infos[j].cache[l2].type == type) + break; + } + if (l2 == infos[j].numcaches) { + /* no cache Llevel of that type in j */ + hwloc_bitmap_clr(remaining_cpuset, j); + continue; + } + if (infos[j].packageid == packageid && infos[j].cache[l2].cacheid == cacheid) { + hwloc_bitmap_set(cache_cpuset, j); + hwloc_bitmap_clr(remaining_cpuset, j); + } + } + cache = hwloc_alloc_setup_object(topology, otype, HWLOC_UNKNOWN_INDEX); + cache->attr->cache.depth = level; + cache->attr->cache.size = infos[i].cache[l].size; + cache->attr->cache.linesize = infos[i].cache[l].linesize; + cache->attr->cache.associativity = infos[i].cache[l].ways; + cache->attr->cache.type = infos[i].cache[l].type; + cache->cpuset = cache_cpuset; + hwloc_obj_add_info(cache, "Inclusive", infos[i].cache[l].inclusive ? "1" : "0"); + hwloc_debug_2args_bitmap("os L%u cache %u has cpuset %s\n", + level, cacheid, cache_cpuset); + hwloc_insert_object_by_cpuset(topology, cache); + } + } + } + level--; + } + + /* FIXME: if KNL and L2 disabled, add tiles instead of L2 */ + + hwloc_bitmap_free(remaining_cpuset); + hwloc_bitmap_free(complete_cpuset); + + if (gotnuma) + topology->support.discovery->numa = 1; +} + +static int +look_procs(struct hwloc_backend *backend, struct procinfo *infos, int fulldiscovery, + unsigned highest_cpuid, unsigned highest_ext_cpuid, unsigned *features, enum cpuid_type cpuid_type, + int (*get_cpubind)(hwloc_topology_t topology, hwloc_cpuset_t set, int flags), + int (*set_cpubind)(hwloc_topology_t topology, hwloc_const_cpuset_t set, int flags)) +{ + struct hwloc_x86_backend_data_s *data = backend->private_data; + struct hwloc_topology *topology = backend->topology; + unsigned nbprocs = data->nbprocs; + hwloc_bitmap_t orig_cpuset = NULL; + hwloc_bitmap_t set = NULL; + unsigned i; + + if (!data->src_cpuiddump_path) { + orig_cpuset = hwloc_bitmap_alloc(); + if (get_cpubind(topology, orig_cpuset, HWLOC_CPUBIND_STRICT)) { + hwloc_bitmap_free(orig_cpuset); + return -1; + } + set = hwloc_bitmap_alloc(); + } + + for (i = 0; i < nbprocs; i++) { + struct cpuiddump *src_cpuiddump = NULL; + if (data->src_cpuiddump_path) { + src_cpuiddump = cpuiddump_read(data->src_cpuiddump_path, i); + if (!src_cpuiddump) + continue; + } else { + hwloc_bitmap_only(set, i); + hwloc_debug("binding to CPU%u\n", i); + if (set_cpubind(topology, set, HWLOC_CPUBIND_STRICT)) { + hwloc_debug("could not bind to CPU%u: %s\n", i, strerror(errno)); + continue; + } + } + + look_proc(backend, &infos[i], highest_cpuid, highest_ext_cpuid, features, cpuid_type, src_cpuiddump); + + if (data->src_cpuiddump_path) { + cpuiddump_free(src_cpuiddump); + } + } + + if (!data->src_cpuiddump_path) { + set_cpubind(topology, orig_cpuset, 0); + hwloc_bitmap_free(set); + hwloc_bitmap_free(orig_cpuset); + } + + if (!data->apicid_unique) + fulldiscovery = 0; + else + summarize(backend, infos, fulldiscovery); + return 0; +} + +#if defined HWLOC_FREEBSD_SYS && defined HAVE_CPUSET_SETID +#include +#include +typedef cpusetid_t hwloc_x86_os_state_t; +static void hwloc_x86_os_state_save(hwloc_x86_os_state_t *state, struct cpuiddump *src_cpuiddump) +{ + if (!src_cpuiddump) { + /* temporary make all cpus available during discovery */ + cpuset_getid(CPU_LEVEL_CPUSET, CPU_WHICH_PID, -1, state); + cpuset_setid(CPU_WHICH_PID, -1, 0); + } +} +static void hwloc_x86_os_state_restore(hwloc_x86_os_state_t *state, struct cpuiddump *src_cpuiddump) +{ + if (!src_cpuiddump) { + /* restore initial cpuset */ + cpuset_setid(CPU_WHICH_PID, -1, *state); + } +} +#else /* !defined HWLOC_FREEBSD_SYS || !defined HAVE_CPUSET_SETID */ +typedef void * hwloc_x86_os_state_t; +static void hwloc_x86_os_state_save(hwloc_x86_os_state_t *state __hwloc_attribute_unused, struct cpuiddump *src_cpuiddump __hwloc_attribute_unused) { } +static void hwloc_x86_os_state_restore(hwloc_x86_os_state_t *state __hwloc_attribute_unused, struct cpuiddump *src_cpuiddump __hwloc_attribute_unused) { } +#endif /* !defined HWLOC_FREEBSD_SYS || !defined HAVE_CPUSET_SETID */ + +/* GenuineIntel */ +#define INTEL_EBX ('G' | ('e'<<8) | ('n'<<16) | ('u'<<24)) +#define INTEL_EDX ('i' | ('n'<<8) | ('e'<<16) | ('I'<<24)) +#define INTEL_ECX ('n' | ('t'<<8) | ('e'<<16) | ('l'<<24)) + +/* AuthenticAMD */ +#define AMD_EBX ('A' | ('u'<<8) | ('t'<<16) | ('h'<<24)) +#define AMD_EDX ('e' | ('n'<<8) | ('t'<<16) | ('i'<<24)) +#define AMD_ECX ('c' | ('A'<<8) | ('M'<<16) | ('D'<<24)) + +/* HYGON "HygonGenuine" */ +#define HYGON_EBX ('H' | ('y'<<8) | ('g'<<16) | ('o'<<24)) +#define HYGON_EDX ('n' | ('G'<<8) | ('e'<<16) | ('n'<<24)) +#define HYGON_ECX ('u' | ('i'<<8) | ('n'<<16) | ('e'<<24)) + +/* (Zhaoxin) CentaurHauls */ +#define ZX_EBX ('C' | ('e'<<8) | ('n'<<16) | ('t'<<24)) +#define ZX_EDX ('a' | ('u'<<8) | ('r'<<16) | ('H'<<24)) +#define ZX_ECX ('a' | ('u'<<8) | ('l'<<16) | ('s'<<24)) +/* (Zhaoxin) Shanghai */ +#define SH_EBX (' ' | (' '<<8) | ('S'<<16) | ('h'<<24)) +#define SH_EDX ('a' | ('n'<<8) | ('g'<<16) | ('h'<<24)) +#define SH_ECX ('a' | ('i'<<8) | (' '<<16) | (' '<<24)) + +/* fake cpubind for when nbprocs=1 and no binding support */ +static int fake_get_cpubind(hwloc_topology_t topology __hwloc_attribute_unused, + hwloc_cpuset_t set __hwloc_attribute_unused, + int flags __hwloc_attribute_unused) +{ + return 0; +} +static int fake_set_cpubind(hwloc_topology_t topology __hwloc_attribute_unused, + hwloc_const_cpuset_t set __hwloc_attribute_unused, + int flags __hwloc_attribute_unused) +{ + return 0; +} + +static +int hwloc_look_x86(struct hwloc_backend *backend, int fulldiscovery) +{ + struct hwloc_x86_backend_data_s *data = backend->private_data; + unsigned nbprocs = data->nbprocs; + unsigned eax, ebx, ecx = 0, edx; + unsigned i; + unsigned highest_cpuid; + unsigned highest_ext_cpuid; + /* This stores cpuid features with the same indexing as Linux */ + unsigned features[10] = { 0 }; + struct procinfo *infos = NULL; + enum cpuid_type cpuid_type = unknown; + hwloc_x86_os_state_t os_state; + struct hwloc_binding_hooks hooks; + struct hwloc_topology_support support; + struct hwloc_topology_membind_support memsupport __hwloc_attribute_unused; + int (*get_cpubind)(hwloc_topology_t topology, hwloc_cpuset_t set, int flags) = NULL; + int (*set_cpubind)(hwloc_topology_t topology, hwloc_const_cpuset_t set, int flags) = NULL; + struct cpuiddump *src_cpuiddump = NULL; + int ret = -1; + + if (data->src_cpuiddump_path) { + /* just read cpuid from the dump */ + src_cpuiddump = cpuiddump_read(data->src_cpuiddump_path, 0); + if (!src_cpuiddump) + goto out; + + } else { + /* otherwise check if binding works */ + memset(&hooks, 0, sizeof(hooks)); + support.membind = &memsupport; + hwloc_set_native_binding_hooks(&hooks, &support); + if (hooks.get_thisthread_cpubind && hooks.set_thisthread_cpubind) { + get_cpubind = hooks.get_thisthread_cpubind; + set_cpubind = hooks.set_thisthread_cpubind; + } else if (hooks.get_thisproc_cpubind && hooks.set_thisproc_cpubind) { + /* FIXME: if called by a multithreaded program, we will restore the original process binding + * for each thread instead of their own original thread binding. + * See issue #158. + */ + get_cpubind = hooks.get_thisproc_cpubind; + set_cpubind = hooks.set_thisproc_cpubind; + } else { + /* we need binding support if there are multiple PUs */ + if (nbprocs > 1) + goto out; + get_cpubind = fake_get_cpubind; + set_cpubind = fake_set_cpubind; + } + } + + if (!src_cpuiddump && !hwloc_have_x86_cpuid()) + goto out; + + infos = calloc(nbprocs, sizeof(struct procinfo)); + if (NULL == infos) + goto out; + for (i = 0; i < nbprocs; i++) { + infos[i].nodeid = (unsigned) -1; + infos[i].packageid = (unsigned) -1; + infos[i].dieid = (unsigned) -1; + infos[i].unitid = (unsigned) -1; + infos[i].coreid = (unsigned) -1; + infos[i].threadid = (unsigned) -1; + } + + eax = 0x00; + cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump); + highest_cpuid = eax; + if (ebx == INTEL_EBX && ecx == INTEL_ECX && edx == INTEL_EDX) + cpuid_type = intel; + else if (ebx == AMD_EBX && ecx == AMD_ECX && edx == AMD_EDX) + cpuid_type = amd; + else if ((ebx == ZX_EBX && ecx == ZX_ECX && edx == ZX_EDX) + || (ebx == SH_EBX && ecx == SH_ECX && edx == SH_EDX)) + cpuid_type = zhaoxin; + else if (ebx == HYGON_EBX && ecx == HYGON_ECX && edx == HYGON_EDX) + cpuid_type = hygon; + + hwloc_debug("highest cpuid %x, cpuid type %u\n", highest_cpuid, cpuid_type); + if (highest_cpuid < 0x01) { + goto out_with_infos; + } + + eax = 0x01; + cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump); + features[0] = edx; + features[4] = ecx; + + eax = 0x80000000; + cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump); + highest_ext_cpuid = eax; + + hwloc_debug("highest extended cpuid %x\n", highest_ext_cpuid); + + if (highest_cpuid >= 0x7) { + eax = 0x7; + ecx = 0; + cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump); + features[9] = ebx; + } + + if (cpuid_type != intel && highest_ext_cpuid >= 0x80000001) { + eax = 0x80000001; + cpuid_or_from_dump(&eax, &ebx, &ecx, &edx, src_cpuiddump); + features[1] = edx; + features[6] = ecx; + } + + hwloc_x86_os_state_save(&os_state, src_cpuiddump); + + ret = look_procs(backend, infos, fulldiscovery, + highest_cpuid, highest_ext_cpuid, features, cpuid_type, + get_cpubind, set_cpubind); + if (!ret) + /* success, we're done */ + goto out_with_os_state; + + if (nbprocs == 1) { + /* only one processor, no need to bind */ + look_proc(backend, &infos[0], highest_cpuid, highest_ext_cpuid, features, cpuid_type, src_cpuiddump); + summarize(backend, infos, fulldiscovery); + ret = 0; + } + +out_with_os_state: + hwloc_x86_os_state_restore(&os_state, src_cpuiddump); + +out_with_infos: + if (NULL != infos) { + for (i = 0; i < nbprocs; i++) { + free(infos[i].cache); + free(infos[i].otherids); + } + free(infos); + } + +out: + if (src_cpuiddump) + cpuiddump_free(src_cpuiddump); + return ret; +} + +static int +hwloc_x86_discover(struct hwloc_backend *backend) +{ + struct hwloc_x86_backend_data_s *data = backend->private_data; + struct hwloc_topology *topology = backend->topology; + int alreadypus = 0; + int ret; + +#if HAVE_DECL_RUNNING_ON_VALGRIND + if (RUNNING_ON_VALGRIND && !data->src_cpuiddump_path) { + fprintf(stderr, "hwloc x86 backend cannot work under Valgrind, disabling.\n" + "May be reenabled by dumping CPUIDs with hwloc-gather-cpuid\n" + "and reloading them under Valgrind with HWLOC_CPUID_PATH.\n"); + return 0; + } +#endif + + if (data->src_cpuiddump_path) { + assert(data->nbprocs > 0); /* enforced by hwloc_x86_component_instantiate() */ + topology->support.discovery->pu = 1; + } else { + int nbprocs = hwloc_fallback_nbprocessors(topology); + if (nbprocs >= 1) + topology->support.discovery->pu = 1; + else + nbprocs = 1; + data->nbprocs = (unsigned) nbprocs; + } + + if (topology->levels[0][0]->cpuset) { + /* somebody else discovered things */ + if (topology->nb_levels == 2 && topology->level_nbobjects[1] == data->nbprocs) { + /* only PUs were discovered, as much as we would, complete the topology with everything else */ + alreadypus = 1; + goto fulldiscovery; + } + + /* several object types were added, we can't easily complete, just do partial discovery */ + hwloc_topology_reconnect(topology, 0); + ret = hwloc_look_x86(backend, 0); + if (ret) + hwloc_obj_add_info(topology->levels[0][0], "Backend", "x86"); + return 0; + } else { + /* topology is empty, initialize it */ + hwloc_alloc_root_sets(topology->levels[0][0]); + } + +fulldiscovery: + if (hwloc_look_x86(backend, 1) < 0) { + /* if failed, create PUs */ + if (!alreadypus) + hwloc_setup_pu_level(topology, data->nbprocs); + } + + hwloc_obj_add_info(topology->levels[0][0], "Backend", "x86"); + + if (!data->src_cpuiddump_path) { /* CPUID dump works for both x86 and x86_64 */ +#ifdef HAVE_UNAME + hwloc_add_uname_info(topology, NULL); /* we already know is_thissystem() is true */ +#else + /* uname isn't available, manually setup the "Architecture" info */ +#ifdef HWLOC_X86_64_ARCH + hwloc_obj_add_info(topology->levels[0][0], "Architecture", "x86_64"); +#else + hwloc_obj_add_info(topology->levels[0][0], "Architecture", "x86"); +#endif +#endif + } + + return 1; +} + +static int +hwloc_x86_check_cpuiddump_input(const char *src_cpuiddump_path, hwloc_bitmap_t set) +{ + +#if !(defined HWLOC_WIN_SYS && !defined __MINGW32__ && !defined __CYGWIN__) /* needs a lot of work */ + struct dirent *dirent; + DIR *dir; + FILE *file; + char line [32]; + + dir = opendir(src_cpuiddump_path); + if (!dir) + return -1; + + char path[strlen(src_cpuiddump_path) + strlen("/hwloc-cpuid-info") + 1]; + sprintf(path, "%s/hwloc-cpuid-info", src_cpuiddump_path); + file = fopen(path, "r"); + if (!file) { + fprintf(stderr, "Couldn't open dumped cpuid summary %s\n", path); + goto out_with_dir; + } + if (!fgets(line, sizeof(line), file)) { + fprintf(stderr, "Found read dumped cpuid summary in %s\n", path); + fclose(file); + goto out_with_dir; + } + fclose(file); + if (strcmp(line, "Architecture: x86\n")) { + fprintf(stderr, "Found non-x86 dumped cpuid summary in %s: %s\n", path, line); + goto out_with_dir; + } + + while ((dirent = readdir(dir)) != NULL) { + if (!strncmp(dirent->d_name, "pu", 2)) { + char *end; + unsigned long idx = strtoul(dirent->d_name+2, &end, 10); + if (!*end) + hwloc_bitmap_set(set, idx); + else + fprintf(stderr, "Ignoring invalid dirent `%s' in dumped cpuid directory `%s'\n", + dirent->d_name, src_cpuiddump_path); + } + } + closedir(dir); + + if (hwloc_bitmap_iszero(set)) { + fprintf(stderr, "Did not find any valid pu%%u entry in dumped cpuid directory `%s'\n", + src_cpuiddump_path); + return -1; + } else if (hwloc_bitmap_last(set) != hwloc_bitmap_weight(set) - 1) { + /* The x86 backends enforces contigous set of PUs starting at 0 so far */ + fprintf(stderr, "Found non-contigous pu%%u range in dumped cpuid directory `%s'\n", + src_cpuiddump_path); + return -1; + } + + return 0; + +out_with_dir: + closedir(dir); +#endif /* HWLOC_WIN_SYS & !__MINGW32__ needs a lot of work */ + return -1; +} + +static void +hwloc_x86_backend_disable(struct hwloc_backend *backend) +{ + struct hwloc_x86_backend_data_s *data = backend->private_data; + hwloc_bitmap_free(data->apicid_set); + free(data->src_cpuiddump_path); + free(data); +} + +static struct hwloc_backend * +hwloc_x86_component_instantiate(struct hwloc_disc_component *component, + const void *_data1 __hwloc_attribute_unused, + const void *_data2 __hwloc_attribute_unused, + const void *_data3 __hwloc_attribute_unused) +{ + struct hwloc_backend *backend; + struct hwloc_x86_backend_data_s *data; + const char *src_cpuiddump_path; + + backend = hwloc_backend_alloc(component); + if (!backend) + goto out; + + data = malloc(sizeof(*data)); + if (!data) { + errno = ENOMEM; + goto out_with_backend; + } + + backend->private_data = data; + backend->discover = hwloc_x86_discover; + backend->disable = hwloc_x86_backend_disable; + + /* default values */ + data->is_knl = 0; + data->apicid_set = hwloc_bitmap_alloc(); + data->apicid_unique = 1; + data->src_cpuiddump_path = NULL; + + src_cpuiddump_path = getenv("HWLOC_CPUID_PATH"); + if (src_cpuiddump_path) { + hwloc_bitmap_t set = hwloc_bitmap_alloc(); + if (!hwloc_x86_check_cpuiddump_input(src_cpuiddump_path, set)) { + backend->is_thissystem = 0; + data->src_cpuiddump_path = strdup(src_cpuiddump_path); + assert(!hwloc_bitmap_iszero(set)); /* enforced by hwloc_x86_check_cpuiddump_input() */ + data->nbprocs = hwloc_bitmap_weight(set); + } else { + fprintf(stderr, "Ignoring dumped cpuid directory.\n"); + } + hwloc_bitmap_free(set); + } + + return backend; + + out_with_backend: + free(backend); + out: + return NULL; +} + +static struct hwloc_disc_component hwloc_x86_disc_component = { + HWLOC_DISC_COMPONENT_TYPE_CPU, + "x86", + HWLOC_DISC_COMPONENT_TYPE_GLOBAL, + hwloc_x86_component_instantiate, + 45, /* between native and no_os */ + 1, + NULL +}; + +const struct hwloc_component hwloc_x86_component = { + HWLOC_COMPONENT_ABI, + NULL, NULL, + HWLOC_COMPONENT_TYPE_DISC, + 0, + &hwloc_x86_disc_component +}; diff --git a/src/3rdparty/hwloc/src/topology-xml-nolibxml.c b/src/3rdparty/hwloc/src/topology-xml-nolibxml.c new file mode 100644 index 00000000..5a0d02da --- /dev/null +++ b/src/3rdparty/hwloc/src/topology-xml-nolibxml.c @@ -0,0 +1,919 @@ +/* + * Copyright © 2009 CNRS + * Copyright © 2009-2018 Inria. All rights reserved. + * Copyright © 2009-2011 Université Bordeaux + * Copyright © 2009-2011 Cisco Systems, Inc. All rights reserved. + * See COPYING in top-level directory. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif + +/******************* + * Import routines * + *******************/ + +struct hwloc__nolibxml_backend_data_s { + size_t buflen; /* size of both buffer and copy buffers, set during backend_init() */ + char *buffer; /* allocated and filled during backend_init() */ + char *copy; /* allocated during backend_init(), used later during actual parsing */ +}; + +typedef struct hwloc__nolibxml_import_state_data_s { + char *tagbuffer; /* buffer containing the next tag */ + char *attrbuffer; /* buffer containing the next attribute of the current node */ + char *tagname; /* tag name of the current node */ + int closed; /* set if the current node is auto-closing */ +} __hwloc_attribute_may_alias * hwloc__nolibxml_import_state_data_t; + +static char * +hwloc__nolibxml_import_ignore_spaces(char *buffer) +{ + return buffer + strspn(buffer, " \t\n"); +} + +static int +hwloc__nolibxml_import_next_attr(hwloc__xml_import_state_t state, char **namep, char **valuep) +{ + hwloc__nolibxml_import_state_data_t nstate = (void*) state->data; + size_t namelen; + size_t len, escaped; + char *buffer, *value, *end; + + if (!nstate->attrbuffer) + return -1; + + /* find the beginning of an attribute */ + buffer = hwloc__nolibxml_import_ignore_spaces(nstate->attrbuffer); + namelen = strspn(buffer, "abcdefghijklmnopqrstuvwxyz_"); + if (buffer[namelen] != '=' || buffer[namelen+1] != '\"') + return -1; + buffer[namelen] = '\0'; + *namep = buffer; + + /* find the beginning of its value, and unescape it */ + *valuep = value = buffer+namelen+2; + len = 0; escaped = 0; + while (value[len+escaped] != '\"') { + if (value[len+escaped] == '&') { + if (!strncmp(&value[1+len+escaped], "#10;", 4)) { + escaped += 4; + value[len] = '\n'; + } else if (!strncmp(&value[1+len+escaped], "#13;", 4)) { + escaped += 4; + value[len] = '\r'; + } else if (!strncmp(&value[1+len+escaped], "#9;", 3)) { + escaped += 3; + value[len] = '\t'; + } else if (!strncmp(&value[1+len+escaped], "quot;", 5)) { + escaped += 5; + value[len] = '\"'; + } else if (!strncmp(&value[1+len+escaped], "lt;", 3)) { + escaped += 3; + value[len] = '<'; + } else if (!strncmp(&value[1+len+escaped], "gt;", 3)) { + escaped += 3; + value[len] = '>'; + } else if (!strncmp(&value[1+len+escaped], "amp;", 4)) { + escaped += 4; + value[len] = '&'; + } else { + return -1; + } + } else { + value[len] = value[len+escaped]; + } + len++; + if (value[len+escaped] == '\0') + return -1; + } + value[len] = '\0'; + + /* find next attribute */ + end = &value[len+escaped+1]; /* skip the ending " */ + nstate->attrbuffer = hwloc__nolibxml_import_ignore_spaces(end); + return 0; +} + +static int +hwloc__nolibxml_import_find_child(hwloc__xml_import_state_t state, + hwloc__xml_import_state_t childstate, + char **tagp) +{ + hwloc__nolibxml_import_state_data_t nstate = (void*) state->data; + hwloc__nolibxml_import_state_data_t nchildstate = (void*) childstate->data; + char *buffer = nstate->tagbuffer; + char *end; + char *tag; + size_t namelen; + + childstate->parent = state; + childstate->global = state->global; + + /* auto-closed tags have no children */ + if (nstate->closed) + return 0; + + /* find the beginning of the tag */ + buffer = hwloc__nolibxml_import_ignore_spaces(buffer); + if (buffer[0] != '<') + return -1; + buffer++; + + /* if closing tag, return nothing and do not advance */ + if (buffer[0] == '/') + return 0; + + /* normal tag */ + tag = nchildstate->tagname = buffer; + + /* find the end, mark it and return it */ + end = strchr(buffer, '>'); + if (!end) + return -1; + end[0] = '\0'; + nchildstate->tagbuffer = end+1; + + /* handle auto-closing tags */ + if (end[-1] == '/') { + nchildstate->closed = 1; + end[-1] = '\0'; + } else + nchildstate->closed = 0; + + /* find attributes */ + namelen = strspn(buffer, "abcdefghijklmnopqrstuvwxyz1234567890_"); + + if (buffer[namelen] == '\0') { + /* no attributes */ + nchildstate->attrbuffer = NULL; + *tagp = tag; + return 1; + } + + if (buffer[namelen] != ' ') + return -1; + + /* found a space, likely starting attributes */ + buffer[namelen] = '\0'; + nchildstate->attrbuffer = buffer+namelen+1; + *tagp = tag; + return 1; +} + +static int +hwloc__nolibxml_import_close_tag(hwloc__xml_import_state_t state) +{ + hwloc__nolibxml_import_state_data_t nstate = (void*) state->data; + char *buffer = nstate->tagbuffer; + char *end; + + /* auto-closed tags need nothing */ + if (nstate->closed) + return 0; + + /* find the beginning of the tag */ + buffer = hwloc__nolibxml_import_ignore_spaces(buffer); + if (buffer[0] != '<') + return -1; + buffer++; + + /* find the end, mark it and return it to the parent */ + end = strchr(buffer, '>'); + if (!end) + return -1; + end[0] = '\0'; + nstate->tagbuffer = end+1; + + /* if closing tag, return nothing */ + if (buffer[0] != '/' || strcmp(buffer+1, nstate->tagname) ) + return -1; + return 0; +} + +static void +hwloc__nolibxml_import_close_child(hwloc__xml_import_state_t state) +{ + hwloc__nolibxml_import_state_data_t nstate = (void*) state->data; + hwloc__nolibxml_import_state_data_t nparent = (void*) state->parent->data; + nparent->tagbuffer = nstate->tagbuffer; +} + +static int +hwloc__nolibxml_import_get_content(hwloc__xml_import_state_t state, + char **beginp, size_t expected_length) +{ + hwloc__nolibxml_import_state_data_t nstate = (void*) state->data; + char *buffer = nstate->tagbuffer; + size_t length; + char *end; + + /* auto-closed tags have no content */ + if (nstate->closed) { + if (expected_length) + return -1; + *beginp = (char *) ""; + return 0; + } + + /* find the next tag, where the content ends */ + end = strchr(buffer, '<'); + if (!end) + return -1; + + length = (size_t) (end-buffer); + if (length != expected_length) + return -1; + nstate->tagbuffer = end; + *end = '\0'; /* mark as 0-terminated for now */ + *beginp = buffer; + return 1; +} + +static void +hwloc__nolibxml_import_close_content(hwloc__xml_import_state_t state) +{ + /* put back the '<' that we overwrote to 0-terminate the content */ + hwloc__nolibxml_import_state_data_t nstate = (void*) state->data; + if (!nstate->closed) + *nstate->tagbuffer = '<'; +} + +static int +hwloc_nolibxml_look_init(struct hwloc_xml_backend_data_s *bdata, + struct hwloc__xml_import_state_s *state) +{ + hwloc__nolibxml_import_state_data_t nstate = (void*) state->data; + struct hwloc__nolibxml_backend_data_s *nbdata = bdata->data; + unsigned major, minor; + char *end; + char *buffer; + + HWLOC_BUILD_ASSERT(sizeof(*nstate) <= sizeof(state->data)); + + /* use a copy in the temporary buffer, we may modify during parsing */ + buffer = nbdata->copy; + memcpy(buffer, nbdata->buffer, nbdata->buflen); + + /* skip headers */ + while (!strncmp(buffer, "", &major, &minor) == 2) { + bdata->version_major = major; + bdata->version_minor = minor; + end = strchr(buffer, '>') + 1; + } else if (!strncmp(buffer, "", 10)) { + bdata->version_major = 1; + bdata->version_minor = 0; + end = buffer + 10; + } else if (!strncmp(buffer, "", 6)) { + bdata->version_major = 0; + bdata->version_minor = 9; + end = buffer + 6; + } else + goto failed; + + state->global->next_attr = hwloc__nolibxml_import_next_attr; + state->global->find_child = hwloc__nolibxml_import_find_child; + state->global->close_tag = hwloc__nolibxml_import_close_tag; + state->global->close_child = hwloc__nolibxml_import_close_child; + state->global->get_content = hwloc__nolibxml_import_get_content; + state->global->close_content = hwloc__nolibxml_import_close_content; + state->parent = NULL; + nstate->closed = 0; + nstate->tagbuffer = end; + nstate->tagname = (char *) "topology"; + nstate->attrbuffer = NULL; + return 0; /* success */ + + failed: + return -1; /* failed */ +} + +/* can be called at the end of the import (to cleanup things early), + * or by backend_exit() if load failed for other reasons. + */ +static void +hwloc_nolibxml_free_buffers(struct hwloc_xml_backend_data_s *bdata) +{ + struct hwloc__nolibxml_backend_data_s *nbdata = bdata->data; + if (nbdata->buffer) { + free(nbdata->buffer); + nbdata->buffer = NULL; + } + if (nbdata->copy) { + free(nbdata->copy); + nbdata->copy = NULL; + } +} + +static void +hwloc_nolibxml_look_done(struct hwloc_xml_backend_data_s *bdata, int result) +{ + hwloc_nolibxml_free_buffers(bdata); + + if (result < 0 && hwloc__xml_verbose()) + fprintf(stderr, "Failed to parse XML input with the minimalistic parser. If it was not\n" + "generated by hwloc, try enabling full XML support with libxml2.\n"); +} + +/******************** + * Backend routines * + ********************/ + +static void +hwloc_nolibxml_backend_exit(struct hwloc_xml_backend_data_s *bdata) +{ + struct hwloc__nolibxml_backend_data_s *nbdata = bdata->data; + hwloc_nolibxml_free_buffers(bdata); + free(nbdata); +} + +static int +hwloc_nolibxml_read_file(const char *xmlpath, char **bufferp, size_t *buflenp) +{ + FILE * file; + size_t buflen, offset, readlen; + struct stat statbuf; + char *buffer, *tmp; + size_t ret; + + if (!strcmp(xmlpath, "-")) + xmlpath = "/dev/stdin"; + + file = fopen(xmlpath, "r"); + if (!file) + goto out; + + /* find the required buffer size for regular files, or use 4k when unknown, we'll realloc later if needed */ + buflen = 4096; + if (!stat(xmlpath, &statbuf)) + if (S_ISREG(statbuf.st_mode)) + buflen = statbuf.st_size+1; /* one additional byte so that the first fread() gets EOF too */ + + buffer = malloc(buflen+1); /* one more byte for the ending \0 */ + if (!buffer) + goto out_with_file; + + offset = 0; readlen = buflen; + while (1) { + ret = fread(buffer+offset, 1, readlen, file); + + offset += ret; + buffer[offset] = 0; + + if (ret != readlen) + break; + + buflen *= 2; + tmp = realloc(buffer, buflen+1); + if (!tmp) + goto out_with_buffer; + buffer = tmp; + readlen = buflen/2; + } + + fclose(file); + *bufferp = buffer; + *buflenp = offset+1; + return 0; + + out_with_buffer: + free(buffer); + out_with_file: + fclose(file); + out: + return -1; +} + +static int +hwloc_nolibxml_backend_init(struct hwloc_xml_backend_data_s *bdata, + const char *xmlpath, const char *xmlbuffer, int xmlbuflen) +{ + struct hwloc__nolibxml_backend_data_s *nbdata = malloc(sizeof(*nbdata)); + + if (!nbdata) + goto out; + bdata->data = nbdata; + + if (xmlbuffer) { + nbdata->buffer = malloc(xmlbuflen+1); + if (!nbdata->buffer) + goto out_with_nbdata; + nbdata->buflen = xmlbuflen+1; + memcpy(nbdata->buffer, xmlbuffer, xmlbuflen); + nbdata->buffer[xmlbuflen] = '\0'; + + } else { + int err = hwloc_nolibxml_read_file(xmlpath, &nbdata->buffer, &nbdata->buflen); + if (err < 0) + goto out_with_nbdata; + } + + /* allocate a temporary copy buffer that we may modify during parsing */ + nbdata->copy = malloc(nbdata->buflen+1); + if (!nbdata->copy) + goto out_with_buffer; + nbdata->copy[nbdata->buflen] = '\0'; + + bdata->look_init = hwloc_nolibxml_look_init; + bdata->look_done = hwloc_nolibxml_look_done; + bdata->backend_exit = hwloc_nolibxml_backend_exit; + return 0; + +out_with_buffer: + free(nbdata->buffer); +out_with_nbdata: + free(nbdata); +out: + return -1; +} + +static int +hwloc_nolibxml_import_diff(struct hwloc__xml_import_state_s *state, + const char *xmlpath, const char *xmlbuffer, int xmlbuflen, + hwloc_topology_diff_t *firstdiffp, char **refnamep) +{ + hwloc__nolibxml_import_state_data_t nstate = (void*) state->data; + struct hwloc__xml_import_state_s childstate; + char *refname = NULL; + char *buffer, *tmp, *tag; + size_t buflen; + int ret; + + HWLOC_BUILD_ASSERT(sizeof(*nstate) <= sizeof(state->data)); + + if (xmlbuffer) { + buffer = malloc(xmlbuflen); + if (!buffer) + goto out; + memcpy(buffer, xmlbuffer, xmlbuflen); + buflen = xmlbuflen; + + } else { + ret = hwloc_nolibxml_read_file(xmlpath, &buffer, &buflen); + if (ret < 0) + goto out; + } + + /* skip headers */ + tmp = buffer; + while (!strncmp(tmp, "global->next_attr = hwloc__nolibxml_import_next_attr; + state->global->find_child = hwloc__nolibxml_import_find_child; + state->global->close_tag = hwloc__nolibxml_import_close_tag; + state->global->close_child = hwloc__nolibxml_import_close_child; + state->global->get_content = hwloc__nolibxml_import_get_content; + state->global->close_content = hwloc__nolibxml_import_close_content; + state->parent = NULL; + nstate->closed = 0; + nstate->tagbuffer = tmp; + nstate->tagname = NULL; + nstate->attrbuffer = NULL; + + /* find root */ + ret = hwloc__nolibxml_import_find_child(state, &childstate, &tag); + if (ret < 0) + goto out_with_buffer; + if (!tag || strcmp(tag, "topologydiff")) + goto out_with_buffer; + + while (1) { + char *attrname, *attrvalue; + if (hwloc__nolibxml_import_next_attr(&childstate, &attrname, &attrvalue) < 0) + break; + if (!strcmp(attrname, "refname")) { + free(refname); + refname = strdup(attrvalue); + } else + goto out_with_buffer; + } + + ret = hwloc__xml_import_diff(&childstate, firstdiffp); + if (refnamep && !ret) + *refnamep = refname; + else + free(refname); + + free(buffer); + return ret; + +out_with_buffer: + free(buffer); + free(refname); +out: + return -1; +} + +/******************* + * Export routines * + *******************/ + +typedef struct hwloc__nolibxml_export_state_data_s { + char *buffer; /* (moving) buffer where to write */ + size_t written; /* how many bytes were written (or would have be written if not truncated) */ + size_t remaining; /* how many bytes are still available in the buffer */ + unsigned indent; /* indentation level for the next line */ + unsigned nr_children; + unsigned has_content; +} __hwloc_attribute_may_alias * hwloc__nolibxml_export_state_data_t; + +static void +hwloc__nolibxml_export_update_buffer(hwloc__nolibxml_export_state_data_t ndata, int res) +{ + if (res >= 0) { + ndata->written += res; + if (res >= (int) ndata->remaining) + res = ndata->remaining>0 ? (int)ndata->remaining-1 : 0; + ndata->buffer += res; + ndata->remaining -= res; + } +} + +static char * +hwloc__nolibxml_export_escape_string(const char *src) +{ + size_t fulllen, sublen; + char *escaped, *dst; + + fulllen = strlen(src); + + sublen = strcspn(src, "\n\r\t\"<>&"); + if (sublen == fulllen) + return NULL; /* nothing to escape */ + + escaped = malloc(fulllen*6+1); /* escaped chars are replaced by at most 6 char */ + dst = escaped; + + memcpy(dst, src, sublen); + src += sublen; + dst += sublen; + + while (*src) { + int replen; + switch (*src) { + case '\n': strcpy(dst, " "); replen=5; break; + case '\r': strcpy(dst, " "); replen=5; break; + case '\t': strcpy(dst, " "); replen=4; break; + case '\"': strcpy(dst, """); replen=6; break; + case '<': strcpy(dst, "<"); replen=4; break; + case '>': strcpy(dst, ">"); replen=4; break; + case '&': strcpy(dst, "&"); replen=5; break; + default: replen=0; break; + } + dst+=replen; src++; + + sublen = strcspn(src, "\n\r\t\"<>&"); + memcpy(dst, src, sublen); + src += sublen; + dst += sublen; + } + + *dst = 0; + return escaped; +} + +static void +hwloc__nolibxml_export_new_child(hwloc__xml_export_state_t parentstate, + hwloc__xml_export_state_t state, + const char *name) +{ + hwloc__nolibxml_export_state_data_t npdata = (void *) parentstate->data; + hwloc__nolibxml_export_state_data_t ndata = (void *) state->data; + int res; + + assert(!npdata->has_content); + if (!npdata->nr_children) { + res = hwloc_snprintf(npdata->buffer, npdata->remaining, ">\n"); + hwloc__nolibxml_export_update_buffer(npdata, res); + } + npdata->nr_children++; + + state->parent = parentstate; + state->new_child = parentstate->new_child; + state->new_prop = parentstate->new_prop; + state->add_content = parentstate->add_content; + state->end_object = parentstate->end_object; + state->global = parentstate->global; + + ndata->buffer = npdata->buffer; + ndata->written = npdata->written; + ndata->remaining = npdata->remaining; + ndata->indent = npdata->indent + 2; + + ndata->nr_children = 0; + ndata->has_content = 0; + + res = hwloc_snprintf(ndata->buffer, ndata->remaining, "%*s<%s", (int) npdata->indent, "", name); + hwloc__nolibxml_export_update_buffer(ndata, res); +} + +static void +hwloc__nolibxml_export_new_prop(hwloc__xml_export_state_t state, const char *name, const char *value) +{ + hwloc__nolibxml_export_state_data_t ndata = (void *) state->data; + char *escaped = hwloc__nolibxml_export_escape_string(value); + int res = hwloc_snprintf(ndata->buffer, ndata->remaining, " %s=\"%s\"", name, escaped ? (const char *) escaped : value); + hwloc__nolibxml_export_update_buffer(ndata, res); + free(escaped); +} + +static void +hwloc__nolibxml_export_end_object(hwloc__xml_export_state_t state, const char *name) +{ + hwloc__nolibxml_export_state_data_t ndata = (void *) state->data; + hwloc__nolibxml_export_state_data_t npdata = (void *) state->parent->data; + int res; + + assert (!(ndata->has_content && ndata->nr_children)); + if (ndata->has_content) { + res = hwloc_snprintf(ndata->buffer, ndata->remaining, "\n", name); + } else if (ndata->nr_children) { + res = hwloc_snprintf(ndata->buffer, ndata->remaining, "%*s\n", (int) npdata->indent, "", name); + } else { + res = hwloc_snprintf(ndata->buffer, ndata->remaining, "/>\n"); + } + hwloc__nolibxml_export_update_buffer(ndata, res); + + npdata->buffer = ndata->buffer; + npdata->written = ndata->written; + npdata->remaining = ndata->remaining; +} + +static void +hwloc__nolibxml_export_add_content(hwloc__xml_export_state_t state, const char *buffer, size_t length) +{ + hwloc__nolibxml_export_state_data_t ndata = (void *) state->data; + int res; + + assert(!ndata->nr_children); + if (!ndata->has_content) { + res = hwloc_snprintf(ndata->buffer, ndata->remaining, ">"); + hwloc__nolibxml_export_update_buffer(ndata, res); + } + ndata->has_content = 1; + + res = hwloc_snprintf(ndata->buffer, ndata->remaining, buffer, length); + hwloc__nolibxml_export_update_buffer(ndata, res); +} + +static size_t +hwloc___nolibxml_prepare_export(hwloc_topology_t topology, struct hwloc__xml_export_data_s *edata, + char *xmlbuffer, int buflen, unsigned long flags) +{ + struct hwloc__xml_export_state_s state, childstate; + hwloc__nolibxml_export_state_data_t ndata = (void *) &state.data; + int v1export = flags & HWLOC_TOPOLOGY_EXPORT_XML_FLAG_V1; + int res; + + HWLOC_BUILD_ASSERT(sizeof(*ndata) <= sizeof(state.data)); + + state.new_child = hwloc__nolibxml_export_new_child; + state.new_prop = hwloc__nolibxml_export_new_prop; + state.add_content = hwloc__nolibxml_export_add_content; + state.end_object = hwloc__nolibxml_export_end_object; + state.global = edata; + + ndata->indent = 0; + ndata->written = 0; + ndata->buffer = xmlbuffer; + ndata->remaining = buflen; + + ndata->nr_children = 1; /* don't close a non-existing previous tag when opening the topology tag */ + ndata->has_content = 0; + + res = hwloc_snprintf(ndata->buffer, ndata->remaining, + "\n" + "\n", v1export ? "hwloc.dtd" : "hwloc2.dtd"); + hwloc__nolibxml_export_update_buffer(ndata, res); + hwloc__nolibxml_export_new_child(&state, &childstate, "topology"); + if (!(flags & HWLOC_TOPOLOGY_EXPORT_XML_FLAG_V1)) + hwloc__nolibxml_export_new_prop(&childstate, "version", "2.0"); + hwloc__xml_export_topology (&childstate, topology, flags); + hwloc__nolibxml_export_end_object(&childstate, "topology"); + + return ndata->written+1; /* ending \0 */ +} + +static int +hwloc_nolibxml_export_buffer(hwloc_topology_t topology, struct hwloc__xml_export_data_s *edata, + char **bufferp, int *buflenp, unsigned long flags) +{ + char *buffer; + size_t bufferlen, res; + + bufferlen = 16384; /* random guess for large enough default */ + buffer = malloc(bufferlen); + if (!buffer) + return -1; + res = hwloc___nolibxml_prepare_export(topology, edata, buffer, (int)bufferlen, flags); + + if (res > bufferlen) { + char *tmp = realloc(buffer, res); + if (!tmp) { + free(buffer); + return -1; + } + buffer = tmp; + hwloc___nolibxml_prepare_export(topology, edata, buffer, (int)res, flags); + } + + *bufferp = buffer; + *buflenp = (int)res; + return 0; +} + +static int +hwloc_nolibxml_export_file(hwloc_topology_t topology, struct hwloc__xml_export_data_s *edata, + const char *filename, unsigned long flags) +{ + FILE *file; + char *buffer; + int bufferlen; + int ret; + + ret = hwloc_nolibxml_export_buffer(topology, edata, &buffer, &bufferlen, flags); + if (ret < 0) + return -1; + + if (!strcmp(filename, "-")) { + file = stdout; + } else { + file = fopen(filename, "w"); + if (!file) { + free(buffer); + return -1; + } + } + + ret = (int)fwrite(buffer, 1, bufferlen-1 /* don't write the ending \0 */, file); + if (ret == bufferlen-1) { + ret = 0; + } else { + errno = ferror(file); + ret = -1; + } + + free(buffer); + + if (file != stdout) + fclose(file); + return ret; +} + +static size_t +hwloc___nolibxml_prepare_export_diff(hwloc_topology_diff_t diff, const char *refname, char *xmlbuffer, int buflen) +{ + struct hwloc__xml_export_state_s state, childstate; + hwloc__nolibxml_export_state_data_t ndata = (void *) &state.data; + int res; + + HWLOC_BUILD_ASSERT(sizeof(*ndata) <= sizeof(state.data)); + + state.new_child = hwloc__nolibxml_export_new_child; + state.new_prop = hwloc__nolibxml_export_new_prop; + state.add_content = hwloc__nolibxml_export_add_content; + state.end_object = hwloc__nolibxml_export_end_object; + + ndata->indent = 0; + ndata->written = 0; + ndata->buffer = xmlbuffer; + ndata->remaining = buflen; + + ndata->nr_children = 1; /* don't close a non-existing previous tag when opening the topology tag */ + ndata->has_content = 0; + + res = hwloc_snprintf(ndata->buffer, ndata->remaining, + "\n" + "\n"); + hwloc__nolibxml_export_update_buffer(ndata, res); + hwloc__nolibxml_export_new_child(&state, &childstate, "topologydiff"); + if (refname) + hwloc__nolibxml_export_new_prop(&childstate, "refname", refname); + hwloc__xml_export_diff (&childstate, diff); + hwloc__nolibxml_export_end_object(&childstate, "topologydiff"); + + return ndata->written+1; +} + +static int +hwloc_nolibxml_export_diff_buffer(hwloc_topology_diff_t diff, const char *refname, char **bufferp, int *buflenp) +{ + char *buffer; + size_t bufferlen, res; + + bufferlen = 16384; /* random guess for large enough default */ + buffer = malloc(bufferlen); + if (!buffer) + return -1; + res = hwloc___nolibxml_prepare_export_diff(diff, refname, buffer, (int)bufferlen); + + if (res > bufferlen) { + char *tmp = realloc(buffer, res); + if (!tmp) { + free(buffer); + return -1; + } + buffer = tmp; + hwloc___nolibxml_prepare_export_diff(diff, refname, buffer, (int)res); + } + + *bufferp = buffer; + *buflenp = (int)res; + return 0; +} + +static int +hwloc_nolibxml_export_diff_file(hwloc_topology_diff_t diff, const char *refname, const char *filename) +{ + FILE *file; + char *buffer; + int bufferlen; + int ret; + + ret = hwloc_nolibxml_export_diff_buffer(diff, refname, &buffer, &bufferlen); + if (ret < 0) + return -1; + + if (!strcmp(filename, "-")) { + file = stdout; + } else { + file = fopen(filename, "w"); + if (!file) { + free(buffer); + return -1; + } + } + + ret = (int)fwrite(buffer, 1, bufferlen-1 /* don't write the ending \0 */, file); + if (ret == bufferlen-1) { + ret = 0; + } else { + errno = ferror(file); + ret = -1; + } + + free(buffer); + + if (file != stdout) + fclose(file); + return ret; +} + +static void +hwloc_nolibxml_free_buffer(void *xmlbuffer) +{ + free(xmlbuffer); +} + +/************* + * Callbacks * + *************/ + +static struct hwloc_xml_callbacks hwloc_xml_nolibxml_callbacks = { + hwloc_nolibxml_backend_init, + hwloc_nolibxml_export_file, + hwloc_nolibxml_export_buffer, + hwloc_nolibxml_free_buffer, + hwloc_nolibxml_import_diff, + hwloc_nolibxml_export_diff_file, + hwloc_nolibxml_export_diff_buffer +}; + +static struct hwloc_xml_component hwloc_nolibxml_xml_component = { + &hwloc_xml_nolibxml_callbacks, + NULL +}; + +const struct hwloc_component hwloc_xml_nolibxml_component = { + HWLOC_COMPONENT_ABI, + NULL, NULL, + HWLOC_COMPONENT_TYPE_XML, + 0, + &hwloc_nolibxml_xml_component +}; diff --git a/src/3rdparty/hwloc/src/topology-xml.c b/src/3rdparty/hwloc/src/topology-xml.c new file mode 100644 index 00000000..e7c5ef62 --- /dev/null +++ b/src/3rdparty/hwloc/src/topology-xml.c @@ -0,0 +1,2886 @@ +/* + * Copyright © 2009 CNRS + * Copyright © 2009-2019 Inria. All rights reserved. + * Copyright © 2009-2011 Université Bordeaux + * Copyright © 2009-2018 Cisco Systems, Inc. All rights reserved. + * See COPYING in top-level directory. + */ + +#include +#include +#include +#include +#include +#include + +#include + +int +hwloc__xml_verbose(void) +{ + static int checked = 0; + static int verbose = 0; + if (!checked) { + const char *env = getenv("HWLOC_XML_VERBOSE"); + if (env) + verbose = atoi(env); + checked = 1; + } + return verbose; +} + +static int +hwloc_nolibxml_import(void) +{ + static int checked = 0; + static int nolibxml = 0; + if (!checked) { + const char *env = getenv("HWLOC_LIBXML"); + if (env) { + nolibxml = !atoi(env); + } else { + env = getenv("HWLOC_LIBXML_IMPORT"); + if (env) + nolibxml = !atoi(env); + } + checked = 1; + } + return nolibxml; +} + +static int +hwloc_nolibxml_export(void) +{ + static int checked = 0; + static int nolibxml = 0; + if (!checked) { + const char *env = getenv("HWLOC_LIBXML"); + if (env) { + nolibxml = !atoi(env); + } else { + env = getenv("HWLOC_LIBXML_EXPORT"); + if (env) + nolibxml = !atoi(env); + } + checked = 1; + } + return nolibxml; +} + +#define BASE64_ENCODED_LENGTH(length) (4*(((length)+2)/3)) + +/********************************* + ********* XML callbacks ********* + *********************************/ + +/* set when registering nolibxml and libxml components. + * modifications protected by the components mutex. + * read by the common XML code in topology-xml.c to jump to the right XML backend. + */ +static struct hwloc_xml_callbacks *hwloc_nolibxml_callbacks = NULL, *hwloc_libxml_callbacks = NULL; + +void +hwloc_xml_callbacks_register(struct hwloc_xml_component *comp) +{ + if (!hwloc_nolibxml_callbacks) + hwloc_nolibxml_callbacks = comp->nolibxml_callbacks; + if (!hwloc_libxml_callbacks) + hwloc_libxml_callbacks = comp->libxml_callbacks; +} + +void +hwloc_xml_callbacks_reset(void) +{ + hwloc_nolibxml_callbacks = NULL; + hwloc_libxml_callbacks = NULL; +} + +/************************************************ + ********* XML import (common routines) ********* + ************************************************/ + +#define _HWLOC_OBJ_CACHE_OLD (HWLOC_OBJ_TYPE_MAX+1) /* temporarily used when importing pre-v2.0 attribute-less cache types */ +#define _HWLOC_OBJ_FUTURE (HWLOC_OBJ_TYPE_MAX+2) /* temporarily used when ignoring future types */ + +static void +hwloc__xml_import_object_attr(struct hwloc_topology *topology, + struct hwloc_xml_backend_data_s *data, + struct hwloc_obj *obj, + const char *name, const char *value, + hwloc__xml_import_state_t state) +{ + if (!strcmp(name, "type")) { + /* already handled */ + return; + } + + else if (!strcmp(name, "os_index")) + obj->os_index = strtoul(value, NULL, 10); + else if (!strcmp(name, "gp_index")) { + obj->gp_index = strtoull(value, NULL, 10); + if (!obj->gp_index && hwloc__xml_verbose()) + fprintf(stderr, "%s: unexpected zero gp_index, topology may be invalid\n", state->global->msgprefix); + if (obj->gp_index >= topology->next_gp_index) + topology->next_gp_index = obj->gp_index + 1; + } else if (!strcmp(name, "cpuset")) { + if (!obj->cpuset) + obj->cpuset = hwloc_bitmap_alloc(); + hwloc_bitmap_sscanf(obj->cpuset, value); + } else if (!strcmp(name, "complete_cpuset")) { + if (!obj->complete_cpuset) + obj->complete_cpuset = hwloc_bitmap_alloc(); + hwloc_bitmap_sscanf(obj->complete_cpuset, value); + } else if (!strcmp(name, "allowed_cpuset")) { + /* ignored except for root */ + if (!obj->parent) + hwloc_bitmap_sscanf(topology->allowed_cpuset, value); + } else if (!strcmp(name, "nodeset")) { + if (!obj->nodeset) + obj->nodeset = hwloc_bitmap_alloc(); + hwloc_bitmap_sscanf(obj->nodeset, value); + } else if (!strcmp(name, "complete_nodeset")) { + if (!obj->complete_nodeset) + obj->complete_nodeset = hwloc_bitmap_alloc(); + hwloc_bitmap_sscanf(obj->complete_nodeset, value); + } else if (!strcmp(name, "allowed_nodeset")) { + /* ignored except for root */ + if (!obj->parent) + hwloc_bitmap_sscanf(topology->allowed_nodeset, value); + } else if (!strcmp(name, "name")) { + if (obj->name) + free(obj->name); + obj->name = strdup(value); + } else if (!strcmp(name, "subtype")) { + if (obj->subtype) + free(obj->subtype); + obj->subtype = strdup(value); + } + + else if (!strcmp(name, "cache_size")) { + unsigned long long lvalue = strtoull(value, NULL, 10); + if (hwloc__obj_type_is_cache(obj->type) || obj->type == _HWLOC_OBJ_CACHE_OLD) + obj->attr->cache.size = lvalue; + else if (hwloc__xml_verbose()) + fprintf(stderr, "%s: ignoring cache_size attribute for non-cache object type\n", + state->global->msgprefix); + } + + else if (!strcmp(name, "cache_linesize")) { + unsigned long lvalue = strtoul(value, NULL, 10); + if (hwloc__obj_type_is_cache(obj->type) || obj->type == _HWLOC_OBJ_CACHE_OLD) + obj->attr->cache.linesize = lvalue; + else if (hwloc__xml_verbose()) + fprintf(stderr, "%s: ignoring cache_linesize attribute for non-cache object type\n", + state->global->msgprefix); + } + + else if (!strcmp(name, "cache_associativity")) { + int lvalue = atoi(value); + if (hwloc__obj_type_is_cache(obj->type) || obj->type == _HWLOC_OBJ_CACHE_OLD) + obj->attr->cache.associativity = lvalue; + else if (hwloc__xml_verbose()) + fprintf(stderr, "%s: ignoring cache_associativity attribute for non-cache object type\n", + state->global->msgprefix); + } + + else if (!strcmp(name, "cache_type")) { + unsigned long lvalue = strtoul(value, NULL, 10); + if (hwloc__obj_type_is_cache(obj->type) || obj->type == _HWLOC_OBJ_CACHE_OLD) { + if (lvalue == HWLOC_OBJ_CACHE_UNIFIED + || lvalue == HWLOC_OBJ_CACHE_DATA + || lvalue == HWLOC_OBJ_CACHE_INSTRUCTION) + obj->attr->cache.type = (hwloc_obj_cache_type_t) lvalue; + else + fprintf(stderr, "%s: ignoring invalid cache_type attribute %lu\n", + state->global->msgprefix, lvalue); + } else if (hwloc__xml_verbose()) + fprintf(stderr, "%s: ignoring cache_type attribute for non-cache object type\n", + state->global->msgprefix); + } + + else if (!strcmp(name, "local_memory")) { + unsigned long long lvalue = strtoull(value, NULL, 10); + if (obj->type == HWLOC_OBJ_NUMANODE) + obj->attr->numanode.local_memory = lvalue; + else if (!obj->parent) + topology->machine_memory.local_memory = lvalue; + else if (hwloc__xml_verbose()) + fprintf(stderr, "%s: ignoring local_memory attribute for non-NUMAnode non-root object\n", + state->global->msgprefix); + } + + else if (!strcmp(name, "depth")) { + unsigned long lvalue = strtoul(value, NULL, 10); + if (hwloc__obj_type_is_cache(obj->type) || obj->type == _HWLOC_OBJ_CACHE_OLD) { + obj->attr->cache.depth = lvalue; + } else if (obj->type == HWLOC_OBJ_GROUP || obj->type == HWLOC_OBJ_BRIDGE) { + /* will be overwritten by the core */ + } else if (hwloc__xml_verbose()) + fprintf(stderr, "%s: ignoring depth attribute for object type without depth\n", + state->global->msgprefix); + } + + else if (!strcmp(name, "kind")) { + unsigned long lvalue = strtoul(value, NULL, 10); + if (obj->type == HWLOC_OBJ_GROUP) + obj->attr->group.kind = lvalue; + else if (hwloc__xml_verbose()) + fprintf(stderr, "%s: ignoring kind attribute for non-group object type\n", + state->global->msgprefix); + } + + else if (!strcmp(name, "subkind")) { + unsigned long lvalue = strtoul(value, NULL, 10); + if (obj->type == HWLOC_OBJ_GROUP) + obj->attr->group.subkind = lvalue; + else if (hwloc__xml_verbose()) + fprintf(stderr, "%s: ignoring subkind attribute for non-group object type\n", + state->global->msgprefix); + } + + else if (!strcmp(name, "dont_merge")) { + unsigned long lvalue = strtoul(value, NULL, 10); + if (obj->type == HWLOC_OBJ_GROUP) + obj->attr->group.dont_merge = lvalue; + else if (hwloc__xml_verbose()) + fprintf(stderr, "%s: ignoring dont_merge attribute for non-group object type\n", + state->global->msgprefix); + } + + else if (!strcmp(name, "pci_busid")) { + switch (obj->type) { + case HWLOC_OBJ_PCI_DEVICE: + case HWLOC_OBJ_BRIDGE: { + unsigned domain, bus, dev, func; + if (sscanf(value, "%04x:%02x:%02x.%01x", + &domain, &bus, &dev, &func) != 4) { + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: ignoring invalid pci_busid format string %s\n", + state->global->msgprefix, value); + } else { + obj->attr->pcidev.domain = domain; + obj->attr->pcidev.bus = bus; + obj->attr->pcidev.dev = dev; + obj->attr->pcidev.func = func; + } + break; + } + default: + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: ignoring pci_busid attribute for non-PCI object\n", + state->global->msgprefix); + break; + } + } + + else if (!strcmp(name, "pci_type")) { + switch (obj->type) { + case HWLOC_OBJ_PCI_DEVICE: + case HWLOC_OBJ_BRIDGE: { + unsigned classid, vendor, device, subvendor, subdevice, revision; + if (sscanf(value, "%04x [%04x:%04x] [%04x:%04x] %02x", + &classid, &vendor, &device, &subvendor, &subdevice, &revision) != 6) { + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: ignoring invalid pci_type format string %s\n", + state->global->msgprefix, value); + } else { + obj->attr->pcidev.class_id = classid; + obj->attr->pcidev.vendor_id = vendor; + obj->attr->pcidev.device_id = device; + obj->attr->pcidev.subvendor_id = subvendor; + obj->attr->pcidev.subdevice_id = subdevice; + obj->attr->pcidev.revision = revision; + } + break; + } + default: + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: ignoring pci_type attribute for non-PCI object\n", + state->global->msgprefix); + break; + } + } + + else if (!strcmp(name, "pci_link_speed")) { + switch (obj->type) { + case HWLOC_OBJ_PCI_DEVICE: + case HWLOC_OBJ_BRIDGE: { + obj->attr->pcidev.linkspeed = (float) atof(value); + break; + } + default: + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: ignoring pci_link_speed attribute for non-PCI object\n", + state->global->msgprefix); + break; + } + } + + else if (!strcmp(name, "bridge_type")) { + switch (obj->type) { + case HWLOC_OBJ_BRIDGE: { + unsigned upstream_type, downstream_type; + if (sscanf(value, "%u-%u", &upstream_type, &downstream_type) != 2) { + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: ignoring invalid bridge_type format string %s\n", + state->global->msgprefix, value); + } else { + obj->attr->bridge.upstream_type = (hwloc_obj_bridge_type_t) upstream_type; + obj->attr->bridge.downstream_type = (hwloc_obj_bridge_type_t) downstream_type; + }; + break; + } + default: + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: ignoring bridge_type attribute for non-bridge object\n", + state->global->msgprefix); + break; + } + } + + else if (!strcmp(name, "bridge_pci")) { + switch (obj->type) { + case HWLOC_OBJ_BRIDGE: { + unsigned domain, secbus, subbus; + if (sscanf(value, "%04x:[%02x-%02x]", + &domain, &secbus, &subbus) != 3) { + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: ignoring invalid bridge_pci format string %s\n", + state->global->msgprefix, value); + } else { + obj->attr->bridge.downstream.pci.domain = domain; + obj->attr->bridge.downstream.pci.secondary_bus = secbus; + obj->attr->bridge.downstream.pci.subordinate_bus = subbus; + } + break; + } + default: + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: ignoring bridge_pci attribute for non-bridge object\n", + state->global->msgprefix); + break; + } + } + + else if (!strcmp(name, "osdev_type")) { + switch (obj->type) { + case HWLOC_OBJ_OS_DEVICE: { + unsigned osdev_type; + if (sscanf(value, "%u", &osdev_type) != 1) { + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: ignoring invalid osdev_type format string %s\n", + state->global->msgprefix, value); + } else + obj->attr->osdev.type = (hwloc_obj_osdev_type_t) osdev_type; + break; + } + default: + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: ignoring osdev_type attribute for non-osdev object\n", + state->global->msgprefix); + break; + } + } + + else if (data->version_major < 2) { + /************************ + * deprecated from 1.x + */ + if (!strcmp(name, "os_level") + || !strcmp(name, "online_cpuset")) + { /* ignored */ } + + /************************* + * deprecated from 1.0 + */ + else if (!strcmp(name, "dmi_board_vendor")) { + if (value[0]) + hwloc_obj_add_info(obj, "DMIBoardVendor", value); + } + else if (!strcmp(name, "dmi_board_name")) { + if (value[0]) + hwloc_obj_add_info(obj, "DMIBoardName", value); + } + + else if (data->version_major < 1) { + /************************* + * deprecated from 0.9 + */ + if (!strcmp(name, "memory_kB")) { + unsigned long long lvalue = strtoull(value, NULL, 10); + if (obj->type == _HWLOC_OBJ_CACHE_OLD) + obj->attr->cache.size = lvalue << 10; + else if (obj->type == HWLOC_OBJ_NUMANODE) + obj->attr->numanode.local_memory = lvalue << 10; + else if (!obj->parent) + topology->machine_memory.local_memory = lvalue << 10; + else if (hwloc__xml_verbose()) + fprintf(stderr, "%s: ignoring memory_kB attribute for non-NUMAnode non-root object\n", + state->global->msgprefix); + } + else if (!strcmp(name, "huge_page_size_kB")) { + unsigned long lvalue = strtoul(value, NULL, 10); + if (obj->type == HWLOC_OBJ_NUMANODE || !obj->parent) { + struct hwloc_numanode_attr_s *memory = obj->type == HWLOC_OBJ_NUMANODE ? &obj->attr->numanode : &topology->machine_memory; + if (!memory->page_types) { + memory->page_types = malloc(sizeof(*memory->page_types)); + memory->page_types_len = 1; + } + memory->page_types[0].size = lvalue << 10; + } else if (hwloc__xml_verbose()) { + fprintf(stderr, "%s: ignoring huge_page_size_kB attribute for non-NUMAnode non-root object\n", + state->global->msgprefix); + } + } + else if (!strcmp(name, "huge_page_free")) { + unsigned long lvalue = strtoul(value, NULL, 10); + if (obj->type == HWLOC_OBJ_NUMANODE || !obj->parent) { + struct hwloc_numanode_attr_s *memory = obj->type == HWLOC_OBJ_NUMANODE ? &obj->attr->numanode : &topology->machine_memory; + if (!memory->page_types) { + memory->page_types = malloc(sizeof(*memory->page_types)); + memory->page_types_len = 1; + } + memory->page_types[0].count = lvalue; + } else if (hwloc__xml_verbose()) { + fprintf(stderr, "%s: ignoring huge_page_free attribute for non-NUMAnode non-root object\n", + state->global->msgprefix); + } + } + /* end of deprecated from 0.9 */ + else goto unknown; + } + /* end of deprecated from 1.0 */ + else goto unknown; + } + else { + unknown: + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: ignoring unknown object attribute %s\n", + state->global->msgprefix, name); + } +} + + +static int +hwloc__xml_import_info(struct hwloc_xml_backend_data_s *data, + hwloc_obj_t obj, + hwloc__xml_import_state_t state) +{ + char *infoname = NULL; + char *infovalue = NULL; + + while (1) { + char *attrname, *attrvalue; + if (state->global->next_attr(state, &attrname, &attrvalue) < 0) + break; + if (!strcmp(attrname, "name")) + infoname = attrvalue; + else if (!strcmp(attrname, "value")) + infovalue = attrvalue; + else + return -1; + } + + if (infoname) { + /* empty strings are ignored by libxml */ + if (data->version_major < 2 && + (!strcmp(infoname, "Type") || !strcmp(infoname, "CoProcType"))) { + /* 1.x stored subtype in Type or CoProcType */ + if (infovalue) { + if (obj->subtype) + free(obj->subtype); + obj->subtype = strdup(infovalue); + } + } else { + if (infovalue) + hwloc_obj_add_info(obj, infoname, infovalue); + } + } + + return state->global->close_tag(state); +} + +static int +hwloc__xml_import_pagetype(hwloc_topology_t topology __hwloc_attribute_unused, struct hwloc_numanode_attr_s *memory, + hwloc__xml_import_state_t state) +{ + uint64_t size = 0, count = 0; + + while (1) { + char *attrname, *attrvalue; + if (state->global->next_attr(state, &attrname, &attrvalue) < 0) + break; + if (!strcmp(attrname, "size")) + size = strtoull(attrvalue, NULL, 10); + else if (!strcmp(attrname, "count")) + count = strtoull(attrvalue, NULL, 10); + else + return -1; + } + + if (size) { + unsigned idx = memory->page_types_len; + struct hwloc_memory_page_type_s *tmp; + tmp = realloc(memory->page_types, (idx+1)*sizeof(*memory->page_types)); + if (tmp) { /* if failed to allocate, ignore this page_type entry */ + memory->page_types = tmp; + memory->page_types_len = idx+1; + memory->page_types[idx].size = size; + memory->page_types[idx].count = count; + } + } + + return state->global->close_tag(state); +} + +static int +hwloc__xml_v1import_distances(struct hwloc_xml_backend_data_s *data, + hwloc_obj_t obj, + hwloc__xml_import_state_t state) +{ + unsigned long reldepth = 0, nbobjs = 0; + float latbase = 0; + char *tag; + int ret; + + while (1) { + char *attrname, *attrvalue; + if (state->global->next_attr(state, &attrname, &attrvalue) < 0) + break; + if (!strcmp(attrname, "nbobjs")) + nbobjs = strtoul(attrvalue, NULL, 10); + else if (!strcmp(attrname, "relative_depth")) + reldepth = strtoul(attrvalue, NULL, 10); + else if (!strcmp(attrname, "latency_base")) + latbase = (float) atof(attrvalue); + else + return -1; + } + + if (nbobjs && reldepth && latbase) { + unsigned i; + float *matrix; + struct hwloc__xml_imported_v1distances_s *v1dist; + + matrix = malloc(nbobjs*nbobjs*sizeof(float)); + v1dist = malloc(sizeof(*v1dist)); + if (!matrix || !v1dist) { + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: failed to allocate v1distance matrix for %lu objects\n", + state->global->msgprefix, nbobjs); + free(v1dist); + free(matrix); + return -1; + } + + v1dist->kind = HWLOC_DISTANCES_KIND_FROM_OS|HWLOC_DISTANCES_KIND_MEANS_LATENCY; + /* TODO: we can't know for sure if it comes from the OS. + * On Linux/x86, it would be 10 on the diagonal. + * On Solaris/T5, 15 on the diagonal. + * Just check whether all values are integers, and that all values on the diagonal are minimal and identical? + */ + + v1dist->nbobjs = nbobjs; + v1dist->floats = matrix; + + for(i=0; iglobal->find_child(state, &childstate, &tag); + if (ret <= 0 || strcmp(tag, "latency")) { + /* a latency child is needed */ + free(matrix); + free(v1dist); + return -1; + } + + ret = state->global->next_attr(&childstate, &attrname, &attrvalue); + if (ret < 0 || strcmp(attrname, "value")) { + free(matrix); + free(v1dist); + return -1; + } + + val = (float) atof((char *) attrvalue); + matrix[i] = val * latbase; + + ret = state->global->close_tag(&childstate); + if (ret < 0) { + free(matrix); + free(v1dist); + return -1; + } + + state->global->close_child(&childstate); + } + + if (nbobjs < 2) { + /* distances with a single object are useless, even if the XML isn't invalid */ + assert(nbobjs == 1); + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: ignoring invalid distance matrix with only 1 object\n", + state->global->msgprefix); + free(matrix); + free(v1dist); + + } else if (obj->parent) { + /* we currently only import distances attached to root. + * we can't save obj in v1dist because obj could be dropped during insert if ignored. + * we could save its complete_cpu/nodeset instead to find it back later. + * but it doesn't matter much since only NUMA distances attached to root matter. + */ + free(matrix); + free(v1dist); + + } else { + /* queue the distance for real */ + v1dist->prev = data->last_v1dist; + v1dist->next = NULL; + if (data->last_v1dist) + data->last_v1dist->next = v1dist; + else + data->first_v1dist = v1dist; + data->last_v1dist = v1dist; + } + } + + return state->global->close_tag(state); +} + +static int +hwloc__xml_import_userdata(hwloc_topology_t topology __hwloc_attribute_unused, hwloc_obj_t obj, + hwloc__xml_import_state_t state) +{ + size_t length = 0; + int encoded = 0; + char *name = NULL; /* optional */ + int ret; + + while (1) { + char *attrname, *attrvalue; + if (state->global->next_attr(state, &attrname, &attrvalue) < 0) + break; + if (!strcmp(attrname, "length")) + length = strtoul(attrvalue, NULL, 10); + else if (!strcmp(attrname, "encoding")) + encoded = !strcmp(attrvalue, "base64"); + else if (!strcmp(attrname, "name")) + name = attrvalue; + else + return -1; + } + + if (!topology->userdata_import_cb) { + char *buffer; + size_t reallength = encoded ? BASE64_ENCODED_LENGTH(length) : length; + ret = state->global->get_content(state, &buffer, reallength); + if (ret < 0) + return -1; + + } else if (topology->userdata_not_decoded) { + char *buffer, *fakename; + size_t reallength = encoded ? BASE64_ENCODED_LENGTH(length) : length; + ret = state->global->get_content(state, &buffer, reallength); + if (ret < 0) + return -1; + fakename = malloc(6 + 1 + (name ? strlen(name) : 4) + 1); + if (!fakename) + return -1; + sprintf(fakename, encoded ? "base64%c%s" : "normal%c%s", name ? ':' : '-', name ? name : "anon"); + topology->userdata_import_cb(topology, obj, fakename, buffer, length); + free(fakename); + + } else if (encoded && length) { + char *encoded_buffer; + size_t encoded_length = BASE64_ENCODED_LENGTH(length); + ret = state->global->get_content(state, &encoded_buffer, encoded_length); + if (ret < 0) + return -1; + if (ret) { + char *decoded_buffer = malloc(length+1); + if (!decoded_buffer) + return -1; + assert(encoded_buffer[encoded_length] == 0); + ret = hwloc_decode_from_base64(encoded_buffer, decoded_buffer, length+1); + if (ret != (int) length) { + free(decoded_buffer); + return -1; + } + topology->userdata_import_cb(topology, obj, name, decoded_buffer, length); + free(decoded_buffer); + } + + } else { /* always handle length==0 in the non-encoded case */ + char *buffer = (char *) ""; + if (length) { + ret = state->global->get_content(state, &buffer, length); + if (ret < 0) + return -1; + } + topology->userdata_import_cb(topology, obj, name, buffer, length); + } + + state->global->close_content(state); + return state->global->close_tag(state); +} + +static void hwloc__xml_import_report_outoforder(hwloc_topology_t topology, hwloc_obj_t new, hwloc_obj_t old) +{ + char *progname = hwloc_progname(topology); + const char *origversion = hwloc_obj_get_info_by_name(topology->levels[0][0], "hwlocVersion"); + const char *origprogname = hwloc_obj_get_info_by_name(topology->levels[0][0], "ProcessName"); + char *c1, *cc1, t1[64]; + char *c2 = NULL, *cc2 = NULL, t2[64]; + + hwloc_bitmap_asprintf(&c1, new->cpuset); + hwloc_bitmap_asprintf(&cc1, new->complete_cpuset); + hwloc_obj_type_snprintf(t1, sizeof(t1), new, 0); + + if (old->cpuset) + hwloc_bitmap_asprintf(&c2, old->cpuset); + if (old->complete_cpuset) + hwloc_bitmap_asprintf(&cc2, old->complete_cpuset); + hwloc_obj_type_snprintf(t2, sizeof(t2), old, 0); + + fprintf(stderr, "****************************************************************************\n"); + fprintf(stderr, "* hwloc has encountered an out-of-order XML topology load.\n"); + fprintf(stderr, "* Object %s cpuset %s complete %s\n", + t1, c1, cc1); + fprintf(stderr, "* was inserted after object %s with %s and %s.\n", + t2, c2 ? c2 : "none", cc2 ? cc2 : "none"); + fprintf(stderr, "* The error occured in hwloc %s inside process `%s', while\n", + HWLOC_VERSION, + progname ? progname : ""); + if (origversion || origprogname) + fprintf(stderr, "* the input XML was generated by hwloc %s inside process `%s'.\n", + origversion ? origversion : "(unknown version)", + origprogname ? origprogname : ""); + else + fprintf(stderr, "* the input XML was generated by an unspecified ancient hwloc release.\n"); + fprintf(stderr, "* Please check that your input topology XML file is valid.\n"); + fprintf(stderr, "* Set HWLOC_DEBUG_CHECK=1 in the environment to detect further issues.\n"); + fprintf(stderr, "****************************************************************************\n"); + + free(c1); + free(cc1); + free(c2); + free(cc2); + free(progname); +} + +static int +hwloc__xml_import_object(hwloc_topology_t topology, + struct hwloc_xml_backend_data_s *data, + hwloc_obj_t parent, hwloc_obj_t obj, int *gotignored, + hwloc__xml_import_state_t state) +{ + int ignored = 0; + int childrengotignored = 0; + int attribute_less_cache = 0; + int numa_was_root = 0; + char *tag; + struct hwloc__xml_import_state_s childstate; + + /* set parent now since it's used during import below or in subfunctions */ + obj->parent = parent; + + /* process attributes */ + while (1) { + char *attrname, *attrvalue; + if (state->global->next_attr(state, &attrname, &attrvalue) < 0) + break; + if (!strcmp(attrname, "type")) { + if (hwloc_type_sscanf(attrvalue, &obj->type, NULL, 0) < 0) { + if (!strcasecmp(attrvalue, "Cache")) { + obj->type = _HWLOC_OBJ_CACHE_OLD; /* will be fixed below */ + attribute_less_cache = 1; + } else if (!strcasecmp(attrvalue, "System")) { + if (!parent) + obj->type = HWLOC_OBJ_MACHINE; + else { + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: obsolete System object only allowed at root\n", + state->global->msgprefix); + goto error_with_object; + } + } else if (!strcasecmp(attrvalue, "Die")) { + /* deal with possible future type */ + obj->type = HWLOC_OBJ_GROUP; + obj->subtype = strdup("Die"); + obj->attr->group.kind = HWLOC_GROUP_KIND_INTEL_DIE; + obj->attr->group.dont_merge = data->dont_merge_die_groups; + } else if (!strcasecmp(attrvalue, "Tile")) { + /* deal with possible future type */ + obj->type = HWLOC_OBJ_GROUP; + obj->subtype = strdup("Tile"); + obj->attr->group.kind = HWLOC_GROUP_KIND_INTEL_TILE; + } else if (!strcasecmp(attrvalue, "Module")) { + /* deal with possible future type */ + obj->type = HWLOC_OBJ_GROUP; + obj->subtype = strdup("Module"); + obj->attr->group.kind = HWLOC_GROUP_KIND_INTEL_MODULE; + } else if (!strcasecmp(attrvalue, "MemCache")) { + /* ignore possible future type */ + obj->type = _HWLOC_OBJ_FUTURE; + ignored = 1; + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: %s object not-supported, will be ignored\n", + state->global->msgprefix, attrvalue); + } else { + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: unrecognized object type string %s\n", + state->global->msgprefix, attrvalue); + goto error_with_object; + } + } + } else { + /* type needed first */ + if (obj->type == HWLOC_OBJ_TYPE_NONE) { + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: object attribute %s found before type\n", + state->global->msgprefix, attrname); + goto error_with_object; + } + hwloc__xml_import_object_attr(topology, data, obj, attrname, attrvalue, state); + } + } + + /* process non-object subnodes to get info attrs (as well as page_types, etc) */ + while (1) { + int ret; + + tag = NULL; + ret = state->global->find_child(state, &childstate, &tag); + if (ret < 0) + goto error; + if (!ret) + break; + + if (!strcmp(tag, "object")) { + /* we'll handle children later */ + break; + + } else if (!strcmp(tag, "page_type")) { + if (obj->type == HWLOC_OBJ_NUMANODE) { + ret = hwloc__xml_import_pagetype(topology, &obj->attr->numanode, &childstate); + } else if (!parent) { + ret = hwloc__xml_import_pagetype(topology, &topology->machine_memory, &childstate); + } else { + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: invalid non-NUMAnode object child %s\n", + state->global->msgprefix, tag); + ret = -1; + } + + } else if (!strcmp(tag, "info")) { + ret = hwloc__xml_import_info(data, obj, &childstate); + } else if (data->version_major < 2 && !strcmp(tag, "distances")) { + ret = hwloc__xml_v1import_distances(data, obj, &childstate); + } else if (!strcmp(tag, "userdata")) { + ret = hwloc__xml_import_userdata(topology, obj, &childstate); + } else { + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: invalid special object child %s\n", + state->global->msgprefix, tag); + ret = -1; + } + + if (ret < 0) + goto error; + + state->global->close_child(&childstate); + } + + if (parent && obj->type == HWLOC_OBJ_MACHINE) { + /* replace non-root Machine with Groups */ + obj->type = HWLOC_OBJ_GROUP; + } + + if (parent && data->version_major >= 2) { + /* check parent/child types for 2.x */ + if (hwloc__obj_type_is_normal(obj->type)) { + if (!hwloc__obj_type_is_normal(parent->type)) { + if (hwloc__xml_verbose()) + fprintf(stderr, "normal object %s cannot be child of non-normal parent %s\n", + hwloc_obj_type_string(obj->type), hwloc_obj_type_string(parent->type)); + goto error_with_object; + } + } else if (hwloc__obj_type_is_memory(obj->type)) { + if (hwloc__obj_type_is_io(parent->type) || HWLOC_OBJ_MISC == parent->type) { + if (hwloc__xml_verbose()) + fprintf(stderr, "Memory object %s cannot be child of non-normal-or-memory parent %s\n", + hwloc_obj_type_string(obj->type), hwloc_obj_type_string(parent->type)); + goto error_with_object; + } + } else if (hwloc__obj_type_is_io(obj->type)) { + if (hwloc__obj_type_is_memory(parent->type) || HWLOC_OBJ_MISC == parent->type) { + if (hwloc__xml_verbose()) + fprintf(stderr, "I/O object %s cannot be child of non-normal-or-I/O parent %s\n", + hwloc_obj_type_string(obj->type), hwloc_obj_type_string(parent->type)); + goto error_with_object; + } + } + + } else if (parent && data->version_major < 2) { + /* check parent/child types for pre-v2.0 */ + if (hwloc__obj_type_is_normal(obj->type) || HWLOC_OBJ_NUMANODE == obj->type) { + if (hwloc__obj_type_is_special(parent->type)) { + if (hwloc__xml_verbose()) + fprintf(stderr, "v1.x normal v1.x object %s cannot be child of special parent %s\n", + hwloc_obj_type_string(obj->type), hwloc_obj_type_string(parent->type)); + goto error_with_object; + } + } else if (hwloc__obj_type_is_io(obj->type)) { + if (HWLOC_OBJ_MISC == parent->type) { + if (hwloc__xml_verbose()) + fprintf(stderr, "I/O object %s cannot be child of Misc parent\n", + hwloc_obj_type_string(obj->type)); + goto error_with_object; + } + } + } + + if (data->version_major < 2) { + /*************************** + * 1.x specific checks + */ + + /* attach pre-v2.0 children of NUMA nodes to normal parent */ + if (parent && parent->type == HWLOC_OBJ_NUMANODE) { + parent = parent->parent; + assert(parent); + } + + /* insert a group above pre-v2.0 NUMA nodes if needed */ + if (obj->type == HWLOC_OBJ_NUMANODE) { + if (!parent) { + /* crazy case of NUMA node root (only possible when filtering Machine keep_structure in v1.x), + * reinsert a Machine object + */ + hwloc_obj_t machine = hwloc_alloc_setup_object(topology, HWLOC_OBJ_MACHINE, HWLOC_UNKNOWN_INDEX); + machine->cpuset = hwloc_bitmap_dup(obj->cpuset); + machine->complete_cpuset = hwloc_bitmap_dup(obj->cpuset); + machine->nodeset = hwloc_bitmap_dup(obj->nodeset); + machine->complete_nodeset = hwloc_bitmap_dup(obj->complete_nodeset); + topology->levels[0][0] = machine; + parent = machine; + numa_was_root = 1; + + } else if (!hwloc_bitmap_isequal(obj->complete_cpuset, parent->complete_cpuset)) { + /* This NUMA node has a different locality from its parent. + * Don't attach it to this parent, or it well get its parent cpusets. + * Add an intermediate Group with the desired locality. + */ + int needgroup = 1; + hwloc_obj_t sibling; + + sibling = parent->memory_first_child; + if (sibling && !sibling->subtype + && !sibling->next_sibling + && obj->subtype && !strcmp(obj->subtype, "MCDRAM") + && hwloc_bitmap_iszero(obj->complete_cpuset)) { + /* this is KNL MCDRAM, we want to attach it near its DDR sibling */ + needgroup = 0; + } + /* Ideally we would also detect similar cases on future non-KNL platforms with multiple local NUMA nodes. + * That's unlikely to occur with v1.x. + * And we have no way to be sure if this CPU-less node is desired or not. + */ + + if (needgroup + && hwloc_filter_check_keep_object_type(topology, HWLOC_OBJ_GROUP)) { + hwloc_obj_t group = hwloc_alloc_setup_object(topology, HWLOC_OBJ_GROUP, HWLOC_UNKNOWN_INDEX); + group->gp_index = 0; /* will be initialized at the end of the discovery once we know the max */ + group->cpuset = hwloc_bitmap_dup(obj->cpuset); + group->complete_cpuset = hwloc_bitmap_dup(obj->cpuset); + group->nodeset = hwloc_bitmap_dup(obj->nodeset); + group->complete_nodeset = hwloc_bitmap_dup(obj->complete_nodeset); + group->attr->group.kind = HWLOC_GROUP_KIND_MEMORY; + hwloc_insert_object_by_parent(topology, parent, group); + parent = group; + } + } + } + + /* fixup attribute-less caches imported from pre-v2.0 XMLs */ + if (attribute_less_cache) { + assert(obj->type == _HWLOC_OBJ_CACHE_OLD); + obj->type = hwloc_cache_type_by_depth_type(obj->attr->cache.depth, obj->attr->cache.type); + } + + /* fixup Misc objects inserted by cpusets in pre-v2.0 XMLs */ + if (obj->type == HWLOC_OBJ_MISC && obj->cpuset) + obj->type = HWLOC_OBJ_GROUP; + + /* check set consistency. + * 1.7.2 and earlier reported I/O Groups with only a cpuset, we don't want to reject those XMLs yet. + * Ignore those Groups since fixing the missing sets is hard (would need to look at children sets which are not available yet). + * Just abort the XML for non-Groups. + */ + if (!obj->cpuset != !obj->complete_cpuset) { + /* has some cpuset without others */ + if (obj->type == HWLOC_OBJ_GROUP) { + ignored = 1; + } else { + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: invalid object %s P#%u with some missing cpusets\n", + state->global->msgprefix, hwloc_obj_type_string(obj->type), obj->os_index); + goto error_with_object; + } + } else if (!obj->nodeset != !obj->complete_nodeset) { + /* has some nodeset without others */ + if (obj->type == HWLOC_OBJ_GROUP) { + ignored = 1; + } else { + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: invalid object %s P#%u with some missing nodesets\n", + state->global->msgprefix, hwloc_obj_type_string(obj->type), obj->os_index); + goto error_with_object; + } + } else if (obj->nodeset && !obj->cpuset) { + /* has nodesets without cpusets (the contrary is allowed in pre-2.0) */ + if (obj->type == HWLOC_OBJ_GROUP) { + ignored = 1; + } else { + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: invalid object %s P#%u with either cpuset or nodeset missing\n", + state->global->msgprefix, hwloc_obj_type_string(obj->type), obj->os_index); + goto error_with_object; + } + } + /* end of 1.x specific checks */ + } + + /* check that cache attributes are coherent with the actual type */ + if (hwloc__obj_type_is_cache(obj->type) + && obj->type != hwloc_cache_type_by_depth_type(obj->attr->cache.depth, obj->attr->cache.type)) { + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: invalid cache type %s with attribute depth %u and type %d\n", + state->global->msgprefix, hwloc_obj_type_string(obj->type), obj->attr->cache.depth, (int) obj->attr->cache.type); + goto error_with_object; + } + + /* check special types vs cpuset */ + if (!obj->cpuset && !hwloc__obj_type_is_special(obj->type)) { + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: invalid normal object %s P#%u without cpuset\n", + state->global->msgprefix, hwloc_obj_type_string(obj->type), obj->os_index); + goto error_with_object; + } + if (obj->cpuset && hwloc__obj_type_is_special(obj->type)) { + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: invalid special object %s with cpuset\n", + state->global->msgprefix, hwloc_obj_type_string(obj->type)); + goto error_with_object; + } + + /* check parent vs child sets */ + if (obj->cpuset && parent && !parent->cpuset) { + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: invalid object %s P#%u with cpuset while parent has none\n", + state->global->msgprefix, hwloc_obj_type_string(obj->type), obj->os_index); + goto error_with_object; + } + if (obj->nodeset && parent && !parent->nodeset) { + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: invalid object %s P#%u with nodeset while parent has none\n", + state->global->msgprefix, hwloc_obj_type_string(obj->type), obj->os_index); + goto error_with_object; + } + + /* check NUMA nodes */ + if (obj->type == HWLOC_OBJ_NUMANODE) { + if (!obj->nodeset) { + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: invalid NUMA node object P#%u without nodeset\n", + state->global->msgprefix, obj->os_index); + goto error_with_object; + } + data->nbnumanodes++; + obj->prev_cousin = data->last_numanode; + obj->next_cousin = NULL; + if (data->last_numanode) + data->last_numanode->next_cousin = obj; + else + data->first_numanode = obj; + data->last_numanode = obj; + } + + if (!hwloc_filter_check_keep_object(topology, obj)) { + /* Ignore this object instead of inserting it. + * + * Well, let the core ignore the root object later + * because we don't know yet if root has more than one child. + */ + if (parent) + ignored = 1; + } + + if (parent && !ignored) { + /* root->parent is NULL, and root is already inserted */ + hwloc_insert_object_by_parent(topology, parent, obj); + /* insert_object_by_parent() doesn't merge during insert, so obj is still valid */ + } + + /* process object subnodes, if we found one win the above loop */ + while (tag) { + int ret; + + if (!strcmp(tag, "object")) { + hwloc_obj_t childobj = hwloc_alloc_setup_object(topology, HWLOC_OBJ_TYPE_MAX, HWLOC_UNKNOWN_INDEX); + childobj->parent = ignored ? parent : obj; + ret = hwloc__xml_import_object(topology, data, ignored ? parent : obj, childobj, + &childrengotignored, + &childstate); + } else { + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: invalid special object child %s while looking for objects\n", + state->global->msgprefix, tag); + ret = -1; + } + + if (ret < 0) + goto error; + + state->global->close_child(&childstate); + + tag = NULL; + ret = state->global->find_child(state, &childstate, &tag); + if (ret < 0) + goto error; + if (!ret) + break; + } + + if (numa_was_root) { + /* duplicate NUMA infos to root, most of them are likely root-specific */ + unsigned i; + for(i=0; iinfos_count; i++) { + struct hwloc_info_s *info = &obj->infos[i]; + hwloc_obj_add_info(parent, info->name, info->value); + } + /* TODO some infos are root-only (hwlocVersion, ProcessName, etc), remove them from obj? */ + } + + if (ignored) { + /* drop that object, and tell the parent that one child got ignored */ + hwloc_free_unlinked_object(obj); + *gotignored = 1; + + } else if (obj->first_child) { + /* now that all children are inserted, make sure they are in-order, + * so that the core doesn't have to deal with crappy children list. + */ + hwloc_obj_t cur, next; + for(cur = obj->first_child, next = cur->next_sibling; + next; + cur = next, next = next->next_sibling) { + /* If reordering is needed, at least one pair of consecutive children will be out-of-order. + * So just check pairs of consecutive children. + * + * We checked above that complete_cpuset is always set. + */ + if (hwloc_bitmap_compare_first(next->complete_cpuset, cur->complete_cpuset) < 0) { + /* next should be before cur */ + if (!childrengotignored) { + static int reported = 0; + if (!reported && !hwloc_hide_errors()) { + hwloc__xml_import_report_outoforder(topology, next, cur); + reported = 1; + } + } + hwloc__reorder_children(obj); + break; + } + } + /* no need to reorder memory children as long as there are no intermediate memory objects + * that could cause reordering when filtered-out. + */ + } + + return state->global->close_tag(state); + + error_with_object: + if (parent) + /* root->parent is NULL, and root is already inserted. the caller will cleanup that root. */ + hwloc_free_unlinked_object(obj); + error: + return -1; +} + +static int +hwloc__xml_v2import_distances(hwloc_topology_t topology, + hwloc__xml_import_state_t state) +{ + hwloc_obj_type_t type = HWLOC_OBJ_TYPE_NONE; + unsigned nbobjs = 0; + int indexing = 0; + int os_indexing = 0; + int gp_indexing = 0; + unsigned long kind = 0; + unsigned nr_indexes, nr_u64values; + uint64_t *indexes; + uint64_t *u64values; + int ret; + + /* process attributes */ + while (1) { + char *attrname, *attrvalue; + if (state->global->next_attr(state, &attrname, &attrvalue) < 0) + break; + if (!strcmp(attrname, "nbobjs")) + nbobjs = strtoul(attrvalue, NULL, 10); + else if (!strcmp(attrname, "type")) { + if (hwloc_type_sscanf(attrvalue, &type, NULL, 0) < 0) + goto out; + } + else if (!strcmp(attrname, "indexing")) { + indexing = 1; + if (!strcmp(attrvalue, "os")) + os_indexing = 1; + else if (!strcmp(attrvalue, "gp")) + gp_indexing = 1; + } + else if (!strcmp(attrname, "kind")) { + kind = strtoul(attrvalue, NULL, 10); + } + else { + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: ignoring unknown distance attribute %s\n", + state->global->msgprefix, attrname); + } + } + + /* abort if missing attribute */ + if (!nbobjs || type == HWLOC_OBJ_TYPE_NONE || !indexing || !kind) { + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: distance2 missing some attributes\n", + state->global->msgprefix); + goto out; + } + + indexes = malloc(nbobjs*sizeof(*indexes)); + u64values = malloc(nbobjs*nbobjs*sizeof(*u64values)); + if (!indexes || !u64values) { + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: failed to allocate distances arrays for %u objects\n", + state->global->msgprefix, nbobjs); + goto out_with_arrays; + } + + /* process children */ + nr_indexes = 0; + nr_u64values = 0; + while (1) { + struct hwloc__xml_import_state_s childstate; + char *attrname, *attrvalue, *tag, *buffer; + int length; + int is_index = 0; + int is_u64values = 0; + + ret = state->global->find_child(state, &childstate, &tag); + if (ret <= 0) + break; + + if (!strcmp(tag, "indexes")) + is_index = 1; + else if (!strcmp(tag, "u64values")) + is_u64values = 1; + if (!is_index && !is_u64values) { + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: distance2 with unrecognized child %s\n", + state->global->msgprefix, tag); + goto out_with_arrays; + } + + if (state->global->next_attr(&childstate, &attrname, &attrvalue) < 0 + || strcmp(attrname, "length")) { + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: distance2 child must have length attribute\n", + state->global->msgprefix); + goto out_with_arrays; + } + length = atoi(attrvalue); + + ret = state->global->get_content(&childstate, &buffer, length); + if (ret < 0) { + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: distance2 child needs content of length %d\n", + state->global->msgprefix, length); + goto out_with_arrays; + } + + if (is_index) { + /* get indexes */ + char *tmp; + if (nr_indexes >= nbobjs) { + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: distance2 with more than %u indexes\n", + state->global->msgprefix, nbobjs); + goto out_with_arrays; + } + tmp = buffer; + while (1) { + char *next; + unsigned long long u = strtoull(tmp, &next, 0); + if (next == tmp) + break; + indexes[nr_indexes++] = u; + if (*next != ' ') + break; + if (nr_indexes == nbobjs) + break; + tmp = next+1; + } + + } else if (is_u64values) { + /* get uint64_t values */ + char *tmp; + if (nr_u64values >= nbobjs*nbobjs) { + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: distance2 with more than %u u64values\n", + state->global->msgprefix, nbobjs*nbobjs); + goto out_with_arrays; + } + tmp = buffer; + while (1) { + char *next; + unsigned long long u = strtoull(tmp, &next, 0); + if (next == tmp) + break; + u64values[nr_u64values++] = u; + if (*next != ' ') + break; + if (nr_u64values == nbobjs*nbobjs) + break; + tmp = next+1; + } + } + + state->global->close_content(&childstate); + + ret = state->global->close_tag(&childstate); + if (ret < 0) { + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: distance2 with more than %u indexes\n", + state->global->msgprefix, nbobjs); + goto out_with_arrays; + } + + state->global->close_child(&childstate); + } + + if (nr_indexes != nbobjs) { + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: distance2 with less than %u indexes\n", + state->global->msgprefix, nbobjs); + goto out_with_arrays; + } + if (nr_u64values != nbobjs*nbobjs) { + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: distance2 with less than %u u64values\n", + state->global->msgprefix, nbobjs*nbobjs); + goto out_with_arrays; + } + + if (nbobjs < 2) { + /* distances with a single object are useless, even if the XML isn't invalid */ + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: ignoring distances2 with only %u objects\n", + state->global->msgprefix, nbobjs); + goto out_ignore; + } + if (type == HWLOC_OBJ_PU || type == HWLOC_OBJ_NUMANODE) { + if (!os_indexing) { + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: ignoring PU or NUMA distances2 without os_indexing\n", + state->global->msgprefix); + goto out_ignore; + } + } else { + if (!gp_indexing) { + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: ignoring !PU or !NUMA distances2 without gp_indexing\n", + state->global->msgprefix); + goto out_ignore; + } + } + + hwloc_internal_distances_add_by_index(topology, type, nbobjs, indexes, u64values, kind, 0); + + /* prevent freeing below */ + indexes = NULL; + u64values = NULL; + + out_ignore: + free(indexes); + free(u64values); + return state->global->close_tag(state); + + out_with_arrays: + free(indexes); + free(u64values); + out: + return -1; +} + +static int +hwloc__xml_import_diff_one(hwloc__xml_import_state_t state, + hwloc_topology_diff_t *firstdiffp, + hwloc_topology_diff_t *lastdiffp) +{ + char *type_s = NULL; + char *obj_depth_s = NULL; + char *obj_index_s = NULL; + char *obj_attr_type_s = NULL; +/* char *obj_attr_index_s = NULL; unused for now */ + char *obj_attr_name_s = NULL; + char *obj_attr_oldvalue_s = NULL; + char *obj_attr_newvalue_s = NULL; + + while (1) { + char *attrname, *attrvalue; + if (state->global->next_attr(state, &attrname, &attrvalue) < 0) + break; + if (!strcmp(attrname, "type")) + type_s = attrvalue; + else if (!strcmp(attrname, "obj_depth")) + obj_depth_s = attrvalue; + else if (!strcmp(attrname, "obj_index")) + obj_index_s = attrvalue; + else if (!strcmp(attrname, "obj_attr_type")) + obj_attr_type_s = attrvalue; + else if (!strcmp(attrname, "obj_attr_index")) + { /* obj_attr_index_s = attrvalue; unused for now */ } + else if (!strcmp(attrname, "obj_attr_name")) + obj_attr_name_s = attrvalue; + else if (!strcmp(attrname, "obj_attr_oldvalue")) + obj_attr_oldvalue_s = attrvalue; + else if (!strcmp(attrname, "obj_attr_newvalue")) + obj_attr_newvalue_s = attrvalue; + else { + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: ignoring unknown diff attribute %s\n", + state->global->msgprefix, attrname); + return -1; + } + } + + if (type_s) { + switch (atoi(type_s)) { + default: + break; + case HWLOC_TOPOLOGY_DIFF_OBJ_ATTR: { + /* object attribute diff */ + hwloc_topology_diff_obj_attr_type_t obj_attr_type; + hwloc_topology_diff_t diff; + + /* obj_attr mandatory generic attributes */ + if (!obj_depth_s || !obj_index_s || !obj_attr_type_s) { + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: missing mandatory obj attr generic attributes\n", + state->global->msgprefix); + break; + } + + /* obj_attr mandatory attributes common to all subtypes */ + if (!obj_attr_oldvalue_s || !obj_attr_newvalue_s) { + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: missing mandatory obj attr value attributes\n", + state->global->msgprefix); + break; + } + + /* mandatory attributes for obj_attr_info subtype */ + obj_attr_type = atoi(obj_attr_type_s); + if (obj_attr_type == HWLOC_TOPOLOGY_DIFF_OBJ_ATTR_INFO && !obj_attr_name_s) { + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: missing mandatory obj attr info name attribute\n", + state->global->msgprefix); + break; + } + + /* now we know we have everything we need */ + diff = malloc(sizeof(*diff)); + if (!diff) + return -1; + diff->obj_attr.type = HWLOC_TOPOLOGY_DIFF_OBJ_ATTR; + diff->obj_attr.obj_depth = atoi(obj_depth_s); + diff->obj_attr.obj_index = atoi(obj_index_s); + memset(&diff->obj_attr.diff, 0, sizeof(diff->obj_attr.diff)); + diff->obj_attr.diff.generic.type = obj_attr_type; + + switch (atoi(obj_attr_type_s)) { + case HWLOC_TOPOLOGY_DIFF_OBJ_ATTR_SIZE: + diff->obj_attr.diff.uint64.oldvalue = strtoull(obj_attr_oldvalue_s, NULL, 0); + diff->obj_attr.diff.uint64.newvalue = strtoull(obj_attr_newvalue_s, NULL, 0); + break; + case HWLOC_TOPOLOGY_DIFF_OBJ_ATTR_INFO: + diff->obj_attr.diff.string.name = strdup(obj_attr_name_s); + /* FALLTHRU */ + case HWLOC_TOPOLOGY_DIFF_OBJ_ATTR_NAME: + diff->obj_attr.diff.string.oldvalue = strdup(obj_attr_oldvalue_s); + diff->obj_attr.diff.string.newvalue = strdup(obj_attr_newvalue_s); + break; + } + + if (*firstdiffp) + (*lastdiffp)->generic.next = diff; + else + *firstdiffp = diff; + *lastdiffp = diff; + diff->generic.next = NULL; + } + } + } + + return state->global->close_tag(state); +} + +int +hwloc__xml_import_diff(hwloc__xml_import_state_t state, + hwloc_topology_diff_t *firstdiffp) +{ + hwloc_topology_diff_t firstdiff = NULL, lastdiff = NULL; + *firstdiffp = NULL; + + while (1) { + struct hwloc__xml_import_state_s childstate; + char *tag; + int ret; + + ret = state->global->find_child(state, &childstate, &tag); + if (ret < 0) + return -1; + if (!ret) + break; + + if (!strcmp(tag, "diff")) { + ret = hwloc__xml_import_diff_one(&childstate, &firstdiff, &lastdiff); + } else + ret = -1; + + if (ret < 0) + return ret; + + state->global->close_child(&childstate); + } + + *firstdiffp = firstdiff; + return 0; +} + +/*********************************** + ********* main XML import ********* + ***********************************/ + +static void +hwloc_convert_from_v1dist_floats(hwloc_topology_t topology, unsigned nbobjs, float *floats, uint64_t *u64s) +{ + unsigned i; + int is_uint; + char *env; + float scale = 1000.f; + char scalestring[20]; + + env = getenv("HWLOC_XML_V1DIST_SCALE"); + if (env) { + scale = (float) atof(env); + goto scale; + } + + is_uint = 1; + /* find out if all values are integers */ + for(i=0; i .001f && fptr < .999f) { + is_uint = 0; + break; + } + u64s[i] = (int)(f+.5f); + } + if (is_uint) + return; + + scale: + /* TODO heuristic to find a good scale */ + for(i=0; itopology; + struct hwloc_xml_backend_data_s *data = backend->private_data; + struct hwloc__xml_import_state_s state, childstate; + struct hwloc_obj *root = topology->levels[0][0]; + char *tag; + int gotignored = 0; + hwloc_localeswitch_declare; + char *env; + int ret; + + state.global = data; + + assert(!root->cpuset); + + hwloc_localeswitch_init(); + + data->nbnumanodes = 0; + data->first_numanode = data->last_numanode = NULL; + data->first_v1dist = data->last_v1dist = NULL; + + env = getenv("HWLOC_DONT_MERGE_DIE_GROUPS"); + data->dont_merge_die_groups = env && atoi(env); + + ret = data->look_init(data, &state); + if (ret < 0) + goto failed; + + if (data->version_major > 2) { + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: cannot import XML version %u.%u > 2\n", + data->msgprefix, data->version_major, data->version_minor); + goto err; + } + + /* find root object tag and import it */ + ret = state.global->find_child(&state, &childstate, &tag); + if (ret < 0 || !ret || strcmp(tag, "object")) + goto failed; + ret = hwloc__xml_import_object(topology, data, NULL /* no parent */, root, + &gotignored, + &childstate); + if (ret < 0) + goto failed; + state.global->close_child(&childstate); + assert(!gotignored); + + /* the root may have changed if we had to reinsert a Machine */ + root = topology->levels[0][0]; + + if (data->version_major >= 2) { + /* find v2 distances */ + while (1) { + ret = state.global->find_child(&state, &childstate, &tag); + if (ret < 0) + goto failed; + if (!ret) + break; + if (strcmp(tag, "distances2")) { + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: ignoring unknown tag `%s' after root object, expected `distances2'\n", + data->msgprefix, tag); + goto done; + } + ret = hwloc__xml_v2import_distances(topology, &childstate); + if (ret < 0) + goto failed; + state.global->close_child(&childstate); + } + } + + /* find end of topology tag */ + state.global->close_tag(&state); + +done: + if (!root->cpuset) { + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: invalid root object without cpuset\n", + data->msgprefix); + goto err; + } + + /* update pre-v2.0 memory group gp_index */ + if (data->version_major < 2 && data->first_numanode) { + hwloc_obj_t node = data->first_numanode; + do { + if (node->parent->type == HWLOC_OBJ_GROUP + && !node->parent->gp_index) + node->parent->gp_index = topology->next_gp_index++; + node = node->next_cousin; + } while (node); + } + + if (data->version_major < 2 && data->first_v1dist) { + /* handle v1 distances */ + struct hwloc__xml_imported_v1distances_s *v1dist, *v1next = data->first_v1dist; + while ((v1dist = v1next) != NULL) { + unsigned nbobjs = v1dist->nbobjs; + v1next = v1dist->next; + /* Handle distances as NUMA node distances if nbobjs matches. + * Otherwise drop, only NUMA distances really matter. + * + * We could also attach to a random level with the right nbobjs, + * but it would require to have those objects in the original XML order (like the first_numanode cousin-list). + * because the topology order can be different if some parents are ignored during load. + */ + if (nbobjs == data->nbnumanodes) { + hwloc_obj_t *objs = malloc(nbobjs*sizeof(hwloc_obj_t)); + uint64_t *values = malloc(nbobjs*nbobjs*sizeof(*values)); + if (objs && values) { + hwloc_obj_t node; + unsigned i; + for(i=0, node = data->first_numanode; + inext_cousin) + objs[i] = node; +hwloc_convert_from_v1dist_floats(topology, nbobjs, v1dist->floats, values); + hwloc_internal_distances_add(topology, nbobjs, objs, values, v1dist->kind, 0); + } else { + free(objs); + free(values); + } + } + free(v1dist->floats); + free(v1dist); + } + data->first_v1dist = data->last_v1dist = NULL; + } + + /* FIXME: + * We should check that the existing object sets are consistent: + * no intersection between objects of a same level, + * object sets included in parent sets. + * hwloc never generated such buggy XML, but users could create one. + * + * We want to add these checks to the existing core code that + * adds missing sets and propagates parent/children sets + * (in case another backend ever generates buggy object sets as well). + */ + + if (data->version_major >= 2) { + /* v2 must have non-empty nodesets since at least one NUMA node is required */ + if (!root->nodeset) { + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: invalid root object without nodeset\n", + data->msgprefix); + goto err; + } + if (hwloc_bitmap_iszero(root->nodeset)) { + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: invalid root object with empty nodeset\n", + data->msgprefix); + goto err; + } + } else { + /* if v1 without nodeset, the core will add a default NUMA node and nodesets */ + } + + /* allocate default cpusets and nodesets if missing, the core will restrict them */ + hwloc_alloc_root_sets(root); + + /* keep the "Backend" information intact */ + /* we could add "BackendSource=XML" to notify that XML was used between the actual backend and here */ + + topology->support.discovery->pu = 1; + if (data->nbnumanodes) { + topology->support.discovery->numa = 1; + topology->support.discovery->numa_memory = 1; // FIXME + } + + if (data->look_done) + data->look_done(data, 0); + + hwloc_localeswitch_fini(); + return 0; + + failed: + if (data->look_done) + data->look_done(data, -1); + if (hwloc__xml_verbose()) + fprintf(stderr, "%s: XML component discovery failed.\n", + data->msgprefix); + err: + hwloc_free_object_siblings_and_children(root->first_child); + root->first_child = NULL; + hwloc_free_object_siblings_and_children(root->memory_first_child); + root->memory_first_child = NULL; + hwloc_free_object_siblings_and_children(root->io_first_child); + root->io_first_child = NULL; + hwloc_free_object_siblings_and_children(root->misc_first_child); + root->misc_first_child = NULL; + + /* make sure the core will abort */ + if (root->cpuset) + hwloc_bitmap_zero(root->cpuset); + if (root->nodeset) + hwloc_bitmap_zero(root->nodeset); + + hwloc_localeswitch_fini(); + return -1; +} + +/* this can be the first XML call */ +int +hwloc_topology_diff_load_xml(const char *xmlpath, + hwloc_topology_diff_t *firstdiffp, char **refnamep) +{ + struct hwloc__xml_import_state_s state; + struct hwloc_xml_backend_data_s fakedata; /* only for storing global info during parsing */ + hwloc_localeswitch_declare; + const char *local_basename; + int force_nolibxml; + int ret; + + state.global = &fakedata; + + local_basename = strrchr(xmlpath, '/'); + if (local_basename) + local_basename++; + else + local_basename = xmlpath; + fakedata.msgprefix = strdup(local_basename); + + hwloc_components_init(); + assert(hwloc_nolibxml_callbacks); + + hwloc_localeswitch_init(); + + *firstdiffp = NULL; + + force_nolibxml = hwloc_nolibxml_import(); +retry: + if (!hwloc_libxml_callbacks || (hwloc_nolibxml_callbacks && force_nolibxml)) + ret = hwloc_nolibxml_callbacks->import_diff(&state, xmlpath, NULL, 0, firstdiffp, refnamep); + else { + ret = hwloc_libxml_callbacks->import_diff(&state, xmlpath, NULL, 0, firstdiffp, refnamep); + if (ret < 0 && errno == ENOSYS) { + hwloc_libxml_callbacks = NULL; + goto retry; + } + } + + hwloc_localeswitch_fini(); + hwloc_components_fini(); + free(fakedata.msgprefix); + return ret; +} + +/* this can be the first XML call */ +int +hwloc_topology_diff_load_xmlbuffer(const char *xmlbuffer, int buflen, + hwloc_topology_diff_t *firstdiffp, char **refnamep) +{ + struct hwloc__xml_import_state_s state; + struct hwloc_xml_backend_data_s fakedata; /* only for storing global info during parsing */ + hwloc_localeswitch_declare; + int force_nolibxml; + int ret; + + state.global = &fakedata; + fakedata.msgprefix = strdup("xmldiffbuffer"); + + hwloc_components_init(); + assert(hwloc_nolibxml_callbacks); + + hwloc_localeswitch_init(); + + *firstdiffp = NULL; + + force_nolibxml = hwloc_nolibxml_import(); + retry: + if (!hwloc_libxml_callbacks || (hwloc_nolibxml_callbacks && force_nolibxml)) + ret = hwloc_nolibxml_callbacks->import_diff(&state, NULL, xmlbuffer, buflen, firstdiffp, refnamep); + else { + ret = hwloc_libxml_callbacks->import_diff(&state, NULL, xmlbuffer, buflen, firstdiffp, refnamep); + if (ret < 0 && errno == ENOSYS) { + hwloc_libxml_callbacks = NULL; + goto retry; + } + } + + hwloc_localeswitch_fini(); + hwloc_components_fini(); + free(fakedata.msgprefix); + return ret; +} + +/************************************************ + ********* XML export (common routines) ********* + ************************************************/ + +#define HWLOC_XML_CHAR_VALID(c) (((c) >= 32 && (c) <= 126) || (c) == '\t' || (c) == '\n' || (c) == '\r') + +static int +hwloc__xml_export_check_buffer(const char *buf, size_t length) +{ + unsigned i; + for(i=0; itype == HWLOC_OBJ_PACKAGE) + state->new_prop(state, "type", "Socket"); + else if (v1export && hwloc__obj_type_is_cache(obj->type)) + state->new_prop(state, "type", "Cache"); + else + state->new_prop(state, "type", hwloc_obj_type_string(obj->type)); + + if (obj->os_index != HWLOC_UNKNOWN_INDEX) { + sprintf(tmp, "%u", obj->os_index); + state->new_prop(state, "os_index", tmp); + } + + if (obj->cpuset) { + if (v1export && obj->type == HWLOC_OBJ_NUMANODE && obj->sibling_rank > 0) { + /* v1 non-first NUMA nodes have empty cpusets */ + state->new_prop(state, "cpuset", "0x0"); + state->new_prop(state, "online_cpuset", "0x0"); + state->new_prop(state, "complete_cpuset", "0x0"); + state->new_prop(state, "allowed_cpuset", "0x0"); + + } else { + /* normal case */ + hwloc_bitmap_asprintf(&setstring, obj->cpuset); + state->new_prop(state, "cpuset", setstring); + + hwloc_bitmap_asprintf(&setstring2, obj->complete_cpuset); + state->new_prop(state, "complete_cpuset", setstring2); + free(setstring2); + + if (v1export) + state->new_prop(state, "online_cpuset", setstring); + free(setstring); + + if (v1export || !obj->parent) { + hwloc_bitmap_t allowed_cpuset = hwloc_bitmap_dup(obj->cpuset); + hwloc_bitmap_and(allowed_cpuset, allowed_cpuset, topology->allowed_cpuset); + hwloc_bitmap_asprintf(&setstring, allowed_cpuset); + state->new_prop(state, "allowed_cpuset", setstring); + free(setstring); + hwloc_bitmap_free(allowed_cpuset); + } + } + + /* If exporting v1, we should clear second local NUMA bits from nodeset, + * but the importer will clear them anyway. + */ + hwloc_bitmap_asprintf(&setstring, obj->nodeset); + state->new_prop(state, "nodeset", setstring); + free(setstring); + + hwloc_bitmap_asprintf(&setstring, obj->complete_nodeset); + state->new_prop(state, "complete_nodeset", setstring); + free(setstring); + + if (v1export || !obj->parent) { + hwloc_bitmap_t allowed_nodeset = hwloc_bitmap_dup(obj->nodeset); + hwloc_bitmap_and(allowed_nodeset, allowed_nodeset, topology->allowed_nodeset); + hwloc_bitmap_asprintf(&setstring, allowed_nodeset); + state->new_prop(state, "allowed_nodeset", setstring); + free(setstring); + hwloc_bitmap_free(allowed_nodeset); + } + } + + if (!v1export) { + sprintf(tmp, "%llu", (unsigned long long) obj->gp_index); + state->new_prop(state, "gp_index", tmp); + } + + if (obj->name) { + char *name = hwloc__xml_export_safestrdup(obj->name); + state->new_prop(state, "name", name); + free(name); + } + if (!v1export && obj->subtype) { + char *subtype = hwloc__xml_export_safestrdup(obj->subtype); + state->new_prop(state, "subtype", subtype); + free(subtype); + } + + switch (obj->type) { + case HWLOC_OBJ_NUMANODE: + if (obj->attr->numanode.local_memory) { + sprintf(tmp, "%llu", (unsigned long long) obj->attr->numanode.local_memory); + state->new_prop(state, "local_memory", tmp); + } + for(i=0; iattr->numanode.page_types_len; i++) { + struct hwloc__xml_export_state_s childstate; + state->new_child(state, &childstate, "page_type"); + sprintf(tmp, "%llu", (unsigned long long) obj->attr->numanode.page_types[i].size); + childstate.new_prop(&childstate, "size", tmp); + sprintf(tmp, "%llu", (unsigned long long) obj->attr->numanode.page_types[i].count); + childstate.new_prop(&childstate, "count", tmp); + childstate.end_object(&childstate, "page_type"); + } + break; + case HWLOC_OBJ_L1CACHE: + case HWLOC_OBJ_L2CACHE: + case HWLOC_OBJ_L3CACHE: + case HWLOC_OBJ_L4CACHE: + case HWLOC_OBJ_L5CACHE: + case HWLOC_OBJ_L1ICACHE: + case HWLOC_OBJ_L2ICACHE: + case HWLOC_OBJ_L3ICACHE: + sprintf(tmp, "%llu", (unsigned long long) obj->attr->cache.size); + state->new_prop(state, "cache_size", tmp); + sprintf(tmp, "%u", obj->attr->cache.depth); + state->new_prop(state, "depth", tmp); + sprintf(tmp, "%u", (unsigned) obj->attr->cache.linesize); + state->new_prop(state, "cache_linesize", tmp); + sprintf(tmp, "%d", obj->attr->cache.associativity); + state->new_prop(state, "cache_associativity", tmp); + sprintf(tmp, "%d", (int) obj->attr->cache.type); + state->new_prop(state, "cache_type", tmp); + break; + case HWLOC_OBJ_GROUP: + if (v1export) { + sprintf(tmp, "%u", obj->attr->group.depth); + state->new_prop(state, "depth", tmp); + if (obj->attr->group.dont_merge) + state->new_prop(state, "dont_merge", "1"); + } else { + sprintf(tmp, "%u", obj->attr->group.kind); + state->new_prop(state, "kind", tmp); + sprintf(tmp, "%u", obj->attr->group.subkind); + state->new_prop(state, "subkind", tmp); + if (obj->attr->group.dont_merge) + state->new_prop(state, "dont_merge", "1"); + } + break; + case HWLOC_OBJ_BRIDGE: + sprintf(tmp, "%d-%d", (int) obj->attr->bridge.upstream_type, (int) obj->attr->bridge.downstream_type); + state->new_prop(state, "bridge_type", tmp); + sprintf(tmp, "%u", obj->attr->bridge.depth); + state->new_prop(state, "depth", tmp); + if (obj->attr->bridge.downstream_type == HWLOC_OBJ_BRIDGE_PCI) { + sprintf(tmp, "%04x:[%02x-%02x]", + (unsigned) obj->attr->bridge.downstream.pci.domain, + (unsigned) obj->attr->bridge.downstream.pci.secondary_bus, + (unsigned) obj->attr->bridge.downstream.pci.subordinate_bus); + state->new_prop(state, "bridge_pci", tmp); + } + if (obj->attr->bridge.upstream_type != HWLOC_OBJ_BRIDGE_PCI) + break; + /* FALLTHRU */ + case HWLOC_OBJ_PCI_DEVICE: + sprintf(tmp, "%04x:%02x:%02x.%01x", + (unsigned) obj->attr->pcidev.domain, + (unsigned) obj->attr->pcidev.bus, + (unsigned) obj->attr->pcidev.dev, + (unsigned) obj->attr->pcidev.func); + state->new_prop(state, "pci_busid", tmp); + sprintf(tmp, "%04x [%04x:%04x] [%04x:%04x] %02x", + (unsigned) obj->attr->pcidev.class_id, + (unsigned) obj->attr->pcidev.vendor_id, (unsigned) obj->attr->pcidev.device_id, + (unsigned) obj->attr->pcidev.subvendor_id, (unsigned) obj->attr->pcidev.subdevice_id, + (unsigned) obj->attr->pcidev.revision); + state->new_prop(state, "pci_type", tmp); + sprintf(tmp, "%f", obj->attr->pcidev.linkspeed); + state->new_prop(state, "pci_link_speed", tmp); + break; + case HWLOC_OBJ_OS_DEVICE: + sprintf(tmp, "%d", (int) obj->attr->osdev.type); + state->new_prop(state, "osdev_type", tmp); + break; + default: + break; + } + + for(i=0; iinfos_count; i++) { + char *name = hwloc__xml_export_safestrdup(obj->infos[i].name); + char *value = hwloc__xml_export_safestrdup(obj->infos[i].value); + struct hwloc__xml_export_state_s childstate; + state->new_child(state, &childstate, "info"); + childstate.new_prop(&childstate, "name", name); + childstate.new_prop(&childstate, "value", value); + childstate.end_object(&childstate, "info"); + free(name); + free(value); + } + if (v1export && obj->subtype) { + char *subtype = hwloc__xml_export_safestrdup(obj->subtype); + struct hwloc__xml_export_state_s childstate; + int is_coproctype = (obj->type == HWLOC_OBJ_OS_DEVICE && obj->attr->osdev.type == HWLOC_OBJ_OSDEV_COPROC); + state->new_child(state, &childstate, "info"); + childstate.new_prop(&childstate, "name", is_coproctype ? "CoProcType" : "Type"); + childstate.new_prop(&childstate, "value", subtype); + childstate.end_object(&childstate, "info"); + free(subtype); + } + + if (v1export && !obj->parent) { + /* only latency matrices covering the entire machine can be exported to v1 */ + struct hwloc_internal_distances_s *dist; + /* refresh distances since we need objects below */ + hwloc_internal_distances_refresh(topology); + for(dist = topology->first_dist; dist; dist = dist->next) { + struct hwloc__xml_export_state_s childstate; + unsigned nbobjs = dist->nbobjs; + int depth; + + if (nbobjs != (unsigned) hwloc_get_nbobjs_by_type(topology, dist->type)) + continue; + if (!(dist->kind & HWLOC_DISTANCES_KIND_MEANS_LATENCY)) + continue; + { + HWLOC_VLA(unsigned, logical_to_v2array, nbobjs); + for(i=0; iobjs[i]->logical_index] = i; + + /* compute the relative depth */ + if (dist->type == HWLOC_OBJ_NUMANODE) { + /* for NUMA nodes, use the highest normal-parent depth + 1 */ + depth = -1; + for(i=0; iobjs[i]->parent; + while (hwloc__obj_type_is_memory(parent->type)) + parent = parent->parent; + if (parent->depth+1 > depth) + depth = parent->depth+1; + } + } else { + /* for non-NUMA nodes, increase the object depth if any of them has memory above */ + int parent_with_memory = 0; + for(i=0; iobjs[i]->parent; + while (parent) { + if (parent->memory_first_child) { + parent_with_memory = 1; + goto done; + } + parent = parent->parent; + } + } + done: + depth = hwloc_get_type_depth(topology, dist->type) + parent_with_memory; + } + + state->new_child(state, &childstate, "distances"); + sprintf(tmp, "%u", nbobjs); + childstate.new_prop(&childstate, "nbobjs", tmp); + sprintf(tmp, "%d", depth); + childstate.new_prop(&childstate, "relative_depth", tmp); + sprintf(tmp, "%f", 1.f); + childstate.new_prop(&childstate, "latency_base", tmp); + for(i=0; ivalues[k]); + greatchildstate.new_prop(&greatchildstate, "value", tmp); + greatchildstate.end_object(&greatchildstate, "latency"); + } + } + childstate.end_object(&childstate, "distances"); + } + } + } + + if (obj->userdata && topology->userdata_export_cb) + topology->userdata_export_cb((void*) state, topology, obj); +} + +static void +hwloc__xml_v2export_object (hwloc__xml_export_state_t parentstate, hwloc_topology_t topology, hwloc_obj_t obj, unsigned long flags) +{ + struct hwloc__xml_export_state_s state; + hwloc_obj_t child; + + parentstate->new_child(parentstate, &state, "object"); + + hwloc__xml_export_object_contents(&state, topology, obj, flags); + + for_each_memory_child(child, obj) + hwloc__xml_v2export_object (&state, topology, child, flags); + for_each_child(child, obj) + hwloc__xml_v2export_object (&state, topology, child, flags); + for_each_io_child(child, obj) + hwloc__xml_v2export_object (&state, topology, child, flags); + for_each_misc_child(child, obj) + hwloc__xml_v2export_object (&state, topology, child, flags); + + state.end_object(&state, "object"); +} + +static void +hwloc__xml_v1export_object (hwloc__xml_export_state_t parentstate, hwloc_topology_t topology, hwloc_obj_t obj, unsigned long flags); + +static void +hwloc__xml_v1export_object_with_memory(hwloc__xml_export_state_t parentstate, hwloc_topology_t topology, hwloc_obj_t obj, unsigned long flags) +{ + struct hwloc__xml_export_state_s gstate, mstate, ostate, *state = parentstate; + hwloc_obj_t child; + + if (obj->parent->arity > 1 && obj->memory_arity > 1 && parentstate->global->v1_memory_group) { + /* child has sibling, we must add a Group around those memory children */ + hwloc_obj_t group = parentstate->global->v1_memory_group; + parentstate->new_child(parentstate, &gstate, "object"); + group->cpuset = obj->cpuset; + group->complete_cpuset = obj->complete_cpuset; + group->nodeset = obj->nodeset; + group->complete_nodeset = obj->complete_nodeset; + hwloc__xml_export_object_contents (&gstate, topology, group, flags); + group->cpuset = NULL; + group->complete_cpuset = NULL; + group->nodeset = NULL; + group->complete_nodeset = NULL; + state = &gstate; + } + + /* export first memory child */ + child = obj->memory_first_child; + assert(child->type == HWLOC_OBJ_NUMANODE); + state->new_child(state, &mstate, "object"); + hwloc__xml_export_object_contents (&mstate, topology, child, flags); + + /* then the actual object */ + mstate.new_child(&mstate, &ostate, "object"); + hwloc__xml_export_object_contents (&ostate, topology, obj, flags); + + /* then its normal/io/misc children */ + for_each_child(child, obj) + hwloc__xml_v1export_object (&ostate, topology, child, flags); + for_each_io_child(child, obj) + hwloc__xml_v1export_object (&ostate, topology, child, flags); + for_each_misc_child(child, obj) + hwloc__xml_v1export_object (&ostate, topology, child, flags); + + /* close object and first memory child */ + ostate.end_object(&ostate, "object"); + mstate.end_object(&mstate, "object"); + + /* now other memory children */ + for_each_memory_child(child, obj) + if (child->sibling_rank > 0) + hwloc__xml_v1export_object (state, topology, child, flags); + + if (state == &gstate) { + /* close group if any */ + gstate.end_object(&gstate, "object"); + } +} + +static void +hwloc__xml_v1export_object (hwloc__xml_export_state_t parentstate, hwloc_topology_t topology, hwloc_obj_t obj, unsigned long flags) +{ + struct hwloc__xml_export_state_s state; + hwloc_obj_t child; + + parentstate->new_child(parentstate, &state, "object"); + + hwloc__xml_export_object_contents(&state, topology, obj, flags); + + for_each_child(child, obj) { + if (!child->memory_arity) { + /* no memory child, just export normally */ + hwloc__xml_v1export_object (&state, topology, child, flags); + } else { + hwloc__xml_v1export_object_with_memory(&state, topology, child, flags); + } + } + + for_each_io_child(child, obj) + hwloc__xml_v1export_object (&state, topology, child, flags); + for_each_misc_child(child, obj) + hwloc__xml_v1export_object (&state, topology, child, flags); + + state.end_object(&state, "object"); +} + +#define EXPORT_ARRAY(state, type, nr, values, tagname, format, maxperline) do { \ + unsigned _i = 0; \ + while (_i<(nr)) { \ + char _tmp[255]; /* enough for (snprintf(format)+space) x maxperline */ \ + char _tmp2[16]; \ + size_t _len = 0; \ + unsigned _j; \ + struct hwloc__xml_export_state_s _childstate; \ + (state)->new_child(state, &_childstate, tagname); \ + for(_j=0; \ + _i+_j<(nr) && _jfirst_dist; dist; dist = dist->next) { + char tmp[255]; + unsigned nbobjs = dist->nbobjs; + struct hwloc__xml_export_state_s state; + + parentstate->new_child(parentstate, &state, "distances2"); + + state.new_prop(&state, "type", hwloc_obj_type_string(dist->type)); + sprintf(tmp, "%u", nbobjs); + state.new_prop(&state, "nbobjs", tmp); + sprintf(tmp, "%lu", dist->kind); + state.new_prop(&state, "kind", tmp); + + state.new_prop(&state, "indexing", + (dist->type == HWLOC_OBJ_NUMANODE || dist->type == HWLOC_OBJ_PU) ? "os" : "gp"); + /* TODO don't hardwire 10 below. either snprintf the max to guess it, or just append until the end of the buffer */ + EXPORT_ARRAY(&state, unsigned long long, nbobjs, dist->indexes, "indexes", "%llu", 10); + EXPORT_ARRAY(&state, unsigned long long, nbobjs*nbobjs, dist->values, "u64values", "%llu", 10); + state.end_object(&state, "distances2"); + } +} + +void +hwloc__xml_export_topology(hwloc__xml_export_state_t state, hwloc_topology_t topology, unsigned long flags) +{ + hwloc_obj_t root = hwloc_get_root_obj(topology); + + if (flags & HWLOC_TOPOLOGY_EXPORT_XML_FLAG_V1) { + if (root->memory_first_child) { + /* we don't use hwloc__xml_v1export_object_with_memory() because we want/can keep root above the numa node */ + struct hwloc__xml_export_state_s rstate, mstate; + hwloc_obj_t child; + /* export the root */ + state->new_child(state, &rstate, "object"); + hwloc__xml_export_object_contents (&rstate, topology, root, flags); + /* export first memory child */ + child = root->memory_first_child; + assert(child->type == HWLOC_OBJ_NUMANODE); + rstate.new_child(&rstate, &mstate, "object"); + hwloc__xml_export_object_contents (&mstate, topology, child, flags); + /* then its normal/io/misc children */ + for_each_child(child, root) + hwloc__xml_v1export_object (&mstate, topology, child, flags); + for_each_io_child(child, root) + hwloc__xml_v1export_object (&mstate, topology, child, flags); + for_each_misc_child(child, root) + hwloc__xml_v1export_object (&mstate, topology, child, flags); + /* close first memory child */ + mstate.end_object(&mstate, "object"); + /* now other memory children */ + for_each_memory_child(child, root) + if (child->sibling_rank > 0) + hwloc__xml_v1export_object (&rstate, topology, child, flags); + /* close the root */ + rstate.end_object(&rstate, "object"); + } else { + hwloc__xml_v1export_object(state, topology, root, flags); + } + + } else { + hwloc__xml_v2export_object (state, topology, root, flags); + hwloc__xml_v2export_distances (state, topology); + } +} + +void +hwloc__xml_export_diff(hwloc__xml_export_state_t parentstate, hwloc_topology_diff_t diff) +{ + while (diff) { + struct hwloc__xml_export_state_s state; + char tmp[255]; + + parentstate->new_child(parentstate, &state, "diff"); + + sprintf(tmp, "%d", (int) diff->generic.type); + state.new_prop(&state, "type", tmp); + + switch (diff->generic.type) { + case HWLOC_TOPOLOGY_DIFF_OBJ_ATTR: + sprintf(tmp, "%d", diff->obj_attr.obj_depth); + state.new_prop(&state, "obj_depth", tmp); + sprintf(tmp, "%u", diff->obj_attr.obj_index); + state.new_prop(&state, "obj_index", tmp); + + sprintf(tmp, "%d", (int) diff->obj_attr.diff.generic.type); + state.new_prop(&state, "obj_attr_type", tmp); + + switch (diff->obj_attr.diff.generic.type) { + case HWLOC_TOPOLOGY_DIFF_OBJ_ATTR_SIZE: + sprintf(tmp, "%llu", (unsigned long long) diff->obj_attr.diff.uint64.index); + state.new_prop(&state, "obj_attr_index", tmp); + sprintf(tmp, "%llu", (unsigned long long) diff->obj_attr.diff.uint64.oldvalue); + state.new_prop(&state, "obj_attr_oldvalue", tmp); + sprintf(tmp, "%llu", (unsigned long long) diff->obj_attr.diff.uint64.newvalue); + state.new_prop(&state, "obj_attr_newvalue", tmp); + break; + case HWLOC_TOPOLOGY_DIFF_OBJ_ATTR_NAME: + case HWLOC_TOPOLOGY_DIFF_OBJ_ATTR_INFO: + if (diff->obj_attr.diff.string.name) + state.new_prop(&state, "obj_attr_name", diff->obj_attr.diff.string.name); + state.new_prop(&state, "obj_attr_oldvalue", diff->obj_attr.diff.string.oldvalue); + state.new_prop(&state, "obj_attr_newvalue", diff->obj_attr.diff.string.newvalue); + break; + } + + break; + default: + assert(0); + } + state.end_object(&state, "diff"); + + diff = diff->generic.next; + } +} + +/********************************** + ********* main XML export ******** + **********************************/ + +/* this can be the first XML call */ +int hwloc_topology_export_xml(hwloc_topology_t topology, const char *filename, unsigned long flags) +{ + hwloc_localeswitch_declare; + struct hwloc__xml_export_data_s edata; + int force_nolibxml; + int ret; + + if (!topology->is_loaded) { + errno = EINVAL; + return -1; + } + + assert(hwloc_nolibxml_callbacks); /* the core called components_init() for the topology */ + + if (flags & ~HWLOC_TOPOLOGY_EXPORT_XML_FLAG_V1) { + errno = EINVAL; + return -1; + } + + hwloc_internal_distances_refresh(topology); + + hwloc_localeswitch_init(); + + edata.v1_memory_group = NULL; + if (flags & HWLOC_TOPOLOGY_EXPORT_XML_FLAG_V1) + /* temporary group to be used during v1 export of memory children */ + edata.v1_memory_group = hwloc_alloc_setup_object(topology, HWLOC_OBJ_GROUP, HWLOC_UNKNOWN_INDEX); + + force_nolibxml = hwloc_nolibxml_export(); +retry: + if (!hwloc_libxml_callbacks || (hwloc_nolibxml_callbacks && force_nolibxml)) + ret = hwloc_nolibxml_callbacks->export_file(topology, &edata, filename, flags); + else { + ret = hwloc_libxml_callbacks->export_file(topology, &edata, filename, flags); + if (ret < 0 && errno == ENOSYS) { + hwloc_libxml_callbacks = NULL; + goto retry; + } + } + + if (edata.v1_memory_group) + hwloc_free_unlinked_object(edata.v1_memory_group); + + hwloc_localeswitch_fini(); + return ret; +} + +/* this can be the first XML call */ +int hwloc_topology_export_xmlbuffer(hwloc_topology_t topology, char **xmlbuffer, int *buflen, unsigned long flags) +{ + hwloc_localeswitch_declare; + struct hwloc__xml_export_data_s edata; + int force_nolibxml; + int ret; + + if (!topology->is_loaded) { + errno = EINVAL; + return -1; + } + + assert(hwloc_nolibxml_callbacks); /* the core called components_init() for the topology */ + + if (flags & ~HWLOC_TOPOLOGY_EXPORT_XML_FLAG_V1) { + errno = EINVAL; + return -1; + } + + hwloc_internal_distances_refresh(topology); + + hwloc_localeswitch_init(); + + edata.v1_memory_group = NULL; + if (flags & HWLOC_TOPOLOGY_EXPORT_XML_FLAG_V1) + /* temporary group to be used during v1 export of memory children */ + edata.v1_memory_group = hwloc_alloc_setup_object(topology, HWLOC_OBJ_GROUP, HWLOC_UNKNOWN_INDEX); + + force_nolibxml = hwloc_nolibxml_export(); +retry: + if (!hwloc_libxml_callbacks || (hwloc_nolibxml_callbacks && force_nolibxml)) + ret = hwloc_nolibxml_callbacks->export_buffer(topology, &edata, xmlbuffer, buflen, flags); + else { + ret = hwloc_libxml_callbacks->export_buffer(topology, &edata, xmlbuffer, buflen, flags); + if (ret < 0 && errno == ENOSYS) { + hwloc_libxml_callbacks = NULL; + goto retry; + } + } + + if (edata.v1_memory_group) + hwloc_free_unlinked_object(edata.v1_memory_group); + + hwloc_localeswitch_fini(); + return ret; +} + +/* this can be the first XML call */ +int +hwloc_topology_diff_export_xml(hwloc_topology_diff_t diff, const char *refname, + const char *filename) +{ + hwloc_localeswitch_declare; + hwloc_topology_diff_t tmpdiff; + int force_nolibxml; + int ret; + + tmpdiff = diff; + while (tmpdiff) { + if (tmpdiff->generic.type == HWLOC_TOPOLOGY_DIFF_TOO_COMPLEX) { + errno = EINVAL; + return -1; + } + tmpdiff = tmpdiff->generic.next; + } + + hwloc_components_init(); + assert(hwloc_nolibxml_callbacks); + + hwloc_localeswitch_init(); + + force_nolibxml = hwloc_nolibxml_export(); +retry: + if (!hwloc_libxml_callbacks || (hwloc_nolibxml_callbacks && force_nolibxml)) + ret = hwloc_nolibxml_callbacks->export_diff_file(diff, refname, filename); + else { + ret = hwloc_libxml_callbacks->export_diff_file(diff, refname, filename); + if (ret < 0 && errno == ENOSYS) { + hwloc_libxml_callbacks = NULL; + goto retry; + } + } + + hwloc_localeswitch_fini(); + hwloc_components_fini(); + return ret; +} + +/* this can be the first XML call */ +int +hwloc_topology_diff_export_xmlbuffer(hwloc_topology_diff_t diff, const char *refname, + char **xmlbuffer, int *buflen) +{ + hwloc_localeswitch_declare; + hwloc_topology_diff_t tmpdiff; + int force_nolibxml; + int ret; + + tmpdiff = diff; + while (tmpdiff) { + if (tmpdiff->generic.type == HWLOC_TOPOLOGY_DIFF_TOO_COMPLEX) { + errno = EINVAL; + return -1; + } + tmpdiff = tmpdiff->generic.next; + } + + hwloc_components_init(); + assert(hwloc_nolibxml_callbacks); + + hwloc_localeswitch_init(); + + force_nolibxml = hwloc_nolibxml_export(); +retry: + if (!hwloc_libxml_callbacks || (hwloc_nolibxml_callbacks && force_nolibxml)) + ret = hwloc_nolibxml_callbacks->export_diff_buffer(diff, refname, xmlbuffer, buflen); + else { + ret = hwloc_libxml_callbacks->export_diff_buffer(diff, refname, xmlbuffer, buflen); + if (ret < 0 && errno == ENOSYS) { + hwloc_libxml_callbacks = NULL; + goto retry; + } + } + + hwloc_localeswitch_fini(); + hwloc_components_fini(); + return ret; +} + +void hwloc_free_xmlbuffer(hwloc_topology_t topology __hwloc_attribute_unused, char *xmlbuffer) +{ + int force_nolibxml; + + assert(hwloc_nolibxml_callbacks); /* the core called components_init() for the topology */ + + force_nolibxml = hwloc_nolibxml_export(); + if (!hwloc_libxml_callbacks || (hwloc_nolibxml_callbacks && force_nolibxml)) + hwloc_nolibxml_callbacks->free_buffer(xmlbuffer); + else + hwloc_libxml_callbacks->free_buffer(xmlbuffer); +} + +void +hwloc_topology_set_userdata_export_callback(hwloc_topology_t topology, + void (*export)(void *reserved, struct hwloc_topology *topology, struct hwloc_obj *obj)) +{ + topology->userdata_export_cb = export; +} + +static void +hwloc__export_obj_userdata(hwloc__xml_export_state_t parentstate, int encoded, + const char *name, size_t length, const void *buffer, size_t encoded_length) +{ + struct hwloc__xml_export_state_s state; + char tmp[255]; + parentstate->new_child(parentstate, &state, "userdata"); + if (name) + state.new_prop(&state, "name", name); + sprintf(tmp, "%lu", (unsigned long) length); + state.new_prop(&state, "length", tmp); + if (encoded) + state.new_prop(&state, "encoding", "base64"); + if (encoded_length) + state.add_content(&state, buffer, encoded ? encoded_length : length); + state.end_object(&state, "userdata"); +} + +int +hwloc_export_obj_userdata(void *reserved, + struct hwloc_topology *topology, struct hwloc_obj *obj __hwloc_attribute_unused, + const char *name, const void *buffer, size_t length) +{ + hwloc__xml_export_state_t state = reserved; + + if (!buffer) { + errno = EINVAL; + return -1; + } + + if ((name && hwloc__xml_export_check_buffer(name, strlen(name)) < 0) + || hwloc__xml_export_check_buffer(buffer, length) < 0) { + errno = EINVAL; + return -1; + } + + if (topology->userdata_not_decoded) { + int encoded; + size_t encoded_length; + const char *realname; + if (!strncmp(name, "base64", 6)) { + encoded = 1; + encoded_length = BASE64_ENCODED_LENGTH(length); + } else { + assert(!strncmp(name, "normal", 6)); + encoded = 0; + encoded_length = length; + } + if (name[6] == ':') + realname = name+7; + else { + assert(!strcmp(name+6, "-anon")); + realname = NULL; + } + hwloc__export_obj_userdata(state, encoded, realname, length, buffer, encoded_length); + + } else + hwloc__export_obj_userdata(state, 0, name, length, buffer, length); + + return 0; +} + +int +hwloc_export_obj_userdata_base64(void *reserved, + struct hwloc_topology *topology __hwloc_attribute_unused, struct hwloc_obj *obj __hwloc_attribute_unused, + const char *name, const void *buffer, size_t length) +{ + hwloc__xml_export_state_t state = reserved; + size_t encoded_length; + char *encoded_buffer; + int ret __hwloc_attribute_unused; + + if (!buffer) { + errno = EINVAL; + return -1; + } + + assert(!topology->userdata_not_decoded); + + if (name && hwloc__xml_export_check_buffer(name, strlen(name)) < 0) { + errno = EINVAL; + return -1; + } + + encoded_length = BASE64_ENCODED_LENGTH(length); + encoded_buffer = malloc(encoded_length+1); + if (!encoded_buffer) { + errno = ENOMEM; + return -1; + } + + ret = hwloc_encode_to_base64(buffer, length, encoded_buffer, encoded_length+1); + assert(ret == (int) encoded_length); + + hwloc__export_obj_userdata(state, 1, name, length, encoded_buffer, encoded_length); + + free(encoded_buffer); + return 0; +} + +void +hwloc_topology_set_userdata_import_callback(hwloc_topology_t topology, + void (*import)(struct hwloc_topology *topology, struct hwloc_obj *obj, const char *name, const void *buffer, size_t length)) +{ + topology->userdata_import_cb = import; +} + +/*************************************** + ************ XML component ************ + ***************************************/ + +static void +hwloc_xml_backend_disable(struct hwloc_backend *backend) +{ + struct hwloc_xml_backend_data_s *data = backend->private_data; + data->backend_exit(data); + free(data->msgprefix); + free(data); +} + +static struct hwloc_backend * +hwloc_xml_component_instantiate(struct hwloc_disc_component *component, + const void *_data1, + const void *_data2, + const void *_data3) +{ + struct hwloc_xml_backend_data_s *data; + struct hwloc_backend *backend; + const char *env; + int force_nolibxml; + const char * xmlpath = (const char *) _data1; + const char * xmlbuffer = (const char *) _data2; + int xmlbuflen = (int)(uintptr_t) _data3; + const char *local_basename; + int err; + + assert(hwloc_nolibxml_callbacks); /* the core called components_init() for the component's topology */ + + if (!xmlpath && !xmlbuffer) { + env = getenv("HWLOC_XMLFILE"); + if (env) { + /* 'xml' was given in HWLOC_COMPONENTS without a filename */ + xmlpath = env; + } else { + errno = EINVAL; + goto out; + } + } + + backend = hwloc_backend_alloc(component); + if (!backend) + goto out; + + data = malloc(sizeof(*data)); + if (!data) { + errno = ENOMEM; + goto out_with_backend; + } + + backend->private_data = data; + backend->discover = hwloc_look_xml; + backend->disable = hwloc_xml_backend_disable; + backend->is_thissystem = 0; + + if (xmlpath) { + local_basename = strrchr(xmlpath, '/'); + if (local_basename) + local_basename++; + else + local_basename = xmlpath; + } else { + local_basename = "xmlbuffer"; + } + data->msgprefix = strdup(local_basename); + + force_nolibxml = hwloc_nolibxml_import(); +retry: + if (!hwloc_libxml_callbacks || (hwloc_nolibxml_callbacks && force_nolibxml)) + err = hwloc_nolibxml_callbacks->backend_init(data, xmlpath, xmlbuffer, xmlbuflen); + else { + err = hwloc_libxml_callbacks->backend_init(data, xmlpath, xmlbuffer, xmlbuflen); + if (err < 0 && errno == ENOSYS) { + hwloc_libxml_callbacks = NULL; + goto retry; + } + } + if (err < 0) + goto out_with_data; + + return backend; + + out_with_data: + free(data->msgprefix); + free(data); + out_with_backend: + free(backend); + out: + return NULL; +} + +static struct hwloc_disc_component hwloc_xml_disc_component = { + HWLOC_DISC_COMPONENT_TYPE_GLOBAL, + "xml", + ~0, + hwloc_xml_component_instantiate, + 30, + 1, + NULL +}; + +const struct hwloc_component hwloc_xml_component = { + HWLOC_COMPONENT_ABI, + NULL, NULL, + HWLOC_COMPONENT_TYPE_DISC, + 0, + &hwloc_xml_disc_component +}; diff --git a/src/3rdparty/hwloc/src/topology.c b/src/3rdparty/hwloc/src/topology.c new file mode 100644 index 00000000..55678a08 --- /dev/null +++ b/src/3rdparty/hwloc/src/topology.c @@ -0,0 +1,4484 @@ +/* + * Copyright © 2009 CNRS + * Copyright © 2009-2018 Inria. All rights reserved. + * Copyright © 2009-2012 Université Bordeaux + * Copyright © 2009-2011 Cisco Systems, Inc. All rights reserved. + * See COPYING in top-level directory. + */ + +#include + +#define _ATFILE_SOURCE +#include +#include +#ifdef HAVE_DIRENT_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef HAVE_MACH_MACH_INIT_H +#include +#endif +#ifdef HAVE_MACH_MACH_HOST_H +#include +#endif + +#ifdef HAVE_SYS_PARAM_H +#include +#endif + +#ifdef HAVE_SYS_SYSCTL_H +#include +#endif + +#ifdef HWLOC_WIN_SYS +#include +#endif + +unsigned hwloc_get_api_version(void) +{ + return HWLOC_API_VERSION; +} + +int hwloc_topology_abi_check(hwloc_topology_t topology) +{ + return topology->topology_abi != HWLOC_TOPOLOGY_ABI ? -1 : 0; +} + +int hwloc_hide_errors(void) +{ + static int hide = 0; + static int checked = 0; + if (!checked) { + const char *envvar = getenv("HWLOC_HIDE_ERRORS"); + if (envvar) + hide = atoi(envvar); + checked = 1; + } + return hide; +} + +void hwloc_report_os_error(const char *msg, int line) +{ + static int reported = 0; + + if (!reported && !hwloc_hide_errors()) { + fprintf(stderr, "****************************************************************************\n"); + fprintf(stderr, "* hwloc %s received invalid information from the operating system.\n", HWLOC_VERSION); + fprintf(stderr, "*\n"); + fprintf(stderr, "* %s\n", msg); + fprintf(stderr, "* Error occurred in topology.c line %d\n", line); + fprintf(stderr, "*\n"); + fprintf(stderr, "* The following FAQ entry in the hwloc documentation may help:\n"); + fprintf(stderr, "* What should I do when hwloc reports \"operating system\" warnings?\n"); + fprintf(stderr, "* Otherwise please report this error message to the hwloc user's mailing list,\n"); +#ifdef HWLOC_LINUX_SYS + fprintf(stderr, "* along with the files generated by the hwloc-gather-topology script.\n"); +#else + fprintf(stderr, "* along with any relevant topology information from your platform.\n"); +#endif + fprintf(stderr, "* \n"); + fprintf(stderr, "* hwloc will now ignore this invalid topology information and continue.\n"); + fprintf(stderr, "****************************************************************************\n"); + reported = 1; + } +} + +#if defined(HAVE_SYSCTLBYNAME) +int hwloc_get_sysctlbyname(const char *name, int64_t *ret) +{ + union { + int32_t i32; + int64_t i64; + } n; + size_t size = sizeof(n); + if (sysctlbyname(name, &n, &size, NULL, 0)) + return -1; + switch (size) { + case sizeof(n.i32): + *ret = n.i32; + break; + case sizeof(n.i64): + *ret = n.i64; + break; + default: + return -1; + } + return 0; +} +#endif + +#if defined(HAVE_SYSCTL) +int hwloc_get_sysctl(int name[], unsigned namelen, int *ret) +{ + int n; + size_t size = sizeof(n); + if (sysctl(name, namelen, &n, &size, NULL, 0)) + return -1; + if (size != sizeof(n)) + return -1; + *ret = n; + return 0; +} +#endif + +/* Return the OS-provided number of processors. Unlike other methods such as + reading sysfs on Linux, this method is not virtualizable; thus it's only + used as a fall-back method, allowing virtual backends (FSROOT, etc) to + have the desired effect. */ +#ifndef HWLOC_WIN_SYS /* The windows implementation is in topology-windows.c */ +int +hwloc_fallback_nbprocessors(struct hwloc_topology *topology __hwloc_attribute_unused) { + int n; +#if HAVE_DECL__SC_NPROCESSORS_ONLN + n = sysconf(_SC_NPROCESSORS_ONLN); +#elif HAVE_DECL__SC_NPROC_ONLN + n = sysconf(_SC_NPROC_ONLN); +#elif HAVE_DECL__SC_NPROCESSORS_CONF + n = sysconf(_SC_NPROCESSORS_CONF); +#elif HAVE_DECL__SC_NPROC_CONF + n = sysconf(_SC_NPROC_CONF); +#elif defined(HAVE_HOST_INFO) && HAVE_HOST_INFO + struct host_basic_info info; + mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT; + host_info(mach_host_self(), HOST_BASIC_INFO, (integer_t*) &info, &count); + n = info.avail_cpus; +#elif defined(HAVE_SYSCTLBYNAME) + int64_t nn; + if (hwloc_get_sysctlbyname("hw.ncpu", &nn)) + nn = -1; + n = nn; +#elif defined(HAVE_SYSCTL) && HAVE_DECL_CTL_HW && HAVE_DECL_HW_NCPU + static int name[2] = {CTL_HW, HW_NCPU}; + if (hwloc_get_sysctl(name, sizeof(name)/sizeof(*name), &n)) + n = -1; +#else +#ifdef __GNUC__ +#warning No known way to discover number of available processors on this system +#endif + n = -1; +#endif + return n; +} +#endif /* !HWLOC_WIN_SYS */ + +/* + * Use the given number of processors to set a PU level. + */ +void +hwloc_setup_pu_level(struct hwloc_topology *topology, + unsigned nb_pus) +{ + struct hwloc_obj *obj; + unsigned oscpu,cpu; + + hwloc_debug("%s", "\n\n * CPU cpusets *\n\n"); + for (cpu=0,oscpu=0; cpucpuset = hwloc_bitmap_alloc(); + hwloc_bitmap_only(obj->cpuset, oscpu); + + hwloc_debug_2args_bitmap("cpu %u (os %u) has cpuset %s\n", + cpu, oscpu, obj->cpuset); + hwloc_insert_object_by_cpuset(topology, obj); + + cpu++; + } +} + +/* Traverse children of a parent in a safe way: reread the next pointer as + * appropriate to prevent crash on child deletion: */ +#define for_each_child_safe(child, parent, pchild) \ + for (pchild = &(parent)->first_child, child = *pchild; \ + child; \ + /* Check whether the current child was not dropped. */ \ + (*pchild == child ? pchild = &(child->next_sibling) : NULL), \ + /* Get pointer to next child. */ \ + child = *pchild) +#define for_each_memory_child_safe(child, parent, pchild) \ + for (pchild = &(parent)->memory_first_child, child = *pchild; \ + child; \ + /* Check whether the current child was not dropped. */ \ + (*pchild == child ? pchild = &(child->next_sibling) : NULL), \ + /* Get pointer to next child. */ \ + child = *pchild) +#define for_each_io_child_safe(child, parent, pchild) \ + for (pchild = &(parent)->io_first_child, child = *pchild; \ + child; \ + /* Check whether the current child was not dropped. */ \ + (*pchild == child ? pchild = &(child->next_sibling) : NULL), \ + /* Get pointer to next child. */ \ + child = *pchild) +#define for_each_misc_child_safe(child, parent, pchild) \ + for (pchild = &(parent)->misc_first_child, child = *pchild; \ + child; \ + /* Check whether the current child was not dropped. */ \ + (*pchild == child ? pchild = &(child->next_sibling) : NULL), \ + /* Get pointer to next child. */ \ + child = *pchild) + +#ifdef HWLOC_DEBUG +/* Just for debugging. */ +static void +hwloc_debug_print_object(int indent __hwloc_attribute_unused, hwloc_obj_t obj) +{ + char type[64], idx[12], attr[1024], *cpuset = NULL; + hwloc_debug("%*s", 2*indent, ""); + hwloc_obj_type_snprintf(type, sizeof(type), obj, 1); + if (obj->os_index != HWLOC_UNKNOWN_INDEX) + snprintf(idx, sizeof(idx), "#%u", obj->os_index); + else + *idx = '\0'; + hwloc_obj_attr_snprintf(attr, sizeof(attr), obj, " ", 1); + hwloc_debug("%s%s%s%s%s", type, idx, *attr ? "(" : "", attr, *attr ? ")" : ""); + if (obj->name) + hwloc_debug(" name \"%s\"", obj->name); + if (obj->subtype) + hwloc_debug(" subtype \"%s\"", obj->subtype); + if (obj->cpuset) { + hwloc_bitmap_asprintf(&cpuset, obj->cpuset); + hwloc_debug(" cpuset %s", cpuset); + free(cpuset); + } + if (obj->complete_cpuset) { + hwloc_bitmap_asprintf(&cpuset, obj->complete_cpuset); + hwloc_debug(" complete %s", cpuset); + free(cpuset); + } + if (obj->nodeset) { + hwloc_bitmap_asprintf(&cpuset, obj->nodeset); + hwloc_debug(" nodeset %s", cpuset); + free(cpuset); + } + if (obj->complete_nodeset) { + hwloc_bitmap_asprintf(&cpuset, obj->complete_nodeset); + hwloc_debug(" completeN %s", cpuset); + free(cpuset); + } + if (obj->arity) + hwloc_debug(" arity %u", obj->arity); + hwloc_debug("%s", "\n"); +} + +static void +hwloc_debug_print_objects(int indent __hwloc_attribute_unused, hwloc_obj_t obj) +{ + hwloc_obj_t child; + hwloc_debug_print_object(indent, obj); + for_each_child (child, obj) + hwloc_debug_print_objects(indent + 1, child); + for_each_memory_child (child, obj) + hwloc_debug_print_objects(indent + 1, child); + for_each_io_child (child, obj) + hwloc_debug_print_objects(indent + 1, child); + for_each_misc_child (child, obj) + hwloc_debug_print_objects(indent + 1, child); +} +#else /* !HWLOC_DEBUG */ +#define hwloc_debug_print_object(indent, obj) do { /* nothing */ } while (0) +#define hwloc_debug_print_objects(indent, obj) do { /* nothing */ } while (0) +#endif /* !HWLOC_DEBUG */ + +void hwloc__free_infos(struct hwloc_info_s *infos, unsigned count) +{ + unsigned i; + for(i=0; iinfos, &obj->infos_count, name, value); +} + +/* This function may be called with topology->tma set, it cannot free() or realloc() */ +static int hwloc__tma_dup_infos(struct hwloc_tma *tma, hwloc_obj_t new, hwloc_obj_t src) +{ + unsigned i, j; + new->infos = hwloc_tma_calloc(tma, src->infos_count * sizeof(*src->infos)); + if (!new->infos) + return -1; + for(i=0; iinfos_count; i++) { + new->infos[i].name = hwloc_tma_strdup(tma, src->infos[i].name); + new->infos[i].value = hwloc_tma_strdup(tma, src->infos[i].value); + if (!new->infos[i].name || !new->infos[i].value) + goto failed; + } + new->infos_count = src->infos_count; + return 0; + + failed: + assert(!tma || !tma->dontfree); /* this tma cannot fail to allocate */ + for(j=0; j<=i; j++) { + free(new->infos[i].name); + free(new->infos[i].value); + } + free(new->infos); + new->infos = NULL; + return -1; +} + +static void +hwloc__free_object_contents(hwloc_obj_t obj) +{ + switch (obj->type) { + case HWLOC_OBJ_NUMANODE: + free(obj->attr->numanode.page_types); + break; + default: + break; + } + hwloc__free_infos(obj->infos, obj->infos_count); + free(obj->attr); + free(obj->children); + free(obj->subtype); + free(obj->name); + hwloc_bitmap_free(obj->cpuset); + hwloc_bitmap_free(obj->complete_cpuset); + hwloc_bitmap_free(obj->nodeset); + hwloc_bitmap_free(obj->complete_nodeset); +} + +/* Free an object and all its content. */ +void +hwloc_free_unlinked_object(hwloc_obj_t obj) +{ + hwloc__free_object_contents(obj); + free(obj); +} + +/* Replace old with contents of new object, and make new freeable by the caller. + * Only updates next_sibling/first_child pointers, + * so may only be used during early discovery. + */ +static void +hwloc_replace_linked_object(hwloc_obj_t old, hwloc_obj_t new) +{ + /* drop old fields */ + hwloc__free_object_contents(old); + /* copy old tree pointers to new */ + new->parent = old->parent; + new->next_sibling = old->next_sibling; + new->first_child = old->first_child; + new->memory_first_child = old->memory_first_child; + new->io_first_child = old->io_first_child; + new->misc_first_child = old->misc_first_child; + /* copy new contents to old now that tree pointers are OK */ + memcpy(old, new, sizeof(*old)); + /* clear new to that we may free it */ + memset(new, 0,sizeof(*new)); +} + +/* Remove an object and its children from its parent and free them. + * Only updates next_sibling/first_child pointers, + * so may only be used during early discovery or during destroy. + */ +static void +unlink_and_free_object_and_children(hwloc_obj_t *pobj) +{ + hwloc_obj_t obj = *pobj, child, *pchild; + + for_each_child_safe(child, obj, pchild) + unlink_and_free_object_and_children(pchild); + for_each_memory_child_safe(child, obj, pchild) + unlink_and_free_object_and_children(pchild); + for_each_io_child_safe(child, obj, pchild) + unlink_and_free_object_and_children(pchild); + for_each_misc_child_safe(child, obj, pchild) + unlink_and_free_object_and_children(pchild); + + *pobj = obj->next_sibling; + hwloc_free_unlinked_object(obj); +} + +/* Free an object and its children without unlinking from parent. + */ +void +hwloc_free_object_and_children(hwloc_obj_t obj) +{ + unlink_and_free_object_and_children(&obj); +} + +/* Free an object, its next siblings and their children without unlinking from parent. + */ +void +hwloc_free_object_siblings_and_children(hwloc_obj_t obj) +{ + while (obj) + unlink_and_free_object_and_children(&obj); +} + +/* insert the (non-empty) list of sibling starting at firstnew as new children of newparent, + * and return the address of the pointer to the next one + */ +static hwloc_obj_t * +insert_siblings_list(hwloc_obj_t *firstp, hwloc_obj_t firstnew, hwloc_obj_t newparent) +{ + hwloc_obj_t tmp; + assert(firstnew); + *firstp = tmp = firstnew; + tmp->parent = newparent; + while (tmp->next_sibling) { + tmp = tmp->next_sibling; + tmp->parent = newparent; + } + return &tmp->next_sibling; +} + +/* Take the new list starting at firstnew and prepend it to the old list starting at *firstp, + * and mark the new children as children of newparent. + * May be used during early or late discovery (updates prev_sibling and sibling_rank). + * List firstnew must be non-NULL. + */ +static void +prepend_siblings_list(hwloc_obj_t *firstp, hwloc_obj_t firstnew, hwloc_obj_t newparent) +{ + hwloc_obj_t *tmpp, tmp, last; + unsigned length; + + /* update parent pointers and find the length and end of the new list */ + for(length = 0, tmpp = &firstnew, last = NULL ; *tmpp; length++, last = *tmpp, tmpp = &((*tmpp)->next_sibling)) + (*tmpp)->parent = newparent; + + /* update sibling_rank */ + for(tmp = *firstp; tmp; tmp = tmp->next_sibling) + tmp->sibling_rank += length; /* if it wasn't initialized yet, it'll be overwritten later */ + + /* place the existing list at the end of the new one */ + *tmpp = *firstp; + if (*firstp) + (*firstp)->prev_sibling = last; + + /* use the beginning of the new list now */ + *firstp = firstnew; +} + +/* Take the new list starting at firstnew and append it to the old list starting at *firstp, + * and mark the new children as children of newparent. + * May be used during early or late discovery (updates prev_sibling and sibling_rank). + */ +static void +append_siblings_list(hwloc_obj_t *firstp, hwloc_obj_t firstnew, hwloc_obj_t newparent) +{ + hwloc_obj_t *tmpp, tmp, last; + unsigned length; + + /* find the length and end of the existing list */ + for(length = 0, tmpp = firstp, last = NULL ; *tmpp; length++, last = *tmpp, tmpp = &((*tmpp)->next_sibling)); + + /* update parent pointers and sibling_rank */ + for(tmp = firstnew; tmp; tmp = tmp->next_sibling) { + tmp->parent = newparent; + tmp->sibling_rank += length; /* if it wasn't set yet, it'll be overwritten later */ + } + + /* place new list at the end of the old one */ + *tmpp = firstnew; + if (firstnew) + firstnew->prev_sibling = last; +} + +/* Remove an object from its parent and free it. + * Only updates next_sibling/first_child pointers, + * so may only be used during early discovery. + * + * Children are inserted in the parent. + * If children should be inserted somewhere else (e.g. when merging with a child), + * the caller should move them before calling this function. + */ +static void +unlink_and_free_single_object(hwloc_obj_t *pparent) +{ + hwloc_obj_t old = *pparent; + hwloc_obj_t *lastp; + + if (old->type == HWLOC_OBJ_MISC) { + /* Misc object */ + + /* no normal children */ + assert(!old->first_child); + /* no memory children */ + assert(!old->memory_first_child); + /* no I/O children */ + assert(!old->io_first_child); + + if (old->misc_first_child) + /* insert old misc object children as new siblings below parent instead of old */ + lastp = insert_siblings_list(pparent, old->misc_first_child, old->parent); + else + lastp = pparent; + /* append old siblings back */ + *lastp = old->next_sibling; + + } else if (hwloc__obj_type_is_io(old->type)) { + /* I/O object */ + + /* no normal children */ + assert(!old->first_child); + /* no memory children */ + assert(!old->memory_first_child); + + if (old->io_first_child) + /* insert old I/O object children as new siblings below parent instead of old */ + lastp = insert_siblings_list(pparent, old->io_first_child, old->parent); + else + lastp = pparent; + /* append old siblings back */ + *lastp = old->next_sibling; + + /* append old Misc children to parent */ + if (old->misc_first_child) + append_siblings_list(&old->parent->misc_first_child, old->misc_first_child, old->parent); + + } else if (hwloc__obj_type_is_memory(old->type)) { + /* memory object */ + + /* no normal children */ + assert(!old->first_child); + /* no I/O children */ + assert(!old->io_first_child); + + if (old->memory_first_child) + /* insert old memory object children as new siblings below parent instead of old */ + lastp = insert_siblings_list(pparent, old->memory_first_child, old->parent); + else + lastp = pparent; + /* append old siblings back */ + *lastp = old->next_sibling; + + /* append old Misc children to parent */ + if (old->misc_first_child) + append_siblings_list(&old->parent->misc_first_child, old->misc_first_child, old->parent); + + } else { + /* Normal object */ + + if (old->first_child) + /* insert old object children as new siblings below parent instead of old */ + lastp = insert_siblings_list(pparent, old->first_child, old->parent); + else + lastp = pparent; + /* append old siblings back */ + *lastp = old->next_sibling; + + /* append old memory, I/O and Misc children to parent + * old->parent cannot be NULL (removing root), misc children should have been moved by the caller earlier. + */ + if (old->memory_first_child) + append_siblings_list(&old->parent->memory_first_child, old->memory_first_child, old->parent); + if (old->io_first_child) + append_siblings_list(&old->parent->io_first_child, old->io_first_child, old->parent); + if (old->misc_first_child) + append_siblings_list(&old->parent->misc_first_child, old->misc_first_child, old->parent); + } + + hwloc_free_unlinked_object(old); +} + +/* This function may use a tma, it cannot free() or realloc() */ +static int +hwloc__duplicate_object(struct hwloc_topology *newtopology, + struct hwloc_obj *newparent, + struct hwloc_obj *newobj, + struct hwloc_obj *src) +{ + struct hwloc_tma *tma = newtopology->tma; + hwloc_obj_t *level; + unsigned level_width; + size_t len; + unsigned i; + hwloc_obj_t child, prev; + int err = 0; + + /* either we're duplicating to an already allocated new root, which has no newparent, + * or we're duplicating to a non-yet allocated new non-root, which will have a newparent. + */ + assert(!newparent == !!newobj); + + if (!newobj) { + newobj = hwloc_alloc_setup_object(newtopology, src->type, src->os_index); + if (!newobj) + return -1; + } + + /* duplicate all non-object-pointer fields */ + newobj->logical_index = src->logical_index; + newobj->depth = src->depth; + newobj->sibling_rank = src->sibling_rank; + + newobj->type = src->type; + newobj->os_index = src->os_index; + newobj->gp_index = src->gp_index; + newobj->symmetric_subtree = src->symmetric_subtree; + + if (src->name) + newobj->name = hwloc_tma_strdup(tma, src->name); + if (src->subtype) + newobj->subtype = hwloc_tma_strdup(tma, src->subtype); + newobj->userdata = src->userdata; + + newobj->total_memory = src->total_memory; + + memcpy(newobj->attr, src->attr, sizeof(*newobj->attr)); + + if (src->type == HWLOC_OBJ_NUMANODE && src->attr->numanode.page_types_len) { + len = src->attr->numanode.page_types_len * sizeof(struct hwloc_memory_page_type_s); + newobj->attr->numanode.page_types = hwloc_tma_malloc(tma, len); + memcpy(newobj->attr->numanode.page_types, src->attr->numanode.page_types, len); + } + + newobj->cpuset = hwloc_bitmap_tma_dup(tma, src->cpuset); + newobj->complete_cpuset = hwloc_bitmap_tma_dup(tma, src->complete_cpuset); + newobj->nodeset = hwloc_bitmap_tma_dup(tma, src->nodeset); + newobj->complete_nodeset = hwloc_bitmap_tma_dup(tma, src->complete_nodeset); + + hwloc__tma_dup_infos(tma, newobj, src); + + /* find our level */ + if (src->depth < 0) { + i = HWLOC_SLEVEL_FROM_DEPTH(src->depth); + level = newtopology->slevels[i].objs; + level_width = newtopology->slevels[i].nbobjs; + /* deal with first/last pointers of special levels, even if not really needed */ + if (!newobj->logical_index) + newtopology->slevels[i].first = newobj; + if (newobj->logical_index == newtopology->slevels[i].nbobjs - 1) + newtopology->slevels[i].last = newobj; + } else { + level = newtopology->levels[src->depth]; + level_width = newtopology->level_nbobjects[src->depth]; + } + /* place us for real */ + assert(newobj->logical_index < level_width); + level[newobj->logical_index] = newobj; + /* link to already-inserted cousins + * (hwloc_pci_belowroot_apply_locality() can cause out-of-order logical indexes) + */ + if (newobj->logical_index > 0 && level[newobj->logical_index-1]) { + newobj->prev_cousin = level[newobj->logical_index-1]; + level[newobj->logical_index-1]->next_cousin = newobj; + } + if (newobj->logical_index < level_width-1 && level[newobj->logical_index+1]) { + newobj->next_cousin = level[newobj->logical_index+1]; + level[newobj->logical_index+1]->prev_cousin = newobj; + } + + /* prepare for children */ + if (src->arity) { + newobj->children = hwloc_tma_malloc(tma, src->arity * sizeof(*newobj->children)); + if (!newobj->children) + return -1; + } + newobj->arity = src->arity; + newobj->memory_arity = src->memory_arity; + newobj->io_arity = src->io_arity; + newobj->misc_arity = src->misc_arity; + + /* actually insert children now */ + for_each_child(child, src) { + err = hwloc__duplicate_object(newtopology, newobj, NULL, child); + if (err < 0) + goto out_with_children; + } + for_each_memory_child(child, src) { + err = hwloc__duplicate_object(newtopology, newobj, NULL, child); + if (err < 0) + return err; + } + for_each_io_child(child, src) { + err = hwloc__duplicate_object(newtopology, newobj, NULL, child); + if (err < 0) + goto out_with_children; + } + for_each_misc_child(child, src) { + err = hwloc__duplicate_object(newtopology, newobj, NULL, child); + if (err < 0) + goto out_with_children; + } + + out_with_children: + + /* link children if all of them where inserted */ + if (!err) { + /* only next_sibling is set by insert_by_parent(). + * sibling_rank was set above. + */ + if (newobj->arity) { + newobj->children[0]->prev_sibling = NULL; + for(i=1; iarity; i++) + newobj->children[i]->prev_sibling = newobj->children[i-1]; + newobj->last_child = newobj->children[newobj->arity-1]; + } + if (newobj->memory_arity) { + child = newobj->memory_first_child; + prev = NULL; + while (child) { + child->prev_sibling = prev; + prev = child; + child = child->next_sibling; + } + } + if (newobj->io_arity) { + child = newobj->io_first_child; + prev = NULL; + while (child) { + child->prev_sibling = prev; + prev = child; + child = child->next_sibling; + } + } + if (newobj->misc_arity) { + child = newobj->misc_first_child; + prev = NULL; + while (child) { + child->prev_sibling = prev; + prev = child; + child = child->next_sibling; + } + } + } + + /* some children insertion may have failed, but some children may have been inserted below us already. + * keep inserting ourself and let the caller clean the entire tree if we return an error. + */ + + if (newparent) { + /* no need to check the children insert order here, the source topology + * is supposed to be OK already, and we have debug asserts. + */ + hwloc_insert_object_by_parent(newtopology, newparent, newobj); + + /* place us inside our parent children array */ + if (hwloc__obj_type_is_normal(newobj->type)) + newparent->children[newobj->sibling_rank] = newobj; + } + + return err; +} + +static int +hwloc__topology_init (struct hwloc_topology **topologyp, unsigned nblevels, struct hwloc_tma *tma); + +/* This function may use a tma, it cannot free() or realloc() */ +int +hwloc__topology_dup(hwloc_topology_t *newp, + hwloc_topology_t old, + struct hwloc_tma *tma) +{ + hwloc_topology_t new; + hwloc_obj_t newroot; + hwloc_obj_t oldroot = hwloc_get_root_obj(old); + unsigned i; + int err; + + if (!old->is_loaded) { + errno = EINVAL; + return -1; + } + + err = hwloc__topology_init(&new, old->nb_levels_allocated, tma); + if (err < 0) + goto out; + + new->flags = old->flags; + memcpy(new->type_filter, old->type_filter, sizeof(old->type_filter)); + new->is_thissystem = old->is_thissystem; + new->is_loaded = 1; + new->pid = old->pid; + new->next_gp_index = old->next_gp_index; + + memcpy(&new->binding_hooks, &old->binding_hooks, sizeof(old->binding_hooks)); + + memcpy(new->support.discovery, old->support.discovery, sizeof(*old->support.discovery)); + memcpy(new->support.cpubind, old->support.cpubind, sizeof(*old->support.cpubind)); + memcpy(new->support.membind, old->support.membind, sizeof(*old->support.membind)); + + new->allowed_cpuset = hwloc_bitmap_tma_dup(tma, old->allowed_cpuset); + new->allowed_nodeset = hwloc_bitmap_tma_dup(tma, old->allowed_nodeset); + + new->userdata_export_cb = old->userdata_export_cb; + new->userdata_import_cb = old->userdata_import_cb; + new->userdata_not_decoded = old->userdata_not_decoded; + + assert(!old->machine_memory.local_memory); + assert(!old->machine_memory.page_types_len); + assert(!old->machine_memory.page_types); + + for(i = HWLOC_OBJ_TYPE_MIN; i < HWLOC_OBJ_TYPE_MAX; i++) + new->type_depth[i] = old->type_depth[i]; + + /* duplicate levels and we'll place objects there when duplicating objects */ + new->nb_levels = old->nb_levels; + assert(new->nb_levels_allocated >= new->nb_levels); + for(i=1 /* root level already allocated */ ; inb_levels; i++) { + new->level_nbobjects[i] = old->level_nbobjects[i]; + new->levels[i] = hwloc_tma_calloc(tma, new->level_nbobjects[i] * sizeof(*new->levels[i])); + } + for(i=0; islevels[i].nbobjs = old->slevels[i].nbobjs; + if (new->slevels[i].nbobjs) + new->slevels[i].objs = hwloc_tma_calloc(tma, new->slevels[i].nbobjs * sizeof(*new->slevels[i].objs)); + } + + /* recursively duplicate object children */ + newroot = hwloc_get_root_obj(new); + err = hwloc__duplicate_object(new, NULL, newroot, oldroot); + if (err < 0) + goto out_with_topology; + + err = hwloc_internal_distances_dup(new, old); + if (err < 0) + goto out_with_topology; + + /* we connected everything during duplication */ + new->modified = 0; + + /* no need to duplicate backends, topology is already loaded */ + new->backends = NULL; + new->get_pci_busid_cpuset_backend = NULL; + +#ifndef HWLOC_DEBUG + if (getenv("HWLOC_DEBUG_CHECK")) +#endif + hwloc_topology_check(new); + + *newp = new; + return 0; + + out_with_topology: + assert(!tma || !tma->dontfree); /* this tma cannot fail to allocate */ + hwloc_topology_destroy(new); + out: + return -1; +} + +int +hwloc_topology_dup(hwloc_topology_t *newp, + hwloc_topology_t old) +{ + return hwloc__topology_dup(newp, old, NULL); +} + +/* WARNING: The indexes of this array MUST match the ordering that of + the obj_order_type[] array, below. Specifically, the values must + be laid out such that: + + obj_order_type[obj_type_order[N]] = N + + for all HWLOC_OBJ_* values of N. Put differently: + + obj_type_order[A] = B + + where the A values are in order of the hwloc_obj_type_t enum, and + the B values are the corresponding indexes of obj_order_type. + + We can't use C99 syntax to initialize this in a little safer manner + -- bummer. :-( + + Correctness is asserted in hwloc_topology_init() when debug is enabled. + */ +/***** Make sure you update obj_type_priority[] below as well. *****/ +static const unsigned obj_type_order[] = { + /* first entry is HWLOC_OBJ_MACHINE */ 0, + /* next entry is HWLOC_OBJ_PACKAGE */ 3, + /* next entry is HWLOC_OBJ_CORE */ 12, + /* next entry is HWLOC_OBJ_PU */ 16, + /* next entry is HWLOC_OBJ_L1CACHE */ 10, + /* next entry is HWLOC_OBJ_L2CACHE */ 8, + /* next entry is HWLOC_OBJ_L3CACHE */ 6, + /* next entry is HWLOC_OBJ_L4CACHE */ 5, + /* next entry is HWLOC_OBJ_L5CACHE */ 4, + /* next entry is HWLOC_OBJ_L1ICACHE */ 11, + /* next entry is HWLOC_OBJ_L2ICACHE */ 9, + /* next entry is HWLOC_OBJ_L3ICACHE */ 7, + /* next entry is HWLOC_OBJ_GROUP */ 1, + /* next entry is HWLOC_OBJ_NUMANODE */ 2, + /* next entry is HWLOC_OBJ_BRIDGE */ 13, + /* next entry is HWLOC_OBJ_PCI_DEVICE */ 14, + /* next entry is HWLOC_OBJ_OS_DEVICE */ 15, + /* next entry is HWLOC_OBJ_MISC */ 17 +}; + +#ifndef NDEBUG /* only used in debug check assert if !NDEBUG */ +static const hwloc_obj_type_t obj_order_type[] = { + HWLOC_OBJ_MACHINE, + HWLOC_OBJ_GROUP, + HWLOC_OBJ_NUMANODE, + HWLOC_OBJ_PACKAGE, + HWLOC_OBJ_L5CACHE, + HWLOC_OBJ_L4CACHE, + HWLOC_OBJ_L3CACHE, + HWLOC_OBJ_L3ICACHE, + HWLOC_OBJ_L2CACHE, + HWLOC_OBJ_L2ICACHE, + HWLOC_OBJ_L1CACHE, + HWLOC_OBJ_L1ICACHE, + HWLOC_OBJ_CORE, + HWLOC_OBJ_BRIDGE, + HWLOC_OBJ_PCI_DEVICE, + HWLOC_OBJ_OS_DEVICE, + HWLOC_OBJ_PU, + HWLOC_OBJ_MISC /* Misc is always a leaf */ +}; +#endif +/***** Make sure you update obj_type_priority[] below as well. *****/ + +/* priority to be used when merging identical parent/children object + * (in merge_useless_child), keep the highest priority one. + * + * Always keep Machine/NUMANode/PU/PCIDev/OSDev + * then Core + * then Package + * then Cache, + * then Instruction Caches + * then always drop Group/Misc/Bridge. + * + * Some type won't actually ever be involved in such merging. + */ +/***** Make sure you update this array when changing the list of types. *****/ +static const int obj_type_priority[] = { + /* first entry is HWLOC_OBJ_MACHINE */ 90, + /* next entry is HWLOC_OBJ_PACKAGE */ 40, + /* next entry is HWLOC_OBJ_CORE */ 60, + /* next entry is HWLOC_OBJ_PU */ 100, + /* next entry is HWLOC_OBJ_L1CACHE */ 20, + /* next entry is HWLOC_OBJ_L2CACHE */ 20, + /* next entry is HWLOC_OBJ_L3CACHE */ 20, + /* next entry is HWLOC_OBJ_L4CACHE */ 20, + /* next entry is HWLOC_OBJ_L5CACHE */ 20, + /* next entry is HWLOC_OBJ_L1ICACHE */ 19, + /* next entry is HWLOC_OBJ_L2ICACHE */ 19, + /* next entry is HWLOC_OBJ_L3ICACHE */ 19, + /* next entry is HWLOC_OBJ_GROUP */ 0, + /* next entry is HWLOC_OBJ_NUMANODE */ 100, + /* next entry is HWLOC_OBJ_BRIDGE */ 0, + /* next entry is HWLOC_OBJ_PCI_DEVICE */ 100, + /* next entry is HWLOC_OBJ_OS_DEVICE */ 100, + /* next entry is HWLOC_OBJ_MISC */ 0 +}; + +int hwloc_compare_types (hwloc_obj_type_t type1, hwloc_obj_type_t type2) +{ + unsigned order1 = obj_type_order[type1]; + unsigned order2 = obj_type_order[type2]; + + /* only normal objects are comparable. others are only comparable with machine */ + if (!hwloc__obj_type_is_normal(type1) + && hwloc__obj_type_is_normal(type2) && type2 != HWLOC_OBJ_MACHINE) + return HWLOC_TYPE_UNORDERED; + if (!hwloc__obj_type_is_normal(type2) + && hwloc__obj_type_is_normal(type1) && type1 != HWLOC_OBJ_MACHINE) + return HWLOC_TYPE_UNORDERED; + + return order1 - order2; +} + +enum hwloc_obj_cmp_e { + HWLOC_OBJ_EQUAL = HWLOC_BITMAP_EQUAL, /**< \brief Equal */ + HWLOC_OBJ_INCLUDED = HWLOC_BITMAP_INCLUDED, /**< \brief Strictly included into */ + HWLOC_OBJ_CONTAINS = HWLOC_BITMAP_CONTAINS, /**< \brief Strictly contains */ + HWLOC_OBJ_INTERSECTS = HWLOC_BITMAP_INTERSECTS, /**< \brief Intersects, but no inclusion! */ + HWLOC_OBJ_DIFFERENT = HWLOC_BITMAP_DIFFERENT /**< \brief No intersection */ +}; + +static enum hwloc_obj_cmp_e +hwloc_type_cmp(hwloc_obj_t obj1, hwloc_obj_t obj2) +{ + hwloc_obj_type_t type1 = obj1->type; + hwloc_obj_type_t type2 = obj2->type; + int compare; + + compare = hwloc_compare_types(type1, type2); + if (compare == HWLOC_TYPE_UNORDERED) + return HWLOC_OBJ_DIFFERENT; /* we cannot do better */ + if (compare > 0) + return HWLOC_OBJ_INCLUDED; + if (compare < 0) + return HWLOC_OBJ_CONTAINS; + + if (obj1->type == HWLOC_OBJ_GROUP + && (obj1->attr->group.kind != obj2->attr->group.kind + || obj1->attr->group.subkind != obj2->attr->group.subkind)) + return HWLOC_OBJ_DIFFERENT; /* we cannot do better */ + + return HWLOC_OBJ_EQUAL; +} + +/* + * How to compare objects based on cpusets. + */ + +static int +hwloc_obj_cmp_sets(hwloc_obj_t obj1, hwloc_obj_t obj2) +{ + hwloc_bitmap_t set1, set2; + int res = HWLOC_OBJ_DIFFERENT; + + assert(!hwloc__obj_type_is_special(obj1->type)); + assert(!hwloc__obj_type_is_special(obj2->type)); + + /* compare cpusets first */ + if (obj1->complete_cpuset && obj2->complete_cpuset) { + set1 = obj1->complete_cpuset; + set2 = obj2->complete_cpuset; + } else { + set1 = obj1->cpuset; + set2 = obj2->cpuset; + } + if (set1 && set2 && !hwloc_bitmap_iszero(set1) && !hwloc_bitmap_iszero(set2)) { + res = hwloc_bitmap_compare_inclusion(set1, set2); + if (res == HWLOC_OBJ_INTERSECTS) + return HWLOC_OBJ_INTERSECTS; + } + + /* then compare nodesets, and combine the results */ + if (obj1->complete_nodeset && obj2->complete_nodeset) { + set1 = obj1->complete_nodeset; + set2 = obj2->complete_nodeset; + } else { + set1 = obj1->nodeset; + set2 = obj2->nodeset; + } + if (set1 && set2 && !hwloc_bitmap_iszero(set1) && !hwloc_bitmap_iszero(set2)) { + int noderes = hwloc_bitmap_compare_inclusion(set1, set2); + /* deal with conflicting cpusets/nodesets inclusions */ + if (noderes == HWLOC_OBJ_INCLUDED) { + if (res == HWLOC_OBJ_CONTAINS) + /* contradicting order for cpusets and nodesets */ + return HWLOC_OBJ_INTERSECTS; + res = HWLOC_OBJ_INCLUDED; + + } else if (noderes == HWLOC_OBJ_CONTAINS) { + if (res == HWLOC_OBJ_INCLUDED) + /* contradicting order for cpusets and nodesets */ + return HWLOC_OBJ_INTERSECTS; + res = HWLOC_OBJ_CONTAINS; + + } else if (noderes == HWLOC_OBJ_INTERSECTS) { + return HWLOC_OBJ_INTERSECTS; + + } else { + /* nodesets are different, keep the cpuset order */ + + } + } + + return res; +} + +/* Compare object cpusets based on complete_cpuset if defined (always correctly ordered), + * or fallback to the main cpusets (only correctly ordered during early insert before disallowed bits are cleared). + * + * This is the sane way to compare object among a horizontal level. + */ +int +hwloc__object_cpusets_compare_first(hwloc_obj_t obj1, hwloc_obj_t obj2) +{ + if (obj1->complete_cpuset && obj2->complete_cpuset) + return hwloc_bitmap_compare_first(obj1->complete_cpuset, obj2->complete_cpuset); + else if (obj1->cpuset && obj2->cpuset) + return hwloc_bitmap_compare_first(obj1->cpuset, obj2->cpuset); + else if (obj1->complete_nodeset && obj2->complete_nodeset) + return hwloc_bitmap_compare_first(obj1->complete_nodeset, obj2->complete_nodeset); + else if (obj1->nodeset && obj2->nodeset) + return hwloc_bitmap_compare_first(obj1->nodeset, obj2->nodeset); + return 0; +} + +/* format the obj info to print in error messages */ +static void +hwloc__report_error_format_obj(char *buf, size_t buflen, hwloc_obj_t obj) +{ + char typestr[64]; + char *cpusetstr; + char *nodesetstr = NULL; + hwloc_obj_type_snprintf(typestr, sizeof(typestr), obj, 0); + hwloc_bitmap_asprintf(&cpusetstr, obj->cpuset); + if (obj->nodeset) /* may be missing during insert */ + hwloc_bitmap_asprintf(&nodesetstr, obj->nodeset); + if (obj->os_index != HWLOC_UNKNOWN_INDEX) + snprintf(buf, buflen, "%s (P#%u cpuset %s%s%s)", + typestr, obj->os_index, cpusetstr, + nodesetstr ? " nodeset " : "", + nodesetstr ? nodesetstr : ""); + else + snprintf(buf, buflen, "%s (cpuset %s%s%s)", + typestr, cpusetstr, + nodesetstr ? " nodeset " : "", + nodesetstr ? nodesetstr : ""); + free(cpusetstr); + free(nodesetstr); +} + +/* + * How to insert objects into the topology. + * + * Note: during detection, only the first_child and next_sibling pointers are + * kept up to date. Others are computed only once topology detection is + * complete. + */ + +/* merge new object attributes in old. + * use old if defined, otherwise use new. + */ +static void +merge_insert_equal(hwloc_obj_t new, hwloc_obj_t old) +{ + if (old->os_index == HWLOC_UNKNOWN_INDEX) + old->os_index = new->os_index; + + if (new->infos_count) { + /* FIXME: dedup */ + hwloc__move_infos(&old->infos, &old->infos_count, + &new->infos, &new->infos_count); + } + + if (new->name && !old->name) { + old->name = new->name; + new->name = NULL; + } + if (new->subtype && !old->subtype) { + old->subtype = new->subtype; + new->subtype = NULL; + } + + /* Ignore userdata. It will be NULL before load(). + * It may be non-NULL if alloc+insert_group() after load(). + */ + + switch(new->type) { + case HWLOC_OBJ_NUMANODE: + if (new->attr->numanode.local_memory && !old->attr->numanode.local_memory) { + /* no memory in old, use new memory */ + old->attr->numanode.local_memory = new->attr->numanode.local_memory; + free(old->attr->numanode.page_types); + old->attr->numanode.page_types_len = new->attr->numanode.page_types_len; + old->attr->numanode.page_types = new->attr->numanode.page_types; + new->attr->numanode.page_types = NULL; + new->attr->numanode.page_types_len = 0; + } + /* old->attr->numanode.total_memory will be updated by propagate_total_memory() */ + break; + case HWLOC_OBJ_L1CACHE: + case HWLOC_OBJ_L2CACHE: + case HWLOC_OBJ_L3CACHE: + case HWLOC_OBJ_L4CACHE: + case HWLOC_OBJ_L5CACHE: + case HWLOC_OBJ_L1ICACHE: + case HWLOC_OBJ_L2ICACHE: + case HWLOC_OBJ_L3ICACHE: + if (!old->attr->cache.size) + old->attr->cache.size = new->attr->cache.size; + if (!old->attr->cache.linesize) + old->attr->cache.size = new->attr->cache.linesize; + if (!old->attr->cache.associativity) + old->attr->cache.size = new->attr->cache.linesize; + break; + default: + break; + } +} + +/* returns the result of merge, or NULL if not merged */ +static __hwloc_inline hwloc_obj_t +hwloc__insert_try_merge_group(hwloc_obj_t old, hwloc_obj_t new) +{ + if (new->type == HWLOC_OBJ_GROUP && old->type == HWLOC_OBJ_GROUP) { + /* which group do we keep? */ + if (new->attr->group.dont_merge) { + if (old->attr->group.dont_merge) + /* nobody wants to be merged */ + return NULL; + + /* keep the new one, it doesn't want to be merged */ + hwloc_replace_linked_object(old, new); + return new; + + } else { + if (old->attr->group.dont_merge) + /* keep the old one, it doesn't want to be merged */ + return old; + + /* compare subkinds to decice who to keep */ + if (new->attr->group.kind < old->attr->group.kind) + hwloc_replace_linked_object(old, new); + return old; + } + } + + if (new->type == HWLOC_OBJ_GROUP && !new->attr->group.dont_merge) { + + if (old->type == HWLOC_OBJ_PU && new->attr->group.kind == HWLOC_GROUP_KIND_MEMORY) + /* Never merge Memory groups with PU, we don't want to attach Memory under PU */ + return NULL; + + /* Remove the Group now. The normal ignore code path wouldn't tell us whether the Group was removed or not, + * while some callers need to know (at least hwloc_topology_insert_group()). + */ + return old; + + } else if (old->type == HWLOC_OBJ_GROUP && !old->attr->group.dont_merge) { + + if (new->type == HWLOC_OBJ_PU && old->attr->group.kind == HWLOC_GROUP_KIND_MEMORY) + /* Never merge Memory groups with PU, we don't want to attach Memory under PU */ + return NULL; + + /* Replace the Group with the new object contents + * and let the caller free the new object + */ + hwloc_replace_linked_object(old, new); + return old; + + } else { + /* cannot merge */ + return NULL; + } +} + +/* Try to insert OBJ in CUR, recurse if needed. + * Returns the object if it was inserted, + * the remaining object it was merged, + * NULL if failed to insert. + */ +static struct hwloc_obj * +hwloc___insert_object_by_cpuset(struct hwloc_topology *topology, hwloc_obj_t cur, hwloc_obj_t obj, + hwloc_report_error_t report_error) +{ + hwloc_obj_t child, next_child = NULL; + /* These will always point to the pointer to their next last child. */ + hwloc_obj_t *cur_children = &cur->first_child; + hwloc_obj_t *obj_children = &obj->first_child; + /* Pointer where OBJ should be put */ + hwloc_obj_t *putp = NULL; /* OBJ position isn't found yet */ + + assert(!hwloc__obj_type_is_memory(obj->type)); + + /* Iteration with prefetching to be completely safe against CHILD removal. + * The list is already sorted by cpuset, and there's no intersection between siblings. + */ + for (child = cur->first_child, child ? next_child = child->next_sibling : NULL; + child; + child = next_child, child ? next_child = child->next_sibling : NULL) { + + int res = hwloc_obj_cmp_sets(obj, child); + int setres = res; + + if (res == HWLOC_OBJ_EQUAL) { + hwloc_obj_t merged = hwloc__insert_try_merge_group(child, obj); + if (merged) + return merged; + /* otherwise compare actual types to decide of the inclusion */ + res = hwloc_type_cmp(obj, child); + } + + switch (res) { + case HWLOC_OBJ_EQUAL: + /* Two objects with same type. + * Groups are handled above. + */ + merge_insert_equal(obj, child); + /* Already present, no need to insert. */ + return child; + + case HWLOC_OBJ_INCLUDED: + /* OBJ is strictly contained is some child of CUR, go deeper. */ + return hwloc___insert_object_by_cpuset(topology, child, obj, report_error); + + case HWLOC_OBJ_INTERSECTS: + if (report_error) { + char childstr[512]; + char objstr[512]; + char msg[1100]; + hwloc__report_error_format_obj(objstr, sizeof(objstr), obj); + hwloc__report_error_format_obj(childstr, sizeof(childstr), child); + snprintf(msg, sizeof(msg), "%s intersects with %s without inclusion!", objstr, childstr); + report_error(msg, __LINE__); + } + goto putback; + + case HWLOC_OBJ_DIFFERENT: + /* OBJ should be a child of CUR before CHILD, mark its position if not found yet. */ + if (!putp && hwloc__object_cpusets_compare_first(obj, child) < 0) + /* Don't insert yet, there could be intersect errors later */ + putp = cur_children; + /* Advance cur_children. */ + cur_children = &child->next_sibling; + break; + + case HWLOC_OBJ_CONTAINS: + /* OBJ contains CHILD, remove CHILD from CUR */ + *cur_children = child->next_sibling; + child->next_sibling = NULL; + /* Put CHILD in OBJ */ + *obj_children = child; + obj_children = &child->next_sibling; + child->parent = obj; + if (setres == HWLOC_OBJ_EQUAL) { + obj->memory_first_child = child->memory_first_child; + child->memory_first_child = NULL; + } + break; + } + } + /* cur/obj_children points to last CUR/OBJ child next_sibling pointer, which must be NULL. */ + assert(!*obj_children); + assert(!*cur_children); + + /* Put OBJ where it belongs, or in last in CUR's children. */ + if (!putp) + putp = cur_children; + obj->next_sibling = *putp; + *putp = obj; + obj->parent = cur; + + topology->modified = 1; + return obj; + + putback: + /* Put-back OBJ children in CUR and return an error. */ + if (putp) + cur_children = putp; /* No need to try to insert before where OBJ was supposed to go */ + else + cur_children = &cur->first_child; /* Start from the beginning */ + /* We can insert in order, but there can be holes in the middle. */ + while ((child = obj->first_child) != NULL) { + /* Remove from OBJ */ + obj->first_child = child->next_sibling; + obj->parent = cur; + /* Find child position in CUR, and insert. */ + while (*cur_children && hwloc__object_cpusets_compare_first(*cur_children, child) < 0) + cur_children = &(*cur_children)->next_sibling; + child->next_sibling = *cur_children; + *cur_children = child; + } + return NULL; +} + +/* this differs from hwloc_get_obj_covering_cpuset() by: + * - not looking at the parent cpuset first, which means we can insert + * below root even if root PU bits are not set yet (PU are inserted later). + * - returning the first child that exactly matches instead of walking down in case + * of identical children. + */ +static struct hwloc_obj * +hwloc__find_obj_covering_memory_cpuset(struct hwloc_topology *topology, hwloc_obj_t parent, hwloc_bitmap_t cpuset) +{ + hwloc_obj_t child = hwloc_get_child_covering_cpuset(topology, cpuset, parent); + if (!child) + return parent; + if (child && hwloc_bitmap_isequal(child->cpuset, cpuset)) + return child; + return hwloc__find_obj_covering_memory_cpuset(topology, child, cpuset); +} + +static struct hwloc_obj * +hwloc__find_insert_memory_parent(struct hwloc_topology *topology, hwloc_obj_t obj, + hwloc_report_error_t report_error) +{ + hwloc_obj_t parent, group, result; + + if (hwloc_bitmap_iszero(obj->cpuset)) { + /* CPU-less go in dedicated group below root */ + parent = topology->levels[0][0]; + + } else { + /* find the highest obj covering the cpuset */ + parent = hwloc__find_obj_covering_memory_cpuset(topology, topology->levels[0][0], obj->cpuset); + if (!parent) { + /* fallback to root */ + parent = hwloc_get_root_obj(topology); + } + + if (parent->type == HWLOC_OBJ_PU) { + /* Never attach to PU, try parent */ + parent = parent->parent; + assert(parent); + } + + /* TODO: if root->cpuset was updated earlier, we would be sure whether the group will remain identical to root */ + if (parent != topology->levels[0][0] && hwloc_bitmap_isequal(parent->cpuset, obj->cpuset)) + /* that parent is fine */ + return parent; + } + + if (!hwloc_filter_check_keep_object_type(topology, HWLOC_OBJ_GROUP)) + /* even if parent isn't perfect, we don't want an intermediate group */ + return parent; + + /* need to insert an intermediate group for attaching the NUMA node */ + group = hwloc_alloc_setup_object(topology, HWLOC_OBJ_GROUP, HWLOC_UNKNOWN_INDEX); + if (!group) + /* failed to create the group, fallback to larger parent */ + return parent; + + group->attr->group.kind = HWLOC_GROUP_KIND_MEMORY; + group->cpuset = hwloc_bitmap_dup(obj->cpuset); + group->complete_cpuset = hwloc_bitmap_dup(obj->complete_cpuset); + /* we could duplicate nodesets too but hwloc__insert_object_by_cpuset() + * doesn't actually need it. and it could prevent future calls from reusing + * that groups for other NUMA nodes. + */ + if (!group->cpuset != !obj->cpuset + || !group->complete_cpuset != !obj->complete_cpuset) { + /* failed to create the group, fallback to larger parent */ + hwloc_free_unlinked_object(group); + return parent; + } + + result = hwloc__insert_object_by_cpuset(topology, parent, group, report_error); + if (!result) { + /* failed to insert, fallback to larger parent */ + return parent; + } + + assert(result == group); + return group; +} + +/*attach the given memory object below the given normal parent. */ +struct hwloc_obj * +hwloc__attach_memory_object(struct hwloc_topology *topology, hwloc_obj_t parent, + hwloc_obj_t obj, + hwloc_report_error_t report_error __hwloc_attribute_unused) +{ + hwloc_obj_t *cur_children; + + assert(parent); + assert(hwloc__obj_type_is_normal(parent->type)); + +#if 0 + /* TODO: enable this instead of hack in fixup_sets once NUMA nodes are inserted late */ + /* copy the parent cpuset in case it's larger than expected. + * we could also keep the cpuset smaller than the parent and say that a normal-parent + * can have multiple memory children with smaller cpusets. + * However, the user decided the ignore Groups, so hierarchy/locality loss is expected. + */ + hwloc_bitmap_copy(obj->cpuset, parent->cpuset); +#endif + + /* only NUMA nodes are memory for now, just append to the end of the list */ + assert(obj->type == HWLOC_OBJ_NUMANODE); + assert(obj->nodeset); + cur_children = &parent->memory_first_child; + while (*cur_children) { + /* TODO check that things are inserted in order. + * it's OK for KNL, the only user so far + */ + cur_children = &(*cur_children)->next_sibling; + } + *cur_children = obj; + obj->next_sibling = NULL; + + /* Initialize the complete nodeset if needed */ + if (!obj->complete_nodeset) { + obj->complete_nodeset = hwloc_bitmap_dup(obj->nodeset); + } + + /* Add the bit to the top sets, and to the parent CPU-side object */ + if (obj->type == HWLOC_OBJ_NUMANODE) { + if (hwloc_bitmap_isset(obj->nodeset, obj->os_index)) + hwloc_bitmap_set(topology->levels[0][0]->nodeset, obj->os_index); + hwloc_bitmap_set(topology->levels[0][0]->complete_nodeset, obj->os_index); + } + + topology->modified = 1; + return obj; +} + +/* insertion routine that lets you change the error reporting callback */ +struct hwloc_obj * +hwloc__insert_object_by_cpuset(struct hwloc_topology *topology, hwloc_obj_t root, + hwloc_obj_t obj, + hwloc_report_error_t report_error) +{ + struct hwloc_obj *result; + +#ifdef HWLOC_DEBUG + assert(!hwloc__obj_type_is_special(obj->type)); + + /* we need at least one non-NULL set (normal or complete, cpuset or nodeset) */ + assert(obj->cpuset || obj->complete_cpuset || obj->nodeset || obj->complete_nodeset); + /* we support the case where all of them are empty. + * it may happen when hwloc__find_insert_memory_parent() + * inserts a Group for a CPU-less NUMA-node. + */ +#endif + + if (hwloc__obj_type_is_memory(obj->type)) { + if (!root) { + root = hwloc__find_insert_memory_parent(topology, obj, report_error); + if (!root) { + hwloc_free_unlinked_object(obj); + return NULL; + } + } + return hwloc__attach_memory_object(topology, root, obj, report_error); + } + + if (!root) + /* Start at the top. */ + root = topology->levels[0][0]; + + result = hwloc___insert_object_by_cpuset(topology, root, obj, report_error); + if (result && result->type == HWLOC_OBJ_PU) { + /* Add the bit to the top sets */ + if (hwloc_bitmap_isset(result->cpuset, result->os_index)) + hwloc_bitmap_set(topology->levels[0][0]->cpuset, result->os_index); + hwloc_bitmap_set(topology->levels[0][0]->complete_cpuset, result->os_index); + } + if (result != obj) { + /* either failed to insert, or got merged, free the original object */ + hwloc_free_unlinked_object(obj); + } + return result; +} + +/* the default insertion routine warns in case of error. + * it's used by most backends */ +struct hwloc_obj * +hwloc_insert_object_by_cpuset(struct hwloc_topology *topology, hwloc_obj_t obj) +{ + return hwloc__insert_object_by_cpuset(topology, NULL, obj, hwloc_report_os_error); +} + +void +hwloc_insert_object_by_parent(struct hwloc_topology *topology, hwloc_obj_t parent, hwloc_obj_t obj) +{ + hwloc_obj_t *current; + + if (obj->type == HWLOC_OBJ_MISC) { + /* Append to the end of the Misc list */ + for (current = &parent->misc_first_child; *current; current = &(*current)->next_sibling); + } else if (hwloc__obj_type_is_io(obj->type)) { + /* Append to the end of the I/O list */ + for (current = &parent->io_first_child; *current; current = &(*current)->next_sibling); + } else if (hwloc__obj_type_is_memory(obj->type)) { + /* Append to the end of the memory list */ + for (current = &parent->memory_first_child; *current; current = &(*current)->next_sibling); + /* Add the bit to the top sets */ + if (obj->type == HWLOC_OBJ_NUMANODE) { + if (hwloc_bitmap_isset(obj->nodeset, obj->os_index)) + hwloc_bitmap_set(topology->levels[0][0]->nodeset, obj->os_index); + hwloc_bitmap_set(topology->levels[0][0]->complete_nodeset, obj->os_index); + } + } else { + /* Append to the end of the list. + * The caller takes care of inserting children in the right cpuset order, without intersection between them. + * Duplicating doesn't need to check the order since the source topology is supposed to be OK already. + * XML reorders if needed, and fails on intersecting siblings. + * Other callers just insert random objects such as I/O or Misc, no cpuset issue there. + */ + for (current = &parent->first_child; *current; current = &(*current)->next_sibling); + /* Add the bit to the top sets */ + if (obj->type == HWLOC_OBJ_PU) { + if (hwloc_bitmap_isset(obj->cpuset, obj->os_index)) + hwloc_bitmap_set(topology->levels[0][0]->cpuset, obj->os_index); + hwloc_bitmap_set(topology->levels[0][0]->complete_cpuset, obj->os_index); + } + } + + *current = obj; + obj->parent = parent; + obj->next_sibling = NULL; + topology->modified = 1; +} + +hwloc_obj_t +hwloc_alloc_setup_object(hwloc_topology_t topology, + hwloc_obj_type_t type, unsigned os_index) +{ + struct hwloc_obj *obj = hwloc_tma_malloc(topology->tma, sizeof(*obj)); + memset(obj, 0, sizeof(*obj)); + obj->type = type; + obj->os_index = os_index; + obj->gp_index = topology->next_gp_index++; + obj->attr = hwloc_tma_malloc(topology->tma, sizeof(*obj->attr)); + memset(obj->attr, 0, sizeof(*obj->attr)); + /* do not allocate the cpuset here, let the caller do it */ + return obj; +} + +hwloc_obj_t +hwloc_topology_alloc_group_object(struct hwloc_topology *topology) +{ + if (!topology->is_loaded) { + /* this could actually work, see insert() below */ + errno = EINVAL; + return NULL; + } + return hwloc_alloc_setup_object(topology, HWLOC_OBJ_GROUP, HWLOC_UNKNOWN_INDEX); +} + +static void hwloc_propagate_symmetric_subtree(hwloc_topology_t topology, hwloc_obj_t root); +static void propagate_total_memory(hwloc_obj_t obj); +static void hwloc_set_group_depth(hwloc_topology_t topology); + +hwloc_obj_t +hwloc_topology_insert_group_object(struct hwloc_topology *topology, hwloc_obj_t obj) +{ + hwloc_obj_t res, root; + int cmp; + + if (!topology->is_loaded) { + /* this could actually work, we would just need to disable connect_children/levels below */ + hwloc_free_unlinked_object(obj); + errno = EINVAL; + return NULL; + } + + if (topology->type_filter[HWLOC_OBJ_GROUP] == HWLOC_TYPE_FILTER_KEEP_NONE) { + hwloc_free_unlinked_object(obj); + errno = EINVAL; + return NULL; + } + + root = hwloc_get_root_obj(topology); + if (obj->cpuset) + hwloc_bitmap_and(obj->cpuset, obj->cpuset, root->cpuset); + if (obj->complete_cpuset) + hwloc_bitmap_and(obj->complete_cpuset, obj->complete_cpuset, root->complete_cpuset); + if (obj->nodeset) + hwloc_bitmap_and(obj->nodeset, obj->nodeset, root->nodeset); + if (obj->complete_nodeset) + hwloc_bitmap_and(obj->complete_nodeset, obj->complete_nodeset, root->complete_nodeset); + + if ((!obj->cpuset || hwloc_bitmap_iszero(obj->cpuset)) + && (!obj->complete_cpuset || hwloc_bitmap_iszero(obj->complete_cpuset)) + && (!obj->nodeset || hwloc_bitmap_iszero(obj->nodeset)) + && (!obj->complete_nodeset || hwloc_bitmap_iszero(obj->complete_nodeset))) { + hwloc_free_unlinked_object(obj); + errno = EINVAL; + return NULL; + } + + cmp = hwloc_obj_cmp_sets(obj, root); + if (cmp == HWLOC_OBJ_INCLUDED) { + res = hwloc__insert_object_by_cpuset(topology, NULL, obj, NULL /* do not show errors on stdout */); + } else { + /* just merge root */ + res = root; + } + + if (!res) + return NULL; + if (res != obj) + /* merged */ + return res; + + /* properly inserted */ + hwloc_obj_add_children_sets(obj); + if (hwloc_topology_reconnect(topology, 0) < 0) + return NULL; + + hwloc_propagate_symmetric_subtree(topology, topology->levels[0][0]); + hwloc_set_group_depth(topology); + +#ifndef HWLOC_DEBUG + if (getenv("HWLOC_DEBUG_CHECK")) +#endif + hwloc_topology_check(topology); + + return obj; +} + +hwloc_obj_t +hwloc_topology_insert_misc_object(struct hwloc_topology *topology, hwloc_obj_t parent, const char *name) +{ + hwloc_obj_t obj; + + if (topology->type_filter[HWLOC_OBJ_MISC] == HWLOC_TYPE_FILTER_KEEP_NONE) { + errno = EINVAL; + return NULL; + } + + if (!topology->is_loaded) { + errno = EINVAL; + return NULL; + } + + obj = hwloc_alloc_setup_object(topology, HWLOC_OBJ_MISC, HWLOC_UNKNOWN_INDEX); + if (name) + obj->name = strdup(name); + + hwloc_insert_object_by_parent(topology, parent, obj); + + /* FIXME: only connect misc parent children and misc level, + * but this API is likely not performance critical anyway + */ + hwloc_topology_reconnect(topology, 0); + +#ifndef HWLOC_DEBUG + if (getenv("HWLOC_DEBUG_CHECK")) +#endif + hwloc_topology_check(topology); + + return obj; +} + +/* assuming set is included in the topology complete_cpuset + * and all objects have a proper complete_cpuset, + * return the best one containing set. + * if some object are equivalent (same complete_cpuset), return the highest one. + */ +static hwloc_obj_t +hwloc_get_highest_obj_covering_complete_cpuset (hwloc_topology_t topology, hwloc_const_cpuset_t set) +{ + hwloc_obj_t current = hwloc_get_root_obj(topology); + hwloc_obj_t child; + + if (hwloc_bitmap_isequal(set, current->complete_cpuset)) + /* root cpuset is exactly what we want, no need to look at children, we want the highest */ + return current; + + recurse: + /* find the right child */ + for_each_child(child, current) { + if (hwloc_bitmap_isequal(set, child->complete_cpuset)) + /* child puset is exactly what we want, no need to look at children, we want the highest */ + return child; + if (!hwloc_bitmap_iszero(child->complete_cpuset) && hwloc_bitmap_isincluded(set, child->complete_cpuset)) + break; + } + + if (child) { + current = child; + goto recurse; + } + + /* no better child */ + return current; +} + +hwloc_obj_t +hwloc_find_insert_io_parent_by_complete_cpuset(struct hwloc_topology *topology, hwloc_cpuset_t cpuset) +{ + hwloc_obj_t group_obj, largeparent, parent; + + /* restrict to the existing complete cpuset to avoid errors later */ + hwloc_bitmap_and(cpuset, cpuset, hwloc_topology_get_complete_cpuset(topology)); + if (hwloc_bitmap_iszero(cpuset)) + /* remaining cpuset is empty, invalid */ + return NULL; + + largeparent = hwloc_get_highest_obj_covering_complete_cpuset(topology, cpuset); + if (hwloc_bitmap_isequal(largeparent->complete_cpuset, cpuset) + || !hwloc_filter_check_keep_object_type(topology, HWLOC_OBJ_GROUP)) + /* Found a valid object (normal case) */ + return largeparent; + + /* we need to insert an intermediate group */ + group_obj = hwloc_alloc_setup_object(topology, HWLOC_OBJ_GROUP, HWLOC_UNKNOWN_INDEX); + if (!group_obj) + /* Failed to insert the exact Group, fallback to largeparent */ + return largeparent; + + group_obj->complete_cpuset = hwloc_bitmap_dup(cpuset); + hwloc_bitmap_and(cpuset, cpuset, hwloc_topology_get_topology_cpuset(topology)); + group_obj->cpuset = hwloc_bitmap_dup(cpuset); + group_obj->attr->group.kind = HWLOC_GROUP_KIND_IO; + parent = hwloc__insert_object_by_cpuset(topology, largeparent, group_obj, hwloc_report_os_error); + if (!parent) + /* Failed to insert the Group, maybe a conflicting cpuset */ + return largeparent; + + /* Group couldn't get merged or we would have gotten the right largeparent earlier */ + assert(parent == group_obj); + + /* Group inserted without being merged, everything OK, setup its sets */ + hwloc_obj_add_children_sets(group_obj); + + return parent; +} + +static int hwloc_memory_page_type_compare(const void *_a, const void *_b) +{ + const struct hwloc_memory_page_type_s *a = _a; + const struct hwloc_memory_page_type_s *b = _b; + /* consider 0 as larger so that 0-size page_type go to the end */ + if (!b->size) + return -1; + /* don't cast a-b in int since those are ullongs */ + if (b->size == a->size) + return 0; + return a->size < b->size ? -1 : 1; +} + +/* Propagate memory counts */ +static void +propagate_total_memory(hwloc_obj_t obj) +{ + hwloc_obj_t child; + unsigned i; + + /* reset total before counting local and children memory */ + obj->total_memory = 0; + + /* Propagate memory up. */ + for_each_child(child, obj) { + propagate_total_memory(child); + obj->total_memory += child->total_memory; + } + for_each_memory_child(child, obj) { + propagate_total_memory(child); + obj->total_memory += child->total_memory; + } + /* No memory under I/O or Misc */ + + if (obj->type == HWLOC_OBJ_NUMANODE) { + obj->total_memory += obj->attr->numanode.local_memory; + + /* By the way, sort the page_type array. + * Cannot do it on insert since some backends (e.g. XML) add page_types after inserting the object. + */ + qsort(obj->attr->numanode.page_types, obj->attr->numanode.page_types_len, sizeof(*obj->attr->numanode.page_types), hwloc_memory_page_type_compare); + /* Ignore 0-size page_types, they are at the end */ + for(i=obj->attr->numanode.page_types_len; i>=1; i--) + if (obj->attr->numanode.page_types[i-1].size) + break; + obj->attr->numanode.page_types_len = i; + } +} + +/* Now that root sets are ready, propagate them to children + * by allocating missing sets and restricting existing ones. + */ +static void +fixup_sets(hwloc_obj_t obj) +{ + int in_memory_list; + hwloc_obj_t child; + + child = obj->first_child; + in_memory_list = 0; + /* iterate over normal children first, we'll come back for memory children later */ + + iterate: + while (child) { + /* our cpuset must be included in our parent's one */ + hwloc_bitmap_and(child->cpuset, child->cpuset, obj->cpuset); + hwloc_bitmap_and(child->nodeset, child->nodeset, obj->nodeset); + /* our complete_cpuset must be included in our parent's one, but can be larger than our cpuset */ + if (child->complete_cpuset) { + hwloc_bitmap_and(child->complete_cpuset, child->complete_cpuset, obj->complete_cpuset); + } else { + child->complete_cpuset = hwloc_bitmap_dup(child->cpuset); + } + if (child->complete_nodeset) { + hwloc_bitmap_and(child->complete_nodeset, child->complete_nodeset, obj->complete_nodeset); + } else { + child->complete_nodeset = hwloc_bitmap_dup(child->nodeset); + } + + fixup_sets(child); + child = child->next_sibling; + } + + /* switch to memory children list if any */ + if (!in_memory_list && obj->memory_first_child) { + child = obj->memory_first_child; + in_memory_list = 1; + goto iterate; + } + + /* No sets in I/O or Misc */ +} + +/* Setup object cpusets/nodesets by OR'ing its children. */ +int +hwloc_obj_add_other_obj_sets(hwloc_obj_t dst, hwloc_obj_t src) +{ +#define ADD_OTHER_OBJ_SET(_dst, _src, _set) \ + if ((_src)->_set) { \ + if (!(_dst)->_set) \ + (_dst)->_set = hwloc_bitmap_alloc(); \ + hwloc_bitmap_or((_dst)->_set, (_dst)->_set, (_src)->_set); \ + } + ADD_OTHER_OBJ_SET(dst, src, cpuset); + ADD_OTHER_OBJ_SET(dst, src, complete_cpuset); + ADD_OTHER_OBJ_SET(dst, src, nodeset); + ADD_OTHER_OBJ_SET(dst, src, complete_nodeset); + return 0; +} + +int +hwloc_obj_add_children_sets(hwloc_obj_t obj) +{ + hwloc_obj_t child; + for_each_child(child, obj) { + hwloc_obj_add_other_obj_sets(obj, child); + } + /* No need to look at Misc children, they contain no PU. */ + return 0; +} + +/* CPU objects are inserted by cpusets, we know their cpusets are properly included. + * We just need fixup_sets() to make sure they aren't too wide. + * + * Memory objects are inserted by cpusets to find their CPU parent, + * but nodesets are only used inside the memory hierarchy below that parent. + * Thus we need to propagate nodesets to CPU-side parents and children. + * + * A memory object nodeset consists of NUMA nodes below it. + * A normal object nodeset consists in NUMA nodes attached to any + * of its children or parents. + */ +static void +propagate_nodeset(hwloc_obj_t obj) +{ + hwloc_obj_t child; + + /* Start our nodeset from the parent one. + * It was emptied at root, and it's being filled with local nodes + * in that branch of the tree as we recurse down. + */ + if (!obj->nodeset) + obj->nodeset = hwloc_bitmap_alloc(); + if (obj->parent) + hwloc_bitmap_copy(obj->nodeset, obj->parent->nodeset); + else + hwloc_bitmap_zero(obj->nodeset); + + /* Don't clear complete_nodeset, just make sure it contains nodeset. + * We cannot clear the complete_nodeset at root and rebuild it down because + * some bits may correspond to offline/disallowed NUMA nodes missing in the topology. + */ + if (!obj->complete_nodeset) + obj->complete_nodeset = hwloc_bitmap_dup(obj->nodeset); + else + hwloc_bitmap_or(obj->complete_nodeset, obj->complete_nodeset, obj->nodeset); + + /* now add our local nodeset */ + for_each_memory_child(child, obj) { + /* FIXME rather recurse in the memory hierarchy */ + + /* first, update children complete_nodeset if needed */ + if (!child->complete_nodeset) + child->complete_nodeset = hwloc_bitmap_dup(child->nodeset); + else + hwloc_bitmap_or(child->complete_nodeset, child->complete_nodeset, child->nodeset); + + /* add memory children nodesets to ours */ + hwloc_bitmap_or(obj->nodeset, obj->nodeset, child->nodeset); + hwloc_bitmap_or(obj->complete_nodeset, obj->complete_nodeset, child->complete_nodeset); + + /* by the way, copy our cpusets to memory children */ + if (child->cpuset) + hwloc_bitmap_copy(child->cpuset, obj->cpuset); + else + child->cpuset = hwloc_bitmap_dup(obj->cpuset); + if (child->complete_cpuset) + hwloc_bitmap_copy(child->complete_cpuset, obj->complete_cpuset); + else + child->complete_cpuset = hwloc_bitmap_dup(obj->complete_cpuset); + } + + /* Propagate our nodeset to CPU children. */ + for_each_child(child, obj) { + propagate_nodeset(child); + } + + /* Propagate CPU children specific nodesets back to us. + * + * We cannot merge these two loops because we don't want to first child + * nodeset to be propagated back to us and then down to the second child. + * Each child may have its own local nodeset, + * each of them is propagated to us, but not to other children. + */ + for_each_child(child, obj) { + hwloc_bitmap_or(obj->nodeset, obj->nodeset, child->nodeset); + hwloc_bitmap_or(obj->complete_nodeset, obj->complete_nodeset, child->complete_nodeset); + } + + /* No nodeset under I/O or Misc */ + +} + +static void +remove_unused_sets(hwloc_topology_t topology, hwloc_obj_t obj) +{ + hwloc_obj_t child; + + hwloc_bitmap_and(obj->cpuset, obj->cpuset, topology->allowed_cpuset); + hwloc_bitmap_and(obj->nodeset, obj->nodeset, topology->allowed_nodeset); + + for_each_child(child, obj) + remove_unused_sets(topology, child); + for_each_memory_child(child, obj) + remove_unused_sets(topology, child); + /* No cpuset under I/O or Misc */ +} + +static void +hwloc__filter_bridges(hwloc_topology_t topology, hwloc_obj_t root, unsigned depth) +{ + hwloc_obj_t child, *pchild; + + /* filter I/O children and recurse */ + for_each_io_child_safe(child, root, pchild) { + enum hwloc_type_filter_e filter = topology->type_filter[child->type]; + + /* recurse into grand-children */ + hwloc__filter_bridges(topology, child, depth+1); + + child->attr->bridge.depth = depth; + + if (child->type == HWLOC_OBJ_BRIDGE + && filter == HWLOC_TYPE_FILTER_KEEP_IMPORTANT + && !child->io_first_child) { + unlink_and_free_single_object(pchild); + topology->modified = 1; + } + } +} + +static void +hwloc_filter_bridges(hwloc_topology_t topology, hwloc_obj_t parent) +{ + hwloc_obj_t child = parent->first_child; + while (child) { + hwloc_filter_bridges(topology, child); + child = child->next_sibling; + } + + hwloc__filter_bridges(topology, parent, 0); +} + +void +hwloc__reorder_children(hwloc_obj_t parent) +{ + /* move the children list on the side */ + hwloc_obj_t *prev, child, children = parent->first_child; + parent->first_child = NULL; + while (children) { + /* dequeue child */ + child = children; + children = child->next_sibling; + /* find where to enqueue it */ + prev = &parent->first_child; + while (*prev && hwloc__object_cpusets_compare_first(child, *prev) > 0) + prev = &((*prev)->next_sibling); + /* enqueue */ + child->next_sibling = *prev; + *prev = child; + } + /* No ordering to enforce for Misc or I/O children. */ +} + +/* Remove all normal children whose cpuset is empty, + * and memory children whose nodeset is empty. + * Also don't remove objects that have I/O children, but ignore Misc. + */ +static void +remove_empty(hwloc_topology_t topology, hwloc_obj_t *pobj) +{ + hwloc_obj_t obj = *pobj, child, *pchild; + + for_each_child_safe(child, obj, pchild) + remove_empty(topology, pchild); + for_each_memory_child_safe(child, obj, pchild) + remove_empty(topology, pchild); + /* No cpuset under I/O or Misc */ + + if (obj->first_child /* only remove if all children were removed above, so that we don't remove parents of NUMAnode */ + || obj->memory_first_child /* only remove if no memory attached there */ + || obj->io_first_child /* only remove if no I/O is attached there */) + /* ignore Misc */ + return; + + if (hwloc__obj_type_is_normal(obj->type)) { + if (!hwloc_bitmap_iszero(obj->cpuset)) + return; + } else { + assert(hwloc__obj_type_is_memory(obj->type)); + if (!hwloc_bitmap_iszero(obj->nodeset)) + return; + } + + hwloc_debug("%s", "\nRemoving empty object "); + hwloc_debug_print_object(0, obj); + unlink_and_free_single_object(pobj); + topology->modified = 1; +} + +/* reset type depth before modifying levels (either reconnecting or filtering/keep_structure) */ +static void +hwloc_reset_normal_type_depths(hwloc_topology_t topology) +{ + unsigned i; + for (i=HWLOC_OBJ_TYPE_MIN; i<=HWLOC_OBJ_GROUP; i++) + topology->type_depth[i] = HWLOC_TYPE_DEPTH_UNKNOWN; + /* type contiguity is asserted in topology_check() */ +} + +static int +hwloc_dont_merge_group_level(hwloc_topology_t topology, unsigned i) +{ + unsigned j; + + /* Don't merge some groups in that level? */ + for(j=0; jlevel_nbobjects[i]; j++) + if (topology->levels[i][j]->attr->group.dont_merge) + return 1; + + return 0; +} + +/* compare i-th and i-1-th levels structure */ +static int +hwloc_compare_levels_structure(hwloc_topology_t topology, unsigned i) +{ + int checkmemory = (topology->levels[i][0]->type == HWLOC_OBJ_PU); + unsigned j; + + if (topology->level_nbobjects[i-1] != topology->level_nbobjects[i]) + return -1; + + for(j=0; jlevel_nbobjects[i]; j++) { + if (topology->levels[i-1][j]->arity != 1) + return -1; + if (checkmemory && topology->levels[i-1][j]->memory_arity) + /* don't merge PUs if there's memory above */ + return -1; + } + /* same number of objects with arity 1 above, no problem */ + return 0; +} + +/* return > 0 if any level was removed, which means reconnect is needed */ +static void +hwloc_filter_levels_keep_structure(hwloc_topology_t topology) +{ + unsigned i, j; + int res = 0; + + /* start from the bottom since we'll remove intermediate levels */ + for(i=topology->nb_levels-1; i>0; i--) { + int replacechild = 0, replaceparent = 0; + hwloc_obj_t obj1 = topology->levels[i-1][0]; + hwloc_obj_t obj2 = topology->levels[i][0]; + hwloc_obj_type_t type1 = obj1->type; + hwloc_obj_type_t type2 = obj2->type; + + /* Check whether parents and/or children can be replaced */ + if (topology->type_filter[type1] == HWLOC_TYPE_FILTER_KEEP_STRUCTURE) { + /* Parents can be ignored in favor of children. */ + replaceparent = 1; + if (type1 == HWLOC_OBJ_GROUP && hwloc_dont_merge_group_level(topology, i-1)) + replaceparent = 0; + } + if (topology->type_filter[type2] == HWLOC_TYPE_FILTER_KEEP_STRUCTURE) { + /* Children can be ignored in favor of parents. */ + replacechild = 1; + if (type1 == HWLOC_OBJ_GROUP && hwloc_dont_merge_group_level(topology, i)) + replacechild = 0; + } + if (!replacechild && !replaceparent) + /* no ignoring */ + continue; + /* Decide which one to actually replace */ + if (replaceparent && replacechild) { + /* If both may be replaced, look at obj_type_priority */ + if (obj_type_priority[type1] >= obj_type_priority[type2]) + replaceparent = 0; + else + replacechild = 0; + } + /* Are these levels actually identical? */ + if (hwloc_compare_levels_structure(topology, i) < 0) + continue; + hwloc_debug("may merge levels #%u=%s and #%u=%s\n", + i-1, hwloc_obj_type_string(type1), i, hwloc_obj_type_string(type2)); + + /* OK, remove intermediate objects from the tree. */ + for(j=0; jlevel_nbobjects[i]; j++) { + hwloc_obj_t parent = topology->levels[i-1][j]; + hwloc_obj_t child = topology->levels[i][j]; + unsigned k; + if (replacechild) { + /* move child's children to parent */ + parent->first_child = child->first_child; + parent->last_child = child->last_child; + parent->arity = child->arity; + free(parent->children); + parent->children = child->children; + child->children = NULL; + /* update children parent */ + for(k=0; karity; k++) + parent->children[k]->parent = parent; + /* append child memory/io/misc children to parent */ + if (child->memory_first_child) { + append_siblings_list(&parent->memory_first_child, child->memory_first_child, parent); + parent->memory_arity += child->memory_arity; + } + if (child->io_first_child) { + append_siblings_list(&parent->io_first_child, child->io_first_child, parent); + parent->io_arity += child->io_arity; + } + if (child->misc_first_child) { + append_siblings_list(&parent->misc_first_child, child->misc_first_child, parent); + parent->misc_arity += child->misc_arity; + } + hwloc_free_unlinked_object(child); + } else { + /* replace parent with child in grand-parent */ + if (parent->parent) { + parent->parent->children[parent->sibling_rank] = child; + child->sibling_rank = parent->sibling_rank; + if (!parent->sibling_rank) { + parent->parent->first_child = child; + /* child->prev_sibling was already NULL, child was single */ + } else { + child->prev_sibling = parent->parent->children[parent->sibling_rank-1]; + child->prev_sibling->next_sibling = child; + } + if (parent->sibling_rank == parent->parent->arity-1) { + parent->parent->last_child = child; + /* child->next_sibling was already NULL, child was single */ + } else { + child->next_sibling = parent->parent->children[parent->sibling_rank+1]; + child->next_sibling->prev_sibling = child; + } + /* update child parent */ + child->parent = parent->parent; + } else { + /* make child the new root */ + topology->levels[0][0] = child; + child->parent = NULL; + } + /* prepend parent memory/io/misc children to child */ + if (parent->memory_first_child) { + prepend_siblings_list(&child->memory_first_child, parent->memory_first_child, child); + child->memory_arity += parent->memory_arity; + } + if (parent->io_first_child) { + prepend_siblings_list(&child->io_first_child, parent->io_first_child, child); + child->io_arity += parent->io_arity; + } + if (parent->misc_first_child) { + prepend_siblings_list(&child->misc_first_child, parent->misc_first_child, child); + child->misc_arity += parent->misc_arity; + } + hwloc_free_unlinked_object(parent); + /* prev/next_sibling will be updated below in another loop */ + } + } + if (replaceparent && i>1) { + /* Update sibling list within modified parent->parent arrays */ + for(j=0; jlevel_nbobjects[i]; j++) { + hwloc_obj_t child = topology->levels[i][j]; + unsigned rank = child->sibling_rank; + child->prev_sibling = rank > 0 ? child->parent->children[rank-1] : NULL; + child->next_sibling = rank < child->parent->arity-1 ? child->parent->children[rank+1] : NULL; + } + } + + /* Update levels so that the next reconnect isn't confused */ + if (replaceparent) { + /* Removing level i-1, so move levels [i..nb_levels-1] to [i-1..] */ + free(topology->levels[i-1]); + memmove(&topology->levels[i-1], + &topology->levels[i], + (topology->nb_levels-i)*sizeof(topology->levels[i])); + memmove(&topology->level_nbobjects[i-1], + &topology->level_nbobjects[i], + (topology->nb_levels-i)*sizeof(topology->level_nbobjects[i])); + hwloc_debug("removed parent level %s at depth %u\n", + hwloc_obj_type_string(type1), i-1); + } else { + /* Removing level i, so move levels [i+1..nb_levels-1] and later to [i..] */ + free(topology->levels[i]); + memmove(&topology->levels[i], + &topology->levels[i+1], + (topology->nb_levels-1-i)*sizeof(topology->levels[i])); + memmove(&topology->level_nbobjects[i], + &topology->level_nbobjects[i+1], + (topology->nb_levels-1-i)*sizeof(topology->level_nbobjects[i])); + hwloc_debug("removed child level %s at depth %u\n", + hwloc_obj_type_string(type2), i); + } + topology->level_nbobjects[topology->nb_levels-1] = 0; + topology->levels[topology->nb_levels-1] = NULL; + topology->nb_levels--; + + res++; + } + + if (res > 0) { + /* Update object and type depths if some levels were removed */ + hwloc_reset_normal_type_depths(topology); + for(i=0; inb_levels; i++) { + hwloc_obj_type_t type = topology->levels[i][0]->type; + for(j=0; jlevel_nbobjects[i]; j++) + topology->levels[i][j]->depth = (int)i; + if (topology->type_depth[type] == HWLOC_TYPE_DEPTH_UNKNOWN) + topology->type_depth[type] = (int)i; + else + topology->type_depth[type] = HWLOC_TYPE_DEPTH_MULTIPLE; + } + } +} + +static void +hwloc_propagate_symmetric_subtree(hwloc_topology_t topology, hwloc_obj_t root) +{ + hwloc_obj_t child; + unsigned arity = root->arity; + int ok; + + /* assume we're not symmetric by default */ + root->symmetric_subtree = 0; + + /* if no child, we are symmetric */ + if (!arity) + goto good; + + /* FIXME ignore memory just like I/O and Misc? */ + + /* look at normal children only, I/O and Misc are ignored. + * return if any child is not symmetric. + */ + ok = 1; + for_each_child(child, root) { + hwloc_propagate_symmetric_subtree(topology, child); + if (!child->symmetric_subtree) + ok = 0; + } + if (!ok) + return; + /* Misc and I/O children do not care about symmetric_subtree */ + + /* if single child is symmetric, we're good */ + if (arity == 1) + goto good; + + /* now check that children subtrees are identical. + * just walk down the first child in each tree and compare their depth and arities + */ +{ + HWLOC_VLA(hwloc_obj_t, array, arity); + memcpy(array, root->children, arity * sizeof(*array)); + while (1) { + unsigned i; + /* check current level arities and depth */ + for(i=1; idepth != array[0]->depth + || array[i]->arity != array[0]->arity) { + return; + } + if (!array[0]->arity) + /* no more children level, we're ok */ + break; + /* look at first child of each element now */ + for(i=0; ifirst_child; + } +} + + /* everything went fine, we're symmetric */ + good: + root->symmetric_subtree = 1; +} + +static void hwloc_set_group_depth(hwloc_topology_t topology) +{ + unsigned groupdepth = 0; + unsigned i, j; + for(i=0; inb_levels; i++) + if (topology->levels[i][0]->type == HWLOC_OBJ_GROUP) { + for (j = 0; j < topology->level_nbobjects[i]; j++) + topology->levels[i][j]->attr->group.depth = groupdepth; + groupdepth++; + } +} + +/* + * Initialize handy pointers in the whole topology. + * The topology only had first_child and next_sibling pointers. + * When this funtions return, all parent/children pointers are initialized. + * The remaining fields (levels, cousins, logical_index, depth, ...) will + * be setup later in hwloc_connect_levels(). + * + * Can be called several times, so may have to update the array. + */ +static void +hwloc_connect_children(hwloc_obj_t parent) +{ + unsigned n, oldn = parent->arity; + hwloc_obj_t child, prev_child; + int ok; + + /* Main children list */ + + ok = 1; + prev_child = NULL; + for (n = 0, child = parent->first_child; + child; + n++, prev_child = child, child = child->next_sibling) { + child->sibling_rank = n; + child->prev_sibling = prev_child; + /* already OK in the array? */ + if (n >= oldn || parent->children[n] != child) + ok = 0; + /* recurse */ + hwloc_connect_children(child); + } + parent->last_child = prev_child; + parent->arity = n; + if (!n) { + /* no need for an array anymore */ + free(parent->children); + parent->children = NULL; + goto memory; + } + if (ok) + /* array is already OK (even if too large) */ + goto memory; + + /* alloc a larger array if needed */ + if (oldn < n) { + free(parent->children); + parent->children = malloc(n * sizeof(*parent->children)); + } + /* refill */ + for (n = 0, child = parent->first_child; + child; + n++, child = child->next_sibling) { + parent->children[n] = child; + } + + + + memory: + /* Memory children list */ + + prev_child = NULL; + for (n = 0, child = parent->memory_first_child; + child; + n++, prev_child = child, child = child->next_sibling) { + child->parent = parent; + child->sibling_rank = n; + child->prev_sibling = prev_child; + hwloc_connect_children(child); + } + parent->memory_arity = n; + + /* I/O children list */ + + prev_child = NULL; + for (n = 0, child = parent->io_first_child; + child; + n++, prev_child = child, child = child->next_sibling) { + child->parent = parent; + child->sibling_rank = n; + child->prev_sibling = prev_child; + hwloc_connect_children(child); + } + parent->io_arity = n; + + /* Misc children list */ + + prev_child = NULL; + for (n = 0, child = parent->misc_first_child; + child; + n++, prev_child = child, child = child->next_sibling) { + child->parent = parent; + child->sibling_rank = n; + child->prev_sibling = prev_child; + hwloc_connect_children(child); + } + parent->misc_arity = n; +} + +/* + * Check whether there is an object below ROOT that has the same type as OBJ + */ +static int +find_same_type(hwloc_obj_t root, hwloc_obj_t obj) +{ + hwloc_obj_t child; + + if (hwloc_type_cmp(root, obj) == HWLOC_OBJ_EQUAL) + return 1; + + for_each_child (child, root) + if (find_same_type(child, obj)) + return 1; + + return 0; +} + +/* traverse the array of current object and compare them with top_obj. + * if equal, take the object and put its children into the remaining objs. + * if not equal, put the object into the remaining objs. + */ +static unsigned +hwloc_level_take_objects(hwloc_obj_t top_obj, + hwloc_obj_t *current_objs, unsigned n_current_objs, + hwloc_obj_t *taken_objs, unsigned n_taken_objs __hwloc_attribute_unused, + hwloc_obj_t *remaining_objs, unsigned n_remaining_objs __hwloc_attribute_unused) +{ + unsigned taken_i = 0; + unsigned new_i = 0; + unsigned i, j; + + for (i = 0; i < n_current_objs; i++) + if (hwloc_type_cmp(top_obj, current_objs[i]) == HWLOC_OBJ_EQUAL) { + /* Take it, add main children. */ + taken_objs[taken_i++] = current_objs[i]; + for (j = 0; j < current_objs[i]->arity; j++) + remaining_objs[new_i++] = current_objs[i]->children[j]; + } else { + /* Leave it. */ + remaining_objs[new_i++] = current_objs[i]; + } + +#ifdef HWLOC_DEBUG + /* Make sure we didn't mess up. */ + assert(taken_i == n_taken_objs); + assert(new_i == n_current_objs - n_taken_objs + n_remaining_objs); +#endif + + return new_i; +} + +static int +hwloc_build_level_from_list(struct hwloc_special_level_s *slevel) +{ + unsigned i, nb; + struct hwloc_obj * obj; + + /* count */ + obj = slevel->first; + i = 0; + while (obj) { + i++; + obj = obj->next_cousin; + } + nb = i; + + if (nb) { + /* allocate and fill level */ + slevel->objs = malloc(nb * sizeof(struct hwloc_obj *)); + obj = slevel->first; + i = 0; + while (obj) { + obj->logical_index = i; + slevel->objs[i] = obj; + i++; + obj = obj->next_cousin; + } + } + + slevel->nbobjs = nb; + return 0; +} + +static void +hwloc_append_special_object(struct hwloc_special_level_s *level, hwloc_obj_t obj) +{ + if (level->first) { + obj->prev_cousin = level->last; + obj->prev_cousin->next_cousin = obj; + level->last = obj; + } else { + obj->prev_cousin = NULL; + level->first = level->last = obj; + } +} + +/* Append special objects to their lists */ +static void +hwloc_list_special_objects(hwloc_topology_t topology, hwloc_obj_t obj) +{ + hwloc_obj_t child; + + if (obj->type == HWLOC_OBJ_NUMANODE) { + obj->next_cousin = NULL; + obj->depth = HWLOC_TYPE_DEPTH_NUMANODE; + /* Insert the main NUMA node list */ + hwloc_append_special_object(&topology->slevels[HWLOC_SLEVEL_NUMANODE], obj); + + /* Recurse */ + for_each_memory_child(child, obj) + hwloc_list_special_objects(topology, child); + for_each_misc_child(child, obj) + hwloc_list_special_objects(topology, child); + + } else if (obj->type == HWLOC_OBJ_MISC) { + obj->next_cousin = NULL; + obj->depth = HWLOC_TYPE_DEPTH_MISC; + /* Insert the main Misc list */ + hwloc_append_special_object(&topology->slevels[HWLOC_SLEVEL_MISC], obj); + /* Recurse, Misc only have Misc children */ + for_each_misc_child(child, obj) + hwloc_list_special_objects(topology, child); + + } else if (hwloc__obj_type_is_io(obj->type)) { + obj->next_cousin = NULL; + + if (obj->type == HWLOC_OBJ_BRIDGE) { + obj->depth = HWLOC_TYPE_DEPTH_BRIDGE; + /* Insert in the main bridge list */ + hwloc_append_special_object(&topology->slevels[HWLOC_SLEVEL_BRIDGE], obj); + + } else if (obj->type == HWLOC_OBJ_PCI_DEVICE) { + obj->depth = HWLOC_TYPE_DEPTH_PCI_DEVICE; + /* Insert in the main pcidev list */ + hwloc_append_special_object(&topology->slevels[HWLOC_SLEVEL_PCIDEV], obj); + + } else if (obj->type == HWLOC_OBJ_OS_DEVICE) { + obj->depth = HWLOC_TYPE_DEPTH_OS_DEVICE; + /* Insert in the main osdev list */ + hwloc_append_special_object(&topology->slevels[HWLOC_SLEVEL_OSDEV], obj); + } + /* Recurse, I/O only have I/O and Misc children */ + for_each_io_child(child, obj) + hwloc_list_special_objects(topology, child); + for_each_misc_child(child, obj) + hwloc_list_special_objects(topology, child); + + } else { + /* Recurse */ + for_each_child(child, obj) + hwloc_list_special_objects(topology, child); + for_each_memory_child(child, obj) + hwloc_list_special_objects(topology, child); + for_each_io_child(child, obj) + hwloc_list_special_objects(topology, child); + for_each_misc_child(child, obj) + hwloc_list_special_objects(topology, child); + } +} + +/* Build I/O levels */ +static void +hwloc_connect_io_misc_levels(hwloc_topology_t topology) +{ + unsigned i; + + for(i=0; islevels[i].objs); + memset(&topology->slevels, 0, sizeof(topology->slevels)); + + hwloc_list_special_objects(topology, topology->levels[0][0]); + + for(i=0; islevels[i]); +} + +/* + * Do the remaining work that hwloc_connect_children() did not do earlier. + * Requires object arity and children list to be properly initialized (by hwloc_connect_children()). + */ +static int +hwloc_connect_levels(hwloc_topology_t topology) +{ + unsigned l, i=0; + hwloc_obj_t *objs, *taken_objs, *new_objs, top_obj, root; + unsigned n_objs, n_taken_objs, n_new_objs; + + /* reset non-root levels (root was initialized during init and will not change here) */ + for(l=1; lnb_levels; l++) + free(topology->levels[l]); + memset(topology->levels+1, 0, (topology->nb_levels-1)*sizeof(*topology->levels)); + memset(topology->level_nbobjects+1, 0, (topology->nb_levels-1)*sizeof(*topology->level_nbobjects)); + topology->nb_levels = 1; + + /* initialize all non-IO/non-Misc depths to unknown */ + hwloc_reset_normal_type_depths(topology); + + /* initialize root type depth */ + root = topology->levels[0][0]; + root->depth = 0; + topology->type_depth[root->type] = 0; + /* root level */ + root->logical_index = 0; + root->prev_cousin = NULL; + root->next_cousin = NULL; + /* root as a child of nothing */ + root->parent = NULL; + root->sibling_rank = 0; + root->prev_sibling = NULL; + root->next_sibling = NULL; + + /* Start with children of the whole system. */ + n_objs = topology->levels[0][0]->arity; + objs = malloc(n_objs * sizeof(objs[0])); + if (!objs) { + errno = ENOMEM; + return -1; + } + memcpy(objs, topology->levels[0][0]->children, n_objs*sizeof(objs[0])); + + /* Keep building levels while there are objects left in OBJS. */ + while (n_objs) { + /* At this point, the objs array contains only objects that may go into levels */ + + /* First find which type of object is the topmost. + * Don't use PU if there are other types since we want to keep PU at the bottom. + */ + + /* Look for the first non-PU object, and use the first PU if we really find nothing else */ + for (i = 0; i < n_objs; i++) + if (objs[i]->type != HWLOC_OBJ_PU) + break; + top_obj = i == n_objs ? objs[0] : objs[i]; + + /* See if this is actually the topmost object */ + for (i = 0; i < n_objs; i++) { + if (hwloc_type_cmp(top_obj, objs[i]) != HWLOC_OBJ_EQUAL) { + if (find_same_type(objs[i], top_obj)) { + /* OBJS[i] is strictly above an object of the same type as TOP_OBJ, so it + * is above TOP_OBJ. */ + top_obj = objs[i]; + } + } + } + + /* Now peek all objects of the same type, build a level with that and + * replace them with their children. */ + + /* First count them. */ + n_taken_objs = 0; + n_new_objs = 0; + for (i = 0; i < n_objs; i++) + if (hwloc_type_cmp(top_obj, objs[i]) == HWLOC_OBJ_EQUAL) { + n_taken_objs++; + n_new_objs += objs[i]->arity; + } + + /* New level. */ + taken_objs = malloc((n_taken_objs + 1) * sizeof(taken_objs[0])); + /* New list of pending objects. */ + if (n_objs - n_taken_objs + n_new_objs) { + new_objs = malloc((n_objs - n_taken_objs + n_new_objs) * sizeof(new_objs[0])); + } else { +#ifdef HWLOC_DEBUG + assert(!n_new_objs); + assert(n_objs == n_taken_objs); +#endif + new_objs = NULL; + } + + n_new_objs = hwloc_level_take_objects(top_obj, + objs, n_objs, + taken_objs, n_taken_objs, + new_objs, n_new_objs); + + /* Ok, put numbers in the level and link cousins. */ + for (i = 0; i < n_taken_objs; i++) { + taken_objs[i]->depth = (int) topology->nb_levels; + taken_objs[i]->logical_index = i; + if (i) { + taken_objs[i]->prev_cousin = taken_objs[i-1]; + taken_objs[i-1]->next_cousin = taken_objs[i]; + } + } + taken_objs[0]->prev_cousin = NULL; + taken_objs[n_taken_objs-1]->next_cousin = NULL; + + /* One more level! */ + hwloc_debug("--- %s level", hwloc_obj_type_string(top_obj->type)); + hwloc_debug(" has number %u\n\n", topology->nb_levels); + + if (topology->type_depth[top_obj->type] == HWLOC_TYPE_DEPTH_UNKNOWN) + topology->type_depth[top_obj->type] = (int) topology->nb_levels; + else + topology->type_depth[top_obj->type] = HWLOC_TYPE_DEPTH_MULTIPLE; /* mark as unknown */ + + taken_objs[n_taken_objs] = NULL; + + if (topology->nb_levels == topology->nb_levels_allocated) { + /* extend the arrays of levels */ + void *tmplevels, *tmpnbobjs; + tmplevels = realloc(topology->levels, + 2 * topology->nb_levels_allocated * sizeof(*topology->levels)); + tmpnbobjs = realloc(topology->level_nbobjects, + 2 * topology->nb_levels_allocated * sizeof(*topology->level_nbobjects)); + if (!tmplevels || !tmpnbobjs) { + fprintf(stderr, "hwloc failed to realloc level arrays to %u\n", topology->nb_levels_allocated * 2); + + /* if one realloc succeeded, make sure the caller will free the new buffer */ + if (tmplevels) + topology->levels = tmplevels; + if (tmpnbobjs) + topology->level_nbobjects = tmpnbobjs; + /* the realloc that failed left topology->level_foo untouched, will be freed by the caller */ + + free(objs); + free(taken_objs); + free(new_objs); + errno = ENOMEM; + return -1; + } + topology->levels = tmplevels; + topology->level_nbobjects = tmpnbobjs; + memset(topology->levels + topology->nb_levels_allocated, + 0, topology->nb_levels_allocated * sizeof(*topology->levels)); + memset(topology->level_nbobjects + topology->nb_levels_allocated, + 0, topology->nb_levels_allocated * sizeof(*topology->level_nbobjects)); + topology->nb_levels_allocated *= 2; + } + /* add the new level */ + topology->level_nbobjects[topology->nb_levels] = n_taken_objs; + topology->levels[topology->nb_levels] = taken_objs; + + topology->nb_levels++; + + free(objs); + + /* Switch to new_objs */ + objs = new_objs; + n_objs = n_new_objs; + } + + /* It's empty now. */ + free(objs); + + return 0; +} + +int +hwloc_topology_reconnect(struct hwloc_topology *topology, unsigned long flags) +{ + if (flags) { + errno = EINVAL; + return -1; + } + if (!topology->modified) + return 0; + + hwloc_connect_children(topology->levels[0][0]); + + if (hwloc_connect_levels(topology) < 0) + return -1; + + hwloc_connect_io_misc_levels(topology); + + topology->modified = 0; + + return 0; +} + +void hwloc_alloc_root_sets(hwloc_obj_t root) +{ + /* + * All sets are initially NULL. + * + * At least one backend should call this function to initialize all sets at once. + * XML uses it lazily in case only some sets were given in the XML import. + * + * Other backends can check root->cpuset != NULL to see if somebody + * discovered things before them. + */ + if (!root->cpuset) + root->cpuset = hwloc_bitmap_alloc(); + if (!root->complete_cpuset) + root->complete_cpuset = hwloc_bitmap_alloc(); + if (!root->nodeset) + root->nodeset = hwloc_bitmap_alloc(); + if (!root->complete_nodeset) + root->complete_nodeset = hwloc_bitmap_alloc(); +} + +/* Main discovery loop */ +static int +hwloc_discover(struct hwloc_topology *topology) +{ + struct hwloc_backend *backend; + + topology->modified = 0; /* no need to reconnect yet */ + + topology->allowed_cpuset = hwloc_bitmap_alloc_full(); + topology->allowed_nodeset = hwloc_bitmap_alloc_full(); + + /* discover() callbacks should use hwloc_insert to add objects initialized + * through hwloc_alloc_setup_object. + * For node levels, nodeset and memory must be initialized. + * For cache levels, memory and type/depth must be initialized. + * For group levels, depth must be initialized. + */ + + /* There must be at least a PU object for each logical processor, at worse + * produced by hwloc_setup_pu_level() + */ + + /* To be able to just use hwloc_insert_object_by_cpuset to insert the object + * in the topology according to the cpuset, the cpuset field must be + * initialized. + */ + + /* A priori, All processors are visible in the topology, and allowed + * for the application. + * + * - If some processors exist but topology information is unknown for them + * (and thus the backend couldn't create objects for them), they should be + * added to the complete_cpuset field of the lowest object where the object + * could reside. + * + * - If some processors are not allowed for the application (e.g. for + * administration reasons), they should be dropped from the allowed_cpuset + * field. + * + * The same applies to the node sets complete_nodeset and allowed_cpuset. + * + * If such field doesn't exist yet, it can be allocated, and initialized to + * zero (for complete), or to full (for allowed). The values are + * automatically propagated to the whole tree after detection. + */ + + /* + * Discover CPUs first + */ + backend = topology->backends; + while (NULL != backend) { + if (backend->component->type != HWLOC_DISC_COMPONENT_TYPE_CPU + && backend->component->type != HWLOC_DISC_COMPONENT_TYPE_GLOBAL) + /* not yet */ + goto next_cpubackend; + if (!backend->discover) + goto next_cpubackend; + backend->discover(backend); + hwloc_debug_print_objects(0, topology->levels[0][0]); + +next_cpubackend: + backend = backend->next; + } + + /* One backend should have called hwloc_alloc_root_sets() + * and set bits during PU and NUMA insert. + */ + if (!topology->levels[0][0]->cpuset || hwloc_bitmap_iszero(topology->levels[0][0]->cpuset)) { + hwloc_debug("%s", "No PU added by any CPU and global backend\n"); + errno = EINVAL; + return -1; + } + + if (topology->binding_hooks.get_allowed_resources && topology->is_thissystem) { + const char *env = getenv("HWLOC_THISSYSTEM_ALLOWED_RESOURCES"); + if ((env && atoi(env)) + || (topology->flags & HWLOC_TOPOLOGY_FLAG_THISSYSTEM_ALLOWED_RESOURCES)) + topology->binding_hooks.get_allowed_resources(topology); + } + + /* If there's no NUMA node, add one with all the memory. + * root->complete_nodeset wouldn't be empty if any NUMA was ever added: + * - insert_by_cpuset() adds bits whe PU/NUMA are added. + * - XML takes care of sanitizing nodesets. + */ + if (hwloc_bitmap_iszero(topology->levels[0][0]->complete_nodeset)) { + hwloc_obj_t node; + hwloc_debug("%s", "\nAdd missing single NUMA node\n"); + node = hwloc_alloc_setup_object(topology, HWLOC_OBJ_NUMANODE, 0); + node->cpuset = hwloc_bitmap_dup(topology->levels[0][0]->cpuset); + node->nodeset = hwloc_bitmap_alloc(); + /* other nodesets will be filled below */ + hwloc_bitmap_set(node->nodeset, 0); + memcpy(&node->attr->numanode, &topology->machine_memory, sizeof(topology->machine_memory)); + memset(&topology->machine_memory, 0, sizeof(topology->machine_memory)); + hwloc_insert_object_by_cpuset(topology, node); + } else { + /* if we're sure we found all NUMA nodes without their sizes (x86 backend?), + * we could split topology->total_memory in all of them. + */ + free(topology->machine_memory.page_types); + memset(&topology->machine_memory, 0, sizeof(topology->machine_memory)); + } + + hwloc_debug("%s", "\nFixup root sets\n"); + hwloc_bitmap_and(topology->levels[0][0]->cpuset, topology->levels[0][0]->cpuset, topology->levels[0][0]->complete_cpuset); + hwloc_bitmap_and(topology->levels[0][0]->nodeset, topology->levels[0][0]->nodeset, topology->levels[0][0]->complete_nodeset); + + hwloc_bitmap_and(topology->allowed_cpuset, topology->allowed_cpuset, topology->levels[0][0]->cpuset); + hwloc_bitmap_and(topology->allowed_nodeset, topology->allowed_nodeset, topology->levels[0][0]->nodeset); + + hwloc_debug("%s", "\nPropagate sets\n"); + /* cpuset are already there thanks to the _by_cpuset insertion, + * but nodeset have to be propagated below and above NUMA nodes + */ + propagate_nodeset(topology->levels[0][0]); + /* now fixup parent/children sets */ + fixup_sets(topology->levels[0][0]); + + hwloc_debug_print_objects(0, topology->levels[0][0]); + + if (!(topology->flags & HWLOC_TOPOLOGY_FLAG_WHOLE_SYSTEM)) { + hwloc_debug("%s", "\nRemoving unauthorized sets from all sets\n"); + remove_unused_sets(topology, topology->levels[0][0]); + hwloc_debug_print_objects(0, topology->levels[0][0]); + } + + /* see if we should ignore the root now that we know how many children it has */ + if (!hwloc_filter_check_keep_object(topology, topology->levels[0][0]) + && topology->levels[0][0]->first_child && !topology->levels[0][0]->first_child->next_sibling) { + hwloc_obj_t oldroot = topology->levels[0][0]; + hwloc_obj_t newroot = oldroot->first_child; + /* switch to the new root */ + newroot->parent = NULL; + topology->levels[0][0] = newroot; + /* move oldroot memory/io/misc children before newroot children */ + if (oldroot->memory_first_child) + prepend_siblings_list(&newroot->memory_first_child, oldroot->memory_first_child, newroot); + if (oldroot->io_first_child) + prepend_siblings_list(&newroot->io_first_child, oldroot->io_first_child, newroot); + if (oldroot->misc_first_child) + prepend_siblings_list(&newroot->misc_first_child, oldroot->misc_first_child, newroot); + /* destroy oldroot and use the new one */ + hwloc_free_unlinked_object(oldroot); + } + + /* + * All object cpusets and nodesets are properly set now. + */ + + /* Now connect handy pointers to make remaining discovery easier. */ + hwloc_debug("%s", "\nOk, finished tweaking, now connect\n"); + if (hwloc_topology_reconnect(topology, 0) < 0) + return -1; + hwloc_debug_print_objects(0, topology->levels[0][0]); + + /* + * Additional discovery with other backends + */ + + backend = topology->backends; + while (NULL != backend) { + if (backend->component->type == HWLOC_DISC_COMPONENT_TYPE_CPU + || backend->component->type == HWLOC_DISC_COMPONENT_TYPE_GLOBAL) + /* already done above */ + goto next_noncpubackend; + if (!backend->discover) + goto next_noncpubackend; + backend->discover(backend); + hwloc_debug_print_objects(0, topology->levels[0][0]); + +next_noncpubackend: + backend = backend->next; + } + + hwloc_pci_belowroot_apply_locality(topology); + + hwloc_debug("%s", "\nNow reconnecting\n"); + hwloc_debug_print_objects(0, topology->levels[0][0]); + + /* Remove some stuff */ + + hwloc_debug("%s", "\nRemoving bridge objects if needed\n"); + hwloc_filter_bridges(topology, topology->levels[0][0]); + hwloc_debug_print_objects(0, topology->levels[0][0]); + + hwloc_debug("%s", "\nRemoving empty objects\n"); + remove_empty(topology, &topology->levels[0][0]); + if (!topology->levels[0][0]) { + fprintf(stderr, "Topology became empty, aborting!\n"); + return -1; + } + if (hwloc_bitmap_iszero(topology->levels[0][0]->cpuset)) { + fprintf(stderr, "Topology does not contain any PU, aborting!\n"); + return -1; + } + if (hwloc_bitmap_iszero(topology->levels[0][0]->nodeset)) { + fprintf(stderr, "Topology does not contain any NUMA node, aborting!\n"); + return -1; + } + hwloc_debug_print_objects(0, topology->levels[0][0]); + + /* Reconnect things after all these changes. + * Often needed because of Groups inserted for I/Os. + * And required for KEEP_STRUCTURE below. + */ + if (hwloc_topology_reconnect(topology, 0) < 0) + return -1; + + hwloc_debug("%s", "\nRemoving levels with HWLOC_TYPE_FILTER_KEEP_STRUCTURE\n"); + hwloc_filter_levels_keep_structure(topology); + hwloc_debug_print_objects(0, topology->levels[0][0]); + + /* accumulate children memory in total_memory fields (only once parent is set) */ + hwloc_debug("%s", "\nPropagate total memory up\n"); + propagate_total_memory(topology->levels[0][0]); + + /* setup the symmetric_subtree attribute */ + hwloc_propagate_symmetric_subtree(topology, topology->levels[0][0]); + + /* apply group depths */ + hwloc_set_group_depth(topology); + + /* add some identification attributes if not loading from XML */ + if (topology->backends + && strcmp(topology->backends->component->name, "xml")) { + char *value; + /* add a hwlocVersion */ + hwloc_obj_add_info(topology->levels[0][0], "hwlocVersion", HWLOC_VERSION); + /* add a ProcessName */ + value = hwloc_progname(topology); + if (value) { + hwloc_obj_add_info(topology->levels[0][0], "ProcessName", value); + free(value); + } + } + + return 0; +} + +/* To be called before discovery is actually launched, + * Resets everything in case a previous load initialized some stuff. + */ +void +hwloc_topology_setup_defaults(struct hwloc_topology *topology) +{ + struct hwloc_obj *root_obj; + + /* reset support */ + memset(&topology->binding_hooks, 0, sizeof(topology->binding_hooks)); + memset(topology->support.discovery, 0, sizeof(*topology->support.discovery)); + memset(topology->support.cpubind, 0, sizeof(*topology->support.cpubind)); + memset(topology->support.membind, 0, sizeof(*topology->support.membind)); + + /* Only the System object on top by default */ + topology->next_gp_index = 1; /* keep 0 as an invalid value */ + topology->nb_levels = 1; /* there's at least SYSTEM */ + topology->levels[0] = hwloc_tma_malloc (topology->tma, sizeof (hwloc_obj_t)); + topology->level_nbobjects[0] = 1; + + /* Machine-wide memory */ + topology->machine_memory.local_memory = 0; + topology->machine_memory.page_types_len = 0; + topology->machine_memory.page_types = NULL; + + /* Allowed stuff */ + topology->allowed_cpuset = NULL; + topology->allowed_nodeset = NULL; + + /* NULLify other special levels */ + memset(&topology->slevels, 0, sizeof(topology->slevels)); + /* assert the indexes of special levels */ + HWLOC_BUILD_ASSERT(HWLOC_SLEVEL_NUMANODE == HWLOC_SLEVEL_FROM_DEPTH(HWLOC_TYPE_DEPTH_NUMANODE)); + HWLOC_BUILD_ASSERT(HWLOC_SLEVEL_MISC == HWLOC_SLEVEL_FROM_DEPTH(HWLOC_TYPE_DEPTH_MISC)); + HWLOC_BUILD_ASSERT(HWLOC_SLEVEL_BRIDGE == HWLOC_SLEVEL_FROM_DEPTH(HWLOC_TYPE_DEPTH_BRIDGE)); + HWLOC_BUILD_ASSERT(HWLOC_SLEVEL_PCIDEV == HWLOC_SLEVEL_FROM_DEPTH(HWLOC_TYPE_DEPTH_PCI_DEVICE)); + HWLOC_BUILD_ASSERT(HWLOC_SLEVEL_OSDEV == HWLOC_SLEVEL_FROM_DEPTH(HWLOC_TYPE_DEPTH_OS_DEVICE)); + + /* sane values to type_depth */ + hwloc_reset_normal_type_depths(topology); + topology->type_depth[HWLOC_OBJ_NUMANODE] = HWLOC_TYPE_DEPTH_NUMANODE; + topology->type_depth[HWLOC_OBJ_MISC] = HWLOC_TYPE_DEPTH_MISC; + topology->type_depth[HWLOC_OBJ_BRIDGE] = HWLOC_TYPE_DEPTH_BRIDGE; + topology->type_depth[HWLOC_OBJ_PCI_DEVICE] = HWLOC_TYPE_DEPTH_PCI_DEVICE; + topology->type_depth[HWLOC_OBJ_OS_DEVICE] = HWLOC_TYPE_DEPTH_OS_DEVICE; + + /* Create the actual machine object, but don't touch its attributes yet + * since the OS backend may still change the object into something else + * (for instance System) + */ + root_obj = hwloc_alloc_setup_object(topology, HWLOC_OBJ_MACHINE, 0); + topology->levels[0][0] = root_obj; +} + +static void hwloc__topology_filter_init(struct hwloc_topology *topology); + +/* This function may use a tma, it cannot free() or realloc() */ +static int +hwloc__topology_init (struct hwloc_topology **topologyp, + unsigned nblevels, + struct hwloc_tma *tma) +{ + struct hwloc_topology *topology; + + topology = hwloc_tma_malloc (tma, sizeof (struct hwloc_topology)); + if(!topology) + return -1; + + topology->tma = tma; + + hwloc_components_init(); /* uses malloc without tma, but won't need it since dup() caller already took a reference */ + hwloc_backends_init(topology); + hwloc_pci_discovery_init(topology); /* make sure both dup() and load() get sane variables */ + + /* Setup topology context */ + topology->is_loaded = 0; + topology->flags = 0; + topology->is_thissystem = 1; + topology->pid = 0; + topology->userdata = NULL; + topology->topology_abi = HWLOC_TOPOLOGY_ABI; + topology->adopted_shmem_addr = NULL; + topology->adopted_shmem_length = 0; + + topology->support.discovery = hwloc_tma_malloc(tma, sizeof(*topology->support.discovery)); + topology->support.cpubind = hwloc_tma_malloc(tma, sizeof(*topology->support.cpubind)); + topology->support.membind = hwloc_tma_malloc(tma, sizeof(*topology->support.membind)); + + topology->nb_levels_allocated = nblevels; /* enough for default 9 levels = Mach+Pack+NUMA+L3+L2+L1d+L1i+Co+PU */ + topology->levels = hwloc_tma_calloc(tma, topology->nb_levels_allocated * sizeof(*topology->levels)); + topology->level_nbobjects = hwloc_tma_calloc(tma, topology->nb_levels_allocated * sizeof(*topology->level_nbobjects)); + + hwloc__topology_filter_init(topology); + + hwloc_internal_distances_init(topology); + + topology->userdata_export_cb = NULL; + topology->userdata_import_cb = NULL; + topology->userdata_not_decoded = 0; + + /* Make the topology look like something coherent but empty */ + hwloc_topology_setup_defaults(topology); + + *topologyp = topology; + return 0; +} + +int +hwloc_topology_init (struct hwloc_topology **topologyp) +{ + return hwloc__topology_init(topologyp, + 16, /* 16 is enough for default 9 levels = Mach+Pack+NUMA+L3+L2+L1d+L1i+Co+PU */ + NULL); /* no TMA for normal topologies, too many allocations to fix */ +} + +int +hwloc_topology_set_pid(struct hwloc_topology *topology __hwloc_attribute_unused, + hwloc_pid_t pid __hwloc_attribute_unused) +{ + if (topology->is_loaded) { + errno = EBUSY; + return -1; + } + + /* this does *not* change the backend */ +#ifdef HWLOC_LINUX_SYS + topology->pid = pid; + return 0; +#else /* HWLOC_LINUX_SYS */ + errno = ENOSYS; + return -1; +#endif /* HWLOC_LINUX_SYS */ +} + +int +hwloc_topology_set_synthetic(struct hwloc_topology *topology, const char *description) +{ + if (topology->is_loaded) { + errno = EBUSY; + return -1; + } + + return hwloc_disc_component_force_enable(topology, + 0 /* api */, + -1, "synthetic", + description, NULL, NULL); +} + +int +hwloc_topology_set_xml(struct hwloc_topology *topology, + const char *xmlpath) +{ + if (topology->is_loaded) { + errno = EBUSY; + return -1; + } + + return hwloc_disc_component_force_enable(topology, + 0 /* api */, + -1, "xml", + xmlpath, NULL, NULL); +} + +int +hwloc_topology_set_xmlbuffer(struct hwloc_topology *topology, + const char *xmlbuffer, + int size) +{ + if (topology->is_loaded) { + errno = EBUSY; + return -1; + } + + return hwloc_disc_component_force_enable(topology, + 0 /* api */, + -1, "xml", NULL, + xmlbuffer, (void*) (uintptr_t) size); +} + +int +hwloc_topology_set_flags (struct hwloc_topology *topology, unsigned long flags) +{ + if (topology->is_loaded) { + /* actually harmless */ + errno = EBUSY; + return -1; + } + + if (flags & ~(HWLOC_TOPOLOGY_FLAG_WHOLE_SYSTEM|HWLOC_TOPOLOGY_FLAG_IS_THISSYSTEM|HWLOC_TOPOLOGY_FLAG_THISSYSTEM_ALLOWED_RESOURCES)) { + errno = EINVAL; + return -1; + } + + topology->flags = flags; + return 0; +} + +unsigned long +hwloc_topology_get_flags (struct hwloc_topology *topology) +{ + return topology->flags; +} + +static void +hwloc__topology_filter_init(struct hwloc_topology *topology) +{ + hwloc_obj_type_t type; + /* Only ignore useless cruft by default */ + for(type = HWLOC_OBJ_TYPE_MIN; type < HWLOC_OBJ_TYPE_MAX; type++) + topology->type_filter[type] = HWLOC_TYPE_FILTER_KEEP_ALL; + topology->type_filter[HWLOC_OBJ_L1ICACHE] = HWLOC_TYPE_FILTER_KEEP_NONE; + topology->type_filter[HWLOC_OBJ_L2ICACHE] = HWLOC_TYPE_FILTER_KEEP_NONE; + topology->type_filter[HWLOC_OBJ_L3ICACHE] = HWLOC_TYPE_FILTER_KEEP_NONE; + topology->type_filter[HWLOC_OBJ_GROUP] = HWLOC_TYPE_FILTER_KEEP_STRUCTURE; + topology->type_filter[HWLOC_OBJ_MISC] = HWLOC_TYPE_FILTER_KEEP_NONE; + topology->type_filter[HWLOC_OBJ_BRIDGE] = HWLOC_TYPE_FILTER_KEEP_NONE; + topology->type_filter[HWLOC_OBJ_PCI_DEVICE] = HWLOC_TYPE_FILTER_KEEP_NONE; + topology->type_filter[HWLOC_OBJ_OS_DEVICE] = HWLOC_TYPE_FILTER_KEEP_NONE; +} + +static int +hwloc__topology_set_type_filter(struct hwloc_topology *topology, hwloc_obj_type_t type, enum hwloc_type_filter_e filter) +{ + if (type == HWLOC_OBJ_PU || type == HWLOC_OBJ_NUMANODE || type == HWLOC_OBJ_MACHINE) { + if (filter != HWLOC_TYPE_FILTER_KEEP_ALL) { + /* we need the Machine, PU and NUMA levels */ + errno = EINVAL; + return -1; + } + } else if (hwloc__obj_type_is_special(type)) { + if (filter == HWLOC_TYPE_FILTER_KEEP_STRUCTURE) { + /* I/O and Misc are outside of the main topology structure, makes no sense. */ + errno = EINVAL; + return -1; + } + } else if (type == HWLOC_OBJ_GROUP) { + if (filter == HWLOC_TYPE_FILTER_KEEP_ALL) { + /* Groups are always ignored, at least keep_structure */ + errno = EINVAL; + return -1; + } + } + + /* "important" just means "all" for non-I/O non-Misc */ + if (!hwloc__obj_type_is_special(type) && filter == HWLOC_TYPE_FILTER_KEEP_IMPORTANT) + filter = HWLOC_TYPE_FILTER_KEEP_ALL; + + topology->type_filter[type] = filter; + return 0; +} + +int +hwloc_topology_set_type_filter(struct hwloc_topology *topology, hwloc_obj_type_t type, enum hwloc_type_filter_e filter) +{ + HWLOC_BUILD_ASSERT(HWLOC_OBJ_TYPE_MIN == 0); + if ((unsigned) type >= HWLOC_OBJ_TYPE_MAX) { + errno = EINVAL; + return -1; + } + if (topology->is_loaded) { + errno = EBUSY; + return -1; + } + return hwloc__topology_set_type_filter(topology, type, filter); +} + +int +hwloc_topology_set_all_types_filter(struct hwloc_topology *topology, enum hwloc_type_filter_e filter) +{ + hwloc_obj_type_t type; + if (topology->is_loaded) { + errno = EBUSY; + return -1; + } + for(type = HWLOC_OBJ_TYPE_MIN; type < HWLOC_OBJ_TYPE_MAX; type++) + hwloc__topology_set_type_filter(topology, type, filter); + return 0; +} + +int +hwloc_topology_set_cache_types_filter(hwloc_topology_t topology, enum hwloc_type_filter_e filter) +{ + unsigned i; + for(i=HWLOC_OBJ_L1CACHE; i= HWLOC_OBJ_TYPE_MAX) { + errno = EINVAL; + return -1; + } + *filterp = topology->type_filter[type]; + return 0; +} + +void +hwloc_topology_clear (struct hwloc_topology *topology) +{ + /* no need to set to NULL after free() since callers will call setup_defaults() or just destroy the rest of the topology */ + unsigned l; + hwloc_internal_distances_destroy(topology); + hwloc_free_object_and_children(topology->levels[0][0]); + hwloc_bitmap_free(topology->allowed_cpuset); + hwloc_bitmap_free(topology->allowed_nodeset); + for (l=0; lnb_levels; l++) + free(topology->levels[l]); + for(l=0; lslevels[l].objs); + free(topology->machine_memory.page_types); +} + +void +hwloc_topology_destroy (struct hwloc_topology *topology) +{ + if (topology->adopted_shmem_addr) { + hwloc__topology_disadopt(topology); + return; + } + + hwloc_backends_disable_all(topology); + hwloc_components_fini(); + + hwloc_topology_clear(topology); + + free(topology->levels); + free(topology->level_nbobjects); + + free(topology->support.discovery); + free(topology->support.cpubind); + free(topology->support.membind); + free(topology); +} + +int +hwloc_topology_load (struct hwloc_topology *topology) +{ + int err; + + if (topology->is_loaded) { + errno = EBUSY; + return -1; + } + + hwloc_internal_distances_prepare(topology); + + if (getenv("HWLOC_XML_USERDATA_NOT_DECODED")) + topology->userdata_not_decoded = 1; + + /* Ignore variables if HWLOC_COMPONENTS is set. It will be processed later */ + if (!getenv("HWLOC_COMPONENTS")) { + /* Only apply variables if we have not changed the backend yet. + * Only the first one will be kept. + * Check for FSROOT first since it's for debugging so likely needs to override everything else. + * Check for XML last (that's the one that may be set system-wide by administrators) + * so that it's only used if other variables are not set, + * to allow users to override easily. + */ + if (!topology->backends) { + const char *fsroot_path_env = getenv("HWLOC_FSROOT"); + if (fsroot_path_env) + hwloc_disc_component_force_enable(topology, + 1 /* env force */, + HWLOC_DISC_COMPONENT_TYPE_CPU, "linux", + NULL /* backend will getenv again */, NULL, NULL); + } + if (!topology->backends) { + const char *cpuid_path_env = getenv("HWLOC_CPUID_PATH"); + if (cpuid_path_env) + hwloc_disc_component_force_enable(topology, + 1 /* env force */, + HWLOC_DISC_COMPONENT_TYPE_CPU, "x86", + NULL /* backend will getenv again */, NULL, NULL); + } + if (!topology->backends) { + const char *synthetic_env = getenv("HWLOC_SYNTHETIC"); + if (synthetic_env) + hwloc_disc_component_force_enable(topology, + 1 /* env force */, + -1, "synthetic", + synthetic_env, NULL, NULL); + } + if (!topology->backends) { + const char *xmlpath_env = getenv("HWLOC_XMLFILE"); + if (xmlpath_env) + hwloc_disc_component_force_enable(topology, + 1 /* env force */, + -1, "xml", + xmlpath_env, NULL, NULL); + } + } + + /* instantiate all possible other backends now */ + hwloc_disc_components_enable_others(topology); + /* now that backends are enabled, update the thissystem flag and some callbacks */ + hwloc_backends_is_thissystem(topology); + hwloc_backends_find_callbacks(topology); + /* + * Now set binding hooks according to topology->is_thissystem + * and what the native OS backend offers. + */ + hwloc_set_binding_hooks(topology); + + hwloc_pci_discovery_prepare(topology); + + /* actual topology discovery */ + err = hwloc_discover(topology); + if (err < 0) + goto out; + + hwloc_pci_discovery_exit(topology); + +#ifndef HWLOC_DEBUG + if (getenv("HWLOC_DEBUG_CHECK")) +#endif + hwloc_topology_check(topology); + + /* Mark distances objs arrays as invalid since we may have removed objects + * from the topology after adding the distances (remove_empty, etc). + * It would be hard to actually verify whether it's needed. + */ + hwloc_internal_distances_invalidate_cached_objs(topology); + /* And refresh distances so that multithreaded concurrent distances_get() + * don't refresh() concurrently (disallowed). + */ + hwloc_internal_distances_refresh(topology); + + topology->is_loaded = 1; + return 0; + + out: + hwloc_pci_discovery_exit(topology); + hwloc_topology_clear(topology); + hwloc_topology_setup_defaults(topology); + hwloc_backends_disable_all(topology); + return -1; +} + +/* adjust object cpusets according the given droppedcpuset, + * drop object whose cpuset becomes empty and that have no children, + * and propagate NUMA node removal as nodeset changes in parents. + */ +static void +restrict_object_by_cpuset(hwloc_topology_t topology, unsigned long flags, hwloc_obj_t *pobj, + hwloc_bitmap_t droppedcpuset, hwloc_bitmap_t droppednodeset) +{ + hwloc_obj_t obj = *pobj, child, *pchild; + int modified = 0; + + if (hwloc_bitmap_intersects(obj->complete_cpuset, droppedcpuset)) { + hwloc_bitmap_andnot(obj->cpuset, obj->cpuset, droppedcpuset); + hwloc_bitmap_andnot(obj->complete_cpuset, obj->complete_cpuset, droppedcpuset); + modified = 1; + } else { + if ((flags & HWLOC_RESTRICT_FLAG_REMOVE_CPULESS) + && hwloc_bitmap_iszero(obj->complete_cpuset)) { + /* we're empty, there's a NUMAnode below us, it'll be removed this time */ + modified = 1; + } + /* nodeset cannot intersect unless cpuset intersects or is empty */ + if (droppednodeset) + assert(!hwloc_bitmap_intersects(obj->complete_nodeset, droppednodeset) + || hwloc_bitmap_iszero(obj->complete_cpuset)); + } + if (droppednodeset) { + hwloc_bitmap_andnot(obj->nodeset, obj->nodeset, droppednodeset); + hwloc_bitmap_andnot(obj->complete_nodeset, obj->complete_nodeset, droppednodeset); + } + + if (modified) { + for_each_child_safe(child, obj, pchild) + restrict_object_by_cpuset(topology, flags, pchild, droppedcpuset, droppednodeset); + /* if some hwloc_bitmap_first(child->complete_cpuset) changed, children might need to be reordered */ + hwloc__reorder_children(obj); + + for_each_memory_child_safe(child, obj, pchild) + restrict_object_by_cpuset(topology, flags, pchild, droppedcpuset, droppednodeset); + /* local NUMA nodes have the same cpusets, no need to reorder them */ + + /* Nothing to restrict under I/O or Misc */ + } + + if (!obj->first_child && !obj->memory_first_child /* arity not updated before connect_children() */ + && hwloc_bitmap_iszero(obj->cpuset) + && (obj->type != HWLOC_OBJ_NUMANODE || (flags & HWLOC_RESTRICT_FLAG_REMOVE_CPULESS))) { + /* remove object */ + hwloc_debug("%s", "\nRemoving object during restrict"); + hwloc_debug_print_object(0, obj); + + if (!(flags & HWLOC_RESTRICT_FLAG_ADAPT_IO)) { + hwloc_free_object_siblings_and_children(obj->io_first_child); + obj->io_first_child = NULL; + } + if (!(flags & HWLOC_RESTRICT_FLAG_ADAPT_MISC)) { + hwloc_free_object_siblings_and_children(obj->misc_first_child); + obj->misc_first_child = NULL; + } + assert(!obj->first_child); + assert(!obj->memory_first_child); + unlink_and_free_single_object(pobj); + topology->modified = 1; + } +} + +int +hwloc_topology_restrict(struct hwloc_topology *topology, hwloc_const_cpuset_t cpuset, unsigned long flags) +{ + hwloc_bitmap_t droppedcpuset, droppednodeset; + + if (!topology->is_loaded) { + errno = EINVAL; + return -1; + } + + if (flags & ~(HWLOC_RESTRICT_FLAG_REMOVE_CPULESS + |HWLOC_RESTRICT_FLAG_ADAPT_MISC|HWLOC_RESTRICT_FLAG_ADAPT_IO)) { + errno = EINVAL; + return -1; + } + + /* make sure we'll keep something in the topology */ + if (!hwloc_bitmap_intersects(cpuset, topology->allowed_cpuset)) { + errno = EINVAL; /* easy failure, just don't touch the topology */ + return -1; + } + + droppedcpuset = hwloc_bitmap_alloc(); + droppednodeset = hwloc_bitmap_alloc(); + if (!droppedcpuset || !droppednodeset) { + hwloc_bitmap_free(droppedcpuset); + hwloc_bitmap_free(droppednodeset); + return -1; + } + + /* cpuset to clear */ + hwloc_bitmap_not(droppedcpuset, cpuset); + /* nodeset to clear */ + if (flags & HWLOC_RESTRICT_FLAG_REMOVE_CPULESS) { + hwloc_obj_t node = hwloc_get_obj_by_type(topology, HWLOC_OBJ_NUMANODE, 0); + do { + /* node will be removed if nodeset gets or was empty */ + if (hwloc_bitmap_iszero(node->cpuset) + || hwloc_bitmap_isincluded(node->cpuset, droppedcpuset)) + hwloc_bitmap_set(droppednodeset, node->os_index); + node = node->next_cousin; + } while (node); + + /* check we're not removing all NUMA nodes */ + if (hwloc_bitmap_isincluded(topology->allowed_nodeset, droppednodeset)) { + errno = EINVAL; /* easy failure, just don't touch the topology */ + hwloc_bitmap_free(droppedcpuset); + hwloc_bitmap_free(droppednodeset); + return -1; + } + } + /* remove nodeset if empty */ + if (!(flags & HWLOC_RESTRICT_FLAG_REMOVE_CPULESS) + || hwloc_bitmap_iszero(droppednodeset)) { + hwloc_bitmap_free(droppednodeset); + droppednodeset = NULL; + } + + /* now recurse to filter sets and drop things */ + restrict_object_by_cpuset(topology, flags, &topology->levels[0][0], droppedcpuset, droppednodeset); + hwloc_bitmap_andnot(topology->allowed_cpuset, topology->allowed_cpuset, droppedcpuset); + if (droppednodeset) + hwloc_bitmap_andnot(topology->allowed_nodeset, topology->allowed_nodeset, droppednodeset); + + hwloc_bitmap_free(droppedcpuset); + hwloc_bitmap_free(droppednodeset); + + if (hwloc_topology_reconnect(topology, 0) < 0) + goto out; + + /* some objects may have disappeared, we need to update distances objs arrays */ + hwloc_internal_distances_invalidate_cached_objs(topology); + + hwloc_filter_levels_keep_structure(topology); + hwloc_propagate_symmetric_subtree(topology, topology->levels[0][0]); + propagate_total_memory(topology->levels[0][0]); + +#ifndef HWLOC_DEBUG + if (getenv("HWLOC_DEBUG_CHECK")) +#endif + hwloc_topology_check(topology); + + return 0; + + out: + /* unrecoverable failure, re-init the topology */ + hwloc_topology_clear(topology); + hwloc_topology_setup_defaults(topology); + return -1; +} + +int +hwloc_topology_is_thissystem(struct hwloc_topology *topology) +{ + return topology->is_thissystem; +} + +int +hwloc_topology_get_depth(struct hwloc_topology *topology) +{ + return (int) topology->nb_levels; +} + +const struct hwloc_topology_support * +hwloc_topology_get_support(struct hwloc_topology * topology) +{ + return &topology->support; +} + +void hwloc_topology_set_userdata(struct hwloc_topology * topology, const void *userdata) +{ + topology->userdata = (void *) userdata; +} + +void * hwloc_topology_get_userdata(struct hwloc_topology * topology) +{ + return topology->userdata; +} + +hwloc_const_cpuset_t +hwloc_topology_get_complete_cpuset(hwloc_topology_t topology) +{ + return hwloc_get_root_obj(topology)->complete_cpuset; +} + +hwloc_const_cpuset_t +hwloc_topology_get_topology_cpuset(hwloc_topology_t topology) +{ + return hwloc_get_root_obj(topology)->cpuset; +} + +hwloc_const_cpuset_t +hwloc_topology_get_allowed_cpuset(hwloc_topology_t topology) +{ + return topology->allowed_cpuset; +} + +hwloc_const_nodeset_t +hwloc_topology_get_complete_nodeset(hwloc_topology_t topology) +{ + return hwloc_get_root_obj(topology)->complete_nodeset; +} + +hwloc_const_nodeset_t +hwloc_topology_get_topology_nodeset(hwloc_topology_t topology) +{ + return hwloc_get_root_obj(topology)->nodeset; +} + +hwloc_const_nodeset_t +hwloc_topology_get_allowed_nodeset(hwloc_topology_t topology) +{ + return topology->allowed_nodeset; +} + + +/**************** + * Debug Checks * + ****************/ + +#ifndef NDEBUG /* assert only enabled if !NDEBUG */ + +static void +hwloc__check_child_siblings(hwloc_obj_t parent, hwloc_obj_t *array, + unsigned arity, unsigned i, + hwloc_obj_t child, hwloc_obj_t prev) +{ + assert(child->parent == parent); + + assert(child->sibling_rank == i); + if (array) + assert(child == array[i]); + + if (prev) + assert(prev->next_sibling == child); + assert(child->prev_sibling == prev); + + if (!i) + assert(child->prev_sibling == NULL); + else + assert(child->prev_sibling != NULL); + + if (i == arity-1) + assert(child->next_sibling == NULL); + else + assert(child->next_sibling != NULL); +} + +static void +hwloc__check_object(hwloc_topology_t topology, hwloc_bitmap_t gp_indexes, hwloc_obj_t obj); + +/* check children between a parent object */ +static void +hwloc__check_normal_children(hwloc_topology_t topology, hwloc_bitmap_t gp_indexes, hwloc_obj_t parent) +{ + hwloc_obj_t child, prev; + unsigned j; + + if (!parent->arity) { + /* check whether that parent has no children for real */ + assert(!parent->children); + assert(!parent->first_child); + assert(!parent->last_child); + return; + } + /* check whether that parent has children for real */ + assert(parent->children); + assert(parent->first_child); + assert(parent->last_child); + + /* sibling checks */ + for(prev = NULL, child = parent->first_child, j = 0; + child; + prev = child, child = child->next_sibling, j++) { + /* normal child */ + assert(hwloc__obj_type_is_normal(child->type)); + /* check depth */ + assert(child->depth > parent->depth); + /* check siblings */ + hwloc__check_child_siblings(parent, parent->children, parent->arity, j, child, prev); + /* recurse */ + hwloc__check_object(topology, gp_indexes, child); + } + /* check arity */ + assert(j == parent->arity); + + assert(parent->first_child == parent->children[0]); + assert(parent->last_child == parent->children[parent->arity-1]); + + /* no normal children below a PU */ + if (parent->type == HWLOC_OBJ_PU) + assert(!parent->arity); +} + +static void +hwloc__check_children_cpusets(hwloc_topology_t topology __hwloc_attribute_unused, hwloc_obj_t obj) +{ + /* we already checked in the caller that objects have either all sets or none */ + hwloc_obj_t child; + int prev_first, prev_empty; + + if (obj->type == HWLOC_OBJ_PU) { + /* PU cpuset is just itself, with no normal children */ + assert(hwloc_bitmap_weight(obj->cpuset) == 1); + assert(hwloc_bitmap_first(obj->cpuset) == (int) obj->os_index); + assert(hwloc_bitmap_weight(obj->complete_cpuset) == 1); + assert(hwloc_bitmap_first(obj->complete_cpuset) == (int) obj->os_index); + if (!(topology->flags & HWLOC_TOPOLOGY_FLAG_WHOLE_SYSTEM)) { + assert(hwloc_bitmap_isset(topology->allowed_cpuset, (int) obj->os_index)); + } + assert(!obj->arity); + } else if (hwloc__obj_type_is_memory(obj->type)) { + /* memory object cpuset is equal to its parent */ + assert(hwloc_bitmap_isequal(obj->parent->cpuset, obj->cpuset)); + assert(!obj->arity); + } else if (!hwloc__obj_type_is_special(obj->type)) { + hwloc_bitmap_t set; + /* other obj cpuset is an exclusive OR of normal children, except for PUs */ + set = hwloc_bitmap_alloc(); + for_each_child(child, obj) { + assert(!hwloc_bitmap_intersects(set, child->cpuset)); + hwloc_bitmap_or(set, set, child->cpuset); + } + assert(hwloc_bitmap_isequal(set, obj->cpuset)); + hwloc_bitmap_free(set); + } + + /* check that memory children have same cpuset */ + for_each_memory_child(child, obj) + assert(hwloc_bitmap_isequal(obj->cpuset, child->cpuset)); + + /* check that children complete_cpusets are properly ordered, empty ones may be anywhere + * (can be wrong for main cpuset since removed PUs can break the ordering). + */ + prev_first = -1; /* -1 works fine with first comparisons below */ + prev_empty = 0; /* no empty cpuset in previous children */ + for_each_child(child, obj) { + int first = hwloc_bitmap_first(child->complete_cpuset); + if (first >= 0) { + assert(!prev_empty); /* no objects with CPU after objects without CPU */ + assert(prev_first < first); + } else { + prev_empty = 1; + } + prev_first = first; + } +} + +static void +hwloc__check_memory_children(hwloc_topology_t topology, hwloc_bitmap_t gp_indexes, hwloc_obj_t parent) +{ + unsigned j; + hwloc_obj_t child, prev; + + if (!parent->memory_arity) { + /* check whether that parent has no children for real */ + assert(!parent->memory_first_child); + return; + } + /* check whether that parent has children for real */ + assert(parent->memory_first_child); + + for(prev = NULL, child = parent->memory_first_child, j = 0; + child; + prev = child, child = child->next_sibling, j++) { + assert(hwloc__obj_type_is_memory(child->type)); + /* check siblings */ + hwloc__check_child_siblings(parent, NULL, parent->memory_arity, j, child, prev); + /* only Memory and Misc children, recurse */ + assert(!child->first_child); + assert(!child->io_first_child); + hwloc__check_object(topology, gp_indexes, child); + } + /* check arity */ + assert(j == parent->memory_arity); + + /* no memory children below a NUMA node */ + if (parent->type == HWLOC_OBJ_NUMANODE) + assert(!parent->memory_arity); +} + +static void +hwloc__check_io_children(hwloc_topology_t topology, hwloc_bitmap_t gp_indexes, hwloc_obj_t parent) +{ + unsigned j; + hwloc_obj_t child, prev; + + if (!parent->io_arity) { + /* check whether that parent has no children for real */ + assert(!parent->io_first_child); + return; + } + /* check whether that parent has children for real */ + assert(parent->io_first_child); + + for(prev = NULL, child = parent->io_first_child, j = 0; + child; + prev = child, child = child->next_sibling, j++) { + /* all children must be I/O */ + assert(hwloc__obj_type_is_io(child->type)); + /* check siblings */ + hwloc__check_child_siblings(parent, NULL, parent->io_arity, j, child, prev); + /* only I/O and Misc children, recurse */ + assert(!child->first_child); + assert(!child->memory_first_child); + hwloc__check_object(topology, gp_indexes, child); + } + /* check arity */ + assert(j == parent->io_arity); +} + +static void +hwloc__check_misc_children(hwloc_topology_t topology, hwloc_bitmap_t gp_indexes, hwloc_obj_t parent) +{ + unsigned j; + hwloc_obj_t child, prev; + + if (!parent->misc_arity) { + /* check whether that parent has no children for real */ + assert(!parent->misc_first_child); + return; + } + /* check whether that parent has children for real */ + assert(parent->misc_first_child); + + for(prev = NULL, child = parent->misc_first_child, j = 0; + child; + prev = child, child = child->next_sibling, j++) { + /* all children must be Misc */ + assert(child->type == HWLOC_OBJ_MISC); + /* check siblings */ + hwloc__check_child_siblings(parent, NULL, parent->misc_arity, j, child, prev); + /* only Misc children, recurse */ + assert(!child->first_child); + assert(!child->memory_first_child); + assert(!child->io_first_child); + hwloc__check_object(topology, gp_indexes, child); + } + /* check arity */ + assert(j == parent->misc_arity); +} + +static void +hwloc__check_object(hwloc_topology_t topology, hwloc_bitmap_t gp_indexes, hwloc_obj_t obj) +{ + assert(!hwloc_bitmap_isset(gp_indexes, obj->gp_index)); + hwloc_bitmap_set(gp_indexes, obj->gp_index); + + HWLOC_BUILD_ASSERT(HWLOC_OBJ_TYPE_MIN == 0); + assert((unsigned) obj->type < HWLOC_OBJ_TYPE_MAX); + + assert(hwloc_filter_check_keep_object(topology, obj)); + + /* check that sets and depth */ + if (hwloc__obj_type_is_special(obj->type)) { + assert(!obj->cpuset); + if (obj->type == HWLOC_OBJ_BRIDGE) + assert(obj->depth == HWLOC_TYPE_DEPTH_BRIDGE); + else if (obj->type == HWLOC_OBJ_PCI_DEVICE) + assert(obj->depth == HWLOC_TYPE_DEPTH_PCI_DEVICE); + else if (obj->type == HWLOC_OBJ_OS_DEVICE) + assert(obj->depth == HWLOC_TYPE_DEPTH_OS_DEVICE); + else if (obj->type == HWLOC_OBJ_MISC) + assert(obj->depth == HWLOC_TYPE_DEPTH_MISC); + } else { + assert(obj->cpuset); + if (obj->type == HWLOC_OBJ_NUMANODE) + assert(obj->depth == HWLOC_TYPE_DEPTH_NUMANODE); + else + assert(obj->depth >= 0); + } + + /* group depth cannot be -1 anymore in v2.0+ */ + if (obj->type == HWLOC_OBJ_GROUP) { + assert(obj->attr->group.depth != (unsigned) -1); + } + + /* there's other cpusets and nodesets if and only if there's a main cpuset */ + assert(!!obj->cpuset == !!obj->complete_cpuset); + assert(!!obj->cpuset == !!obj->nodeset); + assert(!!obj->nodeset == !!obj->complete_nodeset); + + /* check that complete/inline sets are larger than the main sets */ + if (obj->cpuset) { + assert(hwloc_bitmap_isincluded(obj->cpuset, obj->complete_cpuset)); + assert(hwloc_bitmap_isincluded(obj->nodeset, obj->complete_nodeset)); + } + + /* check cache type/depth vs type */ + if (hwloc__obj_type_is_cache(obj->type)) { + if (hwloc__obj_type_is_icache(obj->type)) + assert(obj->attr->cache.type == HWLOC_OBJ_CACHE_INSTRUCTION); + else if (hwloc__obj_type_is_dcache(obj->type)) + assert(obj->attr->cache.type == HWLOC_OBJ_CACHE_DATA + || obj->attr->cache.type == HWLOC_OBJ_CACHE_UNIFIED); + else + assert(0); + assert(hwloc_cache_type_by_depth_type(obj->attr->cache.depth, obj->attr->cache.type) == obj->type); + } + + /* check children */ + hwloc__check_normal_children(topology, gp_indexes, obj); + hwloc__check_memory_children(topology, gp_indexes, obj); + hwloc__check_io_children(topology, gp_indexes, obj); + hwloc__check_misc_children(topology, gp_indexes, obj); + hwloc__check_children_cpusets(topology, obj); + /* nodesets are checked during another recursion with state below */ +} + +static void +hwloc__check_nodesets(hwloc_topology_t topology, hwloc_obj_t obj, hwloc_bitmap_t parentset) +{ + hwloc_obj_t child; + int prev_first; + + if (obj->type == HWLOC_OBJ_NUMANODE) { + /* NUMANODE nodeset is just itself, with no memory/normal children */ + assert(hwloc_bitmap_weight(obj->nodeset) == 1); + assert(hwloc_bitmap_first(obj->nodeset) == (int) obj->os_index); + assert(hwloc_bitmap_weight(obj->complete_nodeset) == 1); + assert(hwloc_bitmap_first(obj->complete_nodeset) == (int) obj->os_index); + if (!(topology->flags & HWLOC_TOPOLOGY_FLAG_WHOLE_SYSTEM)) { + assert(hwloc_bitmap_isset(topology->allowed_nodeset, (int) obj->os_index)); + } + assert(!obj->arity); + assert(!obj->memory_arity); + assert(hwloc_bitmap_isincluded(obj->nodeset, parentset)); + } else { + hwloc_bitmap_t myset; + hwloc_bitmap_t childset; + + /* the local nodeset is an exclusive OR of memory children */ + myset = hwloc_bitmap_alloc(); + for_each_memory_child(child, obj) { + assert(!hwloc_bitmap_intersects(myset, child->nodeset)); + hwloc_bitmap_or(myset, myset, child->nodeset); + } + /* the local nodeset cannot intersect with parents' local nodeset */ + assert(!hwloc_bitmap_intersects(myset, parentset)); + hwloc_bitmap_or(parentset, parentset, myset); + hwloc_bitmap_free(myset); + /* parentset now contains parent+local contribution */ + + /* for each children, recurse to check/get its contribution */ + childset = hwloc_bitmap_alloc(); + for_each_child(child, obj) { + hwloc_bitmap_t set = hwloc_bitmap_dup(parentset); /* don't touch parentset, we don't want to propagate the first child contribution to other children */ + hwloc__check_nodesets(topology, child, set); + /* extract this child contribution */ + hwloc_bitmap_andnot(set, set, parentset); + /* save it */ + assert(!hwloc_bitmap_intersects(childset, set)); + hwloc_bitmap_or(childset, childset, set); + hwloc_bitmap_free(set); + } + /* combine child contribution into parentset */ + assert(!hwloc_bitmap_intersects(parentset, childset)); + hwloc_bitmap_or(parentset, parentset, childset); + hwloc_bitmap_free(childset); + /* now check that our nodeset is combination of parent, local and children */ + assert(hwloc_bitmap_isequal(obj->nodeset, parentset)); + } + + /* check that children complete_nodesets are properly ordered, empty ones may be anywhere + * (can be wrong for main nodeset since removed PUs can break the ordering). + */ + prev_first = -1; /* -1 works fine with first comparisons below */ + for_each_memory_child(child, obj) { + int first = hwloc_bitmap_first(child->complete_nodeset); + assert(prev_first < first); + prev_first = first; + } +} + +static void +hwloc__check_level(struct hwloc_topology *topology, int depth, + hwloc_obj_t first, hwloc_obj_t last) +{ + unsigned width = hwloc_get_nbobjs_by_depth(topology, depth); + struct hwloc_obj *prev = NULL; + hwloc_obj_t obj; + unsigned j; + + /* check each object of the level */ + for(j=0; jdepth == depth); + assert(obj->logical_index == j); + /* check that all objects in the level have the same type */ + if (prev) { + assert(hwloc_type_cmp(obj, prev) == HWLOC_OBJ_EQUAL); + assert(prev->next_cousin == obj); + } + assert(obj->prev_cousin == prev); + + /* check that PUs and NUMA nodes have correct cpuset/nodeset */ + if (obj->type == HWLOC_OBJ_NUMANODE) { + assert(hwloc_bitmap_weight(obj->complete_nodeset) == 1); + assert(hwloc_bitmap_first(obj->complete_nodeset) == (int) obj->os_index); + } + prev = obj; + } + if (prev) + assert(prev->next_cousin == NULL); + + if (width) { + /* check first object of the level */ + obj = hwloc_get_obj_by_depth(topology, depth, 0); + assert(obj); + assert(!obj->prev_cousin); + /* check type */ + assert(hwloc_get_depth_type(topology, depth) == obj->type); + assert(depth == hwloc_get_type_depth(topology, obj->type) + || HWLOC_TYPE_DEPTH_MULTIPLE == hwloc_get_type_depth(topology, obj->type)); + /* check last object of the level */ + obj = hwloc_get_obj_by_depth(topology, depth, width-1); + assert(obj); + assert(!obj->next_cousin); + } + + if (depth < 0) { + assert(first == hwloc_get_obj_by_depth(topology, depth, 0)); + assert(last == hwloc_get_obj_by_depth(topology, depth, width-1)); + } else { + assert(!first); + assert(!last); + } + + /* check last+1 object of the level */ + obj = hwloc_get_obj_by_depth(topology, depth, width); + assert(!obj); +} + +/* check a whole topology structure */ +void +hwloc_topology_check(struct hwloc_topology *topology) +{ + struct hwloc_obj *obj; + hwloc_bitmap_t gp_indexes, set; + hwloc_obj_type_t type; + unsigned i; + int j, depth; + + /* make sure we can use ranges to check types */ + + /* hwloc__obj_type_is_{,d,i}cache() want cache types to be ordered like this */ + HWLOC_BUILD_ASSERT(HWLOC_OBJ_L2CACHE == HWLOC_OBJ_L1CACHE + 1); + HWLOC_BUILD_ASSERT(HWLOC_OBJ_L3CACHE == HWLOC_OBJ_L2CACHE + 1); + HWLOC_BUILD_ASSERT(HWLOC_OBJ_L4CACHE == HWLOC_OBJ_L3CACHE + 1); + HWLOC_BUILD_ASSERT(HWLOC_OBJ_L5CACHE == HWLOC_OBJ_L4CACHE + 1); + HWLOC_BUILD_ASSERT(HWLOC_OBJ_L1ICACHE == HWLOC_OBJ_L5CACHE + 1); + HWLOC_BUILD_ASSERT(HWLOC_OBJ_L2ICACHE == HWLOC_OBJ_L1ICACHE + 1); + HWLOC_BUILD_ASSERT(HWLOC_OBJ_L3ICACHE == HWLOC_OBJ_L2ICACHE + 1); + + /* hwloc__obj_type_is_normal(), hwloc__obj_type_is_memory(), hwloc__obj_type_is_io(), hwloc__obj_type_is_special() + * and hwloc_reset_normal_type_depths() + * want special types to be ordered like this, after all normal types. + */ + HWLOC_BUILD_ASSERT(HWLOC_OBJ_NUMANODE + 1 == HWLOC_OBJ_BRIDGE); + HWLOC_BUILD_ASSERT(HWLOC_OBJ_BRIDGE + 1 == HWLOC_OBJ_PCI_DEVICE); + HWLOC_BUILD_ASSERT(HWLOC_OBJ_PCI_DEVICE + 1 == HWLOC_OBJ_OS_DEVICE); + HWLOC_BUILD_ASSERT(HWLOC_OBJ_OS_DEVICE + 1 == HWLOC_OBJ_MISC); + HWLOC_BUILD_ASSERT(HWLOC_OBJ_MISC + 1 == HWLOC_OBJ_TYPE_MAX); + + /* make sure order and priority arrays have the right size */ + HWLOC_BUILD_ASSERT(sizeof(obj_type_order)/sizeof(*obj_type_order) == HWLOC_OBJ_TYPE_MAX); + HWLOC_BUILD_ASSERT(sizeof(obj_order_type)/sizeof(*obj_order_type) == HWLOC_OBJ_TYPE_MAX); + HWLOC_BUILD_ASSERT(sizeof(obj_type_priority)/sizeof(*obj_type_priority) == HWLOC_OBJ_TYPE_MAX); + + /* make sure group are not entirely ignored */ + assert(topology->type_filter[HWLOC_OBJ_GROUP] != HWLOC_TYPE_FILTER_KEEP_ALL); + + /* make sure order arrays are coherent */ + for(type=HWLOC_OBJ_TYPE_MIN; typemodified); + + /* check that first level is Machine. + * Root object cannot be ignored. And Machine can only be merged into PU, + * but there must be a NUMA node below Machine, and it cannot be below PU. + */ + assert(hwloc_get_depth_type(topology, 0) == HWLOC_OBJ_MACHINE); + + /* check that last level is PU and that it doesn't have memory */ + assert(hwloc_get_depth_type(topology, depth-1) == HWLOC_OBJ_PU); + assert(hwloc_get_nbobjs_by_depth(topology, depth-1) > 0); + for(i=0; itype == HWLOC_OBJ_PU); + assert(!obj->memory_first_child); + } + /* check that other levels are not PU or Machine */ + for(j=1; j=0 || d == HWLOC_TYPE_DEPTH_UNKNOWN || d == HWLOC_TYPE_DEPTH_MULTIPLE); + } + } + + /* top-level specific checks */ + assert(hwloc_get_nbobjs_by_depth(topology, 0) == 1); + obj = hwloc_get_root_obj(topology); + assert(obj); + assert(!obj->parent); + assert(obj->cpuset); + assert(!obj->depth); + + /* check that allowed sets are larger than the main sets */ + if (topology->flags & HWLOC_TOPOLOGY_FLAG_WHOLE_SYSTEM) { + assert(hwloc_bitmap_isincluded(topology->allowed_cpuset, obj->cpuset)); + assert(hwloc_bitmap_isincluded(topology->allowed_nodeset, obj->nodeset)); + } else { + assert(hwloc_bitmap_isequal(topology->allowed_cpuset, obj->cpuset)); + assert(hwloc_bitmap_isequal(topology->allowed_nodeset, obj->nodeset)); + } + + /* check each level */ + for(j=0; jslevels[j].first, topology->slevels[j].last); + + /* recurse and check the tree of children, and type-specific checks */ + gp_indexes = hwloc_bitmap_alloc(); /* TODO prealloc to topology->next_gp_index */ + hwloc__check_object(topology, gp_indexes, obj); + hwloc_bitmap_free(gp_indexes); + + /* recurse and check the nodesets of children */ + set = hwloc_bitmap_alloc(); + hwloc__check_nodesets(topology, obj, set); + hwloc_bitmap_free(set); +} + +#else /* NDEBUG */ + +void +hwloc_topology_check(struct hwloc_topology *topology __hwloc_attribute_unused) +{ +} + +#endif /* NDEBUG */ diff --git a/src/3rdparty/hwloc/src/traversal.c b/src/3rdparty/hwloc/src/traversal.c new file mode 100644 index 00000000..9c5e6268 --- /dev/null +++ b/src/3rdparty/hwloc/src/traversal.c @@ -0,0 +1,616 @@ +/* + * Copyright © 2009 CNRS + * Copyright © 2009-2018 Inria. All rights reserved. + * Copyright © 2009-2010 Université Bordeaux + * Copyright © 2009-2011 Cisco Systems, Inc. All rights reserved. + * See COPYING in top-level directory. + */ + +#include +#include +#include +#include +#include +#ifdef HAVE_STRINGS_H +#include +#endif /* HAVE_STRINGS_H */ + +int +hwloc_get_type_depth (struct hwloc_topology *topology, hwloc_obj_type_t type) +{ + HWLOC_BUILD_ASSERT(HWLOC_OBJ_TYPE_MIN == 0); + if ((unsigned) type >= HWLOC_OBJ_TYPE_MAX) + return HWLOC_TYPE_DEPTH_UNKNOWN; + else + return topology->type_depth[type]; +} + +hwloc_obj_type_t +hwloc_get_depth_type (hwloc_topology_t topology, int depth) +{ + if ((unsigned)depth >= topology->nb_levels) + switch (depth) { + case HWLOC_TYPE_DEPTH_NUMANODE: + return HWLOC_OBJ_NUMANODE; + case HWLOC_TYPE_DEPTH_BRIDGE: + return HWLOC_OBJ_BRIDGE; + case HWLOC_TYPE_DEPTH_PCI_DEVICE: + return HWLOC_OBJ_PCI_DEVICE; + case HWLOC_TYPE_DEPTH_OS_DEVICE: + return HWLOC_OBJ_OS_DEVICE; + case HWLOC_TYPE_DEPTH_MISC: + return HWLOC_OBJ_MISC; + default: + return HWLOC_OBJ_TYPE_NONE; + } + return topology->levels[depth][0]->type; +} + +int +hwloc_get_memory_parents_depth (hwloc_topology_t topology) +{ + int depth = HWLOC_TYPE_DEPTH_UNKNOWN; + /* memory leaves are always NUMA nodes for now, no need to check parents of other memory types */ + hwloc_obj_t numa = hwloc_get_obj_by_depth(topology, HWLOC_TYPE_DEPTH_NUMANODE, 0); + assert(numa); + while (numa) { + hwloc_obj_t parent = numa->parent; + /* walk-up the memory hierarchy */ + while (hwloc__obj_type_is_memory(parent->type)) + parent = parent->parent; + + if (depth == HWLOC_TYPE_DEPTH_UNKNOWN) + depth = parent->depth; + else if (depth != parent->depth) + return HWLOC_TYPE_DEPTH_MULTIPLE; + + numa = numa->next_cousin; + } + + assert(depth >= 0); + return depth; +} + +unsigned +hwloc_get_nbobjs_by_depth (struct hwloc_topology *topology, int depth) +{ + if ((unsigned)depth >= topology->nb_levels) { + unsigned l = HWLOC_SLEVEL_FROM_DEPTH(depth); + if (l < HWLOC_NR_SLEVELS) + return topology->slevels[l].nbobjs; + else + return 0; + } + return topology->level_nbobjects[depth]; +} + +struct hwloc_obj * +hwloc_get_obj_by_depth (struct hwloc_topology *topology, int depth, unsigned idx) +{ + if ((unsigned)depth >= topology->nb_levels) { + unsigned l = HWLOC_SLEVEL_FROM_DEPTH(depth); + if (l < HWLOC_NR_SLEVELS) + return idx < topology->slevels[l].nbobjs ? topology->slevels[l].objs[idx] : NULL; + else + return NULL; + } + if (idx >= topology->level_nbobjects[depth]) + return NULL; + return topology->levels[depth][idx]; +} + +int +hwloc_obj_type_is_normal(hwloc_obj_type_t type) +{ + return hwloc__obj_type_is_normal(type); +} + +int +hwloc_obj_type_is_memory(hwloc_obj_type_t type) +{ + return hwloc__obj_type_is_memory(type); +} + +int +hwloc_obj_type_is_io(hwloc_obj_type_t type) +{ + return hwloc__obj_type_is_io(type); +} + +int +hwloc_obj_type_is_cache(hwloc_obj_type_t type) +{ + return hwloc__obj_type_is_cache(type); +} + +int +hwloc_obj_type_is_dcache(hwloc_obj_type_t type) +{ + return hwloc__obj_type_is_dcache(type); +} + +int +hwloc_obj_type_is_icache(hwloc_obj_type_t type) +{ + return hwloc__obj_type_is_icache(type); +} + +unsigned hwloc_get_closest_objs (struct hwloc_topology *topology, struct hwloc_obj *src, struct hwloc_obj **objs, unsigned max) +{ + struct hwloc_obj *parent, *nextparent, **src_objs; + unsigned i,src_nbobjects; + unsigned stored = 0; + + if (!src->cpuset) + return 0; + + src_nbobjects = topology->level_nbobjects[src->depth]; + src_objs = topology->levels[src->depth]; + + parent = src; + while (stored < max) { + while (1) { + nextparent = parent->parent; + if (!nextparent) + goto out; + if (!hwloc_bitmap_isequal(parent->cpuset, nextparent->cpuset)) + break; + parent = nextparent; + } + + /* traverse src's objects and find those that are in nextparent and were not in parent */ + for(i=0; icpuset, nextparent->cpuset) + && !hwloc_bitmap_isincluded(src_objs[i]->cpuset, parent->cpuset)) { + objs[stored++] = src_objs[i]; + if (stored == max) + goto out; + } + } + parent = nextparent; + } + + out: + return stored; +} + +static int +hwloc__get_largest_objs_inside_cpuset (struct hwloc_obj *current, hwloc_const_bitmap_t set, + struct hwloc_obj ***res, int *max) +{ + int gotten = 0; + unsigned i; + + /* the caller must ensure this */ + if (*max <= 0) + return 0; + + if (hwloc_bitmap_isequal(current->cpuset, set)) { + **res = current; + (*res)++; + (*max)--; + return 1; + } + + for (i=0; iarity; i++) { + hwloc_bitmap_t subset; + int ret; + + /* split out the cpuset part corresponding to this child and see if there's anything to do */ + if (!hwloc_bitmap_intersects(set,current->children[i]->cpuset)) + continue; + + subset = hwloc_bitmap_dup(set); + hwloc_bitmap_and(subset, subset, current->children[i]->cpuset); + ret = hwloc__get_largest_objs_inside_cpuset (current->children[i], subset, res, max); + gotten += ret; + hwloc_bitmap_free(subset); + + /* if no more room to store remaining objects, return what we got so far */ + if (!*max) + break; + } + + return gotten; +} + +int +hwloc_get_largest_objs_inside_cpuset (struct hwloc_topology *topology, hwloc_const_bitmap_t set, + struct hwloc_obj **objs, int max) +{ + struct hwloc_obj *current = topology->levels[0][0]; + + if (!hwloc_bitmap_isincluded(set, current->cpuset)) + return -1; + + if (max <= 0) + return 0; + + return hwloc__get_largest_objs_inside_cpuset (current, set, &objs, &max); +} + +const char * +hwloc_obj_type_string (hwloc_obj_type_t obj) +{ + switch (obj) + { + case HWLOC_OBJ_MACHINE: return "Machine"; + case HWLOC_OBJ_MISC: return "Misc"; + case HWLOC_OBJ_GROUP: return "Group"; + case HWLOC_OBJ_NUMANODE: return "NUMANode"; + case HWLOC_OBJ_PACKAGE: return "Package"; + case HWLOC_OBJ_L1CACHE: return "L1Cache"; + case HWLOC_OBJ_L2CACHE: return "L2Cache"; + case HWLOC_OBJ_L3CACHE: return "L3Cache"; + case HWLOC_OBJ_L4CACHE: return "L4Cache"; + case HWLOC_OBJ_L5CACHE: return "L5Cache"; + case HWLOC_OBJ_L1ICACHE: return "L1iCache"; + case HWLOC_OBJ_L2ICACHE: return "L2iCache"; + case HWLOC_OBJ_L3ICACHE: return "L3iCache"; + case HWLOC_OBJ_CORE: return "Core"; + case HWLOC_OBJ_BRIDGE: return "Bridge"; + case HWLOC_OBJ_PCI_DEVICE: return "PCIDev"; + case HWLOC_OBJ_OS_DEVICE: return "OSDev"; + case HWLOC_OBJ_PU: return "PU"; + default: return "Unknown"; + } +} + +int +hwloc_type_sscanf(const char *string, hwloc_obj_type_t *typep, + union hwloc_obj_attr_u *attrp, size_t attrsize) +{ + hwloc_obj_type_t type = (hwloc_obj_type_t) -1; + unsigned depthattr = (unsigned) -1; + hwloc_obj_cache_type_t cachetypeattr = (hwloc_obj_cache_type_t) -1; /* unspecified */ + hwloc_obj_bridge_type_t ubtype = (hwloc_obj_bridge_type_t) -1; + hwloc_obj_osdev_type_t ostype = (hwloc_obj_osdev_type_t) -1; + char *end; + + /* never match the ending \0 since we want to match things like core:2 too. + * just use hwloc_strncasecmp() everywhere. + */ + + /* types without a custom depth */ + + /* osdev subtype first to avoid conflicts coproc/core etc */ + if (!hwloc_strncasecmp(string, "os", 2)) { + type = HWLOC_OBJ_OS_DEVICE; + } else if (!hwloc_strncasecmp(string, "bloc", 4)) { + type = HWLOC_OBJ_OS_DEVICE; + ostype = HWLOC_OBJ_OSDEV_BLOCK; + } else if (!hwloc_strncasecmp(string, "net", 3)) { + type = HWLOC_OBJ_OS_DEVICE; + ostype = HWLOC_OBJ_OSDEV_NETWORK; + } else if (!hwloc_strncasecmp(string, "openfab", 7)) { + type = HWLOC_OBJ_OS_DEVICE; + ostype = HWLOC_OBJ_OSDEV_OPENFABRICS; + } else if (!hwloc_strncasecmp(string, "dma", 3)) { + type = HWLOC_OBJ_OS_DEVICE; + ostype = HWLOC_OBJ_OSDEV_DMA; + } else if (!hwloc_strncasecmp(string, "gpu", 3)) { + type = HWLOC_OBJ_OS_DEVICE; + ostype = HWLOC_OBJ_OSDEV_GPU; + } else if (!hwloc_strncasecmp(string, "copro", 5) + || !hwloc_strncasecmp(string, "co-pro", 6)) { + type = HWLOC_OBJ_OS_DEVICE; + ostype = HWLOC_OBJ_OSDEV_COPROC; + + } else if (!hwloc_strncasecmp(string, "machine", 2)) { + type = HWLOC_OBJ_MACHINE; + } else if (!hwloc_strncasecmp(string, "node", 2) + || !hwloc_strncasecmp(string, "numa", 2)) { /* matches node and numanode */ + type = HWLOC_OBJ_NUMANODE; + } else if (!hwloc_strncasecmp(string, "package", 2) + || !hwloc_strncasecmp(string, "socket", 2)) { /* backward compat with v1.10 */ + type = HWLOC_OBJ_PACKAGE; + } else if (!hwloc_strncasecmp(string, "core", 2)) { + type = HWLOC_OBJ_CORE; + } else if (!hwloc_strncasecmp(string, "pu", 2)) { + type = HWLOC_OBJ_PU; + } else if (!hwloc_strncasecmp(string, "misc", 4)) { + type = HWLOC_OBJ_MISC; + + } else if (!hwloc_strncasecmp(string, "bridge", 4)) { + type = HWLOC_OBJ_BRIDGE; + } else if (!hwloc_strncasecmp(string, "hostbridge", 6)) { + type = HWLOC_OBJ_BRIDGE; + ubtype = HWLOC_OBJ_BRIDGE_HOST; + } else if (!hwloc_strncasecmp(string, "pcibridge", 5)) { + type = HWLOC_OBJ_BRIDGE; + ubtype = HWLOC_OBJ_BRIDGE_PCI; + + } else if (!hwloc_strncasecmp(string, "pci", 3)) { + type = HWLOC_OBJ_PCI_DEVICE; + + /* types with depthattr */ + } else if ((string[0] == 'l' || string[0] == 'L') && string[1] >= '0' && string[1] <= '9') { + depthattr = strtol(string+1, &end, 10); + if (*end == 'i') { + if (depthattr >= 1 && depthattr <= 3) { + type = HWLOC_OBJ_L1ICACHE + depthattr-1; + cachetypeattr = HWLOC_OBJ_CACHE_INSTRUCTION; + } else + return -1; + } else { + if (depthattr >= 1 && depthattr <= 5) { + type = HWLOC_OBJ_L1CACHE + depthattr-1; + cachetypeattr = *end == 'd' ? HWLOC_OBJ_CACHE_DATA : HWLOC_OBJ_CACHE_UNIFIED; + } else + return -1; + } + + } else if (!hwloc_strncasecmp(string, "group", 2)) { + size_t length; + type = HWLOC_OBJ_GROUP; + length = strcspn(string, "0123456789"); + if (length <= 5 && !hwloc_strncasecmp(string, "group", length) + && string[length] >= '0' && string[length] <= '9') { + depthattr = strtol(string+length, &end, 10); + } + + } else + return -1; + + *typep = type; + if (attrp) { + if (hwloc__obj_type_is_cache(type) && attrsize >= sizeof(attrp->cache)) { + attrp->cache.depth = depthattr; + attrp->cache.type = cachetypeattr; + } else if (type == HWLOC_OBJ_GROUP && attrsize >= sizeof(attrp->group)) { + attrp->group.depth = depthattr; + } else if (type == HWLOC_OBJ_BRIDGE && attrsize >= sizeof(attrp->bridge)) { + attrp->bridge.upstream_type = ubtype; + attrp->bridge.downstream_type = HWLOC_OBJ_BRIDGE_PCI; /* nothing else so far */ + } else if (type == HWLOC_OBJ_OS_DEVICE && attrsize >= sizeof(attrp->osdev)) { + attrp->osdev.type = ostype; + } + } + return 0; +} + +int +hwloc_type_sscanf_as_depth(const char *string, hwloc_obj_type_t *typep, + hwloc_topology_t topology, int *depthp) +{ + union hwloc_obj_attr_u attr; + hwloc_obj_type_t type; + int depth; + int err; + + err = hwloc_type_sscanf(string, &type, &attr, sizeof(attr)); + if (err < 0) + return err; + + depth = hwloc_get_type_depth(topology, type); + if (type == HWLOC_OBJ_GROUP + && depth == HWLOC_TYPE_DEPTH_MULTIPLE + && attr.group.depth != (unsigned)-1) { + unsigned l; + depth = HWLOC_TYPE_DEPTH_UNKNOWN; + for(l=0; lnb_levels; l++) { + if (topology->levels[l][0]->type == HWLOC_OBJ_GROUP + && topology->levels[l][0]->attr->group.depth == attr.group.depth) { + depth = (int)l; + break; + } + } + } + + if (typep) + *typep = type; + *depthp = depth; + return 0; +} + +static const char* hwloc_obj_cache_type_letter(hwloc_obj_cache_type_t type) +{ + switch (type) { + case HWLOC_OBJ_CACHE_UNIFIED: return ""; + case HWLOC_OBJ_CACHE_DATA: return "d"; + case HWLOC_OBJ_CACHE_INSTRUCTION: return "i"; + default: return "unknown"; + } +} + +int +hwloc_obj_type_snprintf(char * __hwloc_restrict string, size_t size, hwloc_obj_t obj, int verbose) +{ + hwloc_obj_type_t type = obj->type; + switch (type) { + case HWLOC_OBJ_MISC: + case HWLOC_OBJ_MACHINE: + case HWLOC_OBJ_NUMANODE: + case HWLOC_OBJ_PACKAGE: + case HWLOC_OBJ_CORE: + case HWLOC_OBJ_PU: + return hwloc_snprintf(string, size, "%s", hwloc_obj_type_string(type)); + case HWLOC_OBJ_L1CACHE: + case HWLOC_OBJ_L2CACHE: + case HWLOC_OBJ_L3CACHE: + case HWLOC_OBJ_L4CACHE: + case HWLOC_OBJ_L5CACHE: + case HWLOC_OBJ_L1ICACHE: + case HWLOC_OBJ_L2ICACHE: + case HWLOC_OBJ_L3ICACHE: + return hwloc_snprintf(string, size, "L%u%s%s", obj->attr->cache.depth, + hwloc_obj_cache_type_letter(obj->attr->cache.type), + verbose ? "Cache" : ""); + case HWLOC_OBJ_GROUP: + if (obj->attr->group.depth != (unsigned) -1) + return hwloc_snprintf(string, size, "%s%u", hwloc_obj_type_string(type), obj->attr->group.depth); + else + return hwloc_snprintf(string, size, "%s", hwloc_obj_type_string(type)); + case HWLOC_OBJ_BRIDGE: + return hwloc_snprintf(string, size, obj->attr->bridge.upstream_type == HWLOC_OBJ_BRIDGE_PCI ? "PCIBridge" : "HostBridge"); + case HWLOC_OBJ_PCI_DEVICE: + return hwloc_snprintf(string, size, "PCI"); + case HWLOC_OBJ_OS_DEVICE: + switch (obj->attr->osdev.type) { + case HWLOC_OBJ_OSDEV_BLOCK: return hwloc_snprintf(string, size, "Block"); + case HWLOC_OBJ_OSDEV_NETWORK: return hwloc_snprintf(string, size, verbose ? "Network" : "Net"); + case HWLOC_OBJ_OSDEV_OPENFABRICS: return hwloc_snprintf(string, size, "OpenFabrics"); + case HWLOC_OBJ_OSDEV_DMA: return hwloc_snprintf(string, size, "DMA"); + case HWLOC_OBJ_OSDEV_GPU: return hwloc_snprintf(string, size, "GPU"); + case HWLOC_OBJ_OSDEV_COPROC: return hwloc_snprintf(string, size, verbose ? "Co-Processor" : "CoProc"); + default: + if (size > 0) + *string = '\0'; + return 0; + } + break; + default: + if (size > 0) + *string = '\0'; + return 0; + } +} + +int +hwloc_obj_attr_snprintf(char * __hwloc_restrict string, size_t size, hwloc_obj_t obj, const char * separator, int verbose) +{ + const char *prefix = ""; + char *tmp = string; + ssize_t tmplen = size; + int ret = 0; + int res; + + /* make sure we output at least an empty string */ + if (size) + *string = '\0'; + + /* print memory attributes */ + res = 0; + if (verbose) { + if (obj->type == HWLOC_OBJ_NUMANODE && obj->attr->numanode.local_memory) + res = hwloc_snprintf(tmp, tmplen, "%slocal=%lu%s%stotal=%lu%s", + prefix, + (unsigned long) hwloc_memory_size_printf_value(obj->attr->numanode.local_memory, verbose), + hwloc_memory_size_printf_unit(obj->attr->numanode.local_memory, verbose), + separator, + (unsigned long) hwloc_memory_size_printf_value(obj->total_memory, verbose), + hwloc_memory_size_printf_unit(obj->total_memory, verbose)); + else if (obj->total_memory) + res = hwloc_snprintf(tmp, tmplen, "%stotal=%lu%s", + prefix, + (unsigned long) hwloc_memory_size_printf_value(obj->total_memory, verbose), + hwloc_memory_size_printf_unit(obj->total_memory, verbose)); + } else { + if (obj->type == HWLOC_OBJ_NUMANODE && obj->attr->numanode.local_memory) + res = hwloc_snprintf(tmp, tmplen, "%s%lu%s", + prefix, + (unsigned long) hwloc_memory_size_printf_value(obj->attr->numanode.local_memory, verbose), + hwloc_memory_size_printf_unit(obj->attr->numanode.local_memory, verbose)); + } + if (res < 0) + return -1; + ret += res; + if (ret > 0) + prefix = separator; + if (res >= tmplen) + res = tmplen>0 ? (int)tmplen - 1 : 0; + tmp += res; + tmplen -= res; + + /* printf type-specific attributes */ + res = 0; + switch (obj->type) { + case HWLOC_OBJ_L1CACHE: + case HWLOC_OBJ_L2CACHE: + case HWLOC_OBJ_L3CACHE: + case HWLOC_OBJ_L4CACHE: + case HWLOC_OBJ_L5CACHE: + case HWLOC_OBJ_L1ICACHE: + case HWLOC_OBJ_L2ICACHE: + case HWLOC_OBJ_L3ICACHE: + if (verbose) { + char assoc[32]; + if (obj->attr->cache.associativity == -1) + snprintf(assoc, sizeof(assoc), "%sfully-associative", separator); + else if (obj->attr->cache.associativity == 0) + *assoc = '\0'; + else + snprintf(assoc, sizeof(assoc), "%sways=%d", separator, obj->attr->cache.associativity); + res = hwloc_snprintf(tmp, tmplen, "%ssize=%lu%s%slinesize=%u%s", + prefix, + (unsigned long) hwloc_memory_size_printf_value(obj->attr->cache.size, verbose), + hwloc_memory_size_printf_unit(obj->attr->cache.size, verbose), + separator, obj->attr->cache.linesize, + assoc); + } else + res = hwloc_snprintf(tmp, tmplen, "%s%lu%s", + prefix, + (unsigned long) hwloc_memory_size_printf_value(obj->attr->cache.size, verbose), + hwloc_memory_size_printf_unit(obj->attr->cache.size, verbose)); + break; + case HWLOC_OBJ_BRIDGE: + if (verbose) { + char up[128], down[64]; + /* upstream is PCI or HOST */ + if (obj->attr->bridge.upstream_type == HWLOC_OBJ_BRIDGE_PCI) { + char linkspeed[64]= ""; + if (obj->attr->pcidev.linkspeed) + snprintf(linkspeed, sizeof(linkspeed), "%slink=%.2fGB/s", separator, obj->attr->pcidev.linkspeed); + snprintf(up, sizeof(up), "busid=%04x:%02x:%02x.%01x%sid=%04x:%04x%sclass=%04x(%s)%s", + obj->attr->pcidev.domain, obj->attr->pcidev.bus, obj->attr->pcidev.dev, obj->attr->pcidev.func, separator, + obj->attr->pcidev.vendor_id, obj->attr->pcidev.device_id, separator, + obj->attr->pcidev.class_id, hwloc_pci_class_string(obj->attr->pcidev.class_id), linkspeed); + } else + *up = '\0'; + /* downstream is_PCI */ + snprintf(down, sizeof(down), "buses=%04x:[%02x-%02x]", + obj->attr->bridge.downstream.pci.domain, obj->attr->bridge.downstream.pci.secondary_bus, obj->attr->bridge.downstream.pci.subordinate_bus); + if (*up) + res = hwloc_snprintf(string, size, "%s%s%s", up, separator, down); + else + res = hwloc_snprintf(string, size, "%s", down); + } + break; + case HWLOC_OBJ_PCI_DEVICE: + if (verbose) { + char linkspeed[64]= ""; + if (obj->attr->pcidev.linkspeed) + snprintf(linkspeed, sizeof(linkspeed), "%slink=%.2fGB/s", separator, obj->attr->pcidev.linkspeed); + res = hwloc_snprintf(string, size, "busid=%04x:%02x:%02x.%01x%sid=%04x:%04x%sclass=%04x(%s)%s", + obj->attr->pcidev.domain, obj->attr->pcidev.bus, obj->attr->pcidev.dev, obj->attr->pcidev.func, separator, + obj->attr->pcidev.vendor_id, obj->attr->pcidev.device_id, separator, + obj->attr->pcidev.class_id, hwloc_pci_class_string(obj->attr->pcidev.class_id), linkspeed); + } + break; + default: + break; + } + if (res < 0) + return -1; + ret += res; + if (ret > 0) + prefix = separator; + if (res >= tmplen) + res = tmplen>0 ? (int)tmplen - 1 : 0; + tmp += res; + tmplen -= res; + + /* printf infos */ + if (verbose) { + unsigned i; + for(i=0; iinfos_count; i++) { + struct hwloc_info_s *info = &obj->infos[i]; + const char *quote = strchr(info->value, ' ') ? "\"" : ""; + res = hwloc_snprintf(tmp, tmplen, "%s%s=%s%s%s", + prefix, + info->name, + quote, info->value, quote); + if (res < 0) + return -1; + ret += res; + if (res >= tmplen) + res = tmplen>0 ? (int)tmplen - 1 : 0; + tmp += res; + tmplen -= res; + if (ret > 0) + prefix = separator; + } + } + + return ret; +} diff --git a/src/3rdparty/libcpuid/asm-bits.c b/src/3rdparty/libcpuid/asm-bits.c index b8e32284..bfabd404 100644 --- a/src/3rdparty/libcpuid/asm-bits.c +++ b/src/3rdparty/libcpuid/asm-bits.c @@ -1,825 +1,836 @@ -/* - * Copyright 2008 Veselin Georgiev, - * anrieffNOSPAM @ mgail_DOT.com (convert to gmail) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "libcpuid.h" -#include "asm-bits.h" - -int cpuid_exists_by_eflags(void) -{ -#if defined(PLATFORM_X64) - return 1; /* CPUID is always present on the x86_64 */ -#elif defined(PLATFORM_X86) -# if defined(COMPILER_GCC) - int result; - __asm __volatile( - " pushfl\n" - " pop %%eax\n" - " mov %%eax, %%ecx\n" - " xor $0x200000, %%eax\n" - " push %%eax\n" - " popfl\n" - " pushfl\n" - " pop %%eax\n" - " xor %%ecx, %%eax\n" - " mov %%eax, %0\n" - " push %%ecx\n" - " popfl\n" - : "=m"(result) - : :"eax", "ecx", "memory"); - return (result != 0); -# elif defined(COMPILER_MICROSOFT) - int result; - __asm { - pushfd - pop eax - mov ecx, eax - xor eax, 0x200000 - push eax - popfd - pushfd - pop eax - xor eax, ecx - mov result, eax - push ecx - popfd - }; - return (result != 0); -# else - return 0; -# endif /* COMPILER_MICROSOFT */ -#else - return 0; -#endif /* PLATFORM_X86 */ -} - -#ifdef INLINE_ASM_SUPPORTED -/* - * with MSVC/AMD64, the exec_cpuid() and cpu_rdtsc() functions - * are implemented in separate .asm files. Otherwise, use inline assembly - */ -void exec_cpuid(uint32_t *regs) -{ -#ifdef COMPILER_GCC -# ifdef PLATFORM_X64 - __asm __volatile( - " mov %0, %%rdi\n" - - " push %%rbx\n" - " push %%rcx\n" - " push %%rdx\n" - - " mov (%%rdi), %%eax\n" - " mov 4(%%rdi), %%ebx\n" - " mov 8(%%rdi), %%ecx\n" - " mov 12(%%rdi), %%edx\n" - - " cpuid\n" - - " movl %%eax, (%%rdi)\n" - " movl %%ebx, 4(%%rdi)\n" - " movl %%ecx, 8(%%rdi)\n" - " movl %%edx, 12(%%rdi)\n" - " pop %%rdx\n" - " pop %%rcx\n" - " pop %%rbx\n" - : - :"m"(regs) - :"memory", "eax", "rdi" - ); -# else - __asm __volatile( - " mov %0, %%edi\n" - - " push %%ebx\n" - " push %%ecx\n" - " push %%edx\n" - - " mov (%%edi), %%eax\n" - " mov 4(%%edi), %%ebx\n" - " mov 8(%%edi), %%ecx\n" - " mov 12(%%edi), %%edx\n" - - " cpuid\n" - - " mov %%eax, (%%edi)\n" - " mov %%ebx, 4(%%edi)\n" - " mov %%ecx, 8(%%edi)\n" - " mov %%edx, 12(%%edi)\n" - " pop %%edx\n" - " pop %%ecx\n" - " pop %%ebx\n" - : - :"m"(regs) - :"memory", "eax", "edi" - ); -# endif /* COMPILER_GCC */ -#else -# ifdef COMPILER_MICROSOFT - __asm { - push ebx - push ecx - push edx - push edi - mov edi, regs - - mov eax, [edi] - mov ebx, [edi+4] - mov ecx, [edi+8] - mov edx, [edi+12] - - cpuid - - mov [edi], eax - mov [edi+4], ebx - mov [edi+8], ecx - mov [edi+12], edx - - pop edi - pop edx - pop ecx - pop ebx - } -# else -# error "Unsupported compiler" -# endif /* COMPILER_MICROSOFT */ -#endif -} -#endif /* INLINE_ASSEMBLY_SUPPORTED */ - -#ifdef INLINE_ASM_SUPPORTED -void cpu_rdtsc(uint64_t* result) -{ - uint32_t low_part, hi_part; -#ifdef COMPILER_GCC - __asm __volatile ( - " rdtsc\n" - " mov %%eax, %0\n" - " mov %%edx, %1\n" - :"=m"(low_part), "=m"(hi_part)::"memory", "eax", "edx" - ); -#else -# ifdef COMPILER_MICROSOFT - __asm { - rdtsc - mov low_part, eax - mov hi_part, edx - }; -# else -# error "Unsupported compiler" -# endif /* COMPILER_MICROSOFT */ -#endif /* COMPILER_GCC */ - *result = (uint64_t)low_part + (((uint64_t) hi_part) << 32); -} -#endif /* INLINE_ASM_SUPPORTED */ - -#ifdef INLINE_ASM_SUPPORTED -void busy_sse_loop(int cycles) -{ -#ifdef COMPILER_GCC -#ifndef __APPLE__ -# define XALIGN ".balign 16\n" -#else -# define XALIGN ".align 4\n" -#endif - __asm __volatile ( - " xorps %%xmm0, %%xmm0\n" - " xorps %%xmm1, %%xmm1\n" - " xorps %%xmm2, %%xmm2\n" - " xorps %%xmm3, %%xmm3\n" - " xorps %%xmm4, %%xmm4\n" - " xorps %%xmm5, %%xmm5\n" - " xorps %%xmm6, %%xmm6\n" - " xorps %%xmm7, %%xmm7\n" - XALIGN - /* ".bsLoop:\n" */ - "1:\n" - // 0: - " addps %%xmm1, %%xmm0\n" - " addps %%xmm2, %%xmm1\n" - " addps %%xmm3, %%xmm2\n" - " addps %%xmm4, %%xmm3\n" - " addps %%xmm5, %%xmm4\n" - " addps %%xmm6, %%xmm5\n" - " addps %%xmm7, %%xmm6\n" - " addps %%xmm0, %%xmm7\n" - // 1: - " addps %%xmm1, %%xmm0\n" - " addps %%xmm2, %%xmm1\n" - " addps %%xmm3, %%xmm2\n" - " addps %%xmm4, %%xmm3\n" - " addps %%xmm5, %%xmm4\n" - " addps %%xmm6, %%xmm5\n" - " addps %%xmm7, %%xmm6\n" - " addps %%xmm0, %%xmm7\n" - // 2: - " addps %%xmm1, %%xmm0\n" - " addps %%xmm2, %%xmm1\n" - " addps %%xmm3, %%xmm2\n" - " addps %%xmm4, %%xmm3\n" - " addps %%xmm5, %%xmm4\n" - " addps %%xmm6, %%xmm5\n" - " addps %%xmm7, %%xmm6\n" - " addps %%xmm0, %%xmm7\n" - // 3: - " addps %%xmm1, %%xmm0\n" - " addps %%xmm2, %%xmm1\n" - " addps %%xmm3, %%xmm2\n" - " addps %%xmm4, %%xmm3\n" - " addps %%xmm5, %%xmm4\n" - " addps %%xmm6, %%xmm5\n" - " addps %%xmm7, %%xmm6\n" - " addps %%xmm0, %%xmm7\n" - // 4: - " addps %%xmm1, %%xmm0\n" - " addps %%xmm2, %%xmm1\n" - " addps %%xmm3, %%xmm2\n" - " addps %%xmm4, %%xmm3\n" - " addps %%xmm5, %%xmm4\n" - " addps %%xmm6, %%xmm5\n" - " addps %%xmm7, %%xmm6\n" - " addps %%xmm0, %%xmm7\n" - // 5: - " addps %%xmm1, %%xmm0\n" - " addps %%xmm2, %%xmm1\n" - " addps %%xmm3, %%xmm2\n" - " addps %%xmm4, %%xmm3\n" - " addps %%xmm5, %%xmm4\n" - " addps %%xmm6, %%xmm5\n" - " addps %%xmm7, %%xmm6\n" - " addps %%xmm0, %%xmm7\n" - // 6: - " addps %%xmm1, %%xmm0\n" - " addps %%xmm2, %%xmm1\n" - " addps %%xmm3, %%xmm2\n" - " addps %%xmm4, %%xmm3\n" - " addps %%xmm5, %%xmm4\n" - " addps %%xmm6, %%xmm5\n" - " addps %%xmm7, %%xmm6\n" - " addps %%xmm0, %%xmm7\n" - // 7: - " addps %%xmm1, %%xmm0\n" - " addps %%xmm2, %%xmm1\n" - " addps %%xmm3, %%xmm2\n" - " addps %%xmm4, %%xmm3\n" - " addps %%xmm5, %%xmm4\n" - " addps %%xmm6, %%xmm5\n" - " addps %%xmm7, %%xmm6\n" - " addps %%xmm0, %%xmm7\n" - // 8: - " addps %%xmm1, %%xmm0\n" - " addps %%xmm2, %%xmm1\n" - " addps %%xmm3, %%xmm2\n" - " addps %%xmm4, %%xmm3\n" - " addps %%xmm5, %%xmm4\n" - " addps %%xmm6, %%xmm5\n" - " addps %%xmm7, %%xmm6\n" - " addps %%xmm0, %%xmm7\n" - // 9: - " addps %%xmm1, %%xmm0\n" - " addps %%xmm2, %%xmm1\n" - " addps %%xmm3, %%xmm2\n" - " addps %%xmm4, %%xmm3\n" - " addps %%xmm5, %%xmm4\n" - " addps %%xmm6, %%xmm5\n" - " addps %%xmm7, %%xmm6\n" - " addps %%xmm0, %%xmm7\n" - //10: - " addps %%xmm1, %%xmm0\n" - " addps %%xmm2, %%xmm1\n" - " addps %%xmm3, %%xmm2\n" - " addps %%xmm4, %%xmm3\n" - " addps %%xmm5, %%xmm4\n" - " addps %%xmm6, %%xmm5\n" - " addps %%xmm7, %%xmm6\n" - " addps %%xmm0, %%xmm7\n" - //11: - " addps %%xmm1, %%xmm0\n" - " addps %%xmm2, %%xmm1\n" - " addps %%xmm3, %%xmm2\n" - " addps %%xmm4, %%xmm3\n" - " addps %%xmm5, %%xmm4\n" - " addps %%xmm6, %%xmm5\n" - " addps %%xmm7, %%xmm6\n" - " addps %%xmm0, %%xmm7\n" - //12: - " addps %%xmm1, %%xmm0\n" - " addps %%xmm2, %%xmm1\n" - " addps %%xmm3, %%xmm2\n" - " addps %%xmm4, %%xmm3\n" - " addps %%xmm5, %%xmm4\n" - " addps %%xmm6, %%xmm5\n" - " addps %%xmm7, %%xmm6\n" - " addps %%xmm0, %%xmm7\n" - //13: - " addps %%xmm1, %%xmm0\n" - " addps %%xmm2, %%xmm1\n" - " addps %%xmm3, %%xmm2\n" - " addps %%xmm4, %%xmm3\n" - " addps %%xmm5, %%xmm4\n" - " addps %%xmm6, %%xmm5\n" - " addps %%xmm7, %%xmm6\n" - " addps %%xmm0, %%xmm7\n" - //14: - " addps %%xmm1, %%xmm0\n" - " addps %%xmm2, %%xmm1\n" - " addps %%xmm3, %%xmm2\n" - " addps %%xmm4, %%xmm3\n" - " addps %%xmm5, %%xmm4\n" - " addps %%xmm6, %%xmm5\n" - " addps %%xmm7, %%xmm6\n" - " addps %%xmm0, %%xmm7\n" - //15: - " addps %%xmm1, %%xmm0\n" - " addps %%xmm2, %%xmm1\n" - " addps %%xmm3, %%xmm2\n" - " addps %%xmm4, %%xmm3\n" - " addps %%xmm5, %%xmm4\n" - " addps %%xmm6, %%xmm5\n" - " addps %%xmm7, %%xmm6\n" - " addps %%xmm0, %%xmm7\n" - //16: - " addps %%xmm1, %%xmm0\n" - " addps %%xmm2, %%xmm1\n" - " addps %%xmm3, %%xmm2\n" - " addps %%xmm4, %%xmm3\n" - " addps %%xmm5, %%xmm4\n" - " addps %%xmm6, %%xmm5\n" - " addps %%xmm7, %%xmm6\n" - " addps %%xmm0, %%xmm7\n" - //17: - " addps %%xmm1, %%xmm0\n" - " addps %%xmm2, %%xmm1\n" - " addps %%xmm3, %%xmm2\n" - " addps %%xmm4, %%xmm3\n" - " addps %%xmm5, %%xmm4\n" - " addps %%xmm6, %%xmm5\n" - " addps %%xmm7, %%xmm6\n" - " addps %%xmm0, %%xmm7\n" - //18: - " addps %%xmm1, %%xmm0\n" - " addps %%xmm2, %%xmm1\n" - " addps %%xmm3, %%xmm2\n" - " addps %%xmm4, %%xmm3\n" - " addps %%xmm5, %%xmm4\n" - " addps %%xmm6, %%xmm5\n" - " addps %%xmm7, %%xmm6\n" - " addps %%xmm0, %%xmm7\n" - //19: - " addps %%xmm1, %%xmm0\n" - " addps %%xmm2, %%xmm1\n" - " addps %%xmm3, %%xmm2\n" - " addps %%xmm4, %%xmm3\n" - " addps %%xmm5, %%xmm4\n" - " addps %%xmm6, %%xmm5\n" - " addps %%xmm7, %%xmm6\n" - " addps %%xmm0, %%xmm7\n" - //20: - " addps %%xmm1, %%xmm0\n" - " addps %%xmm2, %%xmm1\n" - " addps %%xmm3, %%xmm2\n" - " addps %%xmm4, %%xmm3\n" - " addps %%xmm5, %%xmm4\n" - " addps %%xmm6, %%xmm5\n" - " addps %%xmm7, %%xmm6\n" - " addps %%xmm0, %%xmm7\n" - //21: - " addps %%xmm1, %%xmm0\n" - " addps %%xmm2, %%xmm1\n" - " addps %%xmm3, %%xmm2\n" - " addps %%xmm4, %%xmm3\n" - " addps %%xmm5, %%xmm4\n" - " addps %%xmm6, %%xmm5\n" - " addps %%xmm7, %%xmm6\n" - " addps %%xmm0, %%xmm7\n" - //22: - " addps %%xmm1, %%xmm0\n" - " addps %%xmm2, %%xmm1\n" - " addps %%xmm3, %%xmm2\n" - " addps %%xmm4, %%xmm3\n" - " addps %%xmm5, %%xmm4\n" - " addps %%xmm6, %%xmm5\n" - " addps %%xmm7, %%xmm6\n" - " addps %%xmm0, %%xmm7\n" - //23: - " addps %%xmm1, %%xmm0\n" - " addps %%xmm2, %%xmm1\n" - " addps %%xmm3, %%xmm2\n" - " addps %%xmm4, %%xmm3\n" - " addps %%xmm5, %%xmm4\n" - " addps %%xmm6, %%xmm5\n" - " addps %%xmm7, %%xmm6\n" - " addps %%xmm0, %%xmm7\n" - //24: - " addps %%xmm1, %%xmm0\n" - " addps %%xmm2, %%xmm1\n" - " addps %%xmm3, %%xmm2\n" - " addps %%xmm4, %%xmm3\n" - " addps %%xmm5, %%xmm4\n" - " addps %%xmm6, %%xmm5\n" - " addps %%xmm7, %%xmm6\n" - " addps %%xmm0, %%xmm7\n" - //25: - " addps %%xmm1, %%xmm0\n" - " addps %%xmm2, %%xmm1\n" - " addps %%xmm3, %%xmm2\n" - " addps %%xmm4, %%xmm3\n" - " addps %%xmm5, %%xmm4\n" - " addps %%xmm6, %%xmm5\n" - " addps %%xmm7, %%xmm6\n" - " addps %%xmm0, %%xmm7\n" - //26: - " addps %%xmm1, %%xmm0\n" - " addps %%xmm2, %%xmm1\n" - " addps %%xmm3, %%xmm2\n" - " addps %%xmm4, %%xmm3\n" - " addps %%xmm5, %%xmm4\n" - " addps %%xmm6, %%xmm5\n" - " addps %%xmm7, %%xmm6\n" - " addps %%xmm0, %%xmm7\n" - //27: - " addps %%xmm1, %%xmm0\n" - " addps %%xmm2, %%xmm1\n" - " addps %%xmm3, %%xmm2\n" - " addps %%xmm4, %%xmm3\n" - " addps %%xmm5, %%xmm4\n" - " addps %%xmm6, %%xmm5\n" - " addps %%xmm7, %%xmm6\n" - " addps %%xmm0, %%xmm7\n" - //28: - " addps %%xmm1, %%xmm0\n" - " addps %%xmm2, %%xmm1\n" - " addps %%xmm3, %%xmm2\n" - " addps %%xmm4, %%xmm3\n" - " addps %%xmm5, %%xmm4\n" - " addps %%xmm6, %%xmm5\n" - " addps %%xmm7, %%xmm6\n" - " addps %%xmm0, %%xmm7\n" - //29: - " addps %%xmm1, %%xmm0\n" - " addps %%xmm2, %%xmm1\n" - " addps %%xmm3, %%xmm2\n" - " addps %%xmm4, %%xmm3\n" - " addps %%xmm5, %%xmm4\n" - " addps %%xmm6, %%xmm5\n" - " addps %%xmm7, %%xmm6\n" - " addps %%xmm0, %%xmm7\n" - //30: - " addps %%xmm1, %%xmm0\n" - " addps %%xmm2, %%xmm1\n" - " addps %%xmm3, %%xmm2\n" - " addps %%xmm4, %%xmm3\n" - " addps %%xmm5, %%xmm4\n" - " addps %%xmm6, %%xmm5\n" - " addps %%xmm7, %%xmm6\n" - " addps %%xmm0, %%xmm7\n" - //31: - " addps %%xmm1, %%xmm0\n" - " addps %%xmm2, %%xmm1\n" - " addps %%xmm3, %%xmm2\n" - " addps %%xmm4, %%xmm3\n" - " addps %%xmm5, %%xmm4\n" - " addps %%xmm6, %%xmm5\n" - " addps %%xmm7, %%xmm6\n" - " addps %%xmm0, %%xmm7\n" - - " dec %%eax\n" - /* "jnz .bsLoop\n" */ - " jnz 1b\n" - ::"a"(cycles) - ); -#else -# ifdef COMPILER_MICROSOFT - __asm { - mov eax, cycles - xorps xmm0, xmm0 - xorps xmm1, xmm1 - xorps xmm2, xmm2 - xorps xmm3, xmm3 - xorps xmm4, xmm4 - xorps xmm5, xmm5 - xorps xmm6, xmm6 - xorps xmm7, xmm7 - //-- - align 16 -bsLoop: - // 0: - addps xmm0, xmm1 - addps xmm1, xmm2 - addps xmm2, xmm3 - addps xmm3, xmm4 - addps xmm4, xmm5 - addps xmm5, xmm6 - addps xmm6, xmm7 - addps xmm7, xmm0 - // 1: - addps xmm0, xmm1 - addps xmm1, xmm2 - addps xmm2, xmm3 - addps xmm3, xmm4 - addps xmm4, xmm5 - addps xmm5, xmm6 - addps xmm6, xmm7 - addps xmm7, xmm0 - // 2: - addps xmm0, xmm1 - addps xmm1, xmm2 - addps xmm2, xmm3 - addps xmm3, xmm4 - addps xmm4, xmm5 - addps xmm5, xmm6 - addps xmm6, xmm7 - addps xmm7, xmm0 - // 3: - addps xmm0, xmm1 - addps xmm1, xmm2 - addps xmm2, xmm3 - addps xmm3, xmm4 - addps xmm4, xmm5 - addps xmm5, xmm6 - addps xmm6, xmm7 - addps xmm7, xmm0 - // 4: - addps xmm0, xmm1 - addps xmm1, xmm2 - addps xmm2, xmm3 - addps xmm3, xmm4 - addps xmm4, xmm5 - addps xmm5, xmm6 - addps xmm6, xmm7 - addps xmm7, xmm0 - // 5: - addps xmm0, xmm1 - addps xmm1, xmm2 - addps xmm2, xmm3 - addps xmm3, xmm4 - addps xmm4, xmm5 - addps xmm5, xmm6 - addps xmm6, xmm7 - addps xmm7, xmm0 - // 6: - addps xmm0, xmm1 - addps xmm1, xmm2 - addps xmm2, xmm3 - addps xmm3, xmm4 - addps xmm4, xmm5 - addps xmm5, xmm6 - addps xmm6, xmm7 - addps xmm7, xmm0 - // 7: - addps xmm0, xmm1 - addps xmm1, xmm2 - addps xmm2, xmm3 - addps xmm3, xmm4 - addps xmm4, xmm5 - addps xmm5, xmm6 - addps xmm6, xmm7 - addps xmm7, xmm0 - // 8: - addps xmm0, xmm1 - addps xmm1, xmm2 - addps xmm2, xmm3 - addps xmm3, xmm4 - addps xmm4, xmm5 - addps xmm5, xmm6 - addps xmm6, xmm7 - addps xmm7, xmm0 - // 9: - addps xmm0, xmm1 - addps xmm1, xmm2 - addps xmm2, xmm3 - addps xmm3, xmm4 - addps xmm4, xmm5 - addps xmm5, xmm6 - addps xmm6, xmm7 - addps xmm7, xmm0 - // 10: - addps xmm0, xmm1 - addps xmm1, xmm2 - addps xmm2, xmm3 - addps xmm3, xmm4 - addps xmm4, xmm5 - addps xmm5, xmm6 - addps xmm6, xmm7 - addps xmm7, xmm0 - // 11: - addps xmm0, xmm1 - addps xmm1, xmm2 - addps xmm2, xmm3 - addps xmm3, xmm4 - addps xmm4, xmm5 - addps xmm5, xmm6 - addps xmm6, xmm7 - addps xmm7, xmm0 - // 12: - addps xmm0, xmm1 - addps xmm1, xmm2 - addps xmm2, xmm3 - addps xmm3, xmm4 - addps xmm4, xmm5 - addps xmm5, xmm6 - addps xmm6, xmm7 - addps xmm7, xmm0 - // 13: - addps xmm0, xmm1 - addps xmm1, xmm2 - addps xmm2, xmm3 - addps xmm3, xmm4 - addps xmm4, xmm5 - addps xmm5, xmm6 - addps xmm6, xmm7 - addps xmm7, xmm0 - // 14: - addps xmm0, xmm1 - addps xmm1, xmm2 - addps xmm2, xmm3 - addps xmm3, xmm4 - addps xmm4, xmm5 - addps xmm5, xmm6 - addps xmm6, xmm7 - addps xmm7, xmm0 - // 15: - addps xmm0, xmm1 - addps xmm1, xmm2 - addps xmm2, xmm3 - addps xmm3, xmm4 - addps xmm4, xmm5 - addps xmm5, xmm6 - addps xmm6, xmm7 - addps xmm7, xmm0 - // 16: - addps xmm0, xmm1 - addps xmm1, xmm2 - addps xmm2, xmm3 - addps xmm3, xmm4 - addps xmm4, xmm5 - addps xmm5, xmm6 - addps xmm6, xmm7 - addps xmm7, xmm0 - // 17: - addps xmm0, xmm1 - addps xmm1, xmm2 - addps xmm2, xmm3 - addps xmm3, xmm4 - addps xmm4, xmm5 - addps xmm5, xmm6 - addps xmm6, xmm7 - addps xmm7, xmm0 - // 18: - addps xmm0, xmm1 - addps xmm1, xmm2 - addps xmm2, xmm3 - addps xmm3, xmm4 - addps xmm4, xmm5 - addps xmm5, xmm6 - addps xmm6, xmm7 - addps xmm7, xmm0 - // 19: - addps xmm0, xmm1 - addps xmm1, xmm2 - addps xmm2, xmm3 - addps xmm3, xmm4 - addps xmm4, xmm5 - addps xmm5, xmm6 - addps xmm6, xmm7 - addps xmm7, xmm0 - // 20: - addps xmm0, xmm1 - addps xmm1, xmm2 - addps xmm2, xmm3 - addps xmm3, xmm4 - addps xmm4, xmm5 - addps xmm5, xmm6 - addps xmm6, xmm7 - addps xmm7, xmm0 - // 21: - addps xmm0, xmm1 - addps xmm1, xmm2 - addps xmm2, xmm3 - addps xmm3, xmm4 - addps xmm4, xmm5 - addps xmm5, xmm6 - addps xmm6, xmm7 - addps xmm7, xmm0 - // 22: - addps xmm0, xmm1 - addps xmm1, xmm2 - addps xmm2, xmm3 - addps xmm3, xmm4 - addps xmm4, xmm5 - addps xmm5, xmm6 - addps xmm6, xmm7 - addps xmm7, xmm0 - // 23: - addps xmm0, xmm1 - addps xmm1, xmm2 - addps xmm2, xmm3 - addps xmm3, xmm4 - addps xmm4, xmm5 - addps xmm5, xmm6 - addps xmm6, xmm7 - addps xmm7, xmm0 - // 24: - addps xmm0, xmm1 - addps xmm1, xmm2 - addps xmm2, xmm3 - addps xmm3, xmm4 - addps xmm4, xmm5 - addps xmm5, xmm6 - addps xmm6, xmm7 - addps xmm7, xmm0 - // 25: - addps xmm0, xmm1 - addps xmm1, xmm2 - addps xmm2, xmm3 - addps xmm3, xmm4 - addps xmm4, xmm5 - addps xmm5, xmm6 - addps xmm6, xmm7 - addps xmm7, xmm0 - // 26: - addps xmm0, xmm1 - addps xmm1, xmm2 - addps xmm2, xmm3 - addps xmm3, xmm4 - addps xmm4, xmm5 - addps xmm5, xmm6 - addps xmm6, xmm7 - addps xmm7, xmm0 - // 27: - addps xmm0, xmm1 - addps xmm1, xmm2 - addps xmm2, xmm3 - addps xmm3, xmm4 - addps xmm4, xmm5 - addps xmm5, xmm6 - addps xmm6, xmm7 - addps xmm7, xmm0 - // 28: - addps xmm0, xmm1 - addps xmm1, xmm2 - addps xmm2, xmm3 - addps xmm3, xmm4 - addps xmm4, xmm5 - addps xmm5, xmm6 - addps xmm6, xmm7 - addps xmm7, xmm0 - // 29: - addps xmm0, xmm1 - addps xmm1, xmm2 - addps xmm2, xmm3 - addps xmm3, xmm4 - addps xmm4, xmm5 - addps xmm5, xmm6 - addps xmm6, xmm7 - addps xmm7, xmm0 - // 30: - addps xmm0, xmm1 - addps xmm1, xmm2 - addps xmm2, xmm3 - addps xmm3, xmm4 - addps xmm4, xmm5 - addps xmm5, xmm6 - addps xmm6, xmm7 - addps xmm7, xmm0 - // 31: - addps xmm0, xmm1 - addps xmm1, xmm2 - addps xmm2, xmm3 - addps xmm3, xmm4 - addps xmm4, xmm5 - addps xmm5, xmm6 - addps xmm6, xmm7 - addps xmm7, xmm0 - //---------------------- - dec eax - jnz bsLoop - } -# else -# error "Unsupported compiler" -# endif /* COMPILER_MICROSOFT */ -#endif /* COMPILER_GCC */ -} -#endif /* INLINE_ASSEMBLY_SUPPORTED */ +/* + * Copyright 2008 Veselin Georgiev, + * anrieffNOSPAM @ mgail_DOT.com (convert to gmail) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "libcpuid.h" +#include "asm-bits.h" + +int cpuid_exists_by_eflags(void) +{ +#if defined(PLATFORM_X64) + return 1; /* CPUID is always present on the x86_64 */ +#elif defined(PLATFORM_X86) +# if defined(COMPILER_GCC) || defined(COMPILER_CLANG) + int result; + __asm __volatile( + " pushfl\n" + " pop %%eax\n" + " mov %%eax, %%ecx\n" + " xor $0x200000, %%eax\n" + " push %%eax\n" + " popfl\n" + " pushfl\n" + " pop %%eax\n" + " xor %%ecx, %%eax\n" + " mov %%eax, %0\n" + " push %%ecx\n" + " popfl\n" + : "=m"(result) + : :"eax", "ecx", "memory"); + return (result != 0); +# elif defined(COMPILER_MICROSOFT) + int result; + __asm { + pushfd + pop eax + mov ecx, eax + xor eax, 0x200000 + push eax + popfd + pushfd + pop eax + xor eax, ecx + mov result, eax + push ecx + popfd + }; + return (result != 0); +# else + return 0; +# endif /* COMPILER_MICROSOFT */ +#elif defined(PLATFORM_ARM) + return 0; +#else + return 0; +#endif /* PLATFORM_X86 */ +} + +#ifdef INLINE_ASM_SUPPORTED +/* + * with MSVC/AMD64, the exec_cpuid() and cpu_rdtsc() functions + * are implemented in separate .asm files. Otherwise, use inline assembly + */ +void exec_cpuid(uint32_t *regs) +{ +# if defined(COMPILER_GCC) || defined(COMPILER_CLANG) +# ifdef PLATFORM_X64 + __asm __volatile( + " mov %0, %%rdi\n" + + " push %%rbx\n" + " push %%rcx\n" + " push %%rdx\n" + + " mov (%%rdi), %%eax\n" + " mov 4(%%rdi), %%ebx\n" + " mov 8(%%rdi), %%ecx\n" + " mov 12(%%rdi), %%edx\n" + + " cpuid\n" + + " movl %%eax, (%%rdi)\n" + " movl %%ebx, 4(%%rdi)\n" + " movl %%ecx, 8(%%rdi)\n" + " movl %%edx, 12(%%rdi)\n" + " pop %%rdx\n" + " pop %%rcx\n" + " pop %%rbx\n" + : + :"m"(regs) + :"memory", "eax", "rdi" + ); +# elif defined(PLATFORM_X86) + __asm __volatile( + " mov %0, %%edi\n" + + " push %%ebx\n" + " push %%ecx\n" + " push %%edx\n" + + " mov (%%edi), %%eax\n" + " mov 4(%%edi), %%ebx\n" + " mov 8(%%edi), %%ecx\n" + " mov 12(%%edi), %%edx\n" + + " cpuid\n" + + " mov %%eax, (%%edi)\n" + " mov %%ebx, 4(%%edi)\n" + " mov %%ecx, 8(%%edi)\n" + " mov %%edx, 12(%%edi)\n" + " pop %%edx\n" + " pop %%ecx\n" + " pop %%ebx\n" + : + :"m"(regs) + :"memory", "eax", "edi" + ); +# elif defined(PLATFORM_ARM) +# endif /* COMPILER_GCC */ +#else +# ifdef COMPILER_MICROSOFT + __asm { + push ebx + push ecx + push edx + push edi + mov edi, regs + + mov eax, [edi] + mov ebx, [edi+4] + mov ecx, [edi+8] + mov edx, [edi+12] + + cpuid + + mov [edi], eax + mov [edi+4], ebx + mov [edi+8], ecx + mov [edi+12], edx + + pop edi + pop edx + pop ecx + pop ebx + } +# else +# error "Unsupported compiler" +# endif /* COMPILER_MICROSOFT */ +#endif +} +#endif /* INLINE_ASSEMBLY_SUPPORTED */ + +#ifdef INLINE_ASM_SUPPORTED +void cpu_rdtsc(uint64_t* result) +{ + uint32_t low_part, hi_part; +#if defined(COMPILER_GCC) || defined(COMPILER_CLANG) +#ifdef PLATFORM_ARM + low_part = 0; + hi_part = 0; +#else + __asm __volatile ( + " rdtsc\n" + " mov %%eax, %0\n" + " mov %%edx, %1\n" + :"=m"(low_part), "=m"(hi_part)::"memory", "eax", "edx" + ); +#endif +#else +# ifdef COMPILER_MICROSOFT + __asm { + rdtsc + mov low_part, eax + mov hi_part, edx + }; +# else +# error "Unsupported compiler" +# endif /* COMPILER_MICROSOFT */ +#endif /* COMPILER_GCC */ + *result = (uint64_t)low_part + (((uint64_t) hi_part) << 32); +} +#endif /* INLINE_ASM_SUPPORTED */ + +#ifdef INLINE_ASM_SUPPORTED +void busy_sse_loop(int cycles) +{ +# if defined(COMPILER_GCC) || defined(COMPILER_CLANG) +#ifndef __APPLE__ +# define XALIGN ".balign 16\n" +#else +# define XALIGN ".align 4\n" +#endif +#ifdef PLATFORM_ARM +#else + __asm __volatile ( + " xorps %%xmm0, %%xmm0\n" + " xorps %%xmm1, %%xmm1\n" + " xorps %%xmm2, %%xmm2\n" + " xorps %%xmm3, %%xmm3\n" + " xorps %%xmm4, %%xmm4\n" + " xorps %%xmm5, %%xmm5\n" + " xorps %%xmm6, %%xmm6\n" + " xorps %%xmm7, %%xmm7\n" + XALIGN + /* ".bsLoop:\n" */ + "1:\n" + // 0: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + // 1: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + // 2: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + // 3: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + // 4: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + // 5: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + // 6: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + // 7: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + // 8: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + // 9: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + //10: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + //11: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + //12: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + //13: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + //14: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + //15: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + //16: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + //17: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + //18: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + //19: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + //20: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + //21: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + //22: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + //23: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + //24: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + //25: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + //26: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + //27: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + //28: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + //29: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + //30: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + //31: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + + " dec %%eax\n" + /* "jnz .bsLoop\n" */ + " jnz 1b\n" + ::"a"(cycles) + ); +#endif +#else +# ifdef COMPILER_MICROSOFT + __asm { + mov eax, cycles + xorps xmm0, xmm0 + xorps xmm1, xmm1 + xorps xmm2, xmm2 + xorps xmm3, xmm3 + xorps xmm4, xmm4 + xorps xmm5, xmm5 + xorps xmm6, xmm6 + xorps xmm7, xmm7 + //-- + align 16 +bsLoop: + // 0: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 1: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 2: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 3: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 4: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 5: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 6: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 7: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 8: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 9: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 10: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 11: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 12: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 13: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 14: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 15: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 16: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 17: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 18: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 19: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 20: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 21: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 22: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 23: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 24: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 25: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 26: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 27: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 28: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 29: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 30: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 31: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + //---------------------- + dec eax + jnz bsLoop + } +# else +# error "Unsupported compiler" +# endif /* COMPILER_MICROSOFT */ +#endif /* COMPILER_GCC */ +} +#endif /* INLINE_ASSEMBLY_SUPPORTED */ \ No newline at end of file diff --git a/src/3rdparty/libcpuid/asm-bits.h b/src/3rdparty/libcpuid/asm-bits.h index 3a03e11c..9049e2fe 100644 --- a/src/3rdparty/libcpuid/asm-bits.h +++ b/src/3rdparty/libcpuid/asm-bits.h @@ -1,53 +1,71 @@ -/* - * Copyright 2008 Veselin Georgiev, - * anrieffNOSPAM @ mgail_DOT.com (convert to gmail) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#ifndef __ASM_BITS_H__ -#define __ASM_BITS_H__ -#include "libcpuid.h" - -/* Determine Compiler: */ -#if defined(_MSC_VER) -# define COMPILER_MICROSOFT -#elif defined(__GNUC__) -# define COMPILER_GCC -#endif - -/* Determine Platform */ -#if defined(__x86_64__) || defined(_M_AMD64) -# define PLATFORM_X64 -#elif defined(__i386__) || defined(_M_IX86) -# define PLATFORM_X86 -#endif - -/* Under Windows/AMD64 with MSVC, inline assembly isn't supported */ -#if (defined(COMPILER_GCC) && defined(PLATFORM_X64)) || defined(PLATFORM_X86) -# define INLINE_ASM_SUPPORTED -#endif - -int cpuid_exists_by_eflags(void); -void exec_cpuid(uint32_t *regs); -void busy_sse_loop(int cycles); - -#endif /* __ASM_BITS_H__ */ +/* + * Copyright 2008 Veselin Georgiev, + * anrieffNOSPAM @ mgail_DOT.com (convert to gmail) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __ASM_BITS_H__ +#define __ASM_BITS_H__ +#include "libcpuid.h" + +/* Determine Compiler: */ +#if defined(_MSC_VER) +#if !defined(COMPILER_MICROSOFT) +# define COMPILER_MICROSOFT +#endif +#elif defined(__GNUC__) +#if !defined(COMPILER_GCC) +# define COMPILER_GCC +#endif +#elif defined(__clang__) +#if !defined(COMPILER_CLANG) +# define COMPILER_CLANG +#endif +#endif + +/* Determine Platform */ +#if defined(__x86_64__) || defined(_M_AMD64) +#if !defined(PLATFORM_X64) +# define PLATFORM_X64 +#endif +#elif defined(__i386__) || defined(_M_IX86) +#if !defined(PLATFORM_X86) +# define PLATFORM_X86 +#endif +#elif defined(__ARMEL__) +#if !defined(PLATFORM_ARM) +# define PLATFORM_ARM +#endif +#endif + +/* Under Windows/AMD64 with MSVC, inline assembly isn't supported */ +#if (((defined(COMPILER_GCC) || defined(COMPILER_CLANG))) && \ + (defined(PLATFORM_X64) || defined(PLATFORM_X86) || defined(PLATFORM_ARM))) || \ + (defined(COMPILER_MICROSOFT) && defined(PLATFORM_X86)) +# define INLINE_ASM_SUPPORTED +#endif + +int cpuid_exists_by_eflags(void); +void exec_cpuid(uint32_t *regs); +void busy_sse_loop(int cycles); + +#endif /* __ASM_BITS_H__ */ diff --git a/src/3rdparty/libcpuid/cpuid_main.c b/src/3rdparty/libcpuid/cpuid_main.c index f22c7dd6..61cb638d 100644 --- a/src/3rdparty/libcpuid/cpuid_main.c +++ b/src/3rdparty/libcpuid/cpuid_main.c @@ -221,42 +221,42 @@ static void load_features_common(struct cpu_raw_data_t* raw, struct cpu_id_t* da static cpu_vendor_t cpuid_vendor_identify(const uint32_t *raw_vendor, char *vendor_str) { - int i; - cpu_vendor_t vendor = VENDOR_UNKNOWN; - const struct { cpu_vendor_t vendor; char match[16]; } - matchtable[NUM_CPU_VENDORS] = { - /* source: http://www.sandpile.org/ia32/cpuid.htm */ - { VENDOR_INTEL , "GenuineIntel" }, - { VENDOR_AMD , "AuthenticAMD" }, - { VENDOR_CYRIX , "CyrixInstead" }, - { VENDOR_NEXGEN , "NexGenDriven" }, - { VENDOR_TRANSMETA , "GenuineTMx86" }, - { VENDOR_UMC , "UMC UMC UMC " }, - { VENDOR_CENTAUR , "CentaurHauls" }, - { VENDOR_RISE , "RiseRiseRise" }, - { VENDOR_SIS , "SiS SiS SiS " }, - { VENDOR_NSC , "Geode by NSC" }, - }; + int i; + cpu_vendor_t vendor = VENDOR_UNKNOWN; + const struct { cpu_vendor_t vendor; char match[16]; } + matchtable[NUM_CPU_VENDORS] = { + /* source: http://www.sandpile.org/ia32/cpuid.htm */ + { VENDOR_INTEL , "GenuineIntel" }, + { VENDOR_AMD , "AuthenticAMD" }, + { VENDOR_CYRIX , "CyrixInstead" }, + { VENDOR_NEXGEN , "NexGenDriven" }, + { VENDOR_TRANSMETA , "GenuineTMx86" }, + { VENDOR_UMC , "UMC UMC UMC " }, + { VENDOR_CENTAUR , "CentaurHauls" }, + { VENDOR_RISE , "RiseRiseRise" }, + { VENDOR_SIS , "SiS SiS SiS " }, + { VENDOR_NSC , "Geode by NSC" }, + }; - memcpy(vendor_str + 0, &raw_vendor[1], 4); - memcpy(vendor_str + 4, &raw_vendor[3], 4); - memcpy(vendor_str + 8, &raw_vendor[2], 4); - vendor_str[12] = 0; + memcpy(vendor_str + 0, &raw_vendor[1], 4); + memcpy(vendor_str + 4, &raw_vendor[3], 4); + memcpy(vendor_str + 8, &raw_vendor[2], 4); + vendor_str[12] = 0; - /* Determine vendor: */ - for (i = 0; i < NUM_CPU_VENDORS; i++) - if (!strcmp(vendor_str, matchtable[i].match)) { - vendor = matchtable[i].vendor; - break; - } - return vendor; + /* Determine vendor: */ + for (i = 0; i < NUM_CPU_VENDORS; i++) + if (!strcmp(vendor_str, matchtable[i].match)) { + vendor = matchtable[i].vendor; + break; + } + return vendor; } static int cpuid_basic_identify(struct cpu_raw_data_t* raw, struct cpu_id_t* data) { int i, j, basic, xmodel, xfamily, ext; char brandstr[64] = {0}; - data->vendor = cpuid_vendor_identify(raw->basic_cpuid[0], data->vendor_str); + data->vendor = cpuid_vendor_identify(raw->basic_cpuid[0], data->vendor_str); if (data->vendor == VENDOR_UNKNOWN) return set_error(ERR_CPU_UNKN); diff --git a/src/3rdparty/libcpuid/libcpuid.h b/src/3rdparty/libcpuid/libcpuid.h index c44990c3..847e5a4a 100644 --- a/src/3rdparty/libcpuid/libcpuid.h +++ b/src/3rdparty/libcpuid/libcpuid.h @@ -60,7 +60,7 @@ */ /** @mainpage A simple libcpuid introduction - * + * * LibCPUID provides CPU identification and access to the CPUID and RDTSC * instructions on the x86. *

@@ -82,6 +82,7 @@ */ /** @defgroup libcpuid LibCPUID + * @brief LibCPUID provides CPU identification @{ */ /* Include some integer type specifications: */ @@ -535,23 +536,23 @@ typedef enum { * @brief Describes common library error codes */ typedef enum { - ERR_OK = 0, /*!< "No error" */ - ERR_NO_CPUID = -1, /*!< "CPUID instruction is not supported" */ - ERR_NO_RDTSC = -2, /*!< "RDTSC instruction is not supported" */ - ERR_NO_MEM = -3, /*!< "Memory allocation failed" */ - ERR_OPEN = -4, /*!< "File open operation failed" */ - ERR_BADFMT = -5, /*!< "Bad file format" */ - ERR_NOT_IMP = -6, /*!< "Not implemented" */ - ERR_CPU_UNKN = -7, /*!< "Unsupported processor" */ - ERR_NO_RDMSR = -8, /*!< "RDMSR instruction is not supported" */ - ERR_NO_DRIVER= -9, /*!< "RDMSR driver error (generic)" */ - ERR_NO_PERMS = -10, /*!< "No permissions to install RDMSR driver" */ - ERR_EXTRACT = -11, /*!< "Cannot extract RDMSR driver (read only media?)" */ - ERR_HANDLE = -12, /*!< "Bad handle" */ - ERR_INVMSR = -13, /*!< "Invalid MSR" */ - ERR_INVCNB = -14, /*!< "Invalid core number" */ - ERR_HANDLE_R = -15, /*!< "Error on handle read" */ - ERR_INVRANGE = -16, /*!< "Invalid given range" */ + ERR_OK = 0, /*!< No error */ + ERR_NO_CPUID = -1, /*!< CPUID instruction is not supported */ + ERR_NO_RDTSC = -2, /*!< RDTSC instruction is not supported */ + ERR_NO_MEM = -3, /*!< Memory allocation failed */ + ERR_OPEN = -4, /*!< File open operation failed */ + ERR_BADFMT = -5, /*!< Bad file format */ + ERR_NOT_IMP = -6, /*!< Not implemented */ + ERR_CPU_UNKN = -7, /*!< Unsupported processor */ + ERR_NO_RDMSR = -8, /*!< RDMSR instruction is not supported */ + ERR_NO_DRIVER= -9, /*!< RDMSR driver error (generic) */ + ERR_NO_PERMS = -10, /*!< No permissions to install RDMSR driver */ + ERR_EXTRACT = -11, /*!< Cannot extract RDMSR driver (read only media?) */ + ERR_HANDLE = -12, /*!< Bad handle */ + ERR_INVMSR = -13, /*!< Invalid MSR */ + ERR_INVCNB = -14, /*!< Invalid core number */ + ERR_HANDLE_R = -15, /*!< Error on handle read */ + ERR_INVRANGE = -16, /*!< Invalid given range */ } cpu_error_t; /** @@ -668,7 +669,7 @@ struct cpu_epc_t cpuid_get_epc(int index, const struct cpu_raw_data_t* raw); const char* cpuid_lib_version(void); #ifdef __cplusplus -}; /* extern "C" */ +} /* extern "C" */ #endif diff --git a/src/3rdparty/libcpuid/libcpuid_internal.h b/src/3rdparty/libcpuid/libcpuid_internal.h index 038aa209..64804616 100644 --- a/src/3rdparty/libcpuid/libcpuid_internal.h +++ b/src/3rdparty/libcpuid/libcpuid_internal.h @@ -75,8 +75,9 @@ enum _intel_bits_t { _3 = LBIT( 14 ), _5 = LBIT( 15 ), _7 = LBIT( 16 ), - XEON_ = LBIT( 17 ), - ATOM_ = LBIT( 18 ), + _9 = LBIT( 17 ), + XEON_ = LBIT( 18 ), + ATOM_ = LBIT( 19 ), }; typedef enum _intel_bits_t intel_bits_t; diff --git a/src/3rdparty/libcpuid/libcpuid_types.h b/src/3rdparty/libcpuid/libcpuid_types.h index 9e897275..0e667aa6 100644 --- a/src/3rdparty/libcpuid/libcpuid_types.h +++ b/src/3rdparty/libcpuid/libcpuid_types.h @@ -32,6 +32,32 @@ #ifndef __LIBCPUID_TYPES_H__ #define __LIBCPUID_TYPES_H__ -#include +#if !defined(_MSC_VER) || _MSC_VER >= 1600 +# include +#else +/* we have to provide our own: */ +# if !defined(__int32_t_defined) +typedef int int32_t; +# endif + +# if !defined(__uint32_t_defined) +typedef unsigned uint32_t; +# endif + +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef signed short int16_t; +typedef unsigned short uint16_t; +#if (defined _MSC_VER) && (_MSC_VER <= 1300) + /* MSVC 6.0: no long longs ... */ + typedef signed __int64 int64_t; + typedef unsigned __int64 uint64_t; +#else + /* all other sane compilers: */ + typedef signed long long int64_t; + typedef unsigned long long uint64_t; +#endif + +#endif #endif /* __LIBCPUID_TYPES_H__ */ diff --git a/src/3rdparty/libcpuid/recog_amd.c b/src/3rdparty/libcpuid/recog_amd.c index 352d733b..4726f633 100644 --- a/src/3rdparty/libcpuid/recog_amd.c +++ b/src/3rdparty/libcpuid/recog_amd.c @@ -49,6 +49,10 @@ enum _amd_model_codes_t { _1400, _1500, _1600, + _1900, + _2400, + _2500, + _2700, }; static void load_amd_features(struct cpu_raw_data_t* raw, struct cpu_id_t* data) diff --git a/src/3rdparty/libcpuid/recog_intel.c b/src/3rdparty/libcpuid/recog_intel.c index 5467c19f..397d750e 100644 --- a/src/3rdparty/libcpuid/recog_intel.c +++ b/src/3rdparty/libcpuid/recog_intel.c @@ -376,7 +376,7 @@ static intel_code_and_bits_t get_brand_code_and_bits(struct cpu_id_t* data) bits |= bit_matchtable[i].bit; } - if ((i = match_pattern(bs, "Core(TM) [im][357]")) != 0) { + if ((i = match_pattern(bs, "Core(TM) [im][3579]")) != 0) { bits |= CORE_; i--; switch (bs[i + 9]) { @@ -387,6 +387,7 @@ static intel_code_and_bits_t get_brand_code_and_bits(struct cpu_id_t* data) case '3': bits |= _3; break; case '5': bits |= _5; break; case '7': bits |= _7; break; + case '9': bits |= _9; break; } } for (i = 0; i < COUNT_OF(matchtable); i++) diff --git a/src/3rdparty/rapidjson/allocators.h b/src/3rdparty/rapidjson/allocators.h index 98affe03..cc67c897 100644 --- a/src/3rdparty/rapidjson/allocators.h +++ b/src/3rdparty/rapidjson/allocators.h @@ -52,6 +52,19 @@ concept Allocator { \endcode */ + +/*! \def RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY + \ingroup RAPIDJSON_CONFIG + \brief User-defined kDefaultChunkCapacity definition. + + User can define this as any \c size that is a power of 2. +*/ + +#ifndef RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY +#define RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY (64 * 1024) +#endif + + /////////////////////////////////////////////////////////////////////////////// // CrtAllocator @@ -236,7 +249,7 @@ private: */ bool AddChunk(size_t capacity) { if (!baseAllocator_) - ownBaseAllocator_ = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator()); + ownBaseAllocator_ = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator)(); if (ChunkHeader* chunk = reinterpret_cast(baseAllocator_->Malloc(RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + capacity))) { chunk->capacity = capacity; chunk->size = 0; @@ -248,7 +261,7 @@ private: return false; } - static const int kDefaultChunkCapacity = 64 * 1024; //!< Default chunk capacity. + static const int kDefaultChunkCapacity = RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY; //!< Default chunk capacity. //! Chunk header for perpending to each chunk. /*! Chunks are stored as a singly linked list. diff --git a/src/3rdparty/rapidjson/cursorstreamwrapper.h b/src/3rdparty/rapidjson/cursorstreamwrapper.h new file mode 100644 index 00000000..52c11a7c --- /dev/null +++ b/src/3rdparty/rapidjson/cursorstreamwrapper.h @@ -0,0 +1,78 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_CURSORSTREAMWRAPPER_H_ +#define RAPIDJSON_CURSORSTREAMWRAPPER_H_ + +#include "stream.h" + +#if defined(__GNUC__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#if defined(_MSC_VER) && _MSC_VER <= 1800 +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4702) // unreachable code +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +RAPIDJSON_NAMESPACE_BEGIN + + +//! Cursor stream wrapper for counting line and column number if error exists. +/*! + \tparam InputStream Any stream that implements Stream Concept +*/ +template > +class CursorStreamWrapper : public GenericStreamWrapper { +public: + typedef typename Encoding::Ch Ch; + + CursorStreamWrapper(InputStream& is): + GenericStreamWrapper(is), line_(1), col_(0) {} + + // counting line and column number + Ch Take() { + Ch ch = this->is_.Take(); + if(ch == '\n') { + line_ ++; + col_ = 0; + } else { + col_ ++; + } + return ch; + } + + //! Get the error line number, if error exists. + size_t GetLine() const { return line_; } + //! Get the error column number, if error exists. + size_t GetColumn() const { return col_; } + +private: + size_t line_; //!< Current Line + size_t col_; //!< Current Column +}; + +#if defined(_MSC_VER) && _MSC_VER <= 1800 +RAPIDJSON_DIAG_POP +#endif + +#if defined(__GNUC__) +RAPIDJSON_DIAG_POP +#endif + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_CURSORSTREAMWRAPPER_H_ diff --git a/src/3rdparty/rapidjson/document.h b/src/3rdparty/rapidjson/document.h index e3e20dfb..9783fe4a 100644 --- a/src/3rdparty/rapidjson/document.h +++ b/src/3rdparty/rapidjson/document.h @@ -26,26 +26,21 @@ #include RAPIDJSON_DIAG_PUSH -#ifdef _MSC_VER -RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant -RAPIDJSON_DIAG_OFF(4244) // conversion from kXxxFlags to 'uint16_t', possible loss of data -#endif - #ifdef __clang__ RAPIDJSON_DIAG_OFF(padded) RAPIDJSON_DIAG_OFF(switch-enum) RAPIDJSON_DIAG_OFF(c++98-compat) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +RAPIDJSON_DIAG_OFF(4244) // conversion from kXxxFlags to 'uint16_t', possible loss of data #endif #ifdef __GNUC__ RAPIDJSON_DIAG_OFF(effc++) -#if __GNUC__ >= 6 -RAPIDJSON_DIAG_OFF(terminate) // ignore throwing RAPIDJSON_ASSERT in RAPIDJSON_NOEXCEPT functions -#endif #endif // __GNUC__ #ifndef RAPIDJSON_NOMEMBERITERATORCLASS -#include // std::iterator, std::random_access_iterator_tag +#include // std::random_access_iterator_tag #endif #if RAPIDJSON_HAS_CXX11_RVALUE_REFS @@ -71,6 +66,12 @@ template struct GenericMember { GenericValue name; //!< name of member (must be a string) GenericValue value; //!< value of member. + + // swap() for std::sort() and other potential use in STL. + friend inline void swap(GenericMember& a, GenericMember& b) RAPIDJSON_NOEXCEPT { + a.name.Swap(b.name); + a.value.Swap(b.value); + } }; /////////////////////////////////////////////////////////////////////////////// @@ -98,16 +99,13 @@ struct GenericMember { \see GenericMember, GenericValue::MemberIterator, GenericValue::ConstMemberIterator */ template -class GenericMemberIterator - : public std::iterator >::Type> { +class GenericMemberIterator { friend class GenericValue; template friend class GenericMemberIterator; typedef GenericMember PlainType; typedef typename internal::MaybeAddConst::Type ValueType; - typedef std::iterator BaseType; public: //! Iterator type itself @@ -117,12 +115,21 @@ public: //! Non-constant iterator type typedef GenericMemberIterator NonConstIterator; + /** \name std::iterator_traits support */ + //@{ + typedef ValueType value_type; + typedef ValueType * pointer; + typedef ValueType & reference; + typedef std::ptrdiff_t difference_type; + typedef std::random_access_iterator_tag iterator_category; + //@} + //! Pointer to (const) GenericMember - typedef typename BaseType::pointer Pointer; + typedef pointer Pointer; //! Reference to (const) GenericMember - typedef typename BaseType::reference Reference; + typedef reference Reference; //! Signed integer type (e.g. \c ptrdiff_t) - typedef typename BaseType::difference_type DifferenceType; + typedef difference_type DifferenceType; //! Default constructor (singular value) /*! Creates an iterator pointing to no element. @@ -198,17 +205,17 @@ private: // class-based member iterator implementation disabled, use plain pointers template -struct GenericMemberIterator; +class GenericMemberIterator; //! non-const GenericMemberIterator template -struct GenericMemberIterator { +class GenericMemberIterator { //! use plain pointer as iterator type typedef GenericMember* Iterator; }; //! const GenericMemberIterator template -struct GenericMemberIterator { +class GenericMemberIterator { //! use plain const pointer as iterator type typedef const GenericMember* Iterator; }; @@ -300,7 +307,7 @@ struct GenericStringRef { */ #endif explicit GenericStringRef(const CharType* str) - : s(str), length(internal::StrLen(str)){ RAPIDJSON_ASSERT(s != 0); } + : s(str), length(NotNullStrLen(str)) {} //! Create constant string reference from pointer and length #ifndef __clang__ // -Wdocumentation @@ -312,12 +319,10 @@ struct GenericStringRef { */ #endif GenericStringRef(const CharType* str, SizeType len) - : s(str), length(len) { RAPIDJSON_ASSERT(s != 0); } + : s(RAPIDJSON_LIKELY(str) ? str : emptyString), length(len) { RAPIDJSON_ASSERT(str != 0 || len == 0u); } GenericStringRef(const GenericStringRef& rhs) : s(rhs.s), length(rhs.length) {} - GenericStringRef& operator=(const GenericStringRef& rhs) { s = rhs.s; length = rhs.length; } - //! implicit conversion to plain CharType pointer operator const Ch *() const { return s; } @@ -325,11 +330,24 @@ struct GenericStringRef { const SizeType length; //!< length of the string (excluding the trailing NULL terminator) private: + SizeType NotNullStrLen(const CharType* str) { + RAPIDJSON_ASSERT(str != 0); + return internal::StrLen(str); + } + + /// Empty string - used when passing in a NULL pointer + static const Ch emptyString[]; + //! Disallow construction from non-const array template GenericStringRef(CharType (&str)[N]) /* = delete */; + //! Copy assignment operator not permitted - immutable type + GenericStringRef& operator=(const GenericStringRef& rhs) /* = delete */; }; +template +const CharType GenericStringRef::emptyString[] = { CharType() }; + //! Mark a character pointer as constant string /*! Mark a plain character pointer as a "string literal". This function can be used to avoid copying a character string to be referenced as a @@ -344,7 +362,7 @@ private: */ template inline GenericStringRef StringRef(const CharType* str) { - return GenericStringRef(str, internal::StrLen(str)); + return GenericStringRef(str); } //! Mark a character pointer as constant string @@ -434,6 +452,26 @@ struct TypeHelper { static ValueType& Set(ValueType& v, unsigned data, typename ValueType::AllocatorType&) { return v.SetUint(data); } }; +#ifdef _MSC_VER +RAPIDJSON_STATIC_ASSERT(sizeof(long) == sizeof(int)); +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsInt(); } + static long Get(const ValueType& v) { return v.GetInt(); } + static ValueType& Set(ValueType& v, long data) { return v.SetInt(data); } + static ValueType& Set(ValueType& v, long data, typename ValueType::AllocatorType&) { return v.SetInt(data); } +}; + +RAPIDJSON_STATIC_ASSERT(sizeof(unsigned long) == sizeof(unsigned)); +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsUint(); } + static unsigned long Get(const ValueType& v) { return v.GetUint(); } + static ValueType& Set(ValueType& v, unsigned long data) { return v.SetUint(data); } + static ValueType& Set(ValueType& v, unsigned long data, typename ValueType::AllocatorType&) { return v.SetUint(data); } +}; +#endif + template struct TypeHelper { static bool Is(const ValueType& v) { return v.IsInt64(); } @@ -507,7 +545,7 @@ struct TypeHelper { static bool Is(const ValueType& v) { return v.IsObject(); } static ObjectType Get(ValueType& v) { return v.GetObject(); } static ValueType& Set(ValueType& v, ObjectType data) { return v = data; } - static ValueType& Set(ValueType& v, ObjectType data, typename ValueType::AllocatorType&) { v = data; } + static ValueType& Set(ValueType& v, ObjectType data, typename ValueType::AllocatorType&) { return v = data; } }; template @@ -590,11 +628,11 @@ public: \note Default content for number is zero. */ explicit GenericValue(Type type) RAPIDJSON_NOEXCEPT : data_() { - static const uint16_t defaultFlags[7] = { + static const uint16_t defaultFlags[] = { kNullFlag, kFalseFlag, kTrueFlag, kObjectFlag, kArrayFlag, kShortStringFlag, kNumberAnyFlag }; - RAPIDJSON_ASSERT(type <= kNumberType); + RAPIDJSON_NOEXCEPT_ASSERT(type >= kNullType && type <= kNumberType); data_.f.flags = defaultFlags[type]; // Use ShortString to store empty string. @@ -607,10 +645,50 @@ public: \tparam SourceAllocator allocator of \c rhs \param rhs Value to copy from (read-only) \param allocator Allocator for allocating copied elements and buffers. Commonly use GenericDocument::GetAllocator(). + \param copyConstStrings Force copying of constant strings (e.g. referencing an in-situ buffer) \see CopyFrom() */ - template< typename SourceAllocator > - GenericValue(const GenericValue& rhs, Allocator & allocator); + template + GenericValue(const GenericValue& rhs, Allocator& allocator, bool copyConstStrings = false) { + switch (rhs.GetType()) { + case kObjectType: { + SizeType count = rhs.data_.o.size; + Member* lm = reinterpret_cast(allocator.Malloc(count * sizeof(Member))); + const typename GenericValue::Member* rm = rhs.GetMembersPointer(); + for (SizeType i = 0; i < count; i++) { + new (&lm[i].name) GenericValue(rm[i].name, allocator, copyConstStrings); + new (&lm[i].value) GenericValue(rm[i].value, allocator, copyConstStrings); + } + data_.f.flags = kObjectFlag; + data_.o.size = data_.o.capacity = count; + SetMembersPointer(lm); + } + break; + case kArrayType: { + SizeType count = rhs.data_.a.size; + GenericValue* le = reinterpret_cast(allocator.Malloc(count * sizeof(GenericValue))); + const GenericValue* re = rhs.GetElementsPointer(); + for (SizeType i = 0; i < count; i++) + new (&le[i]) GenericValue(re[i], allocator, copyConstStrings); + data_.f.flags = kArrayFlag; + data_.a.size = data_.a.capacity = count; + SetElementsPointer(le); + } + break; + case kStringType: + if (rhs.data_.f.flags == kConstStringFlag && !copyConstStrings) { + data_.f.flags = rhs.data_.f.flags; + data_ = *reinterpret_cast(&rhs.data_); + } + else + SetStringRaw(StringRef(rhs.GetString(), rhs.GetStringLength()), allocator); + break; + default: + data_.f.flags = rhs.data_.f.flags; + data_ = *reinterpret_cast(&rhs.data_); + break; + } + } //! Constructor for boolean value. /*! \param b Boolean value @@ -672,6 +750,9 @@ public: //! Constructor for double value. explicit GenericValue(double d) RAPIDJSON_NOEXCEPT : data_() { data_.n.d = d; data_.f.flags = kNumberDoubleFlag; } + //! Constructor for float value. + explicit GenericValue(float f) RAPIDJSON_NOEXCEPT : data_() { data_.n.d = static_cast(f); data_.f.flags = kNumberDoubleFlag; } + //! Constructor for constant string (i.e. do not make a copy of string) GenericValue(const Ch* s, SizeType length) RAPIDJSON_NOEXCEPT : data_() { SetStringRaw(StringRef(s, length)); } @@ -753,9 +834,10 @@ public: /*! \param rhs Source of the assignment. It will become a null value after assignment. */ GenericValue& operator=(GenericValue& rhs) RAPIDJSON_NOEXCEPT { - RAPIDJSON_ASSERT(this != &rhs); - this->~GenericValue(); - RawAssign(rhs); + if (RAPIDJSON_LIKELY(this != &rhs)) { + this->~GenericValue(); + RawAssign(rhs); + } return *this; } @@ -800,12 +882,13 @@ public: \tparam SourceAllocator Allocator type of \c rhs \param rhs Value to copy from (read-only) \param allocator Allocator to use for copying + \param copyConstStrings Force copying of constant strings (e.g. referencing an in-situ buffer) */ template - GenericValue& CopyFrom(const GenericValue& rhs, Allocator& allocator) { + GenericValue& CopyFrom(const GenericValue& rhs, Allocator& allocator, bool copyConstStrings = false) { RAPIDJSON_ASSERT(static_cast(this) != static_cast(&rhs)); this->~GenericValue(); - new (this) GenericValue(rhs, allocator); + new (this) GenericValue(rhs, allocator, copyConstStrings); return *this; } @@ -846,7 +929,7 @@ public: //! Equal-to operator /*! \note If an object contains duplicated named member, comparing equality with any object is always \c false. - \note Linear time complexity (number of all values in the subtree and total lengths of all strings). + \note Complexity is quadratic in Object's member number and linear for the rest (number of all values in the subtree and total lengths of all strings). */ template bool operator==(const GenericValue& rhs) const { @@ -955,14 +1038,14 @@ public: uint64_t u = GetUint64(); volatile double d = static_cast(u); return (d >= 0.0) - && (d < static_cast(std::numeric_limits::max())) + && (d < static_cast((std::numeric_limits::max)())) && (u == static_cast(d)); } if (IsInt64()) { int64_t i = GetInt64(); volatile double d = static_cast(i); - return (d >= static_cast(std::numeric_limits::min())) - && (d < static_cast(std::numeric_limits::max())) + return (d >= static_cast((std::numeric_limits::min)())) + && (d < static_cast((std::numeric_limits::max)())) && (i == static_cast(d)); } return true; // double, int, uint are always lossless @@ -979,8 +1062,8 @@ public: bool IsLosslessFloat() const { if (!IsNumber()) return false; double a = GetDouble(); - if (a < static_cast(-std::numeric_limits::max()) - || a > static_cast(std::numeric_limits::max())) + if (a < static_cast(-(std::numeric_limits::max)()) + || a > static_cast((std::numeric_limits::max)())) return false; double b = static_cast(static_cast(a)); return a >= b && a <= b; // Prevent -Wfloat-equal @@ -1015,6 +1098,9 @@ public: //! Get the number of members in the object. SizeType MemberCount() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size; } + //! Get the capacity of object. + SizeType MemberCapacity() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.capacity; } + //! Check whether the object is empty. bool ObjectEmpty() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size == 0; } @@ -1083,6 +1169,21 @@ public: /*! \pre IsObject() == true */ MemberIterator MemberEnd() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(GetMembersPointer() + data_.o.size); } + //! Request the object to have enough capacity to store members. + /*! \param newCapacity The capacity that the object at least need to have. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note Linear time complexity. + */ + GenericValue& MemberReserve(SizeType newCapacity, Allocator &allocator) { + RAPIDJSON_ASSERT(IsObject()); + if (newCapacity > data_.o.capacity) { + SetMembersPointer(reinterpret_cast(allocator.Realloc(GetMembersPointer(), data_.o.capacity * sizeof(Member), newCapacity * sizeof(Member)))); + data_.o.capacity = newCapacity; + } + return *this; + } + //! Check whether a member exists in the object. /*! \param name Member name to be searched. @@ -1188,17 +1289,8 @@ public: RAPIDJSON_ASSERT(name.IsString()); ObjectData& o = data_.o; - if (o.size >= o.capacity) { - if (o.capacity == 0) { - o.capacity = kDefaultObjectCapacity; - SetMembersPointer(reinterpret_cast(allocator.Malloc(o.capacity * sizeof(Member)))); - } - else { - SizeType oldCapacity = o.capacity; - o.capacity += (oldCapacity + 1) / 2; // grow by factor 1.5 - SetMembersPointer(reinterpret_cast(allocator.Realloc(GetMembersPointer(), oldCapacity * sizeof(Member), o.capacity * sizeof(Member)))); - } - } + if (o.size >= o.capacity) + MemberReserve(o.capacity == 0 ? kDefaultObjectCapacity : (o.capacity + (o.capacity + 1) / 2), allocator); Member* members = GetMembersPointer(); members[o.size].name.RawAssign(name); members[o.size].value.RawAssign(value); @@ -1425,7 +1517,7 @@ public: MemberIterator pos = MemberBegin() + (first - MemberBegin()); for (MemberIterator itr = pos; itr != last; ++itr) itr->~Member(); - std::memmove(&*pos, &*last, static_cast(MemberEnd() - last) * sizeof(Member)); + std::memmove(static_cast(&*pos), &*last, static_cast(MemberEnd() - last) * sizeof(Member)); data_.o.size -= static_cast(last - first); return pos; } @@ -1628,8 +1720,8 @@ public: RAPIDJSON_ASSERT(last <= End()); ValueIterator pos = Begin() + (first - Begin()); for (ValueIterator itr = pos; itr != last; ++itr) - itr->~GenericValue(); - std::memmove(pos, last, static_cast(End() - last) * sizeof(GenericValue)); + itr->~GenericValue(); + std::memmove(static_cast(pos), last, static_cast(End() - last) * sizeof(GenericValue)); data_.a.size -= static_cast(last - first); return pos; } @@ -1671,7 +1763,7 @@ public: GenericValue& SetInt64(int64_t i64) { this->~GenericValue(); new (this) GenericValue(i64); return *this; } GenericValue& SetUint64(uint64_t u64) { this->~GenericValue(); new (this) GenericValue(u64); return *this; } GenericValue& SetDouble(double d) { this->~GenericValue(); new (this) GenericValue(d); return *this; } - GenericValue& SetFloat(float f) { this->~GenericValue(); new (this) GenericValue(f); return *this; } + GenericValue& SetFloat(float f) { this->~GenericValue(); new (this) GenericValue(static_cast(f)); return *this; } //@} @@ -1710,7 +1802,7 @@ public: \return The value itself for fluent API. \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length */ - GenericValue& SetString(const Ch* s, SizeType length, Allocator& allocator) { this->~GenericValue(); SetStringRaw(StringRef(s, length), allocator); return *this; } + GenericValue& SetString(const Ch* s, SizeType length, Allocator& allocator) { return SetString(StringRef(s, length), allocator); } //! Set this value as a string by copying from source string. /*! \param s source string. @@ -1718,7 +1810,15 @@ public: \return The value itself for fluent API. \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length */ - GenericValue& SetString(const Ch* s, Allocator& allocator) { return SetString(s, internal::StrLen(s), allocator); } + GenericValue& SetString(const Ch* s, Allocator& allocator) { return SetString(StringRef(s), allocator); } + + //! Set this value as a string by copying from source string. + /*! \param s source string reference + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s.s && strcmp(GetString(),s) == 0 && GetStringLength() == length + */ + GenericValue& SetString(StringRefType s, Allocator& allocator) { this->~GenericValue(); SetStringRaw(s, allocator); return *this; } #if RAPIDJSON_HAS_STDSTRING //! Set this value as a string by copying from source string. @@ -1728,7 +1828,7 @@ public: \post IsString() == true && GetString() != s.data() && strcmp(GetString(),s.data() == 0 && GetStringLength() == s.size() \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. */ - GenericValue& SetString(const std::basic_string& s, Allocator& allocator) { return SetString(s.data(), SizeType(s.size()), allocator); } + GenericValue& SetString(const std::basic_string& s, Allocator& allocator) { return SetString(StringRef(s), allocator); } #endif //@} @@ -1936,7 +2036,7 @@ private: if (count) { GenericValue* e = static_cast(allocator.Malloc(count * sizeof(GenericValue))); SetElementsPointer(e); - std::memcpy(e, values, count * sizeof(GenericValue)); + std::memcpy(static_cast(e), values, count * sizeof(GenericValue)); } else SetElementsPointer(0); @@ -1949,7 +2049,7 @@ private: if (count) { Member* m = static_cast(allocator.Malloc(count * sizeof(Member))); SetMembersPointer(m); - std::memcpy(m, members, count * sizeof(Member)); + std::memcpy(static_cast(m), members, count * sizeof(Member)); } else SetMembersPointer(0); @@ -2038,7 +2138,7 @@ public: GenericValue(type), allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_() { if (!allocator_) - ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); } //! Constructor @@ -2051,7 +2151,7 @@ public: allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_() { if (!allocator_) - ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); } #if RAPIDJSON_HAS_CXX11_RVALUE_REFS @@ -2112,6 +2212,10 @@ public: return *this; } + // Allow Swap with ValueType. + // Refer to Effective C++ 3rd Edition/Item 33: Avoid hiding inherited names. + using ValueType::Swap; + //! free-standing swap function helper /*! Helper function to enable support for common swap implementation pattern based on \c std::swap: @@ -2243,7 +2347,7 @@ public: template GenericDocument& Parse(const typename SourceEncoding::Ch* str, size_t length) { RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag)); - MemoryStream ms(static_cast(str), length * sizeof(typename SourceEncoding::Ch)); + MemoryStream ms(reinterpret_cast(str), length * sizeof(typename SourceEncoding::Ch)); EncodedInputStream is(ms); ParseStream(is); return *this; @@ -2280,7 +2384,7 @@ public: //!@name Handling parse errors //!@{ - //! Whether a parse error has occured in the last parsing. + //! Whether a parse error has occurred in the last parsing. bool HasParseError() const { return parseResult_.IsError(); } //! Get the \ref ParseErrorCode of last parsing. @@ -2401,35 +2505,6 @@ private: //! GenericDocument with UTF8 encoding typedef GenericDocument > Document; -// defined here due to the dependency on GenericDocument -template -template -inline -GenericValue::GenericValue(const GenericValue& rhs, Allocator& allocator) -{ - switch (rhs.GetType()) { - case kObjectType: - case kArrayType: { // perform deep copy via SAX Handler - GenericDocument d(&allocator); - rhs.Accept(d); - RawAssign(*d.stack_.template Pop(1)); - } - break; - case kStringType: - if (rhs.data_.f.flags == kConstStringFlag) { - data_.f.flags = rhs.data_.f.flags; - data_ = *reinterpret_cast(&rhs.data_); - } else { - SetStringRaw(StringRef(rhs.GetString(), rhs.GetStringLength()), allocator); - } - break; - default: - data_.f.flags = rhs.data_.f.flags; - data_ = *reinterpret_cast(&rhs.data_); - break; - } -} - //! Helper class for accessing Value of array type. /*! Instance of this helper class is obtained by \c GenericValue::GetArray(). @@ -2510,6 +2585,7 @@ public: ~GenericObject() {} SizeType MemberCount() const { return value_.MemberCount(); } + SizeType MemberCapacity() const { return value_.MemberCapacity(); } bool ObjectEmpty() const { return value_.ObjectEmpty(); } template ValueType& operator[](T* name) const { return value_[name]; } template ValueType& operator[](const GenericValue& name) const { return value_[name]; } @@ -2518,6 +2594,7 @@ public: #endif MemberIterator MemberBegin() const { return value_.MemberBegin(); } MemberIterator MemberEnd() const { return value_.MemberEnd(); } + GenericObject MemberReserve(SizeType newCapacity, AllocatorType &allocator) const { value_.MemberReserve(newCapacity, allocator); return *this; } bool HasMember(const Ch* name) const { return value_.HasMember(name); } #if RAPIDJSON_HAS_STDSTRING bool HasMember(const std::basic_string& name) const { return value_.HasMember(name); } @@ -2543,7 +2620,7 @@ public: GenericObject AddMember(StringRefType name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } GenericObject AddMember(StringRefType name, StringRefType value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericObject)) AddMember(StringRefType name, T value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } - void RemoveAllMembers() { return value_.RemoveAllMembers(); } + void RemoveAllMembers() { value_.RemoveAllMembers(); } bool RemoveMember(const Ch* name) const { return value_.RemoveMember(name); } #if RAPIDJSON_HAS_STDSTRING bool RemoveMember(const std::basic_string& name) const { return value_.RemoveMember(name); } diff --git a/src/3rdparty/rapidjson/encodedstream.h b/src/3rdparty/rapidjson/encodedstream.h index 14506838..223601c0 100644 --- a/src/3rdparty/rapidjson/encodedstream.h +++ b/src/3rdparty/rapidjson/encodedstream.h @@ -200,7 +200,7 @@ private: // xx xx xx xx UTF-8 if (!hasBOM_) { - unsigned pattern = (c[0] ? 1 : 0) | (c[1] ? 2 : 0) | (c[2] ? 4 : 0) | (c[3] ? 8 : 0); + int pattern = (c[0] ? 1 : 0) | (c[1] ? 2 : 0) | (c[2] ? 4 : 0) | (c[3] ? 8 : 0); switch (pattern) { case 0x08: type_ = kUTF32BE; break; case 0x0A: type_ = kUTF16BE; break; diff --git a/src/3rdparty/rapidjson/encodings.h b/src/3rdparty/rapidjson/encodings.h index baa7c2b1..0b244679 100644 --- a/src/3rdparty/rapidjson/encodings.h +++ b/src/3rdparty/rapidjson/encodings.h @@ -17,7 +17,7 @@ #include "rapidjson.h" -#ifdef _MSC_VER +#if defined(_MSC_VER) && !defined(__clang__) RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(4244) // conversion from 'type1' to 'type2', possible loss of data RAPIDJSON_DIAG_OFF(4702) // unreachable code @@ -144,9 +144,9 @@ struct UTF8 { template static bool Decode(InputStream& is, unsigned* codepoint) { -#define COPY() c = is.Take(); *codepoint = (*codepoint << 6) | (static_cast(c) & 0x3Fu) -#define TRANS(mask) result &= ((GetRange(static_cast(c)) & mask) != 0) -#define TAIL() COPY(); TRANS(0x70) +#define RAPIDJSON_COPY() c = is.Take(); *codepoint = (*codepoint << 6) | (static_cast(c) & 0x3Fu) +#define RAPIDJSON_TRANS(mask) result &= ((GetRange(static_cast(c)) & mask) != 0) +#define RAPIDJSON_TAIL() RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x70) typename InputStream::Ch c = is.Take(); if (!(c & 0x80)) { *codepoint = static_cast(c); @@ -157,48 +157,48 @@ struct UTF8 { if (type >= 32) { *codepoint = 0; } else { - *codepoint = (0xFF >> type) & static_cast(c); + *codepoint = (0xFFu >> type) & static_cast(c); } bool result = true; switch (type) { - case 2: TAIL(); return result; - case 3: TAIL(); TAIL(); return result; - case 4: COPY(); TRANS(0x50); TAIL(); return result; - case 5: COPY(); TRANS(0x10); TAIL(); TAIL(); return result; - case 6: TAIL(); TAIL(); TAIL(); return result; - case 10: COPY(); TRANS(0x20); TAIL(); return result; - case 11: COPY(); TRANS(0x60); TAIL(); TAIL(); return result; + case 2: RAPIDJSON_TAIL(); return result; + case 3: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 4: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x50); RAPIDJSON_TAIL(); return result; + case 5: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x10); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 6: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 10: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x20); RAPIDJSON_TAIL(); return result; + case 11: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x60); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; default: return false; } -#undef COPY -#undef TRANS -#undef TAIL +#undef RAPIDJSON_COPY +#undef RAPIDJSON_TRANS +#undef RAPIDJSON_TAIL } template static bool Validate(InputStream& is, OutputStream& os) { -#define COPY() os.Put(c = is.Take()) -#define TRANS(mask) result &= ((GetRange(static_cast(c)) & mask) != 0) -#define TAIL() COPY(); TRANS(0x70) +#define RAPIDJSON_COPY() os.Put(c = is.Take()) +#define RAPIDJSON_TRANS(mask) result &= ((GetRange(static_cast(c)) & mask) != 0) +#define RAPIDJSON_TAIL() RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x70) Ch c; - COPY(); + RAPIDJSON_COPY(); if (!(c & 0x80)) return true; bool result = true; switch (GetRange(static_cast(c))) { - case 2: TAIL(); return result; - case 3: TAIL(); TAIL(); return result; - case 4: COPY(); TRANS(0x50); TAIL(); return result; - case 5: COPY(); TRANS(0x10); TAIL(); TAIL(); return result; - case 6: TAIL(); TAIL(); TAIL(); return result; - case 10: COPY(); TRANS(0x20); TAIL(); return result; - case 11: COPY(); TRANS(0x60); TAIL(); TAIL(); return result; + case 2: RAPIDJSON_TAIL(); return result; + case 3: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 4: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x50); RAPIDJSON_TAIL(); return result; + case 5: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x10); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 6: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 10: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x20); RAPIDJSON_TAIL(); return result; + case 11: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x60); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; default: return false; } -#undef COPY -#undef TRANS -#undef TAIL +#undef RAPIDJSON_COPY +#undef RAPIDJSON_TRANS +#undef RAPIDJSON_TAIL } static unsigned char GetRange(unsigned char c) { @@ -283,7 +283,7 @@ struct UTF16 { RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); unsigned v = codepoint - 0x10000; os.Put(static_cast((v >> 10) | 0xD800)); - os.Put((v & 0x3FF) | 0xDC00); + os.Put(static_cast((v & 0x3FF) | 0xDC00)); } } @@ -299,7 +299,7 @@ struct UTF16 { RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); unsigned v = codepoint - 0x10000; PutUnsafe(os, static_cast((v >> 10) | 0xD800)); - PutUnsafe(os, (v & 0x3FF) | 0xDC00); + PutUnsafe(os, static_cast((v & 0x3FF) | 0xDC00)); } } @@ -384,7 +384,7 @@ struct UTF16BE : UTF16 { static CharType Take(InputByteStream& is) { RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); unsigned c = static_cast(static_cast(is.Take())) << 8; - c |= static_cast(is.Take()); + c |= static_cast(static_cast(is.Take())); return static_cast(c); } @@ -620,28 +620,28 @@ struct AutoUTF { #define RAPIDJSON_ENCODINGS_FUNC(x) UTF8::x, UTF16LE::x, UTF16BE::x, UTF32LE::x, UTF32BE::x template - RAPIDJSON_FORCEINLINE static void Encode(OutputStream& os, unsigned codepoint) { + static RAPIDJSON_FORCEINLINE void Encode(OutputStream& os, unsigned codepoint) { typedef void (*EncodeFunc)(OutputStream&, unsigned); static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Encode) }; (*f[os.GetType()])(os, codepoint); } template - RAPIDJSON_FORCEINLINE static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + static RAPIDJSON_FORCEINLINE void EncodeUnsafe(OutputStream& os, unsigned codepoint) { typedef void (*EncodeFunc)(OutputStream&, unsigned); static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(EncodeUnsafe) }; (*f[os.GetType()])(os, codepoint); } template - RAPIDJSON_FORCEINLINE static bool Decode(InputStream& is, unsigned* codepoint) { + static RAPIDJSON_FORCEINLINE bool Decode(InputStream& is, unsigned* codepoint) { typedef bool (*DecodeFunc)(InputStream&, unsigned*); static const DecodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Decode) }; return (*f[is.GetType()])(is, codepoint); } template - RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { + static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) { typedef bool (*ValidateFunc)(InputStream&, OutputStream&); static const ValidateFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Validate) }; return (*f[is.GetType()])(is, os); @@ -658,7 +658,7 @@ template struct Transcoder { //! Take one Unicode codepoint from source encoding, convert it to target encoding and put it to the output stream. template - RAPIDJSON_FORCEINLINE static bool Transcode(InputStream& is, OutputStream& os) { + static RAPIDJSON_FORCEINLINE bool Transcode(InputStream& is, OutputStream& os) { unsigned codepoint; if (!SourceEncoding::Decode(is, &codepoint)) return false; @@ -667,7 +667,7 @@ struct Transcoder { } template - RAPIDJSON_FORCEINLINE static bool TranscodeUnsafe(InputStream& is, OutputStream& os) { + static RAPIDJSON_FORCEINLINE bool TranscodeUnsafe(InputStream& is, OutputStream& os) { unsigned codepoint; if (!SourceEncoding::Decode(is, &codepoint)) return false; @@ -677,7 +677,7 @@ struct Transcoder { //! Validate one Unicode codepoint from an encoded stream. template - RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { + static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) { return Transcode(is, os); // Since source/target encoding is different, must transcode. } }; @@ -690,26 +690,26 @@ inline void PutUnsafe(Stream& stream, typename Stream::Ch c); template struct Transcoder { template - RAPIDJSON_FORCEINLINE static bool Transcode(InputStream& is, OutputStream& os) { + static RAPIDJSON_FORCEINLINE bool Transcode(InputStream& is, OutputStream& os) { os.Put(is.Take()); // Just copy one code unit. This semantic is different from primary template class. return true; } template - RAPIDJSON_FORCEINLINE static bool TranscodeUnsafe(InputStream& is, OutputStream& os) { + static RAPIDJSON_FORCEINLINE bool TranscodeUnsafe(InputStream& is, OutputStream& os) { PutUnsafe(os, is.Take()); // Just copy one code unit. This semantic is different from primary template class. return true; } template - RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { + static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) { return Encoding::Validate(is, os); // source/target encoding are the same } }; RAPIDJSON_NAMESPACE_END -#if defined(__GNUC__) || defined(_MSC_VER) +#if defined(__GNUC__) || (defined(_MSC_VER) && !defined(__clang__)) RAPIDJSON_DIAG_POP #endif diff --git a/src/3rdparty/rapidjson/error/error.h b/src/3rdparty/rapidjson/error/error.h index 95cb31a7..9311d2f0 100644 --- a/src/3rdparty/rapidjson/error/error.h +++ b/src/3rdparty/rapidjson/error/error.h @@ -104,6 +104,8 @@ enum ParseErrorCode { \see GenericReader::Parse, GenericDocument::Parse */ struct ParseResult { + //!! Unspecified boolean type + typedef bool (ParseResult::*BooleanType)() const; public: //! Default constructor, no error. ParseResult() : code_(kParseErrorNone), offset_(0) {} @@ -115,8 +117,8 @@ public: //! Get the error offset, if \ref IsError(), 0 otherwise. size_t Offset() const { return offset_; } - //! Conversion to \c bool, returns \c true, iff !\ref IsError(). - operator bool() const { return !IsError(); } + //! Explicit conversion to \c bool, returns \c true, iff !\ref IsError(). + operator BooleanType() const { return !IsError() ? &ParseResult::IsError : NULL; } //! Whether the result is an error. bool IsError() const { return code_ != kParseErrorNone; } @@ -124,6 +126,10 @@ public: bool operator==(ParseErrorCode code) const { return code_ == code; } friend bool operator==(ParseErrorCode code, const ParseResult & err) { return code == err.code_; } + bool operator!=(const ParseResult& that) const { return !(*this == that); } + bool operator!=(ParseErrorCode code) const { return !(*this == code); } + friend bool operator!=(ParseErrorCode code, const ParseResult & err) { return err != code; } + //! Reset error code. void Clear() { Set(kParseErrorNone); } //! Update error code and offset. diff --git a/src/3rdparty/rapidjson/filereadstream.h b/src/3rdparty/rapidjson/filereadstream.h index b56ea13b..6b343707 100644 --- a/src/3rdparty/rapidjson/filereadstream.h +++ b/src/3rdparty/rapidjson/filereadstream.h @@ -59,7 +59,7 @@ public: // For encoding detection only. const Ch* Peek4() const { - return (current_ + 4 <= bufferLast_) ? current_ : 0; + return (current_ + 4 - !eof_ <= bufferLast_) ? current_ : 0; } private: @@ -68,7 +68,7 @@ private: ++current_; else if (!eof_) { count_ += readCount_; - readCount_ = fread(buffer_, 1, bufferSize_, fp_); + readCount_ = std::fread(buffer_, 1, bufferSize_, fp_); bufferLast_ = buffer_ + readCount_ - 1; current_ = buffer_; diff --git a/src/3rdparty/rapidjson/filewritestream.h b/src/3rdparty/rapidjson/filewritestream.h index 6378dd60..8b48fee1 100644 --- a/src/3rdparty/rapidjson/filewritestream.h +++ b/src/3rdparty/rapidjson/filewritestream.h @@ -25,7 +25,7 @@ RAPIDJSON_DIAG_OFF(unreachable-code) RAPIDJSON_NAMESPACE_BEGIN -//! Wrapper of C file stream for input using fread(). +//! Wrapper of C file stream for output using fwrite(). /*! \note implements Stream concept */ @@ -62,7 +62,7 @@ public: void Flush() { if (current_ != buffer_) { - size_t result = fwrite(buffer_, 1, static_cast(current_ - buffer_), fp_); + size_t result = std::fwrite(buffer_, 1, static_cast(current_ - buffer_), fp_); if (result < static_cast(current_ - buffer_)) { // failure deliberately ignored at this time // added to avoid warn_unused_result build errors diff --git a/src/3rdparty/rapidjson/internal/biginteger.h b/src/3rdparty/rapidjson/internal/biginteger.h index 9d3e88c9..a31c8a88 100644 --- a/src/3rdparty/rapidjson/internal/biginteger.h +++ b/src/3rdparty/rapidjson/internal/biginteger.h @@ -17,7 +17,7 @@ #include "../rapidjson.h" -#if defined(_MSC_VER) && defined(_M_AMD64) +#if defined(_MSC_VER) && !__INTEL_COMPILER && defined(_M_AMD64) #include // for _umul128 #pragma intrinsic(_umul128) #endif @@ -133,7 +133,7 @@ public: RAPIDJSON_ASSERT(count_ + offset <= kCapacity); if (interShift == 0) { - std::memmove(&digits_[count_ - 1 + offset], &digits_[count_ - 1], count_ * sizeof(Type)); + std::memmove(digits_ + offset, digits_, count_ * sizeof(Type)); count_ += offset; } else { diff --git a/src/3rdparty/rapidjson/internal/diyfp.h b/src/3rdparty/rapidjson/internal/diyfp.h index c9fefdc6..b6c2cf56 100644 --- a/src/3rdparty/rapidjson/internal/diyfp.h +++ b/src/3rdparty/rapidjson/internal/diyfp.h @@ -1,5 +1,5 @@ // Tencent is pleased to support the open source community by making RapidJSON available. -// +// // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. // // Licensed under the MIT License (the "License"); you may not use this file except @@ -7,9 +7,9 @@ // // http://opensource.org/licenses/MIT // -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. // This is a C++ header-only implementation of Grisu2 algorithm from the publication: @@ -20,8 +20,9 @@ #define RAPIDJSON_DIYFP_H_ #include "../rapidjson.h" +#include -#if defined(_MSC_VER) && defined(_M_AMD64) +#if defined(_MSC_VER) && defined(_M_AMD64) && !defined(__INTEL_COMPILER) #include #pragma intrinsic(_BitScanReverse64) #pragma intrinsic(_umul128) @@ -56,7 +57,7 @@ struct DiyFp { if (biased_e != 0) { f = significand + kDpHiddenBit; e = biased_e - kDpExponentBias; - } + } else { f = significand; e = kDpMinExponent + 1; @@ -99,6 +100,7 @@ struct DiyFp { } DiyFp Normalize() const { + RAPIDJSON_ASSERT(f != 0); // https://stackoverflow.com/a/26809183/291737 #if defined(_MSC_VER) && defined(_M_AMD64) unsigned long index; _BitScanReverse64(&index, f); @@ -141,7 +143,16 @@ struct DiyFp { double d; uint64_t u64; }u; - const uint64_t be = (e == kDpDenormalExponent && (f & kDpHiddenBit) == 0) ? 0 : + RAPIDJSON_ASSERT(f <= kDpHiddenBit + kDpSignificandMask); + if (e < kDpDenormalExponent) { + // Underflow. + return 0.0; + } + if (e >= kDpMaxExponent) { + // Overflow. + return std::numeric_limits::infinity(); + } + const uint64_t be = (e == kDpDenormalExponent && (f & kDpHiddenBit) == 0) ? 0 : static_cast(e + kDpExponentBias); u.u64 = (f & kDpSignificandMask) | (be << kDpSignificandSize); return u.d; @@ -220,9 +231,10 @@ inline DiyFp GetCachedPowerByIndex(size_t index) { 641, 667, 694, 720, 747, 774, 800, 827, 853, 880, 907, 933, 960, 986, 1013, 1039, 1066 }; + RAPIDJSON_ASSERT(index < 87); return DiyFp(kCachedPowers_F[index], kCachedPowers_E[index]); } - + inline DiyFp GetCachedPower(int e, int* K) { //int k = static_cast(ceil((-61 - e) * 0.30102999566398114)) + 374; @@ -238,10 +250,11 @@ inline DiyFp GetCachedPower(int e, int* K) { } inline DiyFp GetCachedPower10(int exp, int *outExp) { - unsigned index = (static_cast(exp) + 348u) / 8u; - *outExp = -348 + static_cast(index) * 8; - return GetCachedPowerByIndex(index); - } + RAPIDJSON_ASSERT(exp >= -348); + unsigned index = static_cast(exp + 348) / 8u; + *outExp = -348 + static_cast(index) * 8; + return GetCachedPowerByIndex(index); +} #ifdef __GNUC__ RAPIDJSON_DIAG_POP diff --git a/src/3rdparty/rapidjson/internal/dtoa.h b/src/3rdparty/rapidjson/internal/dtoa.h index 8d6350e6..bf2e9b2e 100644 --- a/src/3rdparty/rapidjson/internal/dtoa.h +++ b/src/3rdparty/rapidjson/internal/dtoa.h @@ -41,7 +41,7 @@ inline void GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest, uin } } -inline unsigned CountDecimalDigit32(uint32_t n) { +inline int CountDecimalDigit32(uint32_t n) { // Simple pure C++ implementation was faster than __builtin_clz version in this situation. if (n < 10) return 1; if (n < 100) return 2; @@ -63,7 +63,7 @@ inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buff const DiyFp wp_w = Mp - W; uint32_t p1 = static_cast(Mp.f >> -one.e); uint64_t p2 = Mp.f & (one.f - 1); - unsigned kappa = CountDecimalDigit32(p1); // kappa in [0, 9] + int kappa = CountDecimalDigit32(p1); // kappa in [0, 9] *len = 0; while (kappa > 0) { @@ -102,8 +102,8 @@ inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buff kappa--; if (p2 < delta) { *K += kappa; - int index = -static_cast(kappa); - GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * (index < 9 ? kPow10[-static_cast(kappa)] : 0)); + int index = -kappa; + GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * (index < 9 ? kPow10[index] : 0)); return; } } diff --git a/src/3rdparty/rapidjson/internal/ieee754.h b/src/3rdparty/rapidjson/internal/ieee754.h index 82bb0b99..c2684ba2 100644 --- a/src/3rdparty/rapidjson/internal/ieee754.h +++ b/src/3rdparty/rapidjson/internal/ieee754.h @@ -48,13 +48,13 @@ public: int IntegerExponent() const { return (IsNormal() ? Exponent() : kDenormalExponent) - kSignificandSize; } uint64_t ToBias() const { return (u_ & kSignMask) ? ~u_ + 1 : u_ | kSignMask; } - static unsigned EffectiveSignificandSize(int order) { + static int EffectiveSignificandSize(int order) { if (order >= -1021) return 53; else if (order <= -1074) return 0; else - return static_cast(order) + 1074; + return order + 1074; } private: diff --git a/src/3rdparty/rapidjson/internal/itoa.h b/src/3rdparty/rapidjson/internal/itoa.h index 01a4e7e7..9b1c45cc 100644 --- a/src/3rdparty/rapidjson/internal/itoa.h +++ b/src/3rdparty/rapidjson/internal/itoa.h @@ -1,5 +1,5 @@ // Tencent is pleased to support the open source community by making RapidJSON available. -// +// // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. // // Licensed under the MIT License (the "License"); you may not use this file except @@ -7,9 +7,9 @@ // // http://opensource.org/licenses/MIT // -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. #ifndef RAPIDJSON_ITOA_ @@ -37,12 +37,14 @@ inline const char* GetDigitsLut() { } inline char* u32toa(uint32_t value, char* buffer) { + RAPIDJSON_ASSERT(buffer != 0); + const char* cDigitsLut = GetDigitsLut(); if (value < 10000) { const uint32_t d1 = (value / 100) << 1; const uint32_t d2 = (value % 100) << 1; - + if (value >= 1000) *buffer++ = cDigitsLut[d1]; if (value >= 100) @@ -55,13 +57,13 @@ inline char* u32toa(uint32_t value, char* buffer) { // value = bbbbcccc const uint32_t b = value / 10000; const uint32_t c = value % 10000; - + const uint32_t d1 = (b / 100) << 1; const uint32_t d2 = (b % 100) << 1; - + const uint32_t d3 = (c / 100) << 1; const uint32_t d4 = (c % 100) << 1; - + if (value >= 10000000) *buffer++ = cDigitsLut[d1]; if (value >= 1000000) @@ -69,7 +71,7 @@ inline char* u32toa(uint32_t value, char* buffer) { if (value >= 100000) *buffer++ = cDigitsLut[d2]; *buffer++ = cDigitsLut[d2 + 1]; - + *buffer++ = cDigitsLut[d3]; *buffer++ = cDigitsLut[d3 + 1]; *buffer++ = cDigitsLut[d4]; @@ -77,10 +79,10 @@ inline char* u32toa(uint32_t value, char* buffer) { } else { // value = aabbbbcccc in decimal - + const uint32_t a = value / 100000000; // 1 to 42 value %= 100000000; - + if (a >= 10) { const unsigned i = a << 1; *buffer++ = cDigitsLut[i]; @@ -91,13 +93,13 @@ inline char* u32toa(uint32_t value, char* buffer) { const uint32_t b = value / 10000; // 0 to 9999 const uint32_t c = value % 10000; // 0 to 9999 - + const uint32_t d1 = (b / 100) << 1; const uint32_t d2 = (b % 100) << 1; - + const uint32_t d3 = (c / 100) << 1; const uint32_t d4 = (c % 100) << 1; - + *buffer++ = cDigitsLut[d1]; *buffer++ = cDigitsLut[d1 + 1]; *buffer++ = cDigitsLut[d2]; @@ -111,6 +113,7 @@ inline char* u32toa(uint32_t value, char* buffer) { } inline char* i32toa(int32_t value, char* buffer) { + RAPIDJSON_ASSERT(buffer != 0); uint32_t u = static_cast(value); if (value < 0) { *buffer++ = '-'; @@ -121,6 +124,7 @@ inline char* i32toa(int32_t value, char* buffer) { } inline char* u64toa(uint64_t value, char* buffer) { + RAPIDJSON_ASSERT(buffer != 0); const char* cDigitsLut = GetDigitsLut(); const uint64_t kTen8 = 100000000; const uint64_t kTen9 = kTen8 * 10; @@ -131,13 +135,13 @@ inline char* u64toa(uint64_t value, char* buffer) { const uint64_t kTen14 = kTen8 * 1000000; const uint64_t kTen15 = kTen8 * 10000000; const uint64_t kTen16 = kTen8 * kTen8; - + if (value < kTen8) { uint32_t v = static_cast(value); if (v < 10000) { const uint32_t d1 = (v / 100) << 1; const uint32_t d2 = (v % 100) << 1; - + if (v >= 1000) *buffer++ = cDigitsLut[d1]; if (v >= 100) @@ -150,13 +154,13 @@ inline char* u64toa(uint64_t value, char* buffer) { // value = bbbbcccc const uint32_t b = v / 10000; const uint32_t c = v % 10000; - + const uint32_t d1 = (b / 100) << 1; const uint32_t d2 = (b % 100) << 1; - + const uint32_t d3 = (c / 100) << 1; const uint32_t d4 = (c % 100) << 1; - + if (value >= 10000000) *buffer++ = cDigitsLut[d1]; if (value >= 1000000) @@ -164,7 +168,7 @@ inline char* u64toa(uint64_t value, char* buffer) { if (value >= 100000) *buffer++ = cDigitsLut[d2]; *buffer++ = cDigitsLut[d2 + 1]; - + *buffer++ = cDigitsLut[d3]; *buffer++ = cDigitsLut[d3 + 1]; *buffer++ = cDigitsLut[d4]; @@ -174,22 +178,22 @@ inline char* u64toa(uint64_t value, char* buffer) { else if (value < kTen16) { const uint32_t v0 = static_cast(value / kTen8); const uint32_t v1 = static_cast(value % kTen8); - + const uint32_t b0 = v0 / 10000; const uint32_t c0 = v0 % 10000; - + const uint32_t d1 = (b0 / 100) << 1; const uint32_t d2 = (b0 % 100) << 1; - + const uint32_t d3 = (c0 / 100) << 1; const uint32_t d4 = (c0 % 100) << 1; const uint32_t b1 = v1 / 10000; const uint32_t c1 = v1 % 10000; - + const uint32_t d5 = (b1 / 100) << 1; const uint32_t d6 = (b1 % 100) << 1; - + const uint32_t d7 = (c1 / 100) << 1; const uint32_t d8 = (c1 % 100) << 1; @@ -207,9 +211,8 @@ inline char* u64toa(uint64_t value, char* buffer) { *buffer++ = cDigitsLut[d3 + 1]; if (value >= kTen9) *buffer++ = cDigitsLut[d4]; - if (value >= kTen8) - *buffer++ = cDigitsLut[d4 + 1]; - + + *buffer++ = cDigitsLut[d4 + 1]; *buffer++ = cDigitsLut[d5]; *buffer++ = cDigitsLut[d5 + 1]; *buffer++ = cDigitsLut[d6]; @@ -222,7 +225,7 @@ inline char* u64toa(uint64_t value, char* buffer) { else { const uint32_t a = static_cast(value / kTen16); // 1 to 1844 value %= kTen16; - + if (a < 10) *buffer++ = static_cast('0' + static_cast(a)); else if (a < 100) { @@ -232,7 +235,7 @@ inline char* u64toa(uint64_t value, char* buffer) { } else if (a < 1000) { *buffer++ = static_cast('0' + static_cast(a / 100)); - + const uint32_t i = (a % 100) << 1; *buffer++ = cDigitsLut[i]; *buffer++ = cDigitsLut[i + 1]; @@ -245,28 +248,28 @@ inline char* u64toa(uint64_t value, char* buffer) { *buffer++ = cDigitsLut[j]; *buffer++ = cDigitsLut[j + 1]; } - + const uint32_t v0 = static_cast(value / kTen8); const uint32_t v1 = static_cast(value % kTen8); - + const uint32_t b0 = v0 / 10000; const uint32_t c0 = v0 % 10000; - + const uint32_t d1 = (b0 / 100) << 1; const uint32_t d2 = (b0 % 100) << 1; - + const uint32_t d3 = (c0 / 100) << 1; const uint32_t d4 = (c0 % 100) << 1; - + const uint32_t b1 = v1 / 10000; const uint32_t c1 = v1 % 10000; - + const uint32_t d5 = (b1 / 100) << 1; const uint32_t d6 = (b1 % 100) << 1; - + const uint32_t d7 = (c1 / 100) << 1; const uint32_t d8 = (c1 % 100) << 1; - + *buffer++ = cDigitsLut[d1]; *buffer++ = cDigitsLut[d1 + 1]; *buffer++ = cDigitsLut[d2]; @@ -284,11 +287,12 @@ inline char* u64toa(uint64_t value, char* buffer) { *buffer++ = cDigitsLut[d8]; *buffer++ = cDigitsLut[d8 + 1]; } - + return buffer; } inline char* i64toa(int64_t value, char* buffer) { + RAPIDJSON_ASSERT(buffer != 0); uint64_t u = static_cast(value); if (value < 0) { *buffer++ = '-'; diff --git a/src/3rdparty/rapidjson/internal/meta.h b/src/3rdparty/rapidjson/internal/meta.h index 5a9aaa42..d401edf8 100644 --- a/src/3rdparty/rapidjson/internal/meta.h +++ b/src/3rdparty/rapidjson/internal/meta.h @@ -21,7 +21,8 @@ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(effc++) #endif -#if defined(_MSC_VER) + +#if defined(_MSC_VER) && !defined(__clang__) RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(6334) #endif @@ -174,7 +175,11 @@ template struct RemoveSfinaeTag { typedef T Type; RAPIDJSON_NAMESPACE_END //@endcond -#if defined(__GNUC__) || defined(_MSC_VER) +#if defined(_MSC_VER) && !defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#ifdef __GNUC__ RAPIDJSON_DIAG_POP #endif diff --git a/src/3rdparty/rapidjson/internal/regex.h b/src/3rdparty/rapidjson/internal/regex.h index 422a5240..16e35592 100644 --- a/src/3rdparty/rapidjson/internal/regex.h +++ b/src/3rdparty/rapidjson/internal/regex.h @@ -24,16 +24,17 @@ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(padded) RAPIDJSON_DIAG_OFF(switch-enum) RAPIDJSON_DIAG_OFF(implicit-fallthrough) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated #endif #ifdef __GNUC__ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(effc++) +#if __GNUC__ >= 7 +RAPIDJSON_DIAG_OFF(implicit-fallthrough) #endif - -#ifdef _MSC_VER -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated #endif #ifndef RAPIDJSON_REGEX_VERBOSE @@ -43,12 +44,40 @@ RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated RAPIDJSON_NAMESPACE_BEGIN namespace internal { +/////////////////////////////////////////////////////////////////////////////// +// DecodedStream + +template +class DecodedStream { +public: + DecodedStream(SourceStream& ss) : ss_(ss), codepoint_() { Decode(); } + unsigned Peek() { return codepoint_; } + unsigned Take() { + unsigned c = codepoint_; + if (c) // No further decoding when '\0' + Decode(); + return c; + } + +private: + void Decode() { + if (!Encoding::Decode(ss_, &codepoint_)) + codepoint_ = 0; + } + + SourceStream& ss_; + unsigned codepoint_; +}; + /////////////////////////////////////////////////////////////////////////////// // GenericRegex static const SizeType kRegexInvalidState = ~SizeType(0); //!< Represents an invalid index in GenericRegex::State::out, out1 static const SizeType kRegexInvalidRange = ~SizeType(0); +template +class GenericRegexSearch; + //! Regular expression engine with subset of ECMAscript grammar. /*! Supported regular expression syntax: @@ -84,45 +113,29 @@ static const SizeType kRegexInvalidRange = ~SizeType(0); template class GenericRegex { public: + typedef Encoding EncodingType; typedef typename Encoding::Ch Ch; + template friend class GenericRegexSearch; GenericRegex(const Ch* source, Allocator* allocator = 0) : - states_(allocator, 256), ranges_(allocator, 256), root_(kRegexInvalidState), stateCount_(), rangeCount_(), - stateSet_(), state0_(allocator, 0), state1_(allocator, 0), anchorBegin_(), anchorEnd_() + ownAllocator_(allocator ? 0 : RAPIDJSON_NEW(Allocator)()), allocator_(allocator ? allocator : ownAllocator_), + states_(allocator_, 256), ranges_(allocator_, 256), root_(kRegexInvalidState), stateCount_(), rangeCount_(), + anchorBegin_(), anchorEnd_() { GenericStringStream ss(source); - DecodedStream > ds(ss); + DecodedStream, Encoding> ds(ss); Parse(ds); } - ~GenericRegex() { - Allocator::Free(stateSet_); + ~GenericRegex() + { + RAPIDJSON_DELETE(ownAllocator_); } bool IsValid() const { return root_ != kRegexInvalidState; } - template - bool Match(InputStream& is) const { - return SearchWithAnchoring(is, true, true); - } - - bool Match(const Ch* s) const { - GenericStringStream is(s); - return Match(is); - } - - template - bool Search(InputStream& is) const { - return SearchWithAnchoring(is, anchorBegin_, anchorEnd_); - } - - bool Search(const Ch* s) const { - GenericStringStream is(s); - return Search(is); - } - private: enum Operator { kZeroOrOne, @@ -157,28 +170,6 @@ private: SizeType minIndex; }; - template - class DecodedStream { - public: - DecodedStream(SourceStream& ss) : ss_(ss), codepoint_() { Decode(); } - unsigned Peek() { return codepoint_; } - unsigned Take() { - unsigned c = codepoint_; - if (c) // No further decoding when '\0' - Decode(); - return c; - } - - private: - void Decode() { - if (!Encoding::Decode(ss_, &codepoint_)) - codepoint_ = 0; - } - - SourceStream& ss_; - unsigned codepoint_; - }; - State& GetState(SizeType index) { RAPIDJSON_ASSERT(index < stateCount_); return states_.template Bottom()[index]; @@ -200,11 +191,10 @@ private: } template - void Parse(DecodedStream& ds) { - Allocator allocator; - Stack operandStack(&allocator, 256); // Frag - Stack operatorStack(&allocator, 256); // Operator - Stack atomCountStack(&allocator, 256); // unsigned (Atom per parenthesis) + void Parse(DecodedStream& ds) { + Stack operandStack(allocator_, 256); // Frag + Stack operatorStack(allocator_, 256); // Operator + Stack atomCountStack(allocator_, 256); // unsigned (Atom per parenthesis) *atomCountStack.template Push() = 0; @@ -327,14 +317,6 @@ private: printf("\n"); #endif } - - // Preallocate buffer for SearchWithAnchoring() - RAPIDJSON_ASSERT(stateSet_ == 0); - if (stateCount_ > 0) { - stateSet_ = static_cast(states_.GetAllocator().Malloc(GetStateSetSize())); - state0_.template Reserve(stateCount_); - state1_.template Reserve(stateCount_); - } } SizeType NewState(SizeType out, SizeType out1, unsigned codepoint) { @@ -413,8 +395,7 @@ private: } return false; - default: - RAPIDJSON_ASSERT(op == kOneOrMore); + case kOneOrMore: if (operandStack.GetSize() >= sizeof(Frag)) { Frag e = *operandStack.template Pop(1); SizeType s = NewState(kRegexInvalidState, e.start, 0); @@ -423,6 +404,10 @@ private: return true; } return false; + + default: + // syntax error (e.g. unclosed kLeftParenthesis) + return false; } } @@ -483,7 +468,7 @@ private: } template - bool ParseUnsigned(DecodedStream& ds, unsigned* u) { + bool ParseUnsigned(DecodedStream& ds, unsigned* u) { unsigned r = 0; if (ds.Peek() < '0' || ds.Peek() > '9') return false; @@ -497,7 +482,7 @@ private: } template - bool ParseRange(DecodedStream& ds, SizeType* range) { + bool ParseRange(DecodedStream& ds, SizeType* range) { bool isBegin = true; bool negate = false; int step = 0; @@ -575,7 +560,7 @@ private: } template - bool CharacterEscape(DecodedStream& ds, unsigned* escapedCodepoint) { + bool CharacterEscape(DecodedStream& ds, unsigned* escapedCodepoint) { unsigned codepoint; switch (codepoint = ds.Take()) { case '^': @@ -603,72 +588,8 @@ private: } } - template - bool SearchWithAnchoring(InputStream& is, bool anchorBegin, bool anchorEnd) const { - RAPIDJSON_ASSERT(IsValid()); - DecodedStream ds(is); - - state0_.Clear(); - Stack *current = &state0_, *next = &state1_; - const size_t stateSetSize = GetStateSetSize(); - std::memset(stateSet_, 0, stateSetSize); - - bool matched = AddState(*current, root_); - unsigned codepoint; - while (!current->Empty() && (codepoint = ds.Take()) != 0) { - std::memset(stateSet_, 0, stateSetSize); - next->Clear(); - matched = false; - for (const SizeType* s = current->template Bottom(); s != current->template End(); ++s) { - const State& sr = GetState(*s); - if (sr.codepoint == codepoint || - sr.codepoint == kAnyCharacterClass || - (sr.codepoint == kRangeCharacterClass && MatchRange(sr.rangeStart, codepoint))) - { - matched = AddState(*next, sr.out) || matched; - if (!anchorEnd && matched) - return true; - } - if (!anchorBegin) - AddState(*next, root_); - } - internal::Swap(current, next); - } - - return matched; - } - - size_t GetStateSetSize() const { - return (stateCount_ + 31) / 32 * 4; - } - - // Return whether the added states is a match state - bool AddState(Stack& l, SizeType index) const { - RAPIDJSON_ASSERT(index != kRegexInvalidState); - - const State& s = GetState(index); - if (s.out1 != kRegexInvalidState) { // Split - bool matched = AddState(l, s.out); - return AddState(l, s.out1) || matched; - } - else if (!(stateSet_[index >> 5] & (1 << (index & 31)))) { - stateSet_[index >> 5] |= (1 << (index & 31)); - *l.template PushUnsafe() = index; - } - return s.out == kRegexInvalidState; // by using PushUnsafe() above, we can ensure s is not validated due to reallocation. - } - - bool MatchRange(SizeType rangeIndex, unsigned codepoint) const { - bool yes = (GetRange(rangeIndex).start & kRangeNegationFlag) == 0; - while (rangeIndex != kRegexInvalidRange) { - const Range& r = GetRange(rangeIndex); - if (codepoint >= (r.start & ~kRangeNegationFlag) && codepoint <= r.end) - return yes; - rangeIndex = r.next; - } - return !yes; - } - + Allocator* ownAllocator_; + Allocator* allocator_; Stack states_; Stack ranges_; SizeType root_; @@ -678,23 +599,141 @@ private: static const unsigned kInfinityQuantifier = ~0u; // For SearchWithAnchoring() - uint32_t* stateSet_; // allocated by states_.GetAllocator() - mutable Stack state0_; - mutable Stack state1_; bool anchorBegin_; bool anchorEnd_; }; +template +class GenericRegexSearch { +public: + typedef typename RegexType::EncodingType Encoding; + typedef typename Encoding::Ch Ch; + + GenericRegexSearch(const RegexType& regex, Allocator* allocator = 0) : + regex_(regex), allocator_(allocator), ownAllocator_(0), + state0_(allocator, 0), state1_(allocator, 0), stateSet_() + { + RAPIDJSON_ASSERT(regex_.IsValid()); + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + stateSet_ = static_cast(allocator_->Malloc(GetStateSetSize())); + state0_.template Reserve(regex_.stateCount_); + state1_.template Reserve(regex_.stateCount_); + } + + ~GenericRegexSearch() { + Allocator::Free(stateSet_); + RAPIDJSON_DELETE(ownAllocator_); + } + + template + bool Match(InputStream& is) { + return SearchWithAnchoring(is, true, true); + } + + bool Match(const Ch* s) { + GenericStringStream is(s); + return Match(is); + } + + template + bool Search(InputStream& is) { + return SearchWithAnchoring(is, regex_.anchorBegin_, regex_.anchorEnd_); + } + + bool Search(const Ch* s) { + GenericStringStream is(s); + return Search(is); + } + +private: + typedef typename RegexType::State State; + typedef typename RegexType::Range Range; + + template + bool SearchWithAnchoring(InputStream& is, bool anchorBegin, bool anchorEnd) { + DecodedStream ds(is); + + state0_.Clear(); + Stack *current = &state0_, *next = &state1_; + const size_t stateSetSize = GetStateSetSize(); + std::memset(stateSet_, 0, stateSetSize); + + bool matched = AddState(*current, regex_.root_); + unsigned codepoint; + while (!current->Empty() && (codepoint = ds.Take()) != 0) { + std::memset(stateSet_, 0, stateSetSize); + next->Clear(); + matched = false; + for (const SizeType* s = current->template Bottom(); s != current->template End(); ++s) { + const State& sr = regex_.GetState(*s); + if (sr.codepoint == codepoint || + sr.codepoint == RegexType::kAnyCharacterClass || + (sr.codepoint == RegexType::kRangeCharacterClass && MatchRange(sr.rangeStart, codepoint))) + { + matched = AddState(*next, sr.out) || matched; + if (!anchorEnd && matched) + return true; + } + if (!anchorBegin) + AddState(*next, regex_.root_); + } + internal::Swap(current, next); + } + + return matched; + } + + size_t GetStateSetSize() const { + return (regex_.stateCount_ + 31) / 32 * 4; + } + + // Return whether the added states is a match state + bool AddState(Stack& l, SizeType index) { + RAPIDJSON_ASSERT(index != kRegexInvalidState); + + const State& s = regex_.GetState(index); + if (s.out1 != kRegexInvalidState) { // Split + bool matched = AddState(l, s.out); + return AddState(l, s.out1) || matched; + } + else if (!(stateSet_[index >> 5] & (1u << (index & 31)))) { + stateSet_[index >> 5] |= (1u << (index & 31)); + *l.template PushUnsafe() = index; + } + return s.out == kRegexInvalidState; // by using PushUnsafe() above, we can ensure s is not validated due to reallocation. + } + + bool MatchRange(SizeType rangeIndex, unsigned codepoint) const { + bool yes = (regex_.GetRange(rangeIndex).start & RegexType::kRangeNegationFlag) == 0; + while (rangeIndex != kRegexInvalidRange) { + const Range& r = regex_.GetRange(rangeIndex); + if (codepoint >= (r.start & ~RegexType::kRangeNegationFlag) && codepoint <= r.end) + return yes; + rangeIndex = r.next; + } + return !yes; + } + + const RegexType& regex_; + Allocator* allocator_; + Allocator* ownAllocator_; + Stack state0_; + Stack state1_; + uint32_t* stateSet_; +}; + typedef GenericRegex > Regex; +typedef GenericRegexSearch RegexSearch; } // namespace internal RAPIDJSON_NAMESPACE_END -#ifdef __clang__ +#ifdef __GNUC__ RAPIDJSON_DIAG_POP #endif -#ifdef _MSC_VER +#if defined(__clang__) || defined(_MSC_VER) RAPIDJSON_DIAG_POP #endif diff --git a/src/3rdparty/rapidjson/internal/stack.h b/src/3rdparty/rapidjson/internal/stack.h index 022c9aab..45dca6a8 100644 --- a/src/3rdparty/rapidjson/internal/stack.h +++ b/src/3rdparty/rapidjson/internal/stack.h @@ -17,6 +17,7 @@ #include "../allocators.h" #include "swap.h" +#include #if defined(__clang__) RAPIDJSON_DIAG_PUSH @@ -100,7 +101,7 @@ public: void ShrinkToFit() { if (Empty()) { // If the stack is empty, completely deallocate the memory. - Allocator::Free(stack_); + Allocator::Free(stack_); // NOLINT (+clang-analyzer-unix.Malloc) stack_ = 0; stackTop_ = 0; stackEnd_ = 0; @@ -114,7 +115,7 @@ public: template RAPIDJSON_FORCEINLINE void Reserve(size_t count = 1) { // Expand the stack if needed - if (RAPIDJSON_UNLIKELY(stackTop_ + sizeof(T) * count > stackEnd_)) + if (RAPIDJSON_UNLIKELY(static_cast(sizeof(T) * count) > (stackEnd_ - stackTop_))) Expand(count); } @@ -126,7 +127,8 @@ public: template RAPIDJSON_FORCEINLINE T* PushUnsafe(size_t count = 1) { - RAPIDJSON_ASSERT(stackTop_ + sizeof(T) * count <= stackEnd_); + RAPIDJSON_ASSERT(stackTop_); + RAPIDJSON_ASSERT(static_cast(sizeof(T) * count) <= (stackEnd_ - stackTop_)); T* ret = reinterpret_cast(stackTop_); stackTop_ += sizeof(T) * count; return ret; @@ -183,7 +185,7 @@ private: size_t newCapacity; if (stack_ == 0) { if (!allocator_) - ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); newCapacity = initialCapacity_; } else { newCapacity = GetCapacity(); diff --git a/src/3rdparty/rapidjson/internal/strfunc.h b/src/3rdparty/rapidjson/internal/strfunc.h index 2edfae52..226439a7 100644 --- a/src/3rdparty/rapidjson/internal/strfunc.h +++ b/src/3rdparty/rapidjson/internal/strfunc.h @@ -16,6 +16,7 @@ #define RAPIDJSON_INTERNAL_STRFUNC_H_ #include "../stream.h" +#include RAPIDJSON_NAMESPACE_BEGIN namespace internal { @@ -28,14 +29,27 @@ namespace internal { */ template inline SizeType StrLen(const Ch* s) { + RAPIDJSON_ASSERT(s != 0); const Ch* p = s; while (*p) ++p; return SizeType(p - s); } +template <> +inline SizeType StrLen(const char* s) { + return SizeType(std::strlen(s)); +} + +template <> +inline SizeType StrLen(const wchar_t* s) { + return SizeType(std::wcslen(s)); +} + //! Returns number of code points in a encoded string. template bool CountStringCodePoint(const typename Encoding::Ch* s, SizeType length, SizeType* outCount) { + RAPIDJSON_ASSERT(s != 0); + RAPIDJSON_ASSERT(outCount != 0); GenericStringStream is(s); const typename Encoding::Ch* end = s + length; SizeType count = 0; diff --git a/src/3rdparty/rapidjson/internal/strtod.h b/src/3rdparty/rapidjson/internal/strtod.h index 289c413b..dfca22b6 100644 --- a/src/3rdparty/rapidjson/internal/strtod.h +++ b/src/3rdparty/rapidjson/internal/strtod.h @@ -19,6 +19,8 @@ #include "biginteger.h" #include "diyfp.h" #include "pow10.h" +#include +#include RAPIDJSON_NAMESPACE_BEGIN namespace internal { @@ -126,46 +128,46 @@ inline bool StrtodFast(double d, int p, double* result) { } // Compute an approximation and see if it is within 1/2 ULP -inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosition, int exp, double* result) { +inline bool StrtodDiyFp(const char* decimals, int dLen, int dExp, double* result) { uint64_t significand = 0; - size_t i = 0; // 2^64 - 1 = 18446744073709551615, 1844674407370955161 = 0x1999999999999999 - for (; i < length; i++) { + int i = 0; // 2^64 - 1 = 18446744073709551615, 1844674407370955161 = 0x1999999999999999 + for (; i < dLen; i++) { if (significand > RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || (significand == RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) && decimals[i] > '5')) break; significand = significand * 10u + static_cast(decimals[i] - '0'); } - if (i < length && decimals[i] >= '5') // Rounding + if (i < dLen && decimals[i] >= '5') // Rounding significand++; - size_t remaining = length - i; - const unsigned kUlpShift = 3; - const unsigned kUlp = 1 << kUlpShift; + int remaining = dLen - i; + const int kUlpShift = 3; + const int kUlp = 1 << kUlpShift; int64_t error = (remaining == 0) ? 0 : kUlp / 2; DiyFp v(significand, 0); v = v.Normalize(); error <<= -v.e; - const int dExp = static_cast(decimalPosition) - static_cast(i) + exp; + dExp += remaining; int actualExp; DiyFp cachedPower = GetCachedPower10(dExp, &actualExp); if (actualExp != dExp) { static const DiyFp kPow10[] = { - DiyFp(RAPIDJSON_UINT64_C2(0xa0000000, 00000000), -60), // 10^1 - DiyFp(RAPIDJSON_UINT64_C2(0xc8000000, 00000000), -57), // 10^2 - DiyFp(RAPIDJSON_UINT64_C2(0xfa000000, 00000000), -54), // 10^3 - DiyFp(RAPIDJSON_UINT64_C2(0x9c400000, 00000000), -50), // 10^4 - DiyFp(RAPIDJSON_UINT64_C2(0xc3500000, 00000000), -47), // 10^5 - DiyFp(RAPIDJSON_UINT64_C2(0xf4240000, 00000000), -44), // 10^6 - DiyFp(RAPIDJSON_UINT64_C2(0x98968000, 00000000), -40) // 10^7 + DiyFp(RAPIDJSON_UINT64_C2(0xa0000000, 0x00000000), -60), // 10^1 + DiyFp(RAPIDJSON_UINT64_C2(0xc8000000, 0x00000000), -57), // 10^2 + DiyFp(RAPIDJSON_UINT64_C2(0xfa000000, 0x00000000), -54), // 10^3 + DiyFp(RAPIDJSON_UINT64_C2(0x9c400000, 0x00000000), -50), // 10^4 + DiyFp(RAPIDJSON_UINT64_C2(0xc3500000, 0x00000000), -47), // 10^5 + DiyFp(RAPIDJSON_UINT64_C2(0xf4240000, 0x00000000), -44), // 10^6 + DiyFp(RAPIDJSON_UINT64_C2(0x98968000, 0x00000000), -40) // 10^7 }; - int adjustment = dExp - actualExp - 1; - RAPIDJSON_ASSERT(adjustment >= 0 && adjustment < 7); - v = v * kPow10[adjustment]; - if (length + static_cast(adjustment)> 19u) // has more digits than decimal digits in 64-bit + int adjustment = dExp - actualExp; + RAPIDJSON_ASSERT(adjustment >= 1 && adjustment < 8); + v = v * kPow10[adjustment - 1]; + if (dLen + adjustment > 19) // has more digits than decimal digits in 64-bit error += kUlp / 2; } @@ -177,17 +179,17 @@ inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosit v = v.Normalize(); error <<= oldExp - v.e; - const unsigned effectiveSignificandSize = Double::EffectiveSignificandSize(64 + v.e); - unsigned precisionSize = 64 - effectiveSignificandSize; + const int effectiveSignificandSize = Double::EffectiveSignificandSize(64 + v.e); + int precisionSize = 64 - effectiveSignificandSize; if (precisionSize + kUlpShift >= 64) { - unsigned scaleExp = (precisionSize + kUlpShift) - 63; + int scaleExp = (precisionSize + kUlpShift) - 63; v.f >>= scaleExp; v.e += scaleExp; - error = (error >> scaleExp) + 1 + static_cast(kUlp); + error = (error >> scaleExp) + 1 + kUlp; precisionSize -= scaleExp; } - DiyFp rounded(v.f >> precisionSize, v.e + static_cast(precisionSize)); + DiyFp rounded(v.f >> precisionSize, v.e + precisionSize); const uint64_t precisionBits = (v.f & ((uint64_t(1) << precisionSize) - 1)) * kUlp; const uint64_t halfWay = (uint64_t(1) << (precisionSize - 1)) * kUlp; if (precisionBits >= halfWay + static_cast(error)) { @@ -203,9 +205,9 @@ inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosit return halfWay - static_cast(error) >= precisionBits || precisionBits >= halfWay + static_cast(error); } -inline double StrtodBigInteger(double approx, const char* decimals, size_t length, size_t decimalPosition, int exp) { - const BigInteger dInt(decimals, length); - const int dExp = static_cast(decimalPosition) - static_cast(length) + exp; +inline double StrtodBigInteger(double approx, const char* decimals, int dLen, int dExp) { + RAPIDJSON_ASSERT(dLen >= 0); + const BigInteger dInt(decimals, static_cast(dLen)); Double a(approx); int cmp = CheckWithinHalfULP(a.Value(), dInt, dExp); if (cmp < 0) @@ -225,42 +227,61 @@ inline double StrtodFullPrecision(double d, int p, const char* decimals, size_t RAPIDJSON_ASSERT(d >= 0.0); RAPIDJSON_ASSERT(length >= 1); - double result; + double result = 0.0; if (StrtodFast(d, p, &result)) return result; + RAPIDJSON_ASSERT(length <= INT_MAX); + int dLen = static_cast(length); + + RAPIDJSON_ASSERT(length >= decimalPosition); + RAPIDJSON_ASSERT(length - decimalPosition <= INT_MAX); + int dExpAdjust = static_cast(length - decimalPosition); + + RAPIDJSON_ASSERT(exp >= INT_MIN + dExpAdjust); + int dExp = exp - dExpAdjust; + + // Make sure length+dExp does not overflow + RAPIDJSON_ASSERT(dExp <= INT_MAX - dLen); + // Trim leading zeros - while (*decimals == '0' && length > 1) { - length--; + while (dLen > 0 && *decimals == '0') { + dLen--; decimals++; - decimalPosition--; } // Trim trailing zeros - while (decimals[length - 1] == '0' && length > 1) { - length--; - decimalPosition--; - exp++; + while (dLen > 0 && decimals[dLen - 1] == '0') { + dLen--; + dExp++; + } + + if (dLen == 0) { // Buffer only contains zeros. + return 0.0; } // Trim right-most digits - const int kMaxDecimalDigit = 780; - if (static_cast(length) > kMaxDecimalDigit) { - int delta = (static_cast(length) - kMaxDecimalDigit); - exp += delta; - decimalPosition -= static_cast(delta); - length = kMaxDecimalDigit; + const int kMaxDecimalDigit = 767 + 1; + if (dLen > kMaxDecimalDigit) { + dExp += dLen - kMaxDecimalDigit; + dLen = kMaxDecimalDigit; } - // If too small, underflow to zero - if (int(length) + exp < -324) + // If too small, underflow to zero. + // Any x <= 10^-324 is interpreted as zero. + if (dLen + dExp <= -324) return 0.0; - if (StrtodDiyFp(decimals, length, decimalPosition, exp, &result)) + // If too large, overflow to infinity. + // Any x >= 10^309 is interpreted as +infinity. + if (dLen + dExp > 309) + return std::numeric_limits::infinity(); + + if (StrtodDiyFp(decimals, dLen, dExp, &result)) return result; // Use approximation from StrtodDiyFp and make adjustment with BigInteger comparison - return StrtodBigInteger(result, decimals, length, decimalPosition, exp); + return StrtodBigInteger(result, decimals, dLen, dExp); } } // namespace internal diff --git a/src/3rdparty/rapidjson/istreamwrapper.h b/src/3rdparty/rapidjson/istreamwrapper.h index f5fe2897..c4950b9d 100644 --- a/src/3rdparty/rapidjson/istreamwrapper.h +++ b/src/3rdparty/rapidjson/istreamwrapper.h @@ -17,13 +17,12 @@ #include "stream.h" #include +#include #ifdef __clang__ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(padded) -#endif - -#ifdef _MSC_VER +#elif defined(_MSC_VER) RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(4351) // new behavior: elements of array 'array' will be default initialized #endif @@ -50,57 +49,71 @@ template class BasicIStreamWrapper { public: typedef typename StreamType::char_type Ch; - BasicIStreamWrapper(StreamType& stream) : stream_(stream), count_(), peekBuffer_() {} - Ch Peek() const { - typename StreamType::int_type c = stream_.peek(); - return RAPIDJSON_LIKELY(c != StreamType::traits_type::eof()) ? static_cast(c) : '\0'; + //! Constructor. + /*! + \param stream stream opened for read. + */ + BasicIStreamWrapper(StreamType &stream) : stream_(stream), buffer_(peekBuffer_), bufferSize_(4), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) { + Read(); } - Ch Take() { - typename StreamType::int_type c = stream_.get(); - if (RAPIDJSON_LIKELY(c != StreamType::traits_type::eof())) { - count_++; - return static_cast(c); - } - else - return '\0'; + //! Constructor. + /*! + \param stream stream opened for read. + \param buffer user-supplied buffer. + \param bufferSize size of buffer in bytes. Must >=4 bytes. + */ + BasicIStreamWrapper(StreamType &stream, char* buffer, size_t bufferSize) : stream_(stream), buffer_(buffer), bufferSize_(bufferSize), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) { + RAPIDJSON_ASSERT(bufferSize >= 4); + Read(); } - // tellg() may return -1 when failed. So we count by ourself. - size_t Tell() const { return count_; } + Ch Peek() const { return *current_; } + Ch Take() { Ch c = *current_; Read(); return c; } + size_t Tell() const { return count_ + static_cast(current_ - buffer_); } - Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + // Not implemented void Put(Ch) { RAPIDJSON_ASSERT(false); } - void Flush() { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } // For encoding detection only. const Ch* Peek4() const { - RAPIDJSON_ASSERT(sizeof(Ch) == 1); // Only usable for byte stream. - int i; - bool hasError = false; - for (i = 0; i < 4; ++i) { - typename StreamType::int_type c = stream_.get(); - if (c == StreamType::traits_type::eof()) { - hasError = true; - stream_.clear(); - break; - } - peekBuffer_[i] = static_cast(c); - } - for (--i; i >= 0; --i) - stream_.putback(peekBuffer_[i]); - return !hasError ? peekBuffer_ : 0; + return (current_ + 4 - !eof_ <= bufferLast_) ? current_ : 0; } private: + BasicIStreamWrapper(); BasicIStreamWrapper(const BasicIStreamWrapper&); BasicIStreamWrapper& operator=(const BasicIStreamWrapper&); - StreamType& stream_; - size_t count_; //!< Number of characters read. Note: - mutable Ch peekBuffer_[4]; + void Read() { + if (current_ < bufferLast_) + ++current_; + else if (!eof_) { + count_ += readCount_; + readCount_ = bufferSize_; + bufferLast_ = buffer_ + readCount_ - 1; + current_ = buffer_; + + if (!stream_.read(buffer_, static_cast(bufferSize_))) { + readCount_ = static_cast(stream_.gcount()); + *(bufferLast_ = buffer_ + readCount_) = '\0'; + eof_ = true; + } + } + } + + StreamType &stream_; + Ch peekBuffer_[4], *buffer_; + size_t bufferSize_; + Ch *bufferLast_; + Ch *current_; + size_t readCount_; + size_t count_; //!< Number of characters read + bool eof_; }; typedef BasicIStreamWrapper IStreamWrapper; diff --git a/src/3rdparty/rapidjson/license.txt b/src/3rdparty/rapidjson/license.txt new file mode 100644 index 00000000..7ccc161c --- /dev/null +++ b/src/3rdparty/rapidjson/license.txt @@ -0,0 +1,57 @@ +Tencent is pleased to support the open source community by making RapidJSON available. + +Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. + +If you have downloaded a copy of the RapidJSON binary from Tencent, please note that the RapidJSON binary is licensed under the MIT License. +If you have downloaded a copy of the RapidJSON source code from Tencent, please note that RapidJSON source code is licensed under the MIT License, except for the third-party components listed below which are subject to different license terms. Your integration of RapidJSON into your own projects may require compliance with the MIT License, as well as the other licenses applicable to the third-party components included within RapidJSON. To avoid the problematic JSON license in your own projects, it's sufficient to exclude the bin/jsonchecker/ directory, as it's the only code under the JSON license. +A copy of the MIT License is included in this file. + +Other dependencies and licenses: + +Open Source Software Licensed Under the BSD License: +-------------------------------------------------------------------- + +The msinttypes r29 +Copyright (c) 2006-2013 Alexander Chemeris +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +* Neither the name of copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Open Source Software Licensed Under the JSON License: +-------------------------------------------------------------------- + +json.org +Copyright (c) 2002 JSON.org +All Rights Reserved. + +JSON_checker +Copyright (c) 2002 JSON.org +All Rights Reserved. + + +Terms of the JSON License: +--------------------------------------------------- + +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 shall be used for Good, not Evil. + +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. + + +Terms of the MIT License: +-------------------------------------------------------------------- + +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. diff --git a/src/3rdparty/rapidjson/pointer.h b/src/3rdparty/rapidjson/pointer.h index 0206ac1c..063abab9 100644 --- a/src/3rdparty/rapidjson/pointer.h +++ b/src/3rdparty/rapidjson/pointer.h @@ -21,9 +21,7 @@ #ifdef __clang__ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(switch-enum) -#endif - -#ifdef _MSC_VER +#elif defined(_MSC_VER) RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated #endif @@ -165,7 +163,12 @@ public: GenericPointer(const Token* tokens, size_t tokenCount) : allocator_(), ownAllocator_(), nameBuffer_(), tokens_(const_cast(tokens)), tokenCount_(tokenCount), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {} //! Copy constructor. - GenericPointer(const GenericPointer& rhs, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + GenericPointer(const GenericPointer& rhs) : allocator_(rhs.allocator_), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + *this = rhs; + } + + //! Copy constructor. + GenericPointer(const GenericPointer& rhs, Allocator* allocator) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { *this = rhs; } @@ -197,6 +200,36 @@ public: return *this; } + //! Swap the content of this pointer with an other. + /*! + \param other The pointer to swap with. + \note Constant complexity. + */ + GenericPointer& Swap(GenericPointer& other) RAPIDJSON_NOEXCEPT { + internal::Swap(allocator_, other.allocator_); + internal::Swap(ownAllocator_, other.ownAllocator_); + internal::Swap(nameBuffer_, other.nameBuffer_); + internal::Swap(tokens_, other.tokens_); + internal::Swap(tokenCount_, other.tokenCount_); + internal::Swap(parseErrorOffset_, other.parseErrorOffset_); + internal::Swap(parseErrorCode_, other.parseErrorCode_); + return *this; + } + + //! free-standing swap function helper + /*! + Helper function to enable support for common swap implementation pattern based on \c std::swap: + \code + void swap(MyClass& a, MyClass& b) { + using std::swap; + swap(a.pointer, b.pointer); + // ... + } + \endcode + \see Swap() + */ + friend inline void swap(GenericPointer& a, GenericPointer& b) RAPIDJSON_NOEXCEPT { a.Swap(b); } + //@} //!@name Append token @@ -240,7 +273,7 @@ public: template RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >), (GenericPointer)) Append(T* name, Allocator* allocator = 0) const { - return Append(name, StrLen(name), allocator); + return Append(name, internal::StrLen(name), allocator); } #if RAPIDJSON_HAS_STDSTRING @@ -274,7 +307,7 @@ public: else { Ch name[21]; for (size_t i = 0; i <= length; i++) - name[i] = buffer[i]; + name[i] = static_cast(buffer[i]); Token token = { name, length, index }; return Append(token, allocator); } @@ -353,6 +386,33 @@ public: */ bool operator!=(const GenericPointer& rhs) const { return !(*this == rhs); } + //! Less than operator. + /*! + \note Invalid pointers are always greater than valid ones. + */ + bool operator<(const GenericPointer& rhs) const { + if (!IsValid()) + return false; + if (!rhs.IsValid()) + return true; + + if (tokenCount_ != rhs.tokenCount_) + return tokenCount_ < rhs.tokenCount_; + + for (size_t i = 0; i < tokenCount_; i++) { + if (tokens_[i].index != rhs.tokens_[i].index) + return tokens_[i].index < rhs.tokens_[i].index; + + if (tokens_[i].length != rhs.tokens_[i].length) + return tokens_[i].length < rhs.tokens_[i].length; + + if (int cmp = std::memcmp(tokens_[i].name, rhs.tokens_[i].name, sizeof(Ch) * tokens_[i].length)) + return cmp < 0; + } + + return false; + } + //@} //!@name Stringify @@ -532,14 +592,14 @@ public: */ ValueType& GetWithDefault(ValueType& root, const ValueType& defaultValue, typename ValueType::AllocatorType& allocator) const { bool alreadyExist; - Value& v = Create(root, allocator, &alreadyExist); + ValueType& v = Create(root, allocator, &alreadyExist); return alreadyExist ? v : v.CopyFrom(defaultValue, allocator); } //! Query a value in a subtree with default null-terminated string. ValueType& GetWithDefault(ValueType& root, const Ch* defaultValue, typename ValueType::AllocatorType& allocator) const { bool alreadyExist; - Value& v = Create(root, allocator, &alreadyExist); + ValueType& v = Create(root, allocator, &alreadyExist); return alreadyExist ? v : v.SetString(defaultValue, allocator); } @@ -547,7 +607,7 @@ public: //! Query a value in a subtree with default std::basic_string. ValueType& GetWithDefault(ValueType& root, const std::basic_string& defaultValue, typename ValueType::AllocatorType& allocator) const { bool alreadyExist; - Value& v = Create(root, allocator, &alreadyExist); + ValueType& v = Create(root, allocator, &alreadyExist); return alreadyExist ? v : v.SetString(defaultValue, allocator); } #endif @@ -758,7 +818,7 @@ private: */ Ch* CopyFromRaw(const GenericPointer& rhs, size_t extraToken = 0, size_t extraNameBufferSize = 0) { if (!allocator_) // allocator is independently owned. - ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); size_t nameBufferSize = rhs.tokenCount_; // null terminators for tokens for (Token *t = rhs.tokens_; t != rhs.tokens_ + rhs.tokenCount_; ++t) @@ -806,7 +866,7 @@ private: // Create own allocator if user did not supply. if (!allocator_) - ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); // Count number of '/' as tokenCount tokenCount_ = 0; @@ -1029,8 +1089,8 @@ private: unsigned char u = static_cast(c); static const char hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; os_.Put('%'); - os_.Put(hexDigits[u >> 4]); - os_.Put(hexDigits[u & 15]); + os_.Put(static_cast(hexDigits[u >> 4])); + os_.Put(static_cast(hexDigits[u & 15])); } private: OutputStream& os_; @@ -1347,11 +1407,7 @@ bool EraseValueByPointer(T& root, const CharType(&source)[N]) { RAPIDJSON_NAMESPACE_END -#ifdef __clang__ -RAPIDJSON_DIAG_POP -#endif - -#ifdef _MSC_VER +#if defined(__clang__) || defined(_MSC_VER) RAPIDJSON_DIAG_POP #endif diff --git a/src/3rdparty/rapidjson/prettywriter.h b/src/3rdparty/rapidjson/prettywriter.h index 0dcb0fee..c7c29b21 100644 --- a/src/3rdparty/rapidjson/prettywriter.h +++ b/src/3rdparty/rapidjson/prettywriter.h @@ -22,6 +22,11 @@ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(effc++) #endif +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + RAPIDJSON_NAMESPACE_BEGIN //! Combination of PrettyWriter format flags. @@ -34,7 +39,7 @@ enum PrettyFormatOptions { //! Writer with indentation and spacing. /*! - \tparam OutputStream Type of ouptut os. + \tparam OutputStream Type of output os. \tparam SourceEncoding Encoding of source string. \tparam TargetEncoding Encoding of output stream. \tparam StackAllocator Type of allocator for allocating memory of stack. @@ -42,7 +47,7 @@ enum PrettyFormatOptions { template, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags> class PrettyWriter : public Writer { public: - typedef Writer Base; + typedef Writer Base; typedef typename Base::Ch Ch; //! Constructor @@ -57,6 +62,11 @@ public: explicit PrettyWriter(StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : Base(allocator, levelDepth), indentChar_(' '), indentCharCount_(4) {} +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + PrettyWriter(PrettyWriter&& rhs) : + Base(std::forward(rhs)), indentChar_(rhs.indentChar_), indentCharCount_(rhs.indentCharCount_), formatOptions_(rhs.formatOptions_) {} +#endif + //! Set custom indentation. /*! \param indentChar Character for indentation. Must be whitespace character (' ', '\\t', '\\n', '\\r'). \param indentCharCount Number of indent characters for each indentation level. @@ -82,24 +92,26 @@ public: */ //@{ - bool Null() { PrettyPrefix(kNullType); return Base::WriteNull(); } - bool Bool(bool b) { PrettyPrefix(b ? kTrueType : kFalseType); return Base::WriteBool(b); } - bool Int(int i) { PrettyPrefix(kNumberType); return Base::WriteInt(i); } - bool Uint(unsigned u) { PrettyPrefix(kNumberType); return Base::WriteUint(u); } - bool Int64(int64_t i64) { PrettyPrefix(kNumberType); return Base::WriteInt64(i64); } - bool Uint64(uint64_t u64) { PrettyPrefix(kNumberType); return Base::WriteUint64(u64); } - bool Double(double d) { PrettyPrefix(kNumberType); return Base::WriteDouble(d); } + bool Null() { PrettyPrefix(kNullType); return Base::EndValue(Base::WriteNull()); } + bool Bool(bool b) { PrettyPrefix(b ? kTrueType : kFalseType); return Base::EndValue(Base::WriteBool(b)); } + bool Int(int i) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteInt(i)); } + bool Uint(unsigned u) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteUint(u)); } + bool Int64(int64_t i64) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteInt64(i64)); } + bool Uint64(uint64_t u64) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteUint64(u64)); } + bool Double(double d) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteDouble(d)); } bool RawNumber(const Ch* str, SizeType length, bool copy = false) { + RAPIDJSON_ASSERT(str != 0); (void)copy; PrettyPrefix(kNumberType); - return Base::WriteString(str, length); + return Base::EndValue(Base::WriteString(str, length)); } bool String(const Ch* str, SizeType length, bool copy = false) { + RAPIDJSON_ASSERT(str != 0); (void)copy; PrettyPrefix(kStringType); - return Base::WriteString(str, length); + return Base::EndValue(Base::WriteString(str, length)); } #if RAPIDJSON_HAS_STDSTRING @@ -124,19 +136,21 @@ public: bool EndObject(SizeType memberCount = 0) { (void)memberCount; - RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); - RAPIDJSON_ASSERT(!Base::level_stack_.template Top()->inArray); + RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); // not inside an Object + RAPIDJSON_ASSERT(!Base::level_stack_.template Top()->inArray); // currently inside an Array, not Object + RAPIDJSON_ASSERT(0 == Base::level_stack_.template Top()->valueCount % 2); // Object has a Key without a Value + bool empty = Base::level_stack_.template Pop(1)->valueCount == 0; if (!empty) { Base::os_->Put('\n'); WriteIndent(); } - bool ret = Base::WriteEndObject(); + bool ret = Base::EndValue(Base::WriteEndObject()); (void)ret; RAPIDJSON_ASSERT(ret == true); if (Base::level_stack_.Empty()) // end of json text - Base::os_->Flush(); + Base::Flush(); return true; } @@ -150,17 +164,18 @@ public: (void)memberCount; RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); RAPIDJSON_ASSERT(Base::level_stack_.template Top()->inArray); - bool empty = Base::level_stack_.template Pop(1)->valueCount == 0; + typename Base::Level* level = Base::level_stack_.template Pop(1); + bool empty = level->valueCount == 0; - if (!empty && !(formatOptions_ & kFormatSingleLineArray)) { + if (!empty && !level->inLine) { Base::os_->Put('\n'); WriteIndent(); } - bool ret = Base::WriteEndArray(); + bool ret = Base::EndValue(Base::WriteEndArray()); (void)ret; RAPIDJSON_ASSERT(ret == true); if (Base::level_stack_.Empty()) // end of json text - Base::os_->Flush(); + Base::Flush(); return true; } @@ -184,7 +199,11 @@ public: \param type Type of the root of json. \note When using PrettyWriter::RawValue(), the result json may not be indented correctly. */ - bool RawValue(const Ch* json, size_t length, Type type) { PrettyPrefix(type); return Base::WriteRawValue(json, length); } + bool RawValue(const Ch* json, size_t length, Type type) { + RAPIDJSON_ASSERT(json != 0); + PrettyPrefix(type); + return Base::EndValue(Base::WriteRawValue(json, length)); + } protected: void PrettyPrefix(Type type) { @@ -193,13 +212,16 @@ protected: typename Base::Level* level = Base::level_stack_.template Top(); if (level->inArray) { + level->inLine = (formatOptions_ & kFormatSingleLineArray) && type != kObjectType && type != kArrayType; + if (level->valueCount > 0) { Base::os_->Put(','); // add comma if it is not the first element in array - if (formatOptions_ & kFormatSingleLineArray) + if (level->inLine) { Base::os_->Put(' '); + } } - if (!(formatOptions_ & kFormatSingleLineArray)) { + if (!level->inLine) { Base::os_->Put('\n'); WriteIndent(); } @@ -233,7 +255,7 @@ protected: void WriteIndent() { size_t count = (Base::level_stack_.GetSize() / sizeof(typename Base::Level)) * indentCharCount_; - PutN(*Base::os_, static_cast(indentChar_), count); + PutN(*Base::os_, static_cast(indentChar_), count); } Ch indentChar_; @@ -248,6 +270,10 @@ private: RAPIDJSON_NAMESPACE_END +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + #ifdef __GNUC__ RAPIDJSON_DIAG_POP #endif diff --git a/src/3rdparty/rapidjson/rapidjson.h b/src/3rdparty/rapidjson/rapidjson.h index 2ef9bc56..78c8aae0 100644 --- a/src/3rdparty/rapidjson/rapidjson.h +++ b/src/3rdparty/rapidjson/rapidjson.h @@ -26,7 +26,7 @@ Some RapidJSON features are configurable to adapt the library to a wide variety of platforms, environments and usage scenarios. Most of the - features can be configured in terms of overriden or predefined + features can be configured in terms of overridden or predefined preprocessor macros at compile-time. Some additional customization is available in the \ref RAPIDJSON_ERRORS APIs. @@ -49,6 +49,11 @@ // token stringification #define RAPIDJSON_STRINGIFY(x) RAPIDJSON_DO_STRINGIFY(x) #define RAPIDJSON_DO_STRINGIFY(x) #x + +// token concatenation +#define RAPIDJSON_JOIN(X, Y) RAPIDJSON_DO_JOIN(X, Y) +#define RAPIDJSON_DO_JOIN(X, Y) RAPIDJSON_DO_JOIN2(X, Y) +#define RAPIDJSON_DO_JOIN2(X, Y) X##Y //!@endcond /*! \def RAPIDJSON_MAJOR_VERSION @@ -214,7 +219,7 @@ # elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ # define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN # else -# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN. +# error Unknown machine endianness detected. User needs to define RAPIDJSON_ENDIAN. # endif // __BYTE_ORDER__ // Detect with GLIBC's endian.h # elif defined(__GLIBC__) @@ -224,7 +229,7 @@ # elif (__BYTE_ORDER == __BIG_ENDIAN) # define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN # else -# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN. +# error Unknown machine endianness detected. User needs to define RAPIDJSON_ENDIAN. # endif // __GLIBC__ // Detect with _LITTLE_ENDIAN and _BIG_ENDIAN macro # elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN) @@ -236,12 +241,12 @@ # define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN # elif defined(__i386__) || defined(__alpha__) || defined(__ia64) || defined(__ia64__) || defined(_M_IX86) || defined(_M_IA64) || defined(_M_ALPHA) || defined(__amd64) || defined(__amd64__) || defined(_M_AMD64) || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || defined(__bfin__) # define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN -# elif defined(_MSC_VER) && defined(_M_ARM) +# elif defined(_MSC_VER) && (defined(_M_ARM) || defined(_M_ARM64)) # define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN # elif defined(RAPIDJSON_DOXYGEN_RUNNING) # define RAPIDJSON_ENDIAN # else -# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN. +# error Unknown machine endianness detected. User needs to define RAPIDJSON_ENDIAN. # endif #endif // RAPIDJSON_ENDIAN @@ -264,16 +269,11 @@ /*! \ingroup RAPIDJSON_CONFIG \param x pointer to align - Some machines require strict data alignment. Currently the default uses 4 bytes - alignment on 32-bit platforms and 8 bytes alignment for 64-bit platforms. + Some machines require strict data alignment. The default is 8 bytes. User can customize by defining the RAPIDJSON_ALIGN function macro. */ #ifndef RAPIDJSON_ALIGN -#if RAPIDJSON_64BIT == 1 -#define RAPIDJSON_ALIGN(x) (((x) + static_cast(7u)) & ~static_cast(7u)) -#else -#define RAPIDJSON_ALIGN(x) (((x) + 3u) & ~3u) -#endif +#define RAPIDJSON_ALIGN(x) (((x) + static_cast(7u)) & ~static_cast(7u)) #endif /////////////////////////////////////////////////////////////////////////////// @@ -320,17 +320,17 @@ #endif /////////////////////////////////////////////////////////////////////////////// -// RAPIDJSON_SSE2/RAPIDJSON_SSE42/RAPIDJSON_SIMD +// RAPIDJSON_SSE2/RAPIDJSON_SSE42/RAPIDJSON_NEON/RAPIDJSON_SIMD /*! \def RAPIDJSON_SIMD \ingroup RAPIDJSON_CONFIG - \brief Enable SSE2/SSE4.2 optimization. + \brief Enable SSE2/SSE4.2/Neon optimization. RapidJSON supports optimized implementations for some parsing operations - based on the SSE2 or SSE4.2 SIMD extensions on modern Intel-compatible - processors. + based on the SSE2, SSE4.2 or NEon SIMD extensions on modern Intel + or ARM compatible processors. - To enable these optimizations, two different symbols can be defined; + To enable these optimizations, three different symbols can be defined; \code // Enable SSE2 optimization. #define RAPIDJSON_SSE2 @@ -339,13 +339,17 @@ #define RAPIDJSON_SSE42 \endcode - \c RAPIDJSON_SSE42 takes precedence, if both are defined. + // Enable ARM Neon optimization. + #define RAPIDJSON_NEON + \endcode + + \c RAPIDJSON_SSE42 takes precedence over SSE2, if both are defined. If any of these symbols is defined, RapidJSON defines the macro \c RAPIDJSON_SIMD to indicate the availability of the optimized code. */ #if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) \ - || defined(RAPIDJSON_DOXYGEN_RUNNING) + || defined(RAPIDJSON_NEON) || defined(RAPIDJSON_DOXYGEN_RUNNING) #define RAPIDJSON_SIMD #endif @@ -398,13 +402,22 @@ RAPIDJSON_NAMESPACE_END \ref RAPIDJSON_ERRORS APIs. */ #ifndef RAPIDJSON_ASSERT +#include #define RAPIDJSON_ASSERT(x) #endif // RAPIDJSON_ASSERT /////////////////////////////////////////////////////////////////////////////// // RAPIDJSON_STATIC_ASSERT -// Adopt from boost +// Prefer C++11 static_assert, if available +#ifndef RAPIDJSON_STATIC_ASSERT +#if __cplusplus >= 201103L || ( defined(_MSC_VER) && _MSC_VER >= 1800 ) +#define RAPIDJSON_STATIC_ASSERT(x) \ + static_assert(x, RAPIDJSON_STRINGIFY(x)) +#endif // C++11 +#endif // RAPIDJSON_STATIC_ASSERT + +// Adopt C++03 implementation from boost #ifndef RAPIDJSON_STATIC_ASSERT #ifndef __clang__ //!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN @@ -412,14 +425,10 @@ RAPIDJSON_NAMESPACE_END RAPIDJSON_NAMESPACE_BEGIN template struct STATIC_ASSERTION_FAILURE; template <> struct STATIC_ASSERTION_FAILURE { enum { value = 1 }; }; -template struct StaticAssertTest {}; +template struct StaticAssertTest {}; RAPIDJSON_NAMESPACE_END -#define RAPIDJSON_JOIN(X, Y) RAPIDJSON_DO_JOIN(X, Y) -#define RAPIDJSON_DO_JOIN(X, Y) RAPIDJSON_DO_JOIN2(X, Y) -#define RAPIDJSON_DO_JOIN2(X, Y) X##Y - -#if defined(__GNUC__) +#if defined(__GNUC__) || defined(__clang__) #define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE __attribute__((unused)) #else #define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE @@ -437,7 +446,7 @@ RAPIDJSON_NAMESPACE_END typedef ::RAPIDJSON_NAMESPACE::StaticAssertTest< \ sizeof(::RAPIDJSON_NAMESPACE::STATIC_ASSERTION_FAILURE)> \ RAPIDJSON_JOIN(StaticAssertTypedef, __LINE__) RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE -#endif +#endif // RAPIDJSON_STATIC_ASSERT /////////////////////////////////////////////////////////////////////////////// // RAPIDJSON_LIKELY, RAPIDJSON_UNLIKELY @@ -529,13 +538,14 @@ RAPIDJSON_NAMESPACE_END #ifndef RAPIDJSON_HAS_CXX11_RVALUE_REFS #if defined(__clang__) #if __has_feature(cxx_rvalue_references) && \ - (defined(_LIBCPP_VERSION) || defined(__GLIBCXX__) && __GLIBCXX__ >= 20080306) + (defined(_MSC_VER) || defined(_LIBCPP_VERSION) || defined(__GLIBCXX__) && __GLIBCXX__ >= 20080306) #define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 #else #define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0 #endif #elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,3,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ - (defined(_MSC_VER) && _MSC_VER >= 1600) + (defined(_MSC_VER) && _MSC_VER >= 1600) || \ + (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x5140 && defined(__GXX_EXPERIMENTAL_CXX0X__)) #define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 #else @@ -546,8 +556,9 @@ RAPIDJSON_NAMESPACE_END #ifndef RAPIDJSON_HAS_CXX11_NOEXCEPT #if defined(__clang__) #define RAPIDJSON_HAS_CXX11_NOEXCEPT __has_feature(cxx_noexcept) -#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) -// (defined(_MSC_VER) && _MSC_VER >= ????) // not yet supported +#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ + (defined(_MSC_VER) && _MSC_VER >= 1900) || \ + (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x5140 && defined(__GXX_EXPERIMENTAL_CXX0X__)) #define RAPIDJSON_HAS_CXX11_NOEXCEPT 1 #else #define RAPIDJSON_HAS_CXX11_NOEXCEPT 0 @@ -561,14 +572,19 @@ RAPIDJSON_NAMESPACE_END // no automatic detection, yet #ifndef RAPIDJSON_HAS_CXX11_TYPETRAITS +#if (defined(_MSC_VER) && _MSC_VER >= 1700) +#define RAPIDJSON_HAS_CXX11_TYPETRAITS 1 +#else #define RAPIDJSON_HAS_CXX11_TYPETRAITS 0 #endif +#endif #ifndef RAPIDJSON_HAS_CXX11_RANGE_FOR #if defined(__clang__) #define RAPIDJSON_HAS_CXX11_RANGE_FOR __has_feature(cxx_range_for) -#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,3,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ - (defined(_MSC_VER) && _MSC_VER >= 1700) +#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ + (defined(_MSC_VER) && _MSC_VER >= 1700) || \ + (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x5140 && defined(__GXX_EXPERIMENTAL_CXX0X__)) #define RAPIDJSON_HAS_CXX11_RANGE_FOR 1 #else #define RAPIDJSON_HAS_CXX11_RANGE_FOR 0 @@ -577,12 +593,38 @@ RAPIDJSON_NAMESPACE_END //!@endcond +//! Assertion (in non-throwing contexts). + /*! \ingroup RAPIDJSON_CONFIG + Some functions provide a \c noexcept guarantee, if the compiler supports it. + In these cases, the \ref RAPIDJSON_ASSERT macro cannot be overridden to + throw an exception. This macro adds a separate customization point for + such cases. + + Defaults to C \c assert() (as \ref RAPIDJSON_ASSERT), if \c noexcept is + supported, and to \ref RAPIDJSON_ASSERT otherwise. + */ + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_NOEXCEPT_ASSERT + +#ifndef RAPIDJSON_NOEXCEPT_ASSERT +#ifdef RAPIDJSON_ASSERT_THROWS +#if RAPIDJSON_HAS_CXX11_NOEXCEPT +#define RAPIDJSON_NOEXCEPT_ASSERT(x) +#else +#define RAPIDJSON_NOEXCEPT_ASSERT(x) RAPIDJSON_ASSERT(x) +#endif // RAPIDJSON_HAS_CXX11_NOEXCEPT +#else +#define RAPIDJSON_NOEXCEPT_ASSERT(x) RAPIDJSON_ASSERT(x) +#endif // RAPIDJSON_ASSERT_THROWS +#endif // RAPIDJSON_NOEXCEPT_ASSERT + /////////////////////////////////////////////////////////////////////////////// // new/delete #ifndef RAPIDJSON_NEW ///! customization point for global \c new -#define RAPIDJSON_NEW(x) new x +#define RAPIDJSON_NEW(TypeName) new TypeName #endif #ifndef RAPIDJSON_DELETE ///! customization point for global \c delete diff --git a/src/3rdparty/rapidjson/reader.h b/src/3rdparty/rapidjson/reader.h index 303aac2e..44a6bcd3 100644 --- a/src/3rdparty/rapidjson/reader.h +++ b/src/3rdparty/rapidjson/reader.h @@ -33,12 +33,8 @@ #include #elif defined(RAPIDJSON_SSE2) #include -#endif - -#ifdef _MSC_VER -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant -RAPIDJSON_DIAG_OFF(4702) // unreachable code +#elif defined(RAPIDJSON_NEON) +#include #endif #ifdef __clang__ @@ -46,6 +42,10 @@ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(old-style-cast) RAPIDJSON_DIAG_OFF(padded) RAPIDJSON_DIAG_OFF(switch-enum) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +RAPIDJSON_DIAG_OFF(4702) // unreachable code #endif #ifdef __GNUC__ @@ -136,7 +136,7 @@ RAPIDJSON_NAMESPACE_BEGIN User can define this as any \c ParseFlag combinations. */ #ifndef RAPIDJSON_PARSE_DEFAULT_FLAGS -#define RAPIDJSON_PARSE_DEFAULT_FLAGS kParseCommentsFlag | kParseTrailingCommasFlag +#define RAPIDJSON_PARSE_DEFAULT_FLAGS kParseNoFlags #endif //! Combination of parseFlags @@ -299,16 +299,9 @@ inline const char *SkipWhitespace_SIMD(const char* p) { for (;; p += 16) { const __m128i s = _mm_load_si128(reinterpret_cast(p)); - const int r = _mm_cvtsi128_si32(_mm_cmpistrm(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_BIT_MASK | _SIDD_NEGATIVE_POLARITY)); - if (r != 0) { // some of characters is non-whitespace -#ifdef _MSC_VER // Find the index of first non-whitespace - unsigned long offset; - _BitScanForward(&offset, r); - return p + offset; -#else - return p + __builtin_ffs(r) - 1; -#endif - } + const int r = _mm_cmpistri(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT | _SIDD_NEGATIVE_POLARITY); + if (r != 16) // some of characters is non-whitespace + return p + r; } } @@ -325,16 +318,9 @@ inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { for (; p <= end - 16; p += 16) { const __m128i s = _mm_loadu_si128(reinterpret_cast(p)); - const int r = _mm_cvtsi128_si32(_mm_cmpistrm(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_BIT_MASK | _SIDD_NEGATIVE_POLARITY)); - if (r != 0) { // some of characters is non-whitespace -#ifdef _MSC_VER // Find the index of first non-whitespace - unsigned long offset; - _BitScanForward(&offset, r); - return p + offset; -#else - return p + __builtin_ffs(r) - 1; -#endif - } + const int r = _mm_cmpistri(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT | _SIDD_NEGATIVE_POLARITY); + if (r != 16) // some of characters is non-whitespace + return p + r; } return SkipWhitespace(p, end); @@ -425,7 +411,92 @@ inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { return SkipWhitespace(p, end); } -#endif // RAPIDJSON_SSE2 +#elif defined(RAPIDJSON_NEON) + +//! Skip whitespace with ARM Neon instructions, testing 16 8-byte characters at once. +inline const char *SkipWhitespace_SIMD(const char* p) { + // Fast return for single non-whitespace + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // 16-byte align to the next boundary + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + const uint8x16_t w0 = vmovq_n_u8(' '); + const uint8x16_t w1 = vmovq_n_u8('\n'); + const uint8x16_t w2 = vmovq_n_u8('\r'); + const uint8x16_t w3 = vmovq_n_u8('\t'); + + for (;; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, w0); + x = vorrq_u8(x, vceqq_u8(s, w1)); + x = vorrq_u8(x, vceqq_u8(s, w2)); + x = vorrq_u8(x, vceqq_u8(s, w3)); + + x = vmvnq_u8(x); // Negate + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(reinterpret_cast(x), 0); // extract + uint64_t high = vgetq_lane_u64(reinterpret_cast(x), 1); // extract + + if (low == 0) { + if (high != 0) { + int lz =__builtin_clzll(high);; + return p + 8 + (lz >> 3); + } + } else { + int lz = __builtin_clzll(low);; + return p + (lz >> 3); + } + } +} + +inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { + // Fast return for single non-whitespace + if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) + ++p; + else + return p; + + const uint8x16_t w0 = vmovq_n_u8(' '); + const uint8x16_t w1 = vmovq_n_u8('\n'); + const uint8x16_t w2 = vmovq_n_u8('\r'); + const uint8x16_t w3 = vmovq_n_u8('\t'); + + for (; p <= end - 16; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, w0); + x = vorrq_u8(x, vceqq_u8(s, w1)); + x = vorrq_u8(x, vceqq_u8(s, w2)); + x = vorrq_u8(x, vceqq_u8(s, w3)); + + x = vmvnq_u8(x); // Negate + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(reinterpret_cast(x), 0); // extract + uint64_t high = vgetq_lane_u64(reinterpret_cast(x), 1); // extract + + if (low == 0) { + if (high != 0) { + int lz = __builtin_clzll(high); + return p + 8 + (lz >> 3); + } + } else { + int lz = __builtin_clzll(low); + return p + (lz >> 3); + } + } + + return SkipWhitespace(p, end); +} + +#endif // RAPIDJSON_NEON #ifdef RAPIDJSON_SIMD //! Template function specialization for InsituStringStream @@ -471,7 +542,8 @@ public: /*! \param stackAllocator Optional allocator for allocating stack memory. (Only use for non-destructive parsing) \param stackCapacity stack capacity in bytes for storing a single decoded string. (Only use for non-destructive parsing) */ - GenericReader(StackAllocator* stackAllocator = 0, size_t stackCapacity = kDefaultStackCapacity) : stack_(stackAllocator, stackCapacity), parseResult_() {} + GenericReader(StackAllocator* stackAllocator = 0, size_t stackCapacity = kDefaultStackCapacity) : + stack_(stackAllocator, stackCapacity), parseResult_(), state_(IterativeParsingStartState) {} //! Parse JSON text. /*! \tparam parseFlags Combination of \ref ParseFlag. @@ -527,7 +599,84 @@ public: return Parse(is, handler); } - //! Whether a parse error has occured in the last parsing. + //! Initialize JSON text token-by-token parsing + /*! + */ + void IterativeParseInit() { + parseResult_.Clear(); + state_ = IterativeParsingStartState; + } + + //! Parse one token from JSON text + /*! \tparam InputStream Type of input stream, implementing Stream concept + \tparam Handler Type of handler, implementing Handler concept. + \param is Input stream to be parsed. + \param handler The handler to receive events. + \return Whether the parsing is successful. + */ + template + bool IterativeParseNext(InputStream& is, Handler& handler) { + while (RAPIDJSON_LIKELY(is.Peek() != '\0')) { + SkipWhitespaceAndComments(is); + + Token t = Tokenize(is.Peek()); + IterativeParsingState n = Predict(state_, t); + IterativeParsingState d = Transit(state_, t, n, is, handler); + + // If we've finished or hit an error... + if (RAPIDJSON_UNLIKELY(IsIterativeParsingCompleteState(d))) { + // Report errors. + if (d == IterativeParsingErrorState) { + HandleError(state_, is); + return false; + } + + // Transition to the finish state. + RAPIDJSON_ASSERT(d == IterativeParsingFinishState); + state_ = d; + + // If StopWhenDone is not set... + if (!(parseFlags & kParseStopWhenDoneFlag)) { + // ... and extra non-whitespace data is found... + SkipWhitespaceAndComments(is); + if (is.Peek() != '\0') { + // ... this is considered an error. + HandleError(state_, is); + return false; + } + } + + // Success! We are done! + return true; + } + + // Transition to the new state. + state_ = d; + + // If we parsed anything other than a delimiter, we invoked the handler, so we can return true now. + if (!IsIterativeParsingDelimiterState(n)) + return true; + } + + // We reached the end of file. + stack_.Clear(); + + if (state_ != IterativeParsingFinishState) { + HandleError(state_, is); + return false; + } + + return true; + } + + //! Check if token-by-token parsing JSON text is complete + /*! \return Whether the JSON has been fully decoded. + */ + RAPIDJSON_FORCEINLINE bool IterativeParseComplete() const { + return IsIterativeParsingCompleteState(state_); + } + + //! Whether a parse error has occurred in the last parsing. bool HasParseError() const { return parseResult_.IsError(); } //! Get the \ref ParseErrorCode of last parsing. @@ -575,7 +724,7 @@ private: } } else if (RAPIDJSON_LIKELY(Consume(is, '/'))) - while (is.Peek() != '\0' && is.Take() != '\n'); + while (is.Peek() != '\0' && is.Take() != '\n') {} else RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); @@ -750,7 +899,7 @@ private: return false; } - // Helper function to parse four hexidecimal digits in \uXXXX in ParseString(). + // Helper function to parse four hexadecimal digits in \uXXXX in ParseString(). template unsigned ParseHex4(InputStream& is, size_t escapeOffset) { unsigned codepoint = 0; @@ -857,7 +1006,7 @@ private: Ch c = is.Peek(); if (RAPIDJSON_UNLIKELY(c == '\\')) { // Escape - size_t escapeOffset = is.Tell(); // For invalid escaping, report the inital '\\' as error offset + size_t escapeOffset = is.Tell(); // For invalid escaping, report the initial '\\' as error offset is.Take(); Ch e = is.Peek(); if ((sizeof(Ch) == 1 || unsigned(e) < 256) && RAPIDJSON_LIKELY(escape[static_cast(e)])) { @@ -892,7 +1041,7 @@ private: if (c == '\0') RAPIDJSON_PARSE_ERROR(kParseErrorStringMissQuotationMark, is.Tell()); else - RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, is.Tell()); + RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, is.Tell()); } else { size_t offset = is.Tell(); @@ -927,7 +1076,7 @@ private: // The rest of string using SIMD static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; - static const char space[16] = { 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19 }; + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); @@ -936,7 +1085,7 @@ private: const __m128i s = _mm_load_si128(reinterpret_cast(p)); const __m128i t1 = _mm_cmpeq_epi8(s, dq); const __m128i t2 = _mm_cmpeq_epi8(s, bs); - const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x19) == 0x19 + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); unsigned short r = static_cast(_mm_movemask_epi8(x)); if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped @@ -948,11 +1097,13 @@ private: #else length = static_cast(__builtin_ffs(r) - 1); #endif - char* q = reinterpret_cast(os.Push(length)); - for (size_t i = 0; i < length; i++) - q[i] = p[i]; + if (length != 0) { + char* q = reinterpret_cast(os.Push(length)); + for (size_t i = 0; i < length; i++) + q[i] = p[i]; - p += length; + p += length; + } break; } _mm_storeu_si128(reinterpret_cast<__m128i *>(os.Push(16)), s); @@ -988,7 +1139,7 @@ private: // The rest of string using SIMD static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; - static const char space[16] = { 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19 }; + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); @@ -997,7 +1148,7 @@ private: const __m128i s = _mm_load_si128(reinterpret_cast(p)); const __m128i t1 = _mm_cmpeq_epi8(s, dq); const __m128i t2 = _mm_cmpeq_epi8(s, bs); - const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x19) == 0x19 + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); unsigned short r = static_cast(_mm_movemask_epi8(x)); if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped @@ -1036,7 +1187,7 @@ private: // The rest of string using SIMD static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; - static const char space[16] = { 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19 }; + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); @@ -1045,7 +1196,7 @@ private: const __m128i s = _mm_load_si128(reinterpret_cast(p)); const __m128i t1 = _mm_cmpeq_epi8(s, dq); const __m128i t2 = _mm_cmpeq_epi8(s, bs); - const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x19) == 0x19 + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); unsigned short r = static_cast(_mm_movemask_epi8(x)); if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped @@ -1064,7 +1215,180 @@ private: is.src_ = is.dst_ = p; } -#endif +#elif defined(RAPIDJSON_NEON) + // StringStream -> StackStream + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(StringStream& is, StackStream& os) { + const char* p = is.src_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = p; + return; + } + else + os.Put(*p++); + + // The rest of string using SIMD + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (;; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(reinterpret_cast(x), 0); // extract + uint64_t high = vgetq_lane_u64(reinterpret_cast(x), 1); // extract + + SizeType length = 0; + bool escaped = false; + if (low == 0) { + if (high != 0) { + unsigned lz = (unsigned)__builtin_clzll(high);; + length = 8 + (lz >> 3); + escaped = true; + } + } else { + unsigned lz = (unsigned)__builtin_clzll(low);; + length = lz >> 3; + escaped = true; + } + if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped + if (length != 0) { + char* q = reinterpret_cast(os.Push(length)); + for (size_t i = 0; i < length; i++) + q[i] = p[i]; + + p += length; + } + break; + } + vst1q_u8(reinterpret_cast(os.Push(16)), s); + } + + is.src_ = p; + } + + // InsituStringStream -> InsituStringStream + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InsituStringStream& is, InsituStringStream& os) { + RAPIDJSON_ASSERT(&is == &os); + (void)os; + + if (is.src_ == is.dst_) { + SkipUnescapedString(is); + return; + } + + char* p = is.src_; + char *q = is.dst_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = p; + is.dst_ = q; + return; + } + else + *q++ = *p++; + + // The rest of string using SIMD + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (;; p += 16, q += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(reinterpret_cast(x), 0); // extract + uint64_t high = vgetq_lane_u64(reinterpret_cast(x), 1); // extract + + SizeType length = 0; + bool escaped = false; + if (low == 0) { + if (high != 0) { + unsigned lz = (unsigned)__builtin_clzll(high); + length = 8 + (lz >> 3); + escaped = true; + } + } else { + unsigned lz = (unsigned)__builtin_clzll(low); + length = lz >> 3; + escaped = true; + } + if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped + for (const char* pend = p + length; p != pend; ) { + *q++ = *p++; + } + break; + } + vst1q_u8(reinterpret_cast(q), s); + } + + is.src_ = p; + is.dst_ = q; + } + + // When read/write pointers are the same for insitu stream, just skip unescaped characters + static RAPIDJSON_FORCEINLINE void SkipUnescapedString(InsituStringStream& is) { + RAPIDJSON_ASSERT(is.src_ == is.dst_); + char* p = is.src_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + for (; p != nextAligned; p++) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = is.dst_ = p; + return; + } + + // The rest of string using SIMD + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (;; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(reinterpret_cast(x), 0); // extract + uint64_t high = vgetq_lane_u64(reinterpret_cast(x), 1); // extract + + if (low == 0) { + if (high != 0) { + int lz = __builtin_clzll(high); + p += 8 + (lz >> 3); + break; + } + } else { + int lz = __builtin_clzll(low); + p += lz >> 3; + break; + } + } + + is.src_ = is.dst_ = p; + } +#endif // RAPIDJSON_NEON template class NumberStream; @@ -1075,7 +1399,6 @@ private: typedef typename InputStream::Ch Ch; NumberStream(GenericReader& reader, InputStream& s) : is(s) { (void)reader; } - ~NumberStream() {} RAPIDJSON_FORCEINLINE Ch Peek() const { return is.Peek(); } RAPIDJSON_FORCEINLINE Ch TakePush() { return is.Take(); } @@ -1097,7 +1420,6 @@ private: typedef NumberStream Base; public: NumberStream(GenericReader& reader, InputStream& is) : Base(reader, is), stackStream(reader.stack_) {} - ~NumberStream() {} RAPIDJSON_FORCEINLINE Ch TakePush() { stackStream.Put(static_cast(Base::is.Peek())); @@ -1124,7 +1446,6 @@ private: typedef NumberStream Base; public: NumberStream(GenericReader& reader, InputStream& is) : Base(reader, is) {} - ~NumberStream() {} RAPIDJSON_FORCEINLINE Ch Take() { return Base::TakePush(); } }; @@ -1185,18 +1506,27 @@ private: } // Parse NaN or Infinity here else if ((parseFlags & kParseNanAndInfFlag) && RAPIDJSON_LIKELY((s.Peek() == 'I' || s.Peek() == 'N'))) { - useNanOrInf = true; - if (RAPIDJSON_LIKELY(Consume(s, 'N') && Consume(s, 'a') && Consume(s, 'N'))) { - d = std::numeric_limits::quiet_NaN(); + if (Consume(s, 'N')) { + if (Consume(s, 'a') && Consume(s, 'N')) { + d = std::numeric_limits::quiet_NaN(); + useNanOrInf = true; + } } - else if (RAPIDJSON_LIKELY(Consume(s, 'I') && Consume(s, 'n') && Consume(s, 'f'))) { - d = (minus ? -std::numeric_limits::infinity() : std::numeric_limits::infinity()); - if (RAPIDJSON_UNLIKELY(s.Peek() == 'i' && !(Consume(s, 'i') && Consume(s, 'n') - && Consume(s, 'i') && Consume(s, 't') && Consume(s, 'y')))) - RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + else if (RAPIDJSON_LIKELY(Consume(s, 'I'))) { + if (Consume(s, 'n') && Consume(s, 'f')) { + d = (minus ? -std::numeric_limits::infinity() : std::numeric_limits::infinity()); + useNanOrInf = true; + + if (RAPIDJSON_UNLIKELY(s.Peek() == 'i' && !(Consume(s, 'i') && Consume(s, 'n') + && Consume(s, 'i') && Consume(s, 't') && Consume(s, 'y')))) { + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + } + } } - else + + if (RAPIDJSON_UNLIKELY(!useNanOrInf)) { RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + } } else RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); @@ -1231,8 +1561,6 @@ private: // Force double for big integer if (useDouble) { while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { - if (RAPIDJSON_UNLIKELY(d >= 1.7976931348623157e307)) // DBL_MAX / 10.0 - RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset); d = d * 10 + (s.TakePush() - '0'); } } @@ -1302,9 +1630,18 @@ private: if (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { exp = static_cast(s.Take() - '0'); if (expMinus) { + // (exp + expFrac) must not underflow int => we're detecting when -exp gets + // dangerously close to INT_MIN (a pessimistic next digit 9 would push it into + // underflow territory): + // + // -(exp * 10 + 9) + expFrac >= INT_MIN + // <=> exp <= (expFrac - INT_MIN - 9) / 10 + RAPIDJSON_ASSERT(expFrac <= 0); + int maxExp = (expFrac + 2147483639) / 10; + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { exp = exp * 10 + static_cast(s.Take() - '0'); - if (exp >= 214748364) { // Issue #313: prevent overflow exponent + if (RAPIDJSON_UNLIKELY(exp > maxExp)) { while (RAPIDJSON_UNLIKELY(s.Peek() >= '0' && s.Peek() <= '9')) // Consume the rest of exponent s.Take(); } @@ -1363,6 +1700,13 @@ private: else d = internal::StrtodNormalPrecision(d, p); + // Use > max, instead of == inf, to fix bogus warning -Wfloat-equal + if (d > (std::numeric_limits::max)()) { + // Overflow + // TODO: internal::StrtodX should report overflow (or underflow) + RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset); + } + cont = handler.Double(minus ? -d : d); } else if (useNanOrInf) { @@ -1408,29 +1752,31 @@ private: // States enum IterativeParsingState { - IterativeParsingStartState = 0, - IterativeParsingFinishState, - IterativeParsingErrorState, + IterativeParsingFinishState = 0, // sink states at top + IterativeParsingErrorState, // sink states at top + IterativeParsingStartState, // Object states IterativeParsingObjectInitialState, IterativeParsingMemberKeyState, - IterativeParsingKeyValueDelimiterState, IterativeParsingMemberValueState, - IterativeParsingMemberDelimiterState, IterativeParsingObjectFinishState, // Array states IterativeParsingArrayInitialState, IterativeParsingElementState, - IterativeParsingElementDelimiterState, IterativeParsingArrayFinishState, // Single value state - IterativeParsingValueState - }; + IterativeParsingValueState, - enum { cIterativeParsingStateCount = IterativeParsingValueState + 1 }; + // Delimiter states (at bottom) + IterativeParsingElementDelimiterState, + IterativeParsingMemberDelimiterState, + IterativeParsingKeyValueDelimiterState, + + cIterativeParsingStateCount + }; // Tokens enum Token { @@ -1452,7 +1798,7 @@ private: kTokenCount }; - RAPIDJSON_FORCEINLINE Token Tokenize(Ch c) { + RAPIDJSON_FORCEINLINE Token Tokenize(Ch c) const { //!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN #define N NumberToken @@ -1479,9 +1825,21 @@ private: return NumberToken; } - RAPIDJSON_FORCEINLINE IterativeParsingState Predict(IterativeParsingState state, Token token) { + RAPIDJSON_FORCEINLINE IterativeParsingState Predict(IterativeParsingState state, Token token) const { // current state x one lookahead token -> new state static const char G[cIterativeParsingStateCount][kTokenCount] = { + // Finish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // Error(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, // Start { IterativeParsingArrayInitialState, // Left bracket @@ -1496,18 +1854,6 @@ private: IterativeParsingValueState, // Null IterativeParsingValueState // Number }, - // Finish(sink state) - { - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState - }, - // Error(sink state) - { - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState - }, // ObjectInitial { IterativeParsingErrorState, // Left bracket @@ -1536,20 +1882,6 @@ private: IterativeParsingErrorState, // Null IterativeParsingErrorState // Number }, - // KeyValueDelimiter - { - IterativeParsingArrayInitialState, // Left bracket(push MemberValue state) - IterativeParsingErrorState, // Right bracket - IterativeParsingObjectInitialState, // Left curly bracket(push MemberValue state) - IterativeParsingErrorState, // Right curly bracket - IterativeParsingErrorState, // Comma - IterativeParsingErrorState, // Colon - IterativeParsingMemberValueState, // String - IterativeParsingMemberValueState, // False - IterativeParsingMemberValueState, // True - IterativeParsingMemberValueState, // Null - IterativeParsingMemberValueState // Number - }, // MemberValue { IterativeParsingErrorState, // Left bracket @@ -1564,20 +1896,6 @@ private: IterativeParsingErrorState, // Null IterativeParsingErrorState // Number }, - // MemberDelimiter - { - IterativeParsingErrorState, // Left bracket - IterativeParsingErrorState, // Right bracket - IterativeParsingErrorState, // Left curly bracket - IterativeParsingObjectFinishState, // Right curly bracket - IterativeParsingErrorState, // Comma - IterativeParsingErrorState, // Colon - IterativeParsingMemberKeyState, // String - IterativeParsingErrorState, // False - IterativeParsingErrorState, // True - IterativeParsingErrorState, // Null - IterativeParsingErrorState // Number - }, // ObjectFinish(sink state) { IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, @@ -1612,6 +1930,18 @@ private: IterativeParsingErrorState, // Null IterativeParsingErrorState // Number }, + // ArrayFinish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // Single Value (sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, // ElementDelimiter { IterativeParsingArrayInitialState, // Left bracket(push Element state) @@ -1626,18 +1956,34 @@ private: IterativeParsingElementState, // Null IterativeParsingElementState // Number }, - // ArrayFinish(sink state) + // MemberDelimiter { - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingObjectFinishState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberKeyState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number }, - // Single Value (sink state) + // KeyValueDelimiter { - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, - IterativeParsingErrorState - } + IterativeParsingArrayInitialState, // Left bracket(push MemberValue state) + IterativeParsingErrorState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push MemberValue state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberValueState, // String + IterativeParsingMemberValueState, // False + IterativeParsingMemberValueState, // True + IterativeParsingMemberValueState, // Null + IterativeParsingMemberValueState // Number + }, }; // End of G return static_cast(G[state][token]); @@ -1818,6 +2164,14 @@ private: } } + RAPIDJSON_FORCEINLINE bool IsIterativeParsingDelimiterState(IterativeParsingState s) const { + return s >= IterativeParsingElementDelimiterState; + } + + RAPIDJSON_FORCEINLINE bool IsIterativeParsingCompleteState(IterativeParsingState s) const { + return s <= IterativeParsingErrorState; + } + template ParseResult IterativeParse(InputStream& is, Handler& handler) { parseResult_.Clear(); @@ -1856,6 +2210,7 @@ private: static const size_t kDefaultStackCapacity = 256; //!< Default stack capacity in bytes for storing a single decoded string. internal::Stack stack_; //!< A stack for storing decoded string temporarily during non-destructive parsing. ParseResult parseResult_; + IterativeParsingState state_; }; // class GenericReader //! Reader with UTF8 encoding and default allocator. @@ -1863,7 +2218,7 @@ typedef GenericReader, UTF8<> > Reader; RAPIDJSON_NAMESPACE_END -#ifdef __clang__ +#if defined(__clang__) || defined(_MSC_VER) RAPIDJSON_DIAG_POP #endif @@ -1872,8 +2227,4 @@ RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_POP #endif -#ifdef _MSC_VER -RAPIDJSON_DIAG_POP -#endif - #endif // RAPIDJSON_READER_H_ diff --git a/src/3rdparty/rapidjson/schema.h b/src/3rdparty/rapidjson/schema.h index b182aa27..26ae9474 100644 --- a/src/3rdparty/rapidjson/schema.h +++ b/src/3rdparty/rapidjson/schema.h @@ -17,6 +17,7 @@ #include "document.h" #include "pointer.h" +#include "stringbuffer.h" #include // abs, floor #if !defined(RAPIDJSON_SCHEMA_USE_INTERNALREGEX) @@ -25,7 +26,7 @@ #define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 0 #endif -#if !RAPIDJSON_SCHEMA_USE_INTERNALREGEX && !defined(RAPIDJSON_SCHEMA_USE_STDREGEX) && (__cplusplus >=201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)) +#if !RAPIDJSON_SCHEMA_USE_INTERNALREGEX && defined(RAPIDJSON_SCHEMA_USE_STDREGEX) && (__cplusplus >=201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)) #define RAPIDJSON_SCHEMA_USE_STDREGEX 1 #else #define RAPIDJSON_SCHEMA_USE_STDREGEX 0 @@ -62,9 +63,7 @@ RAPIDJSON_DIAG_OFF(weak-vtables) RAPIDJSON_DIAG_OFF(exit-time-destructors) RAPIDJSON_DIAG_OFF(c++98-compat-pedantic) RAPIDJSON_DIAG_OFF(variadic-macros) -#endif - -#ifdef _MSC_VER +#elif defined(_MSC_VER) RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated #endif @@ -157,6 +156,62 @@ public: virtual void FreeState(void* p) = 0; }; +/////////////////////////////////////////////////////////////////////////////// +// IValidationErrorHandler + +template +class IValidationErrorHandler { +public: + typedef typename SchemaType::Ch Ch; + typedef typename SchemaType::SValue SValue; + + virtual ~IValidationErrorHandler() {} + + virtual void NotMultipleOf(int64_t actual, const SValue& expected) = 0; + virtual void NotMultipleOf(uint64_t actual, const SValue& expected) = 0; + virtual void NotMultipleOf(double actual, const SValue& expected) = 0; + virtual void AboveMaximum(int64_t actual, const SValue& expected, bool exclusive) = 0; + virtual void AboveMaximum(uint64_t actual, const SValue& expected, bool exclusive) = 0; + virtual void AboveMaximum(double actual, const SValue& expected, bool exclusive) = 0; + virtual void BelowMinimum(int64_t actual, const SValue& expected, bool exclusive) = 0; + virtual void BelowMinimum(uint64_t actual, const SValue& expected, bool exclusive) = 0; + virtual void BelowMinimum(double actual, const SValue& expected, bool exclusive) = 0; + + virtual void TooLong(const Ch* str, SizeType length, SizeType expected) = 0; + virtual void TooShort(const Ch* str, SizeType length, SizeType expected) = 0; + virtual void DoesNotMatch(const Ch* str, SizeType length) = 0; + + virtual void DisallowedItem(SizeType index) = 0; + virtual void TooFewItems(SizeType actualCount, SizeType expectedCount) = 0; + virtual void TooManyItems(SizeType actualCount, SizeType expectedCount) = 0; + virtual void DuplicateItems(SizeType index1, SizeType index2) = 0; + + virtual void TooManyProperties(SizeType actualCount, SizeType expectedCount) = 0; + virtual void TooFewProperties(SizeType actualCount, SizeType expectedCount) = 0; + virtual void StartMissingProperties() = 0; + virtual void AddMissingProperty(const SValue& name) = 0; + virtual bool EndMissingProperties() = 0; + virtual void PropertyViolations(ISchemaValidator** subvalidators, SizeType count) = 0; + virtual void DisallowedProperty(const Ch* name, SizeType length) = 0; + + virtual void StartDependencyErrors() = 0; + virtual void StartMissingDependentProperties() = 0; + virtual void AddMissingDependentProperty(const SValue& targetName) = 0; + virtual void EndMissingDependentProperties(const SValue& sourceName) = 0; + virtual void AddDependencySchemaError(const SValue& souceName, ISchemaValidator* subvalidator) = 0; + virtual bool EndDependencyErrors() = 0; + + virtual void DisallowedValue() = 0; + virtual void StartDisallowedType() = 0; + virtual void AddExpectedType(const typename SchemaType::ValueType& expectedType) = 0; + virtual void EndDisallowedType(const typename SchemaType::ValueType& actualType) = 0; + virtual void NotAllOf(ISchemaValidator** subvalidators, SizeType count) = 0; + virtual void NoneOf(ISchemaValidator** subvalidators, SizeType count) = 0; + virtual void NotOneOf(ISchemaValidator** subvalidators, SizeType count) = 0; + virtual void Disallowed() = 0; +}; + + /////////////////////////////////////////////////////////////////////////////// // Hasher @@ -261,6 +316,7 @@ template struct SchemaValidationContext { typedef Schema SchemaType; typedef ISchemaStateFactory SchemaValidatorFactoryType; + typedef IValidationErrorHandler ErrorHandlerType; typedef typename SchemaType::ValueType ValueType; typedef typename ValueType::Ch Ch; @@ -270,8 +326,9 @@ struct SchemaValidationContext { kPatternValidatorWithAdditionalProperty }; - SchemaValidationContext(SchemaValidatorFactoryType& f, const SchemaType* s) : + SchemaValidationContext(SchemaValidatorFactoryType& f, ErrorHandlerType& eh, const SchemaType* s) : factory(f), + error_handler(eh), schema(s), valueSchema(), invalidKeyword(), @@ -311,6 +368,7 @@ struct SchemaValidationContext { } SchemaValidatorFactoryType& factory; + ErrorHandlerType& error_handler; const SchemaType* schema; const SchemaType* valueSchema; const Ch* invalidKeyword; @@ -345,15 +403,20 @@ public: typedef SchemaValidationContext Context; typedef Schema SchemaType; typedef GenericValue SValue; + typedef IValidationErrorHandler ErrorHandler; friend class GenericSchemaDocument; Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator) : allocator_(allocator), + uri_(schemaDocument->GetURI(), *allocator), + pointer_(p, allocator), + typeless_(schemaDocument->GetTypeless()), enum_(), enumCount_(), not_(), type_((1 << kTotalSchemaType) - 1), // typeless validatorCount_(), + notValidatorIndex_(), properties_(), additionalPropertiesSchema_(), patternProperties_(), @@ -377,7 +440,8 @@ public: minLength_(0), maxLength_(~SizeType(0)), exclusiveMinimum_(false), - exclusiveMaximum_(false) + exclusiveMaximum_(false), + defaultValueLength_(0) { typedef typename SchemaDocumentType::ValueType ValueType; typedef typename ValueType::ConstValueIterator ConstValueIterator; @@ -400,7 +464,7 @@ public: enum_ = static_cast(allocator_->Malloc(sizeof(uint64_t) * v->Size())); for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) { typedef Hasher > EnumHasherType; - char buffer[256 + 24]; + char buffer[256u + 24]; MemoryPoolAllocator<> hasherAllocator(buffer, sizeof(buffer)); EnumHasherType h(&hasherAllocator, 256); itr->Accept(h); @@ -453,7 +517,7 @@ public: for (SizeType i = 0; i < propertyCount_; i++) { new (&properties_[i]) Property(); properties_[i].name = allProperties[i]; - properties_[i].schema = GetTypeless(); + properties_[i].schema = typeless_; } } } @@ -572,12 +636,16 @@ public: if (const ValueType* v = GetMember(value, GetMultipleOfString())) if (v->IsNumber() && v->GetDouble() > 0.0) multipleOf_.CopyFrom(*v, *allocator_); + + // Default + if (const ValueType* v = GetMember(value, GetDefaultValueString())) + if (v->IsString()) + defaultValueLength_ = v->GetStringLength(); + } ~Schema() { - if (allocator_) { - allocator_->Free(enum_); - } + AllocatorType::Free(enum_); if (properties_) { for (SizeType i = 0; i < propertyCount_; i++) properties_[i].~Property(); @@ -592,11 +660,19 @@ public: #if RAPIDJSON_SCHEMA_HAS_REGEX if (pattern_) { pattern_->~RegexType(); - allocator_->Free(pattern_); + AllocatorType::Free(pattern_); } #endif } + const SValue& GetURI() const { + return uri_; + } + + const PointerType& GetPointer() const { + return pointer_; + } + bool BeginValue(Context& context) const { if (context.inArray) { if (uniqueItems_) @@ -610,12 +686,14 @@ public: else if (additionalItemsSchema_) context.valueSchema = additionalItemsSchema_; else if (additionalItems_) - context.valueSchema = GetTypeless(); - else + context.valueSchema = typeless_; + else { + context.error_handler.DisallowedItem(context.arrayElementIndex); RAPIDJSON_INVALID_KEYWORD_RETURN(GetItemsString()); + } } else - context.valueSchema = GetTypeless(); + context.valueSchema = typeless_; context.arrayElementIndex++; } @@ -637,15 +715,21 @@ public: } if (context.objectPatternValidatorType == Context::kPatternValidatorOnly) { - if (!patternValid) + if (!patternValid) { + context.error_handler.PropertyViolations(context.patternPropertiesValidators, count); RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); + } } else if (context.objectPatternValidatorType == Context::kPatternValidatorWithProperty) { - if (!patternValid || !otherValid) + if (!patternValid || !otherValid) { + context.error_handler.PropertyViolations(context.patternPropertiesValidators, count + 1); RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); + } } - else if (!patternValid && !otherValid) // kPatternValidatorWithAdditionalProperty) + else if (!patternValid && !otherValid) { // kPatternValidatorWithAdditionalProperty) + context.error_handler.PropertyViolations(context.patternPropertiesValidators, count + 1); RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); + } } if (enum_) { @@ -653,19 +737,23 @@ public: for (SizeType i = 0; i < enumCount_; i++) if (enum_[i] == h) goto foundEnum; + context.error_handler.DisallowedValue(); RAPIDJSON_INVALID_KEYWORD_RETURN(GetEnumString()); foundEnum:; } if (allOf_.schemas) for (SizeType i = allOf_.begin; i < allOf_.begin + allOf_.count; i++) - if (!context.validators[i]->IsValid()) + if (!context.validators[i]->IsValid()) { + context.error_handler.NotAllOf(&context.validators[allOf_.begin], allOf_.count); RAPIDJSON_INVALID_KEYWORD_RETURN(GetAllOfString()); + } if (anyOf_.schemas) { for (SizeType i = anyOf_.begin; i < anyOf_.begin + anyOf_.count; i++) if (context.validators[i]->IsValid()) goto foundAny; + context.error_handler.NoneOf(&context.validators[anyOf_.begin], anyOf_.count); RAPIDJSON_INVALID_KEYWORD_RETURN(GetAnyOfString()); foundAny:; } @@ -674,30 +762,39 @@ public: bool oneValid = false; for (SizeType i = oneOf_.begin; i < oneOf_.begin + oneOf_.count; i++) if (context.validators[i]->IsValid()) { - if (oneValid) + if (oneValid) { + context.error_handler.NotOneOf(&context.validators[oneOf_.begin], oneOf_.count); RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString()); - else + } else oneValid = true; } - if (!oneValid) + if (!oneValid) { + context.error_handler.NotOneOf(&context.validators[oneOf_.begin], oneOf_.count); RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString()); + } } - if (not_ && context.validators[notValidatorIndex_]->IsValid()) + if (not_ && context.validators[notValidatorIndex_]->IsValid()) { + context.error_handler.Disallowed(); RAPIDJSON_INVALID_KEYWORD_RETURN(GetNotString()); + } return true; } - bool Null(Context& context) const { - if (!(type_ & (1 << kNullSchemaType))) + bool Null(Context& context) const { + if (!(type_ & (1 << kNullSchemaType))) { + DisallowedType(context, GetNullString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + } return CreateParallelValidator(context); } - bool Bool(Context& context, bool) const { - if (!(type_ & (1 << kBooleanSchemaType))) + bool Bool(Context& context, bool) const { + if (!(type_ & (1 << kBooleanSchemaType))) { + DisallowedType(context, GetBooleanString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + } return CreateParallelValidator(context); } @@ -726,8 +823,10 @@ public: } bool Double(Context& context, double d) const { - if (!(type_ & (1 << kNumberSchemaType))) + if (!(type_ & (1 << kNumberSchemaType))) { + DisallowedType(context, GetNumberString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + } if (!minimum_.IsNull() && !CheckDoubleMinimum(context, d)) return false; @@ -742,28 +841,38 @@ public: } bool String(Context& context, const Ch* str, SizeType length, bool) const { - if (!(type_ & (1 << kStringSchemaType))) + if (!(type_ & (1 << kStringSchemaType))) { + DisallowedType(context, GetStringString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + } if (minLength_ != 0 || maxLength_ != SizeType(~0)) { SizeType count; if (internal::CountStringCodePoint(str, length, &count)) { - if (count < minLength_) + if (count < minLength_) { + context.error_handler.TooShort(str, length, minLength_); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinLengthString()); - if (count > maxLength_) + } + if (count > maxLength_) { + context.error_handler.TooLong(str, length, maxLength_); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxLengthString()); + } } } - if (pattern_ && !IsPatternMatch(pattern_, str, length)) + if (pattern_ && !IsPatternMatch(pattern_, str, length)) { + context.error_handler.DoesNotMatch(str, length); RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternString()); + } return CreateParallelValidator(context); } - bool StartObject(Context& context) const { - if (!(type_ & (1 << kObjectSchemaType))) + bool StartObject(Context& context) const { + if (!(type_ & (1 << kObjectSchemaType))) { + DisallowedType(context, GetObjectString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + } if (hasDependencies_ || hasRequired_) { context.propertyExist = static_cast(context.factory.MallocState(sizeof(bool) * propertyCount_)); @@ -784,15 +893,17 @@ public: if (patternProperties_) { context.patternPropertiesSchemaCount = 0; for (SizeType i = 0; i < patternPropertyCount_; i++) - if (patternProperties_[i].pattern && IsPatternMatch(patternProperties_[i].pattern, str, len)) + if (patternProperties_[i].pattern && IsPatternMatch(patternProperties_[i].pattern, str, len)) { context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = patternProperties_[i].schema; + context.valueSchema = typeless_; + } } SizeType index; if (FindPropertyIndex(ValueType(str, len).Move(), &index)) { if (context.patternPropertiesSchemaCount > 0) { context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = properties_[index].schema; - context.valueSchema = GetTypeless(); + context.valueSchema = typeless_; context.valuePatternValidatorType = Context::kPatternValidatorWithProperty; } else @@ -807,7 +918,7 @@ public: if (additionalPropertiesSchema_) { if (additionalPropertiesSchema_ && context.patternPropertiesSchemaCount > 0) { context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = additionalPropertiesSchema_; - context.valueSchema = GetTypeless(); + context.valueSchema = typeless_; context.valuePatternValidatorType = Context::kPatternValidatorWithAdditionalProperty; } else @@ -815,49 +926,70 @@ public: return true; } else if (additionalProperties_) { - context.valueSchema = GetTypeless(); + context.valueSchema = typeless_; return true; } - if (context.patternPropertiesSchemaCount == 0) // patternProperties are not additional properties + if (context.patternPropertiesSchemaCount == 0) { // patternProperties are not additional properties + context.error_handler.DisallowedProperty(str, len); RAPIDJSON_INVALID_KEYWORD_RETURN(GetAdditionalPropertiesString()); + } return true; } bool EndObject(Context& context, SizeType memberCount) const { - if (hasRequired_) + if (hasRequired_) { + context.error_handler.StartMissingProperties(); for (SizeType index = 0; index < propertyCount_; index++) - if (properties_[index].required) - if (!context.propertyExist[index]) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetRequiredString()); + if (properties_[index].required && !context.propertyExist[index]) + if (properties_[index].schema->defaultValueLength_ == 0 ) + context.error_handler.AddMissingProperty(properties_[index].name); + if (context.error_handler.EndMissingProperties()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetRequiredString()); + } - if (memberCount < minProperties_) + if (memberCount < minProperties_) { + context.error_handler.TooFewProperties(memberCount, minProperties_); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinPropertiesString()); + } - if (memberCount > maxProperties_) + if (memberCount > maxProperties_) { + context.error_handler.TooManyProperties(memberCount, maxProperties_); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxPropertiesString()); + } if (hasDependencies_) { - for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++) + context.error_handler.StartDependencyErrors(); + for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++) { + const Property& source = properties_[sourceIndex]; if (context.propertyExist[sourceIndex]) { - if (properties_[sourceIndex].dependencies) { + if (source.dependencies) { + context.error_handler.StartMissingDependentProperties(); for (SizeType targetIndex = 0; targetIndex < propertyCount_; targetIndex++) - if (properties_[sourceIndex].dependencies[targetIndex] && !context.propertyExist[targetIndex]) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString()); + if (source.dependencies[targetIndex] && !context.propertyExist[targetIndex]) + context.error_handler.AddMissingDependentProperty(properties_[targetIndex].name); + context.error_handler.EndMissingDependentProperties(source.name); + } + else if (source.dependenciesSchema) { + ISchemaValidator* dependenciesValidator = context.validators[source.dependenciesValidatorIndex]; + if (!dependenciesValidator->IsValid()) + context.error_handler.AddDependencySchemaError(source.name, dependenciesValidator); } - else if (properties_[sourceIndex].dependenciesSchema) - if (!context.validators[properties_[sourceIndex].dependenciesValidatorIndex]->IsValid()) - RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString()); } + } + if (context.error_handler.EndDependencyErrors()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString()); } return true; } - bool StartArray(Context& context) const { - if (!(type_ & (1 << kArraySchemaType))) + bool StartArray(Context& context) const { + if (!(type_ & (1 << kArraySchemaType))) { + DisallowedType(context, GetArrayString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + } context.arrayElementIndex = 0; context.inArray = true; @@ -865,14 +997,18 @@ public: return CreateParallelValidator(context); } - bool EndArray(Context& context, SizeType elementCount) const { + bool EndArray(Context& context, SizeType elementCount) const { context.inArray = false; - if (elementCount < minItems_) + if (elementCount < minItems_) { + context.error_handler.TooFewItems(elementCount, minItems_); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinItemsString()); + } - if (elementCount > maxItems_) + if (elementCount > maxItems_) { + context.error_handler.TooManyItems(elementCount, maxItems_); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxItemsString()); + } return true; } @@ -881,7 +1017,7 @@ public: #define RAPIDJSON_STRING_(name, ...) \ static const ValueType& Get##name##String() {\ static const Ch s[] = { __VA_ARGS__, '\0' };\ - static const ValueType v(s, sizeof(s) / sizeof(Ch) - 1);\ + static const ValueType v(s, static_cast(sizeof(s) / sizeof(Ch) - 1));\ return v;\ } @@ -918,6 +1054,7 @@ public: RAPIDJSON_STRING_(ExclusiveMinimum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'i', 'n', 'i', 'm', 'u', 'm') RAPIDJSON_STRING_(ExclusiveMaximum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'a', 'x', 'i', 'm', 'u', 'm') RAPIDJSON_STRING_(MultipleOf, 'm', 'u', 'l', 't', 'i', 'p', 'l', 'e', 'O', 'f') + RAPIDJSON_STRING_(DefaultValue, 'd', 'e', 'f', 'a', 'u', 'l', 't') #undef RAPIDJSON_STRING_ @@ -934,7 +1071,7 @@ private: }; #if RAPIDJSON_SCHEMA_USE_INTERNALREGEX - typedef internal::GenericRegex RegexType; + typedef internal::GenericRegex RegexType; #elif RAPIDJSON_SCHEMA_USE_STDREGEX typedef std::basic_regex RegexType; #else @@ -949,11 +1086,6 @@ private: SizeType count; }; - static const SchemaType* GetTypeless() { - static SchemaType typeless(0, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), 0); - return &typeless; - } - template void AddUniqueElement(V1& a, const V2& v) { for (typename V1::ConstValueIterator itr = a.Begin(); itr != a.End(); ++itr) @@ -999,7 +1131,7 @@ private: template RegexType* CreatePattern(const ValueType& value) { if (value.IsString()) { - RegexType* r = new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString()); + RegexType* r = new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString(), allocator_); if (!r->IsValid()) { r->~RegexType(); AllocatorType::Free(r); @@ -1011,17 +1143,21 @@ private: } static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType) { - return pattern->Search(str); + GenericRegexSearch rs(*pattern); + return rs.Search(str); } #elif RAPIDJSON_SCHEMA_USE_STDREGEX template RegexType* CreatePattern(const ValueType& value) { - if (value.IsString()) + if (value.IsString()) { + RegexType *r = static_cast(allocator_->Malloc(sizeof(RegexType))); try { - return new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString(), std::size_t(value.GetStringLength()), std::regex_constants::ECMAScript); + return new (r) RegexType(value.GetString(), std::size_t(value.GetStringLength()), std::regex_constants::ECMAScript); } catch (const std::regex_error&) { + AllocatorType::Free(r); } + } return 0; } @@ -1097,15 +1233,20 @@ private: } bool CheckInt(Context& context, int64_t i) const { - if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) + if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) { + DisallowedType(context, GetIntegerString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + } if (!minimum_.IsNull()) { if (minimum_.IsInt64()) { - if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64()) + if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64()) { + context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); + } } else if (minimum_.IsUint64()) { + context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); // i <= max(int64_t) < minimum.GetUint64() } else if (!CheckDoubleMinimum(context, static_cast(i))) @@ -1114,19 +1255,23 @@ private: if (!maximum_.IsNull()) { if (maximum_.IsInt64()) { - if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64()) + if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64()) { + context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); + } } - else if (maximum_.IsUint64()) - /* do nothing */; // i <= max(int64_t) < maximum_.GetUint64() + else if (maximum_.IsUint64()) { } + /* do nothing */ // i <= max(int64_t) < maximum_.GetUint64() else if (!CheckDoubleMaximum(context, static_cast(i))) return false; } if (!multipleOf_.IsNull()) { if (multipleOf_.IsUint64()) { - if (static_cast(i >= 0 ? i : -i) % multipleOf_.GetUint64() != 0) + if (static_cast(i >= 0 ? i : -i) % multipleOf_.GetUint64() != 0) { + context.error_handler.NotMultipleOf(i, multipleOf_); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString()); + } } else if (!CheckDoubleMultipleOf(context, static_cast(i))) return false; @@ -1136,13 +1281,17 @@ private: } bool CheckUint(Context& context, uint64_t i) const { - if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) + if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) { + DisallowedType(context, GetIntegerString()); RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + } if (!minimum_.IsNull()) { if (minimum_.IsUint64()) { - if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64()) + if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64()) { + context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); + } } else if (minimum_.IsInt64()) /* do nothing */; // i >= 0 > minimum.Getint64() @@ -1152,19 +1301,25 @@ private: if (!maximum_.IsNull()) { if (maximum_.IsUint64()) { - if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64()) + if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64()) { + context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); + } } - else if (maximum_.IsInt64()) + else if (maximum_.IsInt64()) { + context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); // i >= 0 > maximum_ + } else if (!CheckDoubleMaximum(context, static_cast(i))) return false; } if (!multipleOf_.IsNull()) { if (multipleOf_.IsUint64()) { - if (i % multipleOf_.GetUint64() != 0) + if (i % multipleOf_.GetUint64() != 0) { + context.error_handler.NotMultipleOf(i, multipleOf_); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString()); + } } else if (!CheckDoubleMultipleOf(context, static_cast(i))) return false; @@ -1174,14 +1329,18 @@ private: } bool CheckDoubleMinimum(Context& context, double d) const { - if (exclusiveMinimum_ ? d <= minimum_.GetDouble() : d < minimum_.GetDouble()) + if (exclusiveMinimum_ ? d <= minimum_.GetDouble() : d < minimum_.GetDouble()) { + context.error_handler.BelowMinimum(d, minimum_, exclusiveMinimum_); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); + } return true; } bool CheckDoubleMaximum(Context& context, double d) const { - if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble()) + if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble()) { + context.error_handler.AboveMaximum(d, maximum_, exclusiveMaximum_); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); + } return true; } @@ -1189,11 +1348,29 @@ private: double a = std::abs(d), b = std::abs(multipleOf_.GetDouble()); double q = std::floor(a / b); double r = a - q * b; - if (r > 0.0) + if (r > 0.0) { + context.error_handler.NotMultipleOf(d, multipleOf_); RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString()); + } return true; } + void DisallowedType(Context& context, const ValueType& actualType) const { + ErrorHandler& eh = context.error_handler; + eh.StartDisallowedType(); + + if (type_ & (1 << kNullSchemaType)) eh.AddExpectedType(GetNullString()); + if (type_ & (1 << kBooleanSchemaType)) eh.AddExpectedType(GetBooleanString()); + if (type_ & (1 << kObjectSchemaType)) eh.AddExpectedType(GetObjectString()); + if (type_ & (1 << kArraySchemaType)) eh.AddExpectedType(GetArrayString()); + if (type_ & (1 << kStringSchemaType)) eh.AddExpectedType(GetStringString()); + + if (type_ & (1 << kNumberSchemaType)) eh.AddExpectedType(GetNumberString()); + else if (type_ & (1 << kIntegerSchemaType)) eh.AddExpectedType(GetIntegerString()); + + eh.EndDisallowedType(actualType); + } + struct Property { Property() : schema(), dependenciesSchema(), dependenciesValidatorIndex(), dependencies(), required(false) {} ~Property() { AllocatorType::Free(dependencies); } @@ -1218,6 +1395,9 @@ private: }; AllocatorType* allocator_; + SValue uri_; + PointerType pointer_; + const SchemaType* typeless_; uint64_t* enum_; SizeType enumCount_; SchemaArray allOf_; @@ -1258,6 +1438,8 @@ private: SValue multipleOf_; bool exclusiveMinimum_; bool exclusiveMaximum_; + + SizeType defaultValueLength_; }; template @@ -1267,7 +1449,7 @@ struct TokenHelper { char buffer[21]; size_t length = static_cast((sizeof(SizeType) == 4 ? u32toa(index, buffer) : u64toa(index, buffer)) - buffer); for (size_t i = 0; i < length; i++) - *documentStack.template Push() = buffer[i]; + *documentStack.template Push() = static_cast(buffer[i]); } }; @@ -1326,6 +1508,7 @@ public: typedef typename EncodingType::Ch Ch; typedef internal::Schema SchemaType; typedef GenericPointer PointerType; + typedef GenericValue URIType; friend class internal::Schema; template friend class GenericSchemaValidator; @@ -1335,19 +1518,29 @@ public: Compile a JSON document into schema document. \param document A JSON document as source. + \param uri The base URI of this schema document for purposes of violation reporting. + \param uriLength Length of \c name, in code points. \param remoteProvider An optional remote schema document provider for resolving remote reference. Can be null. \param allocator An optional allocator instance for allocating memory. Can be null. */ - explicit GenericSchemaDocument(const ValueType& document, IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0) : + explicit GenericSchemaDocument(const ValueType& document, const Ch* uri = 0, SizeType uriLength = 0, + IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0) : remoteProvider_(remoteProvider), allocator_(allocator), ownAllocator_(), root_(), + typeless_(), schemaMap_(allocator, kInitialSchemaMapSize), schemaRef_(allocator, kInitialSchemaRefSize) { if (!allocator_) - ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + + Ch noUri[1] = {0}; + uri_.SetString(uri ? uri : noUri, uriLength, *allocator_); + + typeless_ = static_cast(allocator_->Malloc(sizeof(SchemaType))); + new (typeless_) SchemaType(this, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), allocator_); // Generate root schema, it will call CreateSchema() to create sub-schemas, // And call AddRefSchema() if there are $ref. @@ -1365,6 +1558,9 @@ public: new (schemaMap_.template Push()) SchemaEntry(refEntry->source, const_cast(s), false, allocator_); } } + else if (refEntry->schema) + *refEntry->schema = typeless_; + refEntry->~SchemaRefEntry(); } @@ -1380,12 +1576,15 @@ public: allocator_(rhs.allocator_), ownAllocator_(rhs.ownAllocator_), root_(rhs.root_), + typeless_(rhs.typeless_), schemaMap_(std::move(rhs.schemaMap_)), - schemaRef_(std::move(rhs.schemaRef_)) + schemaRef_(std::move(rhs.schemaRef_)), + uri_(std::move(rhs.uri_)) { rhs.remoteProvider_ = 0; rhs.allocator_ = 0; rhs.ownAllocator_ = 0; + rhs.typeless_ = 0; } #endif @@ -1394,9 +1593,16 @@ public: while (!schemaMap_.Empty()) schemaMap_.template Pop(1)->~SchemaEntry(); + if (typeless_) { + typeless_->~SchemaType(); + Allocator::Free(typeless_); + } + RAPIDJSON_DELETE(ownAllocator_); } + const URIType& GetURI() const { return uri_; } + //! Get the root schema. const SchemaType& GetRoot() const { return *root_; } @@ -1428,7 +1634,7 @@ private: void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) { if (schema) - *schema = SchemaType::GetTypeless(); + *schema = typeless_; if (v.GetType() == kObjectType) { const SchemaType* s = GetSchema(pointer); @@ -1473,12 +1679,13 @@ private: if (i > 0) { // Remote reference, resolve immediately if (remoteProvider_) { - if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(s, i - 1)) { + if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(s, i)) { PointerType pointer(&s[i], len - i, allocator_); if (pointer.IsValid()) { if (const SchemaType* sc = remoteDocument->GetSchema(pointer)) { if (schema) *schema = sc; + new (schemaMap_.template Push()) SchemaEntry(source, const_cast(sc), false, allocator_); return true; } } @@ -1515,6 +1722,8 @@ private: return PointerType(); } + const SchemaType* GetTypeless() const { return typeless_; } + static const size_t kInitialSchemaMapSize = 64; static const size_t kInitialSchemaRefSize = 64; @@ -1522,8 +1731,10 @@ private: Allocator *allocator_; Allocator *ownAllocator_; const SchemaType* root_; //!< Root schema. + SchemaType* typeless_; internal::Stack schemaMap_; // Stores created Pointer -> Schemas internal::Stack schemaRef_; // Stores Pointer from $ref and schema which holds the $ref + URIType uri_; }; //! GenericSchemaDocument using Value type. @@ -1552,13 +1763,17 @@ template < typename StateAllocator = CrtAllocator> class GenericSchemaValidator : public internal::ISchemaStateFactory, - public internal::ISchemaValidator + public internal::ISchemaValidator, + public internal::IValidationErrorHandler { public: typedef typename SchemaDocumentType::SchemaType SchemaType; typedef typename SchemaDocumentType::PointerType PointerType; typedef typename SchemaType::EncodingType EncodingType; + typedef typename SchemaType::SValue SValue; typedef typename EncodingType::Ch Ch; + typedef GenericStringRef StringRefType; + typedef GenericValue ValueType; //! Constructor without output handler. /*! @@ -1575,11 +1790,14 @@ public: : schemaDocument_(&schemaDocument), root_(schemaDocument.GetRoot()), - outputHandler_(GetNullHandler()), stateAllocator_(allocator), ownStateAllocator_(0), schemaStack_(allocator, schemaStackCapacity), documentStack_(allocator, documentStackCapacity), + outputHandler_(0), + error_(kObjectType), + currentError_(), + missingDependents_(), valid_(true) #if RAPIDJSON_SCHEMA_VERBOSE , depth_(0) @@ -1603,11 +1821,14 @@ public: : schemaDocument_(&schemaDocument), root_(schemaDocument.GetRoot()), - outputHandler_(outputHandler), stateAllocator_(allocator), ownStateAllocator_(0), schemaStack_(allocator, schemaStackCapacity), documentStack_(allocator, documentStackCapacity), + outputHandler_(&outputHandler), + error_(kObjectType), + currentError_(), + missingDependents_(), valid_(true) #if RAPIDJSON_SCHEMA_VERBOSE , depth_(0) @@ -1626,6 +1847,9 @@ public: while (!schemaStack_.Empty()) PopSchema(); documentStack_.Clear(); + error_.SetObject(); + currentError_.SetNull(); + missingDependents_.SetNull(); valid_ = true; } @@ -1633,9 +1857,13 @@ public: // Implementation of ISchemaValidator virtual bool IsValid() const { return valid_; } + //! Gets the error object. + ValueType& GetError() { return error_; } + const ValueType& GetError() const { return error_; } + //! Gets the JSON pointer pointed to the invalid schema. PointerType GetInvalidSchemaPointer() const { - return schemaStack_.Empty() ? PointerType() : schemaDocument_->GetPointer(&CurrentSchema()); + return schemaStack_.Empty() ? PointerType() : CurrentSchema().GetPointer(); } //! Gets the keyword of invalid schema. @@ -1645,9 +1873,196 @@ public: //! Gets the JSON pointer pointed to the invalid value. PointerType GetInvalidDocumentPointer() const { - return documentStack_.Empty() ? PointerType() : PointerType(documentStack_.template Bottom(), documentStack_.GetSize() / sizeof(Ch)); + if (documentStack_.Empty()) { + return PointerType(); + } + else { + return PointerType(documentStack_.template Bottom(), documentStack_.GetSize() / sizeof(Ch)); + } } + void NotMultipleOf(int64_t actual, const SValue& expected) { + AddNumberError(SchemaType::GetMultipleOfString(), ValueType(actual).Move(), expected); + } + void NotMultipleOf(uint64_t actual, const SValue& expected) { + AddNumberError(SchemaType::GetMultipleOfString(), ValueType(actual).Move(), expected); + } + void NotMultipleOf(double actual, const SValue& expected) { + AddNumberError(SchemaType::GetMultipleOfString(), ValueType(actual).Move(), expected); + } + void AboveMaximum(int64_t actual, const SValue& expected, bool exclusive) { + AddNumberError(SchemaType::GetMaximumString(), ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMaximumString : 0); + } + void AboveMaximum(uint64_t actual, const SValue& expected, bool exclusive) { + AddNumberError(SchemaType::GetMaximumString(), ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMaximumString : 0); + } + void AboveMaximum(double actual, const SValue& expected, bool exclusive) { + AddNumberError(SchemaType::GetMaximumString(), ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMaximumString : 0); + } + void BelowMinimum(int64_t actual, const SValue& expected, bool exclusive) { + AddNumberError(SchemaType::GetMinimumString(), ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMinimumString : 0); + } + void BelowMinimum(uint64_t actual, const SValue& expected, bool exclusive) { + AddNumberError(SchemaType::GetMinimumString(), ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMinimumString : 0); + } + void BelowMinimum(double actual, const SValue& expected, bool exclusive) { + AddNumberError(SchemaType::GetMinimumString(), ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMinimumString : 0); + } + + void TooLong(const Ch* str, SizeType length, SizeType expected) { + AddNumberError(SchemaType::GetMaxLengthString(), + ValueType(str, length, GetStateAllocator()).Move(), SValue(expected).Move()); + } + void TooShort(const Ch* str, SizeType length, SizeType expected) { + AddNumberError(SchemaType::GetMinLengthString(), + ValueType(str, length, GetStateAllocator()).Move(), SValue(expected).Move()); + } + void DoesNotMatch(const Ch* str, SizeType length) { + currentError_.SetObject(); + currentError_.AddMember(GetActualString(), ValueType(str, length, GetStateAllocator()).Move(), GetStateAllocator()); + AddCurrentError(SchemaType::GetPatternString()); + } + + void DisallowedItem(SizeType index) { + currentError_.SetObject(); + currentError_.AddMember(GetDisallowedString(), ValueType(index).Move(), GetStateAllocator()); + AddCurrentError(SchemaType::GetAdditionalItemsString(), true); + } + void TooFewItems(SizeType actualCount, SizeType expectedCount) { + AddNumberError(SchemaType::GetMinItemsString(), + ValueType(actualCount).Move(), SValue(expectedCount).Move()); + } + void TooManyItems(SizeType actualCount, SizeType expectedCount) { + AddNumberError(SchemaType::GetMaxItemsString(), + ValueType(actualCount).Move(), SValue(expectedCount).Move()); + } + void DuplicateItems(SizeType index1, SizeType index2) { + ValueType duplicates(kArrayType); + duplicates.PushBack(index1, GetStateAllocator()); + duplicates.PushBack(index2, GetStateAllocator()); + currentError_.SetObject(); + currentError_.AddMember(GetDuplicatesString(), duplicates, GetStateAllocator()); + AddCurrentError(SchemaType::GetUniqueItemsString(), true); + } + + void TooManyProperties(SizeType actualCount, SizeType expectedCount) { + AddNumberError(SchemaType::GetMaxPropertiesString(), + ValueType(actualCount).Move(), SValue(expectedCount).Move()); + } + void TooFewProperties(SizeType actualCount, SizeType expectedCount) { + AddNumberError(SchemaType::GetMinPropertiesString(), + ValueType(actualCount).Move(), SValue(expectedCount).Move()); + } + void StartMissingProperties() { + currentError_.SetArray(); + } + void AddMissingProperty(const SValue& name) { + currentError_.PushBack(ValueType(name, GetStateAllocator()).Move(), GetStateAllocator()); + } + bool EndMissingProperties() { + if (currentError_.Empty()) + return false; + ValueType error(kObjectType); + error.AddMember(GetMissingString(), currentError_, GetStateAllocator()); + currentError_ = error; + AddCurrentError(SchemaType::GetRequiredString()); + return true; + } + void PropertyViolations(ISchemaValidator** subvalidators, SizeType count) { + for (SizeType i = 0; i < count; ++i) + MergeError(static_cast(subvalidators[i])->GetError()); + } + void DisallowedProperty(const Ch* name, SizeType length) { + currentError_.SetObject(); + currentError_.AddMember(GetDisallowedString(), ValueType(name, length, GetStateAllocator()).Move(), GetStateAllocator()); + AddCurrentError(SchemaType::GetAdditionalPropertiesString(), true); + } + + void StartDependencyErrors() { + currentError_.SetObject(); + } + void StartMissingDependentProperties() { + missingDependents_.SetArray(); + } + void AddMissingDependentProperty(const SValue& targetName) { + missingDependents_.PushBack(ValueType(targetName, GetStateAllocator()).Move(), GetStateAllocator()); + } + void EndMissingDependentProperties(const SValue& sourceName) { + if (!missingDependents_.Empty()) + currentError_.AddMember(ValueType(sourceName, GetStateAllocator()).Move(), + missingDependents_, GetStateAllocator()); + } + void AddDependencySchemaError(const SValue& sourceName, ISchemaValidator* subvalidator) { + currentError_.AddMember(ValueType(sourceName, GetStateAllocator()).Move(), + static_cast(subvalidator)->GetError(), GetStateAllocator()); + } + bool EndDependencyErrors() { + if (currentError_.ObjectEmpty()) + return false; + ValueType error(kObjectType); + error.AddMember(GetErrorsString(), currentError_, GetStateAllocator()); + currentError_ = error; + AddCurrentError(SchemaType::GetDependenciesString()); + return true; + } + + void DisallowedValue() { + currentError_.SetObject(); + AddCurrentError(SchemaType::GetEnumString()); + } + void StartDisallowedType() { + currentError_.SetArray(); + } + void AddExpectedType(const typename SchemaType::ValueType& expectedType) { + currentError_.PushBack(ValueType(expectedType, GetStateAllocator()).Move(), GetStateAllocator()); + } + void EndDisallowedType(const typename SchemaType::ValueType& actualType) { + ValueType error(kObjectType); + error.AddMember(GetExpectedString(), currentError_, GetStateAllocator()); + error.AddMember(GetActualString(), ValueType(actualType, GetStateAllocator()).Move(), GetStateAllocator()); + currentError_ = error; + AddCurrentError(SchemaType::GetTypeString()); + } + void NotAllOf(ISchemaValidator** subvalidators, SizeType count) { + for (SizeType i = 0; i < count; ++i) { + MergeError(static_cast(subvalidators[i])->GetError()); + } + } + void NoneOf(ISchemaValidator** subvalidators, SizeType count) { + AddErrorArray(SchemaType::GetAnyOfString(), subvalidators, count); + } + void NotOneOf(ISchemaValidator** subvalidators, SizeType count) { + AddErrorArray(SchemaType::GetOneOfString(), subvalidators, count); + } + void Disallowed() { + currentError_.SetObject(); + AddCurrentError(SchemaType::GetNotString()); + } + +#define RAPIDJSON_STRING_(name, ...) \ + static const StringRefType& Get##name##String() {\ + static const Ch s[] = { __VA_ARGS__, '\0' };\ + static const StringRefType v(s, static_cast(sizeof(s) / sizeof(Ch) - 1)); \ + return v;\ + } + + RAPIDJSON_STRING_(InstanceRef, 'i', 'n', 's', 't', 'a', 'n', 'c', 'e', 'R', 'e', 'f') + RAPIDJSON_STRING_(SchemaRef, 's', 'c', 'h', 'e', 'm', 'a', 'R', 'e', 'f') + RAPIDJSON_STRING_(Expected, 'e', 'x', 'p', 'e', 'c', 't', 'e', 'd') + RAPIDJSON_STRING_(Actual, 'a', 'c', 't', 'u', 'a', 'l') + RAPIDJSON_STRING_(Disallowed, 'd', 'i', 's', 'a', 'l', 'l', 'o', 'w', 'e', 'd') + RAPIDJSON_STRING_(Missing, 'm', 'i', 's', 's', 'i', 'n', 'g') + RAPIDJSON_STRING_(Errors, 'e', 'r', 'r', 'o', 'r', 's') + RAPIDJSON_STRING_(Duplicates, 'd', 'u', 'p', 'l', 'i', 'c', 'a', 't', 'e', 's') + +#undef RAPIDJSON_STRING_ + #if RAPIDJSON_SCHEMA_VERBOSE #define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() \ RAPIDJSON_MULTILINEMACRO_BEGIN\ @@ -1679,14 +2094,14 @@ RAPIDJSON_MULTILINEMACRO_END } #define RAPIDJSON_SCHEMA_HANDLE_END_(method, arg2)\ - return valid_ = EndValue() && outputHandler_.method arg2 + return valid_ = EndValue() && (!outputHandler_ || outputHandler_->method arg2) #define RAPIDJSON_SCHEMA_HANDLE_VALUE_(method, arg1, arg2) \ RAPIDJSON_SCHEMA_HANDLE_BEGIN_ (method, arg1);\ RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2);\ RAPIDJSON_SCHEMA_HANDLE_END_ (method, arg2) - bool Null() { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Null, (CurrentContext() ), ( )); } + bool Null() { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Null, (CurrentContext()), ( )); } bool Bool(bool b) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Bool, (CurrentContext(), b), (b)); } bool Int(int i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int, (CurrentContext(), i), (i)); } bool Uint(unsigned u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint, (CurrentContext(), u), (u)); } @@ -1701,7 +2116,7 @@ RAPIDJSON_MULTILINEMACRO_END bool StartObject() { RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartObject, (CurrentContext())); RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartObject, ()); - return valid_ = outputHandler_.StartObject(); + return valid_ = !outputHandler_ || outputHandler_->StartObject(); } bool Key(const Ch* str, SizeType len, bool copy) { @@ -1709,7 +2124,7 @@ RAPIDJSON_MULTILINEMACRO_END AppendToken(str, len); if (!CurrentSchema().Key(CurrentContext(), str, len, copy)) return valid_ = false; RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy)); - return valid_ = outputHandler_.Key(str, len, copy); + return valid_ = !outputHandler_ || outputHandler_->Key(str, len, copy); } bool EndObject(SizeType memberCount) { @@ -1722,7 +2137,7 @@ RAPIDJSON_MULTILINEMACRO_END bool StartArray() { RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartArray, (CurrentContext())); RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartArray, ()); - return valid_ = outputHandler_.StartArray(); + return valid_ = !outputHandler_ || outputHandler_->StartArray(); } bool EndArray(SizeType elementCount) { @@ -1739,7 +2154,7 @@ RAPIDJSON_MULTILINEMACRO_END // Implementation of ISchemaStateFactory virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root) { - return new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root, + return new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root, documentStack_.template Bottom(), documentStack_.GetSize(), #if RAPIDJSON_SCHEMA_VERBOSE depth_ + 1, #endif @@ -1771,7 +2186,7 @@ RAPIDJSON_MULTILINEMACRO_END } virtual void FreeState(void* p) { - return StateAllocator::Free(p); + StateAllocator::Free(p); } private: @@ -1782,6 +2197,7 @@ private: GenericSchemaValidator( const SchemaDocumentType& schemaDocument, const SchemaType& root, + const char* basePath, size_t basePathSize, #if RAPIDJSON_SCHEMA_VERBOSE unsigned depth, #endif @@ -1791,21 +2207,26 @@ private: : schemaDocument_(&schemaDocument), root_(root), - outputHandler_(GetNullHandler()), stateAllocator_(allocator), ownStateAllocator_(0), schemaStack_(allocator, schemaStackCapacity), documentStack_(allocator, documentStackCapacity), + outputHandler_(0), + error_(kObjectType), + currentError_(), + missingDependents_(), valid_(true) #if RAPIDJSON_SCHEMA_VERBOSE , depth_(depth) #endif { + if (basePath && basePathSize) + memcpy(documentStack_.template Push(basePathSize), basePath, basePathSize); } StateAllocator& GetStateAllocator() { if (!stateAllocator_) - stateAllocator_ = ownStateAllocator_ = RAPIDJSON_NEW(StateAllocator()); + stateAllocator_ = ownStateAllocator_ = RAPIDJSON_NEW(StateAllocator)(); return *stateAllocator_; } @@ -1823,8 +2244,8 @@ private: const SchemaType** sa = CurrentContext().patternPropertiesSchemas; typename Context::PatternValidatorType patternValidatorType = CurrentContext().valuePatternValidatorType; bool valueUniqueness = CurrentContext().valueUniqueness; - if (CurrentContext().valueSchema) - PushSchema(*CurrentContext().valueSchema); + RAPIDJSON_ASSERT(CurrentContext().valueSchema); + PushSchema(*CurrentContext().valueSchema); if (count > 0) { CurrentContext().objectPatternValidatorType = patternValidatorType; @@ -1864,8 +2285,10 @@ private: if (!a) CurrentContext().arrayElementHashCodes = a = new (GetStateAllocator().Malloc(sizeof(HashCodeArray))) HashCodeArray(kArrayType); for (typename HashCodeArray::ConstValueIterator itr = a->Begin(); itr != a->End(); ++itr) - if (itr->GetUint64() == h) + if (itr->GetUint64() == h) { + DuplicateItems(static_cast(itr - a->Begin()), a->Size()); RAPIDJSON_INVALID_KEYWORD_RETURN(SchemaType::GetUniqueItemsString()); + } a->PushBack(h, GetStateAllocator()); } } @@ -1894,7 +2317,7 @@ private: } } - RAPIDJSON_FORCEINLINE void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push()) Context(*this, &schema); } + RAPIDJSON_FORCEINLINE void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push()) Context(*this, *this, &schema); } RAPIDJSON_FORCEINLINE void PopSchema() { Context* c = schemaStack_.template Pop(1); @@ -1905,24 +2328,86 @@ private: c->~Context(); } + void AddErrorLocation(ValueType& result, bool parent) { + GenericStringBuffer sb; + PointerType instancePointer = GetInvalidDocumentPointer(); + ((parent && instancePointer.GetTokenCount() > 0) + ? PointerType(instancePointer.GetTokens(), instancePointer.GetTokenCount() - 1) + : instancePointer).StringifyUriFragment(sb); + ValueType instanceRef(sb.GetString(), static_cast(sb.GetSize() / sizeof(Ch)), + GetStateAllocator()); + result.AddMember(GetInstanceRefString(), instanceRef, GetStateAllocator()); + sb.Clear(); + memcpy(sb.Push(CurrentSchema().GetURI().GetStringLength()), + CurrentSchema().GetURI().GetString(), + CurrentSchema().GetURI().GetStringLength() * sizeof(Ch)); + GetInvalidSchemaPointer().StringifyUriFragment(sb); + ValueType schemaRef(sb.GetString(), static_cast(sb.GetSize() / sizeof(Ch)), + GetStateAllocator()); + result.AddMember(GetSchemaRefString(), schemaRef, GetStateAllocator()); + } + + void AddError(ValueType& keyword, ValueType& error) { + typename ValueType::MemberIterator member = error_.FindMember(keyword); + if (member == error_.MemberEnd()) + error_.AddMember(keyword, error, GetStateAllocator()); + else { + if (member->value.IsObject()) { + ValueType errors(kArrayType); + errors.PushBack(member->value, GetStateAllocator()); + member->value = errors; + } + member->value.PushBack(error, GetStateAllocator()); + } + } + + void AddCurrentError(const typename SchemaType::ValueType& keyword, bool parent = false) { + AddErrorLocation(currentError_, parent); + AddError(ValueType(keyword, GetStateAllocator(), false).Move(), currentError_); + } + + void MergeError(ValueType& other) { + for (typename ValueType::MemberIterator it = other.MemberBegin(), end = other.MemberEnd(); it != end; ++it) { + AddError(it->name, it->value); + } + } + + void AddNumberError(const typename SchemaType::ValueType& keyword, ValueType& actual, const SValue& expected, + const typename SchemaType::ValueType& (*exclusive)() = 0) { + currentError_.SetObject(); + currentError_.AddMember(GetActualString(), actual, GetStateAllocator()); + currentError_.AddMember(GetExpectedString(), ValueType(expected, GetStateAllocator()).Move(), GetStateAllocator()); + if (exclusive) + currentError_.AddMember(ValueType(exclusive(), GetStateAllocator()).Move(), true, GetStateAllocator()); + AddCurrentError(keyword); + } + + void AddErrorArray(const typename SchemaType::ValueType& keyword, + ISchemaValidator** subvalidators, SizeType count) { + ValueType errors(kArrayType); + for (SizeType i = 0; i < count; ++i) + errors.PushBack(static_cast(subvalidators[i])->GetError(), GetStateAllocator()); + currentError_.SetObject(); + currentError_.AddMember(GetErrorsString(), errors, GetStateAllocator()); + AddCurrentError(keyword); + } + const SchemaType& CurrentSchema() const { return *schemaStack_.template Top()->schema; } Context& CurrentContext() { return *schemaStack_.template Top(); } const Context& CurrentContext() const { return *schemaStack_.template Top(); } - static OutputHandler& GetNullHandler() { - static OutputHandler nullHandler; - return nullHandler; - } - static const size_t kDefaultSchemaStackCapacity = 1024; static const size_t kDefaultDocumentStackCapacity = 256; const SchemaDocumentType* schemaDocument_; const SchemaType& root_; - OutputHandler& outputHandler_; StateAllocator* stateAllocator_; StateAllocator* ownStateAllocator_; internal::Stack schemaStack_; //!< stack to store the current path of schema (BaseSchemaType *) internal::Stack documentStack_; //!< stack to store the current path of validating document (Ch) + OutputHandler* outputHandler_; + ValueType error_; + ValueType currentError_; + ValueType missingDependents_; bool valid_; #if RAPIDJSON_SCHEMA_VERBOSE unsigned depth_; @@ -1954,13 +2439,14 @@ class SchemaValidatingReader { public: typedef typename SchemaDocumentType::PointerType PointerType; typedef typename InputStream::Ch Ch; + typedef GenericValue ValueType; //! Constructor /*! \param is Input stream. \param sd Schema document. */ - SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), isValid_(true) {} + SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), error_(kObjectType), isValid_(true) {} template bool operator()(Handler& handler) { @@ -1973,11 +2459,13 @@ public: invalidSchemaPointer_ = PointerType(); invalidSchemaKeyword_ = 0; invalidDocumentPointer_ = PointerType(); + error_.SetObject(); } else { invalidSchemaPointer_ = validator.GetInvalidSchemaPointer(); invalidSchemaKeyword_ = validator.GetInvalidSchemaKeyword(); invalidDocumentPointer_ = validator.GetInvalidDocumentPointer(); + error_.CopyFrom(validator.GetError(), allocator_); } return parseResult_; @@ -1988,6 +2476,7 @@ public: const PointerType& GetInvalidSchemaPointer() const { return invalidSchemaPointer_; } const Ch* GetInvalidSchemaKeyword() const { return invalidSchemaKeyword_; } const PointerType& GetInvalidDocumentPointer() const { return invalidDocumentPointer_; } + const ValueType& GetError() const { return error_; } private: InputStream& is_; @@ -1997,6 +2486,8 @@ private: PointerType invalidSchemaPointer_; const Ch* invalidSchemaKeyword_; PointerType invalidDocumentPointer_; + StackAllocator allocator_; + ValueType error_; bool isValid_; }; diff --git a/src/3rdparty/rapidjson/stream.h b/src/3rdparty/rapidjson/stream.h index fef82c25..7f2643e4 100644 --- a/src/3rdparty/rapidjson/stream.h +++ b/src/3rdparty/rapidjson/stream.h @@ -1,5 +1,5 @@ // Tencent is pleased to support the open source community by making RapidJSON available. -// +// // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. // // Licensed under the MIT License (the "License"); you may not use this file except @@ -7,9 +7,9 @@ // // http://opensource.org/licenses/MIT // -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. #include "rapidjson.h" @@ -100,6 +100,50 @@ inline void PutN(Stream& stream, Ch c, size_t n) { PutUnsafe(stream, c); } +/////////////////////////////////////////////////////////////////////////////// +// GenericStreamWrapper + +//! A Stream Wrapper +/*! \tThis string stream is a wrapper for any stream by just forwarding any + \treceived message to the origin stream. + \note implements Stream concept +*/ + +#if defined(_MSC_VER) && _MSC_VER <= 1800 +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4702) // unreachable code +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +template > +class GenericStreamWrapper { +public: + typedef typename Encoding::Ch Ch; + GenericStreamWrapper(InputStream& is): is_(is) {} + + Ch Peek() const { return is_.Peek(); } + Ch Take() { return is_.Take(); } + size_t Tell() { return is_.Tell(); } + Ch* PutBegin() { return is_.PutBegin(); } + void Put(Ch ch) { is_.Put(ch); } + void Flush() { is_.Flush(); } + size_t PutEnd(Ch* ch) { return is_.PutEnd(ch); } + + // wrapper for MemoryStream + const Ch* Peek4() const { return is_.Peek4(); } + + // wrapper for AutoUTFInputStream + UTFType GetType() const { return is_.GetType(); } + bool HasBOM() const { return is_.HasBOM(); } + +protected: + InputStream& is_; +}; + +#if defined(_MSC_VER) && _MSC_VER <= 1800 +RAPIDJSON_DIAG_POP +#endif + /////////////////////////////////////////////////////////////////////////////// // StringStream diff --git a/src/3rdparty/rapidjson/stringbuffer.h b/src/3rdparty/rapidjson/stringbuffer.h index 78f34d20..4e38b82c 100644 --- a/src/3rdparty/rapidjson/stringbuffer.h +++ b/src/3rdparty/rapidjson/stringbuffer.h @@ -78,8 +78,12 @@ public: return stack_.template Bottom(); } + //! Get the size of string in bytes in the string buffer. size_t GetSize() const { return stack_.GetSize(); } + //! Get the length of string in Ch in the string buffer. + size_t GetLength() const { return stack_.GetSize() / sizeof(Ch); } + static const size_t kDefaultCapacity = 256; mutable internal::Stack stack_; diff --git a/src/3rdparty/rapidjson/writer.h b/src/3rdparty/rapidjson/writer.h index 94f22dd5..1d33b2f9 100644 --- a/src/3rdparty/rapidjson/writer.h +++ b/src/3rdparty/rapidjson/writer.h @@ -16,6 +16,7 @@ #define RAPIDJSON_WRITER_H_ #include "stream.h" +#include "internal/meta.h" #include "internal/stack.h" #include "internal/strfunc.h" #include "internal/dtoa.h" @@ -31,17 +32,18 @@ #include #elif defined(RAPIDJSON_SSE2) #include -#endif - -#ifdef _MSC_VER -RAPIDJSON_DIAG_PUSH -RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +#elif defined(RAPIDJSON_NEON) +#include #endif #ifdef __clang__ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(padded) RAPIDJSON_DIAG_OFF(unreachable-code) +RAPIDJSON_DIAG_OFF(c++98-compat) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant #endif RAPIDJSON_NAMESPACE_BEGIN @@ -103,6 +105,13 @@ public: Writer(StackAllocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth) : os_(0), level_stack_(allocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {} +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + Writer(Writer&& rhs) : + os_(rhs.os_), level_stack_(std::move(rhs.level_stack_)), maxDecimalPlaces_(rhs.maxDecimalPlaces_), hasRoot_(rhs.hasRoot_) { + rhs.os_ = 0; + } +#endif + //! Reset the writer with a new stream. /*! This function reset the writer with a new stream and default settings, @@ -184,12 +193,14 @@ public: bool Double(double d) { Prefix(kNumberType); return EndValue(WriteDouble(d)); } bool RawNumber(const Ch* str, SizeType length, bool copy = false) { + RAPIDJSON_ASSERT(str != 0); (void)copy; Prefix(kNumberType); return EndValue(WriteString(str, length)); } bool String(const Ch* str, SizeType length, bool copy = false) { + RAPIDJSON_ASSERT(str != 0); (void)copy; Prefix(kStringType); return EndValue(WriteString(str, length)); @@ -209,10 +220,18 @@ public: bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } +#if RAPIDJSON_HAS_STDSTRING + bool Key(const std::basic_string& str) + { + return Key(str.data(), SizeType(str.size())); + } +#endif + bool EndObject(SizeType memberCount = 0) { (void)memberCount; - RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); - RAPIDJSON_ASSERT(!level_stack_.template Top()->inArray); + RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); // not inside an Object + RAPIDJSON_ASSERT(!level_stack_.template Top()->inArray); // currently inside an Array, not Object + RAPIDJSON_ASSERT(0 == level_stack_.template Top()->valueCount % 2); // Object has a Key without a Value level_stack_.template Pop(1); return EndValue(WriteEndObject()); } @@ -236,9 +255,9 @@ public: //@{ //! Simpler but slower overload. - bool String(const Ch* str) { return String(str, internal::StrLen(str)); } - bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); } - + bool String(const Ch* const& str) { return String(str, internal::StrLen(str)); } + bool Key(const Ch* const& str) { return Key(str, internal::StrLen(str)); } + //@} //! Write a raw JSON value. @@ -249,7 +268,19 @@ public: \param length Length of the json. \param type Type of the root of json. */ - bool RawValue(const Ch* json, size_t length, Type type) { Prefix(type); return EndValue(WriteRawValue(json, length)); } + bool RawValue(const Ch* json, size_t length, Type type) { + RAPIDJSON_ASSERT(json != 0); + Prefix(type); + return EndValue(WriteRawValue(json, length)); + } + + //! Flush the output stream. + /*! + Allows the user to flush the output stream immediately. + */ + void Flush() { + os_->Flush(); + } protected: //! Information for each nested level @@ -257,6 +288,7 @@ protected: Level(bool inArray_) : valueCount(0), inArray(inArray_) {} size_t valueCount; //!< number of values in this level bool inArray; //!< true if in array, otherwise in object + bool inLine = false; }; static const size_t kDefaultLevelDepth = 32; @@ -283,7 +315,7 @@ protected: const char* end = internal::i32toa(i, buffer); PutReserve(*os_, static_cast(end - buffer)); for (const char* p = buffer; p != end; ++p) - PutUnsafe(*os_, static_cast(*p)); + PutUnsafe(*os_, static_cast(*p)); return true; } @@ -292,7 +324,7 @@ protected: const char* end = internal::u32toa(u, buffer); PutReserve(*os_, static_cast(end - buffer)); for (const char* p = buffer; p != end; ++p) - PutUnsafe(*os_, static_cast(*p)); + PutUnsafe(*os_, static_cast(*p)); return true; } @@ -301,7 +333,7 @@ protected: const char* end = internal::i64toa(i64, buffer); PutReserve(*os_, static_cast(end - buffer)); for (const char* p = buffer; p != end; ++p) - PutUnsafe(*os_, static_cast(*p)); + PutUnsafe(*os_, static_cast(*p)); return true; } @@ -310,7 +342,7 @@ protected: char* end = internal::u64toa(u64, buffer); PutReserve(*os_, static_cast(end - buffer)); for (char* p = buffer; p != end; ++p) - PutUnsafe(*os_, static_cast(*p)); + PutUnsafe(*os_, static_cast(*p)); return true; } @@ -338,12 +370,12 @@ protected: char* end = internal::dtoa(d, buffer, maxDecimalPlaces_); PutReserve(*os_, static_cast(end - buffer)); for (char* p = buffer; p != end; ++p) - PutUnsafe(*os_, static_cast(*p)); + PutUnsafe(*os_, static_cast(*p)); return true; } bool WriteString(const Ch* str, SizeType length) { - static const typename TargetEncoding::Ch hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + static const typename OutputStream::Ch hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; static const char escape[256] = { #define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 //0 1 2 3 4 5 6 7 8 9 A B C D E F @@ -399,7 +431,7 @@ protected: else if ((sizeof(Ch) == 1 || static_cast(c) < 256) && RAPIDJSON_UNLIKELY(escape[static_cast(c)])) { is.Take(); PutUnsafe(*os_, '\\'); - PutUnsafe(*os_, static_cast(escape[static_cast(c)])); + PutUnsafe(*os_, static_cast(escape[static_cast(c)])); if (escape[static_cast(c)] == 'u') { PutUnsafe(*os_, '0'); PutUnsafe(*os_, '0'); @@ -427,9 +459,13 @@ protected: bool WriteRawValue(const Ch* json, size_t length) { PutReserve(*os_, length); - for (size_t i = 0; i < length; i++) { - RAPIDJSON_ASSERT(json[i] != '\0'); - PutUnsafe(*os_, json[i]); + GenericStringStream is(json); + while (RAPIDJSON_LIKELY(is.Tell() < length)) { + RAPIDJSON_ASSERT(is.Peek() != '\0'); + if (RAPIDJSON_UNLIKELY(!(writeFlags & kWriteValidateEncodingFlag ? + Transcoder::Validate(is, *os_) : + Transcoder::TranscodeUnsafe(is, *os_)))) + return false; } return true; } @@ -457,7 +493,7 @@ protected: // Flush the value if it is the top level one. bool EndValue(bool ret) { if (RAPIDJSON_UNLIKELY(level_stack_.Empty())) // end of json text - os_->Flush(); + Flush(); return ret; } @@ -561,7 +597,7 @@ inline bool Writer::ScanWriteUnescapedString(StringStream& is, siz // The rest of string using SIMD static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; - static const char space[16] = { 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19 }; + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); @@ -570,7 +606,7 @@ inline bool Writer::ScanWriteUnescapedString(StringStream& is, siz const __m128i s = _mm_load_si128(reinterpret_cast(p)); const __m128i t1 = _mm_cmpeq_epi8(s, dq); const __m128i t2 = _mm_cmpeq_epi8(s, bs); - const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x19) == 0x19 + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); unsigned short r = static_cast(_mm_movemask_epi8(x)); if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped @@ -595,15 +631,79 @@ inline bool Writer::ScanWriteUnescapedString(StringStream& is, siz is.src_ = p; return RAPIDJSON_LIKELY(is.Tell() < length); } -#endif // defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) +#elif defined(RAPIDJSON_NEON) +template<> +inline bool Writer::ScanWriteUnescapedString(StringStream& is, size_t length) { + if (length < 16) + return RAPIDJSON_LIKELY(is.Tell() < length); + + if (!RAPIDJSON_LIKELY(is.Tell() < length)) + return false; + + const char* p = is.src_; + const char* end = is.head_ + length; + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + const char* endAligned = reinterpret_cast(reinterpret_cast(end) & static_cast(~15)); + if (nextAligned > end) + return true; + + while (p != nextAligned) + if (*p < 0x20 || *p == '\"' || *p == '\\') { + is.src_ = p; + return RAPIDJSON_LIKELY(is.Tell() < length); + } + else + os_->PutUnsafe(*p++); + + // The rest of string using SIMD + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (; p != endAligned; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(reinterpret_cast(x), 0); // extract + uint64_t high = vgetq_lane_u64(reinterpret_cast(x), 1); // extract + + SizeType len = 0; + bool escaped = false; + if (low == 0) { + if (high != 0) { + unsigned lz = (unsigned)__builtin_clzll(high); + len = 8 + (lz >> 3); + escaped = true; + } + } else { + unsigned lz = (unsigned)__builtin_clzll(low); + len = lz >> 3; + escaped = true; + } + if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped + char* q = reinterpret_cast(os_->PushUnsafe(len)); + for (size_t i = 0; i < len; i++) + q[i] = p[i]; + + p += len; + break; + } + vst1q_u8(reinterpret_cast(os_->PushUnsafe(16)), s); + } + + is.src_ = p; + return RAPIDJSON_LIKELY(is.Tell() < length); +} +#endif // RAPIDJSON_NEON RAPIDJSON_NAMESPACE_END -#ifdef _MSC_VER -RAPIDJSON_DIAG_POP -#endif - -#ifdef __clang__ +#if defined(_MSC_VER) || defined(__clang__) RAPIDJSON_DIAG_POP #endif diff --git a/src/App.cpp b/src/App.cpp index cc93220f..3425bbd3 100644 --- a/src/App.cpp +++ b/src/App.cpp @@ -4,9 +4,10 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * Copyright 2017- BenDr0id - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018 Lee Clagett + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , * * 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 @@ -27,213 +28,95 @@ #include #include + #include "App.h" -#include "Console.h" -#include "Cpu.h" -#include "crypto/HashSelector.h" -#include "log/ConsoleLog.h" -#include "log/FileLog.h" -#include "log/RemoteLog.h" -#include "log/Log.h" -#include "Mem.h" +#include "base/io/Console.h" +#include "base/io/log/Log.h" +#include "base/kernel/Signals.h" +#include "core/config/Config.h" +#include "core/Controller.h" +#include "core/Miner.h" +#include "crypto/common/VirtualMemory.h" #include "net/Network.h" -#include "Platform.h" #include "Summary.h" -#include "workers/Workers.h" -#include "cc/CCClient.h" -#include "net/Url.h" - -#ifdef HAVE_SYSLOG_H -# include "log/SysLog.h" -#endif - -#ifndef XMRIG_NO_HTTPD -# include "api/Httpd.h" -# include "api/Api.h" -#endif - - -App *App::m_self = nullptr; - - -App::App(int argc, char **argv) : - m_restart(false), +xmrig::App::App(Process *process) : m_console(nullptr), - m_httpd(nullptr), - m_network(nullptr), - m_options(nullptr), - m_ccclient(nullptr) + m_signals(nullptr) { - m_self = this; - - Cpu::init(); - - m_options = Options::parse(argc, argv); - if (!m_options) { + m_controller = new Controller(process); + if (m_controller->init() != 0) { return; } - Log::init(); - -# ifdef WIN32 - if (!m_options->background()) { -# endif - Log::add(new ConsoleLog(m_options->colors())); + if (!m_controller->config()->isBackground()) { m_console = new Console(this); -# ifdef WIN32 } -# endif - - if (m_options->logFile()) { - Log::add(new FileLog(m_options->logFile())); - } - - if (m_options->ccUseRemoteLogging()) { - // 20 lines per second should be enough - Log::add(new RemoteLog(static_cast(m_options->ccUpdateInterval() * 20))); - } - -# ifdef HAVE_SYSLOG_H - if (m_options->syslog()) { - Log::add(new SysLog()); - } -# endif - - Platform::init(m_options->userAgent()); - Platform::setProcessPriority(m_options->priority()); - - m_network = new Network(m_options); - - uv_signal_init(uv_default_loop(), &m_sigHUP); - uv_signal_init(uv_default_loop(), &m_sigINT); - uv_signal_init(uv_default_loop(), &m_sigTERM); -} - -App::~App() -{ - delete m_network; - - Options::release(); - Platform::release(); - - uv_tty_reset_mode(); - -# ifndef XMRIG_NO_HTTPD - delete m_httpd; -# endif - -# ifndef XMRIG_NO_CC - if (m_ccclient) { - delete m_ccclient; - } -# endif } -int App::start() +xmrig::App::~App() { - if (!m_options) { - return EINVAL; + delete m_signals; + delete m_console; + delete m_controller; +} + + +int xmrig::App::exec() +{ + if (!m_controller->isReady()) { + return 2; } - uv_signal_start(&m_sigHUP, App::onSignal, SIGHUP); - uv_signal_start(&m_sigINT, App::onSignal, SIGINT); - uv_signal_start(&m_sigTERM, App::onSignal, SIGTERM); + m_signals = new Signals(this); background(); - if (Options::i()->colors()) { - LOG_INFO(WHITE_BOLD("%s hash self-test"), m_options->algoName()); - } - else { - LOG_INFO("%s hash self-test", m_options->algoName()); + VirtualMemory::init(m_controller->config()->cpu().isHugePages()); + + Summary::print(m_controller); + + if (m_controller->config()->isDryRun()) { + LOG_NOTICE("OK"); + + return 0; } - if (!HashSelector::init(m_options->algo(), m_options->aesni())) { - LOG_ERR("%s hash self-test... failed.", m_options->algoName()); - return EINVAL; - } else { - if (Options::i()->colors()) { - LOG_INFO(WHITE_BOLD("%s hash self-test... %s."), - m_options->algoName(), - Options::i()->skipSelfCheck() ? YELLOW_BOLD("skipped") : GREEN_BOLD("successful")); - } - else { - LOG_INFO("%s hash self-test... %s.", - m_options->algoName(), - Options::i()->skipSelfCheck() ? "skipped" : "successful"); - } - } + m_controller->start(); - Mem::init(m_options); - - Summary::print(); - -# ifndef XMRIG_NO_API - Api::start(); +# if XMRIG_FEATURE_CC_CLIENT + m_controller->ccClient()->addCommandListener(this); # endif -# ifndef XMRIG_NO_HTTPD - m_httpd = new Httpd(m_options->apiPort(), m_options->apiToken()); - m_httpd->start(); -# endif - -# ifndef XMRIG_NO_CC - if (m_options->ccHost() && m_options->ccPort() > 0) { - uv_async_init(uv_default_loop(), &m_async, App::onCommandReceived); - - m_ccclient = new CCClient(m_options, &m_async); - - if (! m_options->pools().front()->isValid()) { - LOG_WARN("No pool URL supplied, but CC server configured. Trying."); - } - } else { - LOG_WARN("Please configure CC-Url and restart. CC feature is now deactivated."); - } -# endif - - Workers::start(m_options->threads(), m_options->affinity(), m_options->priority()); - - if (m_options->pools().front()->isValid()) { - m_network->connect(); - } - const int r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); uv_loop_close(uv_default_loop()); return m_restart ? EINTR : r; } -void App::onConsoleCommand(char command) + +void xmrig::App::onConsoleCommand(char command) { switch (command) { case 'h': case 'H': - Workers::printHashrate(true); + m_controller->miner()->printHashrate(true); break; case 'p': case 'P': - if (Workers::isEnabled()) { - LOG_INFO(m_options->colors() ? "\x1B[01;33mpaused\x1B[0m, press \x1B[01;35mr\x1B[0m to resume" : "paused, press 'r' to resume"); - Workers::setEnabled(false); - } + m_controller->miner()->setEnabled(false); break; case 'r': case 'R': - if (!Workers::isEnabled()) { - LOG_INFO(m_options->colors() ? "\x1B[01;32mresumed" : "resumed"); - Workers::setEnabled(true); - } + m_controller->miner()->setEnabled(true); break; - case 'q': - case 'Q': case 3: - LOG_INFO(m_options->colors() ? "\x1B[01;33mquitting" : "quitting"); - shutdown(); + LOG_WARN("Ctrl+C received, exiting"); + close(false); break; default: @@ -242,36 +125,7 @@ void App::onConsoleCommand(char command) } -void App::stop(bool restart) -{ - m_restart = restart; - - m_network->stop(); - Workers::stop(); - - uv_stop(uv_default_loop()); -} - -void App::restart() -{ - m_self->stop(true); -} - -void App::shutdown() -{ - m_self->stop(false); -} - -void App::reboot() -{ - auto rebootCmd = m_self->m_options->ccRebootCmd(); - if (rebootCmd) { - system(rebootCmd); - shutdown(); - } -} - -void App::onSignal(uv_signal_t* handle, int signum) +void xmrig::App::onSignal(int signum) { switch (signum) { @@ -288,34 +142,55 @@ void App::onSignal(uv_signal_t* handle, int signum) break; default: - break; + return; } - uv_signal_stop(handle); - App::shutdown(); + close(false); } -void App::onCommandReceived(uv_async_t* async) +void xmrig::App::onCommandReceived(const ControlCommand& controlCommand) { - auto command = reinterpret_cast (async->data); - switch (command) { +# ifdef XMRIG_FEATURE_CC_CLIENT + switch (controlCommand.getCommand()) { case ControlCommand::START: - Workers::setEnabled(true); + m_controller->miner()->setEnabled(true); break; case ControlCommand::STOP: - Workers::setEnabled(false); + m_controller->miner()->setEnabled(false); break; - case ControlCommand::UPDATE_CONFIG:; case ControlCommand::RESTART: - App::restart(); + close(true); break; case ControlCommand::SHUTDOWN: - App::shutdown(); + close(false); break; case ControlCommand::REBOOT: - App::reboot(); - break; + reboot(); + case ControlCommand::UPDATE_CONFIG:; case ControlCommand::PUBLISH_CONFIG:; break; } +# endif } + +void xmrig::App::close(bool restart) +{ + m_restart = restart; + + m_signals->stop(); + m_console->stop(); + m_controller->stop(); + + Log::destroy(); +} + +# ifdef XMRIG_FEATURE_CC_CLIENT +void xmrig::App::reboot() +{ + auto rebootCmd = m_controller->config()->ccClient().rebootCmd(); + if (rebootCmd) { + system(rebootCmd); + close(false); + } +} +# endif diff --git a/src/App.h b/src/App.h index d5286d18..283f198f 100644 --- a/src/App.h +++ b/src/App.h @@ -4,8 +4,10 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018 Lee Clagett + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , * * 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 @@ -21,58 +23,59 @@ * along with this program. If not, see . */ -#ifndef __APP_H__ -#define __APP_H__ +#ifndef XMRIG_APP_H +#define XMRIG_APP_H -#include +#include "base/kernel/interfaces/IConsoleListener.h" +#include "base/kernel/interfaces/ISignalListener.h" +#include "base/cc/interfaces/ICommandListener.h" +#include "cc/ControlCommand.h" -#include "interfaces/IConsoleListener.h" +#if XMRIG_FEATURE_CC_CLIENT +#include "cc/CCClient.h" +#endif + +namespace xmrig { class Console; -class Httpd; +class Controller; class Network; -class Options; -class CCClient; +class Process; +class Signals; -class App : public IConsoleListener +class App : public IConsoleListener, public ISignalListener, public ICommandListener { public: - App(int argc, char **argv); - ~App(); + App(Process *process); + ~App() override; - int start(); - - static void restart(); - static void shutdown(); - static void reboot(); + int exec(); protected: - void onConsoleCommand(char command) override; + void onConsoleCommand(char command) override; + void onSignal(int signum) override; + void onCommandReceived(const ControlCommand& controlCommand) override; private: - void background(); - void stop(bool restart); + void background(); + void close(bool restart); - static void onSignal(uv_signal_t* handle, int signum); - static void onCommandReceived(uv_async_t* handle); +# ifdef XMRIG_FEATURE_CC_CLIENT + void reboot(); +# endif - static App *m_self; + bool m_restart = false; - bool m_restart; - - Console *m_console; - Httpd *m_httpd; - Network *m_network; - Options *m_options; - uv_signal_t m_sigHUP; - uv_signal_t m_sigINT; - uv_signal_t m_sigTERM; - CCClient *m_ccclient; - uv_async_t m_async; + Console *m_console; + Controller *m_controller; + Signals *m_signals; }; -#endif /* __APP_H__ */ +} /* namespace xmrig */ + + +#endif /* XMRIG_APP_H */ diff --git a/src/App_unix.cpp b/src/App_unix.cpp index 8511597c..5149513c 100644 --- a/src/App_unix.cpp +++ b/src/App_unix.cpp @@ -4,8 +4,9 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , * * 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,19 +23,43 @@ */ -#include -#include "App.h" -#include "Cpu.h" -#include "log/Log.h" -#include "Options.h" -#include "version.h" +#include +#include +#include +#include -void App::background() + +#include "App.h" +#include "base/io/log/Log.h" +#include "core/config/Config.h" +#include "core/Controller.h" + + +void xmrig::App::background() { - if (m_options->background()) { - Log::i()->text(Options::i()->colors() - ? "\x1B[01;31m\nBackground mode is not supported by %s on *nix Systems. Please use screen/tmux or systemd service instead.\n" - : "\nBackground mode is not supported by %s on *nix Systems. Please use screen/tmux or systemd service instead.\n", - APP_NAME); + signal(SIGPIPE, SIG_IGN); + + if (!m_controller->config()->isBackground()) { + return; } -} \ No newline at end of file + + int i = fork(); + if (i < 0) { + exit(1); + } + + if (i > 0) { + exit(0); + } + + i = setsid(); + + if (i < 0) { + LOG_ERR("setsid() failed (errno = %d)", errno); + } + + i = chdir("/"); + if (i < 0) { + LOG_ERR("chdir() failed (errno = %d)", errno); + } +} diff --git a/src/App_win.cpp b/src/App_win.cpp index 6b2716f0..a41ed505 100644 --- a/src/App_win.cpp +++ b/src/App_win.cpp @@ -4,8 +4,9 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , * * 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 @@ -27,13 +28,13 @@ #include "App.h" -#include "Options.h" -#include "Cpu.h" +#include "core/Controller.h" +#include "core/config/Config.h" -void App::background() +void xmrig::App::background() { - if (!m_options->background()) { + if (!m_controller->config()->isBackground()) { return; } diff --git a/src/AsmOptimization.h b/src/AsmOptimization.h deleted file mode 100644 index ec41381a..00000000 --- a/src/AsmOptimization.h +++ /dev/null @@ -1,97 +0,0 @@ -/* XMRigCC - * Copyright 2018- BenDr0id - * - * - * 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 . - */ - -#ifndef __ASM_OPTIMIZATION_H__ -#define __ASM_OPTIMIZATION_H__ - -#include - -enum AsmOptimization -{ - ASM_AUTODETECT, - ASM_INTEL, - ASM_RYZEN, - ASM_BULLDOZER, - ASM_OFF -}; - -inline std::string getAsmOptimizationName(AsmOptimization asmOptimization) -{ - switch (asmOptimization) - { - case ASM_INTEL: - return "INTEL"; - case ASM_RYZEN: - return "RYZEN"; - case ASM_BULLDOZER: - return "BULLDOZER"; - case ASM_OFF: - return "OFF"; - case ASM_AUTODETECT: - default: - return "-1"; - } -} - -inline AsmOptimization parseAsmOptimization(int optimization) -{ - AsmOptimization asmOptimization = AsmOptimization::ASM_AUTODETECT; - - switch (optimization) { - case -1: - asmOptimization = AsmOptimization::ASM_AUTODETECT; - break; - case 0: - asmOptimization = AsmOptimization::ASM_OFF; - break; - case 1: - asmOptimization = AsmOptimization::ASM_INTEL; - break; - case 2: - asmOptimization = AsmOptimization::ASM_RYZEN; - break; - case 3: - asmOptimization = AsmOptimization::ASM_AUTODETECT; - break; - default: - asmOptimization = AsmOptimization::ASM_AUTODETECT; - break; - } - - return asmOptimization; -} - -inline AsmOptimization parseAsmOptimization(const std::string optimization) -{ - AsmOptimization asmOptimization = AsmOptimization::ASM_AUTODETECT; - - if (optimization == "0" || optimization == "none" || optimization == "off") { - asmOptimization = AsmOptimization::ASM_OFF; - } else if (optimization == "1" || optimization == "intel") { - asmOptimization = AsmOptimization::ASM_INTEL; - } else if (optimization == "2" || optimization == "ryzen") { - asmOptimization = AsmOptimization::ASM_RYZEN; - } else if (optimization == "3" || optimization == "bulldozer") { - asmOptimization = AsmOptimization::ASM_RYZEN; - } - - return asmOptimization; -} - - -#endif /* __ASM_OPTIMIZATION_H__ */ diff --git a/src/Cpu.cpp b/src/Cpu.cpp deleted file mode 100644 index 384aab44..00000000 --- a/src/Cpu.cpp +++ /dev/null @@ -1,249 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * Copyright 2018 Sebastian Stolzenberg - * - * - * 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 . - */ - - -#include -#include -#include -#include - -#include -#include -#include - -#include "Cpu.h" -#include "CpuImpl.h" - -CpuImpl& CpuImpl::instance() -{ - static CpuImpl cpu; - return cpu; -} - -CpuImpl::CpuImpl() - : m_l2_exclusive(false) - , m_brand{ 0 } - , m_flags(0) - , m_l2_cache(0) - , m_l3_cache(0) - , m_sockets(1) - , m_totalCores(0) - , m_totalThreads(0) - , m_asmOptimization(AsmOptimization::ASM_OFF) -{ -} - -void CpuImpl::optimizeParameters(size_t& threadsCount, size_t& hashFactor, - Options::Algo algo, PowVariant powVariant, size_t maxCpuUsage, bool safeMode) -{ - // limits hashfactor to maximum possible value defined by compiler flag - hashFactor = std::min(hashFactor, (algo == Options::ALGO_CRYPTONIGHT_HEAVY || powVariant == POW_XFH) ? 3 : static_cast(MAX_NUM_HASH_BLOCKS)); - - if (!safeMode && threadsCount > 0 && hashFactor > 0) - { - // all parameters have been set manually and safe mode is off ... no optimization necessary - return; - } - - size_t cache = availableCache(); - size_t algoBlocks; - switch (algo) { - case Options::ALGO_CRYPTONIGHT_EXTREMELITE: - algoBlocks = MEMORY_EXTREME_LITE/1024; - break; - case Options::ALGO_CRYPTONIGHT_ULTRALITE: - algoBlocks = MEMORY_ULTRA_LITE/1024; - break; - case Options::ALGO_CRYPTONIGHT_SUPERLITE: - algoBlocks = MEMORY_SUPER_LITE/1024; - break; - case Options::ALGO_CRYPTONIGHT_LITE: - algoBlocks = MEMORY_LITE/1024; - break; - case Options::ALGO_CRYPTONIGHT_HEAVY: - algoBlocks = MEMORY_HEAVY/1024; - break; - case Options::ALGO_ARGON2_256: - algoBlocks = MEMORY_ARGON2_256/1024; - break; - case Options::ALGO_ARGON2_512: - algoBlocks = MEMORY_ARGON2_512/1024; - break; - case Options::ALGO_CRYPTONIGHT: - default: - algoBlocks = MEMORY/1024; - break; - } - - size_t maximumReasonableFactor = std::max(cache / algoBlocks, static_cast(1ul)); - size_t maximumReasonableThreadCount = std::min(maximumReasonableFactor, m_totalThreads); - size_t maximumReasonableHashFactor = static_cast(MAX_NUM_HASH_BLOCKS); - - if (algo == Options::ALGO_CRYPTONIGHT_HEAVY || powVariant == POW_XFH) { - maximumReasonableHashFactor = 3; - } else if (getCNBaseVariant(powVariant) == POW_V2 || getCNBaseVariant(powVariant) == POW_V4 || algo == Options::ALGO_CRYPTONIGHT_EXTREMELITE || algo == Options::ALGO_CRYPTONIGHT_ULTRALITE) { - maximumReasonableHashFactor = 2; - } else if (!Options::isCNAlgo(algo)) { - maximumReasonableHashFactor = 1; - } - - if (safeMode) { - if (threadsCount > maximumReasonableThreadCount) { - threadsCount = maximumReasonableThreadCount; - } - if (threadsCount > 0 && hashFactor > maximumReasonableFactor / threadsCount) { - hashFactor = std::min(maximumReasonableFactor / threadsCount, maximumReasonableHashFactor); - hashFactor = std::max(hashFactor, static_cast(1)); - } - } - - if (threadsCount == 0) { - if (hashFactor == 0) { - threadsCount = maximumReasonableThreadCount; - } - else { - threadsCount = std::min(maximumReasonableThreadCount, - maximumReasonableFactor / hashFactor); - } - if (maxCpuUsage < 100) - { - threadsCount = std::min(threadsCount, m_totalThreads * maxCpuUsage / 100); - } - threadsCount = std::max(threadsCount, static_cast(1)); - } - - if (hashFactor == 0) { - hashFactor = std::min(maximumReasonableHashFactor, maximumReasonableFactor / threadsCount); - hashFactor = std::max(hashFactor, static_cast(1)); - } -} - -bool CpuImpl::hasAES() -{ - return (m_flags & Cpu::AES) != 0; -} - -bool CpuImpl::isX64() -{ - return (m_flags & Cpu::X86_64) != 0; -} - -size_t CpuImpl::availableCache() -{ - size_t cache = 0; - if (m_l3_cache) { - cache = m_l2_exclusive ? (m_l2_cache + m_l3_cache) : m_l3_cache; - } - else { - cache = m_l2_cache; - } - return cache; -} - -void Cpu::init() -{ - CpuImpl::instance().init(); -} - -void Cpu::optimizeParameters(size_t& threadsCount, size_t& hashFactor, Options::Algo algo, PowVariant powVariant, - size_t maxCpuUsage, bool safeMode) -{ - CpuImpl::instance().optimizeParameters(threadsCount, hashFactor, algo, powVariant, maxCpuUsage, safeMode); -} - -int Cpu::setThreadAffinity(size_t threadId, int64_t affinityMask) -{ - return CpuImpl::instance().setThreadAffinity(threadId, affinityMask); -} - -bool Cpu::hasAES() -{ - return CpuImpl::instance().hasAES(); -} - -bool Cpu::isX64() -{ - return CpuImpl::instance().isX64(); -} - -const char* Cpu::brand() -{ - return CpuImpl::instance().brand(); -} - -size_t Cpu::cores() -{ - return CpuImpl::instance().cores(); -} - -size_t Cpu::l2() -{ - return CpuImpl::instance().l2(); -} - -size_t Cpu::l3() -{ - return CpuImpl::instance().l3(); -} - -size_t Cpu::sockets() -{ - return CpuImpl::instance().sockets(); -} - -size_t Cpu::threads() -{ - return CpuImpl::instance().threads(); -} - -size_t Cpu::availableCache() -{ - return CpuImpl::instance().availableCache(); -} - -int Cpu::getAssignedCpuId(size_t threadId, int64_t affinityMask) -{ - int cpuId = -1; - - Mem::ThreadBitSet threadAffinityMask = Mem::ThreadBitSet(affinityMask); - size_t threadCount = 0; - - for (size_t i = 0; i < CpuImpl::instance().threads(); i++) { - if (threadAffinityMask.test(i)) { - if (threadCount == threadId) { - cpuId = i; - break; - } - - threadCount++; - } - } - - return cpuId; -} - -AsmOptimization Cpu::asmOptimization() -{ - return CpuImpl::instance().asmOptimization(); -} diff --git a/src/Cpu.h b/src/Cpu.h deleted file mode 100644 index 86acca5d..00000000 --- a/src/Cpu.h +++ /dev/null @@ -1,62 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * - * - * 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 . - */ - -#ifndef __CPU_H__ -#define __CPU_H__ - -#include - -#include "Options.h" - -class Cpu -{ -public: - enum Flags { - X86_64 = 1, - AES = 2, - BMI2 = 4, - AVX2 = 8 - }; - - static void init(); - - static void optimizeParameters(size_t& threadsCount, size_t& hashFactor, Options::Algo algo, PowVariant powVariant, - size_t maxCpuUsage, bool safeMode); - - static int setThreadAffinity(size_t threadId, int64_t affinityMask); - - static bool hasAES(); - static bool isX64(); - static const char *brand(); - static size_t l2(); - static size_t l3(); - static size_t cores(); - static size_t sockets(); - static size_t threads(); - static size_t availableCache(); - static int getAssignedCpuId(size_t threadId, int64_t affinityMask); - static AsmOptimization asmOptimization(); -}; - - -#endif /* __CPU_H__ */ diff --git a/src/CpuImpl.h b/src/CpuImpl.h deleted file mode 100644 index 828400b6..00000000 --- a/src/CpuImpl.h +++ /dev/null @@ -1,70 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * Copyright 2018 Sebastian Stolzenberg - * - * 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 . - */ - -#ifndef __CPU_IMPL_H__ -#define __CPU_IMPL_H__ - - -#include -#include - -#include "Options.h" -#include "Mem.h" - -class CpuImpl -{ -public: - static CpuImpl& instance(); - CpuImpl(); - void init(); - - void optimizeParameters(size_t& threadsCount, size_t& hashFactor, Options::Algo algo, PowVariant powVariant, - size_t maxCpuUsage, bool safeMode); - int setThreadAffinity(size_t threadId, int64_t affinityMask); - - bool hasAES(); - bool isX64(); - const char *brand() { return m_brand; } - size_t l2() { return m_l2_cache; } - size_t l3() { return m_l3_cache; } - size_t cores() { return m_totalCores; } - size_t sockets() { return m_sockets; } - size_t threads() { return m_totalThreads; } - size_t availableCache(); - AsmOptimization asmOptimization() { return m_asmOptimization; } - -private: - void initCommon(); - - bool m_l2_exclusive; - char m_brand[64]; - int m_flags; - size_t m_l2_cache; - size_t m_l3_cache; - size_t m_sockets; - size_t m_totalCores; - size_t m_totalThreads; - AsmOptimization m_asmOptimization; -}; - -#endif /* __CPU_IMPL_H__ */ diff --git a/src/Cpu_cpuid.cpp b/src/Cpu_cpuid.cpp deleted file mode 100644 index 84cac9ba..00000000 --- a/src/Cpu_cpuid.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * Copyright 2018 Sebastian Stolzenberg - * - * - * 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 . - */ - - -#include -#include -#include -#include - -#include -#include - -#include "Cpu.h" -#include "CpuImpl.h" - -void CpuImpl::initCommon() -{ - struct cpu_raw_data_t raw = { 0 }; - struct cpu_id_t data = { 0 }; - - cpuid_get_raw_data(&raw); - cpu_identify(&raw, &data); - - strncpy(m_brand, data.brand_str, sizeof(m_brand) - 1); - - m_totalThreads = data.total_logical_cpus; - m_sockets = m_totalThreads / data.num_logical_cpus; - - if (m_sockets == 0) { - m_sockets = 1; - } - - m_totalCores = data.num_cores * m_sockets; - m_l3_cache = data.l3_cache > 0 ? data.l3_cache * m_sockets : 0; - - // Workaround for AMD CPUs https://github.com/anrieff/libcpuid/issues/97 - if (data.vendor == VENDOR_AMD && data.ext_family >= 0x15 && data.ext_family < 0x17) { - m_l2_cache = data.l2_cache * (m_totalCores / 2) * m_sockets; - m_l2_exclusive = true; - } - // Workaround for Intel Pentium Dual-Core, Core Duo, Core 2 Duo, Core 2 Quad and their Xeon homologue - // These processors have L2 cache shared by 2 cores. - else if (data.vendor == VENDOR_INTEL && data.ext_family == 0x06 && (data.ext_model == 0x0E || data.ext_model == 0x0F || data.ext_model == 0x17)) { - int l2_count_per_socket = m_totalCores > 1 ? m_totalCores / 2 : 1; - m_l2_cache = data.l2_cache > 0 ? data.l2_cache * l2_count_per_socket * m_sockets : 0; - } - else{ - m_l2_cache = data.l2_cache > 0 ? data.l2_cache * m_totalCores * m_sockets : 0; - } - -# if defined(__x86_64__) || defined(_M_AMD64) - m_flags |= Cpu::X86_64; -# endif - - if (data.flags[CPU_FEATURE_AES]) { - m_flags |= Cpu::AES; - } - - if (data.flags[CPU_FEATURE_BMI2]) { - m_flags |= Cpu::BMI2; - } - - if (data.flags[CPU_FEATURE_AVX2]) { - m_flags |= Cpu::AVX2; - } - -# ifndef XMRIG_NO_ASM - if (data.vendor == VENDOR_AMD) { - if (data.ext_family >= 0x17) { - m_asmOptimization = AsmOptimization::ASM_RYZEN; - } else if (data.ext_family >= 0x15) { - m_asmOptimization = AsmOptimization::ASM_BULLDOZER; - } - } else if (data.vendor == VENDOR_INTEL && - ((data.ext_family >= 0x06 && data.ext_model > 0x2) || - (data.ext_family >= 0x06 && data.ext_model == 0x2 && data.model >= 0xA))) { - m_asmOptimization = AsmOptimization::ASM_INTEL; - } -# endif - -} diff --git a/src/Cpu_stub.cpp b/src/Cpu_stub.cpp deleted file mode 100644 index a9ebfcfa..00000000 --- a/src/Cpu_stub.cpp +++ /dev/null @@ -1,118 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * - * - * 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 . - */ - - -#ifdef _MSC_VER -# include - -# define bit_AES (1 << 25) -# define bit_BMI2 (1 << 8) -#else -# include -#endif - -#include - - -#include "Cpu.h" -#include "CpuImpl.h" - - -#define VENDOR_ID (0) -#define PROCESSOR_INFO (1) -#define CACHE_TLB_DESCRIPTOR (2) -#define EXTENDED_FEATURES (7) -#define PROCESSOR_BRAND_STRING_1 (0x80000002) -#define PROCESSOR_BRAND_STRING_2 (0x80000003) -#define PROCESSOR_BRAND_STRING_3 (0x80000004) - -#define EAX_Reg (0) -#define EBX_Reg (1) -#define ECX_Reg (2) -#define EDX_Reg (3) - - -#ifdef _MSC_VER -static inline void cpuid(int level, int output[4]) { - __cpuid(output, level); -} -#else -static inline void cpuid(int level, int output[4]) { - int a, b, c, d; - __cpuid_count(level, 0, a, b, c, d); - - output[0] = a; - output[1] = b; - output[2] = c; - output[3] = d; -} -#endif - - -static inline void cpu_brand_string(char* s) { - int cpu_info[4] = { 0 }; - cpuid(VENDOR_ID, cpu_info); - - if (cpu_info[EAX_Reg] >= 4) { - for (int i = 0; i < 4; i++) { - cpuid(0x80000002 + i, cpu_info); - memcpy(s, cpu_info, sizeof(cpu_info)); - s += 16; - } - } -} - - -static inline bool has_aes_ni() -{ - int cpu_info[4] = { 0 }; - cpuid(PROCESSOR_INFO, cpu_info); - - return cpu_info[ECX_Reg] & bit_AES; -} - - -static inline bool has_bmi2() { - int cpu_info[4] = { 0 }; - cpuid(EXTENDED_FEATURES, cpu_info); - - return cpu_info[EBX_Reg] & bit_BMI2; -} - - -void CpuImpl::initCommon() -{ - cpu_brand_string(m_brand); - -# if defined(__x86_64__) || defined(_M_AMD64) - m_flags |= Cpu::X86_64; -# endif - - if (has_aes_ni()) { - m_flags |= Cpu::AES; - } - - if (has_bmi2()) { - m_flags |= Cpu::BMI2; - } -} diff --git a/src/Cpu_unix.cpp b/src/Cpu_unix.cpp deleted file mode 100644 index ea83f56f..00000000 --- a/src/Cpu_unix.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * - * - * 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 . - */ - - -#ifdef __FreeBSD__ -# include -# include -# include -# include -#endif - - -#include -#include -#include -#include - -#include "CpuImpl.h" -#include "Cpu.h" - -#ifdef __FreeBSD__ -typedef cpuset_t cpu_set_t; -#endif - - -void CpuImpl::init() -{ -# ifdef XMRIG_NO_LIBCPUID - m_totalThreads = sysconf(_SC_NPROCESSORS_CONF); -# endif - - initCommon(); -} - - -int CpuImpl::setThreadAffinity(size_t threadId, int64_t affinityMask) -{ - int cpuId = -1; - - if (affinityMask != -1L) { - cpuId = Cpu::getAssignedCpuId(threadId, affinityMask); - } else { - cpuId = static_cast(threadId); - } - - if (cpuId > -1) { - cpu_set_t mn; - CPU_ZERO(&mn); - CPU_SET(cpuId, &mn); - -# ifndef __ANDROID__ - if (pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &mn) != 0) { - cpuId = -1; - } -# else - if (sched_setaffinity(gettid(), sizeof(cpu_set_t), &mn) == -1) { - cpuId = -1; - } -# endif - } - - return cpuId; -} diff --git a/src/Embedded_config.h b/src/Embedded_config.h deleted file mode 100644 index ceda60a0..00000000 --- a/src/Embedded_config.h +++ /dev/null @@ -1,68 +0,0 @@ -/* XMRigCC - * Copyright 2019 BenDroid - * - * - * 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 . - */ -#ifndef XMRIG_EMBEDDED_CONFIG_H -#define XMRIG_EMBEDDED_CONFIG_H - -constexpr static const char* m_embeddedConfig = -R"===( -{ - "algo": "cryptonight", - "aesni": 0, - "threads": 0, - "multihash-factor": 0, - "multihash-thread-mask" : null, - "pow-variant" : "auto", - "asm-optimization" : "auto", - "background": false, - "colors": true, - "cpu-affinity": null, - "cpu-priority": null, - "donate-level": 5, - "log-file": null, - "max-cpu-usage": 100, - "print-time": 60, - "retries": 5, - "retry-pause": 5, - "safe": false, - "syslog": false, - "reboot-cmd" : "", - "force-pow-variant" : false, - "skip-self-check" : false, - "pools": [ - { - "url": "donate2.graef.in:80", - "user": "YOUR_WALLET_ID", - "pass": "x", - "use-tls" : false, - "keepalive": true, - "nicehash": false - } - ], - "cc-client": { - "url": "localhost:3344", - "use-tls" : false, - "access-token": "mySecret", - "worker-id": null, - "update-interval-s": 10, - "use-remote-logging" : true, - "upload-config-on-startup" : true - } -} -)==="; - -#endif //XMRIG_EMBEDDED_CONFIG_H diff --git a/src/Mem.cpp b/src/Mem.cpp deleted file mode 100644 index 62ecfc6c..00000000 --- a/src/Mem.cpp +++ /dev/null @@ -1,110 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * - * - * 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 . - */ - - -#include -#include - -#include "crypto/Argon2.h" -#include "crypto/CryptoNight.h" -#include "Mem.h" - -bool Mem::m_useHugePages = true; -size_t Mem::m_hashFactor = 1; -int Mem::m_flags = 0; -int Mem::m_totalPages = 0; -int Mem::m_totalHugepages = 0; -Options::Algo Mem::m_algo = Options::ALGO_CRYPTONIGHT; -Mem::ThreadBitSet Mem::m_multiHashThreadMask = Mem::ThreadBitSet(-1L); - -ScratchPadMem Mem::create(ScratchPad** scratchPads, int threadId) -{ - size_t scratchPadSize; - - switch (m_algo) { - case Options::ALGO_CRYPTONIGHT_ULTRALITE: - scratchPadSize = MEMORY_ULTRA_LITE; - break; - case Options::ALGO_CRYPTONIGHT_EXTREMELITE: - scratchPadSize = MEMORY_EXTREME_LITE; - break; - case Options::ALGO_CRYPTONIGHT_SUPERLITE: - scratchPadSize = MEMORY_SUPER_LITE; - break; - case Options::ALGO_CRYPTONIGHT_LITE: - scratchPadSize = MEMORY_LITE; - break; - case Options::ALGO_CRYPTONIGHT_HEAVY: - scratchPadSize = MEMORY_HEAVY; - break; - case Options::ALGO_ARGON2_256: - scratchPadSize = MEMORY_ARGON2_256; - break; - case Options::ALGO_ARGON2_512: - scratchPadSize = MEMORY_ARGON2_512; - break; - case Options::ALGO_CRYPTONIGHT: - default: - scratchPadSize = MEMORY; - break; - } - - ScratchPadMem scratchPadMem; - scratchPadMem.realSize = scratchPadSize * getThreadHashFactor(threadId); - scratchPadMem.size = scratchPadSize * getThreadHashFactor(threadId); - scratchPadMem.pages = std::max(scratchPadMem.size / MEMORY, static_cast(1)); - - allocate(scratchPadMem, m_useHugePages); - - for (size_t i = 0; i < getThreadHashFactor(threadId); ++i) { - auto *scratchPad = static_cast(_mm_malloc(sizeof(ScratchPad), 4096)); - scratchPad->memory = scratchPadMem.memory + (i * scratchPadSize); - - auto *p = reinterpret_cast(allocateExecutableMemory(0x4000)); - scratchPad->generated_code = reinterpret_cast(p); - scratchPad->generated_code_double = reinterpret_cast(p + 0x2000); - - scratchPad->generated_code_data.variant = PowVariant::LAST_ITEM; - scratchPad->generated_code_data.height = (uint64_t) (-1); - scratchPad->generated_code_double_data = scratchPad->generated_code_data; - - scratchPads[i] = scratchPad; - } - - m_totalPages += scratchPadMem.pages; - m_totalHugepages += scratchPadMem.hugePages; - - return scratchPadMem; -} - -void Mem::release(ScratchPad** scratchPads, ScratchPadMem& scratchPadMem, int threadId) -{ - m_totalPages -= scratchPadMem.pages; - m_totalHugepages -= scratchPadMem.hugePages; - - release(scratchPadMem); - - for (size_t i = 0; i < getThreadHashFactor(threadId); ++i) { - _mm_free(scratchPads[i]); - } -} diff --git a/src/Mem.h b/src/Mem.h deleted file mode 100644 index 4b893d49..00000000 --- a/src/Mem.h +++ /dev/null @@ -1,113 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * Copyright 2018 Sebastian Stolzenberg - * Copyright 2018 BenDroid - * - * - * 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 . - */ - -#ifndef __MEM_H__ -#define __MEM_H__ - - -#include -#include -#include - -#include "Options.h" - -#ifdef _WIN32 -# ifdef __GNUC__ -# include -# else -# include -# endif -#else -# if defined(XMRIG_ARM) && !defined(__clang__) -# include "aligned_malloc.h" -# else -# include -# endif -#endif - -struct ScratchPad; - -struct ScratchPadMem -{ - alignas(16) uint8_t *memory; - - size_t hugePages; - size_t pages; - size_t size; - size_t realSize; -}; - - -class Mem -{ -public: - typedef std::bitset<128> ThreadBitSet; - - enum Flags { - HugepagesAvailable = 1, - HugepagesEnabled = 2, - Lock = 4 - }; - - static void init(const Options* option); - static ScratchPadMem create(ScratchPad** scratchPads, int threadId); - static void release(ScratchPad** scratchPads, ScratchPadMem& scratchPadMem, int threadId); - - static void *allocateExecutableMemory(size_t size); - static void flushInstructionCache(void *p, size_t size); - - static inline size_t hashFactor() { return m_hashFactor; } - static inline size_t getThreadHashFactor(int threadId) - { - size_t threadHashFactor = 1; - if (Options::isCNAlgo(m_algo)) { - threadHashFactor = (m_multiHashThreadMask.all() || m_multiHashThreadMask.test(threadId)) ? m_hashFactor : 1; - } - - return threadHashFactor; - } - - static inline bool isHugepagesAvailable() { return (m_flags & HugepagesAvailable) != 0; } - static inline bool isHugepagesEnabled() { return (m_flags & HugepagesEnabled) != 0; } - - static inline int getTotalPages() { return m_totalPages; } - static inline int getTotalHugepages() { return m_totalHugepages; } - -private: - static void allocate(ScratchPadMem& scratchPadMem, bool useHugePages); - static void release(ScratchPadMem& scratchPadMem); - -private: - static bool m_useHugePages; - static size_t m_hashFactor; - static int m_flags; - static int m_totalPages; - static int m_totalHugepages; - static Options::Algo m_algo; - static ThreadBitSet m_multiHashThreadMask; -}; - - -#endif /* __MEM_H__ */ diff --git a/src/Mem_unix.cpp b/src/Mem_unix.cpp deleted file mode 100644 index 659e5757..00000000 --- a/src/Mem_unix.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * - * - * 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 . - */ - - -#include -#include - -#include "crypto/HashSelector.h" -#include "log/Log.h" -#include "Mem.h" - - -void Mem::init(const Options* options) -{ - m_hashFactor = options->hashFactor(); - m_useHugePages = options->hugePages(); - m_algo = options->algo(); - m_multiHashThreadMask = Mem::ThreadBitSet(static_cast(options->multiHashThreadMask())); -} - -void Mem::allocate(ScratchPadMem& scratchPadMem, bool useHugePages) -{ - scratchPadMem.hugePages = 0; - - if (!useHugePages) { - scratchPadMem.memory = static_cast(_mm_malloc(scratchPadMem.size, 4096)); - return; - } - -# if defined(__APPLE__) - scratchPadMem.size = std::max(scratchPadMem.size + scratchPadMem.size % MEMORY, static_cast(MEMORY)); - scratchPadMem.memory = static_cast(mmap(0, scratchPadMem.size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, VM_FLAGS_SUPERPAGE_SIZE_2MB, 0)); -# elif defined(__FreeBSD__) - scratchPadMem.memory = static_cast(mmap(0, scratchPadMem.size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_ALIGNED_SUPER | MAP_PREFAULT_READ, -1, 0)); -# else - scratchPadMem.memory = static_cast(mmap(0, scratchPadMem.size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB | MAP_POPULATE, 0, 0)); -# endif - - if (scratchPadMem.memory == MAP_FAILED) { - return allocate(scratchPadMem, false); - } - - scratchPadMem.hugePages = scratchPadMem.pages; - - m_flags |= HugepagesAvailable; - m_flags |= HugepagesEnabled; - - if (madvise(scratchPadMem.memory, scratchPadMem.size, MADV_RANDOM | MADV_WILLNEED) != 0) { - LOG_ERR("madvise failed"); - } - - if (mlock(scratchPadMem.memory, scratchPadMem.size) == 0) { - m_flags |= Lock; - } -} - -void Mem::release(ScratchPadMem &scratchPadMem) -{ - if (scratchPadMem.hugePages) { - if (m_flags & Lock) { - munlock(scratchPadMem.memory, scratchPadMem.size); - } - - munmap(scratchPadMem.memory, scratchPadMem.size); - } - else { - _mm_free(scratchPadMem.memory); - } -} - -void *Mem::allocateExecutableMemory(size_t size) -{ -# if defined(__APPLE__) - return mmap(0, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANON, -1, 0); -# else - return mmap(0, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); -# endif -} - -void Mem::flushInstructionCache(void *p, size_t size) -{ -# ifndef __FreeBSD__ - __builtin___clear_cache(reinterpret_cast(p), reinterpret_cast(p) + size); -# endif -} diff --git a/src/Options.cpp b/src/Options.cpp deleted file mode 100644 index 89f205cb..00000000 --- a/src/Options.cpp +++ /dev/null @@ -1,1434 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * Copyright 2017- BenDr0id - * - * - * 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 . - */ - - -#include -#include - -#ifdef _MSC_VER -# include "getopt/getopt.h" -#else -# include -#include - -#endif - - -#ifndef XMRIG_NO_HTTPD -# include -#endif - - -#include "Cpu.h" -#include "donate.h" -#include "net/Url.h" -#include "Options.h" -#include "Platform.h" -#include "Embedded_config.h" -#include "rapidjson/document.h" -#include "rapidjson/error/en.h" -#include "rapidjson/filereadstream.h" -#include "version.h" - - -#ifndef ARRAY_SIZE -# define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) -#endif - - -Options *Options::m_self = nullptr; - -static char const usage[] = "\ -Usage: " APP_ID " [OPTIONS]\n\ -Options:\n" -# ifndef XMRIG_CC_SERVER -"\ - -a, --algo=ALGO cryptonight (default), cryptonight-lite, cryptonight-ultralite or cryptonight-heavy, cryptonight-ultralite, cryptonight-extremelite, argon2-256, argon2-512\n\ - -o, --url=URL URL of mining server\n\ - -O, --userpass=U:P username:password pair for mining server\n\ - -u, --user=USERNAME username for mining server\n\ - -p, --pass=PASSWORD password for mining server\n\ - -t, --threads=N number of miner threads\n\ - -A, --aesni=N selection of AES-NI mode (0 auto, 1 on, 2 off)\n\ - -k, --keepalive send keepalived for prevent timeout (need pool support)\n\ - -r, --retries=N number of times to retry before switch to backup server (default: 5)\n\ - -R, --retry-pause=N time to pause between retries (default: 5)\n\ - --pow-variant=V specificy the PoW variat to use: \n'auto' (default), '0', '1', '2', 'ipbc', 'xao', 'xtl', 'rto', 'xfh', 'upx', 'turtle', 'hosp', 'r', 'wow', 'double (xcash)', 'zls' (zelerius), 'rwz' (graft), 'upx2', 'conceal', chukwa (trtl), wrkz\n\ - for further help see: https://github.com/Bendr0id/xmrigCC/wiki/Coin-configurations\n\ - --asm-optimization=V specificy the ASM optimization to use: -> 'auto' (default), 'intel', 'ryzen', 'bulldozer', 'off' \n\ - --multihash-factor=N number of hash blocks to process at a time (don't set or 0 enables automatic selection of optimal number of hash blocks)\n\ - --multihash-thread-mask=MASK limits multihash to given threads (mask), (default: all threads)\n\ - --cpu-affinity set process affinity to CPU core(s), mask 0x3 for cores 0 and 1\n\ - --cpu-priority set process priority (0 idle, 2 normal to 5 highest)\n\ - --no-huge-pages disable huge pages support\n\ - --donate-level=N donate level, default 5%% (5 minutes in 100 minutes)\n\ - --user-agent set custom user-agent string for pool\n\ - --max-cpu-usage=N maximum CPU usage for automatic threads mode (default 75)\n\ - --safe safe adjust threads and av settings for current CPU\n\ - --nicehash enable nicehash/xmrig-proxy support\n\ - --use-tls enable tls on pool communication\n\ - --print-time=N print hashrate report every N seconds\n\ - --api-port=N port for the miner API\n\ - --api-access-token=T access token for API\n\ - --api-worker-id=ID custom worker-id for API\n\ - --reboot-cmd command/bat to execute to Reboot miner\n\ - --force-pow-variant skip pow/variant parsing from pool\n\ - --skip-self-check skip self check on startup\n" -# ifndef XMRIG_NO_CC -"\ - --cc-url=URL url of the CC Server\n\ - --cc-use-tls enable tls encryption for CC communication\n\ - --cc-access-token=T access token for CC Server\n\ - --cc-worker-id=ID custom worker-id for CC Server\n\ - --cc-update-interval-s=N status update interval in seconds (default: 10 min: 1)\n\ - --cc-use-remote-logging enable remote logging on CC Server\n\ - --cc-upload-config-on-startup upload current miner config to CC Server on startup\n" -# endif -# endif - -# ifdef XMRIG_CC_SERVER -"\ - --cc-user=USERNAME CC Server admin user\n\ - --cc-pass=PASSWORD CC Server admin pass\n\ - --cc-access-token=T CC Server access token for CC Client\n\ - --cc-port=N CC Server port\n\ - --cc-use-tls enable tls encryption for CC communication\n\ - --cc-cert-file=FILE when tls is turned on, use this to point to the right cert file (default: server.pem) \n\ - --cc-key-file=FILE when tls is turned on, use this to point to the right key file (default: server.key) \n\ - --cc-client-log-lines-history=N maximum lines of log history kept per miner (default: 100)\n\ - --cc-client-config-folder=FOLDER Folder contains the client config files\n\ - --cc-pushover-user-key your user key for pushover notifications\n\ - --cc-pushover-api-token api token/keytoken of the application for pushover notifications\n\ - --cc-telegram-bot-token your bot token for telegram notifications\n\ - --cc-telegram-chat-id your chat-id for telegram notifications\n\ - --cc-push-miner-offline-info push notification for offline miners and recover push\n\ - --cc-push-miner-zero-hash-info push notification when miner reports 0 hashrate and recover push\n\ - --cc-push-periodic-mining-status push periodic status notification (every hour)\n\ - --cc-custom-dashboard=FILE loads a custom dashboard and serve it to '/'\n" -# endif -"\ - --no-color disable colored output\n" -# ifdef HAVE_SYSLOG_H -"\ - -S, --syslog use system log for output messages\n" -# endif -"\ - -B, --background run the miner in the background\n\ - -c, --config=FILE load a JSON-format configuration file\n\ - -l, --log-file=FILE log all output to a file\n\ - -h, --help display this help and exit\n\ - -V, --version output version information and exit\n\ -"; - - -static char const short_options[] = "a:c:khBp:Px:r:R:s:t:T:o:u:O:v:Vl:S"; - - -static struct option const options[] = { - { "algo", 1, nullptr, 'a' }, - { "api-access-token", 1, nullptr, 4001 }, - { "api-port", 1, nullptr, 4000 }, - { "api-worker-id", 1, nullptr, 4002 }, - { "av", 1, nullptr, 'v' }, - { "aesni", 1, nullptr, 'A' }, - { "multihash-factor", 1, nullptr, 'm' }, - { "background", 0, nullptr, 'B' }, - { "config", 1, nullptr, 'c' }, - { "cpu-affinity", 1, nullptr, 1020 }, - { "cpu-priority", 1, nullptr, 1021 }, - { "donate-level", 1, nullptr, 1003 }, - { "help", 0, nullptr, 'h' }, - { "keepalive", 0, nullptr ,'k' }, - { "log-file", 1, nullptr, 'l' }, - { "max-cpu-usage", 1, nullptr, 1004 }, - { "nicehash", 0, nullptr, 1006 }, - { "no-color", 0, nullptr, 1002 }, - { "no-huge-pages", 0, nullptr, 1009 }, - { "pass", 1, nullptr, 'p' }, - { "print-time", 1, nullptr, 1007 }, - { "retries", 1, nullptr, 'r' }, - { "retry-pause", 1, nullptr, 'R' }, - { "safe", 0, nullptr, 1005 }, - { "syslog", 0, nullptr, 'S' }, - { "threads", 1, nullptr, 't' }, - { "url", 1, nullptr, 'o' }, - { "user", 1, nullptr, 'u' }, - { "user-agent", 1, nullptr, 1008 }, - { "userpass", 1, nullptr, 'O' }, - { "version", 0, nullptr, 'V' }, - { "use-tls", 0, nullptr, 1015 }, - { "force-pow-variant", 0, nullptr, 1016 }, - { "pow-variant", 1, nullptr, 1017 }, - { "variant", 1, nullptr, 1017 }, - { "skip-self-check", 0, nullptr, 1018 }, - { "api-port", 1, nullptr, 4000 }, - { "api-access-token", 1, nullptr, 4001 }, - { "api-worker-id", 1, nullptr, 4002 }, - { "reboot-cmd", 1, nullptr, 4021 }, - { "cc-url", 1, nullptr, 4003 }, - { "cc-access-token", 1, nullptr, 4004 }, - { "cc-worker-id", 1, nullptr, 4005 }, - { "cc-update-interval-s", 1, nullptr, 4012 }, - { "cc-port", 1, nullptr, 4006 }, - { "cc-user", 1, nullptr, 4007 }, - { "cc-pass", 1, nullptr, 4008 }, - { "cc-client-config-folder", 1, nullptr, 4009 }, - { "cc-custom-dashboard", 1, nullptr, 4010 }, - { "cc-cert-file", 1, nullptr, 4014 }, - { "cc-key-file", 1, nullptr, 4015 }, - { "cc-use-tls", 0, nullptr, 4016 }, - { "cc-use-remote-logging", 0, nullptr, 4017 }, - { "cc-client-log-lines-history", 1, nullptr, 4018 }, - { "cc-upload-config-on-startup", 0, nullptr, 4019 }, - { "cc-reboot-cmd", 1, nullptr, 4021 }, - { "cc-pushover-user-key", 1, nullptr, 4022 }, - { "cc-pushover-api-token", 1, nullptr, 4023 }, - { "cc-telegram-bot-token", 1, nullptr, 4027 }, - { "cc-telegram-chat-id", 1, nullptr, 4028 }, - { "cc-push-miner-offline-info", 0, nullptr, 4024 }, - { "cc-push-periodic-mining-status", 0, nullptr, 4025 }, - { "cc-push-miner-zero-hash-info", 0, nullptr, 4026 }, - { "daemonized", 0, nullptr, 4011 }, - { "doublehash-thread-mask", 1, nullptr, 4013 }, - { "multihash-thread-mask", 1, nullptr, 4013 }, - { "asm-optimization", 1, nullptr, 4020 }, - { nullptr, 0, nullptr, 0 } -}; - - -static struct option const config_options[] = { - { "algo", 1, nullptr, 'a' }, - { "av", 1, nullptr, 'v' }, - { "aesni", 1, nullptr, 'A' }, - { "multihash-factor", 1, nullptr, 'm' }, - { "background", 0, nullptr, 'B' }, - { "colors", 0, nullptr, 2000 }, - { "cpu-affinity", 1, nullptr, 1020 }, - { "cpu-priority", 1, nullptr, 1021 }, - { "donate-level", 1, nullptr, 1003 }, - { "huge-pages", 0, nullptr, 1009 }, - { "log-file", 1, nullptr, 'l' }, - { "max-cpu-usage", 1, nullptr, 1004 }, - { "print-time", 1, nullptr, 1007 }, - { "retries", 1, nullptr, 'r' }, - { "retry-pause", 1, nullptr, 'R' }, - { "safe", 0, nullptr, 1005 }, - { "syslog", 0, nullptr, 'S' }, - { "threads", 1, nullptr, 't' }, - { "user-agent", 1, nullptr, 1008 }, - { "force-pow-variant", 0, nullptr, 1016 }, - { "pow-variant", 1, nullptr, 1017 }, - { "variant", 1, nullptr, 1017 }, - { "skip-self-check", 0, nullptr, 1018 }, - { "doublehash-thread-mask", 1, nullptr, 4013 }, - { "multihash-thread-mask", 1, nullptr, 4013 }, - { "asm-optimization", 1, nullptr, 4020 }, - { "reboot-cmd", 1, nullptr, 4021 }, - { nullptr, 0, nullptr, 0 } -}; - - -static struct option const pool_options[] = { - { "url", 1, nullptr, 'o' }, - { "pass", 1, nullptr, 'p' }, - { "user", 1, nullptr, 'u' }, - { "userpass", 1, nullptr, 'O' }, - { "keepalive", 0, nullptr ,'k' }, - { "nicehash", 0, nullptr, 1006 }, - { "use-tls", 0, nullptr, 1015 }, - { "pow-variant", 1, nullptr, 1017 }, - { "variant", 1, nullptr, 1017 }, - { nullptr, 0, nullptr, 0 } -}; - - -static struct option const api_options[] = { - { "port", 1, nullptr, 4000 }, - { "access-token", 1, nullptr, 4001 }, - { "worker-id", 1, nullptr, 4002 }, - { nullptr, 0, nullptr, 0 } -}; - - -static struct option const cc_client_options[] = { - { "url", 1, nullptr, 4003 }, - { "access-token", 1, nullptr, 4004 }, - { "worker-id", 1, nullptr, 4005 }, - { "update-interval-s", 1, nullptr, 4012 }, - { "use-tls", 0, nullptr, 4016 }, - { "use-remote-logging", 0, nullptr, 4017 }, - { "upload-config-on-startup", 0, nullptr, 4019 }, - { "reboot-cmd", 1, nullptr, 4021 }, - { nullptr, 0, nullptr, 0 } -}; - -static struct option const cc_server_options[] = { - { "port", 1, nullptr, 4006 }, - { "access-token", 1, nullptr, 4004 }, - { "user", 1, nullptr, 4007 }, - { "pass", 1, nullptr, 4008 }, - { "client-config-folder", 1, nullptr, 4009 }, - { "custom-dashboard", 1, nullptr, 4010 }, - { "cert-file", 1, nullptr, 4014 }, - { "key-file", 1, nullptr, 4015 }, - { "use-tls", 0, nullptr, 4016 }, - { "client-log-lines-history", 1, nullptr, 4018 }, - { "pushover-user-key", 1, nullptr, 4022 }, - { "pushover-api-token", 1, nullptr, 4023 }, - { "telegram-bot-token", 1, nullptr, 4027 }, - { "telegram-chat-id", 1, nullptr, 4028 }, - { "push-miner-offline-info", 0, nullptr, 4024 }, - { "push-miner-zero-hash-info", 0, nullptr, 4026 }, - { "push-periodic-mining-status", 0, nullptr, 4025 }, - { nullptr, 0, nullptr, 0 } -}; - -static const char *algo_names[] = { - "cryptonight", - "cryptonight-lite", - "cryptonight-superlite", - "cryptonight-ultralite", - "cryptonight-extremelite", - "cryptonight-heavy", - "argon2-256", - "argon2-512", -}; - -static const char *algo_short_names[] = { - "cn", - "cn-lite", - "cn-superlite", - "cn-ultralite", - "cn-extremelite", - "cn-heavy", - "ar2-256", - "ar2-512", -}; - -constexpr static const char *pow_variant_names[] = { - "auto", - "0", - "1", - "2", - "tube", - "xao", - "xtl", - "msr", - "xhv", - "rto", - "xfh", - "fast2", - "upx", - "turtle", - "hosp", - "wow", - "r", - "xcash", - "zls", - "graft", - "upx2", - "conceal", - "chukwa", - "wrkz" -}; - -constexpr static const char *asm_optimization_names[] = { - "auto", - "intel", - "ryzen", - "bulldozer", - "off" -}; - -Options *Options::parse(int argc, char **argv) -{ - auto options = new Options(argc, argv); - if (options->isReady()) { - m_self = options; - return m_self; - } - - delete options; - return nullptr; -} - - -const char *Options::algoName() const -{ - return algo_names[m_algo]; -} - -const char *Options::algoShortName() const -{ - return algo_short_names[m_algo]; -} - -Options::Options(int argc, char **argv) : - m_background(false), - m_colors(true), - m_hugePages(true), - m_ready(false), - m_safe(false), - m_syslog(false), - m_daemonized(false), - m_ccUseTls(false), - m_ccUseRemoteLogging(true), - m_ccUploadConfigOnStartup(true), - m_ccPushOfflineMiners(false), - m_ccPushPeriodicStatus(false), - m_ccPushZeroHashrateMiners(false), - m_forcePowVariant(false), - m_skipSelfCheck(false), - m_fileName(Platform::defaultConfigName()), - m_apiToken(nullptr), - m_apiWorkerId(nullptr), - m_logFile(nullptr), - m_userAgent(nullptr), - m_ccHost(nullptr), - m_ccToken(nullptr), - m_ccWorkerId(nullptr), - m_ccAdminUser(nullptr), - m_ccAdminPass(nullptr), - m_ccClientConfigFolder(nullptr), - m_ccCustomDashboard(nullptr), - m_ccKeyFile(nullptr), - m_ccCertFile(nullptr), - m_ccRebootCmd(nullptr), - m_ccPushoverUser(nullptr), - m_ccPushoverToken(nullptr), - m_ccTelegramBotToken(nullptr), - m_ccTelegramChatId(nullptr), - m_algo(ALGO_CRYPTONIGHT), - m_algoVariant(AV0_AUTO), - m_aesni(AESNI_AUTO), - m_powVariant(POW_AUTODETECT), - m_asmOptimization(ASM_AUTODETECT), - m_hashFactor(0), - m_apiPort(0), - m_donateLevel(kDonateLevel), - m_maxCpuUsage(75), - m_printTime(60), - m_priority(-1), - m_retries(5), - m_retryPause(5), - m_threads(0), - m_ccUpdateInterval(10), - m_ccPort(0), - m_ccClientLogLinesHistory(100), - m_affinity(-1L), - m_multiHashThreadMask(-1L) -{ - m_pools.push_back(new Url()); - - int key; - while (true) { - key = getopt_long(argc, argv, short_options, options, nullptr); - if (key < 0) { - break; - } - - if (!parseArg(key, optarg)) { - return; - } - } - - if (optind < argc) { - fprintf(stderr, "%s: unsupported non-option argument '%s'\n", argv[0], argv[optind]); - return; - } - - if (!m_pools[0]->isValid() && (!m_ccHost || m_ccPort == 0)) { - parseConfigFile(Platform::defaultConfigName()); - } - -#ifdef XMRIG_CC_SERVER - if (m_ccPort == 0) { - fprintf(stderr, "No CC Server Port supplied. Exiting.\n"); - return; - } -#else - #ifndef XMRIG_NO_CC - if (!m_daemonized) { - fprintf(stderr, "\"" APP_ID "\" is compiled with CC support, please start the daemon instead.\n"); - return; - } - #endif - - if (!m_pools[0]->isValid() && (!m_ccHost || m_ccPort == 0)) { - fprintf(stderr, "No valid pool/cc configuration found, using embedded config.\n"); - parseEmbeddedConfig(); - } - - if (!m_pools[0]->isValid() && (!m_ccHost || m_ccPort == 0)) { - fprintf(stderr, "Neither pool nor CCServer URL supplied. Exiting.\n"); - return; - } -#endif - - optimizeAlgorithmConfiguration(); - - if (m_asmOptimization == AsmOptimization::ASM_AUTODETECT) { - m_asmOptimization = Cpu::asmOptimization(); - } - - for (Url *url : m_pools) { - url->applyExceptions(); - } - - m_ready = true; -} - - -Options::~Options() -{ -} - -bool Options::readJSONFile(const char *fileName, rapidjson::Document &doc) -{ - uv_fs_t req; - const int fd = uv_fs_open(uv_default_loop(), &req, fileName, O_RDONLY, 0644, nullptr); - if (fd < 0) { - fprintf(stderr, "unable to open %s: %s\n", fileName, uv_strerror(fd)); - return false; - } - - uv_fs_req_cleanup(&req); - - FILE *fp = fdopen(fd, "rb"); - char buf[8192]; - rapidjson::FileReadStream is(fp, buf, sizeof(buf)); - - doc.ParseStream(is); - - uv_fs_close(uv_default_loop(), &req, fd, nullptr); - uv_fs_req_cleanup(&req); - - if (doc.HasParseError()) { - printf("%s:%d: %s\n", fileName, (int) doc.GetErrorOffset(), rapidjson::GetParseError_En(doc.GetParseError())); - return false; - } - - return doc.IsObject(); -} - - -bool Options::parseArg(int key, const char *arg) -{ - switch (key) { - case 'a': /* --algo */ - if (!setAlgo(arg)) { - return false; - } - break; - - case 'o': /* --url */ - if (m_pools.size() > 1 || m_pools[0]->isValid()) { - auto url = new Url(arg); - if (url->isValid()) { - m_pools.push_back(url); - } - else { - delete url; - } - } - else { - m_pools[0]->parse(arg); - } - - if (!m_pools.back()->isValid()) { - return false; - } - break; - - case 'O': /* --userpass */ - if (!m_pools.back()->setUserpass(arg)) { - return false; - } - break; - - case 'u': /* --user */ - m_pools.back()->setUser(arg); - break; - - case 'p': /* --pass */ - m_pools.back()->setPassword(arg); - break; - - case 'l': /* --log-file */ - free(m_logFile); - m_logFile = strdup(arg); - break; - - case 4001: /* --access-token */ - free(m_apiToken); - m_apiToken = strdup(arg); - break; - - case 4002: /* --worker-id */ - free(m_apiWorkerId); - m_apiWorkerId = strdup(arg); - break; - - case 4003: /* --cc-url */ - return parseCCUrl(arg); - - case 4004: /* --cc-access-token */ - free(m_ccToken); - m_ccToken = strdup(arg); - break; - - case 4005: /* --cc-worker-id */ - free(m_ccWorkerId); - m_ccWorkerId = strdup(arg); - break; - - case 4007: /* --cc-user */ - free(m_ccAdminUser); - m_ccAdminUser = strdup(arg); - break; - - case 4008: /* --cc-pass */ - free(m_ccAdminPass); - m_ccAdminPass = strdup(arg); - break; - - case 4009: /* --cc-client-config-folder */ - free(m_ccClientConfigFolder); - m_ccClientConfigFolder = strdup(arg); - break; - - case 4010: /* --cc-custom-dashboard */ - free(m_ccCustomDashboard); - m_ccCustomDashboard = strdup(arg); - break; - - case 4014: /* --cc-cert-file */ - free(m_ccCertFile); - m_ccCertFile = strdup(arg); - break; - - case 4015: /* --cc-key-file */ - free(m_ccKeyFile); - m_ccKeyFile = strdup(arg); - break; - - case 4011: /* --daemonized */ - m_daemonized = true; - break; - - case 'r': /* --retries */ - case 'R': /* --retry-pause */ - case 'v': /* --av */ - case 'A': /* --aesni */ - case 'm': /* --multihash-factor */ - case 1003: /* --donate-level */ - case 1004: /* --max-cpu-usage */ - case 1007: /* --print-time */ - case 1021: /* --cpu-priority */ - case 4000: /* --api-port */ - case 4006: /* --cc-port */ - case 4012: /* --cc-update-interval-c */ - return parseArg(key, strtol(arg, nullptr, 10)); - case 4018: /* --cc-client-log-lines-history */ - return parseArg(key, strtol(arg, nullptr, 25)); - - case 'B': /* --background */ - case 'k': /* --keepalive */ - case 'S': /* --syslog */ - case 1005: /* --safe */ - case 1006: /* --nicehash */ - - case 1002: /* --no-color */ - case 1009: /* --no-huge-pages */ - return parseBoolean(key, false); - - case 1015: /* --use-tls */ - return parseBoolean(key, true); - - case 1016: /* --force-pow-variant */ - return parseBoolean(key, true); - - case 1017: /* --pow-variant/--variant */ - return parsePowVariant(arg); - - case 1018: /* --skip-self-check */ - return parseBoolean(key, true); - - case 4016: /* --cc-use-tls */ - return parseBoolean(key, true); - - case 4017: /* --cc-use-remote-logging */ - return parseBoolean(key, true); - - case 4019: /* --cc-upload-config-on-startup */ - return parseBoolean(key, true); - - case 4020: /* --asm-optimization */ - return parseAsmOptimization(arg); - - case 4021: /* --cc-reboot-cmd || --reboot-cmd */ - free(m_ccRebootCmd); - m_ccRebootCmd = strdup(arg); - break; - - case 4022: /* --cc-pushover-user-key */ - free(m_ccPushoverUser); - m_ccPushoverUser = strdup(arg); - break; - - case 4023: /* --cc-pushover-api-token */ - free(m_ccPushoverToken); - m_ccPushoverToken = strdup(arg); - break; - - case 4027: /* --cc-telegram-bot-token */ - free(m_ccTelegramBotToken); - m_ccTelegramBotToken = strdup(arg); - break; - - case 4028: /* --cc-telegram-chat-id */ - free(m_ccTelegramChatId); - m_ccTelegramChatId = strdup(arg); - break; - - case 4024: /* --cc-push-miner-offline-info */ - return parseBoolean(key, false); - - case 4025: /* --cc-push-periodic-mining-status */ - return parseBoolean(key, false); - - case 4026: /* --cc-push-miner-zero-hash-info */ - return parseBoolean(key, false); - - case 't': /* --threads */ - if (strncmp(arg, "all", 3) == 0) { - m_threads = Cpu::threads(); - return true; - } - - return parseArg(key, strtol(arg, nullptr, 10)); - - case 'V': /* --version */ - showVersion(); - return false; - - case 'h': /* --help */ - showUsage(0); - return false; - - case 'c': /* --config */ - parseConfigFile(arg); - break; - - case 1020: { /* --cpu-affinity */ - const char *p = strstr(arg, "0x"); - return parseArg(key, p ? strtoull(p, nullptr, 16) : strtoull(arg, nullptr, 10)); - } - - case 4013: { /* --multihash-thread-mask */ - const char *p = strstr(arg, "0x"); - return parseArg(key, p ? strtoull(p, nullptr, 16) : strtoull(arg, nullptr, 10)); - } - - case 1008: /* --user-agent */ - free(m_userAgent); - m_userAgent = strdup(arg); - break; - - default: - showUsage(1); - return false; - } - - return true; -} - - -bool Options::parseArg(int key, uint64_t arg) -{ - switch (key) { - case 'r': /* --retries */ - if (arg < 1 || arg > 1000) { - showUsage(1); - return false; - } - - m_retries = (int) arg; - break; - - case 'R': /* --retry-pause */ - if (arg < 1 || arg > 3600) { - showUsage(1); - return false; - } - - m_retryPause = (int) arg; - break; - - case 't': /* --threads */ - if (arg < 0 || arg > 1024) { - showUsage(1); - return false; - } - - m_threads = (int) arg; - break; - - case 'v': /* --av */ - showDeprecateWarning("av", "aesni"); - if (arg > 1000) { - showUsage(1); - return false; - } - - m_algoVariant = static_cast(arg); - break; - - case 'A': /* --aesni */ - if (arg < AESNI_AUTO || arg > AESNI_OFF) { - showUsage(1); - return false; - } - m_aesni = static_cast(arg); - break; - - case 'm': /* --multihash-factor */ - if (arg > MAX_NUM_HASH_BLOCKS) { - showUsage(1); - return false; - } - m_hashFactor = arg; - break; - - case 1003: /* --donate-level */ - if (arg < 1 || arg > 99) { - return true; - } - - m_donateLevel = (int) arg; - break; - - case 1004: /* --max-cpu-usage */ - if (arg < 1 || arg > 100) { - showUsage(1); - return false; - } - - m_maxCpuUsage = (int) arg; - break; - - case 1007: /* --print-time */ - if (arg > 1000) { - showUsage(1); - return false; - } - - m_printTime = (int) arg; - break; - - case 1020: /* --cpu-affinity */ - if (arg) { - m_affinity = arg; - } - break; - - case 1021: /* --cpu-priority */ - if (arg <= 5) { - m_priority = (int) arg; - } - break; - - case 4000: /* --api-port */ - if (arg <= 65536) { - m_apiPort = (int) arg; - } - break; - - case 4006: /* --cc-port */ - if (arg <= 65536) { - m_ccPort = (int) arg; - } - break; - - case 4012: /* --cc-update-interval-s */ - if (arg < 1) { - showUsage(1); - return false; - } - - m_ccUpdateInterval = (int) arg; - break; - - case 4013: /* --multihash-thread-mask */ - if (arg) { - m_multiHashThreadMask = arg; - } - break; - - case 4018: /* --cc-client-log-lines-history */ - m_ccClientLogLinesHistory = (int) arg; - break; - - default: - break; - } - - return true; -} - - -bool Options::parseBoolean(int key, bool enable) -{ - switch (key) { - case 'k': /* --keepalive */ - m_pools.back()->setKeepAlive(enable); - break; - - case 'B': /* --background */ - m_background = enable; - m_colors = enable ? false : m_colors; - break; - - case 'S': /* --syslog */ - m_syslog = enable; - m_colors = enable ? false : m_colors; - break; - - case 1002: /* --no-color */ - m_colors = enable; - break; - - case 1005: /* --safe */ - m_safe = enable; - break; - - case 1006: /* --nicehash */ - m_pools.back()->setNicehash(enable); - break; - - case 1009: /* --no-huge-pages */ - m_hugePages = enable; - break; - - case 1015: /* --use-tls */ - m_pools.back()->setUseTls(enable); - break; - - case 1016: /* --force-pow-variant */ - m_forcePowVariant = enable; - break; - - case 1018: /* --skip-self-check */ - m_skipSelfCheck = enable; - break; - - case 2000: /* --colors */ - m_colors = enable; - break; - - case 4016: /* --cc-use-tls */ - m_ccUseTls = enable; - break; - - case 4017: /* --cc-use-remote-logging */ - m_ccUseRemoteLogging = enable; - break; - - case 4019: /* --cc-upload-config-on-startup */ - m_ccUploadConfigOnStartup = enable; - break; - - case 4024: /* --cc-push-miner-offline-info */ - m_ccPushOfflineMiners = enable; - break; - - case 4026: /* --cc-push-miner-zero-hash-info */ - m_ccPushZeroHashrateMiners = enable; - break; - - case 4025: /* --cc-push-periodic-mining-status */ - m_ccPushPeriodicStatus = enable; - break; - - default: - break; - } - - return true; -} - - -Url *Options::parseUrl(const char *arg) const -{ - auto url = new Url(arg); - if (!url->isValid()) { - delete url; - return nullptr; - } - - return url; -} - -void Options::parseConfigFile(const char *fileName) { - m_fileName = fileName; - - rapidjson::Document doc; - if (!readJSONFile(fileName, doc)) { - return; - } - - parseConfig(doc); -} - -void Options::parseEmbeddedConfig() { - - rapidjson::Document doc; - doc.Parse(m_embeddedConfig); - - parseConfig(doc); -} - -void Options::parseConfig(rapidjson::Document& doc) -{ - for (auto option : config_options) { - parseJSON(&option, doc); - } - - const rapidjson::Value &pools = doc["pools"]; - if (pools.IsArray()) { - for (const rapidjson::Value &value : pools.GetArray()) { - if (!value.IsObject()) { - continue; - } - - for (auto option : pool_options) { - parseJSON(&option, value); - } - } - } - - const rapidjson::Value &api = doc["api"]; - if (api.IsObject()) { - for (auto api_option : api_options) { - parseJSON(&api_option, api); - } - } - - const rapidjson::Value &ccClient = doc["cc-client"]; - if (ccClient.IsObject()) { - for (auto cc_client_option : cc_client_options) { - parseJSON(&cc_client_option, ccClient); - } - } - - const rapidjson::Value &ccServer = doc["cc-server"]; - if (ccServer.IsObject()) { - for (auto cc_server_option : cc_server_options) { - parseJSON(&cc_server_option, ccServer); - } - } -} - - -void Options::parseJSON(const struct option *option, const rapidjson::Value &object) -{ - if (!option->name || !object.HasMember(option->name)) { - return; - } - - const rapidjson::Value &value = object[option->name]; - - if (option->has_arg && value.IsString()) { - parseArg(option->val, value.GetString()); - } - else if (option->has_arg && value.IsUint64()) { - parseArg(option->val, value.GetUint64()); - } - else if (!option->has_arg && value.IsBool()) { - parseBoolean(option->val, value.IsTrue()); - } -} - - -void Options::showUsage(int status) const -{ - if (status) { - fprintf(stderr, "Try \"" APP_ID "\" --help' for more information.\n"); - } - else { - printf(usage); - } -} - -void Options::showDeprecateWarning(const char* deprecated, const char* newParam) const -{ - fprintf(stderr, "Parameter \"%s\" is deprecated, please used \"%s\" instead.\n", deprecated, newParam); -} - -void Options::showVersion() -{ - printf(APP_NAME " " APP_VERSION "\n built on " __DATE__ - -# if defined(__clang__) - " with clang " __clang_version__); -# elif defined(__GNUC__) - " with GCC"); - printf(" %d.%d.%d", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__); -# elif defined(_MSC_VER) - " with MSVC"); - printf(" %d", MSVC_VERSION); -# else - ); -# endif - - printf("\n features:" -# if defined(__i386__) || defined(_M_IX86) - " i386" -# elif defined(__x86_64__) || defined(_M_AMD64) - " x86_64" -# endif - -# if defined(__AES__) || defined(_MSC_VER) - " AES-NI" -# endif - "\n"); - - printf("\nlibuv/%s\n", uv_version_string()); - -# ifndef XMRIG_NO_HTTPD - printf("libmicrohttpd/%s\n", MHD_get_version()); -# endif -} - - -bool Options::setAlgo(const char *algo) -{ - for (size_t i = 0; i < ARRAY_SIZE(algo_names); i++) { - if (algo_names[i] && !strcmp(algo, algo_names[i])) { - m_algo = static_cast(i); - break; - } - - if (i == ARRAY_SIZE(algo_names) - 1 && (!strcmp(algo, "cn-lite") || !strcmp(algo, "cryptonight-light"))) { - m_algo = ALGO_CRYPTONIGHT_LITE; - break; - } - - if (i == ARRAY_SIZE(algo_names) - 1 && (!strcmp(algo, "cn-super-lite") || !strcmp(algo, "cryptonight-super-lite") || !strcmp(algo, "cryptonight-superlight"))) { - m_algo = ALGO_CRYPTONIGHT_SUPERLITE; - break; - } - - if (i == ARRAY_SIZE(algo_names) - 1 && (!strcmp(algo, "cn-ultra-lite") || !strcmp(algo, "cryptonight-ultra-lite") || !strcmp(algo, "cryptonight-ultralight") || !strcmp(algo, "cryptonight-turtle") || !strcmp(algo, "cn-turtle") || !strcmp(algo, "cryptonight-pico") || !strcmp(algo, "cn-pico"))) { - m_algo = ALGO_CRYPTONIGHT_ULTRALITE; - break; - } - - if (i == ARRAY_SIZE(algo_names) - 1 && (!strcmp(algo, "cn-extreme-lite") || !strcmp(algo, "cryptonight-extreme-lite") || !strcmp(algo, "cryptonight-extremelight") || !strcmp(algo, "cryptonight-upx2") || !strcmp(algo, "cn-upx2") || !strcmp(algo, "cryptonight-femto") || !strcmp(algo, "cn-femto"))) { - m_algo = ALGO_CRYPTONIGHT_EXTREMELITE; - break; - } - - if (i == ARRAY_SIZE(algo_names) - 1 && (!strcmp(algo, "cryptonight-lite-ipbc") || !strcmp(algo, "cryptonight-light-ipbc") || !strcmp(algo, "cn-lite-ipbc"))) { - showDeprecateWarning("cryptonight-light-ipbc", "cryptonight-light (with variant \"ipbc\")"); - m_algo = ALGO_CRYPTONIGHT_LITE; - m_powVariant = POW_TUBE; - break; - } - - if (i == ARRAY_SIZE(algo_names) - 1 && (!strcmp(algo, "cryptonight-heavy") || !strcmp(algo, "cn-heavy"))) { - m_algo = ALGO_CRYPTONIGHT_HEAVY; - break; - } - - if (i == ARRAY_SIZE(algo_names) - 1 && (!strcmp(algo, "cryptonight-hospital") || !strcmp(algo, "cryptonight-hosp"))) { - m_algo = ALGO_CRYPTONIGHT; - m_powVariant = POW_HOSP; - break; - } - - if (i == ARRAY_SIZE(algo_names) - 1 && (!strcmp(algo, "argon2-chukwa") || !strcmp(algo, "chukwa") || !strcmp(algo, "argon2d512") || !strcmp(algo, "argon2id512") || !strcmp(algo, "argon2512"))) { - m_algo = ALGO_ARGON2_512; - m_powVariant = POW_ARGON2_CHUKWA; - break; - } - - if (i == ARRAY_SIZE(algo_names) - 1 && (!strcmp(algo, "argon2-wrkz") || !strcmp(algo, "wrkz") || !strcmp(algo, "argon2d256") || !strcmp(algo, "argon2id256") || !strcmp(algo, "argon2256"))) { - m_algo = ALGO_ARGON2_256; - m_powVariant = POW_ARGON2_WRKZ; - break; - } - - if (i == ARRAY_SIZE(algo_names) - 1) { - showUsage(1); - return false; - } - } - - if (m_algo == ALGO_ARGON2_512) { - m_powVariant = POW_ARGON2_CHUKWA; - } - - if (m_algo == ALGO_ARGON2_256) { - m_powVariant = POW_ARGON2_WRKZ; - } - - return true; -} - -bool Options::parsePowVariant(const char *powVariant) -{ - if (m_powVariant != POW_AUTODETECT) { - return true; - } - - for (size_t i = 0; i < ARRAY_SIZE(pow_variant_names); i++) { - if (pow_variant_names[i] && !strcmp(powVariant, pow_variant_names[i])) { - m_powVariant = static_cast(i); - break; - } - - if (i == ARRAY_SIZE(pow_variant_names) - 1 && (!strcmp(powVariant, "auto") || !strcmp(powVariant, "-1"))) { - m_powVariant = POW_AUTODETECT; - break; - } - - if (i == ARRAY_SIZE(pow_variant_names) - 1 && (!strcmp(powVariant, "cnv1") || !strcmp(powVariant, "monerov7") || !strcmp(powVariant, "aeonv7") || !strcmp(powVariant, "v7"))) { - m_powVariant = POW_V1; - break; - } - - if (i == ARRAY_SIZE(pow_variant_names) - 1 && (!strcmp(powVariant, "cnv2") || !strcmp(powVariant, "monerov8") || !strcmp(powVariant, "aeonv8") || !strcmp(powVariant, "v8"))) { - m_powVariant = POW_V2; - break; - } - - if (i == ARRAY_SIZE(pow_variant_names) - 1 && !strcmp(powVariant, "stellite")) { - m_powVariant = POW_XTL; - break; - } - - if (i == ARRAY_SIZE(pow_variant_names) - 1 && (!strcmp(powVariant, "xao") || !strcmp(powVariant, "alloy"))) { - m_powVariant = POW_ALLOY; - break; - } - - if (i == ARRAY_SIZE(pow_variant_names) - 1 && (!strcmp(powVariant, "ipbc") || !strcmp(powVariant, "bittube"))) { - m_powVariant = POW_TUBE; - break; - } - - if (i == ARRAY_SIZE(pow_variant_names) - 1 && !strcmp(powVariant, "haven")) { - m_powVariant = POW_XHV; - break; - } - - if (i == ARRAY_SIZE(pow_variant_names) - 1 && !strcmp(powVariant, "arto")) { - m_powVariant = POW_RTO; - break; - } - - if (i == ARRAY_SIZE(pow_variant_names) - 1 && (!strcmp(powVariant, "freehaven") || !strcmp(powVariant, "faven") || !strcmp(powVariant, "swap"))) { - m_powVariant = POW_XFH; - break; - } - - if (i == ARRAY_SIZE(pow_variant_names) - 1 && (!strcmp(powVariant, "stellitev9") || !strcmp(powVariant, "xtlv2") || - !strcmp(powVariant, "half") || !strcmp(powVariant, "msr2") || - !strcmp(powVariant, "xtlv9"))) { - m_powVariant = POW_FAST_2; - break; - } - - if (i == ARRAY_SIZE(pow_variant_names) - 1 && !strcmp(powVariant, "uplexa")) { - m_powVariant = POW_UPX; - break; - } - - if (i == ARRAY_SIZE(pow_variant_names) - 1 && (!strcmp(powVariant, "trtl") || !strcmp(powVariant, "turtlev2") || !strcmp(powVariant, "pico"))) { - m_powVariant = POW_TURTLE; - break; - } - - if (i == ARRAY_SIZE(pow_variant_names) - 1 && (!strcmp(powVariant, "upx2") || !strcmp(powVariant, "upxv2") || !strcmp(powVariant, "femto"))) { - m_powVariant = POW_UPX2; - break; - } - - if (i == ARRAY_SIZE(pow_variant_names) - 1 && (!strcmp(powVariant, "hosp") || !strcmp(powVariant, "hospital"))) { - m_powVariant = POW_HOSP; - break; - } - - if (i == ARRAY_SIZE(pow_variant_names) - 1 && !strcmp(powVariant, "wow")) { - m_powVariant = POW_WOW; - break; - } - - if (i == ARRAY_SIZE(pow_variant_names) - 1 && (!strcmp(powVariant, "4") || !strcmp(powVariant, "r") || !strcmp(powVariant, "cnv4") || !strcmp(powVariant, "cnv5"))) { - m_powVariant = POW_V4; - break; - } - - if (i == ARRAY_SIZE(pow_variant_names) - 1 && (!strcmp(powVariant, "xcash") || !strcmp(powVariant, "heavyx") || !strcmp(powVariant, "double"))) { - m_powVariant = POW_DOUBLE; - break; - } - - if (i == ARRAY_SIZE(pow_variant_names) - 1 && (!strcmp(powVariant, "zelerius") || !strcmp(powVariant, "zls") || !strcmp(powVariant, "zlx"))) { - m_powVariant = POW_ZELERIUS; - break; - } - - if (i == ARRAY_SIZE(pow_variant_names) - 1 && (!strcmp(powVariant, "rwz") || !strcmp(powVariant, "graft"))) { - m_powVariant = POW_RWZ; - break; - } - - if (i == ARRAY_SIZE(pow_variant_names) - 1 && (!strcmp(powVariant, "conceal") || !strcmp(powVariant, "ccx"))) { - m_powVariant = POW_CONCEAL; - break; - } - - if (i == ARRAY_SIZE(pow_variant_names) - 1 && (!strcmp(powVariant, "trtl-chukwa") || !strcmp(powVariant, "trtl_chukwa") || !strcmp(powVariant, "chuckwa"))) { - m_powVariant = POW_ARGON2_CHUKWA; - break; - } - - if (i == ARRAY_SIZE(pow_variant_names) - 1 && (!strcmp(powVariant, "chukwa-wrkz") || !strcmp(powVariant, "chukwa_wrkz") || !strcmp(powVariant, "trtl-wrkz"))) { - m_powVariant = POW_ARGON2_CHUKWA; - break; - } - - if (i == ARRAY_SIZE(pow_variant_names) - 1) { - showUsage(1); - return false; - } - } - - return true; -} - - -bool Options::parseAsmOptimization(const char *asmOptimization) -{ - for (size_t i = 0; i < ARRAY_SIZE(pow_variant_names); i++) { - if (pow_variant_names[i] && !strcmp(asmOptimization, asm_optimization_names[i])) { - m_asmOptimization = static_cast(i); - break; - } - - if (i == ARRAY_SIZE(asm_optimization_names) - 1 && (!strcmp(asmOptimization, "none") || !strcmp(asmOptimization, "0"))) { - m_asmOptimization = ASM_OFF; - break; - } - - if (i == ARRAY_SIZE(asm_optimization_names) - 1) { - showUsage(1); - return false; - } - } - - return true; -} - - -void Options::optimizeAlgorithmConfiguration() -{ - // backwards compatibility for configs still setting algo variant (av) - // av overrides mutli-hash and aesni when they are either not set or when they are set to auto - if (m_algoVariant != AV0_AUTO) { - size_t hashFactor = m_hashFactor; - AesNi aesni = m_aesni; - switch (m_algoVariant) { - case AV1_AESNI: - hashFactor = 1; - aesni = AESNI_ON; - break; - case AV2_AESNI_DOUBLE: - hashFactor = 2; - aesni = AESNI_ON; - break; - case AV3_SOFT_AES: - hashFactor = 1; - aesni = AESNI_OFF; - break; - case AV4_SOFT_AES_DOUBLE: - hashFactor = 2; - aesni = AESNI_OFF; - break; - case AV0_AUTO: - default: - // no change - break; - } - if (m_hashFactor == 0) { - m_hashFactor = hashFactor; - } - if (m_aesni == AESNI_AUTO) { - m_aesni = aesni; - } - } - - AesNi aesniFromCpu = Cpu::hasAES() ? AESNI_ON : AESNI_OFF; - if (m_aesni == AESNI_AUTO || m_safe) { - m_aesni = aesniFromCpu; - } - - if ((m_algo == Options::ALGO_CRYPTONIGHT_HEAVY || m_powVariant == PowVariant::POW_XFH) && m_hashFactor > 3) { - fprintf(stderr, "Maximum supported hashfactor for cryptonight-heavy and XFH is: 3\n"); - m_hashFactor = 3; - } - - if (m_algo == Options::ALGO_ARGON2_256 || m_algo == Options::ALGO_ARGON2_512) { - m_hashFactor = 1; - } - - Cpu::optimizeParameters(m_threads, m_hashFactor, m_algo, m_powVariant, m_maxCpuUsage, m_safe); -} - -bool Options::parseCCUrl(const char* url) -{ - free(m_ccHost); - - const char *port = strchr(url, ':'); - if (!port) { - m_ccHost = strdup(url); - m_ccPort = kDefaultCCPort; - } else { - const size_t size = port++ - url + 1; - m_ccHost = static_cast(malloc(size)); - memcpy(m_ccHost, url, size - 1); - m_ccHost[size - 1] = '\0'; - - m_ccPort = (uint16_t) strtol(port, nullptr, 10); - - if (m_ccPort < 0 || m_ccPort > 65536) { - m_ccPort = kDefaultCCPort; - return false; - } - } - - return true; -} diff --git a/src/Options.h b/src/Options.h deleted file mode 100644 index 7fefc20c..00000000 --- a/src/Options.h +++ /dev/null @@ -1,225 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * Copyright 2017- BenDr0id - * - * - * 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 . - */ - -#ifndef __OPTIONS_H__ -#define __OPTIONS_H__ - -#ifndef MAX_NUM_HASH_BLOCKS -#define MAX_NUM_HASH_BLOCKS 5 -#endif - -#define MAX_BLOB_SIZE 128 - -#include -#include - -#include "rapidjson/fwd.h" -#include "PowVariant.h" -#include "AsmOptimization.h" - -class Url; -struct option; - - -class Options -{ -public: - enum Algo { - ALGO_CRYPTONIGHT, /* CryptoNight (2MB ScratchPad) */ - ALGO_CRYPTONIGHT_LITE, /* CryptoNight-Lite (1MB ScratchPad) */ - ALGO_CRYPTONIGHT_SUPERLITE, /* CryptoNight-Superlite (512KB ScratchPad) */ - ALGO_CRYPTONIGHT_ULTRALITE, /* CryptoNight-Ultralite (256KB ScratchPad) */ - ALGO_CRYPTONIGHT_EXTREMELITE, /* CryptoNight-Verylite (128KB ScratchPad) */ - ALGO_CRYPTONIGHT_HEAVY, /* CryptoNight-Heavy (4MB ScratchPad) */ - ALGO_ARGON2_256, /* Argon2 (256KB ScratchPad) */ - ALGO_ARGON2_512, /* Argon2 (512KB ScratchPad) */ - }; - - enum AlgoVariant { - AV0_AUTO, - AV1_AESNI, - AV2_AESNI_DOUBLE, - AV3_SOFT_AES, - AV4_SOFT_AES_DOUBLE, - AV_MAX - }; - - enum AesNi { - AESNI_AUTO, - AESNI_ON, - AESNI_OFF - }; - - static inline Options* i() { return m_self; } - static Options *parse(int argc, char **argv); - - inline bool background() const { return m_background; } - inline bool colors() const { return m_colors; } - inline bool hugePages() const { return m_hugePages; } - inline bool syslog() const { return m_syslog; } - inline bool daemonized() const { return m_daemonized; } - inline bool ccUseTls() const { return m_ccUseTls; } - inline bool ccUseRemoteLogging() const { return m_ccUseRemoteLogging; } - inline bool ccUploadConfigOnStartup() const { return m_ccUploadConfigOnStartup; } - inline bool ccPushOfflineMiners() const { return m_ccPushOfflineMiners; } - inline bool ccPushPeriodicStatus() const { return m_ccPushPeriodicStatus; } - inline bool ccPushZeroHashrateMiners() const { return m_ccPushZeroHashrateMiners; } - inline bool ccUsePushover() const { return ccPushoverUser() && ccPushoverToken(); } - inline bool ccUseTelegram() const { return ccTelegramBotToken() && ccTelegramChatId(); } - inline bool forcePowVariant() const { return m_forcePowVariant; } - inline bool skipSelfCheck() const { return m_skipSelfCheck; } - inline const char *fileName() const { return m_fileName; } - inline const char *apiToken() const { return m_apiToken; } - inline const char *apiWorkerId() const { return m_apiWorkerId; } - inline const char *logFile() const { return m_logFile; } - inline const char *userAgent() const { return m_userAgent; } - inline const char *ccHost() const { return m_ccHost; } - inline const char *ccToken() const { return m_ccToken; } - inline const char *ccWorkerId() const { return m_ccWorkerId; } - inline const char *ccAdminUser() const { return m_ccAdminUser; } - inline const char *ccAdminPass() const { return m_ccAdminPass; } - inline const char *ccClientConfigFolder() const { return m_ccClientConfigFolder; } - inline const char *ccCustomDashboard() const { return m_ccCustomDashboard == nullptr ? "index.html" : m_ccCustomDashboard; } - inline const char *ccKeyFile() const { return m_ccKeyFile == nullptr ? "server.key" : m_ccKeyFile; } - inline const char *ccCertFile() const { return m_ccCertFile == nullptr ? "server.pem" : m_ccCertFile; } - inline const char *ccRebootCmd() const { return (m_ccRebootCmd != nullptr && strlen(m_ccRebootCmd) > 0) ? m_ccRebootCmd : nullptr; } - inline const char *ccPushoverUser() const { return (m_ccPushoverUser != nullptr && strlen(m_ccPushoverUser) > 0) ? m_ccPushoverUser : nullptr; } - inline const char *ccPushoverToken() const { return (m_ccPushoverToken != nullptr && strlen(m_ccPushoverToken) > 0) ? m_ccPushoverToken : nullptr; } - inline const char *ccTelegramBotToken() const { return (m_ccTelegramBotToken != nullptr && strlen(m_ccTelegramBotToken) > 0) ? m_ccTelegramBotToken : nullptr; } - inline const char *ccTelegramChatId() const { return (m_ccTelegramChatId != nullptr && strlen(m_ccTelegramChatId) > 0) ? m_ccTelegramChatId : nullptr; } - inline const std::vector &pools() const { return m_pools; } - inline Algo algo() const { return m_algo; } - inline PowVariant powVariant() const { return m_powVariant; } - inline AsmOptimization asmOptimization() const { return m_asmOptimization; } - inline bool aesni() const { return m_aesni == AESNI_ON; } - inline size_t hashFactor() const { return m_hashFactor; } - inline int apiPort() const { return m_apiPort; } - inline int donateLevel() const { return m_donateLevel; } - inline int printTime() const { return m_printTime; } - inline int priority() const { return m_priority; } - inline int retries() const { return m_retries; } - inline int retryPause() const { return m_retryPause; } - inline size_t threads() const { return m_threads; } - inline int ccUpdateInterval() const { return m_ccUpdateInterval; } - inline int ccPort() const { return m_ccPort; } - inline size_t ccClientLogLinesHistory() const { return m_ccClientLogLinesHistory; } - inline int64_t affinity() const { return m_affinity; } - inline int64_t multiHashThreadMask() const { return m_multiHashThreadMask; } - inline void setColors(bool colors) { m_colors = colors; } - - inline static bool isCNAlgo(Algo algo) { return algo != Options::ALGO_ARGON2_256 && - algo != Options::ALGO_ARGON2_512; } - - inline static void release() { delete m_self; } - - const char *algoName() const; - const char *algoShortName() const; - -private: - constexpr static uint16_t kDefaultCCPort = 3344; - - Options(int argc, char **argv); - ~Options(); - - inline bool isReady() const { return m_ready; } - - static Options *m_self; - - bool readJSONFile(const char *fileName, rapidjson::Document &doc); - bool parseArg(int key, const char *arg); - bool parseArg(int key, uint64_t arg); - bool parseBoolean(int key, bool enable); - bool parseCCUrl(const char *arg); - Url *parseUrl(const char *arg) const; - void parseConfig(rapidjson::Document &doc); - void parseConfigFile(const char *fileName); - void parseEmbeddedConfig(); - void parseJSON(const struct option *option, const rapidjson::Value &object); - void showUsage(int status) const; - void showDeprecateWarning(const char* deprecated, const char* newParam) const; - void showVersion(void); - - bool setAlgo(const char *algo); - bool parsePowVariant(const char *powVariant); - bool parseAsmOptimization(const char *arg); - - void optimizeAlgorithmConfiguration(); - - bool m_background; - bool m_colors; - bool m_hugePages; - bool m_ready; - bool m_safe; - bool m_syslog; - bool m_daemonized; - bool m_ccUseTls; - bool m_ccUseRemoteLogging; - bool m_ccUploadConfigOnStartup; - bool m_ccPushOfflineMiners; - bool m_ccPushPeriodicStatus; - bool m_ccPushZeroHashrateMiners; - bool m_forcePowVariant; - bool m_skipSelfCheck; - const char* m_fileName; - char *m_apiToken; - char *m_apiWorkerId; - char *m_logFile; - char *m_userAgent; - char *m_ccHost; - char *m_ccToken; - char *m_ccWorkerId; - char *m_ccAdminUser; - char *m_ccAdminPass; - char *m_ccClientConfigFolder; - char *m_ccCustomDashboard; - char *m_ccKeyFile; - char *m_ccCertFile; - char *m_ccRebootCmd; - char *m_ccPushoverUser; - char *m_ccPushoverToken; - char *m_ccTelegramBotToken; - char *m_ccTelegramChatId; - Algo m_algo; - AlgoVariant m_algoVariant; - AesNi m_aesni; - PowVariant m_powVariant; - AsmOptimization m_asmOptimization; - size_t m_hashFactor; - int m_apiPort; - int m_donateLevel; - size_t m_maxCpuUsage; - int m_printTime; - int m_priority; - int m_retries; - int m_retryPause; - size_t m_threads; - int m_ccUpdateInterval; - int m_ccPort; - size_t m_ccClientLogLinesHistory; - int64_t m_affinity; - int64_t m_multiHashThreadMask; - std::vector m_pools; -}; - -#endif /* __OPTIONS_H__ */ diff --git a/src/Platform_mac.cpp b/src/Platform_mac.cpp deleted file mode 100644 index ba541f1d..00000000 --- a/src/Platform_mac.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * - * - * 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 . - */ - - -#include -#include -#include -#include - - -#include "Platform.h" -#include "version.h" - -#ifdef XMRIG_NVIDIA_PROJECT -# include "nvidia/cryptonight.h" -#endif - - -static inline char *createUserAgent() -{ - const size_t max = 160; - - char *buf = new char[max]; - -# ifdef XMRIG_NVIDIA_PROJECT - const int cudaVersion = cuda_get_runtime_version(); - snprintf(buf, max, "%s/%s (Macintosh; Intel Mac OS X) libuv/%s CUDA/%d.%d clang/%d.%d.%d", APP_NAME, APP_VERSION, uv_version_string(), cudaVersion / 1000, cudaVersion % 100, __clang_major__, __clang_minor__, __clang_patchlevel__); -# else - snprintf(buf, max, "%s/%s (Macintosh; Intel Mac OS X) libuv/%s clang/%d.%d.%d", APP_NAME, APP_VERSION, uv_version_string(), __clang_major__, __clang_minor__, __clang_patchlevel__); -# endif - - return buf; -} - - -void Platform::init(const char *userAgent) -{ - m_userAgent = userAgent ? strdup(userAgent) : createUserAgent(); -} - - -void Platform::release() -{ - delete [] m_userAgent; -} - - -void Platform::setProcessPriority(int priority) -{ - -} - - -void Platform::setThreadPriority(int priority) -{ - if (priority == -1) { - return; - } - - int prio = 19; - switch (priority) - { - case 1: - prio = 5; - break; - - case 2: - prio = 0; - break; - - case 3: - prio = -5; - break; - - case 4: - prio = -10; - break; - - case 5: - prio = -15; - break; - - default: - break; - } - - setpriority(PRIO_PROCESS, 0, prio); -} - diff --git a/src/PowVariant.h b/src/PowVariant.h deleted file mode 100644 index c1b13414..00000000 --- a/src/PowVariant.h +++ /dev/null @@ -1,232 +0,0 @@ -/* XMRigCC - * Copyright 2018- BenDr0id - * - * - * 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 . - */ - -#ifndef __POW_VARIANT_H__ -#define __POW_VARIANT_H__ - -#include -#include - -enum PowVariant -{ - POW_AUTODETECT, - POW_V0, - POW_V1, - POW_V2, - POW_TUBE, - POW_ALLOY, - POW_XTL, - POW_MSR, - POW_XHV, - POW_RTO, - POW_XFH, - POW_FAST_2, - POW_UPX, - POW_TURTLE, - POW_HOSP, - POW_WOW, - POW_V4, - POW_DOUBLE, - POW_ZELERIUS, - POW_RWZ, - POW_UPX2, - POW_CONCEAL, - POW_ARGON2_CHUKWA, - POW_ARGON2_WRKZ, - LAST_ITEM -}; - -inline std::string getPowVariantName(PowVariant powVariant) -{ - switch (powVariant) - { - case POW_V0: - return "0"; - case POW_V1: - return "1"; - case POW_V2: - return "2"; - case POW_TUBE: - return "tube"; - case POW_ALLOY: - return "xao"; - case POW_XTL: - return "xtl"; - case POW_MSR: - return "msr"; - case POW_XHV: - return "xhv"; - case POW_RTO: - return "rto"; - case POW_XFH: - return "xfh"; - case POW_FAST_2: - return "fast2"; - case POW_UPX: - return "upx"; - case POW_TURTLE: - return "turtle"; - case POW_HOSP: - return "hosp"; - case POW_WOW: - return "wow"; - case POW_V4: - return "r"; - case POW_DOUBLE: - return "double"; - case POW_ZELERIUS: - return "zls"; - case POW_RWZ: - return "rwz"; - case POW_UPX2: - return "upx2"; - case POW_CONCEAL: - return "conceal"; - case POW_ARGON2_CHUKWA: - return "chukwa"; - case POW_ARGON2_WRKZ: - return "wrkz"; - case POW_AUTODETECT: - default: - return "-1"; - } -} - -inline std::list getSupportedPowVariants() -{ - std::list supportedPowVariants; - - for (int variant = 0; variant != LAST_ITEM; variant++) - { - supportedPowVariants.push_back(getPowVariantName(static_cast(variant))); - } - - return supportedPowVariants; -} - -inline PowVariant parseVariant(int variant) -{ - PowVariant powVariant = PowVariant::POW_AUTODETECT; - - switch (variant) { - case -1: - powVariant = PowVariant::POW_AUTODETECT; - break; - case 0: - powVariant = PowVariant::POW_V0; - break; - case 1: - powVariant = PowVariant::POW_V1; - break; - case 2: - powVariant = PowVariant::POW_V2; - break; - default: - break; - } - - return powVariant; -} - - -inline PowVariant parseVariant(const std::string variant) -{ - PowVariant powVariant = PowVariant::POW_AUTODETECT; - - if (variant == "0") { - powVariant = PowVariant::POW_V0; - } else if (variant == "1") { - powVariant = PowVariant::POW_V1; - } else if (variant == "2") { - powVariant = PowVariant::POW_V2; - } else if (variant == "ipbc" || variant == "tube" || variant == "bittube") { - powVariant = PowVariant::POW_TUBE; - } else if (variant == "xao" || variant == "alloy") { - powVariant = PowVariant::POW_ALLOY; - } else if (variant == "xtl" || variant == "stellite") { - powVariant = PowVariant::POW_XTL; - } else if (variant == "msr" || variant == "masari") { - powVariant = PowVariant::POW_MSR; - } else if (variant == "xhv" || variant == "haven") { - powVariant = PowVariant::POW_XHV; - } else if (variant == "rto" || variant == "arto") { - powVariant = PowVariant::POW_RTO; - } else if (variant == "xfh" || variant == "freehaven" || variant == "faven") { - powVariant = PowVariant::POW_XFH; - } else if (variant == "xtlv9" || variant == "stellite_v9" || variant == "xtlv2" || variant == "half" || variant == "msr2" || variant == "fast2") { - powVariant = PowVariant::POW_FAST_2; - } else if (variant == "upx" || variant == "uplexa" || variant == "cn-upx") { - powVariant = PowVariant::POW_UPX; - } else if (variant == "turtle" || variant == "trtl" || variant == "pico" || variant == "turtlev2") { - powVariant = PowVariant::POW_TURTLE; - } else if (variant == "hosp" || variant == "hospital") { - powVariant = PowVariant::POW_HOSP; - } else if (variant == "wow" || variant == "wownero") { - powVariant = PowVariant::POW_WOW; - } else if (variant == "r" || variant == "4" || variant == "cnv4" || variant == "cnv5") { - powVariant = PowVariant::POW_V4; - } else if (variant == "xcash" || variant == "heavyx" || variant == "double") { - powVariant = PowVariant::POW_DOUBLE; - } else if (variant == "zelerius" || variant == "zls" || variant == "zlx") { - powVariant = PowVariant::POW_ZELERIUS; - } else if (variant == "rwz" || variant == "graft") { - powVariant = PowVariant::POW_RWZ; - } else if (variant == "upx2") { - powVariant = PowVariant::POW_UPX2; - } else if (variant == "conceal" || variant == "ccx") { - powVariant = PowVariant::POW_CONCEAL; - } else if (variant == "chukwa" || variant == "trtl-chukwa" || variant == "argon2-chukwa") { - powVariant = PowVariant::POW_ARGON2_CHUKWA; - } else if (variant == "chukwa_wrkz" || variant == "wrkz" || variant == "argon2-wrkz") { - powVariant = PowVariant::POW_ARGON2_WRKZ; - } - - return powVariant; -} - -inline PowVariant getCNBaseVariant(PowVariant powVariant) -{ - switch (powVariant) - { - case POW_V1: - case POW_XTL: - case POW_MSR: - case POW_RTO: - case POW_HOSP: - case POW_UPX: - return POW_V1; - - case POW_V2: - case POW_TURTLE: - case POW_DOUBLE: - case POW_ZELERIUS: - case POW_RWZ: - case POW_UPX2: - case POW_FAST_2: - return POW_V2; - - case POW_WOW: - case POW_V4: - return POW_V4; - default: - return POW_V0; - } -} - - -#endif /* __POW_VARIANT_H__ */ diff --git a/src/Summary.cpp b/src/Summary.cpp index efcc4ed9..227fdcc8 100644 --- a/src/Summary.cpp +++ b/src/Summary.cpp @@ -4,8 +4,9 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2017-2019 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig * * 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,187 +23,132 @@ */ -#include #include #include #include -#include "Cpu.h" -#include "log/Log.h" -#include "Mem.h" -#include "net/Url.h" -#include "Options.h" +#include "backend/cpu/Cpu.h" +#include "base/io/log/Log.h" +#include "base/net/stratum/Pool.h" +#include "core/config/Config.h" +#include "core/Controller.h" +#include "crypto/common/Assembly.h" +#include "crypto/common/VirtualMemory.h" #include "Summary.h" #include "version.h" -static void print_versions() -{ - char buf[16]; +namespace xmrig { -# if defined(__clang__) - snprintf(buf, 16, " clang/%d.%d.%d", __clang_major__, __clang_minor__, __clang_patchlevel__); -# elif defined(__GNUC__) - snprintf(buf, 16, " gcc/%d.%d.%d", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__); -# elif defined(_MSC_VER) - snprintf(buf, 16, " MSVC/%d", MSVC_VERSION); + +#ifdef XMRIG_FEATURE_ASM +static const char *coloredAsmNames[] = { + RED_BOLD("none"), + "auto", + GREEN_BOLD("intel"), + GREEN_BOLD("ryzen"), + GREEN_BOLD("bulldozer") +}; + + +inline static const char *asmName(Assembly::Id assembly) +{ + return coloredAsmNames[assembly]; +} +#endif + + +static void print_memory(Config *) { +# ifdef _WIN32 + Log::print(GREEN_BOLD(" * ") WHITE_BOLD("%-13s") "%s", + "HUGE PAGES", VirtualMemory::isHugepagesAvailable() ? GREEN_BOLD("permission granted") : RED_BOLD("unavailable")); +# endif +} + + +static void print_cpu(Config *) +{ + const ICpuInfo *info = Cpu::info(); + + Log::print(GREEN_BOLD(" * ") WHITE_BOLD("%-13s%s (%zu)") " %sx64 %sAES", + "CPU", + info->brand(), + info->packages(), + info->isX64() ? GREEN_BOLD_S : RED_BOLD_S "-", + info->hasAES() ? GREEN_BOLD_S : RED_BOLD_S "-" + ); +# if defined(XMRIG_FEATURE_LIBCPUID) || defined (XMRIG_FEATURE_HWLOC) + Log::print(WHITE_BOLD(" %-13s") BLACK_BOLD("L2:") WHITE_BOLD("%.1f MB") BLACK_BOLD(" L3:") WHITE_BOLD("%.1f MB") + CYAN_BOLD(" %zu") "C" BLACK_BOLD("/") CYAN_BOLD("%zu") "T" +# ifdef XMRIG_FEATURE_HWLOC + BLACK_BOLD(" NUMA:") CYAN_BOLD("%zu") +# endif + , "", + info->L2() / 1048576.0, + info->L3() / 1048576.0, + info->cores(), + info->threads() +# ifdef XMRIG_FEATURE_HWLOC + , info->nodes() +# endif + ); # else - buf[0] = '\0'; + Log::print(WHITE_BOLD(" %-13s") BLACK_BOLD("threads:") CYAN_BOLD("%zu"), + "", + info->threads() + ); # endif - - Log::i()->text(Options::i()->colors() ? "\x1B[01;32m * \x1B[01;37mVERSIONS: \x1B[01;36m%s/%s\x1B[01;37m libuv/%s%s \x1B[01;36m(%s)" : " * VERSIONS: %s/%s libuv/%s%s (%s)", - APP_NAME, APP_VERSION, uv_version_string(), buf, BUILD_TYPE); } -static void print_cpu() +static void print_threads(Config *config) { - if (Options::i()->colors()) { - Log::i()->text("\x1B[01;32m * \x1B[01;37mCPU: %s (%d) %sx64 %sAES-NI %sASM-%s", - Cpu::brand(), - Cpu::sockets(), - Cpu::isX64() ? "\x1B[01;32m" : "\x1B[01;31m-", - Cpu::hasAES() && Options::i()->aesni() ? "\x1B[01;32m" : "\x1B[01;31m-", - Options::i()->asmOptimization() != AsmOptimization::ASM_OFF ? "\x1B[01;32m" : "\x1B[01;31m-", - getAsmOptimizationName(Options::i()->asmOptimization()).c_str()); -# ifndef XMRIG_NO_LIBCPUID - Log::i()->text("\x1B[01;32m * \x1B[01;37mCPU L2/L3: %.1f MB/%.1f MB", Cpu::l2() / 1024.0, Cpu::l3() / 1024.0); -# endif + Log::print(GREEN_BOLD(" * ") WHITE_BOLD("%-13s") WHITE_BOLD("%s%d%%"), + "DONATE", + config->pools().donateLevel() == 0 ? RED_BOLD_S : "", + config->pools().donateLevel() + ); + +# ifdef XMRIG_FEATURE_ASM + if (config->cpu().assembly() == Assembly::AUTO) { + const Assembly assembly = Cpu::info()->assembly(); + + Log::print(GREEN_BOLD(" * ") WHITE_BOLD("%-13sauto:%s"), "ASSEMBLY", asmName(assembly)); } else { - Log::i()->text(" * CPU: %s (%d) %sx64 %sAES-NI ASM-%s", - Cpu::brand(), Cpu::sockets(), Cpu::isX64() ? "" : "-", Cpu::hasAES() ? "" : "-", - getAsmOptimizationName(Options::i()->asmOptimization()).c_str()); -# ifndef XMRIG_NO_LIBCPUID - Log::i()->text(" * CPU L2/L3: %.1f MB/%.1f MB", Cpu::l2() / 1024.0, Cpu::l3() / 1024.0); -# endif - } -} - - -static void print_threads() -{ - char dhtMaskBuf[256]; - if (Options::i()->hashFactor() > 1 && Options::i()->multiHashThreadMask() != -1L) { - - std::string singleThreads; - std::string multiThreads; - - auto addThread = [](std::string& threads, int id) { - if (!threads.empty()) { - threads.append(", "); - } - threads.append(std::to_string(id)); - }; - - for (size_t i=0; i < Options::i()->threads(); i++) { - if (Mem::getThreadHashFactor(i) > 1) { - addThread(multiThreads, i); - } - else { - addThread(singleThreads, i); - } - } - - snprintf(dhtMaskBuf, 256, ", multiHashThreadMask=0x%" PRIX64 " [single threads: %s; multihash threads: %s]", - Options::i()->multiHashThreadMask(), singleThreads.c_str(), multiThreads.c_str()); - } - else { - dhtMaskBuf[0] = '\0'; - } - - char affBuf[32]; - if (Options::i()->affinity() != -1L) { - snprintf(affBuf, 32, ", affinity=0x%" PRIX64, Options::i()->affinity()); - } - else { - snprintf(affBuf, 32, ", affinity=auto"); - } - - Log::i()->text(Options::i()->colors() ? - "\x1B[01;32m * \x1B[01;37mTHREADS: \x1B[01;36m%d\x1B[01;37m, %s, hf=%zu, %sdonate=%d%%\x1B[01;37m%s%s" : - " * THREADS: %d, %s, hf=%zu, %sdonate=%d%%%s%s", - Options::i()->threads(), - Options::i()->algoName(), - Options::i()->hashFactor(), - Options::i()->colors() && Options::i()->donateLevel() == 0 ? "\x1B[01;31m" : "", - Options::i()->donateLevel(), - affBuf, - dhtMaskBuf); -} - - -static void print_pools() -{ - const std::vector &pools = Options::i()->pools(); - - for (size_t i = 0; i < pools.size(); ++i) { - Log::i()->text(Options::i()->colors() ? "\x1B[01;32m * \x1B[01;37mPOOL #%d: \x1B[01;36m%s:%d %s" : " * POOL #%d: %s:%d %s", - i + 1, - pools[i]->host(), - pools[i]->port(), - pools[i]->useTls() ? "(TLS)" : ""); - } - -# ifdef APP_DEBUG - for (size_t i = 0; i < pools.size(); ++i) { - Log::i()->text("%s:%d, user: %s, pass: %s, ka: %d, nicehash: %d", pools[i]->host(), pools[i]->port(), pools[i]->user(), pools[i]->password(), pools[i]->isKeepAlive(), pools[i]->isNicehash()); + Log::print(GREEN_BOLD(" * ") WHITE_BOLD("%-13s%s"), "ASSEMBLY", asmName(config->cpu().assembly())); } # endif } -#ifndef XMRIG_NO_API -static void print_api() +static void print_commands(Config *) { - if (Options::i()->apiPort() == 0) { - return; - } - - Log::i()->text(Options::i()->colors() ? "\x1B[01;32m * \x1B[01;37mAPI PORT: \x1B[01;36m%d" : " * API PORT: %d", Options::i()->apiPort()); -} -#endif - -#ifndef XMRIG_NO_CC -static void print_cc() -{ - if (Options::i()->ccHost() == nullptr) { - return; - } - - Log::i()->text(Options::i()->colors() ? "\x1B[01;32m * \x1B[01;37mCC Server: \x1B[01;36m%s:%d %s" : " * CC Server: %s:%d %s", - Options::i()->ccHost(), - Options::i()->ccPort(), - Options::i()->ccUseTls() ? "(TLS)" : ""); -} -#endif - -static void print_commands() -{ - if (Options::i()->colors()) { - Log::i()->text("\x1B[01;32m * \x1B[01;37mCOMMANDS: \x1B[01;35mh\x1B[01;37mashrate, \x1B[01;35mp\x1B[01;37mause, \x1B[01;35mr\x1B[01;37mesume, \x1B[01;35mq\x1B[01;37muit"); + if (Log::colors) { + Log::print(GREEN_BOLD(" * ") WHITE_BOLD("COMMANDS ") MAGENTA_BOLD("h") WHITE_BOLD("ashrate, ") + MAGENTA_BOLD("p") WHITE_BOLD("ause, ") + MAGENTA_BOLD("r") WHITE_BOLD("esume")); } else { - Log::i()->text(" * COMMANDS: 'h' hashrate, 'p' pause, 'r' resume, 'q' shutdown"); + Log::print(" * COMMANDS 'h' hashrate, 'p' pause, 'r' resume"); } } -void Summary::print() +} // namespace xmrig + + +void xmrig::Summary::print(Controller *controller) { - print_versions(); - print_cpu(); - print_threads(); - print_pools(); + controller->config()->printVersions(); + print_memory(controller->config()); + print_cpu(controller->config()); + print_threads(controller->config()); + controller->config()->pools().print(); -# ifndef XMRIG_NO_API - print_api(); -# endif - -# ifndef XMRIG_NO_CC - print_cc(); -# endif - - print_commands(); + print_commands(controller->config()); } + + + diff --git a/src/Summary.h b/src/Summary.h index f5a34900..4317d13e 100644 --- a/src/Summary.h +++ b/src/Summary.h @@ -4,8 +4,9 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , * * 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 @@ -21,17 +22,24 @@ * along with this program. If not, see . */ -#ifndef __SUMMARY_H__ -#define __SUMMARY_H__ +#ifndef XMRIG_SUMMARY_H +#define XMRIG_SUMMARY_H + + +namespace xmrig { + + +class Controller; class Summary { public: - static void print(); - - static void print_pushinfo(); + static void print(Controller *controller); }; -#endif /* __SUMMARY_H__ */ +} // namespace xmrig + + +#endif /* XMRIG_SUMMARY_H */ diff --git a/src/api/ApiState.cpp b/src/api/ApiState.cpp deleted file mode 100644 index 2e85a1ac..00000000 --- a/src/api/ApiState.cpp +++ /dev/null @@ -1,260 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * - * - * 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 . - */ - -#include -#include -#include - -#if _WIN32 -# include "winsock2.h" -#else -# include "unistd.h" -#endif - - -#include "api/ApiState.h" -#include "Cpu.h" -#include "Mem.h" -#include "net/Job.h" -#include "Options.h" -#include "Platform.h" -#include "rapidjson/document.h" -#include "rapidjson/stringbuffer.h" -#include "rapidjson/prettywriter.h" -#include "version.h" -#include "workers/Hashrate.h" - - -extern "C" -{ -#include "crypto/c_keccak.h" -} - - -static inline double normalize(double d) -{ - if (!isnormal(d)) { - return 0.0; - } - - return floor(d * 100.0) / 100.0; -} - - -ApiState::ApiState() -{ - m_threads = Options::i()->threads(); - m_hashrate = new double[m_threads * 3](); - - memset(m_totalHashrate, 0, sizeof(m_totalHashrate)); - memset(m_workerId, 0, sizeof(m_workerId)); - - if (Options::i()->apiWorkerId()) { - strncpy(m_workerId, Options::i()->apiWorkerId(), sizeof(m_workerId) - 1); - } - else { - gethostname(m_workerId, sizeof(m_workerId) - 1); - } - - genId(); -} - - -ApiState::~ApiState() -{ - delete [] m_hashrate; -} - - -char *ApiState::get(const char *url, int *status) const -{ - rapidjson::Document doc; - doc.SetObject(); - - getIdentify(doc); - getMiner(doc); - getHashrate(doc); - getResults(doc); - getConnection(doc); - - return finalize(doc); -} - - -void ApiState::tick(const Hashrate *hashrate) -{ - for (int i = 0; i < m_threads; ++i) { - m_hashrate[i * 3] = hashrate->calc((size_t) i, Hashrate::ShortInterval); - m_hashrate[i * 3 + 1] = hashrate->calc((size_t) i, Hashrate::MediumInterval); - m_hashrate[i * 3 + 2] = hashrate->calc((size_t) i, Hashrate::LargeInterval); - } - - m_totalHashrate[0] = hashrate->calc(Hashrate::ShortInterval); - m_totalHashrate[1] = hashrate->calc(Hashrate::MediumInterval); - m_totalHashrate[2] = hashrate->calc(Hashrate::LargeInterval); - m_highestHashrate = hashrate->highest(); -} - - -void ApiState::tick(const NetworkState &network) -{ - m_network = network; -} - - -char *ApiState::finalize(rapidjson::Document &doc) const -{ - rapidjson::StringBuffer buffer(0, 4096); - rapidjson::PrettyWriter writer(buffer); - writer.SetMaxDecimalPlaces(10); - doc.Accept(writer); - - return strdup(buffer.GetString()); -} - - -void ApiState::genId() -{ - memset(m_id, 0, sizeof(m_id)); - - uv_interface_address_t *interfaces; - int count = 0; - - if (uv_interface_addresses(&interfaces, &count) < 0) { - return; - } - - for (int i = 0; i < count; i++) { - if (!interfaces[i].is_internal && interfaces[i].address.address4.sin_family == AF_INET) { - uint8_t hash[200]; - const size_t addrSize = sizeof(interfaces[i].phys_addr); - const size_t inSize = strlen(APP_KIND) + addrSize; - - uint8_t *input = new uint8_t[inSize](); - memcpy(input, interfaces[i].phys_addr, addrSize); - memcpy(input + addrSize, APP_KIND, strlen(APP_KIND)); - - keccak(input, static_cast(inSize), hash, sizeof(hash)); - Job::toHex(hash, 8, m_id); - - delete [] input; - break; - } - } - - uv_free_interface_addresses(interfaces, count); -} - - -void ApiState::getConnection(rapidjson::Document &doc) const -{ - auto &allocator = doc.GetAllocator(); - - rapidjson::Value connection(rapidjson::kObjectType); - connection.AddMember("pool", rapidjson::StringRef(m_network.pool), allocator); - connection.AddMember("uptime", m_network.connectionTime(), allocator); - connection.AddMember("ping", m_network.latency(), allocator); - connection.AddMember("failures", m_network.failures, allocator); - connection.AddMember("error_log", rapidjson::Value(rapidjson::kArrayType), allocator); - - doc.AddMember("connection", connection, allocator); -} - - -void ApiState::getHashrate(rapidjson::Document &doc) const -{ - auto &allocator = doc.GetAllocator(); - - rapidjson::Value hashrate(rapidjson::kObjectType); - rapidjson::Value total(rapidjson::kArrayType); - rapidjson::Value threads(rapidjson::kArrayType); - - for (int i = 0; i < 3; ++i) { - total.PushBack(normalize(m_totalHashrate[i]), allocator); - } - - for (int i = 0; i < m_threads * 3; i += 3) { - rapidjson::Value thread(rapidjson::kArrayType); - thread.PushBack(normalize(m_hashrate[i]), allocator); - thread.PushBack(normalize(m_hashrate[i + 1]), allocator); - thread.PushBack(normalize(m_hashrate[i + 2]), allocator); - - threads.PushBack(thread, allocator); - } - - hashrate.AddMember("total", total, allocator); - hashrate.AddMember("highest", normalize(m_highestHashrate), allocator); - hashrate.AddMember("threads", threads, allocator); - doc.AddMember("hashrate", hashrate, allocator); -} - - -void ApiState::getIdentify(rapidjson::Document &doc) const -{ - doc.AddMember("id", rapidjson::StringRef(m_id), doc.GetAllocator()); - doc.AddMember("worker_id", rapidjson::StringRef(m_workerId), doc.GetAllocator()); -} - - -void ApiState::getMiner(rapidjson::Document &doc) const -{ - auto &allocator = doc.GetAllocator(); - - rapidjson::Value cpu(rapidjson::kObjectType); - cpu.AddMember("brand", rapidjson::StringRef(Cpu::brand()), allocator); - cpu.AddMember("aes", Cpu::hasAES(), allocator); - cpu.AddMember("x64", Cpu::isX64(), allocator); - cpu.AddMember("sockets", Cpu::sockets(), allocator); - - doc.AddMember("version", APP_VERSION, allocator); - doc.AddMember("kind", APP_KIND, allocator); - doc.AddMember("ua", rapidjson::StringRef(Platform::userAgent()), allocator); - doc.AddMember("cpu", cpu, allocator); - doc.AddMember("algo", rapidjson::StringRef(Options::i()->algoName()), allocator); - doc.AddMember("hugepages", Mem::isHugepagesAvailable(), allocator); - doc.AddMember("donate_level", Options::i()->donateLevel(), allocator); -} - - -void ApiState::getResults(rapidjson::Document &doc) const -{ - auto &allocator = doc.GetAllocator(); - - rapidjson::Value results(rapidjson::kObjectType); - - results.AddMember("diff_current", m_network.diff, allocator); - results.AddMember("shares_good", m_network.accepted, allocator); - results.AddMember("shares_total", m_network.accepted + m_network.rejected, allocator); - results.AddMember("avg_time", m_network.avgTime(), allocator); - results.AddMember("hashes_total", m_network.total, allocator); - - rapidjson::Value best(rapidjson::kArrayType); - for (size_t i = 0; i < m_network.topDiff.size(); ++i) { - best.PushBack(m_network.topDiff[i], allocator); - } - - results.AddMember("best", best, allocator); - results.AddMember("error_log", rapidjson::Value(rapidjson::kArrayType), allocator); - - doc.AddMember("results", results, allocator); -} diff --git a/src/api/Httpd.cpp b/src/api/Httpd.cpp deleted file mode 100644 index 79ecf0e7..00000000 --- a/src/api/Httpd.cpp +++ /dev/null @@ -1,116 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * - * - * 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 . - */ - -#include -#include -#include - - -#include "api/Api.h" -#include "api/Httpd.h" -#include "log/Log.h" - - -Httpd::Httpd(int port, const char *accessToken) : - m_accessToken(accessToken), - m_port(port), - m_daemon(nullptr) -{ -} - - -bool Httpd::start() -{ - if (!m_port) { - return false; - } - - m_daemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, m_port, nullptr, nullptr, &Httpd::handler, this, MHD_OPTION_END); - if (!m_daemon) { - LOG_ERR("HTTP Daemon failed to start."); - return false; - } - - return true; -} - - -int Httpd::auth(const char *header) -{ - if (!m_accessToken) { - return MHD_HTTP_OK; - } - - if (m_accessToken && !header) { - return MHD_HTTP_UNAUTHORIZED; - } - - const size_t size = strlen(header); - if (size < 8 || strlen(m_accessToken) != size - 7 || memcmp("Bearer ", header, 7) != 0) { - return MHD_HTTP_FORBIDDEN; - } - - return strncmp(m_accessToken, header + 7, strlen(m_accessToken)) == 0 ? MHD_HTTP_OK : MHD_HTTP_FORBIDDEN; -} - - -int Httpd::done(MHD_Connection *connection, int status, MHD_Response *rsp) -{ - if (!rsp) { - rsp = MHD_create_response_from_buffer(0, nullptr, MHD_RESPMEM_PERSISTENT); - } - - MHD_add_response_header(rsp, "Content-Type", "application/json"); - MHD_add_response_header(rsp, "Access-Control-Allow-Origin", "*"); - MHD_add_response_header(rsp, "Access-Control-Allow-Methods", "GET"); - MHD_add_response_header(rsp, "Access-Control-Allow-Headers", "Authorization"); - - const int ret = MHD_queue_response(connection, status, rsp); - MHD_destroy_response(rsp); - return ret; -} - - -int Httpd::handler(void *cls, struct MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **con_cls) -{ - if (strcmp(method, "OPTIONS") == 0) { - return done(connection, MHD_HTTP_OK, nullptr); - } - - if (strcmp(method, "GET") != 0) { - return MHD_NO; - } - - int status = static_cast(cls)->auth(MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "Authorization")); - if (status != MHD_HTTP_OK) { - return done(connection, status, nullptr); - } - - char *buf = Api::get(url, &status); - if (buf == nullptr) { - return MHD_NO; - } - - MHD_Response *rsp = MHD_create_response_from_buffer(strlen(buf), (void*) buf, MHD_RESPMEM_MUST_FREE); - return done(connection, status, rsp); -} diff --git a/src/backend/backend.cmake b/src/backend/backend.cmake new file mode 100644 index 00000000..c37cf262 --- /dev/null +++ b/src/backend/backend.cmake @@ -0,0 +1,13 @@ +include (src/backend/cpu/cpu.cmake) +include (src/backend/common/common.cmake) + + +set(HEADERS_BACKEND + "${HEADERS_BACKEND_COMMON}" + "${HEADERS_BACKEND_CPU}" + ) + +set(SOURCES_BACKEND + "${SOURCES_BACKEND_COMMON}" + "${SOURCES_BACKEND_CPU}" + ) diff --git a/src/workers/Hashrate.cpp b/src/backend/common/Hashrate.cpp similarity index 52% rename from src/workers/Hashrate.cpp rename to src/backend/common/Hashrate.cpp index bd5b7df6..f1c2ba3a 100644 --- a/src/workers/Hashrate.cpp +++ b/src/backend/common/Hashrate.cpp @@ -4,8 +4,9 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , * * 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,19 +23,21 @@ */ -#include -#include +#include +#include #include #include -#include "log/Log.h" -#include "Options.h" -#include "workers/Hashrate.h" + +#include "backend/common/Hashrate.h" +#include "base/tools/Chrono.h" +#include "base/tools/Handle.h" +#include "rapidjson/document.h" -inline const char *format(double h, char* buf, size_t size) +inline static const char *format(double h, char *buf, size_t size) { - if (isnormal(h)) { + if (std::isnormal(h)) { snprintf(buf, size, "%03.1f", h); return buf; } @@ -43,7 +46,7 @@ inline const char *format(double h, char* buf, size_t size) } -Hashrate::Hashrate(int threads) : +xmrig::Hashrate::Hashrate(size_t threads) : m_highest(0.0), m_threads(threads) { @@ -51,34 +54,35 @@ Hashrate::Hashrate(int threads) : m_timestamps = new uint64_t*[threads]; m_top = new uint32_t[threads]; - for (int i = 0; i < threads; i++) { - m_counts[i] = new uint64_t[kBucketSize]; - m_timestamps[i] = new uint64_t[kBucketSize]; - m_top[i] = 0; - - memset(m_counts[0], 0, sizeof(uint64_t) * kBucketSize); - memset(m_timestamps[0], 0, sizeof(uint64_t) * kBucketSize); - } - - const int printTime = Options::i()->printTime(); - - if (printTime > 0) { - uv_timer_init(uv_default_loop(), &m_timer); - m_timer.data = this; - - uv_timer_start(&m_timer, Hashrate::onReport, (printTime + 4) * 1000, printTime * 1000); + for (size_t i = 0; i < threads; i++) { + m_counts[i] = new uint64_t[kBucketSize](); + m_timestamps[i] = new uint64_t[kBucketSize](); + m_top[i] = 0; } } -double Hashrate::calc(size_t ms) const +xmrig::Hashrate::~Hashrate() +{ + for (size_t i = 0; i < m_threads; i++) { + delete [] m_counts[i]; + delete [] m_timestamps[i]; + } + + delete [] m_counts; + delete [] m_timestamps; + delete [] m_top; +} + + +double xmrig::Hashrate::calc(size_t ms) const { double result = 0.0; double data; - for (int i = 0; i < m_threads; ++i) { + for (size_t i = 0; i < m_threads; ++i) { data = calc(i, ms); - if (isnormal(data)) { + if (std::isnormal(data)) { result += data; } } @@ -87,10 +91,12 @@ double Hashrate::calc(size_t ms) const } -double Hashrate::calc(size_t threadId, size_t ms) const +double xmrig::Hashrate::calc(size_t threadId, size_t ms) const { - using namespace std::chrono; - const uint64_t now = time_point_cast(high_resolution_clock::now()).time_since_epoch().count(); + assert(threadId < m_threads); + if (threadId >= m_threads) { + return nan(""); + } uint64_t earliestHashCount = 0; uint64_t earliestStamp = 0; @@ -110,7 +116,7 @@ double Hashrate::calc(size_t threadId, size_t ms) const lastestHashCnt = m_counts[threadId][idx]; } - if (now - m_timestamps[threadId][idx] > ms) { + if (xmrig::Chrono::highResolutionMSecs() - m_timestamps[threadId][idx] > ms) { haveFullSet = true; break; } @@ -119,7 +125,7 @@ double Hashrate::calc(size_t threadId, size_t ms) const earliestHashCount = m_counts[threadId][idx]; } - if (!haveFullSet || earliestStamp == 0 || lastestStamp == 0) { + if ((!haveFullSet && ms > Intervals::ShortInterval) || earliestStamp == 0 || lastestStamp == 0) { return nan(""); } @@ -127,16 +133,14 @@ double Hashrate::calc(size_t threadId, size_t ms) const return nan(""); } - double hashes, time; - hashes = (double) lastestHashCnt - earliestHashCount; - time = (double) lastestStamp - earliestStamp; - time /= 1000.0; + const double hashes = static_cast(lastestHashCnt - earliestHashCount); + const double time = static_cast(lastestStamp - earliestStamp) / 1000.0; return hashes / time; } -void Hashrate::add(size_t threadId, uint64_t count, uint64_t timestamp) +void xmrig::Hashrate::add(size_t threadId, uint64_t count, uint64_t timestamp) { const size_t top = m_top[threadId]; m_counts[threadId][top] = count; @@ -146,38 +150,28 @@ void Hashrate::add(size_t threadId, uint64_t count, uint64_t timestamp) } -void Hashrate::print() -{ - char num1[8]; - char num2[8]; - char num3[8]; - char num4[8]; - - LOG_INFO(Options::i()->colors() ? "\x1B[01;37mspeed\x1B[0m 2.5s/60s/15m \x1B[01;36m%s \x1B[22;36m%s %s \x1B[01;36mH/s\x1B[0m max: \x1B[01;36m%s H/s" : "speed 2.5s/60s/15m %s %s %s H/s max: %s H/s", - format(calc(ShortInterval), num1, sizeof(num1)), - format(calc(MediumInterval), num2, sizeof(num2)), - format(calc(LargeInterval), num3, sizeof(num3)), - format(m_highest, num4, sizeof(num4)) - ); -} - - -void Hashrate::stop() -{ - uv_timer_stop(&m_timer); -} - - -void Hashrate::updateHighest() +void xmrig::Hashrate::updateHighest() { double highest = calc(ShortInterval); - if (isnormal(highest) && highest > m_highest) { + if (std::isnormal(highest) && highest > m_highest) { m_highest = highest; } } -void Hashrate::onReport(uv_timer_t *handle) +const char *xmrig::Hashrate::format(double h, char *buf, size_t size) { - static_cast(handle->data)->print(); + return ::format(h, buf, size); +} + + +rapidjson::Value xmrig::Hashrate::normalize(double d) +{ + using namespace rapidjson; + + if (!std::isnormal(d)) { + return Value(kNullType); + } + + return Value(floor(d * 100.0) / 100.0); } diff --git a/src/workers/Hashrate.h b/src/backend/common/Hashrate.h similarity index 69% rename from src/workers/Hashrate.h rename to src/backend/common/Hashrate.h index 026c0cdf..0674c6ab 100644 --- a/src/workers/Hashrate.h +++ b/src/backend/common/Hashrate.h @@ -4,8 +4,9 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , * * 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 @@ -21,47 +22,55 @@ * along with this program. If not, see . */ -#ifndef __HASHRATE_H__ -#define __HASHRATE_H__ +#ifndef XMRIG_HASHRATE_H +#define XMRIG_HASHRATE_H +#include #include -#include + + +#include "rapidjson/fwd.h" + + +namespace xmrig { class Hashrate { public: enum Intervals { - ShortInterval = 2500, + ShortInterval = 10000, MediumInterval = 60000, LargeInterval = 900000 }; - Hashrate(int threads); + Hashrate(size_t threads); + ~Hashrate(); double calc(size_t ms) const; double calc(size_t threadId, size_t ms) const; void add(size_t threadId, uint64_t count, uint64_t timestamp); - void print(); - void stop(); void updateHighest(); inline double highest() const { return m_highest; } - inline int threads() const { return m_threads; } + inline size_t threads() const { return m_threads; } + + static const char *format(double h, char *buf, size_t size); + static rapidjson::Value normalize(double d); private: - static void onReport(uv_timer_t *handle); - constexpr static size_t kBucketSize = 2 << 11; constexpr static size_t kBucketMask = kBucketSize - 1; double m_highest; - int m_threads; + size_t m_threads; uint32_t* m_top; uint64_t** m_counts; uint64_t** m_timestamps; - uv_timer_t m_timer; }; -#endif /* __HASHRATE_H__ */ +} // namespace xmrig + + +#endif /* XMRIG_HASHRATE_H */ diff --git a/src/backend/common/Thread.h b/src/backend/common/Thread.h new file mode 100644 index 00000000..b7165915 --- /dev/null +++ b/src/backend/common/Thread.h @@ -0,0 +1,67 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_THREAD_H +#define XMRIG_THREAD_H + + +#include + + +#include "backend/common/interfaces/IWorker.h" + + +namespace xmrig { + + +class IBackend; + + +template +class Thread +{ +public: + inline Thread(IBackend *backend, size_t index, const T &config) : m_index(index), m_config(config), m_backend(backend) {} + inline ~Thread() { m_thread.join(); delete m_worker; } + + inline const T &config() const { return m_config; } + inline IBackend *backend() const { return m_backend; } + inline IWorker *worker() const { return m_worker; } + inline size_t index() const { return m_index; } + inline void setWorker(IWorker *worker) { m_worker = worker; } + inline void start(void (*callback) (void *)) { m_thread = std::thread(callback, this); } + +private: + const size_t m_index = 0; + const T m_config; + IBackend *m_backend; + IWorker *m_worker = nullptr; + std::thread m_thread; +}; + + +} // namespace xmrig + + +#endif /* XMRIG_THREAD_H */ diff --git a/src/backend/common/Threads.cpp b/src/backend/common/Threads.cpp new file mode 100644 index 00000000..a16ad8bb --- /dev/null +++ b/src/backend/common/Threads.cpp @@ -0,0 +1,155 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include "backend/common/Threads.h" +#include "backend/cpu/CpuThreads.h" +#include "rapidjson/document.h" + + +namespace xmrig { + + +static const char *kAsterisk = "*"; + + +} // namespace xmrig + + +template +const T &xmrig::Threads::get(const String &profileName) const +{ + static T empty; + if (profileName.isNull() || !has(profileName)) { + return empty; + } + + return m_profiles.at(profileName); +} + + +template +size_t xmrig::Threads::read(const rapidjson::Value &value) +{ + using namespace rapidjson; + + for (auto &member : value.GetObject()) { + if (member.value.IsArray() || member.value.IsObject()) { + T threads(member.value); + + if (!threads.isEmpty()) { + move(member.name.GetString(), std::move(threads)); + } + } + } + + for (auto &member : value.GetObject()) { + if (member.value.IsArray() || member.value.IsObject()) { + continue; + } + + const Algorithm algo(member.name.GetString()); + if (!algo.isValid()) { + continue; + } + + if (member.value.IsBool() && member.value.IsFalse()) { + disable(algo); + continue; + } + + if (member.value.IsString()) { + if (has(member.value.GetString())) { + m_aliases.insert({ algo, member.value.GetString() }); + } + else { + m_disabled.insert(algo); + } + } + } + + return m_profiles.size(); +} + + +template +xmrig::String xmrig::Threads::profileName(const Algorithm &algorithm, bool strict) const +{ + if (isDisabled(algorithm)) { + return String(); + } + + const String name = algorithm.shortName(); + if (has(name)) { + return name; + } + + if (m_aliases.count(algorithm) > 0) { + return m_aliases.at(algorithm); + } + + if (strict) { + return String(); + } + + if (name.contains("/")) { + const String base = name.split('/').at(0); + if (has(base)) { + return base; + } + } + + if (has(kAsterisk)) { + return kAsterisk; + } + + return String(); +} + + +template +void xmrig::Threads::toJSON(rapidjson::Value &out, rapidjson::Document &doc) const +{ + using namespace rapidjson; + auto &allocator = doc.GetAllocator(); + + for (const auto &kv : m_profiles) { + out.AddMember(kv.first.toJSON(), kv.second.toJSON(doc), allocator); + } + + for (const Algorithm &algo : m_disabled) { + out.AddMember(StringRef(algo.shortName()), false, allocator); + } + + for (const auto &kv : m_aliases) { + out.AddMember(StringRef(kv.first.shortName()), kv.second.toJSON(), allocator); + } +} + + +namespace xmrig { + +template class Threads; + +} // namespace xmrig diff --git a/src/backend/common/Threads.h b/src/backend/common/Threads.h new file mode 100644 index 00000000..2cb333d6 --- /dev/null +++ b/src/backend/common/Threads.h @@ -0,0 +1,67 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_THREADS_H +#define XMRIG_THREADS_H + + +#include +#include + + +#include "base/tools/String.h" +#include "crypto/common/Algorithm.h" +#include "rapidjson/fwd.h" + + +namespace xmrig { + + +template +class Threads +{ +public: + inline bool has(const char *profile) const { return m_profiles.count(profile) > 0; } + inline bool isDisabled(const Algorithm &algo) const { return m_disabled.count(algo) > 0; } + inline bool isExist(const Algorithm &algo) const { return isDisabled(algo) || m_aliases.count(algo) > 0 || has(algo.shortName()); } + inline const T &get(const Algorithm &algo, bool strict = false) const { return get(profileName(algo, strict)); } + inline void disable(const Algorithm &algo) { m_disabled.insert(algo); } + inline void move(const char *profile, T &&threads) { m_profiles.insert({ profile, threads }); } + + const T &get(const String &profileName) const; + size_t read(const rapidjson::Value &value); + String profileName(const Algorithm &algorithm, bool strict = false) const; + void toJSON(rapidjson::Value &out, rapidjson::Document &doc) const; + +private: + std::map m_aliases; + std::map m_profiles; + std::set m_disabled; +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_THREADS_H */ diff --git a/src/workers/Worker.cpp b/src/backend/common/Worker.cpp similarity index 56% rename from src/workers/Worker.cpp rename to src/backend/common/Worker.cpp index 4f95cb1a..91ef0c7a 100644 --- a/src/workers/Worker.cpp +++ b/src/backend/common/Worker.cpp @@ -4,8 +4,10 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018 Lee Clagett + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , * * 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 @@ -21,43 +23,29 @@ * along with this program. If not, see . */ -#include + +#include "backend/common/Worker.h" +#include "base/kernel/Platform.h" +#include "base/tools/Chrono.h" +#include "crypto/common/VirtualMemory.h" -#include "Cpu.h" -#include "Mem.h" -#include "Platform.h" -#include "workers/Handle.h" -#include "workers/Worker.h" - - -Worker::Worker(Handle *handle) : - m_id(handle->threadId()), - m_affinedCpu(0), - m_threads(handle->threads()), +xmrig::Worker::Worker(size_t id, int64_t affinity, int priority) : + m_affinity(affinity), + m_id(id), m_hashCount(0), m_timestamp(0), - m_count(0), - m_sequence(0) + m_count(0) { - if (m_threads > 0 && m_threads <= Cpu::threads()) { - m_affinedCpu = Cpu::setThreadAffinity(m_id, handle->affinity()); - } + m_node = VirtualMemory::bindToNUMANode(affinity); - Platform::setThreadPriority(handle->priority()); + Platform::trySetThreadAffinity(affinity); + Platform::setThreadPriority(priority); } -Worker::~Worker() +void xmrig::Worker::storeStats() { -} - - -void Worker::storeStats() -{ - using namespace std::chrono; - - const uint64_t timestamp = time_point_cast(high_resolution_clock::now()).time_since_epoch().count(); m_hashCount.store(m_count, std::memory_order_relaxed); - m_timestamp.store(timestamp, std::memory_order_relaxed); + m_timestamp.store(Chrono::highResolutionMSecs(), std::memory_order_relaxed); } diff --git a/src/backend/common/Worker.h b/src/backend/common/Worker.h new file mode 100644 index 00000000..5f5df925 --- /dev/null +++ b/src/backend/common/Worker.h @@ -0,0 +1,65 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018 Lee Clagett + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_WORKER_H +#define XMRIG_WORKER_H + + +#include +#include + + +#include "backend/common/interfaces/IWorker.h" + + +namespace xmrig { + + +class Worker : public IWorker +{ +public: + Worker(size_t id, int64_t affinity, int priority); + + 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); } + +protected: + void storeStats(); + + const int64_t m_affinity; + const size_t m_id; + std::atomic m_hashCount; + std::atomic m_timestamp; + uint32_t m_node = 0; + uint64_t m_count; +}; + + +} // namespace xmrig + + +#endif /* XMRIG_WORKER_H */ diff --git a/src/backend/common/WorkerJob.h b/src/backend/common/WorkerJob.h new file mode 100644 index 00000000..4b691952 --- /dev/null +++ b/src/backend/common/WorkerJob.h @@ -0,0 +1,143 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_WORKERJOB_H +#define XMRIG_WORKERJOB_H + + +#include + + +#include "base/net/stratum/Job.h" +#include "crypto/common/Nonce.h" + + +namespace xmrig { + + +template +class WorkerJob +{ +public: + inline const Job ¤tJob() const { return m_jobs[index()]; } + inline uint32_t *nonce(size_t i = 0) { return reinterpret_cast(blob() + (i * currentJob().size()) + 39); } + inline uint64_t sequence() const { return m_sequence; } + inline uint8_t *blob() { return m_blobs[index()]; } + inline uint8_t index() const { return m_index; } + + + inline void add(const Job &job, uint64_t sequence, uint32_t reserveCount) + { + m_sequence = sequence; + + if (currentJob() == job) { + return; + } + + if (index() == 1 && job.index() == 0 && job == m_jobs[0]) { + m_index = 0; + return; + } + + save(job, reserveCount); + } + + + inline void nextRound(uint32_t reserveCount) + { + m_rounds[index()]++; + + if ((m_rounds[index()] % reserveCount) == 0) { + for (size_t i = 0; i < N; ++i) { + *nonce(i) = Nonce::next(index(), *nonce(i), reserveCount, currentJob().isNicehash()); + } + } + else { + for (size_t i = 0; i < N; ++i) { + *nonce(i) += 1; + } + } + } + + +private: + inline void save(const Job &job, uint32_t reserveCount) + { + m_index = job.index(); + const size_t size = job.size(); + m_jobs[index()] = job; + m_rounds[index()] = 0; + + for (size_t i = 0; i < N; ++i) { + memcpy(m_blobs[index()] + (i * size), job.blob(), size); + *nonce(i) = Nonce::next(index(), *nonce(i), reserveCount, job.isNicehash()); + } + } + + + alignas(16) uint8_t m_blobs[2][Job::kMaxBlobSize * N]; + Job m_jobs[2]; + uint32_t m_rounds[2] = { 0, 0 }; + uint64_t m_sequence = 0; + uint8_t m_index = 0; +}; + + +template<> +inline uint32_t *xmrig::WorkerJob<1>::nonce(size_t) +{ + return reinterpret_cast(blob() + 39); +} + + +template<> +inline void xmrig::WorkerJob<1>::nextRound(uint32_t reserveCount) +{ + m_rounds[index()]++; + + if ((m_rounds[index()] % reserveCount) == 0) { + *nonce() = Nonce::next(index(), *nonce(), reserveCount, currentJob().isNicehash()); + } + else { + *nonce() += 1; + } +} + + +template<> +inline void xmrig::WorkerJob<1>::save(const Job &job, uint32_t reserveCount) +{ + m_index = job.index(); + m_jobs[index()] = job; + m_rounds[index()] = 0; + + memcpy(blob(), job.blob(), job.size()); + *nonce() = Nonce::next(index(), *nonce(), reserveCount, currentJob().isNicehash()); +} + + +} // namespace xmrig + + +#endif /* XMRIG_WORKERJOB_H */ diff --git a/src/backend/common/Workers.cpp b/src/backend/common/Workers.cpp new file mode 100644 index 00000000..eb348328 --- /dev/null +++ b/src/backend/common/Workers.cpp @@ -0,0 +1,193 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018 Lee Clagett + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include "backend/common/Hashrate.h" +#include "backend/common/interfaces/IBackend.h" +#include "backend/common/Workers.h" +#include "backend/cpu/CpuWorker.h" +#include "base/io/log/Log.h" + + +namespace xmrig { + + +class WorkersPrivate +{ +public: + inline WorkersPrivate() + { + } + + + inline ~WorkersPrivate() + { + delete hashrate; + } + + + Hashrate *hashrate = nullptr; + IBackend *backend = nullptr; +}; + + +} // namespace xmrig + + +template +xmrig::Workers::Workers() : + d_ptr(new WorkersPrivate()) +{ + +} + + +template +xmrig::Workers::~Workers() +{ + delete d_ptr; +} + + +template +const xmrig::Hashrate *xmrig::Workers::hashrate() const +{ + return d_ptr->hashrate; +} + + +template +void xmrig::Workers::setBackend(IBackend *backend) +{ + d_ptr->backend = backend; +} + + +template +void xmrig::Workers::start(const std::vector &data) +{ + for (const T &item : data) { + m_workers.push_back(new Thread(d_ptr->backend, m_workers.size(), item)); + } + + d_ptr->hashrate = new Hashrate(m_workers.size()); + + for (Thread *worker : m_workers) { + worker->start(Workers::onReady); + } +} + + +template +void xmrig::Workers::stop() +{ + Nonce::stop(T::backend()); + + for (Thread *worker : m_workers) { + delete worker; + } + + m_workers.clear(); + Nonce::touch(T::backend()); + + delete d_ptr->hashrate; + d_ptr->hashrate = nullptr; +} + + +template +void xmrig::Workers::tick(uint64_t) +{ + if (!d_ptr->hashrate) { + return; + } + + for (Thread *handle : m_workers) { + if (!handle->worker()) { + return; + } + + d_ptr->hashrate->add(handle->index(), handle->worker()->hashCount(), handle->worker()->timestamp()); + } + + d_ptr->hashrate->updateHighest(); +} + + +template +xmrig::IWorker *xmrig::Workers::create(Thread *) +{ + return nullptr; +} + + +template +void xmrig::Workers::onReady(void *arg) +{ + Thread *handle = static_cast* >(arg); + + IWorker *worker = create(handle); + if (!worker || !worker->selfTest()) { + LOG_ERR("thread %zu error: \"hash self-test failed\".", worker->id()); + + return; + } + + handle->setWorker(worker); + handle->backend()->start(worker); +} + + +namespace xmrig { + + +template<> +xmrig::IWorker *xmrig::Workers::create(Thread *handle) +{ + switch (handle->config().intensity) { + case 1: + return new CpuWorker<1>(handle->index(), handle->config()); + + case 2: + return new CpuWorker<2>(handle->index(), handle->config()); + + case 3: + return new CpuWorker<3>(handle->index(), handle->config()); + + case 4: + return new CpuWorker<4>(handle->index(), handle->config()); + + case 5: + return new CpuWorker<5>(handle->index(), handle->config()); + } + + return nullptr; +} + + +template class Workers; + + +} // namespace xmrig diff --git a/src/backend/common/Workers.h b/src/backend/common/Workers.h new file mode 100644 index 00000000..77dd434c --- /dev/null +++ b/src/backend/common/Workers.h @@ -0,0 +1,73 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018 Lee Clagett + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_WORKERS_H +#define XMRIG_WORKERS_H + + +#include "backend/common/Thread.h" +#include "backend/cpu/CpuLaunchData.h" + + +namespace xmrig { + + +class Hashrate; +class WorkersPrivate; + + +template +class Workers +{ +public: + Workers(); + ~Workers(); + + const Hashrate *hashrate() const; + void setBackend(IBackend *backend); + void start(const std::vector &data); + void stop(); + void tick(uint64_t ticks); + +private: + static IWorker *create(Thread *handle); + static void onReady(void *arg); + + std::vector *> m_workers; + WorkersPrivate *d_ptr; +}; + + +template<> +IWorker *Workers::create(Thread *handle); + + +extern template class Workers; + + +} // namespace xmrig + + +#endif /* XMRIG_WORKERS_H */ diff --git a/src/backend/common/common.cmake b/src/backend/common/common.cmake new file mode 100644 index 00000000..c470ea50 --- /dev/null +++ b/src/backend/common/common.cmake @@ -0,0 +1,18 @@ +set(HEADERS_BACKEND_COMMON + src/backend/common/interfaces/IBackend.h + src/backend/common/interfaces/IThread.h + src/backend/common/interfaces/IWorker.h + src/backend/common/Hashrate.h + src/backend/common/Thread.h + src/backend/common/Threads.h + src/backend/common/Worker.h + src/backend/common/Workers.h + src/backend/common/WorkerJob.h + ) + +set(SOURCES_BACKEND_COMMON + src/backend/common/Hashrate.cpp + src/backend/common/Threads.cpp + src/backend/common/Worker.cpp + src/backend/common/Workers.cpp + ) diff --git a/src/backend/common/interfaces/IBackend.h b/src/backend/common/interfaces/IBackend.h new file mode 100644 index 00000000..2ec8bf04 --- /dev/null +++ b/src/backend/common/interfaces/IBackend.h @@ -0,0 +1,71 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_IBACKEND_H +#define XMRIG_IBACKEND_H + + +#include + + +#include "rapidjson/fwd.h" + + +namespace xmrig { + + +class Algorithm; +class Hashrate; +class IWorker; +class Job; +class String; + + +class IBackend +{ +public: + virtual ~IBackend() = default; + + virtual bool isEnabled() const = 0; + virtual bool isEnabled(const Algorithm &algorithm) const = 0; + virtual const Hashrate *hashrate() const = 0; + virtual const String &profileName() const = 0; + virtual const String &type() const = 0; + virtual void prepare(const Job &nextJob) = 0; + virtual void printHashrate(bool details) = 0; + virtual void setJob(const Job &job) = 0; + virtual void start(IWorker *worker) = 0; + virtual void stop() = 0; + virtual void tick(uint64_t ticks) = 0; + +# ifdef XMRIG_FEATURE_API + virtual rapidjson::Value toJSON(rapidjson::Document &doc) const = 0; +# endif +}; + + +} // namespace xmrig + + +#endif // XMRIG_IBACKEND_H diff --git a/src/backend/common/interfaces/IThread.h b/src/backend/common/interfaces/IThread.h new file mode 100644 index 00000000..3c0a7287 --- /dev/null +++ b/src/backend/common/interfaces/IThread.h @@ -0,0 +1,77 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016-2018 XMRig + * + * 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 . + */ + +#ifndef XMRIG_ITHREAD_H +#define XMRIG_ITHREAD_H + + +#include + + +#include "crypto/common/Algorithm.h" +#include "rapidjson/fwd.h" + + +namespace xmrig { + + +class IThread +{ +public: + enum Type { + CPU, + OpenCL, + CUDA + }; + + enum Multiway { + SingleWay = 1, + DoubleWay, + TripleWay, + QuadWay, + PentaWay + }; + + virtual ~IThread() = default; + + virtual Algorithm algorithm() const = 0; + virtual int priority() const = 0; + virtual int64_t affinity() const = 0; + virtual Multiway multiway() const = 0; + virtual rapidjson::Value toConfig(rapidjson::Document &doc) const = 0; + virtual size_t index() const = 0; + virtual Type type() const = 0; + +# ifdef XMRIG_FEATURE_API + virtual rapidjson::Value toAPI(rapidjson::Document &doc) const = 0; +# endif + +# ifdef APP_DEBUG + virtual void print() const = 0; +# endif +}; + + +} /* namespace xmrig */ + + +#endif // XMRIG_ITHREAD_H diff --git a/src/backend/common/interfaces/IWorker.h b/src/backend/common/interfaces/IWorker.h new file mode 100644 index 00000000..0d7fe1d2 --- /dev/null +++ b/src/backend/common/interfaces/IWorker.h @@ -0,0 +1,56 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_IWORKER_H +#define XMRIG_IWORKER_H + + +#include +#include + + +namespace xmrig { + + +class VirtualMemory; + + +class IWorker +{ +public: + virtual ~IWorker() = default; + + virtual bool selfTest() = 0; + virtual const VirtualMemory *memory() const = 0; + virtual size_t id() const = 0; + virtual uint64_t hashCount() const = 0; + virtual uint64_t timestamp() const = 0; + virtual void start() = 0; +}; + + +} // namespace xmrig + + +#endif // XMRIG_IWORKER_H diff --git a/src/backend/cpu/Cpu.cpp b/src/backend/cpu/Cpu.cpp new file mode 100644 index 00000000..4d9effce --- /dev/null +++ b/src/backend/cpu/Cpu.cpp @@ -0,0 +1,104 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include + + +#include "backend/cpu/Cpu.h" +#include "rapidjson/document.h" + + +#if defined(XMRIG_FEATURE_HWLOC) +# include "backend/cpu/platform/HwlocCpuInfo.h" +#elif defined(XMRIG_FEATURE_LIBCPUID) +# include "backend/cpu/platform/AdvancedCpuInfo.h" +#else +# include "backend/cpu/platform/BasicCpuInfo.h" +#endif + + +static xmrig::ICpuInfo *cpuInfo = nullptr; + + +xmrig::ICpuInfo *xmrig::Cpu::info() +{ + assert(cpuInfo != nullptr); + + return cpuInfo; +} + + +rapidjson::Value xmrig::Cpu::toJSON(rapidjson::Document &doc) +{ + using namespace rapidjson; + auto &allocator = doc.GetAllocator(); + + ICpuInfo *i = info(); + Value cpu(kObjectType); + Assembly assembly(i->assembly()); + + cpu.AddMember("brand", StringRef(i->brand()), allocator); + cpu.AddMember("aes", i->hasAES(), allocator); + cpu.AddMember("avx2", i->hasAVX2(), allocator); + cpu.AddMember("x64", i->isX64(), allocator); + cpu.AddMember("l2", static_cast(i->L2()), allocator); + cpu.AddMember("l3", static_cast(i->L3()), allocator); + cpu.AddMember("cores", static_cast(i->cores()), allocator); + cpu.AddMember("threads", static_cast(i->threads()), allocator); + cpu.AddMember("packages", static_cast(i->packages()), allocator); + cpu.AddMember("nodes", static_cast(i->nodes()), allocator); + cpu.AddMember("backend", StringRef(i->backend()), allocator); + +# ifdef XMRIG_FEATURE_ASM + cpu.AddMember("assembly", StringRef(assembly.toString()), allocator); +# else + cpu.AddMember("assembly", "none", allocator); +# endif + + return cpu; +} + + +void xmrig::Cpu::init() +{ + assert(cpuInfo == nullptr); + +# if defined(XMRIG_FEATURE_HWLOC) + cpuInfo = new HwlocCpuInfo(); +# elif defined(XMRIG_FEATURE_LIBCPUID) + cpuInfo = new AdvancedCpuInfo(); +# else + cpuInfo = new BasicCpuInfo(); +# endif +} + + +void xmrig::Cpu::release() +{ + assert(cpuInfo != nullptr); + + delete cpuInfo; + cpuInfo = nullptr; +} diff --git a/src/Platform.h b/src/backend/cpu/Cpu.h similarity index 61% rename from src/Platform.h rename to src/backend/cpu/Cpu.h index 87c8cc4d..bece97d3 100644 --- a/src/Platform.h +++ b/src/backend/cpu/Cpu.h @@ -4,8 +4,9 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , * * 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 @@ -21,25 +22,29 @@ * along with this program. If not, see . */ -#ifndef __PLATFORM_H__ -#define __PLATFORM_H__ +#ifndef XMRIG_CPU_H +#define XMRIG_CPU_H -class Platform +#include "backend/cpu/interfaces/ICpuInfo.h" + + +namespace xmrig { + + +class Cpu { public: - static const char *defaultConfigName(); - static void init(const char *userAgent); + static ICpuInfo *info(); + static rapidjson::Value toJSON(rapidjson::Document &doc); + static void init(); static void release(); - static void setProcessPriority(int priority); - static void setThreadPriority(int priority); - static inline const char *userAgent() { return m_userAgent; } - -private: - static char *m_defaultConfigName; - static char *m_userAgent; + inline static Assembly::Id assembly(Assembly::Id hint) { return hint == Assembly::AUTO ? Cpu::info()->assembly() : hint; } }; -#endif /* __PLATFORM_H__ */ +} /* namespace xmrig */ + + +#endif /* XMRIG_CPU_H */ diff --git a/src/backend/cpu/CpuBackend.cpp b/src/backend/cpu/CpuBackend.cpp new file mode 100644 index 00000000..cc1fda0f --- /dev/null +++ b/src/backend/cpu/CpuBackend.cpp @@ -0,0 +1,384 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include + + +#include "backend/common/Hashrate.h" +#include "backend/common/interfaces/IWorker.h" +#include "backend/common/Workers.h" +#include "backend/cpu/Cpu.h" +#include "backend/cpu/CpuBackend.h" +#include "base/io/log/Log.h" +#include "base/net/stratum/Job.h" +#include "base/tools/Chrono.h" +#include "base/tools/String.h" +#include "core/config/Config.h" +#include "core/Controller.h" +#include "crypto/common/VirtualMemory.h" +#include "crypto/rx/Rx.h" +#include "crypto/rx/RxDataset.h" +#include "rapidjson/document.h" + + +#ifdef XMRIG_ALGO_ARGON2 +# include "crypto/argon2/Impl.h" +#endif + + +namespace xmrig { + + +extern template class Threads; + + +static const char *tag = CYAN_BG_BOLD(" cpu "); +static const String kType = "cpu"; + + +struct LaunchStatus +{ +public: + inline void reset() + { + hugePages = 0; + memory = 0; + pages = 0; + started = 0; + threads = 0; + ways = 0; + ts = Chrono::steadyMSecs(); + } + + size_t hugePages = 0; + size_t memory = 0; + size_t pages = 0; + size_t started = 0; + size_t threads = 0; + size_t ways = 0; + uint64_t ts = 0; +}; + + +class CpuBackendPrivate +{ +public: + inline CpuBackendPrivate(Controller *controller) : + controller(controller) + { + } + + + inline void start() + { + LOG_INFO("%s use profile " BLUE_BG(WHITE_BOLD_S " %s ") WHITE_BOLD_S " (" CYAN_BOLD("%zu") WHITE_BOLD(" threads)") " scratchpad " CYAN_BOLD("%zu KB"), + tag, + profileName.data(), + threads.size(), + algo.l3() / 1024 + ); + + workers.stop(); + + status.reset(); + status.memory = algo.l3(); + status.threads = threads.size(); + + for (const CpuLaunchData &data : threads) { + status.ways += static_cast(data.intensity); + } + + workers.start(threads); + } + + + size_t ways() + { + std::lock_guard lock(mutex); + + return status.ways; + } + + + Algorithm algo; + Controller *controller; + LaunchStatus status; + std::mutex mutex; + std::vector threads; + String profileName; + Workers workers; +}; + + +} // namespace xmrig + + +xmrig::CpuBackend::CpuBackend(Controller *controller) : + d_ptr(new CpuBackendPrivate(controller)) +{ + d_ptr->workers.setBackend(this); +} + + +xmrig::CpuBackend::~CpuBackend() +{ + delete d_ptr; +} + + +std::pair xmrig::CpuBackend::hugePages() const +{ + std::pair pages(0, 0); + +# ifdef XMRIG_ALGO_RANDOMX + if (d_ptr->algo.family() == Algorithm::RANDOM_X) { + pages = Rx::hugePages(); + } +# endif + + std::lock_guard lock(d_ptr->mutex); + + pages.first += d_ptr->status.hugePages; + pages.second += d_ptr->status.pages; + + return pages; +} + + +bool xmrig::CpuBackend::isEnabled() const +{ + return d_ptr->controller->config()->cpu().isEnabled(); +} + + +bool xmrig::CpuBackend::isEnabled(const Algorithm &algorithm) const +{ + return !d_ptr->controller->config()->cpu().threads().get(algorithm).isEmpty(); +} + + +const xmrig::Hashrate *xmrig::CpuBackend::hashrate() const +{ + return d_ptr->workers.hashrate(); +} + + +const xmrig::String &xmrig::CpuBackend::profileName() const +{ + return d_ptr->profileName; +} + + +size_t xmrig::CpuBackend::ways() const +{ + return d_ptr->ways(); +} + + +const xmrig::String &xmrig::CpuBackend::type() const +{ + return kType; +} + + +void xmrig::CpuBackend::prepare(const Job &nextJob) +{ +# ifdef XMRIG_ALGO_ARGON2 + if (nextJob.algorithm().family() == Algorithm::ARGON2 && argon2::Impl::select(d_ptr->controller->config()->cpu().argon2Impl())) { + LOG_INFO("%s use " WHITE_BOLD("argon2") " implementation " CSI "1;%dm" "%s", + tag, + argon2::Impl::name() == "default" ? 33 : 32, + argon2::Impl::name().data() + ); + } +# endif +} + + +void xmrig::CpuBackend::printHashrate(bool details) +{ + if (!details || !hashrate()) { + return; + } + + char num[8 * 3] = { 0 }; + + Log::print(WHITE_BOLD_S "| CPU THREAD | AFFINITY | 10s H/s | 60s H/s | 15m H/s |"); + + size_t i = 0; + for (const CpuLaunchData &data : d_ptr->threads) { + Log::print("| %13zu | %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) + ); + + i++; + } +} + + +void xmrig::CpuBackend::setJob(const Job &job) +{ + if (!isEnabled()) { + return stop(); + } + + const CpuConfig &cpu = d_ptr->controller->config()->cpu(); + + std::vector threads = cpu.get(d_ptr->controller->miner(), job.algorithm()); + if (d_ptr->threads.size() == threads.size() && std::equal(d_ptr->threads.begin(), d_ptr->threads.end(), threads.begin())) { + return; + } + + d_ptr->algo = job.algorithm(); + d_ptr->profileName = cpu.threads().profileName(job.algorithm()); + + if (d_ptr->profileName.isNull() || threads.empty()) { + d_ptr->workers.stop(); + + LOG_WARN(YELLOW_BOLD_S "CPU disabled, no suitable configuration for algo %s", job.algorithm().shortName()); + + return; + } + + d_ptr->threads = std::move(threads); + d_ptr->start(); +} + + +void xmrig::CpuBackend::start(IWorker *worker) +{ + d_ptr->mutex.lock(); + + const auto pages = worker->memory()->hugePages(); + + d_ptr->status.started++; + d_ptr->status.hugePages += pages.first; + d_ptr->status.pages += pages.second; + + if (d_ptr->status.started == d_ptr->status.threads) { + const double percent = d_ptr->status.hugePages == 0 ? 0.0 : static_cast(d_ptr->status.hugePages) / d_ptr->status.pages * 100.0; + const size_t memory = d_ptr->status.ways * d_ptr->status.memory / 1024; + + LOG_INFO("%s" GREEN_BOLD(" READY") " threads " CYAN_BOLD("%zu(%zu)") " huge pages %s%zu/%zu %1.0f%%\x1B[0m memory " CYAN_BOLD("%zu KB") BLACK_BOLD(" (%" PRIu64 " ms)"), + tag, + d_ptr->status.threads, d_ptr->status.ways, + (d_ptr->status.hugePages == d_ptr->status.pages ? GREEN_BOLD_S : (d_ptr->status.hugePages == 0 ? RED_BOLD_S : YELLOW_BOLD_S)), + d_ptr->status.hugePages, d_ptr->status.pages, percent, memory, + Chrono::steadyMSecs() - d_ptr->status.ts + ); + } + + d_ptr->mutex.unlock(); + + worker->start(); +} + + +void xmrig::CpuBackend::stop() +{ + const uint64_t ts = Chrono::steadyMSecs(); + + d_ptr->workers.stop(); + d_ptr->threads.clear(); + + LOG_INFO("%s" YELLOW(" stopped") BLACK_BOLD(" (%" PRIu64 " ms)"), tag, Chrono::steadyMSecs() - ts); +} + + +void xmrig::CpuBackend::tick(uint64_t ticks) +{ + d_ptr->workers.tick(ticks); +} + + +#ifdef XMRIG_FEATURE_API +rapidjson::Value xmrig::CpuBackend::toJSON(rapidjson::Document &doc) const +{ + using namespace rapidjson; + auto &allocator = doc.GetAllocator(); + const CpuConfig &cpu = d_ptr->controller->config()->cpu(); + + Value out(kObjectType); + out.AddMember("type", type().toJSON(), allocator); + out.AddMember("enabled", isEnabled(), allocator); + out.AddMember("algo", d_ptr->algo.toJSON(), allocator); + out.AddMember("profile", profileName().toJSON(), allocator); + out.AddMember("hw-aes", cpu.isHwAES(), allocator); + out.AddMember("priority", cpu.priority(), allocator); + +# ifdef XMRIG_FEATURE_ASM + const Assembly assembly = Cpu::assembly(cpu.assembly()); + out.AddMember("asm", assembly.toJSON(), allocator); +# else + out.AddMember("asm", false, allocator); +# endif + +# ifdef XMRIG_ALGO_ARGON2 + out.AddMember("argon2-impl", argon2::Impl::name().toJSON(), allocator); +# endif + + const auto pages = hugePages(); + + rapidjson::Value hugepages(rapidjson::kArrayType); + hugepages.PushBack(pages.first, allocator); + hugepages.PushBack(pages.second, allocator); + + out.AddMember("hugepages", hugepages, allocator); + out.AddMember("memory", static_cast(d_ptr->algo.isValid() ? (d_ptr->ways() * d_ptr->algo.l3()) : 0), allocator); + + if (d_ptr->threads.empty() || !hashrate()) { + return out; + } + + Value threads(kArrayType); + const Hashrate *hr = hashrate(); + + size_t i = 0; + for (const CpuLaunchData &data : d_ptr->threads) { + Value thread(kObjectType); + thread.AddMember("intensity", data.intensity, allocator); + thread.AddMember("affinity", data.affinity, allocator); + thread.AddMember("av", data.av(), allocator); + + Value hashrate(kArrayType); + hashrate.PushBack(Hashrate::normalize(hr->calc(i, Hashrate::ShortInterval)), allocator); + hashrate.PushBack(Hashrate::normalize(hr->calc(i, Hashrate::MediumInterval)), allocator); + hashrate.PushBack(Hashrate::normalize(hr->calc(i, Hashrate::LargeInterval)), allocator); + + i++; + + thread.AddMember("hashrate", hashrate, allocator); + threads.PushBack(thread, allocator); + } + + out.AddMember("threads", threads, allocator); + + return out; +} +#endif diff --git a/src/backend/cpu/CpuBackend.h b/src/backend/cpu/CpuBackend.h new file mode 100644 index 00000000..ff665c46 --- /dev/null +++ b/src/backend/cpu/CpuBackend.h @@ -0,0 +1,77 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_CPUBACKEND_H +#define XMRIG_CPUBACKEND_H + + +#include + + +#include "backend/common/interfaces/IBackend.h" + + +namespace xmrig { + + +class Controller; +class CpuBackendPrivate; +class Miner; + + +class CpuBackend : public IBackend +{ +public: + CpuBackend(Controller *controller); + ~CpuBackend() override; + + std::pair hugePages() const; + size_t ways() const; + +protected: + bool isEnabled() const override; + bool isEnabled(const Algorithm &algorithm) const override; + const Hashrate *hashrate() const override; + const String &profileName() const override; + const String &type() const override; + void prepare(const Job &nextJob) override; + void printHashrate(bool details) override; + void setJob(const Job &job) override; + void start(IWorker *worker) override; + void stop() override; + void tick(uint64_t ticks) override; + +# ifdef XMRIG_FEATURE_API + rapidjson::Value toJSON(rapidjson::Document &doc) const override; +# endif + +private: + CpuBackendPrivate *d_ptr; +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_CPUBACKEND_H */ diff --git a/src/backend/cpu/CpuConfig.cpp b/src/backend/cpu/CpuConfig.cpp new file mode 100644 index 00000000..70446f12 --- /dev/null +++ b/src/backend/cpu/CpuConfig.cpp @@ -0,0 +1,223 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include "backend/cpu/Cpu.h" +#include "backend/cpu/CpuConfig.h" +#include "base/io/json/Json.h" +#include "rapidjson/document.h" + + +namespace xmrig { + +static const char *kCn = "cn"; +static const char *kEnabled = "enabled"; +static const char *kHugePages = "huge-pages"; +static const char *kHwAes = "hw-aes"; +static const char *kPriority = "priority"; + +#ifdef XMRIG_FEATURE_ASM +static const char *kAsm = "asm"; +#endif + +#ifdef XMRIG_ALGO_CN_GPU +static const char *kCnGPU = "cn/gpu"; +#endif + +#ifdef XMRIG_ALGO_CN_LITE +static const char *kCnLite = "cn-lite"; +#endif + +#ifdef XMRIG_ALGO_CN_HEAVY +static const char *kCnHeavy = "cn-heavy"; +#endif + +#ifdef XMRIG_ALGO_CN_PICO +static const char *kCnPico = "cn-pico"; +#endif + +#ifdef XMRIG_ALGO_CN_EXTREMELITE +static const char *kCnExtremelite = "cn-extremelite"; +#endif + +#ifdef XMRIG_ALGO_RANDOMX +static const char *kRx = "rx"; +static const char *kRxWOW = "rx/wow"; +#endif + +#ifdef XMRIG_ALGO_ARGON2 +static const char *kArgon2 = "argon2"; +static const char *kArgon2Impl = "argon2-impl"; +#endif + +extern template class Threads; + +} + + +xmrig::CpuConfig::CpuConfig() +{ +} + + +bool xmrig::CpuConfig::isHwAES() const +{ + return (m_aes == AES_AUTO ? (Cpu::info()->hasAES() ? AES_HW : AES_SOFT) : m_aes) == AES_HW; +} + + +rapidjson::Value xmrig::CpuConfig::toJSON(rapidjson::Document &doc) const +{ + using namespace rapidjson; + auto &allocator = doc.GetAllocator(); + + Value obj(kObjectType); + + obj.AddMember(StringRef(kEnabled), m_enabled, allocator); + obj.AddMember(StringRef(kHugePages), m_hugePages, 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); + +# ifdef XMRIG_FEATURE_ASM + obj.AddMember(StringRef(kAsm), m_assembly.toJSON(), allocator); +# endif + +# ifdef XMRIG_ALGO_ARGON2 + obj.AddMember(StringRef(kArgon2Impl), m_argon2Impl.toJSON(), allocator); +# endif + + m_threads.toJSON(obj, doc); + + return obj; +} + + +std::vector xmrig::CpuConfig::get(const Miner *miner, const Algorithm &algorithm) const +{ + std::vector out; + const CpuThreads &threads = m_threads.get(algorithm); + + if (threads.isEmpty()) { + return out; + } + + out.reserve(threads.count()); + + for (const CpuThread &thread : threads.data()) { + out.push_back(CpuLaunchData(miner, algorithm, *this, thread)); + } + + return out; +} + + +void xmrig::CpuConfig::read(const rapidjson::Value &value, uint32_t version) +{ + if (value.IsObject()) { + m_enabled = Json::getBool(value, kEnabled, m_enabled); + m_hugePages = Json::getBool(value, kHugePages, m_hugePages); + + setAesMode(Json::getValue(value, kHwAes)); + setPriority(Json::getInt(value, kPriority, -1)); + +# ifdef XMRIG_FEATURE_ASM + m_assembly = Json::getValue(value, kAsm); +# endif + +# ifdef XMRIG_ALGO_ARGON2 + m_argon2Impl = Json::getString(value, kArgon2Impl); +# endif + + if (!m_threads.read(value)) { + generate(); + } + + if (version == 0) { + generateArgon2(); + } + } + else if (value.IsBool() && value.IsFalse()) { + m_enabled = false; + } + else { + generate(); + } +} + + +void xmrig::CpuConfig::generate() +{ + m_shouldSave = true; + ICpuInfo *cpu = Cpu::info(); + + m_threads.disable(Algorithm::CN_0); + m_threads.move(kCn, cpu->threads(Algorithm::CN_0)); + +# ifdef XMRIG_ALGO_CN_GPU + m_threads.move(kCnGPU, cpu->threads(Algorithm::CN_GPU)); +# endif + +# ifdef XMRIG_ALGO_CN_LITE + m_threads.disable(Algorithm::CN_LITE_0); + m_threads.move(kCnLite, cpu->threads(Algorithm::CN_LITE_1)); +# endif + +# ifdef XMRIG_ALGO_CN_HEAVY + m_threads.move(kCnHeavy, cpu->threads(Algorithm::CN_HEAVY_0)); +# endif + +# ifdef XMRIG_ALGO_CN_PICO + m_threads.move(kCnPico, cpu->threads(Algorithm::CN_PICO_0)); +# endif + +# ifdef XMRIG_ALGO_CN_EXTREMELITE + m_threads.move(kCnExtremelite, cpu->threads(Algorithm::CN_EXTREMELITE_0)); +# endif + +# ifdef XMRIG_ALGO_RANDOMX + m_threads.move(kRx, cpu->threads(Algorithm::RX_0)); + m_threads.move(kRxWOW, cpu->threads(Algorithm::RX_WOW)); +# endif + + generateArgon2(); +} + + +void xmrig::CpuConfig::generateArgon2() +{ +# ifdef XMRIG_ALGO_ARGON2 + m_threads.move(kArgon2, Cpu::info()->threads(Algorithm::AR2_CHUKWA)); +# endif +} + + +void xmrig::CpuConfig::setAesMode(const rapidjson::Value &aesMode) +{ + if (aesMode.IsBool()) { + m_aes = aesMode.GetBool() ? AES_HW : AES_SOFT; + } + else { + m_aes = AES_AUTO; + } +} diff --git a/src/backend/cpu/CpuConfig.h b/src/backend/cpu/CpuConfig.h new file mode 100644 index 00000000..67010eea --- /dev/null +++ b/src/backend/cpu/CpuConfig.h @@ -0,0 +1,83 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_CPUCONFIG_H +#define XMRIG_CPUCONFIG_H + + +#include "backend/common/Threads.h" +#include "backend/cpu/CpuLaunchData.h" +#include "backend/cpu/CpuThreads.h" +#include "crypto/common/Assembly.h" + + +namespace xmrig { + + +class CpuConfig +{ +public: + enum AesMode { + AES_AUTO, + AES_HW, + AES_SOFT + }; + + CpuConfig(); + + bool isHwAES() const; + rapidjson::Value toJSON(rapidjson::Document &doc) const; + std::vector get(const Miner *miner, const Algorithm &algorithm) const; + void read(const rapidjson::Value &value, uint32_t version); + + inline bool isEnabled() const { return m_enabled; } + inline bool isHugePages() const { return m_hugePages; } + inline bool isShouldSave() const { return m_shouldSave; } + inline const Assembly &assembly() const { return m_assembly; } + inline const String &argon2Impl() const { return m_argon2Impl; } + inline const Threads &threads() const { return m_threads; } + inline int priority() const { return m_priority; } + +private: + void generate(); + void generateArgon2(); + void setAesMode(const rapidjson::Value &aesMode); + + inline void setPriority(int priority) { m_priority = (priority >= -1 && priority <= 5) ? priority : -1; } + + AesMode m_aes = AES_AUTO; + Assembly m_assembly; + bool m_enabled = true; + bool m_hugePages = true; + bool m_shouldSave = false; + int m_priority = -1; + String m_argon2Impl; + Threads m_threads; +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_CPUCONFIG_H */ diff --git a/src/backend/cpu/CpuLaunchData.cpp b/src/backend/cpu/CpuLaunchData.cpp new file mode 100644 index 00000000..3916e7d2 --- /dev/null +++ b/src/backend/cpu/CpuLaunchData.cpp @@ -0,0 +1,67 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018 Lee Clagett + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include + + +#include "backend/cpu/CpuLaunchData.h" +#include "backend/cpu/CpuConfig.h" + + +xmrig::CpuLaunchData::CpuLaunchData(const Miner *miner, const Algorithm &algorithm, const CpuConfig &config, const CpuThread &thread) : + algorithm(algorithm), + assembly(config.assembly()), + hugePages(config.isHugePages()), + hwAES(config.isHwAES()), + priority(config.priority()), + affinity(thread.affinity()), + miner(miner), + intensity(std::min(thread.intensity(), algorithm.maxIntensity())) +{ +} + + +bool xmrig::CpuLaunchData::isEqual(const CpuLaunchData &other) const +{ + return (algorithm.l3() == other.algorithm.l3() + && assembly == other.assembly + && hugePages == other.hugePages + && hwAES == other.hwAES + && intensity == other.intensity + && priority == other.priority + && affinity == other.affinity + ); +} + + +xmrig::CnHash::AlgoVariant xmrig::CpuLaunchData::av() const +{ + if (intensity <= 2) { + return static_cast(!hwAES ? (intensity + 2) : intensity); + } + + return static_cast(!hwAES ? (intensity + 5) : (intensity + 2)); +} diff --git a/src/backend/cpu/CpuLaunchData.h b/src/backend/cpu/CpuLaunchData.h new file mode 100644 index 00000000..92636bca --- /dev/null +++ b/src/backend/cpu/CpuLaunchData.h @@ -0,0 +1,71 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018 Lee Clagett + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_CPULAUNCHDATA_H +#define XMRIG_CPULAUNCHDATA_H + + +#include "crypto/cn/CnHash.h" +#include "crypto/common/Algorithm.h" +#include "crypto/common/Assembly.h" +#include "crypto/common/Nonce.h" + + +namespace xmrig { + + +class CpuConfig; +class CpuThread; +class Miner; + + +class CpuLaunchData +{ +public: + CpuLaunchData(const Miner *miner, const Algorithm &algorithm, const CpuConfig &config, const CpuThread &thread); + + bool isEqual(const CpuLaunchData &other) const; + CnHash::AlgoVariant av() const; + + 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); } + + const Algorithm algorithm; + const Assembly assembly; + const bool hugePages; + const bool hwAES; + const int priority; + const int64_t affinity; + const Miner *miner; + const uint32_t intensity; +}; + + +} // namespace xmrig + + +#endif /* XMRIG_CPULAUNCHDATA_H */ diff --git a/src/Cpu_win.cpp b/src/backend/cpu/CpuThread.cpp similarity index 51% rename from src/Cpu_win.cpp rename to src/backend/cpu/CpuThread.cpp index 2a7e74d2..660107fa 100644 --- a/src/Cpu_win.cpp +++ b/src/backend/cpu/CpuThread.cpp @@ -4,9 +4,9 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * Copyright 2018 BenDroid - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , * * 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 @@ -23,49 +23,36 @@ */ -#include +#include "backend/cpu/CpuThread.h" +#include "base/io/json/Json.h" +#include "rapidjson/document.h" -#include "CpuImpl.h" -#include "Mem.h" -#include "Cpu.h" - -void CpuImpl::init() +xmrig::CpuThread::CpuThread(const rapidjson::Value &value) { -# ifdef XMRIG_NO_LIBCPUID - SYSTEM_INFO sysinfo; - GetSystemInfo(&sysinfo); - - m_totalThreads = sysinfo.dwNumberOfProcessors; -# endif - - initCommon(); + if (value.IsArray() && value.Size() >= 2) { + m_intensity = value[0].GetUint(); + m_affinity = value[1].GetInt(); + } + else if (value.IsInt()) { + m_intensity = 0; + m_affinity = value.GetInt(); + } } -int CpuImpl::setThreadAffinity(size_t threadId, int64_t affinityMask) +rapidjson::Value xmrig::CpuThread::toJSON(rapidjson::Document &doc) const { - int cpuId = -1; - - if (affinityMask != -1L) { - cpuId = Cpu::getAssignedCpuId(threadId, affinityMask); - } else { - if (threadId+1 > Cpu::threads()/2) { - cpuId = (threadId - Cpu::threads()/2) + (threadId+1 - Cpu::threads()/2); - } else { - cpuId = threadId * 2; - } + using namespace rapidjson; + if (m_intensity == 0) { + return Value(m_affinity); } - if (cpuId >= 64) { - cpuId = -1; - } + auto &allocator = doc.GetAllocator(); - if (cpuId > -1) { - if (SetThreadAffinityMask(GetCurrentThread(), 1ULL << cpuId) == 0) { - cpuId = -1; - } - } + Value out(kArrayType); + out.PushBack(m_intensity, allocator); + out.PushBack(m_affinity, allocator); - return cpuId; + return out; } diff --git a/src/backend/cpu/CpuThread.h b/src/backend/cpu/CpuThread.h new file mode 100644 index 00000000..a56c4bd3 --- /dev/null +++ b/src/backend/cpu/CpuThread.h @@ -0,0 +1,62 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_CPUTHREAD_H +#define XMRIG_CPUTHREAD_H + + +#include "rapidjson/fwd.h" + + +namespace xmrig { + + +class CpuThread +{ +public: + inline constexpr CpuThread() {} + inline constexpr CpuThread(int64_t affinity, uint32_t intensity) : m_affinity(affinity), m_intensity(intensity) {} + + CpuThread(const rapidjson::Value &value); + + inline bool isEqual(const CpuThread &other) const { return other.m_affinity == m_affinity && other.m_intensity == m_intensity; } + inline bool isValid() const { return m_intensity <= 5; } + inline int64_t affinity() const { return m_affinity; } + inline uint32_t intensity() const { return m_intensity == 0 ? 1 : m_intensity; } + + inline bool operator!=(const CpuThread &other) const { return !isEqual(other); } + inline bool operator==(const CpuThread &other) const { return isEqual(other); } + + rapidjson::Value toJSON(rapidjson::Document &doc) const; + +private: + int64_t m_affinity = -1; + uint32_t m_intensity = 0; +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_CPUTHREAD_H */ diff --git a/src/backend/cpu/CpuThreads.cpp b/src/backend/cpu/CpuThreads.cpp new file mode 100644 index 00000000..5bd9cca9 --- /dev/null +++ b/src/backend/cpu/CpuThreads.cpp @@ -0,0 +1,146 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include + + +#include "backend/cpu/CpuThreads.h" +#include "base/io/json/Json.h" +#include "rapidjson/document.h" + + +namespace xmrig { + + +static const char *kAffinity = "affinity"; +static const char *kIntensity = "intensity"; +static const char *kThreads = "threads"; + + +static inline int64_t getAffinityMask(const rapidjson::Value &value) +{ + if (value.IsInt64()) { + return value.GetInt64(); + } + + if (value.IsString()) { + const char *arg = value.GetString(); + const char *p = strstr(arg, "0x"); + + return p ? strtoll(p, nullptr, 16) : strtoll(arg, nullptr, 10); + } + + return -1L; +} + + +static inline int64_t getAffinity(uint64_t index, int64_t affinity) +{ + if (affinity == -1L) { + return -1L; + } + + size_t idx = 0; + + for (size_t i = 0; i < 64; i++) { + if (!(static_cast(affinity) & (1ULL << i))) { + continue; + } + + if (idx == index) { + return static_cast(i); + } + + idx++; + } + + return -1L; +} + + +} + + +xmrig::CpuThreads::CpuThreads(const rapidjson::Value &value) +{ + if (value.IsArray()) { + for (auto &v : value.GetArray()) { + CpuThread thread(v); + if (thread.isValid()) { + add(std::move(thread)); + } + } + } + else if (value.IsObject()) { + uint32_t intensity = Json::getUint(value, kIntensity, 1); + const size_t threads = std::min(Json::getUint(value, kThreads), 1024); + m_affinity = getAffinityMask(Json::getValue(value, kAffinity)); + m_format = ObjectFormat; + + if (intensity < 1 || intensity > 5) { + intensity = 1; + } + + for (size_t i = 0; i < threads; ++i) { + add(getAffinity(i, m_affinity), intensity); + } + } +} + + +xmrig::CpuThreads::CpuThreads(size_t count, uint32_t intensity) +{ + m_data.reserve(count); + + for (size_t i = 0; i < count; ++i) { + add(-1, intensity); + } +} + + +rapidjson::Value xmrig::CpuThreads::toJSON(rapidjson::Document &doc) const +{ + using namespace rapidjson; + auto &allocator = doc.GetAllocator(); + + Value out; + + if (m_format == ArrayFormat) { + out.SetArray(); + + for (const CpuThread &thread : m_data) { + out.PushBack(thread.toJSON(doc), allocator); + } + } + else { + out.SetObject(); + + out.AddMember(StringRef(kIntensity), m_data.empty() ? 1 : m_data.front().intensity(), allocator); + out.AddMember(StringRef(kThreads), static_cast(m_data.size()), allocator); + out.AddMember(StringRef(kAffinity), m_affinity, allocator); + } + + return out; +} diff --git a/src/backend/cpu/CpuThreads.h b/src/backend/cpu/CpuThreads.h new file mode 100644 index 00000000..13cd725f --- /dev/null +++ b/src/backend/cpu/CpuThreads.h @@ -0,0 +1,71 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_CPUTHREADS_H +#define XMRIG_CPUTHREADS_H + + +#include + + +#include "backend/cpu/CpuThread.h" + + +namespace xmrig { + + +class CpuThreads +{ +public: + inline CpuThreads() {} + inline CpuThreads(size_t count) : m_data(count) {} + + CpuThreads(const rapidjson::Value &value); + CpuThreads(size_t count, uint32_t intensity); + + inline bool isEmpty() const { return m_data.empty(); } + inline const std::vector &data() const { return m_data; } + inline size_t count() const { return m_data.size(); } + inline void add(CpuThread &&thread) { m_data.push_back(thread); } + inline void add(int64_t affinity, uint32_t intensity) { add(CpuThread(affinity, intensity)); } + inline void reserve(size_t capacity) { m_data.reserve(capacity); } + + rapidjson::Value toJSON(rapidjson::Document &doc) const; + +private: + enum Format { + ArrayFormat, + ObjectFormat + }; + + Format m_format = ArrayFormat; + int64_t m_affinity = -1; + std::vector m_data; +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_CPUTHREADS_H */ diff --git a/src/backend/cpu/CpuWorker.cpp b/src/backend/cpu/CpuWorker.cpp new file mode 100644 index 00000000..605b0559 --- /dev/null +++ b/src/backend/cpu/CpuWorker.cpp @@ -0,0 +1,334 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018 Lee Clagett + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include +#include + + +#include "backend/cpu/CpuWorker.h" +#include "core/Miner.h" +#include "crypto/cn/CnCtx.h" +#include "crypto/cn/CryptoNight_test.h" +#include "crypto/common/Nonce.h" +#include "crypto/common/VirtualMemory.h" +#include "crypto/rx/Rx.h" +#include "crypto/rx/RxVm.h" +#include "net/JobResults.h" + + +#ifdef XMRIG_ALGO_RANDOMX +# include "crypto/randomx/randomx.h" +#endif + +namespace xmrig { + +static constexpr uint32_t kReserveCount = 4096; + +} // namespace xmrig + + + +template +xmrig::CpuWorker::CpuWorker(size_t index, const CpuLaunchData &data) : + Worker(index, data.affinity, data.priority), + m_algorithm(data.algorithm), + m_assembly(data.assembly), + m_hwAES(data.hwAES), + m_av(data.av()), + m_miner(data.miner), + m_ctx() +{ + m_memory = new VirtualMemory(m_algorithm.l3() * N, data.hugePages); +} + + +template +xmrig::CpuWorker::~CpuWorker() +{ + CnCtx::release(m_ctx, N); + delete m_memory; + +# ifdef XMRIG_ALGO_RANDOMX + delete m_vm; +# endif +} + + +#ifdef XMRIG_ALGO_RANDOMX +template +void xmrig::CpuWorker::allocateRandomX_VM() +{ + RxDataset *dataset = Rx::dataset(m_job.currentJob(), m_node); + + while (dataset == nullptr) { + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + + if (Nonce::sequence(Nonce::CPU) == 0) { + return; + } + + dataset = Rx::dataset(m_job.currentJob(), m_node); + } + + if (!m_vm) { + m_vm = new RxVm(dataset, m_memory->scratchpad(), !m_hwAES); + } +} +#endif + + +template +bool xmrig::CpuWorker::selfTest() +{ +# ifdef XMRIG_ALGO_RANDOMX + if (m_algorithm.family() == Algorithm::RANDOM_X) { + return N == 1; + } +# endif + + allocateCnCtx(); + + if (m_algorithm.family() == Algorithm::CN) { + const bool rc = verify(Algorithm::CN_0, test_output_v0) && + verify(Algorithm::CN_1, test_output_v1) && + verify(Algorithm::CN_2, test_output_v2) && + verify(Algorithm::CN_FAST, test_output_msr) && + verify(Algorithm::CN_XAO, test_output_xao) && + verify(Algorithm::CN_RTO, test_output_rto) && + verify(Algorithm::CN_HALF, test_output_half) && + verify2(Algorithm::CN_WOW, test_output_wow) && + verify2(Algorithm::CN_R, test_output_r) && + verify(Algorithm::CN_RWZ, test_output_rwz) && + verify(Algorithm::CN_ZLS, test_output_zls) && +#ifndef XMRIG_ARM + verify(Algorithm::CN_CONCEAL, test_output_conceal) && +#endif + verify(Algorithm::CN_DOUBLE, test_output_double); + +# ifdef XMRIG_ALGO_CN_GPU + if (!rc || N > 1) { + return rc; + } + + return verify(Algorithm::CN_GPU, test_output_gpu); +# else + return rc; +# endif + } + +# ifdef XMRIG_ALGO_CN_LITE + if (m_algorithm.family() == Algorithm::CN_LITE) { + return verify(Algorithm::CN_LITE_0, test_output_v0_lite) && + verify(Algorithm::CN_LITE_1, test_output_v1_lite); + } +# endif + +# ifdef XMRIG_ALGO_CN_HEAVY + if (m_algorithm.family() == Algorithm::CN_HEAVY) { + return verify(Algorithm::CN_HEAVY_0, test_output_v0_heavy) && + verify(Algorithm::CN_HEAVY_XHV, test_output_xhv_heavy) && + verify(Algorithm::CN_HEAVY_TUBE, test_output_tube_heavy); + } +# endif + +# ifdef XMRIG_ALGO_CN_PICO + if (m_algorithm.family() == Algorithm::CN_PICO) { + return verify(Algorithm::CN_PICO_0, test_output_pico_trtl); + } +# endif + +# ifdef XMRIG_ALGO_CN_EXTREMELITE + if (m_algorithm.family() == Algorithm::CN_EXTREMELITE) { + return verify(Algorithm::CN_EXTREMELITE_0, test_output_extremelite_upx2); + } +# endif + +# ifdef XMRIG_ALGO_ARGON2 + if (m_algorithm.family() == Algorithm::ARGON2) { + return verify(Algorithm::AR2_CHUKWA, argon2_chukwa_test_out) && + verify(Algorithm::AR2_WRKZ, argon2_wrkz_test_out); + } +# endif + + return false; +} + + +template +void xmrig::CpuWorker::start() +{ + while (Nonce::sequence(Nonce::CPU) > 0) { + if (Nonce::isPaused()) { + do { + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + } + while (Nonce::isPaused() && Nonce::sequence(Nonce::CPU) > 0); + + if (Nonce::sequence(Nonce::CPU) == 0) { + break; + } + + consumeJob(); + } + + while (!Nonce::isOutdated(Nonce::CPU, m_job.sequence())) { + if ((m_count & 0x7) == 0) { + storeStats(); + } + + const Job &job = m_job.currentJob(); + + if (job.algorithm().l3() != m_algorithm.l3()) { + break; + } + +# ifdef XMRIG_ALGO_RANDOMX + if (job.algorithm().family() == Algorithm::RANDOM_X) { + randomx_calculate_hash(m_vm->get(), m_job.blob(), job.size(), m_hash); + } + else +# endif + { + fn(job.algorithm())(m_job.blob(), job.size(), m_hash, m_ctx, job.height()); + } + + for (size_t i = 0; i < N; ++i) { + if (*reinterpret_cast(m_hash + (i * 32) + 24) < job.target()) { + JobResults::submit(JobResult(job, *m_job.nonce(i), m_hash + (i * 32))); + } + } + + m_job.nextRound(kReserveCount); + m_count += N; + + std::this_thread::yield(); + } + + consumeJob(); + } +} + + +template +bool xmrig::CpuWorker::verify(const Algorithm &algorithm, const uint8_t *referenceValue) +{ + cn_hash_fun func = fn(algorithm); + if (!func) { + return false; + } + + func(test_input, 76, m_hash, m_ctx, 0); + return memcmp(m_hash, referenceValue, sizeof m_hash) == 0; +} + + +template +bool xmrig::CpuWorker::verify2(const Algorithm &algorithm, const uint8_t *referenceValue) +{ + cn_hash_fun func = fn(algorithm); + if (!func) { + return false; + } + + for (size_t i = 0; i < (sizeof(cn_r_test_input) / sizeof(cn_r_test_input[0])); ++i) { + const size_t size = cn_r_test_input[i].size; + for (size_t k = 0; k < N; ++k) { + memcpy(m_job.blob() + (k * size), cn_r_test_input[i].data, size); + } + + func(m_job.blob(), size, m_hash, m_ctx, cn_r_test_input[i].height); + + for (size_t k = 0; k < N; ++k) { + if (memcmp(m_hash + k * 32, referenceValue + i * 32, sizeof m_hash / N) != 0) { + return false; + } + } + } + + return true; +} + + +namespace xmrig { + +template<> +bool CpuWorker<1>::verify2(const Algorithm &algorithm, const uint8_t *referenceValue) +{ + cn_hash_fun func = fn(algorithm); + if (!func) { + return false; + } + + for (size_t i = 0; i < (sizeof(cn_r_test_input) / sizeof(cn_r_test_input[0])); ++i) { + func(cn_r_test_input[i].data, cn_r_test_input[i].size, m_hash, m_ctx, cn_r_test_input[i].height); + + if (memcmp(m_hash, referenceValue + i * 32, sizeof m_hash) != 0) { + return false; + } + } + + return true; +} + +} // namespace xmrig + + +template +void xmrig::CpuWorker::allocateCnCtx() +{ + if (m_ctx[0] == nullptr) { + CnCtx::create(m_ctx, m_memory->scratchpad(), m_algorithm.l3(), N); + } +} + + +template +void xmrig::CpuWorker::consumeJob() +{ + m_job.add(m_miner->job(), Nonce::sequence(Nonce::CPU), kReserveCount); + +# ifdef XMRIG_ALGO_RANDOMX + if (m_job.currentJob().algorithm().family() == Algorithm::RANDOM_X) { + allocateRandomX_VM(); + } + else +# endif + { + allocateCnCtx(); + } +} + + +namespace xmrig { + +template class CpuWorker<1>; +template class CpuWorker<2>; +template class CpuWorker<3>; +template class CpuWorker<4>; +template class CpuWorker<5>; + +} // namespace xmrig + diff --git a/src/backend/cpu/CpuWorker.h b/src/backend/cpu/CpuWorker.h new file mode 100644 index 00000000..4cdd10f8 --- /dev/null +++ b/src/backend/cpu/CpuWorker.h @@ -0,0 +1,98 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018 Lee Clagett + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_CPUWORKER_H +#define XMRIG_CPUWORKER_H + + +#include "backend/common/Worker.h" +#include "backend/common/WorkerJob.h" +#include "backend/cpu/CpuLaunchData.h" +#include "base/net/stratum/Job.h" +#include "net/JobResult.h" + + +namespace xmrig { + + +class RxVm; + + +template +class CpuWorker : public Worker +{ +public: + CpuWorker(size_t index, const CpuLaunchData &data); + ~CpuWorker() override; + +protected: + bool selfTest() override; + void start() override; + + inline const VirtualMemory *memory() const override { return m_memory; } + +private: + inline cn_hash_fun fn(const Algorithm &algorithm) const { return CnHash::fn(algorithm, m_av, m_assembly); } + +# ifdef XMRIG_ALGO_RANDOMX + void allocateRandomX_VM(); +# endif + + bool verify(const Algorithm &algorithm, const uint8_t *referenceValue); + bool verify2(const Algorithm &algorithm, const uint8_t *referenceValue); + void allocateCnCtx(); + void consumeJob(); + + const Algorithm m_algorithm; + const Assembly m_assembly; + const bool m_hwAES; + const CnHash::AlgoVariant m_av; + const Miner *m_miner; + cryptonight_ctx *m_ctx[N]; + uint8_t m_hash[N * 32]; + VirtualMemory *m_memory = nullptr; + WorkerJob m_job; + +# ifdef XMRIG_ALGO_RANDOMX + RxVm *m_vm = nullptr; +# endif +}; + + +template<> +bool CpuWorker<1>::verify2(const Algorithm &algorithm, const uint8_t *referenceValue); + + +extern template class CpuWorker<1>; +extern template class CpuWorker<2>; +extern template class CpuWorker<3>; +extern template class CpuWorker<4>; +extern template class CpuWorker<5>; + + +} // namespace xmrig + + +#endif /* XMRIG_CPUWORKER_H */ diff --git a/src/backend/cpu/cpu.cmake b/src/backend/cpu/cpu.cmake new file mode 100644 index 00000000..b6c8915b --- /dev/null +++ b/src/backend/cpu/cpu.cmake @@ -0,0 +1,79 @@ +set(HEADERS_BACKEND_CPU + src/backend/cpu/Cpu.h + src/backend/cpu/CpuBackend.h + src/backend/cpu/CpuConfig.h + src/backend/cpu/CpuLaunchData.cpp + src/backend/cpu/CpuThread.h + src/backend/cpu/CpuThreads.h + src/backend/cpu/CpuWorker.h + src/backend/cpu/interfaces/ICpuInfo.h + ) + +set(SOURCES_BACKEND_CPU + src/backend/cpu/Cpu.cpp + src/backend/cpu/CpuBackend.cpp + src/backend/cpu/CpuConfig.cpp + src/backend/cpu/CpuLaunchData.h + src/backend/cpu/CpuThread.cpp + src/backend/cpu/CpuThreads.cpp + src/backend/cpu/CpuWorker.cpp + ) + + +if (WITH_HWLOC) + if (CMAKE_CXX_COMPILER_ID MATCHES MSVC) + add_subdirectory(src/3rdparty/hwloc) + include_directories(src/3rdparty/hwloc/include) + set(CPUID_LIB hwloc) + else() + find_package(HWLOC REQUIRED) + include_directories(${HWLOC_INCLUDE_DIR}) + set(CPUID_LIB ${HWLOC_LIBRARY}) + endif() + + set(WITH_LIBCPUID OFF) + + remove_definitions(/DXMRIG_FEATURE_LIBCPUID) + add_definitions(/DXMRIG_FEATURE_HWLOC) + + if (HWLOC_DEBUG) + add_definitions(/DXMRIG_HWLOC_DEBUG) + endif() + + set(SOURCES_CPUID + src/backend/cpu/platform/BasicCpuInfo.h + src/backend/cpu/platform/HwlocCpuInfo.cpp + src/backend/cpu/platform/HwlocCpuInfo.h + ) +elseif (WITH_LIBCPUID) + set(WITH_HWLOC OFF) + + add_subdirectory(src/3rdparty/libcpuid) + include_directories(src/3rdparty/libcpuid) + + add_definitions(/DXMRIG_FEATURE_LIBCPUID) + remove_definitions(/DXMRIG_FEATURE_HWLOC) + + set(CPUID_LIB cpuid) + set(SOURCES_CPUID + src/backend/cpu/platform/AdvancedCpuInfo.cpp + src/backend/cpu/platform/AdvancedCpuInfo.h + ) +else() + remove_definitions(/DXMRIG_FEATURE_LIBCPUID) + remove_definitions(/DXMRIG_FEATURE_HWLOC) + + set(CPUID_LIB "") + set(SOURCES_CPUID + src/backend/cpu/platform/BasicCpuInfo.h + ) +endif() + + +if (NOT WITH_LIBCPUID) + if (XMRIG_ARM) + set(SOURCES_CPUID ${SOURCES_CPUID} src/backend/cpu/platform/BasicCpuInfo_arm.cpp) + else() + set(SOURCES_CPUID ${SOURCES_CPUID} src/backend/cpu/platform/BasicCpuInfo.cpp) + endif() +endif() diff --git a/src/backend/cpu/interfaces/ICpuInfo.h b/src/backend/cpu/interfaces/ICpuInfo.h new file mode 100644 index 00000000..9bc3b11a --- /dev/null +++ b/src/backend/cpu/interfaces/ICpuInfo.h @@ -0,0 +1,66 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2019 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig + * + * 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 . + */ + +#ifndef XMRIG_CPUINFO_H +#define XMRIG_CPUINFO_H + + +#include "backend/cpu/CpuThreads.h" +#include "crypto/common/Assembly.h" +#include "crypto/common/Algorithm.h" + + +namespace xmrig { + + +class ICpuInfo +{ +public: + virtual ~ICpuInfo() = default; + +# if defined(__x86_64__) || defined(_M_AMD64) || defined (__arm64__) || defined (__aarch64__) + inline constexpr static bool isX64() { return true; } +# else + inline constexpr static bool isX64() { return false; } +# endif + + virtual Assembly::Id assembly() const = 0; + virtual bool hasAES() const = 0; + virtual bool hasAVX2() const = 0; + virtual const char *backend() const = 0; + virtual const char *brand() const = 0; + virtual CpuThreads threads(const Algorithm &algorithm) const = 0; + virtual size_t cores() const = 0; + virtual size_t L2() const = 0; + virtual size_t L3() const = 0; + virtual size_t nodes() const = 0; + virtual size_t packages() const = 0; + virtual size_t threads() const = 0; +}; + + +} /* namespace xmrig */ + + +#endif // XMRIG_CPUINFO_H diff --git a/src/backend/cpu/platform/AdvancedCpuInfo.cpp b/src/backend/cpu/platform/AdvancedCpuInfo.cpp new file mode 100644 index 00000000..9edfdff3 --- /dev/null +++ b/src/backend/cpu/platform/AdvancedCpuInfo.cpp @@ -0,0 +1,163 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2019 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig + * + * 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 . + */ + +#include +#include +#include +#include +#include + + +#include "3rdparty/libcpuid/libcpuid.h" +#include "backend/cpu/platform/AdvancedCpuInfo.h" + + +namespace xmrig { + + +static inline void cpu_brand_string(char out[64], const char *in) { + size_t pos = 0; + const size_t size = strlen(in); + + for (size_t i = 0; i < size; ++i) { + if (in[i] == ' ' && ((pos > 0 && out[pos - 1] == ' ') || pos == 0)) { + continue; + } + + out[pos++] = in[i]; + } + + if (pos > 0 && out[pos - 1] == ' ') { + out[pos - 1] = '\0'; + } +} + + +} // namespace xmrig + + +xmrig::AdvancedCpuInfo::AdvancedCpuInfo() : + m_brand() +{ + struct cpu_raw_data_t raw = {}; + struct cpu_id_t data = {}; + + cpuid_get_raw_data(&raw); + cpu_identify(&raw, &data); + + cpu_brand_string(m_brand, data.brand_str); + snprintf(m_backend, sizeof m_backend, "libcpuid/%s", cpuid_lib_version()); + + m_threads = static_cast(data.total_logical_cpus); + m_packages = std::max(threads() / static_cast(data.num_logical_cpus), 1); + m_cores = static_cast(data.num_cores) * m_packages; + m_L3 = data.l3_cache > 0 ? static_cast(data.l3_cache) * m_packages : 0; + + const size_t l2 = static_cast(data.l2_cache); + + // Workaround for AMD CPUs https://github.com/anrieff/libcpuid/issues/97 + if (data.vendor == VENDOR_AMD && data.ext_family >= 0x15 && data.ext_family < 0x17) { + m_L2 = l2 * (cores() / 2) * m_packages; + m_L2_exclusive = true; + } + // Workaround for Intel Pentium Dual-Core, Core Duo, Core 2 Duo, Core 2 Quad and their Xeon homologue + // These processors have L2 cache shared by 2 cores. + else if (data.vendor == VENDOR_INTEL && data.ext_family == 0x06 && (data.ext_model == 0x0E || data.ext_model == 0x0F || data.ext_model == 0x17)) { + size_t l2_count_per_socket = cores() > 1 ? cores() / 2 : 1; + m_L2 = data.l2_cache > 0 ? l2 * l2_count_per_socket * m_packages : 0; + } + else{ + m_L2 = data.l2_cache > 0 ? l2 * cores() * m_packages : 0; + } + + m_L2 *= 1024; + m_L3 *= 1024; + + if (data.flags[CPU_FEATURE_AES]) { + m_aes = true; + + if (data.vendor == VENDOR_AMD) { + m_assembly = (data.ext_family >= 23) ? Assembly::RYZEN : Assembly::BULLDOZER; + } + else if (data.vendor == VENDOR_INTEL) { + m_assembly = Assembly::INTEL; + } + } + + m_avx2 = data.flags[CPU_FEATURE_AVX2] && data.flags[CPU_FEATURE_OSXSAVE]; +} + + +xmrig::CpuThreads xmrig::AdvancedCpuInfo::threads(const Algorithm &algorithm) const +{ + if (threads() == 1) { + return 1; + } + +# ifdef XMRIG_ALGO_CN_GPU + if (algorithm == Algorithm::CN_GPU) { + return CpuThreads(threads()); + } +# endif + + size_t cache = 0; + size_t count = 0; + + if (m_L3) { + cache = m_L2_exclusive ? (m_L2 + m_L3) : m_L3; + } + else { + cache = m_L2; + } + + if (cache) { + const size_t memory = algorithm.l3(); + assert(memory > 0); + + count = cache / memory; + + if (cache % memory >= memory / 2) { + count++; + } + } + else { + count = threads() / 2; + } + + uint32_t intensity = algorithm.maxIntensity() == 1 ? 0 : 1; + +# ifdef XMRIG_ALGO_CN_PICO + if (algorithm == Algorithm::CN_PICO_0 && (count / cores()) >= 2) { + intensity = 2; + } +# endif + +# ifdef XMRIG_ALGO_CN_EXTREMELITE + if (algorithm == Algorithm::CN_EXTREMELITE_0 && (count / cores()) >= 2) { + intensity = 2; + } +# endif + + return CpuThreads(std::max(std::min(count, threads()), 1), intensity); +} diff --git a/src/backend/cpu/platform/AdvancedCpuInfo.h b/src/backend/cpu/platform/AdvancedCpuInfo.h new file mode 100644 index 00000000..51b84c9f --- /dev/null +++ b/src/backend/cpu/platform/AdvancedCpuInfo.h @@ -0,0 +1,73 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2019 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig + * + * 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 . + */ + +#ifndef XMRIG_ADVANCEDCPUINFO_H +#define XMRIG_ADVANCEDCPUINFO_H + + +#include "backend/cpu/interfaces/ICpuInfo.h" + + +namespace xmrig { + + +class AdvancedCpuInfo : public ICpuInfo +{ +public: + AdvancedCpuInfo(); + +protected: + CpuThreads threads(const Algorithm &algorithm) const override; + + inline Assembly::Id assembly() const override { return m_assembly; } + inline bool hasAES() const override { return m_aes; } + inline bool hasAVX2() const override { return m_avx2; } + inline const char *backend() const override { return m_backend; } + inline const char *brand() const override { return m_brand; } + inline size_t cores() const override { return m_cores; } + inline size_t L2() const override { return m_L2; } + inline size_t L3() const override { return m_L3; } + inline size_t nodes() const override { return 0; } + inline size_t packages() const override { return m_packages; } + inline size_t threads() const override { return m_threads; } + +private: + Assembly m_assembly; + bool m_aes = false; + bool m_avx2 = false; + bool m_L2_exclusive = false; + char m_backend[32]; + char m_brand[64 + 5]; + size_t m_cores = 0; + size_t m_L2 = 0; + size_t m_L3 = 0; + size_t m_packages = 1; + size_t m_threads = 0; +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_ADVANCEDCPUINFO_H */ diff --git a/src/backend/cpu/platform/BasicCpuInfo.cpp b/src/backend/cpu/platform/BasicCpuInfo.cpp new file mode 100644 index 00000000..31e2faa6 --- /dev/null +++ b/src/backend/cpu/platform/BasicCpuInfo.cpp @@ -0,0 +1,237 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2019 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig + * + * 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 . + */ + +#include +#include +#include + + +#ifdef _MSC_VER +# include +#else +# include +#endif + +#ifndef bit_AES +# define bit_AES (1 << 25) +#endif + +#ifndef bit_OSXSAVE +# define bit_OSXSAVE (1 << 27) +#endif + +#ifndef bit_AVX2 +# define bit_AVX2 (1 << 5) +#endif + + +#include "backend/cpu/platform/BasicCpuInfo.h" +#include "crypto/common/Assembly.h" + + +#define VENDOR_ID (0) +#define PROCESSOR_INFO (1) +#define EXTENDED_FEATURES (7) +#define PROCESSOR_BRAND_STRING_1 (0x80000002) +#define PROCESSOR_BRAND_STRING_2 (0x80000003) +#define PROCESSOR_BRAND_STRING_3 (0x80000004) + +#define EAX_Reg (0) +#define EBX_Reg (1) +#define ECX_Reg (2) +#define EDX_Reg (3) + + +namespace xmrig { + + +static inline void cpuid(uint32_t level, int32_t output[4]) +{ + memset(output, 0, sizeof(int32_t) * 4); + +# ifdef _MSC_VER + __cpuid(output, static_cast(level)); +# else + __cpuid_count(level, 0, output[0], output[1], output[2], output[3]); +# endif +} + + +static void cpu_brand_string(char out[64 + 6]) { + int32_t cpu_info[4] = { 0 }; + char buf[64] = { 0 }; + + cpuid(VENDOR_ID, cpu_info); + + if (cpu_info[EAX_Reg] >= 4) { + for (uint32_t i = 0; i < 4; i++) { + cpuid(0x80000002 + i, cpu_info); + memcpy(buf + (i * 16), cpu_info, sizeof(cpu_info)); + } + } + + size_t pos = 0; + const size_t size = strlen(buf); + + for (size_t i = 0; i < size; ++i) { + if (buf[i] == ' ' && ((pos > 0 && out[pos - 1] == ' ') || pos == 0)) { + continue; + } + + out[pos++] = buf[i]; + } + + if (pos > 0 && out[pos - 1] == ' ') { + out[pos - 1] = '\0'; + } +} + + +static bool has_feature(uint32_t level, uint32_t reg, int32_t bit) +{ + int32_t cpu_info[4] = { 0 }; + cpuid(level, cpu_info); + + return (cpu_info[reg] & bit) != 0; +} + + +static inline int32_t get_masked(int32_t val, int32_t h, int32_t l) +{ + val &= (0x7FFFFFFF >> (31 - (h - l))) << l; + return val >> l; +} + + +static inline bool has_aes_ni() +{ + return has_feature(PROCESSOR_INFO, ECX_Reg, bit_AES); +} + + +static inline bool has_avx2() +{ + return has_feature(EXTENDED_FEATURES, EBX_Reg, bit_AVX2) && has_feature(PROCESSOR_INFO, ECX_Reg, bit_OSXSAVE); +} + + +} // namespace xmrig + + +xmrig::BasicCpuInfo::BasicCpuInfo() : + m_brand(), + m_threads(std::thread::hardware_concurrency()), + m_assembly(Assembly::NONE), + m_aes(has_aes_ni()), + m_avx2(has_avx2()) +{ + cpu_brand_string(m_brand); + +# ifdef XMRIG_FEATURE_ASM + if (hasAES()) { + char vendor[13] = { 0 }; + int32_t data[4] = { 0 }; + + cpuid(VENDOR_ID, data); + + memcpy(vendor + 0, &data[1], 4); + memcpy(vendor + 4, &data[3], 4); + memcpy(vendor + 8, &data[2], 4); + + if (memcmp(vendor, "AuthenticAMD", 12) == 0) { + cpuid(PROCESSOR_INFO, data); + const int32_t family = get_masked(data[EAX_Reg], 12, 8) + get_masked(data[EAX_Reg], 28, 20); + + m_assembly = family >= 23 ? Assembly::RYZEN : Assembly::BULLDOZER; + } + else { + m_assembly = Assembly::INTEL; + } + } +# endif +} + + +const char *xmrig::BasicCpuInfo::backend() const +{ + return "basic"; +} + + +xmrig::CpuThreads xmrig::BasicCpuInfo::threads(const Algorithm &algorithm) const +{ + const size_t count = std::thread::hardware_concurrency(); + + if (count == 1) { + return 1; + } + +# ifdef XMRIG_ALGO_CN_GPU + if (algorithm == Algorithm::CN_GPU) { + return count; + } +# endif + +# ifdef XMRIG_ALGO_CN_LITE + if (algorithm.family() == Algorithm::CN_LITE) { + return CpuThreads(count, 1); + } +# endif + +# ifdef XMRIG_ALGO_CN_PICO + if (algorithm.family() == Algorithm::CN_PICO) { + return CpuThreads(count, 2); + } +# endif + +# ifdef XMRIG_ALGO_CN_EXTREMELITE + if (algorithm.family() == Algorithm::CN_EXTREMELITE) { + return CpuThreads(count, 2); + } +# endif + +# ifdef XMRIG_ALGO_CN_HEAVY + if (algorithm.family() == Algorithm::CN_HEAVY) { + return CpuThreads(std::max(count / 4, 1), 1); + } +# endif + +# ifdef XMRIG_ALGO_RANDOMX + if (algorithm.family() == Algorithm::RANDOM_X) { + if (algorithm == Algorithm::RX_WOW) { + return count; + } + + return std::max(count / 2, 1); + } +# endif + +# ifdef XMRIG_ALGO_ARGON2 + if (algorithm.family() == Algorithm::ARGON2) { + return count; + } +# endif + + return CpuThreads(std::max(count / 2, 1), 1); +} diff --git a/src/backend/cpu/platform/BasicCpuInfo.h b/src/backend/cpu/platform/BasicCpuInfo.h new file mode 100644 index 00000000..6cf25714 --- /dev/null +++ b/src/backend/cpu/platform/BasicCpuInfo.h @@ -0,0 +1,69 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2019 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig + * + * 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 . + */ + +#ifndef XMRIG_BASICCPUINFO_H +#define XMRIG_BASICCPUINFO_H + + +#include "backend/cpu/interfaces/ICpuInfo.h" + + +namespace xmrig { + + +class BasicCpuInfo : public ICpuInfo +{ +public: + BasicCpuInfo(); + +protected: + const char *backend() const override; + CpuThreads threads(const Algorithm &algorithm) const override; + + inline Assembly::Id assembly() const override { return m_assembly; } + inline bool hasAES() const override { return m_aes; } + inline bool hasAVX2() const override { return m_avx2; } + inline const char *brand() const override { return m_brand; } + inline size_t cores() const override { return 0; } + inline size_t L2() const override { return 0; } + inline size_t L3() const override { return 0; } + inline size_t nodes() const override { return 0; } + inline size_t packages() const override { return 1; } + inline size_t threads() const override { return m_threads; } + +protected: + char m_brand[64 + 6]; + size_t m_threads; + +private: + Assembly m_assembly; + bool m_aes; + const bool m_avx2; +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_BASICCPUINFO_H */ diff --git a/src/api/Api.cpp b/src/backend/cpu/platform/BasicCpuInfo_arm.cpp similarity index 51% rename from src/api/Api.cpp rename to src/backend/cpu/platform/BasicCpuInfo_arm.cpp index 771c1ae9..b241e197 100644 --- a/src/api/Api.cpp +++ b/src/backend/cpu/platform/BasicCpuInfo_arm.cpp @@ -4,8 +4,9 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2017-2019 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig * * 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,66 +23,47 @@ */ #include +#include -#include - -#include "api/Api.h" -#include "api/ApiState.h" +#if __ARM_FEATURE_CRYPTO && !defined(__APPLE__) +# include +# include +#endif -ApiState *Api::m_state = nullptr; -uv_mutex_t Api::m_mutex; +#include "backend/cpu/platform/BasicCpuInfo.h" -bool Api::start() +xmrig::BasicCpuInfo::BasicCpuInfo() : + m_brand(), + m_threads(std::thread::hardware_concurrency()), + m_aes(false), + m_avx2(false) { - uv_mutex_init(&m_mutex); - m_state = new ApiState(); +# ifdef XMRIG_ARMv8 + memcpy(m_brand, "ARMv8", 5); +# else + memcpy(m_brand, "ARMv7", 5); +# endif - return true; +# if __ARM_FEATURE_CRYPTO +# if !defined(__APPLE__) + m_aes = getauxval(AT_HWCAP) & HWCAP_AES; +# else + m_aes = true; +# endif +# endif } -void Api::release() +const char *xmrig::BasicCpuInfo::backend() const { - delete m_state; + return "basic_arm"; } -char *Api::get(const char *url, int *status) +xmrig::CpuThreads xmrig::BasicCpuInfo::threads(const Algorithm &) const { - if (!m_state) { - return nullptr; - } - - uv_mutex_lock(&m_mutex); - char *buf = m_state->get(url, status); - uv_mutex_unlock(&m_mutex); - - return buf; -} - - -void Api::tick(const Hashrate *hashrate) -{ - if (!m_state) { - return; - } - - uv_mutex_lock(&m_mutex); - m_state->tick(hashrate); - uv_mutex_unlock(&m_mutex); -} - - -void Api::tick(const NetworkState &network) -{ - if (!m_state) { - return; - } - - uv_mutex_lock(&m_mutex); - m_state->tick(network); - uv_mutex_unlock(&m_mutex); + return CpuThreads(threads()); } diff --git a/src/backend/cpu/platform/HwlocCpuInfo.cpp b/src/backend/cpu/platform/HwlocCpuInfo.cpp new file mode 100644 index 00000000..f318b8f7 --- /dev/null +++ b/src/backend/cpu/platform/HwlocCpuInfo.cpp @@ -0,0 +1,343 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2019 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig + * + * 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 . + */ + + +#ifdef XMRIG_HWLOC_DEBUG +# include +#endif + + +#include +#include + + +#if HWLOC_API_VERSION < 0x00010b00 +# define HWLOC_OBJ_PACKAGE HWLOC_OBJ_SOCKET +# define HWLOC_OBJ_NUMANODE HWLOC_OBJ_NODE +#endif + + +#include "backend/cpu/platform/HwlocCpuInfo.h" +#include "base/io/log/Log.h" + + +namespace xmrig { + + +std::vector HwlocCpuInfo::m_nodeIndexes; +uint32_t HwlocCpuInfo::m_features = 0; + + +static inline bool isCacheObject(hwloc_obj_t obj) +{ +# if HWLOC_API_VERSION >= 0x20000 + return hwloc_obj_type_is_cache(obj->type); +# else + return obj->type == HWLOC_OBJ_CACHE; +# endif +} + + +template +static inline void findCache(hwloc_obj_t obj, unsigned min, unsigned max, func lambda) +{ + for (size_t i = 0; i < obj->arity; i++) { + if (isCacheObject(obj->children[i])) { + const unsigned depth = obj->children[i]->attr->cache.depth; + if (depth < min || depth > max) { + continue; + } + + lambda(obj->children[i]); + } + + findCache(obj->children[i], min, max, lambda); + } +} + + +template +static inline void findByType(hwloc_obj_t obj, hwloc_obj_type_t type, func lambda) +{ + for (size_t i = 0; i < obj->arity; i++) { + if (obj->children[i]->type == type) { + lambda(obj->children[i]); + } + else { + findByType(obj->children[i], type, lambda); + } + } +} + + +static inline std::vector findByType(hwloc_obj_t obj, hwloc_obj_type_t type) +{ + std::vector out; + findByType(obj, type, [&out](hwloc_obj_t found) { out.emplace_back(found); }); + + return out; +} + + +static inline size_t countByType(hwloc_topology_t topology, hwloc_obj_type_t type) +{ + const int count = hwloc_get_nbobjs_by_type(topology, type); + + return count > 0 ? static_cast(count) : 0; +} + + +static inline size_t countByType(hwloc_obj_t obj, hwloc_obj_type_t type) +{ + size_t count = 0; + findByType(obj, type, [&count](hwloc_obj_t) { count++; }); + + return count; +} + + +static inline bool isCacheExclusive(hwloc_obj_t obj) +{ + const char *value = hwloc_obj_get_info_by_name(obj, "Inclusive"); + return value == nullptr || value[0] != '1'; +} + + +} // namespace xmrig + + +xmrig::HwlocCpuInfo::HwlocCpuInfo() : BasicCpuInfo(), + m_backend(), + m_cache() +{ + m_threads = 0; + + hwloc_topology_init(&m_topology); + hwloc_topology_load(m_topology); + +# ifdef XMRIG_HWLOC_DEBUG +# if defined(UV_VERSION_HEX) && UV_VERSION_HEX >= 0x010c00 + { + char env[520] = { 0 }; + size_t size = sizeof(env); + + if (uv_os_getenv("HWLOC_XMLFILE", env, &size) == 0) { + printf("use HWLOC XML file: \"%s\"\n", env); + } + } +# endif + + const std::vector packages = findByType(hwloc_get_root_obj(m_topology), HWLOC_OBJ_PACKAGE); + if (packages.size()) { + const char *value = hwloc_obj_get_info_by_name(packages[0], "CPUModel"); + if (value) { + strncpy(m_brand, value, 64); + } + } +# endif + + hwloc_obj_t root = hwloc_get_root_obj(m_topology); + +# if HWLOC_API_VERSION >= 0x00010b00 + const char *version = hwloc_obj_get_info_by_name(root, "hwlocVersion"); + if (version) { + snprintf(m_backend, sizeof m_backend, "hwloc/%s", version); + } + else +# endif + { + snprintf(m_backend, sizeof m_backend, "hwloc/%d.%d.%d", + (HWLOC_API_VERSION>>16)&0x000000ff, + (HWLOC_API_VERSION>>8 )&0x000000ff, + (HWLOC_API_VERSION )&0x000000ff + ); + } + + findCache(root, 2, 3, [this](hwloc_obj_t found) { this->m_cache[found->attr->cache.depth] += found->attr->cache.size; }); + + m_threads = countByType(m_topology, HWLOC_OBJ_PU); + m_cores = countByType(m_topology, HWLOC_OBJ_CORE); + m_nodes = std::max(countByType(m_topology, HWLOC_OBJ_NUMANODE), 1); + m_packages = countByType(m_topology, HWLOC_OBJ_PACKAGE); + + if (m_nodes > 1) { + if (hwloc_topology_get_support(m_topology)->membind->set_thisthread_membind) { + m_features |= SET_THISTHREAD_MEMBIND; + } + + m_nodeIndexes.reserve(m_nodes); + hwloc_obj_t node = nullptr; + + while ((node = hwloc_get_next_obj_by_type(m_topology, HWLOC_OBJ_NUMANODE, node)) != nullptr) { + m_nodeIndexes.emplace_back(node->os_index); + } + } +} + + +xmrig::HwlocCpuInfo::~HwlocCpuInfo() +{ + hwloc_topology_destroy(m_topology); +} + + +xmrig::CpuThreads xmrig::HwlocCpuInfo::threads(const Algorithm &algorithm) const +{ + if (L2() == 0 && L3() == 0) { + return BasicCpuInfo::threads(algorithm); + } + + const unsigned depth = L3() > 0 ? 3 : 2; + + CpuThreads threads; + threads.reserve(m_threads); + + std::vector caches; + caches.reserve(16); + + findCache(hwloc_get_root_obj(m_topology), depth, depth, [&caches](hwloc_obj_t found) { caches.emplace_back(found); }); + + for (hwloc_obj_t cache : caches) { + processTopLevelCache(cache, algorithm, threads); + } + + if (threads.isEmpty()) { + LOG_WARN("hwloc auto configuration for algorithm \"%s\" failed.", algorithm.shortName()); + + return BasicCpuInfo::threads(algorithm); + } + + return threads; +} + + +void xmrig::HwlocCpuInfo::processTopLevelCache(hwloc_obj_t cache, const Algorithm &algorithm, CpuThreads &threads) const +{ + constexpr size_t oneMiB = 1024u * 1024u; + + size_t PUs = countByType(cache, HWLOC_OBJ_PU); + if (PUs == 0) { + return; + } + + std::vector cores; + cores.reserve(m_cores); + findByType(cache, HWLOC_OBJ_CORE, [&cores](hwloc_obj_t found) { cores.emplace_back(found); }); + + size_t L3 = cache->attr->cache.size; + const bool L3_exclusive = isCacheExclusive(cache); + size_t L2 = 0; + int L2_associativity = 0; + size_t extra = 0; + const size_t scratchpad = algorithm.l3(); + uint32_t intensity = algorithm.maxIntensity() == 1 ? 0 : 1; + + if (cache->attr->cache.depth == 3) { + for (size_t i = 0; i < cache->arity; ++i) { + hwloc_obj_t l2 = cache->children[i]; + if (!isCacheObject(l2) || l2->attr == nullptr) { + continue; + } + + L2 += l2->attr->cache.size; + L2_associativity = l2->attr->cache.associativity; + + if (L3_exclusive && l2->attr->cache.size >= scratchpad) { + extra += scratchpad; + } + } + } + + if (scratchpad == 2 * oneMiB) { + if (L2 && (cores.size() * oneMiB) == L2 && L2_associativity == 16 && L3 >= L2) { + L3 = L2; + extra = L2; + } + } + + size_t cacheHashes = ((L3 + extra) + (scratchpad / 2)) / scratchpad; + +# ifdef XMRIG_ALGO_CN_PICO + if (algorithm == Algorithm::CN_PICO_0 && (cacheHashes / PUs) >= 2) { + intensity = 2; + } +# endif + +# ifdef XMRIG_ALGO_CN_EXTREMELITE + if (algorithm == Algorithm::CN_EXTREMELITE_0 && (cacheHashes / PUs) >= 2) { + intensity = 2; + } +# endif + +# ifdef XMRIG_ALGO_CN_GPU + if (algorithm == Algorithm::CN_GPU) { + cacheHashes = PUs; + } +# endif + +# ifdef XMRIG_ALGO_RANDOMX + if (extra == 0 && algorithm.l2() > 0) { + cacheHashes = std::min(std::max(L2 / algorithm.l2(), cores.size()), cacheHashes); + } +# endif + + if (cacheHashes >= PUs) { + for (hwloc_obj_t core : cores) { + const std::vector units = findByType(core, HWLOC_OBJ_PU); + for (hwloc_obj_t pu : units) { + threads.add(pu->os_index, intensity); + } + } + + return; + } + + size_t pu_id = 0; + while (cacheHashes > 0 && PUs > 0) { + bool allocated_pu = false; + + for (hwloc_obj_t core : cores) { + const std::vector units = findByType(core, HWLOC_OBJ_PU); + if (units.size() <= pu_id) { + continue; + } + + cacheHashes--; + PUs--; + + allocated_pu = true; + threads.add(units[pu_id]->os_index, intensity); + + if (cacheHashes == 0) { + break; + } + } + + if (!allocated_pu) { + break; + } + + pu_id++; + } +} diff --git a/src/backend/cpu/platform/HwlocCpuInfo.h b/src/backend/cpu/platform/HwlocCpuInfo.h new file mode 100644 index 00000000..340864f5 --- /dev/null +++ b/src/backend/cpu/platform/HwlocCpuInfo.h @@ -0,0 +1,81 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2019 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig + * + * 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 . + */ + +#ifndef XMRIG_HWLOCCPUINFO_H +#define XMRIG_HWLOCCPUINFO_H + + +#include "backend/cpu/platform/BasicCpuInfo.h" + + +typedef struct hwloc_obj *hwloc_obj_t; +typedef struct hwloc_topology *hwloc_topology_t; + + +namespace xmrig { + + +class HwlocCpuInfo : public BasicCpuInfo +{ +public: + enum Feature : uint32_t { + SET_THISTHREAD_MEMBIND = 1 + }; + + + HwlocCpuInfo(); + ~HwlocCpuInfo() override; + + static inline bool has(Feature feature) { return m_features & feature; } + static inline const std::vector &nodeIndexes() { return m_nodeIndexes; } + +protected: + CpuThreads threads(const Algorithm &algorithm) const override; + + inline const char *backend() const override { return m_backend; } + inline size_t cores() const override { return m_cores; } + inline size_t L2() const override { return m_cache[2]; } + inline size_t L3() const override { return m_cache[3]; } + inline size_t nodes() const override { return m_nodes; } + inline size_t packages() const override { return m_packages; } + +private: + void processTopLevelCache(hwloc_obj_t obj, const Algorithm &algorithm, CpuThreads &threads) const; + + static std::vector m_nodeIndexes; + static uint32_t m_features; + + char m_backend[20]; + hwloc_topology_t m_topology; + size_t m_cache[5]; + size_t m_cores = 0; + size_t m_nodes = 0; + size_t m_packages = 0; +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_HWLOCCPUINFO_H */ diff --git a/src/base/api/Api.cpp b/src/base/api/Api.cpp new file mode 100644 index 00000000..f358ac4b --- /dev/null +++ b/src/base/api/Api.cpp @@ -0,0 +1,208 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include + + +#ifndef _WIN32 +# include +#endif + + +#include "3rdparty/http-parser/http_parser.h" +#include "base/api/Api.h" +#include "base/api/interfaces/IApiListener.h" +#include "base/api/requests/HttpApiRequest.h" +#include "base/kernel/Base.h" +#include "base/tools/Buffer.h" +#include "base/tools/Chrono.h" +#include "core/config/Config.h" +#include "core/Controller.h" +#include "crypto/common/keccak.h" +#include "version.h" + + +#ifdef XMRIG_FEATURE_HTTP +# include "base/api/Httpd.h" +#endif + + +xmrig::Api::Api(Base *base) : + m_base(base), + m_id(), + m_workerId(), + m_timestamp(Chrono::currentMSecsSinceEpoch()), + m_httpd(nullptr) +{ + base->addListener(this); + + genId(base->config()->apiId()); +} + + +xmrig::Api::~Api() +{ +# ifdef XMRIG_FEATURE_HTTP + delete m_httpd; +# endif +} + + +void xmrig::Api::request(const HttpData &req) +{ + HttpApiRequest request(req, m_base->config()->http().isRestricted()); + + exec(request); +} + + +void xmrig::Api::start() +{ + genWorkerId(m_base->config()->apiWorkerId()); + +# ifdef XMRIG_FEATURE_HTTP + m_httpd = new Httpd(m_base); + m_httpd->start(); +# endif +} + + +void xmrig::Api::stop() +{ +# ifdef XMRIG_FEATURE_HTTP + m_httpd->stop(); +# endif +} + + +void xmrig::Api::onConfigChanged(Config *config, Config *previousConfig) +{ + if (config->apiId() != previousConfig->apiId()) { + genId(config->apiId()); + } + + if (config->apiWorkerId() != previousConfig->apiWorkerId()) { + genWorkerId(config->apiWorkerId()); + } +} + + +void xmrig::Api::exec(IApiRequest &request) +{ + using namespace rapidjson; + + if (request.type() == IApiRequest::REQ_SUMMARY) { + auto &allocator = request.doc().GetAllocator(); + + request.accept(); + request.reply().AddMember("id", StringRef(m_id), allocator); + request.reply().AddMember("worker_id", StringRef(m_workerId), allocator); + request.reply().AddMember("uptime", (Chrono::currentMSecsSinceEpoch() - m_timestamp) / 1000, allocator); + + Value features(kArrayType); +# ifdef XMRIG_FEATURE_API + features.PushBack("api", allocator); +# endif +# ifdef XMRIG_FEATURE_ASM + features.PushBack("asm", allocator); +# endif +# ifdef XMRIG_FEATURE_HTTP + features.PushBack("http", allocator); +# endif +# ifdef XMRIG_FEATURE_LIBCPUID + features.PushBack("cpuid", allocator); +# endif +# ifdef XMRIG_FEATURE_HWLOC + features.PushBack("hwloc", allocator); +# endif +# ifdef XMRIG_FEATURE_TLS + features.PushBack("tls", allocator); +# endif + request.reply().AddMember("features", features, allocator); + } + + for (IApiListener *listener : m_listeners) { + listener->onRequest(request); + + if (request.isDone()) { + return; + } + } + + request.done(request.isNew() ? HTTP_STATUS_NOT_FOUND : HTTP_STATUS_OK); +} + + +void xmrig::Api::genId(const String &id) +{ + memset(m_id, 0, sizeof(m_id)); + + if (id.size() > 0) { + strncpy(m_id, id.data(), sizeof(m_id) - 1); + return; + } + + uv_interface_address_t *interfaces; + int count = 0; + + if (uv_interface_addresses(&interfaces, &count) < 0) { + return; + } + + for (int i = 0; i < count; i++) { + if (!interfaces[i].is_internal && interfaces[i].address.address4.sin_family == AF_INET) { + uint8_t hash[200]; + const size_t addrSize = sizeof(interfaces[i].phys_addr); + const size_t inSize = strlen(APP_KIND) + addrSize + sizeof(uint16_t); + const uint16_t port = static_cast(m_base->config()->http().port()); + + uint8_t *input = new uint8_t[inSize](); + memcpy(input, &port, sizeof(uint16_t)); + memcpy(input + sizeof(uint16_t), interfaces[i].phys_addr, addrSize); + memcpy(input + sizeof(uint16_t) + addrSize, APP_KIND, strlen(APP_KIND)); + + keccak(input, inSize, hash); + Buffer::toHex(hash, 8, m_id); + + delete [] input; + break; + } + } + + uv_free_interface_addresses(interfaces, count); +} + + +void xmrig::Api::genWorkerId(const String &id) +{ + memset(m_workerId, 0, sizeof(m_workerId)); + + if (id.size() > 0) { + strncpy(m_workerId, id.data(), sizeof(m_workerId) - 1); + } + else { + gethostname(m_workerId, sizeof(m_workerId) - 1); + } +} diff --git a/src/base/api/Api.h b/src/base/api/Api.h new file mode 100644 index 00000000..334609c9 --- /dev/null +++ b/src/base/api/Api.h @@ -0,0 +1,81 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_API_H +#define XMRIG_API_H + + +#include +#include + + +#include "base/kernel/interfaces/IBaseListener.h" + + +namespace xmrig { + + +class Base; +class Httpd; +class HttpData; +class IApiListener; +class IApiRequest; +class String; + + +class Api : public IBaseListener +{ +public: + Api(Base *base); + ~Api() override; + + inline const char *id() const { return m_id; } + inline const char *workerId() const { return m_workerId; } + inline void addListener(IApiListener *listener) { m_listeners.push_back(listener); } + + void request(const HttpData &req); + void start(); + void stop(); + +protected: + void onConfigChanged(Config *config, Config *previousConfig) override; + +private: + void exec(IApiRequest &request); + void genId(const String &id); + void genWorkerId(const String &id); + + Base *m_base; + char m_id[32]; + char m_workerId[128]; + const uint64_t m_timestamp; + Httpd *m_httpd; + std::vector m_listeners; +}; + + +} // namespace xmrig + + +#endif /* XMRIG_API_H */ diff --git a/src/base/api/Httpd.cpp b/src/base/api/Httpd.cpp new file mode 100644 index 00000000..e61e66f1 --- /dev/null +++ b/src/base/api/Httpd.cpp @@ -0,0 +1,193 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include "3rdparty/http-parser/http_parser.h" +#include "base/api/Api.h" +#include "base/api/Httpd.h" +#include "base/io/log/Log.h" +#include "base/net/http/HttpApiResponse.h" +#include "base/net/http/HttpData.h" +#include "base/net/http/HttpServer.h" +#include "base/net/tools/TcpServer.h" +#include "core/config/Config.h" +#include "core/Controller.h" + + +namespace xmrig { + +static const char *kAuthorization = "authorization"; +static const char *kContentType = "content-type"; + +#ifdef _WIN32 +static const char *favicon = nullptr; +static size_t faviconSize = 0; +#endif + +} // namespace xmrig + + +xmrig::Httpd::Httpd(Base *base) : + m_base(base), + m_http(nullptr), + m_server(nullptr), + m_port(0) +{ + base->addListener(this); +} + + +xmrig::Httpd::~Httpd() +{ +} + + +bool xmrig::Httpd::start() +{ + const Http &config = m_base->config()->http(); + + if (!config.isEnabled()) { + return true; + } + + m_http = new HttpServer(this); + m_server = new TcpServer(config.host(), config.port(), m_http); + + const int rc = m_server->bind(); + Log::print(GREEN_BOLD(" * ") WHITE_BOLD("%-13s") BLUE_BOLD("%s:%d") " " RED_BOLD("%s"), + "HTTP API", + config.host().data(), + rc < 0 ? config.port() : rc, + rc < 0 ? uv_strerror(rc) : "" + ); + + if (rc < 0) { + stop(); + + return false; + } + + m_port = static_cast(rc); + +# ifdef _WIN32 + HRSRC src = FindResource(nullptr, MAKEINTRESOURCE(1), RT_ICON); + if (src != nullptr) { + HGLOBAL res = LoadResource(nullptr, src); + if (res != nullptr) { + favicon = static_cast(LockResource(res)); + faviconSize = SizeofResource(nullptr, src); + } + } +# endif + + return true; +} + + +void xmrig::Httpd::stop() +{ + delete m_server; + delete m_http; + + m_server = nullptr; + m_http = nullptr; + m_port = 0; +} + + + +void xmrig::Httpd::onConfigChanged(Config *config, Config *previousConfig) +{ + if (config->http() == previousConfig->http()) { + return; + } + + stop(); + start(); +} + + +void xmrig::Httpd::onHttpData(const HttpData &data) +{ + if (data.method == HTTP_OPTIONS) { + return HttpApiResponse(data.id()).end(); + } + + if (data.method == HTTP_GET && data.url == "/favicon.ico") { +# ifdef _WIN32 + if (favicon != nullptr) { + HttpResponse response(data.id()); + response.setHeader("Content-Type", "image/x-icon"); + + return response.end(favicon, faviconSize); + } +# endif + + return HttpResponse(data.id(), 404).end(); + } + + if (data.method > 4) { + return HttpApiResponse(data.id(), HTTP_STATUS_METHOD_NOT_ALLOWED).end(); + } + + const int status = auth(data); + if (status != HTTP_STATUS_OK) { + return HttpApiResponse(data.id(), status).end(); + } + + if (data.method != HTTP_GET) { + if (m_base->config()->http().isRestricted()) { + return HttpApiResponse(data.id(), HTTP_STATUS_FORBIDDEN).end(); + } + + if (!data.headers.count(kContentType) || data.headers.at(kContentType) != "application/json") { + return HttpApiResponse(data.id(), HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE).end(); + } + } + + m_base->api()->request(data); +} + + +int xmrig::Httpd::auth(const HttpData &req) const +{ + const Http &config = m_base->config()->http(); + + if (!req.headers.count(kAuthorization)) { + return config.isAuthRequired() ? HTTP_STATUS_UNAUTHORIZED : HTTP_STATUS_OK; + } + + if (config.token().isNull()) { + return HTTP_STATUS_UNAUTHORIZED; + } + + const std::string &token = req.headers.at(kAuthorization); + const size_t size = token.size(); + + if (token.size() < 8 || config.token().size() != size - 7 || memcmp("Bearer ", token.c_str(), 7) != 0) { + return HTTP_STATUS_FORBIDDEN; + } + + return strncmp(config.token().data(), token.c_str() + 7, config.token().size()) == 0 ? HTTP_STATUS_OK : HTTP_STATUS_FORBIDDEN; +} diff --git a/src/base/api/Httpd.h b/src/base/api/Httpd.h new file mode 100644 index 00000000..220bb7f5 --- /dev/null +++ b/src/base/api/Httpd.h @@ -0,0 +1,70 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_HTTPD_H +#define XMRIG_HTTPD_H + + +#include + + +#include "base/kernel/interfaces/IBaseListener.h" +#include "base/kernel/interfaces/IHttpListener.h" + + +namespace xmrig { + + +class Base; +class HttpServer; +class TcpServer; + + +class Httpd : public IBaseListener, public IHttpListener +{ +public: + Httpd(Base *base); + ~Httpd() override; + + bool start(); + void stop(); + +protected: + void onConfigChanged(Config *config, Config *previousConfig) override; + void onHttpData(const HttpData &data) override; + +private: + int auth(const HttpData &req) const; + + Base *m_base; + HttpServer *m_http; + TcpServer *m_server; + uint16_t m_port; +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_HTTPD_H */ diff --git a/src/interfaces/IJobResultListener.h b/src/base/api/interfaces/IApiListener.h similarity index 73% rename from src/interfaces/IJobResultListener.h rename to src/base/api/interfaces/IApiListener.h index 483a2062..bbf153a6 100644 --- a/src/interfaces/IJobResultListener.h +++ b/src/base/api/interfaces/IApiListener.h @@ -4,8 +4,7 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2016-2018 XMRig * * 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 @@ -21,21 +20,28 @@ * along with this program. If not, see . */ -#ifndef __IJOBRESULTLISTENER_H__ -#define __IJOBRESULTLISTENER_H__ +#ifndef XMRIG_IAPILISTENER_H +#define XMRIG_IAPILISTENER_H -class Client; -class JobResult; +namespace xmrig { -class IJobResultListener +class IApiRequest; + + +class IApiListener { public: - virtual ~IJobResultListener() {} + virtual ~IApiListener() = default; - virtual void onJobResult(const JobResult &result) = 0; +# ifdef XMRIG_FEATURE_API + virtual void onRequest(IApiRequest &request) = 0; +# endif }; -#endif // __IJOBRESULTLISTENER_H__ +} /* namespace xmrig */ + + +#endif // XMRIG_IAPILISTENER_H diff --git a/src/base/api/interfaces/IApiRequest.h b/src/base/api/interfaces/IApiRequest.h new file mode 100644 index 00000000..4f74581c --- /dev/null +++ b/src/base/api/interfaces/IApiRequest.h @@ -0,0 +1,93 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_IAPIREQUEST_H +#define XMRIG_IAPIREQUEST_H + + +#include "rapidjson/fwd.h" + + +namespace xmrig { + + +class String; + + +class IApiRequest +{ +public: + enum Method { + METHOD_DELETE, + METHOD_GET, + METHOD_HEAD, + METHOD_POST, + METHOD_PUT + }; + + + enum Source { + SOURCE_HTTP + }; + + + enum RequestType { + REQ_UNKNOWN, + REQ_SUMMARY, + REQ_JSON_RPC + }; + + + enum ErrorCode : int { + RPC_PARSE_ERROR = -32700, + RPC_INVALID_REQUEST = -32600, + RPC_METHOD_NOT_FOUND = -32601, + RPC_INVALID_PARAMS = -32602 + }; + + + virtual ~IApiRequest() = default; + + virtual bool accept() = 0; + virtual bool hasParseError() const = 0; + virtual bool isDone() const = 0; + virtual bool isNew() const = 0; + virtual bool isRestricted() const = 0; + virtual const rapidjson::Value &json() const = 0; + virtual const String &rpcMethod() const = 0; + virtual const String &url() const = 0; + virtual int version() const = 0; + virtual Method method() const = 0; + virtual rapidjson::Document &doc() = 0; + virtual rapidjson::Value &reply() = 0; + virtual RequestType type() const = 0; + virtual Source source() const = 0; + virtual void done(int status) = 0; +}; + + +} /* namespace xmrig */ + + +#endif // XMRIG_IAPIREQUEST_H diff --git a/src/log/SysLog.cpp b/src/base/api/requests/ApiRequest.cpp similarity index 69% rename from src/log/SysLog.cpp rename to src/base/api/requests/ApiRequest.cpp index 11988214..da73adee 100644 --- a/src/log/SysLog.cpp +++ b/src/base/api/requests/ApiRequest.cpp @@ -4,8 +4,9 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , * * 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,27 +23,16 @@ */ -#include -#include +#include "base/api/requests/ApiRequest.h" -#include "log/SysLog.h" -#include "version.h" - - -SysLog::SysLog() +xmrig::ApiRequest::ApiRequest(Source source, bool restricted) : + m_restricted(restricted), + m_source(source) { - openlog(APP_ID, LOG_PID, LOG_USER); } -void SysLog::message(int level, const char *fmt, va_list args) +xmrig::ApiRequest::~ApiRequest() { - vsyslog(level, fmt, args); -} - - -void SysLog::text(const char *fmt, va_list args) -{ - message(LOG_INFO, fmt, args); } diff --git a/src/base/api/requests/ApiRequest.h b/src/base/api/requests/ApiRequest.h new file mode 100644 index 00000000..ad4b0c35 --- /dev/null +++ b/src/base/api/requests/ApiRequest.h @@ -0,0 +1,75 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#ifndef XMRIG_APIREQUEST_H +#define XMRIG_APIREQUEST_H + + +#include "base/api/interfaces/IApiRequest.h" +#include "base/tools/String.h" + + +namespace xmrig { + + +class ApiRequest : public IApiRequest +{ +public: + ApiRequest(Source source, bool restricted); + ~ApiRequest() override; + +protected: + enum State { + STATE_NEW, + STATE_ACCEPTED, + STATE_DONE + }; + + inline bool accept() override { m_state = STATE_ACCEPTED; return true; } + inline bool isDone() const override { return m_state == STATE_DONE; } + inline bool isNew() const override { return m_state == STATE_NEW; } + inline bool isRestricted() const override { return m_restricted; } + inline const String &rpcMethod() const override { return m_rpcMethod; } + inline int version() const override { return m_version; } + inline RequestType type() const override { return m_type; } + inline Source source() const override { return m_source; } + inline void done(int) override { m_state = STATE_DONE; } + + int m_version = 1; + RequestType m_type = REQ_UNKNOWN; + State m_state = STATE_NEW; + String m_rpcMethod; + +private: + bool m_restricted; + Source m_source; +}; + + +} // namespace xmrig + + +#endif // XMRIG_APIREQUEST_H + diff --git a/src/base/api/requests/HttpApiRequest.cpp b/src/base/api/requests/HttpApiRequest.cpp new file mode 100644 index 00000000..ed13e47d --- /dev/null +++ b/src/base/api/requests/HttpApiRequest.cpp @@ -0,0 +1,178 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include "3rdparty/http-parser/http_parser.h" +#include "base/api/requests/HttpApiRequest.h" +#include "base/io/json/Json.h" +#include "base/net/http/HttpData.h" +#include "rapidjson/error/en.h" + + +namespace xmrig { + + +static const char *kError = "error"; +static const char *kId = "id"; +static const char *kResult = "result"; + + +static inline const char *rpcError(int code) { + switch (code) { + case IApiRequest::RPC_PARSE_ERROR: + return "Parse error"; + + case IApiRequest::RPC_INVALID_REQUEST: + return "Invalid Request"; + + case IApiRequest::RPC_METHOD_NOT_FOUND: + return "Method not found"; + + case IApiRequest::RPC_INVALID_PARAMS: + return "Invalid params"; + } + + return "Internal error"; +} + + +} // namespace xmrig + + +xmrig::HttpApiRequest::HttpApiRequest(const HttpData &req, bool restricted) : + ApiRequest(SOURCE_HTTP, restricted), + m_req(req), + m_res(req.id()), + m_url(req.url.c_str()) +{ + if (method() == METHOD_GET) { + if (url() == "/1/summary" || url() == "/2/summary" || url() == "/api.json") { + m_type = REQ_SUMMARY; + } + } + + if (method() == METHOD_POST && url() == "/json_rpc") { + m_type = REQ_JSON_RPC; + accept(); + + if (hasParseError()) { + done(RPC_PARSE_ERROR); + + return; + } + + m_rpcMethod = Json::getString(json(), "method"); + if (m_rpcMethod.isEmpty()) { + done(RPC_INVALID_REQUEST); + + return; + } + + m_state = STATE_NEW; + + return; + } + + if (url().size() > 4) { + if (memcmp(url().data(), "/2/", 3) == 0) { + m_version = 2; + } + } +} + + +bool xmrig::HttpApiRequest::accept() +{ + using namespace rapidjson; + + ApiRequest::accept(); + + if (m_parsed == 0 && !m_req.body.empty()) { + m_body.Parse(m_req.body.c_str()); + m_parsed = m_body.HasParseError() ? 2 : 1; + + if (!hasParseError()) { + return true; + } + + if (type() != REQ_JSON_RPC) { + reply().AddMember(StringRef(kError), StringRef(GetParseError_En(m_body.GetParseError())), doc().GetAllocator()); + } + + return false; + } + + return hasParseError(); +} + + +const rapidjson::Value &xmrig::HttpApiRequest::json() const +{ + return m_body; +} + + +xmrig::IApiRequest::Method xmrig::HttpApiRequest::method() const +{ + return static_cast(m_req.method); +} + + +void xmrig::HttpApiRequest::done(int status) +{ + ApiRequest::done(status); + + if (type() == REQ_JSON_RPC) { + using namespace rapidjson; + auto &allocator = doc().GetAllocator(); + + m_res.setStatus(HTTP_STATUS_OK); + + if (status != HTTP_STATUS_OK) { + if (status == HTTP_STATUS_NOT_FOUND) { + status = RPC_METHOD_NOT_FOUND; + } + + Value error(kObjectType); + error.AddMember("code", status, allocator); + error.AddMember("message", StringRef(rpcError(status)), allocator); + + reply().AddMember(StringRef(kError), error, allocator); + } + else if (!reply().HasMember(kResult)) { + Value result(kObjectType); + result.AddMember("status", "OK", allocator); + + reply().AddMember(StringRef(kResult), result, allocator); + } + + reply().AddMember("jsonrpc", "2.0", allocator); + reply().AddMember(StringRef(kId), Value().CopyFrom(Json::getValue(json(), kId), allocator), allocator); + } + else { + m_res.setStatus(status); + } + + m_res.end(); +} diff --git a/src/base/api/requests/HttpApiRequest.h b/src/base/api/requests/HttpApiRequest.h new file mode 100644 index 00000000..309b5a6b --- /dev/null +++ b/src/base/api/requests/HttpApiRequest.h @@ -0,0 +1,70 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#ifndef XMRIG_HTTPAPIREQUEST_H +#define XMRIG_HTTPAPIREQUEST_H + + +#include "base/api/requests/ApiRequest.h" +#include "base/net/http/HttpApiResponse.h" +#include "base/tools/String.h" + + +namespace xmrig { + + +class HttpData; + + +class HttpApiRequest : public ApiRequest +{ +public: + HttpApiRequest(const HttpData &req, bool restricted); + +protected: + inline bool hasParseError() const override { return m_parsed == 2; } + inline const String &url() const override { return m_url; } + inline rapidjson::Document &doc() override { return m_res.doc(); } + inline rapidjson::Value &reply() override { return m_res.doc(); } + + bool accept() override; + const rapidjson::Value &json() const override; + Method method() const override; + void done(int status) override; + +private: + const HttpData &m_req; + HttpApiResponse m_res; + int m_parsed = 0; + rapidjson::Document m_body; + String m_url; +}; + + +} // namespace xmrig + + +#endif // XMRIG_HTTPAPIREQUEST_H + diff --git a/src/base/base.cmake b/src/base/base.cmake new file mode 100644 index 00000000..d0815e24 --- /dev/null +++ b/src/base/base.cmake @@ -0,0 +1,162 @@ +set(HEADERS_BASE + src/base/api/interfaces/IApiListener.h + src/base/cc/interfaces/IClientStatusListener.h + src/base/cc/interfaces/ICommandListener.h + src/base/io/Console.h + src/base/io/json/Json.h + src/base/io/json/JsonChain.h + src/base/io/json/JsonRequest.h + src/base/io/log/backends/ConsoleLog.h + src/base/io/log/backends/FileLog.h + src/base/io/log/backends/RemoteLog.h + src/base/io/log/Log.h + src/base/io/Watcher.h + src/base/kernel/Base.h + src/base/kernel/config/BaseConfig.h + src/base/kernel/config/BaseTransform.h + src/base/kernel/Entry.h + src/base/kernel/interfaces/IBaseListener.h + src/base/kernel/interfaces/IClient.h + src/base/kernel/interfaces/IClientListener.h + src/base/kernel/interfaces/IConfig.h + src/base/kernel/interfaces/IConfigListener.h + src/base/kernel/interfaces/IConfigTransform.h + src/base/kernel/interfaces/IConsoleListener.h + src/base/kernel/interfaces/IDnsListener.h + src/base/kernel/interfaces/ILineListener.h + src/base/kernel/interfaces/ILogBackend.h + src/base/kernel/interfaces/ISignalListener.h + src/base/kernel/interfaces/IStrategy.h + src/base/kernel/interfaces/IStrategyListener.h + src/base/kernel/interfaces/ITimerListener.h + src/base/kernel/interfaces/IWatcherListener.h + src/base/kernel/Platform.h + src/base/kernel/Process.h + src/base/kernel/Signals.h + src/base/net/dns/Dns.h + src/base/net/dns/DnsRecord.h + src/base/net/http/Http.h + src/base/net/stratum/BaseClient.h + src/base/net/stratum/Client.h + src/base/net/stratum/Job.h + src/base/net/stratum/Pool.h + src/base/net/stratum/Pools.h + src/base/net/stratum/strategies/FailoverStrategy.h + src/base/net/stratum/strategies/SinglePoolStrategy.h + src/base/net/stratum/SubmitResult.h + src/base/net/tools/RecvBuf.h + src/base/net/tools/Storage.h + src/base/tools/Arguments.h + src/base/tools/Baton.h + src/base/tools/Buffer.h + src/base/tools/Chrono.h + src/base/tools/Handle.h + src/base/tools/String.h + src/base/tools/Timer.h + ) + +set(SOURCES_BASE + src/base/io/Console.cpp + src/base/io/json/Json.cpp + src/base/io/json/JsonChain.cpp + src/base/io/json/JsonRequest.cpp + src/base/io/log/backends/ConsoleLog.cpp + src/base/io/log/backends/FileLog.cpp + src/base/io/log/backends/RemoteLog.cpp + src/base/io/log/Log.cpp + src/base/io/Watcher.cpp + src/base/kernel/Base.cpp + src/base/kernel/config/BaseConfig.cpp + src/base/kernel/config/BaseTransform.cpp + src/base/kernel/Entry.cpp + src/base/kernel/Platform.cpp + src/base/kernel/Process.cpp + src/base/kernel/Signals.cpp + src/base/net/dns/Dns.cpp + src/base/net/dns/DnsRecord.cpp + src/base/net/http/Http.cpp + src/base/net/stratum/BaseClient.cpp + src/base/net/stratum/Client.cpp + src/base/net/stratum/Job.cpp + src/base/net/stratum/Pool.cpp + src/base/net/stratum/Pools.cpp + src/base/net/stratum/strategies/FailoverStrategy.cpp + src/base/net/stratum/strategies/SinglePoolStrategy.cpp + src/base/tools/Arguments.cpp + src/base/tools/Buffer.cpp + src/base/tools/String.cpp + src/base/tools/Timer.cpp + ) + + +if (WIN32) + set(SOURCES_OS + src/base/io/json/Json_win.cpp + src/base/kernel/Platform_win.cpp + ) +elseif (APPLE) + set(SOURCES_OS + src/base/io/json/Json_unix.cpp + src/base/kernel/Platform_mac.cpp + ) +else() + set(SOURCES_OS + src/base/io/json/Json_unix.cpp + src/base/kernel//Platform_unix.cpp + ) +endif() + + +if (NOT WIN32) + CHECK_INCLUDE_FILE (syslog.h HAVE_SYSLOG_H) + if (HAVE_SYSLOG_H) + add_definitions(/DHAVE_SYSLOG_H) + set(SOURCES_SYSLOG src/base/io/log/backends/SysLog.h src/base/io/log/backends/SysLog.cpp) + endif() +endif() + + +if (WITH_HTTP) + set(HEADERS_BASE_HTTP + src/3rdparty/http-parser/http_parser.h + src/base/api/Api.h + src/base/api/Httpd.h + src/base/api/interfaces/IApiRequest.h + src/base/api/requests/ApiRequest.h + src/base/api/requests/HttpApiRequest.h + src/base/kernel/interfaces/IHttpListener.h + src/base/kernel/interfaces/IJsonReader.h + src/base/kernel/interfaces/ITcpServerListener.h + src/base/net/http/HttpApiResponse.h + src/base/net/http/HttpClient.h + src/base/net/http/HttpContext.h + src/base/net/http/HttpData.h + src/base/net/http/HttpResponse.h + src/base/net/http/HttpServer.h + src/base/net/stratum/DaemonClient.h + src/base/net/tools/TcpServer.h + ) + + set(SOURCES_BASE_HTTP + src/3rdparty/http-parser/http_parser.c + src/base/api/Api.cpp + src/base/api/Httpd.cpp + src/base/api/requests/ApiRequest.cpp + src/base/api/requests/HttpApiRequest.cpp + src/base/net/http/HttpApiResponse.cpp + src/base/net/http/HttpClient.cpp + src/base/net/http/HttpContext.cpp + src/base/net/http/HttpResponse.cpp + src/base/net/http/HttpServer.cpp + src/base/net/stratum/DaemonClient.cpp + src/base/net/tools/TcpServer.cpp + ) + + add_definitions(/DXMRIG_FEATURE_HTTP) + add_definitions(/DXMRIG_FEATURE_API) +else() + set(HEADERS_BASE_HTTP "") + set(SOURCES_BASE_HTTP "") + remove_definitions(/DXMRIG_FEATURE_HTTP) + remove_definitions(/DXMRIG_FEATURE_API) +endif() diff --git a/src/net/BoostTlsConnection.h b/src/base/cc/interfaces/IClientStatusListener.h similarity index 58% rename from src/net/BoostTlsConnection.h rename to src/base/cc/interfaces/IClientStatusListener.h index f0d2fe8a..350d7b64 100644 --- a/src/net/BoostTlsConnection.h +++ b/src/base/cc/interfaces/IClientStatusListener.h @@ -1,6 +1,5 @@ /* XMRigCC - * Copyright 2018 Sebastian Stolzenberg - * + * Copyright 2019- BenDr0id , * * 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 @@ -16,11 +15,28 @@ * along with this program. If not, see . */ -#ifndef __BOOSTTLSCONNECTION_H__ -#define __BOOSTTLSCONNECTION_H__ +#ifndef XMRIG_ICLIENTSTATUSLISTENER_H +#define XMRIG_ICLIENTSTATUSLISTENER_H -#include "net/Connection.h" +#include "cc/ClientStatus.h" -Connection::Ptr establishBoostTlsConnection(const ConnectionListener::Ptr& listener); +namespace xmrig { -#endif /* __BOOSTTLSCONNECTION_H__ */ \ No newline at end of file + +class String; + + +class IClientStatusListener +{ +public: + virtual ~IClientStatusListener() = default; +# ifdef XMRIG_FEATURE_CC_CLIENT + virtual void onUpdateRequest(ClientStatus& clientStatus) = 0; +# endif +}; + + +} /* namespace xmrig */ + + +#endif // XMRIG_ICLIENTSTATUSLISTENER_H diff --git a/src/net/BoostTcpConnection.h b/src/base/cc/interfaces/ICommandListener.h similarity index 61% rename from src/net/BoostTcpConnection.h rename to src/base/cc/interfaces/ICommandListener.h index fa22d1ac..73d69198 100644 --- a/src/net/BoostTcpConnection.h +++ b/src/base/cc/interfaces/ICommandListener.h @@ -1,6 +1,5 @@ /* XMRigCC - * Copyright 2018 Sebastian Stolzenberg - * + * Copyright 2019- BenDr0id , * * 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 @@ -16,11 +15,27 @@ * along with this program. If not, see . */ -#ifndef __BOOSTTCPCONNECTION_H__ -#define __BOOSTTCPCONNECTION_H__ +#ifndef XMRIG_ICOMMANDLISTENER_H +#define XMRIG_ICOMMANDLISTENER_H -#include "net/Connection.h" +#include "cc/ControlCommand.h" -Connection::Ptr establishBoostTcpConnection(const ConnectionListener::Ptr& listener); +namespace xmrig { -#endif /* __BOOSTTCPCONNECTION_H__ */ \ No newline at end of file + +class String; + + +class ICommandListener +{ +public: + virtual ~ICommandListener() = default; + + virtual void onCommandReceived(const ControlCommand& controlCommand) = 0; +}; + + +} /* namespace xmrig */ + + +#endif // XMRIG_ICOMMANDLISTENER_H diff --git a/src/Console.cpp b/src/base/io/Console.cpp similarity index 56% rename from src/Console.cpp rename to src/base/io/Console.cpp index befd4c68..0e5cd269 100644 --- a/src/Console.cpp +++ b/src/base/io/Console.cpp @@ -4,8 +4,9 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , * * 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,29 +23,44 @@ */ -#include "Console.h" -#include "interfaces/IConsoleListener.h" +#include "base/io/Console.h" +#include "base/kernel/interfaces/IConsoleListener.h" +#include "base/tools/Handle.h" -Console::Console(IConsoleListener *listener) +xmrig::Console::Console(IConsoleListener *listener) : m_listener(listener) { - m_tty.data = this; + m_tty = new uv_tty_t; - if (uv_tty_init(uv_default_loop(), &m_tty, 0, 1) < 0) { + m_tty->data = this; + uv_tty_init(uv_default_loop(), m_tty, 0, 1); + + if (!uv_is_readable(reinterpret_cast(m_tty))) { return; } - if (!uv_is_readable(reinterpret_cast(&m_tty))) { - return; - } - - uv_tty_set_mode(&m_tty, UV_TTY_MODE_RAW); - uv_read_start(reinterpret_cast(&m_tty), Console::onAllocBuffer, Console::onRead); + uv_tty_set_mode(m_tty, UV_TTY_MODE_RAW); + uv_read_start(reinterpret_cast(m_tty), Console::onAllocBuffer, Console::onRead); } -void Console::onAllocBuffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) +xmrig::Console::~Console() +{ + stop(); +} + + +void xmrig::Console::stop() +{ + uv_tty_reset_mode(); + + Handle::close(m_tty); + m_tty = nullptr; +} + + +void xmrig::Console::onAllocBuffer(uv_handle_t *handle, size_t, uv_buf_t *buf) { auto console = static_cast(handle->data); buf->len = 1; @@ -52,13 +68,13 @@ void Console::onAllocBuffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t } -void Console::onRead(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) +void xmrig::Console::onRead(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) { if (nread < 0) { return uv_close(reinterpret_cast(stream), nullptr); } - if (nread == 1 && static_cast(stream->data)->m_listener && buf) { + if (nread == 1) { static_cast(stream->data)->m_listener->onConsoleCommand(buf->base[0]); } } diff --git a/src/Console.h b/src/base/io/Console.h similarity index 76% rename from src/Console.h rename to src/base/io/Console.h index fd34b3e9..c0a36ec4 100644 --- a/src/Console.h +++ b/src/base/io/Console.h @@ -4,8 +4,9 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , * * 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 @@ -21,13 +22,17 @@ * along with this program. If not, see . */ -#ifndef __CONSOLE_H__ -#define __CONSOLE_H__ +#ifndef XMRIG_CONSOLE_H +#define XMRIG_CONSOLE_H #include + +namespace xmrig { + + class IConsoleListener; @@ -37,14 +42,19 @@ public: Console(IConsoleListener *listener); ~Console(); + void stop(); + private: static void onAllocBuffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf); static void onRead(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf); char m_buf[1]; IConsoleListener *m_listener; - uv_tty_t m_tty; + uv_tty_t *m_tty; }; -#endif /* __CONSOLE_H__ */ +} /* namespace xmrig */ + + +#endif /* XMRIG_CONSOLE_H */ diff --git a/src/base/io/Watcher.cpp b/src/base/io/Watcher.cpp new file mode 100644 index 00000000..f5ce9647 --- /dev/null +++ b/src/base/io/Watcher.cpp @@ -0,0 +1,88 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include + + +#include "base/kernel/interfaces/IWatcherListener.h" +#include "base/io/Watcher.h" +#include "base/tools/Handle.h" +#include "base/tools/Timer.h" + + +xmrig::Watcher::Watcher(const String &path, IWatcherListener *listener) : + m_listener(listener), + m_path(path) +{ + m_timer = new Timer(this); + + m_fsEvent = new uv_fs_event_t; + m_fsEvent->data = this; + uv_fs_event_init(uv_default_loop(), m_fsEvent); + + start(); +} + + +xmrig::Watcher::~Watcher() +{ + delete m_timer; + + Handle::close(m_fsEvent); +} + + +void xmrig::Watcher::onFsEvent(uv_fs_event_t *handle, const char *filename, int, int) +{ + if (!filename) { + return; + } + + static_cast(handle->data)->queueUpdate(); +} + + +void xmrig::Watcher::queueUpdate() +{ + m_timer->stop(); + m_timer->start(kDelay, 0); +} + + +void xmrig::Watcher::reload() +{ + m_listener->onFileChanged(m_path); + +# ifndef _WIN32 + uv_fs_event_stop(m_fsEvent); + start(); +# endif +} + + +void xmrig::Watcher::start() +{ + uv_fs_event_start(m_fsEvent, xmrig::Watcher::onFsEvent, m_path, 0); +} diff --git a/src/base/io/Watcher.h b/src/base/io/Watcher.h new file mode 100644 index 00000000..6438cb7a --- /dev/null +++ b/src/base/io/Watcher.h @@ -0,0 +1,71 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_WATCHER_H +#define XMRIG_WATCHER_H + + +#include "base/kernel/interfaces/ITimerListener.h" +#include "base/tools/String.h" + + +typedef struct uv_fs_event_s uv_fs_event_t; + + +namespace xmrig { + + +class IWatcherListener; +class Timer; + + +class Watcher : public ITimerListener +{ +public: + Watcher(const String &path, IWatcherListener *listener); + ~Watcher() override; + +protected: + inline void onTimer(const Timer *) override { reload(); } + +private: + constexpr static int kDelay = 500; + + static void onFsEvent(uv_fs_event_t *handle, const char *filename, int events, int status); + + void queueUpdate(); + void reload(); + void start(); + + IWatcherListener *m_listener; + String m_path; + Timer *m_timer; + uv_fs_event_t *m_fsEvent; +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_WATCHER_H */ diff --git a/src/base/io/json/Json.cpp b/src/base/io/json/Json.cpp new file mode 100644 index 00000000..07986c4a --- /dev/null +++ b/src/base/io/json/Json.cpp @@ -0,0 +1,139 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include "base/io/json/Json.h" +#include "rapidjson/document.h" + + +namespace xmrig { + +static const rapidjson::Value kNullValue; + +} + + +bool xmrig::Json::getBool(const rapidjson::Value &obj, const char *key, bool defaultValue) +{ + auto i = obj.FindMember(key); + if (i != obj.MemberEnd() && i->value.IsBool()) { + return i->value.GetBool(); + } + + return defaultValue; +} + + +const char *xmrig::Json::getString(const rapidjson::Value &obj, const char *key, const char *defaultValue) +{ + auto i = obj.FindMember(key); + if (i != obj.MemberEnd() && i->value.IsString()) { + return i->value.GetString(); + } + + return defaultValue; +} + + +const rapidjson::Value &xmrig::Json::getArray(const rapidjson::Value &obj, const char *key) +{ + auto i = obj.FindMember(key); + if (i != obj.MemberEnd() && i->value.IsArray()) { + return i->value; + } + + return kNullValue; +} + + +const rapidjson::Value &xmrig::Json::getObject(const rapidjson::Value &obj, const char *key) +{ + auto i = obj.FindMember(key); + if (i != obj.MemberEnd() && i->value.IsObject()) { + return i->value; + } + + return kNullValue; +} + + +const rapidjson::Value &xmrig::Json::getValue(const rapidjson::Value &obj, const char *key) +{ + auto i = obj.FindMember(key); + if (i != obj.MemberEnd()) { + return i->value; + } + + return kNullValue; +} + + +int xmrig::Json::getInt(const rapidjson::Value &obj, const char *key, int defaultValue) +{ + auto i = obj.FindMember(key); + if (i != obj.MemberEnd() && i->value.IsInt()) { + return i->value.GetInt(); + } + + return defaultValue; +} + + +int64_t xmrig::Json::getInt64(const rapidjson::Value &obj, const char *key, int64_t defaultValue) +{ + auto i = obj.FindMember(key); + if (i != obj.MemberEnd() && i->value.IsInt64()) { + return i->value.GetInt64(); + } + + return defaultValue; +} + + +uint64_t xmrig::Json::getUint64(const rapidjson::Value &obj, const char *key, uint64_t defaultValue) +{ + auto i = obj.FindMember(key); + if (i != obj.MemberEnd() && i->value.IsUint64()) { + return i->value.GetUint64(); + } + + return defaultValue; +} + + +unsigned xmrig::Json::getUint(const rapidjson::Value &obj, const char *key, unsigned defaultValue) +{ + auto i = obj.FindMember(key); + if (i != obj.MemberEnd() && i->value.IsUint()) { + return i->value.GetUint(); + } + + return defaultValue; +} + + +bool xmrig::JsonReader::isEmpty() const +{ + return !m_obj.IsObject() || m_obj.ObjectEmpty(); +} diff --git a/src/base/io/json/Json.h b/src/base/io/json/Json.h new file mode 100644 index 00000000..80fe5dc2 --- /dev/null +++ b/src/base/io/json/Json.h @@ -0,0 +1,79 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_JSON_H +#define XMRIG_JSON_H + + +#include "base/kernel/interfaces/IJsonReader.h" +#include "rapidjson/fwd.h" + + +namespace xmrig { + + +class Json +{ +public: + static bool getBool(const rapidjson::Value &obj, const char *key, bool defaultValue = false); + static const char *getString(const rapidjson::Value &obj, const char *key, const char *defaultValue = nullptr); + static const rapidjson::Value &getArray(const rapidjson::Value &obj, const char *key); + static const rapidjson::Value &getObject(const rapidjson::Value &obj, const char *key); + static const rapidjson::Value &getValue(const rapidjson::Value &obj, const char *key); + static int getInt(const rapidjson::Value &obj, const char *key, int defaultValue = 0); + static int64_t getInt64(const rapidjson::Value &obj, const char *key, int64_t defaultValue = 0); + static uint64_t getUint64(const rapidjson::Value &obj, const char *key, uint64_t defaultValue = 0); + static unsigned getUint(const rapidjson::Value &obj, const char *key, unsigned defaultValue = 0); + + static bool get(const char *fileName, rapidjson::Document &doc); + static bool save(const char *fileName, const rapidjson::Document &doc); +}; + + +class JsonReader : public IJsonReader +{ +public: + inline JsonReader(const rapidjson::Value &obj) : m_obj(obj) {} + + inline bool getBool(const char *key, bool defaultValue = false) const override { return Json::getBool(m_obj, key, defaultValue); } + inline const char *getString(const char *key, const char *defaultValue = nullptr) const override { return Json::getString(m_obj, key, defaultValue); } + inline const rapidjson::Value &getArray(const char *key) const override { return Json::getArray(m_obj, key); } + inline const rapidjson::Value &getObject(const char *key) const override { return Json::getObject(m_obj, key); } + inline const rapidjson::Value &getValue(const char *key) const override { return Json::getValue(m_obj, key); } + inline int getInt(const char *key, int defaultValue = 0) const override { return Json::getInt(m_obj, key, defaultValue); } + inline int64_t getInt64(const char *key, int64_t defaultValue = 0) const override { return Json::getInt64(m_obj, key, defaultValue); } + inline uint64_t getUint64(const char *key, uint64_t defaultValue = 0) const override { return Json::getUint64(m_obj, key, defaultValue); } + inline unsigned getUint(const char *key, unsigned defaultValue = 0) const override { return Json::getUint(m_obj, key, defaultValue); } + + bool isEmpty() const override; + +private: + const rapidjson::Value &m_obj; +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_JSON_H */ diff --git a/src/base/io/json/JsonChain.cpp b/src/base/io/json/JsonChain.cpp new file mode 100644 index 00000000..bbaabbde --- /dev/null +++ b/src/base/io/json/JsonChain.cpp @@ -0,0 +1,213 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include "base/io/json/Json.h" +#include "base/io/json/JsonChain.h" +#include "base/io/log/Log.h" +#include "rapidjson/error/en.h" + + +namespace xmrig { + +static const rapidjson::Value kNullValue; + +} + + +xmrig::JsonChain::JsonChain() +{ +} + + +bool xmrig::JsonChain::add(rapidjson::Document &&doc) +{ + if (doc.HasParseError() || !doc.IsObject() || doc.ObjectEmpty()) { + return false; + } + + m_chain.push_back(std::move(doc)); + + return true; +} + + +bool xmrig::JsonChain::addFile(const char *fileName) +{ + using namespace rapidjson; + Document doc; + if (Json::get(fileName, doc)) { + m_fileName = fileName; + + return add(std::move(doc)); + } + + if (doc.HasParseError()) { + LOG_ERR("%s: \"%s\"", fileName, doc.GetErrorOffset(), GetParseError_En(doc.GetParseError())); + } + else { + LOG_ERR("unable to open \"%s\".", fileName); + } + + return false; +} + + +bool xmrig::JsonChain::addRaw(const char *json) +{ + using namespace rapidjson; + Document doc; + doc.Parse(json); + + return add(std::move(doc)); +} + + +void xmrig::JsonChain::dump(const char *fileName) +{ + rapidjson::Document doc(rapidjson::kArrayType); + + for (rapidjson::Document &value : m_chain) { + doc.PushBack(value, doc.GetAllocator()); + } + + Json::save(fileName, doc); +} + + +bool xmrig::JsonChain::getBool(const char *key, bool defaultValue) const +{ + for (auto it = m_chain.rbegin(); it != m_chain.rend(); ++it) { + auto i = it->FindMember(key); + if (i != it->MemberEnd() && i->value.IsBool()) { + return i->value.GetBool(); + } + } + + return defaultValue; +} + + +const char *xmrig::JsonChain::getString(const char *key, const char *defaultValue) const +{ + for (auto it = m_chain.rbegin(); it != m_chain.rend(); ++it) { + auto i = it->FindMember(key); + if (i != it->MemberEnd() && i->value.IsString()) { + return i->value.GetString(); + } + } + + return defaultValue; +} + + +const rapidjson::Value &xmrig::JsonChain::getArray(const char *key) const +{ + for (auto it = m_chain.rbegin(); it != m_chain.rend(); ++it) { + auto i = it->FindMember(key); + if (i != it->MemberEnd() && i->value.IsArray()) { + return i->value; + } + } + + return kNullValue; +} + + +const rapidjson::Value &xmrig::JsonChain::getObject(const char *key) const +{ + for (auto it = m_chain.rbegin(); it != m_chain.rend(); ++it) { + auto i = it->FindMember(key); + if (i != it->MemberEnd() && i->value.IsObject()) { + return i->value; + } + } + + return kNullValue; +} + + +const rapidjson::Value &xmrig::JsonChain::getValue(const char *key) const +{ + for (auto it = m_chain.rbegin(); it != m_chain.rend(); ++it) { + auto i = it->FindMember(key); + if (i != it->MemberEnd()) { + return i->value; + } + } + + return kNullValue; +} + + +int xmrig::JsonChain::getInt(const char *key, int defaultValue) const +{ + for (auto it = m_chain.rbegin(); it != m_chain.rend(); ++it) { + auto i = it->FindMember(key); + if (i != it->MemberEnd() && i->value.IsInt()) { + return i->value.GetInt(); + } + } + + return defaultValue; +} + + +int64_t xmrig::JsonChain::getInt64(const char *key, int64_t defaultValue) const +{ + for (auto it = m_chain.rbegin(); it != m_chain.rend(); ++it) { + auto i = it->FindMember(key); + if (i != it->MemberEnd() && i->value.IsInt64()) { + return i->value.GetInt64(); + } + } + + return defaultValue; +} + + +uint64_t xmrig::JsonChain::getUint64(const char *key, uint64_t defaultValue) const +{ + for (auto it = m_chain.rbegin(); it != m_chain.rend(); ++it) { + auto i = it->FindMember(key); + if (i != it->MemberEnd() && i->value.IsUint64()) { + return i->value.GetUint64(); + } + } + + return defaultValue; +} + + +unsigned xmrig::JsonChain::getUint(const char *key, unsigned defaultValue) const +{ + for (auto it = m_chain.rbegin(); it != m_chain.rend(); ++it) { + auto i = it->FindMember(key); + if (i != it->MemberEnd() && i->value.IsUint()) { + return i->value.GetUint(); + } + } + + return defaultValue; +} diff --git a/src/base/io/json/JsonChain.h b/src/base/io/json/JsonChain.h new file mode 100644 index 00000000..275f789e --- /dev/null +++ b/src/base/io/json/JsonChain.h @@ -0,0 +1,76 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_JSONCHAIN_H +#define XMRIG_JSONCHAIN_H + + +#include + + +#include "base/kernel/interfaces/IJsonReader.h" +#include "base/tools/String.h" +#include "rapidjson/document.h" + + +namespace xmrig { + + +class JsonChain : public IJsonReader +{ +public: + JsonChain(); + + bool add(rapidjson::Document &&doc); + bool addFile(const char *fileName); + bool addRaw(const char *json); + + void dump(const char *fileName); + + inline const String &fileName() const { return m_fileName; } + inline size_t size() const { return m_chain.size(); } + +protected: + inline bool isEmpty() const override { return m_chain.empty(); } + + bool getBool(const char *key, bool defaultValue = false) const override; + const char *getString(const char *key, const char *defaultValue = nullptr) const override; + const rapidjson::Value &getArray(const char *key) const override; + const rapidjson::Value &getObject(const char *key) const override; + const rapidjson::Value &getValue(const char *key) const override; + int getInt(const char *key, int defaultValue = 0) const override; + int64_t getInt64(const char *key, int64_t defaultValue = 0) const override; + uint64_t getUint64(const char *key, uint64_t defaultValue = 0) const override; + unsigned getUint(const char *key, unsigned defaultValue = 0) const override; + +private: + std::vector m_chain; + String m_fileName; +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_JSONCHAIN_H */ diff --git a/src/Cpu_arm.cpp b/src/base/io/json/JsonRequest.cpp similarity index 59% rename from src/Cpu_arm.cpp rename to src/base/io/json/JsonRequest.cpp index 7be95170..4556f3f0 100644 --- a/src/Cpu_arm.cpp +++ b/src/base/io/json/JsonRequest.cpp @@ -4,8 +4,9 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , * * 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,22 +23,16 @@ */ -#include +#include "base/io/json/JsonRequest.h" +#include "rapidjson/document.h" -#include "Cpu.h" -#include "CpuImpl.h" - -void CpuImpl::initCommon() +void xmrig::JsonRequest::create(rapidjson::Document &doc, int64_t id, const char *method, rapidjson::Value ¶ms) { -# ifdef XMRIG_ARMv8 - memcpy(m_brand, "ARMv8", 5); -# else - memcpy(m_brand, "ARMv7", 5); -# endif + auto &allocator = doc.GetAllocator(); -# if defined(XMRIG_ARMv8) - m_flags |= Cpu::X86_64; - m_flags |= Cpu::AES; -# endif + doc.AddMember("id", id, allocator); + doc.AddMember("jsonrpc", "2.0", allocator); + doc.AddMember("method", rapidjson::StringRef(method), allocator); + doc.AddMember("params", params, allocator); } diff --git a/src/base/io/json/JsonRequest.h b/src/base/io/json/JsonRequest.h new file mode 100644 index 00000000..e98c0bae --- /dev/null +++ b/src/base/io/json/JsonRequest.h @@ -0,0 +1,45 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_JSONREQUEST_H +#define XMRIG_JSONREQUEST_H + + +#include "rapidjson/fwd.h" + + +namespace xmrig { + + +class JsonRequest +{ +public: + static void create(rapidjson::Document &doc, int64_t id, const char *method, rapidjson::Value ¶ms); +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_JSONREQUEST_H */ diff --git a/src/base/io/json/Json_unix.cpp b/src/base/io/json/Json_unix.cpp new file mode 100644 index 00000000..dedea947 --- /dev/null +++ b/src/base/io/json/Json_unix.cpp @@ -0,0 +1,64 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include + + +#include "base/io/json/Json.h" +#include "rapidjson/document.h" +#include "rapidjson/istreamwrapper.h" +#include "rapidjson/ostreamwrapper.h" +#include "rapidjson/prettywriter.h" + + +bool xmrig::Json::get(const char *fileName, rapidjson::Document &doc) +{ + std::ifstream ifs(fileName, std::ios_base::in | std::ios_base::binary); + if (!ifs.is_open()) { + return false; + } + + rapidjson::IStreamWrapper isw(ifs); + doc.ParseStream(isw); + + return !doc.HasParseError() && doc.IsObject(); +} + + +bool xmrig::Json::save(const char *fileName, const rapidjson::Document &doc) +{ + std::ofstream ofs(fileName, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc); + if (!ofs.is_open()) { + return false; + } + + rapidjson::OStreamWrapper osw(ofs); + rapidjson::PrettyWriter writer(osw); + writer.SetFormatOptions(rapidjson::kFormatSingleLineArray); + + doc.Accept(writer); + + return true; +} diff --git a/src/base/io/json/Json_win.cpp b/src/base/io/json/Json_win.cpp new file mode 100644 index 00000000..73aff2c5 --- /dev/null +++ b/src/base/io/json/Json_win.cpp @@ -0,0 +1,126 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include + + +#ifdef __GNUC__ +# include +# include +#endif + + +#include + + +#include "base/io/json/Json.h" +#include "rapidjson/document.h" +#include "rapidjson/istreamwrapper.h" +#include "rapidjson/ostreamwrapper.h" +#include "rapidjson/prettywriter.h" + + +#if defined(_MSC_VER) || defined (__GNUC__) +static std::wstring toUtf16(const char *str) +{ + const int size = static_cast(strlen(str)); + std::wstring ret; + + int len = MultiByteToWideChar(CP_UTF8, 0, str, size, nullptr, 0); + if (len > 0) { + ret.resize(static_cast(len)); + MultiByteToWideChar(CP_UTF8, 0, str, size, &ret[0], len); + } + + return ret; +} +#endif + + +bool xmrig::Json::get(const char *fileName, rapidjson::Document &doc) +{ + using namespace rapidjson; + constexpr const std::ios_base::openmode mode = std::ios_base::in | std::ios_base::binary; + +# if defined(_MSC_VER) + std::ifstream ifs(toUtf16(fileName), mode); + if (!ifs.is_open()) { + return false; + } +# elif defined(__GNUC__) + const int fd = _wopen(toUtf16(fileName).c_str(), _O_RDONLY | _O_BINARY); + if (fd == -1) { + return false; + } + + __gnu_cxx::stdio_filebuf buf(fd, mode); + std::istream ifs(&buf); +# else + std::ifstream ifs(fileName, mode); + if (!ifs.is_open()) { + return false; + } +# endif + + IStreamWrapper isw(ifs); + doc.ParseStream(isw); + + return !doc.HasParseError() && doc.IsObject(); +} + + +bool xmrig::Json::save(const char *fileName, const rapidjson::Document &doc) +{ + using namespace rapidjson; + constexpr const std::ios_base::openmode mode = std::ios_base::out | std::ios_base::binary | std::ios_base::trunc; + +# if defined(_MSC_VER) + std::ofstream ofs(toUtf16(fileName), mode); + if (!ofs.is_open()) { + return false; + } +# elif defined(__GNUC__) + const int fd = _wopen(toUtf16(fileName).c_str(), _O_WRONLY | _O_BINARY | _O_CREAT | _O_TRUNC); + if (fd == -1) { + return false; + } + + __gnu_cxx::stdio_filebuf buf(fd, mode); + std::ostream ofs(&buf); +# else + std::ofstream ofs(fileName, mode); + if (!ofs.is_open()) { + return false; + } +# endif + + OStreamWrapper osw(ofs); + PrettyWriter writer(osw); + writer.SetFormatOptions(kFormatSingleLineArray); + + doc.Accept(writer); + + return true; +} diff --git a/src/base/io/log/Log.cpp b/src/base/io/log/Log.cpp new file mode 100644 index 00000000..250bc3c4 --- /dev/null +++ b/src/base/io/log/Log.cpp @@ -0,0 +1,248 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2019 Spudz76 + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#ifdef WIN32 +# include +# include +#endif + + +#include +#include +#include +#include +#include +#include +#include + + +#include "base/io/log/Log.h" +#include "base/kernel/interfaces/ILogBackend.h" +#include "base/tools/Chrono.h" + + +namespace xmrig { + + +static const char *colors_map[] = { + RED_BOLD_S, // EMERG + RED_BOLD_S, // ALERT + RED_BOLD_S, // CRIT + RED_S, // ERR + YELLOW_S, // WARNING + WHITE_BOLD_S, // NOTICE + nullptr, // INFO +# ifdef WIN32 + BLACK_BOLD_S // DEBUG +# else + BRIGHT_BLACK_S // DEBUG +# endif +}; + + + +class LogPrivate +{ +public: + inline LogPrivate() : + m_buf() + { + } + + + inline ~LogPrivate() + { + for (ILogBackend *backend : m_backends) { + delete backend; + } + } + + + inline void add(ILogBackend *backend) { m_backends.push_back(backend); } + + + void print(Log::Level level, const char *fmt, va_list args) + { + size_t size = 0; + size_t offset = 0; + + std::lock_guard lock(m_mutex); + + if (Log::background && m_backends.empty()) { + return; + } + + timestamp(level, size, offset); + color(level, size); + + const int rc = vsnprintf(m_buf + size, sizeof (m_buf) - offset - 32, fmt, args); + if (rc < 0) { + return; + } + + size += std::min(static_cast(rc), sizeof (m_buf) - offset - 32); + endl(size); + + std::string txt(m_buf); + size_t i; + while ((i = txt.find(CSI)) != std::string::npos) { + txt.erase(i, txt.find('m', i) - i + 1); + } + + if (!m_backends.empty()) { + for (ILogBackend *backend : m_backends) { + backend->print(level, m_buf, offset, size, true); + backend->print(level, txt.c_str(), offset ? (offset - 11) : 0, txt.size(), false); + } + } + else { + fputs(txt.c_str(), stdout); + fflush(stdout); + } + } + + +private: + inline void timestamp(Log::Level level, size_t &size, size_t &offset) + { + if (level == Log::NONE) { + return; + } + + const uint64_t ms = Chrono::currentMSecsSinceEpoch(); + time_t now = ms / 1000; + tm stime; + +# ifdef _WIN32 + localtime_s(&stime, &now); +# else + localtime_r(&now, &stime); +# endif + + const int rc = snprintf(m_buf, sizeof(m_buf) - 1, "[%d-%02d-%02d %02d:%02d:%02d" BLACK_BOLD(".%03d") "] ", + stime.tm_year + 1900, + stime.tm_mon + 1, + stime.tm_mday, + stime.tm_hour, + stime.tm_min, + stime.tm_sec, + static_cast(ms % 1000) + ); + + if (rc > 0) { + size = offset = static_cast(rc); + } + } + + + inline void color(Log::Level level, size_t &size) + { + if (level == Log::NONE) { + return; + } + + const char *color = colors_map[level]; + if (color == nullptr) { + return; + } + + const size_t s = strlen(color); + memcpy(m_buf + size, color, s); + + size += s; + } + + + inline void endl(size_t &size) + { +# ifdef _WIN32 + memcpy(m_buf + size, CLEAR "\r\n", 7); + size += 6; +# else + memcpy(m_buf + size, CLEAR "\n", 6); + size += 5; +# endif + } + + + char m_buf[4096]; + std::mutex m_mutex; + std::vector m_backends; +}; + + +bool Log::background = false; +bool Log::colors = true; +LogPrivate *Log::d = new LogPrivate(); + + +} /* namespace xmrig */ + + + +void xmrig::Log::add(ILogBackend *backend) +{ + if (d) { + d->add(backend); + } +} + + +void xmrig::Log::destroy() +{ + delete d; + d = nullptr; +} + + +void xmrig::Log::print(const char *fmt, ...) +{ + if (!d) { + return; + } + + va_list args; + va_start(args, fmt); + + d->print(NONE, fmt, args); + + va_end(args); +} + + +void xmrig::Log::print(Level level, const char *fmt, ...) +{ + if (!d) { + return; + } + + va_list args; + va_start(args, fmt); + + d->print(level, fmt, args); + + va_end(args); +} diff --git a/src/base/io/log/Log.h b/src/base/io/log/Log.h new file mode 100644 index 00000000..3517b61d --- /dev/null +++ b/src/base/io/log/Log.h @@ -0,0 +1,144 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2019 Spudz76 + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_LOG_H +#define XMRIG_LOG_H + + +namespace xmrig { + + +class ILogBackend; +class LogPrivate; + + +class Log +{ +public: + enum Level : int { + NONE = -1, + EMERG, // system is unusable + ALERT, // action must be taken immediately + CRIT, // critical conditions + ERR, // error conditions + WARNING, // warning conditions + NOTICE, // normal but significant condition + INFO, // informational + DEBUG, // debug-level messages + }; + + static void add(ILogBackend *backend); + static void destroy(); + static void print(const char *fmt, ...); + static void print(Level level, const char *fmt, ...); + + static bool background; + static bool colors; + +private: + static LogPrivate *d; +}; + + +#define CSI "\x1B[" // Control Sequence Introducer (ANSI spec name) +#define CLEAR CSI "0m" // all attributes off +#define BRIGHT_BLACK_S CSI "0;90m" // somewhat MD.GRAY +#define BLACK_S CSI "0;30m" +#define BLACK_BOLD_S CSI "1;30m" // another name for GRAY +#define RED_S CSI "0;31m" +#define RED_BOLD_S CSI "1;31m" +#define GREEN_S CSI "0;32m" +#define GREEN_BOLD_S CSI "1;32m" +#define YELLOW_S CSI "0;33m" +#define YELLOW_BOLD_S CSI "1;33m" +#define BLUE_S CSI "0;34m" +#define BLUE_BOLD_S CSI "1;34m" +#define MAGENTA_S CSI "0;35m" +#define MAGENTA_BOLD_S CSI "1;35m" +#define CYAN_S CSI "0;36m" +#define CYAN_BOLD_S CSI "1;36m" +#define WHITE_S CSI "0;37m" // another name for LT.GRAY +#define WHITE_BOLD_S CSI "1;37m" // actually white + +#define BLUE_BG_S CSI "44m" +#define BLUE_BG_BOLD_S CSI "44;1m" +#define MAGENTA_BG_S CSI "45m" +#define MAGENTA_BG_BOLD_S CSI "45;1m" +#define CYAN_BG_S CSI "46m" +#define CYAN_BG_BOLD_S CSI "46;1m" + +//color wrappings +#define BLACK(x) BLACK_S x CLEAR +#define BLACK_BOLD(x) BLACK_BOLD_S x CLEAR +#define RED(x) RED_S x CLEAR +#define RED_BOLD(x) RED_BOLD_S x CLEAR +#define GREEN(x) GREEN_S x CLEAR +#define GREEN_BOLD(x) GREEN_BOLD_S x CLEAR +#define YELLOW(x) YELLOW_S x CLEAR +#define YELLOW_BOLD(x) YELLOW_BOLD_S x CLEAR +#define BLUE(x) BLUE_S x CLEAR +#define BLUE_BOLD(x) BLUE_BOLD_S x CLEAR +#define MAGENTA(x) MAGENTA_S x CLEAR +#define MAGENTA_BOLD(x) MAGENTA_BOLD_S x CLEAR +#define CYAN(x) CYAN_S x CLEAR +#define CYAN_BOLD(x) CYAN_BOLD_S x CLEAR +#define WHITE(x) WHITE_S x CLEAR +#define WHITE_BOLD(x) WHITE_BOLD_S x CLEAR + +#define BLUE_BG(x) BLUE_BG_S x CLEAR +#define BLUE_BG_BOLD(x) BLUE_BG_BOLD_S x CLEAR +#define MAGENTA_BG(x) MAGENTA_BG_S x CLEAR +#define MAGENTA_BG_BOLD(x) MAGENTA_BG_BOLD_S x CLEAR +#define CYAN_BG(x) CYAN_BG_S x CLEAR +#define CYAN_BG_BOLD(x) CYAN_BG_BOLD_S x CLEAR + + +#define LOG_EMERG(x, ...) xmrig::Log::print(xmrig::Log::EMERG, x, ##__VA_ARGS__) +#define LOG_ALERT(x, ...) xmrig::Log::print(xmrig::Log::ALERT, x, ##__VA_ARGS__) +#define LOG_CRIT(x, ...) xmrig::Log::print(xmrig::Log::CRIT, x, ##__VA_ARGS__) +#define LOG_ERR(x, ...) xmrig::Log::print(xmrig::Log::ERR, x, ##__VA_ARGS__) +#define LOG_WARN(x, ...) xmrig::Log::print(xmrig::Log::WARNING, x, ##__VA_ARGS__) +#define LOG_NOTICE(x, ...) xmrig::Log::print(xmrig::Log::NOTICE, x, ##__VA_ARGS__) +#define LOG_INFO(x, ...) xmrig::Log::print(xmrig::Log::INFO, x, ##__VA_ARGS__) + +#ifdef APP_DEBUG +# define LOG_DEBUG(x, ...) xmrig::Log::print(xmrig::Log::DEBUG, x, ##__VA_ARGS__) +#else +# define LOG_DEBUG(x, ...) +#endif + +#if defined(APP_DEBUG) || defined(APP_DEVEL) +# define LOG_DEBUG_ERR(x, ...) xmrig::Log::print(xmrig::Log::ERR, x, ##__VA_ARGS__) +# define LOG_DEBUG_WARN(x, ...) xmrig::Log::print(xmrig::Log::WARNING, x, ##__VA_ARGS__) +#else +# define LOG_DEBUG_ERR(x, ...) +# define LOG_DEBUG_WARN(x, ...) +#endif + + +} /* namespace xmrig */ + + +#endif /* XMRIG_LOG_H */ diff --git a/src/base/io/log/backends/ConsoleLog.cpp b/src/base/io/log/backends/ConsoleLog.cpp new file mode 100644 index 00000000..a5b6c1a7 --- /dev/null +++ b/src/base/io/log/backends/ConsoleLog.cpp @@ -0,0 +1,97 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2019 Spudz76 + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include + + +#include "base/tools/Handle.h" +#include "base/io/log/backends/ConsoleLog.h" +#include "base/io/log/Log.h" + + +xmrig::ConsoleLog::ConsoleLog() : + m_stream(nullptr) +{ + m_tty = new uv_tty_t; + + if (uv_tty_init(uv_default_loop(), m_tty, 1, 0) < 0) { + Log::colors = false; + return; + } + + uv_tty_set_mode(m_tty, UV_TTY_MODE_NORMAL); + m_stream = reinterpret_cast(m_tty); + +# ifdef WIN32 + HANDLE handle = GetStdHandle(STD_INPUT_HANDLE); + if (handle != INVALID_HANDLE_VALUE) { + DWORD mode = 0; + if (GetConsoleMode(handle, &mode)) { + mode &= ~ENABLE_QUICK_EDIT_MODE; + SetConsoleMode(handle, mode | ENABLE_EXTENDED_FLAGS); + } + } +# endif +} + + +xmrig::ConsoleLog::~ConsoleLog() +{ + Handle::close(m_tty); +} + + +void xmrig::ConsoleLog::print(int, const char *line, size_t, size_t size, bool colors) +{ + if (Log::colors != colors) { + return; + } + +# ifdef _WIN32 + uv_buf_t buf = uv_buf_init(const_cast(line), static_cast(size)); +# else + uv_buf_t buf = uv_buf_init(const_cast(line), size); +# endif + + if (!isWritable()) { + fputs(line, stdout); + fflush(stdout); + } + else { + uv_try_write(m_stream, &buf, 1); + } +} + + +bool xmrig::ConsoleLog::isWritable() const +{ + if (!m_stream || uv_is_writable(m_stream) != 1) { + return false; + } + + const uv_handle_type type = uv_guess_handle(1); + return type == UV_TTY || type == UV_NAMED_PIPE; +} diff --git a/src/base/io/log/backends/ConsoleLog.h b/src/base/io/log/backends/ConsoleLog.h new file mode 100644 index 00000000..90e4fa14 --- /dev/null +++ b/src/base/io/log/backends/ConsoleLog.h @@ -0,0 +1,60 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2019 Spudz76 + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_CONSOLELOG_H +#define XMRIG_CONSOLELOG_H + + +typedef struct uv_stream_s uv_stream_t; +typedef struct uv_tty_s uv_tty_t; + + +#include "base/kernel/interfaces/ILogBackend.h" + + +namespace xmrig { + + +class ConsoleLog : public ILogBackend +{ +public: + ConsoleLog(); + ~ConsoleLog() override; + +protected: + void print(int level, const char *line, size_t offset, size_t size, bool colors) override; + +private: + bool isWritable() const; + + uv_stream_t *m_stream; + uv_tty_t *m_tty; +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_CONSOLELOG_H */ diff --git a/src/log/FileLog.cpp b/src/base/io/log/backends/FileLog.cpp similarity index 53% rename from src/log/FileLog.cpp rename to src/base/io/log/backends/FileLog.cpp index 8450a51b..1ff01637 100644 --- a/src/log/FileLog.cpp +++ b/src/base/io/log/backends/FileLog.cpp @@ -4,8 +4,10 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2019 Spudz76 + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , * * 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,18 +24,14 @@ */ -#include -#include -#include -#include #include -#include +#include -#include "log/FileLog.h" +#include "base/io/log/backends/FileLog.h" -FileLog::FileLog(const char *fileName) +xmrig::FileLog::FileLog(const char *fileName) { uv_fs_t req; m_file = uv_fs_open(uv_default_loop(), &req, fileName, O_CREAT | O_APPEND | O_WRONLY, 0644, nullptr); @@ -41,63 +39,29 @@ FileLog::FileLog(const char *fileName) } -void FileLog::message(int level, const char* fmt, va_list args) +void xmrig::FileLog::print(int, const char *line, size_t, size_t size, bool colors) { - if (m_file < 0) { + if (m_file < 0 || colors) { return; } - time_t now = time(nullptr); - tm stime; - # ifdef _WIN32 - localtime_s(&stime, &now); + uv_buf_t buf = uv_buf_init(strdup(line), static_cast(size)); # else - localtime_r(&now, &stime); + uv_buf_t buf = uv_buf_init(strdup(line), size); # endif - snprintf(m_fmt, sizeof(m_fmt) - 1, "[%d-%02d-%02d %02d:%02d:%02d] %s", - stime.tm_year + 1900, - stime.tm_mon + 1, - stime.tm_mday, - stime.tm_hour, - stime.tm_min, - stime.tm_sec, - fmt); + uv_fs_t *req = new uv_fs_t; + req->data = buf.base; - auto *buf = new char[kBufferSize]; - const int size = vsnprintf(buf, kBufferSize - 1, m_fmt, args); - buf[size] = '\n'; - - std::string row = std::regex_replace(std::string(buf, static_cast(size + 1)), std::regex("\x1B\\[[0-9;]*[a-zA-Z]"), ""); - - memcpy(buf, row.c_str(), row.length()); - - write(buf, row.length()); + uv_fs_write(uv_default_loop(), req, m_file, &buf, 1, -1, FileLog::onWrite); } -void FileLog::text(const char* fmt, va_list args) -{ - message(0, fmt, args); -} - - - -void FileLog::onWrite(uv_fs_t *req) +void xmrig::FileLog::onWrite(uv_fs_t *req) { delete [] static_cast(req->data); uv_fs_req_cleanup(req); delete req; } - - -void FileLog::write(char *data, size_t size) -{ - uv_buf_t buf = uv_buf_init(data, (unsigned int) size); - uv_fs_t *req = new uv_fs_t; - req->data = buf.base; - - uv_fs_write(uv_default_loop(), req, m_file, &buf, 1, -1, FileLog::onWrite); -} diff --git a/src/base/io/log/backends/FileLog.h b/src/base/io/log/backends/FileLog.h new file mode 100644 index 00000000..188a99aa --- /dev/null +++ b/src/base/io/log/backends/FileLog.h @@ -0,0 +1,58 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2019 Spudz76 + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_FILELOG_H +#define XMRIG_FILELOG_H + + +typedef struct uv_fs_s uv_fs_t; + + +#include "base/kernel/interfaces/ILogBackend.h" + + +namespace xmrig { + + +class FileLog : public ILogBackend +{ +public: + FileLog(const char *fileName); + + +protected: + void print(int level, const char *line, size_t offset, size_t size, bool colors) override; + +private: + static void onWrite(uv_fs_t *req); + + int m_file; +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_FILELOG_H */ diff --git a/src/base/io/log/backends/RemoteLog.cpp b/src/base/io/log/backends/RemoteLog.cpp new file mode 100644 index 00000000..1770e832 --- /dev/null +++ b/src/base/io/log/backends/RemoteLog.cpp @@ -0,0 +1,82 @@ +/* XMRigCC + * Copyright 2018- BenDr0id , + * + * 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 . + */ + + +#include +#include +#include + +#include "base/tools/Handle.h" +#include "base/io/log/backends/RemoteLog.h" +#include "base/io/log/Log.h" + +xmrig::RemoteLog* xmrig::RemoteLog::m_self = nullptr; + +xmrig::RemoteLog::RemoteLog(size_t maxRows) + : m_maxRows(maxRows) +{ + m_self = this; +} + +xmrig::RemoteLog::~RemoteLog() +{ + m_self = nullptr; +} + +void xmrig::RemoteLog::print(int, const char *line, size_t, size_t size, bool colors) +{ + if (colors) { + return; + } + +# ifdef _WIN32 + uv_buf_t buf = uv_buf_init(strdup(line), static_cast(size)); +# else + uv_buf_t buf = uv_buf_init(strdup(line), size); +# endif + + m_mutex.lock(); + + if (m_rows.size() == m_maxRows) { + m_rows.pop_front(); + } + + m_rows.push_back(std::string(buf.base)); + + m_mutex.unlock(); + + delete[](buf.base); +} + +std::string xmrig::RemoteLog::getRows() +{ + std::stringstream data; + + if (m_self) { + m_self->m_mutex.lock(); + + for (auto& m_row : m_self->m_rows) { + data << m_row.c_str(); + } + m_self->m_rows.clear(); + + m_self->m_mutex.unlock(); + } + + return data.str(); +} + diff --git a/src/log/RemoteLog.h b/src/base/io/log/backends/RemoteLog.h similarity index 68% rename from src/log/RemoteLog.h rename to src/base/io/log/backends/RemoteLog.h index 973cc8f5..e59e4a80 100644 --- a/src/log/RemoteLog.h +++ b/src/base/io/log/backends/RemoteLog.h @@ -1,5 +1,5 @@ -/* XMRig - * Copyright 2018- BenDr0id +/* XMRigCC + * Copyright 2018- BenDr0id , * * 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 @@ -15,35 +15,40 @@ * along with this program. If not, see . */ -#ifndef __REMOTELOG_H__ -#define __REMOTELOG_H__ - +#ifndef XMRIG_REMOTELOG_H +#define XMRIG_REMOTELOG_H #include +#include #include #include -#include "interfaces/ILogBackend.h" +#include "base/kernel/interfaces/ILogBackend.h" + +namespace xmrig { class RemoteLog : public ILogBackend { public: RemoteLog(size_t maxRows); - ~RemoteLog(); - - void message(int level, const char* fmt, va_list args) override; - void text(const char* fmt, va_list args) override; + ~RemoteLog() override; static std::string getRows(); +protected: + void print(int level, const char *line, size_t offset, size_t size, bool colors) override; private: static RemoteLog* m_self; - uv_mutex_t m_mutex; size_t m_maxRows; std::list m_rows; + std::mutex m_mutex; }; -#endif /* __REMOTELOG_H__ */ + +} /* namespace xmrig */ + + +#endif /* XMRIG_REMOTELOG_H */ diff --git a/src/workers/Handle.cpp b/src/base/io/log/backends/SysLog.cpp similarity index 58% rename from src/workers/Handle.cpp rename to src/base/io/log/backends/SysLog.cpp index 89906a36..e66f2e35 100644 --- a/src/workers/Handle.cpp +++ b/src/base/io/log/backends/SysLog.cpp @@ -4,8 +4,10 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2019 Spudz76 + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , * * 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,26 +24,30 @@ */ -#include "workers/Handle.h" +#include -Handle::Handle(size_t threadId, size_t threads, int64_t affinity, int priority) : - m_priority(priority), - m_threadId(threadId), - m_threads(threads), - m_affinity(affinity), - m_worker(nullptr) +#include "base/io/log/backends/SysLog.h" +#include "version.h" + + +xmrig::SysLog::SysLog() { + openlog(APP_ID, LOG_PID, LOG_USER); } -void Handle::join() +xmrig::SysLog::~SysLog() { - uv_thread_join(&m_thread); + closelog(); } -void Handle::start(void (*callback) (void *)) +void xmrig::SysLog::print(int level, const char *line, size_t offset, size_t, bool colors) { - uv_thread_create(&m_thread, callback, this); + if (colors) { + return; + } + + syslog(level == -1 ? LOG_INFO : level, "%s", line + offset); } diff --git a/src/base/io/log/backends/SysLog.h b/src/base/io/log/backends/SysLog.h new file mode 100644 index 00000000..d131784b --- /dev/null +++ b/src/base/io/log/backends/SysLog.h @@ -0,0 +1,50 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2019 Spudz76 + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_SYSLOG_H +#define XMRIG_SYSLOG_H + + +#include "base/kernel/interfaces/ILogBackend.h" + + +namespace xmrig { + + +class SysLog : public ILogBackend +{ +public: + SysLog(); + ~SysLog() override; + +protected: + void print(int level, const char *line, size_t offset, size_t size, bool colors) override; +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_SYSLOG_H */ diff --git a/src/base/kernel/Base.cpp b/src/base/kernel/Base.cpp new file mode 100644 index 00000000..f7acb2ff --- /dev/null +++ b/src/base/kernel/Base.cpp @@ -0,0 +1,381 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include +#include +#include + + +#include "base/io/json/Json.h" +#include "base/io/json/JsonChain.h" +#include "base/io/log/backends/ConsoleLog.h" +#include "base/io/log/backends/FileLog.h" +#include "base/io/log/Log.h" +#include "base/io/Watcher.h" +#include "base/kernel/Base.h" +#include "base/kernel/interfaces/IBaseListener.h" +#include "base/kernel/Platform.h" +#include "base/kernel/Process.h" +#include "core/config/Config.h" +#include "core/config/ConfigTransform.h" + +#include "version.h" + +#ifdef HAVE_SYSLOG_H +# include "base/io/log/backends/SysLog.h" +#endif + + +#ifdef XMRIG_FEATURE_API +# include "base/api/Api.h" +# include "base/api/interfaces/IApiRequest.h" + +namespace xmrig { + +static const char *kConfigPathV1 = "/1/config"; +static const char *kConfigPathV2 = "/2/config"; + +} // namespace xmrig +#endif + + +#ifdef XMRIG_FEATURE_EMBEDDED_CONFIG +# include "core/config/Config_default.h" +#endif + +#ifdef XMRIG_FEATURE_CC_CLIENT +# include "cc/CCClient.h" +#endif + +class xmrig::BasePrivate +{ +public: + inline BasePrivate(Process *process) : + api(nullptr), + ccClient(nullptr), + config(nullptr), + process(process), + watcher(nullptr) + {} + + + inline ~BasePrivate() + { +# ifdef XMRIG_FEATURE_API + delete api; +# endif + +# ifdef XMRIG_FEATURE_CC_CLIENT + delete ccClient; +# endif + + delete config; + delete watcher; + } + + + inline bool read(const JsonChain &chain, std::unique_ptr &config) + { + config = std::unique_ptr(new Config()); + + return config->read(chain, chain.fileName()); + } + + + inline Config *load() + { + JsonChain chain; + ConfigTransform transform; + std::unique_ptr config; + + transform.load(chain, process, transform); + + if (read(chain, config)) { + return config.release(); + } + + chain.addFile(process->location(Process::ExeLocation, "config.json")); + + if (read(chain, config)) { + return config.release(); + } + +# ifdef XMRIG_FEATURE_EMBEDDED_CONFIG + chain.addRaw(default_config); + + if (read(chain, config)) { + return config.release(); + } +# endif + + return nullptr; + } + + + inline void replace(Config *newConfig) + { + Config *previousConfig = config; + config = newConfig; + + for (IBaseListener *listener : listeners) { + listener->onConfigChanged(config, previousConfig); + } + + delete previousConfig; + } + + + Api *api; + CCClient *ccClient; + Config *config; + Process *process; + std::vector listeners; + Watcher *watcher; +}; + + +xmrig::Base::Base(Process *process) + : d_ptr(new BasePrivate(process)) +{ +} + + +xmrig::Base::~Base() +{ + delete d_ptr; +} + + +bool xmrig::Base::isReady() const +{ + return d_ptr->config != nullptr; +} + + +int xmrig::Base::init() +{ + d_ptr->config = d_ptr->load(); + + if (!d_ptr->config) { + LOG_EMERG("No valid configuration found. Exiting."); + + return 1; + } + +#ifndef XMRIG_NO_CC + if (!d_ptr->config->isDaemonized()) { + LOG_EMERG(APP_ID " is compiled with CC support, please start the daemon instead.\n"); + + return 1; + } +#endif + + Platform::init(config()->userAgent()); + +# ifdef XMRIG_FEATURE_API + d_ptr->api = new Api(this); + d_ptr->api->addListener(this); +# endif + +# ifdef XMRIG_FEATURE_CC_CLIENT + d_ptr->ccClient = new CCClient(this); +# endif + +# ifndef XMRIG_PROXY_PROJECT + Platform::setProcessPriority(config()->cpu().priority()); +# endif + + if (config()->isBackground()) { + Log::background = true; + } + else { + Log::add(new ConsoleLog()); + } + + if (config()->logFile()) { + Log::add(new FileLog(config()->logFile())); + } + +# ifdef XMRIG_FEATURE_CC_CLIENT + if (config()->ccClient().useRemoteLogging()) { + // 20 lines per second should be enough + Log::add(new RemoteLog(static_cast(config()->ccClient().updateInterval() * 20))); + } +# endif + +# ifdef HAVE_SYSLOG_H + if (config()->isSyslog()) { + Log::add(new SysLog()); + } +# endif + + return 0; +} + + +void xmrig::Base::start() +{ +# ifdef XMRIG_FEATURE_API + api()->start(); +# endif + +# ifdef XMRIG_FEATURE_CC_CLIENT + if (d_ptr->config->ccClient().enabled()) { + if (d_ptr->config->ccClient().host() && d_ptr->config->ccClient().port() > 0) { + ccClient()->start(); + } else { + LOG_WARN("Please configure CC-Url and restart. CC feature is now deactivated."); + } + } else { + LOG_WARN("CC feature is disabled."); + } +# endif + + if (config()->isShouldSave()) { + config()->save(); + } + + if (config()->isWatch()) { + d_ptr->watcher = new Watcher(config()->fileName(), this); + } +} + + +void xmrig::Base::stop() +{ +# ifdef XMRIG_FEATURE_API + api()->stop(); +# endif + +# ifdef XMRIG_FEATURE_CC_CLIENT + ccClient()->stop(); +# endif + + delete d_ptr->watcher; + d_ptr->watcher = nullptr; +} + +xmrig::Api *xmrig::Base::api() const +{ + assert(d_ptr->api != nullptr); + + return d_ptr->api; +} + +xmrig::CCClient *xmrig::Base::ccClient() const +{ + assert(d_ptr->ccClient != nullptr); + + return d_ptr->ccClient; +} + +bool xmrig::Base::reload(const rapidjson::Value &json) +{ + JsonReader reader(json); + if (reader.isEmpty()) { + return false; + } + + Config *config = new Config(); + if (!config->read(reader, d_ptr->config->fileName())) { + delete config; + + return false; + } + + const bool saved = config->save(); + + if (config->isWatch() && d_ptr->watcher && saved) { + delete config; + + return true; + } + + d_ptr->replace(config); + + return true; +} + + +xmrig::Config *xmrig::Base::config() const +{ + assert(d_ptr->config != nullptr); + + return d_ptr->config; +} + + +void xmrig::Base::addListener(IBaseListener *listener) +{ + d_ptr->listeners.push_back(listener); +} + + +void xmrig::Base::onFileChanged(const String &fileName) +{ + LOG_WARN("\"%s\" was changed, reloading configuration", fileName.data()); + + JsonChain chain; + chain.addFile(fileName); + + Config *config = new Config(); + + if (!config->read(chain, chain.fileName())) { + LOG_ERR("reloading failed"); + + delete config; + return; + } + + d_ptr->replace(config); +} + + +#ifdef XMRIG_FEATURE_API +void xmrig::Base::onRequest(IApiRequest &request) +{ + if (request.method() == IApiRequest::METHOD_GET) { + if (request.url() == kConfigPathV1 || request.url() == kConfigPathV2) { + if (request.isRestricted()) { + return request.done(403); + } + + request.accept(); + config()->getJSON(request.doc()); + } + } + else if (request.method() == IApiRequest::METHOD_PUT || request.method() == IApiRequest::METHOD_POST) { + if (request.url() == kConfigPathV1 || request.url() == kConfigPathV2) { + request.accept(); + + if (!reload(request.json())) { + return request.done(400); + } + + request.done(204); + } + } +} +#endif diff --git a/src/base/kernel/Base.h b/src/base/kernel/Base.h new file mode 100644 index 00000000..6c4c4925 --- /dev/null +++ b/src/base/kernel/Base.h @@ -0,0 +1,77 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_BASE_H +#define XMRIG_BASE_H + + +#include "base/api/interfaces/IApiListener.h" +#include "base/kernel/interfaces/IConfigListener.h" +#include "base/kernel/interfaces/IWatcherListener.h" +#include "rapidjson/fwd.h" + + +namespace xmrig { + + +class Api; +class BasePrivate; +class Config; +class IBaseListener; +class Process; +class CCClient; + +class Base : public IWatcherListener, public IApiListener +{ +public: + Base(Process *process); + ~Base() override; + + virtual bool isReady() const; + virtual int init(); + virtual void start(); + virtual void stop(); + + Api *api() const; + CCClient *ccClient() const; + bool reload(const rapidjson::Value &json); + Config *config() const; + void addListener(IBaseListener *listener); + +protected: + void onFileChanged(const String &fileName) override; + +# ifdef XMRIG_FEATURE_API + void onRequest(IApiRequest &request) override; +# endif + +private: + BasePrivate *d_ptr; +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_BASE_H */ diff --git a/src/base/kernel/Entry.cpp b/src/base/kernel/Entry.cpp new file mode 100644 index 00000000..f9e97c2d --- /dev/null +++ b/src/base/kernel/Entry.cpp @@ -0,0 +1,169 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include +#include + + +#ifdef XMRIG_FEATURE_TLS +# include +#endif + +#ifdef XMRIG_FEATURE_HWLOC +# include +#endif + +#include "base/kernel/Entry.h" +#include "base/kernel/Process.h" +#include "core/config/usage.h" +#include "version.h" + + +namespace xmrig { + + +static int showVersion() +{ + printf(APP_NAME " " APP_VERSION "\n built on " __DATE__ + +# if defined(__clang__) + " with clang " __clang_version__); +# elif defined(__GNUC__) + " with GCC"); + printf(" %d.%d.%d", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__); +# elif defined(_MSC_VER) + " with MSVC"); + printf(" %d", MSVC_VERSION); +# else + ); +# endif + + printf("\n features:" +# if defined(__i386__) || defined(_M_IX86) + " 32-bit" +# elif defined(__x86_64__) || defined(_M_AMD64) + " 64-bit" +# endif + +# if defined(__AES__) || defined(_MSC_VER) + " AES" +# endif + "\n"); + + printf("\nlibuv/%s\n", uv_version_string()); + +# if defined(XMRIG_FEATURE_TLS) && defined(OPENSSL_VERSION_TEXT) + { + constexpr const char *v = OPENSSL_VERSION_TEXT + 8; + printf("OpenSSL/%.*s\n", static_cast(strchr(v, ' ') - v), v); + } +# endif + +# if defined(XMRIG_FEATURE_HWLOC) +# if defined(HWLOC_VERSION) + printf("hwloc/%s\n", HWLOC_VERSION); +# elif HWLOC_API_VERSION >= 0x20000 + printf("hwloc/2\n"); +# else + printf("hwloc/1\n"); +# endif +# endif + + return 0; +} + + +#ifdef XMRIG_FEATURE_HWLOC +static int exportTopology(const Process &process) +{ + const String path = process.location(Process::ExeLocation, "topology.xml"); + + hwloc_topology_t topology; + hwloc_topology_init(&topology); + hwloc_topology_load(topology); + +# if HWLOC_API_VERSION >= 0x20000 + if (hwloc_topology_export_xml(topology, path, 0) == -1) { +# else + if (hwloc_topology_export_xml(topology, path) == -1) { +# endif + printf("failed to export hwloc topology.\n"); + } + else { + printf("hwloc topology successfully exported to \"%s\"\n", path.data()); + } + + hwloc_topology_destroy(topology); + + return 0; +} +#endif + + +} // namespace xmrig + + +xmrig::Entry::Id xmrig::Entry::get(const Process &process) +{ + const Arguments &args = process.arguments(); + if (args.hasArg("-h") || args.hasArg("--help")) { + return Usage; + } + + if (args.hasArg("-V") || args.hasArg("--version")) { + return Version; + } + +# ifdef XMRIG_FEATURE_HWLOC + if (args.hasArg("--export-topology")) { + return Topo; + } +# endif + + return Default; +} + + +int xmrig::Entry::exec(const Process &process, Id id) +{ + switch (id) { + case Usage: + printf(usage); + return 0; + + case Version: + return showVersion(); + +# ifdef XMRIG_FEATURE_HWLOC + case Topo: + return exportTopology(process); +# endif + + default: + break; + } + + return 1; +} diff --git a/src/base/kernel/Entry.h b/src/base/kernel/Entry.h new file mode 100644 index 00000000..c0bde080 --- /dev/null +++ b/src/base/kernel/Entry.h @@ -0,0 +1,53 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_ENTRY_H +#define XMRIG_ENTRY_H + + +namespace xmrig { + + +class Process; + + +class Entry +{ +public: + enum Id { + Default, + Usage, + Version, + Topo + }; + + static Id get(const Process &process); + static int exec(const Process &process, Id id); +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_ENTRY_H */ diff --git a/src/Platform.cpp b/src/base/kernel/Platform.cpp similarity index 58% rename from src/Platform.cpp rename to src/base/kernel/Platform.cpp index cb4ad3a0..a74f1978 100644 --- a/src/Platform.cpp +++ b/src/base/kernel/Platform.cpp @@ -4,8 +4,9 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , * * 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,41 +27,33 @@ #include +#ifdef XMRIG_FEATURE_TLS +# include +# include +#endif + + #include "Platform.h" -char *Platform::m_defaultConfigName = nullptr; -char *Platform::m_userAgent = nullptr; +xmrig::String Platform::m_userAgent; -const char *Platform::defaultConfigName() +void Platform::init(const char *userAgent) { - size_t size = 520; +# ifdef XMRIG_FEATURE_TLS + SSL_library_init(); + SSL_load_error_strings(); + ERR_load_BIO_strings(); + ERR_load_crypto_strings(); + SSL_load_error_strings(); + OpenSSL_add_all_digests(); +# endif - if (m_defaultConfigName == nullptr) { - m_defaultConfigName = new char[size]; + if (userAgent) { + m_userAgent = userAgent; } - - if (uv_exepath(m_defaultConfigName, &size) < 0) { - return nullptr; + else { + m_userAgent = createUserAgent(); } - - if (size < 500) { -# ifdef WIN32 - char *p = strrchr(m_defaultConfigName, '\\'); -# else - char *p = strrchr(m_defaultConfigName, '/'); -# endif - - if (p) { -#ifdef XMRIG_CC_SERVER - strcpy(p + 1, "config_cc.json"); -#else - strcpy(p + 1, "config.json"); -#endif - return m_defaultConfigName; - } - } - - return nullptr; } diff --git a/src/net/JobId.h b/src/base/kernel/Platform.h similarity index 51% rename from src/net/JobId.h rename to src/base/kernel/Platform.h index 06189779..f3c2c719 100644 --- a/src/net/JobId.h +++ b/src/base/kernel/Platform.h @@ -4,8 +4,9 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , * * 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 @@ -21,63 +22,42 @@ * along with this program. If not, see . */ -#ifndef __JOBID_H__ -#define __JOBID_H__ +#ifndef XMRIG_PLATFORM_H +#define XMRIG_PLATFORM_H -#include +#include -class JobId +#include "base/tools/String.h" + + +class Platform { public: - inline JobId() + static inline bool trySetThreadAffinity(int64_t cpu_id) { - memset(m_data, 0, sizeof(m_data)); - } - - - inline JobId(const char *id, size_t sizeFix = 0) - { - setId(id, sizeFix); - } - - - inline bool operator==(const JobId &other) const - { - return memcmp(m_data, other.m_data, sizeof(m_data)) == 0; - } - - - inline bool operator!=(const JobId &other) const - { - return memcmp(m_data, other.m_data, sizeof(m_data)) != 0; - } - - - inline bool setId(const char *id, size_t sizeFix = 0) - { - memset(m_data, 0, sizeof(m_data)); - if (!id) { + if (cpu_id < 0) { return false; } - const size_t size = strlen(id); - if (size >= sizeof(m_data)) { - return false; - } - - memcpy(m_data, id, size - sizeFix); - return true; + return setThreadAffinity(static_cast(cpu_id)); } + static bool setThreadAffinity(uint64_t cpu_id); + static uint32_t setTimerResolution(uint32_t resolution); + static void init(const char *userAgent); + static void restoreTimerResolution(); + static void setProcessPriority(int priority); + static void setThreadPriority(int priority); - inline const char *data() const { return m_data; } - inline bool isValid() const { return *m_data != '\0'; } - + static inline const char *userAgent() { return m_userAgent; } private: - char m_data[64]; + static char *createUserAgent(); + + static xmrig::String m_userAgent; }; -#endif /* __JOBID_H__ */ + +#endif /* XMRIG_PLATFORM_H */ diff --git a/src/Platform_unix.cpp b/src/base/kernel/Platform_mac.cpp similarity index 68% rename from src/Platform_unix.cpp rename to src/base/kernel/Platform_mac.cpp index 9d818ea0..4e4aa0ad 100644 --- a/src/Platform_unix.cpp +++ b/src/base/kernel/Platform_mac.cpp @@ -21,9 +21,11 @@ * along with this program. If not, see . */ -#include -#include -#include + +#include +#include +#include +#include #include #include @@ -36,25 +38,21 @@ #endif -static inline char *createUserAgent() +char *Platform::createUserAgent() { - const size_t max = 160; + constexpr const size_t max = 256; - char *buf = new char[max]; - int length = snprintf(buf, max, "%s/%s (Linux ", APP_NAME, APP_VERSION); - -# if defined(__x86_64__) - length += snprintf(buf + length, max - length, "x86_64) libuv/%s", uv_version_string()); -# else - length += snprintf(buf + length, max - length, "i686) libuv/%s", uv_version_string()); -# endif + char *buf = new char[max](); + int length = snprintf(buf, max, "%s/%s (Macintosh; Intel Mac OS X) libuv/%s", APP_NAME, APP_VERSION, uv_version_string()); # ifdef XMRIG_NVIDIA_PROJECT const int cudaVersion = cuda_get_runtime_version(); length += snprintf(buf + length, max - length, " CUDA/%d.%d", cudaVersion / 1000, cudaVersion % 100); # endif -# ifdef __GNUC__ +# ifdef __clang__ + length += snprintf(buf + length, max - length, " clang/%d.%d.%d", __clang_major__, __clang_minor__, __clang_patchlevel__); +# elif defined(__GNUC__) length += snprintf(buf + length, max - length, " gcc/%d.%d.%d", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__); # endif @@ -62,15 +60,24 @@ static inline char *createUserAgent() } -void Platform::init(const char *userAgent) +bool Platform::setThreadAffinity(uint64_t cpu_id) { - m_userAgent = userAgent ? strdup(userAgent) : createUserAgent(); + thread_port_t mach_thread; + thread_affinity_policy_data_t policy = { static_cast(cpu_id) }; + mach_thread = pthread_mach_thread_np(pthread_self()); + + return thread_policy_set(mach_thread, THREAD_AFFINITY_POLICY, (thread_policy_t)&policy, 1) == KERN_SUCCESS; } -void Platform::release() +uint32_t Platform::setTimerResolution(uint32_t resolution) +{ + return resolution; +} + + +void Platform::restoreTimerResolution() { - delete [] m_userAgent; } @@ -79,7 +86,6 @@ void Platform::setProcessPriority(int priority) } - void Platform::setThreadPriority(int priority) { if (priority == -1) { @@ -114,15 +120,5 @@ void Platform::setThreadPriority(int priority) } setpriority(PRIO_PROCESS, 0, prio); - -# ifdef SCHED_IDLE - if (priority == 0) { - sched_param param; - param.sched_priority = 0; - - if (sched_setscheduler(0, SCHED_IDLE, ¶m) != 0) { - sched_setscheduler(0, SCHED_BATCH, ¶m); - } - } -# endif } + diff --git a/src/base/kernel/Platform_unix.cpp b/src/base/kernel/Platform_unix.cpp new file mode 100644 index 00000000..3066630a --- /dev/null +++ b/src/base/kernel/Platform_unix.cpp @@ -0,0 +1,162 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2016-2018 XMRig , + * + * 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 . + */ + +#ifdef __FreeBSD__ +# include +# include +# include +# include +#endif + + +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "Platform.h" +#include "version.h" + +#ifdef XMRIG_NVIDIA_PROJECT +# include "nvidia/cryptonight.h" +#endif + + +#ifdef __FreeBSD__ +typedef cpuset_t cpu_set_t; +#endif + + +char *Platform::createUserAgent() +{ + constexpr const size_t max = 256; + + char *buf = new char[max](); + int length = snprintf(buf, max, "%s/%s (Linux ", APP_NAME, APP_VERSION); + +# if defined(__x86_64__) + length += snprintf(buf + length, max - length, "x86_64) libuv/%s", uv_version_string()); +# elif defined(__aarch64__) + length += snprintf(buf + length, max - length, "aarch64) libuv/%s", uv_version_string()); +# elif defined(__arm__) + length += snprintf(buf + length, max - length, "arm) libuv/%s", uv_version_string()); +# else + length += snprintf(buf + length, max - length, "i686) libuv/%s", uv_version_string()); +# endif + +# ifdef XMRIG_NVIDIA_PROJECT + const int cudaVersion = cuda_get_runtime_version(); + length += snprintf(buf + length, max - length, " CUDA/%d.%d", cudaVersion / 1000, cudaVersion % 100); +# endif + +# ifdef __clang__ + length += snprintf(buf + length, max - length, " clang/%d.%d.%d", __clang_major__, __clang_minor__, __clang_patchlevel__); +# elif defined(__GNUC__) + length += snprintf(buf + length, max - length, " gcc/%d.%d.%d", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__); +# endif + + return buf; +} + + +bool Platform::setThreadAffinity(uint64_t cpu_id) +{ + cpu_set_t mn; + CPU_ZERO(&mn); + CPU_SET(cpu_id, &mn); + +# ifndef __ANDROID__ + return pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &mn) == 0; +# else + return sched_setaffinity(gettid(), sizeof(cpu_set_t), &mn) == 0; +# endif +} + + +uint32_t Platform::setTimerResolution(uint32_t resolution) +{ + return resolution; +} + + +void Platform::restoreTimerResolution() +{ +} + + +void Platform::setProcessPriority(int priority) +{ +} + + +void Platform::setThreadPriority(int priority) +{ + if (priority == -1) { + return; + } + + int prio = 19; + switch (priority) + { + case 1: + prio = 5; + break; + + case 2: + prio = 0; + break; + + case 3: + prio = -5; + break; + + case 4: + prio = -10; + break; + + case 5: + prio = -15; + break; + + default: + break; + } + + setpriority(PRIO_PROCESS, 0, prio); + +# ifdef SCHED_IDLE + if (priority == 0) { + sched_param param; + param.sched_priority = 0; + + if (sched_setscheduler(0, SCHED_IDLE, ¶m) != 0) { + sched_setscheduler(0, SCHED_BATCH, ¶m); + } + } +# endif +} diff --git a/src/Platform_win.cpp b/src/base/kernel/Platform_win.cpp similarity index 74% rename from src/Platform_win.cpp rename to src/base/kernel/Platform_win.cpp index 4bd0fb39..f2363cd0 100644 --- a/src/Platform_win.cpp +++ b/src/base/kernel/Platform_win.cpp @@ -4,8 +4,9 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018 SChernykh + * Copyright 2016-2019 XMRig , * * 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,21 +23,27 @@ */ +#include #include #include #include -#include -#include +#include "base/io/log/Log.h" #include "Platform.h" #include "version.h" + #ifdef XMRIG_NVIDIA_PROJECT # include "nvidia/cryptonight.h" #endif +#ifdef XMRIG_AMD_PROJECT +static uint32_t timerResolution = 0; +#endif + + static inline OSVERSIONINFOEX winOsVersion() { typedef NTSTATUS (NTAPI *RtlGetVersionFunction)(LPOSVERSIONINFO); @@ -55,12 +62,12 @@ static inline OSVERSIONINFOEX winOsVersion() } -static inline char *createUserAgent() +char *Platform::createUserAgent() { const auto osver = winOsVersion(); - const size_t max = 160; + constexpr const size_t max = 256; - char *buf = new char[max]; + char *buf = new char[max](); int length = snprintf(buf, max, "%s/%s (Windows NT %lu.%lu", APP_NAME, APP_VERSION, osver.dwMajorVersion, osver.dwMinorVersion); # if defined(__x86_64__) || defined(_M_AMD64) @@ -84,16 +91,41 @@ static inline char *createUserAgent() } -void Platform::init(const char *userAgent) +bool Platform::setThreadAffinity(uint64_t cpu_id) { - m_userAgent = userAgent ? strdup(userAgent) : createUserAgent(); + if (cpu_id >= 64) { + LOG_ERR("Unable to set affinity. Windows supports only affinity up to 63."); + } + + return SetThreadAffinityMask(GetCurrentThread(), 1ULL << cpu_id) != 0; } -void Platform::release() +uint32_t Platform::setTimerResolution(uint32_t resolution) { - delete [] m_defaultConfigName; - delete [] m_userAgent; +# ifdef XMRIG_AMD_PROJECT + TIMECAPS tc; + + if (timeGetDevCaps(&tc, sizeof(TIMECAPS)) != TIMERR_NOERROR) { + return 0; + } + + timerResolution = std::min(std::max(tc.wPeriodMin, resolution), tc.wPeriodMax); + + return timeBeginPeriod(timerResolution) == TIMERR_NOERROR ? timerResolution : 0; +# else + return resolution; +# endif +} + + +void Platform::restoreTimerResolution() +{ +# ifdef XMRIG_AMD_PROJECT + if (timerResolution) { + timeEndPeriod(timerResolution); + } +# endif } @@ -124,6 +156,7 @@ void Platform::setProcessPriority(int priority) case 5: prio = REALTIME_PRIORITY_CLASS; + break; default: break; @@ -133,7 +166,6 @@ void Platform::setProcessPriority(int priority) } - void Platform::setThreadPriority(int priority) { if (priority == -1) { diff --git a/src/base/kernel/Process.cpp b/src/base/kernel/Process.cpp new file mode 100644 index 00000000..9193d378 --- /dev/null +++ b/src/base/kernel/Process.cpp @@ -0,0 +1,102 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include +#include + + +#include "base/kernel/Process.h" +#include "base/tools/Chrono.h" + + +static size_t location(xmrig::Process::Location location, char *buf, size_t max) +{ + using namespace xmrig; + + size_t size = max; + if (location == Process::ExeLocation) { + return uv_exepath(buf, &size) < 0 ? 0 : size; + } + + if (location == Process::CwdLocation) { + return uv_cwd(buf, &size) < 0 ? 0 : size; + } + + return 0; +} + + +xmrig::Process::Process(int argc, char **argv) : + m_arguments(argc, argv) +{ + srand(static_cast(Chrono::currentMSecsSinceEpoch() ^ reinterpret_cast(this))); +} + + +xmrig::Process::~Process() +{ +} + + +xmrig::String xmrig::Process::location(Location location, const char *fileName) const +{ + constexpr const size_t max = 520; + + char *buf = new char[max](); + size_t size = ::location(location, buf, max); + + if (size == 0) { + delete [] buf; + + return String(); + } + + if (fileName == nullptr) { + return buf; + } + + if (location == ExeLocation) { + char *p = strrchr(buf, kDirSeparator); + + if (p == nullptr) { + delete [] buf; + + return String(); + } + + size = static_cast(p - buf); + } + + if ((size + strlen(fileName) + 2) >= max) { + delete [] buf; + + return String(); + } + + buf[size] = kDirSeparator; + strcpy(buf + size + 1, fileName); + + return buf; +} diff --git a/src/base/kernel/Process.h b/src/base/kernel/Process.h new file mode 100644 index 00000000..9b29eb57 --- /dev/null +++ b/src/base/kernel/Process.h @@ -0,0 +1,64 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_PROCESS_H +#define XMRIG_PROCESS_H + + +#include "base/tools/Arguments.h" + + +namespace xmrig { + + +class Process +{ +public: + enum Location { + ExeLocation, + CwdLocation + }; + +# ifdef WIN32 + constexpr const static char kDirSeparator = '\\'; +# else + constexpr const static char kDirSeparator = '/'; +# endif + + Process(int argc, char **argv); + ~Process(); + + String location(Location location, const char *fileName = nullptr) const; + + inline const Arguments &arguments() const { return m_arguments; } + +private: + Arguments m_arguments; +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_PROCESS_H */ diff --git a/src/base/kernel/Signals.cpp b/src/base/kernel/Signals.cpp new file mode 100644 index 00000000..87825b83 --- /dev/null +++ b/src/base/kernel/Signals.cpp @@ -0,0 +1,74 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include + + +#include "base/kernel/interfaces/ISignalListener.h" +#include "base/kernel/Signals.h" +#include "base/tools/Handle.h" + + +static const int signums[xmrig::Signals::kSignalsCount] = { SIGHUP, SIGINT, SIGTERM }; + + +xmrig::Signals::Signals(ISignalListener *listener) + : m_listener(listener) +{ + for (size_t i = 0; i < kSignalsCount; ++i) { + uv_signal_t *signal = new uv_signal_t; + signal->data = this; + + m_signals[i] = signal; + + uv_signal_init(uv_default_loop(), signal); + uv_signal_start(signal, Signals::onSignal, signums[i]); + } +} + + +xmrig::Signals::~Signals() +{ + stop(); +} + + +void xmrig::Signals::stop() +{ + if (!m_signals[0]) { + return; + } + + for (size_t i = 0; i < kSignalsCount; ++i) { + Handle::close(m_signals[i]); + m_signals[i] = nullptr; + } +} + + +void xmrig::Signals::onSignal(uv_signal_t *handle, int signum) +{ + static_cast(handle->data)->m_listener->onSignal(signum); +} diff --git a/src/base/kernel/Signals.h b/src/base/kernel/Signals.h new file mode 100644 index 00000000..9b4a870a --- /dev/null +++ b/src/base/kernel/Signals.h @@ -0,0 +1,64 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_SIGNALS_H +#define XMRIG_SIGNALS_H + + +#include + + +typedef struct uv_signal_s uv_signal_t; + + +namespace xmrig { + + +class ISignalListener; + + +class Signals +{ +public: + constexpr static const size_t kSignalsCount = 3; + + Signals(ISignalListener *listener); + ~Signals(); + + void stop(); + +private: + void close(int signum); + + static void onSignal(uv_signal_t *handle, int signum); + + ISignalListener *m_listener; + uv_signal_t *m_signals[kSignalsCount]; +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_SIGNALS_H */ diff --git a/src/base/kernel/config/BaseConfig.cpp b/src/base/kernel/config/BaseConfig.cpp new file mode 100644 index 00000000..7c6c470b --- /dev/null +++ b/src/base/kernel/config/BaseConfig.cpp @@ -0,0 +1,170 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include +#include +#include +#include +#include + + +#ifdef XMRIG_FEATURE_TLS +# include +#endif + +#ifdef XMRIG_FEATURE_HWLOC +# include "backend/cpu/Cpu.h" +#endif + +#ifdef XMRIG_AMD_PROJECT +# if defined(__APPLE__) +# include +# else +# include "3rdparty/CL/cl.h" +# endif +#endif + + +#ifdef XMRIG_NVIDIA_PROJECT +# include "nvidia/cryptonight.h" +#endif + + +#include "base/io/json/Json.h" +#include "base/io/log/Log.h" +#include "base/kernel/config/BaseConfig.h" +#include "base/kernel/interfaces/IJsonReader.h" +#include "donate.h" +#include "rapidjson/document.h" +#include "rapidjson/filewritestream.h" +#include "rapidjson/prettywriter.h" +#include "version.h" + + +xmrig::BaseConfig::BaseConfig() +{ +} + + +void xmrig::BaseConfig::printVersions() +{ + char buf[256] = { 0 }; + +# if defined(__clang__) + snprintf(buf, sizeof buf, "clang/%d.%d.%d", __clang_major__, __clang_minor__, __clang_patchlevel__); +# elif defined(__GNUC__) + snprintf(buf, sizeof buf, "gcc/%d.%d.%d", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__); +# elif defined(_MSC_VER) + snprintf(buf, sizeof buf, "MSVC/%d", MSVC_VERSION); +# endif + + Log::print(GREEN_BOLD(" * ") WHITE_BOLD("%-13s") CYAN_BOLD("%s/%s") WHITE_BOLD(" %s") BLUE_BOLD(" (%s)"), "ABOUT", APP_NAME, APP_VERSION, buf, BUILD_TYPE); + +# if defined(XMRIG_AMD_PROJECT) +# if CL_VERSION_2_0 + const char *ocl = "2.0"; +# elif CL_VERSION_1_2 + const char *ocl = "1.2"; +# elif CL_VERSION_1_1 + const char *ocl = "1.1"; +# elif CL_VERSION_1_0 + const char *ocl = "1.0"; +# else + const char *ocl = "0.0"; +# endif + int length = snprintf(buf, sizeof buf, "OpenCL/%s ", ocl); +# elif defined(XMRIG_NVIDIA_PROJECT) + const int cudaVersion = cuda_get_runtime_version(); + int length = snprintf(buf, sizeof buf, "CUDA/%d.%d ", cudaVersion / 1000, cudaVersion % 100); +# endif + + std::string libs; + +# if defined(XMRIG_FEATURE_TLS) && defined(OPENSSL_VERSION_TEXT) + { + constexpr const char *v = OPENSSL_VERSION_TEXT + 8; + snprintf(buf, sizeof buf, "OpenSSL/%.*s ", static_cast(strchr(v, ' ') - v), v); + libs += buf; + } +# endif + +# if defined(XMRIG_FEATURE_HWLOC) + libs += Cpu::info()->backend(); +# endif + + Log::print(GREEN_BOLD(" * ") WHITE_BOLD("%-13slibuv/%s %s"), "LIBS", uv_version_string(), libs.c_str()); +} + + +bool xmrig::BaseConfig::read(const IJsonReader &reader, const char *fileName) +{ + m_fileName = fileName; + + if (reader.isEmpty()) { + return false; + } + + m_autoSave = reader.getBool("autosave", m_autoSave); + m_background = reader.getBool("background", m_background); + m_dryRun = reader.getBool("dry-run", m_dryRun); + m_daemonized = reader.getBool("daemonized", m_daemonized); + m_syslog = reader.getBool("syslog", m_syslog); + m_watch = reader.getBool("watch", m_watch); + Log::colors = reader.getBool("colors", Log::colors); + m_logFile = reader.getString("log-file"); + m_userAgent = reader.getString("user-agent"); + m_version = reader.getUint("version"); + + setPrintTime(reader.getUint("print-time", 60)); + + const rapidjson::Value &api = reader.getObject("api"); + if (api.IsObject()) { + m_apiId = Json::getString(api, "id"); + m_apiWorkerId = Json::getString(api, "worker-id"); + } + + m_http.load(reader.getObject("http")); + m_pools.load(reader); + + return m_pools.active() > 0; +} + + +bool xmrig::BaseConfig::save() +{ + if (m_fileName.isNull()) { + return false; + } + + rapidjson::Document doc; + getJSON(doc); + + if (Json::save(m_fileName, doc)) { + LOG_NOTICE("configuration saved to: \"%s\"", m_fileName.data()); + return true; + } + + return false; +} diff --git a/src/base/kernel/config/BaseConfig.h b/src/base/kernel/config/BaseConfig.h new file mode 100644 index 00000000..537f42a1 --- /dev/null +++ b/src/base/kernel/config/BaseConfig.h @@ -0,0 +1,110 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_BASECONFIG_H +#define XMRIG_BASECONFIG_H + +#include "base/tools/String.h" +#include "base/kernel/interfaces/IConfig.h" +#include "base/net/http/Http.h" +#include "base/net/stratum/Pools.h" + +#ifdef XMRIG_FEATURE_CC_CLIENT +# include "cc/CCClientConfig.h" +#endif + +struct option; + + +namespace xmrig { + + +class IJsonReader; + + +class BaseConfig : public IConfig +{ +public: + BaseConfig(); + + inline bool isAutoSave() const { return m_autoSave; } + inline bool isBackground() const { return m_background; } + inline bool isDryRun() const { return m_dryRun; } + inline bool isSyslog() const { return m_syslog; } + inline bool isDaemonized() const { return m_daemonized; } + inline const char *logFile() const { return m_logFile.data(); } + inline const char *userAgent() const { return m_userAgent.data(); } + inline const Http &http() const { return m_http; } + inline const Pools &pools() const { return m_pools; } + +# ifdef XMRIG_FEATURE_CC_CLIENT + inline const CCClientConfig &ccClient() const { return m_ccClient; } +# endif + + inline const String &apiId() const { return m_apiId; } + inline const String &apiWorkerId() const { return m_apiWorkerId; } + inline uint32_t printTime() const { return m_printTime; } + inline uint32_t version() const { return m_version; } + + inline bool isWatch() const override { return m_watch && !m_fileName.isNull(); } + inline const String &fileName() const override { return m_fileName; } + inline void setFileName(const char *fileName) override { m_fileName = fileName; } + + bool read(const IJsonReader &reader, const char *fileName) override; + bool save() override; + + void printVersions(); + +protected: + bool m_autoSave = true; + bool m_background = false; + bool m_dryRun = false; + bool m_syslog = false; + bool m_upgrade = false; + bool m_watch = true; + bool m_daemonized = false; + Http m_http; + Pools m_pools; + +# ifdef XMRIG_FEATURE_CC_CLIENT + CCClientConfig m_ccClient; +# endif + + String m_apiId; + String m_apiWorkerId; + String m_fileName; + String m_logFile; + String m_userAgent; + uint32_t m_printTime = 60; + uint32_t m_version = 0; + +private: + inline void setPrintTime(uint32_t printTime) { if (printTime <= 3600) { m_printTime = printTime; } } +}; + + +} // namespace xmrig + + +#endif /* XMRIG_BASECONFIG_H */ diff --git a/src/base/kernel/config/BaseTransform.cpp b/src/base/kernel/config/BaseTransform.cpp new file mode 100644 index 00000000..15758775 --- /dev/null +++ b/src/base/kernel/config/BaseTransform.cpp @@ -0,0 +1,319 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include + + +#ifdef _MSC_VER +# include "getopt/getopt.h" +#else +# include +#endif + + +#include "base/io/json/JsonChain.h" +#include "base/io/log/Log.h" +#include "base/kernel/config/BaseTransform.h" +#include "base/kernel/interfaces/IConfig.h" +#include "base/kernel/Process.h" +#include "base/net/stratum/Pool.h" +#include "core/config/Config_platform.h" + + +namespace xmrig +{ + +static const char *kAlgo = "algo"; +static const char *kApi = "api"; +static const char *kHttp = "http"; +static const char *kPools = "pools"; +static const char *kCCClient = "cc-client"; + +} + + +xmrig::BaseTransform::BaseTransform() +{ +} + + +void xmrig::BaseTransform::load(JsonChain &chain, Process *process, IConfigTransform &transform) +{ + using namespace rapidjson; + + int key; + int argc = process->arguments().argc(); + char **argv = process->arguments().argv(); + + Document doc(kObjectType); + + while (1) { + key = getopt_long(argc, argv, short_options, options, nullptr); + if (key < 0) { + break; + } + + if (key == IConfig::ConfigKey) { + chain.add(std::move(doc)); + chain.addFile(optarg); + + doc = Document(kObjectType); + } + else { + transform.transform(doc, key, optarg); + } + } + + if (optind < argc) { + LOG_WARN("%s: unsupported non-option argument '%s'", argv[0], argv[optind]); + } + + transform.finalize(doc); + chain.add(std::move(doc)); +} + + +void xmrig::BaseTransform::finalize(rapidjson::Document &doc) +{ + using namespace rapidjson; + auto &allocator = doc.GetAllocator(); + + if (m_algorithm.isValid() && doc.HasMember(kPools)) { + auto &pools = doc[kPools]; + for (Value &pool : pools.GetArray()) { + if (!pool.HasMember(kAlgo)) { + pool.AddMember(StringRef(kAlgo), m_algorithm.toJSON(), allocator); + } + } + } +} + + +void xmrig::BaseTransform::transform(rapidjson::Document &doc, int key, const char *arg) +{ + switch (key) { + case IConfig::AlgorithmKey: /* --algo */ + if (!doc.HasMember(kPools)) { + m_algorithm = arg; + } + else { + return add(doc, kPools, kAlgo, arg); + } + break; + + case IConfig::UserpassKey: /* --userpass */ + { + const char *p = strrchr(arg, ':'); + if (!p) { + return; + } + + char *user = new char[p - arg + 1](); + strncpy(user, arg, static_cast(p - arg)); + + add(doc, kPools, "user", user); + add(doc, kPools, "pass", p + 1); + delete [] user; + } + break; + + case IConfig::UrlKey: /* --url */ + { + if (!doc.HasMember(kPools)) { + doc.AddMember(rapidjson::StringRef(kPools), rapidjson::kArrayType, doc.GetAllocator()); + } + + rapidjson::Value &array = doc[kPools]; + if (array.Size() == 0 || Pool(array[array.Size() - 1]).isValid()) { + array.PushBack(rapidjson::kObjectType, doc.GetAllocator()); + } + + set(doc, array[array.Size() - 1], "url", arg); + break; + } + + case IConfig::UserKey: /* --user */ + return add(doc, kPools, "user", arg); + + case IConfig::PasswordKey: /* --pass */ + return add(doc, kPools, "pass", arg); + + case IConfig::RigIdKey: /* --rig-id */ + return add(doc, kPools, "rig-id", arg); + + case IConfig::FingerprintKey: /* --tls-fingerprint */ + return add(doc, kPools, "tls-fingerprint", arg); + + case IConfig::LogFileKey: /* --log-file */ + return set(doc, "log-file", arg); + + case IConfig::HttpAccessTokenKey: /* --http-access-token */ + return set(doc, kHttp, "access-token", arg); + + case IConfig::HttpHostKey: /* --http-host */ + return set(doc, kHttp, "host", arg); + + case IConfig::ApiWorkerIdKey: /* --api-worker-id */ + return set(doc, kApi, "worker-id", arg); + + case IConfig::ApiIdKey: /* --api-id */ + return set(doc, kApi, "id", arg); + + case IConfig::UserAgentKey: /* --user-agent */ + return set(doc, "user-agent", arg); + + case IConfig::CCRebootCmd: /* --cc-reboot-cmd */ + return set(doc, kCCClient, "reboot-cmd", arg); + + case IConfig::CCAccessToken: /* --cc-access-token */ + return set(doc, kCCClient, "access-token", arg); + + case IConfig::CCUrl: /* --cc-url */ + return set(doc, kCCClient, "url", arg); + + case IConfig::CCWorkerId: /* --cc-worker-id */ + return set(doc, kCCClient, "worker-id", arg); + + case IConfig::RetriesKey: /* --retries */ + case IConfig::RetryPauseKey: /* --retry-pause */ + case IConfig::PrintTimeKey: /* --print-time */ + case IConfig::HttpPort: /* --http-port */ + case IConfig::DonateLevelKey: /* --donate-level */ + case IConfig::DaemonPollKey: /* --daemon-poll-interval */ + case IConfig::CCUpdateInterval: /* --cc-update-interval-s */ + return transformUint64(doc, key, static_cast(strtol(arg, nullptr, 10))); + + case IConfig::BackgroundKey: /* --background */ + case IConfig::SyslogKey: /* --syslog */ + case IConfig::KeepAliveKey: /* --keepalive */ + case IConfig::NicehashKey: /* --nicehash */ + case IConfig::TlsKey: /* --tls */ + case IConfig::DryRunKey: /* --dry-run */ + case IConfig::HttpEnabledKey: /* --http-enabled */ + case IConfig::DaemonKey: /* --daemon */ + case IConfig::CCDaemonizedKey: /* --daemonized */ + case IConfig::CCUploadConfigOnStartup: /* --cc-upload-config-on-start */ + case IConfig::CCUseRemoteLog: /* --cc-use-remote-logging */ + case IConfig::CCUseTLS: /* --cc-use-tls */ + return transformBoolean(doc, key, true); + + case IConfig::ColorKey: /* --no-color */ + case IConfig::HttpRestrictedKey: /* --http-no-restricted */ + case IConfig::CCEnabledKey: /* --cc-disabled */ + return transformBoolean(doc, key, false); + + default: + break; + } +} + + +void xmrig::BaseTransform::transformBoolean(rapidjson::Document &doc, int key, bool enable) +{ + switch (key) { + case IConfig::BackgroundKey: /* --background */ + return set(doc, "background", enable); + + case IConfig::SyslogKey: /* --syslog */ + return set(doc, "syslog", enable); + + case IConfig::KeepAliveKey: /* --keepalive */ + return add(doc, kPools, "keepalive", enable); + + case IConfig::TlsKey: /* --tls */ + return add(doc, kPools, "tls", enable); + + case IConfig::DaemonKey: /* --daemon */ + return add(doc, kPools, "daemon", enable); + +# ifndef XMRIG_PROXY_PROJECT + case IConfig::NicehashKey: /* --nicehash */ + return add(doc, kPools, "nicehash", enable); +# endif + + case IConfig::ColorKey: /* --no-color */ + return set(doc, "colors", enable); + + case IConfig::HttpRestrictedKey: /* --http-no-restricted */ + return set(doc, kHttp, "restricted", enable); + + case IConfig::HttpEnabledKey: /* --http-enabled */ + return set(doc, kHttp, "enabled", enable); + + case IConfig::DryRunKey: /* --dry-run */ + return set(doc, "dry-run", enable); + + case IConfig::CCDaemonizedKey: /* --daemonized */ + return set(doc, "daemonized", enable); + + case IConfig::CCEnabledKey: /* --cc-disabled */ + return set(doc, kCCClient, "enabled", enable); + + case IConfig::CCUploadConfigOnStartup: /* --cc-upload-config-on-start */ + return set(doc, kCCClient, "upload-config-on-start", enable); + + case IConfig::CCUseRemoteLog: /* --cc-use-remote-logging */ + return set(doc, kCCClient, "use-remote-logging", enable); + + case IConfig::CCUseTLS: /* --cc-use-tls */ + return set(doc, kCCClient, "use-tls", enable); + + default: + break; + } +} + + +void xmrig::BaseTransform::transformUint64(rapidjson::Document &doc, int key, uint64_t arg) +{ + switch (key) { + case IConfig::RetriesKey: /* --retries */ + return set(doc, "retries", arg); + + case IConfig::RetryPauseKey: /* --retry-pause */ + return set(doc, "retry-pause", arg); + + case IConfig::DonateLevelKey: /* --donate-level */ + return set(doc, "donate-level", arg); + + case IConfig::ProxyDonateKey: /* --donate-over-proxy */ + return set(doc, "donate-over-proxy", arg); + + case IConfig::HttpPort: /* --http-port */ + return set(doc, kHttp, "port", arg); + + case IConfig::PrintTimeKey: /* --print-time */ + return set(doc, "print-time", arg); + + case IConfig::DaemonPollKey: /* --daemon-poll-interval */ + return add(doc, kPools, "daemon-poll-interval", arg); + + case IConfig::CCUpdateInterval: /* --cc-update-interval-s */ + return add(doc, kCCClient, "update-interval-s", arg); + + default: + break; + } +} diff --git a/src/base/kernel/config/BaseTransform.h b/src/base/kernel/config/BaseTransform.h new file mode 100644 index 00000000..02b28c12 --- /dev/null +++ b/src/base/kernel/config/BaseTransform.h @@ -0,0 +1,127 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_BASETRANSFORM_H +#define XMRIG_BASETRANSFORM_H + + +#include "base/kernel/interfaces/IConfigTransform.h" +#include "rapidjson/document.h" + + +struct option; + + +namespace xmrig { + + +class IConfigTransform; +class JsonChain; +class Process; + + +class BaseTransform : public IConfigTransform +{ +public: + BaseTransform(); + + static void load(JsonChain &chain, Process *process, IConfigTransform &transform); + +protected: + void finalize(rapidjson::Document &doc) override; + void transform(rapidjson::Document &doc, int key, const char *arg) override; + + + template + inline void set(rapidjson::Document &doc, const char *key, T value) { set(doc, doc, key, value); } + + + template + inline void set(rapidjson::Document &doc, const char *objKey, const char *key, T value) + { + if (!doc.HasMember(objKey)) { + doc.AddMember(rapidjson::StringRef(objKey), rapidjson::kObjectType, doc.GetAllocator()); + } + + set(doc, doc[objKey], key, value); + } + + + template + inline void add(rapidjson::Document &doc, const char *arrayKey, const char *key, T value, bool force = false) + { + auto &allocator = doc.GetAllocator(); + + if (!doc.HasMember(arrayKey)) { + doc.AddMember(rapidjson::StringRef(arrayKey), rapidjson::kArrayType, allocator); + } + + rapidjson::Value &array = doc[arrayKey]; + if (force || array.Size() == 0) { + array.PushBack(rapidjson::kObjectType, allocator); + } + + set(doc, array[array.Size() - 1], key, value); + } + + + template + inline void set(rapidjson::Document &doc, rapidjson::Value &obj, const char *key, T value) + { + if (obj.HasMember(key)) { + obj[key] = value; + } + else { + obj.AddMember(rapidjson::StringRef(key), value, doc.GetAllocator()); + } + } + +protected: + Algorithm m_algorithm; + + +private: + void transformBoolean(rapidjson::Document &doc, int key, bool enable); + void transformUint64(rapidjson::Document &doc, int key, uint64_t arg); +}; + + +template<> +inline void BaseTransform::set(rapidjson::Document &doc, rapidjson::Value &obj, const char *key, const char *value) +{ + auto &allocator = doc.GetAllocator(); + + if (obj.HasMember(key)) { + obj[key] = rapidjson::Value(value, allocator); + } + else { + obj.AddMember(rapidjson::StringRef(key), rapidjson::Value(value, allocator), allocator); + } +} + + +} // namespace xmrig + + +#endif /* XMRIG_BASETRANSFORM_H */ diff --git a/src/base/kernel/interfaces/IBaseListener.h b/src/base/kernel/interfaces/IBaseListener.h new file mode 100644 index 00000000..1f212369 --- /dev/null +++ b/src/base/kernel/interfaces/IBaseListener.h @@ -0,0 +1,47 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_IBASELISTENER_H +#define XMRIG_IBASELISTENER_H + + +namespace xmrig { + + +class Config; + + +class IBaseListener +{ +public: + virtual ~IBaseListener() = default; + + virtual void onConfigChanged(Config *config, Config *previousConfig) = 0; +}; + + +} /* namespace xmrig */ + + +#endif // XMRIG_IBASELISTENER_H diff --git a/src/base/kernel/interfaces/IClient.h b/src/base/kernel/interfaces/IClient.h new file mode 100644 index 00000000..c872a37a --- /dev/null +++ b/src/base/kernel/interfaces/IClient.h @@ -0,0 +1,85 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_ICLIENT_H +#define XMRIG_ICLIENT_H + + +#include + + +namespace xmrig { + + +class Algorithm; +class Job; +class JobResult; +class Pool; +class String; + + +class IClient +{ +public: + enum Extension { + EXT_ALGO, + EXT_NICEHASH, + EXT_CONNECT, + EXT_TLS, + EXT_KEEPALIVE, + EXT_MAX + }; + + virtual ~IClient() = default; + + virtual bool disconnect() = 0; + virtual bool hasExtension(Extension extension) const noexcept = 0; + virtual bool isEnabled() const = 0; + virtual bool isTLS() const = 0; + virtual const char *mode() const = 0; + virtual const char *tlsFingerprint() const = 0; + virtual const char *tlsVersion() const = 0; + virtual const Job &job() const = 0; + virtual const Pool &pool() const = 0; + virtual const String &ip() const = 0; + virtual int id() const = 0; + virtual int64_t submit(const JobResult &result) = 0; + virtual void connect() = 0; + virtual void connect(const Pool &pool) = 0; + virtual void deleteLater() = 0; + virtual void setAlgo(const Algorithm &algo) = 0; + virtual void setEnabled(bool enabled) = 0; + virtual void setPool(const Pool &pool) = 0; + virtual void setQuiet(bool quiet) = 0; + virtual void setRetries(int retries) = 0; + virtual void setRetryPause(uint64_t ms) = 0; + virtual void tick(uint64_t now) = 0; + +}; + + +} /* namespace xmrig */ + + +#endif // XMRIG_ICLIENT_H diff --git a/src/base/kernel/interfaces/IClientListener.h b/src/base/kernel/interfaces/IClientListener.h new file mode 100644 index 00000000..3583be5a --- /dev/null +++ b/src/base/kernel/interfaces/IClientListener.h @@ -0,0 +1,61 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_ICLIENTLISTENER_H +#define XMRIG_ICLIENTLISTENER_H + + +#include + + +#include "rapidjson/fwd.h" + + +namespace xmrig { + + +class Algorithm; +class IClient; +class Job; +class SubmitResult; + + +class IClientListener +{ +public: + virtual ~IClientListener() = default; + + virtual void onClose(IClient *client, int failures) = 0; + virtual void onJobReceived(IClient *client, const Job &job, const rapidjson::Value ¶ms) = 0; + virtual void onLogin(IClient *client, rapidjson::Document &doc, rapidjson::Value ¶ms) = 0; + virtual void onLoginSuccess(IClient *client) = 0; + virtual void onResultAccepted(IClient *client, const SubmitResult &result, const char *error) = 0; + virtual void onVerifyAlgorithm(const IClient *client, const Algorithm &algorithm, bool *ok) = 0; +}; + + +} /* namespace xmrig */ + + +#endif // XMRIG_ICLIENTLISTENER_H diff --git a/src/base/kernel/interfaces/IConfig.h b/src/base/kernel/interfaces/IConfig.h new file mode 100644 index 00000000..40725483 --- /dev/null +++ b/src/base/kernel/interfaces/IConfig.h @@ -0,0 +1,160 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_ICONFIG_H +#define XMRIG_ICONFIG_H + + +#include "crypto/common/Algorithm.h" +#include "rapidjson/fwd.h" + + +namespace xmrig { + + +class IJsonReader; +class String; + + +class IConfig +{ +public: + enum Keys { + // common + AlgorithmKey = 'a', + ApiWorkerIdKey = 4002, + ApiIdKey = 4005, + HttpPort = 4100, + HttpAccessTokenKey = 4101, + HttpRestrictedKey = 4104, + HttpEnabledKey = 4106, + HttpHostKey = 4107, + BackgroundKey = 'B', + ColorKey = 1002, + ConfigKey = 'c', + DonateLevelKey = 1003, + KeepAliveKey = 'k', + LogFileKey = 'l', + PasswordKey = 'p', + RetriesKey = 'r', + RetryPauseKey = 'R', + RigIdKey = 1012, + SyslogKey = 'S', + UrlKey = 'o', + UserAgentKey = 1008, + UserKey = 'u', + UserpassKey = 'O', + VerboseKey = 1100, + TlsKey = 1013, + FingerprintKey = 1014, + ProxyDonateKey = 1017, + DaemonKey = 1018, + DaemonPollKey = 1019, + + // xmrig common + CPUPriorityKey = 1021, + NicehashKey = 1006, + PrintTimeKey = 1007, + + // xmrig cpu + AVKey = 'v', + CPUAffinityKey = 1020, + DryRunKey = 5000, + HugePagesKey = 1009, + ThreadsKey = 't', + AssemblyKey = 1015, + RandomXInitKey = 1022, + RandomXNumaKey = 1023, + + // xmrig amd + OclPlatformKey = 1400, + OclAffinityKey = 1401, + OclDevicesKey = 1402, + OclLaunchKey = 1403, + OclCacheKey = 1404, + OclPrintKey = 1405, + OclLoaderKey = 1406, + OclSridedIndexKey = 1407, + OclMemChunkKey = 1408, + OclUnrollKey = 1409, + OclCompModeKey = 1410, + + // xmrig-proxy + AccessLogFileKey = 'A', + BindKey = 'b', + CustomDiffKey = 1102, + DebugKey = 1101, + ModeKey = 'm', + PoolCoinKey = 'C', + ReuseTimeoutKey = 1106, + WorkersKey = 1103, + WorkersAdvKey = 1107, + TlsBindKey = 1108, + TlsCertKey = 1109, + TlsCertKeyKey = 1110, + TlsDHparamKey = 1111, + TlsCiphersKey = 1112, + TlsCipherSuitesKey = 1113, + TlsProtocolsKey = 1114, + AlgoExtKey = 1115, + ProxyPasswordKey = 1116, + + // xmrig nvidia + CudaMaxThreadsKey = 1200, + CudaBFactorKey = 1201, + CudaBSleepKey = 1202, + CudaDevicesKey = 1203, + CudaLaunchKey = 1204, + CudaAffinityKey = 1205, + CudaMaxUsageKey = 1206, + + // xmrigCC client over 9000 + CCDaemonizedKey = 9000, + CCEnabledKey = 9001, + CCUseTLS = 9002, + CCUseRemoteLog = 9003, + CCUploadConfigOnStartup = 9004, + CCRebootCmd = 9005, + CCUrl = 9006, + CCAccessToken = 9007, + CCWorkerId = 9008, + CCUpdateInterval = 9009, + + }; + + virtual ~IConfig() = default; + + virtual bool isWatch() const = 0; + virtual bool read(const IJsonReader &reader, const char *fileName) = 0; + virtual bool save() = 0; + virtual const String &fileName() const = 0; + virtual void getJSON(rapidjson::Document &doc) const = 0; + virtual void setFileName(const char *fileName) = 0; +}; + + +} /* namespace xmrig */ + + +#endif // XMRIG_ICONFIG_H diff --git a/src/base/kernel/interfaces/IConfigListener.h b/src/base/kernel/interfaces/IConfigListener.h new file mode 100644 index 00000000..a6fa835f --- /dev/null +++ b/src/base/kernel/interfaces/IConfigListener.h @@ -0,0 +1,47 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_ICONFIGLISTENER_H +#define XMRIG_ICONFIGLISTENER_H + + +namespace xmrig { + + +class IConfig; + + +class IConfigListener +{ +public: + virtual ~IConfigListener() = default; + + virtual void onNewConfig(IConfig *config) = 0; +}; + + +} /* namespace xmrig */ + + +#endif // XMRIG_ICONFIGLISTENER_H diff --git a/src/base/kernel/interfaces/IConfigTransform.h b/src/base/kernel/interfaces/IConfigTransform.h new file mode 100644 index 00000000..8afe8221 --- /dev/null +++ b/src/base/kernel/interfaces/IConfigTransform.h @@ -0,0 +1,53 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_ICONFIGTRANSFORM_H +#define XMRIG_ICONFIGTRANSFORM_H + + +#include "crypto/common/Algorithm.h" +#include "rapidjson/fwd.h" + + +namespace xmrig { + + +class IJsonReader; +class String; + + +class IConfigTransform +{ +public: + virtual ~IConfigTransform() = default; + + virtual void finalize(rapidjson::Document &doc) = 0; + virtual void transform(rapidjson::Document &doc, int key, const char *arg) = 0; +}; + + +} /* namespace xmrig */ + + +#endif // XMRIG_ICONFIGTRANSFORM_H diff --git a/src/interfaces/IConsoleListener.h b/src/base/kernel/interfaces/IConsoleListener.h similarity index 71% rename from src/interfaces/IConsoleListener.h rename to src/base/kernel/interfaces/IConsoleListener.h index 723f87df..88972f05 100644 --- a/src/interfaces/IConsoleListener.h +++ b/src/base/kernel/interfaces/IConsoleListener.h @@ -4,8 +4,9 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , * * 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 @@ -21,17 +22,23 @@ * along with this program. If not, see . */ -#ifndef __ICONSOLELISTENER_H__ -#define __ICONSOLELISTENER_H__ +#ifndef XMRIG_ICONSOLELISTENER_H +#define XMRIG_ICONSOLELISTENER_H + + +namespace xmrig { class IConsoleListener { public: - virtual ~IConsoleListener() {} + virtual ~IConsoleListener() = default; virtual void onConsoleCommand(char command) = 0; }; -#endif // __ICONSOLELISTENER_H__ +} /* namespace xmrig */ + + +#endif // XMRIG_ICONSOLELISTENER_H diff --git a/src/log/FileLog.h b/src/base/kernel/interfaces/IDnsListener.h similarity index 66% rename from src/log/FileLog.h rename to src/base/kernel/interfaces/IDnsListener.h index 469347d4..3683ea51 100644 --- a/src/log/FileLog.h +++ b/src/base/kernel/interfaces/IDnsListener.h @@ -4,8 +4,9 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , * * 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 @@ -21,31 +22,26 @@ * along with this program. If not, see . */ -#ifndef __FILELOG_H__ -#define __FILELOG_H__ +#ifndef XMRIG_IDNSLISTENER_H +#define XMRIG_IDNSLISTENER_H -#include +namespace xmrig { -#include "interfaces/ILogBackend.h" +class Dns; -class FileLog : public ILogBackend +class IDnsListener { public: - FileLog(const char *fileName); + virtual ~IDnsListener() = default; - void message(int level, const char* fmt, va_list args) override; - void text(const char* fmt, va_list args) override; - -private: - static void onWrite(uv_fs_t *req); - - void write(char *data, size_t size); - - char m_fmt[256]; - int m_file; + virtual void onResolved(const Dns &dns, int status) = 0; }; -#endif /* __FILELOG_H__ */ + +} /* namespace xmrig */ + + +#endif // XMRIG_IDNSLISTENER_H diff --git a/src/base/kernel/interfaces/IHttpListener.h b/src/base/kernel/interfaces/IHttpListener.h new file mode 100644 index 00000000..94fd1d18 --- /dev/null +++ b/src/base/kernel/interfaces/IHttpListener.h @@ -0,0 +1,48 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_IHTTPLISTENER_H +#define XMRIG_IHTTPLISTENER_H + + +namespace xmrig { + + +class HttpData; +class HttpResponse; + + +class IHttpListener +{ +public: + virtual ~IHttpListener() = default; + + virtual void onHttpData(const HttpData &data) = 0; +}; + + +} /* namespace xmrig */ + + +#endif // XMRIG_IHTTPLISTENER_H diff --git a/src/base/kernel/interfaces/IJsonReader.h b/src/base/kernel/interfaces/IJsonReader.h new file mode 100644 index 00000000..c0fe09cb --- /dev/null +++ b/src/base/kernel/interfaces/IJsonReader.h @@ -0,0 +1,56 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_IJSONREADER_H +#define XMRIG_IJSONREADER_H + + +#include "rapidjson/fwd.h" + + +namespace xmrig { + + +class IJsonReader +{ +public: + virtual ~IJsonReader() = default; + + virtual bool getBool(const char *key, bool defaultValue = false) const = 0; + virtual bool isEmpty() const = 0; + virtual const char *getString(const char *key, const char *defaultValue = nullptr) const = 0; + virtual const rapidjson::Value &getArray(const char *key) const = 0; + virtual const rapidjson::Value &getObject(const char *key) const = 0; + virtual const rapidjson::Value &getValue(const char *key) const = 0; + virtual int getInt(const char *key, int defaultValue = 0) const = 0; + virtual int64_t getInt64(const char *key, int64_t defaultValue = 0) const = 0; + virtual uint64_t getUint64(const char *key, uint64_t defaultValue = 0) const = 0; + virtual unsigned getUint(const char *key, unsigned defaultValue = 0) const = 0; +}; + + +} /* namespace xmrig */ + + +#endif // XMRIG_IJSONREADER_H diff --git a/src/interfaces/IStrategy.h b/src/base/kernel/interfaces/ILineListener.h similarity index 65% rename from src/interfaces/IStrategy.h rename to src/base/kernel/interfaces/ILineListener.h index 660529ea..1a6d4914 100644 --- a/src/interfaces/IStrategy.h +++ b/src/base/kernel/interfaces/ILineListener.h @@ -4,8 +4,9 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , * * 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 @@ -21,28 +22,29 @@ * along with this program. If not, see . */ -#ifndef __ISTRATEGY_H__ -#define __ISTRATEGY_H__ +#ifndef XMRIG_ILINELISTENER_H +#define XMRIG_ILINELISTENER_H #include -class JobResult; +namespace xmrig { -class IStrategy +class String; + + +class ILineListener { public: - virtual ~IStrategy() {} + virtual ~ILineListener() = default; - virtual bool isActive() const = 0; - virtual int64_t submit(const JobResult &result) = 0; - virtual void connect() = 0; - virtual void resume() = 0; - virtual void stop() = 0; - virtual void tick(uint64_t now) = 0; + virtual void onLine(char *line, size_t size) = 0; }; -#endif // __ISTRATEGY_H__ +} /* namespace xmrig */ + + +#endif // XMRIG_ILINELISTENER_H diff --git a/src/base/kernel/interfaces/ILogBackend.h b/src/base/kernel/interfaces/ILogBackend.h new file mode 100644 index 00000000..ef18da88 --- /dev/null +++ b/src/base/kernel/interfaces/ILogBackend.h @@ -0,0 +1,49 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2019 Spudz76 + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_ILOGBACKEND_H +#define XMRIG_ILOGBACKEND_H + + +#include +#include + + +namespace xmrig { + + +class ILogBackend +{ +public: + virtual ~ILogBackend() = default; + + virtual void print(int level, const char *line, size_t offset, size_t size, bool colors) = 0; +}; + + +} /* namespace xmrig */ + + +#endif // XMRIG_ILOGBACKEND_H diff --git a/src/api/Api.h b/src/base/kernel/interfaces/ISignalListener.h similarity index 66% rename from src/api/Api.h rename to src/base/kernel/interfaces/ISignalListener.h index 72c65c3c..bbcd11f1 100644 --- a/src/api/Api.h +++ b/src/base/kernel/interfaces/ISignalListener.h @@ -4,8 +4,9 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , * * 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 @@ -21,31 +22,26 @@ * along with this program. If not, see . */ -#ifndef __API_H__ -#define __API_H__ +#ifndef XMRIG_ISIGNALLISTENER_H +#define XMRIG_ISIGNALLISTENER_H -#include +namespace xmrig { -class ApiState; -class Hashrate; -class NetworkState; +class String; -class Api +class ISignalListener { public: - static bool start(); - static void release(); + virtual ~ISignalListener() = default; - static char *get(const char *url, int *status); - static void tick(const Hashrate *hashrate); - static void tick(const NetworkState &results); - -private: - static ApiState *m_state; - static uv_mutex_t m_mutex; + virtual void onSignal(int signum) = 0; }; -#endif /* __API_H__ */ + +} /* namespace xmrig */ + + +#endif // XMRIG_ISIGNALLISTENER_H diff --git a/src/base/kernel/interfaces/IStrategy.h b/src/base/kernel/interfaces/IStrategy.h new file mode 100644 index 00000000..f2e58408 --- /dev/null +++ b/src/base/kernel/interfaces/IStrategy.h @@ -0,0 +1,59 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_ISTRATEGY_H +#define XMRIG_ISTRATEGY_H + + +#include + + +namespace xmrig { + + +class Algorithm; +class IClient; +class JobResult; + + +class IStrategy +{ +public: + virtual ~IStrategy() = default; + + virtual bool isActive() const = 0; + virtual IClient *client() const = 0; + virtual int64_t submit(const JobResult &result) = 0; + virtual void connect() = 0; + virtual void resume() = 0; + virtual void setAlgo(const Algorithm &algo) = 0; + virtual void stop() = 0; + virtual void tick(uint64_t now) = 0; +}; + + +} /* namespace xmrig */ + + +#endif // XMRIG_ISTRATEGY_H diff --git a/src/base/kernel/interfaces/IStrategyListener.h b/src/base/kernel/interfaces/IStrategyListener.h new file mode 100644 index 00000000..8b88b506 --- /dev/null +++ b/src/base/kernel/interfaces/IStrategyListener.h @@ -0,0 +1,59 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_ISTRATEGYLISTENER_H +#define XMRIG_ISTRATEGYLISTENER_H + + +#include "rapidjson/fwd.h" + + +namespace xmrig { + + +class Algorithm; +class IClient; +class IStrategy; +class Job; +class SubmitResult; + + +class IStrategyListener +{ +public: + virtual ~IStrategyListener() = default; + + virtual void onActive(IStrategy *strategy, IClient *client) = 0; + virtual void onJob(IStrategy *strategy, IClient *client, const Job &job) = 0; + virtual void onLogin(IStrategy *strategy, IClient *client, rapidjson::Document &doc, rapidjson::Value ¶ms) = 0; + virtual void onPause(IStrategy *strategy) = 0; + virtual void onResultAccepted(IStrategy *strategy, IClient *client, const SubmitResult &result, const char *error) = 0; + virtual void onVerifyAlgorithm(IStrategy *strategy, const IClient *client, const Algorithm &algorithm, bool *ok) = 0; +}; + + +} /* namespace xmrig */ + + +#endif // XMRIG_ISTRATEGYLISTENER_H diff --git a/src/crypto/Argon2.h b/src/base/kernel/interfaces/ITcpServerListener.h similarity index 62% rename from src/crypto/Argon2.h rename to src/base/kernel/interfaces/ITcpServerListener.h index 3f5c56b9..3cb1576e 100644 --- a/src/crypto/Argon2.h +++ b/src/base/kernel/interfaces/ITcpServerListener.h @@ -4,8 +4,9 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , * * 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 @@ -21,14 +22,32 @@ * along with this program. If not, see . */ -#ifndef __ARGON2_H__ -#define __ARGON2_H__ +#ifndef XMRIG_ITCPSERVERLISTENER_H +#define XMRIG_ITCPSERVERLISTENER_H -#include #include -#define MEMORY_ARGON2_256 262144 /* 256 KiB in bytes */ -#define MEMORY_ARGON2_512 524288 /* 512 KiB in bytes */ -#endif /* __ARGON2_H__ */ +typedef struct uv_stream_s uv_stream_t; + + +namespace xmrig { + + +class String; + + +class ITcpServerListener +{ +public: + virtual ~ITcpServerListener() = default; + + virtual void onConnection(uv_stream_t *stream, uint16_t port) = 0; +}; + + +} /* namespace xmrig */ + + +#endif // XMRIG_ITCPSERVERLISTENER_H diff --git a/src/net/SubmitResult.h b/src/base/kernel/interfaces/ITimerListener.h similarity index 66% rename from src/net/SubmitResult.h rename to src/base/kernel/interfaces/ITimerListener.h index 8eddef89..1b6337d6 100644 --- a/src/net/SubmitResult.h +++ b/src/base/kernel/interfaces/ITimerListener.h @@ -4,8 +4,9 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , * * 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 @@ -21,28 +22,26 @@ * along with this program. If not, see . */ -#ifndef __SUBMITRESULT_H__ -#define __SUBMITRESULT_H__ +#ifndef XMRIG_ITIMERLISTENER_H +#define XMRIG_ITIMERLISTENER_H -#include +namespace xmrig { -class SubmitResult +class Timer; + + +class ITimerListener { public: - inline SubmitResult() : seq(0), diff(0), actualDiff(0), elapsed(0), start(0) {} - SubmitResult(int64_t seq, uint32_t diff, uint64_t actualDiff); + virtual ~ITimerListener() = default; - void done(); - - int64_t seq; - uint32_t diff; - uint64_t actualDiff; - uint64_t elapsed; - -private: - uint64_t start; + virtual void onTimer(const Timer *timer) = 0; }; -#endif /* __SUBMITRESULT_H__ */ + +} /* namespace xmrig */ + + +#endif // XMRIG_ITIMERLISTENER_H diff --git a/src/base/kernel/interfaces/IWatcherListener.h b/src/base/kernel/interfaces/IWatcherListener.h new file mode 100644 index 00000000..a62b4b53 --- /dev/null +++ b/src/base/kernel/interfaces/IWatcherListener.h @@ -0,0 +1,47 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_IWATCHERLISTENER_H +#define XMRIG_IWATCHERLISTENER_H + + +namespace xmrig { + + +class String; + + +class IWatcherListener +{ +public: + virtual ~IWatcherListener() = default; + + virtual void onFileChanged(const String &fileName) = 0; +}; + + +} /* namespace xmrig */ + + +#endif // XMRIG_IWATCHERLISTENER_H diff --git a/src/base/net/dns/Dns.cpp b/src/base/net/dns/Dns.cpp new file mode 100644 index 00000000..40d2a6e3 --- /dev/null +++ b/src/base/net/dns/Dns.cpp @@ -0,0 +1,163 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include "base/kernel/interfaces/IDnsListener.h" +#include "base/net/dns/Dns.h" +#include "base/tools/Handle.h" + + +namespace xmrig { + Storage Dns::m_storage; + static const DnsRecord defaultRecord; +} + + +xmrig::Dns::Dns(IDnsListener *listener) : + m_hints(), + m_listener(listener), + m_status(0), + m_resolver(nullptr) +{ + m_key = m_storage.add(this); + + m_resolver = new uv_getaddrinfo_t; + m_resolver->data = m_storage.ptr(m_key); + + m_hints.ai_family = AF_UNSPEC; + m_hints.ai_socktype = SOCK_STREAM; + m_hints.ai_protocol = IPPROTO_TCP; +} + + +xmrig::Dns::~Dns() +{ + m_storage.release(m_key); + + delete m_resolver; +} + + +bool xmrig::Dns::resolve(const String &host) +{ + if (m_host != host) { + m_host = host; + + clear(); + } + + m_status = uv_getaddrinfo(uv_default_loop(), m_resolver, Dns::onResolved, m_host.data(), nullptr, &m_hints); + + return m_status == 0; +} + + +const char *xmrig::Dns::error() const +{ + return uv_strerror(m_status); +} + + +const xmrig::DnsRecord &xmrig::Dns::get(DnsRecord::Type prefered) const +{ + if (count() == 0) { + return defaultRecord; + } + + const size_t ipv4 = m_ipv4.size(); + const size_t ipv6 = m_ipv6.size(); + + if (ipv6 && (prefered == DnsRecord::AAAA || !ipv4)) { + return m_ipv6[ipv6 == 1 ? 0 : static_cast(rand()) % ipv6]; + } + + if (ipv4) { + return m_ipv4[ipv4 == 1 ? 0 : static_cast(rand()) % ipv4]; + } + + return defaultRecord; +} + + +size_t xmrig::Dns::count(DnsRecord::Type type) const +{ + if (type == DnsRecord::A) { + return m_ipv4.size(); + } + + if (type == DnsRecord::AAAA) { + return m_ipv6.size(); + } + + return m_ipv4.size() + m_ipv6.size(); +} + + +void xmrig::Dns::clear() +{ + m_ipv4.clear(); + m_ipv6.clear(); +} + + +void xmrig::Dns::onResolved(int status, addrinfo *res) +{ + m_status = status; + + if (m_status < 0) { + return m_listener->onResolved(*this, status); + } + + clear(); + + addrinfo *ptr = res; + while (ptr != nullptr) { + if (ptr->ai_family == AF_INET) { + m_ipv4.push_back(ptr); + } + + if (ptr->ai_family == AF_INET6) { + m_ipv6.push_back(ptr); + } + + ptr = ptr->ai_next; + } + + if (isEmpty()) { + m_status = UV_EAI_NONAME; + } + + m_listener->onResolved(*this, m_status); +} + + +void xmrig::Dns::onResolved(uv_getaddrinfo_t *req, int status, addrinfo *res) +{ + Dns *dns = m_storage.get(req->data); + if (dns) { + dns->onResolved(status, res); + } + + uv_freeaddrinfo(res); +} diff --git a/src/base/net/dns/Dns.h b/src/base/net/dns/Dns.h new file mode 100644 index 00000000..11f5bf80 --- /dev/null +++ b/src/base/net/dns/Dns.h @@ -0,0 +1,81 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_DNS_H +#define XMRIG_DNS_H + + +#include +#include + + +#include "base/net/dns/DnsRecord.h" +#include "base/net/tools/Storage.h" +#include "base/tools/String.h" + + +namespace xmrig { + + +class IDnsListener; + + +class Dns +{ +public: + Dns(IDnsListener *listener); + ~Dns(); + + inline bool isEmpty() const { return m_ipv4.empty() && m_ipv6.empty(); } + inline const String &host() const { return m_host; } + inline int status() const { return m_status; } + + bool resolve(const String &host); + const char *error() const; + const DnsRecord &get(DnsRecord::Type prefered = DnsRecord::A) const; + size_t count(DnsRecord::Type type = DnsRecord::Unknown) const; + +private: + void clear(); + void onResolved(int status, addrinfo *res); + + static void onResolved(uv_getaddrinfo_t *req, int status, addrinfo *res); + + addrinfo m_hints; + IDnsListener *m_listener; + int m_status; + std::vector m_ipv4; + std::vector m_ipv6; + String m_host; + uintptr_t m_key; + uv_getaddrinfo_t *m_resolver; + + static Storage m_storage; +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_DNS_H */ diff --git a/src/base/net/dns/DnsRecord.cpp b/src/base/net/dns/DnsRecord.cpp new file mode 100644 index 00000000..7a85799b --- /dev/null +++ b/src/base/net/dns/DnsRecord.cpp @@ -0,0 +1,66 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include + + +#include "base/net/dns/DnsRecord.h" + + +xmrig::DnsRecord::DnsRecord(const addrinfo *addr) : + m_type(addr->ai_family == AF_INET6 ? AAAA : A) +{ + char *buf = nullptr; + + if (m_type == AAAA) { + buf = new char[45](); + uv_ip6_name(reinterpret_cast(addr->ai_addr), buf, 45); + } + else { + buf = new char[16](); + uv_ip4_name(reinterpret_cast(addr->ai_addr), buf, 16); + } + + m_ip = buf; +} + + +sockaddr *xmrig::DnsRecord::addr(uint16_t port) const +{ + if (m_type == A) { + sockaddr_in *addr = new sockaddr_in(); + uv_ip4_addr(m_ip.data(), port, addr); + + return reinterpret_cast(addr); + } + else if (m_type == AAAA) { + sockaddr_in6 *addr = new sockaddr_in6(); + uv_ip6_addr(m_ip.data(), port, addr); + + return reinterpret_cast(addr); + } + + return nullptr; +} diff --git a/src/base/net/dns/DnsRecord.h b/src/base/net/dns/DnsRecord.h new file mode 100644 index 00000000..71d1e9c8 --- /dev/null +++ b/src/base/net/dns/DnsRecord.h @@ -0,0 +1,66 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_DNSRECORD_H +#define XMRIG_DNSRECORD_H + + +struct addrinfo; +struct sockaddr; + + +#include "base/tools/String.h" + + +namespace xmrig { + + +class DnsRecord +{ +public: + enum Type { + Unknown, + A, + AAAA + }; + + inline DnsRecord() : m_type(Unknown) {} + DnsRecord(const addrinfo *addr); + + sockaddr *addr(uint16_t port = 0) const; + + inline bool isValid() const { return m_type != Unknown; } + inline const String &ip() const { return m_ip; } + inline Type type() const { return m_type; } + +private: + Type m_type; + String m_ip; +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_DNSRECORD_H */ diff --git a/src/base/net/http/Http.cpp b/src/base/net/http/Http.cpp new file mode 100644 index 00000000..3c275824 --- /dev/null +++ b/src/base/net/http/Http.cpp @@ -0,0 +1,99 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include "3rdparty/rapidjson/document.h" +#include "base/io/json/Json.h" +#include "base/net/http/Http.h" + + +namespace xmrig { + +static const char *kEnabled = "enabled"; +static const char *kHost = "host"; +static const char *kLocalhost = "127.0.0.1"; +static const char *kPort = "port"; +static const char *kRestricted = "restricted"; +static const char *kToken = "access-token"; + +} + + +xmrig::Http::Http() : + m_enabled(false), + m_restricted(true), + m_host(kLocalhost), + m_port(0) +{ +} + + +bool xmrig::Http::isEqual(const Http &other) const +{ + return other.m_enabled == m_enabled && + other.m_restricted == m_restricted && + other.m_host == m_host && + other.m_token == m_token && + other.m_port == m_port; +} + + +rapidjson::Value xmrig::Http::toJSON(rapidjson::Document &doc) const +{ + using namespace rapidjson; + auto &allocator = doc.GetAllocator(); + + Value obj(kObjectType); + + obj.AddMember(StringRef(kEnabled), m_enabled, allocator); + obj.AddMember(StringRef(kHost), m_host.toJSON(), allocator); + obj.AddMember(StringRef(kPort), m_port, allocator); + obj.AddMember(StringRef(kToken), m_token.toJSON(), allocator); + obj.AddMember(StringRef(kRestricted), m_restricted, allocator); + + return obj; +} + + +void xmrig::Http::load(const rapidjson::Value &http) +{ + if (!http.IsObject()) { + return; + } + + m_enabled = Json::getBool(http, kEnabled); + m_restricted = Json::getBool(http, kRestricted, true); + m_host = Json::getString(http, kHost, kLocalhost); + m_token = Json::getString(http, kToken); + + setPort(Json::getInt(http, kPort)); +} + + +void xmrig::Http::setPort(int port) +{ + if (port >= 0 && port <= 65536) { + m_port = static_cast(port); + } +} diff --git a/src/base/net/http/Http.h b/src/base/net/http/Http.h new file mode 100644 index 00000000..21eb581a --- /dev/null +++ b/src/base/net/http/Http.h @@ -0,0 +1,73 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#ifndef XMRIG_HTTP_H +#define XMRIG_HTTP_H + + +#include "base/tools/String.h" + + +namespace xmrig { + + +class Http +{ +public: + Http(); + + inline bool isAuthRequired() const { return m_restricted == false || !m_token.isNull(); } + inline bool isEnabled() const { return m_enabled; } + inline bool isRestricted() const { return m_restricted; } + inline const String &host() const { return m_host; } + inline const String &token() const { return m_token; } + inline uint16_t port() const { return m_port; } + inline void setEnabled(bool enabled) { m_enabled = enabled; } + inline void setHost(const char *host) { m_host = host; } + inline void setRestricted(bool restricted) { m_restricted = restricted; } + inline void setToken(const char *token) { m_token = token; } + + inline bool operator!=(const Http &other) const { return !isEqual(other); } + inline bool operator==(const Http &other) const { return isEqual(other); } + + bool isEqual(const Http &other) const; + rapidjson::Value toJSON(rapidjson::Document &doc) const; + void load(const rapidjson::Value &http); + void setPort(int port); + +private: + bool m_enabled; + bool m_restricted; + String m_host; + String m_token; + uint16_t m_port; +}; + + +} // namespace xmrig + + +#endif // XMRIG_HTTP_H + diff --git a/src/base/net/http/HttpApiResponse.cpp b/src/base/net/http/HttpApiResponse.cpp new file mode 100644 index 00000000..5fe92636 --- /dev/null +++ b/src/base/net/http/HttpApiResponse.cpp @@ -0,0 +1,88 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2014-2019 heapwolf + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include "3rdparty/http-parser/http_parser.h" +#include "base/net/http/HttpApiResponse.h" +#include "rapidjson/prettywriter.h" +#include "rapidjson/stringbuffer.h" + + +namespace xmrig { + +static const char *kError = "error"; +static const char *kStatus = "status"; + +} // namespace xmrig + + +xmrig::HttpApiResponse::HttpApiResponse(uint64_t id) : + HttpResponse(id), + m_doc(rapidjson::kObjectType) +{ +} + + +xmrig::HttpApiResponse::HttpApiResponse(uint64_t id, int status) : + HttpResponse(id), + m_doc(rapidjson::kObjectType) +{ + setStatus(status); +} + + +void xmrig::HttpApiResponse::end() +{ + using namespace rapidjson; + + setHeader("Access-Control-Allow-Origin", "*"); + setHeader("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE"); + setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type"); + + if (statusCode() >= 400) { + if (!m_doc.HasMember(kStatus)) { + m_doc.AddMember(StringRef(kStatus), statusCode(), m_doc.GetAllocator()); + } + + if (!m_doc.HasMember(kError)) { + m_doc.AddMember(StringRef(kError), StringRef(http_status_str(static_cast(statusCode()))), m_doc.GetAllocator()); + } + } + + if (!m_doc.MemberCount()) { + return HttpResponse::end(); + } + + setHeader("Content-Type", "application/json"); + + StringBuffer buffer(nullptr, 4096); + PrettyWriter writer(buffer); + writer.SetMaxDecimalPlaces(10); + writer.SetFormatOptions(kFormatSingleLineArray); + + m_doc.Accept(writer); + + HttpResponse::end(buffer.GetString(), buffer.GetSize()); +} diff --git a/src/base/net/http/HttpApiResponse.h b/src/base/net/http/HttpApiResponse.h new file mode 100644 index 00000000..bbaf132e --- /dev/null +++ b/src/base/net/http/HttpApiResponse.h @@ -0,0 +1,57 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2014-2019 heapwolf + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#ifndef XMRIG_HTTPAPIRESPONSE_H +#define XMRIG_HTTPAPIRESPONSE_H + + +#include "base/net/http/HttpResponse.h" +#include "rapidjson/document.h" + + +namespace xmrig { + + +class HttpApiResponse : public HttpResponse +{ +public: + HttpApiResponse(uint64_t id); + HttpApiResponse(uint64_t id, int status); + + inline rapidjson::Document &doc() { return m_doc; } + + void end(); + +private: + rapidjson::Document m_doc; +}; + + +} // namespace xmrig + + +#endif // XMRIG_HTTPAPIRESPONSE_H + diff --git a/src/base/net/http/HttpClient.cpp b/src/base/net/http/HttpClient.cpp new file mode 100644 index 00000000..113e2f13 --- /dev/null +++ b/src/base/net/http/HttpClient.cpp @@ -0,0 +1,224 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2014-2019 heapwolf + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include + + +#include "3rdparty/http-parser/http_parser.h" +#include "base/io/log/Log.h" +#include "base/kernel/Platform.h" +#include "base/net/dns/Dns.h" +#include "base/net/http/HttpClient.h" +#include "base/tools/Baton.h" + + +namespace xmrig { + +static const char *kCRLF = "\r\n"; + + +class ClientWriteBaton : public Baton +{ +public: + inline ClientWriteBaton(const std::string &header, std::string &&body) : + m_body(std::move(body)), + m_header(header) + { + bufs[0].len = m_header.size(); + bufs[0].base = const_cast(m_header.c_str()); + + if (!m_body.empty()) { + bufs[1].len = m_body.size(); + bufs[1].base = const_cast(m_body.c_str()); + } + else { + bufs[1].base = nullptr; + bufs[1].len = 0; + } + } + + + inline size_t count() const { return bufs[1].base == nullptr ? 1 : 2; } + inline size_t size() const { return bufs[0].len + bufs[1].len; } + inline static void onWrite(uv_write_t *req, int) { delete reinterpret_cast(req->data); } + + + uv_buf_t bufs[2]; + +private: + std::string m_body; + std::string m_header; +}; + + +} // namespace xmrig + + +xmrig::HttpClient::HttpClient(int method, const String &url, IHttpListener *listener, const char *data, size_t size) : + HttpContext(HTTP_RESPONSE, listener), + m_quiet(false), + m_port(0) +{ + this->method = method; + this->url = url; + + if (data) { + body = size ? std::string(data, size) : data; + } + + m_dns = new Dns(this); +} + + +xmrig::HttpClient::~HttpClient() +{ + delete m_dns; +} + + +bool xmrig::HttpClient::connect(const String &host, uint16_t port) +{ + m_port = port; + + return m_dns->resolve(host); +} + + +const xmrig::String &xmrig::HttpClient::host() const +{ + return m_dns->host(); +} + + +void xmrig::HttpClient::onResolved(const Dns &dns, int status) +{ + this->status = status; + + if (status < 0 && dns.isEmpty()) { + if (!m_quiet) { + LOG_ERR("[%s:%d] DNS error: \"%s\"", dns.host().data(), m_port, uv_strerror(status)); + } + + return; + } + + sockaddr *addr = dns.get().addr(m_port); + + uv_connect_t *req = new uv_connect_t; + req->data = this; + + uv_tcp_connect(req, m_tcp, addr, onConnect); +} + + +void xmrig::HttpClient::handshake() +{ + headers.insert({ "Host", m_dns->host().data() }); + headers.insert({ "Connection", "close" }); + headers.insert({ "User-Agent", Platform::userAgent() }); + + if (body.size()) { + headers.insert({ "Content-Length", std::to_string(body.size()) }); + } + + std::stringstream ss; + ss << http_method_str(static_cast(method)) << " " << url << " HTTP/1.1" << kCRLF; + + for (auto &header : headers) { + ss << header.first << ": " << header.second << kCRLF; + } + + ss << kCRLF; + + headers.clear(); + + write(ss.str()); +} + + +void xmrig::HttpClient::read(const char *data, size_t size) +{ + if (parse(data, size) < size) { + close(UV_EPROTO); + } +} + + +void xmrig::HttpClient::write(const std::string &header) +{ + ClientWriteBaton *baton = new ClientWriteBaton(header, std::move(body)); + uv_write(&baton->req, stream(), baton->bufs, baton->count(), ClientWriteBaton::onWrite); +} + + +void xmrig::HttpClient::onConnect(uv_connect_t *req, int status) +{ + HttpClient *client = static_cast(req->data); + if (!client) { + delete req; + return; + } + + if (status < 0) { + if (!client->m_quiet) { + LOG_ERR("[%s:%d] connect error: \"%s\"", client->m_dns->host().data(), client->m_port, uv_strerror(status)); + } + + delete req; + client->close(status); + return; + } + + uv_read_start(client->stream(), + [](uv_handle_t *, size_t suggested_size, uv_buf_t *buf) + { + buf->base = new char[suggested_size]; + +# ifdef _WIN32 + buf->len = static_cast(suggested_size); +# else + buf->len = suggested_size; +# endif + }, + [](uv_stream_t *tcp, ssize_t nread, const uv_buf_t *buf) + { + HttpClient *client = static_cast(tcp->data); + + if (nread >= 0) { + client->read(buf->base, static_cast(nread)); + } else { + if (!client->m_quiet && nread != UV_EOF) { + LOG_ERR("[%s:%d] read error: \"%s\"", client->m_dns->host().data(), client->m_port, uv_strerror(static_cast(nread))); + } + + client->close(static_cast(nread)); + } + + delete [] buf->base; + }); + + client->handshake(); +} diff --git a/src/base/net/http/HttpClient.h b/src/base/net/http/HttpClient.h new file mode 100644 index 00000000..c5dfc43d --- /dev/null +++ b/src/base/net/http/HttpClient.h @@ -0,0 +1,74 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2014-2019 heapwolf + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#ifndef XMRIG_HTTPCLIENT_H +#define XMRIG_HTTPCLIENT_H + + +#include "base/net/http/HttpContext.h" +#include "base/kernel/interfaces/IDnsListener.h" + + +namespace xmrig { + + +class String; + + +class HttpClient : public HttpContext, public IDnsListener +{ +public: + HttpClient(int method, const String &url, IHttpListener *listener, const char *data = nullptr, size_t size = 0); + ~HttpClient() override; + + inline uint16_t port() const { return m_port; } + inline void setQuiet(bool quiet) { m_quiet = quiet; } + + bool connect(const String &host, uint16_t port); + const String &host() const; + +protected: + void onResolved(const Dns &dns, int status) override; + + virtual void handshake(); + virtual void read(const char *data, size_t size); + virtual void write(const std::string &header); + + bool m_quiet; + +private: + static void onConnect(uv_connect_t *req, int status); + + Dns *m_dns; + uint16_t m_port; +}; + + +} // namespace xmrig + + +#endif // XMRIG_HTTPCLIENT_H + diff --git a/src/base/net/http/HttpContext.cpp b/src/base/net/http/HttpContext.cpp new file mode 100644 index 00000000..e97f989b --- /dev/null +++ b/src/base/net/http/HttpContext.cpp @@ -0,0 +1,228 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2014-2019 heapwolf + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include +#include + + +#include "3rdparty/http-parser/http_parser.h" +#include "base/kernel/interfaces/IHttpListener.h" +#include "base/net/http/HttpContext.h" + + +namespace xmrig { + +static http_parser_settings http_settings; +static std::map storage; +static uint64_t SEQUENCE = 0; + +} // namespace xmrig + + +xmrig::HttpContext::HttpContext(int parser_type, IHttpListener *listener) : + HttpData(SEQUENCE++), + m_wasHeaderValue(false), + m_listener(listener) +{ + storage[id()] = this; + + m_parser = new http_parser; + m_tcp = new uv_tcp_t; + + uv_tcp_init(uv_default_loop(), m_tcp); + uv_tcp_nodelay(m_tcp, 1); + + http_parser_init(m_parser, static_cast(parser_type)); + + m_parser->data = m_tcp->data = this; + + if (http_settings.on_message_complete == nullptr) { + attach(&http_settings); + } +} + + +xmrig::HttpContext::~HttpContext() +{ + delete m_tcp; + delete m_parser; +} + + +size_t xmrig::HttpContext::parse(const char *data, size_t size) +{ + return http_parser_execute(m_parser, &http_settings, data, size); +} + + +std::string xmrig::HttpContext::ip() const +{ + char ip[46] = {}; + sockaddr_storage addr = {}; + int size = sizeof(addr); + + uv_tcp_getpeername(m_tcp, reinterpret_cast(&addr), &size); + if (reinterpret_cast(&addr)->sin_family == AF_INET6) { + uv_ip6_name(reinterpret_cast(&addr), ip, 45); + } + else { + uv_ip4_name(reinterpret_cast(&addr), ip, 16); + } + + return ip; +} + + +void xmrig::HttpContext::close(int status) +{ + if (status < 0 && m_listener) { + this->status = status; + m_listener->onHttpData(*this); + } + + auto it = storage.find(id()); + if (it != storage.end()) { + storage.erase(it); + } + + if (!uv_is_closing(handle())) { + uv_close(handle(), [](uv_handle_t *handle) -> void { delete reinterpret_cast(handle->data); }); + } +} + + +xmrig::HttpContext *xmrig::HttpContext::get(uint64_t id) +{ + if (storage.count(id) == 0) { + return nullptr; + } + + return storage[id]; +} + + +void xmrig::HttpContext::closeAll() +{ + for (auto kv : storage) { + if (!uv_is_closing(kv.second->handle())) { + uv_close(kv.second->handle(), [](uv_handle_t *handle) -> void { delete reinterpret_cast(handle->data); }); + } + } +} + + +int xmrig::HttpContext::onHeaderField(http_parser *parser, const char *at, size_t length) +{ + HttpContext *ctx = static_cast(parser->data); + + if (ctx->m_wasHeaderValue) { + if (!ctx->m_lastHeaderField.empty()) { + ctx->setHeader(); + } + + ctx->m_lastHeaderField = std::string(at, length); + ctx->m_wasHeaderValue = false; + } else { + ctx->m_lastHeaderField += std::string(at, length); + } + + return 0; +} + + +int xmrig::HttpContext::onHeaderValue(http_parser *parser, const char *at, size_t length) +{ + HttpContext *ctx = static_cast(parser->data); + + if (!ctx->m_wasHeaderValue) { + ctx->m_lastHeaderValue = std::string(at, length); + ctx->m_wasHeaderValue = true; + } else { + ctx->m_lastHeaderValue += std::string(at, length); + } + + return 0; +} + + +void xmrig::HttpContext::attach(http_parser_settings *settings) +{ + settings->on_message_begin = nullptr; + settings->on_status = nullptr; + settings->on_chunk_header = nullptr; + settings->on_chunk_complete = nullptr; + + settings->on_url = [](http_parser *parser, const char *at, size_t length) -> int + { + static_cast(parser->data)->url = std::string(at, length); + return 0; + }; + + settings->on_header_field = onHeaderField; + settings->on_header_value = onHeaderValue; + + settings->on_headers_complete = [](http_parser* parser) -> int { + HttpContext *ctx = static_cast(parser->data); + ctx->status = parser->status_code; + + if (parser->type == HTTP_REQUEST) { + ctx->method = parser->method; + } + + if (!ctx->m_lastHeaderField.empty()) { + ctx->setHeader(); + } + + return 0; + }; + + settings->on_body = [](http_parser *parser, const char *at, size_t len) -> int + { + static_cast(parser->data)->body += std::string(at, len); + + return 0; + }; + + settings->on_message_complete = [](http_parser *parser) -> int + { + HttpContext *ctx = static_cast(parser->data); + ctx->m_listener->onHttpData(*ctx); + ctx->m_listener = nullptr; + + return 0; + }; +} + + +void xmrig::HttpContext::setHeader() +{ + std::transform(m_lastHeaderField.begin(), m_lastHeaderField.end(), m_lastHeaderField.begin(), ::tolower); + headers.insert({ m_lastHeaderField, m_lastHeaderValue }); + + m_lastHeaderField.clear(); + m_lastHeaderValue.clear(); +} + diff --git a/src/base/net/http/HttpContext.h b/src/base/net/http/HttpContext.h new file mode 100644 index 00000000..fbb453aa --- /dev/null +++ b/src/base/net/http/HttpContext.h @@ -0,0 +1,86 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2014-2019 heapwolf + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#ifndef XMRIG_HTTPCONTEXT_H +#define XMRIG_HTTPCONTEXT_H + + +typedef struct http_parser http_parser; +typedef struct http_parser_settings http_parser_settings; +typedef struct uv_connect_s uv_connect_t; +typedef struct uv_handle_s uv_handle_t; +typedef struct uv_stream_s uv_stream_t; +typedef struct uv_tcp_s uv_tcp_t; + + +#include "base/net/http/HttpData.h" + + +namespace xmrig { + + +class IHttpListener; + + +class HttpContext : public HttpData +{ +public: + HttpContext(int parser_type, IHttpListener *listener); + virtual ~HttpContext(); + + inline uv_stream_t *stream() const { return reinterpret_cast(m_tcp); } + inline uv_handle_t *handle() const { return reinterpret_cast(m_tcp); } + + size_t parse(const char *data, size_t size); + std::string ip() const; + void close(int status = 0); + + static HttpContext *get(uint64_t id); + static void closeAll(); + +protected: + uv_tcp_t *m_tcp; + +private: + static int onHeaderField(http_parser *parser, const char *at, size_t length); + static int onHeaderValue(http_parser *parser, const char *at, size_t length); + static void attach(http_parser_settings *settings); + + void setHeader(); + + bool m_wasHeaderValue; + http_parser *m_parser; + IHttpListener *m_listener; + std::string m_lastHeaderField; + std::string m_lastHeaderValue; +}; + + +} // namespace xmrig + + +#endif // XMRIG_HTTPCONTEXT_H + diff --git a/src/base/net/http/HttpData.h b/src/base/net/http/HttpData.h new file mode 100644 index 00000000..ceb19b8e --- /dev/null +++ b/src/base/net/http/HttpData.h @@ -0,0 +1,60 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2014-2019 heapwolf + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#ifndef XMRIG_HTTPDATA_H +#define XMRIG_HTTPDATA_H + + +#include +#include + + +namespace xmrig { + + +class HttpData +{ +public: + inline HttpData(uint64_t id) : method(0), status(0), m_id(id) {} + + inline uint64_t id() const { return m_id; } + + int method; + int status; + std::map headers; + std::string body; + std::string url; + +private: + const uint64_t m_id; +}; + + +} // namespace xmrig + + +#endif // XMRIG_HTTPDATA_H + diff --git a/src/base/net/http/HttpResponse.cpp b/src/base/net/http/HttpResponse.cpp new file mode 100644 index 00000000..7a4af838 --- /dev/null +++ b/src/base/net/http/HttpResponse.cpp @@ -0,0 +1,153 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2014-2019 heapwolf + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include +#include +#include + + +#include "3rdparty/http-parser/http_parser.h" +#include "base/io/log/Log.h" +#include "base/net/http/HttpContext.h" +#include "base/net/http/HttpResponse.h" +#include "base/tools/Baton.h" + + +namespace xmrig { + +static const char *kCRLF = "\r\n"; +static const char *kUserAgent = "user-agent"; + + +class WriteBaton : public Baton +{ +public: + inline WriteBaton(const std::stringstream &ss, const char *data, size_t size, HttpContext *ctx) : + m_ctx(ctx), + m_header(ss.str()) + { + bufs[0].len = m_header.size(); + bufs[0].base = const_cast(m_header.c_str()); + + if (data) { + bufs[1].len = size; + bufs[1].base = new char[size]; + memcpy(bufs[1].base, data, size); + } + else { + bufs[1].base = nullptr; + bufs[1].len = 0; + } + } + + + inline ~WriteBaton() + { + if (count() == 2) { + delete [] bufs[1].base; + } + + m_ctx->close(); + } + + + inline size_t count() const { return bufs[1].base == nullptr ? 1 : 2; } + inline size_t size() const { return bufs[0].len + bufs[1].len; } + inline static void onWrite(uv_write_t *req, int) { delete reinterpret_cast(req->data); } + + + uv_buf_t bufs[2]; + +private: + HttpContext *m_ctx; + std::string m_header; +}; + +} // namespace xmrig + + +xmrig::HttpResponse::HttpResponse(uint64_t id, int statusCode) : + m_id(id), + m_statusCode(statusCode) +{ +} + + +bool xmrig::HttpResponse::isAlive() const +{ + HttpContext *ctx = HttpContext::get(m_id); + + return ctx && uv_is_writable(ctx->stream()); +} + + +void xmrig::HttpResponse::end(const char *data, size_t size) +{ + if (!isAlive()) { + return; + } + + if (data && !size) { + size = strlen(data); + } + + if (size) { + setHeader("Content-Length", std::to_string(size)); + } + + setHeader("Connection", "close"); + + std::stringstream ss; + ss << "HTTP/1.1 " << statusCode() << " " << http_status_str(static_cast(statusCode())) << kCRLF; + + for (auto &header : m_headers) { + ss << header.first << ": " << header.second << kCRLF; + } + + ss << kCRLF; + + HttpContext *ctx = HttpContext::get(m_id); + WriteBaton *baton = new WriteBaton(ss, data, size, ctx); + +# ifndef APP_DEBUG + if (statusCode() >= 400) +# endif + { + const bool err = statusCode() >= 400; + + Log::print(err ? Log::ERR : Log::INFO, CYAN("%s ") CLEAR MAGENTA_BOLD("%s") WHITE_BOLD(" %s ") CSI "1;%dm%d " CLEAR WHITE_BOLD("%zu ") BLACK_BOLD("\"%s\""), + ctx->ip().c_str(), + http_method_str(static_cast(ctx->method)), + ctx->url.c_str(), + err ? 31 : 32, + statusCode(), + baton->size(), + ctx->headers.count(kUserAgent) ? ctx->headers.at(kUserAgent).c_str() : nullptr + ); + } + + uv_write(&baton->req, ctx->stream(), baton->bufs, baton->count(), WriteBaton::onWrite); +} diff --git a/src/base/net/http/HttpResponse.h b/src/base/net/http/HttpResponse.h new file mode 100644 index 00000000..c2bc9001 --- /dev/null +++ b/src/base/net/http/HttpResponse.h @@ -0,0 +1,61 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2014-2019 heapwolf + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#ifndef XMRIG_HTTPRESPONSE_H +#define XMRIG_HTTPRESPONSE_H + + +#include +#include + + +namespace xmrig { + + +class HttpResponse +{ +public: + HttpResponse(uint64_t id, int statusCode = 200); + + inline int statusCode() const { return m_statusCode; } + inline void setHeader(const std::string &key, const std::string &value) { m_headers.insert({ key, value }); } + inline void setStatus(int code) { m_statusCode = code; } + + bool isAlive() const; + void end(const char *data = nullptr, size_t size = 0); + +private: + const uint64_t m_id; + int m_statusCode; + std::map m_headers; +}; + + +} // namespace xmrig + + +#endif // XMRIG_HTTPRESPONSE_H + diff --git a/src/base/net/http/HttpServer.cpp b/src/base/net/http/HttpServer.cpp new file mode 100644 index 00000000..60db31f6 --- /dev/null +++ b/src/base/net/http/HttpServer.cpp @@ -0,0 +1,83 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2014-2019 heapwolf + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include +#include + + +#include "3rdparty/http-parser/http_parser.h" +#include "base/kernel/interfaces/IHttpListener.h" +#include "base/net/http/HttpContext.h" +#include "base/net/http/HttpResponse.h" +#include "base/net/http/HttpServer.h" + + +xmrig::HttpServer::HttpServer(IHttpListener *listener) : + m_listener(listener) +{ +} + + +xmrig::HttpServer::~HttpServer() +{ + HttpContext::closeAll(); +} + + +void xmrig::HttpServer::onConnection(uv_stream_t *stream, uint16_t) +{ + HttpContext *ctx = new HttpContext(HTTP_REQUEST, m_listener); + uv_accept(stream, ctx->stream()); + + uv_read_start(ctx->stream(), + [](uv_handle_t *, size_t suggested_size, uv_buf_t *buf) + { + buf->base = new char[suggested_size]; + +# ifdef _WIN32 + buf->len = static_cast(suggested_size); +# else + buf->len = suggested_size; +# endif + }, + [](uv_stream_t *tcp, ssize_t nread, const uv_buf_t *buf) + { + HttpContext *ctx = static_cast(tcp->data); + + if (nread >= 0) { + const size_t size = static_cast(nread); + const size_t parsed = ctx->parse(buf->base, size); + + if (parsed < size) { + ctx->close(); + } + } else { + ctx->close(); + } + + delete [] buf->base; + }); +} diff --git a/src/base/net/http/HttpServer.h b/src/base/net/http/HttpServer.h new file mode 100644 index 00000000..45f3843d --- /dev/null +++ b/src/base/net/http/HttpServer.h @@ -0,0 +1,62 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2014-2019 heapwolf + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#ifndef XMRIG_HTTPSERVER_H +#define XMRIG_HTTPSERVER_H + + +typedef struct http_parser http_parser; +typedef struct http_parser_settings http_parser_settings; + + +#include "base/kernel/interfaces/ITcpServerListener.h" + + +namespace xmrig { + + +class IHttpListener; + + +class HttpServer : public ITcpServerListener +{ +public: + HttpServer(IHttpListener *listener); + ~HttpServer() override; + +protected: + void onConnection(uv_stream_t *stream, uint16_t port) override; + +private: + IHttpListener *m_listener; +}; + + +} // namespace xmrig + + +#endif // XMRIG_HTTPSERVER_H + diff --git a/src/base/net/http/HttpsClient.cpp b/src/base/net/http/HttpsClient.cpp new file mode 100644 index 00000000..2c287330 --- /dev/null +++ b/src/base/net/http/HttpsClient.cpp @@ -0,0 +1,208 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2014-2019 heapwolf + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include +#include +#include + + +#include "base/io/log/Log.h" +#include "base/net/http/HttpsClient.h" +#include "base/tools/Buffer.h" + + +#ifdef _MSC_VER +# define strncasecmp(x,y,z) _strnicmp(x,y,z) +#endif + + +xmrig::HttpsClient::HttpsClient(int method, const String &url, IHttpListener *listener, const char *data, size_t size, const String &fingerprint) : + HttpClient(method, url, listener, data, size), + m_ready(false), + m_buf(), + m_ssl(nullptr), + m_fp(fingerprint) +{ + m_ctx = SSL_CTX_new(SSLv23_method()); + assert(m_ctx != nullptr); + + if (!m_ctx) { + return; + } + + m_writeBio = BIO_new(BIO_s_mem()); + m_readBio = BIO_new(BIO_s_mem()); + SSL_CTX_set_options(m_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); +} + + +xmrig::HttpsClient::~HttpsClient() +{ + if (m_ctx) { + SSL_CTX_free(m_ctx); + } + + if (m_ssl) { + SSL_free(m_ssl); + } +} + + +const char *xmrig::HttpsClient::fingerprint() const +{ + return m_ready ? m_fingerprint : nullptr; +} + + +const char *xmrig::HttpsClient::version() const +{ + return m_ready ? SSL_get_version(m_ssl) : nullptr; +} + + +void xmrig::HttpsClient::handshake() +{ + m_ssl = SSL_new(m_ctx); + assert(m_ssl != nullptr); + + if (!m_ssl) { + return; + } + + SSL_set_connect_state(m_ssl); + SSL_set_bio(m_ssl, m_readBio, m_writeBio); + SSL_set_tlsext_host_name(m_ssl, host().data()); + + SSL_do_handshake(m_ssl); + + flush(); +} + + +void xmrig::HttpsClient::read(const char *data, size_t size) +{ + BIO_write(m_readBio, data, size); + + if (!SSL_is_init_finished(m_ssl)) { + const int rc = SSL_connect(m_ssl); + + if (rc < 0 && SSL_get_error(m_ssl, rc) == SSL_ERROR_WANT_READ) { + flush(); + } else if (rc == 1) { + X509 *cert = SSL_get_peer_certificate(m_ssl); + if (!verify(cert)) { + X509_free(cert); + return close(UV_EPROTO); + } + + X509_free(cert); + m_ready = true; + + HttpClient::handshake(); + } + + return; + } + + int bytes_read = 0; + while ((bytes_read = SSL_read(m_ssl, m_buf, sizeof(m_buf))) > 0) { + HttpClient::read(m_buf, static_cast(bytes_read)); + } +} + + +void xmrig::HttpsClient::write(const std::string &header) +{ + SSL_write(m_ssl, (header + body).c_str(), header.size() + body.size()); + body.clear(); + + flush(); +} + + +bool xmrig::HttpsClient::verify(X509 *cert) +{ + if (cert == nullptr) { + return false; + } + + if (!verifyFingerprint(cert)) { + if (!m_quiet) { + LOG_ERR("[%s:%d] Failed to verify server certificate fingerprint", host().data(), port()); + + if (strlen(m_fingerprint) == 64 && !m_fp.isNull()) { + LOG_ERR("\"%s\" was given", m_fingerprint); + LOG_ERR("\"%s\" was configured", m_fp.data()); + } + } + + return false; + } + + return true; +} + + +bool xmrig::HttpsClient::verifyFingerprint(X509 *cert) +{ + const EVP_MD *digest = EVP_get_digestbyname("sha256"); + if (digest == nullptr) { + return false; + } + + unsigned char md[EVP_MAX_MD_SIZE]; + unsigned int dlen; + + if (X509_digest(cert, digest, md, &dlen) != 1) { + return false; + } + + Buffer::toHex(md, 32, m_fingerprint); + + return m_fp.isNull() || strncasecmp(m_fingerprint, m_fp.data(), 64) == 0; +} + + +void xmrig::HttpsClient::flush() +{ + uv_buf_t buf; + buf.len = BIO_get_mem_data(m_writeBio, &buf.base); + + if (buf.len == 0) { + return; + } + + bool result = false; + if (uv_is_writable(stream())) { + result = uv_try_write(stream(), &buf, 1) == static_cast(buf.len); + + if (!result) { + close(UV_EIO); + } + } + + (void) BIO_reset(m_writeBio); +} diff --git a/src/base/net/http/HttpsClient.h b/src/base/net/http/HttpsClient.h new file mode 100644 index 00000000..c6a22809 --- /dev/null +++ b/src/base/net/http/HttpsClient.h @@ -0,0 +1,77 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2014-2019 heapwolf + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#ifndef XMRIG_HTTPSCLIENT_H +#define XMRIG_HTTPSCLIENT_H + + +typedef struct bio_st BIO; +typedef struct ssl_ctx_st SSL_CTX; +typedef struct ssl_st SSL; +typedef struct x509_st X509; + + +#include "base/net/http/HttpClient.h" +#include "base/tools/String.h" + + +namespace xmrig { + + +class HttpsClient : public HttpClient +{ +public: + HttpsClient(int method, const String &url, IHttpListener *listener, const char *data, size_t size, const String &fingerprint); + ~HttpsClient() override; + + const char *fingerprint() const; + const char *version() const; + +protected: + void handshake() override; + void read(const char *data, size_t size) override; + void write(const std::string &header) override; + +private: + bool verify(X509 *cert); + bool verifyFingerprint(X509 *cert); + void flush(); + + BIO *m_readBio; + BIO *m_writeBio; + bool m_ready; + char m_buf[1024 * 2]; + char m_fingerprint[32 * 2 + 8]; + SSL *m_ssl; + SSL_CTX *m_ctx; + String m_fp; +}; + + +} // namespace xmrig + + +#endif // XMRIG_HTTPSCLIENT_H diff --git a/src/base/net/stratum/BaseClient.cpp b/src/base/net/stratum/BaseClient.cpp new file mode 100644 index 00000000..325fce1c --- /dev/null +++ b/src/base/net/stratum/BaseClient.cpp @@ -0,0 +1,63 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include "base/kernel/interfaces/IClientListener.h" +#include "base/net/stratum/BaseClient.h" +#include "base/net/stratum/SubmitResult.h" + + +namespace xmrig { + +int64_t BaseClient::m_sequence = 1; + +} /* namespace xmrig */ + + +xmrig::BaseClient::BaseClient(int id, IClientListener *listener) : + m_quiet(false), + m_listener(listener), + m_id(id), + m_retries(5), + m_failures(0), + m_state(UnconnectedState), + m_retryPause(5000), + m_enabled(true) +{ +} + + +bool xmrig::BaseClient::handleSubmitResponse(int64_t id, const char *error) +{ + auto it = m_results.find(id); + if (it != m_results.end()) { + it->second.done(); + m_listener->onResultAccepted(this, it->second, error); + m_results.erase(it); + + return true; + } + + return false; +} diff --git a/src/base/net/stratum/BaseClient.h b/src/base/net/stratum/BaseClient.h new file mode 100644 index 00000000..56bdc126 --- /dev/null +++ b/src/base/net/stratum/BaseClient.h @@ -0,0 +1,97 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_BASECLIENT_H +#define XMRIG_BASECLIENT_H + + +#include + + +#include "base/kernel/interfaces/IClient.h" +#include "base/net/stratum/Job.h" +#include "base/net/stratum/Pool.h" + + +namespace xmrig { + + +class IClientListener; +class SubmitResult; + + +class BaseClient : public IClient +{ +public: + BaseClient(int id, IClientListener *listener); + + inline bool isEnabled() const override { return m_enabled; } + inline const Job &job() const override { return m_job; } + inline const Pool &pool() const override { return m_pool; } + inline const String &ip() const override { return m_ip; } + inline int id() const override { return m_id; } + inline void setAlgo(const Algorithm &algo) override { m_pool.setAlgo(algo); } + inline void setEnabled(bool enabled) override { m_enabled = enabled; } + inline void setPool(const Pool &pool) override { if (pool.isValid()) { m_pool = pool; } } + inline void setQuiet(bool quiet) override { m_quiet = quiet; } + inline void setRetries(int retries) override { m_retries = retries; } + inline void setRetryPause(uint64_t ms) override { m_retryPause = ms; } + +protected: + enum SocketState { + UnconnectedState, + HostLookupState, + ConnectingState, + ConnectedState, + ClosingState, + ReconnectingState + }; + + inline bool isQuiet() const { return m_quiet || m_failures >= m_retries; } + + bool handleSubmitResponse(int64_t id, const char *error = nullptr); + + bool m_quiet; + IClientListener *m_listener; + int m_id; + int m_retries; + int64_t m_failures; + Job m_job; + Pool m_pool; + SocketState m_state; + std::map m_results; + String m_ip; + uint64_t m_retryPause; + + static int64_t m_sequence; + +private: + bool m_enabled; +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_BASECLIENT_H */ diff --git a/src/base/net/stratum/Client.cpp b/src/base/net/stratum/Client.cpp new file mode 100644 index 00000000..229147ba --- /dev/null +++ b/src/base/net/stratum/Client.cpp @@ -0,0 +1,925 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include + + +#ifdef XMRIG_FEATURE_TLS +# include +# include +# include "base/net/stratum/Tls.h" +#endif + + +#include "base/io/json/Json.h" +#include "base/io/json/JsonRequest.h" +#include "base/io/log/Log.h" +#include "base/kernel/interfaces/IClientListener.h" +#include "base/net/dns/Dns.h" +#include "base/net/stratum/Client.h" +#include "base/tools/Buffer.h" +#include "base/tools/Chrono.h" +#include "net/JobResult.h" +#include "rapidjson/document.h" +#include "rapidjson/error/en.h" +#include "rapidjson/stringbuffer.h" +#include "rapidjson/writer.h" + + +#ifdef _MSC_VER +# define strncasecmp(x,y,z) _strnicmp(x,y,z) +#endif + + +namespace xmrig { + +Storage Client::m_storage; + +} /* namespace xmrig */ + + +#ifdef APP_DEBUG +static const char *states[] = { + "unconnected", + "host-lookup", + "connecting", + "connected", + "closing", + "reconnecting" +}; +#endif + + +xmrig::Client::Client(int id, const char *agent, IClientListener *listener) : + BaseClient(id, listener), + m_agent(agent) +{ + m_key = m_storage.add(this); + m_dns = new Dns(this); +} + + +xmrig::Client::~Client() +{ + delete m_dns; + delete m_socket; +} + + +bool xmrig::Client::disconnect() +{ + m_keepAlive = 0; + m_expire = 0; + m_failures = -1; + + return close(); +} + + +bool xmrig::Client::isTLS() const +{ +# ifdef XMRIG_FEATURE_TLS + return m_pool.isTLS() && m_tls; +# else + return false; +# endif +} + + +const char *xmrig::Client::tlsFingerprint() const +{ +# ifdef XMRIG_FEATURE_TLS + if (isTLS() && m_pool.fingerprint() == nullptr) { + return m_tls->fingerprint(); + } +# endif + + return nullptr; +} + + +const char *xmrig::Client::tlsVersion() const +{ +# ifdef XMRIG_FEATURE_TLS + if (isTLS()) { + return m_tls->version(); + } +# endif + + return nullptr; +} + + +int64_t xmrig::Client::submit(const JobResult &result) +{ +# ifndef XMRIG_PROXY_PROJECT + if (result.clientId != m_rpcId) { + return -1; + } +# endif + + using namespace rapidjson; + +# ifdef XMRIG_PROXY_PROJECT + const char *nonce = result.nonce; + const char *data = result.result; +# else + char *nonce = m_sendBuf; + char *data = m_sendBuf + 16; + + Buffer::toHex(reinterpret_cast(&result.nonce), 4, nonce); + nonce[8] = '\0'; + + Buffer::toHex(result.result(), 32, data); + data[64] = '\0'; +# endif + + Document doc(kObjectType); + auto &allocator = doc.GetAllocator(); + + Value params(kObjectType); + params.AddMember("id", StringRef(m_rpcId.data()), allocator); + params.AddMember("job_id", StringRef(result.jobId.data()), allocator); + params.AddMember("nonce", StringRef(nonce), allocator); + params.AddMember("result", StringRef(data), allocator); + + if (has() && result.algorithm.isValid()) { + params.AddMember("algo", StringRef(result.algorithm.shortName()), allocator); + } + + JsonRequest::create(doc, m_sequence, "submit", params); + +# ifdef XMRIG_PROXY_PROJECT + m_results[m_sequence] = SubmitResult(m_sequence, result.diff, result.actualDiff(), result.id); +# else + m_results[m_sequence] = SubmitResult(m_sequence, result.diff, result.actualDiff()); +# endif + + return send(doc); +} + + +void xmrig::Client::connect() +{ +# ifdef XMRIG_FEATURE_TLS + if (m_pool.isTLS()) { + m_tls = new Tls(this); + } +# endif + + resolve(m_pool.host()); +} + + +void xmrig::Client::connect(const Pool &pool) +{ + setPool(pool); + connect(); +} + + +void xmrig::Client::deleteLater() +{ + if (!m_listener) { + return; + } + + m_listener = nullptr; + + if (!disconnect()) { + m_storage.remove(m_key); + } +} + + +void xmrig::Client::tick(uint64_t now) +{ + if (m_state == ConnectedState) { + if (m_expire && now > m_expire) { + LOG_DEBUG_ERR("[%s] timeout", url()); + close(); + } + else if (m_keepAlive && now > m_keepAlive) { + ping(); + } + + return; + } + + if (m_state == ReconnectingState && m_expire && now > m_expire) { + return connect(); + } + + if (m_state == ConnectingState && m_expire && now > m_expire) { + return reconnect(); + } +} + + +void xmrig::Client::onResolved(const Dns &dns, int status) +{ + assert(m_listener != nullptr); + if (!m_listener) { + return reconnect(); + } + + if (status < 0 && dns.isEmpty()) { + if (!isQuiet()) { + LOG_ERR("[%s] DNS error: \"%s\"", url(), uv_strerror(status)); + } + + return reconnect(); + } + + const DnsRecord &record = dns.get(); + m_ip = record.ip(); + + connect(record.addr(m_pool.port())); +} + + +bool xmrig::Client::close() +{ + if (m_state == ClosingState) { + return m_socket != nullptr; + } + + if (m_state == UnconnectedState || m_socket == nullptr) { + return false; + } + + setState(ClosingState); + + if (uv_is_closing(reinterpret_cast(m_socket)) == 0) { + uv_close(reinterpret_cast(m_socket), Client::onClose); + } + + return true; +} + + +bool xmrig::Client::isCriticalError(const char *message) +{ + if (!message) { + return false; + } + + if (strncasecmp(message, "Unauthenticated", 15) == 0) { + return true; + } + + if (strncasecmp(message, "your IP is banned", 17) == 0) { + return true; + } + + if (strncasecmp(message, "IP Address currently banned", 27) == 0) { + return true; + } + + return false; +} + + +bool xmrig::Client::parseJob(const rapidjson::Value ¶ms, int *code) +{ + if (!params.IsObject()) { + *code = 2; + return false; + } + + Job job(has(), m_pool.algorithm(), m_rpcId); + + if (!job.setId(params["job_id"].GetString())) { + *code = 3; + return false; + } + + if (!job.setBlob(params["blob"].GetString())) { + *code = 4; + return false; + } + + if (!job.setTarget(params["target"].GetString())) { + *code = 5; + return false; + } + + const char *algo = Json::getString(params, "algo"); + if (algo) { + job.setAlgorithm(algo); + } + + job.setHeight(Json::getUint64(params, "height")); + + if (!verifyAlgorithm(job.algorithm(), algo)) { + *code = 6; + return false; + } + + if (job.algorithm().family() == Algorithm::RANDOM_X && !job.setSeedHash(Json::getString(params, "seed_hash"))) { + if (!isQuiet()) { + LOG_ERR("[%s] failed to parse field \"seed_hash\" required by RandomX", url(), algo); + } + + *code = 7; + return false; + } + + m_job.setClientId(m_rpcId); + + if (m_job != job) { + m_jobs++; + m_job = std::move(job); + return true; + } + + if (m_jobs == 0) { // https://github.com/xmrig/xmrig/issues/459 + return false; + } + + if (!isQuiet()) { + LOG_WARN("[%s] duplicate job received, reconnect", url()); + } + + close(); + return false; +} + + +bool xmrig::Client::parseLogin(const rapidjson::Value &result, int *code) +{ + m_rpcId = result["id"].GetString(); + if (m_rpcId.isNull()) { + *code = 1; + return false; + } + + parseExtensions(result); + + const bool rc = parseJob(result["job"], code); + m_jobs = 0; + + return rc; +} + + +bool xmrig::Client::send(BIO *bio) +{ +# ifdef XMRIG_FEATURE_TLS + uv_buf_t buf; + buf.len = BIO_get_mem_data(bio, &buf.base); + + if (buf.len == 0) { + return true; + } + + LOG_DEBUG("[%s] TLS send (%d bytes)", url(), static_cast(buf.len)); + + bool result = false; + if (state() == ConnectedState && uv_is_writable(m_stream)) { + result = uv_try_write(m_stream, &buf, 1) > 0; + + if (!result) { + close(); + } + } + else { + LOG_DEBUG_ERR("[%s] send failed, invalid state: %d", url(), m_state); + } + + (void) BIO_reset(bio); + + return result; +# else + return false; +# endif +} + + +bool xmrig::Client::verifyAlgorithm(const Algorithm &algorithm, const char *algo) const +{ + if (!algorithm.isValid()) { + if (!isQuiet()) { + LOG_ERR("[%s] Unknown/unsupported algorithm \"%s\" detected, reconnect", url(), algo); + } + + return false; + } + + bool ok = true; + m_listener->onVerifyAlgorithm(this, algorithm, &ok); + + if (!ok && !isQuiet()) { + LOG_ERR("[%s] Incompatible/disabled algorithm \"%s\" detected, reconnect", url(), algorithm.shortName()); + } + + return ok; +} + + +int xmrig::Client::resolve(const String &host) +{ + setState(HostLookupState); + + m_recvBuf.reset(); + + if (m_failures == -1) { + m_failures = 0; + } + + if (!m_dns->resolve(host)) { + if (!isQuiet()) { + LOG_ERR("[%s:%u] getaddrinfo error: \"%s\"", host.data(), m_pool.port(), uv_strerror(m_dns->status())); + } + + return 1; + } + + return 0; +} + + +int64_t xmrig::Client::send(const rapidjson::Document &doc) +{ + using namespace rapidjson; + + StringBuffer buffer(nullptr, 512); + Writer writer(buffer); + doc.Accept(writer); + + const size_t size = buffer.GetSize(); + if (size > (sizeof(m_sendBuf) - 2)) { + LOG_ERR("[%s] send failed: \"send buffer overflow: %zu > %zu\"", url(), size, (sizeof(m_sendBuf) - 2)); + close(); + return -1; + } + + memcpy(m_sendBuf, buffer.GetString(), size); + m_sendBuf[size] = '\n'; + m_sendBuf[size + 1] = '\0'; + + return send(size + 1); +} + + +int64_t xmrig::Client::send(size_t size) +{ + LOG_DEBUG("[%s] send (%d bytes): \"%.*s\"", url(), size, static_cast(size) - 1, m_sendBuf); + +# ifdef XMRIG_FEATURE_TLS + if (isTLS()) { + if (!m_tls->send(m_sendBuf, size)) { + return -1; + } + } + else +# endif + { + if (state() != ConnectedState || !uv_is_writable(m_stream)) { + LOG_DEBUG_ERR("[%s] send failed, invalid state: %d", url(), m_state); + return -1; + } + + uv_buf_t buf = uv_buf_init(m_sendBuf, (unsigned int) size); + + if (uv_try_write(m_stream, &buf, 1) < 0) { + close(); + return -1; + } + } + + m_expire = Chrono::steadyMSecs() + kResponseTimeout; + return m_sequence++; +} + + +void xmrig::Client::connect(sockaddr *addr) +{ + setState(ConnectingState); + + uv_connect_t *req = new uv_connect_t; + req->data = m_storage.ptr(m_key); + + m_socket = new uv_tcp_t; + m_socket->data = m_storage.ptr(m_key); + + uv_tcp_init(uv_default_loop(), m_socket); + uv_tcp_nodelay(m_socket, 1); + +# ifndef WIN32 + uv_tcp_keepalive(m_socket, 1, 60); +# endif + + uv_tcp_connect(req, m_socket, addr, onConnect); + + delete addr; +} + + +void xmrig::Client::handshake() +{ +# ifdef XMRIG_FEATURE_TLS + if (isTLS()) { + m_expire = Chrono::steadyMSecs() + kResponseTimeout; + + m_tls->handshake(); + } + else +# endif + { + login(); + } +} + + +void xmrig::Client::login() +{ + using namespace rapidjson; + m_results.clear(); + + Document doc(kObjectType); + auto &allocator = doc.GetAllocator(); + + Value params(kObjectType); + params.AddMember("login", m_pool.user().toJSON(), allocator); + params.AddMember("pass", m_pool.password().toJSON(), allocator); + params.AddMember("agent", StringRef(m_agent), allocator); + + if (!m_pool.rigId().isNull()) { + params.AddMember("rigid", m_pool.rigId().toJSON(), allocator); + } + + m_listener->onLogin(this, doc, params); + + JsonRequest::create(doc, 1, "login", params); + + send(doc); +} + + +void xmrig::Client::onClose() +{ + delete m_socket; + + m_stream = nullptr; + m_socket = nullptr; + setState(UnconnectedState); + +# ifdef XMRIG_FEATURE_TLS + if (m_tls) { + delete m_tls; + m_tls = nullptr; + } +# endif + + reconnect(); +} + + +void xmrig::Client::parse(char *line, size_t len) +{ + startTimeout(); + + LOG_DEBUG("[%s] received (%d bytes): \"%.*s\"", url(), len, static_cast(len), line); + + if (len < 32 || line[0] != '{') { + if (!isQuiet()) { + LOG_ERR("[%s] JSON decode failed", url()); + } + + return; + } + + rapidjson::Document doc; + if (doc.ParseInsitu(line).HasParseError()) { + if (!isQuiet()) { + LOG_ERR("[%s] JSON decode failed: \"%s\"", url(), rapidjson::GetParseError_En(doc.GetParseError())); + } + + return; + } + + if (!doc.IsObject()) { + return; + } + + const rapidjson::Value &id = doc["id"]; + if (id.IsInt64()) { + parseResponse(id.GetInt64(), doc["result"], doc["error"]); + } + else { + parseNotification(doc["method"].GetString(), doc["params"], doc["error"]); + } +} + + +void xmrig::Client::parseExtensions(const rapidjson::Value &result) +{ + m_extensions.reset(); + + if (!result.HasMember("extensions")) { + return; + } + + const rapidjson::Value &extensions = result["extensions"]; + if (!extensions.IsArray()) { + return; + } + + for (const rapidjson::Value &ext : extensions.GetArray()) { + if (!ext.IsString()) { + continue; + } + + const char *name = ext.GetString(); + + if (strcmp(name, "algo") == 0) { + setExtension(EXT_ALGO, true); + } + else if (strcmp(name, "nicehash") == 0) { + setExtension(EXT_NICEHASH, true); + } + else if (strcmp(name, "connect") == 0) { + setExtension(EXT_CONNECT, true); + } + else if (strcmp(name, "keepalive") == 0) { + setExtension(EXT_KEEPALIVE, true); + } +# ifdef XMRIG_FEATURE_TLS + else if (strcmp(name, "tls") == 0) { + setExtension(EXT_TLS, true); + } +# endif + } +} + + +void xmrig::Client::parseNotification(const char *method, const rapidjson::Value ¶ms, const rapidjson::Value &error) +{ + if (error.IsObject()) { + if (!isQuiet()) { + LOG_ERR("[%s] error: \"%s\", code: %d", url(), error["message"].GetString(), error["code"].GetInt()); + } + return; + } + + if (!method) { + return; + } + + if (strcmp(method, "job") == 0) { + int code = -1; + if (parseJob(params, &code)) { + m_listener->onJobReceived(this, m_job, params); + } + else { + close(); + } + + return; + } + + LOG_WARN("[%s] unsupported method: \"%s\"", url(), method); +} + + +void xmrig::Client::parseResponse(int64_t id, const rapidjson::Value &result, const rapidjson::Value &error) +{ + if (error.IsObject()) { + const char *message = error["message"].GetString(); + + if (!handleSubmitResponse(id, message) && !isQuiet()) { + LOG_ERR("[%s] error: " RED_BOLD("\"%s\"") RED_S ", code: %d", url(), message, error["code"].GetInt()); + } + + if (m_id == 1 || isCriticalError(message)) { + close(); + } + + return; + } + + if (!result.IsObject()) { + return; + } + + if (id == 1) { + int code = -1; + if (!parseLogin(result, &code)) { + if (!isQuiet()) { + LOG_ERR("[%s] login error code: %d", url(), code); + } + + close(); + return; + } + + m_failures = 0; + m_listener->onLoginSuccess(this); + m_listener->onJobReceived(this, m_job, result["job"]); + return; + } + + handleSubmitResponse(id); +} + + +void xmrig::Client::ping() +{ + send(snprintf(m_sendBuf, sizeof(m_sendBuf), "{\"id\":%" PRId64 ",\"jsonrpc\":\"2.0\",\"method\":\"keepalived\",\"params\":{\"id\":\"%s\"}}\n", m_sequence, m_rpcId.data())); + + m_keepAlive = 0; +} + + +void xmrig::Client::read(ssize_t nread) +{ + const size_t size = static_cast(nread); + + if (nread > 0 && size > m_recvBuf.available()) { + nread = UV_ENOBUFS; + } + + if (nread < 0) { + if (!isQuiet()) { + LOG_ERR("[%s] read error: \"%s\"", url(), uv_strerror(static_cast(nread))); + } + + close(); + return; + } + + assert(m_listener != nullptr); + if (!m_listener) { + return reconnect(); + } + + m_recvBuf.nread(size); + +# ifdef XMRIG_FEATURE_TLS + if (isTLS()) { + LOG_DEBUG("[%s] TLS received (%d bytes)", url(), static_cast(nread)); + + m_tls->read(m_recvBuf.base(), m_recvBuf.pos()); + m_recvBuf.reset(); + } + else +# endif + { + m_recvBuf.getline(this); + } +} + + +void xmrig::Client::reconnect() +{ + if (!m_listener) { + m_storage.remove(m_key); + + return; + } + + m_keepAlive = 0; + + if (m_failures == -1) { + return m_listener->onClose(this, -1); + } + + setState(ReconnectingState); + + m_failures++; + m_listener->onClose(this, static_cast(m_failures)); +} + + +void xmrig::Client::setState(SocketState state) +{ + LOG_DEBUG("[%s] state: \"%s\"", url(), states[state]); + + if (m_state == state) { + return; + } + + switch (state) { + case HostLookupState: + m_expire = 0; + break; + + case ConnectingState: + m_expire = Chrono::steadyMSecs() + kConnectTimeout; + break; + + case ReconnectingState: + m_expire = Chrono::steadyMSecs() + m_retryPause; + break; + + default: + break; + } + + m_state = state; +} + + +void xmrig::Client::startTimeout() +{ + m_expire = 0; + + if (has()) { + const uint64_t ms = static_cast(m_pool.keepAlive() > 0 ? m_pool.keepAlive() : Pool::kKeepAliveTimeout) * 1000; + + m_keepAlive = Chrono::steadyMSecs() + ms; + } +} + + +void xmrig::Client::onAllocBuffer(uv_handle_t *handle, size_t, uv_buf_t *buf) +{ + auto client = getClient(handle->data); + if (!client) { + return; + } + + buf->base = client->m_recvBuf.current(); + +# ifdef _WIN32 + buf->len = static_cast(client->m_recvBuf.available()); +# else + buf->len = client->m_recvBuf.available(); +# endif +} + + +void xmrig::Client::onClose(uv_handle_t *handle) +{ + auto client = getClient(handle->data); + if (!client) { + return; + } + + client->onClose(); +} + + +void xmrig::Client::onConnect(uv_connect_t *req, int status) +{ + auto client = getClient(req->data); + if (!client) { + delete req; + return; + } + + if (status < 0) { + if (!client->isQuiet()) { + LOG_ERR("[%s] connect error: \"%s\"", client->url(), uv_strerror(status)); + } + + delete req; + client->close(); + return; + } + + client->m_stream = static_cast(req->handle); + client->m_stream->data = req->data; + client->setState(ConnectedState); + + uv_read_start(client->m_stream, onAllocBuffer, onRead); + delete req; + + client->handshake(); +} + + +void xmrig::Client::onRead(uv_stream_t *stream, ssize_t nread, const uv_buf_t *) +{ + auto client = getClient(stream->data); + if (client) { + client->read(nread); + } +} diff --git a/src/base/net/stratum/Client.h b/src/base/net/stratum/Client.h new file mode 100644 index 00000000..ff2bf7f6 --- /dev/null +++ b/src/base/net/stratum/Client.h @@ -0,0 +1,151 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_CLIENT_H +#define XMRIG_CLIENT_H + + +#include +#include +#include +#include + + +#include "base/kernel/interfaces/IDnsListener.h" +#include "base/kernel/interfaces/ILineListener.h" +#include "base/net/stratum/BaseClient.h" +#include "base/net/stratum/Job.h" +#include "base/net/stratum/Pool.h" +#include "base/net/stratum/SubmitResult.h" +#include "base/net/tools/RecvBuf.h" +#include "base/net/tools/Storage.h" +#include "crypto/common/Algorithm.h" + + +typedef struct bio_st BIO; + + +namespace xmrig { + + +class IClientListener; +class JobResult; + + +class Client : public BaseClient, public IDnsListener, public ILineListener +{ +public: + constexpr static uint64_t kConnectTimeout = 20 * 1000; + constexpr static uint64_t kResponseTimeout = 20 * 1000; + +# ifdef XMRIG_FEATURE_TLS + constexpr static size_t kInputBufferSize = 1024 * 16; +# else + constexpr static size_t kInputBufferSize = 1024 * 2; +# endif + + Client(int id, const char *agent, IClientListener *listener); + ~Client() override; + +protected: + bool disconnect() override; + bool isTLS() const override; + const char *tlsFingerprint() const override; + const char *tlsVersion() const override; + int64_t submit(const JobResult &result) override; + void connect() override; + void connect(const Pool &pool) override; + void deleteLater() override; + void tick(uint64_t now) override; + + void onResolved(const Dns &dns, int status) override; + + inline bool hasExtension(Extension extension) const noexcept override { return m_extensions.test(extension); } + inline const char *mode() const override { return "pool"; } + inline void onLine(char *line, size_t size) override { parse(line, size); } + +private: + class Tls; + + bool close(); + bool isCriticalError(const char *message); + bool parseJob(const rapidjson::Value ¶ms, int *code); + bool parseLogin(const rapidjson::Value &result, int *code); + bool send(BIO *bio); + bool verifyAlgorithm(const Algorithm &algorithm, const char *algo) const; + int resolve(const String &host); + int64_t send(const rapidjson::Document &doc); + int64_t send(size_t size); + void connect(sockaddr *addr); + void handshake(); + void login(); + void onClose(); + void parse(char *line, size_t len); + void parseExtensions(const rapidjson::Value &result); + void parseNotification(const char *method, const rapidjson::Value ¶ms, const rapidjson::Value &error); + void parseResponse(int64_t id, const rapidjson::Value &result, const rapidjson::Value &error); + void ping(); + void read(ssize_t nread); + void reconnect(); + void setState(SocketState state); + void startTimeout(); + + inline const char *url() const { return m_pool.url(); } + inline SocketState state() const { return m_state; } + inline void setExtension(Extension ext, bool enable) noexcept { m_extensions.set(ext, enable); } + template inline bool has() const noexcept { return m_extensions.test(ext); } + + static void onAllocBuffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf); + static void onClose(uv_handle_t *handle); + static void onConnect(uv_connect_t *req, int status); + static void onRead(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf); + + static inline Client *getClient(void *data) { return m_storage.get(data); } + + char m_sendBuf[2048] = { 0 }; + const char *m_agent; + Dns *m_dns; + RecvBuf m_recvBuf; + std::bitset m_extensions; + String m_rpcId; + Tls *m_tls = nullptr; + uint64_t m_expire = 0; + uint64_t m_jobs = 0; + uint64_t m_keepAlive = 0; + uintptr_t m_key = 0; + uv_stream_t *m_stream = nullptr; + uv_tcp_t *m_socket = nullptr; + + static Storage m_storage; +}; + + +template<> inline bool Client::has() const noexcept { return m_extensions.test(EXT_NICEHASH) || m_pool.isNicehash(); } +template<> inline bool Client::has() const noexcept { return m_extensions.test(EXT_KEEPALIVE) || m_pool.keepAlive() > 0; } + + +} /* namespace xmrig */ + + +#endif /* XMRIG_CLIENT_H */ diff --git a/src/base/net/stratum/DaemonClient.cpp b/src/base/net/stratum/DaemonClient.cpp new file mode 100644 index 00000000..0c141c7d --- /dev/null +++ b/src/base/net/stratum/DaemonClient.cpp @@ -0,0 +1,379 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2019 Howard Chu + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include +#include + + +#include "3rdparty/http-parser/http_parser.h" +#include "base/io/json/Json.h" +#include "base/io/json/JsonRequest.h" +#include "base/io/log/Log.h" +#include "base/kernel/interfaces/IClientListener.h" +#include "base/net/http/HttpClient.h" +#include "base/net/stratum/DaemonClient.h" +#include "base/net/stratum/SubmitResult.h" +#include "base/tools/Buffer.h" +#include "base/tools/Timer.h" +#include "net/JobResult.h" +#include "rapidjson/document.h" +#include "rapidjson/error/en.h" +#include "rapidjson/stringbuffer.h" +#include "rapidjson/writer.h" + + +#ifdef XMRIG_FEATURE_TLS +# include "base/net/http/HttpsClient.h" +#endif + + +namespace xmrig { + +static const char *kBlocktemplateBlob = "blocktemplate_blob"; +static const char *kGetHeight = "/getheight"; +static const char *kGetInfo = "/getinfo"; +static const char *kHash = "hash"; +static const char *kHeight = "height"; +static const char *kJsonRPC = "/json_rpc"; + +} + + +xmrig::DaemonClient::DaemonClient(int id, IClientListener *listener) : + BaseClient(id, listener), + m_monero(true) +{ + m_timer = new Timer(this); +} + + +xmrig::DaemonClient::~DaemonClient() +{ + delete m_timer; +} + + +bool xmrig::DaemonClient::disconnect() +{ + if (m_state != UnconnectedState) { + setState(UnconnectedState); + } + + return true; +} + + +bool xmrig::DaemonClient::isTLS() const +{ +# ifdef XMRIG_FEATURE_TLS + return m_pool.isTLS(); +# else + return false; +# endif +} + + +int64_t xmrig::DaemonClient::submit(const JobResult &result) +{ + if (result.jobId != (m_blocktemplate.data() + m_blocktemplate.size() - 32)) { + return -1; + } + +# ifdef XMRIG_PROXY_PROJECT + memcpy(m_blocktemplate.data() + 78, result.nonce, 8); +# else + Buffer::toHex(reinterpret_cast(&result.nonce), 4, m_blocktemplate.data() + 78); +# endif + + using namespace rapidjson; + Document doc(kObjectType); + + Value params(kArrayType); + params.PushBack(m_blocktemplate.toJSON(), doc.GetAllocator()); + + JsonRequest::create(doc, m_sequence, "submitblock", params); + +# ifdef XMRIG_PROXY_PROJECT + m_results[m_sequence] = SubmitResult(m_sequence, result.diff, result.actualDiff(), result.id); +# else + m_results[m_sequence] = SubmitResult(m_sequence, result.diff, result.actualDiff()); +# endif + + send(HTTP_POST, kJsonRPC, doc); + + return m_sequence++; +} + + +void xmrig::DaemonClient::connect() +{ + setState(ConnectingState); + getBlockTemplate(); +} + + +void xmrig::DaemonClient::connect(const Pool &pool) +{ + setPool(pool); + connect(); +} + + +void xmrig::DaemonClient::onHttpData(const HttpData &data) +{ + if (data.status != HTTP_STATUS_OK) { + return retry(); + } + + LOG_DEBUG("[%s:%d] received (%d bytes): \"%.*s\"", m_pool.host().data(), m_pool.port(), static_cast(data.body.size()), static_cast(data.body.size()), data.body.c_str()); + + m_ip = static_cast(data).ip().c_str(); + +# ifdef XMRIG_FEATURE_TLS + if (isTLS()) { + m_tlsVersion = static_cast(data).version(); + m_tlsFingerprint = static_cast(data).fingerprint(); + } +# endif + + rapidjson::Document doc; + if (doc.Parse(data.body.c_str()).HasParseError()) { + if (!isQuiet()) { + LOG_ERR("[%s:%d] JSON decode failed: \"%s\"", m_pool.host().data(), m_pool.port(), rapidjson::GetParseError_En(doc.GetParseError())); + } + + return retry(); + } + + if (data.method == HTTP_GET) { + if (data.url == kGetHeight) { + if (!doc.HasMember(kHash)) { + m_monero = false; + + return send(HTTP_GET, kGetInfo); + } + + if (isOutdated(Json::getUint64(doc, kHeight), Json::getString(doc, kHash))) { + getBlockTemplate(); + } + } + else if (data.url == kGetInfo && isOutdated(Json::getUint64(doc, kHeight), Json::getString(doc, "top_block_hash"))) { + getBlockTemplate(); + } + + return; + } + + if (!parseResponse(Json::getInt64(doc, "id", -1), Json::getObject(doc, "result"), Json::getObject(doc, "error"))) { + retry(); + } +} + + +void xmrig::DaemonClient::onTimer(const Timer *) +{ + if (m_state == ConnectingState) { + getBlockTemplate(); + } + else if (m_state == ConnectedState) { + send(HTTP_GET, m_monero ? kGetHeight : kGetInfo); + } +} + + +bool xmrig::DaemonClient::isOutdated(uint64_t height, const char *hash) const +{ + return m_job.height() != height || m_prevHash != hash; +} + + +bool xmrig::DaemonClient::parseJob(const rapidjson::Value ¶ms, int *code) +{ + Job job(false, m_pool.algorithm(), String()); + + String blocktemplate = Json::getString(params, kBlocktemplateBlob); + if (blocktemplate.isNull() || !job.setBlob(Json::getString(params, "blockhashing_blob"))) { + *code = 4; + return false; + } + + job.setSeedHash(Json::getString(params, "seed_hash")); + job.setHeight(Json::getUint64(params, kHeight)); + job.setDiff(Json::getUint64(params, "difficulty")); + job.setId(blocktemplate.data() + blocktemplate.size() - 32); + + m_job = std::move(job); + m_blocktemplate = std::move(blocktemplate); + m_prevHash = Json::getString(params, "prev_hash"); + + if (m_state == ConnectingState) { + setState(ConnectedState); + } + + m_listener->onJobReceived(this, m_job, params); + return true; +} + + +bool xmrig::DaemonClient::parseResponse(int64_t id, const rapidjson::Value &result, const rapidjson::Value &error) +{ + if (id == -1) { + return false; + } + + if (error.IsObject()) { + const char *message = error["message"].GetString(); + + if (!handleSubmitResponse(id, message) && !isQuiet()) { + LOG_ERR("[%s:%d] error: " RED_BOLD("\"%s\"") RED_S ", code: %d", m_pool.host().data(), m_pool.port(), message, error["code"].GetInt()); + } + + return false; + } + + if (!result.IsObject()) { + return false; + } + + int code = -1; + if (result.HasMember(kBlocktemplateBlob) && parseJob(result, &code)) { + return true; + } + + if (handleSubmitResponse(id)) { + getBlockTemplate(); + return true; + } + + + return false; +} + + +int64_t xmrig::DaemonClient::getBlockTemplate() +{ + using namespace rapidjson; + Document doc(kObjectType); + auto &allocator = doc.GetAllocator(); + + Value params(kObjectType); + params.AddMember("wallet_address", m_pool.user().toJSON(), allocator); + params.AddMember("reserve_size", 8, allocator); + + JsonRequest::create(doc, m_sequence, "getblocktemplate", params); + + send(HTTP_POST, kJsonRPC, doc); + + return m_sequence++; +} + + +void xmrig::DaemonClient::retry() +{ + m_failures++; + m_listener->onClose(this, static_cast(m_failures)); + + if (m_failures == -1) { + return; + } + + if (m_state == ConnectedState) { + setState(ConnectingState); + } + + m_timer->stop(); + m_timer->start(m_retryPause, 0); +} + + +void xmrig::DaemonClient::send(int method, const char *url, const char *data, size_t size) +{ + LOG_DEBUG("[%s:%d] " MAGENTA_BOLD("\"%s %s\"") BLACK_BOLD_S " send (%zu bytes): \"%.*s\"", + m_pool.host().data(), + m_pool.port(), + http_method_str(static_cast(method)), + url, + size, + static_cast(size), + data); + + HttpClient *client; +# ifdef XMRIG_FEATURE_TLS + if (m_pool.isTLS()) { + client = new HttpsClient(method, url, this, data, size, m_pool.fingerprint()); + } + else +# endif + { + client = new HttpClient(method, url, this, data, size); + } + + client->setQuiet(isQuiet()); + client->connect(m_pool.host(), m_pool.port()); +} + + +void xmrig::DaemonClient::send(int method, const char *url, const rapidjson::Document &doc) +{ + using namespace rapidjson; + + StringBuffer buffer(nullptr, 512); + Writer writer(buffer); + doc.Accept(writer); + + send(method, url, buffer.GetString(), buffer.GetSize()); +} + + +void xmrig::DaemonClient::setState(SocketState state) +{ + assert(m_state != state); + if (m_state == state) { + return; + } + + m_state = state; + + switch (state) { + case ConnectedState: + { + m_failures = 0; + m_listener->onLoginSuccess(this); + + const uint64_t interval = std::max(20, m_pool.pollInterval()); + m_timer->start(interval, interval); + } + break; + + case UnconnectedState: + m_failures = -1; + m_timer->stop(); + break; + + default: + break; + } +} diff --git a/src/base/net/stratum/DaemonClient.h b/src/base/net/stratum/DaemonClient.h new file mode 100644 index 00000000..00b62e39 --- /dev/null +++ b/src/base/net/stratum/DaemonClient.h @@ -0,0 +1,83 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2019 Howard Chu + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_DAEMONCLIENT_H +#define XMRIG_DAEMONCLIENT_H + + +#include "base/net/stratum/BaseClient.h" +#include "base/kernel/interfaces/ITimerListener.h" +#include "base/kernel/interfaces/IHttpListener.h" + + +namespace xmrig { + + +class DaemonClient : public BaseClient, public ITimerListener, public IHttpListener +{ +public: + DaemonClient(int id, IClientListener *listener); + ~DaemonClient() override; + +protected: + bool disconnect() override; + bool isTLS() const override; + int64_t submit(const JobResult &result) override; + void connect() override; + void connect(const Pool &pool) override; + + void onHttpData(const HttpData &data) override; + void onTimer(const Timer *timer) override; + + inline bool hasExtension(Extension) const noexcept override { return false; } + inline const char *mode() const override { return "daemon"; } + inline const char *tlsFingerprint() const override { return m_tlsFingerprint; } + inline const char *tlsVersion() const override { return m_tlsVersion; } + inline void deleteLater() override { delete this; } + inline void tick(uint64_t) override {} + +private: + bool isOutdated(uint64_t height, const char *hash) const; + bool parseJob(const rapidjson::Value ¶ms, int *code); + bool parseResponse(int64_t id, const rapidjson::Value &result, const rapidjson::Value &error); + int64_t getBlockTemplate(); + void retry(); + void send(int method, const char *url, const char *data = nullptr, size_t size = 0); + void send(int method, const char *url, const rapidjson::Document &doc); + void setState(SocketState state); + + bool m_monero; + String m_blocktemplate; + String m_prevHash; + String m_tlsFingerprint; + String m_tlsVersion; + Timer *m_timer; +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_DAEMONCLIENT_H */ diff --git a/src/base/net/stratum/Job.cpp b/src/base/net/stratum/Job.cpp new file mode 100644 index 00000000..04bd82d8 --- /dev/null +++ b/src/base/net/stratum/Job.cpp @@ -0,0 +1,186 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018 Lee Clagett + * Copyright 2018 SChernykh + * Copyright 2019 Howard Chu + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include +#include + + +#include "base/net/stratum/Job.h" +#include "base/tools/Buffer.h" + + +xmrig::Job::Job() : + m_blob(), + m_seedHash() +{ +} + + +xmrig::Job::Job(bool nicehash, const Algorithm &algorithm, const String &clientId) : + m_algorithm(algorithm), + m_nicehash(nicehash), + m_clientId(clientId), + m_blob(), + m_seedHash() +{ +} + + +xmrig::Job::~Job() +{ +} + + +bool xmrig::Job::isEqual(const Job &other) const +{ + return m_id == other.m_id && m_clientId == other.m_clientId && memcmp(m_blob, other.m_blob, sizeof(m_blob)) == 0; +} + + +bool xmrig::Job::setBlob(const char *blob) +{ + if (!blob) { + return false; + } + + m_size = strlen(blob); + if (m_size % 2 != 0) { + return false; + } + + m_size /= 2; + if (m_size < 76 || m_size >= sizeof(m_blob)) { + return false; + } + + if (!Buffer::fromHex(blob, m_size * 2, m_blob)) { + return false; + } + + if (*nonce() != 0 && !m_nicehash) { + m_nicehash = true; + } + +# ifdef XMRIG_PROXY_PROJECT + memset(m_rawBlob, 0, sizeof(m_rawBlob)); + memcpy(m_rawBlob, blob, m_size * 2); +# endif + + return true; +} + + +bool xmrig::Job::setSeedHash(const char *hash) +{ + if (!hash || (strlen(hash) != sizeof(m_seedHash) * 2)) { + return false; + } + +# ifdef XMRIG_PROXY_PROJECT + m_rawSeedHash = hash; +# endif + + return Buffer::fromHex(hash, sizeof(m_seedHash) * 2, m_seedHash); +} + + +bool xmrig::Job::setTarget(const char *target) +{ + if (!target) { + return false; + } + + const size_t len = strlen(target); + + if (len <= 8) { + uint32_t tmp = 0; + char str[8]; + memcpy(str, target, len); + + if (!Buffer::fromHex(str, 8, reinterpret_cast(&tmp)) || tmp == 0) { + return false; + } + + m_target = 0xFFFFFFFFFFFFFFFFULL / (0xFFFFFFFFULL / static_cast(tmp)); + } + else if (len <= 16) { + m_target = 0; + char str[16]; + memcpy(str, target, len); + + if (!Buffer::fromHex(str, 16, reinterpret_cast(&m_target)) || m_target == 0) { + return false; + } + } + else { + return false; + } + +# ifdef XMRIG_PROXY_PROJECT + memset(m_rawTarget, 0, sizeof(m_rawTarget)); + memcpy(m_rawTarget, target, len); +# endif + + m_diff = toDiff(m_target); + return true; +} + + +void xmrig::Job::setDiff(uint64_t diff) +{ + m_diff = diff; + m_target = toDiff(diff); + +# ifdef XMRIG_PROXY_PROJECT + Buffer::toHex(reinterpret_cast(&m_target), 8, m_rawTarget); + m_rawTarget[16] = '\0'; +# endif +} + + +void xmrig::Job::copy(const Job &other) +{ + m_algorithm = other.m_algorithm; + m_nicehash = other.m_nicehash; + m_size = other.m_size; + m_clientId = other.m_clientId; + m_id = other.m_id; + m_diff = other.m_diff; + m_height = other.m_height; + m_target = other.m_target; + m_index = other.m_index; + + memcpy(m_blob, other.m_blob, sizeof(m_blob)); + memcpy(m_seedHash, other.m_seedHash, sizeof(m_seedHash)); + +# ifdef XMRIG_PROXY_PROJECT + m_rawSeedHash = other.m_rawSeedHash; + + memcpy(m_rawBlob, other.m_rawBlob, sizeof(m_rawBlob)); + memcpy(m_rawTarget, other.m_rawTarget, sizeof(m_rawTarget)); +# endif +} diff --git a/src/base/net/stratum/Job.h b/src/base/net/stratum/Job.h new file mode 100644 index 00000000..2b256a12 --- /dev/null +++ b/src/base/net/stratum/Job.h @@ -0,0 +1,121 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018 Lee Clagett + * Copyright 2018-2019 SChernykh + * Copyright 2019 Howard Chu + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_JOB_H +#define XMRIG_JOB_H + + +#include +#include + + +#include "base/tools/String.h" +#include "crypto/common/Algorithm.h" + + +namespace xmrig { + + +class Job +{ +public: + // Max blob size is 84 (75 fixed + 9 variable), aligned to 96. https://github.com/xmrig/xmrig/issues/1 Thanks fireice-uk. + // SECOR increase requirements for blob size: https://github.com/xmrig/xmrig/issues/913 + static constexpr const size_t kMaxBlobSize = 128; + + Job(); + Job(bool nicehash, const Algorithm &algorithm, const String &clientId); + ~Job(); + + bool isEqual(const Job &other) const; + bool setBlob(const char *blob); + bool setSeedHash(const char *hash); + bool setTarget(const char *target); + void setDiff(uint64_t diff); + + inline bool isNicehash() const { return m_nicehash; } + inline bool isValid() const { return m_size > 0 && m_diff > 0; } + inline bool setId(const char *id) { return m_id = id; } + inline const Algorithm &algorithm() const { return m_algorithm; } + inline const String &clientId() const { return m_clientId; } + inline const String &id() const { return m_id; } + inline const uint32_t *nonce() const { return reinterpret_cast(m_blob + 39); } + inline const uint8_t *blob() const { return m_blob; } + inline const uint8_t *seedHash() const { return m_seedHash; } + inline size_t size() const { return m_size; } + inline uint32_t *nonce() { return reinterpret_cast(m_blob + 39); } + inline uint64_t diff() const { return m_diff; } + inline uint64_t height() const { return m_height; } + inline uint64_t target() const { return m_target; } + inline uint8_t fixedByte() const { return *(m_blob + 42); } + inline uint8_t index() const { return m_index; } + inline void reset() { m_size = 0; m_diff = 0; } + inline void setAlgorithm(const char *algo) { m_algorithm = algo; } + inline void setClientId(const String &id) { m_clientId = id; } + inline void setHeight(uint64_t height) { m_height = height; } + inline void setIndex(uint8_t index) { m_index = index; } + +# 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; } +# endif + + static inline uint32_t *nonce(uint8_t *blob) { return reinterpret_cast(blob + 39); } + static inline uint64_t toDiff(uint64_t target) { return 0xFFFFFFFFFFFFFFFFULL / target; } + + 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; } + +private: + void copy(const Job &other); + + Algorithm m_algorithm; + bool m_nicehash = false; + size_t m_size = 0; + String m_clientId; + String m_id; + uint64_t m_diff = 0; + uint64_t m_height = 0; + uint64_t m_target = 0; + uint8_t m_blob[kMaxBlobSize]; + uint8_t m_index = 0; + uint8_t m_seedHash[32]; + +# ifdef XMRIG_PROXY_PROJECT + char m_rawBlob[kMaxBlobSize * 2 + 8]; + char m_rawTarget[24]; + String m_rawSeedHash; +# endif +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_JOB_H */ diff --git a/src/base/net/stratum/Pool.cpp b/src/base/net/stratum/Pool.cpp new file mode 100644 index 00000000..ba31c35d --- /dev/null +++ b/src/base/net/stratum/Pool.cpp @@ -0,0 +1,339 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2019 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2019 Howard Chu + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include +#include +#include +#include + + +#include "base/io/json/Json.h" +#include "base/net/stratum/Pool.h" +#include "rapidjson/document.h" + + +#ifdef APP_DEBUG +# include "base/io/log/Log.h" +#endif + + +#ifdef _MSC_VER +# define strncasecmp _strnicmp +#endif + + +namespace xmrig { + +static const char *kAlgo = "algo"; +static const char *kDaemon = "daemon"; +static const char *kDaemonPollInterval = "daemon-poll-interval"; +static const char *kEnabled = "enabled"; +static const char *kFingerprint = "tls-fingerprint"; +static const char *kKeepalive = "keepalive"; +static const char *kNicehash = "nicehash"; +static const char *kPass = "pass"; +static const char *kRigId = "rig-id"; +static const char *kTls = "tls"; +static const char *kUrl = "url"; +static const char *kUser = "user"; + +const String Pool::kDefaultPassword = "x"; +const String Pool::kDefaultUser = "x"; + +static const char kStratumTcp[] = "stratum+tcp://"; +static const char kStratumSsl[] = "stratum+ssl://"; + +#ifdef XMRIG_FEATURE_HTTP +static const char kDaemonHttp[] = "daemon+http://"; +static const char kDaemonHttps[] = "daemon+https://"; +#endif + +} + + +xmrig::Pool::Pool() : + m_keepAlive(0), + m_flags(0), + m_port(kDefaultPort), + m_pollInterval(kDefaultPollInterval) +{ +} + + +/** + * @brief Parse url. + * + * Valid urls: + * example.com + * example.com:3333 + * stratum+tcp://example.com + * stratum+tcp://example.com:3333 + * + * @param url + */ +xmrig::Pool::Pool(const char *url) : + m_keepAlive(0), + m_flags(1), + m_port(kDefaultPort), + m_pollInterval(kDefaultPollInterval) +{ + parse(url); +} + + +xmrig::Pool::Pool(const rapidjson::Value &object) : + m_keepAlive(0), + m_flags(1), + m_port(kDefaultPort), + m_pollInterval(kDefaultPollInterval) +{ + if (!parse(Json::getString(object, kUrl))) { + return; + } + + m_user = Json::getString(object, kUser); + m_password = Json::getString(object, kPass); + m_rigId = Json::getString(object, kRigId); + m_fingerprint = Json::getString(object, kFingerprint); + m_pollInterval = Json::getUint64(object, kDaemonPollInterval, kDefaultPollInterval); + m_algorithm = Json::getString(object, kAlgo); + + m_flags.set(FLAG_ENABLED, Json::getBool(object, kEnabled, true)); + m_flags.set(FLAG_NICEHASH, Json::getBool(object, kNicehash)); + m_flags.set(FLAG_TLS, Json::getBool(object, kTls, m_flags.test(FLAG_TLS))); + m_flags.set(FLAG_DAEMON, Json::getBool(object, kDaemon, m_flags.test(FLAG_DAEMON))); + + const rapidjson::Value &keepalive = Json::getValue(object, kKeepalive); + if (keepalive.IsInt()) { + setKeepAlive(keepalive.GetInt()); + } + else if (keepalive.IsBool()) { + setKeepAlive(keepalive.GetBool()); + } +} + + +xmrig::Pool::Pool(const char *host, uint16_t port, const char *user, const char *password, int keepAlive, bool nicehash, bool tls) : + m_keepAlive(keepAlive), + m_flags(1), + m_host(host), + m_password(password), + m_user(user), + m_port(port), + m_pollInterval(kDefaultPollInterval) +{ + const size_t size = m_host.size() + 8; + assert(size > 8); + + char *url = new char[size](); + snprintf(url, size - 1, "%s:%d", m_host.data(), m_port); + + m_url = url; + + m_flags.set(FLAG_NICEHASH, nicehash); + m_flags.set(FLAG_TLS, tls); +} + + +bool xmrig::Pool::isEnabled() const +{ +# ifndef XMRIG_FEATURE_TLS + if (isTLS()) { + return false; + } +# endif + +# ifndef XMRIG_FEATURE_HTTP + if (isDaemon()) { + return false; + } +# endif + + if (isDaemon() && !algorithm().isValid()) { + return false; + } + + return m_flags.test(FLAG_ENABLED) && isValid(); +} + + +bool xmrig::Pool::isEqual(const Pool &other) const +{ + return (m_flags == other.m_flags + && m_keepAlive == other.m_keepAlive + && m_port == other.m_port + && m_algorithm == other.m_algorithm + && m_fingerprint == other.m_fingerprint + && m_host == other.m_host + && m_password == other.m_password + && m_rigId == other.m_rigId + && m_url == other.m_url + && m_user == other.m_user + && m_pollInterval == other.m_pollInterval + ); +} + + +bool xmrig::Pool::parse(const char *url) +{ + assert(url != nullptr); + if (url == nullptr) { + return false; + } + + const char *p = strstr(url, "://"); + const char *base = url; + + if (p) { + if (strncasecmp(url, kStratumTcp, sizeof(kStratumTcp) - 1) == 0) { + m_flags.set(FLAG_DAEMON, false); + m_flags.set(FLAG_TLS, false); + } + else if (strncasecmp(url, kStratumSsl, sizeof(kStratumSsl) - 1) == 0) { + m_flags.set(FLAG_DAEMON, false); + m_flags.set(FLAG_TLS, true); + } +# ifdef XMRIG_FEATURE_HTTP + else if (strncasecmp(url, kDaemonHttps, sizeof(kDaemonHttps) - 1) == 0) { + m_flags.set(FLAG_DAEMON, true); + m_flags.set(FLAG_TLS, true); + } + else if (strncasecmp(url, kDaemonHttp, sizeof(kDaemonHttp) - 1) == 0) { + m_flags.set(FLAG_DAEMON, true); + m_flags.set(FLAG_TLS, false); + } +# endif + else { + return false; + } + + base = p + 3; + } + + if (!strlen(base) || *base == '/') { + return false; + } + + m_url = url; + if (base[0] == '[') { + return parseIPv6(base); + } + + const char *port = strchr(base, ':'); + if (!port) { + m_host = base; + return true; + } + + const size_t size = static_cast(port++ - base + 1); + char *host = new char[size](); + memcpy(host, base, size - 1); + + m_host = host; + m_port = static_cast(strtol(port, nullptr, 10)); + + return true; +} + + +rapidjson::Value xmrig::Pool::toJSON(rapidjson::Document &doc) const +{ + using namespace rapidjson; + + auto &allocator = doc.GetAllocator(); + + Value obj(kObjectType); + + obj.AddMember(StringRef(kAlgo), m_algorithm.toJSON(), allocator); + obj.AddMember(StringRef(kUrl), m_url.toJSON(), allocator); + obj.AddMember(StringRef(kUser), m_user.toJSON(), allocator); + + if (!isDaemon()) { + obj.AddMember(StringRef(kPass), m_password.toJSON(), allocator); + obj.AddMember(StringRef(kRigId), m_rigId.toJSON(), allocator); + +# ifndef XMRIG_PROXY_PROJECT + obj.AddMember(StringRef(kNicehash), isNicehash(), allocator); +# endif + + if (m_keepAlive == 0 || m_keepAlive == kKeepAliveTimeout) { + obj.AddMember(StringRef(kKeepalive), m_keepAlive > 0, allocator); + } + else { + obj.AddMember(StringRef(kKeepalive), m_keepAlive, allocator); + } + } + + obj.AddMember(StringRef(kEnabled), m_flags.test(FLAG_ENABLED), allocator); + obj.AddMember(StringRef(kTls), isTLS(), allocator); + obj.AddMember(StringRef(kFingerprint), m_fingerprint.toJSON(), allocator); + obj.AddMember(StringRef(kDaemon), m_flags.test(FLAG_DAEMON), allocator); + + if (isDaemon()) { + obj.AddMember(StringRef(kDaemonPollInterval), m_pollInterval, allocator); + } + + return obj; +} + + +#ifdef APP_DEBUG +void xmrig::Pool::print() const +{ + LOG_NOTICE("url: %s", m_url.data()); + LOG_DEBUG ("host: %s", m_host.data()); + LOG_DEBUG ("port: %d", static_cast(m_port)); + LOG_DEBUG ("user: %s", m_user.data()); + LOG_DEBUG ("pass: %s", m_password.data()); + LOG_DEBUG ("rig-id %s", m_rigId.data()); + LOG_DEBUG ("algo: %s", m_algorithm.name()); + LOG_DEBUG ("nicehash: %d", static_cast(m_flags.test(FLAG_NICEHASH))); + LOG_DEBUG ("keepAlive: %d", m_keepAlive); +} +#endif + + +bool xmrig::Pool::parseIPv6(const char *addr) +{ + const char *end = strchr(addr, ']'); + if (!end) { + return false; + } + + const char *port = strchr(end, ':'); + if (!port) { + return false; + } + + const size_t size = static_cast(end - addr); + char *host = new char[size](); + memcpy(host, addr + 1, size - 1); + + m_host = host; + m_port = static_cast(strtol(port + 1, nullptr, 10)); + + return true; +} diff --git a/src/base/net/stratum/Pool.h b/src/base/net/stratum/Pool.h new file mode 100644 index 00000000..36c3ed1b --- /dev/null +++ b/src/base/net/stratum/Pool.h @@ -0,0 +1,126 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2019 Howard Chu + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_POOL_H +#define XMRIG_POOL_H + + +#include +#include + + +#include "base/tools/String.h" +#include "crypto/common/Algorithm.h" +#include "rapidjson/fwd.h" + + +namespace xmrig { + + +class Pool +{ +public: + enum Flags { + FLAG_ENABLED, + FLAG_NICEHASH, + FLAG_TLS, + FLAG_DAEMON, + FLAG_MAX + }; + + static const String kDefaultPassword; + static const String kDefaultUser; + + constexpr static int kKeepAliveTimeout = 60; + constexpr static uint16_t kDefaultPort = 3333; + constexpr static uint64_t kDefaultPollInterval = 1000; + + Pool(); + Pool(const char *url); + Pool(const rapidjson::Value &object); + Pool(const char *host, + uint16_t port, + const char *user = nullptr, + const char *password = nullptr, + int keepAlive = 0, + bool nicehash = false, + bool tls = false + ); + + inline bool isDaemon() const { return m_flags.test(FLAG_DAEMON); } + inline bool isNicehash() const { return m_flags.test(FLAG_NICEHASH); } + inline bool isTLS() const { return m_flags.test(FLAG_TLS); } + inline bool isValid() const { return !m_host.isNull() && m_port > 0; } + inline const Algorithm &algorithm() const { return m_algorithm; } + inline const String &fingerprint() const { return m_fingerprint; } + inline const String &host() const { return m_host; } + inline const String &password() const { return !m_password.isNull() ? m_password : kDefaultPassword; } + inline const String &rigId() const { return m_rigId; } + inline const String &url() const { return m_url; } + inline const String &user() const { return !m_user.isNull() ? m_user : kDefaultUser; } + inline int keepAlive() const { return m_keepAlive; } + inline uint16_t port() const { return m_port; } + inline uint64_t pollInterval() const { return m_pollInterval; } + inline void setAlgo(const Algorithm &algorithm) { m_algorithm = algorithm; } + inline void setPassword(const String &password) { m_password = password; } + inline void setRigId(const String &rigId) { m_rigId = rigId; } + inline void setUser(const String &user) { m_user = user; } + + inline bool operator!=(const Pool &other) const { return !isEqual(other); } + inline bool operator==(const Pool &other) const { return isEqual(other); } + + bool isEnabled() const; + bool isEqual(const Pool &other) const; + bool parse(const char *url); + rapidjson::Value toJSON(rapidjson::Document &doc) const; + +# ifdef APP_DEBUG + void print() const; +# endif + +private: + inline void setKeepAlive(bool enable) { setKeepAlive(enable ? kKeepAliveTimeout : 0); } + inline void setKeepAlive(int keepAlive) { m_keepAlive = keepAlive >= 0 ? keepAlive : 0; } + + bool parseIPv6(const char *addr); + + Algorithm m_algorithm; + int m_keepAlive; + std::bitset m_flags; + String m_fingerprint; + String m_host; + String m_password; + String m_rigId; + String m_url; + String m_user; + uint16_t m_port; + uint64_t m_pollInterval; +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_POOL_H */ diff --git a/src/base/net/stratum/Pools.cpp b/src/base/net/stratum/Pools.cpp new file mode 100644 index 00000000..4641ecd4 --- /dev/null +++ b/src/base/net/stratum/Pools.cpp @@ -0,0 +1,190 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include "base/io/log/Log.h" +#include "base/kernel/interfaces/IJsonReader.h" +#include "base/net/stratum/Pools.h" +#include "base/net/stratum/strategies/FailoverStrategy.h" +#include "base/net/stratum/strategies/SinglePoolStrategy.h" +#include "donate.h" +#include "rapidjson/document.h" + + +xmrig::Pools::Pools() : + m_donateLevel(kDefaultDonateLevel), + m_retries(5), + m_retryPause(5), + m_proxyDonate(PROXY_DONATE_AUTO) +{ +# ifdef XMRIG_PROXY_PROJECT + m_retries = 2; + m_retryPause = 1; +# endif +} + + +bool xmrig::Pools::isEqual(const Pools &other) const +{ + if (m_data.size() != other.m_data.size() || m_retries != other.m_retries || m_retryPause != other.m_retryPause) { + return false; + } + + return std::equal(m_data.begin(), m_data.end(), other.m_data.begin()); +} + + +xmrig::IStrategy *xmrig::Pools::createStrategy(IStrategyListener *listener) const +{ + if (active() == 1) { + for (const Pool &pool : m_data) { + if (pool.isEnabled()) { + return new SinglePoolStrategy(pool, retryPause(), retries(), listener); + } + } + } + + FailoverStrategy *strategy = new FailoverStrategy(retryPause(), retries(), listener); + for (const Pool &pool : m_data) { + if (pool.isEnabled()) { + strategy->add(pool); + } + } + + return strategy; +} + + +rapidjson::Value xmrig::Pools::toJSON(rapidjson::Document &doc) const +{ + using namespace rapidjson; + auto &allocator = doc.GetAllocator(); + + Value pools(kArrayType); + + for (const Pool &pool : m_data) { + pools.PushBack(pool.toJSON(doc), allocator); + } + + return pools; +} + + +size_t xmrig::Pools::active() const +{ + size_t count = 0; + for (const Pool &pool : m_data) { + if (pool.isEnabled()) { + count++; + } + } + + return count; +} + + +void xmrig::Pools::load(const IJsonReader &reader) +{ + m_data.clear(); + + const rapidjson::Value &pools = reader.getArray("pools"); + if (!pools.IsArray()) { + return; + } + + for (const rapidjson::Value &value : pools.GetArray()) { + if (!value.IsObject()) { + continue; + } + + Pool pool(value); + if (pool.isValid()) { + m_data.push_back(std::move(pool)); + } + } + + setDonateLevel(reader.getInt("donate-level", kDefaultDonateLevel)); + setProxyDonate(reader.getInt("donate-over-proxy", PROXY_DONATE_AUTO)); + setRetries(reader.getInt("retries")); + setRetryPause(reader.getInt("retry-pause")); +} + + +void xmrig::Pools::print() const +{ + size_t i = 1; + for (const Pool &pool : m_data) { + Log::print(GREEN_BOLD(" * ") WHITE_BOLD("POOL #%-7zu") CSI "1;%dm%s" CLEAR " algo " WHITE_BOLD("%s"), + i, + (pool.isEnabled() ? (pool.isTLS() ? 32 : 36) : 31), + pool.url().data(), + pool.algorithm().isValid() ? pool.algorithm().shortName() : "auto" + ); + + i++; + } + +# ifdef APP_DEBUG + LOG_NOTICE("POOLS --------------------------------------------------------------------"); + for (const Pool &pool : m_data) { + pool.print(); + } + LOG_NOTICE("--------------------------------------------------------------------------"); +# endif +} + + +void xmrig::Pools::setDonateLevel(int level) +{ + if (level >= kMinimumDonateLevel && level <= 99) { + m_donateLevel = level; + } +} + + +void xmrig::Pools::setProxyDonate(int value) +{ + switch (value) { + case PROXY_DONATE_NONE: + case PROXY_DONATE_AUTO: + case PROXY_DONATE_ALWAYS: + m_proxyDonate = static_cast(value); + } +} + + +void xmrig::Pools::setRetries(int retries) +{ + if (retries > 0 && retries <= 1000) { + m_retries = retries; + } +} + + +void xmrig::Pools::setRetryPause(int retryPause) +{ + if (retryPause > 0 && retryPause <= 3600) { + m_retryPause = retryPause; + } +} diff --git a/src/base/net/stratum/Pools.h b/src/base/net/stratum/Pools.h new file mode 100644 index 00000000..2d82eaeb --- /dev/null +++ b/src/base/net/stratum/Pools.h @@ -0,0 +1,87 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_POOLS_H +#define XMRIG_POOLS_H + + +#include + + +#include "base/net/stratum/Pool.h" + + +namespace xmrig { + + +class IJsonReader; +class IStrategy; +class IStrategyListener; + + +class Pools +{ +public: + enum ProxyDonate { + PROXY_DONATE_NONE, + PROXY_DONATE_AUTO, + PROXY_DONATE_ALWAYS + }; + + Pools(); + + inline const std::vector &data() const { return m_data; } + inline int donateLevel() const { return m_donateLevel; } + inline int retries() const { return m_retries; } + inline int retryPause() const { return m_retryPause; } + inline ProxyDonate proxyDonate() const { return PROXY_DONATE_NONE; } + + inline bool operator!=(const Pools &other) const { return !isEqual(other); } + inline bool operator==(const Pools &other) const { return isEqual(other); } + + bool isEqual(const Pools &other) const; + IStrategy *createStrategy(IStrategyListener *listener) const; + rapidjson::Value toJSON(rapidjson::Document &doc) const; + size_t active() const; + void load(const IJsonReader &reader); + void print() const; + +private: + void setDonateLevel(int level); + void setProxyDonate(int value); + void setRetries(int retries); + void setRetryPause(int retryPause); + + int m_donateLevel; + int m_retries; + int m_retryPause; + ProxyDonate m_proxyDonate; + std::vector m_data; +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_POOLS_H */ diff --git a/src/base/net/stratum/SubmitResult.h b/src/base/net/stratum/SubmitResult.h new file mode 100644 index 00000000..5abd3e4b --- /dev/null +++ b/src/base/net/stratum/SubmitResult.h @@ -0,0 +1,72 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_SUBMITRESULT_H +#define XMRIG_SUBMITRESULT_H + + +#include "base/tools/Chrono.h" + + +namespace xmrig { + + +class SubmitResult +{ +public: + inline SubmitResult() : + reqId(0), + seq(0), + actualDiff(0), + diff(0), + elapsed(0), + m_start(0) + {} + + inline SubmitResult(int64_t seq, uint64_t diff, uint64_t actualDiff, int64_t reqId = 0) : + reqId(reqId), + seq(seq), + actualDiff(actualDiff), + diff(diff), + elapsed(0), + m_start(Chrono::steadyMSecs()) + {} + + inline void done() { elapsed = Chrono::steadyMSecs() - m_start; } + + int64_t reqId; + int64_t seq; + uint64_t actualDiff; + uint64_t diff; + uint64_t elapsed; + +private: + uint64_t m_start; +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_SUBMITRESULT_H */ diff --git a/src/base/net/stratum/Tls.cpp b/src/base/net/stratum/Tls.cpp new file mode 100644 index 00000000..8916579b --- /dev/null +++ b/src/base/net/stratum/Tls.cpp @@ -0,0 +1,192 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018 Lee Clagett + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include + + +#include "base/io/log/Log.h" +#include "base/net/stratum/Client.h" +#include "base/net/stratum/Tls.h" +#include "base/tools/Buffer.h" + + +#ifdef _MSC_VER +# define strncasecmp(x,y,z) _strnicmp(x,y,z) +#endif + + +xmrig::Client::Tls::Tls(Client *client) : + m_ready(false), + m_buf(), + m_fingerprint(), + m_client(client), + m_ssl(nullptr) +{ + m_ctx = SSL_CTX_new(SSLv23_method()); + assert(m_ctx != nullptr); + + if (!m_ctx) { + return; + } + + m_writeBio = BIO_new(BIO_s_mem()); + m_readBio = BIO_new(BIO_s_mem()); + SSL_CTX_set_options(m_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); +} + + +xmrig::Client::Tls::~Tls() +{ + if (m_ctx) { + SSL_CTX_free(m_ctx); + } + + if (m_ssl) { + SSL_free(m_ssl); + } +} + + +bool xmrig::Client::Tls::handshake() +{ + m_ssl = SSL_new(m_ctx); + assert(m_ssl != nullptr); + + if (!m_ssl) { + return false; + } + + SSL_set_connect_state(m_ssl); + SSL_set_bio(m_ssl, m_readBio, m_writeBio); + SSL_do_handshake(m_ssl); + + return send(); +} + + +bool xmrig::Client::Tls::send(const char *data, size_t size) +{ + SSL_write(m_ssl, data, size); + + return send(); +} + + +const char *xmrig::Client::Tls::fingerprint() const +{ + return m_ready ? m_fingerprint : nullptr; +} + + +const char *xmrig::Client::Tls::version() const +{ + return m_ready ? SSL_get_version(m_ssl) : nullptr; +} + + +void xmrig::Client::Tls::read(const char *data, size_t size) +{ + BIO_write(m_readBio, data, size); + + if (!SSL_is_init_finished(m_ssl)) { + const int rc = SSL_connect(m_ssl); + + if (rc < 0 && SSL_get_error(m_ssl, rc) == SSL_ERROR_WANT_READ) { + send(); + } else if (rc == 1) { + X509 *cert = SSL_get_peer_certificate(m_ssl); + if (!verify(cert)) { + X509_free(cert); + m_client->close(); + + return; + } + + X509_free(cert); + m_ready = true; + m_client->login(); + } + + return; + } + + int bytes_read = 0; + while ((bytes_read = SSL_read(m_ssl, m_buf, sizeof(m_buf))) > 0) { + m_buf[bytes_read - 1] = '\0'; + m_client->parse(m_buf, static_cast(bytes_read)); + } +} + + +bool xmrig::Client::Tls::send() +{ + return m_client->send(m_writeBio); +} + + +bool xmrig::Client::Tls::verify(X509 *cert) +{ + if (cert == nullptr) { + LOG_ERR("[%s] Failed to get server certificate", m_client->url()); + + return false; + } + + if (!verifyFingerprint(cert)) { + LOG_ERR("[%s] Failed to verify server certificate fingerprint", m_client->url()); + + const char *fingerprint = m_client->m_pool.fingerprint(); + if (strlen(m_fingerprint) == 64 && fingerprint != nullptr) { + LOG_ERR("\"%s\" was given", m_fingerprint); + LOG_ERR("\"%s\" was configured", fingerprint); + } + + return false; + } + + return true; +} + + +bool xmrig::Client::Tls::verifyFingerprint(X509 *cert) +{ + const EVP_MD *digest = EVP_get_digestbyname("sha256"); + if (digest == nullptr) { + return false; + } + + unsigned char md[EVP_MAX_MD_SIZE]; + unsigned int dlen; + + if (X509_digest(cert, digest, md, &dlen) != 1) { + return false; + } + + Buffer::toHex(md, 32, m_fingerprint); + const char *fingerprint = m_client->m_pool.fingerprint(); + + return fingerprint == nullptr || strncasecmp(m_fingerprint, fingerprint, 64) == 0; +} diff --git a/src/api/ApiState.h b/src/base/net/stratum/Tls.h similarity index 51% rename from src/api/ApiState.h rename to src/base/net/stratum/Tls.h index 7ecca36d..e070be52 100644 --- a/src/api/ApiState.h +++ b/src/base/net/stratum/Tls.h @@ -4,8 +4,9 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , * * 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 @@ -21,43 +22,48 @@ * along with this program. If not, see . */ -#ifndef __APISTATE_H__ -#define __APISTATE_H__ +#ifndef XMRIG_CLIENT_TLS_H +#define XMRIG_CLIENT_TLS_H -#include "api/NetworkState.h" -#include "rapidjson/fwd.h" +#include -class Hashrate; +#include "base/net/stratum/Client.h" -class ApiState +namespace xmrig { + + +class Client::Tls { public: - ApiState(); - ~ApiState(); + Tls(Client *client); + ~Tls(); - char *get(const char *url, int *status) const; - void tick(const Hashrate *hashrate); - void tick(const NetworkState &results); + bool handshake(); + bool send(const char *data, size_t size); + const char *fingerprint() const; + const char *version() const; + void read(const char *data, size_t size); private: - char *finalize(rapidjson::Document &doc) const; - void genId(); - void getConnection(rapidjson::Document &doc) const; - void getHashrate(rapidjson::Document &doc) const; - void getIdentify(rapidjson::Document &doc) const; - void getMiner(rapidjson::Document &doc) const; - void getResults(rapidjson::Document &doc) const; + bool send(); + bool verify(X509 *cert); + bool verifyFingerprint(X509 *cert); - char m_id[17]; - char m_workerId[128]; - double *m_hashrate; - double m_highestHashrate; - double m_totalHashrate[3]; - int m_threads; - NetworkState m_network; + BIO *m_readBio; + BIO *m_writeBio; + bool m_ready; + char m_buf[1024 * 2]; + char m_fingerprint[32 * 2 + 8]; + Client *m_client; + SSL *m_ssl; + SSL_CTX *m_ctx; }; -#endif /* __APISTATE_H__ */ + +} /* namespace xmrig */ + + +#endif /* XMRIG_CLIENT_TLS_H */ diff --git a/src/base/net/stratum/strategies/FailoverStrategy.cpp b/src/base/net/stratum/strategies/FailoverStrategy.cpp new file mode 100644 index 00000000..48be2ba3 --- /dev/null +++ b/src/base/net/stratum/strategies/FailoverStrategy.cpp @@ -0,0 +1,210 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include "base/kernel/interfaces/IStrategyListener.h" +#include "base/kernel/Platform.h" +#include "base/net/stratum/Client.h" +#include "base/net/stratum/strategies/FailoverStrategy.h" + + +#ifdef XMRIG_FEATURE_HTTP +# include "base/net/stratum/DaemonClient.h" +#endif + + +xmrig::FailoverStrategy::FailoverStrategy(const std::vector &pools, int retryPause, int retries, IStrategyListener *listener, bool quiet) : + m_quiet(quiet), + m_retries(retries), + m_retryPause(retryPause), + m_active(-1), + m_listener(listener), + m_index(0) +{ + for (const Pool &pool : pools) { + add(pool); + } +} + + +xmrig::FailoverStrategy::FailoverStrategy(int retryPause, int retries, IStrategyListener *listener, bool quiet) : + m_quiet(quiet), + m_retries(retries), + m_retryPause(retryPause), + m_active(-1), + m_listener(listener), + m_index(0) +{ +} + + +xmrig::FailoverStrategy::~FailoverStrategy() +{ + for (IClient *client : m_pools) { + client->deleteLater(); + } +} + + +void xmrig::FailoverStrategy::add(const Pool &pool) +{ + const int id = static_cast(m_pools.size()); + +# ifdef XMRIG_FEATURE_HTTP + IClient *client = !pool.isDaemon() ? static_cast(new Client(id, Platform::userAgent(), this)) + : static_cast(new DaemonClient(id, this)); +# else + IClient *client = new Client(id, Platform::userAgent(), this); +# endif + + client->setPool(pool); + client->setRetries(m_retries); + client->setRetryPause(m_retryPause * 1000); + client->setQuiet(m_quiet); + + m_pools.push_back(client); +} + + +int64_t xmrig::FailoverStrategy::submit(const JobResult &result) +{ + if (!isActive()) { + return -1; + } + + return active()->submit(result); +} + + +void xmrig::FailoverStrategy::connect() +{ + m_pools[m_index]->connect(); +} + + +void xmrig::FailoverStrategy::resume() +{ + if (!isActive()) { + return; + } + + m_listener->onJob(this, active(), active()->job()); +} + + +void xmrig::FailoverStrategy::setAlgo(const Algorithm &algo) +{ + for (IClient *client : m_pools) { + client->setAlgo(algo); + } +} + + +void xmrig::FailoverStrategy::stop() +{ + for (size_t i = 0; i < m_pools.size(); ++i) { + m_pools[i]->disconnect(); + } + + m_index = 0; + m_active = -1; + + m_listener->onPause(this); +} + + +void xmrig::FailoverStrategy::tick(uint64_t now) +{ + for (IClient *client : m_pools) { + client->tick(now); + } +} + + +void xmrig::FailoverStrategy::onClose(IClient *client, int failures) +{ + if (failures == -1) { + return; + } + + if (m_active == client->id()) { + m_active = -1; + m_listener->onPause(this); + } + + if (m_index == 0 && failures < m_retries) { + return; + } + + if (m_index == static_cast(client->id()) && (m_pools.size() - m_index) > 1) { + m_pools[++m_index]->connect(); + } +} + + +void xmrig::FailoverStrategy::onLogin(IClient *client, rapidjson::Document &doc, rapidjson::Value ¶ms) +{ + m_listener->onLogin(this, client, doc, params); +} + + +void xmrig::FailoverStrategy::onJobReceived(IClient *client, const Job &job, const rapidjson::Value &) +{ + if (m_active == client->id()) { + m_listener->onJob(this, client, job); + } +} + + +void xmrig::FailoverStrategy::onLoginSuccess(IClient *client) +{ + int active = m_active; + + if (client->id() == 0 || !isActive()) { + active = client->id(); + } + + for (size_t i = 1; i < m_pools.size(); ++i) { + if (active != static_cast(i)) { + m_pools[i]->disconnect(); + } + } + + if (active >= 0 && active != m_active) { + m_index = m_active = active; + m_listener->onActive(this, client); + } +} + + +void xmrig::FailoverStrategy::onResultAccepted(IClient *client, const SubmitResult &result, const char *error) +{ + m_listener->onResultAccepted(this, client, result, error); +} + + +void xmrig::FailoverStrategy::onVerifyAlgorithm(const IClient *client, const Algorithm &algorithm, bool *ok) +{ + m_listener->onVerifyAlgorithm(this, client, algorithm, ok); +} diff --git a/src/base/net/stratum/strategies/FailoverStrategy.h b/src/base/net/stratum/strategies/FailoverStrategy.h new file mode 100644 index 00000000..283d4916 --- /dev/null +++ b/src/base/net/stratum/strategies/FailoverStrategy.h @@ -0,0 +1,86 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_FAILOVERSTRATEGY_H +#define XMRIG_FAILOVERSTRATEGY_H + + +#include + + +#include "base/kernel/interfaces/IClientListener.h" +#include "base/kernel/interfaces/IStrategy.h" +#include "base/net/stratum/Pool.h" + + +namespace xmrig { + + +class Client; +class IStrategyListener; + + +class FailoverStrategy : public IStrategy, public IClientListener +{ +public: + FailoverStrategy(const std::vector &pool, int retryPause, int retries, IStrategyListener *listener, bool quiet = false); + FailoverStrategy(int retryPause, int retries, IStrategyListener *listener, bool quiet = false); + ~FailoverStrategy() override; + + void add(const Pool &pool); + +protected: + inline bool isActive() const override { return m_active >= 0; } + inline IClient *client() const override { return isActive() ? active() : m_pools[m_index]; } + + int64_t submit(const JobResult &result) override; + void connect() override; + void resume() override; + void setAlgo(const Algorithm &algo) override; + void stop() override; + void tick(uint64_t now) override; + + void onClose(IClient *client, int failures) override; + void onJobReceived(IClient *client, const Job &job, const rapidjson::Value ¶ms) override; + void onLogin(IClient *client, rapidjson::Document &doc, rapidjson::Value ¶ms) override; + void onLoginSuccess(IClient *client) override; + void onResultAccepted(IClient *client, const SubmitResult &result, const char *error) override; + void onVerifyAlgorithm(const IClient *client, const Algorithm &algorithm, bool *ok) override; + +private: + inline IClient *active() const { return m_pools[static_cast(m_active)]; } + + const bool m_quiet; + const int m_retries; + const int m_retryPause; + int m_active; + IStrategyListener *m_listener; + size_t m_index; + std::vector m_pools; +}; + + +} /* namespace xmrig */ + +#endif /* XMRIG_FAILOVERSTRATEGY_H */ diff --git a/src/base/net/stratum/strategies/SinglePoolStrategy.cpp b/src/base/net/stratum/strategies/SinglePoolStrategy.cpp new file mode 100644 index 00000000..c923e1c2 --- /dev/null +++ b/src/base/net/stratum/strategies/SinglePoolStrategy.cpp @@ -0,0 +1,144 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include "base/kernel/interfaces/IStrategyListener.h" +#include "base/kernel/Platform.h" +#include "base/net/stratum/Client.h" +#include "base/net/stratum/strategies/SinglePoolStrategy.h" + + +#ifdef XMRIG_FEATURE_HTTP +# include "base/net/stratum/DaemonClient.h" +#endif + + +xmrig::SinglePoolStrategy::SinglePoolStrategy(const Pool &pool, int retryPause, int retries, IStrategyListener *listener, bool quiet) : + m_active(false), + m_listener(listener) +{ +# ifdef XMRIG_FEATURE_HTTP + if (!pool.isDaemon()) { + m_client = new Client(0, Platform::userAgent(), this); + } + else { + m_client = new DaemonClient(0, this); + } +# else + m_client = new Client(0, Platform::userAgent(), this); +# endif + + m_client->setPool(pool); + m_client->setRetries(retries); + m_client->setRetryPause(retryPause * 1000); + m_client->setQuiet(quiet); +} + + +xmrig::SinglePoolStrategy::~SinglePoolStrategy() +{ + m_client->deleteLater(); +} + + +int64_t xmrig::SinglePoolStrategy::submit(const JobResult &result) +{ + return m_client->submit(result); +} + + +void xmrig::SinglePoolStrategy::connect() +{ + m_client->connect(); +} + + +void xmrig::SinglePoolStrategy::resume() +{ + if (!isActive()) { + return; + } + + m_listener->onJob(this, m_client, m_client->job()); +} + + +void xmrig::SinglePoolStrategy::setAlgo(const Algorithm &algo) +{ + m_client->setAlgo(algo); +} + + +void xmrig::SinglePoolStrategy::stop() +{ + m_client->disconnect(); +} + + +void xmrig::SinglePoolStrategy::tick(uint64_t now) +{ + m_client->tick(now); +} + + +void xmrig::SinglePoolStrategy::onClose(IClient *, int) +{ + if (!isActive()) { + return; + } + + m_active = false; + m_listener->onPause(this); +} + + +void xmrig::SinglePoolStrategy::onJobReceived(IClient *client, const Job &job, const rapidjson::Value &) +{ + m_listener->onJob(this, client, job); +} + + +void xmrig::SinglePoolStrategy::onLogin(IClient *client, rapidjson::Document &doc, rapidjson::Value ¶ms) +{ + m_listener->onLogin(this, client, doc, params); +} + + +void xmrig::SinglePoolStrategy::onLoginSuccess(IClient *client) +{ + m_active = true; + m_listener->onActive(this, client); +} + + +void xmrig::SinglePoolStrategy::onResultAccepted(IClient *client, const SubmitResult &result, const char *error) +{ + m_listener->onResultAccepted(this, client, result, error); +} + + +void xmrig::SinglePoolStrategy::onVerifyAlgorithm(const IClient *client, const Algorithm &algorithm, bool *ok) +{ + m_listener->onVerifyAlgorithm(this, client, algorithm, ok); +} diff --git a/src/base/net/stratum/strategies/SinglePoolStrategy.h b/src/base/net/stratum/strategies/SinglePoolStrategy.h new file mode 100644 index 00000000..ea808193 --- /dev/null +++ b/src/base/net/stratum/strategies/SinglePoolStrategy.h @@ -0,0 +1,75 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_SINGLEPOOLSTRATEGY_H +#define XMRIG_SINGLEPOOLSTRATEGY_H + + +#include "base/kernel/interfaces/IClientListener.h" +#include "base/kernel/interfaces/IStrategy.h" + + +namespace xmrig { + + +class Client; +class IStrategyListener; +class Pool; + + +class SinglePoolStrategy : public IStrategy, public IClientListener +{ +public: + SinglePoolStrategy(const Pool &pool, int retryPause, int retries, IStrategyListener *listener, bool quiet = false); + ~SinglePoolStrategy() override; + +protected: + inline bool isActive() const override { return m_active; } + inline IClient *client() const override { return m_client; } + + int64_t submit(const JobResult &result) override; + void connect() override; + void resume() override; + void setAlgo(const Algorithm &algo) override; + void stop() override; + void tick(uint64_t now) override; + + void onClose(IClient *client, int failures) override; + void onJobReceived(IClient *client, const Job &job, const rapidjson::Value ¶ms) override; + void onLogin(IClient *client, rapidjson::Document &doc, rapidjson::Value ¶ms) override; + void onLoginSuccess(IClient *client) override; + void onResultAccepted(IClient *client, const SubmitResult &result, const char *error) override; + void onVerifyAlgorithm(const IClient *client, const Algorithm &algorithm, bool *ok) override; + +private: + bool m_active; + IClient *m_client; + IStrategyListener *m_listener; +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_SINGLEPOOLSTRATEGY_H */ diff --git a/src/base/net/tools/RecvBuf.h b/src/base/net/tools/RecvBuf.h new file mode 100644 index 00000000..5122c980 --- /dev/null +++ b/src/base/net/tools/RecvBuf.h @@ -0,0 +1,99 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_RECVBUF_H +#define XMRIG_RECVBUF_H + + +#include + + +#include "base/kernel/interfaces/ILineListener.h" + + +namespace xmrig { + + +template +class RecvBuf +{ +public: + inline RecvBuf() : + m_buf(), + m_pos(0) + { + } + + inline char *base() { return m_buf; } + inline char *current() { return m_buf + m_pos; } + inline const char *base() const { return m_buf; } + inline const char *current() const { return m_buf + m_pos; } + inline size_t available() const { return N - m_pos; } + inline size_t pos() const { return m_pos; } + inline void nread(size_t size) { m_pos += size; } + inline void reset() { m_pos = 0; } + + constexpr inline size_t size() const { return N; } + + inline void getline(ILineListener *listener) + { + char *end; + char *start = m_buf; + size_t remaining = m_pos; + + while ((end = static_cast(memchr(start, '\n', remaining))) != nullptr) { + *end = '\0'; + + end++; + const size_t len = static_cast(end - start); + + listener->onLine(start, len - 1); + + remaining -= len; + start = end; + } + + if (remaining == 0) { + m_pos = 0; + return; + } + + if (start == m_buf) { + return; + } + + memcpy(m_buf, start, remaining); + m_pos = remaining; + } + +private: + char m_buf[N]; + size_t m_pos; +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_RECVBUF_H */ diff --git a/src/base/net/tools/Storage.h b/src/base/net/tools/Storage.h new file mode 100644 index 00000000..aff62481 --- /dev/null +++ b/src/base/net/tools/Storage.h @@ -0,0 +1,97 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_STORAGE_H +#define XMRIG_STORAGE_H + + +#include +#include + + +namespace xmrig { + + +template +class Storage +{ +public: + inline Storage() : + m_counter(0) + { + } + + + inline uintptr_t add(TYPE *ptr) + { + m_data[m_counter] = ptr; + + return m_counter++; + } + + + inline static void *ptr(uintptr_t id) { return reinterpret_cast(id); } + + + inline TYPE *get(const void *id) const { return get(reinterpret_cast(id)); } + inline TYPE *get(uintptr_t id) const + { + assert(m_data.count(id) > 0); + if (m_data.count(id) == 0) { + return nullptr; + } + + return m_data.at(id); + } + + + inline void remove(const void *id) { delete release(reinterpret_cast(id)); } + inline void remove(uintptr_t id) { delete release(id); } + + + inline TYPE *release(const void *id) { release(reinterpret_cast(id)); } + inline TYPE *release(uintptr_t id) + { + TYPE *obj = get(id); + if (obj != nullptr) { + auto it = m_data.find(id); + if (it != m_data.end()) { + m_data.erase(it); + } + } + + return obj; + } + + +private: + std::map m_data; + uint64_t m_counter; +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_STORAGE_H */ diff --git a/src/base/net/tools/TcpServer.cpp b/src/base/net/tools/TcpServer.cpp new file mode 100644 index 00000000..e5e6e4e1 --- /dev/null +++ b/src/base/net/tools/TcpServer.cpp @@ -0,0 +1,102 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include "base/kernel/interfaces/ITcpServerListener.h" +#include "base/net/tools/TcpServer.h" +#include "base/tools/Handle.h" +#include "base/tools/String.h" + + +static const xmrig::String kLocalHost("127.0.0.1"); + + +xmrig::TcpServer::TcpServer(const String &host, uint16_t port, ITcpServerListener *listener) : + m_host(host.isNull() ? kLocalHost : host), + m_version(0), + m_listener(listener), + m_addr(), + m_port(port) +{ + m_tcp = new uv_tcp_t; + uv_tcp_init(uv_default_loop(), m_tcp); + m_tcp->data = this; + + uv_tcp_nodelay(m_tcp, 1); + + if (m_host.contains(":") && uv_ip6_addr(m_host.data(), m_port, reinterpret_cast(&m_addr)) == 0) { + m_version = 6; + } + else if (uv_ip4_addr(m_host.data(), m_port, reinterpret_cast(&m_addr)) == 0) { + m_version = 4; + } +} + + +xmrig::TcpServer::~TcpServer() +{ + Handle::close(m_tcp); +} + + +int xmrig::TcpServer::bind() +{ + if (!m_version) { + return UV_EAI_ADDRFAMILY; + } + + uv_tcp_bind(m_tcp, reinterpret_cast(&m_addr), 0); + + const int rc = uv_listen(reinterpret_cast(m_tcp), 511, TcpServer::onConnection); + if (rc != 0) { + return rc; + } + + if (!m_port) { + sockaddr_storage storage = {}; + int size = sizeof(storage); + + uv_tcp_getsockname(m_tcp, reinterpret_cast(&storage), &size); + + m_port = ntohs(reinterpret_cast(&storage)->sin_port); + } + + return m_port; +} + + +void xmrig::TcpServer::create(uv_stream_t *stream, int status) +{ + if (status < 0) { + return; + } + + m_listener->onConnection(stream, m_port); +} + + +void xmrig::TcpServer::onConnection(uv_stream_t *stream, int status) +{ + static_cast(stream->data)->create(stream, status); +} diff --git a/src/base/net/tools/TcpServer.h b/src/base/net/tools/TcpServer.h new file mode 100644 index 00000000..fc29fa2d --- /dev/null +++ b/src/base/net/tools/TcpServer.h @@ -0,0 +1,64 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_TCPSERVER_H +#define XMRIG_TCPSERVER_H + + +#include + + +namespace xmrig { + + +class ITcpServerListener; +class String; + + +class TcpServer +{ +public: + TcpServer(const String &host, uint16_t port, ITcpServerListener *listener); + ~TcpServer(); + + int bind(); + +private: + void create(uv_stream_t *stream, int status); + + static void onConnection(uv_stream_t *stream, int status); + + const String &m_host; + int m_version; + ITcpServerListener *m_listener; + sockaddr_storage m_addr; + uint16_t m_port; + uv_tcp_t *m_tcp; +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_TCPSERVER_H */ diff --git a/src/base/tools/Arguments.cpp b/src/base/tools/Arguments.cpp new file mode 100644 index 00000000..3713dd11 --- /dev/null +++ b/src/base/tools/Arguments.cpp @@ -0,0 +1,76 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include +#include + + +#include "base/tools/Arguments.h" + + +xmrig::Arguments::Arguments(int argc, char **argv) : + m_argv(argv), + m_argc(argc) +{ + uv_setup_args(argc, argv); + + for (size_t i = 0; i < static_cast(argc); ++i) { + add(argv[i]); + } +} + + +bool xmrig::Arguments::hasArg(const char *name) const +{ + if (m_argc == 1) { + return false; + } + + return std::find(m_data.begin() + 1, m_data.end(), name) != m_data.end(); +} + + +void xmrig::Arguments::add(const char *arg) +{ + if (arg == nullptr) { + return; + } + + const size_t size = strlen(arg); + if (size > 4 && arg[0] == '-' && arg[1] == '-') { + const char *p = strstr(arg, "="); + + if (p) { + const size_t keySize = static_cast(p - arg); + + m_data.push_back(String(arg, keySize)); + m_data.push_back(arg + keySize + 1); + + return; + } + } + + m_data.push_back(arg); +} diff --git a/src/base/tools/Arguments.h b/src/base/tools/Arguments.h new file mode 100644 index 00000000..e47e39e3 --- /dev/null +++ b/src/base/tools/Arguments.h @@ -0,0 +1,61 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_ARGUMENTS_H +#define XMRIG_ARGUMENTS_H + + +#include + + +#include "base/tools/String.h" + + +namespace xmrig { + + +class Arguments +{ +public: + Arguments(int argc, char **argv); + + bool hasArg(const char *name) const; + + inline char **argv() const { return m_argv; } + inline const std::vector &data() const { return m_data; } + inline int argc() const { return m_argc; } + +private: + void add(const char *arg); + + char **m_argv; + int m_argc; + std::vector m_data; +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_ARGUMENTS_H */ diff --git a/src/log/SysLog.h b/src/base/tools/Baton.h similarity index 68% rename from src/log/SysLog.h rename to src/base/tools/Baton.h index 38de1a6a..646dc0b8 100644 --- a/src/log/SysLog.h +++ b/src/base/tools/Baton.h @@ -4,8 +4,9 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , * * 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 @@ -21,20 +22,24 @@ * along with this program. If not, see . */ -#ifndef __SYSLOG_H__ -#define __SYSLOG_H__ +#ifndef XMRIG_BATON_H +#define XMRIG_BATON_H -#include "interfaces/ILogBackend.h" +namespace xmrig { -class SysLog : public ILogBackend +template +class Baton { public: - SysLog(); + inline Baton() { req.data = this; } - void message(int level, const char *fmt, va_list args) override; - void text(const char *fmt, va_list args) override; + REQ req; }; -#endif /* __SYSLOG_BACKEND_H__ */ + +} /* namespace xmrig */ + + +#endif /* XMRIG_BATON_H */ diff --git a/src/base/tools/Buffer.cpp b/src/base/tools/Buffer.cpp new file mode 100644 index 00000000..05a68cb4 --- /dev/null +++ b/src/base/tools/Buffer.cpp @@ -0,0 +1,201 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include "base/tools/Buffer.h" + + +static inline uint8_t hf_hex2bin(uint8_t c, bool &err) +{ + if (c >= '0' && c <= '9') { + return c - '0'; + } + else if (c >= 'a' && c <= 'f') { + return c - 'a' + 0xA; + } + else if (c >= 'A' && c <= 'F') { + return c - 'A' + 0xA; + } + + err = true; + return 0; +} + + +static inline uint8_t hf_bin2hex(uint8_t c) +{ + if (c <= 0x9) { + return '0' + c; + } + + return 'a' - 0xA + c; +} + + +xmrig::Buffer::Buffer() : + m_data(nullptr), + m_size(0) +{ +} + + +xmrig::Buffer::Buffer(Buffer &&other) : + m_data(other.m_data), + m_size(other.m_size) +{ + other.m_data = nullptr; + other.m_size = 0; +} + + +xmrig::Buffer::Buffer(const Buffer &other) +{ + copy(other.data(), other.size()); +} + + +xmrig::Buffer::Buffer(const char *data, size_t size) +{ + copy(data, size); +} + + +xmrig::Buffer::Buffer(size_t size) : + m_size(size) +{ + m_data = new char[size](); +} + + +xmrig::Buffer::~Buffer() +{ + delete [] m_data; +} + + +void xmrig::Buffer::from(const char *data, size_t size) +{ + if (m_size > 0) { + if (m_size == size) { + memcpy(m_data, data, m_size); + + return; + } + + delete [] m_data; + } + + copy(data, size); +} + + +xmrig::Buffer xmrig::Buffer::allocUnsafe(size_t size) +{ + Buffer buf; + buf.m_size = size; + buf.m_data = new char[size]; + + return buf; +} + + +bool xmrig::Buffer::fromHex(const uint8_t *in, size_t size, uint8_t *out) +{ + bool error = false; + for (size_t i = 0; i < size; i += 2) { + out[i / 2] = static_cast((hf_hex2bin(in[i], error) << 4) | hf_hex2bin(in[i + 1], error)); + + if (error) { + return false; + } + } + + return true; +} + + +xmrig::Buffer xmrig::Buffer::fromHex(const char *data, size_t size) +{ + if (data == nullptr || size % 2 != 0) { + return Buffer(); + } + + Buffer buf(size / 2); + fromHex(data, size, buf.data()); + + return buf; +} + + +void xmrig::Buffer::toHex(const uint8_t *in, size_t size, uint8_t *out) +{ + for (size_t i = 0; i < size; i++) { + out[i * 2] = hf_bin2hex((in[i] & 0xF0) >> 4); + out[i * 2 + 1] = hf_bin2hex(in[i] & 0x0F); + } +} + + +xmrig::String xmrig::Buffer::toHex(const uint8_t *in, size_t size) +{ + return Buffer(reinterpret_cast(in), size).toHex(); +} + + +xmrig::String xmrig::Buffer::toHex() const +{ + if (m_size == 0) { + return String(); + } + + char *buf = new char[m_size * 2 + 1]; + buf[m_size * 2] = '\0'; + + toHex(m_data, m_size, buf); + + return String(buf); +} + + +void xmrig::Buffer::copy(const char *data, size_t size) +{ + m_data = new char[size]; + m_size = size; + + memcpy(m_data, data, m_size); +} + + +void xmrig::Buffer::move(Buffer &&other) +{ + if (m_size > 0) { + delete [] m_data; + } + + m_data = other.m_data; + m_size = other.m_size; + + other.m_data = nullptr; + other.m_size = 0; +} diff --git a/src/base/tools/Buffer.h b/src/base/tools/Buffer.h new file mode 100644 index 00000000..28f92b9e --- /dev/null +++ b/src/base/tools/Buffer.h @@ -0,0 +1,91 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_BUFFER_H +#define XMRIG_BUFFER_H + + +#include "base/tools/String.h" + + +namespace xmrig { + + +class Buffer +{ +public: + Buffer(); + Buffer(Buffer &&other); + Buffer(const Buffer &other); + Buffer(const char *data, size_t size); + Buffer(size_t size); + ~Buffer(); + + + inline bool isEqual(const Buffer &other) const { return m_size == other.m_size && (m_size == 0 || memcmp(m_data, other.m_data, m_size) == 0); } + inline char *data() { return m_data; } + inline const char *data() const { return m_data; } + inline size_t size() const { return m_size; } + inline void from(const Buffer &other) { from(other.data(), other.size()); } + + + void from(const char *data, size_t size); + + + inline bool operator!=(const Buffer &other) const { return !isEqual(other); } + inline bool operator==(const Buffer &other) const { return isEqual(other); } + inline Buffer &operator=(Buffer &&other) { move(std::move(other)); return *this; } + inline Buffer &operator=(const Buffer &other) { from(other); return *this; } + + + static Buffer allocUnsafe(size_t size); + static inline Buffer alloc(size_t size) { return Buffer(size); } + + + inline static bool fromHex(const char *in, size_t size, char *out) { return fromHex(reinterpret_cast(in), size, reinterpret_cast(out)); } + inline static bool fromHex(const char *in, size_t size, uint8_t *out) { return fromHex(reinterpret_cast(in), size, out); } + inline static Buffer fromHex(const char *data) { return fromHex(data, strlen(data)); } + inline static Buffer fromHex(const String &str) { return fromHex(str.data(), str.size()); } + inline static void toHex(const char *in, size_t size, char *out) { return toHex(reinterpret_cast(in), size, reinterpret_cast(out)); } + inline static void toHex(const uint8_t *in, size_t size, char *out) { return toHex(in, size, reinterpret_cast(out)); } + + static bool fromHex(const uint8_t *in, size_t size, uint8_t *out); + static Buffer fromHex(const char *data, size_t size); + static String toHex(const uint8_t *in, size_t size); + static void toHex(const uint8_t *in, size_t size, uint8_t *out); + String toHex() const; + +private: + void copy(const char *data, size_t size); + void move(Buffer &&other); + + char *m_data; + size_t m_size; +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_BUFFER_H */ diff --git a/src/base/tools/Chrono.h b/src/base/tools/Chrono.h new file mode 100644 index 00000000..e464f361 --- /dev/null +++ b/src/base/tools/Chrono.h @@ -0,0 +1,68 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_CHRONO_H +#define XMRIG_CHRONO_H + + +#include + + +namespace xmrig { + + +class Chrono +{ +public: + static inline uint64_t highResolutionMSecs() + { + using namespace std::chrono; + + return static_cast(time_point_cast(high_resolution_clock::now()).time_since_epoch().count()); + } + + + static inline uint64_t steadyMSecs() + { + using namespace std::chrono; + if (high_resolution_clock::is_steady) { + return static_cast(time_point_cast(high_resolution_clock::now()).time_since_epoch().count()); + } + + return static_cast(time_point_cast(steady_clock::now()).time_since_epoch().count()); + } + + + static inline uint64_t currentMSecsSinceEpoch() + { + using namespace std::chrono; + + return static_cast(time_point_cast(system_clock::now()).time_since_epoch().count()); + } +}; + + +} /* namespace xmrig */ + +#endif /* XMRIG_CHRONO_H */ diff --git a/src/base/tools/Handle.h b/src/base/tools/Handle.h new file mode 100644 index 00000000..1a7d08f1 --- /dev/null +++ b/src/base/tools/Handle.h @@ -0,0 +1,92 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_HANDLE_H +#define XMRIG_HANDLE_H + + +#include + + +namespace xmrig { + + +class Handle +{ +public: + template + static inline void close(T handle) + { + if (handle) { + deleteLater(handle); + } + } + + + template + static inline void deleteLater(T handle) + { + if (uv_is_closing(reinterpret_cast(handle))) { + return; + } + + uv_close(reinterpret_cast(handle), [](uv_handle_t *handle) { delete handle; }); + } +}; + + +template<> +inline void Handle::close(uv_timer_t *handle) +{ + if (handle) { + uv_timer_stop(handle); + deleteLater(handle); + } +} + + +template<> +inline void Handle::close(uv_signal_t *handle) +{ + if (handle) { + uv_signal_stop(handle); + deleteLater(handle); + } +} + + +template<> +inline void Handle::close(uv_fs_event_t *handle) +{ + if (handle) { + uv_fs_event_stop(handle); + deleteLater(handle); + } +} + + +} /* namespace xmrig */ + + +#endif /* XMRIG_HANDLE_H */ diff --git a/src/base/tools/String.cpp b/src/base/tools/String.cpp new file mode 100644 index 00000000..7778c6da --- /dev/null +++ b/src/base/tools/String.cpp @@ -0,0 +1,239 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include + + +#include "base/tools/String.h" +#include "rapidjson/document.h" + + +xmrig::String::String(const char *str) : + m_data(nullptr), + m_size(str == nullptr ? 0 : strlen(str)) +{ + if (m_size == 0) { + return; + } + + m_data = new char[m_size + 1]; + memcpy(m_data, str, m_size + 1); +} + + +xmrig::String::String(const char *str, size_t size) : + m_data(nullptr), + m_size(size) +{ + if (str == nullptr) { + m_size = 0; + + return; + } + + m_data = new char[m_size + 1]; + memcpy(m_data, str, m_size); + m_data[m_size] = '\0'; +} + + +xmrig::String::String(const String &other) : + m_data(nullptr), + m_size(other.m_size) +{ + if (other.m_data == nullptr) { + return; + } + + m_data = new char[m_size + 1]; + memcpy(m_data, other.m_data, m_size + 1); +} + + +bool xmrig::String::isEqual(const char *str) const +{ + return (m_data != nullptr && str != nullptr && strcmp(m_data, str) == 0) || (m_data == nullptr && str == nullptr); +} + + +bool xmrig::String::isEqual(const String &other) const +{ + if (m_size != other.m_size) { + return false; + } + + return (m_data != nullptr && other.m_data != nullptr && memcmp(m_data, other.m_data, m_size) == 0) || (m_data == nullptr && other.m_data == nullptr); +} + + +rapidjson::Value xmrig::String::toJSON() const +{ + using namespace rapidjson; + + return isNull() ? Value(kNullType) : Value(StringRef(m_data)); +} + + +rapidjson::Value xmrig::String::toJSON(rapidjson::Document &doc) const +{ + using namespace rapidjson; + + return isNull() ? Value(kNullType) : Value(m_data, doc.GetAllocator()); +} + + +std::vector xmrig::String::split(char sep) const +{ + std::vector out; + if (m_size == 0) { + return out; + } + + size_t start = 0; + size_t pos = 0; + + for (pos = 0; pos < m_size; ++pos) { + if (m_data[pos] == sep) { + if ((pos - start) > 0) { + out.push_back(String(m_data + start, pos - start)); + } + + start = pos + 1; + } + } + + if ((pos - start) > 0) { + out.push_back(String(m_data + start, pos - start)); + } + + return out; +} + + +xmrig::String &xmrig::String::toLower() +{ + if (isNull() || isEmpty()) { + return *this; + } + + for (size_t i = 0; i < size(); ++i) { + m_data[i] = static_cast(tolower(m_data[i])); + } + + return *this; +} + + +xmrig::String xmrig::String::join(const std::vector &vec, char sep) +{ + if (vec.empty()) { + return String(); + } + + size_t size = vec.size(); + for (const String &str : vec) { + size += str.size(); + } + + size_t offset = 0; + char *buf = new char[size]; + + for (const String &str : vec) { + memcpy(buf + offset, str.data(), str.size()); + + offset += str.size() + 1; + + if (offset < size) { + buf[offset - 1] = sep; + } + } + + buf[size - 1] = '\0'; + + return String(buf); +} + + +void xmrig::String::copy(const char *str) +{ + delete [] m_data; + + if (str == nullptr) { + m_size = 0; + m_data = nullptr; + + return; + } + + m_size = strlen(str); + m_data = new char[m_size + 1]; + + memcpy(m_data, str, m_size + 1); +} + + +void xmrig::String::copy(const String &other) +{ + if (m_size > 0 && m_size == other.m_size) { + memcpy(m_data, other.m_data, m_size + 1); + + return; + } + + delete [] m_data; + + if (other.m_data == nullptr) { + m_size = 0; + m_data = nullptr; + + return; + } + + m_size = other.m_size; + m_data = new char[m_size + 1]; + + memcpy(m_data, other.m_data, m_size + 1); +} + + +void xmrig::String::move(char *str) +{ + delete [] m_data; + + m_size = str == nullptr ? 0 : strlen(str); + m_data = str; +} + + +void xmrig::String::move(String &&other) +{ + delete [] m_data; + + m_data = other.m_data; + m_size = other.m_size; + + other.m_data = nullptr; + other.m_size = 0; +} diff --git a/src/base/tools/String.h b/src/base/tools/String.h new file mode 100644 index 00000000..2c47d850 --- /dev/null +++ b/src/base/tools/String.h @@ -0,0 +1,107 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_STRING_H +#define XMRIG_STRING_H + + +#include +#include + + +#include "rapidjson/fwd.h" + + +namespace xmrig { + + +/** + * @brief Simple C string wrapper. + * + * 1. I know about std:string. + * 2. For some reason I prefer don't use std:string in miner, eg because of file size of MSYS2 builds. + * 3. nullptr and JSON conversion supported. + */ +class String +{ +public: + inline String() : m_data(nullptr), m_size(0) {} + inline String(char *str) : m_data(str), m_size(str == nullptr ? 0 : strlen(str)) {} + inline String(String &&other) : m_data(other.m_data), m_size(other.m_size) { other.m_data = nullptr; other.m_size = 0; } + + String(const char *str); + String(const char *str, size_t size); + String(const String &other); + + inline ~String() { delete [] m_data; } + + + bool isEqual(const char *str) const; + bool isEqual(const String &other) const; + + + inline bool contains(const char *str) const { return isNull() ? false : strstr(m_data, str) != nullptr; } + + + inline bool isEmpty() const { return size() == 0; } + inline bool isNull() const { return m_data == nullptr; } + inline char *data() { return m_data; } + inline const char *data() const { return m_data; } + inline size_t size() const { return m_size; } + + + inline bool operator!=(const char *str) const { return !isEqual(str); } + inline bool operator!=(const String &other) const { return !isEqual(other); } + inline bool operator<(const String &str) const { return strcmp(data(), str.data()) < 0; } + inline bool operator==(const char *str) const { return isEqual(str); } + inline bool operator==(const String &other) const { return isEqual(other); } + inline operator const char*() const { return m_data; } + inline String &operator=(char *str) { move(str); return *this; } + inline String &operator=(const char *str) { copy(str); return *this; } + inline String &operator=(const String &str) { copy(str); return *this; } + inline String &operator=(std::nullptr_t) { delete [] m_data; m_data = nullptr; m_size = 0; return *this; } + inline String &operator=(String &&other) { move(std::move(other)); return *this; } + + rapidjson::Value toJSON() const; + rapidjson::Value toJSON(rapidjson::Document &doc) const; + std::vector split(char sep) const; + String &toLower(); + + static String join(const std::vector &vec, char sep); + +private: + void copy(const char *str); + void copy(const String &other); + void move(char *str); + void move(String &&other); + + char *m_data; + size_t m_size; +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_STRING_H */ diff --git a/src/base/tools/Timer.cpp b/src/base/tools/Timer.cpp new file mode 100644 index 00000000..d06df163 --- /dev/null +++ b/src/base/tools/Timer.cpp @@ -0,0 +1,91 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include "base/kernel/interfaces/ITimerListener.h" +#include "base/tools/Handle.h" +#include "base/tools/Timer.h" + + +xmrig::Timer::Timer(ITimerListener *listener) : + m_listener(listener), + m_timer(nullptr) +{ + init(); +} + + +xmrig::Timer::Timer(ITimerListener *listener, uint64_t timeout, uint64_t repeat) : + m_listener(listener), + m_timer(nullptr) +{ + init(); + start(timeout, repeat); +} + + +xmrig::Timer::~Timer() +{ + Handle::close(m_timer); +} + + +uint64_t xmrig::Timer::repeat() const +{ + return uv_timer_get_repeat(m_timer); +} + + +void xmrig::Timer::setRepeat(uint64_t repeat) +{ + uv_timer_set_repeat(m_timer, repeat); +} + + +void xmrig::Timer::start(uint64_t timeout, uint64_t repeat) +{ + uv_timer_start(m_timer, onTimer, timeout, repeat); +} + + +void xmrig::Timer::stop() +{ + uv_timer_stop(m_timer); +} + + +void xmrig::Timer::init() +{ + m_timer = new uv_timer_t; + m_timer->data = this; + uv_timer_init(uv_default_loop(), m_timer); +} + + +void xmrig::Timer::onTimer(uv_timer_t *handle) +{ + const Timer *timer = static_cast(handle->data); + + timer->m_listener->onTimer(timer); +} diff --git a/src/workers/Handle.h b/src/base/tools/Timer.h similarity index 55% rename from src/workers/Handle.h rename to src/base/tools/Timer.h index 21506faf..e0e210f5 100644 --- a/src/workers/Handle.h +++ b/src/base/tools/Timer.h @@ -4,8 +4,9 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , * * 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 @@ -21,39 +22,45 @@ * along with this program. If not, see . */ -#ifndef __HANDLE_H__ -#define __HANDLE_H__ +#ifndef XMRIG_TIMER_H +#define XMRIG_TIMER_H #include -#include -class IWorker; +typedef struct uv_timer_s uv_timer_t; -class Handle +namespace xmrig { + + +class ITimerListener; + + +class Timer { public: - Handle(size_t threadId, size_t threads, int64_t affinity, int priority); - void join(); - void start(void (*callback) (void *)); + Timer(ITimerListener *listener); + Timer(ITimerListener *listener, uint64_t timeout, uint64_t repeat); + ~Timer(); - inline int priority() const { return m_priority; } - inline size_t threadId() const { return m_threadId; } - inline size_t threads() const { return m_threads; } - inline int64_t affinity() const { return m_affinity; } - inline IWorker *worker() const { return m_worker; } - inline void setWorker(IWorker *worker) { m_worker = worker; } + uint64_t repeat() const; + void setRepeat(uint64_t repeat); + void start(uint64_t timeout, uint64_t repeat); + void stop(); private: - int m_priority; - size_t m_threadId; - size_t m_threads; - int64_t m_affinity; - IWorker *m_worker; - uv_thread_t m_thread; + void init(); + + static void onTimer(uv_timer_t *handle); + + ITimerListener *m_listener; + uv_timer_t *m_timer; }; -#endif /* __HANDLE_H__ */ +} /* namespace xmrig */ + + +#endif /* XMRIG_TIMER_H */ diff --git a/src/cc/CCClient.cpp b/src/cc/CCClient.cpp index d4b74392..4a17b4af 100644 --- a/src/cc/CCClient.cpp +++ b/src/cc/CCClient.cpp @@ -1,6 +1,5 @@ /* XMRigCC - * Copyright 2017- BenDr0id - * + * Copyright 2017- BenDr0id , * * 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 @@ -21,14 +20,21 @@ #include #include <3rdparty/rapidjson/stringbuffer.h> #include <3rdparty/rapidjson/prettywriter.h> +#include -#include "version.h" -#include "api/NetworkState.h" +#include "backend/cpu/Cpu.h" +#include "base/tools/Timer.h" +#include "base/tools/Chrono.h" +#include "base/kernel/Base.h" +#include "base/kernel/Platform.h" + +#include "base/cc/interfaces/IClientStatusListener.h" +#include "base/cc/interfaces/ICommandListener.h" #include "CCClient.h" #include "App.h" -#include "Cpu.h" #include "ControlCommand.h" +#include "version.h" #ifdef TYPE_AMD_GPU #include "common/log/Log.h" @@ -36,16 +42,11 @@ #include "common/Platform.h" #include "core/Config.h" #else -#include "Mem.h" -#include "log/Log.h" -#include "log/RemoteLog.h" -#include "Platform.h" -#include "api/NetworkState.h" +#include "core/config/Config.h" +#include "base/io/log/Log.h" +#include "base/io/log/backends/RemoteLog.h" #endif -#include "workers/Workers.h" -#include "workers/Hashrate.h" - #if _WIN32 # include "winsock2.h" #else @@ -55,24 +56,60 @@ #endif -CCClient* CCClient::m_self = nullptr; -uv_mutex_t CCClient::m_mutex; - #ifdef TYPE_AMD_GPU -CCClient::CCClient(xmrig::Config* config, uv_async_t* async) +xmrig::CCClient::CCClient(xmrig::Config* config, uv_async_t* async) #else -CCClient::CCClient(Options* config, uv_async_t* async) +xmrig::CCClient::CCClient(Base *base) #endif - : m_config(config), - m_async(async) + : m_base(base), + m_startTime(Chrono::currentMSecsSinceEpoch()), + m_configPublishedOnStart(false), + m_timer(nullptr) { - uv_mutex_init(&m_mutex); + base->addListener(this); - m_self = this; + m_timer = new Timer(this); +} + + +xmrig::CCClient::~CCClient() +{ + delete m_timer; +} + +void xmrig::CCClient::start() +{ + LOG_DEBUG("CCClient::start"); + + Log::print(GREEN_BOLD(" * ") WHITE_BOLD("%-13s") CSI "1;%dm%s", + "CC Server", + (m_base->config()->ccClient().useTLS() ? 32 : 36), + m_base->config()->ccClient().url() + ); + + updateAuthorization(); + updateClientInfo(); + + m_timer->start(static_cast(m_base->config()->ccClient().updateInterval()*1000), + static_cast(m_base->config()->ccClient().updateInterval()*1000)); +} + +void xmrig::CCClient::updateAuthorization() +{ + LOG_DEBUG("CCClient::updateAuthorization"); + + if (m_base->config()->ccClient().token() != nullptr) { + m_authorization = std::string("Bearer ") + m_base->config()->ccClient().token(); + } +} + +void xmrig::CCClient::updateClientInfo() +{ + LOG_DEBUG("CCClient::updateClientInfo"); std::string clientId; - if (config->ccWorkerId()) { - clientId =m_self->m_config->ccWorkerId(); + if (m_base->config()->ccClient().workerId()) { + clientId = m_base->config()->ccClient().workerId(); } else { char hostname[128]; memset(hostname, 0, sizeof(hostname)); @@ -80,90 +117,70 @@ CCClient::CCClient(Options* config, uv_async_t* async) clientId = std::string(hostname); } + auto cpuInfo = xmrig::Cpu::info(); + m_clientStatus.setClientId(clientId); m_clientStatus.setVersion(Version::string()); - m_clientStatus.setCpuBrand(Cpu::brand()); - m_clientStatus.setCpuAES(Cpu::hasAES()); - m_clientStatus.setCpuSockets(static_cast(Cpu::sockets())); - m_clientStatus.setCpuCores(static_cast(Cpu::cores())); - m_clientStatus.setCpuThreads(static_cast(Cpu::threads())); - m_clientStatus.setCpuX64(Cpu::isX64()); - m_clientStatus.setCpuL2(static_cast(Cpu::l2())); - m_clientStatus.setCpuL3(static_cast(Cpu::l3())); + m_clientStatus.setCpuBrand(cpuInfo->brand()); + m_clientStatus.setCpuAES(cpuInfo->hasAES()); + m_clientStatus.setCpuSockets(static_cast(cpuInfo->packages())); + m_clientStatus.setCpuCores(static_cast(cpuInfo->cores())); + m_clientStatus.setCpuThreads(static_cast(cpuInfo->threads())); + m_clientStatus.setCpuX64(cpuInfo->isX64()); + m_clientStatus.setCpuL2(static_cast(cpuInfo->L2()/1024)); + m_clientStatus.setCpuL3(static_cast(cpuInfo->L3()/1024)); + m_clientStatus.setNodes(static_cast(cpuInfo->nodes())); + +# ifdef XMRIG_FEATURE_ASM + const Assembly assembly = Cpu::assembly(cpuInfo->assembly()); + m_clientStatus.setAssembly(assembly.toString()); +# else + m_clientStatus.setAssembly("none"); +# endif + #ifdef TYPE_AMD_GPU m_clientStatus.setCurrentThreads(static_cast(config->threads().size())); m_clientStatus.setCurrentAlgoName(config->algorithm().name()); -#else - m_clientStatus.setCurrentThreads(static_cast(config->threads())); - m_clientStatus.setCurrentAlgoName(config->algoName()); #endif - - m_startTime = std::chrono::system_clock::now(); - - if (config->ccToken() != nullptr) { - m_authorization = std::string("Bearer ") + m_self->m_config->ccToken(); - } - - uv_thread_create(&m_thread, CCClient::onThreadStarted, this); } -CCClient::~CCClient() + +void xmrig::CCClient::stop() { - uv_timer_stop(&m_timer); - m_self = nullptr; -} + LOG_DEBUG("CCClient::stop"); -void CCClient::updateHashrate(const Hashrate* hashrate) -{ - if (m_self) { - uv_mutex_lock(&m_mutex); + m_configPublishedOnStart = false; - m_self->m_clientStatus.setHashrateShort(hashrate->calc(Hashrate::ShortInterval)); - m_self->m_clientStatus.setHashrateMedium(hashrate->calc(Hashrate::MediumInterval)); - m_self->m_clientStatus.setHashrateLong(hashrate->calc(Hashrate::LargeInterval)); - m_self->m_clientStatus.setHashrateHighest(hashrate->highest()); - - uv_mutex_unlock(&m_mutex); + if (m_timer) { + m_timer->stop(); } } - -void CCClient::updateNetworkState(const NetworkState& network) +void xmrig::CCClient::updateStatistics() { - if (m_self) { - uv_mutex_lock(&m_mutex); + LOG_DEBUG("CCClient::updateStatistics"); - m_self->m_clientStatus.setCurrentStatus(Workers::isEnabled() ? ClientStatus::RUNNING : ClientStatus::PAUSED); - m_self->m_clientStatus.setCurrentPool(network.pool); - m_self->m_clientStatus.setSharesGood(network.accepted); - m_self->m_clientStatus.setSharesTotal(network.accepted + network.rejected); - m_self->m_clientStatus.setHashesTotal(network.total); - m_self->m_clientStatus.setAvgTime(network.avgTime()); + for (IClientStatusListener *listener : m_ClientStatislisteners) { + listener->onUpdateRequest(m_clientStatus); + } #ifdef TYPE_AMD_GPU - m_self->m_clientStatus.setHashFactor(0); - m_self->m_clientStatus.setHugepagesEnabled(false); - m_self->m_clientStatus.setHugepages(false); - m_self->m_clientStatus.setTotalPages(0); - m_self->m_clientStatus.setTotalHugepages(0); - m_self->m_clientStatus.setCurrentPowVariantName(xmrig::Algorithm::getVariantName(network.powVariant)); -#else - m_self->m_clientStatus.setHashFactor(Mem::hashFactor()); - m_self->m_clientStatus.setHugepagesEnabled(Mem::isHugepagesEnabled()); - m_self->m_clientStatus.setHugepages(Mem::isHugepagesAvailable()); - m_self->m_clientStatus.setTotalPages(Mem::getTotalPages()); - m_self->m_clientStatus.setTotalHugepages(Mem::getTotalHugepages()); - m_self->m_clientStatus.setCurrentPowVariantName(getPowVariantName(network.powVariant)); + m_self->m_clientStatus.setHashFactor(0); + m_self->m_clientStatus.setHugepagesEnabled(false); + m_self->m_clientStatus.setHugepages(false); + m_self->m_clientStatus.setTotalPages(0); + m_self->m_clientStatus.setTotalHugepages(0); + m_self->m_clientStatus.setCurrentPowVariantName(xmrig::Algorithm::getVariantName(network.powVariant)); #endif - uv_mutex_unlock(&m_mutex); - } } #ifdef TYPE_AMD_GPU void CCClient::updateGpuInfo(const std::vector& gpuContext) { + LOG_DEBUG("CCClient::updateGpuInfo"); + if (m_self) { uv_mutex_lock(&m_mutex); @@ -189,67 +206,68 @@ void CCClient::updateGpuInfo(const std::vector& gpuContext) } #endif -void CCClient::publishClientStatusReport() +void xmrig::CCClient::publishClientStatusReport() { - std::string requestUrl = "/client/setClientStatus?clientId=" + m_self->m_clientStatus.getClientId(); - std::string requestBuffer = m_self->m_clientStatus.toJsonString(); + LOG_DEBUG("CCClient::publishClientStatusReport"); + + std::string requestUrl = "/client/setClientStatus?clientId=" + m_clientStatus.getClientId(); + std::string requestBuffer = m_clientStatus.toJsonString(); auto res = performRequest(requestUrl, requestBuffer, "POST"); if (!res) { LOG_ERR("[CC-Client] error: unable to performRequest POST -> http://%s:%d%s", - m_self->m_config->ccHost(), m_self->m_config->ccPort(), requestUrl.c_str()); + m_base->config()->ccClient().host(), m_base->config()->ccClient().port(), requestUrl.c_str()); } else if (res->status != 200) { - LOG_ERR("[CC-Client] error: \"%d\" -> http://%s:%d%s", res->status,m_self->m_config->ccHost(), - m_self->m_config->ccPort(), requestUrl.c_str()); + LOG_ERR("[CC-Client] error: \"%d\" -> http://%s:%d%s", res->status, m_base->config()->ccClient().host(), + m_base->config()->ccClient().port(), requestUrl.c_str()); } else { ControlCommand controlCommand; if (controlCommand.parseFromJsonString(res->body)) { if (controlCommand.getCommand() == ControlCommand::START) { - if (!Workers::isEnabled()) { - LOG_WARN("[CC-Client] Command: START received -> resume"); - } + LOG_DEBUG("[CC-Client] Command: START received -> resume"); } else if (controlCommand.getCommand() == ControlCommand::STOP) { - if (Workers::isEnabled()) { - LOG_WARN("[CC-Client] Command: STOP received -> pause"); - } + LOG_DEBUG("[CC-Client] Command: STOP received -> pause"); } else if (controlCommand.getCommand() == ControlCommand::UPDATE_CONFIG) { LOG_WARN("[CC-Client] Command: UPDATE_CONFIG received -> update config"); - updateConfig(); + fetchConfig(); } else if (controlCommand.getCommand() == ControlCommand::PUBLISH_CONFIG) { LOG_WARN("[CC-Client] Command: PUBLISH_CONFIG received -> publish config"); publishConfig(); }else if (controlCommand.getCommand() == ControlCommand::RESTART) { LOG_WARN("[CC-Client] Command: RESTART received -> trigger restart"); - } else if (controlCommand.getCommand() == ControlCommand::SHUTDOWN) { + } else if (controlCommand.getCommand() == ControlCommand::SHUTDOWN) { LOG_WARN("[CC-Client] Command: SHUTDOWN received -> quit"); } else if (controlCommand.getCommand() == ControlCommand::REBOOT) { LOG_WARN("[CC-Client] Command: REBOOT received -> trigger reboot"); } - m_self->m_async->data = reinterpret_cast(controlCommand.getCommand()); - uv_async_send(m_self->m_async); + for (ICommandListener *listener : m_Commandlisteners) { + listener->onCommandReceived(controlCommand); + } } else { LOG_ERR("[CC-Client] Unknown command received from CC Server."); } } } -void CCClient::updateConfig() +void xmrig::CCClient::fetchConfig() { - std::string requestUrl = "/client/getConfig?clientId=" + m_self->m_clientStatus.getClientId(); + LOG_DEBUG("CCClient::fetchConfig"); + + std::string requestUrl = "/client/getConfig?clientId=" + m_clientStatus.getClientId(); std::string requestBuffer; auto res = performRequest(requestUrl, requestBuffer, "GET"); if (!res) { LOG_ERR("[CC-Client] error: unable to performRequest GET -> http://%s:%d%s", - m_self->m_config->ccHost(), m_self->m_config->ccPort(), requestUrl.c_str()); + m_base->config()->ccClient().host(), m_base->config()->ccClient().port(), requestUrl.c_str()); } else if (res->status != 200) { - LOG_ERR("[CC-Client] error: \"%d\" -> http://%s:%d%s", res->status, m_self->m_config->ccHost(), - m_self->m_config->ccPort(), requestUrl.c_str()); + LOG_ERR("[CC-Client] error: \"%d\" -> http://%s:%d%s", res->status, m_base->config()->ccClient().host(), + m_base->config()->ccClient().port(), requestUrl.c_str()); } else { rapidjson::Document document; if (!document.Parse(res->body.c_str()).HasParseError()) { - std::ofstream clientConfigFile(m_self->m_config->fileName()); + std::ofstream clientConfigFile(m_base->config()->fileName()); if (clientConfigFile) { rapidjson::StringBuffer buffer(0, 65536); rapidjson::PrettyWriter writer(buffer); @@ -259,9 +277,13 @@ void CCClient::updateConfig() clientConfigFile << buffer.GetString(); clientConfigFile.close(); - LOG_WARN("[CC-Client] Config updated. -> trigger restart"); + if (!m_base->config()->isWatch()) { + dynamic_cast(m_base)->onFileChanged(m_base->config()->fileName()); + } + + LOG_WARN("[CC-Client] Config updated. -> reload"); } else { - LOG_ERR("[CC-Client] Not able to store client config to file %s.", m_self->m_config->fileName()); + LOG_ERR("[CC-Client] Not able to store client config to file %s.", m_base->config()->fileName()); } } else { LOG_ERR("[CC-Client] Not able to store client config. received client config is broken!"); @@ -269,12 +291,14 @@ void CCClient::updateConfig() } } -void CCClient::publishConfig() +void xmrig::CCClient::publishConfig() { - std::string requestUrl = "/client/setClientConfig?clientId=" + m_self->m_clientStatus.getClientId(); + LOG_DEBUG("CCClient::publishConfig"); + + std::string requestUrl = "/client/setClientConfig?clientId=" + m_clientStatus.getClientId(); std::stringstream data; - std::ifstream clientConfig(m_self->m_config->fileName()); + std::ifstream clientConfig(m_base->config()->fileName()); if (clientConfig) { data << clientConfig.rdbuf(); @@ -294,32 +318,36 @@ void CCClient::publishConfig() auto res = performRequest(requestUrl, buffer.GetString(), "POST"); if (!res) { LOG_ERR("[CC-Client] error: unable to performRequest POST -> http://%s:%d%s", - m_self->m_config->ccHost(), m_self->m_config->ccPort(), requestUrl.c_str()); + m_base->config()->ccClient().host(), m_base->config()->ccClient().port(), requestUrl.c_str()); } else if (res->status != 200) { - LOG_ERR("[CC-Client] error: \"%d\" -> http://%s:%d%s", res->status, m_self->m_config->ccHost(), - m_self->m_config->ccPort(), requestUrl.c_str()); + LOG_ERR("[CC-Client] error: \"%d\" -> http://%s:%d%s", res->status, m_base->config()->ccClient().host(), + m_base->config()->ccClient().port(), requestUrl.c_str()); } } else { - LOG_ERR("[CC-Client] Not able to send config. Client config %s is broken!", m_self->m_config->fileName()); + LOG_ERR("[CC-Client] Not able to send config. Client config %s is broken!", m_base->config()->fileName()); } } else { - LOG_ERR("[CC-Client] Not able to load client config %s. Please make sure it exists! Using embedded config.", m_self->m_config->fileName()); + LOG_ERR("[CC-Client] Not able to load client config %s. Please make sure it exists! Using embedded config.", m_base->config()->fileName()); } } -std::shared_ptr CCClient::performRequest(const std::string& requestUrl, - const std::string& requestBuffer, - const std::string& operation) +std::shared_ptr xmrig::CCClient::performRequest(const std::string& requestUrl, + const std::string& requestBuffer, + const std::string& operation) { + LOG_DEBUG("CCClient::performRequest"); + std::shared_ptr cli; -# ifndef XMRIG_NO_TLS - if (m_self->m_config->ccUseTls()) { - cli = std::make_shared(m_self->m_config->ccHost(), m_self->m_config->ccPort(), 10); +# ifdef XMRIG_FEATURE_TLS + if (m_base->config()->ccClient().useTLS()) { + cli = std::make_shared(m_base->config()->ccClient().host(), + m_base->config()->ccClient().port(), 10); } else { # endif - cli = std::make_shared(m_self->m_config->ccHost(), m_self->m_config->ccPort(), 10); -# ifndef XMRIG_NO_TLS + cli = std::make_shared(m_base->config()->ccClient().host(), + m_base->config()->ccClient().port(), 10); +# ifdef XMRIG_FEATURE_TLS } # endif @@ -327,13 +355,13 @@ std::shared_ptr CCClient::performRequest(const std::string& r req.method = operation; req.path = requestUrl; req.set_header("Host", ""); - req.set_header("Accept", "*/*"); + req.set_header("Accept", "*//*"); req.set_header("User-Agent", Platform::userAgent()); req.set_header("Accept", "application/json"); req.set_header("Content-Type", "application/json"); - if (!m_self->m_authorization.empty()) { - req.set_header("Authorization", m_self->m_authorization.c_str()); + if (!m_authorization.empty()) { + req.set_header("Authorization", m_authorization.c_str()); } if (!requestBuffer.empty()) { @@ -345,43 +373,49 @@ std::shared_ptr CCClient::performRequest(const std::string& r return cli->send(req, *res) ? res : nullptr; } -void CCClient::refreshUptime() +void xmrig::CCClient::updateUptime() { - std::chrono::time_point now = std::chrono::system_clock::now(); - auto uptime = std::chrono::duration_cast(now - m_self->m_startTime); - - m_self->m_clientStatus.setUptime(static_cast(uptime.count())); + LOG_DEBUG("CCClient::updateUptime"); + m_clientStatus.setUptime(Chrono::currentMSecsSinceEpoch()-m_startTime); } -void CCClient::refreshLog() +void xmrig::CCClient::updateLog() { - m_self->m_clientStatus.setLog(RemoteLog::getRows()); + LOG_DEBUG("CCClient::updateLog"); + m_clientStatus.setLog(RemoteLog::getRows()); } -void CCClient::onThreadStarted(void* handle) +void xmrig::CCClient::onConfigChanged(Config *config, Config *previousConfig) { - if (m_self) { - uv_loop_init(&m_self->m_client_loop); + LOG_DEBUG("CCClient::onConfigChanged"); + if (config->ccClient() != previousConfig->ccClient()) { + stop(); - uv_timer_init(&m_self->m_client_loop, &m_self->m_timer); - uv_timer_start(&m_self->m_timer, CCClient::onReport, - static_cast(m_self->m_config->ccUpdateInterval() * 1000), - static_cast(m_self->m_config->ccUpdateInterval() * 1000)); - - if (m_self->m_config->ccUploadConfigOnStartup()) { - m_self->publishConfig(); + if (config->ccClient().enabled() && config->ccClient().host() && config->ccClient().port() > 0) { + start(); } - - uv_run(&m_self->m_client_loop, UV_RUN_DEFAULT); } } -void CCClient::onReport(uv_timer_t* handle) +void xmrig::CCClient::onTimer(const xmrig::Timer *timer) { - if (m_self) { - m_self->refreshUptime(); - m_self->refreshLog(); - - m_self->publishClientStatusReport(); - } + LOG_DEBUG("CCClient::onTimer"); + std::thread(CCClient::publishThread, this).detach(); } + +void xmrig::CCClient::publishThread(CCClient* handle) +{ + LOG_DEBUG("CCClient::publishThread"); + if (handle) { + if (!handle->m_configPublishedOnStart && handle->m_base->config()->ccClient().uploadConfigOnStartup()) { + handle->m_configPublishedOnStart = true; + handle->publishConfig(); + } + + handle->updateUptime(); + handle->updateLog(); + handle->updateStatistics(); + + handle->publishClientStatusReport(); + } +} \ No newline at end of file diff --git a/src/cc/CCClient.h b/src/cc/CCClient.h index 289bdf2e..08dafadc 100644 --- a/src/cc/CCClient.h +++ b/src/cc/CCClient.h @@ -1,6 +1,5 @@ /* XMRigCC - * Copyright 2017- BenDr0id - * + * Copyright 2017- BenDr0id , * * 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,12 +18,11 @@ #ifndef __CC_CLIENT_H__ #define __CC_CLIENT_H__ -#ifndef XMRIG_NO_CC - #include #include #include #include <3rdparty/cpp-httplib/httplib.h> + #include "ClientStatus.h" #include "version.h" @@ -32,61 +30,72 @@ #include "amd/GpuContext.h" #include "core/Controller.h" #else -#include "Options.h" +#include "base/kernel/interfaces/IBaseListener.h" +#include "base/kernel/interfaces/ITimerListener.h" #endif +namespace xmrig +{ + class Hashrate; class NetworkState; +class Base; +class IClientStatusListener; +class ICommandListener; -class CCClient +class CCClient : public IBaseListener, public ITimerListener { public: #ifdef TYPE_AMD_GPU CCClient(xmrig::Config* m_config, uv_async_t* async); static void updateGpuInfo(const std::vector& network); #else - CCClient(Options* config, uv_async_t* async); + CCClient(Base *base); #endif ~CCClient(); - static void updateHashrate(const Hashrate *hashrate); - static void updateNetworkState(const NetworkState &results); + inline void addClientStatusListener(IClientStatusListener *listener) { m_ClientStatislisteners.push_back(listener); } + inline void addCommandListener(ICommandListener *listener) { m_Commandlisteners.push_back(listener); } + + void start(); + void stop(); + +protected: + void onConfigChanged(Config *config, Config *previousConfig) override; + void onTimer(const Timer *timer) override; private: + static void publishThread(CCClient* handle); void publishClientStatusReport(); - void updateConfig(); + void updateAuthorization(); + void updateClientInfo(); + void updateUptime(); + void updateLog(); + void updateStatistics(); + void fetchConfig(); void publishConfig(); - void refreshUptime(); - void refreshLog(); - - std::shared_ptr performRequest(const std::string& requestUrl, - const std::string& requestBuffer, - const std::string& operation); - static void onThreadStarted(void *handle); - static void onReport(uv_timer_t *handle); + std::shared_ptr performRequest(const std::string &requestUrl, + const std::string &requestBuffer, + const std::string &operation); +private: #ifdef TYPE_AMD_GPU const xmrig::Config* m_config; #else - const Options* m_config; + Base *m_base; #endif - - static CCClient* m_self; - static uv_mutex_t m_mutex; - + const uint64_t m_startTime; ClientStatus m_clientStatus; std::string m_authorization; + bool m_configPublishedOnStart; - std::chrono::time_point m_startTime; - - uv_async_t* m_async; - uv_timer_t m_timer; - uv_loop_t m_client_loop; - uv_thread_t m_thread; + Timer *m_timer; + std::vector m_Commandlisteners; + std::vector m_ClientStatislisteners; }; +} -#endif #endif /* __CC_CLIENT_H__ */ diff --git a/src/cc/CCClientConfig.cpp b/src/cc/CCClientConfig.cpp new file mode 100644 index 00000000..4cdc8d5d --- /dev/null +++ b/src/cc/CCClientConfig.cpp @@ -0,0 +1,122 @@ +/* XMRigCC + * Copyright 2017- BenDr0id , + * + * 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 . + */ + +#include "base/io/json/Json.h" +#include "cc/CCClientConfig.h" +#include "rapidjson/document.h" + + +namespace xmrig { + +static const char *kEnabled = "enabled"; +static const char *kUseTLS = "use-tls"; +static const char *kUseRemoteLog = "use-remote-logging"; +static const char *kUploadConfigOnStartup = "upload-config-on-start"; + +static const char *kUrl = "url"; +static const char *kAccessToken = "access-token"; +static const char *kWorkerId = "worker-id"; +static const char *kRebootCmd = "reboot-cmd"; + +static const char *kUpdateInterval = "update-interval-s"; + +} + + +rapidjson::Value xmrig::CCClientConfig::toJSON(rapidjson::Document &doc) const +{ + using namespace rapidjson; + auto &allocator = doc.GetAllocator(); + + Value obj(kObjectType); + + obj.AddMember(StringRef(kEnabled), m_enabled, allocator); + obj.AddMember(StringRef(kUseTLS), m_useTls, allocator); + obj.AddMember(StringRef(kUseRemoteLog), m_useRemoteLogging, allocator); + obj.AddMember(StringRef(kUploadConfigOnStartup), m_uploadConfigOnStartup, allocator); + + obj.AddMember(StringRef(kUrl), m_url.toJSON(), allocator); + obj.AddMember(StringRef(kAccessToken), m_token.toJSON(), allocator); + obj.AddMember(StringRef(kWorkerId), m_workerId.toJSON(), allocator); + obj.AddMember(StringRef(kRebootCmd), m_rebootCmd.toJSON(), allocator); + + obj.AddMember(StringRef(kUpdateInterval), m_updateInterval, allocator); + + return obj; +} + + +bool xmrig::CCClientConfig::read(const rapidjson::Value &value) +{ + if (value.IsObject()) { + m_enabled = Json::getBool(value, kEnabled, m_enabled); + m_useTls = Json::getBool(value, kUseTLS, m_useTls); + m_useRemoteLogging = Json::getBool(value, kUseRemoteLog, m_useRemoteLogging); + m_uploadConfigOnStartup = Json::getBool(value, kUploadConfigOnStartup, m_uploadConfigOnStartup); + + m_url = Json::getString(value, kUrl, m_url); + m_token = Json::getString(value, kAccessToken, m_token); + m_workerId = Json::getString(value, kWorkerId, m_workerId); + m_rebootCmd = Json::getString(value, kRebootCmd, m_rebootCmd); + + m_updateInterval = Json::getInt(value, kUpdateInterval, m_updateInterval); + + parseCCUrl(m_url); + + return true; + } + + return false; +} + +bool xmrig::CCClientConfig::parseCCUrl(const char* url) +{ + const char *base = url; + if (!base || !strlen(base) || *base == '/') { + return false; + } + + const char *port = strchr(base, ':'); + if (!port) { + m_host = base; + return true; + } + + const size_t size = port++ - base + 1; + auto *host = new char[size](); + memcpy(host, base, size - 1); + + m_host = host; + m_port = static_cast(strtol(port, nullptr, 10)); + + return true; +} + +bool xmrig::CCClientConfig::isEqual(const CCClientConfig &other) const +{ + return other.m_enabled == m_enabled && + other.m_useTls == m_useTls && + other.m_useRemoteLogging == m_useRemoteLogging && + other.m_uploadConfigOnStartup == m_uploadConfigOnStartup && + other.m_url == m_url && + other.m_host == m_host && + other.m_token == m_token && + other.m_port == m_port && + other.m_workerId == m_workerId && + other.m_rebootCmd == m_rebootCmd && + other.m_updateInterval == m_updateInterval; +} diff --git a/src/cc/CCClientConfig.h b/src/cc/CCClientConfig.h new file mode 100644 index 00000000..0bca8fe6 --- /dev/null +++ b/src/cc/CCClientConfig.h @@ -0,0 +1,73 @@ +/* XMRigCC + * Copyright 2017- BenDr0id , + * + * 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 . + */ + +#ifndef XMRIG_CC_CLIENT_CONFIG_H +#define XMRIG_CC_CLIENT_CONFIG_H + +#include "base/tools/String.h" +#include "rapidjson/fwd.h" + +namespace xmrig { + +class CCClientConfig +{ +public: + bool read(const rapidjson::Value &value); + rapidjson::Value toJSON(rapidjson::Document &doc) const; + + inline bool enabled() const { return m_enabled; } + inline bool useTLS() const { return m_useTls; } + inline bool useRemoteLogging() const { return m_useRemoteLogging; } + inline bool uploadConfigOnStartup() const { return m_uploadConfigOnStartup; } + + inline const char *url() const { return m_url.data(); } + inline const char *host() const { return m_host.data(); } + inline const char *token() const { return m_token.data(); } + inline const char *workerId() const { return m_workerId.data(); } + inline const char *rebootCmd() const { return m_rebootCmd.data(); } + + inline int updateInterval() const { return m_updateInterval; } + inline int port() const { return m_port; } + + inline bool operator!=(const CCClientConfig &other) const { return !isEqual(other); } + inline bool operator==(const CCClientConfig &other) const { return isEqual(other); } + + bool isEqual(const CCClientConfig &other) const; + +private: + bool parseCCUrl(const char* url); + + bool m_enabled = true; + bool m_useTls = false; + bool m_useRemoteLogging = true; + bool m_uploadConfigOnStartup = true; + + int m_updateInterval = 10; + int m_port = 3344; + + String m_url; + String m_host; + String m_token; + String m_workerId; + String m_rebootCmd; +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_CC_CLIENT_CONFIG_H */ diff --git a/src/cc/CCServer.cpp b/src/cc/CCServer.cpp index a65b4816..f738d01c 100644 --- a/src/cc/CCServer.cpp +++ b/src/cc/CCServer.cpp @@ -1,12 +1,5 @@ /* XMRigCC - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * Copyright 2017- BenDr0id - * + * Copyright 2017- BenDr0id , * * 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 diff --git a/src/cc/CCServer.h b/src/cc/CCServer.h index fb4a3daa..dd698760 100644 --- a/src/cc/CCServer.h +++ b/src/cc/CCServer.h @@ -1,12 +1,5 @@ /* XMRigCC - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * Copyright 2017- BenDr0id - * + * Copyright 2017- BenDr0id , * * 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 diff --git a/src/cc/ClientStatus.cpp b/src/cc/ClientStatus.cpp index e141c746..bb44ea10 100644 --- a/src/cc/ClientStatus.cpp +++ b/src/cc/ClientStatus.cpp @@ -1,5 +1,5 @@ /* XMRigCC - * Copyright 2017- BenDr0id + * Copyright 2017- BenDr0id , * * 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 @@ -37,11 +37,13 @@ ClientStatus::ClientStatus() m_totalPages(0), m_totalHugepages(0), m_currentThreads(0), + m_currentWays(0), m_cpuSockets(0), m_cpuCores(0), m_cpuThreads(0), m_cpuL2(0), m_cpuL3(0), + m_nodes(0), m_sharesGood(0), m_sharesTotal(0), m_hashesTotal(0), @@ -147,6 +149,16 @@ void ClientStatus::clearLog() m_log.clear(); } +std::string ClientStatus::getAssembly() const +{ + return m_assembly; +} + +void ClientStatus::setAssembly(const std::string& assembly) +{ + m_assembly = assembly; +} + bool ClientStatus::hasHugepages() const { return m_hasHugepages; @@ -267,6 +279,16 @@ void ClientStatus::setCurrentThreads(int currentThreads) m_currentThreads = currentThreads; } +int ClientStatus::getCurrentWays() const +{ + return m_currentWays; +} + +void ClientStatus::setCurrentWays(int currentWays) +{ + m_currentWays = currentWays; +} + int ClientStatus::getCpuSockets() const { return m_cpuSockets; @@ -317,6 +339,16 @@ void ClientStatus::setCpuL3(int cpuL3) m_cpuL3 = cpuL3; } +int ClientStatus::getNodes() +{ + return m_nodes; +} + +void ClientStatus::setNodes(int nodes) +{ + m_nodes = nodes; +} + uint64_t ClientStatus::getSharesGood() const { return m_sharesGood; @@ -463,6 +495,10 @@ bool ClientStatus::parseFromJson(const rapidjson::Document& document) m_currentThreads = clientStatus["current_threads"].GetInt(); } + if (clientStatus.HasMember("current_ways")) { + m_currentThreads = clientStatus["current_ways"].GetInt(); + } + if (clientStatus.HasMember("cpu_sockets")) { m_cpuSockets = clientStatus["cpu_sockets"].GetInt(); } @@ -553,6 +589,7 @@ rapidjson::Value ClientStatus::toJson(rapidjson::MemoryPoolAllocator - * + * Copyright 2017- BenDr0id , * * 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 @@ -81,6 +80,9 @@ public: void setLog(const std::string& log); void clearLog(); + std::string getAssembly() const; + void setAssembly(const std::string& assembly); + bool hasHugepages() const; void setHugepages(bool hasHugepages); @@ -117,6 +119,9 @@ public: int getCurrentThreads() const; void setCurrentThreads(int currentThreads); + int getCurrentWays() const; + void setCurrentWays(int currentWays); + int getCpuSockets() const; void setCpuSockets(int cpuSockets); @@ -132,6 +137,9 @@ public: int getCpuL3() const; void setCpuL3(int cpuL3); + void setNodes(int nodes); + int getNodes(); + const std::list getGPUInfoList() const; void addGPUInfo(const GPUInfo gpuInfo); void clearGPUInfoList(); @@ -174,6 +182,7 @@ private: std::string m_externalIp; std::string m_version; std::string m_log; + std::string m_assembly; bool m_hasHugepages; bool m_isHugepagesEnabled; @@ -189,11 +198,13 @@ private: int m_totalPages; int m_totalHugepages; int m_currentThreads; + int m_currentWays; int m_cpuSockets; int m_cpuCores; int m_cpuThreads; int m_cpuL2; int m_cpuL3; + int m_nodes; std::list m_gpuInfoList; diff --git a/src/cc/ControlCommand.cpp b/src/cc/ControlCommand.cpp index d3e27bc8..ce958c39 100644 --- a/src/cc/ControlCommand.cpp +++ b/src/cc/ControlCommand.cpp @@ -1,5 +1,5 @@ /* XMRigCC - * Copyright 2017- BenDr0id + * Copyright 2017- BenDr0id , * * 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,7 +26,7 @@ #ifdef TYPE_AMD_GPU #include "common/log/Log.h" #else -#include "log/Log.h" +#include "base/io/log/Log.h" #endif ControlCommand::ControlCommand() diff --git a/src/cc/ControlCommand.h b/src/cc/ControlCommand.h index 588a2ef2..95d2b7c3 100644 --- a/src/cc/ControlCommand.h +++ b/src/cc/ControlCommand.h @@ -1,5 +1,5 @@ /* XMRigCC - * Copyright 2017- BenDr0id + * Copyright 2017- BenDr0id , * * 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 diff --git a/src/cc/GPUInfo.cpp b/src/cc/GPUInfo.cpp index 324d7408..81fdbfc6 100644 --- a/src/cc/GPUInfo.cpp +++ b/src/cc/GPUInfo.cpp @@ -1,5 +1,5 @@ /* XMRigCC - * Copyright 2018- BenDr0id + * Copyright 2018- BenDr0id , * * 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 diff --git a/src/cc/GPUInfo.h b/src/cc/GPUInfo.h index 63194895..a20bf066 100644 --- a/src/cc/GPUInfo.h +++ b/src/cc/GPUInfo.h @@ -1,5 +1,5 @@ /* XMRigCC - * Copyright 2018- BenDr0id + * Copyright 2018- BenDr0id , * * 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 @@ -14,7 +14,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - #ifndef XMRIG_GPUINFO_H #define XMRIG_GPUINFO_H diff --git a/src/cc/Httpd.cpp b/src/cc/Httpd.cpp index 79cb7054..83b6d57f 100644 --- a/src/cc/Httpd.cpp +++ b/src/cc/Httpd.cpp @@ -1,12 +1,5 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * Copyright 2017- BenDr0id - * +/* XMRigCC + * Copyright 2017- BenDr0id , * * 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 diff --git a/src/cc/Httpd.h b/src/cc/Httpd.h index fcec7d83..4766f5f0 100644 --- a/src/cc/Httpd.h +++ b/src/cc/Httpd.h @@ -1,12 +1,5 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * Copyright 2017- BenDr0id - * +/* XMRigCC + * Copyright 2017- BenDr0id , * * 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 diff --git a/src/cc/Service.cpp b/src/cc/Service.cpp index 6520a3b0..72891312 100644 --- a/src/cc/Service.cpp +++ b/src/cc/Service.cpp @@ -1,12 +1,5 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * Copyright 2017- BenDr0id - * +/* XMRigCC + * Copyright 2017- BenDr0id , * * 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 diff --git a/src/cc/Service.h b/src/cc/Service.h index 88f5fecf..6f5d829b 100644 --- a/src/cc/Service.h +++ b/src/cc/Service.h @@ -1,12 +1,5 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * Copyright 2017- BenDr0id - * +/* XMRigCC + * Copyright 2017- BenDr0id , * * 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 diff --git a/src/cc/Summary.cpp b/src/cc/Summary.cpp index 938f66dd..9ebddd8f 100644 --- a/src/cc/Summary.cpp +++ b/src/cc/Summary.cpp @@ -1,13 +1,5 @@ - -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * Copyright 2017- BenDr0id - * +/* XMRigCC + * Copyright 2017- BenDr0id , * * 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 diff --git a/src/cc/XMRigCC.cpp b/src/cc/XMRigCC.cpp index 356b2cf2..241ac06a 100644 --- a/src/cc/XMRigCC.cpp +++ b/src/cc/XMRigCC.cpp @@ -1,12 +1,5 @@ /* XMRigCC - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * Copyright 2017- BenDr0id - * + * Copyright 2017- BenDr0id , * * 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 diff --git a/src/cc/XMRigd.cpp b/src/cc/XMRigd.cpp index d70db7c9..7f34982f 100644 --- a/src/cc/XMRigd.cpp +++ b/src/cc/XMRigd.cpp @@ -1,6 +1,5 @@ -/* XMRigd - * Copyright 2017- BenDr0id - * +/* XMRigCC + * Copyright 2017- BenDr0id , * * 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 @@ -67,13 +66,13 @@ int main(int argc, char **argv) { do { status = system(xmrigMinerPath.c_str()); #if defined(_WIN32) || defined(WIN32) - } while (status != EINVAL && status != SIGHUP && status != SIGINT); + } while (status != EINVAL && status != SIGHUP && status != SIGINT && status != 0); if (status == EINVAL) { std::this_thread::sleep_for(std::chrono::milliseconds(3000)); } #else - } while (WEXITSTATUS(status) != EINVAL && WEXITSTATUS(status) != SIGHUP && WEXITSTATUS(status) != SIGINT); + } while (WEXITSTATUS(status) != EINVAL && WEXITSTATUS(status) != SIGHUP && WEXITSTATUS(status) != SIGINT && WEXITSTATUS(status) != 0); #endif } diff --git a/src/config.json b/src/config.json index 52890bf1..f1d1ee86 100644 --- a/src/config.json +++ b/src/config.json @@ -1,44 +1,64 @@ { - "algo": "cryptonight", // cryptonight (default), cryptonight-lite, cryptonight-ultralite or cryptonight-heavy, cryptonight-ultralite, cryptonight-extremelite, argon2-256, argon2-512 - "aesni": 0, // selection of AES-NI mode (0 auto, 1 on, 2 off) - "threads": 0, // number of miner threads (not set or 0 enables automatic selection of optimal thread count) - "multihash-factor": 0, // number of hash blocks to process at a time (not set or 0 enables automatic selection of optimal number of hash blocks) - "multihash-thread-mask" : null, // for multihash-factors>0 only, limits multihash to given threads (mask), mask "0x3" means run multihash on thread 0 and 1 only (default: all threads) - "pow-variant" : "auto", // specificy the PoW variat to use: 'auto' (default), '0', '1', '2', 'ipbc', 'xao', 'xtl', 'rto', 'xfh', 'upx', 'turtle', 'hosp', 'r', 'wow', 'double (xcash)', 'zls' (zelerius), 'rwz' (graft), 'upx2', 'conceal', chukwa (trtl), wrkz - // for further help see: https://github.com/Bendr0id/xmrigCC/wiki/Coin-configurations - "asm-optimization" : "auto", // specificy the ASM optimization to use: -> auto (default), intel, ryzen, bulldozer, off - "background": false, // true to run the miner in the background (Windows only, for *nix plase use screen/tmux or systemd service instead) - "colors": true, // false to disable colored output - "cpu-affinity": null, // set process affinity to CPU core(s), mask "0x3" for cores 0 and 1 - "cpu-priority": null, // set process priority (0 idle, 2 normal to 5 highest) - "donate-level": 5, // donate level, mininum 1% - "log-file": null, // log all output to a file, example: "c:/some/path/xmrig.log" - "max-cpu-usage": 100, // maximum CPU usage for automatic mode, usually limiting factor is CPU cache not this option. - "print-time": 60, // print hashrate report every N seconds - "retries": 5, // number of times to retry before switch to backup server - "retry-pause": 5, // time to pause between retries - "safe": false, // true to safe adjust threads and av settings for current CPU - "syslog": false, // use system log for output messages - "reboot-cmd" : "", // command to execute to reboot the OS - "force-pow-variant" : false, // force pow variant, dont parse pow/variant from pool job - "skip-self-check" : false, // skip the self check on startup + "api": { + "id": null, + "worker-id": null + }, + "http": { + "enabled": false, + "host": "127.0.0.1", + "port": 0, + "access-token": null, + "restricted": true + }, + "autosave": true, + "version": 1, + "background": false, + "colors": true, + "randomx": { + "init": -1, + "numa": true + }, + "cpu": { + "enabled": true, + "huge-pages": true, + "hw-aes": null, + "priority": null, + "asm": true, + "argon2-impl": null, + "cn/0": false, + "cn-lite/0": false + }, + "donate-level": 5, + "log-file": null, "pools": [ { - "url": "donate2.graef.in:80", // URL of mining server - "user": "YOUR_WALLET_ID", // username for mining server - "pass": "x", // password for mining server - "use-tls" : false, // enable tls for pool communication (need pool support) - "keepalive": true, // send keepalived for prevent timeout (need pool support) - "nicehash": false // enable nicehash/xmrig-proxy support + "algo": null, + "url": "donate.graef.in:80", + "user": "YOUR_WALLET_ADDRESS", + "pass": "x", + "rig-id": null, + "nicehash": false, + "keepalive": false, + "enabled": true, + "tls": false, + "tls-fingerprint": null, + "daemon": false } ], "cc-client": { - "url": "localhost:3344", // url of the CC Server (ip:port) - "use-tls" : false, // enable tls for CC communication (needs to be enabled on CC Server too) - "access-token": "mySecret", // access token for CC Server (has to be the same in config_cc.json) - "worker-id": null, // custom worker-id for CC Server (otherwise hostname is used) - "update-interval-s": 10, // status update interval in seconds (default: 10 min: 1) - "use-remote-logging" : true, // enable remote logging on CC Server - "upload-config-on-startup" : true // upload current miner config to CC Server on startup - } -} + "enabled" : true, + "url": "localhost:3344", + "use-tls" : false, + "access-token": "mySecret", + "worker-id": null, + "update-interval-s": 10, + "use-remote-logging" : true, + "upload-config-on-start" : true + }, + "print-time": 60, + "retries": 5, + "retry-pause": 5, + "syslog": false, + "user-agent": null, + "watch": true +} \ No newline at end of file diff --git a/src/core/Controller.cpp b/src/core/Controller.cpp new file mode 100644 index 00000000..54c9ee34 --- /dev/null +++ b/src/core/Controller.cpp @@ -0,0 +1,104 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include + + +#include "backend/cpu/Cpu.h" +#include "core/Controller.h" +#include "core/Miner.h" +#include "net/Network.h" + + +xmrig::Controller::Controller(Process *process) : + Base(process) +{ +} + + +xmrig::Controller::~Controller() +{ + delete m_network; +} + + +bool xmrig::Controller::isReady() const +{ + return Base::isReady() && m_network; +} + + +int xmrig::Controller::init() +{ + Cpu::init(); + + const int rc = Base::init(); + if (rc != 0) { + return rc; + } + + m_network = new Network(this); + return 0; +} + + +void xmrig::Controller::start() +{ + Base::start(); + + m_miner = new Miner(this); + + network()->connect(); +} + + +void xmrig::Controller::stop() +{ + Base::stop(); + + delete m_network; + m_network = nullptr; + + m_miner->stop(); + + delete m_miner; + m_miner = nullptr; +} + + +xmrig::Miner *xmrig::Controller::miner() const +{ + assert(m_miner != nullptr); + + return m_miner; +} + + +xmrig::Network *xmrig::Controller::network() const +{ + assert(m_network != nullptr); + + return m_network; +} diff --git a/src/interfaces/IStrategyListener.h b/src/core/Controller.h similarity index 56% rename from src/interfaces/IStrategyListener.h rename to src/core/Controller.h index 60f95734..da7ba368 100644 --- a/src/interfaces/IStrategyListener.h +++ b/src/core/Controller.h @@ -4,8 +4,9 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , * * 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 @@ -21,29 +22,42 @@ * along with this program. If not, see . */ -#ifndef __ISTRATEGYLISTENER_H__ -#define __ISTRATEGYLISTENER_H__ +#ifndef XMRIG_CONTROLLER_H +#define XMRIG_CONTROLLER_H -#include +#include "base/kernel/Base.h" + + +namespace xmrig { -class Client; -class IStrategy; class Job; -class SubmitResult; +class Miner; +class Network; -class IStrategyListener +class Controller : public Base { public: - virtual ~IStrategyListener() {} + Controller(Process *process); + ~Controller() override; - virtual void onActive(Client *client) = 0; - virtual void onJob(Client *client, const Job &job) = 0; - virtual void onPause(IStrategy *strategy) = 0; - virtual void onResultAccepted(Client *client, const SubmitResult &result, const char *error) = 0; + bool isReady() const override; + int init() override; + void start() override; + void stop() override; + + Miner *miner() const; + Network *network() const; + +private: + Miner *m_miner = nullptr; + Network *m_network = nullptr; }; -#endif // __ISTRATEGYLISTENER_H__ +} // namespace xmrig + + +#endif /* XMRIG_CONTROLLER_H */ diff --git a/src/core/Miner.cpp b/src/core/Miner.cpp new file mode 100644 index 00000000..0b56be22 --- /dev/null +++ b/src/core/Miner.cpp @@ -0,0 +1,548 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include +#include +#include + +#include "backend/common/Hashrate.h" +#include "backend/cpu/Cpu.h" +#include "backend/cpu/CpuBackend.h" +#include "base/io/log/Log.h" +#include "base/kernel/Platform.h" +#include "base/net/stratum/Job.h" +#include "base/tools/Timer.h" +#include "core/config/Config.h" +#include "core/Controller.h" +#include "core/Miner.h" +#include "crypto/common/Nonce.h" +#include "crypto/rx/Rx.h" +#include "rapidjson/document.h" +#include "version.h" + + +#ifdef XMRIG_FEATURE_API +# include "base/api/Api.h" +# include "base/api/interfaces/IApiRequest.h" +#endif + +#ifdef XMRIG_FEATURE_CC_CLIENT +# include "cc/CCClient.h" +# include "crypto/common/VirtualMemory.h" +#endif + +namespace xmrig { + + +class MinerPrivate +{ +public: + inline MinerPrivate(Controller *controller) : controller(controller) + { + uv_rwlock_init(&rwlock); + +# ifdef XMRIG_ALGO_RANDOMX + Rx::init(); +# endif + } + + + inline ~MinerPrivate() + { + uv_rwlock_destroy(&rwlock); + + delete timer; + + for (IBackend *backend : backends) { + delete backend; + } + +# ifdef XMRIG_ALGO_RANDOMX + Rx::destroy(); +# endif + } + + + bool isEnabled(const Algorithm &algorithm) const + { + for (IBackend *backend : backends) { + if (backend->isEnabled(algorithm)) { + return true; + } + } + + return false; + } + + + inline void rebuild() + { + algorithms.clear(); + + for (int i = 0; i < Algorithm::MAX; ++i) { + const Algorithm algo(static_cast(i)); + + if (isEnabled(algo)) { + algorithms.push_back(algo); + } + } + } + + + inline void handleJobChange(bool reset) + { + active = true; + + for (IBackend *backend : backends) { + backend->setJob(job); + } + + if (reset) { + Nonce::reset(job.index()); + } + else { + Nonce::touch(); + } + + if (enabled) { + Nonce::pause(false);; + } + + if (ticks == 0) { + ticks++; + timer->start(500, 500); + } + } + + +# ifdef XMRIG_FEATURE_API + void getMiner(rapidjson::Value &reply, rapidjson::Document &doc, int version) const + { + using namespace rapidjson; + auto &allocator = doc.GetAllocator(); + + reply.AddMember("version", APP_VERSION, allocator); + reply.AddMember("kind", APP_KIND, allocator); + reply.AddMember("ua", StringRef(Platform::userAgent()), allocator); + reply.AddMember("cpu", Cpu::toJSON(doc), allocator); + + Value hugepages; + + if (!backends.empty() && backends.front()->type() == "cpu") { + const auto pages = static_cast(backends.front())->hugePages(); + if (version > 1) { + hugepages.SetArray(); + hugepages.PushBack(pages.first, allocator); + hugepages.PushBack(pages.second, allocator); + } + else { + hugepages = pages.first == pages.second; + } + } + else { + hugepages = false; + } + + reply.AddMember("hugepages", hugepages, allocator); + reply.AddMember("donate_level", controller->config()->pools().donateLevel(), allocator); + + Value algo(kArrayType); + + for (const Algorithm &a : algorithms) { + algo.PushBack(StringRef(a.shortName()), allocator); + } + + reply.AddMember("algorithms", algo, allocator); + } + + + void getHashrate(rapidjson::Value &reply, rapidjson::Document &doc, int version) const + { + using namespace rapidjson; + auto &allocator = doc.GetAllocator(); + + Value hashrate(kObjectType); + Value total(kArrayType); + Value threads(kArrayType); + + double t[3] = { 0.0 }; + + for (IBackend *backend : backends) { + const Hashrate *hr = backend->hashrate(); + if (!hr) { + continue; + } + + t[0] += hr->calc(Hashrate::ShortInterval); + t[1] += hr->calc(Hashrate::MediumInterval); + t[2] += hr->calc(Hashrate::LargeInterval); + + if (version > 1) { + continue; + } + + for (size_t i = 0; i < hr->threads(); i++) { + Value thread(kArrayType); + thread.PushBack(Hashrate::normalize(hr->calc(i, Hashrate::ShortInterval)), allocator); + thread.PushBack(Hashrate::normalize(hr->calc(i, Hashrate::MediumInterval)), allocator); + thread.PushBack(Hashrate::normalize(hr->calc(i, Hashrate::LargeInterval)), allocator); + + threads.PushBack(thread, allocator); + } + } + + total.PushBack(Hashrate::normalize(t[0]), allocator); + total.PushBack(Hashrate::normalize(t[1]), allocator); + total.PushBack(Hashrate::normalize(t[2]), allocator); + + hashrate.AddMember("total", total, allocator); + hashrate.AddMember("highest", Hashrate::normalize(maxHashrate[algorithm]), allocator); + + if (version == 1) { + hashrate.AddMember("threads", threads, allocator); + } + + reply.AddMember("hashrate", hashrate, allocator); + } + + + void getBackends(rapidjson::Value &reply, rapidjson::Document &doc) const + { + using namespace rapidjson; + auto &allocator = doc.GetAllocator(); + + reply.SetArray(); + + for (IBackend *backend : backends) { + reply.PushBack(backend->toJSON(doc), allocator); + } + } +# endif + + + Algorithm algorithm; + Algorithms algorithms; + bool active = false; + bool enabled = true; + Controller *controller; + Job job; + mutable std::map maxHashrate; + std::vector backends; + String userJobId; + Timer *timer = nullptr; + uint64_t ticks = 0; + uv_rwlock_t rwlock; +}; + + +} // namespace xmrig + + + +xmrig::Miner::Miner(Controller *controller) + : d_ptr(new MinerPrivate(controller)) +{ + controller->addListener(this); + +# ifdef XMRIG_FEATURE_API + controller->api()->addListener(this); +# endif + +# ifdef XMRIG_FEATURE_CC_CLIENT + controller->ccClient()->addClientStatusListener(this); +# endif + + d_ptr->timer = new Timer(this); + + d_ptr->backends.push_back(new CpuBackend(controller)); + + d_ptr->rebuild(); +} + + +xmrig::Miner::~Miner() +{ + delete d_ptr; +} + + +bool xmrig::Miner::isEnabled() const +{ + return d_ptr->enabled; +} + + +bool xmrig::Miner::isEnabled(const Algorithm &algorithm) const +{ + return std::find(d_ptr->algorithms.begin(), d_ptr->algorithms.end(), algorithm) != d_ptr->algorithms.end(); +} + + +const xmrig::Algorithms &xmrig::Miner::algorithms() const +{ + return d_ptr->algorithms; +} + + +const std::vector &xmrig::Miner::backends() const +{ + return d_ptr->backends; +} + + +xmrig::Job xmrig::Miner::job() const +{ + uv_rwlock_rdlock(&d_ptr->rwlock); + Job job = d_ptr->job; + uv_rwlock_rdunlock(&d_ptr->rwlock); + + return job; +} + + +void xmrig::Miner::pause() +{ + d_ptr->active = false; + + Nonce::pause(true); + Nonce::touch(); +} + + +void xmrig::Miner::printHashrate(bool details) +{ + char num[8 * 4] = { 0 }; + double speed[3] = { 0.0 }; + + for (IBackend *backend : d_ptr->backends) { + const Hashrate *hashrate = backend->hashrate(); + if (hashrate) { + speed[0] += hashrate->calc(Hashrate::ShortInterval); + speed[1] += hashrate->calc(Hashrate::MediumInterval); + speed[2] += hashrate->calc(Hashrate::LargeInterval); + } + + backend->printHashrate(details); + } + + LOG_INFO(WHITE_BOLD("speed") " 10s/60s/15m " CYAN_BOLD("%s") CYAN(" %s %s ") CYAN_BOLD("H/s") " max " CYAN_BOLD("%s H/s"), + Hashrate::format(speed[0], num, sizeof(num) / 4), + Hashrate::format(speed[1], num + 8, sizeof(num) / 4), + Hashrate::format(speed[2], num + 8 * 2, sizeof(num) / 4 ), + Hashrate::format(d_ptr->maxHashrate[d_ptr->algorithm], num + 8 * 3, sizeof(num) / 4) + ); +} + + +void xmrig::Miner::setEnabled(bool enabled) +{ + if (d_ptr->enabled == enabled) { + return; + } + + d_ptr->enabled = enabled; + + if (enabled) { + LOG_INFO(GREEN_BOLD("resumed")); + } + else { + LOG_INFO(YELLOW_BOLD("paused") ", press " MAGENTA_BG_BOLD(" r ") " to resume"); + } + + if (!d_ptr->active) { + return; + } + + Nonce::pause(!enabled); + Nonce::touch(); +} + + +void xmrig::Miner::setJob(const Job &job, bool donate) +{ + for (IBackend *backend : d_ptr->backends) { + backend->prepare(job); + } + +# ifdef XMRIG_ALGO_RANDOMX + if (d_ptr->algorithm.family() == Algorithm::RANDOM_X && job.algorithm().family() == Algorithm::RANDOM_X && !Rx::isReady(job)) { + stop(); + } +# endif + + d_ptr->algorithm = job.algorithm(); + + uv_rwlock_wrlock(&d_ptr->rwlock); + + const uint8_t index = donate ? 1 : 0; + const bool reset = !(d_ptr->job.index() == 1 && index == 0 && d_ptr->userJobId == job.id()); + + d_ptr->job = job; + d_ptr->job.setIndex(index); + + if (index == 0) { + d_ptr->userJobId = job.id(); + } + +# ifdef XMRIG_ALGO_RANDOMX + Rx::init(d_ptr->job, + d_ptr->controller->config()->rx().threads(), + d_ptr->controller->config()->cpu().isHugePages(), + d_ptr->controller->config()->rx().isNUMA() + ); +# endif + + uv_rwlock_wrunlock(&d_ptr->rwlock); + + d_ptr->handleJobChange(reset); +} + + +void xmrig::Miner::stop() +{ + Nonce::stop(); + + for (IBackend *backend : d_ptr->backends) { + backend->stop(); + } +} + + +void xmrig::Miner::onConfigChanged(Config *config, Config *previousConfig) +{ + d_ptr->rebuild(); + + if (config->pools() != previousConfig->pools() && config->pools().active() > 0) { + return; + } + + const Job job = this->job(); + + for (IBackend *backend : d_ptr->backends) { + backend->setJob(job); + } +} + + +void xmrig::Miner::onTimer(const Timer *) +{ + double maxHashrate = 0.0; + + for (IBackend *backend : d_ptr->backends) { + backend->tick(d_ptr->ticks); + + if (backend->hashrate()) { + maxHashrate += backend->hashrate()->calc(Hashrate::ShortInterval); + } + } + + d_ptr->maxHashrate[d_ptr->algorithm] = std::max(d_ptr->maxHashrate[d_ptr->algorithm], maxHashrate); + + if ((d_ptr->ticks % (d_ptr->controller->config()->printTime() * 2)) == 0) { + printHashrate(false); + } + + d_ptr->ticks++; +} + + +#ifdef XMRIG_FEATURE_API +void xmrig::Miner::onRequest(IApiRequest &request) +{ + if (request.method() == IApiRequest::METHOD_GET) { + if (request.type() == IApiRequest::REQ_SUMMARY) { + request.accept(); + + d_ptr->getMiner(request.reply(), request.doc(), request.version()); + d_ptr->getHashrate(request.reply(), request.doc(), request.version()); + } + else if (request.url() == "/2/backends") { + request.accept(); + + d_ptr->getBackends(request.reply(), request.doc()); + } + } + else if (request.type() == IApiRequest::REQ_JSON_RPC) { + if (request.rpcMethod() == "pause") { + request.accept(); + + setEnabled(false); + } + else if (request.rpcMethod() == "resume") { + request.accept(); + + setEnabled(true); + } + } +} +#endif + + +#ifdef XMRIG_FEATURE_CC_CLIENT +void xmrig::Miner::onUpdateRequest(ClientStatus& clientStatus) +{ + clientStatus.setCurrentStatus(d_ptr->enabled ? ClientStatus::RUNNING : ClientStatus::PAUSED); + + double t[3] = { 0.0 }; + int ways = 0; + int threads = 0; + int totalPages = 0; + int totalHugepages = 0; + + for (IBackend *backend : d_ptr->backends) { + const Hashrate *hr = backend->hashrate(); + if (!hr) { + continue; + } + + t[0] += hr->calc(Hashrate::ShortInterval); + t[1] += hr->calc(Hashrate::MediumInterval); + t[2] += hr->calc(Hashrate::LargeInterval); + + threads += backend->hashrate()->threads(); + + if (backend->type() == "cpu") { + const auto cpuBackend = static_cast(backend); + + ways += cpuBackend->ways(); + totalHugepages += cpuBackend->hugePages().first; + totalPages += cpuBackend->hugePages().second; + + } + } + + clientStatus.setTotalPages(totalPages); + clientStatus.setTotalHugepages(totalHugepages); + clientStatus.setHugepagesEnabled(totalHugepages>0); + clientStatus.setHugepages(VirtualMemory::isHugepagesAvailable()); + clientStatus.setCurrentThreads(threads); + clientStatus.setCurrentWays(ways); + clientStatus.setHashFactor(threads > 0 ? ways/threads : 0); + clientStatus.setHashrateShort(t[0]); + clientStatus.setHashrateMedium(t[1]); + clientStatus.setHashrateLong(t[2]); + clientStatus.setHashrateHighest(d_ptr->maxHashrate[d_ptr->algorithm]); +} +#endif diff --git a/src/core/Miner.h b/src/core/Miner.h new file mode 100644 index 00000000..161a0192 --- /dev/null +++ b/src/core/Miner.h @@ -0,0 +1,84 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_MINER_H +#define XMRIG_MINER_H + + +#include + +#include "base/api/interfaces/IApiListener.h" +#include "base/kernel/interfaces/IBaseListener.h" +#include "base/kernel/interfaces/ITimerListener.h" +#include "base/cc/interfaces/IClientStatusListener.h" +#include "crypto/common/Algorithm.h" + + +namespace xmrig { + + +class Controller; +class Job; +class MinerPrivate; +class IBackend; + + +class Miner : public ITimerListener, public IBaseListener, public IApiListener, public IClientStatusListener +{ +public: + Miner(Controller *controller); + ~Miner() override; + + bool isEnabled() const; + bool isEnabled(const Algorithm &algorithm) const; + const Algorithms &algorithms() const; + const std::vector &backends() const; + Job job() const; + void pause(); + void printHashrate(bool details); + void setEnabled(bool enabled); + void setJob(const Job &job, bool donate); + void stop(); + +protected: + void onConfigChanged(Config *config, Config *previousConfig) override; + void onTimer(const Timer *timer) override; + +# ifdef XMRIG_FEATURE_API + void onRequest(IApiRequest &request) override; +# endif + +# ifdef XMRIG_FEATURE_CC_CLIENT + void onUpdateRequest(ClientStatus& clientStatus) override; +# endif + +private: + MinerPrivate *d_ptr; +}; + + +} // namespace xmrig + + +#endif /* XMRIG_MINER_H */ diff --git a/src/core/config/Config.cpp b/src/core/config/Config.cpp new file mode 100644 index 00000000..21d2d8d3 --- /dev/null +++ b/src/core/config/Config.cpp @@ -0,0 +1,137 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#include +#include +#include +#include + + +#include "backend/cpu/Cpu.h" +#include "base/io/log/Log.h" +#include "base/kernel/interfaces/IJsonReader.h" +#include "core/config/Config.h" +#include "crypto/common/Assembly.h" +#include "rapidjson/document.h" +#include "rapidjson/filewritestream.h" +#include "rapidjson/prettywriter.h" + + +namespace xmrig { + +static const char *kCPU = "cpu"; +static constexpr const uint32_t kVersion = 1; + +#ifdef XMRIG_ALGO_RANDOMX +static const char *kRandomX = "randomx"; +#endif + +#ifdef XMRIG_FEATURE_CC_CLIENT +static const char *kCCClient = "cc-client"; +#endif + +} + + +xmrig::Config::Config() : BaseConfig() +{ +} + + +bool xmrig::Config::isShouldSave() const +{ + if (!isAutoSave()) { + return false; + } + + if (version() < kVersion) { + return true; + } + + return (m_shouldSave || m_upgrade || m_cpu.isShouldSave()); +} + + +bool xmrig::Config::read(const IJsonReader &reader, const char *fileName) +{ + if (!BaseConfig::read(reader, fileName)) { + return false; + } + + m_cpu.read(reader.getValue(kCPU), version()); + +# ifdef XMRIG_ALGO_RANDOMX + if (!m_rx.read(reader.getValue(kRandomX))) { + m_upgrade = true; + } +# endif + +# ifdef XMRIG_FEATURE_CC_CLIENT + m_ccClient.read(reader.getValue(kCCClient)); +# endif + + return true; +} + + +void xmrig::Config::getJSON(rapidjson::Document &doc) const +{ + using namespace rapidjson; + + doc.SetObject(); + + auto &allocator = doc.GetAllocator(); + + Value api(kObjectType); + api.AddMember("id", m_apiId.toJSON(), allocator); + api.AddMember("worker-id", m_apiWorkerId.toJSON(), allocator); + + doc.AddMember("api", api, allocator); + doc.AddMember("http", m_http.toJSON(doc), allocator); + doc.AddMember("autosave", isAutoSave(), allocator); + doc.AddMember("version", kVersion, allocator); + doc.AddMember("background", isBackground(), allocator); + doc.AddMember("colors", Log::colors, allocator); + +# ifdef XMRIG_ALGO_RANDOMX + doc.AddMember(StringRef(kRandomX), m_rx.toJSON(doc), allocator); +# endif + + doc.AddMember(StringRef(kCPU), m_cpu.toJSON(doc), allocator); + doc.AddMember("donate-level", m_pools.donateLevel(), allocator); + doc.AddMember("donate-over-proxy", m_pools.proxyDonate(), allocator); + doc.AddMember("log-file", m_logFile.toJSON(), allocator); + doc.AddMember("pools", m_pools.toJSON(doc), allocator); + +# ifdef XMRIG_FEATURE_CC_CLIENT + doc.AddMember("cc-client", m_ccClient.toJSON(doc), allocator); +# endif + + doc.AddMember("print-time", printTime(), allocator); + doc.AddMember("retries", m_pools.retries(), allocator); + doc.AddMember("retry-pause", m_pools.retryPause(), allocator); + doc.AddMember("syslog", isSyslog(), allocator); + doc.AddMember("user-agent", m_userAgent.toJSON(), allocator); + doc.AddMember("watch", m_watch, allocator); +} diff --git a/src/core/config/Config.h b/src/core/config/Config.h new file mode 100644 index 00000000..89351bb9 --- /dev/null +++ b/src/core/config/Config.h @@ -0,0 +1,74 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_CONFIG_H +#define XMRIG_CONFIG_H + + +#include + + +#include "backend/cpu/CpuConfig.h" +#include "base/kernel/config/BaseConfig.h" +#include "rapidjson/fwd.h" + + +#ifdef XMRIG_ALGO_RANDOMX +# include "crypto/rx/RxConfig.h" +#endif + +namespace xmrig { + +class IThread; + + +class Config : public BaseConfig +{ +public: + Config(); + + bool isShouldSave() const; + bool read(const IJsonReader &reader, const char *fileName) override; + void getJSON(rapidjson::Document &doc) const override; + + inline const CpuConfig &cpu() const { return m_cpu; } + +# ifdef XMRIG_ALGO_RANDOMX + inline const RxConfig &rx() const { return m_rx; } +# endif + +private: + bool m_shouldSave = false; + CpuConfig m_cpu; + +# ifdef XMRIG_ALGO_RANDOMX + RxConfig m_rx; +# endif +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_CONFIG_H */ diff --git a/src/core/config/ConfigTransform.cpp b/src/core/config/ConfigTransform.cpp new file mode 100644 index 00000000..ce0d324c --- /dev/null +++ b/src/core/config/ConfigTransform.cpp @@ -0,0 +1,190 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include "base/kernel/interfaces/IConfig.h" +#include "core/config/ConfigTransform.h" +#include "crypto/cn/CnHash.h" + + +namespace xmrig +{ + + +static const char *kAffinity = "affinity"; +static const char *kAsterisk = "*"; +static const char *kCpu = "cpu"; +static const char *kIntensity = "intensity"; +static const char *kThreads = "threads"; + +#ifdef XMRIG_ALGO_RANDOMX +static const char *kRandomX = "randomx"; +#endif + + +static inline uint64_t intensity(uint64_t av) +{ + switch (av) { + case CnHash::AV_SINGLE: + case CnHash::AV_SINGLE_SOFT: + return 1; + + case CnHash::AV_DOUBLE_SOFT: + case CnHash::AV_DOUBLE: + return 2; + + case CnHash::AV_TRIPLE_SOFT: + case CnHash::AV_TRIPLE: + return 3; + + case CnHash::AV_QUAD_SOFT: + case CnHash::AV_QUAD: + return 4; + + case CnHash::AV_PENTA_SOFT: + case CnHash::AV_PENTA: + return 5; + + default: + break; + } + + return 1; +} + + +static inline bool isHwAes(uint64_t av) +{ + return av == CnHash::AV_SINGLE || av == CnHash::AV_DOUBLE || (av > CnHash::AV_DOUBLE_SOFT && av < CnHash::AV_TRIPLE_SOFT); +} + + +} + + +xmrig::ConfigTransform::ConfigTransform() : BaseTransform() +{ +} + + +void xmrig::ConfigTransform::finalize(rapidjson::Document &doc) +{ + using namespace rapidjson; + auto &allocator = doc.GetAllocator(); + + BaseTransform::finalize(doc); + + if (m_threads) { + doc.AddMember("version", 1, allocator); + + if (!doc.HasMember(kCpu)) { + doc.AddMember(StringRef(kCpu), Value(kObjectType), allocator); + } + + Value profile(kObjectType); + profile.AddMember(StringRef(kIntensity), m_intensity, allocator); + profile.AddMember(StringRef(kThreads), m_threads, allocator); + profile.AddMember(StringRef(kAffinity), m_affinity, allocator); + + doc[kCpu].AddMember(StringRef(kAsterisk), profile, doc.GetAllocator()); + } +} + + +void xmrig::ConfigTransform::transform(rapidjson::Document &doc, int key, const char *arg) +{ + BaseTransform::transform(doc, key, arg); + + switch (key) { + case IConfig::AVKey: /* --av */ + case IConfig::CPUPriorityKey: /* --cpu-priority */ + case IConfig::ThreadsKey: /* --threads */ + return transformUint64(doc, key, static_cast(strtol(arg, nullptr, 10))); + + case IConfig::HugePagesKey: /* --no-huge-pages */ + return transformBoolean(doc, key, false); + + case IConfig::CPUAffinityKey: /* --cpu-affinity */ + { + const char *p = strstr(arg, "0x"); + return transformUint64(doc, key, p ? strtoull(p, nullptr, 16) : strtoull(arg, nullptr, 10)); + } + +# ifndef XMRIG_NO_ASM + case IConfig::AssemblyKey: /* --asm */ + return set(doc, kCpu, "asm", arg); +# endif + +# ifdef XMRIG_ALGO_RANDOMX + case IConfig::RandomXInitKey: /* --randomx-init */ + return set(doc, kRandomX, "init", static_cast(strtol(arg, nullptr, 10))); + + case IConfig::RandomXNumaKey: /* --randomx-no-numa */ + return set(doc, kRandomX, "numa", false); +# endif + + default: + break; + } +} + + +void xmrig::ConfigTransform::transformBoolean(rapidjson::Document &doc, int key, bool enable) +{ + switch (key) { + case IConfig::HugePagesKey: /* --no-huge-pages */ + return set(doc, kCpu, "huge-pages", enable); + + default: + break; + } +} + + +void xmrig::ConfigTransform::transformUint64(rapidjson::Document &doc, int key, uint64_t arg) +{ + using namespace rapidjson; + + switch (key) { + case IConfig::CPUAffinityKey: /* --cpu-affinity */ + m_affinity = static_cast(arg); + break; + + case IConfig::ThreadsKey: /* --threads */ + m_threads = arg; + break; + + case IConfig::AVKey: /* --av */ + m_intensity = intensity(arg); + set(doc, kCpu, "hw-aes", isHwAes(arg)); + break; + + case IConfig::CPUPriorityKey: /* --cpu-priority */ + return set(doc, kCpu, "priority", arg); + + default: + break; + } +} + diff --git a/src/core/config/ConfigTransform.h b/src/core/config/ConfigTransform.h new file mode 100644 index 00000000..440a7169 --- /dev/null +++ b/src/core/config/ConfigTransform.h @@ -0,0 +1,57 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_CONFIGTRANSFORM_H +#define XMRIG_CONFIGTRANSFORM_H + + +#include "base/kernel/config/BaseTransform.h" + + +namespace xmrig { + + +class ConfigTransform : public BaseTransform +{ +public: + ConfigTransform(); + +protected: + void finalize(rapidjson::Document &doc) override; + void transform(rapidjson::Document &doc, int key, const char *arg) override; + +private: + void transformBoolean(rapidjson::Document &doc, int key, bool enable); + void transformUint64(rapidjson::Document &doc, int key, uint64_t arg); + + int64_t m_affinity = -1; + uint64_t m_intensity = 1; + uint64_t m_threads = 0; +}; + + +} // namespace xmrig + + +#endif /* XMRIG_CONFIGTRANSFORM_H */ diff --git a/src/core/config/Config_default.h b/src/core/config/Config_default.h new file mode 100644 index 00000000..8973cbad --- /dev/null +++ b/src/core/config/Config_default.h @@ -0,0 +1,106 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_CONFIG_DEFAULT_H +#define XMRIG_CONFIG_DEFAULT_H + + +namespace xmrig { + + +#ifdef XMRIG_FEATURE_EMBEDDED_CONFIG +const static char *default_config = +R"===( +{ + "api": { + "id": null, + "worker-id": null + }, + "http": { + "enabled": false, + "host": "127.0.0.1", + "port": 0, + "access-token": null, + "restricted": true + }, + "autosave": true, + "version": 1, + "background": false, + "colors": true, + "randomx": { + "init": -1, + "numa": true + }, + "cpu": { + "enabled": true, + "huge-pages": true, + "hw-aes": null, + "priority": null, + "asm": true, + "argon2-impl": null, + "cn/0": false, + "cn-lite/0": false + }, + "donate-level": 5, + "log-file": null, + "pools": [ + { + "algo": null, + "url": "donate.graef.in:80", + "user": "YOUR_WALLET_ADDRESS", + "pass": "x", + "rig-id": null, + "nicehash": false, + "keepalive": false, + "enabled": true, + "tls": false, + "tls-fingerprint": null, + "daemon": false + } + ], + "cc-client": { + "enabled" : true, + "url": "localhost:3344", + "use-tls" : false, + "access-token": "mySecret", + "worker-id": null, + "update-interval-s": 10, + "use-remote-logging" : true, + "upload-config-on-start" : true + }, + "print-time": 60, + "retries": 5, + "retry-pause": 5, + "syslog": false, + "user-agent": null, + "watch": false +} +)==="; +#endif + + +} // namespace xmrig + + +#endif /* XMRIG_CONFIG_DEFAULT_H */ diff --git a/src/core/config/Config_platform.h b/src/core/config/Config_platform.h new file mode 100644 index 00000000..b426ed97 --- /dev/null +++ b/src/core/config/Config_platform.h @@ -0,0 +1,104 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_CONFIG_PLATFORM_H +#define XMRIG_CONFIG_PLATFORM_H + + +#ifdef _MSC_VER +# include "getopt/getopt.h" +#else +# include +#endif + + +#include "base/kernel/interfaces/IConfig.h" +#include "version.h" + + +namespace xmrig { + + +static const char short_options[] = "a:c:kBp:Px:r:R:s:t:T:o:u:O:v:l:S"; + + +static const option options[] = { + { "algo", 1, nullptr, IConfig::AlgorithmKey }, + { "api-worker-id", 1, nullptr, IConfig::ApiWorkerIdKey }, + { "api-id", 1, nullptr, IConfig::ApiIdKey }, + { "http-enabled", 0, nullptr, IConfig::HttpEnabledKey }, + { "http-host", 1, nullptr, IConfig::HttpHostKey }, + { "http-access-token", 1, nullptr, IConfig::HttpAccessTokenKey }, + { "http-port", 1, nullptr, IConfig::HttpPort }, + { "http-no-restricted", 0, nullptr, IConfig::HttpRestrictedKey }, + { "av", 1, nullptr, IConfig::AVKey }, + { "background", 0, nullptr, IConfig::BackgroundKey }, + { "config", 1, nullptr, IConfig::ConfigKey }, + { "cpu-affinity", 1, nullptr, IConfig::CPUAffinityKey }, + { "cpu-priority", 1, nullptr, IConfig::CPUPriorityKey }, + { "donate-level", 1, nullptr, IConfig::DonateLevelKey }, + { "donate-over-proxy", 1, nullptr, IConfig::ProxyDonateKey }, + { "dry-run", 0, nullptr, IConfig::DryRunKey }, + { "keepalive", 0, nullptr, IConfig::KeepAliveKey }, + { "log-file", 1, nullptr, IConfig::LogFileKey }, + { "nicehash", 0, nullptr, IConfig::NicehashKey }, + { "no-color", 0, nullptr, IConfig::ColorKey }, + { "no-huge-pages", 0, nullptr, IConfig::HugePagesKey }, + { "pass", 1, nullptr, IConfig::PasswordKey }, + { "print-time", 1, nullptr, IConfig::PrintTimeKey }, + { "retries", 1, nullptr, IConfig::RetriesKey }, + { "retry-pause", 1, nullptr, IConfig::RetryPauseKey }, + { "syslog", 0, nullptr, IConfig::SyslogKey }, + { "threads", 1, nullptr, IConfig::ThreadsKey }, + { "url", 1, nullptr, IConfig::UrlKey }, + { "user", 1, nullptr, IConfig::UserKey }, + { "user-agent", 1, nullptr, IConfig::UserAgentKey }, + { "userpass", 1, nullptr, IConfig::UserpassKey }, + { "rig-id", 1, nullptr, IConfig::RigIdKey }, + { "tls", 0, nullptr, IConfig::TlsKey }, + { "tls-fingerprint", 1, nullptr, IConfig::FingerprintKey }, + { "asm", 1, nullptr, IConfig::AssemblyKey }, + { "daemon", 0, nullptr, IConfig::DaemonKey }, + { "daemon-poll-interval", 1, nullptr, IConfig::DaemonPollKey }, + { "randomx-init", 1, nullptr, IConfig::RandomXInitKey }, + { "randomx-no-numa", 0, nullptr, IConfig::RandomXNumaKey }, + // xmrigCC related + { "daemonized", 0, nullptr, IConfig::CCDaemonizedKey }, + { "cc-disabled", 0, nullptr, IConfig::CCEnabledKey }, + { "cc-use-tls", 0, nullptr, IConfig::CCUseTLS }, + { "cc-use-remote-logging", 0, nullptr, IConfig::CCUseRemoteLog }, + { "cc-reboot-cmd", 1, nullptr, IConfig::CCRebootCmd }, + { "cc-url", 1, nullptr, IConfig::CCUrl }, + { "cc-access-token", 1, nullptr, IConfig::CCAccessToken }, + { "cc-worker-id", 1, nullptr, IConfig::CCWorkerId }, + { "cc-update-interval-s", 1, nullptr, IConfig::CCUpdateInterval }, + { "cc-upload-config-on-start", 0, nullptr, IConfig::CCUploadConfigOnStartup }, + { nullptr, 0, nullptr, 0 } +}; + + +} // namespace xmrig + + +#endif /* XMRIG_CONFIG_PLATFORM_H */ diff --git a/src/core/config/usage.h b/src/core/config/usage.h new file mode 100644 index 00000000..a91c6f6a --- /dev/null +++ b/src/core/config/usage.h @@ -0,0 +1,149 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_USAGE_H +#define XMRIG_USAGE_H + + +#include "version.h" + + +namespace xmrig { + + +static char const usage[] = "\ +Usage: " APP_ID " [OPTIONS]\n\ +Options:\n\ + -a, --algo=ALGO specify the algorithm to use\n\ + cn/r, cn/2, cn/1, cn/0, cn/double, cn/half, cn/fast,\n\ + cn/rwz, cn/zls, cn/xao, cn/rto, cn/conceal" +#ifdef XMRIG_ALGO_CN_GPU +", cn/gpu,\n" +#else +",\n" +#endif +#ifdef XMRIG_ALGO_CN_LITE +"\ + cn-lite/1,\n" +#endif +#ifdef XMRIG_ALGO_CN_HEAVY +"\ + cn-heavy/xhv, cn-heavy/tube, cn-heavy/0,\n" +#endif +#ifdef XMRIG_ALGO_CN_PICO +"\ + cn-pico\n" +#endif +#ifdef XMRIG_ALGO_CN_EXTREMELITE +"\ + cn-extremelite\n" +#endif +#ifdef XMRIG_ALGO_ARGON2 +"\ + argon2/chukwa, argon2/wrkz\n" +#endif +#ifdef XMRIG_ALGO_RANDOMX +"\ + rx/wow, rx/loki\n" +#endif +"\ + -o, --url=URL URL of mining server\n\ + -O, --userpass=U:P username:password pair for mining server\n\ + -u, --user=USERNAME username for mining server\n\ + -p, --pass=PASSWORD password for mining server\n\ + --rig-id=ID rig identifier for pool-side statistics (needs pool support)\n\ + -t, --threads=N number of miner threads\n\ + -v, --av=N algorithm variation, 0 auto select\n\ + -k, --keepalive send keepalived packet for prevent timeout (needs pool support)\n\ + --nicehash enable nicehash.com support\n" +#ifdef XMRIG_FEATURE_TLS +"\ + --tls enable SSL/TLS support (needs pool support)\n\ + --tls-fingerprint=F pool TLS certificate fingerprint, if set enable strict certificate pinning\n" +#endif +#ifdef XMRIG_FEATURE_HTTP +"\ + --daemon use daemon RPC instead of pool for solo mining\n\ + --daemon-poll-interval=N daemon poll interval in milliseconds (default: 1000)\n" +#endif +"\ + -r, --retries=N number of times to retry before switch to backup server (default: 5)\n\ + -R, --retry-pause=N time to pause between retries (default: 5)\n\ + --cpu-affinity set process affinity to CPU core(s), mask 0x3 for cores 0 and 1\n\ + --cpu-priority set process priority (0 idle, 2 normal to 5 highest)\n\ + --no-huge-pages disable huge pages support\n\ + --no-color disable colored output\n\ + --donate-level=N donate level, default 5%% (5 minutes in 100 minutes)\n\ + --user-agent set custom user-agent string for pool\n\ + -B, --background run the miner in the background\n\ + -c, --config=FILE load a JSON-format configuration file\n\ + -l, --log-file=FILE log all output to a file\n" +# ifdef HAVE_SYSLOG_H +"\ + -S, --syslog use system log for output messages\n" +# endif +"\ + --asm=ASM ASM optimizations, possible values: auto, none, intel, ryzen, bulldozer.\n\ + --print-time=N print hashrate report every N seconds\n" +#ifdef XMRIG_FEATURE_HTTP +"\ + --api-worker-id=ID custom worker-id for API\n\ + --api-id=ID custom instance ID for API\n\ + --http-enabled enable HTTP API\n\ + --http-host=HOST bind host for HTTP API (default: 127.0.0.1)\n\ + --http-port=N bind port for HTTP API\n\ + --http-access-token=T access token for HTTP API\n\ + --http-no-restricted enable full remote access to HTTP API (only if access token set)\n" +#endif +#ifdef XMRIG_ALGO_RANDOMX +"\ + --randomx-init=N threads count to initialize RandomX dataset\n\ + --randomx-no-numa disable NUMA support for RandomX\n" +#endif +#ifdef XMRIG_FEATURE_HWLOC +"\ + --export-topology export hwloc topology to a XML file and exit\n" +#endif +#ifdef XMRIG_FEATURE_CC_CLIENT +"\ + --cc-disabled disable CC Client feature\n\ + --cc-url=URL url of the CC Server\n\ + --cc-access-token=T access token for CC Server\n\ + --cc-worker-id=ID custom worker-id for CC Server\n\ + --cc-update-interval-s=N status update interval in seconds (default: 10 min: 1)\n\ + --cc-use-tls enable tls encryption for CC communication\n\ + --cc-use-remote-logging enable remote logging on CC Server\n\ + --cc-upload-config-on-start upload current miner config to CC Server on startup\n\ + --cc-reboot-cmd=CMD command/bat to execute to Reboot miner machine\n" +#endif +"\ + --dry-run test configuration and exit\n\ + -h, --help display this help and exit\n\ + -V, --version output version information and exit\n\ +"; + + +} /* namespace xmrig */ + +#endif /* XMRIG_USAGE_H */ diff --git a/src/crypto/Argon2_test.h b/src/crypto/Argon2_test.h deleted file mode 100644 index 8c3d9696..00000000 --- a/src/crypto/Argon2_test.h +++ /dev/null @@ -1,49 +0,0 @@ -/* XMRig - * Copyright 2019 BenDroid - * - * - * 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 . - */ - -#ifndef __ARGON2_TEST_H__ -#define __ARGON2_TEST_H__ - -#include - -const static uint8_t argon2_test_input[76] = { - 0x01, 0x00, 0xfb, 0x8e, 0x8a, 0xc8, 0x05, 0x89, 0x93, 0x23, - 0x37, 0x1b, 0xb7, 0x90, 0xdb, 0x19, 0x21, 0x8a, 0xfd, 0x8d, - 0xb8, 0xe3, 0x75, 0x5d, 0x8b, 0x90, 0xf3, 0x9b, 0x3d, 0x55, - 0x06, 0xa9, 0xab, 0xce, 0x4f, 0xa9, 0x12, 0x24, 0x45, 0x00, - 0x00, 0x00, 0x00, 0xee, 0x81, 0x46, 0xd4, 0x9f, 0xa9, 0x3e, - 0xe7, 0x24, 0xde, 0xb5, 0x7d, 0x12, 0xcb, 0xc6, 0xc6, 0xf3, - 0xb9, 0x24, 0xd9, 0x46, 0x12, 0x7c, 0x7a, 0x97, 0x41, 0x8f, - 0x93, 0x48, 0x82, 0x8f, 0x0f, 0x02 -}; - -const static uint8_t argon2_chukwa_test_out[32] = { - 0xc0, 0xda, 0xd0, 0xee, 0xb9, 0xc5, 0x2e, 0x92, 0xa1, 0xc3, - 0xaa, 0x5b, 0x76, 0xa3, 0xcb, 0x90, 0xbd, 0x73, 0x76, 0xc2, - 0x8d, 0xce, 0x19, 0x1c, 0xee, 0xb1, 0x09, 0x6e, 0x3a, 0x39, - 0x0d, 0x2e -}; - -const static uint8_t argon2_wrkz_test_out[32] = { - 0xB2, 0xFB, 0x90, 0x2B, 0xF4, 0x95, 0x99, 0x83, 0x9A, 0x61, - 0xCA, 0x28, 0xA4, 0xF9, 0x81, 0xD5, 0x49, 0x68, 0x8F, 0xCD, - 0x87, 0x59, 0xC4, 0x05, 0xE6, 0x79, 0xED, 0x9E, 0xF1, 0x36, - 0xD1, 0xB9, -}; - -#endif /* __ARGON2_TEST_H__ */ diff --git a/src/crypto/CryptoNight.h b/src/crypto/CryptoNight.h deleted file mode 100644 index d79eec7b..00000000 --- a/src/crypto/CryptoNight.h +++ /dev/null @@ -1,80 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * - * - * 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 . - */ - -#ifndef __CRYPTONIGHT_H__ -#define __CRYPTONIGHT_H__ - - -#include -#include - -#define ONE_MB 1048576 - -#define MEMORY 2097152 /* 2 MiB in bytes*/ -#define MEMORY_LITE 1048576 /* 1 MiB in bytes */ -#define MEMORY_SUPER_LITE 524288 /* 512 KiB in bytes */ -#define MEMORY_ULTRA_LITE 262144 /* 256 KiB in bytes */ -#define MEMORY_EXTREME_LITE 131072 /* 128 KiB in bytes */ -#define MEMORY_HEAVY 4194304 /* 4 MiB in bytes */ - -#define POW_DEFAULT_INDEX_SHIFT 3 -#define POW_XLT_V4_INDEX_SHIFT 4 - -#if defined _MSC_VER || defined XMRIG_ARM -#define ABI_ATTRIBUTE -#else -#define ABI_ATTRIBUTE __attribute__((ms_abi)) -#endif - -struct ScratchPad; -typedef void(*cn_mainloop_fun_ms_abi)(ScratchPad*) ABI_ATTRIBUTE; -typedef void(*cn_mainloop_double_fun_ms_abi)(ScratchPad*, ScratchPad*) ABI_ATTRIBUTE; - -struct cryptonight_r_data { - int variant; - uint64_t height; - - bool match(const int v, const uint64_t h) const { return (v == variant) && (h == height); } -}; - -struct ScratchPad { - alignas(16) uint8_t state[224]; - alignas(16) uint8_t* memory; - - // Additional stuff for asm impl - uint8_t ctx_info[24]; - const void* input; - uint8_t* variant_table; - const uint32_t* t_fn; - - cn_mainloop_fun_ms_abi generated_code; - cn_mainloop_double_fun_ms_abi generated_code_double; - cryptonight_r_data generated_code_data; - cryptonight_r_data generated_code_double_data; -}; - -alignas(64) static uint8_t variant1_table[256]; -alignas(64) static uint8_t variant_xtl_table[256]; - - -#endif /* __CRYPTONIGHT_H__ */ diff --git a/src/crypto/CryptoNight_arm.h b/src/crypto/CryptoNight_arm.h deleted file mode 100644 index cf2b55e1..00000000 --- a/src/crypto/CryptoNight_arm.h +++ /dev/null @@ -1,5534 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016 Imran Yusuff - * Copyright 2016-2017 XMRig - * Copyright 2018 Sebastian Stolzenberg - * Copyright 2018 BenDroid - * - * - * 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 . - */ - -#ifndef __CRYPTONIGHT_ARM_H__ -#define __CRYPTONIGHT_ARM_H__ - - -#if defined(XMRIG_ARM) && !defined(__clang__) -# include "aligned_malloc.h" -#else - -# include - -#endif - -#define SWAP32LE(x) x -#define SWAP64LE(x) x -#define hash_extra_blake(data, length, hash) blake256_hash((uint8_t*)(hash), (uint8_t*)(data), (length)) - -#ifndef NOINLINE -#ifdef __GNUC__ -#define NOINLINE __attribute__ ((noinline)) -#elif _MSC_VER -#define NOINLINE __declspec(noinline) -#else -#define NOINLINE -#endif -#endif - -#include -#include - -#include "crypto/CryptoNight.h" -#include "crypto/soft_aes.h" -#include "variant4_random_math.h" - - -extern "C" -{ -#include "crypto/c_keccak.h" -#include "crypto/c_groestl.h" -#include "crypto/c_blake256.h" -#include "crypto/c_jh.h" -#include "crypto/c_skein.h" -} - -static inline void do_blake_hash(const uint8_t* input, size_t len, uint8_t* output) -{ - blake256_hash(output, input, len); -} - - -static inline void do_groestl_hash(const uint8_t* input, size_t len, uint8_t* output) -{ - groestl(input, len * 8, output); -} - - -static inline void do_jh_hash(const uint8_t* input, size_t len, uint8_t* output) -{ - jh_hash(32 * 8, input, 8 * len, output); -} - - -static inline void do_skein_hash(const uint8_t* input, size_t len, uint8_t* output) -{ - xmr_skein(input, output); -} - - -void (* const extra_hashes[4])(const uint8_t*, size_t, uint8_t*) = {do_blake_hash, do_groestl_hash, do_jh_hash, - do_skein_hash}; - - -static inline __attribute__((always_inline)) __m128i _mm_set_epi64x(const uint64_t a, const uint64_t b) -{ - return vcombine_u64(vcreate_u64(b), vcreate_u64(a)); -} - -#if __ARM_FEATURE_CRYPTO -static inline __attribute__((always_inline)) __m128i _mm_aesenc_si128(__m128i v, __m128i rkey) -{ - alignas(16) const __m128i zero = { 0 }; - return veorq_u8(vaesmcq_u8(vaeseq_u8(v, zero)), rkey ); -} -#else - -static inline __attribute__((always_inline)) __m128i _mm_aesenc_si128(__m128i v, __m128i rkey) -{ - alignas(16) const __m128i zero = {0}; - return zero; -} - -#endif - -/* this one was not implemented yet so here it is */ -static inline __attribute__((always_inline)) uint64_t _mm_cvtsi128_si64(__m128i a) -{ - return vgetq_lane_u64(a, 0); -} - - -#define EXTRACT64(X) _mm_cvtsi128_si64(X) - - -# define SHUFFLE_PHASE_1(l, idx, bx0, bx1, ax, reverse) \ -{ \ - const uint64x2_t chunk1 = vld1q_u64((uint64_t*)((l) + ((idx) ^ (reverse ? 0x30 : 0x10)))); \ - const uint64x2_t chunk2 = vld1q_u64((uint64_t*)((l) + ((idx) ^ 0x20))); \ - const uint64x2_t chunk3 = vld1q_u64((uint64_t*)((l) + ((idx) ^ (reverse ? 0x10 : 0x30)))); \ - vst1q_u64((uint64_t*)((l) + ((idx) ^ 0x10)), vaddq_u64(chunk3, vreinterpretq_u64_u8(bx1))); \ - vst1q_u64((uint64_t*)((l) + ((idx) ^ 0x20)), vaddq_u64(chunk1, vreinterpretq_u64_u8(bx0))); \ - vst1q_u64((uint64_t*)((l) + ((idx) ^ 0x30)), vaddq_u64(chunk2, vreinterpretq_u64_u8(ax))); \ -} - -# define INTEGER_MATH_V2(idx, cl, cx) \ -{ \ - const uint64_t cx_0 = _mm_cvtsi128_si64(cx); \ - cl ^= division_result_xmm##idx ^ (sqrt_result##idx << 32); \ - const uint32_t d = static_cast(cx_0 + (sqrt_result##idx << 1)) | 0x80000001UL; \ - const uint64_t cx_1 = _mm_cvtsi128_si64(_mm_srli_si128(cx, 8)); \ - division_result_xmm##idx = static_cast(cx_1 / d) + ((cx_1 % d) << 32); \ - const uint64_t sqrt_input = cx_0 + division_result_xmm##idx; \ - sqrt_result##idx = sqrt(sqrt_input + 18446744073709551616.0) * 2.0 - 8589934592.0; \ - const uint64_t s = sqrt_result##idx >> 1; \ - const uint64_t b = sqrt_result##idx & 1; \ - const uint64_t r2 = (uint64_t)(s) * (s + b) + (sqrt_result##idx << 32); \ - sqrt_result##idx += ((r2 + b > sqrt_input) ? -1 : 0) + ((r2 + (1ULL << 32) < sqrt_input - s) ? 1 : 0); \ -} - -# define SHUFFLE_PHASE_2(l, idx, bx0, bx1, ax, lo, hi, reverse) \ -{ \ - const uint64x2_t chunk1 = veorq_u64(vld1q_u64((uint64_t*)((l) + ((idx) ^ 0x10))), vcombine_u64(vcreate_u64(hi), vcreate_u64(lo))); \ - const uint64x2_t chunk2 = vld1q_u64((uint64_t*)((l) + ((idx) ^ 0x20))); \ - const uint64x2_t chunk3 = vld1q_u64((uint64_t*)((l) + ((idx) ^ 0x30))); \ - hi ^= ((uint64_t*)((l) + ((idx) ^ 0x20)))[0]; \ - lo ^= ((uint64_t*)((l) + ((idx) ^ 0x20)))[1]; \ - if (reverse) { \ - vst1q_u64((uint64_t*)((l) + ((idx) ^ 0x10)), vaddq_u64(chunk1, vreinterpretq_u64_u8(bx1))); \ - vst1q_u64((uint64_t*)((l) + ((idx) ^ 0x20)), vaddq_u64(chunk3, vreinterpretq_u64_u8(bx0))); \ - } else { \ - vst1q_u64((uint64_t*)((l) + ((idx) ^ 0x10)), vaddq_u64(chunk3, vreinterpretq_u64_u8(bx1))); \ - vst1q_u64((uint64_t*)((l) + ((idx) ^ 0x20)), vaddq_u64(chunk1, vreinterpretq_u64_u8(bx0))); \ - } \ - vst1q_u64((uint64_t*)((l) + ((idx) ^ 0x30)), vaddq_u64(chunk2, vreinterpretq_u64_u8(ax))); \ -} - -# define SHUFFLE_V4(l, idx, bx0, bx1, ax, cx) \ -{ \ - const uint64x2_t chunk1 = vld1q_u64((uint64_t*)((l) + ((idx) ^ 0x10))); \ - const uint64x2_t chunk2 = vld1q_u64((uint64_t*)((l) + ((idx) ^ 0x20))); \ - const uint64x2_t chunk3 = vld1q_u64((uint64_t*)((l) + ((idx) ^ 0x30))); \ - vst1q_u64((uint64_t*)((l) + ((idx) ^ 0x10)), vaddq_u64(chunk3, vreinterpretq_u64_u8(bx1))); \ - vst1q_u64((uint64_t*)((l) + ((idx) ^ 0x20)), vaddq_u64(chunk1, vreinterpretq_u64_u8(bx0))); \ - vst1q_u64((uint64_t*)((l) + ((idx) ^ 0x30)), vaddq_u64(chunk2, vreinterpretq_u64_u8(ax))); \ - cx = veorq_u64(veorq_u64(cx, chunk3), veorq_u64(chunk1, chunk2)); \ -} - -# define VARIANT4_RANDOM_MATH_INIT(idx, h) \ - uint32_t r##idx[9]; \ - struct V4_Instruction code##idx[256]; \ - r##idx[0] = (uint32_t)(h[12]); \ - r##idx[1] = (uint32_t)(h[12] >> 32); \ - r##idx[2] = (uint32_t)(h[13]); \ - r##idx[3] = (uint32_t)(h[13] >> 32); \ - v4_random_math_init(code##idx, VARIANT, height); - -# define VARIANT4_RANDOM_MATH(idx, al, ah, cl, bx0, bx1) \ - cl ^= (r##idx[0] + r##idx[1]) | ((uint64_t)(r##idx[2] + r##idx[3]) << 32); \ - r##idx[4] = static_cast(al); \ - r##idx[5] = static_cast(ah); \ - r##idx[6] = static_cast(_mm_cvtsi128_si32(bx0)); \ - r##idx[7] = static_cast(_mm_cvtsi128_si32(bx1)); \ - r##idx[8] = static_cast(_mm_cvtsi128_si32(_mm_srli_si128(bx1, 8))); \ - v4_random_math(code##idx, r##idx); \ - - -#if defined (__arm64__) || defined (__aarch64__) -static inline uint64_t __umul128(uint64_t a, uint64_t b, uint64_t* hi) -{ - unsigned __int128 r = (unsigned __int128) a * (unsigned __int128) b; - *hi = r >> 64; - return (uint64_t) r; -} -#else - -static inline uint64_t __umul128(uint64_t multiplier, uint64_t multiplicand, uint64_t* product_hi) -{ - uint64_t a = multiplier >> 32; - uint64_t b = multiplier & 0xFFFFFFFF; - uint64_t c = multiplicand >> 32; - uint64_t d = multiplicand & 0xFFFFFFFF; - - uint64_t ad = a * d; - uint64_t bd = b * d; - - uint64_t adbc = ad + (b * c); - uint64_t adbc_carry = adbc < ad ? 1 : 0; - - uint64_t product_lo = bd + (adbc << 32); - uint64_t product_lo_carry = product_lo < bd ? 1 : 0; - *product_hi = (a * c) + (adbc >> 32) + (adbc_carry << 32) + product_lo_carry; - - return product_lo; -} - -#endif - - -// This will shift and xor tmp1 into itself as 4 32-bit vals such as -// sl_xor(a1 a2 a3 a4) = a1 (a2^a1) (a3^a2^a1) (a4^a3^a2^a1) -static inline __m128i sl_xor(__m128i tmp1) -{ - __m128i tmp4; - tmp4 = _mm_slli_si128(tmp1, 0x04); - tmp1 = _mm_xor_si128(tmp1, tmp4); - tmp4 = _mm_slli_si128(tmp4, 0x04); - tmp1 = _mm_xor_si128(tmp1, tmp4); - tmp4 = _mm_slli_si128(tmp4, 0x04); - tmp1 = _mm_xor_si128(tmp1, tmp4); - return tmp1; -} - - -template -static inline void soft_aes_genkey_sub(__m128i* xout0, __m128i* xout2) -{ - __m128i xout1 = soft_aeskeygenassist(*xout2); - xout1 = _mm_shuffle_epi32(xout1, 0xFF); // see PSHUFD, set all elems to 4th elem - *xout0 = sl_xor(*xout0); - *xout0 = _mm_xor_si128(*xout0, xout1); - xout1 = soft_aeskeygenassist<0x00>(*xout0); - xout1 = _mm_shuffle_epi32(xout1, 0xAA); // see PSHUFD, set all elems to 3rd elem - *xout2 = sl_xor(*xout2); - *xout2 = _mm_xor_si128(*xout2, xout1); -} - - -template -static inline void -aes_genkey(const __m128i* memory, __m128i* k0, __m128i* k1, __m128i* k2, __m128i* k3, __m128i* k4, __m128i* k5, - __m128i* k6, __m128i* k7, __m128i* k8, __m128i* k9) -{ - __m128i xout0 = _mm_load_si128(memory); - __m128i xout2 = _mm_load_si128(memory + 1); - *k0 = xout0; - *k1 = xout2; - - soft_aes_genkey_sub<0x01>(&xout0, &xout2); - *k2 = xout0; - *k3 = xout2; - - soft_aes_genkey_sub<0x02>(&xout0, &xout2); - *k4 = xout0; - *k5 = xout2; - - soft_aes_genkey_sub<0x04>(&xout0, &xout2); - *k6 = xout0; - *k7 = xout2; - - soft_aes_genkey_sub<0x08>(&xout0, &xout2); - *k8 = xout0; - *k9 = xout2; -} - - -template -static inline void -aes_round(__m128i key, __m128i* x0, __m128i* x1, __m128i* x2, __m128i* x3, __m128i* x4, __m128i* x5, __m128i* x6, - __m128i* x7) -{ - if (SOFT_AES) { - *x0 = soft_aesenc((uint32_t*)x0, key); - *x1 = soft_aesenc((uint32_t*)x1, key); - *x2 = soft_aesenc((uint32_t*)x2, key); - *x3 = soft_aesenc((uint32_t*)x3, key); - *x4 = soft_aesenc((uint32_t*)x4, key); - *x5 = soft_aesenc((uint32_t*)x5, key); - *x6 = soft_aesenc((uint32_t*)x6, key); - *x7 = soft_aesenc((uint32_t*)x7, key); - } -# ifndef XMRIG_ARMv7 - else { - *x0 = vaesmcq_u8(vaeseq_u8(*((uint8x16_t *) x0), key)); - *x1 = vaesmcq_u8(vaeseq_u8(*((uint8x16_t *) x1), key)); - *x2 = vaesmcq_u8(vaeseq_u8(*((uint8x16_t *) x2), key)); - *x3 = vaesmcq_u8(vaeseq_u8(*((uint8x16_t *) x3), key)); - *x4 = vaesmcq_u8(vaeseq_u8(*((uint8x16_t *) x4), key)); - *x5 = vaesmcq_u8(vaeseq_u8(*((uint8x16_t *) x5), key)); - *x6 = vaesmcq_u8(vaeseq_u8(*((uint8x16_t *) x6), key)); - *x7 = vaesmcq_u8(vaeseq_u8(*((uint8x16_t *) x7), key)); - } -# else - else { - *x0 = _mm_aesenc_si128(*x0, key); - *x1 = _mm_aesenc_si128(*x1, key); - *x2 = _mm_aesenc_si128(*x2, key); - *x3 = _mm_aesenc_si128(*x3, key); - *x4 = _mm_aesenc_si128(*x4, key); - *x5 = _mm_aesenc_si128(*x5, key); - *x6 = _mm_aesenc_si128(*x6, key); - *x7 = _mm_aesenc_si128(*x7, key); - } -# endif -} - - -inline void mix_and_propagate(__m128i& x0, __m128i& x1, __m128i& x2, __m128i& x3, __m128i& x4, __m128i& x5, __m128i& x6, - __m128i& x7) -{ - __m128i tmp0 = x0; - x0 = _mm_xor_si128(x0, x1); - x1 = _mm_xor_si128(x1, x2); - x2 = _mm_xor_si128(x2, x3); - x3 = _mm_xor_si128(x3, x4); - x4 = _mm_xor_si128(x4, x5); - x5 = _mm_xor_si128(x5, x6); - x6 = _mm_xor_si128(x6, x7); - x7 = _mm_xor_si128(x7, tmp0); -} - -template -static inline void cn_explode_scratchpad(const __m128i* input, __m128i* output) -{ - __m128i xin0, xin1, xin2, xin3, xin4, xin5, xin6, xin7; - __m128i k0, k1, k2, k3, k4, k5, k6, k7, k8, k9; - - aes_genkey(input, &k0, &k1, &k2, &k3, &k4, &k5, &k6, &k7, &k8, &k9); - - xin0 = _mm_load_si128(input + 4); - xin1 = _mm_load_si128(input + 5); - xin2 = _mm_load_si128(input + 6); - xin3 = _mm_load_si128(input + 7); - xin4 = _mm_load_si128(input + 8); - xin5 = _mm_load_si128(input + 9); - xin6 = _mm_load_si128(input + 10); - xin7 = _mm_load_si128(input + 11); - - for (size_t i = 0; i < MEM / sizeof(__m128i); i += 8) { - if (!SOFT_AES) { - aes_round(_mm_setzero_si128(), &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - } - - aes_round(k0, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k1, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k2, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k3, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k4, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k5, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k6, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k7, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k8, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - - if (!SOFT_AES) { - xin0 ^= k9; - xin1 ^= k9; - xin2 ^= k9; - xin3 ^= k9; - xin4 ^= k9; - xin5 ^= k9; - xin6 ^= k9; - xin7 ^= k9; - } else { - aes_round(k9, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - } - - _mm_store_si128(output + i + 0, xin0); - _mm_store_si128(output + i + 1, xin1); - _mm_store_si128(output + i + 2, xin2); - _mm_store_si128(output + i + 3, xin3); - _mm_store_si128(output + i + 4, xin4); - _mm_store_si128(output + i + 5, xin5); - _mm_store_si128(output + i + 6, xin6); - _mm_store_si128(output + i + 7, xin7); - } -} - -template -static inline void cn_explode_scratchpad_heavy(const __m128i* input, __m128i* output) -{ - __m128i xin0, xin1, xin2, xin3, xin4, xin5, xin6, xin7; - __m128i k0, k1, k2, k3, k4, k5, k6, k7, k8, k9; - - aes_genkey(input, &k0, &k1, &k2, &k3, &k4, &k5, &k6, &k7, &k8, &k9); - - xin0 = _mm_load_si128(input + 4); - xin1 = _mm_load_si128(input + 5); - xin2 = _mm_load_si128(input + 6); - xin3 = _mm_load_si128(input + 7); - xin4 = _mm_load_si128(input + 8); - xin5 = _mm_load_si128(input + 9); - xin6 = _mm_load_si128(input + 10); - xin7 = _mm_load_si128(input + 11); - - for (size_t i = 0; i < 16; i++) { - - if (!SOFT_AES) { - aes_round(_mm_setzero_si128(), &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - } - - aes_round(k0, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k1, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k2, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k3, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k4, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k5, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k6, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k7, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k8, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - - if (!SOFT_AES) { - xin0 ^= k9; - xin1 ^= k9; - xin2 ^= k9; - xin3 ^= k9; - xin4 ^= k9; - xin5 ^= k9; - xin6 ^= k9; - xin7 ^= k9; - } else { - aes_round(k9, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - } - - mix_and_propagate(xin0, xin1, xin2, xin3, xin4, xin5, xin6, xin7); - } - - for (size_t i = 0; i < MEM / sizeof(__m128i); i += 8) { - if (!SOFT_AES) { - aes_round(_mm_setzero_si128(), &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - } - - aes_round(k0, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k1, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k2, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k3, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k4, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k5, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k6, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k7, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k8, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - - if (!SOFT_AES) { - xin0 ^= k9; - xin1 ^= k9; - xin2 ^= k9; - xin3 ^= k9; - xin4 ^= k9; - xin5 ^= k9; - xin6 ^= k9; - xin7 ^= k9; - } else { - aes_round(k9, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - } - - _mm_store_si128(output + i + 0, xin0); - _mm_store_si128(output + i + 1, xin1); - _mm_store_si128(output + i + 2, xin2); - _mm_store_si128(output + i + 3, xin3); - _mm_store_si128(output + i + 4, xin4); - _mm_store_si128(output + i + 5, xin5); - _mm_store_si128(output + i + 6, xin6); - _mm_store_si128(output + i + 7, xin7); - } -} - -template -static inline void cn_implode_scratchpad(const __m128i* input, __m128i* output) -{ - __m128i xout0, xout1, xout2, xout3, xout4, xout5, xout6, xout7; - __m128i k0, k1, k2, k3, k4, k5, k6, k7, k8, k9; - - aes_genkey(output + 2, &k0, &k1, &k2, &k3, &k4, &k5, &k6, &k7, &k8, &k9); - - xout0 = _mm_load_si128(output + 4); - xout1 = _mm_load_si128(output + 5); - xout2 = _mm_load_si128(output + 6); - xout3 = _mm_load_si128(output + 7); - xout4 = _mm_load_si128(output + 8); - xout5 = _mm_load_si128(output + 9); - xout6 = _mm_load_si128(output + 10); - xout7 = _mm_load_si128(output + 11); - - for (size_t i = 0; i < MEM / sizeof(__m128i); i += 8) { - xout0 = _mm_xor_si128(_mm_load_si128(input + i + 0), xout0); - xout1 = _mm_xor_si128(_mm_load_si128(input + i + 1), xout1); - xout2 = _mm_xor_si128(_mm_load_si128(input + i + 2), xout2); - xout3 = _mm_xor_si128(_mm_load_si128(input + i + 3), xout3); - xout4 = _mm_xor_si128(_mm_load_si128(input + i + 4), xout4); - xout5 = _mm_xor_si128(_mm_load_si128(input + i + 5), xout5); - xout6 = _mm_xor_si128(_mm_load_si128(input + i + 6), xout6); - xout7 = _mm_xor_si128(_mm_load_si128(input + i + 7), xout7); - - if (!SOFT_AES) { - aes_round(_mm_setzero_si128(), &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - } - - aes_round(k0, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k1, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k2, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k3, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k4, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k5, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k6, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k7, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k8, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - - if (!SOFT_AES) { - xout0 ^= k9; - xout1 ^= k9; - xout2 ^= k9; - xout3 ^= k9; - xout4 ^= k9; - xout5 ^= k9; - xout6 ^= k9; - xout7 ^= k9; - } else { - aes_round(k9, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - } - } - - _mm_store_si128(output + 4, xout0); - _mm_store_si128(output + 5, xout1); - _mm_store_si128(output + 6, xout2); - _mm_store_si128(output + 7, xout3); - _mm_store_si128(output + 8, xout4); - _mm_store_si128(output + 9, xout5); - _mm_store_si128(output + 10, xout6); - _mm_store_si128(output + 11, xout7); -} - -template -static inline void cn_implode_scratchpad_heavy(const __m128i* input, __m128i* output) -{ - __m128i xout0, xout1, xout2, xout3, xout4, xout5, xout6, xout7; - __m128i k0, k1, k2, k3, k4, k5, k6, k7, k8, k9; - - aes_genkey(output + 2, &k0, &k1, &k2, &k3, &k4, &k5, &k6, &k7, &k8, &k9); - - xout0 = _mm_load_si128(output + 4); - xout1 = _mm_load_si128(output + 5); - xout2 = _mm_load_si128(output + 6); - xout3 = _mm_load_si128(output + 7); - xout4 = _mm_load_si128(output + 8); - xout5 = _mm_load_si128(output + 9); - xout6 = _mm_load_si128(output + 10); - xout7 = _mm_load_si128(output + 11); - - for (size_t i = 0; i < MEM / sizeof(__m128i); i += 8) { - xout0 = _mm_xor_si128(_mm_load_si128(input + i + 0), xout0); - xout1 = _mm_xor_si128(_mm_load_si128(input + i + 1), xout1); - xout2 = _mm_xor_si128(_mm_load_si128(input + i + 2), xout2); - xout3 = _mm_xor_si128(_mm_load_si128(input + i + 3), xout3); - xout4 = _mm_xor_si128(_mm_load_si128(input + i + 4), xout4); - xout5 = _mm_xor_si128(_mm_load_si128(input + i + 5), xout5); - xout6 = _mm_xor_si128(_mm_load_si128(input + i + 6), xout6); - xout7 = _mm_xor_si128(_mm_load_si128(input + i + 7), xout7); - - if (!SOFT_AES) { - aes_round(_mm_setzero_si128(), &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - } - - aes_round(k0, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k1, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k2, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k3, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k4, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k5, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k6, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k7, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k8, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - - if (!SOFT_AES) { - xout0 ^= k9; - xout1 ^= k9; - xout2 ^= k9; - xout3 ^= k9; - xout4 ^= k9; - xout5 ^= k9; - xout6 ^= k9; - xout7 ^= k9; - } else { - aes_round(k9, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - } - - mix_and_propagate(xout0, xout1, xout2, xout3, xout4, xout5, xout6, xout7); - } - - for (size_t i = 0; i < MEM / sizeof(__m128i); i += 8) { - xout0 = _mm_xor_si128(_mm_load_si128(input + i + 0), xout0); - xout1 = _mm_xor_si128(_mm_load_si128(input + i + 1), xout1); - xout2 = _mm_xor_si128(_mm_load_si128(input + i + 2), xout2); - xout3 = _mm_xor_si128(_mm_load_si128(input + i + 3), xout3); - xout4 = _mm_xor_si128(_mm_load_si128(input + i + 4), xout4); - xout5 = _mm_xor_si128(_mm_load_si128(input + i + 5), xout5); - xout6 = _mm_xor_si128(_mm_load_si128(input + i + 6), xout6); - xout7 = _mm_xor_si128(_mm_load_si128(input + i + 7), xout7); - - if (!SOFT_AES) { - aes_round(_mm_setzero_si128(), &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - } - - aes_round(k0, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k1, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k2, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k3, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k4, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k5, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k6, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k7, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k8, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - - if (!SOFT_AES) { - xout0 ^= k9; - xout1 ^= k9; - xout2 ^= k9; - xout3 ^= k9; - xout4 ^= k9; - xout5 ^= k9; - xout6 ^= k9; - xout7 ^= k9; - } else { - aes_round(k9, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - } - - mix_and_propagate(xout0, xout1, xout2, xout3, xout4, xout5, xout6, xout7); - } - - for (size_t i = 0; i < 16; i++) { - if (!SOFT_AES) { - aes_round(_mm_setzero_si128(), &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - } - - aes_round(k0, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k1, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k2, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k3, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k4, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k5, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k6, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k7, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k8, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - - if (!SOFT_AES) { - xout0 ^= k9; - xout1 ^= k9; - xout2 ^= k9; - xout3 ^= k9; - xout4 ^= k9; - xout5 ^= k9; - xout6 ^= k9; - xout7 ^= k9; - } else { - aes_round(k9, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - } - - mix_and_propagate(xout0, xout1, xout2, xout3, xout4, xout5, xout6, xout7); - } - - _mm_store_si128(output + 4, xout0); - _mm_store_si128(output + 5, xout1); - _mm_store_si128(output + 6, xout2); - _mm_store_si128(output + 7, xout3); - _mm_store_si128(output + 8, xout4); - _mm_store_si128(output + 9, xout5); - _mm_store_si128(output + 10, xout6); - _mm_store_si128(output + 11, xout7); -} - -template -class CryptoNightMultiHash -{ -public: - inline static void hash(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - //dummy - } - - inline static void hashPowV2(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - //dummy - } - - inline static void hashPowV3(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - //dummy - } - - inline static void hashPowV4(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad, - uint64_t height) - { - // dummy - } - - inline static void hashLiteTube(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - //dummy - } - - inline static void hashHeavy(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - //dummy - } - - inline static void hashHeavyTube(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - //dummy - } -}; - -template -class CryptoNightMultiHash -{ -public: - inline static void hash(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - const uint8_t* l; - uint64_t* h; - uint64_t al; - uint64_t ah; - __m128i bx; - uint64_t idx; - - keccak(static_cast(input), (int) size, scratchPad[0]->state, 200); - - l = scratchPad[0]->memory; - h = reinterpret_cast(scratchPad[0]->state); - - cn_explode_scratchpad((__m128i*) h, (__m128i*) l); - - al = h[0] ^ h[4]; - ah = h[1] ^ h[5]; - bx = _mm_set_epi64x(h[3] ^ h[7], h[2] ^ h[6]); - idx = h[0] ^ h[4]; - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx; - - if (SOFT_AES) { - cx = soft_aesenc((uint32_t*) &l[idx & MASK], _mm_set_epi64x(ah, al)); - } else { - cx = _mm_load_si128((__m128i*) &l[idx & MASK]); - cx = _mm_aesenc_si128(cx, _mm_set_epi64x(ah, al)); - } - - _mm_store_si128((__m128i*) &l[idx & MASK], _mm_xor_si128(bx, cx)); - idx = EXTRACT64(cx); - bx = cx; - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l[idx & MASK])[0]; - ch = ((uint64_t*) &l[idx & MASK])[1]; - lo = __umul128(idx, cl, &hi); - - al += hi; - ah += lo; - - ((uint64_t*) &l[idx & MASK])[0] = al; - ((uint64_t*) &l[idx & MASK])[1] = ah; - - ah ^= ch; - al ^= cl; - idx = al; - } - - cn_implode_scratchpad((__m128i*) l, (__m128i*) h); - keccakf(h, 24); - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - } - - inline static void hashPowV2(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - const uint8_t* l; - uint64_t* h; - uint64_t al; - uint64_t ah; - __m128i bx; - uint64_t idx; - - keccak(static_cast(input), (int) size, scratchPad[0]->state, 200); - - uint64_t tweak1_2 = (*reinterpret_cast(input + 35) ^ - *(reinterpret_cast(scratchPad[0]->state) + 24)); - l = scratchPad[0]->memory; - h = reinterpret_cast(scratchPad[0]->state); - - cn_explode_scratchpad((__m128i*) h, (__m128i*) l); - - al = h[0] ^ h[4]; - ah = h[1] ^ h[5]; - bx = _mm_set_epi64x(h[3] ^ h[7], h[2] ^ h[6]); - idx = h[0] ^ h[4]; - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx; - - if (SOFT_AES) { - cx = soft_aesenc((uint32_t*) &l[idx & MASK], _mm_set_epi64x(ah, al)); - } else { - cx = _mm_load_si128((__m128i*) &l[idx & MASK]); - cx = _mm_aesenc_si128(cx, _mm_set_epi64x(ah, al)); - } - - _mm_store_si128((__m128i*) &l[idx & MASK], _mm_xor_si128(bx, cx)); - const uint8_t tmp = reinterpret_cast(&l[idx & MASK])[11]; - static const uint32_t table = 0x75310; - const uint8_t index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*) (&l[idx & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - idx = EXTRACT64(cx); - bx = cx; - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l[idx & MASK])[0]; - ch = ((uint64_t*) &l[idx & MASK])[1]; - lo = __umul128(idx, cl, &hi); - - al += hi; - ah += lo; - - ah ^= tweak1_2; - ((uint64_t*) &l[idx & MASK])[0] = al; - ((uint64_t*) &l[idx & MASK])[1] = ah; - ah ^= tweak1_2; - - ah ^= ch; - al ^= cl; - idx = al; - } - - cn_implode_scratchpad((__m128i*) l, (__m128i*) h); - keccakf(h, 24); - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - } - - // single - inline static void hashPowV3(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - keccak(input, (int) size, scratchPad[0]->state, 200); - - const uint8_t* l0 = scratchPad[0]->memory; - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - - cn_explode_scratchpad((__m128i*) h0, (__m128i*) l0); - - uint64_t al0 = h0[0] ^h0[4]; - uint64_t ah0 = h0[1] ^h0[5]; - - __m128i bx00 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx10 = _mm_set_epi64x(h0[9] ^ h0[11], h0[8] ^ h0[10]); - - uint64_t idx0 = h0[0] ^h0[4]; - uint64_t division_result_xmm0 = h0[12]; - uint64_t sqrt_result0 = h0[13]; - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx0; - const __m128i ax0 = _mm_set_epi64x(ah0, al0); - - if (SOFT_AES) { - cx0 = soft_aesenc((uint32_t*) &l0[idx0 & MASK], ax0); - } else { - cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - cx0 = _mm_aesenc_si128(cx0, _mm_set_epi64x(ah0, al0)); - } - - SHUFFLE_PHASE_1(l0, (idx0&MASK), bx00, bx10, ax0, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - - _mm_store_si128((__m128i*) &l0[idx0 & MASK], _mm_xor_si128(bx00, cx0)); - - idx0 = EXTRACT64(cx0); - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & MASK])[0]; - ch = ((uint64_t*) &l0[idx0 & MASK])[1]; - - INTEGER_MATH_V2(0, cl, cx0); - - lo = __umul128(idx0, cl, &hi); - - SHUFFLE_PHASE_2(l0, (idx0&MASK), bx00, bx10, ax0, lo, hi, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - - al0 += hi; - ah0 += lo; - - ((uint64_t*) &l0[idx0 & MASK])[0] = al0; - ((uint64_t*) &l0[idx0 & MASK])[1] = ah0; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - bx10 = bx00; - bx00 = cx0; - } - - cn_implode_scratchpad((__m128i*) l0, (__m128i*) h0); - - keccakf(h0, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - } - - // single - inline static void hashPowV4(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad, - uint64_t height) - - { - keccak(input, (int) size, scratchPad[0]->state, 200); - - const uint8_t* l0 = scratchPad[0]->memory; - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - - cn_explode_scratchpad((__m128i*) h0, (__m128i*) l0); - - uint64_t al0 = h0[0] ^h0[4]; - uint64_t ah0 = h0[1] ^h0[5]; - - __m128i bx00 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx10 = _mm_set_epi64x(h0[9] ^ h0[11], h0[8] ^ h0[10]); - - uint64_t idx0 = h0[0] ^h0[4]; - - VARIANT4_RANDOM_MATH_INIT(0, h0) - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx0; - const __m128i ax0 = _mm_set_epi64x(ah0, al0); - - if (SOFT_AES) { - cx0 = soft_aesenc((uint32_t*) &l0[idx0 & MASK], ax0); - } else { - cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - cx0 = _mm_aesenc_si128(cx0, _mm_set_epi64x(ah0, al0)); - } - - SHUFFLE_V4(l0, (idx0&MASK), bx00, bx10, ax0, cx0) - - _mm_store_si128((__m128i*) &l0[idx0 & MASK], _mm_xor_si128(bx00, cx0)); - - idx0 = EXTRACT64(cx0); - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & MASK])[0]; - ch = ((uint64_t*) &l0[idx0 & MASK])[1]; - - VARIANT4_RANDOM_MATH(0, al0, ah0, cl, bx00, bx10) - - if (VARIANT == POW_V4) { - al0 ^= r0[2] | ((uint64_t)(r0[3]) << 32); - ah0 ^= r0[0] | ((uint64_t)(r0[1]) << 32); - } - - lo = __umul128(idx0, cl, &hi); - - SHUFFLE_V4(l0, (idx0&MASK), bx00, bx10, ax0, cx0); - - al0 += hi; - ah0 += lo; - - ((uint64_t*) &l0[idx0 & MASK])[0] = al0; - ((uint64_t*) &l0[idx0 & MASK])[1] = ah0; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - bx10 = bx00; - bx00 = cx0; - } - - cn_implode_scratchpad((__m128i*) l0, (__m128i*) h0); - - keccakf(h0, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - } - - inline static void hashLiteTube(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - const uint8_t* l; - uint64_t* h; - uint64_t al; - uint64_t ah; - __m128i bx; - uint64_t idx; - - keccak(static_cast(input), (int) size, scratchPad[0]->state, 200); - - uint64_t tweak1_2 = (*reinterpret_cast(input + 35) ^ - *(reinterpret_cast(scratchPad[0]->state) + 24)); - l = scratchPad[0]->memory; - h = reinterpret_cast(scratchPad[0]->state); - - cn_explode_scratchpad((__m128i*) h, (__m128i*) l); - - al = h[0] ^ h[4]; - ah = h[1] ^ h[5]; - bx = _mm_set_epi64x(h[3] ^ h[7], h[2] ^ h[6]); - idx = h[0] ^ h[4]; - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx; - - if (SOFT_AES) { - cx = soft_aesenc((uint32_t*) &l[idx & MASK], _mm_set_epi64x(ah, al)); - } else { - cx = _mm_load_si128((__m128i*) &l[idx & MASK]); - cx = _mm_aesenc_si128(cx, _mm_set_epi64x(ah, al)); - } - - _mm_store_si128((__m128i*) &l[idx & MASK], _mm_xor_si128(bx, cx)); - const uint8_t tmp = reinterpret_cast(&l[idx & MASK])[11]; - static const uint32_t table = 0x75310; - const uint8_t index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*) (&l[idx & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - idx = EXTRACT64(cx); - bx = cx; - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l[idx & MASK])[0]; - ch = ((uint64_t*) &l[idx & MASK])[1]; - lo = __umul128(idx, cl, &hi); - - al += hi; - ah += lo; - - ah ^= tweak1_2; - ((uint64_t*) &l[idx & MASK])[0] = al; - ((uint64_t*) &l[idx & MASK])[1] = ah; - ah ^= tweak1_2; - - ((uint64_t*) &l[idx & MASK])[1] ^= ((uint64_t*) &l[idx & MASK])[0]; - - ah ^= ch; - al ^= cl; - idx = al; - } - - cn_implode_scratchpad((__m128i*) l, (__m128i*) h); - keccakf(h, 24); - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - } - - inline static void hashHeavy(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - const uint8_t* l; - uint64_t* h; - uint64_t al; - uint64_t ah; - __m128i bx; - uint64_t idx; - - keccak(static_cast(input), (int) size, scratchPad[0]->state, 200); - - cn_explode_scratchpad_heavy((__m128i*) scratchPad[0]->state, (__m128i*) scratchPad[0]->memory); - - l = scratchPad[0]->memory; - h = reinterpret_cast(scratchPad[0]->state); - - al = h[0] ^ h[4]; - ah = h[1] ^ h[5]; - bx = _mm_set_epi64x(h[3] ^ h[7], h[2] ^ h[6]); - idx = h[0] ^ h[4]; - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx; - - if (SOFT_AES) { - cx = soft_aesenc((uint32_t*) &l[idx & MASK], _mm_set_epi64x(ah, al)); - } else { - cx = _mm_load_si128((__m128i*) &l[idx & MASK]); - cx = _mm_aesenc_si128(cx, _mm_set_epi64x(ah, al)); - } - - _mm_store_si128((__m128i*) &l[idx & MASK], _mm_xor_si128(bx, cx)); - idx = EXTRACT64(cx); - bx = cx; - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l[idx & MASK])[0]; - ch = ((uint64_t*) &l[idx & MASK])[1]; - lo = __umul128(idx, cl, &hi); - - al += hi; - ah += lo; - - ((uint64_t*) &l[idx & MASK])[0] = al; - ((uint64_t*) &l[idx & MASK])[1] = ah; - - ah ^= ch; - al ^= cl; - idx = al; - - const int64x2_t x = vld1q_s64(reinterpret_cast(&l[idx & MASK])); - const int64_t n = vgetq_lane_s64(x, 0); - const int32_t d = vgetq_lane_s32(x, 2); - const int64_t q = n / (d | 0x5); - - ((int64_t*) &l[idx & MASK])[0] = n ^ q; - - if (VARIANT == POW_XHV || VARIANT == POW_XFH) { - idx = (~d) ^ q; - } else { - idx = d ^ q; - } - } - - cn_implode_scratchpad_heavy((__m128i*) scratchPad[0]->memory, (__m128i*) scratchPad[0]->state); - keccakf(h, 24); - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - } - - inline static void hashHeavyTube(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - const uint8_t* l; - uint64_t* h; - uint64_t al; - uint64_t ah; - __m128i bx; - uint64_t idx; - - keccak(static_cast(input), (int) size, scratchPad[0]->state, 200); - - uint64_t tweak1_2 = (*reinterpret_cast(reinterpret_cast(input) + 35) ^ - *(reinterpret_cast(scratchPad[0]->state) + 24)); - - l = scratchPad[0]->memory; - h = reinterpret_cast(scratchPad[0]->state); - - cn_explode_scratchpad_heavy((__m128i*) h, (__m128i*) l); - - al = h[0] ^ h[4]; - ah = h[1] ^ h[5]; - bx = _mm_set_epi64x(h[3] ^ h[7], h[2] ^ h[6]); - idx = h[0] ^ h[4]; - - union alignas(16) - { - uint32_t k[4]; - uint64_t v64[2]; - }; - alignas(16) uint32_t x[4]; - -#define BYTE(p, i) ((unsigned char*)&p)[i] - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx = _mm_load_si128((__m128i*) &l[idx & MASK]); - - const __m128i& key = _mm_set_epi64x(ah, al); - - _mm_store_si128((__m128i*) k, key); - cx = _mm_xor_si128(cx, _mm_cmpeq_epi32(_mm_setzero_si128(), _mm_setzero_si128())); - _mm_store_si128((__m128i*) x, cx); - - k[0] ^= saes_table[0][BYTE(x[0], 0)] ^ saes_table[1][BYTE(x[1], 1)] ^ saes_table[2][BYTE(x[2], 2)] ^ - saes_table[3][BYTE(x[3], 3)]; - x[0] ^= k[0]; - k[1] ^= saes_table[0][BYTE(x[1], 0)] ^ saes_table[1][BYTE(x[2], 1)] ^ saes_table[2][BYTE(x[3], 2)] ^ - saes_table[3][BYTE(x[0], 3)]; - x[1] ^= k[1]; - k[2] ^= saes_table[0][BYTE(x[2], 0)] ^ saes_table[1][BYTE(x[3], 1)] ^ saes_table[2][BYTE(x[0], 2)] ^ - saes_table[3][BYTE(x[1], 3)]; - x[2] ^= k[2]; - k[3] ^= saes_table[0][BYTE(x[3], 0)] ^ saes_table[1][BYTE(x[0], 1)] ^ saes_table[2][BYTE(x[1], 2)] ^ - saes_table[3][BYTE(x[2], 3)]; - - cx = _mm_load_si128((__m128i*) k); - - _mm_store_si128((__m128i*) &l[idx & MASK], _mm_xor_si128(bx, cx)); - const uint8_t tmp = reinterpret_cast(&l[idx & MASK])[11]; - static const uint32_t table = 0x75310; - const uint8_t index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*) (&l[idx & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - - idx = EXTRACT64(cx); - bx = cx; - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l[idx & MASK])[0]; - ch = ((uint64_t*) &l[idx & MASK])[1]; - lo = __umul128(idx, cl, &hi); - - al += hi; - ah += lo; - - ah ^= tweak1_2; - ((uint64_t*) &l[idx & MASK])[0] = al; - ((uint64_t*) &l[idx & MASK])[1] = ah; - ah ^= tweak1_2; - - ((uint64_t*) &l[idx & MASK])[1] ^= ((uint64_t*) &l[idx & MASK])[0]; - - ah ^= ch; - al ^= cl; - idx = al; - - const int64x2_t x = vld1q_s64(reinterpret_cast(&l[idx & MASK])); - const int64_t n = vgetq_lane_s64(x, 0); - const int32_t d = vgetq_lane_s32(x, 2); - const int64_t q = n / (d | 0x5); - - ((int64_t*) &l[idx & MASK])[0] = n ^ q; - - idx = d ^ q; - } -#undef BYTE - - cn_implode_scratchpad_heavy((__m128i*) l, (__m128i*) h); - keccakf(h, 24); - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - } -}; - - -template -class CryptoNightMultiHash -{ -public: - inline static void hash(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - keccak(input, (int) size, scratchPad[0]->state, 200); - keccak(input + size, (int) size, scratchPad[1]->state, 200); - - const uint8_t* l0 = scratchPad[0]->memory; - const uint8_t* l1 = scratchPad[1]->memory; - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - uint64_t* h1 = reinterpret_cast(scratchPad[1]->state); - - cn_explode_scratchpad((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad((__m128i*) h1, (__m128i*) l1); - - uint64_t al0 = h0[0] ^h0[4]; - uint64_t al1 = h1[0] ^h1[4]; - uint64_t ah0 = h0[1] ^h0[5]; - uint64_t ah1 = h1[1] ^h1[5]; - - __m128i bx0 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx1 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - - uint64_t idx0 = h0[0] ^h0[4]; - uint64_t idx1 = h1[0] ^h1[4]; - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx0; - __m128i cx1; - - if (SOFT_AES) { - cx0 = soft_aesenc((uint32_t*) &l0[idx0 & MASK], _mm_set_epi64x(ah0, al0)); - cx1 = soft_aesenc((uint32_t*) &l1[idx1 & MASK], _mm_set_epi64x(ah1, al1)); - } else { - cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - cx1 = _mm_load_si128((__m128i*) &l1[idx1 & MASK]); - - cx0 = _mm_aesenc_si128(cx0, _mm_set_epi64x(ah0, al0)); - cx1 = _mm_aesenc_si128(cx1, _mm_set_epi64x(ah1, al1)); - } - - _mm_store_si128((__m128i*) &l0[idx0 & MASK], _mm_xor_si128(bx0, cx0)); - _mm_store_si128((__m128i*) &l1[idx1 & MASK], _mm_xor_si128(bx1, cx1)); - - idx0 = EXTRACT64(cx0); - idx1 = EXTRACT64(cx1); - - bx0 = cx0; - bx1 = cx1; - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & MASK])[0]; - ch = ((uint64_t*) &l0[idx0 & MASK])[1]; - lo = __umul128(idx0, cl, &hi); - - al0 += hi; - ah0 += lo; - - ((uint64_t*) &l0[idx0 & MASK])[0] = al0; - ((uint64_t*) &l0[idx0 & MASK])[1] = ah0; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - cl = ((uint64_t*) &l1[idx1 & MASK])[0]; - ch = ((uint64_t*) &l1[idx1 & MASK])[1]; - lo = __umul128(idx1, cl, &hi); - - al1 += hi; - ah1 += lo; - - ((uint64_t*) &l1[idx1 & MASK])[0] = al1; - ((uint64_t*) &l1[idx1 & MASK])[1] = ah1; - - ah1 ^= ch; - al1 ^= cl; - idx1 = al1; - } - - cn_implode_scratchpad((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad((__m128i*) l1, (__m128i*) h1); - - keccakf(h0, 24); - keccakf(h1, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - extra_hashes[scratchPad[1]->state[0] & 3](scratchPad[1]->state, 200, output + 32); - } - - inline static void hashPowV2(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - keccak(input, (int) size, scratchPad[0]->state, 200); - keccak(input + size, (int) size, scratchPad[1]->state, 200); - - uint64_t tweak1_2_0 = (*reinterpret_cast(input + 35) ^ - *(reinterpret_cast(scratchPad[0]->state) + 24)); - uint64_t tweak1_2_1 = (*reinterpret_cast(input + 35 + size) ^ - *(reinterpret_cast(scratchPad[1]->state) + 24)); - - const uint8_t* l0 = scratchPad[0]->memory; - const uint8_t* l1 = scratchPad[1]->memory; - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - uint64_t* h1 = reinterpret_cast(scratchPad[1]->state); - - cn_explode_scratchpad((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad((__m128i*) h1, (__m128i*) l1); - - uint64_t al0 = h0[0] ^h0[4]; - uint64_t al1 = h1[0] ^h1[4]; - uint64_t ah0 = h0[1] ^h0[5]; - uint64_t ah1 = h1[1] ^h1[5]; - - __m128i bx0 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx1 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - - uint64_t idx0 = h0[0] ^h0[4]; - uint64_t idx1 = h1[0] ^h1[4]; - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx0; - __m128i cx1; - - if (SOFT_AES) { - cx0 = soft_aesenc((uint32_t*) &l0[idx0 & MASK], _mm_set_epi64x(ah0, al0)); - cx1 = soft_aesenc((uint32_t*) &l1[idx1 & MASK], _mm_set_epi64x(ah1, al1)); - } else { - cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - cx1 = _mm_load_si128((__m128i*) &l1[idx1 & MASK]); - - cx0 = _mm_aesenc_si128(cx0, _mm_set_epi64x(ah0, al0)); - cx1 = _mm_aesenc_si128(cx1, _mm_set_epi64x(ah1, al1)); - } - - _mm_store_si128((__m128i*) &l0[idx0 & MASK], _mm_xor_si128(bx0, cx0)); - _mm_store_si128((__m128i*) &l1[idx1 & MASK], _mm_xor_si128(bx1, cx1)); - - static const uint32_t table = 0x75310; - uint8_t tmp = reinterpret_cast(&l0[idx0 & MASK])[11]; - uint8_t index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*) (&l0[idx0 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - tmp = reinterpret_cast(&l1[idx1 & MASK])[11]; - index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*) (&l1[idx1 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - - idx0 = EXTRACT64(cx0); - idx1 = EXTRACT64(cx1); - - bx0 = cx0; - bx1 = cx1; - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & MASK])[0]; - ch = ((uint64_t*) &l0[idx0 & MASK])[1]; - lo = __umul128(idx0, cl, &hi); - - al0 += hi; - ah0 += lo; - - ah0 ^= tweak1_2_0; - ((uint64_t*) &l0[idx0 & MASK])[0] = al0; - ((uint64_t*) &l0[idx0 & MASK])[1] = ah0; - ah0 ^= tweak1_2_0; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - cl = ((uint64_t*) &l1[idx1 & MASK])[0]; - ch = ((uint64_t*) &l1[idx1 & MASK])[1]; - lo = __umul128(idx1, cl, &hi); - - al1 += hi; - ah1 += lo; - - ah1 ^= tweak1_2_1; - ((uint64_t*) &l1[idx1 & MASK])[0] = al1; - ((uint64_t*) &l1[idx1 & MASK])[1] = ah1; - ah1 ^= tweak1_2_1; - - ah1 ^= ch; - al1 ^= cl; - idx1 = al1; - } - - cn_implode_scratchpad((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad((__m128i*) l1, (__m128i*) h1); - - keccakf(h0, 24); - keccakf(h1, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - extra_hashes[scratchPad[1]->state[0] & 3](scratchPad[1]->state, 200, output + 32); - } - - // double - inline static void hashPowV3(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - keccak(input, (int) size, scratchPad[0]->state, 200); - keccak(input + size, (int) size, scratchPad[1]->state, 200); - - const uint8_t* l0 = scratchPad[0]->memory; - const uint8_t* l1 = scratchPad[1]->memory; - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - uint64_t* h1 = reinterpret_cast(scratchPad[1]->state); - - cn_explode_scratchpad((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad((__m128i*) h1, (__m128i*) l1); - - uint64_t al0 = h0[0] ^h0[4]; - uint64_t al1 = h1[0] ^h1[4]; - uint64_t ah0 = h0[1] ^h0[5]; - uint64_t ah1 = h1[1] ^h1[5]; - - __m128i bx00 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx01 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - - __m128i bx10 = _mm_set_epi64x(h0[9] ^ h0[11], h0[8] ^ h0[10]); - __m128i bx11 = _mm_set_epi64x(h1[9] ^ h1[11], h1[8] ^ h1[10]); - - uint64_t idx0 = h0[0] ^h0[4]; - uint64_t idx1 = h1[0] ^h1[4]; - - uint64_t division_result_xmm0 = h0[12]; - uint64_t division_result_xmm1 = h1[12]; - - uint64_t sqrt_result0 = h0[13]; - uint64_t sqrt_result1 = h1[13]; - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx0; - __m128i cx1; - - const __m128i ax0 = _mm_set_epi64x(ah0, al0); - const __m128i ax1 = _mm_set_epi64x(ah1, al1); - - if (SOFT_AES) { - cx0 = soft_aesenc((uint32_t*) &l0[idx0 & MASK], ax0); - cx1 = soft_aesenc((uint32_t*) &l1[idx1 & MASK], ax1); - } else { - cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - cx1 = _mm_load_si128((__m128i*) &l1[idx1 & MASK]); - - cx0 = _mm_aesenc_si128(cx0, _mm_set_epi64x(ah0, al0)); - cx1 = _mm_aesenc_si128(cx1, _mm_set_epi64x(ah1, al1)); - } - - SHUFFLE_PHASE_1(l0, (idx0&MASK), bx00, bx10, ax0, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - SHUFFLE_PHASE_1(l1, (idx1&MASK), bx01, bx11, ax1, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - - _mm_store_si128((__m128i*) &l0[idx0 & MASK], _mm_xor_si128(bx00, cx0)); - _mm_store_si128((__m128i*) &l1[idx1 & MASK], _mm_xor_si128(bx01, cx1)); - - idx0 = EXTRACT64(cx0); - idx1 = EXTRACT64(cx1); - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & MASK])[0]; - ch = ((uint64_t*) &l0[idx0 & MASK])[1]; - - INTEGER_MATH_V2(0, cl, cx0); - - lo = __umul128(idx0, cl, &hi); - - SHUFFLE_PHASE_2(l0, (idx0&MASK), bx00, bx10, ax0, lo, hi, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - - al0 += hi; - ah0 += lo; - - ((uint64_t*) &l0[idx0 & MASK])[0] = al0; - ((uint64_t*) &l0[idx0 & MASK])[1] = ah0; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - bx10 = bx00; - bx00 = cx0; - - - cl = ((uint64_t*) &l1[idx1 & MASK])[0]; - ch = ((uint64_t*) &l1[idx1 & MASK])[1]; - - INTEGER_MATH_V2(1, cl, cx1); - - lo = __umul128(idx1, cl, &hi); - - SHUFFLE_PHASE_2(l1, (idx1&MASK), bx01, bx11, ax1, lo, hi, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - - al1 += hi; - ah1 += lo; - - ((uint64_t*) &l1[idx1 & MASK])[0] = al1; - ((uint64_t*) &l1[idx1 & MASK])[1] = ah1; - - ah1 ^= ch; - al1 ^= cl; - idx1 = al1; - - bx11 = bx01; - bx01 = cx1; - } - - cn_implode_scratchpad((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad((__m128i*) l1, (__m128i*) h1); - - keccakf(h0, 24); - keccakf(h1, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - extra_hashes[scratchPad[1]->state[0] & 3](scratchPad[1]->state, 200, output + 32); - } - - // double - inline static void hashPowV4(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad, - uint64_t height) - { - keccak(input, (int) size, scratchPad[0]->state, 200); - keccak(input + size, (int) size, scratchPad[1]->state, 200); - - const uint8_t* l0 = scratchPad[0]->memory; - const uint8_t* l1 = scratchPad[1]->memory; - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - uint64_t* h1 = reinterpret_cast(scratchPad[1]->state); - - cn_explode_scratchpad((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad((__m128i*) h1, (__m128i*) l1); - - uint64_t al0 = h0[0] ^h0[4]; - uint64_t al1 = h1[0] ^h1[4]; - uint64_t ah0 = h0[1] ^h0[5]; - uint64_t ah1 = h1[1] ^h1[5]; - - __m128i bx00 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx01 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - - __m128i bx10 = _mm_set_epi64x(h0[9] ^ h0[11], h0[8] ^ h0[10]); - __m128i bx11 = _mm_set_epi64x(h1[9] ^ h1[11], h1[8] ^ h1[10]); - - uint64_t idx0 = h0[0] ^h0[4]; - uint64_t idx1 = h1[0] ^h1[4]; - - VARIANT4_RANDOM_MATH_INIT(0, h0) - VARIANT4_RANDOM_MATH_INIT(1, h1) - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx0; - __m128i cx1; - - const __m128i ax0 = _mm_set_epi64x(ah0, al0); - const __m128i ax1 = _mm_set_epi64x(ah1, al1); - - if (SOFT_AES) { - cx0 = soft_aesenc((uint32_t*) &l0[idx0 & MASK], ax0); - cx1 = soft_aesenc((uint32_t*) &l1[idx1 & MASK], ax1); - } else { - cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - cx1 = _mm_load_si128((__m128i*) &l1[idx1 & MASK]); - - cx0 = _mm_aesenc_si128(cx0, _mm_set_epi64x(ah0, al0)); - cx1 = _mm_aesenc_si128(cx1, _mm_set_epi64x(ah1, al1)); - } - - SHUFFLE_V4(l0, (idx0&MASK), bx00, bx10, ax0, cx0) - SHUFFLE_V4(l1, (idx1&MASK), bx01, bx11, ax1, cx1) - - _mm_store_si128((__m128i*) &l0[idx0 & MASK], _mm_xor_si128(bx00, cx0)); - _mm_store_si128((__m128i*) &l1[idx1 & MASK], _mm_xor_si128(bx01, cx1)); - - idx0 = EXTRACT64(cx0); - idx1 = EXTRACT64(cx1); - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & MASK])[0]; - ch = ((uint64_t*) &l0[idx0 & MASK])[1]; - - VARIANT4_RANDOM_MATH(0, al0, ah0, cl, bx00, bx10) - - if (VARIANT == POW_V4) { - al0 ^= r0[2] | ((uint64_t)(r0[3]) << 32); - ah0 ^= r0[0] | ((uint64_t)(r0[1]) << 32); - } - - lo = __umul128(idx0, cl, &hi); - - SHUFFLE_V4(l0, (idx0&MASK), bx00, bx10, ax0, cx0); - - al0 += hi; - ah0 += lo; - - ((uint64_t*) &l0[idx0 & MASK])[0] = al0; - ((uint64_t*) &l0[idx0 & MASK])[1] = ah0; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - bx10 = bx00; - bx00 = cx0; - - - cl = ((uint64_t*) &l1[idx1 & MASK])[0]; - ch = ((uint64_t*) &l1[idx1 & MASK])[1]; - - VARIANT4_RANDOM_MATH(1, al1, ah1, cl, bx01, bx11) - - if (VARIANT == POW_V4) { - al1 ^= r1[2] | ((uint64_t)(r1[3]) << 32); - ah1 ^= r1[0] | ((uint64_t)(r1[1]) << 32); - } - - lo = __umul128(idx1, cl, &hi); - - SHUFFLE_V4(l1, (idx1&MASK), bx01, bx11, ax1, cx1); - - al1 += hi; - ah1 += lo; - - ((uint64_t*) &l1[idx1 & MASK])[0] = al1; - ((uint64_t*) &l1[idx1 & MASK])[1] = ah1; - - ah1 ^= ch; - al1 ^= cl; - idx1 = al1; - - bx11 = bx01; - bx01 = cx1; - } - - cn_implode_scratchpad((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad((__m128i*) l1, (__m128i*) h1); - - keccakf(h0, 24); - keccakf(h1, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - extra_hashes[scratchPad[1]->state[0] & 3](scratchPad[1]->state, 200, output + 32); - } - - inline static void hashLiteTube(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - keccak(input, (int) size, scratchPad[0]->state, 200); - keccak(input + size, (int) size, scratchPad[1]->state, 200); - - uint64_t tweak1_2_0 = (*reinterpret_cast(input + 35) ^ - *(reinterpret_cast(scratchPad[0]->state) + 24)); - uint64_t tweak1_2_1 = (*reinterpret_cast(input + 35 + size) ^ - *(reinterpret_cast(scratchPad[1]->state) + 24)); - - const uint8_t* l0 = scratchPad[0]->memory; - const uint8_t* l1 = scratchPad[1]->memory; - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - uint64_t* h1 = reinterpret_cast(scratchPad[1]->state); - - cn_explode_scratchpad((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad((__m128i*) h1, (__m128i*) l1); - - uint64_t al0 = h0[0] ^h0[4]; - uint64_t al1 = h1[0] ^h1[4]; - uint64_t ah0 = h0[1] ^h0[5]; - uint64_t ah1 = h1[1] ^h1[5]; - - __m128i bx0 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx1 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - - uint64_t idx0 = h0[0] ^h0[4]; - uint64_t idx1 = h1[0] ^h1[4]; - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx0; - __m128i cx1; - - if (SOFT_AES) { - cx0 = soft_aesenc((uint32_t*) &l0[idx0 & MASK], _mm_set_epi64x(ah0, al0)); - cx1 = soft_aesenc((uint32_t*) &l1[idx1 & MASK], _mm_set_epi64x(ah1, al1)); - } else { - cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - cx1 = _mm_load_si128((__m128i*) &l1[idx1 & MASK]); - - cx0 = _mm_aesenc_si128(cx0, _mm_set_epi64x(ah0, al0)); - cx1 = _mm_aesenc_si128(cx1, _mm_set_epi64x(ah1, al1)); - } - - _mm_store_si128((__m128i*) &l0[idx0 & MASK], _mm_xor_si128(bx0, cx0)); - _mm_store_si128((__m128i*) &l1[idx1 & MASK], _mm_xor_si128(bx1, cx1)); - - static const uint32_t table = 0x75310; - uint8_t tmp = reinterpret_cast(&l0[idx0 & MASK])[11]; - uint8_t index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*) (&l0[idx0 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - tmp = reinterpret_cast(&l1[idx1 & MASK])[11]; - index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*) (&l1[idx1 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - - idx0 = EXTRACT64(cx0); - idx1 = EXTRACT64(cx1); - - bx0 = cx0; - bx1 = cx1; - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & MASK])[0]; - ch = ((uint64_t*) &l0[idx0 & MASK])[1]; - lo = __umul128(idx0, cl, &hi); - - al0 += hi; - ah0 += lo; - - ah0 ^= tweak1_2_0; - ((uint64_t*) &l0[idx0 & MASK])[0] = al0; - ((uint64_t*) &l0[idx0 & MASK])[1] = ah0; - ah0 ^= tweak1_2_0; - - ((uint64_t*) &l0[idx0 & MASK])[1] ^= ((uint64_t*) &l0[idx0 & MASK])[0]; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - cl = ((uint64_t*) &l1[idx1 & MASK])[0]; - ch = ((uint64_t*) &l1[idx1 & MASK])[1]; - lo = __umul128(idx1, cl, &hi); - - al1 += hi; - ah1 += lo; - - ah1 ^= tweak1_2_1; - ((uint64_t*) &l1[idx1 & MASK])[0] = al1; - ((uint64_t*) &l1[idx1 & MASK])[1] = ah1; - ah1 ^= tweak1_2_1; - - ((uint64_t*) &l1[idx1 & MASK])[1] ^= ((uint64_t*) &l1[idx1 & MASK])[0]; - - ah1 ^= ch; - al1 ^= cl; - idx1 = al1; - } - - cn_implode_scratchpad((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad((__m128i*) l1, (__m128i*) h1); - - keccakf(h0, 24); - keccakf(h1, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - extra_hashes[scratchPad[1]->state[0] & 3](scratchPad[1]->state, 200, output + 32); - } - - inline static void hashHeavy(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - keccak(input, (int) size, scratchPad[0]->state, 200); - keccak(input + size, (int) size, scratchPad[1]->state, 200); - - const uint8_t* l0 = scratchPad[0]->memory; - const uint8_t* l1 = scratchPad[1]->memory; - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - uint64_t* h1 = reinterpret_cast(scratchPad[1]->state); - - cn_explode_scratchpad_heavy((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad_heavy((__m128i*) h1, (__m128i*) l1); - - uint64_t al0 = h0[0] ^h0[4]; - uint64_t al1 = h1[0] ^h1[4]; - uint64_t ah0 = h0[1] ^h0[5]; - uint64_t ah1 = h1[1] ^h1[5]; - - __m128i bx0 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx1 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - - uint64_t idx0 = h0[0] ^h0[4]; - uint64_t idx1 = h1[0] ^h1[4]; - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx0; - __m128i cx1; - - if (SOFT_AES) { - cx0 = soft_aesenc((uint32_t*) &l0[idx0 & MASK], _mm_set_epi64x(ah0, al0)); - cx1 = soft_aesenc((uint32_t*) &l1[idx1 & MASK], _mm_set_epi64x(ah1, al1)); - } else { - cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - cx1 = _mm_load_si128((__m128i*) &l1[idx1 & MASK]); - - cx0 = _mm_aesenc_si128(cx0, _mm_set_epi64x(ah0, al0)); - cx1 = _mm_aesenc_si128(cx1, _mm_set_epi64x(ah1, al1)); - } - - _mm_store_si128((__m128i*) &l0[idx0 & MASK], _mm_xor_si128(bx0, cx0)); - _mm_store_si128((__m128i*) &l1[idx1 & MASK], _mm_xor_si128(bx1, cx1)); - - idx0 = EXTRACT64(cx0); - idx1 = EXTRACT64(cx1); - - bx0 = cx0; - bx1 = cx1; - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & MASK])[0]; - ch = ((uint64_t*) &l0[idx0 & MASK])[1]; - lo = __umul128(idx0, cl, &hi); - - al0 += hi; - ah0 += lo; - - ((uint64_t*) &l0[idx0 & MASK])[0] = al0; - ((uint64_t*) &l0[idx0 & MASK])[1] = ah0; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - const int64x2_t x0 = vld1q_s64(reinterpret_cast(&l0[idx0 & MASK])); - const int64_t n0 = vgetq_lane_s64(x0, 0); - const int32_t d0 = vgetq_lane_s32(x0, 2); - const int64_t q0 = n0 / (d0 | 0x5); - - ((int64_t*) &l0[idx0 & MASK])[0] = n0 ^ q0; - - if (VARIANT == POW_XHV || VARIANT == POW_XFH) { - idx0 = (~d0) ^ q0; - } else { - idx0 = d0 ^ q0; - } - - cl = ((uint64_t*) &l1[idx1 & MASK])[0]; - ch = ((uint64_t*) &l1[idx1 & MASK])[1]; - lo = __umul128(idx1, cl, &hi); - - al1 += hi; - ah1 += lo; - - ((uint64_t*) &l1[idx1 & MASK])[0] = al1; - ((uint64_t*) &l1[idx1 & MASK])[1] = ah1; - - ah1 ^= ch; - al1 ^= cl; - idx1 = al1; - - const int64x2_t x1 = vld1q_s64(reinterpret_cast(&l1[idx1 & MASK])); - const int64_t n1 = vgetq_lane_s64(x1, 0); - const int32_t d1 = vgetq_lane_s32(x1, 2); - const int64_t q1 = n1 / (d1 | 0x5); - - ((int64_t*) &l1[idx1 & MASK])[0] = n1 ^ q1; - - if (VARIANT == POW_XHV || VARIANT == POW_XFH) { - idx1 = (~d1) ^ q1; - } else { - idx1 = d1 ^ q1; - } - } - - cn_implode_scratchpad_heavy((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad_heavy((__m128i*) l1, (__m128i*) h1); - - keccakf(h0, 24); - keccakf(h1, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - extra_hashes[scratchPad[1]->state[0] & 3](scratchPad[1]->state, 200, output + 32); - } - - inline static void hashHeavyTube(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - keccak((const uint8_t*) input, (int) size, scratchPad[0]->state, 200); - keccak((const uint8_t*) input + size, (int) size, scratchPad[1]->state, 200); - - uint64_t tweak1_2_0 = (*reinterpret_cast(reinterpret_cast(input) + 35) ^ - *(reinterpret_cast(scratchPad[0]->state) + 24)); - uint64_t tweak1_2_1 = (*reinterpret_cast(reinterpret_cast(input) + 35 + size) ^ - *(reinterpret_cast(scratchPad[1]->state) + 24)); - - const uint8_t* l0 = scratchPad[0]->memory; - const uint8_t* l1 = scratchPad[1]->memory; - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - uint64_t* h1 = reinterpret_cast(scratchPad[1]->state); - - cn_explode_scratchpad_heavy((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad_heavy((__m128i*) h1, (__m128i*) l1); - - uint64_t al0 = h0[0] ^h0[4]; - uint64_t al1 = h1[0] ^h1[4]; - uint64_t ah0 = h0[1] ^h0[5]; - uint64_t ah1 = h1[1] ^h1[5]; - - __m128i bx0 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx1 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - - uint64_t idx0 = h0[0] ^h0[4]; - uint64_t idx1 = h1[0] ^h1[4]; - - union alignas(16) - { - uint32_t k[4]; - uint64_t v64[2]; - }; - alignas(16) uint32_t x[4]; - -#define BYTE(p, i) ((unsigned char*)&p)[i] - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - __m128i cx1 = _mm_load_si128((__m128i*) &l1[idx1 & MASK]); - - const __m128i& key0 = _mm_set_epi64x(ah0, al0); - - _mm_store_si128((__m128i*) k, key0); - cx0 = _mm_xor_si128(cx0, _mm_cmpeq_epi32(_mm_setzero_si128(), _mm_setzero_si128())); - _mm_store_si128((__m128i*) x, cx0); - - k[0] ^= saes_table[0][BYTE(x[0], 0)] ^ saes_table[1][BYTE(x[1], 1)] ^ saes_table[2][BYTE(x[2], 2)] ^ - saes_table[3][BYTE(x[3], 3)]; - x[0] ^= k[0]; - k[1] ^= saes_table[0][BYTE(x[1], 0)] ^ saes_table[1][BYTE(x[2], 1)] ^ saes_table[2][BYTE(x[3], 2)] ^ - saes_table[3][BYTE(x[0], 3)]; - x[1] ^= k[1]; - k[2] ^= saes_table[0][BYTE(x[2], 0)] ^ saes_table[1][BYTE(x[3], 1)] ^ saes_table[2][BYTE(x[0], 2)] ^ - saes_table[3][BYTE(x[1], 3)]; - x[2] ^= k[2]; - k[3] ^= saes_table[0][BYTE(x[3], 0)] ^ saes_table[1][BYTE(x[0], 1)] ^ saes_table[2][BYTE(x[1], 2)] ^ - saes_table[3][BYTE(x[2], 3)]; - - cx0 = _mm_load_si128((__m128i*) k); - - const __m128i& key1 = _mm_set_epi64x(ah1, al1); - - _mm_store_si128((__m128i*) k, key1); - cx1 = _mm_xor_si128(cx1, _mm_cmpeq_epi32(_mm_setzero_si128(), _mm_setzero_si128())); - _mm_store_si128((__m128i*) x, cx1); - - k[0] ^= saes_table[0][BYTE(x[0], 0)] ^ saes_table[1][BYTE(x[1], 1)] ^ saes_table[2][BYTE(x[2], 2)] ^ - saes_table[3][BYTE(x[3], 3)]; - x[0] ^= k[0]; - k[1] ^= saes_table[0][BYTE(x[1], 0)] ^ saes_table[1][BYTE(x[2], 1)] ^ saes_table[2][BYTE(x[3], 2)] ^ - saes_table[3][BYTE(x[0], 3)]; - x[1] ^= k[1]; - k[2] ^= saes_table[0][BYTE(x[2], 0)] ^ saes_table[1][BYTE(x[3], 1)] ^ saes_table[2][BYTE(x[0], 2)] ^ - saes_table[3][BYTE(x[1], 3)]; - x[2] ^= k[2]; - k[3] ^= saes_table[0][BYTE(x[3], 0)] ^ saes_table[1][BYTE(x[0], 1)] ^ saes_table[2][BYTE(x[1], 2)] ^ - saes_table[3][BYTE(x[2], 3)]; - - cx1 = _mm_load_si128((__m128i*) k); - - _mm_store_si128((__m128i*) &l0[idx0 & MASK], _mm_xor_si128(bx0, cx0)); - _mm_store_si128((__m128i*) &l1[idx1 & MASK], _mm_xor_si128(bx1, cx1)); - - static const uint32_t table = 0x75310; - uint8_t tmp = reinterpret_cast(&l0[idx0 & MASK])[11]; - uint8_t index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*) (&l0[idx0 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - tmp = reinterpret_cast(&l1[idx1 & MASK])[11]; - index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*) (&l1[idx1 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - - idx0 = EXTRACT64(cx0); - idx1 = EXTRACT64(cx1); - - bx0 = cx0; - bx1 = cx1; - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & MASK])[0]; - ch = ((uint64_t*) &l0[idx0 & MASK])[1]; - lo = __umul128(idx0, cl, &hi); - - al0 += hi; - ah0 += lo; - - ah0 ^= tweak1_2_0; - ((uint64_t*) &l0[idx0 & MASK])[0] = al0; - ((uint64_t*) &l0[idx0 & MASK])[1] = ah0; - ah0 ^= tweak1_2_0; - - ((uint64_t*) &l0[idx0 & MASK])[1] ^= ((uint64_t*) &l0[idx0 & MASK])[0]; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - - const int64x2_t x0 = vld1q_s64(reinterpret_cast(&l0[idx0 & MASK])); - const int64_t n0 = vgetq_lane_s64(x0, 0); - const int32_t d0 = vgetq_lane_s32(x0, 2); - const int64_t q0 = n0 / (d0 | 0x5); - - ((int64_t*) &l0[idx0 & MASK])[0] = n0 ^ q0; - - idx0 = d0 ^ q0; - - - cl = ((uint64_t*) &l1[idx1 & MASK])[0]; - ch = ((uint64_t*) &l1[idx1 & MASK])[1]; - lo = __umul128(idx1, cl, &hi); - - al1 += hi; - ah1 += lo; - - ah1 ^= tweak1_2_1; - ((uint64_t*) &l1[idx1 & MASK])[0] = al1; - ((uint64_t*) &l1[idx1 & MASK])[1] = ah1; - ah1 ^= tweak1_2_1; - - ((uint64_t*) &l1[idx1 & MASK])[1] ^= ((uint64_t*) &l1[idx1 & MASK])[0]; - - ah1 ^= ch; - al1 ^= cl; - idx1 = al1; - - const int64x2_t x1 = vld1q_s64(reinterpret_cast(&l1[idx1 & MASK])); - const int64_t n1 = vgetq_lane_s64(x1, 0); - const int32_t d1 = vgetq_lane_s32(x1, 2); - const int64_t q1 = n1 / (d1 | 0x5); - - ((int64_t*) &l1[idx1 & MASK])[0] = n1 ^ q1; - - idx1 = d1 ^ q1; - } -#undef BYTE - - cn_implode_scratchpad_heavy((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad_heavy((__m128i*) l1, (__m128i*) h1); - - keccakf(h0, 24); - keccakf(h1, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - extra_hashes[scratchPad[1]->state[0] & 3](scratchPad[1]->state, 200, output + 32); - } -}; - -template -class CryptoNightMultiHash -{ -public: - inline static void hash(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - keccak(input, (int) size, scratchPad[0]->state, 200); - keccak(input + size, (int) size, scratchPad[1]->state, 200); - keccak(input + 2 * size, (int) size, scratchPad[2]->state, 200); - - const uint8_t* l0 = scratchPad[0]->memory; - const uint8_t* l1 = scratchPad[1]->memory; - const uint8_t* l2 = scratchPad[2]->memory; - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - uint64_t* h1 = reinterpret_cast(scratchPad[1]->state); - uint64_t* h2 = reinterpret_cast(scratchPad[2]->state); - - cn_explode_scratchpad((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad((__m128i*) h1, (__m128i*) l1); - cn_explode_scratchpad((__m128i*) h2, (__m128i*) l2); - - uint64_t al0 = h0[0] ^h0[4]; - uint64_t al1 = h1[0] ^h1[4]; - uint64_t al2 = h2[0] ^h2[4]; - uint64_t ah0 = h0[1] ^h0[5]; - uint64_t ah1 = h1[1] ^h1[5]; - uint64_t ah2 = h2[1] ^h2[5]; - - __m128i bx0 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx1 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - __m128i bx2 = _mm_set_epi64x(h2[3] ^ h2[7], h2[2] ^ h2[6]); - - uint64_t idx0 = h0[0] ^h0[4]; - uint64_t idx1 = h1[0] ^h1[4]; - uint64_t idx2 = h2[0] ^h2[4]; - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx0; - __m128i cx1; - __m128i cx2; - - if (SOFT_AES) { - cx0 = soft_aesenc((uint32_t*) &l0[idx0 & MASK], _mm_set_epi64x(ah0, al0)); - cx1 = soft_aesenc((uint32_t*) &l1[idx1 & MASK], _mm_set_epi64x(ah1, al1)); - cx2 = soft_aesenc((uint32_t*) &l2[idx2 & MASK], _mm_set_epi64x(ah2, al2)); - } else { - cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - cx1 = _mm_load_si128((__m128i*) &l1[idx1 & MASK]); - cx2 = _mm_load_si128((__m128i*) &l2[idx2 & MASK]); - - cx0 = _mm_aesenc_si128(cx0, _mm_set_epi64x(ah0, al0)); - cx1 = _mm_aesenc_si128(cx1, _mm_set_epi64x(ah1, al1)); - cx2 = _mm_aesenc_si128(cx2, _mm_set_epi64x(ah2, al2)); - } - - _mm_store_si128((__m128i*) &l0[idx0 & MASK], _mm_xor_si128(bx0, cx0)); - _mm_store_si128((__m128i*) &l1[idx1 & MASK], _mm_xor_si128(bx1, cx1)); - _mm_store_si128((__m128i*) &l2[idx2 & MASK], _mm_xor_si128(bx2, cx2)); - - idx0 = EXTRACT64(cx0); - idx1 = EXTRACT64(cx1); - idx2 = EXTRACT64(cx2); - - bx0 = cx0; - bx1 = cx1; - bx2 = cx2; - - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & MASK])[0]; - ch = ((uint64_t*) &l0[idx0 & MASK])[1]; - lo = __umul128(idx0, cl, &hi); - - al0 += hi; - ah0 += lo; - - ((uint64_t*) &l0[idx0 & MASK])[0] = al0; - ((uint64_t*) &l0[idx0 & MASK])[1] = ah0; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - - cl = ((uint64_t*) &l1[idx1 & MASK])[0]; - ch = ((uint64_t*) &l1[idx1 & MASK])[1]; - lo = __umul128(idx1, cl, &hi); - - al1 += hi; - ah1 += lo; - - ((uint64_t*) &l1[idx1 & MASK])[0] = al1; - ((uint64_t*) &l1[idx1 & MASK])[1] = ah1; - - ah1 ^= ch; - al1 ^= cl; - idx1 = al1; - - - cl = ((uint64_t*) &l2[idx2 & MASK])[0]; - ch = ((uint64_t*) &l2[idx2 & MASK])[1]; - lo = __umul128(idx2, cl, &hi); - - al2 += hi; - ah2 += lo; - - ((uint64_t*) &l2[idx2 & MASK])[0] = al2; - ((uint64_t*) &l2[idx2 & MASK])[1] = ah2; - - ah2 ^= ch; - al2 ^= cl; - idx2 = al2; - } - - cn_implode_scratchpad((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad((__m128i*) l1, (__m128i*) h1); - cn_implode_scratchpad((__m128i*) l2, (__m128i*) h2); - - keccakf(h0, 24); - keccakf(h1, 24); - keccakf(h2, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - extra_hashes[scratchPad[1]->state[0] & 3](scratchPad[1]->state, 200, output + 32); - extra_hashes[scratchPad[2]->state[0] & 3](scratchPad[2]->state, 200, output + 64); - } - - inline static void hashPowV2(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - keccak(input, (int) size, scratchPad[0]->state, 200); - keccak(input + size, (int) size, scratchPad[1]->state, 200); - keccak(input + 2 * size, (int) size, scratchPad[2]->state, 200); - - uint64_t tweak1_2_0 = (*reinterpret_cast(input + 35) ^ - *(reinterpret_cast(scratchPad[0]->state) + 24)); - uint64_t tweak1_2_1 = (*reinterpret_cast(input + 35 + size) ^ - *(reinterpret_cast(scratchPad[1]->state) + 24)); - uint64_t tweak1_2_2 = (*reinterpret_cast(input + 35 + 2 * size) ^ - *(reinterpret_cast(scratchPad[2]->state) + 24)); - - const uint8_t* l0 = scratchPad[0]->memory; - const uint8_t* l1 = scratchPad[1]->memory; - const uint8_t* l2 = scratchPad[2]->memory; - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - uint64_t* h1 = reinterpret_cast(scratchPad[1]->state); - uint64_t* h2 = reinterpret_cast(scratchPad[2]->state); - - cn_explode_scratchpad((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad((__m128i*) h1, (__m128i*) l1); - cn_explode_scratchpad((__m128i*) h2, (__m128i*) l2); - - uint64_t al0 = h0[0] ^h0[4]; - uint64_t al1 = h1[0] ^h1[4]; - uint64_t al2 = h2[0] ^h2[4]; - uint64_t ah0 = h0[1] ^h0[5]; - uint64_t ah1 = h1[1] ^h1[5]; - uint64_t ah2 = h2[1] ^h2[5]; - - __m128i bx0 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx1 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - __m128i bx2 = _mm_set_epi64x(h2[3] ^ h2[7], h2[2] ^ h2[6]); - - uint64_t idx0 = h0[0] ^h0[4]; - uint64_t idx1 = h1[0] ^h1[4]; - uint64_t idx2 = h2[0] ^h2[4]; - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx0; - __m128i cx1; - __m128i cx2; - - if (SOFT_AES) { - cx0 = soft_aesenc((uint32_t*) &l0[idx0 & MASK], _mm_set_epi64x(ah0, al0)); - cx1 = soft_aesenc((uint32_t*) &l1[idx1 & MASK], _mm_set_epi64x(ah1, al1)); - cx2 = soft_aesenc((uint32_t*) &l2[idx2 & MASK], _mm_set_epi64x(ah2, al2)); - } else { - cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - cx1 = _mm_load_si128((__m128i*) &l1[idx1 & MASK]); - cx2 = _mm_load_si128((__m128i*) &l2[idx2 & MASK]); - - cx0 = _mm_aesenc_si128(cx0, _mm_set_epi64x(ah0, al0)); - cx1 = _mm_aesenc_si128(cx1, _mm_set_epi64x(ah1, al1)); - cx2 = _mm_aesenc_si128(cx2, _mm_set_epi64x(ah2, al2)); - } - - _mm_store_si128((__m128i*) &l0[idx0 & MASK], _mm_xor_si128(bx0, cx0)); - _mm_store_si128((__m128i*) &l1[idx1 & MASK], _mm_xor_si128(bx1, cx1)); - _mm_store_si128((__m128i*) &l2[idx2 & MASK], _mm_xor_si128(bx2, cx2)); - - static const uint32_t table = 0x75310; - uint8_t tmp = reinterpret_cast(&l0[idx0 & MASK])[11]; - uint8_t index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*) (&l0[idx0 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - tmp = reinterpret_cast(&l1[idx1 & MASK])[11]; - index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*) (&l1[idx1 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - tmp = reinterpret_cast(&l2[idx2 & MASK])[11]; - index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*) (&l2[idx2 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - - idx0 = EXTRACT64(cx0); - idx1 = EXTRACT64(cx1); - idx2 = EXTRACT64(cx2); - - bx0 = cx0; - bx1 = cx1; - bx2 = cx2; - - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & MASK])[0]; - ch = ((uint64_t*) &l0[idx0 & MASK])[1]; - lo = __umul128(idx0, cl, &hi); - - al0 += hi; - ah0 += lo; - - ah0 ^= tweak1_2_0; - ((uint64_t*) &l0[idx0 & MASK])[0] = al0; - ((uint64_t*) &l0[idx0 & MASK])[1] = ah0; - ah0 ^= tweak1_2_0; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - - cl = ((uint64_t*) &l1[idx1 & MASK])[0]; - ch = ((uint64_t*) &l1[idx1 & MASK])[1]; - lo = __umul128(idx1, cl, &hi); - - al1 += hi; - ah1 += lo; - - ah1 ^= tweak1_2_1; - ((uint64_t*) &l1[idx1 & MASK])[0] = al1; - ((uint64_t*) &l1[idx1 & MASK])[1] = ah1; - ah1 ^= tweak1_2_1; - - ah1 ^= ch; - al1 ^= cl; - idx1 = al1; - - - cl = ((uint64_t*) &l2[idx2 & MASK])[0]; - ch = ((uint64_t*) &l2[idx2 & MASK])[1]; - lo = __umul128(idx2, cl, &hi); - - al2 += hi; - ah2 += lo; - - ah2 ^= tweak1_2_2; - ((uint64_t*) &l2[idx2 & MASK])[0] = al2; - ((uint64_t*) &l2[idx2 & MASK])[1] = ah2; - ah2 ^= tweak1_2_2; - - ah2 ^= ch; - al2 ^= cl; - idx2 = al2; - } - - cn_implode_scratchpad((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad((__m128i*) l1, (__m128i*) h1); - cn_implode_scratchpad((__m128i*) l2, (__m128i*) h2); - - keccakf(h0, 24); - keccakf(h1, 24); - keccakf(h2, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - extra_hashes[scratchPad[1]->state[0] & 3](scratchPad[1]->state, 200, output + 32); - extra_hashes[scratchPad[2]->state[0] & 3](scratchPad[2]->state, 200, output + 64); - } - - // triple - inline static void hashPowV3(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - keccak(input, (int) size, scratchPad[0]->state, 200); - keccak(input + size, (int) size, scratchPad[1]->state, 200); - keccak(input + 2 * size, (int) size, scratchPad[2]->state, 200); - - const uint8_t* l0 = scratchPad[0]->memory; - const uint8_t* l1 = scratchPad[1]->memory; - const uint8_t* l2 = scratchPad[2]->memory; - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - uint64_t* h1 = reinterpret_cast(scratchPad[1]->state); - uint64_t* h2 = reinterpret_cast(scratchPad[2]->state); - - cn_explode_scratchpad((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad((__m128i*) h1, (__m128i*) l1); - cn_explode_scratchpad((__m128i*) h2, (__m128i*) l2); - - uint64_t al0 = h0[0] ^h0[4]; - uint64_t al1 = h1[0] ^h1[4]; - uint64_t al2 = h2[0] ^h2[4]; - uint64_t ah0 = h0[1] ^h0[5]; - uint64_t ah1 = h1[1] ^h1[5]; - uint64_t ah2 = h2[1] ^h2[5]; - - __m128i bx00 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx01 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - __m128i bx02 = _mm_set_epi64x(h2[3] ^ h2[7], h2[2] ^ h2[6]); - - __m128i bx10 = _mm_set_epi64x(h0[9] ^ h0[11], h0[8] ^ h0[10]); - __m128i bx11 = _mm_set_epi64x(h1[9] ^ h1[11], h1[8] ^ h1[10]); - __m128i bx12 = _mm_set_epi64x(h2[9] ^ h2[11], h2[8] ^ h2[10]); - - uint64_t idx0 = h0[0] ^h0[4]; - uint64_t idx1 = h1[0] ^h1[4]; - uint64_t idx2 = h2[0] ^h2[4]; - - uint64_t division_result_xmm0 = h0[12]; - uint64_t division_result_xmm1 = h1[12]; - uint64_t division_result_xmm2 = h2[12]; - - uint64_t sqrt_result0 = h0[13]; - uint64_t sqrt_result1 = h1[13]; - uint64_t sqrt_result2 = h2[13]; - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx0; - __m128i cx1; - __m128i cx2; - - const __m128i ax0 = _mm_set_epi64x(ah0, al0); - const __m128i ax1 = _mm_set_epi64x(ah1, al1); - const __m128i ax2 = _mm_set_epi64x(ah2, al2); - - if (SOFT_AES) { - cx0 = soft_aesenc((uint32_t*) &l0[idx0 & MASK], ax0); - cx1 = soft_aesenc((uint32_t*) &l1[idx1 & MASK], ax1); - cx2 = soft_aesenc((uint32_t*) &l2[idx2 & MASK], ax2); - } else { - cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - cx1 = _mm_load_si128((__m128i*) &l1[idx1 & MASK]); - cx2 = _mm_load_si128((__m128i*) &l2[idx2 & MASK]); - - cx0 = _mm_aesenc_si128(cx0, _mm_set_epi64x(ah0, al0)); - cx1 = _mm_aesenc_si128(cx1, _mm_set_epi64x(ah1, al1)); - cx2 = _mm_aesenc_si128(cx2, _mm_set_epi64x(ah2, al2)); - } - - SHUFFLE_PHASE_1(l0, (idx0&MASK), bx00, bx10, ax0, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - SHUFFLE_PHASE_1(l1, (idx1&MASK), bx01, bx11, ax1, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - SHUFFLE_PHASE_1(l2, (idx2&MASK), bx02, bx12, ax2, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - - _mm_store_si128((__m128i*) &l0[idx0 & MASK], _mm_xor_si128(bx00, cx0)); - _mm_store_si128((__m128i*) &l1[idx1 & MASK], _mm_xor_si128(bx01, cx1)); - _mm_store_si128((__m128i*) &l2[idx2 & MASK], _mm_xor_si128(bx02, cx2)); - - idx0 = EXTRACT64(cx0); - idx1 = EXTRACT64(cx1); - idx2 = EXTRACT64(cx2); - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & MASK])[0]; - ch = ((uint64_t*) &l0[idx0 & MASK])[1]; - - INTEGER_MATH_V2(0, cl, cx0); - - lo = __umul128(idx0, cl, &hi); - - SHUFFLE_PHASE_2(l0, (idx0&MASK), bx00, bx10, ax0, lo, hi, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - - al0 += hi; - ah0 += lo; - - ((uint64_t*) &l0[idx0 & MASK])[0] = al0; - ((uint64_t*) &l0[idx0 & MASK])[1] = ah0; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - bx10 = bx00; - bx00 = cx0; - - - cl = ((uint64_t*) &l1[idx1 & MASK])[0]; - ch = ((uint64_t*) &l1[idx1 & MASK])[1]; - - INTEGER_MATH_V2(1, cl, cx1); - - lo = __umul128(idx1, cl, &hi); - - SHUFFLE_PHASE_2(l1, (idx1&MASK), bx01, bx11, ax1, lo, hi, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - - al1 += hi; - ah1 += lo; - - ((uint64_t*) &l1[idx1 & MASK])[0] = al1; - ((uint64_t*) &l1[idx1 & MASK])[1] = ah1; - - ah1 ^= ch; - al1 ^= cl; - idx1 = al1; - - bx11 = bx01; - bx01 = cx1; - - - cl = ((uint64_t*) &l2[idx2 & MASK])[0]; - ch = ((uint64_t*) &l2[idx2 & MASK])[1]; - - INTEGER_MATH_V2(2, cl, cx2); - - lo = __umul128(idx2, cl, &hi); - - SHUFFLE_PHASE_2(l2, (idx2&MASK), bx02, bx12, ax2, lo, hi, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - - al2 += hi; - ah2 += lo; - - ((uint64_t*) &l2[idx2 & MASK])[0] = al2; - ((uint64_t*) &l2[idx2 & MASK])[1] = ah2; - - ah2 ^= ch; - al2 ^= cl; - idx2 = al2; - - bx12 = bx02; - bx02 = cx2; - } - - cn_implode_scratchpad((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad((__m128i*) l1, (__m128i*) h1); - cn_implode_scratchpad((__m128i*) l2, (__m128i*) h2); - - keccakf(h0, 24); - keccakf(h1, 24); - keccakf(h2, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - extra_hashes[scratchPad[1]->state[0] & 3](scratchPad[1]->state, 200, output + 32); - extra_hashes[scratchPad[2]->state[0] & 3](scratchPad[2]->state, 200, output + 64); - } - - // triple - inline static void hashPowV4(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad, - uint64_t height) - { - keccak(input, (int) size, scratchPad[0]->state, 200); - keccak(input + size, (int) size, scratchPad[1]->state, 200); - keccak(input + 2 * size, (int) size, scratchPad[2]->state, 200); - - const uint8_t* l0 = scratchPad[0]->memory; - const uint8_t* l1 = scratchPad[1]->memory; - const uint8_t* l2 = scratchPad[2]->memory; - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - uint64_t* h1 = reinterpret_cast(scratchPad[1]->state); - uint64_t* h2 = reinterpret_cast(scratchPad[2]->state); - - cn_explode_scratchpad((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad((__m128i*) h1, (__m128i*) l1); - cn_explode_scratchpad((__m128i*) h2, (__m128i*) l2); - - uint64_t al0 = h0[0] ^h0[4]; - uint64_t al1 = h1[0] ^h1[4]; - uint64_t al2 = h2[0] ^h2[4]; - uint64_t ah0 = h0[1] ^h0[5]; - uint64_t ah1 = h1[1] ^h1[5]; - uint64_t ah2 = h2[1] ^h2[5]; - - __m128i bx00 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx01 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - __m128i bx02 = _mm_set_epi64x(h2[3] ^ h2[7], h2[2] ^ h2[6]); - - __m128i bx10 = _mm_set_epi64x(h0[9] ^ h0[11], h0[8] ^ h0[10]); - __m128i bx11 = _mm_set_epi64x(h1[9] ^ h1[11], h1[8] ^ h1[10]); - __m128i bx12 = _mm_set_epi64x(h2[9] ^ h2[11], h2[8] ^ h2[10]); - - uint64_t idx0 = h0[0] ^h0[4]; - uint64_t idx1 = h1[0] ^h1[4]; - uint64_t idx2 = h2[0] ^h2[4]; - - VARIANT4_RANDOM_MATH_INIT(0, h0) - VARIANT4_RANDOM_MATH_INIT(1, h1) - VARIANT4_RANDOM_MATH_INIT(2, h2) - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx0; - __m128i cx1; - __m128i cx2; - - const __m128i ax0 = _mm_set_epi64x(ah0, al0); - const __m128i ax1 = _mm_set_epi64x(ah1, al1); - const __m128i ax2 = _mm_set_epi64x(ah2, al2); - - if (SOFT_AES) { - cx0 = soft_aesenc((uint32_t*) &l0[idx0 & MASK], ax0); - cx1 = soft_aesenc((uint32_t*) &l1[idx1 & MASK], ax1); - cx2 = soft_aesenc((uint32_t*) &l2[idx2 & MASK], ax2); - } else { - cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - cx1 = _mm_load_si128((__m128i*) &l1[idx1 & MASK]); - cx2 = _mm_load_si128((__m128i*) &l2[idx2 & MASK]); - - cx0 = _mm_aesenc_si128(cx0, _mm_set_epi64x(ah0, al0)); - cx1 = _mm_aesenc_si128(cx1, _mm_set_epi64x(ah1, al1)); - cx2 = _mm_aesenc_si128(cx2, _mm_set_epi64x(ah2, al2)); - } - - SHUFFLE_V4(l0, (idx0&MASK), bx00, bx10, ax0, cx0) - SHUFFLE_V4(l1, (idx1&MASK), bx01, bx11, ax1, cx1) - SHUFFLE_V4(l2, (idx2&MASK), bx02, bx12, ax2, cx2) - - _mm_store_si128((__m128i*) &l0[idx0 & MASK], _mm_xor_si128(bx00, cx0)); - _mm_store_si128((__m128i*) &l1[idx1 & MASK], _mm_xor_si128(bx01, cx1)); - _mm_store_si128((__m128i*) &l2[idx2 & MASK], _mm_xor_si128(bx02, cx2)); - - idx0 = EXTRACT64(cx0); - idx1 = EXTRACT64(cx1); - idx2 = EXTRACT64(cx2); - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & MASK])[0]; - ch = ((uint64_t*) &l0[idx0 & MASK])[1]; - - VARIANT4_RANDOM_MATH(0, al0, ah0, cl, bx00, bx10) - - if (VARIANT == POW_V4) { - al0 ^= r0[2] | ((uint64_t)(r0[3]) << 32); - ah0 ^= r0[0] | ((uint64_t)(r0[1]) << 32); - } - - lo = __umul128(idx0, cl, &hi); - - SHUFFLE_V4(l0, (idx0&MASK), bx00, bx10, ax0, cx0); - - al0 += hi; - ah0 += lo; - - ((uint64_t*) &l0[idx0 & MASK])[0] = al0; - ((uint64_t*) &l0[idx0 & MASK])[1] = ah0; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - bx10 = bx00; - bx00 = cx0; - - - cl = ((uint64_t*) &l1[idx1 & MASK])[0]; - ch = ((uint64_t*) &l1[idx1 & MASK])[1]; - - VARIANT4_RANDOM_MATH(1, al1, ah1, cl, bx01, bx11) - - if (VARIANT == POW_V4) { - al1 ^= r1[2] | ((uint64_t)(r1[3]) << 32); - ah1 ^= r1[0] | ((uint64_t)(r1[1]) << 32); - } - - lo = __umul128(idx1, cl, &hi); - - SHUFFLE_V4(l1, (idx1&MASK), bx01, bx11, ax1, cx1); - - al1 += hi; - ah1 += lo; - - ((uint64_t*) &l1[idx1 & MASK])[0] = al1; - ((uint64_t*) &l1[idx1 & MASK])[1] = ah1; - - ah1 ^= ch; - al1 ^= cl; - idx1 = al1; - - bx11 = bx01; - bx01 = cx1; - - - cl = ((uint64_t*) &l2[idx2 & MASK])[0]; - ch = ((uint64_t*) &l2[idx2 & MASK])[1]; - - VARIANT4_RANDOM_MATH(2, al2, ah2, cl, bx02, bx12) - - if (VARIANT == POW_V4) { - al2 ^= r2[2] | ((uint64_t)(r2[3]) << 32); - ah2 ^= r2[0] | ((uint64_t)(r2[1]) << 32); - } - - lo = __umul128(idx2, cl, &hi); - - SHUFFLE_V4(l2, (idx2&MASK), bx02, bx12, ax2, cx2) - - al2 += hi; - ah2 += lo; - - ((uint64_t*) &l2[idx2 & MASK])[0] = al2; - ((uint64_t*) &l2[idx2 & MASK])[1] = ah2; - - ah2 ^= ch; - al2 ^= cl; - idx2 = al2; - - bx12 = bx02; - bx02 = cx2; - } - - cn_implode_scratchpad((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad((__m128i*) l1, (__m128i*) h1); - cn_implode_scratchpad((__m128i*) l2, (__m128i*) h2); - - keccakf(h0, 24); - keccakf(h1, 24); - keccakf(h2, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - extra_hashes[scratchPad[1]->state[0] & 3](scratchPad[1]->state, 200, output + 32); - extra_hashes[scratchPad[2]->state[0] & 3](scratchPad[2]->state, 200, output + 64); - } - - inline static void hashLiteTube(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - keccak(input, (int) size, scratchPad[0]->state, 200); - keccak(input + size, (int) size, scratchPad[1]->state, 200); - keccak(input + 2 * size, (int) size, scratchPad[2]->state, 200); - - uint64_t tweak1_2_0 = (*reinterpret_cast(input + 35) ^ - *(reinterpret_cast(scratchPad[0]->state) + 24)); - uint64_t tweak1_2_1 = (*reinterpret_cast(input + 35 + size) ^ - *(reinterpret_cast(scratchPad[1]->state) + 24)); - uint64_t tweak1_2_2 = (*reinterpret_cast(input + 35 + 2 * size) ^ - *(reinterpret_cast(scratchPad[2]->state) + 24)); - - const uint8_t* l0 = scratchPad[0]->memory; - const uint8_t* l1 = scratchPad[1]->memory; - const uint8_t* l2 = scratchPad[2]->memory; - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - uint64_t* h1 = reinterpret_cast(scratchPad[1]->state); - uint64_t* h2 = reinterpret_cast(scratchPad[2]->state); - - cn_explode_scratchpad((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad((__m128i*) h1, (__m128i*) l1); - cn_explode_scratchpad((__m128i*) h2, (__m128i*) l2); - - uint64_t al0 = h0[0] ^h0[4]; - uint64_t al1 = h1[0] ^h1[4]; - uint64_t al2 = h2[0] ^h2[4]; - uint64_t ah0 = h0[1] ^h0[5]; - uint64_t ah1 = h1[1] ^h1[5]; - uint64_t ah2 = h2[1] ^h2[5]; - - __m128i bx0 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx1 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - __m128i bx2 = _mm_set_epi64x(h2[3] ^ h2[7], h2[2] ^ h2[6]); - - uint64_t idx0 = h0[0] ^h0[4]; - uint64_t idx1 = h1[0] ^h1[4]; - uint64_t idx2 = h2[0] ^h2[4]; - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx0; - __m128i cx1; - __m128i cx2; - - if (SOFT_AES) { - cx0 = soft_aesenc((uint32_t*) &l0[idx0 & MASK], _mm_set_epi64x(ah0, al0)); - cx1 = soft_aesenc((uint32_t*) &l1[idx1 & MASK], _mm_set_epi64x(ah1, al1)); - cx2 = soft_aesenc((uint32_t*) &l2[idx2 & MASK], _mm_set_epi64x(ah2, al2)); - } else { - cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - cx1 = _mm_load_si128((__m128i*) &l1[idx1 & MASK]); - cx2 = _mm_load_si128((__m128i*) &l2[idx2 & MASK]); - - cx0 = _mm_aesenc_si128(cx0, _mm_set_epi64x(ah0, al0)); - cx1 = _mm_aesenc_si128(cx1, _mm_set_epi64x(ah1, al1)); - cx2 = _mm_aesenc_si128(cx2, _mm_set_epi64x(ah2, al2)); - } - - _mm_store_si128((__m128i*) &l0[idx0 & MASK], _mm_xor_si128(bx0, cx0)); - _mm_store_si128((__m128i*) &l1[idx1 & MASK], _mm_xor_si128(bx1, cx1)); - _mm_store_si128((__m128i*) &l2[idx2 & MASK], _mm_xor_si128(bx2, cx2)); - - static const uint32_t table = 0x75310; - uint8_t tmp = reinterpret_cast(&l0[idx0 & MASK])[11]; - uint8_t index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*) (&l0[idx0 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - tmp = reinterpret_cast(&l1[idx1 & MASK])[11]; - index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*) (&l1[idx1 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - tmp = reinterpret_cast(&l2[idx2 & MASK])[11]; - index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*) (&l2[idx2 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - - idx0 = EXTRACT64(cx0); - idx1 = EXTRACT64(cx1); - idx2 = EXTRACT64(cx2); - - bx0 = cx0; - bx1 = cx1; - bx2 = cx2; - - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & MASK])[0]; - ch = ((uint64_t*) &l0[idx0 & MASK])[1]; - lo = __umul128(idx0, cl, &hi); - - al0 += hi; - ah0 += lo; - - ah0 ^= tweak1_2_0; - ((uint64_t*) &l0[idx0 & MASK])[0] = al0; - ((uint64_t*) &l0[idx0 & MASK])[1] = ah0; - ah0 ^= tweak1_2_0; - - ((uint64_t*) &l0[idx0 & MASK])[1] ^= ((uint64_t*) &l0[idx0 & MASK])[0]; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - - cl = ((uint64_t*) &l1[idx1 & MASK])[0]; - ch = ((uint64_t*) &l1[idx1 & MASK])[1]; - lo = __umul128(idx1, cl, &hi); - - al1 += hi; - ah1 += lo; - - ah1 ^= tweak1_2_1; - ((uint64_t*) &l1[idx1 & MASK])[0] = al1; - ((uint64_t*) &l1[idx1 & MASK])[1] = ah1; - ah1 ^= tweak1_2_1; - - ((uint64_t*) &l1[idx1 & MASK])[1] ^= ((uint64_t*) &l1[idx1 & MASK])[0]; - - ah1 ^= ch; - al1 ^= cl; - idx1 = al1; - - - cl = ((uint64_t*) &l2[idx2 & MASK])[0]; - ch = ((uint64_t*) &l2[idx2 & MASK])[1]; - lo = __umul128(idx2, cl, &hi); - - al2 += hi; - ah2 += lo; - - ah2 ^= tweak1_2_2; - ((uint64_t*) &l2[idx2 & MASK])[0] = al2; - ((uint64_t*) &l2[idx2 & MASK])[1] = ah2; - ah2 ^= tweak1_2_2; - - ((uint64_t*) &l2[idx2 & MASK])[1] ^= ((uint64_t*) &l2[idx2 & MASK])[0]; - - ah2 ^= ch; - al2 ^= cl; - idx2 = al2; - } - - cn_implode_scratchpad((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad((__m128i*) l1, (__m128i*) h1); - cn_implode_scratchpad((__m128i*) l2, (__m128i*) h2); - - keccakf(h0, 24); - keccakf(h1, 24); - keccakf(h2, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - extra_hashes[scratchPad[1]->state[0] & 3](scratchPad[1]->state, 200, output + 32); - extra_hashes[scratchPad[2]->state[0] & 3](scratchPad[2]->state, 200, output + 64); - } - - inline static void hashHeavy(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - keccak(input, (int) size, scratchPad[0]->state, 200); - keccak(input + size, (int) size, scratchPad[1]->state, 200); - keccak(input + 2 * size, (int) size, scratchPad[2]->state, 200); - - const uint8_t* l0 = scratchPad[0]->memory; - const uint8_t* l1 = scratchPad[1]->memory; - const uint8_t* l2 = scratchPad[2]->memory; - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - uint64_t* h1 = reinterpret_cast(scratchPad[1]->state); - uint64_t* h2 = reinterpret_cast(scratchPad[2]->state); - - cn_explode_scratchpad_heavy((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad_heavy((__m128i*) h1, (__m128i*) l1); - cn_explode_scratchpad_heavy((__m128i*) h2, (__m128i*) l2); - - uint64_t al0 = h0[0] ^h0[4]; - uint64_t al1 = h1[0] ^h1[4]; - uint64_t al2 = h2[0] ^h2[4]; - uint64_t ah0 = h0[1] ^h0[5]; - uint64_t ah1 = h1[1] ^h1[5]; - uint64_t ah2 = h2[1] ^h2[5]; - - __m128i bx0 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx1 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - __m128i bx2 = _mm_set_epi64x(h2[3] ^ h2[7], h2[2] ^ h2[6]); - - uint64_t idx0 = h0[0] ^h0[4]; - uint64_t idx1 = h1[0] ^h1[4]; - uint64_t idx2 = h2[0] ^h2[4]; - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx0; - __m128i cx1; - __m128i cx2; - - if (SOFT_AES) { - cx0 = soft_aesenc((uint32_t*) &l0[idx0 & MASK], _mm_set_epi64x(ah0, al0)); - cx1 = soft_aesenc((uint32_t*) &l1[idx1 & MASK], _mm_set_epi64x(ah1, al1)); - cx2 = soft_aesenc((uint32_t*) &l2[idx2 & MASK], _mm_set_epi64x(ah2, al2)); - } else { - cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - cx1 = _mm_load_si128((__m128i*) &l1[idx1 & MASK]); - cx2 = _mm_load_si128((__m128i*) &l2[idx2 & MASK]); - - cx0 = _mm_aesenc_si128(cx0, _mm_set_epi64x(ah0, al0)); - cx1 = _mm_aesenc_si128(cx1, _mm_set_epi64x(ah1, al1)); - cx2 = _mm_aesenc_si128(cx2, _mm_set_epi64x(ah2, al2)); - } - - _mm_store_si128((__m128i*) &l0[idx0 & MASK], _mm_xor_si128(bx0, cx0)); - _mm_store_si128((__m128i*) &l1[idx1 & MASK], _mm_xor_si128(bx1, cx1)); - _mm_store_si128((__m128i*) &l2[idx2 & MASK], _mm_xor_si128(bx2, cx2)); - - idx0 = EXTRACT64(cx0); - idx1 = EXTRACT64(cx1); - idx2 = EXTRACT64(cx2); - - bx0 = cx0; - bx1 = cx1; - bx2 = cx2; - - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & MASK])[0]; - ch = ((uint64_t*) &l0[idx0 & MASK])[1]; - lo = __umul128(idx0, cl, &hi); - - al0 += hi; - ah0 += lo; - - ((uint64_t*) &l0[idx0 & MASK])[0] = al0; - ((uint64_t*) &l0[idx0 & MASK])[1] = ah0; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - const int64x2_t x0 = vld1q_s64(reinterpret_cast(&l0[idx0 & MASK])); - const int64_t n0 = vgetq_lane_s64(x0, 0); - const int32_t d0 = vgetq_lane_s32(x0, 2); - const int64_t q0 = n0 / (d0 | 0x5); - - ((int64_t*) &l0[idx0 & MASK])[0] = n0 ^ q0; - - if (VARIANT == POW_XHV || VARIANT == POW_XFH) { - idx0 = (~d0) ^ q0; - } else { - idx0 = d0 ^ q0; - } - - cl = ((uint64_t*) &l1[idx1 & MASK])[0]; - ch = ((uint64_t*) &l1[idx1 & MASK])[1]; - lo = __umul128(idx1, cl, &hi); - - al1 += hi; - ah1 += lo; - - ((uint64_t*) &l1[idx1 & MASK])[0] = al1; - ((uint64_t*) &l1[idx1 & MASK])[1] = ah1; - - ah1 ^= ch; - al1 ^= cl; - idx1 = al1; - - const int64x2_t x1 = vld1q_s64(reinterpret_cast(&l1[idx1 & MASK])); - const int64_t n1 = vgetq_lane_s64(x1, 0); - const int32_t d1 = vgetq_lane_s32(x1, 2); - const int64_t q1 = n1 / (d1 | 0x5); - - ((int64_t*) &l1[idx1 & MASK])[0] = n1 ^ q1; - - if (VARIANT == POW_XHV || VARIANT == POW_XFH) { - idx1 = (~d1) ^ q1; - } else { - idx1 = d1 ^ q1; - } - - cl = ((uint64_t*) &l2[idx2 & MASK])[0]; - ch = ((uint64_t*) &l2[idx2 & MASK])[1]; - lo = __umul128(idx2, cl, &hi); - - al2 += hi; - ah2 += lo; - - ((uint64_t*) &l2[idx2 & MASK])[0] = al2; - ((uint64_t*) &l2[idx2 & MASK])[1] = ah2; - - ah2 ^= ch; - al2 ^= cl; - idx2 = al2; - - - const int64x2_t x2 = vld1q_s64(reinterpret_cast(&l2[idx2 & MASK])); - const int64_t n2 = vgetq_lane_s64(x2, 0); - const int32_t d2 = vgetq_lane_s32(x2, 2); - const int64_t q2 = n2 / (d2 | 0x5); - - ((int64_t*) &l2[idx2 & MASK])[0] = n2 ^ q2; - - if (VARIANT == POW_XHV || VARIANT == POW_XFH) { - idx2 = (~d2) ^ q2; - } else { - idx2 = d2 ^ q2; - } - } - - cn_implode_scratchpad_heavy((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad_heavy((__m128i*) l1, (__m128i*) h1); - cn_implode_scratchpad_heavy((__m128i*) l2, (__m128i*) h2); - - keccakf(h0, 24); - keccakf(h1, 24); - keccakf(h2, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - extra_hashes[scratchPad[1]->state[0] & 3](scratchPad[1]->state, 200, output + 32); - extra_hashes[scratchPad[2]->state[0] & 3](scratchPad[2]->state, 200, output + 64); - } - - inline static void hashHeavyTube(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - keccak((const uint8_t*) input, (int) size, scratchPad[0]->state, 200); - keccak((const uint8_t*) input + size, (int) size, scratchPad[1]->state, 200); - keccak((const uint8_t*) input + 2 * size, (int) size, scratchPad[2]->state, 200); - - uint64_t tweak1_2_0 = (*reinterpret_cast(reinterpret_cast(input) + 35) ^ - *(reinterpret_cast(scratchPad[0]->state) + 24)); - uint64_t tweak1_2_1 = (*reinterpret_cast(reinterpret_cast(input) + 35 + size) ^ - *(reinterpret_cast(scratchPad[1]->state) + 24)); - uint64_t tweak1_2_2 = (*reinterpret_cast(reinterpret_cast(input) + 35 + 2 * size) ^ - *(reinterpret_cast(scratchPad[2]->state) + 24)); - - const uint8_t* l0 = scratchPad[0]->memory; - const uint8_t* l1 = scratchPad[1]->memory; - const uint8_t* l2 = scratchPad[2]->memory; - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - uint64_t* h1 = reinterpret_cast(scratchPad[1]->state); - uint64_t* h2 = reinterpret_cast(scratchPad[2]->state); - - cn_explode_scratchpad_heavy((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad_heavy((__m128i*) h1, (__m128i*) l1); - cn_explode_scratchpad_heavy((__m128i*) h2, (__m128i*) l2); - - uint64_t al0 = h0[0] ^h0[4]; - uint64_t al1 = h1[0] ^h1[4]; - uint64_t al2 = h2[0] ^h2[4]; - uint64_t ah0 = h0[1] ^h0[5]; - uint64_t ah1 = h1[1] ^h1[5]; - uint64_t ah2 = h2[1] ^h2[5]; - - __m128i bx0 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx1 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - __m128i bx2 = _mm_set_epi64x(h2[3] ^ h2[7], h2[2] ^ h2[6]); - - uint64_t idx0 = h0[0] ^h0[4]; - uint64_t idx1 = h1[0] ^h1[4]; - uint64_t idx2 = h2[0] ^h2[4]; - - union alignas(16) - { - uint32_t k[4]; - uint64_t v64[2]; - }; - alignas(16) uint32_t x[4]; - -#define BYTE(p, i) ((unsigned char*)&p)[i] - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - __m128i cx1 = _mm_load_si128((__m128i*) &l1[idx1 & MASK]); - __m128i cx2 = _mm_load_si128((__m128i*) &l2[idx2 & MASK]); - - const __m128i& key0 = _mm_set_epi64x(ah0, al0); - - _mm_store_si128((__m128i*) k, key0); - cx0 = _mm_xor_si128(cx0, _mm_cmpeq_epi32(_mm_setzero_si128(), _mm_setzero_si128())); - _mm_store_si128((__m128i*) x, cx0); - - k[0] ^= saes_table[0][BYTE(x[0], 0)] ^ saes_table[1][BYTE(x[1], 1)] ^ saes_table[2][BYTE(x[2], 2)] ^ - saes_table[3][BYTE(x[3], 3)]; - x[0] ^= k[0]; - k[1] ^= saes_table[0][BYTE(x[1], 0)] ^ saes_table[1][BYTE(x[2], 1)] ^ saes_table[2][BYTE(x[3], 2)] ^ - saes_table[3][BYTE(x[0], 3)]; - x[1] ^= k[1]; - k[2] ^= saes_table[0][BYTE(x[2], 0)] ^ saes_table[1][BYTE(x[3], 1)] ^ saes_table[2][BYTE(x[0], 2)] ^ - saes_table[3][BYTE(x[1], 3)]; - x[2] ^= k[2]; - k[3] ^= saes_table[0][BYTE(x[3], 0)] ^ saes_table[1][BYTE(x[0], 1)] ^ saes_table[2][BYTE(x[1], 2)] ^ - saes_table[3][BYTE(x[2], 3)]; - - cx0 = _mm_load_si128((__m128i*) k); - - const __m128i& key1 = _mm_set_epi64x(ah1, al1); - - _mm_store_si128((__m128i*) k, key1); - cx1 = _mm_xor_si128(cx1, _mm_cmpeq_epi32(_mm_setzero_si128(), _mm_setzero_si128())); - _mm_store_si128((__m128i*) x, cx1); - - k[0] ^= saes_table[0][BYTE(x[0], 0)] ^ saes_table[1][BYTE(x[1], 1)] ^ saes_table[2][BYTE(x[2], 2)] ^ - saes_table[3][BYTE(x[3], 3)]; - x[0] ^= k[0]; - k[1] ^= saes_table[0][BYTE(x[1], 0)] ^ saes_table[1][BYTE(x[2], 1)] ^ saes_table[2][BYTE(x[3], 2)] ^ - saes_table[3][BYTE(x[0], 3)]; - x[1] ^= k[1]; - k[2] ^= saes_table[0][BYTE(x[2], 0)] ^ saes_table[1][BYTE(x[3], 1)] ^ saes_table[2][BYTE(x[0], 2)] ^ - saes_table[3][BYTE(x[1], 3)]; - x[2] ^= k[2]; - k[3] ^= saes_table[0][BYTE(x[3], 0)] ^ saes_table[1][BYTE(x[0], 1)] ^ saes_table[2][BYTE(x[1], 2)] ^ - saes_table[3][BYTE(x[2], 3)]; - - cx1 = _mm_load_si128((__m128i*) k); - - const __m128i& key2 = _mm_set_epi64x(ah2, al2); - - _mm_store_si128((__m128i*) k, key2); - cx2 = _mm_xor_si128(cx2, _mm_cmpeq_epi32(_mm_setzero_si128(), _mm_setzero_si128())); - _mm_store_si128((__m128i*) x, cx2); - - k[0] ^= saes_table[0][BYTE(x[0], 0)] ^ saes_table[1][BYTE(x[1], 1)] ^ saes_table[2][BYTE(x[2], 2)] ^ - saes_table[3][BYTE(x[3], 3)]; - x[0] ^= k[0]; - k[1] ^= saes_table[0][BYTE(x[1], 0)] ^ saes_table[1][BYTE(x[2], 1)] ^ saes_table[2][BYTE(x[3], 2)] ^ - saes_table[3][BYTE(x[0], 3)]; - x[1] ^= k[1]; - k[2] ^= saes_table[0][BYTE(x[2], 0)] ^ saes_table[1][BYTE(x[3], 1)] ^ saes_table[2][BYTE(x[0], 2)] ^ - saes_table[3][BYTE(x[1], 3)]; - x[2] ^= k[2]; - k[3] ^= saes_table[0][BYTE(x[3], 0)] ^ saes_table[1][BYTE(x[0], 1)] ^ saes_table[2][BYTE(x[1], 2)] ^ - saes_table[3][BYTE(x[2], 3)]; - - cx2 = _mm_load_si128((__m128i*) k); - - _mm_store_si128((__m128i*) &l0[idx0 & MASK], _mm_xor_si128(bx0, cx0)); - _mm_store_si128((__m128i*) &l1[idx1 & MASK], _mm_xor_si128(bx1, cx1)); - _mm_store_si128((__m128i*) &l2[idx2 & MASK], _mm_xor_si128(bx2, cx2)); - - static const uint32_t table = 0x75310; - uint8_t tmp = reinterpret_cast(&l0[idx0 & MASK])[11]; - uint8_t index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*) (&l0[idx0 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - tmp = reinterpret_cast(&l1[idx1 & MASK])[11]; - index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*) (&l1[idx1 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - tmp = reinterpret_cast(&l2[idx2 & MASK])[11]; - index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*) (&l2[idx2 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - - idx0 = EXTRACT64(cx0); - idx1 = EXTRACT64(cx1); - idx2 = EXTRACT64(cx2); - - bx0 = cx0; - bx1 = cx1; - bx2 = cx2; - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & MASK])[0]; - ch = ((uint64_t*) &l0[idx0 & MASK])[1]; - lo = __umul128(idx0, cl, &hi); - - al0 += hi; - ah0 += lo; - - ah0 ^= tweak1_2_0; - ((uint64_t*) &l0[idx0 & MASK])[0] = al0; - ((uint64_t*) &l0[idx0 & MASK])[1] = ah0; - ah0 ^= tweak1_2_0; - - ((uint64_t*) &l0[idx0 & MASK])[1] ^= ((uint64_t*) &l0[idx0 & MASK])[0]; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - const int64x2_t x0 = vld1q_s64(reinterpret_cast(&l0[idx0 & MASK])); - const int64_t n0 = vgetq_lane_s64(x0, 0); - const int32_t d0 = vgetq_lane_s32(x0, 2); - const int64_t q0 = n0 / (d0 | 0x5); - - ((int64_t*) &l0[idx0 & MASK])[0] = n0 ^ q0; - - idx0 = d0 ^ q0; - - - cl = ((uint64_t*) &l1[idx1 & MASK])[0]; - ch = ((uint64_t*) &l1[idx1 & MASK])[1]; - lo = __umul128(idx1, cl, &hi); - - al1 += hi; - ah1 += lo; - - ah1 ^= tweak1_2_1; - ((uint64_t*) &l1[idx1 & MASK])[0] = al1; - ((uint64_t*) &l1[idx1 & MASK])[1] = ah1; - ah1 ^= tweak1_2_1; - - ((uint64_t*) &l1[idx1 & MASK])[1] ^= ((uint64_t*) &l1[idx1 & MASK])[0]; - - ah1 ^= ch; - al1 ^= cl; - idx1 = al1; - - const int64x2_t x1 = vld1q_s64(reinterpret_cast(&l1[idx1 & MASK])); - const int64_t n1 = vgetq_lane_s64(x1, 0); - const int32_t d1 = vgetq_lane_s32(x1, 2); - const int64_t q1 = n1 / (d1 | 0x5); - - ((int64_t*) &l1[idx1 & MASK])[0] = n1 ^ q1; - - idx1 = d1 ^ q1; - - - cl = ((uint64_t*) &l2[idx2 & MASK])[0]; - ch = ((uint64_t*) &l2[idx2 & MASK])[1]; - lo = __umul128(idx2, cl, &hi); - - al2 += hi; - ah2 += lo; - - ah2 ^= tweak1_2_2; - ((uint64_t*) &l2[idx2 & MASK])[0] = al2; - ((uint64_t*) &l2[idx2 & MASK])[1] = ah2; - ah2 ^= tweak1_2_2; - - ((uint64_t*) &l2[idx2 & MASK])[1] ^= ((uint64_t*) &l2[idx2 & MASK])[0]; - - ah2 ^= ch; - al2 ^= cl; - idx2 = al2; - - const int64x2_t x2 = vld1q_s64(reinterpret_cast(&l2[idx2 & MASK])); - const int64_t n2 = vgetq_lane_s64(x2, 0); - const int32_t d2 = vgetq_lane_s32(x2, 2); - const int64_t q2 = n2 / (d2 | 0x5); - - ((int64_t*) &l2[idx2 & MASK])[0] = n2 ^ q2; - - idx2 = d2 ^ q2; - } -#undef BYTE - - cn_implode_scratchpad_heavy((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad_heavy((__m128i*) l1, (__m128i*) h1); - cn_implode_scratchpad_heavy((__m128i*) l2, (__m128i*) h2); - - keccakf(h0, 24); - keccakf(h1, 24); - keccakf(h2, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - extra_hashes[scratchPad[1]->state[0] & 3](scratchPad[1]->state, 200, output + 32); - extra_hashes[scratchPad[2]->state[0] & 3](scratchPad[2]->state, 200, output + 64); - } -}; - -template -class CryptoNightMultiHash -{ -public: - inline static void hash(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - keccak(input, (int) size, scratchPad[0]->state, 200); - keccak(input + size, (int) size, scratchPad[1]->state, 200); - keccak(input + 2 * size, (int) size, scratchPad[2]->state, 200); - keccak(input + 3 * size, (int) size, scratchPad[3]->state, 200); - - const uint8_t* l0 = scratchPad[0]->memory; - const uint8_t* l1 = scratchPad[1]->memory; - const uint8_t* l2 = scratchPad[2]->memory; - const uint8_t* l3 = scratchPad[3]->memory; - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - uint64_t* h1 = reinterpret_cast(scratchPad[1]->state); - uint64_t* h2 = reinterpret_cast(scratchPad[2]->state); - uint64_t* h3 = reinterpret_cast(scratchPad[3]->state); - - cn_explode_scratchpad((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad((__m128i*) h1, (__m128i*) l1); - cn_explode_scratchpad((__m128i*) h2, (__m128i*) l2); - cn_explode_scratchpad((__m128i*) h3, (__m128i*) l3); - - uint64_t al0 = h0[0] ^h0[4]; - uint64_t al1 = h1[0] ^h1[4]; - uint64_t al2 = h2[0] ^h2[4]; - uint64_t al3 = h3[0] ^h3[4]; - uint64_t ah0 = h0[1] ^h0[5]; - uint64_t ah1 = h1[1] ^h1[5]; - uint64_t ah2 = h2[1] ^h2[5]; - uint64_t ah3 = h3[1] ^h3[5]; - - __m128i bx0 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx1 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - __m128i bx2 = _mm_set_epi64x(h2[3] ^ h2[7], h2[2] ^ h2[6]); - __m128i bx3 = _mm_set_epi64x(h3[3] ^ h3[7], h3[2] ^ h3[6]); - - uint64_t idx0 = h0[0] ^h0[4]; - uint64_t idx1 = h1[0] ^h1[4]; - uint64_t idx2 = h2[0] ^h2[4]; - uint64_t idx3 = h3[0] ^h3[4]; - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx0; - __m128i cx1; - __m128i cx2; - __m128i cx3; - - if (SOFT_AES) { - cx0 = soft_aesenc((uint32_t*) &l0[idx0 & MASK], _mm_set_epi64x(ah0, al0)); - cx1 = soft_aesenc((uint32_t*) &l1[idx1 & MASK], _mm_set_epi64x(ah1, al1)); - cx2 = soft_aesenc((uint32_t*) &l2[idx2 & MASK], _mm_set_epi64x(ah2, al2)); - cx3 = soft_aesenc((uint32_t*) &l3[idx3 & MASK], _mm_set_epi64x(ah3, al3)); - } else { - cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - cx1 = _mm_load_si128((__m128i*) &l1[idx1 & MASK]); - cx2 = _mm_load_si128((__m128i*) &l2[idx2 & MASK]); - cx3 = _mm_load_si128((__m128i*) &l3[idx3 & MASK]); - - cx0 = _mm_aesenc_si128(cx0, _mm_set_epi64x(ah0, al0)); - cx1 = _mm_aesenc_si128(cx1, _mm_set_epi64x(ah1, al1)); - cx2 = _mm_aesenc_si128(cx2, _mm_set_epi64x(ah2, al2)); - cx3 = _mm_aesenc_si128(cx3, _mm_set_epi64x(ah3, al3)); - } - - _mm_store_si128((__m128i*) &l0[idx0 & MASK], _mm_xor_si128(bx0, cx0)); - _mm_store_si128((__m128i*) &l1[idx1 & MASK], _mm_xor_si128(bx1, cx1)); - _mm_store_si128((__m128i*) &l2[idx2 & MASK], _mm_xor_si128(bx2, cx2)); - _mm_store_si128((__m128i*) &l3[idx3 & MASK], _mm_xor_si128(bx3, cx3)); - - idx0 = EXTRACT64(cx0); - idx1 = EXTRACT64(cx1); - idx2 = EXTRACT64(cx2); - idx3 = EXTRACT64(cx3); - - bx0 = cx0; - bx1 = cx1; - bx2 = cx2; - bx3 = cx3; - - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & MASK])[0]; - ch = ((uint64_t*) &l0[idx0 & MASK])[1]; - lo = __umul128(idx0, cl, &hi); - - al0 += hi; - ah0 += lo; - - ((uint64_t*) &l0[idx0 & MASK])[0] = al0; - ((uint64_t*) &l0[idx0 & MASK])[1] = ah0; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - - cl = ((uint64_t*) &l1[idx1 & MASK])[0]; - ch = ((uint64_t*) &l1[idx1 & MASK])[1]; - lo = __umul128(idx1, cl, &hi); - - al1 += hi; - ah1 += lo; - - ((uint64_t*) &l1[idx1 & MASK])[0] = al1; - ((uint64_t*) &l1[idx1 & MASK])[1] = ah1; - - ah1 ^= ch; - al1 ^= cl; - idx1 = al1; - - - cl = ((uint64_t*) &l2[idx2 & MASK])[0]; - ch = ((uint64_t*) &l2[idx2 & MASK])[1]; - lo = __umul128(idx2, cl, &hi); - - al2 += hi; - ah2 += lo; - - ((uint64_t*) &l2[idx2 & MASK])[0] = al2; - ((uint64_t*) &l2[idx2 & MASK])[1] = ah2; - - ah2 ^= ch; - al2 ^= cl; - idx2 = al2; - - - cl = ((uint64_t*) &l3[idx3 & MASK])[0]; - ch = ((uint64_t*) &l3[idx3 & MASK])[1]; - lo = __umul128(idx3, cl, &hi); - - al3 += hi; - ah3 += lo; - - ((uint64_t*) &l3[idx3 & MASK])[0] = al3; - ((uint64_t*) &l3[idx3 & MASK])[1] = ah3; - - ah3 ^= ch; - al3 ^= cl; - idx3 = al3; - } - - cn_implode_scratchpad((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad((__m128i*) l1, (__m128i*) h1); - cn_implode_scratchpad((__m128i*) l2, (__m128i*) h2); - cn_implode_scratchpad((__m128i*) l3, (__m128i*) h3); - - keccakf(h0, 24); - keccakf(h1, 24); - keccakf(h2, 24); - keccakf(h3, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - extra_hashes[scratchPad[1]->state[0] & 3](scratchPad[1]->state, 200, output + 32); - extra_hashes[scratchPad[2]->state[0] & 3](scratchPad[2]->state, 200, output + 64); - extra_hashes[scratchPad[3]->state[0] & 3](scratchPad[3]->state, 200, output + 96); - } - - inline static void hashPowV2(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - keccak(input, (int) size, scratchPad[0]->state, 200); - keccak(input + size, (int) size, scratchPad[1]->state, 200); - keccak(input + 2 * size, (int) size, scratchPad[2]->state, 200); - keccak(input + 3 * size, (int) size, scratchPad[3]->state, 200); - - uint64_t tweak1_2_0 = (*reinterpret_cast(input + 35) ^ - *(reinterpret_cast(scratchPad[0]->state) + 24)); - uint64_t tweak1_2_1 = (*reinterpret_cast(input + 35 + size) ^ - *(reinterpret_cast(scratchPad[1]->state) + 24)); - uint64_t tweak1_2_2 = (*reinterpret_cast(input + 35 + 2 * size) ^ - *(reinterpret_cast(scratchPad[2]->state) + 24)); - uint64_t tweak1_2_3 = (*reinterpret_cast(input + 35 + 3 * size) ^ - *(reinterpret_cast(scratchPad[3]->state) + 24)); - - const uint8_t* l0 = scratchPad[0]->memory; - const uint8_t* l1 = scratchPad[1]->memory; - const uint8_t* l2 = scratchPad[2]->memory; - const uint8_t* l3 = scratchPad[3]->memory; - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - uint64_t* h1 = reinterpret_cast(scratchPad[1]->state); - uint64_t* h2 = reinterpret_cast(scratchPad[2]->state); - uint64_t* h3 = reinterpret_cast(scratchPad[3]->state); - - cn_explode_scratchpad((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad((__m128i*) h1, (__m128i*) l1); - cn_explode_scratchpad((__m128i*) h2, (__m128i*) l2); - cn_explode_scratchpad((__m128i*) h3, (__m128i*) l3); - - uint64_t al0 = h0[0] ^h0[4]; - uint64_t al1 = h1[0] ^h1[4]; - uint64_t al2 = h2[0] ^h2[4]; - uint64_t al3 = h3[0] ^h3[4]; - uint64_t ah0 = h0[1] ^h0[5]; - uint64_t ah1 = h1[1] ^h1[5]; - uint64_t ah2 = h2[1] ^h2[5]; - uint64_t ah3 = h3[1] ^h3[5]; - - __m128i bx0 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx1 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - __m128i bx2 = _mm_set_epi64x(h2[3] ^ h2[7], h2[2] ^ h2[6]); - __m128i bx3 = _mm_set_epi64x(h3[3] ^ h3[7], h3[2] ^ h3[6]); - - uint64_t idx0 = h0[0] ^h0[4]; - uint64_t idx1 = h1[0] ^h1[4]; - uint64_t idx2 = h2[0] ^h2[4]; - uint64_t idx3 = h3[0] ^h3[4]; - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx0; - __m128i cx1; - __m128i cx2; - __m128i cx3; - - if (SOFT_AES) { - cx0 = soft_aesenc((uint32_t*) &l0[idx0 & MASK], _mm_set_epi64x(ah0, al0)); - cx1 = soft_aesenc((uint32_t*) &l1[idx1 & MASK], _mm_set_epi64x(ah1, al1)); - cx2 = soft_aesenc((uint32_t*) &l2[idx2 & MASK], _mm_set_epi64x(ah2, al2)); - cx3 = soft_aesenc((uint32_t*) &l3[idx3 & MASK], _mm_set_epi64x(ah3, al3)); - } else { - cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - cx1 = _mm_load_si128((__m128i*) &l1[idx1 & MASK]); - cx2 = _mm_load_si128((__m128i*) &l2[idx2 & MASK]); - cx3 = _mm_load_si128((__m128i*) &l3[idx3 & MASK]); - - cx0 = _mm_aesenc_si128(cx0, _mm_set_epi64x(ah0, al0)); - cx1 = _mm_aesenc_si128(cx1, _mm_set_epi64x(ah1, al1)); - cx2 = _mm_aesenc_si128(cx2, _mm_set_epi64x(ah2, al2)); - cx3 = _mm_aesenc_si128(cx3, _mm_set_epi64x(ah3, al3)); - } - - _mm_store_si128((__m128i*) &l0[idx0 & MASK], _mm_xor_si128(bx0, cx0)); - _mm_store_si128((__m128i*) &l1[idx1 & MASK], _mm_xor_si128(bx1, cx1)); - _mm_store_si128((__m128i*) &l2[idx2 & MASK], _mm_xor_si128(bx2, cx2)); - _mm_store_si128((__m128i*) &l3[idx3 & MASK], _mm_xor_si128(bx3, cx3)); - - static const uint32_t table = 0x75310; - uint8_t tmp = reinterpret_cast(&l0[idx0 & MASK])[11]; - uint8_t index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*) (&l0[idx0 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - tmp = reinterpret_cast(&l1[idx1 & MASK])[11]; - index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*) (&l1[idx1 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - tmp = reinterpret_cast(&l2[idx2 & MASK])[11]; - index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*) (&l2[idx2 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - tmp = reinterpret_cast(&l3[idx3 & MASK])[11]; - index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*) (&l3[idx3 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - - idx0 = EXTRACT64(cx0); - idx1 = EXTRACT64(cx1); - idx2 = EXTRACT64(cx2); - idx3 = EXTRACT64(cx3); - - bx0 = cx0; - bx1 = cx1; - bx2 = cx2; - bx3 = cx3; - - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & MASK])[0]; - ch = ((uint64_t*) &l0[idx0 & MASK])[1]; - lo = __umul128(idx0, cl, &hi); - - al0 += hi; - ah0 += lo; - - ah0 ^= tweak1_2_0; - ((uint64_t*) &l0[idx0 & MASK])[0] = al0; - ((uint64_t*) &l0[idx0 & MASK])[1] = ah0; - ah0 ^= tweak1_2_0; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - - cl = ((uint64_t*) &l1[idx1 & MASK])[0]; - ch = ((uint64_t*) &l1[idx1 & MASK])[1]; - lo = __umul128(idx1, cl, &hi); - - al1 += hi; - ah1 += lo; - - ah1 ^= tweak1_2_1; - ((uint64_t*) &l1[idx1 & MASK])[0] = al1; - ((uint64_t*) &l1[idx1 & MASK])[1] = ah1; - ah1 ^= tweak1_2_1; - - ah1 ^= ch; - al1 ^= cl; - idx1 = al1; - - - cl = ((uint64_t*) &l2[idx2 & MASK])[0]; - ch = ((uint64_t*) &l2[idx2 & MASK])[1]; - lo = __umul128(idx2, cl, &hi); - - al2 += hi; - ah2 += lo; - - ah2 ^= tweak1_2_2; - ((uint64_t*) &l2[idx2 & MASK])[0] = al2; - ((uint64_t*) &l2[idx2 & MASK])[1] = ah2; - ah2 ^= tweak1_2_2; - - ah2 ^= ch; - al2 ^= cl; - idx2 = al2; - - - cl = ((uint64_t*) &l3[idx3 & MASK])[0]; - ch = ((uint64_t*) &l3[idx3 & MASK])[1]; - lo = __umul128(idx3, cl, &hi); - - al3 += hi; - ah3 += lo; - - ah3 ^= tweak1_2_3; - ((uint64_t*) &l3[idx3 & MASK])[0] = al3; - ((uint64_t*) &l3[idx3 & MASK])[1] = ah3; - ah3 ^= tweak1_2_3; - - ah3 ^= ch; - al3 ^= cl; - idx3 = al3; - } - - cn_implode_scratchpad((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad((__m128i*) l1, (__m128i*) h1); - cn_implode_scratchpad((__m128i*) l2, (__m128i*) h2); - cn_implode_scratchpad((__m128i*) l3, (__m128i*) h3); - - keccakf(h0, 24); - keccakf(h1, 24); - keccakf(h2, 24); - keccakf(h3, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - extra_hashes[scratchPad[1]->state[0] & 3](scratchPad[1]->state, 200, output + 32); - extra_hashes[scratchPad[2]->state[0] & 3](scratchPad[2]->state, 200, output + 64); - extra_hashes[scratchPad[3]->state[0] & 3](scratchPad[3]->state, 200, output + 96); - } - - // quadruple - inline static void hashPowV3(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - keccak(input, (int) size, scratchPad[0]->state, 200); - keccak(input + size, (int) size, scratchPad[1]->state, 200); - keccak(input + 2 * size, (int) size, scratchPad[2]->state, 200); - keccak(input + 3 * size, (int) size, scratchPad[3]->state, 200); - - const uint8_t* l0 = scratchPad[0]->memory; - const uint8_t* l1 = scratchPad[1]->memory; - const uint8_t* l2 = scratchPad[2]->memory; - const uint8_t* l3 = scratchPad[3]->memory; - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - uint64_t* h1 = reinterpret_cast(scratchPad[1]->state); - uint64_t* h2 = reinterpret_cast(scratchPad[2]->state); - uint64_t* h3 = reinterpret_cast(scratchPad[3]->state); - - cn_explode_scratchpad((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad((__m128i*) h1, (__m128i*) l1); - cn_explode_scratchpad((__m128i*) h2, (__m128i*) l2); - cn_explode_scratchpad((__m128i*) h3, (__m128i*) l3); - - uint64_t al0 = h0[0] ^h0[4]; - uint64_t al1 = h1[0] ^h1[4]; - uint64_t al2 = h2[0] ^h2[4]; - uint64_t al3 = h3[0] ^h3[4]; - uint64_t ah0 = h0[1] ^h0[5]; - uint64_t ah1 = h1[1] ^h1[5]; - uint64_t ah2 = h2[1] ^h2[5]; - uint64_t ah3 = h3[1] ^h3[5]; - - __m128i bx00 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx01 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - __m128i bx02 = _mm_set_epi64x(h2[3] ^ h2[7], h2[2] ^ h2[6]); - __m128i bx03 = _mm_set_epi64x(h3[3] ^ h3[7], h3[2] ^ h3[6]); - - __m128i bx10 = _mm_set_epi64x(h0[9] ^ h0[11], h0[8] ^ h0[10]); - __m128i bx11 = _mm_set_epi64x(h1[9] ^ h1[11], h1[8] ^ h1[10]); - __m128i bx12 = _mm_set_epi64x(h2[9] ^ h2[11], h2[8] ^ h2[10]); - __m128i bx13 = _mm_set_epi64x(h3[9] ^ h3[11], h3[8] ^ h3[10]); - - uint64_t idx0 = h0[0] ^h0[4]; - uint64_t idx1 = h1[0] ^h1[4]; - uint64_t idx2 = h2[0] ^h2[4]; - uint64_t idx3 = h3[0] ^h3[4]; - - uint64_t division_result_xmm0 = h0[12]; - uint64_t division_result_xmm1 = h1[12]; - uint64_t division_result_xmm2 = h2[12]; - uint64_t division_result_xmm3 = h3[12]; - - uint64_t sqrt_result0 = h0[13]; - uint64_t sqrt_result1 = h1[13]; - uint64_t sqrt_result2 = h2[13]; - uint64_t sqrt_result3 = h3[13]; - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx0; - __m128i cx1; - __m128i cx2; - __m128i cx3; - - const __m128i ax0 = _mm_set_epi64x(ah0, al0); - const __m128i ax1 = _mm_set_epi64x(ah1, al1); - const __m128i ax2 = _mm_set_epi64x(ah2, al2); - const __m128i ax3 = _mm_set_epi64x(ah3, al3); - - if (SOFT_AES) { - cx0 = soft_aesenc((uint32_t*) &l0[idx0 & MASK], ax0); - cx1 = soft_aesenc((uint32_t*) &l1[idx1 & MASK], ax1); - cx2 = soft_aesenc((uint32_t*) &l2[idx2 & MASK], ax2); - cx3 = soft_aesenc((uint32_t*) &l3[idx3 & MASK], ax3); - } else { - cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - cx1 = _mm_load_si128((__m128i*) &l1[idx1 & MASK]); - cx2 = _mm_load_si128((__m128i*) &l2[idx2 & MASK]); - cx3 = _mm_load_si128((__m128i*) &l3[idx3 & MASK]); - - cx0 = _mm_aesenc_si128(cx0, ax0); - cx1 = _mm_aesenc_si128(cx1, ax1); - cx2 = _mm_aesenc_si128(cx2, ax2); - cx3 = _mm_aesenc_si128(cx3, ax3); - } - - SHUFFLE_PHASE_1(l0, (idx0&MASK), bx00, bx10, ax0, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - SHUFFLE_PHASE_1(l1, (idx1&MASK), bx01, bx11, ax1, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - SHUFFLE_PHASE_1(l2, (idx2&MASK), bx02, bx12, ax2, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - SHUFFLE_PHASE_1(l3, (idx3&MASK), bx03, bx13, ax3, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - - _mm_store_si128((__m128i*) &l0[idx0 & MASK], _mm_xor_si128(bx00, cx0)); - _mm_store_si128((__m128i*) &l1[idx1 & MASK], _mm_xor_si128(bx01, cx1)); - _mm_store_si128((__m128i*) &l2[idx2 & MASK], _mm_xor_si128(bx02, cx2)); - _mm_store_si128((__m128i*) &l3[idx3 & MASK], _mm_xor_si128(bx03, cx3)); - - idx0 = EXTRACT64(cx0); - idx1 = EXTRACT64(cx1); - idx2 = EXTRACT64(cx2); - idx3 = EXTRACT64(cx3); - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & MASK])[0]; - ch = ((uint64_t*) &l0[idx0 & MASK])[1]; - - INTEGER_MATH_V2(0, cl, cx0); - - lo = __umul128(idx0, cl, &hi); - - SHUFFLE_PHASE_2(l0, (idx0&MASK), bx00, bx10, ax0, lo, hi, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - - al0 += hi; - ah0 += lo; - - ((uint64_t*) &l0[idx0 & MASK])[0] = al0; - ((uint64_t*) &l0[idx0 & MASK])[1] = ah0; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - bx10 = bx00; - bx00 = cx0; - - - cl = ((uint64_t*) &l1[idx1 & MASK])[0]; - ch = ((uint64_t*) &l1[idx1 & MASK])[1]; - - INTEGER_MATH_V2(1, cl, cx1); - - lo = __umul128(idx1, cl, &hi); - - SHUFFLE_PHASE_2(l1, (idx1&MASK), bx01, bx11, ax1, lo, hi, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - - al1 += hi; - ah1 += lo; - - ((uint64_t*) &l1[idx1 & MASK])[0] = al1; - ((uint64_t*) &l1[idx1 & MASK])[1] = ah1; - - ah1 ^= ch; - al1 ^= cl; - idx1 = al1; - - bx11 = bx01; - bx01 = cx1; - - - cl = ((uint64_t*) &l2[idx2 & MASK])[0]; - ch = ((uint64_t*) &l2[idx2 & MASK])[1]; - - INTEGER_MATH_V2(2, cl, cx2); - - lo = __umul128(idx2, cl, &hi); - - SHUFFLE_PHASE_2(l2, (idx2&MASK), bx02, bx12, ax2, lo, hi, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - - al2 += hi; - ah2 += lo; - - ((uint64_t*) &l2[idx2 & MASK])[0] = al2; - ((uint64_t*) &l2[idx2 & MASK])[1] = ah2; - - ah2 ^= ch; - al2 ^= cl; - idx2 = al2; - - bx12 = bx02; - bx02 = cx2; - - - cl = ((uint64_t*) &l3[idx3 & MASK])[0]; - ch = ((uint64_t*) &l3[idx3 & MASK])[1]; - - INTEGER_MATH_V2(3, cl, cx3); - - lo = __umul128(idx3, cl, &hi); - - SHUFFLE_PHASE_2(l3, (idx3&MASK), bx03, bx13, ax3, lo, hi, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - - al3 += hi; - ah3 += lo; - - ((uint64_t*) &l3[idx3 & MASK])[0] = al3; - ((uint64_t*) &l3[idx3 & MASK])[1] = ah3; - - ah3 ^= ch; - al3 ^= cl; - idx3 = al3; - - bx13 = bx03; - bx03 = cx3; - } - - cn_implode_scratchpad((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad((__m128i*) l1, (__m128i*) h1); - cn_implode_scratchpad((__m128i*) l2, (__m128i*) h2); - cn_implode_scratchpad((__m128i*) l3, (__m128i*) h3); - - keccakf(h0, 24); - keccakf(h1, 24); - keccakf(h2, 24); - keccakf(h3, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - extra_hashes[scratchPad[1]->state[0] & 3](scratchPad[1]->state, 200, output + 32); - extra_hashes[scratchPad[2]->state[0] & 3](scratchPad[2]->state, 200, output + 64); - extra_hashes[scratchPad[3]->state[0] & 3](scratchPad[3]->state, 200, output + 96); - } - - // quadruple - inline static void hashPowV4(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad, - uint64_t height) - { - keccak(input, (int) size, scratchPad[0]->state, 200); - keccak(input + size, (int) size, scratchPad[1]->state, 200); - keccak(input + 2 * size, (int) size, scratchPad[2]->state, 200); - keccak(input + 3 * size, (int) size, scratchPad[3]->state, 200); - - const uint8_t* l0 = scratchPad[0]->memory; - const uint8_t* l1 = scratchPad[1]->memory; - const uint8_t* l2 = scratchPad[2]->memory; - const uint8_t* l3 = scratchPad[3]->memory; - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - uint64_t* h1 = reinterpret_cast(scratchPad[1]->state); - uint64_t* h2 = reinterpret_cast(scratchPad[2]->state); - uint64_t* h3 = reinterpret_cast(scratchPad[3]->state); - - cn_explode_scratchpad((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad((__m128i*) h1, (__m128i*) l1); - cn_explode_scratchpad((__m128i*) h2, (__m128i*) l2); - cn_explode_scratchpad((__m128i*) h3, (__m128i*) l3); - - uint64_t al0 = h0[0] ^h0[4]; - uint64_t al1 = h1[0] ^h1[4]; - uint64_t al2 = h2[0] ^h2[4]; - uint64_t al3 = h3[0] ^h3[4]; - uint64_t ah0 = h0[1] ^h0[5]; - uint64_t ah1 = h1[1] ^h1[5]; - uint64_t ah2 = h2[1] ^h2[5]; - uint64_t ah3 = h3[1] ^h3[5]; - - __m128i bx00 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx01 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - __m128i bx02 = _mm_set_epi64x(h2[3] ^ h2[7], h2[2] ^ h2[6]); - __m128i bx03 = _mm_set_epi64x(h3[3] ^ h3[7], h3[2] ^ h3[6]); - - __m128i bx10 = _mm_set_epi64x(h0[9] ^ h0[11], h0[8] ^ h0[10]); - __m128i bx11 = _mm_set_epi64x(h1[9] ^ h1[11], h1[8] ^ h1[10]); - __m128i bx12 = _mm_set_epi64x(h2[9] ^ h2[11], h2[8] ^ h2[10]); - __m128i bx13 = _mm_set_epi64x(h3[9] ^ h3[11], h3[8] ^ h3[10]); - - uint64_t idx0 = h0[0] ^h0[4]; - uint64_t idx1 = h1[0] ^h1[4]; - uint64_t idx2 = h2[0] ^h2[4]; - uint64_t idx3 = h3[0] ^h3[4]; - - VARIANT4_RANDOM_MATH_INIT(0, h0) - VARIANT4_RANDOM_MATH_INIT(1, h1) - VARIANT4_RANDOM_MATH_INIT(2, h2) - VARIANT4_RANDOM_MATH_INIT(3, h3) - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx0; - __m128i cx1; - __m128i cx2; - __m128i cx3; - - const __m128i ax0 = _mm_set_epi64x(ah0, al0); - const __m128i ax1 = _mm_set_epi64x(ah1, al1); - const __m128i ax2 = _mm_set_epi64x(ah2, al2); - const __m128i ax3 = _mm_set_epi64x(ah3, al3); - - if (SOFT_AES) { - cx0 = soft_aesenc((uint32_t*) &l0[idx0 & MASK], ax0); - cx1 = soft_aesenc((uint32_t*) &l1[idx1 & MASK], ax1); - cx2 = soft_aesenc((uint32_t*) &l2[idx2 & MASK], ax2); - cx3 = soft_aesenc((uint32_t*) &l3[idx3 & MASK], ax3); - } else { - cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - cx1 = _mm_load_si128((__m128i*) &l1[idx1 & MASK]); - cx2 = _mm_load_si128((__m128i*) &l2[idx2 & MASK]); - cx3 = _mm_load_si128((__m128i*) &l3[idx3 & MASK]); - - cx0 = _mm_aesenc_si128(cx0, ax0); - cx1 = _mm_aesenc_si128(cx1, ax1); - cx2 = _mm_aesenc_si128(cx2, ax2); - cx3 = _mm_aesenc_si128(cx3, ax3); - } - - SHUFFLE_V4(l0, (idx0&MASK), bx00, bx10, ax0, cx0) - SHUFFLE_V4(l1, (idx1&MASK), bx01, bx11, ax1, cx1) - SHUFFLE_V4(l2, (idx2&MASK), bx02, bx12, ax2, cx2) - SHUFFLE_V4(l3, (idx3&MASK), bx03, bx13, ax3, cx3) - - _mm_store_si128((__m128i*) &l0[idx0 & MASK], _mm_xor_si128(bx00, cx0)); - _mm_store_si128((__m128i*) &l1[idx1 & MASK], _mm_xor_si128(bx01, cx1)); - _mm_store_si128((__m128i*) &l2[idx2 & MASK], _mm_xor_si128(bx02, cx2)); - _mm_store_si128((__m128i*) &l3[idx3 & MASK], _mm_xor_si128(bx03, cx3)); - - idx0 = EXTRACT64(cx0); - idx1 = EXTRACT64(cx1); - idx2 = EXTRACT64(cx2); - idx3 = EXTRACT64(cx3); - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & MASK])[0]; - ch = ((uint64_t*) &l0[idx0 & MASK])[1]; - - VARIANT4_RANDOM_MATH(0, al0, ah0, cl, bx00, bx10) - - if (VARIANT == POW_V4) { - al0 ^= r0[2] | ((uint64_t)(r0[3]) << 32); - ah0 ^= r0[0] | ((uint64_t)(r0[1]) << 32); - } - - lo = __umul128(idx0, cl, &hi); - - SHUFFLE_V4(l0, (idx0&MASK), bx00, bx10, ax0, cx0) - - al0 += hi; - ah0 += lo; - - ((uint64_t*) &l0[idx0 & MASK])[0] = al0; - ((uint64_t*) &l0[idx0 & MASK])[1] = ah0; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - bx10 = bx00; - bx00 = cx0; - - - cl = ((uint64_t*) &l1[idx1 & MASK])[0]; - ch = ((uint64_t*) &l1[idx1 & MASK])[1]; - - VARIANT4_RANDOM_MATH(1, al1, ah1, cl, bx01, bx11) - - if (VARIANT == POW_V4) { - al1 ^= r1[2] | ((uint64_t)(r1[3]) << 32); - ah1 ^= r1[0] | ((uint64_t)(r1[1]) << 32); - } - - - lo = __umul128(idx1, cl, &hi); - - SHUFFLE_V4(l1, (idx1&MASK), bx01, bx11, ax1, cx1) - - al1 += hi; - ah1 += lo; - - ((uint64_t*) &l1[idx1 & MASK])[0] = al1; - ((uint64_t*) &l1[idx1 & MASK])[1] = ah1; - - ah1 ^= ch; - al1 ^= cl; - idx1 = al1; - - bx11 = bx01; - bx01 = cx1; - - - cl = ((uint64_t*) &l2[idx2 & MASK])[0]; - ch = ((uint64_t*) &l2[idx2 & MASK])[1]; - - VARIANT4_RANDOM_MATH(2, al2, ah2, cl, bx02, bx12) - - if (VARIANT == POW_V4) { - al2 ^= r2[2] | ((uint64_t)(r2[3]) << 32); - ah2 ^= r2[0] | ((uint64_t)(r2[1]) << 32); - } - - lo = __umul128(idx2, cl, &hi); - - SHUFFLE_V4(l2, (idx2&MASK), bx02, bx12, ax2, cx2) - - al2 += hi; - ah2 += lo; - - ((uint64_t*) &l2[idx2 & MASK])[0] = al2; - ((uint64_t*) &l2[idx2 & MASK])[1] = ah2; - - ah2 ^= ch; - al2 ^= cl; - idx2 = al2; - - bx12 = bx02; - bx02 = cx2; - - - cl = ((uint64_t*) &l3[idx3 & MASK])[0]; - ch = ((uint64_t*) &l3[idx3 & MASK])[1]; - - VARIANT4_RANDOM_MATH(3, al3, ah3, cl, bx03, bx13) - - if (VARIANT == POW_V4) { - al3 ^= r3[2] | ((uint64_t)(r3[3]) << 32); - ah3 ^= r3[0] | ((uint64_t)(r3[1]) << 32); - } - - lo = __umul128(idx3, cl, &hi); - - SHUFFLE_V4(l3, (idx3&MASK), bx03, bx13, ax3, cx3) - - al3 += hi; - ah3 += lo; - - ((uint64_t*) &l3[idx3 & MASK])[0] = al3; - ((uint64_t*) &l3[idx3 & MASK])[1] = ah3; - - ah3 ^= ch; - al3 ^= cl; - idx3 = al3; - - bx13 = bx03; - bx03 = cx3; - } - - cn_implode_scratchpad((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad((__m128i*) l1, (__m128i*) h1); - cn_implode_scratchpad((__m128i*) l2, (__m128i*) h2); - cn_implode_scratchpad((__m128i*) l3, (__m128i*) h3); - - keccakf(h0, 24); - keccakf(h1, 24); - keccakf(h2, 24); - keccakf(h3, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - extra_hashes[scratchPad[1]->state[0] & 3](scratchPad[1]->state, 200, output + 32); - extra_hashes[scratchPad[2]->state[0] & 3](scratchPad[2]->state, 200, output + 64); - extra_hashes[scratchPad[3]->state[0] & 3](scratchPad[3]->state, 200, output + 96); - } - - inline static void hashLiteTube(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - keccak(input, (int) size, scratchPad[0]->state, 200); - keccak(input + size, (int) size, scratchPad[1]->state, 200); - keccak(input + 2 * size, (int) size, scratchPad[2]->state, 200); - keccak(input + 3 * size, (int) size, scratchPad[3]->state, 200); - - uint64_t tweak1_2_0 = (*reinterpret_cast(input + 35) ^ - *(reinterpret_cast(scratchPad[0]->state) + 24)); - uint64_t tweak1_2_1 = (*reinterpret_cast(input + 35 + size) ^ - *(reinterpret_cast(scratchPad[1]->state) + 24)); - uint64_t tweak1_2_2 = (*reinterpret_cast(input + 35 + 2 * size) ^ - *(reinterpret_cast(scratchPad[2]->state) + 24)); - uint64_t tweak1_2_3 = (*reinterpret_cast(input + 35 + 3 * size) ^ - *(reinterpret_cast(scratchPad[3]->state) + 24)); - - const uint8_t* l0 = scratchPad[0]->memory; - const uint8_t* l1 = scratchPad[1]->memory; - const uint8_t* l2 = scratchPad[2]->memory; - const uint8_t* l3 = scratchPad[3]->memory; - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - uint64_t* h1 = reinterpret_cast(scratchPad[1]->state); - uint64_t* h2 = reinterpret_cast(scratchPad[2]->state); - uint64_t* h3 = reinterpret_cast(scratchPad[3]->state); - - cn_explode_scratchpad((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad((__m128i*) h1, (__m128i*) l1); - cn_explode_scratchpad((__m128i*) h2, (__m128i*) l2); - cn_explode_scratchpad((__m128i*) h3, (__m128i*) l3); - - uint64_t al0 = h0[0] ^h0[4]; - uint64_t al1 = h1[0] ^h1[4]; - uint64_t al2 = h2[0] ^h2[4]; - uint64_t al3 = h3[0] ^h3[4]; - uint64_t ah0 = h0[1] ^h0[5]; - uint64_t ah1 = h1[1] ^h1[5]; - uint64_t ah2 = h2[1] ^h2[5]; - uint64_t ah3 = h3[1] ^h3[5]; - - __m128i bx0 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx1 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - __m128i bx2 = _mm_set_epi64x(h2[3] ^ h2[7], h2[2] ^ h2[6]); - __m128i bx3 = _mm_set_epi64x(h3[3] ^ h3[7], h3[2] ^ h3[6]); - - uint64_t idx0 = h0[0] ^h0[4]; - uint64_t idx1 = h1[0] ^h1[4]; - uint64_t idx2 = h2[0] ^h2[4]; - uint64_t idx3 = h3[0] ^h3[4]; - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx0; - __m128i cx1; - __m128i cx2; - __m128i cx3; - - if (SOFT_AES) { - cx0 = soft_aesenc((uint32_t*) &l0[idx0 & MASK], _mm_set_epi64x(ah0, al0)); - cx1 = soft_aesenc((uint32_t*) &l1[idx1 & MASK], _mm_set_epi64x(ah1, al1)); - cx2 = soft_aesenc((uint32_t*) &l2[idx2 & MASK], _mm_set_epi64x(ah2, al2)); - cx3 = soft_aesenc((uint32_t*) &l3[idx3 & MASK], _mm_set_epi64x(ah3, al3)); - } else { - cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - cx1 = _mm_load_si128((__m128i*) &l1[idx1 & MASK]); - cx2 = _mm_load_si128((__m128i*) &l2[idx2 & MASK]); - cx3 = _mm_load_si128((__m128i*) &l3[idx3 & MASK]); - - cx0 = _mm_aesenc_si128(cx0, _mm_set_epi64x(ah0, al0)); - cx1 = _mm_aesenc_si128(cx1, _mm_set_epi64x(ah1, al1)); - cx2 = _mm_aesenc_si128(cx2, _mm_set_epi64x(ah2, al2)); - cx3 = _mm_aesenc_si128(cx3, _mm_set_epi64x(ah3, al3)); - } - - _mm_store_si128((__m128i*) &l0[idx0 & MASK], _mm_xor_si128(bx0, cx0)); - _mm_store_si128((__m128i*) &l1[idx1 & MASK], _mm_xor_si128(bx1, cx1)); - _mm_store_si128((__m128i*) &l2[idx2 & MASK], _mm_xor_si128(bx2, cx2)); - _mm_store_si128((__m128i*) &l3[idx3 & MASK], _mm_xor_si128(bx3, cx3)); - - static const uint32_t table = 0x75310; - uint8_t tmp = reinterpret_cast(&l0[idx0 & MASK])[11]; - uint8_t index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*) (&l0[idx0 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - tmp = reinterpret_cast(&l1[idx1 & MASK])[11]; - index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*) (&l1[idx1 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - tmp = reinterpret_cast(&l2[idx2 & MASK])[11]; - index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*) (&l2[idx2 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - tmp = reinterpret_cast(&l3[idx3 & MASK])[11]; - index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*) (&l3[idx3 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - - idx0 = EXTRACT64(cx0); - idx1 = EXTRACT64(cx1); - idx2 = EXTRACT64(cx2); - idx3 = EXTRACT64(cx3); - - bx0 = cx0; - bx1 = cx1; - bx2 = cx2; - bx3 = cx3; - - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & MASK])[0]; - ch = ((uint64_t*) &l0[idx0 & MASK])[1]; - lo = __umul128(idx0, cl, &hi); - - al0 += hi; - ah0 += lo; - - ah0 ^= tweak1_2_0; - ((uint64_t*) &l0[idx0 & MASK])[0] = al0; - ((uint64_t*) &l0[idx0 & MASK])[1] = ah0; - ah0 ^= tweak1_2_0; - - ((uint64_t*) &l0[idx0 & MASK])[1] ^= ((uint64_t*) &l0[idx0 & MASK])[0]; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - - cl = ((uint64_t*) &l1[idx1 & MASK])[0]; - ch = ((uint64_t*) &l1[idx1 & MASK])[1]; - lo = __umul128(idx1, cl, &hi); - - al1 += hi; - ah1 += lo; - - ah1 ^= tweak1_2_1; - ((uint64_t*) &l1[idx1 & MASK])[0] = al1; - ((uint64_t*) &l1[idx1 & MASK])[1] = ah1; - ah1 ^= tweak1_2_1; - - ((uint64_t*) &l1[idx1 & MASK])[1] ^= ((uint64_t*) &l1[idx1 & MASK])[0]; - - ah1 ^= ch; - al1 ^= cl; - idx1 = al1; - - - cl = ((uint64_t*) &l2[idx2 & MASK])[0]; - ch = ((uint64_t*) &l2[idx2 & MASK])[1]; - lo = __umul128(idx2, cl, &hi); - - al2 += hi; - ah2 += lo; - - ah2 ^= tweak1_2_2; - ((uint64_t*) &l2[idx2 & MASK])[0] = al2; - ((uint64_t*) &l2[idx2 & MASK])[1] = ah2; - ah2 ^= tweak1_2_2; - - ((uint64_t*) &l2[idx2 & MASK])[1] ^= ((uint64_t*) &l2[idx2 & MASK])[0]; - - ah2 ^= ch; - al2 ^= cl; - idx2 = al2; - - - cl = ((uint64_t*) &l3[idx3 & MASK])[0]; - ch = ((uint64_t*) &l3[idx3 & MASK])[1]; - lo = __umul128(idx3, cl, &hi); - - al3 += hi; - ah3 += lo; - - ah3 ^= tweak1_2_3; - ((uint64_t*) &l3[idx3 & MASK])[0] = al3; - ((uint64_t*) &l3[idx3 & MASK])[1] = ah3; - ah3 ^= tweak1_2_3; - - ((uint64_t*) &l3[idx3 & MASK])[1] ^= ((uint64_t*) &l3[idx3 & MASK])[0]; - - ah3 ^= ch; - al3 ^= cl; - idx3 = al3; - } - - cn_implode_scratchpad((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad((__m128i*) l1, (__m128i*) h1); - cn_implode_scratchpad((__m128i*) l2, (__m128i*) h2); - cn_implode_scratchpad((__m128i*) l3, (__m128i*) h3); - - keccakf(h0, 24); - keccakf(h1, 24); - keccakf(h2, 24); - keccakf(h3, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - extra_hashes[scratchPad[1]->state[0] & 3](scratchPad[1]->state, 200, output + 32); - extra_hashes[scratchPad[2]->state[0] & 3](scratchPad[2]->state, 200, output + 64); - extra_hashes[scratchPad[3]->state[0] & 3](scratchPad[3]->state, 200, output + 96); - } - - inline static void hashHeavy(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - // not supported - } - - inline static void hashHeavyTube(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - // not supported - } -}; - -template -class CryptoNightMultiHash -{// -public: - inline static void hash(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - keccak(input, (int) size, scratchPad[0]->state, 200); - keccak(input + size, (int) size, scratchPad[1]->state, 200); - keccak(input + 2 * size, (int) size, scratchPad[2]->state, 200); - keccak(input + 3 * size, (int) size, scratchPad[3]->state, 200); - keccak(input + 4 * size, (int) size, scratchPad[4]->state, 200); - - const uint8_t* l0 = scratchPad[0]->memory; - const uint8_t* l1 = scratchPad[1]->memory; - const uint8_t* l2 = scratchPad[2]->memory; - const uint8_t* l3 = scratchPad[3]->memory; - const uint8_t* l4 = scratchPad[4]->memory; - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - uint64_t* h1 = reinterpret_cast(scratchPad[1]->state); - uint64_t* h2 = reinterpret_cast(scratchPad[2]->state); - uint64_t* h3 = reinterpret_cast(scratchPad[3]->state); - uint64_t* h4 = reinterpret_cast(scratchPad[4]->state); - - cn_explode_scratchpad((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad((__m128i*) h1, (__m128i*) l1); - cn_explode_scratchpad((__m128i*) h2, (__m128i*) l2); - cn_explode_scratchpad((__m128i*) h3, (__m128i*) l3); - cn_explode_scratchpad((__m128i*) h4, (__m128i*) l4); - - uint64_t al0 = h0[0] ^h0[4]; - uint64_t al1 = h1[0] ^h1[4]; - uint64_t al2 = h2[0] ^h2[4]; - uint64_t al3 = h3[0] ^h3[4]; - uint64_t al4 = h4[0] ^h4[4]; - uint64_t ah0 = h0[1] ^h0[5]; - uint64_t ah1 = h1[1] ^h1[5]; - uint64_t ah2 = h2[1] ^h2[5]; - uint64_t ah3 = h3[1] ^h3[5]; - uint64_t ah4 = h4[1] ^h4[5]; - - __m128i bx0 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx1 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - __m128i bx2 = _mm_set_epi64x(h2[3] ^ h2[7], h2[2] ^ h2[6]); - __m128i bx3 = _mm_set_epi64x(h3[3] ^ h3[7], h3[2] ^ h3[6]); - __m128i bx4 = _mm_set_epi64x(h4[3] ^ h4[7], h4[2] ^ h4[6]); - - uint64_t idx0 = h0[0] ^h0[4]; - uint64_t idx1 = h1[0] ^h1[4]; - uint64_t idx2 = h2[0] ^h2[4]; - uint64_t idx3 = h3[0] ^h3[4]; - uint64_t idx4 = h4[0] ^h4[4]; - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx0; - __m128i cx1; - __m128i cx2; - __m128i cx3; - __m128i cx4; - - if (SOFT_AES) { - cx0 = soft_aesenc((uint32_t*) &l0[idx0 & MASK], _mm_set_epi64x(ah0, al0)); - cx1 = soft_aesenc((uint32_t*) &l1[idx1 & MASK], _mm_set_epi64x(ah1, al1)); - cx2 = soft_aesenc((uint32_t*) &l2[idx2 & MASK], _mm_set_epi64x(ah2, al2)); - cx3 = soft_aesenc((uint32_t*) &l3[idx3 & MASK], _mm_set_epi64x(ah3, al3)); - cx4 = soft_aesenc((uint32_t*) &l4[idx4 & MASK], _mm_set_epi64x(ah4, al4)); - } else { - cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - cx1 = _mm_load_si128((__m128i*) &l1[idx1 & MASK]); - cx2 = _mm_load_si128((__m128i*) &l2[idx2 & MASK]); - cx3 = _mm_load_si128((__m128i*) &l3[idx3 & MASK]); - cx4 = _mm_load_si128((__m128i*) &l4[idx4 & MASK]); - - cx0 = _mm_aesenc_si128(cx0, _mm_set_epi64x(ah0, al0)); - cx1 = _mm_aesenc_si128(cx1, _mm_set_epi64x(ah1, al1)); - cx2 = _mm_aesenc_si128(cx2, _mm_set_epi64x(ah2, al2)); - cx3 = _mm_aesenc_si128(cx3, _mm_set_epi64x(ah3, al3)); - cx4 = _mm_aesenc_si128(cx4, _mm_set_epi64x(ah4, al4)); - } - - _mm_store_si128((__m128i*) &l0[idx0 & MASK], _mm_xor_si128(bx0, cx0)); - _mm_store_si128((__m128i*) &l1[idx1 & MASK], _mm_xor_si128(bx1, cx1)); - _mm_store_si128((__m128i*) &l2[idx2 & MASK], _mm_xor_si128(bx2, cx2)); - _mm_store_si128((__m128i*) &l3[idx3 & MASK], _mm_xor_si128(bx3, cx3)); - _mm_store_si128((__m128i*) &l4[idx4 & MASK], _mm_xor_si128(bx4, cx4)); - - idx0 = EXTRACT64(cx0); - idx1 = EXTRACT64(cx1); - idx2 = EXTRACT64(cx2); - idx3 = EXTRACT64(cx3); - idx4 = EXTRACT64(cx4); - - bx0 = cx0; - bx1 = cx1; - bx2 = cx2; - bx3 = cx3; - bx4 = cx4; - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & MASK])[0]; - ch = ((uint64_t*) &l0[idx0 & MASK])[1]; - lo = __umul128(idx0, cl, &hi); - - al0 += hi; - ah0 += lo; - - ((uint64_t*) &l0[idx0 & MASK])[0] = al0; - ((uint64_t*) &l0[idx0 & MASK])[1] = ah0; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - - cl = ((uint64_t*) &l1[idx1 & MASK])[0]; - ch = ((uint64_t*) &l1[idx1 & MASK])[1]; - lo = __umul128(idx1, cl, &hi); - - al1 += hi; - ah1 += lo; - - ((uint64_t*) &l1[idx1 & MASK])[0] = al1; - ((uint64_t*) &l1[idx1 & MASK])[1] = ah1; - - ah1 ^= ch; - al1 ^= cl; - idx1 = al1; - - - cl = ((uint64_t*) &l2[idx2 & MASK])[0]; - ch = ((uint64_t*) &l2[idx2 & MASK])[1]; - lo = __umul128(idx2, cl, &hi); - - al2 += hi; - ah2 += lo; - - ((uint64_t*) &l2[idx2 & MASK])[0] = al2; - ((uint64_t*) &l2[idx2 & MASK])[1] = ah2; - - ah2 ^= ch; - al2 ^= cl; - idx2 = al2; - - - cl = ((uint64_t*) &l3[idx3 & MASK])[0]; - ch = ((uint64_t*) &l3[idx3 & MASK])[1]; - lo = __umul128(idx3, cl, &hi); - - al3 += hi; - ah3 += lo; - - ((uint64_t*) &l3[idx3 & MASK])[0] = al3; - ((uint64_t*) &l3[idx3 & MASK])[1] = ah3; - - ah3 ^= ch; - al3 ^= cl; - idx3 = al3; - - - cl = ((uint64_t*) &l4[idx4 & MASK])[0]; - ch = ((uint64_t*) &l4[idx4 & MASK])[1]; - lo = __umul128(idx4, cl, &hi); - - al4 += hi; - ah4 += lo; - - ((uint64_t*) &l4[idx4 & MASK])[0] = al4; - ((uint64_t*) &l4[idx4 & MASK])[1] = ah4; - - ah4 ^= ch; - al4 ^= cl; - idx4 = al4; - } - - cn_implode_scratchpad((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad((__m128i*) l1, (__m128i*) h1); - cn_implode_scratchpad((__m128i*) l2, (__m128i*) h2); - cn_implode_scratchpad((__m128i*) l3, (__m128i*) h3); - cn_implode_scratchpad((__m128i*) l4, (__m128i*) h4); - - keccakf(h0, 24); - keccakf(h1, 24); - keccakf(h2, 24); - keccakf(h3, 24); - keccakf(h4, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - extra_hashes[scratchPad[1]->state[0] & 3](scratchPad[1]->state, 200, output + 32); - extra_hashes[scratchPad[2]->state[0] & 3](scratchPad[2]->state, 200, output + 64); - extra_hashes[scratchPad[3]->state[0] & 3](scratchPad[3]->state, 200, output + 96); - extra_hashes[scratchPad[4]->state[0] & 3](scratchPad[4]->state, 200, output + 128); - } - - inline static void hashPowV2(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - keccak(input, (int) size, scratchPad[0]->state, 200); - keccak(input + size, (int) size, scratchPad[1]->state, 200); - keccak(input + 2 * size, (int) size, scratchPad[2]->state, 200); - keccak(input + 3 * size, (int) size, scratchPad[3]->state, 200); - keccak(input + 4 * size, (int) size, scratchPad[4]->state, 200); - - uint64_t tweak1_2_0 = (*reinterpret_cast(input + 35) ^ - *(reinterpret_cast(scratchPad[0]->state) + 24)); - uint64_t tweak1_2_1 = (*reinterpret_cast(input + 35 + size) ^ - *(reinterpret_cast(scratchPad[1]->state) + 24)); - uint64_t tweak1_2_2 = (*reinterpret_cast(input + 35 + 2 * size) ^ - *(reinterpret_cast(scratchPad[2]->state) + 24)); - uint64_t tweak1_2_3 = (*reinterpret_cast(input + 35 + 3 * size) ^ - *(reinterpret_cast(scratchPad[3]->state) + 24)); - uint64_t tweak1_2_4 = (*reinterpret_cast(input + 35 + 4 * size) ^ - *(reinterpret_cast(scratchPad[4]->state) + 24)); - - - const uint8_t* l0 = scratchPad[0]->memory; - const uint8_t* l1 = scratchPad[1]->memory; - const uint8_t* l2 = scratchPad[2]->memory; - const uint8_t* l3 = scratchPad[3]->memory; - const uint8_t* l4 = scratchPad[4]->memory; - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - uint64_t* h1 = reinterpret_cast(scratchPad[1]->state); - uint64_t* h2 = reinterpret_cast(scratchPad[2]->state); - uint64_t* h3 = reinterpret_cast(scratchPad[3]->state); - uint64_t* h4 = reinterpret_cast(scratchPad[4]->state); - - cn_explode_scratchpad((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad((__m128i*) h1, (__m128i*) l1); - cn_explode_scratchpad((__m128i*) h2, (__m128i*) l2); - cn_explode_scratchpad((__m128i*) h3, (__m128i*) l3); - cn_explode_scratchpad((__m128i*) h4, (__m128i*) l4); - - uint64_t al0 = h0[0] ^h0[4]; - uint64_t al1 = h1[0] ^h1[4]; - uint64_t al2 = h2[0] ^h2[4]; - uint64_t al3 = h3[0] ^h3[4]; - uint64_t al4 = h4[0] ^h4[4]; - uint64_t ah0 = h0[1] ^h0[5]; - uint64_t ah1 = h1[1] ^h1[5]; - uint64_t ah2 = h2[1] ^h2[5]; - uint64_t ah3 = h3[1] ^h3[5]; - uint64_t ah4 = h4[1] ^h4[5]; - - __m128i bx0 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx1 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - __m128i bx2 = _mm_set_epi64x(h2[3] ^ h2[7], h2[2] ^ h2[6]); - __m128i bx3 = _mm_set_epi64x(h3[3] ^ h3[7], h3[2] ^ h3[6]); - __m128i bx4 = _mm_set_epi64x(h4[3] ^ h4[7], h4[2] ^ h4[6]); - - uint64_t idx0 = h0[0] ^h0[4]; - uint64_t idx1 = h1[0] ^h1[4]; - uint64_t idx2 = h2[0] ^h2[4]; - uint64_t idx3 = h3[0] ^h3[4]; - uint64_t idx4 = h4[0] ^h4[4]; - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx0; - __m128i cx1; - __m128i cx2; - __m128i cx3; - __m128i cx4; - - if (SOFT_AES) { - cx0 = soft_aesenc((uint32_t*) &l0[idx0 & MASK], _mm_set_epi64x(ah0, al0)); - cx1 = soft_aesenc((uint32_t*) &l1[idx1 & MASK], _mm_set_epi64x(ah1, al1)); - cx2 = soft_aesenc((uint32_t*) &l2[idx2 & MASK], _mm_set_epi64x(ah2, al2)); - cx3 = soft_aesenc((uint32_t*) &l3[idx3 & MASK], _mm_set_epi64x(ah3, al3)); - cx4 = soft_aesenc((uint32_t*) &l4[idx4 & MASK], _mm_set_epi64x(ah4, al4)); - } else { - cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - cx1 = _mm_load_si128((__m128i*) &l1[idx1 & MASK]); - cx2 = _mm_load_si128((__m128i*) &l2[idx2 & MASK]); - cx3 = _mm_load_si128((__m128i*) &l3[idx3 & MASK]); - cx4 = _mm_load_si128((__m128i*) &l4[idx4 & MASK]); - - cx0 = _mm_aesenc_si128(cx0, _mm_set_epi64x(ah0, al0)); - cx1 = _mm_aesenc_si128(cx1, _mm_set_epi64x(ah1, al1)); - cx2 = _mm_aesenc_si128(cx2, _mm_set_epi64x(ah2, al2)); - cx3 = _mm_aesenc_si128(cx3, _mm_set_epi64x(ah3, al3)); - cx4 = _mm_aesenc_si128(cx4, _mm_set_epi64x(ah4, al4)); - } - - _mm_store_si128((__m128i*) &l0[idx0 & MASK], _mm_xor_si128(bx0, cx0)); - _mm_store_si128((__m128i*) &l1[idx1 & MASK], _mm_xor_si128(bx1, cx1)); - _mm_store_si128((__m128i*) &l2[idx2 & MASK], _mm_xor_si128(bx2, cx2)); - _mm_store_si128((__m128i*) &l3[idx3 & MASK], _mm_xor_si128(bx3, cx3)); - _mm_store_si128((__m128i*) &l4[idx4 & MASK], _mm_xor_si128(bx4, cx4)); - - static const uint32_t table = 0x75310; - uint8_t tmp = reinterpret_cast(&l0[idx0 & MASK])[11]; - uint8_t index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*) (&l0[idx0 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - tmp = reinterpret_cast(&l1[idx1 & MASK])[11]; - index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*) (&l1[idx1 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - tmp = reinterpret_cast(&l2[idx2 & MASK])[11]; - index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*) (&l2[idx2 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - tmp = reinterpret_cast(&l3[idx3 & MASK])[11]; - index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*) (&l3[idx3 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - tmp = reinterpret_cast(&l4[idx4 & MASK])[11]; - index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*) (&l4[idx4 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - - idx0 = EXTRACT64(cx0); - idx1 = EXTRACT64(cx1); - idx2 = EXTRACT64(cx2); - idx3 = EXTRACT64(cx3); - idx4 = EXTRACT64(cx4); - - bx0 = cx0; - bx1 = cx1; - bx2 = cx2; - bx3 = cx3; - bx4 = cx4; - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & MASK])[0]; - ch = ((uint64_t*) &l0[idx0 & MASK])[1]; - lo = __umul128(idx0, cl, &hi); - - al0 += hi; - ah0 += lo; - - ah0 ^= tweak1_2_0; - ((uint64_t*) &l0[idx0 & MASK])[0] = al0; - ((uint64_t*) &l0[idx0 & MASK])[1] = ah0; - ah0 ^= tweak1_2_0; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - - cl = ((uint64_t*) &l1[idx1 & MASK])[0]; - ch = ((uint64_t*) &l1[idx1 & MASK])[1]; - lo = __umul128(idx1, cl, &hi); - - al1 += hi; - ah1 += lo; - - ah1 ^= tweak1_2_1; - ((uint64_t*) &l1[idx1 & MASK])[0] = al1; - ((uint64_t*) &l1[idx1 & MASK])[1] = ah1; - ah1 ^= tweak1_2_1; - - ah1 ^= ch; - al1 ^= cl; - idx1 = al1; - - - cl = ((uint64_t*) &l2[idx2 & MASK])[0]; - ch = ((uint64_t*) &l2[idx2 & MASK])[1]; - lo = __umul128(idx2, cl, &hi); - - al2 += hi; - ah2 += lo; - - ah2 ^= tweak1_2_2; - ((uint64_t*) &l2[idx2 & MASK])[0] = al2; - ((uint64_t*) &l2[idx2 & MASK])[1] = ah2; - ah2 ^= tweak1_2_2; - - ah2 ^= ch; - al2 ^= cl; - idx2 = al2; - - - cl = ((uint64_t*) &l3[idx3 & MASK])[0]; - ch = ((uint64_t*) &l3[idx3 & MASK])[1]; - lo = __umul128(idx3, cl, &hi); - - al3 += hi; - ah3 += lo; - - ah3 ^= tweak1_2_3; - ((uint64_t*) &l3[idx3 & MASK])[0] = al3; - ((uint64_t*) &l3[idx3 & MASK])[1] = ah3; - ah3 ^= tweak1_2_3; - - ah3 ^= ch; - al3 ^= cl; - idx3 = al3; - - - cl = ((uint64_t*) &l4[idx4 & MASK])[0]; - ch = ((uint64_t*) &l4[idx4 & MASK])[1]; - lo = __umul128(idx4, cl, &hi); - - al4 += hi; - ah4 += lo; - - ah4 ^= tweak1_2_4; - ((uint64_t*) &l4[idx4 & MASK])[0] = al4; - ((uint64_t*) &l4[idx4 & MASK])[1] = ah4; - ah4 ^= tweak1_2_4; - - ah4 ^= ch; - al4 ^= cl; - idx4 = al4; - } - - cn_implode_scratchpad((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad((__m128i*) l1, (__m128i*) h1); - cn_implode_scratchpad((__m128i*) l2, (__m128i*) h2); - cn_implode_scratchpad((__m128i*) l3, (__m128i*) h3); - cn_implode_scratchpad((__m128i*) l4, (__m128i*) h4); - - keccakf(h0, 24); - keccakf(h1, 24); - keccakf(h2, 24); - keccakf(h3, 24); - keccakf(h4, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - extra_hashes[scratchPad[1]->state[0] & 3](scratchPad[1]->state, 200, output + 32); - extra_hashes[scratchPad[2]->state[0] & 3](scratchPad[2]->state, 200, output + 64); - extra_hashes[scratchPad[3]->state[0] & 3](scratchPad[3]->state, 200, output + 96); - extra_hashes[scratchPad[4]->state[0] & 3](scratchPad[4]->state, 200, output + 128); - } - - // quintuple - inline static void hashPowV3(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - keccak(input, (int) size, scratchPad[0]->state, 200); - keccak(input + size, (int) size, scratchPad[1]->state, 200); - keccak(input + 2 * size, (int) size, scratchPad[2]->state, 200); - keccak(input + 3 * size, (int) size, scratchPad[3]->state, 200); - keccak(input + 4 * size, (int) size, scratchPad[4]->state, 200); - - const uint8_t* l0 = scratchPad[0]->memory; - const uint8_t* l1 = scratchPad[1]->memory; - const uint8_t* l2 = scratchPad[2]->memory; - const uint8_t* l3 = scratchPad[3]->memory; - const uint8_t* l4 = scratchPad[4]->memory; - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - uint64_t* h1 = reinterpret_cast(scratchPad[1]->state); - uint64_t* h2 = reinterpret_cast(scratchPad[2]->state); - uint64_t* h3 = reinterpret_cast(scratchPad[3]->state); - uint64_t* h4 = reinterpret_cast(scratchPad[4]->state); - - cn_explode_scratchpad((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad((__m128i*) h1, (__m128i*) l1); - cn_explode_scratchpad((__m128i*) h2, (__m128i*) l2); - cn_explode_scratchpad((__m128i*) h3, (__m128i*) l3); - cn_explode_scratchpad((__m128i*) h4, (__m128i*) l4); - - uint64_t al0 = h0[0] ^h0[4]; - uint64_t al1 = h1[0] ^h1[4]; - uint64_t al2 = h2[0] ^h2[4]; - uint64_t al3 = h3[0] ^h3[4]; - uint64_t al4 = h4[0] ^h4[4]; - uint64_t ah0 = h0[1] ^h0[5]; - uint64_t ah1 = h1[1] ^h1[5]; - uint64_t ah2 = h2[1] ^h2[5]; - uint64_t ah3 = h3[1] ^h3[5]; - uint64_t ah4 = h4[1] ^h4[5]; - - __m128i bx00 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx01 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - __m128i bx02 = _mm_set_epi64x(h2[3] ^ h2[7], h2[2] ^ h2[6]); - __m128i bx03 = _mm_set_epi64x(h3[3] ^ h3[7], h3[2] ^ h3[6]); - __m128i bx04 = _mm_set_epi64x(h4[3] ^ h4[7], h4[2] ^ h4[6]); - - __m128i bx10 = _mm_set_epi64x(h0[9] ^ h0[11], h0[8] ^ h0[10]); - __m128i bx11 = _mm_set_epi64x(h1[9] ^ h1[11], h1[8] ^ h1[10]); - __m128i bx12 = _mm_set_epi64x(h2[9] ^ h2[11], h2[8] ^ h2[10]); - __m128i bx13 = _mm_set_epi64x(h3[9] ^ h3[11], h3[8] ^ h3[10]); - __m128i bx14 = _mm_set_epi64x(h4[9] ^ h4[11], h4[8] ^ h4[10]); - - uint64_t idx0 = h0[0] ^h0[4]; - uint64_t idx1 = h1[0] ^h1[4]; - uint64_t idx2 = h2[0] ^h2[4]; - uint64_t idx3 = h3[0] ^h3[4]; - uint64_t idx4 = h4[0] ^h4[4]; - - uint64_t division_result_xmm0 = h0[12]; - uint64_t division_result_xmm1 = h1[12]; - uint64_t division_result_xmm2 = h2[12]; - uint64_t division_result_xmm3 = h3[12]; - uint64_t division_result_xmm4 = h4[12]; - - uint64_t sqrt_result0 = h0[13]; - uint64_t sqrt_result1 = h1[13]; - uint64_t sqrt_result2 = h2[13]; - uint64_t sqrt_result3 = h3[13]; - uint64_t sqrt_result4 = h4[13]; - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx0; - __m128i cx1; - __m128i cx2; - __m128i cx3; - __m128i cx4; - - const __m128i ax0 = _mm_set_epi64x(ah0, al0); - const __m128i ax1 = _mm_set_epi64x(ah1, al1); - const __m128i ax2 = _mm_set_epi64x(ah2, al2); - const __m128i ax3 = _mm_set_epi64x(ah3, al3); - const __m128i ax4 = _mm_set_epi64x(ah4, al4); - - if (SOFT_AES) { - cx0 = soft_aesenc((uint32_t*) &l0[idx0 & MASK], ax0); - cx1 = soft_aesenc((uint32_t*) &l1[idx1 & MASK], ax1); - cx2 = soft_aesenc((uint32_t*) &l2[idx2 & MASK], ax2); - cx3 = soft_aesenc((uint32_t*) &l3[idx3 & MASK], ax3); - cx4 = soft_aesenc((uint32_t*) &l4[idx4 & MASK], ax4); - } else { - cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - cx1 = _mm_load_si128((__m128i*) &l1[idx1 & MASK]); - cx2 = _mm_load_si128((__m128i*) &l2[idx2 & MASK]); - cx3 = _mm_load_si128((__m128i*) &l3[idx3 & MASK]); - cx4 = _mm_load_si128((__m128i*) &l4[idx4 & MASK]); - - cx0 = _mm_aesenc_si128(cx0, ax0); - cx1 = _mm_aesenc_si128(cx1, ax1); - cx2 = _mm_aesenc_si128(cx2, ax2); - cx3 = _mm_aesenc_si128(cx3, ax3); - cx4 = _mm_aesenc_si128(cx4, ax4); - } - - SHUFFLE_PHASE_1(l0, (idx0&MASK), bx00, bx10, ax0, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - SHUFFLE_PHASE_1(l1, (idx1&MASK), bx01, bx11, ax1, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - SHUFFLE_PHASE_1(l2, (idx2&MASK), bx02, bx12, ax2, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - SHUFFLE_PHASE_1(l3, (idx3&MASK), bx03, bx13, ax3, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - SHUFFLE_PHASE_1(l4, (idx4&MASK), bx04, bx14, ax4, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - - _mm_store_si128((__m128i*) &l0[idx0 & MASK], _mm_xor_si128(bx00, cx0)); - _mm_store_si128((__m128i*) &l1[idx1 & MASK], _mm_xor_si128(bx01, cx1)); - _mm_store_si128((__m128i*) &l2[idx2 & MASK], _mm_xor_si128(bx02, cx2)); - _mm_store_si128((__m128i*) &l3[idx3 & MASK], _mm_xor_si128(bx03, cx3)); - _mm_store_si128((__m128i*) &l4[idx4 & MASK], _mm_xor_si128(bx04, cx4)); - - idx0 = EXTRACT64(cx0); - idx1 = EXTRACT64(cx1); - idx2 = EXTRACT64(cx2); - idx3 = EXTRACT64(cx3); - idx4 = EXTRACT64(cx4); - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & MASK])[0]; - ch = ((uint64_t*) &l0[idx0 & MASK])[1]; - - INTEGER_MATH_V2(0, cl, cx0); - - lo = __umul128(idx0, cl, &hi); - - SHUFFLE_PHASE_2(l0, (idx0&MASK), bx00, bx10, ax0, lo, hi, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - - al0 += hi; - ah0 += lo; - - ((uint64_t*) &l0[idx0 & MASK])[0] = al0; - ((uint64_t*) &l0[idx0 & MASK])[1] = ah0; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - bx10 = bx00; - bx00 = cx0; - - - cl = ((uint64_t*) &l1[idx1 & MASK])[0]; - ch = ((uint64_t*) &l1[idx1 & MASK])[1]; - - INTEGER_MATH_V2(1, cl, cx1); - - lo = __umul128(idx1, cl, &hi); - - SHUFFLE_PHASE_2(l1, (idx1&MASK), bx01, bx11, ax1, lo, hi, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - - al1 += hi; - ah1 += lo; - - ((uint64_t*) &l1[idx1 & MASK])[0] = al1; - ((uint64_t*) &l1[idx1 & MASK])[1] = ah1; - - ah1 ^= ch; - al1 ^= cl; - idx1 = al1; - - bx11 = bx01; - bx01 = cx1; - - - cl = ((uint64_t*) &l2[idx2 & MASK])[0]; - ch = ((uint64_t*) &l2[idx2 & MASK])[1]; - - INTEGER_MATH_V2(2, cl, cx2); - - lo = __umul128(idx2, cl, &hi); - - SHUFFLE_PHASE_2(l2, (idx2&MASK), bx02, bx12, ax2, lo, hi, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - - al2 += hi; - ah2 += lo; - - ((uint64_t*) &l2[idx2 & MASK])[0] = al2; - ((uint64_t*) &l2[idx2 & MASK])[1] = ah2; - - ah2 ^= ch; - al2 ^= cl; - idx2 = al2; - - bx12 = bx02; - bx02 = cx2; - - - cl = ((uint64_t*) &l3[idx3 & MASK])[0]; - ch = ((uint64_t*) &l3[idx3 & MASK])[1]; - - INTEGER_MATH_V2(3, cl, cx3); - - lo = __umul128(idx3, cl, &hi); - - SHUFFLE_PHASE_2(l3, (idx3&MASK), bx03, bx13, ax3, lo, hi, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - - al3 += hi; - ah3 += lo; - - ((uint64_t*) &l3[idx3 & MASK])[0] = al3; - ((uint64_t*) &l3[idx3 & MASK])[1] = ah3; - - ah3 ^= ch; - al3 ^= cl; - idx3 = al3; - - bx13 = bx03; - bx03 = cx3; - - - cl = ((uint64_t*) &l4[idx4 & MASK])[0]; - ch = ((uint64_t*) &l4[idx4 & MASK])[1]; - - INTEGER_MATH_V2(4, cl, cx4); - - lo = __umul128(idx4, cl, &hi); - - SHUFFLE_PHASE_2(l4, (idx4&MASK), bx04, bx14, ax4, lo, hi, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - - al4 += hi; - ah4 += lo; - - ((uint64_t*) &l4[idx4 & MASK])[0] = al4; - ((uint64_t*) &l4[idx4 & MASK])[1] = ah4; - - ah4 ^= ch; - al4 ^= cl; - idx4 = al4; - - bx14 = bx04; - bx04 = cx4; - } - - cn_implode_scratchpad((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad((__m128i*) l1, (__m128i*) h1); - cn_implode_scratchpad((__m128i*) l2, (__m128i*) h2); - cn_implode_scratchpad((__m128i*) l3, (__m128i*) h3); - cn_implode_scratchpad((__m128i*) l4, (__m128i*) h4); - - keccakf(h0, 24); - keccakf(h1, 24); - keccakf(h2, 24); - keccakf(h3, 24); - keccakf(h4, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - extra_hashes[scratchPad[1]->state[0] & 3](scratchPad[1]->state, 200, output + 32); - extra_hashes[scratchPad[2]->state[0] & 3](scratchPad[2]->state, 200, output + 64); - extra_hashes[scratchPad[3]->state[0] & 3](scratchPad[3]->state, 200, output + 96); - extra_hashes[scratchPad[4]->state[0] & 3](scratchPad[4]->state, 200, output + 128); - } - - // quintuple - inline static void hashPowV4(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad, - uint64_t height) - { - keccak(input, (int) size, scratchPad[0]->state, 200); - keccak(input + size, (int) size, scratchPad[1]->state, 200); - keccak(input + 2 * size, (int) size, scratchPad[2]->state, 200); - keccak(input + 3 * size, (int) size, scratchPad[3]->state, 200); - keccak(input + 4 * size, (int) size, scratchPad[4]->state, 200); - - const uint8_t* l0 = scratchPad[0]->memory; - const uint8_t* l1 = scratchPad[1]->memory; - const uint8_t* l2 = scratchPad[2]->memory; - const uint8_t* l3 = scratchPad[3]->memory; - const uint8_t* l4 = scratchPad[4]->memory; - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - uint64_t* h1 = reinterpret_cast(scratchPad[1]->state); - uint64_t* h2 = reinterpret_cast(scratchPad[2]->state); - uint64_t* h3 = reinterpret_cast(scratchPad[3]->state); - uint64_t* h4 = reinterpret_cast(scratchPad[4]->state); - - cn_explode_scratchpad((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad((__m128i*) h1, (__m128i*) l1); - cn_explode_scratchpad((__m128i*) h2, (__m128i*) l2); - cn_explode_scratchpad((__m128i*) h3, (__m128i*) l3); - cn_explode_scratchpad((__m128i*) h4, (__m128i*) l4); - - uint64_t al0 = h0[0] ^h0[4]; - uint64_t al1 = h1[0] ^h1[4]; - uint64_t al2 = h2[0] ^h2[4]; - uint64_t al3 = h3[0] ^h3[4]; - uint64_t al4 = h4[0] ^h4[4]; - uint64_t ah0 = h0[1] ^h0[5]; - uint64_t ah1 = h1[1] ^h1[5]; - uint64_t ah2 = h2[1] ^h2[5]; - uint64_t ah3 = h3[1] ^h3[5]; - uint64_t ah4 = h4[1] ^h4[5]; - - __m128i bx00 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx01 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - __m128i bx02 = _mm_set_epi64x(h2[3] ^ h2[7], h2[2] ^ h2[6]); - __m128i bx03 = _mm_set_epi64x(h3[3] ^ h3[7], h3[2] ^ h3[6]); - __m128i bx04 = _mm_set_epi64x(h4[3] ^ h4[7], h4[2] ^ h4[6]); - - __m128i bx10 = _mm_set_epi64x(h0[9] ^ h0[11], h0[8] ^ h0[10]); - __m128i bx11 = _mm_set_epi64x(h1[9] ^ h1[11], h1[8] ^ h1[10]); - __m128i bx12 = _mm_set_epi64x(h2[9] ^ h2[11], h2[8] ^ h2[10]); - __m128i bx13 = _mm_set_epi64x(h3[9] ^ h3[11], h3[8] ^ h3[10]); - __m128i bx14 = _mm_set_epi64x(h4[9] ^ h4[11], h4[8] ^ h4[10]); - - uint64_t idx0 = h0[0] ^h0[4]; - uint64_t idx1 = h1[0] ^h1[4]; - uint64_t idx2 = h2[0] ^h2[4]; - uint64_t idx3 = h3[0] ^h3[4]; - uint64_t idx4 = h4[0] ^h4[4]; - - VARIANT4_RANDOM_MATH_INIT(0, h0) - VARIANT4_RANDOM_MATH_INIT(1, h1) - VARIANT4_RANDOM_MATH_INIT(2, h2) - VARIANT4_RANDOM_MATH_INIT(3, h3) - VARIANT4_RANDOM_MATH_INIT(4, h4) - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx0; - __m128i cx1; - __m128i cx2; - __m128i cx3; - __m128i cx4; - - const __m128i ax0 = _mm_set_epi64x(ah0, al0); - const __m128i ax1 = _mm_set_epi64x(ah1, al1); - const __m128i ax2 = _mm_set_epi64x(ah2, al2); - const __m128i ax3 = _mm_set_epi64x(ah3, al3); - const __m128i ax4 = _mm_set_epi64x(ah4, al4); - - if (SOFT_AES) { - cx0 = soft_aesenc((uint32_t*) &l0[idx0 & MASK], ax0); - cx1 = soft_aesenc((uint32_t*) &l1[idx1 & MASK], ax1); - cx2 = soft_aesenc((uint32_t*) &l2[idx2 & MASK], ax2); - cx3 = soft_aesenc((uint32_t*) &l3[idx3 & MASK], ax3); - cx4 = soft_aesenc((uint32_t*) &l4[idx4 & MASK], ax4); - } else { - cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - cx1 = _mm_load_si128((__m128i*) &l1[idx1 & MASK]); - cx2 = _mm_load_si128((__m128i*) &l2[idx2 & MASK]); - cx3 = _mm_load_si128((__m128i*) &l3[idx3 & MASK]); - cx4 = _mm_load_si128((__m128i*) &l4[idx4 & MASK]); - - cx0 = _mm_aesenc_si128(cx0, ax0); - cx1 = _mm_aesenc_si128(cx1, ax1); - cx2 = _mm_aesenc_si128(cx2, ax2); - cx3 = _mm_aesenc_si128(cx3, ax3); - cx4 = _mm_aesenc_si128(cx4, ax4); - } - - SHUFFLE_V4(l0, (idx0&MASK), bx00, bx10, ax0, cx0) - SHUFFLE_V4(l1, (idx1&MASK), bx01, bx11, ax1, cx1) - SHUFFLE_V4(l2, (idx2&MASK), bx02, bx12, ax2, cx2) - SHUFFLE_V4(l3, (idx3&MASK), bx03, bx13, ax3, cx3) - SHUFFLE_V4(l4, (idx4&MASK), bx04, bx14, ax4, cx4) - - _mm_store_si128((__m128i*) &l0[idx0 & MASK], _mm_xor_si128(bx00, cx0)); - _mm_store_si128((__m128i*) &l1[idx1 & MASK], _mm_xor_si128(bx01, cx1)); - _mm_store_si128((__m128i*) &l2[idx2 & MASK], _mm_xor_si128(bx02, cx2)); - _mm_store_si128((__m128i*) &l3[idx3 & MASK], _mm_xor_si128(bx03, cx3)); - _mm_store_si128((__m128i*) &l4[idx4 & MASK], _mm_xor_si128(bx04, cx4)); - - idx0 = EXTRACT64(cx0); - idx1 = EXTRACT64(cx1); - idx2 = EXTRACT64(cx2); - idx3 = EXTRACT64(cx3); - idx4 = EXTRACT64(cx4); - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & MASK])[0]; - ch = ((uint64_t*) &l0[idx0 & MASK])[1]; - - VARIANT4_RANDOM_MATH(0, al0, ah0, cl, bx00, bx10) - - if (VARIANT == POW_V4) { - al0 ^= r0[2] | ((uint64_t)(r0[3]) << 32); - ah0 ^= r0[0] | ((uint64_t)(r0[1]) << 32); - } - - lo = __umul128(idx0, cl, &hi); - - SHUFFLE_V4(l0, (idx0&MASK), bx00, bx10, ax0, cx0); - - al0 += hi; - ah0 += lo; - - ((uint64_t*) &l0[idx0 & MASK])[0] = al0; - ((uint64_t*) &l0[idx0 & MASK])[1] = ah0; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - bx10 = bx00; - bx00 = cx0; - - - cl = ((uint64_t*) &l1[idx1 & MASK])[0]; - ch = ((uint64_t*) &l1[idx1 & MASK])[1]; - - VARIANT4_RANDOM_MATH(1, al1, ah1, cl, bx01, bx11) - - if (VARIANT == POW_V4) { - al1 ^= r1[2] | ((uint64_t)(r1[3]) << 32); - ah1 ^= r1[0] | ((uint64_t)(r1[1]) << 32); - } - - lo = __umul128(idx1, cl, &hi); - - SHUFFLE_V4(l1, (idx1&MASK), bx01, bx11, ax1, cx1); - - al1 += hi; - ah1 += lo; - - ((uint64_t*) &l1[idx1 & MASK])[0] = al1; - ((uint64_t*) &l1[idx1 & MASK])[1] = ah1; - - ah1 ^= ch; - al1 ^= cl; - idx1 = al1; - - bx11 = bx01; - bx01 = cx1; - - - cl = ((uint64_t*) &l2[idx2 & MASK])[0]; - ch = ((uint64_t*) &l2[idx2 & MASK])[1]; - - VARIANT4_RANDOM_MATH(2, al2, ah2, cl, bx02, bx12) - - if (VARIANT == POW_V4) { - al2 ^= r2[2] | ((uint64_t)(r2[3]) << 32); - ah2 ^= r2[0] | ((uint64_t)(r2[1]) << 32); - } - - lo = __umul128(idx2, cl, &hi); - - SHUFFLE_V4(l2, (idx2&MASK), bx02, bx12, ax2, cx2); - - al2 += hi; - ah2 += lo; - - ((uint64_t*) &l2[idx2 & MASK])[0] = al2; - ((uint64_t*) &l2[idx2 & MASK])[1] = ah2; - - ah2 ^= ch; - al2 ^= cl; - idx2 = al2; - - bx12 = bx02; - bx02 = cx2; - - - cl = ((uint64_t*) &l3[idx3 & MASK])[0]; - ch = ((uint64_t*) &l3[idx3 & MASK])[1]; - - VARIANT4_RANDOM_MATH(3, al3, ah3, cl, bx03, bx13) - - if (VARIANT == POW_V4) { - al3 ^= r3[2] | ((uint64_t)(r3[3]) << 32); - ah3 ^= r3[0] | ((uint64_t)(r3[1]) << 32); - } - - lo = __umul128(idx3, cl, &hi); - - SHUFFLE_V4(l3, (idx3&MASK), bx03, bx13, ax3, cx3); - - al3 += hi; - ah3 += lo; - - ((uint64_t*) &l3[idx3 & MASK])[0] = al3; - ((uint64_t*) &l3[idx3 & MASK])[1] = ah3; - - ah3 ^= ch; - al3 ^= cl; - idx3 = al3; - - bx13 = bx03; - bx03 = cx3; - - - cl = ((uint64_t*) &l4[idx4 & MASK])[0]; - ch = ((uint64_t*) &l4[idx4 & MASK])[1]; - - VARIANT4_RANDOM_MATH(4, al4, ah4, cl, bx04, bx14) - - if (VARIANT == POW_V4) { - al4 ^= r4[2] | ((uint64_t)(r4[3]) << 32); - ah4 ^= r4[0] | ((uint64_t)(r4[1]) << 32); - } - - lo = __umul128(idx4, cl, &hi); - - SHUFFLE_V4(l4, (idx4&MASK), bx04, bx14, ax4, cx4); - - al4 += hi; - ah4 += lo; - - ((uint64_t*) &l4[idx4 & MASK])[0] = al4; - ((uint64_t*) &l4[idx4 & MASK])[1] = ah4; - - ah4 ^= ch; - al4 ^= cl; - idx4 = al4; - - bx14 = bx04; - bx04 = cx4; - } - - cn_implode_scratchpad((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad((__m128i*) l1, (__m128i*) h1); - cn_implode_scratchpad((__m128i*) l2, (__m128i*) h2); - cn_implode_scratchpad((__m128i*) l3, (__m128i*) h3); - cn_implode_scratchpad((__m128i*) l4, (__m128i*) h4); - - keccakf(h0, 24); - keccakf(h1, 24); - keccakf(h2, 24); - keccakf(h3, 24); - keccakf(h4, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - extra_hashes[scratchPad[1]->state[0] & 3](scratchPad[1]->state, 200, output + 32); - extra_hashes[scratchPad[2]->state[0] & 3](scratchPad[2]->state, 200, output + 64); - extra_hashes[scratchPad[3]->state[0] & 3](scratchPad[3]->state, 200, output + 96); - extra_hashes[scratchPad[4]->state[0] & 3](scratchPad[4]->state, 200, output + 128); - } - - inline static void hashLiteTube(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - keccak(input, (int) size, scratchPad[0]->state, 200); - keccak(input + size, (int) size, scratchPad[1]->state, 200); - keccak(input + 2 * size, (int) size, scratchPad[2]->state, 200); - keccak(input + 3 * size, (int) size, scratchPad[3]->state, 200); - keccak(input + 4 * size, (int) size, scratchPad[4]->state, 200); - - uint64_t tweak1_2_0 = (*reinterpret_cast(input + 35) ^ - *(reinterpret_cast(scratchPad[0]->state) + 24)); - uint64_t tweak1_2_1 = (*reinterpret_cast(input + 35 + size) ^ - *(reinterpret_cast(scratchPad[1]->state) + 24)); - uint64_t tweak1_2_2 = (*reinterpret_cast(input + 35 + 2 * size) ^ - *(reinterpret_cast(scratchPad[2]->state) + 24)); - uint64_t tweak1_2_3 = (*reinterpret_cast(input + 35 + 3 * size) ^ - *(reinterpret_cast(scratchPad[3]->state) + 24)); - uint64_t tweak1_2_4 = (*reinterpret_cast(input + 35 + 4 * size) ^ - *(reinterpret_cast(scratchPad[4]->state) + 24)); - - - const uint8_t* l0 = scratchPad[0]->memory; - const uint8_t* l1 = scratchPad[1]->memory; - const uint8_t* l2 = scratchPad[2]->memory; - const uint8_t* l3 = scratchPad[3]->memory; - const uint8_t* l4 = scratchPad[4]->memory; - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - uint64_t* h1 = reinterpret_cast(scratchPad[1]->state); - uint64_t* h2 = reinterpret_cast(scratchPad[2]->state); - uint64_t* h3 = reinterpret_cast(scratchPad[3]->state); - uint64_t* h4 = reinterpret_cast(scratchPad[4]->state); - - cn_explode_scratchpad((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad((__m128i*) h1, (__m128i*) l1); - cn_explode_scratchpad((__m128i*) h2, (__m128i*) l2); - cn_explode_scratchpad((__m128i*) h3, (__m128i*) l3); - cn_explode_scratchpad((__m128i*) h4, (__m128i*) l4); - - uint64_t al0 = h0[0] ^h0[4]; - uint64_t al1 = h1[0] ^h1[4]; - uint64_t al2 = h2[0] ^h2[4]; - uint64_t al3 = h3[0] ^h3[4]; - uint64_t al4 = h4[0] ^h4[4]; - uint64_t ah0 = h0[1] ^h0[5]; - uint64_t ah1 = h1[1] ^h1[5]; - uint64_t ah2 = h2[1] ^h2[5]; - uint64_t ah3 = h3[1] ^h3[5]; - uint64_t ah4 = h4[1] ^h4[5]; - - __m128i bx0 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx1 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - __m128i bx2 = _mm_set_epi64x(h2[3] ^ h2[7], h2[2] ^ h2[6]); - __m128i bx3 = _mm_set_epi64x(h3[3] ^ h3[7], h3[2] ^ h3[6]); - __m128i bx4 = _mm_set_epi64x(h4[3] ^ h4[7], h4[2] ^ h4[6]); - - uint64_t idx0 = h0[0] ^h0[4]; - uint64_t idx1 = h1[0] ^h1[4]; - uint64_t idx2 = h2[0] ^h2[4]; - uint64_t idx3 = h3[0] ^h3[4]; - uint64_t idx4 = h4[0] ^h4[4]; - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx0; - __m128i cx1; - __m128i cx2; - __m128i cx3; - __m128i cx4; - - if (SOFT_AES) { - cx0 = soft_aesenc((uint32_t*) &l0[idx0 & MASK], _mm_set_epi64x(ah0, al0)); - cx1 = soft_aesenc((uint32_t*) &l1[idx1 & MASK], _mm_set_epi64x(ah1, al1)); - cx2 = soft_aesenc((uint32_t*) &l2[idx2 & MASK], _mm_set_epi64x(ah2, al2)); - cx3 = soft_aesenc((uint32_t*) &l3[idx3 & MASK], _mm_set_epi64x(ah3, al3)); - cx4 = soft_aesenc((uint32_t*) &l4[idx4 & MASK], _mm_set_epi64x(ah4, al4)); - } else { - cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - cx1 = _mm_load_si128((__m128i*) &l1[idx1 & MASK]); - cx2 = _mm_load_si128((__m128i*) &l2[idx2 & MASK]); - cx3 = _mm_load_si128((__m128i*) &l3[idx3 & MASK]); - cx4 = _mm_load_si128((__m128i*) &l4[idx4 & MASK]); - - cx0 = _mm_aesenc_si128(cx0, _mm_set_epi64x(ah0, al0)); - cx1 = _mm_aesenc_si128(cx1, _mm_set_epi64x(ah1, al1)); - cx2 = _mm_aesenc_si128(cx2, _mm_set_epi64x(ah2, al2)); - cx3 = _mm_aesenc_si128(cx3, _mm_set_epi64x(ah3, al3)); - cx4 = _mm_aesenc_si128(cx4, _mm_set_epi64x(ah4, al4)); - } - - _mm_store_si128((__m128i*) &l0[idx0 & MASK], _mm_xor_si128(bx0, cx0)); - _mm_store_si128((__m128i*) &l1[idx1 & MASK], _mm_xor_si128(bx1, cx1)); - _mm_store_si128((__m128i*) &l2[idx2 & MASK], _mm_xor_si128(bx2, cx2)); - _mm_store_si128((__m128i*) &l3[idx3 & MASK], _mm_xor_si128(bx3, cx3)); - _mm_store_si128((__m128i*) &l4[idx4 & MASK], _mm_xor_si128(bx4, cx4)); - - static const uint32_t table = 0x75310; - uint8_t tmp = reinterpret_cast(&l0[idx0 & MASK])[11]; - uint8_t index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*) (&l0[idx0 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - tmp = reinterpret_cast(&l1[idx1 & MASK])[11]; - index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*) (&l1[idx1 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - tmp = reinterpret_cast(&l2[idx2 & MASK])[11]; - index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*) (&l2[idx2 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - tmp = reinterpret_cast(&l3[idx3 & MASK])[11]; - index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*) (&l3[idx3 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - tmp = reinterpret_cast(&l4[idx4 & MASK])[11]; - index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*) (&l4[idx4 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - - idx0 = EXTRACT64(cx0); - idx1 = EXTRACT64(cx1); - idx2 = EXTRACT64(cx2); - idx3 = EXTRACT64(cx3); - idx4 = EXTRACT64(cx4); - - bx0 = cx0; - bx1 = cx1; - bx2 = cx2; - bx3 = cx3; - bx4 = cx4; - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & MASK])[0]; - ch = ((uint64_t*) &l0[idx0 & MASK])[1]; - lo = __umul128(idx0, cl, &hi); - - al0 += hi; - ah0 += lo; - - ah0 ^= tweak1_2_0; - ((uint64_t*) &l0[idx0 & MASK])[0] = al0; - ((uint64_t*) &l0[idx0 & MASK])[1] = ah0; - ah0 ^= tweak1_2_0; - - ((uint64_t*) &l0[idx0 & MASK])[1] ^= ((uint64_t*) &l0[idx0 & MASK])[0]; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - - cl = ((uint64_t*) &l1[idx1 & MASK])[0]; - ch = ((uint64_t*) &l1[idx1 & MASK])[1]; - lo = __umul128(idx1, cl, &hi); - - al1 += hi; - ah1 += lo; - - ah1 ^= tweak1_2_1; - ((uint64_t*) &l1[idx1 & MASK])[0] = al1; - ((uint64_t*) &l1[idx1 & MASK])[1] = ah1; - ah1 ^= tweak1_2_1; - - ((uint64_t*) &l1[idx1 & MASK])[1] ^= ((uint64_t*) &l1[idx1 & MASK])[0]; - - ah1 ^= ch; - al1 ^= cl; - idx1 = al1; - - - cl = ((uint64_t*) &l2[idx2 & MASK])[0]; - ch = ((uint64_t*) &l2[idx2 & MASK])[1]; - lo = __umul128(idx2, cl, &hi); - - al2 += hi; - ah2 += lo; - - ah2 ^= tweak1_2_2; - ((uint64_t*) &l2[idx2 & MASK])[0] = al2; - ((uint64_t*) &l2[idx2 & MASK])[1] = ah2; - ah2 ^= tweak1_2_2; - - ((uint64_t*) &l2[idx2 & MASK])[1] ^= ((uint64_t*) &l2[idx2 & MASK])[0]; - - ah2 ^= ch; - al2 ^= cl; - idx2 = al2; - - - cl = ((uint64_t*) &l3[idx3 & MASK])[0]; - ch = ((uint64_t*) &l3[idx3 & MASK])[1]; - lo = __umul128(idx3, cl, &hi); - - al3 += hi; - ah3 += lo; - - ah3 ^= tweak1_2_3; - ((uint64_t*) &l3[idx3 & MASK])[0] = al3; - ((uint64_t*) &l3[idx3 & MASK])[1] = ah3; - ah3 ^= tweak1_2_3; - - ((uint64_t*) &l3[idx3 & MASK])[1] ^= ((uint64_t*) &l3[idx3 & MASK])[0]; - - ah3 ^= ch; - al3 ^= cl; - idx3 = al3; - - - cl = ((uint64_t*) &l4[idx4 & MASK])[0]; - ch = ((uint64_t*) &l4[idx4 & MASK])[1]; - lo = __umul128(idx4, cl, &hi); - - al4 += hi; - ah4 += lo; - - ah4 ^= tweak1_2_4; - ((uint64_t*) &l4[idx4 & MASK])[0] = al4; - ((uint64_t*) &l4[idx4 & MASK])[1] = ah4; - ah4 ^= tweak1_2_4; - - ((uint64_t*) &l4[idx4 & MASK])[1] ^= ((uint64_t*) &l4[idx4 & MASK])[0]; - - ah4 ^= ch; - al4 ^= cl; - idx4 = al4; - } - - cn_implode_scratchpad((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad((__m128i*) l1, (__m128i*) h1); - cn_implode_scratchpad((__m128i*) l2, (__m128i*) h2); - cn_implode_scratchpad((__m128i*) l3, (__m128i*) h3); - cn_implode_scratchpad((__m128i*) l4, (__m128i*) h4); - - keccakf(h0, 24); - keccakf(h1, 24); - keccakf(h2, 24); - keccakf(h3, 24); - keccakf(h4, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - extra_hashes[scratchPad[1]->state[0] & 3](scratchPad[1]->state, 200, output + 32); - extra_hashes[scratchPad[2]->state[0] & 3](scratchPad[2]->state, 200, output + 64); - extra_hashes[scratchPad[3]->state[0] & 3](scratchPad[3]->state, 200, output + 96); - extra_hashes[scratchPad[4]->state[0] & 3](scratchPad[4]->state, 200, output + 128); - } - - inline static void hashHeavy(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - // not supported - } - - inline static void hashHeavyTube(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - // not supported - } -}; - -#endif /* __CRYPTONIGHT_ARM_H__ */ diff --git a/src/crypto/CryptoNight_test.h b/src/crypto/CryptoNight_test.h deleted file mode 100644 index e69f87c9..00000000 --- a/src/crypto/CryptoNight_test.h +++ /dev/null @@ -1,300 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * Copyright 2018 BenDroid - * - * - * 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 . - */ - -#ifndef __CRYPTONIGHT_TEST_H__ -#define __CRYPTONIGHT_TEST_H__ - -const static uint8_t test_input[380] = { - 0x03, 0x05, 0xA0, 0xDB, 0xD6, 0xBF, 0x05, 0xCF, 0x16, 0xE5, 0x03, 0xF3, 0xA6, 0x6F, 0x78, 0x00, - 0x7C, 0xBF, 0x34, 0x14, 0x43, 0x32, 0xEC, 0xBF, 0xC2, 0x2E, 0xD9, 0x5C, 0x87, 0x00, 0x38, 0x3B, - 0x30, 0x9A, 0xCE, 0x19, 0x23, 0xA0, 0x96, 0x4B, 0x00, 0x00, 0x00, 0x08, 0xBA, 0x93, 0x9A, 0x62, - 0x72, 0x4C, 0x0D, 0x75, 0x81, 0xFC, 0xE5, 0x76, 0x1E, 0x9D, 0x8A, 0x0E, 0x6A, 0x1C, 0x3F, 0x92, - 0x4F, 0xDD, 0x84, 0x93, 0xD1, 0x11, 0x56, 0x49, 0xC0, 0x5E, 0xB6, 0x01, - 0x01, 0x00, 0xFB, 0x8E, 0x8A, 0xC8, 0x05, 0x89, 0x93, 0x23, 0x37, 0x1B, 0xB7, 0x90, 0xDB, 0x19, - 0x21, 0x8A, 0xFD, 0x8D, 0xB8, 0xE3, 0x75, 0x5D, 0x8B, 0x90, 0xF3, 0x9B, 0x3D, 0x55, 0x06, 0xA9, - 0xAB, 0xCE, 0x4F, 0xA9, 0x12, 0x24, 0x45, 0x00, 0x00, 0x00, 0x00, 0xEE, 0x81, 0x46, 0xD4, 0x9F, - 0xA9, 0x3E, 0xE7, 0x24, 0xDE, 0xB5, 0x7D, 0x12, 0xCB, 0xC6, 0xC6, 0xF3, 0xB9, 0x24, 0xD9, 0x46, - 0x12, 0x7C, 0x7A, 0x97, 0x41, 0x8F, 0x93, 0x48, 0x82, 0x8F, 0x0F, 0x02, - 0x07, 0x07, 0xB4, 0x87, 0xD0, 0xD6, 0x05, 0x26, 0xE0, 0xC6, 0xDD, 0x9B, 0xC7, 0x18, 0xC3, 0xCF, - 0x52, 0x04, 0xBD, 0x4F, 0x9B, 0x27, 0xF6, 0x73, 0xB9, 0x3F, 0xEF, 0x7B, 0xB2, 0xF7, 0x2B, 0xBB, - 0x3F, 0x3E, 0x9C, 0x3E, 0x9D, 0x33, 0x1E, 0xDE, 0xAD, 0xBE, 0xEF, 0x4E, 0x00, 0x91, 0x81, 0x29, - 0x74, 0xB2, 0x70, 0xE7, 0x6D, 0xD2, 0x2A, 0x5F, 0x52, 0x04, 0x93, 0xE6, 0x18, 0x89, 0x40, 0xD8, - 0xC6, 0xE3, 0x90, 0x6E, 0xAA, 0x6A, 0xB7, 0xE2, 0x08, 0x7E, 0x78, 0x0E, - 0x01, 0x00, 0xEE, 0xB2, 0xD1, 0xD6, 0x05, 0xFF, 0x27, 0x7F, 0x26, 0xDB, 0xAA, 0xB2, 0xC9, 0x26, - 0x30, 0xC6, 0xCF, 0x11, 0x64, 0xEA, 0x6C, 0x8A, 0xE0, 0x98, 0x01, 0xF8, 0x75, 0x4B, 0x49, 0xAF, - 0x79, 0x70, 0xAE, 0xEE, 0xA7, 0x62, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x47, 0x8C, 0x63, 0xE7, 0xD8, - 0x40, 0x02, 0x3C, 0xDA, 0xEA, 0x92, 0x52, 0x53, 0xAC, 0xFD, 0xC7, 0x8A, 0x4C, 0x31, 0xB2, 0xF2, - 0xEC, 0x72, 0x7B, 0xFF, 0xCE, 0xC0, 0xE7, 0x12, 0xD4, 0xE9, 0x2A, 0x01, - 0x07, 0x07, 0xA9, 0xB7, 0xD1, 0xD6, 0x05, 0x3F, 0x0D, 0x5E, 0xFD, 0xC7, 0x03, 0xFC, 0xFC, 0xD2, - 0xCE, 0xBC, 0x44, 0xD8, 0xAB, 0x44, 0xA6, 0xA0, 0x3A, 0xE4, 0x4D, 0x8F, 0x15, 0xAF, 0x62, 0x17, - 0xD1, 0xE0, 0x92, 0x85, 0xE4, 0x73, 0xF9, 0x00, 0x00, 0x00, 0xA0, 0xFC, 0x09, 0xDE, 0xAB, 0xF5, - 0x8B, 0x6F, 0x1D, 0xCA, 0xA8, 0xBA, 0xAC, 0x74, 0xDD, 0x74, 0x19, 0xD5, 0xD6, 0x10, 0xEC, 0x38, - 0xCF, 0x50, 0x29, 0x6A, 0x07, 0x0B, 0x93, 0x8F, 0x8F, 0xA8, 0x10, 0x04 -}; - - -// CN -const static uint8_t test_output_v0[160] = { - 0x1A, 0x3F, 0xFB, 0xEE, 0x90, 0x9B, 0x42, 0x0D, 0x91, 0xF7, 0xBE, 0x6E, 0x5F, 0xB5, 0x6D, 0xB7, - 0x1B, 0x31, 0x10, 0xD8, 0x86, 0x01, 0x1E, 0x87, 0x7E, 0xE5, 0x78, 0x6A, 0xFD, 0x08, 0x01, 0x00, - 0x1B, 0x60, 0x6A, 0x3F, 0x4A, 0x07, 0xD6, 0x48, 0x9A, 0x1B, 0xCD, 0x07, 0x69, 0x7B, 0xD1, 0x66, - 0x96, 0xB6, 0x1C, 0x8A, 0xE9, 0x82, 0xF6, 0x1A, 0x90, 0x16, 0x0F, 0x4E, 0x52, 0x82, 0x8A, 0x7F, - 0xA1, 0xB4, 0xFA, 0xE3, 0xE5, 0x76, 0xCE, 0xCF, 0xB7, 0x9C, 0xAF, 0x3E, 0x29, 0x92, 0xE4, 0xE0, - 0x31, 0x24, 0x05, 0x48, 0xBF, 0x8D, 0x5F, 0x7B, 0x11, 0x03, 0x60, 0xAA, 0xD7, 0x50, 0x3F, 0x0C, - 0x2D, 0x30, 0xF3, 0x87, 0x4F, 0x86, 0xA1, 0x4A, 0xB5, 0xA2, 0x1A, 0x08, 0xD0, 0x44, 0x2C, 0x9D, - 0x16, 0xE9, 0x28, 0x49, 0xA1, 0xFF, 0x85, 0x6F, 0x12, 0xBB, 0x7D, 0xAB, 0x11, 0x1C, 0xE7, 0xF7, - 0x2D, 0x9D, 0x19, 0xE4, 0xD2, 0x26, 0x44, 0x1E, 0xCD, 0x22, 0x08, 0x24, 0xA8, 0x97, 0x46, 0x62, - 0x04, 0x84, 0x90, 0x4A, 0xEE, 0x99, 0x14, 0xED, 0xB8, 0xC6, 0x0D, 0x37, 0xA1, 0x66, 0x17, 0xB0 -}; - - -// CN CONCEAL -const static uint8_t test_output_conceal[160] = { - 0xB3, 0xA1, 0x67, 0x86, 0xD2, 0xC9, 0x85, 0xEC, 0xAD, 0xC4, 0x5F, 0x91, 0x05, 0x27, 0xC7, 0xA1, - 0x96, 0xF0, 0xE1, 0xE9, 0x7C, 0x87, 0x09, 0x38, 0x1D, 0x7D, 0x41, 0x93, 0x35, 0xF8, 0x16, 0x72, - 0xC3, 0xBD, 0x8D, 0xE8, 0xD5, 0xAE, 0xB8, 0x59, 0x0A, 0x6C, 0xCB, 0x7B, 0x41, 0x30, 0xF7, 0x04, - 0xA5, 0x7C, 0xF9, 0xCA, 0x20, 0x49, 0x9C, 0xFD, 0xE8, 0x43, 0xCF, 0x66, 0x78, 0xEA, 0x76, 0xDD, - 0x91, 0x0C, 0xDE, 0x29, 0x2A, 0xE0, 0xA8, 0xCA, 0xBC, 0xAA, 0x53, 0x4C, 0x93, 0x3E, 0x7B, 0x2C, - 0xF1, 0xF9, 0xE1, 0x98, 0xB2, 0x92, 0x1E, 0x19, 0x93, 0x2A, 0x74, 0x9D, 0xDB, 0x10, 0x0F, 0x16, - 0xD5, 0x3D, 0xE4, 0xC4, 0x23, 0xD9, 0x2E, 0xFD, 0x79, 0x8D, 0x1E, 0x48, 0x4E, 0x46, 0x08, 0x6C, - 0xFF, 0x8A, 0x49, 0xFA, 0x1E, 0xB0, 0xB6, 0x9A, 0x47, 0x1C, 0xC6, 0x30, 0x36, 0x5D, 0xFD, 0x76, - 0x10, 0x07, 0x44, 0xE6, 0xC8, 0x20, 0x2A, 0x84, 0x9D, 0x70, 0x22, 0x00, 0x8B, 0x9B, 0xBD, 0x8D, - 0x27, 0x49, 0xA6, 0x06, 0xDC, 0xF0, 0xA1, 0x4B, 0x50, 0xA0, 0x12, 0xCD, 0x77, 0x01, 0x4C, 0x28 -}; - - -// CN v7 -const static uint8_t test_output_v1[160] = { - 0xF2, 0x2D, 0x3D, 0x62, 0x03, 0xD2, 0xA0, 0x8B, 0x41, 0xD9, 0x02, 0x72, 0x78, 0xD8, 0xBC, 0xC9, - 0x83, 0xAC, 0xAD, 0xA9, 0xB6, 0x8E, 0x52, 0xE3, 0xC6, 0x89, 0x69, 0x2A, 0x50, 0xE9, 0x21, 0xD9, - 0xC9, 0xFA, 0xE8, 0x42, 0x5D, 0x86, 0x88, 0xDC, 0x23, 0x6B, 0xCD, 0xBC, 0x42, 0xFD, 0xB4, 0x2D, - 0x37, 0x6C, 0x6E, 0xC1, 0x90, 0x50, 0x1A, 0xA8, 0x4B, 0x04, 0xA4, 0xB4, 0xCF, 0x1E, 0xE1, 0x22, - 0xE7, 0x8C, 0x5A, 0x6E, 0x38, 0x30, 0x68, 0x4A, 0x73, 0xFC, 0x1B, 0xC6, 0x6D, 0xFC, 0x8D, 0x98, - 0xB4, 0xC2, 0x23, 0x39, 0xAD, 0xE0, 0x9D, 0xF6, 0x6D, 0x8C, 0x6A, 0xAA, 0xF9, 0xB2, 0xE3, 0x4C, - 0xB6, 0x90, 0x6C, 0xE6, 0x15, 0x5E, 0x46, 0x07, 0x9C, 0xB2, 0x6B, 0xAC, 0x3B, 0xAC, 0x1A, 0xDE, - 0x92, 0x2C, 0xD6, 0x0C, 0x46, 0x9D, 0x9B, 0xC2, 0x84, 0x52, 0x65, 0xF6, 0xBD, 0xFA, 0x0D, 0x74, - 0x00, 0x66, 0x10, 0x07, 0xF1, 0x19, 0x06, 0x3A, 0x6C, 0xFF, 0xEE, 0xB2, 0x40, 0xE5, 0x88, 0x2B, - 0x6C, 0xAB, 0x6B, 0x1D, 0x88, 0xB8, 0x44, 0x25, 0xF4, 0xEA, 0xB7, 0xEC, 0xBA, 0x12, 0x8A, 0x24 -}; - - -// CN V8 -const static uint8_t test_output_v2[160] = { - 0x97, 0x37, 0x82, 0x82, 0xcf, 0x10, 0xe7, 0xad, 0x03, 0x3f, 0x7b, 0x80, 0x74, 0xc4, 0x0e, 0x14, - 0xd0, 0x6e, 0x7f, 0x60, 0x9d, 0xdd, 0xda, 0x78, 0x76, 0x80, 0xb5, 0x8c, 0x05, 0xf4, 0x3d, 0x21, - 0x87, 0x1f, 0xcd, 0x68, 0x23, 0xf6, 0xa8, 0x79, 0xbb, 0x3f, 0x33, 0x95, 0x1c, 0x8e, 0x8e, 0x89, - 0x1d, 0x40, 0x43, 0x88, 0x0b, 0x02, 0xdf, 0xa1, 0xbb, 0x3b, 0xe4, 0x98, 0xb5, 0x0e, 0x75, 0x78, - 0xe6, 0x0d, 0x24, 0x0f, 0x65, 0x85, 0x60, 0x3a, 0x4a, 0xe5, 0x5f, 0x54, 0x9b, 0xc8, 0x79, 0x93, - 0xeb, 0x3d, 0x98, 0x2c, 0xfe, 0x9b, 0xfb, 0x15, 0xb6, 0x88, 0x21, 0x94, 0xb0, 0x05, 0x86, 0x5c, - 0x59, 0x8b, 0x93, 0x7a, 0xda, 0xd2, 0xa2, 0x14, 0xed, 0xb7, 0xc4, 0x5d, 0xa1, 0xef, 0x26, 0xf3, - 0xc7, 0x73, 0x29, 0x4d, 0xf1, 0xc8, 0x2c, 0xe0, 0xd0, 0xe9, 0xed, 0x0c, 0x70, 0x75, 0x05, 0x3e, - 0x5b, 0xf6, 0xa0, 0x6e, 0xea, 0xde, 0x87, 0x0b, 0x06, 0x29, 0x03, 0xbf, 0xb4, 0x85, 0x9d, 0x04, - 0x75, 0x1a, 0xcd, 0x1e, 0xd6, 0xaa, 0x1b, 0x05, 0x24, 0x6a, 0x2c, 0x80, 0x69, 0x68, 0xdc, 0x97 -}; - -// CN XTL -const static uint8_t test_output_xtl[32] = { - 0x8F, 0xE5, 0xF0, 0x5F, 0x02, 0x2A, 0x61, 0x7D, 0xE5, 0x3F, 0x79, 0x36, 0x4B, 0x25, 0xCB, 0xC3, - 0xC0, 0x8E, 0x0E, 0x1F, 0xE3, 0xBE, 0x48, 0x57, 0x07, 0x03, 0xFE, 0xE1, 0xEC, 0x0E, 0xB0, 0xB1 -}; - -// CN MSR -const static uint8_t test_output_msr[32] = { - 0x3C, 0x7A, 0x61, 0x08, 0x4C, 0x5E, 0xB8, 0x65, 0xB4, 0x98, 0xAB, 0x2F, 0x5A, 0x1A, 0xC5, 0x2C, - 0x49, 0xC1, 0x77, 0xC2, 0xD0, 0x13, 0x34, 0x42, 0xD6, 0x5E, 0xD5, 0x14, 0x33, 0x5C, 0x82, 0xC5 -}; - -// CN ALLOY -const static uint8_t test_output_alloy[32] = { - 0x9A, 0x29, 0xD0, 0xC4, 0xAF, 0xDC, 0x63, 0x9B, 0x65, 0x53, 0xB1, 0xC8, 0x37, 0x35, 0x11, 0x4C, - 0x5D, 0x77, 0x16, 0x21, 0x42, 0x97, 0x5C, 0xB8, 0x50, 0xC0, 0xA5, 0x1F, 0x64, 0x07, 0xBD, 0x33 -}; - -// CN RTO/HOSP -const static uint8_t test_output_hosp[32] = { - 0x82, 0x66, 0x1E, 0x1C, 0x6E, 0x64, 0x36, 0x66, 0x84, 0x06, 0x32, 0x7A, 0x9B, 0xB1, 0x13, 0x19, - 0xA5, 0x56, 0x16, 0x15, 0xDF, 0xEC, 0x1C, 0x9E, 0xE3, 0x88, 0x4A, 0x6C, 0x1C, 0xEB, 0x76, 0xA5 -}; - -// CN XFH -const static uint8_t test_output_xfh[64] = { - 0x40, 0x86, 0x5A, 0xA8, 0x87, 0x41, 0xEC, 0x1D, 0xCC, 0xBD, 0x2B, 0xC6, 0xFF, 0x36, 0xB9, 0x4D, - 0x54, 0x71, 0x58, 0xDB, 0x94, 0x69, 0x8E, 0x3C, 0xA0, 0x3D, 0xE4, 0x81, 0x9A, 0x65, 0x9F, 0xEF, - 0x52, 0x97, 0x35, 0x9E, 0xF7, 0x62, 0x9B, 0x1A, 0x9A, 0x9F, 0xE5, 0x0E, 0x50, 0x5F, 0xE6, 0xFB, - 0xA4, 0xCF, 0x30, 0x47, 0xF7, 0xFE, 0x68, 0x4F, 0x7C, 0x87, 0x6A, 0xA6, 0x60, 0x3E, 0xD5, 0x3E -}; - -// CN XTL V9 -const static uint8_t test_output_xtl_v9[64] = { - 0x5D, 0x4F, 0xBC, 0x35, 0x60, 0x97, 0xEA, 0x64, 0x40, 0xB0, 0x88, 0x8E, 0xDE, 0xB6, 0x35, 0xDD, - 0xC8, 0x4A, 0x0E, 0x39, 0x7C, 0x86, 0x84, 0x56, 0x89, 0x5C, 0x3F, 0x29, 0xBE, 0x73, 0x12, 0xA7, - 0x02, 0xE6, 0x1D, 0x2B, 0xBC, 0x84, 0xB6, 0x71, 0x96, 0x71, 0xD5, 0x0C, 0xAC, 0x76, 0x0E, 0x6B, - 0xF1, 0xF0, 0x55, 0x34, 0x15, 0x29, 0x93, 0x04, 0x2D, 0xED, 0xD2, 0x33, 0x50, 0x6E, 0xBE, 0x25 -}; - -// CN XCASH -const static uint8_t test_output_xcash[32] = { - 0xAE, 0xFB, 0xB3, 0xF0, 0xCC, 0x88, 0x04, 0x6D, 0x11, 0x9F, 0x6C, 0x54, 0xB9, 0x6D, 0x90, 0xC9, - 0xE8, 0x84, 0xEA, 0x3B, 0x59, 0x83, 0xA6, 0x0D, 0x50, 0xA4, 0x2D, 0x7D, 0x3E, 0xBE, 0x48, 0x21 -}; - -// CN ZELERIUS -const static uint8_t test_output_zelerius[32] = { - 0x51, 0x6E, 0x33, 0xC6, 0xE4, 0x46, 0xAB, 0xBC, 0xCD, 0xAD, 0x18, 0xC0, 0x4C, 0xD9, 0xA2, 0x5E, - 0x64, 0x10, 0x28, 0x53, 0xB2, 0x0A, 0x42, 0xDF, 0xDE, 0xAA, 0x8B, 0x59, 0x9E, 0xCF, 0x40, 0xE2 -}; - -// CN RWZ -const static uint8_t test_output_rwz[64] = { - 0x5f, 0x56, 0xc6, 0xb0, 0x99, 0x6b, 0xa2, 0x3e, 0x0b, 0xba, 0x07, 0x29, 0xc9, 0x90, 0x74, 0x85, - 0x5a, 0x10, 0xe3, 0x08, 0x7f, 0xdb, 0xfe, 0x94, 0x75, 0x33, 0x54, 0x73, 0x76, 0xf0, 0x75, 0xb8, - 0x8b, 0x70, 0x43, 0x9a, 0xfc, 0xf5, 0xeb, 0x15, 0xbb, 0xf9, 0xad, 0x9d, 0x2a, 0xbd, 0x72, 0x52, - 0x49, 0x54, 0x0b, 0x91, 0xea, 0x61, 0x7f, 0x98, 0x7d, 0x39, 0x17, 0xb7, 0xd7, 0x65, 0xff, 0x75 -}; - -// CN V9 aka CN V4/V5 aka CN-R (height 10000) -const static uint8_t test_output_v4[160] = { - 0x90, 0x20, 0x14, 0x86, 0x1E, 0xCD, 0x01, 0xC5, 0x43, 0xB5, 0x61, 0xFA, 0xC8, 0x3D, 0xFF, 0x7D, - 0x76, 0x67, 0xC2, 0xD7, 0xB3, 0xD4, 0xE3, 0x4B, 0x4C, 0x7E, 0x6D, 0x04, 0x31, 0x79, 0xE6, 0x96, - 0xEA, 0xF4, 0x14, 0x76, 0x38, 0x94, 0x7C, 0xCE, 0x02, 0x50, 0x7A, 0x31, 0xB8, 0x4D, 0xDD, 0x3B, - 0x92, 0xAA, 0xC6, 0x49, 0xA1, 0x64, 0xA1, 0xA8, 0x7C, 0xD9, 0x43, 0x14, 0xC5, 0x12, 0x86, 0x61, - 0x0A, 0x18, 0xBD, 0x11, 0x36, 0x06, 0x31, 0x0D, 0x9D, 0xC0, 0x8C, 0x41, 0x88, 0xCB, 0x7C, 0xE9, - 0x5D, 0xD2, 0xBA, 0xA5, 0xFB, 0x0D, 0x2B, 0xA6, 0x6E, 0x7C, 0x78, 0x72, 0x38, 0xFE, 0x53, 0x17, - 0x1A, 0x96, 0x89, 0x0E, 0x14, 0xFF, 0x34, 0x42, 0xC0, 0x5A, 0xAB, 0xC0, 0x3F, 0x39, 0x4E, 0x43, - 0x91, 0x38, 0x67, 0x79, 0x5B, 0xAE, 0xCC, 0xA7, 0xDB, 0x4C, 0xFE, 0x8B, 0x75, 0x76, 0x1F, 0xC4, - 0x98, 0x71, 0xE6, 0xC1, 0x08, 0x9D, 0xED, 0xCC, 0x47, 0xC3, 0xF3, 0x7A, 0xA9, 0x4A, 0x3A, 0xB9, - 0xAC, 0xB8, 0x5C, 0x9F, 0xCC, 0xCB, 0xC1, 0x93, 0x9E, 0xC6, 0x6D, 0xCC, 0x45, 0xF4, 0xBA, 0xBD -}; - -// CN V9 aka CN V4/V5 aka CN-R (height 10001) -const static uint8_t test_output_v4_1[32] = { - 0x82, 0x58, 0x7D, 0x63, 0x7B, 0x6C, 0x0C, 0x96, 0x6A, 0x50, 0xF6, 0xC0, 0xAB, 0xB5, 0xEA, 0x1A, - 0x58, 0x2B, 0xEA, 0x7E, 0xF0, 0x2F, 0x3C, 0xA1, 0x7C, 0x1C, 0x7C, 0x2E, 0xF9, 0xE5, 0x66, 0xF2 -}; - -// CN V9 aka CN V4/V5 aka CN-R (height 10002) -const static uint8_t test_output_v4_2[32] = { - 0x64, 0xB2, 0x4E, 0x48, 0x4A, 0x28, 0xBF, 0x11, 0xC4, 0x8A, 0x68, 0xE7, 0xB7, 0x4B, 0xFD, 0xA7, - 0xFB, 0x95, 0x66, 0x05, 0x0C, 0xF7, 0xFA, 0xA7, 0x4B, 0xD9, 0x18, 0x59, 0x88, 0x7F, 0x47, 0xA2 -}; - - -// CN-LITE -const static uint8_t test_output_v0_lite[160] = { - 0x36, 0x95, 0xB4, 0xB5, 0x3B, 0xB0, 0x03, 0x58, 0xB0, 0xAD, 0x38, 0xDC, 0x16, 0x0F, 0xEB, 0x9E, - 0x00, 0x4E, 0xEC, 0xE0, 0x9B, 0x83, 0xA7, 0x2E, 0xF6, 0xBA, 0x98, 0x64, 0xD3, 0x51, 0x0C, 0x88, - 0x28, 0xA2, 0x2B, 0xAD, 0x3F, 0x93, 0xD1, 0x40, 0x8F, 0xCA, 0x47, 0x2E, 0xB5, 0xAD, 0x1C, 0xBE, - 0x75, 0xF2, 0x1D, 0x05, 0x3C, 0x8C, 0xE5, 0xB3, 0xAF, 0x10, 0x5A, 0x57, 0x71, 0x3E, 0x21, 0xDD, - 0x38, 0x08, 0xE1, 0x17, 0x0B, 0x99, 0x8D, 0x1A, 0x3C, 0xCE, 0x35, 0xC5, 0xC7, 0x3A, 0x00, 0x2E, - 0xCB, 0x54, 0xF0, 0x78, 0x2E, 0x9E, 0xDB, 0xC7, 0xDF, 0x2E, 0x71, 0x9A, 0x16, 0x97, 0xC4, 0x18, - 0x4B, 0x97, 0x07, 0xFE, 0x5D, 0x98, 0x9A, 0xD6, 0xD8, 0xE5, 0x92, 0x66, 0x87, 0x7F, 0x19, 0x37, - 0xA2, 0x5E, 0xE6, 0x96, 0xB5, 0x97, 0x33, 0x89, 0xE0, 0xA7, 0xC9, 0xDD, 0x4A, 0x7E, 0x9E, 0x53, - 0xBE, 0x91, 0x2B, 0xF5, 0xF5, 0xAF, 0xDD, 0x09, 0xA2, 0xF4, 0xA4, 0x56, 0xEB, 0x96, 0x22, 0xC9, - 0x94, 0xFB, 0x7B, 0x28, 0xC9, 0x97, 0x65, 0x04, 0xAC, 0x4F, 0x84, 0x71, 0xDA, 0x6E, 0xD8, 0xC5 -}; - - -// CN-Lite v7 -const static uint8_t test_output_v1_lite[160] = { - 0x6D, 0x8C, 0xDC, 0x44, 0x4E, 0x9B, 0xBB, 0xFD, 0x68, 0xFC, 0x43, 0xFC, 0xD4, 0x85, 0x5B, 0x22, - 0x8C, 0x8A, 0x1B, 0xD9, 0x1D, 0x9D, 0x00, 0x28, 0x5B, 0xEC, 0x02, 0xB7, 0xCA, 0x2D, 0x67, 0x41, - 0x87, 0xC4, 0xE5, 0x70, 0x65, 0x3E, 0xB4, 0xC2, 0xB4, 0x2B, 0x7A, 0x0D, 0x54, 0x65, 0x59, 0x45, - 0x2D, 0xFA, 0xB5, 0x73, 0xB8, 0x2E, 0xC5, 0x2F, 0x15, 0x2B, 0x7F, 0xF9, 0x8E, 0x79, 0x44, 0x6F, - 0x16, 0x08, 0x74, 0xC7, 0xA2, 0xD2, 0xA3, 0x97, 0x95, 0x76, 0xCA, 0x4D, 0x06, 0x39, 0x7A, 0xAB, - 0x6C, 0x87, 0x58, 0x33, 0x4D, 0xC8, 0x5A, 0xAB, 0x04, 0x27, 0xFE, 0x8B, 0x1C, 0x23, 0x2F, 0x32, - 0xC0, 0x44, 0xFF, 0x0D, 0xB5, 0x3B, 0x27, 0x96, 0x06, 0x89, 0x7B, 0xA3, 0x0B, 0xD0, 0xCE, 0x9E, - 0x90, 0x22, 0x77, 0x5A, 0xAD, 0xA1, 0xE5, 0xB6, 0xFC, 0xCB, 0x39, 0x7E, 0x2B, 0x10, 0xEE, 0xB4, - 0x8C, 0x2B, 0xA4, 0x1F, 0x60, 0x76, 0x39, 0xD7, 0xF6, 0x46, 0x77, 0x18, 0x20, 0xAD, 0xD4, 0xC9, - 0x87, 0xF7, 0x37, 0xDA, 0xFD, 0xBA, 0xBA, 0xD2, 0xF2, 0x68, 0xDC, 0x26, 0x8D, 0x1B, 0x08, 0xC6 -}; - -// CN-Lite IPBC -const static uint8_t test_output_ipbc_lite[160] = { - 0xE4, 0x93, 0x8C, 0xAA, 0x59, 0x8D, 0x02, 0x8A, 0xB8, 0x6F, 0x25, 0xD2, 0xB1, 0x23, 0xD0, 0xD5, - 0x33, 0xE3, 0x9F, 0x37, 0xAC, 0xE5, 0xF8, 0xEB, 0x7A, 0xE8, 0x40, 0xEB, 0x5D, 0xB1, 0x35, 0x5F, - 0xB2, 0x47, 0x86, 0xF0, 0x7F, 0x6F, 0x4B, 0x55, 0x3E, 0xA1, 0xBB, 0xE8, 0xA1, 0x75, 0x00, 0x2D, - 0x07, 0x9A, 0x21, 0x0E, 0xBD, 0x06, 0x6A, 0xB0, 0xFD, 0x96, 0x9E, 0xE6, 0xE4, 0x69, 0x67, 0xBB, - 0x88, 0x45, 0x0B, 0x91, 0x0B, 0x7B, 0xCB, 0x21, 0x3C, 0x3C, 0x09, 0x30, 0x07, 0x71, 0x07, 0xD5, - 0xB8, 0x2D, 0x83, 0x09, 0xAF, 0x7E, 0xB2, 0xA8, 0xAC, 0x25, 0xDC, 0x10, 0xF8, 0x63, 0x6A, 0xBC, - 0x73, 0x01, 0x4E, 0xA8, 0x1C, 0xDA, 0x9A, 0x86, 0x17, 0xEC, 0xA8, 0xFB, 0xAA, 0x23, 0x23, 0x17, - 0xE1, 0x32, 0x68, 0x9C, 0x4C, 0xF4, 0x08, 0xED, 0xB0, 0x15, 0xC3, 0xA9, 0x0F, 0xF0, 0xA2, 0x7E, - 0xD9, 0xE4, 0x23, 0xA7, 0x9E, 0x91, 0xD8, 0x73, 0x94, 0xD6, 0x6C, 0x70, 0x9B, 0x8B, 0x72, 0x92, - 0xA3, 0xA4, 0x0A, 0xE2, 0x3C, 0x0A, 0x34, 0x88, 0xA1, 0x6D, 0xFE, 0x02, 0x44, 0x60, 0x7B, 0x3D -}; - - -// CN-Lite v7 -const static uint8_t test_output_upx[32] = { - 0xD1, 0x13, 0xE1, 0x1B, 0xBE, 0xD3, 0x2A, 0xC1, 0x7C, 0x2C, 0xAA, 0x55, 0xCC, 0x84, 0x2F, 0xA4, - 0x88, 0x91, 0xEE, 0x45, 0x63, 0x22, 0xA3, 0x0A, 0xB2, 0x80, 0xDF, 0x35, 0x16, 0x5C, 0xAF, 0x9A -}; - -// CN-Heavy -const static uint8_t test_output_heavy[96] = { - 0x99, 0x83, 0xF2, 0x1B, 0xDF, 0x20, 0x10, 0xA8, 0xD7, 0x07, 0xBB, 0x2F, 0x14, 0xD7, 0x86, 0x64, - 0xBB, 0xE1, 0x18, 0x7F, 0x55, 0x01, 0x4B, 0x39, 0xE5, 0xF3, 0xD6, 0x93, 0x28, 0xE4, 0x8F, 0xC2, - 0x4D, 0x94, 0x7D, 0xD6, 0xDB, 0x6E, 0x07, 0x48, 0x26, 0x4A, 0x51, 0x2E, 0xAC, 0xF3, 0x25, 0x4A, - 0x1F, 0x1A, 0xA2, 0x5B, 0xFC, 0x0A, 0xAD, 0x82, 0xDE, 0xA8, 0x99, 0x96, 0x88, 0x52, 0xD2, 0x7D, - 0x3E, 0xE1, 0x23, 0x03, 0x5A, 0x63, 0x7B, 0x66, 0xF6, 0xD7, 0xC2, 0x2A, 0x34, 0x5E, 0x88, 0xE7, - 0xFA, 0xC4, 0x25, 0x36, 0x54, 0xCB, 0xD2, 0x5C, 0x2F, 0x80, 0x2A, 0xF9, 0xCC, 0x43, 0xF7, 0xCD -}; - -// CN-Heavy Haven -const static uint8_t test_output_heavy_haven[96] = { - 0x5A, 0xC3, 0xF7, 0x85, 0xC4, 0x90, 0xC5, 0x85, 0x50, 0xEC, 0x95, 0xD2, 0x72, 0x65, 0x63, 0x57, - 0x7E, 0x7C, 0x1C, 0x21, 0x2D, 0x0C, 0xDE, 0x59, 0x12, 0x73, 0x20, 0x1E, 0x44, 0xFD, 0xD5, 0xB6, - 0x1F, 0x4E, 0xB2, 0x0A, 0x36, 0x51, 0x4B, 0xF5, 0x4D, 0xC9, 0xE0, 0x90, 0x2C, 0x16, 0x47, 0x3F, - 0xDE, 0x18, 0x29, 0x8E, 0xBB, 0x34, 0x2B, 0xEF, 0x7A, 0x04, 0x22, 0xD1, 0xB1, 0xF2, 0x48, 0xDA, - 0xE3, 0x7F, 0x4B, 0x4C, 0xB4, 0xDF, 0xE8, 0xD3, 0x70, 0xE2, 0xE7, 0x44, 0x25, 0x87, 0x12, 0xF9, - 0x8F, 0x28, 0x0B, 0xCE, 0x2C, 0xEE, 0xDD, 0x88, 0x94, 0x35, 0x48, 0x51, 0xAE, 0xC8, 0x9C, 0x0B -}; - -// CN-Heavy Tube -const static uint8_t test_output_heavy_tube[96] = { - 0xFE, 0x53, 0x35, 0x20, 0x76, 0xEA, 0xE6, 0x89, 0xFA, 0x3B, 0x4F, 0xDA, 0x61, 0x46, 0x34, 0xCF, - 0xC3, 0x12, 0xEE, 0x0C, 0x38, 0x7D, 0xF2, 0xB8, 0xB7, 0x4D, 0xA2, 0xA1, 0x59, 0x74, 0x12, 0x35, - 0xCD, 0x3F, 0x29, 0xDF, 0x07, 0x4A, 0x14, 0xAD, 0x0B, 0x98, 0x99, 0x37, 0xCA, 0x14, 0x68, 0xA3, - 0x8D, 0xAE, 0x86, 0xC1, 0xA3, 0x54, 0x05, 0xBE, 0xEA, 0x6D, 0x29, 0x24, 0x0C, 0x82, 0x97, 0x74, - 0xA0, 0x64, 0x77, 0xCD, 0x8D, 0x8A, 0xC3, 0x10, 0xB4, 0x89, 0x0E, 0xBB, 0x7D, 0xE6, 0x32, 0x8F, - 0xF4, 0x2D, 0xB6, 0x9E, 0x8A, 0xF9, 0xF8, 0xEE, 0x2C, 0xD0, 0x74, 0xED, 0xA9, 0xAA, 0xA1, 0xFB -}; - -// CN-Ultralite/Turtle -const static uint8_t test_output_turtle[64] = { - 0x08, 0xF4, 0x21, 0xD7, 0x83, 0x31, 0x17, 0x30, 0x0E, 0xDA, 0x66, 0xE9, 0x8F, 0x4A, 0x25, 0x69, - 0x09, 0x3D, 0xF3, 0x00, 0x50, 0x01, 0x73, 0x94, 0x4E, 0xFC, 0x40, 0x1E, 0x9A, 0x4A, 0x17, 0xAF, - 0xB2, 0x17, 0x2E, 0xC9, 0x46, 0x6E, 0x1A, 0xEE, 0x70, 0xEC, 0x85, 0x72, 0xA1, 0x4C, 0x23, 0x3E, - 0xE3, 0x54, 0x58, 0x2B, 0xCB, 0x93, 0xF8, 0x69, 0xD4, 0x29, 0x74, 0x4D, 0xE5, 0x72, 0x6A, 0x26 -}; - - -// CN-Extremelite/UPX2 -const static uint8_t test_output_upx2[64] = { - 0xAA, 0xBB, 0xB8, 0xED, 0x14, 0xA8, 0x35, 0xFA, 0x22, 0xCF, 0xB1, 0xB5, 0xDE, 0xA8, 0x72, 0xB0, - 0xA1, 0xD6, 0xCB, 0xD8, 0x46, 0xF4, 0x39, 0x1C, 0x0F, 0x01, 0xF3, 0x87, 0x5E, 0x3A, 0x37, 0x61, - 0x38, 0x59, 0x15, 0x72, 0xF8, 0x20, 0xD4, 0xDE, 0x25, 0x3C, 0xF5, 0x5A, 0x21, 0x92, 0xB6, 0x22, - 0xB0, 0x28, 0x9E, 0x2E, 0x5C, 0x36, 0x16, 0xE6, 0x1E, 0x78, 0x7A, 0x8F, 0xE4, 0x62, 0xEC, 0x5A -}; - -#endif /* __CRYPTONIGHT_TEST_H__ */ diff --git a/src/crypto/CryptoNight_x86.h b/src/crypto/CryptoNight_x86.h deleted file mode 100644 index ec9c2af1..00000000 --- a/src/crypto/CryptoNight_x86.h +++ /dev/null @@ -1,6198 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * Copyright 2018 Sebastian Stolzenberg - * Copyright 2018 BenDroid - * - * - * 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 . - */ - -#ifndef __CRYPTONIGHT_X86_H__ -#define __CRYPTONIGHT_X86_H__ - - -#ifdef __GNUC__ -# include -#include - -#else -# include -# define __restrict__ __restrict -#endif - -#define SWAP32LE(x) x -#define SWAP64LE(x) x -#define hash_extra_blake(data, length, hash) blake256_hash((uint8_t*)(hash), (uint8_t*)(data), (length)) - -#ifndef NOINLINE -#ifdef __GNUC__ -#define NOINLINE __attribute__ ((noinline)) -#elif _MSC_VER -#define NOINLINE __declspec(noinline) -#else -#define NOINLINE -#endif -#endif - -#include "crypto/CryptoNight.h" -#include "crypto/soft_aes.h" -#include "AsmOptimization.h" -#include "variant4_random_math.h" - -extern "C" -{ -#include "crypto/c_keccak.h" -#include "crypto/c_groestl.h" -#include "crypto/c_blake256.h" -#include "crypto/c_jh.h" -#include "crypto/c_skein.h" - -#ifndef XMRIG_NO_ASM - void cnv1_main_loop_sandybridge_asm(ScratchPad* ctx0); - void cnv1_main_loop_lite_sandybridge_asm(ScratchPad* ctx0); - void cnv1_main_loop_fast_sandybridge_asm(ScratchPad* ctx0); - void cnv1_main_loop_upx_sandybridge_asm(ScratchPad* ctx0); - void cnv1_main_loop_rto_sandybridge_asm(ScratchPad* ctx0); - - void cnv2_main_loop_ivybridge_asm(ScratchPad* ctx0); - void cnv2_main_loop_ryzen_asm(ScratchPad* ctx0); - void cnv2_main_loop_bulldozer_asm(ScratchPad* ctx0); - void cnv2_double_main_loop_sandybridge_asm(ScratchPad* ctx0, ScratchPad* ctx1); - - void cnv2_main_loop_fastv2_ivybridge_asm(ScratchPad* ctx0); - void cnv2_main_loop_fastv2_ryzen_asm(ScratchPad* ctx0); - void cnv2_main_loop_fastv2_bulldozer_asm(ScratchPad* ctx0); - void cnv2_double_main_loop_fastv2_sandybridge_asm(ScratchPad* ctx0, ScratchPad* ctx1); - - void cnv2_main_loop_ultralite_ivybridge_asm(ScratchPad* ctx0); - void cnv2_main_loop_ultralite_ryzen_asm(ScratchPad* ctx0); - void cnv2_main_loop_ultralite_bulldozer_asm(ScratchPad* ctx0); - void cnv2_double_main_loop_ultralite_sandybridge_asm(ScratchPad* ctx0, ScratchPad* ctx1); - - void cnv2_main_loop_xcash_ivybridge_asm(ScratchPad* ctx0); - void cnv2_main_loop_xcash_ryzen_asm(ScratchPad* ctx0); - void cnv2_main_loop_xcash_bulldozer_asm(ScratchPad* ctx0); - void cnv2_double_main_loop_xcash_sandybridge_asm(ScratchPad* ctx0, ScratchPad* ctx1); - - void cnv2_main_loop_zelerius_ivybridge_asm(ScratchPad* ctx0); - void cnv2_main_loop_zelerius_ryzen_asm(ScratchPad* ctx0); - void cnv2_main_loop_zelerius_bulldozer_asm(ScratchPad* ctx0); - void cnv2_double_main_loop_zelerius_sandybridge_asm(ScratchPad* ctx0, ScratchPad* ctx1); - - void cnv2_main_loop_rwz_original_all_asm(ScratchPad* ctx0); - void cnv2_double_main_loop_rwz_original_all_asm(ScratchPad* ctx0, ScratchPad* ctx1); - - void cnv2_main_loop_rwz_upx2_all_asm(ScratchPad* ctx0); - void cnv2_double_main_loop_rwz_upx2_all_asm(ScratchPad* ctx0, ScratchPad* ctx1); - - void cnv1_main_loop_soft_aes_sandybridge_asm(ScratchPad* ctx0); - void cnv1_main_loop_lite_soft_aes_sandybridge_asm(ScratchPad* ctx0); - void cnv1_main_loop_fast_soft_aes_sandybridge_asm(ScratchPad* ctx0); - void cnv1_main_loop_upx_soft_aes_sandybridge_asm(ScratchPad* ctx0); - void cnv1_main_loop_rto_soft_aes_sandybridge_asm(ScratchPad* ctx0); - - void cnv2_main_loop_soft_aes_sandybridge_asm(ScratchPad* ctx0); - void cnv2_main_loop_fastv2_soft_aes_sandybridge_asm(ScratchPad* ctx0); - void cnv2_main_loop_ultralite_soft_aes_sandybridge_asm(ScratchPad* ctx); - void cnv2_main_loop_xcash_soft_aes_sandybridge_asm(ScratchPad* ctx); - void cnv2_main_loop_zelerius_soft_aes_sandybridge_asm(ScratchPad* ctx); - - void wow_soft_aes_compile_code(const V4_Instruction* code, int code_size, void* machine_code, AsmOptimization ASM); - void wow_compile_code(const V4_Instruction* code, int code_size, void* machine_code, AsmOptimization ASM); - void wow_compile_code_double(const V4_Instruction* code, int code_size, void* machine_code, AsmOptimization ASM); - - void v4_soft_aes_compile_code(const V4_Instruction* code, int code_size, void* machine_code, AsmOptimization ASM); - void v4_compile_code(const V4_Instruction* code, int code_size, void* machine_code, AsmOptimization ASM); - void v4_compile_code_double(const V4_Instruction* code, int code_size, void* machine_code, AsmOptimization ASM); -#endif -} - -#ifdef __GNUC__ -#define LIKELY(X) __builtin_expect(X, 1) -#define UNLIKELY(X) __builtin_expect(X, 0) -#else -#define LIKELY(X) X -#define UNLIKELY(X) X -#endif - -#if defined(__x86_64__) || defined(_M_AMD64) -# define EXTRACT64(X) _mm_cvtsi128_si64(X) - -# ifdef __GNUC__ -static inline uint64_t __umul128(uint64_t a, uint64_t b, uint64_t* hi) -{ - unsigned __int128 r = (unsigned __int128) a * (unsigned __int128) b; - *hi = r >> 64; - return (uint64_t) r; -} -# else -#define __umul128 _umul128 -# endif -#elif defined(__i386__) || defined(_M_IX86) -# define HI32(X) \ - _mm_srli_si128((X), 4) - - -# define EXTRACT64(X) \ - ((uint64_t)(uint32_t)_mm_cvtsi128_si32(X) | \ - ((uint64_t)(uint32_t)_mm_cvtsi128_si32(HI32(X)) << 32)) - -static inline int64_t _mm_cvtsi128_si64(__m128i a) -{ - return ((uint64_t)(uint32_t)_mm_cvtsi128_si32(a) | ((uint64_t)(uint32_t)_mm_cvtsi128_si32(_mm_srli_si128(a, 4)) << 32)); -} - -static inline __m128i _mm_cvtsi64_si128(int64_t a) { - return _mm_set_epi64x(0, a); -} - -static inline uint64_t __umul128(uint64_t multiplier, uint64_t multiplicand, uint64_t *product_hi) { - // multiplier = ab = a * 2^32 + b - // multiplicand = cd = c * 2^32 + d - // ab * cd = a * c * 2^64 + (a * d + b * c) * 2^32 + b * d - uint64_t a = multiplier >> 32; - uint64_t b = multiplier & 0xFFFFFFFF; - uint64_t c = multiplicand >> 32; - uint64_t d = multiplicand & 0xFFFFFFFF; - - //uint64_t ac = a * c; - uint64_t ad = a * d; - //uint64_t bc = b * c; - uint64_t bd = b * d; - - uint64_t adbc = ad + (b * c); - uint64_t adbc_carry = adbc < ad ? 1 : 0; - - // multiplier * multiplicand = product_hi * 2^64 + product_lo - uint64_t product_lo = bd + (adbc << 32); - uint64_t product_lo_carry = product_lo < bd ? 1 : 0; - *product_hi = (a * c) + (adbc >> 32) + (adbc_carry << 32) + product_lo_carry; - - return product_lo; -} -#endif - -#ifdef _MSC_VER -# define SET_ROUNDING_MODE_UP() _control87(RC_UP, MCW_RC); -# define SET_ROUNDING_MODE_DOWN() _control87(RC_DOWN, MCW_RC); -# define SET_ROUNDING_MODE_NEAREST() _control87(RC_NEAR, MCW_RC);; -#else -# define SET_ROUNDING_MODE_UP() std::fesetround(FE_UPWARD); -# define SET_ROUNDING_MODE_DOWN() fesetround(FE_DOWNWARD); -# define SET_ROUNDING_MODE_NEAREST() fesetround(FE_TONEAREST); -#endif - - -# define SHUFFLE_PHASE_1(l, idx, bx0, bx1, ax, reverse) \ -{ \ - const __m128i chunk1 = _mm_load_si128((__m128i *)((l) + ((idx) ^ (reverse ? 0x30 : 0x10)))); \ - const __m128i chunk2 = _mm_load_si128((__m128i *)((l) + ((idx) ^ 0x20))); \ - const __m128i chunk3 = _mm_load_si128((__m128i *)((l) + ((idx) ^ (reverse ? 0x10 : 0x30)))); \ - _mm_store_si128((__m128i *)((l) + ((idx) ^ 0x10)), _mm_add_epi64(chunk3, bx1)); \ - _mm_store_si128((__m128i *)((l) + ((idx) ^ 0x20)), _mm_add_epi64(chunk1, bx0)); \ - _mm_store_si128((__m128i *)((l) + ((idx) ^ 0x30)), _mm_add_epi64(chunk2, ax)); \ -} - -# define INTEGER_MATH_V2(idx, cl, cx) \ -{ \ - const uint64_t cx_ = _mm_cvtsi128_si64(cx); \ - cl ^= static_cast(_mm_cvtsi128_si64(division_result_xmm##idx)) ^ (sqrt_result##idx << 32); \ - const uint32_t d_ = (cx_ + (sqrt_result##idx << 1)) | 0x80000001UL; \ - const uint64_t cx1_ = _mm_cvtsi128_si64(_mm_srli_si128(cx, 8)); \ - const uint64_t division_result = static_cast(cx1_ / d_) + ((cx1_ % d_) << 32); \ - division_result_xmm##idx = _mm_cvtsi64_si128(static_cast(division_result)); \ - sqrt_result##idx = int_sqrt_v2(cx_ + division_result); \ -} - -# define SHUFFLE_PHASE_2(l, idx, bx0, bx1, ax, lo, hi, reverse) \ -{ \ - const __m128i chunk1 = _mm_xor_si128(_mm_load_si128((__m128i *)((l) + ((idx) ^ 0x10))), _mm_set_epi64x(lo, hi)); \ - const __m128i chunk2 = _mm_load_si128((__m128i *)((l) + ((idx) ^ 0x20))); \ - const __m128i chunk3 = _mm_load_si128((__m128i *)((l) + ((idx) ^ 0x30))); \ - hi ^= ((uint64_t*)((l) + ((idx) ^ 0x20)))[0]; \ - lo ^= ((uint64_t*)((l) + ((idx) ^ 0x20)))[1]; \ - if (reverse) { \ - _mm_store_si128((__m128i *)((l) + ((idx) ^ 0x10)), _mm_add_epi64(chunk1, bx1)); \ - _mm_store_si128((__m128i *)((l) + ((idx) ^ 0x20)), _mm_add_epi64(chunk3, bx0)); \ - } else { \ - _mm_store_si128((__m128i *)((l) + ((idx) ^ 0x10)), _mm_add_epi64(chunk3, bx1)); \ - _mm_store_si128((__m128i *)((l) + ((idx) ^ 0x20)), _mm_add_epi64(chunk1, bx0)); \ - } \ - _mm_store_si128((__m128i *)((l) + ((idx) ^ 0x30)), _mm_add_epi64(chunk2, ax)); \ -} - -# define SHUFFLE_V4(l, idx, bx0, bx1, ax, cx) \ -{ \ - const __m128i chunk1 = _mm_load_si128((__m128i *)((l) + ((idx) ^ 0x10))); \ - const __m128i chunk2 = _mm_load_si128((__m128i *)((l) + ((idx) ^ 0x20))); \ - const __m128i chunk3 = _mm_load_si128((__m128i *)((l) + ((idx) ^ 0x30))); \ - _mm_store_si128((__m128i *)((l) + ((idx) ^ 0x10)), _mm_add_epi64(chunk3, bx1)); \ - _mm_store_si128((__m128i *)((l) + ((idx) ^ 0x20)), _mm_add_epi64(chunk1, bx0)); \ - _mm_store_si128((__m128i *)((l) + ((idx) ^ 0x30)), _mm_add_epi64(chunk2, ax)); \ - cx = _mm_xor_si128(_mm_xor_si128(cx, chunk3), _mm_xor_si128(chunk1, chunk2)); \ -} - -# define VARIANT4_RANDOM_MATH_INIT(idx, h) \ - uint32_t r##idx[9]; \ - struct V4_Instruction code##idx[256]; \ - r##idx[0] = (uint32_t)(h[12]); \ - r##idx[1] = (uint32_t)(h[12] >> 32); \ - r##idx[2] = (uint32_t)(h[13]); \ - r##idx[3] = (uint32_t)(h[13] >> 32); \ - v4_random_math_init(code##idx, VARIANT, height); - -# define VARIANT4_RANDOM_MATH(idx, al, ah, cl, bx0, bx1) \ - cl ^= (r##idx[0] + r##idx[1]) | ((uint64_t)(r##idx[2] + r##idx[3]) << 32); \ - r##idx[4] = static_cast(al); \ - r##idx[5] = static_cast(ah); \ - r##idx[6] = static_cast(_mm_cvtsi128_si32(bx0)); \ - r##idx[7] = static_cast(_mm_cvtsi128_si32(bx1)); \ - r##idx[8] = static_cast(_mm_cvtsi128_si32(_mm_srli_si128(bx1, 8))); \ - v4_random_math(code##idx, r##idx); \ - -static inline void do_blake_hash(const uint8_t *input, size_t len, uint8_t *output) { - blake256_hash(output, input, len); -} - - -static inline void do_groestl_hash(const uint8_t *input, size_t len, uint8_t *output) { - groestl(input, len * 8, output); -} - - -static inline void do_jh_hash(const uint8_t *input, size_t len, uint8_t *output) { - jh_hash(32 * 8, input, 8 * len, output); -} - - -static inline void do_skein_hash(const uint8_t *input, size_t len, uint8_t *output) { - xmr_skein(input, output); -} - - -void (* const extra_hashes[4])(const uint8_t *, size_t, uint8_t *) = {do_blake_hash, do_groestl_hash, do_jh_hash, do_skein_hash}; - - -// This will shift and xor tmp1 into itself as 4 32-bit vals such as -// sl_xor(a1 a2 a3 a4) = a1 (a2^a1) (a3^a2^a1) (a4^a3^a2^a1) -static inline __m128i sl_xor(__m128i tmp1) -{ - __m128i tmp4; - tmp4 = _mm_slli_si128(tmp1, 0x04); - tmp1 = _mm_xor_si128(tmp1, tmp4); - tmp4 = _mm_slli_si128(tmp4, 0x04); - tmp1 = _mm_xor_si128(tmp1, tmp4); - tmp4 = _mm_slli_si128(tmp4, 0x04); - tmp1 = _mm_xor_si128(tmp1, tmp4); - return tmp1; -} - - -template -static inline void aes_genkey_sub(__m128i* xout0, __m128i* xout2) -{ - __m128i xout1 = _mm_aeskeygenassist_si128(*xout2, rcon); - xout1 = _mm_shuffle_epi32(xout1, 0xFF); // see PSHUFD, set all elems to 4th elem - *xout0 = sl_xor(*xout0); - *xout0 = _mm_xor_si128(*xout0, xout1); - xout1 = _mm_aeskeygenassist_si128(*xout0, 0x00); - xout1 = _mm_shuffle_epi32(xout1, 0xAA); // see PSHUFD, set all elems to 3rd elem - *xout2 = sl_xor(*xout2); - *xout2 = _mm_xor_si128(*xout2, xout1); -} - - -template -static inline void soft_aes_genkey_sub(__m128i* xout0, __m128i* xout2) -{ - __m128i xout1 = soft_aeskeygenassist(*xout2); - xout1 = _mm_shuffle_epi32(xout1, 0xFF); // see PSHUFD, set all elems to 4th elem - *xout0 = sl_xor(*xout0); - *xout0 = _mm_xor_si128(*xout0, xout1); - xout1 = soft_aeskeygenassist<0x00>(*xout0); - xout1 = _mm_shuffle_epi32(xout1, 0xAA); // see PSHUFD, set all elems to 3rd elem - *xout2 = sl_xor(*xout2); - *xout2 = _mm_xor_si128(*xout2, xout1); -} - - -template -static inline void -aes_genkey(const __m128i* memory, __m128i* k0, __m128i* k1, __m128i* k2, __m128i* k3, __m128i* k4, __m128i* k5, - __m128i* k6, __m128i* k7, __m128i* k8, __m128i* k9) -{ - __m128i xout0 = _mm_load_si128(memory); - __m128i xout2 = _mm_load_si128(memory + 1); - *k0 = xout0; - *k1 = xout2; - - SOFT_AES ? soft_aes_genkey_sub<0x01>(&xout0, &xout2) : aes_genkey_sub<0x01>(&xout0, &xout2); - *k2 = xout0; - *k3 = xout2; - - SOFT_AES ? soft_aes_genkey_sub<0x02>(&xout0, &xout2) : aes_genkey_sub<0x02>(&xout0, &xout2); - *k4 = xout0; - *k5 = xout2; - - SOFT_AES ? soft_aes_genkey_sub<0x04>(&xout0, &xout2) : aes_genkey_sub<0x04>(&xout0, &xout2); - *k6 = xout0; - *k7 = xout2; - - SOFT_AES ? soft_aes_genkey_sub<0x08>(&xout0, &xout2) : aes_genkey_sub<0x08>(&xout0, &xout2); - *k8 = xout0; - *k9 = xout2; -} - - -template -static inline void -aes_round(__m128i key, __m128i* x0, __m128i* x1, __m128i* x2, __m128i* x3, __m128i* x4, __m128i* x5, __m128i* x6, - __m128i* x7) -{ - if (SOFT_AES) { - *x0 = soft_aesenc((uint32_t*)x0, key); - *x1 = soft_aesenc((uint32_t*)x1, key); - *x2 = soft_aesenc((uint32_t*)x2, key); - *x3 = soft_aesenc((uint32_t*)x3, key); - *x4 = soft_aesenc((uint32_t*)x4, key); - *x5 = soft_aesenc((uint32_t*)x5, key); - *x6 = soft_aesenc((uint32_t*)x6, key); - *x7 = soft_aesenc((uint32_t*)x7, key); - } - else { - *x0 = _mm_aesenc_si128(*x0, key); - *x1 = _mm_aesenc_si128(*x1, key); - *x2 = _mm_aesenc_si128(*x2, key); - *x3 = _mm_aesenc_si128(*x3, key); - *x4 = _mm_aesenc_si128(*x4, key); - *x5 = _mm_aesenc_si128(*x5, key); - *x6 = _mm_aesenc_si128(*x6, key); - *x7 = _mm_aesenc_si128(*x7, key); - } -} - -inline void mix_and_propagate(__m128i& x0, __m128i& x1, __m128i& x2, __m128i& x3, __m128i& x4, __m128i& x5, __m128i& x6, __m128i& x7) -{ - __m128i tmp0 = x0; - x0 = _mm_xor_si128(x0, x1); - x1 = _mm_xor_si128(x1, x2); - x2 = _mm_xor_si128(x2, x3); - x3 = _mm_xor_si128(x3, x4); - x4 = _mm_xor_si128(x4, x5); - x5 = _mm_xor_si128(x5, x6); - x6 = _mm_xor_si128(x6, x7); - x7 = _mm_xor_si128(x7, tmp0); -} - -template -static inline void cn_explode_scratchpad(const __m128i* input, __m128i* output) -{ - __m128i xin0, xin1, xin2, xin3, xin4, xin5, xin6, xin7; - __m128i k0, k1, k2, k3, k4, k5, k6, k7, k8, k9; - - aes_genkey(input, &k0, &k1, &k2, &k3, &k4, &k5, &k6, &k7, &k8, &k9); - - xin0 = _mm_load_si128(input + 4); - xin1 = _mm_load_si128(input + 5); - xin2 = _mm_load_si128(input + 6); - xin3 = _mm_load_si128(input + 7); - xin4 = _mm_load_si128(input + 8); - xin5 = _mm_load_si128(input + 9); - xin6 = _mm_load_si128(input + 10); - xin7 = _mm_load_si128(input + 11); - - for (size_t i = 0; i < MEM / sizeof(__m128i); i += 8) { - aes_round(k0, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k1, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k2, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k3, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k4, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k5, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k6, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k7, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k8, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k9, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - - _mm_store_si128(output + i + 0, xin0); - _mm_store_si128(output + i + 1, xin1); - _mm_store_si128(output + i + 2, xin2); - _mm_store_si128(output + i + 3, xin3); - _mm_store_si128(output + i + 4, xin4); - _mm_store_si128(output + i + 5, xin5); - _mm_store_si128(output + i + 6, xin6); - _mm_store_si128(output + i + 7, xin7); - } -} - -template -static inline void cn_explode_scratchpad_heavy(const __m128i* input, __m128i* output) -{ - __m128i xin0, xin1, xin2, xin3, xin4, xin5, xin6, xin7; - __m128i k0, k1, k2, k3, k4, k5, k6, k7, k8, k9; - - aes_genkey(input, &k0, &k1, &k2, &k3, &k4, &k5, &k6, &k7, &k8, &k9); - - xin0 = _mm_load_si128(input + 4); - xin1 = _mm_load_si128(input + 5); - xin2 = _mm_load_si128(input + 6); - xin3 = _mm_load_si128(input + 7); - xin4 = _mm_load_si128(input + 8); - xin5 = _mm_load_si128(input + 9); - xin6 = _mm_load_si128(input + 10); - xin7 = _mm_load_si128(input + 11); - - for (size_t i = 0; i < 16; i++) { - aes_round(k0, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k1, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k2, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k3, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k4, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k5, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k6, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k7, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k8, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k9, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - - mix_and_propagate(xin0, xin1, xin2, xin3, xin4, xin5, xin6, xin7); - } - - for (size_t i = 0; i < MEM / sizeof(__m128i); i += 8) { - aes_round(k0, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k1, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k2, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k3, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k4, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k5, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k6, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k7, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k8, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - aes_round(k9, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); - - _mm_store_si128(output + i + 0, xin0); - _mm_store_si128(output + i + 1, xin1); - _mm_store_si128(output + i + 2, xin2); - _mm_store_si128(output + i + 3, xin3); - _mm_store_si128(output + i + 4, xin4); - _mm_store_si128(output + i + 5, xin5); - _mm_store_si128(output + i + 6, xin6); - _mm_store_si128(output + i + 7, xin7); - } -} - - -template -static inline void cn_implode_scratchpad(const __m128i* input, __m128i* output) -{ - __m128i xout0, xout1, xout2, xout3, xout4, xout5, xout6, xout7; - __m128i k0, k1, k2, k3, k4, k5, k6, k7, k8, k9; - - aes_genkey(output + 2, &k0, &k1, &k2, &k3, &k4, &k5, &k6, &k7, &k8, &k9); - - xout0 = _mm_load_si128(output + 4); - xout1 = _mm_load_si128(output + 5); - xout2 = _mm_load_si128(output + 6); - xout3 = _mm_load_si128(output + 7); - xout4 = _mm_load_si128(output + 8); - xout5 = _mm_load_si128(output + 9); - xout6 = _mm_load_si128(output + 10); - xout7 = _mm_load_si128(output + 11); - - for (size_t i = 0; i < MEM / sizeof(__m128i); i += 8) { - xout0 = _mm_xor_si128(_mm_load_si128(input + i + 0), xout0); - xout1 = _mm_xor_si128(_mm_load_si128(input + i + 1), xout1); - xout2 = _mm_xor_si128(_mm_load_si128(input + i + 2), xout2); - xout3 = _mm_xor_si128(_mm_load_si128(input + i + 3), xout3); - xout4 = _mm_xor_si128(_mm_load_si128(input + i + 4), xout4); - xout5 = _mm_xor_si128(_mm_load_si128(input + i + 5), xout5); - xout6 = _mm_xor_si128(_mm_load_si128(input + i + 6), xout6); - xout7 = _mm_xor_si128(_mm_load_si128(input + i + 7), xout7); - - aes_round(k0, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k1, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k2, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k3, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k4, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k5, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k6, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k7, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k8, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k9, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - } - - _mm_store_si128(output + 4, xout0); - _mm_store_si128(output + 5, xout1); - _mm_store_si128(output + 6, xout2); - _mm_store_si128(output + 7, xout3); - _mm_store_si128(output + 8, xout4); - _mm_store_si128(output + 9, xout5); - _mm_store_si128(output + 10, xout6); - _mm_store_si128(output + 11, xout7); -} - -template -static inline void cn_implode_scratchpad_heavy(const __m128i* input, __m128i* output) -{ - __m128i xout0, xout1, xout2, xout3, xout4, xout5, xout6, xout7; - __m128i k0, k1, k2, k3, k4, k5, k6, k7, k8, k9; - - aes_genkey(output + 2, &k0, &k1, &k2, &k3, &k4, &k5, &k6, &k7, &k8, &k9); - - xout0 = _mm_load_si128(output + 4); - xout1 = _mm_load_si128(output + 5); - xout2 = _mm_load_si128(output + 6); - xout3 = _mm_load_si128(output + 7); - xout4 = _mm_load_si128(output + 8); - xout5 = _mm_load_si128(output + 9); - xout6 = _mm_load_si128(output + 10); - xout7 = _mm_load_si128(output + 11); - - for (size_t i = 0; i < MEM / sizeof(__m128i); i += 8) { - xout0 = _mm_xor_si128(_mm_load_si128(input + i + 0), xout0); - xout1 = _mm_xor_si128(_mm_load_si128(input + i + 1), xout1); - xout2 = _mm_xor_si128(_mm_load_si128(input + i + 2), xout2); - xout3 = _mm_xor_si128(_mm_load_si128(input + i + 3), xout3); - xout4 = _mm_xor_si128(_mm_load_si128(input + i + 4), xout4); - xout5 = _mm_xor_si128(_mm_load_si128(input + i + 5), xout5); - xout6 = _mm_xor_si128(_mm_load_si128(input + i + 6), xout6); - xout7 = _mm_xor_si128(_mm_load_si128(input + i + 7), xout7); - - aes_round(k0, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k1, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k2, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k3, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k4, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k5, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k6, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k7, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k8, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k9, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - - mix_and_propagate(xout0, xout1, xout2, xout3, xout4, xout5, xout6, xout7); - } - - for (size_t i = 0; i < MEM / sizeof(__m128i); i += 8) { - xout0 = _mm_xor_si128(_mm_load_si128(input + i + 0), xout0); - xout1 = _mm_xor_si128(_mm_load_si128(input + i + 1), xout1); - xout2 = _mm_xor_si128(_mm_load_si128(input + i + 2), xout2); - xout3 = _mm_xor_si128(_mm_load_si128(input + i + 3), xout3); - xout4 = _mm_xor_si128(_mm_load_si128(input + i + 4), xout4); - xout5 = _mm_xor_si128(_mm_load_si128(input + i + 5), xout5); - xout6 = _mm_xor_si128(_mm_load_si128(input + i + 6), xout6); - xout7 = _mm_xor_si128(_mm_load_si128(input + i + 7), xout7); - - aes_round(k0, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k1, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k2, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k3, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k4, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k5, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k6, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k7, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k8, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k9, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - - mix_and_propagate(xout0, xout1, xout2, xout3, xout4, xout5, xout6, xout7); - } - - for (size_t i = 0; i < 16; i++) { - aes_round(k0, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k1, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k2, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k3, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k4, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k5, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k6, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k7, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k8, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - aes_round(k9, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - - mix_and_propagate(xout0, xout1, xout2, xout3, xout4, xout5, xout6, xout7); - } - - _mm_store_si128(output + 4, xout0); - _mm_store_si128(output + 5, xout1); - _mm_store_si128(output + 6, xout2); - _mm_store_si128(output + 7, xout3); - _mm_store_si128(output + 8, xout4); - _mm_store_si128(output + 9, xout5); - _mm_store_si128(output + 10, xout6); - _mm_store_si128(output + 11, xout7); -} - -static inline void int_sqrt_v2_fixup(uint64_t& r, uint64_t n0) -{ - if (LIKELY(r & 524287)) - { - r >>= 19; - return; - } - - --r; - const uint64_t s = r >> 20; - r >>= 19; - - uint64_t x2 = (s - (1022ULL << 32)) * (r - s - (1022ULL << 32) + 1); -#if (defined(_MSC_VER) || __GNUC__ > 7 || (__GNUC__ == 7 && __GNUC_MINOR__ > 1)) && (defined(__x86_64__) || defined(_M_AMD64)) - _addcarry_u64(_subborrow_u64(0, x2, n0, (unsigned long long int*)&x2), r, 0, (unsigned long long int*)&r); -#else - // GCC versions prior to 7 don't generate correct assembly for _subborrow_u64 -> _addcarry_u64 sequence - // Fallback to simpler code - if (x2 < n0) ++r; -#endif -} - -static inline uint64_t int_sqrt_v2(uint64_t n0) -{ -__m128d x = _mm_castsi128_pd(_mm_add_epi64(_mm_cvtsi64_si128(n0 >> 12), _mm_set_epi64x(0, 1023ULL << 52))); -x = _mm_sqrt_sd(_mm_setzero_pd(), x); -uint64_t r = static_cast(_mm_cvtsi128_si64(_mm_castpd_si128(x))); -int_sqrt_v2_fixup(r, n0); -return r; -} - -inline __m128 _mm_set1_ps_epi32(uint32_t x) -{ - return _mm_castsi128_ps(_mm_set1_epi32(x)); -} - -inline void cryptonight_conceal_tweak(__m128i& cx, __m128& conc_var) -{ - __m128 r = _mm_cvtepi32_ps(cx); - __m128 c_old = conc_var; - r = _mm_add_ps(r, conc_var); - r = _mm_mul_ps(r, _mm_mul_ps(r, r)); - r = _mm_and_ps(_mm_set1_ps_epi32(0x807FFFFF), r); - r = _mm_or_ps(_mm_set1_ps_epi32(0x40000000), r); - conc_var = _mm_add_ps(conc_var, r); - - c_old = _mm_and_ps(_mm_set1_ps_epi32(0x807FFFFF), c_old); - c_old = _mm_or_ps(_mm_set1_ps_epi32(0x40000000), c_old); - __m128 nc = _mm_mul_ps(c_old, _mm_set1_ps(536870880.0f)); - cx = _mm_xor_si128(cx, _mm_cvttps_epi32(nc)); -} - -template -class CryptoNightMultiHash -{ -public: - inline static void hash(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - // dummy - } - - inline static void hashPowV2(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - // dummy - } - - inline static void hashPowV2_asm(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad, - AsmOptimization asmOptimization) - { - // dummy - } - - - inline static void hashPowV3(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - // dummy - } - - inline static void hashPowV3_asm(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad, - AsmOptimization asmOptimization) - { - // dummy - } - - inline static void hashPowV4(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad, - uint64_t height) - { - // dummy - } - - inline static void hashPowV4_asm(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad, - uint64_t height, - AsmOptimization asmOptimization) - { - // dummy - } - - inline static void hashLiteTube(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - // dummy - } - - inline static void hashHeavy(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - // dummy - } - - inline static void hashHeavyTube(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - // dummy - } -}; - -template -class CryptoNightMultiHash -{ -public: - inline static void hash(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - const uint8_t* l; - uint64_t* h; - uint64_t al; - uint64_t ah; - uint64_t idx; - __m128i bx; - __m128 conc_var; - - if (VARIANT == POW_CONCEAL) { - SET_ROUNDING_MODE_NEAREST() - conc_var = _mm_setzero_ps(); - } - - keccak(static_cast(input), (int) size, scratchPad[0]->state, 200); - - l = scratchPad[0]->memory; - h = reinterpret_cast(scratchPad[0]->state); - - cn_explode_scratchpad((__m128i*) h, (__m128i*) l); - - al = h[0] ^ h[4]; - ah = h[1] ^ h[5]; - bx = _mm_set_epi64x(h[3] ^ h[7], h[2] ^ h[6]); - idx = h[0] ^ h[4]; - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx; - - if (SOFT_AES) { - if (VARIANT == POW_CONCEAL) { - cx = _mm_load_si128((__m128i*) &l[idx & MASK]); - cryptonight_conceal_tweak(cx, conc_var); - cx = soft_aesenc(cx, _mm_set_epi64x(ah, al)); - } else { - cx = soft_aesenc((uint32_t*)&l[idx & MASK], _mm_set_epi64x(ah, al)); - } - } else { - cx = _mm_load_si128((__m128i*) &l[idx & MASK]); - - if (VARIANT == POW_CONCEAL) - cryptonight_conceal_tweak(cx, conc_var); - - cx = _mm_aesenc_si128(cx, _mm_set_epi64x(ah, al)); - } - - _mm_store_si128((__m128i*) &l[idx & MASK], _mm_xor_si128(bx, cx)); - idx = EXTRACT64(cx); - bx = cx; - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l[idx & MASK])[0]; - ch = ((uint64_t*) &l[idx & MASK])[1]; - lo = __umul128(idx, cl, &hi); - - al += hi; - ah += lo; - - ((uint64_t*) &l[idx & MASK])[0] = al; - ((uint64_t*) &l[idx & MASK])[1] = ah; - - ah ^= ch; - al ^= cl; - idx = al; - } - - cn_implode_scratchpad((__m128i*) l, (__m128i*) h); - keccakf(h, 24); - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - } - - inline static void hashPowV2(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - const uint8_t* l; - uint64_t* h; - uint64_t al; - uint64_t ah; - __m128i bx; - uint64_t idx; - - keccak(static_cast(input), (int) size, scratchPad[0]->state, 200); - - uint64_t tweak1_2 = (*reinterpret_cast(reinterpret_cast(input) + 35) ^ - *(reinterpret_cast(scratchPad[0]->state) + 24)); - l = scratchPad[0]->memory; - h = reinterpret_cast(scratchPad[0]->state); - - cn_explode_scratchpad((__m128i*) h, (__m128i*) l); - - al = h[0] ^ h[4]; - ah = h[1] ^ h[5]; - bx = _mm_set_epi64x(h[3] ^ h[7], h[2] ^ h[6]); - idx = h[0] ^ h[4]; - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx; - - if (SOFT_AES) { - cx = soft_aesenc((uint32_t*)&l[idx & MASK], _mm_set_epi64x(ah, al)); - } else { - cx = _mm_load_si128((__m128i*) &l[idx & MASK]); - cx = _mm_aesenc_si128(cx, _mm_set_epi64x(ah, al)); - } - - _mm_store_si128((__m128i*) &l[idx & MASK], _mm_xor_si128(bx, cx)); - const uint8_t tmp = reinterpret_cast(&l[idx & MASK])[11]; - static const uint32_t table = 0x75310; - const uint8_t index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*)(&l[idx & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - - idx = EXTRACT64(cx); - bx = cx; - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l[idx & MASK])[0]; - ch = ((uint64_t*) &l[idx & MASK])[1]; - lo = __umul128(idx, cl, &hi); - - al += hi; - ah += lo; - - ah ^= tweak1_2; - ((uint64_t*) &l[idx & MASK])[0] = al; - ((uint64_t*) &l[idx & MASK])[1] = ah; - ah ^= tweak1_2; - - ah ^= ch; - al ^= cl; - idx = al; - } - - cn_implode_scratchpad((__m128i*) l, (__m128i*) h); - keccakf(h, 24); - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - } - - inline static void hashPowV2_asm(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad, - AsmOptimization asmOptimization) - { - keccak(static_cast(input), (int) size, scratchPad[0]->state, 200); - - const uint8_t*l = scratchPad[0]->memory; - uint64_t* h = reinterpret_cast(scratchPad[0]->state); - - cn_explode_scratchpad((__m128i*) h, (__m128i*) l); - -#ifndef XMRIG_NO_ASM - if (INDEX_SHIFT == POW_DEFAULT_INDEX_SHIFT) { - scratchPad[0]->variant_table = variant1_table; - } else { - scratchPad[0]->variant_table = variant_xtl_table; - } - - scratchPad[0]->input = input; - - if (SOFT_AES) { - scratchPad[0]->t_fn = (const uint32_t*)saes_table; - - switch (VARIANT) - { - case POW_MSR: - cnv1_main_loop_fast_soft_aes_sandybridge_asm(scratchPad[0]); - break; - case POW_UPX: - cnv1_main_loop_upx_soft_aes_sandybridge_asm(scratchPad[0]); - break; - case POW_RTO: - case POW_HOSP: - cnv1_main_loop_rto_soft_aes_sandybridge_asm(scratchPad[0]); - break; - default: - if (ITERATIONS == 0x40000) { - cnv1_main_loop_lite_soft_aes_sandybridge_asm(scratchPad[0]); - } else { - cnv1_main_loop_soft_aes_sandybridge_asm(scratchPad[0]); - } - break; - } - } else { - switch (VARIANT) - { - case POW_MSR: - cnv1_main_loop_fast_sandybridge_asm(scratchPad[0]); - break; - case POW_UPX: - cnv1_main_loop_upx_sandybridge_asm(scratchPad[0]); - break; - case POW_RTO: - case POW_HOSP: - cnv1_main_loop_rto_sandybridge_asm(scratchPad[0]); - break; - default: - if (ITERATIONS == 0x40000) { - cnv1_main_loop_lite_sandybridge_asm(scratchPad[0]); - } else { - cnv1_main_loop_sandybridge_asm(scratchPad[0]); - } - break; - } - } -#endif - - cn_implode_scratchpad((__m128i*) l, (__m128i*) h); - keccakf(h, 24); - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - } - - // single - inline static void hashPowV3(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - keccak(static_cast(input), (int) size, scratchPad[0]->state, 200); - - const uint8_t*l = scratchPad[0]->memory; - uint64_t* h = reinterpret_cast(scratchPad[0]->state); - - cn_explode_scratchpad((__m128i*) h, (__m128i*) l); - - uint64_t al = h[0] ^ h[4]; - uint64_t ah = h[1] ^ h[5]; - __m128i bx0 = _mm_set_epi64x(h[3] ^ h[7], h[2] ^ h[6]); - __m128i bx1 = _mm_set_epi64x(h[9] ^ h[11], h[8] ^ h[10]); - - uint64_t idx = h[0] ^ h[4]; - - __m128i division_result_xmm0 = _mm_cvtsi64_si128(h[12]); - uint64_t sqrt_result0 = h[13]; - - SET_ROUNDING_MODE_UP(); - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx; - - const __m128i ax = _mm_set_epi64x(ah, al); - - if (SOFT_AES) { - cx = soft_aesenc((uint32_t*)&l[idx & MASK], ax); - } else { - cx = _mm_load_si128((__m128i*) &l[idx & MASK]); - cx = _mm_aesenc_si128(cx, ax); - } - - SHUFFLE_PHASE_1(l, (idx&MASK), bx0, bx1, ax, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - - _mm_store_si128((__m128i*) &l[idx & MASK], _mm_xor_si128(bx0, cx)); - - idx = EXTRACT64(cx); - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l[idx & MASK])[0]; - ch = ((uint64_t*) &l[idx & MASK])[1]; - - INTEGER_MATH_V2(0, cl, cx) - - lo = __umul128(idx, cl, &hi); - - SHUFFLE_PHASE_2(l, (idx&MASK), bx0, bx1, ax, lo, hi, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - - al += hi; // two fence statements are overhead - ah += lo; - - ((uint64_t*) &l[idx & MASK])[0] = al; - ((uint64_t*) &l[idx & MASK])[1] = ah; - - ah ^= ch; - al ^= cl; - idx = al; - - bx1 = bx0; - bx0 = cx; - } - - cn_implode_scratchpad((__m128i*) l, (__m128i*) h); - keccakf(h, 24); - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - } - - // single asm - inline static void hashPowV3_asm(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad, - AsmOptimization asmOptimization) - { - const uint8_t* l = scratchPad[0]->memory; - uint64_t* h = reinterpret_cast(scratchPad[0]->state); - - keccak(static_cast(input), (int) size, scratchPad[0]->state, 200); - cn_explode_scratchpad((__m128i*) h, (__m128i*) l); - -#ifndef XMRIG_NO_ASM - if (asmOptimization == AsmOptimization::ASM_INTEL) { - if (SOFT_AES) { - scratchPad[0]->input = input; - scratchPad[0]->t_fn = (const uint32_t*)saes_table; - - switch (VARIANT) - { - case POW_FAST_2: - cnv2_main_loop_fastv2_soft_aes_sandybridge_asm(scratchPad[0]); - break; - case POW_TURTLE: - cnv2_main_loop_ultralite_soft_aes_sandybridge_asm(scratchPad[0]); - break; - case POW_DOUBLE: - cnv2_main_loop_xcash_soft_aes_sandybridge_asm(scratchPad[0]); - break; - case POW_ZELERIUS: - cnv2_main_loop_zelerius_soft_aes_sandybridge_asm(scratchPad[0]); - break; - default: - cnv2_main_loop_soft_aes_sandybridge_asm(scratchPad[0]); - break; - } - } else { - switch (VARIANT) - { - case POW_FAST_2: - cnv2_main_loop_fastv2_ivybridge_asm(scratchPad[0]); - break; - case POW_TURTLE: - cnv2_main_loop_ultralite_ivybridge_asm(scratchPad[0]); - break; - case POW_DOUBLE: - cnv2_main_loop_xcash_ivybridge_asm(scratchPad[0]); - break; - case POW_ZELERIUS: - cnv2_main_loop_zelerius_ivybridge_asm(scratchPad[0]); - break; - case POW_RWZ: - cnv2_main_loop_rwz_original_all_asm(scratchPad[0]); - break; - case POW_UPX2: - cnv2_main_loop_rwz_upx2_all_asm(scratchPad[0]); - break; - default: - cnv2_main_loop_ivybridge_asm(scratchPad[0]); - break; - } - } - } else if (asmOptimization == AsmOptimization::ASM_RYZEN) { - switch (VARIANT) - { - case POW_FAST_2: - cnv2_main_loop_fastv2_ryzen_asm(scratchPad[0]); - break; - case POW_TURTLE: - cnv2_main_loop_ultralite_ryzen_asm(scratchPad[0]); - break; - case POW_DOUBLE: - cnv2_main_loop_xcash_ryzen_asm(scratchPad[0]); - break; - case POW_ZELERIUS: - cnv2_main_loop_zelerius_ryzen_asm(scratchPad[0]); - break; - case POW_RWZ: - cnv2_main_loop_rwz_original_all_asm(scratchPad[0]); - break; - case POW_UPX2: - cnv2_main_loop_rwz_upx2_all_asm(scratchPad[0]); - break; - default: - cnv2_main_loop_ryzen_asm(scratchPad[0]); - break; - } - } else if (asmOptimization == AsmOptimization::ASM_BULLDOZER) { - switch (VARIANT) - { - case POW_FAST_2: - cnv2_main_loop_fastv2_bulldozer_asm(scratchPad[0]); - break; - case POW_TURTLE: - cnv2_main_loop_ultralite_bulldozer_asm(scratchPad[0]); - break; - case POW_DOUBLE: - cnv2_main_loop_xcash_bulldozer_asm(scratchPad[0]); - break; - case POW_ZELERIUS: - cnv2_main_loop_zelerius_bulldozer_asm(scratchPad[0]); - break; - case POW_RWZ: - cnv2_main_loop_rwz_original_all_asm(scratchPad[0]); - break; - case POW_UPX2: - cnv2_main_loop_rwz_upx2_all_asm(scratchPad[0]); - break; - default: - cnv2_main_loop_bulldozer_asm(scratchPad[0]); - break; - } - } -#endif - - cn_implode_scratchpad((__m128i*) l, (__m128i*) h); - keccakf(h, 24); - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - } - - // single - inline static void hashPowV4(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad, - uint64_t height) - { - keccak(static_cast(input), (int) size, scratchPad[0]->state, 200); - - const uint8_t*l = scratchPad[0]->memory; - uint64_t* h = reinterpret_cast(scratchPad[0]->state); - - cn_explode_scratchpad((__m128i*) h, (__m128i*) l); - - uint64_t al = h[0] ^ h[4]; - uint64_t ah = h[1] ^ h[5]; - - __m128i bx0 = _mm_set_epi64x(h[3] ^ h[7], h[2] ^ h[6]); - __m128i bx1 = _mm_set_epi64x(h[9] ^ h[11], h[8] ^ h[10]); - - uint64_t idx = h[0] ^ h[4]; - - VARIANT4_RANDOM_MATH_INIT(0, h) - - SET_ROUNDING_MODE_UP(); - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx; - - const __m128i ax = _mm_set_epi64x(ah, al); - - if (SOFT_AES) { - cx = soft_aesenc((uint32_t*)&l[idx & MASK], ax); - } else { - cx = _mm_load_si128((__m128i*) &l[idx & MASK]); - cx = _mm_aesenc_si128(cx, ax); - } - - SHUFFLE_V4(l, (idx&MASK), bx0, bx1, ax, cx) - - _mm_store_si128((__m128i*) &l[idx & MASK], _mm_xor_si128(bx0, cx)); - - idx = EXTRACT64(cx); - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l[idx & MASK])[0]; - ch = ((uint64_t*) &l[idx & MASK])[1]; - - VARIANT4_RANDOM_MATH(0, al, ah, cl, bx0, bx1) - - if (VARIANT == POW_V4) { - al ^= r0[2] | ((uint64_t)(r0[3]) << 32); - ah ^= r0[0] | ((uint64_t)(r0[1]) << 32); - } - - lo = __umul128(idx, cl, &hi); - - SHUFFLE_V4(l, (idx&MASK), bx0, bx1, ax, cx) - - al += hi; // two fence statements are overhead - ah += lo; - - ((uint64_t*) &l[idx & MASK])[0] = al; - ((uint64_t*) &l[idx & MASK])[1] = ah; - - ah ^= ch; - al ^= cl; - idx = al; - - bx1 = bx0; - bx0 = cx; - } - - cn_implode_scratchpad((__m128i*) l, (__m128i*) h); - keccakf(h, 24); - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - } - - // single asm - inline static void hashPowV4_asm(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad, - uint64_t height, - AsmOptimization asmOptimization) - { - const uint8_t* l = scratchPad[0]->memory; - uint64_t* h = reinterpret_cast(scratchPad[0]->state); - - keccak(static_cast(input), (int) size, scratchPad[0]->state, 200); - cn_explode_scratchpad((__m128i*) h, (__m128i*) l); - -#ifndef XMRIG_NO_ASM - if (SOFT_AES) { - if (!scratchPad[0]->generated_code_data.match(VARIANT, height)) { - V4_Instruction code[256]; - const int code_size = v4_random_math_init(code, VARIANT, height); - - if (VARIANT == POW_WOW) { - wow_soft_aes_compile_code(code, code_size, reinterpret_cast(scratchPad[0]->generated_code), ASM_OFF); - } else { - v4_soft_aes_compile_code(code, code_size, reinterpret_cast(scratchPad[0]->generated_code), ASM_OFF); - } - - scratchPad[0]->generated_code_data.variant = VARIANT; - scratchPad[0]->generated_code_data.height = height; - } - - scratchPad[0]->input = input; - scratchPad[0]->t_fn = (const uint32_t*)saes_table; - scratchPad[0]->generated_code(scratchPad[0]); - } else { - if (!scratchPad[0]->generated_code_data.match(VARIANT, height)) { - V4_Instruction code[256]; - const int code_size = v4_random_math_init(code, VARIANT, height); - - if (VARIANT == POW_WOW) { - wow_compile_code(code, code_size, reinterpret_cast(scratchPad[0]->generated_code), asmOptimization); - } else { - v4_compile_code(code, code_size, reinterpret_cast(scratchPad[0]->generated_code), asmOptimization); - } - - scratchPad[0]->generated_code_data.variant = VARIANT; - scratchPad[0]->generated_code_data.height = height; - } - - scratchPad[0]->generated_code(scratchPad[0]); - } -#endif - - cn_implode_scratchpad((__m128i*) l, (__m128i*) h); - keccakf(h, 24); - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - } - - inline static void hashLiteTube(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - const uint8_t* l; - uint64_t* h; - uint64_t al; - uint64_t ah; - __m128i bx; - uint64_t idx; - - keccak(static_cast(input), (int) size, scratchPad[0]->state, 200); - - uint64_t tweak1_2 = (*reinterpret_cast(reinterpret_cast(input) + 35) ^ - *(reinterpret_cast(scratchPad[0]->state) + 24)); - l = scratchPad[0]->memory; - h = reinterpret_cast(scratchPad[0]->state); - - cn_explode_scratchpad((__m128i*) h, (__m128i*) l); - - al = h[0] ^ h[4]; - ah = h[1] ^ h[5]; - bx = _mm_set_epi64x(h[3] ^ h[7], h[2] ^ h[6]); - idx = h[0] ^ h[4]; - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx; - - if (SOFT_AES) { - cx = soft_aesenc((uint32_t*)&l[idx & MASK], _mm_set_epi64x(ah, al)); - } else { - cx = _mm_load_si128((__m128i*) &l[idx & MASK]); - cx = _mm_aesenc_si128(cx, _mm_set_epi64x(ah, al)); - } - - _mm_store_si128((__m128i*) &l[idx & MASK], _mm_xor_si128(bx, cx)); - const uint8_t tmp = reinterpret_cast(&l[idx & MASK])[11]; - static const uint32_t table = 0x75310; - const uint8_t index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*)(&l[idx & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - idx = EXTRACT64(cx); - bx = cx; - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l[idx & MASK])[0]; - ch = ((uint64_t*) &l[idx & MASK])[1]; - lo = __umul128(idx, cl, &hi); - - al += hi; - ah += lo; - - ah ^= tweak1_2; - ((uint64_t*) &l[idx & MASK])[0] = al; - ((uint64_t*) &l[idx & MASK])[1] = ah; - ah ^= tweak1_2; - - ((uint64_t*)&l[idx & MASK])[1] ^= ((uint64_t*)&l[idx & MASK])[0]; - - ah ^= ch; - al ^= cl; - idx = al; - } - - cn_implode_scratchpad((__m128i*) l, (__m128i*) h); - keccakf(h, 24); - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - } - - inline static void hashHeavy(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - const uint8_t* l; - uint64_t* h; - uint64_t al; - uint64_t ah; - __m128i bx; - uint64_t idx; - - keccak(static_cast(input), (int) size, scratchPad[0]->state, 200); - - l = scratchPad[0]->memory; - h = reinterpret_cast(scratchPad[0]->state); - - cn_explode_scratchpad_heavy((__m128i*) h, (__m128i*) l); - - al = h[0] ^ h[4]; - ah = h[1] ^ h[5]; - bx = _mm_set_epi64x(h[3] ^ h[7], h[2] ^ h[6]); - idx = h[0] ^ h[4]; - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx; - - if (SOFT_AES) { - cx = soft_aesenc((uint32_t*)&l[idx & MASK], _mm_set_epi64x(ah, al)); - } else { - cx = _mm_load_si128((__m128i*) &l[idx & MASK]); - cx = _mm_aesenc_si128(cx, _mm_set_epi64x(ah, al)); - } - - _mm_store_si128((__m128i*) &l[idx & MASK], _mm_xor_si128(bx, cx)); - idx = EXTRACT64(cx); - bx = cx; - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l[idx & MASK])[0]; - ch = ((uint64_t*) &l[idx & MASK])[1]; - lo = __umul128(idx, cl, &hi); - - al += hi; - ah += lo; - - ((uint64_t*) &l[idx & MASK])[0] = al; - ((uint64_t*) &l[idx & MASK])[1] = ah; - - ah ^= ch; - al ^= cl; - idx = al; - - int64_t n = ((int64_t*)&l[idx & MASK])[0]; - int32_t d = ((int32_t*)&l[idx & MASK])[2]; - int64_t q = n / (d | 0x5); - - ((int64_t*)&l[idx & MASK])[0] = n ^ q; - - if (VARIANT == POW_XHV || VARIANT == POW_XFH) { - idx = (~d) ^ q; - } else { - idx = d ^ q; - } - } - - cn_implode_scratchpad_heavy((__m128i*) l, (__m128i*) h); - keccakf(h, 24); - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - } - - inline static void hashHeavyTube(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - const uint8_t* l; - uint64_t* h; - uint64_t al; - uint64_t ah; - __m128i bx; - uint64_t idx; - - keccak(static_cast(input), (int) size, scratchPad[0]->state, 200); - - uint64_t tweak1_2 = (*reinterpret_cast(reinterpret_cast(input) + 35) ^ - *(reinterpret_cast(scratchPad[0]->state) + 24)); - - l = scratchPad[0]->memory; - h = reinterpret_cast(scratchPad[0]->state); - - cn_explode_scratchpad_heavy((__m128i*) h, (__m128i*) l); - - al = h[0] ^ h[4]; - ah = h[1] ^ h[5]; - bx = _mm_set_epi64x(h[3] ^ h[7], h[2] ^ h[6]); - idx = h[0] ^ h[4]; - - union alignas(16) { - uint32_t k[4]; - uint64_t v64[2]; - }; - alignas(16) uint32_t x[4]; - -#define BYTE(p, i) ((unsigned char*)&p)[i] - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx = _mm_load_si128((__m128i*) &l[idx & MASK]); - - const __m128i& key = _mm_set_epi64x(ah, al); - - _mm_store_si128((__m128i*)k, key); - cx = _mm_xor_si128(cx, _mm_cmpeq_epi32(_mm_setzero_si128(), _mm_setzero_si128())); - _mm_store_si128((__m128i*)x, cx); - - k[0] ^= saes_table[0][BYTE(x[0], 0)] ^ saes_table[1][BYTE(x[1], 1)] ^ saes_table[2][BYTE(x[2], 2)] ^ saes_table[3][BYTE(x[3], 3)]; - x[0] ^= k[0]; - k[1] ^= saes_table[0][BYTE(x[1], 0)] ^ saes_table[1][BYTE(x[2], 1)] ^ saes_table[2][BYTE(x[3], 2)] ^ saes_table[3][BYTE(x[0], 3)]; - x[1] ^= k[1]; - k[2] ^= saes_table[0][BYTE(x[2], 0)] ^ saes_table[1][BYTE(x[3], 1)] ^ saes_table[2][BYTE(x[0], 2)] ^ saes_table[3][BYTE(x[1], 3)]; - x[2] ^= k[2]; - k[3] ^= saes_table[0][BYTE(x[3], 0)] ^ saes_table[1][BYTE(x[0], 1)] ^ saes_table[2][BYTE(x[1], 2)] ^ saes_table[3][BYTE(x[2], 3)]; - - cx = _mm_load_si128((__m128i*)k); - - _mm_store_si128((__m128i*) &l[idx & MASK], _mm_xor_si128(bx, cx)); - const uint8_t tmp = reinterpret_cast(&l[idx & MASK])[11]; - static const uint32_t table = 0x75310; - const uint8_t index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*)(&l[idx & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - - idx = EXTRACT64(cx); - bx = cx; - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l[idx & MASK])[0]; - ch = ((uint64_t*) &l[idx & MASK])[1]; - lo = __umul128(idx, cl, &hi); - - al += hi; - ah += lo; - - ah ^= tweak1_2; - ((uint64_t*) &l[idx & MASK])[0] = al; - ((uint64_t*) &l[idx & MASK])[1] = ah; - ah ^= tweak1_2; - - ((uint64_t*)&l[idx & MASK])[1] ^= ((uint64_t*)&l[idx & MASK])[0]; - - ah ^= ch; - al ^= cl; - idx = al; - - int64_t n = ((int64_t*)&l[idx & MASK])[0]; - int32_t d = ((int32_t*)&l[idx & MASK])[2]; - int64_t q = n / (d | 0x5); - - ((int64_t*)&l[idx & MASK])[0] = n ^ q; - idx = d ^ q; - } -#undef BYTE - - cn_implode_scratchpad_heavy((__m128i*) l, (__m128i*) h); - keccakf(h, 24); - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - } -}; - -template -class CryptoNightMultiHash -{ -public: - inline static void hash(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - keccak((const uint8_t*) input, (int) size, scratchPad[0]->state, 200); - keccak((const uint8_t*) input + size, (int) size, scratchPad[1]->state, 200); - - const uint8_t* l0 = scratchPad[0]->memory; - const uint8_t* l1 = scratchPad[1]->memory; - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - uint64_t* h1 = reinterpret_cast(scratchPad[1]->state); - - cn_explode_scratchpad((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad((__m128i*) h1, (__m128i*) l1); - - uint64_t al0 = h0[0] ^h0[4]; - uint64_t al1 = h1[0] ^h1[4]; - uint64_t ah0 = h0[1] ^h0[5]; - uint64_t ah1 = h1[1] ^h1[5]; - - __m128i bx0 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx1 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - - __m128 conc_var0; - __m128 conc_var1; - - if (VARIANT == POW_CONCEAL) { - SET_ROUNDING_MODE_NEAREST() - conc_var0 = _mm_setzero_ps(); - conc_var1 = _mm_setzero_ps(); - } - - uint64_t idx0 = h0[0] ^h0[4]; - uint64_t idx1 = h1[0] ^h1[4]; - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx0; - __m128i cx1; - - if (SOFT_AES) { - if (VARIANT == POW_CONCEAL) { - cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - cx1 = _mm_load_si128((__m128i*) &l1[idx1 & MASK]); - - cryptonight_conceal_tweak(cx0, conc_var0); - cryptonight_conceal_tweak(cx1, conc_var1); - - cx0 = soft_aesenc(cx0, _mm_set_epi64x(ah0, al0)); - cx1 = soft_aesenc(cx1, _mm_set_epi64x(ah1, al1)); - } else { - cx0 = soft_aesenc((uint32_t *) &l0[idx0 & MASK], _mm_set_epi64x(ah0, al0)); - cx1 = soft_aesenc((uint32_t *) &l1[idx1 & MASK], _mm_set_epi64x(ah1, al1)); - } - } else { - cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - cx1 = _mm_load_si128((__m128i*) &l1[idx1 & MASK]); - - if (VARIANT == POW_CONCEAL) { - cryptonight_conceal_tweak(cx0, conc_var0); - cryptonight_conceal_tweak(cx1, conc_var1); - } - - cx0 = _mm_aesenc_si128(cx0, _mm_set_epi64x(ah0, al0)); - cx1 = _mm_aesenc_si128(cx1, _mm_set_epi64x(ah1, al1)); - } - - _mm_store_si128((__m128i*) &l0[idx0 & MASK], _mm_xor_si128(bx0, cx0)); - _mm_store_si128((__m128i*) &l1[idx1 & MASK], _mm_xor_si128(bx1, cx1)); - - idx0 = EXTRACT64(cx0); - idx1 = EXTRACT64(cx1); - - bx0 = cx0; - bx1 = cx1; - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & MASK])[0]; - ch = ((uint64_t*) &l0[idx0 & MASK])[1]; - lo = __umul128(idx0, cl, &hi); - - al0 += hi; - ah0 += lo; - - ((uint64_t*) &l0[idx0 & MASK])[0] = al0; - ((uint64_t*) &l0[idx0 & MASK])[1] = ah0; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - cl = ((uint64_t*) &l1[idx1 & MASK])[0]; - ch = ((uint64_t*) &l1[idx1 & MASK])[1]; - lo = __umul128(idx1, cl, &hi); - - al1 += hi; - ah1 += lo; - - ((uint64_t*) &l1[idx1 & MASK])[0] = al1; - ((uint64_t*) &l1[idx1 & MASK])[1] = ah1; - - ah1 ^= ch; - al1 ^= cl; - idx1 = al1; - } - - cn_implode_scratchpad((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad((__m128i*) l1, (__m128i*) h1); - - keccakf(h0, 24); - keccakf(h1, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - extra_hashes[scratchPad[1]->state[0] & 3](scratchPad[1]->state, 200, output + 32); - } - - inline static void hashPowV2(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - keccak((const uint8_t*) input, (int) size, scratchPad[0]->state, 200); - keccak((const uint8_t*) input + size, (int) size, scratchPad[1]->state, 200); - - uint64_t tweak1_2_0 = (*reinterpret_cast(reinterpret_cast(input) + 35) ^ - *(reinterpret_cast(scratchPad[0]->state) + 24)); - uint64_t tweak1_2_1 = (*reinterpret_cast(reinterpret_cast(input) + 35 + size) ^ - *(reinterpret_cast(scratchPad[1]->state) + 24)); - - const uint8_t* l0 = scratchPad[0]->memory; - const uint8_t* l1 = scratchPad[1]->memory; - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - uint64_t* h1 = reinterpret_cast(scratchPad[1]->state); - - cn_explode_scratchpad((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad((__m128i*) h1, (__m128i*) l1); - - uint64_t al0 = h0[0] ^h0[4]; - uint64_t al1 = h1[0] ^h1[4]; - uint64_t ah0 = h0[1] ^h0[5]; - uint64_t ah1 = h1[1] ^h1[5]; - - __m128i bx0 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx1 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - - uint64_t idx0 = h0[0] ^h0[4]; - uint64_t idx1 = h1[0] ^h1[4]; - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx0; - __m128i cx1; - - if (SOFT_AES) { - cx0 = soft_aesenc((uint32_t*)&l0[idx0 & MASK], _mm_set_epi64x(ah0, al0)); - cx1 = soft_aesenc((uint32_t*)&l1[idx1 & MASK], _mm_set_epi64x(ah1, al1)); - } else { - cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - cx1 = _mm_load_si128((__m128i*) &l1[idx1 & MASK]); - - cx0 = _mm_aesenc_si128(cx0, _mm_set_epi64x(ah0, al0)); - cx1 = _mm_aesenc_si128(cx1, _mm_set_epi64x(ah1, al1)); - } - - _mm_store_si128((__m128i*) &l0[idx0 & MASK], _mm_xor_si128(bx0, cx0)); - _mm_store_si128((__m128i*) &l1[idx1 & MASK], _mm_xor_si128(bx1, cx1)); - - static const uint32_t table = 0x75310; - uint8_t tmp = reinterpret_cast(&l0[idx0 & MASK])[11]; - uint8_t index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*)(&l0[idx0 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - tmp = reinterpret_cast(&l1[idx1 & MASK])[11]; - index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*)(&l1[idx1 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - - idx0 = EXTRACT64(cx0); - idx1 = EXTRACT64(cx1); - - bx0 = cx0; - bx1 = cx1; - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & MASK])[0]; - ch = ((uint64_t*) &l0[idx0 & MASK])[1]; - lo = __umul128(idx0, cl, &hi); - - al0 += hi; - ah0 += lo; - - ah0 ^= tweak1_2_0; - ((uint64_t*) &l0[idx0 & MASK])[0] = al0; - ((uint64_t*) &l0[idx0 & MASK])[1] = ah0; - ah0 ^= tweak1_2_0; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - cl = ((uint64_t*) &l1[idx1 & MASK])[0]; - ch = ((uint64_t*) &l1[idx1 & MASK])[1]; - lo = __umul128(idx1, cl, &hi); - - al1 += hi; - ah1 += lo; - - ah1 ^= tweak1_2_1; - ((uint64_t*) &l1[idx1 & MASK])[0] = al1; - ((uint64_t*) &l1[idx1 & MASK])[1] = ah1; - ah1 ^= tweak1_2_1; - - ah1 ^= ch; - al1 ^= cl; - idx1 = al1; - } - - cn_implode_scratchpad((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad((__m128i*) l1, (__m128i*) h1); - - keccakf(h0, 24); - keccakf(h1, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - extra_hashes[scratchPad[1]->state[0] & 3](scratchPad[1]->state, 200, output + 32); - } - - inline static void hashPowV2_asm(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad, - AsmOptimization asmOptimization) - { - // not supported - } - - // double - inline static void hashPowV3(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - keccak((const uint8_t*) input, (int) size, scratchPad[0]->state, 200); - keccak((const uint8_t*) input + size, (int) size, scratchPad[1]->state, 200); - - const uint8_t* l0 = scratchPad[0]->memory; - const uint8_t* l1 = scratchPad[1]->memory; - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - uint64_t* h1 = reinterpret_cast(scratchPad[1]->state); - - cn_explode_scratchpad((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad((__m128i*) h1, (__m128i*) l1); - - uint64_t al0 = h0[0] ^h0[4]; - uint64_t al1 = h1[0] ^h1[4]; - uint64_t ah0 = h0[1] ^h0[5]; - uint64_t ah1 = h1[1] ^h1[5]; - - __m128i bx00 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx10 = _mm_set_epi64x(h0[9] ^ h0[11], h0[8] ^ h0[10]); - - __m128i bx01 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - __m128i bx11 = _mm_set_epi64x(h1[9] ^ h1[11], h1[8] ^ h1[10]); - - uint64_t idx0 = h0[0] ^h0[4]; - uint64_t idx1 = h1[0] ^h1[4]; - -#if defined(__x86_64__) || defined(_M_AMD64) - __m128i division_result_xmm = _mm_unpacklo_epi64(_mm_cvtsi64_si128(h0[12]), _mm_cvtsi64_si128(h1[12])); - __m128i sqrt_result_xmm = _mm_unpacklo_epi64(_mm_cvtsi64_si128(h0[13]), _mm_cvtsi64_si128(h1[13])); -#else - __m128i division_result_xmm0 = _mm_cvtsi64_si128(h0[12]); - __m128i division_result_xmm1 = _mm_cvtsi64_si128(h1[12]); - uint64_t sqrt_result0 = h0[13]; - uint64_t sqrt_result1 = h1[13]; -#endif - - SET_ROUNDING_MODE_UP() - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx0; - __m128i cx1; - - const __m128i ax0 = _mm_set_epi64x(ah0, al0); - const __m128i ax1 = _mm_set_epi64x(ah1, al1); - - if (SOFT_AES) { - cx0 = soft_aesenc((uint32_t*)&l0[idx0 & MASK], ax0); - cx1 = soft_aesenc((uint32_t*)&l1[idx1 & MASK], ax1); - } else { - cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - cx1 = _mm_load_si128((__m128i*) &l1[idx1 & MASK]); - - cx0 = _mm_aesenc_si128(cx0, ax0); - cx1 = _mm_aesenc_si128(cx1, ax1); - } - - SHUFFLE_PHASE_1(l0, (idx0 & MASK), bx00, bx10, ax0, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - SHUFFLE_PHASE_1(l1, (idx1 & MASK), bx01, bx11, ax1, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - - _mm_store_si128((__m128i*) &l0[idx0 & MASK], _mm_xor_si128(bx00, cx0)); - _mm_store_si128((__m128i*) &l1[idx1 & MASK], _mm_xor_si128(bx01, cx1)); - - idx0 = EXTRACT64(cx0); - idx1 = EXTRACT64(cx1); - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & MASK])[0]; - ch = ((uint64_t*) &l0[idx0 & MASK])[1]; - -#if defined(__x86_64__) || defined(_M_AMD64) - const uint64_t sqrt_result0 = _mm_cvtsi128_si64(sqrt_result_xmm); - cl ^= static_cast(_mm_cvtsi128_si64(division_result_xmm)) ^ (sqrt_result0 << 32); -#else - INTEGER_MATH_V2(0, cl, cx0) -#endif - - lo = __umul128(idx0, cl, &hi); - - SHUFFLE_PHASE_2(l0, (idx0 & MASK), bx00, bx10, ax0, lo, hi, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - - al0 += hi; - ah0 += lo; - - ((uint64_t*) &l0[idx0 & MASK])[0] = al0; - ((uint64_t*) &l0[idx0 & MASK])[1] = ah0; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - bx10 = bx00; - bx00 = cx0; - - - cl = ((uint64_t*) &l1[idx1 & MASK])[0]; - ch = ((uint64_t*) &l1[idx1 & MASK])[1]; - -#if defined(__x86_64__) || defined(_M_AMD64) - const uint64_t sqrt_result1 = _mm_cvtsi128_si64(_mm_srli_si128(sqrt_result_xmm, 8)); - cl ^= static_cast(_mm_cvtsi128_si64(_mm_srli_si128(division_result_xmm, 8))) ^ (sqrt_result1 << 32); - - const __m128i sqrt_result2 = _mm_add_epi64(_mm_slli_epi64(sqrt_result_xmm, 1), _mm_unpacklo_epi64(cx0, cx1)); - const uint32_t d0 = _mm_cvtsi128_si64(sqrt_result2) | 0x80000001UL; - const uint32_t d1 = _mm_cvtsi128_si64(_mm_srli_si128(sqrt_result2, 8)) | 0x80000001UL; - - const uint64_t cx01 = _mm_cvtsi128_si64(_mm_srli_si128(cx0, 8)); - const uint64_t cx11 = _mm_cvtsi128_si64(_mm_srli_si128(cx1, 8)); - __m128d x = _mm_unpacklo_pd(_mm_cvtsi64_sd(_mm_setzero_pd(), (cx01 + 1) >> 1), _mm_cvtsi64_sd(_mm_setzero_pd(), (cx11 + 1) >> 1)); - __m128d y = _mm_unpacklo_pd(_mm_cvtsi64_sd(_mm_setzero_pd(), d0), _mm_cvtsi64_sd(_mm_setzero_pd(), d1)); - - __m128d result = _mm_div_pd(x, y); - result = _mm_castsi128_pd(_mm_add_epi64(_mm_castpd_si128(result), _mm_set_epi64x(1ULL << 52, 1ULL << 52))); - - uint64_t q0 = _mm_cvttsd_si64(result); - uint64_t q1 = _mm_cvttsd_si64(_mm_castsi128_pd(_mm_srli_si128(_mm_castpd_si128(result), 8))); - - uint64_t r0 = cx01 - d0 * q0; - if (UNLIKELY(int64_t(r0) < 0)) - { - --q0; - r0 += d0; - } - uint64_t r1 = cx11 - d1 * q1; - if (UNLIKELY(int64_t(r1) < 0)) - { - --q1; - r1 += d1; - } - - division_result_xmm = _mm_set_epi32(r1, q1, r0, q0); - - __m128i sqrt_input = _mm_add_epi64(_mm_unpacklo_epi64(cx0, cx1), division_result_xmm); - x = _mm_castsi128_pd(_mm_add_epi64(_mm_srli_epi64(sqrt_input, 12), _mm_set_epi64x(1023ULL << 52, 1023ULL << 52))); - - x = _mm_sqrt_pd(x); - - r0 = static_cast(_mm_cvtsi128_si64(_mm_castpd_si128(x))); - int_sqrt_v2_fixup(r0, _mm_cvtsi128_si64(sqrt_input)); - r1 = static_cast(_mm_cvtsi128_si64(_mm_srli_si128(_mm_castpd_si128(x), 8))); - int_sqrt_v2_fixup(r1, _mm_cvtsi128_si64(_mm_srli_si128(sqrt_input, 8))); - sqrt_result_xmm = _mm_set_epi64x(r1, r0); -#else - INTEGER_MATH_V2(1, cl, cx1) -#endif - - lo = __umul128(idx1, cl, &hi); - - SHUFFLE_PHASE_2(l1, (idx1 & MASK), bx01, bx11, ax1, lo, hi, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - - al1 += hi; - ah1 += lo; - - ((uint64_t*) &l1[idx1 & MASK])[0] = al1; - ((uint64_t*) &l1[idx1 & MASK])[1] = ah1; - - ah1 ^= ch; - al1 ^= cl; - idx1 = al1; - - bx11 = bx01; - bx01 = cx1; - } - - cn_implode_scratchpad((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad((__m128i*) l1, (__m128i*) h1); - - keccakf(h0, 24); - keccakf(h1, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - extra_hashes[scratchPad[1]->state[0] & 3](scratchPad[1]->state, 200, output + 32); - } - - // double asm - inline static void hashPowV3_asm(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad, - AsmOptimization asmOptimization) - { - keccak((const uint8_t*) input, (int) size, scratchPad[0]->state, 200); - keccak((const uint8_t*) input + size, (int) size, scratchPad[1]->state, 200); - - const uint8_t* l0 = scratchPad[0]->memory; - const uint8_t* l1 = scratchPad[1]->memory; - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - uint64_t* h1 = reinterpret_cast(scratchPad[1]->state); - - cn_explode_scratchpad((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad((__m128i*) h1, (__m128i*) l1); - -#ifndef XMRIG_NO_ASM - switch(VARIANT) { - case POW_FAST_2: - cnv2_double_main_loop_fastv2_sandybridge_asm(scratchPad[0], scratchPad[1]); - break; - case POW_TURTLE: - cnv2_double_main_loop_ultralite_sandybridge_asm(scratchPad[0], scratchPad[1]); - break; - case POW_DOUBLE: - cnv2_double_main_loop_xcash_sandybridge_asm(scratchPad[0], scratchPad[1]); - break; - case POW_ZELERIUS: - cnv2_double_main_loop_zelerius_sandybridge_asm(scratchPad[0], scratchPad[1]); - break; - case POW_RWZ: - cnv2_double_main_loop_rwz_original_all_asm(scratchPad[0], scratchPad[1]); - break; - case POW_UPX2: - cnv2_double_main_loop_rwz_upx2_all_asm(scratchPad[0], scratchPad[1]); - break; - default: - cnv2_double_main_loop_sandybridge_asm(scratchPad[0], scratchPad[1]); - break; - } -#endif - cn_implode_scratchpad((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad((__m128i*) l1, (__m128i*) h1); - - keccakf(h0, 24); - keccakf(h1, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - extra_hashes[scratchPad[1]->state[0] & 3](scratchPad[1]->state, 200, output + 32); - } - - // double - inline static void hashPowV4(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad, - uint64_t height) - { - keccak((const uint8_t*) input, (int) size, scratchPad[0]->state, 200); - keccak((const uint8_t*) input + size, (int) size, scratchPad[1]->state, 200); - - const uint8_t* l0 = scratchPad[0]->memory; - const uint8_t* l1 = scratchPad[1]->memory; - - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - uint64_t* h1 = reinterpret_cast(scratchPad[1]->state); - - cn_explode_scratchpad((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad((__m128i*) h1, (__m128i*) l1); - - uint64_t al0 = h0[0] ^h0[4]; - uint64_t al1 = h1[0] ^h1[4]; - - uint64_t ah0 = h0[1] ^h0[5]; - uint64_t ah1 = h1[1] ^h1[5]; - - __m128i bx00 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx01 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - - __m128i bx10 = _mm_set_epi64x(h0[9] ^ h0[11], h0[8] ^ h0[10]); - __m128i bx11 = _mm_set_epi64x(h1[9] ^ h1[11], h1[8] ^ h1[10]); - - uint64_t idx0 = h0[0] ^h0[4]; - uint64_t idx1 = h1[0] ^h1[4]; - - SET_ROUNDING_MODE_UP(); - - VARIANT4_RANDOM_MATH_INIT(0, h0) - VARIANT4_RANDOM_MATH_INIT(1, h1) - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx0; - __m128i cx1; - - const __m128i ax0 = _mm_set_epi64x(ah0, al0); - const __m128i ax1 = _mm_set_epi64x(ah1, al1); - - if (SOFT_AES) { - cx0 = soft_aesenc((uint32_t*)&l0[idx0 & MASK], ax0); - cx1 = soft_aesenc((uint32_t*)&l1[idx1 & MASK], ax1); - } else { - cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - cx1 = _mm_load_si128((__m128i*) &l1[idx1 & MASK]); - - cx0 = _mm_aesenc_si128(cx0, ax0); - cx1 = _mm_aesenc_si128(cx1, ax1); - } - - SHUFFLE_V4(l0, (idx0&MASK), bx00, bx10, ax0, cx0) - SHUFFLE_V4(l1, (idx1&MASK), bx01, bx11, ax1, cx1) - - _mm_store_si128((__m128i*) &l0[idx0 & MASK], _mm_xor_si128(bx00, cx0)); - _mm_store_si128((__m128i*) &l1[idx1 & MASK], _mm_xor_si128(bx01, cx1)); - - idx0 = EXTRACT64(cx0); - idx1 = EXTRACT64(cx1); - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & MASK])[0]; - ch = ((uint64_t*) &l0[idx0 & MASK])[1]; - - VARIANT4_RANDOM_MATH(0, al0, ah0, cl, bx00, bx10); - - if (VARIANT == POW_V4) { - al0 ^= r0[2] | ((uint64_t)(r0[3]) << 32); - ah0 ^= r0[0] | ((uint64_t)(r0[1]) << 32); - } - - lo = __umul128(idx0, cl, &hi); - - SHUFFLE_V4(l0, (idx0&MASK), bx00, bx10, ax0, cx0); - - al0 += hi; - ah0 += lo; - - ((uint64_t*) &l0[idx0 & MASK])[0] = al0; - ((uint64_t*) &l0[idx0 & MASK])[1] = ah0; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - bx10 = bx00; - bx00 = cx0; - - - cl = ((uint64_t*) &l1[idx1 & MASK])[0]; - ch = ((uint64_t*) &l1[idx1 & MASK])[1]; - - VARIANT4_RANDOM_MATH(1, al1, ah1, cl, bx01, bx11); - - if (VARIANT == POW_V4) { - al1 ^= r1[2] | ((uint64_t)(r1[3]) << 32); - ah1 ^= r1[0] | ((uint64_t)(r1[1]) << 32); - } - - lo = __umul128(idx1, cl, &hi); - - SHUFFLE_V4(l1, (idx1&MASK), bx01, bx11, ax1, cx1); - - al1 += hi; - ah1 += lo; - - ((uint64_t*) &l1[idx1 & MASK])[0] = al1; - ((uint64_t*) &l1[idx1 & MASK])[1] = ah1; - - ah1 ^= ch; - al1 ^= cl; - idx1 = al1; - - bx11 = bx01; - bx01 = cx1; - } - - cn_implode_scratchpad((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad((__m128i*) l1, (__m128i*) h1); - - keccakf(h0, 24); - keccakf(h1, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - extra_hashes[scratchPad[1]->state[0] & 3](scratchPad[1]->state, 200, output + 32); - } - - inline static void hashPowV4_asm(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad, - uint64_t height, - AsmOptimization asmOptimization) - { - keccak((const uint8_t*) input, (int) size, scratchPad[0]->state, 200); - keccak((const uint8_t*) input + size, (int) size, scratchPad[1]->state, 200); - - const uint8_t* l0 = scratchPad[0]->memory; - const uint8_t* l1 = scratchPad[1]->memory; - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - uint64_t* h1 = reinterpret_cast(scratchPad[1]->state); - - cn_explode_scratchpad((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad((__m128i*) h1, (__m128i*) l1); - -#ifndef XMRIG_NO_ASM - if (!scratchPad[0]->generated_code_double_data.match(VARIANT, height)) { - V4_Instruction code[256]; - const int code_size = v4_random_math_init(code, VARIANT, height); - - if (VARIANT == POW_WOW) { - wow_compile_code_double(code, code_size, reinterpret_cast(scratchPad[0]->generated_code_double), asmOptimization); - } else { - v4_compile_code_double(code, code_size, reinterpret_cast(scratchPad[0]->generated_code_double), asmOptimization); - } - - scratchPad[0]->generated_code_double_data.variant = VARIANT; - scratchPad[0]->generated_code_double_data.height = height; - } - - scratchPad[0]->generated_code_double(scratchPad[0], scratchPad[1]); -#endif - cn_implode_scratchpad((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad((__m128i*) l1, (__m128i*) h1); - - keccakf(h0, 24); - keccakf(h1, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - extra_hashes[scratchPad[1]->state[0] & 3](scratchPad[1]->state, 200, output + 32); - } - - inline static void hashLiteTube(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - keccak((const uint8_t*) input, (int) size, scratchPad[0]->state, 200); - keccak((const uint8_t*) input + size, (int) size, scratchPad[1]->state, 200); - - uint64_t tweak1_2_0 = (*reinterpret_cast(reinterpret_cast(input) + 35) ^ - *(reinterpret_cast(scratchPad[0]->state) + 24)); - uint64_t tweak1_2_1 = (*reinterpret_cast(reinterpret_cast(input) + 35 + size) ^ - *(reinterpret_cast(scratchPad[1]->state) + 24)); - - const uint8_t* l0 = scratchPad[0]->memory; - const uint8_t* l1 = scratchPad[1]->memory; - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - uint64_t* h1 = reinterpret_cast(scratchPad[1]->state); - - cn_explode_scratchpad((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad((__m128i*) h1, (__m128i*) l1); - - uint64_t al0 = h0[0] ^h0[4]; - uint64_t al1 = h1[0] ^h1[4]; - uint64_t ah0 = h0[1] ^h0[5]; - uint64_t ah1 = h1[1] ^h1[5]; - - __m128i bx0 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx1 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - - uint64_t idx0 = h0[0] ^h0[4]; - uint64_t idx1 = h1[0] ^h1[4]; - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx0; - __m128i cx1; - - if (SOFT_AES) { - cx0 = soft_aesenc((uint32_t*)&l0[idx0 & MASK], _mm_set_epi64x(ah0, al0)); - cx1 = soft_aesenc((uint32_t*)&l1[idx1 & MASK], _mm_set_epi64x(ah1, al1)); - } else { - cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - cx1 = _mm_load_si128((__m128i*) &l1[idx1 & MASK]); - - cx0 = _mm_aesenc_si128(cx0, _mm_set_epi64x(ah0, al0)); - cx1 = _mm_aesenc_si128(cx1, _mm_set_epi64x(ah1, al1)); - } - - _mm_store_si128((__m128i*) &l0[idx0 & MASK], _mm_xor_si128(bx0, cx0)); - _mm_store_si128((__m128i*) &l1[idx1 & MASK], _mm_xor_si128(bx1, cx1)); - - static const uint32_t table = 0x75310; - uint8_t tmp = reinterpret_cast(&l0[idx0 & MASK])[11]; - uint8_t index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*)(&l0[idx0 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - tmp = reinterpret_cast(&l1[idx1 & MASK])[11]; - index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*)(&l1[idx1 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - - idx0 = EXTRACT64(cx0); - idx1 = EXTRACT64(cx1); - - bx0 = cx0; - bx1 = cx1; - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & MASK])[0]; - ch = ((uint64_t*) &l0[idx0 & MASK])[1]; - lo = __umul128(idx0, cl, &hi); - - al0 += hi; - ah0 += lo; - - ah0 ^= tweak1_2_0; - ((uint64_t*) &l0[idx0 & MASK])[0] = al0; - ((uint64_t*) &l0[idx0 & MASK])[1] = ah0; - ah0 ^= tweak1_2_0; - - ((uint64_t*)&l0[idx0 & MASK])[1] ^= ((uint64_t*)&l0[idx0 & MASK])[0]; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - cl = ((uint64_t*) &l1[idx1 & MASK])[0]; - ch = ((uint64_t*) &l1[idx1 & MASK])[1]; - lo = __umul128(idx1, cl, &hi); - - al1 += hi; - ah1 += lo; - - ah1 ^= tweak1_2_1; - ((uint64_t*) &l1[idx1 & MASK])[0] = al1; - ((uint64_t*) &l1[idx1 & MASK])[1] = ah1; - ah1 ^= tweak1_2_1; - - ((uint64_t*)&l1[idx1 & MASK])[1] ^= ((uint64_t*)&l1[idx1 & MASK])[0]; - - ah1 ^= ch; - al1 ^= cl; - idx1 = al1; - } - - cn_implode_scratchpad((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad((__m128i*) l1, (__m128i*) h1); - - keccakf(h0, 24); - keccakf(h1, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - extra_hashes[scratchPad[1]->state[0] & 3](scratchPad[1]->state, 200, output + 32); - } - - inline static void hashHeavy(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - keccak(static_cast(input), (int) size, scratchPad[0]->state, 200); - keccak(static_cast(input) + size, (int) size, scratchPad[1]->state, 200); - - const uint8_t* l0 = scratchPad[0]->memory; - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - - const uint8_t* l1 = scratchPad[1]->memory; - uint64_t* h1 = reinterpret_cast(scratchPad[1]->state); - - cn_explode_scratchpad_heavy((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad_heavy((__m128i*) h1, (__m128i*) l1); - - uint64_t al0 = h0[0] ^ h0[4]; - uint64_t al1 = h1[0] ^ h1[4]; - - uint64_t ah0 = h0[1] ^ h0[5]; - uint64_t ah1 = h1[1] ^ h1[5]; - - __m128i bx0 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx1 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - - uint64_t idx[2]; - idx[0] = al0; - idx[1] = al1; - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx0; - __m128i cx1; - - __m128i ax0 = _mm_set_epi64x(ah0, al0); - __m128i ax1 = _mm_set_epi64x(ah1, al1); - - if (SOFT_AES) { - cx0 = soft_aesenc((uint32_t *) &l0[idx[0] & MASK], ax0); - cx1 = soft_aesenc((uint32_t *) &l1[idx[1] & MASK], ax1); - } else { - cx0 = _mm_load_si128((__m128i *) &l0[idx[0] & MASK]); - cx1 = _mm_load_si128((__m128i *) &l1[idx[1] & MASK]); - cx0 = _mm_aesenc_si128(cx0, ax0); - cx1 = _mm_aesenc_si128(cx1, ax1); - } - - _mm_store_si128((__m128i *) &l0[idx[0] & MASK], _mm_xor_si128(bx0, cx0)); - _mm_store_si128((__m128i *) &l1[idx[1] & MASK], _mm_xor_si128(bx1, cx1)); - - idx[0] = EXTRACT64(cx0); - idx[1] = EXTRACT64(cx1); - - uint64_t hi, lo, cl, ch; - - cl = ((uint64_t*) &l0[idx[0] & MASK])[0]; - ch = ((uint64_t*) &l0[idx[0] & MASK])[1]; - lo = __umul128(idx[0], cl, &hi); - - al0 += hi; - ah0 += lo; - - ((uint64_t*) &l0[idx[0] & MASK])[0] = al0; - ((uint64_t*) &l0[idx[0] & MASK])[1] = ah0; - - ah0 ^= ch; - al0 ^= cl; - idx[0] = al0; - - int64_t n = ((int64_t*)&l0[idx[0] & MASK])[0]; - int32_t d = ((int32_t*)&l0[idx[0] & MASK])[2]; - int64_t q = n / (d | 0x5); - - ((int64_t*)&l0[idx[0] & MASK])[0] = n ^ q; - - if (VARIANT == POW_XHV || VARIANT == POW_XFH) { - idx[0] = (~d) ^ q; - } else { - idx[0] = d ^ q; - } - - bx0 = cx0; - - cl = ((uint64_t*) &l1[idx[1] & MASK])[0]; - ch = ((uint64_t*) &l1[idx[1] & MASK])[1]; - lo = __umul128(idx[1], cl, &hi); - - al1 += hi; - ah1 += lo; - - ((uint64_t*) &l1[idx[1] & MASK])[0] = al1; - ((uint64_t*) &l1[idx[1] & MASK])[1] = ah1; - - ah1 ^= ch; - al1 ^= cl; - idx[1] = al1; - - n = ((int64_t*)&l1[idx[1] & MASK])[0]; - d = ((int32_t*)&l1[idx[1] & MASK])[2]; - q = n / (d | 0x5); - - ((int64_t*)&l1[idx[1] & MASK])[0] = n ^ q; - - if (VARIANT == POW_XHV || VARIANT == POW_XFH) { - idx[1] = (~d) ^ q; - } else { - idx[1] = d ^ q; - } - - bx1 = cx1; - - } - - cn_implode_scratchpad_heavy((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad_heavy((__m128i*) l1, (__m128i*) h1); - - keccakf(h0, 24); - keccakf(h1, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - extra_hashes[scratchPad[1]->state[0] & 3](scratchPad[1]->state, 200, output + 32); - } - - inline static void hashHeavyTube(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - keccak((const uint8_t*) input, (int) size, scratchPad[0]->state, 200); - keccak((const uint8_t*) input + size, (int) size, scratchPad[1]->state, 200); - - uint64_t tweak1_2_0 = (*reinterpret_cast(reinterpret_cast(input) + 35) ^ - *(reinterpret_cast(scratchPad[0]->state) + 24)); - uint64_t tweak1_2_1 = (*reinterpret_cast(reinterpret_cast(input) + 35 + size) ^ - *(reinterpret_cast(scratchPad[1]->state) + 24)); - - const uint8_t* l0 = scratchPad[0]->memory; - const uint8_t* l1 = scratchPad[1]->memory; - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - uint64_t* h1 = reinterpret_cast(scratchPad[1]->state); - - cn_explode_scratchpad_heavy((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad_heavy((__m128i*) h1, (__m128i*) l1); - - uint64_t al0 = h0[0] ^h0[4]; - uint64_t al1 = h1[0] ^h1[4]; - uint64_t ah0 = h0[1] ^h0[5]; - uint64_t ah1 = h1[1] ^h1[5]; - - __m128i bx0 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx1 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - - uint64_t idx0 = h0[0] ^h0[4]; - uint64_t idx1 = h1[0] ^h1[4]; - - union alignas(16) { - uint32_t k[4]; - uint64_t v64[2]; - }; - alignas(16) uint32_t x[4]; - -#define BYTE(p, i) ((unsigned char*)&p)[i] - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - __m128i cx1 = _mm_load_si128((__m128i*) &l1[idx1 & MASK]); - - const __m128i& key0 = _mm_set_epi64x(ah0, al0); - - _mm_store_si128((__m128i*)k, key0); - cx0 = _mm_xor_si128(cx0, _mm_cmpeq_epi32(_mm_setzero_si128(), _mm_setzero_si128())); - _mm_store_si128((__m128i*)x, cx0); - - k[0] ^= saes_table[0][BYTE(x[0], 0)] ^ saes_table[1][BYTE(x[1], 1)] ^ saes_table[2][BYTE(x[2], 2)] ^ saes_table[3][BYTE(x[3], 3)]; - x[0] ^= k[0]; - k[1] ^= saes_table[0][BYTE(x[1], 0)] ^ saes_table[1][BYTE(x[2], 1)] ^ saes_table[2][BYTE(x[3], 2)] ^ saes_table[3][BYTE(x[0], 3)]; - x[1] ^= k[1]; - k[2] ^= saes_table[0][BYTE(x[2], 0)] ^ saes_table[1][BYTE(x[3], 1)] ^ saes_table[2][BYTE(x[0], 2)] ^ saes_table[3][BYTE(x[1], 3)]; - x[2] ^= k[2]; - k[3] ^= saes_table[0][BYTE(x[3], 0)] ^ saes_table[1][BYTE(x[0], 1)] ^ saes_table[2][BYTE(x[1], 2)] ^ saes_table[3][BYTE(x[2], 3)]; - - cx0 = _mm_load_si128((__m128i*)k); - - const __m128i& key1 = _mm_set_epi64x(ah1, al1); - - _mm_store_si128((__m128i*)k, key1); - cx1 = _mm_xor_si128(cx1, _mm_cmpeq_epi32(_mm_setzero_si128(), _mm_setzero_si128())); - _mm_store_si128((__m128i*)x, cx1); - - k[0] ^= saes_table[0][BYTE(x[0], 0)] ^ saes_table[1][BYTE(x[1], 1)] ^ saes_table[2][BYTE(x[2], 2)] ^ saes_table[3][BYTE(x[3], 3)]; - x[0] ^= k[0]; - k[1] ^= saes_table[0][BYTE(x[1], 0)] ^ saes_table[1][BYTE(x[2], 1)] ^ saes_table[2][BYTE(x[3], 2)] ^ saes_table[3][BYTE(x[0], 3)]; - x[1] ^= k[1]; - k[2] ^= saes_table[0][BYTE(x[2], 0)] ^ saes_table[1][BYTE(x[3], 1)] ^ saes_table[2][BYTE(x[0], 2)] ^ saes_table[3][BYTE(x[1], 3)]; - x[2] ^= k[2]; - k[3] ^= saes_table[0][BYTE(x[3], 0)] ^ saes_table[1][BYTE(x[0], 1)] ^ saes_table[2][BYTE(x[1], 2)] ^ saes_table[3][BYTE(x[2], 3)]; - - cx1 = _mm_load_si128((__m128i*)k); - - _mm_store_si128((__m128i*) &l0[idx0 & MASK], _mm_xor_si128(bx0, cx0)); - _mm_store_si128((__m128i*) &l1[idx1 & MASK], _mm_xor_si128(bx1, cx1)); - - static const uint32_t table = 0x75310; - uint8_t tmp = reinterpret_cast(&l0[idx0 & MASK])[11]; - uint8_t index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*)(&l0[idx0 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - tmp = reinterpret_cast(&l1[idx1 & MASK])[11]; - index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*)(&l1[idx1 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - - idx0 = EXTRACT64(cx0); - idx1 = EXTRACT64(cx1); - - bx0 = cx0; - bx1 = cx1; - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & MASK])[0]; - ch = ((uint64_t*) &l0[idx0 & MASK])[1]; - lo = __umul128(idx0, cl, &hi); - - al0 += hi; - ah0 += lo; - - ah0 ^= tweak1_2_0; - ((uint64_t*) &l0[idx0 & MASK])[0] = al0; - ((uint64_t*) &l0[idx0 & MASK])[1] = ah0; - ah0 ^= tweak1_2_0; - - ((uint64_t*)&l0[idx0 & MASK])[1] ^= ((uint64_t*)&l0[idx0 & MASK])[0]; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - int64_t n = ((int64_t*)&l0[idx0 & MASK])[0]; - int32_t d = ((int32_t*)&l0[idx0 & MASK])[2]; - int64_t q = n / (d | 0x5); - - ((int64_t*)&l0[idx0 & MASK])[0] = n ^ q; - idx0 = d ^ q; - - - cl = ((uint64_t*) &l1[idx1 & MASK])[0]; - ch = ((uint64_t*) &l1[idx1 & MASK])[1]; - lo = __umul128(idx1, cl, &hi); - - al1 += hi; - ah1 += lo; - - ah1 ^= tweak1_2_1; - ((uint64_t*) &l1[idx1 & MASK])[0] = al1; - ((uint64_t*) &l1[idx1 & MASK])[1] = ah1; - ah1 ^= tweak1_2_1; - - ((uint64_t*)&l1[idx1 & MASK])[1] ^= ((uint64_t*)&l1[idx1 & MASK])[0]; - - ah1 ^= ch; - al1 ^= cl; - idx1 = al1; - - n = ((int64_t*)&l1[idx1 & MASK])[0]; - d = ((int32_t*)&l1[idx1 & MASK])[2]; - q = n / (d | 0x5); - - ((int64_t*)&l1[idx1 & MASK])[0] = n ^ q; - idx1 = d ^ q; - } -#undef BYTE - - cn_implode_scratchpad_heavy((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad_heavy((__m128i*) l1, (__m128i*) h1); - - keccakf(h0, 24); - keccakf(h1, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - extra_hashes[scratchPad[1]->state[0] & 3](scratchPad[1]->state, 200, output + 32); - } -}; - -template -class CryptoNightMultiHash -{ -public: - inline static void hash(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - keccak((const uint8_t*) input, (int) size, scratchPad[0]->state, 200); - keccak((const uint8_t*) input + size, (int) size, scratchPad[1]->state, 200); - keccak((const uint8_t*) input + 2 * size, (int) size, scratchPad[2]->state, 200); - - const uint8_t* l0 = scratchPad[0]->memory; - const uint8_t* l1 = scratchPad[1]->memory; - const uint8_t* l2 = scratchPad[2]->memory; - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - uint64_t* h1 = reinterpret_cast(scratchPad[1]->state); - uint64_t* h2 = reinterpret_cast(scratchPad[2]->state); - - cn_explode_scratchpad((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad((__m128i*) h1, (__m128i*) l1); - cn_explode_scratchpad((__m128i*) h2, (__m128i*) l2); - - uint64_t al0 = h0[0] ^h0[4]; - uint64_t al1 = h1[0] ^h1[4]; - uint64_t al2 = h2[0] ^h2[4]; - uint64_t ah0 = h0[1] ^h0[5]; - uint64_t ah1 = h1[1] ^h1[5]; - uint64_t ah2 = h2[1] ^h2[5]; - - __m128i bx0 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx1 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - __m128i bx2 = _mm_set_epi64x(h2[3] ^ h2[7], h2[2] ^ h2[6]); - - __m128 conc_var0; - __m128 conc_var1; - __m128 conc_var2; - - if (VARIANT == POW_CONCEAL) { - SET_ROUNDING_MODE_NEAREST() - conc_var0 = _mm_setzero_ps(); - conc_var1 = _mm_setzero_ps(); - conc_var2 = _mm_setzero_ps(); - } - - uint64_t idx0 = h0[0] ^h0[4]; - uint64_t idx1 = h1[0] ^h1[4]; - uint64_t idx2 = h2[0] ^h2[4]; - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx0; - __m128i cx1; - __m128i cx2; - - if (SOFT_AES) { - if (VARIANT == POW_CONCEAL) { - cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - cx1 = _mm_load_si128((__m128i*) &l1[idx1 & MASK]); - cx2 = _mm_load_si128((__m128i*) &l2[idx2 & MASK]); - - cryptonight_conceal_tweak(cx0, conc_var0); - cryptonight_conceal_tweak(cx1, conc_var1); - cryptonight_conceal_tweak(cx2, conc_var2); - - cx0 = soft_aesenc(cx0, _mm_set_epi64x(ah0, al0)); - cx1 = soft_aesenc(cx1, _mm_set_epi64x(ah1, al1)); - cx2 = soft_aesenc(cx2, _mm_set_epi64x(ah2, al2)); - } else { - cx0 = soft_aesenc((uint32_t *) &l0[idx0 & MASK], _mm_set_epi64x(ah0, al0)); - cx1 = soft_aesenc((uint32_t *) &l1[idx1 & MASK], _mm_set_epi64x(ah1, al1)); - cx2 = soft_aesenc((uint32_t *) &l2[idx2 & MASK], _mm_set_epi64x(ah2, al2)); - } - } else { - cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - cx1 = _mm_load_si128((__m128i*) &l1[idx1 & MASK]); - cx2 = _mm_load_si128((__m128i*) &l2[idx2 & MASK]); - - if (VARIANT == POW_CONCEAL) { - cryptonight_conceal_tweak(cx0, conc_var0); - cryptonight_conceal_tweak(cx1, conc_var1); - cryptonight_conceal_tweak(cx2, conc_var2); - } - - cx0 = _mm_aesenc_si128(cx0, _mm_set_epi64x(ah0, al0)); - cx1 = _mm_aesenc_si128(cx1, _mm_set_epi64x(ah1, al1)); - cx2 = _mm_aesenc_si128(cx2, _mm_set_epi64x(ah2, al2)); - } - - _mm_store_si128((__m128i*) &l0[idx0 & MASK], _mm_xor_si128(bx0, cx0)); - _mm_store_si128((__m128i*) &l1[idx1 & MASK], _mm_xor_si128(bx1, cx1)); - _mm_store_si128((__m128i*) &l2[idx2 & MASK], _mm_xor_si128(bx2, cx2)); - - idx0 = EXTRACT64(cx0); - idx1 = EXTRACT64(cx1); - idx2 = EXTRACT64(cx2); - - bx0 = cx0; - bx1 = cx1; - bx2 = cx2; - - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & MASK])[0]; - ch = ((uint64_t*) &l0[idx0 & MASK])[1]; - lo = __umul128(idx0, cl, &hi); - - al0 += hi; - ah0 += lo; - - ((uint64_t*) &l0[idx0 & MASK])[0] = al0; - ((uint64_t*) &l0[idx0 & MASK])[1] = ah0; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - - cl = ((uint64_t*) &l1[idx1 & MASK])[0]; - ch = ((uint64_t*) &l1[idx1 & MASK])[1]; - lo = __umul128(idx1, cl, &hi); - - al1 += hi; - ah1 += lo; - - ((uint64_t*) &l1[idx1 & MASK])[0] = al1; - ((uint64_t*) &l1[idx1 & MASK])[1] = ah1; - - ah1 ^= ch; - al1 ^= cl; - idx1 = al1; - - - cl = ((uint64_t*) &l2[idx2 & MASK])[0]; - ch = ((uint64_t*) &l2[idx2 & MASK])[1]; - lo = __umul128(idx2, cl, &hi); - - al2 += hi; - ah2 += lo; - - ((uint64_t*) &l2[idx2 & MASK])[0] = al2; - ((uint64_t*) &l2[idx2 & MASK])[1] = ah2; - - ah2 ^= ch; - al2 ^= cl; - idx2 = al2; - } - - cn_implode_scratchpad((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad((__m128i*) l1, (__m128i*) h1); - cn_implode_scratchpad((__m128i*) l2, (__m128i*) h2); - - keccakf(h0, 24); - keccakf(h1, 24); - keccakf(h2, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - extra_hashes[scratchPad[1]->state[0] & 3](scratchPad[1]->state, 200, output + 32); - extra_hashes[scratchPad[2]->state[0] & 3](scratchPad[2]->state, 200, output + 64); - } - - inline static void hashPowV2(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - keccak((const uint8_t*) input, (int) size, scratchPad[0]->state, 200); - keccak((const uint8_t*) input + size, (int) size, scratchPad[1]->state, 200); - keccak((const uint8_t*) input + 2 * size, (int) size, scratchPad[2]->state, 200); - - uint64_t tweak1_2_0 = (*reinterpret_cast(reinterpret_cast(input) + 35) ^ - *(reinterpret_cast(scratchPad[0]->state) + 24)); - uint64_t tweak1_2_1 = (*reinterpret_cast(reinterpret_cast(input) + 35 + size) ^ - *(reinterpret_cast(scratchPad[1]->state) + 24)); - uint64_t tweak1_2_2 = (*reinterpret_cast(reinterpret_cast(input) + 35 + 2 * size) ^ - *(reinterpret_cast(scratchPad[2]->state) + 24)); - - const uint8_t* l0 = scratchPad[0]->memory; - const uint8_t* l1 = scratchPad[1]->memory; - const uint8_t* l2 = scratchPad[2]->memory; - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - uint64_t* h1 = reinterpret_cast(scratchPad[1]->state); - uint64_t* h2 = reinterpret_cast(scratchPad[2]->state); - - cn_explode_scratchpad((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad((__m128i*) h1, (__m128i*) l1); - cn_explode_scratchpad((__m128i*) h2, (__m128i*) l2); - - uint64_t al0 = h0[0] ^h0[4]; - uint64_t al1 = h1[0] ^h1[4]; - uint64_t al2 = h2[0] ^h2[4]; - uint64_t ah0 = h0[1] ^h0[5]; - uint64_t ah1 = h1[1] ^h1[5]; - uint64_t ah2 = h2[1] ^h2[5]; - - __m128i bx0 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx1 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - __m128i bx2 = _mm_set_epi64x(h2[3] ^ h2[7], h2[2] ^ h2[6]); - - uint64_t idx0 = h0[0] ^h0[4]; - uint64_t idx1 = h1[0] ^h1[4]; - uint64_t idx2 = h2[0] ^h2[4]; - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx0; - __m128i cx1; - __m128i cx2; - - if (SOFT_AES) { - cx0 = soft_aesenc((uint32_t*)&l0[idx0 & MASK], _mm_set_epi64x(ah0, al0)); - cx1 = soft_aesenc((uint32_t*)&l1[idx1 & MASK], _mm_set_epi64x(ah1, al1)); - cx2 = soft_aesenc((uint32_t*)&l2[idx2 & MASK], _mm_set_epi64x(ah2, al2)); - } else { - cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - cx1 = _mm_load_si128((__m128i*) &l1[idx1 & MASK]); - cx2 = _mm_load_si128((__m128i*) &l2[idx2 & MASK]); - - cx0 = _mm_aesenc_si128(cx0, _mm_set_epi64x(ah0, al0)); - cx1 = _mm_aesenc_si128(cx1, _mm_set_epi64x(ah1, al1)); - cx2 = _mm_aesenc_si128(cx2, _mm_set_epi64x(ah2, al2)); - } - - _mm_store_si128((__m128i*) &l0[idx0 & MASK], _mm_xor_si128(bx0, cx0)); - _mm_store_si128((__m128i*) &l1[idx1 & MASK], _mm_xor_si128(bx1, cx1)); - _mm_store_si128((__m128i*) &l2[idx2 & MASK], _mm_xor_si128(bx2, cx2)); - - static const uint32_t table = 0x75310; - uint8_t tmp = reinterpret_cast(&l0[idx0 & MASK])[11]; - uint8_t index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*)(&l0[idx0 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - tmp = reinterpret_cast(&l1[idx1 & MASK])[11]; - index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*)(&l1[idx1 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - tmp = reinterpret_cast(&l2[idx2 & MASK])[11]; - index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*)(&l2[idx2 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - - idx0 = EXTRACT64(cx0); - idx1 = EXTRACT64(cx1); - idx2 = EXTRACT64(cx2); - - bx0 = cx0; - bx1 = cx1; - bx2 = cx2; - - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & MASK])[0]; - ch = ((uint64_t*) &l0[idx0 & MASK])[1]; - lo = __umul128(idx0, cl, &hi); - - al0 += hi; - ah0 += lo; - - ah0 ^= tweak1_2_0; - ((uint64_t*) &l0[idx0 & MASK])[0] = al0; - ((uint64_t*) &l0[idx0 & MASK])[1] = ah0; - ah0 ^= tweak1_2_0; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - - cl = ((uint64_t*) &l1[idx1 & MASK])[0]; - ch = ((uint64_t*) &l1[idx1 & MASK])[1]; - lo = __umul128(idx1, cl, &hi); - - al1 += hi; - ah1 += lo; - - ah1 ^= tweak1_2_1; - ((uint64_t*) &l1[idx1 & MASK])[0] = al1; - ((uint64_t*) &l1[idx1 & MASK])[1] = ah1; - ah1 ^= tweak1_2_1; - - ah1 ^= ch; - al1 ^= cl; - idx1 = al1; - - - cl = ((uint64_t*) &l2[idx2 & MASK])[0]; - ch = ((uint64_t*) &l2[idx2 & MASK])[1]; - lo = __umul128(idx2, cl, &hi); - - al2 += hi; - ah2 += lo; - - ah2 ^= tweak1_2_2; - ((uint64_t*) &l2[idx2 & MASK])[0] = al2; - ((uint64_t*) &l2[idx2 & MASK])[1] = ah2; - ah2 ^= tweak1_2_2; - - ah2 ^= ch; - al2 ^= cl; - idx2 = al2; - } - - cn_implode_scratchpad((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad((__m128i*) l1, (__m128i*) h1); - cn_implode_scratchpad((__m128i*) l2, (__m128i*) h2); - - keccakf(h0, 24); - keccakf(h1, 24); - keccakf(h2, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - extra_hashes[scratchPad[1]->state[0] & 3](scratchPad[1]->state, 200, output + 32); - extra_hashes[scratchPad[2]->state[0] & 3](scratchPad[2]->state, 200, output + 64); - } - - inline static void hashPowV2_asm(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad, - AsmOptimization asmOptimization) - { - // not supported - } - - // triple - inline static void hashPowV3(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - keccak((const uint8_t*) input, (int) size, scratchPad[0]->state, 200); - keccak((const uint8_t*) input + size, (int) size, scratchPad[1]->state, 200); - keccak((const uint8_t*) input + 2 * size, (int) size, scratchPad[2]->state, 200); - - const uint8_t* l0 = scratchPad[0]->memory; - const uint8_t* l1 = scratchPad[1]->memory; - const uint8_t* l2 = scratchPad[2]->memory; - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - uint64_t* h1 = reinterpret_cast(scratchPad[1]->state); - uint64_t* h2 = reinterpret_cast(scratchPad[2]->state); - - cn_explode_scratchpad((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad((__m128i*) h1, (__m128i*) l1); - cn_explode_scratchpad((__m128i*) h2, (__m128i*) l2); - - uint64_t al0 = h0[0] ^h0[4]; - uint64_t al1 = h1[0] ^h1[4]; - uint64_t al2 = h2[0] ^h2[4]; - - uint64_t ah0 = h0[1] ^h0[5]; - uint64_t ah1 = h1[1] ^h1[5]; - uint64_t ah2 = h2[1] ^h2[5]; - - __m128i bx00 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx01 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - __m128i bx02 = _mm_set_epi64x(h2[3] ^ h2[7], h2[2] ^ h2[6]); - - __m128i bx10 = _mm_set_epi64x(h0[9] ^ h0[11], h0[8] ^ h0[10]); - __m128i bx11 = _mm_set_epi64x(h1[9] ^ h1[11], h1[8] ^ h1[10]); - __m128i bx12 = _mm_set_epi64x(h2[9] ^ h2[11], h2[8] ^ h2[10]); - - uint64_t idx0 = h0[0] ^h0[4]; - uint64_t idx1 = h1[0] ^h1[4]; - uint64_t idx2 = h2[0] ^h2[4]; - - SET_ROUNDING_MODE_UP(); - - __m128i division_result_xmm0 = _mm_cvtsi64_si128(h0[12]); - __m128i division_result_xmm1 = _mm_cvtsi64_si128(h1[12]); - __m128i division_result_xmm2 = _mm_cvtsi64_si128(h2[12]); - - uint64_t sqrt_result0 = h0[13]; - uint64_t sqrt_result1 = h1[13]; - uint64_t sqrt_result2 = h2[13]; - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx0; - __m128i cx1; - __m128i cx2; - - const __m128i ax0 = _mm_set_epi64x(ah0, al0); - const __m128i ax1 = _mm_set_epi64x(ah1, al1); - const __m128i ax2 = _mm_set_epi64x(ah2, al2); - - if (SOFT_AES) { - cx0 = soft_aesenc((uint32_t*)&l0[idx0 & MASK], ax0); - cx1 = soft_aesenc((uint32_t*)&l1[idx1 & MASK], ax1); - cx2 = soft_aesenc((uint32_t*)&l2[idx2 & MASK], ax2); - } else { - cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - cx1 = _mm_load_si128((__m128i*) &l1[idx1 & MASK]); - cx2 = _mm_load_si128((__m128i*) &l2[idx2 & MASK]); - - cx0 = _mm_aesenc_si128(cx0, ax0); - cx1 = _mm_aesenc_si128(cx1, ax1); - cx2 = _mm_aesenc_si128(cx2, ax2); - } - - SHUFFLE_PHASE_1(l0, (idx0&MASK), bx00, bx10, ax0, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - SHUFFLE_PHASE_1(l1, (idx1&MASK), bx01, bx11, ax1, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - SHUFFLE_PHASE_1(l2, (idx2&MASK), bx02, bx12, ax2, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - - _mm_store_si128((__m128i*) &l0[idx0 & MASK], _mm_xor_si128(bx00, cx0)); - _mm_store_si128((__m128i*) &l1[idx1 & MASK], _mm_xor_si128(bx01, cx1)); - _mm_store_si128((__m128i*) &l2[idx2 & MASK], _mm_xor_si128(bx02, cx2)); - - idx0 = EXTRACT64(cx0); - idx1 = EXTRACT64(cx1); - idx2 = EXTRACT64(cx2); - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & MASK])[0]; - ch = ((uint64_t*) &l0[idx0 & MASK])[1]; - - INTEGER_MATH_V2(0, cl, cx0); - - lo = __umul128(idx0, cl, &hi); - - SHUFFLE_PHASE_2(l0, (idx0&MASK), bx00, bx10, ax0, lo, hi, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - - al0 += hi; - ah0 += lo; - - ((uint64_t*) &l0[idx0 & MASK])[0] = al0; - ((uint64_t*) &l0[idx0 & MASK])[1] = ah0; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - bx10 = bx00; - bx00 = cx0; - - - cl = ((uint64_t*) &l1[idx1 & MASK])[0]; - ch = ((uint64_t*) &l1[idx1 & MASK])[1]; - - INTEGER_MATH_V2(1, cl, cx1); - - lo = __umul128(idx1, cl, &hi); - - SHUFFLE_PHASE_2(l1, (idx1&MASK), bx01, bx11, ax1, lo, hi, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - - al1 += hi; - ah1 += lo; - - ((uint64_t*) &l1[idx1 & MASK])[0] = al1; - ((uint64_t*) &l1[idx1 & MASK])[1] = ah1; - - ah1 ^= ch; - al1 ^= cl; - idx1 = al1; - - bx11 = bx01; - bx01 = cx1; - - cl = ((uint64_t*) &l2[idx2 & MASK])[0]; - ch = ((uint64_t*) &l2[idx2 & MASK])[1]; - - INTEGER_MATH_V2(2, cl, cx2); - - lo = __umul128(idx2, cl, &hi); - - SHUFFLE_PHASE_2(l2, (idx2&MASK), bx02, bx12, ax2, lo, hi, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - - al2 += hi; - ah2 += lo; - - ((uint64_t*) &l2[idx2 & MASK])[0] = al2; - ((uint64_t*) &l2[idx2 & MASK])[1] = ah2; - - ah2 ^= ch; - al2 ^= cl; - idx2 = al2; - - bx12 = bx02; - bx02 = cx2; - } - - cn_implode_scratchpad((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad((__m128i*) l1, (__m128i*) h1); - cn_implode_scratchpad((__m128i*) l2, (__m128i*) h2); - - keccakf(h0, 24); - keccakf(h1, 24); - keccakf(h2, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - extra_hashes[scratchPad[1]->state[0] & 3](scratchPad[1]->state, 200, output + 32); - extra_hashes[scratchPad[2]->state[0] & 3](scratchPad[2]->state, 200, output + 64); - } - - inline static void hashPowV3_asm(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad, - AsmOptimization asmOptimization) - { - // not supported - } - - // triple - inline static void hashPowV4(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad, - uint64_t height) - { - keccak((const uint8_t*) input, (int) size, scratchPad[0]->state, 200); - keccak((const uint8_t*) input + size, (int) size, scratchPad[1]->state, 200); - keccak((const uint8_t*) input + 2 * size, (int) size, scratchPad[2]->state, 200); - - const uint8_t* l0 = scratchPad[0]->memory; - const uint8_t* l1 = scratchPad[1]->memory; - const uint8_t* l2 = scratchPad[2]->memory; - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - uint64_t* h1 = reinterpret_cast(scratchPad[1]->state); - uint64_t* h2 = reinterpret_cast(scratchPad[2]->state); - - cn_explode_scratchpad((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad((__m128i*) h1, (__m128i*) l1); - cn_explode_scratchpad((__m128i*) h2, (__m128i*) l2); - - uint64_t al0 = h0[0] ^h0[4]; - uint64_t al1 = h1[0] ^h1[4]; - uint64_t al2 = h2[0] ^h2[4]; - - uint64_t ah0 = h0[1] ^h0[5]; - uint64_t ah1 = h1[1] ^h1[5]; - uint64_t ah2 = h2[1] ^h2[5]; - - __m128i bx00 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx01 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - __m128i bx02 = _mm_set_epi64x(h2[3] ^ h2[7], h2[2] ^ h2[6]); - - __m128i bx10 = _mm_set_epi64x(h0[9] ^ h0[11], h0[8] ^ h0[10]); - __m128i bx11 = _mm_set_epi64x(h1[9] ^ h1[11], h1[8] ^ h1[10]); - __m128i bx12 = _mm_set_epi64x(h2[9] ^ h2[11], h2[8] ^ h2[10]); - - uint64_t idx0 = h0[0] ^h0[4]; - uint64_t idx1 = h1[0] ^h1[4]; - uint64_t idx2 = h2[0] ^h2[4]; - - SET_ROUNDING_MODE_UP(); - - VARIANT4_RANDOM_MATH_INIT(0, h0) - VARIANT4_RANDOM_MATH_INIT(1, h1) - VARIANT4_RANDOM_MATH_INIT(2, h2) - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx0; - __m128i cx1; - __m128i cx2; - - const __m128i ax0 = _mm_set_epi64x(ah0, al0); - const __m128i ax1 = _mm_set_epi64x(ah1, al1); - const __m128i ax2 = _mm_set_epi64x(ah2, al2); - - if (SOFT_AES) { - cx0 = soft_aesenc((uint32_t*)&l0[idx0 & MASK], ax0); - cx1 = soft_aesenc((uint32_t*)&l1[idx1 & MASK], ax1); - cx2 = soft_aesenc((uint32_t*)&l2[idx2 & MASK], ax2); - } else { - cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - cx1 = _mm_load_si128((__m128i*) &l1[idx1 & MASK]); - cx2 = _mm_load_si128((__m128i*) &l2[idx2 & MASK]); - - cx0 = _mm_aesenc_si128(cx0, ax0); - cx1 = _mm_aesenc_si128(cx1, ax1); - cx2 = _mm_aesenc_si128(cx2, ax2); - } - - SHUFFLE_V4(l0, (idx0&MASK), bx00, bx10, ax0, cx0) - SHUFFLE_V4(l1, (idx1&MASK), bx01, bx11, ax1, cx1) - SHUFFLE_V4(l2, (idx2&MASK), bx02, bx12, ax2, cx2) - - _mm_store_si128((__m128i*) &l0[idx0 & MASK], _mm_xor_si128(bx00, cx0)); - _mm_store_si128((__m128i*) &l1[idx1 & MASK], _mm_xor_si128(bx01, cx1)); - _mm_store_si128((__m128i*) &l2[idx2 & MASK], _mm_xor_si128(bx02, cx2)); - - idx0 = EXTRACT64(cx0); - idx1 = EXTRACT64(cx1); - idx2 = EXTRACT64(cx2); - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & MASK])[0]; - ch = ((uint64_t*) &l0[idx0 & MASK])[1]; - - VARIANT4_RANDOM_MATH(0, al0, ah0, cl, bx00, bx10); - - if (VARIANT == POW_V4) { - al0 ^= r0[2] | ((uint64_t)(r0[3]) << 32); - ah0 ^= r0[0] | ((uint64_t)(r0[1]) << 32); - } - - lo = __umul128(idx0, cl, &hi); - - SHUFFLE_V4(l0, (idx0&MASK), bx00, bx10, ax0, cx0); - - al0 += hi; - ah0 += lo; - - ((uint64_t*) &l0[idx0 & MASK])[0] = al0; - ((uint64_t*) &l0[idx0 & MASK])[1] = ah0; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - bx10 = bx00; - bx00 = cx0; - - - cl = ((uint64_t*) &l1[idx1 & MASK])[0]; - ch = ((uint64_t*) &l1[idx1 & MASK])[1]; - - VARIANT4_RANDOM_MATH(1, al1, ah1, cl, bx01, bx11); - - if (VARIANT == POW_V4) { - al1 ^= r1[2] | ((uint64_t)(r1[3]) << 32); - ah1 ^= r1[0] | ((uint64_t)(r1[1]) << 32); - } - - lo = __umul128(idx1, cl, &hi); - - SHUFFLE_V4(l1, (idx1&MASK), bx01, bx11, ax1, cx1) - - al1 += hi; - ah1 += lo; - - ((uint64_t*) &l1[idx1 & MASK])[0] = al1; - ((uint64_t*) &l1[idx1 & MASK])[1] = ah1; - - ah1 ^= ch; - al1 ^= cl; - idx1 = al1; - - bx11 = bx01; - bx01 = cx1; - - cl = ((uint64_t*) &l2[idx2 & MASK])[0]; - ch = ((uint64_t*) &l2[idx2 & MASK])[1]; - - VARIANT4_RANDOM_MATH(2, al2, ah2, cl, bx02, bx12); - - if (VARIANT == POW_V4) { - al2 ^= r2[2] | ((uint64_t)(r2[3]) << 32); - ah2 ^= r2[0] | ((uint64_t)(r2[1]) << 32); - } - - lo = __umul128(idx2, cl, &hi); - - SHUFFLE_V4(l2, (idx2&MASK), bx02, bx12, ax2, cx2) - - al2 += hi; - ah2 += lo; - - ((uint64_t*) &l2[idx2 & MASK])[0] = al2; - ((uint64_t*) &l2[idx2 & MASK])[1] = ah2; - - ah2 ^= ch; - al2 ^= cl; - idx2 = al2; - - bx12 = bx02; - bx02 = cx2; - } - - cn_implode_scratchpad((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad((__m128i*) l1, (__m128i*) h1); - cn_implode_scratchpad((__m128i*) l2, (__m128i*) h2); - - keccakf(h0, 24); - keccakf(h1, 24); - keccakf(h2, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - extra_hashes[scratchPad[1]->state[0] & 3](scratchPad[1]->state, 200, output + 32); - extra_hashes[scratchPad[2]->state[0] & 3](scratchPad[2]->state, 200, output + 64); - } - - inline static void hashPowV4_asm(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad, - uint64_t height, - AsmOptimization asmOptimization) - { - // not supported - } - - inline static void hashLiteTube(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - keccak((const uint8_t*) input, (int) size, scratchPad[0]->state, 200); - keccak((const uint8_t*) input + size, (int) size, scratchPad[1]->state, 200); - keccak((const uint8_t*) input + 2 * size, (int) size, scratchPad[2]->state, 200); - - uint64_t tweak1_2_0 = (*reinterpret_cast(reinterpret_cast(input) + 35) ^ - *(reinterpret_cast(scratchPad[0]->state) + 24)); - uint64_t tweak1_2_1 = (*reinterpret_cast(reinterpret_cast(input) + 35 + size) ^ - *(reinterpret_cast(scratchPad[1]->state) + 24)); - uint64_t tweak1_2_2 = (*reinterpret_cast(reinterpret_cast(input) + 35 + 2 * size) ^ - *(reinterpret_cast(scratchPad[2]->state) + 24)); - - const uint8_t* l0 = scratchPad[0]->memory; - const uint8_t* l1 = scratchPad[1]->memory; - const uint8_t* l2 = scratchPad[2]->memory; - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - uint64_t* h1 = reinterpret_cast(scratchPad[1]->state); - uint64_t* h2 = reinterpret_cast(scratchPad[2]->state); - - cn_explode_scratchpad((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad((__m128i*) h1, (__m128i*) l1); - cn_explode_scratchpad((__m128i*) h2, (__m128i*) l2); - - uint64_t al0 = h0[0] ^h0[4]; - uint64_t al1 = h1[0] ^h1[4]; - uint64_t al2 = h2[0] ^h2[4]; - uint64_t ah0 = h0[1] ^h0[5]; - uint64_t ah1 = h1[1] ^h1[5]; - uint64_t ah2 = h2[1] ^h2[5]; - - __m128i bx0 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx1 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - __m128i bx2 = _mm_set_epi64x(h2[3] ^ h2[7], h2[2] ^ h2[6]); - - uint64_t idx0 = h0[0] ^h0[4]; - uint64_t idx1 = h1[0] ^h1[4]; - uint64_t idx2 = h2[0] ^h2[4]; - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx0; - __m128i cx1; - __m128i cx2; - - if (SOFT_AES) { - cx0 = soft_aesenc((uint32_t*)&l0[idx0 & MASK], _mm_set_epi64x(ah0, al0)); - cx1 = soft_aesenc((uint32_t*)&l1[idx1 & MASK], _mm_set_epi64x(ah1, al1)); - cx2 = soft_aesenc((uint32_t*)&l2[idx2 & MASK], _mm_set_epi64x(ah2, al2)); - } else { - cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - cx1 = _mm_load_si128((__m128i*) &l1[idx1 & MASK]); - cx2 = _mm_load_si128((__m128i*) &l2[idx2 & MASK]); - - cx0 = _mm_aesenc_si128(cx0, _mm_set_epi64x(ah0, al0)); - cx1 = _mm_aesenc_si128(cx1, _mm_set_epi64x(ah1, al1)); - cx2 = _mm_aesenc_si128(cx2, _mm_set_epi64x(ah2, al2)); - } - - _mm_store_si128((__m128i*) &l0[idx0 & MASK], _mm_xor_si128(bx0, cx0)); - _mm_store_si128((__m128i*) &l1[idx1 & MASK], _mm_xor_si128(bx1, cx1)); - _mm_store_si128((__m128i*) &l2[idx2 & MASK], _mm_xor_si128(bx2, cx2)); - - static const uint32_t table = 0x75310; - uint8_t tmp = reinterpret_cast(&l0[idx0 & MASK])[11]; - uint8_t index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*)(&l0[idx0 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - tmp = reinterpret_cast(&l1[idx1 & MASK])[11]; - index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*)(&l1[idx1 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - tmp = reinterpret_cast(&l2[idx2 & MASK])[11]; - index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*)(&l2[idx2 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - - idx0 = EXTRACT64(cx0); - idx1 = EXTRACT64(cx1); - idx2 = EXTRACT64(cx2); - - bx0 = cx0; - bx1 = cx1; - bx2 = cx2; - - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & MASK])[0]; - ch = ((uint64_t*) &l0[idx0 & MASK])[1]; - lo = __umul128(idx0, cl, &hi); - - al0 += hi; - ah0 += lo; - - ah0 ^= tweak1_2_0; - ((uint64_t*) &l0[idx0 & MASK])[0] = al0; - ((uint64_t*) &l0[idx0 & MASK])[1] = ah0; - ah0 ^= tweak1_2_0; - - ((uint64_t*)&l0[idx0 & MASK])[1] ^= ((uint64_t*)&l0[idx0 & MASK])[0]; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - - cl = ((uint64_t*) &l1[idx1 & MASK])[0]; - ch = ((uint64_t*) &l1[idx1 & MASK])[1]; - lo = __umul128(idx1, cl, &hi); - - al1 += hi; - ah1 += lo; - - ah1 ^= tweak1_2_1; - ((uint64_t*) &l1[idx1 & MASK])[0] = al1; - ((uint64_t*) &l1[idx1 & MASK])[1] = ah1; - ah1 ^= tweak1_2_1; - - ((uint64_t*)&l1[idx1 & MASK])[1] ^= ((uint64_t*)&l1[idx1 & MASK])[0]; - - ah1 ^= ch; - al1 ^= cl; - idx1 = al1; - - - cl = ((uint64_t*) &l2[idx2 & MASK])[0]; - ch = ((uint64_t*) &l2[idx2 & MASK])[1]; - lo = __umul128(idx2, cl, &hi); - - al2 += hi; - ah2 += lo; - - ah2 ^= tweak1_2_2; - ((uint64_t*) &l2[idx2 & MASK])[0] = al2; - ((uint64_t*) &l2[idx2 & MASK])[1] = ah2; - ah2 ^= tweak1_2_2; - - ((uint64_t*)&l2[idx2 & MASK])[1] ^= ((uint64_t*)&l2[idx2 & MASK])[0]; - - ah2 ^= ch; - al2 ^= cl; - idx2 = al2; - } - - cn_implode_scratchpad((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad((__m128i*) l1, (__m128i*) h1); - cn_implode_scratchpad((__m128i*) l2, (__m128i*) h2); - - keccakf(h0, 24); - keccakf(h1, 24); - keccakf(h2, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - extra_hashes[scratchPad[1]->state[0] & 3](scratchPad[1]->state, 200, output + 32); - extra_hashes[scratchPad[2]->state[0] & 3](scratchPad[2]->state, 200, output + 64); - } - - inline static void hashHeavy(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - keccak((const uint8_t*) input, (int) size, scratchPad[0]->state, 200); - keccak((const uint8_t*) input + size, (int) size, scratchPad[1]->state, 200); - keccak((const uint8_t*) input + 2 * size, (int) size, scratchPad[2]->state, 200); - - const uint8_t* l0 = scratchPad[0]->memory; - const uint8_t* l1 = scratchPad[1]->memory; - const uint8_t* l2 = scratchPad[2]->memory; - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - uint64_t* h1 = reinterpret_cast(scratchPad[1]->state); - uint64_t* h2 = reinterpret_cast(scratchPad[2]->state); - - cn_explode_scratchpad_heavy((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad_heavy((__m128i*) h1, (__m128i*) l1); - cn_explode_scratchpad_heavy((__m128i*) h2, (__m128i*) l2); - - uint64_t al0 = h0[0] ^h0[4]; - uint64_t al1 = h1[0] ^h1[4]; - uint64_t al2 = h2[0] ^h2[4]; - uint64_t ah0 = h0[1] ^h0[5]; - uint64_t ah1 = h1[1] ^h1[5]; - uint64_t ah2 = h2[1] ^h2[5]; - - __m128i bx0 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx1 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - __m128i bx2 = _mm_set_epi64x(h2[3] ^ h2[7], h2[2] ^ h2[6]); - - uint64_t idx[3]; - idx[0] = al0; - idx[1] = al1; - idx[2] = al2; - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx0; - __m128i cx1; - __m128i cx2; - - if (SOFT_AES) { - cx0 = soft_aesenc((uint32_t*)&l0[idx[0] & MASK], _mm_set_epi64x(ah0, al0)); - cx1 = soft_aesenc((uint32_t*)&l1[idx[1] & MASK], _mm_set_epi64x(ah1, al1)); - cx2 = soft_aesenc((uint32_t*)&l2[idx[2] & MASK], _mm_set_epi64x(ah2, al2)); - } else { - cx0 = _mm_load_si128((__m128i*) &l0[idx[0] & MASK]); - cx1 = _mm_load_si128((__m128i*) &l1[idx[1] & MASK]); - cx2 = _mm_load_si128((__m128i*) &l2[idx[2] & MASK]); - - cx0 = _mm_aesenc_si128(cx0, _mm_set_epi64x(ah0, al0)); - cx1 = _mm_aesenc_si128(cx1, _mm_set_epi64x(ah1, al1)); - cx2 = _mm_aesenc_si128(cx2, _mm_set_epi64x(ah2, al2)); - } - - _mm_store_si128((__m128i*) &l0[idx[0] & MASK], _mm_xor_si128(bx0, cx0)); - _mm_store_si128((__m128i*) &l1[idx[1] & MASK], _mm_xor_si128(bx1, cx1)); - _mm_store_si128((__m128i*) &l2[idx[2] & MASK], _mm_xor_si128(bx2, cx2)); - - idx[0] = EXTRACT64(cx0); - idx[1] = EXTRACT64(cx1); - idx[2] = EXTRACT64(cx2); - - bx0 = cx0; - bx1 = cx1; - bx2 = cx2; - - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx[0] & MASK])[0]; - ch = ((uint64_t*) &l0[idx[0] & MASK])[1]; - lo = __umul128(idx[0], cl, &hi); - - al0 += hi; - ah0 += lo; - - ((uint64_t*) &l0[idx[0] & MASK])[0] = al0; - ((uint64_t*) &l0[idx[0] & MASK])[1] = ah0; - - ah0 ^= ch; - al0 ^= cl; - idx[0] = al0; - - int64_t n = ((int64_t*)&l0[idx[0] & MASK])[0]; - int32_t d = ((int32_t*)&l0[idx[0] & MASK])[2]; - int64_t q = n / (d | 0x5); - - ((int64_t*)&l0[idx[0] & MASK])[0] = n ^ q; - - if (VARIANT == POW_XHV || VARIANT == POW_XFH) { - idx[0] = (~d) ^ q; - } else { - idx[0] = d ^ q; - } - - cl = ((uint64_t*) &l1[idx[1] & MASK])[0]; - ch = ((uint64_t*) &l1[idx[1] & MASK])[1]; - lo = __umul128(idx[1], cl, &hi); - - al1 += hi; - ah1 += lo; - - ((uint64_t*) &l1[idx[1] & MASK])[0] = al1; - ((uint64_t*) &l1[idx[1] & MASK])[1] = ah1; - - ah1 ^= ch; - al1 ^= cl; - idx[1] = al1; - - n = ((int64_t*)&l1[idx[1] & MASK])[0]; - d = ((int32_t*)&l1[idx[1] & MASK])[2]; - q = n / (d | 0x5); - - ((int64_t*)&l1[idx[1] & MASK])[0] = n ^ q; - - if (VARIANT == POW_XHV || VARIANT == POW_XFH) { - idx[1] = (~d) ^ q; - } else { - idx[1] = d ^ q; - } - - cl = ((uint64_t*) &l2[idx[2] & MASK])[0]; - ch = ((uint64_t*) &l2[idx[2] & MASK])[1]; - lo = __umul128(idx[2], cl, &hi); - - al2 += hi; - ah2 += lo; - - ((uint64_t*) &l2[idx[2] & MASK])[0] = al2; - ((uint64_t*) &l2[idx[2] & MASK])[1] = ah2; - - ah2 ^= ch; - al2 ^= cl; - idx[2] = al2; - - n = ((int64_t*)&l2[idx[2] & MASK])[0]; - d = ((int32_t*)&l2[idx[2] & MASK])[2]; - q = n / (d | 0x5); - - ((int64_t*)&l2[idx[2] & MASK])[0] = n ^ q; - - if (VARIANT == POW_XHV || VARIANT == POW_XFH) { - idx[2] = (~d) ^ q; - } else { - idx[2] = d ^ q; - } - } - - cn_implode_scratchpad_heavy((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad_heavy((__m128i*) l1, (__m128i*) h1); - cn_implode_scratchpad_heavy((__m128i*) l2, (__m128i*) h2); - - keccakf(h0, 24); - keccakf(h1, 24); - keccakf(h2, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - extra_hashes[scratchPad[1]->state[0] & 3](scratchPad[1]->state, 200, output + 32); - extra_hashes[scratchPad[2]->state[0] & 3](scratchPad[2]->state, 200, output + 64); - } - - inline static void hashHeavyTube(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - keccak((const uint8_t*) input, (int) size, scratchPad[0]->state, 200); - keccak((const uint8_t*) input + size, (int) size, scratchPad[1]->state, 200); - keccak((const uint8_t*) input + 2 * size, (int) size, scratchPad[2]->state, 200); - - uint64_t tweak1_2_0 = (*reinterpret_cast(reinterpret_cast(input) + 35) ^ - *(reinterpret_cast(scratchPad[0]->state) + 24)); - uint64_t tweak1_2_1 = (*reinterpret_cast(reinterpret_cast(input) + 35 + size) ^ - *(reinterpret_cast(scratchPad[1]->state) + 24)); - uint64_t tweak1_2_2 = (*reinterpret_cast(reinterpret_cast(input) + 35 + 2 * size) ^ - *(reinterpret_cast(scratchPad[2]->state) + 24)); - - const uint8_t* l0 = scratchPad[0]->memory; - const uint8_t* l1 = scratchPad[1]->memory; - const uint8_t* l2 = scratchPad[2]->memory; - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - uint64_t* h1 = reinterpret_cast(scratchPad[1]->state); - uint64_t* h2 = reinterpret_cast(scratchPad[2]->state); - - cn_explode_scratchpad_heavy((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad_heavy((__m128i*) h1, (__m128i*) l1); - cn_explode_scratchpad_heavy((__m128i*) h2, (__m128i*) l2); - - uint64_t al0 = h0[0] ^h0[4]; - uint64_t al1 = h1[0] ^h1[4]; - uint64_t al2 = h2[0] ^h2[4]; - uint64_t ah0 = h0[1] ^h0[5]; - uint64_t ah1 = h1[1] ^h1[5]; - uint64_t ah2 = h2[1] ^h2[5]; - - __m128i bx0 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx1 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - __m128i bx2 = _mm_set_epi64x(h2[3] ^ h2[7], h2[2] ^ h2[6]); - - uint64_t idx0 = h0[0] ^h0[4]; - uint64_t idx1 = h1[0] ^h1[4]; - uint64_t idx2 = h2[0] ^h2[4]; - - union alignas(16) { - uint32_t k[4]; - uint64_t v64[2]; - }; - alignas(16) uint32_t x[4]; - -#define BYTE(p, i) ((unsigned char*)&p)[i] - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - __m128i cx1 = _mm_load_si128((__m128i*) &l1[idx1 & MASK]); - __m128i cx2 = _mm_load_si128((__m128i*) &l2[idx2 & MASK]); - - const __m128i& key0 = _mm_set_epi64x(ah0, al0); - - _mm_store_si128((__m128i*)k, key0); - cx0 = _mm_xor_si128(cx0, _mm_cmpeq_epi32(_mm_setzero_si128(), _mm_setzero_si128())); - _mm_store_si128((__m128i*)x, cx0); - - k[0] ^= saes_table[0][BYTE(x[0], 0)] ^ saes_table[1][BYTE(x[1], 1)] ^ saes_table[2][BYTE(x[2], 2)] ^ saes_table[3][BYTE(x[3], 3)]; - x[0] ^= k[0]; - k[1] ^= saes_table[0][BYTE(x[1], 0)] ^ saes_table[1][BYTE(x[2], 1)] ^ saes_table[2][BYTE(x[3], 2)] ^ saes_table[3][BYTE(x[0], 3)]; - x[1] ^= k[1]; - k[2] ^= saes_table[0][BYTE(x[2], 0)] ^ saes_table[1][BYTE(x[3], 1)] ^ saes_table[2][BYTE(x[0], 2)] ^ saes_table[3][BYTE(x[1], 3)]; - x[2] ^= k[2]; - k[3] ^= saes_table[0][BYTE(x[3], 0)] ^ saes_table[1][BYTE(x[0], 1)] ^ saes_table[2][BYTE(x[1], 2)] ^ saes_table[3][BYTE(x[2], 3)]; - - cx0 = _mm_load_si128((__m128i*)k); - - const __m128i& key1 = _mm_set_epi64x(ah1, al1); - - _mm_store_si128((__m128i*)k, key1); - cx1 = _mm_xor_si128(cx1, _mm_cmpeq_epi32(_mm_setzero_si128(), _mm_setzero_si128())); - _mm_store_si128((__m128i*)x, cx1); - - k[0] ^= saes_table[0][BYTE(x[0], 0)] ^ saes_table[1][BYTE(x[1], 1)] ^ saes_table[2][BYTE(x[2], 2)] ^ saes_table[3][BYTE(x[3], 3)]; - x[0] ^= k[0]; - k[1] ^= saes_table[0][BYTE(x[1], 0)] ^ saes_table[1][BYTE(x[2], 1)] ^ saes_table[2][BYTE(x[3], 2)] ^ saes_table[3][BYTE(x[0], 3)]; - x[1] ^= k[1]; - k[2] ^= saes_table[0][BYTE(x[2], 0)] ^ saes_table[1][BYTE(x[3], 1)] ^ saes_table[2][BYTE(x[0], 2)] ^ saes_table[3][BYTE(x[1], 3)]; - x[2] ^= k[2]; - k[3] ^= saes_table[0][BYTE(x[3], 0)] ^ saes_table[1][BYTE(x[0], 1)] ^ saes_table[2][BYTE(x[1], 2)] ^ saes_table[3][BYTE(x[2], 3)]; - - cx1 = _mm_load_si128((__m128i*)k); - - const __m128i& key2 = _mm_set_epi64x(ah2, al2); - - _mm_store_si128((__m128i*)k, key2); - cx2 = _mm_xor_si128(cx2, _mm_cmpeq_epi32(_mm_setzero_si128(), _mm_setzero_si128())); - _mm_store_si128((__m128i*)x, cx2); - - k[0] ^= saes_table[0][BYTE(x[0], 0)] ^ saes_table[1][BYTE(x[1], 1)] ^ saes_table[2][BYTE(x[2], 2)] ^ saes_table[3][BYTE(x[3], 3)]; - x[0] ^= k[0]; - k[1] ^= saes_table[0][BYTE(x[1], 0)] ^ saes_table[1][BYTE(x[2], 1)] ^ saes_table[2][BYTE(x[3], 2)] ^ saes_table[3][BYTE(x[0], 3)]; - x[1] ^= k[1]; - k[2] ^= saes_table[0][BYTE(x[2], 0)] ^ saes_table[1][BYTE(x[3], 1)] ^ saes_table[2][BYTE(x[0], 2)] ^ saes_table[3][BYTE(x[1], 3)]; - x[2] ^= k[2]; - k[3] ^= saes_table[0][BYTE(x[3], 0)] ^ saes_table[1][BYTE(x[0], 1)] ^ saes_table[2][BYTE(x[1], 2)] ^ saes_table[3][BYTE(x[2], 3)]; - - cx2 = _mm_load_si128((__m128i*)k); - - _mm_store_si128((__m128i*) &l0[idx0 & MASK], _mm_xor_si128(bx0, cx0)); - _mm_store_si128((__m128i*) &l1[idx1 & MASK], _mm_xor_si128(bx1, cx1)); - _mm_store_si128((__m128i*) &l2[idx2 & MASK], _mm_xor_si128(bx2, cx2)); - - static const uint32_t table = 0x75310; - uint8_t tmp = reinterpret_cast(&l0[idx0 & MASK])[11]; - uint8_t index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*)(&l0[idx0 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - tmp = reinterpret_cast(&l1[idx1 & MASK])[11]; - index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*)(&l1[idx1 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - tmp = reinterpret_cast(&l2[idx2 & MASK])[11]; - index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*)(&l2[idx2 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - - idx0 = EXTRACT64(cx0); - idx1 = EXTRACT64(cx1); - idx2 = EXTRACT64(cx2); - - bx0 = cx0; - bx1 = cx1; - bx2 = cx2; - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & MASK])[0]; - ch = ((uint64_t*) &l0[idx0 & MASK])[1]; - lo = __umul128(idx0, cl, &hi); - - al0 += hi; - ah0 += lo; - - ah0 ^= tweak1_2_0; - ((uint64_t*) &l0[idx0 & MASK])[0] = al0; - ((uint64_t*) &l0[idx0 & MASK])[1] = ah0; - ah0 ^= tweak1_2_0; - - ((uint64_t*)&l0[idx0 & MASK])[1] ^= ((uint64_t*)&l0[idx0 & MASK])[0]; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - int64_t n = ((int64_t*)&l0[idx0 & MASK])[0]; - int32_t d = ((int32_t*)&l0[idx0 & MASK])[2]; - int64_t q = n / (d | 0x5); - - ((int64_t*)&l0[idx0 & MASK])[0] = n ^ q; - idx0 = d ^ q; - - - cl = ((uint64_t*) &l1[idx1 & MASK])[0]; - ch = ((uint64_t*) &l1[idx1 & MASK])[1]; - lo = __umul128(idx1, cl, &hi); - - al1 += hi; - ah1 += lo; - - ah1 ^= tweak1_2_1; - ((uint64_t*) &l1[idx1 & MASK])[0] = al1; - ((uint64_t*) &l1[idx1 & MASK])[1] = ah1; - ah1 ^= tweak1_2_1; - - ((uint64_t*)&l1[idx1 & MASK])[1] ^= ((uint64_t*)&l1[idx1 & MASK])[0]; - - ah1 ^= ch; - al1 ^= cl; - idx1 = al1; - - n = ((int64_t*)&l1[idx1 & MASK])[0]; - d = ((int32_t*)&l1[idx1 & MASK])[2]; - q = n / (d | 0x5); - - ((int64_t*)&l1[idx1 & MASK])[0] = n ^ q; - idx1 = d ^ q; - - - cl = ((uint64_t*) &l2[idx2 & MASK])[0]; - ch = ((uint64_t*) &l2[idx2 & MASK])[1]; - lo = __umul128(idx2, cl, &hi); - - al2 += hi; - ah2 += lo; - - ah2 ^= tweak1_2_2; - ((uint64_t*) &l2[idx2 & MASK])[0] = al2; - ((uint64_t*) &l2[idx2 & MASK])[1] = ah2; - ah2 ^= tweak1_2_2; - - ((uint64_t*)&l2[idx2 & MASK])[1] ^= ((uint64_t*)&l2[idx2 & MASK])[0]; - - ah2 ^= ch; - al2 ^= cl; - idx2 = al2; - - n = ((int64_t*)&l2[idx2 & MASK])[0]; - d = ((int32_t*)&l2[idx2 & MASK])[2]; - q = n / (d | 0x5); - - ((int64_t*)&l2[idx2 & MASK])[0] = n ^ q; - idx2 = d ^ q; - } -#undef BYTE - - cn_implode_scratchpad_heavy((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad_heavy((__m128i*) l1, (__m128i*) h1); - cn_implode_scratchpad_heavy((__m128i*) l2, (__m128i*) h2); - - keccakf(h0, 24); - keccakf(h1, 24); - keccakf(h2, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - extra_hashes[scratchPad[1]->state[0] & 3](scratchPad[1]->state, 200, output + 32); - extra_hashes[scratchPad[2]->state[0] & 3](scratchPad[2]->state, 200, output + 64); - } -}; - -template -class CryptoNightMultiHash -{ -public: - inline static void hash(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - keccak((const uint8_t*) input, (int) size, scratchPad[0]->state, 200); - keccak((const uint8_t*) input + size, (int) size, scratchPad[1]->state, 200); - keccak((const uint8_t*) input + 2 * size, (int) size, scratchPad[2]->state, 200); - keccak((const uint8_t*) input + 3 * size, (int) size, scratchPad[3]->state, 200); - - const uint8_t* l0 = scratchPad[0]->memory; - const uint8_t* l1 = scratchPad[1]->memory; - const uint8_t* l2 = scratchPad[2]->memory; - const uint8_t* l3 = scratchPad[3]->memory; - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - uint64_t* h1 = reinterpret_cast(scratchPad[1]->state); - uint64_t* h2 = reinterpret_cast(scratchPad[2]->state); - uint64_t* h3 = reinterpret_cast(scratchPad[3]->state); - - cn_explode_scratchpad((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad((__m128i*) h1, (__m128i*) l1); - cn_explode_scratchpad((__m128i*) h2, (__m128i*) l2); - cn_explode_scratchpad((__m128i*) h3, (__m128i*) l3); - - uint64_t al0 = h0[0] ^h0[4]; - uint64_t al1 = h1[0] ^h1[4]; - uint64_t al2 = h2[0] ^h2[4]; - uint64_t al3 = h3[0] ^h3[4]; - uint64_t ah0 = h0[1] ^h0[5]; - uint64_t ah1 = h1[1] ^h1[5]; - uint64_t ah2 = h2[1] ^h2[5]; - uint64_t ah3 = h3[1] ^h3[5]; - - __m128i bx0 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx1 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - __m128i bx2 = _mm_set_epi64x(h2[3] ^ h2[7], h2[2] ^ h2[6]); - __m128i bx3 = _mm_set_epi64x(h3[3] ^ h3[7], h3[2] ^ h3[6]); - - __m128 conc_var0; - __m128 conc_var1; - __m128 conc_var2; - __m128 conc_var3; - - if (VARIANT == POW_CONCEAL) { - SET_ROUNDING_MODE_NEAREST() - conc_var0 = _mm_setzero_ps(); - conc_var1 = _mm_setzero_ps(); - conc_var2 = _mm_setzero_ps(); - conc_var3 = _mm_setzero_ps(); - } - - uint64_t idx0 = h0[0] ^h0[4]; - uint64_t idx1 = h1[0] ^h1[4]; - uint64_t idx2 = h2[0] ^h2[4]; - uint64_t idx3 = h3[0] ^h3[4]; - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx0; - __m128i cx1; - __m128i cx2; - __m128i cx3; - - if (SOFT_AES) { - if (VARIANT == POW_CONCEAL) { - cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - cx1 = _mm_load_si128((__m128i*) &l1[idx1 & MASK]); - cx2 = _mm_load_si128((__m128i*) &l2[idx2 & MASK]); - cx3 = _mm_load_si128((__m128i*) &l3[idx3 & MASK]); - - cryptonight_conceal_tweak(cx0, conc_var0); - cryptonight_conceal_tweak(cx1, conc_var1); - cryptonight_conceal_tweak(cx2, conc_var2); - cryptonight_conceal_tweak(cx3, conc_var3); - - cx0 = soft_aesenc(cx0, _mm_set_epi64x(ah0, al0)); - cx1 = soft_aesenc(cx1, _mm_set_epi64x(ah1, al1)); - cx2 = soft_aesenc(cx2, _mm_set_epi64x(ah2, al2)); - cx3 = soft_aesenc(cx3, _mm_set_epi64x(ah3, al3)); - } else { - cx0 = soft_aesenc((uint32_t*)&l0[idx0 & MASK], _mm_set_epi64x(ah0, al0)); - cx1 = soft_aesenc((uint32_t*)&l1[idx1 & MASK], _mm_set_epi64x(ah1, al1)); - cx2 = soft_aesenc((uint32_t*)&l2[idx2 & MASK], _mm_set_epi64x(ah2, al2)); - cx3 = soft_aesenc((uint32_t*)&l3[idx3 & MASK], _mm_set_epi64x(ah3, al3)); - } - } else { - cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - cx1 = _mm_load_si128((__m128i*) &l1[idx1 & MASK]); - cx2 = _mm_load_si128((__m128i*) &l2[idx2 & MASK]); - cx3 = _mm_load_si128((__m128i*) &l3[idx3 & MASK]); - - if (VARIANT == POW_CONCEAL) { - cryptonight_conceal_tweak(cx0, conc_var0); - cryptonight_conceal_tweak(cx1, conc_var1); - cryptonight_conceal_tweak(cx2, conc_var2); - cryptonight_conceal_tweak(cx3, conc_var3); - } - - cx0 = _mm_aesenc_si128(cx0, _mm_set_epi64x(ah0, al0)); - cx1 = _mm_aesenc_si128(cx1, _mm_set_epi64x(ah1, al1)); - cx2 = _mm_aesenc_si128(cx2, _mm_set_epi64x(ah2, al2)); - cx3 = _mm_aesenc_si128(cx3, _mm_set_epi64x(ah3, al3)); - } - - _mm_store_si128((__m128i*) &l0[idx0 & MASK], _mm_xor_si128(bx0, cx0)); - _mm_store_si128((__m128i*) &l1[idx1 & MASK], _mm_xor_si128(bx1, cx1)); - _mm_store_si128((__m128i*) &l2[idx2 & MASK], _mm_xor_si128(bx2, cx2)); - _mm_store_si128((__m128i*) &l3[idx3 & MASK], _mm_xor_si128(bx3, cx3)); - - idx0 = EXTRACT64(cx0); - idx1 = EXTRACT64(cx1); - idx2 = EXTRACT64(cx2); - idx3 = EXTRACT64(cx3); - - bx0 = cx0; - bx1 = cx1; - bx2 = cx2; - bx3 = cx3; - - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & MASK])[0]; - ch = ((uint64_t*) &l0[idx0 & MASK])[1]; - lo = __umul128(idx0, cl, &hi); - - al0 += hi; - ah0 += lo; - - ((uint64_t*) &l0[idx0 & MASK])[0] = al0; - ((uint64_t*) &l0[idx0 & MASK])[1] = ah0; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - - cl = ((uint64_t*) &l1[idx1 & MASK])[0]; - ch = ((uint64_t*) &l1[idx1 & MASK])[1]; - lo = __umul128(idx1, cl, &hi); - - al1 += hi; - ah1 += lo; - - ((uint64_t*) &l1[idx1 & MASK])[0] = al1; - ((uint64_t*) &l1[idx1 & MASK])[1] = ah1; - - ah1 ^= ch; - al1 ^= cl; - idx1 = al1; - - - cl = ((uint64_t*) &l2[idx2 & MASK])[0]; - ch = ((uint64_t*) &l2[idx2 & MASK])[1]; - lo = __umul128(idx2, cl, &hi); - - al2 += hi; - ah2 += lo; - - ((uint64_t*) &l2[idx2 & MASK])[0] = al2; - ((uint64_t*) &l2[idx2 & MASK])[1] = ah2; - - ah2 ^= ch; - al2 ^= cl; - idx2 = al2; - - - cl = ((uint64_t*) &l3[idx3 & MASK])[0]; - ch = ((uint64_t*) &l3[idx3 & MASK])[1]; - lo = __umul128(idx3, cl, &hi); - - al3 += hi; - ah3 += lo; - - ((uint64_t*) &l3[idx3 & MASK])[0] = al3; - ((uint64_t*) &l3[idx3 & MASK])[1] = ah3; - - ah3 ^= ch; - al3 ^= cl; - idx3 = al3; - } - - cn_implode_scratchpad((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad((__m128i*) l1, (__m128i*) h1); - cn_implode_scratchpad((__m128i*) l2, (__m128i*) h2); - cn_implode_scratchpad((__m128i*) l3, (__m128i*) h3); - - keccakf(h0, 24); - keccakf(h1, 24); - keccakf(h2, 24); - keccakf(h3, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - extra_hashes[scratchPad[1]->state[0] & 3](scratchPad[1]->state, 200, output + 32); - extra_hashes[scratchPad[2]->state[0] & 3](scratchPad[2]->state, 200, output + 64); - extra_hashes[scratchPad[3]->state[0] & 3](scratchPad[3]->state, 200, output + 96); - } - - inline static void hashPowV2(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - keccak((const uint8_t*) input, (int) size, scratchPad[0]->state, 200); - keccak((const uint8_t*) input + size, (int) size, scratchPad[1]->state, 200); - keccak((const uint8_t*) input + 2 * size, (int) size, scratchPad[2]->state, 200); - keccak((const uint8_t*) input + 3 * size, (int) size, scratchPad[3]->state, 200); - - uint64_t tweak1_2_0 = (*reinterpret_cast(reinterpret_cast(input) + 35) ^ - *(reinterpret_cast(scratchPad[0]->state) + 24)); - uint64_t tweak1_2_1 = (*reinterpret_cast(reinterpret_cast(input) + 35 + size) ^ - *(reinterpret_cast(scratchPad[1]->state) + 24)); - uint64_t tweak1_2_2 = (*reinterpret_cast(reinterpret_cast(input) + 35 + 2 * size) ^ - *(reinterpret_cast(scratchPad[2]->state) + 24)); - uint64_t tweak1_2_3 = (*reinterpret_cast(reinterpret_cast(input) + 35 + 3 * size) ^ - *(reinterpret_cast(scratchPad[3]->state) + 24)); - - const uint8_t* l0 = scratchPad[0]->memory; - const uint8_t* l1 = scratchPad[1]->memory; - const uint8_t* l2 = scratchPad[2]->memory; - const uint8_t* l3 = scratchPad[3]->memory; - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - uint64_t* h1 = reinterpret_cast(scratchPad[1]->state); - uint64_t* h2 = reinterpret_cast(scratchPad[2]->state); - uint64_t* h3 = reinterpret_cast(scratchPad[3]->state); - - cn_explode_scratchpad((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad((__m128i*) h1, (__m128i*) l1); - cn_explode_scratchpad((__m128i*) h2, (__m128i*) l2); - cn_explode_scratchpad((__m128i*) h3, (__m128i*) l3); - - uint64_t al0 = h0[0] ^h0[4]; - uint64_t al1 = h1[0] ^h1[4]; - uint64_t al2 = h2[0] ^h2[4]; - uint64_t al3 = h3[0] ^h3[4]; - uint64_t ah0 = h0[1] ^h0[5]; - uint64_t ah1 = h1[1] ^h1[5]; - uint64_t ah2 = h2[1] ^h2[5]; - uint64_t ah3 = h3[1] ^h3[5]; - - __m128i bx0 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx1 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - __m128i bx2 = _mm_set_epi64x(h2[3] ^ h2[7], h2[2] ^ h2[6]); - __m128i bx3 = _mm_set_epi64x(h3[3] ^ h3[7], h3[2] ^ h3[6]); - - uint64_t idx0 = h0[0] ^h0[4]; - uint64_t idx1 = h1[0] ^h1[4]; - uint64_t idx2 = h2[0] ^h2[4]; - uint64_t idx3 = h3[0] ^h3[4]; - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx0; - __m128i cx1; - __m128i cx2; - __m128i cx3; - - if (SOFT_AES) { - cx0 = soft_aesenc((uint32_t*)&l0[idx0 & MASK], _mm_set_epi64x(ah0, al0)); - cx1 = soft_aesenc((uint32_t*)&l1[idx1 & MASK], _mm_set_epi64x(ah1, al1)); - cx2 = soft_aesenc((uint32_t*)&l2[idx2 & MASK], _mm_set_epi64x(ah2, al2)); - cx3 = soft_aesenc((uint32_t*)&l3[idx3 & MASK], _mm_set_epi64x(ah3, al3)); - } else { - cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - cx1 = _mm_load_si128((__m128i*) &l1[idx1 & MASK]); - cx2 = _mm_load_si128((__m128i*) &l2[idx2 & MASK]); - cx3 = _mm_load_si128((__m128i*) &l3[idx3 & MASK]); - - cx0 = _mm_aesenc_si128(cx0, _mm_set_epi64x(ah0, al0)); - cx1 = _mm_aesenc_si128(cx1, _mm_set_epi64x(ah1, al1)); - cx2 = _mm_aesenc_si128(cx2, _mm_set_epi64x(ah2, al2)); - cx3 = _mm_aesenc_si128(cx3, _mm_set_epi64x(ah3, al3)); - } - - _mm_store_si128((__m128i*) &l0[idx0 & MASK], _mm_xor_si128(bx0, cx0)); - _mm_store_si128((__m128i*) &l1[idx1 & MASK], _mm_xor_si128(bx1, cx1)); - _mm_store_si128((__m128i*) &l2[idx2 & MASK], _mm_xor_si128(bx2, cx2)); - _mm_store_si128((__m128i*) &l3[idx3 & MASK], _mm_xor_si128(bx3, cx3)); - - static const uint32_t table = 0x75310; - uint8_t tmp = reinterpret_cast(&l0[idx0 & MASK])[11]; - uint8_t index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*)(&l0[idx0 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - tmp = reinterpret_cast(&l1[idx1 & MASK])[11]; - index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*)(&l1[idx1 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - tmp = reinterpret_cast(&l2[idx2 & MASK])[11]; - index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*)(&l2[idx2 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - tmp = reinterpret_cast(&l3[idx3 & MASK])[11]; - index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*)(&l3[idx3 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - - idx0 = EXTRACT64(cx0); - idx1 = EXTRACT64(cx1); - idx2 = EXTRACT64(cx2); - idx3 = EXTRACT64(cx3); - - bx0 = cx0; - bx1 = cx1; - bx2 = cx2; - bx3 = cx3; - - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & MASK])[0]; - ch = ((uint64_t*) &l0[idx0 & MASK])[1]; - lo = __umul128(idx0, cl, &hi); - - al0 += hi; - ah0 += lo; - - ah0 ^= tweak1_2_0; - ((uint64_t*) &l0[idx0 & MASK])[0] = al0; - ((uint64_t*) &l0[idx0 & MASK])[1] = ah0; - ah0 ^= tweak1_2_0; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - - cl = ((uint64_t*) &l1[idx1 & MASK])[0]; - ch = ((uint64_t*) &l1[idx1 & MASK])[1]; - lo = __umul128(idx1, cl, &hi); - - al1 += hi; - ah1 += lo; - - ah1 ^= tweak1_2_1; - ((uint64_t*) &l1[idx1 & MASK])[0] = al1; - ((uint64_t*) &l1[idx1 & MASK])[1] = ah1; - ah1 ^= tweak1_2_1; - - ah1 ^= ch; - al1 ^= cl; - idx1 = al1; - - - cl = ((uint64_t*) &l2[idx2 & MASK])[0]; - ch = ((uint64_t*) &l2[idx2 & MASK])[1]; - lo = __umul128(idx2, cl, &hi); - - al2 += hi; - ah2 += lo; - - ah2 ^= tweak1_2_2; - ((uint64_t*) &l2[idx2 & MASK])[0] = al2; - ((uint64_t*) &l2[idx2 & MASK])[1] = ah2; - ah2 ^= tweak1_2_2; - - ah2 ^= ch; - al2 ^= cl; - idx2 = al2; - - - cl = ((uint64_t*) &l3[idx3 & MASK])[0]; - ch = ((uint64_t*) &l3[idx3 & MASK])[1]; - lo = __umul128(idx3, cl, &hi); - - al3 += hi; - ah3 += lo; - - ah3 ^= tweak1_2_3; - ((uint64_t*) &l3[idx3 & MASK])[0] = al3; - ((uint64_t*) &l3[idx3 & MASK])[1] = ah3; - ah3 ^= tweak1_2_3; - - ah3 ^= ch; - al3 ^= cl; - idx3 = al3; - } - - cn_implode_scratchpad((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad((__m128i*) l1, (__m128i*) h1); - cn_implode_scratchpad((__m128i*) l2, (__m128i*) h2); - cn_implode_scratchpad((__m128i*) l3, (__m128i*) h3); - - keccakf(h0, 24); - keccakf(h1, 24); - keccakf(h2, 24); - keccakf(h3, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - extra_hashes[scratchPad[1]->state[0] & 3](scratchPad[1]->state, 200, output + 32); - extra_hashes[scratchPad[2]->state[0] & 3](scratchPad[2]->state, 200, output + 64); - extra_hashes[scratchPad[3]->state[0] & 3](scratchPad[3]->state, 200, output + 96); - } - - inline static void hashPowV2_asm(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad, - AsmOptimization asmOptimization) - { - // not supported - } - - // quadruple - inline static void hashPowV3(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - keccak((const uint8_t*) input, (int) size, scratchPad[0]->state, 200); - keccak((const uint8_t*) input + size, (int) size, scratchPad[1]->state, 200); - keccak((const uint8_t*) input + 2 * size, (int) size, scratchPad[2]->state, 200); - keccak((const uint8_t*) input + 3 * size, (int) size, scratchPad[3]->state, 200); - - const uint8_t* l0 = scratchPad[0]->memory; - const uint8_t* l1 = scratchPad[1]->memory; - const uint8_t* l2 = scratchPad[2]->memory; - const uint8_t* l3 = scratchPad[3]->memory; - - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - uint64_t* h1 = reinterpret_cast(scratchPad[1]->state); - uint64_t* h2 = reinterpret_cast(scratchPad[2]->state); - uint64_t* h3 = reinterpret_cast(scratchPad[3]->state); - - cn_explode_scratchpad((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad((__m128i*) h1, (__m128i*) l1); - cn_explode_scratchpad((__m128i*) h2, (__m128i*) l2); - cn_explode_scratchpad((__m128i*) h3, (__m128i*) l3); - - uint64_t al0 = h0[0] ^h0[4]; - uint64_t al1 = h1[0] ^h1[4]; - uint64_t al2 = h2[0] ^h2[4]; - uint64_t al3 = h3[0] ^h3[4]; - - uint64_t ah0 = h0[1] ^h0[5]; - uint64_t ah1 = h1[1] ^h1[5]; - uint64_t ah2 = h2[1] ^h2[5]; - uint64_t ah3 = h3[1] ^h3[5]; - - __m128i bx00 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx01 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - __m128i bx02 = _mm_set_epi64x(h2[3] ^ h2[7], h2[2] ^ h2[6]); - __m128i bx03 = _mm_set_epi64x(h3[3] ^ h3[7], h3[2] ^ h3[6]); - - __m128i bx10 = _mm_set_epi64x(h0[9] ^ h0[11], h0[8] ^ h0[10]); - __m128i bx11 = _mm_set_epi64x(h1[9] ^ h1[11], h1[8] ^ h1[10]); - __m128i bx12 = _mm_set_epi64x(h2[9] ^ h2[11], h2[8] ^ h2[10]); - __m128i bx13 = _mm_set_epi64x(h3[9] ^ h3[11], h3[8] ^ h3[10]); - - uint64_t idx0 = h0[0] ^h0[4]; - uint64_t idx1 = h1[0] ^h1[4]; - uint64_t idx2 = h2[0] ^h2[4]; - uint64_t idx3 = h3[0] ^h3[4]; - - SET_ROUNDING_MODE_UP(); - - __m128i division_result_xmm0 = _mm_cvtsi64_si128(h0[12]); - __m128i division_result_xmm1 = _mm_cvtsi64_si128(h1[12]); - __m128i division_result_xmm2 = _mm_cvtsi64_si128(h2[12]); - __m128i division_result_xmm3 = _mm_cvtsi64_si128(h3[12]); - - uint64_t sqrt_result0 = h0[13]; - uint64_t sqrt_result1 = h1[13]; - uint64_t sqrt_result2 = h2[13]; - uint64_t sqrt_result3 = h3[13]; - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx0; - __m128i cx1; - __m128i cx2; - __m128i cx3; - - const __m128i ax0 = _mm_set_epi64x(ah0, al0); - const __m128i ax1 = _mm_set_epi64x(ah1, al1); - const __m128i ax2 = _mm_set_epi64x(ah2, al2); - const __m128i ax3 = _mm_set_epi64x(ah3, al3); - - if (SOFT_AES) { - cx0 = soft_aesenc((uint32_t*)&l0[idx0 & MASK], ax0); - cx1 = soft_aesenc((uint32_t*)&l1[idx1 & MASK], ax1); - cx2 = soft_aesenc((uint32_t*)&l2[idx2 & MASK], ax2); - cx3 = soft_aesenc((uint32_t*)&l3[idx3 & MASK], ax3); - } else { - cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - cx1 = _mm_load_si128((__m128i*) &l1[idx1 & MASK]); - cx2 = _mm_load_si128((__m128i*) &l2[idx2 & MASK]); - cx3 = _mm_load_si128((__m128i*) &l3[idx3 & MASK]); - - cx0 = _mm_aesenc_si128(cx0, ax0); - cx1 = _mm_aesenc_si128(cx1, ax1); - cx2 = _mm_aesenc_si128(cx2, ax2); - cx3 = _mm_aesenc_si128(cx3, ax3); - } - - SHUFFLE_PHASE_1(l0, (idx0&MASK), bx00, bx10, ax0, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - SHUFFLE_PHASE_1(l1, (idx1&MASK), bx01, bx11, ax1, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - SHUFFLE_PHASE_1(l2, (idx2&MASK), bx02, bx12, ax2, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - SHUFFLE_PHASE_1(l3, (idx3&MASK), bx03, bx13, ax3, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - - _mm_store_si128((__m128i*) &l0[idx0 & MASK], _mm_xor_si128(bx00, cx0)); - _mm_store_si128((__m128i*) &l1[idx1 & MASK], _mm_xor_si128(bx01, cx1)); - _mm_store_si128((__m128i*) &l2[idx2 & MASK], _mm_xor_si128(bx02, cx2)); - _mm_store_si128((__m128i*) &l3[idx3 & MASK], _mm_xor_si128(bx03, cx3)); - - idx0 = EXTRACT64(cx0); - idx1 = EXTRACT64(cx1); - idx2 = EXTRACT64(cx2); - idx3 = EXTRACT64(cx3); - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & MASK])[0]; - ch = ((uint64_t*) &l0[idx0 & MASK])[1]; - - INTEGER_MATH_V2(0, cl, cx0); - - lo = __umul128(idx0, cl, &hi); - - SHUFFLE_PHASE_2(l0, (idx0&MASK), bx00, bx10, ax0, lo, hi, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - - al0 += hi; - ah0 += lo; - - ((uint64_t*) &l0[idx0 & MASK])[0] = al0; - ((uint64_t*) &l0[idx0 & MASK])[1] = ah0; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - bx10 = bx00; - bx00 = cx0; - - - cl = ((uint64_t*) &l1[idx1 & MASK])[0]; - ch = ((uint64_t*) &l1[idx1 & MASK])[1]; - - INTEGER_MATH_V2(1, cl, cx1); - - lo = __umul128(idx1, cl, &hi); - - SHUFFLE_PHASE_2(l1, (idx1&MASK), bx01, bx11, ax1, lo, hi, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - - al1 += hi; - ah1 += lo; - - ((uint64_t*) &l1[idx1 & MASK])[0] = al1; - ((uint64_t*) &l1[idx1 & MASK])[1] = ah1; - - ah1 ^= ch; - al1 ^= cl; - idx1 = al1; - - bx11 = bx01; - bx01 = cx1; - - - cl = ((uint64_t*) &l2[idx2 & MASK])[0]; - ch = ((uint64_t*) &l2[idx2 & MASK])[1]; - - INTEGER_MATH_V2(2, cl, cx2); - - lo = __umul128(idx2, cl, &hi); - - SHUFFLE_PHASE_2(l2, (idx2&MASK), bx02, bx12, ax2, lo, hi, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - - al2 += hi; - ah2 += lo; - - ((uint64_t*) &l2[idx2 & MASK])[0] = al2; - ((uint64_t*) &l2[idx2 & MASK])[1] = ah2; - - ah2 ^= ch; - al2 ^= cl; - idx2 = al2; - - bx12 = bx02; - bx02 = cx2; - - - cl = ((uint64_t*) &l3[idx3 & MASK])[0]; - ch = ((uint64_t*) &l3[idx3 & MASK])[1]; - - INTEGER_MATH_V2(3, cl, cx3); - - lo = __umul128(idx3, cl, &hi); - - SHUFFLE_PHASE_2(l3, (idx3&MASK), bx03, bx13, ax3, lo, hi, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - - al3 += hi; - ah3 += lo; - - ((uint64_t*) &l3[idx3 & MASK])[0] = al3; - ((uint64_t*) &l3[idx3 & MASK])[1] = ah3; - - ah3 ^= ch; - al3 ^= cl; - idx3 = al3; - - bx13 = bx03; - bx03 = cx3; - } - - cn_implode_scratchpad((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad((__m128i*) l1, (__m128i*) h1); - cn_implode_scratchpad((__m128i*) l2, (__m128i*) h2); - cn_implode_scratchpad((__m128i*) l3, (__m128i*) h3); - - keccakf(h0, 24); - keccakf(h1, 24); - keccakf(h2, 24); - keccakf(h3, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - extra_hashes[scratchPad[1]->state[0] & 3](scratchPad[1]->state, 200, output + 32); - extra_hashes[scratchPad[2]->state[0] & 3](scratchPad[2]->state, 200, output + 64); - extra_hashes[scratchPad[3]->state[0] & 3](scratchPad[3]->state, 200, output + 96); - } - - inline static void hashPowV3_asm(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad, - AsmOptimization asmOptimization) - { - // not supported - } - - // quadruple - inline static void hashPowV4(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad, - uint64_t height) - { - keccak((const uint8_t*) input, (int) size, scratchPad[0]->state, 200); - keccak((const uint8_t*) input + size, (int) size, scratchPad[1]->state, 200); - keccak((const uint8_t*) input + 2 * size, (int) size, scratchPad[2]->state, 200); - keccak((const uint8_t*) input + 3 * size, (int) size, scratchPad[3]->state, 200); - - const uint8_t* l0 = scratchPad[0]->memory; - const uint8_t* l1 = scratchPad[1]->memory; - const uint8_t* l2 = scratchPad[2]->memory; - const uint8_t* l3 = scratchPad[3]->memory; - - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - uint64_t* h1 = reinterpret_cast(scratchPad[1]->state); - uint64_t* h2 = reinterpret_cast(scratchPad[2]->state); - uint64_t* h3 = reinterpret_cast(scratchPad[3]->state); - - cn_explode_scratchpad((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad((__m128i*) h1, (__m128i*) l1); - cn_explode_scratchpad((__m128i*) h2, (__m128i*) l2); - cn_explode_scratchpad((__m128i*) h3, (__m128i*) l3); - - uint64_t al0 = h0[0] ^h0[4]; - uint64_t al1 = h1[0] ^h1[4]; - uint64_t al2 = h2[0] ^h2[4]; - uint64_t al3 = h3[0] ^h3[4]; - - uint64_t ah0 = h0[1] ^h0[5]; - uint64_t ah1 = h1[1] ^h1[5]; - uint64_t ah2 = h2[1] ^h2[5]; - uint64_t ah3 = h3[1] ^h3[5]; - - __m128i bx00 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx01 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - __m128i bx02 = _mm_set_epi64x(h2[3] ^ h2[7], h2[2] ^ h2[6]); - __m128i bx03 = _mm_set_epi64x(h3[3] ^ h3[7], h3[2] ^ h3[6]); - - __m128i bx10 = _mm_set_epi64x(h0[9] ^ h0[11], h0[8] ^ h0[10]); - __m128i bx11 = _mm_set_epi64x(h1[9] ^ h1[11], h1[8] ^ h1[10]); - __m128i bx12 = _mm_set_epi64x(h2[9] ^ h2[11], h2[8] ^ h2[10]); - __m128i bx13 = _mm_set_epi64x(h3[9] ^ h3[11], h3[8] ^ h3[10]); - - uint64_t idx0 = h0[0] ^h0[4]; - uint64_t idx1 = h1[0] ^h1[4]; - uint64_t idx2 = h2[0] ^h2[4]; - uint64_t idx3 = h3[0] ^h3[4]; - - SET_ROUNDING_MODE_UP(); - - VARIANT4_RANDOM_MATH_INIT(0, h0) - VARIANT4_RANDOM_MATH_INIT(1, h1) - VARIANT4_RANDOM_MATH_INIT(2, h2) - VARIANT4_RANDOM_MATH_INIT(3, h3) - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx0; - __m128i cx1; - __m128i cx2; - __m128i cx3; - - const __m128i ax0 = _mm_set_epi64x(ah0, al0); - const __m128i ax1 = _mm_set_epi64x(ah1, al1); - const __m128i ax2 = _mm_set_epi64x(ah2, al2); - const __m128i ax3 = _mm_set_epi64x(ah3, al3); - - if (SOFT_AES) { - cx0 = soft_aesenc((uint32_t*)&l0[idx0 & MASK], ax0); - cx1 = soft_aesenc((uint32_t*)&l1[idx1 & MASK], ax1); - cx2 = soft_aesenc((uint32_t*)&l2[idx2 & MASK], ax2); - cx3 = soft_aesenc((uint32_t*)&l3[idx3 & MASK], ax3); - } else { - cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - cx1 = _mm_load_si128((__m128i*) &l1[idx1 & MASK]); - cx2 = _mm_load_si128((__m128i*) &l2[idx2 & MASK]); - cx3 = _mm_load_si128((__m128i*) &l3[idx3 & MASK]); - - cx0 = _mm_aesenc_si128(cx0, ax0); - cx1 = _mm_aesenc_si128(cx1, ax1); - cx2 = _mm_aesenc_si128(cx2, ax2); - cx3 = _mm_aesenc_si128(cx3, ax3); - } - - SHUFFLE_V4(l0, (idx0&MASK), bx00, bx10, ax0, cx0) - SHUFFLE_V4(l1, (idx1&MASK), bx01, bx11, ax1, cx1) - SHUFFLE_V4(l2, (idx2&MASK), bx02, bx12, ax2, cx2) - SHUFFLE_V4(l3, (idx3&MASK), bx03, bx13, ax3, cx3) - - _mm_store_si128((__m128i*) &l0[idx0 & MASK], _mm_xor_si128(bx00, cx0)); - _mm_store_si128((__m128i*) &l1[idx1 & MASK], _mm_xor_si128(bx01, cx1)); - _mm_store_si128((__m128i*) &l2[idx2 & MASK], _mm_xor_si128(bx02, cx2)); - _mm_store_si128((__m128i*) &l3[idx3 & MASK], _mm_xor_si128(bx03, cx3)); - - idx0 = EXTRACT64(cx0); - idx1 = EXTRACT64(cx1); - idx2 = EXTRACT64(cx2); - idx3 = EXTRACT64(cx3); - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & MASK])[0]; - ch = ((uint64_t*) &l0[idx0 & MASK])[1]; - - VARIANT4_RANDOM_MATH(0, al0, ah0, cl, bx00, bx10); - - if (VARIANT == POW_V4) { - al0 ^= r0[2] | ((uint64_t)(r0[3]) << 32); - ah0 ^= r0[0] | ((uint64_t)(r0[1]) << 32); - } - - lo = __umul128(idx0, cl, &hi); - - SHUFFLE_V4(l0, (idx0&MASK), bx00, bx10, ax0, cx0); - - al0 += hi; - ah0 += lo; - - ((uint64_t*) &l0[idx0 & MASK])[0] = al0; - ((uint64_t*) &l0[idx0 & MASK])[1] = ah0; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - bx10 = bx00; - bx00 = cx0; - - - cl = ((uint64_t*) &l1[idx1 & MASK])[0]; - ch = ((uint64_t*) &l1[idx1 & MASK])[1]; - - VARIANT4_RANDOM_MATH(1, al1, ah1, cl, bx01, bx11); - - if (VARIANT == POW_V4) { - al1 ^= r1[2] | ((uint64_t)(r1[3]) << 32); - ah1 ^= r1[0] | ((uint64_t)(r1[1]) << 32); - } - - lo = __umul128(idx1, cl, &hi); - - SHUFFLE_V4(l1, (idx1&MASK), bx01, bx11, ax1, cx1); - - al1 += hi; - ah1 += lo; - - ((uint64_t*) &l1[idx1 & MASK])[0] = al1; - ((uint64_t*) &l1[idx1 & MASK])[1] = ah1; - - ah1 ^= ch; - al1 ^= cl; - idx1 = al1; - - bx11 = bx01; - bx01 = cx1; - - - cl = ((uint64_t*) &l2[idx2 & MASK])[0]; - ch = ((uint64_t*) &l2[idx2 & MASK])[1]; - - VARIANT4_RANDOM_MATH(2, al2, ah2, cl, bx02, bx12); - - if (VARIANT == POW_V4) { - al2 ^= r2[2] | ((uint64_t)(r2[3]) << 32); - ah2 ^= r2[0] | ((uint64_t)(r2[1]) << 32); - } - - lo = __umul128(idx2, cl, &hi); - - SHUFFLE_V4(l2, (idx2&MASK), bx02, bx12, ax2, cx2); - - al2 += hi; - ah2 += lo; - - ((uint64_t*) &l2[idx2 & MASK])[0] = al2; - ((uint64_t*) &l2[idx2 & MASK])[1] = ah2; - - ah2 ^= ch; - al2 ^= cl; - idx2 = al2; - - bx12 = bx02; - bx02 = cx2; - - - cl = ((uint64_t*) &l3[idx3 & MASK])[0]; - ch = ((uint64_t*) &l3[idx3 & MASK])[1]; - - VARIANT4_RANDOM_MATH(3, al3, ah3, cl, bx03, bx13); - - if (VARIANT == POW_V4) { - al3 ^= r3[2] | ((uint64_t)(r3[3]) << 32); - ah3 ^= r3[0] | ((uint64_t)(r3[1]) << 32); - } - - lo = __umul128(idx3, cl, &hi); - - SHUFFLE_V4(l3, (idx3&MASK), bx03, bx13, ax3, cx3); - - al3 += hi; - ah3 += lo; - - ((uint64_t*) &l3[idx3 & MASK])[0] = al3; - ((uint64_t*) &l3[idx3 & MASK])[1] = ah3; - - ah3 ^= ch; - al3 ^= cl; - idx3 = al3; - - bx13 = bx03; - bx03 = cx3; - } - - cn_implode_scratchpad((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad((__m128i*) l1, (__m128i*) h1); - cn_implode_scratchpad((__m128i*) l2, (__m128i*) h2); - cn_implode_scratchpad((__m128i*) l3, (__m128i*) h3); - - keccakf(h0, 24); - keccakf(h1, 24); - keccakf(h2, 24); - keccakf(h3, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - extra_hashes[scratchPad[1]->state[0] & 3](scratchPad[1]->state, 200, output + 32); - extra_hashes[scratchPad[2]->state[0] & 3](scratchPad[2]->state, 200, output + 64); - extra_hashes[scratchPad[3]->state[0] & 3](scratchPad[3]->state, 200, output + 96); - } - - - inline static void hashPowV4_asm(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad, - uint64_t height, - AsmOptimization asmOptimization) - { - // not supported - } - - inline static void hashLiteTube(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - keccak((const uint8_t*) input, (int) size, scratchPad[0]->state, 200); - keccak((const uint8_t*) input + size, (int) size, scratchPad[1]->state, 200); - keccak((const uint8_t*) input + 2 * size, (int) size, scratchPad[2]->state, 200); - keccak((const uint8_t*) input + 3 * size, (int) size, scratchPad[3]->state, 200); - - uint64_t tweak1_2_0 = (*reinterpret_cast(reinterpret_cast(input) + 35) ^ - *(reinterpret_cast(scratchPad[0]->state) + 24)); - uint64_t tweak1_2_1 = (*reinterpret_cast(reinterpret_cast(input) + 35 + size) ^ - *(reinterpret_cast(scratchPad[1]->state) + 24)); - uint64_t tweak1_2_2 = (*reinterpret_cast(reinterpret_cast(input) + 35 + 2 * size) ^ - *(reinterpret_cast(scratchPad[2]->state) + 24)); - uint64_t tweak1_2_3 = (*reinterpret_cast(reinterpret_cast(input) + 35 + 3 * size) ^ - *(reinterpret_cast(scratchPad[3]->state) + 24)); - - const uint8_t* l0 = scratchPad[0]->memory; - const uint8_t* l1 = scratchPad[1]->memory; - const uint8_t* l2 = scratchPad[2]->memory; - const uint8_t* l3 = scratchPad[3]->memory; - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - uint64_t* h1 = reinterpret_cast(scratchPad[1]->state); - uint64_t* h2 = reinterpret_cast(scratchPad[2]->state); - uint64_t* h3 = reinterpret_cast(scratchPad[3]->state); - - cn_explode_scratchpad((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad((__m128i*) h1, (__m128i*) l1); - cn_explode_scratchpad((__m128i*) h2, (__m128i*) l2); - cn_explode_scratchpad((__m128i*) h3, (__m128i*) l3); - - uint64_t al0 = h0[0] ^h0[4]; - uint64_t al1 = h1[0] ^h1[4]; - uint64_t al2 = h2[0] ^h2[4]; - uint64_t al3 = h3[0] ^h3[4]; - uint64_t ah0 = h0[1] ^h0[5]; - uint64_t ah1 = h1[1] ^h1[5]; - uint64_t ah2 = h2[1] ^h2[5]; - uint64_t ah3 = h3[1] ^h3[5]; - - __m128i bx0 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx1 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - __m128i bx2 = _mm_set_epi64x(h2[3] ^ h2[7], h2[2] ^ h2[6]); - __m128i bx3 = _mm_set_epi64x(h3[3] ^ h3[7], h3[2] ^ h3[6]); - - uint64_t idx0 = h0[0] ^h0[4]; - uint64_t idx1 = h1[0] ^h1[4]; - uint64_t idx2 = h2[0] ^h2[4]; - uint64_t idx3 = h3[0] ^h3[4]; - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx0; - __m128i cx1; - __m128i cx2; - __m128i cx3; - - if (SOFT_AES) { - cx0 = soft_aesenc((uint32_t*)&l0[idx0 & MASK], _mm_set_epi64x(ah0, al0)); - cx1 = soft_aesenc((uint32_t*)&l1[idx1 & MASK], _mm_set_epi64x(ah1, al1)); - cx2 = soft_aesenc((uint32_t*)&l2[idx2 & MASK], _mm_set_epi64x(ah2, al2)); - cx3 = soft_aesenc((uint32_t*)&l3[idx3 & MASK], _mm_set_epi64x(ah3, al3)); - } else { - cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - cx1 = _mm_load_si128((__m128i*) &l1[idx1 & MASK]); - cx2 = _mm_load_si128((__m128i*) &l2[idx2 & MASK]); - cx3 = _mm_load_si128((__m128i*) &l3[idx3 & MASK]); - - cx0 = _mm_aesenc_si128(cx0, _mm_set_epi64x(ah0, al0)); - cx1 = _mm_aesenc_si128(cx1, _mm_set_epi64x(ah1, al1)); - cx2 = _mm_aesenc_si128(cx2, _mm_set_epi64x(ah2, al2)); - cx3 = _mm_aesenc_si128(cx3, _mm_set_epi64x(ah3, al3)); - } - - _mm_store_si128((__m128i*) &l0[idx0 & MASK], _mm_xor_si128(bx0, cx0)); - _mm_store_si128((__m128i*) &l1[idx1 & MASK], _mm_xor_si128(bx1, cx1)); - _mm_store_si128((__m128i*) &l2[idx2 & MASK], _mm_xor_si128(bx2, cx2)); - _mm_store_si128((__m128i*) &l3[idx3 & MASK], _mm_xor_si128(bx3, cx3)); - - static const uint32_t table = 0x75310; - uint8_t tmp = reinterpret_cast(&l0[idx0 & MASK])[11]; - uint8_t index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*)(&l0[idx0 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - tmp = reinterpret_cast(&l1[idx1 & MASK])[11]; - index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*)(&l1[idx1 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - tmp = reinterpret_cast(&l2[idx2 & MASK])[11]; - index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*)(&l2[idx2 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - tmp = reinterpret_cast(&l3[idx3 & MASK])[11]; - index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*)(&l3[idx3 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - - idx0 = EXTRACT64(cx0); - idx1 = EXTRACT64(cx1); - idx2 = EXTRACT64(cx2); - idx3 = EXTRACT64(cx3); - - bx0 = cx0; - bx1 = cx1; - bx2 = cx2; - bx3 = cx3; - - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & MASK])[0]; - ch = ((uint64_t*) &l0[idx0 & MASK])[1]; - lo = __umul128(idx0, cl, &hi); - - al0 += hi; - ah0 += lo; - - ah0 ^= tweak1_2_0; - ((uint64_t*) &l0[idx0 & MASK])[0] = al0; - ((uint64_t*) &l0[idx0 & MASK])[1] = ah0; - ah0 ^= tweak1_2_0; - - ((uint64_t*)&l0[idx0 & MASK])[1] ^= ((uint64_t*)&l0[idx0 & MASK])[0]; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - - cl = ((uint64_t*) &l1[idx1 & MASK])[0]; - ch = ((uint64_t*) &l1[idx1 & MASK])[1]; - lo = __umul128(idx1, cl, &hi); - - al1 += hi; - ah1 += lo; - - ah1 ^= tweak1_2_1; - ((uint64_t*) &l1[idx1 & MASK])[0] = al1; - ((uint64_t*) &l1[idx1 & MASK])[1] = ah1; - ah1 ^= tweak1_2_1; - - ((uint64_t*)&l1[idx1 & MASK])[1] ^= ((uint64_t*)&l1[idx1 & MASK])[0]; - - ah1 ^= ch; - al1 ^= cl; - idx1 = al1; - - - cl = ((uint64_t*) &l2[idx2 & MASK])[0]; - ch = ((uint64_t*) &l2[idx2 & MASK])[1]; - lo = __umul128(idx2, cl, &hi); - - al2 += hi; - ah2 += lo; - - ah2 ^= tweak1_2_2; - ((uint64_t*) &l2[idx2 & MASK])[0] = al2; - ((uint64_t*) &l2[idx2 & MASK])[1] = ah2; - ah2 ^= tweak1_2_2; - - ((uint64_t*)&l2[idx2 & MASK])[1] ^= ((uint64_t*)&l2[idx2 & MASK])[0]; - - ah2 ^= ch; - al2 ^= cl; - idx2 = al2; - - - cl = ((uint64_t*) &l3[idx3 & MASK])[0]; - ch = ((uint64_t*) &l3[idx3 & MASK])[1]; - lo = __umul128(idx3, cl, &hi); - - al3 += hi; - ah3 += lo; - - ah3 ^= tweak1_2_3; - ((uint64_t*) &l3[idx3 & MASK])[0] = al3; - ((uint64_t*) &l3[idx3 & MASK])[1] = ah3; - ah3 ^= tweak1_2_3; - - ((uint64_t*)&l3[idx3 & MASK])[1] ^= ((uint64_t*)&l3[idx3 & MASK])[0]; - - ah3 ^= ch; - al3 ^= cl; - idx3 = al3; - } - - cn_implode_scratchpad((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad((__m128i*) l1, (__m128i*) h1); - cn_implode_scratchpad((__m128i*) l2, (__m128i*) h2); - cn_implode_scratchpad((__m128i*) l3, (__m128i*) h3); - - keccakf(h0, 24); - keccakf(h1, 24); - keccakf(h2, 24); - keccakf(h3, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - extra_hashes[scratchPad[1]->state[0] & 3](scratchPad[1]->state, 200, output + 32); - extra_hashes[scratchPad[2]->state[0] & 3](scratchPad[2]->state, 200, output + 64); - extra_hashes[scratchPad[3]->state[0] & 3](scratchPad[3]->state, 200, output + 96); - } - - inline static void hashHeavy(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - // not supported - } - - inline static void hashHeavyTube(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - // not supported - } -}; - -template -class CryptoNightMultiHash -{ -public: - inline static void hash(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - keccak((const uint8_t*) input, (int) size, scratchPad[0]->state, 200); - keccak((const uint8_t*) input + size, (int) size, scratchPad[1]->state, 200); - keccak((const uint8_t*) input + 2 * size, (int) size, scratchPad[2]->state, 200); - keccak((const uint8_t*) input + 3 * size, (int) size, scratchPad[3]->state, 200); - keccak((const uint8_t*) input + 4 * size, (int) size, scratchPad[4]->state, 200); - - const uint8_t* l0 = scratchPad[0]->memory; - const uint8_t* l1 = scratchPad[1]->memory; - const uint8_t* l2 = scratchPad[2]->memory; - const uint8_t* l3 = scratchPad[3]->memory; - const uint8_t* l4 = scratchPad[4]->memory; - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - uint64_t* h1 = reinterpret_cast(scratchPad[1]->state); - uint64_t* h2 = reinterpret_cast(scratchPad[2]->state); - uint64_t* h3 = reinterpret_cast(scratchPad[3]->state); - uint64_t* h4 = reinterpret_cast(scratchPad[4]->state); - - cn_explode_scratchpad((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad((__m128i*) h1, (__m128i*) l1); - cn_explode_scratchpad((__m128i*) h2, (__m128i*) l2); - cn_explode_scratchpad((__m128i*) h3, (__m128i*) l3); - cn_explode_scratchpad((__m128i*) h4, (__m128i*) l4); - - uint64_t al0 = h0[0] ^h0[4]; - uint64_t al1 = h1[0] ^h1[4]; - uint64_t al2 = h2[0] ^h2[4]; - uint64_t al3 = h3[0] ^h3[4]; - uint64_t al4 = h4[0] ^h4[4]; - uint64_t ah0 = h0[1] ^h0[5]; - uint64_t ah1 = h1[1] ^h1[5]; - uint64_t ah2 = h2[1] ^h2[5]; - uint64_t ah3 = h3[1] ^h3[5]; - uint64_t ah4 = h4[1] ^h4[5]; - - __m128i bx0 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx1 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - __m128i bx2 = _mm_set_epi64x(h2[3] ^ h2[7], h2[2] ^ h2[6]); - __m128i bx3 = _mm_set_epi64x(h3[3] ^ h3[7], h3[2] ^ h3[6]); - __m128i bx4 = _mm_set_epi64x(h4[3] ^ h4[7], h4[2] ^ h4[6]); - - __m128 conc_var0; - __m128 conc_var1; - __m128 conc_var2; - __m128 conc_var3; - __m128 conc_var4; - - if (VARIANT == POW_CONCEAL) { - SET_ROUNDING_MODE_NEAREST() - conc_var0 = _mm_setzero_ps(); - conc_var1 = _mm_setzero_ps(); - conc_var2 = _mm_setzero_ps(); - conc_var3 = _mm_setzero_ps(); - conc_var4 = _mm_setzero_ps(); - } - - uint64_t idx0 = h0[0] ^h0[4]; - uint64_t idx1 = h1[0] ^h1[4]; - uint64_t idx2 = h2[0] ^h2[4]; - uint64_t idx3 = h3[0] ^h3[4]; - uint64_t idx4 = h4[0] ^h4[4]; - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx0; - __m128i cx1; - __m128i cx2; - __m128i cx3; - __m128i cx4; - - if (SOFT_AES) { - if (VARIANT == POW_CONCEAL) { - cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - cx1 = _mm_load_si128((__m128i*) &l1[idx1 & MASK]); - cx2 = _mm_load_si128((__m128i*) &l2[idx2 & MASK]); - cx3 = _mm_load_si128((__m128i*) &l3[idx3 & MASK]); - cx4 = _mm_load_si128((__m128i*) &l4[idx4 & MASK]); - - cryptonight_conceal_tweak(cx0, conc_var0); - cryptonight_conceal_tweak(cx1, conc_var1); - cryptonight_conceal_tweak(cx2, conc_var2); - cryptonight_conceal_tweak(cx3, conc_var3); - cryptonight_conceal_tweak(cx4, conc_var4); - - cx0 = soft_aesenc(cx0, _mm_set_epi64x(ah0, al0)); - cx1 = soft_aesenc(cx1, _mm_set_epi64x(ah1, al1)); - cx2 = soft_aesenc(cx2, _mm_set_epi64x(ah2, al2)); - cx3 = soft_aesenc(cx3, _mm_set_epi64x(ah3, al3)); - cx4 = soft_aesenc(cx4, _mm_set_epi64x(ah4, al4)); - } else { - cx0 = soft_aesenc((uint32_t*)&l0[idx0 & MASK], _mm_set_epi64x(ah0, al0)); - cx1 = soft_aesenc((uint32_t*)&l1[idx1 & MASK], _mm_set_epi64x(ah1, al1)); - cx2 = soft_aesenc((uint32_t*)&l2[idx2 & MASK], _mm_set_epi64x(ah2, al2)); - cx3 = soft_aesenc((uint32_t*)&l3[idx3 & MASK], _mm_set_epi64x(ah3, al3)); - cx4 = soft_aesenc((uint32_t*)&l4[idx4 & MASK], _mm_set_epi64x(ah4, al4)); - } - } else { - cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - cx1 = _mm_load_si128((__m128i*) &l1[idx1 & MASK]); - cx2 = _mm_load_si128((__m128i*) &l2[idx2 & MASK]); - cx3 = _mm_load_si128((__m128i*) &l3[idx3 & MASK]); - cx4 = _mm_load_si128((__m128i*) &l4[idx4 & MASK]); - - if (VARIANT == POW_CONCEAL) { - cryptonight_conceal_tweak(cx0, conc_var0); - cryptonight_conceal_tweak(cx1, conc_var1); - cryptonight_conceal_tweak(cx2, conc_var2); - cryptonight_conceal_tweak(cx3, conc_var3); - cryptonight_conceal_tweak(cx4, conc_var4); - } - - cx0 = _mm_aesenc_si128(cx0, _mm_set_epi64x(ah0, al0)); - cx1 = _mm_aesenc_si128(cx1, _mm_set_epi64x(ah1, al1)); - cx2 = _mm_aesenc_si128(cx2, _mm_set_epi64x(ah2, al2)); - cx3 = _mm_aesenc_si128(cx3, _mm_set_epi64x(ah3, al3)); - cx4 = _mm_aesenc_si128(cx4, _mm_set_epi64x(ah4, al4)); - } - - _mm_store_si128((__m128i*) &l0[idx0 & MASK], _mm_xor_si128(bx0, cx0)); - _mm_store_si128((__m128i*) &l1[idx1 & MASK], _mm_xor_si128(bx1, cx1)); - _mm_store_si128((__m128i*) &l2[idx2 & MASK], _mm_xor_si128(bx2, cx2)); - _mm_store_si128((__m128i*) &l3[idx3 & MASK], _mm_xor_si128(bx3, cx3)); - _mm_store_si128((__m128i*) &l4[idx4 & MASK], _mm_xor_si128(bx4, cx4)); - - idx0 = EXTRACT64(cx0); - idx1 = EXTRACT64(cx1); - idx2 = EXTRACT64(cx2); - idx3 = EXTRACT64(cx3); - idx4 = EXTRACT64(cx4); - - bx0 = cx0; - bx1 = cx1; - bx2 = cx2; - bx3 = cx3; - bx4 = cx4; - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & MASK])[0]; - ch = ((uint64_t*) &l0[idx0 & MASK])[1]; - lo = __umul128(idx0, cl, &hi); - - al0 += hi; - ah0 += lo; - - ((uint64_t*) &l0[idx0 & MASK])[0] = al0; - ((uint64_t*) &l0[idx0 & MASK])[1] = ah0; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - - cl = ((uint64_t*) &l1[idx1 & MASK])[0]; - ch = ((uint64_t*) &l1[idx1 & MASK])[1]; - lo = __umul128(idx1, cl, &hi); - - al1 += hi; - ah1 += lo; - - ((uint64_t*) &l1[idx1 & MASK])[0] = al1; - ((uint64_t*) &l1[idx1 & MASK])[1] = ah1; - - ah1 ^= ch; - al1 ^= cl; - idx1 = al1; - - - cl = ((uint64_t*) &l2[idx2 & MASK])[0]; - ch = ((uint64_t*) &l2[idx2 & MASK])[1]; - lo = __umul128(idx2, cl, &hi); - - al2 += hi; - ah2 += lo; - - ((uint64_t*) &l2[idx2 & MASK])[0] = al2; - ((uint64_t*) &l2[idx2 & MASK])[1] = ah2; - - ah2 ^= ch; - al2 ^= cl; - idx2 = al2; - - - cl = ((uint64_t*) &l3[idx3 & MASK])[0]; - ch = ((uint64_t*) &l3[idx3 & MASK])[1]; - lo = __umul128(idx3, cl, &hi); - - al3 += hi; - ah3 += lo; - - ((uint64_t*) &l3[idx3 & MASK])[0] = al3; - ((uint64_t*) &l3[idx3 & MASK])[1] = ah3; - - ah3 ^= ch; - al3 ^= cl; - idx3 = al3; - - - cl = ((uint64_t*) &l4[idx4 & MASK])[0]; - ch = ((uint64_t*) &l4[idx4 & MASK])[1]; - lo = __umul128(idx4, cl, &hi); - - al4 += hi; - ah4 += lo; - - ((uint64_t*) &l4[idx4 & MASK])[0] = al4; - ((uint64_t*) &l4[idx4 & MASK])[1] = ah4; - - ah4 ^= ch; - al4 ^= cl; - idx4 = al4; - } - - cn_implode_scratchpad((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad((__m128i*) l1, (__m128i*) h1); - cn_implode_scratchpad((__m128i*) l2, (__m128i*) h2); - cn_implode_scratchpad((__m128i*) l3, (__m128i*) h3); - cn_implode_scratchpad((__m128i*) l4, (__m128i*) h4); - - keccakf(h0, 24); - keccakf(h1, 24); - keccakf(h2, 24); - keccakf(h3, 24); - keccakf(h4, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - extra_hashes[scratchPad[1]->state[0] & 3](scratchPad[1]->state, 200, output + 32); - extra_hashes[scratchPad[2]->state[0] & 3](scratchPad[2]->state, 200, output + 64); - extra_hashes[scratchPad[3]->state[0] & 3](scratchPad[3]->state, 200, output + 96); - extra_hashes[scratchPad[4]->state[0] & 3](scratchPad[4]->state, 200, output + 128); - } - - inline static void hashPowV2(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - keccak((const uint8_t*) input, (int) size, scratchPad[0]->state, 200); - keccak((const uint8_t*) input + size, (int) size, scratchPad[1]->state, 200); - keccak((const uint8_t*) input + 2 * size, (int) size, scratchPad[2]->state, 200); - keccak((const uint8_t*) input + 3 * size, (int) size, scratchPad[3]->state, 200); - keccak((const uint8_t*) input + 4 * size, (int) size, scratchPad[4]->state, 200); - - uint64_t tweak1_2_0 = (*reinterpret_cast(reinterpret_cast(input) + 35) ^ - *(reinterpret_cast(scratchPad[0]->state) + 24)); - uint64_t tweak1_2_1 = (*reinterpret_cast(reinterpret_cast(input) + 35 + size) ^ - *(reinterpret_cast(scratchPad[1]->state) + 24)); - uint64_t tweak1_2_2 = (*reinterpret_cast(reinterpret_cast(input) + 35 + 2 * size) ^ - *(reinterpret_cast(scratchPad[2]->state) + 24)); - uint64_t tweak1_2_3 = (*reinterpret_cast(reinterpret_cast(input) + 35 + 3 * size) ^ - *(reinterpret_cast(scratchPad[3]->state) + 24)); - uint64_t tweak1_2_4 = (*reinterpret_cast(reinterpret_cast(input) + 35 + 4 * size) ^ - *(reinterpret_cast(scratchPad[4]->state) + 24)); - - - const uint8_t* l0 = scratchPad[0]->memory; - const uint8_t* l1 = scratchPad[1]->memory; - const uint8_t* l2 = scratchPad[2]->memory; - const uint8_t* l3 = scratchPad[3]->memory; - const uint8_t* l4 = scratchPad[4]->memory; - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - uint64_t* h1 = reinterpret_cast(scratchPad[1]->state); - uint64_t* h2 = reinterpret_cast(scratchPad[2]->state); - uint64_t* h3 = reinterpret_cast(scratchPad[3]->state); - uint64_t* h4 = reinterpret_cast(scratchPad[4]->state); - - cn_explode_scratchpad((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad((__m128i*) h1, (__m128i*) l1); - cn_explode_scratchpad((__m128i*) h2, (__m128i*) l2); - cn_explode_scratchpad((__m128i*) h3, (__m128i*) l3); - cn_explode_scratchpad((__m128i*) h4, (__m128i*) l4); - - uint64_t al0 = h0[0] ^h0[4]; - uint64_t al1 = h1[0] ^h1[4]; - uint64_t al2 = h2[0] ^h2[4]; - uint64_t al3 = h3[0] ^h3[4]; - uint64_t al4 = h4[0] ^h4[4]; - uint64_t ah0 = h0[1] ^h0[5]; - uint64_t ah1 = h1[1] ^h1[5]; - uint64_t ah2 = h2[1] ^h2[5]; - uint64_t ah3 = h3[1] ^h3[5]; - uint64_t ah4 = h4[1] ^h4[5]; - - __m128i bx0 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx1 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - __m128i bx2 = _mm_set_epi64x(h2[3] ^ h2[7], h2[2] ^ h2[6]); - __m128i bx3 = _mm_set_epi64x(h3[3] ^ h3[7], h3[2] ^ h3[6]); - __m128i bx4 = _mm_set_epi64x(h4[3] ^ h4[7], h4[2] ^ h4[6]); - - uint64_t idx0 = h0[0] ^h0[4]; - uint64_t idx1 = h1[0] ^h1[4]; - uint64_t idx2 = h2[0] ^h2[4]; - uint64_t idx3 = h3[0] ^h3[4]; - uint64_t idx4 = h4[0] ^h4[4]; - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx0; - __m128i cx1; - __m128i cx2; - __m128i cx3; - __m128i cx4; - - if (SOFT_AES) { - cx0 = soft_aesenc((uint32_t*)&l0[idx0 & MASK], _mm_set_epi64x(ah0, al0)); - cx1 = soft_aesenc((uint32_t*)&l1[idx1 & MASK], _mm_set_epi64x(ah1, al1)); - cx2 = soft_aesenc((uint32_t*)&l2[idx2 & MASK], _mm_set_epi64x(ah2, al2)); - cx3 = soft_aesenc((uint32_t*)&l3[idx3 & MASK], _mm_set_epi64x(ah3, al3)); - cx4 = soft_aesenc((uint32_t*)&l4[idx4 & MASK], _mm_set_epi64x(ah4, al4)); - } else { - cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - cx1 = _mm_load_si128((__m128i*) &l1[idx1 & MASK]); - cx2 = _mm_load_si128((__m128i*) &l2[idx2 & MASK]); - cx3 = _mm_load_si128((__m128i*) &l3[idx3 & MASK]); - cx4 = _mm_load_si128((__m128i*) &l4[idx4 & MASK]); - - cx0 = _mm_aesenc_si128(cx0, _mm_set_epi64x(ah0, al0)); - cx1 = _mm_aesenc_si128(cx1, _mm_set_epi64x(ah1, al1)); - cx2 = _mm_aesenc_si128(cx2, _mm_set_epi64x(ah2, al2)); - cx3 = _mm_aesenc_si128(cx3, _mm_set_epi64x(ah3, al3)); - cx4 = _mm_aesenc_si128(cx4, _mm_set_epi64x(ah4, al4)); - } - - _mm_store_si128((__m128i*) &l0[idx0 & MASK], _mm_xor_si128(bx0, cx0)); - _mm_store_si128((__m128i*) &l1[idx1 & MASK], _mm_xor_si128(bx1, cx1)); - _mm_store_si128((__m128i*) &l2[idx2 & MASK], _mm_xor_si128(bx2, cx2)); - _mm_store_si128((__m128i*) &l3[idx3 & MASK], _mm_xor_si128(bx3, cx3)); - _mm_store_si128((__m128i*) &l4[idx4 & MASK], _mm_xor_si128(bx4, cx4)); - - static const uint32_t table = 0x75310; - uint8_t tmp = reinterpret_cast(&l0[idx0 & MASK])[11]; - uint8_t index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*)(&l0[idx0 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - tmp = reinterpret_cast(&l1[idx1 & MASK])[11]; - index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*)(&l1[idx1 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - tmp = reinterpret_cast(&l2[idx2 & MASK])[11]; - index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*)(&l2[idx2 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - tmp = reinterpret_cast(&l3[idx3 & MASK])[11]; - index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*)(&l3[idx3 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - tmp = reinterpret_cast(&l4[idx4 & MASK])[11]; - index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*)(&l4[idx4 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - - idx0 = EXTRACT64(cx0); - idx1 = EXTRACT64(cx1); - idx2 = EXTRACT64(cx2); - idx3 = EXTRACT64(cx3); - idx4 = EXTRACT64(cx4); - - bx0 = cx0; - bx1 = cx1; - bx2 = cx2; - bx3 = cx3; - bx4 = cx4; - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & MASK])[0]; - ch = ((uint64_t*) &l0[idx0 & MASK])[1]; - lo = __umul128(idx0, cl, &hi); - - al0 += hi; - ah0 += lo; - - ah0 ^= tweak1_2_0; - ((uint64_t*) &l0[idx0 & MASK])[0] = al0; - ((uint64_t*) &l0[idx0 & MASK])[1] = ah0; - ah0 ^= tweak1_2_0; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - - cl = ((uint64_t*) &l1[idx1 & MASK])[0]; - ch = ((uint64_t*) &l1[idx1 & MASK])[1]; - lo = __umul128(idx1, cl, &hi); - - al1 += hi; - ah1 += lo; - - ah1 ^= tweak1_2_1; - ((uint64_t*) &l1[idx1 & MASK])[0] = al1; - ((uint64_t*) &l1[idx1 & MASK])[1] = ah1; - ah1 ^= tweak1_2_1; - - ah1 ^= ch; - al1 ^= cl; - idx1 = al1; - - - cl = ((uint64_t*) &l2[idx2 & MASK])[0]; - ch = ((uint64_t*) &l2[idx2 & MASK])[1]; - lo = __umul128(idx2, cl, &hi); - - al2 += hi; - ah2 += lo; - - ah2 ^= tweak1_2_2; - ((uint64_t*) &l2[idx2 & MASK])[0] = al2; - ((uint64_t*) &l2[idx2 & MASK])[1] = ah2; - ah2 ^= tweak1_2_2; - - ah2 ^= ch; - al2 ^= cl; - idx2 = al2; - - - cl = ((uint64_t*) &l3[idx3 & MASK])[0]; - ch = ((uint64_t*) &l3[idx3 & MASK])[1]; - lo = __umul128(idx3, cl, &hi); - - al3 += hi; - ah3 += lo; - - ah3 ^= tweak1_2_3; - ((uint64_t*) &l3[idx3 & MASK])[0] = al3; - ((uint64_t*) &l3[idx3 & MASK])[1] = ah3; - ah3 ^= tweak1_2_3; - - ah3 ^= ch; - al3 ^= cl; - idx3 = al3; - - - cl = ((uint64_t*) &l4[idx4 & MASK])[0]; - ch = ((uint64_t*) &l4[idx4 & MASK])[1]; - lo = __umul128(idx4, cl, &hi); - - al4 += hi; - ah4 += lo; - - ah4 ^= tweak1_2_4; - ((uint64_t*) &l4[idx4 & MASK])[0] = al4; - ((uint64_t*) &l4[idx4 & MASK])[1] = ah4; - ah4 ^= tweak1_2_4; - - ah4 ^= ch; - al4 ^= cl; - idx4 = al4; - } - - cn_implode_scratchpad((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad((__m128i*) l1, (__m128i*) h1); - cn_implode_scratchpad((__m128i*) l2, (__m128i*) h2); - cn_implode_scratchpad((__m128i*) l3, (__m128i*) h3); - cn_implode_scratchpad((__m128i*) l4, (__m128i*) h4); - - keccakf(h0, 24); - keccakf(h1, 24); - keccakf(h2, 24); - keccakf(h3, 24); - keccakf(h4, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - extra_hashes[scratchPad[1]->state[0] & 3](scratchPad[1]->state, 200, output + 32); - extra_hashes[scratchPad[2]->state[0] & 3](scratchPad[2]->state, 200, output + 64); - extra_hashes[scratchPad[3]->state[0] & 3](scratchPad[3]->state, 200, output + 96); - extra_hashes[scratchPad[4]->state[0] & 3](scratchPad[4]->state, 200, output + 128); - } - - inline static void hashPowV2_asm(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad, - AsmOptimization asmOptimization) - { - // not supported - } - - // quintuple - inline static void hashPowV3(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - keccak((const uint8_t*) input, (int) size, scratchPad[0]->state, 200); - keccak((const uint8_t*) input + size, (int) size, scratchPad[1]->state, 200); - keccak((const uint8_t*) input + 2 * size, (int) size, scratchPad[2]->state, 200); - keccak((const uint8_t*) input + 3 * size, (int) size, scratchPad[3]->state, 200); - keccak((const uint8_t*) input + 4 * size, (int) size, scratchPad[4]->state, 200); - - const uint8_t* l0 = scratchPad[0]->memory; - const uint8_t* l1 = scratchPad[1]->memory; - const uint8_t* l2 = scratchPad[2]->memory; - const uint8_t* l3 = scratchPad[3]->memory; - const uint8_t* l4 = scratchPad[4]->memory; - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - uint64_t* h1 = reinterpret_cast(scratchPad[1]->state); - uint64_t* h2 = reinterpret_cast(scratchPad[2]->state); - uint64_t* h3 = reinterpret_cast(scratchPad[3]->state); - uint64_t* h4 = reinterpret_cast(scratchPad[4]->state); - - cn_explode_scratchpad((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad((__m128i*) h1, (__m128i*) l1); - cn_explode_scratchpad((__m128i*) h2, (__m128i*) l2); - cn_explode_scratchpad((__m128i*) h3, (__m128i*) l3); - cn_explode_scratchpad((__m128i*) h4, (__m128i*) l4); - - uint64_t al0 = h0[0] ^h0[4]; - uint64_t al1 = h1[0] ^h1[4]; - uint64_t al2 = h2[0] ^h2[4]; - uint64_t al3 = h3[0] ^h3[4]; - uint64_t al4 = h4[0] ^h4[4]; - - uint64_t ah0 = h0[1] ^h0[5]; - uint64_t ah1 = h1[1] ^h1[5]; - uint64_t ah2 = h2[1] ^h2[5]; - uint64_t ah3 = h3[1] ^h3[5]; - uint64_t ah4 = h4[1] ^h4[5]; - - __m128i bx00 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx01 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - __m128i bx02 = _mm_set_epi64x(h2[3] ^ h2[7], h2[2] ^ h2[6]); - __m128i bx03 = _mm_set_epi64x(h3[3] ^ h3[7], h3[2] ^ h3[6]); - __m128i bx04 = _mm_set_epi64x(h4[3] ^ h4[7], h4[2] ^ h4[6]); - - __m128i bx10 = _mm_set_epi64x(h0[9] ^ h0[11], h0[8] ^ h0[10]); - __m128i bx11 = _mm_set_epi64x(h1[9] ^ h1[11], h1[8] ^ h1[10]); - __m128i bx12 = _mm_set_epi64x(h2[9] ^ h2[11], h2[8] ^ h2[10]); - __m128i bx13 = _mm_set_epi64x(h3[9] ^ h3[11], h3[8] ^ h3[10]); - __m128i bx14 = _mm_set_epi64x(h4[9] ^ h4[11], h4[8] ^ h4[10]); - - uint64_t idx0 = h0[0] ^h0[4]; - uint64_t idx1 = h1[0] ^h1[4]; - uint64_t idx2 = h2[0] ^h2[4]; - uint64_t idx3 = h3[0] ^h3[4]; - uint64_t idx4 = h4[0] ^h4[4]; - - SET_ROUNDING_MODE_UP(); - - __m128i division_result_xmm0 = _mm_cvtsi64_si128(h0[12]); - __m128i division_result_xmm1 = _mm_cvtsi64_si128(h1[12]); - __m128i division_result_xmm2 = _mm_cvtsi64_si128(h2[12]); - __m128i division_result_xmm3 = _mm_cvtsi64_si128(h3[12]); - __m128i division_result_xmm4 = _mm_cvtsi64_si128(h4[12]); - - uint64_t sqrt_result0 = h0[13]; - uint64_t sqrt_result1 = h1[13]; - uint64_t sqrt_result2 = h2[13]; - uint64_t sqrt_result3 = h3[13]; - uint64_t sqrt_result4 = h4[13]; - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx0; - __m128i cx1; - __m128i cx2; - __m128i cx3; - __m128i cx4; - - const __m128i ax0 = _mm_set_epi64x(ah0, al0); - const __m128i ax1 = _mm_set_epi64x(ah1, al1); - const __m128i ax2 = _mm_set_epi64x(ah2, al2); - const __m128i ax3 = _mm_set_epi64x(ah3, al3); - const __m128i ax4 = _mm_set_epi64x(ah4, al4); - - if (SOFT_AES) { - cx0 = soft_aesenc((uint32_t*)&l0[idx0 & MASK], ax0); - cx1 = soft_aesenc((uint32_t*)&l1[idx1 & MASK], ax1); - cx2 = soft_aesenc((uint32_t*)&l2[idx2 & MASK], ax2); - cx3 = soft_aesenc((uint32_t*)&l3[idx3 & MASK], ax3); - cx4 = soft_aesenc((uint32_t*)&l4[idx4 & MASK], ax4); - } else { - cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - cx1 = _mm_load_si128((__m128i*) &l1[idx1 & MASK]); - cx2 = _mm_load_si128((__m128i*) &l2[idx2 & MASK]); - cx3 = _mm_load_si128((__m128i*) &l3[idx3 & MASK]); - cx4 = _mm_load_si128((__m128i*) &l4[idx4 & MASK]); - - cx0 = _mm_aesenc_si128(cx0, ax0); - cx1 = _mm_aesenc_si128(cx1, ax1); - cx2 = _mm_aesenc_si128(cx2, ax2); - cx3 = _mm_aesenc_si128(cx3, ax3); - cx4 = _mm_aesenc_si128(cx4, ax4); - } - - SHUFFLE_PHASE_1(l0, (idx0&MASK), bx00, bx10, ax0, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - SHUFFLE_PHASE_1(l1, (idx1&MASK), bx01, bx11, ax1, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - SHUFFLE_PHASE_1(l2, (idx2&MASK), bx02, bx12, ax2, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - SHUFFLE_PHASE_1(l3, (idx3&MASK), bx03, bx13, ax3, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - SHUFFLE_PHASE_1(l4, (idx4&MASK), bx04, bx14, ax4, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - - _mm_store_si128((__m128i*) &l0[idx0 & MASK], _mm_xor_si128(bx00, cx0)); - _mm_store_si128((__m128i*) &l1[idx1 & MASK], _mm_xor_si128(bx01, cx1)); - _mm_store_si128((__m128i*) &l2[idx2 & MASK], _mm_xor_si128(bx02, cx2)); - _mm_store_si128((__m128i*) &l3[idx3 & MASK], _mm_xor_si128(bx03, cx3)); - _mm_store_si128((__m128i*) &l4[idx4 & MASK], _mm_xor_si128(bx04, cx4)); - - idx0 = EXTRACT64(cx0); - idx1 = EXTRACT64(cx1); - idx2 = EXTRACT64(cx2); - idx3 = EXTRACT64(cx3); - idx4 = EXTRACT64(cx4); - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & MASK])[0]; - ch = ((uint64_t*) &l0[idx0 & MASK])[1]; - - INTEGER_MATH_V2(0, cl, cx0); - - lo = __umul128(idx0, cl, &hi); - - SHUFFLE_PHASE_2(l0, (idx0&MASK), bx00, bx10, ax0, lo, hi, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - - al0 += hi; - ah0 += lo; - - ((uint64_t*) &l0[idx0 & MASK])[0] = al0; - ((uint64_t*) &l0[idx0 & MASK])[1] = ah0; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - bx10 = bx00; - bx00 = cx0; - - - cl = ((uint64_t*) &l1[idx1 & MASK])[0]; - ch = ((uint64_t*) &l1[idx1 & MASK])[1]; - - INTEGER_MATH_V2(1, cl, cx1); - - lo = __umul128(idx1, cl, &hi); - - SHUFFLE_PHASE_2(l1, (idx1&MASK), bx01, bx11, ax1, lo, hi, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - - al1 += hi; - ah1 += lo; - - ((uint64_t*) &l1[idx1 & MASK])[0] = al1; - ((uint64_t*) &l1[idx1 & MASK])[1] = ah1; - - ah1 ^= ch; - al1 ^= cl; - idx1 = al1; - - bx11 = bx01; - bx01 = cx1; - - - cl = ((uint64_t*) &l2[idx2 & MASK])[0]; - ch = ((uint64_t*) &l2[idx2 & MASK])[1]; - - INTEGER_MATH_V2(2, cl, cx2); - - lo = __umul128(idx2, cl, &hi); - - SHUFFLE_PHASE_2(l2, (idx2&MASK), bx02, bx12, ax2, lo, hi, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - - al2 += hi; - ah2 += lo; - - ((uint64_t*) &l2[idx2 & MASK])[0] = al2; - ((uint64_t*) &l2[idx2 & MASK])[1] = ah2; - - ah2 ^= ch; - al2 ^= cl; - idx2 = al2; - - bx12 = bx02; - bx02 = cx2; - - - cl = ((uint64_t*) &l3[idx3 & MASK])[0]; - ch = ((uint64_t*) &l3[idx3 & MASK])[1]; - - INTEGER_MATH_V2(3, cl, cx3); - - lo = __umul128(idx3, cl, &hi); - - SHUFFLE_PHASE_2(l3, (idx3&MASK), bx03, bx13, ax3, lo, hi, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - - al3 += hi; - ah3 += lo; - - ((uint64_t*) &l3[idx3 & MASK])[0] = al3; - ((uint64_t*) &l3[idx3 & MASK])[1] = ah3; - - ah3 ^= ch; - al3 ^= cl; - idx3 = al3; - - bx13 = bx03; - bx03 = cx3; - - - cl = ((uint64_t*) &l4[idx4 & MASK])[0]; - ch = ((uint64_t*) &l4[idx4 & MASK])[1]; - - INTEGER_MATH_V2(4, cl, cx4); - - lo = __umul128(idx4, cl, &hi); - - SHUFFLE_PHASE_2(l4, (idx4&MASK), bx04, bx14, ax4, lo, hi, VARIANT == POW_RWZ || VARIANT == POW_UPX2) - - al4 += hi; - ah4 += lo; - - ((uint64_t*) &l4[idx4 & MASK])[0] = al4; - ((uint64_t*) &l4[idx4 & MASK])[1] = ah4; - - ah4 ^= ch; - al4 ^= cl; - idx4 = al4; - - bx14 = bx04; - bx04 = cx4; - } - - cn_implode_scratchpad((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad((__m128i*) l1, (__m128i*) h1); - cn_implode_scratchpad((__m128i*) l2, (__m128i*) h2); - cn_implode_scratchpad((__m128i*) l3, (__m128i*) h3); - cn_implode_scratchpad((__m128i*) l4, (__m128i*) h4); - - keccakf(h0, 24); - keccakf(h1, 24); - keccakf(h2, 24); - keccakf(h3, 24); - keccakf(h4, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - extra_hashes[scratchPad[1]->state[0] & 3](scratchPad[1]->state, 200, output + 32); - extra_hashes[scratchPad[2]->state[0] & 3](scratchPad[2]->state, 200, output + 64); - extra_hashes[scratchPad[3]->state[0] & 3](scratchPad[3]->state, 200, output + 96); - extra_hashes[scratchPad[4]->state[0] & 3](scratchPad[4]->state, 200, output + 128); - } - - inline static void hashPowV3_asm(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad, - AsmOptimization asmOptimization) - { - // not supported - } - - // quintuple - inline static void hashPowV4(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad, - uint64_t height) - { - keccak((const uint8_t*) input, (int) size, scratchPad[0]->state, 200); - keccak((const uint8_t*) input + size, (int) size, scratchPad[1]->state, 200); - keccak((const uint8_t*) input + 2 * size, (int) size, scratchPad[2]->state, 200); - keccak((const uint8_t*) input + 3 * size, (int) size, scratchPad[3]->state, 200); - keccak((const uint8_t*) input + 4 * size, (int) size, scratchPad[4]->state, 200); - - const uint8_t* l0 = scratchPad[0]->memory; - const uint8_t* l1 = scratchPad[1]->memory; - const uint8_t* l2 = scratchPad[2]->memory; - const uint8_t* l3 = scratchPad[3]->memory; - const uint8_t* l4 = scratchPad[4]->memory; - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - uint64_t* h1 = reinterpret_cast(scratchPad[1]->state); - uint64_t* h2 = reinterpret_cast(scratchPad[2]->state); - uint64_t* h3 = reinterpret_cast(scratchPad[3]->state); - uint64_t* h4 = reinterpret_cast(scratchPad[4]->state); - - cn_explode_scratchpad((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad((__m128i*) h1, (__m128i*) l1); - cn_explode_scratchpad((__m128i*) h2, (__m128i*) l2); - cn_explode_scratchpad((__m128i*) h3, (__m128i*) l3); - cn_explode_scratchpad((__m128i*) h4, (__m128i*) l4); - - uint64_t al0 = h0[0] ^h0[4]; - uint64_t al1 = h1[0] ^h1[4]; - uint64_t al2 = h2[0] ^h2[4]; - uint64_t al3 = h3[0] ^h3[4]; - uint64_t al4 = h4[0] ^h4[4]; - - uint64_t ah0 = h0[1] ^h0[5]; - uint64_t ah1 = h1[1] ^h1[5]; - uint64_t ah2 = h2[1] ^h2[5]; - uint64_t ah3 = h3[1] ^h3[5]; - uint64_t ah4 = h4[1] ^h4[5]; - - __m128i bx00 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx01 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - __m128i bx02 = _mm_set_epi64x(h2[3] ^ h2[7], h2[2] ^ h2[6]); - __m128i bx03 = _mm_set_epi64x(h3[3] ^ h3[7], h3[2] ^ h3[6]); - __m128i bx04 = _mm_set_epi64x(h4[3] ^ h4[7], h4[2] ^ h4[6]); - - __m128i bx10 = _mm_set_epi64x(h0[9] ^ h0[11], h0[8] ^ h0[10]); - __m128i bx11 = _mm_set_epi64x(h1[9] ^ h1[11], h1[8] ^ h1[10]); - __m128i bx12 = _mm_set_epi64x(h2[9] ^ h2[11], h2[8] ^ h2[10]); - __m128i bx13 = _mm_set_epi64x(h3[9] ^ h3[11], h3[8] ^ h3[10]); - __m128i bx14 = _mm_set_epi64x(h4[9] ^ h4[11], h4[8] ^ h4[10]); - - uint64_t idx0 = h0[0] ^h0[4]; - uint64_t idx1 = h1[0] ^h1[4]; - uint64_t idx2 = h2[0] ^h2[4]; - uint64_t idx3 = h3[0] ^h3[4]; - uint64_t idx4 = h4[0] ^h4[4]; - - SET_ROUNDING_MODE_UP() - - VARIANT4_RANDOM_MATH_INIT(0, h0) - VARIANT4_RANDOM_MATH_INIT(1, h1) - VARIANT4_RANDOM_MATH_INIT(2, h2) - VARIANT4_RANDOM_MATH_INIT(3, h3) - VARIANT4_RANDOM_MATH_INIT(4, h4) - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx0; - __m128i cx1; - __m128i cx2; - __m128i cx3; - __m128i cx4; - - const __m128i ax0 = _mm_set_epi64x(ah0, al0); - const __m128i ax1 = _mm_set_epi64x(ah1, al1); - const __m128i ax2 = _mm_set_epi64x(ah2, al2); - const __m128i ax3 = _mm_set_epi64x(ah3, al3); - const __m128i ax4 = _mm_set_epi64x(ah4, al4); - - if (SOFT_AES) { - cx0 = soft_aesenc((uint32_t*)&l0[idx0 & MASK], ax0); - cx1 = soft_aesenc((uint32_t*)&l1[idx1 & MASK], ax1); - cx2 = soft_aesenc((uint32_t*)&l2[idx2 & MASK], ax2); - cx3 = soft_aesenc((uint32_t*)&l3[idx3 & MASK], ax3); - cx4 = soft_aesenc((uint32_t*)&l4[idx4 & MASK], ax4); - } else { - cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - cx1 = _mm_load_si128((__m128i*) &l1[idx1 & MASK]); - cx2 = _mm_load_si128((__m128i*) &l2[idx2 & MASK]); - cx3 = _mm_load_si128((__m128i*) &l3[idx3 & MASK]); - cx4 = _mm_load_si128((__m128i*) &l4[idx4 & MASK]); - - cx0 = _mm_aesenc_si128(cx0, ax0); - cx1 = _mm_aesenc_si128(cx1, ax1); - cx2 = _mm_aesenc_si128(cx2, ax2); - cx3 = _mm_aesenc_si128(cx3, ax3); - cx4 = _mm_aesenc_si128(cx4, ax4); - } - - SHUFFLE_V4(l0, (idx0&MASK), bx00, bx10, ax0, cx0) - SHUFFLE_V4(l1, (idx1&MASK), bx01, bx11, ax1, cx1) - SHUFFLE_V4(l2, (idx2&MASK), bx02, bx12, ax2, cx2) - SHUFFLE_V4(l3, (idx3&MASK), bx03, bx13, ax3, cx3) - SHUFFLE_V4(l4, (idx4&MASK), bx04, bx14, ax4, cx4) - - _mm_store_si128((__m128i*) &l0[idx0 & MASK], _mm_xor_si128(bx00, cx0)); - _mm_store_si128((__m128i*) &l1[idx1 & MASK], _mm_xor_si128(bx01, cx1)); - _mm_store_si128((__m128i*) &l2[idx2 & MASK], _mm_xor_si128(bx02, cx2)); - _mm_store_si128((__m128i*) &l3[idx3 & MASK], _mm_xor_si128(bx03, cx3)); - _mm_store_si128((__m128i*) &l4[idx4 & MASK], _mm_xor_si128(bx04, cx4)); - - idx0 = EXTRACT64(cx0); - idx1 = EXTRACT64(cx1); - idx2 = EXTRACT64(cx2); - idx3 = EXTRACT64(cx3); - idx4 = EXTRACT64(cx4); - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & MASK])[0]; - ch = ((uint64_t*) &l0[idx0 & MASK])[1]; - - VARIANT4_RANDOM_MATH(0, al0, ah0, cl, bx00, bx10); - - if (VARIANT == POW_V4) { - al0 ^= r0[2] | ((uint64_t)(r0[3]) << 32); - ah0 ^= r0[0] | ((uint64_t)(r0[1]) << 32); - } - - lo = __umul128(idx0, cl, &hi); - - SHUFFLE_V4(l0, (idx0&MASK), bx00, bx10, ax0, cx0) - - al0 += hi; - ah0 += lo; - - ((uint64_t*) &l0[idx0 & MASK])[0] = al0; - ((uint64_t*) &l0[idx0 & MASK])[1] = ah0; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - bx10 = bx00; - bx00 = cx0; - - - cl = ((uint64_t*) &l1[idx1 & MASK])[0]; - ch = ((uint64_t*) &l1[idx1 & MASK])[1]; - - VARIANT4_RANDOM_MATH(1, al1, ah1, cl, bx01, bx11) - - if (VARIANT == POW_V4) { - al1 ^= r1[2] | ((uint64_t)(r1[3]) << 32); - ah1 ^= r1[0] | ((uint64_t)(r1[1]) << 32); - } - - lo = __umul128(idx1, cl, &hi); - - SHUFFLE_V4(l1, (idx1&MASK), bx01, bx11, ax1, cx1); - - al1 += hi; - ah1 += lo; - - ((uint64_t*) &l1[idx1 & MASK])[0] = al1; - ((uint64_t*) &l1[idx1 & MASK])[1] = ah1; - - ah1 ^= ch; - al1 ^= cl; - idx1 = al1; - - bx11 = bx01; - bx01 = cx1; - - - cl = ((uint64_t*) &l2[idx2 & MASK])[0]; - ch = ((uint64_t*) &l2[idx2 & MASK])[1]; - - VARIANT4_RANDOM_MATH(2, al2, ah2, cl, bx02, bx12); - - if (VARIANT == POW_V4) { - al2 ^= r2[2] | ((uint64_t)(r2[3]) << 32); - ah2 ^= r2[0] | ((uint64_t)(r2[1]) << 32); - } - - lo = __umul128(idx2, cl, &hi); - - SHUFFLE_V4(l2, (idx2&MASK), bx02, bx12, ax2, cx2); - - al2 += hi; - ah2 += lo; - - ((uint64_t*) &l2[idx2 & MASK])[0] = al2; - ((uint64_t*) &l2[idx2 & MASK])[1] = ah2; - - ah2 ^= ch; - al2 ^= cl; - idx2 = al2; - - bx12 = bx02; - bx02 = cx2; - - - cl = ((uint64_t*) &l3[idx3 & MASK])[0]; - ch = ((uint64_t*) &l3[idx3 & MASK])[1]; - - VARIANT4_RANDOM_MATH(3, al3, ah3, cl, bx03, bx13); - - if (VARIANT == POW_V4) { - al3 ^= r3[2] | ((uint64_t)(r3[3]) << 32); - ah3 ^= r3[0] | ((uint64_t)(r3[1]) << 32); - } - - lo = __umul128(idx3, cl, &hi); - - SHUFFLE_V4(l3, (idx3&MASK), bx03, bx13, ax3, cx3); - - al3 += hi; - ah3 += lo; - - ((uint64_t*) &l3[idx3 & MASK])[0] = al3; - ((uint64_t*) &l3[idx3 & MASK])[1] = ah3; - - ah3 ^= ch; - al3 ^= cl; - idx3 = al3; - - bx13 = bx03; - bx03 = cx3; - - - cl = ((uint64_t*) &l4[idx4 & MASK])[0]; - ch = ((uint64_t*) &l4[idx4 & MASK])[1]; - - VARIANT4_RANDOM_MATH(4, al4, ah4, cl, bx04, bx14); - - if (VARIANT == POW_V4) { - al4 ^= r4[2] | ((uint64_t)(r4[3]) << 32); - ah4 ^= r4[0] | ((uint64_t)(r4[1]) << 32); - } - - lo = __umul128(idx4, cl, &hi); - - SHUFFLE_V4(l4, (idx4&MASK), bx04, bx14, ax4, cx4); - - al4 += hi; - ah4 += lo; - - ((uint64_t*) &l4[idx4 & MASK])[0] = al4; - ((uint64_t*) &l4[idx4 & MASK])[1] = ah4; - - ah4 ^= ch; - al4 ^= cl; - idx4 = al4; - - bx14 = bx04; - bx04 = cx4; - } - - cn_implode_scratchpad((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad((__m128i*) l1, (__m128i*) h1); - cn_implode_scratchpad((__m128i*) l2, (__m128i*) h2); - cn_implode_scratchpad((__m128i*) l3, (__m128i*) h3); - cn_implode_scratchpad((__m128i*) l4, (__m128i*) h4); - - keccakf(h0, 24); - keccakf(h1, 24); - keccakf(h2, 24); - keccakf(h3, 24); - keccakf(h4, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - extra_hashes[scratchPad[1]->state[0] & 3](scratchPad[1]->state, 200, output + 32); - extra_hashes[scratchPad[2]->state[0] & 3](scratchPad[2]->state, 200, output + 64); - extra_hashes[scratchPad[3]->state[0] & 3](scratchPad[3]->state, 200, output + 96); - extra_hashes[scratchPad[4]->state[0] & 3](scratchPad[4]->state, 200, output + 128); - } - - inline static void hashPowV4_asm(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad, - uint64_t height, - AsmOptimization asmOptimization) - { - // not supported - } - - inline static void hashLiteTube(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - keccak((const uint8_t*) input, (int) size, scratchPad[0]->state, 200); - keccak((const uint8_t*) input + size, (int) size, scratchPad[1]->state, 200); - keccak((const uint8_t*) input + 2 * size, (int) size, scratchPad[2]->state, 200); - keccak((const uint8_t*) input + 3 * size, (int) size, scratchPad[3]->state, 200); - keccak((const uint8_t*) input + 4 * size, (int) size, scratchPad[4]->state, 200); - - uint64_t tweak1_2_0 = (*reinterpret_cast(reinterpret_cast(input) + 35) ^ - *(reinterpret_cast(scratchPad[0]->state) + 24)); - uint64_t tweak1_2_1 = (*reinterpret_cast(reinterpret_cast(input) + 35 + size) ^ - *(reinterpret_cast(scratchPad[1]->state) + 24)); - uint64_t tweak1_2_2 = (*reinterpret_cast(reinterpret_cast(input) + 35 + 2 * size) ^ - *(reinterpret_cast(scratchPad[2]->state) + 24)); - uint64_t tweak1_2_3 = (*reinterpret_cast(reinterpret_cast(input) + 35 + 3 * size) ^ - *(reinterpret_cast(scratchPad[3]->state) + 24)); - uint64_t tweak1_2_4 = (*reinterpret_cast(reinterpret_cast(input) + 35 + 4 * size) ^ - *(reinterpret_cast(scratchPad[4]->state) + 24)); - - - const uint8_t* l0 = scratchPad[0]->memory; - const uint8_t* l1 = scratchPad[1]->memory; - const uint8_t* l2 = scratchPad[2]->memory; - const uint8_t* l3 = scratchPad[3]->memory; - const uint8_t* l4 = scratchPad[4]->memory; - uint64_t* h0 = reinterpret_cast(scratchPad[0]->state); - uint64_t* h1 = reinterpret_cast(scratchPad[1]->state); - uint64_t* h2 = reinterpret_cast(scratchPad[2]->state); - uint64_t* h3 = reinterpret_cast(scratchPad[3]->state); - uint64_t* h4 = reinterpret_cast(scratchPad[4]->state); - - cn_explode_scratchpad((__m128i*) h0, (__m128i*) l0); - cn_explode_scratchpad((__m128i*) h1, (__m128i*) l1); - cn_explode_scratchpad((__m128i*) h2, (__m128i*) l2); - cn_explode_scratchpad((__m128i*) h3, (__m128i*) l3); - cn_explode_scratchpad((__m128i*) h4, (__m128i*) l4); - - uint64_t al0 = h0[0] ^h0[4]; - uint64_t al1 = h1[0] ^h1[4]; - uint64_t al2 = h2[0] ^h2[4]; - uint64_t al3 = h3[0] ^h3[4]; - uint64_t al4 = h4[0] ^h4[4]; - uint64_t ah0 = h0[1] ^h0[5]; - uint64_t ah1 = h1[1] ^h1[5]; - uint64_t ah2 = h2[1] ^h2[5]; - uint64_t ah3 = h3[1] ^h3[5]; - uint64_t ah4 = h4[1] ^h4[5]; - - __m128i bx0 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); - __m128i bx1 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); - __m128i bx2 = _mm_set_epi64x(h2[3] ^ h2[7], h2[2] ^ h2[6]); - __m128i bx3 = _mm_set_epi64x(h3[3] ^ h3[7], h3[2] ^ h3[6]); - __m128i bx4 = _mm_set_epi64x(h4[3] ^ h4[7], h4[2] ^ h4[6]); - - uint64_t idx0 = h0[0] ^h0[4]; - uint64_t idx1 = h1[0] ^h1[4]; - uint64_t idx2 = h2[0] ^h2[4]; - uint64_t idx3 = h3[0] ^h3[4]; - uint64_t idx4 = h4[0] ^h4[4]; - - for (size_t i = 0; i < ITERATIONS; i++) { - __m128i cx0; - __m128i cx1; - __m128i cx2; - __m128i cx3; - __m128i cx4; - - if (SOFT_AES) { - cx0 = soft_aesenc((uint32_t*)&l0[idx0 & MASK], _mm_set_epi64x(ah0, al0)); - cx1 = soft_aesenc((uint32_t*)&l1[idx1 & MASK], _mm_set_epi64x(ah1, al1)); - cx2 = soft_aesenc((uint32_t*)&l2[idx2 & MASK], _mm_set_epi64x(ah2, al2)); - cx3 = soft_aesenc((uint32_t*)&l3[idx3 & MASK], _mm_set_epi64x(ah3, al3)); - cx4 = soft_aesenc((uint32_t*)&l4[idx4 & MASK], _mm_set_epi64x(ah4, al4)); - } else { - cx0 = _mm_load_si128((__m128i*) &l0[idx0 & MASK]); - cx1 = _mm_load_si128((__m128i*) &l1[idx1 & MASK]); - cx2 = _mm_load_si128((__m128i*) &l2[idx2 & MASK]); - cx3 = _mm_load_si128((__m128i*) &l3[idx3 & MASK]); - cx4 = _mm_load_si128((__m128i*) &l4[idx4 & MASK]); - - cx0 = _mm_aesenc_si128(cx0, _mm_set_epi64x(ah0, al0)); - cx1 = _mm_aesenc_si128(cx1, _mm_set_epi64x(ah1, al1)); - cx2 = _mm_aesenc_si128(cx2, _mm_set_epi64x(ah2, al2)); - cx3 = _mm_aesenc_si128(cx3, _mm_set_epi64x(ah3, al3)); - cx4 = _mm_aesenc_si128(cx4, _mm_set_epi64x(ah4, al4)); - } - - _mm_store_si128((__m128i*) &l0[idx0 & MASK], _mm_xor_si128(bx0, cx0)); - _mm_store_si128((__m128i*) &l1[idx1 & MASK], _mm_xor_si128(bx1, cx1)); - _mm_store_si128((__m128i*) &l2[idx2 & MASK], _mm_xor_si128(bx2, cx2)); - _mm_store_si128((__m128i*) &l3[idx3 & MASK], _mm_xor_si128(bx3, cx3)); - _mm_store_si128((__m128i*) &l4[idx4 & MASK], _mm_xor_si128(bx4, cx4)); - - static const uint32_t table = 0x75310; - uint8_t tmp = reinterpret_cast(&l0[idx0 & MASK])[11]; - uint8_t index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*)(&l0[idx0 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - tmp = reinterpret_cast(&l1[idx1 & MASK])[11]; - index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*)(&l1[idx1 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - tmp = reinterpret_cast(&l2[idx2 & MASK])[11]; - index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*)(&l2[idx2 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - tmp = reinterpret_cast(&l3[idx3 & MASK])[11]; - index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*)(&l3[idx3 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - tmp = reinterpret_cast(&l4[idx4 & MASK])[11]; - index = (((tmp >> INDEX_SHIFT) & 6) | (tmp & 1)) << 1; - ((uint8_t*)(&l4[idx4 & MASK]))[11] = tmp ^ ((table >> index) & 0x30); - - idx0 = EXTRACT64(cx0); - idx1 = EXTRACT64(cx1); - idx2 = EXTRACT64(cx2); - idx3 = EXTRACT64(cx3); - idx4 = EXTRACT64(cx4); - - bx0 = cx0; - bx1 = cx1; - bx2 = cx2; - bx3 = cx3; - bx4 = cx4; - - uint64_t hi, lo, cl, ch; - cl = ((uint64_t*) &l0[idx0 & MASK])[0]; - ch = ((uint64_t*) &l0[idx0 & MASK])[1]; - lo = __umul128(idx0, cl, &hi); - - al0 += hi; - ah0 += lo; - - ah0 ^= tweak1_2_0; - ((uint64_t*) &l0[idx0 & MASK])[0] = al0; - ((uint64_t*) &l0[idx0 & MASK])[1] = ah0; - ah0 ^= tweak1_2_0; - - ((uint64_t*)&l0[idx0 & MASK])[1] ^= ((uint64_t*)&l0[idx0 & MASK])[0]; - - ah0 ^= ch; - al0 ^= cl; - idx0 = al0; - - - cl = ((uint64_t*) &l1[idx1 & MASK])[0]; - ch = ((uint64_t*) &l1[idx1 & MASK])[1]; - lo = __umul128(idx1, cl, &hi); - - al1 += hi; - ah1 += lo; - - ah1 ^= tweak1_2_1; - ((uint64_t*) &l1[idx1 & MASK])[0] = al1; - ((uint64_t*) &l1[idx1 & MASK])[1] = ah1; - ah1 ^= tweak1_2_1; - - ((uint64_t*)&l1[idx1 & MASK])[1] ^= ((uint64_t*)&l1[idx1 & MASK])[0]; - - ah1 ^= ch; - al1 ^= cl; - idx1 = al1; - - - cl = ((uint64_t*) &l2[idx2 & MASK])[0]; - ch = ((uint64_t*) &l2[idx2 & MASK])[1]; - lo = __umul128(idx2, cl, &hi); - - al2 += hi; - ah2 += lo; - - ah2 ^= tweak1_2_2; - ((uint64_t*) &l2[idx2 & MASK])[0] = al2; - ((uint64_t*) &l2[idx2 & MASK])[1] = ah2; - ah2 ^= tweak1_2_2; - - ((uint64_t*)&l2[idx2 & MASK])[1] ^= ((uint64_t*)&l2[idx2 & MASK])[0]; - - ah2 ^= ch; - al2 ^= cl; - idx2 = al2; - - - cl = ((uint64_t*) &l3[idx3 & MASK])[0]; - ch = ((uint64_t*) &l3[idx3 & MASK])[1]; - lo = __umul128(idx3, cl, &hi); - - al3 += hi; - ah3 += lo; - - ah3 ^= tweak1_2_3; - ((uint64_t*) &l3[idx3 & MASK])[0] = al3; - ((uint64_t*) &l3[idx3 & MASK])[1] = ah3; - ah3 ^= tweak1_2_3; - - ((uint64_t*)&l3[idx3 & MASK])[1] ^= ((uint64_t*)&l3[idx3 & MASK])[0]; - - ah3 ^= ch; - al3 ^= cl; - idx3 = al3; - - - cl = ((uint64_t*) &l4[idx4 & MASK])[0]; - ch = ((uint64_t*) &l4[idx4 & MASK])[1]; - lo = __umul128(idx4, cl, &hi); - - al4 += hi; - ah4 += lo; - - ah4 ^= tweak1_2_4; - ((uint64_t*) &l4[idx4 & MASK])[0] = al4; - ((uint64_t*) &l4[idx4 & MASK])[1] = ah4; - ah4 ^= tweak1_2_4; - - ((uint64_t*)&l4[idx4 & MASK])[1] ^= ((uint64_t*)&l4[idx4 & MASK])[0]; - - ah4 ^= ch; - al4 ^= cl; - idx4 = al4; - } - - cn_implode_scratchpad((__m128i*) l0, (__m128i*) h0); - cn_implode_scratchpad((__m128i*) l1, (__m128i*) h1); - cn_implode_scratchpad((__m128i*) l2, (__m128i*) h2); - cn_implode_scratchpad((__m128i*) l3, (__m128i*) h3); - cn_implode_scratchpad((__m128i*) l4, (__m128i*) h4); - - keccakf(h0, 24); - keccakf(h1, 24); - keccakf(h2, 24); - keccakf(h3, 24); - keccakf(h4, 24); - - extra_hashes[scratchPad[0]->state[0] & 3](scratchPad[0]->state, 200, output); - extra_hashes[scratchPad[1]->state[0] & 3](scratchPad[1]->state, 200, output + 32); - extra_hashes[scratchPad[2]->state[0] & 3](scratchPad[2]->state, 200, output + 64); - extra_hashes[scratchPad[3]->state[0] & 3](scratchPad[3]->state, 200, output + 96); - extra_hashes[scratchPad[4]->state[0] & 3](scratchPad[4]->state, 200, output + 128); - } - - inline static void hashHeavy(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - // not supported - } - - inline static void hashHeavyTube(const uint8_t* __restrict__ input, - size_t size, - uint8_t* __restrict__ output, - ScratchPad** __restrict__ scratchPad) - { - // not supported - } -}; - -#endif /* __CRYPTONIGHT_X86_H__ */ diff --git a/src/crypto/HashSelector.cpp b/src/crypto/HashSelector.cpp deleted file mode 100644 index 22dc8d0d..00000000 --- a/src/crypto/HashSelector.cpp +++ /dev/null @@ -1,965 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * Copyright 2018 Sebastian Stolzenberg - * Copyright 2018 BenDroid - * - * - * 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 . - */ - -#include -#include -#include "crypto/HashSelector.h" -#include "crypto/Argon2.h" -#include "crypto/CryptoNight.h" - -#if defined(XMRIG_ARM) -# include "crypto/CryptoNight_arm.h" -#else -# include "crypto/CryptoNight_x86.h" -#endif - -#include "crypto/CryptoNight_test.h" -#include "crypto/Argon2_test.h" - -template -static void cryptonight_aesni(AsmOptimization asmOptimization, uint64_t height, PowVariant variant, const uint8_t* input, size_t size, uint8_t* output, ScratchPad** scratchPad) { -# if !defined(XMRIG_ARMv7) - if (variant == PowVariant::POW_V1) { -#if defined(XMRIG_ARM) - CryptoNightMultiHash<0x80000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, false, POW_V1, NUM_HASH_BLOCKS>::hashPowV2(input, size, output, scratchPad); -#else - if (asmOptimization == AsmOptimization::ASM_INTEL && NUM_HASH_BLOCKS == 1) { - CryptoNightMultiHash<0x80000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, false, POW_V1, NUM_HASH_BLOCKS>::hashPowV2_asm(input, size, output, scratchPad, asmOptimization); - } else { - CryptoNightMultiHash<0x80000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, false, POW_V1, NUM_HASH_BLOCKS>::hashPowV2(input, size, output, scratchPad); - } -#endif - } else if (variant == PowVariant::POW_V2) { -#if defined(XMRIG_ARM) - CryptoNightMultiHash<0x80000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, false, POW_V2, NUM_HASH_BLOCKS>::hashPowV3(input, size, output, scratchPad); -#else - if ((asmOptimization == AsmOptimization::ASM_INTEL && NUM_HASH_BLOCKS <= 2) || - (asmOptimization == AsmOptimization::ASM_RYZEN && NUM_HASH_BLOCKS == 1) || - (asmOptimization == AsmOptimization::ASM_BULLDOZER && NUM_HASH_BLOCKS == 1)) { - CryptoNightMultiHash<0x80000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, false, POW_V2, NUM_HASH_BLOCKS>::hashPowV3_asm(input, size, output, scratchPad, asmOptimization); - } else { - CryptoNightMultiHash<0x80000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, false, POW_V2, NUM_HASH_BLOCKS>::hashPowV3(input, size, output, scratchPad); - } -#endif - } else if (variant == PowVariant::POW_V4) { -#if defined(XMRIG_ARM) - CryptoNightMultiHash<0x80000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, false, POW_V4, NUM_HASH_BLOCKS>::hashPowV4(input, size, output, scratchPad, height); -#else - if (asmOptimization != AsmOptimization::ASM_OFF && NUM_HASH_BLOCKS <= 2) { - CryptoNightMultiHash<0x80000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, false, POW_V4, NUM_HASH_BLOCKS>::hashPowV4_asm(input, size, output, scratchPad, height, asmOptimization); - } else { - CryptoNightMultiHash<0x80000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, false, POW_V4, NUM_HASH_BLOCKS>::hashPowV4(input, size, output, scratchPad, height); - } -#endif - } else if (variant == PowVariant::POW_WOW) { -#if defined(XMRIG_ARM) - CryptoNightMultiHash<0x80000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, false, POW_WOW, NUM_HASH_BLOCKS>::hashPowV4(input, size, output, scratchPad, height); -#else - if (asmOptimization != AsmOptimization::ASM_OFF && NUM_HASH_BLOCKS <= 2) { - CryptoNightMultiHash<0x80000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, false, POW_WOW, NUM_HASH_BLOCKS>::hashPowV4_asm(input, size, output, scratchPad, height, asmOptimization); - } else { - CryptoNightMultiHash<0x80000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, false, POW_WOW, NUM_HASH_BLOCKS>::hashPowV4(input, size, output, scratchPad, height); - } -#endif - } else if (variant == PowVariant::POW_ALLOY) { - CryptoNightMultiHash<0x100000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, false, POW_ALLOY, NUM_HASH_BLOCKS>::hash(input, size, output, scratchPad); - } else if (variant == PowVariant::POW_XTL) { -#if defined(XMRIG_ARM) - CryptoNightMultiHash<0x80000, POW_XLT_V4_INDEX_SHIFT, MEMORY, 0x1FFFF0, false, POW_XTL, NUM_HASH_BLOCKS>::hashPowV2(input, size, output, scratchPad); -#else - if (asmOptimization == AsmOptimization::ASM_INTEL && NUM_HASH_BLOCKS == 1) { - CryptoNightMultiHash<0x80000, POW_XLT_V4_INDEX_SHIFT, MEMORY, 0x1FFFF0, false, POW_XTL, NUM_HASH_BLOCKS>::hashPowV2_asm(input, size, output, scratchPad, asmOptimization); - } else { - CryptoNightMultiHash<0x80000, POW_XLT_V4_INDEX_SHIFT, MEMORY, 0x1FFFF0, false, POW_XTL, NUM_HASH_BLOCKS>::hashPowV2(input, size, output, scratchPad); - } -#endif - } else if (variant == PowVariant::POW_FAST_2) { -#if defined(XMRIG_ARM) - CryptoNightMultiHash<0x40000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, false, POW_FAST_2, NUM_HASH_BLOCKS>::hashPowV3(input, size, output, scratchPad); -#else - if ((asmOptimization == AsmOptimization::ASM_INTEL && NUM_HASH_BLOCKS <= 2) || - (asmOptimization == AsmOptimization::ASM_RYZEN && NUM_HASH_BLOCKS == 1) || - (asmOptimization == AsmOptimization::ASM_BULLDOZER && NUM_HASH_BLOCKS == 1)) { - CryptoNightMultiHash<0x40000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, false, POW_FAST_2, NUM_HASH_BLOCKS>::hashPowV3_asm(input, size, output, scratchPad, asmOptimization); - } else { - CryptoNightMultiHash<0x40000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, false, POW_FAST_2, NUM_HASH_BLOCKS>::hashPowV3(input, size, output, scratchPad); - } -#endif - } else if (variant == PowVariant::POW_DOUBLE) { -#if defined(XMRIG_ARM) - CryptoNightMultiHash<0x100000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, false, POW_DOUBLE, NUM_HASH_BLOCKS>::hashPowV3(input, size, output, scratchPad); -#else - if ((asmOptimization == AsmOptimization::ASM_INTEL && NUM_HASH_BLOCKS <= 2) || - (asmOptimization == AsmOptimization::ASM_RYZEN && NUM_HASH_BLOCKS == 1) || - (asmOptimization == AsmOptimization::ASM_BULLDOZER && NUM_HASH_BLOCKS == 1)) { - CryptoNightMultiHash<0x100000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, false, POW_DOUBLE, NUM_HASH_BLOCKS>::hashPowV3_asm(input, size, output, scratchPad, asmOptimization); - } else { - CryptoNightMultiHash<0x100000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, false, POW_DOUBLE, NUM_HASH_BLOCKS>::hashPowV3(input, size, output, scratchPad); - } -#endif - } else if (variant == PowVariant::POW_ZELERIUS) { -#if defined(XMRIG_ARM) - CryptoNightMultiHash<0x60000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, false, POW_ZELERIUS, NUM_HASH_BLOCKS>::hashPowV3(input, size, output, scratchPad); -#else - if ((asmOptimization == AsmOptimization::ASM_INTEL && NUM_HASH_BLOCKS <= 2) || - (asmOptimization == AsmOptimization::ASM_RYZEN && NUM_HASH_BLOCKS == 1) || - (asmOptimization == AsmOptimization::ASM_BULLDOZER && NUM_HASH_BLOCKS == 1)) { - CryptoNightMultiHash<0x60000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, false, POW_ZELERIUS, NUM_HASH_BLOCKS>::hashPowV3_asm(input, size, output, scratchPad, asmOptimization); - } else { - CryptoNightMultiHash<0x60000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, false, POW_ZELERIUS, NUM_HASH_BLOCKS>::hashPowV3(input, size, output, scratchPad); - } -#endif - } else if (variant == PowVariant::POW_RWZ) { -#if defined(XMRIG_ARM) - CryptoNightMultiHash<0x60000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, false,POW_RWZ, NUM_HASH_BLOCKS>::hashPowV3(input, size, output, scratchPad); -#else - if ((asmOptimization != AsmOptimization::ASM_OFF && NUM_HASH_BLOCKS <= 2)) { - CryptoNightMultiHash<0x60000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, false, POW_RWZ, NUM_HASH_BLOCKS>::hashPowV3_asm(input, size, output, scratchPad, asmOptimization); - } else { - CryptoNightMultiHash<0x60000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, false, POW_RWZ, NUM_HASH_BLOCKS>::hashPowV3(input, size, output, scratchPad); - } -#endif - } else if (variant == PowVariant::POW_MSR) { -#if defined(XMRIG_ARM) - CryptoNightMultiHash<0x40000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, false, POW_MSR, NUM_HASH_BLOCKS>::hashPowV2(input, size, output, scratchPad); -#else - if (asmOptimization == AsmOptimization::ASM_INTEL && NUM_HASH_BLOCKS == 1) { - CryptoNightMultiHash<0x40000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, false, POW_MSR, NUM_HASH_BLOCKS>::hashPowV2_asm(input, size, output, scratchPad, asmOptimization); - } else { - CryptoNightMultiHash<0x40000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, false, POW_MSR, NUM_HASH_BLOCKS>::hashPowV2(input, size, output, scratchPad); - } -#endif - } else if (variant == PowVariant::POW_RTO) { -#if defined(XMRIG_ARM) - CryptoNightMultiHash<0x80000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, false, POW_RTO, NUM_HASH_BLOCKS>::hashLiteTube(input, size, output, scratchPad); -#else - if (asmOptimization == AsmOptimization::ASM_INTEL && NUM_HASH_BLOCKS == 1) { - CryptoNightMultiHash<0x80000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, false, POW_RTO, NUM_HASH_BLOCKS>::hashPowV2_asm(input, size, output, scratchPad, asmOptimization); - } else { - CryptoNightMultiHash<0x80000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, false, POW_RTO, NUM_HASH_BLOCKS>::hashLiteTube(input, size, output, scratchPad); - } -#endif - } else if (variant == PowVariant::POW_HOSP) { -#if defined(XMRIG_ARM) - CryptoNightMultiHash<0x80000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, false, POW_HOSP, NUM_HASH_BLOCKS>::hashLiteTube(input, size, output, scratchPad); -#else - if (asmOptimization == AsmOptimization::ASM_INTEL && NUM_HASH_BLOCKS == 1) { - CryptoNightMultiHash<0x80000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, false, POW_HOSP, NUM_HASH_BLOCKS>::hashPowV2_asm(input, size, output, scratchPad, asmOptimization); - } else { - CryptoNightMultiHash<0x80000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, false, POW_HOSP, NUM_HASH_BLOCKS>::hashLiteTube(input, size, output, scratchPad); - } -#endif - } else if (variant == PowVariant::POW_XFH) { - CryptoNightMultiHash<0x20000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, false, POW_XFH, NUM_HASH_BLOCKS>::hashHeavy(input, size, output, scratchPad); - } else if (variant == PowVariant::POW_CONCEAL) { - CryptoNightMultiHash<0x40000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, false, POW_CONCEAL, NUM_HASH_BLOCKS>::hash(input, size, output, scratchPad); - } else { - CryptoNightMultiHash<0x80000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, false, POW_V0, NUM_HASH_BLOCKS>::hash(input, size, output, scratchPad); - } -# endif -} - -template -static void cryptonight_softaes(AsmOptimization asmOptimization, uint64_t height, PowVariant variant, const uint8_t* input, size_t size, uint8_t* output, ScratchPad** scratchPad) { - if (variant == PowVariant::POW_V1) { -#if defined(XMRIG_ARM) - CryptoNightMultiHash<0x80000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, true, POW_V1, NUM_HASH_BLOCKS>::hashPowV2(input, size, output, scratchPad); -#else - if (asmOptimization == AsmOptimization::ASM_INTEL && NUM_HASH_BLOCKS == 1) { - CryptoNightMultiHash<0x80000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, true, POW_V1, NUM_HASH_BLOCKS>::hashPowV2_asm(input, size, output, scratchPad, asmOptimization); - } else { - CryptoNightMultiHash<0x80000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, true, POW_V1, NUM_HASH_BLOCKS>::hashPowV2(input, size, output, scratchPad); - } -#endif - } else if (variant == PowVariant::POW_V2) { -#if defined(XMRIG_ARM) - CryptoNightMultiHash<0x80000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, true, POW_V2, NUM_HASH_BLOCKS>::hashPowV3(input, size, output, scratchPad); -#else - if (asmOptimization == AsmOptimization::ASM_INTEL && NUM_HASH_BLOCKS == 1) { - CryptoNightMultiHash<0x80000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, true, POW_V2, NUM_HASH_BLOCKS>::hashPowV3_asm(input, size, output, scratchPad, asmOptimization); - } else { - CryptoNightMultiHash<0x80000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, true, POW_V2, NUM_HASH_BLOCKS>::hashPowV3(input, size, output, scratchPad); - } -#endif - } else if (variant == PowVariant::POW_V4) { -#if defined(XMRIG_ARM) - CryptoNightMultiHash<0x80000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, true, POW_V4, NUM_HASH_BLOCKS>::hashPowV4(input, size, output, scratchPad, height); -#else - if (asmOptimization != AsmOptimization::ASM_OFF && NUM_HASH_BLOCKS == 1) { - CryptoNightMultiHash<0x80000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, true, POW_V4, NUM_HASH_BLOCKS>::hashPowV4_asm(input, size, output, scratchPad, height, asmOptimization); - } else { - CryptoNightMultiHash<0x80000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, true, POW_V4, NUM_HASH_BLOCKS>::hashPowV4(input, size, output, scratchPad, height); - } -#endif - } else if (variant == PowVariant::POW_WOW) { -#if defined(XMRIG_ARM) - CryptoNightMultiHash<0x80000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, true, POW_WOW,NUM_HASH_BLOCKS>::hashPowV4(input, size, output, scratchPad, height); -#else - if (asmOptimization != AsmOptimization::ASM_OFF && NUM_HASH_BLOCKS == 1) { - CryptoNightMultiHash<0x80000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, true, POW_WOW, NUM_HASH_BLOCKS>::hashPowV4_asm(input, size, output, scratchPad, height, asmOptimization); - } else { - CryptoNightMultiHash<0x80000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, true, POW_WOW, NUM_HASH_BLOCKS>::hashPowV4(input, size, output, scratchPad, height); - } -#endif - } else if (variant == PowVariant::POW_FAST_2) { -#if defined(XMRIG_ARM) - CryptoNightMultiHash<0x40000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, true, POW_FAST_2, NUM_HASH_BLOCKS>::hashPowV3(input, size, output, scratchPad); -#else - if (asmOptimization == AsmOptimization::ASM_INTEL && NUM_HASH_BLOCKS == 1) { - CryptoNightMultiHash<0x40000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, true, POW_FAST_2, NUM_HASH_BLOCKS>::hashPowV3_asm(input, size, output, scratchPad, asmOptimization); - } else { - CryptoNightMultiHash<0x40000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, true, POW_FAST_2, NUM_HASH_BLOCKS>::hashPowV3(input, size, output, scratchPad); - } -#endif - } else if (variant == PowVariant::POW_DOUBLE) { -#if defined(XMRIG_ARM) - CryptoNightMultiHash<0x100000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, true, POW_DOUBLE, NUM_HASH_BLOCKS>::hashPowV3(input, size, output, scratchPad); -#else - if (asmOptimization == AsmOptimization::ASM_INTEL && NUM_HASH_BLOCKS == 1) { - CryptoNightMultiHash<0x100000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, true, POW_DOUBLE, NUM_HASH_BLOCKS>::hashPowV3_asm(input, size, output, scratchPad, asmOptimization); - } else { - CryptoNightMultiHash<0x100000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, true, POW_DOUBLE, NUM_HASH_BLOCKS>::hashPowV3(input, size, output, scratchPad); - } -#endif - } else if (variant == PowVariant::POW_ZELERIUS) { -#if defined(XMRIG_ARM) - CryptoNightMultiHash<0x60000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, true, POW_ZELERIUS, NUM_HASH_BLOCKS>::hashPowV3(input, size, output, scratchPad); -#else - if (asmOptimization == AsmOptimization::ASM_INTEL && NUM_HASH_BLOCKS == 1) { - CryptoNightMultiHash<0x60000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, true, POW_ZELERIUS, NUM_HASH_BLOCKS>::hashPowV3_asm(input, size, output, scratchPad, asmOptimization); - } else { - CryptoNightMultiHash<0x60000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, true, POW_ZELERIUS, NUM_HASH_BLOCKS>::hashPowV3(input, size, output, scratchPad); - } -#endif - } else if (variant == PowVariant::POW_RWZ) { - CryptoNightMultiHash<0x60000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, true, POW_RWZ, NUM_HASH_BLOCKS>::hashPowV3(input, size, output, scratchPad); - } else if (variant == PowVariant::POW_ALLOY) { - CryptoNightMultiHash<0x100000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, true, POW_ALLOY, NUM_HASH_BLOCKS>::hash(input, size, output, scratchPad); - } else if (variant == PowVariant::POW_XTL) { -#if defined(XMRIG_ARM) - CryptoNightMultiHash<0x80000, POW_XLT_V4_INDEX_SHIFT, MEMORY, 0x1FFFF0, true, POW_XTL, NUM_HASH_BLOCKS>::hashPowV2(input, size, output, scratchPad); -#else - if (asmOptimization == AsmOptimization::ASM_INTEL && NUM_HASH_BLOCKS == 1) { - CryptoNightMultiHash<0x80000, POW_XLT_V4_INDEX_SHIFT, MEMORY, 0x1FFFF0, true, POW_XTL, NUM_HASH_BLOCKS>::hashPowV2_asm(input, size, output, scratchPad, asmOptimization); - } else { - CryptoNightMultiHash<0x80000, POW_XLT_V4_INDEX_SHIFT, MEMORY, 0x1FFFF0, true, POW_XTL, NUM_HASH_BLOCKS>::hashPowV2(input, size, output, scratchPad); - } -#endif - } else if (variant == PowVariant::POW_MSR) { -#if defined(XMRIG_ARM) - CryptoNightMultiHash<0x40000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, true, POW_MSR, NUM_HASH_BLOCKS>::hashPowV2(input, size, output, scratchPad); -#else - if (asmOptimization == AsmOptimization::ASM_INTEL && NUM_HASH_BLOCKS == 1) { - CryptoNightMultiHash<0x40000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, true, POW_MSR, NUM_HASH_BLOCKS>::hashPowV2_asm(input, size, output, scratchPad, asmOptimization); - } else { - CryptoNightMultiHash<0x40000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, true, POW_MSR, NUM_HASH_BLOCKS>::hashPowV2(input, size, output, scratchPad); - } -#endif - } else if (variant == PowVariant::POW_RTO) { -#if defined(XMRIG_ARM) - CryptoNightMultiHash<0x80000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, true, POW_RTO, NUM_HASH_BLOCKS>::hashLiteTube(input, size, output, scratchPad); -#else - if (asmOptimization == AsmOptimization::ASM_INTEL && NUM_HASH_BLOCKS == 1) { - CryptoNightMultiHash<0x80000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, true, POW_RTO, NUM_HASH_BLOCKS>::hashPowV2_asm(input, size, output, scratchPad, asmOptimization); - } else { - CryptoNightMultiHash<0x80000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, true, POW_RTO, NUM_HASH_BLOCKS>::hashLiteTube(input, size, output, scratchPad); - } -#endif - } else if (variant == PowVariant::POW_HOSP) { -#if defined(XMRIG_ARM) - CryptoNightMultiHash<0x80000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, true, POW_HOSP, NUM_HASH_BLOCKS>::hashLiteTube(input, size, output, scratchPad); -#else - if (asmOptimization == AsmOptimization::ASM_INTEL && NUM_HASH_BLOCKS == 1) { - CryptoNightMultiHash<0x80000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, true, POW_HOSP, NUM_HASH_BLOCKS>::hashPowV2_asm(input, size, output, scratchPad, asmOptimization); - } else { - CryptoNightMultiHash<0x80000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, true, POW_HOSP, NUM_HASH_BLOCKS>::hashLiteTube(input, size, output, scratchPad); - } -#endif - } else if (variant == PowVariant::POW_XFH) { - CryptoNightMultiHash<0x20000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, true, POW_XFH, NUM_HASH_BLOCKS>::hashHeavy(input, size, output, scratchPad); - } else if (variant == PowVariant::POW_CONCEAL) { - CryptoNightMultiHash<0x40000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, true, POW_CONCEAL, NUM_HASH_BLOCKS>::hash(input, size, output, scratchPad); - } else { - CryptoNightMultiHash<0x80000, POW_DEFAULT_INDEX_SHIFT, MEMORY, 0x1FFFF0, true, POW_V0, NUM_HASH_BLOCKS>::hash(input, size, output, scratchPad); - } -} - -template -static void cryptonight_lite_aesni(AsmOptimization asmOptimization, uint64_t height, PowVariant variant, const uint8_t* input, size_t size, uint8_t* output, ScratchPad** scratchPad) { -# if !defined(XMRIG_ARMv7) - if (variant == PowVariant::POW_V1) { -#if defined(XMRIG_ARM) - CryptoNightMultiHash<0x40000, POW_DEFAULT_INDEX_SHIFT, MEMORY_LITE, 0xFFFF0, false, POW_V1, NUM_HASH_BLOCKS>::hashPowV2(input, size, output, scratchPad); -#else - if (asmOptimization == AsmOptimization::ASM_INTEL && NUM_HASH_BLOCKS == 1) { - CryptoNightMultiHash<0x40000, POW_DEFAULT_INDEX_SHIFT, MEMORY_LITE, 0xFFFF0, false, POW_V1, NUM_HASH_BLOCKS>::hashPowV2_asm(input, size, output, scratchPad, asmOptimization); - } else { - CryptoNightMultiHash<0x40000, POW_DEFAULT_INDEX_SHIFT, MEMORY_LITE, 0xFFFF0, false, POW_V1, NUM_HASH_BLOCKS>::hashPowV2(input, size, output, scratchPad); - } -#endif - } else if (variant == PowVariant::POW_TUBE) { - CryptoNightMultiHash<0x40000, POW_DEFAULT_INDEX_SHIFT, MEMORY_LITE, 0xFFFF0, false, POW_TUBE, NUM_HASH_BLOCKS>::hashLiteTube(input, size, output, scratchPad); - } else if (variant == PowVariant::POW_UPX) { -#if defined(XMRIG_ARM) - CryptoNightMultiHash<0x20000, POW_DEFAULT_INDEX_SHIFT, MEMORY_LITE, 0xFFFF0, false, POW_UPX, NUM_HASH_BLOCKS>::hashPowV2(input, size, output, scratchPad); -#else - if (asmOptimization == AsmOptimization::ASM_INTEL && NUM_HASH_BLOCKS == 1) { - CryptoNightMultiHash<0x20000, POW_DEFAULT_INDEX_SHIFT, MEMORY_LITE, 0xFFFF0, false, POW_UPX, NUM_HASH_BLOCKS>::hashPowV2_asm(input, size, output, scratchPad, asmOptimization); - } else { - CryptoNightMultiHash<0x20000, POW_DEFAULT_INDEX_SHIFT, MEMORY_LITE, 0xFFFF0, false, POW_UPX, NUM_HASH_BLOCKS>::hashPowV2(input, size, output, scratchPad); - } -#endif - } else { - CryptoNightMultiHash<0x40000, POW_DEFAULT_INDEX_SHIFT, MEMORY_LITE, 0xFFFF0, false, POW_V0, NUM_HASH_BLOCKS>::hash(input, size, output, scratchPad); - } -# endif -} - -template -static void cryptonight_lite_softaes(AsmOptimization asmOptimization, uint64_t height, PowVariant variant, const uint8_t* input, size_t size, uint8_t* output, ScratchPad** scratchPad) { - if (variant == PowVariant::POW_V1) { -#if defined(XMRIG_ARM) - CryptoNightMultiHash<0x40000, POW_DEFAULT_INDEX_SHIFT, MEMORY_LITE, 0xFFFF0, true, POW_V1, NUM_HASH_BLOCKS>::hashPowV2(input, size, output, scratchPad); -#else - if (asmOptimization == AsmOptimization::ASM_INTEL && NUM_HASH_BLOCKS == 1) { - CryptoNightMultiHash<0x40000, POW_DEFAULT_INDEX_SHIFT, MEMORY_LITE, 0xFFFF0, true, POW_V1, NUM_HASH_BLOCKS>::hashPowV2_asm(input, size, output, scratchPad, asmOptimization); - } else { - CryptoNightMultiHash<0x40000, POW_DEFAULT_INDEX_SHIFT, MEMORY_LITE, 0xFFFF0, true, POW_V1, NUM_HASH_BLOCKS>::hashPowV2(input, size, output, scratchPad); - } -#endif - } else if (variant == PowVariant::POW_TUBE) { - CryptoNightMultiHash<0x40000, POW_DEFAULT_INDEX_SHIFT, MEMORY_LITE, 0xFFFF0, true, POW_TUBE, NUM_HASH_BLOCKS>::hashLiteTube(input, size, output, scratchPad); - } else if (variant == PowVariant::POW_UPX) { -#if defined(XMRIG_ARM) - CryptoNightMultiHash<0x20000, POW_DEFAULT_INDEX_SHIFT, MEMORY_LITE, 0xFFFF0, true, POW_UPX, NUM_HASH_BLOCKS>::hashPowV2(input, size, output, scratchPad); -#else - if (asmOptimization == AsmOptimization::ASM_INTEL && NUM_HASH_BLOCKS == 1) { - CryptoNightMultiHash<0x20000, POW_DEFAULT_INDEX_SHIFT, MEMORY_LITE, 0xFFFF0, true, POW_UPX, NUM_HASH_BLOCKS>::hashPowV2_asm(input, size, output, scratchPad, asmOptimization); - } else { - CryptoNightMultiHash<0x20000, POW_DEFAULT_INDEX_SHIFT, MEMORY_LITE, 0xFFFF0, true, POW_UPX, NUM_HASH_BLOCKS>::hashPowV2(input, size, output, scratchPad); - } -#endif - } else { - CryptoNightMultiHash<0x40000, POW_DEFAULT_INDEX_SHIFT, MEMORY_LITE, 0xFFFF0, true, POW_V0, NUM_HASH_BLOCKS>::hash(input, size, output, scratchPad); - } -} - -template -static void cryptonight_super_lite_aesni(AsmOptimization asmOptimization, uint64_t height, PowVariant variant, const uint8_t* input, size_t size, uint8_t* output, ScratchPad** scratchPad) { - -} - -template -static void cryptonight_super_lite_softaes(AsmOptimization asmOptimization, uint64_t height, PowVariant variant, const uint8_t* input, size_t size, uint8_t* output, ScratchPad** scratchPad) { - -} - -template -static void cryptonight_ultra_lite_aesni(AsmOptimization asmOptimization, uint64_t height, PowVariant variant, const uint8_t* input, size_t size, uint8_t* output, ScratchPad** scratchPad) { -# if !defined(XMRIG_ARMv7) -#if defined(XMRIG_ARM) - CryptoNightMultiHash<0x10000, POW_DEFAULT_INDEX_SHIFT, MEMORY_ULTRA_LITE, 0x1FFF0, false, POW_TURTLE, NUM_HASH_BLOCKS>::hashPowV3(input, size, output, scratchPad); -#else - if ((asmOptimization == AsmOptimization::ASM_INTEL && NUM_HASH_BLOCKS <= 2) || - (asmOptimization == AsmOptimization::ASM_RYZEN && NUM_HASH_BLOCKS == 1) || - (asmOptimization == AsmOptimization::ASM_BULLDOZER && NUM_HASH_BLOCKS == 1)) { - CryptoNightMultiHash<0x10000, POW_DEFAULT_INDEX_SHIFT, MEMORY_ULTRA_LITE, 0x1FFF0, false, POW_TURTLE, NUM_HASH_BLOCKS>::hashPowV3_asm(input, size, output, scratchPad, asmOptimization); - } else { - CryptoNightMultiHash<0x10000, POW_DEFAULT_INDEX_SHIFT, MEMORY_ULTRA_LITE, 0x1FFF0, false, POW_TURTLE, NUM_HASH_BLOCKS>::hashPowV3(input, size, output, scratchPad); - } -#endif -# endif -} - -template -static void cryptonight_ultra_lite_softaes(AsmOptimization asmOptimization, uint64_t height, PowVariant variant, const uint8_t* input, size_t size, uint8_t* output, ScratchPad** scratchPad) { -#if defined(XMRIG_ARM) - CryptoNightMultiHash<0x10000, POW_DEFAULT_INDEX_SHIFT, MEMORY_ULTRA_LITE, 0x1FFF0, true, POW_TURTLE, NUM_HASH_BLOCKS>::hashPowV3(input, size, output, scratchPad); -#else - if (asmOptimization == AsmOptimization::ASM_INTEL && NUM_HASH_BLOCKS == 1) { - CryptoNightMultiHash<0x10000, POW_DEFAULT_INDEX_SHIFT, MEMORY_ULTRA_LITE, 0x1FFF0, true, POW_TURTLE, NUM_HASH_BLOCKS>::hashPowV3_asm(input, size, output, scratchPad, asmOptimization); - } else { - CryptoNightMultiHash<0x10000, POW_DEFAULT_INDEX_SHIFT, MEMORY_ULTRA_LITE, 0x1FFF0, true, POW_TURTLE, NUM_HASH_BLOCKS>::hashPowV3(input, size, output, scratchPad); - } -#endif -} - -template -static void cryptonight_extreme_lite_aesni(AsmOptimization asmOptimization, uint64_t height, PowVariant variant, const uint8_t* input, size_t size, uint8_t* output, ScratchPad** scratchPad) { -# if !defined(XMRIG_ARMv7) -#if defined(XMRIG_ARM) - CryptoNightMultiHash<0x4000, POW_DEFAULT_INDEX_SHIFT, MEMORY_EXTREME_LITE, 0x1FFF0, false, POW_UPX2, NUM_HASH_BLOCKS>::hashPowV3(input, size, output, scratchPad); -#else - if ((asmOptimization != AsmOptimization::ASM_OFF && NUM_HASH_BLOCKS <= 2)) { - CryptoNightMultiHash<0x4000, POW_DEFAULT_INDEX_SHIFT, MEMORY_EXTREME_LITE, 0x1FFF0, false, POW_UPX2, NUM_HASH_BLOCKS>::hashPowV3_asm(input, size, output, scratchPad, asmOptimization); - } else { - CryptoNightMultiHash<0x4000, POW_DEFAULT_INDEX_SHIFT, MEMORY_EXTREME_LITE, 0x1FFF0, false, POW_UPX2, NUM_HASH_BLOCKS>::hashPowV3(input, size, output, scratchPad); - } -#endif -# endif -} - -template -static void cryptonight_extreme_lite_softaes(AsmOptimization asmOptimization, uint64_t height, PowVariant variant, const uint8_t* input, size_t size, uint8_t* output, ScratchPad** scratchPad) { - CryptoNightMultiHash<0x4000, POW_DEFAULT_INDEX_SHIFT, MEMORY_EXTREME_LITE, 0x1FFF0, true, POW_UPX2, NUM_HASH_BLOCKS>::hashPowV3(input, size, output, scratchPad); -} - -template -static void cryptonight_heavy_aesni(AsmOptimization asmOptimization, uint64_t height, PowVariant variant, const uint8_t* input, size_t size, uint8_t* output, ScratchPad** scratchPad) { -# if !defined(XMRIG_ARMv7) - if (variant == PowVariant::POW_XHV) { - CryptoNightMultiHash<0x40000, POW_DEFAULT_INDEX_SHIFT, MEMORY_HEAVY, 0x3FFFF0, false, POW_XHV, NUM_HASH_BLOCKS>::hashHeavy(input, size, output, scratchPad); - } - else if (variant == PowVariant::POW_TUBE) { - CryptoNightMultiHash<0x40000, POW_DEFAULT_INDEX_SHIFT, MEMORY_HEAVY, 0x3FFFF0, false, POW_TUBE, NUM_HASH_BLOCKS>::hashHeavyTube(input, size, output, scratchPad); - } - else { - CryptoNightMultiHash<0x40000, POW_DEFAULT_INDEX_SHIFT, MEMORY_HEAVY, 0x3FFFF0, false, POW_V0, NUM_HASH_BLOCKS>::hashHeavy(input, size, output, scratchPad); - } -# endif -} - -template -static void cryptonight_heavy_softaes(AsmOptimization asmOptimization, uint64_t height, PowVariant variant, const uint8_t* input, size_t size, uint8_t* output, ScratchPad** scratchPad) { - if (variant == PowVariant::POW_XHV) { - CryptoNightMultiHash<0x40000, POW_DEFAULT_INDEX_SHIFT, MEMORY_HEAVY, 0x3FFFF0, true, POW_XHV, NUM_HASH_BLOCKS>::hashHeavy(input, size, output, scratchPad); - } - else if (variant == PowVariant::POW_TUBE) { - CryptoNightMultiHash<0x40000, POW_DEFAULT_INDEX_SHIFT, MEMORY_HEAVY, 0x3FFFF0, true, POW_TUBE, NUM_HASH_BLOCKS>::hashHeavyTube(input, size, output, scratchPad); - } - else { - CryptoNightMultiHash<0x40000, POW_DEFAULT_INDEX_SHIFT, MEMORY_HEAVY, 0x3FFFF0, true, POW_V0, NUM_HASH_BLOCKS>::hashHeavy(input, size, output, scratchPad); - } -} - -template -static void argon2(AsmOptimization asmOptimization, uint64_t height, PowVariant variant, const uint8_t* input, size_t size, uint8_t* output, ScratchPad** scratchPad) -{ - if (variant == PowVariant::POW_ARGON2_CHUKWA || variant == POW_TURTLE) { - argon2id_hash_raw(3, MEMORY_ARGON2_512/1024, 1, input, size, input, 16, output, 32, scratchPad[0]->memory, MEMORY_ARGON2_512); - } - - if (variant == PowVariant::POW_ARGON2_WRKZ) { - argon2id_hash_raw(4, MEMORY_ARGON2_256/1024, 1, input, size, input, 16, output, 32, scratchPad[0]->memory, MEMORY_ARGON2_256); - } -} - -void (*hash_ctx[MAX_NUM_HASH_BLOCKS])(AsmOptimization asmOptimization, uint64_t height, PowVariant variant, const uint8_t* input, size_t size, uint8_t* output, ScratchPad** scratchPad); - -template -void setHashMethods(Options::Algo algo, bool aesni) -{ - switch (algo) { - case Options::ALGO_CRYPTONIGHT: - if (aesni) { - hash_ctx[HASH_FACTOR - 1] = cryptonight_aesni; - } else { - hash_ctx[HASH_FACTOR - 1] = cryptonight_softaes; - } - break; - - case Options::ALGO_CRYPTONIGHT_LITE: - if (aesni) { - hash_ctx[HASH_FACTOR - 1] = cryptonight_lite_aesni; - } else { - hash_ctx[HASH_FACTOR - 1] = cryptonight_lite_softaes; - } - break; - - case Options::ALGO_CRYPTONIGHT_SUPERLITE: - if (aesni) { - hash_ctx[HASH_FACTOR - 1] = cryptonight_super_lite_aesni; - } else { - hash_ctx[HASH_FACTOR - 1] = cryptonight_super_lite_softaes; - } - break; - - case Options::ALGO_CRYPTONIGHT_ULTRALITE: - if (aesni) { - hash_ctx[HASH_FACTOR - 1] = cryptonight_ultra_lite_aesni; - } else { - hash_ctx[HASH_FACTOR - 1] = cryptonight_ultra_lite_softaes; - } - break; - - case Options::ALGO_CRYPTONIGHT_EXTREMELITE: - if (aesni) { - hash_ctx[HASH_FACTOR - 1] = cryptonight_extreme_lite_aesni; - } else { - hash_ctx[HASH_FACTOR - 1] = cryptonight_extreme_lite_softaes; - } - break; - - case Options::ALGO_CRYPTONIGHT_HEAVY: - if (aesni) { - hash_ctx[HASH_FACTOR - 1] = cryptonight_heavy_aesni; - } else { - hash_ctx[HASH_FACTOR - 1] = cryptonight_heavy_softaes; - } - break; - case Options::ALGO_ARGON2_256: - case Options::ALGO_ARGON2_512: - hash_ctx[HASH_FACTOR - 1] = argon2; - break; - } - // next iteration - setHashMethods(algo, aesni); -} - -template <> -void setHashMethods<0>(Options::Algo algo, bool aesni) -{ - // template recursion abort -}; - -bool HashSelector::init(Options::Algo algo, bool aesni) -{ - for (int i = 0; i < 256; ++i) - { - const uint64_t index = (((i >> POW_DEFAULT_INDEX_SHIFT) & 6) | (i & 1)) << 1; - const uint64_t index_xtl = (((i >> POW_XLT_V4_INDEX_SHIFT) & 6) | (i & 1)) << 1; - - variant1_table[i] = i ^ ((0x75310 >> index) & 0x30); - variant_xtl_table[i] = i ^ ((0x75310 >> index_xtl) & 0x30); - } - - setHashMethods(algo, aesni); - - return Options::i()->skipSelfCheck() ? true : selfCheck(algo); -} - -void HashSelector::hash(size_t factor, AsmOptimization asmOptimization, uint64_t height, PowVariant variant, const uint8_t* input, size_t size, uint8_t* output, ScratchPad** scratchPad) -{ - hash_ctx[factor-1](asmOptimization, height, variant, input, size, output, scratchPad); -} - -bool HashSelector::selfCheck(Options::Algo algo) -{ - if (hash_ctx[0] == nullptr - #if MAX_NUM_HASH_BLOCKS > 1 - || hash_ctx[1] == nullptr - #endif - #if MAX_NUM_HASH_BLOCKS > 2 - || hash_ctx[2] == nullptr - #endif - #if MAX_NUM_HASH_BLOCKS > 3 - || hash_ctx[3] == nullptr - #endif - #if MAX_NUM_HASH_BLOCKS > 4 - || hash_ctx[4] == nullptr - #endif - ) - { - return false; - } - - uint8_t output[160]; - - ScratchPad* scratchPads [MAX_NUM_HASH_BLOCKS]; - - for (size_t i = 0; i < MAX_NUM_HASH_BLOCKS; ++i) { - ScratchPad* scratchPad = static_cast(_mm_malloc(sizeof(ScratchPad), 4096)); - scratchPad->memory = (uint8_t *) _mm_malloc(MEMORY * 6, 16); - - auto* p = reinterpret_cast(Mem::allocateExecutableMemory(0x4000)); - scratchPad->generated_code = reinterpret_cast(p); - scratchPad->generated_code_double = reinterpret_cast(p + 0x2000); - - scratchPad->generated_code_data.variant = PowVariant::LAST_ITEM; - scratchPad->generated_code_data.height = (uint64_t)(-1); - scratchPad->generated_code_double_data = scratchPad->generated_code_data; - - scratchPads[i] = scratchPad; - } - - bool result = true; - bool resultLite = true; - bool resultSuperLite = true; - bool resultUltraLite = true; - bool resultExtremeLite = true; - bool resultHeavy = true; - bool resultArgon2 = true; - - AsmOptimization asmOptimization = Options::i()->asmOptimization(); - - if (algo == Options::ALGO_CRYPTONIGHT_HEAVY) { - // cn-heavy - - hash_ctx[0](asmOptimization, 0, PowVariant::POW_V0, test_input, 76, output, scratchPads); - resultHeavy = resultHeavy && memcmp(output, test_output_heavy, 32) == 0; - - #if MAX_NUM_HASH_BLOCKS > 1 - hash_ctx[1](asmOptimization, 0, PowVariant::POW_V0, test_input, 76, output, scratchPads); - resultHeavy = resultHeavy && memcmp(output, test_output_heavy, 64) == 0; - #endif - - #if MAX_NUM_HASH_BLOCKS > 2 - hash_ctx[2](asmOptimization, 0, PowVariant::POW_V0, test_input, 76, output, scratchPads); - resultHeavy = resultHeavy && memcmp(output, test_output_heavy, 96) == 0; - #endif - - // cn-heavy haven - - hash_ctx[0](asmOptimization, 0, PowVariant::POW_XHV, test_input, 76, output, scratchPads); - resultHeavy = resultHeavy && memcmp(output, test_output_heavy_haven, 32) == 0; - - #if MAX_NUM_HASH_BLOCKS > 1 - hash_ctx[1](asmOptimization, 0, PowVariant::POW_XHV, test_input, 76, output, scratchPads); - resultHeavy = resultHeavy && memcmp(output, test_output_heavy_haven, 64) == 0; - #endif - - #if MAX_NUM_HASH_BLOCKS > 2 - hash_ctx[2](asmOptimization, 0, PowVariant::POW_XHV, test_input, 76, output, scratchPads); - resultHeavy = resultHeavy && memcmp(output, test_output_heavy_haven, 96) == 0; - #endif - - // cn-heavy bittube - - hash_ctx[0](asmOptimization, 0, PowVariant::POW_TUBE, test_input, 76, output, scratchPads); - resultHeavy = resultHeavy && memcmp(output, test_output_heavy_tube, 32) == 0; - - #if MAX_NUM_HASH_BLOCKS > 1 - hash_ctx[1](asmOptimization, 0, PowVariant::POW_TUBE, test_input, 76, output, scratchPads); - resultHeavy = resultHeavy && memcmp(output, test_output_heavy_tube, 64) == 0; - #endif - - #if MAX_NUM_HASH_BLOCKS > 2 - hash_ctx[2](asmOptimization, 0, PowVariant::POW_TUBE, test_input, 76, output, scratchPads); - resultHeavy = resultHeavy && memcmp(output, test_output_heavy_tube, 96) == 0; - #endif - - } else if (algo == Options::ALGO_CRYPTONIGHT_LITE) { - // cn-lite v0 - - hash_ctx[0](asmOptimization, 0, PowVariant::POW_V0, test_input, 76, output, scratchPads); - resultLite = resultLite && memcmp(output, test_output_v0_lite, 32) == 0; - - #if MAX_NUM_HASH_BLOCKS > 1 - hash_ctx[1](asmOptimization, 0, PowVariant::POW_V0, test_input, 76, output, scratchPads); - resultLite = resultLite && memcmp(output, test_output_v0_lite, 64) == 0; - #endif - - #if MAX_NUM_HASH_BLOCKS > 2 - hash_ctx[2](asmOptimization, 0, PowVariant::POW_V0, test_input, 76, output, scratchPads); - resultLite = resultLite && memcmp(output, test_output_v0_lite, 96) == 0; - #endif - - #if MAX_NUM_HASH_BLOCKS > 3 - hash_ctx[3](asmOptimization, 0, PowVariant::POW_V0, test_input, 76, output, scratchPads); - resultLite = resultLite && memcmp(output, test_output_v0_lite, 128) == 0; - #endif - - #if MAX_NUM_HASH_BLOCKS > 4 - hash_ctx[4](asmOptimization, 0, PowVariant::POW_V0, test_input, 76, output, scratchPads); - resultLite = resultLite && memcmp(output, test_output_v0_lite, 160) == 0; - #endif - - // cn-lite v7 tests - - hash_ctx[0](asmOptimization, 0, PowVariant::POW_V1, test_input, 76, output, scratchPads); - resultLite = resultLite && memcmp(output, test_output_v1_lite, 32) == 0; - - #if MAX_NUM_HASH_BLOCKS > 1 - hash_ctx[1](asmOptimization, 0, PowVariant::POW_V1, test_input, 76, output, scratchPads); - resultLite = resultLite && memcmp(output, test_output_v1_lite, 64) == 0; - #endif - - #if MAX_NUM_HASH_BLOCKS > 2 - hash_ctx[2](asmOptimization, 0, PowVariant::POW_V1, test_input, 76, output, scratchPads); - resultLite = resultLite && memcmp(output, test_output_v1_lite, 96) == 0; - #endif - - #if MAX_NUM_HASH_BLOCKS > 3 - hash_ctx[3](asmOptimization, 0, PowVariant::POW_V1, test_input, 76, output, scratchPads); - resultLite = resultLite && memcmp(output, test_output_v1_lite, 128) == 0; - #endif - - #if MAX_NUM_HASH_BLOCKS > 4 - hash_ctx[4](asmOptimization, 0, PowVariant::POW_V1, test_input, 76, output, scratchPads); - resultLite = resultLite && memcmp(output, test_output_v1_lite, 160) == 0; - #endif - - // cn-lite ibpc tests - - hash_ctx[0](asmOptimization, 0, PowVariant::POW_TUBE, test_input, 76, output, scratchPads); - resultLite = resultLite && memcmp(output, test_output_ipbc_lite, 32) == 0; - - #if MAX_NUM_HASH_BLOCKS > 1 - hash_ctx[1](asmOptimization, 0, PowVariant::POW_TUBE, test_input, 76, output, scratchPads); - resultLite = resultLite && memcmp(output, test_output_ipbc_lite, 64) == 0; - #endif - - #if MAX_NUM_HASH_BLOCKS > 2 - hash_ctx[2](asmOptimization, 0, PowVariant::POW_TUBE, test_input, 76, output, scratchPads); - resultLite = resultLite && memcmp(output, test_output_ipbc_lite, 96) == 0; - #endif - - #if MAX_NUM_HASH_BLOCKS > 3 - hash_ctx[3](asmOptimization, 0, PowVariant::POW_TUBE, test_input, 76, output, scratchPads); - resultLite = resultLite && memcmp(output, test_output_ipbc_lite, 128) == 0; - #endif - - #if MAX_NUM_HASH_BLOCKS > 4 - hash_ctx[4](asmOptimization, 0, PowVariant::POW_TUBE, test_input, 76, output, scratchPads); - resultLite = resultLite && memcmp(output, test_output_ipbc_lite, 160) == 0; - #endif - - // cn-lite upx - - hash_ctx[0](asmOptimization, 0, PowVariant::POW_UPX, test_input, 76, output, scratchPads); - resultLite = resultLite && memcmp(output, test_output_upx, 32) == 0; - - } else if (algo == Options::ALGO_CRYPTONIGHT_SUPERLITE) { - return false; - } else if (algo == Options::ALGO_CRYPTONIGHT_ULTRALITE) { - // cn ultralite (cnv8 + turtle) - - hash_ctx[0](asmOptimization, 0, PowVariant::POW_TURTLE, test_input, 76, output, scratchPads); - resultUltraLite = resultUltraLite && memcmp(output, test_output_turtle, 32) == 0; - - #if MAX_NUM_HASH_BLOCKS > 1 - hash_ctx[1](asmOptimization, 0, PowVariant::POW_TURTLE, test_input, 76, output, scratchPads); - resultUltraLite = resultUltraLite && memcmp(output, test_output_turtle, 64) == 0; - #endif - } else if (algo == Options::ALGO_CRYPTONIGHT_EXTREMELITE) { - // cn extremelite (cnv8 + upx2) - - hash_ctx[0](asmOptimization, 0, PowVariant::POW_UPX2, test_input, 76, output, scratchPads); - resultExtremeLite = resultExtremeLite && memcmp(output, test_output_upx2, 32) == 0; - - #if MAX_NUM_HASH_BLOCKS > 1 - hash_ctx[1](asmOptimization, 0, PowVariant::POW_UPX2, test_input, 76, output, scratchPads); - resultExtremeLite = resultExtremeLite && memcmp(output, test_output_upx2, 64) == 0; - #endif - } else if (algo == Options::ALGO_CRYPTONIGHT) { - // cn v0 aka orignal - hash_ctx[0](asmOptimization, 0, PowVariant::POW_V0,test_input, 76, output, scratchPads); - result = result && memcmp(output, test_output_v0, 32) == 0; - - #if MAX_NUM_HASH_BLOCKS > 1 - hash_ctx[1](asmOptimization, 0, PowVariant::POW_V0, test_input, 76, output, scratchPads); - result = result && memcmp(output, test_output_v0, 64) == 0; - #endif - - #if MAX_NUM_HASH_BLOCKS > 2 - hash_ctx[2](asmOptimization, 0, PowVariant::POW_V0, test_input, 76, output, scratchPads); - result = result && memcmp(output, test_output_v0, 96) == 0; - #endif - - #if MAX_NUM_HASH_BLOCKS > 3 - hash_ctx[3](asmOptimization, 0, PowVariant::POW_V0, test_input, 76, output, scratchPads); - result = result && memcmp(output, test_output_v0, 128) == 0; - #endif - - #if MAX_NUM_HASH_BLOCKS > 4 - hash_ctx[4](asmOptimization, 0, PowVariant::POW_V0, test_input, 76, output, scratchPads); - result = result && memcmp(output, test_output_v0, 160) == 0; - #endif - - // cn v7 aka cnv1 - - hash_ctx[0](asmOptimization, 0, PowVariant::POW_V1, test_input, 76, output, scratchPads); - result = result && memcmp(output, test_output_v1, 32) == 0; - - #if MAX_NUM_HASH_BLOCKS > 1 - hash_ctx[1](asmOptimization, 0, PowVariant::POW_V1, test_input, 76, output, scratchPads); - result = result && memcmp(output, test_output_v1, 64) == 0; - #endif - - #if MAX_NUM_HASH_BLOCKS > 2 - hash_ctx[2](asmOptimization, 0, PowVariant::POW_V1, test_input, 76, output, scratchPads); - result = result && memcmp(output, test_output_v1, 96) == 0; - #endif - - #if MAX_NUM_HASH_BLOCKS > 3 - hash_ctx[3](asmOptimization, 0, PowVariant::POW_V1, test_input, 76, output, scratchPads); - result = result && memcmp(output, test_output_v1, 128) == 0; - #endif - - #if MAX_NUM_HASH_BLOCKS > 4 - hash_ctx[4](asmOptimization, 0, PowVariant::POW_V1, test_input, 76, output, scratchPads); - result = result && memcmp(output, test_output_v1, 160) == 0; - #endif - - // cnv7 + xtl - - hash_ctx[0](asmOptimization, 0, PowVariant::POW_XTL,test_input, 76, output, scratchPads); - result = result && memcmp(output, test_output_xtl, 32) == 0; - - // cnv7 + msr aka cn-fast - - hash_ctx[0](asmOptimization, 0, PowVariant::POW_MSR,test_input, 76, output, scratchPads); - result = result && memcmp(output, test_output_msr, 32) == 0; - - // cnv7 + alloy - - hash_ctx[0](asmOptimization, 0, PowVariant::POW_ALLOY,test_input, 76, output, scratchPads); - result = result && memcmp(output, test_output_alloy, 32) == 0; - - // cnv7 + hosp/rto - - hash_ctx[0](asmOptimization, 0, PowVariant::POW_HOSP,test_input, 76, output, scratchPads); - result = result && memcmp(output, test_output_hosp, 32) == 0; - - // cnv8 aka cnv2 - - hash_ctx[0](asmOptimization, 0, PowVariant::POW_V2, test_input, 76, output, scratchPads); - result = result && memcmp(output, test_output_v2, 32) == 0; - - #if MAX_NUM_HASH_BLOCKS > 1 - hash_ctx[1](asmOptimization, 0, PowVariant::POW_V2, test_input, 76, output, scratchPads); - result = result && memcmp(output, test_output_v2, 64) == 0; - #endif - - #if MAX_NUM_HASH_BLOCKS > 2 - hash_ctx[2](asmOptimization, 0, PowVariant::POW_V2, test_input, 76, output, scratchPads); - result = result && memcmp(output, test_output_v2, 96) == 0; - #endif - - #if MAX_NUM_HASH_BLOCKS > 3 - hash_ctx[3](asmOptimization, 0, PowVariant::POW_V2, test_input, 76, output, scratchPads); - result = result && memcmp(output, test_output_v2, 128) == 0; - #endif - - #if MAX_NUM_HASH_BLOCKS > 4 - hash_ctx[4](asmOptimization, 0, PowVariant::POW_V2, test_input, 76, output, scratchPads); - result = result && memcmp(output, test_output_v2, 160) == 0; - #endif - - // cn conceal - -#if !defined(XMRIG_ARM) - - hash_ctx[0](asmOptimization, 0, PowVariant::POW_CONCEAL, test_input, 76, output, scratchPads); - result = result && memcmp(output, test_output_conceal, 32) == 0; - - #if MAX_NUM_HASH_BLOCKS > 1 - hash_ctx[1](asmOptimization, 0, PowVariant::POW_CONCEAL, test_input, 76, output, scratchPads); - result = result && memcmp(output, test_output_conceal, 64) == 0; - #endif - - #if MAX_NUM_HASH_BLOCKS > 2 - hash_ctx[2](asmOptimization, 0, PowVariant::POW_CONCEAL, test_input, 76, output, scratchPads); - result = result && memcmp(output, test_output_conceal, 96) == 0; - #endif - - #if MAX_NUM_HASH_BLOCKS > 3 - hash_ctx[3](asmOptimization, 0, PowVariant::POW_CONCEAL, test_input, 76, output, scratchPads); - result = result && memcmp(output, test_output_conceal, 128) == 0; - #endif - - #if MAX_NUM_HASH_BLOCKS > 4 - hash_ctx[4](asmOptimization, 0, PowVariant::POW_CONCEAL, test_input, 76, output, scratchPads); - result = result && memcmp(output, test_output_conceal, 160) == 0; - #endif - -#endif - - // cn xfh aka cn-heavy-superfast - - hash_ctx[0](asmOptimization, 0, PowVariant::POW_XFH, test_input, 76, output, scratchPads); - result = result && memcmp(output, test_output_xfh, 32) == 0; - - // cnv8 + xtl aka cn-fast2 - - hash_ctx[0](asmOptimization, 0, PowVariant::POW_FAST_2, test_input, 76, output, scratchPads); - result = result && memcmp(output, test_output_xtl_v9, 32) == 0; - - #if MAX_NUM_HASH_BLOCKS > 1 - hash_ctx[1](asmOptimization, 0, PowVariant::POW_FAST_2, test_input, 76, output, scratchPads); - result = result && memcmp(output, test_output_xtl_v9, 64) == 0; - #endif - - // cnv8 + xcash - - hash_ctx[0](asmOptimization, 0, PowVariant::POW_DOUBLE, test_input, 76, output, scratchPads); - result = result && memcmp(output, test_output_xcash, 32) == 0; - - // cnv8 + zelerius - - hash_ctx[0](asmOptimization, 0, PowVariant::POW_ZELERIUS, test_input, 76, output, scratchPads); - result = result && memcmp(output, test_output_zelerius, 32) == 0; - - // cnv8 + rwz - - hash_ctx[0](asmOptimization, 0, PowVariant::POW_RWZ, test_input, 76, output, scratchPads); - result = result && memcmp(output, test_output_rwz, 32) == 0; - - #if MAX_NUM_HASH_BLOCKS > 1 - hash_ctx[1](asmOptimization, 0, PowVariant::POW_RWZ, test_input, 76, output, scratchPads); - result = result && memcmp(output, test_output_rwz, 64) == 0; - #endif - - // cnv9 aka cnv4 aka cnv5 aka cnr - - hash_ctx[0](asmOptimization, 10000, PowVariant::POW_V4, test_input, 76, output, scratchPads); - result = result && memcmp(output, test_output_v4, 32) == 0; - - #if MAX_NUM_HASH_BLOCKS > 1 - hash_ctx[1](asmOptimization, 10000, PowVariant::POW_V4, test_input, 76, output, scratchPads); - result = result && memcmp(output, test_output_v4, 64) == 0; - #endif - - #if MAX_NUM_HASH_BLOCKS > 2 - hash_ctx[2](asmOptimization, 10000, PowVariant::POW_V4, test_input, 76, output, scratchPads); - result = result && memcmp(output, test_output_v4, 96) == 0; - #endif - - #if MAX_NUM_HASH_BLOCKS > 3 - hash_ctx[3](asmOptimization, 10000, PowVariant::POW_V4, test_input, 76, output, scratchPads); - result = result && memcmp(output, test_output_v4, 128) == 0; - #endif - - #if MAX_NUM_HASH_BLOCKS > 4 - hash_ctx[4](asmOptimization, 10000, PowVariant::POW_V4, test_input, 76, output, scratchPads); - result = result && memcmp(output, test_output_v4, 160) == 0; - #endif - - hash_ctx[0](asmOptimization, 10001, PowVariant::POW_V4, test_input, 76, output, scratchPads); - result = result && memcmp(output, test_output_v4_1, 32) == 0; - - hash_ctx[0](asmOptimization, 10002, PowVariant::POW_V4, test_input, 76, output, scratchPads); - result = result && memcmp(output, test_output_v4_2, 32) == 0; - } - else if (algo == Options::ALGO_ARGON2_256) { - argon2_select_impl(NULL, NULL); - - hash_ctx[0](asmOptimization, 0, PowVariant::POW_ARGON2_WRKZ, argon2_test_input, 76, output, scratchPads); - resultArgon2 = resultArgon2 && memcmp(output, argon2_wrkz_test_out, 32) == 0; - } - else if (algo == Options::ALGO_ARGON2_512) { - argon2_select_impl(NULL, NULL); - - hash_ctx[0](asmOptimization, 0, PowVariant::POW_ARGON2_CHUKWA, argon2_test_input, 76, output, scratchPads); - resultArgon2 = resultArgon2 && memcmp(output, argon2_chukwa_test_out, 32) == 0; - } - - for (size_t i = 0; i < MAX_NUM_HASH_BLOCKS; ++i) { - _mm_free(scratchPads[i]->memory); - _mm_free(scratchPads[i]); - } - - return result && resultLite && resultSuperLite && resultUltraLite && resultExtremeLite && resultHeavy && resultArgon2; -} diff --git a/src/Cpu_mac.cpp b/src/crypto/argon2/Hash.h similarity index 51% rename from src/Cpu_mac.cpp rename to src/crypto/argon2/Hash.h index 6bb787e1..b337f193 100644 --- a/src/Cpu_mac.cpp +++ b/src/crypto/argon2/Hash.h @@ -4,8 +4,9 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , * * 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 @@ -21,43 +22,31 @@ * along with this program. If not, see . */ - -#include -#include -#include -#include -#include +#ifndef XMRIG_ARGON2_HASH_H +#define XMRIG_ARGON2_HASH_H -#include "CpuImpl.h" -#include "Cpu.h" +#include "3rdparty/argon2.h" +#include "crypto/cn/CryptoNight.h" +#include "crypto/common/Algorithm.h" -void CpuImpl::init() + +namespace xmrig { namespace argon2 { + + +template +inline void single_hash(const uint8_t *__restrict__ input, size_t size, uint8_t *__restrict__ output, cryptonight_ctx **__restrict__ ctx, uint64_t) { -# ifdef XMRIG_NO_LIBCPUID - m_totalThreads = sysconf(_SC_NPROCESSORS_CONF); -# endif - - initCommon(); + if (ALGO == Algorithm::AR2_CHUKWA) { + argon2id_hash_raw_ex(3, 512, 1, input, size, input, 16, output, 32, ctx[0]->memory); + } + else if (ALGO == Algorithm::AR2_WRKZ) { + argon2id_hash_raw_ex(4, 256, 1, input, size, input, 16, output, 32, ctx[0]->memory); + } } -int CpuImpl::setThreadAffinity(size_t threadId, int64_t affinityMask) -{ - int cpuId = -1; - if (affinityMask != -1L) { - cpuId = Cpu::getAssignedCpuId(threadId, affinityMask); - } else { - cpuId = static_cast(threadId); - } +}} // namespace xmrig::argon2 - if (cpuId > -1) { - thread_port_t mach_thread; - thread_affinity_policy_data_t policy = {static_cast(cpuId)}; - mach_thread = pthread_mach_thread_np(pthread_self()); - thread_policy_set(mach_thread, THREAD_AFFINITY_POLICY, (thread_policy_t) & policy, 1); - } - - return cpuId; -} +#endif /* XMRIG_ARGON2_HASH_H */ diff --git a/src/net/SubmitResult.cpp b/src/crypto/argon2/Impl.cpp similarity index 54% rename from src/net/SubmitResult.cpp rename to src/crypto/argon2/Impl.cpp index 2e81017c..d0f267f9 100644 --- a/src/net/SubmitResult.cpp +++ b/src/crypto/argon2/Impl.cpp @@ -4,8 +4,9 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , * * 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,23 +23,40 @@ */ -#include +#include "3rdparty/argon2.h" +#include "base/tools/String.h" +#include "crypto/argon2/Impl.h" -#include "net/SubmitResult.h" + +namespace xmrig { -SubmitResult::SubmitResult(int64_t seq, uint32_t diff, uint64_t actualDiff) : - seq(seq), - diff(diff), - actualDiff(actualDiff), - elapsed(0) +static bool selected = false; +static String implName; + + +} // namespace xmrig + + +bool xmrig::argon2::Impl::select(const String &nameHint) { - start = uv_hrtime(); + if (!selected) { + if (nameHint.isEmpty() || argon2_select_impl_by_name(nameHint) == 0) { + argon2_select_impl(); + } + + selected = true; + implName = argon2_get_impl_name(); + + return true; + } + + return false; } -void SubmitResult::done() +const xmrig::String &xmrig::argon2::Impl::name() { - elapsed = (uv_hrtime() - start) / 1000000; + return implName; } diff --git a/src/interfaces/ILogBackend.h b/src/crypto/argon2/Impl.h similarity index 66% rename from src/interfaces/ILogBackend.h rename to src/crypto/argon2/Impl.h index 31d264de..006dbd35 100644 --- a/src/interfaces/ILogBackend.h +++ b/src/crypto/argon2/Impl.h @@ -4,8 +4,9 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , * * 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 @@ -21,27 +22,28 @@ * along with this program. If not, see . */ -#ifndef __ILOGBACKEND_H__ -#define __ILOGBACKEND_H__ +#ifndef XMRIG_ARGON2_IMPL_H +#define XMRIG_ARGON2_IMPL_H -#include +namespace xmrig { -class ILogBackend +class String; + + +namespace argon2 { + + +class Impl { public: -# ifdef APP_DEBUG - constexpr static const size_t kBufferSize = 1024; -# else - constexpr static const size_t kBufferSize = 512; -# endif - - virtual ~ILogBackend() {} - - virtual void message(int level, const char* fmt, va_list args) = 0; - virtual void text(const char* fmt, va_list args) = 0; + static bool select(const String &nameHint); + static const String &name(); }; -#endif // __ILOGBACKEND_H__ +}} // namespace xmrig::argon2 + + +#endif /* XMRIG_ARGON2_IMPL_H */ diff --git a/src/crypto/asm/cn_main_loop.S b/src/crypto/asm/cn_main_loop.S deleted file mode 100644 index ac1e24c3..00000000 --- a/src/crypto/asm/cn_main_loop.S +++ /dev/null @@ -1,534 +0,0 @@ -#define ALIGN .align -.intel_syntax noprefix -#ifdef __APPLE__ -# define FN_PREFIX(fn) _ ## fn -.text -#else -# define FN_PREFIX(fn) fn -.section .text -#endif -.global FN_PREFIX(cnv1_main_loop_sandybridge_asm) -.global FN_PREFIX(cnv1_main_loop_lite_sandybridge_asm) -.global FN_PREFIX(cnv1_main_loop_fast_sandybridge_asm) -.global FN_PREFIX(cnv1_main_loop_upx_sandybridge_asm) -.global FN_PREFIX(cnv1_main_loop_rto_sandybridge_asm) - -.global FN_PREFIX(cnv2_main_loop_ivybridge_asm) -.global FN_PREFIX(cnv2_main_loop_ryzen_asm) -.global FN_PREFIX(cnv2_main_loop_bulldozer_asm) -.global FN_PREFIX(cnv2_double_main_loop_sandybridge_asm) - -.global FN_PREFIX(cnv2_main_loop_fastv2_ivybridge_asm) -.global FN_PREFIX(cnv2_main_loop_fastv2_ryzen_asm) -.global FN_PREFIX(cnv2_main_loop_fastv2_bulldozer_asm) -.global FN_PREFIX(cnv2_double_main_loop_fastv2_sandybridge_asm) - -.global FN_PREFIX(cnv2_main_loop_ultralite_ivybridge_asm) -.global FN_PREFIX(cnv2_main_loop_ultralite_ryzen_asm) -.global FN_PREFIX(cnv2_main_loop_ultralite_bulldozer_asm) -.global FN_PREFIX(cnv2_double_main_loop_ultralite_sandybridge_asm) - -.global FN_PREFIX(cnv2_main_loop_xcash_ivybridge_asm) -.global FN_PREFIX(cnv2_main_loop_xcash_ryzen_asm) -.global FN_PREFIX(cnv2_main_loop_xcash_bulldozer_asm) -.global FN_PREFIX(cnv2_double_main_loop_xcash_sandybridge_asm) - -.global FN_PREFIX(cnv2_main_loop_zelerius_ivybridge_asm) -.global FN_PREFIX(cnv2_main_loop_zelerius_ryzen_asm) -.global FN_PREFIX(cnv2_main_loop_zelerius_bulldozer_asm) -.global FN_PREFIX(cnv2_double_main_loop_zelerius_sandybridge_asm) - -.global FN_PREFIX(cnv2_main_loop_rwz_original_all_asm) -.global FN_PREFIX(cnv2_double_main_loop_rwz_original_all_asm) - -.global FN_PREFIX(cnv2_main_loop_rwz_upx2_all_asm) -.global FN_PREFIX(cnv2_double_main_loop_rwz_upx2_all_asm) - -.global FN_PREFIX(cnv1_main_loop_soft_aes_sandybridge_asm) -.global FN_PREFIX(cnv1_main_loop_lite_soft_aes_sandybridge_asm) -.global FN_PREFIX(cnv1_main_loop_fast_soft_aes_sandybridge_asm) -.global FN_PREFIX(cnv1_main_loop_upx_soft_aes_sandybridge_asm) -.global FN_PREFIX(cnv1_main_loop_rto_soft_aes_sandybridge_asm) - -.global FN_PREFIX(cnv2_main_loop_soft_aes_sandybridge_asm) -.global FN_PREFIX(cnv2_main_loop_fastv2_soft_aes_sandybridge_asm) -.global FN_PREFIX(cnv2_main_loop_ultralite_soft_aes_sandybridge_asm) -.global FN_PREFIX(cnv2_main_loop_xcash_soft_aes_sandybridge_asm) -.global FN_PREFIX(cnv2_main_loop_zelerius_soft_aes_sandybridge_asm) - -#ifdef __APPLE__ -ALIGN 16 -#else -ALIGN 64 -#endif -FN_PREFIX(cnv1_main_loop_sandybridge_asm): - sub rsp, 48 - mov rcx, rdi - #include "cnv1_main_loop_sandybridge.inc" - add rsp, 48 - ret 0 - -#ifdef __APPLE__ -ALIGN 16 -#else -ALIGN 64 -#endif -FN_PREFIX(cnv1_main_loop_lite_sandybridge_asm): - sub rsp, 48 - mov rcx, rdi - #include "cnv1_main_loop_lite_sandybridge.inc" - add rsp, 48 - ret 0 - -#ifdef __APPLE__ -ALIGN 16 -#else -ALIGN 64 -#endif -FN_PREFIX(cnv1_main_loop_fast_sandybridge_asm): - sub rsp, 48 - mov rcx, rdi - #include "cnv1_main_loop_fast_sandybridge.inc" - add rsp, 48 - ret 0 - -#ifdef __APPLE__ -ALIGN 16 -#else -ALIGN 64 -#endif -FN_PREFIX(cnv1_main_loop_upx_sandybridge_asm): - sub rsp, 48 - mov rcx, rdi - #include "cnv1_main_loop_upx_sandybridge.inc" - add rsp, 48 - ret 0 - -#ifdef __APPLE__ -ALIGN 16 -#else -ALIGN 64 -#endif -FN_PREFIX(cnv1_main_loop_rto_sandybridge_asm): - sub rsp, 48 - mov rcx, rdi - #include "cnv1_main_loop_rto_sandybridge.inc" - add rsp, 48 - ret 0 - -#ifdef __APPLE__ -ALIGN 16 -#else -ALIGN 64 -#endif -FN_PREFIX(cnv2_main_loop_ivybridge_asm): - sub rsp, 48 - mov rcx, rdi - #include "cnv2_main_loop_ivybridge.inc" - add rsp, 48 - ret 0 - -#ifdef __APPLE__ -ALIGN 16 -#else -ALIGN 64 -#endif -FN_PREFIX(cnv2_main_loop_ryzen_asm): - sub rsp, 48 - mov rcx, rdi - #include "cnv2_main_loop_ryzen.inc" - add rsp, 48 - ret 0 - -#ifdef __APPLE__ -ALIGN 16 -#else -ALIGN 64 -#endif -FN_PREFIX(cnv2_main_loop_bulldozer_asm): - sub rsp, 48 - mov rcx, rdi - #include "cnv2_main_loop_bulldozer.inc" - add rsp, 48 - ret 0 - -#ifdef __APPLE__ -ALIGN 16 -#else -ALIGN 64 -#endif -FN_PREFIX(cnv2_double_main_loop_sandybridge_asm): - sub rsp, 48 - mov rcx, rdi - mov rdx, rsi - #include "cnv2_double_main_loop_sandybridge.inc" - add rsp, 48 - ret 0 - -#ifdef __APPLE__ -ALIGN 16 -#else -ALIGN 64 -#endif -FN_PREFIX(cnv2_main_loop_fastv2_ivybridge_asm): - sub rsp, 48 - mov rcx, rdi - #include "cnv2_main_loop_fastv2_ivybridge.inc" - add rsp, 48 - ret 0 - -#ifdef __APPLE__ -ALIGN 16 -#else -ALIGN 64 -#endif -FN_PREFIX(cnv2_main_loop_fastv2_ryzen_asm): - sub rsp, 48 - mov rcx, rdi - #include "cnv2_main_loop_fastv2_ryzen.inc" - add rsp, 48 - ret 0 - -#ifdef __APPLE__ -ALIGN 16 -#else -ALIGN 64 -#endif -FN_PREFIX(cnv2_main_loop_fastv2_bulldozer_asm): - sub rsp, 48 - mov rcx, rdi - #include "cnv2_main_loop_fastv2_bulldozer.inc" - add rsp, 48 - ret 0 - -#ifdef __APPLE__ -ALIGN 16 -#else -ALIGN 64 -#endif -FN_PREFIX(cnv2_double_main_loop_fastv2_sandybridge_asm): - sub rsp, 48 - mov rcx, rdi - mov rdx, rsi - #include "cnv2_double_main_loop_fastv2_sandybridge.inc" - add rsp, 48 - ret 0 - -#ifdef __APPLE__ -ALIGN 16 -#else -ALIGN 64 -#endif -FN_PREFIX(cnv2_main_loop_ultralite_ivybridge_asm): - sub rsp, 48 - mov rcx, rdi - #include "cnv2_main_loop_ultralite_ivybridge.inc" - add rsp, 48 - ret 0 - -#ifdef __APPLE__ -ALIGN 16 -#else -ALIGN 64 -#endif -FN_PREFIX(cnv2_main_loop_ultralite_ryzen_asm): - sub rsp, 48 - mov rcx, rdi - #include "cnv2_main_loop_ultralite_ryzen.inc" - add rsp, 48 - ret 0 - -#ifdef __APPLE__ -ALIGN 16 -#else -ALIGN 64 -#endif -FN_PREFIX(cnv2_main_loop_ultralite_bulldozer_asm): - sub rsp, 48 - mov rcx, rdi - #include "cnv2_main_loop_ultralite_bulldozer.inc" - add rsp, 48 - ret 0 - -#ifdef __APPLE__ -ALIGN 16 -#else -ALIGN 64 -#endif -FN_PREFIX(cnv2_double_main_loop_ultralite_sandybridge_asm): - sub rsp, 48 - mov rcx, rdi - mov rdx, rsi - #include "cnv2_double_main_loop_ultralite_sandybridge.inc" - add rsp, 48 - ret 0 - -#ifdef __APPLE__ -ALIGN 16 -#else -ALIGN 64 -#endif -FN_PREFIX(cnv2_main_loop_xcash_ivybridge_asm): - sub rsp, 48 - mov rcx, rdi - #include "cnv2_main_loop_xcash_ivybridge.inc" - add rsp, 48 - ret 0 - -#ifdef __APPLE__ -ALIGN 16 -#else -ALIGN 64 -#endif -FN_PREFIX(cnv2_main_loop_xcash_ryzen_asm): - sub rsp, 48 - mov rcx, rdi - #include "cnv2_main_loop_xcash_ryzen.inc" - add rsp, 48 - ret 0 - -#ifdef __APPLE__ -ALIGN 16 -#else -ALIGN 64 -#endif -FN_PREFIX(cnv2_main_loop_xcash_bulldozer_asm): - sub rsp, 48 - mov rcx, rdi - #include "cnv2_main_loop_xcash_bulldozer.inc" - add rsp, 48 - ret 0 - -#ifdef __APPLE__ -ALIGN 16 -#else -ALIGN 64 -#endif -FN_PREFIX(cnv2_double_main_loop_xcash_sandybridge_asm): - sub rsp, 48 - mov rcx, rdi - mov rdx, rsi - #include "cnv2_double_main_loop_xcash_sandybridge.inc" - add rsp, 48 - ret 0 - -#ifdef __APPLE__ -ALIGN 16 -#else -ALIGN 64 -#endif -FN_PREFIX(cnv2_main_loop_zelerius_ivybridge_asm): - sub rsp, 48 - mov rcx, rdi - #include "cnv2_main_loop_zelerius_ivybridge.inc" - add rsp, 48 - ret 0 - -#ifdef __APPLE__ -ALIGN 16 -#else -ALIGN 64 -#endif -FN_PREFIX(cnv2_main_loop_zelerius_ryzen_asm): - sub rsp, 48 - mov rcx, rdi - #include "cnv2_main_loop_zelerius_ryzen.inc" - add rsp, 48 - ret 0 - -#ifdef __APPLE__ -ALIGN 16 -#else -ALIGN 64 -#endif -FN_PREFIX(cnv2_main_loop_zelerius_bulldozer_asm): - sub rsp, 48 - mov rcx, rdi - #include "cnv2_main_loop_zelerius_bulldozer.inc" - add rsp, 48 - ret 0 - -#ifdef __APPLE__ -ALIGN 16 -#else -ALIGN 64 -#endif -FN_PREFIX(cnv2_double_main_loop_zelerius_sandybridge_asm): - sub rsp, 48 - mov rcx, rdi - mov rdx, rsi - #include "cnv2_double_main_loop_zelerius_sandybridge.inc" - add rsp, 48 - ret 0 - -#ifdef __APPLE__ -ALIGN 16 -#else -ALIGN 64 -#endif -FN_PREFIX(cnv2_main_loop_rwz_original_all_asm): - sub rsp, 48 - mov rcx, rdi - #include "cnv2_main_loop_rwz_original_all.inc" - add rsp, 48 - ret 0 - -#ifdef __APPLE__ -ALIGN 16 -#else -ALIGN 64 -#endif -FN_PREFIX(cnv2_double_main_loop_rwz_original_all_asm): - sub rsp, 48 - mov rcx, rdi - mov rdx, rsi - #include "cnv2_double_main_loop_rwz_original_all.inc" - add rsp, 48 - ret 0 - -#ifdef __APPLE__ -ALIGN 16 -#else -ALIGN 64 -#endif -FN_PREFIX(cnv2_main_loop_rwz_upx2_all_asm): - sub rsp, 48 - mov rcx, rdi - #include "cnv2_main_loop_rwz_upx2_all.inc" - add rsp, 48 - ret 0 - -#ifdef __APPLE__ -ALIGN 16 -#else -ALIGN 64 -#endif -FN_PREFIX(cnv2_double_main_loop_rwz_upx2_all_asm): - sub rsp, 48 - mov rcx, rdi - mov rdx, rsi - #include "cnv2_double_main_loop_rwz_upx2_all.inc" - add rsp, 48 - ret 0 - -#ifdef __APPLE__ -ALIGN 16 -#else -ALIGN 64 -#endif -FN_PREFIX(cnv1_main_loop_soft_aes_sandybridge_asm): - sub rsp, 48 - mov rcx, rdi - #include "cnv1_main_loop_soft_aes_sandybridge.inc" - add rsp, 48 - ret 0 - -#ifdef __APPLE__ -ALIGN 16 -#else -ALIGN 64 -#endif -FN_PREFIX(cnv1_main_loop_lite_soft_aes_sandybridge_asm): - sub rsp, 48 - mov rcx, rdi - #include "cnv1_main_loop_lite_soft_aes_sandybridge.inc" - add rsp, 48 - ret 0 - -#ifdef __APPLE__ -ALIGN 16 -#else -ALIGN 64 -#endif -FN_PREFIX(cnv1_main_loop_fast_soft_aes_sandybridge_asm): - sub rsp, 48 - mov rcx, rdi - #include "cnv1_main_loop_fast_soft_aes_sandybridge.inc" - add rsp, 48 - ret 0 - -#ifdef __APPLE__ -ALIGN 16 -#else -ALIGN 64 -#endif -FN_PREFIX(cnv1_main_loop_upx_soft_aes_sandybridge_asm): - sub rsp, 48 - mov rcx, rdi - #include "cnv1_main_loop_upx_soft_aes_sandybridge.inc" - add rsp, 48 - ret 0 - -#ifdef __APPLE__ -ALIGN 16 -#else -ALIGN 64 -#endif -FN_PREFIX(cnv1_main_loop_rto_soft_aes_sandybridge_asm): - sub rsp, 48 - mov rcx, rdi - #include "cnv1_main_loop_rto_soft_aes_sandybridge.inc" - add rsp, 48 - ret 0 - -#ifdef __APPLE__ -ALIGN 16 -#else -ALIGN 64 -#endif -FN_PREFIX(cnv2_main_loop_soft_aes_sandybridge_asm): - sub rsp, 48 - mov rcx, rdi - #include "cnv2_main_loop_soft_aes_sandybridge.inc" - add rsp, 48 - ret 0 - -#ifdef __APPLE__ -ALIGN 16 -#else -ALIGN 64 -#endif -FN_PREFIX(cnv2_main_loop_fastv2_soft_aes_sandybridge_asm): - sub rsp, 48 - mov rcx, rdi - #include "cnv2_main_loop_fastv2_soft_aes_sandybridge.inc" - add rsp, 48 - ret 0 - - -#ifdef __APPLE__ -ALIGN 16 -#else -ALIGN 64 -#endif -FN_PREFIX(cnv2_main_loop_ultralite_soft_aes_sandybridge_asm): - sub rsp, 48 - mov rcx, rdi - #include "cnv2_main_loop_ultralite_soft_aes_sandybridge.inc" - add rsp, 48 - ret 0 - - -#ifdef __APPLE__ -ALIGN 16 -#else -ALIGN 64 -#endif -FN_PREFIX(cnv2_main_loop_xcash_soft_aes_sandybridge_asm): - sub rsp, 48 - mov rcx, rdi - #include "cnv2_main_loop_xcash_soft_aes_sandybridge.inc" - add rsp, 48 - ret 0 - -#ifdef __APPLE__ -ALIGN 16 -#else -ALIGN 64 -#endif -FN_PREFIX(cnv2_main_loop_zelerius_soft_aes_sandybridge_asm): - sub rsp, 48 - mov rcx, rdi - #include "cnv2_main_loop_zelerius_soft_aes_sandybridge.inc" - add rsp, 48 - ret 0 \ No newline at end of file diff --git a/src/crypto/asm/cnv1_main_loop_rto_sandybridge.inc b/src/crypto/asm/cnv1_main_loop_rto_sandybridge.inc deleted file mode 100644 index c4a3df06..00000000 --- a/src/crypto/asm/cnv1_main_loop_rto_sandybridge.inc +++ /dev/null @@ -1,75 +0,0 @@ - mov QWORD PTR [rsp+8], rbx - mov QWORD PTR [rsp+16], rbp - mov QWORD PTR [rsp+24], rsi - mov QWORD PTR [rsp+32], rdi - push r14 - push r15 - mov rax, QWORD PTR [rcx+48] - mov ebp, 524288 - xor rax, QWORD PTR [rcx+16] - mov rdx, QWORD PTR [rcx+56] - xor rdx, QWORD PTR [rcx+24] - mov r8, QWORD PTR [rcx+32] - xor r8, QWORD PTR [rcx] - movq xmm3, rax - mov rax, QWORD PTR [rcx+256] - mov rdi, QWORD PTR [rcx+40] - movq xmm0, rdx - xor rdi, QWORD PTR [rcx+8] - mov rdx, r8 - mov r15, QWORD PTR [rcx+264] - and edx, 2097136 - mov r14, QWORD PTR [rax+35] - xor r14, QWORD PTR [rcx+192] - mov rsi, QWORD PTR [rcx+224] - punpcklqdq xmm3, xmm0 - movdqu xmm2, XMMWORD PTR [rdx+rsi] - - #ifdef __APPLE__ - ALIGN 16 - #else - ALIGN 64 - #endif -cnv1_main_loop_rto_sandybridge: - movq xmm0, rdi - movq xmm1, r8 - punpcklqdq xmm1, xmm0 - aesenc xmm2, xmm1 - movq r10, xmm2 - mov r9d, r10d - and r9d, 2097136 - add r9, rsi - movdqa xmm0, xmm2 - pxor xmm0, xmm3 - movdqa xmm3, xmm2 - movdqu XMMWORD PTR [rdx+rsi], xmm0 - psrldq xmm0, 11 - movq rax, xmm0 - movzx eax, al - movzx eax, BYTE PTR [rax+r15] - mov BYTE PTR [rsi+rdx+11], al - mov rbx, QWORD PTR [r9] - mov r11, QWORD PTR [r9+8] - mov rax, rbx - mul r10 - add r8, rdx - mov QWORD PTR [r9], r8 - add rdi, rax - mov rax, r14 - xor rax, rdi - xor rax, r8 - mov QWORD PTR [r9+8], rax - xor r8, rbx - mov rdx, r8 - and edx, 2097136 - movdqu xmm2, XMMWORD PTR [rdx+rsi] - xor rdi, r11 - dec ebp - jne cnv1_main_loop_rto_sandybridge - - mov rbx, QWORD PTR [rsp+24] - mov rbp, QWORD PTR [rsp+32] - mov rsi, QWORD PTR [rsp+40] - mov rdi, QWORD PTR [rsp+48] - pop r15 - pop r14 diff --git a/src/crypto/asm/cnv1_main_loop_rto_soft_aes_sandybridge.inc b/src/crypto/asm/cnv1_main_loop_rto_soft_aes_sandybridge.inc deleted file mode 100644 index 43b3488f..00000000 --- a/src/crypto/asm/cnv1_main_loop_rto_soft_aes_sandybridge.inc +++ /dev/null @@ -1,167 +0,0 @@ - push rbx - push rbp - push rsi - push rdi - push r12 - push r13 - push r14 - push r15 - sub rsp, 72 - - movaps XMMWORD PTR [rsp], xmm6 - movaps XMMWORD PTR [rsp+16], xmm7 - movaps XMMWORD PTR [rsp+32], xmm8 - movaps XMMWORD PTR [rsp+48], xmm9 - - mov rax, QWORD PTR [rcx+48] - xor rax, QWORD PTR [rcx+16] - mov rdx, QWORD PTR [rcx+56] - xor rdx, QWORD PTR [rcx+24] - mov r8, QWORD PTR [rcx+32] - xor r8, QWORD PTR [rcx] - movq xmm4, rax - mov rax, QWORD PTR [rcx+256] - mov r13, QWORD PTR [rcx+40] - movq xmm0, rdx - xor r13, QWORD PTR [rcx+8] - mov rdx, r8 - mov rdi, QWORD PTR [rcx+224] - and edx, 2097136 - mov rax, QWORD PTR [rax+35] - xor rax, QWORD PTR [rcx+192] - movq xmm5, rax - movq xmm8, rdi - punpcklqdq xmm4, xmm0 - mov QWORD PTR [rsp+64], rdx - - movq xmm6, rcx - mov rax, QWORD PTR [rcx+264] - movq xmm7, rax - - mov eax, 524288 - - #ifdef __APPLE__ - ALIGN 16 - #else - ALIGN 64 - #endif -cnv1_main_loop_rto_soft_aes_sandybridge: - movq xmm9, rax - mov r12, QWORD PTR [rcx+272] - mov esi, DWORD PTR [rdx+rdi] - mov r10d, DWORD PTR [rdx+rdi+4] - mov ebp, DWORD PTR [rdx+rdi+12] - mov r14d, DWORD PTR [rdx+rdi+8] - mov rdx, QWORD PTR [rsp+64] - movzx ecx, sil - shr esi, 8 - mov r15d, DWORD PTR [r12+rcx*4] - movzx ecx, r10b - shr r10d, 8 - mov edi, DWORD PTR [r12+rcx*4] - movzx ecx, r14b - shr r14d, 8 - mov ebx, DWORD PTR [r12+rcx*4] - movzx ecx, bpl - shr ebp, 8 - mov r9d, DWORD PTR [r12+rcx*4] - movzx ecx, r10b - shr r10d, 8 - xor r15d, DWORD PTR [r12+rcx*4+1024] - movzx ecx, r14b - shr r14d, 8 - mov eax, r14d - shr eax, 8 - xor edi, DWORD PTR [r12+rcx*4+1024] - add eax, 256 - movzx ecx, bpl - shr ebp, 8 - xor ebx, DWORD PTR [r12+rcx*4+1024] - movzx ecx, sil - shr esi, 8 - xor r9d, DWORD PTR [r12+rcx*4+1024] - add r12, 2048 - movzx ecx, r10b - shr r10d, 8 - add r10d, 256 - mov r11d, DWORD PTR [r12+rax*4] - xor r11d, DWORD PTR [r12+rcx*4] - xor r11d, r9d - movzx ecx, sil - mov r10d, DWORD PTR [r12+r10*4] - shr esi, 8 - add esi, 256 - xor r10d, DWORD PTR [r12+rcx*4] - movzx ecx, bpl - xor r10d, ebx - shr ebp, 8 - add ebp, 256 - movd xmm1, r11d - mov r9d, DWORD PTR [r12+rcx*4] - xor r9d, DWORD PTR [r12+rsi*4] - mov eax, DWORD PTR [r12+rbp*4] - xor r9d, edi - movq rdi, xmm8 - movzx ecx, r14b - movd xmm0, r10d - movd xmm2, r9d - punpckldq xmm2, xmm1 - movq xmm1, r8 - xor eax, DWORD PTR [r12+rcx*4] - xor eax, r15d - movd xmm3, eax - movq rax, xmm7 - punpckldq xmm3, xmm0 - movq xmm0, r13 - punpcklqdq xmm1, xmm0 - punpckldq xmm3, xmm2 - pxor xmm3, xmm1 - movq r9, xmm3 - mov r10d, r9d - and r10d, 2097136 - movdqa xmm0, xmm3 - pxor xmm0, xmm4 - movdqu XMMWORD PTR [rdx+rdi], xmm0 - psrldq xmm0, 11 - movq rcx, xmm0 - movzx ecx, cl - mov cl, BYTE PTR [rcx+rax] - mov BYTE PTR [rdi+rdx+11], cl - mov rbx, QWORD PTR [r10+rdi] - mov rcx, r9 - lea r9, QWORD PTR [r10+rdi] - mov r11, QWORD PTR [r9+8] - mov rax, rbx - movdqa xmm4, xmm3 - mul rcx - movq rcx, xmm6 - add r8, rdx - add r13, rax - movq rax, xmm5 - xor rax, r13 - mov QWORD PTR [r9], r8 - xor rax, r8 - xor r8, rbx - mov QWORD PTR [r9+8], rax - movq rax, xmm9 - mov rdx, r8 - xor r13, r11 - and edx, 2097136 - mov QWORD PTR [rsp+64], rdx - sub eax, 1 - jne cnv1_main_loop_rto_soft_aes_sandybridge - - movaps xmm6, XMMWORD PTR [rsp] - movaps xmm7, XMMWORD PTR [rsp+16] - movaps xmm8, XMMWORD PTR [rsp+32] - movaps xmm9, XMMWORD PTR [rsp+48] - - add rsp, 72 - pop r15 - pop r14 - pop r13 - pop r12 - pop rdi - pop rsi - pop rbp - pop rbx diff --git a/src/crypto/asm/cnv1_main_loop_sandybridge.inc.in b/src/crypto/asm/cnv1_main_loop_sandybridge.inc.in deleted file mode 100644 index 6619b23e..00000000 --- a/src/crypto/asm/cnv1_main_loop_sandybridge.inc.in +++ /dev/null @@ -1,74 +0,0 @@ - mov QWORD PTR [rsp+8], rbx - mov QWORD PTR [rsp+16], rbp - mov QWORD PTR [rsp+24], rsi - mov QWORD PTR [rsp+32], rdi - push r14 - push r15 - mov rax, QWORD PTR [rcx+48] - mov ebp, ${ITERATIONS} - xor rax, QWORD PTR [rcx+16] - mov rdx, QWORD PTR [rcx+56] - xor rdx, QWORD PTR [rcx+24] - mov r8, QWORD PTR [rcx+32] - xor r8, QWORD PTR [rcx] - movq xmm3, rax - mov rax, QWORD PTR [rcx+256] - mov rdi, QWORD PTR [rcx+40] - movq xmm0, rdx - xor rdi, QWORD PTR [rcx+8] - mov rdx, r8 - mov r15, QWORD PTR [rcx+264] - and edx, ${MASK} - mov r14, QWORD PTR [rax+35] - xor r14, QWORD PTR [rcx+192] - mov rsi, QWORD PTR [rcx+224] - punpcklqdq xmm3, xmm0 - movdqu xmm2, XMMWORD PTR [rdx+rsi] - - #ifdef __APPLE__ - ALIGN 16 - #else - ALIGN 64 - #endif -cnv1_main_loop_${ALGO}_sandybridge: - movq xmm0, rdi - movq xmm1, r8 - punpcklqdq xmm1, xmm0 - aesenc xmm2, xmm1 - movq r10, xmm2 - mov r9d, r10d - and r9d, ${MASK} - add r9, rsi - movdqa xmm0, xmm2 - pxor xmm0, xmm3 - movdqa xmm3, xmm2 - movdqu XMMWORD PTR [rdx+rsi], xmm0 - psrldq xmm0, 11 - movq rax, xmm0 - movzx eax, al - movzx eax, BYTE PTR [rax+r15] - mov BYTE PTR [rsi+rdx+11], al - mov rbx, QWORD PTR [r9] - mov r11, QWORD PTR [r9+8] - mov rax, rbx - mul r10 - add r8, rdx - mov QWORD PTR [r9], r8 - add rdi, rax - mov rax, r14 - xor rax, rdi - mov QWORD PTR [r9+8], rax - xor r8, rbx - mov rdx, r8 - and edx, ${MASK} - movdqu xmm2, XMMWORD PTR [rdx+rsi] - xor rdi, r11 - dec ebp - jne cnv1_main_loop_${ALGO}_sandybridge - - mov rbx, QWORD PTR [rsp+24] - mov rbp, QWORD PTR [rsp+32] - mov rsi, QWORD PTR [rsp+40] - mov rdi, QWORD PTR [rsp+48] - pop r15 - pop r14 diff --git a/src/crypto/asm/cnv1_main_loop_soft_aes_sandybridge.inc.in b/src/crypto/asm/cnv1_main_loop_soft_aes_sandybridge.inc.in deleted file mode 100644 index c8d8ea59..00000000 --- a/src/crypto/asm/cnv1_main_loop_soft_aes_sandybridge.inc.in +++ /dev/null @@ -1,166 +0,0 @@ - push rbx - push rbp - push rsi - push rdi - push r12 - push r13 - push r14 - push r15 - sub rsp, 72 - - movaps XMMWORD PTR [rsp], xmm6 - movaps XMMWORD PTR [rsp+16], xmm7 - movaps XMMWORD PTR [rsp+32], xmm8 - movaps XMMWORD PTR [rsp+48], xmm9 - - mov rax, QWORD PTR [rcx+48] - xor rax, QWORD PTR [rcx+16] - mov rdx, QWORD PTR [rcx+56] - xor rdx, QWORD PTR [rcx+24] - mov r8, QWORD PTR [rcx+32] - xor r8, QWORD PTR [rcx] - movq xmm4, rax - mov rax, QWORD PTR [rcx+256] - mov r13, QWORD PTR [rcx+40] - movq xmm0, rdx - xor r13, QWORD PTR [rcx+8] - mov rdx, r8 - mov rdi, QWORD PTR [rcx+224] - and edx, ${MASK} - mov rax, QWORD PTR [rax+35] - xor rax, QWORD PTR [rcx+192] - movq xmm5, rax - movq xmm8, rdi - punpcklqdq xmm4, xmm0 - mov QWORD PTR [rsp+64], rdx - - movq xmm6, rcx - mov rax, QWORD PTR [rcx+264] - movq xmm7, rax - - mov eax, ${ITERATIONS} - - #ifdef __APPLE__ - ALIGN 16 - #else - ALIGN 64 - #endif -cnv1_main_loop_${ALGO}_soft_aes_sandybridge: - movq xmm9, rax - mov r12, QWORD PTR [rcx+272] - mov esi, DWORD PTR [rdx+rdi] - mov r10d, DWORD PTR [rdx+rdi+4] - mov ebp, DWORD PTR [rdx+rdi+12] - mov r14d, DWORD PTR [rdx+rdi+8] - mov rdx, QWORD PTR [rsp+64] - movzx ecx, sil - shr esi, 8 - mov r15d, DWORD PTR [r12+rcx*4] - movzx ecx, r10b - shr r10d, 8 - mov edi, DWORD PTR [r12+rcx*4] - movzx ecx, r14b - shr r14d, 8 - mov ebx, DWORD PTR [r12+rcx*4] - movzx ecx, bpl - shr ebp, 8 - mov r9d, DWORD PTR [r12+rcx*4] - movzx ecx, r10b - shr r10d, 8 - xor r15d, DWORD PTR [r12+rcx*4+1024] - movzx ecx, r14b - shr r14d, 8 - mov eax, r14d - shr eax, 8 - xor edi, DWORD PTR [r12+rcx*4+1024] - add eax, 256 - movzx ecx, bpl - shr ebp, 8 - xor ebx, DWORD PTR [r12+rcx*4+1024] - movzx ecx, sil - shr esi, 8 - xor r9d, DWORD PTR [r12+rcx*4+1024] - add r12, 2048 - movzx ecx, r10b - shr r10d, 8 - add r10d, 256 - mov r11d, DWORD PTR [r12+rax*4] - xor r11d, DWORD PTR [r12+rcx*4] - xor r11d, r9d - movzx ecx, sil - mov r10d, DWORD PTR [r12+r10*4] - shr esi, 8 - add esi, 256 - xor r10d, DWORD PTR [r12+rcx*4] - movzx ecx, bpl - xor r10d, ebx - shr ebp, 8 - add ebp, 256 - movd xmm1, r11d - mov r9d, DWORD PTR [r12+rcx*4] - xor r9d, DWORD PTR [r12+rsi*4] - mov eax, DWORD PTR [r12+rbp*4] - xor r9d, edi - movq rdi, xmm8 - movzx ecx, r14b - movd xmm0, r10d - movd xmm2, r9d - punpckldq xmm2, xmm1 - movq xmm1, r8 - xor eax, DWORD PTR [r12+rcx*4] - xor eax, r15d - movd xmm3, eax - movq rax, xmm7 - punpckldq xmm3, xmm0 - movq xmm0, r13 - punpcklqdq xmm1, xmm0 - punpckldq xmm3, xmm2 - pxor xmm3, xmm1 - movq r9, xmm3 - mov r10d, r9d - and r10d, ${MASK} - movdqa xmm0, xmm3 - pxor xmm0, xmm4 - movdqu XMMWORD PTR [rdx+rdi], xmm0 - psrldq xmm0, 11 - movq rcx, xmm0 - movzx ecx, cl - mov cl, BYTE PTR [rcx+rax] - mov BYTE PTR [rdi+rdx+11], cl - mov rbx, QWORD PTR [r10+rdi] - mov rcx, r9 - lea r9, QWORD PTR [r10+rdi] - mov r11, QWORD PTR [r9+8] - mov rax, rbx - movdqa xmm4, xmm3 - mul rcx - movq rcx, xmm6 - add r8, rdx - add r13, rax - movq rax, xmm5 - xor rax, r13 - mov QWORD PTR [r9], r8 - xor r8, rbx - mov QWORD PTR [r9+8], rax - movq rax, xmm9 - mov rdx, r8 - xor r13, r11 - and edx, ${MASK} - mov QWORD PTR [rsp+64], rdx - sub eax, 1 - jne cnv1_main_loop_${ALGO}_soft_aes_sandybridge - - movaps xmm6, XMMWORD PTR [rsp] - movaps xmm7, XMMWORD PTR [rsp+16] - movaps xmm8, XMMWORD PTR [rsp+32] - movaps xmm9, XMMWORD PTR [rsp+48] - - add rsp, 72 - pop r15 - pop r14 - pop r13 - pop r12 - pop rdi - pop rsi - pop rbp - pop rbx diff --git a/src/crypto/asm/cnv2_main_loop_soft_aes_sandybridge.inc.in b/src/crypto/asm/cnv2_main_loop_soft_aes_sandybridge.inc.in deleted file mode 100644 index 46a58fb8..00000000 --- a/src/crypto/asm/cnv2_main_loop_soft_aes_sandybridge.inc.in +++ /dev/null @@ -1,271 +0,0 @@ - mov QWORD PTR [rsp+8], rcx - push rbx - push rbp - push rsi - push rdi - push r12 - push r13 - push r14 - push r15 - sub rsp, 152 - - stmxcsr DWORD PTR [rsp+4] - mov DWORD PTR [rsp], 24448 - ldmxcsr DWORD PTR [rsp] - - mov rax, QWORD PTR [rcx+48] - mov r10, rcx - xor rax, QWORD PTR [rcx+16] - mov r8, QWORD PTR [rcx+32] - xor r8, QWORD PTR [rcx] - mov r9, QWORD PTR [rcx+40] - xor r9, QWORD PTR [rcx+8] - movq xmm4, rax - mov rdx, QWORD PTR [rcx+56] - xor rdx, QWORD PTR [rcx+24] - mov r11, QWORD PTR [rcx+224] - mov rcx, QWORD PTR [rcx+88] - xor rcx, QWORD PTR [r10+72] - mov rax, QWORD PTR [r10+80] - movq xmm0, rdx - xor rax, QWORD PTR [r10+64] - - movaps XMMWORD PTR [rsp+16], xmm6 - movaps XMMWORD PTR [rsp+32], xmm7 - movaps XMMWORD PTR [rsp+48], xmm8 - movaps XMMWORD PTR [rsp+64], xmm9 - movaps XMMWORD PTR [rsp+80], xmm10 - movaps XMMWORD PTR [rsp+96], xmm11 - movaps XMMWORD PTR [rsp+112], xmm12 - movaps XMMWORD PTR [rsp+128], xmm13 - - movq xmm5, rax - - mov ax, 1023 - shl rax, 52 - movq xmm8, rax - - mov rax, r8 - punpcklqdq xmm4, xmm0 - and eax, ${MASK} - movq xmm10, QWORD PTR [r10+96] - movq xmm0, rcx - mov rcx, QWORD PTR [r10+104] - xorps xmm9, xmm9 - mov QWORD PTR [rsp+248], rax - movq xmm12, r11 - mov QWORD PTR [rsp+240], r9 - punpcklqdq xmm5, xmm0 - movq xmm13, rcx - mov r12d, ${ITERATIONS} - - #ifdef __APPLE__ - ALIGN 16 - #else - ALIGN 64 - #endif -cnv2_main_loop_${ALGO}_soft_aes_sandybridge: - movd xmm11, r12d - mov r12, QWORD PTR [r10+272] - lea r13, QWORD PTR [rax+r11] - mov esi, DWORD PTR [r13] - movq xmm0, r9 - mov r10d, DWORD PTR [r13+4] - movq xmm7, r8 - mov ebp, DWORD PTR [r13+12] - mov r14d, DWORD PTR [r13+8] - mov rdx, QWORD PTR [rsp+248] - movzx ecx, sil - shr esi, 8 - punpcklqdq xmm7, xmm0 - mov r15d, DWORD PTR [r12+rcx*4] - movzx ecx, r10b - shr r10d, 8 - mov edi, DWORD PTR [r12+rcx*4] - movzx ecx, r14b - shr r14d, 8 - mov ebx, DWORD PTR [r12+rcx*4] - movzx ecx, bpl - shr ebp, 8 - mov r9d, DWORD PTR [r12+rcx*4] - movzx ecx, r10b - shr r10d, 8 - xor r15d, DWORD PTR [r12+rcx*4+1024] - movzx ecx, r14b - shr r14d, 8 - mov eax, r14d - shr eax, 8 - xor edi, DWORD PTR [r12+rcx*4+1024] - add eax, 256 - movzx ecx, bpl - shr ebp, 8 - xor ebx, DWORD PTR [r12+rcx*4+1024] - movzx ecx, sil - shr esi, 8 - xor r9d, DWORD PTR [r12+rcx*4+1024] - add r12, 2048 - movzx ecx, r10b - shr r10d, 8 - add r10d, 256 - mov r11d, DWORD PTR [r12+rax*4] - xor r11d, DWORD PTR [r12+rcx*4] - xor r11d, r9d - movzx ecx, sil - mov r10d, DWORD PTR [r12+r10*4] - shr esi, 8 - add esi, 256 - xor r10d, DWORD PTR [r12+rcx*4] - movzx ecx, bpl - xor r10d, ebx - shr ebp, 8 - movd xmm1, r11d - add ebp, 256 - movq r11, xmm12 - mov r9d, DWORD PTR [r12+rcx*4] - xor r9d, DWORD PTR [r12+rsi*4] - mov eax, DWORD PTR [r12+rbp*4] - xor r9d, edi - movzx ecx, r14b - movd xmm0, r10d - movd xmm2, r9d - xor eax, DWORD PTR [r12+rcx*4] - mov rcx, rdx - xor eax, r15d - punpckldq xmm2, xmm1 - xor rcx, 16 - movd xmm6, eax - mov rax, rdx - punpckldq xmm6, xmm0 - xor rax, 32 - punpckldq xmm6, xmm2 - xor rdx, 48 - movdqu xmm2, XMMWORD PTR [rcx+r11] - pxor xmm6, xmm7 - paddq xmm2, xmm4 - movdqu xmm1, XMMWORD PTR [rax+r11] - movdqu xmm0, XMMWORD PTR [rdx+r11] - paddq xmm0, xmm5 - movdqu XMMWORD PTR [rcx+r11], xmm0 - movdqu XMMWORD PTR [rax+r11], xmm2 - movq rcx, xmm13 - paddq xmm1, xmm7 - movdqu XMMWORD PTR [rdx+r11], xmm1 - movq rdi, xmm6 - mov r10, rdi - and r10d, ${MASK} - xor edx, edx - mov rax, rcx - shl rax, 32 - movq rbx, xmm10 - xor rbx, rax - lea r9, QWORD PTR [rcx+rcx] - add r9d, edi - movdqa xmm0, xmm6 - pxor xmm0, xmm4 - mov ecx, -2147483647 - movdqu XMMWORD PTR [r13], xmm0 - or r9, rcx - movdqa xmm0, xmm6 - movaps xmm1, xmm9 - psrldq xmm0, 8 - movq rax, xmm0 - xor rbx, QWORD PTR [r10+r11] - lea r14, QWORD PTR [r10+r11] - mov rbp, QWORD PTR [r14+8] - div r9 - shl rdx, 32 - mov eax, eax - add rdx, rax - lea r9, QWORD PTR [rdx+rdi] - movq xmm10, rdx - mov rax, r9 - shr rax, 12 - movq xmm0, rax - paddq xmm0, xmm8 - sqrtsd xmm1, xmm0 - movq rdx, xmm1 - test rdx, 524287 - je sqrt_fixup_${ALGO}_soft_aes_sandybridge - psrlq xmm1, 19 -sqrt_fixup_${ALGO}_soft_aes_sandybridge_ret: - - mov r9, r10 - movdqa xmm13, xmm1 - xor r9, 16 - mov rcx, r10 - xor rcx, 32 - xor r10, 48 - mov rax, rbx - mul rdi - movdqu xmm2, XMMWORD PTR [r9+r11] - movdqu xmm1, XMMWORD PTR [rcx+r11] - paddq xmm1, xmm7 - movq xmm0, rax - movq xmm3, rdx - xor rax, QWORD PTR [r11+rcx+8] - xor rdx, QWORD PTR [rcx+r11] - punpcklqdq xmm3, xmm0 - add r8, rdx - movdqu xmm0, XMMWORD PTR [r10+r11] - pxor xmm2, xmm3 - paddq xmm0, xmm5 - paddq xmm2, xmm4 - movdqu XMMWORD PTR [r9+r11], xmm0 - movdqa xmm5, xmm4 - mov r9, QWORD PTR [rsp+240] - movdqa xmm4, xmm6 - add r9, rax - movdqu XMMWORD PTR [rcx+r11], xmm2 - movdqu XMMWORD PTR [r10+r11], xmm1 - mov r10, QWORD PTR [rsp+224] - movd r12d, xmm11 - mov QWORD PTR [r14], r8 - xor r8, rbx - mov rax, r8 - mov QWORD PTR [r14+8], r9 - and eax, ${MASK} - xor r9, rbp - mov QWORD PTR [rsp+240], r9 - mov QWORD PTR [rsp+248], rax - sub r12d, 1 - jne cnv2_main_loop_${ALGO}_soft_aes_sandybridge - - ldmxcsr DWORD PTR [rsp+4] - movaps xmm6, XMMWORD PTR [rsp+16] - movaps xmm7, XMMWORD PTR [rsp+32] - movaps xmm8, XMMWORD PTR [rsp+48] - movaps xmm9, XMMWORD PTR [rsp+64] - movaps xmm10, XMMWORD PTR [rsp+80] - movaps xmm11, XMMWORD PTR [rsp+96] - movaps xmm12, XMMWORD PTR [rsp+112] - movaps xmm13, XMMWORD PTR [rsp+128] - - add rsp, 152 - pop r15 - pop r14 - pop r13 - pop r12 - pop rdi - pop rsi - pop rbp - pop rbx - jmp cnv2_main_loop_${ALGO}_soft_aes_sandybridge_endp - -sqrt_fixup_${ALGO}_soft_aes_sandybridge: - dec rdx - mov r15d, -1022 - shl r15, 32 - mov rax, rdx - shr rdx, 19 - shr rax, 20 - mov rcx, rdx - sub rcx, rax - lea rcx, [rcx+r15+1] - add rax, r15 - imul rcx, rax - sub rcx, r9 - adc rdx, 0 - movq xmm1, rdx - jmp sqrt_fixup_${ALGO}_soft_aes_sandybridge_ret - -cnv2_main_loop_${ALGO}_soft_aes_sandybridge_endp: diff --git a/src/crypto/asm/win/CryptonightR_template.S b/src/crypto/asm/win/CryptonightR_template.S deleted file mode 100644 index d2974d16..00000000 --- a/src/crypto/asm/win/CryptonightR_template.S +++ /dev/null @@ -1,1595 +0,0 @@ -#ifdef __APPLE__ -# define ALIGN(x) .align 6 -#else -# define ALIGN(x) .align 64 -#endif -.intel_syntax noprefix -#ifdef __APPLE__ -# define FN_PREFIX(fn) _ ## fn -.text -#else -# define FN_PREFIX(fn) fn -.section .text -#endif - -#define PUBLIC .global - -PUBLIC FN_PREFIX(CryptonightR_instruction0) -PUBLIC FN_PREFIX(CryptonightR_instruction1) -PUBLIC FN_PREFIX(CryptonightR_instruction2) -PUBLIC FN_PREFIX(CryptonightR_instruction3) -PUBLIC FN_PREFIX(CryptonightR_instruction4) -PUBLIC FN_PREFIX(CryptonightR_instruction5) -PUBLIC FN_PREFIX(CryptonightR_instruction6) -PUBLIC FN_PREFIX(CryptonightR_instruction7) -PUBLIC FN_PREFIX(CryptonightR_instruction8) -PUBLIC FN_PREFIX(CryptonightR_instruction9) -PUBLIC FN_PREFIX(CryptonightR_instruction10) -PUBLIC FN_PREFIX(CryptonightR_instruction11) -PUBLIC FN_PREFIX(CryptonightR_instruction12) -PUBLIC FN_PREFIX(CryptonightR_instruction13) -PUBLIC FN_PREFIX(CryptonightR_instruction14) -PUBLIC FN_PREFIX(CryptonightR_instruction15) -PUBLIC FN_PREFIX(CryptonightR_instruction16) -PUBLIC FN_PREFIX(CryptonightR_instruction17) -PUBLIC FN_PREFIX(CryptonightR_instruction18) -PUBLIC FN_PREFIX(CryptonightR_instruction19) -PUBLIC FN_PREFIX(CryptonightR_instruction20) -PUBLIC FN_PREFIX(CryptonightR_instruction21) -PUBLIC FN_PREFIX(CryptonightR_instruction22) -PUBLIC FN_PREFIX(CryptonightR_instruction23) -PUBLIC FN_PREFIX(CryptonightR_instruction24) -PUBLIC FN_PREFIX(CryptonightR_instruction25) -PUBLIC FN_PREFIX(CryptonightR_instruction26) -PUBLIC FN_PREFIX(CryptonightR_instruction27) -PUBLIC FN_PREFIX(CryptonightR_instruction28) -PUBLIC FN_PREFIX(CryptonightR_instruction29) -PUBLIC FN_PREFIX(CryptonightR_instruction30) -PUBLIC FN_PREFIX(CryptonightR_instruction31) -PUBLIC FN_PREFIX(CryptonightR_instruction32) -PUBLIC FN_PREFIX(CryptonightR_instruction33) -PUBLIC FN_PREFIX(CryptonightR_instruction34) -PUBLIC FN_PREFIX(CryptonightR_instruction35) -PUBLIC FN_PREFIX(CryptonightR_instruction36) -PUBLIC FN_PREFIX(CryptonightR_instruction37) -PUBLIC FN_PREFIX(CryptonightR_instruction38) -PUBLIC FN_PREFIX(CryptonightR_instruction39) -PUBLIC FN_PREFIX(CryptonightR_instruction40) -PUBLIC FN_PREFIX(CryptonightR_instruction41) -PUBLIC FN_PREFIX(CryptonightR_instruction42) -PUBLIC FN_PREFIX(CryptonightR_instruction43) -PUBLIC FN_PREFIX(CryptonightR_instruction44) -PUBLIC FN_PREFIX(CryptonightR_instruction45) -PUBLIC FN_PREFIX(CryptonightR_instruction46) -PUBLIC FN_PREFIX(CryptonightR_instruction47) -PUBLIC FN_PREFIX(CryptonightR_instruction48) -PUBLIC FN_PREFIX(CryptonightR_instruction49) -PUBLIC FN_PREFIX(CryptonightR_instruction50) -PUBLIC FN_PREFIX(CryptonightR_instruction51) -PUBLIC FN_PREFIX(CryptonightR_instruction52) -PUBLIC FN_PREFIX(CryptonightR_instruction53) -PUBLIC FN_PREFIX(CryptonightR_instruction54) -PUBLIC FN_PREFIX(CryptonightR_instruction55) -PUBLIC FN_PREFIX(CryptonightR_instruction56) -PUBLIC FN_PREFIX(CryptonightR_instruction57) -PUBLIC FN_PREFIX(CryptonightR_instruction58) -PUBLIC FN_PREFIX(CryptonightR_instruction59) -PUBLIC FN_PREFIX(CryptonightR_instruction60) -PUBLIC FN_PREFIX(CryptonightR_instruction61) -PUBLIC FN_PREFIX(CryptonightR_instruction62) -PUBLIC FN_PREFIX(CryptonightR_instruction63) -PUBLIC FN_PREFIX(CryptonightR_instruction64) -PUBLIC FN_PREFIX(CryptonightR_instruction65) -PUBLIC FN_PREFIX(CryptonightR_instruction66) -PUBLIC FN_PREFIX(CryptonightR_instruction67) -PUBLIC FN_PREFIX(CryptonightR_instruction68) -PUBLIC FN_PREFIX(CryptonightR_instruction69) -PUBLIC FN_PREFIX(CryptonightR_instruction70) -PUBLIC FN_PREFIX(CryptonightR_instruction71) -PUBLIC FN_PREFIX(CryptonightR_instruction72) -PUBLIC FN_PREFIX(CryptonightR_instruction73) -PUBLIC FN_PREFIX(CryptonightR_instruction74) -PUBLIC FN_PREFIX(CryptonightR_instruction75) -PUBLIC FN_PREFIX(CryptonightR_instruction76) -PUBLIC FN_PREFIX(CryptonightR_instruction77) -PUBLIC FN_PREFIX(CryptonightR_instruction78) -PUBLIC FN_PREFIX(CryptonightR_instruction79) -PUBLIC FN_PREFIX(CryptonightR_instruction80) -PUBLIC FN_PREFIX(CryptonightR_instruction81) -PUBLIC FN_PREFIX(CryptonightR_instruction82) -PUBLIC FN_PREFIX(CryptonightR_instruction83) -PUBLIC FN_PREFIX(CryptonightR_instruction84) -PUBLIC FN_PREFIX(CryptonightR_instruction85) -PUBLIC FN_PREFIX(CryptonightR_instruction86) -PUBLIC FN_PREFIX(CryptonightR_instruction87) -PUBLIC FN_PREFIX(CryptonightR_instruction88) -PUBLIC FN_PREFIX(CryptonightR_instruction89) -PUBLIC FN_PREFIX(CryptonightR_instruction90) -PUBLIC FN_PREFIX(CryptonightR_instruction91) -PUBLIC FN_PREFIX(CryptonightR_instruction92) -PUBLIC FN_PREFIX(CryptonightR_instruction93) -PUBLIC FN_PREFIX(CryptonightR_instruction94) -PUBLIC FN_PREFIX(CryptonightR_instruction95) -PUBLIC FN_PREFIX(CryptonightR_instruction96) -PUBLIC FN_PREFIX(CryptonightR_instruction97) -PUBLIC FN_PREFIX(CryptonightR_instruction98) -PUBLIC FN_PREFIX(CryptonightR_instruction99) -PUBLIC FN_PREFIX(CryptonightR_instruction100) -PUBLIC FN_PREFIX(CryptonightR_instruction101) -PUBLIC FN_PREFIX(CryptonightR_instruction102) -PUBLIC FN_PREFIX(CryptonightR_instruction103) -PUBLIC FN_PREFIX(CryptonightR_instruction104) -PUBLIC FN_PREFIX(CryptonightR_instruction105) -PUBLIC FN_PREFIX(CryptonightR_instruction106) -PUBLIC FN_PREFIX(CryptonightR_instruction107) -PUBLIC FN_PREFIX(CryptonightR_instruction108) -PUBLIC FN_PREFIX(CryptonightR_instruction109) -PUBLIC FN_PREFIX(CryptonightR_instruction110) -PUBLIC FN_PREFIX(CryptonightR_instruction111) -PUBLIC FN_PREFIX(CryptonightR_instruction112) -PUBLIC FN_PREFIX(CryptonightR_instruction113) -PUBLIC FN_PREFIX(CryptonightR_instruction114) -PUBLIC FN_PREFIX(CryptonightR_instruction115) -PUBLIC FN_PREFIX(CryptonightR_instruction116) -PUBLIC FN_PREFIX(CryptonightR_instruction117) -PUBLIC FN_PREFIX(CryptonightR_instruction118) -PUBLIC FN_PREFIX(CryptonightR_instruction119) -PUBLIC FN_PREFIX(CryptonightR_instruction120) -PUBLIC FN_PREFIX(CryptonightR_instruction121) -PUBLIC FN_PREFIX(CryptonightR_instruction122) -PUBLIC FN_PREFIX(CryptonightR_instruction123) -PUBLIC FN_PREFIX(CryptonightR_instruction124) -PUBLIC FN_PREFIX(CryptonightR_instruction125) -PUBLIC FN_PREFIX(CryptonightR_instruction126) -PUBLIC FN_PREFIX(CryptonightR_instruction127) -PUBLIC FN_PREFIX(CryptonightR_instruction128) -PUBLIC FN_PREFIX(CryptonightR_instruction129) -PUBLIC FN_PREFIX(CryptonightR_instruction130) -PUBLIC FN_PREFIX(CryptonightR_instruction131) -PUBLIC FN_PREFIX(CryptonightR_instruction132) -PUBLIC FN_PREFIX(CryptonightR_instruction133) -PUBLIC FN_PREFIX(CryptonightR_instruction134) -PUBLIC FN_PREFIX(CryptonightR_instruction135) -PUBLIC FN_PREFIX(CryptonightR_instruction136) -PUBLIC FN_PREFIX(CryptonightR_instruction137) -PUBLIC FN_PREFIX(CryptonightR_instruction138) -PUBLIC FN_PREFIX(CryptonightR_instruction139) -PUBLIC FN_PREFIX(CryptonightR_instruction140) -PUBLIC FN_PREFIX(CryptonightR_instruction141) -PUBLIC FN_PREFIX(CryptonightR_instruction142) -PUBLIC FN_PREFIX(CryptonightR_instruction143) -PUBLIC FN_PREFIX(CryptonightR_instruction144) -PUBLIC FN_PREFIX(CryptonightR_instruction145) -PUBLIC FN_PREFIX(CryptonightR_instruction146) -PUBLIC FN_PREFIX(CryptonightR_instruction147) -PUBLIC FN_PREFIX(CryptonightR_instruction148) -PUBLIC FN_PREFIX(CryptonightR_instruction149) -PUBLIC FN_PREFIX(CryptonightR_instruction150) -PUBLIC FN_PREFIX(CryptonightR_instruction151) -PUBLIC FN_PREFIX(CryptonightR_instruction152) -PUBLIC FN_PREFIX(CryptonightR_instruction153) -PUBLIC FN_PREFIX(CryptonightR_instruction154) -PUBLIC FN_PREFIX(CryptonightR_instruction155) -PUBLIC FN_PREFIX(CryptonightR_instruction156) -PUBLIC FN_PREFIX(CryptonightR_instruction157) -PUBLIC FN_PREFIX(CryptonightR_instruction158) -PUBLIC FN_PREFIX(CryptonightR_instruction159) -PUBLIC FN_PREFIX(CryptonightR_instruction160) -PUBLIC FN_PREFIX(CryptonightR_instruction161) -PUBLIC FN_PREFIX(CryptonightR_instruction162) -PUBLIC FN_PREFIX(CryptonightR_instruction163) -PUBLIC FN_PREFIX(CryptonightR_instruction164) -PUBLIC FN_PREFIX(CryptonightR_instruction165) -PUBLIC FN_PREFIX(CryptonightR_instruction166) -PUBLIC FN_PREFIX(CryptonightR_instruction167) -PUBLIC FN_PREFIX(CryptonightR_instruction168) -PUBLIC FN_PREFIX(CryptonightR_instruction169) -PUBLIC FN_PREFIX(CryptonightR_instruction170) -PUBLIC FN_PREFIX(CryptonightR_instruction171) -PUBLIC FN_PREFIX(CryptonightR_instruction172) -PUBLIC FN_PREFIX(CryptonightR_instruction173) -PUBLIC FN_PREFIX(CryptonightR_instruction174) -PUBLIC FN_PREFIX(CryptonightR_instruction175) -PUBLIC FN_PREFIX(CryptonightR_instruction176) -PUBLIC FN_PREFIX(CryptonightR_instruction177) -PUBLIC FN_PREFIX(CryptonightR_instruction178) -PUBLIC FN_PREFIX(CryptonightR_instruction179) -PUBLIC FN_PREFIX(CryptonightR_instruction180) -PUBLIC FN_PREFIX(CryptonightR_instruction181) -PUBLIC FN_PREFIX(CryptonightR_instruction182) -PUBLIC FN_PREFIX(CryptonightR_instruction183) -PUBLIC FN_PREFIX(CryptonightR_instruction184) -PUBLIC FN_PREFIX(CryptonightR_instruction185) -PUBLIC FN_PREFIX(CryptonightR_instruction186) -PUBLIC FN_PREFIX(CryptonightR_instruction187) -PUBLIC FN_PREFIX(CryptonightR_instruction188) -PUBLIC FN_PREFIX(CryptonightR_instruction189) -PUBLIC FN_PREFIX(CryptonightR_instruction190) -PUBLIC FN_PREFIX(CryptonightR_instruction191) -PUBLIC FN_PREFIX(CryptonightR_instruction192) -PUBLIC FN_PREFIX(CryptonightR_instruction193) -PUBLIC FN_PREFIX(CryptonightR_instruction194) -PUBLIC FN_PREFIX(CryptonightR_instruction195) -PUBLIC FN_PREFIX(CryptonightR_instruction196) -PUBLIC FN_PREFIX(CryptonightR_instruction197) -PUBLIC FN_PREFIX(CryptonightR_instruction198) -PUBLIC FN_PREFIX(CryptonightR_instruction199) -PUBLIC FN_PREFIX(CryptonightR_instruction200) -PUBLIC FN_PREFIX(CryptonightR_instruction201) -PUBLIC FN_PREFIX(CryptonightR_instruction202) -PUBLIC FN_PREFIX(CryptonightR_instruction203) -PUBLIC FN_PREFIX(CryptonightR_instruction204) -PUBLIC FN_PREFIX(CryptonightR_instruction205) -PUBLIC FN_PREFIX(CryptonightR_instruction206) -PUBLIC FN_PREFIX(CryptonightR_instruction207) -PUBLIC FN_PREFIX(CryptonightR_instruction208) -PUBLIC FN_PREFIX(CryptonightR_instruction209) -PUBLIC FN_PREFIX(CryptonightR_instruction210) -PUBLIC FN_PREFIX(CryptonightR_instruction211) -PUBLIC FN_PREFIX(CryptonightR_instruction212) -PUBLIC FN_PREFIX(CryptonightR_instruction213) -PUBLIC FN_PREFIX(CryptonightR_instruction214) -PUBLIC FN_PREFIX(CryptonightR_instruction215) -PUBLIC FN_PREFIX(CryptonightR_instruction216) -PUBLIC FN_PREFIX(CryptonightR_instruction217) -PUBLIC FN_PREFIX(CryptonightR_instruction218) -PUBLIC FN_PREFIX(CryptonightR_instruction219) -PUBLIC FN_PREFIX(CryptonightR_instruction220) -PUBLIC FN_PREFIX(CryptonightR_instruction221) -PUBLIC FN_PREFIX(CryptonightR_instruction222) -PUBLIC FN_PREFIX(CryptonightR_instruction223) -PUBLIC FN_PREFIX(CryptonightR_instruction224) -PUBLIC FN_PREFIX(CryptonightR_instruction225) -PUBLIC FN_PREFIX(CryptonightR_instruction226) -PUBLIC FN_PREFIX(CryptonightR_instruction227) -PUBLIC FN_PREFIX(CryptonightR_instruction228) -PUBLIC FN_PREFIX(CryptonightR_instruction229) -PUBLIC FN_PREFIX(CryptonightR_instruction230) -PUBLIC FN_PREFIX(CryptonightR_instruction231) -PUBLIC FN_PREFIX(CryptonightR_instruction232) -PUBLIC FN_PREFIX(CryptonightR_instruction233) -PUBLIC FN_PREFIX(CryptonightR_instruction234) -PUBLIC FN_PREFIX(CryptonightR_instruction235) -PUBLIC FN_PREFIX(CryptonightR_instruction236) -PUBLIC FN_PREFIX(CryptonightR_instruction237) -PUBLIC FN_PREFIX(CryptonightR_instruction238) -PUBLIC FN_PREFIX(CryptonightR_instruction239) -PUBLIC FN_PREFIX(CryptonightR_instruction240) -PUBLIC FN_PREFIX(CryptonightR_instruction241) -PUBLIC FN_PREFIX(CryptonightR_instruction242) -PUBLIC FN_PREFIX(CryptonightR_instruction243) -PUBLIC FN_PREFIX(CryptonightR_instruction244) -PUBLIC FN_PREFIX(CryptonightR_instruction245) -PUBLIC FN_PREFIX(CryptonightR_instruction246) -PUBLIC FN_PREFIX(CryptonightR_instruction247) -PUBLIC FN_PREFIX(CryptonightR_instruction248) -PUBLIC FN_PREFIX(CryptonightR_instruction249) -PUBLIC FN_PREFIX(CryptonightR_instruction250) -PUBLIC FN_PREFIX(CryptonightR_instruction251) -PUBLIC FN_PREFIX(CryptonightR_instruction252) -PUBLIC FN_PREFIX(CryptonightR_instruction253) -PUBLIC FN_PREFIX(CryptonightR_instruction254) -PUBLIC FN_PREFIX(CryptonightR_instruction255) -PUBLIC FN_PREFIX(CryptonightR_instruction256) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov0) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov1) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov2) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov3) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov4) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov5) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov6) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov7) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov8) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov9) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov10) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov11) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov12) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov13) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov14) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov15) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov16) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov17) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov18) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov19) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov20) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov21) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov22) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov23) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov24) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov25) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov26) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov27) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov28) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov29) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov30) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov31) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov32) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov33) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov34) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov35) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov36) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov37) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov38) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov39) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov40) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov41) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov42) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov43) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov44) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov45) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov46) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov47) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov48) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov49) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov50) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov51) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov52) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov53) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov54) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov55) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov56) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov57) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov58) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov59) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov60) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov61) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov62) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov63) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov64) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov65) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov66) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov67) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov68) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov69) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov70) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov71) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov72) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov73) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov74) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov75) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov76) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov77) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov78) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov79) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov80) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov81) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov82) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov83) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov84) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov85) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov86) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov87) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov88) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov89) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov90) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov91) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov92) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov93) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov94) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov95) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov96) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov97) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov98) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov99) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov100) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov101) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov102) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov103) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov104) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov105) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov106) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov107) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov108) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov109) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov110) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov111) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov112) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov113) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov114) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov115) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov116) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov117) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov118) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov119) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov120) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov121) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov122) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov123) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov124) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov125) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov126) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov127) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov128) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov129) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov130) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov131) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov132) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov133) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov134) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov135) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov136) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov137) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov138) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov139) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov140) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov141) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov142) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov143) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov144) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov145) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov146) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov147) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov148) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov149) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov150) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov151) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov152) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov153) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov154) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov155) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov156) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov157) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov158) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov159) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov160) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov161) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov162) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov163) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov164) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov165) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov166) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov167) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov168) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov169) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov170) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov171) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov172) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov173) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov174) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov175) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov176) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov177) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov178) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov179) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov180) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov181) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov182) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov183) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov184) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov185) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov186) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov187) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov188) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov189) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov190) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov191) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov192) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov193) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov194) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov195) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov196) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov197) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov198) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov199) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov200) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov201) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov202) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov203) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov204) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov205) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov206) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov207) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov208) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov209) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov210) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov211) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov212) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov213) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov214) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov215) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov216) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov217) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov218) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov219) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov220) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov221) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov222) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov223) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov224) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov225) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov226) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov227) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov228) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov229) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov230) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov231) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov232) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov233) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov234) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov235) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov236) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov237) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov238) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov239) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov240) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov241) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov242) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov243) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov244) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov245) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov246) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov247) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov248) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov249) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov250) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov251) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov252) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov253) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov254) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov255) -PUBLIC FN_PREFIX(CryptonightR_instruction_mov256) - -#include "CryptonightWOW_template.inc" -#include "CryptonightR_template.inc" -#include "CryptonightWOW_soft_aes_template.inc" -#include "CryptonightR_soft_aes_template.inc" - -FN_PREFIX(CryptonightR_instruction0): - imul rbx, rbx -FN_PREFIX(CryptonightR_instruction1): - imul rbx, rbx -FN_PREFIX(CryptonightR_instruction2): - imul rbx, rbx -FN_PREFIX(CryptonightR_instruction3): - add rbx, r9 - add rbx, 2147483647 -FN_PREFIX(CryptonightR_instruction4): - sub rbx, r9 -FN_PREFIX(CryptonightR_instruction5): - ror ebx, cl -FN_PREFIX(CryptonightR_instruction6): - rol ebx, cl -FN_PREFIX(CryptonightR_instruction7): - xor rbx, r9 -FN_PREFIX(CryptonightR_instruction8): - imul rsi, rbx -FN_PREFIX(CryptonightR_instruction9): - imul rsi, rbx -FN_PREFIX(CryptonightR_instruction10): - imul rsi, rbx -FN_PREFIX(CryptonightR_instruction11): - add rsi, rbx - add rsi, 2147483647 -FN_PREFIX(CryptonightR_instruction12): - sub rsi, rbx -FN_PREFIX(CryptonightR_instruction13): - ror esi, cl -FN_PREFIX(CryptonightR_instruction14): - rol esi, cl -FN_PREFIX(CryptonightR_instruction15): - xor rsi, rbx -FN_PREFIX(CryptonightR_instruction16): - imul rdi, rbx -FN_PREFIX(CryptonightR_instruction17): - imul rdi, rbx -FN_PREFIX(CryptonightR_instruction18): - imul rdi, rbx -FN_PREFIX(CryptonightR_instruction19): - add rdi, rbx - add rdi, 2147483647 -FN_PREFIX(CryptonightR_instruction20): - sub rdi, rbx -FN_PREFIX(CryptonightR_instruction21): - ror edi, cl -FN_PREFIX(CryptonightR_instruction22): - rol edi, cl -FN_PREFIX(CryptonightR_instruction23): - xor rdi, rbx -FN_PREFIX(CryptonightR_instruction24): - imul rbp, rbx -FN_PREFIX(CryptonightR_instruction25): - imul rbp, rbx -FN_PREFIX(CryptonightR_instruction26): - imul rbp, rbx -FN_PREFIX(CryptonightR_instruction27): - add rbp, rbx - add rbp, 2147483647 -FN_PREFIX(CryptonightR_instruction28): - sub rbp, rbx -FN_PREFIX(CryptonightR_instruction29): - ror ebp, cl -FN_PREFIX(CryptonightR_instruction30): - rol ebp, cl -FN_PREFIX(CryptonightR_instruction31): - xor rbp, rbx -FN_PREFIX(CryptonightR_instruction32): - imul rbx, rsi -FN_PREFIX(CryptonightR_instruction33): - imul rbx, rsi -FN_PREFIX(CryptonightR_instruction34): - imul rbx, rsi -FN_PREFIX(CryptonightR_instruction35): - add rbx, rsi - add rbx, 2147483647 -FN_PREFIX(CryptonightR_instruction36): - sub rbx, rsi -FN_PREFIX(CryptonightR_instruction37): - ror ebx, cl -FN_PREFIX(CryptonightR_instruction38): - rol ebx, cl -FN_PREFIX(CryptonightR_instruction39): - xor rbx, rsi -FN_PREFIX(CryptonightR_instruction40): - imul rsi, rsi -FN_PREFIX(CryptonightR_instruction41): - imul rsi, rsi -FN_PREFIX(CryptonightR_instruction42): - imul rsi, rsi -FN_PREFIX(CryptonightR_instruction43): - add rsi, r9 - add rsi, 2147483647 -FN_PREFIX(CryptonightR_instruction44): - sub rsi, r9 -FN_PREFIX(CryptonightR_instruction45): - ror esi, cl -FN_PREFIX(CryptonightR_instruction46): - rol esi, cl -FN_PREFIX(CryptonightR_instruction47): - xor rsi, r9 -FN_PREFIX(CryptonightR_instruction48): - imul rdi, rsi -FN_PREFIX(CryptonightR_instruction49): - imul rdi, rsi -FN_PREFIX(CryptonightR_instruction50): - imul rdi, rsi -FN_PREFIX(CryptonightR_instruction51): - add rdi, rsi - add rdi, 2147483647 -FN_PREFIX(CryptonightR_instruction52): - sub rdi, rsi -FN_PREFIX(CryptonightR_instruction53): - ror edi, cl -FN_PREFIX(CryptonightR_instruction54): - rol edi, cl -FN_PREFIX(CryptonightR_instruction55): - xor rdi, rsi -FN_PREFIX(CryptonightR_instruction56): - imul rbp, rsi -FN_PREFIX(CryptonightR_instruction57): - imul rbp, rsi -FN_PREFIX(CryptonightR_instruction58): - imul rbp, rsi -FN_PREFIX(CryptonightR_instruction59): - add rbp, rsi - add rbp, 2147483647 -FN_PREFIX(CryptonightR_instruction60): - sub rbp, rsi -FN_PREFIX(CryptonightR_instruction61): - ror ebp, cl -FN_PREFIX(CryptonightR_instruction62): - rol ebp, cl -FN_PREFIX(CryptonightR_instruction63): - xor rbp, rsi -FN_PREFIX(CryptonightR_instruction64): - imul rbx, rdi -FN_PREFIX(CryptonightR_instruction65): - imul rbx, rdi -FN_PREFIX(CryptonightR_instruction66): - imul rbx, rdi -FN_PREFIX(CryptonightR_instruction67): - add rbx, rdi - add rbx, 2147483647 -FN_PREFIX(CryptonightR_instruction68): - sub rbx, rdi -FN_PREFIX(CryptonightR_instruction69): - ror ebx, cl -FN_PREFIX(CryptonightR_instruction70): - rol ebx, cl -FN_PREFIX(CryptonightR_instruction71): - xor rbx, rdi -FN_PREFIX(CryptonightR_instruction72): - imul rsi, rdi -FN_PREFIX(CryptonightR_instruction73): - imul rsi, rdi -FN_PREFIX(CryptonightR_instruction74): - imul rsi, rdi -FN_PREFIX(CryptonightR_instruction75): - add rsi, rdi - add rsi, 2147483647 -FN_PREFIX(CryptonightR_instruction76): - sub rsi, rdi -FN_PREFIX(CryptonightR_instruction77): - ror esi, cl -FN_PREFIX(CryptonightR_instruction78): - rol esi, cl -FN_PREFIX(CryptonightR_instruction79): - xor rsi, rdi -FN_PREFIX(CryptonightR_instruction80): - imul rdi, rdi -FN_PREFIX(CryptonightR_instruction81): - imul rdi, rdi -FN_PREFIX(CryptonightR_instruction82): - imul rdi, rdi -FN_PREFIX(CryptonightR_instruction83): - add rdi, r9 - add rdi, 2147483647 -FN_PREFIX(CryptonightR_instruction84): - sub rdi, r9 -FN_PREFIX(CryptonightR_instruction85): - ror edi, cl -FN_PREFIX(CryptonightR_instruction86): - rol edi, cl -FN_PREFIX(CryptonightR_instruction87): - xor rdi, r9 -FN_PREFIX(CryptonightR_instruction88): - imul rbp, rdi -FN_PREFIX(CryptonightR_instruction89): - imul rbp, rdi -FN_PREFIX(CryptonightR_instruction90): - imul rbp, rdi -FN_PREFIX(CryptonightR_instruction91): - add rbp, rdi - add rbp, 2147483647 -FN_PREFIX(CryptonightR_instruction92): - sub rbp, rdi -FN_PREFIX(CryptonightR_instruction93): - ror ebp, cl -FN_PREFIX(CryptonightR_instruction94): - rol ebp, cl -FN_PREFIX(CryptonightR_instruction95): - xor rbp, rdi -FN_PREFIX(CryptonightR_instruction96): - imul rbx, rbp -FN_PREFIX(CryptonightR_instruction97): - imul rbx, rbp -FN_PREFIX(CryptonightR_instruction98): - imul rbx, rbp -FN_PREFIX(CryptonightR_instruction99): - add rbx, rbp - add rbx, 2147483647 -FN_PREFIX(CryptonightR_instruction100): - sub rbx, rbp -FN_PREFIX(CryptonightR_instruction101): - ror ebx, cl -FN_PREFIX(CryptonightR_instruction102): - rol ebx, cl -FN_PREFIX(CryptonightR_instruction103): - xor rbx, rbp -FN_PREFIX(CryptonightR_instruction104): - imul rsi, rbp -FN_PREFIX(CryptonightR_instruction105): - imul rsi, rbp -FN_PREFIX(CryptonightR_instruction106): - imul rsi, rbp -FN_PREFIX(CryptonightR_instruction107): - add rsi, rbp - add rsi, 2147483647 -FN_PREFIX(CryptonightR_instruction108): - sub rsi, rbp -FN_PREFIX(CryptonightR_instruction109): - ror esi, cl -FN_PREFIX(CryptonightR_instruction110): - rol esi, cl -FN_PREFIX(CryptonightR_instruction111): - xor rsi, rbp -FN_PREFIX(CryptonightR_instruction112): - imul rdi, rbp -FN_PREFIX(CryptonightR_instruction113): - imul rdi, rbp -FN_PREFIX(CryptonightR_instruction114): - imul rdi, rbp -FN_PREFIX(CryptonightR_instruction115): - add rdi, rbp - add rdi, 2147483647 -FN_PREFIX(CryptonightR_instruction116): - sub rdi, rbp -FN_PREFIX(CryptonightR_instruction117): - ror edi, cl -FN_PREFIX(CryptonightR_instruction118): - rol edi, cl -FN_PREFIX(CryptonightR_instruction119): - xor rdi, rbp -FN_PREFIX(CryptonightR_instruction120): - imul rbp, rbp -FN_PREFIX(CryptonightR_instruction121): - imul rbp, rbp -FN_PREFIX(CryptonightR_instruction122): - imul rbp, rbp -FN_PREFIX(CryptonightR_instruction123): - add rbp, r9 - add rbp, 2147483647 -FN_PREFIX(CryptonightR_instruction124): - sub rbp, r9 -FN_PREFIX(CryptonightR_instruction125): - ror ebp, cl -FN_PREFIX(CryptonightR_instruction126): - rol ebp, cl -FN_PREFIX(CryptonightR_instruction127): - xor rbp, r9 -FN_PREFIX(CryptonightR_instruction128): - imul rbx, rsp -FN_PREFIX(CryptonightR_instruction129): - imul rbx, rsp -FN_PREFIX(CryptonightR_instruction130): - imul rbx, rsp -FN_PREFIX(CryptonightR_instruction131): - add rbx, rsp - add rbx, 2147483647 -FN_PREFIX(CryptonightR_instruction132): - sub rbx, rsp -FN_PREFIX(CryptonightR_instruction133): - ror ebx, cl -FN_PREFIX(CryptonightR_instruction134): - rol ebx, cl -FN_PREFIX(CryptonightR_instruction135): - xor rbx, rsp -FN_PREFIX(CryptonightR_instruction136): - imul rsi, rsp -FN_PREFIX(CryptonightR_instruction137): - imul rsi, rsp -FN_PREFIX(CryptonightR_instruction138): - imul rsi, rsp -FN_PREFIX(CryptonightR_instruction139): - add rsi, rsp - add rsi, 2147483647 -FN_PREFIX(CryptonightR_instruction140): - sub rsi, rsp -FN_PREFIX(CryptonightR_instruction141): - ror esi, cl -FN_PREFIX(CryptonightR_instruction142): - rol esi, cl -FN_PREFIX(CryptonightR_instruction143): - xor rsi, rsp -FN_PREFIX(CryptonightR_instruction144): - imul rdi, rsp -FN_PREFIX(CryptonightR_instruction145): - imul rdi, rsp -FN_PREFIX(CryptonightR_instruction146): - imul rdi, rsp -FN_PREFIX(CryptonightR_instruction147): - add rdi, rsp - add rdi, 2147483647 -FN_PREFIX(CryptonightR_instruction148): - sub rdi, rsp -FN_PREFIX(CryptonightR_instruction149): - ror edi, cl -FN_PREFIX(CryptonightR_instruction150): - rol edi, cl -FN_PREFIX(CryptonightR_instruction151): - xor rdi, rsp -FN_PREFIX(CryptonightR_instruction152): - imul rbp, rsp -FN_PREFIX(CryptonightR_instruction153): - imul rbp, rsp -FN_PREFIX(CryptonightR_instruction154): - imul rbp, rsp -FN_PREFIX(CryptonightR_instruction155): - add rbp, rsp - add rbp, 2147483647 -FN_PREFIX(CryptonightR_instruction156): - sub rbp, rsp -FN_PREFIX(CryptonightR_instruction157): - ror ebp, cl -FN_PREFIX(CryptonightR_instruction158): - rol ebp, cl -FN_PREFIX(CryptonightR_instruction159): - xor rbp, rsp -FN_PREFIX(CryptonightR_instruction160): - imul rbx, r15 -FN_PREFIX(CryptonightR_instruction161): - imul rbx, r15 -FN_PREFIX(CryptonightR_instruction162): - imul rbx, r15 -FN_PREFIX(CryptonightR_instruction163): - add rbx, r15 - add rbx, 2147483647 -FN_PREFIX(CryptonightR_instruction164): - sub rbx, r15 -FN_PREFIX(CryptonightR_instruction165): - ror ebx, cl -FN_PREFIX(CryptonightR_instruction166): - rol ebx, cl -FN_PREFIX(CryptonightR_instruction167): - xor rbx, r15 -FN_PREFIX(CryptonightR_instruction168): - imul rsi, r15 -FN_PREFIX(CryptonightR_instruction169): - imul rsi, r15 -FN_PREFIX(CryptonightR_instruction170): - imul rsi, r15 -FN_PREFIX(CryptonightR_instruction171): - add rsi, r15 - add rsi, 2147483647 -FN_PREFIX(CryptonightR_instruction172): - sub rsi, r15 -FN_PREFIX(CryptonightR_instruction173): - ror esi, cl -FN_PREFIX(CryptonightR_instruction174): - rol esi, cl -FN_PREFIX(CryptonightR_instruction175): - xor rsi, r15 -FN_PREFIX(CryptonightR_instruction176): - imul rdi, r15 -FN_PREFIX(CryptonightR_instruction177): - imul rdi, r15 -FN_PREFIX(CryptonightR_instruction178): - imul rdi, r15 -FN_PREFIX(CryptonightR_instruction179): - add rdi, r15 - add rdi, 2147483647 -FN_PREFIX(CryptonightR_instruction180): - sub rdi, r15 -FN_PREFIX(CryptonightR_instruction181): - ror edi, cl -FN_PREFIX(CryptonightR_instruction182): - rol edi, cl -FN_PREFIX(CryptonightR_instruction183): - xor rdi, r15 -FN_PREFIX(CryptonightR_instruction184): - imul rbp, r15 -FN_PREFIX(CryptonightR_instruction185): - imul rbp, r15 -FN_PREFIX(CryptonightR_instruction186): - imul rbp, r15 -FN_PREFIX(CryptonightR_instruction187): - add rbp, r15 - add rbp, 2147483647 -FN_PREFIX(CryptonightR_instruction188): - sub rbp, r15 -FN_PREFIX(CryptonightR_instruction189): - ror ebp, cl -FN_PREFIX(CryptonightR_instruction190): - rol ebp, cl -FN_PREFIX(CryptonightR_instruction191): - xor rbp, r15 -FN_PREFIX(CryptonightR_instruction192): - imul rbx, rax -FN_PREFIX(CryptonightR_instruction193): - imul rbx, rax -FN_PREFIX(CryptonightR_instruction194): - imul rbx, rax -FN_PREFIX(CryptonightR_instruction195): - add rbx, rax - add rbx, 2147483647 -FN_PREFIX(CryptonightR_instruction196): - sub rbx, rax -FN_PREFIX(CryptonightR_instruction197): - ror ebx, cl -FN_PREFIX(CryptonightR_instruction198): - rol ebx, cl -FN_PREFIX(CryptonightR_instruction199): - xor rbx, rax -FN_PREFIX(CryptonightR_instruction200): - imul rsi, rax -FN_PREFIX(CryptonightR_instruction201): - imul rsi, rax -FN_PREFIX(CryptonightR_instruction202): - imul rsi, rax -FN_PREFIX(CryptonightR_instruction203): - add rsi, rax - add rsi, 2147483647 -FN_PREFIX(CryptonightR_instruction204): - sub rsi, rax -FN_PREFIX(CryptonightR_instruction205): - ror esi, cl -FN_PREFIX(CryptonightR_instruction206): - rol esi, cl -FN_PREFIX(CryptonightR_instruction207): - xor rsi, rax -FN_PREFIX(CryptonightR_instruction208): - imul rdi, rax -FN_PREFIX(CryptonightR_instruction209): - imul rdi, rax -FN_PREFIX(CryptonightR_instruction210): - imul rdi, rax -FN_PREFIX(CryptonightR_instruction211): - add rdi, rax - add rdi, 2147483647 -FN_PREFIX(CryptonightR_instruction212): - sub rdi, rax -FN_PREFIX(CryptonightR_instruction213): - ror edi, cl -FN_PREFIX(CryptonightR_instruction214): - rol edi, cl -FN_PREFIX(CryptonightR_instruction215): - xor rdi, rax -FN_PREFIX(CryptonightR_instruction216): - imul rbp, rax -FN_PREFIX(CryptonightR_instruction217): - imul rbp, rax -FN_PREFIX(CryptonightR_instruction218): - imul rbp, rax -FN_PREFIX(CryptonightR_instruction219): - add rbp, rax - add rbp, 2147483647 -FN_PREFIX(CryptonightR_instruction220): - sub rbp, rax -FN_PREFIX(CryptonightR_instruction221): - ror ebp, cl -FN_PREFIX(CryptonightR_instruction222): - rol ebp, cl -FN_PREFIX(CryptonightR_instruction223): - xor rbp, rax -FN_PREFIX(CryptonightR_instruction224): - imul rbx, rdx -FN_PREFIX(CryptonightR_instruction225): - imul rbx, rdx -FN_PREFIX(CryptonightR_instruction226): - imul rbx, rdx -FN_PREFIX(CryptonightR_instruction227): - add rbx, rdx - add rbx, 2147483647 -FN_PREFIX(CryptonightR_instruction228): - sub rbx, rdx -FN_PREFIX(CryptonightR_instruction229): - ror ebx, cl -FN_PREFIX(CryptonightR_instruction230): - rol ebx, cl -FN_PREFIX(CryptonightR_instruction231): - xor rbx, rdx -FN_PREFIX(CryptonightR_instruction232): - imul rsi, rdx -FN_PREFIX(CryptonightR_instruction233): - imul rsi, rdx -FN_PREFIX(CryptonightR_instruction234): - imul rsi, rdx -FN_PREFIX(CryptonightR_instruction235): - add rsi, rdx - add rsi, 2147483647 -FN_PREFIX(CryptonightR_instruction236): - sub rsi, rdx -FN_PREFIX(CryptonightR_instruction237): - ror esi, cl -FN_PREFIX(CryptonightR_instruction238): - rol esi, cl -FN_PREFIX(CryptonightR_instruction239): - xor rsi, rdx -FN_PREFIX(CryptonightR_instruction240): - imul rdi, rdx -FN_PREFIX(CryptonightR_instruction241): - imul rdi, rdx -FN_PREFIX(CryptonightR_instruction242): - imul rdi, rdx -FN_PREFIX(CryptonightR_instruction243): - add rdi, rdx - add rdi, 2147483647 -FN_PREFIX(CryptonightR_instruction244): - sub rdi, rdx -FN_PREFIX(CryptonightR_instruction245): - ror edi, cl -FN_PREFIX(CryptonightR_instruction246): - rol edi, cl -FN_PREFIX(CryptonightR_instruction247): - xor rdi, rdx -FN_PREFIX(CryptonightR_instruction248): - imul rbp, rdx -FN_PREFIX(CryptonightR_instruction249): - imul rbp, rdx -FN_PREFIX(CryptonightR_instruction250): - imul rbp, rdx -FN_PREFIX(CryptonightR_instruction251): - add rbp, rdx - add rbp, 2147483647 -FN_PREFIX(CryptonightR_instruction252): - sub rbp, rdx -FN_PREFIX(CryptonightR_instruction253): - ror ebp, cl -FN_PREFIX(CryptonightR_instruction254): - rol ebp, cl -FN_PREFIX(CryptonightR_instruction255): - xor rbp, rdx -FN_PREFIX(CryptonightR_instruction256): - imul rbx, rbx -FN_PREFIX(CryptonightR_instruction_mov0): - -FN_PREFIX(CryptonightR_instruction_mov1): - -FN_PREFIX(CryptonightR_instruction_mov2): - -FN_PREFIX(CryptonightR_instruction_mov3): - -FN_PREFIX(CryptonightR_instruction_mov4): - -FN_PREFIX(CryptonightR_instruction_mov5): - mov rcx, rbx -FN_PREFIX(CryptonightR_instruction_mov6): - mov rcx, rbx -FN_PREFIX(CryptonightR_instruction_mov7): - -FN_PREFIX(CryptonightR_instruction_mov8): - -FN_PREFIX(CryptonightR_instruction_mov9): - -FN_PREFIX(CryptonightR_instruction_mov10): - -FN_PREFIX(CryptonightR_instruction_mov11): - -FN_PREFIX(CryptonightR_instruction_mov12): - -FN_PREFIX(CryptonightR_instruction_mov13): - mov rcx, rbx -FN_PREFIX(CryptonightR_instruction_mov14): - mov rcx, rbx -FN_PREFIX(CryptonightR_instruction_mov15): - -FN_PREFIX(CryptonightR_instruction_mov16): - -FN_PREFIX(CryptonightR_instruction_mov17): - -FN_PREFIX(CryptonightR_instruction_mov18): - -FN_PREFIX(CryptonightR_instruction_mov19): - -FN_PREFIX(CryptonightR_instruction_mov20): - -FN_PREFIX(CryptonightR_instruction_mov21): - mov rcx, rbx -FN_PREFIX(CryptonightR_instruction_mov22): - mov rcx, rbx -FN_PREFIX(CryptonightR_instruction_mov23): - -FN_PREFIX(CryptonightR_instruction_mov24): - -FN_PREFIX(CryptonightR_instruction_mov25): - -FN_PREFIX(CryptonightR_instruction_mov26): - -FN_PREFIX(CryptonightR_instruction_mov27): - -FN_PREFIX(CryptonightR_instruction_mov28): - -FN_PREFIX(CryptonightR_instruction_mov29): - mov rcx, rbx -FN_PREFIX(CryptonightR_instruction_mov30): - mov rcx, rbx -FN_PREFIX(CryptonightR_instruction_mov31): - -FN_PREFIX(CryptonightR_instruction_mov32): - -FN_PREFIX(CryptonightR_instruction_mov33): - -FN_PREFIX(CryptonightR_instruction_mov34): - -FN_PREFIX(CryptonightR_instruction_mov35): - -FN_PREFIX(CryptonightR_instruction_mov36): - -FN_PREFIX(CryptonightR_instruction_mov37): - mov rcx, rsi -FN_PREFIX(CryptonightR_instruction_mov38): - mov rcx, rsi -FN_PREFIX(CryptonightR_instruction_mov39): - -FN_PREFIX(CryptonightR_instruction_mov40): - -FN_PREFIX(CryptonightR_instruction_mov41): - -FN_PREFIX(CryptonightR_instruction_mov42): - -FN_PREFIX(CryptonightR_instruction_mov43): - -FN_PREFIX(CryptonightR_instruction_mov44): - -FN_PREFIX(CryptonightR_instruction_mov45): - mov rcx, rsi -FN_PREFIX(CryptonightR_instruction_mov46): - mov rcx, rsi -FN_PREFIX(CryptonightR_instruction_mov47): - -FN_PREFIX(CryptonightR_instruction_mov48): - -FN_PREFIX(CryptonightR_instruction_mov49): - -FN_PREFIX(CryptonightR_instruction_mov50): - -FN_PREFIX(CryptonightR_instruction_mov51): - -FN_PREFIX(CryptonightR_instruction_mov52): - -FN_PREFIX(CryptonightR_instruction_mov53): - mov rcx, rsi -FN_PREFIX(CryptonightR_instruction_mov54): - mov rcx, rsi -FN_PREFIX(CryptonightR_instruction_mov55): - -FN_PREFIX(CryptonightR_instruction_mov56): - -FN_PREFIX(CryptonightR_instruction_mov57): - -FN_PREFIX(CryptonightR_instruction_mov58): - -FN_PREFIX(CryptonightR_instruction_mov59): - -FN_PREFIX(CryptonightR_instruction_mov60): - -FN_PREFIX(CryptonightR_instruction_mov61): - mov rcx, rsi -FN_PREFIX(CryptonightR_instruction_mov62): - mov rcx, rsi -FN_PREFIX(CryptonightR_instruction_mov63): - -FN_PREFIX(CryptonightR_instruction_mov64): - -FN_PREFIX(CryptonightR_instruction_mov65): - -FN_PREFIX(CryptonightR_instruction_mov66): - -FN_PREFIX(CryptonightR_instruction_mov67): - -FN_PREFIX(CryptonightR_instruction_mov68): - -FN_PREFIX(CryptonightR_instruction_mov69): - mov rcx, rdi -FN_PREFIX(CryptonightR_instruction_mov70): - mov rcx, rdi -FN_PREFIX(CryptonightR_instruction_mov71): - -FN_PREFIX(CryptonightR_instruction_mov72): - -FN_PREFIX(CryptonightR_instruction_mov73): - -FN_PREFIX(CryptonightR_instruction_mov74): - -FN_PREFIX(CryptonightR_instruction_mov75): - -FN_PREFIX(CryptonightR_instruction_mov76): - -FN_PREFIX(CryptonightR_instruction_mov77): - mov rcx, rdi -FN_PREFIX(CryptonightR_instruction_mov78): - mov rcx, rdi -FN_PREFIX(CryptonightR_instruction_mov79): - -FN_PREFIX(CryptonightR_instruction_mov80): - -FN_PREFIX(CryptonightR_instruction_mov81): - -FN_PREFIX(CryptonightR_instruction_mov82): - -FN_PREFIX(CryptonightR_instruction_mov83): - -FN_PREFIX(CryptonightR_instruction_mov84): - -FN_PREFIX(CryptonightR_instruction_mov85): - mov rcx, rdi -FN_PREFIX(CryptonightR_instruction_mov86): - mov rcx, rdi -FN_PREFIX(CryptonightR_instruction_mov87): - -FN_PREFIX(CryptonightR_instruction_mov88): - -FN_PREFIX(CryptonightR_instruction_mov89): - -FN_PREFIX(CryptonightR_instruction_mov90): - -FN_PREFIX(CryptonightR_instruction_mov91): - -FN_PREFIX(CryptonightR_instruction_mov92): - -FN_PREFIX(CryptonightR_instruction_mov93): - mov rcx, rdi -FN_PREFIX(CryptonightR_instruction_mov94): - mov rcx, rdi -FN_PREFIX(CryptonightR_instruction_mov95): - -FN_PREFIX(CryptonightR_instruction_mov96): - -FN_PREFIX(CryptonightR_instruction_mov97): - -FN_PREFIX(CryptonightR_instruction_mov98): - -FN_PREFIX(CryptonightR_instruction_mov99): - -FN_PREFIX(CryptonightR_instruction_mov100): - -FN_PREFIX(CryptonightR_instruction_mov101): - mov rcx, rbp -FN_PREFIX(CryptonightR_instruction_mov102): - mov rcx, rbp -FN_PREFIX(CryptonightR_instruction_mov103): - -FN_PREFIX(CryptonightR_instruction_mov104): - -FN_PREFIX(CryptonightR_instruction_mov105): - -FN_PREFIX(CryptonightR_instruction_mov106): - -FN_PREFIX(CryptonightR_instruction_mov107): - -FN_PREFIX(CryptonightR_instruction_mov108): - -FN_PREFIX(CryptonightR_instruction_mov109): - mov rcx, rbp -FN_PREFIX(CryptonightR_instruction_mov110): - mov rcx, rbp -FN_PREFIX(CryptonightR_instruction_mov111): - -FN_PREFIX(CryptonightR_instruction_mov112): - -FN_PREFIX(CryptonightR_instruction_mov113): - -FN_PREFIX(CryptonightR_instruction_mov114): - -FN_PREFIX(CryptonightR_instruction_mov115): - -FN_PREFIX(CryptonightR_instruction_mov116): - -FN_PREFIX(CryptonightR_instruction_mov117): - mov rcx, rbp -FN_PREFIX(CryptonightR_instruction_mov118): - mov rcx, rbp -FN_PREFIX(CryptonightR_instruction_mov119): - -FN_PREFIX(CryptonightR_instruction_mov120): - -FN_PREFIX(CryptonightR_instruction_mov121): - -FN_PREFIX(CryptonightR_instruction_mov122): - -FN_PREFIX(CryptonightR_instruction_mov123): - -FN_PREFIX(CryptonightR_instruction_mov124): - -FN_PREFIX(CryptonightR_instruction_mov125): - mov rcx, rbp -FN_PREFIX(CryptonightR_instruction_mov126): - mov rcx, rbp -FN_PREFIX(CryptonightR_instruction_mov127): - -FN_PREFIX(CryptonightR_instruction_mov128): - -FN_PREFIX(CryptonightR_instruction_mov129): - -FN_PREFIX(CryptonightR_instruction_mov130): - -FN_PREFIX(CryptonightR_instruction_mov131): - -FN_PREFIX(CryptonightR_instruction_mov132): - -FN_PREFIX(CryptonightR_instruction_mov133): - mov rcx, rsp -FN_PREFIX(CryptonightR_instruction_mov134): - mov rcx, rsp -FN_PREFIX(CryptonightR_instruction_mov135): - -FN_PREFIX(CryptonightR_instruction_mov136): - -FN_PREFIX(CryptonightR_instruction_mov137): - -FN_PREFIX(CryptonightR_instruction_mov138): - -FN_PREFIX(CryptonightR_instruction_mov139): - -FN_PREFIX(CryptonightR_instruction_mov140): - -FN_PREFIX(CryptonightR_instruction_mov141): - mov rcx, rsp -FN_PREFIX(CryptonightR_instruction_mov142): - mov rcx, rsp -FN_PREFIX(CryptonightR_instruction_mov143): - -FN_PREFIX(CryptonightR_instruction_mov144): - -FN_PREFIX(CryptonightR_instruction_mov145): - -FN_PREFIX(CryptonightR_instruction_mov146): - -FN_PREFIX(CryptonightR_instruction_mov147): - -FN_PREFIX(CryptonightR_instruction_mov148): - -FN_PREFIX(CryptonightR_instruction_mov149): - mov rcx, rsp -FN_PREFIX(CryptonightR_instruction_mov150): - mov rcx, rsp -FN_PREFIX(CryptonightR_instruction_mov151): - -FN_PREFIX(CryptonightR_instruction_mov152): - -FN_PREFIX(CryptonightR_instruction_mov153): - -FN_PREFIX(CryptonightR_instruction_mov154): - -FN_PREFIX(CryptonightR_instruction_mov155): - -FN_PREFIX(CryptonightR_instruction_mov156): - -FN_PREFIX(CryptonightR_instruction_mov157): - mov rcx, rsp -FN_PREFIX(CryptonightR_instruction_mov158): - mov rcx, rsp -FN_PREFIX(CryptonightR_instruction_mov159): - -FN_PREFIX(CryptonightR_instruction_mov160): - -FN_PREFIX(CryptonightR_instruction_mov161): - -FN_PREFIX(CryptonightR_instruction_mov162): - -FN_PREFIX(CryptonightR_instruction_mov163): - -FN_PREFIX(CryptonightR_instruction_mov164): - -FN_PREFIX(CryptonightR_instruction_mov165): - mov rcx, r15 -FN_PREFIX(CryptonightR_instruction_mov166): - mov rcx, r15 -FN_PREFIX(CryptonightR_instruction_mov167): - -FN_PREFIX(CryptonightR_instruction_mov168): - -FN_PREFIX(CryptonightR_instruction_mov169): - -FN_PREFIX(CryptonightR_instruction_mov170): - -FN_PREFIX(CryptonightR_instruction_mov171): - -FN_PREFIX(CryptonightR_instruction_mov172): - -FN_PREFIX(CryptonightR_instruction_mov173): - mov rcx, r15 -FN_PREFIX(CryptonightR_instruction_mov174): - mov rcx, r15 -FN_PREFIX(CryptonightR_instruction_mov175): - -FN_PREFIX(CryptonightR_instruction_mov176): - -FN_PREFIX(CryptonightR_instruction_mov177): - -FN_PREFIX(CryptonightR_instruction_mov178): - -FN_PREFIX(CryptonightR_instruction_mov179): - -FN_PREFIX(CryptonightR_instruction_mov180): - -FN_PREFIX(CryptonightR_instruction_mov181): - mov rcx, r15 -FN_PREFIX(CryptonightR_instruction_mov182): - mov rcx, r15 -FN_PREFIX(CryptonightR_instruction_mov183): - -FN_PREFIX(CryptonightR_instruction_mov184): - -FN_PREFIX(CryptonightR_instruction_mov185): - -FN_PREFIX(CryptonightR_instruction_mov186): - -FN_PREFIX(CryptonightR_instruction_mov187): - -FN_PREFIX(CryptonightR_instruction_mov188): - -FN_PREFIX(CryptonightR_instruction_mov189): - mov rcx, r15 -FN_PREFIX(CryptonightR_instruction_mov190): - mov rcx, r15 -FN_PREFIX(CryptonightR_instruction_mov191): - -FN_PREFIX(CryptonightR_instruction_mov192): - -FN_PREFIX(CryptonightR_instruction_mov193): - -FN_PREFIX(CryptonightR_instruction_mov194): - -FN_PREFIX(CryptonightR_instruction_mov195): - -FN_PREFIX(CryptonightR_instruction_mov196): - -FN_PREFIX(CryptonightR_instruction_mov197): - mov rcx, rax -FN_PREFIX(CryptonightR_instruction_mov198): - mov rcx, rax -FN_PREFIX(CryptonightR_instruction_mov199): - -FN_PREFIX(CryptonightR_instruction_mov200): - -FN_PREFIX(CryptonightR_instruction_mov201): - -FN_PREFIX(CryptonightR_instruction_mov202): - -FN_PREFIX(CryptonightR_instruction_mov203): - -FN_PREFIX(CryptonightR_instruction_mov204): - -FN_PREFIX(CryptonightR_instruction_mov205): - mov rcx, rax -FN_PREFIX(CryptonightR_instruction_mov206): - mov rcx, rax -FN_PREFIX(CryptonightR_instruction_mov207): - -FN_PREFIX(CryptonightR_instruction_mov208): - -FN_PREFIX(CryptonightR_instruction_mov209): - -FN_PREFIX(CryptonightR_instruction_mov210): - -FN_PREFIX(CryptonightR_instruction_mov211): - -FN_PREFIX(CryptonightR_instruction_mov212): - -FN_PREFIX(CryptonightR_instruction_mov213): - mov rcx, rax -FN_PREFIX(CryptonightR_instruction_mov214): - mov rcx, rax -FN_PREFIX(CryptonightR_instruction_mov215): - -FN_PREFIX(CryptonightR_instruction_mov216): - -FN_PREFIX(CryptonightR_instruction_mov217): - -FN_PREFIX(CryptonightR_instruction_mov218): - -FN_PREFIX(CryptonightR_instruction_mov219): - -FN_PREFIX(CryptonightR_instruction_mov220): - -FN_PREFIX(CryptonightR_instruction_mov221): - mov rcx, rax -FN_PREFIX(CryptonightR_instruction_mov222): - mov rcx, rax -FN_PREFIX(CryptonightR_instruction_mov223): - -FN_PREFIX(CryptonightR_instruction_mov224): - -FN_PREFIX(CryptonightR_instruction_mov225): - -FN_PREFIX(CryptonightR_instruction_mov226): - -FN_PREFIX(CryptonightR_instruction_mov227): - -FN_PREFIX(CryptonightR_instruction_mov228): - -FN_PREFIX(CryptonightR_instruction_mov229): - mov rcx, rdx -FN_PREFIX(CryptonightR_instruction_mov230): - mov rcx, rdx -FN_PREFIX(CryptonightR_instruction_mov231): - -FN_PREFIX(CryptonightR_instruction_mov232): - -FN_PREFIX(CryptonightR_instruction_mov233): - -FN_PREFIX(CryptonightR_instruction_mov234): - -FN_PREFIX(CryptonightR_instruction_mov235): - -FN_PREFIX(CryptonightR_instruction_mov236): - -FN_PREFIX(CryptonightR_instruction_mov237): - mov rcx, rdx -FN_PREFIX(CryptonightR_instruction_mov238): - mov rcx, rdx -FN_PREFIX(CryptonightR_instruction_mov239): - -FN_PREFIX(CryptonightR_instruction_mov240): - -FN_PREFIX(CryptonightR_instruction_mov241): - -FN_PREFIX(CryptonightR_instruction_mov242): - -FN_PREFIX(CryptonightR_instruction_mov243): - -FN_PREFIX(CryptonightR_instruction_mov244): - -FN_PREFIX(CryptonightR_instruction_mov245): - mov rcx, rdx -FN_PREFIX(CryptonightR_instruction_mov246): - mov rcx, rdx -FN_PREFIX(CryptonightR_instruction_mov247): - -FN_PREFIX(CryptonightR_instruction_mov248): - -FN_PREFIX(CryptonightR_instruction_mov249): - -FN_PREFIX(CryptonightR_instruction_mov250): - -FN_PREFIX(CryptonightR_instruction_mov251): - -FN_PREFIX(CryptonightR_instruction_mov252): - -FN_PREFIX(CryptonightR_instruction_mov253): - mov rcx, rdx -FN_PREFIX(CryptonightR_instruction_mov254): - mov rcx, rdx -FN_PREFIX(CryptonightR_instruction_mov255): - -FN_PREFIX(CryptonightR_instruction_mov256): diff --git a/src/crypto/asm/win/cn_main_loop.asm b/src/crypto/asm/win/cn_main_loop.asm deleted file mode 100644 index 72834397..00000000 --- a/src/crypto/asm/win/cn_main_loop.asm +++ /dev/null @@ -1,287 +0,0 @@ -_TEXT_CN_MAINLOOP SEGMENT PAGE READ EXECUTE - -PUBLIC cnv1_main_loop_sandybridge_asm -PUBLIC cnv1_main_loop_lite_sandybridge_asm -PUBLIC cnv1_main_loop_fast_sandybridge_asm -PUBLIC cnv1_main_loop_upx_sandybridge_asm -PUBLIC cnv1_main_loop_rto_sandybridge_asm - -PUBLIC cnv2_main_loop_ivybridge_asm -PUBLIC cnv2_main_loop_ryzen_asm -PUBLIC cnv2_main_loop_bulldozer_asm -PUBLIC cnv2_double_main_loop_sandybridge_asm - -PUBLIC cnv2_main_loop_fastv2_ivybridge_asm -PUBLIC cnv2_main_loop_fastv2_ryzen_asm -PUBLIC cnv2_main_loop_fastv2_bulldozer_asm -PUBLIC cnv2_double_main_loop_fastv2_sandybridge_asm - -PUBLIC cnv2_main_loop_ultralite_ivybridge_asm -PUBLIC cnv2_main_loop_ultralite_ryzen_asm -PUBLIC cnv2_main_loop_ultralite_bulldozer_asm -PUBLIC cnv2_double_main_loop_ultralite_sandybridge_asm - -PUBLIC cnv2_main_loop_xcash_ivybridge_asm -PUBLIC cnv2_main_loop_xcash_ryzen_asm -PUBLIC cnv2_main_loop_xcash_bulldozer_asm -PUBLIC cnv2_double_main_loop_xcash_sandybridge_asm - -PUBLIC cnv2_main_loop_zelerius_ivybridge_asm -PUBLIC cnv2_main_loop_zelerius_ryzen_asm -PUBLIC cnv2_main_loop_zelerius_bulldozer_asm -PUBLIC cnv2_double_main_loop_zelerius_sandybridge_asm - -PUBLIC cnv2_main_loop_rwz_original_all_asm -PUBLIC cnv2_double_main_loop_rwz_original_all_asm - -PUBLIC cnv2_main_loop_rwz_upx2_all_asm -PUBLIC cnv2_double_main_loop_rwz_upx2_all_asm - -PUBLIC cnv1_main_loop_soft_aes_sandybridge_asm -PUBLIC cnv1_main_loop_lite_soft_aes_sandybridge_asm -PUBLIC cnv1_main_loop_fast_soft_aes_sandybridge_asm -PUBLIC cnv1_main_loop_upx_soft_aes_sandybridge_asm -PUBLIC cnv1_main_loop_rto_soft_aes_sandybridge_asm - -PUBLIC cnv2_main_loop_soft_aes_sandybridge_asm -PUBLIC cnv2_main_loop_fastv2_soft_aes_sandybridge_asm -PUBLIC cnv2_main_loop_ultralite_soft_aes_sandybridge_asm -PUBLIC cnv2_main_loop_xcash_soft_aes_sandybridge_asm -PUBLIC cnv2_main_loop_zelerius_soft_aes_sandybridge_asm - -ALIGN 64 -cnv1_main_loop_sandybridge_asm PROC - INCLUDE cnv1_main_loop_sandybridge.inc - ret 0 -cnv1_main_loop_sandybridge_asm ENDP - -ALIGN 64 -cnv1_main_loop_lite_sandybridge_asm PROC - INCLUDE cnv1_main_loop_lite_sandybridge.inc - ret 0 -cnv1_main_loop_lite_sandybridge_asm ENDP - -ALIGN 64 -cnv1_main_loop_fast_sandybridge_asm PROC - INCLUDE cnv1_main_loop_fast_sandybridge.inc - ret 0 -cnv1_main_loop_fast_sandybridge_asm ENDP - -ALIGN 64 -cnv1_main_loop_upx_sandybridge_asm PROC - INCLUDE cnv1_main_loop_upx_sandybridge.inc - ret 0 -cnv1_main_loop_upx_sandybridge_asm ENDP - -ALIGN 64 -cnv1_main_loop_rto_sandybridge_asm PROC - INCLUDE cnv1_main_loop_rto_sandybridge.inc - ret 0 -cnv1_main_loop_rto_sandybridge_asm ENDP - -ALIGN 64 -cnv2_main_loop_ivybridge_asm PROC - INCLUDE cnv2_main_loop_ivybridge.inc - ret 0 -cnv2_main_loop_ivybridge_asm ENDP - -ALIGN 64 -cnv2_main_loop_ryzen_asm PROC - INCLUDE cnv2_main_loop_ryzen.inc - ret 0 -cnv2_main_loop_ryzen_asm ENDP - -ALIGN 64 -cnv2_main_loop_bulldozer_asm PROC - INCLUDE cnv2_main_loop_bulldozer.inc - ret 0 -cnv2_main_loop_bulldozer_asm ENDP - -ALIGN 64 -cnv2_double_main_loop_sandybridge_asm PROC - INCLUDE cnv2_double_main_loop_sandybridge.inc - ret 0 -cnv2_double_main_loop_sandybridge_asm ENDP - -ALIGN 64 -cnv2_main_loop_fastv2_ivybridge_asm PROC - INCLUDE cnv2_main_loop_fastv2_ivybridge.inc - ret 0 -cnv2_main_loop_fastv2_ivybridge_asm ENDP - -ALIGN 64 -cnv2_main_loop_fastv2_ryzen_asm PROC - INCLUDE cnv2_main_loop_fastv2_ryzen.inc - ret 0 -cnv2_main_loop_fastv2_ryzen_asm ENDP - -ALIGN 64 -cnv2_main_loop_fastv2_bulldozer_asm PROC - INCLUDE cnv2_main_loop_fastv2_bulldozer.inc - ret 0 -cnv2_main_loop_fastv2_bulldozer_asm ENDP - -ALIGN 64 -cnv2_double_main_loop_fastv2_sandybridge_asm PROC - INCLUDE cnv2_double_main_loop_fastv2_sandybridge.inc - ret 0 -cnv2_double_main_loop_fastv2_sandybridge_asm ENDP - -ALIGN 64 -cnv2_main_loop_ultralite_ivybridge_asm PROC - INCLUDE cnv2_main_loop_ultralite_ivybridge.inc - ret 0 -cnv2_main_loop_ultralite_ivybridge_asm ENDP - -ALIGN 64 -cnv2_main_loop_ultralite_ryzen_asm PROC - INCLUDE cnv2_main_loop_ultralite_ryzen.inc - ret 0 -cnv2_main_loop_ultralite_ryzen_asm ENDP - -ALIGN 64 -cnv2_main_loop_ultralite_bulldozer_asm PROC - INCLUDE cnv2_main_loop_ultralite_bulldozer.inc - ret 0 -cnv2_main_loop_ultralite_bulldozer_asm ENDP - -ALIGN 64 -cnv2_double_main_loop_ultralite_sandybridge_asm PROC - INCLUDE cnv2_double_main_loop_ultralite_sandybridge.inc - ret 0 -cnv2_double_main_loop_ultralite_sandybridge_asm ENDP - -ALIGN 64 -cnv2_main_loop_xcash_ivybridge_asm PROC - INCLUDE cnv2_main_loop_xcash_ivybridge.inc - ret 0 -cnv2_main_loop_xcash_ivybridge_asm ENDP - -ALIGN 64 -cnv2_main_loop_xcash_ryzen_asm PROC - INCLUDE cnv2_main_loop_xcash_ryzen.inc - ret 0 -cnv2_main_loop_xcash_ryzen_asm ENDP - -ALIGN 64 -cnv2_main_loop_xcash_bulldozer_asm PROC - INCLUDE cnv2_main_loop_xcash_bulldozer.inc - ret 0 -cnv2_main_loop_xcash_bulldozer_asm ENDP - -ALIGN 64 -cnv2_double_main_loop_xcash_sandybridge_asm PROC - INCLUDE cnv2_double_main_loop_xcash_sandybridge.inc - ret 0 -cnv2_double_main_loop_xcash_sandybridge_asm ENDP - -ALIGN 64 -cnv2_main_loop_zelerius_ivybridge_asm PROC - INCLUDE cnv2_main_loop_zelerius_ivybridge.inc - ret 0 -cnv2_main_loop_zelerius_ivybridge_asm ENDP - -ALIGN 64 -cnv2_main_loop_zelerius_ryzen_asm PROC - INCLUDE cnv2_main_loop_zelerius_ryzen.inc - ret 0 -cnv2_main_loop_zelerius_ryzen_asm ENDP - -ALIGN 64 -cnv2_main_loop_zelerius_bulldozer_asm PROC - INCLUDE cnv2_main_loop_zelerius_bulldozer.inc - ret 0 -cnv2_main_loop_zelerius_bulldozer_asm ENDP - -ALIGN 64 -cnv2_double_main_loop_zelerius_sandybridge_asm PROC - INCLUDE cnv2_double_main_loop_zelerius_sandybridge.inc - ret 0 -cnv2_double_main_loop_zelerius_sandybridge_asm ENDP - -ALIGN 64 -cnv2_main_loop_rwz_original_all_asm PROC - INCLUDE cnv2_main_loop_rwz_original_all.inc - ret 0 -cnv2_main_loop_rwz_original_all_asm ENDP - -ALIGN 64 -cnv2_double_main_loop_rwz_original_all_asm PROC - INCLUDE cnv2_double_main_loop_rwz_original_all.inc - ret 0 -cnv2_double_main_loop_rwz_original_all_asm ENDP - -ALIGN 64 -cnv2_main_loop_rwz_upx2_all_asm PROC - INCLUDE cnv2_main_loop_rwz_upx2_all.inc - ret 0 -cnv2_main_loop_rwz_upx2_all_asm ENDP - -ALIGN 64 -cnv2_double_main_loop_rwz_upx2_all_asm PROC - INCLUDE cnv2_double_main_loop_rwz_upx2_all.inc - ret 0 -cnv2_double_main_loop_rwz_upx2_all_asm ENDP - -ALIGN 64 -cnv1_main_loop_soft_aes_sandybridge_asm PROC - INCLUDE cnv1_main_loop_soft_aes_sandybridge.inc - ret 0 -cnv1_main_loop_soft_aes_sandybridge_asm ENDP - -ALIGN 64 -cnv1_main_loop_lite_soft_aes_sandybridge_asm PROC - INCLUDE cnv1_main_loop_lite_soft_aes_sandybridge.inc - ret 0 -cnv1_main_loop_lite_soft_aes_sandybridge_asm ENDP - -ALIGN 64 -cnv1_main_loop_fast_soft_aes_sandybridge_asm PROC - INCLUDE cnv1_main_loop_fast_soft_aes_sandybridge.inc - ret 0 -cnv1_main_loop_fast_soft_aes_sandybridge_asm ENDP - -ALIGN 64 -cnv1_main_loop_upx_soft_aes_sandybridge_asm PROC - INCLUDE cnv1_main_loop_upx_soft_aes_sandybridge.inc - ret 0 -cnv1_main_loop_upx_soft_aes_sandybridge_asm ENDP - -ALIGN 64 -cnv1_main_loop_rto_soft_aes_sandybridge_asm PROC - INCLUDE cnv1_main_loop_rto_soft_aes_sandybridge.inc - ret 0 -cnv1_main_loop_rto_soft_aes_sandybridge_asm ENDP - -ALIGN 64 -cnv2_main_loop_soft_aes_sandybridge_asm PROC - INCLUDE cnv2_main_loop_soft_aes_sandybridge.inc - ret 0 -cnv2_main_loop_soft_aes_sandybridge_asm ENDP - -ALIGN 64 -cnv2_main_loop_fastv2_soft_aes_sandybridge_asm PROC - INCLUDE cnv2_main_loop_fastv2_soft_aes_sandybridge.inc - ret 0 -cnv2_main_loop_fastv2_soft_aes_sandybridge_asm ENDP - -ALIGN 64 -cnv2_main_loop_ultralite_soft_aes_sandybridge_asm PROC - INCLUDE cnv2_main_loop_ultralite_soft_aes_sandybridge.inc - ret 0 -cnv2_main_loop_ultralite_soft_aes_sandybridge_asm ENDP - -ALIGN 64 -cnv2_main_loop_xcash_soft_aes_sandybridge_asm PROC - INCLUDE cnv2_main_loop_xcash_soft_aes_sandybridge.inc - ret 0 -cnv2_main_loop_xcash_soft_aes_sandybridge_asm ENDP - -ALIGN 64 -cnv2_main_loop_zelerius_soft_aes_sandybridge_asm PROC - INCLUDE cnv2_main_loop_zelerius_soft_aes_sandybridge.inc - ret 0 -cnv2_main_loop_zelerius_soft_aes_sandybridge_asm ENDP - -_TEXT_CN_MAINLOOP ENDS -END \ No newline at end of file diff --git a/src/crypto/asm/win/cn_main_loop_win_gcc.S b/src/crypto/asm/win/cn_main_loop_win_gcc.S deleted file mode 100644 index beaab805..00000000 --- a/src/crypto/asm/win/cn_main_loop_win_gcc.S +++ /dev/null @@ -1,248 +0,0 @@ -#define ALIGN .align -.intel_syntax noprefix -# define FN_PREFIX(fn) fn -.section .text - -.global FN_PREFIX(cnv1_main_loop_sandybridge_asm) -.global FN_PREFIX(cnv1_main_loop_lite_sandybridge_asm) -.global FN_PREFIX(cnv1_main_loop_fast_sandybridge_asm) -.global FN_PREFIX(cnv1_main_loop_upx_sandybridge_asm) -.global FN_PREFIX(cnv1_main_loop_rto_sandybridge_asm) - -.global FN_PREFIX(cnv2_main_loop_ivybridge_asm) -.global FN_PREFIX(cnv2_main_loop_ryzen_asm) -.global FN_PREFIX(cnv2_main_loop_bulldozer_asm) -.global FN_PREFIX(cnv2_double_main_loop_sandybridge_asm) - -.global FN_PREFIX(cnv2_main_loop_fastv2_ivybridge_asm) -.global FN_PREFIX(cnv2_main_loop_fastv2_ryzen_asm) -.global FN_PREFIX(cnv2_main_loop_fastv2_bulldozer_asm) -.global FN_PREFIX(cnv2_double_main_loop_fastv2_sandybridge_asm) - -.global FN_PREFIX(cnv2_main_loop_ultralite_ivybridge_asm) -.global FN_PREFIX(cnv2_main_loop_ultralite_ryzen_asm) -.global FN_PREFIX(cnv2_main_loop_ultralite_bulldozer_asm) -.global FN_PREFIX(cnv2_double_main_loop_ultralite_sandybridge_asm) - -.global FN_PREFIX(cnv2_main_loop_xcash_ivybridge_asm) -.global FN_PREFIX(cnv2_main_loop_xcash_ryzen_asm) -.global FN_PREFIX(cnv2_main_loop_xcash_bulldozer_asm) -.global FN_PREFIX(cnv2_double_main_loop_xcash_sandybridge_asm) - -.global FN_PREFIX(cnv2_main_loop_zelerius_ivybridge_asm) -.global FN_PREFIX(cnv2_main_loop_zelerius_ryzen_asm) -.global FN_PREFIX(cnv2_main_loop_zelerius_bulldozer_asm) -.global FN_PREFIX(cnv2_double_main_loop_zelerius_sandybridge_asm) - -.global FN_PREFIX(cnv2_main_loop_rwz_original_all_asm) -.global FN_PREFIX(cnv2_double_main_loop_rwz_original_all_asm) - -.global FN_PREFIX(cnv2_main_loop_rwz_upx2_all_asm) -.global FN_PREFIX(cnv2_double_main_loop_rwz_upx2_all_asm) - -.global FN_PREFIX(cnv1_main_loop_soft_aes_sandybridge_asm) -.global FN_PREFIX(cnv1_main_loop_lite_soft_aes_sandybridge_asm) -.global FN_PREFIX(cnv1_main_loop_fast_soft_aes_sandybridge_asm) -.global FN_PREFIX(cnv1_main_loop_upx_soft_aes_sandybridge_asm) -.global FN_PREFIX(cnv1_main_loop_rto_soft_aes_sandybridge_asm) - -.global FN_PREFIX(cnv2_main_loop_soft_aes_sandybridge_asm) -.global FN_PREFIX(cnv2_main_loop_fastv2_soft_aes_sandybridge_asm) -.global FN_PREFIX(cnv2_main_loop_ultralite_soft_aes_sandybridge_asm) -.global FN_PREFIX(cnv2_main_loop_xcash_soft_aes_sandybridge_asm) -.global FN_PREFIX(cnv2_main_loop_zelerius_soft_aes_sandybridge_asm) - -ALIGN 64 -FN_PREFIX(cnv1_main_loop_sandybridge_asm): - #include "../cnv1_main_loop_sandybridge.inc" - ret 0 - -ALIGN 64 -FN_PREFIX(cnv1_main_loop_lite_sandybridge_asm): - #include "../cnv1_main_loop_lite_sandybridge.inc" - ret 0 - -ALIGN 64 -FN_PREFIX(cnv1_main_loop_fast_sandybridge_asm): - #include "../cnv1_main_loop_fast_sandybridge.inc" - ret 0 - -ALIGN 64 -FN_PREFIX(cnv1_main_loop_upx_sandybridge_asm): - #include "../cnv1_main_loop_upx_sandybridge.inc" - ret 0 - -ALIGN 64 -FN_PREFIX(cnv1_main_loop_rto_sandybridge_asm): - #include "../cnv1_main_loop_rto_sandybridge.inc" - ret 0 - -ALIGN 64 -FN_PREFIX(cnv2_main_loop_ivybridge_asm): - #include "../cnv2_main_loop_ivybridge.inc" - ret 0 - -ALIGN 64 -FN_PREFIX(cnv2_main_loop_ryzen_asm): - #include "../cnv2_main_loop_ryzen.inc" - ret 0 - -ALIGN 64 -FN_PREFIX(cnv2_main_loop_bulldozer_asm): - #include "../cnv2_main_loop_bulldozer.inc" - ret 0 - -ALIGN 64 -FN_PREFIX(cnv2_double_main_loop_sandybridge_asm): - #include "../cnv2_double_main_loop_sandybridge.inc" - ret 0 - -ALIGN 64 -FN_PREFIX(cnv2_main_loop_fastv2_ivybridge_asm): - #include "../cnv2_main_loop_fastv2_ivybridge.inc" - ret 0 - -ALIGN 64 -FN_PREFIX(cnv2_main_loop_fastv2_ryzen_asm): - #include "../cnv2_main_loop_fastv2_ryzen.inc" - ret 0 - -ALIGN 64 -FN_PREFIX(cnv2_main_loop_fastv2_bulldozer_asm): - #include "../cnv2_main_loop_fastv2_bulldozer.inc" - ret 0 - -ALIGN 64 -FN_PREFIX(cnv2_double_main_loop_fastv2_sandybridge_asm): - #include "../cnv2_double_main_loop_fastv2_sandybridge.inc" - ret 0 - -ALIGN 64 -FN_PREFIX(cnv2_main_loop_ultralite_ivybridge_asm): - #include "../cnv2_main_loop_ultralite_ivybridge.inc" - ret 0 - -ALIGN 64 -FN_PREFIX(cnv2_main_loop_ultralite_ryzen_asm): - #include "../cnv2_main_loop_ultralite_ryzen.inc" - ret 0 - -ALIGN 64 -FN_PREFIX(cnv2_main_loop_ultralite_bulldozer_asm): - #include "../cnv2_main_loop_ultralite_bulldozer.inc" - ret 0 - -ALIGN 64 -FN_PREFIX(cnv2_double_main_loop_ultralite_sandybridge_asm): - #include "../cnv2_double_main_loop_ultralite_sandybridge.inc" - ret 0 - -ALIGN 64 -FN_PREFIX(cnv2_main_loop_xcash_ivybridge_asm): - #include "../cnv2_main_loop_xcash_ivybridge.inc" - ret 0 - -ALIGN 64 -FN_PREFIX(cnv2_main_loop_xcash_ryzen_asm): - #include "../cnv2_main_loop_xcash_ryzen.inc" - ret 0 - -ALIGN 64 -FN_PREFIX(cnv2_main_loop_xcash_bulldozer_asm): - #include "../cnv2_main_loop_xcash_bulldozer.inc" - ret 0 - -ALIGN 64 -FN_PREFIX(cnv2_double_main_loop_xcash_sandybridge_asm): - #include "../cnv2_double_main_loop_xcash_sandybridge.inc" - ret 0 - -ALIGN 64 -FN_PREFIX(cnv2_main_loop_zelerius_ivybridge_asm): - #include "../cnv2_main_loop_zelerius_ivybridge.inc" - ret 0 - -ALIGN 64 -FN_PREFIX(cnv2_main_loop_zelerius_ryzen_asm): - #include "../cnv2_main_loop_zelerius_ryzen.inc" - ret 0 - -ALIGN 64 -FN_PREFIX(cnv2_main_loop_zelerius_bulldozer_asm): - #include "../cnv2_main_loop_zelerius_bulldozer.inc" - ret 0 - -ALIGN 64 -FN_PREFIX(cnv2_double_main_loop_zelerius_sandybridge_asm): - #include "../cnv2_double_main_loop_zelerius_sandybridge.inc" - ret 0 - -ALIGN 64 -FN_PREFIX(cnv2_main_loop_rwz_original_all_asm): - #include "../cnv2_main_loop_rwz_original_all.inc" - ret 0 - -ALIGN 64 -FN_PREFIX(cnv2_double_main_loop_rwz_original_all_asm): - #include "../cnv2_double_main_loop_rwz_original_all.inc" - ret 0 - -ALIGN 64 -FN_PREFIX(cnv2_main_loop_rwz_upx2_all_asm): - #include "../cnv2_main_loop_rwz_upx2_all.inc" - ret 0 - -ALIGN 64 -FN_PREFIX(cnv2_double_main_loop_rwz_upx2_all_asm): - #include "../cnv2_double_main_loop_rwz_upx2_all.inc" - ret 0 - -ALIGN 64 -FN_PREFIX(cnv1_main_loop_soft_aes_sandybridge_asm): - #include "../cnv1_main_loop_soft_aes_sandybridge.inc" - ret 0 - -ALIGN 64 -FN_PREFIX(cnv1_main_loop_lite_soft_aes_sandybridge_asm): - #include "../cnv1_main_loop_lite_soft_aes_sandybridge.inc" - ret 0 - -ALIGN 64 -FN_PREFIX(cnv1_main_loop_fast_soft_aes_sandybridge_asm): - #include "../cnv1_main_loop_fast_soft_aes_sandybridge.inc" - ret 0 - -ALIGN 64 -FN_PREFIX(cnv1_main_loop_upx_soft_aes_sandybridge_asm): - #include "../cnv1_main_loop_upx_soft_aes_sandybridge.inc" - ret 0 - -ALIGN 64 -FN_PREFIX(cnv1_main_loop_rto_soft_aes_sandybridge_asm): - #include "../cnv1_main_loop_rto_soft_aes_sandybridge.inc" - ret 0 - -ALIGN 64 -FN_PREFIX(cnv2_main_loop_soft_aes_sandybridge_asm): - #include "../cnv2_main_loop_soft_aes_sandybridge.inc" - ret 0 - -ALIGN 64 -FN_PREFIX(cnv2_main_loop_fastv2_soft_aes_sandybridge_asm): - #include "../cnv2_main_loop_fastv2_soft_aes_sandybridge.inc" - ret 0 - -ALIGN 64 -FN_PREFIX(cnv2_main_loop_ultralite_soft_aes_sandybridge_asm): - #include "../cnv2_main_loop_ultralite_soft_aes_sandybridge.inc" - ret 0 - -ALIGN 64 -FN_PREFIX(cnv2_main_loop_xcash_soft_aes_sandybridge_asm): - #include "../cnv2_main_loop_xcash_soft_aes_sandybridge.inc" - ret 0 - -ALIGN 64 -FN_PREFIX(cnv2_main_loop_zelerius_soft_aes_sandybridge_asm): - #include "../cnv2_main_loop_zelerius_soft_aes_sandybridge.inc" - ret 0 \ No newline at end of file diff --git a/src/crypto/asm/win/cnv1_main_loop_rto_sandybridge.inc b/src/crypto/asm/win/cnv1_main_loop_rto_sandybridge.inc deleted file mode 100644 index e3d74af7..00000000 --- a/src/crypto/asm/win/cnv1_main_loop_rto_sandybridge.inc +++ /dev/null @@ -1,71 +0,0 @@ - mov QWORD PTR [rsp+8], rbx - mov QWORD PTR [rsp+16], rbp - mov QWORD PTR [rsp+24], rsi - mov QWORD PTR [rsp+32], rdi - push r14 - push r15 - mov rax, QWORD PTR [rcx+48] - mov ebp, 524288 - xor rax, QWORD PTR [rcx+16] - mov rdx, QWORD PTR [rcx+56] - xor rdx, QWORD PTR [rcx+24] - mov r8, QWORD PTR [rcx+32] - xor r8, QWORD PTR [rcx] - movq xmm3, rax - mov rax, QWORD PTR [rcx+256] - mov rdi, QWORD PTR [rcx+40] - movq xmm0, rdx - xor rdi, QWORD PTR [rcx+8] - mov rdx, r8 - mov r15, QWORD PTR [rcx+264] - and edx, 2097136 - mov r14, QWORD PTR [rax+35] - xor r14, QWORD PTR [rcx+192] - mov rsi, QWORD PTR [rcx+224] - punpcklqdq xmm3, xmm0 - movdqu xmm2, XMMWORD PTR [rdx+rsi] - - ALIGN 64 -cnv1_main_loop_rto_sandybridge: - movq xmm0, rdi - movq xmm1, r8 - punpcklqdq xmm1, xmm0 - aesenc xmm2, xmm1 - movq r10, xmm2 - mov r9d, r10d - and r9d, 2097136 - add r9, rsi - movdqa xmm0, xmm2 - pxor xmm0, xmm3 - movdqa xmm3, xmm2 - movdqu XMMWORD PTR [rdx+rsi], xmm0 - psrldq xmm0, 11 - movq rax, xmm0 - movzx eax, al - movzx eax, BYTE PTR [rax+r15] - mov BYTE PTR [rsi+rdx+11], al - mov rbx, QWORD PTR [r9] - mov r11, QWORD PTR [r9+8] - mov rax, rbx - mul r10 - add r8, rdx - mov QWORD PTR [r9], r8 - add rdi, rax - mov rax, r14 - xor rax, rdi - xor rax, r8 - mov QWORD PTR [r9+8], rax - xor r8, rbx - mov rdx, r8 - and edx, 2097136 - movdqu xmm2, XMMWORD PTR [rdx+rsi] - xor rdi, r11 - dec ebp - jne cnv1_main_loop_rto_sandybridge - - mov rbx, QWORD PTR [rsp+24] - mov rbp, QWORD PTR [rsp+32] - mov rsi, QWORD PTR [rsp+40] - mov rdi, QWORD PTR [rsp+48] - pop r15 - pop r14 diff --git a/src/crypto/asm/win/cnv1_main_loop_rto_soft_aes_sandybridge.inc b/src/crypto/asm/win/cnv1_main_loop_rto_soft_aes_sandybridge.inc deleted file mode 100644 index 874d51b6..00000000 --- a/src/crypto/asm/win/cnv1_main_loop_rto_soft_aes_sandybridge.inc +++ /dev/null @@ -1,163 +0,0 @@ - push rbx - push rbp - push rsi - push rdi - push r12 - push r13 - push r14 - push r15 - sub rsp, 72 - - movaps XMMWORD PTR [rsp], xmm6 - movaps XMMWORD PTR [rsp+16], xmm7 - movaps XMMWORD PTR [rsp+32], xmm8 - movaps XMMWORD PTR [rsp+48], xmm9 - - mov rax, QWORD PTR [rcx+48] - xor rax, QWORD PTR [rcx+16] - mov rdx, QWORD PTR [rcx+56] - xor rdx, QWORD PTR [rcx+24] - mov r8, QWORD PTR [rcx+32] - xor r8, QWORD PTR [rcx] - movq xmm4, rax - mov rax, QWORD PTR [rcx+256] - mov r13, QWORD PTR [rcx+40] - movq xmm0, rdx - xor r13, QWORD PTR [rcx+8] - mov rdx, r8 - mov rdi, QWORD PTR [rcx+224] - and edx, 2097136 - mov rax, QWORD PTR [rax+35] - xor rax, QWORD PTR [rcx+192] - movq xmm5, rax - movq xmm8, rdi - punpcklqdq xmm4, xmm0 - mov QWORD PTR [rsp+64], rdx - - movq xmm6, rcx - mov rax, QWORD PTR [rcx+264] - movq xmm7, rax - - mov eax, 524288 - - ALIGN 64 -cnv1_main_loop_rto_soft_aes_sandybridge: - movq xmm9, rax - mov r12, QWORD PTR [rcx+272] - mov esi, DWORD PTR [rdx+rdi] - mov r10d, DWORD PTR [rdx+rdi+4] - mov ebp, DWORD PTR [rdx+rdi+12] - mov r14d, DWORD PTR [rdx+rdi+8] - mov rdx, QWORD PTR [rsp+64] - movzx ecx, sil - shr esi, 8 - mov r15d, DWORD PTR [r12+rcx*4] - movzx ecx, r10b - shr r10d, 8 - mov edi, DWORD PTR [r12+rcx*4] - movzx ecx, r14b - shr r14d, 8 - mov ebx, DWORD PTR [r12+rcx*4] - movzx ecx, bpl - shr ebp, 8 - mov r9d, DWORD PTR [r12+rcx*4] - movzx ecx, r10b - shr r10d, 8 - xor r15d, DWORD PTR [r12+rcx*4+1024] - movzx ecx, r14b - shr r14d, 8 - mov eax, r14d - shr eax, 8 - xor edi, DWORD PTR [r12+rcx*4+1024] - add eax, 256 - movzx ecx, bpl - shr ebp, 8 - xor ebx, DWORD PTR [r12+rcx*4+1024] - movzx ecx, sil - shr esi, 8 - xor r9d, DWORD PTR [r12+rcx*4+1024] - add r12, 2048 - movzx ecx, r10b - shr r10d, 8 - add r10d, 256 - mov r11d, DWORD PTR [r12+rax*4] - xor r11d, DWORD PTR [r12+rcx*4] - xor r11d, r9d - movzx ecx, sil - mov r10d, DWORD PTR [r12+r10*4] - shr esi, 8 - add esi, 256 - xor r10d, DWORD PTR [r12+rcx*4] - movzx ecx, bpl - xor r10d, ebx - shr ebp, 8 - add ebp, 256 - movd xmm1, r11d - mov r9d, DWORD PTR [r12+rcx*4] - xor r9d, DWORD PTR [r12+rsi*4] - mov eax, DWORD PTR [r12+rbp*4] - xor r9d, edi - movq rdi, xmm8 - movzx ecx, r14b - movd xmm0, r10d - movd xmm2, r9d - punpckldq xmm2, xmm1 - movq xmm1, r8 - xor eax, DWORD PTR [r12+rcx*4] - xor eax, r15d - movd xmm3, eax - movq rax, xmm7 - punpckldq xmm3, xmm0 - movq xmm0, r13 - punpcklqdq xmm1, xmm0 - punpckldq xmm3, xmm2 - pxor xmm3, xmm1 - movq r9, xmm3 - mov r10d, r9d - and r10d, 2097136 - movdqa xmm0, xmm3 - pxor xmm0, xmm4 - movdqu XMMWORD PTR [rdx+rdi], xmm0 - psrldq xmm0, 11 - movq rcx, xmm0 - movzx ecx, cl - mov cl, BYTE PTR [rcx+rax] - mov BYTE PTR [rdi+rdx+11], cl - mov rbx, QWORD PTR [r10+rdi] - mov rcx, r9 - lea r9, QWORD PTR [r10+rdi] - mov r11, QWORD PTR [r9+8] - mov rax, rbx - movdqa xmm4, xmm3 - mul rcx - movq rcx, xmm6 - add r8, rdx - add r13, rax - movq rax, xmm5 - xor rax, r13 - mov QWORD PTR [r9], r8 - xor rax, r8 - xor r8, rbx - mov QWORD PTR [r9+8], rax - movq rax, xmm9 - mov rdx, r8 - xor r13, r11 - and edx, 2097136 - mov QWORD PTR [rsp+64], rdx - sub eax, 1 - jne cnv1_main_loop_rto_soft_aes_sandybridge - - movaps xmm6, XMMWORD PTR [rsp] - movaps xmm7, XMMWORD PTR [rsp+16] - movaps xmm8, XMMWORD PTR [rsp+32] - movaps xmm9, XMMWORD PTR [rsp+48] - - add rsp, 72 - pop r15 - pop r14 - pop r13 - pop r12 - pop rdi - pop rsi - pop rbp - pop rbx diff --git a/src/crypto/asm/win/cnv1_main_loop_sandybridge.inc.in b/src/crypto/asm/win/cnv1_main_loop_sandybridge.inc.in deleted file mode 100644 index 2fe423f4..00000000 --- a/src/crypto/asm/win/cnv1_main_loop_sandybridge.inc.in +++ /dev/null @@ -1,70 +0,0 @@ - mov QWORD PTR [rsp+8], rbx - mov QWORD PTR [rsp+16], rbp - mov QWORD PTR [rsp+24], rsi - mov QWORD PTR [rsp+32], rdi - push r14 - push r15 - mov rax, QWORD PTR [rcx+48] - mov ebp, ${ITERATIONS} - xor rax, QWORD PTR [rcx+16] - mov rdx, QWORD PTR [rcx+56] - xor rdx, QWORD PTR [rcx+24] - mov r8, QWORD PTR [rcx+32] - xor r8, QWORD PTR [rcx] - movq xmm3, rax - mov rax, QWORD PTR [rcx+256] - mov rdi, QWORD PTR [rcx+40] - movq xmm0, rdx - xor rdi, QWORD PTR [rcx+8] - mov rdx, r8 - mov r15, QWORD PTR [rcx+264] - and edx, ${MASK} - mov r14, QWORD PTR [rax+35] - xor r14, QWORD PTR [rcx+192] - mov rsi, QWORD PTR [rcx+224] - punpcklqdq xmm3, xmm0 - movdqu xmm2, XMMWORD PTR [rdx+rsi] - - ALIGN 64 -cnv1_main_loop_${ALGO}_sandybridge: - movq xmm0, rdi - movq xmm1, r8 - punpcklqdq xmm1, xmm0 - aesenc xmm2, xmm1 - movq r10, xmm2 - mov r9d, r10d - and r9d, ${MASK} - add r9, rsi - movdqa xmm0, xmm2 - pxor xmm0, xmm3 - movdqa xmm3, xmm2 - movdqu XMMWORD PTR [rdx+rsi], xmm0 - psrldq xmm0, 11 - movq rax, xmm0 - movzx eax, al - movzx eax, BYTE PTR [rax+r15] - mov BYTE PTR [rsi+rdx+11], al - mov rbx, QWORD PTR [r9] - mov r11, QWORD PTR [r9+8] - mov rax, rbx - mul r10 - add r8, rdx - mov QWORD PTR [r9], r8 - add rdi, rax - mov rax, r14 - xor rax, rdi - mov QWORD PTR [r9+8], rax - xor r8, rbx - mov rdx, r8 - and edx, ${MASK} - movdqu xmm2, XMMWORD PTR [rdx+rsi] - xor rdi, r11 - dec ebp - jne cnv1_main_loop_${ALGO}_sandybridge - - mov rbx, QWORD PTR [rsp+24] - mov rbp, QWORD PTR [rsp+32] - mov rsi, QWORD PTR [rsp+40] - mov rdi, QWORD PTR [rsp+48] - pop r15 - pop r14 diff --git a/src/crypto/asm/win/cnv1_main_loop_soft_aes_sandybridge.inc.in b/src/crypto/asm/win/cnv1_main_loop_soft_aes_sandybridge.inc.in deleted file mode 100644 index 549ea2ea..00000000 --- a/src/crypto/asm/win/cnv1_main_loop_soft_aes_sandybridge.inc.in +++ /dev/null @@ -1,162 +0,0 @@ - push rbx - push rbp - push rsi - push rdi - push r12 - push r13 - push r14 - push r15 - sub rsp, 72 - - movaps XMMWORD PTR [rsp], xmm6 - movaps XMMWORD PTR [rsp+16], xmm7 - movaps XMMWORD PTR [rsp+32], xmm8 - movaps XMMWORD PTR [rsp+48], xmm9 - - mov rax, QWORD PTR [rcx+48] - xor rax, QWORD PTR [rcx+16] - mov rdx, QWORD PTR [rcx+56] - xor rdx, QWORD PTR [rcx+24] - mov r8, QWORD PTR [rcx+32] - xor r8, QWORD PTR [rcx] - movq xmm4, rax - mov rax, QWORD PTR [rcx+256] - mov r13, QWORD PTR [rcx+40] - movq xmm0, rdx - xor r13, QWORD PTR [rcx+8] - mov rdx, r8 - mov rdi, QWORD PTR [rcx+224] - and edx, ${MASK} - mov rax, QWORD PTR [rax+35] - xor rax, QWORD PTR [rcx+192] - movq xmm5, rax - movq xmm8, rdi - punpcklqdq xmm4, xmm0 - mov QWORD PTR [rsp+64], rdx - - movq xmm6, rcx - mov rax, QWORD PTR [rcx+264] - movq xmm7, rax - - mov eax, ${ITERATIONS} - - ALIGN 64 -cnv1_main_loop_${ALGO}_soft_aes_sandybridge: - movq xmm9, rax - mov r12, QWORD PTR [rcx+272] - mov esi, DWORD PTR [rdx+rdi] - mov r10d, DWORD PTR [rdx+rdi+4] - mov ebp, DWORD PTR [rdx+rdi+12] - mov r14d, DWORD PTR [rdx+rdi+8] - mov rdx, QWORD PTR [rsp+64] - movzx ecx, sil - shr esi, 8 - mov r15d, DWORD PTR [r12+rcx*4] - movzx ecx, r10b - shr r10d, 8 - mov edi, DWORD PTR [r12+rcx*4] - movzx ecx, r14b - shr r14d, 8 - mov ebx, DWORD PTR [r12+rcx*4] - movzx ecx, bpl - shr ebp, 8 - mov r9d, DWORD PTR [r12+rcx*4] - movzx ecx, r10b - shr r10d, 8 - xor r15d, DWORD PTR [r12+rcx*4+1024] - movzx ecx, r14b - shr r14d, 8 - mov eax, r14d - shr eax, 8 - xor edi, DWORD PTR [r12+rcx*4+1024] - add eax, 256 - movzx ecx, bpl - shr ebp, 8 - xor ebx, DWORD PTR [r12+rcx*4+1024] - movzx ecx, sil - shr esi, 8 - xor r9d, DWORD PTR [r12+rcx*4+1024] - add r12, 2048 - movzx ecx, r10b - shr r10d, 8 - add r10d, 256 - mov r11d, DWORD PTR [r12+rax*4] - xor r11d, DWORD PTR [r12+rcx*4] - xor r11d, r9d - movzx ecx, sil - mov r10d, DWORD PTR [r12+r10*4] - shr esi, 8 - add esi, 256 - xor r10d, DWORD PTR [r12+rcx*4] - movzx ecx, bpl - xor r10d, ebx - shr ebp, 8 - add ebp, 256 - movd xmm1, r11d - mov r9d, DWORD PTR [r12+rcx*4] - xor r9d, DWORD PTR [r12+rsi*4] - mov eax, DWORD PTR [r12+rbp*4] - xor r9d, edi - movq rdi, xmm8 - movzx ecx, r14b - movd xmm0, r10d - movd xmm2, r9d - punpckldq xmm2, xmm1 - movq xmm1, r8 - xor eax, DWORD PTR [r12+rcx*4] - xor eax, r15d - movd xmm3, eax - movq rax, xmm7 - punpckldq xmm3, xmm0 - movq xmm0, r13 - punpcklqdq xmm1, xmm0 - punpckldq xmm3, xmm2 - pxor xmm3, xmm1 - movq r9, xmm3 - mov r10d, r9d - and r10d, ${MASK} - movdqa xmm0, xmm3 - pxor xmm0, xmm4 - movdqu XMMWORD PTR [rdx+rdi], xmm0 - psrldq xmm0, 11 - movq rcx, xmm0 - movzx ecx, cl - mov cl, BYTE PTR [rcx+rax] - mov BYTE PTR [rdi+rdx+11], cl - mov rbx, QWORD PTR [r10+rdi] - mov rcx, r9 - lea r9, QWORD PTR [r10+rdi] - mov r11, QWORD PTR [r9+8] - mov rax, rbx - movdqa xmm4, xmm3 - mul rcx - movq rcx, xmm6 - add r8, rdx - add r13, rax - movq rax, xmm5 - xor rax, r13 - mov QWORD PTR [r9], r8 - xor r8, rbx - mov QWORD PTR [r9+8], rax - movq rax, xmm9 - mov rdx, r8 - xor r13, r11 - and edx, ${MASK} - mov QWORD PTR [rsp+64], rdx - sub eax, 1 - jne cnv1_main_loop_${ALGO}_soft_aes_sandybridge - - movaps xmm6, XMMWORD PTR [rsp] - movaps xmm7, XMMWORD PTR [rsp+16] - movaps xmm8, XMMWORD PTR [rsp+32] - movaps xmm9, XMMWORD PTR [rsp+48] - - add rsp, 72 - pop r15 - pop r14 - pop r13 - pop r12 - pop rdi - pop rsi - pop rbp - pop rbx diff --git a/src/crypto/asm/win/cnv2_main_loop_soft_aes_sandybridge.inc.in b/src/crypto/asm/win/cnv2_main_loop_soft_aes_sandybridge.inc.in deleted file mode 100644 index 87d8d10e..00000000 --- a/src/crypto/asm/win/cnv2_main_loop_soft_aes_sandybridge.inc.in +++ /dev/null @@ -1,267 +0,0 @@ - mov QWORD PTR [rsp+8], rcx - push rbx - push rbp - push rsi - push rdi - push r12 - push r13 - push r14 - push r15 - sub rsp, 152 - - stmxcsr DWORD PTR [rsp+4] - mov DWORD PTR [rsp], 24448 - ldmxcsr DWORD PTR [rsp] - - mov rax, QWORD PTR [rcx+48] - mov r10, rcx - xor rax, QWORD PTR [rcx+16] - mov r8, QWORD PTR [rcx+32] - xor r8, QWORD PTR [rcx] - mov r9, QWORD PTR [rcx+40] - xor r9, QWORD PTR [rcx+8] - movq xmm4, rax - mov rdx, QWORD PTR [rcx+56] - xor rdx, QWORD PTR [rcx+24] - mov r11, QWORD PTR [rcx+224] - mov rcx, QWORD PTR [rcx+88] - xor rcx, QWORD PTR [r10+72] - mov rax, QWORD PTR [r10+80] - movq xmm0, rdx - xor rax, QWORD PTR [r10+64] - - movaps XMMWORD PTR [rsp+16], xmm6 - movaps XMMWORD PTR [rsp+32], xmm7 - movaps XMMWORD PTR [rsp+48], xmm8 - movaps XMMWORD PTR [rsp+64], xmm9 - movaps XMMWORD PTR [rsp+80], xmm10 - movaps XMMWORD PTR [rsp+96], xmm11 - movaps XMMWORD PTR [rsp+112], xmm12 - movaps XMMWORD PTR [rsp+128], xmm13 - - movq xmm5, rax - - mov ax, 1023 - shl rax, 52 - movq xmm8, rax - - mov rax, r8 - punpcklqdq xmm4, xmm0 - and eax, ${MASK} - movq xmm10, QWORD PTR [r10+96] - movq xmm0, rcx - mov rcx, QWORD PTR [r10+104] - xorps xmm9, xmm9 - mov QWORD PTR [rsp+248], rax - movq xmm12, r11 - mov QWORD PTR [rsp+240], r9 - punpcklqdq xmm5, xmm0 - movq xmm13, rcx - mov r12d, ${ITERATIONS} - - ALIGN 64 -cnv2_main_loop_${ALGO}_soft_aes_sandybridge: - movd xmm11, r12d - mov r12, QWORD PTR [r10+272] - lea r13, QWORD PTR [rax+r11] - mov esi, DWORD PTR [r13] - movq xmm0, r9 - mov r10d, DWORD PTR [r13+4] - movq xmm7, r8 - mov ebp, DWORD PTR [r13+12] - mov r14d, DWORD PTR [r13+8] - mov rdx, QWORD PTR [rsp+248] - movzx ecx, sil - shr esi, 8 - punpcklqdq xmm7, xmm0 - mov r15d, DWORD PTR [r12+rcx*4] - movzx ecx, r10b - shr r10d, 8 - mov edi, DWORD PTR [r12+rcx*4] - movzx ecx, r14b - shr r14d, 8 - mov ebx, DWORD PTR [r12+rcx*4] - movzx ecx, bpl - shr ebp, 8 - mov r9d, DWORD PTR [r12+rcx*4] - movzx ecx, r10b - shr r10d, 8 - xor r15d, DWORD PTR [r12+rcx*4+1024] - movzx ecx, r14b - shr r14d, 8 - mov eax, r14d - shr eax, 8 - xor edi, DWORD PTR [r12+rcx*4+1024] - add eax, 256 - movzx ecx, bpl - shr ebp, 8 - xor ebx, DWORD PTR [r12+rcx*4+1024] - movzx ecx, sil - shr esi, 8 - xor r9d, DWORD PTR [r12+rcx*4+1024] - add r12, 2048 - movzx ecx, r10b - shr r10d, 8 - add r10d, 256 - mov r11d, DWORD PTR [r12+rax*4] - xor r11d, DWORD PTR [r12+rcx*4] - xor r11d, r9d - movzx ecx, sil - mov r10d, DWORD PTR [r12+r10*4] - shr esi, 8 - add esi, 256 - xor r10d, DWORD PTR [r12+rcx*4] - movzx ecx, bpl - xor r10d, ebx - shr ebp, 8 - movd xmm1, r11d - add ebp, 256 - movq r11, xmm12 - mov r9d, DWORD PTR [r12+rcx*4] - xor r9d, DWORD PTR [r12+rsi*4] - mov eax, DWORD PTR [r12+rbp*4] - xor r9d, edi - movzx ecx, r14b - movd xmm0, r10d - movd xmm2, r9d - xor eax, DWORD PTR [r12+rcx*4] - mov rcx, rdx - xor eax, r15d - punpckldq xmm2, xmm1 - xor rcx, 16 - movd xmm6, eax - mov rax, rdx - punpckldq xmm6, xmm0 - xor rax, 32 - punpckldq xmm6, xmm2 - xor rdx, 48 - movdqu xmm2, XMMWORD PTR [rcx+r11] - pxor xmm6, xmm7 - paddq xmm2, xmm4 - movdqu xmm1, XMMWORD PTR [rax+r11] - movdqu xmm0, XMMWORD PTR [rdx+r11] - paddq xmm0, xmm5 - movdqu XMMWORD PTR [rcx+r11], xmm0 - movdqu XMMWORD PTR [rax+r11], xmm2 - movq rcx, xmm13 - paddq xmm1, xmm7 - movdqu XMMWORD PTR [rdx+r11], xmm1 - movq rdi, xmm6 - mov r10, rdi - and r10d, ${MASK} - xor edx, edx - mov rax, rcx - shl rax, 32 - movq rbx, xmm10 - xor rbx, rax - lea r9, QWORD PTR [rcx+rcx] - add r9d, edi - movdqa xmm0, xmm6 - pxor xmm0, xmm4 - mov ecx, -2147483647 - movdqu XMMWORD PTR [r13], xmm0 - or r9, rcx - movdqa xmm0, xmm6 - movaps xmm1, xmm9 - psrldq xmm0, 8 - movq rax, xmm0 - xor rbx, QWORD PTR [r10+r11] - lea r14, QWORD PTR [r10+r11] - mov rbp, QWORD PTR [r14+8] - div r9 - shl rdx, 32 - mov eax, eax - add rdx, rax - lea r9, QWORD PTR [rdx+rdi] - movq xmm10, rdx - mov rax, r9 - shr rax, 12 - movq xmm0, rax - paddq xmm0, xmm8 - sqrtsd xmm1, xmm0 - movq rdx, xmm1 - test rdx, 524287 - je sqrt_fixup_${ALGO}_soft_aes_sandybridge - psrlq xmm1, 19 -sqrt_fixup_${ALGO}_soft_aes_sandybridge_ret: - - mov r9, r10 - movdqa xmm13, xmm1 - xor r9, 16 - mov rcx, r10 - xor rcx, 32 - xor r10, 48 - mov rax, rbx - mul rdi - movdqu xmm2, XMMWORD PTR [r9+r11] - movdqu xmm1, XMMWORD PTR [rcx+r11] - paddq xmm1, xmm7 - movq xmm0, rax - movq xmm3, rdx - xor rax, QWORD PTR [r11+rcx+8] - xor rdx, QWORD PTR [rcx+r11] - punpcklqdq xmm3, xmm0 - add r8, rdx - movdqu xmm0, XMMWORD PTR [r10+r11] - pxor xmm2, xmm3 - paddq xmm0, xmm5 - paddq xmm2, xmm4 - movdqu XMMWORD PTR [r9+r11], xmm0 - movdqa xmm5, xmm4 - mov r9, QWORD PTR [rsp+240] - movdqa xmm4, xmm6 - add r9, rax - movdqu XMMWORD PTR [rcx+r11], xmm2 - movdqu XMMWORD PTR [r10+r11], xmm1 - mov r10, QWORD PTR [rsp+224] - movd r12d, xmm11 - mov QWORD PTR [r14], r8 - xor r8, rbx - mov rax, r8 - mov QWORD PTR [r14+8], r9 - and eax, ${MASK} - xor r9, rbp - mov QWORD PTR [rsp+240], r9 - mov QWORD PTR [rsp+248], rax - sub r12d, 1 - jne cnv2_main_loop_${ALGO}_soft_aes_sandybridge - - ldmxcsr DWORD PTR [rsp+4] - movaps xmm6, XMMWORD PTR [rsp+16] - movaps xmm7, XMMWORD PTR [rsp+32] - movaps xmm8, XMMWORD PTR [rsp+48] - movaps xmm9, XMMWORD PTR [rsp+64] - movaps xmm10, XMMWORD PTR [rsp+80] - movaps xmm11, XMMWORD PTR [rsp+96] - movaps xmm12, XMMWORD PTR [rsp+112] - movaps xmm13, XMMWORD PTR [rsp+128] - - add rsp, 152 - pop r15 - pop r14 - pop r13 - pop r12 - pop rdi - pop rsi - pop rbp - pop rbx - jmp cnv2_main_loop_${ALGO}_soft_aes_sandybridge_endp - -sqrt_fixup_${ALGO}_soft_aes_sandybridge: - dec rdx - mov r15d, -1022 - shl r15, 32 - mov rax, rdx - shr rdx, 19 - shr rax, 20 - mov rcx, rdx - sub rcx, rax - lea rcx, [rcx+r15+1] - add rax, r15 - imul rcx, rax - sub rcx, r9 - adc rdx, 0 - movq xmm1, rdx - jmp sqrt_fixup_${ALGO}_soft_aes_sandybridge_ret - -cnv2_main_loop_${ALGO}_soft_aes_sandybridge_endp: diff --git a/src/crypto/c_keccak.h b/src/crypto/c_keccak.h deleted file mode 100644 index 4f7f8572..00000000 --- a/src/crypto/c_keccak.h +++ /dev/null @@ -1,26 +0,0 @@ -// keccak.h -// 19-Nov-11 Markku-Juhani O. Saarinen - -#ifndef KECCAK_H -#define KECCAK_H - -#include -#include - -#ifndef KECCAK_ROUNDS -#define KECCAK_ROUNDS 24 -#endif - -#ifndef ROTL64 -#define ROTL64(x, y) (((x) << (y)) | ((x) >> (64 - (y)))) -#endif - -// compute a keccak hash (md) of given byte length from "in" -int keccak(const uint8_t *in, int inlen, uint8_t *md, int mdlen); - -// update the state -void keccakf(uint64_t st[25], int norounds); - -void keccak1600(const uint8_t *in, int inlen, uint8_t *md); - -#endif diff --git a/src/crypto/cn/CnAlgo.h b/src/crypto/cn/CnAlgo.h new file mode 100644 index 00000000..3fb9c8bb --- /dev/null +++ b/src/crypto/cn/CnAlgo.h @@ -0,0 +1,258 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2019 XMR-Stak , + * Copyright 2018 Lee Clagett + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_CN_ALGO_H +#define XMRIG_CN_ALGO_H + + +#include +#include + + +#include "crypto/common/Algorithm.h" + + +namespace xmrig +{ + + +template +class CnAlgo +{ +public: + constexpr inline CnAlgo() + { + static_assert(ALGO != Algorithm::INVALID && m_memory[ALGO] > 0, "invalid CRYPTONIGHT algorithm"); + static_assert(sizeof(m_memory) / sizeof(m_memory)[0] == Algorithm::MAX, "memory table size mismatch"); + static_assert(sizeof(m_iterations) / sizeof(m_iterations)[0] == Algorithm::MAX, "iterations table size mismatch"); + static_assert(sizeof(m_base) / sizeof(m_base)[0] == Algorithm::MAX, "iterations table size mismatch"); + } + + constexpr inline Algorithm::Id base() const { return m_base[ALGO]; } + constexpr inline bool isHeavy() const { return memory() == CN_MEMORY * 2; } + constexpr inline bool isR() const { return ALGO == Algorithm::CN_R || ALGO == Algorithm::CN_WOW; } + constexpr inline size_t memory() const { return m_memory[ALGO]; } + constexpr inline uint32_t iterations() const { return m_iterations[ALGO]; } + constexpr inline uint32_t mask() const { return ((memory() - 1) / 16) * 16; } + + inline static size_t memory(Algorithm::Id algo) + { + switch (Algorithm::family(algo)) { + case Algorithm::CN: + return CN_MEMORY; + + case Algorithm::CN_LITE: + return CN_MEMORY / 2; + + case Algorithm::CN_HEAVY: + return CN_MEMORY * 2; + + case Algorithm::CN_PICO: + return CN_MEMORY / 8; + + case Algorithm::CN_EXTREMELITE: + return CN_MEMORY / 16; + + default: + break; + } + + return 0; + } + + inline static uint32_t mask(Algorithm::Id algo) + { +# ifdef XMRIG_ALGO_CN_GPU + if (algo == Algorithm::CN_GPU) { + return 0x1FFFC0; + } +# endif + +# ifdef XMRIG_ALGO_CN_PICO + if (algo == Algorithm::CN_PICO_0) { + return 0x1FFF0; + } +# endif + +# ifdef XMRIG_ALGO_CN_EXTREMELITE + if (algo == Algorithm::CN_EXTREMELITE_0) { + return 0x1FFF0; + } +# endif + + return ((memory(algo) - 1) / 16) * 16; + } + +private: + constexpr const static size_t CN_MEMORY = 0x200000; + constexpr const static uint32_t CN_ITER = 0x80000; + + constexpr const static size_t m_memory[] = { + CN_MEMORY, // CN_0 + CN_MEMORY, // CN_1 + CN_MEMORY, // CN_2 + CN_MEMORY, // CN_R + CN_MEMORY, // CN_WOW + CN_MEMORY, // CN_FAST + CN_MEMORY, // CN_HALF + CN_MEMORY, // CN_XAO + CN_MEMORY, // CN_RTO + CN_MEMORY, // CN_RWZ + CN_MEMORY, // CN_ZLS + CN_MEMORY, // CN_DOUBLE + CN_MEMORY, // CN_CONCEAL +# ifdef XMRIG_ALGO_CN_GPU + CN_MEMORY, // CN_GPU +# endif +# ifdef XMRIG_ALGO_CN_LITE + CN_MEMORY / 2, // CN_LITE_0 + CN_MEMORY / 2, // CN_LITE_1 +# endif +# ifdef XMRIG_ALGO_CN_HEAVY + CN_MEMORY * 2, // CN_HEAVY_0 + CN_MEMORY * 2, // CN_HEAVY_TUBE + CN_MEMORY * 2, // CN_HEAVY_XHV +# endif +# ifdef XMRIG_ALGO_CN_PICO + CN_MEMORY / 8, // CN_PICO_0 +# endif +# ifdef XMRIG_ALGO_CN_EXTREMELITE + CN_MEMORY / 16, // CN_EXTREMELITE_0 +# endif +# ifdef XMRIG_ALGO_RANDOMX + 0, // RX_0 + 0, // RX_WOW + 0, // RX_LOKI +# endif +# ifdef XMRIG_ALGO_ARGON2 + 0, // AR2_CHUKWA + 0, // AR2_WRKZ +# endif + }; + + constexpr const static uint32_t m_iterations[] = { + CN_ITER, // CN_0 + CN_ITER, // CN_1 + CN_ITER, // CN_2 + CN_ITER, // CN_R + CN_ITER, // CN_WOW + CN_ITER / 2, // CN_FAST + CN_ITER / 2, // CN_HALF + CN_ITER * 2, // CN_XAO + CN_ITER, // CN_RTO + 0x60000, // CN_RWZ + 0x60000, // CN_ZLS + CN_ITER * 2, // CN_DOUBLE + CN_ITER / 2, // CN_CONCEAL +# ifdef XMRIG_ALGO_CN_GPU + 0xC000, // CN_GPU +# endif +# ifdef XMRIG_ALGO_CN_LITE + CN_ITER / 2, // CN_LITE_0 + CN_ITER / 2, // CN_LITE_1 +# endif +# ifdef XMRIG_ALGO_CN_HEAVY + CN_ITER / 2, // CN_HEAVY_0 + CN_ITER / 2, // CN_HEAVY_TUBE + CN_ITER / 2, // CN_HEAVY_XHV +# endif +# ifdef XMRIG_ALGO_CN_PICO + CN_ITER / 8, // CN_PICO_0 +# endif +# ifdef XMRIG_ALGO_CN_EXTREMELITE + CN_ITER / 32, // CN_EXTREMELITE +# endif +# ifdef XMRIG_ALGO_RANDOMX + 0, // RX_0 + 0, // RX_WOW + 0, // RX_LOKI +# endif +# ifdef XMRIG_ALGO_ARGON2 + 0, // AR2_CHUKWA + 0, // AR2_WRKZ +# endif + }; + + constexpr const static Algorithm::Id m_base[] = { + Algorithm::CN_0, // CN_0 + Algorithm::CN_1, // CN_1 + Algorithm::CN_2, // CN_2 + Algorithm::CN_2, // CN_R + Algorithm::CN_2, // CN_WOW + Algorithm::CN_1, // CN_FAST + Algorithm::CN_2, // CN_HALF + Algorithm::CN_0, // CN_XAO + Algorithm::CN_1, // CN_RTO + Algorithm::CN_2, // CN_RWZ + Algorithm::CN_2, // CN_ZLS + Algorithm::CN_2, // CN_DOUBLE + Algorithm::CN_0, // CN_CONCEAL +# ifdef XMRIG_ALGO_CN_GPU + Algorithm::CN_GPU, // CN_GPU +# endif +# ifdef XMRIG_ALGO_CN_LITE + Algorithm::CN_0, // CN_LITE_0 + Algorithm::CN_1, // CN_LITE_1 +# endif +# ifdef XMRIG_ALGO_CN_HEAVY + Algorithm::CN_0, // CN_HEAVY_0 + Algorithm::CN_1, // CN_HEAVY_TUBE + Algorithm::CN_0, // CN_HEAVY_XHV +# endif +# ifdef XMRIG_ALGO_CN_PICO + Algorithm::CN_2, // CN_PICO_0, +# endif +# ifdef XMRIG_ALGO_CN_EXTREMELITE + Algorithm::CN_2, // CN_EXTREMELITE_0, +# endif +# ifdef XMRIG_ALGO_RANDOMX + Algorithm::INVALID, // RX_0 + Algorithm::INVALID, // RX_WOW + Algorithm::INVALID, // RX_LOKI +# endif +# ifdef XMRIG_ALGO_ARGON2 + Algorithm::INVALID, // AR2_CHUKWA + Algorithm::INVALID, // AR2_WRKZ +# endif + }; +}; + + +#ifdef XMRIG_ALGO_CN_GPU +template<> constexpr inline uint32_t CnAlgo::mask() const { return 0x1FFFC0; } +#endif + +#ifdef XMRIG_ALGO_CN_PICO +template<> constexpr inline uint32_t CnAlgo::mask() const { return 0x1FFF0; } +#endif + +#ifdef XMRIG_ALGO_CN_EXTREMELITE +template<> constexpr inline uint32_t CnAlgo::mask() const { return 0x1FFF0; } +#endif + +} /* namespace xmrig */ + + +#endif /* XMRIG_CN_ALGO_H */ diff --git a/src/crypto/cn/CnCtx.cpp b/src/crypto/cn/CnCtx.cpp new file mode 100644 index 00000000..5d41bca0 --- /dev/null +++ b/src/crypto/cn/CnCtx.cpp @@ -0,0 +1,60 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2019 XMR-Stak , + * Copyright 2018 Lee Clagett + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#include + + +#include "crypto/cn/CnCtx.h" +#include "crypto/cn/CryptoNight.h" +#include "crypto/common/Algorithm.h" +#include "crypto/common/portable/mm_malloc.h" +#include "crypto/common/VirtualMemory.h" + + +void xmrig::CnCtx::create(cryptonight_ctx **ctx, uint8_t *memory, size_t size, size_t count) +{ + for (size_t i = 0; i < count; ++i) { + cryptonight_ctx *c = static_cast(_mm_malloc(sizeof(cryptonight_ctx), 4096)); + c->memory = memory + (i * size); + + c->generated_code = reinterpret_cast(VirtualMemory::allocateExecutableMemory(0x4000)); + c->generated_code_data.algo = Algorithm::INVALID; + c->generated_code_data.height = std::numeric_limits::max(); + + ctx[i] = c; + } +} + + +void xmrig::CnCtx::release(cryptonight_ctx **ctx, size_t count) +{ + if (ctx[0] == nullptr) { + return; + } + + for (size_t i = 0; i < count; ++i) { + _mm_free(ctx[i]); + } +} diff --git a/src/crypto/cn/CnCtx.h b/src/crypto/cn/CnCtx.h new file mode 100644 index 00000000..7b0adbec --- /dev/null +++ b/src/crypto/cn/CnCtx.h @@ -0,0 +1,52 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2019 XMR-Stak , + * Copyright 2018 Lee Clagett + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_CN_CTX_H +#define XMRIG_CN_CTX_H + + +#include +#include + + +struct cryptonight_ctx; + + +namespace xmrig +{ + + +class CnCtx +{ +public: + static void create(cryptonight_ctx **ctx, uint8_t *memory, size_t size, size_t count); + static void release(cryptonight_ctx **ctx, size_t count); +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_CN_CTX_H */ diff --git a/src/crypto/cn/CnHash.cpp b/src/crypto/cn/CnHash.cpp new file mode 100644 index 00000000..a99aeb3f --- /dev/null +++ b/src/crypto/cn/CnHash.cpp @@ -0,0 +1,312 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2019 XMR-Stak , + * Copyright 2018 Lee Clagett + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#include + + +#include "backend/cpu/Cpu.h" +#include "crypto/cn/CnHash.h" +#include "crypto/common/VirtualMemory.h" + + +#if defined(XMRIG_ARM) +# include "crypto/cn/CryptoNight_arm.h" +#else +# include "crypto/cn/CryptoNight_x86.h" +#endif + + +#ifdef XMRIG_ALGO_ARGON2 +# include "crypto/argon2/Hash.h" +#endif + + +#define ADD_FN(algo) \ + m_map[algo][AV_SINGLE][Assembly::NONE] = cryptonight_single_hash; \ + m_map[algo][AV_SINGLE_SOFT][Assembly::NONE] = cryptonight_single_hash; \ + m_map[algo][AV_DOUBLE][Assembly::NONE] = cryptonight_double_hash; \ + m_map[algo][AV_DOUBLE_SOFT][Assembly::NONE] = cryptonight_double_hash; \ + m_map[algo][AV_TRIPLE][Assembly::NONE] = cryptonight_triple_hash; \ + m_map[algo][AV_TRIPLE_SOFT][Assembly::NONE] = cryptonight_triple_hash; \ + m_map[algo][AV_QUAD][Assembly::NONE] = cryptonight_quad_hash; \ + m_map[algo][AV_QUAD_SOFT][Assembly::NONE] = cryptonight_quad_hash; \ + m_map[algo][AV_PENTA][Assembly::NONE] = cryptonight_penta_hash; \ + m_map[algo][AV_PENTA_SOFT][Assembly::NONE] = cryptonight_penta_hash; + + +#ifdef XMRIG_FEATURE_ASM +# define ADD_FN_ASM(algo) \ + m_map[algo][AV_SINGLE][Assembly::INTEL] = cryptonight_single_hash_asm; \ + m_map[algo][AV_SINGLE][Assembly::RYZEN] = cryptonight_single_hash_asm; \ + m_map[algo][AV_SINGLE][Assembly::BULLDOZER] = cryptonight_single_hash_asm; \ + m_map[algo][AV_DOUBLE][Assembly::INTEL] = cryptonight_double_hash_asm; \ + m_map[algo][AV_DOUBLE][Assembly::RYZEN] = cryptonight_double_hash_asm; \ + m_map[algo][AV_DOUBLE][Assembly::BULLDOZER] = cryptonight_double_hash_asm; + + +extern "C" void cnv2_mainloop_ivybridge_asm(cryptonight_ctx **ctx); +extern "C" void cnv2_mainloop_ryzen_asm(cryptonight_ctx **ctx); +extern "C" void cnv2_mainloop_bulldozer_asm(cryptonight_ctx **ctx); +extern "C" void cnv2_double_mainloop_sandybridge_asm(cryptonight_ctx **ctx); +extern "C" void cnv2_rwz_mainloop_asm(cryptonight_ctx **ctx); +extern "C" void cnv2_rwz_double_mainloop_asm(cryptonight_ctx **ctx); + +namespace xmrig { + + +cn_mainloop_fun cn_half_mainloop_ivybridge_asm = nullptr; +cn_mainloop_fun cn_half_mainloop_ryzen_asm = nullptr; +cn_mainloop_fun cn_half_mainloop_bulldozer_asm = nullptr; +cn_mainloop_fun cn_half_double_mainloop_sandybridge_asm = nullptr; + +cn_mainloop_fun cn_trtl_mainloop_ivybridge_asm = nullptr; +cn_mainloop_fun cn_trtl_mainloop_ryzen_asm = nullptr; +cn_mainloop_fun cn_trtl_mainloop_bulldozer_asm = nullptr; +cn_mainloop_fun cn_trtl_double_mainloop_sandybridge_asm = nullptr; + +cn_mainloop_fun cn_zls_mainloop_ivybridge_asm = nullptr; +cn_mainloop_fun cn_zls_mainloop_ryzen_asm = nullptr; +cn_mainloop_fun cn_zls_mainloop_bulldozer_asm = nullptr; +cn_mainloop_fun cn_zls_double_mainloop_sandybridge_asm = nullptr; + +cn_mainloop_fun cn_double_mainloop_ivybridge_asm = nullptr; +cn_mainloop_fun cn_double_mainloop_ryzen_asm = nullptr; +cn_mainloop_fun cn_double_mainloop_bulldozer_asm = nullptr; +cn_mainloop_fun cn_double_double_mainloop_sandybridge_asm = nullptr; + +cn_mainloop_fun cnv2_upx2_mainloop_asm = nullptr; +cn_mainloop_fun cnv2_upx2_double_mainloop_asm = nullptr; + +template +static void patchCode(T dst, U src, const uint32_t iterations, const uint32_t mask = CnAlgo().mask()) +{ + const uint8_t* p = reinterpret_cast(src); + + // Workaround for Visual Studio placing trampoline in debug builds. +# if defined(_MSC_VER) + if (p[0] == 0xE9) { + p += *(int32_t*)(p + 1) + 5; + } +# endif + + size_t size = 0; + while (*(uint32_t*)(p + size) != 0xDEADC0DE) { + ++size; + } + + size += sizeof(uint32_t); + + memcpy((void*) dst, (const void*) src, size); + + uint8_t* patched_data = reinterpret_cast(dst); + for (size_t i = 0; i + sizeof(uint32_t) <= size; ++i) { + switch (*(uint32_t*)(patched_data + i)) { + case CnAlgo().iterations(): + *(uint32_t*)(patched_data + i) = iterations; + break; + + case CnAlgo().iterations(): + *(uint32_t*)(patched_data + i) = iterations; + break; + + case CnAlgo().mask(): + *(uint32_t*)(patched_data + i) = mask; + break; + } + } +} + + +static void patchAsmVariants() +{ + const int allocation_size = 131070; + uint8_t *base = static_cast(VirtualMemory::allocateExecutableMemory(allocation_size)); + + cn_half_mainloop_ivybridge_asm = reinterpret_cast (base + 0x0000); + cn_half_mainloop_ryzen_asm = reinterpret_cast (base + 0x1000); + cn_half_mainloop_bulldozer_asm = reinterpret_cast (base + 0x2000); + cn_half_double_mainloop_sandybridge_asm = reinterpret_cast (base + 0x3000); + +# ifdef XMRIG_ALGO_CN_PICO + cn_trtl_mainloop_ivybridge_asm = reinterpret_cast (base + 0x4000); + cn_trtl_mainloop_ryzen_asm = reinterpret_cast (base + 0x5000); + cn_trtl_mainloop_bulldozer_asm = reinterpret_cast (base + 0x6000); + cn_trtl_double_mainloop_sandybridge_asm = reinterpret_cast (base + 0x7000); +# endif + + cn_zls_mainloop_ivybridge_asm = reinterpret_cast (base + 0x8000); + cn_zls_mainloop_ryzen_asm = reinterpret_cast (base + 0x9000); + cn_zls_mainloop_bulldozer_asm = reinterpret_cast (base + 0xA000); + cn_zls_double_mainloop_sandybridge_asm = reinterpret_cast (base + 0xB000); + + cn_double_mainloop_ivybridge_asm = reinterpret_cast (base + 0xC000); + cn_double_mainloop_ryzen_asm = reinterpret_cast (base + 0xD000); + cn_double_mainloop_bulldozer_asm = reinterpret_cast (base + 0xE000); + cn_double_double_mainloop_sandybridge_asm = reinterpret_cast (base + 0xF000); + +# ifdef XMRIG_ALGO_CN_EXTREMELITE + cnv2_upx2_mainloop_asm = reinterpret_cast (base + 0x10000); + cnv2_upx2_double_mainloop_asm = reinterpret_cast (base + 0x11000); +# endif + + { + constexpr uint32_t ITER = CnAlgo().iterations(); + + patchCode(cn_half_mainloop_ivybridge_asm, cnv2_mainloop_ivybridge_asm, ITER); + patchCode(cn_half_mainloop_ryzen_asm, cnv2_mainloop_ryzen_asm, ITER); + patchCode(cn_half_mainloop_bulldozer_asm, cnv2_mainloop_bulldozer_asm, ITER); + patchCode(cn_half_double_mainloop_sandybridge_asm, cnv2_double_mainloop_sandybridge_asm, ITER); + } + +# ifdef XMRIG_ALGO_CN_PICO + { + constexpr uint32_t ITER = CnAlgo().iterations(); + constexpr uint32_t MASK = CnAlgo().mask(); + + patchCode(cn_trtl_mainloop_ivybridge_asm, cnv2_mainloop_ivybridge_asm, ITER, MASK); + patchCode(cn_trtl_mainloop_ryzen_asm, cnv2_mainloop_ryzen_asm, ITER, MASK); + patchCode(cn_trtl_mainloop_bulldozer_asm, cnv2_mainloop_bulldozer_asm, ITER, MASK); + patchCode(cn_trtl_double_mainloop_sandybridge_asm, cnv2_double_mainloop_sandybridge_asm, ITER, MASK); + } +# endif + + { + constexpr uint32_t ITER = CnAlgo().iterations(); + + patchCode(cn_zls_mainloop_ivybridge_asm, cnv2_mainloop_ivybridge_asm, ITER); + patchCode(cn_zls_mainloop_ryzen_asm, cnv2_mainloop_ryzen_asm, ITER); + patchCode(cn_zls_mainloop_bulldozer_asm, cnv2_mainloop_bulldozer_asm, ITER); + patchCode(cn_zls_double_mainloop_sandybridge_asm, cnv2_double_mainloop_sandybridge_asm, ITER); + } + + { + constexpr uint32_t ITER = CnAlgo().iterations(); + + patchCode(cn_double_mainloop_ivybridge_asm, cnv2_mainloop_ivybridge_asm, ITER); + patchCode(cn_double_mainloop_ryzen_asm, cnv2_mainloop_ryzen_asm, ITER); + patchCode(cn_double_mainloop_bulldozer_asm, cnv2_mainloop_bulldozer_asm, ITER); + patchCode(cn_double_double_mainloop_sandybridge_asm, cnv2_double_mainloop_sandybridge_asm, ITER); + } + +# ifdef XMRIG_ALGO_CN_EXTREMELITE + { + constexpr uint32_t ITER = CnAlgo().iterations(); + constexpr uint32_t MASK = CnAlgo().mask(); + + patchCode(cnv2_upx2_mainloop_asm, cnv2_rwz_mainloop_asm, ITER, MASK); + patchCode(cnv2_upx2_double_mainloop_asm, cnv2_rwz_double_mainloop_asm, ITER, MASK); + } +# endif + + VirtualMemory::protectExecutableMemory(base, allocation_size); + VirtualMemory::flushInstructionCache(base, allocation_size); +} +} // namespace xmrig +#else +# define ADD_FN_ASM(algo) +#endif + + +static const xmrig::CnHash cnHash; + + +xmrig::CnHash::CnHash() +{ + ADD_FN(Algorithm::CN_0); + ADD_FN(Algorithm::CN_1); + ADD_FN(Algorithm::CN_2); + ADD_FN(Algorithm::CN_R); + ADD_FN(Algorithm::CN_WOW); + ADD_FN(Algorithm::CN_FAST); + ADD_FN(Algorithm::CN_HALF); + ADD_FN(Algorithm::CN_XAO); + ADD_FN(Algorithm::CN_RTO); + ADD_FN(Algorithm::CN_RWZ); + ADD_FN(Algorithm::CN_ZLS); + ADD_FN(Algorithm::CN_DOUBLE); + ADD_FN(Algorithm::CN_CONCEAL); + + ADD_FN_ASM(Algorithm::CN_2); + ADD_FN_ASM(Algorithm::CN_HALF); + ADD_FN_ASM(Algorithm::CN_R); + ADD_FN_ASM(Algorithm::CN_WOW); + ADD_FN_ASM(Algorithm::CN_RWZ); + ADD_FN_ASM(Algorithm::CN_ZLS); + ADD_FN_ASM(Algorithm::CN_DOUBLE); + +# ifdef XMRIG_ALGO_CN_GPU + m_map[Algorithm::CN_GPU][AV_SINGLE][Assembly::NONE] = cryptonight_single_hash_gpu; + m_map[Algorithm::CN_GPU][AV_SINGLE_SOFT][Assembly::NONE] = cryptonight_single_hash_gpu; +# endif + +# ifdef XMRIG_ALGO_CN_LITE + ADD_FN(Algorithm::CN_LITE_0); + ADD_FN(Algorithm::CN_LITE_1); +# endif + +# ifdef XMRIG_ALGO_CN_HEAVY + ADD_FN(Algorithm::CN_HEAVY_0); + ADD_FN(Algorithm::CN_HEAVY_TUBE); + ADD_FN(Algorithm::CN_HEAVY_XHV); +# endif + +# ifdef XMRIG_ALGO_CN_PICO + ADD_FN(Algorithm::CN_PICO_0); + ADD_FN_ASM(Algorithm::CN_PICO_0); +# endif + +# ifdef XMRIG_ALGO_CN_EXTREMELITE + ADD_FN(Algorithm::CN_EXTREMELITE_0); + ADD_FN_ASM(Algorithm::CN_EXTREMELITE_0); +# endif + +# ifdef XMRIG_ALGO_ARGON2 + m_map[Algorithm::AR2_CHUKWA][AV_SINGLE][Assembly::NONE] = argon2::single_hash; + m_map[Algorithm::AR2_CHUKWA][AV_SINGLE_SOFT][Assembly::NONE] = argon2::single_hash; + m_map[Algorithm::AR2_WRKZ][AV_SINGLE][Assembly::NONE] = argon2::single_hash; + m_map[Algorithm::AR2_WRKZ][AV_SINGLE_SOFT][Assembly::NONE] = argon2::single_hash; +# endif + +# ifdef XMRIG_FEATURE_ASM + patchAsmVariants(); +# endif +} + + +xmrig::cn_hash_fun xmrig::CnHash::fn(const Algorithm &algorithm, AlgoVariant av, Assembly::Id assembly) +{ + if (!algorithm.isValid()) { + return nullptr; + } + +# ifdef XMRIG_FEATURE_ASM + cn_hash_fun fun = cnHash.m_map[algorithm][av][Cpu::assembly(assembly)]; + if (fun) { + return fun; + } +# endif + + return cnHash.m_map[algorithm][av][Assembly::NONE]; +} diff --git a/src/crypto/cn/CnHash.h b/src/crypto/cn/CnHash.h new file mode 100644 index 00000000..e4a7ebd2 --- /dev/null +++ b/src/crypto/cn/CnHash.h @@ -0,0 +1,78 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2019 XMR-Stak , + * Copyright 2018 Lee Clagett + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_CN_HASH_H +#define XMRIG_CN_HASH_H + + +#include +#include + + +#include "crypto/cn/CnAlgo.h" +#include "crypto/common/Assembly.h" + + +struct cryptonight_ctx; + + +namespace xmrig +{ + +typedef void (*cn_hash_fun)(const uint8_t *input, size_t size, uint8_t *output, cryptonight_ctx **ctx, uint64_t height); +typedef void (*cn_mainloop_fun)(cryptonight_ctx **ctx); + + +class CnHash +{ +public: + enum AlgoVariant { + AV_AUTO, // --av=0 Automatic mode. + AV_SINGLE, // --av=1 Single hash mode + AV_DOUBLE, // --av=2 Double hash mode + AV_SINGLE_SOFT, // --av=3 Single hash mode (Software AES) + AV_DOUBLE_SOFT, // --av=4 Double hash mode (Software AES) + AV_TRIPLE, // --av=5 Triple hash mode + AV_QUAD, // --av=6 Quard hash mode + AV_PENTA, // --av=7 Penta hash mode + AV_TRIPLE_SOFT, // --av=8 Triple hash mode (Software AES) + AV_QUAD_SOFT, // --av=9 Quard hash mode (Software AES) + AV_PENTA_SOFT, // --av=10 Penta hash mode (Software AES) + AV_MAX + }; + + CnHash(); + + static cn_hash_fun fn(const Algorithm &algorithm, AlgoVariant av, Assembly::Id assembly); + +private: + cn_hash_fun m_map[Algorithm::MAX][AV_MAX][Assembly::MAX] = {}; +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_CN_HASH_H */ diff --git a/src/crypto/cn/CryptoNight.h b/src/crypto/cn/CryptoNight.h new file mode 100644 index 00000000..434c34f8 --- /dev/null +++ b/src/crypto/cn/CryptoNight.h @@ -0,0 +1,64 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018 Lee Clagett + * Copyright 2018-2019 SChernykh + * Copyright 2016-2018 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_CRYPTONIGHT_H +#define XMRIG_CRYPTONIGHT_H + + +#include +#include + +#if defined _MSC_VER || defined XMRIG_ARM +# define ABI_ATTRIBUTE +#else +# define ABI_ATTRIBUTE __attribute__((ms_abi)) +#endif + + +struct cryptonight_ctx; +typedef void(*cn_mainloop_fun_ms_abi)(cryptonight_ctx**) ABI_ATTRIBUTE; + + +struct cryptonight_r_data { + int algo; + uint64_t height; + + bool match(const int a, const uint64_t h) const { return (a == algo) && (h == height); } +}; + + +struct cryptonight_ctx { + alignas(16) uint8_t state[224]; + alignas(16) uint8_t *memory; + + uint8_t unused[40]; + const uint32_t *saes_table; + + cn_mainloop_fun_ms_abi generated_code; + cryptonight_r_data generated_code_data; +}; + + +#endif /* XMRIG_CRYPTONIGHT_H */ diff --git a/src/crypto/cn/CryptoNight_arm.h b/src/crypto/cn/CryptoNight_arm.h new file mode 100644 index 00000000..c4717b02 --- /dev/null +++ b/src/crypto/cn/CryptoNight_arm.h @@ -0,0 +1,980 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2016 Imran Yusuff + * Copyright 2017-2019 XMR-Stak , + * Copyright 2018 Lee Clagett + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_CRYPTONIGHT_ARM_H +#define XMRIG_CRYPTONIGHT_ARM_H + + +#include "crypto/cn/CnAlgo.h" +#include "crypto/cn/CryptoNight_monero.h" +#include "crypto/cn/CryptoNight.h" +#include "crypto/cn/soft_aes.h" +#include "crypto/common/keccak.h" +#include "crypto/common/portable/mm_malloc.h" + + +extern "C" +{ +#include "crypto/cn/c_groestl.h" +#include "crypto/cn/c_blake256.h" +#include "crypto/cn/c_jh.h" +#include "crypto/cn/c_skein.h" +} + + +static inline void do_blake_hash(const uint8_t *input, size_t len, uint8_t *output) { + blake256_hash(output, input, len); +} + + +static inline void do_groestl_hash(const uint8_t *input, size_t len, uint8_t *output) { + groestl(input, len * 8, output); +} + + +static inline void do_jh_hash(const uint8_t *input, size_t len, uint8_t *output) { + jh_hash(32 * 8, input, 8 * len, output); +} + + +static inline void do_skein_hash(const uint8_t *input, size_t len, uint8_t *output) { + xmr_skein(input, output); +} + + +void (* const extra_hashes[4])(const uint8_t *, size_t, uint8_t *) = {do_blake_hash, do_groestl_hash, do_jh_hash, do_skein_hash}; + + +static inline __attribute__((always_inline)) __m128i _mm_set_epi64x(const uint64_t a, const uint64_t b) +{ + return vcombine_u64(vcreate_u64(b), vcreate_u64(a)); +} + + +#if __ARM_FEATURE_CRYPTO +static inline __attribute__((always_inline)) __m128i _mm_aesenc_si128(__m128i v, __m128i rkey) +{ + alignas(16) const __m128i zero = { 0 }; + return veorq_u8(vaesmcq_u8(vaeseq_u8(v, zero)), rkey ); +} +#else +static inline __attribute__((always_inline)) __m128i _mm_aesenc_si128(__m128i v, __m128i rkey) +{ + alignas(16) const __m128i zero = { 0 }; + return zero; +} +#endif + + +/* this one was not implemented yet so here it is */ +static inline __attribute__((always_inline)) uint64_t _mm_cvtsi128_si64(__m128i a) +{ + return vgetq_lane_u64(a, 0); +} + + +#if defined (__arm64__) || defined (__aarch64__) +static inline uint64_t __umul128(uint64_t a, uint64_t b, uint64_t* hi) +{ + unsigned __int128 r = (unsigned __int128) a * (unsigned __int128) b; + *hi = r >> 64; + return (uint64_t) r; +} +#else +static inline uint64_t __umul128(uint64_t multiplier, uint64_t multiplicand, uint64_t *product_hi) { + // multiplier = ab = a * 2^32 + b + // multiplicand = cd = c * 2^32 + d + // ab * cd = a * c * 2^64 + (a * d + b * c) * 2^32 + b * d + uint64_t a = multiplier >> 32; + uint64_t b = multiplier & 0xFFFFFFFF; + uint64_t c = multiplicand >> 32; + uint64_t d = multiplicand & 0xFFFFFFFF; + + //uint64_t ac = a * c; + uint64_t ad = a * d; + //uint64_t bc = b * c; + uint64_t bd = b * d; + + uint64_t adbc = ad + (b * c); + uint64_t adbc_carry = adbc < ad ? 1 : 0; + + // multiplier * multiplicand = product_hi * 2^64 + product_lo + uint64_t product_lo = bd + (adbc << 32); + uint64_t product_lo_carry = product_lo < bd ? 1 : 0; + *product_hi = (a * c) + (adbc >> 32) + (adbc_carry << 32) + product_lo_carry; + + return product_lo; +} +#endif + + +// This will shift and xor tmp1 into itself as 4 32-bit vals such as +// sl_xor(a1 a2 a3 a4) = a1 (a2^a1) (a3^a2^a1) (a4^a3^a2^a1) +static inline __m128i sl_xor(__m128i tmp1) +{ + __m128i tmp4; + tmp4 = _mm_slli_si128(tmp1, 0x04); + tmp1 = _mm_xor_si128(tmp1, tmp4); + tmp4 = _mm_slli_si128(tmp4, 0x04); + tmp1 = _mm_xor_si128(tmp1, tmp4); + tmp4 = _mm_slli_si128(tmp4, 0x04); + tmp1 = _mm_xor_si128(tmp1, tmp4); + return tmp1; +} + + +template +static inline void soft_aes_genkey_sub(__m128i* xout0, __m128i* xout2) +{ + __m128i xout1 = soft_aeskeygenassist(*xout2); + xout1 = _mm_shuffle_epi32(xout1, 0xFF); // see PSHUFD, set all elems to 4th elem + *xout0 = sl_xor(*xout0); + *xout0 = _mm_xor_si128(*xout0, xout1); + xout1 = soft_aeskeygenassist<0x00>(*xout0); + xout1 = _mm_shuffle_epi32(xout1, 0xAA); // see PSHUFD, set all elems to 3rd elem + *xout2 = sl_xor(*xout2); + *xout2 = _mm_xor_si128(*xout2, xout1); +} + + +template +static inline void aes_genkey(const __m128i* memory, __m128i* k0, __m128i* k1, __m128i* k2, __m128i* k3, __m128i* k4, __m128i* k5, __m128i* k6, __m128i* k7, __m128i* k8, __m128i* k9) +{ + __m128i xout0 = _mm_load_si128(memory); + __m128i xout2 = _mm_load_si128(memory + 1); + *k0 = xout0; + *k1 = xout2; + + soft_aes_genkey_sub<0x01>(&xout0, &xout2); + *k2 = xout0; + *k3 = xout2; + + soft_aes_genkey_sub<0x02>(&xout0, &xout2); + *k4 = xout0; + *k5 = xout2; + + soft_aes_genkey_sub<0x04>(&xout0, &xout2); + *k6 = xout0; + *k7 = xout2; + + soft_aes_genkey_sub<0x08>(&xout0, &xout2); + *k8 = xout0; + *k9 = xout2; +} + + +template +static inline void aes_round(__m128i key, __m128i* x0, __m128i* x1, __m128i* x2, __m128i* x3, __m128i* x4, __m128i* x5, __m128i* x6, __m128i* x7) +{ + if (SOFT_AES) { + *x0 = soft_aesenc((uint32_t*)x0, key); + *x1 = soft_aesenc((uint32_t*)x1, key); + *x2 = soft_aesenc((uint32_t*)x2, key); + *x3 = soft_aesenc((uint32_t*)x3, key); + *x4 = soft_aesenc((uint32_t*)x4, key); + *x5 = soft_aesenc((uint32_t*)x5, key); + *x6 = soft_aesenc((uint32_t*)x6, key); + *x7 = soft_aesenc((uint32_t*)x7, key); + } +# ifndef XMRIG_ARMv7 + else { + *x0 = vaesmcq_u8(vaeseq_u8(*((uint8x16_t *) x0), key)); + *x1 = vaesmcq_u8(vaeseq_u8(*((uint8x16_t *) x1), key)); + *x2 = vaesmcq_u8(vaeseq_u8(*((uint8x16_t *) x2), key)); + *x3 = vaesmcq_u8(vaeseq_u8(*((uint8x16_t *) x3), key)); + *x4 = vaesmcq_u8(vaeseq_u8(*((uint8x16_t *) x4), key)); + *x5 = vaesmcq_u8(vaeseq_u8(*((uint8x16_t *) x5), key)); + *x6 = vaesmcq_u8(vaeseq_u8(*((uint8x16_t *) x6), key)); + *x7 = vaesmcq_u8(vaeseq_u8(*((uint8x16_t *) x7), key)); + } +# else + else { + *x0 = _mm_aesenc_si128(*x0, key); + *x1 = _mm_aesenc_si128(*x1, key); + *x2 = _mm_aesenc_si128(*x2, key); + *x3 = _mm_aesenc_si128(*x3, key); + *x4 = _mm_aesenc_si128(*x4, key); + *x5 = _mm_aesenc_si128(*x5, key); + *x6 = _mm_aesenc_si128(*x6, key); + *x7 = _mm_aesenc_si128(*x7, key); + } +# endif +} + + +inline void mix_and_propagate(__m128i& x0, __m128i& x1, __m128i& x2, __m128i& x3, __m128i& x4, __m128i& x5, __m128i& x6, __m128i& x7) +{ + __m128i tmp0 = x0; + x0 = _mm_xor_si128(x0, x1); + x1 = _mm_xor_si128(x1, x2); + x2 = _mm_xor_si128(x2, x3); + x3 = _mm_xor_si128(x3, x4); + x4 = _mm_xor_si128(x4, x5); + x5 = _mm_xor_si128(x5, x6); + x6 = _mm_xor_si128(x6, x7); + x7 = _mm_xor_si128(x7, tmp0); +} + + +namespace xmrig { + + +template +static inline void cn_explode_scratchpad(const __m128i *input, __m128i *output) +{ + constexpr CnAlgo props; + + __m128i xin0, xin1, xin2, xin3, xin4, xin5, xin6, xin7; + __m128i k0, k1, k2, k3, k4, k5, k6, k7, k8, k9; + + aes_genkey(input, &k0, &k1, &k2, &k3, &k4, &k5, &k6, &k7, &k8, &k9); + + xin0 = _mm_load_si128(input + 4); + xin1 = _mm_load_si128(input + 5); + xin2 = _mm_load_si128(input + 6); + xin3 = _mm_load_si128(input + 7); + xin4 = _mm_load_si128(input + 8); + xin5 = _mm_load_si128(input + 9); + xin6 = _mm_load_si128(input + 10); + xin7 = _mm_load_si128(input + 11); + + if (props.isHeavy()) { + for (size_t i = 0; i < 16; i++) { + if (!SOFT_AES) { + aes_round(_mm_setzero_si128(), &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + } + + aes_round(k0, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + aes_round(k1, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + aes_round(k2, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + aes_round(k3, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + aes_round(k4, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + aes_round(k5, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + aes_round(k6, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + aes_round(k7, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + aes_round(k8, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + + if (!SOFT_AES) { + xin0 ^= k9; + xin1 ^= k9; + xin2 ^= k9; + xin3 ^= k9; + xin4 ^= k9; + xin5 ^= k9; + xin6 ^= k9; + xin7 ^= k9; + } else { + aes_round(k9, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + } + + mix_and_propagate(xin0, xin1, xin2, xin3, xin4, xin5, xin6, xin7); + } + } + + for (size_t i = 0; i < props.memory() / sizeof(__m128i); i += 8) { + if (!SOFT_AES) { + aes_round(_mm_setzero_si128(), &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + } + + aes_round(k0, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + aes_round(k1, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + aes_round(k2, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + aes_round(k3, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + aes_round(k4, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + aes_round(k5, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + aes_round(k6, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + aes_round(k7, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + aes_round(k8, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + + if (!SOFT_AES) { + xin0 ^= k9; + xin1 ^= k9; + xin2 ^= k9; + xin3 ^= k9; + xin4 ^= k9; + xin5 ^= k9; + xin6 ^= k9; + xin7 ^= k9; + } else { + aes_round(k9, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + } + + _mm_store_si128(output + i + 0, xin0); + _mm_store_si128(output + i + 1, xin1); + _mm_store_si128(output + i + 2, xin2); + _mm_store_si128(output + i + 3, xin3); + _mm_store_si128(output + i + 4, xin4); + _mm_store_si128(output + i + 5, xin5); + _mm_store_si128(output + i + 6, xin6); + _mm_store_si128(output + i + 7, xin7); + } +} + + +template +static inline void cn_implode_scratchpad(const __m128i *input, __m128i *output) +{ + constexpr CnAlgo props; + +# ifdef XMRIG_ALGO_CN_GPU + constexpr bool IS_HEAVY = props.isHeavy() || ALGO == Algorithm::CN_GPU; +# else + constexpr bool IS_HEAVY = props.isHeavy(); +# endif + + __m128i xout0, xout1, xout2, xout3, xout4, xout5, xout6, xout7; + __m128i k0, k1, k2, k3, k4, k5, k6, k7, k8, k9; + + aes_genkey(output + 2, &k0, &k1, &k2, &k3, &k4, &k5, &k6, &k7, &k8, &k9); + + xout0 = _mm_load_si128(output + 4); + xout1 = _mm_load_si128(output + 5); + xout2 = _mm_load_si128(output + 6); + xout3 = _mm_load_si128(output + 7); + xout4 = _mm_load_si128(output + 8); + xout5 = _mm_load_si128(output + 9); + xout6 = _mm_load_si128(output + 10); + xout7 = _mm_load_si128(output + 11); + + for (size_t i = 0; i < props.memory() / sizeof(__m128i); i += 8) { + xout0 = _mm_xor_si128(_mm_load_si128(input + i + 0), xout0); + xout1 = _mm_xor_si128(_mm_load_si128(input + i + 1), xout1); + xout2 = _mm_xor_si128(_mm_load_si128(input + i + 2), xout2); + xout3 = _mm_xor_si128(_mm_load_si128(input + i + 3), xout3); + xout4 = _mm_xor_si128(_mm_load_si128(input + i + 4), xout4); + xout5 = _mm_xor_si128(_mm_load_si128(input + i + 5), xout5); + xout6 = _mm_xor_si128(_mm_load_si128(input + i + 6), xout6); + xout7 = _mm_xor_si128(_mm_load_si128(input + i + 7), xout7); + + if (SOFT_AES) { + aes_round(_mm_setzero_si128(), &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + } + + aes_round(k0, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k1, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k2, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k3, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k4, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k5, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k6, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k7, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k8, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + + if (SOFT_AES) { + xout0 ^= k9; + xout1 ^= k9; + xout2 ^= k9; + xout3 ^= k9; + xout4 ^= k9; + xout5 ^= k9; + xout6 ^= k9; + xout7 ^= k9; + } else { + aes_round(k9, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + } + + if (IS_HEAVY) { + mix_and_propagate(xout0, xout1, xout2, xout3, xout4, xout5, xout6, xout7); + } + } + + if (IS_HEAVY) { + for (size_t i = 0; i < props.memory() / sizeof(__m128i); i += 8) { + xout0 = _mm_xor_si128(_mm_load_si128(input + i + 0), xout0); + xout1 = _mm_xor_si128(_mm_load_si128(input + i + 1), xout1); + xout2 = _mm_xor_si128(_mm_load_si128(input + i + 2), xout2); + xout3 = _mm_xor_si128(_mm_load_si128(input + i + 3), xout3); + xout4 = _mm_xor_si128(_mm_load_si128(input + i + 4), xout4); + xout5 = _mm_xor_si128(_mm_load_si128(input + i + 5), xout5); + xout6 = _mm_xor_si128(_mm_load_si128(input + i + 6), xout6); + xout7 = _mm_xor_si128(_mm_load_si128(input + i + 7), xout7); + + if (SOFT_AES) { + aes_round(_mm_setzero_si128(), &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + } + + aes_round(k0, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k1, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k2, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k3, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k4, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k5, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k6, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k7, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k8, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + + if (SOFT_AES) { + xout0 ^= k9; + xout1 ^= k9; + xout2 ^= k9; + xout3 ^= k9; + xout4 ^= k9; + xout5 ^= k9; + xout6 ^= k9; + xout7 ^= k9; + } else { + aes_round(k9, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + } + + mix_and_propagate(xout0, xout1, xout2, xout3, xout4, xout5, xout6, xout7); + } + + for (size_t i = 0; i < 16; i++) { + if (SOFT_AES) { + aes_round(_mm_setzero_si128(), &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + } + + aes_round(k0, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k1, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k2, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k3, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k4, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k5, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k6, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k7, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k8, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + + if (SOFT_AES) { + xout0 ^= k9; + xout1 ^= k9; + xout2 ^= k9; + xout3 ^= k9; + xout4 ^= k9; + xout5 ^= k9; + xout6 ^= k9; + xout7 ^= k9; + } else { + aes_round(k9, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + } + + mix_and_propagate(xout0, xout1, xout2, xout3, xout4, xout5, xout6, xout7); + } + } + + _mm_store_si128(output + 4, xout0); + _mm_store_si128(output + 5, xout1); + _mm_store_si128(output + 6, xout2); + _mm_store_si128(output + 7, xout3); + _mm_store_si128(output + 8, xout4); + _mm_store_si128(output + 9, xout5); + _mm_store_si128(output + 10, xout6); + _mm_store_si128(output + 11, xout7); +} + + +} /* namespace xmrig */ + + +static inline __m128i aes_round_tweak_div(const __m128i &in, const __m128i &key) +{ + alignas(16) uint32_t k[4]; + alignas(16) uint32_t x[4]; + + _mm_store_si128((__m128i*) k, key); + _mm_store_si128((__m128i*) x, _mm_xor_si128(in, _mm_set_epi64x(0xffffffffffffffff, 0xffffffffffffffff))); + + #define BYTE(p, i) ((unsigned char*)&x[p])[i] + k[0] ^= saes_table[0][BYTE(0, 0)] ^ saes_table[1][BYTE(1, 1)] ^ saes_table[2][BYTE(2, 2)] ^ saes_table[3][BYTE(3, 3)]; + x[0] ^= k[0]; + k[1] ^= saes_table[0][BYTE(1, 0)] ^ saes_table[1][BYTE(2, 1)] ^ saes_table[2][BYTE(3, 2)] ^ saes_table[3][BYTE(0, 3)]; + x[1] ^= k[1]; + k[2] ^= saes_table[0][BYTE(2, 0)] ^ saes_table[1][BYTE(3, 1)] ^ saes_table[2][BYTE(0, 2)] ^ saes_table[3][BYTE(1, 3)]; + x[2] ^= k[2]; + k[3] ^= saes_table[0][BYTE(3, 0)] ^ saes_table[1][BYTE(0, 1)] ^ saes_table[2][BYTE(1, 2)] ^ saes_table[3][BYTE(2, 3)]; + #undef BYTE + + return _mm_load_si128((__m128i*)k); +} + + +namespace xmrig { + + +template +static inline void cryptonight_monero_tweak(const uint8_t* l, uint64_t idx, __m128i ax0, __m128i bx0, __m128i bx1, __m128i& cx) +{ + constexpr CnAlgo props; + + uint64_t* mem_out = (uint64_t*)&l[idx]; + + if (props.base() == Algorithm::CN_2) { + VARIANT2_SHUFFLE(l, idx, ax0, bx0, bx1, cx, (ALGO == Algorithm::CN_RWZ || ALGO == Algorithm::CN_EXTREMELITE_0 ? 1 : 0)); + _mm_store_si128((__m128i *)mem_out, _mm_xor_si128(bx0, cx)); + } else { + __m128i tmp = _mm_xor_si128(bx0, cx); + mem_out[0] = _mm_cvtsi128_si64(tmp); + + uint64_t vh = vgetq_lane_u64(tmp, 1); + + uint8_t x = vh >> 24; + static const uint16_t table = 0x7531; + const uint8_t index = (((x >> (3)) & 6) | (x & 1)) << 1; + vh ^= ((table >> index) & 0x3) << 28; + + mem_out[1] = vh; + } +} + + +template +inline void cryptonight_single_hash(const uint8_t *__restrict__ input, size_t size, uint8_t *__restrict__ output, cryptonight_ctx **__restrict__ ctx, uint64_t height) +{ + constexpr CnAlgo props; + constexpr size_t MASK = props.mask(); + constexpr Algorithm::Id BASE = props.base(); + +# ifdef XMRIG_ALGO_CN_HEAVY + constexpr bool IS_CN_HEAVY_TUBE = ALGO == Algorithm::CN_HEAVY_TUBE; +# else + constexpr bool IS_CN_HEAVY_TUBE = false; +# endif + + if (BASE == Algorithm::CN_1 && size < 43) { + memset(output, 0, 32); + return; + } + + keccak(input, size, ctx[0]->state); + cn_explode_scratchpad(reinterpret_cast(ctx[0]->state), reinterpret_cast<__m128i *>(ctx[0]->memory)); + + uint8_t* l0 = ctx[0]->memory; + uint64_t* h0 = reinterpret_cast(ctx[0]->state); + + VARIANT1_INIT(0); + VARIANT2_INIT(0); + VARIANT4_RANDOM_MATH_INIT(0); + + uint64_t al0 = h0[0] ^ h0[4]; + uint64_t ah0 = h0[1] ^ h0[5]; + __m128i bx0 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); + __m128i bx1 = _mm_set_epi64x(h0[9] ^ h0[11], h0[8] ^ h0[10]); + + uint64_t idx0 = al0; + + for (size_t i = 0; i < props.iterations(); i++) { + __m128i cx; + if (IS_CN_HEAVY_TUBE || !SOFT_AES) { + cx = _mm_load_si128(reinterpret_cast(&l0[idx0 & MASK])); + } + + const __m128i ax0 = _mm_set_epi64x(ah0, al0); + if (IS_CN_HEAVY_TUBE) { + cx = aes_round_tweak_div(cx, ax0); + } + else if (SOFT_AES) { + cx = soft_aesenc((uint32_t*)&l0[idx0 & MASK], ax0); + } + else { + cx = _mm_aesenc_si128(cx, ax0); + } + + if (BASE == Algorithm::CN_1 || BASE == Algorithm::CN_2) { + cryptonight_monero_tweak(l0, idx0 & MASK, ax0, bx0, bx1, cx); + } else { + _mm_store_si128((__m128i *)&l0[idx0 & MASK], _mm_xor_si128(bx0, cx)); + } + + idx0 = _mm_cvtsi128_si64(cx); + + uint64_t hi, lo, cl, ch; + cl = ((uint64_t*) &l0[idx0 & MASK])[0]; + ch = ((uint64_t*) &l0[idx0 & MASK])[1]; + + if (BASE == Algorithm::CN_2) { + if (props.isR()) { + VARIANT4_RANDOM_MATH(0, al0, ah0, cl, bx0, bx1); + if (ALGO == Algorithm::CN_R) { + al0 ^= r0[2] | ((uint64_t)(r0[3]) << 32); + ah0 ^= r0[0] | ((uint64_t)(r0[1]) << 32); + } + } else { + VARIANT2_INTEGER_MATH(0, cl, cx); + } + } + + lo = __umul128(idx0, cl, &hi); + + if (BASE == Algorithm::CN_2) { + if (ALGO == Algorithm::CN_R) { + VARIANT2_SHUFFLE(l0, idx0 & MASK, ax0, bx0, bx1, cx, 0); + } else { + VARIANT2_SHUFFLE2(l0, idx0 & MASK, ax0, bx0, bx1, hi, lo, (ALGO == Algorithm::CN_RWZ || ALGO == Algorithm::CN_EXTREMELITE_0 ? 1 : 0)); + } + } + + al0 += hi; + ah0 += lo; + + ((uint64_t*)&l0[idx0 & MASK])[0] = al0; + + if (IS_CN_HEAVY_TUBE || ALGO == Algorithm::CN_RTO) { + ((uint64_t*)&l0[idx0 & MASK])[1] = ah0 ^ tweak1_2_0 ^ al0; + } else if (BASE == Algorithm::CN_1) { + ((uint64_t*)&l0[idx0 & MASK])[1] = ah0 ^ tweak1_2_0; + } else { + ((uint64_t*)&l0[idx0 & MASK])[1] = ah0; + } + + al0 ^= cl; + ah0 ^= ch; + idx0 = al0; + +# ifdef XMRIG_ALGO_CN_HEAVY + if (props.isHeavy()) { + const int64x2_t x = vld1q_s64(reinterpret_cast(&l0[idx0 & MASK])); + const int64_t n = vgetq_lane_s64(x, 0); + const int32_t d = vgetq_lane_s32(x, 2); + const int64_t q = n / (d | 0x5); + + ((int64_t*)&l0[idx0 & MASK])[0] = n ^ q; + + if (ALGO == Algorithm::CN_HEAVY_XHV) { + idx0 = (~d) ^ q; + } + else { + idx0 = d ^ q; + } + } +# endif + + if (BASE == Algorithm::CN_2) { + bx1 = bx0; + } + + bx0 = cx; + } + + cn_implode_scratchpad(reinterpret_cast(ctx[0]->memory), reinterpret_cast<__m128i *>(ctx[0]->state)); + keccakf(h0, 24); + extra_hashes[ctx[0]->state[0] & 3](ctx[0]->state, 200, output); +} + + +} /* namespace xmrig */ + + +#ifdef XMRIG_ALGO_CN_GPU +template +void cn_gpu_inner_arm(const uint8_t *spad, uint8_t *lpad); + + +namespace xmrig { + + +template +void cn_explode_scratchpad_gpu(const uint8_t *input, uint8_t *output) +{ + constexpr size_t hash_size = 200; // 25x8 bytes + alignas(16) uint64_t hash[25]; + + for (uint64_t i = 0; i < MEM / 512; i++) { + memcpy(hash, input, hash_size); + hash[0] ^= i; + + xmrig::keccakf(hash, 24); + memcpy(output, hash, 160); + output += 160; + + xmrig::keccakf(hash, 24); + memcpy(output, hash, 176); + output += 176; + + xmrig::keccakf(hash, 24); + memcpy(output, hash, 176); + output += 176; + } +} + + +template +inline void cryptonight_single_hash_gpu(const uint8_t *__restrict__ input, size_t size, uint8_t *__restrict__ output, cryptonight_ctx **__restrict__ ctx, uint64_t height) +{ + constexpr CnAlgo props; + + keccak(input, size, ctx[0]->state); + cn_explode_scratchpad_gpu(ctx[0]->state, ctx[0]->memory); + + fesetround(FE_TONEAREST); + + cn_gpu_inner_arm(ctx[0]->state, ctx[0]->memory); + + cn_implode_scratchpad(reinterpret_cast(ctx[0]->memory), reinterpret_cast<__m128i *>(ctx[0]->state)); + keccakf(reinterpret_cast(ctx[0]->state), 24); + memcpy(output, ctx[0]->state, 32); +} + +} /* namespace xmrig */ +#endif + + +namespace xmrig { + + +template +inline void cryptonight_double_hash(const uint8_t *__restrict__ input, size_t size, uint8_t *__restrict__ output, struct cryptonight_ctx **__restrict__ ctx, uint64_t height) +{ + constexpr CnAlgo props; + constexpr size_t MASK = props.mask(); + constexpr Algorithm::Id BASE = props.base(); + +# ifdef XMRIG_ALGO_CN_HEAVY + constexpr bool IS_CN_HEAVY_TUBE = ALGO == Algorithm::CN_HEAVY_TUBE; +# else + constexpr bool IS_CN_HEAVY_TUBE = false; +# endif + + if (BASE == Algorithm::CN_1 && size < 43) { + memset(output, 0, 64); + return; + } + + keccak(input, size, ctx[0]->state); + keccak(input + size, size, ctx[1]->state); + + uint8_t *l0 = ctx[0]->memory; + uint8_t *l1 = ctx[1]->memory; + uint64_t *h0 = reinterpret_cast(ctx[0]->state); + uint64_t *h1 = reinterpret_cast(ctx[1]->state); + + VARIANT1_INIT(0); + VARIANT1_INIT(1); + VARIANT2_INIT(0); + VARIANT2_INIT(1); + VARIANT4_RANDOM_MATH_INIT(0); + VARIANT4_RANDOM_MATH_INIT(1); + + cn_explode_scratchpad(reinterpret_cast(h0), reinterpret_cast<__m128i *>(l0)); + cn_explode_scratchpad(reinterpret_cast(h1), reinterpret_cast<__m128i *>(l1)); + + uint64_t al0 = h0[0] ^ h0[4]; + uint64_t al1 = h1[0] ^ h1[4]; + uint64_t ah0 = h0[1] ^ h0[5]; + uint64_t ah1 = h1[1] ^ h1[5]; + + __m128i bx00 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); + __m128i bx01 = _mm_set_epi64x(h0[9] ^ h0[11], h0[8] ^ h0[10]); + __m128i bx10 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); + __m128i bx11 = _mm_set_epi64x(h1[9] ^ h1[11], h1[8] ^ h1[10]); + + uint64_t idx0 = al0; + uint64_t idx1 = al1; + + for (size_t i = 0; i < props.iterations(); i++) { + __m128i cx0, cx1; + if (IS_CN_HEAVY_TUBE || !SOFT_AES) { + cx0 = _mm_load_si128((__m128i *) &l0[idx0 & MASK]); + cx1 = _mm_load_si128((__m128i *) &l1[idx1 & MASK]); + } + + const __m128i ax0 = _mm_set_epi64x(ah0, al0); + const __m128i ax1 = _mm_set_epi64x(ah1, al1); + if (IS_CN_HEAVY_TUBE) { + cx0 = aes_round_tweak_div(cx0, ax0); + cx1 = aes_round_tweak_div(cx1, ax1); + } + else if (SOFT_AES) { + cx0 = soft_aesenc((uint32_t*)&l0[idx0 & MASK], ax0); + cx1 = soft_aesenc((uint32_t*)&l1[idx1 & MASK], ax1); + } + else { + cx0 = _mm_aesenc_si128(cx0, ax0); + cx1 = _mm_aesenc_si128(cx1, ax1); + } + + if (BASE == Algorithm::CN_1 || BASE == Algorithm::CN_2) { + cryptonight_monero_tweak(l0, idx0 & MASK, ax0, bx00, bx01, cx0); + cryptonight_monero_tweak(l1, idx1 & MASK, ax1, bx10, bx11, cx1); + } else { + _mm_store_si128((__m128i *) &l0[idx0 & MASK], _mm_xor_si128(bx00, cx0)); + _mm_store_si128((__m128i *) &l1[idx1 & MASK], _mm_xor_si128(bx10, cx1)); + } + + idx0 = _mm_cvtsi128_si64(cx0); + idx1 = _mm_cvtsi128_si64(cx1); + + uint64_t hi, lo, cl, ch; + cl = ((uint64_t*) &l0[idx0 & MASK])[0]; + ch = ((uint64_t*) &l0[idx0 & MASK])[1]; + + if (BASE == Algorithm::CN_2) { + if (props.isR()) { + VARIANT4_RANDOM_MATH(0, al0, ah0, cl, bx00, bx01); + if (ALGO == Algorithm::CN_R) { + al0 ^= r0[2] | ((uint64_t)(r0[3]) << 32); + ah0 ^= r0[0] | ((uint64_t)(r0[1]) << 32); + } + } else { + VARIANT2_INTEGER_MATH(0, cl, cx0); + } + } + + lo = __umul128(idx0, cl, &hi); + + if (BASE == Algorithm::CN_2) { + if (ALGO == Algorithm::CN_R) { + VARIANT2_SHUFFLE(l0, idx0 & MASK, ax0, bx00, bx01, cx0, 0); + } else { + VARIANT2_SHUFFLE2(l0, idx0 & MASK, ax0, bx00, bx01, hi, lo, (ALGO == Algorithm::CN_RWZ || ALGO == Algorithm::CN_EXTREMELITE_0 ? 1 : 0)); + } + } + + al0 += hi; + ah0 += lo; + + ((uint64_t*)&l0[idx0 & MASK])[0] = al0; + + if (IS_CN_HEAVY_TUBE || ALGO == Algorithm::CN_RTO) { + ((uint64_t*)&l0[idx0 & MASK])[1] = ah0 ^ tweak1_2_0 ^ al0; + } else if (BASE == Algorithm::CN_1) { + ((uint64_t*)&l0[idx0 & MASK])[1] = ah0 ^ tweak1_2_0; + } else { + ((uint64_t*)&l0[idx0 & MASK])[1] = ah0; + } + + al0 ^= cl; + ah0 ^= ch; + idx0 = al0; + +# ifdef XMRIG_ALGO_CN_HEAVY + if (props.isHeavy()) { + const int64x2_t x = vld1q_s64(reinterpret_cast(&l0[idx0 & MASK])); + const int64_t n = vgetq_lane_s64(x, 0); + const int32_t d = vgetq_lane_s32(x, 2); + const int64_t q = n / (d | 0x5); + + ((int64_t*)&l0[idx0 & MASK])[0] = n ^ q; + + if (ALGO == Algorithm::CN_HEAVY_XHV) { + idx0 = (~d) ^ q; + } + else { + idx0 = d ^ q; + } + } +# endif + + cl = ((uint64_t*) &l1[idx1 & MASK])[0]; + ch = ((uint64_t*) &l1[idx1 & MASK])[1]; + + if (BASE == Algorithm::CN_2) { + if (props.isR()) { + VARIANT4_RANDOM_MATH(1, al1, ah1, cl, bx10, bx11); + if (ALGO == Algorithm::CN_R) { + al1 ^= r1[2] | ((uint64_t)(r1[3]) << 32); + ah1 ^= r1[0] | ((uint64_t)(r1[1]) << 32); + } + } else { + VARIANT2_INTEGER_MATH(1, cl, cx1); + } + } + + lo = __umul128(idx1, cl, &hi); + + if (BASE == Algorithm::CN_2) { + if (ALGO == Algorithm::CN_R) { + VARIANT2_SHUFFLE(l1, idx1 & MASK, ax1, bx10, bx11, cx1, 0); + } else { + VARIANT2_SHUFFLE2(l1, idx1 & MASK, ax1, bx10, bx11, hi, lo, (ALGO == Algorithm::CN_RWZ || ALGO == Algorithm::CN_EXTREMELITE_0 ? 1 : 0)); + } + } + + al1 += hi; + ah1 += lo; + + ((uint64_t*)&l1[idx1 & MASK])[0] = al1; + + if (IS_CN_HEAVY_TUBE || ALGO == Algorithm::CN_RTO) { + ((uint64_t*)&l1[idx1 & MASK])[1] = ah1 ^ tweak1_2_1 ^ al1; + } else if (BASE == Algorithm::CN_1) { + ((uint64_t*)&l1[idx1 & MASK])[1] = ah1 ^ tweak1_2_1; + } else { + ((uint64_t*)&l1[idx1 & MASK])[1] = ah1; + } + + al1 ^= cl; + ah1 ^= ch; + idx1 = al1; + +# ifdef XMRIG_ALGO_CN_HEAVY + if (props.isHeavy()) { + const int64x2_t x = vld1q_s64(reinterpret_cast(&l1[idx1 & MASK])); + const int64_t n = vgetq_lane_s64(x, 0); + const int32_t d = vgetq_lane_s32(x, 2); + const int64_t q = n / (d | 0x5); + + ((int64_t*)&l1[idx1 & MASK])[0] = n ^ q; + + if (ALGO == Algorithm::CN_HEAVY_XHV) { + idx1 = (~d) ^ q; + } + else { + idx1 = d ^ q; + } + } +# endif + + if (BASE == Algorithm::CN_2) { + bx01 = bx00; + bx11 = bx10; + } + + bx00 = cx0; + bx10 = cx1; + } + + cn_implode_scratchpad(reinterpret_cast(l0), reinterpret_cast<__m128i *>(h0)); + cn_implode_scratchpad(reinterpret_cast(l1), reinterpret_cast<__m128i *>(h1)); + + keccakf(h0, 24); + keccakf(h1, 24); + + extra_hashes[ctx[0]->state[0] & 3](ctx[0]->state, 200, output); + extra_hashes[ctx[1]->state[0] & 3](ctx[1]->state, 200, output + 32); +} + + +template +inline void cryptonight_triple_hash(const uint8_t *__restrict__ input, size_t size, uint8_t *__restrict__ output, struct cryptonight_ctx **__restrict__ ctx, uint64_t height) +{ +} + + +template +inline void cryptonight_quad_hash(const uint8_t *__restrict__ input, size_t size, uint8_t *__restrict__ output, struct cryptonight_ctx **__restrict__ ctx, uint64_t height) +{ +} + + +template +inline void cryptonight_penta_hash(const uint8_t *__restrict__ input, size_t size, uint8_t *__restrict__ output, struct cryptonight_ctx **__restrict__ ctx, uint64_t height) +{ +} + + +} /* namespace xmrig */ + + +#endif /* XMRIG_CRYPTONIGHT_ARM_H */ diff --git a/src/crypto/cn/CryptoNight_monero.h b/src/crypto/cn/CryptoNight_monero.h new file mode 100644 index 00000000..cdcec8d2 --- /dev/null +++ b/src/crypto/cn/CryptoNight_monero.h @@ -0,0 +1,207 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018 Lee Clagett + * Copyright 2018 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_CRYPTONIGHT_MONERO_H +#define XMRIG_CRYPTONIGHT_MONERO_H + +#include +#include + +// VARIANT ALTERATIONS +#ifndef XMRIG_ARM +# define VARIANT1_INIT(part) \ + uint64_t tweak1_2_##part = 0; \ + if (BASE == Algorithm::CN_1) { \ + tweak1_2_##part = (*reinterpret_cast(input + 35 + part * size) ^ \ + *(reinterpret_cast(ctx[part]->state) + 24)); \ + } +#else +# define VARIANT1_INIT(part) \ + uint64_t tweak1_2_##part = 0; \ + if (BASE == Algorithm::CN_1) { \ + memcpy(&tweak1_2_##part, input + 35 + part * size, sizeof tweak1_2_##part); \ + tweak1_2_##part ^= *(reinterpret_cast(ctx[part]->state) + 24); \ + } +#endif + +#define VARIANT1_1(p) \ + if (BASE == Algorithm::CN_1) { \ + const uint8_t tmp = reinterpret_cast(p)[11]; \ + static const uint32_t table = 0x75310; \ + const uint8_t index = (((tmp >> 3) & 6) | (tmp & 1)) << 1; \ + ((uint8_t*)(p))[11] = tmp ^ ((table >> index) & 0x30); \ + } + +#define VARIANT1_2(p, part) \ + if (BASE == Algorithm::CN_1) { \ + (p) ^= tweak1_2_##part; \ + } + + +#ifndef XMRIG_ARM +# define VARIANT2_INIT(part) \ + __m128i division_result_xmm_##part = _mm_cvtsi64_si128(static_cast(h##part[12])); \ + __m128i sqrt_result_xmm_##part = _mm_cvtsi64_si128(static_cast(h##part[13])); + +#ifdef _MSC_VER +# define VARIANT2_SET_ROUNDING_MODE() if (BASE == Algorithm::CN_2) { _control87(RC_DOWN, MCW_RC); } +# define SET_ROUNDING_MODE_NEAREST() _control87(RC_NEAR, MCW_RC);; +#else +# define VARIANT2_SET_ROUNDING_MODE() if (BASE == Algorithm::CN_2) { fesetround(FE_DOWNWARD); } +# define SET_ROUNDING_MODE_NEAREST() fesetround(FE_TONEAREST); +#endif + +# define VARIANT2_INTEGER_MATH(part, cl, cx) \ + do { \ + const uint64_t sqrt_result = static_cast(_mm_cvtsi128_si64(sqrt_result_xmm_##part)); \ + const uint64_t cx_0 = _mm_cvtsi128_si64(cx); \ + cl ^= static_cast(_mm_cvtsi128_si64(division_result_xmm_##part)) ^ (sqrt_result << 32); \ + const uint32_t d = static_cast(cx_0 + (sqrt_result << 1)) | 0x80000001UL; \ + const uint64_t cx_1 = _mm_cvtsi128_si64(_mm_srli_si128(cx, 8)); \ + const uint64_t division_result = static_cast(cx_1 / d) + ((cx_1 % d) << 32); \ + division_result_xmm_##part = _mm_cvtsi64_si128(static_cast(division_result)); \ + sqrt_result_xmm_##part = int_sqrt_v2(cx_0 + division_result); \ + } while (0) + +# define VARIANT2_SHUFFLE(base_ptr, offset, _a, _b, _b1, _c, reverse) \ + do { \ + const __m128i chunk1 = _mm_load_si128((__m128i *)((base_ptr) + ((offset) ^ (reverse ? 0x30 : 0x10)))); \ + const __m128i chunk2 = _mm_load_si128((__m128i *)((base_ptr) + ((offset) ^ 0x20))); \ + const __m128i chunk3 = _mm_load_si128((__m128i *)((base_ptr) + ((offset) ^ (reverse ? 0x10 : 0x30)))); \ + _mm_store_si128((__m128i *)((base_ptr) + ((offset) ^ 0x10)), _mm_add_epi64(chunk3, _b1)); \ + _mm_store_si128((__m128i *)((base_ptr) + ((offset) ^ 0x20)), _mm_add_epi64(chunk1, _b)); \ + _mm_store_si128((__m128i *)((base_ptr) + ((offset) ^ 0x30)), _mm_add_epi64(chunk2, _a)); \ + if (ALGO == Algorithm::CN_R) { \ + _c = _mm_xor_si128(_mm_xor_si128(_c, chunk3), _mm_xor_si128(chunk1, chunk2)); \ + } \ + } while (0) + +# define VARIANT2_SHUFFLE2(base_ptr, offset, _a, _b, _b1, hi, lo, reverse) \ + do { \ + const __m128i chunk1 = _mm_xor_si128(_mm_load_si128((__m128i *)((base_ptr) + ((offset) ^ 0x10))), _mm_set_epi64x(lo, hi)); \ + const __m128i chunk2 = _mm_load_si128((__m128i *)((base_ptr) + ((offset) ^ 0x20))); \ + hi ^= ((uint64_t*)((base_ptr) + ((offset) ^ 0x20)))[0]; \ + lo ^= ((uint64_t*)((base_ptr) + ((offset) ^ 0x20)))[1]; \ + const __m128i chunk3 = _mm_load_si128((__m128i *)((base_ptr) + ((offset) ^ 0x30))); \ + if (reverse) { \ + _mm_store_si128((__m128i *)((base_ptr) + ((offset) ^ 0x10)), _mm_add_epi64(chunk1, _b1)); \ + _mm_store_si128((__m128i *)((base_ptr) + ((offset) ^ 0x20)), _mm_add_epi64(chunk3, _b)); \ + } else { \ + _mm_store_si128((__m128i *)((base_ptr) + ((offset) ^ 0x10)), _mm_add_epi64(chunk3, _b1)); \ + _mm_store_si128((__m128i *)((base_ptr) + ((offset) ^ 0x20)), _mm_add_epi64(chunk1, _b)); \ + } \ + _mm_store_si128((__m128i *)((base_ptr) + ((offset) ^ 0x30)), _mm_add_epi64(chunk2, _a)); \ + } while (0) + +#else +# define VARIANT2_INIT(part) \ + uint64_t division_result_##part = h##part[12]; \ + uint64_t sqrt_result_##part = h##part[13]; + +# define VARIANT2_INTEGER_MATH(part, cl, cx) \ + do { \ + const uint64_t cx_0 = _mm_cvtsi128_si64(cx); \ + cl ^= division_result_##part ^ (sqrt_result_##part << 32); \ + const uint32_t d = static_cast(cx_0 + (sqrt_result_##part << 1)) | 0x80000001UL; \ + const uint64_t cx_1 = _mm_cvtsi128_si64(_mm_srli_si128(cx, 8)); \ + division_result_##part = static_cast(cx_1 / d) + ((cx_1 % d) << 32); \ + const uint64_t sqrt_input = cx_0 + division_result_##part; \ + sqrt_result_##part = sqrt(sqrt_input + 18446744073709551616.0) * 2.0 - 8589934592.0; \ + const uint64_t s = sqrt_result_##part >> 1; \ + const uint64_t b = sqrt_result_##part & 1; \ + const uint64_t r2 = (uint64_t)(s) * (s + b) + (sqrt_result_##part << 32); \ + sqrt_result_##part += ((r2 + b > sqrt_input) ? -1 : 0) + ((r2 + (1ULL << 32) < sqrt_input - s) ? 1 : 0); \ + } while (0) + +# define VARIANT2_SHUFFLE(base_ptr, offset, _a, _b, _b1, _c, reverse) \ + do { \ + const uint64x2_t chunk1 = vld1q_u64((uint64_t*)((base_ptr) + ((offset) ^ (reverse ? 0x30 : 0x10)))); \ + const uint64x2_t chunk2 = vld1q_u64((uint64_t*)((base_ptr) + ((offset) ^ 0x20))); \ + const uint64x2_t chunk3 = vld1q_u64((uint64_t*)((base_ptr) + ((offset) ^ (reverse ? 0x10 : 0x30)))); \ + vst1q_u64((uint64_t*)((base_ptr) + ((offset) ^ 0x10)), vaddq_u64(chunk3, vreinterpretq_u64_u8(_b1))); \ + vst1q_u64((uint64_t*)((base_ptr) + ((offset) ^ 0x20)), vaddq_u64(chunk1, vreinterpretq_u64_u8(_b))); \ + vst1q_u64((uint64_t*)((base_ptr) + ((offset) ^ 0x30)), vaddq_u64(chunk2, vreinterpretq_u64_u8(_a))); \ + if (ALGO == Algorithm::CN_R) { \ + _c = veorq_u64(veorq_u64(_c, chunk3), veorq_u64(chunk1, chunk2)); \ + } \ + } while (0) + +# define VARIANT2_SHUFFLE2(base_ptr, offset, _a, _b, _b1, hi, lo, reverse) \ + do { \ + const uint64x2_t chunk1 = veorq_u64(vld1q_u64((uint64_t*)((base_ptr) + ((offset) ^ 0x10))), vcombine_u64(vcreate_u64(hi), vcreate_u64(lo))); \ + const uint64x2_t chunk2 = vld1q_u64((uint64_t*)((base_ptr) + ((offset) ^ 0x20))); \ + hi ^= ((uint64_t*)((base_ptr) + ((offset) ^ 0x20)))[0]; \ + lo ^= ((uint64_t*)((base_ptr) + ((offset) ^ 0x20)))[1]; \ + const uint64x2_t chunk3 = vld1q_u64((uint64_t*)((base_ptr) + ((offset) ^ 0x30))); \ + if (reverse) { \ + vst1q_u64((uint64_t*)((base_ptr) + ((offset) ^ 0x10)), vaddq_u64(chunk1, vreinterpretq_u64_u8(_b1))); \ + vst1q_u64((uint64_t*)((base_ptr) + ((offset) ^ 0x20)), vaddq_u64(chunk3, vreinterpretq_u64_u8(_b))); \ + } else { \ + vst1q_u64((uint64_t*)((base_ptr) + ((offset) ^ 0x10)), vaddq_u64(chunk3, vreinterpretq_u64_u8(_b1))); \ + vst1q_u64((uint64_t*)((base_ptr) + ((offset) ^ 0x20)), vaddq_u64(chunk1, vreinterpretq_u64_u8(_b))); \ + } \ + vst1q_u64((uint64_t*)((base_ptr) + ((offset) ^ 0x30)), vaddq_u64(chunk2, vreinterpretq_u64_u8(_a))); \ + } while (0) +#endif + +#define SWAP32LE(x) x +#define SWAP64LE(x) x +#define hash_extra_blake(data, length, hash) blake256_hash((uint8_t*)(hash), (uint8_t*)(data), (length)) + +#ifndef NOINLINE +#ifdef __GNUC__ +#define NOINLINE __attribute__ ((noinline)) +#elif _MSC_VER +#define NOINLINE __declspec(noinline) +#else +#define NOINLINE +#endif +#endif + +#include "crypto/cn/r/variant4_random_math.h" + +#define VARIANT4_RANDOM_MATH_INIT(part) \ + uint32_t r##part[9]; \ + struct V4_Instruction code##part[256]; \ + if (props.isR()) { \ + r##part[0] = static_cast(h##part[12]); \ + r##part[1] = static_cast(h##part[12] >> 32); \ + r##part[2] = static_cast(h##part[13]); \ + r##part[3] = static_cast(h##part[13] >> 32); \ + } \ + v4_random_math_init(code##part, height); + +#define VARIANT4_RANDOM_MATH(part, al, ah, cl, bx0, bx1) \ + if (props.isR()) { \ + cl ^= (r##part[0] + r##part[1]) | (static_cast(r##part[2] + r##part[3]) << 32); \ + r##part[4] = static_cast(al); \ + r##part[5] = static_cast(ah); \ + r##part[6] = static_cast(_mm_cvtsi128_si32(bx0)); \ + r##part[7] = static_cast(_mm_cvtsi128_si32(bx1)); \ + r##part[8] = static_cast(_mm_cvtsi128_si32(_mm_srli_si128(bx1, 8))); \ + v4_random_math(code##part, r##part); \ + } + +#endif /* XMRIG_CRYPTONIGHT_MONERO_H */ diff --git a/src/crypto/cn/CryptoNight_test.h b/src/crypto/cn/CryptoNight_test.h new file mode 100644 index 00000000..56a3eb54 --- /dev/null +++ b/src/crypto/cn/CryptoNight_test.h @@ -0,0 +1,433 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018 Lee Clagett + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_CRYPTONIGHT_TEST_H +#define XMRIG_CRYPTONIGHT_TEST_H + + +#include + + +namespace xmrig { + + +const static uint8_t test_input[380] = { + 0x03, 0x05, 0xA0, 0xDB, 0xD6, 0xBF, 0x05, 0xCF, 0x16, 0xE5, 0x03, 0xF3, 0xA6, 0x6F, 0x78, 0x00, + 0x7C, 0xBF, 0x34, 0x14, 0x43, 0x32, 0xEC, 0xBF, 0xC2, 0x2E, 0xD9, 0x5C, 0x87, 0x00, 0x38, 0x3B, + 0x30, 0x9A, 0xCE, 0x19, 0x23, 0xA0, 0x96, 0x4B, 0x00, 0x00, 0x00, 0x08, 0xBA, 0x93, 0x9A, 0x62, + 0x72, 0x4C, 0x0D, 0x75, 0x81, 0xFC, 0xE5, 0x76, 0x1E, 0x9D, 0x8A, 0x0E, 0x6A, 0x1C, 0x3F, 0x92, + 0x4F, 0xDD, 0x84, 0x93, 0xD1, 0x11, 0x56, 0x49, 0xC0, 0x5E, 0xB6, 0x01, + 0x01, 0x00, 0xFB, 0x8E, 0x8A, 0xC8, 0x05, 0x89, 0x93, 0x23, 0x37, 0x1B, 0xB7, 0x90, 0xDB, 0x19, + 0x21, 0x8A, 0xFD, 0x8D, 0xB8, 0xE3, 0x75, 0x5D, 0x8B, 0x90, 0xF3, 0x9B, 0x3D, 0x55, 0x06, 0xA9, + 0xAB, 0xCE, 0x4F, 0xA9, 0x12, 0x24, 0x45, 0x00, 0x00, 0x00, 0x00, 0xEE, 0x81, 0x46, 0xD4, 0x9F, + 0xA9, 0x3E, 0xE7, 0x24, 0xDE, 0xB5, 0x7D, 0x12, 0xCB, 0xC6, 0xC6, 0xF3, 0xB9, 0x24, 0xD9, 0x46, + 0x12, 0x7C, 0x7A, 0x97, 0x41, 0x8F, 0x93, 0x48, 0x82, 0x8F, 0x0F, 0x02, + 0x07, 0x07, 0xB4, 0x87, 0xD0, 0xD6, 0x05, 0x26, 0xE0, 0xC6, 0xDD, 0x9B, 0xC7, 0x18, 0xC3, 0xCF, + 0x52, 0x04, 0xBD, 0x4F, 0x9B, 0x27, 0xF6, 0x73, 0xB9, 0x3F, 0xEF, 0x7B, 0xB2, 0xF7, 0x2B, 0xBB, + 0x3F, 0x3E, 0x9C, 0x3E, 0x9D, 0x33, 0x1E, 0xDE, 0xAD, 0xBE, 0xEF, 0x4E, 0x00, 0x91, 0x81, 0x29, + 0x74, 0xB2, 0x70, 0xE7, 0x6D, 0xD2, 0x2A, 0x5F, 0x52, 0x04, 0x93, 0xE6, 0x18, 0x89, 0x40, 0xD8, + 0xC6, 0xE3, 0x90, 0x6E, 0xAA, 0x6A, 0xB7, 0xE2, 0x08, 0x7E, 0x78, 0x0E, + 0x01, 0x00, 0xEE, 0xB2, 0xD1, 0xD6, 0x05, 0xFF, 0x27, 0x7F, 0x26, 0xDB, 0xAA, 0xB2, 0xC9, 0x26, + 0x30, 0xC6, 0xCF, 0x11, 0x64, 0xEA, 0x6C, 0x8A, 0xE0, 0x98, 0x01, 0xF8, 0x75, 0x4B, 0x49, 0xAF, + 0x79, 0x70, 0xAE, 0xEE, 0xA7, 0x62, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x47, 0x8C, 0x63, 0xE7, 0xD8, + 0x40, 0x02, 0x3C, 0xDA, 0xEA, 0x92, 0x52, 0x53, 0xAC, 0xFD, 0xC7, 0x8A, 0x4C, 0x31, 0xB2, 0xF2, + 0xEC, 0x72, 0x7B, 0xFF, 0xCE, 0xC0, 0xE7, 0x12, 0xD4, 0xE9, 0x2A, 0x01, + 0x07, 0x07, 0xA9, 0xB7, 0xD1, 0xD6, 0x05, 0x3F, 0x0D, 0x5E, 0xFD, 0xC7, 0x03, 0xFC, 0xFC, 0xD2, + 0xCE, 0xBC, 0x44, 0xD8, 0xAB, 0x44, 0xA6, 0xA0, 0x3A, 0xE4, 0x4D, 0x8F, 0x15, 0xAF, 0x62, 0x17, + 0xD1, 0xE0, 0x92, 0x85, 0xE4, 0x73, 0xF9, 0x00, 0x00, 0x00, 0xA0, 0xFC, 0x09, 0xDE, 0xAB, 0xF5, + 0x8B, 0x6F, 0x1D, 0xCA, 0xA8, 0xBA, 0xAC, 0x74, 0xDD, 0x74, 0x19, 0xD5, 0xD6, 0x10, 0xEC, 0x38, + 0xCF, 0x50, 0x29, 0x6A, 0x07, 0x0B, 0x93, 0x8F, 0x8F, 0xA8, 0x10, 0x04 +}; + + +struct cn_r_test_input_data +{ + uint64_t height; + size_t size; + uint8_t data[64]; +}; + + +const static cn_r_test_input_data cn_r_test_input[] = { + { 1806260, 44, { 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x74, 0x65, 0x73, 0x74, 0x20, 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x74, 0x65, 0x73, 0x74, 0x20, 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x74, 0x65, 0x73, 0x74 } }, + { 1806261, 50, { 0x4c, 0x6f, 0x72, 0x65, 0x6d, 0x20, 0x69, 0x70, 0x73, 0x75, 0x6d, 0x20, 0x64, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x73, 0x69, 0x74, 0x20, 0x61, 0x6d, 0x65, 0x74, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x63, 0x74, 0x65, 0x74, 0x75, 0x72, 0x20, 0x61, 0x64, 0x69, 0x70, 0x69, 0x73, 0x63, 0x69, 0x6e, 0x67 } }, + { 1806262, 48, { 0x65, 0x6c, 0x69, 0x74, 0x2c, 0x20, 0x73, 0x65, 0x64, 0x20, 0x64, 0x6f, 0x20, 0x65, 0x69, 0x75, 0x73, 0x6d, 0x6f, 0x64, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x20, 0x69, 0x6e, 0x63, 0x69, 0x64, 0x69, 0x64, 0x75, 0x6e, 0x74, 0x20, 0x75, 0x74, 0x20, 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x65 } }, + { 1806263, 48, { 0x65, 0x74, 0x20, 0x64, 0x6f, 0x6c, 0x6f, 0x72, 0x65, 0x20, 0x6d, 0x61, 0x67, 0x6e, 0x61, 0x20, 0x61, 0x6c, 0x69, 0x71, 0x75, 0x61, 0x2e, 0x20, 0x55, 0x74, 0x20, 0x65, 0x6e, 0x69, 0x6d, 0x20, 0x61, 0x64, 0x20, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x20, 0x76, 0x65, 0x6e, 0x69, 0x61, 0x6d, 0x2c } }, + { 1806264, 46, { 0x71, 0x75, 0x69, 0x73, 0x20, 0x6e, 0x6f, 0x73, 0x74, 0x72, 0x75, 0x64, 0x20, 0x65, 0x78, 0x65, 0x72, 0x63, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x75, 0x6c, 0x6c, 0x61, 0x6d, 0x63, 0x6f, 0x20, 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x69, 0x73, 0x20, 0x6e, 0x69, 0x73, 0x69 } }, + { 1806265, 45, { 0x75, 0x74, 0x20, 0x61, 0x6c, 0x69, 0x71, 0x75, 0x69, 0x70, 0x20, 0x65, 0x78, 0x20, 0x65, 0x61, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x64, 0x6f, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x71, 0x75, 0x61, 0x74, 0x2e, 0x20, 0x44, 0x75, 0x69, 0x73, 0x20, 0x61, 0x75, 0x74, 0x65 } }, + { 1806266, 47, { 0x69, 0x72, 0x75, 0x72, 0x65, 0x20, 0x64, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x69, 0x6e, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x68, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x69, 0x74, 0x20, 0x69, 0x6e, 0x20, 0x76, 0x6f, 0x6c, 0x75, 0x70, 0x74, 0x61, 0x74, 0x65, 0x20, 0x76, 0x65, 0x6c, 0x69, 0x74 } }, + { 1806267, 44, { 0x65, 0x73, 0x73, 0x65, 0x20, 0x63, 0x69, 0x6c, 0x6c, 0x75, 0x6d, 0x20, 0x64, 0x6f, 0x6c, 0x6f, 0x72, 0x65, 0x20, 0x65, 0x75, 0x20, 0x66, 0x75, 0x67, 0x69, 0x61, 0x74, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x61, 0x20, 0x70, 0x61, 0x72, 0x69, 0x61, 0x74, 0x75, 0x72, 0x2e } }, + { 1806268, 47, { 0x45, 0x78, 0x63, 0x65, 0x70, 0x74, 0x65, 0x75, 0x72, 0x20, 0x73, 0x69, 0x6e, 0x74, 0x20, 0x6f, 0x63, 0x63, 0x61, 0x65, 0x63, 0x61, 0x74, 0x20, 0x63, 0x75, 0x70, 0x69, 0x64, 0x61, 0x74, 0x61, 0x74, 0x20, 0x6e, 0x6f, 0x6e, 0x20, 0x70, 0x72, 0x6f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x2c } }, + { 1806269, 62, { 0x73, 0x75, 0x6e, 0x74, 0x20, 0x69, 0x6e, 0x20, 0x63, 0x75, 0x6c, 0x70, 0x61, 0x20, 0x71, 0x75, 0x69, 0x20, 0x6f, 0x66, 0x66, 0x69, 0x63, 0x69, 0x61, 0x20, 0x64, 0x65, 0x73, 0x65, 0x72, 0x75, 0x6e, 0x74, 0x20, 0x6d, 0x6f, 0x6c, 0x6c, 0x69, 0x74, 0x20, 0x61, 0x6e, 0x69, 0x6d, 0x20, 0x69, 0x64, 0x20, 0x65, 0x73, 0x74, 0x20, 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x75, 0x6d, 0x2e } }, +}; + + +// "cn/wow" +const static uint8_t test_output_wow[] = { + 0x9d, 0x47, 0xbf, 0x4c, 0x41, 0xb7, 0xe8, 0xe7, 0x27, 0xe6, 0x81, 0x71, 0x5a, 0xcb, 0x47, 0xfa, 0x16, 0x77, 0xcd, 0xba, 0x9c, 0xa7, 0xbc, 0xb0, 0x5a, 0xd8, 0xcc, 0x8a, 0xbd, 0x5d, 0xaa, 0x66, + 0x0d, 0x4a, 0x49, 0x5c, 0xb8, 0x44, 0xa3, 0xca, 0x8b, 0xa4, 0xed, 0xb8, 0xe6, 0xbc, 0xf8, 0x29, 0xef, 0x1c, 0x06, 0xd9, 0xcd, 0xea, 0x2b, 0x62, 0xca, 0x46, 0xc2, 0xa2, 0x1b, 0x8b, 0x0a, 0x79, + 0xa1, 0xd6, 0xd8, 0x48, 0xb5, 0xc5, 0x91, 0x5f, 0xcc, 0xd2, 0xf6, 0x4c, 0xf2, 0x16, 0xc6, 0xb1, 0xa0, 0x2c, 0xf7, 0xc7, 0x7b, 0xc8, 0x0d, 0x8d, 0x4e, 0x51, 0xb4, 0x19, 0xe8, 0x8f, 0xf0, 0xdd, + 0xaf, 0x3a, 0x85, 0x44, 0xa0, 0x22, 0x1a, 0x14, 0x8c, 0x2a, 0xc9, 0x04, 0x84, 0xb1, 0x98, 0x61, 0xe3, 0xaf, 0xca, 0x33, 0xfe, 0x17, 0x02, 0x1e, 0xfb, 0x8a, 0xd6, 0x49, 0x6b, 0x56, 0x79, 0x15, + 0x31, 0x33, 0x99, 0xe0, 0x96, 0x3a, 0xe8, 0xa9, 0x9d, 0xab, 0x8a, 0xf6, 0x6d, 0x34, 0x3e, 0x09, 0x7d, 0xae, 0x0c, 0x0f, 0xeb, 0x08, 0xdb, 0xc4, 0x3c, 0xcd, 0xaf, 0xef, 0x55, 0x15, 0xf4, 0x13, + 0x60, 0x21, 0xc6, 0xef, 0x90, 0xbf, 0xf9, 0xae, 0x94, 0xa7, 0x50, 0x6d, 0x62, 0x3d, 0x3a, 0x7a, 0x86, 0xc1, 0x75, 0x6d, 0x65, 0x5f, 0x50, 0xdd, 0x55, 0x8f, 0x71, 0x6d, 0x64, 0x62, 0x2a, 0x34, + 0x2b, 0x13, 0x00, 0x05, 0x35, 0xf3, 0xdb, 0x5f, 0x9b, 0x9b, 0x84, 0xa6, 0x5c, 0x43, 0x51, 0xf3, 0x86, 0xcd, 0x2c, 0xde, 0xde, 0xbb, 0x8c, 0x3a, 0xd2, 0xea, 0xb0, 0x86, 0xe6, 0xa3, 0xfe, 0xe5, + 0xfc, 0x0e, 0x1d, 0xad, 0x8e, 0x89, 0x57, 0x49, 0xdc, 0x90, 0xeb, 0x69, 0x0b, 0xc1, 0xba, 0x05, 0x9a, 0x1c, 0xd7, 0x72, 0xaf, 0xaa, 0xf6, 0x5a, 0x10, 0x6b, 0xf9, 0xe5, 0xe6, 0xb8, 0x05, 0x03, + 0xb6, 0x0b, 0x0a, 0xfe, 0x14, 0x4d, 0xef, 0xf7, 0xd9, 0x03, 0xed, 0x2d, 0x55, 0x45, 0xe7, 0x7e, 0xbe, 0x66, 0xa3, 0xc5, 0x1f, 0xee, 0x70, 0x16, 0xee, 0xb8, 0xfe, 0xe9, 0xeb, 0x63, 0x0c, 0x0f, + 0x64, 0x77, 0x4b, 0x27, 0xe7, 0xd5, 0xfe, 0xc8, 0x62, 0xfc, 0x4c, 0x0c, 0x13, 0xac, 0x6b, 0xf0, 0x91, 0x23, 0xb6, 0xf0, 0x5b, 0xb0, 0xe4, 0xb7, 0x5c, 0x97, 0xf3, 0x79, 0xa2, 0xb3, 0xa6, 0x79, +}; + + +// "cn/r" +const static uint8_t test_output_r[] = { + 0xf7, 0x59, 0x58, 0x8a, 0xd5, 0x7e, 0x75, 0x84, 0x67, 0x29, 0x54, 0x43, 0xa9, 0xbd, 0x71, 0x49, 0x0a, 0xbf, 0xf8, 0xe9, 0xda, 0xd1, 0xb9, 0x5b, 0x6b, 0xf2, 0xf5, 0xd0, 0xd7, 0x83, 0x87, 0xbc, + 0x5b, 0xb8, 0x33, 0xde, 0xca, 0x2b, 0xdd, 0x72, 0x52, 0xa9, 0xcc, 0xd7, 0xb4, 0xce, 0x0b, 0x6a, 0x48, 0x54, 0x51, 0x57, 0x94, 0xb5, 0x6c, 0x20, 0x72, 0x62, 0xf7, 0xa5, 0xb9, 0xbd, 0xb5, 0x66, + 0x1e, 0xe6, 0x72, 0x8d, 0xa6, 0x0f, 0xbd, 0x8d, 0x7d, 0x55, 0xb2, 0xb1, 0xad, 0xe4, 0x87, 0xa3, 0xcf, 0x52, 0xa2, 0xc3, 0xac, 0x6f, 0x52, 0x0d, 0xb1, 0x2c, 0x27, 0xd8, 0x92, 0x1f, 0x6c, 0xab, + 0x69, 0x69, 0xfe, 0x2d, 0xdf, 0xb7, 0x58, 0x43, 0x8d, 0x48, 0x04, 0x9f, 0x30, 0x2f, 0xc2, 0x10, 0x8a, 0x4f, 0xcc, 0x93, 0xe3, 0x76, 0x69, 0x17, 0x0e, 0x6d, 0xb4, 0xb0, 0xb9, 0xb4, 0xc4, 0xcb, + 0x7f, 0x30, 0x48, 0xb4, 0xe9, 0x0d, 0x0c, 0xbe, 0x7a, 0x57, 0xc0, 0x39, 0x4f, 0x37, 0x33, 0x8a, 0x01, 0xfa, 0xe3, 0xad, 0xfd, 0xc0, 0xe5, 0x12, 0x6d, 0x86, 0x3a, 0x89, 0x5e, 0xb0, 0x4e, 0x02, + 0x1d, 0x29, 0x04, 0x43, 0xa4, 0xb5, 0x42, 0xaf, 0x04, 0xa8, 0x2f, 0x6b, 0x24, 0x94, 0xa6, 0xee, 0x7f, 0x20, 0xf2, 0x75, 0x4c, 0x58, 0xe0, 0x84, 0x90, 0x32, 0x48, 0x3a, 0x56, 0xe8, 0xe2, 0xef, + 0xc4, 0x3c, 0xc6, 0x56, 0x74, 0x36, 0xa8, 0x6a, 0xfb, 0xd6, 0xaa, 0x9e, 0xaa, 0x7c, 0x27, 0x6e, 0x98, 0x06, 0x83, 0x03, 0x34, 0xb6, 0x14, 0xb2, 0xbe, 0xe2, 0x3c, 0xc7, 0x66, 0x34, 0xf6, 0xfd, + 0x87, 0xbe, 0x24, 0x79, 0xc0, 0xc4, 0xe8, 0xed, 0xfd, 0xfa, 0xa5, 0x60, 0x3e, 0x93, 0xf4, 0x26, 0x5b, 0x3f, 0x82, 0x24, 0xc1, 0xc5, 0x94, 0x6f, 0xeb, 0x42, 0x48, 0x19, 0xd1, 0x89, 0x90, 0xa4, + 0xdd, 0x9d, 0x6a, 0x6d, 0x8e, 0x47, 0x46, 0x5c, 0xce, 0xac, 0x08, 0x77, 0xef, 0x88, 0x9b, 0x93, 0xe7, 0xeb, 0xa9, 0x79, 0x55, 0x7e, 0x39, 0x35, 0xd7, 0xf8, 0x6d, 0xce, 0x11, 0xb0, 0x70, 0xf3, + 0x75, 0xc6, 0xf2, 0xae, 0x49, 0xa2, 0x05, 0x21, 0xde, 0x97, 0x28, 0x5b, 0x43, 0x1e, 0x71, 0x71, 0x25, 0x84, 0x7f, 0xb8, 0x93, 0x5e, 0xd8, 0x4a, 0x61, 0xe7, 0xf8, 0xd3, 0x6a, 0x2c, 0x3d, 0x8e, +}; + + +// "cn/0" +const static uint8_t test_output_v0[160] = { + 0x1A, 0x3F, 0xFB, 0xEE, 0x90, 0x9B, 0x42, 0x0D, 0x91, 0xF7, 0xBE, 0x6E, 0x5F, 0xB5, 0x6D, 0xB7, + 0x1B, 0x31, 0x10, 0xD8, 0x86, 0x01, 0x1E, 0x87, 0x7E, 0xE5, 0x78, 0x6A, 0xFD, 0x08, 0x01, 0x00, + 0x1B, 0x60, 0x6A, 0x3F, 0x4A, 0x07, 0xD6, 0x48, 0x9A, 0x1B, 0xCD, 0x07, 0x69, 0x7B, 0xD1, 0x66, + 0x96, 0xB6, 0x1C, 0x8A, 0xE9, 0x82, 0xF6, 0x1A, 0x90, 0x16, 0x0F, 0x4E, 0x52, 0x82, 0x8A, 0x7F, + 0xA1, 0xB4, 0xFA, 0xE3, 0xE5, 0x76, 0xCE, 0xCF, 0xB7, 0x9C, 0xAF, 0x3E, 0x29, 0x92, 0xE4, 0xE0, + 0x31, 0x24, 0x05, 0x48, 0xBF, 0x8D, 0x5F, 0x7B, 0x11, 0x03, 0x60, 0xAA, 0xD7, 0x50, 0x3F, 0x0C, + 0x2D, 0x30, 0xF3, 0x87, 0x4F, 0x86, 0xA1, 0x4A, 0xB5, 0xA2, 0x1A, 0x08, 0xD0, 0x44, 0x2C, 0x9D, + 0x16, 0xE9, 0x28, 0x49, 0xA1, 0xFF, 0x85, 0x6F, 0x12, 0xBB, 0x7D, 0xAB, 0x11, 0x1C, 0xE7, 0xF7, + 0x2D, 0x9D, 0x19, 0xE4, 0xD2, 0x26, 0x44, 0x1E, 0xCD, 0x22, 0x08, 0x24, 0xA8, 0x97, 0x46, 0x62, + 0x04, 0x84, 0x90, 0x4A, 0xEE, 0x99, 0x14, 0xED, 0xB8, 0xC6, 0x0D, 0x37, 0xA1, 0x66, 0x17, 0xB0 +}; + + +// "cn/1" Cryptonight variant 1 (Monero v7) +const static uint8_t test_output_v1[160] = { + 0xF2, 0x2D, 0x3D, 0x62, 0x03, 0xD2, 0xA0, 0x8B, 0x41, 0xD9, 0x02, 0x72, 0x78, 0xD8, 0xBC, 0xC9, + 0x83, 0xAC, 0xAD, 0xA9, 0xB6, 0x8E, 0x52, 0xE3, 0xC6, 0x89, 0x69, 0x2A, 0x50, 0xE9, 0x21, 0xD9, + 0xC9, 0xFA, 0xE8, 0x42, 0x5D, 0x86, 0x88, 0xDC, 0x23, 0x6B, 0xCD, 0xBC, 0x42, 0xFD, 0xB4, 0x2D, + 0x37, 0x6C, 0x6E, 0xC1, 0x90, 0x50, 0x1A, 0xA8, 0x4B, 0x04, 0xA4, 0xB4, 0xCF, 0x1E, 0xE1, 0x22, + 0xE7, 0x8C, 0x5A, 0x6E, 0x38, 0x30, 0x68, 0x4A, 0x73, 0xFC, 0x1B, 0xC6, 0x6D, 0xFC, 0x8D, 0x98, + 0xB4, 0xC2, 0x23, 0x39, 0xAD, 0xE0, 0x9D, 0xF6, 0x6D, 0x8C, 0x6A, 0xAA, 0xF9, 0xB2, 0xE3, 0x4C, + 0xB6, 0x90, 0x6C, 0xE6, 0x15, 0x5E, 0x46, 0x07, 0x9C, 0xB2, 0x6B, 0xAC, 0x3B, 0xAC, 0x1A, 0xDE, + 0x92, 0x2C, 0xD6, 0x0C, 0x46, 0x9D, 0x9B, 0xC2, 0x84, 0x52, 0x65, 0xF6, 0xBD, 0xFA, 0x0D, 0x74, + 0x00, 0x66, 0x10, 0x07, 0xF1, 0x19, 0x06, 0x3A, 0x6C, 0xFF, 0xEE, 0xB2, 0x40, 0xE5, 0x88, 0x2B, + 0x6C, 0xAB, 0x6B, 0x1D, 0x88, 0xB8, 0x44, 0x25, 0xF4, 0xEA, 0xB7, 0xEC, 0xBA, 0x12, 0x8A, 0x24 +}; + + +// "cn/2" Cryptonight variant 2 (Monero v8) +const static uint8_t test_output_v2[160] = { + 0x97, 0x37, 0x82, 0x82, 0xCF, 0x10, 0xE7, 0xAD, 0x03, 0x3F, 0x7B, 0x80, 0x74, 0xC4, 0x0E, 0x14, + 0xD0, 0x6E, 0x7F, 0x60, 0x9D, 0xDD, 0xDA, 0x78, 0x76, 0x80, 0xB5, 0x8C, 0x05, 0xF4, 0x3D, 0x21, + 0x87, 0x1F, 0xCD, 0x68, 0x23, 0xF6, 0xA8, 0x79, 0xBB, 0x3F, 0x33, 0x95, 0x1C, 0x8E, 0x8E, 0x89, + 0x1D, 0x40, 0x43, 0x88, 0x0B, 0x02, 0xDF, 0xA1, 0xBB, 0x3B, 0xE4, 0x98, 0xB5, 0x0E, 0x75, 0x78, + 0xE6, 0x0D, 0x24, 0x0F, 0x65, 0x85, 0x60, 0x3A, 0x4A, 0xE5, 0x5F, 0x54, 0x9B, 0xC8, 0x79, 0x93, + 0xEB, 0x3D, 0x98, 0x2C, 0xFE, 0x9B, 0xFB, 0x15, 0xB6, 0x88, 0x21, 0x94, 0xB0, 0x05, 0x86, 0x5C, + 0x59, 0x8B, 0x93, 0x7A, 0xDA, 0xD2, 0xA2, 0x14, 0xED, 0xB7, 0xC4, 0x5D, 0xA1, 0xEF, 0x26, 0xF3, + 0xC7, 0x73, 0x29, 0x4D, 0xF1, 0xC8, 0x2C, 0xE0, 0xD0, 0xE9, 0xED, 0x0C, 0x70, 0x75, 0x05, 0x3E, + 0x5B, 0xF6, 0xA0, 0x6E, 0xEA, 0xDE, 0x87, 0x0B, 0x06, 0x29, 0x03, 0xBF, 0xB4, 0x85, 0x9D, 0x04, + 0x75, 0x1A, 0xCD, 0x1E, 0xD6, 0xAA, 0x1B, 0x05, 0x24, 0x6A, 0x2C, 0x80, 0x69, 0x68, 0xDC, 0x97 +}; + + +// "cn/half" +const static uint8_t test_output_half[160] = { + 0x5D, 0x4F, 0xBC, 0x35, 0x60, 0x97, 0xEA, 0x64, 0x40, 0xB0, 0x88, 0x8E, 0xDE, 0xB6, 0x35, 0xDD, + 0xC8, 0x4A, 0x0E, 0x39, 0x7C, 0x86, 0x84, 0x56, 0x89, 0x5C, 0x3F, 0x29, 0xBE, 0x73, 0x12, 0xA7, + 0x02, 0xE6, 0x1D, 0x2B, 0xBC, 0x84, 0xB6, 0x71, 0x96, 0x71, 0xD5, 0x0C, 0xAC, 0x76, 0x0E, 0x6B, + 0xF1, 0xF0, 0x55, 0x34, 0x15, 0x29, 0x93, 0x04, 0x2D, 0xED, 0xD2, 0x33, 0x50, 0x6E, 0xBE, 0x25, + 0xD0, 0xFD, 0x8E, 0xC6, 0x15, 0xD5, 0x12, 0x53, 0x7B, 0x26, 0xF6, 0x01, 0xA5, 0xA8, 0xBE, 0x7C, + 0xCF, 0x5E, 0x19, 0xB7, 0x63, 0x0D, 0x0F, 0x02, 0x2B, 0xD7, 0xC4, 0x8C, 0x12, 0x24, 0x80, 0x02, + 0xE7, 0xB7, 0xA0, 0x4F, 0x94, 0xF9, 0x46, 0xB5, 0x18, 0x64, 0x7E, 0x4E, 0x9C, 0x81, 0x6C, 0x60, + 0x7D, 0x2E, 0xEA, 0xCF, 0x90, 0xCB, 0x68, 0x09, 0xC9, 0x53, 0xF6, 0xA9, 0xCA, 0x0C, 0xAC, 0xDC, + 0xFD, 0x07, 0xDA, 0x24, 0x1D, 0xD1, 0x35, 0x32, 0x3C, 0xE8, 0x64, 0x44, 0x5E, 0xCB, 0xB5, 0x00, + 0x69, 0xF4, 0x6F, 0xBB, 0x62, 0x0D, 0x25, 0xD8, 0xAC, 0x20, 0x90, 0xC5, 0x1B, 0xD3, 0x5F, 0xCA +}; + + +// "cn/msr" Masari (MSR) +const static uint8_t test_output_msr[160] = { + 0x3C, 0x7A, 0x61, 0x08, 0x4C, 0x5E, 0xB8, 0x65, 0xB4, 0x98, 0xAB, 0x2F, 0x5A, 0x1A, 0xC5, 0x2C, + 0x49, 0xC1, 0x77, 0xC2, 0xD0, 0x13, 0x34, 0x42, 0xD6, 0x5E, 0xD5, 0x14, 0x33, 0x5C, 0x82, 0xC5, + 0x69, 0xDF, 0x38, 0x51, 0x1B, 0xB3, 0xEB, 0x7D, 0xE7, 0x6B, 0x08, 0x8E, 0xB6, 0x7E, 0xB7, 0x1C, + 0x5F, 0x3C, 0x81, 0xC9, 0xF7, 0xCE, 0xAE, 0x28, 0xC0, 0xFE, 0xEB, 0xBA, 0x0B, 0x40, 0x38, 0x1D, + 0x44, 0xD0, 0xD5, 0xD3, 0x98, 0x1F, 0xA3, 0x0E, 0xE9, 0x89, 0x1A, 0xD7, 0x88, 0xCC, 0x25, 0x76, + 0x9C, 0xFF, 0x4D, 0x7F, 0x9C, 0xCF, 0x48, 0x07, 0x91, 0xF9, 0x82, 0xF5, 0x4C, 0xE9, 0xBD, 0x82, + 0x36, 0x36, 0x64, 0x14, 0xED, 0xB8, 0x54, 0xEE, 0x22, 0xA1, 0x66, 0xA3, 0x87, 0x10, 0x76, 0x1F, + 0x5A, 0xCD, 0x4C, 0x31, 0x4C, 0xBA, 0x41, 0xD2, 0xDB, 0x6C, 0x31, 0x2E, 0x7A, 0x64, 0x15, 0xFF, + 0xA6, 0xD9, 0xB9, 0x7D, 0x1C, 0x3C, 0x98, 0xDD, 0x16, 0xE6, 0xD3, 0xAA, 0xEF, 0xB6, 0xB3, 0x53, + 0x74, 0xD1, 0xAC, 0x5C, 0x04, 0x26, 0x7D, 0x71, 0xDE, 0xAB, 0x66, 0x28, 0x91, 0x3A, 0x6F, 0x4F +}; + + +// "cn/xao" Alloy (XAO) +const static uint8_t test_output_xao[160] = { + 0x9A, 0x29, 0xD0, 0xC4, 0xAF, 0xDC, 0x63, 0x9B, 0x65, 0x53, 0xB1, 0xC8, 0x37, 0x35, 0x11, 0x4C, + 0x5D, 0x77, 0x16, 0x21, 0x42, 0x97, 0x5C, 0xB8, 0x50, 0xC0, 0xA5, 0x1F, 0x64, 0x07, 0xBD, 0x33, + 0xF1, 0xC9, 0x98, 0x40, 0x42, 0xDE, 0x39, 0xD1, 0xBA, 0x2D, 0xAD, 0xEC, 0xFE, 0xEA, 0xD8, 0x46, + 0x56, 0x1C, 0x32, 0x90, 0x42, 0x63, 0x10, 0x80, 0xD7, 0x01, 0xE4, 0xE6, 0x20, 0xB3, 0x60, 0x45, + 0x05, 0xE5, 0xC2, 0x18, 0xCD, 0x07, 0xA4, 0x40, 0x42, 0x91, 0xE2, 0xA4, 0x52, 0x54, 0x79, 0xBA, + 0xCD, 0x7E, 0x61, 0x2D, 0x7F, 0x7E, 0x69, 0x5E, 0xD7, 0xC0, 0x06, 0x65, 0xD7, 0xA1, 0xB8, 0xB8, + 0x1E, 0x31, 0x1C, 0xD3, 0xB7, 0xBC, 0x78, 0x3C, 0x01, 0xAF, 0x77, 0xAA, 0xF3, 0x0F, 0x4C, 0xF2, + 0xD1, 0x8B, 0x58, 0xC7, 0xEB, 0x99, 0x91, 0x53, 0x43, 0x71, 0x47, 0x99, 0x9E, 0x04, 0xA4, 0xEA, + 0xB8, 0xA3, 0xB0, 0x9E, 0x09, 0xF5, 0x57, 0x5C, 0xCF, 0x8A, 0xC6, 0xCA, 0x88, 0x51, 0x9A, 0x01, + 0x31, 0xCC, 0x0C, 0xA6, 0x53, 0xB5, 0x5F, 0xFD, 0x7D, 0x29, 0x3A, 0x35, 0xE9, 0x0E, 0x25, 0x6C +}; + + +// "cn/rto" Arto (RTO) +const static uint8_t test_output_rto[160] = { + 0x82, 0x66, 0x1E, 0x1C, 0x6E, 0x64, 0x36, 0x66, 0x84, 0x06, 0x32, 0x7A, 0x9B, 0xB1, 0x13, 0x19, + 0xA5, 0x56, 0x16, 0x15, 0xDF, 0xEC, 0x1C, 0x9E, 0xE3, 0x88, 0x4A, 0x6C, 0x1C, 0xEB, 0x76, 0xA5, + 0xB3, 0xFB, 0xF4, 0x3F, 0x2B, 0x6A, 0x3A, 0x39, 0xA3, 0x6E, 0x08, 0x33, 0x67, 0x90, 0x31, 0xB9, + 0x3F, 0x27, 0xE4, 0x79, 0x32, 0x61, 0x6B, 0x5C, 0x8A, 0xF8, 0xAF, 0xC0, 0x60, 0xFD, 0x83, 0xB7, + 0x11, 0x11, 0x89, 0xB4, 0xDC, 0xAE, 0x40, 0xC8, 0x64, 0xAA, 0x4D, 0x19, 0x23, 0x7B, 0xD3, 0x27, + 0xB2, 0x0F, 0xA7, 0x50, 0x7D, 0xCA, 0xF5, 0x03, 0x06, 0xB2, 0x26, 0x62, 0xF3, 0x68, 0x2D, 0x30, + 0x6F, 0x93, 0x1E, 0xFF, 0xCD, 0x85, 0x40, 0x28, 0x5F, 0xC3, 0x8C, 0x76, 0x51, 0x9E, 0xD5, 0x06, + 0x32, 0xD6, 0x35, 0x83, 0xF6, 0x3B, 0x54, 0x4F, 0xA1, 0x9C, 0x13, 0xD8, 0xC4, 0x0E, 0x01, 0x2F, + 0x29, 0xDB, 0x8C, 0x1C, 0xB7, 0x06, 0x86, 0x79, 0x6D, 0xFF, 0x9F, 0x89, 0x3B, 0x3A, 0xA5, 0x79, + 0xE7, 0x81, 0x4E, 0x2A, 0xBD, 0x62, 0xC1, 0x1B, 0x7C, 0xB9, 0x33, 0x7B, 0xEE, 0x95, 0x80, 0xB3 +}; + +// "cn/rwz" +const static uint8_t test_output_rwz[160] = { + 0x5f, 0x56, 0xc6, 0xb0, 0x99, 0x6b, 0xa2, 0x3e, 0x0b, 0xba, 0x07, 0x29, 0xc9, 0x90, 0x74, 0x85, + 0x5a, 0x10, 0xe3, 0x08, 0x7f, 0xdb, 0xfe, 0x94, 0x75, 0x33, 0x54, 0x73, 0x76, 0xf0, 0x75, 0xb8, + 0x8b, 0x70, 0x43, 0x9a, 0xfc, 0xf5, 0xeb, 0x15, 0xbb, 0xf9, 0xad, 0x9d, 0x2a, 0xbd, 0x72, 0x52, + 0x49, 0x54, 0x0b, 0x91, 0xea, 0x61, 0x7f, 0x98, 0x7d, 0x39, 0x17, 0xb7, 0xd7, 0x65, 0xff, 0x75, + 0x13, 0x21, 0x1d, 0xce, 0x61, 0x5a, 0xdc, 0x5f, 0x8c, 0xcb, 0x1f, 0x6f, 0xbb, 0x92, 0x88, 0xc3, + 0xe3, 0xe2, 0xfc, 0x4f, 0x62, 0xfb, 0xf0, 0x48, 0x02, 0x01, 0xd3, 0xbe, 0x77, 0x6a, 0x40, 0xca, + 0x9a, 0xe9, 0xba, 0x0c, 0xc0, 0x2b, 0x11, 0xf6, 0x9b, 0xee, 0x24, 0x3a, 0xd8, 0x86, 0x18, 0xd0, + 0xe8, 0xeb, 0xcb, 0x38, 0x2c, 0xf5, 0x99, 0x83, 0x14, 0x7b, 0x0c, 0x20, 0xbe, 0x50, 0xf4, 0x87, + 0x83, 0x41, 0x75, 0xd8, 0xd1, 0xdd, 0x4b, 0x73, 0xb3, 0x92, 0x8f, 0xe6, 0x1c, 0x72, 0x70, 0xf5, + 0x7c, 0xf6, 0x23, 0x3a, 0xb4, 0x5f, 0xdf, 0xde, 0xa6, 0x5a, 0x58, 0xec, 0x13, 0x5a, 0x23, 0x2f +}; + +// "cn/zls" +const static uint8_t test_output_zls[160] = { + 0x51, 0x6E, 0x33, 0xC6, 0xE4, 0x46, 0xAB, 0xBC, 0xCD, 0xAD, 0x18, 0xC0, 0x4C, 0xD9, 0xA2, 0x5E, + 0x64, 0x10, 0x28, 0x53, 0xB2, 0x0A, 0x42, 0xDF, 0xDE, 0xAA, 0x8B, 0x59, 0x9E, 0xCF, 0x40, 0xE2, + 0x0D, 0x62, 0x5B, 0x42, 0x18, 0xE2, 0x76, 0xAD, 0xD0, 0x74, 0x90, 0x60, 0x8D, 0xC4, 0xC7, 0x80, + 0x17, 0xB5, 0x1B, 0x25, 0x31, 0x39, 0x87, 0xD2, 0x2D, 0x6A, 0x9D, 0x1C, 0x74, 0xF4, 0x43, 0x22, + 0x4B, 0x97, 0x1F, 0x6A, 0xD0, 0xBE, 0x00, 0x74, 0xEC, 0xC5, 0xD8, 0x3B, 0xE6, 0xF4, 0x03, 0x8A, + 0x7B, 0xBA, 0x80, 0xCC, 0x9F, 0x00, 0xCB, 0xC2, 0x14, 0x8F, 0xF3, 0xD8, 0x92, 0x73, 0xBF, 0x17, + 0x3D, 0x9B, 0x22, 0xA3, 0x61, 0x94, 0x41, 0x9E, 0xF9, 0x68, 0x1D, 0x42, 0x48, 0x3B, 0x39, 0x45, + 0xE2, 0xE6, 0x16, 0x84, 0xFC, 0x21, 0xE6, 0xDA, 0x38, 0x7F, 0x17, 0xAB, 0xD3, 0xF2, 0xCE, 0x1A, + 0x2F, 0x35, 0xD5, 0x74, 0xFA, 0x45, 0x3B, 0x06, 0xD1, 0x4E, 0x84, 0x3A, 0x5D, 0xE3, 0x0E, 0xA5, + 0x00, 0x08, 0x64, 0xF0, 0xA6, 0xC8, 0x94, 0x45, 0x08, 0xED, 0x03, 0x95, 0x52, 0xE9, 0xBC, 0x5F +}; + +// "cn/double" +const static uint8_t test_output_double[160] = { + 0xAE, 0xFB, 0xB3, 0xF0, 0xCC, 0x88, 0x04, 0x6D, 0x11, 0x9F, 0x6C, 0x54, 0xB9, 0x6D, 0x90, 0xC9, + 0xE8, 0x84, 0xEA, 0x3B, 0x59, 0x83, 0xA6, 0x0D, 0x50, 0xA4, 0x2D, 0x7D, 0x3E, 0xBE, 0x48, 0x21, + 0x49, 0xCE, 0x8E, 0xF3, 0xBC, 0x8A, 0x36, 0xBF, 0x86, 0x37, 0x89, 0x55, 0x09, 0xBA, 0x22, 0xF8, + 0xEB, 0x3A, 0xE1, 0xDC, 0x91, 0xF7, 0x62, 0x4B, 0x9F, 0x48, 0xE6, 0x92, 0xBD, 0xE4, 0x5D, 0xC1, + 0xF1, 0x3C, 0x63, 0x1D, 0xEB, 0x0B, 0x04, 0xA3, 0x30, 0xD5, 0x11, 0x15, 0x4C, 0xCE, 0xEF, 0x4F, + 0xDF, 0x69, 0xE3, 0x9E, 0xD2, 0x68, 0xFC, 0x1B, 0x6F, 0xE8, 0x08, 0x9C, 0xBB, 0xA5, 0x2B, 0x60, + 0x52, 0x0F, 0xE5, 0xD2, 0xF3, 0x8A, 0xB3, 0xE1, 0x76, 0x7F, 0x44, 0x25, 0x76, 0xEC, 0xFF, 0xA2, + 0x0C, 0x64, 0xD0, 0x0E, 0x32, 0x33, 0x28, 0x20, 0x73, 0xE0, 0x31, 0x66, 0x4E, 0x54, 0x83, 0x49, + 0x51, 0x55, 0x4D, 0x2E, 0x22, 0xB7, 0x51, 0x09, 0x73, 0x61, 0x7E, 0x6A, 0x57, 0x0B, 0x28, 0x3C, + 0x5E, 0x2E, 0xC1, 0x80, 0x89, 0x39, 0xB3, 0x54, 0x39, 0x52, 0x0E, 0x69, 0x3D, 0xF6, 0xC5, 0x4A +}; + +// "cn/conceal" +const static uint8_t test_output_conceal[160] = { + 0xB3, 0xA1, 0x67, 0x86, 0xD2, 0xC9, 0x85, 0xEC, 0xAD, 0xC4, 0x5F, 0x91, 0x05, 0x27, 0xC7, 0xA1, + 0x96, 0xF0, 0xE1, 0xE9, 0x7C, 0x87, 0x09, 0x38, 0x1D, 0x7D, 0x41, 0x93, 0x35, 0xF8, 0x16, 0x72, + 0xC3, 0xBD, 0x8D, 0xE8, 0xD5, 0xAE, 0xB8, 0x59, 0x0A, 0x6C, 0xCB, 0x7B, 0x41, 0x30, 0xF7, 0x04, + 0xA5, 0x7C, 0xF9, 0xCA, 0x20, 0x49, 0x9C, 0xFD, 0xE8, 0x43, 0xCF, 0x66, 0x78, 0xEA, 0x76, 0xDD, + 0x91, 0x0C, 0xDE, 0x29, 0x2A, 0xE0, 0xA8, 0xCA, 0xBC, 0xAA, 0x53, 0x4C, 0x93, 0x3E, 0x7B, 0x2C, + 0xF1, 0xF9, 0xE1, 0x98, 0xB2, 0x92, 0x1E, 0x19, 0x93, 0x2A, 0x74, 0x9D, 0xDB, 0x10, 0x0F, 0x16, + 0xD5, 0x3D, 0xE4, 0xC4, 0x23, 0xD9, 0x2E, 0xFD, 0x79, 0x8D, 0x1E, 0x48, 0x4E, 0x46, 0x08, 0x6C, + 0xFF, 0x8A, 0x49, 0xFA, 0x1E, 0xB0, 0xB6, 0x9A, 0x47, 0x1C, 0xC6, 0x30, 0x36, 0x5D, 0xFD, 0x76, + 0x10, 0x07, 0x44, 0xE6, 0xC8, 0x20, 0x2A, 0x84, 0x9D, 0x70, 0x22, 0x00, 0x8B, 0x9B, 0xBD, 0x8D, + 0x27, 0x49, 0xA6, 0x06, 0xDC, 0xF0, 0xA1, 0x4B, 0x50, 0xA0, 0x12, 0xCD, 0x77, 0x01, 0x4C, 0x28 +}; + +#ifdef XMRIG_ALGO_CN_LITE +// "cn-lite/0" +const static uint8_t test_output_v0_lite[160] = { + 0x36, 0x95, 0xB4, 0xB5, 0x3B, 0xB0, 0x03, 0x58, 0xB0, 0xAD, 0x38, 0xDC, 0x16, 0x0F, 0xEB, 0x9E, + 0x00, 0x4E, 0xEC, 0xE0, 0x9B, 0x83, 0xA7, 0x2E, 0xF6, 0xBA, 0x98, 0x64, 0xD3, 0x51, 0x0C, 0x88, + 0x28, 0xA2, 0x2B, 0xAD, 0x3F, 0x93, 0xD1, 0x40, 0x8F, 0xCA, 0x47, 0x2E, 0xB5, 0xAD, 0x1C, 0xBE, + 0x75, 0xF2, 0x1D, 0x05, 0x3C, 0x8C, 0xE5, 0xB3, 0xAF, 0x10, 0x5A, 0x57, 0x71, 0x3E, 0x21, 0xDD, + 0x38, 0x08, 0xE1, 0x17, 0x0B, 0x99, 0x8D, 0x1A, 0x3C, 0xCE, 0x35, 0xC5, 0xC7, 0x3A, 0x00, 0x2E, + 0xCB, 0x54, 0xF0, 0x78, 0x2E, 0x9E, 0xDB, 0xC7, 0xDF, 0x2E, 0x71, 0x9A, 0x16, 0x97, 0xC4, 0x18, + 0x4B, 0x97, 0x07, 0xFE, 0x5D, 0x98, 0x9A, 0xD6, 0xD8, 0xE5, 0x92, 0x66, 0x87, 0x7F, 0x19, 0x37, + 0xA2, 0x5E, 0xE6, 0x96, 0xB5, 0x97, 0x33, 0x89, 0xE0, 0xA7, 0xC9, 0xDD, 0x4A, 0x7E, 0x9E, 0x53, + 0xBE, 0x91, 0x2B, 0xF5, 0xF5, 0xAF, 0xDD, 0x09, 0xA2, 0xF4, 0xA4, 0x56, 0xEB, 0x96, 0x22, 0xC9, + 0x94, 0xFB, 0x7B, 0x28, 0xC9, 0x97, 0x65, 0x04, 0xAC, 0x4F, 0x84, 0x71, 0xDA, 0x6E, 0xD8, 0xC5 +}; + + +// "cn-lite/1" AEON v7 +const static uint8_t test_output_v1_lite[160] = { + 0x6D, 0x8C, 0xDC, 0x44, 0x4E, 0x9B, 0xBB, 0xFD, 0x68, 0xFC, 0x43, 0xFC, 0xD4, 0x85, 0x5B, 0x22, + 0x8C, 0x8A, 0x1B, 0xD9, 0x1D, 0x9D, 0x00, 0x28, 0x5B, 0xEC, 0x02, 0xB7, 0xCA, 0x2D, 0x67, 0x41, + 0x87, 0xC4, 0xE5, 0x70, 0x65, 0x3E, 0xB4, 0xC2, 0xB4, 0x2B, 0x7A, 0x0D, 0x54, 0x65, 0x59, 0x45, + 0x2D, 0xFA, 0xB5, 0x73, 0xB8, 0x2E, 0xC5, 0x2F, 0x15, 0x2B, 0x7F, 0xF9, 0x8E, 0x79, 0x44, 0x6F, + 0x16, 0x08, 0x74, 0xC7, 0xA2, 0xD2, 0xA3, 0x97, 0x95, 0x76, 0xCA, 0x4D, 0x06, 0x39, 0x7A, 0xAB, + 0x6C, 0x87, 0x58, 0x33, 0x4D, 0xC8, 0x5A, 0xAB, 0x04, 0x27, 0xFE, 0x8B, 0x1C, 0x23, 0x2F, 0x32, + 0xC0, 0x44, 0xFF, 0x0D, 0xB5, 0x3B, 0x27, 0x96, 0x06, 0x89, 0x7B, 0xA3, 0x0B, 0xD0, 0xCE, 0x9E, + 0x90, 0x22, 0x77, 0x5A, 0xAD, 0xA1, 0xE5, 0xB6, 0xFC, 0xCB, 0x39, 0x7E, 0x2B, 0x10, 0xEE, 0xB4, + 0x8C, 0x2B, 0xA4, 0x1F, 0x60, 0x76, 0x39, 0xD7, 0xF6, 0x46, 0x77, 0x18, 0x20, 0xAD, 0xD4, 0xC9, + 0x87, 0xF7, 0x37, 0xDA, 0xFD, 0xBA, 0xBA, 0xD2, 0xF2, 0x68, 0xDC, 0x26, 0x8D, 0x1B, 0x08, 0xC6 +}; +#endif + + +#ifdef XMRIG_ALGO_CN_HEAVY +// "cn-heavy/0" +const static uint8_t test_output_v0_heavy[160] = { + 0x99, 0x83, 0xF2, 0x1B, 0xDF, 0x20, 0x10, 0xA8, 0xD7, 0x07, 0xBB, 0x2F, 0x14, 0xD7, 0x86, 0x64, + 0xBB, 0xE1, 0x18, 0x7F, 0x55, 0x01, 0x4B, 0x39, 0xE5, 0xF3, 0xD6, 0x93, 0x28, 0xE4, 0x8F, 0xC2, + 0x4D, 0x94, 0x7D, 0xD6, 0xDB, 0x6E, 0x07, 0x48, 0x26, 0x4A, 0x51, 0x2E, 0xAC, 0xF3, 0x25, 0x4A, + 0x1F, 0x1A, 0xA2, 0x5B, 0xFC, 0x0A, 0xAD, 0x82, 0xDE, 0xA8, 0x99, 0x96, 0x88, 0x52, 0xD2, 0x7D, + 0x3E, 0xE1, 0x23, 0x03, 0x5A, 0x63, 0x7B, 0x66, 0xF6, 0xD7, 0xC2, 0x2A, 0x34, 0x5E, 0x88, 0xE7, + 0xFA, 0xC4, 0x25, 0x36, 0x54, 0xCB, 0xD2, 0x5C, 0x2F, 0x80, 0x2A, 0xF9, 0xCC, 0x43, 0xF7, 0xCD, + 0xE5, 0x18, 0xA8, 0x05, 0x60, 0x18, 0xA5, 0x73, 0x72, 0x9B, 0x32, 0xDC, 0x69, 0x83, 0xC1, 0xE1, + 0x1F, 0xDB, 0xDA, 0x6B, 0xAC, 0xEC, 0x9F, 0x67, 0xF8, 0x27, 0x1D, 0xC7, 0xE6, 0x46, 0x42, 0xF9, + 0x53, 0x62, 0x0A, 0x54, 0x7D, 0x43, 0xEA, 0x18, 0x94, 0xED, 0xD8, 0x92, 0x06, 0x6A, 0xA1, 0x51, + 0xAD, 0xB1, 0xFD, 0x89, 0xFB, 0x5C, 0xB4, 0x25, 0x6A, 0xDD, 0xB0, 0x09, 0xC5, 0x72, 0x87, 0xEB +}; + + +// "cn-heavy/xhv" +const static uint8_t test_output_xhv_heavy[160] = { + 0x5A, 0xC3, 0xF7, 0x85, 0xC4, 0x90, 0xC5, 0x85, 0x50, 0xEC, 0x95, 0xD2, 0x72, 0x65, 0x63, 0x57, + 0x7E, 0x7C, 0x1C, 0x21, 0x2D, 0x0C, 0xDE, 0x59, 0x12, 0x73, 0x20, 0x1E, 0x44, 0xFD, 0xD5, 0xB6, + 0x1F, 0x4E, 0xB2, 0x0A, 0x36, 0x51, 0x4B, 0xF5, 0x4D, 0xC9, 0xE0, 0x90, 0x2C, 0x16, 0x47, 0x3F, + 0xDE, 0x18, 0x29, 0x8E, 0xBB, 0x34, 0x2B, 0xEF, 0x7A, 0x04, 0x22, 0xD1, 0xB1, 0xF2, 0x48, 0xDA, + 0xE3, 0x7F, 0x4B, 0x4C, 0xB4, 0xDF, 0xE8, 0xD3, 0x70, 0xE2, 0xE7, 0x44, 0x25, 0x87, 0x12, 0xF9, + 0x8F, 0x28, 0x0B, 0xCE, 0x2C, 0xEE, 0xDD, 0x88, 0x94, 0x35, 0x48, 0x51, 0xAE, 0xC8, 0x9C, 0x0B, + 0xED, 0x2F, 0xE6, 0x0F, 0x39, 0x05, 0xB4, 0x4A, 0x8F, 0x38, 0x44, 0x2D, 0x4B, 0xE9, 0x7B, 0x81, + 0xC6, 0xB0, 0xE0, 0x0A, 0x39, 0x8C, 0x38, 0xFE, 0x63, 0x31, 0x47, 0x65, 0x0D, 0x2B, 0xF4, 0x96, + 0x13, 0x91, 0x89, 0xB4, 0x5B, 0xA9, 0x2A, 0x7A, 0x09, 0x65, 0x14, 0x20, 0x76, 0x24, 0x6C, 0x80, + 0x1D, 0x3F, 0x9F, 0xCD, 0x68, 0x39, 0xA9, 0x42, 0x27, 0xC1, 0x0C, 0x53, 0x98, 0x35, 0x60, 0x7A +}; + + +// "cn-heavy/tube" +const static uint8_t test_output_tube_heavy[160] = { + 0xFE, 0x53, 0x35, 0x20, 0x76, 0xEA, 0xE6, 0x89, 0xFA, 0x3B, 0x4F, 0xDA, 0x61, 0x46, 0x34, 0xCF, + 0xC3, 0x12, 0xEE, 0x0C, 0x38, 0x7D, 0xF2, 0xB8, 0xB7, 0x4D, 0xA2, 0xA1, 0x59, 0x74, 0x12, 0x35, + 0xCD, 0x3F, 0x29, 0xDF, 0x07, 0x4A, 0x14, 0xAD, 0x0B, 0x98, 0x99, 0x37, 0xCA, 0x14, 0x68, 0xA3, + 0x8D, 0xAE, 0x86, 0xC1, 0xA3, 0x54, 0x05, 0xBE, 0xEA, 0x6D, 0x29, 0x24, 0x0C, 0x82, 0x97, 0x74, + 0xA0, 0x64, 0x77, 0xCD, 0x8D, 0x8A, 0xC3, 0x10, 0xB4, 0x89, 0x0E, 0xBB, 0x7D, 0xE6, 0x32, 0x8F, + 0xF4, 0x2D, 0xB6, 0x9E, 0x8A, 0xF9, 0xF8, 0xEE, 0x2C, 0xD0, 0x74, 0xED, 0xA9, 0xAA, 0xA1, 0xFB, + 0xE2, 0xC9, 0x89, 0x66, 0xD6, 0x66, 0x52, 0xA2, 0x16, 0xDA, 0x36, 0xA0, 0x10, 0x62, 0xD2, 0xB1, + 0x76, 0xD1, 0x31, 0xE9, 0x1C, 0x08, 0xB6, 0xCA, 0xAF, 0x89, 0xB9, 0x3D, 0x2C, 0xFA, 0x9A, 0x30, + 0x74, 0x6A, 0x96, 0xA1, 0x95, 0x6C, 0xBB, 0x46, 0x4D, 0xE0, 0xEB, 0x28, 0xBE, 0x2A, 0x8C, 0x34, + 0x57, 0x79, 0xBE, 0x52, 0xFB, 0xBC, 0x68, 0x43, 0x45, 0xF4, 0xDF, 0xA5, 0xA8, 0xFD, 0x55, 0xA6 +}; +#endif + + +#ifdef XMRIG_ALGO_CN_PICO +// "cn-pico/trtl" +const static uint8_t test_output_pico_trtl[160] = { + 0x08, 0xF4, 0x21, 0xD7, 0x83, 0x31, 0x17, 0x30, 0x0E, 0xDA, 0x66, 0xE9, 0x8F, 0x4A, 0x25, 0x69, + 0x09, 0x3D, 0xF3, 0x00, 0x50, 0x01, 0x73, 0x94, 0x4E, 0xFC, 0x40, 0x1E, 0x9A, 0x4A, 0x17, 0xAF, + 0xB2, 0x17, 0x2E, 0xC9, 0x46, 0x6E, 0x1A, 0xEE, 0x70, 0xEC, 0x85, 0x72, 0xA1, 0x4C, 0x23, 0x3E, + 0xE3, 0x54, 0x58, 0x2B, 0xCB, 0x93, 0xF8, 0x69, 0xD4, 0x29, 0x74, 0x4D, 0xE5, 0x72, 0x6A, 0x26, + 0x4E, 0xFD, 0x28, 0xFC, 0xD3, 0x74, 0x8A, 0x83, 0xF3, 0xCA, 0x92, 0x84, 0xE7, 0x4E, 0x10, 0xC2, + 0x05, 0x62, 0xC7, 0xBE, 0x99, 0x73, 0xED, 0x90, 0xB5, 0x6F, 0xDA, 0x64, 0x71, 0x2D, 0x99, 0x39, + 0x29, 0xDB, 0x22, 0x2B, 0x97, 0xB6, 0x37, 0x0E, 0x9A, 0x03, 0x65, 0xCC, 0xF7, 0xD0, 0x9A, 0xB7, + 0x68, 0xCE, 0x07, 0x3E, 0x15, 0x40, 0x3C, 0xCE, 0x8C, 0x63, 0x16, 0x72, 0xB5, 0x74, 0x84, 0xF4, + 0xA1, 0xE7, 0x53, 0x85, 0xFB, 0x72, 0xDD, 0x75, 0x90, 0x39, 0xB2, 0x3D, 0xC3, 0x08, 0x2C, 0xD5, + 0x01, 0x08, 0x27, 0x75, 0x86, 0xB9, 0xBB, 0x9B, 0xDF, 0xEA, 0x49, 0xDE, 0x46, 0xCB, 0x83, 0x45 +}; +#endif + +#ifdef XMRIG_ALGO_CN_EXTREMELITE +// "cn-extremelite/upx2" +const static uint8_t test_output_extremelite_upx2[64] = { + 0xAA, 0xBB, 0xB8, 0xED, 0x14, 0xA8, 0x35, 0xFA, 0x22, 0xCF, 0xB1, 0xB5, 0xDE, 0xA8, 0x72, 0xB0, + 0xA1, 0xD6, 0xCB, 0xD8, 0x46, 0xF4, 0x39, 0x1C, 0x0F, 0x01, 0xF3, 0x87, 0x5E, 0x3A, 0x37, 0x61, + 0x38, 0x59, 0x15, 0x72, 0xF8, 0x20, 0xD4, 0xDE, 0x25, 0x3C, 0xF5, 0x5A, 0x21, 0x92, 0xB6, 0x22, + 0xB0, 0x28, 0x9E, 0x2E, 0x5C, 0x36, 0x16, 0xE6, 0x1E, 0x78, 0x7A, 0x8F, 0xE4, 0x62, 0xEC, 0x5A +}; +#endif + +#ifdef XMRIG_ALGO_CN_GPU +// "cn/gpu" +const static uint8_t test_output_gpu[160] = { + 0xE5, 0x5C, 0xB2, 0x3E, 0x51, 0x64, 0x9A, 0x59, 0xB1, 0x27, 0xB9, 0x6B, 0x51, 0x5F, 0x2B, 0xF7, + 0xBF, 0xEA, 0x19, 0x97, 0x41, 0xA0, 0x21, 0x6C, 0xF8, 0x38, 0xDE, 0xD0, 0x6E, 0xFF, 0x82, 0xDF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +#endif + + +#ifdef XMRIG_ALGO_ARGON2 +// "argon2/chukwa" +const static uint8_t argon2_chukwa_test_out[160] = { + 0xC1, 0x58, 0xA1, 0x05, 0xAE, 0x75, 0xC7, 0x56, 0x1C, 0xFD, 0x02, 0x90, 0x83, 0xA4, 0x7A, 0x87, + 0x65, 0x3D, 0x51, 0xF9, 0x14, 0x12, 0x8E, 0x21, 0xC1, 0x97, 0x1D, 0x8B, 0x10, 0xC4, 0x90, 0x34, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +// "argon2/wrkz" +const static uint8_t argon2_wrkz_test_out[160] = { + 0x35, 0xE0, 0x83, 0xD4, 0xB9, 0xC6, 0x4C, 0x2A, 0x68, 0x82, 0x0A, 0x43, 0x1F, 0x61, 0x31, 0x19, + 0x98, 0xA8, 0xCD, 0x18, 0x64, 0xDB, 0xA4, 0x07, 0x7E, 0x25, 0xB7, 0xF1, 0x21, 0xD5, 0x4B, 0xD1, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +#endif + + +} // namespace xmrig + + +#endif /* XMRIG_CRYPTONIGHT_TEST_H */ diff --git a/src/crypto/cn/CryptoNight_x86.h b/src/crypto/cn/CryptoNight_x86.h new file mode 100644 index 00000000..5db298b7 --- /dev/null +++ b/src/crypto/cn/CryptoNight_x86.h @@ -0,0 +1,1663 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2019 XMR-Stak , + * Copyright 2018 Lee Clagett + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_CRYPTONIGHT_X86_H +#define XMRIG_CRYPTONIGHT_X86_H + + +#ifdef __GNUC__ +# include +#else +# include +# define __restrict__ __restrict +#endif + + +#include "backend/cpu/Cpu.h" +#include "crypto/cn/CnAlgo.h" +#include "crypto/cn/CryptoNight_monero.h" +#include "crypto/cn/CryptoNight.h" +#include "crypto/cn/soft_aes.h" +#include "crypto/common/keccak.h" + + +extern "C" +{ +#include "crypto/cn/c_groestl.h" +#include "crypto/cn/c_blake256.h" +#include "crypto/cn/c_jh.h" +#include "crypto/cn/c_skein.h" +} + + +static inline void do_blake_hash(const uint8_t *input, size_t len, uint8_t *output) { + blake256_hash(output, input, len); +} + + +static inline void do_groestl_hash(const uint8_t *input, size_t len, uint8_t *output) { + groestl(input, len * 8, output); +} + + +static inline void do_jh_hash(const uint8_t *input, size_t len, uint8_t *output) { + jh_hash(32 * 8, input, 8 * len, output); +} + + +static inline void do_skein_hash(const uint8_t *input, size_t len, uint8_t *output) { + xmr_skein(input, output); +} + + +void (* const extra_hashes[4])(const uint8_t *, size_t, uint8_t *) = {do_blake_hash, do_groestl_hash, do_jh_hash, do_skein_hash}; + + +#if defined(__x86_64__) || defined(_M_AMD64) +# ifdef __GNUC__ +static inline uint64_t __umul128(uint64_t a, uint64_t b, uint64_t* hi) +{ + unsigned __int128 r = (unsigned __int128) a * (unsigned __int128) b; + *hi = r >> 64; + return (uint64_t) r; +} +# else + #define __umul128 _umul128 +# endif +#elif defined(__i386__) || defined(_M_IX86) +static inline int64_t _mm_cvtsi128_si64(__m128i a) +{ + return ((uint64_t)(uint32_t)_mm_cvtsi128_si32(a) | ((uint64_t)(uint32_t)_mm_cvtsi128_si32(_mm_srli_si128(a, 4)) << 32)); +} + +static inline __m128i _mm_cvtsi64_si128(int64_t a) { + return _mm_set_epi64x(0, a); +} + +static inline uint64_t __umul128(uint64_t multiplier, uint64_t multiplicand, uint64_t *product_hi) { + // multiplier = ab = a * 2^32 + b + // multiplicand = cd = c * 2^32 + d + // ab * cd = a * c * 2^64 + (a * d + b * c) * 2^32 + b * d + uint64_t a = multiplier >> 32; + uint64_t b = multiplier & 0xFFFFFFFF; + uint64_t c = multiplicand >> 32; + uint64_t d = multiplicand & 0xFFFFFFFF; + + //uint64_t ac = a * c; + uint64_t ad = a * d; + //uint64_t bc = b * c; + uint64_t bd = b * d; + + uint64_t adbc = ad + (b * c); + uint64_t adbc_carry = adbc < ad ? 1 : 0; + + // multiplier * multiplicand = product_hi * 2^64 + product_lo + uint64_t product_lo = bd + (adbc << 32); + uint64_t product_lo_carry = product_lo < bd ? 1 : 0; + *product_hi = (a * c) + (adbc >> 32) + (adbc_carry << 32) + product_lo_carry; + + return product_lo; +} +#endif + + +// This will shift and xor tmp1 into itself as 4 32-bit vals such as +// sl_xor(a1 a2 a3 a4) = a1 (a2^a1) (a3^a2^a1) (a4^a3^a2^a1) +static inline __m128i sl_xor(__m128i tmp1) +{ + __m128i tmp4; + tmp4 = _mm_slli_si128(tmp1, 0x04); + tmp1 = _mm_xor_si128(tmp1, tmp4); + tmp4 = _mm_slli_si128(tmp4, 0x04); + tmp1 = _mm_xor_si128(tmp1, tmp4); + tmp4 = _mm_slli_si128(tmp4, 0x04); + tmp1 = _mm_xor_si128(tmp1, tmp4); + return tmp1; +} + + +template +static inline void aes_genkey_sub(__m128i* xout0, __m128i* xout2) +{ + __m128i xout1 = _mm_aeskeygenassist_si128(*xout2, rcon); + xout1 = _mm_shuffle_epi32(xout1, 0xFF); // see PSHUFD, set all elems to 4th elem + *xout0 = sl_xor(*xout0); + *xout0 = _mm_xor_si128(*xout0, xout1); + xout1 = _mm_aeskeygenassist_si128(*xout0, 0x00); + xout1 = _mm_shuffle_epi32(xout1, 0xAA); // see PSHUFD, set all elems to 3rd elem + *xout2 = sl_xor(*xout2); + *xout2 = _mm_xor_si128(*xout2, xout1); +} + + +template +static inline void soft_aes_genkey_sub(__m128i* xout0, __m128i* xout2) +{ + __m128i xout1 = soft_aeskeygenassist(*xout2); + xout1 = _mm_shuffle_epi32(xout1, 0xFF); // see PSHUFD, set all elems to 4th elem + *xout0 = sl_xor(*xout0); + *xout0 = _mm_xor_si128(*xout0, xout1); + xout1 = soft_aeskeygenassist<0x00>(*xout0); + xout1 = _mm_shuffle_epi32(xout1, 0xAA); // see PSHUFD, set all elems to 3rd elem + *xout2 = sl_xor(*xout2); + *xout2 = _mm_xor_si128(*xout2, xout1); +} + + +template +static inline void aes_genkey(const __m128i* memory, __m128i* k0, __m128i* k1, __m128i* k2, __m128i* k3, __m128i* k4, __m128i* k5, __m128i* k6, __m128i* k7, __m128i* k8, __m128i* k9) +{ + __m128i xout0 = _mm_load_si128(memory); + __m128i xout2 = _mm_load_si128(memory + 1); + *k0 = xout0; + *k1 = xout2; + + SOFT_AES ? soft_aes_genkey_sub<0x01>(&xout0, &xout2) : aes_genkey_sub<0x01>(&xout0, &xout2); + *k2 = xout0; + *k3 = xout2; + + SOFT_AES ? soft_aes_genkey_sub<0x02>(&xout0, &xout2) : aes_genkey_sub<0x02>(&xout0, &xout2); + *k4 = xout0; + *k5 = xout2; + + SOFT_AES ? soft_aes_genkey_sub<0x04>(&xout0, &xout2) : aes_genkey_sub<0x04>(&xout0, &xout2); + *k6 = xout0; + *k7 = xout2; + + SOFT_AES ? soft_aes_genkey_sub<0x08>(&xout0, &xout2) : aes_genkey_sub<0x08>(&xout0, &xout2); + *k8 = xout0; + *k9 = xout2; +} + + +static FORCEINLINE void soft_aesenc(void* __restrict ptr, const void* __restrict key, const uint32_t* __restrict t) +{ + uint32_t x0 = ((const uint32_t*)(ptr))[0]; + uint32_t x1 = ((const uint32_t*)(ptr))[1]; + uint32_t x2 = ((const uint32_t*)(ptr))[2]; + uint32_t x3 = ((const uint32_t*)(ptr))[3]; + + uint32_t y0 = t[x0 & 0xff]; x0 >>= 8; + uint32_t y1 = t[x1 & 0xff]; x1 >>= 8; + uint32_t y2 = t[x2 & 0xff]; x2 >>= 8; + uint32_t y3 = t[x3 & 0xff]; x3 >>= 8; + t += 256; + + y0 ^= t[x1 & 0xff]; x1 >>= 8; + y1 ^= t[x2 & 0xff]; x2 >>= 8; + y2 ^= t[x3 & 0xff]; x3 >>= 8; + y3 ^= t[x0 & 0xff]; x0 >>= 8; + t += 256; + + y0 ^= t[x2 & 0xff]; x2 >>= 8; + y1 ^= t[x3 & 0xff]; x3 >>= 8; + y2 ^= t[x0 & 0xff]; x0 >>= 8; + y3 ^= t[x1 & 0xff]; x1 >>= 8; + t += 256; + + y0 ^= t[x3]; + y1 ^= t[x0]; + y2 ^= t[x1]; + y3 ^= t[x2]; + + ((uint32_t*)ptr)[0] = y0 ^ ((uint32_t*)key)[0]; + ((uint32_t*)ptr)[1] = y1 ^ ((uint32_t*)key)[1]; + ((uint32_t*)ptr)[2] = y2 ^ ((uint32_t*)key)[2]; + ((uint32_t*)ptr)[3] = y3 ^ ((uint32_t*)key)[3]; +} + +static FORCEINLINE __m128i soft_aesenc(const void* __restrict ptr, const __m128i key, const uint32_t* __restrict t) +{ + uint32_t x0 = ((const uint32_t*)(ptr))[0]; + uint32_t x1 = ((const uint32_t*)(ptr))[1]; + uint32_t x2 = ((const uint32_t*)(ptr))[2]; + uint32_t x3 = ((const uint32_t*)(ptr))[3]; + + uint32_t y0 = t[x0 & 0xff]; x0 >>= 8; + uint32_t y1 = t[x1 & 0xff]; x1 >>= 8; + uint32_t y2 = t[x2 & 0xff]; x2 >>= 8; + uint32_t y3 = t[x3 & 0xff]; x3 >>= 8; + t += 256; + + y0 ^= t[x1 & 0xff]; x1 >>= 8; + y1 ^= t[x2 & 0xff]; x2 >>= 8; + y2 ^= t[x3 & 0xff]; x3 >>= 8; + y3 ^= t[x0 & 0xff]; x0 >>= 8; + t += 256; + + y0 ^= t[x2 & 0xff]; x2 >>= 8; + y1 ^= t[x3 & 0xff]; x3 >>= 8; + y2 ^= t[x0 & 0xff]; x0 >>= 8; + y3 ^= t[x1 & 0xff]; x1 >>= 8; + + y0 ^= t[x3 + 256]; + y1 ^= t[x0 + 256]; + y2 ^= t[x1 + 256]; + y3 ^= t[x2 + 256]; + + return _mm_xor_si128(_mm_set_epi32(y3, y2, y1, y0), key); +} + +template +void aes_round(__m128i key, __m128i* x0, __m128i* x1, __m128i* x2, __m128i* x3, __m128i* x4, __m128i* x5, __m128i* x6, __m128i* x7); + +template<> +NOINLINE void aes_round(__m128i key, __m128i* x0, __m128i* x1, __m128i* x2, __m128i* x3, __m128i* x4, __m128i* x5, __m128i* x6, __m128i* x7) +{ + *x0 = soft_aesenc((uint32_t*)x0, key, (const uint32_t*)saes_table); + *x1 = soft_aesenc((uint32_t*)x1, key, (const uint32_t*)saes_table); + *x2 = soft_aesenc((uint32_t*)x2, key, (const uint32_t*)saes_table); + *x3 = soft_aesenc((uint32_t*)x3, key, (const uint32_t*)saes_table); + *x4 = soft_aesenc((uint32_t*)x4, key, (const uint32_t*)saes_table); + *x5 = soft_aesenc((uint32_t*)x5, key, (const uint32_t*)saes_table); + *x6 = soft_aesenc((uint32_t*)x6, key, (const uint32_t*)saes_table); + *x7 = soft_aesenc((uint32_t*)x7, key, (const uint32_t*)saes_table); +} + +template<> +FORCEINLINE void aes_round(__m128i key, __m128i* x0, __m128i* x1, __m128i* x2, __m128i* x3, __m128i* x4, __m128i* x5, __m128i* x6, __m128i* x7) +{ + *x0 = _mm_aesenc_si128(*x0, key); + *x1 = _mm_aesenc_si128(*x1, key); + *x2 = _mm_aesenc_si128(*x2, key); + *x3 = _mm_aesenc_si128(*x3, key); + *x4 = _mm_aesenc_si128(*x4, key); + *x5 = _mm_aesenc_si128(*x5, key); + *x6 = _mm_aesenc_si128(*x6, key); + *x7 = _mm_aesenc_si128(*x7, key); +} + +inline void mix_and_propagate(__m128i& x0, __m128i& x1, __m128i& x2, __m128i& x3, __m128i& x4, __m128i& x5, __m128i& x6, __m128i& x7) +{ + __m128i tmp0 = x0; + x0 = _mm_xor_si128(x0, x1); + x1 = _mm_xor_si128(x1, x2); + x2 = _mm_xor_si128(x2, x3); + x3 = _mm_xor_si128(x3, x4); + x4 = _mm_xor_si128(x4, x5); + x5 = _mm_xor_si128(x5, x6); + x6 = _mm_xor_si128(x6, x7); + x7 = _mm_xor_si128(x7, tmp0); +} + + +namespace xmrig { + + +template +static inline void cn_explode_scratchpad(const __m128i *input, __m128i *output) +{ + constexpr CnAlgo props; + + __m128i xin0, xin1, xin2, xin3, xin4, xin5, xin6, xin7; + __m128i k0, k1, k2, k3, k4, k5, k6, k7, k8, k9; + + aes_genkey(input, &k0, &k1, &k2, &k3, &k4, &k5, &k6, &k7, &k8, &k9); + + xin0 = _mm_load_si128(input + 4); + xin1 = _mm_load_si128(input + 5); + xin2 = _mm_load_si128(input + 6); + xin3 = _mm_load_si128(input + 7); + xin4 = _mm_load_si128(input + 8); + xin5 = _mm_load_si128(input + 9); + xin6 = _mm_load_si128(input + 10); + xin7 = _mm_load_si128(input + 11); + + if (props.isHeavy()) { + for (size_t i = 0; i < 16; i++) { + aes_round(k0, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + aes_round(k1, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + aes_round(k2, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + aes_round(k3, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + aes_round(k4, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + aes_round(k5, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + aes_round(k6, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + aes_round(k7, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + aes_round(k8, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + aes_round(k9, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + + mix_and_propagate(xin0, xin1, xin2, xin3, xin4, xin5, xin6, xin7); + } + } + + for (size_t i = 0; i < props.memory() / sizeof(__m128i); i += 8) { + aes_round(k0, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + aes_round(k1, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + aes_round(k2, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + aes_round(k3, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + aes_round(k4, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + aes_round(k5, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + aes_round(k6, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + aes_round(k7, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + aes_round(k8, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + aes_round(k9, &xin0, &xin1, &xin2, &xin3, &xin4, &xin5, &xin6, &xin7); + + _mm_store_si128(output + i + 0, xin0); + _mm_store_si128(output + i + 1, xin1); + _mm_store_si128(output + i + 2, xin2); + _mm_store_si128(output + i + 3, xin3); + _mm_store_si128(output + i + 4, xin4); + _mm_store_si128(output + i + 5, xin5); + _mm_store_si128(output + i + 6, xin6); + _mm_store_si128(output + i + 7, xin7); + } +} + +inline __m128 _mm_set1_ps_epi32(uint32_t x) +{ + return _mm_castsi128_ps(_mm_set1_epi32(x)); +} + +inline void cryptonight_conceal_tweak(__m128i& cx, __m128& conc_var) +{ + __m128 r = _mm_cvtepi32_ps(cx); + __m128 c_old = conc_var; + r = _mm_add_ps(r, conc_var); + r = _mm_mul_ps(r, _mm_mul_ps(r, r)); + r = _mm_and_ps(_mm_set1_ps_epi32(0x807FFFFF), r); + r = _mm_or_ps(_mm_set1_ps_epi32(0x40000000), r); + conc_var = _mm_add_ps(conc_var, r); + + c_old = _mm_and_ps(_mm_set1_ps_epi32(0x807FFFFF), c_old); + c_old = _mm_or_ps(_mm_set1_ps_epi32(0x40000000), c_old); + __m128 nc = _mm_mul_ps(c_old, _mm_set1_ps(536870880.0f)); + cx = _mm_xor_si128(cx, _mm_cvttps_epi32(nc)); +} + +template +static inline void cn_implode_scratchpad(const __m128i *input, __m128i *output) +{ + constexpr CnAlgo props; + +# ifdef XMRIG_ALGO_CN_GPU + constexpr bool IS_HEAVY = props.isHeavy() || ALGO == Algorithm::CN_GPU; +# else + constexpr bool IS_HEAVY = props.isHeavy(); +# endif + + __m128i xout0, xout1, xout2, xout3, xout4, xout5, xout6, xout7; + __m128i k0, k1, k2, k3, k4, k5, k6, k7, k8, k9; + + aes_genkey(output + 2, &k0, &k1, &k2, &k3, &k4, &k5, &k6, &k7, &k8, &k9); + + xout0 = _mm_load_si128(output + 4); + xout1 = _mm_load_si128(output + 5); + xout2 = _mm_load_si128(output + 6); + xout3 = _mm_load_si128(output + 7); + xout4 = _mm_load_si128(output + 8); + xout5 = _mm_load_si128(output + 9); + xout6 = _mm_load_si128(output + 10); + xout7 = _mm_load_si128(output + 11); + + for (size_t i = 0; i < props.memory() / sizeof(__m128i); i += 8) { + xout0 = _mm_xor_si128(_mm_load_si128(input + i + 0), xout0); + xout1 = _mm_xor_si128(_mm_load_si128(input + i + 1), xout1); + xout2 = _mm_xor_si128(_mm_load_si128(input + i + 2), xout2); + xout3 = _mm_xor_si128(_mm_load_si128(input + i + 3), xout3); + xout4 = _mm_xor_si128(_mm_load_si128(input + i + 4), xout4); + xout5 = _mm_xor_si128(_mm_load_si128(input + i + 5), xout5); + xout6 = _mm_xor_si128(_mm_load_si128(input + i + 6), xout6); + xout7 = _mm_xor_si128(_mm_load_si128(input + i + 7), xout7); + + aes_round(k0, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k1, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k2, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k3, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k4, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k5, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k6, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k7, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k8, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k9, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + + if (IS_HEAVY) { + mix_and_propagate(xout0, xout1, xout2, xout3, xout4, xout5, xout6, xout7); + } + } + + if (IS_HEAVY) { + for (size_t i = 0; i < props.memory() / sizeof(__m128i); i += 8) { + xout0 = _mm_xor_si128(_mm_load_si128(input + i + 0), xout0); + xout1 = _mm_xor_si128(_mm_load_si128(input + i + 1), xout1); + xout2 = _mm_xor_si128(_mm_load_si128(input + i + 2), xout2); + xout3 = _mm_xor_si128(_mm_load_si128(input + i + 3), xout3); + xout4 = _mm_xor_si128(_mm_load_si128(input + i + 4), xout4); + xout5 = _mm_xor_si128(_mm_load_si128(input + i + 5), xout5); + xout6 = _mm_xor_si128(_mm_load_si128(input + i + 6), xout6); + xout7 = _mm_xor_si128(_mm_load_si128(input + i + 7), xout7); + + aes_round(k0, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k1, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k2, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k3, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k4, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k5, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k6, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k7, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k8, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k9, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + + mix_and_propagate(xout0, xout1, xout2, xout3, xout4, xout5, xout6, xout7); + } + + for (size_t i = 0; i < 16; i++) { + aes_round(k0, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k1, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k2, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k3, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k4, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k5, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k6, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k7, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k8, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + aes_round(k9, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); + + mix_and_propagate(xout0, xout1, xout2, xout3, xout4, xout5, xout6, xout7); + } + } + + _mm_store_si128(output + 4, xout0); + _mm_store_si128(output + 5, xout1); + _mm_store_si128(output + 6, xout2); + _mm_store_si128(output + 7, xout3); + _mm_store_si128(output + 8, xout4); + _mm_store_si128(output + 9, xout5); + _mm_store_si128(output + 10, xout6); + _mm_store_si128(output + 11, xout7); +} + + +} /* namespace xmrig */ + + +static inline __m128i aes_round_tweak_div(const __m128i &in, const __m128i &key) +{ + alignas(16) uint32_t k[4]; + alignas(16) uint32_t x[4]; + + _mm_store_si128((__m128i*) k, key); + _mm_store_si128((__m128i*) x, _mm_xor_si128(in, _mm_set_epi64x(0xffffffffffffffff, 0xffffffffffffffff))); + + #define BYTE(p, i) ((unsigned char*)&x[p])[i] + k[0] ^= saes_table[0][BYTE(0, 0)] ^ saes_table[1][BYTE(1, 1)] ^ saes_table[2][BYTE(2, 2)] ^ saes_table[3][BYTE(3, 3)]; + x[0] ^= k[0]; + k[1] ^= saes_table[0][BYTE(1, 0)] ^ saes_table[1][BYTE(2, 1)] ^ saes_table[2][BYTE(3, 2)] ^ saes_table[3][BYTE(0, 3)]; + x[1] ^= k[1]; + k[2] ^= saes_table[0][BYTE(2, 0)] ^ saes_table[1][BYTE(3, 1)] ^ saes_table[2][BYTE(0, 2)] ^ saes_table[3][BYTE(1, 3)]; + x[2] ^= k[2]; + k[3] ^= saes_table[0][BYTE(3, 0)] ^ saes_table[1][BYTE(0, 1)] ^ saes_table[2][BYTE(1, 2)] ^ saes_table[3][BYTE(2, 3)]; + #undef BYTE + + return _mm_load_si128((__m128i*)k); +} + + +static inline __m128i int_sqrt_v2(const uint64_t n0) +{ + __m128d x = _mm_castsi128_pd(_mm_add_epi64(_mm_cvtsi64_si128(n0 >> 12), _mm_set_epi64x(0, 1023ULL << 52))); + x = _mm_sqrt_sd(_mm_setzero_pd(), x); + uint64_t r = static_cast(_mm_cvtsi128_si64(_mm_castpd_si128(x))); + + const uint64_t s = r >> 20; + r >>= 19; + + uint64_t x2 = (s - (1022ULL << 32)) * (r - s - (1022ULL << 32) + 1); +# if (defined(_MSC_VER) || __GNUC__ > 7 || (__GNUC__ == 7 && __GNUC_MINOR__ > 1)) && (defined(__x86_64__) || defined(_M_AMD64)) + _addcarry_u64(_subborrow_u64(0, x2, n0, (unsigned long long int*)&x2), r, 0, (unsigned long long int*)&r); +# else + if (x2 < n0) ++r; +# endif + + return _mm_cvtsi64_si128(r); +} + + +void wow_soft_aes_compile_code(const V4_Instruction *code, int code_size, void *machine_code, xmrig::Assembly ASM); +void v4_soft_aes_compile_code(const V4_Instruction *code, int code_size, void *machine_code, xmrig::Assembly ASM); + + +namespace xmrig { + + +template +static inline void cryptonight_monero_tweak(uint64_t *mem_out, const uint8_t *l, uint64_t idx, __m128i ax0, __m128i bx0, __m128i bx1, __m128i& cx) +{ + constexpr CnAlgo props; + + if (props.base() == Algorithm::CN_2) { + VARIANT2_SHUFFLE(l, idx, ax0, bx0, bx1, cx, (ALGO == Algorithm::CN_RWZ || ALGO == Algorithm::CN_EXTREMELITE_0 ? 1 : 0)); + _mm_store_si128(reinterpret_cast<__m128i *>(mem_out), _mm_xor_si128(bx0, cx)); + } else { + __m128i tmp = _mm_xor_si128(bx0, cx); + mem_out[0] = _mm_cvtsi128_si64(tmp); + + tmp = _mm_castps_si128(_mm_movehl_ps(_mm_castsi128_ps(tmp), _mm_castsi128_ps(tmp))); + uint64_t vh = _mm_cvtsi128_si64(tmp); + + uint8_t x = static_cast(vh >> 24); + static const uint16_t table = 0x7531; + const uint8_t index = (((x >> (3)) & 6) | (x & 1)) << 1; + vh ^= ((table >> index) & 0x3) << 28; + + mem_out[1] = vh; + } +} + + +template +inline void cryptonight_single_hash(const uint8_t *__restrict__ input, size_t size, uint8_t *__restrict__ output, cryptonight_ctx **__restrict__ ctx, uint64_t height) +{ + constexpr CnAlgo props; + constexpr size_t MASK = props.mask(); + constexpr Algorithm::Id BASE = props.base(); + +# ifdef XMRIG_ALGO_CN_HEAVY + constexpr bool IS_CN_HEAVY_TUBE = ALGO == Algorithm::CN_HEAVY_TUBE; +# else + constexpr bool IS_CN_HEAVY_TUBE = false; +# endif + + if (BASE == Algorithm::CN_1 && size < 43) { + memset(output, 0, 32); + return; + } + + keccak(input, size, ctx[0]->state); + cn_explode_scratchpad(reinterpret_cast(ctx[0]->state), reinterpret_cast<__m128i *>(ctx[0]->memory)); + + uint64_t *h0 = reinterpret_cast(ctx[0]->state); + uint8_t *l0 = ctx[0]->memory; + +# ifdef XMRIG_FEATURE_ASM + if (SOFT_AES && props.isR()) { + if (!ctx[0]->generated_code_data.match(ALGO, height)) { + V4_Instruction code[256]; + const int code_size = v4_random_math_init(code, height); + + if (ALGO == Algorithm::CN_WOW) { + wow_soft_aes_compile_code(code, code_size, reinterpret_cast(ctx[0]->generated_code), Assembly::NONE); + } + else if (ALGO == Algorithm::CN_R) { + v4_soft_aes_compile_code(code, code_size, reinterpret_cast(ctx[0]->generated_code), Assembly::NONE); + } + + ctx[0]->generated_code_data = { ALGO, height }; + } + + ctx[0]->saes_table = reinterpret_cast(saes_table); + ctx[0]->generated_code(ctx); + } else { +# endif + + VARIANT1_INIT(0); + VARIANT2_INIT(0); + VARIANT2_SET_ROUNDING_MODE(); + VARIANT4_RANDOM_MATH_INIT(0); + + uint64_t al0 = h0[0] ^ h0[4]; + uint64_t ah0 = h0[1] ^ h0[5]; + uint64_t idx0 = al0; + __m128i bx0 = _mm_set_epi64x(static_cast(h0[3] ^ h0[7]), static_cast(h0[2] ^ h0[6])); + __m128i bx1 = _mm_set_epi64x(static_cast(h0[9] ^ h0[11]), static_cast(h0[8] ^ h0[10])); + __m128 conc_var; + + if (ALGO == Algorithm::CN_CONCEAL) { + SET_ROUNDING_MODE_NEAREST() + conc_var = _mm_setzero_ps(); + } + + for (size_t i = 0; i < props.iterations(); i++) { + __m128i cx; + if (IS_CN_HEAVY_TUBE || !SOFT_AES) { + cx = _mm_load_si128(reinterpret_cast(&l0[idx0 & MASK])); + } + + const __m128i ax0 = _mm_set_epi64x(static_cast(ah0), static_cast(al0)); + + if(ALGO == Algorithm::CN_CONCEAL) { + cryptonight_conceal_tweak(cx, conc_var); + } + + if (IS_CN_HEAVY_TUBE) { + cx = aes_round_tweak_div(cx, ax0); + } + else if (SOFT_AES) { + cx = soft_aesenc(&l0[idx0 & MASK], ax0, reinterpret_cast(saes_table)); + } + else { + cx = _mm_aesenc_si128(cx, ax0); + } + + if (BASE == Algorithm::CN_1 || BASE == Algorithm::CN_2) { + cryptonight_monero_tweak(reinterpret_cast(&l0[idx0 & MASK]), l0, idx0 & MASK, ax0, bx0, bx1, cx); + } else { + _mm_store_si128(reinterpret_cast<__m128i *>(&l0[idx0 & MASK]), _mm_xor_si128(bx0, cx)); + } + + idx0 = static_cast(_mm_cvtsi128_si64(cx)); + + uint64_t hi, lo, cl, ch; + cl = (reinterpret_cast(&l0[idx0 & MASK]))[0]; + ch = (reinterpret_cast(&l0[idx0 & MASK]))[1]; + + if (BASE == Algorithm::CN_2) { + if (props.isR()) { + VARIANT4_RANDOM_MATH(0, al0, ah0, cl, bx0, bx1); + if (ALGO == Algorithm::CN_R) { + al0 ^= r0[2] | (static_cast(r0[3]) << 32); + ah0 ^= r0[0] | (static_cast(r0[1]) << 32); + } + } else { + VARIANT2_INTEGER_MATH(0, cl, cx); + } + } + + lo = __umul128(idx0, cl, &hi); + + if (BASE == Algorithm::CN_2) { + if (ALGO == Algorithm::CN_R) { + VARIANT2_SHUFFLE(l0, idx0 & MASK, ax0, bx0, bx1, cx, 0); + } else { + VARIANT2_SHUFFLE2(l0, idx0 & MASK, ax0, bx0, bx1, hi, lo, (ALGO == Algorithm::CN_RWZ || ALGO == Algorithm::CN_EXTREMELITE_0 ? 1 : 0)); + } + } + + al0 += hi; + ah0 += lo; + + reinterpret_cast(&l0[idx0 & MASK])[0] = al0; + + if (IS_CN_HEAVY_TUBE || ALGO == Algorithm::CN_RTO) { + reinterpret_cast(&l0[idx0 & MASK])[1] = ah0 ^ tweak1_2_0 ^ al0; + } else if (BASE == Algorithm::CN_1) { + reinterpret_cast(&l0[idx0 & MASK])[1] = ah0 ^ tweak1_2_0; + } else { + reinterpret_cast(&l0[idx0 & MASK])[1] = ah0; + } + + al0 ^= cl; + ah0 ^= ch; + idx0 = al0; + +# ifdef XMRIG_ALGO_CN_HEAVY + if (props.isHeavy()) { + int64_t n = ((int64_t*)&l0[idx0 & MASK])[0]; + int32_t d = ((int32_t*)&l0[idx0 & MASK])[2]; + int64_t q = n / (d | 0x5); + + ((int64_t*)&l0[idx0 & MASK])[0] = n ^ q; + + if (ALGO == Algorithm::CN_HEAVY_XHV) { + d = ~d; + } + + idx0 = d ^ q; + } +# endif + + if (BASE == Algorithm::CN_2) { + bx1 = bx0; + } + + bx0 = cx; + } + +# ifdef XMRIG_FEATURE_ASM + } +# endif + + cn_implode_scratchpad(reinterpret_cast(ctx[0]->memory), reinterpret_cast<__m128i *>(ctx[0]->state)); + keccakf(h0, 24); + extra_hashes[ctx[0]->state[0] & 3](ctx[0]->state, 200, output); +} + + +} /* namespace xmrig */ + + +#ifdef XMRIG_ALGO_CN_GPU +template +void cn_gpu_inner_avx(const uint8_t *spad, uint8_t *lpad); + + +template +void cn_gpu_inner_ssse3(const uint8_t *spad, uint8_t *lpad); + + +namespace xmrig { + + +template +void cn_explode_scratchpad_gpu(const uint8_t *input, uint8_t *output) +{ + constexpr size_t hash_size = 200; // 25x8 bytes + alignas(16) uint64_t hash[25]; + + for (uint64_t i = 0; i < MEM / 512; i++) { + memcpy(hash, input, hash_size); + hash[0] ^= i; + + xmrig::keccakf(hash, 24); + memcpy(output, hash, 160); + output += 160; + + xmrig::keccakf(hash, 24); + memcpy(output, hash, 176); + output += 176; + + xmrig::keccakf(hash, 24); + memcpy(output, hash, 176); + output += 176; + } +} + + +template +inline void cryptonight_single_hash_gpu(const uint8_t *__restrict__ input, size_t size, uint8_t *__restrict__ output, cryptonight_ctx **__restrict__ ctx, uint64_t) +{ + constexpr CnAlgo props; + + keccak(input, size, ctx[0]->state); + cn_explode_scratchpad_gpu(ctx[0]->state, ctx[0]->memory); + +# ifdef _MSC_VER + _control87(RC_NEAR, MCW_RC); +# else + fesetround(FE_TONEAREST); +# endif + + if (xmrig::Cpu::info()->hasAVX2()) { + cn_gpu_inner_avx(ctx[0]->state, ctx[0]->memory); + } else { + cn_gpu_inner_ssse3(ctx[0]->state, ctx[0]->memory); + } + + cn_implode_scratchpad(reinterpret_cast(ctx[0]->memory), reinterpret_cast<__m128i *>(ctx[0]->state)); + keccakf(reinterpret_cast(ctx[0]->state), 24); + memcpy(output, ctx[0]->state, 32); +} + + +} /* namespace xmrig */ +#endif + + +#ifdef XMRIG_FEATURE_ASM +extern "C" void cnv2_mainloop_ivybridge_asm(cryptonight_ctx **ctx); +extern "C" void cnv2_mainloop_ryzen_asm(cryptonight_ctx **ctx); +extern "C" void cnv2_mainloop_bulldozer_asm(cryptonight_ctx **ctx); +extern "C" void cnv2_double_mainloop_sandybridge_asm(cryptonight_ctx **ctx); +extern "C" void cnv2_rwz_mainloop_asm(cryptonight_ctx **ctx); +extern "C" void cnv2_rwz_double_mainloop_asm(cryptonight_ctx **ctx); + +namespace xmrig { + + +typedef void (*cn_mainloop_fun)(cryptonight_ctx **ctx); + + +extern cn_mainloop_fun cn_half_mainloop_ivybridge_asm; +extern cn_mainloop_fun cn_half_mainloop_ryzen_asm; +extern cn_mainloop_fun cn_half_mainloop_bulldozer_asm; +extern cn_mainloop_fun cn_half_double_mainloop_sandybridge_asm; + +extern cn_mainloop_fun cn_trtl_mainloop_ivybridge_asm; +extern cn_mainloop_fun cn_trtl_mainloop_ryzen_asm; +extern cn_mainloop_fun cn_trtl_mainloop_bulldozer_asm; +extern cn_mainloop_fun cn_trtl_double_mainloop_sandybridge_asm; + +extern cn_mainloop_fun cn_zls_mainloop_ivybridge_asm; +extern cn_mainloop_fun cn_zls_mainloop_ryzen_asm; +extern cn_mainloop_fun cn_zls_mainloop_bulldozer_asm; +extern cn_mainloop_fun cn_zls_double_mainloop_sandybridge_asm; + +extern cn_mainloop_fun cn_double_mainloop_ivybridge_asm; +extern cn_mainloop_fun cn_double_mainloop_ryzen_asm; +extern cn_mainloop_fun cn_double_mainloop_bulldozer_asm; +extern cn_mainloop_fun cn_double_double_mainloop_sandybridge_asm; + +extern cn_mainloop_fun cnv2_upx2_mainloop_asm; +extern cn_mainloop_fun cnv2_upx2_double_mainloop_asm; + +} // namespace xmrig + + +void wow_compile_code(const V4_Instruction* code, int code_size, void* machine_code, xmrig::Assembly ASM); +void v4_compile_code(const V4_Instruction* code, int code_size, void* machine_code, xmrig::Assembly ASM); +void wow_compile_code_double(const V4_Instruction* code, int code_size, void* machine_code, xmrig::Assembly ASM); +void v4_compile_code_double(const V4_Instruction* code, int code_size, void* machine_code, xmrig::Assembly ASM); + + +template +void cn_r_compile_code(const V4_Instruction* code, int code_size, void* machine_code, xmrig::Assembly ASM) +{ + v4_compile_code(code, code_size, machine_code, ASM); +} + + +template +void cn_r_compile_code_double(const V4_Instruction* code, int code_size, void* machine_code, xmrig::Assembly ASM) +{ + v4_compile_code_double(code, code_size, machine_code, ASM); +} + + +template<> +void cn_r_compile_code(const V4_Instruction* code, int code_size, void* machine_code, xmrig::Assembly ASM) +{ + wow_compile_code(code, code_size, machine_code, ASM); +} + + +template<> +void cn_r_compile_code_double(const V4_Instruction* code, int code_size, void* machine_code, xmrig::Assembly ASM) +{ + wow_compile_code_double(code, code_size, machine_code, ASM); +} + + +namespace xmrig { + + +template +inline void cryptonight_single_hash_asm(const uint8_t *__restrict__ input, size_t size, uint8_t *__restrict__ output, cryptonight_ctx **__restrict__ ctx, uint64_t height) +{ + constexpr CnAlgo props; + + if (props.isR() && !ctx[0]->generated_code_data.match(ALGO, height)) { + V4_Instruction code[256]; + const int code_size = v4_random_math_init(code, height); + cn_r_compile_code(code, code_size, reinterpret_cast(ctx[0]->generated_code), ASM); + + ctx[0]->generated_code_data = { ALGO, height }; + } + + keccak(input, size, ctx[0]->state); + cn_explode_scratchpad(reinterpret_cast(ctx[0]->state), reinterpret_cast<__m128i*>(ctx[0]->memory)); + + if (ALGO == Algorithm::CN_2) { + if (ASM == Assembly::INTEL) { + cnv2_mainloop_ivybridge_asm(ctx); + } + else if (ASM == Assembly::RYZEN) { + cnv2_mainloop_ryzen_asm(ctx); + } + else { + cnv2_mainloop_bulldozer_asm(ctx); + } + } + else if (ALGO == Algorithm::CN_HALF) { + if (ASM == Assembly::INTEL) { + cn_half_mainloop_ivybridge_asm(ctx); + } + else if (ASM == Assembly::RYZEN) { + cn_half_mainloop_ryzen_asm(ctx); + } + else { + cn_half_mainloop_bulldozer_asm(ctx); + } + } +# ifdef XMRIG_ALGO_CN_PICO + else if (ALGO == Algorithm::CN_PICO_0) { + if (ASM == Assembly::INTEL) { + cn_trtl_mainloop_ivybridge_asm(ctx); + } + else if (ASM == Assembly::RYZEN) { + cn_trtl_mainloop_ryzen_asm(ctx); + } + else { + cn_trtl_mainloop_bulldozer_asm(ctx); + } + } +# endif + else if (ALGO == Algorithm::CN_RWZ) { + cnv2_rwz_mainloop_asm(ctx); + } + else if (ALGO == Algorithm::CN_ZLS) { + if (ASM == Assembly::INTEL) { + cn_zls_mainloop_ivybridge_asm(ctx); + } + else if (ASM == Assembly::RYZEN) { + cn_zls_mainloop_ryzen_asm(ctx); + } + else { + cn_zls_mainloop_bulldozer_asm(ctx); + } + } + else if (ALGO == Algorithm::CN_DOUBLE) { + if (ASM == Assembly::INTEL) { + cn_double_mainloop_ivybridge_asm(ctx); + } + else if (ASM == Assembly::RYZEN) { + cn_double_mainloop_ryzen_asm(ctx); + } + else { + cn_double_mainloop_bulldozer_asm(ctx); + } + } +# ifdef XMRIG_ALGO_CN_EXTREMELITE + else if (ALGO == Algorithm::CN_EXTREMELITE_0) { + cnv2_upx2_mainloop_asm(ctx); + } +# endif + else if (props.isR()) { + ctx[0]->generated_code(ctx); + } + + cn_implode_scratchpad(reinterpret_cast(ctx[0]->memory), reinterpret_cast<__m128i*>(ctx[0]->state)); + keccakf(reinterpret_cast(ctx[0]->state), 24); + extra_hashes[ctx[0]->state[0] & 3](ctx[0]->state, 200, output); +} + + +template +inline void cryptonight_double_hash_asm(const uint8_t *__restrict__ input, size_t size, uint8_t *__restrict__ output, cryptonight_ctx **__restrict__ ctx, uint64_t height) +{ + constexpr CnAlgo props; + + if (props.isR() && !ctx[0]->generated_code_data.match(ALGO, height)) { + V4_Instruction code[256]; + const int code_size = v4_random_math_init(code, height); + cn_r_compile_code_double(code, code_size, reinterpret_cast(ctx[0]->generated_code), ASM); + + ctx[0]->generated_code_data = { ALGO, height }; + } + + keccak(input, size, ctx[0]->state); + keccak(input + size, size, ctx[1]->state); + + cn_explode_scratchpad(reinterpret_cast(ctx[0]->state), reinterpret_cast<__m128i*>(ctx[0]->memory)); + cn_explode_scratchpad(reinterpret_cast(ctx[1]->state), reinterpret_cast<__m128i*>(ctx[1]->memory)); + + if (ALGO == Algorithm::CN_2) { + cnv2_double_mainloop_sandybridge_asm(ctx); + } + else if (ALGO == Algorithm::CN_HALF) { + cn_half_double_mainloop_sandybridge_asm(ctx); + } +# ifdef XMRIG_ALGO_CN_PICO + else if (ALGO == Algorithm::CN_PICO_0) { + cn_trtl_double_mainloop_sandybridge_asm(ctx); + } +# endif +# ifdef XMRIG_ALGO_CN_EXTREMELITE + else if (ALGO == Algorithm::CN_EXTREMELITE_0) { + cnv2_upx2_double_mainloop_asm(ctx); + } +# endif + else if (ALGO == Algorithm::CN_RWZ) { + cnv2_rwz_double_mainloop_asm(ctx); + } + else if (ALGO == Algorithm::CN_ZLS) { + cn_zls_double_mainloop_sandybridge_asm(ctx); + } + else if (ALGO == Algorithm::CN_DOUBLE) { + cn_double_double_mainloop_sandybridge_asm(ctx); + } + else if (props.isR()) { + ctx[0]->generated_code(ctx); + } + + cn_implode_scratchpad(reinterpret_cast(ctx[0]->memory), reinterpret_cast<__m128i*>(ctx[0]->state)); + cn_implode_scratchpad(reinterpret_cast(ctx[1]->memory), reinterpret_cast<__m128i*>(ctx[1]->state)); + + keccakf(reinterpret_cast(ctx[0]->state), 24); + keccakf(reinterpret_cast(ctx[1]->state), 24); + + extra_hashes[ctx[0]->state[0] & 3](ctx[0]->state, 200, output); + extra_hashes[ctx[1]->state[0] & 3](ctx[1]->state, 200, output + 32); +} + + +} /* namespace xmrig */ +#endif + + +namespace xmrig { + + +template +inline void cryptonight_double_hash(const uint8_t *__restrict__ input, size_t size, uint8_t *__restrict__ output, cryptonight_ctx **__restrict__ ctx, uint64_t height) +{ + constexpr CnAlgo props; + constexpr size_t MASK = props.mask(); + constexpr Algorithm::Id BASE = props.base(); + +# ifdef XMRIG_ALGO_CN_HEAVY + constexpr bool IS_CN_HEAVY_TUBE = ALGO == Algorithm::CN_HEAVY_TUBE; +# else + constexpr bool IS_CN_HEAVY_TUBE = false; +# endif + + if (BASE == Algorithm::CN_1 && size < 43) { + memset(output, 0, 64); + return; + } + + keccak(input, size, ctx[0]->state); + keccak(input + size, size, ctx[1]->state); + + uint8_t *l0 = ctx[0]->memory; + uint8_t *l1 = ctx[1]->memory; + uint64_t *h0 = reinterpret_cast(ctx[0]->state); + uint64_t *h1 = reinterpret_cast(ctx[1]->state); + + VARIANT1_INIT(0); + VARIANT1_INIT(1); + VARIANT2_INIT(0); + VARIANT2_INIT(1); + VARIANT2_SET_ROUNDING_MODE(); + VARIANT4_RANDOM_MATH_INIT(0); + VARIANT4_RANDOM_MATH_INIT(1); + + cn_explode_scratchpad(reinterpret_cast(h0), reinterpret_cast<__m128i *>(l0)); + cn_explode_scratchpad(reinterpret_cast(h1), reinterpret_cast<__m128i *>(l1)); + + uint64_t al0 = h0[0] ^ h0[4]; + uint64_t al1 = h1[0] ^ h1[4]; + uint64_t ah0 = h0[1] ^ h0[5]; + uint64_t ah1 = h1[1] ^ h1[5]; + + __m128i bx00 = _mm_set_epi64x(h0[3] ^ h0[7], h0[2] ^ h0[6]); + __m128i bx01 = _mm_set_epi64x(h0[9] ^ h0[11], h0[8] ^ h0[10]); + __m128i bx10 = _mm_set_epi64x(h1[3] ^ h1[7], h1[2] ^ h1[6]); + __m128i bx11 = _mm_set_epi64x(h1[9] ^ h1[11], h1[8] ^ h1[10]); + + uint64_t idx0 = al0; + uint64_t idx1 = al1; + + __m128 conc_var0; + __m128 conc_var1; + + if (ALGO == Algorithm::CN_CONCEAL) { + SET_ROUNDING_MODE_NEAREST() + conc_var0 = _mm_setzero_ps(); + conc_var1 = _mm_setzero_ps(); + } + + for (size_t i = 0; i < props.iterations(); i++) { + __m128i cx0, cx1; + if (IS_CN_HEAVY_TUBE || !SOFT_AES) { + cx0 = _mm_load_si128(reinterpret_cast(&l0[idx0 & MASK])); + cx1 = _mm_load_si128(reinterpret_cast(&l1[idx1 & MASK])); + } + + const __m128i ax0 = _mm_set_epi64x(ah0, al0); + const __m128i ax1 = _mm_set_epi64x(ah1, al1); + + if (ALGO == Algorithm::CN_CONCEAL) { + cryptonight_conceal_tweak(cx0, conc_var0); + cryptonight_conceal_tweak(cx1, conc_var1); + } + + if (IS_CN_HEAVY_TUBE) { + cx0 = aes_round_tweak_div(cx0, ax0); + cx1 = aes_round_tweak_div(cx1, ax1); + } + else if (SOFT_AES) { + cx0 = soft_aesenc(&l0[idx0 & MASK], ax0, reinterpret_cast(saes_table)); + cx1 = soft_aesenc(&l1[idx1 & MASK], ax1, reinterpret_cast(saes_table)); + } + else { + cx0 = _mm_aesenc_si128(cx0, ax0); + cx1 = _mm_aesenc_si128(cx1, ax1); + } + + if (BASE == Algorithm::CN_1 || BASE == Algorithm::CN_2) { + cryptonight_monero_tweak((uint64_t*)&l0[idx0 & MASK], l0, idx0 & MASK, ax0, bx00, bx01, cx0); + cryptonight_monero_tweak((uint64_t*)&l1[idx1 & MASK], l1, idx1 & MASK, ax1, bx10, bx11, cx1); + } else { + _mm_store_si128((__m128i *) &l0[idx0 & MASK], _mm_xor_si128(bx00, cx0)); + _mm_store_si128((__m128i *) &l1[idx1 & MASK], _mm_xor_si128(bx10, cx1)); + } + + idx0 = _mm_cvtsi128_si64(cx0); + idx1 = _mm_cvtsi128_si64(cx1); + + uint64_t hi, lo, cl, ch; + cl = ((uint64_t*) &l0[idx0 & MASK])[0]; + ch = ((uint64_t*) &l0[idx0 & MASK])[1]; + + if (BASE == Algorithm::CN_2) { + if (props.isR()) { + VARIANT4_RANDOM_MATH(0, al0, ah0, cl, bx00, bx01); + if (ALGO == Algorithm::CN_R) { + al0 ^= r0[2] | ((uint64_t)(r0[3]) << 32); + ah0 ^= r0[0] | ((uint64_t)(r0[1]) << 32); + } + } else { + VARIANT2_INTEGER_MATH(0, cl, cx0); + } + } + + lo = __umul128(idx0, cl, &hi); + + if (BASE == Algorithm::CN_2) { + if (ALGO == Algorithm::CN_R) { + VARIANT2_SHUFFLE(l0, idx0 & MASK, ax0, bx00, bx01, cx0, 0); + } else { + VARIANT2_SHUFFLE2(l0, idx0 & MASK, ax0, bx00, bx01, hi, lo, (ALGO == Algorithm::CN_RWZ || ALGO == Algorithm::CN_EXTREMELITE_0 ? 1 : 0)); + } + } + + al0 += hi; + ah0 += lo; + + ((uint64_t*)&l0[idx0 & MASK])[0] = al0; + + if (IS_CN_HEAVY_TUBE || ALGO == Algorithm::CN_RTO) { + ((uint64_t*) &l0[idx0 & MASK])[1] = ah0 ^ tweak1_2_0 ^ al0; + } else if (BASE == Algorithm::CN_1) { + ((uint64_t*) &l0[idx0 & MASK])[1] = ah0 ^ tweak1_2_0; + } else { + ((uint64_t*) &l0[idx0 & MASK])[1] = ah0; + } + + al0 ^= cl; + ah0 ^= ch; + idx0 = al0; + +# ifdef XMRIG_ALGO_CN_HEAVY + if (props.isHeavy()) { + int64_t n = ((int64_t*)&l0[idx0 & MASK])[0]; + int32_t d = ((int32_t*)&l0[idx0 & MASK])[2]; + int64_t q = n / (d | 0x5); + + ((int64_t*)&l0[idx0 & MASK])[0] = n ^ q; + + if (ALGO == Algorithm::CN_HEAVY_XHV) { + d = ~d; + } + + idx0 = d ^ q; + } +# endif + + cl = ((uint64_t*) &l1[idx1 & MASK])[0]; + ch = ((uint64_t*) &l1[idx1 & MASK])[1]; + + if (BASE == Algorithm::CN_2) { + if (props.isR()) { + VARIANT4_RANDOM_MATH(1, al1, ah1, cl, bx10, bx11); + if (ALGO == Algorithm::CN_R) { + al1 ^= r1[2] | ((uint64_t)(r1[3]) << 32); + ah1 ^= r1[0] | ((uint64_t)(r1[1]) << 32); + } + } else { + VARIANT2_INTEGER_MATH(1, cl, cx1); + } + } + + lo = __umul128(idx1, cl, &hi); + + if (BASE == Algorithm::CN_2) { + if (ALGO == Algorithm::CN_R) { + VARIANT2_SHUFFLE(l1, idx1 & MASK, ax1, bx10, bx11, cx1, 0); + } else { + VARIANT2_SHUFFLE2(l1, idx1 & MASK, ax1, bx10, bx11, hi, lo, (ALGO == Algorithm::CN_RWZ || ALGO == Algorithm::CN_EXTREMELITE_0 ? 1 : 0)); + } + } + + al1 += hi; + ah1 += lo; + + ((uint64_t*)&l1[idx1 & MASK])[0] = al1; + + if (IS_CN_HEAVY_TUBE || ALGO == Algorithm::CN_RTO) { + ((uint64_t*)&l1[idx1 & MASK])[1] = ah1 ^ tweak1_2_1 ^ al1; + } else if (BASE == Algorithm::CN_1) { + ((uint64_t*)&l1[idx1 & MASK])[1] = ah1 ^ tweak1_2_1; + } else { + ((uint64_t*)&l1[idx1 & MASK])[1] = ah1; + } + + al1 ^= cl; + ah1 ^= ch; + idx1 = al1; + +# ifdef XMRIG_ALGO_CN_HEAVY + if (props.isHeavy()) { + int64_t n = ((int64_t*)&l1[idx1 & MASK])[0]; + int32_t d = ((int32_t*)&l1[idx1 & MASK])[2]; + int64_t q = n / (d | 0x5); + + ((int64_t*)&l1[idx1 & MASK])[0] = n ^ q; + + if (ALGO == Algorithm::CN_HEAVY_XHV) { + d = ~d; + } + + idx1 = d ^ q; + } +# endif + + if (BASE == Algorithm::CN_2) { + bx01 = bx00; + bx11 = bx10; + } + + bx00 = cx0; + bx10 = cx1; + } + + cn_implode_scratchpad(reinterpret_cast(l0), reinterpret_cast<__m128i *>(h0)); + cn_implode_scratchpad(reinterpret_cast(l1), reinterpret_cast<__m128i *>(h1)); + + keccakf(h0, 24); + keccakf(h1, 24); + + extra_hashes[ctx[0]->state[0] & 3](ctx[0]->state, 200, output); + extra_hashes[ctx[1]->state[0] & 3](ctx[1]->state, 200, output + 32); +} + + +#define CN_STEP1(a, b0, b1, c, l, ptr, idx) \ + ptr = reinterpret_cast<__m128i*>(&l[idx & MASK]); \ + c = _mm_load_si128(ptr); + + +#define CN_STEP2(conc_var, a, b0, b1, c, l, ptr, idx) \ + if(ALGO == Algorithm::CN_CONCEAL) { \ + cryptonight_conceal_tweak(c, conc_var); \ + } \ + if (IS_CN_HEAVY_TUBE) { \ + c = aes_round_tweak_div(c, a); \ + } \ + else if (SOFT_AES) { \ + c = soft_aesenc(&c, a, (const uint32_t*)saes_table); \ + } else { \ + c = _mm_aesenc_si128(c, a); \ + } \ + \ + if (BASE == Algorithm::CN_1 || BASE == Algorithm::CN_2) { \ + cryptonight_monero_tweak((uint64_t*)ptr, l, idx & MASK, a, b0, b1, c); \ + } else { \ + _mm_store_si128(ptr, _mm_xor_si128(b0, c)); \ + } + + +#define CN_STEP3(part, a, b0, b1, c, l, ptr, idx) \ + idx = _mm_cvtsi128_si64(c); \ + ptr = reinterpret_cast<__m128i*>(&l[idx & MASK]); \ + uint64_t cl##part = ((uint64_t*)ptr)[0]; \ + uint64_t ch##part = ((uint64_t*)ptr)[1]; + + +#define CN_STEP4(part, a, b0, b1, c, l, mc, ptr, idx) \ + uint64_t al##part, ah##part; \ + if (BASE == Algorithm::CN_2) { \ + if (props.isR()) { \ + al##part = _mm_cvtsi128_si64(a); \ + ah##part = _mm_cvtsi128_si64(_mm_srli_si128(a, 8)); \ + VARIANT4_RANDOM_MATH(part, al##part, ah##part, cl##part, b0, b1); \ + if (ALGO == Algorithm::CN_R) { \ + al##part ^= r##part[2] | ((uint64_t)(r##part[3]) << 32); \ + ah##part ^= r##part[0] | ((uint64_t)(r##part[1]) << 32); \ + } \ + } else { \ + VARIANT2_INTEGER_MATH(part, cl##part, c); \ + } \ + } \ + lo = __umul128(idx, cl##part, &hi); \ + if (BASE == Algorithm::CN_2) { \ + if (ALGO == Algorithm::CN_R) { \ + VARIANT2_SHUFFLE(l, idx & MASK, a, b0, b1, c, 0); \ + } else { \ + VARIANT2_SHUFFLE2(l, idx & MASK, a, b0, b1, hi, lo, (ALGO == Algorithm::CN_RWZ || ALGO == Algorithm::CN_EXTREMELITE_0 ? 1 : 0)); \ + } \ + } \ + if (ALGO == Algorithm::CN_R) { \ + a = _mm_set_epi64x(ah##part, al##part); \ + } \ + a = _mm_add_epi64(a, _mm_set_epi64x(lo, hi)); \ + \ + if (BASE == Algorithm::CN_1) { \ + _mm_store_si128(ptr, _mm_xor_si128(a, mc)); \ + \ + if (IS_CN_HEAVY_TUBE || ALGO == Algorithm::CN_RTO) { \ + ((uint64_t*)ptr)[1] ^= ((uint64_t*)ptr)[0]; \ + } \ + } else { \ + _mm_store_si128(ptr, a); \ + } \ + \ + a = _mm_xor_si128(a, _mm_set_epi64x(ch##part, cl##part)); \ + idx = _mm_cvtsi128_si64(a); \ + if (props.isHeavy()) { \ + int64_t n = ((int64_t*)&l[idx & MASK])[0]; \ + int32_t d = ((int32_t*)&l[idx & MASK])[2]; \ + int64_t q = n / (d | 0x5); \ + ((int64_t*)&l[idx & MASK])[0] = n ^ q; \ + if (IS_CN_HEAVY_XHV) { \ + d = ~d; \ + } \ + \ + idx = d ^ q; \ + } \ + if (BASE == Algorithm::CN_2) { \ + b1 = b0; \ + } \ + b0 = c; + + +#define CONST_INIT(ctx, n) \ + __m128i mc##n; \ + __m128i division_result_xmm_##n; \ + __m128i sqrt_result_xmm_##n; \ + if (BASE == Algorithm::CN_1) { \ + mc##n = _mm_set_epi64x(*reinterpret_cast(input + n * size + 35) ^ \ + *(reinterpret_cast((ctx)->state) + 24), 0); \ + } \ + if (BASE == Algorithm::CN_2) { \ + division_result_xmm_##n = _mm_cvtsi64_si128(h##n[12]); \ + sqrt_result_xmm_##n = _mm_cvtsi64_si128(h##n[13]); \ + } \ + __m128i ax##n = _mm_set_epi64x(h##n[1] ^ h##n[5], h##n[0] ^ h##n[4]); \ + __m128i bx##n##0 = _mm_set_epi64x(h##n[3] ^ h##n[7], h##n[2] ^ h##n[6]); \ + __m128i bx##n##1 = _mm_set_epi64x(h##n[9] ^ h##n[11], h##n[8] ^ h##n[10]); \ + __m128i cx##n = _mm_setzero_si128(); \ + VARIANT4_RANDOM_MATH_INIT(n); + + +template +inline void cryptonight_triple_hash(const uint8_t *__restrict__ input, size_t size, uint8_t *__restrict__ output, cryptonight_ctx **__restrict__ ctx, uint64_t height) +{ + constexpr CnAlgo props; + constexpr size_t MASK = props.mask(); + constexpr Algorithm::Id BASE = props.base(); + +# ifdef XMRIG_ALGO_CN_HEAVY + constexpr bool IS_CN_HEAVY_TUBE = ALGO == Algorithm::CN_HEAVY_TUBE; + constexpr bool IS_CN_HEAVY_XHV = ALGO == Algorithm::CN_HEAVY_XHV; +# else + constexpr bool IS_CN_HEAVY_TUBE = false; + constexpr bool IS_CN_HEAVY_XHV = false; +# endif + + if (BASE == Algorithm::CN_1 && size < 43) { + memset(output, 0, 32 * 3); + return; + } + + for (size_t i = 0; i < 3; i++) { + keccak(input + size * i, size, ctx[i]->state); + cn_explode_scratchpad(reinterpret_cast(ctx[i]->state), reinterpret_cast<__m128i*>(ctx[i]->memory)); + } + + uint8_t* l0 = ctx[0]->memory; + uint8_t* l1 = ctx[1]->memory; + uint8_t* l2 = ctx[2]->memory; + uint64_t* h0 = reinterpret_cast(ctx[0]->state); + uint64_t* h1 = reinterpret_cast(ctx[1]->state); + uint64_t* h2 = reinterpret_cast(ctx[2]->state); + + CONST_INIT(ctx[0], 0); + CONST_INIT(ctx[1], 1); + CONST_INIT(ctx[2], 2); + VARIANT2_SET_ROUNDING_MODE(); + + __m128 conc_var0; + __m128 conc_var1; + __m128 conc_var2; + + if (ALGO == Algorithm::CN_CONCEAL) { + SET_ROUNDING_MODE_NEAREST() + conc_var0 = _mm_setzero_ps(); + conc_var1 = _mm_setzero_ps(); + conc_var2 = _mm_setzero_ps(); + } + + uint64_t idx0, idx1, idx2; + idx0 = _mm_cvtsi128_si64(ax0); + idx1 = _mm_cvtsi128_si64(ax1); + idx2 = _mm_cvtsi128_si64(ax2); + + for (size_t i = 0; i < props.iterations(); i++) { + uint64_t hi, lo; + __m128i *ptr0, *ptr1, *ptr2; + + CN_STEP1(ax0, bx00, bx01, cx0, l0, ptr0, idx0); + CN_STEP1(ax1, bx10, bx11, cx1, l1, ptr1, idx1); + CN_STEP1(ax2, bx20, bx21, cx2, l2, ptr2, idx2); + + CN_STEP2(conc_var0, ax0, bx00, bx01, cx0, l0, ptr0, idx0); + CN_STEP2(conc_var1, ax1, bx10, bx11, cx1, l1, ptr1, idx1); + CN_STEP2(conc_var2, ax2, bx20, bx21, cx2, l2, ptr2, idx2); + + CN_STEP3(0, ax0, bx00, bx01, cx0, l0, ptr0, idx0); + CN_STEP3(1, ax1, bx10, bx11, cx1, l1, ptr1, idx1); + CN_STEP3(2, ax2, bx20, bx21, cx2, l2, ptr2, idx2); + + CN_STEP4(0, ax0, bx00, bx01, cx0, l0, mc0, ptr0, idx0); + CN_STEP4(1, ax1, bx10, bx11, cx1, l1, mc1, ptr1, idx1); + CN_STEP4(2, ax2, bx20, bx21, cx2, l2, mc2, ptr2, idx2); + } + + for (size_t i = 0; i < 3; i++) { + cn_implode_scratchpad(reinterpret_cast(ctx[i]->memory), reinterpret_cast<__m128i*>(ctx[i]->state)); + keccakf(reinterpret_cast(ctx[i]->state), 24); + extra_hashes[ctx[i]->state[0] & 3](ctx[i]->state, 200, output + 32 * i); + } +} + + +template +inline void cryptonight_quad_hash(const uint8_t *__restrict__ input, size_t size, uint8_t *__restrict__ output, cryptonight_ctx **__restrict__ ctx, uint64_t height) +{ + constexpr CnAlgo props; + constexpr size_t MASK = props.mask(); + constexpr Algorithm::Id BASE = props.base(); + +# ifdef XMRIG_ALGO_CN_HEAVY + constexpr bool IS_CN_HEAVY_TUBE = ALGO == Algorithm::CN_HEAVY_TUBE; + constexpr bool IS_CN_HEAVY_XHV = ALGO == Algorithm::CN_HEAVY_XHV; +# else + constexpr bool IS_CN_HEAVY_TUBE = false; + constexpr bool IS_CN_HEAVY_XHV = false; +# endif + + if (BASE == Algorithm::CN_1 && size < 43) { + memset(output, 0, 32 * 4); + return; + } + + for (size_t i = 0; i < 4; i++) { + keccak(input + size * i, size, ctx[i]->state); + cn_explode_scratchpad(reinterpret_cast(ctx[i]->state), reinterpret_cast<__m128i*>(ctx[i]->memory)); + } + + uint8_t* l0 = ctx[0]->memory; + uint8_t* l1 = ctx[1]->memory; + uint8_t* l2 = ctx[2]->memory; + uint8_t* l3 = ctx[3]->memory; + uint64_t* h0 = reinterpret_cast(ctx[0]->state); + uint64_t* h1 = reinterpret_cast(ctx[1]->state); + uint64_t* h2 = reinterpret_cast(ctx[2]->state); + uint64_t* h3 = reinterpret_cast(ctx[3]->state); + + CONST_INIT(ctx[0], 0); + CONST_INIT(ctx[1], 1); + CONST_INIT(ctx[2], 2); + CONST_INIT(ctx[3], 3); + VARIANT2_SET_ROUNDING_MODE(); + + __m128 conc_var0; + __m128 conc_var1; + __m128 conc_var2; + __m128 conc_var3; + + if (ALGO == Algorithm::CN_CONCEAL) { + SET_ROUNDING_MODE_NEAREST() + conc_var0 = _mm_setzero_ps(); + conc_var1 = _mm_setzero_ps(); + conc_var2 = _mm_setzero_ps(); + conc_var3 = _mm_setzero_ps(); + } + + uint64_t idx0, idx1, idx2, idx3; + idx0 = _mm_cvtsi128_si64(ax0); + idx1 = _mm_cvtsi128_si64(ax1); + idx2 = _mm_cvtsi128_si64(ax2); + idx3 = _mm_cvtsi128_si64(ax3); + + for (size_t i = 0; i < props.iterations(); i++) { + uint64_t hi, lo; + __m128i *ptr0, *ptr1, *ptr2, *ptr3; + + CN_STEP1(ax0, bx00, bx01, cx0, l0, ptr0, idx0); + CN_STEP1(ax1, bx10, bx11, cx1, l1, ptr1, idx1); + CN_STEP1(ax2, bx20, bx21, cx2, l2, ptr2, idx2); + CN_STEP1(ax3, bx30, bx31, cx3, l3, ptr3, idx3); + + CN_STEP2(conc_var0, ax0, bx00, bx01, cx0, l0, ptr0, idx0); + CN_STEP2(conc_var1, ax1, bx10, bx11, cx1, l1, ptr1, idx1); + CN_STEP2(conc_var2, ax2, bx20, bx21, cx2, l2, ptr2, idx2); + CN_STEP2(conc_var3, ax3, bx30, bx31, cx3, l3, ptr3, idx3); + + CN_STEP3(0, ax0, bx00, bx01, cx0, l0, ptr0, idx0); + CN_STEP3(1, ax1, bx10, bx11, cx1, l1, ptr1, idx1); + CN_STEP3(2, ax2, bx20, bx21, cx2, l2, ptr2, idx2); + CN_STEP3(3, ax3, bx30, bx31, cx3, l3, ptr3, idx3); + + CN_STEP4(0, ax0, bx00, bx01, cx0, l0, mc0, ptr0, idx0); + CN_STEP4(1, ax1, bx10, bx11, cx1, l1, mc1, ptr1, idx1); + CN_STEP4(2, ax2, bx20, bx21, cx2, l2, mc2, ptr2, idx2); + CN_STEP4(3, ax3, bx30, bx31, cx3, l3, mc3, ptr3, idx3); + } + + for (size_t i = 0; i < 4; i++) { + cn_implode_scratchpad(reinterpret_cast(ctx[i]->memory), reinterpret_cast<__m128i*>(ctx[i]->state)); + keccakf(reinterpret_cast(ctx[i]->state), 24); + extra_hashes[ctx[i]->state[0] & 3](ctx[i]->state, 200, output + 32 * i); + } +} + + +template +inline void cryptonight_penta_hash(const uint8_t *__restrict__ input, size_t size, uint8_t *__restrict__ output, cryptonight_ctx **__restrict__ ctx, uint64_t height) +{ + constexpr CnAlgo props; + constexpr size_t MASK = props.mask(); + constexpr Algorithm::Id BASE = props.base(); + +# ifdef XMRIG_ALGO_CN_HEAVY + constexpr bool IS_CN_HEAVY_TUBE = ALGO == Algorithm::CN_HEAVY_TUBE; + constexpr bool IS_CN_HEAVY_XHV = ALGO == Algorithm::CN_HEAVY_XHV; +# else + constexpr bool IS_CN_HEAVY_TUBE = false; + constexpr bool IS_CN_HEAVY_XHV = false; +# endif + + if (BASE == Algorithm::CN_1 && size < 43) { + memset(output, 0, 32 * 5); + return; + } + + for (size_t i = 0; i < 5; i++) { + keccak(input + size * i, size, ctx[i]->state); + cn_explode_scratchpad(reinterpret_cast(ctx[i]->state), reinterpret_cast<__m128i*>(ctx[i]->memory)); + } + + uint8_t* l0 = ctx[0]->memory; + uint8_t* l1 = ctx[1]->memory; + uint8_t* l2 = ctx[2]->memory; + uint8_t* l3 = ctx[3]->memory; + uint8_t* l4 = ctx[4]->memory; + uint64_t* h0 = reinterpret_cast(ctx[0]->state); + uint64_t* h1 = reinterpret_cast(ctx[1]->state); + uint64_t* h2 = reinterpret_cast(ctx[2]->state); + uint64_t* h3 = reinterpret_cast(ctx[3]->state); + uint64_t* h4 = reinterpret_cast(ctx[4]->state); + + CONST_INIT(ctx[0], 0); + CONST_INIT(ctx[1], 1); + CONST_INIT(ctx[2], 2); + CONST_INIT(ctx[3], 3); + CONST_INIT(ctx[4], 4); + VARIANT2_SET_ROUNDING_MODE(); + + __m128 conc_var0; + __m128 conc_var1; + __m128 conc_var2; + __m128 conc_var3; + __m128 conc_var4; + + if (ALGO == Algorithm::CN_CONCEAL) { + SET_ROUNDING_MODE_NEAREST() + conc_var0 = _mm_setzero_ps(); + conc_var1 = _mm_setzero_ps(); + conc_var2 = _mm_setzero_ps(); + conc_var3 = _mm_setzero_ps(); + conc_var4 = _mm_setzero_ps(); + } + + uint64_t idx0, idx1, idx2, idx3, idx4; + idx0 = _mm_cvtsi128_si64(ax0); + idx1 = _mm_cvtsi128_si64(ax1); + idx2 = _mm_cvtsi128_si64(ax2); + idx3 = _mm_cvtsi128_si64(ax3); + idx4 = _mm_cvtsi128_si64(ax4); + + for (size_t i = 0; i < props.iterations(); i++) { + uint64_t hi, lo; + __m128i *ptr0, *ptr1, *ptr2, *ptr3, *ptr4; + + CN_STEP1(ax0, bx00, bx01, cx0, l0, ptr0, idx0); + CN_STEP1(ax1, bx10, bx11, cx1, l1, ptr1, idx1); + CN_STEP1(ax2, bx20, bx21, cx2, l2, ptr2, idx2); + CN_STEP1(ax3, bx30, bx31, cx3, l3, ptr3, idx3); + CN_STEP1(ax4, bx40, bx41, cx4, l4, ptr4, idx4); + + CN_STEP2(conc_var0, ax0, bx00, bx01, cx0, l0, ptr0, idx0); + CN_STEP2(conc_var1,ax1, bx10, bx11, cx1, l1, ptr1, idx1); + CN_STEP2(conc_var2,ax2, bx20, bx21, cx2, l2, ptr2, idx2); + CN_STEP2(conc_var3,ax3, bx30, bx31, cx3, l3, ptr3, idx3); + CN_STEP2(conc_var4,ax4, bx40, bx41, cx4, l4, ptr4, idx4); + + CN_STEP3(0, ax0, bx00, bx01, cx0, l0, ptr0, idx0); + CN_STEP3(1, ax1, bx10, bx11, cx1, l1, ptr1, idx1); + CN_STEP3(2, ax2, bx20, bx21, cx2, l2, ptr2, idx2); + CN_STEP3(3, ax3, bx30, bx31, cx3, l3, ptr3, idx3); + CN_STEP3(4, ax4, bx40, bx41, cx4, l4, ptr4, idx4); + + CN_STEP4(0, ax0, bx00, bx01, cx0, l0, mc0, ptr0, idx0); + CN_STEP4(1, ax1, bx10, bx11, cx1, l1, mc1, ptr1, idx1); + CN_STEP4(2, ax2, bx20, bx21, cx2, l2, mc2, ptr2, idx2); + CN_STEP4(3, ax3, bx30, bx31, cx3, l3, mc3, ptr3, idx3); + CN_STEP4(4, ax4, bx40, bx41, cx4, l4, mc4, ptr4, idx4); + } + + for (size_t i = 0; i < 5; i++) { + cn_implode_scratchpad(reinterpret_cast(ctx[i]->memory), reinterpret_cast<__m128i*>(ctx[i]->state)); + keccakf(reinterpret_cast(ctx[i]->state), 24); + extra_hashes[ctx[i]->state[0] & 3](ctx[i]->state, 200, output + 32 * i); + } +} + + +} /* namespace xmrig */ + + +#endif /* XMRIG_CRYPTONIGHT_X86_H */ diff --git a/src/crypto/SSE2NEON.h b/src/crypto/cn/SSE2NEON.h similarity index 99% rename from src/crypto/SSE2NEON.h rename to src/crypto/cn/SSE2NEON.h index 0b8413fc..6a00448d 100644 --- a/src/crypto/SSE2NEON.h +++ b/src/crypto/cn/SSE2NEON.h @@ -1189,12 +1189,6 @@ FORCE_INLINE __m128i _mm_cmpgt_epi32(__m128i a, __m128i b) return vreinterpretq_m128i_u32(vcgtq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); } -// Compares the 4 signed 32-bit integers in a and the 4 signed 32-bit integers in b for greater than. https://msdn.microsoft.com/en-us/library/vstudio/1s9f2z0y(v=vs.100).aspx -FORCE_INLINE __m128i _mm_cmpeq_epi32(__m128i a, __m128i b) -{ - return vreinterpretq_m128i_u32(vceqq_s32(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b))); -} - // Compares the four 32-bit floats in a and b to check if any values are NaN. Ordered compare between each value returns true for "orderable" and false for "not orderable" (NaN). https://msdn.microsoft.com/en-us/library/vstudio/0h9w00fx(v=vs.100).aspx // see also: // http://stackoverflow.com/questions/8627331/what-does-ordered-unordered-comparison-mean diff --git a/src/crypto/asm/CryptonightR_soft_aes_template.inc b/src/crypto/cn/asm/CryptonightR_soft_aes_template.inc similarity index 99% rename from src/crypto/asm/CryptonightR_soft_aes_template.inc rename to src/crypto/cn/asm/CryptonightR_soft_aes_template.inc index 40c7874d..e9e1bb4f 100644 --- a/src/crypto/asm/CryptonightR_soft_aes_template.inc +++ b/src/crypto/cn/asm/CryptonightR_soft_aes_template.inc @@ -6,6 +6,8 @@ PUBLIC FN_PREFIX(CryptonightR_soft_aes_template_end) ALIGN(64) FN_PREFIX(CryptonightR_soft_aes_template_part1): + mov rcx, [rcx] + mov QWORD PTR [rsp+8], rcx push rbx push rbp diff --git a/src/crypto/asm/win/CryptonightR_soft_aes_template.inc b/src/crypto/cn/asm/CryptonightR_soft_aes_template_win.inc similarity index 86% rename from src/crypto/asm/win/CryptonightR_soft_aes_template.inc rename to src/crypto/cn/asm/CryptonightR_soft_aes_template_win.inc index c4a0559b..589192ca 100644 --- a/src/crypto/asm/win/CryptonightR_soft_aes_template.inc +++ b/src/crypto/cn/asm/CryptonightR_soft_aes_template_win.inc @@ -1,11 +1,13 @@ -PUBLIC FN_PREFIX(CryptonightR_soft_aes_template_part1) -PUBLIC FN_PREFIX(CryptonightR_soft_aes_template_mainloop) -PUBLIC FN_PREFIX(CryptonightR_soft_aes_template_part2) -PUBLIC FN_PREFIX(CryptonightR_soft_aes_template_part3) -PUBLIC FN_PREFIX(CryptonightR_soft_aes_template_end) +PUBLIC CryptonightR_soft_aes_template_part1 +PUBLIC CryptonightR_soft_aes_template_mainloop +PUBLIC CryptonightR_soft_aes_template_part2 +PUBLIC CryptonightR_soft_aes_template_part3 +PUBLIC CryptonightR_soft_aes_template_end ALIGN(64) -FN_PREFIX(CryptonightR_soft_aes_template_part1): +CryptonightR_soft_aes_template_part1: + mov rcx, [rcx] + mov QWORD PTR [rsp+8], rcx push rbx push rbp @@ -33,14 +35,14 @@ FN_PREFIX(CryptonightR_soft_aes_template_part1): xor r8, QWORD PTR [rcx] mov r9, QWORD PTR [rcx+40] xor r9, QWORD PTR [rcx+8] - movd xmm4, rax + movq xmm4, rax mov rdx, QWORD PTR [rcx+56] xor rdx, QWORD PTR [rcx+24] mov r11, QWORD PTR [rcx+224] mov rcx, QWORD PTR [rcx+88] xor rcx, QWORD PTR [r10+72] mov rax, QWORD PTR [r10+80] - movd xmm0, rdx + movq xmm0, rdx xor rax, QWORD PTR [r10+64] movaps XMMWORD PTR [rsp+16], xmm6 @@ -52,31 +54,31 @@ FN_PREFIX(CryptonightR_soft_aes_template_part1): movaps XMMWORD PTR [rsp+112], xmm12 movaps XMMWORD PTR [rsp+128], xmm13 - movd xmm5, rax + movq xmm5, rax mov rax, r8 punpcklqdq xmm4, xmm0 and eax, 2097136 - movd xmm10, QWORD PTR [r10+96] - movd xmm0, rcx + movq xmm10, QWORD PTR [r10+96] + movq xmm0, rcx mov rcx, QWORD PTR [r10+104] xorps xmm9, xmm9 mov QWORD PTR [rsp+328], rax - movd xmm12, r11 + movq xmm12, r11 mov QWORD PTR [rsp+320], r9 punpcklqdq xmm5, xmm0 - movd xmm13, rcx + movq xmm13, rcx mov r12d, 524288 ALIGN(64) -FN_PREFIX(CryptonightR_soft_aes_template_mainloop): +CryptonightR_soft_aes_template_mainloop: movd xmm11, r12d mov r12, QWORD PTR [r10+272] lea r13, QWORD PTR [rax+r11] mov esi, DWORD PTR [r13] - movd xmm0, r9 + movq xmm0, r9 mov r10d, DWORD PTR [r13+4] - movd xmm7, r8 + movq xmm7, r8 mov ebp, DWORD PTR [r13+12] mov r14d, DWORD PTR [r13+8] mov rdx, QWORD PTR [rsp+328] @@ -125,7 +127,7 @@ FN_PREFIX(CryptonightR_soft_aes_template_mainloop): shr ebp, 8 movd xmm1, r11d add ebp, 256 - movd r11, xmm12 + movq r11, xmm12 mov r9d, DWORD PTR [r12+rcx*4] xor r9d, DWORD PTR [r12+rsi*4] mov eax, DWORD PTR [r12+rbp*4] @@ -155,10 +157,10 @@ FN_PREFIX(CryptonightR_soft_aes_template_mainloop): paddq xmm0, xmm5 movdqu XMMWORD PTR [rcx+r11], xmm0 movdqu XMMWORD PTR [rax+r11], xmm2 - movd rcx, xmm13 + movq rcx, xmm13 paddq xmm1, xmm7 movdqu XMMWORD PTR [rdx+r11], xmm1 - movd rdi, xmm6 + movq rdi, xmm6 mov r10, rdi and r10d, 2097136 movdqa xmm0, xmm6 @@ -197,7 +199,7 @@ FN_PREFIX(CryptonightR_soft_aes_template_mainloop): psrldq xmm0, 8 movd r9d, xmm0 -FN_PREFIX(CryptonightR_soft_aes_template_part2): +CryptonightR_soft_aes_template_part2: mov rsp, r10 mov [rsp+144], ebx mov [rsp+148], esi @@ -254,9 +256,9 @@ FN_PREFIX(CryptonightR_soft_aes_template_part2): mov QWORD PTR [rsp+320], r9 mov QWORD PTR [rsp+328], rax sub r12d, 1 - jne FN_PREFIX(CryptonightR_soft_aes_template_mainloop) + jne CryptonightR_soft_aes_template_mainloop -FN_PREFIX(CryptonightR_soft_aes_template_part3): +CryptonightR_soft_aes_template_part3: movaps xmm6, XMMWORD PTR [rsp+16] movaps xmm7, XMMWORD PTR [rsp+32] movaps xmm8, XMMWORD PTR [rsp+48] @@ -276,4 +278,4 @@ FN_PREFIX(CryptonightR_soft_aes_template_part3): pop rbp pop rbx ret -FN_PREFIX(CryptonightR_soft_aes_template_end): +CryptonightR_soft_aes_template_end: diff --git a/src/crypto/asm/CryptonightR_template.S b/src/crypto/cn/asm/CryptonightR_template.S similarity index 99% rename from src/crypto/asm/CryptonightR_template.S rename to src/crypto/cn/asm/CryptonightR_template.S index d2974d16..bfedeb30 100644 --- a/src/crypto/asm/CryptonightR_template.S +++ b/src/crypto/cn/asm/CryptonightR_template.S @@ -1593,3 +1593,7 @@ FN_PREFIX(CryptonightR_instruction_mov254): FN_PREFIX(CryptonightR_instruction_mov255): FN_PREFIX(CryptonightR_instruction_mov256): + +#if defined(__linux__) && defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/src/crypto/asm/win/CryptonightR_template.asm b/src/crypto/cn/asm/CryptonightR_template.asm similarity index 100% rename from src/crypto/asm/win/CryptonightR_template.asm rename to src/crypto/cn/asm/CryptonightR_template.asm diff --git a/src/crypto/asm/CryptonightR_template.h b/src/crypto/cn/asm/CryptonightR_template.h similarity index 100% rename from src/crypto/asm/CryptonightR_template.h rename to src/crypto/cn/asm/CryptonightR_template.h diff --git a/src/crypto/asm/CryptonightR_template.inc b/src/crypto/cn/asm/CryptonightR_template.inc similarity index 97% rename from src/crypto/asm/CryptonightR_template.inc rename to src/crypto/cn/asm/CryptonightR_template.inc index b54486a5..61b6b985 100644 --- a/src/crypto/asm/CryptonightR_template.inc +++ b/src/crypto/cn/asm/CryptonightR_template.inc @@ -12,6 +12,8 @@ PUBLIC FN_PREFIX(CryptonightR_template_double_end) ALIGN(64) FN_PREFIX(CryptonightR_template_part1): + mov rcx, [rcx] + mov QWORD PTR [rsp+16], rbx mov QWORD PTR [rsp+24], rbp mov QWORD PTR [rsp+32], rsi @@ -70,29 +72,30 @@ FN_PREFIX(CryptonightR_template_mainloop): aesenc xmm5, xmm4 - mov r12d, r9d + mov r13d, r9d mov eax, r9d xor r9d, 48 - xor r12d, 16 + xor r13d, 16 xor eax, 32 movdqu xmm0, XMMWORD PTR [r9+r11] movaps xmm3, xmm0 - movdqu xmm2, XMMWORD PTR [r12+r11] + movdqu xmm2, XMMWORD PTR [r13+r11] movdqu xmm1, XMMWORD PTR [rax+r11] pxor xmm0, xmm2 pxor xmm5, xmm1 pxor xmm5, xmm0 - paddq xmm3, xmm7 - paddq xmm2, xmm6 - paddq xmm1, xmm4 - movdqu XMMWORD PTR [r12+r11], xmm3 - movdqu XMMWORD PTR [rax+r11], xmm2 - movdqu XMMWORD PTR [r9+r11], xmm1 movq r12, xmm5 movd r10d, xmm5 and r10d, 2097136 + paddq xmm3, xmm7 + paddq xmm2, xmm6 + paddq xmm1, xmm4 + movdqu XMMWORD PTR [r13+r11], xmm3 + movdqu XMMWORD PTR [rax+r11], xmm2 + movdqu XMMWORD PTR [r9+r11], xmm1 + movdqa xmm0, xmm5 pxor xmm0, xmm6 movdqu XMMWORD PTR [rdx], xmm0 @@ -102,14 +105,16 @@ FN_PREFIX(CryptonightR_template_mainloop): shl rdx, 32 or r13, rdx - xor r13, QWORD PTR [r10+r11] - mov r14, QWORD PTR [r10+r11+8] - movd eax, xmm6 movd edx, xmm7 pextrd r9d, xmm7, 2 + xor r13, QWORD PTR [r10+r11] + mov r14, QWORD PTR [r10+r11+8] + FN_PREFIX(CryptonightR_template_part2): + lea rcx, [r10+r11] + mov eax, edi mov edx, ebp shl rdx, 32 @@ -124,6 +129,8 @@ FN_PREFIX(CryptonightR_template_part2): mov rax, r13 mul r12 + add r15, rax + add rsp, rdx mov r9d, r10d mov r12d, r10d @@ -145,13 +152,10 @@ FN_PREFIX(CryptonightR_template_part2): movdqu XMMWORD PTR [r10+r11], xmm3 movdqa xmm7, xmm6 - add r15, rax - add rsp, rdx - xor r10, 48 - mov QWORD PTR [r10+r11], rsp + mov QWORD PTR [rcx], rsp xor rsp, r13 mov r9d, esp - mov QWORD PTR [r10+r11+8], r15 + mov QWORD PTR [rcx+8], r15 and r9d, 2097136 xor r15, r14 movdqa xmm6, xmm5 @@ -181,6 +185,9 @@ FN_PREFIX(CryptonightR_template_end): ALIGN(64) FN_PREFIX(CryptonightR_template_double_part1): + mov rdx, [rcx+8] + mov rcx, [rcx] + mov QWORD PTR [rsp+24], rbx push rbp push rsi diff --git a/src/crypto/asm/win/CryptonightR_template.inc b/src/crypto/cn/asm/CryptonightR_template_win.inc similarity index 79% rename from src/crypto/asm/win/CryptonightR_template.inc rename to src/crypto/cn/asm/CryptonightR_template_win.inc index 1dae434a..1bb89eb1 100644 --- a/src/crypto/asm/win/CryptonightR_template.inc +++ b/src/crypto/cn/asm/CryptonightR_template_win.inc @@ -1,17 +1,19 @@ -PUBLIC FN_PREFIX(CryptonightR_template_part1) -PUBLIC FN_PREFIX(CryptonightR_template_mainloop) -PUBLIC FN_PREFIX(CryptonightR_template_part2) -PUBLIC FN_PREFIX(CryptonightR_template_part3) -PUBLIC FN_PREFIX(CryptonightR_template_end) -PUBLIC FN_PREFIX(CryptonightR_template_double_part1) -PUBLIC FN_PREFIX(CryptonightR_template_double_mainloop) -PUBLIC FN_PREFIX(CryptonightR_template_double_part2) -PUBLIC FN_PREFIX(CryptonightR_template_double_part3) -PUBLIC FN_PREFIX(CryptonightR_template_double_part4) -PUBLIC FN_PREFIX(CryptonightR_template_double_end) +PUBLIC CryptonightR_template_part1 +PUBLIC CryptonightR_template_mainloop +PUBLIC CryptonightR_template_part2 +PUBLIC CryptonightR_template_part3 +PUBLIC CryptonightR_template_end +PUBLIC CryptonightR_template_double_part1 +PUBLIC CryptonightR_template_double_mainloop +PUBLIC CryptonightR_template_double_part2 +PUBLIC CryptonightR_template_double_part3 +PUBLIC CryptonightR_template_double_part4 +PUBLIC CryptonightR_template_double_end ALIGN(64) -FN_PREFIX(CryptonightR_template_part1): +CryptonightR_template_part1: + mov rcx, [rcx] + mov QWORD PTR [rsp+16], rbx mov QWORD PTR [rsp+24], rbp mov QWORD PTR [rsp+32], rsi @@ -36,22 +38,22 @@ FN_PREFIX(CryptonightR_template_part1): mov rax, QWORD PTR [rdx+48] xor rax, QWORD PTR [rdx+16] movaps XMMWORD PTR [rsp+48], xmm6 - movd xmm0, r12 + movq xmm0, r12 movaps XMMWORD PTR [rsp+32], xmm7 movaps XMMWORD PTR [rsp+16], xmm8 movaps XMMWORD PTR [rsp], xmm9 mov r12, QWORD PTR [rdx+88] xor r12, QWORD PTR [rdx+72] - movd xmm6, rax + movq xmm6, rax mov rax, QWORD PTR [rdx+80] xor rax, QWORD PTR [rdx+64] punpcklqdq xmm6, xmm0 and r9d, 2097136 - movd xmm0, r12 - movd xmm7, rax + movq xmm0, r12 + movq xmm7, rax punpcklqdq xmm7, xmm0 mov r10d, r9d - movd xmm9, rsp + movq xmm9, rsp mov rsp, r8 mov r8d, 524288 @@ -61,38 +63,39 @@ FN_PREFIX(CryptonightR_template_part1): mov ebp, [rdx+108] ALIGN(64) -FN_PREFIX(CryptonightR_template_mainloop): +CryptonightR_template_mainloop: movdqa xmm5, XMMWORD PTR [r9+r11] - movd xmm0, r15 - movd xmm4, rsp + movq xmm0, r15 + movq xmm4, rsp punpcklqdq xmm4, xmm0 lea rdx, QWORD PTR [r9+r11] aesenc xmm5, xmm4 - mov r12d, r9d + mov r13d, r9d mov eax, r9d xor r9d, 48 - xor r12d, 16 + xor r13d, 16 xor eax, 32 movdqu xmm0, XMMWORD PTR [r9+r11] movaps xmm3, xmm0 - movdqu xmm2, XMMWORD PTR [r12+r11] + movdqu xmm2, XMMWORD PTR [r13+r11] movdqu xmm1, XMMWORD PTR [rax+r11] pxor xmm0, xmm2 pxor xmm5, xmm1 pxor xmm5, xmm0 + + movq r12, xmm5 + movd r10d, xmm5 + and r10d, 2097136 + paddq xmm3, xmm7 paddq xmm2, xmm6 paddq xmm1, xmm4 - movdqu XMMWORD PTR [r12+r11], xmm3 + movdqu XMMWORD PTR [r13+r11], xmm3 movdqu XMMWORD PTR [rax+r11], xmm2 movdqu XMMWORD PTR [r9+r11], xmm1 - movd r12, xmm5 - movd r10d, xmm5 - and r10d, 2097136 - movdqa xmm0, xmm5 pxor xmm0, xmm6 movdqu XMMWORD PTR [rdx], xmm0 @@ -102,14 +105,16 @@ FN_PREFIX(CryptonightR_template_mainloop): shl rdx, 32 or r13, rdx - xor r13, QWORD PTR [r10+r11] - mov r14, QWORD PTR [r10+r11+8] - movd eax, xmm6 movd edx, xmm7 pextrd r9d, xmm7, 2 -FN_PREFIX(CryptonightR_template_part2): + xor r13, QWORD PTR [r10+r11] + mov r14, QWORD PTR [r10+r11+8] + +CryptonightR_template_part2: + lea rcx, [r10+r11] + mov eax, edi mov edx, ebp shl rdx, 32 @@ -124,6 +129,8 @@ FN_PREFIX(CryptonightR_template_part2): mov rax, r13 mul r12 + add r15, rax + add rsp, rdx mov r9d, r10d mov r12d, r10d @@ -145,21 +152,18 @@ FN_PREFIX(CryptonightR_template_part2): movdqu XMMWORD PTR [r10+r11], xmm3 movdqa xmm7, xmm6 - add r15, rax - add rsp, rdx - xor r10, 48 - mov QWORD PTR [r10+r11], rsp + mov QWORD PTR [rcx], rsp xor rsp, r13 mov r9d, esp - mov QWORD PTR [r10+r11+8], r15 + mov QWORD PTR [rcx+8], r15 and r9d, 2097136 xor r15, r14 movdqa xmm6, xmm5 dec r8d - jnz FN_PREFIX(CryptonightR_template_mainloop) + jnz CryptonightR_template_mainloop -FN_PREFIX(CryptonightR_template_part3): - movd rsp, xmm9 +CryptonightR_template_part3: + movq rsp, xmm9 mov rbx, QWORD PTR [rsp+136] mov rbp, QWORD PTR [rsp+144] @@ -177,10 +181,13 @@ FN_PREFIX(CryptonightR_template_part3): pop r11 pop r10 ret 0 -FN_PREFIX(CryptonightR_template_end): +CryptonightR_template_end: ALIGN(64) -FN_PREFIX(CryptonightR_template_double_part1): +CryptonightR_template_double_part1: + mov rdx, [rcx+8] + mov rcx, [rcx] + mov QWORD PTR [rsp+24], rbx push rbp push rsi @@ -204,7 +211,7 @@ FN_PREFIX(CryptonightR_template_double_part1): xor rax, QWORD PTR [r8+16] mov r15, QWORD PTR [rdx+32] xor r15, QWORD PTR [rdx] - movd xmm0, rcx + movq xmm0, rcx mov rcx, QWORD PTR [r8+88] xor rcx, QWORD PTR [r8+72] mov r13, QWORD PTR [rdx+40] @@ -220,7 +227,7 @@ FN_PREFIX(CryptonightR_template_double_part1): movaps XMMWORD PTR [rsp+272], xmm13 movaps XMMWORD PTR [rsp+288], xmm14 movaps XMMWORD PTR [rsp+304], xmm15 - movd xmm7, rax + movq xmm7, rax mov rax, QWORD PTR [r8+80] xor rax, QWORD PTR [r8+64] @@ -231,41 +238,41 @@ FN_PREFIX(CryptonightR_template_double_part1): mov r8d, r15d punpcklqdq xmm7, xmm0 - movd xmm0, rcx + movq xmm0, rcx mov rcx, QWORD PTR [rdx+56] xor rcx, QWORD PTR [rdx+24] - movd xmm9, rax + movq xmm9, rax mov QWORD PTR [rsp+128], rsi mov rax, QWORD PTR [rdx+48] xor rax, QWORD PTR [rdx+16] punpcklqdq xmm9, xmm0 - movd xmm0, rcx + movq xmm0, rcx mov rcx, QWORD PTR [rdx+88] xor rcx, QWORD PTR [rdx+72] - movd xmm8, rax + movq xmm8, rax mov QWORD PTR [rsp+136], rdi mov rax, QWORD PTR [rdx+80] xor rax, QWORD PTR [rdx+64] punpcklqdq xmm8, xmm0 and r8d, 2097136 - movd xmm0, rcx + movq xmm0, rcx mov r11d, 524288 - movd xmm10, rax + movq xmm10, rax punpcklqdq xmm10, xmm0 - movd xmm14, QWORD PTR [rsp+128] - movd xmm15, QWORD PTR [rsp+136] + movq xmm14, QWORD PTR [rsp+128] + movq xmm15, QWORD PTR [rsp+136] ALIGN(64) -FN_PREFIX(CryptonightR_template_double_mainloop): +CryptonightR_template_double_mainloop: movdqu xmm6, XMMWORD PTR [rbx+rsi] - movd xmm0, r12 + movq xmm0, r12 mov ecx, ebx - movd xmm3, r14 + movq xmm3, r14 punpcklqdq xmm3, xmm0 xor ebx, 16 aesenc xmm6, xmm3 - movd xmm4, r15 + movq xmm4, r15 movdqu xmm0, XMMWORD PTR [rbx+rsi] pxor xmm6, xmm0 xor ebx, 48 @@ -279,7 +286,7 @@ FN_PREFIX(CryptonightR_template_double_mainloop): xor rax, 32 movdqu xmm0, XMMWORD PTR [rbx+rsi] pxor xmm6, xmm0 - movd rdx, xmm6 + movq rdx, xmm6 movdqu XMMWORD PTR [rbx+rsi], xmm1 paddq xmm0, xmm9 movdqu XMMWORD PTR [rax+rsi], xmm0 @@ -290,7 +297,7 @@ FN_PREFIX(CryptonightR_template_double_mainloop): movdqu xmm5, XMMWORD PTR [r8+rdi] and esi, 2097136 mov ecx, r8d - movd xmm0, r13 + movq xmm0, r13 punpcklqdq xmm4, xmm0 xor r8d, 16 aesenc xmm5, xmm4 @@ -313,20 +320,20 @@ FN_PREFIX(CryptonightR_template_double_mainloop): movdqa xmm0, xmm5 pxor xmm0, xmm8 movdqu XMMWORD PTR [rcx+rdi], xmm0 - movd rdi, xmm5 - movd rcx, xmm14 + movq rdi, xmm5 + movq rcx, xmm14 mov ebp, edi mov r8, QWORD PTR [rcx+rsi] mov r10, QWORD PTR [rcx+rsi+8] lea r9, QWORD PTR [rcx+rsi] xor esi, 16 - movd xmm0, rsp - movd xmm1, rsi - movd xmm2, rdi - movd xmm11, rbp - movd xmm12, r15 - movd xmm13, rdx + movq xmm0, rsp + movq xmm1, rsi + movq xmm2, rdi + movq xmm11, rbp + movq xmm12, r15 + movq xmm13, rdx mov [rsp+104], rcx mov [rsp+112], r9 @@ -347,7 +354,7 @@ FN_PREFIX(CryptonightR_template_double_mainloop): movd edx, xmm9 pextrd r9d, xmm9, 2 -FN_PREFIX(CryptonightR_template_double_part2): +CryptonightR_template_double_part2: mov eax, edi mov edx, ebp @@ -361,17 +368,17 @@ FN_PREFIX(CryptonightR_template_double_part2): or rax, rdx xor r12, rax - movd rsp, xmm0 + movq rsp, xmm0 mov DWORD PTR [rsp+16], ebx mov DWORD PTR [rsp+20], esi mov DWORD PTR [rsp+24], edi mov DWORD PTR [rsp+28], ebp - movd rsi, xmm1 - movd rdi, xmm2 - movd rbp, xmm11 - movd r15, xmm12 - movd rdx, xmm13 + movq rsi, xmm1 + movq rdi, xmm2 + movq rbp, xmm11 + movq r15, xmm12 + movq rdx, xmm13 mov rcx, [rsp+104] mov r9, [rsp+112] @@ -404,7 +411,7 @@ FN_PREFIX(CryptonightR_template_double_part2): mov QWORD PTR [r9+8], r12 xor r12, r10 mov QWORD PTR [r9], r14 - movd rcx, xmm15 + movq rcx, xmm15 xor r14, rbx mov r10d, ebp mov ebx, r14d @@ -413,12 +420,12 @@ FN_PREFIX(CryptonightR_template_double_part2): mov r8, QWORD PTR [r10+rcx] mov r9, QWORD PTR [r10+rcx+8] - movd xmm0, rsp - movd xmm1, rbx - movd xmm2, rsi - movd xmm11, rdi - movd xmm12, rbp - movd xmm13, r15 + movq xmm0, rsp + movq xmm1, rbx + movq xmm2, rsi + movq xmm11, rdi + movq xmm12, rbp + movq xmm13, r15 mov [rsp+104], rcx mov [rsp+112], r9 @@ -433,7 +440,7 @@ FN_PREFIX(CryptonightR_template_double_part2): or rax, rdx xor r8, rax - movd xmm3, r8 + movq xmm3, r8 movd esp, xmm4 pextrd r15d, xmm4, 2 @@ -441,9 +448,9 @@ FN_PREFIX(CryptonightR_template_double_part2): movd edx, xmm10 pextrd r9d, xmm10, 2 -FN_PREFIX(CryptonightR_template_double_part3): +CryptonightR_template_double_part3: - movd r15, xmm13 + movq r15, xmm13 mov eax, edi mov edx, ebp @@ -457,16 +464,16 @@ FN_PREFIX(CryptonightR_template_double_part3): or rax, rdx xor r13, rax - movd rsp, xmm0 + movq rsp, xmm0 mov DWORD PTR [rsp], ebx mov DWORD PTR [rsp+4], esi mov DWORD PTR [rsp+8], edi mov DWORD PTR [rsp+12], ebp - movd rbx, xmm1 - movd rsi, xmm2 - movd rdi, xmm11 - movd rbp, xmm12 + movq rbx, xmm1 + movq rsi, xmm2 + movq rdi, xmm11 + movq rbp, xmm12 mov rcx, [rsp+104] mov r9, [rsp+112] @@ -492,7 +499,7 @@ FN_PREFIX(CryptonightR_template_double_part3): movdqu XMMWORD PTR [rbp+rcx], xmm2 paddq xmm0, xmm10 movdqu XMMWORD PTR [rax+rcx], xmm0 - movd rax, xmm3 + movq rax, xmm3 movdqa xmm10, xmm8 mov QWORD PTR [r10+rcx], r15 movdqa xmm8, xmm5 @@ -502,9 +509,9 @@ FN_PREFIX(CryptonightR_template_double_part3): xor r13, r9 and r8d, 2097136 dec r11d - jnz FN_PREFIX(CryptonightR_template_double_mainloop) + jnz CryptonightR_template_double_mainloop -FN_PREFIX(CryptonightR_template_double_part4): +CryptonightR_template_double_part4: mov rbx, QWORD PTR [rsp+400] movaps xmm6, XMMWORD PTR [rsp+160] @@ -526,4 +533,4 @@ FN_PREFIX(CryptonightR_template_double_part4): pop rsi pop rbp ret 0 -FN_PREFIX(CryptonightR_template_double_end): +CryptonightR_template_double_end: diff --git a/src/crypto/asm/CryptonightWOW_soft_aes_template.inc b/src/crypto/cn/asm/CryptonightWOW_soft_aes_template.inc similarity index 99% rename from src/crypto/asm/CryptonightWOW_soft_aes_template.inc rename to src/crypto/cn/asm/CryptonightWOW_soft_aes_template.inc index feea3949..53b7016a 100644 --- a/src/crypto/asm/CryptonightWOW_soft_aes_template.inc +++ b/src/crypto/cn/asm/CryptonightWOW_soft_aes_template.inc @@ -6,6 +6,8 @@ PUBLIC FN_PREFIX(CryptonightWOW_soft_aes_template_end) ALIGN(64) FN_PREFIX(CryptonightWOW_soft_aes_template_part1): + mov rcx, [rcx] + mov QWORD PTR [rsp+8], rcx push rbx push rbp diff --git a/src/crypto/asm/win/CryptonightWOW_soft_aes_template.inc b/src/crypto/cn/asm/CryptonightWOW_soft_aes_template_win.inc similarity index 85% rename from src/crypto/asm/win/CryptonightWOW_soft_aes_template.inc rename to src/crypto/cn/asm/CryptonightWOW_soft_aes_template_win.inc index cc273781..b3202b78 100644 --- a/src/crypto/asm/win/CryptonightWOW_soft_aes_template.inc +++ b/src/crypto/cn/asm/CryptonightWOW_soft_aes_template_win.inc @@ -1,11 +1,13 @@ -PUBLIC FN_PREFIX(CryptonightWOW_soft_aes_template_part1) -PUBLIC FN_PREFIX(CryptonightWOW_soft_aes_template_mainloop) -PUBLIC FN_PREFIX(CryptonightWOW_soft_aes_template_part2) -PUBLIC FN_PREFIX(CryptonightWOW_soft_aes_template_part3) -PUBLIC FN_PREFIX(CryptonightWOW_soft_aes_template_end) +PUBLIC CryptonightWOW_soft_aes_template_part1 +PUBLIC CryptonightWOW_soft_aes_template_mainloop +PUBLIC CryptonightWOW_soft_aes_template_part2 +PUBLIC CryptonightWOW_soft_aes_template_part3 +PUBLIC CryptonightWOW_soft_aes_template_end ALIGN(64) -FN_PREFIX(CryptonightWOW_soft_aes_template_part1): +CryptonightWOW_soft_aes_template_part1: + mov rcx, [rcx] + mov QWORD PTR [rsp+8], rcx push rbx push rbp @@ -33,14 +35,14 @@ FN_PREFIX(CryptonightWOW_soft_aes_template_part1): xor r8, QWORD PTR [rcx] mov r9, QWORD PTR [rcx+40] xor r9, QWORD PTR [rcx+8] - movd xmm4, rax + movq xmm4, rax mov rdx, QWORD PTR [rcx+56] xor rdx, QWORD PTR [rcx+24] mov r11, QWORD PTR [rcx+224] mov rcx, QWORD PTR [rcx+88] xor rcx, QWORD PTR [r10+72] mov rax, QWORD PTR [r10+80] - movd xmm0, rdx + movq xmm0, rdx xor rax, QWORD PTR [r10+64] movaps XMMWORD PTR [rsp+16], xmm6 @@ -52,31 +54,31 @@ FN_PREFIX(CryptonightWOW_soft_aes_template_part1): movaps XMMWORD PTR [rsp+112], xmm12 movaps XMMWORD PTR [rsp+128], xmm13 - movd xmm5, rax + movq xmm5, rax mov rax, r8 punpcklqdq xmm4, xmm0 and eax, 2097136 - movd xmm10, QWORD PTR [r10+96] - movd xmm0, rcx + movq xmm10, QWORD PTR [r10+96] + movq xmm0, rcx mov rcx, QWORD PTR [r10+104] xorps xmm9, xmm9 mov QWORD PTR [rsp+328], rax - movd xmm12, r11 + movq xmm12, r11 mov QWORD PTR [rsp+320], r9 punpcklqdq xmm5, xmm0 - movd xmm13, rcx + movq xmm13, rcx mov r12d, 524288 ALIGN(64) -FN_PREFIX(CryptonightWOW_soft_aes_template_mainloop): +CryptonightWOW_soft_aes_template_mainloop: movd xmm11, r12d mov r12, QWORD PTR [r10+272] lea r13, QWORD PTR [rax+r11] mov esi, DWORD PTR [r13] - movd xmm0, r9 + movq xmm0, r9 mov r10d, DWORD PTR [r13+4] - movd xmm7, r8 + movq xmm7, r8 mov ebp, DWORD PTR [r13+12] mov r14d, DWORD PTR [r13+8] mov rdx, QWORD PTR [rsp+328] @@ -125,7 +127,7 @@ FN_PREFIX(CryptonightWOW_soft_aes_template_mainloop): shr ebp, 8 movd xmm1, r11d add ebp, 256 - movd r11, xmm12 + movq r11, xmm12 mov r9d, DWORD PTR [r12+rcx*4] xor r9d, DWORD PTR [r12+rsi*4] mov eax, DWORD PTR [r12+rbp*4] @@ -152,10 +154,10 @@ FN_PREFIX(CryptonightWOW_soft_aes_template_mainloop): paddq xmm0, xmm5 movdqu XMMWORD PTR [rcx+r11], xmm0 movdqu XMMWORD PTR [rax+r11], xmm2 - movd rcx, xmm13 + movq rcx, xmm13 paddq xmm1, xmm7 movdqu XMMWORD PTR [rdx+r11], xmm1 - movd rdi, xmm6 + movq rdi, xmm6 mov r10, rdi and r10d, 2097136 movdqa xmm0, xmm6 @@ -191,7 +193,7 @@ FN_PREFIX(CryptonightWOW_soft_aes_template_mainloop): movd eax, xmm4 movd edx, xmm5 -FN_PREFIX(CryptonightWOW_soft_aes_template_part2): +CryptonightWOW_soft_aes_template_part2: mov rsp, r10 mov [rsp+144], ebx mov [rsp+148], esi @@ -213,8 +215,8 @@ FN_PREFIX(CryptonightWOW_soft_aes_template_part2): movdqu xmm2, XMMWORD PTR [r9+r11] movdqu xmm1, XMMWORD PTR [rcx+r11] paddq xmm1, xmm7 - movd xmm0, rax - movd xmm3, rdx + movq xmm0, rax + movq xmm3, rdx xor rax, QWORD PTR [r11+rcx+8] xor rdx, QWORD PTR [rcx+r11] punpcklqdq xmm3, xmm0 @@ -241,9 +243,9 @@ FN_PREFIX(CryptonightWOW_soft_aes_template_part2): mov QWORD PTR [rsp+320], r9 mov QWORD PTR [rsp+328], rax sub r12d, 1 - jne FN_PREFIX(CryptonightWOW_soft_aes_template_mainloop) + jne CryptonightWOW_soft_aes_template_mainloop -FN_PREFIX(CryptonightWOW_soft_aes_template_part3): +CryptonightWOW_soft_aes_template_part3: movaps xmm6, XMMWORD PTR [rsp+16] movaps xmm7, XMMWORD PTR [rsp+32] movaps xmm8, XMMWORD PTR [rsp+48] @@ -263,4 +265,4 @@ FN_PREFIX(CryptonightWOW_soft_aes_template_part3): pop rbp pop rbx ret -FN_PREFIX(CryptonightWOW_soft_aes_template_end): +CryptonightWOW_soft_aes_template_end: diff --git a/src/crypto/asm/CryptonightWOW_template.inc b/src/crypto/cn/asm/CryptonightWOW_template.inc similarity index 99% rename from src/crypto/asm/CryptonightWOW_template.inc rename to src/crypto/cn/asm/CryptonightWOW_template.inc index 7183a659..82d455f6 100644 --- a/src/crypto/asm/CryptonightWOW_template.inc +++ b/src/crypto/cn/asm/CryptonightWOW_template.inc @@ -12,6 +12,8 @@ PUBLIC FN_PREFIX(CryptonightWOW_template_double_end) ALIGN(64) FN_PREFIX(CryptonightWOW_template_part1): + mov rcx, [rcx] + mov QWORD PTR [rsp+16], rbx mov QWORD PTR [rsp+24], rbp mov QWORD PTR [rsp+32], rsi @@ -165,6 +167,9 @@ FN_PREFIX(CryptonightWOW_template_end): ALIGN(64) FN_PREFIX(CryptonightWOW_template_double_part1): + mov rdx, [rcx+8] + mov rcx, [rcx] + mov QWORD PTR [rsp+24], rbx push rbp push rsi diff --git a/src/crypto/asm/win/CryptonightWOW_template.inc b/src/crypto/cn/asm/CryptonightWOW_template_win.inc similarity index 79% rename from src/crypto/asm/win/CryptonightWOW_template.inc rename to src/crypto/cn/asm/CryptonightWOW_template_win.inc index 47fbc94f..644c01f1 100644 --- a/src/crypto/asm/win/CryptonightWOW_template.inc +++ b/src/crypto/cn/asm/CryptonightWOW_template_win.inc @@ -1,17 +1,19 @@ -PUBLIC FN_PREFIX(CryptonightWOW_template_part1) -PUBLIC FN_PREFIX(CryptonightWOW_template_mainloop) -PUBLIC FN_PREFIX(CryptonightWOW_template_part2) -PUBLIC FN_PREFIX(CryptonightWOW_template_part3) -PUBLIC FN_PREFIX(CryptonightWOW_template_end) -PUBLIC FN_PREFIX(CryptonightWOW_template_double_part1) -PUBLIC FN_PREFIX(CryptonightWOW_template_double_mainloop) -PUBLIC FN_PREFIX(CryptonightWOW_template_double_part2) -PUBLIC FN_PREFIX(CryptonightWOW_template_double_part3) -PUBLIC FN_PREFIX(CryptonightWOW_template_double_part4) -PUBLIC FN_PREFIX(CryptonightWOW_template_double_end) +PUBLIC CryptonightWOW_template_part1 +PUBLIC CryptonightWOW_template_mainloop +PUBLIC CryptonightWOW_template_part2 +PUBLIC CryptonightWOW_template_part3 +PUBLIC CryptonightWOW_template_end +PUBLIC CryptonightWOW_template_double_part1 +PUBLIC CryptonightWOW_template_double_mainloop +PUBLIC CryptonightWOW_template_double_part2 +PUBLIC CryptonightWOW_template_double_part3 +PUBLIC CryptonightWOW_template_double_part4 +PUBLIC CryptonightWOW_template_double_end ALIGN(64) -FN_PREFIX(CryptonightWOW_template_part1): +CryptonightWOW_template_part1: + mov rcx, [rcx] + mov QWORD PTR [rsp+16], rbx mov QWORD PTR [rsp+24], rbp mov QWORD PTR [rsp+32], rsi @@ -36,22 +38,22 @@ FN_PREFIX(CryptonightWOW_template_part1): mov rax, QWORD PTR [rdx+48] xor rax, QWORD PTR [rdx+16] movaps XMMWORD PTR [rsp+48], xmm6 - movd xmm0, r12 + movq xmm0, r12 movaps XMMWORD PTR [rsp+32], xmm7 movaps XMMWORD PTR [rsp+16], xmm8 movaps XMMWORD PTR [rsp], xmm9 mov r12, QWORD PTR [rdx+88] xor r12, QWORD PTR [rdx+72] - movd xmm6, rax + movq xmm6, rax mov rax, QWORD PTR [rdx+80] xor rax, QWORD PTR [rdx+64] punpcklqdq xmm6, xmm0 and r9d, 2097136 - movd xmm0, r12 - movd xmm7, rax + movq xmm0, r12 + movq xmm7, rax punpcklqdq xmm7, xmm0 mov r10d, r9d - movd xmm9, rsp + movq xmm9, rsp mov rsp, r8 mov r8d, 524288 @@ -61,10 +63,10 @@ FN_PREFIX(CryptonightWOW_template_part1): mov ebp, [rdx+108] ALIGN(64) -FN_PREFIX(CryptonightWOW_template_mainloop): +CryptonightWOW_template_mainloop: movdqa xmm5, XMMWORD PTR [r9+r11] - movd xmm0, r15 - movd xmm4, rsp + movq xmm0, r15 + movq xmm4, rsp punpcklqdq xmm4, xmm0 lea rdx, QWORD PTR [r9+r11] @@ -84,7 +86,7 @@ FN_PREFIX(CryptonightWOW_template_mainloop): paddq xmm2, xmm6 paddq xmm1, xmm4 movdqu XMMWORD PTR [r12+r11], xmm0 - movd r12, xmm5 + movq r12, xmm5 movdqu XMMWORD PTR [rax+r11], xmm2 movdqu XMMWORD PTR [r9+r11], xmm1 @@ -104,11 +106,11 @@ FN_PREFIX(CryptonightWOW_template_mainloop): movd edx, xmm7 pextrd r9d, xmm7, 2 -FN_PREFIX(CryptonightWOW_template_part2): +CryptonightWOW_template_part2: mov rax, r13 mul r12 - movd xmm0, rax - movd xmm3, rdx + movq xmm0, rax + movq xmm3, rdx punpcklqdq xmm3, xmm0 mov r9d, r10d @@ -140,10 +142,10 @@ FN_PREFIX(CryptonightWOW_template_part2): xor r15, r14 movdqa xmm6, xmm5 dec r8d - jnz FN_PREFIX(CryptonightWOW_template_mainloop) + jnz CryptonightWOW_template_mainloop -FN_PREFIX(CryptonightWOW_template_part3): - movd rsp, xmm9 +CryptonightWOW_template_part3: + movq rsp, xmm9 mov rbx, QWORD PTR [rsp+136] mov rbp, QWORD PTR [rsp+144] @@ -161,10 +163,13 @@ FN_PREFIX(CryptonightWOW_template_part3): pop r11 pop r10 ret 0 -FN_PREFIX(CryptonightWOW_template_end): +CryptonightWOW_template_end: ALIGN(64) -FN_PREFIX(CryptonightWOW_template_double_part1): +CryptonightWOW_template_double_part1: + mov rdx, [rcx+8] + mov rcx, [rcx] + mov QWORD PTR [rsp+24], rbx push rbp push rsi @@ -188,7 +193,7 @@ FN_PREFIX(CryptonightWOW_template_double_part1): xor rax, QWORD PTR [r8+16] mov r15, QWORD PTR [rdx+32] xor r15, QWORD PTR [rdx] - movd xmm0, rcx + movq xmm0, rcx mov rcx, QWORD PTR [r8+88] xor rcx, QWORD PTR [r8+72] mov r13, QWORD PTR [rdx+40] @@ -204,7 +209,7 @@ FN_PREFIX(CryptonightWOW_template_double_part1): movaps XMMWORD PTR [rsp+272], xmm13 movaps XMMWORD PTR [rsp+288], xmm14 movaps XMMWORD PTR [rsp+304], xmm15 - movd xmm7, rax + movq xmm7, rax mov rax, QWORD PTR [r8+80] xor rax, QWORD PTR [r8+64] @@ -215,42 +220,42 @@ FN_PREFIX(CryptonightWOW_template_double_part1): mov r8d, r15d punpcklqdq xmm7, xmm0 - movd xmm0, rcx + movq xmm0, rcx mov rcx, QWORD PTR [rdx+56] xor rcx, QWORD PTR [rdx+24] - movd xmm9, rax + movq xmm9, rax mov QWORD PTR [rsp+128], rsi mov rax, QWORD PTR [rdx+48] xor rax, QWORD PTR [rdx+16] punpcklqdq xmm9, xmm0 - movd xmm0, rcx + movq xmm0, rcx mov rcx, QWORD PTR [rdx+88] xor rcx, QWORD PTR [rdx+72] - movd xmm8, rax + movq xmm8, rax mov QWORD PTR [rsp+136], rdi mov rax, QWORD PTR [rdx+80] xor rax, QWORD PTR [rdx+64] punpcklqdq xmm8, xmm0 and r8d, 2097136 - movd xmm0, rcx + movq xmm0, rcx mov r11d, 524288 - movd xmm10, rax + movq xmm10, rax punpcklqdq xmm10, xmm0 - movd xmm14, QWORD PTR [rsp+128] - movd xmm15, QWORD PTR [rsp+136] + movq xmm14, QWORD PTR [rsp+128] + movq xmm15, QWORD PTR [rsp+136] ALIGN(64) -FN_PREFIX(CryptonightWOW_template_double_mainloop): +CryptonightWOW_template_double_mainloop: movdqu xmm6, XMMWORD PTR [rbx+rsi] - movd xmm0, r12 + movq xmm0, r12 mov ecx, ebx - movd xmm3, r14 + movq xmm3, r14 punpcklqdq xmm3, xmm0 xor ebx, 16 aesenc xmm6, xmm3 - movd rdx, xmm6 - movd xmm4, r15 + movq rdx, xmm6 + movq xmm4, r15 movdqu xmm0, XMMWORD PTR [rbx+rsi] xor ebx, 48 paddq xmm0, xmm7 @@ -271,7 +276,7 @@ FN_PREFIX(CryptonightWOW_template_double_mainloop): movdqu xmm5, XMMWORD PTR [r8+rdi] and esi, 2097136 mov ecx, r8d - movd xmm0, r13 + movq xmm0, r13 punpcklqdq xmm4, xmm0 xor r8d, 16 aesenc xmm5, xmm4 @@ -291,20 +296,20 @@ FN_PREFIX(CryptonightWOW_template_double_mainloop): movdqa xmm0, xmm5 pxor xmm0, xmm8 movdqu XMMWORD PTR [rcx+rdi], xmm0 - movd rdi, xmm5 - movd rcx, xmm14 + movq rdi, xmm5 + movq rcx, xmm14 mov ebp, edi mov r8, QWORD PTR [rcx+rsi] mov r10, QWORD PTR [rcx+rsi+8] lea r9, QWORD PTR [rcx+rsi] xor esi, 16 - movd xmm0, rsp - movd xmm1, rsi - movd xmm2, rdi - movd xmm11, rbp - movd xmm12, r15 - movd xmm13, rdx + movq xmm0, rsp + movq xmm1, rsi + movq xmm2, rdi + movq xmm11, rbp + movq xmm12, r15 + movq xmm13, rdx mov [rsp+104], rcx mov [rsp+112], r9 @@ -325,19 +330,19 @@ FN_PREFIX(CryptonightWOW_template_double_mainloop): movd edx, xmm9 pextrd r9d, xmm9, 2 -FN_PREFIX(CryptonightWOW_template_double_part2): +CryptonightWOW_template_double_part2: - movd rsp, xmm0 + movq rsp, xmm0 mov DWORD PTR [rsp+16], ebx mov DWORD PTR [rsp+20], esi mov DWORD PTR [rsp+24], edi mov DWORD PTR [rsp+28], ebp - movd rsi, xmm1 - movd rdi, xmm2 - movd rbp, xmm11 - movd r15, xmm12 - movd rdx, xmm13 + movq rsi, xmm1 + movq rdi, xmm2 + movq rbp, xmm11 + movq r15, xmm12 + movq rdx, xmm13 mov rcx, [rsp+104] mov r9, [rsp+112] @@ -346,8 +351,8 @@ FN_PREFIX(CryptonightWOW_template_double_part2): mul rdx and ebp, 2097136 mov r8, rax - movd xmm1, rdx - movd xmm0, r8 + movq xmm1, rdx + movq xmm0, r8 punpcklqdq xmm1, xmm0 pxor xmm1, XMMWORD PTR [rcx+rsi] xor esi, 48 @@ -372,7 +377,7 @@ FN_PREFIX(CryptonightWOW_template_double_part2): mov QWORD PTR [r9+8], r12 xor r12, r10 mov QWORD PTR [r9], r14 - movd rcx, xmm15 + movq rcx, xmm15 xor r14, rbx mov r10d, ebp mov ebx, r14d @@ -381,12 +386,12 @@ FN_PREFIX(CryptonightWOW_template_double_part2): mov r8, QWORD PTR [r10+rcx] mov r9, QWORD PTR [r10+rcx+8] - movd xmm0, rsp - movd xmm1, rbx - movd xmm2, rsi - movd xmm11, rdi - movd xmm12, rbp - movd xmm13, r15 + movq xmm0, rsp + movq xmm1, rbx + movq xmm2, rsi + movq xmm11, rdi + movq xmm12, rbp + movq xmm13, r15 mov [rsp+104], rcx mov [rsp+112], r9 @@ -401,7 +406,7 @@ FN_PREFIX(CryptonightWOW_template_double_part2): or rax, rdx xor r8, rax - movd xmm3, r8 + movq xmm3, r8 movd esp, xmm4 pextrd r15d, xmm4, 2 @@ -409,26 +414,26 @@ FN_PREFIX(CryptonightWOW_template_double_part2): movd edx, xmm10 pextrd r9d, xmm10, 2 -FN_PREFIX(CryptonightWOW_template_double_part3): +CryptonightWOW_template_double_part3: - movd rsp, xmm0 + movq rsp, xmm0 mov DWORD PTR [rsp], ebx mov DWORD PTR [rsp+4], esi mov DWORD PTR [rsp+8], edi mov DWORD PTR [rsp+12], ebp - movd rbx, xmm1 - movd rsi, xmm2 - movd rdi, xmm11 - movd rbp, xmm12 - movd r15, xmm13 + movq rbx, xmm1 + movq rsi, xmm2 + movq rdi, xmm11 + movq rbp, xmm12 + movq r15, xmm13 mov rcx, [rsp+104] mov r9, [rsp+112] mov rax, r8 mul rdi - movd xmm1, rdx - movd xmm0, rax + movq xmm1, rdx + movq xmm0, rax punpcklqdq xmm1, xmm0 mov rdi, rcx mov r8, rax @@ -449,7 +454,7 @@ FN_PREFIX(CryptonightWOW_template_double_part3): movdqu XMMWORD PTR [rbp+rcx], xmm2 paddq xmm0, xmm10 movdqu XMMWORD PTR [rax+rcx], xmm0 - movd rax, xmm3 + movq rax, xmm3 movdqa xmm10, xmm8 mov QWORD PTR [r10+rcx], r15 movdqa xmm8, xmm5 @@ -459,9 +464,9 @@ FN_PREFIX(CryptonightWOW_template_double_part3): xor r13, r9 and r8d, 2097136 dec r11d - jnz FN_PREFIX(CryptonightWOW_template_double_mainloop) + jnz CryptonightWOW_template_double_mainloop -FN_PREFIX(CryptonightWOW_template_double_part4): +CryptonightWOW_template_double_part4: mov rbx, QWORD PTR [rsp+400] movaps xmm6, XMMWORD PTR [rsp+160] @@ -483,4 +488,4 @@ FN_PREFIX(CryptonightWOW_template_double_part4): pop rsi pop rbp ret 0 -FN_PREFIX(CryptonightWOW_template_double_end): +CryptonightWOW_template_double_end: diff --git a/src/crypto/asm/win/cnv2_double_main_loop_sandybridge.inc.in b/src/crypto/cn/asm/cn2/cnv2_double_main_loop_sandybridge.inc similarity index 89% rename from src/crypto/asm/win/cnv2_double_main_loop_sandybridge.inc.in rename to src/crypto/cn/asm/cn2/cnv2_double_main_loop_sandybridge.inc index 2821945c..1710cac7 100644 --- a/src/crypto/asm/win/cnv2_double_main_loop_sandybridge.inc.in +++ b/src/crypto/cn/asm/cn2/cnv2_double_main_loop_sandybridge.inc @@ -1,3 +1,6 @@ + mov rdx, [rcx+8] + mov rcx, [rcx] + mov rax, rsp push rbx push rbp @@ -18,7 +21,7 @@ mov r10, QWORD PTR [rcx+32] mov r8, rcx xor r10, QWORD PTR [rcx] - mov r14d, ${ITERATIONS} + mov r14d, 524288 mov r11, QWORD PTR [rcx+40] xor r11, QWORD PTR [rcx+8] mov rsi, QWORD PTR [rdx+224] @@ -41,7 +44,7 @@ movaps XMMWORD PTR [rsp+16], xmm15 mov rdx, r10 movq xmm4, QWORD PTR [r8+96] - and edx, ${MASK} + and edx, 2097136 mov rax, QWORD PTR [rcx+48] xorps xmm13, xmm13 xor rax, QWORD PTR [rcx+16] @@ -83,7 +86,7 @@ mov rcx, rdi mov QWORD PTR [rsp+264], r11 movq xmm8, rax - and ecx, ${MASK} + and ecx, 2097136 punpcklqdq xmm8, xmm0 movq xmm0, QWORD PTR [r9+96] punpcklqdq xmm4, xmm0 @@ -94,8 +97,8 @@ lea r9, QWORD PTR [rdx+r13] movdqu xmm15, XMMWORD PTR [r9] - ALIGN 64 -cnv2_double_main_loop_${ALGO}_sandybridge: + ALIGN(64) +main_loop_double_sandybridge: movdqu xmm9, xmm15 mov eax, edx mov ebx, edx @@ -120,7 +123,7 @@ cnv2_double_main_loop_${ALGO}_sandybridge: movq r11, xmm9 mov edx, r11d - and edx, ${MASK} + and edx, 2097136 movdqa xmm0, xmm9 pxor xmm0, xmm7 movdqu XMMWORD PTR [r9], xmm0 @@ -151,7 +154,7 @@ cnv2_double_main_loop_${ALGO}_sandybridge: movdqu XMMWORD PTR [rax+rsi], xmm0 movq rcx, xmm10 - and ecx, ${MASK} + and ecx, 2097136 movdqa xmm0, xmm10 pxor xmm0, xmm6 @@ -199,7 +202,7 @@ cnv2_double_main_loop_${ALGO}_sandybridge: mov QWORD PTR [rbx+8], rdx xor rdx, r9 mov QWORD PTR [rsp+256], r11 - and r11d, ${MASK} + and r11d, 2097136 mov QWORD PTR [rsp+264], rdx mov QWORD PTR [rsp+8], r11 lea r15, QWORD PTR [r11+r13] @@ -249,8 +252,8 @@ cnv2_double_main_loop_${ALGO}_sandybridge: mov rbx, rax imul rax, rdx sub r11, rax - js div_fix_1_${ALGO}_sandybridge -div_fix_1_ret_${ALGO}_sandybridge: + js div_fix_1_sandybridge +div_fix_1_ret_sandybridge: cvttsd2si rdx, xmm2 mov rax, rdx @@ -258,8 +261,8 @@ div_fix_1_ret_${ALGO}_sandybridge: movd xmm2, r11d movd xmm4, ebx sub r8, rax - js div_fix_2_${ALGO}_sandybridge -div_fix_2_ret_${ALGO}_sandybridge: + js div_fix_2_sandybridge +div_fix_2_ret_sandybridge: movd xmm1, r8d movd xmm0, edx @@ -275,15 +278,15 @@ div_fix_2_ret_${ALGO}_sandybridge: movdqa xmm5, xmm1 psrlq xmm5, 19 test r9, 524287 - je sqrt_fix_1_${ALGO}_sandybridge -sqrt_fix_1_ret_${ALGO}_sandybridge: + je sqrt_fix_1_sandybridge +sqrt_fix_1_ret_sandybridge: movq r9, xmm10 psrldq xmm1, 8 movq r8, xmm1 test r8, 524287 - je sqrt_fix_2_${ALGO}_sandybridge -sqrt_fix_2_ret_${ALGO}_sandybridge: + je sqrt_fix_2_sandybridge +sqrt_fix_2_ret_sandybridge: mov r12d, ecx mov r8d, ecx @@ -313,7 +316,7 @@ sqrt_fix_2_ret_${ALGO}_sandybridge: mov QWORD PTR [r13], rdi xor rdi, r10 mov ecx, edi - and ecx, ${MASK} + and ecx, 2097136 lea r8, QWORD PTR [rcx+rsi] mov rdx, QWORD PTR [r13+8] @@ -331,7 +334,7 @@ sqrt_fix_2_ret_${ALGO}_sandybridge: movdqa xmm6, xmm10 mov r9, r15 dec r14d - jne cnv2_double_main_loop_${ALGO}_sandybridge + jne main_loop_double_sandybridge ldmxcsr DWORD PTR [rsp+272] movaps xmm13, XMMWORD PTR [rsp+48] @@ -354,19 +357,19 @@ sqrt_fix_2_ret_${ALGO}_sandybridge: pop rsi pop rbp pop rbx - jmp cnv2_double_main_loop_${ALGO}_sandybridge_endp + jmp cnv2_double_mainloop_asm_sandybridge_endp -div_fix_1_${ALGO}_sandybridge: +div_fix_1_sandybridge: dec rbx add r11, rdx - jmp div_fix_1_ret_${ALGO}_sandybridge + jmp div_fix_1_ret_sandybridge -div_fix_2_${ALGO}_sandybridge: +div_fix_2_sandybridge: dec rdx add r8, r9 - jmp div_fix_2_ret_${ALGO}_sandybridge + jmp div_fix_2_ret_sandybridge -sqrt_fix_1_${ALGO}_sandybridge: +sqrt_fix_1_sandybridge: movq r8, xmm3 movdqa xmm0, xmm5 psrldq xmm0, 8 @@ -385,9 +388,9 @@ sqrt_fix_1_${ALGO}_sandybridge: adc r9, 0 movq xmm5, r9 punpcklqdq xmm5, xmm0 - jmp sqrt_fix_1_ret_${ALGO}_sandybridge + jmp sqrt_fix_1_ret_sandybridge -sqrt_fix_2_${ALGO}_sandybridge: +sqrt_fix_2_sandybridge: psrldq xmm3, 8 movq r11, xmm3 dec r8 @@ -405,6 +408,6 @@ sqrt_fix_2_${ALGO}_sandybridge: adc r8, 0 movq xmm0, r8 punpcklqdq xmm5, xmm0 - jmp sqrt_fix_2_ret_${ALGO}_sandybridge + jmp sqrt_fix_2_ret_sandybridge -cnv2_double_main_loop_${ALGO}_sandybridge_endp: +cnv2_double_mainloop_asm_sandybridge_endp: diff --git a/src/crypto/asm/cnv2_main_loop_bulldozer.inc.in b/src/crypto/cn/asm/cn2/cnv2_main_loop_bulldozer.inc similarity index 89% rename from src/crypto/asm/cnv2_main_loop_bulldozer.inc.in rename to src/crypto/cn/asm/cn2/cnv2_main_loop_bulldozer.inc index f849f3ca..b881b669 100644 --- a/src/crypto/asm/cnv2_main_loop_bulldozer.inc.in +++ b/src/crypto/cn/asm/cn2/cnv2_main_loop_bulldozer.inc @@ -1,3 +1,5 @@ + mov rcx, [rcx] + mov QWORD PTR [rsp+16], rbx mov QWORD PTR [rsp+24], rbp mov QWORD PTR [rsp+32], rsi @@ -15,7 +17,7 @@ mov rax, QWORD PTR [rcx+48] mov r9, rcx xor rax, QWORD PTR [rcx+16] - mov ebp, ${ITERATIONS} + mov ebp, 524288 mov r8, QWORD PTR [rcx+32] xor r8, QWORD PTR [rcx] mov r11, QWORD PTR [rcx+40] @@ -31,7 +33,7 @@ mov rcx, QWORD PTR [rcx+88] xor rcx, QWORD PTR [r9+72] mov rdi, QWORD PTR [r9+104] - and r10d, ${MASK} + and r10d, 2097136 movaps XMMWORD PTR [rsp+48], xmm6 movq xmm4, rax movaps XMMWORD PTR [rsp+32], xmm7 @@ -45,8 +47,8 @@ movq xmm0, rcx punpcklqdq xmm4, xmm0 - ALIGN 16 -cnv2_main_loop_${ALGO}_bulldozer: + ALIGN(64) +cnv2_main_loop_bulldozer: movdqa xmm5, XMMWORD PTR [r10+rbx] movq xmm6, r8 pinsrq xmm6, r11, 1 @@ -83,7 +85,7 @@ cnv2_main_loop_${ALGO}_bulldozer: movdqa xmm0, xmm5 pxor xmm0, xmm3 mov r10, r14 - and r10d, ${MASK} + and r10d, 2097136 movdqa XMMWORD PTR [rdx], xmm0 xor rsi, QWORD PTR [r10+rbx] lea r12, QWORD PTR [r10+rbx] @@ -103,10 +105,10 @@ cnv2_main_loop_${ALGO}_bulldozer: sqrtsd xmm1, xmm0 movq rdi, xmm1 test rdi, 524287 - je sqrt_fixup_${ALGO}_bulldozer + je sqrt_fixup_bulldozer shr rdi, 19 -sqrt_fixup_${ALGO}_bulldozer_ret: +sqrt_fixup_bulldozer_ret: mov rax, rsi mul r14 movq xmm1, rax @@ -138,10 +140,10 @@ sqrt_fixup_${ALGO}_bulldozer_ret: mov QWORD PTR [r12+8], r11 mov r10, r8 xor r11, r13 - and r10d, ${MASK} + and r10d, 2097136 movdqa xmm3, xmm5 dec ebp - jne cnv2_main_loop_${ALGO}_bulldozer + jne cnv2_main_loop_bulldozer ldmxcsr DWORD PTR [rsp] movaps xmm6, XMMWORD PTR [rsp+48] @@ -157,9 +159,9 @@ sqrt_fixup_${ALGO}_bulldozer_ret: pop r13 pop r12 pop rdi - jmp cnv2_main_loop_${ALGO}_bulldozer_endp + jmp cnv2_main_loop_bulldozer_endp -sqrt_fixup_${ALGO}_bulldozer: +sqrt_fixup_bulldozer: movq r9, xmm5 add r9, r15 dec rdi @@ -175,6 +177,6 @@ sqrt_fixup_${ALGO}_bulldozer: imul rcx, rax sub rcx, r9 adc rdi, 0 - jmp sqrt_fixup_${ALGO}_bulldozer_ret + jmp sqrt_fixup_bulldozer_ret -cnv2_main_loop_${ALGO}_bulldozer_endp: \ No newline at end of file +cnv2_main_loop_bulldozer_endp: diff --git a/src/crypto/asm/win/cnv2_main_loop_ivybridge.inc.in b/src/crypto/cn/asm/cn2/cnv2_main_loop_ivybridge.inc similarity index 89% rename from src/crypto/asm/win/cnv2_main_loop_ivybridge.inc.in rename to src/crypto/cn/asm/cn2/cnv2_main_loop_ivybridge.inc index 1094c6d7..863673de 100644 --- a/src/crypto/asm/win/cnv2_main_loop_ivybridge.inc.in +++ b/src/crypto/cn/asm/cn2/cnv2_main_loop_ivybridge.inc @@ -1,3 +1,5 @@ + mov rcx, [rcx] + mov QWORD PTR [rsp+24], rbx push rbp push rsi @@ -15,7 +17,7 @@ mov rax, QWORD PTR [rcx+48] mov r9, rcx xor rax, QWORD PTR [rcx+16] - mov esi, ${ITERATIONS} + mov esi, 524288 mov r8, QWORD PTR [rcx+32] mov r13d, -2147483647 xor r8, QWORD PTR [rcx] @@ -35,9 +37,12 @@ movaps XMMWORD PTR [rsp+64], xmm6 movaps XMMWORD PTR [rsp+48], xmm7 movaps XMMWORD PTR [rsp+32], xmm8 - and r10d, ${MASK} + and r10d, 2097136 movq xmm5, rax + xor eax, eax + mov QWORD PTR [rsp+16], rax + mov ax, 1023 shl rax, 52 movq xmm8, rax @@ -47,8 +52,8 @@ punpcklqdq xmm5, xmm0 movdqu xmm6, XMMWORD PTR [r10+rbx] - ALIGN 64 -cnv2_main_loop_${ALGO}_ivybridge: + ALIGN(64) +main_loop_ivybridge: lea rdx, QWORD PTR [r10+rbx] mov ecx, r10d mov eax, r10d @@ -62,7 +67,7 @@ cnv2_main_loop_${ALGO}_ivybridge: aesenc xmm6, xmm7 movq rbp, xmm6 mov r9, rbp - and r9d, ${MASK} + and r9d, 2097136 movdqu xmm2, XMMWORD PTR [rcx+rbx] movdqu xmm1, XMMWORD PTR [rax+rbx] movdqu xmm0, XMMWORD PTR [r10+rbx] @@ -103,11 +108,12 @@ cnv2_main_loop_${ALGO}_ivybridge: movq xmm0, rax paddq xmm0, xmm8 sqrtsd xmm3, xmm0 + psubq xmm3, XMMWORD PTR [rsp+16] movq rdx, xmm3 test edx, 524287 - je sqrt_fixup_${ALGO}_ivybridge + je sqrt_fixup_ivybridge psrlq xmm3, 19 -sqrt_fixup_${ALGO}_ivybridge_ret: +sqrt_fixup_ivybridge_ret: mov ecx, r10d mov rax, rdi @@ -118,7 +124,7 @@ sqrt_fixup_${ALGO}_ivybridge_ret: mov QWORD PTR [r14], r8 xor r8, rdi mov edi, r8d - and edi, ${MASK} + and edi, 2097136 movq xmm0, rax xor rax, [rcx+rbx+8] add r11, rax @@ -143,7 +149,7 @@ sqrt_fixup_${ALGO}_ivybridge_ret: mov r10d, edi xor r11, r12 dec rsi - jne cnv2_main_loop_${ALGO}_ivybridge + jne main_loop_ivybridge ldmxcsr DWORD PTR [rsp] mov rbx, QWORD PTR [rsp+160] @@ -158,9 +164,9 @@ sqrt_fixup_${ALGO}_ivybridge_ret: pop rdi pop rsi pop rbp - jmp cnv2_main_loop_${ALGO}_ivybridge_endp + jmp cnv2_main_loop_ivybridge_endp -sqrt_fixup_${ALGO}_ivybridge: +sqrt_fixup_ivybridge: dec rdx mov r13d, -1022 shl r13, 32 @@ -177,6 +183,6 @@ sqrt_fixup_${ALGO}_ivybridge: sub rcx, r9 adc rdx, 0 movq xmm3, rdx - jmp sqrt_fixup_${ALGO}_ivybridge_ret + jmp sqrt_fixup_ivybridge_ret -cnv2_main_loop_${ALGO}_ivybridge_endp: +cnv2_main_loop_ivybridge_endp: diff --git a/src/crypto/asm/win/cnv2_main_loop_ryzen.inc.in b/src/crypto/cn/asm/cn2/cnv2_main_loop_ryzen.inc similarity index 90% rename from src/crypto/asm/win/cnv2_main_loop_ryzen.inc.in rename to src/crypto/cn/asm/cn2/cnv2_main_loop_ryzen.inc index 4fef1335..8ccc5e17 100644 --- a/src/crypto/asm/win/cnv2_main_loop_ryzen.inc.in +++ b/src/crypto/cn/asm/cn2/cnv2_main_loop_ryzen.inc @@ -1,3 +1,5 @@ + mov rcx, [rcx] + mov QWORD PTR [rsp+16], rbx mov QWORD PTR [rsp+24], rbp mov QWORD PTR [rsp+32], rsi @@ -15,7 +17,7 @@ mov rax, QWORD PTR [rcx+48] mov r9, rcx xor rax, QWORD PTR [rcx+16] - mov ebp, ${ITERATIONS} + mov ebp, 524288 mov r8, QWORD PTR [rcx+32] xor r8, QWORD PTR [rcx] mov r11, QWORD PTR [rcx+40] @@ -31,7 +33,7 @@ mov rcx, QWORD PTR [rcx+88] xor rcx, QWORD PTR [r9+72] mov rdi, QWORD PTR [r9+104] - and r10d, ${MASK} + and r10d, 2097136 movaps XMMWORD PTR [rsp+48], xmm6 movq xmm4, rax movaps XMMWORD PTR [rsp+32], xmm7 @@ -45,8 +47,8 @@ movq xmm0, rcx punpcklqdq xmm4, xmm0 - ALIGN 64 -cnv2_main_loop_${ALGO}_ryzen: + ALIGN(64) +main_loop_ryzen: movdqa xmm5, XMMWORD PTR [r10+rbx] movq xmm0, r11 movq xmm6, r8 @@ -78,7 +80,7 @@ cnv2_main_loop_${ALGO}_ryzen: movdqa xmm0, xmm5 pxor xmm0, xmm3 mov r10, r14 - and r10d, ${MASK} + and r10d, 2097136 movdqa XMMWORD PTR [rdx], xmm0 xor rsi, QWORD PTR [r10+rbx] lea r12, QWORD PTR [r10+rbx] @@ -103,10 +105,10 @@ cnv2_main_loop_${ALGO}_ryzen: sqrtsd xmm1, xmm0 movq rdi, xmm1 test rdi, 524287 - je sqrt_fixup_${ALGO}_ryzen + je sqrt_fixup_ryzen shr rdi, 19 -sqrt_fixup_${ALGO}_ryzen_ret: +sqrt_fixup_ryzen_ret: mov rax, rsi mul r14 movq xmm1, rax @@ -138,10 +140,10 @@ sqrt_fixup_${ALGO}_ryzen_ret: mov QWORD PTR [r12+8], r11 mov r10, r8 xor r11, r13 - and r10d, ${MASK} + and r10d, 2097136 movdqa xmm3, xmm5 dec ebp - jne cnv2_main_loop_${ALGO}_ryzen + jne main_loop_ryzen ldmxcsr DWORD PTR [rsp] movaps xmm6, XMMWORD PTR [rsp+48] @@ -157,9 +159,9 @@ sqrt_fixup_${ALGO}_ryzen_ret: pop r13 pop r12 pop rdi - jmp cnv2_main_loop_${ALGO}_ryzen_endp + jmp cnv2_main_loop_ryzen_endp -sqrt_fixup_${ALGO}_ryzen: +sqrt_fixup_ryzen: movq r9, xmm2 dec rdi mov edx, -1022 @@ -174,6 +176,6 @@ sqrt_fixup_${ALGO}_ryzen: imul rcx, rax sub rcx, r9 adc rdi, 0 - jmp sqrt_fixup_${ALGO}_ryzen_ret + jmp sqrt_fixup_ryzen_ret -cnv2_main_loop_${ALGO}_ryzen_endp: +cnv2_main_loop_ryzen_endp: diff --git a/src/crypto/asm/cnv2_double_main_loop_rwz_all.inc.in b/src/crypto/cn/asm/cn2/cnv2_rwz_double_main_loop.inc similarity index 90% rename from src/crypto/asm/cnv2_double_main_loop_rwz_all.inc.in rename to src/crypto/cn/asm/cn2/cnv2_rwz_double_main_loop.inc index 969032ff..d9bfc9c1 100644 --- a/src/crypto/asm/cnv2_double_main_loop_rwz_all.inc.in +++ b/src/crypto/cn/asm/cn2/cnv2_rwz_double_main_loop.inc @@ -1,3 +1,6 @@ + mov rdx, [rcx+8] + mov rcx, [rcx] + mov rax, rsp push rbx push rbp @@ -18,7 +21,7 @@ mov r10, QWORD PTR [rcx+32] mov r8, rcx xor r10, QWORD PTR [rcx] - mov r14d, ${ITERATIONS} + mov r14d, 393216 mov r11, QWORD PTR [rcx+40] xor r11, QWORD PTR [rcx+8] mov rsi, QWORD PTR [rdx+224] @@ -41,7 +44,7 @@ movaps XMMWORD PTR [rsp+16], xmm15 mov rdx, r10 movq xmm4, QWORD PTR [r8+96] - and edx, ${MASK} + and edx, 2097136 mov rax, QWORD PTR [rcx+48] xorps xmm13, xmm13 xor rax, QWORD PTR [rcx+16] @@ -83,7 +86,7 @@ mov rcx, rdi mov QWORD PTR [rsp+264], r11 movq xmm8, rax - and ecx, ${MASK} + and ecx, 2097136 punpcklqdq xmm8, xmm0 movq xmm0, QWORD PTR [r9+96] punpcklqdq xmm4, xmm0 @@ -94,12 +97,8 @@ lea r9, QWORD PTR [rdx+r13] movdqu xmm15, XMMWORD PTR [r9] - #ifdef __APPLE__ - ALIGN(16) - #else ALIGN(64) - #endif -rwz_main_loop_double_${ALGO}: +rwz_main_loop_double: movdqu xmm9, xmm15 mov eax, edx mov ebx, edx @@ -124,7 +123,7 @@ rwz_main_loop_double_${ALGO}: movq r11, xmm9 mov edx, r11d - and edx, ${MASK} + and edx, 2097136 movdqa xmm0, xmm9 pxor xmm0, xmm7 movdqu XMMWORD PTR [r9], xmm0 @@ -155,7 +154,7 @@ rwz_main_loop_double_${ALGO}: movdqu XMMWORD PTR [rax+rsi], xmm0 movq rcx, xmm10 - and ecx, ${MASK} + and ecx, 2097136 movdqa xmm0, xmm10 pxor xmm0, xmm6 @@ -203,7 +202,7 @@ rwz_main_loop_double_${ALGO}: mov QWORD PTR [rbx+8], rdx xor rdx, r9 mov QWORD PTR [rsp+256], r11 - and r11d, ${MASK} + and r11d, 2097136 mov QWORD PTR [rsp+264], rdx mov QWORD PTR [rsp+8], r11 lea r15, QWORD PTR [r11+r13] @@ -253,8 +252,8 @@ rwz_main_loop_double_${ALGO}: mov rbx, rax imul rax, rdx sub r11, rax - js rwz_div_fix_1_${ALGO} -rwz_div_fix_1_${ALGO}_ret: + js rwz_div_fix_1 +rwz_div_fix_1_ret: cvttsd2si rdx, xmm2 mov rax, rdx @@ -262,8 +261,8 @@ rwz_div_fix_1_${ALGO}_ret: movd xmm2, r11d movd xmm4, ebx sub r8, rax - js rwz_div_fix_2_${ALGO} -rwz_div_fix_2_${ALGO}_ret: + js rwz_div_fix_2 +rwz_div_fix_2_ret: movd xmm1, r8d movd xmm0, edx @@ -279,15 +278,15 @@ rwz_div_fix_2_${ALGO}_ret: movdqa xmm5, xmm1 psrlq xmm5, 19 test r9, 524287 - je rwz_sqrt_fix_1_${ALGO} -rwz_sqrt_fix_1_${ALGO}_ret: + je rwz_sqrt_fix_1 +rwz_sqrt_fix_1_ret: movq r9, xmm10 psrldq xmm1, 8 movq r8, xmm1 test r8, 524287 - je rwz_sqrt_fix_2_${ALGO} -rwz_sqrt_fix_2_${ALGO}_ret: + je rwz_sqrt_fix_2 +rwz_sqrt_fix_2_ret: mov r12d, ecx mov r8d, ecx @@ -317,7 +316,7 @@ rwz_sqrt_fix_2_${ALGO}_ret: mov QWORD PTR [r13], rdi xor rdi, r10 mov ecx, edi - and ecx, ${MASK} + and ecx, 2097136 lea r8, QWORD PTR [rcx+rsi] mov rdx, QWORD PTR [r13+8] @@ -335,7 +334,7 @@ rwz_sqrt_fix_2_${ALGO}_ret: movdqa xmm6, xmm10 mov r9, r15 dec r14d - jne rwz_main_loop_double_${ALGO} + jne rwz_main_loop_double ldmxcsr DWORD PTR [rsp+272] movaps xmm13, XMMWORD PTR [rsp+48] @@ -358,19 +357,19 @@ rwz_sqrt_fix_2_${ALGO}_ret: pop rsi pop rbp pop rbx - jmp rwz_cnv2_double_mainloop_${ALGO}_asm_endp + jmp rwz_cnv2_double_mainloop_asm_endp -rwz_div_fix_1_${ALGO}: +rwz_div_fix_1: dec rbx add r11, rdx - jmp rwz_div_fix_1_${ALGO}_ret + jmp rwz_div_fix_1_ret -rwz_div_fix_2_${ALGO}: +rwz_div_fix_2: dec rdx add r8, r9 - jmp rwz_div_fix_2_${ALGO}_ret + jmp rwz_div_fix_2_ret -rwz_sqrt_fix_1_${ALGO}: +rwz_sqrt_fix_1: movq r8, xmm3 movdqa xmm0, xmm5 psrldq xmm0, 8 @@ -389,9 +388,9 @@ rwz_sqrt_fix_1_${ALGO}: adc r9, 0 movq xmm5, r9 punpcklqdq xmm5, xmm0 - jmp rwz_sqrt_fix_1_${ALGO}_ret + jmp rwz_sqrt_fix_1_ret -rwz_sqrt_fix_2_${ALGO}: +rwz_sqrt_fix_2: psrldq xmm3, 8 movq r11, xmm3 dec r8 @@ -409,6 +408,6 @@ rwz_sqrt_fix_2_${ALGO}: adc r8, 0 movq xmm0, r8 punpcklqdq xmm5, xmm0 - jmp rwz_sqrt_fix_2_${ALGO}_ret + jmp rwz_sqrt_fix_2_ret -rwz_cnv2_double_mainloop_${ALGO}_asm_endp: +rwz_cnv2_double_mainloop_asm_endp: diff --git a/src/crypto/asm/cnv2_main_loop_rwz_all.inc.in b/src/crypto/cn/asm/cn2/cnv2_rwz_main_loop.inc similarity index 89% rename from src/crypto/asm/cnv2_main_loop_rwz_all.inc.in rename to src/crypto/cn/asm/cn2/cnv2_rwz_main_loop.inc index 042af620..b59c02d6 100644 --- a/src/crypto/asm/cnv2_main_loop_rwz_all.inc.in +++ b/src/crypto/cn/asm/cn2/cnv2_rwz_main_loop.inc @@ -1,3 +1,5 @@ + mov rcx, [rcx] + mov QWORD PTR [rsp+24], rbx push rbp push rsi @@ -15,7 +17,7 @@ mov rax, QWORD PTR [rcx+48] mov r9, rcx xor rax, QWORD PTR [rcx+16] - mov esi, ${ITERATIONS} + mov esi, 393216 mov r8, QWORD PTR [rcx+32] mov r13d, -2147483647 xor r8, QWORD PTR [rcx] @@ -35,7 +37,7 @@ movaps XMMWORD PTR [rsp+64], xmm6 movaps XMMWORD PTR [rsp+48], xmm7 movaps XMMWORD PTR [rsp+32], xmm8 - and r10d, ${MASK} + and r10d, 2097136 movq xmm5, rax xor eax, eax @@ -50,12 +52,8 @@ punpcklqdq xmm5, xmm0 movdqu xmm6, XMMWORD PTR [r10+rbx] - #ifdef __APPLE__ - ALIGN(16) - #else - ALIGN(64) - #endif -rwz_main_loop_${ALGO}: + ALIGN(64) +rwz_main_loop: lea rdx, QWORD PTR [r10+rbx] mov ecx, r10d mov eax, r10d @@ -69,7 +67,7 @@ rwz_main_loop_${ALGO}: aesenc xmm6, xmm7 movq rbp, xmm6 mov r9, rbp - and r9d, ${MASK} + and r9d, 2097136 movdqu xmm0, XMMWORD PTR [rcx+rbx] movdqu xmm1, XMMWORD PTR [rax+rbx] movdqu xmm2, XMMWORD PTR [r10+rbx] @@ -113,9 +111,9 @@ rwz_main_loop_${ALGO}: psubq xmm3, XMMWORD PTR [rsp+16] movq rdx, xmm3 test edx, 524287 - je rwz_sqrt_fixup_${ALGO} + je rwz_sqrt_fixup psrlq xmm3, 19 -rwz_sqrt_fixup_${ALGO}_ret: +rwz_sqrt_fixup_ret: mov ecx, r10d mov rax, rdi @@ -126,7 +124,7 @@ rwz_sqrt_fixup_${ALGO}_ret: mov QWORD PTR [r14], r8 xor r8, rdi mov edi, r8d - and edi, ${MASK} + and edi, 2097136 movq xmm0, rax xor rax, [rcx+rbx+8] add r11, rax @@ -151,7 +149,7 @@ rwz_sqrt_fixup_${ALGO}_ret: mov r10d, edi xor r11, r12 dec rsi - jne rwz_main_loop_${ALGO} + jne rwz_main_loop ldmxcsr DWORD PTR [rsp] mov rbx, QWORD PTR [rsp+160] @@ -166,9 +164,9 @@ rwz_sqrt_fixup_${ALGO}_ret: pop rdi pop rsi pop rbp - jmp cnv2_rwz_main_loop_${ALGO}_endp + jmp cnv2_rwz_main_loop_endp -rwz_sqrt_fixup_${ALGO}: +rwz_sqrt_fixup: dec rdx mov r13d, -1022 shl r13, 32 @@ -185,6 +183,6 @@ rwz_sqrt_fixup_${ALGO}: sub rcx, r9 adc rdx, 0 movq xmm3, rdx - jmp rwz_sqrt_fixup_${ALGO}_ret + jmp rwz_sqrt_fixup_ret -cnv2_rwz_main_loop_${ALGO}_endp: +cnv2_rwz_main_loop_endp: diff --git a/src/crypto/cn/asm/cn_main_loop.S b/src/crypto/cn/asm/cn_main_loop.S new file mode 100644 index 00000000..609b0fe8 --- /dev/null +++ b/src/crypto/cn/asm/cn_main_loop.S @@ -0,0 +1,77 @@ +#ifdef __APPLE__ +# define ALIGN(x) .align 6 +#else +# define ALIGN(x) .align 64 +#endif +.intel_syntax noprefix +#ifdef __APPLE__ +# define FN_PREFIX(fn) _ ## fn +.text +#else +# define FN_PREFIX(fn) fn +.section .text +#endif +.global FN_PREFIX(cnv2_mainloop_ivybridge_asm) +.global FN_PREFIX(cnv2_mainloop_ryzen_asm) +.global FN_PREFIX(cnv2_mainloop_bulldozer_asm) +.global FN_PREFIX(cnv2_double_mainloop_sandybridge_asm) +.global FN_PREFIX(cnv2_rwz_mainloop_asm) +.global FN_PREFIX(cnv2_rwz_double_mainloop_asm) + +ALIGN(64) +FN_PREFIX(cnv2_mainloop_ivybridge_asm): + sub rsp, 48 + mov rcx, rdi + #include "cn2/cnv2_main_loop_ivybridge.inc" + add rsp, 48 + ret 0 + mov eax, 3735929054 + +ALIGN(64) +FN_PREFIX(cnv2_mainloop_ryzen_asm): + sub rsp, 48 + mov rcx, rdi + #include "cn2/cnv2_main_loop_ryzen.inc" + add rsp, 48 + ret 0 + mov eax, 3735929054 + +ALIGN(64) +FN_PREFIX(cnv2_mainloop_bulldozer_asm): + sub rsp, 48 + mov rcx, rdi + #include "cn2/cnv2_main_loop_bulldozer.inc" + add rsp, 48 + ret 0 + mov eax, 3735929054 + +ALIGN(64) +FN_PREFIX(cnv2_double_mainloop_sandybridge_asm): + sub rsp, 48 + mov rcx, rdi + #include "cn2/cnv2_double_main_loop_sandybridge.inc" + add rsp, 48 + ret 0 + mov eax, 3735929054 + +ALIGN(64) +FN_PREFIX(cnv2_rwz_mainloop_asm): + sub rsp, 48 + mov rcx, rdi + #include "cn2/cnv2_rwz_main_loop.inc" + add rsp, 48 + ret 0 + mov eax, 3735929054 + +ALIGN(64) +FN_PREFIX(cnv2_rwz_double_mainloop_asm): + sub rsp, 48 + mov rcx, rdi + #include "cn2/cnv2_rwz_double_main_loop.inc" + add rsp, 48 + ret 0 + mov eax, 3735929054 + +#if defined(__linux__) && defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/src/crypto/cn/asm/cn_main_loop.asm b/src/crypto/cn/asm/cn_main_loop.asm new file mode 100644 index 00000000..f0766a7c --- /dev/null +++ b/src/crypto/cn/asm/cn_main_loop.asm @@ -0,0 +1,52 @@ +_TEXT_CNV2_MAINLOOP SEGMENT PAGE READ EXECUTE +PUBLIC cnv2_mainloop_ivybridge_asm +PUBLIC cnv2_mainloop_ryzen_asm +PUBLIC cnv2_mainloop_bulldozer_asm +PUBLIC cnv2_double_mainloop_sandybridge_asm +PUBLIC cnv2_rwz_mainloop_asm +PUBLIC cnv2_rwz_double_mainloop_asm + +ALIGN(64) +cnv2_mainloop_ivybridge_asm PROC + INCLUDE cn2/cnv2_main_loop_ivybridge.inc + ret 0 + mov eax, 3735929054 +cnv2_mainloop_ivybridge_asm ENDP + +ALIGN(64) +cnv2_mainloop_ryzen_asm PROC + INCLUDE cn2/cnv2_main_loop_ryzen.inc + ret 0 + mov eax, 3735929054 +cnv2_mainloop_ryzen_asm ENDP + +ALIGN(64) +cnv2_mainloop_bulldozer_asm PROC + INCLUDE cn2/cnv2_main_loop_bulldozer.inc + ret 0 + mov eax, 3735929054 +cnv2_mainloop_bulldozer_asm ENDP + +ALIGN(64) +cnv2_double_mainloop_sandybridge_asm PROC + INCLUDE cn2/cnv2_double_main_loop_sandybridge.inc + ret 0 + mov eax, 3735929054 +cnv2_double_mainloop_sandybridge_asm ENDP + +ALIGN(64) +cnv2_rwz_mainloop_asm PROC + INCLUDE cn2/cnv2_rwz_main_loop.inc + ret 0 + mov eax, 3735929054 +cnv2_rwz_mainloop_asm ENDP + +ALIGN(64) +cnv2_rwz_double_mainloop_asm PROC + INCLUDE cn2/cnv2_rwz_double_main_loop.inc + ret 0 + mov eax, 3735929054 +cnv2_rwz_double_mainloop_asm ENDP + +_TEXT_CNV2_MAINLOOP ENDS +END diff --git a/src/crypto/asm/win/CryptonightR_soft_aes_template_win.inc b/src/crypto/cn/asm/win64/CryptonightR_soft_aes_template_win.inc similarity index 99% rename from src/crypto/asm/win/CryptonightR_soft_aes_template_win.inc rename to src/crypto/cn/asm/win64/CryptonightR_soft_aes_template_win.inc index d6d393a9..6898a604 100644 --- a/src/crypto/asm/win/CryptonightR_soft_aes_template_win.inc +++ b/src/crypto/cn/asm/win64/CryptonightR_soft_aes_template_win.inc @@ -6,6 +6,8 @@ PUBLIC CryptonightR_soft_aes_template_end ALIGN(64) CryptonightR_soft_aes_template_part1: + mov rcx, [rcx] + mov QWORD PTR [rsp+8], rcx push rbx push rbp diff --git a/src/crypto/cn/asm/win64/CryptonightR_template.asm b/src/crypto/cn/asm/win64/CryptonightR_template.asm new file mode 100644 index 00000000..250eca3d --- /dev/null +++ b/src/crypto/cn/asm/win64/CryptonightR_template.asm @@ -0,0 +1,1585 @@ +; Auto-generated file, do not edit + +_TEXT_CN_TEMPLATE SEGMENT PAGE READ EXECUTE +PUBLIC CryptonightR_instruction0 +PUBLIC CryptonightR_instruction1 +PUBLIC CryptonightR_instruction2 +PUBLIC CryptonightR_instruction3 +PUBLIC CryptonightR_instruction4 +PUBLIC CryptonightR_instruction5 +PUBLIC CryptonightR_instruction6 +PUBLIC CryptonightR_instruction7 +PUBLIC CryptonightR_instruction8 +PUBLIC CryptonightR_instruction9 +PUBLIC CryptonightR_instruction10 +PUBLIC CryptonightR_instruction11 +PUBLIC CryptonightR_instruction12 +PUBLIC CryptonightR_instruction13 +PUBLIC CryptonightR_instruction14 +PUBLIC CryptonightR_instruction15 +PUBLIC CryptonightR_instruction16 +PUBLIC CryptonightR_instruction17 +PUBLIC CryptonightR_instruction18 +PUBLIC CryptonightR_instruction19 +PUBLIC CryptonightR_instruction20 +PUBLIC CryptonightR_instruction21 +PUBLIC CryptonightR_instruction22 +PUBLIC CryptonightR_instruction23 +PUBLIC CryptonightR_instruction24 +PUBLIC CryptonightR_instruction25 +PUBLIC CryptonightR_instruction26 +PUBLIC CryptonightR_instruction27 +PUBLIC CryptonightR_instruction28 +PUBLIC CryptonightR_instruction29 +PUBLIC CryptonightR_instruction30 +PUBLIC CryptonightR_instruction31 +PUBLIC CryptonightR_instruction32 +PUBLIC CryptonightR_instruction33 +PUBLIC CryptonightR_instruction34 +PUBLIC CryptonightR_instruction35 +PUBLIC CryptonightR_instruction36 +PUBLIC CryptonightR_instruction37 +PUBLIC CryptonightR_instruction38 +PUBLIC CryptonightR_instruction39 +PUBLIC CryptonightR_instruction40 +PUBLIC CryptonightR_instruction41 +PUBLIC CryptonightR_instruction42 +PUBLIC CryptonightR_instruction43 +PUBLIC CryptonightR_instruction44 +PUBLIC CryptonightR_instruction45 +PUBLIC CryptonightR_instruction46 +PUBLIC CryptonightR_instruction47 +PUBLIC CryptonightR_instruction48 +PUBLIC CryptonightR_instruction49 +PUBLIC CryptonightR_instruction50 +PUBLIC CryptonightR_instruction51 +PUBLIC CryptonightR_instruction52 +PUBLIC CryptonightR_instruction53 +PUBLIC CryptonightR_instruction54 +PUBLIC CryptonightR_instruction55 +PUBLIC CryptonightR_instruction56 +PUBLIC CryptonightR_instruction57 +PUBLIC CryptonightR_instruction58 +PUBLIC CryptonightR_instruction59 +PUBLIC CryptonightR_instruction60 +PUBLIC CryptonightR_instruction61 +PUBLIC CryptonightR_instruction62 +PUBLIC CryptonightR_instruction63 +PUBLIC CryptonightR_instruction64 +PUBLIC CryptonightR_instruction65 +PUBLIC CryptonightR_instruction66 +PUBLIC CryptonightR_instruction67 +PUBLIC CryptonightR_instruction68 +PUBLIC CryptonightR_instruction69 +PUBLIC CryptonightR_instruction70 +PUBLIC CryptonightR_instruction71 +PUBLIC CryptonightR_instruction72 +PUBLIC CryptonightR_instruction73 +PUBLIC CryptonightR_instruction74 +PUBLIC CryptonightR_instruction75 +PUBLIC CryptonightR_instruction76 +PUBLIC CryptonightR_instruction77 +PUBLIC CryptonightR_instruction78 +PUBLIC CryptonightR_instruction79 +PUBLIC CryptonightR_instruction80 +PUBLIC CryptonightR_instruction81 +PUBLIC CryptonightR_instruction82 +PUBLIC CryptonightR_instruction83 +PUBLIC CryptonightR_instruction84 +PUBLIC CryptonightR_instruction85 +PUBLIC CryptonightR_instruction86 +PUBLIC CryptonightR_instruction87 +PUBLIC CryptonightR_instruction88 +PUBLIC CryptonightR_instruction89 +PUBLIC CryptonightR_instruction90 +PUBLIC CryptonightR_instruction91 +PUBLIC CryptonightR_instruction92 +PUBLIC CryptonightR_instruction93 +PUBLIC CryptonightR_instruction94 +PUBLIC CryptonightR_instruction95 +PUBLIC CryptonightR_instruction96 +PUBLIC CryptonightR_instruction97 +PUBLIC CryptonightR_instruction98 +PUBLIC CryptonightR_instruction99 +PUBLIC CryptonightR_instruction100 +PUBLIC CryptonightR_instruction101 +PUBLIC CryptonightR_instruction102 +PUBLIC CryptonightR_instruction103 +PUBLIC CryptonightR_instruction104 +PUBLIC CryptonightR_instruction105 +PUBLIC CryptonightR_instruction106 +PUBLIC CryptonightR_instruction107 +PUBLIC CryptonightR_instruction108 +PUBLIC CryptonightR_instruction109 +PUBLIC CryptonightR_instruction110 +PUBLIC CryptonightR_instruction111 +PUBLIC CryptonightR_instruction112 +PUBLIC CryptonightR_instruction113 +PUBLIC CryptonightR_instruction114 +PUBLIC CryptonightR_instruction115 +PUBLIC CryptonightR_instruction116 +PUBLIC CryptonightR_instruction117 +PUBLIC CryptonightR_instruction118 +PUBLIC CryptonightR_instruction119 +PUBLIC CryptonightR_instruction120 +PUBLIC CryptonightR_instruction121 +PUBLIC CryptonightR_instruction122 +PUBLIC CryptonightR_instruction123 +PUBLIC CryptonightR_instruction124 +PUBLIC CryptonightR_instruction125 +PUBLIC CryptonightR_instruction126 +PUBLIC CryptonightR_instruction127 +PUBLIC CryptonightR_instruction128 +PUBLIC CryptonightR_instruction129 +PUBLIC CryptonightR_instruction130 +PUBLIC CryptonightR_instruction131 +PUBLIC CryptonightR_instruction132 +PUBLIC CryptonightR_instruction133 +PUBLIC CryptonightR_instruction134 +PUBLIC CryptonightR_instruction135 +PUBLIC CryptonightR_instruction136 +PUBLIC CryptonightR_instruction137 +PUBLIC CryptonightR_instruction138 +PUBLIC CryptonightR_instruction139 +PUBLIC CryptonightR_instruction140 +PUBLIC CryptonightR_instruction141 +PUBLIC CryptonightR_instruction142 +PUBLIC CryptonightR_instruction143 +PUBLIC CryptonightR_instruction144 +PUBLIC CryptonightR_instruction145 +PUBLIC CryptonightR_instruction146 +PUBLIC CryptonightR_instruction147 +PUBLIC CryptonightR_instruction148 +PUBLIC CryptonightR_instruction149 +PUBLIC CryptonightR_instruction150 +PUBLIC CryptonightR_instruction151 +PUBLIC CryptonightR_instruction152 +PUBLIC CryptonightR_instruction153 +PUBLIC CryptonightR_instruction154 +PUBLIC CryptonightR_instruction155 +PUBLIC CryptonightR_instruction156 +PUBLIC CryptonightR_instruction157 +PUBLIC CryptonightR_instruction158 +PUBLIC CryptonightR_instruction159 +PUBLIC CryptonightR_instruction160 +PUBLIC CryptonightR_instruction161 +PUBLIC CryptonightR_instruction162 +PUBLIC CryptonightR_instruction163 +PUBLIC CryptonightR_instruction164 +PUBLIC CryptonightR_instruction165 +PUBLIC CryptonightR_instruction166 +PUBLIC CryptonightR_instruction167 +PUBLIC CryptonightR_instruction168 +PUBLIC CryptonightR_instruction169 +PUBLIC CryptonightR_instruction170 +PUBLIC CryptonightR_instruction171 +PUBLIC CryptonightR_instruction172 +PUBLIC CryptonightR_instruction173 +PUBLIC CryptonightR_instruction174 +PUBLIC CryptonightR_instruction175 +PUBLIC CryptonightR_instruction176 +PUBLIC CryptonightR_instruction177 +PUBLIC CryptonightR_instruction178 +PUBLIC CryptonightR_instruction179 +PUBLIC CryptonightR_instruction180 +PUBLIC CryptonightR_instruction181 +PUBLIC CryptonightR_instruction182 +PUBLIC CryptonightR_instruction183 +PUBLIC CryptonightR_instruction184 +PUBLIC CryptonightR_instruction185 +PUBLIC CryptonightR_instruction186 +PUBLIC CryptonightR_instruction187 +PUBLIC CryptonightR_instruction188 +PUBLIC CryptonightR_instruction189 +PUBLIC CryptonightR_instruction190 +PUBLIC CryptonightR_instruction191 +PUBLIC CryptonightR_instruction192 +PUBLIC CryptonightR_instruction193 +PUBLIC CryptonightR_instruction194 +PUBLIC CryptonightR_instruction195 +PUBLIC CryptonightR_instruction196 +PUBLIC CryptonightR_instruction197 +PUBLIC CryptonightR_instruction198 +PUBLIC CryptonightR_instruction199 +PUBLIC CryptonightR_instruction200 +PUBLIC CryptonightR_instruction201 +PUBLIC CryptonightR_instruction202 +PUBLIC CryptonightR_instruction203 +PUBLIC CryptonightR_instruction204 +PUBLIC CryptonightR_instruction205 +PUBLIC CryptonightR_instruction206 +PUBLIC CryptonightR_instruction207 +PUBLIC CryptonightR_instruction208 +PUBLIC CryptonightR_instruction209 +PUBLIC CryptonightR_instruction210 +PUBLIC CryptonightR_instruction211 +PUBLIC CryptonightR_instruction212 +PUBLIC CryptonightR_instruction213 +PUBLIC CryptonightR_instruction214 +PUBLIC CryptonightR_instruction215 +PUBLIC CryptonightR_instruction216 +PUBLIC CryptonightR_instruction217 +PUBLIC CryptonightR_instruction218 +PUBLIC CryptonightR_instruction219 +PUBLIC CryptonightR_instruction220 +PUBLIC CryptonightR_instruction221 +PUBLIC CryptonightR_instruction222 +PUBLIC CryptonightR_instruction223 +PUBLIC CryptonightR_instruction224 +PUBLIC CryptonightR_instruction225 +PUBLIC CryptonightR_instruction226 +PUBLIC CryptonightR_instruction227 +PUBLIC CryptonightR_instruction228 +PUBLIC CryptonightR_instruction229 +PUBLIC CryptonightR_instruction230 +PUBLIC CryptonightR_instruction231 +PUBLIC CryptonightR_instruction232 +PUBLIC CryptonightR_instruction233 +PUBLIC CryptonightR_instruction234 +PUBLIC CryptonightR_instruction235 +PUBLIC CryptonightR_instruction236 +PUBLIC CryptonightR_instruction237 +PUBLIC CryptonightR_instruction238 +PUBLIC CryptonightR_instruction239 +PUBLIC CryptonightR_instruction240 +PUBLIC CryptonightR_instruction241 +PUBLIC CryptonightR_instruction242 +PUBLIC CryptonightR_instruction243 +PUBLIC CryptonightR_instruction244 +PUBLIC CryptonightR_instruction245 +PUBLIC CryptonightR_instruction246 +PUBLIC CryptonightR_instruction247 +PUBLIC CryptonightR_instruction248 +PUBLIC CryptonightR_instruction249 +PUBLIC CryptonightR_instruction250 +PUBLIC CryptonightR_instruction251 +PUBLIC CryptonightR_instruction252 +PUBLIC CryptonightR_instruction253 +PUBLIC CryptonightR_instruction254 +PUBLIC CryptonightR_instruction255 +PUBLIC CryptonightR_instruction256 +PUBLIC CryptonightR_instruction_mov0 +PUBLIC CryptonightR_instruction_mov1 +PUBLIC CryptonightR_instruction_mov2 +PUBLIC CryptonightR_instruction_mov3 +PUBLIC CryptonightR_instruction_mov4 +PUBLIC CryptonightR_instruction_mov5 +PUBLIC CryptonightR_instruction_mov6 +PUBLIC CryptonightR_instruction_mov7 +PUBLIC CryptonightR_instruction_mov8 +PUBLIC CryptonightR_instruction_mov9 +PUBLIC CryptonightR_instruction_mov10 +PUBLIC CryptonightR_instruction_mov11 +PUBLIC CryptonightR_instruction_mov12 +PUBLIC CryptonightR_instruction_mov13 +PUBLIC CryptonightR_instruction_mov14 +PUBLIC CryptonightR_instruction_mov15 +PUBLIC CryptonightR_instruction_mov16 +PUBLIC CryptonightR_instruction_mov17 +PUBLIC CryptonightR_instruction_mov18 +PUBLIC CryptonightR_instruction_mov19 +PUBLIC CryptonightR_instruction_mov20 +PUBLIC CryptonightR_instruction_mov21 +PUBLIC CryptonightR_instruction_mov22 +PUBLIC CryptonightR_instruction_mov23 +PUBLIC CryptonightR_instruction_mov24 +PUBLIC CryptonightR_instruction_mov25 +PUBLIC CryptonightR_instruction_mov26 +PUBLIC CryptonightR_instruction_mov27 +PUBLIC CryptonightR_instruction_mov28 +PUBLIC CryptonightR_instruction_mov29 +PUBLIC CryptonightR_instruction_mov30 +PUBLIC CryptonightR_instruction_mov31 +PUBLIC CryptonightR_instruction_mov32 +PUBLIC CryptonightR_instruction_mov33 +PUBLIC CryptonightR_instruction_mov34 +PUBLIC CryptonightR_instruction_mov35 +PUBLIC CryptonightR_instruction_mov36 +PUBLIC CryptonightR_instruction_mov37 +PUBLIC CryptonightR_instruction_mov38 +PUBLIC CryptonightR_instruction_mov39 +PUBLIC CryptonightR_instruction_mov40 +PUBLIC CryptonightR_instruction_mov41 +PUBLIC CryptonightR_instruction_mov42 +PUBLIC CryptonightR_instruction_mov43 +PUBLIC CryptonightR_instruction_mov44 +PUBLIC CryptonightR_instruction_mov45 +PUBLIC CryptonightR_instruction_mov46 +PUBLIC CryptonightR_instruction_mov47 +PUBLIC CryptonightR_instruction_mov48 +PUBLIC CryptonightR_instruction_mov49 +PUBLIC CryptonightR_instruction_mov50 +PUBLIC CryptonightR_instruction_mov51 +PUBLIC CryptonightR_instruction_mov52 +PUBLIC CryptonightR_instruction_mov53 +PUBLIC CryptonightR_instruction_mov54 +PUBLIC CryptonightR_instruction_mov55 +PUBLIC CryptonightR_instruction_mov56 +PUBLIC CryptonightR_instruction_mov57 +PUBLIC CryptonightR_instruction_mov58 +PUBLIC CryptonightR_instruction_mov59 +PUBLIC CryptonightR_instruction_mov60 +PUBLIC CryptonightR_instruction_mov61 +PUBLIC CryptonightR_instruction_mov62 +PUBLIC CryptonightR_instruction_mov63 +PUBLIC CryptonightR_instruction_mov64 +PUBLIC CryptonightR_instruction_mov65 +PUBLIC CryptonightR_instruction_mov66 +PUBLIC CryptonightR_instruction_mov67 +PUBLIC CryptonightR_instruction_mov68 +PUBLIC CryptonightR_instruction_mov69 +PUBLIC CryptonightR_instruction_mov70 +PUBLIC CryptonightR_instruction_mov71 +PUBLIC CryptonightR_instruction_mov72 +PUBLIC CryptonightR_instruction_mov73 +PUBLIC CryptonightR_instruction_mov74 +PUBLIC CryptonightR_instruction_mov75 +PUBLIC CryptonightR_instruction_mov76 +PUBLIC CryptonightR_instruction_mov77 +PUBLIC CryptonightR_instruction_mov78 +PUBLIC CryptonightR_instruction_mov79 +PUBLIC CryptonightR_instruction_mov80 +PUBLIC CryptonightR_instruction_mov81 +PUBLIC CryptonightR_instruction_mov82 +PUBLIC CryptonightR_instruction_mov83 +PUBLIC CryptonightR_instruction_mov84 +PUBLIC CryptonightR_instruction_mov85 +PUBLIC CryptonightR_instruction_mov86 +PUBLIC CryptonightR_instruction_mov87 +PUBLIC CryptonightR_instruction_mov88 +PUBLIC CryptonightR_instruction_mov89 +PUBLIC CryptonightR_instruction_mov90 +PUBLIC CryptonightR_instruction_mov91 +PUBLIC CryptonightR_instruction_mov92 +PUBLIC CryptonightR_instruction_mov93 +PUBLIC CryptonightR_instruction_mov94 +PUBLIC CryptonightR_instruction_mov95 +PUBLIC CryptonightR_instruction_mov96 +PUBLIC CryptonightR_instruction_mov97 +PUBLIC CryptonightR_instruction_mov98 +PUBLIC CryptonightR_instruction_mov99 +PUBLIC CryptonightR_instruction_mov100 +PUBLIC CryptonightR_instruction_mov101 +PUBLIC CryptonightR_instruction_mov102 +PUBLIC CryptonightR_instruction_mov103 +PUBLIC CryptonightR_instruction_mov104 +PUBLIC CryptonightR_instruction_mov105 +PUBLIC CryptonightR_instruction_mov106 +PUBLIC CryptonightR_instruction_mov107 +PUBLIC CryptonightR_instruction_mov108 +PUBLIC CryptonightR_instruction_mov109 +PUBLIC CryptonightR_instruction_mov110 +PUBLIC CryptonightR_instruction_mov111 +PUBLIC CryptonightR_instruction_mov112 +PUBLIC CryptonightR_instruction_mov113 +PUBLIC CryptonightR_instruction_mov114 +PUBLIC CryptonightR_instruction_mov115 +PUBLIC CryptonightR_instruction_mov116 +PUBLIC CryptonightR_instruction_mov117 +PUBLIC CryptonightR_instruction_mov118 +PUBLIC CryptonightR_instruction_mov119 +PUBLIC CryptonightR_instruction_mov120 +PUBLIC CryptonightR_instruction_mov121 +PUBLIC CryptonightR_instruction_mov122 +PUBLIC CryptonightR_instruction_mov123 +PUBLIC CryptonightR_instruction_mov124 +PUBLIC CryptonightR_instruction_mov125 +PUBLIC CryptonightR_instruction_mov126 +PUBLIC CryptonightR_instruction_mov127 +PUBLIC CryptonightR_instruction_mov128 +PUBLIC CryptonightR_instruction_mov129 +PUBLIC CryptonightR_instruction_mov130 +PUBLIC CryptonightR_instruction_mov131 +PUBLIC CryptonightR_instruction_mov132 +PUBLIC CryptonightR_instruction_mov133 +PUBLIC CryptonightR_instruction_mov134 +PUBLIC CryptonightR_instruction_mov135 +PUBLIC CryptonightR_instruction_mov136 +PUBLIC CryptonightR_instruction_mov137 +PUBLIC CryptonightR_instruction_mov138 +PUBLIC CryptonightR_instruction_mov139 +PUBLIC CryptonightR_instruction_mov140 +PUBLIC CryptonightR_instruction_mov141 +PUBLIC CryptonightR_instruction_mov142 +PUBLIC CryptonightR_instruction_mov143 +PUBLIC CryptonightR_instruction_mov144 +PUBLIC CryptonightR_instruction_mov145 +PUBLIC CryptonightR_instruction_mov146 +PUBLIC CryptonightR_instruction_mov147 +PUBLIC CryptonightR_instruction_mov148 +PUBLIC CryptonightR_instruction_mov149 +PUBLIC CryptonightR_instruction_mov150 +PUBLIC CryptonightR_instruction_mov151 +PUBLIC CryptonightR_instruction_mov152 +PUBLIC CryptonightR_instruction_mov153 +PUBLIC CryptonightR_instruction_mov154 +PUBLIC CryptonightR_instruction_mov155 +PUBLIC CryptonightR_instruction_mov156 +PUBLIC CryptonightR_instruction_mov157 +PUBLIC CryptonightR_instruction_mov158 +PUBLIC CryptonightR_instruction_mov159 +PUBLIC CryptonightR_instruction_mov160 +PUBLIC CryptonightR_instruction_mov161 +PUBLIC CryptonightR_instruction_mov162 +PUBLIC CryptonightR_instruction_mov163 +PUBLIC CryptonightR_instruction_mov164 +PUBLIC CryptonightR_instruction_mov165 +PUBLIC CryptonightR_instruction_mov166 +PUBLIC CryptonightR_instruction_mov167 +PUBLIC CryptonightR_instruction_mov168 +PUBLIC CryptonightR_instruction_mov169 +PUBLIC CryptonightR_instruction_mov170 +PUBLIC CryptonightR_instruction_mov171 +PUBLIC CryptonightR_instruction_mov172 +PUBLIC CryptonightR_instruction_mov173 +PUBLIC CryptonightR_instruction_mov174 +PUBLIC CryptonightR_instruction_mov175 +PUBLIC CryptonightR_instruction_mov176 +PUBLIC CryptonightR_instruction_mov177 +PUBLIC CryptonightR_instruction_mov178 +PUBLIC CryptonightR_instruction_mov179 +PUBLIC CryptonightR_instruction_mov180 +PUBLIC CryptonightR_instruction_mov181 +PUBLIC CryptonightR_instruction_mov182 +PUBLIC CryptonightR_instruction_mov183 +PUBLIC CryptonightR_instruction_mov184 +PUBLIC CryptonightR_instruction_mov185 +PUBLIC CryptonightR_instruction_mov186 +PUBLIC CryptonightR_instruction_mov187 +PUBLIC CryptonightR_instruction_mov188 +PUBLIC CryptonightR_instruction_mov189 +PUBLIC CryptonightR_instruction_mov190 +PUBLIC CryptonightR_instruction_mov191 +PUBLIC CryptonightR_instruction_mov192 +PUBLIC CryptonightR_instruction_mov193 +PUBLIC CryptonightR_instruction_mov194 +PUBLIC CryptonightR_instruction_mov195 +PUBLIC CryptonightR_instruction_mov196 +PUBLIC CryptonightR_instruction_mov197 +PUBLIC CryptonightR_instruction_mov198 +PUBLIC CryptonightR_instruction_mov199 +PUBLIC CryptonightR_instruction_mov200 +PUBLIC CryptonightR_instruction_mov201 +PUBLIC CryptonightR_instruction_mov202 +PUBLIC CryptonightR_instruction_mov203 +PUBLIC CryptonightR_instruction_mov204 +PUBLIC CryptonightR_instruction_mov205 +PUBLIC CryptonightR_instruction_mov206 +PUBLIC CryptonightR_instruction_mov207 +PUBLIC CryptonightR_instruction_mov208 +PUBLIC CryptonightR_instruction_mov209 +PUBLIC CryptonightR_instruction_mov210 +PUBLIC CryptonightR_instruction_mov211 +PUBLIC CryptonightR_instruction_mov212 +PUBLIC CryptonightR_instruction_mov213 +PUBLIC CryptonightR_instruction_mov214 +PUBLIC CryptonightR_instruction_mov215 +PUBLIC CryptonightR_instruction_mov216 +PUBLIC CryptonightR_instruction_mov217 +PUBLIC CryptonightR_instruction_mov218 +PUBLIC CryptonightR_instruction_mov219 +PUBLIC CryptonightR_instruction_mov220 +PUBLIC CryptonightR_instruction_mov221 +PUBLIC CryptonightR_instruction_mov222 +PUBLIC CryptonightR_instruction_mov223 +PUBLIC CryptonightR_instruction_mov224 +PUBLIC CryptonightR_instruction_mov225 +PUBLIC CryptonightR_instruction_mov226 +PUBLIC CryptonightR_instruction_mov227 +PUBLIC CryptonightR_instruction_mov228 +PUBLIC CryptonightR_instruction_mov229 +PUBLIC CryptonightR_instruction_mov230 +PUBLIC CryptonightR_instruction_mov231 +PUBLIC CryptonightR_instruction_mov232 +PUBLIC CryptonightR_instruction_mov233 +PUBLIC CryptonightR_instruction_mov234 +PUBLIC CryptonightR_instruction_mov235 +PUBLIC CryptonightR_instruction_mov236 +PUBLIC CryptonightR_instruction_mov237 +PUBLIC CryptonightR_instruction_mov238 +PUBLIC CryptonightR_instruction_mov239 +PUBLIC CryptonightR_instruction_mov240 +PUBLIC CryptonightR_instruction_mov241 +PUBLIC CryptonightR_instruction_mov242 +PUBLIC CryptonightR_instruction_mov243 +PUBLIC CryptonightR_instruction_mov244 +PUBLIC CryptonightR_instruction_mov245 +PUBLIC CryptonightR_instruction_mov246 +PUBLIC CryptonightR_instruction_mov247 +PUBLIC CryptonightR_instruction_mov248 +PUBLIC CryptonightR_instruction_mov249 +PUBLIC CryptonightR_instruction_mov250 +PUBLIC CryptonightR_instruction_mov251 +PUBLIC CryptonightR_instruction_mov252 +PUBLIC CryptonightR_instruction_mov253 +PUBLIC CryptonightR_instruction_mov254 +PUBLIC CryptonightR_instruction_mov255 +PUBLIC CryptonightR_instruction_mov256 + +INCLUDE CryptonightWOW_template_win.inc +INCLUDE CryptonightR_template_win.inc +INCLUDE CryptonightWOW_soft_aes_template_win.inc +INCLUDE CryptonightR_soft_aes_template_win.inc + +CryptonightR_instruction0: + imul rbx, rbx +CryptonightR_instruction1: + imul rbx, rbx +CryptonightR_instruction2: + imul rbx, rbx +CryptonightR_instruction3: + add rbx, r9 + add rbx, 2147483647 +CryptonightR_instruction4: + sub rbx, r9 +CryptonightR_instruction5: + ror ebx, cl +CryptonightR_instruction6: + rol ebx, cl +CryptonightR_instruction7: + xor rbx, r9 +CryptonightR_instruction8: + imul rsi, rbx +CryptonightR_instruction9: + imul rsi, rbx +CryptonightR_instruction10: + imul rsi, rbx +CryptonightR_instruction11: + add rsi, rbx + add rsi, 2147483647 +CryptonightR_instruction12: + sub rsi, rbx +CryptonightR_instruction13: + ror esi, cl +CryptonightR_instruction14: + rol esi, cl +CryptonightR_instruction15: + xor rsi, rbx +CryptonightR_instruction16: + imul rdi, rbx +CryptonightR_instruction17: + imul rdi, rbx +CryptonightR_instruction18: + imul rdi, rbx +CryptonightR_instruction19: + add rdi, rbx + add rdi, 2147483647 +CryptonightR_instruction20: + sub rdi, rbx +CryptonightR_instruction21: + ror edi, cl +CryptonightR_instruction22: + rol edi, cl +CryptonightR_instruction23: + xor rdi, rbx +CryptonightR_instruction24: + imul rbp, rbx +CryptonightR_instruction25: + imul rbp, rbx +CryptonightR_instruction26: + imul rbp, rbx +CryptonightR_instruction27: + add rbp, rbx + add rbp, 2147483647 +CryptonightR_instruction28: + sub rbp, rbx +CryptonightR_instruction29: + ror ebp, cl +CryptonightR_instruction30: + rol ebp, cl +CryptonightR_instruction31: + xor rbp, rbx +CryptonightR_instruction32: + imul rbx, rsi +CryptonightR_instruction33: + imul rbx, rsi +CryptonightR_instruction34: + imul rbx, rsi +CryptonightR_instruction35: + add rbx, rsi + add rbx, 2147483647 +CryptonightR_instruction36: + sub rbx, rsi +CryptonightR_instruction37: + ror ebx, cl +CryptonightR_instruction38: + rol ebx, cl +CryptonightR_instruction39: + xor rbx, rsi +CryptonightR_instruction40: + imul rsi, rsi +CryptonightR_instruction41: + imul rsi, rsi +CryptonightR_instruction42: + imul rsi, rsi +CryptonightR_instruction43: + add rsi, r9 + add rsi, 2147483647 +CryptonightR_instruction44: + sub rsi, r9 +CryptonightR_instruction45: + ror esi, cl +CryptonightR_instruction46: + rol esi, cl +CryptonightR_instruction47: + xor rsi, r9 +CryptonightR_instruction48: + imul rdi, rsi +CryptonightR_instruction49: + imul rdi, rsi +CryptonightR_instruction50: + imul rdi, rsi +CryptonightR_instruction51: + add rdi, rsi + add rdi, 2147483647 +CryptonightR_instruction52: + sub rdi, rsi +CryptonightR_instruction53: + ror edi, cl +CryptonightR_instruction54: + rol edi, cl +CryptonightR_instruction55: + xor rdi, rsi +CryptonightR_instruction56: + imul rbp, rsi +CryptonightR_instruction57: + imul rbp, rsi +CryptonightR_instruction58: + imul rbp, rsi +CryptonightR_instruction59: + add rbp, rsi + add rbp, 2147483647 +CryptonightR_instruction60: + sub rbp, rsi +CryptonightR_instruction61: + ror ebp, cl +CryptonightR_instruction62: + rol ebp, cl +CryptonightR_instruction63: + xor rbp, rsi +CryptonightR_instruction64: + imul rbx, rdi +CryptonightR_instruction65: + imul rbx, rdi +CryptonightR_instruction66: + imul rbx, rdi +CryptonightR_instruction67: + add rbx, rdi + add rbx, 2147483647 +CryptonightR_instruction68: + sub rbx, rdi +CryptonightR_instruction69: + ror ebx, cl +CryptonightR_instruction70: + rol ebx, cl +CryptonightR_instruction71: + xor rbx, rdi +CryptonightR_instruction72: + imul rsi, rdi +CryptonightR_instruction73: + imul rsi, rdi +CryptonightR_instruction74: + imul rsi, rdi +CryptonightR_instruction75: + add rsi, rdi + add rsi, 2147483647 +CryptonightR_instruction76: + sub rsi, rdi +CryptonightR_instruction77: + ror esi, cl +CryptonightR_instruction78: + rol esi, cl +CryptonightR_instruction79: + xor rsi, rdi +CryptonightR_instruction80: + imul rdi, rdi +CryptonightR_instruction81: + imul rdi, rdi +CryptonightR_instruction82: + imul rdi, rdi +CryptonightR_instruction83: + add rdi, r9 + add rdi, 2147483647 +CryptonightR_instruction84: + sub rdi, r9 +CryptonightR_instruction85: + ror edi, cl +CryptonightR_instruction86: + rol edi, cl +CryptonightR_instruction87: + xor rdi, r9 +CryptonightR_instruction88: + imul rbp, rdi +CryptonightR_instruction89: + imul rbp, rdi +CryptonightR_instruction90: + imul rbp, rdi +CryptonightR_instruction91: + add rbp, rdi + add rbp, 2147483647 +CryptonightR_instruction92: + sub rbp, rdi +CryptonightR_instruction93: + ror ebp, cl +CryptonightR_instruction94: + rol ebp, cl +CryptonightR_instruction95: + xor rbp, rdi +CryptonightR_instruction96: + imul rbx, rbp +CryptonightR_instruction97: + imul rbx, rbp +CryptonightR_instruction98: + imul rbx, rbp +CryptonightR_instruction99: + add rbx, rbp + add rbx, 2147483647 +CryptonightR_instruction100: + sub rbx, rbp +CryptonightR_instruction101: + ror ebx, cl +CryptonightR_instruction102: + rol ebx, cl +CryptonightR_instruction103: + xor rbx, rbp +CryptonightR_instruction104: + imul rsi, rbp +CryptonightR_instruction105: + imul rsi, rbp +CryptonightR_instruction106: + imul rsi, rbp +CryptonightR_instruction107: + add rsi, rbp + add rsi, 2147483647 +CryptonightR_instruction108: + sub rsi, rbp +CryptonightR_instruction109: + ror esi, cl +CryptonightR_instruction110: + rol esi, cl +CryptonightR_instruction111: + xor rsi, rbp +CryptonightR_instruction112: + imul rdi, rbp +CryptonightR_instruction113: + imul rdi, rbp +CryptonightR_instruction114: + imul rdi, rbp +CryptonightR_instruction115: + add rdi, rbp + add rdi, 2147483647 +CryptonightR_instruction116: + sub rdi, rbp +CryptonightR_instruction117: + ror edi, cl +CryptonightR_instruction118: + rol edi, cl +CryptonightR_instruction119: + xor rdi, rbp +CryptonightR_instruction120: + imul rbp, rbp +CryptonightR_instruction121: + imul rbp, rbp +CryptonightR_instruction122: + imul rbp, rbp +CryptonightR_instruction123: + add rbp, r9 + add rbp, 2147483647 +CryptonightR_instruction124: + sub rbp, r9 +CryptonightR_instruction125: + ror ebp, cl +CryptonightR_instruction126: + rol ebp, cl +CryptonightR_instruction127: + xor rbp, r9 +CryptonightR_instruction128: + imul rbx, rsp +CryptonightR_instruction129: + imul rbx, rsp +CryptonightR_instruction130: + imul rbx, rsp +CryptonightR_instruction131: + add rbx, rsp + add rbx, 2147483647 +CryptonightR_instruction132: + sub rbx, rsp +CryptonightR_instruction133: + ror ebx, cl +CryptonightR_instruction134: + rol ebx, cl +CryptonightR_instruction135: + xor rbx, rsp +CryptonightR_instruction136: + imul rsi, rsp +CryptonightR_instruction137: + imul rsi, rsp +CryptonightR_instruction138: + imul rsi, rsp +CryptonightR_instruction139: + add rsi, rsp + add rsi, 2147483647 +CryptonightR_instruction140: + sub rsi, rsp +CryptonightR_instruction141: + ror esi, cl +CryptonightR_instruction142: + rol esi, cl +CryptonightR_instruction143: + xor rsi, rsp +CryptonightR_instruction144: + imul rdi, rsp +CryptonightR_instruction145: + imul rdi, rsp +CryptonightR_instruction146: + imul rdi, rsp +CryptonightR_instruction147: + add rdi, rsp + add rdi, 2147483647 +CryptonightR_instruction148: + sub rdi, rsp +CryptonightR_instruction149: + ror edi, cl +CryptonightR_instruction150: + rol edi, cl +CryptonightR_instruction151: + xor rdi, rsp +CryptonightR_instruction152: + imul rbp, rsp +CryptonightR_instruction153: + imul rbp, rsp +CryptonightR_instruction154: + imul rbp, rsp +CryptonightR_instruction155: + add rbp, rsp + add rbp, 2147483647 +CryptonightR_instruction156: + sub rbp, rsp +CryptonightR_instruction157: + ror ebp, cl +CryptonightR_instruction158: + rol ebp, cl +CryptonightR_instruction159: + xor rbp, rsp +CryptonightR_instruction160: + imul rbx, r15 +CryptonightR_instruction161: + imul rbx, r15 +CryptonightR_instruction162: + imul rbx, r15 +CryptonightR_instruction163: + add rbx, r15 + add rbx, 2147483647 +CryptonightR_instruction164: + sub rbx, r15 +CryptonightR_instruction165: + ror ebx, cl +CryptonightR_instruction166: + rol ebx, cl +CryptonightR_instruction167: + xor rbx, r15 +CryptonightR_instruction168: + imul rsi, r15 +CryptonightR_instruction169: + imul rsi, r15 +CryptonightR_instruction170: + imul rsi, r15 +CryptonightR_instruction171: + add rsi, r15 + add rsi, 2147483647 +CryptonightR_instruction172: + sub rsi, r15 +CryptonightR_instruction173: + ror esi, cl +CryptonightR_instruction174: + rol esi, cl +CryptonightR_instruction175: + xor rsi, r15 +CryptonightR_instruction176: + imul rdi, r15 +CryptonightR_instruction177: + imul rdi, r15 +CryptonightR_instruction178: + imul rdi, r15 +CryptonightR_instruction179: + add rdi, r15 + add rdi, 2147483647 +CryptonightR_instruction180: + sub rdi, r15 +CryptonightR_instruction181: + ror edi, cl +CryptonightR_instruction182: + rol edi, cl +CryptonightR_instruction183: + xor rdi, r15 +CryptonightR_instruction184: + imul rbp, r15 +CryptonightR_instruction185: + imul rbp, r15 +CryptonightR_instruction186: + imul rbp, r15 +CryptonightR_instruction187: + add rbp, r15 + add rbp, 2147483647 +CryptonightR_instruction188: + sub rbp, r15 +CryptonightR_instruction189: + ror ebp, cl +CryptonightR_instruction190: + rol ebp, cl +CryptonightR_instruction191: + xor rbp, r15 +CryptonightR_instruction192: + imul rbx, rax +CryptonightR_instruction193: + imul rbx, rax +CryptonightR_instruction194: + imul rbx, rax +CryptonightR_instruction195: + add rbx, rax + add rbx, 2147483647 +CryptonightR_instruction196: + sub rbx, rax +CryptonightR_instruction197: + ror ebx, cl +CryptonightR_instruction198: + rol ebx, cl +CryptonightR_instruction199: + xor rbx, rax +CryptonightR_instruction200: + imul rsi, rax +CryptonightR_instruction201: + imul rsi, rax +CryptonightR_instruction202: + imul rsi, rax +CryptonightR_instruction203: + add rsi, rax + add rsi, 2147483647 +CryptonightR_instruction204: + sub rsi, rax +CryptonightR_instruction205: + ror esi, cl +CryptonightR_instruction206: + rol esi, cl +CryptonightR_instruction207: + xor rsi, rax +CryptonightR_instruction208: + imul rdi, rax +CryptonightR_instruction209: + imul rdi, rax +CryptonightR_instruction210: + imul rdi, rax +CryptonightR_instruction211: + add rdi, rax + add rdi, 2147483647 +CryptonightR_instruction212: + sub rdi, rax +CryptonightR_instruction213: + ror edi, cl +CryptonightR_instruction214: + rol edi, cl +CryptonightR_instruction215: + xor rdi, rax +CryptonightR_instruction216: + imul rbp, rax +CryptonightR_instruction217: + imul rbp, rax +CryptonightR_instruction218: + imul rbp, rax +CryptonightR_instruction219: + add rbp, rax + add rbp, 2147483647 +CryptonightR_instruction220: + sub rbp, rax +CryptonightR_instruction221: + ror ebp, cl +CryptonightR_instruction222: + rol ebp, cl +CryptonightR_instruction223: + xor rbp, rax +CryptonightR_instruction224: + imul rbx, rdx +CryptonightR_instruction225: + imul rbx, rdx +CryptonightR_instruction226: + imul rbx, rdx +CryptonightR_instruction227: + add rbx, rdx + add rbx, 2147483647 +CryptonightR_instruction228: + sub rbx, rdx +CryptonightR_instruction229: + ror ebx, cl +CryptonightR_instruction230: + rol ebx, cl +CryptonightR_instruction231: + xor rbx, rdx +CryptonightR_instruction232: + imul rsi, rdx +CryptonightR_instruction233: + imul rsi, rdx +CryptonightR_instruction234: + imul rsi, rdx +CryptonightR_instruction235: + add rsi, rdx + add rsi, 2147483647 +CryptonightR_instruction236: + sub rsi, rdx +CryptonightR_instruction237: + ror esi, cl +CryptonightR_instruction238: + rol esi, cl +CryptonightR_instruction239: + xor rsi, rdx +CryptonightR_instruction240: + imul rdi, rdx +CryptonightR_instruction241: + imul rdi, rdx +CryptonightR_instruction242: + imul rdi, rdx +CryptonightR_instruction243: + add rdi, rdx + add rdi, 2147483647 +CryptonightR_instruction244: + sub rdi, rdx +CryptonightR_instruction245: + ror edi, cl +CryptonightR_instruction246: + rol edi, cl +CryptonightR_instruction247: + xor rdi, rdx +CryptonightR_instruction248: + imul rbp, rdx +CryptonightR_instruction249: + imul rbp, rdx +CryptonightR_instruction250: + imul rbp, rdx +CryptonightR_instruction251: + add rbp, rdx + add rbp, 2147483647 +CryptonightR_instruction252: + sub rbp, rdx +CryptonightR_instruction253: + ror ebp, cl +CryptonightR_instruction254: + rol ebp, cl +CryptonightR_instruction255: + xor rbp, rdx +CryptonightR_instruction256: + imul rbx, rbx +CryptonightR_instruction_mov0: + +CryptonightR_instruction_mov1: + +CryptonightR_instruction_mov2: + +CryptonightR_instruction_mov3: + +CryptonightR_instruction_mov4: + +CryptonightR_instruction_mov5: + mov rcx, rbx +CryptonightR_instruction_mov6: + mov rcx, rbx +CryptonightR_instruction_mov7: + +CryptonightR_instruction_mov8: + +CryptonightR_instruction_mov9: + +CryptonightR_instruction_mov10: + +CryptonightR_instruction_mov11: + +CryptonightR_instruction_mov12: + +CryptonightR_instruction_mov13: + mov rcx, rbx +CryptonightR_instruction_mov14: + mov rcx, rbx +CryptonightR_instruction_mov15: + +CryptonightR_instruction_mov16: + +CryptonightR_instruction_mov17: + +CryptonightR_instruction_mov18: + +CryptonightR_instruction_mov19: + +CryptonightR_instruction_mov20: + +CryptonightR_instruction_mov21: + mov rcx, rbx +CryptonightR_instruction_mov22: + mov rcx, rbx +CryptonightR_instruction_mov23: + +CryptonightR_instruction_mov24: + +CryptonightR_instruction_mov25: + +CryptonightR_instruction_mov26: + +CryptonightR_instruction_mov27: + +CryptonightR_instruction_mov28: + +CryptonightR_instruction_mov29: + mov rcx, rbx +CryptonightR_instruction_mov30: + mov rcx, rbx +CryptonightR_instruction_mov31: + +CryptonightR_instruction_mov32: + +CryptonightR_instruction_mov33: + +CryptonightR_instruction_mov34: + +CryptonightR_instruction_mov35: + +CryptonightR_instruction_mov36: + +CryptonightR_instruction_mov37: + mov rcx, rsi +CryptonightR_instruction_mov38: + mov rcx, rsi +CryptonightR_instruction_mov39: + +CryptonightR_instruction_mov40: + +CryptonightR_instruction_mov41: + +CryptonightR_instruction_mov42: + +CryptonightR_instruction_mov43: + +CryptonightR_instruction_mov44: + +CryptonightR_instruction_mov45: + mov rcx, rsi +CryptonightR_instruction_mov46: + mov rcx, rsi +CryptonightR_instruction_mov47: + +CryptonightR_instruction_mov48: + +CryptonightR_instruction_mov49: + +CryptonightR_instruction_mov50: + +CryptonightR_instruction_mov51: + +CryptonightR_instruction_mov52: + +CryptonightR_instruction_mov53: + mov rcx, rsi +CryptonightR_instruction_mov54: + mov rcx, rsi +CryptonightR_instruction_mov55: + +CryptonightR_instruction_mov56: + +CryptonightR_instruction_mov57: + +CryptonightR_instruction_mov58: + +CryptonightR_instruction_mov59: + +CryptonightR_instruction_mov60: + +CryptonightR_instruction_mov61: + mov rcx, rsi +CryptonightR_instruction_mov62: + mov rcx, rsi +CryptonightR_instruction_mov63: + +CryptonightR_instruction_mov64: + +CryptonightR_instruction_mov65: + +CryptonightR_instruction_mov66: + +CryptonightR_instruction_mov67: + +CryptonightR_instruction_mov68: + +CryptonightR_instruction_mov69: + mov rcx, rdi +CryptonightR_instruction_mov70: + mov rcx, rdi +CryptonightR_instruction_mov71: + +CryptonightR_instruction_mov72: + +CryptonightR_instruction_mov73: + +CryptonightR_instruction_mov74: + +CryptonightR_instruction_mov75: + +CryptonightR_instruction_mov76: + +CryptonightR_instruction_mov77: + mov rcx, rdi +CryptonightR_instruction_mov78: + mov rcx, rdi +CryptonightR_instruction_mov79: + +CryptonightR_instruction_mov80: + +CryptonightR_instruction_mov81: + +CryptonightR_instruction_mov82: + +CryptonightR_instruction_mov83: + +CryptonightR_instruction_mov84: + +CryptonightR_instruction_mov85: + mov rcx, rdi +CryptonightR_instruction_mov86: + mov rcx, rdi +CryptonightR_instruction_mov87: + +CryptonightR_instruction_mov88: + +CryptonightR_instruction_mov89: + +CryptonightR_instruction_mov90: + +CryptonightR_instruction_mov91: + +CryptonightR_instruction_mov92: + +CryptonightR_instruction_mov93: + mov rcx, rdi +CryptonightR_instruction_mov94: + mov rcx, rdi +CryptonightR_instruction_mov95: + +CryptonightR_instruction_mov96: + +CryptonightR_instruction_mov97: + +CryptonightR_instruction_mov98: + +CryptonightR_instruction_mov99: + +CryptonightR_instruction_mov100: + +CryptonightR_instruction_mov101: + mov rcx, rbp +CryptonightR_instruction_mov102: + mov rcx, rbp +CryptonightR_instruction_mov103: + +CryptonightR_instruction_mov104: + +CryptonightR_instruction_mov105: + +CryptonightR_instruction_mov106: + +CryptonightR_instruction_mov107: + +CryptonightR_instruction_mov108: + +CryptonightR_instruction_mov109: + mov rcx, rbp +CryptonightR_instruction_mov110: + mov rcx, rbp +CryptonightR_instruction_mov111: + +CryptonightR_instruction_mov112: + +CryptonightR_instruction_mov113: + +CryptonightR_instruction_mov114: + +CryptonightR_instruction_mov115: + +CryptonightR_instruction_mov116: + +CryptonightR_instruction_mov117: + mov rcx, rbp +CryptonightR_instruction_mov118: + mov rcx, rbp +CryptonightR_instruction_mov119: + +CryptonightR_instruction_mov120: + +CryptonightR_instruction_mov121: + +CryptonightR_instruction_mov122: + +CryptonightR_instruction_mov123: + +CryptonightR_instruction_mov124: + +CryptonightR_instruction_mov125: + mov rcx, rbp +CryptonightR_instruction_mov126: + mov rcx, rbp +CryptonightR_instruction_mov127: + +CryptonightR_instruction_mov128: + +CryptonightR_instruction_mov129: + +CryptonightR_instruction_mov130: + +CryptonightR_instruction_mov131: + +CryptonightR_instruction_mov132: + +CryptonightR_instruction_mov133: + mov rcx, rsp +CryptonightR_instruction_mov134: + mov rcx, rsp +CryptonightR_instruction_mov135: + +CryptonightR_instruction_mov136: + +CryptonightR_instruction_mov137: + +CryptonightR_instruction_mov138: + +CryptonightR_instruction_mov139: + +CryptonightR_instruction_mov140: + +CryptonightR_instruction_mov141: + mov rcx, rsp +CryptonightR_instruction_mov142: + mov rcx, rsp +CryptonightR_instruction_mov143: + +CryptonightR_instruction_mov144: + +CryptonightR_instruction_mov145: + +CryptonightR_instruction_mov146: + +CryptonightR_instruction_mov147: + +CryptonightR_instruction_mov148: + +CryptonightR_instruction_mov149: + mov rcx, rsp +CryptonightR_instruction_mov150: + mov rcx, rsp +CryptonightR_instruction_mov151: + +CryptonightR_instruction_mov152: + +CryptonightR_instruction_mov153: + +CryptonightR_instruction_mov154: + +CryptonightR_instruction_mov155: + +CryptonightR_instruction_mov156: + +CryptonightR_instruction_mov157: + mov rcx, rsp +CryptonightR_instruction_mov158: + mov rcx, rsp +CryptonightR_instruction_mov159: + +CryptonightR_instruction_mov160: + +CryptonightR_instruction_mov161: + +CryptonightR_instruction_mov162: + +CryptonightR_instruction_mov163: + +CryptonightR_instruction_mov164: + +CryptonightR_instruction_mov165: + mov rcx, r15 +CryptonightR_instruction_mov166: + mov rcx, r15 +CryptonightR_instruction_mov167: + +CryptonightR_instruction_mov168: + +CryptonightR_instruction_mov169: + +CryptonightR_instruction_mov170: + +CryptonightR_instruction_mov171: + +CryptonightR_instruction_mov172: + +CryptonightR_instruction_mov173: + mov rcx, r15 +CryptonightR_instruction_mov174: + mov rcx, r15 +CryptonightR_instruction_mov175: + +CryptonightR_instruction_mov176: + +CryptonightR_instruction_mov177: + +CryptonightR_instruction_mov178: + +CryptonightR_instruction_mov179: + +CryptonightR_instruction_mov180: + +CryptonightR_instruction_mov181: + mov rcx, r15 +CryptonightR_instruction_mov182: + mov rcx, r15 +CryptonightR_instruction_mov183: + +CryptonightR_instruction_mov184: + +CryptonightR_instruction_mov185: + +CryptonightR_instruction_mov186: + +CryptonightR_instruction_mov187: + +CryptonightR_instruction_mov188: + +CryptonightR_instruction_mov189: + mov rcx, r15 +CryptonightR_instruction_mov190: + mov rcx, r15 +CryptonightR_instruction_mov191: + +CryptonightR_instruction_mov192: + +CryptonightR_instruction_mov193: + +CryptonightR_instruction_mov194: + +CryptonightR_instruction_mov195: + +CryptonightR_instruction_mov196: + +CryptonightR_instruction_mov197: + mov rcx, rax +CryptonightR_instruction_mov198: + mov rcx, rax +CryptonightR_instruction_mov199: + +CryptonightR_instruction_mov200: + +CryptonightR_instruction_mov201: + +CryptonightR_instruction_mov202: + +CryptonightR_instruction_mov203: + +CryptonightR_instruction_mov204: + +CryptonightR_instruction_mov205: + mov rcx, rax +CryptonightR_instruction_mov206: + mov rcx, rax +CryptonightR_instruction_mov207: + +CryptonightR_instruction_mov208: + +CryptonightR_instruction_mov209: + +CryptonightR_instruction_mov210: + +CryptonightR_instruction_mov211: + +CryptonightR_instruction_mov212: + +CryptonightR_instruction_mov213: + mov rcx, rax +CryptonightR_instruction_mov214: + mov rcx, rax +CryptonightR_instruction_mov215: + +CryptonightR_instruction_mov216: + +CryptonightR_instruction_mov217: + +CryptonightR_instruction_mov218: + +CryptonightR_instruction_mov219: + +CryptonightR_instruction_mov220: + +CryptonightR_instruction_mov221: + mov rcx, rax +CryptonightR_instruction_mov222: + mov rcx, rax +CryptonightR_instruction_mov223: + +CryptonightR_instruction_mov224: + +CryptonightR_instruction_mov225: + +CryptonightR_instruction_mov226: + +CryptonightR_instruction_mov227: + +CryptonightR_instruction_mov228: + +CryptonightR_instruction_mov229: + mov rcx, rdx +CryptonightR_instruction_mov230: + mov rcx, rdx +CryptonightR_instruction_mov231: + +CryptonightR_instruction_mov232: + +CryptonightR_instruction_mov233: + +CryptonightR_instruction_mov234: + +CryptonightR_instruction_mov235: + +CryptonightR_instruction_mov236: + +CryptonightR_instruction_mov237: + mov rcx, rdx +CryptonightR_instruction_mov238: + mov rcx, rdx +CryptonightR_instruction_mov239: + +CryptonightR_instruction_mov240: + +CryptonightR_instruction_mov241: + +CryptonightR_instruction_mov242: + +CryptonightR_instruction_mov243: + +CryptonightR_instruction_mov244: + +CryptonightR_instruction_mov245: + mov rcx, rdx +CryptonightR_instruction_mov246: + mov rcx, rdx +CryptonightR_instruction_mov247: + +CryptonightR_instruction_mov248: + +CryptonightR_instruction_mov249: + +CryptonightR_instruction_mov250: + +CryptonightR_instruction_mov251: + +CryptonightR_instruction_mov252: + +CryptonightR_instruction_mov253: + mov rcx, rdx +CryptonightR_instruction_mov254: + mov rcx, rdx +CryptonightR_instruction_mov255: + +CryptonightR_instruction_mov256: + +_TEXT_CN_TEMPLATE ENDS +END diff --git a/src/crypto/asm/win/CryptonightR_template_win.inc b/src/crypto/cn/asm/win64/CryptonightR_template_win.inc similarity index 97% rename from src/crypto/asm/win/CryptonightR_template_win.inc rename to src/crypto/cn/asm/win64/CryptonightR_template_win.inc index 2f2d71a2..d24eedaa 100644 --- a/src/crypto/asm/win/CryptonightR_template_win.inc +++ b/src/crypto/cn/asm/win64/CryptonightR_template_win.inc @@ -12,6 +12,8 @@ PUBLIC CryptonightR_template_double_end ALIGN(64) CryptonightR_template_part1: + mov rcx, [rcx] + mov QWORD PTR [rsp+16], rbx mov QWORD PTR [rsp+24], rbp mov QWORD PTR [rsp+32], rsi @@ -70,29 +72,30 @@ CryptonightR_template_mainloop: aesenc xmm5, xmm4 - mov r12d, r9d + mov r13d, r9d mov eax, r9d xor r9d, 48 - xor r12d, 16 + xor r13d, 16 xor eax, 32 movdqu xmm0, XMMWORD PTR [r9+r11] movaps xmm3, xmm0 - movdqu xmm2, XMMWORD PTR [r12+r11] + movdqu xmm2, XMMWORD PTR [r13+r11] movdqu xmm1, XMMWORD PTR [rax+r11] pxor xmm0, xmm2 pxor xmm5, xmm1 pxor xmm5, xmm0 - paddq xmm3, xmm7 - paddq xmm2, xmm6 - paddq xmm1, xmm4 - movdqu XMMWORD PTR [r12+r11], xmm3 - movdqu XMMWORD PTR [rax+r11], xmm2 - movdqu XMMWORD PTR [r9+r11], xmm1 movd r12, xmm5 movd r10d, xmm5 and r10d, 2097136 + paddq xmm3, xmm7 + paddq xmm2, xmm6 + paddq xmm1, xmm4 + movdqu XMMWORD PTR [r13+r11], xmm3 + movdqu XMMWORD PTR [rax+r11], xmm2 + movdqu XMMWORD PTR [r9+r11], xmm1 + movdqa xmm0, xmm5 pxor xmm0, xmm6 movdqu XMMWORD PTR [rdx], xmm0 @@ -102,14 +105,16 @@ CryptonightR_template_mainloop: shl rdx, 32 or r13, rdx - xor r13, QWORD PTR [r10+r11] - mov r14, QWORD PTR [r10+r11+8] - movd eax, xmm6 movd edx, xmm7 pextrd r9d, xmm7, 2 + xor r13, QWORD PTR [r10+r11] + mov r14, QWORD PTR [r10+r11+8] + CryptonightR_template_part2: + lea rcx, [r10+r11] + mov eax, edi mov edx, ebp shl rdx, 32 @@ -124,6 +129,8 @@ CryptonightR_template_part2: mov rax, r13 mul r12 + add r15, rax + add rsp, rdx mov r9d, r10d mov r12d, r10d @@ -145,13 +152,10 @@ CryptonightR_template_part2: movdqu XMMWORD PTR [r10+r11], xmm3 movdqa xmm7, xmm6 - add r15, rax - add rsp, rdx - xor r10, 48 - mov QWORD PTR [r10+r11], rsp + mov QWORD PTR [rcx], rsp xor rsp, r13 mov r9d, esp - mov QWORD PTR [r10+r11+8], r15 + mov QWORD PTR [rcx+8], r15 and r9d, 2097136 xor r15, r14 movdqa xmm6, xmm5 @@ -181,6 +185,9 @@ CryptonightR_template_end: ALIGN(64) CryptonightR_template_double_part1: + mov rdx, [rcx+8] + mov rcx, [rcx] + mov QWORD PTR [rsp+24], rbx push rbp push rsi diff --git a/src/crypto/asm/win/CryptonightWOW_soft_aes_template_win.inc b/src/crypto/cn/asm/win64/CryptonightWOW_soft_aes_template_win.inc similarity index 99% rename from src/crypto/asm/win/CryptonightWOW_soft_aes_template_win.inc rename to src/crypto/cn/asm/win64/CryptonightWOW_soft_aes_template_win.inc index 68209036..1c73f77c 100644 --- a/src/crypto/asm/win/CryptonightWOW_soft_aes_template_win.inc +++ b/src/crypto/cn/asm/win64/CryptonightWOW_soft_aes_template_win.inc @@ -6,6 +6,8 @@ PUBLIC CryptonightWOW_soft_aes_template_end ALIGN(64) CryptonightWOW_soft_aes_template_part1: + mov rcx, [rcx] + mov QWORD PTR [rsp+8], rcx push rbx push rbp diff --git a/src/crypto/asm/win/CryptonightWOW_template_win.inc b/src/crypto/cn/asm/win64/CryptonightWOW_template_win.inc similarity index 99% rename from src/crypto/asm/win/CryptonightWOW_template_win.inc rename to src/crypto/cn/asm/win64/CryptonightWOW_template_win.inc index 9db2cf39..55c8c8df 100644 --- a/src/crypto/asm/win/CryptonightWOW_template_win.inc +++ b/src/crypto/cn/asm/win64/CryptonightWOW_template_win.inc @@ -12,6 +12,8 @@ PUBLIC CryptonightWOW_template_double_end ALIGN(64) CryptonightWOW_template_part1: + mov rcx, [rcx] + mov QWORD PTR [rsp+16], rbx mov QWORD PTR [rsp+24], rbp mov QWORD PTR [rsp+32], rsi @@ -165,6 +167,9 @@ CryptonightWOW_template_end: ALIGN(64) CryptonightWOW_template_double_part1: + mov rdx, [rcx+8] + mov rcx, [rcx] + mov QWORD PTR [rsp+24], rbx push rbp push rsi diff --git a/src/crypto/asm/cnv2_double_main_loop_sandybridge.inc.in b/src/crypto/cn/asm/win64/cn2/cnv2_double_main_loop_sandybridge.inc similarity index 79% rename from src/crypto/asm/cnv2_double_main_loop_sandybridge.inc.in rename to src/crypto/cn/asm/win64/cn2/cnv2_double_main_loop_sandybridge.inc index d3a328cf..85077a20 100644 --- a/src/crypto/asm/cnv2_double_main_loop_sandybridge.inc.in +++ b/src/crypto/cn/asm/win64/cn2/cnv2_double_main_loop_sandybridge.inc @@ -1,3 +1,6 @@ + mov rdx, [rcx+8] + mov rcx, [rcx] + mov rax, rsp push rbx push rbp @@ -18,7 +21,7 @@ mov r10, QWORD PTR [rcx+32] mov r8, rcx xor r10, QWORD PTR [rcx] - mov r14d, ${ITERATIONS} + mov r14d, 524288 mov r11, QWORD PTR [rcx+40] xor r11, QWORD PTR [rcx+8] mov rsi, QWORD PTR [rdx+224] @@ -28,7 +31,7 @@ xor rdi, QWORD PTR [r9] mov rbp, QWORD PTR [r9+40] xor rbp, QWORD PTR [r9+8] - movq xmm0, rdx + movd xmm0, rdx movaps XMMWORD PTR [rax-88], xmm6 movaps XMMWORD PTR [rax-104], xmm7 movaps XMMWORD PTR [rax-120], xmm8 @@ -40,66 +43,62 @@ movaps XMMWORD PTR [rsp+32], xmm14 movaps XMMWORD PTR [rsp+16], xmm15 mov rdx, r10 - movq xmm4, QWORD PTR [r8+96] - and edx, ${MASK} + movd xmm4, QWORD PTR [r8+96] + and edx, 2097136 mov rax, QWORD PTR [rcx+48] xorps xmm13, xmm13 xor rax, QWORD PTR [rcx+16] mov rcx, QWORD PTR [rcx+88] xor rcx, QWORD PTR [r8+72] - movq xmm5, QWORD PTR [r8+104] - movq xmm7, rax + movd xmm5, QWORD PTR [r8+104] + movd xmm7, rax mov eax, 1 shl rax, 52 - movq xmm14, rax + movd xmm14, rax punpcklqdq xmm14, xmm14 mov eax, 1023 shl rax, 52 - movq xmm12, rax + movd xmm12, rax punpcklqdq xmm12, xmm12 mov rax, QWORD PTR [r8+80] xor rax, QWORD PTR [r8+64] punpcklqdq xmm7, xmm0 - movq xmm0, rcx + movd xmm0, rcx mov rcx, QWORD PTR [r9+56] xor rcx, QWORD PTR [r9+24] - movq xmm3, rax + movd xmm3, rax mov rax, QWORD PTR [r9+48] xor rax, QWORD PTR [r9+16] punpcklqdq xmm3, xmm0 - movq xmm0, rcx + movd xmm0, rcx mov QWORD PTR [rsp], r13 mov rcx, QWORD PTR [r9+88] xor rcx, QWORD PTR [r9+72] - movq xmm6, rax + movd xmm6, rax mov rax, QWORD PTR [r9+80] xor rax, QWORD PTR [r9+64] punpcklqdq xmm6, xmm0 - movq xmm0, rcx + movd xmm0, rcx mov QWORD PTR [rsp+256], r10 mov rcx, rdi mov QWORD PTR [rsp+264], r11 - movq xmm8, rax - and ecx, ${MASK} + movd xmm8, rax + and ecx, 2097136 punpcklqdq xmm8, xmm0 - movq xmm0, QWORD PTR [r9+96] + movd xmm0, QWORD PTR [r9+96] punpcklqdq xmm4, xmm0 - movq xmm0, QWORD PTR [r9+104] + movd xmm0, QWORD PTR [r9+104] lea r8, QWORD PTR [rcx+rsi] movdqu xmm11, XMMWORD PTR [r8] punpcklqdq xmm5, xmm0 lea r9, QWORD PTR [rdx+r13] movdqu xmm15, XMMWORD PTR [r9] - #ifdef __APPLE__ - ALIGN 16 - #else - ALIGN 64 - #endif -cnv2_double_main_loop_${ALGO}_sandybridge: + ALIGN(64) +main_loop_double_sandybridge: movdqu xmm9, xmm15 mov eax, edx mov ebx, edx @@ -107,8 +106,8 @@ cnv2_double_main_loop_${ALGO}_sandybridge: xor ebx, 32 xor edx, 48 - movq xmm0, r11 - movq xmm2, r10 + movd xmm0, r11 + movd xmm2, r10 punpcklqdq xmm2, xmm0 aesenc xmm9, xmm2 @@ -122,9 +121,9 @@ cnv2_double_main_loop_${ALGO}_sandybridge: paddq xmm0, xmm3 movdqu XMMWORD PTR [rax+r13], xmm0 - movq r11, xmm9 + movd r11, xmm9 mov edx, r11d - and edx, ${MASK} + and edx, 2097136 movdqa xmm0, xmm9 pxor xmm0, xmm7 movdqu XMMWORD PTR [r9], xmm0 @@ -133,8 +132,8 @@ cnv2_double_main_loop_${ALGO}_sandybridge: mov r10, QWORD PTR [rdx+r13] movdqu xmm10, xmm11 - movq xmm0, rbp - movq xmm11, rdi + movd xmm0, rbp + movd xmm11, rdi punpcklqdq xmm11, xmm0 aesenc xmm10, xmm11 @@ -154,8 +153,8 @@ cnv2_double_main_loop_${ALGO}_sandybridge: paddq xmm0, xmm8 movdqu XMMWORD PTR [rax+rsi], xmm0 - movq rcx, xmm10 - and ecx, ${MASK} + movd rcx, xmm10 + and ecx, 2097136 movdqa xmm0, xmm10 pxor xmm0, xmm6 @@ -168,18 +167,18 @@ cnv2_double_main_loop_${ALGO}_sandybridge: mov r8d, edx mov r15d, edx - movq rdx, xmm5 + movd rdx, xmm5 shl rdx, 32 - movq rax, xmm4 + movd rax, xmm4 xor rdx, rax xor r10, rdx mov rax, r10 mul r11 mov r11d, r8d xor r11d, 48 - movq xmm0, rdx + movd xmm0, rdx xor rdx, [r11+r13] - movq xmm1, rax + movd xmm1, rax xor rax, [r11+r13+8] punpcklqdq xmm0, xmm1 @@ -203,7 +202,7 @@ cnv2_double_main_loop_${ALGO}_sandybridge: mov QWORD PTR [rbx+8], rdx xor rdx, r9 mov QWORD PTR [rsp+256], r11 - and r11d, ${MASK} + and r11d, 2097136 mov QWORD PTR [rsp+264], rdx mov QWORD PTR [rsp+8], r11 lea r15, QWORD PTR [r11+r13] @@ -212,18 +211,18 @@ cnv2_double_main_loop_${ALGO}_sandybridge: movdqa xmm0, xmm5 psrldq xmm0, 8 movaps xmm2, xmm13 - movq r10, xmm0 + movd r10, xmm0 psllq xmm5, 1 shl r10, 32 movdqa xmm0, xmm9 psrldq xmm0, 8 movdqa xmm1, xmm10 - movq r11, xmm0 + movd r11, xmm0 psrldq xmm1, 8 - movq r8, xmm1 + movd r8, xmm1 psrldq xmm4, 8 movaps xmm0, xmm13 - movq rax, xmm4 + movd rax, xmm4 xor r10, rax movaps xmm1, xmm13 xor r10, r12 @@ -232,13 +231,13 @@ cnv2_double_main_loop_${ALGO}_sandybridge: movdqa xmm3, xmm9 punpcklqdq xmm3, xmm10 paddq xmm5, xmm3 - movq rdx, xmm5 + movd rdx, xmm5 psrldq xmm5, 8 cvtsi2sd xmm2, rax or edx, -2147483647 lea rax, QWORD PTR [r8+1] shr rax, 1 - movq r9, xmm5 + movd r9, xmm5 cvtsi2sd xmm0, rax or r9d, -2147483647 cvtsi2sd xmm1, rdx @@ -253,8 +252,8 @@ cnv2_double_main_loop_${ALGO}_sandybridge: mov rbx, rax imul rax, rdx sub r11, rax - js div_fix_1_${ALGO}_sandybridge -div_fix_1_ret_${ALGO}_sandybridge: + js div_fix_1_sandybridge +div_fix_1_ret_sandybridge: cvttsd2si rdx, xmm2 mov rax, rdx @@ -262,8 +261,8 @@ div_fix_1_ret_${ALGO}_sandybridge: movd xmm2, r11d movd xmm4, ebx sub r8, rax - js div_fix_2_${ALGO}_sandybridge -div_fix_2_ret_${ALGO}_sandybridge: + js div_fix_2_sandybridge +div_fix_2_ret_sandybridge: movd xmm1, r8d movd xmm0, edx @@ -275,19 +274,19 @@ div_fix_2_ret_${ALGO}_sandybridge: psrlq xmm0, 12 paddq xmm0, xmm12 sqrtpd xmm1, xmm0 - movq r9, xmm1 + movd r9, xmm1 movdqa xmm5, xmm1 psrlq xmm5, 19 test r9, 524287 - je sqrt_fix_1_${ALGO}_sandybridge -sqrt_fix_1_ret_${ALGO}_sandybridge: + je sqrt_fix_1_sandybridge +sqrt_fix_1_ret_sandybridge: - movq r9, xmm10 + movd r9, xmm10 psrldq xmm1, 8 - movq r8, xmm1 + movd r8, xmm1 test r8, 524287 - je sqrt_fix_2_${ALGO}_sandybridge -sqrt_fix_2_ret_${ALGO}_sandybridge: + je sqrt_fix_2_sandybridge +sqrt_fix_2_ret_sandybridge: mov r12d, ecx mov r8d, ecx @@ -296,8 +295,8 @@ sqrt_fix_2_ret_${ALGO}_sandybridge: xor ecx, 48 mov rax, r10 mul r9 - movq xmm0, rax - movq xmm3, rdx + movd xmm0, rax + movd xmm3, rdx punpcklqdq xmm3, xmm0 movdqu xmm0, XMMWORD PTR [r12+rsi] @@ -317,10 +316,10 @@ sqrt_fix_2_ret_${ALGO}_sandybridge: mov QWORD PTR [r13], rdi xor rdi, r10 mov ecx, edi - and ecx, ${MASK} + and ecx, 2097136 lea r8, QWORD PTR [rcx+rsi] - mov rdx, QWORD PTR [r13+8] + mov rdx, QWORD PTR [r13+8] add rbp, rax mov QWORD PTR [r13+8], rbp movdqu xmm11, XMMWORD PTR [rcx+rsi] @@ -335,7 +334,7 @@ sqrt_fix_2_ret_${ALGO}_sandybridge: movdqa xmm6, xmm10 mov r9, r15 dec r14d - jne cnv2_double_main_loop_${ALGO}_sandybridge + jne main_loop_double_sandybridge ldmxcsr DWORD PTR [rsp+272] movaps xmm13, XMMWORD PTR [rsp+48] @@ -358,20 +357,20 @@ sqrt_fix_2_ret_${ALGO}_sandybridge: pop rsi pop rbp pop rbx - jmp cnv2_double_main_loop_${ALGO}_sandybridge_endp + jmp cnv2_double_mainloop_asm_sandybridge_endp -div_fix_1_${ALGO}_sandybridge: +div_fix_1_sandybridge: dec rbx add r11, rdx - jmp div_fix_1_ret_${ALGO}_sandybridge + jmp div_fix_1_ret_sandybridge -div_fix_2_${ALGO}_sandybridge: +div_fix_2_sandybridge: dec rdx add r8, r9 - jmp div_fix_2_ret_${ALGO}_sandybridge + jmp div_fix_2_ret_sandybridge -sqrt_fix_1_${ALGO}_sandybridge: - movq r8, xmm3 +sqrt_fix_1_sandybridge: + movd r8, xmm3 movdqa xmm0, xmm5 psrldq xmm0, 8 dec r9 @@ -387,13 +386,13 @@ sqrt_fix_1_${ALGO}_sandybridge: imul rdx, rax sub rdx, r8 adc r9, 0 - movq xmm5, r9 + movd xmm5, r9 punpcklqdq xmm5, xmm0 - jmp sqrt_fix_1_ret_${ALGO}_sandybridge + jmp sqrt_fix_1_ret_sandybridge -sqrt_fix_2_${ALGO}_sandybridge: +sqrt_fix_2_sandybridge: psrldq xmm3, 8 - movq r11, xmm3 + movd r11, xmm3 dec r8 mov ebx, -1022 shl rbx, 32 @@ -407,8 +406,8 @@ sqrt_fix_2_${ALGO}_sandybridge: imul rdx, rax sub rdx, r11 adc r8, 0 - movq xmm0, r8 + movd xmm0, r8 punpcklqdq xmm5, xmm0 - jmp sqrt_fix_2_ret_${ALGO}_sandybridge + jmp sqrt_fix_2_ret_sandybridge -cnv2_double_main_loop_${ALGO}_sandybridge_endp: +cnv2_double_mainloop_asm_sandybridge_endp: diff --git a/src/crypto/asm/win/cnv2_main_loop_bulldozer.inc.in b/src/crypto/cn/asm/win64/cn2/cnv2_main_loop_bulldozer.inc similarity index 89% rename from src/crypto/asm/win/cnv2_main_loop_bulldozer.inc.in rename to src/crypto/cn/asm/win64/cn2/cnv2_main_loop_bulldozer.inc index 163cc0e5..f17017a0 100644 --- a/src/crypto/asm/win/cnv2_main_loop_bulldozer.inc.in +++ b/src/crypto/cn/asm/win64/cn2/cnv2_main_loop_bulldozer.inc @@ -1,3 +1,5 @@ + mov rcx, [rcx] + mov QWORD PTR [rsp+16], rbx mov QWORD PTR [rsp+24], rbp mov QWORD PTR [rsp+32], rsi @@ -15,7 +17,7 @@ mov rax, QWORD PTR [rcx+48] mov r9, rcx xor rax, QWORD PTR [rcx+16] - mov ebp, ${ITERATIONS} + mov ebp, 524288 mov r8, QWORD PTR [rcx+32] xor r8, QWORD PTR [rcx] mov r11, QWORD PTR [rcx+40] @@ -31,7 +33,7 @@ mov rcx, QWORD PTR [rcx+88] xor rcx, QWORD PTR [r9+72] mov rdi, QWORD PTR [r9+104] - and r10d, ${MASK} + and r10d, 2097136 movaps XMMWORD PTR [rsp+48], xmm6 movd xmm4, rax movaps XMMWORD PTR [rsp+32], xmm7 @@ -45,8 +47,8 @@ movd xmm0, rcx punpcklqdq xmm4, xmm0 - ALIGN 16 -cnv2_main_loop_${ALGO}_bulldozer: + ALIGN(64) +cnv2_main_loop_bulldozer: movdqa xmm5, XMMWORD PTR [r10+rbx] movd xmm6, r8 pinsrq xmm6, r11, 1 @@ -83,7 +85,7 @@ cnv2_main_loop_${ALGO}_bulldozer: movdqa xmm0, xmm5 pxor xmm0, xmm3 mov r10, r14 - and r10d, ${MASK} + and r10d, 2097136 movdqa XMMWORD PTR [rdx], xmm0 xor rsi, QWORD PTR [r10+rbx] lea r12, QWORD PTR [r10+rbx] @@ -103,10 +105,10 @@ cnv2_main_loop_${ALGO}_bulldozer: sqrtsd xmm1, xmm0 movd rdi, xmm1 test rdi, 524287 - je sqrt_fixup_${ALGO}_bulldozer + je sqrt_fixup_bulldozer shr rdi, 19 -sqrt_fixup_${ALGO}_bulldozer_ret: +sqrt_fixup_bulldozer_ret: mov rax, rsi mul r14 movd xmm1, rax @@ -138,10 +140,10 @@ sqrt_fixup_${ALGO}_bulldozer_ret: mov QWORD PTR [r12+8], r11 mov r10, r8 xor r11, r13 - and r10d, ${MASK} + and r10d, 2097136 movdqa xmm3, xmm5 dec ebp - jne cnv2_main_loop_${ALGO}_bulldozer + jne cnv2_main_loop_bulldozer ldmxcsr DWORD PTR [rsp] movaps xmm6, XMMWORD PTR [rsp+48] @@ -157,9 +159,9 @@ sqrt_fixup_${ALGO}_bulldozer_ret: pop r13 pop r12 pop rdi - jmp cnv2_main_loop_${ALGO}_bulldozer_endp + jmp cnv2_main_loop_bulldozer_endp -sqrt_fixup_${ALGO}_bulldozer: +sqrt_fixup_bulldozer: movd r9, xmm5 add r9, r15 dec rdi @@ -175,6 +177,6 @@ sqrt_fixup_${ALGO}_bulldozer: imul rcx, rax sub rcx, r9 adc rdi, 0 - jmp sqrt_fixup_${ALGO}_bulldozer_ret + jmp sqrt_fixup_bulldozer_ret -cnv2_main_loop_${ALGO}_bulldozer_endp: \ No newline at end of file +cnv2_main_loop_bulldozer_endp: diff --git a/src/crypto/asm/cnv2_main_loop_ivybridge.inc.in b/src/crypto/cn/asm/win64/cn2/cnv2_main_loop_ivybridge.inc similarity index 80% rename from src/crypto/asm/cnv2_main_loop_ivybridge.inc.in rename to src/crypto/cn/asm/win64/cn2/cnv2_main_loop_ivybridge.inc index 012bed12..a12ac35c 100644 --- a/src/crypto/asm/cnv2_main_loop_ivybridge.inc.in +++ b/src/crypto/cn/asm/win64/cn2/cnv2_main_loop_ivybridge.inc @@ -1,3 +1,5 @@ + mov rcx, [rcx] + mov QWORD PTR [rsp+24], rbx push rbp push rsi @@ -15,44 +17,43 @@ mov rax, QWORD PTR [rcx+48] mov r9, rcx xor rax, QWORD PTR [rcx+16] - mov esi, ${ITERATIONS} + mov esi, 524288 mov r8, QWORD PTR [rcx+32] mov r13d, -2147483647 xor r8, QWORD PTR [rcx] mov r11, QWORD PTR [rcx+40] mov r10, r8 mov rdx, QWORD PTR [rcx+56] - movq xmm4, rax + movd xmm4, rax xor rdx, QWORD PTR [rcx+24] xor r11, QWORD PTR [rcx+8] mov rbx, QWORD PTR [rcx+224] mov rax, QWORD PTR [r9+80] xor rax, QWORD PTR [r9+64] - movq xmm0, rdx + movd xmm0, rdx mov rcx, QWORD PTR [rcx+88] xor rcx, QWORD PTR [r9+72] - movq xmm3, QWORD PTR [r9+104] + movd xmm3, QWORD PTR [r9+104] movaps XMMWORD PTR [rsp+64], xmm6 movaps XMMWORD PTR [rsp+48], xmm7 movaps XMMWORD PTR [rsp+32], xmm8 - and r10d, ${MASK} - movq xmm5, rax + and r10d, 2097136 + movd xmm5, rax + + xor eax, eax + mov QWORD PTR [rsp+16], rax mov ax, 1023 shl rax, 52 - movq xmm8, rax + movd xmm8, rax mov r15, QWORD PTR [r9+96] punpcklqdq xmm4, xmm0 - movq xmm0, rcx + movd xmm0, rcx punpcklqdq xmm5, xmm0 movdqu xmm6, XMMWORD PTR [r10+rbx] - #ifdef __APPLE__ - ALIGN 16 - #else - ALIGN 64 - #endif -cnv2_main_loop_${ALGO}_ivybridge: + ALIGN(64) +main_loop_ivybridge: lea rdx, QWORD PTR [r10+rbx] mov ecx, r10d mov eax, r10d @@ -60,13 +61,13 @@ cnv2_main_loop_${ALGO}_ivybridge: xor ecx, 16 xor eax, 32 xor r10d, 48 - movq xmm0, r11 - movq xmm7, r8 + movd xmm0, r11 + movd xmm7, r8 punpcklqdq xmm7, xmm0 aesenc xmm6, xmm7 - movq rbp, xmm6 + movd rbp, xmm6 mov r9, rbp - and r9d, ${MASK} + and r9d, 2097136 movdqu xmm2, XMMWORD PTR [rcx+rbx] movdqu xmm1, XMMWORD PTR [rax+rbx] movdqu xmm0, XMMWORD PTR [r10+rbx] @@ -78,7 +79,7 @@ cnv2_main_loop_${ALGO}_ivybridge: movdqu XMMWORD PTR [r10+rbx], xmm1 mov r10, r9 xor r10d, 32 - movq rcx, xmm3 + movd rcx, xmm3 mov rax, rcx shl rax, 32 xor rdi, rax @@ -94,7 +95,7 @@ cnv2_main_loop_${ALGO}_ivybridge: movdqa xmm0, xmm6 psrldq xmm0, 8 or r9d, r13d - movq rax, xmm0 + movd rax, xmm0 div r9 xorps xmm3, xmm3 mov eax, eax @@ -104,26 +105,27 @@ cnv2_main_loop_${ALGO}_ivybridge: mov r15, rdx mov rax, r9 shr rax, 12 - movq xmm0, rax + movd xmm0, rax paddq xmm0, xmm8 sqrtsd xmm3, xmm0 - movq rdx, xmm3 + psubq xmm3, XMMWORD PTR [rsp+16] + movd rdx, xmm3 test edx, 524287 - je sqrt_fixup_${ALGO}_ivybridge + je sqrt_fixup_ivybridge psrlq xmm3, 19 -sqrt_fixup_${ALGO}_ivybridge_ret: +sqrt_fixup_ivybridge_ret: mov ecx, r10d mov rax, rdi mul rbp - movq xmm2, rdx + movd xmm2, rdx xor rdx, [rcx+rbx] add r8, rdx mov QWORD PTR [r14], r8 xor r8, rdi mov edi, r8d - and edi, ${MASK} - movq xmm0, rax + and edi, 2097136 + movd xmm0, rax xor rax, [rcx+rbx+8] add r11, rax mov QWORD PTR [r14+8], r11 @@ -147,7 +149,7 @@ sqrt_fixup_${ALGO}_ivybridge_ret: mov r10d, edi xor r11, r12 dec rsi - jne cnv2_main_loop_${ALGO}_ivybridge + jne main_loop_ivybridge ldmxcsr DWORD PTR [rsp] mov rbx, QWORD PTR [rsp+160] @@ -162,9 +164,9 @@ sqrt_fixup_${ALGO}_ivybridge_ret: pop rdi pop rsi pop rbp - jmp cnv2_main_loop_${ALGO}_ivybridge_endp + jmp cnv2_main_loop_ivybridge_endp -sqrt_fixup_${ALGO}_ivybridge: +sqrt_fixup_ivybridge: dec rdx mov r13d, -1022 shl r13, 32 @@ -180,7 +182,7 @@ sqrt_fixup_${ALGO}_ivybridge: imul rcx, rax sub rcx, r9 adc rdx, 0 - movq xmm3, rdx - jmp sqrt_fixup_${ALGO}_ivybridge_ret + movd xmm3, rdx + jmp sqrt_fixup_ivybridge_ret -cnv2_main_loop_${ALGO}_ivybridge_endp: +cnv2_main_loop_ivybridge_endp: diff --git a/src/crypto/asm/cnv2_main_loop_ryzen.inc.in b/src/crypto/cn/asm/win64/cn2/cnv2_main_loop_ryzen.inc similarity index 81% rename from src/crypto/asm/cnv2_main_loop_ryzen.inc.in rename to src/crypto/cn/asm/win64/cn2/cnv2_main_loop_ryzen.inc index 6d71264f..044235d8 100644 --- a/src/crypto/asm/cnv2_main_loop_ryzen.inc.in +++ b/src/crypto/cn/asm/win64/cn2/cnv2_main_loop_ryzen.inc @@ -1,3 +1,5 @@ + mov rcx, [rcx] + mov QWORD PTR [rsp+16], rbx mov QWORD PTR [rsp+24], rbp mov QWORD PTR [rsp+32], rsi @@ -15,45 +17,41 @@ mov rax, QWORD PTR [rcx+48] mov r9, rcx xor rax, QWORD PTR [rcx+16] - mov ebp, ${ITERATIONS} + mov ebp, 524288 mov r8, QWORD PTR [rcx+32] xor r8, QWORD PTR [rcx] mov r11, QWORD PTR [rcx+40] mov r10, r8 mov rdx, QWORD PTR [rcx+56] - movq xmm3, rax + movd xmm3, rax xor rdx, QWORD PTR [rcx+24] xor r11, QWORD PTR [rcx+8] mov rbx, QWORD PTR [rcx+224] mov rax, QWORD PTR [r9+80] xor rax, QWORD PTR [r9+64] - movq xmm0, rdx + movd xmm0, rdx mov rcx, QWORD PTR [rcx+88] xor rcx, QWORD PTR [r9+72] mov rdi, QWORD PTR [r9+104] - and r10d, ${MASK} + and r10d, 2097136 movaps XMMWORD PTR [rsp+48], xmm6 - movq xmm4, rax + movd xmm4, rax movaps XMMWORD PTR [rsp+32], xmm7 movaps XMMWORD PTR [rsp+16], xmm8 xorps xmm8, xmm8 mov ax, 1023 shl rax, 52 - movq xmm7, rax + movd xmm7, rax mov r15, QWORD PTR [r9+96] punpcklqdq xmm3, xmm0 - movq xmm0, rcx + movd xmm0, rcx punpcklqdq xmm4, xmm0 - #ifdef __APPLE__ - ALIGN 16 - #else - ALIGN 64 - #endif -cnv2_main_loop_${ALGO}_ryzen: + ALIGN(64) +main_loop_ryzen: movdqa xmm5, XMMWORD PTR [r10+rbx] - movq xmm0, r11 - movq xmm6, r8 + movd xmm0, r11 + movd xmm6, r8 punpcklqdq xmm6, xmm0 lea rdx, QWORD PTR [r10+rbx] lea r9, QWORD PTR [rdi+rdi] @@ -78,11 +76,11 @@ cnv2_main_loop_${ALGO}_ryzen: movaps xmm1, xmm8 mov rsi, r15 xor rsi, rdi - movq r14, xmm5 + movd r14, xmm5 movdqa xmm0, xmm5 pxor xmm0, xmm3 mov r10, r14 - and r10d, ${MASK} + and r10d, 2097136 movdqa XMMWORD PTR [rdx], xmm0 xor rsi, QWORD PTR [r10+rbx] lea r12, QWORD PTR [r10+rbx] @@ -93,28 +91,28 @@ cnv2_main_loop_${ALGO}_ryzen: xor edx, edx movdqa xmm0, xmm5 psrldq xmm0, 8 - movq rax, xmm0 + movd rax, xmm0 div r9 - movq xmm0, rax - movq xmm1, rdx + movd xmm0, rax + movd xmm1, rdx punpckldq xmm0, xmm1 - movq r15, xmm0 + movd r15, xmm0 paddq xmm0, xmm5 movdqa xmm2, xmm0 psrlq xmm0, 12 paddq xmm0, xmm7 sqrtsd xmm1, xmm0 - movq rdi, xmm1 + movd rdi, xmm1 test rdi, 524287 - je sqrt_fixup_${ALGO}_ryzen + je sqrt_fixup_ryzen shr rdi, 19 -sqrt_fixup_${ALGO}_ryzen_ret: +sqrt_fixup_ryzen_ret: mov rax, rsi mul r14 - movq xmm1, rax - movq xmm0, rdx + movd xmm1, rax + movd xmm0, rdx punpcklqdq xmm0, xmm1 mov r9d, r10d @@ -142,10 +140,10 @@ sqrt_fixup_${ALGO}_ryzen_ret: mov QWORD PTR [r12+8], r11 mov r10, r8 xor r11, r13 - and r10d, ${MASK} + and r10d, 2097136 movdqa xmm3, xmm5 dec ebp - jne cnv2_main_loop_${ALGO}_ryzen + jne main_loop_ryzen ldmxcsr DWORD PTR [rsp] movaps xmm6, XMMWORD PTR [rsp+48] @@ -161,10 +159,10 @@ sqrt_fixup_${ALGO}_ryzen_ret: pop r13 pop r12 pop rdi - jmp cnv2_main_loop_${ALGO}_ryzen_endp + jmp cnv2_main_loop_ryzen_endp -sqrt_fixup_${ALGO}_ryzen: - movq r9, xmm2 +sqrt_fixup_ryzen: + movd r9, xmm2 dec rdi mov edx, -1022 shl rdx, 32 @@ -178,6 +176,6 @@ sqrt_fixup_${ALGO}_ryzen: imul rcx, rax sub rcx, r9 adc rdi, 0 - jmp sqrt_fixup_${ALGO}_ryzen_ret + jmp sqrt_fixup_ryzen_ret -cnv2_main_loop_${ALGO}_ryzen_endp: +cnv2_main_loop_ryzen_endp: diff --git a/src/crypto/asm/win/cnv2_double_main_loop_rwz_all.inc.in b/src/crypto/cn/asm/win64/cn2/cnv2_rwz_double_main_loop.inc similarity index 91% rename from src/crypto/asm/win/cnv2_double_main_loop_rwz_all.inc.in rename to src/crypto/cn/asm/win64/cn2/cnv2_rwz_double_main_loop.inc index 4f08527a..97fb691b 100644 --- a/src/crypto/asm/win/cnv2_double_main_loop_rwz_all.inc.in +++ b/src/crypto/cn/asm/win64/cn2/cnv2_rwz_double_main_loop.inc @@ -1,3 +1,6 @@ + mov rdx, [rcx+8] + mov rcx, [rcx] + mov rax, rsp push rbx push rbp @@ -18,7 +21,7 @@ mov r10, QWORD PTR [rcx+32] mov r8, rcx xor r10, QWORD PTR [rcx] - mov r14d, ${ITERATIONS} + mov r14d, 393216 mov r11, QWORD PTR [rcx+40] xor r11, QWORD PTR [rcx+8] mov rsi, QWORD PTR [rdx+224] @@ -41,7 +44,7 @@ movaps XMMWORD PTR [rsp+16], xmm15 mov rdx, r10 movd xmm4, QWORD PTR [r8+96] - and edx, ${MASK} + and edx, 2097136 mov rax, QWORD PTR [rcx+48] xorps xmm13, xmm13 xor rax, QWORD PTR [rcx+16] @@ -83,7 +86,7 @@ mov rcx, rdi mov QWORD PTR [rsp+264], r11 movd xmm8, rax - and ecx, ${MASK} + and ecx, 2097136 punpcklqdq xmm8, xmm0 movd xmm0, QWORD PTR [r9+96] punpcklqdq xmm4, xmm0 @@ -95,7 +98,7 @@ movdqu xmm15, XMMWORD PTR [r9] ALIGN(64) -rwz_main_loop_double_${ALGO}: +rwz_main_loop_double: movdqu xmm9, xmm15 mov eax, edx mov ebx, edx @@ -120,7 +123,7 @@ rwz_main_loop_double_${ALGO}: movd r11, xmm9 mov edx, r11d - and edx, ${MASK} + and edx, 2097136 movdqa xmm0, xmm9 pxor xmm0, xmm7 movdqu XMMWORD PTR [r9], xmm0 @@ -151,7 +154,7 @@ rwz_main_loop_double_${ALGO}: movdqu XMMWORD PTR [rax+rsi], xmm0 movd rcx, xmm10 - and ecx, ${MASK} + and ecx, 2097136 movdqa xmm0, xmm10 pxor xmm0, xmm6 @@ -199,7 +202,7 @@ rwz_main_loop_double_${ALGO}: mov QWORD PTR [rbx+8], rdx xor rdx, r9 mov QWORD PTR [rsp+256], r11 - and r11d, ${MASK} + and r11d, 2097136 mov QWORD PTR [rsp+264], rdx mov QWORD PTR [rsp+8], r11 lea r15, QWORD PTR [r11+r13] @@ -249,8 +252,8 @@ rwz_main_loop_double_${ALGO}: mov rbx, rax imul rax, rdx sub r11, rax - js rwz_div_fix_1_${ALGO} -rwz_div_fix_1_${ALGO}_ret: + js rwz_div_fix_1 +rwz_div_fix_1_ret: cvttsd2si rdx, xmm2 mov rax, rdx @@ -258,8 +261,8 @@ rwz_div_fix_1_${ALGO}_ret: movd xmm2, r11d movd xmm4, ebx sub r8, rax - js rwz_div_fix_2_${ALGO} -rwz_div_fix_2_${ALGO}_ret: + js rwz_div_fix_2 +rwz_div_fix_2_ret: movd xmm1, r8d movd xmm0, edx @@ -275,15 +278,15 @@ rwz_div_fix_2_${ALGO}_ret: movdqa xmm5, xmm1 psrlq xmm5, 19 test r9, 524287 - je rwz_sqrt_fix_1_${ALGO} -rwz_sqrt_fix_1_${ALGO}_ret: + je rwz_sqrt_fix_1 +rwz_sqrt_fix_1_ret: movd r9, xmm10 psrldq xmm1, 8 movd r8, xmm1 test r8, 524287 - je rwz_sqrt_fix_2_${ALGO} -rwz_sqrt_fix_2_${ALGO}_ret: + je rwz_sqrt_fix_2 +rwz_sqrt_fix_2_ret: mov r12d, ecx mov r8d, ecx @@ -313,7 +316,7 @@ rwz_sqrt_fix_2_${ALGO}_ret: mov QWORD PTR [r13], rdi xor rdi, r10 mov ecx, edi - and ecx, ${MASK} + and ecx, 2097136 lea r8, QWORD PTR [rcx+rsi] mov rdx, QWORD PTR [r13+8] @@ -331,7 +334,7 @@ rwz_sqrt_fix_2_${ALGO}_ret: movdqa xmm6, xmm10 mov r9, r15 dec r14d - jne rwz_main_loop_double_${ALGO} + jne rwz_main_loop_double ldmxcsr DWORD PTR [rsp+272] movaps xmm13, XMMWORD PTR [rsp+48] @@ -354,19 +357,19 @@ rwz_sqrt_fix_2_${ALGO}_ret: pop rsi pop rbp pop rbx - jmp rwz_cnv2_double_mainloop_${ALGO}_asm_endp + jmp rwz_cnv2_double_mainloop_asm_endp -rwz_div_fix_1_${ALGO}: +rwz_div_fix_1: dec rbx add r11, rdx - jmp rwz_div_fix_1_${ALGO}_ret + jmp rwz_div_fix_1_ret -rwz_div_fix_2_${ALGO}: +rwz_div_fix_2: dec rdx add r8, r9 - jmp rwz_div_fix_2_${ALGO}_ret + jmp rwz_div_fix_2_ret -rwz_sqrt_fix_1_${ALGO}: +rwz_sqrt_fix_1: movd r8, xmm3 movdqa xmm0, xmm5 psrldq xmm0, 8 @@ -385,9 +388,9 @@ rwz_sqrt_fix_1_${ALGO}: adc r9, 0 movd xmm5, r9 punpcklqdq xmm5, xmm0 - jmp rwz_sqrt_fix_1_${ALGO}_ret + jmp rwz_sqrt_fix_1_ret -rwz_sqrt_fix_2_${ALGO}: +rwz_sqrt_fix_2: psrldq xmm3, 8 movd r11, xmm3 dec r8 @@ -405,6 +408,6 @@ rwz_sqrt_fix_2_${ALGO}: adc r8, 0 movd xmm0, r8 punpcklqdq xmm5, xmm0 - jmp rwz_sqrt_fix_2_${ALGO}_ret + jmp rwz_sqrt_fix_2_ret -rwz_cnv2_double_mainloop_${ALGO}_asm_endp: +rwz_cnv2_double_mainloop_asm_endp: diff --git a/src/crypto/asm/win/cnv2_main_loop_rwz_all.inc.in b/src/crypto/cn/asm/win64/cn2/cnv2_rwz_main_loop.inc similarity index 91% rename from src/crypto/asm/win/cnv2_main_loop_rwz_all.inc.in rename to src/crypto/cn/asm/win64/cn2/cnv2_rwz_main_loop.inc index b3d7a6c7..e2b7a5fc 100644 --- a/src/crypto/asm/win/cnv2_main_loop_rwz_all.inc.in +++ b/src/crypto/cn/asm/win64/cn2/cnv2_rwz_main_loop.inc @@ -1,3 +1,5 @@ + mov rcx, [rcx] + mov QWORD PTR [rsp+24], rbx push rbp push rsi @@ -15,7 +17,7 @@ mov rax, QWORD PTR [rcx+48] mov r9, rcx xor rax, QWORD PTR [rcx+16] - mov esi, ${ITERATIONS} + mov esi, 393216 mov r8, QWORD PTR [rcx+32] mov r13d, -2147483647 xor r8, QWORD PTR [rcx] @@ -35,7 +37,7 @@ movaps XMMWORD PTR [rsp+64], xmm6 movaps XMMWORD PTR [rsp+48], xmm7 movaps XMMWORD PTR [rsp+32], xmm8 - and r10d, ${MASK} + and r10d, 2097136 movd xmm5, rax xor eax, eax @@ -51,7 +53,7 @@ movdqu xmm6, XMMWORD PTR [r10+rbx] ALIGN(64) -rwz_main_loop_${ALGO}: +rwz_main_loop: lea rdx, QWORD PTR [r10+rbx] mov ecx, r10d mov eax, r10d @@ -65,7 +67,7 @@ rwz_main_loop_${ALGO}: aesenc xmm6, xmm7 movd rbp, xmm6 mov r9, rbp - and r9d, ${MASK} + and r9d, 2097136 movdqu xmm0, XMMWORD PTR [rcx+rbx] movdqu xmm1, XMMWORD PTR [rax+rbx] movdqu xmm2, XMMWORD PTR [r10+rbx] @@ -109,9 +111,9 @@ rwz_main_loop_${ALGO}: psubq xmm3, XMMWORD PTR [rsp+16] movd rdx, xmm3 test edx, 524287 - je rwz_sqrt_fixup_${ALGO} + je rwz_sqrt_fixup psrlq xmm3, 19 -rwz_sqrt_fixup_${ALGO}_ret: +rwz_sqrt_fixup_ret: mov ecx, r10d mov rax, rdi @@ -122,7 +124,7 @@ rwz_sqrt_fixup_${ALGO}_ret: mov QWORD PTR [r14], r8 xor r8, rdi mov edi, r8d - and edi, ${MASK} + and edi, 2097136 movd xmm0, rax xor rax, [rcx+rbx+8] add r11, rax @@ -147,7 +149,7 @@ rwz_sqrt_fixup_${ALGO}_ret: mov r10d, edi xor r11, r12 dec rsi - jne rwz_main_loop_${ALGO} + jne rwz_main_loop ldmxcsr DWORD PTR [rsp] mov rbx, QWORD PTR [rsp+160] @@ -162,9 +164,9 @@ rwz_sqrt_fixup_${ALGO}_ret: pop rdi pop rsi pop rbp - jmp cnv2_rwz_main_loop_${ALGO}_endp + jmp cnv2_rwz_main_loop_endp -rwz_sqrt_fixup_${ALGO}: +rwz_sqrt_fixup: dec rdx mov r13d, -1022 shl r13, 32 @@ -181,6 +183,6 @@ rwz_sqrt_fixup_${ALGO}: sub rcx, r9 adc rdx, 0 movd xmm3, rdx - jmp rwz_sqrt_fixup_${ALGO}_ret + jmp rwz_sqrt_fixup_ret -cnv2_rwz_main_loop_${ALGO}_endp: +cnv2_rwz_main_loop_endp: diff --git a/src/crypto/cn/asm/win64/cn_main_loop.S b/src/crypto/cn/asm/win64/cn_main_loop.S new file mode 100644 index 00000000..63c3a8ba --- /dev/null +++ b/src/crypto/cn/asm/win64/cn_main_loop.S @@ -0,0 +1,45 @@ +#define ALIGN(x) .align 64 +.intel_syntax noprefix +.section .text +.global cnv2_mainloop_ivybridge_asm +.global cnv2_mainloop_ryzen_asm +.global cnv2_mainloop_bulldozer_asm +.global cnv2_double_mainloop_sandybridge_asm +.global cnv2_rwz_mainloop_asm +.global cnv2_rwz_double_mainloop_asm + +ALIGN(64) +cnv2_mainloop_ivybridge_asm: + #include "../cn2/cnv2_main_loop_ivybridge.inc" + ret 0 + mov eax, 3735929054 + +ALIGN(64) +cnv2_mainloop_ryzen_asm: + #include "../cn2/cnv2_main_loop_ryzen.inc" + ret 0 + mov eax, 3735929054 + +ALIGN(64) +cnv2_mainloop_bulldozer_asm: + #include "../cn2/cnv2_main_loop_bulldozer.inc" + ret 0 + mov eax, 3735929054 + +ALIGN(64) +cnv2_double_mainloop_sandybridge_asm: + #include "../cn2/cnv2_double_main_loop_sandybridge.inc" + ret 0 + mov eax, 3735929054 + +ALIGN(64) +cnv2_rwz_mainloop_asm: + #include "cn2/cnv2_rwz_main_loop.inc" + ret 0 + mov eax, 3735929054 + +ALIGN(64) +cnv2_rwz_double_mainloop_asm: + #include "cn2/cnv2_rwz_double_main_loop.inc" + ret 0 + mov eax, 3735929054 diff --git a/src/crypto/cn/asm/win64/cn_main_loop.asm b/src/crypto/cn/asm/win64/cn_main_loop.asm new file mode 100644 index 00000000..57246cf5 --- /dev/null +++ b/src/crypto/cn/asm/win64/cn_main_loop.asm @@ -0,0 +1,52 @@ +_TEXT_CNV2_MAINLOOP SEGMENT PAGE READ EXECUTE +PUBLIC cnv2_mainloop_ivybridge_asm +PUBLIC cnv2_mainloop_ryzen_asm +PUBLIC cnv2_mainloop_bulldozer_asm +PUBLIC cnv2_double_mainloop_sandybridge_asm +PUBLIC cnv2_rwz_mainloop_asm +PUBLIC cnv2_rwz_double_mainloop_asm + +ALIGN 64 +cnv2_mainloop_ivybridge_asm PROC + INCLUDE cn2/cnv2_main_loop_ivybridge.inc + ret 0 + mov eax, 3735929054 +cnv2_mainloop_ivybridge_asm ENDP + +ALIGN 64 +cnv2_mainloop_ryzen_asm PROC + INCLUDE cn2/cnv2_main_loop_ryzen.inc + ret 0 + mov eax, 3735929054 +cnv2_mainloop_ryzen_asm ENDP + +ALIGN 64 +cnv2_mainloop_bulldozer_asm PROC + INCLUDE cn2/cnv2_main_loop_bulldozer.inc + ret 0 + mov eax, 3735929054 +cnv2_mainloop_bulldozer_asm ENDP + +ALIGN 64 +cnv2_double_mainloop_sandybridge_asm PROC + INCLUDE cn2/cnv2_double_main_loop_sandybridge.inc + ret 0 + mov eax, 3735929054 +cnv2_double_mainloop_sandybridge_asm ENDP + +ALIGN(64) +cnv2_rwz_mainloop_asm PROC + INCLUDE cn2/cnv2_rwz_main_loop.inc + ret 0 + mov eax, 3735929054 +cnv2_rwz_mainloop_asm ENDP + +ALIGN(64) +cnv2_rwz_double_mainloop_asm PROC + INCLUDE cn2/cnv2_rwz_double_main_loop.inc + ret 0 + mov eax, 3735929054 +cnv2_rwz_double_mainloop_asm ENDP + +_TEXT_CNV2_MAINLOOP ENDS +END diff --git a/src/crypto/c_blake256.c b/src/crypto/cn/c_blake256.c similarity index 100% rename from src/crypto/c_blake256.c rename to src/crypto/cn/c_blake256.c diff --git a/src/crypto/c_blake256.h b/src/crypto/cn/c_blake256.h similarity index 100% rename from src/crypto/c_blake256.h rename to src/crypto/cn/c_blake256.h diff --git a/src/crypto/c_groestl.c b/src/crypto/cn/c_groestl.c similarity index 100% rename from src/crypto/c_groestl.c rename to src/crypto/cn/c_groestl.c diff --git a/src/crypto/c_groestl.h b/src/crypto/cn/c_groestl.h similarity index 100% rename from src/crypto/c_groestl.h rename to src/crypto/cn/c_groestl.h diff --git a/src/crypto/c_jh.c b/src/crypto/cn/c_jh.c similarity index 100% rename from src/crypto/c_jh.c rename to src/crypto/cn/c_jh.c diff --git a/src/crypto/c_jh.h b/src/crypto/cn/c_jh.h similarity index 100% rename from src/crypto/c_jh.h rename to src/crypto/cn/c_jh.h diff --git a/src/crypto/c_skein.c b/src/crypto/cn/c_skein.c similarity index 100% rename from src/crypto/c_skein.c rename to src/crypto/cn/c_skein.c diff --git a/src/crypto/c_skein.h b/src/crypto/cn/c_skein.h similarity index 100% rename from src/crypto/c_skein.h rename to src/crypto/cn/c_skein.h diff --git a/src/crypto/cn/gpu/cn_gpu_arm.cpp b/src/crypto/cn/gpu/cn_gpu_arm.cpp new file mode 100644 index 00000000..520d3fc8 --- /dev/null +++ b/src/crypto/cn/gpu/cn_gpu_arm.cpp @@ -0,0 +1,240 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2019 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig + * + * 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 . + */ + + +#include + + +#include "crypto/cn/CnAlgo.h" + + +inline void vandq_f32(float32x4_t &v, uint32_t v2) +{ + uint32x4_t vc = vdupq_n_u32(v2); + v = (float32x4_t)vandq_u32((uint32x4_t)v, vc); +} + + +inline void vorq_f32(float32x4_t &v, uint32_t v2) +{ + uint32x4_t vc = vdupq_n_u32(v2); + v = (float32x4_t)vorrq_u32((uint32x4_t)v, vc); +} + + +template +inline void vrot_si32(int32x4_t &r) +{ + r = (int32x4_t)vextq_s8((int8x16_t)r, (int8x16_t)r, v); +} + +template <> +inline void vrot_si32<0>(int32x4_t &r) +{ +} + + +inline uint32_t vheor_s32(const int32x4_t &v) +{ + int32x4_t v0 = veorq_s32(v, vrev64q_s32(v)); + int32x2_t vf = veor_s32(vget_high_s32(v0), vget_low_s32(v0)); + return (uint32_t)vget_lane_s32(vf, 0); +} + + +inline void prep_dv(int32_t *idx, int32x4_t &v, float32x4_t &n) +{ + v = vld1q_s32(idx); + n = vcvtq_f32_s32(v); +} + + +inline void sub_round(const float32x4_t &n0, const float32x4_t &n1, const float32x4_t &n2, const float32x4_t &n3, const float32x4_t &rnd_c, float32x4_t &n, float32x4_t &d, float32x4_t &c) +{ + float32x4_t ln1 = vaddq_f32(n1, c); + float32x4_t nn = vmulq_f32(n0, c); + nn = vmulq_f32(ln1, vmulq_f32(nn, nn)); + vandq_f32(nn, 0xFEFFFFFF); + vorq_f32(nn, 0x00800000); + n = vaddq_f32(n, nn); + + float32x4_t ln3 = vsubq_f32(n3, c); + float32x4_t dd = vmulq_f32(n2, c); + dd = vmulq_f32(ln3, vmulq_f32(dd, dd)); + vandq_f32(dd, 0xFEFFFFFF); + vorq_f32(dd, 0x00800000); + d = vaddq_f32(d, dd); + + //Constant feedback + c = vaddq_f32(c, rnd_c); + c = vaddq_f32(c, vdupq_n_f32(0.734375f)); + float32x4_t r = vaddq_f32(nn, dd); + vandq_f32(r, 0x807FFFFF); + vorq_f32(r, 0x40000000); + c = vaddq_f32(c, r); +} + + +inline void round_compute(const float32x4_t &n0, const float32x4_t &n1, const float32x4_t &n2, const float32x4_t &n3, const float32x4_t &rnd_c, float32x4_t &c, float32x4_t &r) +{ + float32x4_t n = vdupq_n_f32(0.0f), d = vdupq_n_f32(0.0f); + + sub_round(n0, n1, n2, n3, rnd_c, n, d, c); + sub_round(n1, n2, n3, n0, rnd_c, n, d, c); + sub_round(n2, n3, n0, n1, rnd_c, n, d, c); + sub_round(n3, n0, n1, n2, rnd_c, n, d, c); + sub_round(n3, n2, n1, n0, rnd_c, n, d, c); + sub_round(n2, n1, n0, n3, rnd_c, n, d, c); + sub_round(n1, n0, n3, n2, rnd_c, n, d, c); + sub_round(n0, n3, n2, n1, rnd_c, n, d, c); + + // Make sure abs(d) > 2.0 - this prevents division by zero and accidental overflows by division by < 1.0 + vandq_f32(d, 0xFF7FFFFF); + vorq_f32(d, 0x40000000); + r = vaddq_f32(r, vdivq_f32(n, d)); +} + + +// 112×4 = 448 +template +inline int32x4_t single_compute(const float32x4_t &n0, const float32x4_t &n1, const float32x4_t &n2, const float32x4_t &n3, float cnt, const float32x4_t &rnd_c, float32x4_t &sum) +{ + float32x4_t c = vdupq_n_f32(cnt); + float32x4_t r = vdupq_n_f32(0.0f); + + round_compute(n0, n1, n2, n3, rnd_c, c, r); + round_compute(n0, n1, n2, n3, rnd_c, c, r); + round_compute(n0, n1, n2, n3, rnd_c, c, r); + round_compute(n0, n1, n2, n3, rnd_c, c, r); + + // do a quick fmod by setting exp to 2 + vandq_f32(r, 0x807FFFFF); + vorq_f32(r, 0x40000000); + + if (add) { + sum = vaddq_f32(sum, r); + } else { + sum = r; + } + + const float32x4_t cc2 = vdupq_n_f32(536870880.0f); + r = vmulq_f32(r, cc2); // 35 + return vcvtq_s32_f32(r); +} + + +template +inline void single_compute_wrap(const float32x4_t &n0, const float32x4_t &n1, const float32x4_t &n2, const float32x4_t &n3, float cnt, const float32x4_t &rnd_c, float32x4_t &sum, int32x4_t &out) +{ + int32x4_t r = single_compute(n0, n1, n2, n3, cnt, rnd_c, sum); + vrot_si32(r); + out = veorq_s32(out, r); +} + + +template +inline int32_t *scratchpad_ptr(uint8_t* lpad, uint32_t idx, size_t n) { return reinterpret_cast(lpad + (idx & MASK) + n * 16); } + + +template +void cn_gpu_inner_arm(const uint8_t *spad, uint8_t *lpad) +{ + uint32_t s = reinterpret_cast(spad)[0] >> 8; + int32_t *idx0 = scratchpad_ptr(lpad, s, 0); + int32_t *idx1 = scratchpad_ptr(lpad, s, 1); + int32_t *idx2 = scratchpad_ptr(lpad, s, 2); + int32_t *idx3 = scratchpad_ptr(lpad, s, 3); + float32x4_t sum0 = vdupq_n_f32(0.0f); + + for (size_t i = 0; i < ITER; i++) { + float32x4_t n0, n1, n2, n3; + int32x4_t v0, v1, v2, v3; + float32x4_t suma, sumb, sum1, sum2, sum3; + + prep_dv(idx0, v0, n0); + prep_dv(idx1, v1, n1); + prep_dv(idx2, v2, n2); + prep_dv(idx3, v3, n3); + float32x4_t rc = sum0; + + int32x4_t out, out2; + out = vdupq_n_s32(0); + single_compute_wrap<0>(n0, n1, n2, n3, 1.3437500f, rc, suma, out); + single_compute_wrap<1>(n0, n2, n3, n1, 1.2812500f, rc, suma, out); + single_compute_wrap<2>(n0, n3, n1, n2, 1.3593750f, rc, sumb, out); + single_compute_wrap<3>(n0, n3, n2, n1, 1.3671875f, rc, sumb, out); + sum0 = vaddq_f32(suma, sumb); + vst1q_s32(idx0, veorq_s32(v0, out)); + out2 = out; + + out = vdupq_n_s32(0); + single_compute_wrap<0>(n1, n0, n2, n3, 1.4296875f, rc, suma, out); + single_compute_wrap<1>(n1, n2, n3, n0, 1.3984375f, rc, suma, out); + single_compute_wrap<2>(n1, n3, n0, n2, 1.3828125f, rc, sumb, out); + single_compute_wrap<3>(n1, n3, n2, n0, 1.3046875f, rc, sumb, out); + sum1 = vaddq_f32(suma, sumb); + vst1q_s32(idx1, veorq_s32(v1, out)); + out2 = veorq_s32(out2, out); + + out = vdupq_n_s32(0); + single_compute_wrap<0>(n2, n1, n0, n3, 1.4140625f, rc, suma, out); + single_compute_wrap<1>(n2, n0, n3, n1, 1.2734375f, rc, suma, out); + single_compute_wrap<2>(n2, n3, n1, n0, 1.2578125f, rc, sumb, out); + single_compute_wrap<3>(n2, n3, n0, n1, 1.2890625f, rc, sumb, out); + sum2 = vaddq_f32(suma, sumb); + vst1q_s32(idx2, veorq_s32(v2, out)); + out2 = veorq_s32(out2, out); + + out = vdupq_n_s32(0); + single_compute_wrap<0>(n3, n1, n2, n0, 1.3203125f, rc, suma, out); + single_compute_wrap<1>(n3, n2, n0, n1, 1.3515625f, rc, suma, out); + single_compute_wrap<2>(n3, n0, n1, n2, 1.3359375f, rc, sumb, out); + single_compute_wrap<3>(n3, n0, n2, n1, 1.4609375f, rc, sumb, out); + sum3 = vaddq_f32(suma, sumb); + vst1q_s32(idx3, veorq_s32(v3, out)); + out2 = veorq_s32(out2, out); + + sum0 = vaddq_f32(sum0, sum1); + sum2 = vaddq_f32(sum2, sum3); + sum0 = vaddq_f32(sum0, sum2); + + const float32x4_t cc1 = vdupq_n_f32(16777216.0f); + const float32x4_t cc2 = vdupq_n_f32(64.0f); + vandq_f32(sum0, 0x7fffffff); // take abs(va) by masking the float sign bit + // vs range 0 - 64 + n0 = vmulq_f32(sum0, cc1); + v0 = vcvtq_s32_f32(n0); + v0 = veorq_s32(v0, out2); + uint32_t n = vheor_s32(v0); + + // vs is now between 0 and 1 + sum0 = vdivq_f32(sum0, cc2); + idx0 = scratchpad_ptr(lpad, n, 0); + idx1 = scratchpad_ptr(lpad, n, 1); + idx2 = scratchpad_ptr(lpad, n, 2); + idx3 = scratchpad_ptr(lpad, n, 3); + } +} + +template void cn_gpu_inner_arm().iterations(), xmrig::CnAlgo().mask()>(const uint8_t* spad, uint8_t* lpad); diff --git a/src/crypto/cn/gpu/cn_gpu_avx.cpp b/src/crypto/cn/gpu/cn_gpu_avx.cpp new file mode 100644 index 00000000..38da9714 --- /dev/null +++ b/src/crypto/cn/gpu/cn_gpu_avx.cpp @@ -0,0 +1,211 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2019 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig + * + * 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 . + */ + + +#include "crypto/cn/CnAlgo.h" + + +#ifdef __GNUC__ +# include +#else +# include +# define __restrict__ __restrict +#endif +#ifndef _mm256_bslli_epi128 + #define _mm256_bslli_epi128(a, count) _mm256_slli_si256((a), (count)) +#endif +#ifndef _mm256_bsrli_epi128 + #define _mm256_bsrli_epi128(a, count) _mm256_srli_si256((a), (count)) +#endif + +inline void prep_dv_avx(__m256i* idx, __m256i& v, __m256& n01) +{ + v = _mm256_load_si256(idx); + n01 = _mm256_cvtepi32_ps(v); +} + +inline __m256 fma_break(const __m256& x) +{ + // Break the dependency chain by setitng the exp to ?????01 + __m256 xx = _mm256_and_ps(_mm256_castsi256_ps(_mm256_set1_epi32(0xFEFFFFFF)), x); + return _mm256_or_ps(_mm256_castsi256_ps(_mm256_set1_epi32(0x00800000)), xx); +} + +// 14 +inline void sub_round(const __m256& n0, const __m256& n1, const __m256& n2, const __m256& n3, const __m256& rnd_c, __m256& n, __m256& d, __m256& c) +{ + __m256 nn = _mm256_mul_ps(n0, c); + nn = _mm256_mul_ps(_mm256_add_ps(n1, c), _mm256_mul_ps(nn, nn)); + nn = fma_break(nn); + n = _mm256_add_ps(n, nn); + + __m256 dd = _mm256_mul_ps(n2, c); + dd = _mm256_mul_ps(_mm256_sub_ps(n3, c), _mm256_mul_ps(dd, dd)); + dd = fma_break(dd); + d = _mm256_add_ps(d, dd); + + //Constant feedback + c = _mm256_add_ps(c, rnd_c); + c = _mm256_add_ps(c, _mm256_set1_ps(0.734375f)); + __m256 r = _mm256_add_ps(nn, dd); + r = _mm256_and_ps(_mm256_castsi256_ps(_mm256_set1_epi32(0x807FFFFF)), r); + r = _mm256_or_ps(_mm256_castsi256_ps(_mm256_set1_epi32(0x40000000)), r); + c = _mm256_add_ps(c, r); +} + +// 14*8 + 2 = 112 +inline void round_compute(const __m256& n0, const __m256& n1, const __m256& n2, const __m256& n3, const __m256& rnd_c, __m256& c, __m256& r) +{ + __m256 n = _mm256_setzero_ps(), d = _mm256_setzero_ps(); + + sub_round(n0, n1, n2, n3, rnd_c, n, d, c); + sub_round(n1, n2, n3, n0, rnd_c, n, d, c); + sub_round(n2, n3, n0, n1, rnd_c, n, d, c); + sub_round(n3, n0, n1, n2, rnd_c, n, d, c); + sub_round(n3, n2, n1, n0, rnd_c, n, d, c); + sub_round(n2, n1, n0, n3, rnd_c, n, d, c); + sub_round(n1, n0, n3, n2, rnd_c, n, d, c); + sub_round(n0, n3, n2, n1, rnd_c, n, d, c); + + // Make sure abs(d) > 2.0 - this prevents division by zero and accidental overflows by division by < 1.0 + d = _mm256_and_ps(_mm256_castsi256_ps(_mm256_set1_epi32(0xFF7FFFFF)), d); + d = _mm256_or_ps(_mm256_castsi256_ps(_mm256_set1_epi32(0x40000000)), d); + r = _mm256_add_ps(r, _mm256_div_ps(n, d)); +} + +// 112×4 = 448 +template +inline __m256i double_compute(const __m256& n0, const __m256& n1, const __m256& n2, const __m256& n3, + float lcnt, float hcnt, const __m256& rnd_c, __m256& sum) +{ + __m256 c = _mm256_insertf128_ps(_mm256_castps128_ps256(_mm_set1_ps(lcnt)), _mm_set1_ps(hcnt), 1); + __m256 r = _mm256_setzero_ps(); + + round_compute(n0, n1, n2, n3, rnd_c, c, r); + round_compute(n0, n1, n2, n3, rnd_c, c, r); + round_compute(n0, n1, n2, n3, rnd_c, c, r); + round_compute(n0, n1, n2, n3, rnd_c, c, r); + + // do a quick fmod by setting exp to 2 + r = _mm256_and_ps(_mm256_castsi256_ps(_mm256_set1_epi32(0x807FFFFF)), r); + r = _mm256_or_ps(_mm256_castsi256_ps(_mm256_set1_epi32(0x40000000)), r); + + if(add) + sum = _mm256_add_ps(sum, r); + else + sum = r; + + r = _mm256_mul_ps(r, _mm256_set1_ps(536870880.0f)); // 35 + return _mm256_cvttps_epi32(r); +} + +template +inline void double_compute_wrap(const __m256& n0, const __m256& n1, const __m256& n2, const __m256& n3, + float lcnt, float hcnt, const __m256& rnd_c, __m256& sum, __m256i& out) +{ + __m256i r = double_compute(n0, n1, n2, n3, lcnt, hcnt, rnd_c, sum); + if(rot != 0) + r = _mm256_or_si256(_mm256_bslli_epi128(r, 16 - rot), _mm256_bsrli_epi128(r, rot)); + + out = _mm256_xor_si256(out, r); +} + +template +inline __m256i* scratchpad_ptr(uint8_t* lpad, uint32_t idx, size_t n) { return reinterpret_cast<__m256i*>(lpad + (idx & MASK) + n*16); } + +template +void cn_gpu_inner_avx(const uint8_t* spad, uint8_t* lpad) +{ + uint32_t s = reinterpret_cast(spad)[0] >> 8; + __m256i* idx0 = scratchpad_ptr(lpad, s, 0); + __m256i* idx2 = scratchpad_ptr(lpad, s, 2); + __m256 sum0 = _mm256_setzero_ps(); + + for(size_t i = 0; i < ITER; i++) + { + __m256i v01, v23; + __m256 suma, sumb, sum1; + __m256 rc = sum0; + + __m256 n01, n23; + prep_dv_avx(idx0, v01, n01); + prep_dv_avx(idx2, v23, n23); + + __m256i out, out2; + __m256 n10, n22, n33; + n10 = _mm256_permute2f128_ps(n01, n01, 0x01); + n22 = _mm256_permute2f128_ps(n23, n23, 0x00); + n33 = _mm256_permute2f128_ps(n23, n23, 0x11); + + out = _mm256_setzero_si256(); + double_compute_wrap<0>(n01, n10, n22, n33, 1.3437500f, 1.4296875f, rc, suma, out); + double_compute_wrap<1>(n01, n22, n33, n10, 1.2812500f, 1.3984375f, rc, suma, out); + double_compute_wrap<2>(n01, n33, n10, n22, 1.3593750f, 1.3828125f, rc, sumb, out); + double_compute_wrap<3>(n01, n33, n22, n10, 1.3671875f, 1.3046875f, rc, sumb, out); + _mm256_store_si256(idx0, _mm256_xor_si256(v01, out)); + sum0 = _mm256_add_ps(suma, sumb); + out2 = out; + + __m256 n11, n02, n30; + n11 = _mm256_permute2f128_ps(n01, n01, 0x11); + n02 = _mm256_permute2f128_ps(n01, n23, 0x20); + n30 = _mm256_permute2f128_ps(n01, n23, 0x03); + + out = _mm256_setzero_si256(); + double_compute_wrap<0>(n23, n11, n02, n30, 1.4140625f, 1.3203125f, rc, suma, out); + double_compute_wrap<1>(n23, n02, n30, n11, 1.2734375f, 1.3515625f, rc, suma, out); + double_compute_wrap<2>(n23, n30, n11, n02, 1.2578125f, 1.3359375f, rc, sumb, out); + double_compute_wrap<3>(n23, n30, n02, n11, 1.2890625f, 1.4609375f, rc, sumb, out); + _mm256_store_si256(idx2, _mm256_xor_si256(v23, out)); + sum1 = _mm256_add_ps(suma, sumb); + + out2 = _mm256_xor_si256(out2, out); + out2 = _mm256_xor_si256(_mm256_permute2x128_si256(out2,out2,0x41), out2); + suma = _mm256_permute2f128_ps(sum0, sum1, 0x30); + sumb = _mm256_permute2f128_ps(sum0, sum1, 0x21); + sum0 = _mm256_add_ps(suma, sumb); + sum0 = _mm256_add_ps(sum0, _mm256_permute2f128_ps(sum0, sum0, 0x41)); + + // Clear the high 128 bits + __m128 sum = _mm256_castps256_ps128(sum0); + + sum = _mm_and_ps(_mm_castsi128_ps(_mm_set1_epi32(0x7fffffff)), sum); // take abs(va) by masking the float sign bit + // vs range 0 - 64 + __m128i v0 = _mm_cvttps_epi32(_mm_mul_ps(sum, _mm_set1_ps(16777216.0f))); + v0 = _mm_xor_si128(v0, _mm256_castsi256_si128(out2)); + __m128i v1 = _mm_shuffle_epi32(v0, _MM_SHUFFLE(0, 1, 2, 3)); + v0 = _mm_xor_si128(v0, v1); + v1 = _mm_shuffle_epi32(v0, _MM_SHUFFLE(0, 1, 0, 1)); + v0 = _mm_xor_si128(v0, v1); + + // vs is now between 0 and 1 + sum = _mm_div_ps(sum, _mm_set1_ps(64.0f)); + sum0 = _mm256_insertf128_ps(_mm256_castps128_ps256(sum), sum, 1); + uint32_t n = _mm_cvtsi128_si32(v0); + idx0 = scratchpad_ptr(lpad, n, 0); + idx2 = scratchpad_ptr(lpad, n, 2); + } +} + +template void cn_gpu_inner_avx().iterations(), xmrig::CnAlgo().mask()>(const uint8_t* spad, uint8_t* lpad); diff --git a/src/crypto/cn/gpu/cn_gpu_ssse3.cpp b/src/crypto/cn/gpu/cn_gpu_ssse3.cpp new file mode 100644 index 00000000..7cca096e --- /dev/null +++ b/src/crypto/cn/gpu/cn_gpu_ssse3.cpp @@ -0,0 +1,212 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2019 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig + * + * 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 . + */ + + +#include "crypto/cn/CnAlgo.h" + + +#ifdef __GNUC__ +# include +#else +# include +# define __restrict__ __restrict +#endif + +inline void prep_dv(__m128i* idx, __m128i& v, __m128& n) +{ + v = _mm_load_si128(idx); + n = _mm_cvtepi32_ps(v); +} + +inline __m128 fma_break(__m128 x) +{ + // Break the dependency chain by setitng the exp to ?????01 + x = _mm_and_ps(_mm_castsi128_ps(_mm_set1_epi32(0xFEFFFFFF)), x); + return _mm_or_ps(_mm_castsi128_ps(_mm_set1_epi32(0x00800000)), x); +} + +// 14 +inline void sub_round(__m128 n0, __m128 n1, __m128 n2, __m128 n3, __m128 rnd_c, __m128& n, __m128& d, __m128& c) +{ + n1 = _mm_add_ps(n1, c); + __m128 nn = _mm_mul_ps(n0, c); + nn = _mm_mul_ps(n1, _mm_mul_ps(nn,nn)); + nn = fma_break(nn); + n = _mm_add_ps(n, nn); + + n3 = _mm_sub_ps(n3, c); + __m128 dd = _mm_mul_ps(n2, c); + dd = _mm_mul_ps(n3, _mm_mul_ps(dd,dd)); + dd = fma_break(dd); + d = _mm_add_ps(d, dd); + + //Constant feedback + c = _mm_add_ps(c, rnd_c); + c = _mm_add_ps(c, _mm_set1_ps(0.734375f)); + __m128 r = _mm_add_ps(nn, dd); + r = _mm_and_ps(_mm_castsi128_ps(_mm_set1_epi32(0x807FFFFF)), r); + r = _mm_or_ps(_mm_castsi128_ps(_mm_set1_epi32(0x40000000)), r); + c = _mm_add_ps(c, r); +} + +// 14*8 + 2 = 112 +inline void round_compute(__m128 n0, __m128 n1, __m128 n2, __m128 n3, __m128 rnd_c, __m128& c, __m128& r) +{ + __m128 n = _mm_setzero_ps(), d = _mm_setzero_ps(); + + sub_round(n0, n1, n2, n3, rnd_c, n, d, c); + sub_round(n1, n2, n3, n0, rnd_c, n, d, c); + sub_round(n2, n3, n0, n1, rnd_c, n, d, c); + sub_round(n3, n0, n1, n2, rnd_c, n, d, c); + sub_round(n3, n2, n1, n0, rnd_c, n, d, c); + sub_round(n2, n1, n0, n3, rnd_c, n, d, c); + sub_round(n1, n0, n3, n2, rnd_c, n, d, c); + sub_round(n0, n3, n2, n1, rnd_c, n, d, c); + + // Make sure abs(d) > 2.0 - this prevents division by zero and accidental overflows by division by < 1.0 + d = _mm_and_ps(_mm_castsi128_ps(_mm_set1_epi32(0xFF7FFFFF)), d); + d = _mm_or_ps(_mm_castsi128_ps(_mm_set1_epi32(0x40000000)), d); + r =_mm_add_ps(r, _mm_div_ps(n,d)); +} + +// 112×4 = 448 +template +inline __m128i single_compute(__m128 n0, __m128 n1, __m128 n2, __m128 n3, float cnt, __m128 rnd_c, __m128& sum) +{ + __m128 c = _mm_set1_ps(cnt); + __m128 r = _mm_setzero_ps(); + + round_compute(n0, n1, n2, n3, rnd_c, c, r); + round_compute(n0, n1, n2, n3, rnd_c, c, r); + round_compute(n0, n1, n2, n3, rnd_c, c, r); + round_compute(n0, n1, n2, n3, rnd_c, c, r); + + // do a quick fmod by setting exp to 2 + r = _mm_and_ps(_mm_castsi128_ps(_mm_set1_epi32(0x807FFFFF)), r); + r = _mm_or_ps(_mm_castsi128_ps(_mm_set1_epi32(0x40000000)), r); + + if(add) + sum = _mm_add_ps(sum, r); + else + sum = r; + + r = _mm_mul_ps(r, _mm_set1_ps(536870880.0f)); // 35 + return _mm_cvttps_epi32(r); +} + +template +inline void single_compute_wrap(__m128 n0, __m128 n1, __m128 n2, __m128 n3, float cnt, __m128 rnd_c, __m128& sum, __m128i& out) +{ + __m128i r = single_compute(n0, n1, n2, n3, cnt, rnd_c, sum); + if(rot != 0) + r = _mm_or_si128(_mm_slli_si128(r, 16 - rot), _mm_srli_si128(r, rot)); + out = _mm_xor_si128(out, r); +} + +template +inline __m128i* scratchpad_ptr(uint8_t* lpad, uint32_t idx, size_t n) { return reinterpret_cast<__m128i*>(lpad + (idx & MASK) + n*16); } + +template +void cn_gpu_inner_ssse3(const uint8_t* spad, uint8_t* lpad) +{ + uint32_t s = reinterpret_cast(spad)[0] >> 8; + __m128i* idx0 = scratchpad_ptr(lpad, s, 0); + __m128i* idx1 = scratchpad_ptr(lpad, s, 1); + __m128i* idx2 = scratchpad_ptr(lpad, s, 2); + __m128i* idx3 = scratchpad_ptr(lpad, s, 3); + __m128 sum0 = _mm_setzero_ps(); + + for(size_t i = 0; i < ITER; i++) + { + __m128 n0, n1, n2, n3; + __m128i v0, v1, v2, v3; + __m128 suma, sumb, sum1, sum2, sum3; + + prep_dv(idx0, v0, n0); + prep_dv(idx1, v1, n1); + prep_dv(idx2, v2, n2); + prep_dv(idx3, v3, n3); + __m128 rc = sum0; + + __m128i out, out2; + out = _mm_setzero_si128(); + single_compute_wrap<0>(n0, n1, n2, n3, 1.3437500f, rc, suma, out); + single_compute_wrap<1>(n0, n2, n3, n1, 1.2812500f, rc, suma, out); + single_compute_wrap<2>(n0, n3, n1, n2, 1.3593750f, rc, sumb, out); + single_compute_wrap<3>(n0, n3, n2, n1, 1.3671875f, rc, sumb, out); + sum0 = _mm_add_ps(suma, sumb); + _mm_store_si128(idx0, _mm_xor_si128(v0, out)); + out2 = out; + + out = _mm_setzero_si128(); + single_compute_wrap<0>(n1, n0, n2, n3, 1.4296875f, rc, suma, out); + single_compute_wrap<1>(n1, n2, n3, n0, 1.3984375f, rc, suma, out); + single_compute_wrap<2>(n1, n3, n0, n2, 1.3828125f, rc, sumb, out); + single_compute_wrap<3>(n1, n3, n2, n0, 1.3046875f, rc, sumb, out); + sum1 = _mm_add_ps(suma, sumb); + _mm_store_si128(idx1, _mm_xor_si128(v1, out)); + out2 = _mm_xor_si128(out2, out); + + out = _mm_setzero_si128(); + single_compute_wrap<0>(n2, n1, n0, n3, 1.4140625f, rc, suma, out); + single_compute_wrap<1>(n2, n0, n3, n1, 1.2734375f, rc, suma, out); + single_compute_wrap<2>(n2, n3, n1, n0, 1.2578125f, rc, sumb, out); + single_compute_wrap<3>(n2, n3, n0, n1, 1.2890625f, rc, sumb, out); + sum2 = _mm_add_ps(suma, sumb); + _mm_store_si128(idx2, _mm_xor_si128(v2, out)); + out2 = _mm_xor_si128(out2, out); + + out = _mm_setzero_si128(); + single_compute_wrap<0>(n3, n1, n2, n0, 1.3203125f, rc, suma, out); + single_compute_wrap<1>(n3, n2, n0, n1, 1.3515625f, rc, suma, out); + single_compute_wrap<2>(n3, n0, n1, n2, 1.3359375f, rc, sumb, out); + single_compute_wrap<3>(n3, n0, n2, n1, 1.4609375f, rc, sumb, out); + sum3 = _mm_add_ps(suma, sumb); + _mm_store_si128(idx3, _mm_xor_si128(v3, out)); + out2 = _mm_xor_si128(out2, out); + sum0 = _mm_add_ps(sum0, sum1); + sum2 = _mm_add_ps(sum2, sum3); + sum0 = _mm_add_ps(sum0, sum2); + + sum0 = _mm_and_ps(_mm_castsi128_ps(_mm_set1_epi32(0x7fffffff)), sum0); // take abs(va) by masking the float sign bit + // vs range 0 - 64 + n0 = _mm_mul_ps(sum0, _mm_set1_ps(16777216.0f)); + v0 = _mm_cvttps_epi32(n0); + v0 = _mm_xor_si128(v0, out2); + v1 = _mm_shuffle_epi32(v0, _MM_SHUFFLE(0, 1, 2, 3)); + v0 = _mm_xor_si128(v0, v1); + v1 = _mm_shuffle_epi32(v0, _MM_SHUFFLE(0, 1, 0, 1)); + v0 = _mm_xor_si128(v0, v1); + + // vs is now between 0 and 1 + sum0 = _mm_div_ps(sum0, _mm_set1_ps(64.0f)); + uint32_t n = _mm_cvtsi128_si32(v0); + idx0 = scratchpad_ptr(lpad, n, 0); + idx1 = scratchpad_ptr(lpad, n, 1); + idx2 = scratchpad_ptr(lpad, n, 2); + idx3 = scratchpad_ptr(lpad, n, 3); + } +} + +template void cn_gpu_inner_ssse3().iterations(), xmrig::CnAlgo().mask()>(const uint8_t* spad, uint8_t* lpad); diff --git a/src/crypto/groestl_tables.h b/src/crypto/cn/groestl_tables.h similarity index 100% rename from src/crypto/groestl_tables.h rename to src/crypto/cn/groestl_tables.h diff --git a/src/crypto/hash.h b/src/crypto/cn/hash.h similarity index 100% rename from src/crypto/hash.h rename to src/crypto/cn/hash.h diff --git a/src/crypto/CryptoNightR_gen.cpp b/src/crypto/cn/r/CryptonightR_gen.cpp similarity index 85% rename from src/crypto/CryptoNightR_gen.cpp rename to src/crypto/cn/r/CryptonightR_gen.cpp index d856cade..3b80f805 100644 --- a/src/crypto/CryptoNightR_gen.cpp +++ b/src/crypto/cn/r/CryptonightR_gen.cpp @@ -24,15 +24,14 @@ */ #include +#include "crypto/cn/CryptoNight_monero.h" typedef void(*void_func)(); -#include "crypto/asm/CryptonightR_template.h" -#include "Mem.h" +#include "crypto/cn/asm/CryptonightR_template.h" +#include "crypto/common/Assembly.h" +#include "crypto/common/VirtualMemory.h" -#if !defined XMRIG_ARM && !defined XMRIG_NO_ASM - -#include "crypto/CryptoNight_x86.h" static inline void add_code(uint8_t* &p, void (*p1)(), void (*p2)()) { @@ -43,7 +42,7 @@ static inline void add_code(uint8_t* &p, void (*p1)(), void (*p2)()) } } -static inline void add_random_math(uint8_t* &p, const V4_Instruction* code, int code_size, const void_func* instructions, const void_func* instructions_mov, bool is_64_bit, AsmOptimization ASM) +static inline void add_random_math(uint8_t* &p, const V4_Instruction* code, int code_size, const void_func* instructions, const void_func* instructions_mov, bool is_64_bit, xmrig::Assembly::Id ASM) { uint32_t prev_rot_src = (uint32_t)(-1); @@ -62,13 +61,13 @@ static inline void add_random_math(uint8_t* &p, const V4_Instruction* code, int const uint8_t c = opcode | (dst_index << V4_OPCODE_BITS) | (((src_index == 8) ? dst_index : src_index) << (V4_OPCODE_BITS + V4_DST_INDEX_BITS)); switch (inst.opcode) { - case ROR: - case ROL: - if (b != prev_rot_src) { - prev_rot_src = b; - add_code(p, instructions_mov[c], instructions_mov[c + 1]); - } - break; + case ROR: + case ROL: + if (b != prev_rot_src) { + prev_rot_src = b; + add_code(p, instructions_mov[c], instructions_mov[c + 1]); + } + break; } if (a == prev_rot_src) { @@ -77,7 +76,7 @@ static inline void add_random_math(uint8_t* &p, const V4_Instruction* code, int void_func begin = instructions[c]; - if ((ASM = ASM_BULLDOZER) && (inst.opcode == MUL) && !is_64_bit) { + if ((ASM = xmrig::Assembly::BULLDOZER) && (inst.opcode == MUL) && !is_64_bit) { // AMD Bulldozer has latency 4 for 32-bit IMUL and 6 for 64-bit IMUL // Always use 32-bit IMUL for AMD Bulldozer in 32-bit mode - skip prefix 0x48 and change 0x49 to 0x41 uint8_t* prefix = reinterpret_cast(begin); @@ -100,7 +99,7 @@ static inline void add_random_math(uint8_t* &p, const V4_Instruction* code, int } } -void wow_compile_code(const V4_Instruction* code, int code_size, void* machine_code, AsmOptimization ASM) +void wow_compile_code(const V4_Instruction* code, int code_size, void* machine_code, xmrig::Assembly ASM) { uint8_t* p0 = reinterpret_cast(machine_code); uint8_t* p = p0; @@ -111,10 +110,10 @@ void wow_compile_code(const V4_Instruction* code, int code_size, void* machine_c *(int*)(p - 4) = static_cast((((const uint8_t*)CryptonightWOW_template_mainloop) - ((const uint8_t*)CryptonightWOW_template_part1)) - (p - p0)); add_code(p, CryptonightWOW_template_part3, CryptonightWOW_template_end); - Mem::flushInstructionCache(machine_code, p - p0); + xmrig::VirtualMemory::flushInstructionCache(machine_code, p - p0); } -void v4_compile_code(const V4_Instruction* code, int code_size, void* machine_code, AsmOptimization ASM) +void v4_compile_code(const V4_Instruction* code, int code_size, void* machine_code, xmrig::Assembly ASM) { uint8_t* p0 = reinterpret_cast(machine_code); uint8_t* p = p0; @@ -125,10 +124,10 @@ void v4_compile_code(const V4_Instruction* code, int code_size, void* machine_co *(int*)(p - 4) = static_cast((((const uint8_t*)CryptonightR_template_mainloop) - ((const uint8_t*)CryptonightR_template_part1)) - (p - p0)); add_code(p, CryptonightR_template_part3, CryptonightR_template_end); - Mem::flushInstructionCache(machine_code, p - p0); + xmrig::VirtualMemory::flushInstructionCache(machine_code, p - p0); } -void wow_compile_code_double(const V4_Instruction* code, int code_size, void* machine_code, AsmOptimization ASM) +void wow_compile_code_double(const V4_Instruction* code, int code_size, void* machine_code, xmrig::Assembly ASM) { uint8_t* p0 = reinterpret_cast(machine_code); uint8_t* p = p0; @@ -141,10 +140,10 @@ void wow_compile_code_double(const V4_Instruction* code, int code_size, void* ma *(int*)(p - 4) = static_cast((((const uint8_t*)CryptonightWOW_template_double_mainloop) - ((const uint8_t*)CryptonightWOW_template_double_part1)) - (p - p0)); add_code(p, CryptonightWOW_template_double_part4, CryptonightWOW_template_double_end); - Mem::flushInstructionCache(machine_code, p - p0); + xmrig::VirtualMemory::flushInstructionCache(machine_code, p - p0); } -void v4_compile_code_double(const V4_Instruction* code, int code_size, void* machine_code, AsmOptimization ASM) +void v4_compile_code_double(const V4_Instruction* code, int code_size, void* machine_code, xmrig::Assembly ASM) { uint8_t* p0 = reinterpret_cast(machine_code); uint8_t* p = p0; @@ -157,10 +156,10 @@ void v4_compile_code_double(const V4_Instruction* code, int code_size, void* mac *(int*)(p - 4) = static_cast((((const uint8_t*)CryptonightR_template_double_mainloop) - ((const uint8_t*)CryptonightR_template_double_part1)) - (p - p0)); add_code(p, CryptonightR_template_double_part4, CryptonightR_template_double_end); - Mem::flushInstructionCache(machine_code, p - p0); + xmrig::VirtualMemory::flushInstructionCache(machine_code, p - p0); } -void wow_soft_aes_compile_code(const V4_Instruction* code, int code_size, void* machine_code, AsmOptimization ASM) +void wow_soft_aes_compile_code(const V4_Instruction* code, int code_size, void* machine_code, xmrig::Assembly ASM) { uint8_t* p0 = reinterpret_cast(machine_code); uint8_t* p = p0; @@ -171,10 +170,10 @@ void wow_soft_aes_compile_code(const V4_Instruction* code, int code_size, void* *(int*)(p - 4) = static_cast((((const uint8_t*)CryptonightWOW_soft_aes_template_mainloop) - ((const uint8_t*)CryptonightWOW_soft_aes_template_part1)) - (p - p0)); add_code(p, CryptonightWOW_soft_aes_template_part3, CryptonightWOW_soft_aes_template_end); - Mem::flushInstructionCache(machine_code, p - p0); + xmrig::VirtualMemory::flushInstructionCache(machine_code, p - p0); } -void v4_soft_aes_compile_code(const V4_Instruction* code, int code_size, void* machine_code, AsmOptimization ASM) +void v4_soft_aes_compile_code(const V4_Instruction* code, int code_size, void* machine_code, xmrig::Assembly ASM) { uint8_t* p0 = reinterpret_cast(machine_code); uint8_t* p = p0; @@ -185,6 +184,5 @@ void v4_soft_aes_compile_code(const V4_Instruction* code, int code_size, void* m *(int*)(p - 4) = static_cast((((const uint8_t*)CryptonightR_soft_aes_template_mainloop) - ((const uint8_t*)CryptonightR_soft_aes_template_part1)) - (p - p0)); add_code(p, CryptonightR_soft_aes_template_part3, CryptonightR_soft_aes_template_end); - Mem::flushInstructionCache(machine_code, p - p0); + xmrig::VirtualMemory::flushInstructionCache(machine_code, p - p0); } -#endif \ No newline at end of file diff --git a/src/crypto/cn/r/variant4_random_math.h b/src/crypto/cn/r/variant4_random_math.h new file mode 100644 index 00000000..48f0f6ce --- /dev/null +++ b/src/crypto/cn/r/variant4_random_math.h @@ -0,0 +1,453 @@ +#ifndef VARIANT4_RANDOM_MATH_H +#define VARIANT4_RANDOM_MATH_H + + +#include + + +#include "crypto/common/Algorithm.h" + + +extern "C" +{ + #include "crypto/cn/c_blake256.h" +} + +enum V4_Settings +{ + // Generate code with minimal theoretical latency = 45 cycles, which is equivalent to 15 multiplications + TOTAL_LATENCY = 15 * 3, + + // Always generate at least 60 instructions + NUM_INSTRUCTIONS_MIN = 60, + + // Never generate more than 70 instructions (final RET instruction doesn't count here) + NUM_INSTRUCTIONS_MAX = 70, + + // Available ALUs for MUL + // Modern CPUs typically have only 1 ALU which can do multiplications + ALU_COUNT_MUL = 1, + + // Total available ALUs + // Modern CPUs have 4 ALUs, but we use only 3 because random math executes together with other main loop code + ALU_COUNT = 3, +}; + +enum V4_InstructionList +{ + MUL, // a*b + ADD, // a+b + C, C is an unsigned 32-bit constant + SUB, // a-b + ROR, // rotate right "a" by "b & 31" bits + ROL, // rotate left "a" by "b & 31" bits + XOR, // a^b + RET, // finish execution + V4_INSTRUCTION_COUNT = RET, +}; + +// V4_InstructionDefinition is used to generate code from random data +// Every random sequence of bytes is a valid code +// +// There are 9 registers in total: +// - 4 variable registers +// - 5 constant registers initialized from loop variables +// This is why dst_index is 2 bits +enum V4_InstructionDefinition +{ + V4_OPCODE_BITS = 3, + V4_DST_INDEX_BITS = 2, + V4_SRC_INDEX_BITS = 3, +}; + +struct V4_Instruction +{ + uint8_t opcode; + uint8_t dst_index; + uint8_t src_index; + uint32_t C; +}; + +#ifndef FORCEINLINE +#ifdef __GNUC__ +#define FORCEINLINE __attribute__((always_inline)) inline +#elif _MSC_VER +#define FORCEINLINE __forceinline +#else +#define FORCEINLINE inline +#endif +#endif + +#ifndef UNREACHABLE_CODE +#ifdef __GNUC__ +#define UNREACHABLE_CODE __builtin_unreachable() +#elif _MSC_VER +#define UNREACHABLE_CODE __assume(false) +#else +#define UNREACHABLE_CODE +#endif +#endif + +// Random math interpreter's loop is fully unrolled and inlined to achieve 100% branch prediction on CPU: +// every switch-case will point to the same destination on every iteration of Cryptonight main loop +// +// This is about as fast as it can get without using low-level machine code generation +template +static void v4_random_math(const struct V4_Instruction* code, v4_reg* r) +{ + enum + { + REG_BITS = sizeof(v4_reg) * 8, + }; + +#define V4_EXEC(i) \ + { \ + const struct V4_Instruction* op = code + i; \ + const v4_reg src = r[op->src_index]; \ + v4_reg* dst = r + op->dst_index; \ + switch (op->opcode) \ + { \ + case MUL: \ + *dst *= src; \ + break; \ + case ADD: \ + *dst += src + op->C; \ + break; \ + case SUB: \ + *dst -= src; \ + break; \ + case ROR: \ + { \ + const uint32_t shift = src % REG_BITS; \ + *dst = (*dst >> shift) | (*dst << ((REG_BITS - shift) % REG_BITS)); \ + } \ + break; \ + case ROL: \ + { \ + const uint32_t shift = src % REG_BITS; \ + *dst = (*dst << shift) | (*dst >> ((REG_BITS - shift) % REG_BITS)); \ + } \ + break; \ + case XOR: \ + *dst ^= src; \ + break; \ + case RET: \ + return; \ + default: \ + UNREACHABLE_CODE; \ + break; \ + } \ + } + +#define V4_EXEC_10(j) \ + V4_EXEC(j + 0) \ + V4_EXEC(j + 1) \ + V4_EXEC(j + 2) \ + V4_EXEC(j + 3) \ + V4_EXEC(j + 4) \ + V4_EXEC(j + 5) \ + V4_EXEC(j + 6) \ + V4_EXEC(j + 7) \ + V4_EXEC(j + 8) \ + V4_EXEC(j + 9) + + // Generated program can have 60 + a few more (usually 2-3) instructions to achieve required latency + // I've checked all block heights < 10,000,000 and here is the distribution of program sizes: + // + // 60 27960 + // 61 105054 + // 62 2452759 + // 63 5115997 + // 64 1022269 + // 65 1109635 + // 66 153145 + // 67 8550 + // 68 4529 + // 69 102 + + // Unroll 70 instructions here + V4_EXEC_10(0); // instructions 0-9 + V4_EXEC_10(10); // instructions 10-19 + V4_EXEC_10(20); // instructions 20-29 + V4_EXEC_10(30); // instructions 30-39 + V4_EXEC_10(40); // instructions 40-49 + V4_EXEC_10(50); // instructions 50-59 + V4_EXEC_10(60); // instructions 60-69 + +#undef V4_EXEC_10 +#undef V4_EXEC +} + +// If we don't have enough data available, generate more +static FORCEINLINE void check_data(size_t* data_index, const size_t bytes_needed, int8_t* data, const size_t data_size) +{ + if (*data_index + bytes_needed > data_size) + { + hash_extra_blake(data, data_size, (char*) data); + *data_index = 0; + } +} + +// Generates as many random math operations as possible with given latency and ALU restrictions +// "code" array must have space for NUM_INSTRUCTIONS_MAX+1 instructions +template +static int v4_random_math_init(struct V4_Instruction* code, const uint64_t height) +{ + // MUL is 3 cycles, 3-way addition and rotations are 2 cycles, SUB/XOR are 1 cycle + // These latencies match real-life instruction latencies for Intel CPUs starting from Sandy Bridge and up to Skylake/Coffee lake + // + // AMD Ryzen has the same latencies except 1-cycle ROR/ROL, so it'll be a bit faster than Intel Sandy Bridge and newer processors + // Surprisingly, Intel Nehalem also has 1-cycle ROR/ROL, so it'll also be faster than Intel Sandy Bridge and newer processors + // AMD Bulldozer has 4 cycles latency for MUL (slower than Intel) and 1 cycle for ROR/ROL (faster than Intel), so average performance will be the same + // Source: https://www.agner.org/optimize/instruction_tables.pdf + const int op_latency[V4_INSTRUCTION_COUNT] = { 3, 2, 1, 2, 2, 1 }; + + // Instruction latencies for theoretical ASIC implementation + const int asic_op_latency[V4_INSTRUCTION_COUNT] = { 3, 1, 1, 1, 1, 1 }; + + // Available ALUs for each instruction + const int op_ALUs[V4_INSTRUCTION_COUNT] = { ALU_COUNT_MUL, ALU_COUNT, ALU_COUNT, ALU_COUNT, ALU_COUNT, ALU_COUNT }; + + int8_t data[32]; + memset(data, 0, sizeof(data)); + uint64_t tmp = SWAP64LE(height); + memcpy(data, &tmp, sizeof(uint64_t)); + if (ALGO == xmrig::Algorithm::CN_R) { + data[20] = -38; + } + + // Set data_index past the last byte in data + // to trigger full data update with blake hash + // before we start using it + size_t data_index = sizeof(data); + + int code_size; + + // There is a small chance (1.8%) that register R8 won't be used in the generated program + // So we keep track of it and try again if it's not used + bool r8_used; + do { + int latency[9]; + int asic_latency[9]; + + // Tracks previous instruction and value of the source operand for registers R0-R3 throughout code execution + // byte 0: current value of the destination register + // byte 1: instruction opcode + // byte 2: current value of the source register + // + // Registers R4-R8 are constant and are treated as having the same value because when we do + // the same operation twice with two constant source registers, it can be optimized into a single operation + uint32_t inst_data[9] = { 0, 1, 2, 3, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF }; + + bool alu_busy[TOTAL_LATENCY + 1][ALU_COUNT]; + bool is_rotation[V4_INSTRUCTION_COUNT]; + bool rotated[4]; + int rotate_count = 0; + + memset(latency, 0, sizeof(latency)); + memset(asic_latency, 0, sizeof(asic_latency)); + memset(alu_busy, 0, sizeof(alu_busy)); + memset(is_rotation, 0, sizeof(is_rotation)); + memset(rotated, 0, sizeof(rotated)); + is_rotation[ROR] = true; + is_rotation[ROL] = true; + + int num_retries = 0; + code_size = 0; + + int total_iterations = 0; + r8_used = (ALGO == xmrig::Algorithm::CN_WOW); + + // Generate random code to achieve minimal required latency for our abstract CPU + // Try to get this latency for all 4 registers + while (((latency[0] < TOTAL_LATENCY) || (latency[1] < TOTAL_LATENCY) || (latency[2] < TOTAL_LATENCY) || (latency[3] < TOTAL_LATENCY)) && (num_retries < 64)) + { + // Fail-safe to guarantee loop termination + ++total_iterations; + if (total_iterations > 256) + break; + + check_data(&data_index, 1, data, sizeof(data)); + + const uint8_t c = ((uint8_t*)data)[data_index++]; + + // MUL = opcodes 0-2 + // ADD = opcode 3 + // SUB = opcode 4 + // ROR/ROL = opcode 5, shift direction is selected randomly + // XOR = opcodes 6-7 + uint8_t opcode = c & ((1 << V4_OPCODE_BITS) - 1); + if (opcode == 5) + { + check_data(&data_index, 1, data, sizeof(data)); + opcode = (data[data_index++] >= 0) ? ROR : ROL; + } + else if (opcode >= 6) + { + opcode = XOR; + } + else + { + opcode = (opcode <= 2) ? MUL : (opcode - 2); + } + + uint8_t dst_index = (c >> V4_OPCODE_BITS) & ((1 << V4_DST_INDEX_BITS) - 1); + uint8_t src_index = (c >> (V4_OPCODE_BITS + V4_DST_INDEX_BITS)) & ((1 << V4_SRC_INDEX_BITS) - 1); + + const int a = dst_index; + int b = src_index; + + // Don't do ADD/SUB/XOR with the same register + if (((opcode == ADD) || (opcode == SUB) || (opcode == XOR)) && (a == b)) { + // a is always < 4, so we don't need to check bounds here + b = (ALGO == xmrig::Algorithm::CN_WOW) ? (a + 4) : 8; + src_index = b; + } + + // Don't do rotation with the same destination twice because it's equal to a single rotation + if (is_rotation[opcode] && rotated[a]) + { + continue; + } + + // Don't do the same instruction (except MUL) with the same source value twice because all other cases can be optimized: + // 2xADD(a, b, C) = ADD(a, b*2, C1+C2), same for SUB and rotations + // 2xXOR(a, b) = NOP + if ((opcode != MUL) && ((inst_data[a] & 0xFFFF00) == (opcode << 8) + ((inst_data[b] & 255) << 16))) + { + continue; + } + + // Find which ALU is available (and when) for this instruction + int next_latency = (latency[a] > latency[b]) ? latency[a] : latency[b]; + int alu_index = -1; + while (next_latency < TOTAL_LATENCY) + { + for (int i = op_ALUs[opcode] - 1; i >= 0; --i) + { + if (!alu_busy[next_latency][i]) + { + // ADD is implemented as two 1-cycle instructions on a real CPU, so do an additional availability check + if ((opcode == ADD) && alu_busy[next_latency + 1][i]) + { + continue; + } + + // Rotation can only start when previous rotation is finished, so do an additional availability check + if (is_rotation[opcode] && (next_latency < rotate_count * op_latency[opcode])) + { + continue; + } + + alu_index = i; + break; + } + } + if (alu_index >= 0) + { + break; + } + ++next_latency; + } + + // Don't generate instructions that leave some register unchanged for more than 7 cycles + if (next_latency > latency[a] + 7) + { + continue; + } + + next_latency += op_latency[opcode]; + + if (next_latency <= TOTAL_LATENCY) + { + if (is_rotation[opcode]) + { + ++rotate_count; + } + + // Mark ALU as busy only for the first cycle when it starts executing the instruction because ALUs are fully pipelined + alu_busy[next_latency - op_latency[opcode]][alu_index] = true; + latency[a] = next_latency; + + // ASIC is supposed to have enough ALUs to run as many independent instructions per cycle as possible, so latency calculation for ASIC is simple + asic_latency[a] = ((asic_latency[a] > asic_latency[b]) ? asic_latency[a] : asic_latency[b]) + asic_op_latency[opcode]; + + rotated[a] = is_rotation[opcode]; + + inst_data[a] = code_size + (opcode << 8) + ((inst_data[b] & 255) << 16); + + code[code_size].opcode = opcode; + code[code_size].dst_index = dst_index; + code[code_size].src_index = src_index; + code[code_size].C = 0; + + if (src_index == 8) + { + r8_used = true; + } + + if (opcode == ADD) + { + // ADD instruction is implemented as two 1-cycle instructions on a real CPU, so mark ALU as busy for the next cycle too + alu_busy[next_latency - op_latency[opcode] + 1][alu_index] = true; + + // ADD instruction requires 4 more random bytes for 32-bit constant "C" in "a = a + b + C" + check_data(&data_index, sizeof(uint32_t), data, sizeof(data)); + uint32_t t; + memcpy(&t, data + data_index, sizeof(uint32_t)); + code[code_size].C = SWAP32LE(t); + data_index += sizeof(uint32_t); + } + + ++code_size; + if (code_size >= NUM_INSTRUCTIONS_MIN) + { + break; + } + } + else + { + ++num_retries; + } + } + + // ASIC has more execution resources and can extract as much parallelism from the code as possible + // We need to add a few more MUL and ROR instructions to achieve minimal required latency for ASIC + // Get this latency for at least 1 of the 4 registers + const int prev_code_size = code_size; + while ((code_size < NUM_INSTRUCTIONS_MAX) && (asic_latency[0] < TOTAL_LATENCY) && (asic_latency[1] < TOTAL_LATENCY) && (asic_latency[2] < TOTAL_LATENCY) && (asic_latency[3] < TOTAL_LATENCY)) + { + int min_idx = 0; + int max_idx = 0; + for (int i = 1; i < 4; ++i) + { + if (asic_latency[i] < asic_latency[min_idx]) min_idx = i; + if (asic_latency[i] > asic_latency[max_idx]) max_idx = i; + } + + const uint8_t pattern[3] = { ROR, MUL, MUL }; + const uint8_t opcode = pattern[(code_size - prev_code_size) % 3]; + latency[min_idx] = latency[max_idx] + op_latency[opcode]; + asic_latency[min_idx] = asic_latency[max_idx] + asic_op_latency[opcode]; + + code[code_size].opcode = opcode; + code[code_size].dst_index = min_idx; + code[code_size].src_index = max_idx; + code[code_size].C = 0; + ++code_size; + } + + // There is ~98.15% chance that loop condition is false, so this loop will execute only 1 iteration most of the time + // It never does more than 4 iterations for all block heights < 10,000,000 + } while (!r8_used || (code_size < NUM_INSTRUCTIONS_MIN) || (code_size > NUM_INSTRUCTIONS_MAX)); + + // It's guaranteed that NUM_INSTRUCTIONS_MIN <= code_size <= NUM_INSTRUCTIONS_MAX here + // Add final instruction to stop the interpreter + code[code_size].opcode = RET; + code[code_size].dst_index = 0; + code[code_size].src_index = 0; + code[code_size].C = 0; + + return code_size; +} + +#endif diff --git a/src/crypto/skein_port.h b/src/crypto/cn/skein_port.h similarity index 100% rename from src/crypto/skein_port.h rename to src/crypto/cn/skein_port.h diff --git a/src/crypto/soft_aes.h b/src/crypto/cn/soft_aes.h similarity index 88% rename from src/crypto/soft_aes.h rename to src/crypto/cn/soft_aes.h index 20c67c09..fca31d1c 100644 --- a/src/crypto/soft_aes.h +++ b/src/crypto/cn/soft_aes.h @@ -28,7 +28,7 @@ #if defined(XMRIG_ARM) -# include "crypto/SSE2NEON.h" +# include "crypto/cn/SSE2NEON.h" #elif defined(__GNUC__) # include #else @@ -114,23 +114,23 @@ static inline __m128i soft_aesenc(__m128i in, __m128i key) x3 = _mm_cvtsi128_si32(_mm_shuffle_epi32(in, 0xFF)); __m128i out = _mm_set_epi32( - (saes_table[0][x3 & 0xff] ^ saes_table[1][(x0 >> 8) & 0xff] ^ saes_table[2][(x1 >> 16) & 0xff] ^ saes_table[3][x2 >> 24]), - (saes_table[0][x2 & 0xff] ^ saes_table[1][(x3 >> 8) & 0xff] ^ saes_table[2][(x0 >> 16) & 0xff] ^ saes_table[3][x1 >> 24]), - (saes_table[0][x1 & 0xff] ^ saes_table[1][(x2 >> 8) & 0xff] ^ saes_table[2][(x3 >> 16) & 0xff] ^ saes_table[3][x0 >> 24]), - (saes_table[0][x0 & 0xff] ^ saes_table[1][(x1 >> 8) & 0xff] ^ saes_table[2][(x2 >> 16) & 0xff] ^ saes_table[3][x3 >> 24])); + (saes_table[0][x3 & 0xff] ^ saes_table[1][(x0 >> 8) & 0xff] ^ saes_table[2][(x1 >> 16) & 0xff] ^ saes_table[3][x2 >> 24]), + (saes_table[0][x2 & 0xff] ^ saes_table[1][(x3 >> 8) & 0xff] ^ saes_table[2][(x0 >> 16) & 0xff] ^ saes_table[3][x1 >> 24]), + (saes_table[0][x1 & 0xff] ^ saes_table[1][(x2 >> 8) & 0xff] ^ saes_table[2][(x3 >> 16) & 0xff] ^ saes_table[3][x0 >> 24]), + (saes_table[0][x0 & 0xff] ^ saes_table[1][(x1 >> 8) & 0xff] ^ saes_table[2][(x2 >> 16) & 0xff] ^ saes_table[3][x3 >> 24])); return _mm_xor_si128(out, key); } static inline uint32_t sub_word(uint32_t key) { - return (saes_sbox[key >> 24 ] << 24) | - (saes_sbox[(key >> 16) & 0xff] << 16 ) | - (saes_sbox[(key >> 8) & 0xff] << 8 ) | - saes_sbox[key & 0xff]; + return (saes_sbox[key >> 24 ] << 24) | + (saes_sbox[(key >> 16) & 0xff] << 16 ) | + (saes_sbox[(key >> 8) & 0xff] << 8 ) | + saes_sbox[key & 0xff]; } -#if defined(__clang__) || defined(XMRIG_ARM) +#ifndef HAVE_ROTR static inline uint32_t _rotr(uint32_t value, uint32_t amount) { return (value >> amount) | (value << ((32 - amount) & 31)); diff --git a/src/crypto/common/Algorithm.cpp b/src/crypto/common/Algorithm.cpp new file mode 100644 index 00000000..4fee85ff --- /dev/null +++ b/src/crypto/common/Algorithm.cpp @@ -0,0 +1,332 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018 Lee Clagett + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include +#include +#include +#include + + +#include "crypto/cn/CnAlgo.h" +#include "crypto/common/Algorithm.h" +#include "rapidjson/document.h" + + +#ifdef _MSC_VER +# define strcasecmp _stricmp +#endif + + +#ifndef ARRAY_SIZE +# define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) +#endif + + +namespace xmrig { + + +struct AlgoName +{ + const char *name; + const char *shortName; + const Algorithm::Id id; +}; + + +static AlgoName const algorithm_names[] = { + { "cryptonight/0", "cn/0", Algorithm::CN_0 }, + { "cryptonight", "cn", Algorithm::CN_0 }, + { "cryptonight/1", "cn/1", Algorithm::CN_1 }, + { "cryptonight-monerov7", nullptr, Algorithm::CN_1 }, + { "cryptonight_v7", nullptr, Algorithm::CN_1 }, + { "cryptonight/2", "cn/2", Algorithm::CN_2 }, + { "cryptonight-monerov8", nullptr, Algorithm::CN_2 }, + { "cryptonight_v8", nullptr, Algorithm::CN_2 }, + { "cryptonight/r", "cn/r", Algorithm::CN_R }, + { "cryptonight_r", nullptr, Algorithm::CN_R }, + { "cryptonight/wow", "cn/wow", Algorithm::CN_WOW }, + { "cryptonight/fast", "cn/fast", Algorithm::CN_FAST }, + { "cryptonight/msr", "cn/msr", Algorithm::CN_FAST }, + { "cryptonight/half", "cn/half", Algorithm::CN_HALF }, + { "cryptonight/xao", "cn/xao", Algorithm::CN_XAO }, + { "cryptonight_alloy", nullptr, Algorithm::CN_XAO }, + { "cryptonight/rto", "cn/rto", Algorithm::CN_RTO }, + { "cryptonight/rwz", "cn/rwz", Algorithm::CN_RWZ }, + { "cryptonight/zls", "cn/zls", Algorithm::CN_ZLS }, + { "cryptonight/double", "cn/double", Algorithm::CN_DOUBLE }, + { "cryptonight/conceal", "cn/conceal", Algorithm::CN_CONCEAL }, + { "cryptonight/ccx", "cn/ccx", Algorithm::CN_CONCEAL }, +# ifdef XMRIG_ALGO_CN_GPU + { "cryptonight/gpu", "cn/gpu", Algorithm::CN_GPU }, + { "cryptonight_gpu", nullptr, Algorithm::CN_GPU }, +# endif +# ifdef XMRIG_ALGO_CN_LITE + { "cryptonight-lite/0", "cn-lite/0", Algorithm::CN_LITE_0 }, + { "cryptonight-lite/1", "cn-lite/1", Algorithm::CN_LITE_1 }, + { "cryptonight-lite", "cn-lite", Algorithm::CN_LITE_1 }, + { "cryptonight-light", "cn-light", Algorithm::CN_LITE_1 }, + { "cryptonight_lite", nullptr, Algorithm::CN_LITE_1 }, + { "cryptonight-aeonv7", nullptr, Algorithm::CN_LITE_1 }, + { "cryptonight_lite_v7", nullptr, Algorithm::CN_LITE_1 }, +# endif +# ifdef XMRIG_ALGO_CN_HEAVY + { "cryptonight-heavy/0", "cn-heavy/0", Algorithm::CN_HEAVY_0 }, + { "cryptonight-heavy", "cn-heavy", Algorithm::CN_HEAVY_0 }, + { "cryptonight_heavy", nullptr, Algorithm::CN_HEAVY_0 }, + { "cryptonight-heavy/xhv", "cn-heavy/xhv", Algorithm::CN_HEAVY_XHV }, + { "cryptonight_haven", nullptr, Algorithm::CN_HEAVY_XHV }, + { "cryptonight-heavy/tube", "cn-heavy/tube", Algorithm::CN_HEAVY_TUBE }, + { "cryptonight-bittube2", nullptr, Algorithm::CN_HEAVY_TUBE }, +# endif +# ifdef XMRIG_ALGO_CN_PICO + { "cryptonight-pico", "cn-pico", Algorithm::CN_PICO_0 }, + { "cryptonight-pico/trtl", "cn-pico/trtl", Algorithm::CN_PICO_0 }, + { "cryptonight-turtle", "cn-trtl", Algorithm::CN_PICO_0 }, + { "cryptonight-ultralite", "cn-ultralite", Algorithm::CN_PICO_0 }, + { "cryptonight_turtle", "cn_turtle", Algorithm::CN_PICO_0 }, +# endif +# ifdef XMRIG_ALGO_CN_EXTREMELITE + { "cryptonight-extremelite/upx2", "cn-extremelite/upx2", Algorithm::CN_EXTREMELITE_0 }, + { "cryptonight-extremelite", "cn-extremelite", Algorithm::CN_EXTREMELITE_0}, + { "cryptonight-upx2", "cn-upx2", Algorithm::CN_EXTREMELITE_0}, + { "upx2", nullptr, Algorithm::CN_EXTREMELITE_0}, + { "cryptonight-extremelite", nullptr, Algorithm::CN_EXTREMELITE_0}, +# endif +# ifdef XMRIG_ALGO_RANDOMX + { "randomx/test", "rx/test", Algorithm::RX_0 }, + { "randomx/0", "rx/0", Algorithm::RX_0 }, + { "randomx/0", "rx/0", Algorithm::RX_0 }, + { "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 }, +# endif +# ifdef XMRIG_ALGO_ARGON2 + { "argon2/chukwa", nullptr, Algorithm::AR2_CHUKWA }, + { "chukwa", nullptr, Algorithm::AR2_CHUKWA }, + { "ar2/512", nullptr, Algorithm::AR2_CHUKWA }, + { "ar2-512", nullptr, Algorithm::AR2_CHUKWA }, + { "argon2/wrkz", nullptr, Algorithm::AR2_WRKZ }, + { "ar2/256", nullptr, Algorithm::AR2_WRKZ }, + { "ar2-256", nullptr, Algorithm::AR2_WRKZ }, +# endif +}; + + +} /* namespace xmrig */ + + +rapidjson::Value xmrig::Algorithm::toJSON() const +{ + using namespace rapidjson; + + return isValid() ? Value(StringRef(shortName())) : Value(kNullType); +} + + +size_t xmrig::Algorithm::l2() const +{ +# ifdef XMRIG_ALGO_RANDOMX + switch (m_id) { + case RX_0: + case RX_LOKI: + return 0x40000; + + case RX_WOW: + return 0x20000; + + default: + break; + } +# endif + + return 0; +} + + +size_t xmrig::Algorithm::l3() const +{ + constexpr size_t oneMiB = 0x100000; + + const Family f = family(); + assert(f != UNKNOWN); + + if (f < RANDOM_X) { + return CnAlgo<>::memory(m_id); + } + +# ifdef XMRIG_ALGO_RANDOMX + if (f == RANDOM_X) { + switch (m_id) { + case RX_0: + case RX_LOKI: + return oneMiB * 2; + + case RX_WOW: + return oneMiB; + + default: + break; + } + } +# endif + +# ifdef XMRIG_ALGO_ARGON2 + if (f == ARGON2) { + switch (m_id) { + case AR2_CHUKWA: + return oneMiB / 2; + + case AR2_WRKZ: + return oneMiB / 4; + + default: + break; + } + } +# endif + + return 0; +} + + +uint32_t xmrig::Algorithm::maxIntensity() const +{ +# ifdef XMRIG_ALGO_RANDOMX + if (family() == RANDOM_X) { + return 1; + } +# endif + +# ifdef XMRIG_ALGO_ARGON2 + if (family() == ARGON2) { + return 1; + } +# endif + +# ifdef XMRIG_ALGO_CN_GPU + if (m_id == CN_GPU) { + return 1; + } +# endif + + return 5; +} + + +xmrig::Algorithm::Family xmrig::Algorithm::family(Id id) +{ + switch (id) { + case CN_0: + case CN_1: + case CN_2: + case CN_R: + case CN_WOW: + case CN_FAST: + case CN_HALF: + case CN_XAO: + case CN_RTO: + case CN_RWZ: + case CN_ZLS: + case CN_DOUBLE: + case CN_CONCEAL: +# ifdef XMRIG_ALGO_CN_GPU + case CN_GPU: +# endif + return CN; + +# ifdef XMRIG_ALGO_CN_LITE + case CN_LITE_0: + case CN_LITE_1: + return CN_LITE; +# endif + +# ifdef XMRIG_ALGO_CN_HEAVY + case CN_HEAVY_0: + case CN_HEAVY_TUBE: + case CN_HEAVY_XHV: + return CN_HEAVY; +# endif + +# ifdef XMRIG_ALGO_CN_PICO + case CN_PICO_0: + return CN_PICO; +# endif + +# ifdef XMRIG_ALGO_CN_EXTREMELITE + case CN_EXTREMELITE_0: + return CN_EXTREMELITE; +# endif + +# ifdef XMRIG_ALGO_RANDOMX + case RX_0: + case RX_WOW: + case RX_LOKI: + return RANDOM_X; +# endif + +# ifdef XMRIG_ALGO_ARGON2 + case AR2_CHUKWA: + case AR2_WRKZ: + return ARGON2; +# endif + + case INVALID: + case MAX: + return UNKNOWN; + } + + return UNKNOWN; +} + + +xmrig::Algorithm::Id xmrig::Algorithm::parse(const char *name) +{ + if (name == nullptr || strlen(name) < 1) { + return INVALID; + } + + for (size_t i = 0; i < ARRAY_SIZE(algorithm_names); i++) { + if ((strcasecmp(name, algorithm_names[i].name) == 0) || (algorithm_names[i].shortName != nullptr && strcasecmp(name, algorithm_names[i].shortName) == 0)) { + return algorithm_names[i].id; + } + } + + return INVALID; +} + + +const char *xmrig::Algorithm::name(bool shortName) const +{ + for (size_t i = 0; i < ARRAY_SIZE(algorithm_names); i++) { + if (algorithm_names[i].id == m_id) { + return (shortName && algorithm_names[i].shortName) ? algorithm_names[i].shortName : algorithm_names[i].name; + } + } + + return "invalid"; +} diff --git a/src/crypto/common/Algorithm.h b/src/crypto/common/Algorithm.h new file mode 100644 index 00000000..520fb15c --- /dev/null +++ b/src/crypto/common/Algorithm.h @@ -0,0 +1,136 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018 Lee Clagett + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_ALGORITHM_H +#define XMRIG_ALGORITHM_H + + +#include + + +#include "rapidjson/fwd.h" + + +namespace xmrig { + + +class Algorithm +{ +public: + enum Id : int { + INVALID = -1, + CN_0, // "cn/0" CryptoNight (original). + CN_1, // "cn/1" CryptoNight variant 1 also known as Monero7 and CryptoNightV7. + CN_2, // "cn/2" CryptoNight variant 2. + CN_R, // "cn/r" CryptoNightR (Monero's variant 4). + CN_WOW, // "cn/wow" CryptoNightR (Wownero). + CN_FAST, // "cn/fast" CryptoNight variant 1 with half iterations. + CN_HALF, // "cn/half" CryptoNight variant 2 with half iterations (Masari/Torque). + CN_XAO, // "cn/xao" CryptoNight variant 0 (modified, Alloy only). + CN_RTO, // "cn/rto" CryptoNight variant 1 (modified, Arto only). + CN_RWZ, // "cn/rwz" CryptoNight variant 2 with 3/4 iterations and reversed shuffle operation (Graft). + CN_ZLS, // "cn/zls" CryptoNight variant 2 with 3/4 iterations (Zelerius). + CN_DOUBLE, // "cn/double" CryptoNight variant 2 with double iterations (X-CASH). + CN_CONCEAL, // "cn/conceal" CryptoNight variant 0 (modified, Conceal only). +# ifdef XMRIG_ALGO_CN_GPU + CN_GPU, // "cn/gpu" CryptoNight-GPU (Ryo). +# endif +# ifdef XMRIG_ALGO_CN_LITE + CN_LITE_0, // "cn-lite/0" CryptoNight-Lite variant 0. + CN_LITE_1, // "cn-lite/1" CryptoNight-Lite variant 1. +# endif +# ifdef XMRIG_ALGO_CN_HEAVY + CN_HEAVY_0, // "cn-heavy/0" CryptoNight-Heavy (4 MB). + CN_HEAVY_TUBE, // "cn-heavy/tube" CryptoNight-Heavy (modified, TUBE only). + CN_HEAVY_XHV, // "cn-heavy/xhv" CryptoNight-Heavy (modified, Haven Protocol only). +# endif +# ifdef XMRIG_ALGO_CN_PICO + CN_PICO_0, // "cn-pico" CryptoNight Turtle (TRTL) +# endif +# ifdef XMRIG_ALGO_CN_EXTREMELITE + CN_EXTREMELITE_0, // "cn-extremelite" CryptoNight UPX +# endif +# ifdef XMRIG_ALGO_RANDOMX + RX_0, // "rx/0" RandomX (reference configuration). + RX_WOW, // "rx/wow" RandomWOW (Wownero). + RX_LOKI, // "rx/loki" RandomXL (Loki). +# endif +# ifdef XMRIG_ALGO_ARGON2 + AR2_CHUKWA, // "argon2/chukwa" + AR2_WRKZ, // "argon2/wrkz" +# endif + MAX + }; + + enum Family : int { + UNKNOWN, + CN, + CN_LITE, + CN_HEAVY, + CN_PICO, + CN_EXTREMELITE, + RANDOM_X, + ARGON2 + }; + + inline Algorithm() {} + inline Algorithm(const char *algo) : m_id(parse(algo)) {} + inline Algorithm(Id id) : m_id(id) {} + + inline bool isEqual(const Algorithm &other) const { return m_id == other.m_id; } + inline bool isValid() const { return m_id != INVALID; } + inline const char *name() const { return name(false); } + inline const char *shortName() const { return name(true); } + inline Family family() const { return family(m_id); } + inline Id id() const { return m_id; } + + inline bool operator!=(Algorithm::Id id) const { return m_id != id; } + inline bool operator!=(const Algorithm &other) const { return !isEqual(other); } + inline bool operator==(Algorithm::Id id) const { return m_id == id; } + inline bool operator==(const Algorithm &other) const { return isEqual(other); } + inline operator Algorithm::Id() const { return m_id; } + + rapidjson::Value toJSON() const; + size_t l2() const; + size_t l3() const; + uint32_t maxIntensity() const; + + static Family family(Id id); + static Id parse(const char *name); + +private: + const char *name(bool shortName) const; + + Id m_id = INVALID; +}; + + +typedef std::vector Algorithms; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_ALGORITHM_H */ diff --git a/src/crypto/common/Assembly.cpp b/src/crypto/common/Assembly.cpp new file mode 100644 index 00000000..44bf0a94 --- /dev/null +++ b/src/crypto/common/Assembly.cpp @@ -0,0 +1,107 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018 SChernykh + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include +#include + + +#ifdef _MSC_VER +# define strcasecmp _stricmp +#endif + + +#include "crypto/common/Assembly.h" +#include "rapidjson/document.h" + + +namespace xmrig { + + +static const char *asmNames[] = { + "none", + "auto", + "intel", + "ryzen", + "bulldozer" +}; + + +} /* namespace xmrig */ + + +xmrig::Assembly::Id xmrig::Assembly::parse(const char *assembly, Id defaultValue) +{ + constexpr size_t const size = sizeof(asmNames) / sizeof((asmNames)[0]); + static_assert(size == MAX, "asmNames size mismatch"); + + if (assembly == nullptr) { + return defaultValue; + } + + for (size_t i = 0; i < size; i++) { + if (strcasecmp(assembly, asmNames[i]) == 0) { + return static_cast(i); + } + } + + return defaultValue; +} + + +xmrig::Assembly::Id xmrig::Assembly::parse(const rapidjson::Value &value, Id defaultValue) +{ + if (value.IsBool()) { + return value.GetBool() ? AUTO : NONE; + } + + if (value.IsString()) { + return parse(value.GetString(), defaultValue); + } + + return defaultValue; +} + + +const char *xmrig::Assembly::toString() const +{ + return asmNames[m_id]; +} + + +rapidjson::Value xmrig::Assembly::toJSON() const +{ + using namespace rapidjson; + + if (m_id == NONE) { + return Value(false); + } + + if (m_id == AUTO) { + return Value(true); + } + + return Value(StringRef(toString())); +} diff --git a/src/crypto/common/Assembly.h b/src/crypto/common/Assembly.h new file mode 100644 index 00000000..5ea29e11 --- /dev/null +++ b/src/crypto/common/Assembly.h @@ -0,0 +1,75 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_ASSEMBLY_H +#define XMRIG_ASSEMBLY_H + + +#include "rapidjson/fwd.h" + + +namespace xmrig { + + +class Assembly +{ +public: + enum Id : int { + NONE, + AUTO, + INTEL, + RYZEN, + BULLDOZER, + MAX + }; + + + inline Assembly() {} + inline Assembly(Id id) : m_id(id) {} + inline Assembly(const char *assembly) : m_id(parse(assembly)) {} + inline Assembly(const rapidjson::Value &value) : m_id(parse(value)) {} + + static Id parse(const char *assembly, Id defaultValue = AUTO); + static Id parse(const rapidjson::Value &value, Id defaultValue = AUTO); + + const char *toString() const; + rapidjson::Value toJSON() const; + + inline bool isEqual(const Assembly &other) const { return m_id == other.m_id; } + + inline bool operator!=(Assembly::Id id) const { return m_id != id; } + inline bool operator!=(const Assembly &other) const { return !isEqual(other); } + inline bool operator==(Assembly::Id id) const { return m_id == id; } + inline bool operator==(const Assembly &other) const { return isEqual(other); } + inline operator Assembly::Id() const { return m_id; } + +private: + Id m_id = AUTO; +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_ASSEMBLY_H */ diff --git a/src/crypto/common/Nonce.cpp b/src/crypto/common/Nonce.cpp new file mode 100644 index 00000000..151819e0 --- /dev/null +++ b/src/crypto/common/Nonce.cpp @@ -0,0 +1,100 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include + + +#include "crypto/common/Nonce.h" + + +namespace xmrig { + + +std::atomic Nonce::m_paused; +std::atomic Nonce::m_sequence[Nonce::MAX]; +uint32_t Nonce::m_nonces[2] = { 0, 0 }; + + +static std::mutex mutex; +static Nonce nonce; + + +} // namespace xmrig + + +xmrig::Nonce::Nonce() +{ + m_paused = true; + + for (int i = 0; i < MAX; ++i) { + m_sequence[i] = 1; + } +} + + +uint32_t xmrig::Nonce::next(uint8_t index, uint32_t nonce, uint32_t reserveCount, bool nicehash) +{ + uint32_t next; + + std::lock_guard lock(mutex); + + if (nicehash) { + next = (nonce & 0xFF000000) | m_nonces[index]; + } + else { + next = m_nonces[index]; + } + + m_nonces[index] += reserveCount; + + return next; +} + + +void xmrig::Nonce::reset(uint8_t index) +{ + std::lock_guard lock(mutex); + + m_nonces[index] = 0; + touch(); +} + + +void xmrig::Nonce::stop() +{ + pause(false); + + for (int i = 0; i < MAX; ++i) { + m_sequence[i] = 0; + } +} + + +void xmrig::Nonce::touch() +{ + for (int i = 0; i < MAX; ++i) { + m_sequence[i]++; + } +} diff --git a/src/crypto/common/Nonce.h b/src/crypto/common/Nonce.h new file mode 100644 index 00000000..401139fd --- /dev/null +++ b/src/crypto/common/Nonce.h @@ -0,0 +1,70 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_NONCE_H +#define XMRIG_NONCE_H + + +#include + + +namespace xmrig { + + +class Nonce +{ +public: + enum Backend { + CPU, + OPENCL, + CUDA, + MAX + }; + + + Nonce(); + + static inline bool isOutdated(Backend backend, uint64_t sequence) { return m_sequence[backend].load(std::memory_order_relaxed) != sequence; } + static inline bool isPaused() { return m_paused.load(std::memory_order_relaxed); } + static inline uint64_t sequence(Backend backend) { return m_sequence[backend].load(std::memory_order_relaxed); } + static inline void pause(bool paused) { m_paused = paused; } + static inline void stop(Backend backend) { m_sequence[backend] = 0; } + static inline void touch(Backend backend) { m_sequence[backend]++; } + + static uint32_t next(uint8_t index, uint32_t nonce, uint32_t reserveCount, bool nicehash); + static void reset(uint8_t index); + static void stop(); + static void touch(); + +private: + static std::atomic m_paused; + static std::atomic m_sequence[MAX]; + static uint32_t m_nonces[2]; +}; + + +} // namespace xmrig + + +#endif /* XMRIG_NONCE_H */ diff --git a/src/crypto/common/VirtualMemory.cpp b/src/crypto/common/VirtualMemory.cpp new file mode 100644 index 00000000..081b6c0f --- /dev/null +++ b/src/crypto/common/VirtualMemory.cpp @@ -0,0 +1,85 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018 Lee Clagett + * Copyright 2018-2019 SChernykh + * Copyright 2018-2019 tevador + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#ifdef XMRIG_FEATURE_HWLOC +# include +# include "backend/cpu/platform/HwlocCpuInfo.h" +# +# if HWLOC_API_VERSION < 0x00010b00 +# define HWLOC_OBJ_NUMANODE HWLOC_OBJ_NODE +# endif +#endif + + +#include "base/io/log/Log.h" +#include "crypto/common/VirtualMemory.h" + + +uint32_t xmrig::VirtualMemory::bindToNUMANode(int64_t affinity) +{ +# ifdef XMRIG_FEATURE_HWLOC + if (affinity < 0 || !HwlocCpuInfo::has(HwlocCpuInfo::SET_THISTHREAD_MEMBIND)) { + return 0; + } + + hwloc_topology_t topology; + hwloc_topology_init(&topology); + hwloc_topology_load(topology); + + const unsigned puId = static_cast(affinity); + + hwloc_obj_t pu = hwloc_get_pu_obj_by_os_index(topology, puId); + +# if HWLOC_API_VERSION >= 0x20000 + if (pu == nullptr || hwloc_set_membind(topology, pu->nodeset, HWLOC_MEMBIND_BIND, HWLOC_MEMBIND_THREAD | HWLOC_MEMBIND_BYNODESET) < 0) { +# else + if (pu == nullptr || hwloc_set_membind_nodeset(topology, pu->nodeset, HWLOC_MEMBIND_BIND, HWLOC_MEMBIND_THREAD) < 0) { +# endif + LOG_WARN("CPU #%02u warning: \"can't bind memory\"", puId); + } + + uint32_t nodeId = 0; + + if (pu) { + hwloc_obj_t node = nullptr; + + while ((node = hwloc_get_next_obj_by_type(topology, HWLOC_OBJ_NUMANODE, node)) != nullptr) { + if (hwloc_bitmap_intersects(node->cpuset, pu->cpuset)) { + nodeId = node->os_index; + + break; + } + } + } + + hwloc_topology_destroy(topology); + + return nodeId; +# else + return 0; +# endif +} diff --git a/src/crypto/common/VirtualMemory.h b/src/crypto/common/VirtualMemory.h new file mode 100644 index 00000000..ac2f75dd --- /dev/null +++ b/src/crypto/common/VirtualMemory.h @@ -0,0 +1,86 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018 Lee Clagett + * Copyright 2018-2019 SChernykh + * Copyright 2018-2019 tevador + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_VIRTUALMEMORY_H +#define XMRIG_VIRTUALMEMORY_H + + +#include +#include +#include + + +namespace xmrig { + + +class VirtualMemory +{ +public: + inline VirtualMemory() {} + VirtualMemory(size_t size, bool hugePages = true, size_t align = 64); + ~VirtualMemory(); + + inline bool isHugePages() const { return m_flags & HUGEPAGES; } + inline size_t size() const { return m_size; } + inline uint8_t *scratchpad() const { return m_scratchpad; } + + inline std::pair hugePages() const + { + return std::pair(isHugePages() ? (align(size()) / 2097152) : 0, align(size()) / 2097152); + } + + static uint32_t bindToNUMANode(int64_t affinity); + static void *allocateExecutableMemory(size_t size); + static void *allocateLargePagesMemory(size_t size); + static void flushInstructionCache(void *p, size_t size); + static void freeLargePagesMemory(void *p, size_t size); + static void init(bool hugePages); + static void protectExecutableMemory(void *p, size_t size); + static void unprotectExecutableMemory(void *p, size_t size); + + static inline bool isHugepagesAvailable() { return (m_globalFlags & HUGEPAGES_AVAILABLE) != 0; } + static inline constexpr size_t align(size_t pos, size_t align = 2097152) { return ((pos - 1) / align + 1) * align; } + +private: + enum Flags { + HUGEPAGES_AVAILABLE = 1, + HUGEPAGES = 2, + LOCK = 4 + }; + + static int m_globalFlags; + + int m_flags = 0; + size_t m_size = 0; + uint8_t *m_scratchpad = nullptr; +}; + + +} /* namespace xmrig */ + + + +#endif /* XMRIG_VIRTUALMEMORY_H */ diff --git a/src/crypto/common/VirtualMemory_unix.cpp b/src/crypto/common/VirtualMemory_unix.cpp new file mode 100644 index 00000000..310a043a --- /dev/null +++ b/src/crypto/common/VirtualMemory_unix.cpp @@ -0,0 +1,143 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018 Lee Clagett + * Copyright 2018-2019 SChernykh + * Copyright 2018-2019 tevador + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include +#include + + +#include "crypto/common/portable/mm_malloc.h" +#include "crypto/common/VirtualMemory.h" + + +#if defined(__APPLE__) +# include +#endif + + +int xmrig::VirtualMemory::m_globalFlags = 0; + + +xmrig::VirtualMemory::VirtualMemory(size_t size, bool hugePages, size_t align) : + m_size(VirtualMemory::align(size)) +{ + if (hugePages) { + m_scratchpad = static_cast(allocateLargePagesMemory(m_size)); + if (m_scratchpad) { + m_flags |= HUGEPAGES; + + madvise(m_scratchpad, size, MADV_RANDOM | MADV_WILLNEED); + + if (mlock(m_scratchpad, m_size) == 0) { + m_flags |= LOCK; + } + + return; + } + } + + m_scratchpad = static_cast(_mm_malloc(m_size, align)); +} + + +xmrig::VirtualMemory::~VirtualMemory() +{ + if (!m_scratchpad) { + return; + } + + if (isHugePages()) { + if (m_flags & LOCK) { + munlock(m_scratchpad, m_size); + } + + freeLargePagesMemory(m_scratchpad, m_size); + } + else { + _mm_free(m_scratchpad); + } +} + + + +void *xmrig::VirtualMemory::allocateExecutableMemory(size_t size) +{ +# if defined(__APPLE__) + void *mem = mmap(0, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANON, -1, 0); +# else + void *mem = mmap(0, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); +# endif + + return mem == MAP_FAILED ? nullptr : mem; +} + + +void *xmrig::VirtualMemory::allocateLargePagesMemory(size_t size) +{ +# if defined(__APPLE__) + void *mem = mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, VM_FLAGS_SUPERPAGE_SIZE_2MB, 0); +# elif defined(__FreeBSD__) + void *mem = mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_ALIGNED_SUPER | MAP_PREFAULT_READ, -1, 0); +# else + void *mem = mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB | MAP_POPULATE, 0, 0); +# endif + + return mem == MAP_FAILED ? nullptr : mem; +} + + +void xmrig::VirtualMemory::flushInstructionCache(void *p, size_t size) +{ +# ifdef HAVE_BUILTIN_CLEAR_CACHE + __builtin___clear_cache(reinterpret_cast(p), reinterpret_cast(p) + size); +# endif +} + + +void xmrig::VirtualMemory::freeLargePagesMemory(void *p, size_t size) +{ + munmap(p, size); +} + + +void xmrig::VirtualMemory::init(bool hugePages) +{ + if (hugePages) { + m_globalFlags = HUGEPAGES | HUGEPAGES_AVAILABLE; + } +} + + +void xmrig::VirtualMemory::protectExecutableMemory(void *p, size_t size) +{ + mprotect(p, size, PROT_READ | PROT_EXEC); +} + + +void xmrig::VirtualMemory::unprotectExecutableMemory(void *p, size_t size) +{ + mprotect(p, size, PROT_WRITE | PROT_EXEC); +} diff --git a/src/Mem_win.cpp b/src/crypto/common/VirtualMemory_win.cpp similarity index 59% rename from src/Mem_win.cpp rename to src/crypto/common/VirtualMemory_win.cpp index a3cdf055..7bdb6365 100644 --- a/src/Mem_win.cpp +++ b/src/crypto/common/VirtualMemory_win.cpp @@ -4,8 +4,11 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018 Lee Clagett + * Copyright 2018-2019 SChernykh + * Copyright 2018-2019 tevador + * Copyright 2016-2019 XMRig , * * 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 @@ -21,16 +24,16 @@ * along with this program. If not, see . */ -#include + #include #include #include #include -#include "log/Log.h" -#include "crypto/HashSelector.h" -#include "Mem.h" -#include "Options.h" + +#include "base/io/log/Log.h" +#include "crypto/common/portable/mm_malloc.h" +#include "crypto/common/VirtualMemory.h" /***************************************************************** @@ -62,11 +65,11 @@ static BOOL SetLockPagesPrivilege() { tp.PrivilegeCount = 1; tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; - if (LookupPrivilegeValue(NULL, SE_LOCK_MEMORY_NAME, &(tp.Privileges[0].Luid)) != TRUE) { + if (LookupPrivilegeValue(nullptr, SE_LOCK_MEMORY_NAME, &(tp.Privileges[0].Luid)) != TRUE) { return FALSE; } - BOOL rc = AdjustTokenPrivileges(token, FALSE, (PTOKEN_PRIVILEGES) &tp, 0, NULL, NULL); + BOOL rc = AdjustTokenPrivileges(token, FALSE, (PTOKEN_PRIVILEGES) &tp, 0, nullptr, nullptr); if (rc != TRUE || GetLastError() != ERROR_SUCCESS) { return FALSE; } @@ -90,12 +93,12 @@ static LSA_UNICODE_STRING StringToLsaUnicodeString(LPCTSTR string) { static BOOL ObtainLockPagesPrivilege() { HANDLE token; - PTOKEN_USER user = NULL; + PTOKEN_USER user = nullptr; if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token) == TRUE) { DWORD size = 0; - GetTokenInformation(token, TokenUser, NULL, 0, &size); + GetTokenInformation(token, TokenUser, nullptr, 0, &size); if (size) { user = (PTOKEN_USER) LocalAlloc(LPTR, size); } @@ -113,7 +116,7 @@ static BOOL ObtainLockPagesPrivilege() { ZeroMemory(&attributes, sizeof(attributes)); BOOL result = FALSE; - if (LsaOpenPolicy(NULL, &attributes, POLICY_ALL_ACCESS, &handle) == 0) { + if (LsaOpenPolicy(nullptr, &attributes, POLICY_ALL_ACCESS, &handle) == 0) { LSA_UNICODE_STRING str = StringToLsaUnicodeString(_T(SE_LOCK_MEMORY_NAME)); if (LsaAddAccountRights(handle, user->User.Sid, &str, 1) == 0) { @@ -138,58 +141,94 @@ static BOOL TrySetLockPagesPrivilege() { } -void Mem::init(const Options* options) +int xmrig::VirtualMemory::m_globalFlags = 0; + + +xmrig::VirtualMemory::VirtualMemory(size_t size, bool hugePages, size_t align) : + m_size(VirtualMemory::align(size)) { - m_hashFactor = options->hashFactor(); - m_useHugePages = options->hugePages(); - m_algo = options->algo(); - m_multiHashThreadMask = Mem::ThreadBitSet(static_cast(options->multiHashThreadMask())); + if (hugePages) { + m_scratchpad = static_cast(allocateLargePagesMemory(m_size)); + if (m_scratchpad) { + m_flags |= HUGEPAGES; - if (m_useHugePages && TrySetLockPagesPrivilege()) { - m_flags |= HugepagesAvailable; - } -} - -void Mem::allocate(ScratchPadMem& scratchPadMem, bool useHugePages) -{ - scratchPadMem.hugePages = 0; - - if (!useHugePages) { - scratchPadMem.memory = static_cast(_mm_malloc(scratchPadMem.size, 4096)); - return; + return; + } } - scratchPadMem.size = std::max(scratchPadMem.size + scratchPadMem.size % MEMORY, static_cast(MEMORY)); - - scratchPadMem.memory = static_cast(VirtualAlloc(nullptr, scratchPadMem.size, MEM_COMMIT | MEM_RESERVE | MEM_LARGE_PAGES, PAGE_READWRITE)); - if (scratchPadMem.memory) { - scratchPadMem.hugePages = scratchPadMem.pages; - - m_flags |= HugepagesEnabled; - - return; - } - - allocate(scratchPadMem, false); + m_scratchpad = static_cast(_mm_malloc(m_size, align)); } -void Mem::release(ScratchPadMem &scratchPadMem) +xmrig::VirtualMemory::~VirtualMemory() { - if (scratchPadMem.hugePages) { - VirtualFree(scratchPadMem.memory, 0, MEM_RELEASE); + if (!m_scratchpad) { + return; + } + + if (isHugePages()) { + freeLargePagesMemory(m_scratchpad, m_size); } else { - _mm_free(scratchPadMem.memory); + _mm_free(m_scratchpad); } } -void *Mem::allocateExecutableMemory(size_t size) + +void *xmrig::VirtualMemory::allocateExecutableMemory(size_t size) { - return VirtualAlloc(0, size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); + return VirtualAlloc(nullptr, size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); } -void Mem::flushInstructionCache(void *p, size_t size) + +void *xmrig::VirtualMemory::allocateLargePagesMemory(size_t size) +{ + const size_t min = GetLargePageMinimum(); + void *mem = nullptr; + + if (min > 0) { + mem = VirtualAlloc(nullptr, align(size, min), MEM_COMMIT | MEM_RESERVE | MEM_LARGE_PAGES, PAGE_READWRITE); + } + + return mem; +} + + +void xmrig::VirtualMemory::flushInstructionCache(void *p, size_t size) { ::FlushInstructionCache(GetCurrentProcess(), p, size); -} \ No newline at end of file +} + + +void xmrig::VirtualMemory::freeLargePagesMemory(void *p, size_t) +{ + VirtualFree(p, 0, MEM_RELEASE); +} + + +void xmrig::VirtualMemory::init(bool hugePages) +{ + if (!hugePages) { + return; + } + + m_globalFlags = HUGEPAGES; + + if (TrySetLockPagesPrivilege()) { + m_globalFlags |= HUGEPAGES_AVAILABLE; + } +} + + +void xmrig::VirtualMemory::protectExecutableMemory(void *p, size_t size) +{ + DWORD oldProtect; + VirtualProtect(p, size, PAGE_EXECUTE_READ, &oldProtect); +} + + +void xmrig::VirtualMemory::unprotectExecutableMemory(void *p, size_t size) +{ + DWORD oldProtect; + VirtualProtect(p, size, PAGE_EXECUTE_READWRITE, &oldProtect); +} diff --git a/src/crypto/c_keccak.c b/src/crypto/common/keccak.cpp similarity index 74% rename from src/crypto/c_keccak.c rename to src/crypto/common/keccak.cpp index 997db241..132ae0a8 100644 --- a/src/crypto/c_keccak.c +++ b/src/crypto/common/keccak.cpp @@ -1,10 +1,35 @@ -// keccak.c -// 19-Nov-11 Markku-Juhani O. Saarinen -// A baseline Keccak (3rd round) implementation. +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2011 Markku-Juhani O. Saarinen + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2016-2018 XMRig , + * + * 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 . + */ + #include #include + +#include "crypto/common/keccak.h" + + #define HASH_DATA_AREA 136 #define KECCAK_ROUNDS 24 @@ -26,7 +51,7 @@ const uint64_t keccakf_rndc[24] = // update the state with given number of rounds -void keccakf(uint64_t st[25], int rounds) +void xmrig::keccakf(uint64_t st[25], int rounds) { int i, j, round; uint64_t t, bc[5]; @@ -139,7 +164,8 @@ void keccakf(uint64_t st[25], int rounds) // compute a keccak hash (md) of given byte length from "in" typedef uint64_t state_t[25]; -void keccak(const uint8_t *in, int inlen, uint8_t *md, int mdlen) + +void xmrig::keccak(const uint8_t *in, int inlen, uint8_t *md, int mdlen) { state_t st; uint8_t temp[144]; @@ -151,26 +177,24 @@ void keccak(const uint8_t *in, int inlen, uint8_t *md, int mdlen) memset(st, 0, sizeof(st)); for ( ; inlen >= rsiz; inlen -= rsiz, in += rsiz) { - for (i = 0; i < rsizw; i++) + for (i = 0; i < rsizw; i++) { st[i] ^= ((uint64_t *) in)[i]; - keccakf(st, KECCAK_ROUNDS); + } + + xmrig::keccakf(st, KECCAK_ROUNDS); } - + // last block and padding memcpy(temp, in, inlen); temp[inlen++] = 1; memset(temp + inlen, 0, rsiz - inlen); temp[rsiz - 1] |= 0x80; - for (i = 0; i < rsizw; i++) + for (i = 0; i < rsizw; i++) { st[i] ^= ((uint64_t *) temp)[i]; + } keccakf(st, KECCAK_ROUNDS); memcpy(md, st, mdlen); } - -void keccak1600(const uint8_t *in, int inlen, uint8_t *md) -{ - keccak(in, inlen, md, sizeof(state_t)); -} diff --git a/src/crypto/common/keccak.h b/src/crypto/common/keccak.h new file mode 100644 index 00000000..6121044a --- /dev/null +++ b/src/crypto/common/keccak.h @@ -0,0 +1,55 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2011 Markku-Juhani O. Saarinen + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2016-2018 XMRig , + * + * 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 . + */ + + +#ifndef XMRIG_KECCAK_H +#define XMRIG_KECCAK_H + +#include +#include + + +namespace xmrig { + +// compute a keccak hash (md) of given byte length from "in" +void keccak(const uint8_t *in, int inlen, uint8_t *md, int mdlen); + + +inline void keccak(const uint8_t *in, size_t inlen, uint8_t *md) +{ + keccak(in, static_cast(inlen), md, 200); +} + + +inline void keccak(const char *in, size_t inlen, uint8_t *md) +{ + keccak(reinterpret_cast(in), static_cast(inlen), md, 200); +} + +// update the state +void keccakf(uint64_t st[25], int norounds); + +} /* namespace xmrig */ + +#endif /* XMRIG_KECCAK_H */ diff --git a/src/3rdparty/aligned_malloc.h b/src/crypto/common/portable/mm_malloc.h similarity index 57% rename from src/3rdparty/aligned_malloc.h rename to src/crypto/common/portable/mm_malloc.h index 0b74b17e..34ca7d48 100644 --- a/src/3rdparty/aligned_malloc.h +++ b/src/crypto/common/portable/mm_malloc.h @@ -4,8 +4,9 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , * * 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 @@ -21,45 +22,50 @@ * along with this program. If not, see . */ -#ifndef __ALIGNED_MALLOC_H__ -#define __ALIGNED_MALLOC_H__ +#ifndef XMRIG_MM_MALLOC_PORTABLE_H +#define XMRIG_MM_MALLOC_PORTABLE_H +#if defined(XMRIG_ARM) && !defined(__clang__) #include #ifndef __cplusplus -extern int posix_memalign(void **__memptr, size_t __alignment, size_t __size); +extern #else -// Some systems (e.g. those with GNU libc) declare posix_memalign with an -// exception specifier. Via an "egregious workaround" in -// Sema::CheckEquivalentExceptionSpec, Clang accepts the following as a valid -// redeclaration of glibc's declaration. -extern "C" int posix_memalign(void **__memptr, size_t __alignment, size_t __size); +extern "C" #endif +int posix_memalign(void **__memptr, size_t __alignment, size_t __size); static __inline__ void *__attribute__((__always_inline__, __malloc__)) _mm_malloc(size_t __size, size_t __align) { - if (__align == 1) { - return malloc(__size); - } + if (__align == 1) { + return malloc(__size); + } - if (!(__align & (__align - 1)) && __align < sizeof(void *)) - __align = sizeof(void *); + if (!(__align & (__align - 1)) && __align < sizeof(void *)) { + __align = sizeof(void *); + } - void *__mallocedMemory; - if (posix_memalign(&__mallocedMemory, __align, __size)) { - return 0; - } + void *__mallocedMemory; + if (posix_memalign(&__mallocedMemory, __align, __size)) { + return nullptr; + } - return __mallocedMemory; + return __mallocedMemory; } static __inline__ void __attribute__((__always_inline__)) _mm_free(void *__p) { - free(__p); + free(__p); } +#elif defined(_WIN32) && !defined(__GNUC__) +# include +#else +# include +#endif -#endif /* __ALIGNED_MALLOC_H__ */ + +#endif /* XMRIG_MM_MALLOC_PORTABLE_H */ diff --git a/src/crypto/randomx/aes_hash.cpp b/src/crypto/randomx/aes_hash.cpp new file mode 100644 index 00000000..fe149dfe --- /dev/null +++ b/src/crypto/randomx/aes_hash.cpp @@ -0,0 +1,214 @@ +/* +Copyright (c) 2018-2019, tevador + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "crypto/randomx/soft_aes.h" +#include "crypto/randomx/randomx.h" + +#define AES_HASH_1R_STATE0 0xd7983aad, 0xcc82db47, 0x9fa856de, 0x92b52c0d +#define AES_HASH_1R_STATE1 0xace78057, 0xf59e125a, 0x15c7b798, 0x338d996e +#define AES_HASH_1R_STATE2 0xe8a07ce4, 0x5079506b, 0xae62c7d0, 0x6a770017 +#define AES_HASH_1R_STATE3 0x7e994948, 0x79a10005, 0x07ad828d, 0x630a240c + +#define AES_HASH_1R_XKEY0 0x06890201, 0x90dc56bf, 0x8b24949f, 0xf6fa8389 +#define AES_HASH_1R_XKEY1 0xed18f99b, 0xee1043c6, 0x51f4e03c, 0x61b263d1 + +/* + Calculate a 512-bit hash of 'input' using 4 lanes of AES. + The input is treated as a set of round keys for the encryption + of the initial state. + + 'inputSize' must be a multiple of 64. + + For a 2 MiB input, this has the same security as 32768-round + AES encryption. + + Hashing throughput: >20 GiB/s per CPU core with hardware AES +*/ +template +void hashAes1Rx4(const void *input, size_t inputSize, void *hash) { + const uint8_t* inptr = (uint8_t*)input; + const uint8_t* inputEnd = inptr + inputSize; + + rx_vec_i128 state0, state1, state2, state3; + rx_vec_i128 in0, in1, in2, in3; + + //intial state + state0 = rx_set_int_vec_i128(AES_HASH_1R_STATE0); + state1 = rx_set_int_vec_i128(AES_HASH_1R_STATE1); + state2 = rx_set_int_vec_i128(AES_HASH_1R_STATE2); + state3 = rx_set_int_vec_i128(AES_HASH_1R_STATE3); + + //process 64 bytes at a time in 4 lanes + while (inptr < inputEnd) { + in0 = rx_load_vec_i128((rx_vec_i128*)inptr + 0); + in1 = rx_load_vec_i128((rx_vec_i128*)inptr + 1); + in2 = rx_load_vec_i128((rx_vec_i128*)inptr + 2); + in3 = rx_load_vec_i128((rx_vec_i128*)inptr + 3); + + state0 = aesenc(state0, in0); + state1 = aesdec(state1, in1); + state2 = aesenc(state2, in2); + state3 = aesdec(state3, in3); + + inptr += 64; + } + + //two extra rounds to achieve full diffusion + rx_vec_i128 xkey0 = rx_set_int_vec_i128(AES_HASH_1R_XKEY0); + rx_vec_i128 xkey1 = rx_set_int_vec_i128(AES_HASH_1R_XKEY1); + + state0 = aesenc(state0, xkey0); + state1 = aesdec(state1, xkey0); + state2 = aesenc(state2, xkey0); + state3 = aesdec(state3, xkey0); + + state0 = aesenc(state0, xkey1); + state1 = aesdec(state1, xkey1); + state2 = aesenc(state2, xkey1); + state3 = aesdec(state3, xkey1); + + //output hash + rx_store_vec_i128((rx_vec_i128*)hash + 0, state0); + rx_store_vec_i128((rx_vec_i128*)hash + 1, state1); + rx_store_vec_i128((rx_vec_i128*)hash + 2, state2); + rx_store_vec_i128((rx_vec_i128*)hash + 3, state3); +} + +template void hashAes1Rx4(const void *input, size_t inputSize, void *hash); +template void hashAes1Rx4(const void *input, size_t inputSize, void *hash); + +#define AES_GEN_1R_KEY0 0xb4f44917, 0xdbb5552b, 0x62716609, 0x6daca553 +#define AES_GEN_1R_KEY1 0x0da1dc4e, 0x1725d378, 0x846a710d, 0x6d7caf07 +#define AES_GEN_1R_KEY2 0x3e20e345, 0xf4c0794f, 0x9f947ec6, 0x3f1262f1 +#define AES_GEN_1R_KEY3 0x49169154, 0x16314c88, 0xb1ba317c, 0x6aef8135 + +/* + Fill 'buffer' with pseudorandom data based on 512-bit 'state'. + The state is encrypted using a single AES round per 16 bytes of output + in 4 lanes. + + 'outputSize' must be a multiple of 64. + + The modified state is written back to 'state' to allow multiple + calls to this function. +*/ +template +void fillAes1Rx4(void *state, size_t outputSize, void *buffer) { + const uint8_t* outptr = (uint8_t*)buffer; + const uint8_t* outputEnd = outptr + outputSize; + + rx_vec_i128 state0, state1, state2, state3; + rx_vec_i128 key0, key1, key2, key3; + + key0 = rx_set_int_vec_i128(AES_GEN_1R_KEY0); + key1 = rx_set_int_vec_i128(AES_GEN_1R_KEY1); + key2 = rx_set_int_vec_i128(AES_GEN_1R_KEY2); + key3 = rx_set_int_vec_i128(AES_GEN_1R_KEY3); + + state0 = rx_load_vec_i128((rx_vec_i128*)state + 0); + state1 = rx_load_vec_i128((rx_vec_i128*)state + 1); + state2 = rx_load_vec_i128((rx_vec_i128*)state + 2); + state3 = rx_load_vec_i128((rx_vec_i128*)state + 3); + + while (outptr < outputEnd) { + state0 = aesdec(state0, key0); + state1 = aesenc(state1, key1); + state2 = aesdec(state2, key2); + state3 = aesenc(state3, key3); + + rx_store_vec_i128((rx_vec_i128*)outptr + 0, state0); + rx_store_vec_i128((rx_vec_i128*)outptr + 1, state1); + rx_store_vec_i128((rx_vec_i128*)outptr + 2, state2); + rx_store_vec_i128((rx_vec_i128*)outptr + 3, state3); + + outptr += 64; + } + + rx_store_vec_i128((rx_vec_i128*)state + 0, state0); + rx_store_vec_i128((rx_vec_i128*)state + 1, state1); + rx_store_vec_i128((rx_vec_i128*)state + 2, state2); + rx_store_vec_i128((rx_vec_i128*)state + 3, state3); +} + +template void fillAes1Rx4(void *state, size_t outputSize, void *buffer); +template void fillAes1Rx4(void *state, size_t outputSize, void *buffer); + +template +void fillAes4Rx4(void *state, size_t outputSize, void *buffer) { + const uint8_t* outptr = (uint8_t*)buffer; + const uint8_t* outputEnd = outptr + outputSize; + + rx_vec_i128 state0, state1, state2, state3; + rx_vec_i128 key0, key1, key2, key3, key4, key5, key6, key7; + + key0 = RandomX_CurrentConfig.fillAes4Rx4_Key[0]; + key1 = RandomX_CurrentConfig.fillAes4Rx4_Key[1]; + key2 = RandomX_CurrentConfig.fillAes4Rx4_Key[2]; + key3 = RandomX_CurrentConfig.fillAes4Rx4_Key[3]; + key4 = RandomX_CurrentConfig.fillAes4Rx4_Key[4]; + key5 = RandomX_CurrentConfig.fillAes4Rx4_Key[5]; + key6 = RandomX_CurrentConfig.fillAes4Rx4_Key[6]; + key7 = RandomX_CurrentConfig.fillAes4Rx4_Key[7]; + + state0 = rx_load_vec_i128((rx_vec_i128*)state + 0); + state1 = rx_load_vec_i128((rx_vec_i128*)state + 1); + state2 = rx_load_vec_i128((rx_vec_i128*)state + 2); + state3 = rx_load_vec_i128((rx_vec_i128*)state + 3); + + while (outptr < outputEnd) { + state0 = aesdec(state0, key0); + state1 = aesenc(state1, key0); + state2 = aesdec(state2, key4); + state3 = aesenc(state3, key4); + + state0 = aesdec(state0, key1); + state1 = aesenc(state1, key1); + state2 = aesdec(state2, key5); + state3 = aesenc(state3, key5); + + state0 = aesdec(state0, key2); + state1 = aesenc(state1, key2); + state2 = aesdec(state2, key6); + state3 = aesenc(state3, key6); + + state0 = aesdec(state0, key3); + state1 = aesenc(state1, key3); + state2 = aesdec(state2, key7); + state3 = aesenc(state3, key7); + + rx_store_vec_i128((rx_vec_i128*)outptr + 0, state0); + rx_store_vec_i128((rx_vec_i128*)outptr + 1, state1); + rx_store_vec_i128((rx_vec_i128*)outptr + 2, state2); + rx_store_vec_i128((rx_vec_i128*)outptr + 3, state3); + + outptr += 64; + } +} + +template void fillAes4Rx4(void *state, size_t outputSize, void *buffer); +template void fillAes4Rx4(void *state, size_t outputSize, void *buffer); diff --git a/src/crypto/randomx/aes_hash.hpp b/src/crypto/randomx/aes_hash.hpp new file mode 100644 index 00000000..b4d0e940 --- /dev/null +++ b/src/crypto/randomx/aes_hash.hpp @@ -0,0 +1,40 @@ +/* +Copyright (c) 2018-2019, tevador + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#pragma once + +#include + +template +void hashAes1Rx4(const void *input, size_t inputSize, void *hash); + +template +void fillAes1Rx4(void *state, size_t outputSize, void *buffer); + +template +void fillAes4Rx4(void *state, size_t outputSize, void *buffer); diff --git a/src/crypto/randomx/allocator.cpp b/src/crypto/randomx/allocator.cpp new file mode 100644 index 00000000..60fb8056 --- /dev/null +++ b/src/crypto/randomx/allocator.cpp @@ -0,0 +1,60 @@ +/* +Copyright (c) 2018-2019, tevador + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include "crypto/randomx/allocator.hpp" +#include "crypto/randomx/intrin_portable.h" +#include "crypto/randomx/virtual_memory.hpp" +#include "crypto/randomx/common.hpp" + +namespace randomx { + + template + void* AlignedAllocator::allocMemory(size_t count) { + void *mem = rx_aligned_alloc(count, alignment); + if (mem == nullptr) + throw std::bad_alloc(); + return mem; + } + + template + void AlignedAllocator::freeMemory(void* ptr, size_t count) { + rx_aligned_free(ptr); + } + + template class AlignedAllocator; + + void* LargePageAllocator::allocMemory(size_t count) { + return allocLargePagesMemory(count); + } + + void LargePageAllocator::freeMemory(void* ptr, size_t count) { + freePagedMemory(ptr, count); + }; + +} diff --git a/src/crypto/randomx/allocator.hpp b/src/crypto/randomx/allocator.hpp new file mode 100644 index 00000000..d7aa3f95 --- /dev/null +++ b/src/crypto/randomx/allocator.hpp @@ -0,0 +1,46 @@ +/* +Copyright (c) 2018-2019, tevador + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#pragma once + +#include + +namespace randomx { + + template + struct AlignedAllocator { + static void* allocMemory(size_t); + static void freeMemory(void*, size_t); + }; + + struct LargePageAllocator { + static void* allocMemory(size_t); + static void freeMemory(void*, size_t); + }; + +} \ No newline at end of file diff --git a/src/crypto/randomx/argon2.h b/src/crypto/randomx/argon2.h new file mode 100644 index 00000000..9d427159 --- /dev/null +++ b/src/crypto/randomx/argon2.h @@ -0,0 +1,229 @@ +/* +Copyright (c) 2018-2019, tevador + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* Original code from Argon2 reference source code package used under CC0 Licence + * https://github.com/P-H-C/phc-winner-argon2 + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves +*/ + +#pragma once + +#include +#include +#include + +/* + * Argon2 input parameter restrictions + */ + + /* Minimum and maximum number of lanes (degree of parallelism) */ +#define ARGON2_MIN_LANES UINT32_C(1) +#define ARGON2_MAX_LANES UINT32_C(0xFFFFFF) + +/* Minimum and maximum number of threads */ +#define ARGON2_MIN_THREADS UINT32_C(1) +#define ARGON2_MAX_THREADS UINT32_C(0xFFFFFF) + +/* Number of synchronization points between lanes per pass */ +#define ARGON2_SYNC_POINTS UINT32_C(4) + +/* Minimum and maximum digest size in bytes */ +#define ARGON2_MIN_OUTLEN UINT32_C(4) +#define ARGON2_MAX_OUTLEN UINT32_C(0xFFFFFFFF) + +/* Minimum and maximum number of memory blocks (each of BLOCK_SIZE bytes) */ +#define ARGON2_MIN_MEMORY (2 * ARGON2_SYNC_POINTS) /* 2 blocks per slice */ + +#define ARGON2_MIN(a, b) ((a) < (b) ? (a) : (b)) +/* Max memory size is addressing-space/2, topping at 2^32 blocks (4 TB) */ +#define ARGON2_MAX_MEMORY_BITS \ + ARGON2_MIN(UINT32_C(32), (sizeof(void *) * CHAR_BIT - 10 - 1)) +#define ARGON2_MAX_MEMORY \ + ARGON2_MIN(UINT32_C(0xFFFFFFFF), UINT64_C(1) << ARGON2_MAX_MEMORY_BITS) + +/* Minimum and maximum number of passes */ +#define ARGON2_MIN_TIME UINT32_C(1) +#define ARGON2_MAX_TIME UINT32_C(0xFFFFFFFF) + +/* Minimum and maximum password length in bytes */ +#define ARGON2_MIN_PWD_LENGTH UINT32_C(0) +#define ARGON2_MAX_PWD_LENGTH UINT32_C(0xFFFFFFFF) + +/* Minimum and maximum associated data length in bytes */ +#define ARGON2_MIN_AD_LENGTH UINT32_C(0) +#define ARGON2_MAX_AD_LENGTH UINT32_C(0xFFFFFFFF) + +/* Minimum and maximum salt length in bytes */ +#define ARGON2_MIN_SALT_LENGTH UINT32_C(8) +#define ARGON2_MAX_SALT_LENGTH UINT32_C(0xFFFFFFFF) + +/* Minimum and maximum key length in bytes */ +#define ARGON2_MIN_SECRET UINT32_C(0) +#define ARGON2_MAX_SECRET UINT32_C(0xFFFFFFFF) + +/* Flags to determine which fields are securely wiped (default = no wipe). */ +#define ARGON2_DEFAULT_FLAGS UINT32_C(0) +#define ARGON2_FLAG_CLEAR_PASSWORD (UINT32_C(1) << 0) +#define ARGON2_FLAG_CLEAR_SECRET (UINT32_C(1) << 1) + + +/* Error codes */ +typedef enum Argon2_ErrorCodes { + ARGON2_OK = 0, + + ARGON2_OUTPUT_PTR_NULL = -1, + + ARGON2_OUTPUT_TOO_SHORT = -2, + ARGON2_OUTPUT_TOO_LONG = -3, + + ARGON2_PWD_TOO_SHORT = -4, + ARGON2_PWD_TOO_LONG = -5, + + ARGON2_SALT_TOO_SHORT = -6, + ARGON2_SALT_TOO_LONG = -7, + + ARGON2_AD_TOO_SHORT = -8, + ARGON2_AD_TOO_LONG = -9, + + ARGON2_SECRET_TOO_SHORT = -10, + ARGON2_SECRET_TOO_LONG = -11, + + ARGON2_TIME_TOO_SMALL = -12, + ARGON2_TIME_TOO_LARGE = -13, + + ARGON2_MEMORY_TOO_LITTLE = -14, + ARGON2_MEMORY_TOO_MUCH = -15, + + ARGON2_LANES_TOO_FEW = -16, + ARGON2_LANES_TOO_MANY = -17, + + ARGON2_PWD_PTR_MISMATCH = -18, /* NULL ptr with non-zero length */ + ARGON2_SALT_PTR_MISMATCH = -19, /* NULL ptr with non-zero length */ + ARGON2_SECRET_PTR_MISMATCH = -20, /* NULL ptr with non-zero length */ + ARGON2_AD_PTR_MISMATCH = -21, /* NULL ptr with non-zero length */ + + ARGON2_MEMORY_ALLOCATION_ERROR = -22, + + ARGON2_FREE_MEMORY_CBK_NULL = -23, + ARGON2_ALLOCATE_MEMORY_CBK_NULL = -24, + + ARGON2_INCORRECT_PARAMETER = -25, + ARGON2_INCORRECT_TYPE = -26, + + ARGON2_OUT_PTR_MISMATCH = -27, + + ARGON2_THREADS_TOO_FEW = -28, + ARGON2_THREADS_TOO_MANY = -29, + + ARGON2_MISSING_ARGS = -30, + + ARGON2_ENCODING_FAIL = -31, + + ARGON2_DECODING_FAIL = -32, + + ARGON2_THREAD_FAIL = -33, + + ARGON2_DECODING_LENGTH_FAIL = -34, + + ARGON2_VERIFY_MISMATCH = -35 +} argon2_error_codes; + +/* Memory allocator types --- for external allocation */ +typedef int(*allocate_fptr)(uint8_t **memory, size_t bytes_to_allocate); +typedef void(*deallocate_fptr)(uint8_t *memory, size_t bytes_to_allocate); + +/* Argon2 external data structures */ + +/* + ***** + * Context: structure to hold Argon2 inputs: + * output array and its length, + * password and its length, + * salt and its length, + * secret and its length, + * associated data and its length, + * number of passes, amount of used memory (in KBytes, can be rounded up a bit) + * number of parallel threads that will be run. + * All the parameters above affect the output hash value. + * Additionally, two function pointers can be provided to allocate and + * deallocate the memory (if NULL, memory will be allocated internally). + * Also, three flags indicate whether to erase password, secret as soon as they + * are pre-hashed (and thus not needed anymore), and the entire memory + ***** + * Simplest situation: you have output array out[8], password is stored in + * pwd[32], salt is stored in salt[16], you do not have keys nor associated + * data. You need to spend 1 GB of RAM and you run 5 passes of Argon2d with + * 4 parallel lanes. + * You want to erase the password, but you're OK with last pass not being + * erased. You want to use the default memory allocator. + * Then you initialize: + Argon2_Context(out,8,pwd,32,salt,16,NULL,0,NULL,0,5,1<<20,4,4,NULL,NULL,true,false,false,false) + */ +typedef struct Argon2_Context { + uint8_t *out; /* output array */ + uint32_t outlen; /* digest length */ + + uint8_t *pwd; /* password array */ + uint32_t pwdlen; /* password length */ + + uint8_t *salt; /* salt array */ + uint32_t saltlen; /* salt length */ + + uint8_t *secret; /* key array */ + uint32_t secretlen; /* key length */ + + uint8_t *ad; /* associated data array */ + uint32_t adlen; /* associated data length */ + + uint32_t t_cost; /* number of passes */ + uint32_t m_cost; /* amount of memory requested (KB) */ + uint32_t lanes; /* number of lanes */ + uint32_t threads; /* maximum number of threads */ + + uint32_t version; /* version number */ + + allocate_fptr allocate_cbk; /* pointer to memory allocator */ + deallocate_fptr free_cbk; /* pointer to memory deallocator */ + + uint32_t flags; /* array of bool options */ +} argon2_context; + +/* Argon2 primitive type */ +typedef enum Argon2_type { + Argon2_d = 0, + Argon2_i = 1, + Argon2_id = 2 +} argon2_type; + +/* Version of the algorithm */ +typedef enum Argon2_version { + ARGON2_VERSION_10 = 0x10, + ARGON2_VERSION_13 = 0x13, + ARGON2_VERSION_NUMBER = ARGON2_VERSION_13 +} argon2_version; diff --git a/src/crypto/randomx/argon2_core.c b/src/crypto/randomx/argon2_core.c new file mode 100644 index 00000000..97176608 --- /dev/null +++ b/src/crypto/randomx/argon2_core.c @@ -0,0 +1,502 @@ +/* +Copyright (c) 2018-2019, tevador + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* Original code from Argon2 reference source code package used under CC0 Licence + * https://github.com/P-H-C/phc-winner-argon2 + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves +*/ + + /*For memory wiping*/ +#ifdef _MSC_VER +#include +#include /* For SecureZeroMemory */ +#endif +#if defined __STDC_LIB_EXT1__ +#define __STDC_WANT_LIB_EXT1__ 1 +#endif +#define VC_GE_2005(version) (version >= 1400) + +#include +#include +#include + +#include "crypto/randomx/argon2_core.h" +#include "crypto/randomx/blake2/blake2.h" +#include "crypto/randomx/blake2/blake2-impl.h" + +#ifdef GENKAT +#include "genkat.h" +#endif + +#if defined(__clang__) +#if __has_attribute(optnone) +#define NOT_OPTIMIZED __attribute__((optnone)) +#endif +#elif defined(__GNUC__) +#define GCC_VERSION \ + (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) +#if GCC_VERSION >= 40400 +#define NOT_OPTIMIZED __attribute__((optimize("O0"))) +#endif +#endif +#ifndef NOT_OPTIMIZED +#define NOT_OPTIMIZED +#endif + +/***************Instance and Position constructors**********/ +void rxa2_init_block_value(block *b, uint8_t in) { memset(b->v, in, sizeof(b->v)); } + +void rxa2_copy_block(block *dst, const block *src) { + memcpy(dst->v, src->v, sizeof(uint64_t) * ARGON2_QWORDS_IN_BLOCK); +} + +void rxa2_xor_block(block *dst, const block *src) { + int i; + for (i = 0; i < ARGON2_QWORDS_IN_BLOCK; ++i) { + dst->v[i] ^= src->v[i]; + } +} + +static void load_block(block *dst, const void *input) { + unsigned i; + for (i = 0; i < ARGON2_QWORDS_IN_BLOCK; ++i) { + dst->v[i] = load64((const uint8_t *)input + i * sizeof(dst->v[i])); + } +} + +//static void store_block(void *output, const block *src) { +// unsigned i; +// for (i = 0; i < ARGON2_QWORDS_IN_BLOCK; ++i) { +// store64((uint8_t *)output + i * sizeof(src->v[i]), src->v[i]); +// } +//} + +/***************Memory functions*****************/ + +int rxa2_allocate_memory(const argon2_context *context, uint8_t **memory, + size_t num, size_t size) { + size_t memory_size = num * size; + if (memory == NULL) { + return ARGON2_MEMORY_ALLOCATION_ERROR; + } + + /* 1. Check for multiplication overflow */ + if (size != 0 && memory_size / size != num) { + return ARGON2_MEMORY_ALLOCATION_ERROR; + } + + /* 2. Try to allocate with appropriate allocator */ + if (context->allocate_cbk) { + (context->allocate_cbk)(memory, memory_size); + } + else { + *memory = (uint8_t*)malloc(memory_size); + } + + if (*memory == NULL) { + return ARGON2_MEMORY_ALLOCATION_ERROR; + } + + return ARGON2_OK; +} + +void rxa2_free_memory(const argon2_context *context, uint8_t *memory, + size_t num, size_t size) { + size_t memory_size = num * size; + rxa2_clear_internal_memory(memory, memory_size); + if (context->free_cbk) { + (context->free_cbk)(memory, memory_size); + } + else { + free(memory); + } +} + +void NOT_OPTIMIZED rxa2_secure_wipe_memory(void *v, size_t n) { +#if defined(_MSC_VER) && VC_GE_2005(_MSC_VER) + SecureZeroMemory(v, n); +#elif defined memset_s + memset_s(v, n, 0, n); +#elif defined(__OpenBSD__) + explicit_bzero(v, n); +#else + static void *(*const volatile memset_sec)(void *, int, size_t) = &memset; + memset_sec(v, 0, n); +#endif +} + +/* Memory clear flag defaults to true. */ +#define FLAG_clear_internal_memory 0 +void rxa2_clear_internal_memory(void *v, size_t n) { + if (FLAG_clear_internal_memory && v) { + rxa2_secure_wipe_memory(v, n); + } +} + +uint32_t rxa2_index_alpha(const argon2_instance_t *instance, + const argon2_position_t *position, uint32_t pseudo_rand, + int same_lane) { + /* + * Pass 0: + * This lane : all already finished segments plus already constructed + * blocks in this segment + * Other lanes : all already finished segments + * Pass 1+: + * This lane : (SYNC_POINTS - 1) last segments plus already constructed + * blocks in this segment + * Other lanes : (SYNC_POINTS - 1) last segments + */ + uint32_t reference_area_size; + uint64_t relative_position; + uint32_t start_position, absolute_position; + + if (0 == position->pass) { + /* First pass */ + if (0 == position->slice) { + /* First slice */ + reference_area_size = + position->index - 1; /* all but the previous */ + } + else { + if (same_lane) { + /* The same lane => add current segment */ + reference_area_size = + position->slice * instance->segment_length + + position->index - 1; + } + else { + reference_area_size = + position->slice * instance->segment_length + + ((position->index == 0) ? (-1) : 0); + } + } + } + else { + /* Second pass */ + if (same_lane) { + reference_area_size = instance->lane_length - + instance->segment_length + position->index - + 1; + } + else { + reference_area_size = instance->lane_length - + instance->segment_length + + ((position->index == 0) ? (-1) : 0); + } + } + + /* 1.2.4. Mapping pseudo_rand to 0.. and produce + * relative position */ + relative_position = pseudo_rand; + relative_position = relative_position * relative_position >> 32; + relative_position = reference_area_size - 1 - + (reference_area_size * relative_position >> 32); + + /* 1.2.5 Computing starting position */ + start_position = 0; + + if (0 != position->pass) { + start_position = (position->slice == ARGON2_SYNC_POINTS - 1) + ? 0 + : (position->slice + 1) * instance->segment_length; + } + + /* 1.2.6. Computing absolute position */ + absolute_position = (start_position + relative_position) % + instance->lane_length; /* absolute position */ + return absolute_position; +} + +/* Single-threaded version for p=1 case */ +static int fill_memory_blocks_st(argon2_instance_t *instance) { + uint32_t r, s, l; + + for (r = 0; r < instance->passes; ++r) { + for (s = 0; s < ARGON2_SYNC_POINTS; ++s) { + for (l = 0; l < instance->lanes; ++l) { + argon2_position_t position = { r, l, (uint8_t)s, 0 }; + rxa2_fill_segment(instance, position); + } + } +#ifdef GENKAT + internal_kat(instance, r); /* Print all memory blocks */ +#endif + } + return ARGON2_OK; +} + +int rxa2_fill_memory_blocks(argon2_instance_t *instance) { + if (instance == NULL || instance->lanes == 0) { + return ARGON2_INCORRECT_PARAMETER; + } + return fill_memory_blocks_st(instance); +} + +int rxa2_validate_inputs(const argon2_context *context) { + if (NULL == context) { + return ARGON2_INCORRECT_PARAMETER; + } + + /* Validate password (required param) */ + if (NULL == context->pwd) { + if (0 != context->pwdlen) { + return ARGON2_PWD_PTR_MISMATCH; + } + } + + if (ARGON2_MIN_PWD_LENGTH > context->pwdlen) { + return ARGON2_PWD_TOO_SHORT; + } + + if (ARGON2_MAX_PWD_LENGTH < context->pwdlen) { + return ARGON2_PWD_TOO_LONG; + } + + /* Validate salt (required param) */ + if (NULL == context->salt) { + if (0 != context->saltlen) { + return ARGON2_SALT_PTR_MISMATCH; + } + } + + if (ARGON2_MIN_SALT_LENGTH > context->saltlen) { + return ARGON2_SALT_TOO_SHORT; + } + + if (ARGON2_MAX_SALT_LENGTH < context->saltlen) { + return ARGON2_SALT_TOO_LONG; + } + + /* Validate secret (optional param) */ + if (NULL == context->secret) { + if (0 != context->secretlen) { + return ARGON2_SECRET_PTR_MISMATCH; + } + } + else { + if (ARGON2_MIN_SECRET > context->secretlen) { + return ARGON2_SECRET_TOO_SHORT; + } + if (ARGON2_MAX_SECRET < context->secretlen) { + return ARGON2_SECRET_TOO_LONG; + } + } + + /* Validate associated data (optional param) */ + if (NULL == context->ad) { + if (0 != context->adlen) { + return ARGON2_AD_PTR_MISMATCH; + } + } + else { + if (ARGON2_MIN_AD_LENGTH > context->adlen) { + return ARGON2_AD_TOO_SHORT; + } + if (ARGON2_MAX_AD_LENGTH < context->adlen) { + return ARGON2_AD_TOO_LONG; + } + } + + /* Validate memory cost */ + if (ARGON2_MIN_MEMORY > context->m_cost) { + return ARGON2_MEMORY_TOO_LITTLE; + } + + if (ARGON2_MAX_MEMORY < context->m_cost) { + return ARGON2_MEMORY_TOO_MUCH; + } + + if (context->m_cost < 8 * context->lanes) { + return ARGON2_MEMORY_TOO_LITTLE; + } + + /* Validate time cost */ + if (ARGON2_MIN_TIME > context->t_cost) { + return ARGON2_TIME_TOO_SMALL; + } + + if (ARGON2_MAX_TIME < context->t_cost) { + return ARGON2_TIME_TOO_LARGE; + } + + /* Validate lanes */ + if (ARGON2_MIN_LANES > context->lanes) { + return ARGON2_LANES_TOO_FEW; + } + + if (ARGON2_MAX_LANES < context->lanes) { + return ARGON2_LANES_TOO_MANY; + } + + /* Validate threads */ + if (ARGON2_MIN_THREADS > context->threads) { + return ARGON2_THREADS_TOO_FEW; + } + + if (ARGON2_MAX_THREADS < context->threads) { + return ARGON2_THREADS_TOO_MANY; + } + + if (NULL != context->allocate_cbk && NULL == context->free_cbk) { + return ARGON2_FREE_MEMORY_CBK_NULL; + } + + if (NULL == context->allocate_cbk && NULL != context->free_cbk) { + return ARGON2_ALLOCATE_MEMORY_CBK_NULL; + } + + return ARGON2_OK; +} + +void rxa2_fill_first_blocks(uint8_t *blockhash, const argon2_instance_t *instance) { + uint32_t l; + /* Make the first and second block in each lane as G(H0||0||i) or + G(H0||1||i) */ + uint8_t blockhash_bytes[ARGON2_BLOCK_SIZE]; + for (l = 0; l < instance->lanes; ++l) { + + store32(blockhash + ARGON2_PREHASH_DIGEST_LENGTH, 0); + store32(blockhash + ARGON2_PREHASH_DIGEST_LENGTH + 4, l); + rxa2_blake2b_long(blockhash_bytes, ARGON2_BLOCK_SIZE, blockhash, + ARGON2_PREHASH_SEED_LENGTH); + load_block(&instance->memory[l * instance->lane_length + 0], + blockhash_bytes); + + store32(blockhash + ARGON2_PREHASH_DIGEST_LENGTH, 1); + rxa2_blake2b_long(blockhash_bytes, ARGON2_BLOCK_SIZE, blockhash, + ARGON2_PREHASH_SEED_LENGTH); + load_block(&instance->memory[l * instance->lane_length + 1], + blockhash_bytes); + } + rxa2_clear_internal_memory(blockhash_bytes, ARGON2_BLOCK_SIZE); +} + +void rxa2_initial_hash(uint8_t *blockhash, argon2_context *context, argon2_type type) { + blake2b_state BlakeHash; + uint8_t value[sizeof(uint32_t)]; + + if (NULL == context || NULL == blockhash) { + return; + } + + rx_blake2b_init(&BlakeHash, ARGON2_PREHASH_DIGEST_LENGTH); + + store32(&value, context->lanes); + rx_blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); + + store32(&value, context->outlen); + rx_blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); + + store32(&value, context->m_cost); + rx_blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); + + store32(&value, context->t_cost); + rx_blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); + + store32(&value, context->version); + rx_blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); + + store32(&value, (uint32_t)type); + rx_blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); + + store32(&value, context->pwdlen); + rx_blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); + + if (context->pwd != NULL) { + rx_blake2b_update(&BlakeHash, (const uint8_t *)context->pwd, + context->pwdlen); + + if (context->flags & ARGON2_FLAG_CLEAR_PASSWORD) { + rxa2_secure_wipe_memory(context->pwd, context->pwdlen); + context->pwdlen = 0; + } + } + + store32(&value, context->saltlen); + rx_blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); + + if (context->salt != NULL) { + rx_blake2b_update(&BlakeHash, (const uint8_t *)context->salt, context->saltlen); + } + + store32(&value, context->secretlen); + rx_blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); + + if (context->secret != NULL) { + rx_blake2b_update(&BlakeHash, (const uint8_t *)context->secret, + context->secretlen); + + if (context->flags & ARGON2_FLAG_CLEAR_SECRET) { + rxa2_secure_wipe_memory(context->secret, context->secretlen); + context->secretlen = 0; + } + } + + store32(&value, context->adlen); + rx_blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); + + if (context->ad != NULL) { + rx_blake2b_update(&BlakeHash, (const uint8_t *)context->ad, + context->adlen); + } + + rx_blake2b_final(&BlakeHash, blockhash, ARGON2_PREHASH_DIGEST_LENGTH); +} + +int rxa2_argon_initialize(argon2_instance_t *instance, argon2_context *context) { + uint8_t blockhash[ARGON2_PREHASH_SEED_LENGTH]; + + if (instance == NULL || context == NULL) + return ARGON2_INCORRECT_PARAMETER; + instance->context_ptr = context; + + /* 1. Memory allocation */ + /*result = allocate_memory(context, (uint8_t **)&(instance->memory), instance->memory_blocks, sizeof(block)); + if (result != ARGON2_OK) { + return result; + }*/ + + /* 2. Initial hashing */ + /* H_0 + 8 extra bytes to produce the first blocks */ + /* uint8_t blockhash[ARGON2_PREHASH_SEED_LENGTH]; */ + /* Hashing all inputs */ + rxa2_initial_hash(blockhash, context, instance->type); + /* Zeroing 8 extra bytes */ + rxa2_clear_internal_memory(blockhash + ARGON2_PREHASH_DIGEST_LENGTH, + ARGON2_PREHASH_SEED_LENGTH - + ARGON2_PREHASH_DIGEST_LENGTH); + + /* 3. Creating first blocks, we always have at least two blocks in a slice + */ + rxa2_fill_first_blocks(blockhash, instance); + /* Clearing the hash */ + rxa2_clear_internal_memory(blockhash, ARGON2_PREHASH_SEED_LENGTH); + + return ARGON2_OK; +} diff --git a/src/crypto/randomx/argon2_core.h b/src/crypto/randomx/argon2_core.h new file mode 100644 index 00000000..274a01c7 --- /dev/null +++ b/src/crypto/randomx/argon2_core.h @@ -0,0 +1,254 @@ +/* +Copyright (c) 2018-2019, tevador + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* Original code from Argon2 reference source code package used under CC0 Licence + * https://github.com/P-H-C/phc-winner-argon2 + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves +*/ + +#ifndef ARGON2_CORE_H +#define ARGON2_CORE_H + +#include +#include "crypto/randomx/argon2.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +#define CONST_CAST(x) (x)(uintptr_t) + + /**********************Argon2 internal constants*******************************/ + +enum argon2_core_constants { + /* Memory block size in bytes */ + ARGON2_BLOCK_SIZE = 1024, + ARGON2_QWORDS_IN_BLOCK = ARGON2_BLOCK_SIZE / 8, + ARGON2_OWORDS_IN_BLOCK = ARGON2_BLOCK_SIZE / 16, + ARGON2_HWORDS_IN_BLOCK = ARGON2_BLOCK_SIZE / 32, + ARGON2_512BIT_WORDS_IN_BLOCK = ARGON2_BLOCK_SIZE / 64, + + /* Number of pseudo-random values generated by one call to Blake in Argon2i + to + generate reference block positions */ + ARGON2_ADDRESSES_IN_BLOCK = 128, + + /* Pre-hashing digest length and its extension*/ + ARGON2_PREHASH_DIGEST_LENGTH = 64, + ARGON2_PREHASH_SEED_LENGTH = 72 +}; + +/*************************Argon2 internal data types***********************/ + +/* + * Structure for the (1KB) memory block implemented as 128 64-bit words. + * Memory blocks can be copied, XORed. Internal words can be accessed by [] (no + * bounds checking). + */ +typedef struct block_ { uint64_t v[ARGON2_QWORDS_IN_BLOCK]; } block; + +/*****************Functions that work with the block******************/ + +/* Initialize each byte of the block with @in */ +void rxa2_init_block_value(block *b, uint8_t in); + +/* Copy block @src to block @dst */ +void rxa2_copy_block(block *dst, const block *src); + +/* XOR @src onto @dst bytewise */ +void rxa2_xor_block(block *dst, const block *src); + +/* + * Argon2 instance: memory pointer, number of passes, amount of memory, type, + * and derived values. + * Used to evaluate the number and location of blocks to construct in each + * thread + */ +typedef struct Argon2_instance_t { + block *memory; /* Memory pointer */ + uint32_t version; + uint32_t passes; /* Number of passes */ + uint32_t memory_blocks; /* Number of blocks in memory */ + uint32_t segment_length; + uint32_t lane_length; + uint32_t lanes; + uint32_t threads; + argon2_type type; + int print_internals; /* whether to print the memory blocks */ + argon2_context *context_ptr; /* points back to original context */ +} argon2_instance_t; + +/* + * Argon2 position: where we construct the block right now. Used to distribute + * work between threads. + */ +typedef struct Argon2_position_t { + uint32_t pass; + uint32_t lane; + uint8_t slice; + uint32_t index; +} argon2_position_t; + +/*Struct that holds the inputs for thread handling FillSegment*/ +typedef struct Argon2_thread_data { + argon2_instance_t *instance_ptr; + argon2_position_t pos; +} argon2_thread_data; + +/*************************Argon2 core functions********************************/ + +/* Allocates memory to the given pointer, uses the appropriate allocator as + * specified in the context. Total allocated memory is num*size. + * @param context argon2_context which specifies the allocator + * @param memory pointer to the pointer to the memory + * @param size the size in bytes for each element to be allocated + * @param num the number of elements to be allocated + * @return ARGON2_OK if @memory is a valid pointer and memory is allocated + */ +int rxa2_allocate_memory(const argon2_context *context, uint8_t **memory, + size_t num, size_t size); + +/* + * Frees memory at the given pointer, uses the appropriate deallocator as + * specified in the context. Also cleans the memory using clear_internal_memory. + * @param context argon2_context which specifies the deallocator + * @param memory pointer to buffer to be freed + * @param size the size in bytes for each element to be deallocated + * @param num the number of elements to be deallocated + */ +void rxa2_free_memory(const argon2_context *context, uint8_t *memory, + size_t num, size_t size); + +/* Function that securely cleans the memory. This ignores any flags set + * regarding clearing memory. Usually one just calls clear_internal_memory. + * @param mem Pointer to the memory + * @param s Memory size in bytes + */ +void rxa2_secure_wipe_memory(void *v, size_t n); + +/* Function that securely clears the memory if FLAG_clear_internal_memory is + * set. If the flag isn't set, this function does nothing. + * @param mem Pointer to the memory + * @param s Memory size in bytes + */ +void rxa2_clear_internal_memory(void *v, size_t n); + +/* + * Computes absolute position of reference block in the lane following a skewed + * distribution and using a pseudo-random value as input + * @param instance Pointer to the current instance + * @param position Pointer to the current position + * @param pseudo_rand 32-bit pseudo-random value used to determine the position + * @param same_lane Indicates if the block will be taken from the current lane. + * If so we can reference the current segment + * @pre All pointers must be valid + */ +uint32_t rxa2_index_alpha(const argon2_instance_t *instance, + const argon2_position_t *position, uint32_t pseudo_rand, + int same_lane); + +/* + * Function that validates all inputs against predefined restrictions and return + * an error code + * @param context Pointer to current Argon2 context + * @return ARGON2_OK if everything is all right, otherwise one of error codes + * (all defined in + */ +int rxa2_validate_inputs(const argon2_context *context); + +/* + * Hashes all the inputs into @a blockhash[PREHASH_DIGEST_LENGTH], clears + * password and secret if needed + * @param context Pointer to the Argon2 internal structure containing memory + * pointer, and parameters for time and space requirements. + * @param blockhash Buffer for pre-hashing digest + * @param type Argon2 type + * @pre @a blockhash must have at least @a PREHASH_DIGEST_LENGTH bytes + * allocated + */ +void rxa2_initial_hash(uint8_t *blockhash, argon2_context *context, + argon2_type type); + +/* + * Function creates first 2 blocks per lane + * @param instance Pointer to the current instance + * @param blockhash Pointer to the pre-hashing digest + * @pre blockhash must point to @a PREHASH_SEED_LENGTH allocated values + */ +void rxa2_fill_first_blocks(uint8_t *blockhash, const argon2_instance_t *instance); + +/* + * Function allocates memory, hashes the inputs with Blake, and creates first + * two blocks. Returns the pointer to the main memory with 2 blocks per lane + * initialized + * @param context Pointer to the Argon2 internal structure containing memory + * pointer, and parameters for time and space requirements. + * @param instance Current Argon2 instance + * @return Zero if successful, -1 if memory failed to allocate. @context->state + * will be modified if successful. + */ +int rxa2_argon_initialize(argon2_instance_t *instance, argon2_context *context); + +/* + * XORing the last block of each lane, hashing it, making the tag. Deallocates + * the memory. + * @param context Pointer to current Argon2 context (use only the out parameters + * from it) + * @param instance Pointer to current instance of Argon2 + * @pre instance->state must point to necessary amount of memory + * @pre context->out must point to outlen bytes of memory + * @pre if context->free_cbk is not NULL, it should point to a function that + * deallocates memory + */ +void rxa2_finalize(const argon2_context *context, argon2_instance_t *instance); + +/* + * Function that fills the segment using previous segments also from other + * threads + * @param context current context + * @param instance Pointer to the current instance + * @param position Current position + * @pre all block pointers must be valid + */ +void rxa2_fill_segment(const argon2_instance_t *instance, + argon2_position_t position); + +/* + * Function that fills the entire memory t_cost times based on the first two + * blocks in each lane + * @param instance Pointer to the current instance + * @return ARGON2_OK if successful, @context->state + */ +int rxa2_fill_memory_blocks(argon2_instance_t *instance); + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/src/crypto/randomx/argon2_ref.c b/src/crypto/randomx/argon2_ref.c new file mode 100644 index 00000000..157eda86 --- /dev/null +++ b/src/crypto/randomx/argon2_ref.c @@ -0,0 +1,214 @@ +/* +Copyright (c) 2018-2019, tevador + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* Original code from Argon2 reference source code package used under CC0 Licence + * https://github.com/P-H-C/phc-winner-argon2 + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves +*/ + +#include +#include +#include + +#include "crypto/randomx/argon2.h" +#include "crypto/randomx/argon2_core.h" + +#include "crypto/randomx/blake2/blamka-round-ref.h" +#include "crypto/randomx/blake2/blake2-impl.h" +#include "crypto/randomx/blake2/blake2.h" + + /* + * Function fills a new memory block and optionally XORs the old block over the new one. + * @next_block must be initialized. + * @param prev_block Pointer to the previous block + * @param ref_block Pointer to the reference block + * @param next_block Pointer to the block to be constructed + * @param with_xor Whether to XOR into the new block (1) or just overwrite (0) + * @pre all block pointers must be valid + */ +static void fill_block(const block *prev_block, const block *ref_block, + block *next_block, int with_xor) { + block blockR, block_tmp; + unsigned i; + + rxa2_copy_block(&blockR, ref_block); + rxa2_xor_block(&blockR, prev_block); + rxa2_copy_block(&block_tmp, &blockR); + /* Now blockR = ref_block + prev_block and block_tmp = ref_block + prev_block */ + if (with_xor) { + /* Saving the next block contents for XOR over: */ + rxa2_xor_block(&block_tmp, next_block); + /* Now blockR = ref_block + prev_block and + block_tmp = ref_block + prev_block + next_block */ + } + + /* Apply Blake2 on columns of 64-bit words: (0,1,...,15) , then + (16,17,..31)... finally (112,113,...127) */ + for (i = 0; i < 8; ++i) { + BLAKE2_ROUND_NOMSG( + blockR.v[16 * i], blockR.v[16 * i + 1], blockR.v[16 * i + 2], + blockR.v[16 * i + 3], blockR.v[16 * i + 4], blockR.v[16 * i + 5], + blockR.v[16 * i + 6], blockR.v[16 * i + 7], blockR.v[16 * i + 8], + blockR.v[16 * i + 9], blockR.v[16 * i + 10], blockR.v[16 * i + 11], + blockR.v[16 * i + 12], blockR.v[16 * i + 13], blockR.v[16 * i + 14], + blockR.v[16 * i + 15]); + } + + /* Apply Blake2 on rows of 64-bit words: (0,1,16,17,...112,113), then + (2,3,18,19,...,114,115).. finally (14,15,30,31,...,126,127) */ + for (i = 0; i < 8; i++) { + BLAKE2_ROUND_NOMSG( + blockR.v[2 * i], blockR.v[2 * i + 1], blockR.v[2 * i + 16], + blockR.v[2 * i + 17], blockR.v[2 * i + 32], blockR.v[2 * i + 33], + blockR.v[2 * i + 48], blockR.v[2 * i + 49], blockR.v[2 * i + 64], + blockR.v[2 * i + 65], blockR.v[2 * i + 80], blockR.v[2 * i + 81], + blockR.v[2 * i + 96], blockR.v[2 * i + 97], blockR.v[2 * i + 112], + blockR.v[2 * i + 113]); + } + + rxa2_copy_block(next_block, &block_tmp); + rxa2_xor_block(next_block, &blockR); +} + +static void next_addresses(block *address_block, block *input_block, + const block *zero_block) { + input_block->v[6]++; + fill_block(zero_block, input_block, address_block, 0); + fill_block(zero_block, address_block, address_block, 0); +} + +void rxa2_fill_segment(const argon2_instance_t *instance, + argon2_position_t position) { + block *ref_block = NULL, *curr_block = NULL; + block address_block, input_block, zero_block; + uint64_t pseudo_rand, ref_index, ref_lane; + uint32_t prev_offset, curr_offset; + uint32_t starting_index; + uint32_t i; + int data_independent_addressing; + + if (instance == NULL) { + return; + } + + data_independent_addressing = + (instance->type == Argon2_i) || + (instance->type == Argon2_id && (position.pass == 0) && + (position.slice < ARGON2_SYNC_POINTS / 2)); + + if (data_independent_addressing) { + rxa2_init_block_value(&zero_block, 0); + rxa2_init_block_value(&input_block, 0); + + input_block.v[0] = position.pass; + input_block.v[1] = position.lane; + input_block.v[2] = position.slice; + input_block.v[3] = instance->memory_blocks; + input_block.v[4] = instance->passes; + input_block.v[5] = instance->type; + } + + starting_index = 0; + + if ((0 == position.pass) && (0 == position.slice)) { + starting_index = 2; /* we have already generated the first two blocks */ + + /* Don't forget to generate the first block of addresses: */ + if (data_independent_addressing) { + next_addresses(&address_block, &input_block, &zero_block); + } + } + + /* Offset of the current block */ + curr_offset = position.lane * instance->lane_length + + position.slice * instance->segment_length + starting_index; + + if (0 == curr_offset % instance->lane_length) { + /* Last block in this lane */ + prev_offset = curr_offset + instance->lane_length - 1; + } + else { + /* Previous block */ + prev_offset = curr_offset - 1; + } + + for (i = starting_index; i < instance->segment_length; + ++i, ++curr_offset, ++prev_offset) { + /*1.1 Rotating prev_offset if needed */ + if (curr_offset % instance->lane_length == 1) { + prev_offset = curr_offset - 1; + } + + /* 1.2 Computing the index of the reference block */ + /* 1.2.1 Taking pseudo-random value from the previous block */ + if (data_independent_addressing) { + if (i % ARGON2_ADDRESSES_IN_BLOCK == 0) { + next_addresses(&address_block, &input_block, &zero_block); + } + pseudo_rand = address_block.v[i % ARGON2_ADDRESSES_IN_BLOCK]; + } + else { + pseudo_rand = instance->memory[prev_offset].v[0]; + } + + /* 1.2.2 Computing the lane of the reference block */ + ref_lane = ((pseudo_rand >> 32)) % instance->lanes; + + if ((position.pass == 0) && (position.slice == 0)) { + /* Can not reference other lanes yet */ + ref_lane = position.lane; + } + + /* 1.2.3 Computing the number of possible reference block within the + * lane. + */ + position.index = i; + ref_index = rxa2_index_alpha(instance, &position, pseudo_rand & 0xFFFFFFFF, + ref_lane == position.lane); + + /* 2 Creating a new block */ + ref_block = + instance->memory + instance->lane_length * ref_lane + ref_index; + curr_block = instance->memory + curr_offset; + if (ARGON2_VERSION_10 == instance->version) { + /* version 1.2.1 and earlier: overwrite, not XOR */ + fill_block(instance->memory + prev_offset, ref_block, curr_block, 0); + } + else { + if (0 == position.pass) { + fill_block(instance->memory + prev_offset, ref_block, + curr_block, 0); + } + else { + fill_block(instance->memory + prev_offset, ref_block, + curr_block, 1); + } + } + } +} diff --git a/src/crypto/randomx/asm/program_epilogue_linux.inc b/src/crypto/randomx/asm/program_epilogue_linux.inc new file mode 100644 index 00000000..eaacae54 --- /dev/null +++ b/src/crypto/randomx/asm/program_epilogue_linux.inc @@ -0,0 +1,10 @@ + ;# restore callee-saved registers - System V AMD64 ABI + pop r15 + pop r14 + pop r13 + pop r12 + pop rbp + pop rbx + + ;# program finished + ret 0 \ No newline at end of file diff --git a/src/crypto/randomx/asm/program_epilogue_store.inc b/src/crypto/randomx/asm/program_epilogue_store.inc new file mode 100644 index 00000000..b94fa4d9 --- /dev/null +++ b/src/crypto/randomx/asm/program_epilogue_store.inc @@ -0,0 +1,19 @@ + ;# save VM register values + pop rcx + mov qword ptr [rcx+0], r8 + mov qword ptr [rcx+8], r9 + mov qword ptr [rcx+16], r10 + mov qword ptr [rcx+24], r11 + mov qword ptr [rcx+32], r12 + mov qword ptr [rcx+40], r13 + mov qword ptr [rcx+48], r14 + mov qword ptr [rcx+56], r15 + movdqa xmmword ptr [rcx+64], xmm0 + movdqa xmmword ptr [rcx+80], xmm1 + movdqa xmmword ptr [rcx+96], xmm2 + movdqa xmmword ptr [rcx+112], xmm3 + lea rcx, [rcx+64] + movdqa xmmword ptr [rcx+64], xmm4 + movdqa xmmword ptr [rcx+80], xmm5 + movdqa xmmword ptr [rcx+96], xmm6 + movdqa xmmword ptr [rcx+112], xmm7 \ No newline at end of file diff --git a/src/crypto/randomx/asm/program_epilogue_win64.inc b/src/crypto/randomx/asm/program_epilogue_win64.inc new file mode 100644 index 00000000..8d70a0a3 --- /dev/null +++ b/src/crypto/randomx/asm/program_epilogue_win64.inc @@ -0,0 +1,24 @@ + ;# restore callee-saved registers - Microsoft x64 calling convention + movdqu xmm15, xmmword ptr [rsp] + movdqu xmm14, xmmword ptr [rsp+16] + movdqu xmm13, xmmword ptr [rsp+32] + movdqu xmm12, xmmword ptr [rsp+48] + movdqu xmm11, xmmword ptr [rsp+64] + add rsp, 80 + movdqu xmm10, xmmword ptr [rsp] + movdqu xmm9, xmmword ptr [rsp+16] + movdqu xmm8, xmmword ptr [rsp+32] + movdqu xmm7, xmmword ptr [rsp+48] + movdqu xmm6, xmmword ptr [rsp+64] + add rsp, 80 + pop r15 + pop r14 + pop r13 + pop r12 + pop rsi + pop rdi + pop rbp + pop rbx + + ;# program finished + ret diff --git a/src/crypto/randomx/asm/program_loop_load.inc b/src/crypto/randomx/asm/program_loop_load.inc new file mode 100644 index 00000000..374af66a --- /dev/null +++ b/src/crypto/randomx/asm/program_loop_load.inc @@ -0,0 +1,32 @@ + mov rdx, rax + and eax, RANDOMX_SCRATCHPAD_MASK + lea rcx, [rsi+rax] + push rcx + xor r8, qword ptr [rcx+0] + xor r9, qword ptr [rcx+8] + xor r10, qword ptr [rcx+16] + xor r11, qword ptr [rcx+24] + xor r12, qword ptr [rcx+32] + xor r13, qword ptr [rcx+40] + xor r14, qword ptr [rcx+48] + xor r15, qword ptr [rcx+56] + ror rdx, 32 + and edx, RANDOMX_SCRATCHPAD_MASK + lea rcx, [rsi+rdx] + push rcx + cvtdq2pd xmm0, qword ptr [rcx+0] + cvtdq2pd xmm1, qword ptr [rcx+8] + cvtdq2pd xmm2, qword ptr [rcx+16] + cvtdq2pd xmm3, qword ptr [rcx+24] + cvtdq2pd xmm4, qword ptr [rcx+32] + cvtdq2pd xmm5, qword ptr [rcx+40] + cvtdq2pd xmm6, qword ptr [rcx+48] + cvtdq2pd xmm7, qword ptr [rcx+56] + andps xmm4, xmm13 + andps xmm5, xmm13 + andps xmm6, xmm13 + andps xmm7, xmm13 + orps xmm4, xmm14 + orps xmm5, xmm14 + orps xmm6, xmm14 + orps xmm7, xmm14 diff --git a/src/crypto/randomx/asm/program_loop_store.inc b/src/crypto/randomx/asm/program_loop_store.inc new file mode 100644 index 00000000..53164cb0 --- /dev/null +++ b/src/crypto/randomx/asm/program_loop_store.inc @@ -0,0 +1,19 @@ + xor eax, eax + pop rcx + mov qword ptr [rcx+0], r8 + mov qword ptr [rcx+8], r9 + mov qword ptr [rcx+16], r10 + mov qword ptr [rcx+24], r11 + mov qword ptr [rcx+32], r12 + mov qword ptr [rcx+40], r13 + mov qword ptr [rcx+48], r14 + mov qword ptr [rcx+56], r15 + pop rcx + xorpd xmm0, xmm4 + xorpd xmm1, xmm5 + xorpd xmm2, xmm6 + xorpd xmm3, xmm7 + movapd xmmword ptr [rcx+0], xmm0 + movapd xmmword ptr [rcx+16], xmm1 + movapd xmmword ptr [rcx+32], xmm2 + movapd xmmword ptr [rcx+48], xmm3 diff --git a/src/crypto/randomx/asm/program_prologue_linux.inc b/src/crypto/randomx/asm/program_prologue_linux.inc new file mode 100644 index 00000000..ffde152c --- /dev/null +++ b/src/crypto/randomx/asm/program_prologue_linux.inc @@ -0,0 +1,34 @@ + ;# callee-saved registers - System V AMD64 ABI + push rbx + push rbp + push r12 + push r13 + push r14 + push r15 + + ;# function arguments + mov rbx, rcx ;# loop counter + push rdi ;# RegisterFile& registerFile + mov rcx, rdi + mov rbp, qword ptr [rsi] ;# "mx", "ma" + mov rdi, qword ptr [rsi+8] ;# uint8_t* dataset + mov rsi, rdx ;# uint8_t* scratchpad + + mov rax, rbp + + ;# zero integer registers + xor r8, r8 + xor r9, r9 + xor r10, r10 + xor r11, r11 + xor r12, r12 + xor r13, r13 + xor r14, r14 + xor r15, r15 + + ;# load constant registers + lea rcx, [rcx+120] + movapd xmm8, xmmword ptr [rcx+72] + movapd xmm9, xmmword ptr [rcx+88] + movapd xmm10, xmmword ptr [rcx+104] + movapd xmm11, xmmword ptr [rcx+120] diff --git a/src/crypto/randomx/asm/program_prologue_win64.inc b/src/crypto/randomx/asm/program_prologue_win64.inc new file mode 100644 index 00000000..590a98de --- /dev/null +++ b/src/crypto/randomx/asm/program_prologue_win64.inc @@ -0,0 +1,47 @@ + ;# callee-saved registers - Microsoft x64 calling convention + push rbx + push rbp + push rdi + push rsi + push r12 + push r13 + push r14 + push r15 + sub rsp, 80 + movdqu xmmword ptr [rsp+64], xmm6 + movdqu xmmword ptr [rsp+48], xmm7 + movdqu xmmword ptr [rsp+32], xmm8 + movdqu xmmword ptr [rsp+16], xmm9 + movdqu xmmword ptr [rsp+0], xmm10 + sub rsp, 80 + movdqu xmmword ptr [rsp+64], xmm11 + movdqu xmmword ptr [rsp+48], xmm12 + movdqu xmmword ptr [rsp+32], xmm13 + movdqu xmmword ptr [rsp+16], xmm14 + movdqu xmmword ptr [rsp+0], xmm15 + + ;# function arguments + push rcx ;# RegisterFile& registerFile + mov rbp, qword ptr [rdx] ;# "mx", "ma" + mov rdi, qword ptr [rdx+8] ;# uint8_t* dataset + mov rsi, r8 ;# uint8_t* scratchpad + mov rbx, r9 ;# loop counter + + mov rax, rbp + + ;# zero integer registers + xor r8, r8 + xor r9, r9 + xor r10, r10 + xor r11, r11 + xor r12, r12 + xor r13, r13 + xor r14, r14 + xor r15, r15 + + ;# load constant registers + lea rcx, [rcx+120] + movapd xmm8, xmmword ptr [rcx+72] + movapd xmm9, xmmword ptr [rcx+88] + movapd xmm10, xmmword ptr [rcx+104] + movapd xmm11, xmmword ptr [rcx+120] diff --git a/src/crypto/randomx/asm/program_read_dataset.inc b/src/crypto/randomx/asm/program_read_dataset.inc new file mode 100644 index 00000000..b81d0c32 --- /dev/null +++ b/src/crypto/randomx/asm/program_read_dataset.inc @@ -0,0 +1,17 @@ + xor rbp, rax ;# modify "mx" + mov edx, ebp ;# edx = mx + and edx, RANDOMX_DATASET_BASE_MASK + prefetchnta byte ptr [rdi+rdx] + ror rbp, 32 ;# swap "ma" and "mx" + mov edx, ebp ;# edx = ma + and edx, RANDOMX_DATASET_BASE_MASK + lea rcx, [rdi+rdx] ;# dataset cache line + xor r8, qword ptr [rcx+0] + xor r9, qword ptr [rcx+8] + xor r10, qword ptr [rcx+16] + xor r11, qword ptr [rcx+24] + xor r12, qword ptr [rcx+32] + xor r13, qword ptr [rcx+40] + xor r14, qword ptr [rcx+48] + xor r15, qword ptr [rcx+56] + \ No newline at end of file diff --git a/src/crypto/randomx/asm/program_read_dataset_sshash_fin.inc b/src/crypto/randomx/asm/program_read_dataset_sshash_fin.inc new file mode 100644 index 00000000..f5a067d2 --- /dev/null +++ b/src/crypto/randomx/asm/program_read_dataset_sshash_fin.inc @@ -0,0 +1,10 @@ + mov rbx, qword ptr [rsp+64] + xor r8, qword ptr [rsp+56] + xor r9, qword ptr [rsp+48] + xor r10, qword ptr [rsp+40] + xor r11, qword ptr [rsp+32] + xor r12, qword ptr [rsp+24] + xor r13, qword ptr [rsp+16] + xor r14, qword ptr [rsp+8] + xor r15, qword ptr [rsp+0] + add rsp, 72 \ No newline at end of file diff --git a/src/crypto/randomx/asm/program_read_dataset_sshash_init.inc b/src/crypto/randomx/asm/program_read_dataset_sshash_init.inc new file mode 100644 index 00000000..6fe9525d --- /dev/null +++ b/src/crypto/randomx/asm/program_read_dataset_sshash_init.inc @@ -0,0 +1,17 @@ + sub rsp, 72 + mov qword ptr [rsp+64], rbx + mov qword ptr [rsp+56], r8 + mov qword ptr [rsp+48], r9 + mov qword ptr [rsp+40], r10 + mov qword ptr [rsp+32], r11 + mov qword ptr [rsp+24], r12 + mov qword ptr [rsp+16], r13 + mov qword ptr [rsp+8], r14 + mov qword ptr [rsp+0], r15 + xor rbp, rax ;# modify "mx" + ror rbp, 32 ;# swap "ma" and "mx" + mov ebx, ebp ;# ecx = ma + and ebx, RANDOMX_DATASET_BASE_MASK + shr ebx, 6 ;# ebx = Dataset block number + ;# add ebx, datasetOffset / 64 + ;# call 32768 \ No newline at end of file diff --git a/src/crypto/randomx/asm/program_sshash_constants.inc b/src/crypto/randomx/asm/program_sshash_constants.inc new file mode 100644 index 00000000..53dc1755 --- /dev/null +++ b/src/crypto/randomx/asm/program_sshash_constants.inc @@ -0,0 +1,24 @@ +r0_mul: + ;#/ 6364136223846793005 + db 45, 127, 149, 76, 45, 244, 81, 88 +r1_add: + ;#/ 9298411001130361340 + db 252, 161, 245, 89, 138, 151, 10, 129 +r2_add: + ;#/ 12065312585734608966 + db 70, 216, 194, 56, 223, 153, 112, 167 +r3_add: + ;#/ 9306329213124626780 + db 92, 73, 34, 191, 28, 185, 38, 129 +r4_add: + ;#/ 5281919268842080866 + db 98, 138, 159, 23, 151, 37, 77, 73 +r5_add: + ;#/ 10536153434571861004 + db 12, 236, 170, 206, 185, 239, 55, 146 +r6_add: + ;#/ 3398623926847679864 + db 120, 45, 230, 108, 116, 86, 42, 47 +r7_add: + ;#/ 9549104520008361294 + db 78, 229, 44, 182, 247, 59, 133, 132 \ No newline at end of file diff --git a/src/crypto/randomx/asm/program_sshash_load.inc b/src/crypto/randomx/asm/program_sshash_load.inc new file mode 100644 index 00000000..53513569 --- /dev/null +++ b/src/crypto/randomx/asm/program_sshash_load.inc @@ -0,0 +1,8 @@ + xor r8, qword ptr [rbx+0] + xor r9, qword ptr [rbx+8] + xor r10, qword ptr [rbx+16] + xor r11, qword ptr [rbx+24] + xor r12, qword ptr [rbx+32] + xor r13, qword ptr [rbx+40] + xor r14, qword ptr [rbx+48] + xor r15, qword ptr [rbx+56] \ No newline at end of file diff --git a/src/crypto/randomx/asm/program_sshash_prefetch.inc b/src/crypto/randomx/asm/program_sshash_prefetch.inc new file mode 100644 index 00000000..26efb515 --- /dev/null +++ b/src/crypto/randomx/asm/program_sshash_prefetch.inc @@ -0,0 +1,4 @@ + and rbx, RANDOMX_CACHE_MASK + shl rbx, 6 + add rbx, rdi + prefetchnta byte ptr [rbx] \ No newline at end of file diff --git a/src/crypto/randomx/asm/program_xmm_constants.inc b/src/crypto/randomx/asm/program_xmm_constants.inc new file mode 100644 index 00000000..296237a4 --- /dev/null +++ b/src/crypto/randomx/asm/program_xmm_constants.inc @@ -0,0 +1,6 @@ +mantissaMask: + db 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 0 +exp240: + db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +scaleMask: + db 0, 0, 0, 0, 0, 0, 240, 128, 0, 0, 0, 0, 0, 0, 240, 128 \ No newline at end of file diff --git a/src/crypto/randomx/asm/randomx_reciprocal.inc b/src/crypto/randomx/asm/randomx_reciprocal.inc new file mode 100644 index 00000000..e1f22fdc --- /dev/null +++ b/src/crypto/randomx/asm/randomx_reciprocal.inc @@ -0,0 +1,7 @@ + mov edx, 1 + mov r8, rcx + xor eax, eax + bsr rcx, rcx + shl rdx, cl + div r8 + ret \ No newline at end of file diff --git a/src/crypto/randomx/blake2/blake2-impl.h b/src/crypto/randomx/blake2/blake2-impl.h new file mode 100644 index 00000000..28f0e301 --- /dev/null +++ b/src/crypto/randomx/blake2/blake2-impl.h @@ -0,0 +1,76 @@ +/* +Copyright (c) 2018-2019, tevador + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* Original code from Argon2 reference source code package used under CC0 Licence + * https://github.com/P-H-C/phc-winner-argon2 + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves +*/ + +#ifndef PORTABLE_BLAKE2_IMPL_H +#define PORTABLE_BLAKE2_IMPL_H + +#include + +#include "crypto/randomx/blake2/endian.h" + +static FORCE_INLINE uint64_t load48(const void *src) { + const uint8_t *p = (const uint8_t *)src; + uint64_t w = *p++; + w |= (uint64_t)(*p++) << 8; + w |= (uint64_t)(*p++) << 16; + w |= (uint64_t)(*p++) << 24; + w |= (uint64_t)(*p++) << 32; + w |= (uint64_t)(*p++) << 40; + return w; +} + +static FORCE_INLINE void store48(void *dst, uint64_t w) { + uint8_t *p = (uint8_t *)dst; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; +} + +static FORCE_INLINE uint32_t rotr32(const uint32_t w, const unsigned c) { + return (w >> c) | (w << (32 - c)); +} + +static FORCE_INLINE uint64_t rotr64(const uint64_t w, const unsigned c) { + return (w >> c) | (w << (64 - c)); +} + +#endif diff --git a/src/crypto/randomx/blake2/blake2.h b/src/crypto/randomx/blake2/blake2.h new file mode 100644 index 00000000..4d364c36 --- /dev/null +++ b/src/crypto/randomx/blake2/blake2.h @@ -0,0 +1,105 @@ +/* +Copyright (c) 2018-2019, tevador + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* Original code from Argon2 reference source code package used under CC0 Licence + * https://github.com/P-H-C/phc-winner-argon2 + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves +*/ + +#ifndef PORTABLE_BLAKE2_H +#define PORTABLE_BLAKE2_H + +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + + enum blake2b_constant { + BLAKE2B_BLOCKBYTES = 128, + BLAKE2B_OUTBYTES = 64, + BLAKE2B_KEYBYTES = 64, + BLAKE2B_SALTBYTES = 16, + BLAKE2B_PERSONALBYTES = 16 + }; + +#pragma pack(push, 1) + typedef struct __blake2b_param { + uint8_t digest_length; /* 1 */ + uint8_t key_length; /* 2 */ + uint8_t fanout; /* 3 */ + uint8_t depth; /* 4 */ + uint32_t leaf_length; /* 8 */ + uint64_t node_offset; /* 16 */ + uint8_t node_depth; /* 17 */ + uint8_t inner_length; /* 18 */ + uint8_t reserved[14]; /* 32 */ + uint8_t salt[BLAKE2B_SALTBYTES]; /* 48 */ + uint8_t personal[BLAKE2B_PERSONALBYTES]; /* 64 */ + } blake2b_param; +#pragma pack(pop) + + typedef struct __blake2b_state { + uint64_t h[8]; + uint64_t t[2]; + uint64_t f[2]; + uint8_t buf[BLAKE2B_BLOCKBYTES]; + unsigned buflen; + unsigned outlen; + uint8_t last_node; + } blake2b_state; + + /* Ensure param structs have not been wrongly padded */ + /* Poor man's static_assert */ + enum { + blake2_size_check_0 = 1 / !!(CHAR_BIT == 8), + blake2_size_check_2 = + 1 / !!(sizeof(blake2b_param) == sizeof(uint64_t) * CHAR_BIT) + }; + + /* Streaming API */ + int rx_blake2b_init(blake2b_state *S, size_t outlen); + int rx_blake2b_init_key(blake2b_state *S, size_t outlen, const void *key, size_t keylen); + int rx_blake2b_init_param(blake2b_state *S, const blake2b_param *P); + int rx_blake2b_update(blake2b_state *S, const void *in, size_t inlen); + int rx_blake2b_final(blake2b_state *S, void *out, size_t outlen); + + /* Simple API */ + int rx_blake2b(void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen); + + /* Argon2 Team - Begin Code */ + int rxa2_blake2b_long(void *out, size_t outlen, const void *in, size_t inlen); + /* Argon2 Team - End Code */ + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/src/crypto/randomx/blake2/blake2b.c b/src/crypto/randomx/blake2/blake2b.c new file mode 100644 index 00000000..d2e02887 --- /dev/null +++ b/src/crypto/randomx/blake2/blake2b.c @@ -0,0 +1,409 @@ +/* +Copyright (c) 2018-2019, tevador + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* Original code from Argon2 reference source code package used under CC0 Licence + * https://github.com/P-H-C/phc-winner-argon2 + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves +*/ + +#include +#include +#include + +#include "crypto/randomx/blake2/blake2.h" +#include "crypto/randomx/blake2/blake2-impl.h" + +static const uint64_t blake2b_IV[8] = { + UINT64_C(0x6a09e667f3bcc908), UINT64_C(0xbb67ae8584caa73b), + UINT64_C(0x3c6ef372fe94f82b), UINT64_C(0xa54ff53a5f1d36f1), + UINT64_C(0x510e527fade682d1), UINT64_C(0x9b05688c2b3e6c1f), + UINT64_C(0x1f83d9abfb41bd6b), UINT64_C(0x5be0cd19137e2179) }; + +static const unsigned int blake2b_sigma[12][16] = { + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3}, + {11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4}, + {7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8}, + {9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13}, + {2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9}, + {12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11}, + {13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10}, + {6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5}, + {10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3}, +}; + +static FORCE_INLINE void blake2b_set_lastnode(blake2b_state *S) { + S->f[1] = (uint64_t)-1; +} + +static FORCE_INLINE void blake2b_set_lastblock(blake2b_state *S) { + if (S->last_node) { + blake2b_set_lastnode(S); + } + S->f[0] = (uint64_t)-1; +} + +static FORCE_INLINE void blake2b_increment_counter(blake2b_state *S, + uint64_t inc) { + S->t[0] += inc; + S->t[1] += (S->t[0] < inc); +} + +static FORCE_INLINE void blake2b_invalidate_state(blake2b_state *S) { + //clear_internal_memory(S, sizeof(*S)); /* wipe */ + blake2b_set_lastblock(S); /* invalidate for further use */ +} + +static FORCE_INLINE void blake2b_init0(blake2b_state *S) { + memset(S, 0, sizeof(*S)); + memcpy(S->h, blake2b_IV, sizeof(S->h)); +} + +int blake2b_init_param(blake2b_state *S, const blake2b_param *P) { + const unsigned char *p = (const unsigned char *)P; + unsigned int i; + + if (NULL == P || NULL == S) { + return -1; + } + + blake2b_init0(S); + /* IV XOR Parameter Block */ + for (i = 0; i < 8; ++i) { + S->h[i] ^= load64(&p[i * sizeof(S->h[i])]); + } + S->outlen = P->digest_length; + return 0; +} + +/* Sequential blake2b initialization */ +int rx_blake2b_init(blake2b_state *S, size_t outlen) { + blake2b_param P; + + if (S == NULL) { + return -1; + } + + if ((outlen == 0) || (outlen > BLAKE2B_OUTBYTES)) { + blake2b_invalidate_state(S); + return -1; + } + + /* Setup Parameter Block for unkeyed BLAKE2 */ + P.digest_length = (uint8_t)outlen; + P.key_length = 0; + P.fanout = 1; + P.depth = 1; + P.leaf_length = 0; + P.node_offset = 0; + P.node_depth = 0; + P.inner_length = 0; + memset(P.reserved, 0, sizeof(P.reserved)); + memset(P.salt, 0, sizeof(P.salt)); + memset(P.personal, 0, sizeof(P.personal)); + + return blake2b_init_param(S, &P); +} + +int rx_blake2b_init_key(blake2b_state *S, size_t outlen, const void *key, size_t keylen) { + blake2b_param P; + + if (S == NULL) { + return -1; + } + + if ((outlen == 0) || (outlen > BLAKE2B_OUTBYTES)) { + blake2b_invalidate_state(S); + return -1; + } + + if ((key == 0) || (keylen == 0) || (keylen > BLAKE2B_KEYBYTES)) { + blake2b_invalidate_state(S); + return -1; + } + + /* Setup Parameter Block for keyed BLAKE2 */ + P.digest_length = (uint8_t)outlen; + P.key_length = (uint8_t)keylen; + P.fanout = 1; + P.depth = 1; + P.leaf_length = 0; + P.node_offset = 0; + P.node_depth = 0; + P.inner_length = 0; + memset(P.reserved, 0, sizeof(P.reserved)); + memset(P.salt, 0, sizeof(P.salt)); + memset(P.personal, 0, sizeof(P.personal)); + + if (blake2b_init_param(S, &P) < 0) { + blake2b_invalidate_state(S); + return -1; + } + + { + uint8_t block[BLAKE2B_BLOCKBYTES]; + memset(block, 0, BLAKE2B_BLOCKBYTES); + memcpy(block, key, keylen); + rx_blake2b_update(S, block, BLAKE2B_BLOCKBYTES); + /* Burn the key from stack */ + //clear_internal_memory(block, BLAKE2B_BLOCKBYTES); + } + return 0; +} + +static void rx_blake2b_compress(blake2b_state *S, const uint8_t *block) { + uint64_t m[16]; + uint64_t v[16]; + unsigned int i, r; + + for (i = 0; i < 16; ++i) { + m[i] = load64(block + i * sizeof(m[i])); + } + + for (i = 0; i < 8; ++i) { + v[i] = S->h[i]; + } + + v[8] = blake2b_IV[0]; + v[9] = blake2b_IV[1]; + v[10] = blake2b_IV[2]; + v[11] = blake2b_IV[3]; + v[12] = blake2b_IV[4] ^ S->t[0]; + v[13] = blake2b_IV[5] ^ S->t[1]; + v[14] = blake2b_IV[6] ^ S->f[0]; + v[15] = blake2b_IV[7] ^ S->f[1]; + +#define G(r, i, a, b, c, d) \ + do { \ + a = a + b + m[blake2b_sigma[r][2 * i + 0]]; \ + d = rotr64(d ^ a, 32); \ + c = c + d; \ + b = rotr64(b ^ c, 24); \ + a = a + b + m[blake2b_sigma[r][2 * i + 1]]; \ + d = rotr64(d ^ a, 16); \ + c = c + d; \ + b = rotr64(b ^ c, 63); \ + } while ((void)0, 0) + +#define ROUND(r) \ + do { \ + G(r, 0, v[0], v[4], v[8], v[12]); \ + G(r, 1, v[1], v[5], v[9], v[13]); \ + G(r, 2, v[2], v[6], v[10], v[14]); \ + G(r, 3, v[3], v[7], v[11], v[15]); \ + G(r, 4, v[0], v[5], v[10], v[15]); \ + G(r, 5, v[1], v[6], v[11], v[12]); \ + G(r, 6, v[2], v[7], v[8], v[13]); \ + G(r, 7, v[3], v[4], v[9], v[14]); \ + } while ((void)0, 0) + + for (r = 0; r < 12; ++r) { + ROUND(r); + } + + for (i = 0; i < 8; ++i) { + S->h[i] = S->h[i] ^ v[i] ^ v[i + 8]; + } + +#undef G +#undef ROUND +} + +int rx_blake2b_update(blake2b_state *S, const void *in, size_t inlen) { + const uint8_t *pin = (const uint8_t *)in; + + if (inlen == 0) { + return 0; + } + + /* Sanity check */ + if (S == NULL || in == NULL) { + return -1; + } + + /* Is this a reused state? */ + if (S->f[0] != 0) { + return -1; + } + + if (S->buflen + inlen > BLAKE2B_BLOCKBYTES) { + /* Complete current block */ + size_t left = S->buflen; + size_t fill = BLAKE2B_BLOCKBYTES - left; + memcpy(&S->buf[left], pin, fill); + blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES); + rx_blake2b_compress(S, S->buf); + S->buflen = 0; + inlen -= fill; + pin += fill; + /* Avoid buffer copies when possible */ + while (inlen > BLAKE2B_BLOCKBYTES) { + blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES); + rx_blake2b_compress(S, pin); + inlen -= BLAKE2B_BLOCKBYTES; + pin += BLAKE2B_BLOCKBYTES; + } + } + memcpy(&S->buf[S->buflen], pin, inlen); + S->buflen += (unsigned int)inlen; + return 0; +} + +int rx_blake2b_final(blake2b_state *S, void *out, size_t outlen) { + uint8_t buffer[BLAKE2B_OUTBYTES] = { 0 }; + unsigned int i; + + /* Sanity checks */ + if (S == NULL || out == NULL || outlen < S->outlen) { + return -1; + } + + /* Is this a reused state? */ + if (S->f[0] != 0) { + return -1; + } + + blake2b_increment_counter(S, S->buflen); + blake2b_set_lastblock(S); + memset(&S->buf[S->buflen], 0, BLAKE2B_BLOCKBYTES - S->buflen); /* Padding */ + rx_blake2b_compress(S, S->buf); + + for (i = 0; i < 8; ++i) { /* Output full hash to temp buffer */ + store64(buffer + sizeof(S->h[i]) * i, S->h[i]); + } + + memcpy(out, buffer, S->outlen); + //clear_internal_memory(buffer, sizeof(buffer)); + //clear_internal_memory(S->buf, sizeof(S->buf)); + //clear_internal_memory(S->h, sizeof(S->h)); + return 0; +} + +int rx_blake2b(void *out, size_t outlen, const void *in, size_t inlen, + const void *key, size_t keylen) { + blake2b_state S; + int ret = -1; + + /* Verify parameters */ + if (NULL == in && inlen > 0) { + goto fail; + } + + if (NULL == out || outlen == 0 || outlen > BLAKE2B_OUTBYTES) { + goto fail; + } + + if ((NULL == key && keylen > 0) || keylen > BLAKE2B_KEYBYTES) { + goto fail; + } + + if (keylen > 0) { + if (rx_blake2b_init_key(&S, outlen, key, keylen) < 0) { + goto fail; + } + } + else { + if (rx_blake2b_init(&S, outlen) < 0) { + goto fail; + } + } + + if (rx_blake2b_update(&S, in, inlen) < 0) { + goto fail; + } + ret = rx_blake2b_final(&S, out, outlen); + +fail: + //clear_internal_memory(&S, sizeof(S)); + return ret; +} + +/* Argon2 Team - Begin Code */ +int rxa2_blake2b_long(void *pout, size_t outlen, const void *in, size_t inlen) { + uint8_t *out = (uint8_t *)pout; + blake2b_state blake_state; + uint8_t outlen_bytes[sizeof(uint32_t)] = { 0 }; + int ret = -1; + + if (outlen > UINT32_MAX) { + goto fail; + } + + /* Ensure little-endian byte order! */ + store32(outlen_bytes, (uint32_t)outlen); + +#define TRY(statement) \ + do { \ + ret = statement; \ + if (ret < 0) { \ + goto fail; \ + } \ + } while ((void)0, 0) + + if (outlen <= BLAKE2B_OUTBYTES) { + TRY(rx_blake2b_init(&blake_state, outlen)); + TRY(rx_blake2b_update(&blake_state, outlen_bytes, sizeof(outlen_bytes))); + TRY(rx_blake2b_update(&blake_state, in, inlen)); + TRY(rx_blake2b_final(&blake_state, out, outlen)); + } + else { + uint32_t toproduce; + uint8_t out_buffer[BLAKE2B_OUTBYTES]; + uint8_t in_buffer[BLAKE2B_OUTBYTES]; + TRY(rx_blake2b_init(&blake_state, BLAKE2B_OUTBYTES)); + TRY(rx_blake2b_update(&blake_state, outlen_bytes, sizeof(outlen_bytes))); + TRY(rx_blake2b_update(&blake_state, in, inlen)); + TRY(rx_blake2b_final(&blake_state, out_buffer, BLAKE2B_OUTBYTES)); + memcpy(out, out_buffer, BLAKE2B_OUTBYTES / 2); + out += BLAKE2B_OUTBYTES / 2; + toproduce = (uint32_t)outlen - BLAKE2B_OUTBYTES / 2; + + while (toproduce > BLAKE2B_OUTBYTES) { + memcpy(in_buffer, out_buffer, BLAKE2B_OUTBYTES); + TRY(rx_blake2b(out_buffer, BLAKE2B_OUTBYTES, in_buffer, + BLAKE2B_OUTBYTES, NULL, 0)); + memcpy(out, out_buffer, BLAKE2B_OUTBYTES / 2); + out += BLAKE2B_OUTBYTES / 2; + toproduce -= BLAKE2B_OUTBYTES / 2; + } + + memcpy(in_buffer, out_buffer, BLAKE2B_OUTBYTES); + TRY(rx_blake2b(out_buffer, toproduce, in_buffer, BLAKE2B_OUTBYTES, NULL, + 0)); + memcpy(out, out_buffer, toproduce); + } +fail: + //clear_internal_memory(&blake_state, sizeof(blake_state)); + return ret; +#undef TRY +} +/* Argon2 Team - End Code */ + diff --git a/src/crypto/randomx/blake2/blamka-round-ref.h b/src/crypto/randomx/blake2/blamka-round-ref.h new file mode 100644 index 00000000..45606b8f --- /dev/null +++ b/src/crypto/randomx/blake2/blamka-round-ref.h @@ -0,0 +1,73 @@ +/* +Copyright (c) 2018-2019, tevador + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* Original code from Argon2 reference source code package used under CC0 Licence + * https://github.com/P-H-C/phc-winner-argon2 + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves +*/ + +#ifndef BLAKE_ROUND_MKA_H +#define BLAKE_ROUND_MKA_H + +#include "crypto/randomx/blake2/blake2.h" +#include "crypto/randomx/blake2/blake2-impl.h" + + /* designed by the Lyra PHC team */ +static FORCE_INLINE uint64_t fBlaMka(uint64_t x, uint64_t y) { + const uint64_t m = UINT64_C(0xFFFFFFFF); + const uint64_t xy = (x & m) * (y & m); + return x + y + 2 * xy; +} + +#define G(a, b, c, d) \ + do { \ + a = fBlaMka(a, b); \ + d = rotr64(d ^ a, 32); \ + c = fBlaMka(c, d); \ + b = rotr64(b ^ c, 24); \ + a = fBlaMka(a, b); \ + d = rotr64(d ^ a, 16); \ + c = fBlaMka(c, d); \ + b = rotr64(b ^ c, 63); \ + } while ((void)0, 0) + +#define BLAKE2_ROUND_NOMSG(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, \ + v12, v13, v14, v15) \ + do { \ + G(v0, v4, v8, v12); \ + G(v1, v5, v9, v13); \ + G(v2, v6, v10, v14); \ + G(v3, v7, v11, v15); \ + G(v0, v5, v10, v15); \ + G(v1, v6, v11, v12); \ + G(v2, v7, v8, v13); \ + G(v3, v4, v9, v14); \ + } while ((void)0, 0) + +#endif diff --git a/src/crypto/randomx/blake2/endian.h b/src/crypto/randomx/blake2/endian.h new file mode 100644 index 00000000..c7afed26 --- /dev/null +++ b/src/crypto/randomx/blake2/endian.h @@ -0,0 +1,107 @@ +#pragma once +#include +#include + +#if defined(_MSC_VER) +#define FORCE_INLINE __inline +#elif defined(__GNUC__) || defined(__clang__) +#define FORCE_INLINE __inline__ +#else +#define FORCE_INLINE +#endif + + /* Argon2 Team - Begin Code */ + /* + Not an exhaustive list, but should cover the majority of modern platforms + Additionally, the code will always be correct---this is only a performance + tweak. + */ +#if (defined(__BYTE_ORDER__) && \ + (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) || \ + defined(__LITTLE_ENDIAN__) || defined(__ARMEL__) || defined(__MIPSEL__) || \ + defined(__AARCH64EL__) || defined(__amd64__) || defined(__i386__) || \ + defined(_M_IX86) || defined(_M_X64) || defined(_M_AMD64) || \ + defined(_M_ARM) +#define NATIVE_LITTLE_ENDIAN +#endif + /* Argon2 Team - End Code */ + +static FORCE_INLINE uint32_t load32(const void *src) { +#if defined(NATIVE_LITTLE_ENDIAN) + uint32_t w; + memcpy(&w, src, sizeof w); + return w; +#else + const uint8_t *p = (const uint8_t *)src; + uint32_t w = *p++; + w |= (uint32_t)(*p++) << 8; + w |= (uint32_t)(*p++) << 16; + w |= (uint32_t)(*p++) << 24; + return w; +#endif +} + +static FORCE_INLINE uint64_t load64_native(const void *src) { + uint64_t w; + memcpy(&w, src, sizeof w); + return w; +} + +static FORCE_INLINE uint64_t load64(const void *src) { +#if defined(NATIVE_LITTLE_ENDIAN) + return load64_native(src); +#else + const uint8_t *p = (const uint8_t *)src; + uint64_t w = *p++; + w |= (uint64_t)(*p++) << 8; + w |= (uint64_t)(*p++) << 16; + w |= (uint64_t)(*p++) << 24; + w |= (uint64_t)(*p++) << 32; + w |= (uint64_t)(*p++) << 40; + w |= (uint64_t)(*p++) << 48; + w |= (uint64_t)(*p++) << 56; + return w; +#endif +} + +static FORCE_INLINE void store32(void *dst, uint32_t w) { +#if defined(NATIVE_LITTLE_ENDIAN) + memcpy(dst, &w, sizeof w); +#else + uint8_t *p = (uint8_t *)dst; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; +#endif +} + +static FORCE_INLINE void store64_native(void *dst, uint64_t w) { + memcpy(dst, &w, sizeof w); +} + +static FORCE_INLINE void store64(void *dst, uint64_t w) { +#if defined(NATIVE_LITTLE_ENDIAN) + store64_native(dst, w); +#else + uint8_t *p = (uint8_t *)dst; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; + w >>= 8; + *p++ = (uint8_t)w; +#endif +} diff --git a/src/crypto/randomx/blake2_generator.cpp b/src/crypto/randomx/blake2_generator.cpp new file mode 100644 index 00000000..edfe2e34 --- /dev/null +++ b/src/crypto/randomx/blake2_generator.cpp @@ -0,0 +1,62 @@ +/* +Copyright (c) 2018-2019, tevador + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include "crypto/randomx/blake2/blake2.h" +#include "crypto/randomx/blake2/endian.h" +#include "crypto/randomx/blake2_generator.hpp" + +namespace randomx { + + constexpr int maxSeedSize = 60; + + Blake2Generator::Blake2Generator(const void* seed, size_t seedSize, int nonce) : dataIndex(sizeof(data)) { + memset(data, 0, sizeof(data)); + memcpy(data, seed, seedSize > maxSeedSize ? maxSeedSize : seedSize); + store32(&data[maxSeedSize], nonce); + } + + uint8_t Blake2Generator::getByte() { + checkData(1); + return data[dataIndex++]; + } + + uint32_t Blake2Generator::getUInt32() { + checkData(4); + auto ret = load32(&data[dataIndex]); + dataIndex += 4; + return ret; + } + + void Blake2Generator::checkData(const size_t bytesNeeded) { + if (dataIndex + bytesNeeded > sizeof(data)) { + rx_blake2b(data, sizeof(data), data, sizeof(data), nullptr, 0); + dataIndex = 0; + } + } +} diff --git a/src/crypto/randomx/blake2_generator.hpp b/src/crypto/randomx/blake2_generator.hpp new file mode 100644 index 00000000..5e7f61f2 --- /dev/null +++ b/src/crypto/randomx/blake2_generator.hpp @@ -0,0 +1,46 @@ +/* +Copyright (c) 2018-2019, tevador + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#pragma once + +#include + +namespace randomx { + + class Blake2Generator { + public: + Blake2Generator(const void* seed, size_t seedSize, int nonce = 0); + uint8_t getByte(); + uint32_t getUInt32(); + private: + void checkData(const size_t); + + uint8_t data[64]; + size_t dataIndex; + }; +} \ No newline at end of file diff --git a/src/crypto/randomx/bytecode_machine.cpp b/src/crypto/randomx/bytecode_machine.cpp new file mode 100644 index 00000000..55a63935 --- /dev/null +++ b/src/crypto/randomx/bytecode_machine.cpp @@ -0,0 +1,482 @@ +/* +Copyright (c) 2019, tevador + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "crypto/randomx/bytecode_machine.hpp" +#include "crypto/randomx/reciprocal.h" + +namespace randomx { + + const int_reg_t BytecodeMachine::zero = 0; + +#define INSTR_CASE(x) case InstructionType::x: \ + exe_ ## x(ibc, pc, scratchpad, config); \ + break; + + void BytecodeMachine::executeInstruction(RANDOMX_EXE_ARGS) { + switch (ibc.type) + { + INSTR_CASE(IADD_RS) + INSTR_CASE(IADD_M) + INSTR_CASE(ISUB_R) + INSTR_CASE(ISUB_M) + INSTR_CASE(IMUL_R) + INSTR_CASE(IMUL_M) + INSTR_CASE(IMULH_R) + INSTR_CASE(IMULH_M) + INSTR_CASE(ISMULH_R) + INSTR_CASE(ISMULH_M) + INSTR_CASE(INEG_R) + INSTR_CASE(IXOR_R) + INSTR_CASE(IXOR_M) + INSTR_CASE(IROR_R) + INSTR_CASE(IROL_R) + INSTR_CASE(ISWAP_R) + INSTR_CASE(FSWAP_R) + INSTR_CASE(FADD_R) + INSTR_CASE(FADD_M) + INSTR_CASE(FSUB_R) + INSTR_CASE(FSUB_M) + INSTR_CASE(FSCAL_R) + INSTR_CASE(FMUL_R) + INSTR_CASE(FDIV_M) + INSTR_CASE(FSQRT_R) + INSTR_CASE(CBRANCH) + INSTR_CASE(CFROUND) + INSTR_CASE(ISTORE) + + case InstructionType::NOP: + break; + + case InstructionType::IMUL_RCP: //executed as IMUL_R + default: + UNREACHABLE; + } + } + + void BytecodeMachine::compileInstruction(RANDOMX_GEN_ARGS) { + int opcode = instr.opcode; + + if (opcode < RandomX_CurrentConfig.CEIL_IADD_RS) { + auto dst = instr.dst % RegistersCount; + auto src = instr.src % RegistersCount; + ibc.type = InstructionType::IADD_RS; + ibc.idst = &nreg->r[dst]; + if (dst != RegisterNeedsDisplacement) { + ibc.isrc = &nreg->r[src]; + ibc.shift = instr.getModShift(); + ibc.imm = 0; + } + else { + ibc.isrc = &nreg->r[src]; + ibc.shift = instr.getModShift(); + ibc.imm = signExtend2sCompl(instr.getImm32()); + } + registerUsage[dst] = i; + return; + } + + if (opcode < RandomX_CurrentConfig.CEIL_IADD_M) { + auto dst = instr.dst % RegistersCount; + auto src = instr.src % RegistersCount; + ibc.type = InstructionType::IADD_M; + ibc.idst = &nreg->r[dst]; + ibc.imm = signExtend2sCompl(instr.getImm32()); + if (src != dst) { + ibc.isrc = &nreg->r[src]; + ibc.memMask = (instr.getModMem() ? ScratchpadL1Mask : ScratchpadL2Mask); + } + else { + ibc.isrc = &zero; + ibc.memMask = ScratchpadL3Mask; + } + registerUsage[dst] = i; + return; + } + + if (opcode < RandomX_CurrentConfig.CEIL_ISUB_R) { + auto dst = instr.dst % RegistersCount; + auto src = instr.src % RegistersCount; + ibc.type = InstructionType::ISUB_R; + ibc.idst = &nreg->r[dst]; + if (src != dst) { + ibc.isrc = &nreg->r[src]; + } + else { + ibc.imm = signExtend2sCompl(instr.getImm32()); + ibc.isrc = &ibc.imm; + } + registerUsage[dst] = i; + return; + } + + if (opcode < RandomX_CurrentConfig.CEIL_ISUB_M) { + auto dst = instr.dst % RegistersCount; + auto src = instr.src % RegistersCount; + ibc.type = InstructionType::ISUB_M; + ibc.idst = &nreg->r[dst]; + ibc.imm = signExtend2sCompl(instr.getImm32()); + if (src != dst) { + ibc.isrc = &nreg->r[src]; + ibc.memMask = (instr.getModMem() ? ScratchpadL1Mask : ScratchpadL2Mask); + } + else { + ibc.isrc = &zero; + ibc.memMask = ScratchpadL3Mask; + } + registerUsage[dst] = i; + return; + } + + if (opcode < RandomX_CurrentConfig.CEIL_IMUL_R) { + auto dst = instr.dst % RegistersCount; + auto src = instr.src % RegistersCount; + ibc.type = InstructionType::IMUL_R; + ibc.idst = &nreg->r[dst]; + if (src != dst) { + ibc.isrc = &nreg->r[src]; + } + else { + ibc.imm = signExtend2sCompl(instr.getImm32()); + ibc.isrc = &ibc.imm; + } + registerUsage[dst] = i; + return; + } + + if (opcode < RandomX_CurrentConfig.CEIL_IMUL_M) { + auto dst = instr.dst % RegistersCount; + auto src = instr.src % RegistersCount; + ibc.type = InstructionType::IMUL_M; + ibc.idst = &nreg->r[dst]; + ibc.imm = signExtend2sCompl(instr.getImm32()); + if (src != dst) { + ibc.isrc = &nreg->r[src]; + ibc.memMask = (instr.getModMem() ? ScratchpadL1Mask : ScratchpadL2Mask); + } + else { + ibc.isrc = &zero; + ibc.memMask = ScratchpadL3Mask; + } + registerUsage[dst] = i; + return; + } + + if (opcode < RandomX_CurrentConfig.CEIL_IMULH_R) { + auto dst = instr.dst % RegistersCount; + auto src = instr.src % RegistersCount; + ibc.type = InstructionType::IMULH_R; + ibc.idst = &nreg->r[dst]; + ibc.isrc = &nreg->r[src]; + registerUsage[dst] = i; + return; + } + + if (opcode < RandomX_CurrentConfig.CEIL_IMULH_M) { + auto dst = instr.dst % RegistersCount; + auto src = instr.src % RegistersCount; + ibc.type = InstructionType::IMULH_M; + ibc.idst = &nreg->r[dst]; + ibc.imm = signExtend2sCompl(instr.getImm32()); + if (src != dst) { + ibc.isrc = &nreg->r[src]; + ibc.memMask = (instr.getModMem() ? ScratchpadL1Mask : ScratchpadL2Mask); + } + else { + ibc.isrc = &zero; + ibc.memMask = ScratchpadL3Mask; + } + registerUsage[dst] = i; + return; + } + + if (opcode < RandomX_CurrentConfig.CEIL_ISMULH_R) { + auto dst = instr.dst % RegistersCount; + auto src = instr.src % RegistersCount; + ibc.type = InstructionType::ISMULH_R; + ibc.idst = &nreg->r[dst]; + ibc.isrc = &nreg->r[src]; + registerUsage[dst] = i; + return; + } + + if (opcode < RandomX_CurrentConfig.CEIL_ISMULH_M) { + auto dst = instr.dst % RegistersCount; + auto src = instr.src % RegistersCount; + ibc.type = InstructionType::ISMULH_M; + ibc.idst = &nreg->r[dst]; + ibc.imm = signExtend2sCompl(instr.getImm32()); + if (src != dst) { + ibc.isrc = &nreg->r[src]; + ibc.memMask = (instr.getModMem() ? ScratchpadL1Mask : ScratchpadL2Mask); + } + else { + ibc.isrc = &zero; + ibc.memMask = ScratchpadL3Mask; + } + registerUsage[dst] = i; + return; + } + + if (opcode < RandomX_CurrentConfig.CEIL_IMUL_RCP) { + uint64_t divisor = instr.getImm32(); + if (!isZeroOrPowerOf2(divisor)) { + auto dst = instr.dst % RegistersCount; + ibc.type = InstructionType::IMUL_R; + ibc.idst = &nreg->r[dst]; + ibc.imm = randomx_reciprocal(divisor); + ibc.isrc = &ibc.imm; + registerUsage[dst] = i; + } + else { + ibc.type = InstructionType::NOP; + } + return; + } + + if (opcode < RandomX_CurrentConfig.CEIL_INEG_R) { + auto dst = instr.dst % RegistersCount; + ibc.type = InstructionType::INEG_R; + ibc.idst = &nreg->r[dst]; + registerUsage[dst] = i; + return; + } + + if (opcode < RandomX_CurrentConfig.CEIL_IXOR_R) { + auto dst = instr.dst % RegistersCount; + auto src = instr.src % RegistersCount; + ibc.type = InstructionType::IXOR_R; + ibc.idst = &nreg->r[dst]; + if (src != dst) { + ibc.isrc = &nreg->r[src]; + } + else { + ibc.imm = signExtend2sCompl(instr.getImm32()); + ibc.isrc = &ibc.imm; + } + registerUsage[dst] = i; + return; + } + + if (opcode < RandomX_CurrentConfig.CEIL_IXOR_M) { + auto dst = instr.dst % RegistersCount; + auto src = instr.src % RegistersCount; + ibc.type = InstructionType::IXOR_M; + ibc.idst = &nreg->r[dst]; + ibc.imm = signExtend2sCompl(instr.getImm32()); + if (src != dst) { + ibc.isrc = &nreg->r[src]; + ibc.memMask = (instr.getModMem() ? ScratchpadL1Mask : ScratchpadL2Mask); + } + else { + ibc.isrc = &zero; + ibc.memMask = ScratchpadL3Mask; + } + registerUsage[dst] = i; + return; + } + + if (opcode < RandomX_CurrentConfig.CEIL_IROR_R) { + auto dst = instr.dst % RegistersCount; + auto src = instr.src % RegistersCount; + ibc.type = InstructionType::IROR_R; + ibc.idst = &nreg->r[dst]; + if (src != dst) { + ibc.isrc = &nreg->r[src]; + } + else { + ibc.imm = instr.getImm32(); + ibc.isrc = &ibc.imm; + } + registerUsage[dst] = i; + return; + } + + if (opcode < RandomX_CurrentConfig.CEIL_IROL_R) { + auto dst = instr.dst % RegistersCount; + auto src = instr.src % RegistersCount; + ibc.type = InstructionType::IROL_R; + ibc.idst = &nreg->r[dst]; + if (src != dst) { + ibc.isrc = &nreg->r[src]; + } + else { + ibc.imm = instr.getImm32(); + ibc.isrc = &ibc.imm; + } + registerUsage[dst] = i; + return; + } + + if (opcode < RandomX_CurrentConfig.CEIL_ISWAP_R) { + auto dst = instr.dst % RegistersCount; + auto src = instr.src % RegistersCount; + if (src != dst) { + ibc.idst = &nreg->r[dst]; + ibc.isrc = &nreg->r[src]; + ibc.type = InstructionType::ISWAP_R; + registerUsage[dst] = i; + registerUsage[src] = i; + } + else { + ibc.type = InstructionType::NOP; + } + return; + } + + if (opcode < RandomX_CurrentConfig.CEIL_FSWAP_R) { + auto dst = instr.dst % RegistersCount; + ibc.type = InstructionType::FSWAP_R; + if (dst < RegisterCountFlt) + ibc.fdst = &nreg->f[dst]; + else + ibc.fdst = &nreg->e[dst - RegisterCountFlt]; + return; + } + + if (opcode < RandomX_CurrentConfig.CEIL_FADD_R) { + auto dst = instr.dst % RegisterCountFlt; + auto src = instr.src % RegisterCountFlt; + ibc.type = InstructionType::FADD_R; + ibc.fdst = &nreg->f[dst]; + ibc.fsrc = &nreg->a[src]; + return; + } + + if (opcode < RandomX_CurrentConfig.CEIL_FADD_M) { + auto dst = instr.dst % RegisterCountFlt; + auto src = instr.src % RegistersCount; + ibc.type = InstructionType::FADD_M; + ibc.fdst = &nreg->f[dst]; + ibc.isrc = &nreg->r[src]; + ibc.memMask = (instr.getModMem() ? ScratchpadL1Mask : ScratchpadL2Mask); + ibc.imm = signExtend2sCompl(instr.getImm32()); + return; + } + + if (opcode < RandomX_CurrentConfig.CEIL_FSUB_R) { + auto dst = instr.dst % RegisterCountFlt; + auto src = instr.src % RegisterCountFlt; + ibc.type = InstructionType::FSUB_R; + ibc.fdst = &nreg->f[dst]; + ibc.fsrc = &nreg->a[src]; + return; + } + + if (opcode < RandomX_CurrentConfig.CEIL_FSUB_M) { + auto dst = instr.dst % RegisterCountFlt; + auto src = instr.src % RegistersCount; + ibc.type = InstructionType::FSUB_M; + ibc.fdst = &nreg->f[dst]; + ibc.isrc = &nreg->r[src]; + ibc.memMask = (instr.getModMem() ? ScratchpadL1Mask : ScratchpadL2Mask); + ibc.imm = signExtend2sCompl(instr.getImm32()); + return; + } + + if (opcode < RandomX_CurrentConfig.CEIL_FSCAL_R) { + auto dst = instr.dst % RegisterCountFlt; + ibc.fdst = &nreg->f[dst]; + ibc.type = InstructionType::FSCAL_R; + return; + } + + if (opcode < RandomX_CurrentConfig.CEIL_FMUL_R) { + auto dst = instr.dst % RegisterCountFlt; + auto src = instr.src % RegisterCountFlt; + ibc.type = InstructionType::FMUL_R; + ibc.fdst = &nreg->e[dst]; + ibc.fsrc = &nreg->a[src]; + return; + } + + if (opcode < RandomX_CurrentConfig.CEIL_FDIV_M) { + auto dst = instr.dst % RegisterCountFlt; + auto src = instr.src % RegistersCount; + ibc.type = InstructionType::FDIV_M; + ibc.fdst = &nreg->e[dst]; + ibc.isrc = &nreg->r[src]; + ibc.memMask = (instr.getModMem() ? ScratchpadL1Mask : ScratchpadL2Mask); + ibc.imm = signExtend2sCompl(instr.getImm32()); + return; + } + + if (opcode < RandomX_CurrentConfig.CEIL_FSQRT_R) { + auto dst = instr.dst % RegisterCountFlt; + ibc.type = InstructionType::FSQRT_R; + ibc.fdst = &nreg->e[dst]; + return; + } + + if (opcode < RandomX_CurrentConfig.CEIL_CBRANCH) { + ibc.type = InstructionType::CBRANCH; + //jump condition + int creg = instr.dst % RegistersCount; + ibc.idst = &nreg->r[creg]; + ibc.target = registerUsage[creg]; + int shift = instr.getModCond() + RandomX_CurrentConfig.JumpOffset; + ibc.imm = signExtend2sCompl(instr.getImm32()) | (1ULL << shift); + if (RandomX_CurrentConfig.JumpOffset > 0 || shift > 0) //clear the bit below the condition mask - this limits the number of successive jumps to 2 + ibc.imm &= ~(1ULL << (shift - 1)); + ibc.memMask = RandomX_CurrentConfig.ConditionMask_Calculated << shift; + //mark all registers as used + for (unsigned j = 0; j < RegistersCount; ++j) { + registerUsage[j] = i; + } + return; + } + + if (opcode < RandomX_CurrentConfig.CEIL_CFROUND) { + auto src = instr.src % RegistersCount; + ibc.isrc = &nreg->r[src]; + ibc.type = InstructionType::CFROUND; + ibc.imm = instr.getImm32() & 63; + return; + } + + if (opcode < RandomX_CurrentConfig.CEIL_ISTORE) { + auto dst = instr.dst % RegistersCount; + auto src = instr.src % RegistersCount; + ibc.type = InstructionType::ISTORE; + ibc.idst = &nreg->r[dst]; + ibc.isrc = &nreg->r[src]; + ibc.imm = signExtend2sCompl(instr.getImm32()); + if (instr.getModCond() < StoreL3Condition) + ibc.memMask = (instr.getModMem() ? ScratchpadL1Mask : ScratchpadL2Mask); + else + ibc.memMask = ScratchpadL3Mask; + return; + } + + if (opcode < RandomX_CurrentConfig.CEIL_NOP) { + ibc.type = InstructionType::NOP; + return; + } + + UNREACHABLE; + } +} diff --git a/src/crypto/randomx/bytecode_machine.hpp b/src/crypto/randomx/bytecode_machine.hpp new file mode 100644 index 00000000..8aee78d8 --- /dev/null +++ b/src/crypto/randomx/bytecode_machine.hpp @@ -0,0 +1,288 @@ +/* +Copyright (c) 2019, tevador + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#pragma once + +#include "crypto/randomx/common.hpp" +#include "crypto/randomx/intrin_portable.h" +#include "crypto/randomx/instruction.hpp" +#include "crypto/randomx/program.hpp" + +namespace randomx { + + //register file in machine byte order + struct NativeRegisterFile { + int_reg_t r[RegistersCount] = { 0 }; + rx_vec_f128 f[RegisterCountFlt]; + rx_vec_f128 e[RegisterCountFlt]; + rx_vec_f128 a[RegisterCountFlt]; + }; + + struct InstructionByteCode { + union { + int_reg_t* idst; + rx_vec_f128* fdst; + }; + union { + const int_reg_t* isrc; + const rx_vec_f128* fsrc; + }; + union { + uint64_t imm; + int64_t simm; + }; + InstructionType type; + union { + int16_t target; + uint16_t shift; + }; + uint32_t memMask; + }; + +#define RANDOMX_EXE_ARGS InstructionByteCode& ibc, int& pc, uint8_t* scratchpad, ProgramConfiguration& config +#define RANDOMX_GEN_ARGS Instruction& instr, int i, InstructionByteCode& ibc + + class BytecodeMachine; + + typedef void(BytecodeMachine::*InstructionGenBytecode)(RANDOMX_GEN_ARGS); + + class BytecodeMachine { + public: + void beginCompilation(NativeRegisterFile& regFile) { + for (unsigned i = 0; i < RegistersCount; ++i) { + registerUsage[i] = -1; + } + nreg = ®File; + } + + void compileProgram(Program& program, InstructionByteCode* bytecode, NativeRegisterFile& regFile) { + beginCompilation(regFile); + for (unsigned i = 0; i < RandomX_CurrentConfig.ProgramSize; ++i) { + auto& instr = program(i); + auto& ibc = bytecode[i]; + compileInstruction(instr, i, ibc); + } + } + + static void executeBytecode(InstructionByteCode* bytecode, uint8_t* scratchpad, ProgramConfiguration& config) { + for (int pc = 0; pc < static_cast(RandomX_CurrentConfig.ProgramSize); ++pc) { + auto& ibc = bytecode[pc]; + executeInstruction(ibc, pc, scratchpad, config); + } + } + + void compileInstruction(RANDOMX_GEN_ARGS) +#ifdef RANDOMX_GEN_TABLE + { + auto generator = genTable[instr.opcode]; + (this->*generator)(instr, i, ibc); + } +#else + ; +#endif + + static void executeInstruction(RANDOMX_EXE_ARGS); + + static void exe_IADD_RS(RANDOMX_EXE_ARGS) { + *ibc.idst += (*ibc.isrc << ibc.shift) + ibc.imm; + } + + static void exe_IADD_M(RANDOMX_EXE_ARGS) { + *ibc.idst += load64(getScratchpadAddress(ibc, scratchpad)); + } + + static void exe_ISUB_R(RANDOMX_EXE_ARGS) { + *ibc.idst -= *ibc.isrc; + } + + static void exe_ISUB_M(RANDOMX_EXE_ARGS) { + *ibc.idst -= load64(getScratchpadAddress(ibc, scratchpad)); + } + + static void exe_IMUL_R(RANDOMX_EXE_ARGS) { + *ibc.idst *= *ibc.isrc; + } + + static void exe_IMUL_M(RANDOMX_EXE_ARGS) { + *ibc.idst *= load64(getScratchpadAddress(ibc, scratchpad)); + } + + static void exe_IMULH_R(RANDOMX_EXE_ARGS) { + *ibc.idst = mulh(*ibc.idst, *ibc.isrc); + } + + static void exe_IMULH_M(RANDOMX_EXE_ARGS) { + *ibc.idst = mulh(*ibc.idst, load64(getScratchpadAddress(ibc, scratchpad))); + } + + static void exe_ISMULH_R(RANDOMX_EXE_ARGS) { + *ibc.idst = smulh(unsigned64ToSigned2sCompl(*ibc.idst), unsigned64ToSigned2sCompl(*ibc.isrc)); + } + + static void exe_ISMULH_M(RANDOMX_EXE_ARGS) { + *ibc.idst = smulh(unsigned64ToSigned2sCompl(*ibc.idst), unsigned64ToSigned2sCompl(load64(getScratchpadAddress(ibc, scratchpad)))); + } + + static void exe_INEG_R(RANDOMX_EXE_ARGS) { + *ibc.idst = ~(*ibc.idst) + 1; //two's complement negative + } + + static void exe_IXOR_R(RANDOMX_EXE_ARGS) { + *ibc.idst ^= *ibc.isrc; + } + + static void exe_IXOR_M(RANDOMX_EXE_ARGS) { + *ibc.idst ^= load64(getScratchpadAddress(ibc, scratchpad)); + } + + static void exe_IROR_R(RANDOMX_EXE_ARGS) { + *ibc.idst = rotr64(*ibc.idst, *ibc.isrc & 63); + } + + static void exe_IROL_R(RANDOMX_EXE_ARGS) { + *ibc.idst = rotl64(*ibc.idst, *ibc.isrc & 63); + } + + static void exe_ISWAP_R(RANDOMX_EXE_ARGS) { + int_reg_t temp = *ibc.isrc; + *(int_reg_t*)ibc.isrc = *ibc.idst; + *ibc.idst = temp; + } + + static void exe_FSWAP_R(RANDOMX_EXE_ARGS) { + *ibc.fdst = rx_swap_vec_f128(*ibc.fdst); + } + + static void exe_FADD_R(RANDOMX_EXE_ARGS) { + *ibc.fdst = rx_add_vec_f128(*ibc.fdst, *ibc.fsrc); + } + + static void exe_FADD_M(RANDOMX_EXE_ARGS) { + rx_vec_f128 fsrc = rx_cvt_packed_int_vec_f128(getScratchpadAddress(ibc, scratchpad)); + *ibc.fdst = rx_add_vec_f128(*ibc.fdst, fsrc); + } + + static void exe_FSUB_R(RANDOMX_EXE_ARGS) { + *ibc.fdst = rx_sub_vec_f128(*ibc.fdst, *ibc.fsrc); + } + + static void exe_FSUB_M(RANDOMX_EXE_ARGS) { + rx_vec_f128 fsrc = rx_cvt_packed_int_vec_f128(getScratchpadAddress(ibc, scratchpad)); + *ibc.fdst = rx_sub_vec_f128(*ibc.fdst, fsrc); + } + + static void exe_FSCAL_R(RANDOMX_EXE_ARGS) { + const rx_vec_f128 mask = rx_set1_vec_f128(0x80F0000000000000); + *ibc.fdst = rx_xor_vec_f128(*ibc.fdst, mask); + } + + static void exe_FMUL_R(RANDOMX_EXE_ARGS) { + *ibc.fdst = rx_mul_vec_f128(*ibc.fdst, *ibc.fsrc); + } + + static void exe_FDIV_M(RANDOMX_EXE_ARGS) { + rx_vec_f128 fsrc = maskRegisterExponentMantissa( + config, + rx_cvt_packed_int_vec_f128(getScratchpadAddress(ibc, scratchpad)) + ); + *ibc.fdst = rx_div_vec_f128(*ibc.fdst, fsrc); + } + + static void exe_FSQRT_R(RANDOMX_EXE_ARGS) { + *ibc.fdst = rx_sqrt_vec_f128(*ibc.fdst); + } + + static void exe_CBRANCH(RANDOMX_EXE_ARGS) { + *ibc.idst += ibc.imm; + if ((*ibc.idst & ibc.memMask) == 0) { + pc = ibc.target; + } + } + + static void exe_CFROUND(RANDOMX_EXE_ARGS) { + rx_set_rounding_mode(rotr64(*ibc.isrc, ibc.imm) % 4); + } + + static void exe_ISTORE(RANDOMX_EXE_ARGS) { + store64(scratchpad + ((*ibc.idst + ibc.imm) & ibc.memMask), *ibc.isrc); + } + protected: + static rx_vec_f128 maskRegisterExponentMantissa(ProgramConfiguration& config, rx_vec_f128 x) { + const rx_vec_f128 xmantissaMask = rx_set_vec_f128(dynamicMantissaMask, dynamicMantissaMask); + const rx_vec_f128 xexponentMask = rx_load_vec_f128((const double*)&config.eMask); + x = rx_and_vec_f128(x, xmantissaMask); + x = rx_or_vec_f128(x, xexponentMask); + return x; + } + + private: + static const int_reg_t zero; + int registerUsage[RegistersCount]; + NativeRegisterFile* nreg; + + static void* getScratchpadAddress(InstructionByteCode& ibc, uint8_t* scratchpad) { + uint32_t addr = (*ibc.isrc + ibc.imm) & ibc.memMask; + return scratchpad + addr; + } + +#ifdef RANDOMX_GEN_TABLE + static InstructionGenBytecode genTable[256]; + + void gen_IADD_RS(RANDOMX_GEN_ARGS); + void gen_IADD_M(RANDOMX_GEN_ARGS); + void gen_ISUB_R(RANDOMX_GEN_ARGS); + void gen_ISUB_M(RANDOMX_GEN_ARGS); + void gen_IMUL_R(RANDOMX_GEN_ARGS); + void gen_IMUL_M(RANDOMX_GEN_ARGS); + void gen_IMULH_R(RANDOMX_GEN_ARGS); + void gen_IMULH_M(RANDOMX_GEN_ARGS); + void gen_ISMULH_R(RANDOMX_GEN_ARGS); + void gen_ISMULH_M(RANDOMX_GEN_ARGS); + void gen_IMUL_RCP(RANDOMX_GEN_ARGS); + void gen_INEG_R(RANDOMX_GEN_ARGS); + void gen_IXOR_R(RANDOMX_GEN_ARGS); + void gen_IXOR_M(RANDOMX_GEN_ARGS); + void gen_IROR_R(RANDOMX_GEN_ARGS); + void gen_IROL_R(RANDOMX_GEN_ARGS); + void gen_ISWAP_R(RANDOMX_GEN_ARGS); + void gen_FSWAP_R(RANDOMX_GEN_ARGS); + void gen_FADD_R(RANDOMX_GEN_ARGS); + void gen_FADD_M(RANDOMX_GEN_ARGS); + void gen_FSUB_R(RANDOMX_GEN_ARGS); + void gen_FSUB_M(RANDOMX_GEN_ARGS); + void gen_FSCAL_R(RANDOMX_GEN_ARGS); + void gen_FMUL_R(RANDOMX_GEN_ARGS); + void gen_FDIV_M(RANDOMX_GEN_ARGS); + void gen_FSQRT_R(RANDOMX_GEN_ARGS); + void gen_CBRANCH(RANDOMX_GEN_ARGS); + void gen_CFROUND(RANDOMX_GEN_ARGS); + void gen_ISTORE(RANDOMX_GEN_ARGS); + void gen_NOP(RANDOMX_GEN_ARGS); +#endif + }; +} diff --git a/src/crypto/randomx/common.hpp b/src/crypto/randomx/common.hpp new file mode 100644 index 00000000..da36f2c5 --- /dev/null +++ b/src/crypto/randomx/common.hpp @@ -0,0 +1,173 @@ +/* +Copyright (c) 2018-2019, tevador + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#pragma once + +#include +#include +#include +#include "crypto/randomx/blake2/endian.h" +#include "crypto/randomx/configuration.h" +#include "crypto/randomx/randomx.h" + +namespace randomx { + + //static_assert(RANDOMX_ARGON_MEMORY > 0, "RANDOMX_ARGON_MEMORY must be greater than 0."); + //static_assert((RANDOMX_ARGON_MEMORY & (RANDOMX_ARGON_MEMORY - 1)) == 0, "RANDOMX_ARGON_MEMORY must be a power of 2."); + //static_assert(RANDOMX_DATASET_BASE_SIZE >= 64, "RANDOMX_DATASET_BASE_SIZE must be at least 64."); + //static_assert((RANDOMX_DATASET_BASE_SIZE & (RANDOMX_DATASET_BASE_SIZE - 1)) == 0, "RANDOMX_DATASET_BASE_SIZE must be a power of 2."); + //static_assert(RANDOMX_DATASET_BASE_SIZE <= 4294967296ULL, "RANDOMX_DATASET_BASE_SIZE must not exceed 4294967296."); + //static_assert(RANDOMX_DATASET_EXTRA_SIZE % 64 == 0, "RANDOMX_DATASET_EXTRA_SIZE must be divisible by 64."); + //static_assert((uint64_t)RANDOMX_DATASET_BASE_SIZE + RANDOMX_DATASET_EXTRA_SIZE <= 17179869184, "Dataset size must not exceed 16 GiB."); + //static_assert(RANDOMX_PROGRAM_SIZE > 0, "RANDOMX_PROGRAM_SIZE must be greater than 0"); + //static_assert(RANDOMX_PROGRAM_ITERATIONS > 0, "RANDOMX_PROGRAM_ITERATIONS must be greater than 0"); + //static_assert(RANDOMX_PROGRAM_COUNT > 0, "RANDOMX_PROGRAM_COUNT must be greater than 0"); + //static_assert((RANDOMX_SCRATCHPAD_L3 & (RANDOMX_SCRATCHPAD_L3 - 1)) == 0, "RANDOMX_SCRATCHPAD_L3 must be a power of 2."); + //static_assert(RANDOMX_SCRATCHPAD_L3 >= RANDOMX_SCRATCHPAD_L2, "RANDOMX_SCRATCHPAD_L3 must be greater than or equal to RANDOMX_SCRATCHPAD_L2."); + //static_assert((RANDOMX_SCRATCHPAD_L2 & (RANDOMX_SCRATCHPAD_L2 - 1)) == 0, "RANDOMX_SCRATCHPAD_L2 must be a power of 2."); + //static_assert(RANDOMX_SCRATCHPAD_L2 >= RANDOMX_SCRATCHPAD_L1, "RANDOMX_SCRATCHPAD_L2 must be greater than or equal to RANDOMX_SCRATCHPAD_L1."); + //static_assert(RANDOMX_SCRATCHPAD_L1 >= 64, "RANDOMX_SCRATCHPAD_L1 must be at least 64."); + //static_assert((RANDOMX_SCRATCHPAD_L1 & (RANDOMX_SCRATCHPAD_L1 - 1)) == 0, "RANDOMX_SCRATCHPAD_L1 must be a power of 2."); + //static_assert(RANDOMX_CACHE_ACCESSES > 1, "RANDOMX_CACHE_ACCESSES must be greater than 1"); + //static_assert(RANDOMX_SUPERSCALAR_LATENCY > 0, "RANDOMX_SUPERSCALAR_LATENCY must be greater than 0"); + //static_assert(RANDOMX_JUMP_BITS > 0, "RANDOMX_JUMP_BITS must be greater than 0."); + //static_assert(RANDOMX_JUMP_OFFSET >= 0, "RANDOMX_JUMP_OFFSET must be greater than or equal to 0."); + //static_assert(RANDOMX_JUMP_BITS + RANDOMX_JUMP_OFFSET <= 16, "RANDOMX_JUMP_BITS + RANDOMX_JUMP_OFFSET must not exceed 16."); + + /*constexpr int wtSum = RANDOMX_FREQ_IADD_RS + RANDOMX_FREQ_IADD_M + RANDOMX_FREQ_ISUB_R + \ + RANDOMX_FREQ_ISUB_M + RANDOMX_FREQ_IMUL_R + RANDOMX_FREQ_IMUL_M + RANDOMX_FREQ_IMULH_R + \ + RANDOMX_FREQ_IMULH_M + RANDOMX_FREQ_ISMULH_R + RANDOMX_FREQ_ISMULH_M + RANDOMX_FREQ_IMUL_RCP + \ + RANDOMX_FREQ_INEG_R + RANDOMX_FREQ_IXOR_R + RANDOMX_FREQ_IXOR_M + RANDOMX_FREQ_IROR_R + RANDOMX_FREQ_IROL_R + RANDOMX_FREQ_ISWAP_R + \ + RANDOMX_FREQ_FSWAP_R + RANDOMX_FREQ_FADD_R + RANDOMX_FREQ_FADD_M + RANDOMX_FREQ_FSUB_R + RANDOMX_FREQ_FSUB_M + \ + RANDOMX_FREQ_FSCAL_R + RANDOMX_FREQ_FMUL_R + RANDOMX_FREQ_FDIV_M + RANDOMX_FREQ_FSQRT_R + RANDOMX_FREQ_CBRANCH + \ + RANDOMX_FREQ_CFROUND + RANDOMX_FREQ_ISTORE + RANDOMX_FREQ_NOP;*/ + + //static_assert(wtSum == 256, "Sum of instruction frequencies must be 256."); + + + constexpr uint32_t ArgonBlockSize = 1024; + constexpr int SuperscalarMaxSize = 3 * RANDOMX_SUPERSCALAR_MAX_LATENCY + 2; + constexpr size_t CacheLineSize = RANDOMX_DATASET_ITEM_SIZE; + #define ScratchpadSize RandomX_CurrentConfig.ScratchpadL3_Size + #define CacheLineAlignMask RandomX_CurrentConfig.CacheLineAlignMask_Calculated + #define DatasetExtraItems RandomX_CurrentConfig.DatasetExtraItems_Calculated + constexpr int StoreL3Condition = 14; + + //Prevent some unsafe configurations. +#ifndef RANDOMX_UNSAFE + //static_assert((uint64_t)ArgonBlockSize * RANDOMX_CACHE_ACCESSES * RANDOMX_ARGON_MEMORY + 33554432 >= (uint64_t)RANDOMX_DATASET_BASE_SIZE + RANDOMX_DATASET_EXTRA_SIZE, "Unsafe configuration: Memory-time tradeoffs"); + //static_assert((128 + RANDOMX_PROGRAM_SIZE * RANDOMX_FREQ_ISTORE / 256) * (RANDOMX_PROGRAM_COUNT * RANDOMX_PROGRAM_ITERATIONS) >= RANDOMX_SCRATCHPAD_L3, "Unsafe configuration: Insufficient Scratchpad writes"); + //static_assert(RANDOMX_PROGRAM_COUNT > 1, "Unsafe configuration: Program filtering strategies"); + //static_assert(RANDOMX_PROGRAM_SIZE >= 64, "Unsafe configuration: Low program entropy"); + //static_assert(RANDOMX_PROGRAM_ITERATIONS >= 400, "Unsafe configuration: High compilation overhead"); +#endif + +#ifdef TRACE + constexpr bool trace = true; +#else + constexpr bool trace = false; +#endif + +#ifndef UNREACHABLE +#ifdef __GNUC__ +#define UNREACHABLE __builtin_unreachable() +#elif _MSC_VER +#define UNREACHABLE __assume(false) +#else +#define UNREACHABLE +#endif +#endif + +#if defined(_M_X64) || defined(__x86_64__) + #define RANDOMX_HAVE_COMPILER 1 + class JitCompilerX86; + using JitCompiler = JitCompilerX86; +#elif defined(__aarch64__) + #define RANDOMX_HAVE_COMPILER 0 + class JitCompilerA64; + using JitCompiler = JitCompilerA64; +#else + #define RANDOMX_HAVE_COMPILER 0 + class JitCompilerFallback; + using JitCompiler = JitCompilerFallback; +#endif + + using addr_t = uint32_t; + + using int_reg_t = uint64_t; + + struct fpu_reg_t { + double lo; + double hi; + }; + + #define ScratchpadL1Mask RandomX_CurrentConfig.ScratchpadL1Mask_Calculated + #define ScratchpadL1Mask16 RandomX_CurrentConfig.ScratchpadL1Mask16_Calculated + #define ScratchpadL2Mask RandomX_CurrentConfig.ScratchpadL2Mask_Calculated + #define ScratchpadL2Mask16 RandomX_CurrentConfig.ScratchpadL2Mask16_Calculated + #define ScratchpadL3Mask RandomX_CurrentConfig.ScratchpadL3Mask_Calculated + #define ScratchpadL3Mask64 RandomX_CurrentConfig.ScratchpadL3Mask64_Calculated + constexpr int RegistersCount = 8; + constexpr int RegisterCountFlt = RegistersCount / 2; + constexpr int RegisterNeedsDisplacement = 5; //x86 r13 register + constexpr int RegisterNeedsSib = 4; //x86 r12 register + + inline bool isZeroOrPowerOf2(uint64_t x) { + return (x & (x - 1)) == 0; + } + + constexpr int mantissaSize = 52; + constexpr int exponentSize = 11; + constexpr uint64_t mantissaMask = (1ULL << mantissaSize) - 1; + constexpr uint64_t exponentMask = (1ULL << exponentSize) - 1; + constexpr int exponentBias = 1023; + constexpr int dynamicExponentBits = 4; + constexpr int staticExponentBits = 4; + constexpr uint64_t constExponentBits = 0x300; + constexpr uint64_t dynamicMantissaMask = (1ULL << (mantissaSize + dynamicExponentBits)) - 1; + + struct MemoryRegisters { + addr_t mx, ma; + uint8_t* memory = nullptr; + }; + + //register file in little-endian byte order + struct RegisterFile { + int_reg_t r[RegistersCount]; + fpu_reg_t f[RegisterCountFlt]; + fpu_reg_t e[RegisterCountFlt]; + fpu_reg_t a[RegisterCountFlt]; + }; + + typedef void(ProgramFunc)(RegisterFile&, MemoryRegisters&, uint8_t* /* scratchpad */, uint64_t); + typedef void(DatasetInitFunc)(randomx_cache* cache, uint8_t* dataset, uint32_t startBlock, uint32_t endBlock); + + typedef void(DatasetDeallocFunc)(randomx_dataset*); + typedef void(CacheDeallocFunc)(randomx_cache*); + typedef void(CacheInitializeFunc)(randomx_cache*, const void*, size_t); +} diff --git a/src/crypto/randomx/configuration.h b/src/crypto/randomx/configuration.h new file mode 100644 index 00000000..678cb2f8 --- /dev/null +++ b/src/crypto/randomx/configuration.h @@ -0,0 +1,47 @@ +/* +Copyright (c) 2018-2019, tevador + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#pragma once + +// Increase it if some configs use more cache accesses +#define RANDOMX_CACHE_MAX_ACCESSES 16 + +// Increase it if some configs use larger superscalar latency +#define RANDOMX_SUPERSCALAR_MAX_LATENCY 256 + +// Increase it if some configs use larger cache +#define RANDOMX_CACHE_MAX_SIZE 268435456 + +// Increase it if some configs use larger dataset +#define RANDOMX_DATASET_MAX_SIZE 2181038080 + +// Increase it if some configs use larger programs +#define RANDOMX_PROGRAM_MAX_SIZE 512 + +// Increase it if some configs use larger scratchpad +#define RANDOMX_SCRATCHPAD_L3_MAX_SIZE 2097152 diff --git a/src/crypto/randomx/dataset.cpp b/src/crypto/randomx/dataset.cpp new file mode 100644 index 00000000..8aeafffd --- /dev/null +++ b/src/crypto/randomx/dataset.cpp @@ -0,0 +1,189 @@ +/* +Copyright (c) 2018-2019, tevador + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* Original code from Argon2 reference source code package used under CC0 Licence + * https://github.com/P-H-C/phc-winner-argon2 + * Copyright 2015 + * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves +*/ + +#include +#include +#include +#include +#include +#include + +#include "crypto/randomx/common.hpp" +#include "crypto/randomx/dataset.hpp" +#include "crypto/randomx/virtual_memory.hpp" +#include "crypto/randomx/superscalar.hpp" +#include "crypto/randomx/blake2_generator.hpp" +#include "crypto/randomx/reciprocal.h" +#include "crypto/randomx/blake2/endian.h" +#include "crypto/randomx/argon2.h" +#include "crypto/randomx/argon2_core.h" +#include "crypto/randomx/jit_compiler.hpp" +#include "crypto/randomx/intrin_portable.h" + +//static_assert(RANDOMX_ARGON_MEMORY % (RANDOMX_ARGON_LANES * ARGON2_SYNC_POINTS) == 0, "RANDOMX_ARGON_MEMORY - invalid value"); +static_assert(ARGON2_BLOCK_SIZE == randomx::ArgonBlockSize, "Unpexpected value of ARGON2_BLOCK_SIZE"); + +namespace randomx { + + template + void deallocCache(randomx_cache* cache) { + if (cache->memory != nullptr) + Allocator::freeMemory(cache->memory, RANDOMX_CACHE_MAX_SIZE); + if (cache->jit != nullptr) + delete cache->jit; + } + + template void deallocCache(randomx_cache* cache); + template void deallocCache(randomx_cache* cache); + + void initCache(randomx_cache* cache, const void* key, size_t keySize) { + uint32_t memory_blocks, segment_length; + argon2_instance_t instance; + argon2_context context; + + context.out = nullptr; + context.outlen = 0; + context.pwd = CONST_CAST(uint8_t *)key; + context.pwdlen = (uint32_t)keySize; + context.salt = CONST_CAST(uint8_t *)RandomX_CurrentConfig.ArgonSalt; + context.saltlen = (uint32_t)strlen(RandomX_CurrentConfig.ArgonSalt); + context.secret = NULL; + context.secretlen = 0; + context.ad = NULL; + context.adlen = 0; + context.t_cost = RandomX_CurrentConfig.ArgonIterations; + context.m_cost = RandomX_CurrentConfig.ArgonMemory; + context.lanes = RandomX_CurrentConfig.ArgonLanes; + context.threads = 1; + context.allocate_cbk = NULL; + context.free_cbk = NULL; + context.flags = ARGON2_DEFAULT_FLAGS; + context.version = ARGON2_VERSION_NUMBER; + + /* 2. Align memory size */ + /* Minimum memory_blocks = 8L blocks, where L is the number of lanes */ + memory_blocks = context.m_cost; + + segment_length = memory_blocks / (context.lanes * ARGON2_SYNC_POINTS); + + instance.version = context.version; + instance.memory = NULL; + instance.passes = context.t_cost; + instance.memory_blocks = memory_blocks; + instance.segment_length = segment_length; + instance.lane_length = segment_length * ARGON2_SYNC_POINTS; + instance.lanes = context.lanes; + instance.threads = context.threads; + instance.type = Argon2_d; + instance.memory = (block*)cache->memory; + + if (instance.threads > instance.lanes) { + instance.threads = instance.lanes; + } + + /* 3. Initialization: Hashing inputs, allocating memory, filling first + * blocks + */ + rxa2_argon_initialize(&instance, &context); + + rxa2_fill_memory_blocks(&instance); + + cache->reciprocalCache.clear(); + randomx::Blake2Generator gen(key, keySize); + for (uint32_t i = 0; i < RandomX_CurrentConfig.CacheAccesses; ++i) { + randomx::generateSuperscalar(cache->programs[i], gen); + for (unsigned j = 0; j < cache->programs[i].getSize(); ++j) { + auto& instr = cache->programs[i](j); + if ((SuperscalarInstructionType)instr.opcode == SuperscalarInstructionType::IMUL_RCP) { + auto rcp = randomx_reciprocal(instr.getImm32()); + instr.setImm32(cache->reciprocalCache.size()); + cache->reciprocalCache.push_back(rcp); + } + } + } + } + + void initCacheCompile(randomx_cache* cache, const void* key, size_t keySize) { + initCache(cache, key, keySize); + cache->jit->generateSuperscalarHash(cache->programs, cache->reciprocalCache); + cache->jit->generateDatasetInitCode(); + } + + constexpr uint64_t superscalarMul0 = 6364136223846793005ULL; + constexpr uint64_t superscalarAdd1 = 9298411001130361340ULL; + constexpr uint64_t superscalarAdd2 = 12065312585734608966ULL; + constexpr uint64_t superscalarAdd3 = 9306329213124626780ULL; + constexpr uint64_t superscalarAdd4 = 5281919268842080866ULL; + constexpr uint64_t superscalarAdd5 = 10536153434571861004ULL; + constexpr uint64_t superscalarAdd6 = 3398623926847679864ULL; + constexpr uint64_t superscalarAdd7 = 9549104520008361294ULL; + + static inline uint8_t* getMixBlock(uint64_t registerValue, uint8_t *memory) { + const uint32_t mask = (RandomX_CurrentConfig.ArgonMemory * randomx::ArgonBlockSize) / CacheLineSize - 1; + return memory + (registerValue & mask) * CacheLineSize; + } + + void initDatasetItem(randomx_cache* cache, uint8_t* out, uint64_t itemNumber) { + int_reg_t rl[8]; + uint8_t* mixBlock; + uint64_t registerValue = itemNumber; + rl[0] = (itemNumber + 1) * superscalarMul0; + rl[1] = rl[0] ^ superscalarAdd1; + rl[2] = rl[0] ^ superscalarAdd2; + rl[3] = rl[0] ^ superscalarAdd3; + rl[4] = rl[0] ^ superscalarAdd4; + rl[5] = rl[0] ^ superscalarAdd5; + rl[6] = rl[0] ^ superscalarAdd6; + rl[7] = rl[0] ^ superscalarAdd7; + for (unsigned i = 0; i < RandomX_CurrentConfig.CacheAccesses; ++i) { + mixBlock = getMixBlock(registerValue, cache->memory); + rx_prefetch_nta(mixBlock); + SuperscalarProgram& prog = cache->programs[i]; + + executeSuperscalar(rl, prog, &cache->reciprocalCache); + + for (unsigned q = 0; q < 8; ++q) + rl[q] ^= load64_native(mixBlock + 8 * q); + + registerValue = rl[prog.getAddressRegister()]; + } + + memcpy(out, &rl, CacheLineSize); + } + + void initDataset(randomx_cache* cache, uint8_t* dataset, uint32_t startItem, uint32_t endItem) { + for (uint32_t itemNumber = startItem; itemNumber < endItem; ++itemNumber, dataset += CacheLineSize) + initDatasetItem(cache, dataset, itemNumber); + } +} diff --git a/src/crypto/randomx/dataset.hpp b/src/crypto/randomx/dataset.hpp new file mode 100644 index 00000000..6c179b5d --- /dev/null +++ b/src/crypto/randomx/dataset.hpp @@ -0,0 +1,80 @@ +/* +Copyright (c) 2018-2019, tevador + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#pragma once + +#include +#include +#include +#include "crypto/randomx/common.hpp" +#include "crypto/randomx/superscalar_program.hpp" +#include "crypto/randomx/allocator.hpp" + +/* Global scope for C binding */ +struct randomx_dataset { + uint8_t* memory = nullptr; + randomx::DatasetDeallocFunc* dealloc; +}; + +/* Global scope for C binding */ +struct randomx_cache { + uint8_t* memory = nullptr; + randomx::CacheDeallocFunc* dealloc; + randomx::JitCompiler* jit; + randomx::CacheInitializeFunc* initialize; + randomx::DatasetInitFunc* datasetInit; + randomx::SuperscalarProgram programs[RANDOMX_CACHE_MAX_ACCESSES]; + std::vector reciprocalCache; + + bool isInitialized() { + return programs[0].getSize() != 0; + } +}; + +//A pointer to a standard-layout struct object points to its initial member +static_assert(std::is_standard_layout(), "randomx_dataset must be a standard-layout struct"); +static_assert(std::is_standard_layout(), "randomx_cache must be a standard-layout struct"); + +namespace randomx { + + using DefaultAllocator = AlignedAllocator; + + template + void deallocDataset(randomx_dataset* dataset) { + if (dataset->memory != nullptr) + Allocator::freeMemory(dataset->memory, RANDOMX_DATASET_MAX_SIZE); + } + + template + void deallocCache(randomx_cache* cache); + + void initCache(randomx_cache*, const void*, size_t); + void initCacheCompile(randomx_cache*, const void*, size_t); + void initDatasetItem(randomx_cache* cache, uint8_t* out, uint64_t blockNumber); + void initDataset(randomx_cache* cache, uint8_t* dataset, uint32_t startBlock, uint32_t endBlock); +} diff --git a/src/crypto/randomx/instruction.hpp b/src/crypto/randomx/instruction.hpp new file mode 100644 index 00000000..446ebfa8 --- /dev/null +++ b/src/crypto/randomx/instruction.hpp @@ -0,0 +1,102 @@ +/* +Copyright (c) 2018-2019, tevador + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#pragma once + +#include +#include +#include "crypto/randomx/blake2/endian.h" + +namespace randomx { + + class Instruction; + + enum class InstructionType : uint16_t { + IADD_RS = 0, + IADD_M = 1, + ISUB_R = 2, + ISUB_M = 3, + IMUL_R = 4, + IMUL_M = 5, + IMULH_R = 6, + IMULH_M = 7, + ISMULH_R = 8, + ISMULH_M = 9, + IMUL_RCP = 10, + INEG_R = 11, + IXOR_R = 12, + IXOR_M = 13, + IROR_R = 14, + IROL_R = 15, + ISWAP_R = 16, + FSWAP_R = 17, + FADD_R = 18, + FADD_M = 19, + FSUB_R = 20, + FSUB_M = 21, + FSCAL_R = 22, + FMUL_R = 23, + FDIV_M = 24, + FSQRT_R = 25, + CBRANCH = 26, + CFROUND = 27, + ISTORE = 28, + NOP = 29, + }; + + class Instruction { + public: + uint32_t getImm32() const { + return load32(&imm32); + } + void setImm32(uint32_t val) { + return store32(&imm32, val); + } + uint32_t getModMem() const { + return mod & 3; //bits 0-1 + } + uint32_t getModShift() const { + return (mod >> 2) & 3; //bits 2-3 + } + uint32_t getModCond() const { + return mod >> 4; //bits 4-7 + } + void setMod(uint8_t val) { + mod = val; + } + + uint8_t opcode; + uint8_t dst; + uint8_t src; + uint8_t mod; + uint32_t imm32; + }; + + static_assert(sizeof(Instruction) == 8, "Invalid size of struct randomx::Instruction"); + static_assert(std::is_standard_layout(), "randomx::Instruction must be a standard-layout struct"); +} diff --git a/src/crypto/randomx/instructions_portable.cpp b/src/crypto/randomx/instructions_portable.cpp new file mode 100644 index 00000000..b28203a9 --- /dev/null +++ b/src/crypto/randomx/instructions_portable.cpp @@ -0,0 +1,193 @@ +/* +Copyright (c) 2018-2019, tevador + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include "crypto/randomx/common.hpp" +#include "crypto/randomx/intrin_portable.h" +#include "crypto/randomx/blake2/endian.h" + +#if defined(__SIZEOF_INT128__) + typedef unsigned __int128 uint128_t; + typedef __int128 int128_t; + uint64_t mulh(uint64_t a, uint64_t b) { + return ((uint128_t)a * b) >> 64; + } + int64_t smulh(int64_t a, int64_t b) { + return ((int128_t)a * b) >> 64; + } + #define HAVE_MULH + #define HAVE_SMULH +#endif + +#if defined(_MSC_VER) + #define HAS_VALUE(X) X ## 0 + #define EVAL_DEFINE(X) HAS_VALUE(X) + #include + #include + + uint64_t rotl64(uint64_t x, unsigned int c) { + return _rotl64(x, c); + } + uint64_t rotr64(uint64_t x, unsigned int c) { + return _rotr64(x, c); + } + #define HAVE_ROTL64 + #define HAVE_ROTR64 + + #if EVAL_DEFINE(__MACHINEARM64_X64(1)) + uint64_t mulh(uint64_t a, uint64_t b) { + return __umulh(a, b); + } + #define HAVE_MULH + #endif + + #if EVAL_DEFINE(__MACHINEX64(1)) + int64_t smulh(int64_t a, int64_t b) { + int64_t hi; + _mul128(a, b, &hi); + return hi; + } + #define HAVE_SMULH + #endif + + static void setRoundMode_(uint32_t mode) { + _controlfp(mode, _MCW_RC); + } + #define HAVE_SETROUNDMODE_IMPL +#endif + +#ifndef HAVE_ROTR64 + uint64_t rotr64(uint64_t a, unsigned int b) { + return (a >> b) | (a << (-b & 63)); + } + #define HAVE_ROTR64 +#endif + +#ifndef HAVE_ROTL64 + uint64_t rotl64(uint64_t a, unsigned int b) { + return (a << b) | (a >> (-b & 63)); + } + #define HAVE_ROTL64 +#endif + +#ifndef HAVE_MULH + #define LO(x) ((x)&0xffffffff) + #define HI(x) ((x)>>32) + uint64_t mulh(uint64_t a, uint64_t b) { + uint64_t ah = HI(a), al = LO(a); + uint64_t bh = HI(b), bl = LO(b); + uint64_t x00 = al * bl; + uint64_t x01 = al * bh; + uint64_t x10 = ah * bl; + uint64_t x11 = ah * bh; + uint64_t m1 = LO(x10) + LO(x01) + HI(x00); + uint64_t m2 = HI(x10) + HI(x01) + LO(x11) + HI(m1); + uint64_t m3 = HI(x11) + HI(m2); + + return (m3 << 32) + LO(m2); + } + #define HAVE_MULH +#endif + +#ifndef HAVE_SMULH + int64_t smulh(int64_t a, int64_t b) { + int64_t hi = mulh(a, b); + if (a < 0LL) hi -= b; + if (b < 0LL) hi -= a; + return hi; + } + #define HAVE_SMULH +#endif + +#ifdef RANDOMX_DEFAULT_FENV + +# ifndef HAVE_SETROUNDMODE_IMPL + static void setRoundMode_(uint32_t mode) { + fesetround(mode); + } +# endif + +void rx_reset_float_state() { + setRoundMode_(FE_TONEAREST); + rx_set_double_precision(); //set precision to 53 bits if needed by the platform +} + +void rx_set_rounding_mode(uint32_t mode) { + switch (mode & 3) { + case RoundDown: + setRoundMode_(FE_DOWNWARD); + break; + case RoundUp: + setRoundMode_(FE_UPWARD); + break; + case RoundToZero: + setRoundMode_(FE_TOWARDZERO); + break; + case RoundToNearest: + setRoundMode_(FE_TONEAREST); + break; + default: + UNREACHABLE; + } +} + +#endif + +#ifdef RANDOMX_USE_X87 + +#ifdef _M_IX86 + +void rx_set_double_precision() { + _control87(_PC_53, _MCW_PC); +} + +#elif defined(__i386) + +void rx_set_double_precision() { + uint16_t volatile x87cw; + asm volatile("fstcw %0" : "=m" (x87cw)); + x87cw &= ~0x300; + x87cw |= 0x200; + asm volatile("fldcw %0" : : "m" (x87cw)); +} + +#endif + +#endif //RANDOMX_USE_X87 + +union double_ser_t { + double f; + uint64_t i; +}; + +double loadDoublePortable(const void* addr) { + double_ser_t ds; + ds.i = load64(addr); + return ds.f; +} diff --git a/src/crypto/randomx/intrin_portable.h b/src/crypto/randomx/intrin_portable.h new file mode 100644 index 00000000..e4916096 --- /dev/null +++ b/src/crypto/randomx/intrin_portable.h @@ -0,0 +1,605 @@ +/* +Copyright (c) 2018-2019, tevador + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#pragma once + +#include +#include "crypto/randomx/blake2/endian.h" + +constexpr int32_t unsigned32ToSigned2sCompl(uint32_t x) { + return (-1 == ~0) ? (int32_t)x : (x > INT32_MAX ? (-(int32_t)(UINT32_MAX - x) - 1) : (int32_t)x); +} + +constexpr int64_t unsigned64ToSigned2sCompl(uint64_t x) { + return (-1 == ~0) ? (int64_t)x : (x > INT64_MAX ? (-(int64_t)(UINT64_MAX - x) - 1) : (int64_t)x); +} + +constexpr uint64_t signExtend2sCompl(uint32_t x) { + return (-1 == ~0) ? (int64_t)(int32_t)(x) : (x > INT32_MAX ? (x | 0xffffffff00000000ULL) : (uint64_t)x); +} + +constexpr int RoundToNearest = 0; +constexpr int RoundDown = 1; +constexpr int RoundUp = 2; +constexpr int RoundToZero = 3; + +//MSVC doesn't define __SSE2__, so we have to define it manually if SSE2 is available +#if !defined(__SSE2__) && (defined(_M_X64) || (defined(_M_IX86_FP) && _M_IX86_FP == 2)) +#define __SSE2__ 1 +#endif + +//MSVC doesn't define __AES__ +#if defined(_MSC_VER) && defined(__SSE2__) +#define __AES__ +#endif + +//the library "sqrt" function provided by MSVC for x86 targets doesn't give +//the correct results, so we have to use inline assembly to call x87 fsqrt directly +#if !defined(__SSE2__) +#if defined(_M_IX86) +inline double __cdecl rx_sqrt(double x) { + __asm { + fld x + fsqrt + } +} +#define rx_sqrt rx_sqrt + +void rx_set_double_precision(); +#define RANDOMX_USE_X87 + +#elif defined(__i386) + +void rx_set_double_precision(); +#define RANDOMX_USE_X87 + +#endif +#endif //__SSE2__ + +#if !defined(rx_sqrt) +#define rx_sqrt sqrt +#endif + +#if !defined(RANDOMX_USE_X87) +#define rx_set_double_precision(x) +#endif + +#ifdef __SSE2__ +#ifdef __GNUC__ +#include +#else +#include +#endif + +typedef __m128i rx_vec_i128; +typedef __m128d rx_vec_f128; + +#define rx_aligned_alloc(a, b) _mm_malloc(a,b) +#define rx_aligned_free(a) _mm_free(a) +#define rx_prefetch_nta(x) _mm_prefetch((const char *)(x), _MM_HINT_NTA) + +#define rx_load_vec_f128 _mm_load_pd +#define rx_store_vec_f128 _mm_store_pd +#define rx_add_vec_f128 _mm_add_pd +#define rx_sub_vec_f128 _mm_sub_pd +#define rx_mul_vec_f128 _mm_mul_pd +#define rx_div_vec_f128 _mm_div_pd +#define rx_sqrt_vec_f128 _mm_sqrt_pd + +FORCE_INLINE rx_vec_f128 rx_swap_vec_f128(rx_vec_f128 a) { + return _mm_shuffle_pd(a, a, 1); +} + +FORCE_INLINE rx_vec_f128 rx_set_vec_f128(uint64_t x1, uint64_t x0) { + return _mm_castsi128_pd(_mm_set_epi64x(x1, x0)); +} + +FORCE_INLINE rx_vec_f128 rx_set1_vec_f128(uint64_t x) { + return _mm_castsi128_pd(_mm_set1_epi64x(x)); +} + +#define rx_xor_vec_f128 _mm_xor_pd +#define rx_and_vec_f128 _mm_and_pd +#define rx_or_vec_f128 _mm_or_pd + +#ifdef __AES__ + +#define rx_aesenc_vec_i128 _mm_aesenc_si128 +#define rx_aesdec_vec_i128 _mm_aesdec_si128 + +#define HAVE_AES + +#endif //__AES__ + +FORCE_INLINE int rx_vec_i128_x(rx_vec_i128 a) { + return _mm_cvtsi128_si32(a); +} + +FORCE_INLINE int rx_vec_i128_y(rx_vec_i128 a) { + return _mm_cvtsi128_si32(_mm_shuffle_epi32(a, 0x55)); +} + +FORCE_INLINE int rx_vec_i128_z(rx_vec_i128 a) { + return _mm_cvtsi128_si32(_mm_shuffle_epi32(a, 0xaa)); +} + +FORCE_INLINE int rx_vec_i128_w(rx_vec_i128 a) { + return _mm_cvtsi128_si32(_mm_shuffle_epi32(a, 0xff)); +} + +#define rx_set_int_vec_i128 _mm_set_epi32 +#define rx_xor_vec_i128 _mm_xor_si128 +#define rx_load_vec_i128 _mm_load_si128 +#define rx_store_vec_i128 _mm_store_si128 + +FORCE_INLINE rx_vec_f128 rx_cvt_packed_int_vec_f128(const void* addr) { + __m128i ix = _mm_loadl_epi64((const __m128i*)addr); + return _mm_cvtepi32_pd(ix); +} + +constexpr uint32_t rx_mxcsr_default = 0x9FC0; //Flush to zero, denormals are zero, default rounding mode, all exceptions disabled + +FORCE_INLINE void rx_reset_float_state() { + _mm_setcsr(rx_mxcsr_default); +} + +FORCE_INLINE void rx_set_rounding_mode(uint32_t mode) { + _mm_setcsr(rx_mxcsr_default | (mode << 13)); +} + +#elif defined(__PPC64__) && defined(__ALTIVEC__) && defined(__VSX__) //sadly only POWER7 and newer will be able to use SIMD acceleration. Earlier processors cant use doubles or 64 bit integers with SIMD +#include +#include +#include +#include +#undef vector +#undef pixel +#undef bool + +typedef __vector uint8_t __m128i; +typedef __vector uint32_t __m128l; +typedef __vector int __m128li; +typedef __vector uint64_t __m128ll; +typedef __vector double __m128d; + +typedef __m128i rx_vec_i128; +typedef __m128d rx_vec_f128; +typedef union{ + rx_vec_i128 i; + rx_vec_f128 d; + uint64_t u64[2]; + double d64[2]; + uint32_t u32[4]; + int i32[4]; +} vec_u; + +#define rx_aligned_alloc(a, b) malloc(a) +#define rx_aligned_free(a) free(a) +#define rx_prefetch_nta(x) + +/* Splat 64-bit long long to 2 64-bit long longs */ +FORCE_INLINE __m128i vec_splat2sd (int64_t scalar) +{ return (__m128i) vec_splats (scalar); } + +FORCE_INLINE rx_vec_f128 rx_load_vec_f128(const double* pd) { +#if defined(NATIVE_LITTLE_ENDIAN) + return (rx_vec_f128)vec_vsx_ld(0,pd); +#else + vec_u t; + t.u64[0] = load64(pd + 0); + t.u64[1] = load64(pd + 1); + return (rx_vec_f128)t.d; +#endif +} + +FORCE_INLINE void rx_store_vec_f128(double* mem_addr, rx_vec_f128 a) { +#if defined(NATIVE_LITTLE_ENDIAN) + vec_vsx_st(a,0,(rx_vec_f128*)mem_addr); +#else + vec_u _a; + _a.d = a; + store64(mem_addr + 0, _a.u64[0]); + store64(mem_addr + 1, _a.u64[1]); +#endif +} + +FORCE_INLINE rx_vec_f128 rx_swap_vec_f128(rx_vec_f128 a) { + return (rx_vec_f128)vec_perm((__m128i)a,(__m128i)a,(__m128i){8,9,10,11,12,13,14,15,0,1,2,3,4,5,6,7}); +} + +FORCE_INLINE rx_vec_f128 rx_add_vec_f128(rx_vec_f128 a, rx_vec_f128 b) { + return (rx_vec_f128)vec_add(a,b); +} + +FORCE_INLINE rx_vec_f128 rx_sub_vec_f128(rx_vec_f128 a, rx_vec_f128 b) { + return (rx_vec_f128)vec_sub(a,b); +} + +FORCE_INLINE rx_vec_f128 rx_mul_vec_f128(rx_vec_f128 a, rx_vec_f128 b) { + return (rx_vec_f128)vec_mul(a,b); +} + +FORCE_INLINE rx_vec_f128 rx_div_vec_f128(rx_vec_f128 a, rx_vec_f128 b) { + return (rx_vec_f128)vec_div(a,b); +} + +FORCE_INLINE rx_vec_f128 rx_sqrt_vec_f128(rx_vec_f128 a) { + return (rx_vec_f128)vec_sqrt(a); +} + +FORCE_INLINE rx_vec_i128 rx_set1_long_vec_i128(uint64_t a) { + return (rx_vec_i128)vec_splat2sd(a); +} + +FORCE_INLINE rx_vec_f128 rx_vec_i128_vec_f128(rx_vec_i128 a) { + return (rx_vec_f128)a; +} + +FORCE_INLINE rx_vec_f128 rx_set_vec_f128(uint64_t x1, uint64_t x0) { + return (rx_vec_f128)(__m128ll){x0,x1}; +} + +FORCE_INLINE rx_vec_f128 rx_set1_vec_f128(uint64_t x) { + return (rx_vec_f128)vec_splat2sd(x); +} + +FORCE_INLINE rx_vec_f128 rx_xor_vec_f128(rx_vec_f128 a, rx_vec_f128 b) { + return (rx_vec_f128)vec_xor(a,b); +} + +FORCE_INLINE rx_vec_f128 rx_and_vec_f128(rx_vec_f128 a, rx_vec_f128 b) { + return (rx_vec_f128)vec_and(a,b); +} + +FORCE_INLINE rx_vec_f128 rx_or_vec_f128(rx_vec_f128 a, rx_vec_f128 b) { + return (rx_vec_f128)vec_or(a,b); +} + +#if defined(__CRYPTO__) + +FORCE_INLINE __m128ll vrev(__m128i v){ +#if defined(NATIVE_LITTLE_ENDIAN) + return (__m128ll)vec_perm((__m128i)v,(__m128i){0},(__m128i){15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0}); +#else + return (__m128ll)vec_perm((__m128i)v,(__m128i){0},(__m128i){3,2,1,0, 7,6,5,4, 11,10,9,8, 15,14,13,12}); +#endif +} + +FORCE_INLINE rx_vec_i128 rx_aesenc_vec_i128(rx_vec_i128 v, rx_vec_i128 rkey) { + __m128ll _v = vrev(v); + __m128ll _rkey = vrev(rkey); + __m128ll result = vrev((__m128i)__builtin_crypto_vcipher(_v,_rkey)); + return (rx_vec_i128)result; +} + +FORCE_INLINE rx_vec_i128 rx_aesdec_vec_i128(rx_vec_i128 v, rx_vec_i128 rkey) { + __m128ll _v = vrev(v); + __m128ll zero = (__m128ll){0}; + __m128ll out = vrev((__m128i)__builtin_crypto_vncipher(_v,zero)); + return (rx_vec_i128)vec_xor((__m128i)out,rkey); +} +#define HAVE_AES + +#endif //__CRYPTO__ + +FORCE_INLINE int rx_vec_i128_x(rx_vec_i128 a) { + vec_u _a; + _a.i = a; + return _a.i32[0]; +} + +FORCE_INLINE int rx_vec_i128_y(rx_vec_i128 a) { + vec_u _a; + _a.i = a; + return _a.i32[1]; +} + +FORCE_INLINE int rx_vec_i128_z(rx_vec_i128 a) { + vec_u _a; + _a.i = a; + return _a.i32[2]; +} + +FORCE_INLINE int rx_vec_i128_w(rx_vec_i128 a) { + vec_u _a; + _a.i = a; + return _a.i32[3]; +} + +FORCE_INLINE rx_vec_i128 rx_set_int_vec_i128(int _I3, int _I2, int _I1, int _I0) { + return (rx_vec_i128)((__m128li){_I0,_I1,_I2,_I3}); +}; + +FORCE_INLINE rx_vec_i128 rx_xor_vec_i128(rx_vec_i128 _A, rx_vec_i128 _B) { + return (rx_vec_i128)vec_xor(_A,_B); +} + +FORCE_INLINE rx_vec_i128 rx_load_vec_i128(rx_vec_i128 const *_P) { +#if defined(NATIVE_LITTLE_ENDIAN) + return *_P; +#else + uint32_t* ptr = (uint32_t*)_P; + vec_u c; + c.u32[0] = load32(ptr + 0); + c.u32[1] = load32(ptr + 1); + c.u32[2] = load32(ptr + 2); + c.u32[3] = load32(ptr + 3); + return (rx_vec_i128)c.i; +#endif +} + +FORCE_INLINE void rx_store_vec_i128(rx_vec_i128 *_P, rx_vec_i128 _B) { +#if defined(NATIVE_LITTLE_ENDIAN) + *_P = _B; +#else + uint32_t* ptr = (uint32_t*)_P; + vec_u B; + B.i = _B; + store32(ptr + 0, B.u32[0]); + store32(ptr + 1, B.u32[1]); + store32(ptr + 2, B.u32[2]); + store32(ptr + 3, B.u32[3]); +#endif +} + +FORCE_INLINE rx_vec_f128 rx_cvt_packed_int_vec_f128(const void* addr) { + vec_u x; + x.d64[0] = (double)unsigned32ToSigned2sCompl(load32((uint8_t*)addr + 0)); + x.d64[1] = (double)unsigned32ToSigned2sCompl(load32((uint8_t*)addr + 4)); + return (rx_vec_f128)x.d; +} + +#define RANDOMX_DEFAULT_FENV + +void rx_reset_float_state(); + +void rx_set_rounding_mode(uint32_t mode); + +#else //end altivec + +#include +#include +#include +#include + +typedef union { + uint64_t u64[2]; + uint32_t u32[4]; + uint16_t u16[8]; + uint8_t u8[16]; +} rx_vec_i128; + +typedef union { + struct { + double lo; + double hi; + }; + rx_vec_i128 i; +} rx_vec_f128; + +#define rx_aligned_alloc(a, b) malloc(a) +#define rx_aligned_free(a) free(a) +#define rx_prefetch_nta(x) + +FORCE_INLINE rx_vec_f128 rx_load_vec_f128(const double* pd) { + rx_vec_f128 x; + x.i.u64[0] = load64(pd + 0); + x.i.u64[1] = load64(pd + 1); + return x; +} + +FORCE_INLINE void rx_store_vec_f128(double* mem_addr, rx_vec_f128 a) { + store64(mem_addr + 0, a.i.u64[0]); + store64(mem_addr + 1, a.i.u64[1]); +} + +FORCE_INLINE rx_vec_f128 rx_swap_vec_f128(rx_vec_f128 a) { + double temp = a.hi; + a.hi = a.lo; + a.lo = temp; + return a; +} + +FORCE_INLINE rx_vec_f128 rx_add_vec_f128(rx_vec_f128 a, rx_vec_f128 b) { + rx_vec_f128 x; + x.lo = a.lo + b.lo; + x.hi = a.hi + b.hi; + return x; +} + +FORCE_INLINE rx_vec_f128 rx_sub_vec_f128(rx_vec_f128 a, rx_vec_f128 b) { + rx_vec_f128 x; + x.lo = a.lo - b.lo; + x.hi = a.hi - b.hi; + return x; +} + +FORCE_INLINE rx_vec_f128 rx_mul_vec_f128(rx_vec_f128 a, rx_vec_f128 b) { + rx_vec_f128 x; + x.lo = a.lo * b.lo; + x.hi = a.hi * b.hi; + return x; +} + +FORCE_INLINE rx_vec_f128 rx_div_vec_f128(rx_vec_f128 a, rx_vec_f128 b) { + rx_vec_f128 x; + x.lo = a.lo / b.lo; + x.hi = a.hi / b.hi; + return x; +} + +FORCE_INLINE rx_vec_f128 rx_sqrt_vec_f128(rx_vec_f128 a) { + rx_vec_f128 x; + x.lo = rx_sqrt(a.lo); + x.hi = rx_sqrt(a.hi); + return x; +} + +FORCE_INLINE rx_vec_i128 rx_set1_long_vec_i128(uint64_t a) { + rx_vec_i128 x; + x.u64[0] = a; + x.u64[1] = a; + return x; +} + +FORCE_INLINE rx_vec_f128 rx_vec_i128_vec_f128(rx_vec_i128 a) { + rx_vec_f128 x; + x.i = a; + return x; +} + +FORCE_INLINE rx_vec_f128 rx_set_vec_f128(uint64_t x1, uint64_t x0) { + rx_vec_f128 v; + v.i.u64[0] = x0; + v.i.u64[1] = x1; + return v; +} + +FORCE_INLINE rx_vec_f128 rx_set1_vec_f128(uint64_t x) { + rx_vec_f128 v; + v.i.u64[0] = x; + v.i.u64[1] = x; + return v; +} + + +FORCE_INLINE rx_vec_f128 rx_xor_vec_f128(rx_vec_f128 a, rx_vec_f128 b) { + rx_vec_f128 x; + x.i.u64[0] = a.i.u64[0] ^ b.i.u64[0]; + x.i.u64[1] = a.i.u64[1] ^ b.i.u64[1]; + return x; +} + +FORCE_INLINE rx_vec_f128 rx_and_vec_f128(rx_vec_f128 a, rx_vec_f128 b) { + rx_vec_f128 x; + x.i.u64[0] = a.i.u64[0] & b.i.u64[0]; + x.i.u64[1] = a.i.u64[1] & b.i.u64[1]; + return x; +} + +FORCE_INLINE rx_vec_f128 rx_or_vec_f128(rx_vec_f128 a, rx_vec_f128 b) { + rx_vec_f128 x; + x.i.u64[0] = a.i.u64[0] | b.i.u64[0]; + x.i.u64[1] = a.i.u64[1] | b.i.u64[1]; + return x; +} + +FORCE_INLINE int rx_vec_i128_x(rx_vec_i128 a) { + return a.u32[0]; +} + +FORCE_INLINE int rx_vec_i128_y(rx_vec_i128 a) { + return a.u32[1]; +} + +FORCE_INLINE int rx_vec_i128_z(rx_vec_i128 a) { + return a.u32[2]; +} + +FORCE_INLINE int rx_vec_i128_w(rx_vec_i128 a) { + return a.u32[3]; +} + +FORCE_INLINE rx_vec_i128 rx_set_int_vec_i128(int _I3, int _I2, int _I1, int _I0) { + rx_vec_i128 v; + v.u32[0] = _I0; + v.u32[1] = _I1; + v.u32[2] = _I2; + v.u32[3] = _I3; + return v; +}; + +FORCE_INLINE rx_vec_i128 rx_xor_vec_i128(rx_vec_i128 _A, rx_vec_i128 _B) { + rx_vec_i128 c; + c.u32[0] = _A.u32[0] ^ _B.u32[0]; + c.u32[1] = _A.u32[1] ^ _B.u32[1]; + c.u32[2] = _A.u32[2] ^ _B.u32[2]; + c.u32[3] = _A.u32[3] ^ _B.u32[3]; + return c; +} + +FORCE_INLINE rx_vec_i128 rx_load_vec_i128(rx_vec_i128 const*_P) { +#if defined(NATIVE_LITTLE_ENDIAN) + return *_P; +#else + uint32_t* ptr = (uint32_t*)_P; + rx_vec_i128 c; + c.u32[0] = load32(ptr + 0); + c.u32[1] = load32(ptr + 1); + c.u32[2] = load32(ptr + 2); + c.u32[3] = load32(ptr + 3); + return c; +#endif +} + +FORCE_INLINE void rx_store_vec_i128(rx_vec_i128 *_P, rx_vec_i128 _B) { +#if defined(NATIVE_LITTLE_ENDIAN) + *_P = _B; +#else + uint32_t* ptr = (uint32_t*)_P; + store32(ptr + 0, _B.u32[0]); + store32(ptr + 1, _B.u32[1]); + store32(ptr + 2, _B.u32[2]); + store32(ptr + 3, _B.u32[3]); +#endif +} + +FORCE_INLINE rx_vec_f128 rx_cvt_packed_int_vec_f128(const void* addr) { + rx_vec_f128 x; + x.lo = (double)unsigned32ToSigned2sCompl(load32((uint8_t*)addr + 0)); + x.hi = (double)unsigned32ToSigned2sCompl(load32((uint8_t*)addr + 4)); + return x; +} + +#define RANDOMX_DEFAULT_FENV + +void rx_reset_float_state(); + +void rx_set_rounding_mode(uint32_t mode); + +#endif + +#ifndef HAVE_AES +static const char* platformError = "Platform doesn't support hardware AES"; + +#include + +FORCE_INLINE rx_vec_i128 rx_aesenc_vec_i128(rx_vec_i128 v, rx_vec_i128 rkey) { + throw std::runtime_error(platformError); +} + +FORCE_INLINE rx_vec_i128 rx_aesdec_vec_i128(rx_vec_i128 v, rx_vec_i128 rkey) { + throw std::runtime_error(platformError); +} +#endif + +double loadDoublePortable(const void* addr); +uint64_t mulh(uint64_t, uint64_t); +int64_t smulh(int64_t, int64_t); +uint64_t rotl64(uint64_t, unsigned int); +uint64_t rotr64(uint64_t, unsigned int); diff --git a/src/crypto/randomx/jit_compiler.hpp b/src/crypto/randomx/jit_compiler.hpp new file mode 100644 index 00000000..03b60508 --- /dev/null +++ b/src/crypto/randomx/jit_compiler.hpp @@ -0,0 +1,37 @@ +/* +Copyright (c) 2018-2019, tevador + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#pragma once + +#if defined(_M_X64) || defined(__x86_64__) +#include "crypto/randomx/jit_compiler_x86.hpp" +#elif defined(__aarch64__) +#include "crypto/randomx/jit_compiler_a64.hpp" +#else +#include "crypto/randomx/jit_compiler_fallback.hpp" +#endif diff --git a/src/crypto/randomx/jit_compiler_a64.hpp b/src/crypto/randomx/jit_compiler_a64.hpp new file mode 100644 index 00000000..4b0bed66 --- /dev/null +++ b/src/crypto/randomx/jit_compiler_a64.hpp @@ -0,0 +1,73 @@ +/* +Copyright (c) 2018-2019, tevador + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#pragma once + +#include +#include +#include +#include "crypto/randomx/common.hpp" + +namespace randomx { + + class Program; + class ProgramConfiguration; + class SuperscalarProgram; + + class JitCompilerA64 { + public: + JitCompilerA64() { + throw std::runtime_error("ARM64 JIT compiler is not implemented yet."); + } + void generateProgram(Program&, ProgramConfiguration&) { + + } + void generateProgramLight(Program&, ProgramConfiguration&, uint32_t) { + + } + template + void generateSuperscalarHash(SuperscalarProgram(&programs)[N], std::vector &) { + + } + void generateDatasetInitCode() { + + } + ProgramFunc* getProgramFunc() { + return nullptr; + } + DatasetInitFunc* getDatasetInitFunc() { + return nullptr; + } + uint8_t* getCode() { + return nullptr; + } + size_t getCodeSize() { + return 0; + } + }; +} diff --git a/src/crypto/randomx/jit_compiler_fallback.hpp b/src/crypto/randomx/jit_compiler_fallback.hpp new file mode 100644 index 00000000..bc363858 --- /dev/null +++ b/src/crypto/randomx/jit_compiler_fallback.hpp @@ -0,0 +1,73 @@ +/* +Copyright (c) 2018-2019, tevador + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#pragma once + +#include +#include +#include +#include "crypto/randomx/common.hpp" + +namespace randomx { + + class Program; + class ProgramConfiguration; + class SuperscalarProgram; + + class JitCompilerFallback { + public: + JitCompilerFallback() { + throw std::runtime_error("JIT compilation is not supported on this platform"); + } + void generateProgram(Program&, ProgramConfiguration&) { + + } + void generateProgramLight(Program&, ProgramConfiguration&, uint32_t) { + + } + template + void generateSuperscalarHash(SuperscalarProgram(&programs)[N], std::vector &) { + + } + void generateDatasetInitCode() { + + } + ProgramFunc* getProgramFunc() { + return nullptr; + } + DatasetInitFunc* getDatasetInitFunc() { + return nullptr; + } + uint8_t* getCode() { + return nullptr; + } + size_t getCodeSize() { + return 0; + } + }; +} diff --git a/src/crypto/randomx/jit_compiler_x86.cpp b/src/crypto/randomx/jit_compiler_x86.cpp new file mode 100644 index 00000000..d2ac8370 --- /dev/null +++ b/src/crypto/randomx/jit_compiler_x86.cpp @@ -0,0 +1,930 @@ +/* +Copyright (c) 2018-2019, tevador + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include "crypto/randomx/jit_compiler_x86.hpp" +#include "crypto/randomx/jit_compiler_x86_static.hpp" +#include "crypto/randomx/superscalar.hpp" +#include "crypto/randomx/program.hpp" +#include "crypto/randomx/reciprocal.h" +#include "crypto/randomx/virtual_memory.hpp" + +namespace randomx { + /* + + REGISTER ALLOCATION: + + ; rax -> temporary + ; rbx -> iteration counter "ic" + ; rcx -> temporary + ; rdx -> temporary + ; rsi -> scratchpad pointer + ; rdi -> dataset pointer + ; rbp -> memory registers "ma" (high 32 bits), "mx" (low 32 bits) + ; rsp -> stack pointer + ; r8 -> "r0" + ; r9 -> "r1" + ; r10 -> "r2" + ; r11 -> "r3" + ; r12 -> "r4" + ; r13 -> "r5" + ; r14 -> "r6" + ; r15 -> "r7" + ; xmm0 -> "f0" + ; xmm1 -> "f1" + ; xmm2 -> "f2" + ; xmm3 -> "f3" + ; xmm4 -> "e0" + ; xmm5 -> "e1" + ; xmm6 -> "e2" + ; xmm7 -> "e3" + ; xmm8 -> "a0" + ; xmm9 -> "a1" + ; xmm10 -> "a2" + ; xmm11 -> "a3" + ; xmm12 -> temporary + ; xmm13 -> E 'and' mask = 0x00ffffffffffffff00ffffffffffffff + ; xmm14 -> E 'or' mask = 0x3*00000000******3*00000000****** + ; xmm15 -> scale mask = 0x81f000000000000081f0000000000000 + + */ + + const uint8_t* codePrologue = (uint8_t*)&randomx_program_prologue; + const uint8_t* codeLoopBegin = (uint8_t*)&randomx_program_loop_begin; + const uint8_t* codeLoopLoad = (uint8_t*)&randomx_program_loop_load; + const uint8_t* codeProgamStart = (uint8_t*)&randomx_program_start; + const uint8_t* codeReadDataset = (uint8_t*)&randomx_program_read_dataset; + const uint8_t* codeReadDatasetLightSshInit = (uint8_t*)&randomx_program_read_dataset_sshash_init; + const uint8_t* codeReadDatasetLightSshFin = (uint8_t*)&randomx_program_read_dataset_sshash_fin; + const uint8_t* codeDatasetInit = (uint8_t*)&randomx_dataset_init; + const uint8_t* codeLoopStore = (uint8_t*)&randomx_program_loop_store; + const uint8_t* codeLoopEnd = (uint8_t*)&randomx_program_loop_end; + const uint8_t* codeEpilogue = (uint8_t*)&randomx_program_epilogue; + const uint8_t* codeProgramEnd = (uint8_t*)&randomx_program_end; + const uint8_t* codeShhLoad = (uint8_t*)&randomx_sshash_load; + const uint8_t* codeShhPrefetch = (uint8_t*)&randomx_sshash_prefetch; + const uint8_t* codeShhEnd = (uint8_t*)&randomx_sshash_end; + const uint8_t* codeShhInit = (uint8_t*)&randomx_sshash_init; + + const int32_t prologueSize = codeLoopBegin - codePrologue; + const int32_t loopLoadSize = codeProgamStart - codeLoopLoad; + const int32_t readDatasetSize = codeReadDatasetLightSshInit - codeReadDataset; + const int32_t readDatasetLightInitSize = codeReadDatasetLightSshFin - codeReadDatasetLightSshInit; + const int32_t readDatasetLightFinSize = codeLoopStore - codeReadDatasetLightSshFin; + const int32_t loopStoreSize = codeLoopEnd - codeLoopStore; + const int32_t datasetInitSize = codeEpilogue - codeDatasetInit; + const int32_t epilogueSize = codeShhLoad - codeEpilogue; + const int32_t codeSshLoadSize = codeShhPrefetch - codeShhLoad; + const int32_t codeSshPrefetchSize = codeShhEnd - codeShhPrefetch; + const int32_t codeSshInitSize = codeProgramEnd - codeShhInit; + + const int32_t epilogueOffset = CodeSize - epilogueSize; + constexpr int32_t superScalarHashOffset = 32768; + + static const uint8_t REX_ADD_RR[] = { 0x4d, 0x03 }; + static const uint8_t REX_ADD_RM[] = { 0x4c, 0x03 }; + static const uint8_t REX_SUB_RR[] = { 0x4d, 0x2b }; + static const uint8_t REX_SUB_RM[] = { 0x4c, 0x2b }; + static const uint8_t REX_MOV_RR[] = { 0x41, 0x8b }; + static const uint8_t REX_MOV_RR64[] = { 0x49, 0x8b }; + static const uint8_t REX_MOV_R64R[] = { 0x4c, 0x8b }; + static const uint8_t REX_IMUL_RR[] = { 0x4d, 0x0f, 0xaf }; + static const uint8_t REX_IMUL_RRI[] = { 0x4d, 0x69 }; + static const uint8_t REX_IMUL_RM[] = { 0x4c, 0x0f, 0xaf }; + static const uint8_t REX_MUL_R[] = { 0x49, 0xf7 }; + static const uint8_t REX_MUL_M[] = { 0x48, 0xf7 }; + static const uint8_t REX_81[] = { 0x49, 0x81 }; + static const uint8_t AND_EAX_I = 0x25; + static const uint8_t MOV_EAX_I = 0xb8; + static const uint8_t MOV_RAX_I[] = { 0x48, 0xb8 }; + static const uint8_t MOV_RCX_I[] = { 0x48, 0xb9 }; + static const uint8_t REX_LEA[] = { 0x4f, 0x8d }; + static const uint8_t REX_MUL_MEM[] = { 0x48, 0xf7, 0x24, 0x0e }; + static const uint8_t REX_IMUL_MEM[] = { 0x48, 0xf7, 0x2c, 0x0e }; + static const uint8_t REX_SHR_RAX[] = { 0x48, 0xc1, 0xe8 }; + static const uint8_t RAX_ADD_SBB_1[] = { 0x48, 0x83, 0xC0, 0x01, 0x48, 0x83, 0xD8, 0x00 }; + static const uint8_t MUL_RCX[] = { 0x48, 0xf7, 0xe1 }; + static const uint8_t REX_SHR_RDX[] = { 0x48, 0xc1, 0xea }; + static const uint8_t REX_SH[] = { 0x49, 0xc1 }; + static const uint8_t MOV_RCX_RAX_SAR_RCX_63[] = { 0x48, 0x89, 0xc1, 0x48, 0xc1, 0xf9, 0x3f }; + static const uint8_t AND_ECX_I[] = { 0x81, 0xe1 }; + static const uint8_t ADD_RAX_RCX[] = { 0x48, 0x01, 0xC8 }; + static const uint8_t SAR_RAX_I8[] = { 0x48, 0xC1, 0xF8 }; + static const uint8_t NEG_RAX[] = { 0x48, 0xF7, 0xD8 }; + static const uint8_t ADD_R_RAX[] = { 0x4C, 0x03 }; + static const uint8_t XOR_EAX_EAX[] = { 0x33, 0xC0 }; + static const uint8_t ADD_RDX_R[] = { 0x4c, 0x01 }; + static const uint8_t SUB_RDX_R[] = { 0x4c, 0x29 }; + static const uint8_t SAR_RDX_I8[] = { 0x48, 0xC1, 0xFA }; + static const uint8_t TEST_RDX_RDX[] = { 0x48, 0x85, 0xD2 }; + static const uint8_t SETS_AL_ADD_RDX_RAX[] = { 0x0F, 0x98, 0xC0, 0x48, 0x03, 0xD0 }; + static const uint8_t REX_NEG[] = { 0x49, 0xF7 }; + static const uint8_t REX_XOR_RR[] = { 0x4D, 0x33 }; + static const uint8_t REX_XOR_RI[] = { 0x49, 0x81 }; + static const uint8_t REX_XOR_RM[] = { 0x4c, 0x33 }; + static const uint8_t REX_ROT_CL[] = { 0x49, 0xd3 }; + static const uint8_t REX_ROT_I8[] = { 0x49, 0xc1 }; + static const uint8_t SHUFPD[] = { 0x66, 0x0f, 0xc6 }; + static const uint8_t REX_ADDPD[] = { 0x66, 0x41, 0x0f, 0x58 }; + static const uint8_t REX_CVTDQ2PD_XMM12[] = { 0xf3, 0x44, 0x0f, 0xe6, 0x24, 0x06 }; + static const uint8_t REX_SUBPD[] = { 0x66, 0x41, 0x0f, 0x5c }; + static const uint8_t REX_XORPS[] = { 0x41, 0x0f, 0x57 }; + static const uint8_t REX_MULPD[] = { 0x66, 0x41, 0x0f, 0x59 }; + static const uint8_t REX_MAXPD[] = { 0x66, 0x41, 0x0f, 0x5f }; + static const uint8_t REX_DIVPD[] = { 0x66, 0x41, 0x0f, 0x5e }; + static const uint8_t SQRTPD[] = { 0x66, 0x0f, 0x51 }; + static const uint8_t AND_OR_MOV_LDMXCSR[] = { 0x25, 0x00, 0x60, 0x00, 0x00, 0x0D, 0xC0, 0x9F, 0x00, 0x00, 0x50, 0x0F, 0xAE, 0x14, 0x24, 0x58 }; + static const uint8_t ROL_RAX[] = { 0x48, 0xc1, 0xc0 }; + static const uint8_t XOR_ECX_ECX[] = { 0x33, 0xC9 }; + static const uint8_t REX_CMP_R32I[] = { 0x41, 0x81 }; + static const uint8_t REX_CMP_M32I[] = { 0x81, 0x3c, 0x06 }; + static const uint8_t MOVAPD[] = { 0x66, 0x0f, 0x29 }; + static const uint8_t REX_MOV_MR[] = { 0x4c, 0x89 }; + static const uint8_t REX_XOR_EAX[] = { 0x41, 0x33 }; + static const uint8_t SUB_EBX[] = { 0x83, 0xEB, 0x01 }; + static const uint8_t JNZ[] = { 0x0f, 0x85 }; + static const uint8_t JMP = 0xe9; + static const uint8_t REX_XOR_RAX_R64[] = { 0x49, 0x33 }; + static const uint8_t REX_XCHG[] = { 0x4d, 0x87 }; + static const uint8_t REX_ANDPS_XMM12[] = { 0x45, 0x0F, 0x54, 0xE5, 0x45, 0x0F, 0x56, 0xE6 }; + static const uint8_t REX_PADD[] = { 0x66, 0x44, 0x0f }; + static const uint8_t PADD_OPCODES[] = { 0xfc, 0xfd, 0xfe, 0xd4 }; + static const uint8_t CALL = 0xe8; + static const uint8_t REX_ADD_I[] = { 0x49, 0x81 }; + static const uint8_t REX_TEST[] = { 0x49, 0xF7 }; + static const uint8_t JZ[] = { 0x0f, 0x84 }; + static const uint8_t RET = 0xc3; + static const uint8_t LEA_32[] = { 0x41, 0x8d }; + static const uint8_t MOVNTI[] = { 0x4c, 0x0f, 0xc3 }; + static const uint8_t ADD_EBX_I[] = { 0x81, 0xc3 }; + + static const uint8_t NOP1[] = { 0x90 }; + static const uint8_t NOP2[] = { 0x66, 0x90 }; + static const uint8_t NOP3[] = { 0x66, 0x66, 0x90 }; + static const uint8_t NOP4[] = { 0x0F, 0x1F, 0x40, 0x00 }; + static const uint8_t NOP5[] = { 0x0F, 0x1F, 0x44, 0x00, 0x00 }; + static const uint8_t NOP6[] = { 0x66, 0x0F, 0x1F, 0x44, 0x00, 0x00 }; + static const uint8_t NOP7[] = { 0x0F, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00 }; + static const uint8_t NOP8[] = { 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +// static const uint8_t* NOPX[] = { NOP1, NOP2, NOP3, NOP4, NOP5, NOP6, NOP7, NOP8 }; + + size_t JitCompilerX86::getCodeSize() { + return codePos < prologueSize ? 0 : codePos - prologueSize; + } + + JitCompilerX86::JitCompilerX86() { + code = (uint8_t*)allocExecutableMemory(CodeSize); + memcpy(code, codePrologue, prologueSize); + memcpy(code + epilogueOffset, codeEpilogue, epilogueSize); + } + + JitCompilerX86::~JitCompilerX86() { + freePagedMemory(code, CodeSize); + } + + void JitCompilerX86::generateProgram(Program& prog, ProgramConfiguration& pcfg) { + generateProgramPrologue(prog, pcfg); + memcpy(code + codePos, RandomX_CurrentConfig.codeReadDatasetTweaked, readDatasetSize); + codePos += readDatasetSize; + generateProgramEpilogue(prog); + } + + void JitCompilerX86::generateProgramLight(Program& prog, ProgramConfiguration& pcfg, uint32_t datasetOffset) { + generateProgramPrologue(prog, pcfg); + emit(RandomX_CurrentConfig.codeReadDatasetLightSshInitTweaked, readDatasetLightInitSize, code, codePos); + emit(ADD_EBX_I, code, codePos); + emit32(datasetOffset / CacheLineSize, code, codePos); + emitByte(CALL, code, codePos); + emit32(superScalarHashOffset - (codePos + 4), code, codePos); + emit(codeReadDatasetLightSshFin, readDatasetLightFinSize, code, codePos); + generateProgramEpilogue(prog); + } + + template + void JitCompilerX86::generateSuperscalarHash(SuperscalarProgram(&programs)[N], std::vector &reciprocalCache) { + memcpy(code + superScalarHashOffset, codeShhInit, codeSshInitSize); + codePos = superScalarHashOffset + codeSshInitSize; + for (unsigned j = 0; j < RandomX_CurrentConfig.CacheAccesses; ++j) { + SuperscalarProgram& prog = programs[j]; + for (unsigned i = 0; i < prog.getSize(); ++i) { + Instruction& instr = prog(i); + generateSuperscalarCode(instr, reciprocalCache); + } + emit(codeShhLoad, codeSshLoadSize, code, codePos); + if (j < RandomX_CurrentConfig.CacheAccesses - 1) { + emit(REX_MOV_RR64, code, codePos); + emitByte(0xd8 + prog.getAddressRegister(), code, codePos); + emit(RandomX_CurrentConfig.codeShhPrefetchTweaked, codeSshPrefetchSize, code, codePos); +#ifdef RANDOMX_ALIGN + int align = (codePos % 16); + while (align != 0) { + int nopSize = 16 - align; + if (nopSize > 8) nopSize = 8; + emit(NOPX[nopSize - 1], nopSize, code, codePos); + align = (codePos % 16); + } +#endif + } + } + emitByte(RET, code, codePos); + } + + template + void JitCompilerX86::generateSuperscalarHash(SuperscalarProgram(&programs)[RANDOMX_CACHE_MAX_ACCESSES], std::vector &reciprocalCache); + + void JitCompilerX86::generateDatasetInitCode() { + memcpy(code, codeDatasetInit, datasetInitSize); + } + + void JitCompilerX86::generateProgramPrologue(Program& prog, ProgramConfiguration& pcfg) { + memset(registerUsage, -1, sizeof(registerUsage)); + codePos = prologueSize; + memcpy(code + codePos - 48, &pcfg.eMask, sizeof(pcfg.eMask)); + emit(REX_XOR_RAX_R64, code, codePos); + emitByte(0xc0 + pcfg.readReg0, code, codePos); + emit(REX_XOR_RAX_R64, code, codePos); + emitByte(0xc0 + pcfg.readReg1, code, codePos); + memcpy(code + codePos, RandomX_CurrentConfig.codeLoopLoadTweaked, loopLoadSize); + codePos += loopLoadSize; + for (unsigned i = 0; i < prog.getSize(); ++i) { + Instruction& instr = prog(i); + instr.src %= RegistersCount; + instr.dst %= RegistersCount; + instructionOffsets[i] = codePos; + (this->*(engine[instr.opcode]))(instr, i); + } + emit(REX_MOV_RR, code, codePos); + emitByte(0xc0 + pcfg.readReg2, code, codePos); + emit(REX_XOR_EAX, code, codePos); + emitByte(0xc0 + pcfg.readReg3, code, codePos); + } + + void JitCompilerX86::generateProgramEpilogue(Program& prog) { + memcpy(code + codePos, codeLoopStore, loopStoreSize); + codePos += loopStoreSize; + emit(SUB_EBX, code, codePos); + emit(JNZ, code, codePos); + emit32(prologueSize - codePos - 4, code, codePos); + emitByte(JMP, code, codePos); + emit32(epilogueOffset - codePos - 4, code, codePos); + } + + void JitCompilerX86::generateSuperscalarCode(Instruction& instr, std::vector &reciprocalCache) { + switch ((SuperscalarInstructionType)instr.opcode) + { + case randomx::SuperscalarInstructionType::ISUB_R: + emit(REX_SUB_RR, code, codePos); + emitByte(0xc0 + 8 * instr.dst + instr.src, code, codePos); + break; + case randomx::SuperscalarInstructionType::IXOR_R: + emit(REX_XOR_RR, code, codePos); + emitByte(0xc0 + 8 * instr.dst + instr.src, code, codePos); + break; + case randomx::SuperscalarInstructionType::IADD_RS: + emit(REX_LEA, code, codePos); + emitByte(0x04 + 8 * instr.dst, code, codePos); + genSIB(instr.getModShift(), instr.src, instr.dst, code, codePos); + break; + case randomx::SuperscalarInstructionType::IMUL_R: + emit(REX_IMUL_RR, code, codePos); + emitByte(0xc0 + 8 * instr.dst + instr.src, code, codePos); + break; + case randomx::SuperscalarInstructionType::IROR_C: + emit(REX_ROT_I8, code, codePos); + emitByte(0xc8 + instr.dst, code, codePos); + emitByte(instr.getImm32() & 63, code, codePos); + break; + case randomx::SuperscalarInstructionType::IADD_C7: + emit(REX_81, code, codePos); + emitByte(0xc0 + instr.dst, code, codePos); + emit32(instr.getImm32(), code, codePos); + break; + case randomx::SuperscalarInstructionType::IXOR_C7: + emit(REX_XOR_RI, code, codePos); + emitByte(0xf0 + instr.dst, code, codePos); + emit32(instr.getImm32(), code, codePos); + break; + case randomx::SuperscalarInstructionType::IADD_C8: + emit(REX_81, code, codePos); + emitByte(0xc0 + instr.dst, code, codePos); + emit32(instr.getImm32(), code, codePos); +#ifdef RANDOMX_ALIGN + emit(NOP1, code, codePos); +#endif + break; + case randomx::SuperscalarInstructionType::IXOR_C8: + emit(REX_XOR_RI, code, codePos); + emitByte(0xf0 + instr.dst, code, codePos); + emit32(instr.getImm32(), code, codePos); +#ifdef RANDOMX_ALIGN + emit(NOP1, code, codePos); +#endif + break; + case randomx::SuperscalarInstructionType::IADD_C9: + emit(REX_81, code, codePos); + emitByte(0xc0 + instr.dst, code, codePos); + emit32(instr.getImm32(), code, codePos); +#ifdef RANDOMX_ALIGN + emit(NOP2, code, codePos); +#endif + break; + case randomx::SuperscalarInstructionType::IXOR_C9: + emit(REX_XOR_RI, code, codePos); + emitByte(0xf0 + instr.dst, code, codePos); + emit32(instr.getImm32(), code, codePos); +#ifdef RANDOMX_ALIGN + emit(NOP2, code, codePos); +#endif + break; + case randomx::SuperscalarInstructionType::IMULH_R: + emit(REX_MOV_RR64, code, codePos); + emitByte(0xc0 + instr.dst, code, codePos); + emit(REX_MUL_R, code, codePos); + emitByte(0xe0 + instr.src, code, codePos); + emit(REX_MOV_R64R, code, codePos); + emitByte(0xc2 + 8 * instr.dst, code, codePos); + break; + case randomx::SuperscalarInstructionType::ISMULH_R: + emit(REX_MOV_RR64, code, codePos); + emitByte(0xc0 + instr.dst, code, codePos); + emit(REX_MUL_R, code, codePos); + emitByte(0xe8 + instr.src, code, codePos); + emit(REX_MOV_R64R, code, codePos); + emitByte(0xc2 + 8 * instr.dst, code, codePos); + break; + case randomx::SuperscalarInstructionType::IMUL_RCP: + emit(MOV_RAX_I, code, codePos); + emit64(reciprocalCache[instr.getImm32()], code, codePos); + emit(REX_IMUL_RM, code, codePos); + emitByte(0xc0 + 8 * instr.dst, code, codePos); + break; + default: + UNREACHABLE; + } + } + + void JitCompilerX86::genAddressReg(Instruction& instr, uint8_t* code, int& codePos, bool rax) { + emit(LEA_32, code, codePos); + emitByte(0x80 + instr.src + (rax ? 0 : 8), code, codePos); + if (instr.src == RegisterNeedsSib) { + emitByte(0x24, code, codePos); + } + emit32(instr.getImm32(), code, codePos); + if (rax) + emitByte(AND_EAX_I, code, codePos); + else + emit(AND_ECX_I, code, codePos); + emit32(instr.getModMem() ? ScratchpadL1Mask : ScratchpadL2Mask, code, codePos); + } + + void JitCompilerX86::genAddressRegDst(Instruction& instr, uint8_t* code, int& codePos) { + emit(LEA_32, code, codePos); + emitByte(0x80 + instr.dst, code, codePos); + if (instr.dst == RegisterNeedsSib) { + emitByte(0x24, code, codePos); + } + emit32(instr.getImm32(), code, codePos); + emitByte(AND_EAX_I, code, codePos); + if (instr.getModCond() < StoreL3Condition) { + emit32(instr.getModMem() ? ScratchpadL1Mask : ScratchpadL2Mask, code, codePos); + } + else { + emit32(ScratchpadL3Mask, code, codePos); + } + } + + void JitCompilerX86::genAddressImm(Instruction& instr, uint8_t* code, int& codePos) { + emit32(instr.getImm32() & ScratchpadL3Mask, code, codePos); + } + + static const uint32_t template_IADD_RS[8] = { + 0x048d4f, + 0x0c8d4f, + 0x148d4f, + 0x1c8d4f, + 0x248d4f, + 0xac8d4f, + 0x348d4f, + 0x3c8d4f, + }; + + void JitCompilerX86::h_IADD_RS(Instruction& instr, int i) { + int pos = codePos; + uint8_t* const p = code + pos; + + registerUsage[instr.dst] = i; + + const uint32_t sib = (instr.getModShift() << 6) | (instr.src << 3) | instr.dst; + *(uint32_t*)(p) = template_IADD_RS[instr.dst] | (sib << 24); + *(uint32_t*)(p + 4) = instr.getImm32(); + + codePos = pos + ((instr.dst == RegisterNeedsDisplacement) ? 8 : 4); + } + + static const uint32_t template_IADD_M[8] = { + 0x0604034c, + 0x060c034c, + 0x0614034c, + 0x061c034c, + 0x0624034c, + 0x062c034c, + 0x0634034c, + 0x063c034c, + }; + + void JitCompilerX86::h_IADD_M(Instruction& instr, int i) { + uint8_t* const p = code; + int pos = codePos; + + registerUsage[instr.dst] = i; + if (instr.src != instr.dst) { + genAddressReg(instr, p, pos); + emit32(template_IADD_M[instr.dst], p, pos); + } + else { + emit(REX_ADD_RM, p, pos); + emitByte(0x86 + 8 * instr.dst, p, pos); + genAddressImm(instr, p, pos); + } + + codePos = pos; + } + + void JitCompilerX86::genSIB(int scale, int index, int base, uint8_t* code, int& codePos) { + emitByte((scale << 6) | (index << 3) | base, code, codePos); + } + + void JitCompilerX86::h_ISUB_R(Instruction& instr, int i) { + uint8_t* const p = code; + int pos = codePos; + + registerUsage[instr.dst] = i; + if (instr.src != instr.dst) { + emit(REX_SUB_RR, p, pos); + emitByte(0xc0 + 8 * instr.dst + instr.src, p, pos); + } + else { + emit(REX_81, p, pos); + emitByte(0xe8 + instr.dst, p, pos); + emit32(instr.getImm32(), p, pos); + } + + codePos = pos; + } + + void JitCompilerX86::h_ISUB_M(Instruction& instr, int i) { + uint8_t* const p = code; + int pos = codePos; + + registerUsage[instr.dst] = i; + if (instr.src != instr.dst) { + genAddressReg(instr, p, pos); + emit(REX_SUB_RM, p, pos); + emitByte(0x04 + 8 * instr.dst, p, pos); + emitByte(0x06, p, pos); + } + else { + emit(REX_SUB_RM, p, pos); + emitByte(0x86 + 8 * instr.dst, p, pos); + genAddressImm(instr, p, pos); + } + + codePos = pos; + } + + void JitCompilerX86::h_IMUL_R(Instruction& instr, int i) { + uint8_t* const p = code; + int pos = codePos; + + registerUsage[instr.dst] = i; + if (instr.src != instr.dst) { + emit(REX_IMUL_RR, p, pos); + emitByte(0xc0 + 8 * instr.dst + instr.src, p, pos); + } + else { + emit(REX_IMUL_RRI, p, pos); + emitByte(0xc0 + 9 * instr.dst, p, pos); + emit32(instr.getImm32(), p, pos); + } + + codePos = pos; + } + + void JitCompilerX86::h_IMUL_M(Instruction& instr, int i) { + uint8_t* const p = code; + int pos = codePos; + + registerUsage[instr.dst] = i; + if (instr.src != instr.dst) { + genAddressReg(instr, p, pos); + emit(REX_IMUL_RM, p, pos); + emitByte(0x04 + 8 * instr.dst, p, pos); + emitByte(0x06, p, pos); + } + else { + emit(REX_IMUL_RM, p, pos); + emitByte(0x86 + 8 * instr.dst, p, pos); + genAddressImm(instr, p, pos); + } + + codePos = pos; + } + + void JitCompilerX86::h_IMULH_R(Instruction& instr, int i) { + uint8_t* const p = code; + int pos = codePos; + + registerUsage[instr.dst] = i; + emit(REX_MOV_RR64, p, pos); + emitByte(0xc0 + instr.dst, p, pos); + emit(REX_MUL_R, p, pos); + emitByte(0xe0 + instr.src, p, pos); + emit(REX_MOV_R64R, p, pos); + emitByte(0xc2 + 8 * instr.dst, p, pos); + + codePos = pos; + } + + void JitCompilerX86::h_IMULH_M(Instruction& instr, int i) { + uint8_t* const p = code; + int pos = codePos; + + registerUsage[instr.dst] = i; + if (instr.src != instr.dst) { + genAddressReg(instr, p, pos, false); + emit(REX_MOV_RR64, p, pos); + emitByte(0xc0 + instr.dst, p, pos); + emit(REX_MUL_MEM, p, pos); + } + else { + emit(REX_MOV_RR64, p, pos); + emitByte(0xc0 + instr.dst, p, pos); + emit(REX_MUL_M, p, pos); + emitByte(0xa6, p, pos); + genAddressImm(instr, p, pos); + } + emit(REX_MOV_R64R, p, pos); + emitByte(0xc2 + 8 * instr.dst, p, pos); + + codePos = pos; + } + + void JitCompilerX86::h_ISMULH_R(Instruction& instr, int i) { + uint8_t* const p = code; + int pos = codePos; + + registerUsage[instr.dst] = i; + emit(REX_MOV_RR64, p, pos); + emitByte(0xc0 + instr.dst, p, pos); + emit(REX_MUL_R, p, pos); + emitByte(0xe8 + instr.src, p, pos); + emit(REX_MOV_R64R, p, pos); + emitByte(0xc2 + 8 * instr.dst, p, pos); + + codePos = pos; + } + + void JitCompilerX86::h_ISMULH_M(Instruction& instr, int i) { + uint8_t* const p = code; + int pos = codePos; + + registerUsage[instr.dst] = i; + if (instr.src != instr.dst) { + genAddressReg(instr, p, pos, false); + emit(REX_MOV_RR64, p, pos); + emitByte(0xc0 + instr.dst, p, pos); + emit(REX_IMUL_MEM, p, pos); + } + else { + emit(REX_MOV_RR64, p, pos); + emitByte(0xc0 + instr.dst, p, pos); + emit(REX_MUL_M, p, pos); + emitByte(0xae, p, pos); + genAddressImm(instr, p, pos); + } + emit(REX_MOV_R64R, p, pos); + emitByte(0xc2 + 8 * instr.dst, p, pos); + + codePos = pos; + } + + void JitCompilerX86::h_IMUL_RCP(Instruction& instr, int i) { + uint8_t* const p = code; + int pos = codePos; + + uint64_t divisor = instr.getImm32(); + if (!isZeroOrPowerOf2(divisor)) { + registerUsage[instr.dst] = i; + emit(MOV_RAX_I, p, pos); + emit64(randomx_reciprocal_fast(divisor), p, pos); + emit(REX_IMUL_RM, p, pos); + emitByte(0xc0 + 8 * instr.dst, p, pos); + } + + codePos = pos; + } + + void JitCompilerX86::h_INEG_R(Instruction& instr, int i) { + uint8_t* const p = code; + int pos = codePos; + + registerUsage[instr.dst] = i; + emit(REX_NEG, p, pos); + emitByte(0xd8 + instr.dst, p, pos); + + codePos = pos; + } + + void JitCompilerX86::h_IXOR_R(Instruction& instr, int i) { + uint8_t* const p = code; + int pos = codePos; + + registerUsage[instr.dst] = i; + if (instr.src != instr.dst) { + emit(REX_XOR_RR, p, pos); + emitByte(0xc0 + 8 * instr.dst + instr.src, p, pos); + } + else { + emit(REX_XOR_RI, p, pos); + emitByte(0xf0 + instr.dst, p, pos); + emit32(instr.getImm32(), p, pos); + } + + codePos = pos; + } + + void JitCompilerX86::h_IXOR_M(Instruction& instr, int i) { + uint8_t* const p = code; + int pos = codePos; + + registerUsage[instr.dst] = i; + if (instr.src != instr.dst) { + genAddressReg(instr, p, pos); + emit(REX_XOR_RM, p, pos); + emitByte(0x04 + 8 * instr.dst, p, pos); + emitByte(0x06, p, pos); + } + else { + emit(REX_XOR_RM, p, pos); + emitByte(0x86 + 8 * instr.dst, p, pos); + genAddressImm(instr, p, pos); + } + + codePos = pos; + } + + void JitCompilerX86::h_IROR_R(Instruction& instr, int i) { + uint8_t* const p = code; + int pos = codePos; + + registerUsage[instr.dst] = i; + if (instr.src != instr.dst) { + emit(REX_MOV_RR, p, pos); + emitByte(0xc8 + instr.src, p, pos); + emit(REX_ROT_CL, p, pos); + emitByte(0xc8 + instr.dst, p, pos); + } + else { + emit(REX_ROT_I8, p, pos); + emitByte(0xc8 + instr.dst, p, pos); + emitByte(instr.getImm32() & 63, p, pos); + } + + codePos = pos; + } + + void JitCompilerX86::h_IROL_R(Instruction& instr, int i) { + uint8_t* const p = code; + int pos = codePos; + + registerUsage[instr.dst] = i; + if (instr.src != instr.dst) { + emit(REX_MOV_RR, p, pos); + emitByte(0xc8 + instr.src, p, pos); + emit(REX_ROT_CL, p, pos); + emitByte(0xc0 + instr.dst, p, pos); + } + else { + emit(REX_ROT_I8, p, pos); + emitByte(0xc0 + instr.dst, p, pos); + emitByte(instr.getImm32() & 63, p, pos); + } + + codePos = pos; + } + + void JitCompilerX86::h_ISWAP_R(Instruction& instr, int i) { + uint8_t* const p = code; + int pos = codePos; + + if (instr.src != instr.dst) { + registerUsage[instr.dst] = i; + registerUsage[instr.src] = i; + emit(REX_XCHG, p, pos); + emitByte(0xc0 + instr.src + 8 * instr.dst, p, pos); + } + + codePos = pos; + } + + void JitCompilerX86::h_FSWAP_R(Instruction& instr, int i) { + uint8_t* const p = code; + int pos = codePos; + + emit(SHUFPD, p, pos); + emitByte(0xc0 + 9 * instr.dst, p, pos); + emitByte(1, p, pos); + + codePos = pos; + } + + void JitCompilerX86::h_FADD_R(Instruction& instr, int i) { + uint8_t* const p = code; + int pos = codePos; + + instr.dst %= RegisterCountFlt; + instr.src %= RegisterCountFlt; + emit(REX_ADDPD, p, pos); + emitByte(0xc0 + instr.src + 8 * instr.dst, p, pos); + + codePos = pos; + } + + void JitCompilerX86::h_FADD_M(Instruction& instr, int i) { + uint8_t* const p = code; + int pos = codePos; + + instr.dst %= RegisterCountFlt; + genAddressReg(instr, p, pos); + emit(REX_CVTDQ2PD_XMM12, p, pos); + emit(REX_ADDPD, p, pos); + emitByte(0xc4 + 8 * instr.dst, p, pos); + + codePos = pos; + } + + void JitCompilerX86::h_FSUB_R(Instruction& instr, int i) { + uint8_t* const p = code; + int pos = codePos; + + instr.dst %= RegisterCountFlt; + instr.src %= RegisterCountFlt; + emit(REX_SUBPD, p, pos); + emitByte(0xc0 + instr.src + 8 * instr.dst, p, pos); + + codePos = pos; + } + + void JitCompilerX86::h_FSUB_M(Instruction& instr, int i) { + uint8_t* const p = code; + int pos = codePos; + + instr.dst %= RegisterCountFlt; + genAddressReg(instr, p, pos); + emit(REX_CVTDQ2PD_XMM12, p, pos); + emit(REX_SUBPD, p, pos); + emitByte(0xc4 + 8 * instr.dst, p, pos); + + codePos = pos; + } + + void JitCompilerX86::h_FSCAL_R(Instruction& instr, int i) { + uint8_t* const p = code; + int pos = codePos; + + instr.dst %= RegisterCountFlt; + emit(REX_XORPS, p, pos); + emitByte(0xc7 + 8 * instr.dst, p, pos); + + codePos = pos; + } + + void JitCompilerX86::h_FMUL_R(Instruction& instr, int i) { + uint8_t* const p = code; + int pos = codePos; + + instr.dst %= RegisterCountFlt; + instr.src %= RegisterCountFlt; + emit(REX_MULPD, p, pos); + emitByte(0xe0 + instr.src + 8 * instr.dst, p, pos); + + codePos = pos; + } + + void JitCompilerX86::h_FDIV_M(Instruction& instr, int i) { + uint8_t* const p = code; + int pos = codePos; + + instr.dst %= RegisterCountFlt; + genAddressReg(instr, p, pos); + emit(REX_CVTDQ2PD_XMM12, p, pos); + emit(REX_ANDPS_XMM12, p, pos); + emit(REX_DIVPD, p, pos); + emitByte(0xe4 + 8 * instr.dst, p, pos); + + codePos = pos; + } + + void JitCompilerX86::h_FSQRT_R(Instruction& instr, int i) { + uint8_t* const p = code; + int pos = codePos; + + instr.dst %= RegisterCountFlt; + emit(SQRTPD, p, pos); + emitByte(0xe4 + 9 * instr.dst, p, pos); + + codePos = pos; + } + + void JitCompilerX86::h_CFROUND(Instruction& instr, int i) { + uint8_t* const p = code; + int pos = codePos; + + emit(REX_MOV_RR64, p, pos); + emitByte(0xc0 + instr.src, p, pos); + int rotate = (13 - (instr.getImm32() & 63)) & 63; + if (rotate != 0) { + emit(ROL_RAX, p, pos); + emitByte(rotate, p, pos); + } + emit(AND_OR_MOV_LDMXCSR, p, pos); + + codePos = pos; + } + + void JitCompilerX86::h_CBRANCH(Instruction& instr, int i) { + uint8_t* const p = code; + int pos = codePos; + + int reg = instr.dst; + int target = registerUsage[reg] + 1; + emit(REX_ADD_I, p, pos); + emitByte(0xc0 + reg, p, pos); + int shift = instr.getModCond() + RandomX_CurrentConfig.JumpOffset; + uint32_t imm = instr.getImm32() | (1UL << shift); + if (RandomX_CurrentConfig.JumpOffset > 0 || shift > 0) + imm &= ~(1UL << (shift - 1)); + emit32(imm, p, pos); + emit(REX_TEST, p, pos); + emitByte(0xc0 + reg, p, pos); + emit32(RandomX_CurrentConfig.ConditionMask_Calculated << shift, p, pos); + emit(JZ, p, pos); + emit32(instructionOffsets[target] - (pos + 4), p, pos); + //mark all registers as used + uint64_t* r = (uint64_t*) registerUsage; + uint64_t k = i; + k |= k << 32; + for (unsigned j = 0; j < RegistersCount / 2; ++j) { + r[j] = k; + } + + codePos = pos; + } + + void JitCompilerX86::h_ISTORE(Instruction& instr, int i) { + uint8_t* const p = code; + int pos = codePos; + + genAddressRegDst(instr, p, pos); + emit(REX_MOV_MR, p, pos); + emitByte(0x04 + 8 * instr.src, p, pos); + emitByte(0x06, p, pos); + + codePos = pos; + } + + void JitCompilerX86::h_NOP(Instruction& instr, int i) { + emit(NOP1, code, codePos); + } + + InstructionGeneratorX86 JitCompilerX86::engine[256] = {}; + +} diff --git a/src/crypto/randomx/jit_compiler_x86.hpp b/src/crypto/randomx/jit_compiler_x86.hpp new file mode 100644 index 00000000..f72bce86 --- /dev/null +++ b/src/crypto/randomx/jit_compiler_x86.hpp @@ -0,0 +1,140 @@ +/* +Copyright (c) 2018-2019, tevador + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#pragma once + +#include +#include +#include +#include "crypto/randomx/common.hpp" + +namespace randomx { + + class Program; + class ProgramConfiguration; + class SuperscalarProgram; + class JitCompilerX86; + class Instruction; + + typedef void(JitCompilerX86::*InstructionGeneratorX86)(Instruction&, int); + + constexpr uint32_t CodeSize = 64 * 1024; + + class JitCompilerX86 { + public: + JitCompilerX86(); + ~JitCompilerX86(); + void generateProgram(Program&, ProgramConfiguration&); + void generateProgramLight(Program&, ProgramConfiguration&, uint32_t); + template + void generateSuperscalarHash(SuperscalarProgram (&programs)[N], std::vector &); + void generateDatasetInitCode(); + ProgramFunc* getProgramFunc() { + return (ProgramFunc*)code; + } + DatasetInitFunc* getDatasetInitFunc() { + return (DatasetInitFunc*)code; + } + uint8_t* getCode() { + return code; + } + size_t getCodeSize(); + + static InstructionGeneratorX86 engine[256]; + int32_t instructionOffsets[512]; + int registerUsage[RegistersCount]; + uint8_t* code; + int32_t codePos; + + void generateProgramPrologue(Program&, ProgramConfiguration&); + void generateProgramEpilogue(Program&); + static void genAddressReg(Instruction&, uint8_t* code, int& codePos, bool rax = true); + static void genAddressRegDst(Instruction&, uint8_t* code, int& codePos); + static void genAddressImm(Instruction&, uint8_t* code, int& codePos); + static void genSIB(int scale, int index, int base, uint8_t* code, int& codePos); + + void generateSuperscalarCode(Instruction &, std::vector &); + + static void emitByte(uint8_t val, uint8_t* code, int& codePos) { + code[codePos] = val; + ++codePos; + } + + static void emit32(uint32_t val, uint8_t* code, int& codePos) { + memcpy(code + codePos, &val, sizeof val); + codePos += sizeof val; + } + + static void emit64(uint64_t val, uint8_t* code, int& codePos) { + memcpy(code + codePos, &val, sizeof val); + codePos += sizeof val; + } + + template + static void emit(const uint8_t (&src)[N], uint8_t* code, int& codePos) { + emit(src, N, code, codePos); + } + + static void emit(const uint8_t* src, size_t count, uint8_t* code, int& codePos) { + memcpy(code + codePos, src, count); + codePos += count; + } + + void h_IADD_RS(Instruction&, int); + void h_IADD_M(Instruction&, int); + void h_ISUB_R(Instruction&, int); + void h_ISUB_M(Instruction&, int); + void h_IMUL_R(Instruction&, int); + void h_IMUL_M(Instruction&, int); + void h_IMULH_R(Instruction&, int); + void h_IMULH_M(Instruction&, int); + void h_ISMULH_R(Instruction&, int); + void h_ISMULH_M(Instruction&, int); + void h_IMUL_RCP(Instruction&, int); + void h_INEG_R(Instruction&, int); + void h_IXOR_R(Instruction&, int); + void h_IXOR_M(Instruction&, int); + void h_IROR_R(Instruction&, int); + void h_IROL_R(Instruction&, int); + void h_ISWAP_R(Instruction&, int); + void h_FSWAP_R(Instruction&, int); + void h_FADD_R(Instruction&, int); + void h_FADD_M(Instruction&, int); + void h_FSUB_R(Instruction&, int); + void h_FSUB_M(Instruction&, int); + void h_FSCAL_R(Instruction&, int); + void h_FMUL_R(Instruction&, int); + void h_FDIV_M(Instruction&, int); + void h_FSQRT_R(Instruction&, int); + void h_CBRANCH(Instruction&, int); + void h_CFROUND(Instruction&, int); + void h_ISTORE(Instruction&, int); + void h_NOP(Instruction&, int); + }; + +} diff --git a/src/crypto/randomx/jit_compiler_x86_static.S b/src/crypto/randomx/jit_compiler_x86_static.S new file mode 100644 index 00000000..b6338d85 --- /dev/null +++ b/src/crypto/randomx/jit_compiler_x86_static.S @@ -0,0 +1,212 @@ +# Copyright (c) 2018-2019, tevador +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +.intel_syntax noprefix +#if defined(__APPLE__) +.text +#define DECL(x) _##x +#else +.section .text +#define DECL(x) x +#endif + +#if defined(__WIN32__) || defined(__CYGWIN__) +#define WINABI +#endif + +.global DECL(randomx_program_prologue) +.global DECL(randomx_program_loop_begin) +.global DECL(randomx_program_loop_load) +.global DECL(randomx_program_start) +.global DECL(randomx_program_read_dataset) +.global DECL(randomx_program_read_dataset_sshash_init) +.global DECL(randomx_program_read_dataset_sshash_fin) +.global DECL(randomx_program_loop_store) +.global DECL(randomx_program_loop_end) +.global DECL(randomx_dataset_init) +.global DECL(randomx_program_epilogue) +.global DECL(randomx_sshash_load) +.global DECL(randomx_sshash_prefetch) +.global DECL(randomx_sshash_end) +.global DECL(randomx_sshash_init) +.global DECL(randomx_program_end) +.global DECL(randomx_reciprocal_fast) + +#define RANDOMX_SCRATCHPAD_MASK 2097088 +#define RANDOMX_DATASET_BASE_MASK 2147483584 +#define RANDOMX_CACHE_MASK 4194303 + +#define db .byte + +.balign 64 +DECL(randomx_program_prologue): +#if defined(WINABI) + #include "asm/program_prologue_win64.inc" +#else + #include "asm/program_prologue_linux.inc" +#endif + movapd xmm13, xmmword ptr [mantissaMask+rip] + movapd xmm14, xmmword ptr [exp240+rip] + movapd xmm15, xmmword ptr [scaleMask+rip] + jmp DECL(randomx_program_loop_begin) + +.balign 64 + #include "asm/program_xmm_constants.inc" + +.balign 64 +DECL(randomx_program_loop_begin): + nop + +DECL(randomx_program_loop_load): + #include "asm/program_loop_load.inc" + +DECL(randomx_program_start): + nop + +DECL(randomx_program_read_dataset): + #include "asm/program_read_dataset.inc" + +DECL(randomx_program_read_dataset_sshash_init): + #include "asm/program_read_dataset_sshash_init.inc" + +DECL(randomx_program_read_dataset_sshash_fin): + #include "asm/program_read_dataset_sshash_fin.inc" + +DECL(randomx_program_loop_store): + #include "asm/program_loop_store.inc" + +DECL(randomx_program_loop_end): + nop + +.balign 64 +DECL(randomx_dataset_init): + push rbx + push rbp + push r12 + push r13 + push r14 + push r15 +#if defined(WINABI) + push rdi + push rsi + mov rdi, qword ptr [rcx] ;# cache->memory + mov rsi, rdx ;# dataset + mov rbp, r8 ;# block index + push r9 ;# max. block index +#else + mov rdi, qword ptr [rdi] ;# cache->memory + ;# dataset in rsi + mov rbp, rdx ;# block index + push rcx ;# max. block index +#endif +init_block_loop: + prefetchw byte ptr [rsi] + mov rbx, rbp + .byte 232 ;# 0xE8 = call + ;# .set CALL_LOC, + .int 32768 - (call_offset - DECL(randomx_dataset_init)) +call_offset: + mov qword ptr [rsi+0], r8 + mov qword ptr [rsi+8], r9 + mov qword ptr [rsi+16], r10 + mov qword ptr [rsi+24], r11 + mov qword ptr [rsi+32], r12 + mov qword ptr [rsi+40], r13 + mov qword ptr [rsi+48], r14 + mov qword ptr [rsi+56], r15 + add rbp, 1 + add rsi, 64 + cmp rbp, qword ptr [rsp] + jb init_block_loop + pop rax +#if defined(WINABI) + pop rsi + pop rdi +#endif + pop r15 + pop r14 + pop r13 + pop r12 + pop rbp + pop rbx + ret + +.balign 64 +DECL(randomx_program_epilogue): + #include "asm/program_epilogue_store.inc" +#if defined(WINABI) + #include "asm/program_epilogue_win64.inc" +#else + #include "asm/program_epilogue_linux.inc" +#endif + +.balign 64 +DECL(randomx_sshash_load): + #include "asm/program_sshash_load.inc" + +DECL(randomx_sshash_prefetch): + #include "asm/program_sshash_prefetch.inc" + +DECL(randomx_sshash_end): + nop + +.balign 64 +DECL(randomx_sshash_init): + lea r8, [rbx+1] + #include "asm/program_sshash_prefetch.inc" + imul r8, qword ptr [r0_mul+rip] + mov r9, qword ptr [r1_add+rip] + xor r9, r8 + mov r10, qword ptr [r2_add+rip] + xor r10, r8 + mov r11, qword ptr [r3_add+rip] + xor r11, r8 + mov r12, qword ptr [r4_add+rip] + xor r12, r8 + mov r13, qword ptr [r5_add+rip] + xor r13, r8 + mov r14, qword ptr [r6_add+rip] + xor r14, r8 + mov r15, qword ptr [r7_add+rip] + xor r15, r8 + jmp DECL(randomx_program_end) + +.balign 64 + #include "asm/program_sshash_constants.inc" + +.balign 64 +DECL(randomx_program_end): + nop + +DECL(randomx_reciprocal_fast): +#if !defined(WINABI) + mov rcx, rdi +#endif + #include "asm/randomx_reciprocal.inc" + +#if defined(__linux__) && defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/src/crypto/randomx/jit_compiler_x86_static.asm b/src/crypto/randomx/jit_compiler_x86_static.asm new file mode 100644 index 00000000..5ecfb435 --- /dev/null +++ b/src/crypto/randomx/jit_compiler_x86_static.asm @@ -0,0 +1,199 @@ +; Copyright (c) 2018-2019, tevador +; +; All rights reserved. +; +; Redistribution and use in source and binary forms, with or without +; modification, are permitted provided that the following conditions are met: +; * Redistributions of source code must retain the above copyright +; notice, this list of conditions and the following disclaimer. +; * Redistributions in binary form must reproduce the above copyright +; notice, this list of conditions and the following disclaimer in the +; documentation and/or other materials provided with the distribution. +; * Neither the name of the copyright holder nor the +; names of its contributors may be used to endorse or promote products +; derived from this software without specific prior written permission. +; +; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +; DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +; SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +; CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +; OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +; OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +IFDEF RAX + +_RANDOMX_JITX86_STATIC SEGMENT PAGE READ EXECUTE + +PUBLIC randomx_program_prologue +PUBLIC randomx_program_loop_begin +PUBLIC randomx_program_loop_load +PUBLIC randomx_program_start +PUBLIC randomx_program_read_dataset +PUBLIC randomx_program_read_dataset_sshash_init +PUBLIC randomx_program_read_dataset_sshash_fin +PUBLIC randomx_dataset_init +PUBLIC randomx_program_loop_store +PUBLIC randomx_program_loop_end +PUBLIC randomx_program_epilogue +PUBLIC randomx_sshash_load +PUBLIC randomx_sshash_prefetch +PUBLIC randomx_sshash_end +PUBLIC randomx_sshash_init +PUBLIC randomx_program_end +PUBLIC randomx_reciprocal_fast + +RANDOMX_SCRATCHPAD_MASK EQU 2097088 +RANDOMX_DATASET_BASE_MASK EQU 2147483584 +RANDOMX_CACHE_MASK EQU 4194303 + +ALIGN 64 +randomx_program_prologue PROC + include asm/program_prologue_win64.inc + movapd xmm13, xmmword ptr [mantissaMask] + movapd xmm14, xmmword ptr [exp240] + movapd xmm15, xmmword ptr [scaleMask] + jmp randomx_program_loop_begin +randomx_program_prologue ENDP + +ALIGN 64 + include asm/program_xmm_constants.inc + +ALIGN 64 +randomx_program_loop_begin PROC + nop +randomx_program_loop_begin ENDP + +randomx_program_loop_load PROC + include asm/program_loop_load.inc +randomx_program_loop_load ENDP + +randomx_program_start PROC + nop +randomx_program_start ENDP + +randomx_program_read_dataset PROC + include asm/program_read_dataset.inc +randomx_program_read_dataset ENDP + +randomx_program_read_dataset_sshash_init PROC + include asm/program_read_dataset_sshash_init.inc +randomx_program_read_dataset_sshash_init ENDP + +randomx_program_read_dataset_sshash_fin PROC + include asm/program_read_dataset_sshash_fin.inc +randomx_program_read_dataset_sshash_fin ENDP + +randomx_program_loop_store PROC + include asm/program_loop_store.inc +randomx_program_loop_store ENDP + +randomx_program_loop_end PROC + nop +randomx_program_loop_end ENDP + +ALIGN 64 +randomx_dataset_init PROC + push rbx + push rbp + push rdi + push rsi + push r12 + push r13 + push r14 + push r15 + mov rdi, qword ptr [rcx] ;# cache->memory + mov rsi, rdx ;# dataset + mov rbp, r8 ;# block index + push r9 ;# max. block index +init_block_loop: + prefetchw byte ptr [rsi] + mov rbx, rbp + db 232 ;# 0xE8 = call + dd 32768 - distance + distance equ $ - offset randomx_dataset_init + mov qword ptr [rsi+0], r8 + mov qword ptr [rsi+8], r9 + mov qword ptr [rsi+16], r10 + mov qword ptr [rsi+24], r11 + mov qword ptr [rsi+32], r12 + mov qword ptr [rsi+40], r13 + mov qword ptr [rsi+48], r14 + mov qword ptr [rsi+56], r15 + add rbp, 1 + add rsi, 64 + cmp rbp, qword ptr [rsp] + jb init_block_loop + pop r9 + pop r15 + pop r14 + pop r13 + pop r12 + pop rsi + pop rdi + pop rbp + pop rbx + ret +randomx_dataset_init ENDP + +ALIGN 64 +randomx_program_epilogue PROC + include asm/program_epilogue_store.inc + include asm/program_epilogue_win64.inc +randomx_program_epilogue ENDP + +ALIGN 64 +randomx_sshash_load PROC + include asm/program_sshash_load.inc +randomx_sshash_load ENDP + +randomx_sshash_prefetch PROC + include asm/program_sshash_prefetch.inc +randomx_sshash_prefetch ENDP + +randomx_sshash_end PROC + nop +randomx_sshash_end ENDP + +ALIGN 64 +randomx_sshash_init PROC + lea r8, [rbx+1] + include asm/program_sshash_prefetch.inc + imul r8, qword ptr [r0_mul] + mov r9, qword ptr [r1_add] + xor r9, r8 + mov r10, qword ptr [r2_add] + xor r10, r8 + mov r11, qword ptr [r3_add] + xor r11, r8 + mov r12, qword ptr [r4_add] + xor r12, r8 + mov r13, qword ptr [r5_add] + xor r13, r8 + mov r14, qword ptr [r6_add] + xor r14, r8 + mov r15, qword ptr [r7_add] + xor r15, r8 + jmp randomx_program_end +randomx_sshash_init ENDP + +ALIGN 64 + include asm/program_sshash_constants.inc + +ALIGN 64 +randomx_program_end PROC + nop +randomx_program_end ENDP + +randomx_reciprocal_fast PROC + include asm/randomx_reciprocal.inc +randomx_reciprocal_fast ENDP + +_RANDOMX_JITX86_STATIC ENDS + +ENDIF + +END \ No newline at end of file diff --git a/src/crypto/randomx/jit_compiler_x86_static.hpp b/src/crypto/randomx/jit_compiler_x86_static.hpp new file mode 100644 index 00000000..ba196862 --- /dev/null +++ b/src/crypto/randomx/jit_compiler_x86_static.hpp @@ -0,0 +1,48 @@ +/* +Copyright (c) 2018-2019, tevador + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#pragma once + +extern "C" { + void randomx_program_prologue(); + void randomx_program_loop_begin(); + void randomx_program_loop_load(); + void randomx_program_start(); + void randomx_program_read_dataset(); + void randomx_program_read_dataset_sshash_init(); + void randomx_program_read_dataset_sshash_fin(); + void randomx_program_loop_store(); + void randomx_program_loop_end(); + void randomx_dataset_init(); + void randomx_program_epilogue(); + void randomx_sshash_load(); + void randomx_sshash_prefetch(); + void randomx_sshash_end(); + void randomx_sshash_init(); + void randomx_program_end(); +} diff --git a/src/crypto/randomx/program.hpp b/src/crypto/randomx/program.hpp new file mode 100644 index 00000000..40460758 --- /dev/null +++ b/src/crypto/randomx/program.hpp @@ -0,0 +1,60 @@ +/* +Copyright (c) 2018-2019, tevador + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#pragma once + +#include +#include "crypto/randomx/common.hpp" +#include "crypto/randomx/instruction.hpp" +#include "crypto/randomx/blake2/endian.h" + +namespace randomx { + + struct ProgramConfiguration { + uint64_t eMask[2]; + uint32_t readReg0, readReg1, readReg2, readReg3; + }; + + class Program { + public: + Instruction& operator()(int pc) { + return programBuffer[pc]; + } + uint64_t getEntropy(int i) { + return load64(&entropyBuffer[i]); + } + uint32_t getSize() { + return RandomX_CurrentConfig.ProgramSize; + } + private: + uint64_t entropyBuffer[16]; + Instruction programBuffer[RANDOMX_PROGRAM_MAX_SIZE]; + }; + + static_assert(sizeof(Program) % 64 == 0, "Invalid size of class randomx::Program"); +} diff --git a/src/crypto/randomx/randomx.cpp b/src/crypto/randomx/randomx.cpp new file mode 100644 index 00000000..a5f6bc08 --- /dev/null +++ b/src/crypto/randomx/randomx.cpp @@ -0,0 +1,449 @@ +/* +Copyright (c) 2018-2019, tevador + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "crypto/randomx/randomx.h" +#include "crypto/randomx/dataset.hpp" +#include "crypto/randomx/vm_interpreted.hpp" +#include "crypto/randomx/vm_interpreted_light.hpp" +#include "crypto/randomx/vm_compiled.hpp" +#include "crypto/randomx/vm_compiled_light.hpp" +#include "crypto/randomx/blake2/blake2.h" +#include "crypto/randomx/jit_compiler_x86_static.hpp" +#include + +RandomX_ConfigurationWownero::RandomX_ConfigurationWownero() +{ + ArgonSalt = "RandomWOW\x01"; + ProgramIterations = 1024; + ProgramCount = 16; + ScratchpadL2_Size = 131072; + ScratchpadL3_Size = 1048576; + + RANDOMX_FREQ_IADD_RS = 25; + RANDOMX_FREQ_IROR_R = 10; + RANDOMX_FREQ_IROL_R = 0; + RANDOMX_FREQ_FSWAP_R = 8; + RANDOMX_FREQ_FADD_R = 20; + RANDOMX_FREQ_FSUB_R = 20; + RANDOMX_FREQ_FMUL_R = 20; + RANDOMX_FREQ_CBRANCH = 16; + + fillAes4Rx4_Key[0] = rx_set_int_vec_i128(0xcf359e95, 0x141f82b7, 0x7ffbe4a6, 0xf890465d); + fillAes4Rx4_Key[1] = rx_set_int_vec_i128(0x6741ffdc, 0xbd5c5ac3, 0xfee8278a, 0x6a55c450); + fillAes4Rx4_Key[2] = rx_set_int_vec_i128(0x3d324aac, 0xa7279ad2, 0xd524fde4, 0x114c47a4); + fillAes4Rx4_Key[3] = rx_set_int_vec_i128(0x76f6db08, 0x42d3dbd9, 0x99a9aeff, 0x810c3a2a); + fillAes4Rx4_Key[4] = fillAes4Rx4_Key[0]; + fillAes4Rx4_Key[5] = fillAes4Rx4_Key[1]; + fillAes4Rx4_Key[6] = fillAes4Rx4_Key[2]; + fillAes4Rx4_Key[7] = fillAes4Rx4_Key[3]; +} + +RandomX_ConfigurationLoki::RandomX_ConfigurationLoki() +{ + ArgonIterations = 4; + ArgonLanes = 2; + ArgonSalt = "RandomXL\x12"; + ProgramSize = 320; + ProgramCount = 7; + + RANDOMX_FREQ_IADD_RS = 25; + RANDOMX_FREQ_CBRANCH = 16; +} + +RandomX_ConfigurationBase::RandomX_ConfigurationBase() + : ArgonMemory(262144) + , ArgonIterations(3) + , ArgonLanes(1) + , ArgonSalt("RandomX\x03") + , CacheAccesses(8) + , SuperscalarLatency(170) + , DatasetBaseSize(2147483648) + , DatasetExtraSize(33554368) + , ScratchpadL1_Size(16384) + , ScratchpadL2_Size(262144) + , ScratchpadL3_Size(2097152) + , ProgramSize(256) + , ProgramIterations(2048) + , ProgramCount(8) + , JumpBits(8) + , JumpOffset(8) + , RANDOMX_FREQ_IADD_RS(16) + , RANDOMX_FREQ_IADD_M(7) + , RANDOMX_FREQ_ISUB_R(16) + , RANDOMX_FREQ_ISUB_M(7) + , RANDOMX_FREQ_IMUL_R(16) + , RANDOMX_FREQ_IMUL_M(4) + , RANDOMX_FREQ_IMULH_R(4) + , RANDOMX_FREQ_IMULH_M(1) + , RANDOMX_FREQ_ISMULH_R(4) + , RANDOMX_FREQ_ISMULH_M(1) + , RANDOMX_FREQ_IMUL_RCP(8) + , RANDOMX_FREQ_INEG_R(2) + , RANDOMX_FREQ_IXOR_R(15) + , RANDOMX_FREQ_IXOR_M(5) + , RANDOMX_FREQ_IROR_R(8) + , RANDOMX_FREQ_IROL_R(2) + , RANDOMX_FREQ_ISWAP_R(4) + , RANDOMX_FREQ_FSWAP_R(4) + , RANDOMX_FREQ_FADD_R(16) + , RANDOMX_FREQ_FADD_M(5) + , RANDOMX_FREQ_FSUB_R(16) + , RANDOMX_FREQ_FSUB_M(5) + , RANDOMX_FREQ_FSCAL_R(6) + , RANDOMX_FREQ_FMUL_R(32) + , RANDOMX_FREQ_FDIV_M(4) + , RANDOMX_FREQ_FSQRT_R(6) + , RANDOMX_FREQ_CBRANCH(25) + , RANDOMX_FREQ_CFROUND(1) + , RANDOMX_FREQ_ISTORE(16) + , RANDOMX_FREQ_NOP(0) +{ + fillAes4Rx4_Key[0] = rx_set_int_vec_i128(0x99e5d23f, 0x2f546d2b, 0xd1833ddb, 0x6421aadd); + fillAes4Rx4_Key[1] = rx_set_int_vec_i128(0xa5dfcde5, 0x06f79d53, 0xb6913f55, 0xb20e3450); + fillAes4Rx4_Key[2] = rx_set_int_vec_i128(0x171c02bf, 0x0aa4679f, 0x515e7baf, 0x5c3ed904); + fillAes4Rx4_Key[3] = rx_set_int_vec_i128(0xd8ded291, 0xcd673785, 0xe78f5d08, 0x85623763); + fillAes4Rx4_Key[4] = rx_set_int_vec_i128(0x229effb4, 0x3d518b6d, 0xe3d6a7a6, 0xb5826f73); + fillAes4Rx4_Key[5] = rx_set_int_vec_i128(0xb272b7d2, 0xe9024d4e, 0x9c10b3d9, 0xc7566bf3); + fillAes4Rx4_Key[6] = rx_set_int_vec_i128(0xf63befa7, 0x2ba9660a, 0xf765a38b, 0xf273c9e7); + fillAes4Rx4_Key[7] = rx_set_int_vec_i128(0xc0b0762d, 0x0c06d1fd, 0x915839de, 0x7a7cd609); + +#if defined(_M_X64) || defined(__x86_64__) + { + const uint8_t* a = (const uint8_t*)&randomx_sshash_prefetch; + const uint8_t* b = (const uint8_t*)&randomx_sshash_end; + memcpy(codeShhPrefetchTweaked, a, b - a); + } + { + const uint8_t* a = (const uint8_t*)&randomx_program_read_dataset; + const uint8_t* b = (const uint8_t*)&randomx_program_read_dataset_sshash_init; + memcpy(codeReadDatasetTweaked, a, b - a); + } + { + const uint8_t* a = (const uint8_t*)&randomx_program_read_dataset_sshash_init; + const uint8_t* b = (const uint8_t*)&randomx_program_read_dataset_sshash_fin; + memcpy(codeReadDatasetLightSshInitTweaked, a, b - a); + } + { + const uint8_t* a = (const uint8_t*)&randomx_program_loop_load; + const uint8_t* b = (const uint8_t*)&randomx_program_start; + memcpy(codeLoopLoadTweaked, a, b - a); + } +#endif +} + +void RandomX_ConfigurationBase::Apply() +{ +#if defined(_M_X64) || defined(__x86_64__) + *(uint32_t*)(codeShhPrefetchTweaked + 3) = ArgonMemory * 16 - 1; + const uint32_t DatasetBaseMask = DatasetBaseSize - RANDOMX_DATASET_ITEM_SIZE; + *(uint32_t*)(codeReadDatasetTweaked + 7) = DatasetBaseMask; + *(uint32_t*)(codeReadDatasetTweaked + 23) = DatasetBaseMask; + *(uint32_t*)(codeReadDatasetLightSshInitTweaked + 59) = DatasetBaseMask; +#endif + + CacheLineAlignMask_Calculated = (DatasetBaseSize - 1) & ~(RANDOMX_DATASET_ITEM_SIZE - 1); + DatasetExtraItems_Calculated = DatasetExtraSize / RANDOMX_DATASET_ITEM_SIZE; + + ScratchpadL1Mask_Calculated = (ScratchpadL1_Size / sizeof(uint64_t) - 1) * 8; + ScratchpadL1Mask16_Calculated = (ScratchpadL1_Size / sizeof(uint64_t) / 2 - 1) * 16; + ScratchpadL2Mask_Calculated = (ScratchpadL2_Size / sizeof(uint64_t) - 1) * 8; + ScratchpadL2Mask16_Calculated = (ScratchpadL2_Size / sizeof(uint64_t) / 2 - 1) * 16; + ScratchpadL3Mask_Calculated = (((ScratchpadL3_Size / sizeof(uint64_t)) - 1) * 8); + ScratchpadL3Mask64_Calculated = ((ScratchpadL3_Size / sizeof(uint64_t)) / 8 - 1) * 64; + +#if defined(_M_X64) || defined(__x86_64__) + *(uint32_t*)(codeLoopLoadTweaked + 4) = ScratchpadL3Mask64_Calculated; + *(uint32_t*)(codeLoopLoadTweaked + 50) = ScratchpadL3Mask64_Calculated; +#endif + + ConditionMask_Calculated = (1 << JumpBits) - 1; + + constexpr int CEIL_NULL = 0; + int k = 0; + +#if defined(_M_X64) || defined(__x86_64__) +#define JIT_HANDLE(x, prev) randomx::JitCompilerX86::engine[k] = &randomx::JitCompilerX86::h_##x +#else +#define JIT_HANDLE(x, prev) +#endif + +#define INST_HANDLE(x, prev) \ + CEIL_##x = CEIL_##prev + RANDOMX_FREQ_##x; \ + for (; k < CEIL_##x; ++k) { JIT_HANDLE(x, prev); } + + INST_HANDLE(IADD_RS, NULL); + INST_HANDLE(IADD_M, IADD_RS); + INST_HANDLE(ISUB_R, IADD_M); + INST_HANDLE(ISUB_M, ISUB_R); + INST_HANDLE(IMUL_R, ISUB_M); + INST_HANDLE(IMUL_M, IMUL_R); + INST_HANDLE(IMULH_R, IMUL_M); + INST_HANDLE(IMULH_M, IMULH_R); + INST_HANDLE(ISMULH_R, IMULH_M); + INST_HANDLE(ISMULH_M, ISMULH_R); + INST_HANDLE(IMUL_RCP, ISMULH_M); + INST_HANDLE(INEG_R, IMUL_RCP); + INST_HANDLE(IXOR_R, INEG_R); + INST_HANDLE(IXOR_M, IXOR_R); + INST_HANDLE(IROR_R, IXOR_M); + INST_HANDLE(IROL_R, IROR_R); + INST_HANDLE(ISWAP_R, IROL_R); + INST_HANDLE(FSWAP_R, ISWAP_R); + INST_HANDLE(FADD_R, FSWAP_R); + INST_HANDLE(FADD_M, FADD_R); + INST_HANDLE(FSUB_R, FADD_M); + INST_HANDLE(FSUB_M, FSUB_R); + INST_HANDLE(FSCAL_R, FSUB_M); + INST_HANDLE(FMUL_R, FSCAL_R); + INST_HANDLE(FDIV_M, FMUL_R); + INST_HANDLE(FSQRT_R, FDIV_M); + INST_HANDLE(CBRANCH, FSQRT_R); + INST_HANDLE(CFROUND, CBRANCH); + INST_HANDLE(ISTORE, CFROUND); + INST_HANDLE(NOP, ISTORE); +#undef INST_HANDLE +} + +RandomX_ConfigurationMonero RandomX_MoneroConfig; +RandomX_ConfigurationWownero RandomX_WowneroConfig; +RandomX_ConfigurationLoki RandomX_LokiConfig; + +RandomX_ConfigurationBase RandomX_CurrentConfig; + +extern "C" { + + randomx_cache *randomx_alloc_cache(randomx_flags flags) { + randomx_cache *cache = nullptr; + + try { + cache = new randomx_cache(); + switch (flags & (RANDOMX_FLAG_JIT | RANDOMX_FLAG_LARGE_PAGES)) { + case RANDOMX_FLAG_DEFAULT: + cache->dealloc = &randomx::deallocCache; + cache->jit = nullptr; + cache->initialize = &randomx::initCache; + cache->datasetInit = &randomx::initDataset; + cache->memory = (uint8_t*)randomx::DefaultAllocator::allocMemory(RANDOMX_CACHE_MAX_SIZE); + break; + + case RANDOMX_FLAG_JIT: + cache->dealloc = &randomx::deallocCache; + cache->jit = new randomx::JitCompiler(); + cache->initialize = &randomx::initCacheCompile; + cache->datasetInit = cache->jit->getDatasetInitFunc(); + cache->memory = (uint8_t*)randomx::DefaultAllocator::allocMemory(RANDOMX_CACHE_MAX_SIZE); + break; + + case RANDOMX_FLAG_LARGE_PAGES: + cache->dealloc = &randomx::deallocCache; + cache->jit = nullptr; + cache->initialize = &randomx::initCache; + cache->datasetInit = &randomx::initDataset; + cache->memory = (uint8_t*)randomx::LargePageAllocator::allocMemory(RANDOMX_CACHE_MAX_SIZE); + break; + + case RANDOMX_FLAG_JIT | RANDOMX_FLAG_LARGE_PAGES: + cache->dealloc = &randomx::deallocCache; + cache->jit = new randomx::JitCompiler(); + cache->initialize = &randomx::initCacheCompile; + cache->datasetInit = cache->jit->getDatasetInitFunc(); + cache->memory = (uint8_t*)randomx::LargePageAllocator::allocMemory(RANDOMX_CACHE_MAX_SIZE); + break; + + default: + UNREACHABLE; + } + } + catch (std::exception &ex) { + if (cache != nullptr) { + randomx_release_cache(cache); + cache = nullptr; + } + } + + return cache; + } + + void randomx_init_cache(randomx_cache *cache, const void *key, size_t keySize) { + assert(cache != nullptr); + assert(keySize == 0 || key != nullptr); + cache->initialize(cache, key, keySize); + } + + void randomx_release_cache(randomx_cache* cache) { + assert(cache != nullptr); + cache->dealloc(cache); + delete cache; + } + + randomx_dataset *randomx_alloc_dataset(randomx_flags flags) { + randomx_dataset *dataset = nullptr; + + try { + dataset = new randomx_dataset(); + if (flags & RANDOMX_FLAG_LARGE_PAGES) { + dataset->dealloc = &randomx::deallocDataset; + dataset->memory = (uint8_t*)randomx::LargePageAllocator::allocMemory(RANDOMX_DATASET_MAX_SIZE); + } + else { + dataset->dealloc = &randomx::deallocDataset; + dataset->memory = (uint8_t*)randomx::DefaultAllocator::allocMemory(RANDOMX_DATASET_MAX_SIZE); + } + } + catch (std::exception &ex) { + if (dataset != nullptr) { + randomx_release_dataset(dataset); + dataset = nullptr; + } + } + + return dataset; + } + + #define DatasetItemCount ((RandomX_CurrentConfig.DatasetBaseSize + RandomX_CurrentConfig.DatasetExtraSize) / RANDOMX_DATASET_ITEM_SIZE) + + unsigned long randomx_dataset_item_count() { + return DatasetItemCount; + } + + void randomx_init_dataset(randomx_dataset *dataset, randomx_cache *cache, unsigned long startItem, unsigned long itemCount) { + assert(dataset != nullptr); + assert(cache != nullptr); + assert(startItem < DatasetItemCount && itemCount <= DatasetItemCount); + assert(startItem + itemCount <= DatasetItemCount); + cache->datasetInit(cache, dataset->memory + startItem * randomx::CacheLineSize, startItem, startItem + itemCount); + } + + void *randomx_get_dataset_memory(randomx_dataset *dataset) { + assert(dataset != nullptr); + return dataset->memory; + } + + void randomx_release_dataset(randomx_dataset *dataset) { + assert(dataset != nullptr); + dataset->dealloc(dataset); + delete dataset; + } + + randomx_vm *randomx_create_vm(randomx_flags flags, randomx_cache *cache, randomx_dataset *dataset, uint8_t *scratchpad) { + assert(cache != nullptr || (flags & RANDOMX_FLAG_FULL_MEM)); + assert(cache == nullptr || cache->isInitialized()); + assert(dataset != nullptr || !(flags & RANDOMX_FLAG_FULL_MEM)); + + randomx_vm *vm = nullptr; + + try { + switch (flags & (RANDOMX_FLAG_FULL_MEM | RANDOMX_FLAG_JIT | RANDOMX_FLAG_HARD_AES)) { + case RANDOMX_FLAG_DEFAULT: + vm = new randomx::InterpretedLightVmDefault(); + break; + + case RANDOMX_FLAG_FULL_MEM: + vm = new randomx::InterpretedVmDefault(); + break; + + case RANDOMX_FLAG_JIT: + vm = new randomx::CompiledLightVmDefault(); + break; + + case RANDOMX_FLAG_FULL_MEM | RANDOMX_FLAG_JIT: + vm = new randomx::CompiledVmDefault(); + break; + + case RANDOMX_FLAG_HARD_AES: + vm = new randomx::InterpretedLightVmHardAes(); + break; + + case RANDOMX_FLAG_FULL_MEM | RANDOMX_FLAG_HARD_AES: + vm = new randomx::InterpretedVmHardAes(); + break; + + case RANDOMX_FLAG_JIT | RANDOMX_FLAG_HARD_AES: + vm = new randomx::CompiledLightVmHardAes(); + break; + + case RANDOMX_FLAG_FULL_MEM | RANDOMX_FLAG_JIT | RANDOMX_FLAG_HARD_AES: + vm = new randomx::CompiledVmHardAes(); + break; + + default: + UNREACHABLE; + } + + if (cache != nullptr) { + vm->setCache(cache); + } + + if (dataset != nullptr) { + vm->setDataset(dataset); + } + + vm->setScratchpad(scratchpad); + } + catch (std::exception &ex) { + delete vm; + vm = nullptr; + } + + return vm; + } + + void randomx_vm_set_cache(randomx_vm *machine, randomx_cache* cache) { + assert(machine != nullptr); + assert(cache != nullptr && cache->isInitialized()); + machine->setCache(cache); + } + + void randomx_vm_set_dataset(randomx_vm *machine, randomx_dataset *dataset) { + assert(machine != nullptr); + assert(dataset != nullptr); + machine->setDataset(dataset); + } + + void randomx_destroy_vm(randomx_vm *machine) { + assert(machine != nullptr); + delete machine; + } + + void randomx_calculate_hash(randomx_vm *machine, const void *input, size_t inputSize, void *output) { + assert(machine != nullptr); + assert(inputSize == 0 || input != nullptr); + assert(output != nullptr); + alignas(16) uint64_t tempHash[8]; + rx_blake2b(tempHash, sizeof(tempHash), input, inputSize, nullptr, 0); + machine->initScratchpad(&tempHash); + machine->resetRoundingMode(); + for (uint32_t chain = 0; chain < RandomX_CurrentConfig.ProgramCount - 1; ++chain) { + machine->run(&tempHash); + rx_blake2b(tempHash, sizeof(tempHash), machine->getRegisterFile(), sizeof(randomx::RegisterFile), nullptr, 0); + } + machine->run(&tempHash); + machine->getFinalResult(output, RANDOMX_HASH_SIZE); + } + +} diff --git a/src/crypto/randomx/randomx.h b/src/crypto/randomx/randomx.h new file mode 100644 index 00000000..137edc50 --- /dev/null +++ b/src/crypto/randomx/randomx.h @@ -0,0 +1,332 @@ +/* +Copyright (c) 2018-2019, tevador + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef RANDOMX_H +#define RANDOMX_H + +#include +#include +#include +#include "crypto/randomx/intrin_portable.h" + +#define RANDOMX_HASH_SIZE 32 +#define RANDOMX_DATASET_ITEM_SIZE 64 + +#ifndef RANDOMX_EXPORT +#define RANDOMX_EXPORT +#endif + +typedef enum { + RANDOMX_FLAG_DEFAULT = 0, + RANDOMX_FLAG_LARGE_PAGES = 1, + RANDOMX_FLAG_HARD_AES = 2, + RANDOMX_FLAG_FULL_MEM = 4, + RANDOMX_FLAG_JIT = 8, +} randomx_flags; + +typedef struct randomx_dataset randomx_dataset; +typedef struct randomx_cache randomx_cache; +typedef struct randomx_vm randomx_vm; + +struct RandomX_ConfigurationBase +{ + RandomX_ConfigurationBase(); + + void Apply(); + + uint32_t ArgonMemory; + uint32_t ArgonIterations; + uint32_t ArgonLanes; + const char* ArgonSalt; + uint32_t CacheAccesses; + uint32_t SuperscalarLatency; + + uint32_t DatasetBaseSize; + uint32_t DatasetExtraSize; + + uint32_t ScratchpadL1_Size; + uint32_t ScratchpadL2_Size; + uint32_t ScratchpadL3_Size; + + uint32_t ProgramSize; + uint32_t ProgramIterations; + uint32_t ProgramCount; + + uint32_t JumpBits; + uint32_t JumpOffset; + + uint32_t RANDOMX_FREQ_IADD_RS; + uint32_t RANDOMX_FREQ_IADD_M; + uint32_t RANDOMX_FREQ_ISUB_R; + uint32_t RANDOMX_FREQ_ISUB_M; + uint32_t RANDOMX_FREQ_IMUL_R; + uint32_t RANDOMX_FREQ_IMUL_M; + uint32_t RANDOMX_FREQ_IMULH_R; + uint32_t RANDOMX_FREQ_IMULH_M; + uint32_t RANDOMX_FREQ_ISMULH_R; + uint32_t RANDOMX_FREQ_ISMULH_M; + uint32_t RANDOMX_FREQ_IMUL_RCP; + uint32_t RANDOMX_FREQ_INEG_R; + uint32_t RANDOMX_FREQ_IXOR_R; + uint32_t RANDOMX_FREQ_IXOR_M; + uint32_t RANDOMX_FREQ_IROR_R; + uint32_t RANDOMX_FREQ_IROL_R; + uint32_t RANDOMX_FREQ_ISWAP_R; + uint32_t RANDOMX_FREQ_FSWAP_R; + uint32_t RANDOMX_FREQ_FADD_R; + uint32_t RANDOMX_FREQ_FADD_M; + uint32_t RANDOMX_FREQ_FSUB_R; + uint32_t RANDOMX_FREQ_FSUB_M; + uint32_t RANDOMX_FREQ_FSCAL_R; + uint32_t RANDOMX_FREQ_FMUL_R; + uint32_t RANDOMX_FREQ_FDIV_M; + uint32_t RANDOMX_FREQ_FSQRT_R; + uint32_t RANDOMX_FREQ_CBRANCH; + uint32_t RANDOMX_FREQ_CFROUND; + uint32_t RANDOMX_FREQ_ISTORE; + uint32_t RANDOMX_FREQ_NOP; + + rx_vec_i128 fillAes4Rx4_Key[8]; + + uint8_t codeShhPrefetchTweaked[20]; + uint8_t codeReadDatasetTweaked[64]; + uint8_t codeReadDatasetLightSshInitTweaked[68]; + uint8_t codeLoopLoadTweaked[140]; + + uint32_t CacheLineAlignMask_Calculated; + uint32_t DatasetExtraItems_Calculated; + + uint32_t ScratchpadL1Mask_Calculated; + uint32_t ScratchpadL1Mask16_Calculated; + uint32_t ScratchpadL2Mask_Calculated; + uint32_t ScratchpadL2Mask16_Calculated; + uint32_t ScratchpadL3Mask_Calculated; + uint32_t ScratchpadL3Mask64_Calculated; + + uint32_t ConditionMask_Calculated; + + int CEIL_IADD_RS; + int CEIL_IADD_M; + int CEIL_ISUB_R; + int CEIL_ISUB_M; + int CEIL_IMUL_R; + int CEIL_IMUL_M; + int CEIL_IMULH_R; + int CEIL_IMULH_M; + int CEIL_ISMULH_R; + int CEIL_ISMULH_M; + int CEIL_IMUL_RCP; + int CEIL_INEG_R; + int CEIL_IXOR_R; + int CEIL_IXOR_M; + int CEIL_IROR_R; + int CEIL_IROL_R; + int CEIL_ISWAP_R; + int CEIL_FSWAP_R; + int CEIL_FADD_R; + int CEIL_FADD_M; + int CEIL_FSUB_R; + int CEIL_FSUB_M; + int CEIL_FSCAL_R; + int CEIL_FMUL_R; + int CEIL_FDIV_M; + int CEIL_FSQRT_R; + int CEIL_CBRANCH; + int CEIL_CFROUND; + int CEIL_ISTORE; + int CEIL_NOP; +}; + +struct RandomX_ConfigurationMonero : public RandomX_ConfigurationBase {}; +struct RandomX_ConfigurationWownero : public RandomX_ConfigurationBase { RandomX_ConfigurationWownero(); }; +struct RandomX_ConfigurationLoki : public RandomX_ConfigurationBase { RandomX_ConfigurationLoki(); }; + +extern RandomX_ConfigurationMonero RandomX_MoneroConfig; +extern RandomX_ConfigurationWownero RandomX_WowneroConfig; +extern RandomX_ConfigurationLoki RandomX_LokiConfig; + +extern RandomX_ConfigurationBase RandomX_CurrentConfig; + +template +void randomx_apply_config(const T& config) +{ + static_assert(sizeof(T) == sizeof(RandomX_ConfigurationBase), "Invalid RandomX configuration struct size"); + static_assert(std::is_base_of::value, "Incompatible RandomX configuration struct"); + RandomX_CurrentConfig = config; + RandomX_CurrentConfig.Apply(); +} + +#if defined(__cplusplus) +extern "C" { +#endif + +/** + * Creates a randomx_cache structure and allocates memory for RandomX Cache. + * + * @param flags is any combination of these 2 flags (each flag can be set or not set): + * RANDOMX_FLAG_LARGE_PAGES - allocate memory in large pages + * RANDOMX_FLAG_JIT - create cache structure with JIT compilation support; this makes + * subsequent Dataset initialization faster + * + * @return Pointer to an allocated randomx_cache structure. + * NULL is returned if memory allocation fails or if the RANDOMX_FLAG_JIT + * is set and JIT compilation is not supported on the current platform. + */ +RANDOMX_EXPORT randomx_cache *randomx_alloc_cache(randomx_flags flags); + +/** + * Initializes the cache memory and SuperscalarHash using the provided key value. + * + * @param cache is a pointer to a previously allocated randomx_cache structure. Must not be NULL. + * @param key is a pointer to memory which contains the key value. Must not be NULL. + * @param keySize is the number of bytes of the key. +*/ +RANDOMX_EXPORT void randomx_init_cache(randomx_cache *cache, const void *key, size_t keySize); + +/** + * Releases all memory occupied by the randomx_cache structure. + * + * @param cache is a pointer to a previously allocated randomx_cache structure. +*/ +RANDOMX_EXPORT void randomx_release_cache(randomx_cache* cache); + +/** + * Creates a randomx_dataset structure and allocates memory for RandomX Dataset. + * + * @param flags is the initialization flags. Only one flag is supported (can be set or not set): + * RANDOMX_FLAG_LARGE_PAGES - allocate memory in large pages + * + * @return Pointer to an allocated randomx_dataset structure. + * NULL is returned if memory allocation fails. + */ +RANDOMX_EXPORT randomx_dataset *randomx_alloc_dataset(randomx_flags flags); + +/** + * Gets the number of items contained in the dataset. + * + * @return the number of items contained in the dataset. +*/ +RANDOMX_EXPORT unsigned long randomx_dataset_item_count(void); + +/** + * Initializes dataset items. + * + * Note: In order to use the Dataset, all items from 0 to (randomx_dataset_item_count() - 1) must be initialized. + * This may be done by several calls to this function using non-overlapping item sequences. + * + * @param dataset is a pointer to a previously allocated randomx_dataset structure. Must not be NULL. + * @param cache is a pointer to a previously allocated and initialized randomx_cache structure. Must not be NULL. + * @param startItem is the item number where intialization should start. + * @param itemCount is the number of items that should be initialized. +*/ +RANDOMX_EXPORT void randomx_init_dataset(randomx_dataset *dataset, randomx_cache *cache, unsigned long startItem, unsigned long itemCount); + +/** + * Returns a pointer to the internal memory buffer of the dataset structure. The size + * of the internal memory buffer is randomx_dataset_item_count() * RANDOMX_DATASET_ITEM_SIZE. + * + * @param dataset is dataset is a pointer to a previously allocated randomx_dataset structure. Must not be NULL. + * + * @return Pointer to the internal memory buffer of the dataset structure. +*/ +RANDOMX_EXPORT void *randomx_get_dataset_memory(randomx_dataset *dataset); + +/** + * Releases all memory occupied by the randomx_dataset structure. + * + * @param dataset is a pointer to a previously allocated randomx_dataset structure. +*/ +RANDOMX_EXPORT void randomx_release_dataset(randomx_dataset *dataset); + +/** + * Creates and initializes a RandomX virtual machine. + * + * @param flags is any combination of these 4 flags (each flag can be set or not set): + * RANDOMX_FLAG_LARGE_PAGES - allocate scratchpad memory in large pages + * RANDOMX_FLAG_HARD_AES - virtual machine will use hardware accelerated AES + * RANDOMX_FLAG_FULL_MEM - virtual machine will use the full dataset + * RANDOMX_FLAG_JIT - virtual machine will use a JIT compiler + * The numeric values of the flags are ordered so that a higher value will provide + * faster hash calculation and a lower numeric value will provide higher portability. + * Using RANDOMX_FLAG_DEFAULT (all flags not set) works on all platforms, but is the slowest. + * @param cache is a pointer to an initialized randomx_cache structure. Can be + * NULL if RANDOMX_FLAG_FULL_MEM is set. + * @param dataset is a pointer to a randomx_dataset structure. Can be NULL + * if RANDOMX_FLAG_FULL_MEM is not set. + * + * @return Pointer to an initialized randomx_vm structure. + * Returns NULL if: + * (1) Scratchpad memory allocation fails. + * (2) The requested initialization flags are not supported on the current platform. + * (3) cache parameter is NULL and RANDOMX_FLAG_FULL_MEM is not set + * (4) dataset parameter is NULL and RANDOMX_FLAG_FULL_MEM is set +*/ +RANDOMX_EXPORT randomx_vm *randomx_create_vm(randomx_flags flags, randomx_cache *cache, randomx_dataset *dataset, uint8_t *scratchpad); + +/** + * Reinitializes a virtual machine with a new Cache. This function should be called anytime + * the Cache is reinitialized with a new key. + * + * @param machine is a pointer to a randomx_vm structure that was initialized + * without RANDOMX_FLAG_FULL_MEM. Must not be NULL. + * @param cache is a pointer to an initialized randomx_cache structure. Must not be NULL. +*/ +RANDOMX_EXPORT void randomx_vm_set_cache(randomx_vm *machine, randomx_cache* cache); + +/** + * Reinitializes a virtual machine with a new Dataset. + * + * @param machine is a pointer to a randomx_vm structure that was initialized + * with RANDOMX_FLAG_FULL_MEM. Must not be NULL. + * @param dataset is a pointer to an initialized randomx_dataset structure. Must not be NULL. +*/ +RANDOMX_EXPORT void randomx_vm_set_dataset(randomx_vm *machine, randomx_dataset *dataset); + +/** + * Releases all memory occupied by the randomx_vm structure. + * + * @param machine is a pointer to a previously created randomx_vm structure. +*/ +RANDOMX_EXPORT void randomx_destroy_vm(randomx_vm *machine); + +/** + * Calculates a RandomX hash value. + * + * @param machine is a pointer to a randomx_vm structure. Must not be NULL. + * @param input is a pointer to memory to be hashed. Must not be NULL. + * @param inputSize is the number of bytes to be hashed. + * @param output is a pointer to memory where the hash will be stored. Must not + * be NULL and at least RANDOMX_HASH_SIZE bytes must be available for writing. +*/ +RANDOMX_EXPORT void randomx_calculate_hash(randomx_vm *machine, const void *input, size_t inputSize, void *output); + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/src/crypto/randomx/reciprocal.c b/src/crypto/randomx/reciprocal.c new file mode 100644 index 00000000..87cda267 --- /dev/null +++ b/src/crypto/randomx/reciprocal.c @@ -0,0 +1,80 @@ +/* +Copyright (c) 2018-2019, tevador + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include "crypto/randomx/reciprocal.h" + +/* + Calculates rcp = 2**x / divisor for highest integer x such that rcp < 2**64. + divisor must not be 0 or a power of 2 + + Equivalent x86 assembly (divisor in rcx): + + mov edx, 1 + mov r8, rcx + xor eax, eax + bsr rcx, rcx + shl rdx, cl + div r8 + ret + +*/ +uint64_t randomx_reciprocal(uint64_t divisor) { + + assert(divisor != 0); + + const uint64_t p2exp63 = 1ULL << 63; + + uint64_t quotient = p2exp63 / divisor, remainder = p2exp63 % divisor; + + unsigned bsr = 0; //highest set bit in divisor + + for (uint64_t bit = divisor; bit > 0; bit >>= 1) + bsr++; + + for (unsigned shift = 0; shift < bsr; shift++) { + if (remainder >= divisor - remainder) { + quotient = quotient * 2 + 1; + remainder = remainder * 2 - divisor; + } + else { + quotient = quotient * 2; + remainder = remainder * 2; + } + } + + return quotient; +} + +#if !RANDOMX_HAVE_FAST_RECIPROCAL + +uint64_t randomx_reciprocal_fast(uint64_t divisor) { + return randomx_reciprocal(divisor); +} + +#endif diff --git a/src/crypto/randomx/reciprocal.h b/src/crypto/randomx/reciprocal.h new file mode 100644 index 00000000..8858df2b --- /dev/null +++ b/src/crypto/randomx/reciprocal.h @@ -0,0 +1,48 @@ +/* +Copyright (c) 2018-2019, tevador + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#pragma once + +#include + +#if defined(_M_X64) || defined(__x86_64__) +#define RANDOMX_HAVE_FAST_RECIPROCAL 1 +#else +#define RANDOMX_HAVE_FAST_RECIPROCAL 0 +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +uint64_t randomx_reciprocal(uint64_t); +uint64_t randomx_reciprocal_fast(uint64_t); + +#if defined(__cplusplus) +} +#endif diff --git a/src/crypto/randomx/soft_aes.cpp b/src/crypto/randomx/soft_aes.cpp new file mode 100644 index 00000000..a8e592c3 --- /dev/null +++ b/src/crypto/randomx/soft_aes.cpp @@ -0,0 +1,364 @@ +/* +Copyright (c) 2018-2019, tevador + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "crypto/randomx/soft_aes.h" + +alignas(16) const uint8_t sbox[256] = { + 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, + 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, + 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, + 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, + 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, + 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, + 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, + 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, + 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, + 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, + 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, + 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, + 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, + 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, + 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, + 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16, +}; + +alignas(16) const uint32_t lutEnc0[256] = { + 0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, 0x0df2f2ff, 0xbd6b6bd6, 0xb16f6fde, 0x54c5c591, + 0x50303060, 0x03010102, 0xa96767ce, 0x7d2b2b56, 0x19fefee7, 0x62d7d7b5, 0xe6abab4d, 0x9a7676ec, + 0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa, 0x15fafaef, 0xeb5959b2, 0xc947478e, 0x0bf0f0fb, + 0xecadad41, 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, 0xbf9c9c23, 0xf7a4a453, 0x967272e4, 0x5bc0c09b, + 0xc2b7b775, 0x1cfdfde1, 0xae93933d, 0x6a26264c, 0x5a36366c, 0x413f3f7e, 0x02f7f7f5, 0x4fcccc83, + 0x5c343468, 0xf4a5a551, 0x34e5e5d1, 0x08f1f1f9, 0x937171e2, 0x73d8d8ab, 0x53313162, 0x3f15152a, + 0x0c040408, 0x52c7c795, 0x65232346, 0x5ec3c39d, 0x28181830, 0xa1969637, 0x0f05050a, 0xb59a9a2f, + 0x0907070e, 0x36121224, 0x9b80801b, 0x3de2e2df, 0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea, + 0x1b090912, 0x9e83831d, 0x742c2c58, 0x2e1a1a34, 0x2d1b1b36, 0xb26e6edc, 0xee5a5ab4, 0xfba0a05b, + 0xf65252a4, 0x4d3b3b76, 0x61d6d6b7, 0xceb3b37d, 0x7b292952, 0x3ee3e3dd, 0x712f2f5e, 0x97848413, + 0xf55353a6, 0x68d1d1b9, 0x00000000, 0x2cededc1, 0x60202040, 0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6, + 0xbe6a6ad4, 0x46cbcb8d, 0xd9bebe67, 0x4b393972, 0xde4a4a94, 0xd44c4c98, 0xe85858b0, 0x4acfcf85, + 0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed, 0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511, + 0xcf45458a, 0x10f9f9e9, 0x06020204, 0x817f7ffe, 0xf05050a0, 0x443c3c78, 0xba9f9f25, 0xe3a8a84b, + 0xf35151a2, 0xfea3a35d, 0xc0404080, 0x8a8f8f05, 0xad92923f, 0xbc9d9d21, 0x48383870, 0x04f5f5f1, + 0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142, 0x30101020, 0x1affffe5, 0x0ef3f3fd, 0x6dd2d2bf, + 0x4ccdcd81, 0x140c0c18, 0x35131326, 0x2fececc3, 0xe15f5fbe, 0xa2979735, 0xcc444488, 0x3917172e, + 0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a, 0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6, + 0xa06060c0, 0x98818119, 0xd14f4f9e, 0x7fdcdca3, 0x66222244, 0x7e2a2a54, 0xab90903b, 0x8388880b, + 0xca46468c, 0x29eeeec7, 0xd3b8b86b, 0x3c141428, 0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad, + 0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14, 0xdb494992, 0x0a06060c, 0x6c242448, 0xe45c5cb8, + 0x5dc2c29f, 0x6ed3d3bd, 0xefacac43, 0xa66262c4, 0xa8919139, 0xa4959531, 0x37e4e4d3, 0x8b7979f2, + 0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda, 0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949, + 0xb46c6cd8, 0xfa5656ac, 0x07f4f4f3, 0x25eaeacf, 0xaf6565ca, 0x8e7a7af4, 0xe9aeae47, 0x18080810, + 0xd5baba6f, 0x887878f0, 0x6f25254a, 0x722e2e5c, 0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697, + 0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e, 0xdd4b4b96, 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f, + 0x907070e0, 0x423e3e7c, 0xc4b5b571, 0xaa6666cc, 0xd8484890, 0x05030306, 0x01f6f6f7, 0x120e0e1c, + 0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969, 0x91868617, 0x58c1c199, 0x271d1d3a, 0xb99e9e27, + 0x38e1e1d9, 0x13f8f8eb, 0xb398982b, 0x33111122, 0xbb6969d2, 0x70d9d9a9, 0x898e8e07, 0xa7949433, + 0xb69b9b2d, 0x221e1e3c, 0x92878715, 0x20e9e9c9, 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5, + 0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, 0xdabfbf65, 0x31e6e6d7, 0xc6424284, 0xb86868d0, + 0xc3414182, 0xb0999929, 0x772d2d5a, 0x110f0f1e, 0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d, 0x3a16162c, +}; + +alignas(16) const uint32_t lutEnc1[256] = { + 0x6363c6a5, 0x7c7cf884, 0x7777ee99, 0x7b7bf68d, 0xf2f2ff0d, 0x6b6bd6bd, 0x6f6fdeb1, 0xc5c59154, + 0x30306050, 0x01010203, 0x6767cea9, 0x2b2b567d, 0xfefee719, 0xd7d7b562, 0xabab4de6, 0x7676ec9a, + 0xcaca8f45, 0x82821f9d, 0xc9c98940, 0x7d7dfa87, 0xfafaef15, 0x5959b2eb, 0x47478ec9, 0xf0f0fb0b, + 0xadad41ec, 0xd4d4b367, 0xa2a25ffd, 0xafaf45ea, 0x9c9c23bf, 0xa4a453f7, 0x7272e496, 0xc0c09b5b, + 0xb7b775c2, 0xfdfde11c, 0x93933dae, 0x26264c6a, 0x36366c5a, 0x3f3f7e41, 0xf7f7f502, 0xcccc834f, + 0x3434685c, 0xa5a551f4, 0xe5e5d134, 0xf1f1f908, 0x7171e293, 0xd8d8ab73, 0x31316253, 0x15152a3f, + 0x0404080c, 0xc7c79552, 0x23234665, 0xc3c39d5e, 0x18183028, 0x969637a1, 0x05050a0f, 0x9a9a2fb5, + 0x07070e09, 0x12122436, 0x80801b9b, 0xe2e2df3d, 0xebebcd26, 0x27274e69, 0xb2b27fcd, 0x7575ea9f, + 0x0909121b, 0x83831d9e, 0x2c2c5874, 0x1a1a342e, 0x1b1b362d, 0x6e6edcb2, 0x5a5ab4ee, 0xa0a05bfb, + 0x5252a4f6, 0x3b3b764d, 0xd6d6b761, 0xb3b37dce, 0x2929527b, 0xe3e3dd3e, 0x2f2f5e71, 0x84841397, + 0x5353a6f5, 0xd1d1b968, 0x00000000, 0xededc12c, 0x20204060, 0xfcfce31f, 0xb1b179c8, 0x5b5bb6ed, + 0x6a6ad4be, 0xcbcb8d46, 0xbebe67d9, 0x3939724b, 0x4a4a94de, 0x4c4c98d4, 0x5858b0e8, 0xcfcf854a, + 0xd0d0bb6b, 0xefefc52a, 0xaaaa4fe5, 0xfbfbed16, 0x434386c5, 0x4d4d9ad7, 0x33336655, 0x85851194, + 0x45458acf, 0xf9f9e910, 0x02020406, 0x7f7ffe81, 0x5050a0f0, 0x3c3c7844, 0x9f9f25ba, 0xa8a84be3, + 0x5151a2f3, 0xa3a35dfe, 0x404080c0, 0x8f8f058a, 0x92923fad, 0x9d9d21bc, 0x38387048, 0xf5f5f104, + 0xbcbc63df, 0xb6b677c1, 0xdadaaf75, 0x21214263, 0x10102030, 0xffffe51a, 0xf3f3fd0e, 0xd2d2bf6d, + 0xcdcd814c, 0x0c0c1814, 0x13132635, 0xececc32f, 0x5f5fbee1, 0x979735a2, 0x444488cc, 0x17172e39, + 0xc4c49357, 0xa7a755f2, 0x7e7efc82, 0x3d3d7a47, 0x6464c8ac, 0x5d5dbae7, 0x1919322b, 0x7373e695, + 0x6060c0a0, 0x81811998, 0x4f4f9ed1, 0xdcdca37f, 0x22224466, 0x2a2a547e, 0x90903bab, 0x88880b83, + 0x46468cca, 0xeeeec729, 0xb8b86bd3, 0x1414283c, 0xdedea779, 0x5e5ebce2, 0x0b0b161d, 0xdbdbad76, + 0xe0e0db3b, 0x32326456, 0x3a3a744e, 0x0a0a141e, 0x494992db, 0x06060c0a, 0x2424486c, 0x5c5cb8e4, + 0xc2c29f5d, 0xd3d3bd6e, 0xacac43ef, 0x6262c4a6, 0x919139a8, 0x959531a4, 0xe4e4d337, 0x7979f28b, + 0xe7e7d532, 0xc8c88b43, 0x37376e59, 0x6d6ddab7, 0x8d8d018c, 0xd5d5b164, 0x4e4e9cd2, 0xa9a949e0, + 0x6c6cd8b4, 0x5656acfa, 0xf4f4f307, 0xeaeacf25, 0x6565caaf, 0x7a7af48e, 0xaeae47e9, 0x08081018, + 0xbaba6fd5, 0x7878f088, 0x25254a6f, 0x2e2e5c72, 0x1c1c3824, 0xa6a657f1, 0xb4b473c7, 0xc6c69751, + 0xe8e8cb23, 0xdddda17c, 0x7474e89c, 0x1f1f3e21, 0x4b4b96dd, 0xbdbd61dc, 0x8b8b0d86, 0x8a8a0f85, + 0x7070e090, 0x3e3e7c42, 0xb5b571c4, 0x6666ccaa, 0x484890d8, 0x03030605, 0xf6f6f701, 0x0e0e1c12, + 0x6161c2a3, 0x35356a5f, 0x5757aef9, 0xb9b969d0, 0x86861791, 0xc1c19958, 0x1d1d3a27, 0x9e9e27b9, + 0xe1e1d938, 0xf8f8eb13, 0x98982bb3, 0x11112233, 0x6969d2bb, 0xd9d9a970, 0x8e8e0789, 0x949433a7, + 0x9b9b2db6, 0x1e1e3c22, 0x87871592, 0xe9e9c920, 0xcece8749, 0x5555aaff, 0x28285078, 0xdfdfa57a, + 0x8c8c038f, 0xa1a159f8, 0x89890980, 0x0d0d1a17, 0xbfbf65da, 0xe6e6d731, 0x424284c6, 0x6868d0b8, + 0x414182c3, 0x999929b0, 0x2d2d5a77, 0x0f0f1e11, 0xb0b07bcb, 0x5454a8fc, 0xbbbb6dd6, 0x16162c3a, +}; + +alignas(16) const uint32_t lutEnc2[256] = { + 0x63c6a563, 0x7cf8847c, 0x77ee9977, 0x7bf68d7b, 0xf2ff0df2, 0x6bd6bd6b, 0x6fdeb16f, 0xc59154c5, + 0x30605030, 0x01020301, 0x67cea967, 0x2b567d2b, 0xfee719fe, 0xd7b562d7, 0xab4de6ab, 0x76ec9a76, + 0xca8f45ca, 0x821f9d82, 0xc98940c9, 0x7dfa877d, 0xfaef15fa, 0x59b2eb59, 0x478ec947, 0xf0fb0bf0, + 0xad41ecad, 0xd4b367d4, 0xa25ffda2, 0xaf45eaaf, 0x9c23bf9c, 0xa453f7a4, 0x72e49672, 0xc09b5bc0, + 0xb775c2b7, 0xfde11cfd, 0x933dae93, 0x264c6a26, 0x366c5a36, 0x3f7e413f, 0xf7f502f7, 0xcc834fcc, + 0x34685c34, 0xa551f4a5, 0xe5d134e5, 0xf1f908f1, 0x71e29371, 0xd8ab73d8, 0x31625331, 0x152a3f15, + 0x04080c04, 0xc79552c7, 0x23466523, 0xc39d5ec3, 0x18302818, 0x9637a196, 0x050a0f05, 0x9a2fb59a, + 0x070e0907, 0x12243612, 0x801b9b80, 0xe2df3de2, 0xebcd26eb, 0x274e6927, 0xb27fcdb2, 0x75ea9f75, + 0x09121b09, 0x831d9e83, 0x2c58742c, 0x1a342e1a, 0x1b362d1b, 0x6edcb26e, 0x5ab4ee5a, 0xa05bfba0, + 0x52a4f652, 0x3b764d3b, 0xd6b761d6, 0xb37dceb3, 0x29527b29, 0xe3dd3ee3, 0x2f5e712f, 0x84139784, + 0x53a6f553, 0xd1b968d1, 0x00000000, 0xedc12ced, 0x20406020, 0xfce31ffc, 0xb179c8b1, 0x5bb6ed5b, + 0x6ad4be6a, 0xcb8d46cb, 0xbe67d9be, 0x39724b39, 0x4a94de4a, 0x4c98d44c, 0x58b0e858, 0xcf854acf, + 0xd0bb6bd0, 0xefc52aef, 0xaa4fe5aa, 0xfbed16fb, 0x4386c543, 0x4d9ad74d, 0x33665533, 0x85119485, + 0x458acf45, 0xf9e910f9, 0x02040602, 0x7ffe817f, 0x50a0f050, 0x3c78443c, 0x9f25ba9f, 0xa84be3a8, + 0x51a2f351, 0xa35dfea3, 0x4080c040, 0x8f058a8f, 0x923fad92, 0x9d21bc9d, 0x38704838, 0xf5f104f5, + 0xbc63dfbc, 0xb677c1b6, 0xdaaf75da, 0x21426321, 0x10203010, 0xffe51aff, 0xf3fd0ef3, 0xd2bf6dd2, + 0xcd814ccd, 0x0c18140c, 0x13263513, 0xecc32fec, 0x5fbee15f, 0x9735a297, 0x4488cc44, 0x172e3917, + 0xc49357c4, 0xa755f2a7, 0x7efc827e, 0x3d7a473d, 0x64c8ac64, 0x5dbae75d, 0x19322b19, 0x73e69573, + 0x60c0a060, 0x81199881, 0x4f9ed14f, 0xdca37fdc, 0x22446622, 0x2a547e2a, 0x903bab90, 0x880b8388, + 0x468cca46, 0xeec729ee, 0xb86bd3b8, 0x14283c14, 0xdea779de, 0x5ebce25e, 0x0b161d0b, 0xdbad76db, + 0xe0db3be0, 0x32645632, 0x3a744e3a, 0x0a141e0a, 0x4992db49, 0x060c0a06, 0x24486c24, 0x5cb8e45c, + 0xc29f5dc2, 0xd3bd6ed3, 0xac43efac, 0x62c4a662, 0x9139a891, 0x9531a495, 0xe4d337e4, 0x79f28b79, + 0xe7d532e7, 0xc88b43c8, 0x376e5937, 0x6ddab76d, 0x8d018c8d, 0xd5b164d5, 0x4e9cd24e, 0xa949e0a9, + 0x6cd8b46c, 0x56acfa56, 0xf4f307f4, 0xeacf25ea, 0x65caaf65, 0x7af48e7a, 0xae47e9ae, 0x08101808, + 0xba6fd5ba, 0x78f08878, 0x254a6f25, 0x2e5c722e, 0x1c38241c, 0xa657f1a6, 0xb473c7b4, 0xc69751c6, + 0xe8cb23e8, 0xdda17cdd, 0x74e89c74, 0x1f3e211f, 0x4b96dd4b, 0xbd61dcbd, 0x8b0d868b, 0x8a0f858a, + 0x70e09070, 0x3e7c423e, 0xb571c4b5, 0x66ccaa66, 0x4890d848, 0x03060503, 0xf6f701f6, 0x0e1c120e, + 0x61c2a361, 0x356a5f35, 0x57aef957, 0xb969d0b9, 0x86179186, 0xc19958c1, 0x1d3a271d, 0x9e27b99e, + 0xe1d938e1, 0xf8eb13f8, 0x982bb398, 0x11223311, 0x69d2bb69, 0xd9a970d9, 0x8e07898e, 0x9433a794, + 0x9b2db69b, 0x1e3c221e, 0x87159287, 0xe9c920e9, 0xce8749ce, 0x55aaff55, 0x28507828, 0xdfa57adf, + 0x8c038f8c, 0xa159f8a1, 0x89098089, 0x0d1a170d, 0xbf65dabf, 0xe6d731e6, 0x4284c642, 0x68d0b868, + 0x4182c341, 0x9929b099, 0x2d5a772d, 0x0f1e110f, 0xb07bcbb0, 0x54a8fc54, 0xbb6dd6bb, 0x162c3a16, +}; + +alignas(16) const uint32_t lutEnc3[256] = { + 0xc6a56363, 0xf8847c7c, 0xee997777, 0xf68d7b7b, 0xff0df2f2, 0xd6bd6b6b, 0xdeb16f6f, 0x9154c5c5, + 0x60503030, 0x02030101, 0xcea96767, 0x567d2b2b, 0xe719fefe, 0xb562d7d7, 0x4de6abab, 0xec9a7676, + 0x8f45caca, 0x1f9d8282, 0x8940c9c9, 0xfa877d7d, 0xef15fafa, 0xb2eb5959, 0x8ec94747, 0xfb0bf0f0, + 0x41ecadad, 0xb367d4d4, 0x5ffda2a2, 0x45eaafaf, 0x23bf9c9c, 0x53f7a4a4, 0xe4967272, 0x9b5bc0c0, + 0x75c2b7b7, 0xe11cfdfd, 0x3dae9393, 0x4c6a2626, 0x6c5a3636, 0x7e413f3f, 0xf502f7f7, 0x834fcccc, + 0x685c3434, 0x51f4a5a5, 0xd134e5e5, 0xf908f1f1, 0xe2937171, 0xab73d8d8, 0x62533131, 0x2a3f1515, + 0x080c0404, 0x9552c7c7, 0x46652323, 0x9d5ec3c3, 0x30281818, 0x37a19696, 0x0a0f0505, 0x2fb59a9a, + 0x0e090707, 0x24361212, 0x1b9b8080, 0xdf3de2e2, 0xcd26ebeb, 0x4e692727, 0x7fcdb2b2, 0xea9f7575, + 0x121b0909, 0x1d9e8383, 0x58742c2c, 0x342e1a1a, 0x362d1b1b, 0xdcb26e6e, 0xb4ee5a5a, 0x5bfba0a0, + 0xa4f65252, 0x764d3b3b, 0xb761d6d6, 0x7dceb3b3, 0x527b2929, 0xdd3ee3e3, 0x5e712f2f, 0x13978484, + 0xa6f55353, 0xb968d1d1, 0x00000000, 0xc12ceded, 0x40602020, 0xe31ffcfc, 0x79c8b1b1, 0xb6ed5b5b, + 0xd4be6a6a, 0x8d46cbcb, 0x67d9bebe, 0x724b3939, 0x94de4a4a, 0x98d44c4c, 0xb0e85858, 0x854acfcf, + 0xbb6bd0d0, 0xc52aefef, 0x4fe5aaaa, 0xed16fbfb, 0x86c54343, 0x9ad74d4d, 0x66553333, 0x11948585, + 0x8acf4545, 0xe910f9f9, 0x04060202, 0xfe817f7f, 0xa0f05050, 0x78443c3c, 0x25ba9f9f, 0x4be3a8a8, + 0xa2f35151, 0x5dfea3a3, 0x80c04040, 0x058a8f8f, 0x3fad9292, 0x21bc9d9d, 0x70483838, 0xf104f5f5, + 0x63dfbcbc, 0x77c1b6b6, 0xaf75dada, 0x42632121, 0x20301010, 0xe51affff, 0xfd0ef3f3, 0xbf6dd2d2, + 0x814ccdcd, 0x18140c0c, 0x26351313, 0xc32fecec, 0xbee15f5f, 0x35a29797, 0x88cc4444, 0x2e391717, + 0x9357c4c4, 0x55f2a7a7, 0xfc827e7e, 0x7a473d3d, 0xc8ac6464, 0xbae75d5d, 0x322b1919, 0xe6957373, + 0xc0a06060, 0x19988181, 0x9ed14f4f, 0xa37fdcdc, 0x44662222, 0x547e2a2a, 0x3bab9090, 0x0b838888, + 0x8cca4646, 0xc729eeee, 0x6bd3b8b8, 0x283c1414, 0xa779dede, 0xbce25e5e, 0x161d0b0b, 0xad76dbdb, + 0xdb3be0e0, 0x64563232, 0x744e3a3a, 0x141e0a0a, 0x92db4949, 0x0c0a0606, 0x486c2424, 0xb8e45c5c, + 0x9f5dc2c2, 0xbd6ed3d3, 0x43efacac, 0xc4a66262, 0x39a89191, 0x31a49595, 0xd337e4e4, 0xf28b7979, + 0xd532e7e7, 0x8b43c8c8, 0x6e593737, 0xdab76d6d, 0x018c8d8d, 0xb164d5d5, 0x9cd24e4e, 0x49e0a9a9, + 0xd8b46c6c, 0xacfa5656, 0xf307f4f4, 0xcf25eaea, 0xcaaf6565, 0xf48e7a7a, 0x47e9aeae, 0x10180808, + 0x6fd5baba, 0xf0887878, 0x4a6f2525, 0x5c722e2e, 0x38241c1c, 0x57f1a6a6, 0x73c7b4b4, 0x9751c6c6, + 0xcb23e8e8, 0xa17cdddd, 0xe89c7474, 0x3e211f1f, 0x96dd4b4b, 0x61dcbdbd, 0x0d868b8b, 0x0f858a8a, + 0xe0907070, 0x7c423e3e, 0x71c4b5b5, 0xccaa6666, 0x90d84848, 0x06050303, 0xf701f6f6, 0x1c120e0e, + 0xc2a36161, 0x6a5f3535, 0xaef95757, 0x69d0b9b9, 0x17918686, 0x9958c1c1, 0x3a271d1d, 0x27b99e9e, + 0xd938e1e1, 0xeb13f8f8, 0x2bb39898, 0x22331111, 0xd2bb6969, 0xa970d9d9, 0x07898e8e, 0x33a79494, + 0x2db69b9b, 0x3c221e1e, 0x15928787, 0xc920e9e9, 0x8749cece, 0xaaff5555, 0x50782828, 0xa57adfdf, + 0x038f8c8c, 0x59f8a1a1, 0x09808989, 0x1a170d0d, 0x65dabfbf, 0xd731e6e6, 0x84c64242, 0xd0b86868, + 0x82c34141, 0x29b09999, 0x5a772d2d, 0x1e110f0f, 0x7bcbb0b0, 0xa8fc5454, 0x6dd6bbbb, 0x2c3a1616, +}; + +alignas(16) const uint32_t lutDec0[256] = { + 0x50a7f451, 0x5365417e, 0xc3a4171a, 0x965e273a, 0xcb6bab3b, 0xf1459d1f, 0xab58faac, 0x9303e34b, + 0x55fa3020, 0xf66d76ad, 0x9176cc88, 0x254c02f5, 0xfcd7e54f, 0xd7cb2ac5, 0x80443526, 0x8fa362b5, + 0x495ab1de, 0x671bba25, 0x980eea45, 0xe1c0fe5d, 0x02752fc3, 0x12f04c81, 0xa397468d, 0xc6f9d36b, + 0xe75f8f03, 0x959c9215, 0xeb7a6dbf, 0xda595295, 0x2d83bed4, 0xd3217458, 0x2969e049, 0x44c8c98e, + 0x6a89c275, 0x78798ef4, 0x6b3e5899, 0xdd71b927, 0xb64fe1be, 0x17ad88f0, 0x66ac20c9, 0xb43ace7d, + 0x184adf63, 0x82311ae5, 0x60335197, 0x457f5362, 0xe07764b1, 0x84ae6bbb, 0x1ca081fe, 0x942b08f9, + 0x58684870, 0x19fd458f, 0x876cde94, 0xb7f87b52, 0x23d373ab, 0xe2024b72, 0x578f1fe3, 0x2aab5566, + 0x0728ebb2, 0x03c2b52f, 0x9a7bc586, 0xa50837d3, 0xf2872830, 0xb2a5bf23, 0xba6a0302, 0x5c8216ed, + 0x2b1ccf8a, 0x92b479a7, 0xf0f207f3, 0xa1e2694e, 0xcdf4da65, 0xd5be0506, 0x1f6234d1, 0x8afea6c4, + 0x9d532e34, 0xa055f3a2, 0x32e18a05, 0x75ebf6a4, 0x39ec830b, 0xaaef6040, 0x069f715e, 0x51106ebd, + 0xf98a213e, 0x3d06dd96, 0xae053edd, 0x46bde64d, 0xb58d5491, 0x055dc471, 0x6fd40604, 0xff155060, + 0x24fb9819, 0x97e9bdd6, 0xcc434089, 0x779ed967, 0xbd42e8b0, 0x888b8907, 0x385b19e7, 0xdbeec879, + 0x470a7ca1, 0xe90f427c, 0xc91e84f8, 0x00000000, 0x83868009, 0x48ed2b32, 0xac70111e, 0x4e725a6c, + 0xfbff0efd, 0x5638850f, 0x1ed5ae3d, 0x27392d36, 0x64d90f0a, 0x21a65c68, 0xd1545b9b, 0x3a2e3624, + 0xb1670a0c, 0x0fe75793, 0xd296eeb4, 0x9e919b1b, 0x4fc5c080, 0xa220dc61, 0x694b775a, 0x161a121c, + 0x0aba93e2, 0xe52aa0c0, 0x43e0223c, 0x1d171b12, 0x0b0d090e, 0xadc78bf2, 0xb9a8b62d, 0xc8a91e14, + 0x8519f157, 0x4c0775af, 0xbbdd99ee, 0xfd607fa3, 0x9f2601f7, 0xbcf5725c, 0xc53b6644, 0x347efb5b, + 0x7629438b, 0xdcc623cb, 0x68fcedb6, 0x63f1e4b8, 0xcadc31d7, 0x10856342, 0x40229713, 0x2011c684, + 0x7d244a85, 0xf83dbbd2, 0x1132f9ae, 0x6da129c7, 0x4b2f9e1d, 0xf330b2dc, 0xec52860d, 0xd0e3c177, + 0x6c16b32b, 0x99b970a9, 0xfa489411, 0x2264e947, 0xc48cfca8, 0x1a3ff0a0, 0xd82c7d56, 0xef903322, + 0xc74e4987, 0xc1d138d9, 0xfea2ca8c, 0x360bd498, 0xcf81f5a6, 0x28de7aa5, 0x268eb7da, 0xa4bfad3f, + 0xe49d3a2c, 0x0d927850, 0x9bcc5f6a, 0x62467e54, 0xc2138df6, 0xe8b8d890, 0x5ef7392e, 0xf5afc382, + 0xbe805d9f, 0x7c93d069, 0xa92dd56f, 0xb31225cf, 0x3b99acc8, 0xa77d1810, 0x6e639ce8, 0x7bbb3bdb, + 0x097826cd, 0xf418596e, 0x01b79aec, 0xa89a4f83, 0x656e95e6, 0x7ee6ffaa, 0x08cfbc21, 0xe6e815ef, + 0xd99be7ba, 0xce366f4a, 0xd4099fea, 0xd67cb029, 0xafb2a431, 0x31233f2a, 0x3094a5c6, 0xc066a235, + 0x37bc4e74, 0xa6ca82fc, 0xb0d090e0, 0x15d8a733, 0x4a9804f1, 0xf7daec41, 0x0e50cd7f, 0x2ff69117, + 0x8dd64d76, 0x4db0ef43, 0x544daacc, 0xdf0496e4, 0xe3b5d19e, 0x1b886a4c, 0xb81f2cc1, 0x7f516546, + 0x04ea5e9d, 0x5d358c01, 0x737487fa, 0x2e410bfb, 0x5a1d67b3, 0x52d2db92, 0x335610e9, 0x1347d66d, + 0x8c61d79a, 0x7a0ca137, 0x8e14f859, 0x893c13eb, 0xee27a9ce, 0x35c961b7, 0xede51ce1, 0x3cb1477a, + 0x59dfd29c, 0x3f73f255, 0x79ce1418, 0xbf37c773, 0xeacdf753, 0x5baafd5f, 0x146f3ddf, 0x86db4478, + 0x81f3afca, 0x3ec468b9, 0x2c342438, 0x5f40a3c2, 0x72c31d16, 0x0c25e2bc, 0x8b493c28, 0x41950dff, + 0x7101a839, 0xdeb30c08, 0x9ce4b4d8, 0x90c15664, 0x6184cb7b, 0x70b632d5, 0x745c6c48, 0x4257b8d0, +}; + +alignas(16) const uint32_t lutDec1[256] = { + 0xa7f45150, 0x65417e53, 0xa4171ac3, 0x5e273a96, 0x6bab3bcb, 0x459d1ff1, 0x58faacab, 0x03e34b93, + 0xfa302055, 0x6d76adf6, 0x76cc8891, 0x4c02f525, 0xd7e54ffc, 0xcb2ac5d7, 0x44352680, 0xa362b58f, + 0x5ab1de49, 0x1bba2567, 0x0eea4598, 0xc0fe5de1, 0x752fc302, 0xf04c8112, 0x97468da3, 0xf9d36bc6, + 0x5f8f03e7, 0x9c921595, 0x7a6dbfeb, 0x595295da, 0x83bed42d, 0x217458d3, 0x69e04929, 0xc8c98e44, + 0x89c2756a, 0x798ef478, 0x3e58996b, 0x71b927dd, 0x4fe1beb6, 0xad88f017, 0xac20c966, 0x3ace7db4, + 0x4adf6318, 0x311ae582, 0x33519760, 0x7f536245, 0x7764b1e0, 0xae6bbb84, 0xa081fe1c, 0x2b08f994, + 0x68487058, 0xfd458f19, 0x6cde9487, 0xf87b52b7, 0xd373ab23, 0x024b72e2, 0x8f1fe357, 0xab55662a, + 0x28ebb207, 0xc2b52f03, 0x7bc5869a, 0x0837d3a5, 0x872830f2, 0xa5bf23b2, 0x6a0302ba, 0x8216ed5c, + 0x1ccf8a2b, 0xb479a792, 0xf207f3f0, 0xe2694ea1, 0xf4da65cd, 0xbe0506d5, 0x6234d11f, 0xfea6c48a, + 0x532e349d, 0x55f3a2a0, 0xe18a0532, 0xebf6a475, 0xec830b39, 0xef6040aa, 0x9f715e06, 0x106ebd51, + 0x8a213ef9, 0x06dd963d, 0x053eddae, 0xbde64d46, 0x8d5491b5, 0x5dc47105, 0xd406046f, 0x155060ff, + 0xfb981924, 0xe9bdd697, 0x434089cc, 0x9ed96777, 0x42e8b0bd, 0x8b890788, 0x5b19e738, 0xeec879db, + 0x0a7ca147, 0x0f427ce9, 0x1e84f8c9, 0x00000000, 0x86800983, 0xed2b3248, 0x70111eac, 0x725a6c4e, + 0xff0efdfb, 0x38850f56, 0xd5ae3d1e, 0x392d3627, 0xd90f0a64, 0xa65c6821, 0x545b9bd1, 0x2e36243a, + 0x670a0cb1, 0xe757930f, 0x96eeb4d2, 0x919b1b9e, 0xc5c0804f, 0x20dc61a2, 0x4b775a69, 0x1a121c16, + 0xba93e20a, 0x2aa0c0e5, 0xe0223c43, 0x171b121d, 0x0d090e0b, 0xc78bf2ad, 0xa8b62db9, 0xa91e14c8, + 0x19f15785, 0x0775af4c, 0xdd99eebb, 0x607fa3fd, 0x2601f79f, 0xf5725cbc, 0x3b6644c5, 0x7efb5b34, + 0x29438b76, 0xc623cbdc, 0xfcedb668, 0xf1e4b863, 0xdc31d7ca, 0x85634210, 0x22971340, 0x11c68420, + 0x244a857d, 0x3dbbd2f8, 0x32f9ae11, 0xa129c76d, 0x2f9e1d4b, 0x30b2dcf3, 0x52860dec, 0xe3c177d0, + 0x16b32b6c, 0xb970a999, 0x489411fa, 0x64e94722, 0x8cfca8c4, 0x3ff0a01a, 0x2c7d56d8, 0x903322ef, + 0x4e4987c7, 0xd138d9c1, 0xa2ca8cfe, 0x0bd49836, 0x81f5a6cf, 0xde7aa528, 0x8eb7da26, 0xbfad3fa4, + 0x9d3a2ce4, 0x9278500d, 0xcc5f6a9b, 0x467e5462, 0x138df6c2, 0xb8d890e8, 0xf7392e5e, 0xafc382f5, + 0x805d9fbe, 0x93d0697c, 0x2dd56fa9, 0x1225cfb3, 0x99acc83b, 0x7d1810a7, 0x639ce86e, 0xbb3bdb7b, + 0x7826cd09, 0x18596ef4, 0xb79aec01, 0x9a4f83a8, 0x6e95e665, 0xe6ffaa7e, 0xcfbc2108, 0xe815efe6, + 0x9be7bad9, 0x366f4ace, 0x099fead4, 0x7cb029d6, 0xb2a431af, 0x233f2a31, 0x94a5c630, 0x66a235c0, + 0xbc4e7437, 0xca82fca6, 0xd090e0b0, 0xd8a73315, 0x9804f14a, 0xdaec41f7, 0x50cd7f0e, 0xf691172f, + 0xd64d768d, 0xb0ef434d, 0x4daacc54, 0x0496e4df, 0xb5d19ee3, 0x886a4c1b, 0x1f2cc1b8, 0x5165467f, + 0xea5e9d04, 0x358c015d, 0x7487fa73, 0x410bfb2e, 0x1d67b35a, 0xd2db9252, 0x5610e933, 0x47d66d13, + 0x61d79a8c, 0x0ca1377a, 0x14f8598e, 0x3c13eb89, 0x27a9ceee, 0xc961b735, 0xe51ce1ed, 0xb1477a3c, + 0xdfd29c59, 0x73f2553f, 0xce141879, 0x37c773bf, 0xcdf753ea, 0xaafd5f5b, 0x6f3ddf14, 0xdb447886, + 0xf3afca81, 0xc468b93e, 0x3424382c, 0x40a3c25f, 0xc31d1672, 0x25e2bc0c, 0x493c288b, 0x950dff41, + 0x01a83971, 0xb30c08de, 0xe4b4d89c, 0xc1566490, 0x84cb7b61, 0xb632d570, 0x5c6c4874, 0x57b8d042, +}; + +alignas(16) const uint32_t lutDec2[256] = { + 0xf45150a7, 0x417e5365, 0x171ac3a4, 0x273a965e, 0xab3bcb6b, 0x9d1ff145, 0xfaacab58, 0xe34b9303, + 0x302055fa, 0x76adf66d, 0xcc889176, 0x02f5254c, 0xe54ffcd7, 0x2ac5d7cb, 0x35268044, 0x62b58fa3, + 0xb1de495a, 0xba25671b, 0xea45980e, 0xfe5de1c0, 0x2fc30275, 0x4c8112f0, 0x468da397, 0xd36bc6f9, + 0x8f03e75f, 0x9215959c, 0x6dbfeb7a, 0x5295da59, 0xbed42d83, 0x7458d321, 0xe0492969, 0xc98e44c8, + 0xc2756a89, 0x8ef47879, 0x58996b3e, 0xb927dd71, 0xe1beb64f, 0x88f017ad, 0x20c966ac, 0xce7db43a, + 0xdf63184a, 0x1ae58231, 0x51976033, 0x5362457f, 0x64b1e077, 0x6bbb84ae, 0x81fe1ca0, 0x08f9942b, + 0x48705868, 0x458f19fd, 0xde94876c, 0x7b52b7f8, 0x73ab23d3, 0x4b72e202, 0x1fe3578f, 0x55662aab, + 0xebb20728, 0xb52f03c2, 0xc5869a7b, 0x37d3a508, 0x2830f287, 0xbf23b2a5, 0x0302ba6a, 0x16ed5c82, + 0xcf8a2b1c, 0x79a792b4, 0x07f3f0f2, 0x694ea1e2, 0xda65cdf4, 0x0506d5be, 0x34d11f62, 0xa6c48afe, + 0x2e349d53, 0xf3a2a055, 0x8a0532e1, 0xf6a475eb, 0x830b39ec, 0x6040aaef, 0x715e069f, 0x6ebd5110, + 0x213ef98a, 0xdd963d06, 0x3eddae05, 0xe64d46bd, 0x5491b58d, 0xc471055d, 0x06046fd4, 0x5060ff15, + 0x981924fb, 0xbdd697e9, 0x4089cc43, 0xd967779e, 0xe8b0bd42, 0x8907888b, 0x19e7385b, 0xc879dbee, + 0x7ca1470a, 0x427ce90f, 0x84f8c91e, 0x00000000, 0x80098386, 0x2b3248ed, 0x111eac70, 0x5a6c4e72, + 0x0efdfbff, 0x850f5638, 0xae3d1ed5, 0x2d362739, 0x0f0a64d9, 0x5c6821a6, 0x5b9bd154, 0x36243a2e, + 0x0a0cb167, 0x57930fe7, 0xeeb4d296, 0x9b1b9e91, 0xc0804fc5, 0xdc61a220, 0x775a694b, 0x121c161a, + 0x93e20aba, 0xa0c0e52a, 0x223c43e0, 0x1b121d17, 0x090e0b0d, 0x8bf2adc7, 0xb62db9a8, 0x1e14c8a9, + 0xf1578519, 0x75af4c07, 0x99eebbdd, 0x7fa3fd60, 0x01f79f26, 0x725cbcf5, 0x6644c53b, 0xfb5b347e, + 0x438b7629, 0x23cbdcc6, 0xedb668fc, 0xe4b863f1, 0x31d7cadc, 0x63421085, 0x97134022, 0xc6842011, + 0x4a857d24, 0xbbd2f83d, 0xf9ae1132, 0x29c76da1, 0x9e1d4b2f, 0xb2dcf330, 0x860dec52, 0xc177d0e3, + 0xb32b6c16, 0x70a999b9, 0x9411fa48, 0xe9472264, 0xfca8c48c, 0xf0a01a3f, 0x7d56d82c, 0x3322ef90, + 0x4987c74e, 0x38d9c1d1, 0xca8cfea2, 0xd498360b, 0xf5a6cf81, 0x7aa528de, 0xb7da268e, 0xad3fa4bf, + 0x3a2ce49d, 0x78500d92, 0x5f6a9bcc, 0x7e546246, 0x8df6c213, 0xd890e8b8, 0x392e5ef7, 0xc382f5af, + 0x5d9fbe80, 0xd0697c93, 0xd56fa92d, 0x25cfb312, 0xacc83b99, 0x1810a77d, 0x9ce86e63, 0x3bdb7bbb, + 0x26cd0978, 0x596ef418, 0x9aec01b7, 0x4f83a89a, 0x95e6656e, 0xffaa7ee6, 0xbc2108cf, 0x15efe6e8, + 0xe7bad99b, 0x6f4ace36, 0x9fead409, 0xb029d67c, 0xa431afb2, 0x3f2a3123, 0xa5c63094, 0xa235c066, + 0x4e7437bc, 0x82fca6ca, 0x90e0b0d0, 0xa73315d8, 0x04f14a98, 0xec41f7da, 0xcd7f0e50, 0x91172ff6, + 0x4d768dd6, 0xef434db0, 0xaacc544d, 0x96e4df04, 0xd19ee3b5, 0x6a4c1b88, 0x2cc1b81f, 0x65467f51, + 0x5e9d04ea, 0x8c015d35, 0x87fa7374, 0x0bfb2e41, 0x67b35a1d, 0xdb9252d2, 0x10e93356, 0xd66d1347, + 0xd79a8c61, 0xa1377a0c, 0xf8598e14, 0x13eb893c, 0xa9ceee27, 0x61b735c9, 0x1ce1ede5, 0x477a3cb1, + 0xd29c59df, 0xf2553f73, 0x141879ce, 0xc773bf37, 0xf753eacd, 0xfd5f5baa, 0x3ddf146f, 0x447886db, + 0xafca81f3, 0x68b93ec4, 0x24382c34, 0xa3c25f40, 0x1d1672c3, 0xe2bc0c25, 0x3c288b49, 0x0dff4195, + 0xa8397101, 0x0c08deb3, 0xb4d89ce4, 0x566490c1, 0xcb7b6184, 0x32d570b6, 0x6c48745c, 0xb8d04257, +}; + +alignas(16) const uint32_t lutDec3[256] = { + 0x5150a7f4, 0x7e536541, 0x1ac3a417, 0x3a965e27, 0x3bcb6bab, 0x1ff1459d, 0xacab58fa, 0x4b9303e3, + 0x2055fa30, 0xadf66d76, 0x889176cc, 0xf5254c02, 0x4ffcd7e5, 0xc5d7cb2a, 0x26804435, 0xb58fa362, + 0xde495ab1, 0x25671bba, 0x45980eea, 0x5de1c0fe, 0xc302752f, 0x8112f04c, 0x8da39746, 0x6bc6f9d3, + 0x03e75f8f, 0x15959c92, 0xbfeb7a6d, 0x95da5952, 0xd42d83be, 0x58d32174, 0x492969e0, 0x8e44c8c9, + 0x756a89c2, 0xf478798e, 0x996b3e58, 0x27dd71b9, 0xbeb64fe1, 0xf017ad88, 0xc966ac20, 0x7db43ace, + 0x63184adf, 0xe582311a, 0x97603351, 0x62457f53, 0xb1e07764, 0xbb84ae6b, 0xfe1ca081, 0xf9942b08, + 0x70586848, 0x8f19fd45, 0x94876cde, 0x52b7f87b, 0xab23d373, 0x72e2024b, 0xe3578f1f, 0x662aab55, + 0xb20728eb, 0x2f03c2b5, 0x869a7bc5, 0xd3a50837, 0x30f28728, 0x23b2a5bf, 0x02ba6a03, 0xed5c8216, + 0x8a2b1ccf, 0xa792b479, 0xf3f0f207, 0x4ea1e269, 0x65cdf4da, 0x06d5be05, 0xd11f6234, 0xc48afea6, + 0x349d532e, 0xa2a055f3, 0x0532e18a, 0xa475ebf6, 0x0b39ec83, 0x40aaef60, 0x5e069f71, 0xbd51106e, + 0x3ef98a21, 0x963d06dd, 0xddae053e, 0x4d46bde6, 0x91b58d54, 0x71055dc4, 0x046fd406, 0x60ff1550, + 0x1924fb98, 0xd697e9bd, 0x89cc4340, 0x67779ed9, 0xb0bd42e8, 0x07888b89, 0xe7385b19, 0x79dbeec8, + 0xa1470a7c, 0x7ce90f42, 0xf8c91e84, 0x00000000, 0x09838680, 0x3248ed2b, 0x1eac7011, 0x6c4e725a, + 0xfdfbff0e, 0x0f563885, 0x3d1ed5ae, 0x3627392d, 0x0a64d90f, 0x6821a65c, 0x9bd1545b, 0x243a2e36, + 0x0cb1670a, 0x930fe757, 0xb4d296ee, 0x1b9e919b, 0x804fc5c0, 0x61a220dc, 0x5a694b77, 0x1c161a12, + 0xe20aba93, 0xc0e52aa0, 0x3c43e022, 0x121d171b, 0x0e0b0d09, 0xf2adc78b, 0x2db9a8b6, 0x14c8a91e, + 0x578519f1, 0xaf4c0775, 0xeebbdd99, 0xa3fd607f, 0xf79f2601, 0x5cbcf572, 0x44c53b66, 0x5b347efb, + 0x8b762943, 0xcbdcc623, 0xb668fced, 0xb863f1e4, 0xd7cadc31, 0x42108563, 0x13402297, 0x842011c6, + 0x857d244a, 0xd2f83dbb, 0xae1132f9, 0xc76da129, 0x1d4b2f9e, 0xdcf330b2, 0x0dec5286, 0x77d0e3c1, + 0x2b6c16b3, 0xa999b970, 0x11fa4894, 0x472264e9, 0xa8c48cfc, 0xa01a3ff0, 0x56d82c7d, 0x22ef9033, + 0x87c74e49, 0xd9c1d138, 0x8cfea2ca, 0x98360bd4, 0xa6cf81f5, 0xa528de7a, 0xda268eb7, 0x3fa4bfad, + 0x2ce49d3a, 0x500d9278, 0x6a9bcc5f, 0x5462467e, 0xf6c2138d, 0x90e8b8d8, 0x2e5ef739, 0x82f5afc3, + 0x9fbe805d, 0x697c93d0, 0x6fa92dd5, 0xcfb31225, 0xc83b99ac, 0x10a77d18, 0xe86e639c, 0xdb7bbb3b, + 0xcd097826, 0x6ef41859, 0xec01b79a, 0x83a89a4f, 0xe6656e95, 0xaa7ee6ff, 0x2108cfbc, 0xefe6e815, + 0xbad99be7, 0x4ace366f, 0xead4099f, 0x29d67cb0, 0x31afb2a4, 0x2a31233f, 0xc63094a5, 0x35c066a2, + 0x7437bc4e, 0xfca6ca82, 0xe0b0d090, 0x3315d8a7, 0xf14a9804, 0x41f7daec, 0x7f0e50cd, 0x172ff691, + 0x768dd64d, 0x434db0ef, 0xcc544daa, 0xe4df0496, 0x9ee3b5d1, 0x4c1b886a, 0xc1b81f2c, 0x467f5165, + 0x9d04ea5e, 0x015d358c, 0xfa737487, 0xfb2e410b, 0xb35a1d67, 0x9252d2db, 0xe9335610, 0x6d1347d6, + 0x9a8c61d7, 0x377a0ca1, 0x598e14f8, 0xeb893c13, 0xceee27a9, 0xb735c961, 0xe1ede51c, 0x7a3cb147, + 0x9c59dfd2, 0x553f73f2, 0x1879ce14, 0x73bf37c7, 0x53eacdf7, 0x5f5baafd, 0xdf146f3d, 0x7886db44, + 0xca81f3af, 0xb93ec468, 0x382c3424, 0xc25f40a3, 0x1672c31d, 0xbc0c25e2, 0x288b493c, 0xff41950d, + 0x397101a8, 0x08deb30c, 0xd89ce4b4, 0x6490c156, 0x7b6184cb, 0xd570b632, 0x48745c6c, 0xd04257b8, +}; + +rx_vec_i128 soft_aesenc(rx_vec_i128 in, rx_vec_i128 key) { + uint32_t s0, s1, s2, s3; + + s0 = rx_vec_i128_w(in); + s1 = rx_vec_i128_z(in); + s2 = rx_vec_i128_y(in); + s3 = rx_vec_i128_x(in); + + rx_vec_i128 out = rx_set_int_vec_i128( + (lutEnc0[s0 & 0xff] ^ lutEnc1[(s3 >> 8) & 0xff] ^ lutEnc2[(s2 >> 16) & 0xff] ^ lutEnc3[s1 >> 24]), + (lutEnc0[s1 & 0xff] ^ lutEnc1[(s0 >> 8) & 0xff] ^ lutEnc2[(s3 >> 16) & 0xff] ^ lutEnc3[s2 >> 24]), + (lutEnc0[s2 & 0xff] ^ lutEnc1[(s1 >> 8) & 0xff] ^ lutEnc2[(s0 >> 16) & 0xff] ^ lutEnc3[s3 >> 24]), + (lutEnc0[s3 & 0xff] ^ lutEnc1[(s2 >> 8) & 0xff] ^ lutEnc2[(s1 >> 16) & 0xff] ^ lutEnc3[s0 >> 24]) + ); + + return rx_xor_vec_i128(out, key); +} + +rx_vec_i128 soft_aesdec(rx_vec_i128 in, rx_vec_i128 key) { + uint32_t s0, s1, s2, s3; + + s0 = rx_vec_i128_w(in); + s1 = rx_vec_i128_z(in); + s2 = rx_vec_i128_y(in); + s3 = rx_vec_i128_x(in); + + rx_vec_i128 out = rx_set_int_vec_i128( + (lutDec0[s0 & 0xff] ^ lutDec1[(s1 >> 8) & 0xff] ^ lutDec2[(s2 >> 16) & 0xff] ^ lutDec3[s3 >> 24]), + (lutDec0[s1 & 0xff] ^ lutDec1[(s2 >> 8) & 0xff] ^ lutDec2[(s3 >> 16) & 0xff] ^ lutDec3[s0 >> 24]), + (lutDec0[s2 & 0xff] ^ lutDec1[(s3 >> 8) & 0xff] ^ lutDec2[(s0 >> 16) & 0xff] ^ lutDec3[s1 >> 24]), + (lutDec0[s3 & 0xff] ^ lutDec1[(s0 >> 8) & 0xff] ^ lutDec2[(s1 >> 16) & 0xff] ^ lutDec3[s2 >> 24]) + ); + + return rx_xor_vec_i128(out, key); +} diff --git a/src/crypto/randomx/soft_aes.h b/src/crypto/randomx/soft_aes.h new file mode 100644 index 00000000..650a8194 --- /dev/null +++ b/src/crypto/randomx/soft_aes.h @@ -0,0 +1,46 @@ +/* +Copyright (c) 2018-2019, tevador + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#pragma once + +#include +#include "crypto/randomx/intrin_portable.h" + +rx_vec_i128 soft_aesenc(rx_vec_i128 in, rx_vec_i128 key); + +rx_vec_i128 soft_aesdec(rx_vec_i128 in, rx_vec_i128 key); + +template +inline rx_vec_i128 aesenc(rx_vec_i128 in, rx_vec_i128 key) { + return soft ? soft_aesenc(in, key) : rx_aesenc_vec_i128(in, key); +} + +template +inline rx_vec_i128 aesdec(rx_vec_i128 in, rx_vec_i128 key) { + return soft ? soft_aesdec(in, key) : rx_aesdec_vec_i128(in, key); +} diff --git a/src/crypto/randomx/superscalar.cpp b/src/crypto/randomx/superscalar.cpp new file mode 100644 index 00000000..aaa91f62 --- /dev/null +++ b/src/crypto/randomx/superscalar.cpp @@ -0,0 +1,891 @@ +/* +Copyright (c) 2018-2019, tevador + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "crypto/randomx/configuration.h" +#include "crypto/randomx/program.hpp" +#include "crypto/randomx/blake2/endian.h" +#include "crypto/randomx/superscalar.hpp" +#include "crypto/randomx/intrin_portable.h" +#include "crypto/randomx/reciprocal.h" + +namespace randomx { + + static bool isMultiplication(SuperscalarInstructionType type) { + return type == SuperscalarInstructionType::IMUL_R || type == SuperscalarInstructionType::IMULH_R || type == SuperscalarInstructionType::ISMULH_R || type == SuperscalarInstructionType::IMUL_RCP; + } + + //uOPs (micro-ops) are represented only by the execution port they can go to + namespace ExecutionPort { + using type = int; + constexpr type Null = 0; + constexpr type P0 = 1; + constexpr type P1 = 2; + constexpr type P5 = 4; + constexpr type P01 = P0 | P1; + constexpr type P05 = P0 | P5; + constexpr type P015 = P0 | P1 | P5; + } + + //Macro-operation as output of the x86 decoder + //Usually one macro-op = one x86 instruction, but 2 instructions are sometimes fused into 1 macro-op + //Macro-op can consist of 1 or 2 uOPs. + class MacroOp { + public: + MacroOp(const char* name, int size) + : name_(name), size_(size), latency_(0), uop1_(ExecutionPort::Null), uop2_(ExecutionPort::Null) {} + MacroOp(const char* name, int size, int latency, ExecutionPort::type uop) + : name_(name), size_(size), latency_(latency), uop1_(uop), uop2_(ExecutionPort::Null) {} + MacroOp(const char* name, int size, int latency, ExecutionPort::type uop1, ExecutionPort::type uop2) + : name_(name), size_(size), latency_(latency), uop1_(uop1), uop2_(uop2) {} + MacroOp(const MacroOp& parent, bool dependent) + : name_(parent.name_), size_(parent.size_), latency_(parent.latency_), uop1_(parent.uop1_), uop2_(parent.uop2_), dependent_(dependent) {} + const char* getName() const { + return name_; + } + int getSize() const { + return size_; + } + int getLatency() const { + return latency_; + } + ExecutionPort::type getUop1() const { + return uop1_; + } + ExecutionPort::type getUop2() const { + return uop2_; + } + bool isSimple() const { + return uop2_ == ExecutionPort::Null; + } + bool isEliminated() const { + return uop1_ == ExecutionPort::Null; + } + bool isDependent() const { + return dependent_; + } + static const MacroOp Add_rr; + static const MacroOp Add_ri; + static const MacroOp Lea_sib; + static const MacroOp Sub_rr; + static const MacroOp Imul_rr; + static const MacroOp Imul_r; + static const MacroOp Mul_r; + static const MacroOp Mov_rr; + static const MacroOp Mov_ri64; + static const MacroOp Xor_rr; + static const MacroOp Xor_ri; + static const MacroOp Ror_rcl; + static const MacroOp Ror_ri; + static const MacroOp TestJz_fused; + static const MacroOp Xor_self; + static const MacroOp Cmp_ri; + static const MacroOp Setcc_r; + private: + const char* name_; + int size_; + int latency_; + ExecutionPort::type uop1_; + ExecutionPort::type uop2_; + bool dependent_ = false; + }; + + //Size: 3 bytes + const MacroOp MacroOp::Add_rr = MacroOp("add r,r", 3, 1, ExecutionPort::P015); + const MacroOp MacroOp::Sub_rr = MacroOp("sub r,r", 3, 1, ExecutionPort::P015); + const MacroOp MacroOp::Xor_rr = MacroOp("xor r,r", 3, 1, ExecutionPort::P015); + const MacroOp MacroOp::Imul_r = MacroOp("imul r", 3, 4, ExecutionPort::P1, ExecutionPort::P5); + const MacroOp MacroOp::Mul_r = MacroOp("mul r", 3, 4, ExecutionPort::P1, ExecutionPort::P5); + const MacroOp MacroOp::Mov_rr = MacroOp("mov r,r", 3); + + //Size: 4 bytes + const MacroOp MacroOp::Lea_sib = MacroOp("lea r,r+r*s", 4, 1, ExecutionPort::P01); + const MacroOp MacroOp::Imul_rr = MacroOp("imul r,r", 4, 3, ExecutionPort::P1); + const MacroOp MacroOp::Ror_ri = MacroOp("ror r,i", 4, 1, ExecutionPort::P05); + + //Size: 7 bytes (can be optionally padded with nop to 8 or 9 bytes) + const MacroOp MacroOp::Add_ri = MacroOp("add r,i", 7, 1, ExecutionPort::P015); + const MacroOp MacroOp::Xor_ri = MacroOp("xor r,i", 7, 1, ExecutionPort::P015); + + //Size: 10 bytes + const MacroOp MacroOp::Mov_ri64 = MacroOp("mov rax,i64", 10, 1, ExecutionPort::P015); + + //Unused: + const MacroOp MacroOp::Ror_rcl = MacroOp("ror r,cl", 3, 1, ExecutionPort::P0, ExecutionPort::P5); + const MacroOp MacroOp::Xor_self = MacroOp("xor rcx,rcx", 3); + const MacroOp MacroOp::Cmp_ri = MacroOp("cmp r,i", 7, 1, ExecutionPort::P015); + const MacroOp MacroOp::Setcc_r = MacroOp("setcc cl", 3, 1, ExecutionPort::P05); + const MacroOp MacroOp::TestJz_fused = MacroOp("testjz r,i", 13, 0, ExecutionPort::P5); + + const MacroOp IMULH_R_ops_array[] = { MacroOp::Mov_rr, MacroOp::Mul_r, MacroOp::Mov_rr }; + const MacroOp ISMULH_R_ops_array[] = { MacroOp::Mov_rr, MacroOp::Imul_r, MacroOp::Mov_rr }; + const MacroOp IMUL_RCP_ops_array[] = { MacroOp::Mov_ri64, MacroOp(MacroOp::Imul_rr, true) }; + + class SuperscalarInstructionInfo { + public: + const char* getName() const { + return name_; + } + int getSize() const { + return ops_.size(); + } + bool isSimple() const { + return getSize() == 1; + } + int getLatency() const { + return latency_; + } + const MacroOp& getOp(int index) const { + return ops_[index]; + } + SuperscalarInstructionType getType() const { + return type_; + } + int getResultOp() const { + return resultOp_; + } + int getDstOp() const { + return dstOp_; + } + int getSrcOp() const { + return srcOp_; + } + static const SuperscalarInstructionInfo ISUB_R; + static const SuperscalarInstructionInfo IXOR_R; + static const SuperscalarInstructionInfo IADD_RS; + static const SuperscalarInstructionInfo IMUL_R; + static const SuperscalarInstructionInfo IROR_C; + static const SuperscalarInstructionInfo IADD_C7; + static const SuperscalarInstructionInfo IXOR_C7; + static const SuperscalarInstructionInfo IADD_C8; + static const SuperscalarInstructionInfo IXOR_C8; + static const SuperscalarInstructionInfo IADD_C9; + static const SuperscalarInstructionInfo IXOR_C9; + static const SuperscalarInstructionInfo IMULH_R; + static const SuperscalarInstructionInfo ISMULH_R; + static const SuperscalarInstructionInfo IMUL_RCP; + static const SuperscalarInstructionInfo NOP; + private: + const char* name_; + SuperscalarInstructionType type_; + std::vector ops_; + int latency_; + int resultOp_ = 0; + int dstOp_ = 0; + int srcOp_; + + SuperscalarInstructionInfo(const char* name) + : name_(name), type_(SuperscalarInstructionType::INVALID), latency_(0) {} + SuperscalarInstructionInfo(const char* name, SuperscalarInstructionType type, const MacroOp& op, int srcOp) + : name_(name), type_(type), latency_(op.getLatency()), srcOp_(srcOp) { + ops_.push_back(MacroOp(op)); + } + template + SuperscalarInstructionInfo(const char* name, SuperscalarInstructionType type, const MacroOp(&arr)[N], int resultOp, int dstOp, int srcOp) + : name_(name), type_(type), latency_(0), resultOp_(resultOp), dstOp_(dstOp), srcOp_(srcOp) { + for (unsigned i = 0; i < N; ++i) { + ops_.push_back(MacroOp(arr[i])); + latency_ += ops_.back().getLatency(); + } + static_assert(N > 1, "Invalid array size"); + } + }; + + const SuperscalarInstructionInfo SuperscalarInstructionInfo::ISUB_R = SuperscalarInstructionInfo("ISUB_R", SuperscalarInstructionType::ISUB_R, MacroOp::Sub_rr, 0); + const SuperscalarInstructionInfo SuperscalarInstructionInfo::IXOR_R = SuperscalarInstructionInfo("IXOR_R", SuperscalarInstructionType::IXOR_R, MacroOp::Xor_rr, 0); + const SuperscalarInstructionInfo SuperscalarInstructionInfo::IADD_RS = SuperscalarInstructionInfo("IADD_RS", SuperscalarInstructionType::IADD_RS, MacroOp::Lea_sib, 0); + const SuperscalarInstructionInfo SuperscalarInstructionInfo::IMUL_R = SuperscalarInstructionInfo("IMUL_R", SuperscalarInstructionType::IMUL_R, MacroOp::Imul_rr, 0); + const SuperscalarInstructionInfo SuperscalarInstructionInfo::IROR_C = SuperscalarInstructionInfo("IROR_C", SuperscalarInstructionType::IROR_C, MacroOp::Ror_ri, -1); + + const SuperscalarInstructionInfo SuperscalarInstructionInfo::IADD_C7 = SuperscalarInstructionInfo("IADD_C7", SuperscalarInstructionType::IADD_C7, MacroOp::Add_ri, -1); + const SuperscalarInstructionInfo SuperscalarInstructionInfo::IXOR_C7 = SuperscalarInstructionInfo("IXOR_C7", SuperscalarInstructionType::IXOR_C7, MacroOp::Xor_ri, -1); + const SuperscalarInstructionInfo SuperscalarInstructionInfo::IADD_C8 = SuperscalarInstructionInfo("IADD_C8", SuperscalarInstructionType::IADD_C8, MacroOp::Add_ri, -1); + const SuperscalarInstructionInfo SuperscalarInstructionInfo::IXOR_C8 = SuperscalarInstructionInfo("IXOR_C8", SuperscalarInstructionType::IXOR_C8, MacroOp::Xor_ri, -1); + const SuperscalarInstructionInfo SuperscalarInstructionInfo::IADD_C9 = SuperscalarInstructionInfo("IADD_C9", SuperscalarInstructionType::IADD_C9, MacroOp::Add_ri, -1); + const SuperscalarInstructionInfo SuperscalarInstructionInfo::IXOR_C9 = SuperscalarInstructionInfo("IXOR_C9", SuperscalarInstructionType::IXOR_C9, MacroOp::Xor_ri, -1); + + const SuperscalarInstructionInfo SuperscalarInstructionInfo::IMULH_R = SuperscalarInstructionInfo("IMULH_R", SuperscalarInstructionType::IMULH_R, IMULH_R_ops_array, 1, 0, 1); + const SuperscalarInstructionInfo SuperscalarInstructionInfo::ISMULH_R = SuperscalarInstructionInfo("ISMULH_R", SuperscalarInstructionType::ISMULH_R, ISMULH_R_ops_array, 1, 0, 1); + const SuperscalarInstructionInfo SuperscalarInstructionInfo::IMUL_RCP = SuperscalarInstructionInfo("IMUL_RCP", SuperscalarInstructionType::IMUL_RCP, IMUL_RCP_ops_array, 1, 1, -1); + + const SuperscalarInstructionInfo SuperscalarInstructionInfo::NOP = SuperscalarInstructionInfo("NOP"); + + //these are some of the options how to split a 16-byte window into 3 or 4 x86 instructions. + //RandomX uses instructions with a native size of 3 (sub, xor, mul, mov), 4 (lea, mul), 7 (xor, add immediate) or 10 bytes (mov 64-bit immediate). + //Slots with sizes of 8 or 9 bytes need to be padded with a nop instruction. + const int buffer0[] = { 4, 8, 4 }; + const int buffer1[] = { 7, 3, 3, 3 }; + const int buffer2[] = { 3, 7, 3, 3 }; + const int buffer3[] = { 4, 9, 3 }; + const int buffer4[] = { 4, 4, 4, 4 }; + const int buffer5[] = { 3, 3, 10 }; + + class DecoderBuffer { + public: + static const DecoderBuffer Default; + template + DecoderBuffer(const char* name, int index, const int(&arr)[N]) + : name_(name), index_(index), counts_(arr), opsCount_(N) {} + const int* getCounts() const { + return counts_; + } + int getSize() const { + return opsCount_; + } + int getIndex() const { + return index_; + } + const char* getName() const { + return name_; + } + const DecoderBuffer* fetchNext(SuperscalarInstructionType instrType, int cycle, int mulCount, Blake2Generator& gen) const { + //If the current RandomX instruction is "IMULH", the next fetch configuration must be 3-3-10 + //because the full 128-bit multiplication instruction is 3 bytes long and decodes to 2 uOPs on Intel CPUs. + //Intel CPUs can decode at most 4 uOPs per cycle, so this requires a 2-1-1 configuration for a total of 3 macro ops. + if (instrType == SuperscalarInstructionType::IMULH_R || instrType == SuperscalarInstructionType::ISMULH_R) + return &decodeBuffer3310; + + //To make sure that the multiplication port is saturated, a 4-4-4-4 configuration is generated if the number of multiplications + //is lower than the number of cycles. + if (mulCount < cycle + 1) + return &decodeBuffer4444; + + //If the current RandomX instruction is "IMUL_RCP", the next buffer must begin with a 4-byte slot for multiplication. + if(instrType == SuperscalarInstructionType::IMUL_RCP) + return (gen.getByte() & 1) ? &decodeBuffer484 : &decodeBuffer493; + + //Default: select a random fetch configuration. + return fetchNextDefault(gen); + } + private: + const char* name_; + int index_; + const int* counts_; + int opsCount_; + DecoderBuffer() : index_(-1) {} + static const DecoderBuffer decodeBuffer484; + static const DecoderBuffer decodeBuffer7333; + static const DecoderBuffer decodeBuffer3733; + static const DecoderBuffer decodeBuffer493; + static const DecoderBuffer decodeBuffer4444; + static const DecoderBuffer decodeBuffer3310; + static const DecoderBuffer* decodeBuffers[4]; + const DecoderBuffer* fetchNextDefault(Blake2Generator& gen) const { + return decodeBuffers[gen.getByte() & 3]; + } + }; + + const DecoderBuffer DecoderBuffer::decodeBuffer484 = DecoderBuffer("4,8,4", 0, buffer0); + const DecoderBuffer DecoderBuffer::decodeBuffer7333 = DecoderBuffer("7,3,3,3", 1, buffer1); + const DecoderBuffer DecoderBuffer::decodeBuffer3733 = DecoderBuffer("3,7,3,3", 2, buffer2); + const DecoderBuffer DecoderBuffer::decodeBuffer493 = DecoderBuffer("4,9,3", 3, buffer3); + const DecoderBuffer DecoderBuffer::decodeBuffer4444 = DecoderBuffer("4,4,4,4", 4, buffer4); + const DecoderBuffer DecoderBuffer::decodeBuffer3310 = DecoderBuffer("3,3,10", 5, buffer5); + + const DecoderBuffer* DecoderBuffer::decodeBuffers[4] = { + &DecoderBuffer::decodeBuffer484, + &DecoderBuffer::decodeBuffer7333, + &DecoderBuffer::decodeBuffer3733, + &DecoderBuffer::decodeBuffer493, + }; + + const DecoderBuffer DecoderBuffer::Default = DecoderBuffer(); + + const SuperscalarInstructionInfo* slot_3[] = { &SuperscalarInstructionInfo::ISUB_R, &SuperscalarInstructionInfo::IXOR_R }; + const SuperscalarInstructionInfo* slot_3L[] = { &SuperscalarInstructionInfo::ISUB_R, &SuperscalarInstructionInfo::IXOR_R, &SuperscalarInstructionInfo::IMULH_R, &SuperscalarInstructionInfo::ISMULH_R }; + const SuperscalarInstructionInfo* slot_4[] = { &SuperscalarInstructionInfo::IROR_C, &SuperscalarInstructionInfo::IADD_RS }; + const SuperscalarInstructionInfo* slot_7[] = { &SuperscalarInstructionInfo::IXOR_C7, &SuperscalarInstructionInfo::IADD_C7 }; + const SuperscalarInstructionInfo* slot_8[] = { &SuperscalarInstructionInfo::IXOR_C8, &SuperscalarInstructionInfo::IADD_C8 }; + const SuperscalarInstructionInfo* slot_9[] = { &SuperscalarInstructionInfo::IXOR_C9, &SuperscalarInstructionInfo::IADD_C9 }; + const SuperscalarInstructionInfo* slot_10 = &SuperscalarInstructionInfo::IMUL_RCP; + + static bool selectRegister(std::vector& availableRegisters, Blake2Generator& gen, int& reg) { + int index; + if (availableRegisters.size() == 0) + return false; + + if (availableRegisters.size() > 1) { + index = gen.getUInt32() % availableRegisters.size(); + } + else { + index = 0; + } + reg = availableRegisters[index]; + return true; + } + + class RegisterInfo { + public: + RegisterInfo() : latency(0), lastOpGroup(SuperscalarInstructionType::INVALID), lastOpPar(-1), value(0) {} + int latency; + SuperscalarInstructionType lastOpGroup; + int lastOpPar; + int value; + }; + + //"SuperscalarInstruction" consists of one or more macro-ops + class SuperscalarInstruction { + public: + void toInstr(Instruction& instr) { //translate to a RandomX instruction format + instr.opcode = (int)getType(); + instr.dst = dst_; + instr.src = src_ >= 0 ? src_ : dst_; + instr.setMod(mod_); + instr.setImm32(imm32_); + } + + void createForSlot(Blake2Generator& gen, int slotSize, int fetchType, bool isLast, bool isFirst) { + switch (slotSize) + { + case 3: + //if this is the last slot, we can also select "IMULH" instructions + if (isLast) { + create(slot_3L[gen.getByte() & 3], gen); + } + else { + create(slot_3[gen.getByte() & 1], gen); + } + break; + case 4: + //if this is the 4-4-4-4 buffer, issue multiplications as the first 3 instructions + if (fetchType == 4 && !isLast) { + create(&SuperscalarInstructionInfo::IMUL_R, gen); + } + else { + create(slot_4[gen.getByte() & 1], gen); + } + break; + case 7: + create(slot_7[gen.getByte() & 1], gen); + break; + case 8: + create(slot_8[gen.getByte() & 1], gen); + break; + case 9: + create(slot_9[gen.getByte() & 1], gen); + break; + case 10: + create(slot_10, gen); + break; + default: + UNREACHABLE; + } + } + + void create(const SuperscalarInstructionInfo* info, Blake2Generator& gen) { + info_ = info; + reset(); + switch (info->getType()) + { + case SuperscalarInstructionType::ISUB_R: { + mod_ = 0; + imm32_ = 0; + opGroup_ = SuperscalarInstructionType::IADD_RS; + groupParIsSource_ = true; + } break; + + case SuperscalarInstructionType::IXOR_R: { + mod_ = 0; + imm32_ = 0; + opGroup_ = SuperscalarInstructionType::IXOR_R; + groupParIsSource_ = true; + } break; + + case SuperscalarInstructionType::IADD_RS: { + mod_ = gen.getByte(); + imm32_ = 0; + opGroup_ = SuperscalarInstructionType::IADD_RS; + groupParIsSource_ = true; + } break; + + case SuperscalarInstructionType::IMUL_R: { + mod_ = 0; + imm32_ = 0; + opGroup_ = SuperscalarInstructionType::IMUL_R; + groupParIsSource_ = true; + } break; + + case SuperscalarInstructionType::IROR_C: { + mod_ = 0; + do { + imm32_ = gen.getByte() & 63; + } while (imm32_ == 0); + opGroup_ = SuperscalarInstructionType::IROR_C; + opGroupPar_ = -1; + } break; + + case SuperscalarInstructionType::IADD_C7: + case SuperscalarInstructionType::IADD_C8: + case SuperscalarInstructionType::IADD_C9: { + mod_ = 0; + imm32_ = gen.getUInt32(); + opGroup_ = SuperscalarInstructionType::IADD_C7; + opGroupPar_ = -1; + } break; + + case SuperscalarInstructionType::IXOR_C7: + case SuperscalarInstructionType::IXOR_C8: + case SuperscalarInstructionType::IXOR_C9: { + mod_ = 0; + imm32_ = gen.getUInt32(); + opGroup_ = SuperscalarInstructionType::IXOR_C7; + opGroupPar_ = -1; + } break; + + case SuperscalarInstructionType::IMULH_R: { + canReuse_ = true; + mod_ = 0; + imm32_ = 0; + opGroup_ = SuperscalarInstructionType::IMULH_R; + opGroupPar_ = gen.getUInt32(); + } break; + + case SuperscalarInstructionType::ISMULH_R: { + canReuse_ = true; + mod_ = 0; + imm32_ = 0; + opGroup_ = SuperscalarInstructionType::ISMULH_R; + opGroupPar_ = gen.getUInt32(); + } break; + + case SuperscalarInstructionType::IMUL_RCP: { + mod_ = 0; + do { + imm32_ = gen.getUInt32(); + } while (isZeroOrPowerOf2(imm32_)); + opGroup_ = SuperscalarInstructionType::IMUL_RCP; + opGroupPar_ = -1; + } break; + + default: + break; + } + } + + bool selectDestination(int cycle, bool allowChainedMul, RegisterInfo (®isters)[8], Blake2Generator& gen) { + /*if (allowChainedMultiplication && opGroup_ == SuperscalarInstructionType::IMUL_R) + std::cout << "Selecting destination with chained MUL enabled" << std::endl;*/ + std::vector availableRegisters; + //Conditions for the destination register: + // * value must be ready at the required cycle + // * cannot be the same as the source register unless the instruction allows it + // - this avoids optimizable instructions such as "xor r, r" or "sub r, r" + // * register cannot be multiplied twice in a row unless allowChainedMul is true + // - this avoids accumulation of trailing zeroes in registers due to excessive multiplication + // - allowChainedMul is set to true if an attempt to find source/destination registers failed (this is quite rare, but prevents a catastrophic failure of the generator) + // * either the last instruction applied to the register or its source must be different than this instruction + // - this avoids optimizable instruction sequences such as "xor r1, r2; xor r1, r2" or "ror r, C1; ror r, C2" or "add r, C1; add r, C2" + // * register r5 cannot be the destination of the IADD_RS instruction (limitation of the x86 lea instruction) + for (int i = 0; i < 8; ++i) { + if (registers[i].latency <= cycle && (canReuse_ || i != src_) && (allowChainedMul || opGroup_ != SuperscalarInstructionType::IMUL_R || registers[i].lastOpGroup != SuperscalarInstructionType::IMUL_R) && (registers[i].lastOpGroup != opGroup_ || registers[i].lastOpPar != opGroupPar_) && (info_->getType() != SuperscalarInstructionType::IADD_RS || i != RegisterNeedsDisplacement)) + availableRegisters.push_back(i); + } + return selectRegister(availableRegisters, gen, dst_); + } + + bool selectSource(int cycle, RegisterInfo(®isters)[8], Blake2Generator& gen) { + std::vector availableRegisters; + //all registers that are ready at the cycle + for (unsigned i = 0; i < 8; ++i) { + if (registers[i].latency <= cycle) + availableRegisters.push_back(i); + } + //if there are only 2 available registers for IADD_RS and one of them is r5, select it as the source because it cannot be the destination + if (availableRegisters.size() == 2 && info_->getType() == SuperscalarInstructionType::IADD_RS) { + if (availableRegisters[0] == RegisterNeedsDisplacement || availableRegisters[1] == RegisterNeedsDisplacement) { + opGroupPar_ = src_ = RegisterNeedsDisplacement; + return true; + } + } + if (selectRegister(availableRegisters, gen, src_)) { + if (groupParIsSource_) + opGroupPar_ = src_; + return true; + } + return false; + } + + SuperscalarInstructionType getType() { + return info_->getType(); + } + int getSource() { + return src_; + } + int getDestination() { + return dst_; + } + SuperscalarInstructionType getGroup() { + return opGroup_; + } + int getGroupPar() { + return opGroupPar_; + } + + const SuperscalarInstructionInfo& getInfo() const { + return *info_; + } + + static const SuperscalarInstruction Null; + + private: + const SuperscalarInstructionInfo* info_; + int src_ = -1; + int dst_ = -1; + int mod_; + uint32_t imm32_; + SuperscalarInstructionType opGroup_; + int opGroupPar_; + bool canReuse_ = false; + bool groupParIsSource_ = false; + + void reset() { + src_ = dst_ = -1; + canReuse_ = groupParIsSource_ = false; + } + + SuperscalarInstruction(const SuperscalarInstructionInfo* info) : info_(info) { + } + }; + + const SuperscalarInstruction SuperscalarInstruction::Null = SuperscalarInstruction(&SuperscalarInstructionInfo::NOP); + + constexpr int CYCLE_MAP_SIZE = RANDOMX_SUPERSCALAR_MAX_LATENCY + 4; + constexpr int LOOK_FORWARD_CYCLES = 4; + constexpr int MAX_THROWAWAY_COUNT = 256; + + template + static int scheduleUop(ExecutionPort::type uop, ExecutionPort::type(&portBusy)[CYCLE_MAP_SIZE][3], int cycle) { + //The scheduling here is done optimistically by checking port availability in order P5 -> P0 -> P1 to not overload + //port P1 (multiplication) by instructions that can go to any port. + for (; cycle < static_cast(RandomX_CurrentConfig.SuperscalarLatency) + 4; ++cycle) { + if ((uop & ExecutionPort::P5) != 0 && !portBusy[cycle][2]) { + if (commit) { + if (trace) std::cout << "; P5 at cycle " << cycle << std::endl; + portBusy[cycle][2] = uop; + } + return cycle; + } + if ((uop & ExecutionPort::P0) != 0 && !portBusy[cycle][0]) { + if (commit) { + if (trace) std::cout << "; P0 at cycle " << cycle << std::endl; + portBusy[cycle][0] = uop; + } + return cycle; + } + if ((uop & ExecutionPort::P1) != 0 && !portBusy[cycle][1]) { + if (commit) { + if (trace) std::cout << "; P1 at cycle " << cycle << std::endl; + portBusy[cycle][1] = uop; + } + return cycle; + } + } + return -1; + } + + template + static int scheduleMop(const MacroOp& mop, ExecutionPort::type(&portBusy)[CYCLE_MAP_SIZE][3], int cycle, int depCycle) { + //if this macro-op depends on the previous one, increase the starting cycle if needed + //this handles an explicit dependency chain in IMUL_RCP + if (mop.isDependent()) { + cycle = (cycle > depCycle) ? cycle : depCycle; + } + //move instructions are eliminated and don't need an execution unit + if (mop.isEliminated()) { + if (commit) + if (trace) std::cout << "; (eliminated)" << std::endl; + return cycle; + } + else if (mop.isSimple()) { + //this macro-op has only one uOP + return scheduleUop(mop.getUop1(), portBusy, cycle); + } + else { + //macro-ops with 2 uOPs are scheduled conservatively by requiring both uOPs to execute in the same cycle + for (; cycle < static_cast(RandomX_CurrentConfig.SuperscalarLatency) + 4; ++cycle) { + + int cycle1 = scheduleUop(mop.getUop1(), portBusy, cycle); + int cycle2 = scheduleUop(mop.getUop2(), portBusy, cycle); + + if (cycle1 == cycle2) { + if (commit) { + scheduleUop(mop.getUop1(), portBusy, cycle1); + scheduleUop(mop.getUop2(), portBusy, cycle2); + } + return cycle1; + } + } + } + + return -1; + } + + void generateSuperscalar(SuperscalarProgram& prog, Blake2Generator& gen) { + + ExecutionPort::type portBusy[CYCLE_MAP_SIZE][3]; + memset(portBusy, 0, sizeof(portBusy)); + RegisterInfo registers[8]; + + const DecoderBuffer* decodeBuffer = &DecoderBuffer::Default; + SuperscalarInstruction currentInstruction = SuperscalarInstruction::Null; + int macroOpIndex = 0; + int codeSize = 0; + int macroOpCount = 0; + int cycle = 0; + int depCycle = 0; + int retireCycle = 0; + bool portsSaturated = false; + int programSize = 0; + int mulCount = 0; + int decodeCycle; + int throwAwayCount = 0; + + //decode instructions for RANDOMX_SUPERSCALAR_LATENCY cycles or until an execution port is saturated. + //Each decode cycle decodes 16 bytes of x86 code. + //Since a decode cycle produces on average 3.45 macro-ops and there are only 3 ALU ports, execution ports are always + //saturated first. The cycle limit is present only to guarantee loop termination. + //Program size is limited to SuperscalarMaxSize instructions. + for (decodeCycle = 0; decodeCycle < static_cast(RandomX_CurrentConfig.SuperscalarLatency) && !portsSaturated && programSize < 3 * static_cast(RandomX_CurrentConfig.SuperscalarLatency) + 2; ++decodeCycle) { + + //select a decode configuration + decodeBuffer = decodeBuffer->fetchNext(currentInstruction.getType(), decodeCycle, mulCount, gen); + if (trace) std::cout << "; ------------- fetch cycle " << cycle << " (" << decodeBuffer->getName() << ")" << std::endl; + + int bufferIndex = 0; + + //fill all instruction slots in the current decode buffer + while (bufferIndex < decodeBuffer->getSize()) { + int topCycle = cycle; + + //if we have issued all macro-ops for the current RandomX instruction, create a new instruction + if (macroOpIndex >= currentInstruction.getInfo().getSize()) { + if (portsSaturated || programSize >= 3 * static_cast(RandomX_CurrentConfig.SuperscalarLatency) + 2) + break; + //select an instruction so that the first macro-op fits into the current slot + currentInstruction.createForSlot(gen, decodeBuffer->getCounts()[bufferIndex], decodeBuffer->getIndex(), decodeBuffer->getSize() == bufferIndex + 1, bufferIndex == 0); + macroOpIndex = 0; + if (trace) std::cout << "; " << currentInstruction.getInfo().getName() << std::endl; + } + const MacroOp& mop = currentInstruction.getInfo().getOp(macroOpIndex); + if (trace) std::cout << mop.getName() << " "; + + //calculate the earliest cycle when this macro-op (all of its uOPs) can be scheduled for execution + int scheduleCycle = scheduleMop(mop, portBusy, cycle, depCycle); + if (scheduleCycle < 0) { + if (trace) std::cout << "Unable to map operation '" << mop.getName() << "' to execution port (cycle " << cycle << ")" << std::endl; + //__debugbreak(); + portsSaturated = true; + break; + } + + //find a source register (if applicable) that will be ready when this instruction executes + if (macroOpIndex == currentInstruction.getInfo().getSrcOp()) { + int forward; + //if no suitable operand is ready, look up to LOOK_FORWARD_CYCLES forward + for (forward = 0; forward < LOOK_FORWARD_CYCLES && !currentInstruction.selectSource(scheduleCycle, registers, gen); ++forward) { + if (trace) std::cout << "; src STALL at cycle " << cycle << std::endl; + ++scheduleCycle; + ++cycle; + } + //if no register was found, throw the instruction away and try another one + if (forward == LOOK_FORWARD_CYCLES) { + if (throwAwayCount < MAX_THROWAWAY_COUNT) { + throwAwayCount++; + macroOpIndex = currentInstruction.getInfo().getSize(); + if (trace) std::cout << "; THROW away " << currentInstruction.getInfo().getName() << std::endl; + //cycle = topCycle; + continue; + } + //abort this decode buffer + if (trace) std::cout << "Aborting at cycle " << cycle << " with decode buffer " << decodeBuffer->getName() << " - source registers not available for operation " << currentInstruction.getInfo().getName() << std::endl; + currentInstruction = SuperscalarInstruction::Null; + break; + } + if (trace) std::cout << "; src = r" << currentInstruction.getSource() << std::endl; + } + //find a destination register that will be ready when this instruction executes + if (macroOpIndex == currentInstruction.getInfo().getDstOp()) { + int forward; + for (forward = 0; forward < LOOK_FORWARD_CYCLES && !currentInstruction.selectDestination(scheduleCycle, throwAwayCount > 0, registers, gen); ++forward) { + if (trace) std::cout << "; dst STALL at cycle " << cycle << std::endl; + ++scheduleCycle; + ++cycle; + } + if (forward == LOOK_FORWARD_CYCLES) { //throw instruction away + if (throwAwayCount < MAX_THROWAWAY_COUNT) { + throwAwayCount++; + macroOpIndex = currentInstruction.getInfo().getSize(); + if (trace) std::cout << "; THROW away " << currentInstruction.getInfo().getName() << std::endl; + //cycle = topCycle; + continue; + } + //abort this decode buffer + if (trace) std::cout << "Aborting at cycle " << cycle << " with decode buffer " << decodeBuffer->getName() << " - destination registers not available" << std::endl; + currentInstruction = SuperscalarInstruction::Null; + break; + } + if (trace) std::cout << "; dst = r" << currentInstruction.getDestination() << std::endl; + } + throwAwayCount = 0; + + //recalculate when the instruction can be scheduled for execution based on operand availability + scheduleCycle = scheduleMop(mop, portBusy, scheduleCycle, scheduleCycle); + + //calculate when the result will be ready + depCycle = scheduleCycle + mop.getLatency(); + + //if this instruction writes the result, modify register information + // RegisterInfo.latency - which cycle the register will be ready + // RegisterInfo.lastOpGroup - the last operation that was applied to the register + // RegisterInfo.lastOpPar - the last operation source value (-1 = constant, 0-7 = register) + if (macroOpIndex == currentInstruction.getInfo().getResultOp()) { + int dst = currentInstruction.getDestination(); + RegisterInfo& ri = registers[dst]; + retireCycle = depCycle; + ri.latency = retireCycle; + ri.lastOpGroup = currentInstruction.getGroup(); + ri.lastOpPar = currentInstruction.getGroupPar(); + if (trace) std::cout << "; RETIRED at cycle " << retireCycle << std::endl; + } + codeSize += mop.getSize(); + bufferIndex++; + macroOpIndex++; + macroOpCount++; + + //terminating condition + if (scheduleCycle >= static_cast(RandomX_CurrentConfig.SuperscalarLatency)) { + portsSaturated = true; + } + cycle = topCycle; + + //when all macro-ops of the current instruction have been issued, add the instruction into the program + if (macroOpIndex >= currentInstruction.getInfo().getSize()) { + currentInstruction.toInstr(prog(programSize++)); + mulCount += isMultiplication(currentInstruction.getType()); + } + } + ++cycle; + } + + double ipc = (macroOpCount / (double)retireCycle); + + memset(prog.asicLatencies, 0, sizeof(prog.asicLatencies)); + + //Calculate ASIC latency: + //Assumes 1 cycle latency for all operations and unlimited parallelization. + for (int i = 0; i < programSize; ++i) { + Instruction& instr = prog(i); + int latDst = prog.asicLatencies[instr.dst] + 1; + int latSrc = instr.dst != instr.src ? prog.asicLatencies[instr.src] + 1 : 0; + prog.asicLatencies[instr.dst] = (latDst > latSrc) ? latDst : latSrc; + } + + //address register is the register with the highest ASIC latency + int asicLatencyMax = 0; + int addressReg = 0; + for (int i = 0; i < 8; ++i) { + if (prog.asicLatencies[i] > asicLatencyMax) { + asicLatencyMax = prog.asicLatencies[i]; + addressReg = i; + } + prog.cpuLatencies[i] = registers[i].latency; + } + + prog.setSize(programSize); + prog.setAddressRegister(addressReg); + + prog.cpuLatency = retireCycle; + prog.asicLatency = asicLatencyMax; + prog.codeSize = codeSize; + prog.macroOps = macroOpCount; + prog.decodeCycles = decodeCycle; + prog.ipc = ipc; + prog.mulCount = mulCount; + + + /*if(INFO) std::cout << "; ALU port utilization:" << std::endl; + if (INFO) std::cout << "; (* = in use, _ = idle)" << std::endl; + + int portCycles = 0; + for (int i = 0; i < RandomX_Config.SuperscalarLatency + 4; ++i) { + std::cout << "; " << std::setw(3) << i << " "; + for (int j = 0; j < 3; ++j) { + std::cout << (portBusy[i][j] ? '*' : '_'); + portCycles += !!portBusy[i][j]; + } + std::cout << std::endl; + }*/ + } + + void executeSuperscalar(int_reg_t(&r)[8], SuperscalarProgram& prog, std::vector *reciprocals) { + for (unsigned j = 0; j < prog.getSize(); ++j) { + Instruction& instr = prog(j); + switch ((SuperscalarInstructionType)instr.opcode) + { + case SuperscalarInstructionType::ISUB_R: + r[instr.dst] -= r[instr.src]; + break; + case SuperscalarInstructionType::IXOR_R: + r[instr.dst] ^= r[instr.src]; + break; + case SuperscalarInstructionType::IADD_RS: + r[instr.dst] += r[instr.src] << instr.getModShift(); + break; + case SuperscalarInstructionType::IMUL_R: + r[instr.dst] *= r[instr.src]; + break; + case SuperscalarInstructionType::IROR_C: + r[instr.dst] = rotr64(r[instr.dst], instr.getImm32()); + break; + case SuperscalarInstructionType::IADD_C7: + case SuperscalarInstructionType::IADD_C8: + case SuperscalarInstructionType::IADD_C9: + r[instr.dst] += signExtend2sCompl(instr.getImm32()); + break; + case SuperscalarInstructionType::IXOR_C7: + case SuperscalarInstructionType::IXOR_C8: + case SuperscalarInstructionType::IXOR_C9: + r[instr.dst] ^= signExtend2sCompl(instr.getImm32()); + break; + case SuperscalarInstructionType::IMULH_R: + r[instr.dst] = mulh(r[instr.dst], r[instr.src]); + break; + case SuperscalarInstructionType::ISMULH_R: + r[instr.dst] = smulh(r[instr.dst], r[instr.src]); + break; + case SuperscalarInstructionType::IMUL_RCP: + if (reciprocals != nullptr) + r[instr.dst] *= (*reciprocals)[instr.getImm32()]; + else + r[instr.dst] *= randomx_reciprocal(instr.getImm32()); + break; + default: + UNREACHABLE; + } + } + } +} diff --git a/src/crypto/randomx/superscalar.hpp b/src/crypto/randomx/superscalar.hpp new file mode 100644 index 00000000..fd2a593b --- /dev/null +++ b/src/crypto/randomx/superscalar.hpp @@ -0,0 +1,60 @@ +/* +Copyright (c) 2018-2019, tevador + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#pragma once + +#include +#include +#include "crypto/randomx/superscalar_program.hpp" +#include "crypto/randomx/blake2_generator.hpp" + +namespace randomx { + // Intel Ivy Bridge reference + enum class SuperscalarInstructionType { //uOPs (decode) execution ports latency code size + ISUB_R = 0, //1 p015 1 3 (sub) + IXOR_R = 1, //1 p015 1 3 (xor) + IADD_RS = 2, //1 p01 1 4 (lea) + IMUL_R = 3, //1 p1 3 4 (imul) + IROR_C = 4, //1 p05 1 4 (ror) + IADD_C7 = 5, //1 p015 1 7 (add) + IXOR_C7 = 6, //1 p015 1 7 (xor) + IADD_C8 = 7, //1+0 p015 1 7+1 (add+nop) + IXOR_C8 = 8, //1+0 p015 1 7+1 (xor+nop) + IADD_C9 = 9, //1+0 p015 1 7+2 (add+nop) + IXOR_C9 = 10, //1+0 p015 1 7+2 (xor+nop) + IMULH_R = 11, //1+2+1 0+(p1,p5)+0 3 3+3+3 (mov+mul+mov) + ISMULH_R = 12, //1+2+1 0+(p1,p5)+0 3 3+3+3 (mov+imul+mov) + IMUL_RCP = 13, //1+1 p015+p1 4 10+4 (mov+imul) + + COUNT = 14, + INVALID = -1 + }; + + void generateSuperscalar(SuperscalarProgram& prog, Blake2Generator& gen); + void executeSuperscalar(uint64_t(&r)[8], SuperscalarProgram& prog, std::vector *reciprocals = nullptr); +} diff --git a/src/crypto/randomx/superscalar_program.hpp b/src/crypto/randomx/superscalar_program.hpp new file mode 100644 index 00000000..dc173591 --- /dev/null +++ b/src/crypto/randomx/superscalar_program.hpp @@ -0,0 +1,73 @@ +/* +Copyright (c) 2018-2019, tevador + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#pragma once + +#include +#include "crypto/randomx/instruction.hpp" +#include "crypto/randomx/common.hpp" + +namespace randomx { + + class SuperscalarProgram { + public: + Instruction& operator()(int pc) { + return programBuffer[pc]; + } + uint32_t getSize() { + return size; + } + void setSize(uint32_t val) { + size = val; + } + int getAddressRegister() { + return addrReg; + } + void setAddressRegister(int val) { + addrReg = val; + } + + Instruction programBuffer[SuperscalarMaxSize]; + uint32_t size +#ifndef NDEBUG + = 0 +#endif + ; + int addrReg; + double ipc; + int codeSize; + int macroOps; + int decodeCycles; + int cpuLatency; + int asicLatency; + int mulCount; + int cpuLatencies[8]; + int asicLatencies[8]; + }; + +} diff --git a/src/crypto/randomx/virtual_machine.cpp b/src/crypto/randomx/virtual_machine.cpp new file mode 100644 index 00000000..2913c7e5 --- /dev/null +++ b/src/crypto/randomx/virtual_machine.cpp @@ -0,0 +1,129 @@ +/* +Copyright (c) 2018-2019, tevador + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include "crypto/randomx/virtual_machine.hpp" +#include "crypto/randomx/common.hpp" +#include "crypto/randomx/aes_hash.hpp" +#include "crypto/randomx/blake2/blake2.h" +#include "crypto/randomx/intrin_portable.h" +#include "crypto/randomx/allocator.hpp" + +randomx_vm::~randomx_vm() { + +} + +void randomx_vm::resetRoundingMode() { + rx_reset_float_state(); +} + +namespace randomx { + + static inline uint64_t getSmallPositiveFloatBits(uint64_t entropy) { + auto exponent = entropy >> 59; //0..31 + auto mantissa = entropy & mantissaMask; + exponent += exponentBias; + exponent &= exponentMask; + exponent <<= mantissaSize; + return exponent | mantissa; + } + + static inline uint64_t getStaticExponent(uint64_t entropy) { + auto exponent = constExponentBits; + exponent |= (entropy >> (64 - staticExponentBits)) << dynamicExponentBits; + exponent <<= mantissaSize; + return exponent; + } + + static inline uint64_t getFloatMask(uint64_t entropy) { + constexpr uint64_t mask22bit = (1ULL << 22) - 1; + return (entropy & mask22bit) | getStaticExponent(entropy); + } + +} + +void randomx_vm::initialize() { + store64(®.a[0].lo, randomx::getSmallPositiveFloatBits(program.getEntropy(0))); + store64(®.a[0].hi, randomx::getSmallPositiveFloatBits(program.getEntropy(1))); + store64(®.a[1].lo, randomx::getSmallPositiveFloatBits(program.getEntropy(2))); + store64(®.a[1].hi, randomx::getSmallPositiveFloatBits(program.getEntropy(3))); + store64(®.a[2].lo, randomx::getSmallPositiveFloatBits(program.getEntropy(4))); + store64(®.a[2].hi, randomx::getSmallPositiveFloatBits(program.getEntropy(5))); + store64(®.a[3].lo, randomx::getSmallPositiveFloatBits(program.getEntropy(6))); + store64(®.a[3].hi, randomx::getSmallPositiveFloatBits(program.getEntropy(7))); + mem.ma = program.getEntropy(8) & CacheLineAlignMask; + mem.mx = program.getEntropy(10); + auto addressRegisters = program.getEntropy(12); + config.readReg0 = 0 + (addressRegisters & 1); + addressRegisters >>= 1; + config.readReg1 = 2 + (addressRegisters & 1); + addressRegisters >>= 1; + config.readReg2 = 4 + (addressRegisters & 1); + addressRegisters >>= 1; + config.readReg3 = 6 + (addressRegisters & 1); + datasetOffset = (program.getEntropy(13) % (DatasetExtraItems + 1)) * randomx::CacheLineSize; + store64(&config.eMask[0], randomx::getFloatMask(program.getEntropy(14))); + store64(&config.eMask[1], randomx::getFloatMask(program.getEntropy(15))); +} + +namespace randomx { + + template + VmBase::~VmBase() { + } + + template + void VmBase::setScratchpad(uint8_t *scratchpad) { + if (datasetPtr == nullptr) { + throw std::invalid_argument("Cache/Dataset not set"); + } + + this->scratchpad = scratchpad; + } + + template + void VmBase::getFinalResult(void* out, size_t outSize) { + hashAes1Rx4(scratchpad, ScratchpadSize, ®.a); + rx_blake2b(out, outSize, ®, sizeof(RegisterFile), nullptr, 0); + } + + template + void VmBase::initScratchpad(void* seed) { + fillAes1Rx4(seed, ScratchpadSize, scratchpad); + } + + template + void VmBase::generateProgram(void* seed) { + fillAes4Rx4(seed, 128 + RandomX_CurrentConfig.ProgramSize * 8, &program); + } + + template class VmBase; + template class VmBase; +} diff --git a/src/crypto/randomx/virtual_machine.hpp b/src/crypto/randomx/virtual_machine.hpp new file mode 100644 index 00000000..2dc89bb5 --- /dev/null +++ b/src/crypto/randomx/virtual_machine.hpp @@ -0,0 +1,90 @@ +/* +Copyright (c) 2018-2019, tevador + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#pragma once + +#include +#include "crypto/randomx/common.hpp" +#include "crypto/randomx/program.hpp" + +/* Global namespace for C binding */ +class randomx_vm +{ +public: + virtual ~randomx_vm() = 0; + virtual void setScratchpad(uint8_t *scratchpad) = 0; + virtual void getFinalResult(void* out, size_t outSize) = 0; + virtual void setDataset(randomx_dataset* dataset) { } + virtual void setCache(randomx_cache* cache) { } + virtual void initScratchpad(void* seed) = 0; + virtual void run(void* seed) = 0; + void resetRoundingMode(); + + randomx::RegisterFile *getRegisterFile() { + return ® + } + + const void* getScratchpad() { + return scratchpad; + } + + const randomx::Program& getProgram() + { + return program; + } + +protected: + void initialize(); + alignas(64) randomx::Program program; + alignas(64) randomx::RegisterFile reg; + alignas(16) randomx::ProgramConfiguration config; + randomx::MemoryRegisters mem; + uint8_t* scratchpad; + union { + randomx_cache* cachePtr = nullptr; + randomx_dataset* datasetPtr; + }; + uint64_t datasetOffset; +}; + +namespace randomx { + + template + class VmBase : public randomx_vm + { + public: + ~VmBase() override; + void setScratchpad(uint8_t *scratchpad) override; + void initScratchpad(void* seed) override; + void getFinalResult(void* out, size_t outSize) override; + + protected: + void generateProgram(void* seed); + }; + +} diff --git a/src/crypto/randomx/virtual_memory.cpp b/src/crypto/randomx/virtual_memory.cpp new file mode 100644 index 00000000..06165ffb --- /dev/null +++ b/src/crypto/randomx/virtual_memory.cpp @@ -0,0 +1,58 @@ +/* +Copyright (c) 2018-2019, tevador + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include + + +#include "crypto/common/VirtualMemory.h" +#include "crypto/randomx/virtual_memory.hpp" + + +void* allocExecutableMemory(std::size_t bytes) { + void *mem = xmrig::VirtualMemory::allocateExecutableMemory(bytes); + if (mem == nullptr) { + throw std::runtime_error("Failed to allocate executable memory"); + } + + return mem; +} + + +void* allocLargePagesMemory(std::size_t bytes) { + void *mem = xmrig::VirtualMemory::allocateLargePagesMemory(bytes); + if (mem == nullptr) { + throw std::runtime_error("Failed to allocate large pages memory"); + } + + return mem; +} + + +void freePagedMemory(void* ptr, std::size_t bytes) { + xmrig::VirtualMemory::freeLargePagesMemory(ptr, bytes); +} diff --git a/src/crypto/randomx/virtual_memory.hpp b/src/crypto/randomx/virtual_memory.hpp new file mode 100644 index 00000000..d3b31db1 --- /dev/null +++ b/src/crypto/randomx/virtual_memory.hpp @@ -0,0 +1,35 @@ +/* +Copyright (c) 2018-2019, tevador + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#pragma once + +#include + +void* allocExecutableMemory(std::size_t); +void* allocLargePagesMemory(std::size_t); +void freePagedMemory(void*, std::size_t); diff --git a/src/crypto/randomx/vm_compiled.cpp b/src/crypto/randomx/vm_compiled.cpp new file mode 100644 index 00000000..f3b9758c --- /dev/null +++ b/src/crypto/randomx/vm_compiled.cpp @@ -0,0 +1,58 @@ +/* +Copyright (c) 2018-2019, tevador + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "crypto/randomx/vm_compiled.hpp" +#include "crypto/randomx/common.hpp" + +namespace randomx { + + static_assert(sizeof(MemoryRegisters) == 2 * sizeof(addr_t) + sizeof(uintptr_t), "Invalid alignment of struct randomx::MemoryRegisters"); + static_assert(sizeof(RegisterFile) == 256, "Invalid alignment of struct randomx::RegisterFile"); + + template + void CompiledVm::setDataset(randomx_dataset* dataset) { + datasetPtr = dataset; + } + + template + void CompiledVm::run(void* seed) { + VmBase::generateProgram(seed); + randomx_vm::initialize(); + compiler.generateProgram(program, config); + mem.memory = datasetPtr->memory + datasetOffset; + execute(); + } + + template + void CompiledVm::execute() { + compiler.getProgramFunc()(reg, mem, scratchpad, RandomX_CurrentConfig.ProgramIterations); + } + + template class CompiledVm; + template class CompiledVm; +} diff --git a/src/crypto/randomx/vm_compiled.hpp b/src/crypto/randomx/vm_compiled.hpp new file mode 100644 index 00000000..6fa82415 --- /dev/null +++ b/src/crypto/randomx/vm_compiled.hpp @@ -0,0 +1,74 @@ +/* +Copyright (c) 2018-2019, tevador + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#pragma once + +#include +#include +#include "crypto/randomx/virtual_machine.hpp" +#include "crypto/randomx/jit_compiler.hpp" +#include "crypto/randomx/allocator.hpp" +#include "crypto/randomx/dataset.hpp" + +namespace randomx { + + template + class CompiledVm : public VmBase + { + public: + void* operator new(size_t size) { + void* ptr = AlignedAllocator::allocMemory(size); + if (ptr == nullptr) + throw std::bad_alloc(); + return ptr; + } + + void operator delete(void* ptr) { + AlignedAllocator::freeMemory(ptr, sizeof(CompiledVm)); + } + + void setDataset(randomx_dataset* dataset) override; + void run(void* seed) override; + + using VmBase::mem; + using VmBase::program; + using VmBase::config; + using VmBase::reg; + using VmBase::scratchpad; + using VmBase::datasetPtr; + using VmBase::datasetOffset; + + protected: + void execute(); + + JitCompiler compiler; + }; + + using CompiledVmDefault = CompiledVm; + using CompiledVmHardAes = CompiledVm; +} diff --git a/src/crypto/randomx/vm_compiled_light.cpp b/src/crypto/randomx/vm_compiled_light.cpp new file mode 100644 index 00000000..02115cef --- /dev/null +++ b/src/crypto/randomx/vm_compiled_light.cpp @@ -0,0 +1,52 @@ +/* +Copyright (c) 2018-2019, tevador + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "crypto/randomx/vm_compiled_light.hpp" +#include "crypto/randomx/common.hpp" +#include + +namespace randomx { + + template + void CompiledLightVm::setCache(randomx_cache* cache) { + cachePtr = cache; + mem.memory = cache->memory; + compiler.generateSuperscalarHash(cache->programs, cache->reciprocalCache); + } + + template + void CompiledLightVm::run(void* seed) { + VmBase::generateProgram(seed); + randomx_vm::initialize(); + compiler.generateProgramLight(program, config, datasetOffset); + CompiledVm::execute(); + } + + template class CompiledLightVm; + template class CompiledLightVm; +} diff --git a/src/crypto/randomx/vm_compiled_light.hpp b/src/crypto/randomx/vm_compiled_light.hpp new file mode 100644 index 00000000..4d8638a8 --- /dev/null +++ b/src/crypto/randomx/vm_compiled_light.hpp @@ -0,0 +1,65 @@ +/* +Copyright (c) 2018-2019, tevador + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#pragma once + +#include +#include "crypto/randomx/vm_compiled.hpp" + +namespace randomx { + + template + class CompiledLightVm : public CompiledVm + { + public: + void* operator new(size_t size) { + void* ptr = AlignedAllocator::allocMemory(size); + if (ptr == nullptr) + throw std::bad_alloc(); + return ptr; + } + + void operator delete(void* ptr) { + AlignedAllocator::freeMemory(ptr, sizeof(CompiledLightVm)); + } + + void setCache(randomx_cache* cache) override; + void setDataset(randomx_dataset* dataset) override { } + void run(void* seed) override; + + using CompiledVm::mem; + using CompiledVm::compiler; + using CompiledVm::program; + using CompiledVm::config; + using CompiledVm::cachePtr; + using CompiledVm::datasetOffset; + }; + + using CompiledLightVmDefault = CompiledLightVm; + using CompiledLightVmHardAes = CompiledLightVm; +} diff --git a/src/crypto/randomx/vm_interpreted.cpp b/src/crypto/randomx/vm_interpreted.cpp new file mode 100644 index 00000000..e21ecfe6 --- /dev/null +++ b/src/crypto/randomx/vm_interpreted.cpp @@ -0,0 +1,123 @@ +/* +Copyright (c) 2018-2019, tevador + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "crypto/randomx/vm_interpreted.hpp" +#include "crypto/randomx/dataset.hpp" +#include "crypto/randomx/intrin_portable.h" +#include "crypto/randomx/reciprocal.h" + +namespace randomx { + + template + void InterpretedVm::setDataset(randomx_dataset* dataset) { + datasetPtr = dataset; + mem.memory = dataset->memory; + } + + template + void InterpretedVm::run(void* seed) { + VmBase::generateProgram(seed); + randomx_vm::initialize(); + execute(); + } + + template + void InterpretedVm::execute() { + + NativeRegisterFile nreg; + + for(unsigned i = 0; i < RegisterCountFlt; ++i) + nreg.a[i] = rx_load_vec_f128(®.a[i].lo); + + compileProgram(program, bytecode, nreg); + + uint32_t spAddr0 = mem.mx; + uint32_t spAddr1 = mem.ma; + + for(unsigned ic = 0; ic < RandomX_CurrentConfig.ProgramIterations; ++ic) { + uint64_t spMix = nreg.r[config.readReg0] ^ nreg.r[config.readReg1]; + spAddr0 ^= spMix; + spAddr0 &= ScratchpadL3Mask64; + spAddr1 ^= spMix >> 32; + spAddr1 &= ScratchpadL3Mask64; + + for (unsigned i = 0; i < RegistersCount; ++i) + nreg.r[i] ^= load64(scratchpad + spAddr0 + 8 * i); + + for (unsigned i = 0; i < RegisterCountFlt; ++i) + nreg.f[i] = rx_cvt_packed_int_vec_f128(scratchpad + spAddr1 + 8 * i); + + for (unsigned i = 0; i < RegisterCountFlt; ++i) + nreg.e[i] = maskRegisterExponentMantissa(config, rx_cvt_packed_int_vec_f128(scratchpad + spAddr1 + 8 * (RegisterCountFlt + i))); + + executeBytecode(bytecode, scratchpad, config); + + mem.mx ^= nreg.r[config.readReg2] ^ nreg.r[config.readReg3]; + mem.mx &= CacheLineAlignMask; + datasetPrefetch(datasetOffset + mem.mx); + datasetRead(datasetOffset + mem.ma, nreg.r); + std::swap(mem.mx, mem.ma); + + for (unsigned i = 0; i < RegistersCount; ++i) + store64(scratchpad + spAddr1 + 8 * i, nreg.r[i]); + + for (unsigned i = 0; i < RegisterCountFlt; ++i) + nreg.f[i] = rx_xor_vec_f128(nreg.f[i], nreg.e[i]); + + for (unsigned i = 0; i < RegisterCountFlt; ++i) + rx_store_vec_f128((double*)(scratchpad + spAddr0 + 16 * i), nreg.f[i]); + + spAddr0 = 0; + spAddr1 = 0; + } + + for (unsigned i = 0; i < RegistersCount; ++i) + store64(®.r[i], nreg.r[i]); + + for (unsigned i = 0; i < RegisterCountFlt; ++i) + rx_store_vec_f128(®.f[i].lo, nreg.f[i]); + + for (unsigned i = 0; i < RegisterCountFlt; ++i) + rx_store_vec_f128(®.e[i].lo, nreg.e[i]); + } + + template + void InterpretedVm::datasetRead(uint64_t address, int_reg_t(&r)[RegistersCount]) { + uint64_t* datasetLine = (uint64_t*)(mem.memory + address); + for (int i = 0; i < RegistersCount; ++i) + r[i] ^= datasetLine[i]; + } + + template + void InterpretedVm::datasetPrefetch(uint64_t address) { + rx_prefetch_nta(mem.memory + address); + } + + template class InterpretedVm; + template class InterpretedVm; +} diff --git a/src/crypto/randomx/vm_interpreted.hpp b/src/crypto/randomx/vm_interpreted.hpp new file mode 100644 index 00000000..b369ab11 --- /dev/null +++ b/src/crypto/randomx/vm_interpreted.hpp @@ -0,0 +1,78 @@ +/* +Copyright (c) 2018-2019, tevador + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#pragma once + +#include +#include +#include "common.hpp" +#include "crypto/randomx/virtual_machine.hpp" +#include "crypto/randomx/bytecode_machine.hpp" +#include "crypto/randomx/intrin_portable.h" +#include "crypto/randomx/allocator.hpp" + +namespace randomx { + + template + class InterpretedVm : public VmBase, public BytecodeMachine { + public: + using VmBase::mem; + using VmBase::scratchpad; + using VmBase::program; + using VmBase::config; + using VmBase::reg; + using VmBase::datasetPtr; + using VmBase::datasetOffset; + + void* operator new(size_t size) { + void* ptr = AlignedAllocator::allocMemory(size); + if (ptr == nullptr) + throw std::bad_alloc(); + return ptr; + } + + void operator delete(void* ptr) { + AlignedAllocator::freeMemory(ptr, sizeof(InterpretedVm)); + } + + void run(void* seed) override; + void setDataset(randomx_dataset* dataset) override; + + protected: + virtual void datasetRead(uint64_t blockNumber, int_reg_t(&r)[RegistersCount]); + virtual void datasetPrefetch(uint64_t blockNumber); + + private: + void execute(); + + InstructionByteCode bytecode[RANDOMX_PROGRAM_MAX_SIZE]; + }; + + using InterpretedVmDefault = InterpretedVm; + using InterpretedVmHardAes = InterpretedVm; +} diff --git a/src/crypto/randomx/vm_interpreted_light.cpp b/src/crypto/randomx/vm_interpreted_light.cpp new file mode 100644 index 00000000..bed6f35b --- /dev/null +++ b/src/crypto/randomx/vm_interpreted_light.cpp @@ -0,0 +1,53 @@ +/* +Copyright (c) 2018-2019, tevador + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "crypto/randomx/vm_interpreted_light.hpp" +#include "crypto/randomx/dataset.hpp" + +namespace randomx { + + template + void InterpretedLightVm::setCache(randomx_cache* cache) { + cachePtr = cache; + mem.memory = cache->memory; + } + + template + void InterpretedLightVm::datasetRead(uint64_t address, int_reg_t(&r)[8]) { + uint32_t itemNumber = address / CacheLineSize; + int_reg_t rl[8]; + + initDatasetItem(cachePtr, (uint8_t*)rl, itemNumber); + + for (unsigned q = 0; q < 8; ++q) + r[q] ^= rl[q]; + } + + template class InterpretedLightVm; + template class InterpretedLightVm; +} diff --git a/src/crypto/randomx/vm_interpreted_light.hpp b/src/crypto/randomx/vm_interpreted_light.hpp new file mode 100644 index 00000000..c8abba2c --- /dev/null +++ b/src/crypto/randomx/vm_interpreted_light.hpp @@ -0,0 +1,63 @@ +/* +Copyright (c) 2018-2019, tevador + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#pragma once + +#include +#include "crypto/randomx/vm_interpreted.hpp" + +namespace randomx { + + template + class InterpretedLightVm : public InterpretedVm { + public: + using VmBase::mem; + using VmBase::cachePtr; + + void* operator new(size_t size) { + void* ptr = AlignedAllocator::allocMemory(size); + if (ptr == nullptr) + throw std::bad_alloc(); + return ptr; + } + + void operator delete(void* ptr) { + AlignedAllocator::freeMemory(ptr, sizeof(InterpretedLightVm)); + } + + void setDataset(randomx_dataset* dataset) override { } + void setCache(randomx_cache* cache) override; + + protected: + void datasetRead(uint64_t address, int_reg_t(&r)[8]) override; + void datasetPrefetch(uint64_t address) override { } + }; + + using InterpretedLightVmDefault = InterpretedLightVm; + using InterpretedLightVmHardAes = InterpretedLightVm; +} diff --git a/src/crypto/rx/Rx.cpp b/src/crypto/rx/Rx.cpp new file mode 100644 index 00000000..8e757ddf --- /dev/null +++ b/src/crypto/rx/Rx.cpp @@ -0,0 +1,323 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2019 XMR-Stak , + * Copyright 2018 Lee Clagett + * Copyright 2018-2019 tevador + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include +#include +#include + + +#ifdef XMRIG_FEATURE_HWLOC +# include +# include "backend/cpu/platform/HwlocCpuInfo.h" +#endif + + +#include "backend/cpu/Cpu.h" +#include "base/io/log/Log.h" +#include "base/kernel/Platform.h" +#include "base/net/stratum/Job.h" +#include "base/tools/Buffer.h" +#include "base/tools/Chrono.h" +#include "crypto/rx/Rx.h" +#include "crypto/rx/RxAlgo.h" +#include "crypto/rx/RxCache.h" +#include "crypto/rx/RxDataset.h" + + +namespace xmrig { + + +class RxPrivate; + + +static const char *tag = BLUE_BG(WHITE_BOLD_S " rx ") " "; +static RxPrivate *d_ptr = nullptr; + + +#ifdef XMRIG_FEATURE_HWLOC +static void bindToNUMANode(uint32_t nodeId) +{ + hwloc_topology_t topology; + hwloc_topology_init(&topology); + hwloc_topology_load(topology); + + hwloc_obj_t node = hwloc_get_numanode_obj_by_os_index(topology, nodeId); + if (node) { + if (HwlocCpuInfo::has(HwlocCpuInfo::SET_THISTHREAD_MEMBIND)) { +# if HWLOC_API_VERSION >= 0x20000 + hwloc_set_membind(topology, node->nodeset, HWLOC_MEMBIND_BIND, HWLOC_MEMBIND_THREAD | HWLOC_MEMBIND_BYNODESET); +# else + hwloc_set_membind_nodeset(topology, node->nodeset, HWLOC_MEMBIND_BIND, HWLOC_MEMBIND_THREAD); +# endif + } + + Platform::setThreadAffinity(static_cast(hwloc_bitmap_first(node->cpuset))); + } + + hwloc_topology_destroy(topology); +} +#else +inline static void bindToNUMANode(uint32_t) {} +#endif + + +class RxPrivate +{ +public: + inline RxPrivate() : + m_seed() + { +# ifdef XMRIG_FEATURE_HWLOC + if (Cpu::info()->nodes() > 1) { + for (uint32_t nodeId : HwlocCpuInfo::nodeIndexes()) { + datasets.insert({ nodeId, nullptr }); + } + } + else +# endif + { + datasets.insert({ 0, nullptr }); + } + } + + + inline ~RxPrivate() + { + for (auto const &item : datasets) { + delete item.second; + } + + datasets.clear(); + } + + + inline bool isNUMA() const { return m_numa; } + inline const Algorithm &algorithm() const { return m_algorithm; } + inline const uint8_t *seed() const { return m_seed; } + inline size_t count() const { return isNUMA() ? datasets.size() : 1; } + + + static void allocate(uint32_t nodeId) + { + const uint64_t ts = Chrono::steadyMSecs(); + + if (d_ptr->isNUMA()) { + bindToNUMANode(nodeId); + } + + LOG_INFO("%s" CYAN_BOLD("#%u") MAGENTA_BOLD(" allocate") CYAN_BOLD(" %zu MB") BLACK_BOLD(" (%zu+%zu) for RandomX dataset & cache"), + tag, + nodeId, + (RxDataset::size() + RxCache::size()) / 1024 / 1024, + RxDataset::size() / 1024 / 1024, + RxCache::size() / 1024 / 1024 + ); + + RxDataset *dataset = new RxDataset(d_ptr->m_hugePages); + d_ptr->datasets[nodeId] = dataset; + + if (dataset->get() != nullptr) { + const auto hugePages = dataset->hugePages(); + const double percent = hugePages.first == 0 ? 0.0 : static_cast(hugePages.first) / hugePages.second * 100.0; + + LOG_INFO("%s" CYAN_BOLD("#%u") GREEN(" allocate done") " huge pages %s%u/%u %1.0f%%" CLEAR " %sJIT" BLACK_BOLD(" (%" PRIu64 " ms)"), + tag, + nodeId, + (hugePages.first == hugePages.second ? GREEN_BOLD_S : (hugePages.first == 0 ? RED_BOLD_S : YELLOW_BOLD_S)), + hugePages.first, + hugePages.second, + percent, + dataset->cache()->isJIT() ? GREEN_BOLD_S "+" : RED_BOLD_S "-", + Chrono::steadyMSecs() - ts + ); + } + else { + LOG_WARN(CLEAR "%s" CYAN_BOLD("#%u") YELLOW_BOLD_S " failed to allocate RandomX dataset, switching to slow mode", tag, nodeId); + } + } + + + static void initDataset(uint32_t nodeId, uint32_t threads) + { + std::lock_guard lock(d_ptr->mutex); + + const uint64_t ts = Chrono::steadyMSecs(); + + d_ptr->getOrAllocate(nodeId)->init(d_ptr->seed(), threads); + d_ptr->m_ready++; + + LOG_INFO("%s" CYAN_BOLD("#%u") GREEN(" init done") BLACK_BOLD(" (%" PRIu64 " ms)"), tag, nodeId, Chrono::steadyMSecs() - ts); + } + + + inline RxDataset *getOrAllocate(uint32_t nodeId) + { + RxDataset *dataset = datasets.at(nodeId); + + if (dataset == nullptr) { + # ifdef XMRIG_FEATURE_HWLOC + if (d_ptr->isNUMA()) { + std::thread thread(allocate, nodeId); + thread.join(); + } else + # endif + { + allocate(nodeId); + } + + dataset = datasets.at(nodeId); + } + + return dataset; + } + + + inline void setState(const Job &job, bool hugePages, bool numa) + { + if (m_algorithm != job.algorithm()) { + m_algorithm = RxAlgo::apply(job.algorithm()); + } + + m_ready = 0; + m_numa = numa && Cpu::info()->nodes() > 1; + m_hugePages = hugePages; + + memcpy(m_seed, job.seedHash(), sizeof(m_seed)); + } + + + inline bool isReady(const Job &job) + { + return m_ready == count() && m_algorithm == job.algorithm() && memcmp(m_seed, job.seedHash(), sizeof(m_seed)) == 0; + } + + + std::map datasets; + std::mutex mutex; + +private: + bool m_hugePages = true; + bool m_numa = true; + Algorithm m_algorithm; + size_t m_ready = 0; + uint8_t m_seed[32]; +}; + + +} // namespace xmrig + + +bool xmrig::Rx::isReady(const Job &job) +{ + std::lock_guard lock(d_ptr->mutex); + + return d_ptr->isReady(job); +} + + +xmrig::RxDataset *xmrig::Rx::dataset(const Job &job, uint32_t nodeId) +{ + std::lock_guard lock(d_ptr->mutex); + if (!d_ptr->isReady(job)) { + return nullptr; + } + + return d_ptr->datasets.at(d_ptr->isNUMA() ? nodeId : 0); +} + + +std::pair xmrig::Rx::hugePages() +{ + std::pair pages(0, 0); + std::lock_guard lock(d_ptr->mutex); + + for (auto const &item : d_ptr->datasets) { + if (!item.second) { + continue; + } + + const auto p = item.second->hugePages(); + pages.first += p.first; + pages.second += p.second; + } + + return pages; +} + + +void xmrig::Rx::destroy() +{ + delete d_ptr; + + d_ptr = nullptr; +} + + +void xmrig::Rx::init() +{ + d_ptr = new RxPrivate(); +} + + +void xmrig::Rx::init(const Job &job, int initThreads, bool hugePages, bool numa) +{ + if (job.algorithm().family() != Algorithm::RANDOM_X) { + return; + } + + std::lock_guard lock(d_ptr->mutex); + + if (d_ptr->isReady(job)) { + return; + } + + d_ptr->setState(job, hugePages, numa); + const uint32_t threads = initThreads < 1 ? static_cast(Cpu::info()->threads()) : static_cast(initThreads); + const String buf = Buffer::toHex(job.seedHash(), 8); + + LOG_INFO("%s" MAGENTA_BOLD("init dataset%s") " algo " WHITE_BOLD("%s (") CYAN_BOLD("%u") WHITE_BOLD(" threads)") BLACK_BOLD(" seed %s..."), + tag, + d_ptr->count() > 1 ? "s" : "", + job.algorithm().shortName(), + threads, + buf.data() + ); + +# ifdef XMRIG_FEATURE_HWLOC + if (d_ptr->isNUMA()) { + for (auto const &item : d_ptr->datasets) { + std::thread thread(RxPrivate::initDataset, item.first, threads); + thread.detach(); + } + } + else +# endif + { + std::thread thread(RxPrivate::initDataset, 0, threads); + thread.detach(); + } +} diff --git a/src/interfaces/IClientListener.h b/src/crypto/rx/Rx.h similarity index 55% rename from src/interfaces/IClientListener.h rename to src/crypto/rx/Rx.h index f6e7fd3c..1a383055 100644 --- a/src/interfaces/IClientListener.h +++ b/src/crypto/rx/Rx.h @@ -4,8 +4,11 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2017-2019 XMR-Stak , + * Copyright 2018 Lee Clagett + * Copyright 2018-2019 tevador + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , * * 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 @@ -21,28 +24,36 @@ * along with this program. If not, see . */ -#ifndef __ICLIENTLISTENER_H__ -#define __ICLIENTLISTENER_H__ +#ifndef XMRIG_RX_H +#define XMRIG_RX_H #include +#include -class Client; +namespace xmrig +{ + + +class Algorithm; +class RxDataset; class Job; -class SubmitResult; -class IClientListener +class Rx { public: - virtual ~IClientListener() {} - - virtual void onClose(Client *client, int failures) = 0; - virtual void onJobReceived(Client *client, const Job &job) = 0; - virtual void onLoginSuccess(Client *client) = 0; - virtual void onResultAccepted(Client *client, const SubmitResult &result, const char *error) = 0; + static bool isReady(const Job &job); + static RxDataset *dataset(const Job &job, uint32_t nodeId); + static std::pair hugePages(); + static void destroy(); + static void init(); + static void init(const Job &job, int initThreads, bool hugePages, bool numa); }; -#endif // __ICLIENTLISTENER_H__ +} /* namespace xmrig */ + + +#endif /* XMRIG_RX_H */ diff --git a/src/crypto/rx/RxAlgo.cpp b/src/crypto/rx/RxAlgo.cpp new file mode 100644 index 00000000..daf3ec2d --- /dev/null +++ b/src/crypto/rx/RxAlgo.cpp @@ -0,0 +1,49 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2019 XMR-Stak , + * Copyright 2018 Lee Clagett + * Copyright 2018-2019 tevador + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include "crypto/randomx/randomx.h" +#include "crypto/rx/RxAlgo.h" + + +xmrig::Algorithm::Id xmrig::RxAlgo::apply(Algorithm::Id algorithm) +{ + switch (algorithm) { + case Algorithm::RX_WOW: + randomx_apply_config(RandomX_WowneroConfig); + break; + + case Algorithm::RX_LOKI: + randomx_apply_config(RandomX_LokiConfig); + break; + + default: + randomx_apply_config(RandomX_MoneroConfig); + break; + } + + return algorithm; +} diff --git a/src/crypto/HashSelector.h b/src/crypto/rx/RxAlgo.h similarity index 63% rename from src/crypto/HashSelector.h rename to src/crypto/rx/RxAlgo.h index 3beeca5b..95033a9c 100644 --- a/src/crypto/HashSelector.h +++ b/src/crypto/rx/RxAlgo.h @@ -4,8 +4,11 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2017-2019 XMR-Stak , + * Copyright 2018 Lee Clagett + * Copyright 2018-2019 tevador + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , * * 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 @@ -21,31 +24,29 @@ * along with this program. If not, see . */ -#ifndef __HASH_SELECTOR_H__ -#define __HASH_SELECTOR_H__ +#ifndef XMRIG_RX_ALGO_H +#define XMRIG_RX_ALGO_H #include #include -#include "CryptoNight.h" -#include "AsmOptimization.h" -#include "Options.h" -class Job; -class JobResult; +#include "crypto/common/Algorithm.h" -class HashSelector + +namespace xmrig +{ + + +class RxAlgo { public: - static bool init(Options::Algo algo, bool aesni); - static void hash(size_t factor, AsmOptimization asmOptimization, uint64_t height, PowVariant powVersion, const uint8_t* input, size_t size, uint8_t* output, ScratchPad** scratchPads); - -public: - -private: - static bool selfCheck(Options::Algo algo); + static Algorithm::Id apply(Algorithm::Id algorithm); }; -#endif /* __HASH_SELECTOR_H__ */ +} /* namespace xmrig */ + + +#endif /* XMRIG_RX_ALGO_H */ diff --git a/src/crypto/rx/RxCache.cpp b/src/crypto/rx/RxCache.cpp new file mode 100644 index 00000000..92c366a2 --- /dev/null +++ b/src/crypto/rx/RxCache.cpp @@ -0,0 +1,83 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2019 XMR-Stak , + * Copyright 2018 Lee Clagett + * Copyright 2018-2019 tevador + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include "crypto/randomx/randomx.h" +#include "crypto/rx/RxCache.h" + + +static_assert(RANDOMX_FLAG_JIT == 8, "RANDOMX_FLAG_JIT flag mismatch"); +static_assert(RANDOMX_FLAG_LARGE_PAGES == 1, "RANDOMX_FLAG_LARGE_PAGES flag mismatch"); + + + +xmrig::RxCache::RxCache(bool hugePages) : + m_seed() +{ + if (hugePages) { + m_flags = RANDOMX_FLAG_JIT | RANDOMX_FLAG_LARGE_PAGES; + m_cache = randomx_alloc_cache(static_cast(m_flags)); + } + + if (!m_cache) { + m_flags = RANDOMX_FLAG_JIT; + m_cache = randomx_alloc_cache(static_cast(m_flags)); + } + + if (!m_cache) { + m_flags = RANDOMX_FLAG_DEFAULT; + m_cache = randomx_alloc_cache(static_cast(m_flags)); + } +} + + +xmrig::RxCache::~RxCache() +{ + if (m_cache) { + randomx_release_cache(m_cache); + } +} + + +bool xmrig::RxCache::init(const uint8_t *seed) +{ + if (isReady(seed)) { + return false; + } + + memcpy(m_seed, seed, sizeof(m_seed)); + randomx_init_cache(m_cache, m_seed, sizeof(m_seed)); + + m_initCount++; + + return true; +} + + +bool xmrig::RxCache::isReady(const uint8_t *seed) const +{ + return m_initCount && memcmp(m_seed, seed, sizeof(m_seed)) == 0; +} diff --git a/src/crypto/rx/RxCache.h b/src/crypto/rx/RxCache.h new file mode 100644 index 00000000..e6b2397c --- /dev/null +++ b/src/crypto/rx/RxCache.h @@ -0,0 +1,73 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2019 XMR-Stak , + * Copyright 2018 Lee Clagett + * Copyright 2018-2019 tevador + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_RX_CACHE_H +#define XMRIG_RX_CACHE_H + + +#include + + +#include "crypto/randomx/configuration.h" + + +struct randomx_cache; + + +namespace xmrig +{ + + +class RxCache +{ +public: + RxCache(bool hugePages = true); + ~RxCache(); + + inline bool isHugePages() const { return m_flags & 1; } + inline bool isJIT() const { return m_flags & 8; } + inline const uint8_t *seed() const { return m_seed; } + inline randomx_cache *get() const { return m_cache; } + inline uint64_t initCount() const { return m_initCount; } + + bool init(const uint8_t *seed); + + static inline constexpr size_t size() { return RANDOMX_CACHE_MAX_SIZE; } + +private: + bool isReady(const uint8_t *seed) const; + + int m_flags = 0; + randomx_cache *m_cache = nullptr; + uint64_t m_initCount = 0; + uint8_t m_seed[32]; +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_RX_CACHE_H */ diff --git a/src/crypto/rx/RxConfig.cpp b/src/crypto/rx/RxConfig.cpp new file mode 100644 index 00000000..dc543fb4 --- /dev/null +++ b/src/crypto/rx/RxConfig.cpp @@ -0,0 +1,63 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include "base/io/json/Json.h" +#include "crypto/rx/RxConfig.h" +#include "rapidjson/document.h" + + +namespace xmrig { + +static const char *kInit = "init"; +static const char *kNUMA = "numa"; + +} + + +rapidjson::Value xmrig::RxConfig::toJSON(rapidjson::Document &doc) const +{ + using namespace rapidjson; + auto &allocator = doc.GetAllocator(); + + Value obj(kObjectType); + + obj.AddMember(StringRef(kInit), m_threads, allocator); + obj.AddMember(StringRef(kNUMA), m_numa, allocator); + + return obj; +} + + +bool xmrig::RxConfig::read(const rapidjson::Value &value) +{ + if (value.IsObject()) { + m_numa = Json::getBool(value, kNUMA, m_numa); + m_threads = Json::getInt(value, kInit, m_threads); + + return true; + } + + return false; +} diff --git a/src/api/Httpd.h b/src/crypto/rx/RxConfig.h similarity index 59% rename from src/api/Httpd.h rename to src/crypto/rx/RxConfig.h index 7a4dd932..e06c764c 100644 --- a/src/api/Httpd.h +++ b/src/crypto/rx/RxConfig.h @@ -4,8 +4,9 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , * * 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 @@ -21,33 +22,32 @@ * along with this program. If not, see . */ -#ifndef __HTTPD_H__ -#define __HTTPD_H__ +#ifndef XMRIG_RXCONFIG_H +#define XMRIG_RXCONFIG_H -#include +#include "rapidjson/fwd.h" -struct MHD_Connection; -struct MHD_Daemon; -struct MHD_Response; +namespace xmrig { -class Httpd +class RxConfig { public: - Httpd(int port, const char *accessToken); - bool start(); + bool read(const rapidjson::Value &value); + rapidjson::Value toJSON(rapidjson::Document &doc) const; + + inline bool isNUMA() const { return m_numa; } + inline int threads() const { return m_threads; } private: - int auth(const char *header); - - static int done(MHD_Connection *connection, int status, MHD_Response *rsp); - static int handler(void *cls, MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **con_cls); - - const char *m_accessToken; - const int m_port; - MHD_Daemon *m_daemon; + bool m_numa = true; + int m_threads = -1; }; -#endif /* __HTTPD_H__ */ + +} /* namespace xmrig */ + + +#endif /* XMRIG_RXCONFIG_H */ diff --git a/src/crypto/rx/RxDataset.cpp b/src/crypto/rx/RxDataset.cpp new file mode 100644 index 00000000..50459a55 --- /dev/null +++ b/src/crypto/rx/RxDataset.cpp @@ -0,0 +1,114 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2019 XMR-Stak , + * Copyright 2018 Lee Clagett + * Copyright 2018-2019 tevador + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include + + +#include "crypto/common/VirtualMemory.h" +#include "crypto/randomx/randomx.h" +#include "crypto/rx/RxAlgo.h" +#include "crypto/rx/RxCache.h" +#include "crypto/rx/RxDataset.h" + + +static_assert(RANDOMX_FLAG_LARGE_PAGES == 1, "RANDOMX_FLAG_LARGE_PAGES flag mismatch"); + + +xmrig::RxDataset::RxDataset(bool hugePages) +{ + if (hugePages) { + m_flags = RANDOMX_FLAG_LARGE_PAGES; + m_dataset = randomx_alloc_dataset(static_cast(m_flags)); + } + + if (!m_dataset) { + m_flags = RANDOMX_FLAG_DEFAULT; + m_dataset = randomx_alloc_dataset(static_cast(m_flags)); + } + + m_cache = new RxCache(hugePages); +} + + +xmrig::RxDataset::~RxDataset() +{ + if (m_dataset) { + randomx_release_dataset(m_dataset); + } + + delete m_cache; +} + + +bool xmrig::RxDataset::init(const uint8_t *seed, uint32_t numThreads) +{ + cache()->init(seed); + + if (!get()) { + return true; + } + + const uint32_t datasetItemCount = randomx_dataset_item_count(); + + if (numThreads > 1) { + std::vector threads; + threads.reserve(numThreads); + + for (uint32_t i = 0; i < numThreads; ++i) { + const uint32_t a = (datasetItemCount * i) / numThreads; + const uint32_t b = (datasetItemCount * (i + 1)) / numThreads; + threads.emplace_back(randomx_init_dataset, m_dataset, m_cache->get(), a, b - a); + } + + for (uint32_t i = 0; i < numThreads; ++i) { + threads[i].join(); + } + } + else { + randomx_init_dataset(m_dataset, m_cache->get(), 0, datasetItemCount); + } + + return true; +} + + +std::pair xmrig::RxDataset::hugePages() const +{ + constexpr size_t twoMiB = 2u * 1024u * 1024u; + constexpr const size_t total = (VirtualMemory::align(size(), twoMiB) + VirtualMemory::align(RxCache::size(), twoMiB)) / twoMiB; + + size_t count = 0; + if (isHugePages()) { + count += VirtualMemory::align(size(), twoMiB) / twoMiB; + } + + if (m_cache->isHugePages()) { + count += VirtualMemory::align(RxCache::size(), twoMiB) / twoMiB; + } + + return std::pair(count, total); +} diff --git a/src/crypto/rx/RxDataset.h b/src/crypto/rx/RxDataset.h new file mode 100644 index 00000000..932f4ed9 --- /dev/null +++ b/src/crypto/rx/RxDataset.h @@ -0,0 +1,71 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2019 XMR-Stak , + * Copyright 2018 Lee Clagett + * Copyright 2018-2019 tevador + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_RX_DATASET_H +#define XMRIG_RX_DATASET_H + + +#include "crypto/common/Algorithm.h" +#include "crypto/randomx/configuration.h" + + +struct randomx_dataset; + + +namespace xmrig +{ + + +class RxCache; + + +class RxDataset +{ +public: + RxDataset(bool hugePages = true); + ~RxDataset(); + + inline bool isHugePages() const { return m_flags & 1; } + inline randomx_dataset *get() const { return m_dataset; } + inline RxCache *cache() const { return m_cache; } + + bool init(const uint8_t *seed, uint32_t numThreads); + std::pair hugePages() const; + + static inline constexpr size_t size() { return RANDOMX_DATASET_MAX_SIZE; } + +private: + Algorithm m_algorithm; + int m_flags = 0; + randomx_dataset *m_dataset = nullptr; + RxCache *m_cache = nullptr; +}; + + +} /* namespace xmrig */ + + +#endif /* XMRIG_RX_DATASET_H */ diff --git a/src/crypto/rx/RxVm.cpp b/src/crypto/rx/RxVm.cpp new file mode 100644 index 00000000..275f9558 --- /dev/null +++ b/src/crypto/rx/RxVm.cpp @@ -0,0 +1,59 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2019 XMR-Stak , + * Copyright 2018 Lee Clagett + * Copyright 2018-2019 tevador + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include "crypto/randomx/randomx.h" +#include "crypto/rx/RxCache.h" +#include "crypto/rx/RxDataset.h" +#include "crypto/rx/RxVm.h" + + +xmrig::RxVm::RxVm(RxDataset *dataset, uint8_t *scratchpad, bool softAes) +{ +# ifndef XMRIG_ARM + if (!softAes) { + m_flags |= RANDOMX_FLAG_HARD_AES; + } +# endif + + if (dataset->get()) { + m_flags |= RANDOMX_FLAG_FULL_MEM; + } + + if (dataset->cache()->isJIT()) { + m_flags |= RANDOMX_FLAG_JIT; + } + + m_vm = randomx_create_vm(static_cast(m_flags), dataset->cache()->get(), dataset->get(), scratchpad); +} + + +xmrig::RxVm::~RxVm() +{ + if (m_vm) { + randomx_destroy_vm(m_vm); + } +} diff --git a/src/workers/Worker.h b/src/crypto/rx/RxVm.h similarity index 58% rename from src/workers/Worker.h rename to src/crypto/rx/RxVm.h index 9abf2ec3..d7e617e4 100644 --- a/src/workers/Worker.h +++ b/src/crypto/rx/RxVm.h @@ -4,8 +4,11 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2017-2019 XMR-Stak , + * Copyright 2018 Lee Clagett + * Copyright 2018-2019 tevador + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , * * 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 @@ -21,41 +24,38 @@ * along with this program. If not, see . */ -#ifndef __WORKER_H__ -#define __WORKER_H__ +#ifndef XMRIG_RX_VM_H +#define XMRIG_RX_VM_H -#include #include -#include "interfaces/IWorker.h" +struct randomx_vm; -struct ScratchPad; -class Handle; +namespace xmrig +{ -class Worker : public IWorker +class RxDataset; + + +class RxVm { public: - Worker(Handle *handle); - ~Worker(); + RxVm(RxDataset *dataset, uint8_t *scratchpad, bool softAes); + ~RxVm(); - 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 randomx_vm *get() const { return m_vm; } -protected: - void storeStats(); - - int m_id; - int m_affinedCpu; - size_t m_threads; - std::atomic m_hashCount; - std::atomic m_timestamp; - uint64_t m_count; - uint64_t m_sequence; +private: + int m_flags = 0; + randomx_vm *m_vm = nullptr; }; -#endif /* __WORKER_H__ */ +} /* namespace xmrig */ + + +#endif /* XMRIG_RX_CACHE_H */ diff --git a/src/crypto/variant4_random_math.h b/src/crypto/variant4_random_math.h deleted file mode 100644 index 3bbcdc5b..00000000 --- a/src/crypto/variant4_random_math.h +++ /dev/null @@ -1,447 +0,0 @@ -#ifndef VARIANT4_RANDOM_MATH_H -#define VARIANT4_RANDOM_MATH_H - -extern "C" -{ -#include "c_blake256.h" -} - -enum V4_Settings -{ - // Generate code with minimal theoretical latency = 45 cycles, which is equivalent to 15 multiplications - TOTAL_LATENCY = 15 * 3, - - // Always generate at least 60 instructions - NUM_INSTRUCTIONS_MIN = 60, - - // Never generate more than 70 instructions (final RET instruction doesn't count here) - NUM_INSTRUCTIONS_MAX = 70, - - // Available ALUs for MUL - // Modern CPUs typically have only 1 ALU which can do multiplications - ALU_COUNT_MUL = 1, - - // Total available ALUs - // Modern CPUs have 4 ALUs, but we use only 3 because random math executes together with other main loop code - ALU_COUNT = 3, -}; - -enum V4_InstructionList -{ - MUL, // a*b - ADD, // a+b + C, C is an unsigned 32-bit constant - SUB, // a-b - ROR, // rotate right "a" by "b & 31" bits - ROL, // rotate left "a" by "b & 31" bits - XOR, // a^b - RET, // finish execution - V4_INSTRUCTION_COUNT = RET, -}; - -// V4_InstructionDefinition is used to generate code from random data -// Every random sequence of bytes is a valid code -// -// There are 9 registers in total: -// - 4 variable registers -// - 5 constant registers initialized from loop variables -// This is why dst_index is 2 bits -enum V4_InstructionDefinition -{ - V4_OPCODE_BITS = 3, - V4_DST_INDEX_BITS = 2, - V4_SRC_INDEX_BITS = 3, -}; - -struct V4_Instruction -{ - uint8_t opcode; - uint8_t dst_index; - uint8_t src_index; - uint32_t C; -}; - -#ifndef FORCEINLINE -#ifdef __GNUC__ -#define FORCEINLINE __attribute__((always_inline)) inline -#elif _MSC_VER -#define FORCEINLINE __forceinline -#else -#define FORCEINLINE inline -#endif -#endif - -#ifndef UNREACHABLE_CODE -#ifdef __GNUC__ -#define UNREACHABLE_CODE __builtin_unreachable() -#elif _MSC_VER -#define UNREACHABLE_CODE __assume(false) -#else -#define UNREACHABLE_CODE -#endif -#endif - -// Random math interpreter's loop is fully unrolled and inlined to achieve 100% branch prediction on CPU: -// every switch-case will point to the same destination on every iteration of Cryptonight main loop -// -// This is about as fast as it can get without using low-level machine code generation -template -static void v4_random_math(const struct V4_Instruction* code, v4_reg* r) -{ - enum - { - REG_BITS = sizeof(v4_reg) * 8, - }; - -#define V4_EXEC(i) \ - { \ - const struct V4_Instruction* op = code + i; \ - const v4_reg src = r[op->src_index]; \ - v4_reg* dst = r + op->dst_index; \ - switch (op->opcode) \ - { \ - case MUL: \ - *dst *= src; \ - break; \ - case ADD: \ - *dst += src + op->C; \ - break; \ - case SUB: \ - *dst -= src; \ - break; \ - case ROR: \ - { \ - const uint32_t shift = src % REG_BITS; \ - *dst = (*dst >> shift) | (*dst << ((REG_BITS - shift) % REG_BITS)); \ - } \ - break; \ - case ROL: \ - { \ - const uint32_t shift = src % REG_BITS; \ - *dst = (*dst << shift) | (*dst >> ((REG_BITS - shift) % REG_BITS)); \ - } \ - break; \ - case XOR: \ - *dst ^= src; \ - break; \ - case RET: \ - return; \ - default: \ - UNREACHABLE_CODE; \ - break; \ - } \ - } - -#define V4_EXEC_10(j) \ - V4_EXEC(j + 0) \ - V4_EXEC(j + 1) \ - V4_EXEC(j + 2) \ - V4_EXEC(j + 3) \ - V4_EXEC(j + 4) \ - V4_EXEC(j + 5) \ - V4_EXEC(j + 6) \ - V4_EXEC(j + 7) \ - V4_EXEC(j + 8) \ - V4_EXEC(j + 9) - - // Generated program can have 60 + a few more (usually 2-3) instructions to achieve required latency - // I've checked all block heights < 10,000,000 and here is the distribution of program sizes: - // - // 60 27960 - // 61 105054 - // 62 2452759 - // 63 5115997 - // 64 1022269 - // 65 1109635 - // 66 153145 - // 67 8550 - // 68 4529 - // 69 102 - - // Unroll 70 instructions here - V4_EXEC_10(0); // instructions 0-9 - V4_EXEC_10(10); // instructions 10-19 - V4_EXEC_10(20); // instructions 20-29 - V4_EXEC_10(30); // instructions 30-39 - V4_EXEC_10(40); // instructions 40-49 - V4_EXEC_10(50); // instructions 50-59 - V4_EXEC_10(60); // instructions 60-69 - -#undef V4_EXEC_10 -#undef V4_EXEC -} - -// If we don't have enough data available, generate more -static FORCEINLINE void check_data(size_t* data_index, const size_t bytes_needed, int8_t* data, const size_t data_size) -{ - if (*data_index + bytes_needed > data_size) - { - hash_extra_blake(data, data_size, (char*) data); - *data_index = 0; - } -} - -// Generates as many random math operations as possible with given latency and ALU restrictions -// "code" array must have space for NUM_INSTRUCTIONS_MAX+1 instructions -static int v4_random_math_init(struct V4_Instruction* code, PowVariant variant, const uint64_t height) -{ - // MUL is 3 cycles, 3-way addition and rotations are 2 cycles, SUB/XOR are 1 cycle - // These latencies match real-life instruction latencies for Intel CPUs starting from Sandy Bridge and up to Skylake/Coffee lake - // - // AMD Ryzen has the same latencies except 1-cycle ROR/ROL, so it'll be a bit faster than Intel Sandy Bridge and newer processors - // Surprisingly, Intel Nehalem also has 1-cycle ROR/ROL, so it'll also be faster than Intel Sandy Bridge and newer processors - // AMD Bulldozer has 4 cycles latency for MUL (slower than Intel) and 1 cycle for ROR/ROL (faster than Intel), so average performance will be the same - // Source: https://www.agner.org/optimize/instruction_tables.pdf - const int op_latency[V4_INSTRUCTION_COUNT] = { 3, 2, 1, 2, 2, 1 }; - - // Instruction latencies for theoretical ASIC implementation - const int asic_op_latency[V4_INSTRUCTION_COUNT] = { 3, 1, 1, 1, 1, 1 }; - - // Available ALUs for each instruction - const int op_ALUs[V4_INSTRUCTION_COUNT] = { ALU_COUNT_MUL, ALU_COUNT, ALU_COUNT, ALU_COUNT, ALU_COUNT, ALU_COUNT }; - - int8_t data[32]; - memset(data, 0, sizeof(data)); - uint64_t tmp = SWAP64LE(height); - memcpy(data, &tmp, sizeof(uint64_t)); - if (variant == POW_V4) - { - data[20] = -38; - } - - // Set data_index past the last byte in data - // to trigger full data update with blake hash - // before we start using it - size_t data_index = sizeof(data); - - int code_size; - - // There is a small chance (1.8%) that register R8 won't be used in the generated program - // So we keep track of it and try again if it's not used - bool r8_used; - do { - int latency[9]; - int asic_latency[9]; - - // Tracks previous instruction and value of the source operand for registers R0-R3 throughout code execution - // byte 0: current value of the destination register - // byte 1: instruction opcode - // byte 2: current value of the source register - // - // Registers R4-R8 are constant and are treated as having the same value because when we do - // the same operation twice with two constant source registers, it can be optimized into a single operation - uint32_t inst_data[9] = { 0, 1, 2, 3, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF }; - - bool alu_busy[TOTAL_LATENCY + 1][ALU_COUNT]; - bool is_rotation[V4_INSTRUCTION_COUNT]; - bool rotated[4]; - int rotate_count = 0; - - memset(latency, 0, sizeof(latency)); - memset(asic_latency, 0, sizeof(asic_latency)); - memset(alu_busy, 0, sizeof(alu_busy)); - memset(is_rotation, 0, sizeof(is_rotation)); - memset(rotated, 0, sizeof(rotated)); - is_rotation[ROR] = true; - is_rotation[ROL] = true; - - int num_retries = 0; - code_size = 0; - - int total_iterations = 0; - r8_used = (variant == POW_WOW); - - // Generate random code to achieve minimal required latency for our abstract CPU - // Try to get this latency for all 4 registers - while (((latency[0] < TOTAL_LATENCY) || (latency[1] < TOTAL_LATENCY) || (latency[2] < TOTAL_LATENCY) || (latency[3] < TOTAL_LATENCY)) && (num_retries < 64)) - { - // Fail-safe to guarantee loop termination - ++total_iterations; - if (total_iterations > 256) - break; - - check_data(&data_index, 1, data, sizeof(data)); - - const uint8_t c = ((uint8_t*)data)[data_index++]; - - // MUL = opcodes 0-2 - // ADD = opcode 3 - // SUB = opcode 4 - // ROR/ROL = opcode 5, shift direction is selected randomly - // XOR = opcodes 6-7 - uint8_t opcode = c & ((1 << V4_OPCODE_BITS) - 1); - if (opcode == 5) - { - check_data(&data_index, 1, data, sizeof(data)); - opcode = (data[data_index++] >= 0) ? ROR : ROL; - } - else if (opcode >= 6) - { - opcode = XOR; - } - else - { - opcode = (opcode <= 2) ? MUL : (opcode - 2); - } - - uint8_t dst_index = (c >> V4_OPCODE_BITS) & ((1 << V4_DST_INDEX_BITS) - 1); - uint8_t src_index = (c >> (V4_OPCODE_BITS + V4_DST_INDEX_BITS)) & ((1 << V4_SRC_INDEX_BITS) - 1); - - const int a = dst_index; - int b = src_index; - - // Don't do ADD/SUB/XOR with the same register - if (((opcode == ADD) || (opcode == SUB) || (opcode == XOR)) && (a == b)) - { - // a is always < 4, so we don't need to check bounds here - b = (variant == POW_WOW) ? (a + 4) : 8; - src_index = b; - } - - // Don't do rotation with the same destination twice because it's equal to a single rotation - if (is_rotation[opcode] && rotated[a]) - { - continue; - } - - // Don't do the same instruction (except MUL) with the same source value twice because all other cases can be optimized: - // 2xADD(a, b, C) = ADD(a, b*2, C1+C2), same for SUB and rotations - // 2xXOR(a, b) = NOP - if ((opcode != MUL) && ((inst_data[a] & 0xFFFF00) == (opcode << 8) + ((inst_data[b] & 255) << 16))) - { - continue; - } - - // Find which ALU is available (and when) for this instruction - int next_latency = (latency[a] > latency[b]) ? latency[a] : latency[b]; - int alu_index = -1; - while (next_latency < TOTAL_LATENCY) - { - for (int i = op_ALUs[opcode] - 1; i >= 0; --i) - { - if (!alu_busy[next_latency][i]) - { - // ADD is implemented as two 1-cycle instructions on a real CPU, so do an additional availability check - if ((opcode == ADD) && alu_busy[next_latency + 1][i]) - { - continue; - } - - // Rotation can only start when previous rotation is finished, so do an additional availability check - if (is_rotation[opcode] && (next_latency < rotate_count * op_latency[opcode])) - { - continue; - } - - alu_index = i; - break; - } - } - if (alu_index >= 0) - { - break; - } - ++next_latency; - } - - // Don't generate instructions that leave some register unchanged for more than 7 cycles - if (next_latency > latency[a] + 7) - { - continue; - } - - next_latency += op_latency[opcode]; - - if (next_latency <= TOTAL_LATENCY) - { - if (is_rotation[opcode]) - { - ++rotate_count; - } - - // Mark ALU as busy only for the first cycle when it starts executing the instruction because ALUs are fully pipelined - alu_busy[next_latency - op_latency[opcode]][alu_index] = true; - latency[a] = next_latency; - - // ASIC is supposed to have enough ALUs to run as many independent instructions per cycle as possible, so latency calculation for ASIC is simple - asic_latency[a] = ((asic_latency[a] > asic_latency[b]) ? asic_latency[a] : asic_latency[b]) + asic_op_latency[opcode]; - - rotated[a] = is_rotation[opcode]; - - inst_data[a] = code_size + (opcode << 8) + ((inst_data[b] & 255) << 16); - - code[code_size].opcode = opcode; - code[code_size].dst_index = dst_index; - code[code_size].src_index = src_index; - code[code_size].C = 0; - - if (src_index == 8) - { - r8_used = true; - } - - if (opcode == ADD) - { - // ADD instruction is implemented as two 1-cycle instructions on a real CPU, so mark ALU as busy for the next cycle too - alu_busy[next_latency - op_latency[opcode] + 1][alu_index] = true; - - // ADD instruction requires 4 more random bytes for 32-bit constant "C" in "a = a + b + C" - check_data(&data_index, sizeof(uint32_t), data, sizeof(data)); - uint32_t t; - memcpy(&t, data + data_index, sizeof(uint32_t)); - code[code_size].C = SWAP32LE(t); - data_index += sizeof(uint32_t); - } - - ++code_size; - if (code_size >= NUM_INSTRUCTIONS_MIN) - { - break; - } - } - else - { - ++num_retries; - } - } - - // ASIC has more execution resources and can extract as much parallelism from the code as possible - // We need to add a few more MUL and ROR instructions to achieve minimal required latency for ASIC - // Get this latency for at least 1 of the 4 registers - const int prev_code_size = code_size; - while ((code_size < NUM_INSTRUCTIONS_MAX) && (asic_latency[0] < TOTAL_LATENCY) && (asic_latency[1] < TOTAL_LATENCY) && (asic_latency[2] < TOTAL_LATENCY) && (asic_latency[3] < TOTAL_LATENCY)) - { - int min_idx = 0; - int max_idx = 0; - for (int i = 1; i < 4; ++i) - { - if (asic_latency[i] < asic_latency[min_idx]) min_idx = i; - if (asic_latency[i] > asic_latency[max_idx]) max_idx = i; - } - - const uint8_t pattern[3] = { ROR, MUL, MUL }; - const uint8_t opcode = pattern[(code_size - prev_code_size) % 3]; - latency[min_idx] = latency[max_idx] + op_latency[opcode]; - asic_latency[min_idx] = asic_latency[max_idx] + asic_op_latency[opcode]; - - code[code_size].opcode = opcode; - code[code_size].dst_index = min_idx; - code[code_size].src_index = max_idx; - code[code_size].C = 0; - ++code_size; - } - - // There is ~98.15% chance that loop condition is false, so this loop will execute only 1 iteration most of the time - // It never does more than 4 iterations for all block heights < 10,000,000 - } while (!r8_used || (code_size < NUM_INSTRUCTIONS_MIN) || (code_size > NUM_INSTRUCTIONS_MAX)); - - // It's guaranteed that NUM_INSTRUCTIONS_MIN <= code_size <= NUM_INSTRUCTIONS_MAX here - // Add final instruction to stop the interpreter - code[code_size].opcode = RET; - code[code_size].dst_index = 0; - code[code_size].src_index = 0; - code[code_size].C = 0; - - return code_size; -} - -#endif \ No newline at end of file diff --git a/src/default_miner_config.json b/src/default_miner_config.json deleted file mode 100644 index 33ffb5d8..00000000 --- a/src/default_miner_config.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "algo": "cryptonight", - "aesni": 0, - "threads": 0, - "multihash-factor": 0, - "multihash-thread-mask" : null, - "pow-variant" : "auto", - "asm-optimization" : "auto", - "background": false, - "colors": true, - "cpu-affinity": null, - "cpu-priority": null, - "donate-level": 5, - "log-file": null, - "max-cpu-usage": 100, - "print-time": 60, - "retries": 5, - "retry-pause": 5, - "safe": false, - "syslog": false, - "reboot-cmd" : "", - "force-pow-variant" : false, - "skip-self-check" : false, - "pools": [ - { - "url": "donate2.graef.in:80", - "user": "YOUR_WALLET_ID", - "pass": "x", - "use-tls" : false, - "keepalive": true, - "nicehash": false - } - ], - "cc-client": { - "url": "localhost:3344", - "use-tls" : false, - "access-token": "mySecret", - "worker-id": null, - "update-interval-s": 10, - "use-remote-logging" : true, - "upload-config-on-startup" : true - } -} diff --git a/src/donate.h b/src/donate.h index 1527a759..46f26b73 100644 --- a/src/donate.h +++ b/src/donate.h @@ -5,9 +5,7 @@ * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee * Copyright 2017-2018 XMR-Stak , - * Copyright 2017-2018 XMRig - * Copyright 2017- BenDr0id - * + * Copyright 2016-2018 XMRig , * * 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 @@ -31,23 +29,22 @@ * Dev donation. * * Percentage of your hashing power that you want to donate to the developer, can be 0 if you don't want to do that. - * Example of how it works for the default setting of 1: - * You miner will mine into your usual pool for 99 minutes, then switch to the developer's pool for 1 minute. - * Since v1.5.2 start time randomized in range from 30 to 60 minutes minus donation time, to reduce peaks on donation - * pool when restarting a bunch of miners. + * + * Example of how it works for the setting of 1%: + * You miner will mine into your usual pool for random time (in range from 49.5 to 148.5 minutes), + * then switch to the developer's pool for 1 minute, then switch again to your pool for 99 minutes + * and then switch agaiin to developer's pool for 1 minute, these rounds will continue until miner working. + * + * Randomised only first round, to prevent waves on the donation pool. + * * Switching is instant, and only happens after a successful connection, so you never loose any hashes. * * If you plan on changing this setting to 0 please consider making a one off donation to my wallet: - * XMR: 4BEn3sSa2SsHBcwa9dNdKnGvvbyHPABr2JzoY7omn7DA2hPv84pVFvwDrcwMCWgz3dQVcrkw3gE9aTC9Mi5HxzkfF9ev1eH - * BTC: 128qLZCaGdoWhBTfaS7rytpbvG4mNTyAQm - * AEON: Wmtm4S2cQ8uEBBAVjvbiaVAPv2d6gA1mAUmBmjna4VF7VixLxLRUYag5cvsym3WnuzdJ9zvhQ3Xwa8gWxPDPRfcQ3AUkYra3W - * - * XMRig's wallet: * XMR: 48edfHu7V9Z84YzzMa6fUueoELZ9ZRXq9VetWzYGzKt52XU5xvqgzYnDK9URnRoJMk1j8nLwEVsaSWJ4fhdUyZijBGUicoD * BTC: 1P7ujsXeX7GxQwHNnJsRMgAdNkFZmNVqJT - * */ -constexpr const int kDonateLevel = 5; +constexpr const int kDefaultDonateLevel = 5; +constexpr const int kMinimumDonateLevel = 1; #endif /* __DONATE_H__ */ diff --git a/src/log/ConsoleLog.cpp b/src/log/ConsoleLog.cpp deleted file mode 100644 index 277cc30e..00000000 --- a/src/log/ConsoleLog.cpp +++ /dev/null @@ -1,156 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * - * - * 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 . - */ - - -#include -#include -#include -#include -#include - -#include "Options.h" - -#ifdef WIN32 -# include -# include -#endif - - -#include "log/ConsoleLog.h" -#include "log/Log.h" -#include "Options.h" - - -ConsoleLog::ConsoleLog(bool colors) : - m_colors(colors), - m_stream(nullptr) -{ - if (uv_tty_init(uv_default_loop(), &m_tty, 1, 0) < 0) { - Options::i()->setColors(false); - m_colors = false; - return; - } - - uv_tty_set_mode(&m_tty, UV_TTY_MODE_NORMAL); - m_uvBuf.base = m_buf; - m_stream = reinterpret_cast(&m_tty); - -# ifdef WIN32 - HANDLE handle = GetStdHandle(STD_INPUT_HANDLE); - if (handle != INVALID_HANDLE_VALUE) { - DWORD mode = 0; - if (GetConsoleMode(handle, &mode)) { - mode &= ~ENABLE_QUICK_EDIT_MODE; - SetConsoleMode(handle, mode | ENABLE_EXTENDED_FLAGS); - } - } -# endif -} - - -void ConsoleLog::message(int level, const char* fmt, va_list args) -{ - time_t now = time(nullptr); - tm stime; - -# ifdef _WIN32 - localtime_s(&stime, &now); -# else - localtime_r(&now, &stime); -# endif - - const char* color = nullptr; - if (m_colors) { - switch (level) { - case Log::ERR: - color = Log::kCL_RED; - break; - - case Log::WARNING: - color = Log::kCL_YELLOW; - break; - - case Log::NOTICE: - color = Log::kCL_WHITE; - break; - - case Log::DEBUG: - color = Log::kCL_GRAY; - break; - - default: - color = ""; - break; - } - } - - snprintf(m_fmt, sizeof(m_fmt) - 1, "[%d-%02d-%02d %02d:%02d:%02d]%s %s%s\n", - stime.tm_year + 1900, - stime.tm_mon + 1, - stime.tm_mday, - stime.tm_hour, - stime.tm_min, - stime.tm_sec, - m_colors ? color : "", - fmt, - m_colors ? Log::kCL_N : "" - ); - - print(args); -} - - -void ConsoleLog::text(const char* fmt, va_list args) -{ - snprintf(m_fmt, sizeof(m_fmt) - 1, "%s%s\n", fmt, m_colors ? Log::kCL_N : ""); - - print(args); -} - - -bool ConsoleLog::isWritable() const -{ - if (!m_stream || uv_is_writable(m_stream) != 1) { - return false; - } - - const uv_handle_type type = uv_guess_handle(1); - return type == UV_TTY || type == UV_NAMED_PIPE; -} - - -void ConsoleLog::print(va_list args) -{ - m_uvBuf.len = vsnprintf(m_buf, sizeof(m_buf) - 1, m_fmt, args); - if (m_uvBuf.len <= 0) { - return; - } - - if (!isWritable()) { - fputs(m_buf, stdout); - fflush(stdout); - } - else { - uv_try_write(m_stream, &m_uvBuf, 1); - } -} diff --git a/src/log/Log.cpp b/src/log/Log.cpp deleted file mode 100644 index 906ddf17..00000000 --- a/src/log/Log.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * - * - * 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 . - */ - - -#include -#include -#include -#include -#include - - -#include "interfaces/ILogBackend.h" -#include "log/Log.h" - - -Log *Log::m_self = nullptr; - -Log::Log() -{ - uv_mutex_init(&m_mutex); -} - -Log::~Log() -{ - for (auto backend : m_backends) { - delete backend; - } - - uv_mutex_destroy(&m_mutex); -} - -void Log::message(Log::Level level, const char* fmt, ...) -{ - uv_mutex_lock(&m_mutex); - - va_list args; - va_list copy; - va_start(args, fmt); - - for (ILogBackend *backend : m_backends) { - va_copy(copy, args); - backend->message(level, fmt, copy); - va_end(copy); - } - - va_end(args); - - uv_mutex_unlock(&m_mutex); -} - - -void Log::text(const char* fmt, ...) -{ - uv_mutex_lock(&m_mutex); - - va_list args; - va_list copy; - va_start(args, fmt); - - for (ILogBackend *backend : m_backends) { - va_copy(copy, args); - backend->text(fmt, copy); - va_end(copy); - } - - va_end(args); - - uv_mutex_unlock(&m_mutex); -} diff --git a/src/log/Log.h b/src/log/Log.h deleted file mode 100644 index 53564394..00000000 --- a/src/log/Log.h +++ /dev/null @@ -1,108 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * - * - * 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 . - */ - -#ifndef __LOG_H__ -#define __LOG_H__ - - -#include -#include - - -class ILogBackend; - - -class Log -{ -public: - enum Level { - ERR, - WARNING, - NOTICE, - INFO, - DEBUG - }; - - constexpr static const char* kCL_N = "\x1B[0m"; - constexpr static const char* kCL_RED = "\x1B[31m"; - constexpr static const char* kCL_YELLOW = "\x1B[33m"; - constexpr static const char* kCL_WHITE = "\x1B[01;37m"; - -# ifdef WIN32 - constexpr static const char* kCL_GRAY = "\x1B[01;30m"; -# else - constexpr static const char* kCL_GRAY = "\x1B[90m"; -# endif - - static inline Log* i() { return m_self; } - static inline void add(ILogBackend *backend) { i()->m_backends.push_back(backend); } - static inline void init() { if (!m_self) { m_self = new Log();} } - static inline void release() { delete m_self; m_self = nullptr; } - - void message(Level level, const char* fmt, ...); - void text(const char* fmt, ...); - -private: - Log(); - ~Log(); - - static Log *m_self; - - uv_mutex_t m_mutex; - std::vector m_backends; -}; - - -#define RED_BOLD(x) "\x1B[1;31m" x "\x1B[0m" -#define RED(x) "\x1B[0;31m" x "\x1B[0m" -#define GREEN_BOLD(x) "\x1B[1;32m" x "\x1B[0m" -#define GREEN(x) "\x1B[0;32m" x "\x1B[0m" -#define MAGENTA_BOLD(x) "\x1B[1;35m" x "\x1B[0m" -#define MAGENTA(x) "\x1B[0;35m" x "\x1B[0m" -#define CYAN_BOLD(x) "\x1B[1;36m" x "\x1B[0m" -#define CYAN(x) "\x1B[0;36m" x "\x1B[0m" -#define WHITE_BOLD(x) "\x1B[1;37m" x "\x1B[0m" -#define WHITE(x) "\x1B[0;37m" x "\x1B[0m" -#define YELLOW_BOLD(x) "\x1B[1;33m" x "\x1B[0m" -#define YELLOW(x) "\x1B[0;33m" x "\x1B[0m" - -#define LOG_ERR(x, ...) Log::i()->message(Log::ERR, x, ##__VA_ARGS__) -#define LOG_WARN(x, ...) Log::i()->message(Log::WARNING, x, ##__VA_ARGS__) -#define LOG_NOTICE(x, ...) Log::i()->message(Log::NOTICE, x, ##__VA_ARGS__) -#define LOG_INFO(x, ...) Log::i()->message(Log::INFO, x, ##__VA_ARGS__) - -#ifdef APP_DEBUG -# define LOG_DEBUG(x, ...) Log::i()->message(Log::DEBUG, x, ##__VA_ARGS__) -#else -# define LOG_DEBUG(x, ...) -#endif - -#if defined(APP_DEBUG) || defined(APP_DEVEL) -# define LOG_DEBUG_ERR(x, ...) Log::i()->message(Log::ERR, x, ##__VA_ARGS__) -# define LOG_DEBUG_WARN(x, ...) Log::i()->message(Log::WARNING, x, ##__VA_ARGS__) -#else -# define LOG_DEBUG_ERR(x, ...) -# define LOG_DEBUG_WARN(x, ...) -#endif - -#endif /* __LOG_H__ */ diff --git a/src/log/RemoteLog.cpp b/src/log/RemoteLog.cpp deleted file mode 100644 index 5d405c1f..00000000 --- a/src/log/RemoteLog.cpp +++ /dev/null @@ -1,103 +0,0 @@ -/* XMRig - * Copyright 2018- BenDr0id - * - * 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 . - */ - - -#include -#include -#include "log/RemoteLog.h" - -RemoteLog* RemoteLog::m_self = nullptr; -static const std::regex COLOR_PATTERN("\x1B\\[[0-9;]*[a-zA-Z]"); - -RemoteLog::RemoteLog(size_t maxRows) - : m_maxRows(maxRows) -{ - uv_mutex_init(&m_mutex); - - m_self = this; -} - -RemoteLog::~RemoteLog() -{ - m_self = nullptr; - - uv_mutex_destroy(&m_mutex); -} - -void RemoteLog::message(int level, const char* fmt, va_list args) -{ - time_t now = time(nullptr); - tm stime; - -# ifdef _WIN32 - localtime_s(&stime, &now); -# else - localtime_r(&now, &stime); -# endif - - auto *buf = new char[512]; - int size = snprintf(buf, 23, "[%d-%02d-%02d %02d:%02d:%02d] ", - stime.tm_year + 1900, - stime.tm_mon + 1, - stime.tm_mday, - stime.tm_hour, - stime.tm_min, - stime.tm_sec); - - size = vsnprintf(buf + size, static_cast(512 - size - 1), fmt, args) + size; - buf[size] = '\n'; - - std::string coloredLogLine(buf, static_cast(size + 1)); - std::string logLine = std::regex_replace(coloredLogLine, COLOR_PATTERN, ""); - - uv_mutex_lock(&m_mutex); - - if (m_rows.size() == m_maxRows) { - m_rows.pop_front(); - } - - m_rows.push_back(logLine); - - uv_mutex_unlock(&m_mutex); - - delete[](buf); -} - - -void RemoteLog::text(const char* fmt, va_list args) -{ - message(0, fmt, args); -} - -std::string RemoteLog::getRows() -{ - std::stringstream data; - - if (m_self) { - uv_mutex_lock(&m_self->m_mutex); - - for (auto& m_row : m_self->m_rows) { - data << m_row.c_str(); - } - - m_self->m_rows.clear(); - - uv_mutex_unlock(&m_self->m_mutex); - } - - return data.str(); -} diff --git a/src/net/BoostConnection.h b/src/net/BoostConnection.h deleted file mode 100644 index 058ee71f..00000000 --- a/src/net/BoostConnection.h +++ /dev/null @@ -1,164 +0,0 @@ -/* XMRigCC - * Copyright 2018 Sebastian Stolzenberg - * Copyright 2018- BenDr0id - * - * - * 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 . - */ - -#ifndef __BOOSTCONNECTION_H__ -#define __BOOSTCONNECTION_H__ - -#include "net/Connection.h" -#include "log/Log.h" - -template -class BoostConnection : public Connection, public std::enable_shared_from_this > -{ -public: - BoostConnection(const ConnectionListener::Ptr& listener) - : Connection(listener) - , m_resolver(m_ioService) - , m_socket(m_ioService) - { - } - - ~BoostConnection() - { - disconnect(); - } - - void connect(const std::string& server, uint16_t port) override - { - LOG_DEBUG("[%s:%d] Connecting", server.c_str(), port); - - boost::asio::ip::tcp::resolver::query query(server, std::to_string(port)); - - m_resolver.async_resolve(query, - boost::bind(&BoostConnection::handleResolve, this->shared_from_this(), - boost::asio::placeholders::error, - boost::asio::placeholders::iterator)); - - std::thread([this]() { m_ioService.run(); }).detach(); - } - - void handleResolve(const boost::system::error_code& error, - boost::asio::ip::tcp::resolver::iterator endpointIterator) - { - if (!error) { - #ifdef APP_DEBUG - boost::asio::ip::tcp::endpoint endpoint = *endpointIterator; - #endif - - LOG_DEBUG("[%s:%d] DNS resolved ", endpoint.address().to_string().c_str(), endpoint.port()); - m_socket.connect(endpointIterator, boost::bind(&BoostConnection::handleConnect, this->shared_from_this(), - boost::asio::placeholders::error)); - } else { - notifyDNSError(std::string("[DNS resolve] ") + error.message()); - } - } - - void handleConnect(const boost::system::error_code& error) - { - if (!error) { - startReading(); - LOG_DEBUG("[%s:%d] Connected", getConnectedIp().c_str(), getConnectedPort()); - notifyConnected(); - } else { - notifyError(std::string("[Connect] ") + error.message()); - } - } - - void disconnect() override - { - if (isConnected()) { - LOG_DEBUG("[%s:%d] Disconnecting", getConnectedIp().c_str(), getConnectedPort()); - - boost::system::error_code ec; - m_socket.get().lowest_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); - m_socket.get().lowest_layer().close(); - } - - m_ioService.stop(); - m_ioService.reset(); - } - - bool isConnected() const override - { - boost::system::error_code ec; - m_socket.get().lowest_layer().remote_endpoint(ec); - return !ec && m_socket.get().lowest_layer().is_open(); - } - - std::string getConnectedIp() const override - { - return isConnected() ? m_socket.get().lowest_layer().remote_endpoint().address().to_string() : ""; - } - - uint16_t getConnectedPort() const override - { - return isConnected() ? m_socket.get().lowest_layer().remote_endpoint().port() : 0; - } - - void send(const char* data, std::size_t size) override - { - LOG_DEBUG("[%s:%d] Sending: %.*s", getConnectedIp().c_str(), getConnectedPort(), size, data); - - boost::asio::async_write(m_socket.get(), - boost::asio::buffer(data, size), - boost::bind(&BoostConnection::handleWrite, this->shared_from_this(), - boost::asio::placeholders::error, - boost::asio::placeholders::bytes_transferred)); - } - - void handleWrite(const boost::system::error_code& error, - size_t bytes_transferred) - { - if (error) { - LOG_DEBUG_ERR("[%s:%d] Sending failed: %s", getConnectedIp().c_str(), getConnectedPort(), error.message().c_str()); - notifyError(std::string("[Send] ") + error.message()); - } - } - - void startReading() - { - boost::asio::async_read(m_socket.get(), - boost::asio::buffer(receiveBuffer_, sizeof(receiveBuffer_)), - boost::asio::transfer_at_least(1), - boost::bind(&BoostConnection::handleRead, this->shared_from_this(), - boost::asio::placeholders::error, - boost::asio::placeholders::bytes_transferred)); - } - - void handleRead(const boost::system::error_code& error, - size_t bytes_transferred) - { - if (!error) { - LOG_DEBUG("[%s:%d] Read: %.*s", getConnectedIp().c_str(), getConnectedPort(), bytes_transferred, receiveBuffer_); - notifyRead(receiveBuffer_, bytes_transferred); - startReading(); - } else { - LOG_DEBUG_ERR("[%s:%d] Read failed: %s", getConnectedIp().c_str(), getConnectedPort(), error.message().c_str()); - notifyError(std::string("[Read] ") + error.message()); - } - } - -private: - boost::asio::io_service m_ioService; - boost::asio::ip::tcp::resolver m_resolver; - SOCKET m_socket; - char receiveBuffer_[2048]; -}; - -#endif /* __BOOSTCONNECTION_H__ */ diff --git a/src/net/BoostTcpConnection.cpp b/src/net/BoostTcpConnection.cpp deleted file mode 100644 index 9309a37a..00000000 --- a/src/net/BoostTcpConnection.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* XMRigCC - * Copyright 2018 Sebastian Stolzenberg - * - * - * 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 . - */ - -#include -#include - -#include -#include - -#include "net/BoostConnection.h" -#include "net/BoostTcpConnection.h" - -class BoostTcpSocket -{ -public: - typedef boost::asio::ip::tcp::socket SocketType; -public: - BoostTcpSocket(boost::asio::io_service& ioService) - : socket_(ioService) - - { - } - - template - void connect(ITERATOR& iterator, HANDLER handler) - { - boost::asio::async_connect( - socket_, iterator, - [this, handler](const boost::system::error_code& ec, const ITERATOR& iterator) - { - if (!ec) { - socket_.set_option(boost::asio::ip::tcp::no_delay(true)); - socket_.set_option(boost::asio::socket_base::keep_alive(true)); - } - handler(ec); - } - ); - } - - SocketType& get() - { - return socket_; - } - - const SocketType& get() const - { - return socket_; - } - -private: - SocketType socket_; -}; - -Connection::Ptr establishBoostTcpConnection(const ConnectionListener::Ptr& listener) -{ - return std::make_shared >(listener); -} \ No newline at end of file diff --git a/src/net/BoostTlsConnection.cpp b/src/net/BoostTlsConnection.cpp deleted file mode 100644 index 81a940cf..00000000 --- a/src/net/BoostTlsConnection.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/* XMRigCC - * Copyright 2018 Sebastian Stolzenberg - * - * - * 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 . - */ - -#include -#include - -#include -#include -#include - -#include "net/BoostConnection.h" -#include "net/BoostTlsConnection.h" - -class BoostTlsSocket -{ -public: - typedef boost::asio::ssl::stream SocketType; -public: - BoostTlsSocket(boost::asio::io_service& ioService) - : sslContext_(boost::asio::ssl::context::sslv23_client) - , socket_(ioService, sslContext_) - - { - socket_.set_verify_mode(boost::asio::ssl::verify_none); - } - - template - void connect(ITERATOR& iterator, HANDLER handler) - { - boost::asio::async_connect( - socket_.lowest_layer(), iterator, - [this, handler](const boost::system::error_code& ec, const ITERATOR& iterator) - { - if (!ec) { - socket_.lowest_layer().set_option(boost::asio::ip::tcp::no_delay(true)); - socket_.lowest_layer().set_option(boost::asio::socket_base::keep_alive(true)); - socket_.async_handshake( - boost::asio::ssl::stream_base::client, - [handler](const boost::system::error_code& ec) { - handler(ec); - }); - } else { - handler(ec); - } - } - ); - } - - SocketType& get() - { - return socket_; - } - - const SocketType& get() const - { - return socket_; - } - - -private: - boost::asio::ssl::context sslContext_; - SocketType socket_; -}; - -Connection::Ptr establishBoostTlsConnection(const ConnectionListener::Ptr& listener) -{ - return std::make_shared >(listener); -} \ No newline at end of file diff --git a/src/net/Client.cpp b/src/net/Client.cpp deleted file mode 100644 index 4a03780b..00000000 --- a/src/net/Client.cpp +++ /dev/null @@ -1,699 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * Copyright 2018 Sebastian Stolzenberg - * Copyright 2018- BenDr0id - * - * - * 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 . - */ - -#include -#include -#include -#include -#include -#include - -#include "PowVariant.h" - -#include "interfaces/IClientListener.h" -#include "log/Log.h" -#include "net/Client.h" -#include "net/JobResult.h" -#include "rapidjson/document.h" -#include "rapidjson/error/en.h" -#include "rapidjson/stringbuffer.h" -#include "rapidjson/writer.h" - -#ifdef _MSC_VER -# define strncasecmp(x,y,z) _strnicmp(x,y,z) -#endif - - -int64_t Client::m_sequence = 1; - - -Client::Client(int id, const char *agent, IClientListener *listener) : - m_quiet(false), - m_nicehash(false), - m_donate(false), - m_agent(agent), - m_listener(listener), - m_id(id), - m_retryPause(5000), - m_failures(0), - m_jobs(0), - m_recvBufPos(0), - m_expire(0) -{ - m_recvBuf.base = m_buf; - m_recvBuf.len = sizeof(m_buf); - - m_keepAliveTimer.data = this; - - uv_timer_init(uv_default_loop(), &m_keepAliveTimer); - - uv_mutex_init(&m_mutex); - - uv_async_init(uv_default_loop(), &onConnectedAsync, Client::onConnected); - uv_async_init(uv_default_loop(), &onReceivedAsync, Client::onReceived); - uv_async_init(uv_default_loop(), &onErrorAsync, Client::onError); - uv_async_init(uv_default_loop(), &onDNSErrorAsync, Client::onDNSError); -} - - -Client::~Client() -{ - uv_close((uv_handle_t*) &onConnectedAsync, NULL); - uv_close((uv_handle_t*) &onReceivedAsync, NULL); - uv_close((uv_handle_t*) &onErrorAsync, NULL); - - uv_mutex_destroy(&m_mutex); -} - -void Client::connect(const Url *url) -{ - LOG_DEBUG("connect %s", url); - - setUrl(url); - connect(); -} - - -void Client::connect() -{ - LOG_DEBUG("[%d] connect", m_id); - - m_connection = establishConnection(shared_from_this(), - m_url.useTls() ? CONNECTION_TYPE_TLS : CONNECTION_TYPE_TCP, - m_url.host(), m_url.port()); -} - - -void Client::disconnect() -{ - LOG_DEBUG("[%d] disconnect", m_id); - - uv_timer_stop(&m_keepAliveTimer); - - m_expire = 0; - m_failures = -1; - - LOG_DEBUG("[%d] disconnect set m_failure to -1", m_id); - - close(); -} - - -void Client::setUrl(const Url *url) -{ - LOG_DEBUG("setUrl"); - - if (!url || !url->isValid()) { - return; - } - - m_url = url; -} - - -void Client::tick(uint64_t now) -{ - if (m_expire == 0 || now < m_expire) { - return; - } - - LOG_DEBUG("tick expired"); - - if (m_connection) { - LOG_WARN("[%s:%u] timeout", m_url.host(), m_url.port()); - LOG_DEBUG("tick -> reconnect"); - reconnect(); - } - else { - LOG_DEBUG("tick -> connect"); - connect(); - } -} - - -int64_t Client::submit(const JobResult &result) -{ - char nonce[9]; - char data[65]; - - Job::toHex(reinterpret_cast(&result.nonce), 4, nonce); - nonce[8] = '\0'; - - Job::toHex(result.result, 32, data); - data[64] = '\0'; - - const int size = snprintf(m_sendBuf, sizeof(m_sendBuf), - "{" - "\"id\":%" PRIu64 "," - "\"jsonrpc\":\"2.0\"," - "\"method\":\"submit\"," - "\"params\":" - "{" - "\"id\":\"%s\"," - "\"job_id\":\"%s\"," - "\"nonce\":\"%s\"," - "\"result\":\"%s\"" - "}" - "}\n", - m_sequence, m_rpcId, result.jobId.data(), nonce, data); - - m_results[m_sequence] = SubmitResult(m_sequence, result.diff, result.actualDiff()); - return send(m_sendBuf, size); -} - - -bool Client::isCriticalError(const char *message) -{ - if (!message) { - return false; - } - - if (strncasecmp(message, "Unauthenticated", 15) == 0) { - return true; - } - - if (strncasecmp(message, "your IP is banned", 17) == 0) { - return true; - } - - if (strncasecmp(message, "IP Address currently banned", 27) == 0) { - return true; - } - - return false; -} - - -bool Client::parseJob(const rapidjson::Value ¶ms, int *code) -{ - if (!params.IsObject()) { - *code = 2; - return false; - } - - Job job(m_id, m_nicehash); - - if (!job.setId(params["job_id"].GetString())) { - *code = 3; - return false; - } - - if (!job.setBlob(params["blob"].GetString())) { - *code = 4; - return false; - } - - if (!job.setTarget(params["target"].GetString())) { - *code = 5; - return false; - } - - PowVariant powVariant = Options::i()->powVariant(); - - if (!Options::i()->forcePowVariant() || m_donate) { - if (params.HasMember("algo")) { - std::string algo = params["algo"].GetString(); - - if (algo.find("/") != std::string::npos) { - powVariant = parseVariant(algo.substr(algo.find("/") + 1)); - } - } - - if (params.HasMember("variant")) { - const rapidjson::Value& variant = params["variant"]; - - PowVariant parsedVariant = powVariant; - - if (variant.IsInt()) { - parsedVariant = parseVariant(variant.GetInt()); - } else if (variant.IsString()) { - parsedVariant = parseVariant(variant.GetString()); - } - - if (parsedVariant != POW_AUTODETECT) { - powVariant = parsedVariant; - } - } - } - - job.setPowVariant(powVariant); - - if (params.HasMember("height")) { - const rapidjson::Value &variant = params["height"]; - - if (variant.IsUint64()) { - job.setHeight(variant.GetUint64()); - } - } - - if (m_job != job) { - m_jobs++; - m_job = std::move(job); - return true; - } - - if (m_jobs == 0) { // https://github.com/xmrig/xmrig/issues/459 - return false; - } - - if (!m_quiet) { - LOG_WARN("[%s:%u] duplicate job received, reconnect", m_url.host(), m_url.port()); - } - - reconnect(); - return false; -} - - -void Client::parseExtensions(const rapidjson::Value &value) -{ - if (!value.IsArray()) { - return; - } - - for (const rapidjson::Value &ext : value.GetArray()) { - if (!ext.IsString()) { - continue; - } - - if (strcmp(ext.GetString(), "nicehash") == 0) { - m_nicehash = true; - } - } -} - - -bool Client::parseLogin(const rapidjson::Value &result, int *code) -{ - const char *id = result["id"].GetString(); - if (!id || strlen(id) >= sizeof(m_rpcId)) { - *code = 1; - return false; - } - - m_nicehash = m_url.isNicehash(); - - if (result.HasMember("extensions")) { - parseExtensions(result["extensions"]); - } - - memset(m_rpcId, 0, sizeof(m_rpcId)); - memcpy(m_rpcId, id, strlen(id)); - - const bool rc = parseJob(result["job"], code); - m_jobs = 0; - - return rc; -} - - -int64_t Client::send(char* buf, size_t size) -{ - if (m_connection) - { - m_connection->send(buf, size); - m_expire = uv_now(uv_default_loop()) + kResponseTimeout; - m_sequence++; - } - - return m_sequence; -} - - -void Client::close() -{ - LOG_DEBUG("[%d] close", m_id); - - if (m_connection) { - m_connection->disconnect(); - } - - m_connection.reset(); -} - - -void Client::login() -{ - LOG_DEBUG("login"); - - m_results.clear(); - - rapidjson::Document doc; - doc.SetObject(); - - auto &allocator = doc.GetAllocator(); - - doc.AddMember("id", 1, allocator); - doc.AddMember("jsonrpc", "2.0", allocator); - doc.AddMember("method", "login", allocator); - - rapidjson::Value params(rapidjson::kObjectType); - params.AddMember("login", rapidjson::StringRef(m_url.user()), allocator); - params.AddMember("pass", rapidjson::StringRef(m_url.password()), allocator); - params.AddMember("agent", rapidjson::StringRef(m_agent), allocator); - - params.AddMember("algo", rapidjson::StringRef(Options::i()->algoShortName()), allocator); - - rapidjson::Value supportedPowVariantsList(rapidjson::kArrayType); - for (auto& supportedPowVariant : getSupportedPowVariants()) { - rapidjson::Value val(supportedPowVariant.c_str(), allocator); - supportedPowVariantsList.PushBack(val, allocator); - } - - params.AddMember("supported-variants", supportedPowVariantsList, allocator); - - doc.AddMember("params", params, allocator); - - rapidjson::StringBuffer buffer(0, 512); - rapidjson::Writer writer(buffer); - doc.Accept(writer); - - const size_t size = buffer.GetSize(); - if (size > (sizeof(m_buf) - 2)) { - return; - } - - memcpy(m_sendBuf, buffer.GetString(), size); - m_sendBuf[size] = '\n'; - m_sendBuf[size + 1] = '\0'; - - send(m_sendBuf, size + 1); -} - -void Client::processReceivedData(char* data, size_t size) -{ - LOG_DEBUG("processReceivedData"); - - if ((size_t) size > (sizeof(m_buf) - 8 - m_recvBufPos)) { - reconnect(); - return; - } - - m_recvBufPos += size; - - char* end; - char* start = data; - size_t remaining = m_recvBufPos; - - while ((end = static_cast(memchr(start, '\n', remaining))) != nullptr) { - end++; - size_t len = end - start; - parse(start, len); - - remaining -= len; - start = end; - } - - if (remaining == 0) { - m_recvBufPos = 0; - return; - } - - if (start == data) { - return; - } - - memcpy(data, start, remaining); - m_recvBufPos = remaining; -} - -void Client::parse(char *line, size_t len) -{ - LOG_DEBUG("parse"); - - startTimeout(); - - line[len - 1] = '\0'; - - rapidjson::Document doc; - if (doc.ParseInsitu(line).HasParseError()) { - if (!m_quiet) { - LOG_ERR("[%s:%u] JSON decode failed: \"%s\"", m_url.host(), m_url.port(), rapidjson::GetParseError_En(doc.GetParseError())); - } - - return; - } - - if (!doc.IsObject()) { - return; - } - - const rapidjson::Value &id = doc["id"]; - if (id.IsInt64()) { - parseResponse(id.GetInt64(), doc["result"], doc["error"]); - } - else { - parseNotification(doc["method"].GetString(), doc["params"], doc["error"]); - } -} - - -void Client::parseNotification(const char *method, const rapidjson::Value ¶ms, const rapidjson::Value &error) -{ - if (error.IsObject()) { - if (!m_quiet) { - LOG_ERR("[%s:%u] Parse notification failed: \"%s\", code: %d", m_url.host(), m_url.port(), error["message"].GetString(), error["code"].GetInt()); - } - return; - } - - if (!method) { - return; - } - - if (strcmp(method, "job") == 0) { - int code = -1; - if (parseJob(params, &code)) { - m_listener->onJobReceived(this, m_job); - } - - return; - } - - LOG_WARN("[%s:%u] Unsupported method: \"%s\"", m_url.host(), m_url.port(), method); -} - - -void Client::parseResponse(int64_t id, const rapidjson::Value &result, const rapidjson::Value &error) -{ - if (error.IsObject()) { - const char *message = error["message"].GetString(); - - auto it = m_results.find(id); - if (it != m_results.end()) { - it->second.done(); - m_listener->onResultAccepted(this, it->second, message); - m_results.erase(it); - } - else if (!m_quiet) { - LOG_ERR("[%s:%u] Parse response failed: \"%s\", code: %d", m_url.host(), m_url.port(), message, error["code"].GetInt()); - } - - if (id == 1 || isCriticalError(message)) { - reconnect(); - } - - return; - } - - if (!result.IsObject()) { - return; - } - - if (id == 1) { - int code = -1; - if (!parseLogin(result, &code)) { - if (!m_quiet) { - LOG_ERR("[%s:%u] Login error code: %d", m_url.host(), m_url.port(), code); - } - - return reconnect(); - } - - LOG_DEBUG("[%d] login set m_failure to 0", m_id); - m_failures = 0; - - m_listener->onLoginSuccess(this); - m_listener->onJobReceived(this, m_job); - return; - } - - auto it = m_results.find(id); - if (it != m_results.end()) { - it->second.done(); - m_listener->onResultAccepted(this, it->second, nullptr); - m_results.erase(it); - } -} - - -void Client::ping() -{ - LOG_DEBUG("ping"); - - const int size = snprintf(m_sendBuf, sizeof(m_sendBuf), - "{" - "\"id\":%" PRId64 "," - "\"jsonrpc\":\"2.0\"," - "\"method\":\"keepalived\"," - "\"params\":" - "{" - "\"id\":\"%s\"" - "}" - "}\n", - m_sequence, m_rpcId); - send(m_sendBuf, size); -} - - -void Client::reconnect() -{ - LOG_DEBUG("[%d] reconnect", m_id); - - close(); - - if (m_url.isKeepAlive()) { - uv_timer_stop(&m_keepAliveTimer); - } - - if (m_failures == -1) { - LOG_DEBUG("[%d] reconnect -> m_failures == -1", m_id); - return m_listener->onClose(this, -1); - } - - m_failures++; - LOG_DEBUG("[%d] increment m_failures to: %d", m_id, m_failures); - m_listener->onClose(this, (int) m_failures); - - m_expire = uv_now(uv_default_loop()) + m_retryPause; -} - - -void Client::startTimeout() -{ - LOG_DEBUG("startTimeout"); - - m_expire = 0; - - if (!m_url.isKeepAlive()) { - return; - } - - uv_timer_start(&m_keepAliveTimer, [](uv_timer_t *handle) { getClient(handle->data)->ping(); }, kKeepAliveTimeout, 0); -} - -void Client::onConnected(uv_async_t *handle) -{ - LOG_DEBUG("onConnected"); - - auto client = getClient(handle->data); - if (client) { - client->login(); - } -} - -void Client::scheduleOnConnected() -{ - LOG_DEBUG("scheduleOnConnected"); - onConnectedAsync.data = this; - - uv_async_send(&onConnectedAsync); -} - -void Client::onReceived(uv_async_t *handle) -{ - LOG_DEBUG("onReceived"); - - auto client = getClient(handle->data); - if (client) { - uv_mutex_lock(&client->m_mutex); - - while (!client->m_readQueue.empty()) { - std::string data = client->m_readQueue.front(); - client->processReceivedData(const_cast(data.c_str()), data.size()); - client->m_readQueue.pop_front(); - } - - uv_mutex_unlock(&client->m_mutex); - } -} - -void Client::scheduleOnReceived(char* data, std::size_t size) -{ - LOG_DEBUG("scheduleOnReceived"); - - uv_mutex_lock(&m_mutex); - m_readQueue.emplace_back(data, size); - uv_mutex_unlock(&m_mutex); - - onReceivedAsync.data = this; - uv_async_send(&onReceivedAsync); -} - -void Client::onError(uv_async_t *handle) -{ - auto client = getClient(handle->data); - if (client) { - LOG_DEBUG("[%d] onError", client->m_id); - client->reconnect(); - } -} - -void Client::scheduleOnError(const std::string &error) -{ - LOG_DEBUG("scheduleOnError"); - - if (!m_quiet) { - LOG_ERR("[%s:%u] Error: \"%s\"", m_url.host(), m_url.port(), error.c_str()); - } - - onErrorAsync.data = this; - uv_async_send(&onErrorAsync); -} - -void Client::onDNSError(uv_async_t *handle) -{ - auto client = getClient(handle->data); - if (client) { - if (client->m_failures == -1) { - client->m_failures = 0; - } - - LOG_DEBUG("[%d] onDNSError", client->m_id); - client->reconnect(); - } -} - -void Client::scheduleOnDNSError(const std::string &error) -{ - LOG_DEBUG("scheduleOnDNSError"); - - if (!m_quiet) { - LOG_ERR("[%s:%u] DNS Error: \"%s\"", m_url.host(), m_url.port(), error.c_str()); - } - - onDNSErrorAsync.data = this; - uv_async_send(&onDNSErrorAsync); -} diff --git a/src/net/Client.h b/src/net/Client.h deleted file mode 100644 index a42fdb4e..00000000 --- a/src/net/Client.h +++ /dev/null @@ -1,136 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * Copyright 2018- BenDr0id - * - * - * 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 . - */ - -#ifndef __CLIENT_H__ -#define __CLIENT_H__ - - -#include -#include -#include -#include - -#include "net/Connection.h" -#include "net/Job.h" -#include "net/SubmitResult.h" -#include "net/Url.h" -#include "rapidjson/fwd.h" - - -class IClientListener; -class JobResult; - - -class Client : public ConnectionListener, - public std::enable_shared_from_this -{ -public: - typedef std::shared_ptr Ptr; - -public: - constexpr static int kResponseTimeout = 20 * 1000; - constexpr static int kKeepAliveTimeout = 60 * 1000; - - Client(int id, const char *agent, IClientListener *listener); - ~Client(); - - int64_t submit(const JobResult &result); - void connect(); - void connect(const Url *url); - void disconnect(); - void setUrl(const Url *url); - void tick(uint64_t now); - - inline const char *host() const { return m_url.host(); } - inline const Job &job() const { return m_job; } - inline int id() const { return m_id; } - inline uint16_t port() const { return m_url.port(); } - inline void setQuiet(bool quiet) { m_quiet = quiet; } - inline void setDonate(bool donate) { m_donate = donate; } - inline void setRetryPause(int ms) { m_retryPause = ms; } - - static void onConnected(uv_async_t *handle); - static void onReceived(uv_async_t *handle); - static void onError(uv_async_t *handle); - static void onDNSError(uv_async_t *handle); - -private: - bool isCriticalError(const char *message); - bool parseJob(const rapidjson::Value ¶ms, int *code); - bool parseLogin(const rapidjson::Value &result, int *code); - int64_t send(char* buf, size_t size); - void close(); - void reconnect(); - void login(); - void processReceivedData(char* data, size_t size); - void parse(char *line, size_t len); - void parseNotification(const char *method, const rapidjson::Value ¶ms, const rapidjson::Value &error); - void parseExtensions(const rapidjson::Value &value); - void parseResponse(int64_t id, const rapidjson::Value &result, const rapidjson::Value &error); - void ping(); - void startTimeout(); - - virtual void scheduleOnConnected(); - virtual void scheduleOnReceived(char *data, size_t size); - virtual void scheduleOnError(const std::string &error); - virtual void scheduleOnDNSError(const std::string &error); - - static inline Client *getClient(void *data) { return static_cast(data); } - - bool m_quiet; - bool m_nicehash; - bool m_donate; - char m_buf[2048]; - char m_rpcId[64]; - char m_sendBuf[768]; - const char *m_agent; - IClientListener *m_listener; - int m_id; - int m_retryPause; - int64_t m_failures; - uint64_t m_jobs; - Job m_job; - size_t m_recvBufPos; - static int64_t m_sequence; - std::map m_results; - uint64_t m_expire; - Url m_url; - uv_buf_t m_recvBuf; - - uv_mutex_t m_mutex; - std::list m_readQueue; - - Connection::Ptr m_connection; - - uv_async_t onConnectedAsync; - uv_async_t onReceivedAsync; - uv_async_t onErrorAsync; - uv_async_t onDNSErrorAsync; - - uv_timer_t m_keepAliveTimer; - -}; - - -#endif /* __CLIENT_H__ */ \ No newline at end of file diff --git a/src/net/Connection.cpp b/src/net/Connection.cpp deleted file mode 100644 index a114b295..00000000 --- a/src/net/Connection.cpp +++ /dev/null @@ -1,101 +0,0 @@ -/* XMRigCC - * Copyright 2018 Sebastian Stolzenberg - * Copyright 2018- BenDr0id - * - * - * 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 . - */ - -#include -#include -#include - -#include "Connection.h" -#include "BoostTcpConnection.h" - -#ifndef XMRIG_NO_TLS -#include "BoostTlsConnection.h" -#endif - -Connection::Connection(const ConnectionListener::Ptr &listener) - : listener_(listener) -{ -} - -void Connection::notifyConnected() -{ - ConnectionListener::Ptr listener = listener_.lock(); - if (listener) - { - listener->scheduleOnConnected(); - } -} - -void Connection::notifyRead(char* data, size_t size) -{ - ConnectionListener::Ptr listener = listener_.lock(); - if (listener) - { - listener->scheduleOnReceived(data, size); - } -} - -void Connection::notifyError(const std::string& error) -{ - ConnectionListener::Ptr listener = listener_.lock(); - if (listener) - { - listener->scheduleOnError(error); - } -} - -void Connection::notifyDNSError(const std::string& error) -{ - ConnectionListener::Ptr listener = listener_.lock(); - if (listener) - { - listener->scheduleOnDNSError(error); - } -} - -Connection::Ptr establishConnection(const ConnectionListener::Ptr& listener, - ConnectionType type, const std::string& host, uint16_t port) -{ - Connection::Ptr connection; - - try { - switch (type) { - case CONNECTION_TYPE_TLS: -#ifndef XMRIG_NO_TLS - connection = establishBoostTlsConnection(listener); - break; -#endif - case CONNECTION_TYPE_TCP: - connection = establishBoostTcpConnection(listener); - break; - } - - connection->connect(host, port); - } - catch (...) { - if (connection) { - connection->disconnect(); - } - - connection->notifyError(std::string("[EstablishConnection] ") + boost::current_exception_diagnostic_information()); - } - - - return connection; -} \ No newline at end of file diff --git a/src/net/Connection.h b/src/net/Connection.h deleted file mode 100644 index 5e1d1081..00000000 --- a/src/net/Connection.h +++ /dev/null @@ -1,81 +0,0 @@ -/* XMRig - * Copyright 2018 Sebastian Stolzenberg - * - * - * 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 . - */ - -#ifndef __CONNECTION_H__ -#define __CONNECTION_H__ - -#include -#include -#include - -#include - -class ConnectionListener -{ -public: - typedef std::shared_ptr Ptr; - typedef std::weak_ptr WeakPtr; - -protected: - ConnectionListener() {}; - virtual ~ConnectionListener() {}; - -public: - virtual void scheduleOnConnected() = 0; - virtual void scheduleOnReceived(char *data, std::size_t size) = 0; - virtual void scheduleOnError(const std::string &error) = 0; - virtual void scheduleOnDNSError(const std::string &error) = 0; -}; - -class Connection : private boost::noncopyable -{ -public: - typedef std::shared_ptr Ptr; - -protected: - Connection(const ConnectionListener::Ptr& listener); - virtual ~Connection() {}; - -public: - virtual void connect(const std::string& server, uint16_t port) = 0; - virtual void disconnect() = 0; - virtual bool isConnected() const = 0; - virtual std::string getConnectedIp() const = 0; - virtual uint16_t getConnectedPort() const = 0; - virtual void send(const char* data, std::size_t size) = 0; - - void notifyConnected(); - void notifyRead(char* data, size_t size); - void notifyError(const std::string& error); - void notifyDNSError(const std::string& error); - -private: - ConnectionListener::WeakPtr listener_; -}; - -enum ConnectionType -{ - CONNECTION_TYPE_TCP, - CONNECTION_TYPE_TLS -}; - -Connection::Ptr establishConnection(const ConnectionListener::Ptr& listener, - ConnectionType type, - const std::string& host, uint16_t port); - -#endif /* __CONNECTION_H__ */ \ No newline at end of file diff --git a/src/net/Job.cpp b/src/net/Job.cpp deleted file mode 100644 index eb2362e7..00000000 --- a/src/net/Job.cpp +++ /dev/null @@ -1,218 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * - * - * 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 . - */ - - -#include - - -#include "net/Job.h" - - -static inline unsigned char hf_hex2bin(char c, bool &err) -{ - if (c >= '0' && c <= '9') { - return c - '0'; - } - else if (c >= 'a' && c <= 'f') { - return c - 'a' + 0xA; - } - else if (c >= 'A' && c <= 'F') { - return c - 'A' + 0xA; - } - - err = true; - return 0; -} - - -static inline char hf_bin2hex(unsigned char c) -{ - if (c <= 0x9) { - return '0' + c; - } - - return 'a' - 0xA + c; -} - - -Job::Job(int poolId, bool nicehash) : - m_nicehash(nicehash), - m_poolId(poolId), - m_threadId(-1), - m_size(0), - m_diff(0), - m_target(0), - m_height(0), - m_powVariant(PowVariant::POW_AUTODETECT) -{ -} - - -Job::~Job() -{ -} - - -bool Job::setBlob(const char *blob) -{ - if (!blob) { - return false; - } - - m_size = strlen(blob); - if (m_size % 2 != 0) { - return false; - } - - m_size /= 2; - if (m_size < 76 || m_size >= sizeof(m_blob)) { - return false; - } - - if (!fromHex(blob, (int) m_size * 2, m_blob)) { - return false; - } - - if (*nonce() != 0 && !m_nicehash) { - m_nicehash = true; - } - - return true; -} - - -bool Job::setTarget(const char *target) -{ - if (!target) { - return false; - } - - const size_t len = strlen(target); - - if (len <= 8) { - uint32_t tmp = 0; - char str[8]; - memcpy(str, target, len); - - if (!fromHex(str, 8, reinterpret_cast(&tmp)) || tmp == 0) { - return false; - } - - m_target = 0xFFFFFFFFFFFFFFFFULL / (0xFFFFFFFFULL / static_cast(tmp)); - } - else if (len <= 16) { - m_target = 0; - char str[16]; - memcpy(str, target, len); - - if (!fromHex(str, 16, reinterpret_cast(&m_target)) || m_target == 0) { - return false; - } - } - else { - return false; - } - - m_diff = toDiff(m_target); - return true; -} - -PowVariant Job::powVariant() const -{ - if (Options::i()->algo() == Options::ALGO_CRYPTONIGHT_ULTRALITE) { - return PowVariant::POW_TURTLE; - } - - if (Options::i()->algo() == Options::ALGO_CRYPTONIGHT_EXTREMELITE) { - return PowVariant::POW_UPX2; - } - - if (m_powVariant == PowVariant::POW_AUTODETECT) { - if (m_blob[0] >= 10) { - return PowVariant::POW_V4; - } else if (m_blob[0] > 7) { - return PowVariant::POW_V2; - } else if (m_blob[0] > 6) { - return PowVariant::POW_V1; - } else { - return PowVariant::POW_V0; - } - } else if (m_powVariant == PowVariant::POW_XTL) { - if (!Options::i()->forcePowVariant()) { - if (m_blob[0] >= 9) { - return PowVariant::POW_FAST_2; - } - } - } else if (m_powVariant == PowVariant::POW_MSR) { - if (!Options::i()->forcePowVariant()) { - if (m_blob[0] >= 8) { - return PowVariant::POW_FAST_2; - } - } - } else if (m_powVariant == PowVariant::POW_RWZ) { - if (!Options::i()->forcePowVariant()) { - if (m_blob[0] < 12) { - return PowVariant::POW_V2; - } - } - } else if (m_powVariant == PowVariant::POW_ZELERIUS) { - if (!Options::i()->forcePowVariant()) { - if (m_blob[0] < 8) { - return PowVariant::POW_V2; - } - } - } - - return m_powVariant; -} - -bool Job::fromHex(const char* in, unsigned int len, unsigned char* out) -{ - bool error = false; - for (unsigned int i = 0; i < len; i += 2) { - out[i / 2] = (hf_hex2bin(in[i], error) << 4) | hf_hex2bin(in[i + 1], error); - - if (error) { - return false; - } - } - return true; -} - -void Job::toHex(const unsigned char* in, unsigned int len, char* out) -{ - for (unsigned int i = 0; i < len; i++) { - out[i * 2] = hf_bin2hex((in[i] & 0xF0) >> 4); - out[i * 2 + 1] = hf_bin2hex(in[i] & 0x0F); - } -} - -bool Job::operator==(const Job &other) const -{ - return m_id == other.m_id && memcmp(m_blob, other.m_blob, sizeof(m_blob)) == 0; -} - -bool Job::operator!=(const Job &other) const -{ - return m_id != other.m_id || memcmp(m_blob, other.m_blob, sizeof(m_blob)) != 0; -} diff --git a/src/net/Job.h b/src/net/Job.h deleted file mode 100644 index 62ac26d3..00000000 --- a/src/net/Job.h +++ /dev/null @@ -1,85 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * - * - * 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 . - */ - -#ifndef __JOB_H__ -#define __JOB_H__ - - -#include -#include -#include - - -#include "net/JobId.h" - - -class Job -{ -public: - Job(int poolId = -2, bool nicehash = false); - ~Job(); - - bool setBlob(const char *blob); - bool setTarget(const char *target); - PowVariant powVariant() const; - - inline bool isNicehash() const { return m_nicehash; } - inline bool isValid() const { return m_size > 0 && m_diff > 0; } - inline bool setId(const char *id) { return m_id.setId(id); } - inline const JobId &id() const { return m_id; } - inline const uint32_t *nonce() const { return reinterpret_cast(m_blob + 39); } - inline const uint8_t *blob() const { return m_blob; } - inline int poolId() const { return m_poolId; } - inline size_t size() const { return m_size; } - inline uint32_t *nonce() { return reinterpret_cast(m_blob + 39); } - inline uint32_t diff() const { return (uint32_t) m_diff; } - inline uint64_t target() const { return m_target; } - inline uint64_t height() const { return m_height; } - inline void setNicehash(bool nicehash) { m_nicehash = nicehash; } - inline void setThreadId(int threadId) { m_threadId = threadId; } - inline void setPowVariant(PowVariant powVariant) { m_powVariant = powVariant; } - inline void setHeight(uint64_t height) { m_height = height; } - - static bool fromHex(const char* in, unsigned int len, unsigned char* out); - static inline uint32_t *nonce(uint8_t *blob) { return reinterpret_cast(blob + 39); } - static inline uint64_t toDiff(uint64_t target) { return 0xFFFFFFFFFFFFFFFFULL / target; } - static void toHex(const unsigned char* in, unsigned int len, char* out); - - bool operator==(const Job &other) const; - bool operator!=(const Job &other) const; - -private: - uint8_t m_blob[MAX_BLOB_SIZE]; // Max blob size is 84 (75 fixed + 9 variable), aligned to 96. https://github.com/xmrig/xmrig/issues/1 Thanks fireice-uk. - - bool m_nicehash; - int m_poolId; - int m_threadId; - JobId m_id; - size_t m_size; - uint64_t m_diff; - uint64_t m_target; - uint64_t m_height; - PowVariant m_powVariant; -}; - -#endif /* __JOB_H__ */ diff --git a/src/net/JobResult.h b/src/net/JobResult.h index 520022a7..2c2fded5 100644 --- a/src/net/JobResult.h +++ b/src/net/JobResult.h @@ -4,8 +4,10 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018 Lee Clagett + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , * * 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 @@ -21,60 +23,53 @@ * along with this program. If not, see . */ -#ifndef __JOBRESULT_H__ -#define __JOBRESULT_H__ +#ifndef XMRIG_JOBRESULT_H +#define XMRIG_JOBRESULT_H #include #include -#include "Job.h" +#include "base/tools/String.h" +#include "base/net/stratum/Job.h" + + +namespace xmrig { class JobResult { public: - inline JobResult() : poolId(0), diff(0), nonce(0) {} - inline JobResult(int poolId, const JobId &jobId, uint32_t nonce, const uint8_t *result, uint32_t diff) : - poolId(poolId), - jobId(jobId), - diff(diff), - nonce(nonce) + inline JobResult() {} + + inline JobResult(const Job &job, uint32_t nonce, const uint8_t *result) : + algorithm(job.algorithm()), + clientId(job.clientId()), + jobId(job.id()), + nonce(nonce), + diff(job.diff()), + index(job.index()) { - memcpy(this->result, result, sizeof(this->result)); + memcpy(m_result, result, sizeof(m_result)); } + inline const uint8_t *result() const { return m_result; } + inline uint64_t actualDiff() const { return Job::toDiff(reinterpret_cast(m_result)[3]); } - inline JobResult(const Job &job) : poolId(0), diff(0), nonce(0) - { - jobId = job.id(); - poolId = job.poolId(); - diff = job.diff(); - nonce = *job.nonce(); - } + const Algorithm algorithm; + const String clientId; + const String jobId; + const uint32_t nonce = 0; + const uint64_t diff = 0; + const uint8_t index = 0; - - inline JobResult &operator=(const Job &job) { - jobId = job.id(); - poolId = job.poolId(); - diff = job.diff(); - - return *this; - } - - - inline uint64_t actualDiff() const - { - return Job::toDiff(reinterpret_cast(result)[3]); - } - - - int poolId; - JobId jobId; - uint32_t diff; - uint32_t nonce; - uint8_t result[32]; +private: + uint8_t m_result[32]; }; -#endif /* __JOBRESULT_H__ */ + +} /* namespace xmrig */ + + +#endif /* XMRIG_JOBRESULT_H */ diff --git a/src/net/JobResults.cpp b/src/net/JobResults.cpp new file mode 100644 index 00000000..40c2e50b --- /dev/null +++ b/src/net/JobResults.cpp @@ -0,0 +1,134 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + + +#include +#include +#include +#include + + +#include "base/tools/Handle.h" +#include "net/interfaces/IJobResultListener.h" +#include "net/JobResult.h" +#include "net/JobResults.h" + + +namespace xmrig { + + +class JobResultsPrivate +{ +public: + inline JobResultsPrivate(IJobResultListener *listener) : + listener(listener) + { + async = new uv_async_t; + async->data = this; + + uv_async_init(uv_default_loop(), async, JobResultsPrivate::onResult); + } + + + inline ~JobResultsPrivate() + { + Handle::close(async); + } + + + void submit(const JobResult &result) + { + mutex.lock(); + queue.push_back(result); + mutex.unlock(); + + uv_async_send(async); + } + + +private: + static void onResult(uv_async_t *handle) { static_cast(handle->data)->submit(); } + + + inline void submit() + { + std::list results; + + mutex.lock(); + + while (!queue.empty()) { + results.push_back(std::move(queue.front())); + queue.pop_front(); + } + + mutex.unlock(); + + for (auto result : results) { + listener->onJobResult(result); + } + + results.clear(); + } + + + IJobResultListener *listener; + std::list queue; + std::mutex mutex; + uv_async_t *async; +}; + + +static JobResultsPrivate *handler = nullptr; + + +} // namespace xmrig + + + +void xmrig::JobResults::setListener(IJobResultListener *listener) +{ + assert(handler == nullptr); + + handler = new JobResultsPrivate(listener); +} + + +void xmrig::JobResults::stop() +{ + assert(handler != nullptr); + + delete handler; + + handler = nullptr; +} + + +void xmrig::JobResults::submit(const JobResult &result) +{ + assert(handler != nullptr); + + if (handler) { + handler->submit(result); + } +} diff --git a/src/log/ConsoleLog.h b/src/net/JobResults.h similarity index 61% rename from src/log/ConsoleLog.h rename to src/net/JobResults.h index a04a27c5..e7082acb 100644 --- a/src/log/ConsoleLog.h +++ b/src/net/JobResults.h @@ -4,8 +4,10 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018 Lee Clagett + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , * * 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 @@ -21,34 +23,27 @@ * along with this program. If not, see . */ -#ifndef __CONSOLELOG_H__ -#define __CONSOLELOG_H__ +#ifndef XMRIG_JOBRESULTS_H +#define XMRIG_JOBRESULTS_H -#include +namespace xmrig { -#include "interfaces/ILogBackend.h" +class IJobResultListener; +class JobResult; -class ConsoleLog : public ILogBackend +class JobResults { public: - ConsoleLog(bool colors); - - void message(int level, const char *fmt, va_list args) override; - void text(const char *fmt, va_list args) override; - -private: - bool isWritable() const; - void print(va_list args); - - bool m_colors; - char m_buf[512]; - char m_fmt[256]; - uv_buf_t m_uvBuf; - uv_stream_t *m_stream; - uv_tty_t m_tty; + static void setListener(IJobResultListener *listener); + static void stop(); + static void submit(const JobResult &result); }; -#endif /* __CONSOLELOG_H__ */ + +} // namespace xmrig + + +#endif /* XMRIG_JOBRESULTS_H */ diff --git a/src/net/Network.cpp b/src/net/Network.cpp index 78faa7e3..22277468 100644 --- a/src/net/Network.cpp +++ b/src/net/Network.cpp @@ -4,9 +4,10 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * Copyright 2017- BenDr0id - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2019 Howard Chu + * Copyright 2016-2019 XMRig , * * 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,107 +27,133 @@ #pragma warning(disable:4244) #endif +#include #include +#include #include #include -#include -#include "api/Api.h" -#include "log/Log.h" -#include "net/Client.h" +#include "base/io/log/Log.h" +#include "base/net/stratum/Client.h" +#include "base/net/stratum/SubmitResult.h" +#include "base/tools/Chrono.h" +#include "base/tools/Timer.h" +#include "core/config/Config.h" +#include "core/Controller.h" +#include "core/Miner.h" +#include "net/JobResult.h" +#include "net/JobResults.h" #include "net/Network.h" #include "net/strategies/DonateStrategy.h" -#include "net/strategies/FailoverStrategy.h" -#include "net/strategies/SinglePoolStrategy.h" -#include "net/SubmitResult.h" -#include "net/Url.h" -#include "Options.h" -#include "Platform.h" -#include "workers/Workers.h" +#include "rapidjson/document.h" -Network::Network(const Options *options) : - m_options(options), - m_donate(nullptr) -{ - srand(time(0) ^ (uintptr_t) this); - - Workers::setListener(this); - - const std::vector &pools = options->pools(); - -#ifndef XMRIG_NO_TLS - //ssl_init(); +#ifdef XMRIG_FEATURE_API +# include "base/api/Api.h" +# include "base/api/interfaces/IApiRequest.h" #endif - if (pools.size() > 1) { - m_strategy = new FailoverStrategy(pools, Platform::userAgent(), this); - } - else { - m_strategy = new SinglePoolStrategy(pools.front(), Platform::userAgent(), this); +#ifdef XMRIG_FEATURE_CC_CLIENT +# include "cc/CCClient.h" +#endif + +xmrig::Network::Network(Controller *controller) : + m_controller(controller), + m_donate(nullptr), + m_timer(nullptr) +{ + JobResults::setListener(this); + controller->addListener(this); + +# ifdef XMRIG_FEATURE_API + controller->api()->addListener(this); +# endif + +# ifdef XMRIG_FEATURE_CC_CLIENT + controller->ccClient()->addClientStatusListener(this); +# endif + + const Pools &pools = controller->config()->pools(); + m_strategy = pools.createStrategy(this); + + if (pools.donateLevel() > 0) { + m_donate = new DonateStrategy(controller, this); } - if (m_options->donateLevel() > 0) { - m_donate = new DonateStrategy(Platform::userAgent(), this); - } - - m_timer.data = this; - uv_timer_init(uv_default_loop(), &m_timer); - - uv_timer_start(&m_timer, Network::onTick, kTickInterval, kTickInterval); + m_timer = new Timer(this, kTickInterval, kTickInterval); } -Network::~Network() +xmrig::Network::~Network() { -#ifndef XMRIG_NO_TLS - //ssl_destroy(); -#endif + JobResults::stop(); + + delete m_timer; + + if (m_donate) { + delete m_donate; + } + + delete m_strategy; } -void Network::connect() +void xmrig::Network::connect() { m_strategy->connect(); } -void Network::stop() +void xmrig::Network::onActive(IStrategy *strategy, IClient *client) { - if (m_donate) { - m_donate->stop(); - } - - m_strategy->stop(); -} - - -void Network::onActive(Client *client) -{ - if (client->id() == -1) { + if (m_donate && m_donate == strategy) { LOG_NOTICE("dev donate started"); return; } - m_state.setPool(client->host(), client->port()); - LOG_INFO(m_options->colors() ? "\x1B[01;37muse pool \x1B[01;36m%s:%d" : "use pool %s:%d", client->host(), client->port()); + m_state.onActive(client); + + const char *tlsVersion = client->tlsVersion(); + LOG_INFO(WHITE_BOLD("use %s ") CYAN_BOLD("%s:%d ") GREEN_BOLD("%s") " " BLACK_BOLD("%s"), + client->mode(), client->pool().host().data(), client->pool().port(), tlsVersion ? tlsVersion : "", client->ip().data()); + + const char *fingerprint = client->tlsFingerprint(); + if (fingerprint != nullptr) { + LOG_INFO(BLACK_BOLD("fingerprint (SHA-256): \"%s\""), fingerprint); + } } -void Network::onJob(Client *client, const Job &job) +void xmrig::Network::onConfigChanged(Config *config, Config *previousConfig) { - if (m_donate && m_donate->isActive() && client->id() != -1) { + if (config->pools() == previousConfig->pools() || !config->pools().active()) { return; } - setJob(client, job); + m_strategy->stop(); + + config->pools().print(); + + delete m_strategy; + m_strategy = config->pools().createStrategy(this); + connect(); } -void Network::onJobResult(const JobResult &result) +void xmrig::Network::onJob(IStrategy *strategy, IClient *client, const Job &job) { - if (result.poolId == -1 && m_donate) { + if (m_donate && m_donate->isActive() && m_donate != strategy) { + return; + } + + setJob(client, job, m_donate == strategy); +} + + +void xmrig::Network::onJobResult(const JobResult &result) +{ + if (result.index == 1 && m_donate) { m_donate->submit(result); return; } @@ -135,7 +162,31 @@ void Network::onJobResult(const JobResult &result) } -void Network::onPause(IStrategy *strategy) +void xmrig::Network::onLogin(IStrategy *, IClient *client, rapidjson::Document &doc, rapidjson::Value ¶ms) +{ + using namespace rapidjson; + auto &allocator = doc.GetAllocator(); + + Algorithms algorithms = m_controller->miner()->algorithms(); + const Algorithm algorithm = client->pool().algorithm(); + if (algorithm.isValid()) { + const size_t index = static_cast(std::distance(algorithms.begin(), std::find(algorithms.begin(), algorithms.end(), algorithm))); + if (index > 0 && index < algorithms.size()) { + std::swap(algorithms[0], algorithms[index]); + } + } + + Value algo(kArrayType); + + for (const auto &a : algorithms) { + algo.PushBack(StringRef(a.shortName()), allocator); + } + + params.AddMember("algo", algo, allocator); +} + + +void xmrig::Network::onPause(IStrategy *strategy) { if (m_donate && m_donate == strategy) { LOG_NOTICE("dev donate finished"); @@ -145,65 +196,146 @@ void Network::onPause(IStrategy *strategy) if (!m_strategy->isActive()) { LOG_ERR("no active pools, stop mining"); m_state.stop(); - return Workers::pause(); + + return m_controller->miner()->pause(); } } -void Network::onResultAccepted(Client *client, const SubmitResult &result, const char *error) +void xmrig::Network::onResultAccepted(IStrategy *, IClient *, const SubmitResult &result, const char *error) { m_state.add(result, error); if (error) { - LOG_INFO(m_options->colors() ? "\x1B[01;31mrejected\x1B[0m (%" PRId64 "/%" PRId64 ") diff \x1B[01;37m%u\x1B[0m \x1B[31m\"%s\"\x1B[0m \x1B[01;30m(%" PRIu64 " ms)" - : "rejected (%" PRId64 "/%" PRId64 ") diff %u \"%s\" (%" PRIu64 " ms)", + LOG_INFO(RED_BOLD("rejected") " (%" PRId64 "/%" PRId64 ") diff " WHITE_BOLD("%" PRIu64) " " RED("\"%s\"") " " BLACK_BOLD("(%" PRIu64 " ms)"), m_state.accepted, m_state.rejected, result.diff, error, result.elapsed); } else { - LOG_INFO(m_options->colors() ? "\x1B[01;32maccepted\x1B[0m (%" PRId64 "/%" PRId64 ") diff \x1B[01;37m%u\x1B[0m \x1B[01;30m(%" PRIu64 " ms)" - : "accepted (%" PRId64 "/%" PRId64 ") diff %u (%" PRIu64 " ms)", + LOG_INFO(GREEN_BOLD("accepted") " (%" PRId64 "/%" PRId64 ") diff " WHITE_BOLD("%" PRIu64) " " BLACK_BOLD("(%" PRIu64 " ms)"), m_state.accepted, m_state.rejected, result.diff, result.elapsed); } } -void Network::setJob(Client *client, const Job &job) +void xmrig::Network::onVerifyAlgorithm(IStrategy *, const IClient *, const Algorithm &algorithm, bool *ok) { - if (m_options->colors()) { - LOG_INFO("\x1B[01;35mnew job\x1B[0m from \x1B[01;37m%s:%d\x1B[0m with diff \x1B[01;37m%d\x1B[0m variant \x1B[01;37m%s", - client->host(), client->port(), job.diff(), getPowVariantName(job.powVariant()).c_str()); - } - else { - LOG_INFO("new job from %s:%d with diff %d variant %s", client->host(), client->port(), job.diff(), getPowVariantName(job.powVariant()).c_str()); - } + if (!m_controller->miner()->isEnabled(algorithm)) { + *ok = false; - m_state.powVariant = job.powVariant(); - m_state.diff = job.diff(); - Workers::setJob(job); + return; + } } -void Network::tick() +#ifdef XMRIG_FEATURE_API +void xmrig::Network::onRequest(IApiRequest &request) { - const uint64_t now = uv_now(uv_default_loop()); + if (request.type() == IApiRequest::REQ_SUMMARY) { + request.accept(); + + getResults(request.reply(), request.doc(), request.version()); + getConnection(request.reply(), request.doc(), request.version()); + } +} +#endif + + +#ifdef XMRIG_FEATURE_CC_CLIENT +void xmrig::Network::onUpdateRequest(ClientStatus& clientStatus) +{ + clientStatus.setSharesGood(m_state.accepted); + clientStatus.setSharesTotal(m_state.accepted + m_state.rejected); + clientStatus.setHashesTotal(m_state.total); + clientStatus.setAvgTime(m_state.avgTime()); + + clientStatus.setCurrentPool(m_state.pool); + + clientStatus.setCurrentAlgoName(m_strategy->client()->job().algorithm().name()); +} +#endif + + +void xmrig::Network::setJob(IClient *client, const Job &job, bool donate) +{ + if (job.height()) { + LOG_INFO(MAGENTA_BOLD("new job") " from " WHITE_BOLD("%s:%d") " diff " WHITE_BOLD("%" PRIu64) " algo " WHITE_BOLD("%s") " height " WHITE_BOLD("%" PRIu64), + client->pool().host().data(), client->pool().port(), job.diff(), job.algorithm().shortName(), job.height()); + } + else { + LOG_INFO(MAGENTA_BOLD("new job") " from " WHITE_BOLD("%s:%d") " diff " WHITE_BOLD("%" PRIu64) " algo " WHITE_BOLD("%s"), + client->pool().host().data(), client->pool().port(), job.diff(), job.algorithm().shortName()); + } + + if (!donate && m_donate) { + m_donate->setAlgo(job.algorithm()); + } + + m_state.diff = job.diff(); + m_controller->miner()->setJob(job, donate); +} + + +void xmrig::Network::tick() +{ + const uint64_t now = Chrono::steadyMSecs(); m_strategy->tick(now); if (m_donate) { m_donate->tick(now); } - -# ifndef XMRIG_NO_API - Api::tick(m_state); -# endif - -# ifndef XMRIG_NO_CC - CCClient::updateNetworkState(m_state); -# endif } -void Network::onTick(uv_timer_t *handle) +#ifdef XMRIG_FEATURE_API +void xmrig::Network::getConnection(rapidjson::Value &reply, rapidjson::Document &doc, int version) const { - static_cast(handle->data)->tick(); + using namespace rapidjson; + auto &allocator = doc.GetAllocator(); + + reply.AddMember("algo", StringRef(m_strategy->client()->job().algorithm().shortName()), allocator); + + Value connection(kObjectType); + connection.AddMember("pool", StringRef(m_state.pool), allocator); + connection.AddMember("ip", m_state.ip().toJSON(), allocator); + connection.AddMember("uptime", m_state.connectionTime(), allocator); + connection.AddMember("ping", m_state.latency(), allocator); + connection.AddMember("failures", m_state.failures, allocator); + connection.AddMember("tls", m_state.tls().toJSON(), allocator); + connection.AddMember("tls-fingerprint", m_state.fingerprint().toJSON(), allocator); + + if (version == 1) { + connection.AddMember("error_log", Value(kArrayType), allocator); + } + + reply.AddMember("connection", connection, allocator); } + + +void xmrig::Network::getResults(rapidjson::Value &reply, rapidjson::Document &doc, int version) const +{ + using namespace rapidjson; + auto &allocator = doc.GetAllocator(); + + Value results(kObjectType); + + results.AddMember("diff_current", m_state.diff, allocator); + results.AddMember("shares_good", m_state.accepted, allocator); + results.AddMember("shares_total", m_state.accepted + m_state.rejected, allocator); + results.AddMember("avg_time", m_state.avgTime(), allocator); + results.AddMember("hashes_total", m_state.total, allocator); + + Value best(kArrayType); + for (size_t i = 0; i < m_state.topDiff.size(); ++i) { + best.PushBack(m_state.topDiff[i], allocator); + } + + results.AddMember("best", best, allocator); + + if (version == 1) { + results.AddMember("error_log", Value(kArrayType), allocator); + } + + reply.AddMember("results", results, allocator); +} +#endif diff --git a/src/net/Network.h b/src/net/Network.h index fe13d9b7..5d6960da 100644 --- a/src/net/Network.h +++ b/src/net/Network.h @@ -4,8 +4,10 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2019 Howard Chu + * Copyright 2016-2019 XMRig , * * 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 @@ -21,54 +23,79 @@ * along with this program. If not, see . */ -#ifndef __NETWORK_H__ -#define __NETWORK_H__ +#ifndef XMRIG_NETWORK_H +#define XMRIG_NETWORK_H #include -#include -#include "api/NetworkState.h" +#include "base/api/interfaces/IApiListener.h" +#include "base/kernel/interfaces/IBaseListener.h" +#include "base/kernel/interfaces/IStrategyListener.h" +#include "base/kernel/interfaces/ITimerListener.h" +#include "base/cc/interfaces/IClientStatusListener.h" #include "interfaces/IJobResultListener.h" -#include "interfaces/IStrategyListener.h" +#include "net/NetworkState.h" +#include "rapidjson/fwd.h" + +namespace xmrig { +class Controller; class IStrategy; -class Options; -class Url; -class Network : public IJobResultListener, public IStrategyListener +class Network : public IJobResultListener, public IStrategyListener, public IBaseListener, public ITimerListener, public IApiListener, public IClientStatusListener { public: - Network(const Options *options); - ~Network(); + Network(Controller *controller); + ~Network() override; - void connect(); - void stop(); + inline IStrategy *strategy() const { return m_strategy; } + + void connect(); protected: - void onActive(Client *client) override; - void onJob(Client *client, const Job &job) override; - void onJobResult(const JobResult &result) override; - void onPause(IStrategy *strategy) override; - void onResultAccepted(Client *client, const SubmitResult &result, const char *error) override; + inline void onTimer(const Timer *) override { tick(); } + + void onActive(IStrategy *strategy, IClient *client) override; + void onConfigChanged(Config *config, Config *previousConfig) override; + void onJob(IStrategy *strategy, IClient *client, const Job &job) override; + void onJobResult(const JobResult &result) override; + void onLogin(IStrategy *strategy, IClient *client, rapidjson::Document &doc, rapidjson::Value ¶ms) override; + void onPause(IStrategy *strategy) override; + void onResultAccepted(IStrategy *strategy, IClient *client, const SubmitResult &result, const char *error) override; + void onVerifyAlgorithm(IStrategy *strategy, const IClient *client, const Algorithm &algorithm, bool *ok) override; + +# ifdef XMRIG_FEATURE_API + void onRequest(IApiRequest &request) override; +# endif + +# ifdef XMRIG_FEATURE_CC_CLIENT + void onUpdateRequest(ClientStatus& clientStatus) override; +# endif private: - constexpr static int kTickInterval = 1 * 1000; + constexpr static int kTickInterval = 1 * 1000; - void setJob(Client *client, const Job &job); - void tick(); + void setJob(IClient *client, const Job &job, bool donate); + void tick(); - static void onTick(uv_timer_t *handle); +# ifdef XMRIG_FEATURE_API + void getConnection(rapidjson::Value &reply, rapidjson::Document &doc, int version) const; + void getResults(rapidjson::Value &reply, rapidjson::Document &doc, int version) const; +# endif - const Options *m_options; - IStrategy *m_donate; - IStrategy *m_strategy; - NetworkState m_state; - uv_timer_t m_timer; + Controller *m_controller; + IStrategy *m_donate; + IStrategy *m_strategy; + NetworkState m_state; + Timer *m_timer; }; -#endif /* __NETWORK_H__ */ +} /* namespace xmrig */ + + +#endif /* XMRIG_NETWORK_H */ diff --git a/src/api/NetworkState.cpp b/src/net/NetworkState.cpp similarity index 56% rename from src/api/NetworkState.cpp rename to src/net/NetworkState.cpp index d88d08db..6868f57e 100644 --- a/src/api/NetworkState.cpp +++ b/src/net/NetworkState.cpp @@ -4,8 +4,9 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , * * 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,30 +29,26 @@ #include -#include "api/NetworkState.h" -#include "net/SubmitResult.h" +#include "base/kernel/interfaces/IClient.h" +#include "base/net/stratum/Pool.h" +#include "base/net/stratum/SubmitResult.h" +#include "base/tools/Chrono.h" +#include "net/NetworkState.h" -NetworkState::NetworkState() : - diff(0), +xmrig::NetworkState::NetworkState() : + pool(), accepted(0), + diff(0), failures(0), rejected(0), total(0), - powVariant(POW_AUTODETECT), m_active(false) { - memset(pool, 0, sizeof(pool)); } -int NetworkState::connectionTime() const -{ - return m_active ? (int)((uv_now(uv_default_loop()) - m_connectionTime) / 1000) : 0; -} - - -uint32_t NetworkState::avgTime() const +uint32_t xmrig::NetworkState::avgTime() const { if (m_latency.empty()) { return 0; @@ -61,7 +58,7 @@ uint32_t NetworkState::avgTime() const } -uint32_t NetworkState::latency() const +uint32_t xmrig::NetworkState::latency() const { const size_t calls = m_latency.size(); if (calls == 0) { @@ -75,7 +72,13 @@ uint32_t NetworkState::latency() const } -void NetworkState::add(const SubmitResult &result, const char *error) +uint64_t xmrig::NetworkState::connectionTime() const +{ + return m_active ? ((Chrono::steadyMSecs() - m_connectionTime) / 1000) : 0; +} + + +void xmrig::NetworkState::add(const SubmitResult &result, const char *error) { if (error) { rejected++; @@ -91,23 +94,29 @@ void NetworkState::add(const SubmitResult &result, const char *error) std::sort(topDiff.rbegin(), topDiff.rend()); } - m_latency.push_back(result.elapsed > 0xFFFF ? 0xFFFF : (uint16_t) result.elapsed); + m_latency.push_back(result.elapsed > 0xFFFF ? 0xFFFF : static_cast(result.elapsed)); } -void NetworkState::setPool(const char *host, int port) +void xmrig::NetworkState::onActive(IClient *client) { - snprintf(pool, sizeof(pool) - 1, "%s:%d", host, port); + snprintf(pool, sizeof(pool) - 1, "%s:%d", client->pool().host().data(), client->pool().port()); - m_active = true; - m_connectionTime = uv_now(uv_default_loop()); + m_ip = client->ip(); + m_tls = client->tlsVersion(); + m_fingerprint = client->tlsFingerprint(); + m_active = true; + m_connectionTime = Chrono::steadyMSecs(); } -void NetworkState::stop() +void xmrig::NetworkState::stop() { - m_active = false; - diff = 0; + m_active = false; + diff = 0; + m_ip = nullptr; + m_tls = nullptr; + m_fingerprint = nullptr; failures++; m_latency.clear(); diff --git a/src/api/NetworkState.h b/src/net/NetworkState.h similarity index 65% rename from src/api/NetworkState.h rename to src/net/NetworkState.h index 4b0498d3..ce56ec72 100644 --- a/src/api/NetworkState.h +++ b/src/net/NetworkState.h @@ -4,8 +4,9 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , * * 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 @@ -21,15 +22,21 @@ * along with this program. If not, see . */ -#ifndef __NETWORKSTATE_H__ -#define __NETWORKSTATE_H__ +#ifndef XMRIG_NETWORKSTATE_H +#define XMRIG_NETWORKSTATE_H #include #include -#include +#include "base/tools/String.h" + + +namespace xmrig { + + +class IClient; class SubmitResult; @@ -38,26 +45,36 @@ class NetworkState public: NetworkState(); - int connectionTime() const; + inline const String &fingerprint() const { return m_fingerprint; } + inline const String &ip() const { return m_ip; } + inline const String &tls() const { return m_tls; } + uint32_t avgTime() const; uint32_t latency() const; + uint64_t connectionTime() const; void add(const SubmitResult &result, const char *error); - void setPool(const char *host, int port); + void onActive(IClient *client); void stop(); char pool[256]; std::array topDiff { { } }; - uint32_t diff; uint64_t accepted; + uint64_t diff; uint64_t failures; uint64_t rejected; uint64_t total; - PowVariant powVariant; private: bool m_active; std::vector m_latency; + String m_fingerprint; + String m_ip; + String m_tls; uint64_t m_connectionTime; }; -#endif /* __NETWORKSTATE_H__ */ + +} /* namespace xmrig */ + + +#endif /* XMRIG_NETWORKSTATE_H */ diff --git a/src/net/Url.cpp b/src/net/Url.cpp deleted file mode 100644 index 10442236..00000000 --- a/src/net/Url.cpp +++ /dev/null @@ -1,198 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * - * - * 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 . - */ - - -#include -#include -#include - - -#include "net/Url.h" - - -#ifdef _MSC_VER -# define strncasecmp(x,y,z) _strnicmp(x,y,z) -#endif - - -Url::Url() : - m_useTls(false), - m_keepAlive(false), - m_nicehash(false), - m_host(nullptr), - m_password(nullptr), - m_user(nullptr), - m_port(kDefaultPort) -{ -} - - -/** - * @brief Parse url. - * - * Valid urls: - * example.com - * example.com:3333 - * stratum+tcp://example.com - * stratum+tcp://example.com:3333 - * - * @param url - */ -Url::Url(const char *url) : - m_useTls(false), - m_keepAlive(false), - m_nicehash(false), - m_host(nullptr), - m_password(nullptr), - m_user(nullptr), - m_port(kDefaultPort) -{ - parse(url); -} - - -Url::Url(const char *host, uint16_t port, const char *user, const char *password, bool useTls, bool keepAlive, bool nicehash) : - m_useTls(useTls), - m_keepAlive(keepAlive), - m_nicehash(nicehash), - m_password(password ? strdup(password) : nullptr), - m_user(user ? strdup(user) : nullptr), - m_port(port) -{ - m_host = strdup(host); -} - - -Url::~Url() -{ - free(m_host); - free(m_password); - free(m_user); -} - - -bool Url::parse(const char *url) -{ - const char *p = strstr(url, "://"); - const char *base = url; - - if (p) { - if (strncasecmp(url, "stratum+tcp://", 14)) { - return false; - } - - base = url + 14; - } - - if (!strlen(base) || *base == '/') { - return false; - } - - const char *port = strchr(base, ':'); - if (!port) { - m_host = strdup(base); - return false; - } - - const size_t size = port++ - base + 1; - m_host = static_cast(malloc(size)); - memcpy(m_host, base, size - 1); - m_host[size - 1] = '\0'; - - m_port = (uint16_t) strtol(port, nullptr, 10); - return true; -} - - -bool Url::setUserpass(const char *userpass) -{ - const char *p = strchr(userpass, ':'); - if (!p) { - return false; - } - - free(m_user); - free(m_password); - - m_user = static_cast(calloc(p - userpass + 1, 1)); - strncpy(m_user, userpass, p - userpass); - m_password = strdup(p + 1); - - return true; -} - - -void Url::applyExceptions() -{ - if (!isValid()) { - return; - } - - if (strstr(m_host, ".nicehash.com")) { - m_keepAlive = false; - m_nicehash = true; - } - - if (strstr(m_host, ".minergate.com")) { - m_keepAlive = false; - } -} - - -void Url::setPassword(const char *password) -{ - if (!password) { - return; - } - - free(m_password); - m_password = strdup(password); -} - - -void Url::setUser(const char *user) -{ - if (!user) { - return; - } - - free(m_user); - m_user = strdup(user); -} - - -Url &Url::operator=(const Url *other) -{ - m_useTls = other->m_useTls; - m_keepAlive = other->m_keepAlive; - m_nicehash = other->m_nicehash; - m_port = other->m_port; - - free(m_host); - m_host = strdup(other->m_host); - - setPassword(other->m_password); - setUser(other->m_user); - - return *this; -} diff --git a/src/net/Url.h b/src/net/Url.h deleted file mode 100644 index 6d4788c6..00000000 --- a/src/net/Url.h +++ /dev/null @@ -1,73 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * - * - * 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 . - */ - -#ifndef __URL_H__ -#define __URL_H__ - - -#include - - -class Url -{ -public: - constexpr static const char *kDefaultPassword = "x"; - constexpr static const char *kDefaultUser = "x"; - constexpr static uint16_t kDefaultPort = 3333; - - Url(); - Url(const char *url); - Url(const char *host, uint16_t port, const char *user = nullptr, const char *password = nullptr, bool useTls = false, bool keepAlive = false, bool nicehash = false ); - ~Url(); - - inline bool useTls() const { return m_useTls; } - inline bool isKeepAlive() const { return m_keepAlive; } - inline bool isNicehash() const { return m_nicehash; } - inline bool isValid() const { return m_host && m_port > 0; } - inline const char *host() const { return m_host; } - inline const char *password() const { return m_password ? m_password : kDefaultPassword; } - inline const char *user() const { return m_user ? m_user : kDefaultUser; } - inline uint16_t port() const { return m_port; } - inline void setUseTls(bool tls) { m_useTls = tls; } - inline void setKeepAlive(bool keepAlive) { m_keepAlive = keepAlive; } - inline void setNicehash(bool nicehash) { m_nicehash = nicehash; } - - bool parse(const char *url); - bool setUserpass(const char *userpass); - void applyExceptions(); - void setPassword(const char *password); - void setUser(const char *user); - - Url &operator=(const Url *other); - -private: - bool m_useTls; - bool m_keepAlive; - bool m_nicehash; - char *m_host; - char *m_password; - char *m_user; - uint16_t m_port; -}; - -#endif /* __URL_H__ */ diff --git a/src/net/interfaces/IJobResultListener.h b/src/net/interfaces/IJobResultListener.h new file mode 100644 index 00000000..e99db502 --- /dev/null +++ b/src/net/interfaces/IJobResultListener.h @@ -0,0 +1,48 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 . + */ + +#ifndef XMRIG_IJOBRESULTLISTENER_H +#define XMRIG_IJOBRESULTLISTENER_H + + +namespace xmrig { + + +class Client; +class JobResult; + + +class IJobResultListener +{ +public: + virtual ~IJobResultListener() = default; + + virtual void onJobResult(const JobResult &result) = 0; +}; + + +} /* namespace xmrig */ + + +#endif // XMRIG_IJOBRESULTLISTENER_H diff --git a/src/net/strategies/DonateStrategy.cpp b/src/net/strategies/DonateStrategy.cpp index 2ca359f7..b963062d 100644 --- a/src/net/strategies/DonateStrategy.cpp +++ b/src/net/strategies/DonateStrategy.cpp @@ -4,9 +4,9 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2018 XMRig - * Copyright 2017- BenDr0id - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , * * 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 @@ -23,175 +23,322 @@ */ -#include -#include "interfaces/IStrategyListener.h" -#include "net/Client.h" -#include "net/Job.h" +#include +#include +#include + + +#include "base/kernel/Platform.h" +#include "base/net/stratum/Client.h" +#include "base/net/stratum/Job.h" +#include "base/net/stratum/strategies/FailoverStrategy.h" +#include "base/net/stratum/strategies/SinglePoolStrategy.h" +#include "base/tools/Buffer.h" +#include "base/tools/Timer.h" +#include "core/config/Config.h" +#include "core/Controller.h" +#include "core/Miner.h" +#include "crypto/common/keccak.h" +#include "net/Network.h" #include "net/strategies/DonateStrategy.h" -#include "Options.h" +#include "rapidjson/document.h" -extern "C" -{ -#include "crypto/c_keccak.h" -} +namespace xmrig { -static inline int random(int min, int max) { - return min + rand() / (RAND_MAX / (max - min + 1) + 1); -} +static inline double randomf(double min, double max) { return (max - min) * (((static_cast(rand())) / static_cast(RAND_MAX))) + min; } +static inline uint64_t random(uint64_t base, double min, double max) { return static_cast(base * randomf(min, max)); } -DonateStrategy::DonateStrategy(const char *agent, IStrategyListener *listener) : - m_active(false), - m_donateTime(Options::i()->donateLevel() * 60 * 1000), - m_idleTime((100 - Options::i()->donateLevel()) * 60 * 1000), - m_listener(listener) +static const char *kDonateHost = "donate.graef.in"; + +} /* namespace xmrig */ + + +xmrig::DonateStrategy::DonateStrategy(Controller *controller, IStrategyListener *listener) : + m_tls(false), + m_userId(), + m_donateTime(static_cast(controller->config()->pools().donateLevel()) * 60 * 1000), + m_idleTime((100 - static_cast(controller->config()->pools().donateLevel())) * 60 * 1000), + m_controller(controller), + m_proxy(nullptr), + m_strategy(nullptr), + m_listener(listener), + m_state(STATE_NEW), + m_now(0), + m_timestamp(0) { uint8_t hash[200]; - char userId[65] = { 0 }; - const char *user = Options::i()->pools().front()->user(); - keccak(reinterpret_cast(user), static_cast(strlen(user)), hash, sizeof(hash)); - Job::toHex(hash, 32, userId); + const String &user = controller->config()->pools().data().front().user(); + keccak(reinterpret_cast(user.data()), user.size(), hash); + Buffer::toHex(hash, 32, m_userId); - Url *url; +# ifdef XMRIG_FEATURE_TLS + m_pools.emplace_back(kDonateHost, 443, m_userId, nullptr, 0, true, true); + m_pools.emplace_back(kDonateHost, 4000, m_userId, nullptr, 0, true, true); +# endif + m_pools.emplace_back(kDonateHost, 80, m_userId, nullptr, 0, true); + m_pools.emplace_back(kDonateHost, 4100, m_userId, nullptr, 0, true); -#ifndef XMRIG_NO_TLS - if (Options::i()->algo() == Options::ALGO_CRYPTONIGHT_HEAVY) { - url = new Url("donate2.graef.in", 8443, userId, nullptr, true, false, true); - } else if (Options::i()->algo() == Options::ALGO_CRYPTONIGHT_LITE) { - url = new Url("donate2.graef.in", 1080, userId, nullptr, true, false, true); - } else if (Options::i()->algo() == Options::ALGO_CRYPTONIGHT_ULTRALITE) { - url = new Url("donate2.graef.in", 8090, userId, nullptr, true, false, true); - } else if (Options::i()->algo() == Options::ALGO_CRYPTONIGHT_EXTREMELITE) { - url = new Url("donate2.graef.in", 9091, userId, nullptr, true, false, true); - } else if (Options::i()->algo() == Options::ALGO_CRYPTONIGHT) { - url = new Url("donate2.graef.in", 443, userId, nullptr, true, false, true); - } else if (Options::i()->algo() == Options::ALGO_ARGON2_256) { - url = new Url("donate2.graef.in", 3128, userId, nullptr, true, false, true); - } else if (Options::i()->algo() == Options::ALGO_ARGON2_512) { - url = new Url("donate2.graef.in", 3389, userId, nullptr, true, false, true); + if (m_pools.size() > 1) { + m_strategy = new FailoverStrategy(m_pools, 1, 2, this, true); } -#else - if (Options::i()->algo() == Options::ALGO_CRYPTONIGHT_HEAVY) { - url = new Url("donate2.graef.in", 9000, userId, nullptr, false, false, true); - } else if (Options::i()->algo() == Options::ALGO_CRYPTONIGHT_LITE) { - url = new Url("donate2.graef.in", 7000, userId, nullptr, false, false, true); - } else if (Options::i()->algo() == Options::ALGO_CRYPTONIGHT_ULTRALITE) { - url = new Url("donate2.graef.in", 8088, userId, nullptr, false, false, true); - } else if (Options::i()->algo() == Options::ALGO_CRYPTONIGHT_EXTREMELITE) { - url = new Url("donate2.graef.in", 8188, userId, nullptr, false, false, true); - } else if (Options::i()->algo() == Options::ALGO_CRYPTONIGHT) { - url = new Url("donate2.graef.in", 80, userId, nullptr, false, false, true); - } else if (Options::i()->algo() == Options::ALGO_ARGON2_256) { - url = new Url("donate2.graef.in", 3306, userId, nullptr, false, false, true); - } else if (Options::i()->algo() == Options::ALGO_ARGON2_512) { - url = new Url("donate2.graef.in", 3535, userId, nullptr, false, false, true); + else { + m_strategy = new SinglePoolStrategy(m_pools.front(), 1, 2, this, true); } -#endif - m_client = std::make_shared(-1, agent, this); - m_client->setUrl(url); - m_client->setRetryPause(Options::i()->retryPause() * 1000); - m_client->setQuiet(true); - m_client->setDonate(true); + m_timer = new Timer(this); - delete url; - - m_timer.data = this; - uv_timer_init(uv_default_loop(), &m_timer); - - idle(random(1800, 3600) * 1000); + setState(STATE_IDLE); } -int64_t DonateStrategy::submit(const JobResult &result) +xmrig::DonateStrategy::~DonateStrategy() { - return m_client->submit(result); -} + delete m_timer; + delete m_strategy; - -void DonateStrategy::connect() -{ - m_client->connect(); -} - - -void DonateStrategy::stop() -{ - uv_timer_stop(&m_timer); - m_client->disconnect(); -} - - -void DonateStrategy::tick(uint64_t now) -{ - m_client->tick(now); -} - - -void DonateStrategy::onClose(Client *client, int failures) -{ - if (failures == 5) { - LOG_ERR("Failed to connect to donate address. Reschedule."); - uv_timer_stop(&m_timer); - uv_timer_start(&m_timer, DonateStrategy::onSuspendTimer, 1000, 0); + if (m_proxy) { + m_proxy->deleteLater(); } } -void DonateStrategy::onJobReceived(Client *client, const Job &job) +int64_t xmrig::DonateStrategy::submit(const JobResult &result) { - m_listener->onJob(client, job); + return m_proxy ? m_proxy->submit(result) : m_strategy->submit(result); } -void DonateStrategy::onLoginSuccess(Client *client) +void xmrig::DonateStrategy::connect() { - if (!isActive()) { - uv_timer_start(&m_timer, DonateStrategy::onTimer, m_donateTime, 0); + m_proxy = createProxy(); + if (m_proxy) { + m_proxy->connect(); + } + else if (m_controller->config()->pools().proxyDonate() == Pools::PROXY_DONATE_ALWAYS) { + setState(STATE_IDLE); + } + else { + m_strategy->connect(); + } +} + + +void xmrig::DonateStrategy::setAlgo(const xmrig::Algorithm &algo) +{ + m_algorithm = algo; + + m_strategy->setAlgo(algo); +} + + +void xmrig::DonateStrategy::stop() +{ + m_timer->stop(); + m_strategy->stop(); +} + + +void xmrig::DonateStrategy::tick(uint64_t now) +{ + m_now = now; + + m_strategy->tick(now); + + if (m_proxy) { + m_proxy->tick(now); } - m_active = true; - m_listener->onActive(client); + if (state() == STATE_WAIT && now > m_timestamp) { + setState(STATE_IDLE); + } } -void DonateStrategy::onResultAccepted(Client *client, const SubmitResult &result, const char *error) +void xmrig::DonateStrategy::onActive(IStrategy *, IClient *client) { - m_listener->onResultAccepted(client, result, error); -} - - -void DonateStrategy::idle(uint64_t timeout) -{ - uv_timer_start(&m_timer, DonateStrategy::onTimer, timeout, 0); -} - - -void DonateStrategy::suspend() -{ - m_client->disconnect(); - - m_active = false; - m_listener->onPause(this); - - idle(m_idleTime); -} - - -void DonateStrategy::onTimer(uv_timer_t *handle) -{ - auto strategy = static_cast(handle->data); - - if (!strategy->isActive()) { - return strategy->connect(); + if (isActive()) { + return; } - strategy->suspend(); + setState(STATE_ACTIVE); + m_listener->onActive(this, client); } -void DonateStrategy::onSuspendTimer(uv_timer_t *handle) + +void xmrig::DonateStrategy::onPause(IStrategy *) { - auto strategy = static_cast(handle->data); - strategy->suspend(); -} \ No newline at end of file +} + + +void xmrig::DonateStrategy::onClose(IClient *, int failures) +{ + if (failures == 2 && m_controller->config()->pools().proxyDonate() == Pools::PROXY_DONATE_AUTO) { + m_proxy->deleteLater(); + m_proxy = nullptr; + + m_strategy->connect(); + } +} + + +void xmrig::DonateStrategy::onLogin(IClient *, rapidjson::Document &doc, rapidjson::Value ¶ms) +{ + using namespace rapidjson; + auto &allocator = doc.GetAllocator(); + +# ifdef XMRIG_FEATURE_TLS + if (m_tls) { + char buf[40] = { 0 }; + snprintf(buf, sizeof(buf), "stratum+ssl://%s", m_pools[0].url().data()); + params.AddMember("url", Value(buf, allocator), allocator); + } + else { + params.AddMember("url", m_pools[1].url().toJSON(), allocator); + } +# else + params.AddMember("url", m_pools[0].url().toJSON(), allocator); +# endif + + setAlgorithms(doc, params); +} + + +void xmrig::DonateStrategy::onLogin(IStrategy *, IClient *, rapidjson::Document &doc, rapidjson::Value ¶ms) +{ + setAlgorithms(doc, params); +} + + +void xmrig::DonateStrategy::onLoginSuccess(IClient *client) +{ + if (isActive()) { + return; + } + + setState(STATE_ACTIVE); + m_listener->onActive(this, client); +} + + +void xmrig::DonateStrategy::onTimer(const Timer *) +{ + setState(isActive() ? STATE_WAIT : STATE_CONNECT); +} + + +xmrig::Client *xmrig::DonateStrategy::createProxy() +{ + if (m_controller->config()->pools().proxyDonate() == Pools::PROXY_DONATE_NONE) { + return nullptr; + } + + IStrategy *strategy = m_controller->network()->strategy(); + if (!strategy->isActive() || !strategy->client()->hasExtension(IClient::EXT_CONNECT)) { + return nullptr; + } + + const IClient *client = strategy->client(); + m_tls = client->hasExtension(IClient::EXT_TLS); + + Pool pool(client->ip(), client->pool().port(), m_userId, client->pool().password(), 0, true, client->isTLS()); + pool.setAlgo(client->pool().algorithm()); + + Client *proxy = new Client(-1, Platform::userAgent(), this); + proxy->setPool(pool); + proxy->setQuiet(true); + + return proxy; +} + + +void xmrig::DonateStrategy::idle(double min, double max) +{ + m_timer->start(random(m_idleTime, min, max), 0); +} + + +void xmrig::DonateStrategy::setAlgorithms(rapidjson::Document &doc, rapidjson::Value ¶ms) +{ + using namespace rapidjson; + auto &allocator = doc.GetAllocator(); + + Algorithms algorithms = m_controller->miner()->algorithms(); + const size_t index = static_cast(std::distance(algorithms.begin(), std::find(algorithms.begin(), algorithms.end(), m_algorithm))); + if (index > 0 && index < algorithms.size()) { + std::swap(algorithms[0], algorithms[index]); + } + + Value algo(kArrayType); + + for (const auto &a : algorithms) { + algo.PushBack(StringRef(a.shortName()), allocator); + } + + params.AddMember("algo", algo, allocator); +} + + +void xmrig::DonateStrategy::setJob(IClient *client, const Job &job) +{ + if (isActive()) { + m_listener->onJob(this, client, job); + } +} + + +void xmrig::DonateStrategy::setResult(IClient *client, const SubmitResult &result, const char *error) +{ + m_listener->onResultAccepted(this, client, result, error); +} + + +void xmrig::DonateStrategy::setState(State state) +{ + constexpr const uint64_t waitTime = 3000; + + assert(m_state != state && state != STATE_NEW); + if (m_state == state) { + return; + } + + const State prev = m_state; + m_state = state; + + switch (state) { + case STATE_NEW: + break; + + case STATE_IDLE: + if (prev == STATE_NEW) { + idle(0.5, 1.5); + } + else if (prev == STATE_CONNECT) { + m_timer->start(20000, 0); + } + else { + m_strategy->stop(); + if (m_proxy) { + m_proxy->deleteLater(); + m_proxy = nullptr; + } + + idle(0.8, 1.2); + } + break; + + case STATE_CONNECT: + connect(); + break; + + case STATE_ACTIVE: + m_timer->start(m_donateTime, 0); + break; + + case STATE_WAIT: + m_timestamp = m_now + waitTime; + m_listener->onPause(this); + break; + } +} diff --git a/src/net/strategies/DonateStrategy.h b/src/net/strategies/DonateStrategy.h index fa3e06a5..134127bf 100644 --- a/src/net/strategies/DonateStrategy.h +++ b/src/net/strategies/DonateStrategy.h @@ -4,8 +4,9 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , * * 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 @@ -21,55 +22,97 @@ * along with this program. If not, see . */ -#ifndef __DONATESTRATEGY_H__ -#define __DONATESTRATEGY_H__ +#ifndef XMRIG_DONATESTRATEGY_H +#define XMRIG_DONATESTRATEGY_H -#include +#include -#include "interfaces/IClientListener.h" -#include "interfaces/IStrategy.h" +#include "base/kernel/interfaces/IClientListener.h" +#include "base/kernel/interfaces/IStrategy.h" +#include "base/kernel/interfaces/IStrategyListener.h" +#include "base/kernel/interfaces/ITimerListener.h" +#include "base/net/stratum/Pool.h" + + +namespace xmrig { class Client; +class Controller; class IStrategyListener; -class Url; -class DonateStrategy : public IStrategy, public IClientListener +class DonateStrategy : public IStrategy, public IStrategyListener, public ITimerListener, public IClientListener { public: - DonateStrategy(const char *agent, IStrategyListener *listener); + DonateStrategy(Controller *controller, IStrategyListener *listener); + ~DonateStrategy() override; -public: - inline bool isActive() const override { return m_active; } - inline void resume() override {} +protected: + inline bool isActive() const override { return state() == STATE_ACTIVE; } + inline IClient *client() const override { return m_proxy ? m_proxy : m_strategy->client(); } + inline void onJob(IStrategy *, IClient *client, const Job &job) override { setJob(client, job); } + inline void onJobReceived(IClient *client, const Job &job, const rapidjson::Value &) override { setJob(client, job); } + inline void onResultAccepted(IClient *client, const SubmitResult &result, const char *error) override { setResult(client, result, error); } + inline void onResultAccepted(IStrategy *, IClient *client, const SubmitResult &result, const char *error) override { setResult(client, result, error); } + inline void onVerifyAlgorithm(const IClient *, const Algorithm &, bool *) override {} + inline void onVerifyAlgorithm(IStrategy *, const IClient *, const Algorithm &, bool *) override {} + inline void resume() override {} int64_t submit(const JobResult &result) override; void connect() override; + void setAlgo(const Algorithm &algo) override; void stop() override; void tick(uint64_t now) override; -protected: - void onClose(Client *client, int failures) override; - void onJobReceived(Client *client, const Job &job) override; - void onLoginSuccess(Client *client) override; - void onResultAccepted(Client *client, const SubmitResult &result, const char *error) override; + void onActive(IStrategy *strategy, IClient *client) override; + void onPause(IStrategy *strategy) override; + + void onClose(IClient *client, int failures) override; + void onLogin(IClient *client, rapidjson::Document &doc, rapidjson::Value ¶ms) override; + void onLogin(IStrategy *strategy, IClient *client, rapidjson::Document &doc, rapidjson::Value ¶ms) override; + void onLoginSuccess(IClient *client) override; + + void onTimer(const Timer *timer) override; private: - void idle(uint64_t timeout); - void suspend(); + enum State { + STATE_NEW, + STATE_IDLE, + STATE_CONNECT, + STATE_ACTIVE, + STATE_WAIT + }; - static void onTimer(uv_timer_t *handle); - static void onSuspendTimer(uv_timer_t *handle); + inline State state() const { return m_state; } - bool m_active; - Client::Ptr m_client; - const int m_donateTime; - const int m_idleTime; + Client *createProxy(); + void idle(double min, double max); + void setAlgorithms(rapidjson::Document &doc, rapidjson::Value ¶ms); + void setJob(IClient *client, const Job &job); + void setResult(IClient *client, const SubmitResult &result, const char *error); + void setState(State state); + + Algorithm m_algorithm; + bool m_tls; + char m_userId[65]; + const uint64_t m_donateTime; + const uint64_t m_idleTime; + Controller *m_controller; + IClient *m_proxy; + IStrategy *m_strategy; IStrategyListener *m_listener; - uv_timer_t m_timer; + State m_state; + std::vector m_pools; + Timer *m_timer; + uint64_t m_now; + uint64_t m_timestamp; }; -#endif /* __DONATESTRATEGY_H__ */ + +} /* namespace xmrig */ + + +#endif /* XMRIG_DONATESTRATEGY_H */ diff --git a/src/net/strategies/FailoverStrategy.cpp b/src/net/strategies/FailoverStrategy.cpp deleted file mode 100644 index c8fde52c..00000000 --- a/src/net/strategies/FailoverStrategy.cpp +++ /dev/null @@ -1,148 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * - * - * 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 . - */ - - -#include "interfaces/IStrategyListener.h" -#include "net/Client.h" -#include "net/strategies/FailoverStrategy.h" -#include "Options.h" - - -FailoverStrategy::FailoverStrategy(const std::vector &urls, const char *agent, IStrategyListener *listener) : - m_active(-1), - m_index(0), - m_listener(listener) -{ - for (const Url *url : urls) { - add(url, agent); - } -} - - -int64_t FailoverStrategy::submit(const JobResult &result) -{ - return m_pools[m_active]->submit(result); -} - - -void FailoverStrategy::connect() -{ - m_pools[m_index]->connect(); -} - - -void FailoverStrategy::resume() -{ - if (!isActive()) { - return; - } - - m_listener->onJob(m_pools[m_active].get(), m_pools[m_active]->job()); -} - - -void FailoverStrategy::stop() -{ - for (size_t i = 0; i < m_pools.size(); ++i) { - m_pools[i]->disconnect(); - } - - m_index = 0; - m_active = -1; - - m_listener->onPause(this); -} - - -void FailoverStrategy::tick(uint64_t now) -{ - for (auto client : m_pools) { - client->tick(now); - } -} - - -void FailoverStrategy::onClose(Client *client, int failures) -{ - if (failures == -1) { - return; - } - - if (m_active == client->id()) { - m_active = -1; - m_listener->onPause(this); - } - - if (m_index == 0 && failures < Options::i()->retries()) { - return; - } - - if (m_index == client->id() && (m_pools.size() - m_index) > 1) { - m_pools[++m_index]->connect(); - } -} - - -void FailoverStrategy::onJobReceived(Client *client, const Job &job) -{ - if (m_active == client->id()) { - m_listener->onJob(client, job); - } -} - - -void FailoverStrategy::onLoginSuccess(Client *client) -{ - int active = m_active; - - if (client->id() == 0 || !isActive()) { - active = client->id(); - } - - for (size_t i = 1; i < m_pools.size(); ++i) { - if (active != static_cast(i)) { - m_pools[i]->disconnect(); - } - } - - if (active >= 0 && active != m_active) { - m_index = m_active = active; - m_listener->onActive(client); - } -} - - -void FailoverStrategy::onResultAccepted(Client *client, const SubmitResult &result, const char *error) -{ - m_listener->onResultAccepted(client, result, error); -} - - -void FailoverStrategy::add(const Url *url, const char *agent) -{ - Client::Ptr client = std::make_shared((int) m_pools.size(), agent, this); - client->setUrl(url); - client->setRetryPause(Options::i()->retryPause() * 1000); - - m_pools.push_back(client); -} diff --git a/src/net/strategies/FailoverStrategy.h b/src/net/strategies/FailoverStrategy.h deleted file mode 100644 index 40a486ca..00000000 --- a/src/net/strategies/FailoverStrategy.h +++ /dev/null @@ -1,69 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * - * - * 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 . - */ - -#ifndef __FAILOVERSTRATEGY_H__ -#define __FAILOVERSTRATEGY_H__ - - -#include - - -#include "interfaces/IClientListener.h" -#include "interfaces/IStrategy.h" - - -class Client; -class IStrategyListener; -class Url; - - -class FailoverStrategy : public IStrategy, public IClientListener -{ -public: - FailoverStrategy(const std::vector &urls, const char *agent, IStrategyListener *listener); - -public: - inline bool isActive() const override { return m_active >= 0; } - - int64_t submit(const JobResult &result) override; - void connect() override; - void resume() override; - void stop() override; - void tick(uint64_t now) override; - -protected: - void onClose(Client *client, int failures) override; - void onJobReceived(Client *client, const Job &job) override; - void onLoginSuccess(Client *client) override; - void onResultAccepted(Client *client, const SubmitResult &result, const char *error) override; - -private: - void add(const Url *url, const char *agent); - - int m_active; - int m_index; - IStrategyListener *m_listener; - std::vector m_pools; -}; - -#endif /* __FAILOVERSTRATEGY_H__ */ diff --git a/src/net/strategies/SinglePoolStrategy.cpp b/src/net/strategies/SinglePoolStrategy.cpp deleted file mode 100644 index 88b0dcb9..00000000 --- a/src/net/strategies/SinglePoolStrategy.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * - * - * 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 . - */ - - -#include "interfaces/IStrategyListener.h" -#include "net/Client.h" -#include "net/strategies/SinglePoolStrategy.h" -#include "Options.h" - - -SinglePoolStrategy::SinglePoolStrategy(const Url *url, const char *agent, IStrategyListener *listener) : - m_active(false), - m_listener(listener) -{ - m_client = std::make_shared(0, agent, this); - m_client->setUrl(url); - m_client->setRetryPause(Options::i()->retryPause() * 1000); -} - - -int64_t SinglePoolStrategy::submit(const JobResult &result) -{ - return m_client->submit(result); -} - - -void SinglePoolStrategy::connect() -{ - m_client->connect(); -} - - -void SinglePoolStrategy::resume() -{ - if (!isActive()) { - return; - } - - m_listener->onJob(m_client.get(), m_client->job()); -} - - -void SinglePoolStrategy::stop() -{ - m_client->disconnect(); -} - - -void SinglePoolStrategy::tick(uint64_t now) -{ - m_client->tick(now); -} - - -void SinglePoolStrategy::onClose(Client *client, int failures) -{ - if (!isActive()) { - return; - } - - m_active = false; - m_listener->onPause(this); -} - - -void SinglePoolStrategy::onJobReceived(Client *client, const Job &job) -{ - m_listener->onJob(client, job); -} - - -void SinglePoolStrategy::onLoginSuccess(Client *client) -{ - m_active = true; - m_listener->onActive(client); -} - - -void SinglePoolStrategy::onResultAccepted(Client *client, const SubmitResult &result, const char *error) -{ - m_listener->onResultAccepted(client, result, error); -} diff --git a/src/net/strategies/SinglePoolStrategy.h b/src/net/strategies/SinglePoolStrategy.h deleted file mode 100644 index 47d4f42d..00000000 --- a/src/net/strategies/SinglePoolStrategy.h +++ /dev/null @@ -1,63 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * - * - * 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 . - */ - -#ifndef __SINGLEPOOLSTRATEGY_H__ -#define __SINGLEPOOLSTRATEGY_H__ - - -#include "interfaces/IClientListener.h" -#include "interfaces/IStrategy.h" - - -class Client; -class IStrategyListener; -class Url; - - -class SinglePoolStrategy : public IStrategy, public IClientListener -{ -public: - SinglePoolStrategy(const Url *url, const char *agent, IStrategyListener *listener); - -public: - inline bool isActive() const override { return m_active; } - - int64_t submit(const JobResult &result) override; - void connect() override; - void resume() override; - void stop() override; - void tick(uint64_t now) override; - -protected: - void onClose(Client *client, int failures) override; - void onJobReceived(Client *client, const Job &job) override; - void onLoginSuccess(Client *client) override; - void onResultAccepted(Client *client, const SubmitResult &result, const char *error) override; - -private: - bool m_active; - Client::Ptr m_client; - IStrategyListener *m_listener; -}; - -#endif /* __SINGLEPOOLSTRATEGY_H__ */ diff --git a/src/version.h b/src/version.h index ff6ba77d..8064e538 100644 --- a/src/version.h +++ b/src/version.h @@ -4,9 +4,9 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * Copyright 2017- BenDr0id - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , * * 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,42 +22,26 @@ * along with this program. If not, see . */ -#ifndef __VERSION_H__ -#define __VERSION_H__ +#ifndef XMRIG_VERSION_H +#define XMRIG_VERSION_H -#ifdef XMRIG_CC_SERVER -#define APP_ID "XMRigCC" -#define APP_NAME "XMRigCC" -#define APP_DESC "XMRigCC Command'n'Control Server" -#define APP_COPYRIGHT "Copyright (C) 2017- BenDr0id" -# else #define APP_ID "XMRigCC" #define APP_NAME "XMRigCC" #define APP_DESC "XMRigCC CPU miner" -#define APP_COPYRIGHT "Copyright (C) 2017- BenDr0id" -#endif -#define APP_VERSION "1.9.5 (based on XMRig)" +#define APP_VERSION "2.0.0" #define APP_DOMAIN "" -#define APP_SITE "https://github.com/Bendr0id/xmrigCC" +#define APP_SITE "https://github.com/BenDr0id/xmrigCC/" +#define APP_COPYRIGHT "Copyright (C) 2017- XMRigCC" #define APP_KIND "cpu" -#define APP_VER_MAJOR 1 -#define APP_VER_MINOR 9 -#define APP_VER_BUILD 5 -#define APP_VER_REV 0 +#define APP_VER_MAJOR 2 +#define APP_VER_MINOR 0 +#define APP_VER_PATCH 0 #ifndef NDEBUG - #ifndef XMRIG_NO_TLS - #define BUILD_TYPE "DEBUG with TLS" - #else - #define BUILD_TYPE "DEBUG" - #endif +#define BUILD_TYPE "DEBUG" #else - #ifndef XMRIG_NO_TLS - #define BUILD_TYPE "RELEASE with TLS" - #else - #define BUILD_TYPE "RELEASE" - #endif +#define BUILD_TYPE "RELEASE" #endif #ifdef _MSC_VER @@ -76,17 +60,9 @@ # else # define MSVC_VERSION 0 # endif -#include -#else - #if defined(__FreeBSD__) || defined(__APPLE__) - #include - #else - #include - #endif #endif - - +#include class Version { @@ -94,16 +70,17 @@ public: inline static std::string string() { std::string version = std::to_string(APP_VER_MAJOR) + std::string(".") + std::to_string(APP_VER_MINOR) + - std::string(".") + std::to_string(APP_VER_BUILD); + std::string(".") + std::to_string(APP_VER_PATCH); return version; } inline static int code() { - std::string version = std::to_string(APP_VER_MAJOR) + std::to_string(APP_VER_MINOR) + std::to_string(APP_VER_BUILD); + std::string version = std::to_string(APP_VER_MAJOR) + std::to_string(APP_VER_MINOR) + std::to_string(APP_VER_PATCH); return std::stoi(version); } }; -#endif /* __VERSION_H__ */ + +#endif /* XMRIG_VERSION_H */ diff --git a/src/win_dirent.h b/src/win_dirent.h deleted file mode 100644 index 0958d417..00000000 --- a/src/win_dirent.h +++ /dev/null @@ -1,1224 +0,0 @@ -/* - * Dirent interface for Microsoft Visual Studio - * - * Copyright (C) 2006-2012 Toni Ronkko - * This file is part of dirent. Dirent may be freely distributed - * under the MIT license. For all details and documentation, see - * https://github.com/tronkko/dirent - */ -#ifndef WIN_DIRENT_H -#define WIN_DIRENT_H - -/* - * Include windows.h without Windows Sockets 1.1 to prevent conflicts with - * Windows Sockets 2.0. - */ -#ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -#endif -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* Indicates that d_type field is available in dirent structure */ -#define _DIRENT_HAVE_D_TYPE - -/* Indicates that d_namlen field is available in dirent structure */ -#define _DIRENT_HAVE_D_NAMLEN - -/* Entries missing from MSVC 6.0 */ -#if !defined(FILE_ATTRIBUTE_DEVICE) -# define FILE_ATTRIBUTE_DEVICE 0x40 -#endif - -/* File type and permission flags for stat(), general mask */ -#if !defined(S_IFMT) -# define S_IFMT _S_IFMT -#endif - -/* Directory bit */ -#if !defined(S_IFDIR) -# define S_IFDIR _S_IFDIR -#endif - -/* Character device bit */ -#if !defined(S_IFCHR) -# define S_IFCHR _S_IFCHR -#endif - -/* Pipe bit */ -#if !defined(S_IFFIFO) -# define S_IFFIFO _S_IFFIFO -#endif - -/* Regular file bit */ -#if !defined(S_IFREG) -# define S_IFREG _S_IFREG -#endif - -/* Read permission */ -#if !defined(S_IREAD) -# define S_IREAD _S_IREAD -#endif - -/* Write permission */ -#if !defined(S_IWRITE) -# define S_IWRITE _S_IWRITE -#endif - -/* Execute permission */ -#if !defined(S_IEXEC) -# define S_IEXEC _S_IEXEC -#endif - -/* Pipe */ -#if !defined(S_IFIFO) -# define S_IFIFO _S_IFIFO -#endif - -/* Block device */ -#if !defined(S_IFBLK) -# define S_IFBLK 0 -#endif - -/* Link */ -#if !defined(S_IFLNK) -# define S_IFLNK 0 -#endif - -/* Socket */ -#if !defined(S_IFSOCK) -# define S_IFSOCK 0 -#endif - -/* Read user permission */ -#if !defined(S_IRUSR) -# define S_IRUSR S_IREAD -#endif - -/* Write user permission */ -#if !defined(S_IWUSR) -# define S_IWUSR S_IWRITE -#endif - -/* Execute user permission */ -#if !defined(S_IXUSR) -# define S_IXUSR 0 -#endif - -/* Read group permission */ -#if !defined(S_IRGRP) -# define S_IRGRP 0 -#endif - -/* Write group permission */ -#if !defined(S_IWGRP) -# define S_IWGRP 0 -#endif - -/* Execute group permission */ -#if !defined(S_IXGRP) -# define S_IXGRP 0 -#endif - -/* Read others permission */ -#if !defined(S_IROTH) -# define S_IROTH 0 -#endif - -/* Write others permission */ -#if !defined(S_IWOTH) -# define S_IWOTH 0 -#endif - -/* Execute others permission */ -#if !defined(S_IXOTH) -# define S_IXOTH 0 -#endif - -/* Maximum length of file name */ -#if !defined(PATH_MAX) -# define PATH_MAX MAX_PATH -#endif -#if !defined(FILENAME_MAX) -# define FILENAME_MAX MAX_PATH -#endif -#if !defined(NAME_MAX) -# define NAME_MAX FILENAME_MAX -#endif - -/* File type flags for d_type */ -#define DT_UNKNOWN 0 -#define DT_REG S_IFREG -#define DT_DIR S_IFDIR -#define DT_FIFO S_IFIFO -#define DT_SOCK S_IFSOCK -#define DT_CHR S_IFCHR -#define DT_BLK S_IFBLK -#define DT_LNK S_IFLNK - -/* Macros for converting between st_mode and d_type */ -#define IFTODT(mode) ((mode) & S_IFMT) -#define DTTOIF(type) (type) - -/* - * File type macros. Note that block devices, sockets and links cannot be - * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are - * only defined for compatibility. These macros should always return false - * on Windows. - */ -#if !defined(S_ISFIFO) -# define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO) -#endif -#if !defined(S_ISDIR) -# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) -#endif -#if !defined(S_ISREG) -# define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) -#endif -#if !defined(S_ISLNK) -# define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) -#endif -#if !defined(S_ISSOCK) -# define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) -#endif -#if !defined(S_ISCHR) -# define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) -#endif -#if !defined(S_ISBLK) -# define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) -#endif - -/* Return the exact length of the file name without zero terminator */ -#define _D_EXACT_NAMLEN(p) ((p)->d_namlen) - -/* Return the maximum size of a file name */ -#define _D_ALLOC_NAMLEN(p) ((PATH_MAX)+1) - - -#ifdef __cplusplus -extern "C" { -#endif - - -/* Wide-character version */ -struct _wdirent { - /* Always zero */ - long d_ino; - - /* File position within stream */ - long d_off; - - /* Structure size */ - unsigned short d_reclen; - - /* Length of name without \0 */ - size_t d_namlen; - - /* File type */ - int d_type; - - /* File name */ - wchar_t d_name[PATH_MAX+1]; -}; -typedef struct _wdirent _wdirent; - -struct _WDIR { - /* Current directory entry */ - struct _wdirent ent; - - /* Private file data */ - WIN32_FIND_DATAW data; - - /* True if data is valid */ - int cached; - - /* Win32 search handle */ - HANDLE handle; - - /* Initial directory name */ - wchar_t *patt; -}; -typedef struct _WDIR _WDIR; - -/* Multi-byte character version */ -struct dirent { - /* Always zero */ - long d_ino; - - /* File position within stream */ - long d_off; - - /* Structure size */ - unsigned short d_reclen; - - /* Length of name without \0 */ - size_t d_namlen; - - /* File type */ - int d_type; - - /* File name */ - char d_name[PATH_MAX+1]; -}; -typedef struct dirent dirent; - -struct DIR { - struct dirent ent; - struct _WDIR *wdirp; -}; -typedef struct DIR DIR; - - -/* Dirent functions */ -static DIR *opendir (const char *dirname); -static _WDIR *_wopendir (const wchar_t *dirname); - -static struct dirent *readdir (DIR *dirp); -static struct _wdirent *_wreaddir (_WDIR *dirp); - -static int readdir_r( - DIR *dirp, struct dirent *entry, struct dirent **result); -static int _wreaddir_r( - _WDIR *dirp, struct _wdirent *entry, struct _wdirent **result); - -static int closedir (DIR *dirp); -static int _wclosedir (_WDIR *dirp); - -static void rewinddir (DIR* dirp); -static void _wrewinddir (_WDIR* dirp); - -static int scandir (const char *dirname, struct dirent ***namelist, - int (*filter)(const struct dirent*), - int (*compare)(const struct dirent**, const struct dirent**)); - -static int alphasort (const struct dirent **a, const struct dirent **b); - -static int versionsort (const struct dirent **a, const struct dirent **b); - - -/* For compatibility with Symbian */ -#define wdirent _wdirent -#define WDIR _WDIR -#define wopendir _wopendir -#define wreaddir _wreaddir -#define wclosedir _wclosedir -#define wrewinddir _wrewinddir - - -/* Internal utility functions */ -static WIN32_FIND_DATAW *dirent_first (_WDIR *dirp); -static WIN32_FIND_DATAW *dirent_next (_WDIR *dirp); - -static int dirent_mbstowcs_s( - size_t *pReturnValue, - wchar_t *wcstr, - size_t sizeInWords, - const char *mbstr, - size_t count); - -static int dirent_wcstombs_s( - size_t *pReturnValue, - char *mbstr, - size_t sizeInBytes, - const wchar_t *wcstr, - size_t count); - -static void dirent_set_errno (int error); - - -/* - * Open directory stream DIRNAME for read and return a pointer to the - * internal working area that is used to retrieve individual directory - * entries. - */ -static _WDIR* -_wopendir( - const wchar_t *dirname) -{ - _WDIR *dirp = NULL; - int error; - - /* Must have directory name */ - if (dirname == NULL || dirname[0] == '\0') { - dirent_set_errno (ENOENT); - return NULL; - } - - /* Allocate new _WDIR structure */ - dirp = (_WDIR*) malloc (sizeof (struct _WDIR)); - if (dirp != NULL) { - DWORD n; - - /* Reset _WDIR structure */ - dirp->handle = INVALID_HANDLE_VALUE; - dirp->patt = NULL; - dirp->cached = 0; - - /* Compute the length of full path plus zero terminator - * - * Note that on WinRT there's no way to convert relative paths - * into absolute paths, so just assume it is an absolute path. - */ -# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) - n = wcslen(dirname); -# else - n = GetFullPathNameW (dirname, 0, NULL, NULL); -# endif - - /* Allocate room for absolute directory name and search pattern */ - dirp->patt = (wchar_t*) malloc (sizeof (wchar_t) * n + 16); - if (dirp->patt) { - - /* - * Convert relative directory name to an absolute one. This - * allows rewinddir() to function correctly even when current - * working directory is changed between opendir() and rewinddir(). - * - * Note that on WinRT there's no way to convert relative paths - * into absolute paths, so just assume it is an absolute path. - */ -# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) - wcsncpy_s(dirp->patt, n+1, dirname, n); -# else - n = GetFullPathNameW (dirname, n, dirp->patt, NULL); -# endif - if (n > 0) { - wchar_t *p; - - /* Append search pattern \* to the directory name */ - p = dirp->patt + n; - if (dirp->patt < p) { - switch (p[-1]) { - case '\\': - case '/': - case ':': - /* Directory ends in path separator, e.g. c:\temp\ */ - /*NOP*/; - break; - - default: - /* Directory name doesn't end in path separator */ - *p++ = '\\'; - } - } - *p++ = '*'; - *p = '\0'; - - /* Open directory stream and retrieve the first entry */ - if (dirent_first (dirp)) { - /* Directory stream opened successfully */ - error = 0; - } else { - /* Cannot retrieve first entry */ - error = 1; - dirent_set_errno (ENOENT); - } - - } else { - /* Cannot retrieve full path name */ - dirent_set_errno (ENOENT); - error = 1; - } - - } else { - /* Cannot allocate memory for search pattern */ - error = 1; - } - - } else { - /* Cannot allocate _WDIR structure */ - error = 1; - } - - /* Clean up in case of error */ - if (error && dirp) { - _wclosedir (dirp); - dirp = NULL; - } - - return dirp; -} - -/* - * Read next directory entry. - * - * Returns pointer to static directory entry which may be overwritten by - * subsequent calls to _wreaddir(). - */ -static struct _wdirent* -_wreaddir( - _WDIR *dirp) -{ - struct _wdirent *entry; - - /* - * Read directory entry to buffer. We can safely ignore the return value - * as entry will be set to NULL in case of error. - */ - (void) _wreaddir_r (dirp, &dirp->ent, &entry); - - /* Return pointer to statically allocated directory entry */ - return entry; -} - -/* - * Read next directory entry. - * - * Returns zero on success. If end of directory stream is reached, then sets - * result to NULL and returns zero. - */ -static int -_wreaddir_r( - _WDIR *dirp, - struct _wdirent *entry, - struct _wdirent **result) -{ - WIN32_FIND_DATAW *datap; - - /* Read next directory entry */ - datap = dirent_next (dirp); - if (datap) { - size_t n; - DWORD attr; - - /* - * Copy file name as wide-character string. If the file name is too - * long to fit in to the destination buffer, then truncate file name - * to PATH_MAX characters and zero-terminate the buffer. - */ - n = 0; - while (n < PATH_MAX && datap->cFileName[n] != 0) { - entry->d_name[n] = datap->cFileName[n]; - n++; - } - entry->d_name[n] = 0; - - /* Length of file name excluding zero terminator */ - entry->d_namlen = n; - - /* File type */ - attr = datap->dwFileAttributes; - if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { - entry->d_type = DT_CHR; - } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { - entry->d_type = DT_DIR; - } else { - entry->d_type = DT_REG; - } - - /* Reset dummy fields */ - entry->d_ino = 0; - entry->d_off = 0; - entry->d_reclen = sizeof (struct _wdirent); - - /* Set result address */ - *result = entry; - - } else { - - /* Return NULL to indicate end of directory */ - *result = NULL; - - } - - return /*OK*/0; -} - -/* - * Close directory stream opened by opendir() function. This invalidates the - * DIR structure as well as any directory entry read previously by - * _wreaddir(). - */ -static int -_wclosedir( - _WDIR *dirp) -{ - int ok; - if (dirp) { - - /* Release search handle */ - if (dirp->handle != INVALID_HANDLE_VALUE) { - FindClose (dirp->handle); - dirp->handle = INVALID_HANDLE_VALUE; - } - - /* Release search pattern */ - if (dirp->patt) { - free (dirp->patt); - dirp->patt = NULL; - } - - /* Release directory structure */ - free (dirp); - ok = /*success*/0; - - } else { - - /* Invalid directory stream */ - dirent_set_errno (EBADF); - ok = /*failure*/-1; - - } - return ok; -} - -/* - * Rewind directory stream such that _wreaddir() returns the very first - * file name again. - */ -static void -_wrewinddir( - _WDIR* dirp) -{ - if (dirp) { - /* Release existing search handle */ - if (dirp->handle != INVALID_HANDLE_VALUE) { - FindClose (dirp->handle); - } - - /* Open new search handle */ - dirent_first (dirp); - } -} - -/* Get first directory entry (internal) */ -static WIN32_FIND_DATAW* -dirent_first( - _WDIR *dirp) -{ - WIN32_FIND_DATAW *datap; - - /* Open directory and retrieve the first entry */ - dirp->handle = FindFirstFileExW( - dirp->patt, FindExInfoStandard, &dirp->data, - FindExSearchNameMatch, NULL, 0); - if (dirp->handle != INVALID_HANDLE_VALUE) { - - /* a directory entry is now waiting in memory */ - datap = &dirp->data; - dirp->cached = 1; - - } else { - - /* Failed to re-open directory: no directory entry in memory */ - dirp->cached = 0; - datap = NULL; - - } - return datap; -} - -/* - * Get next directory entry (internal). - * - * Returns - */ -static WIN32_FIND_DATAW* -dirent_next( - _WDIR *dirp) -{ - WIN32_FIND_DATAW *p; - - /* Get next directory entry */ - if (dirp->cached != 0) { - - /* A valid directory entry already in memory */ - p = &dirp->data; - dirp->cached = 0; - - } else if (dirp->handle != INVALID_HANDLE_VALUE) { - - /* Get the next directory entry from stream */ - if (FindNextFileW (dirp->handle, &dirp->data) != FALSE) { - /* Got a file */ - p = &dirp->data; - } else { - /* The very last entry has been processed or an error occurred */ - FindClose (dirp->handle); - dirp->handle = INVALID_HANDLE_VALUE; - p = NULL; - } - - } else { - - /* End of directory stream reached */ - p = NULL; - - } - - return p; -} - -/* - * Open directory stream using plain old C-string. - */ -static DIR* -opendir( - const char *dirname) -{ - struct DIR *dirp; - int error; - - /* Must have directory name */ - if (dirname == NULL || dirname[0] == '\0') { - dirent_set_errno (ENOENT); - return NULL; - } - - /* Allocate memory for DIR structure */ - dirp = (DIR*) malloc (sizeof (struct DIR)); - if (dirp) { - wchar_t wname[PATH_MAX + 1]; - size_t n; - - /* Convert directory name to wide-character string */ - error = dirent_mbstowcs_s( - &n, wname, PATH_MAX + 1, dirname, PATH_MAX + 1); - if (!error) { - - /* Open directory stream using wide-character name */ - dirp->wdirp = _wopendir (wname); - if (dirp->wdirp) { - /* Directory stream opened */ - error = 0; - } else { - /* Failed to open directory stream */ - error = 1; - } - - } else { - /* - * Cannot convert file name to wide-character string. This - * occurs if the string contains invalid multi-byte sequences or - * the output buffer is too small to contain the resulting - * string. - */ - error = 1; - } - - } else { - /* Cannot allocate DIR structure */ - error = 1; - } - - /* Clean up in case of error */ - if (error && dirp) { - free (dirp); - dirp = NULL; - } - - return dirp; -} - -/* - * Read next directory entry. - */ -static struct dirent* -readdir( - DIR *dirp) -{ - struct dirent *entry; - - /* - * Read directory entry to buffer. We can safely ignore the return value - * as entry will be set to NULL in case of error. - */ - (void) readdir_r (dirp, &dirp->ent, &entry); - - /* Return pointer to statically allocated directory entry */ - return entry; -} - -/* - * Read next directory entry into called-allocated buffer. - * - * Returns zero on success. If the end of directory stream is reached, then - * sets result to NULL and returns zero. - */ -static int -readdir_r( - DIR *dirp, - struct dirent *entry, - struct dirent **result) -{ - WIN32_FIND_DATAW *datap; - - /* Read next directory entry */ - datap = dirent_next (dirp->wdirp); - if (datap) { - size_t n; - int error; - - /* Attempt to convert file name to multi-byte string */ - error = dirent_wcstombs_s( - &n, entry->d_name, PATH_MAX + 1, datap->cFileName, PATH_MAX + 1); - - /* - * If the file name cannot be represented by a multi-byte string, - * then attempt to use old 8+3 file name. This allows traditional - * Unix-code to access some file names despite of unicode - * characters, although file names may seem unfamiliar to the user. - * - * Be ware that the code below cannot come up with a short file - * name unless the file system provides one. At least - * VirtualBox shared folders fail to do this. - */ - if (error && datap->cAlternateFileName[0] != '\0') { - error = dirent_wcstombs_s( - &n, entry->d_name, PATH_MAX + 1, - datap->cAlternateFileName, PATH_MAX + 1); - } - - if (!error) { - DWORD attr; - - /* Length of file name excluding zero terminator */ - entry->d_namlen = n - 1; - - /* File attributes */ - attr = datap->dwFileAttributes; - if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { - entry->d_type = DT_CHR; - } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { - entry->d_type = DT_DIR; - } else { - entry->d_type = DT_REG; - } - - /* Reset dummy fields */ - entry->d_ino = 0; - entry->d_off = 0; - entry->d_reclen = sizeof (struct dirent); - - } else { - - /* - * Cannot convert file name to multi-byte string so construct - * an erroneous directory entry and return that. Note that - * we cannot return NULL as that would stop the processing - * of directory entries completely. - */ - entry->d_name[0] = '?'; - entry->d_name[1] = '\0'; - entry->d_namlen = 1; - entry->d_type = DT_UNKNOWN; - entry->d_ino = 0; - entry->d_off = -1; - entry->d_reclen = 0; - - } - - /* Return pointer to directory entry */ - *result = entry; - - } else { - - /* No more directory entries */ - *result = NULL; - - } - - return /*OK*/0; -} - -/* - * Close directory stream. - */ -static int -closedir( - DIR *dirp) -{ - int ok; - if (dirp) { - - /* Close wide-character directory stream */ - ok = _wclosedir (dirp->wdirp); - dirp->wdirp = NULL; - - /* Release multi-byte character version */ - free (dirp); - - } else { - - /* Invalid directory stream */ - dirent_set_errno (EBADF); - ok = /*failure*/-1; - - } - return ok; -} - -/* - * Rewind directory stream to beginning. - */ -static void -rewinddir( - DIR* dirp) -{ - /* Rewind wide-character string directory stream */ - _wrewinddir (dirp->wdirp); -} - -/* - * Scan directory for entries. - */ -static int -scandir( - const char *dirname, - struct dirent ***namelist, - int (*filter)(const struct dirent*), - int (*compare)(const struct dirent**, const struct dirent**)) -{ - struct dirent **files = NULL; - size_t size = 0; - size_t allocated = 0; - const size_t init_size = 1; - DIR *dir = NULL; - struct dirent *entry; - struct dirent *tmp = NULL; - size_t i; - int result = 0; - - /* Open directory stream */ - dir = opendir (dirname); - if (dir) { - - /* Read directory entries to memory */ - while (1) { - - /* Enlarge pointer table to make room for another pointer */ - if (size >= allocated) { - void *p; - size_t num_entries; - - /* Compute number of entries in the enlarged pointer table */ - if (size < init_size) { - /* Allocate initial pointer table */ - num_entries = init_size; - } else { - /* Double the size */ - num_entries = size * 2; - } - - /* Allocate first pointer table or enlarge existing table */ - p = realloc (files, sizeof (void*) * num_entries); - if (p != NULL) { - /* Got the memory */ - files = (dirent**) p; - allocated = num_entries; - } else { - /* Out of memory */ - result = -1; - break; - } - - } - - /* Allocate room for temporary directory entry */ - if (tmp == NULL) { - tmp = (struct dirent*) malloc (sizeof (struct dirent)); - if (tmp == NULL) { - /* Cannot allocate temporary directory entry */ - result = -1; - break; - } - } - - /* Read directory entry to temporary area */ - if (readdir_r (dir, tmp, &entry) == /*OK*/0) { - - /* Did we get an entry? */ - if (entry != NULL) { - int pass; - - /* Determine whether to include the entry in result */ - if (filter) { - /* Let the filter function decide */ - pass = filter (tmp); - } else { - /* No filter function, include everything */ - pass = 1; - } - - if (pass) { - /* Store the temporary entry to pointer table */ - files[size++] = tmp; - tmp = NULL; - - /* Keep up with the number of files */ - result++; - } - - } else { - - /* - * End of directory stream reached => sort entries and - * exit. - */ - qsort (files, size, sizeof (void*), - (int (*) (const void*, const void*)) compare); - break; - - } - - } else { - /* Error reading directory entry */ - result = /*Error*/ -1; - break; - } - - } - - } else { - /* Cannot open directory */ - result = /*Error*/ -1; - } - - /* Release temporary directory entry */ - if (tmp) { - free (tmp); - } - - /* Release allocated memory on error */ - if (result < 0) { - for (i = 0; i < size; i++) { - free (files[i]); - } - free (files); - files = NULL; - } - - /* Close directory stream */ - if (dir) { - closedir (dir); - } - - /* Pass pointer table to caller */ - if (namelist) { - *namelist = files; - } - return result; -} - -/* Alphabetical sorting */ -static int -alphasort( - const struct dirent **a, const struct dirent **b) -{ - return strcoll ((*a)->d_name, (*b)->d_name); -} - -/* Sort versions */ -static int -versionsort( - const struct dirent **a, const struct dirent **b) -{ - /* FIXME: implement strverscmp and use that */ - return alphasort (a, b); -} - -/* Convert multi-byte string to wide character string */ -static int -dirent_mbstowcs_s( - size_t *pReturnValue, - wchar_t *wcstr, - size_t sizeInWords, - const char *mbstr, - size_t count) -{ - int error; - int n; - size_t len; - UINT cp; - DWORD flags; - - /* Determine code page for multi-byte string */ - if (AreFileApisANSI ()) { - /* Default ANSI code page */ - cp = GetACP (); - } else { - /* Default OEM code page */ - cp = GetOEMCP (); - } - - /* - * Determine flags based on the character set. For more information, - * please see https://docs.microsoft.com/fi-fi/windows/desktop/api/stringapiset/nf-stringapiset-multibytetowidechar - */ - switch (cp) { - case 42: - case 50220: - case 50221: - case 50222: - case 50225: - case 50227: - case 50229: - case 57002: - case 57003: - case 57004: - case 57005: - case 57006: - case 57007: - case 57008: - case 57009: - case 57010: - case 57011: - case 65000: - /* MultiByteToWideChar does not support MB_ERR_INVALID_CHARS */ - flags = 0; - break; - - default: - /* - * Ask MultiByteToWideChar to return an error if a multi-byte - * character cannot be converted to a wide-character. - */ - flags = MB_ERR_INVALID_CHARS; - } - - /* Compute the length of input string without zero-terminator */ - len = 0; - while (mbstr[len] != '\0' && len < count) { - len++; - } - - /* Convert to wide-character string */ - n = MultiByteToWideChar( - /* Source code page */ cp, - /* Flags */ flags, - /* Pointer to string to convert */ mbstr, - /* Size of multi-byte string */ (int) len, - /* Pointer to output buffer */ wcstr, - /* Size of output buffer */ sizeInWords - 1 - ); - - /* Ensure that output buffer is zero-terminated */ - wcstr[n] = '\0'; - - /* Return length of wide-character string with zero-terminator */ - *pReturnValue = (size_t) (n + 1); - - /* Return zero if conversion succeeded */ - if (n > 0) { - error = 0; - } else { - error = 1; - } - return error; -} - -/* Convert wide-character string to multi-byte string */ -static int -dirent_wcstombs_s( - size_t *pReturnValue, - char *mbstr, - size_t sizeInBytes, /* max size of mbstr */ - const wchar_t *wcstr, - size_t count) -{ - int n; - int error; - UINT cp; - size_t len; - BOOL flag = 0; - LPBOOL pflag; - - /* Determine code page for multi-byte string */ - if (AreFileApisANSI ()) { - /* Default ANSI code page */ - cp = GetACP (); - } else { - /* Default OEM code page */ - cp = GetOEMCP (); - } - - /* Compute the length of input string without zero-terminator */ - len = 0; - while (wcstr[len] != '\0' && len < count) { - len++; - } - - /* - * Determine if we can ask WideCharToMultiByte to return information on - * broken characters. For more information, please see - * https://docs.microsoft.com/en-us/windows/desktop/api/stringapiset/nf-stringapiset-widechartomultibyte - */ - switch (cp) { - case CP_UTF7: - case CP_UTF8: - /* - * WideCharToMultiByte fails if we request information on default - * characters. This is just a nuisance but doesn't affect the - * conversion: if Windows is configured to use UTF-8, then the default - * character should not be needed anyway. - */ - pflag = NULL; - break; - - default: - /* - * Request that WideCharToMultiByte sets the flag if it uses the - * default character. - */ - pflag = &flag; - } - - /* Convert wide-character string to multi-byte character string */ - n = WideCharToMultiByte( - /* Target code page */ cp, - /* Flags */ 0, - /* Pointer to unicode string */ wcstr, - /* Length of unicode string */ (int) len, - /* Pointer to output buffer */ mbstr, - /* Size of output buffer */ sizeInBytes - 1, - /* Default character */ NULL, - /* Whether default character was used or not */ pflag - ); - - /* Ensure that output buffer is zero-terminated */ - mbstr[n] = '\0'; - - /* Return length of multi-byte string with zero-terminator */ - *pReturnValue = (size_t) (n + 1); - - /* Return zero if conversion succeeded without using default characters */ - if (n > 0 && flag == 0) { - error = 0; - } else { - error = 1; - } - return error; -} - -/* Set errno variable */ -static void -dirent_set_errno( - int error) -{ -#if defined(_MSC_VER) && _MSC_VER >= 1400 - - /* Microsoft Visual Studio 2005 and later */ - _set_errno (error); - -#else - - /* Non-Microsoft compiler or older Microsoft compiler */ - errno = error; - -#endif -} - - -#ifdef __cplusplus -} -#endif -#endif /*WIN_DIRENT_H*/ \ No newline at end of file diff --git a/src/workers/MultiWorker.cpp b/src/workers/MultiWorker.cpp deleted file mode 100644 index 7e2709f4..00000000 --- a/src/workers/MultiWorker.cpp +++ /dev/null @@ -1,209 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * Copyright 2018 Sebastian Stolzenberg - * - * - * 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 . - */ - - -#include -#include - - -#include "crypto/HashSelector.h" -#include "workers/MultiWorker.h" -#include "workers/Workers.h" -#include "Mem.h" - - -class MultiWorker : public Worker -{ -public: - explicit MultiWorker(Handle *handle, size_t hashFactor); - ~MultiWorker(); - - void start() override; - -private: - bool resume(const Job &job); - void consumeJob(); - void save(const Job &job); - - class State; - - uint8_t* m_hash; - State *m_state; - State *m_pausedState; - size_t m_hashFactor; - - ScratchPadMem scratchPadMem; - ScratchPad* scratchPads[MAX_NUM_HASH_BLOCKS]; -}; - -class MultiWorker::State -{ -public: - State(size_t hashMultiplier) - { - nonces = new uint32_t[hashMultiplier]; - blob = new uint8_t[MAX_BLOB_SIZE * hashMultiplier]; - - for(size_t i=0; i ONE_MB ? scratchPadMem.realSize / ONE_MB : scratchPadMem.realSize / 1024; - - if (Options::i()->colors()) { - LOG_INFO(WHITE_BOLD("Starting thread ") GREEN_BOLD("%zu/%zu") " affined to core: " GREEN_BOLD("#%d") " -> huge pages:" GREEN_BOLD(" %s%zu/%zu") " scratchpad: " CYAN_BOLD("%zu.0 %s"), - m_id+1, Options::i()->threads(), m_affinedCpu, - (scratchPadMem.hugePages == scratchPadMem.pages ? "\x1B[1;32m" : (scratchPadMem.hugePages == 0 ? "\x1B[1;31m" : "\x1B[1;33m")), - scratchPadMem.hugePages, scratchPadMem.pages, memory, scratchPadMem.realSize > ONE_MB ? "MB" : "KB"); - } - else { - LOG_INFO("Starting thread %zu/%zu affined to core: #%d -> huge pages: %zu/%zu scratchpad: %zu.0 %s", - m_id+1, Options::i()->threads(), m_affinedCpu, scratchPadMem.hugePages, scratchPadMem.pages, memory, - scratchPadMem.realSize > ONE_MB ? "MB" : "KB"); - } - - while (Workers::sequence() > 0) { - if (Workers::isPaused()) { - do { - std::this_thread::sleep_for(std::chrono::milliseconds(200)); - } - while (Workers::isPaused()); - - if (Workers::sequence() == 0) { - break; - } - - consumeJob(); - } - - while (!Workers::isOutdated(m_sequence)) { - if ((m_count & 0xF) == 0) { - storeStats(); - } - - m_count += m_hashFactor; - - for (size_t i=0; i < m_hashFactor; ++i) { - *Job::nonce(m_state->blob + i * m_state->job.size()) = ++m_state->nonces[i]; - } - - HashSelector::hash(m_hashFactor, Options::i()->asmOptimization(), m_state->job.height(), m_state->job.powVariant(), m_state->blob, m_state->job.size(), m_hash, scratchPads); - - for (size_t i=0; i < m_hashFactor; ++i) { - if (*reinterpret_cast(m_hash + 24 + i * 32) < m_state->job.target()) { - Workers::submit(JobResult(m_state->job.poolId(), m_state->job.id(), m_state->nonces[i], m_hash + i * 32, - m_state->job.diff()), m_id); - } - } - - std::this_thread::yield(); - } - - consumeJob(); - } -} - -bool MultiWorker::resume(const Job &job) -{ - if (m_state->job.poolId() == -1 && job.poolId() >= 0 && job.id() == m_pausedState->job.id()) { - *m_state = *m_pausedState; - return true; - } - - return false; -} - -void MultiWorker::consumeJob() -{ - Job job = Workers::job(); - m_sequence = Workers::sequence(); - if (m_state->job == job) { - return; - } - - save(job); - - if (resume(job)) { - return; - } - - m_state->job = std::move(job); - - for (size_t i=0; i < m_hashFactor; ++i) { - memcpy(m_state->blob + i * m_state->job.size(), m_state->job.blob(), m_state->job.size()); - if (m_state->job.isNicehash()) { - m_state->nonces[i] = (*Job::nonce(m_state->blob + i * m_state->job.size()) & 0xff000000U) + - (0xffffffU / (m_threads * Mem::hashFactor()) * (m_id + i * m_threads)); - } - else { - m_state->nonces[i] = std::numeric_limits::max() / (m_threads * - Mem::hashFactor()) * - (m_id + i * m_threads); - } - } -} - -void MultiWorker::save(const Job &job) -{ - if (job.poolId() == -1 && m_state->job.poolId() >= 0) { - *m_pausedState = *m_state; - } -} - -Worker* createMultiWorker(Handle *handle, size_t hashFactor) { - return new MultiWorker(handle, hashFactor); -} \ No newline at end of file diff --git a/src/workers/MultiWorker.h b/src/workers/MultiWorker.h deleted file mode 100644 index 14b3d13d..00000000 --- a/src/workers/MultiWorker.h +++ /dev/null @@ -1,39 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * Copyright 2018 Sebastian Stolzenberg - * - * - * 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 . - */ - -#ifndef __MULTIWORKER_H__ -#define __MULTIWORKER_H__ - - -#include "net/Job.h" -#include "net/JobResult.h" -#include "workers/Worker.h" - - -class Handle; - -Worker* createMultiWorker(Handle *handle, size_t hashFactor); - - -#endif /* __SINGLEWORKER_H__ */ diff --git a/src/workers/Workers.cpp b/src/workers/Workers.cpp deleted file mode 100644 index ef786842..00000000 --- a/src/workers/Workers.cpp +++ /dev/null @@ -1,198 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * Copyright 2017- BenDr0id - * - * - * 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 . - */ - -#include -#include -#include - - -#include "api/Api.h" -#include "interfaces/IJobResultListener.h" -#include "Mem.h" -#include "workers/MultiWorker.h" -#include "workers/Handle.h" -#include "workers/Hashrate.h" -#include "workers/Workers.h" - - -bool Workers::m_active = false; -bool Workers::m_enabled = true; -Hashrate *Workers::m_hashrate = nullptr; -IJobResultListener *Workers::m_listener = nullptr; -Job Workers::m_job; -std::atomic Workers::m_paused; -std::atomic Workers::m_sequence; -std::list Workers::m_queue; -std::vector Workers::m_workers; -uint64_t Workers::m_ticks = 0; -uv_async_t Workers::m_async; -uv_mutex_t Workers::m_mutex; -uv_rwlock_t Workers::m_rwlock; -uv_timer_t Workers::m_timer; - - -Job Workers::job() -{ - uv_rwlock_rdlock(&m_rwlock); - Job job = m_job; - uv_rwlock_rdunlock(&m_rwlock); - - return job; -} - - -void Workers::printHashrate(bool detail) -{ - m_hashrate->print(); -} - - -void Workers::setEnabled(bool enabled) -{ - if (m_enabled == enabled) { - return; - } - - m_enabled = enabled; - if (!m_active) { - return; - } - - m_paused = enabled ? 0 : 1; - m_sequence++; -} - - -void Workers::setJob(const Job &job) -{ - uv_rwlock_wrlock(&m_rwlock); - m_job = job; - uv_rwlock_wrunlock(&m_rwlock); - - m_active = true; - if (!m_enabled) { - return; - } - - m_sequence++; - m_paused = 0; -} - - -void Workers::start(size_t threads, int64_t affinityMask, int priority) -{ - m_hashrate = new Hashrate(threads); - - uv_mutex_init(&m_mutex); - uv_rwlock_init(&m_rwlock); - - m_sequence = 1; - m_paused = 1; - - uv_async_init(uv_default_loop(), &m_async, Workers::onResult); - uv_timer_init(uv_default_loop(), &m_timer); - uv_timer_start(&m_timer, Workers::onTick, 500, 500); - - for (size_t i = 0; i < threads; ++i) { - auto handle = new Handle(i, threads, affinityMask, priority); - m_workers.push_back(handle); - handle->start(Workers::onReady); - } -} - - -void Workers::stop() -{ - uv_timer_stop(&m_timer); - m_hashrate->stop(); - - uv_close(reinterpret_cast(&m_async), nullptr); - m_paused = 0; - m_sequence = 0; - - for (auto worker : m_workers) { - worker->join(); - } -} - - -void Workers::submit(const JobResult &result, int threadId) -{ - uv_mutex_lock(&m_mutex); - m_queue.push_back(result); - uv_mutex_unlock(&m_mutex); - - uv_async_send(&m_async); -} - - -void Workers::onReady(void *arg) -{ - auto handle = static_cast(arg); - handle->setWorker(createMultiWorker(handle, Mem::getThreadHashFactor(handle->threadId()))); - handle->worker()->start(); -} - - -void Workers::onResult(uv_async_t *handle) -{ - std::list results; - - uv_mutex_lock(&m_mutex); - while (!m_queue.empty()) { - results.push_back(std::move(m_queue.front())); - m_queue.pop_front(); - } - uv_mutex_unlock(&m_mutex); - - for (auto result : results) { - m_listener->onJobResult(result); - } - - results.clear(); -} - - -void Workers::onTick(uv_timer_t *handle) -{ - for (auto workerHandle : m_workers) { - if (!workerHandle->worker()) { - return; - } - - m_hashrate->add(workerHandle->threadId(), workerHandle->worker()->hashCount(), workerHandle->worker()->timestamp()); - } - - if ((m_ticks++ & 0xF) == 0) { - m_hashrate->updateHighest(); - } - -# ifndef XMRIG_NO_API - Api::tick(m_hashrate); -# endif - -# ifndef XMRIG_NO_CC - CCClient::updateHashrate(m_hashrate); -# endif -} diff --git a/src/workers/Workers.h b/src/workers/Workers.h deleted file mode 100644 index c21f5564..00000000 --- a/src/workers/Workers.h +++ /dev/null @@ -1,82 +0,0 @@ -/* XMRig - * Copyright 2010 Jeff Garzik - * Copyright 2012-2014 pooler - * Copyright 2014 Lucas Jones - * Copyright 2014-2016 Wolf9466 - * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * - * - * 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 . - */ - -#ifndef __WORKERS_H__ -#define __WORKERS_H__ - - -#include -#include -#include -#include - -#include "net/Job.h" -#include "net/JobResult.h" - - -class Handle; -class Hashrate; -class IJobResultListener; - - -class Workers -{ -public: - static Job job(); - static void printHashrate(bool detail); - static void setEnabled(bool enabled); - static void setJob(const Job &job); - static void start(size_t threads, int64_t affinityMask, int priority); - static void stop(); - static void submit(const JobResult &result, int threadId); - - static inline bool isEnabled() { return m_enabled; } - static inline bool isOutdated(uint64_t sequence) { return m_sequence.load(std::memory_order_relaxed) != sequence; } - static inline bool isPaused() { return m_paused.load(std::memory_order_relaxed) == 1; } - static inline uint64_t sequence() { return m_sequence.load(std::memory_order_relaxed); } - static inline void pause() { m_active = false; m_paused = 1; m_sequence++; } - static inline void setListener(IJobResultListener *listener) { m_listener = listener; } - -private: - static void onReady(void *arg); - static void onResult(uv_async_t *handle); - static void onTick(uv_timer_t *handle); - - static bool m_active; - static bool m_enabled; - static Hashrate *m_hashrate; - static IJobResultListener *m_listener; - static Job m_job; - static std::atomic m_paused; - static std::atomic m_sequence; - static std::list m_queue; - static std::vector m_workers; - static uint64_t m_ticks; - static uv_async_t m_async; - static uv_mutex_t m_mutex; - static uv_rwlock_t m_rwlock; - static uv_timer_t m_timer; -}; - - -#endif /* __WORKERS_H__ */ diff --git a/src/xmrig.cpp b/src/xmrig.cpp index c0c2279a..07f18e37 100644 --- a/src/xmrig.cpp +++ b/src/xmrig.cpp @@ -4,9 +4,9 @@ * Copyright 2014 Lucas Jones * Copyright 2014-2016 Wolf9466 * Copyright 2016 Jay D Dee - * Copyright 2016-2017 XMRig - * Copyright 2017- BenDr0id - * + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , * * 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 @@ -23,11 +23,20 @@ */ #include "App.h" +#include "base/kernel/Entry.h" +#include "base/kernel/Process.h" + int main(int argc, char **argv) { - App* app = new App(argc, argv); - int res = app->start(); - delete app; + using namespace xmrig; - return res; + Process process(argc, argv); + const Entry::Id entry = Entry::get(process); + if (entry) { + return Entry::exec(process, entry); + } + + App app(&process); + + return app.exec(); }

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 G`2P=ASWI95 literal 0 HcmV?d00001 diff --git a/doc/screenshot_dashboard.png b/doc/screenshot_dashboard.png new file mode 100644 index 0000000000000000000000000000000000000000..e883e9d87f7b0f17ea9bbec3e2877d897718894f GIT binary patch literal 252242 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≪%ja>|--I*1vpX5(exI(U$|0Sn#+2GRiY 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