From e0e36b871f516c97cf7efdf1a9c4fdc5fe93a9b2 Mon Sep 17 00:00:00 2001 From: BenDr0id Date: Thu, 5 Jul 2018 17:39:21 +0200 Subject: [PATCH 01/97] WIP integration of CC --- CMakeLists.txt | 62 +- src/3rdparty/cpp-httplib/.gitignore | 20 + src/3rdparty/cpp-httplib/README.md | 68 + 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 + src/3rdparty/cpp-httplib/example/server.cc | 107 + .../cpp-httplib/example/server.vcxproj | 88 + src/3rdparty/cpp-httplib/example/simplesvr.cc | 105 + src/3rdparty/cpp-httplib/httplib.h | 2352 ++ 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/api/NetworkState.cpp | 1 + src/api/NetworkState.h | 2 + src/cc/CCClient.cpp | 335 + src/cc/CCClient.h | 81 + src/cc/CCServer.cpp | 200 + src/cc/CCServer.h | 65 + src/cc/ClientStatus.cpp | 555 + src/cc/ClientStatus.h | 200 + src/cc/ControlCommand.cpp | 100 + src/cc/ControlCommand.h | 85 + src/cc/Httpd.cpp | 296 + src/cc/Httpd.h | 70 + src/cc/Service.cpp | 342 + src/cc/Service.h | 70 + src/cc/Summary.cpp | 67 + src/cc/XMRigCC.cpp | 31 + src/cc/XMRigd.cpp | 79 + src/common/Platform_unix.cpp | 3 +- src/common/crypto/Algorithm.cpp | 22 +- src/common/crypto/Algorithm.h | 7 +- src/common/log/SysLog.cpp | 2 +- src/common/net/Client.cpp | 7 + src/config.json | 10 +- src/net/strategies/DonateStrategy.cpp | 25 +- src/version.h | 67 +- 49 files changed, 35258 insertions(+), 37 deletions(-) create mode 100644 src/3rdparty/cpp-httplib/.gitignore create mode 100644 src/3rdparty/cpp-httplib/README.md create mode 100644 src/3rdparty/cpp-httplib/example/Makefile create mode 100644 src/3rdparty/cpp-httplib/example/benchmark.cc create mode 100644 src/3rdparty/cpp-httplib/example/client.cc create mode 100644 src/3rdparty/cpp-httplib/example/client.vcxproj create mode 100644 src/3rdparty/cpp-httplib/example/example.sln create mode 100644 src/3rdparty/cpp-httplib/example/hello.cc create mode 100644 src/3rdparty/cpp-httplib/example/server.cc create mode 100644 src/3rdparty/cpp-httplib/example/server.vcxproj create mode 100644 src/3rdparty/cpp-httplib/example/simplesvr.cc create mode 100644 src/3rdparty/cpp-httplib/httplib.h create mode 100644 src/3rdparty/cpp-httplib/test/Makefile create mode 100644 src/3rdparty/cpp-httplib/test/gtest/gtest-all.cc create mode 100644 src/3rdparty/cpp-httplib/test/gtest/gtest.h create mode 100644 src/3rdparty/cpp-httplib/test/gtest/gtest_main.cc create mode 100644 src/3rdparty/cpp-httplib/test/test.cc create mode 100644 src/3rdparty/cpp-httplib/test/test.sln create mode 100644 src/3rdparty/cpp-httplib/test/test.vcxproj create mode 100644 src/3rdparty/cpp-httplib/test/test.xcodeproj/project.pbxproj create mode 100644 src/3rdparty/cpp-httplib/test/test.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 src/3rdparty/cpp-httplib/test/www/dir/index.html create mode 100644 src/3rdparty/cpp-httplib/test/www/dir/test.html create mode 100644 src/cc/CCClient.cpp create mode 100644 src/cc/CCClient.h create mode 100644 src/cc/CCServer.cpp create mode 100644 src/cc/CCServer.h create mode 100644 src/cc/ClientStatus.cpp create mode 100644 src/cc/ClientStatus.h create mode 100644 src/cc/ControlCommand.cpp create mode 100644 src/cc/ControlCommand.h create mode 100644 src/cc/Httpd.cpp create mode 100644 src/cc/Httpd.h create mode 100644 src/cc/Service.cpp create mode 100644 src/cc/Service.h create mode 100644 src/cc/Summary.cpp create mode 100644 src/cc/XMRigCC.cpp create mode 100644 src/cc/XMRigd.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e73cb96..97eb2950 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,11 +1,16 @@ cmake_minimum_required(VERSION 2.8) -project(xmrig-amd) +project(xmrigMiner-amd) option(WITH_AEON "CryptoNight-Lite support" ON) option(WITH_SUMO "CryptoNight-Heavy support" ON) option(WITH_HTTPD "HTTP REST API" ON) +option(WITH_CC_CLIENT "CC Client" ON) +option(WITH_CC_SERVER "CC Server" ON) option(BUILD_STATIC "Build static binary" OFF) +set(MINER_EXECUTABLE_NAME "xmrigMiner-amd" CACHE STRING "Miner executable file name") +set(DAEMON_EXECUTABLE_NAME "xmrigDaemon-amd" CACHE STRING "Daemon executable file name") + include (CheckIncludeFile) include (cmake/cpu.cmake) @@ -179,6 +184,8 @@ add_definitions(/DUNICODE) #add_definitions(/DAPP_DEBUG) add_definitions(/DXMRIG_AMD_PROJECT) add_definitions(/DXMRIG_NO_LIBCPUID) +add_definitions(/DXMRIG_NO_TLS) +add_definitions(/DMINER_EXECUTABLE_NAME=${MINER_EXECUTABLE_NAME}) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake") @@ -237,6 +244,43 @@ else() add_definitions(/DXMRIG_NO_API) endif() +if (WITH_CC_SERVER) + find_package(MHD) + + if (MHD_FOUND) + include_directories(${MHD_INCLUDE_DIRS}) + else() + message(FATAL_ERROR "microhttpd NOT found: use `-DWITH_CC_SERVER=OFF` to build without CC Server support") + endif() + + set(SOURCES_CC_SERVER + src/cc/CCServer.cpp + src/cc/Service.cpp + src/cc/Summary.cpp + src/cc/Httpd.cpp + src/cc/XMRigCC.cpp + ) +endif() + +if (WITH_CC_CLIENT) + set(SOURCES_CC_CLIENT + src/cc/CCClient.cpp) +endif() + +if (WITH_CC_SERVER OR WITH_CC_CLIENT) + set(SOURCES_CC_COMMON + src/cc/ControlCommand.cpp + src/cc/ClientStatus.cpp) +else() + add_definitions(/DXMRIG_NO_CC) +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) + + include_directories(src) include_directories(src/3rdparty) include_directories(${UV_INCLUDE_DIR}) @@ -245,5 +289,19 @@ if (BUILD_STATIC) set(CMAKE_EXE_LINKER_FLAGS " -static") endif() -add_executable(${PROJECT_NAME} ${HEADERS} ${SOURCES} ${SOURCES_OS} ${SOURCES_CPUID} ${HEADERS_CRYPTO} ${SOURCES_CRYPTO} ${SOURCES_SYSLOG} ${HTTPD_SOURCES}) +add_executable(${PROJECT_NAME} ${HEADERS} ${SOURCES} ${SOURCES_OS} ${SOURCES_CPUID} ${HEADERS_CRYPTO} ${SOURCES_CRYPTO} ${SOURCES_SYSLOG} ${HTTPD_SOURCES} ${SOURCES_CC_CLIENT}) +set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME ${MINER_EXECUTABLE_NAME}) target_link_libraries(${PROJECT_NAME} ${UV_LIBRARIES} ${MHD_LIBRARY} ${EXTRA_LIBS} ${LIBS} ${OpenCL_LIBRARY}) + + +add_executable(xmrigDaemon-amd src/cc/XMRigd.cpp res/app.rc) +set_target_properties(xmrigDaemon-amd PROPERTIES OUTPUT_NAME ${DAEMON_EXECUTABLE_NAME}) + + +if (WITH_CC_SERVER AND MHD_FOUND) + add_executable(xmrigCCServer ${HEADERS} ${SOURCES} ${SOURCES_OS} ${SOURCES_CPUID} ${HEADERS_CRYPTO} ${SOURCES_CRYPTO} ${SOURCES_SYSLOG} ${HTTPD_SOURCES} ${SOURCES_CC_SERVER} res/app.rc) + target_link_libraries(xmrigCCServer + xmrig_cc_common ${UV_LIBRARIES} ${MHD_LIBRARY} ${EXTRA_LIBS} ${LIBS} ${OpenCL_LIBRARY}) + + set_target_properties(xmrigCCServer PROPERTIES COMPILE_FLAGS "-DXMRIG_CC_SERVER ${SHARED_FLAGS}") +endif() \ No newline at end of file diff --git a/src/3rdparty/cpp-httplib/.gitignore b/src/3rdparty/cpp-httplib/.gitignore new file mode 100644 index 00000000..99d000df --- /dev/null +++ b/src/3rdparty/cpp-httplib/.gitignore @@ -0,0 +1,20 @@ +tags + +example/server +example/client +example/hello +example/simplesvr +test/test +test/test.xcodeproj/xcuser* +test/test.xcodeproj/*/xcuser* + +*.swp + +Debug +Release +*.vcxproj.user +*.sdf +*.suo +*.opensdf +ipch +*.dSYM diff --git a/src/3rdparty/cpp-httplib/README.md b/src/3rdparty/cpp-httplib/README.md new file mode 100644 index 00000000..7210bc3c --- /dev/null +++ b/src/3rdparty/cpp-httplib/README.md @@ -0,0 +1,68 @@ +cpp-httplib +=========== + +A C++11 header-only HTTP library. + +[The Boost Software License 1.0](http://www.boost.org/LICENSE_1_0.txt) + +It's extremely easy to setup. Just include **httplib.h** file in your code! + +Server Example +-------------- + +Inspired by [Sinatra](http://www.sinatrarb.com/) and [express](https://github.com/visionmedia/express). + +```c++ +#include + +int main(void) +{ + using namespace httplib; + + Server svr; + + svr.get("/hi", [](const Request& req, const Response& res) { + res.set_content("Hello World!", "text/plain"); + }); + + svr.get(R"(/numbers/(\d+))", [&](const Request& req, const Response& res) { + auto numbers = req.matches[1]; + res.set_content(numbers, "text/plain"); + }); + + svr.listen("localhost", 1234); +} +``` + +Client Example +-------------- + +```c++ +#include +#include + +int main(void) +{ + httplib::Client cli("localhost", 1234); + + auto res = cli.get("/hi"); + if (res && res->status == 200) { + std::cout << res->body << std::endl; + } +} +``` + +OpenSSL Support +--------------- + +SSL support is available with `CPPHTTPLIB_OPENSSL_SUPPORT`. `libssl` and `libcrypto` should be linked. + +```c++ +#define CPPHTTPLIB_OPENSSL_SUPPORT + +SSLServer svr("./cert.pem", "./key.pem"); + +SSLClient cli("localhost", 8080); +``` + +Copyright (c) 2017 Yuji Hirose. All rights reserved. diff --git a/src/3rdparty/cpp-httplib/example/Makefile b/src/3rdparty/cpp-httplib/example/Makefile new file mode 100644 index 00000000..575e0fae --- /dev/null +++ b/src/3rdparty/cpp-httplib/example/Makefile @@ -0,0 +1,28 @@ + +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 new file mode 100644 index 00000000..7e3c3dd7 --- /dev/null +++ b/src/3rdparty/cpp-httplib/example/benchmark.cc @@ -0,0 +1,33 @@ +#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 new file mode 100644 index 00000000..3bd1641c --- /dev/null +++ b/src/3rdparty/cpp-httplib/example/client.cc @@ -0,0 +1,31 @@ +// +// 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 new file mode 100644 index 00000000..51d06c1c --- /dev/null +++ b/src/3rdparty/cpp-httplib/example/client.vcxproj @@ -0,0 +1,88 @@ + + + + + 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 new file mode 100644 index 00000000..e11aa85e --- /dev/null +++ b/src/3rdparty/cpp-httplib/example/example.sln @@ -0,0 +1,31 @@ + +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 new file mode 100644 index 00000000..de6f4fbf --- /dev/null +++ b/src/3rdparty/cpp-httplib/example/hello.cc @@ -0,0 +1,22 @@ +// +// 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.cc b/src/3rdparty/cpp-httplib/example/server.cc new file mode 100644 index 00000000..b6e348df --- /dev/null +++ b/src/3rdparty/cpp-httplib/example/server.cc @@ -0,0 +1,107 @@ +// +// sample.cc +// +// Copyright (c) 2012 Yuji Hirose. All rights reserved. +// The Boost Software License 1.0 +// + +#include +#include + +#define SERVER_CERT_FILE "./cert.pem" +#define SERVER_PRIVATE_KEY_FILE "./key.pem" + +using namespace httplib; + +std::string dump_headers(const MultiMap& headers) +{ + std::string s; + char buf[BUFSIZ]; + + for (auto it = headers.begin(); it != headers.end(); ++it) { + const auto& x = *it; + snprintf(buf, sizeof(buf), "%s: %s\n", x.first.c_str(), x.second.c_str()); + s += buf; + } + + return s; +} + +std::string log(const Request& req, const Response& res) +{ + std::string s; + char buf[BUFSIZ]; + + s += "================================\n"; + + snprintf(buf, sizeof(buf), "%s %s", req.method.c_str(), req.path.c_str()); + s += buf; + + std::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); + + if (!res.body.empty()) { + s += res.body; + } + + s += "\n"; + + return s; +} + +int main(void) +{ +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE); +#else + Server svr; +#endif + + svr.get("/", [=](const auto& req, auto& res) { + res.set_redirect("/hi"); + }); + + svr.get("/hi", [](const auto& req, auto& res) { + res.set_content("Hello World!", "text/plain"); + }); + + svr.get("/dump", [](const auto& req, auto& res) { + res.set_content(dump_headers(req.headers), "text/plain"); + }); + + svr.get("/stop", [&](const auto& req, auto& res) { + svr.stop(); + }); + + 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) { + printf("%s", log(req, res).c_str()); + }); + + svr.listen("localhost", 8080); + + return 0; +} + +// 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 new file mode 100644 index 00000000..f4e71812 --- /dev/null +++ b/src/3rdparty/cpp-httplib/example/server.vcxproj @@ -0,0 +1,88 @@ + + + + + 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 new file mode 100644 index 00000000..1c9d5a2f --- /dev/null +++ b/src/3rdparty/cpp-httplib/example/simplesvr.cc @@ -0,0 +1,105 @@ +// +// 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/httplib.h b/src/3rdparty/cpp-httplib/httplib.h new file mode 100644 index 00000000..87171e0b --- /dev/null +++ b/src/3rdparty/cpp-httplib/httplib.h @@ -0,0 +1,2352 @@ +// +// httplib.h +// +// Copyright (c) 2017 Yuji Hirose. All rights reserved. +// MIT License +// + +#ifndef _CPPHTTPLIB_HTTPLIB_H_ +#define _CPPHTTPLIB_HTTPLIB_H_ + +#ifdef _WIN32 +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif +#ifndef _CRT_NONSTDC_NO_DEPRECATE +#define _CRT_NONSTDC_NO_DEPRECATE +#endif + +#if defined(_MSC_VER) && _MSC_VER < 1900 +#define snprintf _snprintf_s +#endif + +#ifndef S_ISREG +#define S_ISREG(m) (((m)&S_IFREG)==S_IFREG) +#endif +#ifndef S_ISDIR +#define S_ISDIR(m) (((m)&S_IFDIR)==S_IFDIR) +#endif + +#include +#include +#include + +#undef min +#undef max + +#ifndef strcasecmp +#define strcasecmp _stricmp +#endif + +typedef SOCKET socket_t; +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef int socket_t; +#define INVALID_SOCKET (-1) +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +#include +#endif + +#ifdef CPPHTTPLIB_ZLIB_SUPPORT +#include +#endif + +/* + * Configuration + */ +#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5 +#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND 0 + +namespace httplib +{ + + namespace detail { + + struct ci { + bool operator() (const std::string & s1, const std::string & s2) const { + return std::lexicographical_compare( + s1.begin(), s1.end(), + s2.begin(), s2.end(), + [](char c1, char c2) { + return ::tolower(c1) < ::tolower(c2); + }); + } + }; + + } // namespace detail + + enum class HttpVersion { v1_0 = 0, v1_1 }; + + typedef std::multimap Headers; + + template + std::pair make_range_header(uint64_t value, Args... args); + + typedef std::multimap Params; + typedef std::smatch Match; + typedef std::function Progress; + + struct MultipartFile { + std::string filename; + std::string content_type; + size_t offset = 0; + size_t length = 0; + }; + typedef std::multimap MultipartFiles; + + struct Request { + std::string version; + std::string method; + std::string target; + std::string path; + Headers headers; + std::string body; + Params params; + MultipartFiles files; + Match matches; + + Progress progress; + + bool has_header(const char* key) const; + std::string get_header_value(const char* key) const; + void set_header(const char* key, const char* val); + + bool has_param(const char* key) const; + std::string get_param_value(const char* key) const; + + bool has_file(const char* key) const; + MultipartFile get_file_value(const char* key) const; + }; + + struct Response { + std::string version; + int status; + Headers headers; + std::string body; + + bool has_header(const char* key) const; + std::string get_header_value(const char* key) const; + void set_header(const char* key, const char* val); + + void set_redirect(const char* uri); + void set_content(const char* s, size_t n, const char* content_type); + void set_content(const std::string& s, const char* content_type); + + Response() : status(-1) {} + }; + + class Stream { + public: + virtual ~Stream() {} + virtual int read(char* ptr, size_t size) = 0; + virtual int write(const char* ptr, size_t size1) = 0; + virtual int write(const char* ptr) = 0; + virtual std::string get_remote_addr() = 0; + + template + void write_format(const char* fmt, const Args& ...args); + }; + + class SocketStream : public Stream { + public: + SocketStream(socket_t sock); + virtual ~SocketStream(); + + virtual int read(char* ptr, size_t size); + virtual int write(const char* ptr, size_t size); + virtual int write(const char* ptr); + virtual std::string get_remote_addr(); + + private: + socket_t sock_; + }; + + class Server { + public: + typedef std::function Handler; + typedef std::function Logger; + + Server(); + + virtual ~Server(); + + virtual bool is_valid() const; + + Server& Get(const char* pattern, Handler handler); + Server& Post(const char* pattern, Handler handler); + + Server& Put(const char* pattern, Handler handler); + Server& Delete(const char* pattern, Handler handler); + Server& Options(const char* pattern, Handler handler); + + bool set_base_dir(const char* path); + + void set_error_handler(Handler handler); + void set_logger(Logger logger); + + void set_keep_alive_max_count(size_t count); + + int bind_to_any_port(const char* host, int socket_flags = 0); + bool listen_after_bind(); + + bool listen(const char* host, int port, int socket_flags = 0); + + bool is_running() const; + void stop(); + + protected: + bool process_request(Stream& strm, bool last_connection, bool& connection_close); + + size_t keep_alive_max_count_; + + private: + typedef std::vector> Handlers; + + socket_t create_server_socket(const char* host, int port, int socket_flags) const; + int bind_internal(const char* host, int port, int socket_flags); + bool listen_internal(); + + bool routing(Request& req, Response& res); + bool handle_file_request(Request& req, Response& res); + bool dispatch_request(Request& req, Response& res, Handlers& handlers); + + bool parse_request_line(const char* s, Request& req); + void write_response(Stream& strm, bool last_connection, const Request& req, Response& res); + + virtual bool read_and_close_socket(socket_t sock); + + bool is_running_; + socket_t svr_sock_; + std::string base_dir_; + Handlers get_handlers_; + Handlers post_handlers_; + Handlers put_handlers_; + Handlers delete_handlers_; + Handlers options_handlers_; + Handler error_handler_; + Logger logger_; + + // TODO: Use thread pool... + std::mutex running_threads_mutex_; + int running_threads_; + }; + + class Client { + public: + Client( + const char* host, + int port = 80, + size_t timeout_sec = 300); + + virtual ~Client(); + + virtual bool is_valid() const; + + std::shared_ptr Get(const char* path, Progress progress = nullptr); + std::shared_ptr Get(const char* path, const Headers& headers, Progress progress = nullptr); + + std::shared_ptr Head(const char* path); + std::shared_ptr Head(const char* path, const Headers& headers); + + std::shared_ptr Post(const char* path, const std::string& body, const char* content_type); + std::shared_ptr Post(const char* path, const Headers& headers, const std::string& body, const char* content_type); + + std::shared_ptr Post(const char* path, const Params& params); + std::shared_ptr Post(const char* path, const Headers& headers, const Params& params); + + std::shared_ptr Put(const char* path, const std::string& body, const char* content_type); + std::shared_ptr Put(const char* path, const Headers& headers, const std::string& body, const char* content_type); + + std::shared_ptr Delete(const char* path); + std::shared_ptr Delete(const char* path, const Headers& headers); + + std::shared_ptr Options(const char* path); + std::shared_ptr Options(const char* path, const Headers& headers); + + bool send(Request& req, Response& res); + + protected: + bool process_request(Stream& strm, Request& req, Response& res, bool& connection_close); + + const std::string host_; + const int port_; + size_t timeout_sec_; + const std::string host_and_port_; + + private: + socket_t create_client_socket() const; + bool read_response_line(Stream& strm, Response& res); + void write_request(Stream& strm, Request& req); + + virtual bool read_and_close_socket(socket_t sock, Request& req, Response& res); + }; + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + class SSLSocketStream : public Stream { + public: + SSLSocketStream(socket_t sock, SSL* ssl); + virtual ~SSLSocketStream(); + + virtual int read(char* ptr, size_t size); + virtual int write(const char* ptr, size_t size); + virtual int write(const char* ptr); + virtual std::string get_remote_addr(); + + private: + socket_t sock_; + SSL* ssl_; + }; + + class SSLServer : public Server { + public: + SSLServer( + const char* cert_path, const char* private_key_path); + + virtual ~SSLServer(); + + virtual bool is_valid() const; + + private: + virtual bool read_and_close_socket(socket_t sock); + + SSL_CTX* ctx_; + std::mutex ctx_mutex_; + }; + + class SSLClient : public Client { + public: + SSLClient( + const char* host, + int port = 80, + size_t timeout_sec = 300); + + virtual ~SSLClient(); + + virtual bool is_valid() const; + + private: + virtual bool read_and_close_socket(socket_t sock, Request& req, Response& res); + + SSL_CTX* ctx_; + std::mutex ctx_mutex_; + }; +#endif + +/* + * Implementation + */ + namespace detail { + + template + void split(const char* b, const char* e, char d, Fn fn) + { + int i = 0; + int beg = 0; + + while (e ? (b + i != e) : (b[i] != '\0')) { + if (b[i] == d) { + fn(&b[beg], &b[i]); + beg = i + 1; + } + i++; + } + + if (i) { + fn(&b[beg], &b[i]); + } + } + +// NOTE: until the read size reaches `fixed_buffer_size`, use `fixed_buffer` +// to store data. The call can set memory on stack for performance. + class stream_line_reader { + public: + stream_line_reader(Stream& strm, char* fixed_buffer, size_t fixed_buffer_size) + : strm_(strm) + , fixed_buffer_(fixed_buffer) + , fixed_buffer_size_(fixed_buffer_size) { + } + + const char* ptr() const { + if (glowable_buffer_.empty()) { + return fixed_buffer_; + } else { + return glowable_buffer_.data(); + } + } + + bool getline() { + fixed_buffer_used_size_ = 0; + glowable_buffer_.clear(); + + for (size_t i = 0; ; i++) { + char byte; + auto n = strm_.read(&byte, 1); + + if (n < 0) { + return false; + } else if (n == 0) { + if (i == 0) { + return false; + } else { + break; + } + } + + append(byte); + + if (byte == '\n') { + break; + } + } + + return true; + } + + private: + void append(char c) { + if (fixed_buffer_used_size_ < fixed_buffer_size_ - 1) { + fixed_buffer_[fixed_buffer_used_size_++] = c; + fixed_buffer_[fixed_buffer_used_size_] = '\0'; + } else { + if (glowable_buffer_.empty()) { + assert(fixed_buffer_[fixed_buffer_used_size_] == '\0'); + glowable_buffer_.assign(fixed_buffer_, fixed_buffer_used_size_); + } + glowable_buffer_ += c; + } + } + + Stream& strm_; + char* fixed_buffer_; + const size_t fixed_buffer_size_; + size_t fixed_buffer_used_size_; + std::string glowable_buffer_; + }; + + inline int close_socket(socket_t sock) + { +#ifdef _WIN32 + return closesocket(sock); +#else + return close(sock); +#endif + } + + inline int select_read(socket_t sock, size_t sec, size_t usec) + { + fd_set fds; + FD_ZERO(&fds); + FD_SET(sock, &fds); + + timeval tv; + tv.tv_sec = sec; + tv.tv_usec = usec; + + return select(sock + 1, &fds, NULL, NULL, &tv); + } + + inline bool wait_until_socket_is_ready(socket_t sock, size_t sec, size_t usec) + { + fd_set fdsr; + FD_ZERO(&fdsr); + FD_SET(sock, &fdsr); + + auto fdsw = fdsr; + auto fdse = fdsr; + + timeval tv; + tv.tv_sec = sec; + tv.tv_usec = usec; + + if (select(sock + 1, &fdsr, &fdsw, &fdse, &tv) < 0) { + return false; + } else if (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw)) { + int error = 0; + socklen_t len = sizeof(error); + if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (char*)&error, &len) < 0 || error) { + return false; + } + } else { + return false; + } + + return true; + } + + template + inline bool read_and_close_socket(socket_t sock, size_t keep_alive_max_count, T callback) + { + bool ret = false; + + if (keep_alive_max_count > 0) { + auto count = keep_alive_max_count; + while (count > 0 && + detail::select_read(sock, + CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND, + CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND) > 0) { + SocketStream strm(sock); + auto last_connection = count == 1; + auto connection_close = false; + + ret = callback(strm, last_connection, connection_close); + if (!ret || connection_close) { + break; + } + + count--; + } + } else { + SocketStream strm(sock); + auto dummy_connection_close = false; + ret = callback(strm, true, dummy_connection_close); + } + + close_socket(sock); + return ret; + } + + inline int shutdown_socket(socket_t sock) + { +#ifdef _WIN32 + return shutdown(sock, SD_BOTH); +#else + return shutdown(sock, SHUT_RDWR); +#endif + } + + template + socket_t create_socket(const char* host, int port, Fn fn, int socket_flags = 0) + { +#ifdef _WIN32 + #define SO_SYNCHRONOUS_NONALERT 0x20 +#define SO_OPENTYPE 0x7008 + + int opt = SO_SYNCHRONOUS_NONALERT; + setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char*)&opt, sizeof(opt)); +#endif + + // Get address info + struct addrinfo hints; + struct addrinfo *result; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = socket_flags; + hints.ai_protocol = 0; + + auto service = std::to_string(port); + + if (getaddrinfo(host, service.c_str(), &hints, &result)) { + return INVALID_SOCKET; + } + + for (auto rp = result; rp; rp = rp->ai_next) { + // Create a socket + auto sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (sock == INVALID_SOCKET) { + continue; + } + + // Make 'reuse address' option available + int yes = 1; + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)&yes, sizeof(yes)); + + // Make socket also having a timeout + struct timeval timeout; + timeout.tv_sec = CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND; + timeout.tv_usec = CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND; + + setsockopt (sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)); + setsockopt (sock, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout)); + + // bind or connect + if (fn(sock, *rp)) { + freeaddrinfo(result); + return sock; + } + + close_socket(sock); + } + + freeaddrinfo(result); + return INVALID_SOCKET; + } + + inline void set_nonblocking(socket_t sock, bool nonblocking) + { +#ifdef _WIN32 + auto flags = nonblocking ? 1UL : 0UL; + ioctlsocket(sock, FIONBIO, &flags); +#else + auto flags = fcntl(sock, F_GETFL, 0); + fcntl(sock, F_SETFL, nonblocking ? (flags | O_NONBLOCK) : (flags & (~O_NONBLOCK))); +#endif + } + + inline bool is_connection_error() + { +#ifdef _WIN32 + return WSAGetLastError() != WSAEWOULDBLOCK; +#else + return errno != EINPROGRESS; +#endif + } + + inline std::string get_remote_addr(socket_t sock) { + struct sockaddr_storage addr; + socklen_t len = sizeof(addr); + + if (!getpeername(sock, (struct sockaddr*)&addr, &len)) { + char ipstr[NI_MAXHOST]; + + if (!getnameinfo((struct sockaddr*)&addr, len, + ipstr, sizeof(ipstr), nullptr, 0, NI_NUMERICHOST)) { + return ipstr; + } + } + + return std::string(); + } + + inline bool is_file(const std::string& path) + { + struct stat st; + return stat(path.c_str(), &st) >= 0 && S_ISREG(st.st_mode); + } + + inline bool is_dir(const std::string& path) + { + struct stat st; + return stat(path.c_str(), &st) >= 0 && S_ISDIR(st.st_mode); + } + + inline bool is_valid_path(const std::string& path) { + size_t level = 0; + size_t i = 0; + + // Skip slash + while (i < path.size() && path[i] == '/') { + i++; + } + + while (i < path.size()) { + // Read component + auto beg = i; + while (i < path.size() && path[i] != '/') { + i++; + } + + auto len = i - beg; + assert(len > 0); + + if (!path.compare(beg, len, ".")) { + ; + } else if (!path.compare(beg, len, "..")) { + if (level == 0) { + return false; + } + level--; + } else { + level++; + } + + // Skip slash + while (i < path.size() && path[i] == '/') { + i++; + } + } + + return true; + } + + inline void read_file(const std::string& path, std::string& out) + { + std::ifstream fs(path, std::ios_base::binary); + fs.seekg(0, std::ios_base::end); + auto size = fs.tellg(); + fs.seekg(0); + out.resize(static_cast(size)); + fs.read(&out[0], size); + } + + inline std::string file_extension(const std::string& path) + { + std::smatch m; + auto pat = std::regex("\\.([a-zA-Z0-9]+)$"); + if (std::regex_search(path, m, pat)) { + return m[1].str(); + } + return std::string(); + } + + inline const char* find_content_type(const std::string& path) + { + auto ext = file_extension(path); + if (ext == "txt") { + return "text/plain"; + } else if (ext == "html") { + return "text/html"; + } else if (ext == "css") { + return "text/css"; + } else if (ext == "jpeg" || ext == "jpg") { + return "image/jpg"; + } else if (ext == "png") { + return "image/png"; + } else if (ext == "gif") { + return "image/gif"; + } else if (ext == "svg") { + return "image/svg+xml"; + } else if (ext == "ico") { + return "image/x-icon"; + } else if (ext == "json") { + return "application/json"; + } else if (ext == "pdf") { + return "application/pdf"; + } else if (ext == "js") { + return "application/javascript"; + } else if (ext == "xml") { + return "application/xml"; + } else if (ext == "xhtml") { + return "application/xhtml+xml"; + } + return nullptr; + } + + inline const char* status_message(int status) + { + switch (status) { + case 200: return "OK"; + case 301: return "Moved Permanently"; + case 302: return "Found"; + case 303: return "See Other"; + case 304: return "Not Modified"; + case 400: return "Bad Request"; + case 403: return "Forbidden"; + case 404: return "Not Found"; + case 415: return "Unsupported Media Type"; + default: + case 500: return "Internal Server Error"; + } + } + + inline const char* get_header_value(const Headers& headers, const char* key, const char* def) + { + auto it = headers.find(key); + if (it != headers.end()) { + return it->second.c_str(); + } + return def; + } + + inline int get_header_value_int(const Headers& headers, const char* key, int def) + { + auto it = headers.find(key); + if (it != headers.end()) { + return std::stoi(it->second); + } + return def; + } + + inline bool read_headers(Stream& strm, Headers& headers) + { + static std::regex re(R"((.+?):\s*(.+?)\s*\r\n)"); + + const auto bufsiz = 2048; + char buf[bufsiz]; + + stream_line_reader reader(strm, buf, bufsiz); + + for (;;) { + if (!reader.getline()) { + return false; + } + if (!strcmp(reader.ptr(), "\r\n")) { + break; + } + std::cmatch m; + if (std::regex_match(reader.ptr(), m, re)) { + auto key = std::string(m[1]); + auto val = std::string(m[2]); + headers.emplace(key, val); + } + } + + return true; + } + + inline bool read_content_with_length(Stream& strm, std::string& out, size_t len, Progress progress) + { + out.assign(len, 0); + size_t r = 0; + while (r < len){ + auto n = strm.read(&out[r], len - r); + if (n <= 0) { + return false; + } + + r += n; + + if (progress) { + progress(r, len); + } + } + + return true; + } + + inline bool read_content_without_length(Stream& strm, std::string& out) + { + for (;;) { + char byte; + auto n = strm.read(&byte, 1); + if (n < 0) { + return false; + } else if (n == 0) { + return true; + } + out += byte; + } + + return true; + } + + inline bool read_content_chunked(Stream& strm, std::string& out) + { + const auto bufsiz = 16; + char buf[bufsiz]; + + stream_line_reader reader(strm, buf, bufsiz); + + if (!reader.getline()) { + return false; + } + + auto chunk_len = std::stoi(reader.ptr(), 0, 16); + + while (chunk_len > 0){ + std::string chunk; + if (!read_content_with_length(strm, chunk, chunk_len, nullptr)) { + return false; + } + + if (!reader.getline()) { + return false; + } + + if (strcmp(reader.ptr(), "\r\n")) { + break; + } + + out += chunk; + + if (!reader.getline()) { + return false; + } + + chunk_len = std::stoi(reader.ptr(), 0, 16); + } + + if (chunk_len == 0) { + // Reader terminator after chunks + if (!reader.getline() || strcmp(reader.ptr(), "\r\n")) + return false; + } + + return true; + } + + template + bool read_content(Stream& strm, T& x, Progress progress = Progress()) + { + auto len = get_header_value_int(x.headers, "Content-Length", 0); + + if (len) { + return read_content_with_length(strm, x.body, len, progress); + } else { + const auto& encoding = get_header_value(x.headers, "Transfer-Encoding", ""); + + if (!strcasecmp(encoding, "chunked")) { + return read_content_chunked(strm, x.body); + } else { + return read_content_without_length(strm, x.body); + } + } + + return true; + } + + template + inline void write_headers(Stream& strm, const T& info) + { + for (const auto& x: info.headers) { + strm.write_format("%s: %s\r\n", x.first.c_str(), x.second.c_str()); + } + strm.write("\r\n"); + } + + inline std::string encode_url(const std::string& s) + { + std::string result; + + for (auto i = 0; s[i]; i++) { + switch (s[i]) { + case ' ': result += "+"; break; + case '\'': result += "%27"; break; + case ',': result += "%2C"; break; + case ':': result += "%3A"; break; + case ';': result += "%3B"; break; + default: + if (s[i] < 0) { + result += '%'; + char hex[4]; + size_t len = snprintf(hex, sizeof(hex) - 1, "%02X", (unsigned char)s[i]); + assert(len == 2); + result.append(hex, len); + } else { + result += s[i]; + } + break; + } + } + + return result; + } + + inline bool is_hex(char c, int& v) + { + if (0x20 <= c && isdigit(c)) { + v = c - '0'; + return true; + } else if ('A' <= c && c <= 'F') { + v = c - 'A' + 10; + return true; + } else if ('a' <= c && c <= 'f') { + v = c - 'a' + 10; + return true; + } + return false; + } + + inline bool from_hex_to_i(const std::string& s, size_t i, size_t cnt, int& val) + { + if (i >= s.size()) { + return false; + } + + val = 0; + for (; cnt; i++, cnt--) { + if (!s[i]) { + return false; + } + int v = 0; + if (is_hex(s[i], v)) { + val = val * 16 + v; + } else { + return false; + } + } + return true; + } + + inline size_t to_utf8(int code, char* buff) + { + if (code < 0x0080) { + buff[0] = (code & 0x7F); + return 1; + } else if (code < 0x0800) { + buff[0] = (0xC0 | ((code >> 6) & 0x1F)); + buff[1] = (0x80 | (code & 0x3F)); + return 2; + } else if (code < 0xD800) { + buff[0] = (0xE0 | ((code >> 12) & 0xF)); + buff[1] = (0x80 | ((code >> 6) & 0x3F)); + buff[2] = (0x80 | (code & 0x3F)); + return 3; + } else if (code < 0xE000) { // D800 - DFFF is invalid... + return 0; + } else if (code < 0x10000) { + buff[0] = (0xE0 | ((code >> 12) & 0xF)); + buff[1] = (0x80 | ((code >> 6) & 0x3F)); + buff[2] = (0x80 | (code & 0x3F)); + return 3; + } else if (code < 0x110000) { + buff[0] = (0xF0 | ((code >> 18) & 0x7)); + buff[1] = (0x80 | ((code >> 12) & 0x3F)); + buff[2] = (0x80 | ((code >> 6) & 0x3F)); + buff[3] = (0x80 | (code & 0x3F)); + return 4; + } + + // NOTREACHED + return 0; + } + + inline std::string decode_url(const std::string& s) + { + std::string result; + + for (size_t i = 0; i < s.size(); i++) { + if (s[i] == '%' && i + 1 < s.size()) { + if (s[i + 1] == 'u') { + int val = 0; + if (from_hex_to_i(s, i + 2, 4, val)) { + // 4 digits Unicode codes + char buff[4]; + size_t len = to_utf8(val, buff); + if (len > 0) { + result.append(buff, len); + } + i += 5; // 'u0000' + } else { + result += s[i]; + } + } else { + int val = 0; + if (from_hex_to_i(s, i + 1, 2, val)) { + // 2 digits hex codes + result += val; + i += 2; // '00' + } else { + result += s[i]; + } + } + } else if (s[i] == '+') { + result += ' '; + } else { + result += s[i]; + } + } + + return result; + } + + inline void parse_query_text(const std::string& s, Params& params) + { + split(&s[0], &s[s.size()], '&', [&](const char* b, const char* e) { + std::string key; + std::string val; + split(b, e, '=', [&](const char* b, const char* e) { + if (key.empty()) { + key.assign(b, e); + } else { + val.assign(b, e); + } + }); + params.emplace(key, decode_url(val)); + }); + } + + inline bool parse_multipart_boundary(const std::string& content_type, std::string& boundary) + { + auto pos = content_type.find("boundary="); + if (pos == std::string::npos) { + return false; + } + + boundary = content_type.substr(pos + 9); + return true; + } + + inline bool parse_multipart_formdata( + const std::string& boundary, const std::string& body, MultipartFiles& files) + { + static std::string dash = "--"; + static std::string crlf = "\r\n"; + + static std::regex re_content_type( + "Content-Type: (.*?)", std::regex_constants::icase); + + static std::regex re_content_disposition( + "Content-Disposition: form-data; name=\"(.*?)\"(?:; filename=\"(.*?)\")?", + std::regex_constants::icase); + + auto dash_boundary = dash + boundary; + + auto pos = body.find(dash_boundary); + if (pos != 0) { + return false; + } + + pos += dash_boundary.size(); + + auto next_pos = body.find(crlf, pos); + if (next_pos == std::string::npos) { + return false; + } + + pos = next_pos + crlf.size(); + + while (pos < body.size()) { + next_pos = body.find(crlf, pos); + if (next_pos == std::string::npos) { + return false; + } + + std::string name; + MultipartFile file; + + auto header = body.substr(pos, (next_pos - pos)); + + while (pos != next_pos) { + std::smatch m; + if (std::regex_match(header, m, re_content_type)) { + file.content_type = m[1]; + } else if (std::regex_match(header, m, re_content_disposition)) { + name = m[1]; + file.filename = m[2]; + } + + pos = next_pos + crlf.size(); + + next_pos = body.find(crlf, pos); + if (next_pos == std::string::npos) { + return false; + } + + header = body.substr(pos, (next_pos - pos)); + } + + pos = next_pos + crlf.size(); + + next_pos = body.find(crlf + dash_boundary, pos); + + if (next_pos == std::string::npos) { + return false; + } + + file.offset = pos; + file.length = next_pos - pos; + + pos = next_pos + crlf.size() + dash_boundary.size(); + + next_pos = body.find(crlf, pos); + if (next_pos == std::string::npos) { + return false; + } + + files.emplace(name, file); + + pos = next_pos + crlf.size(); + } + + return true; + } + + inline std::string to_lower(const char* beg, const char* end) + { + std::string out; + auto it = beg; + while (it != end) { + out += ::tolower(*it); + it++; + } + return out; + } + + inline void make_range_header_core(std::string&) {} + + template + inline void make_range_header_core(std::string& field, uint64_t value) + { + if (!field.empty()) { + field += ", "; + } + field += std::to_string(value) + "-"; + } + + template + inline void make_range_header_core(std::string& field, uint64_t value1, uint64_t value2, Args... args) + { + if (!field.empty()) { + field += ", "; + } + field += std::to_string(value1) + "-" + std::to_string(value2); + make_range_header_core(field, args...); + } + +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + inline bool can_compress(const std::string& content_type) { + return !content_type.find("text/") || + content_type == "image/svg+xml" || + content_type == "application/javascript" || + content_type == "application/json" || + content_type == "application/xml" || + content_type == "application/xhtml+xml"; +} + +inline void compress(std::string& content) +{ + z_stream strm; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + + auto ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY); + if (ret != Z_OK) { + return; + } + + strm.avail_in = content.size(); + strm.next_in = (Bytef *)content.data(); + + std::string compressed; + + const auto bufsiz = 16384; + char buff[bufsiz]; + do { + strm.avail_out = bufsiz; + strm.next_out = (Bytef *)buff; + deflate(&strm, Z_FINISH); + compressed.append(buff, bufsiz - strm.avail_out); + } while (strm.avail_out == 0); + + content.swap(compressed); + + deflateEnd(&strm); +} + +inline void decompress(std::string& content) +{ + z_stream strm; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + + // 15 is the value of wbits, which should be at the maximum possible value to ensure + // that any gzip stream can be decoded. The offset of 16 specifies that the stream + // to decompress will be formatted with a gzip wrapper. + auto ret = inflateInit2(&strm, 16 + 15); + if (ret != Z_OK) { + return; + } + + strm.avail_in = content.size(); + strm.next_in = (Bytef *)content.data(); + + std::string decompressed; + + const auto bufsiz = 16384; + char buff[bufsiz]; + do { + strm.avail_out = bufsiz; + strm.next_out = (Bytef *)buff; + inflate(&strm, Z_NO_FLUSH); + decompressed.append(buff, bufsiz - strm.avail_out); + } while (strm.avail_out == 0); + + content.swap(decompressed); + + inflateEnd(&strm); +} +#endif + +#ifdef _WIN32 + class WSInit { +public: + WSInit() { + WSADATA wsaData; + WSAStartup(0x0002, &wsaData); + } + + ~WSInit() { + WSACleanup(); + } +}; + +static WSInit wsinit_; +#endif + + } // namespace detail + +// Header utilities + template + inline std::pair make_range_header(uint64_t value, Args... args) + { + std::string field; + detail::make_range_header_core(field, value, args...); + field.insert(0, "bytes="); + return std::make_pair("Range", field); + } + +// Request implementation + inline bool Request::has_header(const char* key) const + { + return headers.find(key) != headers.end(); + } + + inline std::string Request::get_header_value(const char* key) const + { + return detail::get_header_value(headers, key, ""); + } + + inline void Request::set_header(const char* key, const char* val) + { + headers.emplace(key, val); + } + + inline bool Request::has_param(const char* key) const + { + return params.find(key) != params.end(); + } + + inline std::string Request::get_param_value(const char* key) const + { + auto it = params.find(key); + if (it != params.end()) { + return it->second; + } + return std::string(); + } + + inline bool Request::has_file(const char* key) const + { + return files.find(key) != files.end(); + } + + inline MultipartFile Request::get_file_value(const char* key) const + { + auto it = files.find(key); + if (it != files.end()) { + return it->second; + } + return MultipartFile(); + } + +// Response implementation + inline bool Response::has_header(const char* key) const + { + return headers.find(key) != headers.end(); + } + + inline std::string Response::get_header_value(const char* key) const + { + return detail::get_header_value(headers, key, ""); + } + + inline void Response::set_header(const char* key, const char* val) + { + headers.emplace(key, val); + } + + inline void Response::set_redirect(const char* url) + { + set_header("Location", url); + status = 302; + } + + inline void Response::set_content(const char* s, size_t n, const char* content_type) + { + body.assign(s, n); + set_header("Content-Type", content_type); + } + + inline void Response::set_content(const std::string& s, const char* content_type) + { + body = s; + set_header("Content-Type", content_type); + } + +// Rstream implementation + template + inline void Stream::write_format(const char* fmt, const Args& ...args) + { + const auto bufsiz = 2048; + char buf[bufsiz]; + +#if defined(_MSC_VER) && _MSC_VER < 1900 + auto n = _snprintf_s(buf, bufsiz, bufsiz - 1, fmt, args...); +#else + auto n = snprintf(buf, bufsiz - 1, fmt, args...); +#endif + if (n > 0) { + if (n >= bufsiz - 1) { + std::vector glowable_buf(bufsiz); + + while (n >= static_cast(glowable_buf.size() - 1)) { + glowable_buf.resize(glowable_buf.size() * 2); +#if defined(_MSC_VER) && _MSC_VER < 1900 + n = _snprintf_s(&glowable_buf[0], glowable_buf.size(), glowable_buf.size() - 1, fmt, args...); +#else + n = snprintf(&glowable_buf[0], glowable_buf.size() - 1, fmt, args...); +#endif + } + write(&glowable_buf[0], n); + } else { + write(buf, n); + } + } + } + +// Socket stream implementation + inline SocketStream::SocketStream(socket_t sock): sock_(sock) + { + } + + inline SocketStream::~SocketStream() + { + } + + inline int SocketStream::read(char* ptr, size_t size) + { + return recv(sock_, ptr, size, 0); + } + + inline int SocketStream::write(const char* ptr, size_t size) + { + return send(sock_, ptr, size, 0); + } + + inline int SocketStream::write(const char* ptr) + { + return write(ptr, strlen(ptr)); + } + + inline std::string SocketStream::get_remote_addr() { + return detail::get_remote_addr(sock_); + } + +// HTTP server implementation + inline Server::Server() + : keep_alive_max_count_(5) + , is_running_(false) + , svr_sock_(INVALID_SOCKET) + , running_threads_(0) + { +#ifndef _WIN32 + signal(SIGPIPE, SIG_IGN); +#endif + } + + inline Server::~Server() + { + } + + inline Server& Server::Get(const char* pattern, Handler handler) + { + get_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; + } + + inline Server& Server::Post(const char* pattern, Handler handler) + { + post_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; + } + + inline Server& Server::Put(const char* pattern, Handler handler) + { + put_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; + } + + inline Server& Server::Delete(const char* pattern, Handler handler) + { + delete_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; + } + + inline Server& Server::Options(const char* pattern, Handler handler) + { + options_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; + } + + inline bool Server::set_base_dir(const char* path) + { + if (detail::is_dir(path)) { + base_dir_ = path; + return true; + } + return false; + } + + inline void Server::set_error_handler(Handler handler) + { + error_handler_ = handler; + } + + inline void Server::set_logger(Logger logger) + { + logger_ = logger; + } + + inline void Server::set_keep_alive_max_count(size_t count) + { + keep_alive_max_count_ = count; + } + + inline int Server::bind_to_any_port(const char* host, int socket_flags) + { + return bind_internal(host, 0, socket_flags); + } + + inline bool Server::listen_after_bind() { + return listen_internal(); + } + + inline bool Server::listen(const char* host, int port, int socket_flags) + { + if (bind_internal(host, port, socket_flags) < 0) + return false; + return listen_internal(); + } + + inline bool Server::is_running() const + { + return is_running_; + } + + inline void Server::stop() + { + if (is_running_) { + assert(svr_sock_ != INVALID_SOCKET); + detail::shutdown_socket(svr_sock_); + detail::close_socket(svr_sock_); + svr_sock_ = INVALID_SOCKET; + } + } + + inline bool Server::parse_request_line(const char* s, Request& req) + { + static std::regex re("(GET|HEAD|POST|PUT|DELETE|OPTIONS) (([^?]+)(?:\\?(.+?))?) (HTTP/1\\.[01])\r\n"); + + std::cmatch m; + if (std::regex_match(s, m, re)) { + req.version = std::string(m[4]); + req.method = std::string(m[1]); + req.target = std::string(m[2]); + req.path = detail::decode_url(m[3]); + + // Parse query text + auto len = std::distance(m[4].first, m[4].second); + if (len > 0) { + detail::parse_query_text(m[4], req.params); + } + + return true; + } + + return false; + } + + inline void Server::write_response(Stream& strm, bool last_connection, const Request& req, Response& res) + { + assert(res.status != -1); + + if (400 <= res.status && error_handler_) { + error_handler_(req, res); + } + + // Response line + strm.write_format("HTTP/1.1 %d %s\r\n", + res.status, + detail::status_message(res.status)); + + // Headers + if (last_connection || + req.version == "HTTP/1.0" || + req.get_header_value("Connection") == "close") { + res.set_header("Connection", "close"); + } + + if (!res.body.empty()) { +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + // TODO: 'Accpet-Encoding' has gzip, not gzip;q=0 + const auto& encodings = req.get_header_value("Accept-Encoding"); + if (encodings.find("gzip") != std::string::npos && + detail::can_compress(res.get_header_value("Content-Type"))) { + detail::compress(res.body); + res.set_header("Content-Encoding", "gzip"); + } +#endif + + if (!res.has_header("Content-Type")) { + res.set_header("Content-Type", "text/plain"); + } + + auto length = std::to_string(res.body.size()); + res.set_header("Content-Length", length.c_str()); + } + + detail::write_headers(strm, res); + + // Body + if (!res.body.empty() && req.method != "HEAD") { + strm.write(res.body.c_str(), res.body.size()); + } + + // Log + if (logger_) { + logger_(req, res); + } + } + + inline bool Server::handle_file_request(Request& req, Response& res) + { + if (!base_dir_.empty() && detail::is_valid_path(req.path)) { + std::string path = base_dir_ + req.path; + + if (!path.empty() && path.back() == '/') { + path += "index.html"; + } + + if (detail::is_file(path)) { + detail::read_file(path, res.body); + auto type = detail::find_content_type(path); + if (type) { + res.set_header("Content-Type", type); + } + res.status = 200; + return true; + } + } + + return false; + } + + inline socket_t Server::create_server_socket(const char* host, int port, int socket_flags) const + { + return detail::create_socket(host, port, + [](socket_t sock, struct addrinfo& ai) -> bool { + if (::bind(sock, ai.ai_addr, ai.ai_addrlen)) { + return false; + } + if (::listen(sock, 5)) { // Listen through 5 channels + return false; + } + return true; + }, socket_flags); + } + + inline int Server::bind_internal(const char* host, int port, int socket_flags) + { + if (!is_valid()) { + return -1; + } + + svr_sock_ = create_server_socket(host, port, socket_flags); + if (svr_sock_ == INVALID_SOCKET) { + return -1; + } + + if (port == 0) { + struct sockaddr_storage address; + socklen_t len = sizeof(address); + if (getsockname(svr_sock_, reinterpret_cast(&address), &len) == -1) { + return -1; + } + if (address.ss_family == AF_INET) { + return ntohs(reinterpret_cast(&address)->sin_port); + } else if (address.ss_family == AF_INET6) { + return ntohs(reinterpret_cast(&address)->sin6_port); + } else { + return -1; + } + } else { + return port; + } + } + + inline bool Server::listen_internal() + { + auto ret = true; + + is_running_ = true; + + for (;;) { + auto val = detail::select_read(svr_sock_, 0, 100000); + + if (val == 0) { // Timeout + if (svr_sock_ == INVALID_SOCKET) { + // The server socket was closed by 'stop' method. + break; + } + continue; + } + + socket_t sock = accept(svr_sock_, NULL, NULL); + + if (sock == INVALID_SOCKET) { + if (svr_sock_ != INVALID_SOCKET) { + detail::close_socket(svr_sock_); + ret = false; + } else { + ; // The server socket was closed by user. + } + break; + } + + // TODO: Use thread pool... + std::thread([=]() { + { + std::lock_guard guard(running_threads_mutex_); + running_threads_++; + } + + read_and_close_socket(sock); + + { + std::lock_guard guard(running_threads_mutex_); + running_threads_--; + } + }).detach(); + } + + // TODO: Use thread pool... + for (;;) { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + std::lock_guard guard(running_threads_mutex_); + if (!running_threads_) { + break; + } + } + + is_running_ = false; + + return ret; + } + + inline bool Server::routing(Request& req, Response& res) + { + if (req.method == "GET" && handle_file_request(req, res)) { + return true; + } + + if (req.method == "GET" || req.method == "HEAD") { + return dispatch_request(req, res, get_handlers_); + } else if (req.method == "POST") { + return dispatch_request(req, res, post_handlers_); + } else if (req.method == "PUT") { + return dispatch_request(req, res, put_handlers_); + } else if (req.method == "DELETE") { + return dispatch_request(req, res, delete_handlers_); + } else if (req.method == "OPTIONS") { + return dispatch_request(req, res, options_handlers_); + } + return false; + } + + inline bool Server::dispatch_request(Request& req, Response& res, Handlers& handlers) + { + for (const auto& x: handlers) { + const auto& pattern = x.first; + const auto& handler = x.second; + + if (std::regex_match(req.path, req.matches, pattern)) { + handler(req, res); + return true; + } + } + return false; + } + + inline bool Server::process_request(Stream& strm, bool last_connection, bool& connection_close) + { + const auto bufsiz = 2048; + char buf[bufsiz]; + + detail::stream_line_reader reader(strm, buf, bufsiz); + + // Connection has been closed on client + if (!reader.getline()) { + return false; + } + + Request req; + Response res; + + res.version = "HTTP/1.1"; + + // Request line and headers + if (!parse_request_line(reader.ptr(), req) || !detail::read_headers(strm, req.headers)) { + res.status = 400; + write_response(strm, last_connection, req, res); + return true; + } + + auto ret = true; + if (req.get_header_value("Connection") == "close") { + // ret = false; + connection_close = true; + } + + req.set_header("REMOTE_ADDR", strm.get_remote_addr().c_str()); + + // Body + if (req.method == "POST" || req.method == "PUT") { + if (!detail::read_content(strm, req)) { + res.status = 400; + write_response(strm, last_connection, req, res); + return ret; + } + + const auto& content_type = req.get_header_value("Content-Type"); + + if (req.get_header_value("Content-Encoding") == "gzip") { +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + detail::decompress(req.body); +#else + res.status = 415; + write_response(strm, last_connection, req, res); + return ret; +#endif + } + + if (!content_type.find("application/x-www-form-urlencoded")) { + detail::parse_query_text(req.body, req.params); + } else if(!content_type.find("multipart/form-data")) { + std::string boundary; + if (!detail::parse_multipart_boundary(content_type, boundary) || + !detail::parse_multipart_formdata(boundary, req.body, req.files)) { + res.status = 400; + write_response(strm, last_connection, req, res); + return ret; + } + } + } + + if (routing(req, res)) { + if (res.status == -1) { + res.status = 200; + } + } else { + res.status = 404; + } + + write_response(strm, last_connection, req, res); + return ret; + } + + inline bool Server::is_valid() const + { + return true; + } + + inline bool Server::read_and_close_socket(socket_t sock) + { + return detail::read_and_close_socket( + sock, + keep_alive_max_count_, + [this](Stream& strm, bool last_connection, bool& connection_close) { + return process_request(strm, last_connection, connection_close); + }); + } + +// HTTP client implementation + inline Client::Client( + const char* host, int port, size_t timeout_sec) + : host_(host) + , port_(port) + , timeout_sec_(timeout_sec) + , host_and_port_(host_ + ":" + std::to_string(port_)) + { + } + + inline Client::~Client() + { + } + + inline bool Client::is_valid() const + { + return true; + } + + inline socket_t Client::create_client_socket() const + { + return detail::create_socket(host_.c_str(), port_, + [=](socket_t sock, struct addrinfo& ai) -> bool { + detail::set_nonblocking(sock, true); + + auto ret = connect(sock, ai.ai_addr, ai.ai_addrlen); + if (ret < 0) { + if (detail::is_connection_error() || + !detail::wait_until_socket_is_ready(sock, timeout_sec_, 0)) { + detail::close_socket(sock); + return false; + } + } + + detail::set_nonblocking(sock, false); + return true; + }); + } + + inline bool Client::read_response_line(Stream& strm, Response& res) + { + const auto bufsiz = 2048; + char buf[bufsiz]; + + detail::stream_line_reader reader(strm, buf, bufsiz); + + if (!reader.getline()) { + return false; + } + + const static std::regex re("(HTTP/1\\.[01]) (\\d+?) .+\r\n"); + + std::cmatch m; + if (std::regex_match(reader.ptr(), m, re)) { + res.version = std::string(m[1]); + res.status = std::stoi(std::string(m[2])); + } + + return true; + } + + inline bool Client::send(Request& req, Response& res) + { + if (req.path.empty()) { + return false; + } + + auto sock = create_client_socket(); + if (sock == INVALID_SOCKET) { + return false; + } + + return read_and_close_socket(sock, req, res); + } + + inline void Client::write_request(Stream& strm, Request& req) + { + auto path = detail::encode_url(req.path); + + // Request line + strm.write_format("%s %s HTTP/1.1\r\n", + req.method.c_str(), + path.c_str()); + + // Headers + req.set_header("Host", host_and_port_.c_str()); + + if (!req.has_header("Accept")) { + req.set_header("Accept", "*/*"); + } + + if (!req.has_header("User-Agent")) { + req.set_header("User-Agent", "cpp-httplib/0.2"); + } + + // TODO: Support KeepAlive connection + // if (!req.has_header("Connection")) { + req.set_header("Connection", "close"); + // } + + if (!req.body.empty()) { + if (!req.has_header("Content-Type")) { + req.set_header("Content-Type", "text/plain"); + } + + auto length = std::to_string(req.body.size()); + req.set_header("Content-Length", length.c_str()); + } + + detail::write_headers(strm, req); + + // Body + if (!req.body.empty()) { + if (req.get_header_value("Content-Type") == "application/x-www-form-urlencoded") { + auto str = detail::encode_url(req.body); + strm.write(str.c_str(), str.size()); + } else { + strm.write(req.body.c_str(), req.body.size()); + } + } + } + + inline bool Client::process_request(Stream& strm, Request& req, Response& res, bool& connection_close) + { + // Send request + write_request(strm, req); + + // Receive response and headers + if (!read_response_line(strm, res) || !detail::read_headers(strm, res.headers)) { + return false; + } + + if (res.get_header_value("Connection") == "close" || res.version == "HTTP/1.0") { + connection_close = true; + } + + // Body + if (req.method != "HEAD") { + if (!detail::read_content(strm, res, req.progress)) { + return false; + } + + if (res.get_header_value("Content-Encoding") == "gzip") { +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + detail::decompress(res.body); +#else + return false; +#endif + } + } + + return true; + } + + inline bool Client::read_and_close_socket(socket_t sock, Request& req, Response& res) + { + return detail::read_and_close_socket( + sock, + 0, + [&](Stream& strm, bool /*last_connection*/, bool& connection_close) { + return process_request(strm, req, res, connection_close); + }); + } + + inline std::shared_ptr Client::Get(const char* path, Progress progress) + { + return Get(path, Headers(), progress); + } + + inline std::shared_ptr Client::Get(const char* path, const Headers& headers, Progress progress) + { + Request req; + req.method = "GET"; + req.path = path; + req.headers = headers; + req.progress = progress; + + auto res = std::make_shared(); + + return send(req, *res) ? res : nullptr; + } + + inline std::shared_ptr Client::Head(const char* path) + { + return Head(path, Headers()); + } + + inline std::shared_ptr Client::Head(const char* path, const Headers& headers) + { + Request req; + req.method = "HEAD"; + req.headers = headers; + req.path = path; + + auto res = std::make_shared(); + + return send(req, *res) ? res : nullptr; + } + + inline std::shared_ptr Client::Post( + const char* path, const std::string& body, const char* content_type) + { + return Post(path, Headers(), body, content_type); + } + + inline std::shared_ptr Client::Post( + const char* path, const Headers& headers, const std::string& body, const char* content_type) + { + Request req; + req.method = "POST"; + req.headers = headers; + req.path = path; + + req.headers.emplace("Content-Type", content_type); + req.body = body; + + auto res = std::make_shared(); + + return send(req, *res) ? res : nullptr; + } + + inline std::shared_ptr Client::Post(const char* path, const Params& params) + { + return Post(path, Headers(), params); + } + + inline std::shared_ptr Client::Post(const char* path, const Headers& headers, const Params& params) + { + std::string query; + for (auto it = params.begin(); it != params.end(); ++it) { + if (it != params.begin()) { + query += "&"; + } + query += it->first; + query += "="; + query += it->second; + } + + return Post(path, headers, query, "application/x-www-form-urlencoded"); + } + + inline std::shared_ptr Client::Put( + const char* path, const std::string& body, const char* content_type) + { + return Put(path, Headers(), body, content_type); + } + + inline std::shared_ptr Client::Put( + const char* path, const Headers& headers, const std::string& body, const char* content_type) + { + Request req; + req.method = "PUT"; + req.headers = headers; + req.path = path; + + req.headers.emplace("Content-Type", content_type); + req.body = body; + + auto res = std::make_shared(); + + return send(req, *res) ? res : nullptr; + } + + inline std::shared_ptr Client::Delete(const char* path) + { + return Delete(path, Headers()); + } + + inline std::shared_ptr Client::Delete(const char* path, const Headers& headers) + { + Request req; + req.method = "DELETE"; + req.path = path; + req.headers = headers; + + auto res = std::make_shared(); + + return send(req, *res) ? res : nullptr; + } + + inline std::shared_ptr Client::Options(const char* path) + { + return Options(path, Headers()); + } + + inline std::shared_ptr Client::Options(const char* path, const Headers& headers) + { + Request req; + req.method = "OPTIONS"; + req.path = path; + req.headers = headers; + + auto res = std::make_shared(); + + return send(req, *res) ? res : nullptr; + } + +/* + * SSL Implementation + */ +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + namespace detail { + + template + inline bool read_and_close_socket_ssl( + socket_t sock, size_t keep_alive_max_count, + // TODO: OpenSSL 1.0.2 occasionally crashes... + // The upcoming 1.1.0 is going to be thread safe. + SSL_CTX* ctx, std::mutex& ctx_mutex, + U SSL_connect_or_accept, V setup, + T callback) + { + SSL* ssl = nullptr; + { + std::lock_guard guard(ctx_mutex); + + ssl = SSL_new(ctx); + if (!ssl) { + return false; + } + } + + auto bio = BIO_new_socket(sock, BIO_NOCLOSE); + SSL_set_bio(ssl, bio, bio); + + setup(ssl); + + SSL_connect_or_accept(ssl); + + bool ret = false; + + if (keep_alive_max_count > 0) { + auto count = keep_alive_max_count; + while (count > 0 && + detail::select_read(sock, + CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND, + CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND) > 0) { + SSLSocketStream strm(sock, ssl); + auto last_connection = count == 1; + auto connection_close = false; + + ret = callback(strm, last_connection, connection_close); + if (!ret || connection_close) { + break; + } + + count--; + } + } else { + SSLSocketStream strm(sock, ssl); + auto dummy_connection_close = false; + ret = callback(strm, true, dummy_connection_close); + } + + SSL_shutdown(ssl); + + { + std::lock_guard guard(ctx_mutex); + SSL_free(ssl); + } + + close_socket(sock); + + return ret; + } + + class SSLInit { + public: + SSLInit() { + SSL_load_error_strings(); + SSL_library_init(); + } + }; + + static SSLInit sslinit_; + + } // namespace detail + +// SSL socket stream implementation + inline SSLSocketStream::SSLSocketStream(socket_t sock, SSL* ssl) + : sock_(sock), ssl_(ssl) + { + } + + inline SSLSocketStream::~SSLSocketStream() + { + } + + inline int SSLSocketStream::read(char* ptr, size_t size) + { + return SSL_read(ssl_, ptr, size); + } + + inline int SSLSocketStream::write(const char* ptr, size_t size) + { + return SSL_write(ssl_, ptr, size); + } + + inline int SSLSocketStream::write(const char* ptr) + { + return write(ptr, strlen(ptr)); + } + + inline std::string SSLSocketStream::get_remote_addr() { + return detail::get_remote_addr(sock_); + } + +// SSL HTTP server implementation + inline SSLServer::SSLServer(const char* cert_path, const char* private_key_path) + { + ctx_ = SSL_CTX_new(SSLv23_server_method()); + + if (ctx_) { + SSL_CTX_set_options(ctx_, + SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | + SSL_OP_NO_COMPRESSION | + SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); + + // auto ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); + // SSL_CTX_set_tmp_ecdh(ctx_, ecdh); + // EC_KEY_free(ecdh); + + if (SSL_CTX_use_certificate_file(ctx_, cert_path, SSL_FILETYPE_PEM) != 1 || + SSL_CTX_use_PrivateKey_file(ctx_, private_key_path, SSL_FILETYPE_PEM) != 1) { + SSL_CTX_free(ctx_); + ctx_ = nullptr; + } + } + } + + inline SSLServer::~SSLServer() + { + if (ctx_) { + SSL_CTX_free(ctx_); + } + } + + inline bool SSLServer::is_valid() const + { + return ctx_; + } + + inline bool SSLServer::read_and_close_socket(socket_t sock) + { + return detail::read_and_close_socket_ssl( + sock, + keep_alive_max_count_, + ctx_, ctx_mutex_, + SSL_accept, + [](SSL* /*ssl*/) {}, + [this](Stream& strm, bool last_connection, bool& connection_close) { + return process_request(strm, last_connection, connection_close); + }); + } + + // SSL HTTP client implementation + inline SSLClient::SSLClient(const char* host, int port, size_t timeout_sec) + : Client(host, port, timeout_sec) + { + ctx_ = SSL_CTX_new(SSLv23_client_method()); + } + + inline SSLClient::~SSLClient() + { + if (ctx_) { + SSL_CTX_free(ctx_); + } + } + + inline bool SSLClient::is_valid() const + { + return ctx_; + } + + inline bool SSLClient::read_and_close_socket(socket_t sock, Request& req, Response& res) + { + return is_valid() && detail::read_and_close_socket_ssl( + sock, 0, + ctx_, ctx_mutex_, + SSL_connect, + [&](SSL* ssl) { + SSL_set_tlsext_host_name(ssl, host_.c_str()); + }, + [&](Stream& strm, bool /*last_connection*/, bool& connection_close) { + return process_request(strm, req, res, connection_close); + }); + } +#endif + +} // namespace httplib + +#endif + +// vim: et ts=4 sw=4 cin cino={1s ff=unix \ No newline at end of file diff --git a/src/3rdparty/cpp-httplib/test/Makefile b/src/3rdparty/cpp-httplib/test/Makefile new file mode 100644 index 00000000..76809f33 --- /dev/null +++ b/src/3rdparty/cpp-httplib/test/Makefile @@ -0,0 +1,17 @@ + +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 new file mode 100644 index 00000000..5ced66a9 --- /dev/null +++ b/src/3rdparty/cpp-httplib/test/gtest/gtest-all.cc @@ -0,0 +1,9118 @@ +// 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 new file mode 100644 index 00000000..3143bd67 --- /dev/null +++ b/src/3rdparty/cpp-httplib/test/gtest/gtest.h @@ -0,0 +1,19537 @@ +// 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 new file mode 100644 index 00000000..a09bbe0c --- /dev/null +++ b/src/3rdparty/cpp-httplib/test/gtest/gtest_main.cc @@ -0,0 +1,39 @@ +// 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 new file mode 100644 index 00000000..98f09542 --- /dev/null +++ b/src/3rdparty/cpp-httplib/test/test.cc @@ -0,0 +1,378 @@ + +#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 new file mode 100644 index 00000000..8377dd7f --- /dev/null +++ b/src/3rdparty/cpp-httplib/test/test.sln @@ -0,0 +1,28 @@ + +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 new file mode 100644 index 00000000..b19fd4c3 --- /dev/null +++ b/src/3rdparty/cpp-httplib/test/test.vcxproj @@ -0,0 +1,157 @@ + + + + + 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 new file mode 100644 index 00000000..abff30ef --- /dev/null +++ b/src/3rdparty/cpp-httplib/test/test.xcodeproj/project.pbxproj @@ -0,0 +1,248 @@ +// !$*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 new file mode 100644 index 00000000..d683bc92 --- /dev/null +++ b/src/3rdparty/cpp-httplib/test/test.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/src/3rdparty/cpp-httplib/test/www/dir/index.html b/src/3rdparty/cpp-httplib/test/www/dir/index.html new file mode 100644 index 00000000..be3c05f7 --- /dev/null +++ b/src/3rdparty/cpp-httplib/test/www/dir/index.html @@ -0,0 +1,8 @@ + + + + + Test + hi + + diff --git a/src/3rdparty/cpp-httplib/test/www/dir/test.html b/src/3rdparty/cpp-httplib/test/www/dir/test.html new file mode 100644 index 00000000..6d70cd0e --- /dev/null +++ b/src/3rdparty/cpp-httplib/test/www/dir/test.html @@ -0,0 +1 @@ +test.html \ No newline at end of file diff --git a/src/api/NetworkState.cpp b/src/api/NetworkState.cpp index 0ab80093..9428cfbe 100644 --- a/src/api/NetworkState.cpp +++ b/src/api/NetworkState.cpp @@ -38,6 +38,7 @@ NetworkState::NetworkState() : failures(0), rejected(0), total(0), + powVariant(xmrig::VARIANT_AUTO), m_active(false) { memset(pool, 0, sizeof(pool)); diff --git a/src/api/NetworkState.h b/src/api/NetworkState.h index d0998074..45cea38c 100644 --- a/src/api/NetworkState.h +++ b/src/api/NetworkState.h @@ -27,6 +27,7 @@ #include #include +#include class SubmitResult; @@ -51,6 +52,7 @@ class NetworkState uint64_t failures; uint64_t rejected; uint64_t total; + xmrig::Variant powVariant; private: bool m_active; diff --git a/src/cc/CCClient.cpp b/src/cc/CCClient.cpp new file mode 100644 index 00000000..fe6f311b --- /dev/null +++ b/src/cc/CCClient.cpp @@ -0,0 +1,335 @@ +/* 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 <3rdparty/rapidjson/stringbuffer.h> +#include <3rdparty/rapidjson/prettywriter.h> +#include +//#include +#include +#include +#include + +#include "CCClient.h" +#include "App.h" +#include "Cpu.h" +#include "ControlCommand.h" + +#include "common/log/Log.h" +#include "workers/Workers.h" +#include "workers/Hashrate.h" + +#if _WIN32 +# include "winsock2.h" +#else + +# include "unistd.h" + +#endif + + +CCClient* CCClient::m_self = nullptr; +uv_mutex_t CCClient::m_mutex; + +CCClient::CCClient(xmrig::Controller* controller, uv_async_t* async) + : m_controller(controller), + m_async(async) +{ + uv_mutex_init(&m_mutex); + + m_self = this; + + xmrig::Config* config = m_controller->config(); + + std::string clientId; + if (m_options->ccWorkerId()) { + clientId = m_options->ccWorkerId(); + } else { + char hostname[128]; + memset(hostname, 0, sizeof(hostname)); + gethostname(hostname, sizeof(hostname) - 1); + clientId = std::string(hostname); + } + + m_clientStatus.setClientId(clientId); + + if (m_controller->config() != nullptr) { + m_clientStatus.setCurrentAlgoName(config->algorithm().name())); + } + + m_clientStatus.setHugepages(""); + m_clientStatus.setHashFactor(-1); + + m_clientStatus.setVersion(Version::string()); + m_clientStatus.setCpuBrand(Cpu::brand()); + m_clientStatus.setCpuAES(Cpu::hasAES()); + m_clientStatus.setCpuSockets(Cpu::sockets()); + m_clientStatus.setCpuCores(Cpu::cores()); + m_clientStatus.setCpuThreads(Cpu::threads()); + m_clientStatus.setCpuX64(Cpu::isX64()); + m_clientStatus.setCpuL2(Cpu::l2()); + m_clientStatus.setCpuL3(Cpu::l3()); + m_clientStatus.setCurrentThreads(static_cast(config->threads().size())); + + m_startTime = std::chrono::system_clock::now(); + + if (m_options->ccToken() != nullptr) { + m_authorization = std::string("Bearer ") + m_self->m_options->ccToken(); + } + + uv_thread_create(&m_thread, CCClient::onThreadStarted, this); +} + +CCClient::~CCClient() +{ + uv_timer_stop(&m_timer); + m_self = nullptr; +} + +void CCClient::updateHashrate(const Hashrate* hashrate) +{ + if (m_self) { + uv_mutex_lock(&m_mutex); + + 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); + } +} + + +void CCClient::updateNetworkState(const NetworkState& network) +{ + if (m_self) { + uv_mutex_lock(&m_mutex); + + 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()); + m_self->m_clientStatus.setCurrentPowVariantName(xmrig::Algorithm::getVariantName(network.powVariant)); + + uv_mutex_unlock(&m_mutex); + } +} + +void CCClient::publishClientStatusReport() +{ + std::string requestUrl = "/client/setClientStatus?clientId=" + m_self->m_clientStatus.getClientId(); + std::string requestBuffer = m_self->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_options->ccHost(), m_self->m_options->ccPort(), requestUrl.c_str()); + } else if (res->status != 200) { + LOG_ERR("[CC-Client] error: \"%d\" -> http://%s:%d%s", res->status, m_self->m_options->ccHost(), + m_self->m_options->ccPort(), 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"); + } + } else if (controlCommand.getCommand() == ControlCommand::STOP) { + if (Workers::isEnabled()) { + LOG_WARN("[CC-Client] Command: STOP received -> pause"); + } + } else if (controlCommand.getCommand() == ControlCommand::UPDATE_CONFIG) { + LOG_WARN("[CC-Client] Command: UPDATE_CONFIG received -> update config"); + updateConfig(); + } 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 -> restart"); + } else if (controlCommand.getCommand() == ControlCommand::SHUTDOWN) { + LOG_WARN("[CC-Client] Command: SHUTDOWN received -> shutdown"); + } + + m_self->m_async->data = reinterpret_cast(controlCommand.getCommand()); + uv_async_send(m_self->m_async); + } else { + LOG_ERR("[CC-Client] Unknown command received from CC Server."); + } + } +} + +void CCClient::updateConfig() +{ + std::string requestUrl = "/client/getConfig?clientId=" + m_self->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_options->ccHost(), m_self->m_options->ccPort(), requestUrl.c_str()); + } else if (res->status != 200) { + LOG_ERR("[CC-Client] error: \"%d\" -> http://%s:%d%s", res->status, m_self->m_options->ccHost(), + m_self->m_options->ccPort(), requestUrl.c_str()); + } else { + rapidjson::Document document; + if (!document.Parse(res->body.c_str()).HasParseError()) { + std::ofstream clientConfigFile(m_self->m_options->configFile()); + if (clientConfigFile) { + rapidjson::StringBuffer buffer(0, 65536); + rapidjson::PrettyWriter writer(buffer); + writer.SetMaxDecimalPlaces(10); + document.Accept(writer); + + clientConfigFile << buffer.GetString(); + clientConfigFile.close(); + + LOG_WARN("[CC-Client] Config updated. -> trigger restart"); + } else { + LOG_ERR("[CC-Client] Not able to store client config to file %s.", m_self->m_options->configFile()); + } + } else { + LOG_ERR("[CC-Client] Not able to store client config. received client config is broken!"); + } + } +} + +void CCClient::publishConfig() +{ + std::string requestUrl = "/client/setClientConfig?clientId=" + m_self->m_clientStatus.getClientId(); + + std::stringstream data; + std::ifstream clientConfig(m_self->m_options->configFile()); + + if (clientConfig) { + data << clientConfig.rdbuf(); + clientConfig.close(); + } + + if (data.tellp() > 0) { + rapidjson::Document document; + document.Parse(data.str().c_str()); + + if (!document.HasParseError()) { + rapidjson::StringBuffer buffer(0, 65536); + rapidjson::Writer writer(buffer); + writer.SetMaxDecimalPlaces(10); + document.Accept(writer); + + 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_options->ccHost(), m_self->m_options->ccPort(), requestUrl.c_str()); + } else if (res->status != 200) { + LOG_ERR("[CC-Client] error: \"%d\" -> http://%s:%d%s", res->status, m_self->m_options->ccHost(), + m_self->m_options->ccPort(), requestUrl.c_str()); + } + } else { + LOG_ERR("Not able to send config. Client config %s is broken!", m_self->m_options->configFile()); + } + } else { + LOG_ERR("Not able to load client config %s. Please make sure it exists!", m_self->m_options->configFile()); + } +} + +std::shared_ptr CCClient::performRequest(const std::string& requestUrl, + const std::string& requestBuffer, + const std::string& operation) +{ + std::shared_ptr cli; + +# ifndef XMRIG_NO_TLS + if (m_self->m_options->ccUseTls()) { + cli = std::make_shared(m_self->m_options->ccHost(), m_self->m_options->ccPort(), 10); + } else { +# endif + cli = std::make_shared(m_self->m_options->ccHost(), m_self->m_options->ccPort(), 10); +# ifndef XMRIG_NO_TLS + } +# endif + + httplib::Request req; + req.method = operation; + req.path = requestUrl; + req.set_header("Host", ""); + 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 (!requestBuffer.empty()) { + req.body = requestBuffer; + } + + auto res = std::make_shared(); + + return cli->send(req, *res) ? res : nullptr; +} + +void CCClient::refreshUptime() +{ + 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())); +} + +void CCClient::refreshLog() +{ + //m_self->m_clientStatus.setLog(RemoteLog::getRows()); +} + +void CCClient::onThreadStarted(void* handle) +{ + if (m_self) { + uv_loop_init(&m_self->m_client_loop); + + 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_options->ccUpdateInterval() * 1000), + static_cast(m_self->m_options->ccUpdateInterval() * 1000)); + + m_self->publishConfig(); + + uv_run(&m_self->m_client_loop, UV_RUN_DEFAULT); + } +} + +void CCClient::onReport(uv_timer_t* handle) +{ + if (m_self) { + m_self->refreshUptime(); + m_self->refreshLog(); + + m_self->publishClientStatusReport(); + } +} diff --git a/src/cc/CCClient.h b/src/cc/CCClient.h new file mode 100644 index 00000000..559b6115 --- /dev/null +++ b/src/cc/CCClient.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 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 __CC_CLIENT_H__ +#define __CC_CLIENT_H__ + +#ifndef XMRIG_NO_CC + +#include +#include +#include +#include <3rdparty/cpp-httplib/httplib.h> +#include +#include "ClientStatus.h" + +class Hashrate; +class NetworkState; + +class CCClient +{ +public: + CCClient(xmrig::Controller* m_controller, uv_async_t* async); + ~CCClient(); + + static void updateHashrate(const Hashrate *hashrate); + static void updateNetworkState(const NetworkState &results); + +private: + + void publishClientStatusReport(); + void updateConfig(); + 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); + + const xmrig::Controller* m_controller; + + static CCClient* m_self; + static uv_mutex_t m_mutex; + + ClientStatus m_clientStatus; + + std::string m_authorization; + + 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; +}; + +#endif +#endif /* __CC_CLIENT_H__ */ diff --git a/src/cc/CCServer.cpp b/src/cc/CCServer.cpp new file mode 100644 index 00000000..a65b4816 --- /dev/null +++ b/src/cc/CCServer.cpp @@ -0,0 +1,200 @@ +/* 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 + * + * + * This 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 "CCServer.h" +#include "Service.h" +#include "Httpd.h" +#include "Console.h" +#include "log/ConsoleLog.h" +#include "log/FileLog.h" +#include "log/Log.h" +#include "Options.h" +#include "Summary.h" + +#if _WIN32 + #include + #include +#else +# include "unistd.h" +#endif + +#ifdef HAVE_SYSLOG_H +# include "log/SysLog.h" +#endif + +CCServer *CCServer::m_self = nullptr; + + +CCServer::CCServer(int argc, char** argv) : + m_console(nullptr), + m_httpd(nullptr), + m_options(nullptr) +{ + m_self = this; + + Log::init(); + + m_options = Options::parse(argc, argv); + if (!m_options) { + return; + } + + if (!m_options->background()) { + Log::add(new ConsoleLog(m_options->colors())); + m_console = new Console(this); + } + + if (m_options->logFile()) { + Log::add(new FileLog(m_options->logFile())); + } + +# ifdef HAVE_SYSLOG_H + if (m_options->syslog()) { + Log::add(new SysLog()); + } +# endif + + uv_signal_init(uv_default_loop(), &m_signal); +} + +CCServer::~CCServer() +{ + uv_tty_reset_mode(); + + delete m_httpd; +} + +int CCServer::start() +{ + if (!m_options) { + return EINVAL; + } + + uv_signal_start(&m_signal, CCServer::onSignal, SIGHUP); + uv_signal_start(&m_signal, CCServer::onSignal, SIGTERM); + uv_signal_start(&m_signal, CCServer::onSignal, SIGINT); + + if (m_options->background()) { + moveToBackground(); + } + + Summary::print(); + + Service::start(); + + m_httpd = new Httpd(m_options); + if (!m_httpd->start()) { + return EINVAL; + } + + const int r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + uv_loop_close(uv_default_loop()); + + Options::release(); + + return r; +} + +void CCServer::onConsoleCommand(char command) +{ + switch (command) { + case 'q': + case 'Q': + stop(); + break; + + case 3: + LOG_WARN("Ctrl+C received, exiting"); + stop(); + break; + + default: + break; + } +} + +void CCServer::stop() +{ + uv_stop(uv_default_loop()); +} + +void CCServer::onSignal(uv_signal_t* handle, int signum) +{ + switch (signum) + { + case SIGHUP: + LOG_WARN("SIGHUP received, exiting"); + break; + + case SIGTERM: + LOG_WARN("SIGTERM received, exiting"); + break; + + case SIGINT: + LOG_WARN("SIGINT received, exiting"); + break; + + default: + break; + } + + uv_signal_stop(handle); + m_self->stop(); +} + +void CCServer::moveToBackground() +{ +#ifdef WIN32 + HWND hcon = GetConsoleWindow(); + if (hcon) { + ShowWindow(hcon, SW_HIDE); + } else { + HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE); + CloseHandle(h); + FreeConsole(); + } +#else + 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); + } +#endif +} diff --git a/src/cc/CCServer.h b/src/cc/CCServer.h new file mode 100644 index 00000000..ebbf6d4d --- /dev/null +++ b/src/cc/CCServer.h @@ -0,0 +1,65 @@ +/* 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 + * + * + * This 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 __CC_SERVER_H__ +#define __CC_SERVER_H__ + + +#include + + +#include "common/interfaces/IConsoleListener.h" + + +class Console; +class Httpd; +class Options; + +class CCServer : public IConsoleListener +{ +public: + CCServer(int argc, char **argv); + ~CCServer(); + + int start(); + +protected: + void onConsoleCommand(char command) override; + +private: + void stop(); + void moveToBackground(); + + static void onSignal(uv_signal_t* handle, int signum); + + static CCServer* m_self; + + Console* m_console; + Httpd* m_httpd; + Options* m_options; + uv_signal_t m_signal; +}; + + +#endif /* __CC_SERVER_H__ */ diff --git a/src/cc/ClientStatus.cpp b/src/cc/ClientStatus.cpp new file mode 100644 index 00000000..24057ebd --- /dev/null +++ b/src/cc/ClientStatus.cpp @@ -0,0 +1,555 @@ +/* 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 <3rdparty/rapidjson/stringbuffer.h> +#include <3rdparty/rapidjson/prettywriter.h> +#include "common/log/Log.h" + +#include "ClientStatus.h" + +ClientStatus::ClientStatus() + : m_currentStatus(Status::PAUSED), + m_hasHugepages(false), + m_isHugepagesEnabled(false), + m_isCpuX64(false), + m_hasCpuAES(false), + m_hashFactor(1), + m_hashrateShort(0), + m_hashrateMedium(0), + m_hashrateLong(0), + m_hashrateHighest(0), + m_currentThreads(0), + m_cpuSockets(0), + m_cpuCores(0), + m_cpuThreads(0), + m_cpuL2(0), + m_cpuL3(0), + m_sharesGood(0), + m_sharesTotal(0), + m_hashesTotal(0), + m_uptime(0), + m_avgTime(0), + m_lastStatusUpdate(0) +{ + +} + +ClientStatus::Status ClientStatus::getCurrentStatus() const +{ + return m_currentStatus; +} + +void ClientStatus::setCurrentStatus(Status currentStatus) +{ + m_currentStatus = currentStatus; +} + +std::string ClientStatus::getClientId() const +{ + return m_clientId; +} + +void ClientStatus::setClientId(const std::string& clientId) +{ + m_clientId = clientId; +} + +std::string ClientStatus::getCurrentPool() const +{ + return m_currentPool; +} + +void ClientStatus::setCurrentPool(const std::string& currentPool) +{ + m_currentPool = currentPool; +} + +void ClientStatus::setCurrentAlgoName(const std::string& algoName) +{ + m_currentAlgoName = algoName; +} + +std::string ClientStatus::getCurrentAlgoName() const +{ + return m_currentAlgoName; +} + +void ClientStatus::setCurrentPowVariantName(const std::string& powVariantName) +{ + m_currentPowVariantName = powVariantName; +} + +std::string ClientStatus::getCurrentPowVariantName() const +{ + return m_currentPowVariantName; +} + +std::string ClientStatus::getCpuBrand() const +{ + return m_cpuBrand; +} + +void ClientStatus::setCpuBrand(const std::string& cpuBrand) +{ + m_cpuBrand = cpuBrand; +} + +std::string ClientStatus::getExternalIp() const +{ + return m_externalIp; +} + +void ClientStatus::setExternalIp(const std::string& externalIp) +{ + m_externalIp = externalIp; +} + +std::string ClientStatus::getVersion() const +{ + return m_version; +} + +void ClientStatus::setVersion(const std::string& version) +{ + m_version = version; +} + +std::string ClientStatus::getLog() const +{ + return m_log; +} + +void ClientStatus::setLog(const std::string& log) +{ + m_log = log; +} + +bool ClientStatus::hasHugepages() const +{ + return m_hasHugepages; +} + +void ClientStatus::setHugepages(bool hasHugepages) +{ + m_hasHugepages = hasHugepages; +} + +bool ClientStatus::isHugepagesEnabled() const +{ + return m_isHugepagesEnabled; +} + +void ClientStatus::setHugepagesEnabled(bool hugepagesEnabled) +{ + m_isHugepagesEnabled = hugepagesEnabled; +} + +int ClientStatus::getHashFactor() const +{ + return m_hashFactor; +} + +void ClientStatus::setHashFactor(int hashFactor) +{ + m_hashFactor = hashFactor; +} + +bool ClientStatus::isCpuX64() const +{ + return m_isCpuX64; +} + +void ClientStatus::setCpuX64(bool isCpuX64) +{ + m_isCpuX64 = isCpuX64; +} + +bool ClientStatus::hasCpuAES() const +{ + return m_hasCpuAES; +} + +void ClientStatus::setCpuAES(bool hasCpuAES) +{ + m_hasCpuAES = hasCpuAES; +} + +double ClientStatus::getHashrateShort() const +{ + return m_hashrateShort; +} + +void ClientStatus::setHashrateShort(double hashrateShort) +{ + m_hashrateShort = hashrateShort; +} + +double ClientStatus::getHashrateMedium() const +{ + return m_hashrateMedium; +} + +void ClientStatus::setHashrateMedium(double hashrateMedium) +{ + m_hashrateMedium = hashrateMedium; +} + +double ClientStatus::getHashrateLong() const +{ + return m_hashrateLong; +} + +void ClientStatus::setHashrateLong(double hashrateLong) +{ + m_hashrateLong = hashrateLong; +} + +void ClientStatus::setHashrateHighest(double hashrateHighest) +{ + m_hashrateHighest = hashrateHighest; +} + +double ClientStatus::getHashrateHighest() const +{ + return m_hashrateHighest; +} + +int ClientStatus::getCurrentThreads() const +{ + return m_currentThreads; +} + +void ClientStatus::setCurrentThreads(int currentThreads) +{ + m_currentThreads = currentThreads; +} + +int ClientStatus::getCpuSockets() const +{ + return m_cpuSockets; +} + +void ClientStatus::setCpuSockets(int cpuSockets) +{ + m_cpuSockets = cpuSockets; +} + +int ClientStatus::getCpuCores() const +{ + return m_cpuCores; +} + +void ClientStatus::setCpuCores(int cpuCores) +{ + m_cpuCores = cpuCores; +} + +int ClientStatus::getCpuThreads() const +{ + return m_cpuThreads; +} + +void ClientStatus::setCpuThreads(int cpuThreads) +{ + m_cpuThreads = cpuThreads; +} + +int ClientStatus::getCpuL2() const +{ + return m_cpuL2; +} + +void ClientStatus::setCpuL2(int cpuL2) +{ + m_cpuL2 = cpuL2; +} + +int ClientStatus::getCpuL3() const +{ + return m_cpuL3; +} + +void ClientStatus::setCpuL3(int cpuL3) +{ + m_cpuL3 = cpuL3; +} + +uint64_t ClientStatus::getSharesGood() const +{ + return m_sharesGood; +} + +void ClientStatus::setSharesGood(uint64_t sharesGood) +{ + m_sharesGood = sharesGood; +} + +uint64_t ClientStatus::getSharesTotal() const +{ + return m_sharesTotal; +} + +void ClientStatus::setSharesTotal(uint64_t sharesTotal) +{ + m_sharesTotal = sharesTotal; +} + +uint64_t ClientStatus::getHashesTotal() const +{ + return m_hashesTotal; +} + +void ClientStatus::setHashesTotal(uint64_t hashesTotal) +{ + m_hashesTotal = hashesTotal; +} +void ClientStatus::setAvgTime(uint32_t avgTime) +{ + m_avgTime = avgTime; +} + +uint32_t ClientStatus::getAvgTime() const +{ + return m_avgTime; +} + +std::time_t ClientStatus::getLastStatusUpdate() const +{ + return m_lastStatusUpdate; +} + +uint64_t ClientStatus::getUptime() const +{ + return m_uptime; +} + +void ClientStatus::setUptime(uint64_t uptime) +{ + m_uptime = uptime; +} + +bool ClientStatus::parseFromJson(const rapidjson::Document& document) +{ + bool result = false; + + if (document.HasMember("client_status")) + { + rapidjson::Value::ConstObject clientStatus = document["client_status"].GetObject(); + + if (clientStatus.HasMember("current_status")) { + m_currentStatus = toStatus(clientStatus["current_status"].GetString()); + } + + if (clientStatus.HasMember("client_id")) { + m_clientId = clientStatus["client_id"].GetString(); + } + + if (clientStatus.HasMember("current_pool")) { + m_currentPool = clientStatus["current_pool"].GetString(); + } + + if (clientStatus.HasMember("current_algo_name")) { + m_currentAlgoName = clientStatus["current_algo_name"].GetString(); + } + + if (clientStatus.HasMember("current_pow_variant_name")) { + m_currentPowVariantName = clientStatus["current_pow_variant_name"].GetString(); + } + + if (clientStatus.HasMember("cpu_brand")) { + m_cpuBrand = clientStatus["cpu_brand"].GetString(); + } + + if (clientStatus.HasMember("external_ip")) { + m_externalIp = clientStatus["external_ip"].GetString(); + } + + if (clientStatus.HasMember("version")) { + m_version = clientStatus["version"].GetString(); + } + + if (clientStatus.HasMember("log")) { + m_log = clientStatus["log"].GetString(); + } + + if (clientStatus.HasMember("hugepages_available")) { + m_hasHugepages = clientStatus["hugepages_available"].GetBool(); + } + + if (clientStatus.HasMember("hugepages_enabled")) { + m_isHugepagesEnabled = clientStatus["hugepages_enabled"].GetBool(); + } + + if (clientStatus.HasMember("hash_factor")) { + m_hashFactor = clientStatus["hash_factor"].GetInt(); + } + + if (clientStatus.HasMember("cpu_is_x64")) { + m_isCpuX64 = clientStatus["cpu_is_x64"].GetBool(); + } + + if (clientStatus.HasMember("cpu_has_aes")) { + m_hasCpuAES = clientStatus["cpu_has_aes"].GetBool(); + } + + if (clientStatus.HasMember("hashrate_short")) { + m_hashrateShort = clientStatus["hashrate_short"].GetDouble(); + } + + if (clientStatus.HasMember("hashrate_medium")) { + m_hashrateMedium = clientStatus["hashrate_medium"].GetDouble(); + } + + if (clientStatus.HasMember("hashrate_long")) { + m_hashrateLong = clientStatus["hashrate_long"].GetDouble(); + } + + if (clientStatus.HasMember("hashrate_highest")) { + m_hashrateHighest = clientStatus["hashrate_highest"].GetDouble(); + } + + if (clientStatus.HasMember("current_threads")) { + m_currentThreads = clientStatus["current_threads"].GetInt(); + } + + if (clientStatus.HasMember("cpu_sockets")) { + m_cpuSockets = clientStatus["cpu_sockets"].GetInt(); + } + + if (clientStatus.HasMember("cpu_cores")) { + m_cpuCores = clientStatus["cpu_cores"].GetInt(); + } + + if (clientStatus.HasMember("cpu_threads")) { + m_cpuThreads = clientStatus["cpu_threads"].GetInt(); + } + + if (clientStatus.HasMember("cpu_l2")) { + m_cpuL2 = clientStatus["cpu_l2"].GetInt(); + } + + if (clientStatus.HasMember("cpu_l3")) { + m_cpuL3 = clientStatus["cpu_l3"].GetInt(); + } + + if (clientStatus.HasMember("shares_good")) { + m_sharesGood = clientStatus["shares_good"].GetUint64(); + } + + if (clientStatus.HasMember("shares_total")) { + m_sharesTotal = clientStatus["shares_total"].GetUint64(); + } + + if (clientStatus.HasMember("hashes_total")) { + m_hashesTotal = clientStatus["hashes_total"].GetUint64(); + } + + if (clientStatus.HasMember("avg_time")) { + m_avgTime = clientStatus["avg_time"].GetUint(); + } + + if (clientStatus.HasMember("uptime")) { + m_uptime = clientStatus["uptime"].GetUint64(); + } + + auto time_point = std::chrono::system_clock::now(); + m_lastStatusUpdate = std::chrono::system_clock::to_time_t(time_point); + + result = true; + } else { + LOG_ERR("Parse Error, JSON does not contain: control_command"); + } + + return result; +} + +rapidjson::Value ClientStatus::toJson(rapidjson::MemoryPoolAllocator& allocator) +{ + rapidjson::Value clientStatus(rapidjson::kObjectType); + + clientStatus.AddMember("current_status", rapidjson::StringRef(toString(m_currentStatus)), allocator); + + clientStatus.AddMember("client_id", rapidjson::StringRef(m_clientId.c_str()), allocator); + clientStatus.AddMember("current_pool", rapidjson::StringRef(m_currentPool.c_str()), allocator); + clientStatus.AddMember("current_algo_name", rapidjson::StringRef(m_currentAlgoName.c_str()), allocator); + clientStatus.AddMember("current_pow_variant_name", rapidjson::StringRef(m_currentPowVariantName.c_str()), allocator); + clientStatus.AddMember("cpu_brand", rapidjson::StringRef(m_cpuBrand.c_str()), allocator); + clientStatus.AddMember("external_ip", rapidjson::StringRef(m_externalIp.c_str()), allocator); + clientStatus.AddMember("version", rapidjson::StringRef(m_version.c_str()), allocator); + + clientStatus.AddMember("hugepages_available", m_hasHugepages, allocator); + clientStatus.AddMember("hugepages_enabled", m_isHugepagesEnabled, allocator); + clientStatus.AddMember("hash_factor", m_hashFactor, allocator); + clientStatus.AddMember("cpu_is_x64", m_isCpuX64, allocator); + clientStatus.AddMember("cpu_has_aes", m_hasCpuAES, allocator); + + clientStatus.AddMember("hashrate_short", m_hashrateShort, allocator); + clientStatus.AddMember("hashrate_medium", m_hashrateMedium, allocator); + clientStatus.AddMember("hashrate_long", m_hashrateLong, allocator); + clientStatus.AddMember("hashrate_highest", m_hashrateHighest, allocator); + + clientStatus.AddMember("current_threads", m_currentThreads, allocator); + clientStatus.AddMember("cpu_sockets", m_cpuSockets, allocator); + clientStatus.AddMember("cpu_cores", m_cpuCores, allocator); + clientStatus.AddMember("cpu_threads", m_cpuThreads, allocator); + clientStatus.AddMember("cpu_l2", m_cpuL2, allocator); + clientStatus.AddMember("cpu_l3", m_cpuL3, allocator); + + clientStatus.AddMember("shares_good", m_sharesGood, allocator); + clientStatus.AddMember("shares_total", m_sharesTotal, allocator); + clientStatus.AddMember("hashes_total", m_hashesTotal, allocator); + + clientStatus.AddMember("avg_time", m_avgTime, allocator); + + clientStatus.AddMember("uptime", m_uptime, allocator); + + clientStatus.AddMember("last_status_update", static_cast(m_lastStatusUpdate), allocator); + + clientStatus.AddMember("log", rapidjson::StringRef(m_log.c_str()), allocator); + + + return clientStatus; +} + +std::string ClientStatus::toJsonString() +{ + rapidjson::Document respDocument; + respDocument.SetObject(); + + auto& allocator = respDocument.GetAllocator(); + + rapidjson::Value clientStatus = ClientStatus::toJson(allocator); + respDocument.AddMember("client_status", clientStatus, allocator); + + rapidjson::StringBuffer buffer(0, 4096); + rapidjson::Writer writer(buffer); + writer.SetMaxDecimalPlaces(10); + respDocument.Accept(writer); + + return strdup(buffer.GetString()); +} + diff --git a/src/cc/ClientStatus.h b/src/cc/ClientStatus.h new file mode 100644 index 00000000..e524c9c1 --- /dev/null +++ b/src/cc/ClientStatus.h @@ -0,0 +1,200 @@ +/* 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 __CLIENT_STATUS_H__ +#define __CLIENT_STATUS_H__ + +#include +#include +#include + +class ClientStatus +{ + +public: + enum Status { + RUNNING, + PAUSED + }; + +public: + ClientStatus(); + + inline const char *toString (Status status) + { + return status_str[static_cast(status)]; + } + + inline Status toStatus (const char *status) + { + const int n = sizeof(status_str) / sizeof(status_str[0]); + for (int i = 0; i < n; ++i) + { + if (strcmp(status_str[i], status) == 0) + return (Status) i; + } + return Status::RUNNING; + } + + Status getCurrentStatus() const; + void setCurrentStatus(Status currentStatus); + + std::string getClientId() const; + void setClientId(const std::string& clientId); + + std::string getCurrentPool() const; + void setCurrentPool(const std::string& currentPool); + + std::string getCurrentAlgoName() const; + void setCurrentAlgoName(const std::string& algoName); + + std::string getCurrentPowVariantName() const; + void setCurrentPowVariantName(const std::string& powVariantName); + + std::string getCpuBrand() const; + void setCpuBrand(const std::string& cpuBrand); + + std::string getExternalIp() const; + void setExternalIp(const std::string& externalIp); + + std::string getVersion() const; + void setVersion(const std::string& version); + + std::string getLog() const; + void setLog(const std::string& version); + + bool hasHugepages() const; + void setHugepages(bool hasHugepages); + + bool isHugepagesEnabled() const; + void setHugepagesEnabled(bool hugepagesEnabled); + + int getHashFactor() const; + void setHashFactor(int hashFactor); + + bool isCpuX64() const; + void setCpuX64(bool isCpuX64); + + bool hasCpuAES() const; + void setCpuAES(bool hasCpuAES); + + double getHashrateShort() const; + void setHashrateShort(double hashrateShort); + + double getHashrateMedium() const; + void setHashrateMedium(double hashrateMedium); + + double getHashrateLong() const; + void setHashrateLong(double hashrateLong); + + void setHashrateHighest(double hashrateHighest); + double getHashrateHighest() const; + + int getCurrentThreads() const; + void setCurrentThreads(int currentThreads); + + int getCpuSockets() const; + void setCpuSockets(int cpuSockets); + + int getCpuCores() const; + void setCpuCores(int cpuCores); + + int getCpuThreads() const; + void setCpuThreads(int cpuThreads); + + int getCpuL2() const; + void setCpuL2(int cpuL2); + + int getCpuL3() const; + void setCpuL3(int cpuL3); + + uint64_t getSharesGood() const; + void setSharesGood(uint64_t sharesGood); + + uint64_t getSharesTotal() const; + void setSharesTotal(uint64_t sharesTotal); + + uint64_t getHashesTotal() const; + void setHashesTotal(uint64_t hashesTotal); + + void setAvgTime(uint32_t avgTime); + uint32_t getAvgTime() const; + + std::time_t getLastStatusUpdate() const; + + void setUptime(uint64_t uptime); + uint64_t getUptime() const; + + std::string toJsonString(); + rapidjson::Value toJson(rapidjson::MemoryPoolAllocator& allocator); + bool parseFromJson(const rapidjson::Document& document); + + +private: + const char* status_str[3] = { + "RUNNING", + "PAUSED", + "CONFIG_UPDATED" + }; + + Status m_currentStatus; + + std::string m_clientId; + std::string m_currentPool; + std::string m_currentAlgoName; + std::string m_currentPowVariantName; + std::string m_cpuBrand; + std::string m_externalIp; + std::string m_version; + std::string m_log; + + bool m_hasHugepages; + bool m_isHugepagesEnabled; + bool m_isCpuX64; + bool m_hasCpuAES; + + int m_hashFactor; + double m_hashrateShort; + double m_hashrateMedium; + double m_hashrateLong; + double m_hashrateHighest; + + int m_currentThreads; + int m_cpuSockets; + int m_cpuCores; + int m_cpuThreads; + int m_cpuL2; + int m_cpuL3; + + uint64_t m_sharesGood; + uint64_t m_sharesTotal; + uint64_t m_hashesTotal; + uint64_t m_uptime; + + uint32_t m_avgTime; + + std::time_t m_lastStatusUpdate; +}; + +#endif /* __CLIENT_STATUS_H__ */ diff --git a/src/cc/ControlCommand.cpp b/src/cc/ControlCommand.cpp new file mode 100644 index 00000000..60154236 --- /dev/null +++ b/src/cc/ControlCommand.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 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 <3rdparty/rapidjson/stringbuffer.h> +#include <3rdparty/rapidjson/prettywriter.h> + +#include "common/log/Log.h" +#include "ControlCommand.h" + +ControlCommand::ControlCommand() + : m_command(Command::START) +{ + +} + +ControlCommand::ControlCommand(Command command) + : m_command(command) +{ + +} + +bool ControlCommand::parseFromJsonString(const std::string& json) +{ + bool result = false; + + rapidjson::Document document; + if (!document.Parse(json.c_str()).HasParseError()) { + result = parseFromJson(document); + } + + return result; +} + +bool ControlCommand::parseFromJson(const rapidjson::Document& document) +{ + bool result = false; + + if (document.HasMember("control_command")) { + rapidjson::Value::ConstObject controlCommand = document["control_command"].GetObject(); + if (controlCommand.HasMember("command")) { + m_command = toCommand(controlCommand["command"].GetString()); + result = true; + } + else { + LOG_ERR("Parse Error, JSON does not contain: command"); + } + } else { + LOG_ERR("Parse Error, JSON does not contain: control_command"); + } + + return result; +} + +rapidjson::Value ControlCommand::toJson(rapidjson::MemoryPoolAllocator& allocator) +{ + rapidjson::Value controlCommand(rapidjson::kObjectType); + + controlCommand.AddMember("command", rapidjson::StringRef(toString(m_command)), allocator); + + return controlCommand; +} + +void ControlCommand::setCommand(Command command) +{ + m_command = command; +} + +ControlCommand::Command ControlCommand::getCommand() const +{ + return m_command; +} + +bool ControlCommand::isOneTimeCommand() const { + + return m_command == ControlCommand::UPDATE_CONFIG || + m_command == ControlCommand::PUBLISH_CONFIG || + m_command == ControlCommand::RESTART || + m_command == ControlCommand::SHUTDOWN; +} diff --git a/src/cc/ControlCommand.h b/src/cc/ControlCommand.h new file mode 100644 index 00000000..dbd5b391 --- /dev/null +++ b/src/cc/ControlCommand.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 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 __CONTROL_COMMAND_H__ +#define __CONTROL_COMMAND_H__ + +#include +#include "rapidjson/document.h" + +static const char* command_str[6] = { + "START", + "STOP", + "UPDATE_CONFIG", + "PUBLISH_CONFIG", + "RESTART", + "SHUTDOWN" +}; + +class ControlCommand +{ +public: + enum Command { + START, + STOP, + UPDATE_CONFIG, + PUBLISH_CONFIG, + RESTART, + SHUTDOWN + }; + +public: + ControlCommand(); + explicit ControlCommand(Command command); + + static inline const char *toString (Command command) + { + return command_str[static_cast(command)]; + } + + static inline Command toCommand (const char *command) + { + const int n = sizeof(command_str) / sizeof(command_str[0]); + for (int i = 0; i < n; ++i) + { + if (strcmp(command_str[i], command) == 0) + return (Command) i; + } + return Command::START; + } + + rapidjson::Value toJson(rapidjson::MemoryPoolAllocator& allocator); + bool parseFromJsonString(const std::string& json); + bool parseFromJson(const rapidjson::Document& document); + + Command getCommand() const; + void setCommand(Command command); + + bool isOneTimeCommand() const; + +private: + Command m_command; +}; + +#endif /* __CONTROL_COMMAND_H__ */ diff --git a/src/cc/Httpd.cpp b/src/cc/Httpd.cpp new file mode 100644 index 00000000..f222b10a --- /dev/null +++ b/src/cc/Httpd.cpp @@ -0,0 +1,296 @@ +/* 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 +#include + +#include "version.h" +#include "Service.h" +#include "Httpd.h" +#include "log/Log.h" + +Httpd::Httpd(const Options *options) + : m_options(options) + , m_daemon(nullptr) +{ +} + +bool Httpd::start() +{ + if (!m_options->ccPort()) { + return false; + } + +# ifndef XMRIG_NO_TLS + if (m_options->ccUseTls()) { + + m_keyPem = readFile(m_options->ccKeyFile()); + m_certPem = readFile(m_options->ccCertFile()); + + if (m_keyPem.empty() || m_certPem.empty()) { + LOG_ERR("HTTPS Daemon failed to start. Unable to load Key/Cert."); + return false; + } + + m_daemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_SSL, + static_cast(m_options->ccPort()), nullptr, nullptr, &Httpd::handler, + this, MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 25, + MHD_OPTION_HTTPS_MEM_KEY, m_keyPem.c_str(), + MHD_OPTION_HTTPS_MEM_CERT, m_certPem.c_str(), + MHD_OPTION_END); + } else { +# endif + m_daemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, static_cast(m_options->ccPort()), nullptr, + nullptr, &Httpd::handler, + this, MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 25, MHD_OPTION_END); +# ifndef XMRIG_NO_TLS + } +# endif + + if (!m_daemon) { + LOG_ERR("HTTP Daemon failed to start."); + return false; + } else { + LOG_INFO("%s Server started on Port: %d %s", APP_NAME, m_options->ccPort(), m_options->ccUseTls() ? "with TLS" : ""); + } + + return true; +} + +std::string Httpd::readFile(const std::string &fileName) +{ + std::stringstream data; + std::ifstream file(fileName); + if (file) { + data << file.rdbuf(); + file.close(); + } + + return data.str(); +} + +unsigned Httpd::tokenAuth(struct MHD_Connection* connection) +{ + if (!m_options->ccToken()) { + LOG_WARN("AccessToken not set. Access Granted!"); + return MHD_HTTP_OK; + } + + const char* header = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_AUTHORIZATION); + if (m_options->ccToken() && !header) { + return MHD_HTTP_UNAUTHORIZED; + } + + const size_t size = strlen(header); + if (size < 8 || strlen(m_options->ccToken()) != size - 7 || memcmp("Bearer ", header, 7) != 0) { + LOG_WARN("AccessToken wrong. Access Forbidden!"); + return MHD_HTTP_FORBIDDEN; + } + + return strncmp(m_options->ccToken(), header + 7, strlen(m_options->ccToken())) == 0 ? MHD_HTTP_OK : MHD_HTTP_FORBIDDEN; +} + +unsigned Httpd::basicAuth(struct MHD_Connection* connection, std::string& resp) +{ + unsigned result = MHD_HTTP_OK; + + if (!m_options->ccAdminUser() || !m_options->ccAdminPass()) { + resp = std::string("" + "Please configure admin user and pass to view this Page." + ""); + + LOG_WARN("Admin user/password not set. Access Forbidden!"); + result = MHD_HTTP_FORBIDDEN; + } + else { + const char* header = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_AUTHORIZATION); + if (header) { + char* user = nullptr; + char* pass = nullptr; + + user = MHD_basic_auth_get_username_password(connection, &pass); + if (user == nullptr || strcmp(user, m_options->ccAdminUser()) != 0 || + pass == nullptr || strcmp(pass, m_options->ccAdminPass()) != 0) { + + LOG_WARN("Admin user/password wrong. Access Unauthorized!"); + result = MHD_HTTP_UNAUTHORIZED; + } + + if (user) { + free(user); + } + + if (pass) { + free(pass); + } + } else { + result = MHD_HTTP_UNAUTHORIZED; + } + } + + return result; +} + +int Httpd::sendResponse(MHD_Connection* connection, unsigned status, MHD_Response* rsp, const char* contentType) +{ + if (!rsp) { + rsp = MHD_create_response_from_buffer(0, nullptr, MHD_RESPMEM_MUST_COPY); + } + + MHD_add_response_header(rsp, "Content-Type", contentType); + MHD_add_response_header(rsp, "Access-Control-Allow-Origin", "*"); + MHD_add_response_header(rsp, "Access-Control-Allow-Methods", "POST, GET, OPTIONS"); + MHD_add_response_header(rsp, "Access-Control-Allow-Headers", "Content-Type, Authorization"); + MHD_add_response_header(rsp, "WWW-Authenticate", "Basic"); + MHD_add_response_header(rsp, "WWW-Authenticate", "Bearer"); + + int ret = MHD_queue_response(connection, status, rsp); + + MHD_destroy_response(rsp); + + return ret; +} + + +int Httpd::handler(void* httpd, 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, MHD_HTTP_METHOD_OPTIONS) == 0) { + LOG_INFO("OPTIONS Requested"); + return sendResponse(connection, MHD_HTTP_OK, nullptr, CONTENT_TYPE_HTML); + } + + if (strcmp(method, MHD_HTTP_METHOD_GET) != 0 && strcmp(method, MHD_HTTP_METHOD_POST) != 0) { + LOG_ERR("HTTP_METHOD_NOT_ALLOWED"); + return sendResponse(connection, MHD_HTTP_METHOD_NOT_ALLOWED, nullptr, CONTENT_TYPE_HTML); + } + + if (strstr(url, "/client/")) { + unsigned status = static_cast(httpd)->tokenAuth(connection); + if (status != MHD_HTTP_OK) { + return sendResponse(connection, status, nullptr, CONTENT_TYPE_JSON); + } + } else { + std::string resp; + unsigned status = static_cast(httpd)->basicAuth(connection, resp); + if (status != MHD_HTTP_OK) { + MHD_Response* rsp = nullptr; + if (!resp.empty()) { + rsp = MHD_create_response_from_buffer(resp.length(), (void*)resp.c_str(), MHD_RESPMEM_MUST_COPY); + } + return sendResponse(connection, status, rsp, CONTENT_TYPE_HTML); + } + } + + if (strcmp(method, MHD_HTTP_METHOD_GET) == 0) { + return handleGET(static_cast(httpd), connection, url); + } else { + return handlePOST(static_cast(httpd), connection, url, upload_data, upload_data_size, con_cls); + } + + return MHD_NO; +} + +int Httpd::handleGET(const Httpd* httpd, struct MHD_Connection* connection, const char* urlPtr) +{ + std::string resp; + std::string url(urlPtr); + std::string clientId; + + const char* clientIdPtr = MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "clientId"); + if (clientIdPtr) + { + clientId = std::string(clientIdPtr); + } + + unsigned status = Service::handleGET(httpd->m_options, url, clientId, resp); + + MHD_Response* rsp = nullptr; + if (!resp.empty()) { + rsp = MHD_create_response_from_buffer(resp.length(), (void*) resp.c_str(), MHD_RESPMEM_MUST_COPY); + } + + char* contentType; + if (url == "/") { + contentType = const_cast(CONTENT_TYPE_HTML); + } else { + contentType = const_cast(CONTENT_TYPE_JSON); + } + + return sendResponse(connection, status, rsp, contentType); +} + +int Httpd::handlePOST(const Httpd* httpd, struct MHD_Connection* connection, const char* urlPtr, const char* upload_data, + size_t* upload_data_size, void** con_cls) +{ + auto* cc = (ConnectionContext*)* con_cls; + if (cc == nullptr) { + cc = new ConnectionContext(); + *con_cls = (void*) cc; + } else { + if (*upload_data_size != 0) { + cc->data << std::string(upload_data, *upload_data_size); + + *upload_data_size = 0; + } else { + std::string resp; + std::string url(urlPtr); + std::string clientIp; + std::string clientId; + + const MHD_ConnectionInfo *info = MHD_get_connection_info(connection, MHD_CONNECTION_INFO_CLIENT_ADDRESS); + if (info) { + char clientHost[NI_MAXHOST]; + int ec = getnameinfo(info->client_addr, sizeof(*info->client_addr), clientHost, sizeof(clientHost), + 0, 0, NI_NUMERICHOST|NI_NUMERICSERV); + + if (ec == 0) { + clientIp = std::string(clientHost); + } + } + + const char* clientIdPtr = MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "clientId"); + if (clientIdPtr) { + clientId = std::string(clientIdPtr); + } + + unsigned status = Service::handlePOST(httpd->m_options, url, clientIp, clientId, cc->data.str(), resp); + + MHD_Response* rsp = nullptr; + if (!resp.empty()) { + rsp = MHD_create_response_from_buffer(resp.length(), (void*) resp.c_str(), MHD_RESPMEM_MUST_COPY); + } + + delete cc; + *con_cls = nullptr; + + return sendResponse(connection, status, rsp, CONTENT_TYPE_JSON); + } + } + + return MHD_YES; +} diff --git a/src/cc/Httpd.h b/src/cc/Httpd.h new file mode 100644 index 00000000..8d3ee6fe --- /dev/null +++ b/src/cc/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 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 __HTTPD_H__ +#define __HTTPD_H__ + +#include +#include +#include + +#include "Options.h" + + +struct MHD_Connection; +struct MHD_Daemon; +struct MHD_Response; + +class Httpd +{ +public: + Httpd(const Options *options); + bool start(); + +private: + + typedef struct PostContext + { + std::stringstream data; + } ConnectionContext; + + static int sendResponse(MHD_Connection* connection, unsigned status, MHD_Response* rsp, const char* contentType); + + unsigned basicAuth(MHD_Connection* connection, std::string &resp); + unsigned tokenAuth(MHD_Connection* connection); + + static int handler(void* httpd, MHD_Connection* connection, const char* url, const char* method, const char* version, const char* upload_data, size_t* upload_data_size, void**con_cls); + static int handleGET(const Httpd* httpd, MHD_Connection* connection, const char* url); + static int handlePOST(const Httpd* httpd, MHD_Connection* connection, const char* url, const char* upload_data, size_t* upload_data_size, void** con_cls); + + static std::string readFile(const std::string &fileName); + + const Options* m_options; + MHD_Daemon* m_daemon; + + std::string m_keyPem; + std::string m_certPem; +}; + +#endif /* __HTTPD_H__ */ diff --git a/src/cc/Service.cpp b/src/cc/Service.cpp new file mode 100644 index 00000000..3a044768 --- /dev/null +++ b/src/cc/Service.cpp @@ -0,0 +1,342 @@ +/* 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 +#include <3rdparty/rapidjson/document.h> +#include <3rdparty/rapidjson/stringbuffer.h> +#include <3rdparty/rapidjson/writer.h> +#include <3rdparty/rapidjson/filewritestream.h> +#include <3rdparty/rapidjson/filereadstream.h> +#include <3rdparty/rapidjson/error/en.h> +#include <3rdparty/rapidjson/prettywriter.h> +#include +#include "common/log/Log.h" +#include "Service.h" + +uv_mutex_t Service::m_mutex; +std::map Service::m_clientCommand; +std::map Service::m_clientStatus; +int Service::m_currentServerTime = 0; + +bool Service::start() +{ + uv_mutex_init(&m_mutex); + + return true; +} + +void Service::release() +{ + uv_mutex_lock(&m_mutex); + + m_clientCommand.clear(); + m_clientStatus.clear(); + + uv_mutex_unlock(&m_mutex); +} + +unsigned Service::handleGET(const Options* options, const std::string& url, const std::string& clientId, std::string& resp) +{ + uv_mutex_lock(&m_mutex); + + unsigned resultCode = MHD_HTTP_NOT_FOUND; + + LOG_INFO("GET(url='%s', clientId='%s')", url.c_str(), clientId.c_str()); + + if (url == "/") { + resultCode = getAdminPage(options, resp); + } else if (url.rfind("/admin/getClientStatusList", 0) == 0) { + resultCode = getClientStatusList(resp); + } else { + if (!clientId.empty()) { + if (url.rfind("/client/getConfig", 0) == 0 || url.rfind("/admin/getClientConfig", 0) == 0) { + resultCode = getClientConfig(options, clientId, resp); + } else if (url.rfind("/admin/getClientCommand", 0) == 0) { + resultCode = getClientCommand(clientId, resp); + } + } + else { + LOG_ERR("Request does not contain clientId: %s", url.c_str()); + } + } + + uv_mutex_unlock(&m_mutex); + + return resultCode; +} + +unsigned Service::handlePOST(const Options* options, const std::string& url, const std::string& clientIp, + const std::string& clientId, const std::string& data, std::string& resp) +{ + uv_mutex_lock(&m_mutex); + + unsigned resultCode = MHD_HTTP_NOT_FOUND; + + LOG_INFO("POST(url='%s', clientIp='%s', clientId='%s', dataLen='%d')", + url.c_str(), clientIp.c_str(), clientId.c_str(), data.length()); + + if (url.rfind("/client/setClientStatus", 0) == 0) { + resultCode = setClientStatus(clientIp, clientId, data, resp); + } else if (url.rfind("/admin/setClientConfig", 0) == 0 || url.rfind("/client/setClientConfig", 0) == 0) { + resultCode = setClientConfig(options, clientId, data, resp); + } else if (url.rfind("/admin/setClientCommand", 0) == 0) { + resultCode = setClientCommand(clientId, data, resp); + } else if (url.rfind("/admin/resetClientStatusList", 0) == 0) { + resultCode = resetClientStatusList(data, resp); + } + + uv_mutex_unlock(&m_mutex); + + return resultCode; +} + +unsigned Service::getClientConfig(const Options* options, const std::string& clientId, std::string& resp) +{ + unsigned resultCode = MHD_HTTP_INTERNAL_SERVER_ERROR; + + std::string clientConfigFileName = getClientConfigFileName(options, clientId); + + std::stringstream data; + std::ifstream clientConfig(clientConfigFileName); + if (clientConfig) { + data << clientConfig.rdbuf(); + clientConfig.close(); + } else { + std::ifstream defaultConfig("default_config.json"); + if (defaultConfig) { + data << defaultConfig.rdbuf(); + defaultConfig.close(); + } + } + + if (data.tellp() > 0) { + rapidjson::Document document; + document.Parse(data.str().c_str()); + + if (!document.HasParseError()) { + rapidjson::StringBuffer buffer(0, 4096); + rapidjson::Writer writer(buffer); + writer.SetMaxDecimalPlaces(10); + document.Accept(writer); + + resp = buffer.GetString(); + + resultCode = MHD_HTTP_OK; + } else { + LOG_ERR("Not able to send client config. Client config %s is broken!", clientConfigFileName.c_str()); + } + } else{ + LOG_ERR("Not able to load a client config. Please check your configuration!"); + } + + return resultCode; +} + +unsigned Service::setClientConfig(const Options* options, const std::string &clientId, const std::string &data, std::string &resp) +{ + unsigned resultCode = MHD_HTTP_BAD_REQUEST; + + rapidjson::Document document; + if (!document.Parse(data.c_str()).HasParseError()) { + std::string clientConfigFileName = getClientConfigFileName(options, clientId); + std::ofstream clientConfigFile(clientConfigFileName); + + if (clientConfigFile){ + rapidjson::StringBuffer buffer(0, 4096); + rapidjson::PrettyWriter writer(buffer); + writer.SetMaxDecimalPlaces(10); + document.Accept(writer); + + clientConfigFile << buffer.GetString(); + clientConfigFile.close(); + + resultCode = MHD_HTTP_OK; + } else { + LOG_ERR("Not able to store client config to file %s.", clientConfigFileName.c_str()); + } + } else{ + LOG_ERR("Not able to store client config. The received client config for client %s is broken!", clientId.c_str()); + } + + return resultCode; +} + +unsigned Service::getClientStatusList(std::string& resp) +{ + rapidjson::Document document; + document.SetObject(); + + auto& allocator = document.GetAllocator(); + + rapidjson::Value clientStatusList(rapidjson::kArrayType); + for (auto& clientStatus : m_clientStatus) { + rapidjson::Value clientStatusEntry(rapidjson::kObjectType); + clientStatusEntry.AddMember("client_status", clientStatus.second.toJson(allocator), allocator); + clientStatusList.PushBack(clientStatusEntry, allocator); + } + + auto time_point = std::chrono::system_clock::now(); + m_currentServerTime = std::chrono::system_clock::to_time_t(time_point); + + document.AddMember("current_server_time", m_currentServerTime, allocator); + document.AddMember("current_version", rapidjson::StringRef(Version::string().c_str()), allocator); + document.AddMember("client_status_list", clientStatusList, allocator); + + rapidjson::StringBuffer buffer(0, 4096); + rapidjson::Writer writer(buffer); + writer.SetMaxDecimalPlaces(10); + document.Accept(writer); + + resp = buffer.GetString(); + + return MHD_HTTP_OK; +} + +unsigned Service::setClientStatus(const std::string& clientIp, const std::string& clientId, const std::string& data, std::string& resp) +{ + int resultCode = MHD_HTTP_BAD_REQUEST; + + rapidjson::Document document; + if (!document.Parse(data.c_str()).HasParseError()) { + LOG_INFO("Status from client: %s", clientId.c_str()); + + ClientStatus clientStatus; + clientStatus.parseFromJson(document); + clientStatus.setExternalIp(clientIp); + + m_clientStatus[clientId] = clientStatus; + + resultCode = getClientCommand(clientId, resp); + + if (m_clientCommand[clientId].isOneTimeCommand()) { + m_clientCommand.erase(clientId); + } + } else { + LOG_ERR("Parse Error Occured: %d", document.GetParseError()); + } + + return resultCode; +} + +unsigned Service::getClientCommand(const std::string& clientId, std::string& resp) +{ + if (m_clientCommand.find(clientId) == m_clientCommand.end()) { + m_clientCommand[clientId] = ControlCommand(); + } + + rapidjson::Document respDocument; + respDocument.SetObject(); + + auto& allocator = respDocument.GetAllocator(); + + rapidjson::Value controlCommand = m_clientCommand[clientId].toJson(allocator); + respDocument.AddMember("control_command", controlCommand, allocator); + + rapidjson::StringBuffer buffer(0, 4096); + rapidjson::Writer writer(buffer); + writer.SetMaxDecimalPlaces(10); + respDocument.Accept(writer); + + resp = buffer.GetString(); + + return MHD_HTTP_OK; +} + +unsigned Service::setClientCommand(const std::string& clientId, const std::string& data, std::string& resp) +{ + ControlCommand controlCommand; + + rapidjson::Document document; + if (!document.Parse(data.c_str()).HasParseError()) { + controlCommand.parseFromJson(document); + + m_clientCommand[clientId] = controlCommand; + + return MHD_HTTP_OK; + } else { + return MHD_HTTP_BAD_REQUEST; + } +} + +unsigned Service::resetClientStatusList(const std::string& data, std::string& resp) +{ + m_clientStatus.clear(); + + return MHD_HTTP_OK; +} + +unsigned Service::getAdminPage(const Options* options, std::string& resp) +{ + std::stringstream data; + + if (options->ccCustomDashboard() != nullptr) { + std::ifstream customDashboard(options->ccCustomDashboard()); + if (customDashboard) + { + data << customDashboard.rdbuf(); + customDashboard.close(); + resp = data.str(); + } + } + + if (resp.empty()) { + data << ""; + data << ""; + data << ""; + data << ""; + data << "XMRigCC Dashboard"; + data << ""; + data << ""; + data << "
"; + data << "

Please configure a Dashboard

"; + data << "
"; + data << ""; + data << ""; + } + + resp = data.str(); + + return MHD_HTTP_OK; +} + +std::string Service::getClientConfigFileName(const Options* options, const std::string& clientId) +{ + std::string clientConfigFileName; + + if (options->ccClientConfigFolder() != nullptr) { + clientConfigFileName += options->ccClientConfigFolder(); +# ifdef WIN32 + clientConfigFileName += '\\'; +# else + clientConfigFileName += '/'; +# endif + } + + clientConfigFileName += clientId + std::string("_config.json"); + + return clientConfigFileName; +} diff --git a/src/cc/Service.h b/src/cc/Service.h new file mode 100644 index 00000000..6f66ef6c --- /dev/null +++ b/src/cc/Service.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 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 __SERVICE_H__ +#define __SERVICE_H__ + +#define CONTENT_TYPE_HTML "text/html" +#define CONTENT_TYPE_JSON "application/json" + +#include +#include +#include +#include +#include "ClientStatus.h" +#include "ControlCommand.h" + +class Service +{ +public: + static bool start(); + static void release(); + + static unsigned handleGET(const Options* options, const std::string& url, const std::string& clientId, std::string& resp); + static unsigned handlePOST(const Options* options, const std::string& url, const std::string& clientIp, const std::string& clientId, const std::string& data, std::string& resp); + +private: + static unsigned getClientConfig(const Options* options, const std::string& clientId, std::string& resp); + static unsigned getClientCommand(const std::string& clientId, std::string& resp); + static unsigned getClientStatusList(std::string& resp); + static unsigned getAdminPage(const Options* options, std::string& resp); + + static unsigned setClientStatus(const std::string& clientIp, const std::string& clientId, const std::string& data, std::string& resp); + static unsigned setClientCommand(const std::string& clientId, const std::string& data, std::string& resp); + static unsigned setClientConfig(const Options* options, const std::string &clientId, const std::string &data, std::string &resp); + static unsigned resetClientStatusList(const std::string& data, std::string& resp); + + static std::string getClientConfigFileName(const Options *options, const std::string &clientId); + +private: + static int m_currentServerTime; + + static std::map m_clientStatus; + static std::map m_clientCommand; + + static uv_mutex_t m_mutex; + +}; + +#endif /* __SERVICE_H__ */ diff --git a/src/cc/Summary.cpp b/src/cc/Summary.cpp new file mode 100644 index 00000000..23f17ede --- /dev/null +++ b/src/cc/Summary.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 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 "common/log/Log.h" +#include "Summary.h" +#include "version.h" + +static void print_versions(xmrig::Config *config) +{ + char buf[16]; + +# 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); +# else + buf[0] = '\0'; +# endif + + Log::i()->text(config->isColors() ? "\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_commands(xmrig::Config *config) +{ + if (config->isColors()) { + Log::i()->text("\x1B[01;32m * \x1B[01;37mCOMMANDS: \x1B[01;35mq\x1B[01;37muit"); + } + else { + Log::i()->text(" * COMMANDS: 'q' Quit"); + } +} + + +void Summary::print(xmrig::Controller *controller) +{ + print_versions(controller->config()); + print_commands(controller->config()); +} diff --git a/src/cc/XMRigCC.cpp b/src/cc/XMRigCC.cpp new file mode 100644 index 00000000..356b2cf2 --- /dev/null +++ b/src/cc/XMRigCC.cpp @@ -0,0 +1,31 @@ +/* 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 + * + * + * This 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 "CCServer.h" + + +int main(int argc, char** argv) { + CCServer ccServer(argc, argv); + return ccServer.start(); +} diff --git a/src/cc/XMRigd.cpp b/src/cc/XMRigd.cpp new file mode 100644 index 00000000..d70db7c9 --- /dev/null +++ b/src/cc/XMRigd.cpp @@ -0,0 +1,79 @@ +/* XMRigd + * 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 +#include + +#ifdef WIN32 + #define WIN32_LEAN_AND_MEAN /* avoid including junk */ + #include + #include +#else + #include + #include +#endif + +#ifndef MINER_EXECUTABLE_NAME + #define MINER_EXECUTABLE_NAME xmrigMiner +#endif +#define VALUE_TO_STRING(x) #x +#define VALUE(x) VALUE_TO_STRING(x) + +int main(int argc, char **argv) { + + std::string ownPath(argv[0]); + +#if defined(_WIN32) || defined(WIN32) + int pos = ownPath.rfind('\\'); + std::string xmrigMiner( VALUE(MINER_EXECUTABLE_NAME) ".exe"); +#else + int pos = ownPath.rfind('/'); + std::string xmrigMiner( VALUE(MINER_EXECUTABLE_NAME) ); +#endif + + std::string xmrigMinerPath = ownPath.substr(0, pos+1) + xmrigMiner; + +#if defined(_WIN32) || defined(WIN32) + xmrigMinerPath = "\"" + xmrigMinerPath + "\""; +#endif + + for (int i=1; i < argc; i++){ + xmrigMinerPath += " "; + xmrigMinerPath += argv[i]; + } + + xmrigMinerPath += " --daemonized"; + + int status = 0; + + do { + status = system(xmrigMinerPath.c_str()); +#if defined(_WIN32) || defined(WIN32) + } while (status != EINVAL && status != SIGHUP && status != SIGINT); + + if (status == EINVAL) { + std::this_thread::sleep_for(std::chrono::milliseconds(3000)); + } +#else + + } while (WEXITSTATUS(status) != EINVAL && WEXITSTATUS(status) != SIGHUP && WEXITSTATUS(status) != SIGINT); +#endif +} diff --git a/src/common/Platform_unix.cpp b/src/common/Platform_unix.cpp index 97b32ee8..fc9b9f9b 100644 --- a/src/common/Platform_unix.cpp +++ b/src/common/Platform_unix.cpp @@ -33,7 +33,8 @@ #include #include #include -#include +#include +#include #include #include #include diff --git a/src/common/crypto/Algorithm.cpp b/src/common/crypto/Algorithm.cpp index 73940309..17632a84 100644 --- a/src/common/crypto/Algorithm.cpp +++ b/src/common/crypto/Algorithm.cpp @@ -28,7 +28,6 @@ #include #include - #include "common/crypto/Algorithm.h" @@ -217,3 +216,24 @@ const char *xmrig::Algorithm::name(bool shortName) const return "invalid"; } + +const char* xmrig::Algorithm::getVariantName(Variant variant) +{ + if (variant == VARIANT_AUTO) { + return "auto"; + } + + return variants[variant]; +} + +std::list xmrig::Algorithm::getSupportedPowVariants() +{ + std::list supportedPowVariants; + + for (size_t i = 0; i < ARRAY_SIZE(variants); i++) + { + supportedPowVariants.emplace_back(variants[i]); + } + + return supportedPowVariants; +} diff --git a/src/common/crypto/Algorithm.h b/src/common/crypto/Algorithm.h index bcf029d8..1e89a15d 100644 --- a/src/common/crypto/Algorithm.h +++ b/src/common/crypto/Algorithm.h @@ -27,7 +27,8 @@ #include - +#include +#include #include "common/xmrig.h" @@ -71,6 +72,10 @@ class Algorithm void parseVariant(int variant); void setAlgo(Algo algo); + static const char* getVariantName(Variant variant); + static std::list getSupportedPowVariants(); + + # ifdef XMRIG_PROXY_PROJECT void parseXmrStakAlgorithm(const char *algo); # endif diff --git a/src/common/log/SysLog.cpp b/src/common/log/SysLog.cpp index bcb96394..b3d52a86 100644 --- a/src/common/log/SysLog.cpp +++ b/src/common/log/SysLog.cpp @@ -26,9 +26,9 @@ #include "common/log/SysLog.h" +#include #include "version.h" - SysLog::SysLog() { openlog(APP_ID, LOG_PID, LOG_USER); diff --git a/src/common/net/Client.cpp b/src/common/net/Client.cpp index f4553d97..eda336d6 100644 --- a/src/common/net/Client.cpp +++ b/src/common/net/Client.cpp @@ -497,6 +497,13 @@ void Client::login() params.AddMember("algo", algo, allocator); } + rapidjson::Value supportedPowVariantsList(rapidjson::kArrayType); + for (auto& supportedPowVariant : xmrig::Algorithm::getSupportedPowVariants()) { + supportedPowVariantsList.PushBack(rapidjson::StringRef(supportedPowVariant.c_str()), allocator); + } + + params.AddMember("supported-variants", supportedPowVariantsList, allocator); + doc.AddMember("params", params, allocator); send(doc); diff --git a/src/config.json b/src/config.json index 45d4759e..53a8300d 100644 --- a/src/config.json +++ b/src/config.json @@ -14,7 +14,7 @@ "opencl-platform": 0, "pools": [ { - "url": "proxy.fee.xmrig.com:9999", + "url": "donate2.graef.in:80", "user": "YOUR_WALLET", "pass": "x", "rig-id": null, @@ -29,5 +29,11 @@ "threads": null, "user-agent": null, "syslog": false, - "watch": false + "watch": false, + "cc-client": { + "url": "localhost:3344", + "access-token": "mySecret", + "worker-id": null, + "update-interval-s": 10 + } } \ No newline at end of file diff --git a/src/net/strategies/DonateStrategy.cpp b/src/net/strategies/DonateStrategy.cpp index 468d7bbc..323f6987 100644 --- a/src/net/strategies/DonateStrategy.cpp +++ b/src/net/strategies/DonateStrategy.cpp @@ -33,10 +33,6 @@ #include "net/strategies/DonateStrategy.h" -const static char *kDonatePool1 = "miner.fee.xmrig.com"; -const static char *kDonatePool2 = "emergency.fee.xmrig.com"; - - static inline float randomf(float min, float max) { return (max - min) * ((((float) rand()) / (float) RAND_MAX)) + min; } @@ -55,21 +51,12 @@ DonateStrategy::DonateStrategy(int level, const char *user, const xmrig::Algorit xmrig::keccak(user, strlen(user), hash); Job::toHex(hash, 32, userId); - if (algorithm.algo() == xmrig::CRYPTONIGHT) { - if (algorithm.variant() == xmrig::VARIANT_MSR) { - m_pools.push_back(Pool(kDonatePool1, 7783, userId, nullptr, false, true)); - } - else { - m_pools.push_back(Pool(kDonatePool1, 6666, userId, nullptr, false, true)); - m_pools.push_back(Pool(kDonatePool1, 80, userId, nullptr, false, true)); - m_pools.push_back(Pool(kDonatePool2, 5555, "48edfHu7V9Z84YzzMa6fUueoELZ9ZRXq9VetWzYGzKt52XU5xvqgzYnDK9URnRoJMk1j8nLwEVsaSWJ4fhdUyZijBGUicoD", "emergency", false, false)); - } - } - else if (algorithm.algo() == xmrig::CRYPTONIGHT_HEAVY) { - m_pools.push_back(Pool(kDonatePool1, 8888, userId, nullptr, false, true)); - } - else { - m_pools.push_back(Pool(kDonatePool1, 5555, userId, nullptr, false, true)); + if (algorithm.algo() == xmrig::CRYPTONIGHT_HEAVY) { + m_pools.push_back(Pool("donate.graef.in", 8443,userId, nullptr, false, true)); + } else if (algorithm.algo() == xmrig::CRYPTONIGHT_LITE) { + m_pools.push_back(Pool("donate.graef.in", 1080, userId, nullptr, false, true)); + } else { + m_pools.push_back(Pool("donate2.graef.in", 80, userId, nullptr, false, true)); } for (Pool &pool : m_pools) { diff --git a/src/version.h b/src/version.h index 3b2a06c5..6a557602 100644 --- a/src/version.h +++ b/src/version.h @@ -24,18 +24,39 @@ #ifndef __VERSION_H__ #define __VERSION_H__ -#define APP_ID "xmrig" -#define APP_NAME "XMRig" -#define APP_DESC "XMRig OpenCL miner" -#define APP_VERSION "2.7.1-beta" -#define APP_DOMAIN "xmrig.com" -#define APP_SITE "www.xmrig.com" -#define APP_COPYRIGHT "Copyright (C) 2016-2018 xmrig.com" +#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-AMD" +#define APP_NAME "XMRigCC-AMD" +#define APP_DESC "XMRigCC-AMD OpenCL miner" +#define APP_COPYRIGHT "Copyright (C) 2017- BenDr0id" +#endif +#define APP_VERSION "1.6.5 (based on XMRig)" +#define APP_DOMAIN "" +#define APP_SITE "https://github.com/Bendr0id/xmrigCC-amd" #define APP_KIND "amd" -#define APP_VER_MAJOR 2 -#define APP_VER_MINOR 7 -#define APP_VER_PATCH 1 +#define APP_VER_MAJOR 1 +#define APP_VER_MINOR 6 +#define APP_VER_PATCH 5 + +#ifndef NDEBUG +#ifndef XMRIG_NO_TLS +#define BUILD_TYPE "DEBUG with TLS" +#else +#define BUILD_TYPE "DEBUG" +#endif +#else +#ifndef XMRIG_NO_TLS + #define BUILD_TYPE "RELEASE with TLS" + #else + #define BUILD_TYPE "RELEASE" + #endif +#endif #ifdef _MSC_VER # if (_MSC_VER >= 1910) @@ -51,6 +72,32 @@ # else # define MSVC_VERSION 0 # endif +#include +#else +#if defined(__FreeBSD__) || defined(__APPLE__) +#include +#else +#include +#endif #endif +class Version +{ +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_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_PATCH); + + return std::stoi(version); + } +}; + #endif /* __VERSION_H__ */ From c08ee3fa47a038275211142240a063862bd51bfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Thu, 5 Jul 2018 23:12:27 +0200 Subject: [PATCH 02/97] Integrate CC client and wip config --- CMakeLists.txt | 86 ++++---- src/App.cpp | 67 +++++- src/App.h | 11 +- src/cc/CCClient.cpp | 52 +++-- src/cc/CCServer.cpp | 200 ----------------- src/cc/CCServer.h | 65 ------ src/cc/Httpd.cpp | 296 ------------------------- src/cc/Httpd.h | 70 ------ src/cc/Service.cpp | 342 ----------------------------- src/cc/Service.h | 70 ------ src/cc/Summary.cpp | 67 ------ src/cc/XMRigCC.cpp | 31 --- src/common/config/CommonConfig.cpp | 6 + src/common/config/CommonConfig.h | 50 ++++- src/common/interfaces/IConfig.h | 2 + src/net/Network.cpp | 6 + src/xmrig.cpp | 10 +- 17 files changed, 199 insertions(+), 1232 deletions(-) delete mode 100644 src/cc/CCServer.cpp delete mode 100644 src/cc/CCServer.h delete mode 100644 src/cc/Httpd.cpp delete mode 100644 src/cc/Httpd.h delete mode 100644 src/cc/Service.cpp delete mode 100644 src/cc/Service.h delete mode 100644 src/cc/Summary.cpp delete mode 100644 src/cc/XMRigCC.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 97eb2950..4300cffc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,15 +1,15 @@ cmake_minimum_required(VERSION 2.8) -project(xmrigMiner-amd) +project(xmrigMinerAMD) option(WITH_AEON "CryptoNight-Lite support" ON) option(WITH_SUMO "CryptoNight-Heavy support" ON) option(WITH_HTTPD "HTTP REST API" ON) option(WITH_CC_CLIENT "CC Client" ON) -option(WITH_CC_SERVER "CC Server" ON) +option(WITH_TLS "TLS support" ON) option(BUILD_STATIC "Build static binary" OFF) -set(MINER_EXECUTABLE_NAME "xmrigMiner-amd" CACHE STRING "Miner executable file name") -set(DAEMON_EXECUTABLE_NAME "xmrigDaemon-amd" CACHE STRING "Daemon executable file name") +set(MINER_EXECUTABLE_NAME "xmrigMinerAMD" CACHE STRING "Miner executable file name") +set(DAEMON_EXECUTABLE_NAME "xmrigDaemonAMD" CACHE STRING "Daemon executable file name") include (CheckIncludeFile) include (cmake/cpu.cmake) @@ -99,24 +99,14 @@ set(SOURCES src/amd/OclGPU.cpp src/api/NetworkState.cpp src/App.cpp - src/common/config/CommonConfig.cpp - src/common/config/ConfigLoader.cpp - src/common/config/ConfigWatcher.cpp - src/common/Console.cpp src/common/crypto/Algorithm.cpp src/common/crypto/keccak.cpp - src/common/log/ConsoleLog.cpp - src/common/log/FileLog.cpp - src/common/log/Log.cpp src/common/net/Client.cpp src/common/net/Job.cpp src/common/net/Pool.cpp src/common/net/strategies/FailoverStrategy.cpp src/common/net/strategies/SinglePoolStrategy.cpp src/common/net/SubmitResult.cpp - src/common/Platform.cpp - src/core/Config.cpp - src/core/Controller.cpp src/net/Network.cpp src/net/strategies/DonateStrategy.cpp src/Summary.cpp @@ -128,6 +118,20 @@ set(SOURCES src/xmrig.cpp ) +set(SOURCES_COMMON + src/common/Console.cpp + src/common/config/CommonConfig.cpp + src/common/config/ConfigLoader.cpp + src/common/config/ConfigWatcher.cpp + src/common/log/ConsoleLog.cpp + src/common/log/FileLog.cpp + src/common/log/Log.cpp + src/common/Platform.cpp + src/core/Config.cpp + src/core/Controller.cpp + ) + + set(SOURCES_CRYPTO src/crypto/c_groestl.c src/crypto/c_blake256.c @@ -184,7 +188,6 @@ add_definitions(/DUNICODE) #add_definitions(/DAPP_DEBUG) add_definitions(/DXMRIG_AMD_PROJECT) add_definitions(/DXMRIG_NO_LIBCPUID) -add_definitions(/DXMRIG_NO_TLS) add_definitions(/DMINER_EXECUTABLE_NAME=${MINER_EXECUTABLE_NAME}) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake") @@ -244,22 +247,19 @@ else() add_definitions(/DXMRIG_NO_API) endif() -if (WITH_CC_SERVER) - find_package(MHD) +if (WITH_TLS) + find_package(OpenSSL) - if (MHD_FOUND) - include_directories(${MHD_INCLUDE_DIRS}) + 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 "microhttpd NOT found: use `-DWITH_CC_SERVER=OFF` to build without CC Server support") + message(FATAL_ERROR "OpenSSL NOT found: use `-DWITH_TLS=OFF` to build without TLS support") endif() - - set(SOURCES_CC_SERVER - src/cc/CCServer.cpp - src/cc/Service.cpp - src/cc/Summary.cpp - src/cc/Httpd.cpp - src/cc/XMRigCC.cpp - ) +else() + add_definitions(/DXMRIG_NO_TLS) endif() if (WITH_CC_CLIENT) @@ -267,7 +267,7 @@ if (WITH_CC_CLIENT) src/cc/CCClient.cpp) endif() -if (WITH_CC_SERVER OR WITH_CC_CLIENT) +if (WITH_CC_CLIENT) set(SOURCES_CC_COMMON src/cc/ControlCommand.cpp src/cc/ClientStatus.cpp) @@ -275,33 +275,27 @@ else() add_definitions(/DXMRIG_NO_CC) 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) - - include_directories(src) include_directories(src/3rdparty) include_directories(${UV_INCLUDE_DIR}) +if (WITH_TLS) + #add_library(xmrig_tls STATIC ${SOURCES_SSL_TLS}) +endif (WITH_TLS) + if (BUILD_STATIC) set(CMAKE_EXE_LINKER_FLAGS " -static") endif() -add_executable(${PROJECT_NAME} ${HEADERS} ${SOURCES} ${SOURCES_OS} ${SOURCES_CPUID} ${HEADERS_CRYPTO} ${SOURCES_CRYPTO} ${SOURCES_SYSLOG} ${HTTPD_SOURCES} ${SOURCES_CC_CLIENT}) +add_executable(${PROJECT_NAME} ${HEADERS} ${SOURCES_CC_COMMON} ${SOURCES} ${SOURCES_COMMON} ${SOURCES_OS} ${SOURCES_CPUID} ${HEADERS_CRYPTO} ${SOURCES_CRYPTO} ${SOURCES_SYSLOG} ${HTTPD_SOURCES} ${SOURCES_CC_CLIENT}) set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME ${MINER_EXECUTABLE_NAME}) target_link_libraries(${PROJECT_NAME} ${UV_LIBRARIES} ${MHD_LIBRARY} ${EXTRA_LIBS} ${LIBS} ${OpenCL_LIBRARY}) +if (WITH_TLS) + #target_link_libraries(${PROJECT_NAME} xmrig_tls ${OPENSSL_LIBRARIES} ${EXTRA_LIBS}) + target_link_libraries(${PROJECT_NAME} ${OPENSSL_LIBRARIES} ${EXTRA_LIBS}) +endif (WITH_TLS) -add_executable(xmrigDaemon-amd src/cc/XMRigd.cpp res/app.rc) -set_target_properties(xmrigDaemon-amd PROPERTIES OUTPUT_NAME ${DAEMON_EXECUTABLE_NAME}) - - -if (WITH_CC_SERVER AND MHD_FOUND) - add_executable(xmrigCCServer ${HEADERS} ${SOURCES} ${SOURCES_OS} ${SOURCES_CPUID} ${HEADERS_CRYPTO} ${SOURCES_CRYPTO} ${SOURCES_SYSLOG} ${HTTPD_SOURCES} ${SOURCES_CC_SERVER} res/app.rc) - target_link_libraries(xmrigCCServer - xmrig_cc_common ${UV_LIBRARIES} ${MHD_LIBRARY} ${EXTRA_LIBS} ${LIBS} ${OpenCL_LIBRARY}) - set_target_properties(xmrigCCServer PROPERTIES COMPILE_FLAGS "-DXMRIG_CC_SERVER ${SHARED_FLAGS}") -endif() \ No newline at end of file +add_executable(xmrigDaemonAMD src/cc/XMRigd.cpp res/app.rc) +set_target_properties(xmrigDaemonAMD PROPERTIES OUTPUT_NAME ${DAEMON_EXECUTABLE_NAME}) \ No newline at end of file diff --git a/src/App.cpp b/src/App.cpp index a037382a..87cf4c5a 100644 --- a/src/App.cpp +++ b/src/App.cpp @@ -24,6 +24,8 @@ #include #include +#include +#include #include "api/Api.h" @@ -51,6 +53,7 @@ App *App::m_self = nullptr; App::App(int argc, char **argv) : + m_restart(false), m_console(nullptr), m_httpd(nullptr) { @@ -81,6 +84,12 @@ App::~App() # ifndef XMRIG_NO_HTTPD delete m_httpd; # endif + +# ifndef XMRIG_NO_CC + if (m_ccclient) { + delete m_ccclient; + } +# endif } @@ -123,6 +132,20 @@ int App::exec() m_httpd->start(); # endif +# ifndef XMRIG_NO_CC + if (m_controller->config()->ccHost() && m_controller->config()->ccPort() > 0) { + uv_async_init(uv_default_loop(), &m_async, App::onCommandReceived); + + m_ccclient = new CCClient(m_controller, &m_async); + + if (!m_controller->config()->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 + m_controller->config()->oclInit(); if (!Workers::start(m_controller)) { @@ -135,7 +158,7 @@ int App::exec() const int r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); uv_loop_close(uv_default_loop()); - return r; + return m_restart ? EINTR : r; } @@ -165,7 +188,7 @@ void App::onConsoleCommand(char command) case 3: LOG_WARN("Ctrl+C received, exiting"); - close(); + shutdown(); break; default: @@ -173,15 +196,26 @@ void App::onConsoleCommand(char command) } } - -void App::close() +void App::stop(bool restart) { + m_restart = restart; + m_controller->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::onSignal(uv_signal_t *handle, int signum) { @@ -204,5 +238,28 @@ void App::onSignal(uv_signal_t *handle, int signum) } uv_signal_stop(handle); - m_self->close(); + m_self->shutdown(); +} + + +void App::onCommandReceived(uv_async_t* async) +{ + auto command = reinterpret_cast (async->data); + switch (command) { + case ControlCommand::START: + Workers::setEnabled(true); + break; + case ControlCommand::STOP: + Workers::setEnabled(false); + break; + case ControlCommand::UPDATE_CONFIG:; + case ControlCommand::RESTART: + App::restart(); + break; + case ControlCommand::SHUTDOWN: + App::shutdown(); + break; + case ControlCommand::PUBLISH_CONFIG:; + break; + } } diff --git a/src/App.h b/src/App.h index 10042baa..0b8b9578 100644 --- a/src/App.h +++ b/src/App.h @@ -27,7 +27,7 @@ #include - +#include "cc/CCClient.h" #include "common/interfaces/IConsoleListener.h" @@ -49,15 +49,20 @@ class App : public IConsoleListener ~App(); int exec(); + static void restart(); + static void shutdown(); protected: void onConsoleCommand(char command) override; private: void background(); - void close(); + void stop(bool restart); static void onSignal(uv_signal_t *handle, int signum); + static void onCommandReceived(uv_async_t* handle); + + bool m_restart; static App *m_self; @@ -67,6 +72,8 @@ class App : public IConsoleListener uv_signal_t m_sigINT; uv_signal_t m_sigTERM; xmrig::Controller *m_controller; + CCClient *m_ccclient; + uv_async_t m_async; }; diff --git a/src/cc/CCClient.cpp b/src/cc/CCClient.cpp index fe6f311b..4e7492a8 100644 --- a/src/cc/CCClient.cpp +++ b/src/cc/CCClient.cpp @@ -62,11 +62,9 @@ CCClient::CCClient(xmrig::Controller* controller, uv_async_t* async) m_self = this; - xmrig::Config* config = m_controller->config(); - std::string clientId; - if (m_options->ccWorkerId()) { - clientId = m_options->ccWorkerId(); + if (m_controller->config()->ccWorkerId()) { + clientId = m_controller->config()->ccWorkerId(); } else { char hostname[128]; memset(hostname, 0, sizeof(hostname)); @@ -77,7 +75,7 @@ CCClient::CCClient(xmrig::Controller* controller, uv_async_t* async) m_clientStatus.setClientId(clientId); if (m_controller->config() != nullptr) { - m_clientStatus.setCurrentAlgoName(config->algorithm().name())); + m_clientStatus.setCurrentAlgoName(m_controller->config()->algorithm().name()); } m_clientStatus.setHugepages(""); @@ -92,12 +90,12 @@ CCClient::CCClient(xmrig::Controller* controller, uv_async_t* async) m_clientStatus.setCpuX64(Cpu::isX64()); m_clientStatus.setCpuL2(Cpu::l2()); m_clientStatus.setCpuL3(Cpu::l3()); - m_clientStatus.setCurrentThreads(static_cast(config->threads().size())); + m_clientStatus.setCurrentThreads(static_cast(m_controller->config()->threads().size())); m_startTime = std::chrono::system_clock::now(); - if (m_options->ccToken() != nullptr) { - m_authorization = std::string("Bearer ") + m_self->m_options->ccToken(); + if (m_controller->config()->ccToken() != nullptr) { + m_authorization = std::string("Bearer ") + m_self->m_controller->config()->ccToken(); } uv_thread_create(&m_thread, CCClient::onThreadStarted, this); @@ -149,10 +147,10 @@ void CCClient::publishClientStatusReport() auto res = performRequest(requestUrl, requestBuffer, "POST"); if (!res) { LOG_ERR("[CC-Client] error: unable to performRequest POST -> http://%s:%d%s", - m_self->m_options->ccHost(), m_self->m_options->ccPort(), requestUrl.c_str()); + m_self->m_controller->config()->ccHost(), m_self->m_controller->config()->ccPort(), requestUrl.c_str()); } else if (res->status != 200) { - LOG_ERR("[CC-Client] error: \"%d\" -> http://%s:%d%s", res->status, m_self->m_options->ccHost(), - m_self->m_options->ccPort(), requestUrl.c_str()); + LOG_ERR("[CC-Client] error: \"%d\" -> http://%s:%d%s", res->status, m_controller->config()->ccHost(), + m_self->m_controller->config()->ccPort(), requestUrl.c_str()); } else { ControlCommand controlCommand; if (controlCommand.parseFromJsonString(res->body)) { @@ -192,14 +190,14 @@ void CCClient::updateConfig() auto res = performRequest(requestUrl, requestBuffer, "GET"); if (!res) { LOG_ERR("[CC-Client] error: unable to performRequest GET -> http://%s:%d%s", - m_self->m_options->ccHost(), m_self->m_options->ccPort(), requestUrl.c_str()); + m_self->m_controller->config()->ccHost(), m_self->m_controller->config()->ccPort(), requestUrl.c_str()); } else if (res->status != 200) { - LOG_ERR("[CC-Client] error: \"%d\" -> http://%s:%d%s", res->status, m_self->m_options->ccHost(), - m_self->m_options->ccPort(), requestUrl.c_str()); + LOG_ERR("[CC-Client] error: \"%d\" -> http://%s:%d%s", res->status, m_self->m_controller->config()->ccHost(), + m_self->m_controller->config()->ccPort(), requestUrl.c_str()); } else { rapidjson::Document document; if (!document.Parse(res->body.c_str()).HasParseError()) { - std::ofstream clientConfigFile(m_self->m_options->configFile()); + std::ofstream clientConfigFile(m_self->m_controller->config()->fileName()); if (clientConfigFile) { rapidjson::StringBuffer buffer(0, 65536); rapidjson::PrettyWriter writer(buffer); @@ -211,7 +209,7 @@ void CCClient::updateConfig() LOG_WARN("[CC-Client] Config updated. -> trigger restart"); } else { - LOG_ERR("[CC-Client] Not able to store client config to file %s.", m_self->m_options->configFile()); + LOG_ERR("[CC-Client] Not able to store client config to file %s.", m_self->m_controller->config()->fileName()); } } else { LOG_ERR("[CC-Client] Not able to store client config. received client config is broken!"); @@ -224,7 +222,7 @@ void CCClient::publishConfig() std::string requestUrl = "/client/setClientConfig?clientId=" + m_self->m_clientStatus.getClientId(); std::stringstream data; - std::ifstream clientConfig(m_self->m_options->configFile()); + std::ifstream clientConfig(m_self->m_controller->config()->fileName()); if (clientConfig) { data << clientConfig.rdbuf(); @@ -244,16 +242,16 @@ 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_options->ccHost(), m_self->m_options->ccPort(), requestUrl.c_str()); + m_self->m_controller->config()->ccHost(), m_self->m_controller->config()->ccPort(), requestUrl.c_str()); } else if (res->status != 200) { - LOG_ERR("[CC-Client] error: \"%d\" -> http://%s:%d%s", res->status, m_self->m_options->ccHost(), - m_self->m_options->ccPort(), requestUrl.c_str()); + LOG_ERR("[CC-Client] error: \"%d\" -> http://%s:%d%s", res->status, m_self->m_controller->config()->ccHost(), + m_self->m_controller->config()->ccPort(), requestUrl.c_str()); } } else { - LOG_ERR("Not able to send config. Client config %s is broken!", m_self->m_options->configFile()); + LOG_ERR("Not able to send config. Client config %s is broken!", m_self->m_controller->config()->fileName()); } } else { - LOG_ERR("Not able to load client config %s. Please make sure it exists!", m_self->m_options->configFile()); + LOG_ERR("Not able to load client config %s. Please make sure it exists!", m_self->m_controller->config()->fileName()); } } @@ -264,11 +262,11 @@ std::shared_ptr CCClient::performRequest(const std::string& r std::shared_ptr cli; # ifndef XMRIG_NO_TLS - if (m_self->m_options->ccUseTls()) { - cli = std::make_shared(m_self->m_options->ccHost(), m_self->m_options->ccPort(), 10); + if (m_self->m_controller->config()->ccUseTls()) { + cli = std::make_shared(m_self->m_controller->config()->ccHost(), m_self->m_controller->config()->ccPort(), 10); } else { # endif - cli = std::make_shared(m_self->m_options->ccHost(), m_self->m_options->ccPort(), 10); + cli = std::make_shared(m_self->m_controller->config()->ccHost(), m_self->m_controller->config()->ccPort(), 10); # ifndef XMRIG_NO_TLS } # endif @@ -315,8 +313,8 @@ void CCClient::onThreadStarted(void* handle) 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_options->ccUpdateInterval() * 1000), - static_cast(m_self->m_options->ccUpdateInterval() * 1000)); + static_cast(m_self->m_controller->config()->ccUpdateInterval() * 1000), + static_cast(m_self->m_controller->config()->ccUpdateInterval() * 1000)); m_self->publishConfig(); diff --git a/src/cc/CCServer.cpp b/src/cc/CCServer.cpp deleted file mode 100644 index a65b4816..00000000 --- a/src/cc/CCServer.cpp +++ /dev/null @@ -1,200 +0,0 @@ -/* 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 - * - * - * This 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 "CCServer.h" -#include "Service.h" -#include "Httpd.h" -#include "Console.h" -#include "log/ConsoleLog.h" -#include "log/FileLog.h" -#include "log/Log.h" -#include "Options.h" -#include "Summary.h" - -#if _WIN32 - #include - #include -#else -# include "unistd.h" -#endif - -#ifdef HAVE_SYSLOG_H -# include "log/SysLog.h" -#endif - -CCServer *CCServer::m_self = nullptr; - - -CCServer::CCServer(int argc, char** argv) : - m_console(nullptr), - m_httpd(nullptr), - m_options(nullptr) -{ - m_self = this; - - Log::init(); - - m_options = Options::parse(argc, argv); - if (!m_options) { - return; - } - - if (!m_options->background()) { - Log::add(new ConsoleLog(m_options->colors())); - m_console = new Console(this); - } - - if (m_options->logFile()) { - Log::add(new FileLog(m_options->logFile())); - } - -# ifdef HAVE_SYSLOG_H - if (m_options->syslog()) { - Log::add(new SysLog()); - } -# endif - - uv_signal_init(uv_default_loop(), &m_signal); -} - -CCServer::~CCServer() -{ - uv_tty_reset_mode(); - - delete m_httpd; -} - -int CCServer::start() -{ - if (!m_options) { - return EINVAL; - } - - uv_signal_start(&m_signal, CCServer::onSignal, SIGHUP); - uv_signal_start(&m_signal, CCServer::onSignal, SIGTERM); - uv_signal_start(&m_signal, CCServer::onSignal, SIGINT); - - if (m_options->background()) { - moveToBackground(); - } - - Summary::print(); - - Service::start(); - - m_httpd = new Httpd(m_options); - if (!m_httpd->start()) { - return EINVAL; - } - - const int r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - uv_loop_close(uv_default_loop()); - - Options::release(); - - return r; -} - -void CCServer::onConsoleCommand(char command) -{ - switch (command) { - case 'q': - case 'Q': - stop(); - break; - - case 3: - LOG_WARN("Ctrl+C received, exiting"); - stop(); - break; - - default: - break; - } -} - -void CCServer::stop() -{ - uv_stop(uv_default_loop()); -} - -void CCServer::onSignal(uv_signal_t* handle, int signum) -{ - switch (signum) - { - case SIGHUP: - LOG_WARN("SIGHUP received, exiting"); - break; - - case SIGTERM: - LOG_WARN("SIGTERM received, exiting"); - break; - - case SIGINT: - LOG_WARN("SIGINT received, exiting"); - break; - - default: - break; - } - - uv_signal_stop(handle); - m_self->stop(); -} - -void CCServer::moveToBackground() -{ -#ifdef WIN32 - HWND hcon = GetConsoleWindow(); - if (hcon) { - ShowWindow(hcon, SW_HIDE); - } else { - HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE); - CloseHandle(h); - FreeConsole(); - } -#else - 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); - } -#endif -} diff --git a/src/cc/CCServer.h b/src/cc/CCServer.h deleted file mode 100644 index ebbf6d4d..00000000 --- a/src/cc/CCServer.h +++ /dev/null @@ -1,65 +0,0 @@ -/* 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 - * - * - * This 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 __CC_SERVER_H__ -#define __CC_SERVER_H__ - - -#include - - -#include "common/interfaces/IConsoleListener.h" - - -class Console; -class Httpd; -class Options; - -class CCServer : public IConsoleListener -{ -public: - CCServer(int argc, char **argv); - ~CCServer(); - - int start(); - -protected: - void onConsoleCommand(char command) override; - -private: - void stop(); - void moveToBackground(); - - static void onSignal(uv_signal_t* handle, int signum); - - static CCServer* m_self; - - Console* m_console; - Httpd* m_httpd; - Options* m_options; - uv_signal_t m_signal; -}; - - -#endif /* __CC_SERVER_H__ */ diff --git a/src/cc/Httpd.cpp b/src/cc/Httpd.cpp deleted file mode 100644 index f222b10a..00000000 --- a/src/cc/Httpd.cpp +++ /dev/null @@ -1,296 +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 -#include - -#include "version.h" -#include "Service.h" -#include "Httpd.h" -#include "log/Log.h" - -Httpd::Httpd(const Options *options) - : m_options(options) - , m_daemon(nullptr) -{ -} - -bool Httpd::start() -{ - if (!m_options->ccPort()) { - return false; - } - -# ifndef XMRIG_NO_TLS - if (m_options->ccUseTls()) { - - m_keyPem = readFile(m_options->ccKeyFile()); - m_certPem = readFile(m_options->ccCertFile()); - - if (m_keyPem.empty() || m_certPem.empty()) { - LOG_ERR("HTTPS Daemon failed to start. Unable to load Key/Cert."); - return false; - } - - m_daemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_SSL, - static_cast(m_options->ccPort()), nullptr, nullptr, &Httpd::handler, - this, MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 25, - MHD_OPTION_HTTPS_MEM_KEY, m_keyPem.c_str(), - MHD_OPTION_HTTPS_MEM_CERT, m_certPem.c_str(), - MHD_OPTION_END); - } else { -# endif - m_daemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, static_cast(m_options->ccPort()), nullptr, - nullptr, &Httpd::handler, - this, MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 25, MHD_OPTION_END); -# ifndef XMRIG_NO_TLS - } -# endif - - if (!m_daemon) { - LOG_ERR("HTTP Daemon failed to start."); - return false; - } else { - LOG_INFO("%s Server started on Port: %d %s", APP_NAME, m_options->ccPort(), m_options->ccUseTls() ? "with TLS" : ""); - } - - return true; -} - -std::string Httpd::readFile(const std::string &fileName) -{ - std::stringstream data; - std::ifstream file(fileName); - if (file) { - data << file.rdbuf(); - file.close(); - } - - return data.str(); -} - -unsigned Httpd::tokenAuth(struct MHD_Connection* connection) -{ - if (!m_options->ccToken()) { - LOG_WARN("AccessToken not set. Access Granted!"); - return MHD_HTTP_OK; - } - - const char* header = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_AUTHORIZATION); - if (m_options->ccToken() && !header) { - return MHD_HTTP_UNAUTHORIZED; - } - - const size_t size = strlen(header); - if (size < 8 || strlen(m_options->ccToken()) != size - 7 || memcmp("Bearer ", header, 7) != 0) { - LOG_WARN("AccessToken wrong. Access Forbidden!"); - return MHD_HTTP_FORBIDDEN; - } - - return strncmp(m_options->ccToken(), header + 7, strlen(m_options->ccToken())) == 0 ? MHD_HTTP_OK : MHD_HTTP_FORBIDDEN; -} - -unsigned Httpd::basicAuth(struct MHD_Connection* connection, std::string& resp) -{ - unsigned result = MHD_HTTP_OK; - - if (!m_options->ccAdminUser() || !m_options->ccAdminPass()) { - resp = std::string("" - "Please configure admin user and pass to view this Page." - ""); - - LOG_WARN("Admin user/password not set. Access Forbidden!"); - result = MHD_HTTP_FORBIDDEN; - } - else { - const char* header = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_AUTHORIZATION); - if (header) { - char* user = nullptr; - char* pass = nullptr; - - user = MHD_basic_auth_get_username_password(connection, &pass); - if (user == nullptr || strcmp(user, m_options->ccAdminUser()) != 0 || - pass == nullptr || strcmp(pass, m_options->ccAdminPass()) != 0) { - - LOG_WARN("Admin user/password wrong. Access Unauthorized!"); - result = MHD_HTTP_UNAUTHORIZED; - } - - if (user) { - free(user); - } - - if (pass) { - free(pass); - } - } else { - result = MHD_HTTP_UNAUTHORIZED; - } - } - - return result; -} - -int Httpd::sendResponse(MHD_Connection* connection, unsigned status, MHD_Response* rsp, const char* contentType) -{ - if (!rsp) { - rsp = MHD_create_response_from_buffer(0, nullptr, MHD_RESPMEM_MUST_COPY); - } - - MHD_add_response_header(rsp, "Content-Type", contentType); - MHD_add_response_header(rsp, "Access-Control-Allow-Origin", "*"); - MHD_add_response_header(rsp, "Access-Control-Allow-Methods", "POST, GET, OPTIONS"); - MHD_add_response_header(rsp, "Access-Control-Allow-Headers", "Content-Type, Authorization"); - MHD_add_response_header(rsp, "WWW-Authenticate", "Basic"); - MHD_add_response_header(rsp, "WWW-Authenticate", "Bearer"); - - int ret = MHD_queue_response(connection, status, rsp); - - MHD_destroy_response(rsp); - - return ret; -} - - -int Httpd::handler(void* httpd, 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, MHD_HTTP_METHOD_OPTIONS) == 0) { - LOG_INFO("OPTIONS Requested"); - return sendResponse(connection, MHD_HTTP_OK, nullptr, CONTENT_TYPE_HTML); - } - - if (strcmp(method, MHD_HTTP_METHOD_GET) != 0 && strcmp(method, MHD_HTTP_METHOD_POST) != 0) { - LOG_ERR("HTTP_METHOD_NOT_ALLOWED"); - return sendResponse(connection, MHD_HTTP_METHOD_NOT_ALLOWED, nullptr, CONTENT_TYPE_HTML); - } - - if (strstr(url, "/client/")) { - unsigned status = static_cast(httpd)->tokenAuth(connection); - if (status != MHD_HTTP_OK) { - return sendResponse(connection, status, nullptr, CONTENT_TYPE_JSON); - } - } else { - std::string resp; - unsigned status = static_cast(httpd)->basicAuth(connection, resp); - if (status != MHD_HTTP_OK) { - MHD_Response* rsp = nullptr; - if (!resp.empty()) { - rsp = MHD_create_response_from_buffer(resp.length(), (void*)resp.c_str(), MHD_RESPMEM_MUST_COPY); - } - return sendResponse(connection, status, rsp, CONTENT_TYPE_HTML); - } - } - - if (strcmp(method, MHD_HTTP_METHOD_GET) == 0) { - return handleGET(static_cast(httpd), connection, url); - } else { - return handlePOST(static_cast(httpd), connection, url, upload_data, upload_data_size, con_cls); - } - - return MHD_NO; -} - -int Httpd::handleGET(const Httpd* httpd, struct MHD_Connection* connection, const char* urlPtr) -{ - std::string resp; - std::string url(urlPtr); - std::string clientId; - - const char* clientIdPtr = MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "clientId"); - if (clientIdPtr) - { - clientId = std::string(clientIdPtr); - } - - unsigned status = Service::handleGET(httpd->m_options, url, clientId, resp); - - MHD_Response* rsp = nullptr; - if (!resp.empty()) { - rsp = MHD_create_response_from_buffer(resp.length(), (void*) resp.c_str(), MHD_RESPMEM_MUST_COPY); - } - - char* contentType; - if (url == "/") { - contentType = const_cast(CONTENT_TYPE_HTML); - } else { - contentType = const_cast(CONTENT_TYPE_JSON); - } - - return sendResponse(connection, status, rsp, contentType); -} - -int Httpd::handlePOST(const Httpd* httpd, struct MHD_Connection* connection, const char* urlPtr, const char* upload_data, - size_t* upload_data_size, void** con_cls) -{ - auto* cc = (ConnectionContext*)* con_cls; - if (cc == nullptr) { - cc = new ConnectionContext(); - *con_cls = (void*) cc; - } else { - if (*upload_data_size != 0) { - cc->data << std::string(upload_data, *upload_data_size); - - *upload_data_size = 0; - } else { - std::string resp; - std::string url(urlPtr); - std::string clientIp; - std::string clientId; - - const MHD_ConnectionInfo *info = MHD_get_connection_info(connection, MHD_CONNECTION_INFO_CLIENT_ADDRESS); - if (info) { - char clientHost[NI_MAXHOST]; - int ec = getnameinfo(info->client_addr, sizeof(*info->client_addr), clientHost, sizeof(clientHost), - 0, 0, NI_NUMERICHOST|NI_NUMERICSERV); - - if (ec == 0) { - clientIp = std::string(clientHost); - } - } - - const char* clientIdPtr = MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "clientId"); - if (clientIdPtr) { - clientId = std::string(clientIdPtr); - } - - unsigned status = Service::handlePOST(httpd->m_options, url, clientIp, clientId, cc->data.str(), resp); - - MHD_Response* rsp = nullptr; - if (!resp.empty()) { - rsp = MHD_create_response_from_buffer(resp.length(), (void*) resp.c_str(), MHD_RESPMEM_MUST_COPY); - } - - delete cc; - *con_cls = nullptr; - - return sendResponse(connection, status, rsp, CONTENT_TYPE_JSON); - } - } - - return MHD_YES; -} diff --git a/src/cc/Httpd.h b/src/cc/Httpd.h deleted file mode 100644 index 8d3ee6fe..00000000 --- a/src/cc/Httpd.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 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 __HTTPD_H__ -#define __HTTPD_H__ - -#include -#include -#include - -#include "Options.h" - - -struct MHD_Connection; -struct MHD_Daemon; -struct MHD_Response; - -class Httpd -{ -public: - Httpd(const Options *options); - bool start(); - -private: - - typedef struct PostContext - { - std::stringstream data; - } ConnectionContext; - - static int sendResponse(MHD_Connection* connection, unsigned status, MHD_Response* rsp, const char* contentType); - - unsigned basicAuth(MHD_Connection* connection, std::string &resp); - unsigned tokenAuth(MHD_Connection* connection); - - static int handler(void* httpd, MHD_Connection* connection, const char* url, const char* method, const char* version, const char* upload_data, size_t* upload_data_size, void**con_cls); - static int handleGET(const Httpd* httpd, MHD_Connection* connection, const char* url); - static int handlePOST(const Httpd* httpd, MHD_Connection* connection, const char* url, const char* upload_data, size_t* upload_data_size, void** con_cls); - - static std::string readFile(const std::string &fileName); - - const Options* m_options; - MHD_Daemon* m_daemon; - - std::string m_keyPem; - std::string m_certPem; -}; - -#endif /* __HTTPD_H__ */ diff --git a/src/cc/Service.cpp b/src/cc/Service.cpp deleted file mode 100644 index 3a044768..00000000 --- a/src/cc/Service.cpp +++ /dev/null @@ -1,342 +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 -#include <3rdparty/rapidjson/document.h> -#include <3rdparty/rapidjson/stringbuffer.h> -#include <3rdparty/rapidjson/writer.h> -#include <3rdparty/rapidjson/filewritestream.h> -#include <3rdparty/rapidjson/filereadstream.h> -#include <3rdparty/rapidjson/error/en.h> -#include <3rdparty/rapidjson/prettywriter.h> -#include -#include "common/log/Log.h" -#include "Service.h" - -uv_mutex_t Service::m_mutex; -std::map Service::m_clientCommand; -std::map Service::m_clientStatus; -int Service::m_currentServerTime = 0; - -bool Service::start() -{ - uv_mutex_init(&m_mutex); - - return true; -} - -void Service::release() -{ - uv_mutex_lock(&m_mutex); - - m_clientCommand.clear(); - m_clientStatus.clear(); - - uv_mutex_unlock(&m_mutex); -} - -unsigned Service::handleGET(const Options* options, const std::string& url, const std::string& clientId, std::string& resp) -{ - uv_mutex_lock(&m_mutex); - - unsigned resultCode = MHD_HTTP_NOT_FOUND; - - LOG_INFO("GET(url='%s', clientId='%s')", url.c_str(), clientId.c_str()); - - if (url == "/") { - resultCode = getAdminPage(options, resp); - } else if (url.rfind("/admin/getClientStatusList", 0) == 0) { - resultCode = getClientStatusList(resp); - } else { - if (!clientId.empty()) { - if (url.rfind("/client/getConfig", 0) == 0 || url.rfind("/admin/getClientConfig", 0) == 0) { - resultCode = getClientConfig(options, clientId, resp); - } else if (url.rfind("/admin/getClientCommand", 0) == 0) { - resultCode = getClientCommand(clientId, resp); - } - } - else { - LOG_ERR("Request does not contain clientId: %s", url.c_str()); - } - } - - uv_mutex_unlock(&m_mutex); - - return resultCode; -} - -unsigned Service::handlePOST(const Options* options, const std::string& url, const std::string& clientIp, - const std::string& clientId, const std::string& data, std::string& resp) -{ - uv_mutex_lock(&m_mutex); - - unsigned resultCode = MHD_HTTP_NOT_FOUND; - - LOG_INFO("POST(url='%s', clientIp='%s', clientId='%s', dataLen='%d')", - url.c_str(), clientIp.c_str(), clientId.c_str(), data.length()); - - if (url.rfind("/client/setClientStatus", 0) == 0) { - resultCode = setClientStatus(clientIp, clientId, data, resp); - } else if (url.rfind("/admin/setClientConfig", 0) == 0 || url.rfind("/client/setClientConfig", 0) == 0) { - resultCode = setClientConfig(options, clientId, data, resp); - } else if (url.rfind("/admin/setClientCommand", 0) == 0) { - resultCode = setClientCommand(clientId, data, resp); - } else if (url.rfind("/admin/resetClientStatusList", 0) == 0) { - resultCode = resetClientStatusList(data, resp); - } - - uv_mutex_unlock(&m_mutex); - - return resultCode; -} - -unsigned Service::getClientConfig(const Options* options, const std::string& clientId, std::string& resp) -{ - unsigned resultCode = MHD_HTTP_INTERNAL_SERVER_ERROR; - - std::string clientConfigFileName = getClientConfigFileName(options, clientId); - - std::stringstream data; - std::ifstream clientConfig(clientConfigFileName); - if (clientConfig) { - data << clientConfig.rdbuf(); - clientConfig.close(); - } else { - std::ifstream defaultConfig("default_config.json"); - if (defaultConfig) { - data << defaultConfig.rdbuf(); - defaultConfig.close(); - } - } - - if (data.tellp() > 0) { - rapidjson::Document document; - document.Parse(data.str().c_str()); - - if (!document.HasParseError()) { - rapidjson::StringBuffer buffer(0, 4096); - rapidjson::Writer writer(buffer); - writer.SetMaxDecimalPlaces(10); - document.Accept(writer); - - resp = buffer.GetString(); - - resultCode = MHD_HTTP_OK; - } else { - LOG_ERR("Not able to send client config. Client config %s is broken!", clientConfigFileName.c_str()); - } - } else{ - LOG_ERR("Not able to load a client config. Please check your configuration!"); - } - - return resultCode; -} - -unsigned Service::setClientConfig(const Options* options, const std::string &clientId, const std::string &data, std::string &resp) -{ - unsigned resultCode = MHD_HTTP_BAD_REQUEST; - - rapidjson::Document document; - if (!document.Parse(data.c_str()).HasParseError()) { - std::string clientConfigFileName = getClientConfigFileName(options, clientId); - std::ofstream clientConfigFile(clientConfigFileName); - - if (clientConfigFile){ - rapidjson::StringBuffer buffer(0, 4096); - rapidjson::PrettyWriter writer(buffer); - writer.SetMaxDecimalPlaces(10); - document.Accept(writer); - - clientConfigFile << buffer.GetString(); - clientConfigFile.close(); - - resultCode = MHD_HTTP_OK; - } else { - LOG_ERR("Not able to store client config to file %s.", clientConfigFileName.c_str()); - } - } else{ - LOG_ERR("Not able to store client config. The received client config for client %s is broken!", clientId.c_str()); - } - - return resultCode; -} - -unsigned Service::getClientStatusList(std::string& resp) -{ - rapidjson::Document document; - document.SetObject(); - - auto& allocator = document.GetAllocator(); - - rapidjson::Value clientStatusList(rapidjson::kArrayType); - for (auto& clientStatus : m_clientStatus) { - rapidjson::Value clientStatusEntry(rapidjson::kObjectType); - clientStatusEntry.AddMember("client_status", clientStatus.second.toJson(allocator), allocator); - clientStatusList.PushBack(clientStatusEntry, allocator); - } - - auto time_point = std::chrono::system_clock::now(); - m_currentServerTime = std::chrono::system_clock::to_time_t(time_point); - - document.AddMember("current_server_time", m_currentServerTime, allocator); - document.AddMember("current_version", rapidjson::StringRef(Version::string().c_str()), allocator); - document.AddMember("client_status_list", clientStatusList, allocator); - - rapidjson::StringBuffer buffer(0, 4096); - rapidjson::Writer writer(buffer); - writer.SetMaxDecimalPlaces(10); - document.Accept(writer); - - resp = buffer.GetString(); - - return MHD_HTTP_OK; -} - -unsigned Service::setClientStatus(const std::string& clientIp, const std::string& clientId, const std::string& data, std::string& resp) -{ - int resultCode = MHD_HTTP_BAD_REQUEST; - - rapidjson::Document document; - if (!document.Parse(data.c_str()).HasParseError()) { - LOG_INFO("Status from client: %s", clientId.c_str()); - - ClientStatus clientStatus; - clientStatus.parseFromJson(document); - clientStatus.setExternalIp(clientIp); - - m_clientStatus[clientId] = clientStatus; - - resultCode = getClientCommand(clientId, resp); - - if (m_clientCommand[clientId].isOneTimeCommand()) { - m_clientCommand.erase(clientId); - } - } else { - LOG_ERR("Parse Error Occured: %d", document.GetParseError()); - } - - return resultCode; -} - -unsigned Service::getClientCommand(const std::string& clientId, std::string& resp) -{ - if (m_clientCommand.find(clientId) == m_clientCommand.end()) { - m_clientCommand[clientId] = ControlCommand(); - } - - rapidjson::Document respDocument; - respDocument.SetObject(); - - auto& allocator = respDocument.GetAllocator(); - - rapidjson::Value controlCommand = m_clientCommand[clientId].toJson(allocator); - respDocument.AddMember("control_command", controlCommand, allocator); - - rapidjson::StringBuffer buffer(0, 4096); - rapidjson::Writer writer(buffer); - writer.SetMaxDecimalPlaces(10); - respDocument.Accept(writer); - - resp = buffer.GetString(); - - return MHD_HTTP_OK; -} - -unsigned Service::setClientCommand(const std::string& clientId, const std::string& data, std::string& resp) -{ - ControlCommand controlCommand; - - rapidjson::Document document; - if (!document.Parse(data.c_str()).HasParseError()) { - controlCommand.parseFromJson(document); - - m_clientCommand[clientId] = controlCommand; - - return MHD_HTTP_OK; - } else { - return MHD_HTTP_BAD_REQUEST; - } -} - -unsigned Service::resetClientStatusList(const std::string& data, std::string& resp) -{ - m_clientStatus.clear(); - - return MHD_HTTP_OK; -} - -unsigned Service::getAdminPage(const Options* options, std::string& resp) -{ - std::stringstream data; - - if (options->ccCustomDashboard() != nullptr) { - std::ifstream customDashboard(options->ccCustomDashboard()); - if (customDashboard) - { - data << customDashboard.rdbuf(); - customDashboard.close(); - resp = data.str(); - } - } - - if (resp.empty()) { - data << ""; - data << ""; - data << ""; - data << ""; - data << "XMRigCC Dashboard"; - data << ""; - data << ""; - data << "
"; - data << "

Please configure a Dashboard

"; - data << "
"; - data << ""; - data << ""; - } - - resp = data.str(); - - return MHD_HTTP_OK; -} - -std::string Service::getClientConfigFileName(const Options* options, const std::string& clientId) -{ - std::string clientConfigFileName; - - if (options->ccClientConfigFolder() != nullptr) { - clientConfigFileName += options->ccClientConfigFolder(); -# ifdef WIN32 - clientConfigFileName += '\\'; -# else - clientConfigFileName += '/'; -# endif - } - - clientConfigFileName += clientId + std::string("_config.json"); - - return clientConfigFileName; -} diff --git a/src/cc/Service.h b/src/cc/Service.h deleted file mode 100644 index 6f66ef6c..00000000 --- a/src/cc/Service.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 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 __SERVICE_H__ -#define __SERVICE_H__ - -#define CONTENT_TYPE_HTML "text/html" -#define CONTENT_TYPE_JSON "application/json" - -#include -#include -#include -#include -#include "ClientStatus.h" -#include "ControlCommand.h" - -class Service -{ -public: - static bool start(); - static void release(); - - static unsigned handleGET(const Options* options, const std::string& url, const std::string& clientId, std::string& resp); - static unsigned handlePOST(const Options* options, const std::string& url, const std::string& clientIp, const std::string& clientId, const std::string& data, std::string& resp); - -private: - static unsigned getClientConfig(const Options* options, const std::string& clientId, std::string& resp); - static unsigned getClientCommand(const std::string& clientId, std::string& resp); - static unsigned getClientStatusList(std::string& resp); - static unsigned getAdminPage(const Options* options, std::string& resp); - - static unsigned setClientStatus(const std::string& clientIp, const std::string& clientId, const std::string& data, std::string& resp); - static unsigned setClientCommand(const std::string& clientId, const std::string& data, std::string& resp); - static unsigned setClientConfig(const Options* options, const std::string &clientId, const std::string &data, std::string &resp); - static unsigned resetClientStatusList(const std::string& data, std::string& resp); - - static std::string getClientConfigFileName(const Options *options, const std::string &clientId); - -private: - static int m_currentServerTime; - - static std::map m_clientStatus; - static std::map m_clientCommand; - - static uv_mutex_t m_mutex; - -}; - -#endif /* __SERVICE_H__ */ diff --git a/src/cc/Summary.cpp b/src/cc/Summary.cpp deleted file mode 100644 index 23f17ede..00000000 --- a/src/cc/Summary.cpp +++ /dev/null @@ -1,67 +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 "common/log/Log.h" -#include "Summary.h" -#include "version.h" - -static void print_versions(xmrig::Config *config) -{ - char buf[16]; - -# 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); -# else - buf[0] = '\0'; -# endif - - Log::i()->text(config->isColors() ? "\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_commands(xmrig::Config *config) -{ - if (config->isColors()) { - Log::i()->text("\x1B[01;32m * \x1B[01;37mCOMMANDS: \x1B[01;35mq\x1B[01;37muit"); - } - else { - Log::i()->text(" * COMMANDS: 'q' Quit"); - } -} - - -void Summary::print(xmrig::Controller *controller) -{ - print_versions(controller->config()); - print_commands(controller->config()); -} diff --git a/src/cc/XMRigCC.cpp b/src/cc/XMRigCC.cpp deleted file mode 100644 index 356b2cf2..00000000 --- a/src/cc/XMRigCC.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/* 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 - * - * - * This 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 "CCServer.h" - - -int main(int argc, char** argv) { - CCServer ccServer(argc, argv); - return ccServer.start(); -} diff --git a/src/common/config/CommonConfig.cpp b/src/common/config/CommonConfig.cpp index 7e43b39d..3e557315 100644 --- a/src/common/config/CommonConfig.cpp +++ b/src/common/config/CommonConfig.cpp @@ -52,12 +52,18 @@ xmrig::CommonConfig::CommonConfig() : # else m_watch(false), // TODO: enable config file watch by default when this feature propertly handled and tested. # endif + m_daemonized(false), + m_ccUseTls(false), + m_ccUseRemoteLogging(true), m_apiPort(0), m_donateLevel(kDefaultDonateLevel), m_printTime(60), m_retries(5), m_retryPause(5), + m_ccUpdateInterval(10), + m_ccPort(0), + m_ccRemoteLoggingMaxRows(25), m_state(NoneState) { m_pools.push_back(Pool()); diff --git a/src/common/config/CommonConfig.h b/src/common/config/CommonConfig.h index ffebb6b2..ab824734 100644 --- a/src/common/config/CommonConfig.h +++ b/src/common/config/CommonConfig.h @@ -43,22 +43,37 @@ class CommonConfig : public IConfig CommonConfig(); ~CommonConfig(); - inline bool isApiIPv6() const { return m_apiIPv6; } - inline bool isApiRestricted() const { return m_apiRestricted; } - inline bool isBackground() const { return m_background; } - inline bool isColors() const { return m_colors; } - inline bool isDryRun() const { return m_dryRun; } - inline bool isSyslog() const { return m_syslog; } - inline const char *apiToken() const { return m_apiToken.data(); } - inline const char *apiWorkerId() const { return m_apiWorkerId.data(); } - inline const char *logFile() const { return m_logFile.data(); } - inline const char *userAgent() const { return m_userAgent.data(); } + inline bool isApiIPv6() const { return m_apiIPv6; } + inline bool isApiRestricted() const { return m_apiRestricted; } + inline bool isBackground() const { return m_background; } + inline bool isColors() const { return m_colors; } + inline bool isDryRun() const { return m_dryRun; } + inline bool isSyslog() const { return m_syslog; } + inline bool ccUseTls() const { return m_ccUseTls; } + inline bool ccUseRemoteLogging() const { return m_ccUseRemoteLogging; } + inline const char *apiToken() const { return m_apiToken.data(); } + inline const char *apiWorkerId() const { return m_apiWorkerId.data(); } + inline const char *logFile() const { return m_logFile.data(); } + inline const char *userAgent() const { return m_userAgent.data(); } + inline const char *ccHost() const { return m_ccHost.data(); } + inline const char *ccToken() const { return m_ccToken.data(); } + inline const char *ccWorkerId() const { return m_ccWorkerId.data(); } + inline const char *ccAdminUser() const { return m_ccAdminUser.data(); } + inline const char *ccAdminPass() const { return m_ccAdminPass.data(); } + inline const char *ccClientConfigFolder() const { return m_ccClientConfigFolder.data(); } + inline const char *ccCustomDashboard() const { return m_ccCustomDashboard.isNull() ? "index.html" : m_ccCustomDashboard.data(); } + inline const char *ccKeyFile() const { return m_ccKeyFile.isNull() ? "server.key" : m_ccKeyFile.data(); } + inline const char *ccCertFile() const { return m_ccCertFile.isNull() ? "server.pem" : m_ccCertFile.data(); } + inline const std::vector &pools() const { return m_activePools; } inline int apiPort() const { return m_apiPort; } inline int donateLevel() const { return m_donateLevel; } inline int printTime() const { return m_printTime; } inline int retries() const { return m_retries; } inline int retryPause() const { return m_retryPause; } + inline int ccUpdateInterval() const { return m_ccUpdateInterval; } + inline int ccPort() const { return m_ccPort; } + inline size_t ccRemoteLoggingMaxRows() const { return m_ccRemoteLoggingMaxRows; } inline void setColors(bool colors) { m_colors = colors; } inline bool isWatch() const override { return m_watch && !m_fileName.isNull(); } @@ -89,11 +104,17 @@ class CommonConfig : public IConfig bool m_dryRun; bool m_syslog; bool m_watch; + bool m_daemonized; + bool m_ccUseTls; + bool m_ccUseRemoteLogging; int m_apiPort; int m_donateLevel; int m_printTime; int m_retries; int m_retryPause; + int m_ccUpdateInterval; + int m_ccPort; + size_t m_ccRemoteLoggingMaxRows; State m_state; std::vector m_activePools; std::vector m_pools; @@ -102,6 +123,15 @@ class CommonConfig : public IConfig xmrig::c_str m_fileName; xmrig::c_str m_logFile; xmrig::c_str m_userAgent; + xmrig::c_str m_ccHost; + xmrig::c_str m_ccToken; + xmrig::c_str m_ccWorkerId; + xmrig::c_str m_ccAdminUser; + xmrig::c_str m_ccAdminPass; + xmrig::c_str m_ccClientConfigFolder; + xmrig::c_str m_ccCustomDashboard; + xmrig::c_str m_ccKeyFile; + xmrig::c_str m_ccCertFile; private: bool parseInt(int key, int arg); diff --git a/src/common/interfaces/IConfig.h b/src/common/interfaces/IConfig.h index 5c1f5296..0caa9a46 100644 --- a/src/common/interfaces/IConfig.h +++ b/src/common/interfaces/IConfig.h @@ -96,6 +96,8 @@ class IConfig PoolCoinKey = 'C', ReuseTimeoutKey = 1106, WorkersKey = 1103, + + // xmrig-cc }; virtual ~IConfig() {} diff --git a/src/net/Network.cpp b/src/net/Network.cpp index a7479087..485be02e 100644 --- a/src/net/Network.cpp +++ b/src/net/Network.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include "api/Api.h" @@ -170,6 +171,7 @@ void Network::setJob(Client *client, const Job &job, bool donate) : "new job from %s:%d diff %d algo %s", client->host(), client->port(), job.diff(), job.algorithm().shortName()); + m_state.powVariant = job.variant(); m_state.diff = job.diff(); Workers::setJob(job, donate); } @@ -188,6 +190,10 @@ void Network::tick() # ifndef XMRIG_NO_API Api::tick(m_state); # endif + +# ifndef XMRIG_NO_CC + CCClient::updateNetworkState(m_state); +# endif } diff --git a/src/xmrig.cpp b/src/xmrig.cpp index 48362ada..b95d276d 100644 --- a/src/xmrig.cpp +++ b/src/xmrig.cpp @@ -8,7 +8,15 @@ * * * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by + * it under the terms of the GNU General Public License as published by m_controller = new xmrig::Controller(); + if (m_controller->init(argc, argv) != 0) { + return; + } + + if (!m_controller->config()->isBackground()) { + m_console = new Console(this); + } +b * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * From 7ba9f711807484fe4371b8246544946795121637 Mon Sep 17 00:00:00 2001 From: BenDr0id Date: Fri, 6 Jul 2018 18:30:23 +0200 Subject: [PATCH 03/97] WIP config file --- CMakeLists.txt | 10 +++++----- src/Summary.cpp | 6 +++--- src/common/config/CommonConfig.h | 1 + src/common/interfaces/IConfig.h | 1 + src/core/Config.cpp | 33 ++++++++++++++++++++++++++++++++ 5 files changed, 43 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4300cffc..505faea6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 2.8) -project(xmrigMinerAMD) +project(xmrigMiner-amd) option(WITH_AEON "CryptoNight-Lite support" ON) option(WITH_SUMO "CryptoNight-Heavy support" ON) @@ -8,8 +8,8 @@ option(WITH_CC_CLIENT "CC Client" ON) option(WITH_TLS "TLS support" ON) option(BUILD_STATIC "Build static binary" OFF) -set(MINER_EXECUTABLE_NAME "xmrigMinerAMD" CACHE STRING "Miner executable file name") -set(DAEMON_EXECUTABLE_NAME "xmrigDaemonAMD" CACHE STRING "Daemon executable file name") +set(MINER_EXECUTABLE_NAME "xmrigMiner-amd" CACHE STRING "Miner executable file name") +set(DAEMON_EXECUTABLE_NAME "xmrigDaemon-amd" CACHE STRING "Daemon executable file name") include (CheckIncludeFile) include (cmake/cpu.cmake) @@ -297,5 +297,5 @@ if (WITH_TLS) endif (WITH_TLS) -add_executable(xmrigDaemonAMD src/cc/XMRigd.cpp res/app.rc) -set_target_properties(xmrigDaemonAMD PROPERTIES OUTPUT_NAME ${DAEMON_EXECUTABLE_NAME}) \ No newline at end of file +add_executable(xmrigDaemon-amd src/cc/XMRigd.cpp res/app.rc) +set_target_properties(xmrigDaemon-amd PROPERTIES OUTPUT_NAME ${DAEMON_EXECUTABLE_NAME}) \ No newline at end of file diff --git a/src/Summary.cpp b/src/Summary.cpp index a37330ad..b118e44f 100644 --- a/src/Summary.cpp +++ b/src/Summary.cpp @@ -67,9 +67,9 @@ static void print_versions(xmrig::Config *config) const char *ocl = "0.0"; # endif - Log::i()->text(config->isColors() ? GREEN_BOLD(" * ") WHITE_BOLD("%-13s") CYAN_BOLD("%s/%s") WHITE_BOLD(" libuv/%s OpenCL/%s%s") - : " * %-13s%s/%s libuv/%s OpenCL/%s%s", - "VERSIONS", APP_NAME, APP_VERSION, uv_version_string(), ocl, buf); + Log::i()->text(config->isColors() ? GREEN_BOLD(" * ") WHITE_BOLD("%-13s") CYAN_BOLD("%s/%s") WHITE_BOLD(" libuv/%s OpenCL/%s%s") CYAN_BOLD(" (%s)") + : " * %-13s%s/%s libuv/%s OpenCL/%s%s (%s)", + "VERSIONS", APP_NAME, APP_VERSION, uv_version_string(), ocl, buf, BUILD_TYPE); } diff --git a/src/common/config/CommonConfig.h b/src/common/config/CommonConfig.h index ab824734..102cd0cb 100644 --- a/src/common/config/CommonConfig.h +++ b/src/common/config/CommonConfig.h @@ -93,6 +93,7 @@ class CommonConfig : public IConfig bool parseBoolean(int key, bool enable) override; bool parseString(int key, const char *arg) override; bool parseUint64(int key, uint64_t arg) override; + bool parseCCUrl(const char* url) override; void setFileName(const char *fileName) override; Algorithm m_algorithm; diff --git a/src/common/interfaces/IConfig.h b/src/common/interfaces/IConfig.h index 0caa9a46..06dab18c 100644 --- a/src/common/interfaces/IConfig.h +++ b/src/common/interfaces/IConfig.h @@ -107,6 +107,7 @@ class IConfig virtual bool parseBoolean(int key, bool enable) = 0; virtual bool parseString(int key, const char *arg) = 0; virtual bool parseUint64(int key, uint64_t arg) = 0; + virtual bool parseCCUrl(const char* url) = 0; virtual bool save() = 0; virtual const Algorithm &algorithm() const = 0; virtual const char *fileName() const = 0; diff --git a/src/core/Config.cpp b/src/core/Config.cpp index 0b03abb8..043b7ee8 100644 --- a/src/core/Config.cpp +++ b/src/core/Config.cpp @@ -118,6 +118,14 @@ void xmrig::Config::getJSON(rapidjson::Document &doc) const doc.AddMember("user-agent", userAgent() ? Value(StringRef(userAgent())).Move() : Value(kNullType).Move(), allocator); doc.AddMember("syslog", isSyslog(), allocator); doc.AddMember("watch", m_watch, allocator); + + Value cc(kObjectType); + cc.AddMember("url", ccHost(), allocator); + cc.AddMember("access-token", ccToken() ? Value(StringRef(ccToken())).Move() : Value(kNullType).Move(), allocator); + cc.AddMember("worker-id", ccWorkerId() ? Value(StringRef(ccWorkerId())).Move() : Value(kNullType).Move(), allocator); + cc.AddMember("update-interval-s", ccUpdateInterval(), allocator); + cc.AddMember("use-tls", ccUseTls(), allocator); + doc.AddMember("cc-client", cc, allocator); } @@ -215,6 +223,31 @@ bool xmrig::Config::parseUint64(int key, uint64_t arg) return true; } +bool xmrig::Config::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; +} + void xmrig::Config::parseJSON(const rapidjson::Document &doc) { From bb90777579476a6f8c48473fa600e584eb49df87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Fri, 6 Jul 2018 23:24:54 +0200 Subject: [PATCH 04/97] WIP config --- src/common/config/CommonConfig.cpp | 26 +++++++++++++++++++++++ src/common/config/CommonConfig.h | 18 +++------------- src/common/config/ConfigLoader.cpp | 7 +++++++ src/common/interfaces/IConfig.h | 8 ++++++++ src/core/Config.cpp | 31 +++------------------------- src/core/ConfigLoader_platform.h | 33 +++++++++++++++++++++++++++++- 6 files changed, 79 insertions(+), 44 deletions(-) diff --git a/src/common/config/CommonConfig.cpp b/src/common/config/CommonConfig.cpp index 3e557315..08e5ca8e 100644 --- a/src/common/config/CommonConfig.cpp +++ b/src/common/config/CommonConfig.cpp @@ -356,3 +356,29 @@ bool xmrig::CommonConfig::parseInt(int key, int arg) return true; } + + +bool xmrig::CommonConfig::parseCCUrl(const char* url) +{ + assert(url != nullptr); + + const char *base = url; + if (!strlen(base) || *base == '/') { + return false; + } + + const char *port = strchr(base, ':'); + if (!port) { + m_ccHost = base; + return true; + } + + const size_t size = port++ - base + 1; + auto *host = new char[size](); + memcpy(host, base, size - 1); + + m_ccHost = host; + m_ccPort = static_cast(strtol(port, nullptr, 10)); + + return true; +} \ No newline at end of file diff --git a/src/common/config/CommonConfig.h b/src/common/config/CommonConfig.h index 102cd0cb..540bc7a4 100644 --- a/src/common/config/CommonConfig.h +++ b/src/common/config/CommonConfig.h @@ -58,12 +58,6 @@ class CommonConfig : public IConfig inline const char *ccHost() const { return m_ccHost.data(); } inline const char *ccToken() const { return m_ccToken.data(); } inline const char *ccWorkerId() const { return m_ccWorkerId.data(); } - inline const char *ccAdminUser() const { return m_ccAdminUser.data(); } - inline const char *ccAdminPass() const { return m_ccAdminPass.data(); } - inline const char *ccClientConfigFolder() const { return m_ccClientConfigFolder.data(); } - inline const char *ccCustomDashboard() const { return m_ccCustomDashboard.isNull() ? "index.html" : m_ccCustomDashboard.data(); } - inline const char *ccKeyFile() const { return m_ccKeyFile.isNull() ? "server.key" : m_ccKeyFile.data(); } - inline const char *ccCertFile() const { return m_ccCertFile.isNull() ? "server.pem" : m_ccCertFile.data(); } inline const std::vector &pools() const { return m_activePools; } inline int apiPort() const { return m_apiPort; } @@ -71,9 +65,9 @@ class CommonConfig : public IConfig inline int printTime() const { return m_printTime; } inline int retries() const { return m_retries; } inline int retryPause() const { return m_retryPause; } - inline int ccUpdateInterval() const { return m_ccUpdateInterval; } - inline int ccPort() const { return m_ccPort; } - inline size_t ccRemoteLoggingMaxRows() const { return m_ccRemoteLoggingMaxRows; } + inline int ccUpdateInterval() const { return m_ccUpdateInterval; } + inline int ccPort() const { return m_ccPort; } + inline size_t ccRemoteLoggingMaxRows() const { return m_ccRemoteLoggingMaxRows; } inline void setColors(bool colors) { m_colors = colors; } inline bool isWatch() const override { return m_watch && !m_fileName.isNull(); } @@ -127,12 +121,6 @@ class CommonConfig : public IConfig xmrig::c_str m_ccHost; xmrig::c_str m_ccToken; xmrig::c_str m_ccWorkerId; - xmrig::c_str m_ccAdminUser; - xmrig::c_str m_ccAdminPass; - xmrig::c_str m_ccClientConfigFolder; - xmrig::c_str m_ccCustomDashboard; - xmrig::c_str m_ccKeyFile; - xmrig::c_str m_ccCertFile; private: bool parseInt(int key, int arg); diff --git a/src/common/config/ConfigLoader.cpp b/src/common/config/ConfigLoader.cpp index cc5d9a49..738aea06 100644 --- a/src/common/config/ConfigLoader.cpp +++ b/src/common/config/ConfigLoader.cpp @@ -107,6 +107,13 @@ bool xmrig::ConfigLoader::loadFromJSON(xmrig::IConfig *config, const rapidjson:: } } + const rapidjson::Value &cc = doc["cc-client"]; + if (api.IsObject()) { + for (size_t i = 0; i < ARRAY_SIZE(cc_client_options); i++) { + parseJSON(config, &cc_client_options[i], cc); + } + } + config->parseJSON(doc); return config->finalize(); diff --git a/src/common/interfaces/IConfig.h b/src/common/interfaces/IConfig.h index 06dab18c..6c023011 100644 --- a/src/common/interfaces/IConfig.h +++ b/src/common/interfaces/IConfig.h @@ -98,6 +98,14 @@ class IConfig WorkersKey = 1103, // xmrig-cc + CCUrlKey = 6000, + CCAccessTokenKey = 6001, + CCWorkerIdKey = 6002, + CCUpdateIntervalKey = 6003, + CCUseTlsKey = 6004, + CCUseRemoteLoggingKey = 6005, + CCUseRemoteMaxRowKey = 6006, + DaemonizedKey = 6007 }; virtual ~IConfig() {} diff --git a/src/core/Config.cpp b/src/core/Config.cpp index 043b7ee8..3fc8f030 100644 --- a/src/core/Config.cpp +++ b/src/core/Config.cpp @@ -120,7 +120,9 @@ void xmrig::Config::getJSON(rapidjson::Document &doc) const doc.AddMember("watch", m_watch, allocator); Value cc(kObjectType); - cc.AddMember("url", ccHost(), allocator); + + std::string url = std::string(ccHost()) + ":" + std::to_string(ccPort()); + cc.AddMember("url", StringRef(url.c_str()), allocator); cc.AddMember("access-token", ccToken() ? Value(StringRef(ccToken())).Move() : Value(kNullType).Move(), allocator); cc.AddMember("worker-id", ccWorkerId() ? Value(StringRef(ccWorkerId())).Move() : Value(kNullType).Move(), allocator); cc.AddMember("update-interval-s", ccUpdateInterval(), allocator); @@ -223,32 +225,6 @@ bool xmrig::Config::parseUint64(int key, uint64_t arg) return true; } -bool xmrig::Config::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; -} - - void xmrig::Config::parseJSON(const rapidjson::Document &doc) { const rapidjson::Value &threads = doc["threads"]; @@ -266,7 +242,6 @@ void xmrig::Config::parseJSON(const rapidjson::Document &doc) } } - void xmrig::Config::parseThread(const rapidjson::Value &object) { m_threads.push_back(new OclThread(object)); diff --git a/src/core/ConfigLoader_platform.h b/src/core/ConfigLoader_platform.h index 4b52b734..551937c5 100644 --- a/src/core/ConfigLoader_platform.h +++ b/src/core/ConfigLoader_platform.h @@ -85,7 +85,18 @@ Options:\n\ --api-access-token=T access token for API\n\ --api-worker-id=ID custom worker-id for API\n\ --api-ipv6 enable IPv6 support for API\n\ - --api-no-restricted enable full remote access (only if API token set)\n\ + --api-no-restricted enable full remote access (only if API token set)\n" +# ifndef XMRIG_NO_CC +"\ + --cc-url=URL url (host:port) 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-remote-logging-max-rows=N maximum last n-log rows to send CC Server\n" +# endif +"\ -h, --help display this help and exit\n\ -V, --version output version information and exit\n\ "; @@ -128,6 +139,14 @@ static struct option const options[] = { { "opencl-platform", 1, nullptr, xmrig::IConfig::OclPlatform }, { "no-cache", 0, nullptr, xmrig::IConfig::OclCache }, { "print-platforms", 0, nullptr, xmrig::IConfig::OclPrint }, + { "cc-url", 1, nullptr, xmrig::IConfig::CCUrlKey }, + { "cc-access-token", 1, nullptr, xmrig::IConfig::CCAccessTokenKey }, + { "cc-worker-id", 1, nullptr, xmrig::IConfig::CCWorkerIdKey }, + { "cc-update-interval-s", 1, nullptr, xmrig::IConfig::CCUpdateIntervalKey }, + { "cc-use-tls", 0, nullptr, xmrig::IConfig::CCUseTlsKey }, + { "cc-use-remote-logging", 0, nullptr, xmrig::IConfig::CCUseRemoteLoggingKey }, + { "cc-remote-logging-max-rows", 1, nullptr, xmrig::IConfig::CCUseRemoteMaxRowKey }, + { "daemonized", 0, nullptr, xmrig::IConfig::DaemonizedKey }, { 0, 0, 0, 0 } }; @@ -173,6 +192,18 @@ static struct option const api_options[] = { }; +static struct option const cc_client_options[] = { + { "url", 1, nullptr, xmrig::IConfig::CCUrlKey }, + { "access-token", 1, nullptr, xmrig::IConfig::CCAccessTokenKey }, + { "worker-id", 1, nullptr, xmrig::IConfig::CCWorkerIdKey }, + { "update-interval-s", 1, nullptr, xmrig::IConfig::CCUpdateIntervalKey }, + { "use-tls", 0, nullptr, xmrig::IConfig::CCUseTlsKey }, + { "use-remote-logging", 0, nullptr, xmrig::IConfig::CCUseRemoteLoggingKey }, + { "remote-logging-max-rows", 1, nullptr, xmrig::IConfig::CCUseRemoteMaxRowKey }, + { 0, 0, 0, 0 } +}; + + } /* namespace xmrig */ #endif /* __CONFIGLOADER_PLATFORM_H__ */ From 2ff1b778183112433f561b2d4e092a06eaa24955 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Sat, 7 Jul 2018 21:19:15 +0200 Subject: [PATCH 05/97] Fix config file parsing --- src/App.cpp | 3 +- src/Summary.cpp | 20 ++++++++++++ src/common/config/CommonConfig.cpp | 50 ++++++++++++++++++++++++++++-- src/common/config/CommonConfig.h | 1 + src/common/config/ConfigLoader.cpp | 10 +++++- src/common/interfaces/IConfig.h | 17 +++++----- src/config.json | 1 + src/core/ConfigLoader_platform.h | 5 +-- src/core/Controller.cpp | 5 ++- 9 files changed, 96 insertions(+), 16 deletions(-) diff --git a/src/App.cpp b/src/App.cpp index 87cf4c5a..a4c7f7aa 100644 --- a/src/App.cpp +++ b/src/App.cpp @@ -55,7 +55,8 @@ App *App::m_self = nullptr; App::App(int argc, char **argv) : m_restart(false), m_console(nullptr), - m_httpd(nullptr) + m_httpd(nullptr), + m_ccclient(nullptr) { m_self = this; diff --git a/src/Summary.cpp b/src/Summary.cpp index b118e44f..4516fa5d 100644 --- a/src/Summary.cpp +++ b/src/Summary.cpp @@ -136,6 +136,21 @@ static void print_api(xmrig::Config *config) #endif +#ifndef XMRIG_NO_CC +static void print_cc(xmrig::Config *config) +{ + if (config->ccHost() == nullptr) { + return; + } + + Log::i()->text(config->isColors() ? "\x1B[01;32m * \x1B[01;37mCC Server: \x1B[01;36m%s:%d %s" : " * CC Server: %s:%d %s", + config->ccHost(), + config->ccPort(), + config->ccUseTls() ? "(TLS)" : ""); +} +#endif + + static void print_commands(xmrig::Config *config) { if (config->isColors()) { @@ -160,6 +175,11 @@ void Summary::print(xmrig::Controller *controller) print_api(controller->config()); # endif +# ifndef XMRIG_NO_CC + print_cc(controller->config()); +# endif + + print_commands(controller->config()); } diff --git a/src/common/config/CommonConfig.cpp b/src/common/config/CommonConfig.cpp index 08e5ca8e..4a6ff01a 100644 --- a/src/common/config/CommonConfig.cpp +++ b/src/common/config/CommonConfig.cpp @@ -22,11 +22,12 @@ */ +#include #include #include #include -#include #include +#include #include "common/config/CommonConfig.h" @@ -54,7 +55,7 @@ xmrig::CommonConfig::CommonConfig() : # endif m_daemonized(false), m_ccUseTls(false), - m_ccUseRemoteLogging(true), + m_ccUseRemoteLogging(false), m_apiPort(0), m_donateLevel(kDefaultDonateLevel), @@ -138,7 +139,7 @@ bool xmrig::CommonConfig::finalize() m_pools.clear(); - if (m_activePools.empty()) { + if (m_activePools.empty() && m_ccHost.isNull() && m_ccPort == 0) { m_state = ErrorState; return false; } @@ -189,6 +190,18 @@ bool xmrig::CommonConfig::parseBoolean(int key, bool enable) m_dryRun = enable; break; + case DaemonizedKey: /* --daemonized */ + m_daemonized = enable; + break; + + case CCUseTlsKey: /* --cc-use-tls */ + m_ccUseTls = enable; + break; + + case CCUseRemoteLoggingKey: /* --cc-use-remote-logging */ + m_ccUseRemoteLogging = enable; + break; + default: break; } @@ -261,10 +274,23 @@ bool xmrig::CommonConfig::parseString(int key, const char *arg) m_userAgent = arg; break; + case CCUrlKey: + parseCCUrl(arg); + break; + + case CCAccessTokenKey: + m_ccToken = arg; + break; + + case CCWorkerIdKey: + m_ccWorkerId = arg; + break; + case RetriesKey: /* --retries */ case RetryPauseKey: /* --retry-pause */ case ApiPort: /* --api-port */ case PrintTimeKey: /* --cpu-priority */ + case CCRemoteLoggingMaxRowKey: /* --cc-remote-logging-max-rows */ return parseUint64(key, strtol(arg, nullptr, 10)); case BackgroundKey: /* --background */ @@ -273,6 +299,9 @@ bool xmrig::CommonConfig::parseString(int key, const char *arg) case NicehashKey: /* --nicehash */ case ApiIPv6Key: /* --api-ipv6 */ case DryRunKey: /* --dry-run */ + case CCUseTlsKey: /* --cc-use-tls-run */ + case CCUseRemoteLoggingKey: /* --cc-use-remote-logging */ + case DaemonizedKey: /* --daemonized */ return parseBoolean(key, true); case ColorKey: /* --no-color */ @@ -350,6 +379,21 @@ bool xmrig::CommonConfig::parseInt(int key, int arg) } break; + case CCRemoteLoggingMaxRowKey: /* --cc-remote-logging-max-rows */ + if (arg < 1) { + m_ccUseRemoteLogging = false; + } + else { + m_ccRemoteLoggingMaxRows = static_cast(arg); + } + break; + + case CCUpdateIntervalKey: /* --cc-update-interval-s */ + if (arg > 0) { + m_ccUpdateInterval = arg; + } + break; + default: break; } diff --git a/src/common/config/CommonConfig.h b/src/common/config/CommonConfig.h index 540bc7a4..30406dbd 100644 --- a/src/common/config/CommonConfig.h +++ b/src/common/config/CommonConfig.h @@ -71,6 +71,7 @@ class CommonConfig : public IConfig inline void setColors(bool colors) { m_colors = colors; } inline bool isWatch() const override { return m_watch && !m_fileName.isNull(); } + inline bool isDaemonized() const override { return m_daemonized; } inline const Algorithm &algorithm() const override { return m_algorithm; } inline const char *fileName() const override { return m_fileName.data(); } diff --git a/src/common/config/ConfigLoader.cpp b/src/common/config/ConfigLoader.cpp index 738aea06..af0f4fce 100644 --- a/src/common/config/ConfigLoader.cpp +++ b/src/common/config/ConfigLoader.cpp @@ -163,6 +163,14 @@ xmrig::IConfig *xmrig::ConfigLoader::load(int argc, char **argv, IConfigCreator } } +#ifndef XMRIG_NO_CC + if (!config->isDaemonized()) { + fprintf(stderr, "\"" APP_ID "\" is compiled with CC support, please start the daemon instead.\n"); + delete config; + return nullptr; + } +#endif + if (optind < argc) { fprintf(stderr, "%s: unsupported non-option argument '%s'\n", argv[0], argv[optind]); delete config; @@ -251,7 +259,7 @@ bool xmrig::ConfigLoader::parseArg(xmrig::IConfig *config, int key, const char * break; default: - return config->parseString(key, arg);; + return config->parseString(key, arg); } return true; diff --git a/src/common/interfaces/IConfig.h b/src/common/interfaces/IConfig.h index 6c023011..f9f732d8 100644 --- a/src/common/interfaces/IConfig.h +++ b/src/common/interfaces/IConfig.h @@ -98,20 +98,21 @@ class IConfig WorkersKey = 1103, // xmrig-cc - CCUrlKey = 6000, - CCAccessTokenKey = 6001, - CCWorkerIdKey = 6002, - CCUpdateIntervalKey = 6003, - CCUseTlsKey = 6004, - CCUseRemoteLoggingKey = 6005, - CCUseRemoteMaxRowKey = 6006, - DaemonizedKey = 6007 + CCUrlKey = 6000, + CCAccessTokenKey = 6001, + CCWorkerIdKey = 6002, + CCUpdateIntervalKey = 6003, + CCUseTlsKey = 6004, + CCUseRemoteLoggingKey = 6005, + CCRemoteLoggingMaxRowKey = 6006, + DaemonizedKey = 6007 }; virtual ~IConfig() {} virtual bool finalize() = 0; virtual bool isWatch() const = 0; + virtual bool isDaemonized() const = 0; virtual bool parseBoolean(int key, bool enable) = 0; virtual bool parseString(int key, const char *arg) = 0; virtual bool parseUint64(int key, uint64_t arg) = 0; diff --git a/src/config.json b/src/config.json index 53a8300d..53d506fc 100644 --- a/src/config.json +++ b/src/config.json @@ -32,6 +32,7 @@ "watch": false, "cc-client": { "url": "localhost:3344", + "use-tls" : false, "access-token": "mySecret", "worker-id": null, "update-interval-s": 10 diff --git a/src/core/ConfigLoader_platform.h b/src/core/ConfigLoader_platform.h index 551937c5..570fa18a 100644 --- a/src/core/ConfigLoader_platform.h +++ b/src/core/ConfigLoader_platform.h @@ -145,7 +145,7 @@ static struct option const options[] = { { "cc-update-interval-s", 1, nullptr, xmrig::IConfig::CCUpdateIntervalKey }, { "cc-use-tls", 0, nullptr, xmrig::IConfig::CCUseTlsKey }, { "cc-use-remote-logging", 0, nullptr, xmrig::IConfig::CCUseRemoteLoggingKey }, - { "cc-remote-logging-max-rows", 1, nullptr, xmrig::IConfig::CCUseRemoteMaxRowKey }, + { "cc-remote-logging-max-rows", 1, nullptr, xmrig::IConfig::CCRemoteLoggingMaxRowKey }, { "daemonized", 0, nullptr, xmrig::IConfig::DaemonizedKey }, { 0, 0, 0, 0 } }; @@ -165,6 +165,7 @@ static struct option const config_options[] = { { "user-agent", 1, nullptr, xmrig::IConfig::UserAgentKey }, { "opencl-platform", 1, nullptr, xmrig::IConfig::OclPlatform }, { "cache", 0, nullptr, xmrig::IConfig::OclCache }, + { "daemonized", 0, nullptr, xmrig::IConfig::DaemonizedKey }, { 0, 0, 0, 0 } }; @@ -199,7 +200,7 @@ static struct option const cc_client_options[] = { { "update-interval-s", 1, nullptr, xmrig::IConfig::CCUpdateIntervalKey }, { "use-tls", 0, nullptr, xmrig::IConfig::CCUseTlsKey }, { "use-remote-logging", 0, nullptr, xmrig::IConfig::CCUseRemoteLoggingKey }, - { "remote-logging-max-rows", 1, nullptr, xmrig::IConfig::CCUseRemoteMaxRowKey }, + { "remote-logging-max-rows", 1, nullptr, xmrig::IConfig::CCRemoteLoggingMaxRowKey }, { 0, 0, 0, 0 } }; diff --git a/src/core/Controller.cpp b/src/core/Controller.cpp index 0539e513..57afabd7 100644 --- a/src/core/Controller.cpp +++ b/src/core/Controller.cpp @@ -118,7 +118,10 @@ int xmrig::Controller::init(int argc, char **argv) } # endif - d_ptr->network = new Network(this); + if (!d_ptr->config->pools().empty()) { + d_ptr->network = new Network(this); + } + return 0; } From 79d8a764a824837bbf1f444cf70e77a06e82ab6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Sat, 7 Jul 2018 22:10:36 +0200 Subject: [PATCH 06/97] Fix publish of stats --- src/workers/Workers.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/workers/Workers.cpp b/src/workers/Workers.cpp index 4ec4a83d..f2b09e32 100644 --- a/src/workers/Workers.cpp +++ b/src/workers/Workers.cpp @@ -23,6 +23,7 @@ #include #include +#include #include "amd/OclGPU.h" @@ -335,6 +336,10 @@ void Workers::onTick(uv_timer_t *handle) if ((m_ticks++ & 0xF) == 0) { m_hashrate->updateHighest(); } + +# ifndef XMRIG_NO_CC + CCClient::updateHashrate(m_hashrate); +# endif } From 72d7beccab4e0b75dcf9fddd5788351bf6b80b5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Sat, 14 Jul 2018 19:59:18 +0200 Subject: [PATCH 07/97] Fixed windows build --- CMakeLists.txt | 2 +- res/app.ico | Bin 21497 -> 128150 bytes res/app.rc | 73 ++++++++++++++++++++++++------------------------- 3 files changed, 37 insertions(+), 38 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 505faea6..e15bca71 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -153,7 +153,7 @@ if (WIN32) endif() add_definitions(/DWIN32) - set(EXTRA_LIBS ws2_32 psapi iphlpapi userenv) + set(EXTRA_LIBS ws2_32 psapi iphlpapi userenv crypt32) elseif (APPLE) set(SOURCES_OS src/amd/OclCache_unix.cpp diff --git a/res/app.ico b/res/app.ico index 8c3d628fdfc1a97e45224d9722a44a50ab75f9bf..e9266d97e22a61066463ddee040f58d1c565afca 100644 GIT binary patch literal 128150 zcmV*sKtsO(00967000000096X04Q$&0A>IH0Dyo10096X04N9n0FG|}06;(h0096X z04PEL0Je7l05C8B0096X0H`GZ0N%L)03aX$0096X0H_cE0EXWI01yxW0096X0B8gN z050$W0EtjeM-2)Z3IG5A4M|8uQUCw}0000100;&E003NasAd2FZtqD%K~#9!?VWX; zBjXx5rGrGc#js2OLggr@^MluMlhsY101MUy~*cr%4)Uzz{pJV~i=b z&v3>=AJg96GJ_TbGrvD(Mw*dETCGM}?cV8guelu!Jo5~E`TKkyK_2rF5>N;f0Y0D+ zXwv^S0M$UbzU~F;^z$n9^O9i*7z08;FW?9IfgYew|9c8((|?ca$4mfft{+SuGlR%= z2lAK}g=oaR5Ll@%OZ08^Kn>u^QQs!fm_!%Q0UQJl0|)fq?Z8wHpIsi)AkP5i9u@*; z0b790XarxOFH){Mmm-2_nnWM47kC}mssHWEb?5SkBF_MF2LY4-3xG?23xJEzB)g0G zL+q(SL1PSufR}*hfES3G8F}OYc?K}Ma04yCR^U3|Twoa>=elD#!W8f(8bf#z*n#F- zP;%X|GX;4DkbSt&RQaocn}8K)&ba)uw?akpCF}+s2L6cVPslqKvxxjhm>u|lRlwWz zWigsJK9AI(qD2*U1HT0xK+_BYx$fBMhCBnv9NcI`e=Besu$I%$j?WbWz{|irz#~8_ z5YBa{^2h_Uc>5i|6TmQ<^vW;UTYAtK!L4YTRvvlGI%Hry@Ly=L?l8G0j5BQsqWK8^ z5oji_ZRIfwC`51ZFzytg_T3TwZKh{}UyhSByNlk-VQq!QiJOw%L z&nzurU>k5f`KSSqPXuxUmjeF*Y-ApN6ha7G1wPzfAD+TuDjFA3TGvELbv?z^breXB%Vv-3)h~ zVz{fF;N&==sY!x?Nl?|9bU)V!1CIk=1om(Sk3Be38Gt~`)cbohk8!TX)29SUmMN^L zp{#xZRSTCv%nSvkqG4KtvRO2%aXCebA0sad3G+O1HIR{CWscuWzGuP|M znZy8O;7Z`@z`D6~@9qL0C3Q_K+;l!m&byKY>(9YgUWx4XSXK3T2vt=G1*RD5?c(H) z7diIwpE$K^JO05w!htzeEGP{82`&1w3z+ljOC~VCzpm8L1pE{5K{SV7&QV-lNA2?U zEWO}r7HzqRszu9@HX@{X<9Eyolu(f2j+2~t^;wQ=dyG@N zw-cNg&vmy{v?3*ci#7`JwABVqX9iFXd=2;jIg9-Y+ywzt;=O;1M#Afbi!zE6&%zU*>S*|LI_ zSHF#wSKLBr{eoOSllcc#rSJFw_W$u-jy(4$!yPAb=|p@Q_!O`&*N-LBINcaP0q{9A z)os>OKi$3}mRxuZtFOI{#trA-E|{(ae;%n|Vq}n$uRhEE$9_xO8_%PJg1P8M2k<4d zNWp1i5}Zy9pc(i9Imq(zl~uClruVYy+S@2^TAWw$vjim=pzr8D_Wt2l9De!%f|IlE zD+mBTK}#1O&Gqxh1WpGAAb@Mph`(jlE-FcsFId9r8{Wg(o8E`pmmhsPoACE{vHN#F zvnaD6Po# z{pVrASWi0#9=VHs5B`#=(V^M8tRF4J{2$EI48r-!0G0y(i#D51*5RwDX6@VloK-iz z7f)evuJ1jMBp4s)W%oTlWfw}+ow81-{1-?ucoqaB^ zk42Z>#KsT&)tq(ksjAA<$N&?AeGIoAW~lWrBkjlW_jWMW)ke@i3!!NtC@8C>sHTzP z`esTOETMAAYKj}0@m1F1E-aa|Okt#>l{fz9-|5`(JZdM z0R9>HYqD6~wX}IDn{NLM^_wo7TS+{s%0z!RgU1graA-Gu2X`{oeTvC}KElDdm74A< zD8yG)OG(2bs#k5IYWW5#mafKKT$byuSrDF@qN2c{T2x{t1%FVM65WkyaNBQ!aI zs?4vc@FbTDS3waKOV?1h@qFqxT|jYTi&oGi*BXMVGS=0`j-UJ+-8)}IRkKE)lL+w{ zNs_*=gu`=E6PTL}KnNf3P2gidRK3r!gP)=C?8|cdL8>Z~gT3@0+C}GU&(gDN zI}?Mwx$93BQQWYQx=k0*c+M4+x2(!J(^bPEjy`r52k!d`Q`x9TpnX?i;BSQx-&YkS zCxaN0b}`o-i0_f`0sjen6m2|z6d7U!^;<7x;~k%)uqJn*z2Nv5y?b7!(8C#8&e66jncF0MNT~aTLusBo)6V> za{|fjp?1Uhv|MwWrWMTcaB5XiXnp$k9JuRy1pPCp29^rL(#-)O#Q%{b=?CFpa859R zxxoM=$pd^9_zbZQzYH*f%2k_L|KY!*xM9((-qu8aH|@_qLi_WNGS=BT-)iUQ3__A9 zU$T}3m)u0d`B&jCESc5o4;~1=?KhD%(RzppvH-w<5b^*;- z+{%K>Z^1o_<1og1I@t5$e_>$1O;TTagm2z9EQI)o+wHzLFfox+CXj0eAduuwXsW+z zP4Y=Z@F_}B$>nD0Egz!g#`n)!D(^&JC#{d&P5ZWo2+w}8iqjh2@@f`c`!1R;z7cmx z`K(+sJUPz(UwxC#7arC80?rBFykSU|<#&VwQ%}vx%_Qn-E-#KO-wNWFSkilA2s{l& z;3_O(<-7lq#*1$NMAlQWriS`x|I_`PdiwVSW^X9!83|u)6N|2UH}&UViz~}TSOlOd zVGiB(T~0oE@ASeaCBipa288I8W%>H>)a0&Nd3cF?k^{Kh=L5gP7}#TZxHB*TVzzrq zD_HZPFHp7ayhuu_nz&U*P(lGZpMR9rM}Ey@U*~K;!7~>^P}01N#kYQls*M*QL}oQS z5%V`RInM6y{XGNwUQ5`Yl(w14O+=W$ZnxWWQ*eANCsUKGue+ z-oyn>Y*&~blFe(e?2n?bVG-;8@?R-xUOMd@(GX<-Go4?0qOXhN_x*^Yzx_Ueyn`>d zP*o#ZysD|E`w^4EHCN`0;i=CUqa{_b!?DwJ56E zSXNo}@RWaSR>Om{J1ed%yOHH@fY5^Rsv3PLs{UY5W45c&2W5U;riWcvzmSz5{(HRj z3kj=snnZG>Vj`%DLf`f$XnXj7ne3gP>b?*HUui|cDab;B0Fz_Gx$fqS5uO~U^}%1z zzw1R7z5Qd9t=yEkze7o(cs=jZ0V&Y9zRsUl)M^nz; zmE0bIB>go)`hS4S^3-kDRT3U!BGsbiWvu!5KjW#WNlw72^ijV??3IE;{hYY#+YIjB zJ|_p|NG=z$%R@m)8P$uHQ@L;{<&6s|t81dDvWDWSI*Q7xkUd^xmy1NNT~$?-aEO3^ zl=0y~{DZxWbhk6y-OfP?^#_-Yrh{6k-$uyJvmEoU5W zznzr$KzMST_6L7X?+cHj8Z*)4$l2%ec&S;shJ|NcNb{!isb0L2@`h$@Xzd(8aB_m7 zjuUhp+QZ2=UgXrS?ew=E$(^>O6OQaFrt$jsPYJ^HU_;w# zGJ02Sj#ruNK1JJ|-(u|8G*iK4iy;Vs%j2ba^98K9^hR3FzLessI^4ebG1b`A*a*X& zCpr4kQyhBcA-WIl%D%afKw1h(rhMZCG`;;}xJxVJ>DKgr+_|P*YBe)O{s&N_!CFG3?e4h4ue?Vv^gF#~ZoF=@*rPQt5!0Kz?!O{z^p|GObQG0U* zL#K{&__;?o`1Awx9Xm*9YQkBAX{g5D+66Sf_pc~S5+c@va<<#Yj_l^>kG_IpR2ZHq z;hWb}VFHLLkH_=DwX0X(v*U&5vc?3m%K!?CuR?bH4k)px@&;ze2wa6Fw7ln2l&;G?3{?mVB?8qML zUbBi2+t2g~Kk{1$(OOtkbhW?pRMtuwXO96C_{#Bkw z$}N{#GW=p4f6+6n#AlBC$09sAPUn69&B)GeiN}IxLftptkC15GcrF{>dI!y0F2*~@ zYUgDaD&qsa9C`L(_CEMa`i~#9KHp3@zY>`QZVy$Lzm=M+ZbOpeec&SYbQL-u{5gHw z9#ZG=;@9pLG#i-uEeL zmao$z9a(t`^8%_8;^eE(vh&^_)Ai=-gac_7J57B|J^&@_&!_I@J8+d%MFe?l3!&jb zx*z>D!*4t{b7`Mw-A)@YGZjm+khbpecs@JT-SPEoLitM@*(CR$dN%mA3 z0Z@6?B`kRBMTW_a& z>6#f!x%tK;X8K{N;V`FmyuhB{|BUt>&!dD=m}+l^L>~)RSv94bFQRzWS-2}|kO~S> z0#k&C2N*l>2E%VWOK_-LLJX9Ylw38^dTd8pFC*FG$#jDhl{Vq^K8X;k zY^A#0y7`zGMo`qUikA2MALPQ~2=T@;06R~9ECYx&5hhy?(slQD2@b^8;!8qmsY-pb zsxL`2pL01IZ~GXvt2bw5XmYNYLpG#`5)9Dx>a*;;`v>$Mj2R^7WH@e;UJI8Cxv&J` z_M(P@geUyb(63#a*(Q9OZ7$jML_=NO+uHW*$moQJnSF=8LKg*vKR|YUIBtMXvJhYb zM&K#0X7TNx$6a0%*=8Qd6TO1HSKmzZs%hi+@qKjP^&P^oX|JlSzK|G67=jQI6^mE0 z<%54i)8>m1Qm*G4m?@jc?MU%(V2WeUKFH2HzsFcl`!rphuCFPK){FJz#~$O90hs%d zfXEZ>Lx3dI0&l@*&f2*Cn=d~6V8(cUX5VsI<+XUc4*{+yg{H>{B$t~-@A))Ei&xut z`t`qNA3(y8pNRpeD*gjI=(+EQD3O94YJ!s!#SW4{KgU;5!bb z$)SGs-1{SrJo&rmY`^IcyzO}+0V7Coyc5DtMdHWqGtSff50WJHS5%Z=+<)NB{h4~c znPUJ2g(Z}fy@({Ovl4fDjG*?4w^Du4jgf7!l3(8u`QKjEH)|H)Ke&V5`+q{%_>k-* zzC&iP=z?q6_?}Nv+O*i>v&bVo2=swihj#J$&%aJztXW_=GImpf;9fGqj{Tj=KHK9H z>`R&OjeXYrZja}I>#w-{?SJ^iPcxEPl^F)$EfAEH{S$7_KLR2`;1;QF{MOeYAtNYV zxrwG*K7??2j5DLsK31()t?a-~>H|!kI6(K^-$9w2uwE~A-Q1eFuSt&uUquxg-}hIv zTztJ{QqhEoO8FWB79{d;o41ChCOGimFWL9|pAkw_axGP&k3BY%(%$a4WP~62o0#xT zOR&7O^!CC1dw!FNs6b{IKt=T?e7>g;Ql+h6^Y2pf}Us?!j)t!4oBZ#4#@%puU-M(^)`M0j*4 zUfoLdH?dq;+vX}LWbJJqW5fIZ58l#pZ8)u>qN?dHu}sslUr|sTv~6BOlM@8|ql65N zNJtrJH_~UYy&p2rzOSm51?OHtXmWz#)`W6^;aFq496OPsII?O_zE9F2v_7K5hL|e)CejzUO>ZD z?*LOFPqQi?A#WSC-xBuI_lHIX=)U{A1dV!qGnM>Feizm>u=OKfqGt7G?PbLvR$<7R zWH*OARRvX1m>TY9tn(yeohKRZ>tu4EhpFKK0{&5^^citgB^=3m6q1aPB!uk3TUvpq zv;uEgCBDj9ifS4us&A&aVG*99QZ0;W#HS(k&|Dgbl@W2b9u9Hx`N!D(o9_@X4@Gt& z>CL~VllFGUWted&;x-C~TMzT{x4(qHw2yMtxf!BNAhu`zAcYf=i|1s?{VvZPqtFGyzqOjftWsVzF$t@^uL5c)NdIHM!N31?;>gU78Kg-3 zLUys}k{en3&X40ND754jB!fE5&Y5H%1pOlnpEyYGo|hRsyqnSX6ND!G(_JMC5O~Wf zDQ;|`YSm_{*PTP@!evRro+8n2k=pV02WiAGeDVk{fBWyEldR5^31m{*8|$%zk4Q}5 zsStkTeuaLB2sYH!U3qf*wrw+cb2EO%c92WTs^!X>ZOE=wR_SjLu3@{I%mQsg93!Y) zcPx#hjnP(;Az{QI9HxK&8+5+%H2wSEU}~rjB{ZidnMLw=DXeawX2W?joO>l@3s-=Y z`14BT!%Jpj*fEn++n-?P&%PEdL^2c4|4fNK*}fFQc?s83AbcVWK*H;BKY80tH{AHs zum8V5I`1uA1|U2OT0Sp%3;r!ifKlfUGhhUs(n^-TL{(H9iEf;a>2#BMpqhUB z5AI;#@w>EHcN1nAni{g(7qjjv@UiwipQh>DD-%8tb%xs$wqxA85@x)ogN~OTr{kr^ znH=n$R@0a(5JIB3sfET1uBCqKWq2!UA|LWhLsw!3+m&E|?cew;L&r^dGO338QZ3{$ zt(vd5WpWlI(EUF%?MT7^V%wRCZEvi{?Q=%>+Aa|x{OF;=!VJs| zqQK+bzH-UpYhHfvccarj+!&*IP_;b>Ay8G7 z;sr}7UA#J~&f5~GRq_jq*E@QAcxrT_^>lvCRt zrt_7jsa&;*g;%|uvL$OH(k!;C!t{8A=~>x7;b=5M)mKWJmmy?V5;Gu0Nk83wTl?PB zzHP{mj1s=kw+F+av%C8Hu2WU@w=>lOX3PNGWfi5eujo@+3{%zVT<9vent%})p+W6t zR8(Bv0_x7b0wGBnabYqgB5&M8Vi6``eU(~VTw!SY;{_ z{zb}{tck?lZDICHSi77Q5hfN6F>q)Xt&iQs$cY2df+SfCuVo!JeGdmF>D|4ZzBgZ^ zYVB59u6r-V3l?KkKZ;SVrfENVyg;CuPIeWRAY?i6O<`%B3=zH22^*p}{w9<9M$czJ z__4=nW&*}<+3%nD(kH(3Pxk{eat_Uy0eC8_KOuSCm8b?$s!>LuCT9dfCnSNOeEBB4 zm32|VQnhoUCWHBdi1E0Y%n5+;BYW{5d_%vW>3ye4_%Yi&C1tF8-(OMMvI<2f$Yid( zM4`Q;AG4w`)_$DU$M2zk?<VF^2HeByCUr zmagqj5K1({&1fuD;zm@v)u0fb)EV2mJ7**eB z%INIwk1*VjlYT1{K#=pN$-U4Eo}%a3_u9M(D2BozVPo~`@_F~;j<}6 z1ZKnlyp=V#$!^a=m8cybPZrv1(&$Y@7=vL32n1DY&&6F>f|A7ZuD#&cSX)e9fTA$C z{f~qv{ZZ9FLi8fa3?jsYRocf$ej&-MeA`E;TDw*2o5=xb>OrKBsYrYCZ7GVv@ZsH@ zc<|?p_q4||qnSv1J1r*HNVN~9D#lN#kYKj!n)?De?T>qx_F0YoK~Q} z%l-p9tk+1H&|~{=qv9LKOegIle=|h*5yeAm)4~LVK+?^qKrndjLr*<(CGcpfuPo)U z0EM*;W!|!i?+BN>!R%--I%067$TpMvR+#doJ;i0zpMR~+?X9Z3@pAQzkqv4bAJBe{ z9p1(0zE`X-Bcap9^dcjDjvgyy7mKfXCynP^9+%9d>V5`QQ6pM}CGXv|D5{pTAGfgxbbi|M`C{T=T< z#fc~G(Q+hWua{`u@=S%MMN0bL$Trh?#<_OwMYc%@A)%-2M$)H+kdTBCz7tydwP~9W z=4}!}GHut})Y{V+fGo)|nt|j}Rkh?VKJ}^JJ^$q6DGe7+Sp#qvms}}(JnOX=W2Qw_ z<3Iuw-BWI+R$=;SNPpSoj!J)3MM8`JD-g-@v)2fs>!Gn>M)tfEB?L3U88i;bFn?QWI7sW$zb821PcGeKrF^6@5wk6cl(*e( z7Jg?+URg>Ez*kx0a`_5BA;~T& z8pBr+!pfl~-8T^HA23V;cWEW13zuupTLmRXxf79;VIuwmgQzrrfuZ_`8QJ@iHhG8f zBFsc@eo40J$L1HTQn6$$EmyuBBuSfN&?dy5%ny*rL{Jok{@pLp_Sl_-{G-W@SM!d! zNZhF)<$jB#=MTL3D&yU!Sai$nlq^~mk%Fp9WSE%Qy`!oMC!f5R{(Y~eLG($Ex6>x$ zwkJ~awS7XE{-&tvr$hLWXM|cbI6QLum%sU)*T4MNpPJSSoDu_Y`+Vy?zQW5R+}9#p zg@q9qor^jKbw&VGtk{g?DTolFFnRN%FVmW|BlLB}cQ}&NPhFn zvtb4W)HHyKst?PuOODvMYODsJ z-+B^8;4UuHk~CCvbihndYJ|f_>C{>ZOn^2!?at>BDv@EhB63X=GcX*Fv08!gGF27X z<7L^6??)~yAsjQ2WgLMgaQelY3J~&-(DvA!3>kxwru*<`8m`MwbT8vb#{@{SO$i0* zc#^5Nh3GB&Vq*8EeTgJ{ z^fK{;A8j^u!pC%O{5Ea7qA2CZPPV-h`1W+KY+43TP*zdp_7%QWlC**m!o&n-!U!rB zuR->BEzTSPn*AR!1q>!YoCjWG3|d9s&~RT=5=8QgG@_0$ors!mVghDnAOy`9TuWK= zvWNzv#9TA#kBo*NEj|Ggj9$|R8@eYM51e|#m=`f0%MzSc_>@5HtNh#qmmP}+yP-GK2^170zx+d!V`YR z4!)s3EJ-`Ztnmvy-B<{Vd7-tdMlTLjE?r06S(io}mu5{v)sMGz1QPiX$0()-`#AR7 z?=v;nWBt$)gdHs(-*j>OnI;5fnt9BWk)`Vp6k?iV?17m`Mlnu;&)1tBve&J)yqbK6jAL} zp(bSnswya3yc)^vG1>K+=ifZoSLlC@?W*ZGMNc*!KfV`bVpO|nBe6(7(zvET#4&@| z9C~+QF)f$gjG@)U+VIxj<|krKM6wQ5p6u_T^}(NO6?cSo7uKZqn2#=-(mh*If|;~A zQwA_CvFj_b(mnS4ghV~fHakhL@3$p-)4pWVK5||hGtk#<7(#NJ-^vVPlYyk@x!(Sv zp?9mQ`h*Z7;m4Mg0aPw%S?%@~oF^nxy*N~mV&k)k83B?^>B5y!QZ*cYCR0Fr{HoQ? zHtE{}2>VAFKfWIk$@VjnjSK-QVzx(_fokT;s>;F(Z@^PhZjCQTE^8(1__XABr{C02 zKdle_g2}#4>jx!F93VE=Ax?{(2Wo#N7(l`{Vu;<&k!RONGJ;RE-D1;6+n?61*Y_pT z0*rNGZa0?TNly65BtGc*12%*o%K(HB1OmaU-uvmlZ3Gg|tdf)g6qJ;{-RnFMHm zE0?dQYWW7ujKQSt$9etDqYkZ(Kr@Mk{G%Ly;D0p9Z`1}*zBIhNti0JTh`;vgk+99YxC1-0Wkz_?6(}B*uJ;yPeavD zM*NZxghHX_;gQj+R8{>&qEUhg89?olk@K(tr{Y4BDh-D0Mi~xz!MJtemh*bB9 z(}qa7Q?L#|GL1ce2s7{$71Mb36(EEb z?>9OUbx1^h+Is%gC|gj0(72zIkNujd;eLCYKjFHey2Mb0u=XQ5%uU~Jv1CMadrl#e zY)X;HqjJI2r;q#6zu!xdC9|hFulZ&CH&amL!+bb`s%m8 z{~HpF5=_VdBq43~78b1)!bpy>623w7ilQJ1o3u?;#Z_E}uc8)Vc@VH6TwDB zwE!gD`8VEr5R_2FW{>F5x;N2a1`+ve(F#;u?UJb5Z~+Bnm6|it=#3gf_%Tku7~T*$ zP6-BR-}VRm-EH(`ip)eJE9sH#f$t4}j}`~VTcOho9hAE~M$ z$ub3AFK(Btf3PvqecInvgu`JbrzQ!7!c0qm8Gs~7TL0bd#0S}``S!oH{!bAKCm0ev zEwP*0ju0ww1Ru2q;$!gUrl%$PG^KsC?Q~Rqo%o3-e1joae(MZDQIwLyCt7a+{@d;~ zC1L<>Us3TbvLr{Ozn$G5mlL25!&FoiB#Dy77R$Rfd}30>G`31#5g~9;bh?=AJdPTe zjNFtO3H|9lfEZ>Vgl=l+{s80J4O=hQD#aLuBFqF$yl3B{iN|FB4Ikdkz?(0dUb2WD zXI9N)q`ceZV(W(Wyz|Bz*t%go4Ry6hDcI}NO(QlmJi_k%`+4w*r+DJIZH$g(bO!0@ zN8xsJ!PYIj{f6txb#+OP7b)Buz~JyOyY}tp!6%>Q@#kN_KR#|WU~bK}-DDEp z+=LxbPq#l-(i?5t5q-QbAhEQM?w^5;Z)!^tLK`$h3^O1a1~i{L<{!UJRn;HaMFVW# zP~)BDNH6@Cq=Y7DnE*43E(s4C%L zfWB8ACpbEUA$ct-yWwvz>Fu%Q$W;}J8yBMOArq*KYpqYv15_+bLjrVuxu6Uf&oe`Qx+zW1E#R*gEvILuV}Q zBX`f`g33;F(6{)y`^y9Bq} zow8({DTEL-*4J^#dFQfy$4>f&hN2HT-9?h9C@bT;U;SsUzT_fg+3{?02_Z=m3mWRV z?7VY$;q@I1jf_M;?8uN>p{v5?j~uJii>Sg@+cp@_lO#i znu2Hvd9%ZCT=)E&AE>r2TV8_s1sMG{URddZrT8lAP!&ZBJF1GlXyf)#we7-4C)D?= ziZRchf-&XRz^)gW>hF%eOi41mS>h`yT5f4T64p+dIE-xGuW$EN z)*`#T5n`4igstktZ!E$>@WQkx<0FJe`r|mLnb@M+WZM_xa(k&8Hxq)f!-mzXxbce1(F$9b zm+7Rw_4qX_R`9m#uFYy+?QquGHC%V;#c}UjNHK(Erv=0m;fR;=n54W;@MZ}uStWm* zbInAwk#{d7;-$Uyo3#?WCao+uwnYpS&F091s<4>w{2|GXjrAFt{Gody){G@&FYEXwE4HOZq7ms z1sK@zT*Sj4`JhdqM#B;{(vY=QRIa_`B1(#iEYEm4r(UX)J*@d^PDxz8_CZ{;UP&q`z;b)}wn2R{8N{B%3sc5dgsz#VV z#3^SPcYx`yh%kl9m78^ko;AN9avihtFecwu=*O$54DWl5kbekC()F0wb4<=vVIppq z>|)EhbxA*TJ7kJMtXaN{veHsU97F{!+`1(reM#lMW3hSd8VY>{jQPjSjYL+7-j&;iC?m10`XA-Gtpa!ACu-{aRf$~hTTCe`25h zvFPfFF6xa3X*R7)1DkQVT$GoVWQ|a!V-!LNn(OP?fApyNwo|x%biKB!Dx-a#2@1Sk zswyfN8=r`Y<;O^O{f8aFn+aX}X)ooC-^q#I>cNkm6D4|cJF(h8oYOFw*%T8cNapP~ z6Mrn>n~$BGn!4cEzkjd*h)g$SVF2M!c(L2_K5z+nRPEfY3mf#t`(p|EMh<4f7dELD5j4l^K-l!;j-;8%mU> zi(!4J5lul=!i*l;ff^2K%m@{XL?cU{zDdI{F$H7Ni)>*!q7#6}<2HSCGrvZ8X-QUy zJ5{JEk*c@RZWxl=xXa`OZ{D9swU7QyO!UUORjS()cdBasbi|)j;>R!ov#%o7Jdh-b zP&mBqxmR8*ArS-UZEw4D>DmoaL1Ce;N~`gVK#gGpBI+Lifw#O`BSQU=NqRXjRcOR- zkc4Wn{n5<>ffAfV85=;Bq-b71WPAZujljHLF9EKi;wmhqu%;<4tI!}^Ym%L6+GkBr z3iNd_)pIIR^faP{=m|*CCZcvjvgw{>S|$BViQd>}miCctMn$U>On|u5U@7W^HcNd>Oe2;t zj1CW1y!hCI%Ya_vnOGRWMGSVF*ki>3)c~ML*UU!DnY86xT1p<@Q9AgdjSnV8qX;Moo8Ued?<1B`woa_$KcbrMcT_{O=t?QL|o zwO$ImWIUr-C*b)f4(z3Q*$RqE%QRxwNA_u+Lkl;4Q1t)HTJFW~&Q# za|Iw(HSGJSh9I=Jjxsu+4g3+-fktMPU$NO{mHv0@jbE?e3_yDqvO#2D8 z@7Tq*S6{bgc_z4?9g!y{c=Nt^@4Z=)8|TD%@*y#SlSta53lpuKSUWyS_>wJSFcZDu z6NwG=#X1X(?W&@%`=#d?9U25Um#8vsVE|i<-@P5}yz$)AY`OF*WS7fI@HULV*yky! z(6h79!iGX8azg^^&Ot+xD|$~<{Q(81#vwQrDf)p>BV!3v%*@6{8_)@&sD6>=5jGLL zW#EyCa0Joyh^YVs273sN44A@x2EhxR_@kl5$cOYz4z17)Wr3dUL3bCU8$c6 z!KtbmNpeXSb|yq7Vb}J)!$>n^gl~1=nU9G|`RIPTV^b&kxP1{x zoJEr~VR zGzlUfOPU&hh=e|!Ayo#VC<@QL`a1vq!ynV#*KcxOCL(t0K0AW9{aGC zw=YJ$2W&!kBxD43(mqwfk0t(aD8!zZpXbP)UFOH^0@h-~0JfTsQdO0chYv6@Hp+Qd z-#}qWNkk>LV+5|EGGtkbRQ6FYV*je<)KH>CCxpff6g8>=sDL^)6pj50V-%sGWoR!& zO~Mdd#pSrXg;wXE5GE=$W%-x}{RmSCAUx?OIM5YQo1kC;!A7Z|)-)3h8}LOziOvJ-hH;jNc%EO>Z#;h!Q7|Hqj>|aC^sJRP~Rw z;q8b!QIx>g7nqpf)n}fdf!{)u0h9tu?OslAXFE^ccPHmwaSingTOuC* zSVrJ3D$#F3BWz5=?xT4Gdg`x1hm!VNRaKO57^eIY2VlghXPSQ8s_vVZfxgdI(}Yn& zPS)L74tpwxJT}X+x-rriqvwdxqCPmLVADoBzg}1|rgh!zWt#=Fk3@Z~XLs zO;6cuibQ{V+-@HJ$q%TmteBS25)q>@J|pt#X9^QBq=R>T>ThW6=rkIOQ7FtrYwEKp zY-zoAa$>hr=@b1mOL>y1@z@i!^_XN*Jr!mU%EOEn-N6g)+x5lJcu0ebOX#gwt9=&w-H zAH7!YThX+IDZdsmFv@<#*!>pCAGck*p0^?@Q6s{^$ORBl4a7VxdDIV}9T)67r86OI zn{iFRmLX`{j86DMFcZTQ%0#ceZ1A!3O?@o!<< zrj^)Dzmur^abZ1uhb>{p?6V_u41$g&dK1ADN95My5_$0r4JA&+pN8;_Hk43^1G`>l z|BhD)Oid+wRm*^4V9a0u3xTp^XM{pQ_U(9;&XdPE`?9O4u5Uz=1&X2~x!kzCz6kLu zieho@8RmeY)EW5!#<&0>1ffYkvLq11#Q!m;{+javbS9x6BNY@O7ZgUnpV+KFtd2gz zGKgvj5E>bTz@#39(ArQ<-v3xhAC~|WVG6>;K;kup$OR)GcJ#N|oDjxXY8#|v7C>ZF zS-LQ*%;~@wxfW?Hp$3p>-RK2TRX*mpq>^55L?qfTqAl7=_yj}`#1XC?vD>TuQhZ`h zB5lBi@GTjCl7xg{c%YwG{`5!sx;j%44&^{2aL8Z)Ye@L1nV_l+_Vx1Q@9tvhnhmVo zdamXh@c6XE8=;T(({lk-ML|K!{TI5OZ`J}Lj6qcqlVkdP=ZaO@$1cQc1fb$BsnC;$ zh^F#dJ0E*1+DCyNszeYP>@*b~O(6LrEvXhQ!R~m=HY58$jh=7xBibj`stSv!9h?q~ zkKPFbaBzA3bb%yUi-^QB6gwL~R;njjH%r~f|EUms0xcl29vdl`0nr-_BRX6>x<686 zhr|RQEyQCkW@06JW8dWXI0tsV#=)Jh6AoMFw@iX^G_60v09MZAr7DWT;k`RKb^Hix zx17t0voAuDU8Y#OF6s4Zxke}uOkqGvdtrn=_{SJkXh-~(_M5?I9AP6c*;-h;T9P zGq;IAaT8JM={eNAlJ+ieqZ$4)Qd&D$gE#1pjje36*wZTBUY2VgLS za45v_BZt`Y%8QH*56$$o$Y^H3(=IeWKubC=cfvo$>)W2@c;5({uPp{Yg4a5)2}gkSPPuq~e=Hf{ML1fRJ$)mPC9IR`uU(i)#lMz6-NsP@Nb< zgn|*Gi>4PxXB@U=2*PC3M;MPVmGqBi1_@1r80KIO0i~TUAUV_k93bkPib>@)?T@ZU zAAni%S|mI1L~g9dsr@7-_~>yW=34O%yF^4!3sA^X>X9Src+}f(I0cQ0-HI-d zl~52_fU4*JOOiFq&=~nA32j|bjN&6YWAOTryuK*Wi|9wA7*PR}&2O})-GFwWmVpSE z@(_&sv;C|6h+_)o=MwoVqQ^(h)z_k@nAbH(dga&H4}K=^MiyEbs&rvilhZ-8N=`Cj z*M1Ar0e1JDP|_#cmr&A2+l*%lR`Vh;LrCE0OJe`WI(DU46Tvc~PctF3^4?+kx;ojl z{ds!Y+Z^YJ0E+?P0t$ip^uG`ZTprYLn7*EF{`lCVG&D4@di81+G%v*El8F@U)@%1G zipfj>L~t^a{u>RYDJE%e^#>RUNX8=jijZ9{>+!nO)HD*ExO74krbHlBfU2l4KBB4r zkq{$9oPR{M5lD?GMDq-iF$iJuEr=+i&|8ybDb{s9vsWWTq=c4(MjTyV(o0gsN^)a= zvUM|oCu0in$43d(j^NGb#UGPE+A|Ho#}U2F{%NGWIU-<4d?g&FoF}{ zo*6U(1%?JtnRYXh1hO0SdizRPq5V`Fo$aTnsjXx6nl-d6UaVJ>)qDU#645Ef6opW5 zDiV52MC7*NKwUd<7nSP11>MnTL;*1UHM$Ft5rqcSa1arkjLSmQ7=f7~L=%vVb@N}l zZE=#{hB3sh8^6ucVWx8 zcPQQ6A>G~G-5mmo(jjeuf+7e?gQ8-gpr{~a0Ft(Xl!%C^V1R&T%~L*#ZEh(*Je7yEzB^HDeTUZ$WTYSFLnGXZX>t*?eS#up0%a8toE|K zHRCl={e|}2eBmRfwVBurrfBs|Jv?M}u^#6qsd2&=N%(f;%$U;*`mGa_*6+J{h55m( z!jBvU{wr%pTQ|#=R@$WlJMAe*ZE!ldk)c z{ISxU{uW%VbPg;}F}9+>ex9~;LbJC=xWHL)3PQwR*_b+Q6qnD zKR2%G$*hdK#OxX!I(?f$a3AE7?Bu>YJjBBKZ0tkRmVCJj6yTX&Z+8qnKi&-|xibLE zX@i-Cr77>Hvbu{CWaKaV44pNV4Q)^PsK3S5k~@(tbI&?jlB;qW&nVv~kXU-Hd2Wm0mNew|7Wmvn*yZ< zyJ1&;f=Mjqx^k}2pxwcHJKO76IXFU&iXGU!v1U-`a1CGNNog)4e_n?X1={TeUM@BJ zONzRzcAR^uo=0`2YFe&^qE7$*v3uiZ-=9s?q3ansO7swKcxaai(YEA#g}AI4suupx z_G)TFVV8^BGT#u-H(^IVQk6ItiLHt$#8!^drI@BNma(w5J}q-do3eWE_Gm3kzi)Ac zUWvdSZ!!G^!q-gZI}VvNUNz!as5_c0&JdE&@dQh+xrC}`e0sX~9e&4}gl8Lr9PsK?>LBkNi>m~+}}QhmVn(xCz(vLlCmaTcP4)K$KCGa+_HFHd1o+^ zWFR+a>Y>_+EZMb*<(98ICpgp9ci_gqJeO!k;3|Amcjj9DKCw?ZW|vK0jLROMEgtO% zt*IHmwcsU_A3a_?pq;6%CZL|VwigI+96hF7QQFf#pc4_pgtv9;9T}P1XFa6P8Iw%W zes~vrpr?JOwj>VE#zYr2TxSoQiuH^>fnN9r@5X6*)A2V61Wjd9Zn99aIhiqsmnoK< za?5q_pjvNUw{)Cgrq}K~@sJp&)H6*-M%bF4MyGGVoMj&CFe;>@G#z?6ebv1_ayR>4 zuAZ~1+vKkv6mF~gu=7Q9f}30FKo_|Q^CMj-(1>T5ziNwc$#uL#HXoEZQY7&yQ}46# zwzSAcJz)=OAs-PEK6KunL!{l-e6Wu2;pnXci50uWr>OnXsY{$YCgzTlN}I4DGMLyjRE+unM7GiURzR=aci zY82mw7|owNb%LGQ=H1Jiw7bKstr=KZc$jTCyRXN($MEIX^AX+D?cxz{a;$l%OKy?* zuDo7gSgfDrh@Wz7Lf1aN+^LLFi)%&XTe0ODNn5b(%xnk>o8Q%n-6Nnnwn@iS9m`n6 z(EdUop7l90YHC`}`%;?bQM>l_FbbHNiL6z+#CB(R!<{A*NByEm=7eopHxsmO;^Y|m zbe=uz6gC_c=unla;hOkziw}EyfC=Yb-Zg~y@}YIvM_i*1*hakc_Sk%5bDi^r?t8EJ zu(&EMfv?__TI@s7f<$9u6S1D##(g(6QC(`=6n}~>tm1+xCdiK~`Ie?X*#(vk7FijL z<%pz8r>&h<*cL95u8aA-JjeM3PVabjdcR*Hj~9Ibp_O#{#713~@!MT#BsjfPVULZ} z0$RI`TCZ*0`e-5DQ7-=N^ zflLVl!3saS3%TWog^r#G=<(UUb%(BMoZXrF)05m4@%+&*q+H@ZhUr~p#t_%BF3fP% zRQWv2d^h=)RAX?661*N%J$(5%-&qmyGkiUSfw?+o^%5?!v~6=gn%e9Zzpmh9%ebF% zSL+aFAC)AXQwZrgMvm9-%&o|qv2b$f^%YYwe!M%FMS}QK8YhGfZ5Z}=?zrEgHdwsA z)WfTWV8=m1`t@;>M7eJqb2Xn_AXLx0GwYw5G!XiPnFE(Ca>CeY%gtH+jh>=cL?r`Qv3Oh;6yH+NK@k{s^AfRmti1JYnq>A7)eR zAGqBv+G5P_-cdEuMPG3xc*oka)g{>bn)DssllNSDAI5E8e&SMk#dV+cbfP7t=Mzg) z4Y(g3z8tJyY_L-!VJ8=68<)i1eLWv)j~?5b)~zCx`8u2M*wLGBM@K@|uO;NLJMYIa zR&XxS*ELS0#A-W(7It6_l3ZQjP18l1qCYt zjvYOA_-r|?tGPJdR_%iXgD>p3H(51~W(H|)F_BKR>OF8gSHDPkgY4+xYq&{?+#E^) zHboVJMkYpRP{u5t4M*9TbmqOpqd`-*^%A22MFPQH7!yrg9IUxoX#p(wzB{%n2Q2p2c!{1siJrH3tpS zu#Tl_=$RAZB^#w@+`b)mvFSL8_ z31e*zW^&zy4KeR1Ui6;fI$iHekPJ;_&p9l0|Dp^@)nZG0c23JIAL^z?YR&n}yFShv znK-BpT-eFtL2Gy_D#=&zKD{<)ke1NUdY-8V*Xq{MawX$LJ?>_`E?JD#$}X>myH)~5 zX_?ZzLr-E`_U=01t`K%t^i_}#xmzKVih^zI3waaWH;L8?Fs6R*Mp^l~&n~0iXCbfU zQ6kB+mbM*gZKJ|8cA1U(qI+K8KWwtYeMrNQsbuS>D<+vc&BI~gPUz#&JyNaPif1IZ zUW3p#U5&3ddD9l-KKmqk`1?pE9Vi`f|dYbIN!%)O`zMIi&bp#5x1lS>NrN&OS7_z1f03bcX&- zt0!w|J6`_gJ1J{7^C$9jQVgYF;qq4B@)|F_>hApxW)V-X5rJKZt8qwCOwU|OH-Dd9 ztUAXoiX%~HE3#=$n>194*RP$TR%0&;DQsa#R&i@?#eKokFOO}lU3Gxe+62G0ubBaLdF^zH2WO+of?Ia&!?EBWfa=QXmwtxuf2H zf>`h!bIW~kdzeWTr)d*fNT~cJFg&E5nr8bKm?N`n)YR8}xIL}?WN%BIMhX^&dGj4B zH;WMb^1Lx+rXCn78**^=z>zQlS|uX_)L$@=HD zE$R*%^?69;TJ;&n@6Th%=X$D1O;{Uez3`ySN5pqM#n~a2u+PsE`xeQL?V1j}&hGwm z&Vr|Un0JoTMqZca4$gz>&>Wp^SfFs5or)WZex0}L0E+I7M@#gKC z$=1&%?^s3g8Qy;5)R3?Ard1}rm=0TlzELi?#~iL>;NY{f8wkI~^jlvc8Y)G`{XElfyj)!=Ns%jEiZML{bT|?+V5} z58c1`PDC&yJf@%pbJt$A;(5vSrUyyTRBE5$n9!pHxi9&O1(8n6aVh0Erd?BPdRBao zf-eq}!(>x5B79jM)*{}*7V5G6f#KyD!zv=X#!`sx2nWmDHkHE-BAA|G*1}nzSF*vJ ze9)3L$_sDZ!T75Z3BJtIEX0iu5+qgC{Ih+C<0g9@L5>X zjw!s-rlg#TjpN)Ug*n}QkwHux{|LEaH__1zDLfOK-xa=T{iEjr!Q8F^7ac9J+!0dwN$|SYw1mIBl%6YbWdb9?o}TDSa49 z-nF|kHa9|!`Dy*fS6tLL==|C!)3LUD%JzyUP&LNs<}f}>I!5H{qvkGfMTc)aD7@ms7^t}BICfa0wZE%@dGS%|3Pvd!rT%LpTP?fRXNxJ|?iMjKA912O z>WtY^;81^sv@&GO#KFPA?tCBvC!TBbz~z|PyoOmOe0==;{*TNF6%{WYUEGI9Lfi3d zLG}H`ZgDB8tch1eqJ&tk*UR77aM51W6s;lbzva<4v{@NT}wk=t9`EHPP6qCj>U2r%qJA^wb zz2p5SveM!ndqqq|>fH4rvl`{2ooRdUl~^f6b4cJ2zS#~n_JnBsPO}%UBZDWrA9Y{- zSmgbPRLQS~>pUhPLDpZ4Qg z2r}Rh;$%18O&4V&*Jx+#jg-Mym7Fg{>C+3g3KfWBYIH*` zgt10^vdT)Qirl~PsI~*Fa z$(O^?#do{rBa(e@@48~fUUL6)1O@EWS}!NB*3K|PW93L0(S}QhRe5j?r&IB3c1awA ziQr6?U0c}4n{_BNa4^*e|B&kmQ=;T{JY&Ox0IYgWx#8FuR_~(}CH&OFjv3tT4B`Y{ zr!$hyjTOHOTl;)s^P?6e5B7G#3_6bXaZ~n?r*2GBY`0mmh;ec?49V(S%_E?{8;=g+ zG}CoZBYT(M(fUHUq>C!@`N7wBqcXNOH#T^Ye1NpCttq3lz(+uH1sGvQ%$v7t5y4O*5C(U z>J!RL0!>~b!+WuGiaz3$ty9_Wd)_=B+s0k;10U_N^cofoLlT#fqmE_Cm4Rh4Z_{t@ zO==PtYg12P%;)S0&wV^S#u2mSz-?SFIy@3vv&u+m%aa6V*I7DZW6RYEi5+arV?NSN zi8yAwc*7muNK0*g!9%jyvGLB$%Tc_=!%U4-$Cz^kam;S|P;a3y$xpPTI!HEHNzYB- zlfYday@iiLKF`8QH_~g*lp6(4z`M^SJ{Uj#~bP zDg5<=v-(Qj*~gT9^_y#g<=ugpWsq)ughyN%-3KXwu4(ywsZ7p|*=ki~ucjlK4~FFH z;y!W-(zxjvBhfsato)La)aj- zxWd!zwX}5;>yBNXW4RE!l~G(VvVOf-<5U-&oaTXvD~&Zio8L-$W467DE-TxxEvZ+n zgl8h3hE#krj^O&CXRIQFjRMqoU|9LXb6T@I4@%?r9B}s1FOR%wASmOUfZNEMZ|s>26lz zn%ivM!|i(@(%YJl=R_xA7S_>WO0JFd2Iih5+GJ@G$MJG$BixC+1I}Aq9M?!H(=?h6 zEA>g*+3DU|&-q+5io{T5ib3#RQChp>o}0dmj7;T|*IG!MJ4HNv3a zrrn1Mbtx&d)k$aAPs%e5OR?ByQtnPG`AALbU7nT2NG@I%p@?Tq245`F$$YXa06TLJ zr;qydz4oJa@uI_AN`i5 zj>kJo)aCFjF8AJZhl}eDJ{QTcIJcnWWr}5)tei4-QMb0zorC056rJJVe9oQ15S~}f zk58yvDa>K@{V>$!G&mDD!ZDQR*BqszmxEHI;FUM4!%8J8OPCd#vNO8%Uhia zeNj4VrY_%cMU}2hxP(u|Io}Zh!OrI)`%+RQ7)_LyCoo|cw&rhU8ruD4f zT{QZR{7JU&@a5TVb(03dGYlmx-D@M?u(r^o2va8EMN}_c*1p62cdgvofE0-`Q|6WiI4&v$KPE?G`iZ-oi^6I1O`8>)f*#n)@4L zZtA@1qVGB+TgP&NfG-LQbCzV%xMsv9#6+iXM+HA_VSUcfaZ{f|mkCpf!*6S!xHu9* zZ3UOp$e+2ql_5a2X{f2-UBj$kS&(k-vw|&SB~NC|tOkw7OS7#`tLB{bp5Q;>e}_|2 zzOivZ_QC6y>(+)kD22zllgedK@tpjO=FNJDHdN+mS;uu0p|76>%#+;Z5flkPoB76?=_{oQ90RY z>7cHE+rxlX^hoTu!wuTgrNyQAIj8w+x-rilPIK1AeW1bib5BrN;YU`eLJ` zoB&h#mCH{9QwQe6$3n25oAG`6FgYtzBJ8y-%HW3Gd*7mx@uUDs1_|fTD+>I1*?Y@iBGNEV01R6W1&)(oa{Um-h0eGSUtU zh~nsL=yhdXHsVy0ZrZy&?%MFj_-&a(g(jZ)8fBNs`F49HVYT|@KPyXj%;ZiT*i0~B zoKBNUm{p>3{nY|_3F$88U0e4!S3Awx+^Zj3w~0wWBN4GFwQd z@^yN*`8f|wo8E3NVr%nfk)!_M;IZoKqKjohH8}T6vtLD|7)1xF;C-SqVM$7e8796l z2o7{~JTBvU)YsK>xx`fRUe3T^Q2uT@cZ_ukOF^$5u}6ISp0ryH&vvt0=7J*;~q zQAVo8)wi6nV^^(mDtR&P1whzwh;-|k-q#}5Km2=1b*Y7QO znL2gu>F`YHx&`nuzLgR#ok?%d6g_^!k5BV5%e(&RpmG!Mh7_yO%)5bc;D~?kW};U~ zb@A*Byt>=uW;&8z)PLOm;iClavrG*^VRcbK^$U-nM^BBj}DOL1nV?pCfzg_H} ztv0>)Xd2miZf#|`QnOidn(dQPb3vTBhhhqT%=qQ87$fF~`wq8IZr$Vc{C<#k|H+!F zsXC#({R1alLsU4R@WV%5`3`C}cuZY&_bDshe#Cvv;W6jrjkEYpuqDZ$lcwgDy3_q+ z=lm|X!bID??PJ(net4pHfe-aV4-EDP*`c=w)}Q)R%5=1TRFfg%#8$rR&n|wf-Su*d zykSN&aocFQ&#Z#Xz>GOue_13`sLCj)o@0n@bZB3L=ld<-ik`2~^FZo1d$@0`tKV61 zd~jd+70Pi>T&Y8jMJ zk&CfB*0%N9ow`x-8(kMAMhR~>7R8wkUTNsq%BG>6H#q6(*QUy#dtP@#p!RV~o4lRZ zxifO+YU1$S{K4pbhKJLSPt4FgNS1k`62M^k z&vq zQ3|*Dz=a(&IsI2N-<~e@8DAfJcytFTS=J5O<`Q-)&H2z*eNB7mSjSq=>oTxCFJkE% zz;7Ncf0|ii7IEd{-h3~=3S!-biK-77FY|V9INmsxaN2i%w&Qv~hNiK~?{=Cu?kl*+ z>fmeb)SOeDH|Rs9sSk~rzP~-)QcLLTHp6j*|CoRArhc1)vIV9iy&_gJ>q;u?1jpbq z!u-2t!fp;mr&!jW4_W_SDD+iK@=kfP>=w8j)$NmP4F0F*->a5?Y>V+No1iGZ z+uM5S-GR>2W6V~nX~)kh z&y!Gn_F$EN{mT(*uV6F#_f-_ZZ(dD$*S?Rr`C_4?BV3ZqXCD^f5RTu)4hE8>T0=p* zSvuDezt_)C^c(p#o3?N@lujHVc#THL?gFm_eh-WxOTcP-qvvrn#Xt_=4R$<$UOmp)6E&eV)g zIEULMecZ2VS~I9z*tf3%5S8(fcWiyKN682-@_MZ|#O~cp#^V6Htn2{hkJIrtr(dw0 z4I{xC(ri3k6^5nQab8zndt?JsV)lXcQgG?qo`w4Y_SH$v&#wy`Kj9cY?tdt{-TOkF z%5@Xg!o1!_2EvWrB8CIA`n3J9EqwF*L|uHdy2-+@FK*RX-v@=>;5mD3QKuUm)n__# zu*R=>1yaJ*RpVRUW{EneyZ0yUB4bT;_R7e4lg)H&Ae5tN7>hT$S6!LwNXZR(7zysq z8R)1`1&TLpH)w5EXfn$5_L~$6{WQK1s3mDNRO3ez=hJ`c#$l2uo+rMUFHCjj-L#E!lC%w@$qc4U=QZKdp|5NUOQ+A& zPDSLm&P{jO5vpIiVJv)U#@Q-Jj)KECo>|+kxqHF&6G4BUq?3osl{l%4_@^W`K)UxqVn=`O9=}U3d~E99-)?1X!ArB$fJSuRFeBvCURTMReQgX1+F?&QaIYDs z*>ApGWL`UMamb!gpMn0BZ+Aw$0T{4tYpx9>*nl101y?X`bb@mZK@KC6neMz8vvc7> z6Dps0hyxgXUT%Ynsw))6XHmu1F~yIy)%ow?g$weXQ*P2PlTR-$CedwsxdHt<_{X1H?`jv96X1R5CdQQ=kPS;pv#}HETvNgc7D#CK7 zxGg=#&uEBknrCE+I_J#X<)ZT0TQwbXm3?r*LAV;X-IXeeXB>VGIWvRzG~*Yk#o}frgx25!{}PC z)9d)&lFZfiKA0VeFWER5;I&g`dtwIWz0U;pxZ5dlOb;P2^x8>;$y#UMSxynZGu1m_ z^9&*M;5}#NLoc7%ed>OZ=b@L+@{Lb-O2IE_z}1b;3?&5|>kq}^*)1ZUXJutgt>7BJ zUs)9}j$F1st}eIB=FSM~Q_9y@9(iFXy|F>oNs>rIA-m}rimG>)BF17jH1s@?D?5^Y zbNafBg6rUQxQg15f(Esj$YT!Yc5bT@EeRi)h0Bhs%{)0T>H$qIFJep4i0Pv|k!&`p zbD%rdGjp)f$s0o{O`u|m&&zu5aukPo%{aD9{D(B^`?6!Pejr)%uC zs%GhYdLey{)VcVs2cOQg&eIh!uXP9=3h?64@+&T`OAVV`;7(iDfcH>QQ6A?QCo`$o zt?;ZS&yVB%0RPI#?F^HX9_u56yHe986`W;ypMajUD_^S~^r?@F2V-qCni zN|PmXeh7X^z|m!^R14w2u2+{&zqt-RDJHj6sT{0gIhnb!Yvj{ycNH;AS>j5Jy>s?#k^|XWfZU{46ZLNN3TYNPiXSt_4{f`@t*c4W~%$H%o~wYp!M)d zy36$mS&3#KVo5a&+_cuR}euSs3w?Q^j<@U|k z#;a=UY9~LDw}5x-B`@y3>!Ya4VfOuSICb_lewiG_2fRwoJ~~0y>io|5nfp>f7pVoV zP-Q+5SA;UiFTl8TZaRXqV_lkA;#iN*r2O8A7*m?JyRi~vqNj5F_Hp~rWVRc>y=Ysx zP{3BUem44QU96KYtCjWe9k{;h`vJC_Rg=>X1@)pS4mJq#mCfC{7CT>hJb?`}nY%kC z_#?4>^tF?LvE6(E6{otVFOTiTR8&X=f5Ip(yl?P}pOEmOBl`vns$@?#gM(+E)u4Erl_WGE^0F zi4E$v^~O#dQDt~4iGvX_ie#D+Dc*2i)WOcWy<%dp^TzQ(6aSIYeD5;_ug$38g1JMT z1PeJ_-Aa1F;l7N$+i6FNrpJ>ccHSOs?@y80-uv`n-OHC|NRJl*V=1pSZZ-EUYj|oLceRq?SOb4?YQ8V%Z}9=Ft`A=r?h5=`XvjZD+9K z@szSBd^7d)(+7`W?v>2qiFWG~z=iZL74pzIbv_T+Smyq6zogIeNh1zs(%?7e#vx?<8tFG)VUI^KmL&B7Gvj66Mj@fE3Gn|5}mq5nWEmSJ~8>h*Ju{QPa|*`>^G z)iwLhJNL<-pZD*LXlM=|YkO$WtB)7tcg^pWy5Sr7lbg!LFe5JaOZ;(cu`jaap3=;8 zj!ZK==6JD?XUxfDaY661#HQ*TnE_p)k|HU|SlCo?m&jj~PfrKsN4}JY3z4(S4HuoH z?za-(X;Zk+-f^p=E(FVWEsofb2GhyujK<81bEL!u?J%TFzkWB*-|q5(gYe<3Qx(^2 zZN&Lf*FR{=wU3Og%s7=?JN}?1xeG^eT%|h4Y)0^v+*a(m#PU}~HmnO)VtlF#_8L@E z0@YZ|Ai_zz!}RG-?Jc`&+yR2o%-|@J;Fv9uvDzpucIZ>H@5fFRTtAg~M$?E;XH8JZGl1&gzyEI9i|1ia{8}`MnYYh{n8(v#_=7SyEe9W zI|H_#RvN2Pg3LOnQ$$Zg?dN&|KQi4E)EL{#kw1PQ;b9U<5Pn%+qGJL{&z(DUo`TXT ziRwcP)%IaUcjCK`-PAmS^M=$dyeGk?;F-=)tQ*!_;g^xLVu>;{3)(~KQXf^)s92uD z=N!cPDE$Oq2o4Djc?=0}wmZY$;-{OZIce&L8&8>gcWr~O!W&ZdnyF%INfnY#qYIb^ z#MEV5t4wm+f^+)l( z?$yjYIfe);nSuhZu}US`hV(ZnmF7;cS+e#jR;az@64uIN4&$k`8??c(D)ScV;{D_| zFw0uKXZaJ!;HnWyEjo!#HYOrS+D0r z9<)}ZfL7{sEt_!=8-8@6Xff07A%dIC5mbI76K^xOus#MjA~%I??wwD(0>6NV(bqQ7 zI;G*3#1DTbpabypcX-Pen0Skafb&B#R8!Y;SG@jQ4CQ7(GJlI@dRQD z;`Q&Cg!>VQ2M}!#)er>`i4YzT$`FhYI1vAC{~QfqLs94;Od)na)Ip3xe1rhM7;`Ya zQ3zy5$q)t*R1mO+{k#2z8bDwARD(!`=!W<+xL&R8e1Q2Qe}U{l0Rn!d^xy4I&;S8U zQwt&=0@d#Yh}ClRS6zo>LjEBeLJ0y7;@|D(G{6CGNA33@#9zhZugC}40*WPKAW+=! z@3wjkpf(-FZj}&!r*Hoic7W2Dg*Xf$3-Rx^yarHgZ31x(VjkjG>HnXS&L>zV6rUp7 zMmGNM_L~M!{HzIa9pazD)t@N`Y8z@HWFY>3-vponP+$9(#&g7kcz~Ck8W^kdgN-hV zAU)C$98I?emy5kX*Xbb8dodJ@G)4Uf9$pLqou`68ZJ{S9-)#rd!u3I*qdd@8;R9T( zRDh5G_gC8l@^PqLV2AksZ6k#R-U=}T@l$%m!omX7Ylwk~20z%gNgG@$bOS^6p4gN5vc_+i~4 z&V{&XLAaM9xK!W*#;=Bhmn{*0DV{cmf~p(`u)$dt@UX4@;j(|s{|lQF zEW}%g@8TXG4+ofQ@_>tZHsJAP|Nlxkr#SF&wfJ1u=RM zXfF)PwrPT{3ag)z6_xc|w!xyD-=iydZ7T#V#6NN4fbXgsWI5ONfy>wCO}Gzx2k+sl4-T)D9P|)jKT^s!sS8hA=4k0zw4Me2zC+%_;}dg zBRhKL;h$y$oKU7!bI;8}29BkvgU+KS-;MTiV^Ex|3{2Ho|5DDEvklaC+3IqDBRkbV z$C2+Z57I$lq7q=DCH`-<0S2g>`^)L)%j0U&bf7*@2echB_%5`R>4TlYl0cMq%`frm zU&{5f|FL1eMOv5|?2V8Ctx(qQE#HOxnm|(UyT%bwTkvL{v+%Ax5|y;MmZ5GP?{{W>M|qUG~}rP z5gv-~u>sUK{5!@6&{+OA<;26q23x#&VYw7njjBCzz)YRtN5{>+Dbs4?{#DnY*v`>_ z1=M88ue$uvp1j{%h6pI{U%9!#%)a@I4jwKRi1p$EwK+1Y#*PjAfNm|p_m=;EU61An z>8MD7x-2IMaFhdkq7A{xeU6~zs6Tj6vl%>TiUi}=Viqw1f8RU530y7r1;_U~fL#&# zz}Hp=C`+;fDoWyC;(yo}h(Knz(5m#XFGA>ll?Up3rDJHE5Iv9nS8O6MlXnm+$+K8@ zquklSg)H$^p=!@MV53d!-j2NfDHqv z-Yc{!lqK^4A+8^rH%0A>x(pl0i8la`E(Su*(Y)}Vhu)e1kP)d3Ab8U7*J7kT0Mr%O14knvfc(yC^z!*NYzzb-JA(Z`baX0>7dYrq|7?2`2lM$e zHa(c=H|O#cL`lH$o$TObD#tew?@bR#h`wv=3yz!P0e`!70KP2@1}}O2BXG6M4mg_# ze0SSGh!1_26dCX|rGmV#0X!@}ANN3x-a|aIMtjhv!&+bZ+Tq?`tS9 zguZ}rMV)<1r$2dq3f|8K@iQBI#Ch?Zl@-e&Er?{N$Qg=2WRD&HkrL!K&NXZ)`D-T%n@TO54j z{6%>FN9G5tzL75AX2yU$!Pp>k^YYlc2^*+Mtgha3vk-&hsgmGwuH33oohbu+E!hAS z1^%zp*@|f~&=7)6j+}5zUv|}U8Yt1OnC7>1{S)Vr|5k_io(;W=i=H!CR%UE$4A^9c z0qGl7hL;HjKKHtOI!k%5(i4Is+k~M`#aD-Y;vm9}9k$_r9q&uBF6~*M%!z(ntCCk+ z?z`spm(}}H-ru=LKHz&cBY5$O?Y;yb2BZe8j08^%pdkG=_tccQpdgMLoXrqe70|Um zF-H1Jb&J{sHAxCkmdv}X>?hOs0X@~}vct!?MBx>zd9f6*Sjo3YXwbr#<$i>)RG zL^>^xa0d)nL$)&isPC}ZfdJ%25Uq&);Y2`Q)abtpc7{*4`1X zn{6;46!!a;gQ5KL_b;U_FN_7p3~<1{6@bp`$*nwQgo}#{@bJF>*?}cFes+)YNBG%v z{^I*l`~I~Jt;zBRV%hS@3t>R8_43$gg#mPHmcJjRO^A;Hs9*T4aRWEQ<>N33?>UFKM|StM{6$4Y|MdL7N7iMoMQy;>)*?Kx%=L?9qNT(D zU)ZPfgX4M20mX(Zr9ni10a4Dd-{`$Ol3>4*n`!xU($doYm+qIiIDY*2iutc3+iK4t zzq6Fzm-|D94#EEEKQiwBQZEUxyq|B#A3TEi^146%OTj+B7xc-?!C4yv=k=GrAEm7* zf&p=EE5k%-`805Fa6nU2)3W-%fB!xpCnx_y=9S9xqvtw0I#$e|nD`&l-(N`&EwKD( zoNsAE?{6+U<^wMTCJGqfrn@|pgqPCH2&p(W&NzAyRV!}fX9|L^t|Y@x);e(|JCW~3!ay&o3rpbG#_ZW()DCSzwwW3;A_wlTe-chDp;}occG!1B94`k6*tb|I|Ig3;%m3UlbFORj~i2eBBSiTw@8(a=3{gv|n{M^6%uU=Q5>urVKXW+Sk70v@T z=>Pd(|86;ud}GXpAM!8DvplHDS6`;p@J~law>Srb=3)K}{2v+rU8-}}Aos6U;-3-D z=d;4G-Q|G)jX57KdJGU^SsvfApU-9c7XM500DtHHzhwM>iT}G$zjL4K_{-naa9oB4 z#&F94&Fg%5&*%S{V0?gL$mM|djdA18)A|sMP~Q&42+LtL?*=WUMNCYz_`DC|1#vYxIts?d#=!XaIC%d2Ie78n#a{xH7s?am zjbuQwAeoSzFf!1usE?(x{6qFnza&)QIIm>2N#Fb6i~nm|iU8ieaXzphv{&8_}00SKrkQCzwhB^ws!_f$A^s@n*1MI-2^|m0` z-x~h60vmiS7xx1LegDD#N?a~?4%OeUVY%y9I{vx6OiVlnAwlq)V86?XWZ$&@x3UEL z+W=1|BVeSf2&BXXpnX%r=Vku(XGg!u|F8D{3~;S4X<>R`Yaj}u{4~L4s2^0XH-FBx zj|B+yHU|M-W+1@R6s-3!0sijB&<7ZShPrcq)Zedh__brmSN{mUme$vfE!js`XX~Pz zNLD2CdMG<811d`pEEBTh-)zDbgap`wU})zKRysgMK@4!PG5`XCzx+LiF<5_B{2tq4 z{0IAJG|S*y@gFMVU{iu8VEJkMQ|t-h<+Rl(-FxG9p=#%t&@r22>VgyT~Sx zJ)^QBdq95T4|{-h0Qo&T3oRfgB?M?`*1~6|R(}5dTfTP{=g}PCFYo_hL%G>l=oj1W zosnjsIL!$Zr8Ed4fvcSc zaIsYf&Ngbm*;*BZ2fKjg#!G+1eqZwX?AbH$XW+|x(0`OqTT3&Di}C|bP&OnZk`>8} zWJhK2aL`_~izS;_vIl<;=qEn22jm|%!FB?*eWB1EeBI1|wz|xsZ@|I%Q)~gf4>Y)v zove2J2iYnsGab-@@&tGg-V3-TS{_sRYn9U*7lme;+p^XoJuW5zjX2zyZpKWI-|^*^rD#RwOf$ z9hKoroA|>Xpr1fKV#yvB{ezb&2!id!l5f}?V7q7wf!XBexd6Tkb3&+P%VX~>Vc*}?Y09<~?IA9_LG zuoOFdX$z>02w7|+92VmRejYa1Px~RCkO#TXU5S6>|Ci=|wZ-v4psg^-g)!XW40muS z-4*PKw_l89P@IeW7qVR>_hQTj%dy!<7jka|xiA&8^C&v>J;J{>hW`)mpC@i6ae}f0zeOb+g(rnHy(9Vd4Y~9&*K07{zr>rGN>MZ z2{h(s1@qE`-{f2lPoDfXuJq--OaIMbJx6(=Jdq6{8IUYUCM4UEZ7kUZD#PbCVFAmC z`~>o4*2d6BKzp!*w!df(pS2grH+*Res6PGHM*J2(qWXsVO(<4CK0!uO5TLP>mH7PP zTs-_A{l&5WxF0?Lp(lk0!rUZ4;Z8?TzSjeC?*__tJA;xm2aug)v)JcBZJ0NT(;??5 z=2?u{ppWx(P=j?IuECd}1J~%*2K(bQLG~7PkQJi}zs;u%_C_fJCvyo{ch)V|A*8n@ zuKzdBJV0B88-fdHDRV&%ITv}ew#g9*xtPL0_#Z$FeL&MjkJF zKT5+G)^U^<%JVlH;#|xZ$%AA=w(*;9`P?pk^A~^E#AiNY(H?&H4>qvPSZps~41wCg zrPyJ~7LaaHy;^D`)_a;Pwy~(KMgAA{qXWIHV0$bE2v^sabc5VmFULQMIaOr&A zXr>n^--GJB3n<;?2uf1zK+z5q%Ub|n7v10DH|W2RA4Bb%1B|7RO(WaRh}MGqYeDWc zAopq@J5~kquLSv51esCtAkayCvEKZ~_fnnyjq4?zHDH}ZdRJFq18VZDi$FZ8$T341 znSrt_)8etdzK4rEBd)&$8rL`06#d%MtPw;QQk;~C3%o+i?*@o zUw*TT&us$v3VjX!FMI@QH`XosyruSH(Kmd>7Erv2+K9z?;j`Frv3>ZxPl;kL)Ha}a zQAR=#J~O*wyPyoYU-vcsnVD#TtGxjT53&b`_jo|=T_N{Qkb4Kny)EP(#dYQ&F9nWq zxM?l+%TTQ&b&uszecCkN}wERMcnq3dd;Pj}ZcWtM`xZbE7)1rX&u>C4P_R;5bFz zc1Os)BPdO?2gRw@kb6tWy&2@*c(Gj%^HpDraTnt^82h634cRhk$5DBF?WI6&oGQrK zq73$LQ3Ba93Lq<54)QMpG9#toddX72M2ick!TO^LzrXrhokm=L!86pmJc0x*kc0Sx zM=@$310g3t2j9(lusGL;c>EG*PEQBcK`GI-%VKG+@XLFb{%b-TMS000{BB1`76c?4 zvW?H}0+nIWUwm#8sQo}b0@?#=%a+;<6h|z@55_Q-{lgZZE*E14s9V&hK>dlO_5t-H zmVCle%(yt#1Y>X%i!Y5Spm~%pe8DK>ed#^$#dSW3i3s7mg~orx@hGh{*oP}kv;BYV zeFu0{N0zPxNPqw#1So)na?Uwtfj|-xAcDv_=bVv-`v-@`5?AzJ#dH-MC)wgkqY|E&to7(a?OFQ1TXB5gk59ObWJ$18X@A6FWKn8dq z9XyaKyOt!&%;Av@_1avYnV%|e&H2oHPfzG9)yR*BP9Bv{KKV$##GD)F-86Lh@oWzz zXrmKj+$AyE27CtS`C{8|b@|)IM}6b5vk%Hp^j%4t;tc~|kXJ1{q2+|R{h(aa@`1X9 zwQL|SM5#P9@j^oz!M@QT@B(!OEfYA#p!5XjUL1d%gmDEEA5>ZRVCcY7>CoP?KJ`hI zzdjDPc6E_qgGx=hJa|Xz@eFI}^a{xQ8kBuM)cYzqyslght|?Pxzpt)Xb}lP`yh^m_ zZ{%UNbChx9ZKkVk8f8EZcpyvmt;hfmq=N_2WcSh(AX&D~kC%cBA4|Q~Wu1mR)%7(c zu9H-tUcC42+jh&=w#;nrS+<2a!4enI&LKG8Z~I+S?mzkXBgF%xks!^a-N+M};0q%^ zu-%}1pe~{Hh$6@a`@DeuE&94_BRKxReiGYO_A5<&2*?EJT-3crV;qca1NB8)J{VbB zCT%eniGp9N3m@lkCHcv!-)<~-hQY8V3@<|2SEKAJRoNdx*&l#D$2NU0^ts(D@?>;P zg1KK7VJUN_gZdElqY>2+a-c3-4y?+AKkjt!K$`4Zo+^8nrO2M8i9mu(s0p+x>*QDu zA(CP`TI%eH_1rA2E_+OCO#O^zy}0Ode+c?X9VO>1k zBgYOmjW@6j(fv22$D<#@Fn6x#gR<`fxsU$*>Qa;?^|=Bi*LR~mUpPKd^?%r=sWN9< zPW>F|VY>3+3!i(V9)w@-gR9czz{)i6K`Qtl8GMikK8TlX3t}WY+0&AT2~iy+KB~RM zN48h>H6hAfu3UM&+2y0liDjFe?k^F+u4cI4Oz3-8`SQyzZaDleU?Y^4*j3R=nhB;s zwKZs4G*1}$f$~A?5>~RotT$+0V4kwS%l?E(cQok=>{F^S2I`E|6F9EGz61JQN=LBb z13jix`M2!!xX!x%M}+#d8acT5D*F{|LyYB4o+588pH?7;*Y-l$m!R$!ne{oX%Vj`* zr$EjJLVxR_em;N?(KQn_3$Vzxo++s4g8y10SS-50bFYXM*e}mdDBT+8z>T zF7McIH;Ey_+N$~*3%Pye%4^pxAC|K{P)A6N=_H{6EgjisS2J2ByfNH6Lo0(-zl?n&rMpoNLV9!?o}1QH$^9d%)8+2^RQUK$29o5^szmTYyc}E^ z2Ofx#(ku^&3T*?lmdFr5*UumqIev7n!}76L=Ei4FJ$X)o{hbwpa5h3ezvuY=8xH@8 z^QSGel4jD*G%zjE27VwPlgEwn!B#eCUa;s4$WxQP(9k|mCa}M3k_m>kLF))g9$51M z$C{^%?aTdqtd6pxgh8CUV*i5mU6(uc4bymB6!g4&Iar^M^3PRz++OH$T8G=UBuSP{ zh_ce(*xr%1Ep0e-?&(8Auz!2H99@%&ea};XWbi_w++7zBUckN^z{c6wyCJ-_grmH} z16xTLq3SI5%gPIr_uhNQs*JSm-`M$M_=f4%H%oeSgDq+!D98iq^2emWpnsA%f8Spm@T-4-qE$&=@bMHUf2t`hu1TrZ&}2=~gD4QS(7~ zgN|S&56t>O|Lh+zK2Ty+__zrT^6A*WJmoLcC0O6trm)SJZ@2tSpVAw60&)LE=G3j~VtBjn@*M}>LbSozRq_jec% zI(z0}spy>{-LclBDJaN|k_S)Tqrztxjm1G*;In5QlD_3>inbuor)3Fg4<*36IiOfZi#NY+`AzSNRboki6rnuBKFHp0OG+HaWb?|7nP?0SVN-gtiP|5 zm_YgMD#1P4z&---CtBnE1_aP*;%wzdh61?yYGSSzGrEy>|PQnI~HJkG^r!Xyq(hB*zU3YV_QfW$n-{qxIkZwl?OIu$O9YG z;)gXP1nX`$tS2OVRT-f?&E@KivQ@x(18=~~ z2v69rH4|QV-zp4qxH=xiOJ*8Ki>6D<7t*VFmAq@k4`v-i@q?wku;m4_T%ZnV))#bN zf^t>!0rdoqL+Z9c=?HfDAVQ5TVt%Gfm$&UXEV!$Cac;;D)G2hoonsRWlR6#asI3Tu zu9gNFj`5roN$9&JKnBF3{G(9*5wd%6m`oeuYbkT3yP?dDZ8+1Nlhh97o*@ryA=2f+ z&1pcYJh&-EPHsvDlH}w@$b)t9Qkv;*UCxHGHI{1^tW`3iGv+RD1&m?x+8C$i5%L^) z!-_A=yh@(1%MVI6nA(f$@q(31fLx_~)jFddSD>zK%LlBxtivo*$n-Cl%pcR}{=hk@ z{`*Yzo#oz5l{;k&c}(e~37&H20_y9@-~smA;=lvZvTs?W>|GimdzOUCT?>O%`GyBk zcQf_1m_D|PY#%8TSszAYFYHIRWdoVmUn@f%xieiJ*_sNZ0Lk+379t69Awha&dsy;a zmxr-zJ+Vf~h)%FqwFeKZXKD=gzTq=t*t|Bzt?AKxp=F1WCzQOh!w(kOV2>BrMj#J4 zMiCj}s@hJ~KA8KIN>6Bv56tZX>oCg{`rJC(I&XU)7uh8V<^Ch*Fm$<7|0IuTJ$_`B zzopN1UhG-X+1ZXta;g-&C2S+;y2Vy_AcpKKMAx+UZepW=9T? z4P?rh?HO`fAze;yO9N7Y6vYR%y}ebw>$1>gWh_tbH)9X(c-|b?3xm&$aq0Lqe{_Mo zsV?dwn`Vb2Phw@=bhM!vCcdSdu<(Pq-C&zxlnst~p+R?~JdHwIYT^S^+o1KuYvcjy zEX$B(i8-jh)eguGvMq1h`>vjCom1nxzQp>j%U$X67|&EX9rXC5==QRGVT>GJ6$d_u zl|w6|o%PAfwD5#zm*yXdVE)&JiaSe9=|I` z9=j_W$TDFk&O0;Z+|CR+w<8^UGX~?kmX95AfBo26Z%jIhWDb9cXI%H`dluT&oPewzxQsYd}5$y4S z?mJ>$_4Kao^|r-r_dYhC_K3*UrfluITy%Lcw%?95QkPJ zNoq_7OF7$?Ydfr0Yz*`*Z4ZroqyIC;rFkSjr4#g&6h)hfMiadz+Vhlr3q`-d*lrm0 z2qhaD;RU5Tn%jqv9?q6A22(4(WPOMFzv57~Do zPjUQ`Ppi-6e8z$8X3~&Lp26(?zfwC`360na%u6{9vH8Qk>hKkfC%tH7!WE)>x01yK|mnd0)L4M zbyaoUw#;>VN?A(TniSbl9@|wSFCXh;KyO?VWj5%AdlSy@E|S^9A|%AmO7vmN99R)4C)Py+ zk>G`JAWZIA6AFF^mgB2~zz>0P=iKh%?`_iCm~Nd1x;@qHE9Goxk9Kl$bCJAuZxztj zj7pphIDzXE6$X^!x*U5nmt&4GTSj6oDy&Bb)O}~WYnWSN?XnT});YJq-h@%X?lQiA zD8?sq(i4A7 ziR)b0P@>Jd>d>x5iy;3NuTZ|Ie!HREwH{Ae`WAR#%q|*;l>634$i3^s<=%B+0P<~Z z2oMAWO5G$MwE51Kv@;)cyJ~E!DQCNSxXAWJS#ss%00a8tn&{Vn2XO5GZ{A-mPwXkf zSYx_O8VI|T?C#hTypweIa)T^x1v*S{Z|{QssGB52bdo-~elm41#xWOV%G3LM%bO2W zI}BgNYotZdrfD?My=HofxVLL*X_It1b-Fd*lOMH2OgcdgU0KeKe4KgR2#<>Vca^1vMt*vmIu?%xn5_pc8zBN)69 zBnw7*AwQf|x$C^p?W%64wft`5>MUdXMW767y=Te}9DC|GMv4 z!qH(jftJfB`b$k|P(xc}&_i^3b^49GV3Z45Cg?E-ww=0d(EUg?=4eMA1o}DuB{s4{ zgiUca`?d{63%hwc?c;cj9(UK}P8zgM=kL=J{r)iQ-yJOvZHfdU7!nu(`}Cznj;ewALy}XGas<5f_u2E5uusi%^#gQI(3E~`g?ojZq(&XeP`cQ_uX`xMH(yfJmt)`cpy$r-x&+U0MQDkwnoXRt&uj(z@;BS7u zt@9g7?$%tnxHvhZt{-Q)v(9%!-_=;|Y`3)BnmC}FJhmeNW1&Pm5Qn|dV=XwlJx0!M zkCwCBqGjW(K(uMCt|@b!hmOu(o^G;mT!#GSk+Jg4g&98!UcG;mOdFbv{AgoU?)GT5 z<%Mn-x7s!@Nacab3*-r9W=%;Kr85}Y2ct}29y`(%SvFlgTW+~ka^IG7+P8J`gkJg? zbyJo-c~O@;`Bc-%wtdOiAbDaJ#zc1|%lW&K%t*vJ!Hjr0zcWsOV~3&sZ5z!)&|+^5 z6ZA+I=~n`Mb8CP3=HeXr)64U2E53SumK<5%SITlj&?Y%vTW604^t1S|_UIc$S<-+0 zF64zdUs!LFBRi;eQH?*C^+w7BoxjX$TRvc!baHpT;^&J}x{U|o%|N;Tr@e9~jpSSE z@09Cnr-jSYd(we4Gg9TLJt+;?jqBaX3TL(@N=0s0B?ojqIVy8U&pUgxmD+O5N3E@v z_s>n2A6{D`|LgUoH-#TwT_V3ZHC1-6tdM@CQQ-Mzt*=%*tJA|h!H4wn!MsqCCEZUk z4-9$o^qy2%Ha^&*J6iOF>+`{%J9l!qX?_2CtkVj+M}z-n06*$FZ!C9BFZCSqz}9&& z^4$I`Gcs{D;aQxmurEWN1y)WBgI;m-d-}Ry-D|MFr}Xa`Ei0xL$+3+C<-$D^<+tbO z${$}|Cf~oh!U})5v{XKPe2zSObUfC#4Ui?1;3ov@@dCPepe%0Q_l~z`YqWKdDveC1 zRocyYqVs0=l2|KUfqAL(m351C%qR~~C;!{Meaj)YOYYn91oRkZ#DCbf+_i3N)N|b0 zw2(Vy#bBH~&jE7f!a*X(f=9QeOKFCeY7ZRM^_zXpcB5TeSB18?zS-~`ec!T$vs4%Q zLI=vUq|cOAr1=nWZN99U6lu|ijCrZ^m%L!)1Jtt(KULWyo$3G$Z9&5Ywo#;qv>9pD z`VM6u`);cy#mGx{mzYtEb5Wys@$N#ob6x`Ug7&wqY><%K8g^`GN7zQCG@8zad|;lK z=Co3yJ6UugosY)6HSz(*w;y(I-x^Z+r$0bb7tr!sBW;>q-R9|Yt5(i3eMq>xa;&FZ zKHhY^cy}qr7GuPxbDP^*R=1KD0=uZ9m%qdV+wfnm!}V`WnPp%C2vLpS*aaOkTgYyji$}`M>28;w0FwgC#6uxIgzb z_sEULIOVz-Npks~KFvxS^WynKJ*5CP{l;|Y{4<_)J|oZHN1gJ*`#<{unxaADH%2;n z4Z`e#b1>)bDwVfRRy7;1-`5vD12Uy39sW`q>3=zj`&K`9Ytu@~^Zc-$F<0KWzi+eC z!#p^-xv){bw#`@M_s1<;I{W?X1#ch#G+KC|DS7PC!4>mz8S>7<18x}49V(aQ6O$x8 z&I>xo&Fkn*p_}it0pH~%`^cKwhtq z3Bc+;Pl`d#`~uR%PaTv!+bIPP4x+t$RH2+ziL{Z+R|k2 zzFpf^5*zA?bxYw`@0cgA-8VoO=grXk+@ZdZVcnXPPX?MkK%V`g>OE+)J!oT0zp_nE zn|Eh7*|{W7&Fg)1Zn)eEyng>6Ik}}w*3Zh6i8b)mm=E6)Fe{S4G3Vij^oy0t_YIe?pP%{hD)y6b8{doW1fnmU};Ydd! zeg`y*2lzhaK~+(hoZeL<|MJ2N`TfP2zi52*{B+DkR!h(9pc|?0I)C0q+M|G9)6ofu zIR^Y4(CKXQnR_`#hjfwp8TsBC|3!ebyz$U@Su!yj>$EU-djsh+=Lz>> z*$%V=evJp)gZ{ubK$FYf_Fc+^>?D6#Kc_@qe`uoo@akgu*Pj^QUtTPi?j0v}(+eae z2759zziqcoKjq^!ep&GB*LZj$QG0>^0&Mf4S@$iF?}6Pr$*6wtAy?N|-gtP5eEaee z`SY7AZYloo(qg&%z$DqRtXzgx#A8jPyR?8zx>@@1J$0jvz|T64kNAI%gL=Uw;8x_L z3WH-xoju$nJ>E}7RwqgQ%u@KS94Jp79xHF1nkipAzX*0AE9K8u>g3<9)GP4%JJppJME_%-kW$8ad;djbC}cVt2n56VXFO|b{~4){l?<3<|U$Jq+pcAHGZ@oPUg z_B|MQ1hDK?foo%a{}Qgbm*xqeFK}D!uo3?M)Pwt#uzffPd;$ENZ3M@%J_abG$^kdv zpT|#^1Jo75fHA-U;4R?afZM?n4D){juK~LO>Z$?2KihO9->INU41Dc~+(K2Qxr0>8TY4ZCq1e|B=}Xu49rV-D{fB1>>6VIF4c_)-DyEy1Jq^4?4>|5p)Q<(27Z&HpdzJJfXE*Eig&2peuygofx< ziVV>!0u0fsdqeb!Agg;2bk!I>2)fwVJqWBf{tp6;x6E>@dnd6X7YVSrud^Z_1l!(Q zB?k$xy|+pZa`<}pSFP!5eZBjO*6+8x-hI9G`_4CeZynq}?|$YSxasn|Xq|xT^3zr8 z_gi1@{(Agx`@d~{vc0#hkGA)=_1X5`Rvuh)UvEqx-q;{Z-@tkXKd-f;_Xm zw@OfemAF>&(TdI7^l(YWa?t!UFxiDGSWh@S#eg<#lrs z>MXCT7tDWCsVM~N0cWQ`{NgBdatetzLEZl}KfZ{DI@@aCLEv@ZDnOl^g+#D_{UtzK z$OnKrpbGE?{?QK3dt?GzfZqaSH1fiYfc?<-fQ>*Z@JpUo=mh$xQ-2KHNLrin4d)PM z0q(#r?4}iloxxf}XEv~yv9hxq?SM`-A4~>y` z&(4&uFD{brE-zE~`obc4=gdrb;^1i6wW3lc4@r~cm~PnD^tx+5zD3;Q0M@ym^5EF- zD}dc}w{dMHeT(2T7yIab{>&`-{_?^b!lzHo#GY&w(kCzUdbWlaKuge1lmVjbDhD0vB~oOnUV7O zlVfiy-aR!!W(`ktw6=(}Z2)d{eQ1L)PXX8PdkOYGzHp=(zS@R5#N~SjU~OL>d^N^M zak`&G1a^X7-?s2E{5!BsH9^INDFCNaf!V3rU;Qufb{Cw-u zZ5ucJna&*%4&M^iag&x!vtzC3G<-{Wa3jYUd}I}7V7E5#|Cj)uKPgr?wmJ^=*Rekb z+VZ8vc9(f$vgMvlHFEhR{H;AVA8r3qg)g3+FR$D`QI25`^4X&@v6jXcYvmo=;Khe^ zfS;v!8yXy@pE9wz?Lrj3;kdCS{a&6fa;!cSzRseoP&cUu)|}a`f8y`cL6%R>mbV`r zFMoV-cC&Eh;c;k7GvOoLe!kN8?7C?Iik7q6BBftZXA7-Hy3N;Y8=I;hu+G0`41@k! zx6byFhd1`H!nA>%?aDT;!&^5iOFn*V^i8*gzdbh!YiZN%uB+pk`KX{a66ovNSlab} z&m&Dukp(l2Y3KFi{vG7R>aJFp(9hkjADg}fJ>=zk;HT=;pj(O;k5<9Yv7b)Mjh@FM zJ&hfm@hAHhmhe*|Tgjni9jq{Gu$%H*VSJCa+sh}!I`oh55`05!nVTv@%7WkvzBBxO zG_?OdUD`@YbY~gXCm4H0r@_C5BR>*-WaiKa3vI^un|RIsLI=Rnu^E3@!lz%h^;25O z?)eQ^HNF*W3me|y`_u)t&ZRFT?fa+Ud=mTM^)K%3NMEzWWsH|?$e^Bn@VT3QO_(ph zr&9N>H{~;Bm4n20!E-CUzB0#2w$5z8=IPku345LyX?21Bm-W*F;pYxM{|q<-AE86b zywPvDZXeDnJY(3(H*BRZd`iYz!hRgS1-&~rslGc3_g@i*JswzUjN2VP5bMXG{3kV_ zcZPM^`Tn$lo>nrEGIG!2?h=gsDUIRX%ymRS8|=B$O^&SclYWIAZ$-G2amIsvn=b%M z*d-}WvT{rV<_$6VCo{g2o9HUXR(V=s-PDfa(f-EE+!(&?HEqCcpWWd7`}oc{mR4X4 zhklr5);P(cVGT%&w2qs88Mn@Kl|2g^ux660@`0|yq7KuT9;4rLbiUc|9ffOto`SPJ zcHzR3x8c~6Xcp41F}cdL?}qW6LB&q8Wkv%wOu?RK@KdedDJ(3MmtJ~FUVZgdsi>&1 zJlFqU?_6A5ESD}_lFOGbOP@Y{n*IIt!pHx206fSCp97XSdZs$bq{;^5#x?xM_d3Bx z?y9jS`9K*#ry5%9B%^vZzzcg67~d&OLcJK;fJxwwmiSJ;)1pNSdFP#XEcAc!$tTjT zT|3MF`n|@^AAkI@<$LC%o10s+zHcmS^M4#$_X36)-|Ye)$b<76P@QG+Pp#jf3>wqh zNv8K}Kul0W+WFo;Ncx$deE}<-C&U-~exRMxNDZ@28+l=LSp$ZbVl6M^rOuy!u=Gpc`le7-YVF?;nW&IfhT$FiRT4!T+%+V-5ObHU6KBx}Jml(Qt>~ZsY&Fc9eeI zo;T(E6UaZ(&35nq8R_>y{qBZ6{4`oX-WmT7hW;OlJkp@9Wc;7kw&|z*{`AvN<+IN| zlg~f@T)z0?3;FWPFRj35{EzSOUEOx-^KFp-q?>&KEBfjC)(G4G+qZ#lbOZGNOtWVv zcL@*eAzA5hNaN@7?YG~`ci(*{-+%wT{P4pM%>uvSw+!PCfB1tG=B7vl!tU(Z5pC@) z)35)c>DOmt+Ig*?>vKDE+PylpmW%{HDauWf+5x39eFDbrrVNyslltSjpM3t=?^L>( z4xJXJr>W52@%+sr9no{IRvV@2Di(A_p?hiBlR%?|V@jsMA z@%8R3qp==&-Q3=CXv1JRxUNR#OztOB$M%&;qswK&$lfw;crO_NiuO%mCTtsNcOHBEQihI;&J^y&%+1it8W{9|9x@*dm2 zylCu6QywH+7L`lg{95qd0GT?jN+yjem(5Emk@gC#NiUP#D@tVc*c?S4)6%a`l2n%^ zNZ(#@Qdt@+$W|P92PT(o-VC1C&fQrqg_V8Ry6JbGA$UibG`o z+JSQK_R(^D%TTGGS1sd_$72xw2$acE_*~ytS1Nl}7RoO8%v+AJS(R4M+PesQu@!`2 zPx=rk$qiEb(-&q3ND*)qeEiKf-zYvHFYvuQ_{r1YGlK6B{9k>W6R5&sn0!`N7@_io zc~aFYUgbv(@_tZNhSc`YRx~m%#*gSFlSfyej#MkTG=4;-L`MeM)(zSk(N0G?d32B= z{YzxivSD)Xwh?lC^I*AqLw{L6zYKYtBZCn>;~6_RP4=xSkUcAMW!JJy*|soEdgTYJ zG!|g~GC!lcV4rohH+MGtzH*=LES%ZC=`?=*_1BW0)kD&-|GEaB>Hm4oM_BxpVQ`;s zhKaqtg>f)HnZM*8<_CGCvM2bXccQ9Kq?I&}99$@4hn1r4_K|7eU&@MF$W69W<}$Ct zd3bl5=ilskzj{L`XSjIb-X3M@6S(v9y$GmO| z*5e1OG-h&d@x(4FO;aG7-hKBss@z%jUw!qJ0aqn6#Yf_!Iw&OIeD&&8!~b7dzWd;V z_hinDVJb|9%{Z8!IjO$j9dmwwN5~&#h2e@u(#m==urf`C^vhB_F{ZXe$!FG$DPya| zzq=RnpxV`?RmjL8J!K-~D&H9i*>Y%Yg&bN_Cj0A(P|kT$+c!q#bur>${?8p9D+g9( zU@!Po*}FVR?wB1dY4M$edunrEUZ&^Do3E)fFCEl}44;p=CdIXz%775$fkW za&Xa?THaE&teaaP2kTMKSLVu|Wf?MiWVFibZ15rTzcj}eesR-*RP6DdhVY~E44Gl#3CdHZP2~gR7-ZT<=g*x~V}Sk3)Alltsvuv<;}eG@$N~8M z-M1nW=}&<_+jxZA6?twdW0votap7`oZK@nylO#u0Cx91X;Jc zTdLl(?rXgF?mIGm>;U2Zo<?QQ@4_ui37H88-K9W!^=GxSH}Kysg^Dn>3L( zxGy;Qg0dqw%~$b6aZZquolG0k$h4xo?*#v=qclIVn7qQg;=3g|J!JQ?bQ6CsOO)M9 zVq{o_FZY~Pd(yKU(&E}7?WuC#9jS8fh9vBxnjkwCMqoebRw`ZGtIp4-1+H7++)BC= z-mNg7*#hJDP72)rR_}+*XZ(-v@Lhgm`qs41y!C#l&T7wE=0`a4gZo!=KjRqYMPxfw z2a{txl*}McP=4maPQ{oH!)kJ~bo#@ByS6OK4!p|xlY>0tH#3Ka%Yl_i@beFQl%9Sq^3awPd1!NzY@3h0iv3%ubQ$+iny|M)&O`ffyTRylUS_^aWQ;ijZ?V zbAT-D*_{ETJ6M!=C5Sa(Gp^99$71dzS`Ca+IrZZ(PRD@+HsiT$}{kltSzu zT>#|E)7ZoQ+>UG+UhapqJHtM}w67EQ)iJ`j@00Ple&55rg^a1{A&=dat-@s3Pwvgf z{>N<;ACXVGqOWeu3)U0v<80yy?jg^7Xsy!3v_U@nxmRJhhfcTg93ASmjp=6Ik;h{~ zTgl#K-1|EM`+kS2eZTLR(G7X-tm4=E1p0Krd(Z_q$NSQeUf2hK=qcRe;r@-;uubT$ z_R7_JNO6x71@3o3=zE;`jz{~JGO*N79^9Ox!cbu$OjFopupe_%JVYKL4XjftFIZ2& z6TW7i2mqf3sk+g?4++uk>x^kOUI%t}@j|-)%)DbBlCR41I%41BDC~h9j=hdUvHx|D z%pcKJ@uqRFERPN?W&O+)_=>MKqY7u>KKQNzE*$P78)l_o@7Ms;4G-x8dnN8eO>{+H zIzGG;_L2{jO>^LT{74_mcPb2qV-9!mxHz?682N>KqW6qbd4aKO<^|;jd4l_jGY!lK zrU`Aw_bKtnsYVX)&wPHvZy5%|S~wbIu`v>RyoLjz*aJ9NCiU;Ec$s4as$5Zy+%INu zS+KI*``~mf{Akz024aXqy!+^2c@Jppoexf#!nu56fbuEMIP^ZNjGJ{x=LO{n<%i}8 z+k6P_(Q@TVF zLi2=eKKOKTei?g*Hfc>W_JNE;y8kc3&^#X0tu^*}j+4iCCSaelc}TY7eu}m+c_{(1Y;wLlv}1_8eR{h5;SziszP21b%a+UU;e+n2N2kfjZG&a;ggnVg z^anpV9^;FGt=+NJ@s{{W%keb{iU-LTs(zU32Ga1Od;1o{jcIGjHF(Sk={~@)jQkxQ z&|Xe&%a)6W3xNV4U*Wm^c`|W8kg|nA__}}D=$U)D5lwxc{`#j64w2^%QEm@oAE74I`D+qyet@vtknZQ0b|amn>5f^c4(*aJ z++8jU$Hc4s6*#VB{I)6Av>8o|=qyVoB+5%iD;&mChkZ6^e`O5QTu$e)mr83qzv%l(QB@|SWjA65Wg0ymPTrhM}m!d?V;0>9LQyjTS61~|Xg zlyo)r&Zh`t2apR`jalLTmws@3IS8PQ*=FD@K)bs?0bKX4(@kH|e+1qE&Hx*MAwUm6 zjZx!#TMnTg1m!{iVodoCxM=o=!|0$nE2jhnW^V@pW-kbM5wEEi>v?w88n*VTP^Uga z$mVka6N@7NvGCCTVme+`w@$6~`J$6LTIzGX>E7A)Y<6$eYzLkJJ^^ThK&lD0XCDJk0c=xP2f6{bwGJTs#tzE$ zIlybc-vC`_n|l5)d_(Df2d45S5=h-n_rkf>%#esCj|Z=q?!JYr4K{T0`CyV-IQa8p1G! zd18I8a2!j=i!<}DxoH?^{!WDx7v_$!;T-IDj{ytHvC*5~+c_f89b@pxmT(#V zqg%6V*WR)mn#xOYc$a-Nm<9hPBW1(f5-H6JzDD+NUy%XD-Wan^RB=49D+%Lv*Q{k@ zeYjqoZi{!`)^z!KxybR=fta5NQ8-o~gn0pLKN;MYa{ibUdF$a(^7V65nuIqW8Y#0! zBw-HIn#P!5H(4^an^fdFw6CLFYD~7S)U=N)I3Fv7Jh+seS1f zeM}6wAzV08C1uTDJG%fh+B^L5@GpSko1wj2<>1nG3cDAylaTHWW04&9UotjYUcILb z-85WW!_=##LIc)0tA!SsvLOWWoFq-qPN|I(>xckS?yX zpNDTxtO=0n0*CzW=zHo*MxBp3hkCa==BbyDg8js3Cs{fYbFG+f)c>Ngvn$RC>v~}5XK6qL`Cs_cxiRi$FbWNT-*+S2P1q-fA&-CfjoAth?&B1vvp6}8B zMPZHA0MJb&g*4C@0C_YKb(-KBMB|#mpPBxxp#7qzKPlKr%92dzhO*GllS5(uP+&p= z-qFu_{+a225;R=Z^dy2WvLa02S{D601JBBmP3Q_+68)U#pPBxTK!c^t<(j+@@Csq% zky!9mQjiIp`*lSB0}nhPvu4d|6emucxRw0>gOL`lq0ult_kjJ1286Amzg@ew65#J6 zYgVsRcJW*rz;z>BFQV5L8Q0428UNGkhGjvU-341;SLCXVVW)d)K+IZAx69;Y!o z2?Ncf-!f-s&4uauQ!NN^zh9Xej)P5${x zr-${o6t>2*hbPPIiB&3Jri`tW9V@D2|LStA&FCppN9L+E5wv-xEj;a?D|?#Or0l(G zy;^TWUOIf>PG$E?+h5z~rga5aXMu0^x|2aw=~nA3xc-K5bL|b+-Ea*O?87s2vZFgzYP5agD zCnw zQ#RPN&!_GD;`w7`%f@AL{_#hI>ph5_+g2-kTiV=i+OWj({OKpps&|(ynyAvoH4n7s z&dSv>8{0E2lCl?tU+U$u>J)FC?9!uhK2ZzoiNhG)3e8y zVlJ`*z9ma!3v3Pg6h)};N^=9`uB92UdryPidy)+9?WbrLK(j~H0+-&_YaurgWpuEhF;Rm|&j>EA0*g-5&h8N3WR<`@`2sbUh1J+a| z!@pj#9ABFt^GErc=7 z!z3iYMS}dCEo%r&>jz*j4;yX9$#nxP7utxE$5>~$wurQXuU@ZPI)%EwQ*lnnVy2Pr zRF(L{K0Xs?RK?I zJg+rqf5~_Gjk4*5J!&_!_x`X=rERXZ>$S3P#(EHzNrpKcq_s~;6vyt>krMCK4gdXx z?RgZ>!dzdU5?PIa|rA_(L@!_z$HrchG+nE7Biy`9MrKQD|-O6?yc7`1Y zwBH)EWv5MUcQ1@ZjtzmmSPJ}GnQYh9S_i~oZJXOtt?A%87qgu!k^9PH8BG8a2y>q-l zt268pOnn>LHrj&!xwmV9@O)5yS?XJcadK0RvU`Sos46p-56g!6MZ0tI7|Wk)5SR|g zw7ZP7^Ex!3bwF4^tG}}R_|1%3U)WYhz^*zJHueD$ALgp^khYf|&?i<;O_bN~tAby# zzCfkC2AtYfEVG71Np_+a_^OSvp>7L1YTvGHF{a|B##YYkC|2+C8}%*1m^n1k%2t@= z!?dXU0-u@ckSQIomHrRNJNISc7*I-(qHS+iSX@vYAnVMr<&nt9MSau z+mDWctyY?NdYEhzb$HRiZDlcR$}4g`t?ZUb3)9i1lk*k-ZmrumqMy%E&yrxH`yb?0 z+Mg$5ZNs@8sqhJD!kKMJ7=P+&=^Nt{rIWOGb5*{Xw=F4?3&+P|{)o0XOBKF&Zl1hw zY>aGO)Jw|q!;~G9{*Gt*Lg1M2x$Q|Ro+o#wO1G}9l^r%|AT6Nj->_~Y2Jc_@Xzl7W z6n*x;!$MQpt|mu$$m4hAVT`-TGS0naZUXED8~2?<9r$`a0Yd!SV|^BEDKT!Yae7-0 z>RKxmH)#MZ{|$TD{?{ek@B+$o0r=zZdJG~2WBm_pDX^MrxO`8!tecUHxfRc5+52$s z_2j6|SYMff@zZjvFz?44Nq{+D7&mD^8m1daYtkjYW822DUdtQ|?blXKNs;$XSLYy6CIoO!?vQGD%3 ziFouLpz}r5-?Ug?xocTZ`S|?QX3d>)uJxnGr^vR&rQjoPK|x!>HOIm6%vj)79<&u+ z4Ose{xK{DDgX}56ywJ*Nh4RSGTD1;T^^fH&}S9khK50>%J)fQ!IYfOVecK?JYA1TFx~=TSfaKpA&a2knRy zkQ)WO#>@siQOZS{72#ZuNO)!}_+h3w6*QdxllK88V_Bz;Cc-&Gc$Lp_zQ{8y2lct0 z_n=yzdHZu7^^Dv=&qdxF=)KB&LwY#z!*uF&tFs|LSXj&_OTJw+=cCS7ozHv+;Dm4> zPzX4yD_nTol%1A4LZ}4}0&f7{0sqeo+9$pa><5Mb9e`VPgd?E4f$Pc@yn~c0kgrK+ z2ymkZ<5}JG0?CArBg6U?=Fdi; zxpFrQ-+aJ$snZPU6D}8z4UwzQPHGZfI5JoU_6jrVPVXSBV4x{SGG6)~kYK+Ka&mJC z)?5x$c;;Y*EEtoBIcKivZHInTOJ%dr9oGe^-DT0(BvZ`66W5pfxh2;BUQ{jFtVTxI{_wlblrwbFlRZ$E!zn956* z#ff!UGPqZu;tj5&BB;X;DGQYQHe^}e8vq~H2-6W*FN}5b7~f5gc9M-#P4nC6>!@$1 z)O3+EcZOph0gRKO{|BAdQJCm&IZIW3XSr`3{M=+&<+;6Y4j@h*kcv)!Ww=u@7rG2{ z>)JnOdUQ+pF>fz>7qyqDK)ZUCy>OfQj53OHas(ip-{)NHs9vV|SZij$Vo<;>7h*f z@WT&TE$!3W;@`4yO7s+Bntd&$uLIkIMUzU*F}E0b&E)m#wA zhB=qQaeMlv;(Ssb?l~`^=avdE4&E@|LZ3>J$`2N4XPETK$9a~aH6_yBSJ~&jo){N; zlyeqq=a-`m=`9P!XDAm+`ZlInTmzQ(o(57oLAg zjcF_YM(|xi-$q54&!Vp@`ah%JGy3_WkFV6Ehy%6#b4Su&-q=AYvLEv!o8~4VO!5Fh^!LLtaOM~1d{~Bj|C$L%@RO;zu?z|x|Zd@81PQM6N`Dd`_@OIy$zSKRVE(^Y>TRk0_D;@1EG72G-HH(bZ&%v zOgu)Ip1*jkMk?|#_Ka`nr^VZow$i3~4vw9(-28l;w^$-JU*NxX7^D4&@8%_Sgx`o{ zc@p-ni^pJ0!>oUCZRC(X(Q+JXJl{JrMZUeXK;hjpljZ1!Dj8H3WwD{9{w zpWI1}`7&&b8NUzT4z!28=77!-3A+E4dQe_c7kTbLq0(m_*_tDznZ8DS+R`Q&bba-V z`g&P*H~4+XRo_0dzYt@5?)X;uMfeWmrC2TF_<(Fm#lQawz;BN`d_|R7^y#Pemt*cP z0rMEX%J!UNh_o4{9eQq(ud*|L`ap%{ed_Mi;Tab29l}n+xhV(r!QTREta5l|q+B{t z+oZk+`zS9R9|B)j5k}kd_Yqd3>9#{uJ3QL~&_-M7{`si^vVKmnvZMa&={fQP#^H%i zas3qRsMpRamfVyc#&Tlc?M{HY{f!<``0zMjWz&Lt)sE=)n)e29ERT8>`>wa@=!Aeq z0t}Zr6ZIC3K@;Scw*bnN;Q)2oCLMASi@ZQPhY!#gbG=>1s~DNMh~WjI9>WKOQ!@|! z-9>XaR}m(|W*nBG3d9|Wa7k<+;H-Xe(W*(~6y)W7z{ddf2!hvpfds(O!McACb>>Iz zJ^B36VRH4k84AxI877Oyr$bh{Q776Dw6O_<-@eo<%SWC)&)dR%^B?n|Qr= zTmt^60+C_|?)Y z0bqi@Pnz`S_bCkk42A34O$ znZ=cN=l7-62HdF*yxUV{f48?HJ}0jt$inX`;!g#83^aq%G_s#*D=8^--8ej=RceCr zM)K(onWOe6y;@OIzlF%64kd$|@0QZtbYBMMS<`KE(q~T0AFr3!W4~SY<<`IeL!I^JJINY(*E2 z1PR%3xh>=&G12>J-cC*N$UmGi5c6mm5@O`8qd&)|ZEpN^_J}t9;g9b{xP8^6(FaW> z6)`}>o%-wNHf;6osHz~cUb{^8|Kmgbgdu}x#}`F-BLM%jwY{{`spIRr_`?G-y80b3`zG*5hWgqGQum*2MWZJVO`;pW($``ibzG9F^MQ-&XdZ5@vk zEDF}9yYSk*tJCjBlO0+T4I65%ueS(kI#kn^FGXMuYKS>{s7LkdbrnmRK}v1Mcyd?~ z0_HcJ>1_&?@lq^TBGdf;I9ImK-CLgwq=#3k7tZ5cqp(I$m0Lb1N}3EdnY0v?&1h?l zW8aDxnVk}2dN(DMa&Y+y_aorQ*|H#?4o+08@g-vZ-13Lx0^V7Kgv74RXv_M8nvGi%}_N+ z<+f_DUds|!6T$T!MBIj=VQCG@uF+;UDUE3Y!!55kAv18yyL!?q@m|r+EB3^!(R_>^ayBvY{SFt5l2882E;7AYR?>ZDWmdLo*QNO=Ve{U;( z6m#V*mknO=J1*j%N`gfpOtq}-3_pC~OMO~Y0;Pr&gS^h2V^>tKWvq=uK1k%M*&k(0 z!7~yl2gA%&sga=SokZ&BSQ2R{u<5`p-ksuNc?vLdCNb=!<#Qn?XQrP2ihdZoY ze&L}<=NsjsN8X*Ek2ancr}J`a)|f6t12p*|8dsAWKE|y8GaIjdt**A@T^8Qhc;QCZOQ-E#+gdKq z4R2j|;NqOln{Ju<8_EX9xo~SIUcHy=e0QAB^sx%u4PKdBoI-5;{RvBpjZN-+ zT5ypUlVdny`fL_*22Y-Mj>ZB`&fJnQ4OXEq2-FNmx)Cmf)0J*fb_|~>i5wx)qVZc9 zt)=05(h)AQAgwhpV}DTfY*VAqO`)x&(s`qw>|B+B=-@f49#X8R`p(=|wTvM*^YI+t zl@@3nlruq3Ng+-hG?=rueK;1`ARl*ah@2Hw`FwBJ9_8t4nR+j#Pn{p-td28vSx7eu zZQ^v*T0FZ>G?Yg6Bm|R*w{W%RXjW0gxzfnfSzS~qg4pxQk*v!MOFYur8+mR~@Zom` zs!|;i&nstJuhMjoTa%1v;m8*cX|fnJXYM1lx;4i_iO8K2wt`XUXHHk*f?-3+%Yf^P zf*^-(c0zT`y0F-FR^WN%x?6im5^}(9e`i{veyQ@utwpPmR4q`uVcJR)0$vh%I)_){ z?s)B%a(2f_R^l7gxeAO{38O;UQ0F04$q&A=&h?(4O6>r(GPCoEJLp|+FEr+L2UDLM z=AHkOJYRD1JkBNHS3zGwgVcj6L}xBe`jM=AP&Ic9ZD*?nxKdJScZAmVDWFJVspR5g z;0*1Q(Ut;@1zDaI6;`!AHwehK6t>4fWt7oCqen@+L;)yw`Rcz|R3u>|UCe^@Bg<#! zoPEB_c_EIJeLqUeJO+Qhmm?E0P5$&)kQLCoZ;ta^Sg$Gk=wnVA+Gd#+6*Ulu0oh}` z%JL<^wXr8XA_MgYg>Yi2oAVOfuCrgCv>SMS(`o6kM0}|NLocUOF2)Pii8%2pz(tXW zEa`UIJ^>PpQkD-or3KPn@tTJ?B%t1tVOfqB->V2p>gi+C=HZsn$rAfq5IbD7?=g$v zmQ$rTUE!BcU07~uhqFB0kym$K6d(u`0s zobZjlVkVMc=h+*p4feq*n=HK+e6hlNe?FJ0XgqzdW){@ONsy$`Hdf4_SIA_Y>yaOt zWdeyK0!}cS2}tT{_GVR2v>8jK_ddWhzbj@By-}Z$z z>mhgQBA$BkxmoKS?ddP^ww&TWZrCl(H*<}WsrSF*d}(SO$7UK*G?KrTUM;Y9B->~fHzDr2-IFM=s zx^5oVqNcaDlQlqAQjZZ@Vk@gx4W8?~N~C=)aww7*D?P>tcjzoTiiF$ss7-#hetOG= z2eY!$10Ri$1>y4_!^<}qv07{0x;O0oFb9%ox%PQhtKJ+@-p+uI$u zUa@?~VfC@VrfP0Nrfc{|H)Azij`nMMERv^%%r%{|_c-265-4`MmU1sC`0CDo!?i1F zdy_NP6GRwX(qe9JZ8x0{Tn}wqF<H*I5&50C>c;rsSs_ldsR}UkW0;WgDIb@ zdS|NPottzx)KeqBm5h->Y}1OoN(p^%Uif8077ocO#*EoN40`y^vv^td1bL?CTi z%-q)YT}yygdON#&&($M=U(7GPUS&}d;s3{twI}Xb1m2)Dmn{k7W>D2PE*(Rls!yJW zD?wXe9Ughx?B?iLKkH<1WY`*|rHRXRw!jMb$tJi_XV_8Zc(TT^;vojB_V@VhCFh@~ zN~2|2#<&$|nGU`%yWY{Ws{DW&O(xiBxmWP#GUK`8C&h8m(dN>o(KTitVg}(kZ`Dt9 zn7)p_U-gU%WOuDsUi&ms{hI7}z-rzshXB<{ZJIo}%@&t07u_dDD}do&A=S_@(eUXS z*~xqSjE7G&l0qD=I0aC_Xta-I*T@l#B*At1m9|cn3{4hNi%=>Vr836a((!mQ`OsNf z>}M+^0$8>+I((5}$;{EE0vt0e{^9Knrk-3oo1J)70wVEIfj~lltHbOpC2e|&F$u9w z|B{azaWWnv{LA`dI78P3#We8?ehB6tvHYa&tm zgk`N@Y7N9Ig71HRnd8thT66nV9ldGdV|Ok>rR>w%bsVT)QDN&iyD_C|p#B)VnG{2t zHB2)DIQteB)wrXyXh>U2Lb-h!K@<_2<%jzJC<=V*R`><=HqGekf<;Ob$4o(?oIi|L0YD+hqU-Kk+ ze6HEkKWi#6o4HP=?QA86|MYbu^_$)Vvql^sWG&-WR5*H;q%C=Vda-8tQMX5jSFeB3 z6j8J0NatS3Ou-WWTFWJG~KKP>W}3?`GT<37khQ0@oEUD&Krko3(>a#ei@{_sQFYW1Z=8W z0=Ceyhae9w;%qW|byDZyNJvkiGKd#@@rJuDTc$9Rf(}dELk=W;#@V2HQcY0yYkT8)LBeP+-mARW%v-{R3q00{kwh4=F;)Oi zrkwUk5Py}ti5Nb(G3$3MJBGTHSQVqd==~XYyu^>}E&68kWJKSKYqm)kPe(CZL(Jjj&z2A4wd8%u;w}BO)8>fEnRHa7 z1{9g%*EMm0G0sv~yax3fx)35WHl1?iNnU+LNmCH>yVR?LP9h}4FYP70y)vDfsjbUV zK>Je=D%R-aVXkHx+E0uPQkT({%AqS}zTZxtpXoWrAamK)yeqpai23MneAF0SAs%0K zY5i;I^f{cJpl7#lGmW*=C}7WpUL7=wsH5n0Bo1$B5QBna<|!CaVjilmMGnpHxHHk`VQVtpEc*Daa(S&m2$3;u(ES*F)#0*hVbdd$cJn~4 z$DI5FfLt?ZWSo2LlQ1pt*gCuUiCQ#ps4m;8+LiBWDE^H+a)LjhYB5cwWju&b!%G}V z9M0OgNE{7zRD+3c;fzkVu3&FVT*u;%A81CD6nEF`KTDF-0Pj=#O#>oBg%`Q{vNRkV zsZzu48+>HjUEJ0?Us4^)qrWM;rrl*~JUYA8%|KyNeBzM=US(L1!7$t@U$1ROl+z?v z)WgyEdsdAkj`uDaNe4unjx^9l-D!kd)wHDJw%k&%!xyKGhMEzsKBbPPSyOV9qyZw# z)bKF=P@?3sW~ccIxJhe_(76t^lsP^$sCR)RYLDauWc4?SM#{NfBO4r(&6~KOcm_TV z9UD-_7xg{u_2+Hrs(S!=<`db%o6rLH$i~CG`9@0MmTc(Bq(rtN*XvFUI!Y3)5b55c z8J&$WUFtrPNVc!9Ny{v`p}F5Mj34%vgg8CZj4=kJr1=s4XtPG+=gVJycS+7iilKBn zZ00wO8ZlGq^5hZ$Ii6(h?-d`t$+j8^{030H7Bp^U1jtY~@U8km*^PINdR@oK-5f+1`coF5xJmU>SBeace|qEFeEP zFp=i>F8_^m=M7Cz^W?(Xp(+*EHaGjJJbAPbc(J)TXXGUowYIj7&AhG$CO<7JbMVgS zkx!=g5+`(T^p*!M?+d!UXx>#E-`P%LAnDKy-w&fkOxN$6eeUB&ZE#0S=FF=bbYAb> zYfciN{BjK|HsFjCd!^))(y9qSfwzCB6LNA-61MNk-NmN3SDPV>WTyNYwSSAgiN z4PA-pL0h&29HRloE6aw!35TVVgWHZ)yyY&BA%W0d0D5lDYCo{&AR50ifH~9bU9YvA zBEYxez}~2jo9G&H=+0aUKS}v>OYNsmhi-dbW6Q-zgV03=;KrEyi)c!t@ra(4bEZ5o zCw5CPaJ`w?yI^E3d+r@iY1FWiwC+Y;PxF)M(d)#3>Y+J7ZE1r?B$KDfFE|CrGWs1H z>F&1rt4sW50n@{%)$4+KzO-J7Sr_h&qmI;UihoPEP-YJp8})2^%OCHfr_=XH8D(iLk*VWx?$$W;`Q-QLfCb3Xf8cCe~@cfYRj7J^_4@Xrccx*uObwO%t?esGW3X2o}RV#-eMe?DLIwJ#lH3X$J^`6mn&5a z8x2%yMu3vS8MU^zv~bi()}SC|l?0)}6JbN;_#AEJ6Mme{J<8UOB$>4W{>s@cq!`BEU z&Gqi5=wvRzC1op3Pp{>&N32f)o+`f(0>5jLf;`%P4-6SJsFx(a+k`1O zBQ9<*yYADc&BQi+bg>bZQWTMzdhZ06D^@wxm|PPP7~gs6u`WBTA}A99>bHGKcU^pL z9{R)WL)wjpGsYjAv*zK!A+K`+-%3rSkO7fugn?~S zZf=)jZe^h*&sck3S42ZX@X6O18D+DE#o8X=NrY)XjqP4*?~VSc0nCgS8D$tcqw^s8 zcM1A9qYwN`1SSUhx5@8FK0ZF?n>KG|qWL>FH@A+ZwzfQB4=jUq{;F&KJFxUuCd3>N z7p@-6hyB4fkUCK2@3dp}>eXt#etsnb!So|_p$@3) zXZ_62@8|Eo@@L{?pmsoAP$xfzAAJwpv?+U_`hQh@sQX88Lq6EIeifD;&Shz z*fE<2YQujsW-!cxd4FRG_8URo|CR6W^=YpS8%BmlM6@$PLwTYePft&wbE_yDx5viv z($dp^)zgH|-3p@m1CPhsj6Qv)%F4=qEKcR-UNm-pekG=Ra~$aU?2kPT%YJ4;|HY-2adVKLqQD!UQp6Z7^m-Jy+SSjwkBT z^seW9Y3==oE=UlJBT;b!eIO`bm|qshefdW{K|c5mF+=?N^hR@G<$|=8`@ags&mTvk z;s)A2ZF9pvaQzH^h!0{njcj5HViom$=}D(h8?ZPM6*nQLTmFF^>iAjw5IbtaJ5hKA z>3zi!o5K-mo3OaS1ZDon7$i)uFrDA)i^AlMV&lgu>U~~h zQ}%xgqbR))lPJ8x=--0W_8Z#JX!5sD*Y+U*s z@gXP^>gsP_6~rp)8;)Co^Mjz?U-1ofd~b{tg|R<$IQPN%`&VG)mjPvpnpgXSYoK+Y zF4(tzCaxeal<{xk7G@{ZkDaCVwV%fJtJuCtn5==)csvG<#{+=Z0{{)?O#sNioCAOu z%o*tYA(%tK=nr~l5db=RaS`S;06Jh!0RUw~83Cw*xy1kH9QZ8;0G2c0Hx9jvB#^_; ze={+lG9frPkj{s<3V!q1gV~AMjrjrd3+ke8e)`*QoWJ~t`4#i?Uw()8w5aHVpwHj; zw8-ek4<^rb>!xFOvH1UsRz zeSXD#C@5o~_lAD;y`X{I`++{|kAHup&iC5m-?(ET>aGNAqkrQr7Stgu9?;_^q5reP zdlsU6?Ywd&@AK!+!j9LzM-%*?TwN3cye9$m2*Ysk;zdzx{h<%Xx*}RRtuKaxf`WhO zhxb^1GNnQNbHf^S;2FB$1OIE_PyFp5=y!O|3C|TU8T@=0O;`?fi#lh8du0CqLHOH2 z(C>nKZ+`v!Ybn8ZY;7P&$G#zE{sh}W(C_@`j2It34@MJ~qwACJt{36ix~K&GE_hA| z_3$TQdIv%iYzKbXf_}&3^4Ebr#OObo3$_E4FU;?le183+Fk*H4lMC|!Jfj!I%da1{ z5q8Eb?5tjt41T(>7=UsiF8+GhyP$sl?}7S5nC~%p@Ei`)$)988Fo(87tXL9^*}nRK zpx+^{;Mt!rpTgL{9*7I}5$Hq6%inHLx2W+4@(KDK`c4=oQSHF*6JhUA_w+3d(}k4@ z`kg=C1*x$!A=-C4W9w$_xCleg>cmX~P^!NZiV1NnZ -#include "../src/version.h" - -IDI_ICON1 ICON DISCARDABLE "app.ico" - -VS_VERSION_INFO VERSIONINFO - 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 -#else - FILEFLAGS 0x0L -#endif - 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 - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x0, 1200 - END - END - +#include +#include "../src/version.h" + +IDI_ICON1 ICON DISCARDABLE "app.ico" + +VS_VERSION_INFO VERSIONINFO + 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 +#else + FILEFLAGS 0x0L +#endif + 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 + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0, 1200 + END + END \ No newline at end of file From 778c5289d3b3e57ec4599b7bb629c446049d5d87 Mon Sep 17 00:00:00 2001 From: BenDr0id Date: Mon, 16 Jul 2018 11:51:31 +0200 Subject: [PATCH 08/97] Fixed config file storage --- src/common/config/CommonConfig.cpp | 1 + src/common/config/CommonConfig.h | 2 ++ src/core/Config.cpp | 3 +-- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/common/config/CommonConfig.cpp b/src/common/config/CommonConfig.cpp index 4a6ff01a..6d318aae 100644 --- a/src/common/config/CommonConfig.cpp +++ b/src/common/config/CommonConfig.cpp @@ -275,6 +275,7 @@ bool xmrig::CommonConfig::parseString(int key, const char *arg) break; case CCUrlKey: + m_ccUrl = arg; parseCCUrl(arg); break; diff --git a/src/common/config/CommonConfig.h b/src/common/config/CommonConfig.h index 30406dbd..3d2ae723 100644 --- a/src/common/config/CommonConfig.h +++ b/src/common/config/CommonConfig.h @@ -55,6 +55,7 @@ class CommonConfig : public IConfig inline const char *apiWorkerId() const { return m_apiWorkerId.data(); } inline const char *logFile() const { return m_logFile.data(); } inline const char *userAgent() const { return m_userAgent.data(); } + inline const char *ccUrl() const { return m_ccUrl.data(); } inline const char *ccHost() const { return m_ccHost.data(); } inline const char *ccToken() const { return m_ccToken.data(); } inline const char *ccWorkerId() const { return m_ccWorkerId.data(); } @@ -119,6 +120,7 @@ class CommonConfig : public IConfig xmrig::c_str m_fileName; xmrig::c_str m_logFile; xmrig::c_str m_userAgent; + xmrig::c_str m_ccUrl; xmrig::c_str m_ccHost; xmrig::c_str m_ccToken; xmrig::c_str m_ccWorkerId; diff --git a/src/core/Config.cpp b/src/core/Config.cpp index 3fc8f030..5d717fd2 100644 --- a/src/core/Config.cpp +++ b/src/core/Config.cpp @@ -121,8 +121,7 @@ void xmrig::Config::getJSON(rapidjson::Document &doc) const Value cc(kObjectType); - std::string url = std::string(ccHost()) + ":" + std::to_string(ccPort()); - cc.AddMember("url", StringRef(url.c_str()), allocator); + cc.AddMember("url", StringRef(ccUrl()), allocator); cc.AddMember("access-token", ccToken() ? Value(StringRef(ccToken())).Move() : Value(kNullType).Move(), allocator); cc.AddMember("worker-id", ccWorkerId() ? Value(StringRef(ccWorkerId())).Move() : Value(kNullType).Move(), allocator); cc.AddMember("update-interval-s", ccUpdateInterval(), allocator); From 86677da8f40d830df4c5a7a424d789c62c644f21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Mon, 16 Jul 2018 17:44:05 +0200 Subject: [PATCH 09/97] Update version.h --- src/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/version.h b/src/version.h index 6a557602..89ee2614 100644 --- a/src/version.h +++ b/src/version.h @@ -35,7 +35,7 @@ #define APP_DESC "XMRigCC-AMD OpenCL miner" #define APP_COPYRIGHT "Copyright (C) 2017- BenDr0id" #endif -#define APP_VERSION "1.6.5 (based on XMRig)" +#define APP_VERSION "1.6.5_beta2 (based on XMRig)" #define APP_DOMAIN "" #define APP_SITE "https://github.com/Bendr0id/xmrigCC-amd" #define APP_KIND "amd" From 7137a515832ad9845a49a1d64495728eb3e37044 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Mon, 16 Jul 2018 17:44:19 +0200 Subject: [PATCH 10/97] Update CMakeLists.txt --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e15bca71..3962336f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 2.8) -project(xmrigMiner-amd) +project(xmrig) option(WITH_AEON "CryptoNight-Lite support" ON) option(WITH_SUMO "CryptoNight-Heavy support" ON) @@ -298,4 +298,4 @@ endif (WITH_TLS) add_executable(xmrigDaemon-amd src/cc/XMRigd.cpp res/app.rc) -set_target_properties(xmrigDaemon-amd PROPERTIES OUTPUT_NAME ${DAEMON_EXECUTABLE_NAME}) \ No newline at end of file +set_target_properties(xmrigDaemon-amd PROPERTIES OUTPUT_NAME ${DAEMON_EXECUTABLE_NAME}) From 8e7d903b45fd6a020d0b533d6bce2471c597f3da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Tue, 17 Jul 2018 09:15:23 +0200 Subject: [PATCH 11/97] Update README.md --- README.md | 150 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 77 insertions(+), 73 deletions(-) diff --git a/README.md b/README.md index 8c2d1adf..cf9054dd 100644 --- a/README.md +++ b/README.md @@ -1,93 +1,97 @@ -# XMRig AMD +# XMRigCC -:warning: **You must update miners to version 2.5 before April 6 due [Monero PoW change](https://getmonero.org/2018/02/11/PoW-change-and-key-reuse.html).** +:warning: **Confused by all the forks? Check the [Coin Configuration](https://github.com/Bendr0id/xmrigCC/wiki/Coin-configurations) guide.** -[![Github All Releases](https://img.shields.io/github/downloads/xmrig/xmrig-amd/total.svg)](https://github.com/xmrig/xmrig-amd/releases) -[![GitHub release](https://img.shields.io/github/release/xmrig/xmrig-amd/all.svg)](https://github.com/xmrig/xmrig-amd/releases) -[![GitHub Release Date](https://img.shields.io/github/release-date-pre/xmrig/xmrig-amd.svg)](https://github.com/xmrig/xmrig-amd/releases) -[![GitHub license](https://img.shields.io/github/license/xmrig/xmrig-amd.svg)](https://github.com/xmrig/xmrig-amd/blob/master/LICENSE) -[![GitHub stars](https://img.shields.io/github/stars/xmrig/xmrig-amd.svg)](https://github.com/xmrig/xmrig-amd/stargazers) -[![GitHub forks](https://img.shields.io/github/forks/xmrig/xmrig-amd.svg)](https://github.com/xmrig/xmrig-amd/network) -XMRig is high performance Monero (XMR) OpenCL miner, with the official full Windows support. +[![GitHub release](https://img.shields.io/github/release/bendr0id/xmrigCC-amd/all.svg)](https://github.com/bendr0id/xmrigCC-amd/releases) +[![Github downloads latest](https://img.shields.io/github/downloads/bendr0id/xmrigCC-amd/latest/total.svg)](https://github.com/bendr0id/xmrigCC-amd/releases) +[![Github downloads total](https://img.shields.io/github/downloads/bendr0id/xmrigCC-amd/total.svg)](https://github.com/bendr0id/xmrigCC-amd/releases) +[![GitHub stars](https://img.shields.io/github/stars/bendr0id/xmrigCC-amd.svg)](https://github.com/bendr0id/xmrigCC-amd/stargazers) + +![XMRigCC Logo](https://i.imgur.com/7mi0WCe.png) + + +### About XMRigCC-amd + +XMRigCC-amd is a fork of [XMRig-amd](https://github.com/xmrig/xmrig-amd) which adds the ability to remote control your XMRig-amd instances via a Webfrontend and REST api. +This fork is based on XMRig-amd and adds a "Command and Control" (C&C) server, a daemon to reload the miner on config changes and modifications in XMRig-amd 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. GPU mining part based on [Wolf9466](https://github.com/OhGodAPet) and [psychocrypt](https://github.com/psychocrypt) code. -* This is the AMD (OpenCL) GPU mining version, there is also a [CPU version](https://github.com/xmrig/xmrig) and [NVIDIA GPU version](https://github.com/xmrig/xmrig-nvidia). -* [Roadmap](https://github.com/xmrig/xmrig/issues/106) for next releases. :warning: Suggested values for GPU auto configuration can be not optimal or not working, you may need tweak your threads options. Please fell free open an [issue](https://github.com/xmrig/xmrig-amd/issues) if auto configuration suggest wrong values. - - -#### Table of contents -* [Features](#features) -* [Download](#download) -* [Usage](#usage) -* [Build](https://github.com/xmrig/xmrig-amd/wiki/Build) -* [Donations](#donations) -* [Release checksums](#release-checksums) -* [Contacts](#contacts) - -## Features -* High performance. -* Official Windows support. -* Support for backup (failover) mining server. -* CryptoNight-Lite support for AEON. -* Automatic GPU configuration. -* Nicehash support. -* It's open source software. +**[Find Help/Howto](https://github.com/Bendr0id/xmrigCC/wiki/)** + + +**XMRigCC Daemon(miner)** + +![Screenshot of XMRig Daemon (miner)](https://i.imgur.com/gYq1QSP.png) + +**XMRigCC Server** + +![Screenshot of XMRigCC Server](https://i.imgur.com/0Ke9gIg.png) + +**XMRigCC Dashboard** + +![Screenshot of XMRigCC Dashboard](https://i.imgur.com/VwJaf26.png) + ## Download -* Binary releases: https://github.com/xmrig/xmrig-amd/releases -* Git tree: https://github.com/xmrig/xmrig-amd.git - * Clone with `git clone https://github.com/xmrig/xmrig-amd.git` :hammer: [Build instructions](https://github.com/xmrig/xmrig-amd/wiki/Build). +* Binary releases: https://github.com/Bendr0id/xmrigCC-amd/releases +* Git tree: https://github.com/Bendr0id/xmrigCC-amd.git + * Clone with `git clone https://github.com/Bendr0id/xmrigCC.git` :hammer: [Build instructions](https://github.com/Bendr0id/xmrigCC/wiki/Build-Debian%5CUbuntu). ## Usage -Use [config.xmrig.com](https://config.xmrig.com/amd) to generate, edit or share configurations. +### Basic example xmrigCCServer +``` +xmrigCCServer --cc-port=3344 --cc-user=admin --cc-pass=pass --cc-access-token=SECRET_TOKEN_TO_ACCESS_CC_SERVER +``` -### Command line options +### Options xmrigCCServer ``` - -a, --algo=ALGO cryptonight (default) or cryptonight-lite - -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 - -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) - --opencl-devices=N list of OpenCL devices to use. - --opencl-launch=IxW list of launch config, intensity and worksize - --opencl-affinity=N affine GPU threads to a CPU - --opencl-platform=N OpenCL platform index - --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 - --nicehash enable nicehash support - --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 - -h, --help display this help and exit - -V, --version output version information and exit + --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 '/' + --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 ``` -## Donations -Default donation 5% (5 minutes in 100 minutes) can be reduced to 1% via command line option `--donate-level`. +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. -* XMR: `48edfHu7V9Z84YzzMa6fUueoELZ9ZRXq9VetWzYGzKt52XU5xvqgzYnDK9URnRoJMk1j8nLwEVsaSWJ4fhdUyZijBGUicoD` -* BTC: `1P7ujsXeX7GxQwHNnJsRMgAdNkFZmNVqJT` +## Common Issues +### XMRigMiner +* XMRigMiner is just the worker, it is not designed to work standalone. Please start **XMRigDaemon** instead. -## Release checksums -### SHA-256 -``` -d1caf88eab58d8bd905404c0f75a67435755bc58914217692b68d36f34ad0df6 xmrig-amd-2.7.2-beta-win32.zip/xmrig-amd.exe -9012af9e9bd24da4e663f514f5351d1be732845b4a9797327b93f8596e66d293 xmrig-amd-2.7.2-beta-win64.zip/xmrig-amd.exe -``` +### Windows only: DLL error on starting +* Make sure that you installed latest Visual C++ Redistributable for Visual Studio 2015. Can be downloaded here: [microsoft.com](https://www.microsoft.com/de-de/download/details.aspx?id=48145) -## Contacts -* support@xmrig.com -* [reddit](https://www.reddit.com/user/XMRig/) +## Donations +* Default donation 5% (5 minutes in 100 minutes) can be reduced to 1% via command line option `--donate-level`. + +##### BenDroid (xmrigCC): +XMR: `4BEn3sSa2SsHBcwa9dNdKnGvvbyHPABr2JzoY7omn7DA2hPv84pVFvwDrcwMCWgz3dQVcrkw3gE9aTC9Mi5HxzkfF9ev1eH` +AEON: `Wmtm4S2cQ8uEBBAVjvbiaVAPv2d6gA1mAUmBmjna4VF7VixLxLRUYag5cvsym3WnuzdJ9zvhQ3Xwa8gWxPDPRfcQ3AUkYra3W` +BTC: `128qLZCaGdoWhBTfaS7rytpbvG4mNTyAQm` + +##### xmrig: +XMR: `48edfHu7V9Z84YzzMa6fUueoELZ9ZRXq9VetWzYGzKt52XU5xvqgzYnDK9URnRoJMk1j8nLwEVsaSWJ4fhdUyZijBGUicoD` +BTC: `1P7ujsXeX7GxQwHNnJsRMgAdNkFZmNVqJT` + +## Contact +* ben [at] graef.in +* Telegram: @BenDr0id +* [discord](https://discord.gg/r3rCKTB) +* [reddit](https://www.reddit.com/user/BenDr0id/) From 36e11e1cfdc3b86f7cc5cbf8c9f45bba403fec66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Tue, 17 Jul 2018 09:19:51 +0200 Subject: [PATCH 12/97] Update README.md --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index cf9054dd..550b2ce3 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,9 @@ -# XMRigCC +# XMRigCC-amd (OpenCL) :warning: **Confused by all the forks? Check the [Coin Configuration](https://github.com/Bendr0id/xmrigCC/wiki/Coin-configurations) guide.** +:bulb: **This is the AMD GPU version of XMRigCC, if you're looking for the CPU variant [click here](https://github.com/Bendr0id/xmrigCC/).** + [![GitHub release](https://img.shields.io/github/release/bendr0id/xmrigCC-amd/all.svg)](https://github.com/bendr0id/xmrigCC-amd/releases) [![Github downloads latest](https://img.shields.io/github/downloads/bendr0id/xmrigCC-amd/latest/total.svg)](https://github.com/bendr0id/xmrigCC-amd/releases) @@ -11,7 +13,7 @@ ![XMRigCC Logo](https://i.imgur.com/7mi0WCe.png) -### About XMRigCC-amd +### About XMRigCC-amd (OpenCL) XMRigCC-amd is a fork of [XMRig-amd](https://github.com/xmrig/xmrig-amd) which adds the ability to remote control your XMRig-amd instances via a Webfrontend and REST api. This fork is based on XMRig-amd and adds a "Command and Control" (C&C) server, a daemon to reload the miner on config changes and modifications in XMRig-amd to send the current status to the C&C Server. From bfb76346660cf7ff3121935bc06bac8daecdc4b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Tue, 17 Jul 2018 09:20:59 +0200 Subject: [PATCH 13/97] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 550b2ce3..3c66c5d3 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ :warning: **Confused by all the forks? Check the [Coin Configuration](https://github.com/Bendr0id/xmrigCC/wiki/Coin-configurations) guide.** -:bulb: **This is the AMD GPU version of XMRigCC, if you're looking for the CPU variant [click here](https://github.com/Bendr0id/xmrigCC/).** +:bulb: **This is the AMD GPU (OpenCL) variant of XMRigCC, if you're looking for the CPU variant [click here](https://github.com/Bendr0id/xmrigCC/).** [![GitHub release](https://img.shields.io/github/release/bendr0id/xmrigCC-amd/all.svg)](https://github.com/bendr0id/xmrigCC-amd/releases) From 795dffea30e2f44517b7ca00766f4ad4b32cab4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Tue, 17 Jul 2018 12:37:38 +0200 Subject: [PATCH 14/97] Update README.md --- README.md | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/README.md b/README.md index 3c66c5d3..56057c91 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,47 @@ xmrigCCServer --cc-port=3344 --cc-user=admin --cc-pass=pass --cc-access-token=SE 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) or cryptonight-lite + -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 + -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) + --opencl-devices=N list of OpenCL devices to use. + --opencl-launch=IxW list of launch config, intensity and worksize + --opencl-affinity=N affine GPU threads to a CPU + --opencl-platform=N OpenCL platform index + --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 + --nicehash enable nicehash support + --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-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) + -h, --help display this help and exit + -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. + ## Common Issues ### XMRigMiner * XMRigMiner is just the worker, it is not designed to work standalone. Please start **XMRigDaemon** instead. From 907d2d4044a6ca6649251a6c02df003846afe669 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Tue, 17 Jul 2018 12:55:49 +0200 Subject: [PATCH 15/97] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 56057c91..db4f163f 100644 --- a/README.md +++ b/README.md @@ -126,11 +126,14 @@ Also you can use configuration via config file, default **[config.json](https:// ##### BenDroid (xmrigCC): XMR: `4BEn3sSa2SsHBcwa9dNdKnGvvbyHPABr2JzoY7omn7DA2hPv84pVFvwDrcwMCWgz3dQVcrkw3gE9aTC9Mi5HxzkfF9ev1eH` + AEON: `Wmtm4S2cQ8uEBBAVjvbiaVAPv2d6gA1mAUmBmjna4VF7VixLxLRUYag5cvsym3WnuzdJ9zvhQ3Xwa8gWxPDPRfcQ3AUkYra3W` + BTC: `128qLZCaGdoWhBTfaS7rytpbvG4mNTyAQm` ##### xmrig: XMR: `48edfHu7V9Z84YzzMa6fUueoELZ9ZRXq9VetWzYGzKt52XU5xvqgzYnDK9URnRoJMk1j8nLwEVsaSWJ4fhdUyZijBGUicoD` + BTC: `1P7ujsXeX7GxQwHNnJsRMgAdNkFZmNVqJT` ## Contact From 86ca2353cdd0d4238802c44b30f2a1d5ad6a4ed6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Tue, 17 Jul 2018 15:06:37 +0200 Subject: [PATCH 16/97] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index db4f163f..8d01d4d1 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ The modified version can also handle commands like "update config", "start/stop GPU mining part based on [Wolf9466](https://github.com/OhGodAPet) and [psychocrypt](https://github.com/psychocrypt) code. -:warning: Suggested values for GPU auto configuration can be not optimal or not working, you may need tweak your threads options. Please fell free open an [issue](https://github.com/xmrig/xmrig-amd/issues) if auto configuration suggest wrong values. +:warning: Suggested values for GPU auto configuration can be not optimal or not working, you may need tweak your threads options. Please fell free open an [issue](https://github.com/bendr0id/xmrigCC-amd/issues) if auto configuration suggest wrong values. **[Find Help/Howto](https://github.com/Bendr0id/xmrigCC/wiki/)** From 86295b0e63078f0e779a6822dc956cc687a3c145 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Tue, 17 Jul 2018 17:03:12 +0200 Subject: [PATCH 17/97] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8d01d4d1..4a919d02 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ GPU mining part based on [Wolf9466](https://github.com/OhGodAPet) and [psychocry **XMRigCC Daemon(miner)** -![Screenshot of XMRig Daemon (miner)](https://i.imgur.com/gYq1QSP.png) +![Screenshot of XMRig Daemon (miner)](https://imgur.com/a/3kDbspd.png) **XMRigCC Server** From c2af4446adc6428bf96aff88cbc8bcd085843589 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Tue, 17 Jul 2018 17:04:38 +0200 Subject: [PATCH 18/97] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4a919d02..12882dd8 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ GPU mining part based on [Wolf9466](https://github.com/OhGodAPet) and [psychocry **XMRigCC Daemon(miner)** -![Screenshot of XMRig Daemon (miner)](https://imgur.com/a/3kDbspd.png) +![Screenshot of XMRig Daemon (miner)](https://i.imgur.com/48uGuDI.jpg) **XMRigCC Server** From c0398038a943e5aaeadf13de48fac9548b31f7eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Sun, 22 Jul 2018 21:38:31 +0200 Subject: [PATCH 19/97] Fixed merge conflicts --- CMakeLists.txt | 11 ++++++----- res/app.rc | 2 +- src/version.h | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cda32c02..d2c72a0c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -130,6 +130,7 @@ set(SOURCES_COMMON src/common/log/ConsoleLog.cpp src/common/log/FileLog.cpp src/common/log/Log.cpp + src/common/log/BasicLog.cpp src/common/Platform.cpp src/core/Config.cpp src/core/Controller.cpp @@ -293,13 +294,13 @@ if (BUILD_STATIC) set(CMAKE_EXE_LINKER_FLAGS " -static") endif() -add_executable(${PROJECT_NAME} ${HEADERS} ${SOURCES_CC_COMMON} ${SOURCES} ${SOURCES_COMMON} ${SOURCES_OS} ${SOURCES_CPUID} ${HEADERS_CRYPTO} ${SOURCES_CRYPTO} ${SOURCES_SYSLOG} ${HTTPD_SOURCES} ${SOURCES_CC_CLIENT}) -set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME ${MINER_EXECUTABLE_NAME}) -target_link_libraries(${PROJECT_NAME} ${UV_LIBRARIES} ${MHD_LIBRARY} ${EXTRA_LIBS} ${LIBS} ${OpenCL_LIBRARY}) +add_executable(xmrigMiner-amd ${HEADERS} ${SOURCES_CC_COMMON} ${SOURCES} ${SOURCES_COMMON} ${SOURCES_OS} ${SOURCES_CPUID} ${HEADERS_CRYPTO} ${SOURCES_CRYPTO} ${SOURCES_SYSLOG} ${HTTPD_SOURCES} ${SOURCES_CC_CLIENT}) +set_target_properties(xmrigMiner-amd PROPERTIES OUTPUT_NAME ${MINER_EXECUTABLE_NAME}) +target_link_libraries(xmrigMiner-amd ${UV_LIBRARIES} ${MHD_LIBRARY} ${EXTRA_LIBS} ${LIBS} ${OpenCL_LIBRARY}) if (WITH_TLS) - #target_link_libraries(${PROJECT_NAME} xmrig_tls ${OPENSSL_LIBRARIES} ${EXTRA_LIBS}) - target_link_libraries(${PROJECT_NAME} ${OPENSSL_LIBRARIES} ${EXTRA_LIBS}) + #target_link_libraries(xmrigMiner-amd xmrig_tls ${OPENSSL_LIBRARIES} ${EXTRA_LIBS}) + target_link_libraries(xmrigMiner-amd ${OPENSSL_LIBRARIES} ${EXTRA_LIBS}) endif (WITH_TLS) diff --git a/res/app.rc b/res/app.rc index 7842640c..384dadb6 100644 --- a/res/app.rc +++ b/res/app.rc @@ -24,7 +24,7 @@ VS_VERSION_INFO VERSIONINFO VALUE "FileDescription", APP_DESC VALUE "FileVersion", APP_VERSION VALUE "LegalCopyright", APP_COPYRIGHT - VALUE "OriginalFilename", "xmrig.exe" + VALUE "OriginalFilename", "xmrig-amd.exe" VALUE "ProductName", APP_NAME VALUE "ProductVersion", APP_VERSION END diff --git a/src/version.h b/src/version.h index 89ee2614..a847edba 100644 --- a/src/version.h +++ b/src/version.h @@ -35,7 +35,7 @@ #define APP_DESC "XMRigCC-AMD OpenCL miner" #define APP_COPYRIGHT "Copyright (C) 2017- BenDr0id" #endif -#define APP_VERSION "1.6.5_beta2 (based on XMRig)" +#define APP_VERSION "1.6.5_beta3 (based on XMRig)" #define APP_DOMAIN "" #define APP_SITE "https://github.com/Bendr0id/xmrigCC-amd" #define APP_KIND "amd" From 22f9a648b62f756f348aed7656fc2d5ea804daec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Sun, 22 Jul 2018 21:46:09 +0200 Subject: [PATCH 20/97] Default disable old HTTP API --- .gitignore | 6 ++++++ CMakeLists.txt | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 30ce3a1f..9f293081 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,9 @@ /build /CMakeLists.txt.user /build2 +/.idea +/CMakeFiles +/cmake-build-* +CMakeCache.txt +cmake_install.cmake +Makefile diff --git a/CMakeLists.txt b/CMakeLists.txt index d2c72a0c..e6ebb661 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ project(xmrig) option(WITH_AEON "CryptoNight-Lite support" ON) option(WITH_SUMO "CryptoNight-Heavy support" ON) -option(WITH_HTTPD "HTTP REST API" ON) +option(WITH_HTTPD "HTTP REST API" OFF) option(WITH_CC_CLIENT "CC Client" ON) option(WITH_TLS "TLS support" ON) option(BUILD_STATIC "Build static binary" OFF) From c5b6c36b01370746e53af9dc4ed56f7de4062d99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Tue, 24 Jul 2018 22:30:33 +0200 Subject: [PATCH 21/97] Update Config.cpp --- src/core/Config.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/Config.cpp b/src/core/Config.cpp index 4fd5ca62..ed38348c 100644 --- a/src/core/Config.cpp +++ b/src/core/Config.cpp @@ -128,7 +128,7 @@ void xmrig::Config::getJSON(rapidjson::Document &doc) const Value cc(kObjectType); - cc.AddMember("url", StringRef(ccUrl()), allocator); + cc.AddMember("url", ccUrl() ? Value(StringRef(ccUrl())).Move() : Value(kNullType).Move(), allocator); cc.AddMember("access-token", ccToken() ? Value(StringRef(ccToken())).Move() : Value(kNullType).Move(), allocator); cc.AddMember("worker-id", ccWorkerId() ? Value(StringRef(ccWorkerId())).Move() : Value(kNullType).Move(), allocator); cc.AddMember("update-interval-s", ccUpdateInterval(), allocator); From 784035a9902b0a0980c239fab502dc4ad165c3a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Wed, 25 Jul 2018 10:45:49 +0200 Subject: [PATCH 22/97] Fix parsing of CC config params --- src/common/config/ConfigLoader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/config/ConfigLoader.cpp b/src/common/config/ConfigLoader.cpp index af0f4fce..9c70833b 100644 --- a/src/common/config/ConfigLoader.cpp +++ b/src/common/config/ConfigLoader.cpp @@ -108,7 +108,7 @@ bool xmrig::ConfigLoader::loadFromJSON(xmrig::IConfig *config, const rapidjson:: } const rapidjson::Value &cc = doc["cc-client"]; - if (api.IsObject()) { + if (cc.IsObject()) { for (size_t i = 0; i < ARRAY_SIZE(cc_client_options); i++) { parseJSON(config, &cc_client_options[i], cc); } From 766dd41e17879a99dcbfa49eb5a7fd58a5cec664 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Wed, 25 Jul 2018 10:57:06 +0200 Subject: [PATCH 23/97] Update version.h --- src/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/version.h b/src/version.h index a847edba..2ad0b115 100644 --- a/src/version.h +++ b/src/version.h @@ -35,7 +35,7 @@ #define APP_DESC "XMRigCC-AMD OpenCL miner" #define APP_COPYRIGHT "Copyright (C) 2017- BenDr0id" #endif -#define APP_VERSION "1.6.5_beta3 (based on XMRig)" +#define APP_VERSION "1.6.5_beta4 (based on XMRig)" #define APP_DOMAIN "" #define APP_SITE "https://github.com/Bendr0id/xmrigCC-amd" #define APP_KIND "amd" From 45ef21e5645002f2a6a981e9b1c44acc401b42ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Wed, 25 Jul 2018 16:40:43 +0200 Subject: [PATCH 24/97] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f29295c5..32634cc1 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ GPU mining part based on [Wolf9466](https://github.com/OhGodAPet) and [psychocry **XMRigCC Dashboard** -![Screenshot of XMRigCC Dashboard](https://i.imgur.com/VwJaf26.png) +![Screenshot of XMRigCC Dashboard](https://i.imgur.com/EciC0gW.png) ## Download From b393bb6200438b025b52a7afe8a7794a2405f71c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Wed, 25 Jul 2018 16:42:14 +0200 Subject: [PATCH 25/97] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 32634cc1..8c9ff2be 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ GPU mining part based on [Wolf9466](https://github.com/OhGodAPet) and [psychocry **XMRigCC Dashboard** -![Screenshot of XMRigCC Dashboard](https://i.imgur.com/EciC0gW.png) +![Screenshot of XMRigCC Dashboard](https://imgur.com/UrdTHpM.png) ## Download From b336cfd4393e8f0730f03a0ea0ec4ab0b2c1f234 Mon Sep 17 00:00:00 2001 From: BenDr0id Date: Thu, 2 Aug 2018 16:37:55 +0200 Subject: [PATCH 26/97] Add remote logging and config property to enable/disable config upload on startup --- CMakeLists.txt | 6 ++ src/cc/CCClient.cpp | 8 ++- src/common/config/CommonConfig.cpp | 25 +++----- src/common/config/CommonConfig.h | 4 +- src/common/interfaces/IConfig.h | 2 +- src/common/log/RemoteLog.cpp | 100 +++++++++++++++++++++++++++++ src/common/log/RemoteLog.h | 49 ++++++++++++++ src/config.json | 4 +- src/core/ConfigLoader_platform.h | 6 +- src/core/Controller.cpp | 6 ++ src/version.h | 4 +- 11 files changed, 187 insertions(+), 27 deletions(-) create mode 100644 src/common/log/RemoteLog.cpp create mode 100644 src/common/log/RemoteLog.h diff --git a/CMakeLists.txt b/CMakeLists.txt index e6ebb661..4ad0d2a1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,10 @@ 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) + option(WITH_AEON "CryptoNight-Lite support" ON) option(WITH_SUMO "CryptoNight-Heavy support" ON) option(WITH_HTTPD "HTTP REST API" OFF) @@ -41,6 +45,7 @@ set(HEADERS src/common/interfaces/IStrategyListener.h src/common/interfaces/IWatcherListener.h src/common/log/BasicLog.h + src/common/log/RemoteLog.h src/common/log/ConsoleLog.h src/common/log/FileLog.h src/common/log/Log.h @@ -128,6 +133,7 @@ set(SOURCES_COMMON src/common/config/ConfigLoader.cpp src/common/config/ConfigWatcher.cpp src/common/log/ConsoleLog.cpp + src/common/log/RemoteLog.cpp src/common/log/FileLog.cpp src/common/log/Log.cpp src/common/log/BasicLog.cpp diff --git a/src/cc/CCClient.cpp b/src/cc/CCClient.cpp index 4e7492a8..793a1798 100644 --- a/src/cc/CCClient.cpp +++ b/src/cc/CCClient.cpp @@ -28,7 +28,7 @@ #include <3rdparty/rapidjson/stringbuffer.h> #include <3rdparty/rapidjson/prettywriter.h> #include -//#include +#include #include #include #include @@ -303,7 +303,7 @@ void CCClient::refreshUptime() void CCClient::refreshLog() { - //m_self->m_clientStatus.setLog(RemoteLog::getRows()); + m_self->m_clientStatus.setLog(RemoteLog::getRows()); } void CCClient::onThreadStarted(void* handle) @@ -316,7 +316,9 @@ void CCClient::onThreadStarted(void* handle) static_cast(m_self->m_controller->config()->ccUpdateInterval() * 1000), static_cast(m_self->m_controller->config()->ccUpdateInterval() * 1000)); - m_self->publishConfig(); + if (m_self->m_controller->config()->ccUploadConfigOnStartup()) { + m_self->publishConfig(); + } uv_run(&m_self->m_client_loop, UV_RUN_DEFAULT); } diff --git a/src/common/config/CommonConfig.cpp b/src/common/config/CommonConfig.cpp index 6d318aae..849cb362 100644 --- a/src/common/config/CommonConfig.cpp +++ b/src/common/config/CommonConfig.cpp @@ -55,7 +55,8 @@ xmrig::CommonConfig::CommonConfig() : # endif m_daemonized(false), m_ccUseTls(false), - m_ccUseRemoteLogging(false), + m_ccUseRemoteLogging(true), + m_ccUploadConfigOnStartup(true), m_apiPort(0), m_donateLevel(kDefaultDonateLevel), @@ -64,7 +65,6 @@ xmrig::CommonConfig::CommonConfig() : m_retryPause(5), m_ccUpdateInterval(10), m_ccPort(0), - m_ccRemoteLoggingMaxRows(25), m_state(NoneState) { m_pools.push_back(Pool()); @@ -202,6 +202,10 @@ bool xmrig::CommonConfig::parseBoolean(int key, bool enable) m_ccUseRemoteLogging = enable; break; + case CCUploadConfigOnStartupKey: /* --cc-upload-config-on-startup */ + m_ccUploadConfigOnStartup = enable; + break; + default: break; } @@ -291,7 +295,6 @@ bool xmrig::CommonConfig::parseString(int key, const char *arg) case RetryPauseKey: /* --retry-pause */ case ApiPort: /* --api-port */ case PrintTimeKey: /* --cpu-priority */ - case CCRemoteLoggingMaxRowKey: /* --cc-remote-logging-max-rows */ return parseUint64(key, strtol(arg, nullptr, 10)); case BackgroundKey: /* --background */ @@ -300,9 +303,10 @@ bool xmrig::CommonConfig::parseString(int key, const char *arg) case NicehashKey: /* --nicehash */ case ApiIPv6Key: /* --api-ipv6 */ case DryRunKey: /* --dry-run */ - case CCUseTlsKey: /* --cc-use-tls-run */ - case CCUseRemoteLoggingKey: /* --cc-use-remote-logging */ - case DaemonizedKey: /* --daemonized */ + case CCUseTlsKey: /* --cc-use-tls-run */ + case CCUseRemoteLoggingKey: /* --cc-use-remote-logging */ + case CCUploadConfigOnStartupKey: /* --cc-upload-config-on-startup */ + case DaemonizedKey: /* --daemonized */ return parseBoolean(key, true); case ColorKey: /* --no-color */ @@ -380,15 +384,6 @@ bool xmrig::CommonConfig::parseInt(int key, int arg) } break; - case CCRemoteLoggingMaxRowKey: /* --cc-remote-logging-max-rows */ - if (arg < 1) { - m_ccUseRemoteLogging = false; - } - else { - m_ccRemoteLoggingMaxRows = static_cast(arg); - } - break; - case CCUpdateIntervalKey: /* --cc-update-interval-s */ if (arg > 0) { m_ccUpdateInterval = arg; diff --git a/src/common/config/CommonConfig.h b/src/common/config/CommonConfig.h index 3d2ae723..d74ed8ff 100644 --- a/src/common/config/CommonConfig.h +++ b/src/common/config/CommonConfig.h @@ -51,6 +51,7 @@ class CommonConfig : public IConfig inline bool isSyslog() const { return m_syslog; } inline bool ccUseTls() const { return m_ccUseTls; } inline bool ccUseRemoteLogging() const { return m_ccUseRemoteLogging; } + inline bool ccUploadConfigOnStartup() const { return m_ccUploadConfigOnStartup; } inline const char *apiToken() const { return m_apiToken.data(); } inline const char *apiWorkerId() const { return m_apiWorkerId.data(); } inline const char *logFile() const { return m_logFile.data(); } @@ -68,7 +69,6 @@ class CommonConfig : public IConfig inline int retryPause() const { return m_retryPause; } inline int ccUpdateInterval() const { return m_ccUpdateInterval; } inline int ccPort() const { return m_ccPort; } - inline size_t ccRemoteLoggingMaxRows() const { return m_ccRemoteLoggingMaxRows; } inline void setColors(bool colors) { m_colors = colors; } inline bool isWatch() const override { return m_watch && !m_fileName.isNull(); } @@ -104,6 +104,7 @@ class CommonConfig : public IConfig bool m_daemonized; bool m_ccUseTls; bool m_ccUseRemoteLogging; + bool m_ccUploadConfigOnStartup; int m_apiPort; int m_donateLevel; int m_printTime; @@ -111,7 +112,6 @@ class CommonConfig : public IConfig int m_retryPause; int m_ccUpdateInterval; int m_ccPort; - size_t m_ccRemoteLoggingMaxRows; State m_state; std::vector m_activePools; std::vector m_pools; diff --git a/src/common/interfaces/IConfig.h b/src/common/interfaces/IConfig.h index 3512371e..9e0032e8 100644 --- a/src/common/interfaces/IConfig.h +++ b/src/common/interfaces/IConfig.h @@ -105,7 +105,7 @@ class IConfig CCUpdateIntervalKey = 6003, CCUseTlsKey = 6004, CCUseRemoteLoggingKey = 6005, - CCRemoteLoggingMaxRowKey = 6006, + CCUploadConfigOnStartupKey = 6006, DaemonizedKey = 6007 }; diff --git a/src/common/log/RemoteLog.cpp b/src/common/log/RemoteLog.cpp new file mode 100644 index 00000000..f51ff8bf --- /dev/null +++ b/src/common/log/RemoteLog.cpp @@ -0,0 +1,100 @@ +/* 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 "common/log/RemoteLog.h" + +RemoteLog* RemoteLog::m_self = nullptr; + +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(Level 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, 512 - size - 1, fmt, args) + size; + buf[size] = '\n'; + + uv_mutex_lock(&m_mutex); + + if (m_rows.size() == m_maxRows) { + m_rows.pop_front(); + } + + std::string row = std::regex_replace(std::string(buf, size+1), std::regex("\x1B\\[[0-9;]*[a-zA-Z]"), ""); + + m_rows.push_back(row); + + uv_mutex_unlock(&m_mutex); + + delete[](buf); +} + +void RemoteLog::text(const char* fmt, va_list args) +{ + message(INFO, fmt, args); +} + +std::string RemoteLog::getRows() +{ + std::stringstream data; + + uv_mutex_lock(&m_self->m_mutex); + + if (m_self) { + 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/common/log/RemoteLog.h b/src/common/log/RemoteLog.h new file mode 100644 index 00000000..20f27264 --- /dev/null +++ b/src/common/log/RemoteLog.h @@ -0,0 +1,49 @@ +/* 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 __REMOTELOG_H__ +#define __REMOTELOG_H__ + + +#include +#include +#include + +#include "common/interfaces/ILogBackend.h" + + +class RemoteLog : public ILogBackend +{ +public: + RemoteLog(size_t maxRows); + ~RemoteLog(); + + void message(Level level, const char* fmt, va_list args) override; + void text(const char* fmt, va_list args) override; + + static std::string getRows(); + + +private: + static RemoteLog* m_self; + + uv_mutex_t m_mutex; + size_t m_maxRows; + std::list m_rows; +}; + +#endif /* __REMOTELOG_H__ */ diff --git a/src/config.json b/src/config.json index 53d506fc..7005fb44 100644 --- a/src/config.json +++ b/src/config.json @@ -35,6 +35,8 @@ "use-tls" : false, "access-token": "mySecret", "worker-id": null, - "update-interval-s": 10 + "update-interval-s": 10, + "use-remote-logging" : true, + "upload-config-on-startup" : true } } \ No newline at end of file diff --git a/src/core/ConfigLoader_platform.h b/src/core/ConfigLoader_platform.h index 7221ece3..6dcb0dca 100644 --- a/src/core/ConfigLoader_platform.h +++ b/src/core/ConfigLoader_platform.h @@ -145,8 +145,8 @@ static struct option const options[] = { { "cc-worker-id", 1, nullptr, xmrig::IConfig::CCWorkerIdKey }, { "cc-update-interval-s", 1, nullptr, xmrig::IConfig::CCUpdateIntervalKey }, { "cc-use-tls", 0, nullptr, xmrig::IConfig::CCUseTlsKey }, - { "cc-use-remote-logging", 0, nullptr, xmrig::IConfig::CCUseRemoteLoggingKey }, - { "cc-remote-logging-max-rows", 1, nullptr, xmrig::IConfig::CCRemoteLoggingMaxRowKey }, + { "cc-use-remote-logging", 0, nullptr, xmrig::IConfig::CCUseRemoteLoggingKey }, + { "cc-upload-config-on-startup", 0, nullptr, xmrig::IConfig::CCUploadConfigOnStartupKey }, { "daemonized", 0, nullptr, xmrig::IConfig::DaemonizedKey }, { "opencl-loader", 1, nullptr, xmrig::IConfig::OclLoader }, { 0, 0, 0, 0 } @@ -203,7 +203,7 @@ static struct option const cc_client_options[] = { { "update-interval-s", 1, nullptr, xmrig::IConfig::CCUpdateIntervalKey }, { "use-tls", 0, nullptr, xmrig::IConfig::CCUseTlsKey }, { "use-remote-logging", 0, nullptr, xmrig::IConfig::CCUseRemoteLoggingKey }, - { "remote-logging-max-rows", 1, nullptr, xmrig::IConfig::CCRemoteLoggingMaxRowKey }, + { "cc-upload-config-on-startup",1, nullptr, xmrig::IConfig::CCUploadConfigOnStartupKey }, { 0, 0, 0, 0 } }; diff --git a/src/core/Controller.cpp b/src/core/Controller.cpp index dc2fc8f8..709a5764 100644 --- a/src/core/Controller.cpp +++ b/src/core/Controller.cpp @@ -23,6 +23,7 @@ #include +#include #include "amd/OclLib.h" @@ -119,6 +120,11 @@ int xmrig::Controller::init(int argc, char **argv) Log::add(new FileLog(this, config()->logFile())); } + if (config()->ccUseRemoteLogging()) { + // 20 lines per second should be enough + Log::add(new RemoteLog(static_cast(config()->ccUpdateInterval() * 20))); + } + # ifdef HAVE_SYSLOG_H if (config()->isSyslog()) { Log::add(new SysLog()); diff --git a/src/version.h b/src/version.h index a847edba..f5ed6b19 100644 --- a/src/version.h +++ b/src/version.h @@ -35,14 +35,14 @@ #define APP_DESC "XMRigCC-AMD OpenCL miner" #define APP_COPYRIGHT "Copyright (C) 2017- BenDr0id" #endif -#define APP_VERSION "1.6.5_beta3 (based on XMRig)" +#define APP_VERSION "1.6.6_beta1 (based on XMRig)" #define APP_DOMAIN "" #define APP_SITE "https://github.com/Bendr0id/xmrigCC-amd" #define APP_KIND "amd" #define APP_VER_MAJOR 1 #define APP_VER_MINOR 6 -#define APP_VER_PATCH 5 +#define APP_VER_PATCH 6 #ifndef NDEBUG #ifndef XMRIG_NO_TLS From 5006a9e861558f962469aaf3001dd7a737e2ad4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Fri, 3 Aug 2018 23:14:48 +0200 Subject: [PATCH 27/97] Merged XMRigCC-cpu changes --- src/cc/CCClient.cpp | 9 ++++-- src/cc/ClientStatus.cpp | 70 +++++++++++++++++++++++++++++++---------- src/cc/ClientStatus.h | 19 ++++++++--- 3 files changed, 73 insertions(+), 25 deletions(-) diff --git a/src/cc/CCClient.cpp b/src/cc/CCClient.cpp index 793a1798..41cf56b0 100644 --- a/src/cc/CCClient.cpp +++ b/src/cc/CCClient.cpp @@ -78,9 +78,7 @@ CCClient::CCClient(xmrig::Controller* controller, uv_async_t* async) m_clientStatus.setCurrentAlgoName(m_controller->config()->algorithm().name()); } - m_clientStatus.setHugepages(""); - m_clientStatus.setHashFactor(-1); - + m_clientStatus.setHashFactor(0); m_clientStatus.setVersion(Version::string()); m_clientStatus.setCpuBrand(Cpu::brand()); m_clientStatus.setCpuAES(Cpu::hasAES()); @@ -133,6 +131,11 @@ void CCClient::updateNetworkState(const NetworkState& network) m_self->m_clientStatus.setSharesTotal(network.accepted + network.rejected); m_self->m_clientStatus.setHashesTotal(network.total); m_self->m_clientStatus.setAvgTime(network.avgTime()); + + 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)); uv_mutex_unlock(&m_mutex); diff --git a/src/cc/ClientStatus.cpp b/src/cc/ClientStatus.cpp index 24057ebd..28b9c771 100644 --- a/src/cc/ClientStatus.cpp +++ b/src/cc/ClientStatus.cpp @@ -37,11 +37,13 @@ ClientStatus::ClientStatus() m_isHugepagesEnabled(false), m_isCpuX64(false), m_hasCpuAES(false), - m_hashFactor(1), m_hashrateShort(0), m_hashrateMedium(0), m_hashrateLong(0), m_hashrateHighest(0), + m_hashFactor(1), + m_totalPages(0), + m_totalHugepages(0), m_currentThreads(0), m_cpuSockets(0), m_cpuCores(0), @@ -148,6 +150,11 @@ void ClientStatus::setLog(const std::string& log) m_log = log; } +void ClientStatus::clearLog() +{ + m_log.clear(); +} + bool ClientStatus::hasHugepages() const { return m_hasHugepages; @@ -168,16 +175,6 @@ void ClientStatus::setHugepagesEnabled(bool hugepagesEnabled) m_isHugepagesEnabled = hugepagesEnabled; } -int ClientStatus::getHashFactor() const -{ - return m_hashFactor; -} - -void ClientStatus::setHashFactor(int hashFactor) -{ - m_hashFactor = hashFactor; -} - bool ClientStatus::isCpuX64() const { return m_isCpuX64; @@ -238,6 +235,36 @@ double ClientStatus::getHashrateHighest() const return m_hashrateHighest; } +int ClientStatus::getHashFactor() const +{ + return m_hashFactor; +} + +void ClientStatus::setHashFactor(int hashFactor) +{ + m_hashFactor = hashFactor; +} + +int ClientStatus::getTotalPages() const +{ + return m_totalPages; +} + +void ClientStatus::setTotalPages(int totalPages) +{ + m_totalPages = totalPages; +} + +int ClientStatus::getTotalHugepages() const +{ + return m_totalHugepages; +} + +void ClientStatus::setTotalHugepages(int totalHugepages) +{ + m_totalHugepages = totalHugepages; +} + int ClientStatus::getCurrentThreads() const { return m_currentThreads; @@ -404,10 +431,6 @@ bool ClientStatus::parseFromJson(const rapidjson::Document& document) m_isHugepagesEnabled = clientStatus["hugepages_enabled"].GetBool(); } - if (clientStatus.HasMember("hash_factor")) { - m_hashFactor = clientStatus["hash_factor"].GetInt(); - } - if (clientStatus.HasMember("cpu_is_x64")) { m_isCpuX64 = clientStatus["cpu_is_x64"].GetBool(); } @@ -432,6 +455,18 @@ bool ClientStatus::parseFromJson(const rapidjson::Document& document) m_hashrateHighest = clientStatus["hashrate_highest"].GetDouble(); } + if (clientStatus.HasMember("hash_factor")) { + m_hashFactor = clientStatus["hash_factor"].GetInt(); + } + + if (clientStatus.HasMember("total_pages")) { + m_totalPages = clientStatus["total_pages"].GetInt(); + } + + if (clientStatus.HasMember("total_hugepages")) { + m_totalHugepages = clientStatus["total_hugepages"].GetInt(); + } + if (clientStatus.HasMember("current_threads")) { m_currentThreads = clientStatus["current_threads"].GetInt(); } @@ -503,7 +538,6 @@ rapidjson::Value ClientStatus::toJson(rapidjson::MemoryPoolAllocator Date: Wed, 8 Aug 2018 17:02:24 +0200 Subject: [PATCH 28/97] WIP GPUInfo on Dashboard --- src/cc/CCClient.cpp | 20 +++++++++++++------- src/cc/CCClient.h | 17 ++++++++++------- src/cc/ClientStatus.cpp | 9 +-------- src/cc/ClientStatus.h | 12 +++++------- src/cc/ControlCommand.cpp | 9 +-------- src/cc/ControlCommand.h | 9 +-------- src/cc/GPUInfo.cpp | 19 +++++++++++++++++++ src/cc/GPUInfo.h | 26 ++++++++++++++++++++++++++ src/cc/XMRigd.cpp | 1 - src/version.h | 3 +++ src/workers/Workers.cpp | 4 ++++ 11 files changed, 83 insertions(+), 46 deletions(-) create mode 100644 src/cc/GPUInfo.cpp create mode 100644 src/cc/GPUInfo.h diff --git a/src/cc/CCClient.cpp b/src/cc/CCClient.cpp index 41cf56b0..8c5de780 100644 --- a/src/cc/CCClient.cpp +++ b/src/cc/CCClient.cpp @@ -1,10 +1,4 @@ -/* 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 +/* XMRigCC * Copyright 2017- BenDr0id * * @@ -142,6 +136,18 @@ void CCClient::updateNetworkState(const NetworkState& network) } } +#ifdef TYPE_AMD_GPU +void CCClient::updateGpuInfo(const std::vector& gpuContext) +{ + if (m_self) { + uv_mutex_lock(&m_mutex); + + + uv_mutex_unlock(&m_mutex); + } +} +#endif + void CCClient::publishClientStatusReport() { std::string requestUrl = "/client/setClientStatus?clientId=" + m_self->m_clientStatus.getClientId(); diff --git a/src/cc/CCClient.h b/src/cc/CCClient.h index 559b6115..9d6b2acf 100644 --- a/src/cc/CCClient.h +++ b/src/cc/CCClient.h @@ -1,10 +1,4 @@ -/* 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 +/* XMRigCC * Copyright 2017- BenDr0id * * @@ -33,6 +27,11 @@ #include <3rdparty/cpp-httplib/httplib.h> #include #include "ClientStatus.h" +#include "version.h" + +#ifdef TYPE_AMD_GPU +#include +#endif class Hashrate; class NetworkState; @@ -46,6 +45,9 @@ class CCClient static void updateHashrate(const Hashrate *hashrate); static void updateNetworkState(const NetworkState &results); +#ifdef TYPE_AMD_GPU + static void updateGpuInfo(const std::vector& network); +#endif private: void publishClientStatusReport(); @@ -75,6 +77,7 @@ class CCClient uv_timer_t m_timer; uv_loop_t m_client_loop; uv_thread_t m_thread; + }; #endif diff --git a/src/cc/ClientStatus.cpp b/src/cc/ClientStatus.cpp index 28b9c771..46c6f80a 100644 --- a/src/cc/ClientStatus.cpp +++ b/src/cc/ClientStatus.cpp @@ -1,13 +1,6 @@ -/* 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 +/* 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 diff --git a/src/cc/ClientStatus.h b/src/cc/ClientStatus.h index e8204126..91343c8f 100644 --- a/src/cc/ClientStatus.h +++ b/src/cc/ClientStatus.h @@ -1,10 +1,4 @@ -/* 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 +/* XMRigCC * Copyright 2017- BenDr0id * * @@ -27,7 +21,9 @@ #include #include +#include #include +#include "GPUInfo.h" class ClientStatus { @@ -196,6 +192,8 @@ class ClientStatus int m_cpuL2; int m_cpuL3; + std::list m_gpuInfo; + uint64_t m_sharesGood; uint64_t m_sharesTotal; uint64_t m_hashesTotal; diff --git a/src/cc/ControlCommand.cpp b/src/cc/ControlCommand.cpp index 60154236..0bd23515 100644 --- a/src/cc/ControlCommand.cpp +++ b/src/cc/ControlCommand.cpp @@ -1,13 +1,6 @@ -/* 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 +/* 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 diff --git a/src/cc/ControlCommand.h b/src/cc/ControlCommand.h index dbd5b391..bde0e53b 100644 --- a/src/cc/ControlCommand.h +++ b/src/cc/ControlCommand.h @@ -1,13 +1,6 @@ -/* 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 +/* 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 diff --git a/src/cc/GPUInfo.cpp b/src/cc/GPUInfo.cpp new file mode 100644 index 00000000..c8233f52 --- /dev/null +++ b/src/cc/GPUInfo.cpp @@ -0,0 +1,19 @@ +/* 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 "GPUInfo.h" + diff --git a/src/cc/GPUInfo.h b/src/cc/GPUInfo.h new file mode 100644 index 00000000..39a4ce7b --- /dev/null +++ b/src/cc/GPUInfo.h @@ -0,0 +1,26 @@ +/* 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 XMRIG_GPUINFO_H +#define XMRIG_GPUINFO_H + +class GPUInfo +{ +}; + + +#endif //XMRIG_GPUINFO_H diff --git a/src/cc/XMRigd.cpp b/src/cc/XMRigd.cpp index d70db7c9..0954d567 100644 --- a/src/cc/XMRigd.cpp +++ b/src/cc/XMRigd.cpp @@ -1,7 +1,6 @@ /* XMRigd * 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 diff --git a/src/version.h b/src/version.h index f5ed6b19..3fb83705 100644 --- a/src/version.h +++ b/src/version.h @@ -24,6 +24,7 @@ #ifndef __VERSION_H__ #define __VERSION_H__ + #ifdef XMRIG_CC_SERVER #define APP_ID "XMRigCC" #define APP_NAME "XMRigCC" @@ -44,6 +45,8 @@ #define APP_VER_MINOR 6 #define APP_VER_PATCH 6 +#define TYPE_AMD_GPU + #ifndef NDEBUG #ifndef XMRIG_NO_TLS #define BUILD_TYPE "DEBUG with TLS" diff --git a/src/workers/Workers.cpp b/src/workers/Workers.cpp index f6414d97..cb7ebb29 100644 --- a/src/workers/Workers.cpp +++ b/src/workers/Workers.cpp @@ -198,6 +198,10 @@ bool Workers::start(xmrig::Controller *controller) return false; } +# ifndef XMRIG_NO_CC + CCClient::updateGpuInfo(contexts); +# endif + uv_timer_init(uv_default_loop(), &m_timer); uv_timer_start(&m_timer, Workers::onTick, 500, 500); From 8713b300bb83acd8d11a7daa288eafd1f1298750 Mon Sep 17 00:00:00 2001 From: BenDr0id Date: Thu, 9 Aug 2018 17:45:45 +0200 Subject: [PATCH 29/97] WIP adding GPUInfo to Dashboard --- CMakeLists.txt | 3 +- src/amd/GpuContext.h | 3 + src/amd/OclGPU.cpp | 5 +- src/cc/ClientStatus.cpp | 25 ++++++++- src/cc/ClientStatus.h | 6 +- src/cc/ControlCommand.cpp | 2 +- src/cc/ControlCommand.h | 2 +- src/cc/GPUInfo.cpp | 114 ++++++++++++++++++++++++++++++++++++++ src/cc/GPUInfo.h | 60 ++++++++++++++++++++ 9 files changed, 210 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4ad0d2a1..ccd14c5b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -283,7 +283,8 @@ endif() if (WITH_CC_CLIENT) set(SOURCES_CC_COMMON src/cc/ControlCommand.cpp - src/cc/ClientStatus.cpp) + src/cc/ClientStatus.cpp + src/cc/GPUInfo.cpp) else() add_definitions(/DXMRIG_NO_CC) endif() diff --git a/src/amd/GpuContext.h b/src/amd/GpuContext.h index 733ca238..7ccf0076 100644 --- a/src/amd/GpuContext.h +++ b/src/amd/GpuContext.h @@ -38,6 +38,7 @@ struct GpuContext deviceIdx(0), rawIntensity(0), workSize(0), + maximumWorkSize(0), stridedIndex(1), memChunk(2), compMode(1), @@ -58,6 +59,7 @@ struct GpuContext deviceIdx(index), rawIntensity(intensity), workSize(worksize), + maximumWorkSize(0), stridedIndex(stridedIndex), memChunk(memChunk), compMode(compMode ? 1 : 0), @@ -77,6 +79,7 @@ struct GpuContext size_t deviceIdx; size_t rawIntensity; size_t workSize; + size_t maximumWorkSize; int stridedIndex; int memChunk; int compMode; diff --git a/src/amd/OclGPU.cpp b/src/amd/OclGPU.cpp index 03b056b2..dd92b786 100644 --- a/src/amd/OclGPU.cpp +++ b/src/amd/OclGPU.cpp @@ -117,10 +117,9 @@ inline static int cnKernelOffset(uint32_t variant) size_t InitOpenCLGpu(int index, cl_context opencl_ctx, GpuContext* ctx, const char* source_code, xmrig::Config *config) { - size_t MaximumWorkSize; cl_int ret; - if (OclLib::getDeviceInfo(ctx->DeviceID, CL_DEVICE_MAX_WORK_GROUP_SIZE, sizeof(size_t), &MaximumWorkSize) != CL_SUCCESS) { + if (OclLib::getDeviceInfo(ctx->DeviceID, CL_DEVICE_MAX_WORK_GROUP_SIZE, sizeof(size_t), &ctx->maximumWorkSize) != CL_SUCCESS) { return OCL_ERR_API; } @@ -129,7 +128,7 @@ size_t InitOpenCLGpu(int index, cl_context opencl_ctx, GpuContext* ctx, const ch ctx->computeUnits = getDeviceMaxComputeUnits(ctx->DeviceID); LOG_INFO(config->isColors() ? "\x1B[01;37m#%d\x1B[0m, GPU \x1B[01;37m#%zu\x1B[0m \x1B[01;32m%s\x1B[0m, intensity: \x1B[01;37m%zu\x1B[0m (%zu/%zu), cu: \x1B[01;37m%d" : "#%d, GPU #%zu (%s), intensity: %zu (%zu/%zu), cu: %d", - index, ctx->deviceIdx, buf, ctx->rawIntensity, ctx->workSize, MaximumWorkSize, ctx->computeUnits); + index, ctx->deviceIdx, buf, ctx->rawIntensity, ctx->workSize, ctx->maximumWorkSize, ctx->computeUnits); ctx->CommandQueues = OclLib::createCommandQueue(opencl_ctx, ctx->DeviceID, &ret); if (ret != CL_SUCCESS) { diff --git a/src/cc/ClientStatus.cpp b/src/cc/ClientStatus.cpp index 46c6f80a..8a38beb3 100644 --- a/src/cc/ClientStatus.cpp +++ b/src/cc/ClientStatus.cpp @@ -484,6 +484,11 @@ bool ClientStatus::parseFromJson(const rapidjson::Document& document) m_cpuL3 = clientStatus["cpu_l3"].GetInt(); } + if (document.HasMember("client_status")) { + rapidjson::Value::ConstObject clientStatus = document["client_status"].GetObject(); + + } + if (clientStatus.HasMember("shares_good")) { m_sharesGood = clientStatus["shares_good"].GetUint64(); } @@ -549,6 +554,14 @@ rapidjson::Value ClientStatus::toJson(rapidjson::MemoryPoolAllocator(m_lastStatusUpdate), allocator); - clientStatus.AddMember("log", rapidjson::StringRef(m_log.c_str()), allocator); @@ -582,3 +593,13 @@ std::string ClientStatus::toJsonString() return strdup(buffer.GetString()); } + +const std::list ClientStatus::getGPUInfoList() const +{ + return m_gpuInfoList; +} + +void ClientStatus::setGPUInfoList(const std::list& gpuInfoList) +{ + m_gpuInfoList = gpuInfoList; +} diff --git a/src/cc/ClientStatus.h b/src/cc/ClientStatus.h index 91343c8f..9611b370 100644 --- a/src/cc/ClientStatus.h +++ b/src/cc/ClientStatus.h @@ -132,6 +132,9 @@ class ClientStatus int getCpuL3() const; void setCpuL3(int cpuL3); + const std::list getGPUInfoList() const; + void setGPUInfoList(const std::list& gpuInfoList); + uint64_t getSharesGood() const; void setSharesGood(uint64_t sharesGood); @@ -153,7 +156,6 @@ class ClientStatus rapidjson::Value toJson(rapidjson::MemoryPoolAllocator& allocator); bool parseFromJson(const rapidjson::Document& document); - private: const char* status_str[3] = { "RUNNING", @@ -192,7 +194,7 @@ class ClientStatus int m_cpuL2; int m_cpuL3; - std::list m_gpuInfo; + std::list m_gpuInfoList; uint64_t m_sharesGood; uint64_t m_sharesTotal; diff --git a/src/cc/ControlCommand.cpp b/src/cc/ControlCommand.cpp index 0bd23515..7348c994 100644 --- a/src/cc/ControlCommand.cpp +++ b/src/cc/ControlCommand.cpp @@ -74,7 +74,7 @@ rapidjson::Value ControlCommand::toJson(rapidjson::MemoryPoolAllocator& allocator) +{ + +} + +bool GPUInfo::parseFromJson(const rapidjson::Document& document) +{ + +} + +bool GPUInfo::parseFromJsonString(const std::string& json) +{ + +} + +size_t GPUInfo::getDeviceIdx() const +{ + return m_deviceIdx; +} + +void GPUInfo::setDeviceIdx(size_t deviceIdx) +{ + m_deviceIdx = deviceIdx; +} + +size_t GPUInfo::getRawIntensity() const +{ + return m_rawIntensity; +} + +void GPUInfo::setRawIntensity(size_t rawIntensity) +{ + m_rawIntensity = rawIntensity; +} + +size_t GPUInfo::getWorkSize() const +{ + return m_workSize; +} + +void GPUInfo::setWorkSize(size_t workSize) +{ + m_workSize = workSize; +} + +size_t GPUInfo::getFreeMem() const +{ + return m_freeMem; +} + +void GPUInfo::setFreeMem(size_t freeMem) +{ + m_freeMem = freeMem; +} + +int GPUInfo::getStridedIndex() const +{ + return m_stridedIndex; +} + +void GPUInfo::setStridedIndex(int stridedIndex) +{ + m_stridedIndex = stridedIndex; +} + +int GPUInfo::getMemChunk() const +{ + return m_memChunk; +} + +void GPUInfo::setMemChunk(int memChunk) +{ + m_memChunk = memChunk; +} + +int GPUInfo::getCompMode() const +{ + return m_compMode; +} + +void GPUInfo::setCompMode(int compMode) +{ + m_compMode = compMode; +} + +int GPUInfo::getComputeUnits() const +{ + return m_computeUnits; +} + +void GPUInfo::setComputeUnits(int computeUnits) +{ + m_computeUnits = computeUnits; +} + +std::string GPUInfo::getName() const +{ + return m_name; +} + +void GPUInfo::setName(const std::string& name) +{ + m_name = name; +} diff --git a/src/cc/GPUInfo.h b/src/cc/GPUInfo.h index 39a4ce7b..59f7123b 100644 --- a/src/cc/GPUInfo.h +++ b/src/cc/GPUInfo.h @@ -18,8 +18,68 @@ #ifndef XMRIG_GPUINFO_H #define XMRIG_GPUINFO_H +#include +#include <3rdparty/rapidjson/document.h> + class GPUInfo { +public: + GPUInfo(); + ~GPUInfo(); + + rapidjson::Value toJson(rapidjson::MemoryPoolAllocator& allocator); + bool parseFromJsonString(const std::string& json); + bool parseFromJson(const rapidjson::Document& document); + + std::string getName() const; + void setName(const std::string& name); + + size_t getDeviceIdx() const; + + void setDeviceIdx(size_t deviceIdx); + + size_t getRawIntensity() const; + + void setRawIntensity(size_t rawIntensity); + + size_t getWorkSize() const; + + void setWorkSize(size_t workSize); + + size_t getFreeMem() const; + + void setFreeMem(size_t freeMem); + + int getStridedIndex() const; + + void setStridedIndex(int stridedIndex); + + int getMemChunk() const; + + void setMemChunk(int memChunk); + + int getCompMode() const; + + void setCompMode(int compMode); + + int getComputeUnits() const; + + void setComputeUnits(int computeUnits); + +private: + size_t m_deviceIdx; + size_t m_rawIntensity; + size_t m_workSize; + size_t m_maxWorkSize; + size_t m_freeMem; + + int m_stridedIndex; + int m_memChunk; + int m_compMode; + int m_computeUnits; + + std::string m_name; + }; From e513641141222d381f9d8561b0f27d5d53d0a465 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Thu, 9 Aug 2018 21:47:53 +0200 Subject: [PATCH 30/97] WIP --- src/cc/CCClient.cpp | 8 ++++++++ src/cc/ClientStatus.cpp | 19 +++++++++++-------- src/cc/ClientStatus.h | 3 ++- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/cc/CCClient.cpp b/src/cc/CCClient.cpp index 8c5de780..3ea9d391 100644 --- a/src/cc/CCClient.cpp +++ b/src/cc/CCClient.cpp @@ -142,6 +142,14 @@ void CCClient::updateGpuInfo(const std::vector& gpuContext) if (m_self) { uv_mutex_lock(&m_mutex); + m_self->m_clientStatus.clearGPUInfoList(); + + for (auto gpu : gpuContext) { + GPUInfo gpuInfo; + gpuInfo.setName(gpu.name); + + m_self->m_clientStatus.addGPUInfo(gpuInfo); + } uv_mutex_unlock(&m_mutex); } diff --git a/src/cc/ClientStatus.cpp b/src/cc/ClientStatus.cpp index 8a38beb3..6818c90d 100644 --- a/src/cc/ClientStatus.cpp +++ b/src/cc/ClientStatus.cpp @@ -484,9 +484,12 @@ bool ClientStatus::parseFromJson(const rapidjson::Document& document) m_cpuL3 = clientStatus["cpu_l3"].GetInt(); } - if (document.HasMember("client_status")) { - rapidjson::Value::ConstObject clientStatus = document["client_status"].GetObject(); - + if (clientStatus.HasMember("gpu_info_list") && clientStatus["gpu_info_list"].IsArray()) { + auto gpuInfoList = clientStatus["gpu_info_list"].GetArray(); + for (rapidjson::Value::ConstValueIterator itr = gpuInfoList.Begin(); itr != gpuInfoList.End(); ++itr) { + const rapidjson::Value& gpuInfo = (*itr)["gpu_info"]; + // TODO parsing + } } if (clientStatus.HasMember("shares_good")) { @@ -557,7 +560,7 @@ rapidjson::Value ClientStatus::toJson(rapidjson::MemoryPoolAllocator ClientStatus::getGPUInfoList() const +void ClientStatus::clearGPUInfoList() { - return m_gpuInfoList; + m_gpuInfoList.clear(); } -void ClientStatus::setGPUInfoList(const std::list& gpuInfoList) +void ClientStatus::addGPUInfo(const GPUInfo gpuInfo) { - m_gpuInfoList = gpuInfoList; + m_gpuInfoList.push_back(gpuInfo); } diff --git a/src/cc/ClientStatus.h b/src/cc/ClientStatus.h index 9611b370..65eb1ed2 100644 --- a/src/cc/ClientStatus.h +++ b/src/cc/ClientStatus.h @@ -133,7 +133,8 @@ class ClientStatus void setCpuL3(int cpuL3); const std::list getGPUInfoList() const; - void setGPUInfoList(const std::list& gpuInfoList); + void addGPUInfo(const GPUInfo gpuInfo); + void clearGPUInfoList(); uint64_t getSharesGood() const; void setSharesGood(uint64_t sharesGood); From 56bc4a66e953d1abff5e6303f5065d096d38b6c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Mon, 13 Aug 2018 21:07:17 +0200 Subject: [PATCH 31/97] Add gpuInfo parseFromJson and toJson --- src/amd/OclGPU.cpp | 1 + src/cc/CCClient.cpp | 8 +++++ src/cc/ClientStatus.cpp | 8 +++-- src/cc/GPUInfo.cpp | 78 ++++++++++++++++++++++++++++++++++------- src/cc/GPUInfo.h | 19 +++------- 5 files changed, 85 insertions(+), 29 deletions(-) diff --git a/src/amd/OclGPU.cpp b/src/amd/OclGPU.cpp index dd92b786..4525dadb 100644 --- a/src/amd/OclGPU.cpp +++ b/src/amd/OclGPU.cpp @@ -125,6 +125,7 @@ size_t InitOpenCLGpu(int index, cl_context opencl_ctx, GpuContext* ctx, const ch char buf[128] = { 0 }; getDeviceName(ctx->DeviceID, buf, sizeof(buf)); + ctx->name = buf; ctx->computeUnits = getDeviceMaxComputeUnits(ctx->DeviceID); LOG_INFO(config->isColors() ? "\x1B[01;37m#%d\x1B[0m, GPU \x1B[01;37m#%zu\x1B[0m \x1B[01;32m%s\x1B[0m, intensity: \x1B[01;37m%zu\x1B[0m (%zu/%zu), cu: \x1B[01;37m%d" : "#%d, GPU #%zu (%s), intensity: %zu (%zu/%zu), cu: %d", diff --git a/src/cc/CCClient.cpp b/src/cc/CCClient.cpp index 3ea9d391..e767f710 100644 --- a/src/cc/CCClient.cpp +++ b/src/cc/CCClient.cpp @@ -147,6 +147,14 @@ void CCClient::updateGpuInfo(const std::vector& gpuContext) for (auto gpu : gpuContext) { GPUInfo gpuInfo; gpuInfo.setName(gpu.name); + gpuInfo.setCompMode(gpu.compMode); + gpuInfo.setComputeUnits(gpu.computeUnits); + gpuInfo.setDeviceIdx(gpu.deviceIdx); + gpuInfo.setFreeMem(gpu.freeMem); + gpuInfo.setWorkSize(gpu.workSize); + gpuInfo.setMaxWorkSize(gpu.maximumWorkSize); + gpuInfo.setMemChunk(gpu.memChunk); + gpuInfo.setRawIntensity(gpu.rawIntensity); m_self->m_clientStatus.addGPUInfo(gpuInfo); } diff --git a/src/cc/ClientStatus.cpp b/src/cc/ClientStatus.cpp index 6818c90d..33f94c88 100644 --- a/src/cc/ClientStatus.cpp +++ b/src/cc/ClientStatus.cpp @@ -485,10 +485,14 @@ bool ClientStatus::parseFromJson(const rapidjson::Document& document) } if (clientStatus.HasMember("gpu_info_list") && clientStatus["gpu_info_list"].IsArray()) { + m_gpuInfoList.clear(); + auto gpuInfoList = clientStatus["gpu_info_list"].GetArray(); for (rapidjson::Value::ConstValueIterator itr = gpuInfoList.Begin(); itr != gpuInfoList.End(); ++itr) { - const rapidjson::Value& gpuInfo = (*itr)["gpu_info"]; - // TODO parsing + GPUInfo gpuInfo; + gpuInfo.parseFromJson((*itr)["gpu_info"]); + + m_gpuInfoList.push_back(gpuInfo); } } diff --git a/src/cc/GPUInfo.cpp b/src/cc/GPUInfo.cpp index e8f3f755..72ba83f9 100644 --- a/src/cc/GPUInfo.cpp +++ b/src/cc/GPUInfo.cpp @@ -18,6 +18,14 @@ #include "GPUInfo.h" GPUInfo::GPUInfo() + : m_deviceIdx(0), + m_rawIntensity(0), + m_workSize(0), + m_maxWorkSize(0), + m_freeMem(0), + m_memChunk(0), + m_compMode(0), + m_computeUnits(0) { } @@ -29,17 +37,63 @@ GPUInfo::~GPUInfo() rapidjson::Value GPUInfo::toJson(rapidjson::MemoryPoolAllocator & allocator) { + rapidjson::Value gpuInfo(rapidjson::kObjectType); + gpuInfo.AddMember("name", rapidjson::StringRef(m_name.c_str()), allocator); + gpuInfo.AddMember("device_idx", m_deviceIdx, allocator); + gpuInfo.AddMember("raw_intensity", m_rawIntensity, allocator); + gpuInfo.AddMember("work_size", m_workSize, allocator); + gpuInfo.AddMember("max_work_size", m_maxWorkSize, allocator); + gpuInfo.AddMember("free_mem", m_freeMem, allocator); + gpuInfo.AddMember("mem_chunk", m_memChunk, allocator); + gpuInfo.AddMember("comp_mode", m_memChunk, allocator); + gpuInfo.AddMember("compute_units", m_memChunk, allocator); + + return gpuInfo; } -bool GPUInfo::parseFromJson(const rapidjson::Document& document) +bool GPUInfo::parseFromJson(const rapidjson::Value& gpuInfo) { + bool result = false; -} + if (gpuInfo.HasMember("name")) { + m_name = gpuInfo["name"].GetString(); + result = true; + } -bool GPUInfo::parseFromJsonString(const std::string& json) -{ + if (gpuInfo.HasMember("device_idx")) { + m_deviceIdx = static_cast(gpuInfo["device_idx"].GetInt()); + } + + if (gpuInfo.HasMember("raw_intensity")) { + m_rawIntensity = static_cast(gpuInfo["raw_intensity"].GetInt()); + } + + if (gpuInfo.HasMember("work_size")) { + m_workSize = static_cast(gpuInfo["work_size"].GetInt()); + } + + if (gpuInfo.HasMember("max_work_size")) { + m_maxWorkSize = static_cast(gpuInfo["max_work_size"].GetInt()); + } + + if (gpuInfo.HasMember("free_mem")) { + m_freeMem = static_cast(gpuInfo["free_mem"].GetInt()); + } + + if (gpuInfo.HasMember("mem_chunk")) { + m_memChunk = gpuInfo["mem_chunk"].GetInt(); + } + + if (gpuInfo.HasMember("comp_mode")) { + m_compMode = gpuInfo["comp_mode"].GetInt(); + } + if (gpuInfo.HasMember("compute_units")) { + m_computeUnits = gpuInfo["compute_units"].GetInt(); + } + + return result; } size_t GPUInfo::getDeviceIdx() const @@ -72,24 +126,24 @@ void GPUInfo::setWorkSize(size_t workSize) m_workSize = workSize; } -size_t GPUInfo::getFreeMem() const +size_t GPUInfo::getMaxWorkSize() const { - return m_freeMem; + return m_maxWorkSize; } -void GPUInfo::setFreeMem(size_t freeMem) +void GPUInfo::setMaxWorkSize(size_t maxWorkSize) { - m_freeMem = freeMem; + m_maxWorkSize = maxWorkSize; } -int GPUInfo::getStridedIndex() const +size_t GPUInfo::getFreeMem() const { - return m_stridedIndex; + return m_freeMem; } -void GPUInfo::setStridedIndex(int stridedIndex) +void GPUInfo::setFreeMem(size_t freeMem) { - m_stridedIndex = stridedIndex; + m_freeMem = freeMem; } int GPUInfo::getMemChunk() const diff --git a/src/cc/GPUInfo.h b/src/cc/GPUInfo.h index 59f7123b..63194895 100644 --- a/src/cc/GPUInfo.h +++ b/src/cc/GPUInfo.h @@ -28,42 +28,33 @@ class GPUInfo ~GPUInfo(); rapidjson::Value toJson(rapidjson::MemoryPoolAllocator& allocator); - bool parseFromJsonString(const std::string& json); - bool parseFromJson(const rapidjson::Document& document); + bool parseFromJson(const rapidjson::Value& gpuInfo); std::string getName() const; void setName(const std::string& name); size_t getDeviceIdx() const; - void setDeviceIdx(size_t deviceIdx); size_t getRawIntensity() const; - void setRawIntensity(size_t rawIntensity); size_t getWorkSize() const; - void setWorkSize(size_t workSize); - size_t getFreeMem() const; + size_t getMaxWorkSize() const; + void setMaxWorkSize(size_t maxWorkSize); + size_t getFreeMem() const; void setFreeMem(size_t freeMem); - int getStridedIndex() const; - - void setStridedIndex(int stridedIndex); - int getMemChunk() const; - void setMemChunk(int memChunk); int getCompMode() const; - void setCompMode(int compMode); int getComputeUnits() const; - void setComputeUnits(int computeUnits); private: @@ -73,13 +64,11 @@ class GPUInfo size_t m_maxWorkSize; size_t m_freeMem; - int m_stridedIndex; int m_memChunk; int m_compMode; int m_computeUnits; std::string m_name; - }; From 8d9dbe2a683c03a78cf165fa49955c96182b3e24 Mon Sep 17 00:00:00 2001 From: BenDr0id Date: Tue, 14 Aug 2018 16:00:59 +0200 Subject: [PATCH 32/97] Refactoring to use common CC code on cpu/gpu --- src/App.cpp | 2 +- src/cc/CCClient.cpp | 111 +++++++++++++++++++++++--------------- src/cc/CCClient.h | 24 ++++++--- src/cc/ClientStatus.cpp | 3 -- src/cc/ControlCommand.cpp | 10 +++- 5 files changed, 94 insertions(+), 56 deletions(-) diff --git a/src/App.cpp b/src/App.cpp index f76c8bfd..9ca0abf6 100644 --- a/src/App.cpp +++ b/src/App.cpp @@ -137,7 +137,7 @@ int App::exec() if (m_controller->config()->ccHost() && m_controller->config()->ccPort() > 0) { uv_async_init(uv_default_loop(), &m_async, App::onCommandReceived); - m_ccclient = new CCClient(m_controller, &m_async); + m_ccclient = new CCClient(m_controller->config(), &m_async); if (!m_controller->config()->pools().front().isValid()) { LOG_WARN("No pool URL supplied, but CC server configured. Trying."); diff --git a/src/cc/CCClient.cpp b/src/cc/CCClient.cpp index e767f710..854ecbf0 100644 --- a/src/cc/CCClient.cpp +++ b/src/cc/CCClient.cpp @@ -21,18 +21,28 @@ #include #include <3rdparty/rapidjson/stringbuffer.h> #include <3rdparty/rapidjson/prettywriter.h> -#include -#include -#include -#include -#include + +#include "version.h" +#include "api/NetworkState.h" #include "CCClient.h" #include "App.h" #include "Cpu.h" #include "ControlCommand.h" +#ifdef TYPE_AMD_GPU #include "common/log/Log.h" +#include "common/log/RemoteLog.h" +#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" +#endif + #include "workers/Workers.h" #include "workers/Hashrate.h" @@ -48,8 +58,12 @@ CCClient* CCClient::m_self = nullptr; uv_mutex_t CCClient::m_mutex; -CCClient::CCClient(xmrig::Controller* controller, uv_async_t* async) - : m_controller(controller), +#ifdef TYPE_AMD_GPU +CCClient::CCClient(xmrig::Config* config, uv_async_t* async) +#else +CCClient::CCClient(Options* config, uv_async_t* async) +#endif + : m_config(config), m_async(async) { uv_mutex_init(&m_mutex); @@ -57,8 +71,8 @@ CCClient::CCClient(xmrig::Controller* controller, uv_async_t* async) m_self = this; std::string clientId; - if (m_controller->config()->ccWorkerId()) { - clientId = m_controller->config()->ccWorkerId(); + if (config->ccWorkerId()) { + clientId =m_self->m_config->ccWorkerId(); } else { char hostname[128]; memset(hostname, 0, sizeof(hostname)); @@ -67,27 +81,28 @@ CCClient::CCClient(xmrig::Controller* controller, uv_async_t* async) } m_clientStatus.setClientId(clientId); - - if (m_controller->config() != nullptr) { - m_clientStatus.setCurrentAlgoName(m_controller->config()->algorithm().name()); - } - - m_clientStatus.setHashFactor(0); m_clientStatus.setVersion(Version::string()); m_clientStatus.setCpuBrand(Cpu::brand()); m_clientStatus.setCpuAES(Cpu::hasAES()); - m_clientStatus.setCpuSockets(Cpu::sockets()); - m_clientStatus.setCpuCores(Cpu::cores()); - m_clientStatus.setCpuThreads(Cpu::threads()); + 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(Cpu::l2()); - m_clientStatus.setCpuL3(Cpu::l3()); - m_clientStatus.setCurrentThreads(static_cast(m_controller->config()->threads().size())); + m_clientStatus.setCpuL2(static_cast(Cpu::l2())); + m_clientStatus.setCpuL3(static_cast(Cpu::l3())); + +#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 (m_controller->config()->ccToken() != nullptr) { - m_authorization = std::string("Bearer ") + m_self->m_controller->config()->ccToken(); + if (config->ccToken() != nullptr) { + m_authorization = std::string("Bearer ") + m_self->m_config->ccToken(); } uv_thread_create(&m_thread, CCClient::onThreadStarted, this); @@ -126,11 +141,21 @@ void CCClient::updateNetworkState(const NetworkState& network) m_self->m_clientStatus.setHashesTotal(network.total); m_self->m_clientStatus.setAvgTime(network.avgTime()); +#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)); +#endif uv_mutex_unlock(&m_mutex); } @@ -172,10 +197,10 @@ void CCClient::publishClientStatusReport() auto res = performRequest(requestUrl, requestBuffer, "POST"); if (!res) { LOG_ERR("[CC-Client] error: unable to performRequest POST -> http://%s:%d%s", - m_self->m_controller->config()->ccHost(), m_self->m_controller->config()->ccPort(), requestUrl.c_str()); + m_self->m_config->ccHost(), m_self->m_config->ccPort(), requestUrl.c_str()); } else if (res->status != 200) { - LOG_ERR("[CC-Client] error: \"%d\" -> http://%s:%d%s", res->status, m_controller->config()->ccHost(), - m_self->m_controller->config()->ccPort(), requestUrl.c_str()); + 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()); } else { ControlCommand controlCommand; if (controlCommand.parseFromJsonString(res->body)) { @@ -215,14 +240,14 @@ void CCClient::updateConfig() auto res = performRequest(requestUrl, requestBuffer, "GET"); if (!res) { LOG_ERR("[CC-Client] error: unable to performRequest GET -> http://%s:%d%s", - m_self->m_controller->config()->ccHost(), m_self->m_controller->config()->ccPort(), requestUrl.c_str()); + m_self->m_config->ccHost(), m_self->m_config->ccPort(), requestUrl.c_str()); } else if (res->status != 200) { - LOG_ERR("[CC-Client] error: \"%d\" -> http://%s:%d%s", res->status, m_self->m_controller->config()->ccHost(), - m_self->m_controller->config()->ccPort(), requestUrl.c_str()); + 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()); } else { rapidjson::Document document; if (!document.Parse(res->body.c_str()).HasParseError()) { - std::ofstream clientConfigFile(m_self->m_controller->config()->fileName()); + std::ofstream clientConfigFile(m_self->m_config->fileName()); if (clientConfigFile) { rapidjson::StringBuffer buffer(0, 65536); rapidjson::PrettyWriter writer(buffer); @@ -234,7 +259,7 @@ void CCClient::updateConfig() LOG_WARN("[CC-Client] Config updated. -> trigger restart"); } else { - LOG_ERR("[CC-Client] Not able to store client config to file %s.", m_self->m_controller->config()->fileName()); + LOG_ERR("[CC-Client] Not able to store client config to file %s.", m_self->m_config->fileName()); } } else { LOG_ERR("[CC-Client] Not able to store client config. received client config is broken!"); @@ -247,7 +272,7 @@ void CCClient::publishConfig() std::string requestUrl = "/client/setClientConfig?clientId=" + m_self->m_clientStatus.getClientId(); std::stringstream data; - std::ifstream clientConfig(m_self->m_controller->config()->fileName()); + std::ifstream clientConfig(m_self->m_config->fileName()); if (clientConfig) { data << clientConfig.rdbuf(); @@ -267,16 +292,16 @@ 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_controller->config()->ccHost(), m_self->m_controller->config()->ccPort(), requestUrl.c_str()); + m_self->m_config->ccHost(), m_self->m_config->ccPort(), requestUrl.c_str()); } else if (res->status != 200) { - LOG_ERR("[CC-Client] error: \"%d\" -> http://%s:%d%s", res->status, m_self->m_controller->config()->ccHost(), - m_self->m_controller->config()->ccPort(), requestUrl.c_str()); + 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()); } } else { - LOG_ERR("Not able to send config. Client config %s is broken!", m_self->m_controller->config()->fileName()); + LOG_ERR("Not able to send config. Client config %s is broken!", m_self->m_config->fileName()); } } else { - LOG_ERR("Not able to load client config %s. Please make sure it exists!", m_self->m_controller->config()->fileName()); + LOG_ERR("Not able to load client config %s. Please make sure it exists!", m_self->m_config->fileName()); } } @@ -287,11 +312,11 @@ std::shared_ptr CCClient::performRequest(const std::string& r std::shared_ptr cli; # ifndef XMRIG_NO_TLS - if (m_self->m_controller->config()->ccUseTls()) { - cli = std::make_shared(m_self->m_controller->config()->ccHost(), m_self->m_controller->config()->ccPort(), 10); + if (m_self->m_config->ccUseTls()) { + cli = std::make_shared(m_self->m_config->ccHost(), m_self->m_config->ccPort(), 10); } else { # endif - cli = std::make_shared(m_self->m_controller->config()->ccHost(), m_self->m_controller->config()->ccPort(), 10); + cli = std::make_shared(m_self->m_config->ccHost(), m_self->m_config->ccPort(), 10); # ifndef XMRIG_NO_TLS } # endif @@ -338,10 +363,10 @@ void CCClient::onThreadStarted(void* handle) 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_controller->config()->ccUpdateInterval() * 1000), - static_cast(m_self->m_controller->config()->ccUpdateInterval() * 1000)); + static_cast(m_self->m_config->ccUpdateInterval() * 1000), + static_cast(m_self->m_config->ccUpdateInterval() * 1000)); - if (m_self->m_controller->config()->ccUploadConfigOnStartup()) { + if (m_self->m_config->ccUploadConfigOnStartup()) { m_self->publishConfig(); } diff --git a/src/cc/CCClient.h b/src/cc/CCClient.h index 9d6b2acf..289bdf2e 100644 --- a/src/cc/CCClient.h +++ b/src/cc/CCClient.h @@ -25,12 +25,14 @@ #include #include #include <3rdparty/cpp-httplib/httplib.h> -#include #include "ClientStatus.h" #include "version.h" #ifdef TYPE_AMD_GPU -#include +#include "amd/GpuContext.h" +#include "core/Controller.h" +#else +#include "Options.h" #endif class Hashrate; @@ -39,15 +41,18 @@ class NetworkState; class CCClient { public: - CCClient(xmrig::Controller* m_controller, uv_async_t* async); +#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); +#endif + ~CCClient(); static void updateHashrate(const Hashrate *hashrate); static void updateNetworkState(const NetworkState &results); -#ifdef TYPE_AMD_GPU - static void updateGpuInfo(const std::vector& network); -#endif private: void publishClientStatusReport(); @@ -62,7 +67,11 @@ class CCClient static void onThreadStarted(void *handle); static void onReport(uv_timer_t *handle); - const xmrig::Controller* m_controller; +#ifdef TYPE_AMD_GPU + const xmrig::Config* m_config; +#else + const Options* m_config; +#endif static CCClient* m_self; static uv_mutex_t m_mutex; @@ -77,7 +86,6 @@ class CCClient uv_timer_t m_timer; uv_loop_t m_client_loop; uv_thread_t m_thread; - }; #endif diff --git a/src/cc/ClientStatus.cpp b/src/cc/ClientStatus.cpp index 33f94c88..42182cc2 100644 --- a/src/cc/ClientStatus.cpp +++ b/src/cc/ClientStatus.cpp @@ -20,7 +20,6 @@ #include #include <3rdparty/rapidjson/stringbuffer.h> #include <3rdparty/rapidjson/prettywriter.h> -#include "common/log/Log.h" #include "ClientStatus.h" @@ -520,8 +519,6 @@ bool ClientStatus::parseFromJson(const rapidjson::Document& document) m_lastStatusUpdate = std::chrono::system_clock::to_time_t(time_point); result = true; - } else { - LOG_ERR("Parse Error, JSON does not contain: control_command"); } return result; diff --git a/src/cc/ControlCommand.cpp b/src/cc/ControlCommand.cpp index 7348c994..aeb5c4de 100644 --- a/src/cc/ControlCommand.cpp +++ b/src/cc/ControlCommand.cpp @@ -15,12 +15,20 @@ * along with this program. If not, see . */ +#include #include <3rdparty/rapidjson/stringbuffer.h> #include <3rdparty/rapidjson/prettywriter.h> -#include "common/log/Log.h" #include "ControlCommand.h" +#include "version.h" + +#ifdef TYPE_AMD_GPU +#include "common/log/Log.h" +#else +#include "log/Log.h" +#endif + ControlCommand::ControlCommand() : m_command(Command::START) { From b6dfbd8873ffc984834a7ab0f34cc7435bf8a481 Mon Sep 17 00:00:00 2001 From: BenDr0id Date: Thu, 16 Aug 2018 13:41:19 +0200 Subject: [PATCH 33/97] 1.7.0-beta1 preparation --- CHANGELOG.md | 75 +++++++-------------------------------------------- README.md | 1 + src/version.h | 6 ++--- 3 files changed, 13 insertions(+), 69 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5dbe0da6..674a445d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,66 +1,9 @@ -# v2.7.3-beta -- [#145](https://github.com/xmrig/xmrig-amd/issues/145) Added runtime linking with OpenCL ICD, **AMD APP SDK not required anymore**. -- [#140](https://github.com/xmrig/xmrig-amd/issues/140) `cryptonight-lite/ipbc` replaced to `cryptonight-heavy/tube` for **Bittube (TUBE)**. -- [#128](https://github.com/xmrig/xmrig-amd/issues/128) Improved `cryptonight/msr` support, removed usage restrictions. -- Added `cryptonight/rto` (cryptonight variant 1 with IPBC/TUBE mod) variant for **Arto (RTO)** coin. -- Added `cryptonight/xao` (original cryptonight with bigger iteration count) variant for **Alloy (XAO)** coin. -- Added option `opencl-loader` for custom path to OpenCL ICD. -- Vega APU (AMD Ryzen with embedded GPU) now excluded from autoconfig, reason: slow and cause BSOD. - -# v2.7.2-beta -- [#132](https://github.com/xmrig/xmrig-amd/issues/132) Fixed regression, command line option `--opencl-platform` was broken. - -# v2.7.1-beta -- [#130](https://github.com/xmrig/xmrig-amd/issues/130) **Added OpenCL cache support**. - - Added config option `cache` and command line option `--no-cache` to allow disable cache. -- **Added support for new cryptonight-heavy variant xhv** (`cn-heavy/xhv`) for Haven Protocol fork. -- [#128](https://github.com/xmrig/xmrig-amd/issues/128) **Added support for new cryptonight variant msr** (`cn/msr`) also known as `cryptonight-fast` for Masari fork. -- [#126](https://github.com/xmrig/xmrig-amd/issues/126) Fixed regression, command line option `--print-platforms` was broken. -- [#127](https://github.com/xmrig/xmrig-amd/issues/127) Fixed regression, miner was not exit if OpenCL errors happen. - -# v2.7.0-beta -- **Added support for cryptonight-lite variant ipbc** (`cn-lite/ipbc`) for BitTube also was known as IPBC. -- **Added support for cryptonight variant xtl** (`cn/xtl`) for Stellite. -- Added [config options](https://github.com/xmrig/xmrig-amd/blob/dev/doc/THREADS.md) `strided_index`, `mem_chunk` and `comp_mode`. -- Added new detailed hashrate report. -- Added command line option `--dry-run`. - -# v2.6.1 -- Fixed critical bug, in some cases miner was can't recovery connection and switch to failover pool, version 2.5.2 and v2.6.0-beta1 affected. -- [#499](https://github.com/xmrig/xmrig/issues/499) IPv6 support disabled for internal HTTP API. -- Added workaround for nicehash.com if you use `cryptonightv7..nicehash.com` option `variant=1` will be set automatically. - -# v2.6.0-beta1 - - [#476](https://github.com/xmrig/xmrig/issues/476) **Added Cryptonight-Heavy support for Sumokoin ASIC resistance fork.** - -# v2.5.2 -- [#448](https://github.com/xmrig/xmrig/issues/478) Fixed broken reconnect. - -# v2.5.1 -- [#454](https://github.com/xmrig/xmrig/issues/454) Fixed build with libmicrohttpd version below v0.9.35. -- [#456](https://github.com/xmrig/xmrig/issues/459) Verbose errors related to donation pool was not fully silenced. -- [#459](https://github.com/xmrig/xmrig/issues/459) Fixed regression (version 2.5.0 affected) with connection to **xmr.f2pool.com**. - -# v2.5.0 -- [#434](https://github.com/xmrig/xmrig/issues/434) **Added support for Monero v7 PoW, scheduled on April 6.** -- Added full IPv6 support. -- Added protocol extension, when use the miner with xmrig-proxy 2.5+ no more need manually specify `nicehash` option. -- [#51](https://github.com/xmrig/xmrig-amd/issues/51) Fixed multiple pools in initial config was saved incorrectly. -- [#123](https://github.com/xmrig/xmrig-proxy/issues/123) Fixed regression (all versions since 2.4 affected) fragmented responses from pool/proxy was parsed incorrectly. - -# v2.4.5 - - [#49](https://github.com/xmrig/xmrig-amd/issues/49) Fixed, in some cases, pause was cause an infinite loop. - - [#200](https://github.com/xmrig/xmrig/issues/200) In some cases miner was doesn't write log to stdout. - - Added libmicrohttpd version to --version output. - - Fixed bug in singal handler, in some cases miner wasn't shutdown properly. - - Fixed recent MSVC 2017 version detection. - -# v2.4.3-beta2 - - Fixed, auto config wasn't write opencl-platform to config.json. - - Added command line option `--print-platforms`. - - Fixed 32 bit build. - - [#2](https://github.com/xmrig/xmrig-amd/issues/2) Fixed Linux build. - - [#3](https://github.com/xmrig/xmrig-amd/issues/3) Fixed macOS build. - -# v2.4.3-beta1 - - First public release. +# 1.7.0 +- First official Release of XMRigCC-amd base on XMRig-amd 2.7.3-beta #33 #3 +- Full integration of xmrigCC-amd into XMRigCCServer/Dashboard with GPUInfo / remote logging +- All features from XMRigCC 1.7.0 (except TLS on stratum) + - Remote restart/start/pause/stop + - Remote stats/info + - Remote log + - Remote configuration + - ... diff --git a/README.md b/README.md index 8c9ff2be..7d9c4ad0 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,7 @@ xmrigCCServer --cc-port=3344 --cc-user=admin --cc-pass=pass --cc-access-token=SE --cc-client-config-folder=FOLDER Folder contains the client config files --cc-custom-dashboard=FILE loads a custom dashboard and serve it to '/' --no-color disable colored output + --cc-client-log-lines-history=N maximum lines of log history kept per miner (default: 100) -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 diff --git a/src/version.h b/src/version.h index 3fb83705..c0423598 100644 --- a/src/version.h +++ b/src/version.h @@ -36,14 +36,14 @@ #define APP_DESC "XMRigCC-AMD OpenCL miner" #define APP_COPYRIGHT "Copyright (C) 2017- BenDr0id" #endif -#define APP_VERSION "1.6.6_beta1 (based on XMRig)" +#define APP_VERSION "1.7.0_beta1 (based on XMRig)" #define APP_DOMAIN "" #define APP_SITE "https://github.com/Bendr0id/xmrigCC-amd" #define APP_KIND "amd" #define APP_VER_MAJOR 1 -#define APP_VER_MINOR 6 -#define APP_VER_PATCH 6 +#define APP_VER_MINOR 7 +#define APP_VER_PATCH 0 #define TYPE_AMD_GPU From 5b56caeb6a050f428e85e84f6754fe1390c5fd57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Thu, 16 Aug 2018 23:05:16 +0200 Subject: [PATCH 34/97] Fix mvc build --- src/cc/ClientStatus.cpp | 2 +- src/cc/ControlCommand.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc/ClientStatus.cpp b/src/cc/ClientStatus.cpp index 42182cc2..7f84d8cd 100644 --- a/src/cc/ClientStatus.cpp +++ b/src/cc/ClientStatus.cpp @@ -377,7 +377,7 @@ bool ClientStatus::parseFromJson(const rapidjson::Document& document) if (document.HasMember("client_status")) { - rapidjson::Value::ConstObject clientStatus = document["client_status"].GetObject(); + const rapidjson::Value& clientStatus = document["client_status"]; if (clientStatus.HasMember("current_status")) { m_currentStatus = toStatus(clientStatus["current_status"].GetString()); diff --git a/src/cc/ControlCommand.cpp b/src/cc/ControlCommand.cpp index aeb5c4de..afdda3dd 100644 --- a/src/cc/ControlCommand.cpp +++ b/src/cc/ControlCommand.cpp @@ -58,7 +58,7 @@ bool ControlCommand::parseFromJson(const rapidjson::Document& document) bool result = false; if (document.HasMember("control_command")) { - rapidjson::Value::ConstObject controlCommand = document["control_command"].GetObject(); + const rapidjson::Value& controlCommand = document["control_command"]; if (controlCommand.HasMember("command")) { m_command = toCommand(controlCommand["command"].GetString()); result = true; From 9dafc88859f13688fa78de38deb7dcd637b41b4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Thu, 16 Aug 2018 23:05:16 +0200 Subject: [PATCH 35/97] Fix mvc build --- src/cc/ClientStatus.cpp | 2 +- src/cc/ControlCommand.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc/ClientStatus.cpp b/src/cc/ClientStatus.cpp index 42182cc2..7f84d8cd 100644 --- a/src/cc/ClientStatus.cpp +++ b/src/cc/ClientStatus.cpp @@ -377,7 +377,7 @@ bool ClientStatus::parseFromJson(const rapidjson::Document& document) if (document.HasMember("client_status")) { - rapidjson::Value::ConstObject clientStatus = document["client_status"].GetObject(); + const rapidjson::Value& clientStatus = document["client_status"]; if (clientStatus.HasMember("current_status")) { m_currentStatus = toStatus(clientStatus["current_status"].GetString()); diff --git a/src/cc/ControlCommand.cpp b/src/cc/ControlCommand.cpp index aeb5c4de..afdda3dd 100644 --- a/src/cc/ControlCommand.cpp +++ b/src/cc/ControlCommand.cpp @@ -58,7 +58,7 @@ bool ControlCommand::parseFromJson(const rapidjson::Document& document) bool result = false; if (document.HasMember("control_command")) { - rapidjson::Value::ConstObject controlCommand = document["control_command"].GetObject(); + const rapidjson::Value& controlCommand = document["control_command"]; if (controlCommand.HasMember("command")) { m_command = toCommand(controlCommand["command"].GetString()); result = true; From 5b3865763ce44b8ae9582faab24b03d730fa0d12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Mon, 20 Aug 2018 11:51:40 +0200 Subject: [PATCH 36/97] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7d9c4ad0..8c3c560d 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ GPU mining part based on [Wolf9466](https://github.com/OhGodAPet) and [psychocry **XMRigCC Server** -![Screenshot of XMRigCC Server](https://i.imgur.com/0Ke9gIg.png) +![Screenshot of XMRigCC Server](https://i.imgur.com/iS1RzgO.png) **XMRigCC Dashboard** From c9416f003621d8c088e8cf284e2820bcb025790e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Mon, 27 Aug 2018 21:10:32 +0200 Subject: [PATCH 37/97] 1.7.0 Preparation --- CMakeLists.txt | 9 +++++++-- src/version.h | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ccd14c5b..c834840e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,8 +12,13 @@ option(WITH_CC_CLIENT "CC Client" ON) option(WITH_TLS "TLS support" ON) option(BUILD_STATIC "Build static binary" OFF) -set(MINER_EXECUTABLE_NAME "xmrigMiner-amd" CACHE STRING "Miner executable file name") -set(DAEMON_EXECUTABLE_NAME "xmrigDaemon-amd" CACHE STRING "Daemon executable file name") +if(NOT MINER_EXECUTABLE_NAME) + set(MINER_EXECUTABLE_NAME "xmrigMiner-amd" CACHE STRING "Miner executable file name") +endif(NOT MINER_EXECUTABLE_NAME) + +if(NOT DAEMON_EXECUTABLE_NAME) + set(DAEMON_EXECUTABLE_NAME "xmrigDaemon-amd" CACHE STRING "Daemon executable file name") +endif(NOT DAEMON_EXECUTABLE_NAME) include (CheckIncludeFile) include (cmake/cpu.cmake) diff --git a/src/version.h b/src/version.h index c0423598..0cfc49b6 100644 --- a/src/version.h +++ b/src/version.h @@ -36,7 +36,7 @@ #define APP_DESC "XMRigCC-AMD OpenCL miner" #define APP_COPYRIGHT "Copyright (C) 2017- BenDr0id" #endif -#define APP_VERSION "1.7.0_beta1 (based on XMRig)" +#define APP_VERSION "1.7.0 (based on XMRig)" #define APP_DOMAIN "" #define APP_SITE "https://github.com/Bendr0id/xmrigCC-amd" #define APP_KIND "amd" From 9e891d124ea09f01710b90522feae633ee9271a0 Mon Sep 17 00:00:00 2001 From: BenDr0id Date: Wed, 12 Sep 2018 11:05:47 +0200 Subject: [PATCH 38/97] Fix compile issue --- src/common/config/CommonConfig.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/common/config/CommonConfig.h b/src/common/config/CommonConfig.h index 215092e9..957e7e4c 100644 --- a/src/common/config/CommonConfig.h +++ b/src/common/config/CommonConfig.h @@ -53,6 +53,7 @@ class CommonConfig : public IConfig inline bool ccUseRemoteLogging() const { return m_ccUseRemoteLogging; } inline bool ccUploadConfigOnStartup() const { return m_ccUploadConfigOnStartup; } inline const char *apiToken() const { return m_apiToken.data(); } + inline const char *apiId() const { return m_apiId.data(); } inline const char *apiWorkerId() const { return m_apiWorkerId.data(); } inline const char *logFile() const { return m_logFile.data(); } inline const char *userAgent() const { return m_userAgent.data(); } From 72bd3d89e6fc22f2df1d7f578df9a16747cd99a1 Mon Sep 17 00:00:00 2001 From: Petr Volf Date: Wed, 12 Sep 2018 11:57:55 +0200 Subject: [PATCH 39/97] RapidJSON fix, the same as for xmrigCC. --- src/cc/GPUInfo.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cc/GPUInfo.cpp b/src/cc/GPUInfo.cpp index 72ba83f9..772adf67 100644 --- a/src/cc/GPUInfo.cpp +++ b/src/cc/GPUInfo.cpp @@ -40,11 +40,11 @@ rapidjson::Value GPUInfo::toJson(rapidjson::MemoryPoolAllocator (m_deviceIdx), allocator); + gpuInfo.AddMember("raw_intensity", static_cast(m_rawIntensity), allocator); + gpuInfo.AddMember("work_size", static_cast(m_workSize), allocator); + gpuInfo.AddMember("max_work_size", static_cast(m_maxWorkSize), allocator); + gpuInfo.AddMember("free_mem", static_cast(m_freeMem), allocator); gpuInfo.AddMember("mem_chunk", m_memChunk, allocator); gpuInfo.AddMember("comp_mode", m_memChunk, allocator); gpuInfo.AddMember("compute_units", m_memChunk, allocator); From ceb8036a36db7d8b9c0b9b0396dcd70d7bdd08a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Fri, 19 Oct 2018 22:30:53 +0200 Subject: [PATCH 40/97] Fixed merge conflicts/build --- CMakeLists.txt | 8 +- cmake/OpenSSL.cmake | 23 ---- src/cc/CCClient.cpp | 22 +++- src/common/config/CommonConfig.h | 154 +------------------------- src/core/ConfigLoader_platform.h | 22 ++-- src/net/Network.cpp | 2 +- src/net/strategies/DonateStrategy.cpp | 22 ++-- 7 files changed, 51 insertions(+), 202 deletions(-) delete mode 100644 cmake/OpenSSL.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 1b7eb742..aa3a5bac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -206,8 +206,6 @@ add_definitions(/DCL_TARGET_OPENCL_VERSION=200) add_definitions(/DCL_USE_DEPRECATED_OPENCL_1_2_APIS) -include(cmake/OpenSSL.cmake) - CHECK_INCLUDE_FILE (syslog.h HAVE_SYSLOG_H) if (HAVE_SYSLOG_H) add_definitions(/DHAVE_SYSLOG_H) @@ -264,7 +262,7 @@ if (WITH_TLS) if (OPENSSL_FOUND) include_directories(${OPENSSL_INCLUDE_DIR}) - #set(SOURCES_SSL_TLS src/net/BoostTlsConnection.cpp) + set(SOURCES_SSL_TLS src/common/net/Tls.cpp) else() message(FATAL_ERROR "OpenSSL NOT found: use `-DWITH_TLS=OFF` to build without TLS support") endif() @@ -291,7 +289,7 @@ include_directories(src/3rdparty) include_directories(${UV_INCLUDE_DIR}) if (WITH_TLS) - #add_library(xmrig_tls STATIC ${SOURCES_SSL_TLS}) + add_library(xmrig_tls STATIC ${SOURCES_SSL_TLS}) endif (WITH_TLS) if (BUILD_STATIC) @@ -309,7 +307,7 @@ add_executable(${PROJECT_NAME} ${HEADERS} ${SOURCES} ${SOURCES_OS} ${HEADERS_CRY target_link_libraries(${PROJECT_NAME} ${OPENSSL_LIBRARIES} ${UV_LIBRARIES} ${MHD_LIBRARY} ${EXTRA_LIBS} ${LIBS}) if (WITH_TLS) - #target_link_libraries(xmrigMiner-amd xmrig_tls ${OPENSSL_LIBRARIES} ${EXTRA_LIBS}) + target_link_libraries(xmrigMiner-amd xmrig_tls ${OPENSSL_LIBRARIES} ${EXTRA_LIBS}) target_link_libraries(xmrigMiner-amd ${OPENSSL_LIBRARIES} ${EXTRA_LIBS}) endif (WITH_TLS) diff --git a/cmake/OpenSSL.cmake b/cmake/OpenSSL.cmake deleted file mode 100644 index ed287e7e..00000000 --- a/cmake/OpenSSL.cmake +++ /dev/null @@ -1,23 +0,0 @@ -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/common/net/Tls.h src/common/net/Tls.cpp) - include_directories(${OPENSSL_INCLUDE_DIR}) - else() - message(FATAL_ERROR "OpenSSL NOT found: use `-DWITH_TLS=OFF` to build without TLS support") - endif() -else() - set(TLS_SOURCES "") - set(OPENSSL_LIBRARIES "") - add_definitions(/DXMRIG_NO_TLS) -endif() diff --git a/src/cc/CCClient.cpp b/src/cc/CCClient.cpp index 854ecbf0..7eb88cc2 100644 --- a/src/cc/CCClient.cpp +++ b/src/cc/CCClient.cpp @@ -27,15 +27,16 @@ #include "CCClient.h" #include "App.h" -#include "Cpu.h" #include "ControlCommand.h" #ifdef TYPE_AMD_GPU +#include "common/cpu/Cpu.h" #include "common/log/Log.h" #include "common/log/RemoteLog.h" #include "common/Platform.h" #include "core/Config.h" #else +#include "Cpu.h" #include "Mem.h" #include "log/Log.h" #include "log/RemoteLog.h" @@ -82,6 +83,20 @@ CCClient::CCClient(Options* config, uv_async_t* async) m_clientStatus.setClientId(clientId); m_clientStatus.setVersion(Version::string()); + + +#ifdef TYPE_AMD_GPU + m_clientStatus.setCpuBrand(xmrig::Cpu::info()->brand()); + m_clientStatus.setCpuAES(xmrig::Cpu::info()->hasAES()); + m_clientStatus.setCpuSockets(static_cast(xmrig::Cpu::info()->sockets())); + m_clientStatus.setCpuCores(static_cast(xmrig::Cpu::info()->cores())); + m_clientStatus.setCpuThreads(static_cast(xmrig::Cpu::info()->threads())); + m_clientStatus.setCpuX64(xmrig::Cpu::info()->isX64()); + m_clientStatus.setCpuL2(static_cast(xmrig::Cpu::info()->L2())); + m_clientStatus.setCpuL3(static_cast(xmrig::Cpu::info()->L3())); + m_clientStatus.setCurrentThreads(static_cast(config->threads().size())); + m_clientStatus.setCurrentAlgoName(config->algorithm().name()); +#else m_clientStatus.setCpuBrand(Cpu::brand()); m_clientStatus.setCpuAES(Cpu::hasAES()); m_clientStatus.setCpuSockets(static_cast(Cpu::sockets())); @@ -90,11 +105,6 @@ CCClient::CCClient(Options* config, uv_async_t* async) m_clientStatus.setCpuX64(Cpu::isX64()); m_clientStatus.setCpuL2(static_cast(Cpu::l2())); m_clientStatus.setCpuL3(static_cast(Cpu::l3())); - -#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 diff --git a/src/common/config/CommonConfig.h b/src/common/config/CommonConfig.h index 3201209c..bb7e1086 100644 --- a/src/common/config/CommonConfig.h +++ b/src/common/config/CommonConfig.h @@ -1,4 +1,4 @@ - +/* * Copyright 2010 Jeff Garzik * Copyright 2012-2014 pooler * Copyright 2014 Lucas Jones @@ -42,150 +42,6 @@ class CommonConfig : public IConfig public: CommonConfig(); - inline bool isApiIPv6() const { return m_apiIPv6; } - inline bool isApiRestricted() const { return m_apiRestricted; } - inline bool isBackground() const { return m_background; } - inline bool isColors() const { return m_colors; } - inline bool isDryRun() const { return m_dryRun; } - inline bool isSyslog() const { return m_syslog; } - inline const char *apiId() const { return m_apiId.data(); } - inline const char *apiToken() const { return m_apiToken.data(); } - inline const char *apiWorkerId() const { return m_apiWorkerId.data(); } - inline const char *logFile() const { return m_logFile.data(); } - inline const char *userAgent() const { return m_userAgent.data(); } - inline const std::vector &pools() const { return m_activePools; } - inline int apiPort() const { return m_apiPort; } - inline int donateLevel() const { return m_donateLevel; } - inline int printTime() const { return m_printTime; } - inline int retries() const { return m_retries; } - inline int retryPause() const { return m_retryPause; } - inline int ccUpdateInterval() const { return m_ccUpdateInterval; } - inline int ccPort() const { return m_ccPort; } - inline void setColors(bool colors) { m_colors = colors; } - - inline bool isWatch() const override { return m_watch && !m_fileName.isNull(); } - inline bool isDaemonized() const override { return m_daemonized; } - inline const Algorithm &algorithm() const override { return m_algorithm; } - inline const char *fileName() const override { return m_fileName.data(); } - - bool save() override; - - void printAPI(); - void printPools(); - void printVersions(); - -protected: - enum State { - NoneState, - ReadyState, - ErrorState - }; - - bool finalize() override; - bool parseBoolean(int key, bool enable) override; - bool parseString(int key, const char *arg) override; - bool parseUint64(int key, uint64_t arg) override; - bool parseCCUrl(const char* url) override; - void setFileName(const char *fileName) override; - - Algorithm m_algorithm; - bool m_adjusted; - bool m_apiIPv6; - bool m_apiRestricted; - bool m_autoSave; - bool m_background; - bool m_colors; - bool m_dryRun; - bool m_syslog; - bool m_watch; - bool m_daemonized; - bool m_ccUseTls; - bool m_ccUseRemoteLogging; - bool m_ccUploadConfigOnStartup; - int m_apiPort; - int m_donateLevel; - int m_printTime; - int m_retries; - int m_retryPause; - int m_ccUpdateInterval; - int m_ccPort; - State m_state; - std::vector m_activePools; - std::vector m_pools; - xmrig::c_str m_apiId; - xmrig::c_str m_apiToken; - xmrig::c_str m_apiWorkerId; - xmrig::c_str m_fileName; - xmrig::c_str m_logFile; - xmrig::c_str m_userAgent; - xmrig::c_str m_ccUrl; - xmrig::c_str m_ccHost; - xmrig::c_str m_ccToken; - xmrig::c_str m_ccWorkerId; - -private: - bool parseInt(int key, int arg); -}; - - -} /* namespace xmrig */ - -#endif /* __COMMONCONFIG_H__ */ - - * 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 . - */ - -#ifndef XMRIG_COMMONCONFIG_H -#define XMRIG_COMMONCONFIG_H - - -#include - - -#include "common/interfaces/IConfig.h" -#include "common/net/Pool.h" -#include "common/utils/c_str.h" -#include "common/xmrig.h" - - -namespace xmrig { - - -class CommonConfig : public IConfig -{ -public: - CommonConfig(); - - inline bool isApiIPv6() const { return m_apiIPv6; } - inline bool isApiRestricted() const { return m_apiRestricted; } - inline bool isBackground() const { return m_background; } - inline bool isColors() const { return m_colors; } - inline bool isDryRun() const { return m_dryRun; } - inline bool isSyslog() const { return m_syslog; } - inline const char *apiId() const { return m_apiId.data(); } - inline const char *apiToken() const { return m_apiToken.data(); } - inline const char *apiWorkerId() const { return m_apiWorkerId.data(); } - inline const char *logFile() const { return m_logFile.data(); } - inline const char *userAgent() const { return m_userAgent.data(); } inline bool isApiIPv6() const { return m_apiIPv6; } inline bool isApiRestricted() const { return m_apiRestricted; } inline bool isAutoSave() const { return m_autoSave; } @@ -201,10 +57,10 @@ class CommonConfig : public IConfig inline const char *apiWorkerId() const { return m_apiWorkerId.data(); } inline const char *logFile() const { return m_logFile.data(); } inline const char *userAgent() const { return m_userAgent.data(); } - inline const char *ccUrl() const { return m_ccUrl.data(); } - inline const char *ccHost() const { return m_ccHost.data(); } - inline const char *ccToken() const { return m_ccToken.data(); } - inline const char *ccWorkerId() const { return m_ccWorkerId.data(); } + inline const char *ccUrl() const { return m_ccUrl.data(); } + inline const char *ccHost() const { return m_ccHost.data(); } + inline const char *ccToken() const { return m_ccToken.data(); } + inline const char *ccWorkerId() const { return m_ccWorkerId.data(); } inline const std::vector &pools() const { return m_activePools; } inline int apiPort() const { return m_apiPort; } diff --git a/src/core/ConfigLoader_platform.h b/src/core/ConfigLoader_platform.h index 210b9c13..2f96cda9 100644 --- a/src/core/ConfigLoader_platform.h +++ b/src/core/ConfigLoader_platform.h @@ -92,8 +92,8 @@ Options:\n\ --api-worker-id=ID custom worker-id for API\n\ --api-id=ID custom instance ID for API\n\ --api-ipv6 enable IPv6 support for API\n\ - --api-no-restricted enable full remote access (only if API token set)\n" - --dry-run test configuration and exit\n\ + --api-no-restricted enable full remote access (only if API token set)\n\ + --dry-run test configuration and exit\n" # ifndef XMRIG_NO_CC "\ --cc-url=URL url (host:port) of the CC Server\n\ @@ -101,8 +101,8 @@ Options:\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-remote-logging-max-rows=N maximum last n-log rows to send CC Server\n" - --cc-use-remote-logging enable remote logging on CC Server\n\ + --cc-remote-logging-max-rows=N maximum last n-log rows to send CC Server\n\ + --cc-use-remote-logging enable remote logging on CC Server\n" # endif "\ -h, --help display this help and exit\n\ @@ -156,15 +156,15 @@ static struct option const options[] = { { "no-cache", 0, nullptr, xmrig::IConfig::OclCacheKey }, { "print-platforms", 0, nullptr, xmrig::IConfig::OclPrintKey }, { "opencl-loader", 1, nullptr, xmrig::IConfig::OclLoaderKey }, - { "cc-url", 1, nullptr, xmrig::IConfig::CCUrlKey }, - { "cc-access-token", 1, nullptr, xmrig::IConfig::CCAccessTokenKey }, - { "cc-worker-id", 1, nullptr, xmrig::IConfig::CCWorkerIdKey }, + { "cc-url", 1, nullptr, xmrig::IConfig::CCUrlKey }, + { "cc-access-token", 1, nullptr, xmrig::IConfig::CCAccessTokenKey }, + { "cc-worker-id", 1, nullptr, xmrig::IConfig::CCWorkerIdKey }, { "cc-update-interval-s", 1, nullptr, xmrig::IConfig::CCUpdateIntervalKey }, - { "cc-use-tls", 0, nullptr, xmrig::IConfig::CCUseTlsKey }, + { "cc-use-tls", 0, nullptr, xmrig::IConfig::CCUseTlsKey }, { "cc-use-remote-logging", 0, nullptr, xmrig::IConfig::CCUseRemoteLoggingKey }, { "cc-upload-config-on-startup", 0, nullptr, xmrig::IConfig::CCUploadConfigOnStartupKey }, - { "daemonized", 0, nullptr, xmrig::IConfig::DaemonizedKey }, - { nullptr, 0, nullptr, 0 } + { "daemonized", 0, nullptr, xmrig::IConfig::DaemonizedKey }, + { nullptr, 0, nullptr, 0 } }; @@ -222,7 +222,7 @@ static struct option const cc_client_options[] = { { "update-interval-s", 1, nullptr, xmrig::IConfig::CCUpdateIntervalKey }, { "use-tls", 0, nullptr, xmrig::IConfig::CCUseTlsKey }, { "use-remote-logging", 0, nullptr, xmrig::IConfig::CCUseRemoteLoggingKey }, - { "cc-upload-config-on-startup",1, nullptr, xmrig::IConfig::CCUploadConfigOnStartupKey }, + { "upload-config-on-startup", 1, nullptr, xmrig::IConfig::CCUploadConfigOnStartupKey }, { 0, 0, 0, 0 } }; diff --git a/src/net/Network.cpp b/src/net/Network.cpp index 9be2eaa8..d71d7b7e 100644 --- a/src/net/Network.cpp +++ b/src/net/Network.cpp @@ -179,7 +179,7 @@ void Network::setJob(Client *client, const Job &job, bool donate) : "new job from %s:%d diff %d algo %s", client->host(), client->port(), job.diff(), job.algorithm().shortName()); - m_state.powVariant = job.variant(); + m_state.powVariant = job.algorithm().variant(); m_state.diff = job.diff(); Workers::setJob(job, donate); } diff --git a/src/net/strategies/DonateStrategy.cpp b/src/net/strategies/DonateStrategy.cpp index dbb2bbe2..ddb384dd 100644 --- a/src/net/strategies/DonateStrategy.cpp +++ b/src/net/strategies/DonateStrategy.cpp @@ -51,15 +51,23 @@ DonateStrategy::DonateStrategy(int level, const char *user, xmrig::Algo algo, IS xmrig::keccak(reinterpret_cast(user), strlen(user), hash); Job::toHex(hash, 32, userId); - if (algorithm.algo() == xmrig::CRYPTONIGHT_HEAVY) { - m_pools.push_back(Pool("donate.graef.in", 8443,userId, nullptr, false, true)); - } else if (algorithm.algo() == xmrig::CRYPTONIGHT_LITE) { - m_pools.push_back(Pool("donate.graef.in", 1080, userId, nullptr, false, true)); +#ifndef XMRIG_NO_TLS + if (algo == xmrig::Algo::CRYPTONIGHT_HEAVY) { + m_pools.push_back(Pool("donate2.graef.in", 8443, userId, nullptr, true, false, true)); + } else if (algo == xmrig::Algo::CRYPTONIGHT_LITE) { + m_pools.push_back(Pool("donate2.graef.in", 1080, userId, nullptr, true, false, true)); } else { - m_pools.push_back(Pool("donate2.graef.in", 80, userId, nullptr, false, true)); + m_pools.push_back(Pool("donate2.graef.in", 443, userId, nullptr, true, false, true)); } - - m_pools.push_back(Pool("donate.v2.xmrig.com", 3333, userId, nullptr, false, true)); +#else + if (algo == xmrig::Algo::CRYPTONIGHT_HEAVY) { + m_pools.push_back(Pool("donate.graef.in", 8443, userId, nullptr, false, false, true)); + } else if (algo == xmrig::Algo::CRYPTONIGHT_LITE) { + m_pools.push_back(Pool("donate.graef.in", 1080, userId, nullptr, false, false, true)); + } else { + m_pools.push_back(Pool("donate2.graef.in", 80, userId, nullptr, false, false, true)); + } +#endif for (Pool &pool : m_pools) { pool.adjust(xmrig::Algorithm(algo, xmrig::VARIANT_AUTO)); From bbd849d2b7b679892db26d221f2afc5135eca9b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Fri, 19 Oct 2018 22:37:38 +0200 Subject: [PATCH 41/97] Changed version --- src/common/config/CommonConfig.cpp | 6 +++--- src/version.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/common/config/CommonConfig.cpp b/src/common/config/CommonConfig.cpp index 8c2b9b30..c1b70367 100644 --- a/src/common/config/CommonConfig.cpp +++ b/src/common/config/CommonConfig.cpp @@ -159,9 +159,9 @@ void xmrig::CommonConfig::printVersions() snprintf(buf, sizeof buf, "MSVC/%d", MSVC_VERSION); # endif - Log::i()->text(isColors() ? GREEN_BOLD(" * ") WHITE_BOLD("%-13s") CYAN_BOLD("%s/%s") WHITE_BOLD(" %s") - : " * %-13s%s/%s %s", - "ABOUT", APP_NAME, APP_VERSION, buf); + Log::i()->text(isColors() ? GREEN_BOLD(" * ") WHITE_BOLD("%-13s") CYAN_BOLD("%s/%s") WHITE_BOLD(" %s") CYAN_BOLD(" (%s)") + : " * %-13s%s/%s %s (%s)", + "ABOUT", APP_NAME, APP_VERSION, buf, BUILD_TYPE); # if defined(XMRIG_AMD_PROJECT) # if CL_VERSION_2_0 diff --git a/src/version.h b/src/version.h index 907eec83..bc2265f9 100644 --- a/src/version.h +++ b/src/version.h @@ -28,7 +28,7 @@ #define APP_NAME "XMRigCC-AMD" #define APP_COPYRIGHT "Copyright (C) 2017- BenDr0id" #define APP_DESC "XMRigCC-AMD OpenCL miner" -#define APP_VERSION "1.7.0 (based on XMRig)" +#define APP_VERSION "1.8.0 (based on XMRig)" #define APP_SITE "https://github.com/Bendr0id/xmrigCC-amd" #define APP_DOMAIN "" #define APP_KIND "amd" From 4e3b609216df9e5ec27da79802b6e0be8cfd0270 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Fri, 19 Oct 2018 22:44:24 +0200 Subject: [PATCH 42/97] Merge conflicts on CMakeLists --- CMakeLists.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index aa3a5bac..8c638c58 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,16 @@ 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) + +option(WITH_AEON "CryptoNight-Lite support" ON) +option(WITH_SUMO "CryptoNight-Heavy support" ON) +option(WITH_HTTPD "HTTP REST API" OFF) +option(WITH_CC_CLIENT "CC Client" ON) +option(WITH_TLS "TLS support" ON) +option(BUILD_STATIC "Build static binary" OFF) option(STRICT_CACHE "Enable strict checks for OpenCL cache" ON) option(WITH_DEBUG_LOG "Enable debug log output" OFF) From 940e7dd1ba2578e325ef2d4d068eb9dc866105cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Sat, 20 Oct 2018 15:42:59 +0200 Subject: [PATCH 43/97] Cleanup --- src/cc/CCClient.cpp | 5 ----- src/common/net/Client.cpp | 3 ++- src/version.h | 2 +- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/cc/CCClient.cpp b/src/cc/CCClient.cpp index 7eb88cc2..7797793b 100644 --- a/src/cc/CCClient.cpp +++ b/src/cc/CCClient.cpp @@ -88,12 +88,7 @@ CCClient::CCClient(Options* config, uv_async_t* async) #ifdef TYPE_AMD_GPU m_clientStatus.setCpuBrand(xmrig::Cpu::info()->brand()); m_clientStatus.setCpuAES(xmrig::Cpu::info()->hasAES()); - m_clientStatus.setCpuSockets(static_cast(xmrig::Cpu::info()->sockets())); - m_clientStatus.setCpuCores(static_cast(xmrig::Cpu::info()->cores())); - m_clientStatus.setCpuThreads(static_cast(xmrig::Cpu::info()->threads())); m_clientStatus.setCpuX64(xmrig::Cpu::info()->isX64()); - m_clientStatus.setCpuL2(static_cast(xmrig::Cpu::info()->L2())); - m_clientStatus.setCpuL3(static_cast(xmrig::Cpu::info()->L3())); m_clientStatus.setCurrentThreads(static_cast(config->threads().size())); m_clientStatus.setCurrentAlgoName(config->algorithm().name()); #else diff --git a/src/common/net/Client.cpp b/src/common/net/Client.cpp index 0cc2b3dc..bf2e5d40 100644 --- a/src/common/net/Client.cpp +++ b/src/common/net/Client.cpp @@ -624,7 +624,8 @@ void Client::login() rapidjson::Value supportedPowVariantsList(rapidjson::kArrayType); for (auto& supportedPowVariant : xmrig::Algorithm::getSupportedPowVariants()) { - supportedPowVariantsList.PushBack(rapidjson::StringRef(supportedPowVariant.c_str()), allocator); + rapidjson::Value val(supportedPowVariant.c_str(), allocator); + supportedPowVariantsList.PushBack(val, allocator); } params.AddMember("supported-variants", supportedPowVariantsList, allocator); diff --git a/src/version.h b/src/version.h index bc2265f9..a8eb0b34 100644 --- a/src/version.h +++ b/src/version.h @@ -34,7 +34,7 @@ #define APP_KIND "amd" #define APP_VER_MAJOR 1 -#define APP_VER_MINOR 7 +#define APP_VER_MINOR 8 #define APP_VER_PATCH 0 #define TYPE_AMD_GPU From cd7a451a7b347353c74f2c77f4ca04ba209e7c85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Sun, 21 Oct 2018 20:46:02 +0200 Subject: [PATCH 44/97] --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 674a445d..313beb7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# 1.8.0 +- Rebase from XMRig-amd 2.8.4 (THX xmrig!) + - Now we have full TLS support on Stratum+CC + - Integration of CNV2 aka monero v8 +- Fixed avg.time on Dashboard +- Fix supported-variants announcement in login # 1.7.0 - First official Release of XMRigCC-amd base on XMRig-amd 2.7.3-beta #33 #3 - Full integration of xmrigCC-amd into XMRigCCServer/Dashboard with GPUInfo / remote logging From a1c6ca09f85c798c817b2a29c8dde992fc2aa564 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Sun, 21 Oct 2018 21:26:07 +0200 Subject: [PATCH 45/97] Fixed compile issues --- CMakeLists.txt | 5 ----- 1 file changed, 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4c48c484..0380a8f9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -315,14 +315,9 @@ add_executable(xmrigMiner-amd ${HEADERS} ${SOURCES_CC_COMMON} ${SOURCES} ${SOURC set_target_properties(xmrigMiner-amd PROPERTIES OUTPUT_NAME ${MINER_EXECUTABLE_NAME}) target_link_libraries(xmrigMiner-amd ${UV_LIBRARIES} ${MHD_LIBRARY} ${EXTRA_LIBS} ${LIBS} ${OpenCL_LIBRARY}) -add_executable(${PROJECT_NAME} ${HEADERS} ${SOURCES} ${SOURCES_OS} ${HEADERS_CRYPTO} ${SOURCES_CRYPTO} ${SOURCES_SYSLOG} ${HTTPD_SOURCES} ${TLS_SOURCES}) -target_link_libraries(${PROJECT_NAME} ${OPENSSL_LIBRARIES} ${UV_LIBRARIES} ${MHD_LIBRARY} ${EXTRA_LIBS} ${LIBS}) - if (WITH_TLS) target_link_libraries(xmrigMiner-amd xmrig_tls ${OPENSSL_LIBRARIES} ${EXTRA_LIBS}) - target_link_libraries(xmrigMiner-amd ${OPENSSL_LIBRARIES} ${EXTRA_LIBS}) endif (WITH_TLS) - add_executable(xmrigDaemon-amd src/cc/XMRigd.cpp res/app.rc) set_target_properties(xmrigDaemon-amd PROPERTIES OUTPUT_NAME ${DAEMON_EXECUTABLE_NAME}) From b0de7022f9ec9394f4d073585ddcc4eef5b28458 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Sun, 21 Oct 2018 22:08:08 +0200 Subject: [PATCH 46/97] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 8c3c560d..1d9d036a 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # XMRigCC-amd (OpenCL) +:warning: **Monero forked to cnv2 on block 1685555 (2018-10-18) make sure you upgrade to at least XMRigCC 1.8.0 to mine XMR** + :warning: **Confused by all the forks? Check the [Coin Configuration](https://github.com/Bendr0id/xmrigCC/wiki/Coin-configurations) guide.** :bulb: **This is the AMD GPU (OpenCL) variant of XMRigCC, if you're looking for the CPU variant [click here](https://github.com/Bendr0id/xmrigCC/).** From 69b7e524a8fdbab5e80759140a9b4ad891589866 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Sun, 21 Oct 2018 22:08:21 +0200 Subject: [PATCH 47/97] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1d9d036a..22b0d9ec 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # XMRigCC-amd (OpenCL) -:warning: **Monero forked to cnv2 on block 1685555 (2018-10-18) make sure you upgrade to at least XMRigCC 1.8.0 to mine XMR** +:warning: **Monero forked to cnv2 on block 1685555 (2018-10-18) make sure you upgrade to at least XMRigCC-amd 1.8.0 to mine XMR** :warning: **Confused by all the forks? Check the [Coin Configuration](https://github.com/Bendr0id/xmrigCC/wiki/Coin-configurations) guide.** From 465dcc71f7a7d43a5ed5ed380f1440389a0098c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Thu, 15 Nov 2018 20:18:29 +0100 Subject: [PATCH 48/97] Fix memory leak in client status --- src/cc/ClientStatus.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/ClientStatus.cpp b/src/cc/ClientStatus.cpp index 7f84d8cd..9655386a 100644 --- a/src/cc/ClientStatus.cpp +++ b/src/cc/ClientStatus.cpp @@ -595,7 +595,7 @@ std::string ClientStatus::toJsonString() writer.SetMaxDecimalPlaces(10); respDocument.Accept(writer); - return strdup(buffer.GetString()); + return buffer.GetString(); } void ClientStatus::clearGPUInfoList() From 572e6349ac19e7c751e13d2a3aef138ba48669a0 Mon Sep 17 00:00:00 2001 From: BenDr0id Date: Wed, 21 Nov 2018 09:02:19 +0100 Subject: [PATCH 49/97] Fixed merge issues --- CMakeLists.txt | 2 +- src/amd/OclGPU.cpp | 4 ++-- src/cc/CCClient.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 71ac07a4..1c486d06 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -177,7 +177,7 @@ if (WIN32) ) add_definitions(/DWIN32) - set(EXTRA_LIBS ws2_32 psapi iphlpapi userenv crypt32) + set(EXTRA_LIBS ws2_32 psapi iphlpapi userenv crypt32 winmm) elseif (APPLE) set(SOURCES_OS src/amd/OclCache_unix.cpp diff --git a/src/amd/OclGPU.cpp b/src/amd/OclGPU.cpp index bb6d1b15..b2a9f24a 100644 --- a/src/amd/OclGPU.cpp +++ b/src/amd/OclGPU.cpp @@ -113,12 +113,12 @@ size_t InitOpenCLGpu(int index, cl_context opencl_ctx, GpuContext* ctx, const ch if (ctx->name == ctx->board) { LOG_INFO(config->isColors() ? WHITE_BOLD("#%d") ", GPU " WHITE_BOLD("#%zu") " " GREEN_BOLD("%s") ", intensity: " WHITE_BOLD("%zu") " (%zu/%zu), unroll: " WHITE_BOLD("%d") ", cu: " WHITE_BOLD("%d") : "#%d, GPU #%zu (%s), intensity: %zu (%zu/%zu), unroll: %d, cu: %d", - index, ctx->deviceIdx, ctx->board.data(), ctx->rawIntensity, ctx->workSize, MaximumWorkSize, ctx->unrollFactor, ctx->computeUnits); + index, ctx->deviceIdx, ctx->board.data(), ctx->rawIntensity, ctx->workSize, ctx->maximumWorkSize, ctx->unrollFactor, ctx->computeUnits); } else { LOG_INFO(config->isColors() ? WHITE_BOLD("#%d") ", GPU " WHITE_BOLD("#%zu") " " GREEN_BOLD("%s") " (" CYAN_BOLD("%s") "), intensity: " WHITE_BOLD("%zu") " (%zu/%zu), unroll: " WHITE_BOLD("%d") ", cu: " WHITE_BOLD("%d") : "#%d, GPU #%zu %s (%s), intensity: %zu (%zu/%zu), unroll: %d, cu: %d", - index, ctx->deviceIdx, ctx->board.data(), ctx->name.data(), ctx->rawIntensity, ctx->workSize, MaximumWorkSize, ctx->unrollFactor, ctx->computeUnits); + index, ctx->deviceIdx, ctx->board.data(), ctx->name.data(), ctx->rawIntensity, ctx->workSize, ctx->maximumWorkSize, ctx->unrollFactor, ctx->computeUnits); } ctx->CommandQueues = OclLib::createCommandQueue(opencl_ctx, ctx->DeviceID, &ret); diff --git a/src/cc/CCClient.cpp b/src/cc/CCClient.cpp index 7797793b..4392f342 100644 --- a/src/cc/CCClient.cpp +++ b/src/cc/CCClient.cpp @@ -176,7 +176,7 @@ void CCClient::updateGpuInfo(const std::vector& gpuContext) for (auto gpu : gpuContext) { GPUInfo gpuInfo; - gpuInfo.setName(gpu.name); + gpuInfo.setName(gpu.name.data()); gpuInfo.setCompMode(gpu.compMode); gpuInfo.setComputeUnits(gpu.computeUnits); gpuInfo.setDeviceIdx(gpu.deviceIdx); From b4f8dafe2aeaa2db9a170882d2e45379ade5020b Mon Sep 17 00:00:00 2001 From: BenDr0id Date: Wed, 21 Nov 2018 09:03:45 +0100 Subject: [PATCH 50/97] increased version --- src/version.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/version.h b/src/version.h index a8eb0b34..d1af5851 100644 --- a/src/version.h +++ b/src/version.h @@ -28,14 +28,14 @@ #define APP_NAME "XMRigCC-AMD" #define APP_COPYRIGHT "Copyright (C) 2017- BenDr0id" #define APP_DESC "XMRigCC-AMD OpenCL miner" -#define APP_VERSION "1.8.0 (based on XMRig)" +#define APP_VERSION "1.8.2-dev (based on XMRig)" #define APP_SITE "https://github.com/Bendr0id/xmrigCC-amd" #define APP_DOMAIN "" #define APP_KIND "amd" #define APP_VER_MAJOR 1 #define APP_VER_MINOR 8 -#define APP_VER_PATCH 0 +#define APP_VER_PATCH 2 #define TYPE_AMD_GPU From 83f05f9b38ea3d389894a276b185d0fdaf98496a Mon Sep 17 00:00:00 2001 From: BenDr0id Date: Wed, 21 Nov 2018 17:10:24 +0100 Subject: [PATCH 51/97] WIP --- CMakeLists.txt | 2 + src/amd/GpuContext.h | 2 +- src/amd/OclGPU.cpp | 21 +- src/amd/opencl/cryptonight.cl | 173 +++++-- src/common/crypto/Algorithm.cpp | 2 + src/common/xmrig.h | 1 + src/crypto/CryptoNight.cpp | 12 +- src/crypto/CryptoNight_constants.h | 6 + src/crypto/CryptoNight_test.h | 5 + src/crypto/CryptoNight_x86.h | 30 +- src/version.h | 4 +- xmrig.cbp | 711 +++++++++++++++++++++++++++++ 12 files changed, 917 insertions(+), 52 deletions(-) create mode 100644 xmrig.cbp diff --git a/CMakeLists.txt b/CMakeLists.txt index 1c486d06..60783b62 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,8 @@ cmake_minimum_required(VERSION 2.8) project(xmrig) +set(CMAKE_BUILD_TYPE "Release") + 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) diff --git a/src/amd/GpuContext.h b/src/amd/GpuContext.h index ef844dd5..13ec6ba8 100644 --- a/src/amd/GpuContext.h +++ b/src/amd/GpuContext.h @@ -109,7 +109,7 @@ struct GpuContext cl_mem OutputBuffer; cl_mem ExtraBuffers[6]; cl_program Program; - cl_kernel Kernels[12]; + cl_kernel Kernels[13]; size_t freeMem; cl_uint computeUnits; xmrig::String board; diff --git a/src/amd/OclGPU.cpp b/src/amd/OclGPU.cpp index b2a9f24a..6d2f4fe8 100644 --- a/src/amd/OclGPU.cpp +++ b/src/amd/OclGPU.cpp @@ -88,6 +88,9 @@ inline static int cnKernelOffset(xmrig::Variant variant) case xmrig::VARIANT_2: return 11; + case xmrig::VARIANT_SWAP: + return 12; + default: break; } @@ -185,8 +188,8 @@ size_t InitOpenCLGpu(int index, cl_context opencl_ctx, GpuContext* ctx, const ch return OCL_ERR_API; } - const char *KernelNames[] = { "cn0", "cn1", "cn2", "Blake", "Groestl", "JH", "Skein", "cn1_monero", "cn1_msr", "cn1_xao", "cn1_tube", "cn1_v2_monero"}; - for (int i = 0; i < 12; ++i) { + const char *KernelNames[] = { "cn0", "cn1", "cn2", "Blake", "Groestl", "JH", "Skein", "cn1_monero", "cn1_msr", "cn1_xao", "cn1_tube", "cn1_v2_monero", "cn1_swap"}; + for (int i = 0; i < 13; ++i) { ctx->Kernels[i] = OclLib::createKernel(ctx->Program, KernelNames[i], &ret); if (ret != CL_SUCCESS) { return OCL_ERR_API; @@ -481,6 +484,13 @@ size_t XMRSetJob(GpuContext *ctx, uint8_t *input, size_t input_len, uint64_t tar return OCL_ERR_API; } + // variant + const cl_uint v = static_cast(variant); + if ((ret = OclLib::setKernelArg(ctx->Kernels[0], 4, sizeof(cl_uint), &v)) != CL_SUCCESS) { + LOG_ERR(kSetKernelArgErr, err_to_str(ret), 0, 4); + return OCL_ERR_API; + } + // CN1 Kernel const int cn_kernel_offset = cnKernelOffset(variant); @@ -496,7 +506,6 @@ size_t XMRSetJob(GpuContext *ctx, uint8_t *input, size_t input_len, uint64_t tar } // variant - const cl_uint v = static_cast(variant); if ((ret = OclLib::setKernelArg(ctx->Kernels[cn_kernel_offset], 2, sizeof(cl_uint), &v)) != CL_SUCCESS) { LOG_ERR(kSetKernelArgErr, err_to_str(ret), cn_kernel_offset, 2); return OCL_ERR_API; @@ -527,6 +536,12 @@ size_t XMRSetJob(GpuContext *ctx, uint8_t *input, size_t input_len, uint64_t tar return OCL_ERR_API; } + // variant + if ((ret = OclLib::setKernelArg(ctx->Kernels[2], 7, sizeof(cl_uint), &v)) != CL_SUCCESS) { + LOG_ERR(kSetKernelArgErr, err_to_str(ret), 2, 7); + return OCL_ERR_API; + } + for (int i = 0; i < 4; ++i) { // Nonce buffer, Output if (!setKernelArgFromExtraBuffers(ctx, i + 3, 0, 1) || !setKernelArgFromExtraBuffers(ctx, i + 3, 1, i + 2)) { diff --git a/src/amd/opencl/cryptonight.cl b/src/amd/opencl/cryptonight.cl index 9852c91f..27c315ba 100644 --- a/src/amd/opencl/cryptonight.cl +++ b/src/amd/opencl/cryptonight.cl @@ -103,6 +103,7 @@ XMRIG_INCLUDE_FAST_DIV_HEAVY #define VARIANT_XAO 6 // Modified CryptoNight variant 0 (Alloy only) #define VARIANT_RTO 7 // Modified CryptoNight variant 1 (Arto only) #define VARIANT_2 8 // CryptoNight variant 2 +#define VARIANT_SWAP 9 // CryptoNight variant swap aka cn-heavy-superfast #define CRYPTONIGHT 0 /* CryptoNight (Monero) */ #define CRYPTONIGHT_LITE 1 /* CryptoNight-Lite (AEON) */ @@ -381,16 +382,12 @@ void AESExpandKey256(uint *keybuf) #define MEM_CHUNK (1 << MEM_CHUNK_EXPONENT) -#if (STRIDED_INDEX == 0) -# define IDX(x) (x) -#elif (STRIDED_INDEX == 1) -# if (ALGO == CRYPTONIGHT_HEAVY) -# define IDX(x) ((x) * WORKSIZE) -# else -# define IDX(x) mul24((x), Threads) -# endif -#elif (STRIDED_INDEX == 2) -# define IDX(x) (((x) % MEM_CHUNK) + ((x) / MEM_CHUNK) * WORKSIZE * MEM_CHUNK) +#if(STRIDED_INDEX==0) +# define IDX(x) (x) +#elif(STRIDED_INDEX==1) +# define IDX(x) ((x) * (Threads)) +#elif(STRIDED_INDEX==2) +# define IDX(x) (((x) % MEM_CHUNK) + ((x) / MEM_CHUNK) * WORKSIZE * MEM_CHUNK) #endif inline ulong getIdx() @@ -403,7 +400,7 @@ inline ulong getIdx() #define mix_and_propagate(xin) (xin)[(get_local_id(1)) % 8][get_local_id(0)] ^ (xin)[(get_local_id(1) + 1) % 8][get_local_id(0)] __attribute__((reqd_work_group_size(8, 8, 1))) -__kernel void cn0(__global ulong *input, __global uint4 *Scratchpad, __global ulong *states, uint Threads) +__kernel void cn0(__global ulong *input, __global uint4 *Scratchpad, __global ulong *states, uint Threads, uint variant) { uint ExpandedKey1[40]; __local uint AES0[256], AES1[256], AES2[256], AES3[256]; @@ -491,13 +488,17 @@ __kernel void cn0(__global ulong *input, __global uint4 *Scratchpad, __global ul ((ulong *)ExpandedKey1)[i] = states[i]; } + AESExpandKey256(ExpandedKey1); } mem_fence(CLK_LOCAL_MEM_FENCE); -# if (ALGO == CRYPTONIGHT_HEAVY) +# if (ALGO != CRYPTONIGHT_HEAVY) + if (variant == VARIANT_SWAP) { +# endif + __local uint4 xin[8][8]; /* Also left over threads perform this loop. @@ -520,6 +521,8 @@ __kernel void cn0(__global ulong *input, __global uint4 *Scratchpad, __global ul barrier(CLK_LOCAL_MEM_FENCE); text = mix_and_propagate(xin); } + +# if (ALGO != CRYPTONIGHT_HEAVY) } # endif @@ -1103,6 +1106,89 @@ __kernel void cn1(__global uint4 *Scratchpad, __global ulong *states, uint varia )===" R"===( +__attribute__((reqd_work_group_size(WORKSIZE, 1, 1))) +__kernel void cn1_swap(__global uint4 *Scratchpad, __global ulong *states, uint variant, __global ulong *input, uint Threads) +{ + ulong a[2], b[2]; + __local uint AES0[256], AES1[256]; + + const ulong gIdx = getIdx(); + + for (int i = get_local_id(0); i < 256; i += WORKSIZE) { + const uint tmp = AES0_C[i]; + AES0[i] = tmp; + AES1[i] = rotate(tmp, 8U); + } + + barrier(CLK_LOCAL_MEM_FENCE); + + uint4 b_x; +# if (COMP_MODE == 1) + // do not use early return here + if (gIdx < Threads) +# endif + { + states += 25 * gIdx; +# if (STRIDED_INDEX == 0) + Scratchpad += gIdx * (MEMORY >> 4); +# elif (STRIDED_INDEX == 1) + Scratchpad += gIdx; +# elif(STRIDED_INDEX == 2) + Scratchpad += get_group_id(0) * (MEMORY >> 4) * WORKSIZE + MEM_CHUNK * get_local_id(0); +# endif + + a[0] = states[0] ^ states[4]; + b[0] = states[2] ^ states[6]; + a[1] = states[1] ^ states[5]; + b[1] = states[3] ^ states[7]; + + b_x = ((uint4 *)b)[0]; + } + + mem_fence(CLK_LOCAL_MEM_FENCE); + +# if (COMP_MODE == 1) + // do not use early return here + if (gIdx < Threads) +# endif + { + uint idx0 = a[0]; + + #pragma unroll UNROLL_FACTOR + for (int i = 0; i < 0x20000; ++i) { + ulong c[2]; + + ((uint4 *)c)[0] = Scratchpad[IDX((idx0 & MASK) >> 4)]; + ((uint4 *)c)[0] = AES_Round_Two_Tables(AES0, AES1, ((uint4 *)c)[0], ((uint4 *)a)[0]); + + Scratchpad[IDX((idx0 & MASK) >> 4)] = b_x ^ ((uint4 *)c)[0]; + + uint4 tmp; + tmp = Scratchpad[IDX((as_uint2(c[0]).s0 & MASK) >> 4)]; + + a[1] += c[0] * as_ulong2(tmp).s0; + a[0] += mul_hi(c[0], as_ulong2(tmp).s0); + + Scratchpad[IDX((as_uint2(c[0]).s0 & MASK) >> 4)] = ((uint4 *)a)[0]; + + ((uint4 *)a)[0] ^= tmp; + idx0 = a[0]; + + b_x = ((uint4 *)c)[0]; + + const long2 n = *((__global long2*)(Scratchpad + (IDX((idx0 & MASK) >> 4)))); + long q = fast_div_heavy(n.s0, as_int4(n).s2 | 0x5); + *((__global long*)(Scratchpad + (IDX((idx0 & MASK) >> 4)))) = n.s0 ^ q; + + idx0 = (~as_int4(n).s2) ^ q; + } + } + mem_fence(CLK_GLOBAL_MEM_FENCE); +} + +)===" +R"===( + __attribute__((reqd_work_group_size(WORKSIZE, 1, 1))) __kernel void cn1_xao(__global uint4 *Scratchpad, __global ulong *states, uint variant, __global ulong *input, uint Threads) { @@ -1187,7 +1273,7 @@ __kernel void cn1_xao(__global uint4 *Scratchpad, __global ulong *states, uint v R"===( __attribute__((reqd_work_group_size(8, 8, 1))) -__kernel void cn2(__global uint4 *Scratchpad, __global ulong *states, __global uint *Branch0, __global uint *Branch1, __global uint *Branch2, __global uint *Branch3, uint Threads) +__kernel void cn2(__global uint4 *Scratchpad, __global ulong *states, __global uint *Branch0, __global uint *Branch1, __global uint *Branch2, __global uint *Branch3, uint Threads, uint variant) { __local uint AES0[256], AES1[256], AES2[256], AES3[256]; uint ExpandedKey2[40]; @@ -1240,22 +1326,39 @@ __kernel void cn2(__global uint4 *Scratchpad, __global ulong *states, __global u barrier(CLK_LOCAL_MEM_FENCE); -# if (ALGO == CRYPTONIGHT_HEAVY) __local uint4 xin1[8][8]; __local uint4 xin2[8][8]; - __local uint4* xin1_store = &xin1[get_local_id(1)][get_local_id(0)]; - __local uint4* xin1_load = &xin1[(get_local_id(1) + 1) % 8][get_local_id(0)]; - __local uint4* xin2_store = &xin2[get_local_id(1)][get_local_id(0)]; - __local uint4* xin2_load = &xin2[(get_local_id(1) + 1) % 8][get_local_id(0)]; + __local uint4* xin1_store; + __local uint4* xin1_load; + __local uint4* xin2_store; + __local uint4* xin2_load; + +# if (ALGO != CRYPTONIGHT_HEAVY) + if (variant == VARIANT_SWAP) + { +# endif + + xin1_store = &xin1[get_local_id(1)][get_local_id(0)]; + xin1_load = &xin1[(get_local_id(1) + 1) % 8][get_local_id(0)]; + xin2_store = &xin2[get_local_id(1)][get_local_id(0)]; + xin2_load = &xin2[(get_local_id(1) + 1) % 8][get_local_id(0)]; *xin2_store = (uint4)(0, 0, 0, 0); + +# if (ALGO != CRYPTONIGHT_HEAVY) + } # endif + # if (COMP_MODE == 1) // do not use early return here if (gIdx < Threads) # endif { -# if (ALGO == CRYPTONIGHT_HEAVY) +# if (ALGO != CRYPTONIGHT_HEAVY) + if (variant == VARIANT_SWAP) + { +# endif + #pragma unroll 2 for(int i = 0, i1 = get_local_id(1); i < (MEMORY >> 7); ++i, i1 = (i1 + 16) % (MEMORY >> 4)) { @@ -1283,23 +1386,34 @@ __kernel void cn2(__global uint4 *Scratchpad, __global ulong *states, __global u barrier(CLK_LOCAL_MEM_FENCE); text ^= *xin2_load; -# else - const uint local_id1 = get_local_id(1); - #pragma unroll 2 - for (uint i = 0; i < (MEMORY >> 7); ++i) { - text ^= Scratchpad[IDX((i << 3) + local_id1)]; +# if (ALGO != CRYPTONIGHT_HEAVY) + } +# endif - #pragma unroll 10 - for(uint j = 0; j < 10; ++j) - text = AES_Round(AES0, AES1, AES2, AES3, text, ((uint4 *)ExpandedKey2)[j]); +# if (ALGO != CRYPTONIGHT_HEAVY) + if (variant != VARIANT_SWAP) + { + const uint local_id1 = get_local_id(1); + #pragma unroll 2 + for (uint i = 0; i < (MEMORY >> 7); ++i) { + text ^= Scratchpad[IDX((i << 3) + local_id1)]; + + #pragma unroll 10 + for(uint j = 0; j < 10; ++j) + text = AES_Round(AES0, AES1, AES2, AES3, text, ((uint4 *)ExpandedKey2)[j]); + } } # endif } -# if (ALGO == CRYPTONIGHT_HEAVY) +# if (ALGO != CRYPTONIGHT_HEAVY) + if (variant == VARIANT_SWAP) + { +# endif /* Also left over threads performe this loop. * The left over thread results will be ignored */ + #pragma unroll 16 for(size_t i = 0; i < 16; i++) { @@ -1313,6 +1427,9 @@ __kernel void cn2(__global uint4 *Scratchpad, __global ulong *states, __global u barrier(CLK_LOCAL_MEM_FENCE); text ^= *xin1_load; } + +# if (ALGO != CRYPTONIGHT_HEAVY) + } # endif # if (COMP_MODE == 1) diff --git a/src/common/crypto/Algorithm.cpp b/src/common/crypto/Algorithm.cpp index 81a07fdb..f77aa0ff 100644 --- a/src/common/crypto/Algorithm.cpp +++ b/src/common/crypto/Algorithm.cpp @@ -61,6 +61,7 @@ static AlgoData const algorithms[] = { { "cryptonight/xao", "cn/xao", xmrig::CRYPTONIGHT, xmrig::VARIANT_XAO }, { "cryptonight/rto", "cn/rto", xmrig::CRYPTONIGHT, xmrig::VARIANT_RTO }, { "cryptonight/2", "cn/2", xmrig::CRYPTONIGHT, xmrig::VARIANT_2 }, + { "cryptonight/swap", "cn/swap", xmrig::CRYPTONIGHT, xmrig::VARIANT_SWAP }, # ifndef XMRIG_NO_AEON { "cryptonight-lite", "cn-lite", xmrig::CRYPTONIGHT_LITE, xmrig::VARIANT_AUTO }, @@ -108,6 +109,7 @@ static const char *variants[] = { "xao", "rto", "2", + "swap" }; diff --git a/src/common/xmrig.h b/src/common/xmrig.h index 20306d1c..41629511 100644 --- a/src/common/xmrig.h +++ b/src/common/xmrig.h @@ -69,6 +69,7 @@ enum Variant { VARIANT_XAO = 6, // Modified CryptoNight variant 0 (Alloy only) VARIANT_RTO = 7, // Modified CryptoNight variant 1 (Arto only) VARIANT_2 = 8, // CryptoNight variant 2 + VARIANT_SWAP = 9, // CryptoNight variant swap aka cn-heavy-superfast VARIANT_MAX }; diff --git a/src/crypto/CryptoNight.cpp b/src/crypto/CryptoNight.cpp index 84ef42fb..8ff07bb1 100644 --- a/src/crypto/CryptoNight.cpp +++ b/src/crypto/CryptoNight.cpp @@ -93,6 +93,9 @@ CryptoNight::cn_hash_fun CryptoNight::fn(xmrig::Algo algorithm, xmrig::AlgoVerif cryptonight_single_hash, cryptonight_single_hash, + cryptonight_single_hash, + cryptonight_single_hash, + # ifndef XMRIG_NO_AEON cryptonight_single_hash, cryptonight_single_hash, @@ -107,12 +110,13 @@ CryptoNight::cn_hash_fun CryptoNight::fn(xmrig::Algo algorithm, xmrig::AlgoVerif nullptr, nullptr, // VARIANT_XAO nullptr, nullptr, // VARIANT_RTO nullptr, nullptr, // VARIANT_2 + nullptr, nullptr, // VARIANT_SWAP # else nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr # endif # ifndef XMRIG_NO_SUMO @@ -133,12 +137,13 @@ CryptoNight::cn_hash_fun CryptoNight::fn(xmrig::Algo algorithm, xmrig::AlgoVerif nullptr, nullptr, // VARIANT_XAO nullptr, nullptr, // VARIANT_RTO nullptr, nullptr, // VARIANT_2 + nullptr, nullptr, // VARIANT_SWAP # else nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr + nullptr, nullptr, nullptr, nullptr # endif }; @@ -185,7 +190,8 @@ bool CryptoNight::selfTest() { verify(VARIANT_XTL, test_output_xtl) && verify(VARIANT_MSR, test_output_msr) && verify(VARIANT_XAO, test_output_xao) && - verify(VARIANT_RTO, test_output_rto); + verify(VARIANT_RTO, test_output_rto) && + verify(VARIANT_SWAP, test_output_swap); } # ifndef XMRIG_NO_AEON diff --git a/src/crypto/CryptoNight_constants.h b/src/crypto/CryptoNight_constants.h index f13891a7..44fb2607 100644 --- a/src/crypto/CryptoNight_constants.h +++ b/src/crypto/CryptoNight_constants.h @@ -40,6 +40,7 @@ constexpr const uint32_t CRYPTONIGHT_MASK = 0x1FFFF0; constexpr const uint32_t CRYPTONIGHT_ITER = 0x80000; constexpr const uint32_t CRYPTONIGHT_MSR_ITER = 0x40000; constexpr const uint32_t CRYPTONIGHT_XAO_ITER = 0x100000; +constexpr const uint32_t CRYPTONIGHT_SWAP_ITER = 0x20000; constexpr const size_t CRYPTONIGHT_LITE_MEMORY = 1 * 1024 * 1024; constexpr const uint32_t CRYPTONIGHT_LITE_MASK = 0xFFFF0; @@ -112,6 +113,7 @@ template<> inline constexpr uint32_t cn_select_iter() template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_MSR_ITER; } template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_XAO_ITER; } template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_ITER; } +template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_SWAP_ITER; } template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_LITE_ITER; } template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_LITE_ITER; } template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_HEAVY_ITER; } @@ -128,6 +130,9 @@ inline uint32_t cn_select_iter(Algo algorithm, Variant variant) case VARIANT_RTO: return CRYPTONIGHT_XAO_ITER; + case VARIANT_SWAP: + return CRYPTONIGHT_SWAP_ITER; + default: break; } @@ -160,6 +165,7 @@ template<> inline constexpr Variant cn_base_variant() { return VA template<> inline constexpr Variant cn_base_variant() { return VARIANT_0; } template<> inline constexpr Variant cn_base_variant() { return VARIANT_0; } template<> inline constexpr Variant cn_base_variant() { return VARIANT_1; } +template<> inline constexpr Variant cn_base_variant() { return VARIANT_0; } template<> inline constexpr Variant cn_base_variant() { return VARIANT_2; } diff --git a/src/crypto/CryptoNight_test.h b/src/crypto/CryptoNight_test.h index 95e12197..e901fd3c 100644 --- a/src/crypto/CryptoNight_test.h +++ b/src/crypto/CryptoNight_test.h @@ -158,6 +158,11 @@ const static uint8_t test_output_rto[160] = { 0xE7, 0x81, 0x4E, 0x2A, 0xBD, 0x62, 0xC1, 0x1B, 0x7C, 0xB9, 0x33, 0x7B, 0xEE, 0x95, 0x80, 0xB3 }; +// SWAP +const static uint8_t test_output_swap[32] = { + 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 +}; #ifndef XMRIG_NO_AEON const static uint8_t test_output_v0_lite[160] = { diff --git a/src/crypto/CryptoNight_x86.h b/src/crypto/CryptoNight_x86.h index 8dcdd414..750eaed7 100644 --- a/src/crypto/CryptoNight_x86.h +++ b/src/crypto/CryptoNight_x86.h @@ -231,7 +231,7 @@ inline void mix_and_propagate(__m128i& x0, __m128i& x1, __m128i& x2, __m128i& x3 } -template +template static inline void cn_explode_scratchpad(const __m128i *input, __m128i *output) { __m128i xin0, xin1, xin2, xin3, xin4, xin5, xin6, xin7; @@ -248,7 +248,7 @@ static inline void cn_explode_scratchpad(const __m128i *input, __m128i *output) xin6 = _mm_load_si128(input + 10); xin7 = _mm_load_si128(input + 11); - if (ALGO == xmrig::CRYPTONIGHT_HEAVY) { + if (ALGO == xmrig::CRYPTONIGHT_HEAVY || VARIANT == xmrig::VARIANT_SWAP) { 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); @@ -289,7 +289,7 @@ static inline void cn_explode_scratchpad(const __m128i *input, __m128i *output) } -template +template static inline void cn_implode_scratchpad(const __m128i *input, __m128i *output) { __m128i xout0, xout1, xout2, xout3, xout4, xout5, xout6, xout7; @@ -328,12 +328,12 @@ static inline void cn_implode_scratchpad(const __m128i *input, __m128i *output) aes_round(k8, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); aes_round(k9, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - if (ALGO == xmrig::CRYPTONIGHT_HEAVY) { + if (ALGO == xmrig::CRYPTONIGHT_HEAVY || VARIANT == xmrig::VARIANT_SWAP) { mix_and_propagate(xout0, xout1, xout2, xout3, xout4, xout5, xout6, xout7); } } - if (ALGO == xmrig::CRYPTONIGHT_HEAVY) { + if (ALGO == xmrig::CRYPTONIGHT_HEAVY || VARIANT == xmrig::VARIANT_SWAP) { 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); @@ -465,7 +465,7 @@ inline void cryptonight_single_hash(const uint8_t *__restrict__ input, size_t si xmrig::keccak(input, size, ctx[0]->state); - cn_explode_scratchpad((__m128i*) ctx[0]->state, (__m128i*) ctx[0]->memory); + cn_explode_scratchpad((__m128i*) ctx[0]->state, (__m128i*) ctx[0]->memory); const uint8_t* l0 = ctx[0]->memory; uint64_t* h0 = reinterpret_cast(ctx[0]->state); @@ -535,14 +535,14 @@ inline void cryptonight_single_hash(const uint8_t *__restrict__ input, size_t si ah0 ^= ch; idx0 = al0; - if (ALGO == xmrig::CRYPTONIGHT_HEAVY) { + if (ALGO == xmrig::CRYPTONIGHT_HEAVY || VARIANT == xmrig::VARIANT_SWAP) { 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 (VARIANT == xmrig::VARIANT_XHV) { + if (VARIANT == xmrig::VARIANT_XHV || VARIANT == xmrig::VARIANT_SWAP) { d = ~d; } @@ -554,7 +554,7 @@ inline void cryptonight_single_hash(const uint8_t *__restrict__ input, size_t si bx0 = cx; } - cn_implode_scratchpad((__m128i*) ctx[0]->memory, (__m128i*) ctx[0]->state); + cn_implode_scratchpad((__m128i*) ctx[0]->memory, (__m128i*) ctx[0]->state); xmrig::keccakf(h0, 24); extra_hashes[ctx[0]->state[0] & 3](ctx[0]->state, 200, output); @@ -717,14 +717,14 @@ inline void cryptonight_double_hash(const uint8_t *__restrict__ input, size_t si ah0 ^= ch; idx0 = al0; - if (ALGO == xmrig::CRYPTONIGHT_HEAVY) { + if (ALGO == xmrig::CRYPTONIGHT_HEAVY || VARIANT == xmrig::VARIANT_SWAP) { 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 (VARIANT == xmrig::VARIANT_XHV) { + if (VARIANT == xmrig::VARIANT_XHV || VARIANT == xmrig::VARIANT_SWAP) { d = ~d; } @@ -758,14 +758,14 @@ inline void cryptonight_double_hash(const uint8_t *__restrict__ input, size_t si ah1 ^= ch; idx1 = al1; - if (ALGO == xmrig::CRYPTONIGHT_HEAVY) { + if (ALGO == xmrig::CRYPTONIGHT_HEAVY || VARIANT == xmrig::VARIANT_SWAP) { 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 (VARIANT == xmrig::VARIANT_XHV) { + if (VARIANT == xmrig::VARIANT_XHV || VARIANT == xmrig::VARIANT_SWAP) { d = ~d; } @@ -844,12 +844,12 @@ inline void cryptonight_double_hash(const uint8_t *__restrict__ input, size_t si a = _mm_xor_si128(a, _mm_set_epi64x(ch##part, cl##part)); \ idx = _mm_cvtsi128_si64(a); \ \ - if (ALGO == xmrig::CRYPTONIGHT_HEAVY) { \ + if (ALGO == xmrig::CRYPTONIGHT_HEAVY || VARIANT == xmrig::VARIANT_SWAP) { \ 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 == xmrig::VARIANT_XHV) { \ + if (VARIANT == xmrig::VARIANT_XHV || VARIANT == xmrig::VARIANT_SWAP) { \ d = ~d; \ } \ \ diff --git a/src/version.h b/src/version.h index d1af5851..a87e3955 100644 --- a/src/version.h +++ b/src/version.h @@ -28,14 +28,14 @@ #define APP_NAME "XMRigCC-AMD" #define APP_COPYRIGHT "Copyright (C) 2017- BenDr0id" #define APP_DESC "XMRigCC-AMD OpenCL miner" -#define APP_VERSION "1.8.2-dev (based on XMRig)" +#define APP_VERSION "1.8.4-dev5 (based on XMRig)" #define APP_SITE "https://github.com/Bendr0id/xmrigCC-amd" #define APP_DOMAIN "" #define APP_KIND "amd" #define APP_VER_MAJOR 1 #define APP_VER_MINOR 8 -#define APP_VER_PATCH 2 +#define APP_VER_PATCH 4 #define TYPE_AMD_GPU diff --git a/xmrig.cbp b/xmrig.cbp new file mode 100644 index 00000000..39a355ac --- /dev/null +++ b/xmrig.cbp @@ -0,0 +1,711 @@ + + + + + + From 805648e071e13abdbb9ca6c437a1a3c66917ee7e Mon Sep 17 00:00:00 2001 From: BenDr0id Date: Thu, 22 Nov 2018 08:52:08 +0100 Subject: [PATCH 52/97] Cleanup --- CMakeLists.txt | 2 - src/amd/OclGPU.cpp | 4 +- src/amd/opencl/cryptonight.cl | 14 +- src/common/crypto/Algorithm.cpp | 4 +- src/common/xmrig.h | 2 +- src/crypto/CryptoNight.cpp | 10 +- src/crypto/CryptoNight_constants.h | 10 +- src/crypto/CryptoNight_test.h | 4 +- src/crypto/CryptoNight_x86.h | 22 +- src/version.h | 2 +- xmrig.cbp | 711 ----------------------------- 11 files changed, 36 insertions(+), 749 deletions(-) delete mode 100644 xmrig.cbp diff --git a/CMakeLists.txt b/CMakeLists.txt index 60783b62..1c486d06 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,8 +1,6 @@ cmake_minimum_required(VERSION 2.8) project(xmrig) -set(CMAKE_BUILD_TYPE "Release") - 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) diff --git a/src/amd/OclGPU.cpp b/src/amd/OclGPU.cpp index 6d2f4fe8..92d00fb6 100644 --- a/src/amd/OclGPU.cpp +++ b/src/amd/OclGPU.cpp @@ -88,7 +88,7 @@ inline static int cnKernelOffset(xmrig::Variant variant) case xmrig::VARIANT_2: return 11; - case xmrig::VARIANT_SWAP: + case xmrig::VARIANT_XFH: return 12; default: @@ -188,7 +188,7 @@ size_t InitOpenCLGpu(int index, cl_context opencl_ctx, GpuContext* ctx, const ch return OCL_ERR_API; } - const char *KernelNames[] = { "cn0", "cn1", "cn2", "Blake", "Groestl", "JH", "Skein", "cn1_monero", "cn1_msr", "cn1_xao", "cn1_tube", "cn1_v2_monero", "cn1_swap"}; + const char *KernelNames[] = { "cn0", "cn1", "cn2", "Blake", "Groestl", "JH", "Skein", "cn1_monero", "cn1_msr", "cn1_xao", "cn1_tube", "cn1_v2_monero", "cn1_xfh"}; for (int i = 0; i < 13; ++i) { ctx->Kernels[i] = OclLib::createKernel(ctx->Program, KernelNames[i], &ret); if (ret != CL_SUCCESS) { diff --git a/src/amd/opencl/cryptonight.cl b/src/amd/opencl/cryptonight.cl index 27c315ba..ad215630 100644 --- a/src/amd/opencl/cryptonight.cl +++ b/src/amd/opencl/cryptonight.cl @@ -103,7 +103,7 @@ XMRIG_INCLUDE_FAST_DIV_HEAVY #define VARIANT_XAO 6 // Modified CryptoNight variant 0 (Alloy only) #define VARIANT_RTO 7 // Modified CryptoNight variant 1 (Arto only) #define VARIANT_2 8 // CryptoNight variant 2 -#define VARIANT_SWAP 9 // CryptoNight variant swap aka cn-heavy-superfast +#define VARIANT_XFH 9 // CryptoNight variant xfh aka cn-heavy-superfast #define CRYPTONIGHT 0 /* CryptoNight (Monero) */ #define CRYPTONIGHT_LITE 1 /* CryptoNight-Lite (AEON) */ @@ -495,7 +495,7 @@ __kernel void cn0(__global ulong *input, __global uint4 *Scratchpad, __global ul mem_fence(CLK_LOCAL_MEM_FENCE); # if (ALGO != CRYPTONIGHT_HEAVY) - if (variant == VARIANT_SWAP) + if (variant == VARIANT_XFH) { # endif @@ -1107,7 +1107,7 @@ __kernel void cn1(__global uint4 *Scratchpad, __global ulong *states, uint varia R"===( __attribute__((reqd_work_group_size(WORKSIZE, 1, 1))) -__kernel void cn1_swap(__global uint4 *Scratchpad, __global ulong *states, uint variant, __global ulong *input, uint Threads) +__kernel void cn1_xfh(__global uint4 *Scratchpad, __global ulong *states, uint variant, __global ulong *input, uint Threads) { ulong a[2], b[2]; __local uint AES0[256], AES1[256]; @@ -1334,7 +1334,7 @@ __kernel void cn2(__global uint4 *Scratchpad, __global ulong *states, __global u __local uint4* xin2_load; # if (ALGO != CRYPTONIGHT_HEAVY) - if (variant == VARIANT_SWAP) + if (variant == VARIANT_XFH) { # endif @@ -1355,7 +1355,7 @@ __kernel void cn2(__global uint4 *Scratchpad, __global ulong *states, __global u # endif { # if (ALGO != CRYPTONIGHT_HEAVY) - if (variant == VARIANT_SWAP) + if (variant == VARIANT_XFH) { # endif @@ -1391,7 +1391,7 @@ __kernel void cn2(__global uint4 *Scratchpad, __global ulong *states, __global u # endif # if (ALGO != CRYPTONIGHT_HEAVY) - if (variant != VARIANT_SWAP) + if (variant != VARIANT_XFH) { const uint local_id1 = get_local_id(1); #pragma unroll 2 @@ -1407,7 +1407,7 @@ __kernel void cn2(__global uint4 *Scratchpad, __global ulong *states, __global u } # if (ALGO != CRYPTONIGHT_HEAVY) - if (variant == VARIANT_SWAP) + if (variant == VARIANT_XFH) { # endif /* Also left over threads performe this loop. diff --git a/src/common/crypto/Algorithm.cpp b/src/common/crypto/Algorithm.cpp index f77aa0ff..5b7a3611 100644 --- a/src/common/crypto/Algorithm.cpp +++ b/src/common/crypto/Algorithm.cpp @@ -61,7 +61,7 @@ static AlgoData const algorithms[] = { { "cryptonight/xao", "cn/xao", xmrig::CRYPTONIGHT, xmrig::VARIANT_XAO }, { "cryptonight/rto", "cn/rto", xmrig::CRYPTONIGHT, xmrig::VARIANT_RTO }, { "cryptonight/2", "cn/2", xmrig::CRYPTONIGHT, xmrig::VARIANT_2 }, - { "cryptonight/swap", "cn/swap", xmrig::CRYPTONIGHT, xmrig::VARIANT_SWAP }, + { "cryptonight/xfh", "cn/xfh", xmrig::CRYPTONIGHT, xmrig::VARIANT_XFH }, # ifndef XMRIG_NO_AEON { "cryptonight-lite", "cn-lite", xmrig::CRYPTONIGHT_LITE, xmrig::VARIANT_AUTO }, @@ -109,7 +109,7 @@ static const char *variants[] = { "xao", "rto", "2", - "swap" + "xfh" }; diff --git a/src/common/xmrig.h b/src/common/xmrig.h index 41629511..3b6d065e 100644 --- a/src/common/xmrig.h +++ b/src/common/xmrig.h @@ -69,7 +69,7 @@ enum Variant { VARIANT_XAO = 6, // Modified CryptoNight variant 0 (Alloy only) VARIANT_RTO = 7, // Modified CryptoNight variant 1 (Arto only) VARIANT_2 = 8, // CryptoNight variant 2 - VARIANT_SWAP = 9, // CryptoNight variant swap aka cn-heavy-superfast + VARIANT_XFH = 9, // CryptoNight variant xfh/swap aka cn-heavy-superfast VARIANT_MAX }; diff --git a/src/crypto/CryptoNight.cpp b/src/crypto/CryptoNight.cpp index 8ff07bb1..638e971a 100644 --- a/src/crypto/CryptoNight.cpp +++ b/src/crypto/CryptoNight.cpp @@ -93,8 +93,8 @@ CryptoNight::cn_hash_fun CryptoNight::fn(xmrig::Algo algorithm, xmrig::AlgoVerif cryptonight_single_hash, cryptonight_single_hash, - cryptonight_single_hash, - cryptonight_single_hash, + cryptonight_single_hash, + cryptonight_single_hash, # ifndef XMRIG_NO_AEON cryptonight_single_hash, @@ -110,7 +110,7 @@ CryptoNight::cn_hash_fun CryptoNight::fn(xmrig::Algo algorithm, xmrig::AlgoVerif nullptr, nullptr, // VARIANT_XAO nullptr, nullptr, // VARIANT_RTO nullptr, nullptr, // VARIANT_2 - nullptr, nullptr, // VARIANT_SWAP + nullptr, nullptr, // VARIANT_XFH # else nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, @@ -137,7 +137,7 @@ CryptoNight::cn_hash_fun CryptoNight::fn(xmrig::Algo algorithm, xmrig::AlgoVerif nullptr, nullptr, // VARIANT_XAO nullptr, nullptr, // VARIANT_RTO nullptr, nullptr, // VARIANT_2 - nullptr, nullptr, // VARIANT_SWAP + nullptr, nullptr, // VARIANT_XFH # else nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, @@ -191,7 +191,7 @@ bool CryptoNight::selfTest() { verify(VARIANT_MSR, test_output_msr) && verify(VARIANT_XAO, test_output_xao) && verify(VARIANT_RTO, test_output_rto) && - verify(VARIANT_SWAP, test_output_swap); + verify(VARIANT_XFH, test_output_xfh); } # ifndef XMRIG_NO_AEON diff --git a/src/crypto/CryptoNight_constants.h b/src/crypto/CryptoNight_constants.h index 44fb2607..47bacd23 100644 --- a/src/crypto/CryptoNight_constants.h +++ b/src/crypto/CryptoNight_constants.h @@ -40,7 +40,7 @@ constexpr const uint32_t CRYPTONIGHT_MASK = 0x1FFFF0; constexpr const uint32_t CRYPTONIGHT_ITER = 0x80000; constexpr const uint32_t CRYPTONIGHT_MSR_ITER = 0x40000; constexpr const uint32_t CRYPTONIGHT_XAO_ITER = 0x100000; -constexpr const uint32_t CRYPTONIGHT_SWAP_ITER = 0x20000; +constexpr const uint32_t CRYPTONIGHT_XFH_ITER = 0x20000; constexpr const size_t CRYPTONIGHT_LITE_MEMORY = 1 * 1024 * 1024; constexpr const uint32_t CRYPTONIGHT_LITE_MASK = 0xFFFF0; @@ -113,7 +113,7 @@ template<> inline constexpr uint32_t cn_select_iter() template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_MSR_ITER; } template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_XAO_ITER; } template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_ITER; } -template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_SWAP_ITER; } +template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_XFH_ITER; } template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_LITE_ITER; } template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_LITE_ITER; } template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_HEAVY_ITER; } @@ -130,8 +130,8 @@ inline uint32_t cn_select_iter(Algo algorithm, Variant variant) case VARIANT_RTO: return CRYPTONIGHT_XAO_ITER; - case VARIANT_SWAP: - return CRYPTONIGHT_SWAP_ITER; + case VARIANT_XFH: + return CRYPTONIGHT_XFH_ITER; default: break; @@ -165,7 +165,7 @@ template<> inline constexpr Variant cn_base_variant() { return VA template<> inline constexpr Variant cn_base_variant() { return VARIANT_0; } template<> inline constexpr Variant cn_base_variant() { return VARIANT_0; } template<> inline constexpr Variant cn_base_variant() { return VARIANT_1; } -template<> inline constexpr Variant cn_base_variant() { return VARIANT_0; } +template<> inline constexpr Variant cn_base_variant() { return VARIANT_0; } template<> inline constexpr Variant cn_base_variant() { return VARIANT_2; } diff --git a/src/crypto/CryptoNight_test.h b/src/crypto/CryptoNight_test.h index e901fd3c..1af6e61c 100644 --- a/src/crypto/CryptoNight_test.h +++ b/src/crypto/CryptoNight_test.h @@ -158,8 +158,8 @@ const static uint8_t test_output_rto[160] = { 0xE7, 0x81, 0x4E, 0x2A, 0xBD, 0x62, 0xC1, 0x1B, 0x7C, 0xB9, 0x33, 0x7B, 0xEE, 0x95, 0x80, 0xB3 }; -// SWAP -const static uint8_t test_output_swap[32] = { +// XFH +const static uint8_t test_output_xfh[32] = { 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 }; diff --git a/src/crypto/CryptoNight_x86.h b/src/crypto/CryptoNight_x86.h index 750eaed7..8d562061 100644 --- a/src/crypto/CryptoNight_x86.h +++ b/src/crypto/CryptoNight_x86.h @@ -248,7 +248,7 @@ static inline void cn_explode_scratchpad(const __m128i *input, __m128i *output) xin6 = _mm_load_si128(input + 10); xin7 = _mm_load_si128(input + 11); - if (ALGO == xmrig::CRYPTONIGHT_HEAVY || VARIANT == xmrig::VARIANT_SWAP) { + if (ALGO == xmrig::CRYPTONIGHT_HEAVY || VARIANT == xmrig::VARIANT_XFH) { 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); @@ -328,12 +328,12 @@ static inline void cn_implode_scratchpad(const __m128i *input, __m128i *output) aes_round(k8, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); aes_round(k9, &xout0, &xout1, &xout2, &xout3, &xout4, &xout5, &xout6, &xout7); - if (ALGO == xmrig::CRYPTONIGHT_HEAVY || VARIANT == xmrig::VARIANT_SWAP) { + if (ALGO == xmrig::CRYPTONIGHT_HEAVY || VARIANT == xmrig::VARIANT_XFH) { mix_and_propagate(xout0, xout1, xout2, xout3, xout4, xout5, xout6, xout7); } } - if (ALGO == xmrig::CRYPTONIGHT_HEAVY || VARIANT == xmrig::VARIANT_SWAP) { + if (ALGO == xmrig::CRYPTONIGHT_HEAVY || VARIANT == xmrig::VARIANT_XFH) { 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); @@ -535,14 +535,14 @@ inline void cryptonight_single_hash(const uint8_t *__restrict__ input, size_t si ah0 ^= ch; idx0 = al0; - if (ALGO == xmrig::CRYPTONIGHT_HEAVY || VARIANT == xmrig::VARIANT_SWAP) { + if (ALGO == xmrig::CRYPTONIGHT_HEAVY || VARIANT == xmrig::VARIANT_XFH) { 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 (VARIANT == xmrig::VARIANT_XHV || VARIANT == xmrig::VARIANT_SWAP) { + if (VARIANT == xmrig::VARIANT_XHV || VARIANT == xmrig::VARIANT_XFH) { d = ~d; } @@ -717,14 +717,14 @@ inline void cryptonight_double_hash(const uint8_t *__restrict__ input, size_t si ah0 ^= ch; idx0 = al0; - if (ALGO == xmrig::CRYPTONIGHT_HEAVY || VARIANT == xmrig::VARIANT_SWAP) { + if (ALGO == xmrig::CRYPTONIGHT_HEAVY || VARIANT == xmrig::VARIANT_XFH) { 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 (VARIANT == xmrig::VARIANT_XHV || VARIANT == xmrig::VARIANT_SWAP) { + if (VARIANT == xmrig::VARIANT_XHV || VARIANT == xmrig::VARIANT_XFH) { d = ~d; } @@ -758,14 +758,14 @@ inline void cryptonight_double_hash(const uint8_t *__restrict__ input, size_t si ah1 ^= ch; idx1 = al1; - if (ALGO == xmrig::CRYPTONIGHT_HEAVY || VARIANT == xmrig::VARIANT_SWAP) { + if (ALGO == xmrig::CRYPTONIGHT_HEAVY || VARIANT == xmrig::VARIANT_XFH) { 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 (VARIANT == xmrig::VARIANT_XHV || VARIANT == xmrig::VARIANT_SWAP) { + if (VARIANT == xmrig::VARIANT_XHV || VARIANT == xmrig::VARIANT_XFH) { d = ~d; } @@ -844,12 +844,12 @@ inline void cryptonight_double_hash(const uint8_t *__restrict__ input, size_t si a = _mm_xor_si128(a, _mm_set_epi64x(ch##part, cl##part)); \ idx = _mm_cvtsi128_si64(a); \ \ - if (ALGO == xmrig::CRYPTONIGHT_HEAVY || VARIANT == xmrig::VARIANT_SWAP) { \ + if (ALGO == xmrig::CRYPTONIGHT_HEAVY || VARIANT == xmrig::VARIANT_XFH) { \ 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 == xmrig::VARIANT_XHV || VARIANT == xmrig::VARIANT_SWAP) { \ + if (VARIANT == xmrig::VARIANT_XHV || VARIANT == xmrig::VARIANT_XFH) { \ d = ~d; \ } \ \ diff --git a/src/version.h b/src/version.h index a87e3955..92071f2f 100644 --- a/src/version.h +++ b/src/version.h @@ -28,7 +28,7 @@ #define APP_NAME "XMRigCC-AMD" #define APP_COPYRIGHT "Copyright (C) 2017- BenDr0id" #define APP_DESC "XMRigCC-AMD OpenCL miner" -#define APP_VERSION "1.8.4-dev5 (based on XMRig)" +#define APP_VERSION "1.8.4 (based on XMRig)" #define APP_SITE "https://github.com/Bendr0id/xmrigCC-amd" #define APP_DOMAIN "" #define APP_KIND "amd" diff --git a/xmrig.cbp b/xmrig.cbp deleted file mode 100644 index 39a355ac..00000000 --- a/xmrig.cbp +++ /dev/null @@ -1,711 +0,0 @@ - - - - - - From 6d554b2cd8b5d4b7456759199e9138c8a65e055d Mon Sep 17 00:00:00 2001 From: BenDr0id Date: Thu, 22 Nov 2018 09:50:13 +0100 Subject: [PATCH 53/97] #1.8.4 Preparation --- CHANGELOG.md | 14 ++++++++++++++ README.md | 2 -- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 313beb7d..fc971897 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ +# 1.8.4 +- Added XFH (Freehaven-project) support aka CN-Heavy-superfast (algo=cryptonight variant=xfh) +- Fix memory leak in cc client component +- Rebase from XMRig-amd 2.8.6 (Thanks xmrig) + - Improved cn-heavy, cn-heavy/xhv perfomance up to 8% since v2.8.5 and up to 16% since v2.8.4, thanks @SChernykh + - Fixed hashrate fluctuations. It's no longer necessary to use different intensities per thread. + - Improved cn-heavy/tube perfomance up to 6% and cn/2 perfomance up to 1%. + - Reduced power consumption with cn/2 + - Fixed possible invalid shares right after donation finish. + - Improved AMD Vega64 auto configuration. + - It's now recommended to revise your config.json and try: + - Same intensities for both threads. + - strided_index=2, mem_chunk=1 for cn/2. + - strided_index=1 for other algorithms. # 1.8.0 - Rebase from XMRig-amd 2.8.4 (THX xmrig!) - Now we have full TLS support on Stratum+CC diff --git a/README.md b/README.md index 22b0d9ec..8c3c560d 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ # XMRigCC-amd (OpenCL) -:warning: **Monero forked to cnv2 on block 1685555 (2018-10-18) make sure you upgrade to at least XMRigCC-amd 1.8.0 to mine XMR** - :warning: **Confused by all the forks? Check the [Coin Configuration](https://github.com/Bendr0id/xmrigCC/wiki/Coin-configurations) guide.** :bulb: **This is the AMD GPU (OpenCL) variant of XMRigCC, if you're looking for the CPU variant [click here](https://github.com/Bendr0id/xmrigCC/).** From eb2deadde10cc84f68197c34c1d9fb06e0e4b6ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Mon, 26 Nov 2018 22:41:46 +0100 Subject: [PATCH 54/97] Fix parsing cn/xfh algo send from pool --- src/common/net/Pool.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/common/net/Pool.cpp b/src/common/net/Pool.cpp index 089a8727..488e09b2 100644 --- a/src/common/net/Pool.cpp +++ b/src/common/net/Pool.cpp @@ -402,6 +402,7 @@ void Pool::rebuild() addVariant(xmrig::VARIANT_XHV); addVariant(xmrig::VARIANT_XAO); addVariant(xmrig::VARIANT_RTO); + addVariant(xmrig::VARIANT_XFH); addVariant(xmrig::VARIANT_AUTO); # endif } From 21ffbeeb548d9723e369f6d90842775c4b888e72 Mon Sep 17 00:00:00 2001 From: BenDr0id Date: Tue, 27 Nov 2018 17:19:22 +0100 Subject: [PATCH 55/97] Integrated remote reboot feature --- src/App.cpp | 10 ++++++++++ src/App.h | 1 + src/cc/CCClient.cpp | 6 ++++-- src/cc/ControlCommand.cpp | 7 ++++--- src/cc/ControlCommand.h | 10 ++++++---- src/common/config/CommonConfig.cpp | 10 +++++++--- src/common/config/CommonConfig.h | 2 ++ src/common/crypto/Algorithm.cpp | 2 +- src/common/interfaces/IConfig.h | 3 ++- src/core/Config.cpp | 5 ++++- src/core/ConfigLoader_platform.h | 5 ++++- src/version.h | 4 ++-- 12 files changed, 47 insertions(+), 18 deletions(-) diff --git a/src/App.cpp b/src/App.cpp index 06a75e15..42c2aa81 100644 --- a/src/App.cpp +++ b/src/App.cpp @@ -225,6 +225,14 @@ void App::shutdown() m_self->stop(false); } +void App::reboot() +{ + auto rebootCmd = m_self->m_controller->config()->ccRebootCmd(); + if (rebootCmd) { + system(rebootCmd); + shutdown(); + } +} void App::onSignal(uv_signal_t *handle, int signum) { @@ -268,6 +276,8 @@ void App::onCommandReceived(uv_async_t* async) case ControlCommand::SHUTDOWN: App::shutdown(); break; + case ControlCommand::REBOOT: + App::reboot(); case ControlCommand::PUBLISH_CONFIG:; break; } diff --git a/src/App.h b/src/App.h index c10ddfee..7f7b06e5 100644 --- a/src/App.h +++ b/src/App.h @@ -52,6 +52,7 @@ class App : public IConsoleListener int exec(); static void restart(); static void shutdown(); + static void reboot(); protected: void onConsoleCommand(char command) override; diff --git a/src/cc/CCClient.cpp b/src/cc/CCClient.cpp index 4392f342..88345fb1 100644 --- a/src/cc/CCClient.cpp +++ b/src/cc/CCClient.cpp @@ -224,9 +224,11 @@ void CCClient::publishClientStatusReport() LOG_WARN("[CC-Client] Command: PUBLISH_CONFIG received -> publish config"); publishConfig(); }else if (controlCommand.getCommand() == ControlCommand::RESTART) { - LOG_WARN("[CC-Client] Command: RESTART received -> restart"); + LOG_WARN("[CC-Client] Command: RESTART received -> trigger restart"); } else if (controlCommand.getCommand() == ControlCommand::SHUTDOWN) { - LOG_WARN("[CC-Client] Command: SHUTDOWN received -> 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()); diff --git a/src/cc/ControlCommand.cpp b/src/cc/ControlCommand.cpp index afdda3dd..611057fc 100644 --- a/src/cc/ControlCommand.cpp +++ b/src/cc/ControlCommand.cpp @@ -30,13 +30,13 @@ #endif ControlCommand::ControlCommand() - : m_command(Command::START) + : m_command(Command::START) { } ControlCommand::ControlCommand(Command command) - : m_command(command) + : m_command(command) { } @@ -97,5 +97,6 @@ bool ControlCommand::isOneTimeCommand() const { return m_command == ControlCommand::UPDATE_CONFIG || m_command == ControlCommand::PUBLISH_CONFIG || m_command == ControlCommand::RESTART || - m_command == ControlCommand::SHUTDOWN; + m_command == ControlCommand::SHUTDOWN || + m_command == ControlCommand::REBOOT; } diff --git a/src/cc/ControlCommand.h b/src/cc/ControlCommand.h index 246aa923..3219c531 100644 --- a/src/cc/ControlCommand.h +++ b/src/cc/ControlCommand.h @@ -21,13 +21,14 @@ #include #include "rapidjson/document.h" -static const char* command_str[6] = { +static const char* command_str[7] = { "START", "STOP", "UPDATE_CONFIG", "PUBLISH_CONFIG", "RESTART", - "SHUTDOWN" + "SHUTDOWN", + "REBOOT" }; class ControlCommand @@ -39,7 +40,8 @@ class ControlCommand UPDATE_CONFIG, PUBLISH_CONFIG, RESTART, - SHUTDOWN + SHUTDOWN, + REBOOT }; public: @@ -75,4 +77,4 @@ class ControlCommand Command m_command; }; -#endif /* __CONTROL_COMMAND_H__ */ +#endif /* __CONTROL_COMMAND_H__ */ \ No newline at end of file diff --git a/src/common/config/CommonConfig.cpp b/src/common/config/CommonConfig.cpp index 783751de..c425ba19 100644 --- a/src/common/config/CommonConfig.cpp +++ b/src/common/config/CommonConfig.cpp @@ -425,19 +425,23 @@ bool xmrig::CommonConfig::parseString(int key, const char *arg) m_userAgent = arg; break; - case CCUrlKey: + case CCUrlKey: /* --cc-url */ m_ccUrl = arg; parseCCUrl(arg); break; - case CCAccessTokenKey: + case CCAccessTokenKey: /* --cc-access-token */ m_ccToken = arg; break; - case CCWorkerIdKey: + case CCWorkerIdKey: /* --cc-worker-id */ m_ccWorkerId = arg; break; + case CCRebootCmdKey:/* --cc-reboot-cmd */ + m_ccRebootCmd = arg; + break; + case RetriesKey: /* --retries */ case RetryPauseKey: /* --retry-pause */ case ApiPort: /* --api-port */ diff --git a/src/common/config/CommonConfig.h b/src/common/config/CommonConfig.h index 74110df7..42800d3a 100644 --- a/src/common/config/CommonConfig.h +++ b/src/common/config/CommonConfig.h @@ -61,6 +61,7 @@ class CommonConfig : public IConfig inline const char *ccHost() const { return m_ccHost.data(); } inline const char *ccToken() const { return m_ccToken.data(); } inline const char *ccWorkerId() const { return m_ccWorkerId.data(); } + inline const char *ccRebootCmd() const { return m_ccRebootCmd.data(); } inline const std::vector &pools() const { return m_activePools; } inline int apiPort() const { return m_apiPort; } @@ -131,6 +132,7 @@ class CommonConfig : public IConfig xmrig::c_str m_ccHost; xmrig::c_str m_ccToken; xmrig::c_str m_ccWorkerId; + xmrig::c_str m_ccRebootCmd; private: bool parseInt(int key, int arg); diff --git a/src/common/crypto/Algorithm.cpp b/src/common/crypto/Algorithm.cpp index 5b7a3611..e7cbdbf2 100644 --- a/src/common/crypto/Algorithm.cpp +++ b/src/common/crypto/Algorithm.cpp @@ -109,7 +109,7 @@ static const char *variants[] = { "xao", "rto", "2", - "xfh" + "xfh", }; diff --git a/src/common/interfaces/IConfig.h b/src/common/interfaces/IConfig.h index aad4186b..ee573b1d 100644 --- a/src/common/interfaces/IConfig.h +++ b/src/common/interfaces/IConfig.h @@ -132,7 +132,8 @@ class IConfig CCUseTlsKey = 6004, CCUseRemoteLoggingKey = 6005, CCUploadConfigOnStartupKey = 6006, - DaemonizedKey = 6007 + CCRebootCmdKey = 6007, + DaemonizedKey = 6008, }; virtual ~IConfig() {} diff --git a/src/core/Config.cpp b/src/core/Config.cpp index 4e922a9e..bfb0c214 100644 --- a/src/core/Config.cpp +++ b/src/core/Config.cpp @@ -168,12 +168,15 @@ void xmrig::Config::getJSON(rapidjson::Document &doc) const doc.AddMember("watch", m_watch, allocator); Value cc(kObjectType); - cc.AddMember("url", ccUrl() ? Value(StringRef(ccUrl())).Move() : Value(kNullType).Move(), allocator); cc.AddMember("access-token", ccToken() ? Value(StringRef(ccToken())).Move() : Value(kNullType).Move(), allocator); cc.AddMember("worker-id", ccWorkerId() ? Value(StringRef(ccWorkerId())).Move() : Value(kNullType).Move(), allocator); cc.AddMember("update-interval-s", ccUpdateInterval(), allocator); cc.AddMember("use-tls", ccUseTls(), allocator); + cc.AddMember("use-remote-logging", ccUseRemoteLogging(), allocator); + cc.AddMember("upload-config-on-startup", ccUploadConfigOnStartup(), allocator); + cc.AddMember("reboot-cmd", ccRebootCmd() ? Value(StringRef(ccRebootCmd())).Move() : Value(kNullType).Move(), allocator); + doc.AddMember("cc-client", cc, allocator); } diff --git a/src/core/ConfigLoader_platform.h b/src/core/ConfigLoader_platform.h index 2f96cda9..ed43393b 100644 --- a/src/core/ConfigLoader_platform.h +++ b/src/core/ConfigLoader_platform.h @@ -102,7 +102,8 @@ Options:\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-remote-logging-max-rows=N maximum last n-log rows to send CC Server\n\ - --cc-use-remote-logging enable remote logging on CC Server\n" + --cc-use-remote-logging enable remote logging on CC Server\n\ + --cc-reboot-cmd command/bat to execute to Reboot miner\n" # endif "\ -h, --help display this help and exit\n\ @@ -163,6 +164,7 @@ static struct option const options[] = { { "cc-use-tls", 0, nullptr, xmrig::IConfig::CCUseTlsKey }, { "cc-use-remote-logging", 0, nullptr, xmrig::IConfig::CCUseRemoteLoggingKey }, { "cc-upload-config-on-startup", 0, nullptr, xmrig::IConfig::CCUploadConfigOnStartupKey }, + { "cc-reboot-cmd", 1, nullptr, xmrig::IConfig::CCRebootCmdKey }, { "daemonized", 0, nullptr, xmrig::IConfig::DaemonizedKey }, { nullptr, 0, nullptr, 0 } }; @@ -223,6 +225,7 @@ static struct option const cc_client_options[] = { { "use-tls", 0, nullptr, xmrig::IConfig::CCUseTlsKey }, { "use-remote-logging", 0, nullptr, xmrig::IConfig::CCUseRemoteLoggingKey }, { "upload-config-on-startup", 1, nullptr, xmrig::IConfig::CCUploadConfigOnStartupKey }, + { "reboot-cmd", 1, nullptr, xmrig::IConfig::CCRebootCmdKey }, { 0, 0, 0, 0 } }; diff --git a/src/version.h b/src/version.h index 92071f2f..312c45e4 100644 --- a/src/version.h +++ b/src/version.h @@ -28,14 +28,14 @@ #define APP_NAME "XMRigCC-AMD" #define APP_COPYRIGHT "Copyright (C) 2017- BenDr0id" #define APP_DESC "XMRigCC-AMD OpenCL miner" -#define APP_VERSION "1.8.4 (based on XMRig)" +#define APP_VERSION "1.8.5-beta1 (based on XMRig)" #define APP_SITE "https://github.com/Bendr0id/xmrigCC-amd" #define APP_DOMAIN "" #define APP_KIND "amd" #define APP_VER_MAJOR 1 #define APP_VER_MINOR 8 -#define APP_VER_PATCH 4 +#define APP_VER_PATCH 5 #define TYPE_AMD_GPU From bbc13cdb65c74477403a5fbfd8c7d9e1c0c8bf20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Tue, 27 Nov 2018 19:00:29 +0100 Subject: [PATCH 56/97] Updated default config file --- src/config.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/config.json b/src/config.json index d225fe4b..ad4c3636 100644 --- a/src/config.json +++ b/src/config.json @@ -23,7 +23,7 @@ "rig-id": null, "nicehash": false, "keepalive": false, - "variant": -1, + "variant": "auto", "tls": false, "tls-fingerprint": null } @@ -42,6 +42,7 @@ "worker-id": null, "update-interval-s": 10, "use-remote-logging" : true, - "upload-config-on-startup" : true + "upload-config-on-startup" : true, + "reboot-cmd" : "" } } \ No newline at end of file From 8923d56b9ea39828bb4a5d4774596690e3ebb766 Mon Sep 17 00:00:00 2001 From: BenDr0id Date: Wed, 28 Nov 2018 14:29:15 +0100 Subject: [PATCH 57/97] #1.8.5 preparation --- CHANGELOG.md | 4 ++++ src/version.h | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fc971897..68e5996d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 1.8.5 +- Add remote reboot (machine) feature to Dashboard, Server & Miner +- Integrated Pushover push notifications for Offline miners and periodical status notifications on iOS and Android +- Fixed XFH algo recognition send by Pool/Proxy # 1.8.4 - Added XFH (Freehaven-project) support aka CN-Heavy-superfast (algo=cryptonight variant=xfh) - Fix memory leak in cc client component diff --git a/src/version.h b/src/version.h index 312c45e4..c1c928ca 100644 --- a/src/version.h +++ b/src/version.h @@ -28,7 +28,7 @@ #define APP_NAME "XMRigCC-AMD" #define APP_COPYRIGHT "Copyright (C) 2017- BenDr0id" #define APP_DESC "XMRigCC-AMD OpenCL miner" -#define APP_VERSION "1.8.5-beta1 (based on XMRig)" +#define APP_VERSION "1.8.5 (based on XMRig)" #define APP_SITE "https://github.com/Bendr0id/xmrigCC-amd" #define APP_DOMAIN "" #define APP_KIND "amd" From 4f952532f07e84ce81b25df0b4452c6204b9069b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Thu, 6 Dec 2018 23:34:48 +0100 Subject: [PATCH 58/97] Integrated Hashrate monitor * Check for minimum rig hashrate * Check of crashed threads * Auto reboot/restart --- CMakeLists.txt | 1 + src/App.cpp | 27 ++++-- src/App.h | 2 + src/common/config/CommonConfig.cpp | 28 +++++- src/common/config/CommonConfig.h | 18 ++-- src/common/interfaces/IConfig.h | 9 +- src/config.json | 8 +- src/core/Config.cpp | 7 +- src/core/ConfigLoader_platform.h | 25 ++++-- src/net/Network.cpp | 4 +- src/version.h | 4 +- src/workers/HashrateMonitor.cpp | 132 +++++++++++++++++++++++++++++ src/workers/HashrateMonitor.h | 61 +++++++++++++ src/workers/Workers.cpp | 13 ++- src/workers/Workers.h | 2 +- 15 files changed, 307 insertions(+), 34 deletions(-) create mode 100644 src/workers/HashrateMonitor.cpp create mode 100644 src/workers/HashrateMonitor.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 1c486d06..c6988d41 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -141,6 +141,7 @@ set(SOURCES src/workers/OclThread.cpp src/workers/OclWorker.cpp src/workers/Workers.cpp + src/workers/HashrateMonitor.cpp src/xmrig.cpp ) diff --git a/src/App.cpp b/src/App.cpp index 42c2aa81..f9d60b22 100644 --- a/src/App.cpp +++ b/src/App.cpp @@ -25,9 +25,6 @@ #include #include -#include -#include - #include "api/Api.h" #include "App.h" @@ -38,6 +35,8 @@ #include "core/Controller.h" #include "crypto/CryptoNight.h" #include "net/Network.h" +#include "cc/CCClient.h" +#include "cc/ControlCommand.h" #include "Summary.h" #include "version.h" #include "workers/Workers.h" @@ -56,7 +55,8 @@ App::App(int argc, char **argv) : m_restart(false), m_console(nullptr), m_httpd(nullptr), - m_ccclient(nullptr) + m_ccclient(nullptr), + m_hashrateMonitor(nullptr) { m_self = this; @@ -88,10 +88,10 @@ App::~App() delete m_httpd; # endif + delete m_hashrateMonitor; + # ifndef XMRIG_NO_CC - if (m_ccclient) { - delete m_ccclient; - } + delete m_ccclient; # endif } @@ -106,6 +106,11 @@ int App::exec() return 2; } + auto startCmd = m_controller->config()->startCmd(); + if (startCmd) { + system(startCmd); + } + uv_signal_start(&m_sigHUP, App::onSignal, SIGHUP); uv_signal_start(&m_sigINT, App::onSignal, SIGINT); uv_signal_start(&m_sigTERM, App::onSignal, SIGTERM); @@ -139,6 +144,10 @@ int App::exec() m_httpd->start(); # endif + if (m_controller->config()->minRigHashrate()) { + m_hashrateMonitor = new HashrateMonitor(&m_async, m_controller); + } + # ifndef XMRIG_NO_CC if (m_controller->config()->ccHost() && m_controller->config()->ccPort() > 0) { uv_async_init(uv_default_loop(), &m_async, App::onCommandReceived); @@ -210,7 +219,7 @@ void App::stop(bool restart) m_restart = restart; m_controller->network()->stop(); - Workers::stop(); + Workers::stop(restart); uv_stop(uv_default_loop()); } @@ -227,7 +236,7 @@ void App::shutdown() void App::reboot() { - auto rebootCmd = m_self->m_controller->config()->ccRebootCmd(); + auto rebootCmd = m_self->m_controller->config()->rebootCmd(); if (rebootCmd) { system(rebootCmd); shutdown(); diff --git a/src/App.h b/src/App.h index 7f7b06e5..f6542693 100644 --- a/src/App.h +++ b/src/App.h @@ -29,6 +29,7 @@ #include #include "cc/CCClient.h" +#include "workers/HashrateMonitor.h" #include "common/interfaces/IConsoleListener.h" @@ -75,6 +76,7 @@ class App : public IConsoleListener uv_signal_t m_sigTERM; xmrig::Controller *m_controller; CCClient *m_ccclient; + HashrateMonitor* m_hashrateMonitor; uv_async_t m_async; }; diff --git a/src/common/config/CommonConfig.cpp b/src/common/config/CommonConfig.cpp index c425ba19..a2ed73eb 100644 --- a/src/common/config/CommonConfig.cpp +++ b/src/common/config/CommonConfig.cpp @@ -79,6 +79,8 @@ xmrig::CommonConfig::CommonConfig() : # else m_watch(false), // TODO: enable config file watch by default when this feature propertly handled and tested. # endif + m_rebootOnCardCrash(false), + m_rigWatchdog(true), m_daemonized(false), m_ccUseTls(false), m_ccUseRemoteLogging(true), @@ -89,6 +91,7 @@ xmrig::CommonConfig::CommonConfig() : m_printTime(60), m_retries(5), m_retryPause(5), + m_minRigHashrate(0), m_ccUpdateInterval(10), m_ccPort(0), m_state(NoneState) @@ -327,6 +330,14 @@ bool xmrig::CommonConfig::parseBoolean(int key, bool enable) m_autoSave = enable; break; + case RigWatchdogKey: /* --rig-watchdog */ + m_rigWatchdog = enable; + break; + + case RebootOnCardcrashKey: /* --reboot-on-cardcrash */ + m_rebootOnCardCrash = enable; + break; + case DaemonizedKey: /* --daemonized */ m_daemonized = enable; break; @@ -438,14 +449,19 @@ bool xmrig::CommonConfig::parseString(int key, const char *arg) m_ccWorkerId = arg; break; - case CCRebootCmdKey:/* --cc-reboot-cmd */ - m_ccRebootCmd = arg; + case StartCmdKey:/* --cc-start-cmd && --start-cmd */ + m_startCmd = arg; + break; + + case RebootCmdKey:/* --cc-reboot-cmd && --reboot-cmd */ + m_rebootCmd = arg; break; case RetriesKey: /* --retries */ case RetryPauseKey: /* --retry-pause */ case ApiPort: /* --api-port */ case PrintTimeKey: /* --print-time */ + case MinRigHashrateKey: /* --min-rig-hashrate */ return parseUint64(key, strtol(arg, nullptr, 10)); case BackgroundKey: /* --background */ @@ -458,6 +474,8 @@ bool xmrig::CommonConfig::parseString(int key, const char *arg) case CCUseTlsKey: /* --cc-use-tls-run */ case CCUseRemoteLoggingKey: /* --cc-use-remote-logging */ case CCUploadConfigOnStartupKey: /* --cc-upload-config-on-startup */ + case RigWatchdogKey: /* --rig-watchdog */ + case RebootOnCardcrashKey: /* --reboot-on-cardcrash */ case DaemonizedKey: /* --daemonized */ return parseBoolean(key, true); @@ -536,6 +554,12 @@ bool xmrig::CommonConfig::parseInt(int key, int arg) } break; + case MinRigHashrateKey: /* --min-rig-hashrate */ + if (arg > 0) { + m_minRigHashrate = arg; + } + break; + case CCUpdateIntervalKey: /* --cc-update-interval-s */ if (arg > 0) { m_ccUpdateInterval = arg; diff --git a/src/common/config/CommonConfig.h b/src/common/config/CommonConfig.h index 42800d3a..d7b66835 100644 --- a/src/common/config/CommonConfig.h +++ b/src/common/config/CommonConfig.h @@ -49,19 +49,22 @@ class CommonConfig : public IConfig inline bool isColors() const { return m_colors; } inline bool isDryRun() const { return m_dryRun; } inline bool isSyslog() const { return m_syslog; } - inline bool ccUseTls() const { return m_ccUseTls; } - inline bool ccUseRemoteLogging() const { return m_ccUseRemoteLogging; } - inline bool ccUploadConfigOnStartup() const { return m_ccUploadConfigOnStartup; } + inline bool hasRigWatchdog() const { return m_rigWatchdog; } + inline bool isRebootOnCardCrash() const { return m_rebootOnCardCrash; } + inline bool ccUseTls() const { return m_ccUseTls; } + inline bool ccUseRemoteLogging() const { return m_ccUseRemoteLogging; } + inline bool ccUploadConfigOnStartup() const { return m_ccUploadConfigOnStartup; } inline const char *apiId() const { return m_apiId.data(); } inline const char *apiToken() const { return m_apiToken.data(); } inline const char *apiWorkerId() const { return m_apiWorkerId.data(); } inline const char *logFile() const { return m_logFile.data(); } inline const char *userAgent() const { return m_userAgent.data(); } + inline const char *startCmd() const { return m_startCmd.size() > 0 ? m_startCmd.data() : nullptr; } + inline const char *rebootCmd() const { return m_rebootCmd.size() > 0 ? m_rebootCmd.data() : nullptr; } inline const char *ccUrl() const { return m_ccUrl.data(); } inline const char *ccHost() const { return m_ccHost.data(); } inline const char *ccToken() const { return m_ccToken.data(); } inline const char *ccWorkerId() const { return m_ccWorkerId.data(); } - inline const char *ccRebootCmd() const { return m_ccRebootCmd.data(); } inline const std::vector &pools() const { return m_activePools; } inline int apiPort() const { return m_apiPort; } @@ -69,6 +72,7 @@ class CommonConfig : public IConfig inline int printTime() const { return m_printTime; } inline int retries() const { return m_retries; } inline int retryPause() const { return m_retryPause; } + inline int minRigHashrate() const { return m_minRigHashrate; } inline int ccUpdateInterval() const { return m_ccUpdateInterval; } inline int ccPort() const { return m_ccPort; } inline void setColors(bool colors) { m_colors = colors; } @@ -108,6 +112,8 @@ class CommonConfig : public IConfig bool m_dryRun; bool m_syslog; bool m_watch; + bool m_rebootOnCardCrash; + bool m_rigWatchdog; bool m_daemonized; bool m_ccUseTls; bool m_ccUseRemoteLogging; @@ -117,6 +123,7 @@ class CommonConfig : public IConfig int m_printTime; int m_retries; int m_retryPause; + int m_minRigHashrate; int m_ccUpdateInterval; int m_ccPort; State m_state; @@ -132,7 +139,8 @@ class CommonConfig : public IConfig xmrig::c_str m_ccHost; xmrig::c_str m_ccToken; xmrig::c_str m_ccWorkerId; - xmrig::c_str m_ccRebootCmd; + xmrig::c_str m_startCmd; + xmrig::c_str m_rebootCmd; private: bool parseInt(int key, int arg); diff --git a/src/common/interfaces/IConfig.h b/src/common/interfaces/IConfig.h index ee573b1d..c0c4df67 100644 --- a/src/common/interfaces/IConfig.h +++ b/src/common/interfaces/IConfig.h @@ -66,6 +66,11 @@ class IConfig TlsKey = 1013, FingerprintKey = 1014, AutoSaveKey = 1016, + StartCmdKey = 6007, + RebootCmdKey = 6008, + RigWatchdogKey = 6009, + RebootOnCardcrashKey = 6010, + MinRigHashrateKey = 6011, // xmrig common CPUPriorityKey = 1021, @@ -132,8 +137,8 @@ class IConfig CCUseTlsKey = 6004, CCUseRemoteLoggingKey = 6005, CCUploadConfigOnStartupKey = 6006, - CCRebootCmdKey = 6007, - DaemonizedKey = 6008, + //CCRebootCmdKey = 6008, + DaemonizedKey = 6012, }; virtual ~IConfig() {} diff --git a/src/config.json b/src/config.json index ad4c3636..c7e12659 100644 --- a/src/config.json +++ b/src/config.json @@ -35,7 +35,12 @@ "user-agent": null, "syslog": false, "watch": false, - "cc-client": { + "start-cmd" : "", // command/bat to execute when the miner starts + "reboot-cmd" : "", // command/bat to execute to reboot miner + "rig-watchdog" : true, // enable rig monitor/watchdog + "reboot-on-cardcrash" : false, // reboot the miner instead of restart on cardcrash/min rig hashrate error + "min-rig-hashrate" : 0, // minimum rig hashrate for last 60s, when below miner will restart/reboot + "cc-client": { // to disable CC at all just remove the whole cc-client block "url": "localhost:3344", "use-tls" : false, "access-token": "mySecret", @@ -43,6 +48,5 @@ "update-interval-s": 10, "use-remote-logging" : true, "upload-config-on-startup" : true, - "reboot-cmd" : "" } } \ No newline at end of file diff --git a/src/core/Config.cpp b/src/core/Config.cpp index bfb0c214..fd66e98d 100644 --- a/src/core/Config.cpp +++ b/src/core/Config.cpp @@ -167,6 +167,10 @@ void xmrig::Config::getJSON(rapidjson::Document &doc) const doc.AddMember("syslog", isSyslog(), allocator); doc.AddMember("watch", m_watch, allocator); + doc.AddMember("rig-watchdog", hasRigWatchdog(), allocator); + doc.AddMember("reboot-on-cardcrash", isRebootOnCardCrash(), allocator); + doc.AddMember("min-rig-hashrate", minRigHashrate(), allocator); + Value cc(kObjectType); cc.AddMember("url", ccUrl() ? Value(StringRef(ccUrl())).Move() : Value(kNullType).Move(), allocator); cc.AddMember("access-token", ccToken() ? Value(StringRef(ccToken())).Move() : Value(kNullType).Move(), allocator); @@ -175,7 +179,8 @@ void xmrig::Config::getJSON(rapidjson::Document &doc) const cc.AddMember("use-tls", ccUseTls(), allocator); cc.AddMember("use-remote-logging", ccUseRemoteLogging(), allocator); cc.AddMember("upload-config-on-startup", ccUploadConfigOnStartup(), allocator); - cc.AddMember("reboot-cmd", ccRebootCmd() ? Value(StringRef(ccRebootCmd())).Move() : Value(kNullType).Move(), allocator); + cc.AddMember("start-cmd", startCmd() ? Value(StringRef(startCmd())).Move() : Value(kNullType).Move(), allocator); + cc.AddMember("reboot-cmd", rebootCmd() ? Value(StringRef(rebootCmd())).Move() : Value(kNullType).Move(), allocator); doc.AddMember("cc-client", cc, allocator); } diff --git a/src/core/ConfigLoader_platform.h b/src/core/ConfigLoader_platform.h index ed43393b..8828b47f 100644 --- a/src/core/ConfigLoader_platform.h +++ b/src/core/ConfigLoader_platform.h @@ -81,7 +81,12 @@ Options:\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" + -l, --log-file=FILE log all output to a file\n\ + --start-cmd=CMD command/bat to execute when the miner starts\n\ + --reboot-cmd=CMD command/bat to execute to reboot miner\n\ + --rig-monitor enable rig monitor/watchdog\n\ + --reboot-on-cardcrash reboot the miner instead of restart on cardcrash/min rig hashrate error\n\ + --min-rig-hashrate=N minimum rig hashrate for last 60s, when below miner will restart/reboot\n" # ifdef HAVE_SYSLOG_H "\ -S, --syslog use system log for output messages\n" @@ -102,8 +107,7 @@ Options:\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-remote-logging-max-rows=N maximum last n-log rows to send CC Server\n\ - --cc-use-remote-logging enable remote logging on CC Server\n\ - --cc-reboot-cmd command/bat to execute to Reboot miner\n" + --cc-use-remote-logging enable remote logging on CC Server\n" # endif "\ -h, --help display this help and exit\n\ @@ -164,7 +168,12 @@ static struct option const options[] = { { "cc-use-tls", 0, nullptr, xmrig::IConfig::CCUseTlsKey }, { "cc-use-remote-logging", 0, nullptr, xmrig::IConfig::CCUseRemoteLoggingKey }, { "cc-upload-config-on-startup", 0, nullptr, xmrig::IConfig::CCUploadConfigOnStartupKey }, - { "cc-reboot-cmd", 1, nullptr, xmrig::IConfig::CCRebootCmdKey }, + { "start-cmd", 1, nullptr, xmrig::IConfig::StartCmdKey }, + { "reboot-cmd", 1, nullptr, xmrig::IConfig::RebootCmdKey }, + { "cc-reboot-cmd", 1, nullptr, xmrig::IConfig::RebootCmdKey }, + { "rig-watchdog", 0, nullptr, xmrig::IConfig::RigWatchdogKey }, + { "reboot-on-cardcrash", 0, nullptr, xmrig::IConfig::RebootOnCardcrashKey }, + { "min-rig-hashrate", 1, nullptr, xmrig::IConfig::MinRigHashrateKey }, { "daemonized", 0, nullptr, xmrig::IConfig::DaemonizedKey }, { nullptr, 0, nullptr, 0 } }; @@ -186,6 +195,11 @@ static struct option const config_options[] = { { "cache", 0, nullptr, xmrig::IConfig::OclCacheKey }, { "opencl-loader", 1, nullptr, xmrig::IConfig::OclLoaderKey }, { "autosave", 0, nullptr, xmrig::IConfig::AutoSaveKey }, + { "start-cmd", 1, nullptr, xmrig::IConfig::StartCmdKey }, + { "reboot-cmd", 1, nullptr, xmrig::IConfig::RebootCmdKey }, + { "rig-watchdog", 0, nullptr, xmrig::IConfig::RigWatchdogKey }, + { "reboot-on-cardcrash", 0, nullptr, xmrig::IConfig::RebootOnCardcrashKey }, + { "min-rig-hashrate", 1, nullptr, xmrig::IConfig::MinRigHashrateKey }, { "daemonized", 0, nullptr, xmrig::IConfig::DaemonizedKey }, { nullptr, 0, nullptr, 0 } }; @@ -225,7 +239,8 @@ static struct option const cc_client_options[] = { { "use-tls", 0, nullptr, xmrig::IConfig::CCUseTlsKey }, { "use-remote-logging", 0, nullptr, xmrig::IConfig::CCUseRemoteLoggingKey }, { "upload-config-on-startup", 1, nullptr, xmrig::IConfig::CCUploadConfigOnStartupKey }, - { "reboot-cmd", 1, nullptr, xmrig::IConfig::CCRebootCmdKey }, + { "start-cmd", 1, nullptr, xmrig::IConfig::StartCmdKey }, + { "reboot-cmd", 1, nullptr, xmrig::IConfig::RebootCmdKey }, { 0, 0, 0, 0 } }; diff --git a/src/net/Network.cpp b/src/net/Network.cpp index d71d7b7e..1159588b 100644 --- a/src/net/Network.cpp +++ b/src/net/Network.cpp @@ -41,9 +41,9 @@ #include "core/Controller.h" #include "net/Network.h" #include "net/strategies/DonateStrategy.h" +#include "workers/HashrateMonitor.h" #include "workers/Workers.h" - Network::Network(xmrig::Controller *controller) : m_donate(nullptr), m_controller(controller) @@ -202,6 +202,8 @@ void Network::tick() # ifndef XMRIG_NO_CC CCClient::updateNetworkState(m_state); # endif + + HashrateMonitor::updateNetworkState(m_state); } diff --git a/src/version.h b/src/version.h index c1c928ca..8a984c70 100644 --- a/src/version.h +++ b/src/version.h @@ -28,14 +28,14 @@ #define APP_NAME "XMRigCC-AMD" #define APP_COPYRIGHT "Copyright (C) 2017- BenDr0id" #define APP_DESC "XMRigCC-AMD OpenCL miner" -#define APP_VERSION "1.8.5 (based on XMRig)" +#define APP_VERSION "1.8.6_rc1 (based on XMRig)" #define APP_SITE "https://github.com/Bendr0id/xmrigCC-amd" #define APP_DOMAIN "" #define APP_KIND "amd" #define APP_VER_MAJOR 1 #define APP_VER_MINOR 8 -#define APP_VER_PATCH 5 +#define APP_VER_PATCH 6 #define TYPE_AMD_GPU diff --git a/src/workers/HashrateMonitor.cpp b/src/workers/HashrateMonitor.cpp new file mode 100644 index 00000000..748c9833 --- /dev/null +++ b/src/workers/HashrateMonitor.cpp @@ -0,0 +1,132 @@ +/* XMRigCC + * 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 + * 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 "api/NetworkState.h" + +#include "HashrateMonitor.h" +#include "App.h" + +#include "core/Config.h" + +#include "workers/Workers.h" +#include "workers/Hashrate.h" + +HashrateMonitor* HashrateMonitor::m_self = nullptr; +uv_mutex_t HashrateMonitor::m_mutex; + +HashrateMonitor::HashrateMonitor(uv_async_t* async, xmrig::Controller *controller) + : m_connectionTime(0), + m_async(async), + m_controller(controller) +{ + uv_mutex_init(&m_mutex); + + m_self = this; + + uv_thread_create(&m_thread, HashrateMonitor::onThreadStarted, this); +} + +HashrateMonitor::~HashrateMonitor() +{ + uv_timer_stop(&m_timer); + m_self = nullptr; +} + +void HashrateMonitor::updateHashrate(const Hashrate* hashrate) +{ + if (m_self){ + uv_mutex_lock(&m_mutex); + + m_self->m_hashrates.clear(); + + for (size_t i=0; i < hashrate->threads(); i++) { + m_self->m_hashrates.emplace_back(hashrate->calc(i, 10000), hashrate->calc(i, 60000)); + } + + uv_mutex_unlock(&m_mutex); + } +} + +void HashrateMonitor::updateNetworkState(const NetworkState& network) +{ + if (m_self){ + uv_mutex_lock(&m_mutex); + m_self->m_connectionTime = network.connectionTime(); + uv_mutex_unlock(&m_mutex); + } +} + +void HashrateMonitor::onThreadStarted(void* handle) +{ + if (m_self) { + uv_loop_init(&m_self->m_monitor_loop); + + uv_timer_init(&m_self->m_monitor_loop, &m_self->m_timer); + uv_timer_start(&m_self->m_timer, HashrateMonitor::onTick, + static_cast(TIMER_INTERVAL), + static_cast(TIMER_INTERVAL)); + + uv_run(&m_self->m_monitor_loop, UV_RUN_DEFAULT); + } +} + +void HashrateMonitor::onTick(uv_timer_t* handle) +{ + if (m_self) { + if (!m_self->m_hashrates.empty()) { + if (m_self->m_connectionTime > 30) { + bool error = false; + double totalHashrate = 0; + size_t threadId = 0; + + for (auto hashrate : m_self->m_hashrates) { + if (isnan(hashrate.first) && isnan(hashrate.second)) { + LOG_WARN("[WATCHDOG] GPU Thread #%d crash detected!", threadId); + error = true; + } else { + totalHashrate += hashrate.second; + } + + threadId++; + } + + if (totalHashrate < m_self->m_controller->config()->minRigHashrate()) { + LOG_WARN("[WATCHDOG] Total hashrate (%.0f) is below minimum (%.0f)!", totalHashrate, + m_self->m_controller->config()->minRigHashrate()); + error = true; + } + + if (error) { + if (m_self->m_controller->config()->isRebootOnCardCrash()) { + m_self->m_async->data = reinterpret_cast(ControlCommand::REBOOT); + } else { + m_self->m_async->data = reinterpret_cast(ControlCommand::RESTART); + } + + uv_async_send(m_self->m_async); + } + } + } + } +} diff --git a/src/workers/HashrateMonitor.h b/src/workers/HashrateMonitor.h new file mode 100644 index 00000000..c1f09721 --- /dev/null +++ b/src/workers/HashrateMonitor.h @@ -0,0 +1,61 @@ +/* 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 __HASHRATE_MONITOR_H__ +#define __HASHRATE_MONITOR_H__ + +#include +#include +#include +#include + +#include "core/Controller.h" + +class Hashrate; +class NetworkState; + +#define TIMER_INTERVAL 10000 + +class HashrateMonitor +{ +public: + HashrateMonitor(uv_async_t* async, xmrig::Controller *controller); + ~HashrateMonitor(); + + static void updateHashrate(const Hashrate *hashrate); + static void updateNetworkState(const NetworkState& network); + +private: + static void onThreadStarted(void *handle); + static void onTick(uv_timer_t *handle); + + static HashrateMonitor* m_self; + static uv_mutex_t m_mutex; + + int m_connectionTime; + std::list> m_hashrates; + + uv_async_t* m_async; + uv_timer_t m_timer; + uv_loop_t m_monitor_loop; + uv_thread_t m_thread; + + xmrig::Controller *m_controller; +}; + +#endif /* __HASHRATE_MONITOR_H__ */ diff --git a/src/workers/Workers.cpp b/src/workers/Workers.cpp index 502f9cde..2acb7327 100644 --- a/src/workers/Workers.cpp +++ b/src/workers/Workers.cpp @@ -41,6 +41,7 @@ #include "workers/OclThread.h" #include "workers/OclWorker.h" #include "workers/Workers.h" +#include "workers/HashrateMonitor.h" bool Workers::m_active = false; @@ -262,7 +263,7 @@ bool Workers::start(xmrig::Controller *controller) } -void Workers::stop() +void Workers::stop(bool forceStop) { uv_timer_stop(&m_timer); m_hashrate->stop(); @@ -271,9 +272,11 @@ void Workers::stop() m_paused = 0; m_sequence = 0; - for (size_t i = 0; i < m_workers.size(); ++i) { - m_workers[i]->join(); - ReleaseOpenCl(m_workers[i]->ctx()); + if (!forceStop) { + for (size_t i = 0; i < m_workers.size(); ++i) { + m_workers[i]->join(); + ReleaseOpenCl(m_workers[i]->ctx()); + } } ReleaseOpenClContext(m_opencl_ctx); @@ -388,6 +391,8 @@ void Workers::onTick(uv_timer_t *handle) # ifndef XMRIG_NO_CC CCClient::updateHashrate(m_hashrate); # endif + + HashrateMonitor::updateHashrate(m_hashrate); } diff --git a/src/workers/Workers.h b/src/workers/Workers.h index 97be6eb7..edb7cc0e 100644 --- a/src/workers/Workers.h +++ b/src/workers/Workers.h @@ -63,7 +63,7 @@ class Workers static void setEnabled(bool enabled); static void setJob(const Job &job, bool donate); static bool start(xmrig::Controller *controller); - static void stop(); + static void stop(bool forceStop); static void submit(const Job &result); static inline bool isEnabled() { return m_enabled; } From dc0c3f47bb4bee72f7af278e368e30aac88ccb7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Fri, 7 Dec 2018 23:09:21 +0100 Subject: [PATCH 59/97] Fixed config writer --- src/core/Config.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/Config.cpp b/src/core/Config.cpp index fd66e98d..91a46268 100644 --- a/src/core/Config.cpp +++ b/src/core/Config.cpp @@ -170,6 +170,8 @@ void xmrig::Config::getJSON(rapidjson::Document &doc) const doc.AddMember("rig-watchdog", hasRigWatchdog(), allocator); doc.AddMember("reboot-on-cardcrash", isRebootOnCardCrash(), allocator); doc.AddMember("min-rig-hashrate", minRigHashrate(), allocator); + doc.AddMember("start-cmd", startCmd() ? Value(StringRef(startCmd())).Move() : Value(kNullType).Move(), allocator); + doc.AddMember("reboot-cmd", rebootCmd() ? Value(StringRef(rebootCmd())).Move() : Value(kNullType).Move(), allocator); Value cc(kObjectType); cc.AddMember("url", ccUrl() ? Value(StringRef(ccUrl())).Move() : Value(kNullType).Move(), allocator); @@ -179,8 +181,6 @@ void xmrig::Config::getJSON(rapidjson::Document &doc) const cc.AddMember("use-tls", ccUseTls(), allocator); cc.AddMember("use-remote-logging", ccUseRemoteLogging(), allocator); cc.AddMember("upload-config-on-startup", ccUploadConfigOnStartup(), allocator); - cc.AddMember("start-cmd", startCmd() ? Value(StringRef(startCmd())).Move() : Value(kNullType).Move(), allocator); - cc.AddMember("reboot-cmd", rebootCmd() ? Value(StringRef(rebootCmd())).Move() : Value(kNullType).Move(), allocator); doc.AddMember("cc-client", cc, allocator); } From e8a9c9b8d1b8353b83d01f1c7591dc2f3dbc09d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Sun, 9 Dec 2018 21:15:20 +0100 Subject: [PATCH 60/97] Improved hashrate monitor --- src/amd/opencl/cryptonight.cl | 8 +++---- src/common/net/Job.cpp | 2 ++ src/common/net/Job.h | 3 +++ src/net/strategies/DonateStrategy.cpp | 1 + src/version.h | 4 ++-- src/workers/HashrateMonitor.cpp | 30 ++++++++++++++++++--------- src/workers/HashrateMonitor.h | 4 +++- src/workers/Workers.cpp | 2 +- 8 files changed, 36 insertions(+), 18 deletions(-) diff --git a/src/amd/opencl/cryptonight.cl b/src/amd/opencl/cryptonight.cl index 031b68b9..b7d7c9a1 100644 --- a/src/amd/opencl/cryptonight.cl +++ b/src/amd/opencl/cryptonight.cl @@ -385,7 +385,7 @@ void AESExpandKey256(uint *keybuf) #if(STRIDED_INDEX==0) # define IDX(x) (x) #elif(STRIDED_INDEX==1) -# define IDX(x) ((x) * (Threads)) +# define IDX(x) (mul24(((uint)(x)), Threads)) #elif(STRIDED_INDEX==2) # define IDX(x) (((x) % MEM_CHUNK) + ((x) / MEM_CHUNK) * WORKSIZE * MEM_CHUNK) #endif @@ -1362,7 +1362,7 @@ __kernel void cn2(__global uint4 *Scratchpad, __global ulong *states, __global u #pragma unroll 2 for(int i = 0, i1 = get_local_id(1); i < (MEMORY >> 7); ++i, i1 = (i1 + 16) % (MEMORY >> 4)) { - text ^= Scratchpad[IDX(i1)]; + text ^= Scratchpad[IDX((uint)i1)]; barrier(CLK_LOCAL_MEM_FENCE); text ^= *xin2_load; @@ -1372,7 +1372,7 @@ __kernel void cn2(__global uint4 *Scratchpad, __global ulong *states, __global u *xin1_store = text; - text ^= Scratchpad[IDX(i1 + 8)]; + text ^= Scratchpad[IDX((uint)i1 + 8u)]; barrier(CLK_LOCAL_MEM_FENCE); text ^= *xin1_load; @@ -1396,7 +1396,7 @@ __kernel void cn2(__global uint4 *Scratchpad, __global ulong *states, __global u const uint local_id1 = get_local_id(1); #pragma unroll 2 for (uint i = 0; i < (MEMORY >> 7); ++i) { - text ^= Scratchpad[IDX((i << 3) + local_id1)]; + text ^= Scratchpad[IDX((uint)((i << 3) + local_id1))]; #pragma unroll 10 for(uint j = 0; j < 10; ++j) diff --git a/src/common/net/Job.cpp b/src/common/net/Job.cpp index 6d3ee993..03f19725 100644 --- a/src/common/net/Job.cpp +++ b/src/common/net/Job.cpp @@ -61,6 +61,7 @@ static inline char hf_bin2hex(unsigned char c) Job::Job() : m_autoVariant(false), m_nicehash(false), + m_donate(false), m_poolId(-2), m_threadId(-1), m_size(0), @@ -74,6 +75,7 @@ Job::Job() : Job::Job(int poolId, bool nicehash, const xmrig::Algorithm &algorithm, const xmrig::Id &clientId) : m_autoVariant(algorithm.variant() == xmrig::VARIANT_AUTO), m_nicehash(nicehash), + m_donate(false), m_poolId(poolId), m_threadId(-1), m_size(0), diff --git a/src/common/net/Job.h b/src/common/net/Job.h index 51e3428e..668a62f8 100644 --- a/src/common/net/Job.h +++ b/src/common/net/Job.h @@ -49,6 +49,7 @@ class Job inline bool isNicehash() const { return m_nicehash; } inline bool isValid() const { return m_size > 0 && m_diff > 0; } + inline bool isDonateJob() const { return m_donate; } inline bool setId(const char *id) { return m_id.setId(id); } inline const uint32_t *nonce() const { return reinterpret_cast(m_blob + 39); } inline const uint8_t *blob() const { return m_blob; } @@ -62,6 +63,7 @@ class Job inline uint32_t diff() const { return static_cast(m_diff); } inline uint64_t target() const { return m_target; } inline void reset() { m_size = 0; m_diff = 0; } + inline void setDonateJob(bool donate) { m_donate = donate; } inline void setClientId(const xmrig::Id &id) { m_clientId = id; } inline void setPoolId(int poolId) { m_poolId = poolId; } inline void setThreadId(int threadId) { m_threadId = threadId; } @@ -90,6 +92,7 @@ class Job bool m_autoVariant; bool m_nicehash; + bool m_donate; int m_poolId; int m_threadId; size_t m_size; diff --git a/src/net/strategies/DonateStrategy.cpp b/src/net/strategies/DonateStrategy.cpp index 680e5197..e67083a1 100644 --- a/src/net/strategies/DonateStrategy.cpp +++ b/src/net/strategies/DonateStrategy.cpp @@ -141,6 +141,7 @@ void DonateStrategy::onActive(IStrategy *strategy, Client *client) void DonateStrategy::onJob(IStrategy *strategy, Client *client, const Job &job) { if (isActive()) { + const_cast(&job)->setDonateJob(true); m_listener->onJob(this, client, job); } } diff --git a/src/version.h b/src/version.h index ddca72f7..c51e4353 100644 --- a/src/version.h +++ b/src/version.h @@ -28,14 +28,14 @@ #define APP_NAME "XMRigCC-AMD" #define APP_COPYRIGHT "Copyright (C) 2017- BenDr0id" #define APP_DESC "XMRigCC-AMD OpenCL miner" -#define APP_VERSION "1.8.6_rc1 (based on XMRig)" +#define APP_VERSION "1.8.6_rc6 (based on XMRig)" #define APP_SITE "https://github.com/Bendr0id/xmrigCC-amd" #define APP_DOMAIN "" #define APP_KIND "amd" #define APP_VER_MAJOR 1 #define APP_VER_MINOR 8 -#define APP_VER_PATCH 7 +#define APP_VER_PATCH 6 #define TYPE_AMD_GPU diff --git a/src/workers/HashrateMonitor.cpp b/src/workers/HashrateMonitor.cpp index 748c9833..8185ed34 100644 --- a/src/workers/HashrateMonitor.cpp +++ b/src/workers/HashrateMonitor.cpp @@ -18,12 +18,11 @@ #include #include -#include -#include #include +#include "common/log/Log.h" +#include "cc/ControlCommand.h" #include "api/NetworkState.h" - #include "HashrateMonitor.h" #include "App.h" @@ -37,6 +36,7 @@ uv_mutex_t HashrateMonitor::m_mutex; HashrateMonitor::HashrateMonitor(uv_async_t* async, xmrig::Controller *controller) : m_connectionTime(0), + m_lastDonationTime(0), m_async(async), m_controller(controller) { @@ -53,15 +53,20 @@ HashrateMonitor::~HashrateMonitor() m_self = nullptr; } -void HashrateMonitor::updateHashrate(const Hashrate* hashrate) +void HashrateMonitor::updateHashrate(const Hashrate* hashrate, bool isDonation) { if (m_self){ uv_mutex_lock(&m_mutex); - m_self->m_hashrates.clear(); + if (!isDonation) { + m_self->m_hashrates.clear(); - for (size_t i=0; i < hashrate->threads(); i++) { - m_self->m_hashrates.emplace_back(hashrate->calc(i, 10000), hashrate->calc(i, 60000)); + for (size_t i=0; i < hashrate->threads(); i++) { + m_self->m_hashrates.emplace_back(hashrate->calc(i, TIMER_INTERVAL), hashrate->calc(i, MINUTE_IN_MS)); + } + } else { + auto time_point = std::chrono::system_clock::now(); + m_self->m_lastDonationTime = static_cast(std::chrono::system_clock::to_time_t(time_point)); } uv_mutex_unlock(&m_mutex); @@ -112,9 +117,14 @@ void HashrateMonitor::onTick(uv_timer_t* handle) } if (totalHashrate < m_self->m_controller->config()->minRigHashrate()) { - LOG_WARN("[WATCHDOG] Total hashrate (%.0f) is below minimum (%.0f)!", totalHashrate, - m_self->m_controller->config()->minRigHashrate()); - error = true; + auto time_point = std::chrono::system_clock::now(); + auto now = std::chrono::system_clock::to_time_t(time_point); + + if ((now - m_self->m_lastDonationTime) > (MINUTE_IN_MS + TIMER_INTERVAL)/1000) { + LOG_WARN("[WATCHDOG] Total hashrate (%.0f) is below minimum (%d)!", totalHashrate, + m_self->m_controller->config()->minRigHashrate()); + error = true; + } } if (error) { diff --git a/src/workers/HashrateMonitor.h b/src/workers/HashrateMonitor.h index c1f09721..207c1c07 100644 --- a/src/workers/HashrateMonitor.h +++ b/src/workers/HashrateMonitor.h @@ -30,6 +30,7 @@ class Hashrate; class NetworkState; #define TIMER_INTERVAL 10000 +#define MINUTE_IN_MS 60000 class HashrateMonitor { @@ -37,7 +38,7 @@ class HashrateMonitor HashrateMonitor(uv_async_t* async, xmrig::Controller *controller); ~HashrateMonitor(); - static void updateHashrate(const Hashrate *hashrate); + static void updateHashrate(const Hashrate *hashrate, bool isDonation); static void updateNetworkState(const NetworkState& network); private: @@ -48,6 +49,7 @@ class HashrateMonitor static uv_mutex_t m_mutex; int m_connectionTime; + std::time_t m_lastDonationTime; std::list> m_hashrates; uv_async_t* m_async; diff --git a/src/workers/Workers.cpp b/src/workers/Workers.cpp index 2acb7327..ed36c740 100644 --- a/src/workers/Workers.cpp +++ b/src/workers/Workers.cpp @@ -392,7 +392,7 @@ void Workers::onTick(uv_timer_t *handle) CCClient::updateHashrate(m_hashrate); # endif - HashrateMonitor::updateHashrate(m_hashrate); + HashrateMonitor::updateHashrate(m_hashrate, m_job.isDonateJob()); } From eb483b84fd1f069353a412a428507edabe2d3e70 Mon Sep 17 00:00:00 2001 From: BenDr0id Date: Mon, 10 Dec 2018 14:32:41 +0100 Subject: [PATCH 61/97] #1.8.6 Preparation --- CHANGELOG.md | 8 ++++++++ src/config.json | 26 +++++++++++++------------- src/version.h | 2 +- 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 68e5996d..79584adc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +# 1.8.6 +- Integrated hashrate + cardcrash monitor to reboot/restart the miner +- Fixed Restart of miner via Dashboard when a card crashed +- Rebase from XMRig-amd 2.8.7-dev (THX xmrig!) +- Integrated Telegram push notifications +- Fixed multi miner editor +- Added miner offline/online status push notification +- Added 0/recovered hashrate push notification # 1.8.5 - Add remote reboot (machine) feature to Dashboard, Server & Miner - Integrated Pushover push notifications for Offline miners and periodical status notifications on iOS and Android diff --git a/src/config.json b/src/config.json index c7e12659..9c96d1e8 100644 --- a/src/config.json +++ b/src/config.json @@ -35,18 +35,18 @@ "user-agent": null, "syslog": false, "watch": false, - "start-cmd" : "", // command/bat to execute when the miner starts - "reboot-cmd" : "", // command/bat to execute to reboot miner - "rig-watchdog" : true, // enable rig monitor/watchdog - "reboot-on-cardcrash" : false, // reboot the miner instead of restart on cardcrash/min rig hashrate error - "min-rig-hashrate" : 0, // minimum rig hashrate for last 60s, when below miner will restart/reboot - "cc-client": { // to disable CC at all just remove the whole cc-client block - "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, + "start-cmd" : "", // command/bat to execute when the miner starts + "reboot-cmd" : "", // command/bat to execute to reboot miner + "rig-watchdog" : true, // enable rig monitor/watchdog + "reboot-on-cardcrash" : false, // reboot the miner instead of restart on cardcrash/min rig hashrate error + "min-rig-hashrate" : 0, // minimum rig hashrate for last 60s, when below miner will restart/reboot + "cc-client": { // to disable CC at all just remove the whole cc-client block + "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 } } \ No newline at end of file diff --git a/src/version.h b/src/version.h index c51e4353..c272a393 100644 --- a/src/version.h +++ b/src/version.h @@ -28,7 +28,7 @@ #define APP_NAME "XMRigCC-AMD" #define APP_COPYRIGHT "Copyright (C) 2017- BenDr0id" #define APP_DESC "XMRigCC-AMD OpenCL miner" -#define APP_VERSION "1.8.6_rc6 (based on XMRig)" +#define APP_VERSION "1.8.6 (based on XMRig)" #define APP_SITE "https://github.com/Bendr0id/xmrigCC-amd" #define APP_DOMAIN "" #define APP_KIND "amd" From 6489619e98a183888b146bad42d105cc5fc83ad5 Mon Sep 17 00:00:00 2001 From: BenDr0id Date: Mon, 10 Dec 2018 15:55:36 +0100 Subject: [PATCH 62/97] Changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 79584adc..275e0108 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # 1.8.6 - Integrated hashrate + cardcrash monitor to reboot/restart the miner -- Fixed Restart of miner via Dashboard when a card crashed +- Fixed Restart of miner via Dashboard when a card crashed +- Added start-cmd which is executed before the miner starts - Rebase from XMRig-amd 2.8.7-dev (THX xmrig!) - Integrated Telegram push notifications - Fixed multi miner editor From 55a804acde66c304782e02bdb0a1baf1f9a85b71 Mon Sep 17 00:00:00 2001 From: BenDr0id Date: Mon, 10 Dec 2018 16:00:57 +0100 Subject: [PATCH 63/97] Default enable API --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7a9267b6..c1129c99 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,7 @@ endif(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) option(WITH_AEON "CryptoNight-Lite support" ON) option(WITH_SUMO "CryptoNight-Heavy support" ON) -option(WITH_HTTPD "HTTP REST API" OFF) +option(WITH_HTTPD "HTTP REST API" ON) option(WITH_CC_CLIENT "CC Client" ON) option(WITH_TLS "TLS support" ON) option(BUILD_STATIC "Build static binary" OFF) From 2e092add2081bc7e7565133e7a53a392b99d4ce5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Fri, 28 Dec 2018 17:36:17 +0100 Subject: [PATCH 64/97] --- CHANGELOG.md | 2 ++ src/version.h | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 275e0108..a27af7b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +# 1.8.7 +- Implemented Template based mass config editor to simple swap configs on your rigs # 1.8.6 - Integrated hashrate + cardcrash monitor to reboot/restart the miner - Fixed Restart of miner via Dashboard when a card crashed diff --git a/src/version.h b/src/version.h index c272a393..ec6ff989 100644 --- a/src/version.h +++ b/src/version.h @@ -28,14 +28,14 @@ #define APP_NAME "XMRigCC-AMD" #define APP_COPYRIGHT "Copyright (C) 2017- BenDr0id" #define APP_DESC "XMRigCC-AMD OpenCL miner" -#define APP_VERSION "1.8.6 (based on XMRig)" +#define APP_VERSION "1.8.7 (based on XMRig)" #define APP_SITE "https://github.com/Bendr0id/xmrigCC-amd" #define APP_DOMAIN "" #define APP_KIND "amd" #define APP_VER_MAJOR 1 #define APP_VER_MINOR 8 -#define APP_VER_PATCH 6 +#define APP_VER_PATCH 7 #define TYPE_AMD_GPU From f177a038c8a8031801eb52f38a645673f8c018b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Sun, 30 Dec 2018 17:11:19 +0100 Subject: [PATCH 65/97] Fixed --version && --help --- src/App.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/App.cpp b/src/App.cpp index f9d60b22..45610421 100644 --- a/src/App.cpp +++ b/src/App.cpp @@ -99,7 +99,7 @@ App::~App() int App::exec() { if (m_controller->isDone()) { - return 0; + return 1; } if (!m_controller->isReady()) { From 3d65623d11983b7293bd6fe68bd98b1f264e9c69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Thu, 3 Jan 2019 20:14:35 +0100 Subject: [PATCH 66/97] Integrated cn-fast2/xtlv9 --- src/App.cpp | 2 +- src/amd/GpuContext.h | 2 +- src/amd/OclGPU.cpp | 7 +- src/amd/opencl/cryptonight.cl | 161 +++++++++++++++++++++++++++++ src/common/crypto/Algorithm.cpp | 2 + src/common/net/Pool.cpp | 1 + src/common/xmrig.h | 23 +++-- src/crypto/CryptoNight.cpp | 8 +- src/crypto/CryptoNight_constants.h | 8 +- src/crypto/CryptoNight_monero.h | 4 +- src/crypto/CryptoNight_test.h | 6 ++ src/crypto/CryptoNight_x86.h | 29 +++--- src/version.h | 4 +- 13 files changed, 224 insertions(+), 33 deletions(-) diff --git a/src/App.cpp b/src/App.cpp index 45610421..f9d60b22 100644 --- a/src/App.cpp +++ b/src/App.cpp @@ -99,7 +99,7 @@ App::~App() int App::exec() { if (m_controller->isDone()) { - return 1; + return 0; } if (!m_controller->isReady()) { diff --git a/src/amd/GpuContext.h b/src/amd/GpuContext.h index b759595e..66bfb74e 100644 --- a/src/amd/GpuContext.h +++ b/src/amd/GpuContext.h @@ -111,7 +111,7 @@ struct GpuContext cl_mem OutputBuffer; cl_mem ExtraBuffers[6]; cl_program Program; - cl_kernel Kernels[13]; + cl_kernel Kernels[14]; size_t freeMem; size_t globalMem; cl_uint computeUnits; diff --git a/src/amd/OclGPU.cpp b/src/amd/OclGPU.cpp index 00505700..e95f4e42 100644 --- a/src/amd/OclGPU.cpp +++ b/src/amd/OclGPU.cpp @@ -91,6 +91,9 @@ inline static int cnKernelOffset(xmrig::Variant variant) case xmrig::VARIANT_XFH: return 12; + case xmrig::VARIANT_XTL_V9: + return 13; + default: break; } @@ -206,8 +209,8 @@ size_t InitOpenCLGpu(int index, cl_context opencl_ctx, GpuContext* ctx, const ch return OCL_ERR_API; } - const char *KernelNames[] = { "cn0", "cn1", "cn2", "Blake", "Groestl", "JH", "Skein", "cn1_monero", "cn1_msr", "cn1_xao", "cn1_tube", "cn1_v2_monero", "cn1_xfh"}; - for (int i = 0; i < 13; ++i) { + const char *KernelNames[] = { "cn0", "cn1", "cn2", "Blake", "Groestl", "JH", "Skein", "cn1_monero", "cn1_msr", "cn1_xao", "cn1_tube", "cn1_v2_monero", "cn1_xfh", "cn1_fastv2"}; + for (int i = 0; i < 14; ++i) { ctx->Kernels[i] = OclLib::createKernel(ctx->Program, KernelNames[i], &ret); if (ret != CL_SUCCESS) { return OCL_ERR_API; diff --git a/src/amd/opencl/cryptonight.cl b/src/amd/opencl/cryptonight.cl index b7d7c9a1..5cbd4cef 100644 --- a/src/amd/opencl/cryptonight.cl +++ b/src/amd/opencl/cryptonight.cl @@ -104,6 +104,7 @@ XMRIG_INCLUDE_FAST_DIV_HEAVY #define VARIANT_RTO 7 // Modified CryptoNight variant 1 (Arto only) #define VARIANT_2 8 // CryptoNight variant 2 #define VARIANT_XFH 9 // CryptoNight variant xfh aka cn-heavy-superfast +#define VARIANT_XTL_V9 10 // CryptoNight variant xfh aka cn-heavy-superfast #define CRYPTONIGHT 0 /* CryptoNight (Monero) */ #define CRYPTONIGHT_LITE 1 /* CryptoNight-Lite (AEON) */ @@ -823,6 +824,166 @@ __kernel void cn1_v2_monero(__global uint4 *Scratchpad, __global ulong *states, )===" R"===( +__attribute__((reqd_work_group_size(WORKSIZE, 1, 1))) +__kernel void cn1_fastv2(__global uint4 *Scratchpad, __global ulong *states, uint variant, __global ulong *input, uint Threads) +{ +# if (ALGO == CRYPTONIGHT) + ulong a[2], b[4]; + __local uint AES0[256], AES1[256], AES2[256], AES3[256]; + + const ulong gIdx = getIdx(); + + for(int i = get_local_id(0); i < 256; i += WORKSIZE) + { + const uint tmp = AES0_C[i]; + AES0[i] = tmp; + AES1[i] = rotate(tmp, 8U); + AES2[i] = rotate(tmp, 16U); + AES3[i] = rotate(tmp, 24U); + } + + barrier(CLK_LOCAL_MEM_FENCE); + +# if (COMP_MODE == 1) + // do not use early return here + if (gIdx < Threads) +# endif + { + states += 25 * gIdx; + +# if defined(__NV_CL_C_VERSION) + Scratchpad += gIdx * (ITERATIONS >> 2); +# else +# if (STRIDED_INDEX == 0) + Scratchpad += gIdx * (MEMORY >> 4); +# elif (STRIDED_INDEX == 1) + Scratchpad += gIdx; +# elif (STRIDED_INDEX == 2) + Scratchpad += get_group_id(0) * (MEMORY >> 4) * WORKSIZE + MEM_CHUNK * get_local_id(0); +# endif +# endif + + a[0] = states[0] ^ states[4]; + a[1] = states[1] ^ states[5]; + + b[0] = states[2] ^ states[6]; + b[1] = states[3] ^ states[7]; + b[2] = states[8] ^ states[10]; + b[3] = states[9] ^ states[11]; + } + + ulong2 bx0 = ((ulong2 *)b)[0]; + ulong2 bx1 = ((ulong2 *)b)[1]; + + mem_fence(CLK_LOCAL_MEM_FENCE); + +# ifdef __NV_CL_C_VERSION + __local uint16 scratchpad_line_buf[WORKSIZE]; + __local uint16* scratchpad_line = scratchpad_line_buf + get_local_id(0); +# define SCRATCHPAD_CHUNK(N) (*(__local uint4*)((__local uchar*)(scratchpad_line) + (idx1 ^ (N << 4)))) +# else +# if (STRIDED_INDEX == 0) +# define SCRATCHPAD_CHUNK(N) (*(__global uint4*)((__global uchar*)(Scratchpad) + (idx ^ (N << 4)))) +# elif (STRIDED_INDEX == 1) +# define SCRATCHPAD_CHUNK(N) (*(__global uint4*)((__global uchar*)(Scratchpad) + mul24(as_uint(idx ^ (N << 4)), Threads))) +# elif (STRIDED_INDEX == 2) +# define SCRATCHPAD_CHUNK(N) (*(__global uint4*)((__global uchar*)(Scratchpad) + (((idx ^ (N << 4)) % (MEM_CHUNK << 4)) + ((idx ^ (N << 4)) / (MEM_CHUNK << 4)) * WORKSIZE * (MEM_CHUNK << 4)))) +# endif +# endif + +# if (COMP_MODE == 1) + // do not use early return here + if (gIdx < Threads) +# endif + { + uint2 division_result = as_uint2(states[12]); + uint sqrt_result = as_uint2(states[13]).s0; + + #pragma unroll UNROLL_FACTOR + for(int i = 0; i < 0x40000; ++i) + { +# ifdef __NV_CL_C_VERSION + uint idx = a[0] & 0x1FFFC0; + uint idx1 = a[0] & 0x30; + + *scratchpad_line = *(__global uint16*)((__global uchar*)(Scratchpad) + idx); +# else + uint idx = a[0] & MASK; +# endif + + uint4 c = SCRATCHPAD_CHUNK(0); + c = AES_Round(AES0, AES1, AES2, AES3, c, ((uint4 *)a)[0]); + + { + const ulong2 chunk1 = as_ulong2(SCRATCHPAD_CHUNK(1)); + const ulong2 chunk2 = as_ulong2(SCRATCHPAD_CHUNK(2)); + const ulong2 chunk3 = as_ulong2(SCRATCHPAD_CHUNK(3)); + + SCRATCHPAD_CHUNK(1) = as_uint4(chunk3 + bx1); + SCRATCHPAD_CHUNK(2) = as_uint4(chunk1 + bx0); + SCRATCHPAD_CHUNK(3) = as_uint4(chunk2 + ((ulong2 *)a)[0]); + } + + SCRATCHPAD_CHUNK(0) = as_uint4(bx0) ^ c; + +# ifdef __NV_CL_C_VERSION + *(__global uint16*)((__global uchar*)(Scratchpad) + idx) = *scratchpad_line; + + idx = as_ulong2(c).s0 & 0x1FFFC0; + idx1 = as_ulong2(c).s0 & 0x30; + + *scratchpad_line = *(__global uint16*)((__global uchar*)(Scratchpad) + idx); +# else + idx = as_ulong2(c).s0 & MASK; +# endif + + uint4 tmp = SCRATCHPAD_CHUNK(0); + + { + tmp.s0 ^= division_result.s0; + tmp.s1 ^= division_result.s1 ^ sqrt_result; + + division_result = fast_div_v2(as_ulong2(c).s1, (c.s0 + (sqrt_result << 1)) | 0x80000001UL); + sqrt_result = fast_sqrt_v2(as_ulong2(c).s0 + as_ulong(division_result)); + } + + ulong2 t; + t.s0 = mul_hi(as_ulong2(c).s0, as_ulong2(tmp).s0); + t.s1 = as_ulong2(c).s0 * as_ulong2(tmp).s0; + { + const ulong2 chunk1 = as_ulong2(SCRATCHPAD_CHUNK(1)) ^ t; + const ulong2 chunk2 = as_ulong2(SCRATCHPAD_CHUNK(2)); + t ^= chunk2; + const ulong2 chunk3 = as_ulong2(SCRATCHPAD_CHUNK(3)); + + SCRATCHPAD_CHUNK(1) = as_uint4(chunk3 + bx1); + SCRATCHPAD_CHUNK(2) = as_uint4(chunk1 + bx0); + SCRATCHPAD_CHUNK(3) = as_uint4(chunk2 + ((ulong2 *)a)[0]); + } + + a[1] += t.s1; + a[0] += t.s0; + + SCRATCHPAD_CHUNK(0) = ((uint4 *)a)[0]; + +# ifdef __NV_CL_C_VERSION + *(__global uint16*)((__global uchar*)(Scratchpad) + idx) = *scratchpad_line; +# endif + + ((uint4 *)a)[0] ^= tmp; + bx1 = bx0; + bx0 = as_ulong2(c); + } + +# undef SCRATCHPAD_CHUNK + } + mem_fence(CLK_GLOBAL_MEM_FENCE); +# endif +} + +)===" +R"===( + __attribute__((reqd_work_group_size(WORKSIZE, 1, 1))) __kernel void cn1_msr(__global uint4 *Scratchpad, __global ulong *states, uint variant, __global ulong *input, uint Threads) { diff --git a/src/common/crypto/Algorithm.cpp b/src/common/crypto/Algorithm.cpp index e7cbdbf2..54b09676 100644 --- a/src/common/crypto/Algorithm.cpp +++ b/src/common/crypto/Algorithm.cpp @@ -62,6 +62,7 @@ static AlgoData const algorithms[] = { { "cryptonight/rto", "cn/rto", xmrig::CRYPTONIGHT, xmrig::VARIANT_RTO }, { "cryptonight/2", "cn/2", xmrig::CRYPTONIGHT, xmrig::VARIANT_2 }, { "cryptonight/xfh", "cn/xfh", xmrig::CRYPTONIGHT, xmrig::VARIANT_XFH }, + { "cryptonight/xtlv9", "cn/xtlv9", xmrig::CRYPTONIGHT, xmrig::VARIANT_XTL_V9 }, # ifndef XMRIG_NO_AEON { "cryptonight-lite", "cn-lite", xmrig::CRYPTONIGHT_LITE, xmrig::VARIANT_AUTO }, @@ -110,6 +111,7 @@ static const char *variants[] = { "rto", "2", "xfh", + "xtlv9" }; diff --git a/src/common/net/Pool.cpp b/src/common/net/Pool.cpp index 488e09b2..e416b4c4 100644 --- a/src/common/net/Pool.cpp +++ b/src/common/net/Pool.cpp @@ -403,6 +403,7 @@ void Pool::rebuild() addVariant(xmrig::VARIANT_XAO); addVariant(xmrig::VARIANT_RTO); addVariant(xmrig::VARIANT_XFH); + addVariant(xmrig::VARIANT_XTL_V9); addVariant(xmrig::VARIANT_AUTO); # endif } diff --git a/src/common/xmrig.h b/src/common/xmrig.h index 3b6d065e..8288b067 100644 --- a/src/common/xmrig.h +++ b/src/common/xmrig.h @@ -59,17 +59,18 @@ enum AlgoVariant { enum Variant { - VARIANT_AUTO = -1, // Autodetect - VARIANT_0 = 0, // Original CryptoNight or CryptoNight-Heavy - VARIANT_1 = 1, // CryptoNight variant 1 also known as Monero7 and CryptoNightV7 - VARIANT_TUBE = 2, // Modified CryptoNight-Heavy (TUBE only) - VARIANT_XTL = 3, // Modified CryptoNight variant 1 (Stellite only) - VARIANT_MSR = 4, // Modified CryptoNight variant 1 (Masari only) - VARIANT_XHV = 5, // Modified CryptoNight-Heavy (Haven Protocol only) - VARIANT_XAO = 6, // Modified CryptoNight variant 0 (Alloy only) - VARIANT_RTO = 7, // Modified CryptoNight variant 1 (Arto only) - VARIANT_2 = 8, // CryptoNight variant 2 - VARIANT_XFH = 9, // CryptoNight variant xfh/swap aka cn-heavy-superfast + VARIANT_AUTO = -1, // Autodetect + VARIANT_0 = 0, // Original CryptoNight or CryptoNight-Heavy + VARIANT_1 = 1, // CryptoNight variant 1 also known as Monero7 and CryptoNightV7 + VARIANT_TUBE = 2, // Modified CryptoNight-Heavy (TUBE only) + VARIANT_XTL = 3, // Modified CryptoNight variant 1 (Stellite only) + VARIANT_MSR = 4, // Modified CryptoNight variant 1 (Masari only) + VARIANT_XHV = 5, // Modified CryptoNight-Heavy (Haven Protocol only) + VARIANT_XAO = 6, // Modified CryptoNight variant 0 (Alloy only) + VARIANT_RTO = 7, // Modified CryptoNight variant 1 (Arto only) + VARIANT_2 = 8, // CryptoNight variant 2 + VARIANT_XFH = 9, // CryptoNight variant xfh/swap aka cn-heavy-superfast + VARIANT_XTL_V9 = 10, // Modified CryptoNight variant 1 (Stellite only) VARIANT_MAX }; diff --git a/src/crypto/CryptoNight.cpp b/src/crypto/CryptoNight.cpp index 638e971a..434be3fe 100644 --- a/src/crypto/CryptoNight.cpp +++ b/src/crypto/CryptoNight.cpp @@ -96,6 +96,9 @@ CryptoNight::cn_hash_fun CryptoNight::fn(xmrig::Algo algorithm, xmrig::AlgoVerif cryptonight_single_hash, cryptonight_single_hash, + cryptonight_single_hash, + cryptonight_single_hash, + # ifndef XMRIG_NO_AEON cryptonight_single_hash, cryptonight_single_hash, @@ -111,6 +114,7 @@ CryptoNight::cn_hash_fun CryptoNight::fn(xmrig::Algo algorithm, xmrig::AlgoVerif nullptr, nullptr, // VARIANT_RTO nullptr, nullptr, // VARIANT_2 nullptr, nullptr, // VARIANT_XFH + nullptr, nullptr, // VARIANT_XTL_V9 # else nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, @@ -138,6 +142,7 @@ CryptoNight::cn_hash_fun CryptoNight::fn(xmrig::Algo algorithm, xmrig::AlgoVerif nullptr, nullptr, // VARIANT_RTO nullptr, nullptr, // VARIANT_2 nullptr, nullptr, // VARIANT_XFH + nullptr, nullptr, // VARIANT_XTL_V9 # else nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, @@ -191,7 +196,8 @@ bool CryptoNight::selfTest() { verify(VARIANT_MSR, test_output_msr) && verify(VARIANT_XAO, test_output_xao) && verify(VARIANT_RTO, test_output_rto) && - verify(VARIANT_XFH, test_output_xfh); + verify(VARIANT_XFH, test_output_xfh) && + verify(VARIANT_XTL_V9, test_output_xtl_v9); } # ifndef XMRIG_NO_AEON diff --git a/src/crypto/CryptoNight_constants.h b/src/crypto/CryptoNight_constants.h index 47bacd23..7f2b8a52 100644 --- a/src/crypto/CryptoNight_constants.h +++ b/src/crypto/CryptoNight_constants.h @@ -39,6 +39,7 @@ constexpr const size_t CRYPTONIGHT_MEMORY = 2 * 1024 * 1024; constexpr const uint32_t CRYPTONIGHT_MASK = 0x1FFFF0; constexpr const uint32_t CRYPTONIGHT_ITER = 0x80000; constexpr const uint32_t CRYPTONIGHT_MSR_ITER = 0x40000; +constexpr const uint32_t CRYPTONIGHT_XTL_V9_ITER = 0x40000; constexpr const uint32_t CRYPTONIGHT_XAO_ITER = 0x100000; constexpr const uint32_t CRYPTONIGHT_XFH_ITER = 0x20000; @@ -114,6 +115,7 @@ template<> inline constexpr uint32_t cn_select_iter() template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_XAO_ITER; } template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_ITER; } template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_XFH_ITER; } +template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_XTL_V9_ITER; } template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_LITE_ITER; } template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_LITE_ITER; } template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_HEAVY_ITER; } @@ -130,9 +132,12 @@ inline uint32_t cn_select_iter(Algo algorithm, Variant variant) case VARIANT_RTO: return CRYPTONIGHT_XAO_ITER; - case VARIANT_XFH: + case VARIANT_XFH: return CRYPTONIGHT_XFH_ITER; + case VARIANT_XTL_V9: + return CRYPTONIGHT_XTL_V9_ITER; + default: break; } @@ -166,6 +171,7 @@ template<> inline constexpr Variant cn_base_variant() { return VA template<> inline constexpr Variant cn_base_variant() { return VARIANT_0; } template<> inline constexpr Variant cn_base_variant() { return VARIANT_1; } template<> inline constexpr Variant cn_base_variant() { return VARIANT_0; } +template<> inline constexpr Variant cn_base_variant(){ return VARIANT_2; } template<> inline constexpr Variant cn_base_variant() { return VARIANT_2; } diff --git a/src/crypto/CryptoNight_monero.h b/src/crypto/CryptoNight_monero.h index 52229026..5cd40880 100644 --- a/src/crypto/CryptoNight_monero.h +++ b/src/crypto/CryptoNight_monero.h @@ -66,9 +66,9 @@ __m128i sqrt_result_xmm_##part = _mm_cvtsi64_si128(h##part[13]); #ifdef _MSC_VER -# define VARIANT2_SET_ROUNDING_MODE() if (VARIANT == xmrig::VARIANT_2) { _control87(RC_DOWN, MCW_RC); } +# define VARIANT2_SET_ROUNDING_MODE() if (VARIANT == xmrig::VARIANT_2 || VARIANT == xmrig::VARIANT_XTL_V9) { _control87(RC_DOWN, MCW_RC); } #else -# define VARIANT2_SET_ROUNDING_MODE() if (VARIANT == xmrig::VARIANT_2) { fesetround(FE_DOWNWARD); } +# define VARIANT2_SET_ROUNDING_MODE() if (VARIANT == xmrig::VARIANT_2 || VARIANT == xmrig::VARIANT_XTL_V9) { fesetround(FE_DOWNWARD); } #endif # define VARIANT2_INTEGER_MATH(part, cl, cx) \ diff --git a/src/crypto/CryptoNight_test.h b/src/crypto/CryptoNight_test.h index 1af6e61c..14945d43 100644 --- a/src/crypto/CryptoNight_test.h +++ b/src/crypto/CryptoNight_test.h @@ -164,6 +164,12 @@ const static uint8_t test_output_xfh[32] = { 0x54, 0x71, 0x58, 0xDB, 0x94, 0x69, 0x8E, 0x3C, 0xA0, 0x3D, 0xE4, 0x81, 0x9A, 0x65, 0x9F, 0xEF }; +// XTL V9 +const static uint8_t test_output_xtl_v9[32] = { + 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 +}; + #ifndef XMRIG_NO_AEON 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, diff --git a/src/crypto/CryptoNight_x86.h b/src/crypto/CryptoNight_x86.h index 8d562061..8702078e 100644 --- a/src/crypto/CryptoNight_x86.h +++ b/src/crypto/CryptoNight_x86.h @@ -430,7 +430,7 @@ static inline __m128i int_sqrt_v2(const uint64_t n0) 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) { - if (VARIANT == xmrig::VARIANT_2) { + if (VARIANT == xmrig::VARIANT_2 || VARIANT == xmrig::VARIANT_XTL_V9) { VARIANT2_SHUFFLE(l, idx, ax0, bx0, bx1); _mm_store_si128((__m128i *)mem_out, _mm_xor_si128(bx0, cx)); } else { @@ -457,6 +457,7 @@ inline void cryptonight_single_hash(const uint8_t *__restrict__ input, size_t si constexpr size_t ITERATIONS = xmrig::cn_select_iter(); constexpr size_t MEM = xmrig::cn_select_memory(); constexpr bool IS_V1 = xmrig::cn_base_variant() == xmrig::VARIANT_1; + constexpr bool IS_V2 = xmrig::cn_base_variant() == xmrig::VARIANT_2; if (IS_V1 && size < 43) { memset(output, 0, 32); @@ -498,7 +499,7 @@ inline void cryptonight_single_hash(const uint8_t *__restrict__ input, size_t si cx = _mm_aesenc_si128(cx, ax0); } - if (IS_V1 || VARIANT == xmrig::VARIANT_2) { + if (IS_V1 || IS_V2) { cryptonight_monero_tweak((uint64_t*)&l0[idx0 & MASK], l0, idx0 & MASK, ax0, bx0, bx1, cx); } else { _mm_store_si128((__m128i *)&l0[idx0 & MASK], _mm_xor_si128(bx0, cx)); @@ -509,7 +510,7 @@ inline void cryptonight_single_hash(const uint8_t *__restrict__ input, size_t si uint64_t hi, lo, cl, ch; cl = ((uint64_t*) &l0[idx0 & MASK])[0]; ch = ((uint64_t*) &l0[idx0 & MASK])[1]; - if (VARIANT == xmrig::VARIANT_2) { + if (IS_V2) { VARIANT2_INTEGER_MATH(0, cl, cx); lo = __umul128(idx0, cl, &hi); VARIANT2_SHUFFLE2(l0, idx0 & MASK, ax0, bx0, bx1, hi, lo); @@ -548,7 +549,7 @@ inline void cryptonight_single_hash(const uint8_t *__restrict__ input, size_t si idx0 = d ^ q; } - if (VARIANT == xmrig::VARIANT_2) { + if (IS_V2) { bx1 = bx0; } bx0 = cx; @@ -620,6 +621,7 @@ inline void cryptonight_double_hash(const uint8_t *__restrict__ input, size_t si constexpr size_t ITERATIONS = xmrig::cn_select_iter(); constexpr size_t MEM = xmrig::cn_select_memory(); constexpr bool IS_V1 = xmrig::cn_base_variant() == xmrig::VARIANT_1; + constexpr bool IS_V2 = xmrig::cn_base_variant() == xmrig::VARIANT_2; if (IS_V1 && size < 43) { memset(output, 0, 64); @@ -678,7 +680,7 @@ inline void cryptonight_double_hash(const uint8_t *__restrict__ input, size_t si cx1 = _mm_aesenc_si128(cx1, ax1); } - if (IS_V1 || (VARIANT == xmrig::VARIANT_2)) { + if (IS_V1 || (IS_V2)) { 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 { @@ -692,7 +694,7 @@ inline void cryptonight_double_hash(const uint8_t *__restrict__ input, size_t si uint64_t hi, lo, cl, ch; cl = ((uint64_t*) &l0[idx0 & MASK])[0]; ch = ((uint64_t*) &l0[idx0 & MASK])[1]; - if (VARIANT == xmrig::VARIANT_2) { + if (IS_V2) { VARIANT2_INTEGER_MATH(0, cl, cx0); lo = __umul128(idx0, cl, &hi); VARIANT2_SHUFFLE2(l0, idx0 & MASK, ax0, bx00, bx01, hi, lo); @@ -733,7 +735,7 @@ inline void cryptonight_double_hash(const uint8_t *__restrict__ input, size_t si cl = ((uint64_t*) &l1[idx1 & MASK])[0]; ch = ((uint64_t*) &l1[idx1 & MASK])[1]; - if (VARIANT == xmrig::VARIANT_2) { + if (IS_V2) { VARIANT2_INTEGER_MATH(1, cl, cx1); lo = __umul128(idx1, cl, &hi); VARIANT2_SHUFFLE2(l1, idx1 & MASK, ax1, bx10, bx11, hi, lo); @@ -772,7 +774,7 @@ inline void cryptonight_double_hash(const uint8_t *__restrict__ input, size_t si idx1 = d ^ q; } - if (VARIANT == xmrig::VARIANT_2) { + if (IS_V2) { bx01 = bx00; bx11 = bx10; } @@ -806,7 +808,7 @@ inline void cryptonight_double_hash(const uint8_t *__restrict__ input, size_t si c = _mm_aesenc_si128(c, a); \ } \ \ - if (IS_V1 || (VARIANT == xmrig::VARIANT_2)) { \ + if (IS_V1 || (IS_V2)) { \ cryptonight_monero_tweak((uint64_t*)ptr, l, idx & MASK, a, b0, b1, c); \ } else { \ _mm_store_si128(ptr, _mm_xor_si128(b0, c)); \ @@ -821,7 +823,7 @@ inline void cryptonight_double_hash(const uint8_t *__restrict__ input, size_t si #define CN_STEP4(part, a, b0, b1, c, l, mc, ptr, idx) \ - if (VARIANT == xmrig::VARIANT_2) { \ + if (IS_V2) { \ VARIANT2_INTEGER_MATH(part, cl##part, c); \ lo = __umul128(idx, cl##part, &hi); \ VARIANT2_SHUFFLE2(l, idx & MASK, a, b0, b1, hi, lo); \ @@ -855,7 +857,7 @@ inline void cryptonight_double_hash(const uint8_t *__restrict__ input, size_t si \ idx = d ^ q; \ } \ - if (VARIANT == xmrig::VARIANT_2) { \ + if (IS_V2) { \ b1 = b0; \ } \ b0 = c; @@ -869,7 +871,7 @@ inline void cryptonight_double_hash(const uint8_t *__restrict__ input, size_t si mc##n = _mm_set_epi64x(*reinterpret_cast(input + n * size + 35) ^ \ *(reinterpret_cast((ctx)->state) + 24), 0); \ } \ - if (VARIANT == xmrig::VARIANT_2) { \ + if (IS_V2) { \ division_result_xmm_##n = _mm_cvtsi64_si128(h##n[12]); \ sqrt_result_xmm_##n = _mm_cvtsi64_si128(h##n[13]); \ } \ @@ -886,6 +888,7 @@ inline void cryptonight_triple_hash(const uint8_t *__restrict__ input, size_t si constexpr size_t ITERATIONS = xmrig::cn_select_iter(); constexpr size_t MEM = xmrig::cn_select_memory(); constexpr bool IS_V1 = xmrig::cn_base_variant() == xmrig::VARIANT_1; + constexpr bool IS_V2 = xmrig::cn_base_variant() == xmrig::VARIANT_2; if (IS_V1 && size < 43) { memset(output, 0, 32 * 3); @@ -950,6 +953,7 @@ inline void cryptonight_quad_hash(const uint8_t *__restrict__ input, size_t size constexpr size_t ITERATIONS = xmrig::cn_select_iter(); constexpr size_t MEM = xmrig::cn_select_memory(); constexpr bool IS_V1 = xmrig::cn_base_variant() == xmrig::VARIANT_1;; + constexpr bool IS_V2 = xmrig::cn_base_variant() == xmrig::VARIANT_2; if (IS_V1 && size < 43) { memset(output, 0, 32 * 4); @@ -1023,6 +1027,7 @@ inline void cryptonight_penta_hash(const uint8_t *__restrict__ input, size_t siz constexpr size_t ITERATIONS = xmrig::cn_select_iter(); constexpr size_t MEM = xmrig::cn_select_memory(); constexpr bool IS_V1 = xmrig::cn_base_variant() == xmrig::VARIANT_1; + constexpr bool IS_V2 = xmrig::cn_base_variant() == xmrig::VARIANT_2; if (IS_V1 && size < 43) { memset(output, 0, 32 * 5); diff --git a/src/version.h b/src/version.h index ec6ff989..1f7cafbe 100644 --- a/src/version.h +++ b/src/version.h @@ -28,14 +28,14 @@ #define APP_NAME "XMRigCC-AMD" #define APP_COPYRIGHT "Copyright (C) 2017- BenDr0id" #define APP_DESC "XMRigCC-AMD OpenCL miner" -#define APP_VERSION "1.8.7 (based on XMRig)" +#define APP_VERSION "1.8.8-beta1 (based on XMRig)" #define APP_SITE "https://github.com/Bendr0id/xmrigCC-amd" #define APP_DOMAIN "" #define APP_KIND "amd" #define APP_VER_MAJOR 1 #define APP_VER_MINOR 8 -#define APP_VER_PATCH 7 +#define APP_VER_PATCH 8 #define TYPE_AMD_GPU From 611870adcc22c8de465242977bab326dfceac0be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Thu, 3 Jan 2019 22:44:13 +0100 Subject: [PATCH 67/97] WIP upx --- src/amd/GpuContext.h | 2 +- src/amd/OclGPU.cpp | 7 +- src/amd/opencl/cryptonight.cl | 110 +++++++++++++++++++++++++++++ src/common/crypto/Algorithm.cpp | 4 +- src/common/net/Pool.cpp | 1 + src/common/xmrig.h | 3 +- src/crypto/CryptoNight.cpp | 17 ++++- src/crypto/CryptoNight_constants.h | 6 ++ src/crypto/CryptoNight_test.h | 5 ++ 9 files changed, 147 insertions(+), 8 deletions(-) diff --git a/src/amd/GpuContext.h b/src/amd/GpuContext.h index 66bfb74e..6f7597fd 100644 --- a/src/amd/GpuContext.h +++ b/src/amd/GpuContext.h @@ -111,7 +111,7 @@ struct GpuContext cl_mem OutputBuffer; cl_mem ExtraBuffers[6]; cl_program Program; - cl_kernel Kernels[14]; + cl_kernel Kernels[15]; size_t freeMem; size_t globalMem; cl_uint computeUnits; diff --git a/src/amd/OclGPU.cpp b/src/amd/OclGPU.cpp index e95f4e42..55afe2ed 100644 --- a/src/amd/OclGPU.cpp +++ b/src/amd/OclGPU.cpp @@ -94,6 +94,9 @@ inline static int cnKernelOffset(xmrig::Variant variant) case xmrig::VARIANT_XTL_V9: return 13; + case xmrig::VARIANT_UPX: + return 14; + default: break; } @@ -209,8 +212,8 @@ size_t InitOpenCLGpu(int index, cl_context opencl_ctx, GpuContext* ctx, const ch return OCL_ERR_API; } - const char *KernelNames[] = { "cn0", "cn1", "cn2", "Blake", "Groestl", "JH", "Skein", "cn1_monero", "cn1_msr", "cn1_xao", "cn1_tube", "cn1_v2_monero", "cn1_xfh", "cn1_fastv2"}; - for (int i = 0; i < 14; ++i) { + const char *KernelNames[] = { "cn0", "cn1", "cn2", "Blake", "Groestl", "JH", "Skein", "cn1_monero", "cn1_msr", "cn1_xao", "cn1_tube", "cn1_v2_monero", "cn1_xfh", "cn1_fastv2", "cn1_upx"}; + for (int i = 0; i < 15; ++i) { ctx->Kernels[i] = OclLib::createKernel(ctx->Program, KernelNames[i], &ret); if (ret != CL_SUCCESS) { return OCL_ERR_API; diff --git a/src/amd/opencl/cryptonight.cl b/src/amd/opencl/cryptonight.cl index 5cbd4cef..b19409d1 100644 --- a/src/amd/opencl/cryptonight.cl +++ b/src/amd/opencl/cryptonight.cl @@ -661,6 +661,116 @@ __kernel void cn1_monero(__global uint4 *Scratchpad, __global ulong *states, uin } +)===" +R"===( + +#define VARIANT1_1(p) \ + uint table = 0x75310U; \ + uint index = (((p).s2 >> 26) & 12) | (((p).s2 >> 23) & 2); \ + (p).s2 ^= ((table >> index) & 0x30U) << 24 + +#define VARIANT1_1_XTL(p) \ + uint table = 0x75310U; \ + uint offset = variant == VARIANT_XTL ? 27 : 26; \ + uint index = (((p).s2 >> offset) & 12) | (((p).s2 >> 23) & 2); \ + (p).s2 ^= ((table >> index) & 0x30U) << 24 + +#define VARIANT1_2(p) ((uint2 *)&(p))[0] ^= tweak1_2_0 + +#define VARIANT1_INIT() \ + tweak1_2 = as_uint2(input[4]); \ + tweak1_2.s0 >>= 24; \ + tweak1_2.s0 |= tweak1_2.s1 << 8; \ + tweak1_2.s1 = (uint) get_global_id(0); \ + tweak1_2 ^= as_uint2(states[24]) + +__attribute__((reqd_work_group_size(WORKSIZE, 1, 1))) +__kernel void cn1_upx(__global uint4 *Scratchpad, __global ulong *states, uint variant, __global ulong *input, uint Threads) +{ + ulong a[2], b[2]; + __local uint AES0[256], AES1[256]; + + const ulong gIdx = getIdx(); + + for (int i = get_local_id(0); i < 256; i += WORKSIZE) { + const uint tmp = AES0_C[i]; + AES0[i] = tmp; + AES1[i] = rotate(tmp, 8U); + } + + barrier(CLK_LOCAL_MEM_FENCE); + + uint2 tweak1_2; + uint4 b_x; +# if (COMP_MODE == 1) + // do not use early return here + if (gIdx < Threads) +# endif + { + states += 25 * gIdx; +# if (STRIDED_INDEX == 0) + Scratchpad += gIdx * (MEMORY >> 4); +# elif (STRIDED_INDEX == 1) +# if (ALGO == CRYPTONIGHT_HEAVY) + Scratchpad += (gIdx / WORKSIZE) * (MEMORY >> 4) * WORKSIZE + (gIdx % WORKSIZE); +# else + Scratchpad += gIdx; +# endif +# elif (STRIDED_INDEX == 2) + Scratchpad += get_group_id(0) * (MEMORY >> 4) * WORKSIZE + MEM_CHUNK * get_local_id(0); +# endif + + a[0] = states[0] ^ states[4]; + b[0] = states[2] ^ states[6]; + a[1] = states[1] ^ states[5]; + b[1] = states[3] ^ states[7]; + + b_x = ((uint4 *)b)[0]; + VARIANT1_INIT(); + } + + mem_fence(CLK_LOCAL_MEM_FENCE); + +# if (COMP_MODE == 1) + // do not use early return here + if (gIdx < Threads) +# endif + { + #pragma unroll UNROLL_FACTOR + for (int i = 0; i < 0x20000; ++i) { + ulong c[2]; + + ((uint4 *)c)[0] = Scratchpad[IDX((as_uint2(a[0]).s0 & MASK) >> 4)]; + ((uint4 *)c)[0] = AES_Round_Two_Tables(AES0, AES1, ((uint4 *)c)[0], ((uint4 *)a)[0]); + + b_x ^= ((uint4 *)c)[0]; + VARIANT1_1_XTL(b_x); + Scratchpad[IDX((as_uint2(a[0]).s0 & MASK) >> 4)] = b_x; + + uint4 tmp; + tmp = Scratchpad[IDX((as_uint2(c[0]).s0 & MASK) >> 4)]; + + a[1] += c[0] * as_ulong2(tmp).s0; + a[0] += mul_hi(c[0], as_ulong2(tmp).s0); + + uint2 tweak1_2_0 = tweak1_2; + if (variant == VARIANT_RTO) { + tweak1_2_0 ^= ((uint2 *)&(a[0]))[0]; + } + + VARIANT1_2(a[1]); + Scratchpad[IDX((as_uint2(c[0]).s0 & MASK) >> 4)] = ((uint4 *)a)[0]; + VARIANT1_2(a[1]); + + ((uint4 *)a)[0] ^= tmp; + + b_x = ((uint4 *)c)[0]; + } + } + mem_fence(CLK_GLOBAL_MEM_FENCE); +} + + )===" R"===( diff --git a/src/common/crypto/Algorithm.cpp b/src/common/crypto/Algorithm.cpp index 54b09676..875d2da3 100644 --- a/src/common/crypto/Algorithm.cpp +++ b/src/common/crypto/Algorithm.cpp @@ -69,6 +69,7 @@ static AlgoData const algorithms[] = { { "cryptonight-light", "cn-light", xmrig::CRYPTONIGHT_LITE, xmrig::VARIANT_AUTO }, { "cryptonight-lite/0", "cn-lite/0", xmrig::CRYPTONIGHT_LITE, xmrig::VARIANT_0 }, { "cryptonight-lite/1", "cn-lite/1", xmrig::CRYPTONIGHT_LITE, xmrig::VARIANT_1 }, + { "cryptonight-lite/upx", "cn-lite/upx", xmrig::CRYPTONIGHT_LITE, xmrig::VARIANT_UPX }, # endif # ifndef XMRIG_NO_SUMO @@ -111,7 +112,8 @@ static const char *variants[] = { "rto", "2", "xfh", - "xtlv9" + "xtlv9", + "upx" }; diff --git a/src/common/net/Pool.cpp b/src/common/net/Pool.cpp index e416b4c4..f8165f19 100644 --- a/src/common/net/Pool.cpp +++ b/src/common/net/Pool.cpp @@ -404,6 +404,7 @@ void Pool::rebuild() addVariant(xmrig::VARIANT_RTO); addVariant(xmrig::VARIANT_XFH); addVariant(xmrig::VARIANT_XTL_V9); + addVariant(xmrig::VARIANT_UPX); addVariant(xmrig::VARIANT_AUTO); # endif } diff --git a/src/common/xmrig.h b/src/common/xmrig.h index 8288b067..a4c676ba 100644 --- a/src/common/xmrig.h +++ b/src/common/xmrig.h @@ -70,7 +70,8 @@ enum Variant { VARIANT_RTO = 7, // Modified CryptoNight variant 1 (Arto only) VARIANT_2 = 8, // CryptoNight variant 2 VARIANT_XFH = 9, // CryptoNight variant xfh/swap aka cn-heavy-superfast - VARIANT_XTL_V9 = 10, // Modified CryptoNight variant 1 (Stellite only) + VARIANT_XTL_V9 = 10, // Modified CryptoNight variant xtl v9 + VARIANT_UPX = 11, // Modified CryptoNight-Lite variant upx VARIANT_MAX }; diff --git a/src/crypto/CryptoNight.cpp b/src/crypto/CryptoNight.cpp index 434be3fe..4ac15039 100644 --- a/src/crypto/CryptoNight.cpp +++ b/src/crypto/CryptoNight.cpp @@ -99,6 +99,8 @@ CryptoNight::cn_hash_fun CryptoNight::fn(xmrig::Algo algorithm, xmrig::AlgoVerif cryptonight_single_hash, cryptonight_single_hash, + nullptr, nullptr, // VARIANT_UPX + # ifndef XMRIG_NO_AEON cryptonight_single_hash, cryptonight_single_hash, @@ -115,12 +117,17 @@ CryptoNight::cn_hash_fun CryptoNight::fn(xmrig::Algo algorithm, xmrig::AlgoVerif nullptr, nullptr, // VARIANT_2 nullptr, nullptr, // VARIANT_XFH nullptr, nullptr, // VARIANT_XTL_V9 + + cryptonight_single_hash, + cryptonight_single_hash, + # else nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr + nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, # endif # ifndef XMRIG_NO_SUMO @@ -143,12 +150,15 @@ CryptoNight::cn_hash_fun CryptoNight::fn(xmrig::Algo algorithm, xmrig::AlgoVerif nullptr, nullptr, // VARIANT_2 nullptr, nullptr, // VARIANT_XFH nullptr, nullptr, // VARIANT_XTL_V9 + nullptr, nullptr, // VARIANT_UPX + # else nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr + nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, # endif }; @@ -203,7 +213,8 @@ bool CryptoNight::selfTest() { # ifndef XMRIG_NO_AEON if (m_algorithm == xmrig::CRYPTONIGHT_LITE) { return verify(VARIANT_0, test_output_v0_lite) && - verify(VARIANT_1, test_output_v1_lite); + verify(VARIANT_1, test_output_v1_lite) && + verify(VARIANT_UPX, test_output_upx_lite); } # endif diff --git a/src/crypto/CryptoNight_constants.h b/src/crypto/CryptoNight_constants.h index 7f2b8a52..fd3a8ee4 100644 --- a/src/crypto/CryptoNight_constants.h +++ b/src/crypto/CryptoNight_constants.h @@ -46,6 +46,7 @@ constexpr const uint32_t CRYPTONIGHT_XFH_ITER = 0x20000; constexpr const size_t CRYPTONIGHT_LITE_MEMORY = 1 * 1024 * 1024; constexpr const uint32_t CRYPTONIGHT_LITE_MASK = 0xFFFF0; constexpr const uint32_t CRYPTONIGHT_LITE_ITER = 0x40000; +constexpr const uint32_t CRYPTONIGHT_LITE_UPX_ITER = 0x20000; constexpr const size_t CRYPTONIGHT_HEAVY_MEMORY = 4 * 1024 * 1024; constexpr const uint32_t CRYPTONIGHT_HEAVY_MASK = 0x3FFFF0; @@ -118,6 +119,7 @@ template<> inline constexpr uint32_t cn_select_iter() template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_XTL_V9_ITER; } template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_LITE_ITER; } template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_LITE_ITER; } +template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_LITE_UPX_ITER; } template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_HEAVY_ITER; } template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_HEAVY_ITER; } template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_HEAVY_ITER; } @@ -138,6 +140,9 @@ inline uint32_t cn_select_iter(Algo algorithm, Variant variant) case VARIANT_XTL_V9: return CRYPTONIGHT_XTL_V9_ITER; + case VARIANT_UPX: + return CRYPTONIGHT_LITE_UPX_ITER; + default: break; } @@ -173,6 +178,7 @@ template<> inline constexpr Variant cn_base_variant() { return VA template<> inline constexpr Variant cn_base_variant() { return VARIANT_0; } template<> inline constexpr Variant cn_base_variant(){ return VARIANT_2; } template<> inline constexpr Variant cn_base_variant() { return VARIANT_2; } +template<> inline constexpr Variant cn_base_variant() { return VARIANT_1; } } /* namespace xmrig */ diff --git a/src/crypto/CryptoNight_test.h b/src/crypto/CryptoNight_test.h index 14945d43..b7481205 100644 --- a/src/crypto/CryptoNight_test.h +++ b/src/crypto/CryptoNight_test.h @@ -198,6 +198,11 @@ const static uint8_t test_output_v1_lite[160] = { 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 }; + +const static uint8_t test_output_upx_lite[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 +}; #endif From d8e3e2141819d11c72db83d0fa4cff668c7285d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Fri, 4 Jan 2019 20:48:04 +0100 Subject: [PATCH 68/97] --- CHANGELOG.md | 3 +++ src/version.h | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a27af7b8..f25d7004 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# 1.8.8 +- Added XLT v5/9 with autodetect(algo: "cryptonight", variant: "xtl" (autodetect), "xtlv9" (force v9)) +- Added cn-lite variant UPX/uPlexa (algo: "cryptonight-lite", variant "upx") # 1.8.7 - Implemented Template based mass config editor to simple swap configs on your rigs # 1.8.6 diff --git a/src/version.h b/src/version.h index 1f7cafbe..00c7b1bd 100644 --- a/src/version.h +++ b/src/version.h @@ -28,7 +28,7 @@ #define APP_NAME "XMRigCC-AMD" #define APP_COPYRIGHT "Copyright (C) 2017- BenDr0id" #define APP_DESC "XMRigCC-AMD OpenCL miner" -#define APP_VERSION "1.8.8-beta1 (based on XMRig)" +#define APP_VERSION "1.8.8 (based on XMRig)" #define APP_SITE "https://github.com/Bendr0id/xmrigCC-amd" #define APP_DOMAIN "" #define APP_KIND "amd" From e49bd14f4574c7ce49ba8c896a3b58b1260810f1 Mon Sep 17 00:00:00 2001 From: BenDr0id Date: Tue, 15 Jan 2019 13:04:03 +0100 Subject: [PATCH 69/97] Improved auto detection for XTLv9/cn-half --- src/common/crypto/Algorithm.cpp | 5 +++++ src/version.h | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/common/crypto/Algorithm.cpp b/src/common/crypto/Algorithm.cpp index 875d2da3..cfa42198 100644 --- a/src/common/crypto/Algorithm.cpp +++ b/src/common/crypto/Algorithm.cpp @@ -63,6 +63,7 @@ static AlgoData const algorithms[] = { { "cryptonight/2", "cn/2", xmrig::CRYPTONIGHT, xmrig::VARIANT_2 }, { "cryptonight/xfh", "cn/xfh", xmrig::CRYPTONIGHT, xmrig::VARIANT_XFH }, { "cryptonight/xtlv9", "cn/xtlv9", xmrig::CRYPTONIGHT, xmrig::VARIANT_XTL_V9 }, + { "cryptonight/half", "cn/half", xmrig::CRYPTONIGHT, xmrig::VARIANT_XTL_V9 }, # ifndef XMRIG_NO_AEON { "cryptonight-lite", "cn-lite", xmrig::CRYPTONIGHT_LITE, xmrig::VARIANT_AUTO }, @@ -171,6 +172,10 @@ void xmrig::Algorithm::parseVariant(const char *variant) { m_variant = VARIANT_AUTO; + if (strcasecmp(variant, "half") == 0) { + variant = "xtlv9"; + } + for (size_t i = 0; i < ARRAY_SIZE(variants); i++) { if (strcasecmp(variant, variants[i]) == 0) { m_variant = static_cast(i); diff --git a/src/version.h b/src/version.h index 00c7b1bd..703911a9 100644 --- a/src/version.h +++ b/src/version.h @@ -28,14 +28,14 @@ #define APP_NAME "XMRigCC-AMD" #define APP_COPYRIGHT "Copyright (C) 2017- BenDr0id" #define APP_DESC "XMRigCC-AMD OpenCL miner" -#define APP_VERSION "1.8.8 (based on XMRig)" +#define APP_VERSION "1.8.9 (based on XMRig)" #define APP_SITE "https://github.com/Bendr0id/xmrigCC-amd" #define APP_DOMAIN "" #define APP_KIND "amd" #define APP_VER_MAJOR 1 #define APP_VER_MINOR 8 -#define APP_VER_PATCH 8 +#define APP_VER_PATCH 9 #define TYPE_AMD_GPU From 9ad43480d17323d8fa6853d0c70f7ca7c90f3403 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Tue, 15 Jan 2019 13:32:03 +0100 Subject: [PATCH 70/97] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f25d7004..f84140ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +# 1.8.9 +- Improved algo parsing for XTL v9 aka cn-half # 1.8.8 - Added XLT v5/9 with autodetect(algo: "cryptonight", variant: "xtl" (autodetect), "xtlv9" (force v9)) - Added cn-lite variant UPX/uPlexa (algo: "cryptonight-lite", variant "upx") From 9e76e68cb8af8aa6223c09eb79f7f6b44e52a4b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Thu, 24 Jan 2019 20:19:03 +0100 Subject: [PATCH 71/97] Integrated cn-ultralite/turtle - fixed merge conflicts - cleanup --- CMakeLists.txt | 2 +- src/amd/OclCLI.cpp | 8 ++-- src/amd/OclCLI.h | 2 +- src/amd/OclGPU.cpp | 4 +- src/amd/opencl/cryptonight.cl | 7 +-- src/common/crypto/Algorithm.cpp | 44 ++++++++++++------ src/common/net/Job.cpp | 4 +- src/common/net/Pool.cpp | 17 ++----- src/common/xmrig.h | 11 ++--- src/core/Config.cpp | 4 +- src/crypto/CryptoNight.cpp | 34 +++++++++----- src/crypto/CryptoNight_constants.h | 64 ++++++++++++--------------- src/crypto/CryptoNight_test.h | 19 +++----- src/crypto/CryptoNight_x86.h | 8 ++-- src/net/strategies/DonateStrategy.cpp | 4 ++ src/version.h | 4 +- 16 files changed, 122 insertions(+), 114 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7cb90e42..6e596f2d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -247,7 +247,7 @@ if (NOT WITH_SUMO) endif() if (NOT WITH_CN_PICO) - add_definitions(/DXMRIG_NO_CN_PICO) + add_definitions(/DXMRIG_NO_CN_ULTRALITE) endif() if (WITH_HTTPD) diff --git a/src/amd/OclCLI.cpp b/src/amd/OclCLI.cpp index 9000a261..74db1543 100644 --- a/src/amd/OclCLI.cpp +++ b/src/amd/OclCLI.cpp @@ -188,8 +188,8 @@ int OclCLI::getHints(const GpuContext &ctx, xmrig::Config *config) const hints |= CNv2; } - if (config->algorithm().algo() == xmrig::CRYPTONIGHT_PICO) { - hints |= Pico; + if (config->algorithm().algo() == xmrig::CRYPTONIGHT_ULTRALITE) { + hints |= Ultralite; } if (ctx.vendor == xmrig::OCL_VENDOR_AMD && (ctx.name == "gfx901" || @@ -248,7 +248,7 @@ void OclCLI::parse(std::vector &vector, const char *arg) const size_t OclCLI::getMaxThreads(const GpuContext &ctx, xmrig::Algo algo, int hints) { - const size_t ratio = (algo == xmrig::CRYPTONIGHT_LITE || algo == xmrig::CRYPTONIGHT_PICO) ? 2u : 1u; + const size_t ratio = (algo == xmrig::CRYPTONIGHT_LITE || algo == xmrig::CRYPTONIGHT_ULTRALITE) ? 2u : 1u; if (ctx.vendor == xmrig::OCL_VENDOR_INTEL) { return ratio * ctx.computeUnits * 8; } @@ -287,7 +287,7 @@ size_t OclCLI::getPossibleIntensity(const GpuContext &ctx, size_t maxThreads, si size_t OclCLI::worksizeByHints(int hints) { if (hints & Vega) { - if (hints & Pico) { + if (hints & Ultralite) { return 64; } diff --git a/src/amd/OclCLI.h b/src/amd/OclCLI.h index 6e28ce75..3f807073 100644 --- a/src/amd/OclCLI.h +++ b/src/amd/OclCLI.h @@ -64,7 +64,7 @@ class OclCLI DoubleThreads = 1, Vega = 2, CNv2 = 4, - Pico = 8 + Ultralite = 8 }; inline bool isEmpty() const { return m_devices.empty() && m_intensity.empty(); } diff --git a/src/amd/OclGPU.cpp b/src/amd/OclGPU.cpp index 80be3726..0403c3ab 100644 --- a/src/amd/OclGPU.cpp +++ b/src/amd/OclGPU.cpp @@ -87,13 +87,13 @@ inline static int cnKernelOffset(xmrig::Variant variant) return 10; case xmrig::VARIANT_2: - case xmrig::VARIANT_TRTL: + case xmrig::VARIANT_TURTLE: return 11; case xmrig::VARIANT_XFH: return 12; - case xmrig::VARIANT_XTL_V9: + case xmrig::VARIANT_FAST_2: return 13; case xmrig::VARIANT_UPX: diff --git a/src/amd/opencl/cryptonight.cl b/src/amd/opencl/cryptonight.cl index 0e8ac572..6226731e 100644 --- a/src/amd/opencl/cryptonight.cl +++ b/src/amd/opencl/cryptonight.cl @@ -104,12 +104,13 @@ XMRIG_INCLUDE_FAST_DIV_HEAVY #define VARIANT_RTO 7 // Modified CryptoNight variant 1 (Arto only) #define VARIANT_2 8 // CryptoNight variant 2 #define VARIANT_XFH 9 // CryptoNight variant xfh aka cn-heavy-superfast -#define VARIANT_XTL_V9 10 // CryptoNight variant xfh aka cn-heavy-superfast +#define VARIANT_FAST_2 10 // CryptoNight variant 2 with half iterations (Masari/Stellite) +#define VARIANT_TURTLE 11 // CryptoNight Turtle (TRTL) #define CRYPTONIGHT 0 /* CryptoNight (2 MB) */ #define CRYPTONIGHT_LITE 1 /* CryptoNight (1 MB) */ #define CRYPTONIGHT_HEAVY 2 /* CryptoNight (4 MB) */ -#define CRYPTONIGHT_PICO 3 /* CryptoNight (256 KB) */ +#define CRYPTONIGHT_ULTRALITE 3 /* CryptoNight (256 KB) */ #if defined(__NV_CL_C_VERSION) && STRIDED_INDEX != 0 # undef STRIDED_INDEX @@ -783,7 +784,7 @@ R"===( __attribute__((reqd_work_group_size(WORKSIZE, 1, 1))) __kernel void cn1_v2_monero(__global uint4 *Scratchpad, __global ulong *states, uint variant, __global ulong *input, uint Threads) { -# if (ALGO == CRYPTONIGHT || ALGO == CRYPTONIGHT_PICO) +# if (ALGO == CRYPTONIGHT || ALGO == CRYPTONIGHT_ULTRALITE) ulong a[2], b[4]; __local uint AES0[256], AES1[256], AES2[256], AES3[256]; diff --git a/src/common/crypto/Algorithm.cpp b/src/common/crypto/Algorithm.cpp index 20c8259a..ade08240 100644 --- a/src/common/crypto/Algorithm.cpp +++ b/src/common/crypto/Algorithm.cpp @@ -62,8 +62,10 @@ static AlgoData const algorithms[] = { { "cryptonight/rto", "cn/rto", xmrig::CRYPTONIGHT, xmrig::VARIANT_RTO }, { "cryptonight/2", "cn/2", xmrig::CRYPTONIGHT, xmrig::VARIANT_2 }, { "cryptonight/xfh", "cn/xfh", xmrig::CRYPTONIGHT, xmrig::VARIANT_XFH }, - { "cryptonight/xtlv9", "cn/xtlv9", xmrig::CRYPTONIGHT, xmrig::VARIANT_XTL_V9 }, - { "cryptonight/half", "cn/half", xmrig::CRYPTONIGHT, xmrig::VARIANT_XTL_V9 }, + { "cryptonight/swap", "cn/swap", xmrig::CRYPTONIGHT, xmrig::VARIANT_XFH }, + { "cryptonight/fast2", "cn/fast2", xmrig::CRYPTONIGHT, xmrig::VARIANT_FAST_2 }, + { "cryptonight/xtlv9", "cn/xtlv9", xmrig::CRYPTONIGHT, xmrig::VARIANT_FAST_2 }, + { "cryptonight/half", "cn/half", xmrig::CRYPTONIGHT, xmrig::VARIANT_FAST_2 }, # ifndef XMRIG_NO_AEON { "cryptonight-lite", "cn-lite", xmrig::CRYPTONIGHT_LITE, xmrig::VARIANT_AUTO }, @@ -80,12 +82,14 @@ static AlgoData const algorithms[] = { { "cryptonight-heavy/tube", "cn-heavy/tube", xmrig::CRYPTONIGHT_HEAVY, xmrig::VARIANT_TUBE }, # endif -# ifndef XMRIG_NO_CN_PICO - { "cryptonight-pico/trtl", "cn-pico/trtl", xmrig::CRYPTONIGHT_PICO, xmrig::VARIANT_TRTL }, - { "cryptonight-pico", "cn-pico", xmrig::CRYPTONIGHT_PICO, xmrig::VARIANT_TRTL }, - { "cryptonight-turtle", "cn-trtl", xmrig::CRYPTONIGHT_PICO, xmrig::VARIANT_TRTL }, - { "cryptonight-ultralite", "cn-ultralite", xmrig::CRYPTONIGHT_PICO, xmrig::VARIANT_TRTL }, - { "cryptonight_turtle", "cn_turtle", xmrig::CRYPTONIGHT_PICO, xmrig::VARIANT_TRTL }, +# ifndef XMRIG_NO_CN_ULTRALITE + { "cryptonight-ultralite/turtle", "cn-ultralite/turtle", xmrig::CRYPTONIGHT_ULTRALITE, xmrig::VARIANT_TURTLE }, + { "cryptonight-ultralite", "cn-ultralite", xmrig::CRYPTONIGHT_ULTRALITE, xmrig::VARIANT_TURTLE }, + { "cryptonight-ultralite/2", "cn-ultralite/2", xmrig::CRYPTONIGHT_ULTRALITE, xmrig::VARIANT_TURTLE }, + { "cryptonight-pico/trtl", "cn-pico/trtl", xmrig::CRYPTONIGHT_ULTRALITE, xmrig::VARIANT_TURTLE }, + { "cryptonight-pico", "cn-pico", xmrig::CRYPTONIGHT_ULTRALITE, xmrig::VARIANT_TURTLE }, + { "cryptonight-turtle", "cn-trtl", xmrig::CRYPTONIGHT_ULTRALITE, xmrig::VARIANT_TURTLE }, + { "cryptonight_turtle", "cn_turtle", xmrig::CRYPTONIGHT_ULTRALITE, xmrig::VARIANT_TURTLE }, # endif }; @@ -121,8 +125,9 @@ static const char *variants[] = { "rto", "2", "xfh", - "xtlv9", - "upx" + "fast2", + "upx", + "turtle" }; @@ -199,6 +204,11 @@ void xmrig::Algorithm::parseVariant(const char *variant) return parseVariant(variant + 1); } + if (m_algo == xmrig::CRYPTONIGHT_ULTRALITE) { + m_variant = VARIANT_TURTLE; + return; + } + for (size_t i = 0; i < ARRAY_SIZE(variants); i++) { if (strcasecmp(variant, variants[i]) == 0) { m_variant = static_cast(i); @@ -207,7 +217,15 @@ void xmrig::Algorithm::parseVariant(const char *variant) } if (strcasecmp(variant, "xtlv9") == 0) { - m_variant = VARIANT_HALF; + m_variant = VARIANT_FAST_2; + } + + if (strcasecmp(variant, "half") == 0) { + m_variant = VARIANT_FAST_2; + } + + if (strcasecmp(variant, "msr2") == 0) { + m_variant = VARIANT_FAST_2; } } @@ -237,8 +255,8 @@ void xmrig::Algorithm::setAlgo(Algo algo) { m_algo = algo; - if (m_algo == CRYPTONIGHT_PICO && m_variant == VARIANT_AUTO) { - m_variant = xmrig::VARIANT_TRTL; + if (m_algo == CRYPTONIGHT_ULTRALITE && m_variant == VARIANT_AUTO) { + m_variant = xmrig::VARIANT_TURTLE; } } diff --git a/src/common/net/Job.cpp b/src/common/net/Job.cpp index 80cab893..611e85ff 100644 --- a/src/common/net/Job.cpp +++ b/src/common/net/Job.cpp @@ -129,10 +129,10 @@ bool Job::setBlob(const char *blob) if (!m_algorithm.isForced()) { if (m_algorithm.variant() == xmrig::VARIANT_XTL && m_blob[0] >= 9) { - m_algorithm.setVariant(xmrig::VARIANT_HALF); + m_algorithm.setVariant(xmrig::VARIANT_FAST_2); } else if (m_algorithm.variant() == xmrig::VARIANT_MSR && m_blob[0] >= 8) { - m_algorithm.setVariant(xmrig::VARIANT_HALF); + m_algorithm.setVariant(xmrig::VARIANT_FAST_2); } } diff --git a/src/common/net/Pool.cpp b/src/common/net/Pool.cpp index c2fe4269..5eb10268 100644 --- a/src/common/net/Pool.cpp +++ b/src/common/net/Pool.cpp @@ -219,18 +219,7 @@ rapidjson::Value Pool::toJSON(rapidjson::Document &doc) const obj.AddMember("keepalive", m_keepAlive, allocator); } - switch (m_algorithm.variant()) { - case xmrig::VARIANT_AUTO: - case xmrig::VARIANT_0: - case xmrig::VARIANT_1: - case xmrig::VARIANT_2: - obj.AddMember("variant", m_algorithm.variant(), allocator); - break; - - default: - obj.AddMember("variant", StringRef(m_algorithm.variantName()), allocator); - break; - } + obj.AddMember("variant", StringRef(m_algorithm.variantName()), allocator); obj.AddMember("tls", isTLS(), allocator); obj.AddMember("tls-fingerprint", fingerprint() ? Value(StringRef(fingerprint())).Move() : Value(kNullType).Move(), allocator); @@ -412,7 +401,6 @@ void Pool::rebuild() addVariant(xmrig::VARIANT_2); addVariant(xmrig::VARIANT_1); addVariant(xmrig::VARIANT_0); - addVariant(xmrig::VARIANT_HALF); addVariant(xmrig::VARIANT_XTL); addVariant(xmrig::VARIANT_TUBE); addVariant(xmrig::VARIANT_MSR); @@ -420,8 +408,9 @@ void Pool::rebuild() addVariant(xmrig::VARIANT_XAO); addVariant(xmrig::VARIANT_RTO); addVariant(xmrig::VARIANT_XFH); - addVariant(xmrig::VARIANT_XTL_V9); + addVariant(xmrig::VARIANT_FAST_2); addVariant(xmrig::VARIANT_UPX); + addVariant(xmrig::VARIANT_TURTLE); addVariant(xmrig::VARIANT_AUTO); # endif } diff --git a/src/common/xmrig.h b/src/common/xmrig.h index 5407fc22..24eedc2a 100644 --- a/src/common/xmrig.h +++ b/src/common/xmrig.h @@ -32,10 +32,10 @@ namespace xmrig enum Algo { INVALID_ALGO = -1, - CRYPTONIGHT, /* CryptoNight (2 MB) */ - CRYPTONIGHT_LITE, /* CryptoNight (1 MB) */ - CRYPTONIGHT_HEAVY, /* CryptoNight (4 MB) */ - CRYPTONIGHT_PICO, /* CryptoNight (256 KB) */ + CRYPTONIGHT, /* CryptoNight (2 MB) */ + CRYPTONIGHT_LITE, /* CryptoNight (1 MB) */ + CRYPTONIGHT_HEAVY, /* CryptoNight (4 MB) */ + CRYPTONIGHT_ULTRALITE, /* CryptoNight (256 KB) */ ALGO_MAX }; @@ -72,8 +72,9 @@ enum Variant { VARIANT_RTO = 7, // Modified CryptoNight variant 1 (Arto only) VARIANT_2 = 8, // CryptoNight variant 2 VARIANT_XFH = 9, // CryptoNight variant xfh/swap aka cn-heavy-superfast - VARIANT_XTL_V9 = 10, // Modified CryptoNight variant xtl v9 + VARIANT_FAST_2 = 10, // Modified CryptoNight variant used by xtl v9 / msr v8 VARIANT_UPX = 11, // Modified CryptoNight-Lite variant upx + VARIANT_TURTLE = 12, // Modified CryptoNight-Ultralite variant turtle VARIANT_MAX }; diff --git a/src/core/Config.cpp b/src/core/Config.cpp index 8a8a3cbd..7b70e1be 100644 --- a/src/core/Config.cpp +++ b/src/core/Config.cpp @@ -71,7 +71,7 @@ xmrig::Config::Config() : xmrig::CommonConfig(), bool xmrig::Config::isCNv2() const { - if (algorithm().algo() == CRYPTONIGHT_PICO) { + if (algorithm().algo() == CRYPTONIGHT_ULTRALITE) { return true; } @@ -82,7 +82,7 @@ bool xmrig::Config::isCNv2() const for (const Pool &pool : pools()) { const Variant variant = pool.algorithm().variant(); - if (variant == VARIANT_2 || variant == VARIANT_AUTO || variant == VARIANT_HALF) { + if (variant == VARIANT_2 || variant == VARIANT_AUTO || variant == VARIANT_FAST_2) { return true; } } diff --git a/src/crypto/CryptoNight.cpp b/src/crypto/CryptoNight.cpp index 7a8c582c..b18948b9 100644 --- a/src/crypto/CryptoNight.cpp +++ b/src/crypto/CryptoNight.cpp @@ -96,10 +96,11 @@ CryptoNight::cn_hash_fun CryptoNight::fn(xmrig::Algo algorithm, xmrig::AlgoVerif cryptonight_single_hash, cryptonight_single_hash, - cryptonight_single_hash, - cryptonight_single_hash, + cryptonight_single_hash, + cryptonight_single_hash, nullptr, nullptr, // VARIANT_UPX + nullptr, nullptr, // VARIANT_TURTLE # ifndef XMRIG_NO_AEON cryptonight_single_hash, @@ -116,11 +117,13 @@ CryptoNight::cn_hash_fun CryptoNight::fn(xmrig::Algo algorithm, xmrig::AlgoVerif nullptr, nullptr, // VARIANT_RTO nullptr, nullptr, // VARIANT_2 nullptr, nullptr, // VARIANT_XFH - nullptr, nullptr, // VARIANT_XTL_V9 + nullptr, nullptr, // VARIANT_FAST_2 cryptonight_single_hash, cryptonight_single_hash, + nullptr, nullptr, // VARIANT_TURTLE + # else nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, @@ -128,6 +131,7 @@ CryptoNight::cn_hash_fun CryptoNight::fn(xmrig::Algo algorithm, xmrig::AlgoVerif nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, # endif # ifndef XMRIG_NO_SUMO @@ -149,8 +153,9 @@ CryptoNight::cn_hash_fun CryptoNight::fn(xmrig::Algo algorithm, xmrig::AlgoVerif nullptr, nullptr, // VARIANT_RTO nullptr, nullptr, // VARIANT_2 nullptr, nullptr, // VARIANT_XFH - nullptr, nullptr, // VARIANT_XTL_V9 + nullptr, nullptr, // VARIANT_FAST_2 nullptr, nullptr, // VARIANT_UPX + nullptr, nullptr, // VARIANT_TURTLE # else nullptr, nullptr, nullptr, nullptr, @@ -159,8 +164,10 @@ CryptoNight::cn_hash_fun CryptoNight::fn(xmrig::Algo algorithm, xmrig::AlgoVerif nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, # endif -# ifndef XMRIG_NO_CN_PICO + +# ifndef XMRIG_NO_CN_ULTRALITE nullptr, nullptr, // VARIANT_0 nullptr, nullptr, // VARIANT_1 nullptr, nullptr, // VARIANT_TUBE @@ -170,16 +177,19 @@ CryptoNight::cn_hash_fun CryptoNight::fn(xmrig::Algo algorithm, xmrig::AlgoVerif nullptr, nullptr, // VARIANT_XAO nullptr, nullptr, // VARIANT_RTO nullptr, nullptr, // VARIANT_2 - nullptr, nullptr, // VARIANT_HALF + nullptr, nullptr, // VARIANT_XFH + nullptr, nullptr, // VARIANT_FAST_2 + nullptr, nullptr, // VARIANT_UPX - cryptonight_single_hash, - cryptonight_single_hash, + cryptonight_single_hash, + cryptonight_single_hash, #else nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, # endif }; @@ -231,7 +241,7 @@ bool CryptoNight::selfTest() { verify(VARIANT_XAO, test_output_xao) && verify(VARIANT_RTO, test_output_rto) && verify(VARIANT_XFH, test_output_xfh) && - verify(VARIANT_XTL_V9, test_output_xtl_v9); + verify(VARIANT_FAST_2, test_output_xtl_v9); } # ifndef XMRIG_NO_AEON @@ -250,9 +260,9 @@ bool CryptoNight::selfTest() { } # endif -# ifndef XMRIG_NO_CN_PICO - if (m_algorithm == xmrig::CRYPTONIGHT_PICO) { - return verify(VARIANT_TRTL, test_output_pico_trtl); +# ifndef XMRIG_NO_CN_ULTRALITE + if (m_algorithm == xmrig::CRYPTONIGHT_ULTRALITE) { + return verify(VARIANT_TURTLE, test_output_turtle); } # endif diff --git a/src/crypto/CryptoNight_constants.h b/src/crypto/CryptoNight_constants.h index 4a0430ae..f49aec41 100644 --- a/src/crypto/CryptoNight_constants.h +++ b/src/crypto/CryptoNight_constants.h @@ -36,34 +36,30 @@ namespace xmrig { +constexpr const uint32_t CRYPTONIGHT_ITER = 0x80000; +constexpr const uint32_t CRYPTONIGHT_HALF_ITER = 0x40000; +constexpr const uint32_t CRYPTONIGHT_XAO_ITER = 0x100000; +constexpr const uint32_t CRYPTONIGHT_XFH_ITER = 0x20000; +constexpr const uint32_t CRYPTONIGHT_LITE_UPX_ITER = 0x20000; +constexpr const uint32_t CRYPTONIGHT_TRTL_ITER = 0x10000; + constexpr const size_t CRYPTONIGHT_MEMORY = 2 * 1024 * 1024; constexpr const uint32_t CRYPTONIGHT_MASK = 0x1FFFF0; -constexpr const uint32_t CRYPTONIGHT_ITER = 0x80000; -constexpr const uint32_t CRYPTONIGHT_MSR_ITER = 0x40000; -constexpr const uint32_t CRYPTONIGHT_XTL_V9_ITER = 0x40000; -constexpr const uint32_t CRYPTONIGHT_XAO_ITER = 0x100000; -constexpr const uint32_t CRYPTONIGHT_XFH_ITER = 0x20000; constexpr const size_t CRYPTONIGHT_LITE_MEMORY = 1 * 1024 * 1024; constexpr const uint32_t CRYPTONIGHT_LITE_MASK = 0xFFFF0; -constexpr const uint32_t CRYPTONIGHT_LITE_ITER = 0x40000; -constexpr const uint32_t CRYPTONIGHT_LITE_UPX_ITER = 0x20000; constexpr const size_t CRYPTONIGHT_HEAVY_MEMORY = 4 * 1024 * 1024; constexpr const uint32_t CRYPTONIGHT_HEAVY_MASK = 0x3FFFF0; -constexpr const uint32_t CRYPTONIGHT_HEAVY_ITER = 0x40000; - -constexpr const size_t CRYPTONIGHT_PICO_MEMORY = 256 * 1024; -constexpr const uint32_t CRYPTONIGHT_PICO_MASK = 0x1FFF0; -constexpr const uint32_t CRYPTONIGHT_PICO_ITER = 0x40000; -constexpr const uint32_t CRYPTONIGHT_TRTL_ITER = 0x10000; +constexpr const size_t CRYPTONIGHT_ULTRALITE_MEMORY = 256 * 1024; +constexpr const uint32_t CRYPTONIGHT_ULTRALITE_MASK = 0x1FFF0; template inline constexpr size_t cn_select_memory() { return 0; } template<> inline constexpr size_t cn_select_memory() { return CRYPTONIGHT_MEMORY; } template<> inline constexpr size_t cn_select_memory() { return CRYPTONIGHT_LITE_MEMORY; } template<> inline constexpr size_t cn_select_memory() { return CRYPTONIGHT_HEAVY_MEMORY; } -template<> inline constexpr size_t cn_select_memory() { return CRYPTONIGHT_PICO_MEMORY; } +template<> inline constexpr size_t cn_select_memory() { return CRYPTONIGHT_ULTRALITE_MEMORY; } inline size_t cn_select_memory(Algo algorithm) @@ -79,8 +75,8 @@ inline size_t cn_select_memory(Algo algorithm) case CRYPTONIGHT_HEAVY: return CRYPTONIGHT_HEAVY_MEMORY; - case CRYPTONIGHT_PICO: - return CRYPTONIGHT_PICO_MEMORY; + case CRYPTONIGHT_ULTRALITE: + return CRYPTONIGHT_ULTRALITE_MEMORY; default: break; @@ -94,7 +90,7 @@ template inline constexpr uint32_t cn_select_mask() { retur template<> inline constexpr uint32_t cn_select_mask() { return CRYPTONIGHT_MASK; } template<> inline constexpr uint32_t cn_select_mask() { return CRYPTONIGHT_LITE_MASK; } template<> inline constexpr uint32_t cn_select_mask() { return CRYPTONIGHT_HEAVY_MASK; } -template<> inline constexpr uint32_t cn_select_mask() { return CRYPTONIGHT_PICO_MASK; } +template<> inline constexpr uint32_t cn_select_mask() { return CRYPTONIGHT_ULTRALITE_MASK; } inline uint32_t cn_select_mask(Algo algorithm) @@ -110,8 +106,8 @@ inline uint32_t cn_select_mask(Algo algorithm) case CRYPTONIGHT_HEAVY: return CRYPTONIGHT_HEAVY_MASK; - case CRYPTONIGHT_PICO: - return CRYPTONIGHT_PICO_MASK; + case CRYPTONIGHT_ULTRALITE: + return CRYPTONIGHT_ULTRALITE_MASK; default: break; @@ -126,26 +122,25 @@ template<> inline constexpr uint32_t cn_select_iter() template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_ITER; } template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_ITER; } template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_ITER; } -template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_HALF_ITER; } template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_HALF_ITER; } template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_XAO_ITER; } template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_ITER; } template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_XFH_ITER; } -template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_XTL_V9_ITER; } -template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_LITE_ITER; } -template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_LITE_ITER; } +template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_HALF_ITER; } +template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_HALF_ITER; } +template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_HALF_ITER; } template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_LITE_UPX_ITER; } -template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_HEAVY_ITER; } -template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_HEAVY_ITER; } -template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_HEAVY_ITER; } -template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_TRTL_ITER; } +template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_HALF_ITER; } +template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_HALF_ITER; } +template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_HALF_ITER; } +template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_TRTL_ITER; } inline uint32_t cn_select_iter(Algo algorithm, Variant variant) { switch (variant) { case VARIANT_MSR: - case VARIANT_HALF: + case VARIANT_FAST_2: return CRYPTONIGHT_HALF_ITER; case VARIANT_RTO: @@ -154,9 +149,6 @@ inline uint32_t cn_select_iter(Algo algorithm, Variant variant) case VARIANT_XFH: return CRYPTONIGHT_XFH_ITER; - case VARIANT_XTL_V9: - return CRYPTONIGHT_XTL_V9_ITER; - case VARIANT_UPX: return CRYPTONIGHT_LITE_UPX_ITER; @@ -170,12 +162,12 @@ inline uint32_t cn_select_iter(Algo algorithm, Variant variant) return CRYPTONIGHT_ITER; case CRYPTONIGHT_LITE: - return CRYPTONIGHT_LITE_ITER; + return CRYPTONIGHT_HALF_ITER; case CRYPTONIGHT_HEAVY: - return CRYPTONIGHT_HEAVY_ITER; + return CRYPTONIGHT_HALF_ITER; - case CRYPTONIGHT_PICO: + case CRYPTONIGHT_ULTRALITE: return CRYPTONIGHT_TRTL_ITER; default: @@ -196,10 +188,10 @@ template<> inline constexpr Variant cn_base_variant() { return VA template<> inline constexpr Variant cn_base_variant() { return VARIANT_0; } template<> inline constexpr Variant cn_base_variant() { return VARIANT_1; } template<> inline constexpr Variant cn_base_variant() { return VARIANT_0; } -template<> inline constexpr Variant cn_base_variant(){ return VARIANT_2; } +template<> inline constexpr Variant cn_base_variant(){ return VARIANT_2; } template<> inline constexpr Variant cn_base_variant() { return VARIANT_2; } template<> inline constexpr Variant cn_base_variant() { return VARIANT_1; } - +template<> inline constexpr Variant cn_base_variant(){ return VARIANT_2; } } /* namespace xmrig */ diff --git a/src/crypto/CryptoNight_test.h b/src/crypto/CryptoNight_test.h index c1151cff..1f130cfe 100644 --- a/src/crypto/CryptoNight_test.h +++ b/src/crypto/CryptoNight_test.h @@ -271,20 +271,13 @@ const static uint8_t test_output_tube_heavy[160] = { #endif -#ifndef XMRIG_NO_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 +#ifndef XMRIG_NO_CN_ULTRALITE +// "cn-ultralite/turtle" +const static uint8_t test_output_turtle[32] = { + 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 }; + #endif diff --git a/src/crypto/CryptoNight_x86.h b/src/crypto/CryptoNight_x86.h index 3fe75aaf..3b485562 100644 --- a/src/crypto/CryptoNight_x86.h +++ b/src/crypto/CryptoNight_x86.h @@ -602,7 +602,7 @@ inline void cryptonight_single_hash_asm(const uint8_t *__restrict__ input, size_ cnv2_mainloop_bulldozer_asm(ctx[0]); } } - else if (VARIANT == xmrig::VARIANT_HALF) { + else if (VARIANT == xmrig::VARIANT_FAST_2) { if (ASM == xmrig::ASM_INTEL) { cn_half_mainloop_ivybridge_asm(ctx[0]); } @@ -613,7 +613,7 @@ inline void cryptonight_single_hash_asm(const uint8_t *__restrict__ input, size_ cn_half_mainloop_bulldozer_asm(ctx[0]); } } - else if (VARIANT == xmrig::VARIANT_TRTL) { + else if (VARIANT == xmrig::VARIANT_TURTLE) { if (ASM == xmrig::ASM_INTEL) { cn_trtl_mainloop_ivybridge_asm(ctx[0]); } @@ -645,10 +645,10 @@ inline void cryptonight_double_hash_asm(const uint8_t *__restrict__ input, size_ if (VARIANT == xmrig::VARIANT_2) { cnv2_double_mainloop_sandybridge_asm(ctx[0], ctx[1]); } - else if (VARIANT == xmrig::VARIANT_HALF) { + else if (VARIANT == xmrig::VARIANT_FAST_2) { cn_half_double_mainloop_sandybridge_asm(ctx[0], ctx[1]); } - else if (VARIANT == xmrig::VARIANT_TRTL) { + else if (VARIANT == xmrig::VARIANT_TURTLE) { cn_trtl_double_mainloop_sandybridge_asm(ctx[0], ctx[1]); } diff --git a/src/net/strategies/DonateStrategy.cpp b/src/net/strategies/DonateStrategy.cpp index e67083a1..c038b863 100644 --- a/src/net/strategies/DonateStrategy.cpp +++ b/src/net/strategies/DonateStrategy.cpp @@ -58,6 +58,8 @@ DonateStrategy::DonateStrategy(int level, const char *user, xmrig::Algo algo, IS m_pools.push_back(Pool("donate2.graef.in", 8443, userId, nullptr, true, false, true)); } else if (algo == xmrig::Algo::CRYPTONIGHT_LITE) { m_pools.push_back(Pool("donate2.graef.in", 1080, userId, nullptr, true, false, true)); + } else if (algo == xmrig::Algo::CRYPTONIGHT_ULTRALITE) { + m_pools.push_back(Pool("donate2.graef.in", 8090, userId, nullptr, true, false, true)); } else { m_pools.push_back(Pool("donate2.graef.in", 443, userId, nullptr, true, false, true)); } @@ -66,6 +68,8 @@ DonateStrategy::DonateStrategy(int level, const char *user, xmrig::Algo algo, IS m_pools.push_back(Pool("donate.graef.in", 8443, userId, nullptr, false, false, true)); } else if (algo == xmrig::Algo::CRYPTONIGHT_LITE) { m_pools.push_back(Pool("donate.graef.in", 1080, userId, nullptr, false, false, true)); + } else if (algo == xmrig::Algo::CRYPTONIGHT_ULTRALITE) { + m_pools.push_back(Pool("donate2.graef.in", 8088, userId, nullptr, false, false, true)); } else { m_pools.push_back(Pool("donate2.graef.in", 80, userId, nullptr, false, false, true)); } diff --git a/src/version.h b/src/version.h index 7b3eb174..ac9da3b6 100644 --- a/src/version.h +++ b/src/version.h @@ -29,14 +29,14 @@ #define APP_NAME "XMRigCC-AMD" #define APP_COPYRIGHT "Copyright (C) 2017- BenDr0id" #define APP_DESC "XMRigCC-AMD OpenCL miner" -#define APP_VERSION "1.8.9 (based on XMRig)" +#define APP_VERSION "1.8.10 (based on XMRig)" #define APP_SITE "https://github.com/Bendr0id/xmrigCC-amd" #define APP_DOMAIN "" #define APP_KIND "amd" #define APP_VER_MAJOR 1 #define APP_VER_MINOR 8 -#define APP_VER_PATCH 9 +#define APP_VER_PATCH 10 #define TYPE_AMD_GPU From 18f9b506a404fac71ec4b6ca040c5ccdca07a53e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Thu, 24 Jan 2019 20:29:03 +0100 Subject: [PATCH 72/97] Increased version --- src/version.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/version.h b/src/version.h index ac9da3b6..a3dd5b67 100644 --- a/src/version.h +++ b/src/version.h @@ -29,14 +29,14 @@ #define APP_NAME "XMRigCC-AMD" #define APP_COPYRIGHT "Copyright (C) 2017- BenDr0id" #define APP_DESC "XMRigCC-AMD OpenCL miner" -#define APP_VERSION "1.8.10 (based on XMRig)" +#define APP_VERSION "1.8.11 (based on XMRig)" #define APP_SITE "https://github.com/Bendr0id/xmrigCC-amd" #define APP_DOMAIN "" #define APP_KIND "amd" #define APP_VER_MAJOR 1 #define APP_VER_MINOR 8 -#define APP_VER_PATCH 10 +#define APP_VER_PATCH 11 #define TYPE_AMD_GPU From 99b083695c0abbe206489e62ceb5d6ddfe49c6fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Thu, 24 Jan 2019 20:47:23 +0100 Subject: [PATCH 73/97] --- CHANGELOG.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f84140ae..91ca8d21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,11 @@ +# 1.8.11 +- Added fix for upcoming Masari fork (cn-half/fast2) (algo: "cryptonight", variant: "msr" (autodetect), "fast2" (force)) +- Integrated cn-ultralite (turtleV2/DEGO) (algo: "cryptonight-ultralite", variant: "auto") +- Rebase from XMRig-amd 2.10.0 # 1.8.9 -- Improved algo parsing for XTL v9 aka cn-half +- Improved algo parsing for XTL v9 aka cn-half/fast2 # 1.8.8 -- Added XLT v5/9 with autodetect(algo: "cryptonight", variant: "xtl" (autodetect), "xtlv9" (force v9)) +- Added XLT v5/9 with autodetect(algo: "cryptonight", variant: "xtl" (autodetect), "fast2" (force v9)) - Added cn-lite variant UPX/uPlexa (algo: "cryptonight-lite", variant "upx") # 1.8.7 - Implemented Template based mass config editor to simple swap configs on your rigs From e7e483288d13bc5f0396575141364dde8fb6eec3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Thu, 24 Jan 2019 22:08:03 +0100 Subject: [PATCH 74/97] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 91ca8d21..af12f904 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,5 @@ # 1.8.11 -- Added fix for upcoming Masari fork (cn-half/fast2) (algo: "cryptonight", variant: "msr" (autodetect), "fast2" (force)) +- Added fix for upcoming Massari fork (cn-half/fast2) (algo: "cryptonight", variant: "msr" (autodetect), "fast2" (force)) - Integrated cn-ultralite (turtleV2/DEGO) (algo: "cryptonight-ultralite", variant: "auto") - Rebase from XMRig-amd 2.10.0 # 1.8.9 From fc410b993d2734756e8e543bdc91bb373701ae6b Mon Sep 17 00:00:00 2001 From: BenDr0id Date: Mon, 28 Jan 2019 10:34:21 +0100 Subject: [PATCH 75/97] Fixed xmrig-proxy usage for cn-ultralite/turtle/pico --- src/common/crypto/Algorithm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/crypto/Algorithm.cpp b/src/common/crypto/Algorithm.cpp index ade08240..46d0a68b 100644 --- a/src/common/crypto/Algorithm.cpp +++ b/src/common/crypto/Algorithm.cpp @@ -83,8 +83,8 @@ static AlgoData const algorithms[] = { # endif # ifndef XMRIG_NO_CN_ULTRALITE - { "cryptonight-ultralite/turtle", "cn-ultralite/turtle", xmrig::CRYPTONIGHT_ULTRALITE, xmrig::VARIANT_TURTLE }, { "cryptonight-ultralite", "cn-ultralite", xmrig::CRYPTONIGHT_ULTRALITE, xmrig::VARIANT_TURTLE }, + { "cryptonight-ultralite/turtle", "cn-ultralite/turtle", xmrig::CRYPTONIGHT_ULTRALITE, xmrig::VARIANT_TURTLE }, { "cryptonight-ultralite/2", "cn-ultralite/2", xmrig::CRYPTONIGHT_ULTRALITE, xmrig::VARIANT_TURTLE }, { "cryptonight-pico/trtl", "cn-pico/trtl", xmrig::CRYPTONIGHT_ULTRALITE, xmrig::VARIANT_TURTLE }, { "cryptonight-pico", "cn-pico", xmrig::CRYPTONIGHT_ULTRALITE, xmrig::VARIANT_TURTLE }, From ea6a69586577889bbac4adf4e25224b8e72cbf37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Mon, 28 Jan 2019 22:52:24 +0100 Subject: [PATCH 76/97] #1.8.12 Preparation --- CHANGELOG.md | 4 +++- src/version.h | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index af12f904..64249df9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ +# 1.8.12 +- Fixed cn-ultralite no suitable algo error when using xmrig-proxy # 1.8.11 -- Added fix for upcoming Massari fork (cn-half/fast2) (algo: "cryptonight", variant: "msr" (autodetect), "fast2" (force)) +- Added fix for upcoming Masari fork (cn-half/fast2) (algo: "cryptonight", variant: "msr" (autodetect), "fast2" (force)) - Integrated cn-ultralite (turtleV2/DEGO) (algo: "cryptonight-ultralite", variant: "auto") - Rebase from XMRig-amd 2.10.0 # 1.8.9 diff --git a/src/version.h b/src/version.h index a3dd5b67..d7a9942c 100644 --- a/src/version.h +++ b/src/version.h @@ -29,14 +29,14 @@ #define APP_NAME "XMRigCC-AMD" #define APP_COPYRIGHT "Copyright (C) 2017- BenDr0id" #define APP_DESC "XMRigCC-AMD OpenCL miner" -#define APP_VERSION "1.8.11 (based on XMRig)" +#define APP_VERSION "1.8.12 (based on XMRig)" #define APP_SITE "https://github.com/Bendr0id/xmrigCC-amd" #define APP_DOMAIN "" #define APP_KIND "amd" #define APP_VER_MAJOR 1 #define APP_VER_MINOR 8 -#define APP_VER_PATCH 11 +#define APP_VER_PATCH 12 #define TYPE_AMD_GPU From 8c4294363f9d948ff99c976d563ea53d68cc5bb7 Mon Sep 17 00:00:00 2001 From: BenDr0id Date: Thu, 31 Jan 2019 11:54:27 +0100 Subject: [PATCH 77/97] Added more names to the algo parse CN-Turtle/Ultralite --- src/common/crypto/Algorithm.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/common/crypto/Algorithm.cpp b/src/common/crypto/Algorithm.cpp index 46d0a68b..3e770d51 100644 --- a/src/common/crypto/Algorithm.cpp +++ b/src/common/crypto/Algorithm.cpp @@ -88,6 +88,7 @@ static AlgoData const algorithms[] = { { "cryptonight-ultralite/2", "cn-ultralite/2", xmrig::CRYPTONIGHT_ULTRALITE, xmrig::VARIANT_TURTLE }, { "cryptonight-pico/trtl", "cn-pico/trtl", xmrig::CRYPTONIGHT_ULTRALITE, xmrig::VARIANT_TURTLE }, { "cryptonight-pico", "cn-pico", xmrig::CRYPTONIGHT_ULTRALITE, xmrig::VARIANT_TURTLE }, + { "cryptonight-turtle", "cn-turtle", xmrig::CRYPTONIGHT_ULTRALITE, xmrig::VARIANT_TURTLE }, { "cryptonight-turtle", "cn-trtl", xmrig::CRYPTONIGHT_ULTRALITE, xmrig::VARIANT_TURTLE }, { "cryptonight_turtle", "cn_turtle", xmrig::CRYPTONIGHT_ULTRALITE, xmrig::VARIANT_TURTLE }, # endif From 888aa22bc57306978f86613ccd3d69df2d29e4f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Thu, 31 Jan 2019 18:02:43 +0100 Subject: [PATCH 78/97] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 64249df9..6f66ff5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # 1.8.12 - Fixed cn-ultralite no suitable algo error when using xmrig-proxy +- Added more names to the algo parse CN-Turtle/Ultralite # 1.8.11 - Added fix for upcoming Masari fork (cn-half/fast2) (algo: "cryptonight", variant: "msr" (autodetect), "fast2" (force)) - Integrated cn-ultralite (turtleV2/DEGO) (algo: "cryptonight-ultralite", variant: "auto") From b696eadf9603cf61c2dd4f02600793acfde66ef1 Mon Sep 17 00:00:00 2001 From: BenDr0id Date: Tue, 12 Feb 2019 18:35:53 +0100 Subject: [PATCH 79/97] Fix for GPU variant --- src/amd/OclGPU.cpp | 21 ++++++++++++--------- src/version.h | 4 ++-- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/amd/OclGPU.cpp b/src/amd/OclGPU.cpp index 3a0ed2cb..a688e369 100644 --- a/src/amd/OclGPU.cpp +++ b/src/amd/OclGPU.cpp @@ -69,7 +69,7 @@ inline static int cn0KernelOffset(xmrig::Variant variant) { # ifndef XMRIG_NO_CN_GPU if (variant == xmrig::VARIANT_GPU) { - return 13; + return 15; } # endif @@ -112,7 +112,7 @@ inline static int cn1KernelOffset(xmrig::Variant variant) # ifndef XMRIG_NO_CN_GPU case xmrig::VARIANT_GPU: - return 14; + return 16; # endif default: @@ -128,7 +128,7 @@ inline static int cn2KernelOffset(xmrig::Variant variant) { # ifndef XMRIG_NO_CN_GPU if (variant == xmrig::VARIANT_GPU) { - return 15; + return 17; } # endif @@ -552,11 +552,13 @@ size_t XMRSetJob(GpuContext *ctx, uint8_t *input, size_t input_len, uint64_t tar return OCL_ERR_API; } - // variant - const cl_uint v = static_cast(variant); - if ((ret = OclLib::setKernelArg(ctx->Kernels[0], 4, sizeof(cl_uint), &v)) != CL_SUCCESS) { - LOG_ERR(kSetKernelArgErr, err_to_str(ret), 0, 4); - return OCL_ERR_API; + if (variant != xmrig::VARIANT_GPU) { + // variant + const auto v = static_cast(variant); + if ((ret = OclLib::setKernelArg(ctx->Kernels[cn0_kernel_offset], 4, sizeof(cl_uint), &v)) != CL_SUCCESS) { + LOG_ERR(kSetKernelArgErr, err_to_str(ret), cn0_kernel_offset, 4); + return OCL_ERR_API; + } } // CN1 Kernel @@ -576,7 +578,7 @@ size_t XMRSetJob(GpuContext *ctx, uint8_t *input, size_t input_len, uint64_t tar } else { // variant - const cl_uint v = static_cast(variant); + const auto v = static_cast(variant); if ((ret = OclLib::setKernelArg(ctx->Kernels[cn1_kernel_offset], 2, sizeof(cl_uint), &v)) != CL_SUCCESS) { LOG_ERR(kSetKernelArgErr, err_to_str(ret), cn1_kernel_offset, 2); return OCL_ERR_API; @@ -637,6 +639,7 @@ size_t XMRSetJob(GpuContext *ctx, uint8_t *input, size_t input_len, uint64_t tar } // variant + const auto v = static_cast(variant); if ((ret = OclLib::setKernelArg(ctx->Kernels[cn2_kernel_offset], 7, sizeof(cl_uint), &v)) != CL_SUCCESS) { LOG_ERR(kSetKernelArgErr, err_to_str(ret), 2, 7); return OCL_ERR_API; diff --git a/src/version.h b/src/version.h index d7a9942c..191d564c 100644 --- a/src/version.h +++ b/src/version.h @@ -29,14 +29,14 @@ #define APP_NAME "XMRigCC-AMD" #define APP_COPYRIGHT "Copyright (C) 2017- BenDr0id" #define APP_DESC "XMRigCC-AMD OpenCL miner" -#define APP_VERSION "1.8.12 (based on XMRig)" +#define APP_VERSION "1.8.14 (based on XMRig)" #define APP_SITE "https://github.com/Bendr0id/xmrigCC-amd" #define APP_DOMAIN "" #define APP_KIND "amd" #define APP_VER_MAJOR 1 #define APP_VER_MINOR 8 -#define APP_VER_PATCH 12 +#define APP_VER_PATCH 14 #define TYPE_AMD_GPU From 9201a4ce61918a13733e2d7c6c70dd28b276fd42 Mon Sep 17 00:00:00 2001 From: BenDr0id Date: Wed, 13 Feb 2019 15:25:00 +0100 Subject: [PATCH 80/97] Fixed build --- CMakeLists.txt | 3 +++ src/common/crypto/Algorithm.cpp | 8 +++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9a2ede61..cd7404b3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -330,3 +330,6 @@ target_link_libraries(xmrigMiner-amd ${UV_LIBRARIES} ${MHD_LIBRARY} ${EXTRA_LIBS if (WITH_TLS) target_link_libraries(xmrigMiner-amd xmrig_tls ${OPENSSL_LIBRARIES} ${EXTRA_LIBS}) endif (WITH_TLS) + +add_executable(xmrigDaemon-amd src/cc/XMRigd.cpp res/app.rc) +set_target_properties(xmrigDaemon-amd PROPERTIES OUTPUT_NAME ${DAEMON_EXECUTABLE_NAME}) diff --git a/src/common/crypto/Algorithm.cpp b/src/common/crypto/Algorithm.cpp index 228c4310..ddbba1a3 100644 --- a/src/common/crypto/Algorithm.cpp +++ b/src/common/crypto/Algorithm.cpp @@ -66,6 +66,8 @@ static AlgoData const algorithms[] = { { "cryptonight/fast2", "cn/fast2", xmrig::CRYPTONIGHT, xmrig::VARIANT_FAST_2 }, { "cryptonight/xtlv9", "cn/xtlv9", xmrig::CRYPTONIGHT, xmrig::VARIANT_FAST_2 }, { "cryptonight/half", "cn/half", xmrig::CRYPTONIGHT, xmrig::VARIANT_FAST_2 }, + { "cryptonight/hosp", "cn/hosp", xmrig::CRYPTONIGHT, xmrig::VARIANT_RTO }, + { "cryptonight/hospital", "cn/hospital", xmrig::CRYPTONIGHT, xmrig::VARIANT_RTO }, # ifndef XMRIG_NO_AEON { "cryptonight-lite", "cn-lite", xmrig::CRYPTONIGHT_LITE, xmrig::VARIANT_AUTO }, @@ -228,11 +230,11 @@ void xmrig::Algorithm::parseVariant(const char *variant) m_variant = VARIANT_FAST_2; } - if (strcasecmp(variant, "half") == 0) { - m_variant = VARIANT_FAST_2; + if (strcasecmp(variant, "hosp") == 0 || strcasecmp(variant, "hospital") == 0) { + m_variant = VARIANT_RTO; } - if (strcasecmp(variant, "msr2") == 0) { + if (strcasecmp(variant, "half") == 0 || strcasecmp(variant, "msr2") == 0) { m_variant = VARIANT_FAST_2; } } From a58d7dadc338a0c6e80ade4fa4ea25c3bb013fbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Wed, 13 Feb 2019 21:22:59 +0100 Subject: [PATCH 81/97] #1.8.14 Preparation --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f66ff5f..649062b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 1.8.14 +- Rebase from XMRig-amd 2.11.0 + - Integrated new RYO algo CN-GPU (algo: "cryptonight", variant: "gpu") +- Added alias CN-HOSP for RTO algo (algo: "cryptonight", variant: "hosp" or variant: "rto") # 1.8.12 - Fixed cn-ultralite no suitable algo error when using xmrig-proxy - Added more names to the algo parse CN-Turtle/Ultralite From 75e1dff46496bbe2b9aa6649891aab2075bc8316 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Sat, 16 Feb 2019 19:39:01 +0100 Subject: [PATCH 82/97] Fixed crash when remote logging is disabled --- src/common/log/RemoteLog.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/common/log/RemoteLog.cpp b/src/common/log/RemoteLog.cpp index f51ff8bf..75cb5ed3 100644 --- a/src/common/log/RemoteLog.cpp +++ b/src/common/log/RemoteLog.cpp @@ -84,17 +84,17 @@ std::string RemoteLog::getRows() { std::stringstream data; - uv_mutex_lock(&m_self->m_mutex); - if (m_self) { - for (auto& m_row : m_self->m_rows) { + 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(); + m_self->m_rows.clear(); - uv_mutex_unlock(&m_self->m_mutex); + uv_mutex_unlock(&m_self->m_mutex); + } return data.str(); } From ffc18c2edc0d4257e9f33015040384865323de6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Sun, 17 Feb 2019 13:19:15 +0100 Subject: [PATCH 83/97] Fixed startup loop when using -h parameter --- src/App.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/App.cpp b/src/App.cpp index f9d60b22..45610421 100644 --- a/src/App.cpp +++ b/src/App.cpp @@ -99,7 +99,7 @@ App::~App() int App::exec() { if (m_controller->isDone()) { - return 0; + return 1; } if (!m_controller->isReady()) { From b7843fa6c4e3413c70bc2334aea81e14ecb2af3f Mon Sep 17 00:00:00 2001 From: BenDr0id Date: Fri, 22 Feb 2019 16:24:47 +0100 Subject: [PATCH 84/97] Fixed segfault on stop --- src/App.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/App.cpp b/src/App.cpp index bddb956f..a789a884 100644 --- a/src/App.cpp +++ b/src/App.cpp @@ -59,6 +59,8 @@ xmrig::App::App(Process *process) : m_ccclient(nullptr), m_hashrateMonitor(nullptr) { + m_self = this; + m_controller = new xmrig::Controller(process); if (m_controller->init() != 0) { return; @@ -213,7 +215,6 @@ void xmrig::App::stop(bool restart) m_controller->network()->stop(); Workers::stop(restart); - uv_stop(uv_default_loop()); } From ea5de9b30dac5307f047a0d6f8ec61e17de7c285 Mon Sep 17 00:00:00 2001 From: BenDr0id Date: Fri, 22 Feb 2019 16:49:18 +0100 Subject: [PATCH 85/97] Cleanup --- src/App.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/App.cpp b/src/App.cpp index a789a884..c968557a 100644 --- a/src/App.cpp +++ b/src/App.cpp @@ -147,10 +147,6 @@ int xmrig::App::exec() uv_async_init(uv_default_loop(), &m_async, xmrig::App::onCommandReceived); m_ccclient = new CCClient(m_controller->config(), &m_async); - - if (!m_controller->config()->pools().data().empty()) { - 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."); } From c88de439b045e8233a052c832849cb3bef825801 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Tue, 5 Mar 2019 23:58:21 +0100 Subject: [PATCH 86/97] WIP Integration XCASH/Zelerius --- src/amd/OclGPU.cpp | 16 +- src/amd/opencl/cryptonight.cl | 329 ++++++++++++++++- src/base/net/Pool.cpp | 9 + src/common/crypto/Algorithm.cpp | 14 + src/common/interfaces/IWatcherListener.h | 46 +++ src/common/net/Pool.cpp | 431 +++++++++++++++++++++++ src/common/net/Pool.h | 116 ++++++ src/common/xmrig.h | 2 + src/crypto/CryptoNight.cpp | 21 +- src/crypto/CryptoNight_constants.h | 13 +- src/crypto/CryptoNight_test.h | 13 + 11 files changed, 998 insertions(+), 12 deletions(-) create mode 100644 src/common/interfaces/IWatcherListener.h create mode 100644 src/common/net/Pool.cpp create mode 100644 src/common/net/Pool.h diff --git a/src/amd/OclGPU.cpp b/src/amd/OclGPU.cpp index c6dea43d..f685037a 100644 --- a/src/amd/OclGPU.cpp +++ b/src/amd/OclGPU.cpp @@ -71,7 +71,7 @@ inline static int cn0KernelOffset(xmrig::Variant variant) { # ifndef XMRIG_NO_CN_GPU if (variant == xmrig::VARIANT_GPU) { - return 15; + return 17; } # endif @@ -112,14 +112,20 @@ inline static int cn1KernelOffset(xmrig::Variant variant) case xmrig::VARIANT_UPX: return 14; + case xmrig::VARIANT_XCASH: + return 15; + + case xmrig::VARIANT_ZELERIUS: + return 16; + # ifndef XMRIG_NO_CN_GPU case xmrig::VARIANT_GPU: - return 16; + return 19; # endif case xmrig::VARIANT_WOW: case xmrig::VARIANT_4: - return 18; + return 21; default: break; @@ -134,7 +140,7 @@ inline static int cn2KernelOffset(xmrig::Variant variant) { # ifndef XMRIG_NO_CN_GPU if (variant == xmrig::VARIANT_GPU) { - return 17; + return 20; } # endif @@ -252,7 +258,7 @@ size_t InitOpenCLGpu(int index, cl_context opencl_ctx, GpuContext* ctx, const ch const char *KernelNames[] = { "cn0", "cn1", "cn2", "Blake", "Groestl", "JH", "Skein", - "cn1_monero", "cn1_msr", "cn1_xao", "cn1_tube", "cn1_v2_monero", "cn1_xfh", "cn1_fastv2", "cn1_upx", + "cn1_monero", "cn1_msr", "cn1_xao", "cn1_tube", "cn1_v2_monero", "cn1_xfh", "cn1_fastv2", "cn1_upx", "cn1_xcash", "cn1_zelerius", # ifndef XMRIG_NO_CN_GPU "cn0_cn_gpu", "cn00_cn_gpu", "cn1_cn_gpu", "cn2_cn_gpu", # endif diff --git a/src/amd/opencl/cryptonight.cl b/src/amd/opencl/cryptonight.cl index cb894963..de079c58 100644 --- a/src/amd/opencl/cryptonight.cl +++ b/src/amd/opencl/cryptonight.cl @@ -104,10 +104,12 @@ XMRIG_INCLUDE_FAST_DIV_HEAVY #define VARIANT_RTO 7 // Modified CryptoNight variant 1 (Arto only) #define VARIANT_2 8 // CryptoNight variant 2 #define VARIANT_XFH 9 // CryptoNight variant xfh aka cn-heavy-superfast -#define VARIANT_FAST_2 10 // CryptoNight variant 2 with half iterations (Masari/Stellite) -#define VARIANT_TURTLE 11 // CryptoNight Turtle (TRTL) -#define VARIANT_UPX 12 // CryptoNight UPX -#define VARIANT_GPU 13 // CryptoNight-GPU (Ryo) +#define VARIANT_FAST_2 10 // CryptoNight variant 2 with half iterations (Masari/Stellite) +#define VARIANT_TURTLE 11 // CryptoNight Turtle (TRTL) +#define VARIANT_UPX 12 // CryptoNight UPX +#define VARIANT_GPU 13 // CryptoNight-GPU (Ryo) +#define VARIANT_XCASH = 16 // Cryptonight (2) variant xcash +#define VARIANT_ZELERIUS = 17 // Cryptonight (2) variant zelerius #define CRYPTONIGHT 0 /* CryptoNight (2 MB) */ #define CRYPTONIGHT_LITE 1 /* CryptoNight (1 MB) */ @@ -1103,6 +1105,325 @@ __kernel void cn1_fastv2(__global uint4 *Scratchpad, __global ulong *states, uin # endif } +)===" +R"===( +__attribute__((reqd_work_group_size(WORKSIZE, 1, 1))) +__kernel void cn1_xcash(__global uint4 *Scratchpad, __global ulong *states, uint variant, __global ulong *input, uint Threads) +{ +# if (ALGO == CRYPTONIGHT) + ulong a[2], b[4]; + __local uint AES0[256], AES1[256], AES2[256], AES3[256]; + + const ulong gIdx = getIdx(); + + for(int i = get_local_id(0); i < 256; i += WORKSIZE) + { + const uint tmp = AES0_C[i]; + AES0[i] = tmp; + AES1[i] = rotate(tmp, 8U); + AES2[i] = rotate(tmp, 16U); + AES3[i] = rotate(tmp, 24U); + } + + barrier(CLK_LOCAL_MEM_FENCE); + +# if (COMP_MODE == 1) + // do not use early return here + if (gIdx < Threads) +# endif + { + states += 25 * gIdx; + +# if defined(__NV_CL_C_VERSION) + Scratchpad += gIdx * (ITERATIONS >> 2); +# else +# if (STRIDED_INDEX == 0) + Scratchpad += gIdx * (MEMORY >> 4); +# elif (STRIDED_INDEX == 1) + Scratchpad += gIdx; +# elif (STRIDED_INDEX == 2) + Scratchpad += get_group_id(0) * (MEMORY >> 4) * WORKSIZE + MEM_CHUNK * get_local_id(0); +# endif +# endif + + a[0] = states[0] ^ states[4]; + a[1] = states[1] ^ states[5]; + + b[0] = states[2] ^ states[6]; + b[1] = states[3] ^ states[7]; + b[2] = states[8] ^ states[10]; + b[3] = states[9] ^ states[11]; + } + + ulong2 bx0 = ((ulong2 *)b)[0]; + ulong2 bx1 = ((ulong2 *)b)[1]; + + mem_fence(CLK_LOCAL_MEM_FENCE); + +# ifdef __NV_CL_C_VERSION + __local uint16 scratchpad_line_buf[WORKSIZE]; + __local uint16* scratchpad_line = scratchpad_line_buf + get_local_id(0); +# define SCRATCHPAD_CHUNK(N) (*(__local uint4*)((__local uchar*)(scratchpad_line) + (idx1 ^ (N << 4)))) +# else +# if (STRIDED_INDEX == 0) +# define SCRATCHPAD_CHUNK(N) (*(__global uint4*)((__global uchar*)(Scratchpad) + (idx ^ (N << 4)))) +# elif (STRIDED_INDEX == 1) +# define SCRATCHPAD_CHUNK(N) (*(__global uint4*)((__global uchar*)(Scratchpad) + mul24(as_uint(idx ^ (N << 4)), Threads))) +# elif (STRIDED_INDEX == 2) +# define SCRATCHPAD_CHUNK(N) (*(__global uint4*)((__global uchar*)(Scratchpad) + (((idx ^ (N << 4)) % (MEM_CHUNK << 4)) + ((idx ^ (N << 4)) / (MEM_CHUNK << 4)) * WORKSIZE * (MEM_CHUNK << 4)))) +# endif +# endif + +# if (COMP_MODE == 1) + // do not use early return here + if (gIdx < Threads) +# endif + { + uint2 division_result = as_uint2(states[12]); + uint sqrt_result = as_uint2(states[13]).s0; + + #pragma unroll UNROLL_FACTOR + for(int i = 0; i < 0x100000; ++i) + { +# ifdef __NV_CL_C_VERSION + uint idx = a[0] & 0x1FFFC0; + uint idx1 = a[0] & 0x30; + + *scratchpad_line = *(__global uint16*)((__global uchar*)(Scratchpad) + idx); +# else + uint idx = a[0] & MASK; +# endif + + uint4 c = SCRATCHPAD_CHUNK(0); + c = AES_Round(AES0, AES1, AES2, AES3, c, ((uint4 *)a)[0]); + + { + const ulong2 chunk1 = as_ulong2(SCRATCHPAD_CHUNK(1)); + const ulong2 chunk2 = as_ulong2(SCRATCHPAD_CHUNK(2)); + const ulong2 chunk3 = as_ulong2(SCRATCHPAD_CHUNK(3)); + + SCRATCHPAD_CHUNK(1) = as_uint4(chunk3 + bx1); + SCRATCHPAD_CHUNK(2) = as_uint4(chunk1 + bx0); + SCRATCHPAD_CHUNK(3) = as_uint4(chunk2 + ((ulong2 *)a)[0]); + } + + SCRATCHPAD_CHUNK(0) = as_uint4(bx0) ^ c; + +# ifdef __NV_CL_C_VERSION + *(__global uint16*)((__global uchar*)(Scratchpad) + idx) = *scratchpad_line; + + idx = as_ulong2(c).s0 & 0x1FFFC0; + idx1 = as_ulong2(c).s0 & 0x30; + + *scratchpad_line = *(__global uint16*)((__global uchar*)(Scratchpad) + idx); +# else + idx = as_ulong2(c).s0 & MASK; +# endif + + uint4 tmp = SCRATCHPAD_CHUNK(0); + + { + tmp.s0 ^= division_result.s0; + tmp.s1 ^= division_result.s1 ^ sqrt_result; + + division_result = fast_div_v2(as_ulong2(c).s1, (c.s0 + (sqrt_result << 1)) | 0x80000001UL); + sqrt_result = fast_sqrt_v2(as_ulong2(c).s0 + as_ulong(division_result)); + } + + ulong2 t; + t.s0 = mul_hi(as_ulong2(c).s0, as_ulong2(tmp).s0); + t.s1 = as_ulong2(c).s0 * as_ulong2(tmp).s0; + { + const ulong2 chunk1 = as_ulong2(SCRATCHPAD_CHUNK(1)) ^ t; + const ulong2 chunk2 = as_ulong2(SCRATCHPAD_CHUNK(2)); + t ^= chunk2; + const ulong2 chunk3 = as_ulong2(SCRATCHPAD_CHUNK(3)); + + SCRATCHPAD_CHUNK(1) = as_uint4(chunk3 + bx1); + SCRATCHPAD_CHUNK(2) = as_uint4(chunk1 + bx0); + SCRATCHPAD_CHUNK(3) = as_uint4(chunk2 + ((ulong2 *)a)[0]); + } + + a[1] += t.s1; + a[0] += t.s0; + + SCRATCHPAD_CHUNK(0) = ((uint4 *)a)[0]; + +# ifdef __NV_CL_C_VERSION + *(__global uint16*)((__global uchar*)(Scratchpad) + idx) = *scratchpad_line; +# endif + + ((uint4 *)a)[0] ^= tmp; + bx1 = bx0; + bx0 = as_ulong2(c); + } + +# undef SCRATCHPAD_CHUNK + } + mem_fence(CLK_GLOBAL_MEM_FENCE); +# endif +} + +)===" +R"===( + +__attribute__((reqd_work_group_size(WORKSIZE, 1, 1))) +__kernel void cn1_zelerius(__global uint4 *Scratchpad, __global ulong *states, uint variant, __global ulong *input, uint Threads) +{ +# if (ALGO == CRYPTONIGHT) + ulong a[2], b[4]; + __local uint AES0[256], AES1[256], AES2[256], AES3[256]; + + const ulong gIdx = getIdx(); + + for(int i = get_local_id(0); i < 256; i += WORKSIZE) + { + const uint tmp = AES0_C[i]; + AES0[i] = tmp; + AES1[i] = rotate(tmp, 8U); + AES2[i] = rotate(tmp, 16U); + AES3[i] = rotate(tmp, 24U); + } + + barrier(CLK_LOCAL_MEM_FENCE); + +# if (COMP_MODE == 1) + // do not use early return here + if (gIdx < Threads) +# endif + { + states += 25 * gIdx; + +# if defined(__NV_CL_C_VERSION) + Scratchpad += gIdx * (ITERATIONS >> 2); +# else +# if (STRIDED_INDEX == 0) + Scratchpad += gIdx * (MEMORY >> 4); +# elif (STRIDED_INDEX == 1) + Scratchpad += gIdx; +# elif (STRIDED_INDEX == 2) + Scratchpad += get_group_id(0) * (MEMORY >> 4) * WORKSIZE + MEM_CHUNK * get_local_id(0); +# endif +# endif + + a[0] = states[0] ^ states[4]; + a[1] = states[1] ^ states[5]; + + b[0] = states[2] ^ states[6]; + b[1] = states[3] ^ states[7]; + b[2] = states[8] ^ states[10]; + b[3] = states[9] ^ states[11]; + } + + ulong2 bx0 = ((ulong2 *)b)[0]; + ulong2 bx1 = ((ulong2 *)b)[1]; + + mem_fence(CLK_LOCAL_MEM_FENCE); + +# ifdef __NV_CL_C_VERSION + __local uint16 scratchpad_line_buf[WORKSIZE]; + __local uint16* scratchpad_line = scratchpad_line_buf + get_local_id(0); +# define SCRATCHPAD_CHUNK(N) (*(__local uint4*)((__local uchar*)(scratchpad_line) + (idx1 ^ (N << 4)))) +# else +# if (STRIDED_INDEX == 0) +# define SCRATCHPAD_CHUNK(N) (*(__global uint4*)((__global uchar*)(Scratchpad) + (idx ^ (N << 4)))) +# elif (STRIDED_INDEX == 1) +# define SCRATCHPAD_CHUNK(N) (*(__global uint4*)((__global uchar*)(Scratchpad) + mul24(as_uint(idx ^ (N << 4)), Threads))) +# elif (STRIDED_INDEX == 2) +# define SCRATCHPAD_CHUNK(N) (*(__global uint4*)((__global uchar*)(Scratchpad) + (((idx ^ (N << 4)) % (MEM_CHUNK << 4)) + ((idx ^ (N << 4)) / (MEM_CHUNK << 4)) * WORKSIZE * (MEM_CHUNK << 4)))) +# endif +# endif + +# if (COMP_MODE == 1) + // do not use early return here + if (gIdx < Threads) +# endif + { + uint2 division_result = as_uint2(states[12]); + uint sqrt_result = as_uint2(states[13]).s0; + + #pragma unroll UNROLL_FACTOR + for(int i = 0; i < 0x60000; ++i) + { +# ifdef __NV_CL_C_VERSION + uint idx = a[0] & 0x1FFFC0; + uint idx1 = a[0] & 0x30; + + *scratchpad_line = *(__global uint16*)((__global uchar*)(Scratchpad) + idx); +# else + uint idx = a[0] & MASK; +# endif + + uint4 c = SCRATCHPAD_CHUNK(0); + c = AES_Round(AES0, AES1, AES2, AES3, c, ((uint4 *)a)[0]); + + { + const ulong2 chunk1 = as_ulong2(SCRATCHPAD_CHUNK(1)); + const ulong2 chunk2 = as_ulong2(SCRATCHPAD_CHUNK(2)); + const ulong2 chunk3 = as_ulong2(SCRATCHPAD_CHUNK(3)); + + SCRATCHPAD_CHUNK(1) = as_uint4(chunk3 + bx1); + SCRATCHPAD_CHUNK(2) = as_uint4(chunk1 + bx0); + SCRATCHPAD_CHUNK(3) = as_uint4(chunk2 + ((ulong2 *)a)[0]); + } + + SCRATCHPAD_CHUNK(0) = as_uint4(bx0) ^ c; + +# ifdef __NV_CL_C_VERSION + *(__global uint16*)((__global uchar*)(Scratchpad) + idx) = *scratchpad_line; + + idx = as_ulong2(c).s0 & 0x1FFFC0; + idx1 = as_ulong2(c).s0 & 0x30; + + *scratchpad_line = *(__global uint16*)((__global uchar*)(Scratchpad) + idx); +# else + idx = as_ulong2(c).s0 & MASK; +# endif + + uint4 tmp = SCRATCHPAD_CHUNK(0); + + { + tmp.s0 ^= division_result.s0; + tmp.s1 ^= division_result.s1 ^ sqrt_result; + + division_result = fast_div_v2(as_ulong2(c).s1, (c.s0 + (sqrt_result << 1)) | 0x80000001UL); + sqrt_result = fast_sqrt_v2(as_ulong2(c).s0 + as_ulong(division_result)); + } + + ulong2 t; + t.s0 = mul_hi(as_ulong2(c).s0, as_ulong2(tmp).s0); + t.s1 = as_ulong2(c).s0 * as_ulong2(tmp).s0; + { + const ulong2 chunk1 = as_ulong2(SCRATCHPAD_CHUNK(1)) ^ t; + const ulong2 chunk2 = as_ulong2(SCRATCHPAD_CHUNK(2)); + t ^= chunk2; + const ulong2 chunk3 = as_ulong2(SCRATCHPAD_CHUNK(3)); + + SCRATCHPAD_CHUNK(1) = as_uint4(chunk3 + bx1); + SCRATCHPAD_CHUNK(2) = as_uint4(chunk1 + bx0); + SCRATCHPAD_CHUNK(3) = as_uint4(chunk2 + ((ulong2 *)a)[0]); + } + + a[1] += t.s1; + a[0] += t.s0; + + SCRATCHPAD_CHUNK(0) = ((uint4 *)a)[0]; + +# ifdef __NV_CL_C_VERSION + *(__global uint16*)((__global uchar*)(Scratchpad) + idx) = *scratchpad_line; +# endif + + ((uint4 *)a)[0] ^= tmp; + bx1 = bx0; + bx0 = as_ulong2(c); + } + +# undef SCRATCHPAD_CHUNK + } + mem_fence(CLK_GLOBAL_MEM_FENCE); +# endif +} + )===" R"===( diff --git a/src/base/net/Pool.cpp b/src/base/net/Pool.cpp index 90bca977..eb448d5a 100644 --- a/src/base/net/Pool.cpp +++ b/src/base/net/Pool.cpp @@ -496,6 +496,15 @@ void xmrig::Pool::rebuild() addVariant(VARIANT_XAO); addVariant(VARIANT_RTO); addVariant(VARIANT_GPU); + + // XMrigCC custom variants + addVariant(VARIANT_TURTLE); + addVariant(VARIANT_XFH); + addVariant(VARIANT_UPX); + addVariant(VARIANT_XCASH); + addVariant(VARIANT_ZELERIUS); + addVariant(VARIANT_AUTO); + # endif } diff --git a/src/common/crypto/Algorithm.cpp b/src/common/crypto/Algorithm.cpp index c2030e5d..cb4677f1 100644 --- a/src/common/crypto/Algorithm.cpp +++ b/src/common/crypto/Algorithm.cpp @@ -70,6 +70,10 @@ static AlgoData const algorithms[] = { { "cryptonight/hospital", "cn/hospital", xmrig::CRYPTONIGHT, xmrig::VARIANT_RTO }, { "cryptonight/wow", "cn/wow", xmrig::CRYPTONIGHT, xmrig::VARIANT_WOW }, { "cryptonight/r", "cn/r", xmrig::CRYPTONIGHT, xmrig::VARIANT_4 }, + { "cryptonight/xcash", "cn/xcash", xmrig::CRYPTONIGHT, xmrig::VARIANT_XCASH}, + { "cryptonight/heavyx", "cn/heavyx", xmrig::CRYPTONIGHT, xmrig::VARIANT_XCASH}, + { "cryptonight/zelerius", "cn/zelerius", xmrig::CRYPTONIGHT, xmrig::VARIANT_ZELERIUS}, + { "cryptonight/zlx", "cn/zlx", xmrig::CRYPTONIGHT, xmrig::VARIANT_ZELERIUS}, # ifndef XMRIG_NO_AEON { "cryptonight-lite", "cn-lite", xmrig::CRYPTONIGHT_LITE, xmrig::VARIANT_AUTO }, @@ -142,6 +146,8 @@ static const char *variants[] = { "gpu", "wow", "r", + "xcash", + "zelerius" }; @@ -241,6 +247,14 @@ void xmrig::Algorithm::parseVariant(const char *variant) if (strcasecmp(variant, "half") == 0 || strcasecmp(variant, "msr2") == 0) { m_variant = VARIANT_FAST_2; } + + if (strcasecmp(variant, "zlx") == 0 || strcasecmp(variant, "zelerius") == 0) { + m_variant = VARIANT_ZELERIUS; + } + + if (strcasecmp(variant, "xcash") == 0 || strcasecmp(variant, "heavyx") == 0) { + m_variant = VARIANT_XCASH; + } } diff --git a/src/common/interfaces/IWatcherListener.h b/src/common/interfaces/IWatcherListener.h new file mode 100644 index 00000000..bfafb9a0 --- /dev/null +++ b/src/common/interfaces/IWatcherListener.h @@ -0,0 +1,46 @@ +/* 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 . + */ + +#ifndef __IWATCHERLISTENER_H__ +#define __IWATCHERLISTENER_H__ + + +namespace xmrig { + + +class IConfig; + + +class IWatcherListener +{ +public: + virtual ~IWatcherListener() {} + + virtual void onNewConfig(IConfig *config) = 0; +}; + + +} /* namespace xmrig */ + + +#endif // __IWATCHERLISTENER_H__ diff --git a/src/common/net/Pool.cpp b/src/common/net/Pool.cpp new file mode 100644 index 00000000..734c3599 --- /dev/null +++ b/src/common/net/Pool.cpp @@ -0,0 +1,431 @@ +/* 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 "common/net/Pool.h" +#include "rapidjson/document.h" + + +#ifdef APP_DEBUG +# include "common/log/Log.h" +#endif + + +#ifdef _MSC_VER +# define strncasecmp _strnicmp +# define strcasecmp _stricmp +#endif + + +Pool::Pool() : + m_nicehash(false), + m_tls(false), + m_keepAlive(0), + m_port(kDefaultPort) +{ +} + + +/** + * @brief Parse url. + * + * Valid urls: + * example.com + * example.com:3333 + * stratum+tcp://example.com + * stratum+tcp://example.com:3333 + * + * @param url + */ +Pool::Pool(const char *url) : + m_nicehash(false), + m_tls(false), + m_keepAlive(0), + m_port(kDefaultPort) +{ + parse(url); +} + + +Pool::Pool(const char *host, uint16_t port, const char *user, const char *password, int keepAlive, bool nicehash, bool tls) : + m_nicehash(nicehash), + m_tls(tls), + m_keepAlive(keepAlive), + m_port(port), + m_host(host), + m_password(password), + m_user(user) +{ + 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; +} + + +bool Pool::isCompatible(const xmrig::Algorithm &algorithm) const +{ + if (m_algorithms.empty()) { + return true; + } + + for (const auto &a : m_algorithms) { + if (algorithm == a) { + return true; + } + } + +# ifdef XMRIG_PROXY_PROJECT + if (m_algorithm.algo() == xmrig::CRYPTONIGHT && algorithm.algo() == xmrig::CRYPTONIGHT) { + return m_algorithm.variant() == xmrig::VARIANT_XTL || m_algorithm.variant() == xmrig::VARIANT_MSR; + } +# endif + + return false; +} + + +bool Pool::isEqual(const Pool &other) const +{ + return (m_nicehash == other.m_nicehash + && m_tls == other.m_tls + && 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); +} + + +bool Pool::parse(const char *url) +{ + assert(url != nullptr); + + const char *p = strstr(url, "://"); + const char *base = url; + + if (p) { + if (strncasecmp(url, "stratum+tcp://", 14) == 0) { + m_tls = false; + } + else if (strncasecmp(url, "stratum+ssl://", 14) == 0) { + m_tls = true; + } + else { + return false; + } + + base = url + 14; + } + + 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 = 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; +} + + +bool Pool::setUserpass(const char *userpass) +{ + const char *p = strchr(userpass, ':'); + if (!p) { + return false; + } + + char *user = new char[p - userpass + 1](); + strncpy(user, userpass, p - userpass); + + m_user = user; + m_password = p + 1; + + return true; +} + + +rapidjson::Value Pool::toJSON(rapidjson::Document &doc) const +{ + using namespace rapidjson; + + auto &allocator = doc.GetAllocator(); + + Value obj(kObjectType); + + obj.AddMember("url", m_url.toJSON(), allocator); + obj.AddMember("user", m_user.toJSON(), allocator); + obj.AddMember("pass", m_password.toJSON(), allocator); + obj.AddMember("rig-id", m_rigId.toJSON(), allocator); + +# ifndef XMRIG_PROXY_PROJECT + obj.AddMember("nicehash", isNicehash(), allocator); +# endif + + if (m_keepAlive == 0 || m_keepAlive == kKeepAliveTimeout) { + obj.AddMember("keepalive", m_keepAlive > 0, allocator); + } + else { + obj.AddMember("keepalive", m_keepAlive, allocator); + } + + switch (m_algorithm.variant()) { + case xmrig::VARIANT_AUTO: + case xmrig::VARIANT_0: + case xmrig::VARIANT_1: + obj.AddMember("variant", m_algorithm.variant(), allocator); + break; + + case xmrig::VARIANT_2: + obj.AddMember("variant", 2, allocator); + break; + + default: + obj.AddMember("variant", StringRef(m_algorithm.variantName()), allocator); + break; + } + + obj.AddMember("tls", isTLS(), allocator); + obj.AddMember("tls-fingerprint", m_fingerprint.toJSON(), allocator); + + return obj; +} + + +void Pool::adjust(const xmrig::Algorithm &algorithm) +{ + if (!isValid()) { + return; + } + + if (!m_algorithm.isValid()) { + m_algorithm.setAlgo(algorithm.algo()); + adjustVariant(algorithm.variant()); + } + + rebuild(); +} + + +void Pool::setAlgo(const xmrig::Algorithm &algorithm) +{ + m_algorithm = algorithm; + + rebuild(); +} + + +#ifdef APP_DEBUG +void 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_nicehash)); + LOG_DEBUG ("keepAlive: %d", m_keepAlive); +} +#endif + + +bool 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 = 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; +} + + +void Pool::addVariant(xmrig::Variant variant) +{ + const xmrig::Algorithm algorithm(m_algorithm.algo(), variant); + if (!algorithm.isValid() || m_algorithm == algorithm) { + return; + } + + m_algorithms.push_back(algorithm); +} + + +void Pool::adjustVariant(const xmrig::Variant variantHint) +{ +# ifndef XMRIG_PROXY_PROJECT + using namespace xmrig; + + if (m_host.contains(".nicehash.com")) { + m_keepAlive = false; + m_nicehash = true; + bool valid = true; + + switch (m_port) { + case 3355: + case 33355: + valid = m_algorithm.algo() == CRYPTONIGHT && m_host.contains("cryptonight."); + m_algorithm.setVariant(VARIANT_0); + break; + + case 3363: + case 33363: + valid = m_algorithm.algo() == CRYPTONIGHT && m_host.contains("cryptonightv7."); + m_algorithm.setVariant(VARIANT_1); + break; + + case 3364: + valid = m_algorithm.algo() == CRYPTONIGHT_HEAVY && m_host.contains("cryptonightheavy."); + m_algorithm.setVariant(VARIANT_0); + break; + + case 3367: + case 33367: + valid = m_algorithm.algo() == CRYPTONIGHT && m_host.contains("cryptonightv8."); + m_algorithm.setVariant(VARIANT_2); + break; + + default: + break; + } + + if (!valid) { + m_algorithm.setAlgo(INVALID_ALGO); + } + + m_tls = m_port > 33000; + return; + } + + if (m_host.contains(".minergate.com")) { + m_keepAlive = false; + bool valid = true; + m_algorithm.setVariant(VARIANT_1); + + if (m_host.contains("xmr.pool.")) { + valid = m_algorithm.algo() == CRYPTONIGHT; + m_algorithm.setVariant(m_port == 45700 ? VARIANT_AUTO : VARIANT_0); + } + else if (m_host.contains("aeon.pool.") && m_port == 45690) { + valid = m_algorithm.algo() == CRYPTONIGHT_LITE; + m_algorithm.setVariant(VARIANT_1); + } + + if (!valid) { + m_algorithm.setAlgo(INVALID_ALGO); + } + + return; + } + + if (variantHint != VARIANT_AUTO) { + m_algorithm.setVariant(variantHint); + return; + } + + if (m_algorithm.variant() != VARIANT_AUTO) { + return; + } + + if (m_algorithm.algo() == CRYPTONIGHT_HEAVY) { + m_algorithm.setVariant(VARIANT_0); + } + else if (m_algorithm.algo() == CRYPTONIGHT_LITE) { + m_algorithm.setVariant(VARIANT_1); + } +# endif +} + + +void Pool::rebuild() +{ + m_algorithms.clear(); + + if (!m_algorithm.isValid()) { + return; + } + + m_algorithms.push_back(m_algorithm); + +# ifndef XMRIG_PROXY_PROJECT + addVariant(xmrig::VARIANT_2); + addVariant(xmrig::VARIANT_1); + addVariant(xmrig::VARIANT_0); + addVariant(xmrig::VARIANT_XTL); + addVariant(xmrig::VARIANT_TUBE); + addVariant(xmrig::VARIANT_MSR); + addVariant(xmrig::VARIANT_XHV); + addVariant(xmrig::VARIANT_XAO); + addVariant(xmrig::VARIANT_RTO); + addVariant(xmrig::VARIANT_XFH); + addVariant(xmrig::VARIANT_FAST_2); + addVariant(xmrig::VARIANT_UPX); + addVariant(xmrig::VARIANT_TURTLE); + addVariant(xmrig::VARIANT_GPU); + addVariant(xmrig::VARIANT_AUTO); +# endif +} diff --git a/src/common/net/Pool.h b/src/common/net/Pool.h new file mode 100644 index 00000000..c051b0ee --- /dev/null +++ b/src/common/net/Pool.h @@ -0,0 +1,116 @@ +/* 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 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 "common/crypto/Algorithm.h" +#include "common/utils/c_str.h" +#include "rapidjson/fwd.h" + + +class Pool +{ +public: + constexpr static const char *kDefaultPassword = "x"; + constexpr static const char *kDefaultUser = "x"; + constexpr static uint16_t kDefaultPort = 3333; + constexpr static int kKeepAliveTimeout = 60; + + Pool(); + Pool(const char *url); + 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 isNicehash() const { return m_nicehash; } + inline bool isTLS() const { return m_tls; } + inline bool isValid() const { return !m_host.isNull() && m_port > 0; } + inline const char *fingerprint() const { return m_fingerprint.data(); } + inline const char *host() const { return m_host.data(); } + inline const char *password() const { return !m_password.isNull() ? m_password.data() : kDefaultPassword; } + inline const char *rigId() const { return m_rigId.data(); } + inline const char *url() const { return m_url.data(); } + inline const char *user() const { return !m_user.isNull() ? m_user.data() : kDefaultUser; } + inline const xmrig::Algorithm &algorithm() const { return m_algorithm; } + inline const xmrig::Algorithms &algorithms() const { return m_algorithms; } + inline int keepAlive() const { return m_keepAlive; } + inline uint16_t port() const { return m_port; } + inline void setFingerprint(const char *fingerprint) { m_fingerprint = fingerprint; } + inline void setKeepAlive(int keepAlive) { m_keepAlive = keepAlive >= 0 ? keepAlive : 0; } + inline void setNicehash(bool nicehash) { m_nicehash = nicehash; } + inline void setPassword(const char *password) { m_password = password; } + inline void setRigId(const char *rigId) { m_rigId = rigId; } + inline void setTLS(bool tls) { m_tls = tls; } + inline void setUser(const char *user) { m_user = user; } + inline xmrig::Algorithm &algorithm() { return m_algorithm; } + + inline bool operator!=(const Pool &other) const { return !isEqual(other); } + inline bool operator==(const Pool &other) const { return isEqual(other); } + + bool isCompatible(const xmrig::Algorithm &algorithm) const; + bool isEqual(const Pool &other) const; + bool parse(const char *url); + bool setUserpass(const char *userpass); + rapidjson::Value toJSON(rapidjson::Document &doc) const; + void adjust(const xmrig::Algorithm &algorithm); + void setAlgo(const xmrig::Algorithm &algorithm); + +# ifdef APP_DEBUG + void print() const; +# endif + +private: + bool parseIPv6(const char *addr); + void addVariant(xmrig::Variant variant); + void adjustVariant(const xmrig::Variant variantHint); + void rebuild(); + + bool m_nicehash; + bool m_tls; + int m_keepAlive; + uint16_t m_port; + xmrig::Algorithm m_algorithm; + xmrig::Algorithms m_algorithms; + xmrig::c_str m_fingerprint; + xmrig::c_str m_host; + xmrig::c_str m_password; + xmrig::c_str m_rigId; + xmrig::c_str m_url; + xmrig::c_str m_user; +}; + + +typedef std::vector Pools; + +#endif /* XMRIG_POOL_H */ diff --git a/src/common/xmrig.h b/src/common/xmrig.h index 33650a64..7645c344 100644 --- a/src/common/xmrig.h +++ b/src/common/xmrig.h @@ -78,6 +78,8 @@ enum Variant { VARIANT_GPU = 13, // CryptoNight-GPU (Ryo) VARIANT_WOW = 14, // CryptoNightR (Wownero) VARIANT_4 = 15, // CryptoNightR + VARIANT_XCASH = 16, // Cryptonight (2) variant xcash + VARIANT_ZELERIUS = 17, // Cryptonight (2) variant zelerius VARIANT_MAX }; diff --git a/src/crypto/CryptoNight.cpp b/src/crypto/CryptoNight.cpp index 6ff02f54..9c7b7aa2 100644 --- a/src/crypto/CryptoNight.cpp +++ b/src/crypto/CryptoNight.cpp @@ -209,6 +209,12 @@ CryptoNight::cn_hash_fun CryptoNight::fn(xmrig::Algo algorithm, xmrig::AlgoVerif # endif cryptonight_single_hash, + cryptonight_single_hash, + cryptonight_single_hash, + + cryptonight_single_hash, + cryptonight_single_hash, + # ifndef XMRIG_NO_AEON cryptonight_single_hash, cryptonight_single_hash, @@ -233,6 +239,8 @@ CryptoNight::cn_hash_fun CryptoNight::fn(xmrig::Algo algorithm, xmrig::AlgoVerif nullptr, nullptr, // VARIANT_GPU nullptr, nullptr, // VARIANT_WOW nullptr, nullptr, // VARIANT_4 + nullptr, nullptr, // VARIANT_XCASH + nullptr, nullptr, // VARIANT_ZELERIUS # else nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, @@ -242,6 +250,7 @@ CryptoNight::cn_hash_fun CryptoNight::fn(xmrig::Algo algorithm, xmrig::AlgoVerif nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, # endif # ifndef XMRIG_NO_SUMO @@ -269,6 +278,8 @@ CryptoNight::cn_hash_fun CryptoNight::fn(xmrig::Algo algorithm, xmrig::AlgoVerif nullptr, nullptr, // VARIANT_GPU nullptr, nullptr, // VARIANT_WOW nullptr, nullptr, // VARIANT_4 + nullptr, nullptr, // VARIANT_XCASH + nullptr, nullptr, // VARIANT_ZELERIUS # else nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, @@ -278,6 +289,7 @@ CryptoNight::cn_hash_fun CryptoNight::fn(xmrig::Algo algorithm, xmrig::AlgoVerif nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, # endif # ifndef XMRIG_NO_CN_ULTRALITE @@ -304,7 +316,10 @@ CryptoNight::cn_hash_fun CryptoNight::fn(xmrig::Algo algorithm, xmrig::AlgoVerif nullptr, nullptr, // VARIANT_GPU nullptr, nullptr, // VARIANT_WOW nullptr, nullptr, // VARIANT_4 - #else + nullptr, nullptr, // VARIANT_XCASH + nullptr, nullptr, // VARIANT_ZELERIUS +#else + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, @@ -359,7 +374,9 @@ bool CryptoNight::selfTest() { # ifndef XMRIG_NO_CN_GPU verify(VARIANT_GPU, test_output_gpu) && # endif - verify(VARIANT_FAST_2, test_output_xtl_v9); + verify(VARIANT_FAST_2, test_output_xtl_v9) && + verify(VARIANT_XCASH, test_output_xcash) && + verify(VARIANT_ZELERIUS, test_output_zelerius); } # ifndef XMRIG_NO_AEON diff --git a/src/crypto/CryptoNight_constants.h b/src/crypto/CryptoNight_constants.h index e6cd6359..e86ee9c8 100644 --- a/src/crypto/CryptoNight_constants.h +++ b/src/crypto/CryptoNight_constants.h @@ -43,6 +43,8 @@ constexpr const uint32_t CRYPTONIGHT_XAO_ITER = 0x100000; constexpr const uint32_t CRYPTONIGHT_XFH_ITER = 0x20000; constexpr const uint32_t CRYPTONIGHT_LITE_UPX_ITER = 0x20000; constexpr const uint32_t CRYPTONIGHT_TRTL_ITER = 0x10000; +constexpr const uint32_t CRYPTONIGHT_XCASH_ITER = 0x100000; +constexpr const uint32_t CRYPTONIGHT_ZELERIUS_ITER = 0x60000; constexpr const size_t CRYPTONIGHT_MEMORY = 2 * 1024 * 1024; constexpr const uint32_t CRYPTONIGHT_MASK = 0x1FFFF0; @@ -132,7 +134,9 @@ template<> inline constexpr uint32_t cn_select_iter() template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_XAO_ITER; } template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_ITER; } template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_XFH_ITER; } -template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_HALF_ITER; } +template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_HALF_ITER; } +template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_XCASH_ITER; } +template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_ZELERIUS_ITER; } template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_HALF_ITER; } template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_HALF_ITER; } template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_LITE_UPX_ITER; } @@ -162,6 +166,11 @@ inline uint32_t cn_select_iter(Algo algorithm, Variant variant) case VARIANT_UPX: return CRYPTONIGHT_LITE_UPX_ITER; + case VARIANT_XCASH: + return CRYPTONIGHT_XCASH_ITER; + + case VARIANT_ZELERIUS: + return CRYPTONIGHT_ZELERIUS_ITER; default: break; } @@ -205,6 +214,8 @@ template<> inline constexpr Variant cn_base_variant(){ return VA template<> inline constexpr Variant cn_base_variant() { return VARIANT_GPU; } template<> inline constexpr Variant cn_base_variant() { return VARIANT_2; } template<> inline constexpr Variant cn_base_variant() { return VARIANT_2; } +template<> inline constexpr Variant cn_base_variant() { return VARIANT_2; } +template<> inline constexpr Variant cn_base_variant() { return VARIANT_2; } template inline constexpr bool cn_is_cryptonight_r() { return false; } diff --git a/src/crypto/CryptoNight_test.h b/src/crypto/CryptoNight_test.h index 6724882c..4c9ed5c9 100644 --- a/src/crypto/CryptoNight_test.h +++ b/src/crypto/CryptoNight_test.h @@ -211,6 +211,19 @@ const static uint8_t test_output_xtl_v9[32] = { 0xC8, 0x4A, 0x0E, 0x39, 0x7C, 0x86, 0x84, 0x56, 0x89, 0x5C, 0x3F, 0x29, 0xBE, 0x73, 0x12, 0xA7 }; + +// 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 +}; + #ifndef XMRIG_NO_AEON // "cn-lite/0" const static uint8_t test_output_v0_lite[160] = { From 054e3e0ed378e6ec5099d856d45ec3ae81e08730 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Wed, 6 Mar 2019 21:30:41 +0100 Subject: [PATCH 87/97] Integrated RWZ (Graft) algo --- src/amd/OclGPU.cpp | 19 +- src/amd/opencl/cryptonight.cl | 143 ++++- src/base/net/Pool.cpp | 3 +- src/common/crypto/Algorithm.cpp | 30 +- src/common/net/Job.cpp | 14 +- src/common/xmrig.h | 5 +- src/crypto/CryptoNight.cpp | 22 +- src/crypto/CryptoNight_constants.h | 36 +- src/crypto/CryptoNight_monero.h | 46 +- src/crypto/CryptoNight_test.h | 12 +- src/crypto/CryptoNight_x86.h | 18 +- src/version.h | 6 +- xmrig.cbp | 829 +++++++++++++++++++++++++++++ 13 files changed, 1103 insertions(+), 80 deletions(-) create mode 100644 xmrig.cbp diff --git a/src/amd/OclGPU.cpp b/src/amd/OclGPU.cpp index f685037a..857adbdf 100644 --- a/src/amd/OclGPU.cpp +++ b/src/amd/OclGPU.cpp @@ -71,7 +71,7 @@ inline static int cn0KernelOffset(xmrig::Variant variant) { # ifndef XMRIG_NO_CN_GPU if (variant == xmrig::VARIANT_GPU) { - return 17; + return 18; } # endif @@ -112,20 +112,27 @@ inline static int cn1KernelOffset(xmrig::Variant variant) case xmrig::VARIANT_UPX: return 14; - case xmrig::VARIANT_XCASH: + case xmrig::VARIANT_RWZ: return 15; case xmrig::VARIANT_ZELERIUS: return 16; + case xmrig::VARIANT_DOUBLE: + return 17; + + // 18, 19 is used by cn0 and cn00 of Variant GPU + # ifndef XMRIG_NO_CN_GPU case xmrig::VARIANT_GPU: - return 19; + return 20; # endif + // 21 is by for cn2 Variant GPU + case xmrig::VARIANT_WOW: case xmrig::VARIANT_4: - return 21; + return 22; default: break; @@ -140,7 +147,7 @@ inline static int cn2KernelOffset(xmrig::Variant variant) { # ifndef XMRIG_NO_CN_GPU if (variant == xmrig::VARIANT_GPU) { - return 20; + return 21; } # endif @@ -258,7 +265,7 @@ size_t InitOpenCLGpu(int index, cl_context opencl_ctx, GpuContext* ctx, const ch const char *KernelNames[] = { "cn0", "cn1", "cn2", "Blake", "Groestl", "JH", "Skein", - "cn1_monero", "cn1_msr", "cn1_xao", "cn1_tube", "cn1_v2_monero", "cn1_xfh", "cn1_fastv2", "cn1_upx", "cn1_xcash", "cn1_zelerius", + "cn1_monero", "cn1_msr", "cn1_xao", "cn1_tube", "cn1_v2_monero", "cn1_xfh", "cn1_fastv2", "cn1_upx", "cn1_rwz", "cn1_zelerius", "cn1_double", # ifndef XMRIG_NO_CN_GPU "cn0_cn_gpu", "cn00_cn_gpu", "cn1_cn_gpu", "cn2_cn_gpu", # endif diff --git a/src/amd/opencl/cryptonight.cl b/src/amd/opencl/cryptonight.cl index de079c58..225dd273 100644 --- a/src/amd/opencl/cryptonight.cl +++ b/src/amd/opencl/cryptonight.cl @@ -104,12 +104,13 @@ XMRIG_INCLUDE_FAST_DIV_HEAVY #define VARIANT_RTO 7 // Modified CryptoNight variant 1 (Arto only) #define VARIANT_2 8 // CryptoNight variant 2 #define VARIANT_XFH 9 // CryptoNight variant xfh aka cn-heavy-superfast -#define VARIANT_FAST_2 10 // CryptoNight variant 2 with half iterations (Masari/Stellite) -#define VARIANT_TURTLE 11 // CryptoNight Turtle (TRTL) -#define VARIANT_UPX 12 // CryptoNight UPX -#define VARIANT_GPU 13 // CryptoNight-GPU (Ryo) -#define VARIANT_XCASH = 16 // Cryptonight (2) variant xcash -#define VARIANT_ZELERIUS = 17 // Cryptonight (2) variant zelerius +#define VARIANT_FAST_2 10 // CryptoNight variant 2 with half iterations (Masari/Stellite) +#define VARIANT_TURTLE 11 // CryptoNight Turtle (TRTL) +#define VARIANT_UPX 12 // CryptoNight UPX +#define VARIANT_GPU 13 // CryptoNight-GPU (Ryo) +#define VARIANT_RWZ = 16, // Cryptonight (2) variant 3/4 Iterations + reverse shuffle rwz (Graft) +#define VARIANT_ZELERIUS = 17, // Cryptonight (2) variant 3/4 Iterations (Zelerius) +#define VARIANT_DOUBLE = 18, // Cryptonight (2) variant double (XCash) #define CRYPTONIGHT 0 /* CryptoNight (2 MB) */ #define CRYPTONIGHT_LITE 1 /* CryptoNight (1 MB) */ @@ -1108,7 +1109,7 @@ __kernel void cn1_fastv2(__global uint4 *Scratchpad, __global ulong *states, uin )===" R"===( __attribute__((reqd_work_group_size(WORKSIZE, 1, 1))) -__kernel void cn1_xcash(__global uint4 *Scratchpad, __global ulong *states, uint variant, __global ulong *input, uint Threads) +__kernel void cn1_double(__global uint4 *Scratchpad, __global ulong *states, uint variant, __global ulong *input, uint Threads) { # if (ALGO == CRYPTONIGHT) ulong a[2], b[4]; @@ -1264,6 +1265,134 @@ __kernel void cn1_xcash(__global uint4 *Scratchpad, __global ulong *states, uint # endif } +)===" +R"===( +__attribute__((reqd_work_group_size(WORKSIZE, 1, 1))) +__kernel void cn1_rwz(__global uint4 *Scratchpad, __global ulong *states, uint variant, __global ulong *input, uint Threads) +{ +# if (ALGO == CRYPTONIGHT) + ulong a[2], b[4]; + __local uint AES0[256], AES1[256], AES2[256], AES3[256]; + const ulong gIdx = getIdx(); + for(int i = get_local_id(0); i < 256; i += WORKSIZE) + { + const uint tmp = AES0_C[i]; + AES0[i] = tmp; + AES1[i] = rotate(tmp, 8U); + AES2[i] = rotate(tmp, 16U); + AES3[i] = rotate(tmp, 24U); + } + barrier(CLK_LOCAL_MEM_FENCE); +# if (COMP_MODE == 1) + // do not use early return here + if (gIdx < Threads) +# endif + { + states += 25 * gIdx; +# if defined(__NV_CL_C_VERSION) + Scratchpad += gIdx * (0x40000 >> 2); +# else +# if (STRIDED_INDEX == 0) + Scratchpad += gIdx * (MEMORY >> 4); +# elif (STRIDED_INDEX == 1) + Scratchpad += gIdx; +# elif (STRIDED_INDEX == 2) + Scratchpad += get_group_id(0) * (MEMORY >> 4) * WORKSIZE + MEM_CHUNK * get_local_id(0); +# endif +# endif + a[0] = states[0] ^ states[4]; + a[1] = states[1] ^ states[5]; + b[0] = states[2] ^ states[6]; + b[1] = states[3] ^ states[7]; + b[2] = states[8] ^ states[10]; + b[3] = states[9] ^ states[11]; + } + ulong2 bx0 = ((ulong2 *)b)[0]; + ulong2 bx1 = ((ulong2 *)b)[1]; + mem_fence(CLK_LOCAL_MEM_FENCE); +# ifdef __NV_CL_C_VERSION + __local uint16 scratchpad_line_buf[WORKSIZE]; + __local uint16* scratchpad_line = scratchpad_line_buf + get_local_id(0); +# define SCRATCHPAD_CHUNK(N) (*(__local uint4*)((__local uchar*)(scratchpad_line) + (idx1 ^ (N << 4)))) +# else +# if (STRIDED_INDEX == 0) +# define SCRATCHPAD_CHUNK(N) (*(__global uint4*)((__global uchar*)(Scratchpad) + (idx ^ (N << 4)))) +# elif (STRIDED_INDEX == 1) +# define SCRATCHPAD_CHUNK(N) (*(__global uint4*)((__global uchar*)(Scratchpad) + mul24(as_uint(idx ^ (N << 4)), Threads))) +# elif (STRIDED_INDEX == 2) +# define SCRATCHPAD_CHUNK(N) (*(__global uint4*)((__global uchar*)(Scratchpad) + (((idx ^ (N << 4)) % (MEM_CHUNK << 4)) + ((idx ^ (N << 4)) / (MEM_CHUNK << 4)) * WORKSIZE * (MEM_CHUNK << 4)))) +# endif +# endif +# if (COMP_MODE == 1) + // do not use early return here + if (gIdx < Threads) +# endif + { + uint2 division_result = as_uint2(states[12]); + uint sqrt_result = as_uint2(states[13]).s0; + #pragma unroll UNROLL_FACTOR + for(int i = 0; i < 0x60000; ++i) + { +# ifdef __NV_CL_C_VERSION + uint idx = a[0] & 0x1FFFC0; + uint idx1 = a[0] & 0x30; + *scratchpad_line = *(__global uint16*)((__global uchar*)(Scratchpad) + idx); +# else + uint idx = a[0] & MASK; +# endif + uint4 c = SCRATCHPAD_CHUNK(0); + c = AES_Round(AES0, AES1, AES2, AES3, c, ((uint4 *)a)[0]); + { + const ulong2 chunk1 = as_ulong2(SCRATCHPAD_CHUNK(3)); + const ulong2 chunk2 = as_ulong2(SCRATCHPAD_CHUNK(2)); + const ulong2 chunk3 = as_ulong2(SCRATCHPAD_CHUNK(1)); + SCRATCHPAD_CHUNK(1) = as_uint4(chunk3 + bx1); + SCRATCHPAD_CHUNK(2) = as_uint4(chunk1 + bx0); + SCRATCHPAD_CHUNK(3) = as_uint4(chunk2 + ((ulong2 *)a)[0]); + } + SCRATCHPAD_CHUNK(0) = as_uint4(bx0) ^ c; +# ifdef __NV_CL_C_VERSION + *(__global uint16*)((__global uchar*)(Scratchpad) + idx) = *scratchpad_line; + idx = as_ulong2(c).s0 & 0x1FFFC0; + idx1 = as_ulong2(c).s0 & 0x30; + *scratchpad_line = *(__global uint16*)((__global uchar*)(Scratchpad) + idx); +# else + idx = as_ulong2(c).s0 & MASK; +# endif + uint4 tmp = SCRATCHPAD_CHUNK(0); + { + tmp.s0 ^= division_result.s0; + tmp.s1 ^= division_result.s1 ^ sqrt_result; + division_result = fast_div_v2(as_ulong2(c).s1, (c.s0 + (sqrt_result << 1)) | 0x80000001UL); + sqrt_result = fast_sqrt_v2(as_ulong2(c).s0 + as_ulong(division_result)); + } + ulong2 t; + t.s0 = mul_hi(as_ulong2(c).s0, as_ulong2(tmp).s0); + t.s1 = as_ulong2(c).s0 * as_ulong2(tmp).s0; + { + const ulong2 chunk1 = as_ulong2(SCRATCHPAD_CHUNK(1)) ^ t; + const ulong2 chunk2 = as_ulong2(SCRATCHPAD_CHUNK(2)); + t ^= chunk2; + const ulong2 chunk3 = as_ulong2(SCRATCHPAD_CHUNK(3)); + SCRATCHPAD_CHUNK(1) = as_uint4(chunk1 + bx1); + SCRATCHPAD_CHUNK(2) = as_uint4(chunk3 + bx0); + SCRATCHPAD_CHUNK(3) = as_uint4(chunk2 + ((ulong2 *)a)[0]); + } + a[1] += t.s1; + a[0] += t.s0; + SCRATCHPAD_CHUNK(0) = ((uint4 *)a)[0]; +# ifdef __NV_CL_C_VERSION + *(__global uint16*)((__global uchar*)(Scratchpad) + idx) = *scratchpad_line; +# endif + ((uint4 *)a)[0] ^= tmp; + bx1 = bx0; + bx0 = as_ulong2(c); + } +# undef SCRATCHPAD_CHUNK + } + mem_fence(CLK_GLOBAL_MEM_FENCE); +# endif +} )===" R"===( diff --git a/src/base/net/Pool.cpp b/src/base/net/Pool.cpp index eb448d5a..cd79dd22 100644 --- a/src/base/net/Pool.cpp +++ b/src/base/net/Pool.cpp @@ -501,8 +501,9 @@ void xmrig::Pool::rebuild() addVariant(VARIANT_TURTLE); addVariant(VARIANT_XFH); addVariant(VARIANT_UPX); - addVariant(VARIANT_XCASH); + addVariant(VARIANT_RWZ); addVariant(VARIANT_ZELERIUS); + addVariant(VARIANT_DOUBLE); addVariant(VARIANT_AUTO); diff --git a/src/common/crypto/Algorithm.cpp b/src/common/crypto/Algorithm.cpp index cb4677f1..25c79d91 100644 --- a/src/common/crypto/Algorithm.cpp +++ b/src/common/crypto/Algorithm.cpp @@ -63,17 +63,20 @@ static AlgoData const algorithms[] = { { "cryptonight/2", "cn/2", xmrig::CRYPTONIGHT, xmrig::VARIANT_2 }, { "cryptonight/xfh", "cn/xfh", xmrig::CRYPTONIGHT, xmrig::VARIANT_XFH }, { "cryptonight/swap", "cn/swap", xmrig::CRYPTONIGHT, xmrig::VARIANT_XFH }, - { "cryptonight/fast2", "cn/fast2", xmrig::CRYPTONIGHT, xmrig::VARIANT_FAST_2 }, - { "cryptonight/xtlv9", "cn/xtlv9", xmrig::CRYPTONIGHT, xmrig::VARIANT_FAST_2 }, - { "cryptonight/half", "cn/half", xmrig::CRYPTONIGHT, xmrig::VARIANT_FAST_2 }, + { "cryptonight/fast2", "cn/fast2", xmrig::CRYPTONIGHT, xmrig::VARIANT_FAST_2}, + { "cryptonight/xtlv9", "cn/xtlv9", xmrig::CRYPTONIGHT, xmrig::VARIANT_FAST_2}, + { "cryptonight/half", "cn/half", xmrig::CRYPTONIGHT, xmrig::VARIANT_FAST_2}, { "cryptonight/hosp", "cn/hosp", xmrig::CRYPTONIGHT, xmrig::VARIANT_RTO }, { "cryptonight/hospital", "cn/hospital", xmrig::CRYPTONIGHT, xmrig::VARIANT_RTO }, { "cryptonight/wow", "cn/wow", xmrig::CRYPTONIGHT, xmrig::VARIANT_WOW }, { "cryptonight/r", "cn/r", xmrig::CRYPTONIGHT, xmrig::VARIANT_4 }, - { "cryptonight/xcash", "cn/xcash", xmrig::CRYPTONIGHT, xmrig::VARIANT_XCASH}, - { "cryptonight/heavyx", "cn/heavyx", xmrig::CRYPTONIGHT, xmrig::VARIANT_XCASH}, + { "cryptonight/rwz", "cn/rwz", xmrig::CRYPTONIGHT, xmrig::VARIANT_RWZ }, + { "cryptonight/graft", "cn/graft", xmrig::CRYPTONIGHT, xmrig::VARIANT_RWZ }, { "cryptonight/zelerius", "cn/zelerius", xmrig::CRYPTONIGHT, xmrig::VARIANT_ZELERIUS}, - { "cryptonight/zlx", "cn/zlx", xmrig::CRYPTONIGHT, xmrig::VARIANT_ZELERIUS}, + { "cryptonight/zls", "cn/zls", xmrig::CRYPTONIGHT, xmrig::VARIANT_ZELERIUS}, + { "cryptonight/double", "cn/double", xmrig::CRYPTONIGHT, xmrig::VARIANT_DOUBLE}, + { "cryptonight/heavyx", "cn/heavyx", xmrig::CRYPTONIGHT, xmrig::VARIANT_DOUBLE}, + { "cryptonight/xcash", "cn/xcash", xmrig::CRYPTONIGHT, xmrig::VARIANT_DOUBLE}, # ifndef XMRIG_NO_AEON { "cryptonight-lite", "cn-lite", xmrig::CRYPTONIGHT_LITE, xmrig::VARIANT_AUTO }, @@ -146,8 +149,9 @@ static const char *variants[] = { "gpu", "wow", "r", - "xcash", - "zelerius" + "rwz", + "zelerius", + "double" }; @@ -248,12 +252,16 @@ void xmrig::Algorithm::parseVariant(const char *variant) m_variant = VARIANT_FAST_2; } - if (strcasecmp(variant, "zlx") == 0 || strcasecmp(variant, "zelerius") == 0) { + if (strcasecmp(variant, "zlx") == 0 || strcasecmp(variant, "zelerius") == 0 || strcasecmp(variant, "zls") == 0) { m_variant = VARIANT_ZELERIUS; } - if (strcasecmp(variant, "xcash") == 0 || strcasecmp(variant, "heavyx") == 0) { - m_variant = VARIANT_XCASH; + if (strcasecmp(variant, "xcash") == 0 || strcasecmp(variant, "heavyx") == 0 || strcasecmp(variant, "double") == 0) { + m_variant = VARIANT_DOUBLE; + } + + if (strcasecmp(variant, "rwz") == 0 || strcasecmp(variant, "graft") == 0) { + m_variant = VARIANT_RWZ; } } diff --git a/src/common/net/Job.cpp b/src/common/net/Job.cpp index 51f02bfa..682b9ffb 100644 --- a/src/common/net/Job.cpp +++ b/src/common/net/Job.cpp @@ -130,15 +130,21 @@ bool xmrig::Job::setBlob(const char *blob) } if (!m_algorithm.isForced()) { - if (m_algorithm.variant() == xmrig::VARIANT_XTL && m_blob[0] >= 9) { - m_algorithm.setVariant(xmrig::VARIANT_FAST_2); + if (m_algorithm.variant() == VARIANT_XTL && m_blob[0] >= 9) { + m_algorithm.setVariant(VARIANT_FAST_2); } - else if (m_algorithm.variant() == xmrig::VARIANT_MSR && m_blob[0] >= 8) { - m_algorithm.setVariant(xmrig::VARIANT_FAST_2); + else if (m_algorithm.variant() == VARIANT_MSR && m_blob[0] >= 8) { + m_algorithm.setVariant(VARIANT_FAST_2); } else if (m_algorithm.variant() == VARIANT_WOW && m_blob[0] < 11) { m_algorithm.setVariant(VARIANT_2); } + else if (m_algorithm.variant() == VARIANT_RWZ && m_blob[0] < 12) { + m_algorithm.setVariant(VARIANT_2); + } + else if (m_algorithm.variant() == VARIANT_ZELERIUS && m_blob[0] < 8) { + m_algorithm.setVariant(VARIANT_2); + } } # ifdef XMRIG_PROXY_PROJECT diff --git a/src/common/xmrig.h b/src/common/xmrig.h index 7645c344..4d71cae2 100644 --- a/src/common/xmrig.h +++ b/src/common/xmrig.h @@ -78,8 +78,9 @@ enum Variant { VARIANT_GPU = 13, // CryptoNight-GPU (Ryo) VARIANT_WOW = 14, // CryptoNightR (Wownero) VARIANT_4 = 15, // CryptoNightR - VARIANT_XCASH = 16, // Cryptonight (2) variant xcash - VARIANT_ZELERIUS = 17, // Cryptonight (2) variant zelerius + VARIANT_RWZ = 16, // Cryptonight (2) variant 3/4 Iterations + reverse shuffle rwz (Graft) + VARIANT_ZELERIUS = 17, // Cryptonight (2) variant 3/4 Iterations (Zelerius) + VARIANT_DOUBLE = 18, // Cryptonight (2) variant double (XCash) VARIANT_MAX }; diff --git a/src/crypto/CryptoNight.cpp b/src/crypto/CryptoNight.cpp index 9c7b7aa2..c80936be 100644 --- a/src/crypto/CryptoNight.cpp +++ b/src/crypto/CryptoNight.cpp @@ -209,12 +209,15 @@ CryptoNight::cn_hash_fun CryptoNight::fn(xmrig::Algo algorithm, xmrig::AlgoVerif # endif cryptonight_single_hash, - cryptonight_single_hash, - cryptonight_single_hash, + cryptonight_single_hash, + cryptonight_single_hash, cryptonight_single_hash, cryptonight_single_hash, + cryptonight_single_hash, + cryptonight_single_hash, + # ifndef XMRIG_NO_AEON cryptonight_single_hash, cryptonight_single_hash, @@ -239,8 +242,9 @@ CryptoNight::cn_hash_fun CryptoNight::fn(xmrig::Algo algorithm, xmrig::AlgoVerif nullptr, nullptr, // VARIANT_GPU nullptr, nullptr, // VARIANT_WOW nullptr, nullptr, // VARIANT_4 - nullptr, nullptr, // VARIANT_XCASH + nullptr, nullptr, // VARIANT_RWZ nullptr, nullptr, // VARIANT_ZELERIUS + nullptr, nullptr, // VARIANT_DOUBLE # else nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, @@ -251,6 +255,7 @@ CryptoNight::cn_hash_fun CryptoNight::fn(xmrig::Algo algorithm, xmrig::AlgoVerif nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, # endif # ifndef XMRIG_NO_SUMO @@ -278,8 +283,9 @@ CryptoNight::cn_hash_fun CryptoNight::fn(xmrig::Algo algorithm, xmrig::AlgoVerif nullptr, nullptr, // VARIANT_GPU nullptr, nullptr, // VARIANT_WOW nullptr, nullptr, // VARIANT_4 - nullptr, nullptr, // VARIANT_XCASH + nullptr, nullptr, // VARIANT_RWZ nullptr, nullptr, // VARIANT_ZELERIUS + nullptr, nullptr, // VARIANT_DOUBLE # else nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, @@ -290,6 +296,7 @@ CryptoNight::cn_hash_fun CryptoNight::fn(xmrig::Algo algorithm, xmrig::AlgoVerif nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, # endif # ifndef XMRIG_NO_CN_ULTRALITE @@ -316,8 +323,9 @@ CryptoNight::cn_hash_fun CryptoNight::fn(xmrig::Algo algorithm, xmrig::AlgoVerif nullptr, nullptr, // VARIANT_GPU nullptr, nullptr, // VARIANT_WOW nullptr, nullptr, // VARIANT_4 - nullptr, nullptr, // VARIANT_XCASH + nullptr, nullptr, // VARIANT_RWZ nullptr, nullptr, // VARIANT_ZELERIUS + nullptr, nullptr, // VARIANT_DOUBLE #else nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, @@ -328,6 +336,7 @@ CryptoNight::cn_hash_fun CryptoNight::fn(xmrig::Algo algorithm, xmrig::AlgoVerif nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, # endif }; @@ -375,7 +384,8 @@ bool CryptoNight::selfTest() { verify(VARIANT_GPU, test_output_gpu) && # endif verify(VARIANT_FAST_2, test_output_xtl_v9) && - verify(VARIANT_XCASH, test_output_xcash) && + verify(VARIANT_RWZ, test_output_rwz) && + verify(VARIANT_DOUBLE, test_output_double) && verify(VARIANT_ZELERIUS, test_output_zelerius); } diff --git a/src/crypto/CryptoNight_constants.h b/src/crypto/CryptoNight_constants.h index e86ee9c8..e2de25b1 100644 --- a/src/crypto/CryptoNight_constants.h +++ b/src/crypto/CryptoNight_constants.h @@ -37,14 +37,14 @@ namespace xmrig { -constexpr const uint32_t CRYPTONIGHT_ITER = 0x80000; -constexpr const uint32_t CRYPTONIGHT_HALF_ITER = 0x40000; -constexpr const uint32_t CRYPTONIGHT_XAO_ITER = 0x100000; -constexpr const uint32_t CRYPTONIGHT_XFH_ITER = 0x20000; -constexpr const uint32_t CRYPTONIGHT_LITE_UPX_ITER = 0x20000; -constexpr const uint32_t CRYPTONIGHT_TRTL_ITER = 0x10000; -constexpr const uint32_t CRYPTONIGHT_XCASH_ITER = 0x100000; -constexpr const uint32_t CRYPTONIGHT_ZELERIUS_ITER = 0x60000; +constexpr const uint32_t CRYPTONIGHT_ITER = 0x80000; +constexpr const uint32_t CRYPTONIGHT_HALF_ITER = 0x40000; +constexpr const uint32_t CRYPTONIGHT_XAO_ITER = 0x100000; +constexpr const uint32_t CRYPTONIGHT_XFH_ITER = 0x20000; +constexpr const uint32_t CRYPTONIGHT_LITE_UPX_ITER = 0x20000; +constexpr const uint32_t CRYPTONIGHT_TRTL_ITER = 0x10000; +constexpr const uint32_t CRYPTONIGHT_DOUBLE_ITER = 0x100000; +constexpr const uint32_t CRYPTONIGHT_WALTZ_ITER = 0x60000; constexpr const size_t CRYPTONIGHT_MEMORY = 2 * 1024 * 1024; constexpr const uint32_t CRYPTONIGHT_MASK = 0x1FFFF0; @@ -123,7 +123,7 @@ inline uint32_t cn_select_mask(Algo algorithm) } -template inline constexpr uint32_t cn_select_iter() { return 0; } +template inline constexpr uint32_t cn_select_iter() { return 0; } template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_ITER; } template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_ITER; } template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_ITER; } @@ -135,8 +135,9 @@ template<> inline constexpr uint32_t cn_select_iter() template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_ITER; } template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_XFH_ITER; } template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_HALF_ITER; } -template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_XCASH_ITER; } -template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_ZELERIUS_ITER; } +template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_WALTZ_ITER; } +template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_WALTZ_ITER; } +template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_DOUBLE_ITER; } template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_HALF_ITER; } template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_HALF_ITER; } template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_LITE_UPX_ITER; } @@ -166,11 +167,13 @@ inline uint32_t cn_select_iter(Algo algorithm, Variant variant) case VARIANT_UPX: return CRYPTONIGHT_LITE_UPX_ITER; - case VARIANT_XCASH: - return CRYPTONIGHT_XCASH_ITER; - + case VARIANT_RWZ: case VARIANT_ZELERIUS: - return CRYPTONIGHT_ZELERIUS_ITER; + return CRYPTONIGHT_WALTZ_ITER; + + case VARIANT_DOUBLE: + return CRYPTONIGHT_DOUBLE_ITER; + default: break; } @@ -214,8 +217,9 @@ template<> inline constexpr Variant cn_base_variant(){ return VA template<> inline constexpr Variant cn_base_variant() { return VARIANT_GPU; } template<> inline constexpr Variant cn_base_variant() { return VARIANT_2; } template<> inline constexpr Variant cn_base_variant() { return VARIANT_2; } -template<> inline constexpr Variant cn_base_variant() { return VARIANT_2; } +template<> inline constexpr Variant cn_base_variant() { return VARIANT_2; } template<> inline constexpr Variant cn_base_variant() { return VARIANT_2; } +template<> inline constexpr Variant cn_base_variant() { return VARIANT_2; } template inline constexpr bool cn_is_cryptonight_r() { return false; } diff --git a/src/crypto/CryptoNight_monero.h b/src/crypto/CryptoNight_monero.h index 26c1fff0..7de530cd 100644 --- a/src/crypto/CryptoNight_monero.h +++ b/src/crypto/CryptoNight_monero.h @@ -83,11 +83,11 @@ sqrt_result_xmm_##part = int_sqrt_v2(cx_0 + division_result); \ } while (0) -# define VARIANT2_SHUFFLE(base_ptr, offset, _a, _b, _b1, _c) \ +# define VARIANT2_SHUFFLE(base_ptr, offset, _a, _b, _b1, _c, reverse) \ do { \ - const __m128i chunk1 = _mm_load_si128((__m128i *)((base_ptr) + ((offset) ^ 0x10))); \ + 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) ^ 0x30))); \ + 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)); \ @@ -96,15 +96,20 @@ } \ } while (0) -# define VARIANT2_SHUFFLE2(base_ptr, offset, _a, _b, _b1, hi, lo) \ +# 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))); \ - _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)); \ + 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) @@ -128,11 +133,11 @@ 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) \ +# define VARIANT2_SHUFFLE(base_ptr, offset, _a, _b, _b1, _c, reverse) \ do { \ - const uint64x2_t chunk1 = vld1q_u64((uint64_t*)((base_ptr) + ((offset) ^ 0x10))); \ + 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) ^ 0x30))); \ + 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))); \ @@ -141,15 +146,20 @@ } \ } while (0) -# define VARIANT2_SHUFFLE2(base_ptr, offset, _a, _b, _b1, hi, lo) \ +# 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))); \ - 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))); \ + 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 @@ -158,6 +168,16 @@ #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 "common/xmrig.h" #include "variant4_random_math.h" @@ -183,4 +203,4 @@ v4_random_math(code##part, r##part); \ } -#endif /* XMRIG_CRYPTONIGHT_MONERO_H */ +#endif /* XMRIG_CRYPTONIGHT_MONERO_H */ \ No newline at end of file diff --git a/src/crypto/CryptoNight_test.h b/src/crypto/CryptoNight_test.h index 4c9ed5c9..773f4889 100644 --- a/src/crypto/CryptoNight_test.h +++ b/src/crypto/CryptoNight_test.h @@ -212,8 +212,16 @@ const static uint8_t test_output_xtl_v9[32] = { }; -// CN XCASH -const static uint8_t test_output_xcash[32] = { +// 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 DOUBLE +const static uint8_t test_output_double[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 }; diff --git a/src/crypto/CryptoNight_x86.h b/src/crypto/CryptoNight_x86.h index c2290119..543dece6 100644 --- a/src/crypto/CryptoNight_x86.h +++ b/src/crypto/CryptoNight_x86.h @@ -460,7 +460,7 @@ 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) { if (BASE == xmrig::VARIANT_2) { - VARIANT2_SHUFFLE(l, idx, ax0, bx0, bx1, cx); + VARIANT2_SHUFFLE(l, idx, ax0, bx0, bx1, cx, (VARIANT == xmrig::VARIANT_RWZ ? 1 : 0)); _mm_store_si128((__m128i *)mem_out, _mm_xor_si128(bx0, cx)); } else { __m128i tmp = _mm_xor_si128(bx0, cx); @@ -558,9 +558,9 @@ inline void cryptonight_single_hash(const uint8_t *__restrict__ input, size_t si if (BASE == xmrig::VARIANT_2) { if (VARIANT == xmrig::VARIANT_4) { - VARIANT2_SHUFFLE(l0, idx0 & MASK, ax0, bx0, bx1, cx); + VARIANT2_SHUFFLE(l0, idx0 & MASK, ax0, bx0, bx1, cx, 0); } else { - VARIANT2_SHUFFLE2(l0, idx0 & MASK, ax0, bx0, bx1, hi, lo); + VARIANT2_SHUFFLE2(l0, idx0 & MASK, ax0, bx0, bx1, hi, lo, (VARIANT == xmrig::VARIANT_RWZ ? 1 : 0)); } } @@ -896,9 +896,9 @@ inline void cryptonight_double_hash(const uint8_t *__restrict__ input, size_t si if (BASE == xmrig::VARIANT_2) { if (VARIANT == xmrig::VARIANT_4) { - VARIANT2_SHUFFLE(l0, idx0 & MASK, ax0, bx00, bx01, cx0); + VARIANT2_SHUFFLE(l0, idx0 & MASK, ax0, bx00, bx01, cx0, 0); } else { - VARIANT2_SHUFFLE2(l0, idx0 & MASK, ax0, bx00, bx01, hi, lo); + VARIANT2_SHUFFLE2(l0, idx0 & MASK, ax0, bx00, bx01, hi, lo, (VARIANT == xmrig::VARIANT_RWZ ? 1 : 0)); } } @@ -952,9 +952,9 @@ inline void cryptonight_double_hash(const uint8_t *__restrict__ input, size_t si if (BASE == xmrig::VARIANT_2) { if (VARIANT == xmrig::VARIANT_4) { - VARIANT2_SHUFFLE(l1, idx1 & MASK, ax1, bx10, bx11, cx1); + VARIANT2_SHUFFLE(l1, idx1 & MASK, ax1, bx10, bx11, cx1, 0); } else { - VARIANT2_SHUFFLE2(l1, idx1 & MASK, ax1, bx10, bx11, hi, lo); + VARIANT2_SHUFFLE2(l1, idx1 & MASK, ax1, bx10, bx11, hi, lo, (VARIANT == xmrig::VARIANT_RWZ ? 1 : 0)); } } @@ -1056,9 +1056,9 @@ inline void cryptonight_double_hash(const uint8_t *__restrict__ input, size_t si lo = __umul128(idx, cl##part, &hi); \ if (BASE == xmrig::VARIANT_2) { \ if (VARIANT == xmrig::VARIANT_4) { \ - VARIANT2_SHUFFLE(l, idx & MASK, a, b0, b1, c); \ + VARIANT2_SHUFFLE(l, idx & MASK, a, b0, b1, c, 0); \ } else { \ - VARIANT2_SHUFFLE2(l, idx & MASK, a, b0, b1, hi, lo); \ + VARIANT2_SHUFFLE2(l, idx & MASK, a, b0, b1, hi, lo, (VARIANT == xmrig::VARIANT_RWZ ? 1 : 0)); \ } \ } \ if (VARIANT == xmrig::VARIANT_4) { \ diff --git a/src/version.h b/src/version.h index 20b8004c..46a0d046 100644 --- a/src/version.h +++ b/src/version.h @@ -29,14 +29,14 @@ #define APP_NAME "XMRigCC-AMD" #define APP_COPYRIGHT "Copyright (C) 2017- BenDr0id" #define APP_DESC "XMRigCC-AMD OpenCL miner" -#define APP_VERSION "1.8.15 (based on XMRig)" +#define APP_VERSION "1.9.0 (based on XMRig)" #define APP_SITE "https://github.com/Bendr0id/xmrigCC-amd" #define APP_DOMAIN "" #define APP_KIND "amd" #define APP_VER_MAJOR 1 -#define APP_VER_MINOR 8 -#define APP_VER_PATCH 15 +#define APP_VER_MINOR 9 +#define APP_VER_PATCH 0 #define TYPE_AMD_GPU diff --git a/xmrig.cbp b/xmrig.cbp new file mode 100644 index 00000000..94aebc3b --- /dev/null +++ b/xmrig.cbp @@ -0,0 +1,829 @@ + + + + + + From 36a0571642cc0a7d0e51322d7e28e94affb9c990 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Wed, 6 Mar 2019 21:46:06 +0100 Subject: [PATCH 88/97] Updated Changelog --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 649062b9..063700d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +# 1.9.1 +- Rebase from XMRig-amd 2.14.0 +- Integrated Monero CN-R variant so called CNv4, aka CN-R, aka CNv5, aka Cryptonight-R (algo: "cryptonight", variant: "r" / "auto") +- Integrated Wownero CN-R variant (algo: "cryptonight", variant: "wow") +- Integrated Graft variant (algo: "cryptonight", variant: "rwz" OR variant: "graft") +- Integrated X-Cash variant (algo: "cryptonight", variant: "double" OR variant: "heavyx" OR variant: "xcash") +- Integrated Zelerius variant (algo: "cryptonight", variant: "zls" OR variant: "zelerius") +- Add miner version column to the Dashboard (version turns red when its outdated) +- Fixed crash when remote logging is disabled # 1.8.14 - Rebase from XMRig-amd 2.11.0 - Integrated new RYO algo CN-GPU (algo: "cryptonight", variant: "gpu") From 52ed1c02f4db38bc2337b98d4a29c1e78e6f4364 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Wed, 6 Mar 2019 21:48:18 +0100 Subject: [PATCH 89/97] Preparation #1.9.1 --- src/version.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/version.h b/src/version.h index 46a0d046..7a83b35e 100644 --- a/src/version.h +++ b/src/version.h @@ -29,14 +29,14 @@ #define APP_NAME "XMRigCC-AMD" #define APP_COPYRIGHT "Copyright (C) 2017- BenDr0id" #define APP_DESC "XMRigCC-AMD OpenCL miner" -#define APP_VERSION "1.9.0 (based on XMRig)" +#define APP_VERSION "1.9.1 (based on XMRig)" #define APP_SITE "https://github.com/Bendr0id/xmrigCC-amd" #define APP_DOMAIN "" #define APP_KIND "amd" #define APP_VER_MAJOR 1 #define APP_VER_MINOR 9 -#define APP_VER_PATCH 0 +#define APP_VER_PATCH 1 #define TYPE_AMD_GPU From 9e5a32d070669856c281d34534234cf0b7ce71dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Wed, 6 Mar 2019 23:00:48 +0100 Subject: [PATCH 90/97] Windows build fixes --- src/amd/OclGPU.cpp | 4 + src/amd/opencl/cryptonight.cl | 453 +-------------------------------- src/amd/opencl/cryptonight2.cl | 447 ++++++++++++++++++++++++++++++++ 3 files changed, 456 insertions(+), 448 deletions(-) create mode 100644 src/amd/opencl/cryptonight2.cl diff --git a/src/amd/OclGPU.cpp b/src/amd/OclGPU.cpp index 857adbdf..baeba675 100644 --- a/src/amd/OclGPU.cpp +++ b/src/amd/OclGPU.cpp @@ -493,6 +493,9 @@ size_t InitOpenCL(const std::vector &contexts, xmrig::Config *conf const char *cryptonightCL = #include "./opencl/cryptonight.cl" ; + const char *cryptonightCL2 = + #include "./opencl/cryptonight2.cl" + ; const char *blake256CL = #include "./opencl/blake256.cl" ; @@ -527,6 +530,7 @@ size_t InitOpenCL(const std::vector &contexts, xmrig::Config *conf source_code = std::regex_replace(source_code, std::regex("XMRIG_INCLUDE_FAST_INT_MATH_V2"), fastIntMathV2CL); source_code = std::regex_replace(source_code, std::regex("XMRIG_INCLUDE_FAST_DIV_HEAVY"), fastDivHeavyCL); source_code = std::regex_replace(source_code, std::regex("XMRIG_INCLUDE_CN_GPU"), cryptonight_gpu); + source_code = std::regex_replace(source_code, std::regex("XMRIG_INCLUDE_CRYPTONIGHT2"), cryptonightCL2); for (size_t i = 0; i < num_gpus; ++i) { if (contexts[i]->stridedIndex == 2 && (contexts[i]->rawIntensity % contexts[i]->workSize) != 0) { diff --git a/src/amd/opencl/cryptonight.cl b/src/amd/opencl/cryptonight.cl index 225dd273..4d8b6a34 100644 --- a/src/amd/opencl/cryptonight.cl +++ b/src/amd/opencl/cryptonight.cl @@ -78,6 +78,7 @@ inline int amd_bfe(const uint src0, const uint offset, const uint width) } #endif +XMRIG_INCLUDE_CRYPTONIGHT_CL2 //#include "opencl/wolf-aes.cl" XMRIG_INCLUDE_WOLF_AES //#include "opencl/wolf-skein.cl" @@ -408,6 +409,9 @@ inline ulong getIdx() //#include "opencl/cryptonight_gpu.cl" XMRIG_INCLUDE_CN_GPU +//#include "opencl/cryptonight2.cl" +XMRIG_INCLUDE_CRYPTONIGHT2 + #define mix_and_propagate(xin) (xin)[(get_local_id(1)) % 8][get_local_id(0)] ^ (xin)[(get_local_id(1) + 1) % 8][get_local_id(0)] __attribute__((reqd_work_group_size(8, 8, 1))) @@ -1106,453 +1110,6 @@ __kernel void cn1_fastv2(__global uint4 *Scratchpad, __global ulong *states, uin # endif } -)===" -R"===( -__attribute__((reqd_work_group_size(WORKSIZE, 1, 1))) -__kernel void cn1_double(__global uint4 *Scratchpad, __global ulong *states, uint variant, __global ulong *input, uint Threads) -{ -# if (ALGO == CRYPTONIGHT) - ulong a[2], b[4]; - __local uint AES0[256], AES1[256], AES2[256], AES3[256]; - - const ulong gIdx = getIdx(); - - for(int i = get_local_id(0); i < 256; i += WORKSIZE) - { - const uint tmp = AES0_C[i]; - AES0[i] = tmp; - AES1[i] = rotate(tmp, 8U); - AES2[i] = rotate(tmp, 16U); - AES3[i] = rotate(tmp, 24U); - } - - barrier(CLK_LOCAL_MEM_FENCE); - -# if (COMP_MODE == 1) - // do not use early return here - if (gIdx < Threads) -# endif - { - states += 25 * gIdx; - -# if defined(__NV_CL_C_VERSION) - Scratchpad += gIdx * (ITERATIONS >> 2); -# else -# if (STRIDED_INDEX == 0) - Scratchpad += gIdx * (MEMORY >> 4); -# elif (STRIDED_INDEX == 1) - Scratchpad += gIdx; -# elif (STRIDED_INDEX == 2) - Scratchpad += get_group_id(0) * (MEMORY >> 4) * WORKSIZE + MEM_CHUNK * get_local_id(0); -# endif -# endif - - a[0] = states[0] ^ states[4]; - a[1] = states[1] ^ states[5]; - - b[0] = states[2] ^ states[6]; - b[1] = states[3] ^ states[7]; - b[2] = states[8] ^ states[10]; - b[3] = states[9] ^ states[11]; - } - - ulong2 bx0 = ((ulong2 *)b)[0]; - ulong2 bx1 = ((ulong2 *)b)[1]; - - mem_fence(CLK_LOCAL_MEM_FENCE); - -# ifdef __NV_CL_C_VERSION - __local uint16 scratchpad_line_buf[WORKSIZE]; - __local uint16* scratchpad_line = scratchpad_line_buf + get_local_id(0); -# define SCRATCHPAD_CHUNK(N) (*(__local uint4*)((__local uchar*)(scratchpad_line) + (idx1 ^ (N << 4)))) -# else -# if (STRIDED_INDEX == 0) -# define SCRATCHPAD_CHUNK(N) (*(__global uint4*)((__global uchar*)(Scratchpad) + (idx ^ (N << 4)))) -# elif (STRIDED_INDEX == 1) -# define SCRATCHPAD_CHUNK(N) (*(__global uint4*)((__global uchar*)(Scratchpad) + mul24(as_uint(idx ^ (N << 4)), Threads))) -# elif (STRIDED_INDEX == 2) -# define SCRATCHPAD_CHUNK(N) (*(__global uint4*)((__global uchar*)(Scratchpad) + (((idx ^ (N << 4)) % (MEM_CHUNK << 4)) + ((idx ^ (N << 4)) / (MEM_CHUNK << 4)) * WORKSIZE * (MEM_CHUNK << 4)))) -# endif -# endif - -# if (COMP_MODE == 1) - // do not use early return here - if (gIdx < Threads) -# endif - { - uint2 division_result = as_uint2(states[12]); - uint sqrt_result = as_uint2(states[13]).s0; - - #pragma unroll UNROLL_FACTOR - for(int i = 0; i < 0x100000; ++i) - { -# ifdef __NV_CL_C_VERSION - uint idx = a[0] & 0x1FFFC0; - uint idx1 = a[0] & 0x30; - - *scratchpad_line = *(__global uint16*)((__global uchar*)(Scratchpad) + idx); -# else - uint idx = a[0] & MASK; -# endif - - uint4 c = SCRATCHPAD_CHUNK(0); - c = AES_Round(AES0, AES1, AES2, AES3, c, ((uint4 *)a)[0]); - - { - const ulong2 chunk1 = as_ulong2(SCRATCHPAD_CHUNK(1)); - const ulong2 chunk2 = as_ulong2(SCRATCHPAD_CHUNK(2)); - const ulong2 chunk3 = as_ulong2(SCRATCHPAD_CHUNK(3)); - - SCRATCHPAD_CHUNK(1) = as_uint4(chunk3 + bx1); - SCRATCHPAD_CHUNK(2) = as_uint4(chunk1 + bx0); - SCRATCHPAD_CHUNK(3) = as_uint4(chunk2 + ((ulong2 *)a)[0]); - } - - SCRATCHPAD_CHUNK(0) = as_uint4(bx0) ^ c; - -# ifdef __NV_CL_C_VERSION - *(__global uint16*)((__global uchar*)(Scratchpad) + idx) = *scratchpad_line; - - idx = as_ulong2(c).s0 & 0x1FFFC0; - idx1 = as_ulong2(c).s0 & 0x30; - - *scratchpad_line = *(__global uint16*)((__global uchar*)(Scratchpad) + idx); -# else - idx = as_ulong2(c).s0 & MASK; -# endif - - uint4 tmp = SCRATCHPAD_CHUNK(0); - - { - tmp.s0 ^= division_result.s0; - tmp.s1 ^= division_result.s1 ^ sqrt_result; - - division_result = fast_div_v2(as_ulong2(c).s1, (c.s0 + (sqrt_result << 1)) | 0x80000001UL); - sqrt_result = fast_sqrt_v2(as_ulong2(c).s0 + as_ulong(division_result)); - } - - ulong2 t; - t.s0 = mul_hi(as_ulong2(c).s0, as_ulong2(tmp).s0); - t.s1 = as_ulong2(c).s0 * as_ulong2(tmp).s0; - { - const ulong2 chunk1 = as_ulong2(SCRATCHPAD_CHUNK(1)) ^ t; - const ulong2 chunk2 = as_ulong2(SCRATCHPAD_CHUNK(2)); - t ^= chunk2; - const ulong2 chunk3 = as_ulong2(SCRATCHPAD_CHUNK(3)); - - SCRATCHPAD_CHUNK(1) = as_uint4(chunk3 + bx1); - SCRATCHPAD_CHUNK(2) = as_uint4(chunk1 + bx0); - SCRATCHPAD_CHUNK(3) = as_uint4(chunk2 + ((ulong2 *)a)[0]); - } - - a[1] += t.s1; - a[0] += t.s0; - - SCRATCHPAD_CHUNK(0) = ((uint4 *)a)[0]; - -# ifdef __NV_CL_C_VERSION - *(__global uint16*)((__global uchar*)(Scratchpad) + idx) = *scratchpad_line; -# endif - - ((uint4 *)a)[0] ^= tmp; - bx1 = bx0; - bx0 = as_ulong2(c); - } - -# undef SCRATCHPAD_CHUNK - } - mem_fence(CLK_GLOBAL_MEM_FENCE); -# endif -} - -)===" -R"===( -__attribute__((reqd_work_group_size(WORKSIZE, 1, 1))) -__kernel void cn1_rwz(__global uint4 *Scratchpad, __global ulong *states, uint variant, __global ulong *input, uint Threads) -{ -# if (ALGO == CRYPTONIGHT) - ulong a[2], b[4]; - __local uint AES0[256], AES1[256], AES2[256], AES3[256]; - const ulong gIdx = getIdx(); - for(int i = get_local_id(0); i < 256; i += WORKSIZE) - { - const uint tmp = AES0_C[i]; - AES0[i] = tmp; - AES1[i] = rotate(tmp, 8U); - AES2[i] = rotate(tmp, 16U); - AES3[i] = rotate(tmp, 24U); - } - barrier(CLK_LOCAL_MEM_FENCE); -# if (COMP_MODE == 1) - // do not use early return here - if (gIdx < Threads) -# endif - { - states += 25 * gIdx; -# if defined(__NV_CL_C_VERSION) - Scratchpad += gIdx * (0x40000 >> 2); -# else -# if (STRIDED_INDEX == 0) - Scratchpad += gIdx * (MEMORY >> 4); -# elif (STRIDED_INDEX == 1) - Scratchpad += gIdx; -# elif (STRIDED_INDEX == 2) - Scratchpad += get_group_id(0) * (MEMORY >> 4) * WORKSIZE + MEM_CHUNK * get_local_id(0); -# endif -# endif - a[0] = states[0] ^ states[4]; - a[1] = states[1] ^ states[5]; - b[0] = states[2] ^ states[6]; - b[1] = states[3] ^ states[7]; - b[2] = states[8] ^ states[10]; - b[3] = states[9] ^ states[11]; - } - ulong2 bx0 = ((ulong2 *)b)[0]; - ulong2 bx1 = ((ulong2 *)b)[1]; - mem_fence(CLK_LOCAL_MEM_FENCE); -# ifdef __NV_CL_C_VERSION - __local uint16 scratchpad_line_buf[WORKSIZE]; - __local uint16* scratchpad_line = scratchpad_line_buf + get_local_id(0); -# define SCRATCHPAD_CHUNK(N) (*(__local uint4*)((__local uchar*)(scratchpad_line) + (idx1 ^ (N << 4)))) -# else -# if (STRIDED_INDEX == 0) -# define SCRATCHPAD_CHUNK(N) (*(__global uint4*)((__global uchar*)(Scratchpad) + (idx ^ (N << 4)))) -# elif (STRIDED_INDEX == 1) -# define SCRATCHPAD_CHUNK(N) (*(__global uint4*)((__global uchar*)(Scratchpad) + mul24(as_uint(idx ^ (N << 4)), Threads))) -# elif (STRIDED_INDEX == 2) -# define SCRATCHPAD_CHUNK(N) (*(__global uint4*)((__global uchar*)(Scratchpad) + (((idx ^ (N << 4)) % (MEM_CHUNK << 4)) + ((idx ^ (N << 4)) / (MEM_CHUNK << 4)) * WORKSIZE * (MEM_CHUNK << 4)))) -# endif -# endif -# if (COMP_MODE == 1) - // do not use early return here - if (gIdx < Threads) -# endif - { - uint2 division_result = as_uint2(states[12]); - uint sqrt_result = as_uint2(states[13]).s0; - #pragma unroll UNROLL_FACTOR - for(int i = 0; i < 0x60000; ++i) - { -# ifdef __NV_CL_C_VERSION - uint idx = a[0] & 0x1FFFC0; - uint idx1 = a[0] & 0x30; - *scratchpad_line = *(__global uint16*)((__global uchar*)(Scratchpad) + idx); -# else - uint idx = a[0] & MASK; -# endif - uint4 c = SCRATCHPAD_CHUNK(0); - c = AES_Round(AES0, AES1, AES2, AES3, c, ((uint4 *)a)[0]); - { - const ulong2 chunk1 = as_ulong2(SCRATCHPAD_CHUNK(3)); - const ulong2 chunk2 = as_ulong2(SCRATCHPAD_CHUNK(2)); - const ulong2 chunk3 = as_ulong2(SCRATCHPAD_CHUNK(1)); - SCRATCHPAD_CHUNK(1) = as_uint4(chunk3 + bx1); - SCRATCHPAD_CHUNK(2) = as_uint4(chunk1 + bx0); - SCRATCHPAD_CHUNK(3) = as_uint4(chunk2 + ((ulong2 *)a)[0]); - } - SCRATCHPAD_CHUNK(0) = as_uint4(bx0) ^ c; -# ifdef __NV_CL_C_VERSION - *(__global uint16*)((__global uchar*)(Scratchpad) + idx) = *scratchpad_line; - idx = as_ulong2(c).s0 & 0x1FFFC0; - idx1 = as_ulong2(c).s0 & 0x30; - *scratchpad_line = *(__global uint16*)((__global uchar*)(Scratchpad) + idx); -# else - idx = as_ulong2(c).s0 & MASK; -# endif - uint4 tmp = SCRATCHPAD_CHUNK(0); - { - tmp.s0 ^= division_result.s0; - tmp.s1 ^= division_result.s1 ^ sqrt_result; - division_result = fast_div_v2(as_ulong2(c).s1, (c.s0 + (sqrt_result << 1)) | 0x80000001UL); - sqrt_result = fast_sqrt_v2(as_ulong2(c).s0 + as_ulong(division_result)); - } - ulong2 t; - t.s0 = mul_hi(as_ulong2(c).s0, as_ulong2(tmp).s0); - t.s1 = as_ulong2(c).s0 * as_ulong2(tmp).s0; - { - const ulong2 chunk1 = as_ulong2(SCRATCHPAD_CHUNK(1)) ^ t; - const ulong2 chunk2 = as_ulong2(SCRATCHPAD_CHUNK(2)); - t ^= chunk2; - const ulong2 chunk3 = as_ulong2(SCRATCHPAD_CHUNK(3)); - SCRATCHPAD_CHUNK(1) = as_uint4(chunk1 + bx1); - SCRATCHPAD_CHUNK(2) = as_uint4(chunk3 + bx0); - SCRATCHPAD_CHUNK(3) = as_uint4(chunk2 + ((ulong2 *)a)[0]); - } - a[1] += t.s1; - a[0] += t.s0; - SCRATCHPAD_CHUNK(0) = ((uint4 *)a)[0]; -# ifdef __NV_CL_C_VERSION - *(__global uint16*)((__global uchar*)(Scratchpad) + idx) = *scratchpad_line; -# endif - ((uint4 *)a)[0] ^= tmp; - bx1 = bx0; - bx0 = as_ulong2(c); - } -# undef SCRATCHPAD_CHUNK - } - mem_fence(CLK_GLOBAL_MEM_FENCE); -# endif -} -)===" -R"===( - -__attribute__((reqd_work_group_size(WORKSIZE, 1, 1))) -__kernel void cn1_zelerius(__global uint4 *Scratchpad, __global ulong *states, uint variant, __global ulong *input, uint Threads) -{ -# if (ALGO == CRYPTONIGHT) - ulong a[2], b[4]; - __local uint AES0[256], AES1[256], AES2[256], AES3[256]; - - const ulong gIdx = getIdx(); - - for(int i = get_local_id(0); i < 256; i += WORKSIZE) - { - const uint tmp = AES0_C[i]; - AES0[i] = tmp; - AES1[i] = rotate(tmp, 8U); - AES2[i] = rotate(tmp, 16U); - AES3[i] = rotate(tmp, 24U); - } - - barrier(CLK_LOCAL_MEM_FENCE); - -# if (COMP_MODE == 1) - // do not use early return here - if (gIdx < Threads) -# endif - { - states += 25 * gIdx; - -# if defined(__NV_CL_C_VERSION) - Scratchpad += gIdx * (ITERATIONS >> 2); -# else -# if (STRIDED_INDEX == 0) - Scratchpad += gIdx * (MEMORY >> 4); -# elif (STRIDED_INDEX == 1) - Scratchpad += gIdx; -# elif (STRIDED_INDEX == 2) - Scratchpad += get_group_id(0) * (MEMORY >> 4) * WORKSIZE + MEM_CHUNK * get_local_id(0); -# endif -# endif - - a[0] = states[0] ^ states[4]; - a[1] = states[1] ^ states[5]; - - b[0] = states[2] ^ states[6]; - b[1] = states[3] ^ states[7]; - b[2] = states[8] ^ states[10]; - b[3] = states[9] ^ states[11]; - } - - ulong2 bx0 = ((ulong2 *)b)[0]; - ulong2 bx1 = ((ulong2 *)b)[1]; - - mem_fence(CLK_LOCAL_MEM_FENCE); - -# ifdef __NV_CL_C_VERSION - __local uint16 scratchpad_line_buf[WORKSIZE]; - __local uint16* scratchpad_line = scratchpad_line_buf + get_local_id(0); -# define SCRATCHPAD_CHUNK(N) (*(__local uint4*)((__local uchar*)(scratchpad_line) + (idx1 ^ (N << 4)))) -# else -# if (STRIDED_INDEX == 0) -# define SCRATCHPAD_CHUNK(N) (*(__global uint4*)((__global uchar*)(Scratchpad) + (idx ^ (N << 4)))) -# elif (STRIDED_INDEX == 1) -# define SCRATCHPAD_CHUNK(N) (*(__global uint4*)((__global uchar*)(Scratchpad) + mul24(as_uint(idx ^ (N << 4)), Threads))) -# elif (STRIDED_INDEX == 2) -# define SCRATCHPAD_CHUNK(N) (*(__global uint4*)((__global uchar*)(Scratchpad) + (((idx ^ (N << 4)) % (MEM_CHUNK << 4)) + ((idx ^ (N << 4)) / (MEM_CHUNK << 4)) * WORKSIZE * (MEM_CHUNK << 4)))) -# endif -# endif - -# if (COMP_MODE == 1) - // do not use early return here - if (gIdx < Threads) -# endif - { - uint2 division_result = as_uint2(states[12]); - uint sqrt_result = as_uint2(states[13]).s0; - - #pragma unroll UNROLL_FACTOR - for(int i = 0; i < 0x60000; ++i) - { -# ifdef __NV_CL_C_VERSION - uint idx = a[0] & 0x1FFFC0; - uint idx1 = a[0] & 0x30; - - *scratchpad_line = *(__global uint16*)((__global uchar*)(Scratchpad) + idx); -# else - uint idx = a[0] & MASK; -# endif - - uint4 c = SCRATCHPAD_CHUNK(0); - c = AES_Round(AES0, AES1, AES2, AES3, c, ((uint4 *)a)[0]); - - { - const ulong2 chunk1 = as_ulong2(SCRATCHPAD_CHUNK(1)); - const ulong2 chunk2 = as_ulong2(SCRATCHPAD_CHUNK(2)); - const ulong2 chunk3 = as_ulong2(SCRATCHPAD_CHUNK(3)); - - SCRATCHPAD_CHUNK(1) = as_uint4(chunk3 + bx1); - SCRATCHPAD_CHUNK(2) = as_uint4(chunk1 + bx0); - SCRATCHPAD_CHUNK(3) = as_uint4(chunk2 + ((ulong2 *)a)[0]); - } - - SCRATCHPAD_CHUNK(0) = as_uint4(bx0) ^ c; - -# ifdef __NV_CL_C_VERSION - *(__global uint16*)((__global uchar*)(Scratchpad) + idx) = *scratchpad_line; - - idx = as_ulong2(c).s0 & 0x1FFFC0; - idx1 = as_ulong2(c).s0 & 0x30; - - *scratchpad_line = *(__global uint16*)((__global uchar*)(Scratchpad) + idx); -# else - idx = as_ulong2(c).s0 & MASK; -# endif - - uint4 tmp = SCRATCHPAD_CHUNK(0); - - { - tmp.s0 ^= division_result.s0; - tmp.s1 ^= division_result.s1 ^ sqrt_result; - - division_result = fast_div_v2(as_ulong2(c).s1, (c.s0 + (sqrt_result << 1)) | 0x80000001UL); - sqrt_result = fast_sqrt_v2(as_ulong2(c).s0 + as_ulong(division_result)); - } - - ulong2 t; - t.s0 = mul_hi(as_ulong2(c).s0, as_ulong2(tmp).s0); - t.s1 = as_ulong2(c).s0 * as_ulong2(tmp).s0; - { - const ulong2 chunk1 = as_ulong2(SCRATCHPAD_CHUNK(1)) ^ t; - const ulong2 chunk2 = as_ulong2(SCRATCHPAD_CHUNK(2)); - t ^= chunk2; - const ulong2 chunk3 = as_ulong2(SCRATCHPAD_CHUNK(3)); - - SCRATCHPAD_CHUNK(1) = as_uint4(chunk3 + bx1); - SCRATCHPAD_CHUNK(2) = as_uint4(chunk1 + bx0); - SCRATCHPAD_CHUNK(3) = as_uint4(chunk2 + ((ulong2 *)a)[0]); - } - - a[1] += t.s1; - a[0] += t.s0; - - SCRATCHPAD_CHUNK(0) = ((uint4 *)a)[0]; - -# ifdef __NV_CL_C_VERSION - *(__global uint16*)((__global uchar*)(Scratchpad) + idx) = *scratchpad_line; -# endif - - ((uint4 *)a)[0] ^= tmp; - bx1 = bx0; - bx0 = as_ulong2(c); - } - -# undef SCRATCHPAD_CHUNK - } - mem_fence(CLK_GLOBAL_MEM_FENCE); -# endif -} - )===" R"===( @@ -2504,4 +2061,4 @@ __kernel void Groestl(__global ulong *states, __global uint *BranchBuf, __global } } -)===" +)===" \ No newline at end of file diff --git a/src/amd/opencl/cryptonight2.cl b/src/amd/opencl/cryptonight2.cl new file mode 100644 index 00000000..8569037b --- /dev/null +++ b/src/amd/opencl/cryptonight2.cl @@ -0,0 +1,447 @@ +R"===( +__attribute__((reqd_work_group_size(WORKSIZE, 1, 1))) +__kernel void cn1_double(__global uint4 *Scratchpad, __global ulong *states, uint variant, __global ulong *input, uint Threads) +{ +# if (ALGO == CRYPTONIGHT) + ulong a[2], b[4]; + __local uint AES0[256], AES1[256], AES2[256], AES3[256]; + + const ulong gIdx = getIdx(); + + for(int i = get_local_id(0); i < 256; i += WORKSIZE) + { + const uint tmp = AES0_C[i]; + AES0[i] = tmp; + AES1[i] = rotate(tmp, 8U); + AES2[i] = rotate(tmp, 16U); + AES3[i] = rotate(tmp, 24U); + } + + barrier(CLK_LOCAL_MEM_FENCE); + +# if (COMP_MODE == 1) + // do not use early return here + if (gIdx < Threads) +# endif + { + states += 25 * gIdx; + +# if defined(__NV_CL_C_VERSION) + Scratchpad += gIdx * (ITERATIONS >> 2); +# else +# if (STRIDED_INDEX == 0) + Scratchpad += gIdx * (MEMORY >> 4); +# elif (STRIDED_INDEX == 1) + Scratchpad += gIdx; +# elif (STRIDED_INDEX == 2) + Scratchpad += get_group_id(0) * (MEMORY >> 4) * WORKSIZE + MEM_CHUNK * get_local_id(0); +# endif +# endif + + a[0] = states[0] ^ states[4]; + a[1] = states[1] ^ states[5]; + + b[0] = states[2] ^ states[6]; + b[1] = states[3] ^ states[7]; + b[2] = states[8] ^ states[10]; + b[3] = states[9] ^ states[11]; + } + + ulong2 bx0 = ((ulong2 *)b)[0]; + ulong2 bx1 = ((ulong2 *)b)[1]; + + mem_fence(CLK_LOCAL_MEM_FENCE); + +# ifdef __NV_CL_C_VERSION + __local uint16 scratchpad_line_buf[WORKSIZE]; + __local uint16* scratchpad_line = scratchpad_line_buf + get_local_id(0); +# define SCRATCHPAD_CHUNK(N) (*(__local uint4*)((__local uchar*)(scratchpad_line) + (idx1 ^ (N << 4)))) +# else +# if (STRIDED_INDEX == 0) +# define SCRATCHPAD_CHUNK(N) (*(__global uint4*)((__global uchar*)(Scratchpad) + (idx ^ (N << 4)))) +# elif (STRIDED_INDEX == 1) +# define SCRATCHPAD_CHUNK(N) (*(__global uint4*)((__global uchar*)(Scratchpad) + mul24(as_uint(idx ^ (N << 4)), Threads))) +# elif (STRIDED_INDEX == 2) +# define SCRATCHPAD_CHUNK(N) (*(__global uint4*)((__global uchar*)(Scratchpad) + (((idx ^ (N << 4)) % (MEM_CHUNK << 4)) + ((idx ^ (N << 4)) / (MEM_CHUNK << 4)) * WORKSIZE * (MEM_CHUNK << 4)))) +# endif +# endif + +# if (COMP_MODE == 1) + // do not use early return here + if (gIdx < Threads) +# endif + { + uint2 division_result = as_uint2(states[12]); + uint sqrt_result = as_uint2(states[13]).s0; + + #pragma unroll UNROLL_FACTOR + for(int i = 0; i < 0x100000; ++i) + { +# ifdef __NV_CL_C_VERSION + uint idx = a[0] & 0x1FFFC0; + uint idx1 = a[0] & 0x30; + + *scratchpad_line = *(__global uint16*)((__global uchar*)(Scratchpad) + idx); +# else + uint idx = a[0] & MASK; +# endif + + uint4 c = SCRATCHPAD_CHUNK(0); + c = AES_Round(AES0, AES1, AES2, AES3, c, ((uint4 *)a)[0]); + + { + const ulong2 chunk1 = as_ulong2(SCRATCHPAD_CHUNK(1)); + const ulong2 chunk2 = as_ulong2(SCRATCHPAD_CHUNK(2)); + const ulong2 chunk3 = as_ulong2(SCRATCHPAD_CHUNK(3)); + + SCRATCHPAD_CHUNK(1) = as_uint4(chunk3 + bx1); + SCRATCHPAD_CHUNK(2) = as_uint4(chunk1 + bx0); + SCRATCHPAD_CHUNK(3) = as_uint4(chunk2 + ((ulong2 *)a)[0]); + } + + SCRATCHPAD_CHUNK(0) = as_uint4(bx0) ^ c; + +# ifdef __NV_CL_C_VERSION + *(__global uint16*)((__global uchar*)(Scratchpad) + idx) = *scratchpad_line; + + idx = as_ulong2(c).s0 & 0x1FFFC0; + idx1 = as_ulong2(c).s0 & 0x30; + + *scratchpad_line = *(__global uint16*)((__global uchar*)(Scratchpad) + idx); +# else + idx = as_ulong2(c).s0 & MASK; +# endif + + uint4 tmp = SCRATCHPAD_CHUNK(0); + + { + tmp.s0 ^= division_result.s0; + tmp.s1 ^= division_result.s1 ^ sqrt_result; + + division_result = fast_div_v2(as_ulong2(c).s1, (c.s0 + (sqrt_result << 1)) | 0x80000001UL); + sqrt_result = fast_sqrt_v2(as_ulong2(c).s0 + as_ulong(division_result)); + } + + ulong2 t; + t.s0 = mul_hi(as_ulong2(c).s0, as_ulong2(tmp).s0); + t.s1 = as_ulong2(c).s0 * as_ulong2(tmp).s0; + { + const ulong2 chunk1 = as_ulong2(SCRATCHPAD_CHUNK(1)) ^ t; + const ulong2 chunk2 = as_ulong2(SCRATCHPAD_CHUNK(2)); + t ^= chunk2; + const ulong2 chunk3 = as_ulong2(SCRATCHPAD_CHUNK(3)); + + SCRATCHPAD_CHUNK(1) = as_uint4(chunk3 + bx1); + SCRATCHPAD_CHUNK(2) = as_uint4(chunk1 + bx0); + SCRATCHPAD_CHUNK(3) = as_uint4(chunk2 + ((ulong2 *)a)[0]); + } + + a[1] += t.s1; + a[0] += t.s0; + + SCRATCHPAD_CHUNK(0) = ((uint4 *)a)[0]; + +# ifdef __NV_CL_C_VERSION + *(__global uint16*)((__global uchar*)(Scratchpad) + idx) = *scratchpad_line; +# endif + + ((uint4 *)a)[0] ^= tmp; + bx1 = bx0; + bx0 = as_ulong2(c); + } + +# undef SCRATCHPAD_CHUNK + } + mem_fence(CLK_GLOBAL_MEM_FENCE); +# endif +} + +)===" +R"===( +__attribute__((reqd_work_group_size(WORKSIZE, 1, 1))) +__kernel void cn1_rwz(__global uint4 *Scratchpad, __global ulong *states, uint variant, __global ulong *input, uint Threads) +{ +# if (ALGO == CRYPTONIGHT) + ulong a[2], b[4]; + __local uint AES0[256], AES1[256], AES2[256], AES3[256]; + const ulong gIdx = getIdx(); + for(int i = get_local_id(0); i < 256; i += WORKSIZE) + { + const uint tmp = AES0_C[i]; + AES0[i] = tmp; + AES1[i] = rotate(tmp, 8U); + AES2[i] = rotate(tmp, 16U); + AES3[i] = rotate(tmp, 24U); + } + barrier(CLK_LOCAL_MEM_FENCE); +# if (COMP_MODE == 1) + // do not use early return here + if (gIdx < Threads) +# endif + { + states += 25 * gIdx; +# if defined(__NV_CL_C_VERSION) + Scratchpad += gIdx * (0x40000 >> 2); +# else +# if (STRIDED_INDEX == 0) + Scratchpad += gIdx * (MEMORY >> 4); +# elif (STRIDED_INDEX == 1) + Scratchpad += gIdx; +# elif (STRIDED_INDEX == 2) + Scratchpad += get_group_id(0) * (MEMORY >> 4) * WORKSIZE + MEM_CHUNK * get_local_id(0); +# endif +# endif + a[0] = states[0] ^ states[4]; + a[1] = states[1] ^ states[5]; + b[0] = states[2] ^ states[6]; + b[1] = states[3] ^ states[7]; + b[2] = states[8] ^ states[10]; + b[3] = states[9] ^ states[11]; + } + ulong2 bx0 = ((ulong2 *)b)[0]; + ulong2 bx1 = ((ulong2 *)b)[1]; + mem_fence(CLK_LOCAL_MEM_FENCE); +# ifdef __NV_CL_C_VERSION + __local uint16 scratchpad_line_buf[WORKSIZE]; + __local uint16* scratchpad_line = scratchpad_line_buf + get_local_id(0); +# define SCRATCHPAD_CHUNK(N) (*(__local uint4*)((__local uchar*)(scratchpad_line) + (idx1 ^ (N << 4)))) +# else +# if (STRIDED_INDEX == 0) +# define SCRATCHPAD_CHUNK(N) (*(__global uint4*)((__global uchar*)(Scratchpad) + (idx ^ (N << 4)))) +# elif (STRIDED_INDEX == 1) +# define SCRATCHPAD_CHUNK(N) (*(__global uint4*)((__global uchar*)(Scratchpad) + mul24(as_uint(idx ^ (N << 4)), Threads))) +# elif (STRIDED_INDEX == 2) +# define SCRATCHPAD_CHUNK(N) (*(__global uint4*)((__global uchar*)(Scratchpad) + (((idx ^ (N << 4)) % (MEM_CHUNK << 4)) + ((idx ^ (N << 4)) / (MEM_CHUNK << 4)) * WORKSIZE * (MEM_CHUNK << 4)))) +# endif +# endif +# if (COMP_MODE == 1) + // do not use early return here + if (gIdx < Threads) +# endif + { + uint2 division_result = as_uint2(states[12]); + uint sqrt_result = as_uint2(states[13]).s0; + #pragma unroll UNROLL_FACTOR + for(int i = 0; i < 0x60000; ++i) + { +# ifdef __NV_CL_C_VERSION + uint idx = a[0] & 0x1FFFC0; + uint idx1 = a[0] & 0x30; + *scratchpad_line = *(__global uint16*)((__global uchar*)(Scratchpad) + idx); +# else + uint idx = a[0] & MASK; +# endif + uint4 c = SCRATCHPAD_CHUNK(0); + c = AES_Round(AES0, AES1, AES2, AES3, c, ((uint4 *)a)[0]); + { + const ulong2 chunk1 = as_ulong2(SCRATCHPAD_CHUNK(3)); + const ulong2 chunk2 = as_ulong2(SCRATCHPAD_CHUNK(2)); + const ulong2 chunk3 = as_ulong2(SCRATCHPAD_CHUNK(1)); + SCRATCHPAD_CHUNK(1) = as_uint4(chunk3 + bx1); + SCRATCHPAD_CHUNK(2) = as_uint4(chunk1 + bx0); + SCRATCHPAD_CHUNK(3) = as_uint4(chunk2 + ((ulong2 *)a)[0]); + } + SCRATCHPAD_CHUNK(0) = as_uint4(bx0) ^ c; +# ifdef __NV_CL_C_VERSION + *(__global uint16*)((__global uchar*)(Scratchpad) + idx) = *scratchpad_line; + idx = as_ulong2(c).s0 & 0x1FFFC0; + idx1 = as_ulong2(c).s0 & 0x30; + *scratchpad_line = *(__global uint16*)((__global uchar*)(Scratchpad) + idx); +# else + idx = as_ulong2(c).s0 & MASK; +# endif + uint4 tmp = SCRATCHPAD_CHUNK(0); + { + tmp.s0 ^= division_result.s0; + tmp.s1 ^= division_result.s1 ^ sqrt_result; + division_result = fast_div_v2(as_ulong2(c).s1, (c.s0 + (sqrt_result << 1)) | 0x80000001UL); + sqrt_result = fast_sqrt_v2(as_ulong2(c).s0 + as_ulong(division_result)); + } + ulong2 t; + t.s0 = mul_hi(as_ulong2(c).s0, as_ulong2(tmp).s0); + t.s1 = as_ulong2(c).s0 * as_ulong2(tmp).s0; + { + const ulong2 chunk1 = as_ulong2(SCRATCHPAD_CHUNK(1)) ^ t; + const ulong2 chunk2 = as_ulong2(SCRATCHPAD_CHUNK(2)); + t ^= chunk2; + const ulong2 chunk3 = as_ulong2(SCRATCHPAD_CHUNK(3)); + SCRATCHPAD_CHUNK(1) = as_uint4(chunk1 + bx1); + SCRATCHPAD_CHUNK(2) = as_uint4(chunk3 + bx0); + SCRATCHPAD_CHUNK(3) = as_uint4(chunk2 + ((ulong2 *)a)[0]); + } + a[1] += t.s1; + a[0] += t.s0; + SCRATCHPAD_CHUNK(0) = ((uint4 *)a)[0]; +# ifdef __NV_CL_C_VERSION + *(__global uint16*)((__global uchar*)(Scratchpad) + idx) = *scratchpad_line; +# endif + ((uint4 *)a)[0] ^= tmp; + bx1 = bx0; + bx0 = as_ulong2(c); + } +# undef SCRATCHPAD_CHUNK + } + mem_fence(CLK_GLOBAL_MEM_FENCE); +# endif +} +)===" +R"===( + +__attribute__((reqd_work_group_size(WORKSIZE, 1, 1))) +__kernel void cn1_zelerius(__global uint4 *Scratchpad, __global ulong *states, uint variant, __global ulong *input, uint Threads) +{ +# if (ALGO == CRYPTONIGHT) + ulong a[2], b[4]; + __local uint AES0[256], AES1[256], AES2[256], AES3[256]; + + const ulong gIdx = getIdx(); + + for(int i = get_local_id(0); i < 256; i += WORKSIZE) + { + const uint tmp = AES0_C[i]; + AES0[i] = tmp; + AES1[i] = rotate(tmp, 8U); + AES2[i] = rotate(tmp, 16U); + AES3[i] = rotate(tmp, 24U); + } + + barrier(CLK_LOCAL_MEM_FENCE); + +# if (COMP_MODE == 1) + // do not use early return here + if (gIdx < Threads) +# endif + { + states += 25 * gIdx; + +# if defined(__NV_CL_C_VERSION) + Scratchpad += gIdx * (ITERATIONS >> 2); +# else +# if (STRIDED_INDEX == 0) + Scratchpad += gIdx * (MEMORY >> 4); +# elif (STRIDED_INDEX == 1) + Scratchpad += gIdx; +# elif (STRIDED_INDEX == 2) + Scratchpad += get_group_id(0) * (MEMORY >> 4) * WORKSIZE + MEM_CHUNK * get_local_id(0); +# endif +# endif + + a[0] = states[0] ^ states[4]; + a[1] = states[1] ^ states[5]; + + b[0] = states[2] ^ states[6]; + b[1] = states[3] ^ states[7]; + b[2] = states[8] ^ states[10]; + b[3] = states[9] ^ states[11]; + } + + ulong2 bx0 = ((ulong2 *)b)[0]; + ulong2 bx1 = ((ulong2 *)b)[1]; + + mem_fence(CLK_LOCAL_MEM_FENCE); + +# ifdef __NV_CL_C_VERSION + __local uint16 scratchpad_line_buf[WORKSIZE]; + __local uint16* scratchpad_line = scratchpad_line_buf + get_local_id(0); +# define SCRATCHPAD_CHUNK(N) (*(__local uint4*)((__local uchar*)(scratchpad_line) + (idx1 ^ (N << 4)))) +# else +# if (STRIDED_INDEX == 0) +# define SCRATCHPAD_CHUNK(N) (*(__global uint4*)((__global uchar*)(Scratchpad) + (idx ^ (N << 4)))) +# elif (STRIDED_INDEX == 1) +# define SCRATCHPAD_CHUNK(N) (*(__global uint4*)((__global uchar*)(Scratchpad) + mul24(as_uint(idx ^ (N << 4)), Threads))) +# elif (STRIDED_INDEX == 2) +# define SCRATCHPAD_CHUNK(N) (*(__global uint4*)((__global uchar*)(Scratchpad) + (((idx ^ (N << 4)) % (MEM_CHUNK << 4)) + ((idx ^ (N << 4)) / (MEM_CHUNK << 4)) * WORKSIZE * (MEM_CHUNK << 4)))) +# endif +# endif + +# if (COMP_MODE == 1) + // do not use early return here + if (gIdx < Threads) +# endif + { + uint2 division_result = as_uint2(states[12]); + uint sqrt_result = as_uint2(states[13]).s0; + + #pragma unroll UNROLL_FACTOR + for(int i = 0; i < 0x60000; ++i) + { +# ifdef __NV_CL_C_VERSION + uint idx = a[0] & 0x1FFFC0; + uint idx1 = a[0] & 0x30; + + *scratchpad_line = *(__global uint16*)((__global uchar*)(Scratchpad) + idx); +# else + uint idx = a[0] & MASK; +# endif + + uint4 c = SCRATCHPAD_CHUNK(0); + c = AES_Round(AES0, AES1, AES2, AES3, c, ((uint4 *)a)[0]); + + { + const ulong2 chunk1 = as_ulong2(SCRATCHPAD_CHUNK(1)); + const ulong2 chunk2 = as_ulong2(SCRATCHPAD_CHUNK(2)); + const ulong2 chunk3 = as_ulong2(SCRATCHPAD_CHUNK(3)); + + SCRATCHPAD_CHUNK(1) = as_uint4(chunk3 + bx1); + SCRATCHPAD_CHUNK(2) = as_uint4(chunk1 + bx0); + SCRATCHPAD_CHUNK(3) = as_uint4(chunk2 + ((ulong2 *)a)[0]); + } + + SCRATCHPAD_CHUNK(0) = as_uint4(bx0) ^ c; + +# ifdef __NV_CL_C_VERSION + *(__global uint16*)((__global uchar*)(Scratchpad) + idx) = *scratchpad_line; + + idx = as_ulong2(c).s0 & 0x1FFFC0; + idx1 = as_ulong2(c).s0 & 0x30; + + *scratchpad_line = *(__global uint16*)((__global uchar*)(Scratchpad) + idx); +# else + idx = as_ulong2(c).s0 & MASK; +# endif + + uint4 tmp = SCRATCHPAD_CHUNK(0); + + { + tmp.s0 ^= division_result.s0; + tmp.s1 ^= division_result.s1 ^ sqrt_result; + + division_result = fast_div_v2(as_ulong2(c).s1, (c.s0 + (sqrt_result << 1)) | 0x80000001UL); + sqrt_result = fast_sqrt_v2(as_ulong2(c).s0 + as_ulong(division_result)); + } + + ulong2 t; + t.s0 = mul_hi(as_ulong2(c).s0, as_ulong2(tmp).s0); + t.s1 = as_ulong2(c).s0 * as_ulong2(tmp).s0; + { + const ulong2 chunk1 = as_ulong2(SCRATCHPAD_CHUNK(1)) ^ t; + const ulong2 chunk2 = as_ulong2(SCRATCHPAD_CHUNK(2)); + t ^= chunk2; + const ulong2 chunk3 = as_ulong2(SCRATCHPAD_CHUNK(3)); + + SCRATCHPAD_CHUNK(1) = as_uint4(chunk3 + bx1); + SCRATCHPAD_CHUNK(2) = as_uint4(chunk1 + bx0); + SCRATCHPAD_CHUNK(3) = as_uint4(chunk2 + ((ulong2 *)a)[0]); + } + + a[1] += t.s1; + a[0] += t.s0; + + SCRATCHPAD_CHUNK(0) = ((uint4 *)a)[0]; + +# ifdef __NV_CL_C_VERSION + *(__global uint16*)((__global uchar*)(Scratchpad) + idx) = *scratchpad_line; +# endif + + ((uint4 *)a)[0] ^= tmp; + bx1 = bx0; + bx0 = as_ulong2(c); + } + +# undef SCRATCHPAD_CHUNK + } + mem_fence(CLK_GLOBAL_MEM_FENCE); +# endif +} + +)===" \ No newline at end of file From 789cc8249834302bed8fd184761784dfa02f5ba6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Wed, 6 Mar 2019 23:10:02 +0100 Subject: [PATCH 91/97] Cleanup --- src/amd/OclCache.cpp | 2 +- src/amd/OclGPU.cpp | 2 +- src/amd/opencl/cryptonight.cl | 3 --- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/amd/OclCache.cpp b/src/amd/OclCache.cpp index b4b5030a..54fcb1be 100644 --- a/src/amd/OclCache.cpp +++ b/src/amd/OclCache.cpp @@ -70,7 +70,7 @@ void OclCache::getOptions(xmrig::Algo algo, xmrig::Variant variant, const GpuCon { snprintf(options, options_size, "-DITERATIONS=%u -DMASK=%u -DWORKSIZE=%zu -DSTRIDED_INDEX=%d -DMEM_CHUNK_EXPONENT=%d -DCOMP_MODE=%d -DMEMORY=%zu " "-DALGO=%d -DUNROLL_FACTOR=%d -DOPENCL_DRIVER_MAJOR=%d -DWORKSIZE_GPU=%zu -cl-fp32-correctly-rounded-divide-sqrt", - xmrig::cn_select_iter(algo, variant), + xmrig::cn_select_iter(algo, xmrig::VARIANT_AUTO), xmrig::cn_select_mask(algo), ctx->workSize, ctx->stridedIndex, diff --git a/src/amd/OclGPU.cpp b/src/amd/OclGPU.cpp index baeba675..765e4d89 100644 --- a/src/amd/OclGPU.cpp +++ b/src/amd/OclGPU.cpp @@ -522,6 +522,7 @@ size_t InitOpenCL(const std::vector &contexts, xmrig::Config *conf ; std::string source_code(cryptonightCL); + source_code.append(cryptonightCL2); source_code = std::regex_replace(source_code, std::regex("XMRIG_INCLUDE_WOLF_AES"), wolfAesCL); source_code = std::regex_replace(source_code, std::regex("XMRIG_INCLUDE_WOLF_SKEIN"), wolfSkeinCL); source_code = std::regex_replace(source_code, std::regex("XMRIG_INCLUDE_JH"), jhCL); @@ -530,7 +531,6 @@ size_t InitOpenCL(const std::vector &contexts, xmrig::Config *conf source_code = std::regex_replace(source_code, std::regex("XMRIG_INCLUDE_FAST_INT_MATH_V2"), fastIntMathV2CL); source_code = std::regex_replace(source_code, std::regex("XMRIG_INCLUDE_FAST_DIV_HEAVY"), fastDivHeavyCL); source_code = std::regex_replace(source_code, std::regex("XMRIG_INCLUDE_CN_GPU"), cryptonight_gpu); - source_code = std::regex_replace(source_code, std::regex("XMRIG_INCLUDE_CRYPTONIGHT2"), cryptonightCL2); for (size_t i = 0; i < num_gpus; ++i) { if (contexts[i]->stridedIndex == 2 && (contexts[i]->rawIntensity % contexts[i]->workSize) != 0) { diff --git a/src/amd/opencl/cryptonight.cl b/src/amd/opencl/cryptonight.cl index 4d8b6a34..7b616297 100644 --- a/src/amd/opencl/cryptonight.cl +++ b/src/amd/opencl/cryptonight.cl @@ -409,9 +409,6 @@ inline ulong getIdx() //#include "opencl/cryptonight_gpu.cl" XMRIG_INCLUDE_CN_GPU -//#include "opencl/cryptonight2.cl" -XMRIG_INCLUDE_CRYPTONIGHT2 - #define mix_and_propagate(xin) (xin)[(get_local_id(1)) % 8][get_local_id(0)] ^ (xin)[(get_local_id(1) + 1) % 8][get_local_id(0)] __attribute__((reqd_work_group_size(8, 8, 1))) From 414cb8c3bd9b3eb955072667a7eebdfe5f4fe598 Mon Sep 17 00:00:00 2001 From: BenDr0id Date: Thu, 7 Mar 2019 08:39:54 +0100 Subject: [PATCH 92/97] Fixed compile issue --- src/amd/opencl/cryptonight.cl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/amd/opencl/cryptonight.cl b/src/amd/opencl/cryptonight.cl index 7b616297..3114e8bc 100644 --- a/src/amd/opencl/cryptonight.cl +++ b/src/amd/opencl/cryptonight.cl @@ -78,7 +78,6 @@ inline int amd_bfe(const uint src0, const uint offset, const uint width) } #endif -XMRIG_INCLUDE_CRYPTONIGHT_CL2 //#include "opencl/wolf-aes.cl" XMRIG_INCLUDE_WOLF_AES //#include "opencl/wolf-skein.cl" From 05986a54e8af802bfd77a12e1a3f415832e9d65f Mon Sep 17 00:00:00 2001 From: BenDr0id Date: Thu, 7 Mar 2019 09:02:13 +0100 Subject: [PATCH 93/97] Moved more kernels to 2nd file --- src/amd/opencl/cryptonight.cl | 83 ---------------------------------- src/amd/opencl/cryptonight2.cl | 83 ++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 83 deletions(-) diff --git a/src/amd/opencl/cryptonight.cl b/src/amd/opencl/cryptonight.cl index 3114e8bc..c358936e 100644 --- a/src/amd/opencl/cryptonight.cl +++ b/src/amd/opencl/cryptonight.cl @@ -1392,89 +1392,6 @@ __kernel void cn1(__global uint4 *Scratchpad, __global ulong *states, uint varia )===" R"===( -__attribute__((reqd_work_group_size(WORKSIZE, 1, 1))) -__kernel void cn1_xfh(__global uint4 *Scratchpad, __global ulong *states, uint variant, __global ulong *input, uint Threads) -{ - ulong a[2], b[2]; - __local uint AES0[256], AES1[256]; - - const ulong gIdx = getIdx(); - - for (int i = get_local_id(0); i < 256; i += WORKSIZE) { - const uint tmp = AES0_C[i]; - AES0[i] = tmp; - AES1[i] = rotate(tmp, 8U); - } - - barrier(CLK_LOCAL_MEM_FENCE); - - uint4 b_x; -# if (COMP_MODE == 1) - // do not use early return here - if (gIdx < Threads) -# endif - { - states += 25 * gIdx; -# if (STRIDED_INDEX == 0) - Scratchpad += gIdx * (MEMORY >> 4); -# elif (STRIDED_INDEX == 1) - Scratchpad += gIdx; -# elif(STRIDED_INDEX == 2) - Scratchpad += get_group_id(0) * (MEMORY >> 4) * WORKSIZE + MEM_CHUNK * get_local_id(0); -# endif - - a[0] = states[0] ^ states[4]; - b[0] = states[2] ^ states[6]; - a[1] = states[1] ^ states[5]; - b[1] = states[3] ^ states[7]; - - b_x = ((uint4 *)b)[0]; - } - - mem_fence(CLK_LOCAL_MEM_FENCE); - -# if (COMP_MODE == 1) - // do not use early return here - if (gIdx < Threads) -# endif - { - uint idx0 = a[0]; - - #pragma unroll UNROLL_FACTOR - for (int i = 0; i < 0x20000; ++i) { - ulong c[2]; - - ((uint4 *)c)[0] = Scratchpad[IDX((idx0 & MASK) >> 4)]; - ((uint4 *)c)[0] = AES_Round_Two_Tables(AES0, AES1, ((uint4 *)c)[0], ((uint4 *)a)[0]); - - Scratchpad[IDX((idx0 & MASK) >> 4)] = b_x ^ ((uint4 *)c)[0]; - - uint4 tmp; - tmp = Scratchpad[IDX((as_uint2(c[0]).s0 & MASK) >> 4)]; - - a[1] += c[0] * as_ulong2(tmp).s0; - a[0] += mul_hi(c[0], as_ulong2(tmp).s0); - - Scratchpad[IDX((as_uint2(c[0]).s0 & MASK) >> 4)] = ((uint4 *)a)[0]; - - ((uint4 *)a)[0] ^= tmp; - idx0 = a[0]; - - b_x = ((uint4 *)c)[0]; - - const long2 n = *((__global long2*)(Scratchpad + (IDX((idx0 & MASK) >> 4)))); - long q = fast_div_heavy(n.s0, as_int4(n).s2 | 0x5); - *((__global long*)(Scratchpad + (IDX((idx0 & MASK) >> 4)))) = n.s0 ^ q; - - idx0 = (~as_int4(n).s2) ^ q; - } - } - mem_fence(CLK_GLOBAL_MEM_FENCE); -} - -)===" -R"===( - __attribute__((reqd_work_group_size(WORKSIZE, 1, 1))) __kernel void cn1_xao(__global uint4 *Scratchpad, __global ulong *states, uint variant, __global ulong *input, uint Threads) { diff --git a/src/amd/opencl/cryptonight2.cl b/src/amd/opencl/cryptonight2.cl index 8569037b..dff58c2e 100644 --- a/src/amd/opencl/cryptonight2.cl +++ b/src/amd/opencl/cryptonight2.cl @@ -1,4 +1,87 @@ R"===( + +__attribute__((reqd_work_group_size(WORKSIZE, 1, 1))) +__kernel void cn1_xfh(__global uint4 *Scratchpad, __global ulong *states, uint variant, __global ulong *input, uint Threads) +{ + ulong a[2], b[2]; + __local uint AES0[256], AES1[256]; + + const ulong gIdx = getIdx(); + + for (int i = get_local_id(0); i < 256; i += WORKSIZE) { + const uint tmp = AES0_C[i]; + AES0[i] = tmp; + AES1[i] = rotate(tmp, 8U); + } + + barrier(CLK_LOCAL_MEM_FENCE); + + uint4 b_x; +# if (COMP_MODE == 1) + // do not use early return here + if (gIdx < Threads) +# endif + { + states += 25 * gIdx; +# if (STRIDED_INDEX == 0) + Scratchpad += gIdx * (MEMORY >> 4); +# elif (STRIDED_INDEX == 1) + Scratchpad += gIdx; +# elif(STRIDED_INDEX == 2) + Scratchpad += get_group_id(0) * (MEMORY >> 4) * WORKSIZE + MEM_CHUNK * get_local_id(0); +# endif + + a[0] = states[0] ^ states[4]; + b[0] = states[2] ^ states[6]; + a[1] = states[1] ^ states[5]; + b[1] = states[3] ^ states[7]; + + b_x = ((uint4 *)b)[0]; + } + + mem_fence(CLK_LOCAL_MEM_FENCE); + +# if (COMP_MODE == 1) + // do not use early return here + if (gIdx < Threads) +# endif + { + uint idx0 = a[0]; + + #pragma unroll UNROLL_FACTOR + for (int i = 0; i < 0x20000; ++i) { + ulong c[2]; + + ((uint4 *)c)[0] = Scratchpad[IDX((idx0 & MASK) >> 4)]; + ((uint4 *)c)[0] = AES_Round_Two_Tables(AES0, AES1, ((uint4 *)c)[0], ((uint4 *)a)[0]); + + Scratchpad[IDX((idx0 & MASK) >> 4)] = b_x ^ ((uint4 *)c)[0]; + + uint4 tmp; + tmp = Scratchpad[IDX((as_uint2(c[0]).s0 & MASK) >> 4)]; + + a[1] += c[0] * as_ulong2(tmp).s0; + a[0] += mul_hi(c[0], as_ulong2(tmp).s0); + + Scratchpad[IDX((as_uint2(c[0]).s0 & MASK) >> 4)] = ((uint4 *)a)[0]; + + ((uint4 *)a)[0] ^= tmp; + idx0 = a[0]; + + b_x = ((uint4 *)c)[0]; + + const long2 n = *((__global long2*)(Scratchpad + (IDX((idx0 & MASK) >> 4)))); + long q = fast_div_heavy(n.s0, as_int4(n).s2 | 0x5); + *((__global long*)(Scratchpad + (IDX((idx0 & MASK) >> 4)))) = n.s0 ^ q; + + idx0 = (~as_int4(n).s2) ^ q; + } + } + mem_fence(CLK_GLOBAL_MEM_FENCE); +} + +)===" +R"===( __attribute__((reqd_work_group_size(WORKSIZE, 1, 1))) __kernel void cn1_double(__global uint4 *Scratchpad, __global ulong *states, uint variant, __global ulong *input, uint Threads) { From 82c88c433bef4cc1516d99bc735099c5f86cf1da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Mon, 18 Mar 2019 16:53:30 +0100 Subject: [PATCH 94/97] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8c3c560d..68aa6bf4 100644 --- a/README.md +++ b/README.md @@ -130,7 +130,7 @@ XMR: `4BEn3sSa2SsHBcwa9dNdKnGvvbyHPABr2JzoY7omn7DA2hPv84pVFvwDrcwMCWgz3dQVcrkw3 AEON: `Wmtm4S2cQ8uEBBAVjvbiaVAPv2d6gA1mAUmBmjna4VF7VixLxLRUYag5cvsym3WnuzdJ9zvhQ3Xwa8gWxPDPRfcQ3AUkYra3W` -BTC: `128qLZCaGdoWhBTfaS7rytpbvG4mNTyAQm` +BTC: `3Gwq9tveCZtLAiXX7mxhjbrh38GPx1iXdB` ##### xmrig: XMR: `48edfHu7V9Z84YzzMa6fUueoELZ9ZRXq9VetWzYGzKt52XU5xvqgzYnDK9URnRoJMk1j8nLwEVsaSWJ4fhdUyZijBGUicoD` From b3924c1dab5337a2bbc1b210a9ba918bb16f3f61 Mon Sep 17 00:00:00 2001 From: BenDr0id Date: Mon, 29 Apr 2019 17:03:42 +0200 Subject: [PATCH 95/97] Integrated UPX2 / cn-extremelite --- CHANGELOG.md | 3 ++ CMakeLists.txt | 5 +++ src/App.cpp | 2 +- src/amd/OclCLI.cpp | 10 ++++- src/amd/OclCLI.h | 3 +- src/amd/OclGPU.cpp | 1 + src/amd/opencl/cryptonight.cl | 1 + src/amd/opencl/cryptonight2.cl | 7 +++- src/base/kernel/Entry.cpp | 2 +- src/common/crypto/Algorithm.cpp | 18 ++++++++- src/common/xmrig.h | 2 + src/core/Config.cpp | 4 ++ src/core/usage.h | 8 ++++ src/crypto/CryptoNight.cpp | 54 +++++++++++++++++++++++++-- src/crypto/CryptoNight_constants.h | 17 +++++++++ src/crypto/CryptoNight_test.h | 11 +++++- src/crypto/CryptoNight_x86.h | 10 ++--- src/net/strategies/DonateStrategy.cpp | 8 +++- src/version.h | 4 +- 19 files changed, 151 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 063700d3..7568f468 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# 1.9.3 +- Integrated UPX2 variant (algo: "cryptonight-extremelite", variant: "upx2" OR variant: "auto") +- Integrated merged templates and replace of @WORKER-ID@ in template assignment # 1.9.1 - Rebase from XMRig-amd 2.14.0 - Integrated Monero CN-R variant so called CNv4, aka CN-R, aka CNv5, aka Cryptonight-R (algo: "cryptonight", variant: "r" / "auto") diff --git a/CMakeLists.txt b/CMakeLists.txt index 42c6db7a..4a84e648 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,6 +13,7 @@ option(WITH_TLS "TLS support" ON) option(BUILD_STATIC "Build static binary" OFF) option(WITH_CN_PICO "CryptoNight-Pico support" ON) option(WITH_CN_GPU "CryptoNight-GPU support" ON) +option(WITH_CN_EXTREMELITE "CryptoNight-Extremelite support" ON) option(WITH_ASM "Enable ASM PoW implementations" ON) option(STRICT_CACHE "Enable strict checks for OpenCL cache" ON) option(ARM_TARGET "Force use specific ARM target 8 or 7" 0) @@ -291,6 +292,10 @@ if (NOT WITH_CN_PICO) add_definitions(/DXMRIG_NO_CN_ULTRALITE) endif() +if (NOT WITH_CN_EXTREMELITE) + add_definitions(/DXMRIG_NO_CN_EXTREMELITE) +endif() + if (WITH_HTTPD) find_package(MHD) diff --git a/src/App.cpp b/src/App.cpp index c968557a..8dfc303f 100644 --- a/src/App.cpp +++ b/src/App.cpp @@ -97,7 +97,7 @@ xmrig::App::~App() int xmrig::App::exec() { if (!m_controller->isReady()) { - return 2; + return 1; } auto startCmd = m_controller->config()->startCmd(); diff --git a/src/amd/OclCLI.cpp b/src/amd/OclCLI.cpp index b4fc2126..940eb4aa 100644 --- a/src/amd/OclCLI.cpp +++ b/src/amd/OclCLI.cpp @@ -192,6 +192,10 @@ int OclCLI::getHints(const GpuContext &ctx, xmrig::Config *config) const hints |= Ultralite; } + if (config->algorithm().algo() == xmrig::CRYPTONIGHT_EXTREMELITE) { + hints |= Extremelite; + } + if (ctx.vendor == xmrig::OCL_VENDOR_AMD && (ctx.name == "gfx901" || ctx.name == "gfx904" || ctx.name == "gfx900" || @@ -248,7 +252,7 @@ void OclCLI::parse(std::vector &vector, const char *arg) const size_t OclCLI::getMaxThreads(const GpuContext &ctx, xmrig::Algo algo, int hints) { - const size_t ratio = (algo == xmrig::CRYPTONIGHT_LITE || algo == xmrig::CRYPTONIGHT_ULTRALITE) ? 2u : 1u; + const size_t ratio = (algo == xmrig::CRYPTONIGHT_LITE || algo == xmrig::CRYPTONIGHT_ULTRALITE || algo == xmrig::CRYPTONIGHT_EXTREMELITE) ? 2u : 1u; if (ctx.vendor == xmrig::OCL_VENDOR_INTEL) { return ratio * ctx.computeUnits * 8; } @@ -291,6 +295,10 @@ size_t OclCLI::worksizeByHints(int hints) return 64; } + if (hints & Extremelite) { + return 64; + } + if (hints & CNv2) { return 16; } diff --git a/src/amd/OclCLI.h b/src/amd/OclCLI.h index de6ce211..6ce65200 100644 --- a/src/amd/OclCLI.h +++ b/src/amd/OclCLI.h @@ -64,7 +64,8 @@ class OclCLI DoubleThreads = 1, Vega = 2, CNv2 = 4, - Ultralite = 8 + Ultralite = 8, + Extremelite = 16 }; inline bool isEmpty() const { return m_devices.empty() && m_intensity.empty(); } diff --git a/src/amd/OclGPU.cpp b/src/amd/OclGPU.cpp index 765e4d89..1eeed8f1 100644 --- a/src/amd/OclGPU.cpp +++ b/src/amd/OclGPU.cpp @@ -113,6 +113,7 @@ inline static int cn1KernelOffset(xmrig::Variant variant) return 14; case xmrig::VARIANT_RWZ: + case xmrig::VARIANT_UPX2: return 15; case xmrig::VARIANT_ZELERIUS: diff --git a/src/amd/opencl/cryptonight.cl b/src/amd/opencl/cryptonight.cl index c358936e..2e9e67e6 100644 --- a/src/amd/opencl/cryptonight.cl +++ b/src/amd/opencl/cryptonight.cl @@ -116,6 +116,7 @@ XMRIG_INCLUDE_FAST_DIV_HEAVY #define CRYPTONIGHT_LITE 1 /* CryptoNight (1 MB) */ #define CRYPTONIGHT_HEAVY 2 /* CryptoNight (4 MB) */ #define CRYPTONIGHT_ULTRALITE 3 /* CryptoNight (256 KB) */ +#define CRYPTONIGHT_EXTREMELITE 4 /* CryptoNight (128 KB) */ #if defined(__NV_CL_C_VERSION) && STRIDED_INDEX != 0 # undef STRIDED_INDEX diff --git a/src/amd/opencl/cryptonight2.cl b/src/amd/opencl/cryptonight2.cl index dff58c2e..f4bc4f1e 100644 --- a/src/amd/opencl/cryptonight2.cl +++ b/src/amd/opencl/cryptonight2.cl @@ -244,7 +244,7 @@ R"===( __attribute__((reqd_work_group_size(WORKSIZE, 1, 1))) __kernel void cn1_rwz(__global uint4 *Scratchpad, __global ulong *states, uint variant, __global ulong *input, uint Threads) { -# if (ALGO == CRYPTONIGHT) +# if (ALGO == CRYPTONIGHT || ALGO == CRYPTONIGHT_EXTREMELITE) ulong a[2], b[4]; __local uint AES0[256], AES1[256], AES2[256], AES3[256]; const ulong gIdx = getIdx(); @@ -305,7 +305,12 @@ __kernel void cn1_rwz(__global uint4 *Scratchpad, __global ulong *states, uint v uint2 division_result = as_uint2(states[12]); uint sqrt_result = as_uint2(states[13]).s0; #pragma unroll UNROLL_FACTOR +# if (ALGO == CRYPTONIGHT) for(int i = 0; i < 0x60000; ++i) +# endif +# if (ALGO == CRYPTONIGHT_EXTREMELITE) + for(int i = 0; i < 0x4000; ++i) +# endif { # ifdef __NV_CL_C_VERSION uint idx = a[0] & 0x1FFFC0; diff --git a/src/base/kernel/Entry.cpp b/src/base/kernel/Entry.cpp index abdfd1e1..c2ce69b9 100644 --- a/src/base/kernel/Entry.cpp +++ b/src/base/kernel/Entry.cpp @@ -107,7 +107,7 @@ int xmrig::Entry::exec(const Process &, Id id) switch (id) { case Usage: printf(usage); - return 0; + return 1; case Version: return showVersion(); diff --git a/src/common/crypto/Algorithm.cpp b/src/common/crypto/Algorithm.cpp index 25c79d91..7df9190c 100644 --- a/src/common/crypto/Algorithm.cpp +++ b/src/common/crypto/Algorithm.cpp @@ -104,6 +104,12 @@ static AlgoData const algorithms[] = { { "cryptonight_turtle", "cn_turtle", xmrig::CRYPTONIGHT_ULTRALITE, xmrig::VARIANT_TURTLE }, # endif +# ifndef XMRIG_NO_CN_EXTREMELITE + { "cryptonight-extremelite", "cn-extremelite", xmrig::CRYPTONIGHT_EXTREMELITE, xmrig::VARIANT_UPX2 }, + { "cryptonight-extremelite/upx2", "cn-extremelite/upx2", xmrig::CRYPTONIGHT_EXTREMELITE, xmrig::VARIANT_UPX2 }, + { "cryptonight-upx2", "cn-upx2", xmrig::CRYPTONIGHT_EXTREMELITE, xmrig::VARIANT_UPX2 }, +# endif + # ifndef XMRIG_NO_CN_GPU { "cryptonight/gpu", "cn/gpu", xmrig::CRYPTONIGHT, xmrig::VARIANT_GPU }, # endif @@ -151,7 +157,8 @@ static const char *variants[] = { "r", "rwz", "zelerius", - "double" + "double", + "upx2" }; @@ -233,6 +240,11 @@ void xmrig::Algorithm::parseVariant(const char *variant) return; } + if (m_algo == xmrig::CRYPTONIGHT_EXTREMELITE) { + m_variant = VARIANT_UPX2; + return; + } + for (size_t i = 0; i < ARRAY_SIZE(variants); i++) { if (strcasecmp(variant, variants[i]) == 0) { m_variant = static_cast(i); @@ -294,6 +306,10 @@ void xmrig::Algorithm::setAlgo(Algo algo) if (m_algo == CRYPTONIGHT_ULTRALITE && m_variant == VARIANT_AUTO) { m_variant = xmrig::VARIANT_TURTLE; } + + if (m_algo == CRYPTONIGHT_EXTREMELITE && m_variant == VARIANT_AUTO) { + m_variant = xmrig::VARIANT_UPX2; + } } diff --git a/src/common/xmrig.h b/src/common/xmrig.h index 4d71cae2..c235e07f 100644 --- a/src/common/xmrig.h +++ b/src/common/xmrig.h @@ -36,6 +36,7 @@ enum Algo { CRYPTONIGHT_LITE, /* CryptoNight (1 MB) */ CRYPTONIGHT_HEAVY, /* CryptoNight (4 MB) */ CRYPTONIGHT_ULTRALITE, /* CryptoNight (256 KB) */ + CRYPTONIGHT_EXTREMELITE, /* CryptoNight (128 KB) */ ALGO_MAX }; @@ -81,6 +82,7 @@ enum Variant { VARIANT_RWZ = 16, // Cryptonight (2) variant 3/4 Iterations + reverse shuffle rwz (Graft) VARIANT_ZELERIUS = 17, // Cryptonight (2) variant 3/4 Iterations (Zelerius) VARIANT_DOUBLE = 18, // Cryptonight (2) variant double (XCash) + VARIANT_UPX2 = 19, // Modified CryptoNight-Extremelite variant upx2 VARIANT_MAX }; diff --git a/src/core/Config.cpp b/src/core/Config.cpp index ce10d6a0..82620b70 100644 --- a/src/core/Config.cpp +++ b/src/core/Config.cpp @@ -75,6 +75,10 @@ bool xmrig::Config::isCNv2() const return true; } + if (algorithm().algo() == CRYPTONIGHT_EXTREMELITE) { + return true; + } + if (algorithm().algo() != CRYPTONIGHT) { return false; } diff --git a/src/core/usage.h b/src/core/usage.h index 19c5fcb7..81f9ba50 100644 --- a/src/core/usage.h +++ b/src/core/usage.h @@ -41,6 +41,14 @@ Options:\n\ "\ cryptonight-lite\n" #endif +#ifndef XMRIG_NO_CN_ULTRALITE + "\ + cryptonight-ultralite\n" +#endif +#ifndef XMRIG_NO_CN_EXTREMELITE + "\ + cryptonight-extremelite\n" +#endif #ifndef XMRIG_NO_SUMO "\ cryptonight-heavy\n" diff --git a/src/crypto/CryptoNight.cpp b/src/crypto/CryptoNight.cpp index c80936be..bb26ceb1 100644 --- a/src/crypto/CryptoNight.cpp +++ b/src/crypto/CryptoNight.cpp @@ -218,6 +218,8 @@ CryptoNight::cn_hash_fun CryptoNight::fn(xmrig::Algo algorithm, xmrig::AlgoVerif cryptonight_single_hash, cryptonight_single_hash, + nullptr, nullptr, // VARIANT UPX2 + # ifndef XMRIG_NO_AEON cryptonight_single_hash, cryptonight_single_hash, @@ -245,6 +247,8 @@ CryptoNight::cn_hash_fun CryptoNight::fn(xmrig::Algo algorithm, xmrig::AlgoVerif nullptr, nullptr, // VARIANT_RWZ nullptr, nullptr, // VARIANT_ZELERIUS nullptr, nullptr, // VARIANT_DOUBLE + nullptr, nullptr, // VARIANT UPX2 + # else nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, @@ -255,7 +259,7 @@ CryptoNight::cn_hash_fun CryptoNight::fn(xmrig::Algo algorithm, xmrig::AlgoVerif nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, # endif # ifndef XMRIG_NO_SUMO @@ -286,6 +290,7 @@ CryptoNight::cn_hash_fun CryptoNight::fn(xmrig::Algo algorithm, xmrig::AlgoVerif nullptr, nullptr, // VARIANT_RWZ nullptr, nullptr, // VARIANT_ZELERIUS nullptr, nullptr, // VARIANT_DOUBLE + nullptr, nullptr, // VARIANT UPX2 # else nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, @@ -296,7 +301,7 @@ CryptoNight::cn_hash_fun CryptoNight::fn(xmrig::Algo algorithm, xmrig::AlgoVerif nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, # endif # ifndef XMRIG_NO_CN_ULTRALITE @@ -326,6 +331,43 @@ CryptoNight::cn_hash_fun CryptoNight::fn(xmrig::Algo algorithm, xmrig::AlgoVerif nullptr, nullptr, // VARIANT_RWZ nullptr, nullptr, // VARIANT_ZELERIUS nullptr, nullptr, // VARIANT_DOUBLE + nullptr, nullptr, // VARIANT UPX2 +#else + nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, +# endif + +# ifndef XMRIG_NO_CN_EXTREMELITE + nullptr, nullptr, // VARIANT_0 + nullptr, nullptr, // VARIANT_1 + nullptr, nullptr, // VARIANT_TUBE + nullptr, nullptr, // VARIANT_XTL + nullptr, nullptr, // VARIANT_MSR + nullptr, nullptr, // VARIANT_XHV + nullptr, nullptr, // VARIANT_XAO + nullptr, nullptr, // VARIANT_RTO + nullptr, nullptr, // VARIANT_2 + nullptr, nullptr, // VARIANT_XFH + nullptr, nullptr, // VARIANT_FAST_2 + nullptr, nullptr, // VARIANT_UPX + nullptr, nullptr, // VARIANT_TURTLE + nullptr, nullptr, // VARIANT_GPU + nullptr, nullptr, // VARIANT_WOW + nullptr, nullptr, // VARIANT_4 + nullptr, nullptr, // VARIANT_RWZ + nullptr, nullptr, // VARIANT_ZELERIUS + nullptr, nullptr, // VARIANT_DOUBLE + + cryptonight_single_hash, + cryptonight_single_hash, #else nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, @@ -336,7 +378,7 @@ CryptoNight::cn_hash_fun CryptoNight::fn(xmrig::Algo algorithm, xmrig::AlgoVerif nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr # endif }; @@ -411,6 +453,12 @@ bool CryptoNight::selfTest() { } # endif +# ifndef XMRIG_NO_CN_EXTREMELITE + if (m_algorithm == xmrig::CRYPTONIGHT_EXTREMELITE) { + return verify(VARIANT_UPX2, test_output_upx2); + } +# endif + return false; } diff --git a/src/crypto/CryptoNight_constants.h b/src/crypto/CryptoNight_constants.h index e2de25b1..2aa5d232 100644 --- a/src/crypto/CryptoNight_constants.h +++ b/src/crypto/CryptoNight_constants.h @@ -45,6 +45,7 @@ constexpr const uint32_t CRYPTONIGHT_LITE_UPX_ITER = 0x20000; constexpr const uint32_t CRYPTONIGHT_TRTL_ITER = 0x10000; constexpr const uint32_t CRYPTONIGHT_DOUBLE_ITER = 0x100000; constexpr const uint32_t CRYPTONIGHT_WALTZ_ITER = 0x60000; +constexpr const uint32_t CRYPTONIGHT_UPX2_ITER = 0x4000; constexpr const size_t CRYPTONIGHT_MEMORY = 2 * 1024 * 1024; constexpr const uint32_t CRYPTONIGHT_MASK = 0x1FFFF0; @@ -61,11 +62,15 @@ constexpr const uint32_t CRYPTONIGHT_HEAVY_MASK = 0x3FFFF0; constexpr const size_t CRYPTONIGHT_ULTRALITE_MEMORY = 256 * 1024; constexpr const uint32_t CRYPTONIGHT_ULTRALITE_MASK = 0x1FFF0; +constexpr const size_t CRYPTONIGHT_EXTREMELITE_MEMORY = 128 * 1024; +constexpr const uint32_t CRYPTONIGHT_EXTREMELITE_MASK = 0x1FFF0; + template inline constexpr size_t cn_select_memory() { return 0; } template<> inline constexpr size_t cn_select_memory() { return CRYPTONIGHT_MEMORY; } template<> inline constexpr size_t cn_select_memory() { return CRYPTONIGHT_LITE_MEMORY; } template<> inline constexpr size_t cn_select_memory() { return CRYPTONIGHT_HEAVY_MEMORY; } template<> inline constexpr size_t cn_select_memory() { return CRYPTONIGHT_ULTRALITE_MEMORY; } +template<> inline constexpr size_t cn_select_memory() { return CRYPTONIGHT_EXTREMELITE_MEMORY; } inline size_t cn_select_memory(Algo algorithm) @@ -84,6 +89,9 @@ inline size_t cn_select_memory(Algo algorithm) case CRYPTONIGHT_ULTRALITE: return CRYPTONIGHT_ULTRALITE_MEMORY; + case CRYPTONIGHT_EXTREMELITE: + return CRYPTONIGHT_EXTREMELITE_MEMORY; + default: break; } @@ -97,6 +105,7 @@ template<> inline constexpr uint32_t cn_select_mask() { retur template<> inline constexpr uint32_t cn_select_mask() { return CRYPTONIGHT_LITE_MASK; } template<> inline constexpr uint32_t cn_select_mask() { return CRYPTONIGHT_HEAVY_MASK; } template<> inline constexpr uint32_t cn_select_mask() { return CRYPTONIGHT_ULTRALITE_MASK; } +template<> inline constexpr uint32_t cn_select_mask() { return CRYPTONIGHT_EXTREMELITE_MASK; } inline uint32_t cn_select_mask(Algo algorithm) @@ -115,6 +124,9 @@ inline uint32_t cn_select_mask(Algo algorithm) case CRYPTONIGHT_ULTRALITE: return CRYPTONIGHT_ULTRALITE_MASK; + case CRYPTONIGHT_EXTREMELITE: + return CRYPTONIGHT_EXTREMELITE_MASK; + default: break; } @@ -145,6 +157,7 @@ template<> inline constexpr uint32_t cn_select_iter inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_HALF_ITER; } template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_HALF_ITER; } template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_TRTL_ITER; } +template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_UPX2_ITER; } template<> inline constexpr uint32_t cn_select_iter() { return CRYPTONIGHT_GPU_ITER; } @@ -192,6 +205,9 @@ inline uint32_t cn_select_iter(Algo algorithm, Variant variant) case CRYPTONIGHT_ULTRALITE: return CRYPTONIGHT_TRTL_ITER; + case CRYPTONIGHT_EXTREMELITE: + return CRYPTONIGHT_UPX2_ITER; + default: break; } @@ -220,6 +236,7 @@ template<> inline constexpr Variant cn_base_variant() { return VA template<> inline constexpr Variant cn_base_variant() { return VARIANT_2; } template<> inline constexpr Variant cn_base_variant() { return VARIANT_2; } template<> inline constexpr Variant cn_base_variant() { return VARIANT_2; } +template<> inline constexpr Variant cn_base_variant() { return VARIANT_2; } template inline constexpr bool cn_is_cryptonight_r() { return false; } diff --git a/src/crypto/CryptoNight_test.h b/src/crypto/CryptoNight_test.h index 773f4889..97becc9a 100644 --- a/src/crypto/CryptoNight_test.h +++ b/src/crypto/CryptoNight_test.h @@ -322,7 +322,6 @@ const static uint8_t test_output_turtle[32] = { 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 }; - #endif unsigned char hf_hex2bin(char c, bool &err); @@ -344,5 +343,15 @@ const static uint8_t test_output_gpu[160] = { }; #endif +#ifndef XMRIG_NO_CN_EXTREMELITE +// 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 + #endif /* XMRIG_CRYPTONIGHT_TEST_H */ diff --git a/src/crypto/CryptoNight_x86.h b/src/crypto/CryptoNight_x86.h index 543dece6..beb546de 100644 --- a/src/crypto/CryptoNight_x86.h +++ b/src/crypto/CryptoNight_x86.h @@ -460,7 +460,7 @@ 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) { if (BASE == xmrig::VARIANT_2) { - VARIANT2_SHUFFLE(l, idx, ax0, bx0, bx1, cx, (VARIANT == xmrig::VARIANT_RWZ ? 1 : 0)); + VARIANT2_SHUFFLE(l, idx, ax0, bx0, bx1, cx, (VARIANT == xmrig::VARIANT_RWZ || VARIANT == xmrig::VARIANT_UPX2? 1 : 0)); _mm_store_si128((__m128i *)mem_out, _mm_xor_si128(bx0, cx)); } else { __m128i tmp = _mm_xor_si128(bx0, cx); @@ -560,7 +560,7 @@ inline void cryptonight_single_hash(const uint8_t *__restrict__ input, size_t si if (VARIANT == xmrig::VARIANT_4) { VARIANT2_SHUFFLE(l0, idx0 & MASK, ax0, bx0, bx1, cx, 0); } else { - VARIANT2_SHUFFLE2(l0, idx0 & MASK, ax0, bx0, bx1, hi, lo, (VARIANT == xmrig::VARIANT_RWZ ? 1 : 0)); + VARIANT2_SHUFFLE2(l0, idx0 & MASK, ax0, bx0, bx1, hi, lo, (VARIANT == xmrig::VARIANT_RWZ || VARIANT == xmrig::VARIANT_UPX2 ? 1 : 0)); } } @@ -898,7 +898,7 @@ inline void cryptonight_double_hash(const uint8_t *__restrict__ input, size_t si if (VARIANT == xmrig::VARIANT_4) { VARIANT2_SHUFFLE(l0, idx0 & MASK, ax0, bx00, bx01, cx0, 0); } else { - VARIANT2_SHUFFLE2(l0, idx0 & MASK, ax0, bx00, bx01, hi, lo, (VARIANT == xmrig::VARIANT_RWZ ? 1 : 0)); + VARIANT2_SHUFFLE2(l0, idx0 & MASK, ax0, bx00, bx01, hi, lo, (VARIANT == xmrig::VARIANT_RWZ || VARIANT == xmrig::VARIANT_UPX2 ? 1 : 0)); } } @@ -954,7 +954,7 @@ inline void cryptonight_double_hash(const uint8_t *__restrict__ input, size_t si if (VARIANT == xmrig::VARIANT_4) { VARIANT2_SHUFFLE(l1, idx1 & MASK, ax1, bx10, bx11, cx1, 0); } else { - VARIANT2_SHUFFLE2(l1, idx1 & MASK, ax1, bx10, bx11, hi, lo, (VARIANT == xmrig::VARIANT_RWZ ? 1 : 0)); + VARIANT2_SHUFFLE2(l1, idx1 & MASK, ax1, bx10, bx11, hi, lo, (VARIANT == xmrig::VARIANT_RWZ || VARIANT == xmrig::VARIANT_UPX2 ? 1 : 0)); } } @@ -1058,7 +1058,7 @@ inline void cryptonight_double_hash(const uint8_t *__restrict__ input, size_t si if (VARIANT == xmrig::VARIANT_4) { \ VARIANT2_SHUFFLE(l, idx & MASK, a, b0, b1, c, 0); \ } else { \ - VARIANT2_SHUFFLE2(l, idx & MASK, a, b0, b1, hi, lo, (VARIANT == xmrig::VARIANT_RWZ ? 1 : 0)); \ + VARIANT2_SHUFFLE2(l, idx & MASK, a, b0, b1, hi, lo, (VARIANT == xmrig::VARIANT_RWZ || VARIANT == xmrig::VARIANT_UPX2 ? 1 : 0)); \ } \ } \ if (VARIANT == xmrig::VARIANT_4) { \ diff --git a/src/net/strategies/DonateStrategy.cpp b/src/net/strategies/DonateStrategy.cpp index c817ead6..39dfa05f 100644 --- a/src/net/strategies/DonateStrategy.cpp +++ b/src/net/strategies/DonateStrategy.cpp @@ -61,16 +61,20 @@ xmrig::DonateStrategy::DonateStrategy(int level, const char *user, Algo algo, IS m_pools.push_back(Pool("donate2.graef.in", 1080, userId, nullptr, true, false, true)); } else if (algo == xmrig::Algo::CRYPTONIGHT_ULTRALITE) { m_pools.push_back(Pool("donate2.graef.in", 8090, userId, nullptr, true, false, true)); + } else if (algo == xmrig::Algo::CRYPTONIGHT_EXTREMELITE) { + m_pools.push_back(Pool("donate2.graef.in", 9091, userId, nullptr, true, false, true)); } else { m_pools.push_back(Pool("donate2.graef.in", 443, userId, nullptr, true, false, true)); } #else if (algo == xmrig::Algo::CRYPTONIGHT_HEAVY) { - m_pools.push_back(Pool("donate.graef.in", 8443, userId, nullptr, false, false, true)); + m_pools.push_back(Pool("donate2.graef.in", 9000, userId, nullptr, false, false, true)); } else if (algo == xmrig::Algo::CRYPTONIGHT_LITE) { - m_pools.push_back(Pool("donate.graef.in", 1080, userId, nullptr, false, false, true)); + m_pools.push_back(Pool("donate2.graef.in", 7000, userId, nullptr, false, false, true)); } else if (algo == xmrig::Algo::CRYPTONIGHT_ULTRALITE) { m_pools.push_back(Pool("donate2.graef.in", 8088, userId, nullptr, false, false, true)); + } else if (algo == xmrig::Algo::CRYPTONIGHT_EXTREMELITE) { + m_pools.push_back(Pool("donate2.graef.in", 8188, userId, nullptr, false, false, true)); } else { m_pools.push_back(Pool("donate2.graef.in", 80, userId, nullptr, false, false, true)); } diff --git a/src/version.h b/src/version.h index 7a83b35e..515e6d38 100644 --- a/src/version.h +++ b/src/version.h @@ -29,14 +29,14 @@ #define APP_NAME "XMRigCC-AMD" #define APP_COPYRIGHT "Copyright (C) 2017- BenDr0id" #define APP_DESC "XMRigCC-AMD OpenCL miner" -#define APP_VERSION "1.9.1 (based on XMRig)" +#define APP_VERSION "1.9.3 (based on XMRig)" #define APP_SITE "https://github.com/Bendr0id/xmrigCC-amd" #define APP_DOMAIN "" #define APP_KIND "amd" #define APP_VER_MAJOR 1 #define APP_VER_MINOR 9 -#define APP_VER_PATCH 1 +#define APP_VER_PATCH 3 #define TYPE_AMD_GPU From d760c1b95dccf101dd95975990254adfc6fdec25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Wed, 14 Aug 2019 20:14:30 +0200 Subject: [PATCH 96/97] Upgraded version --- CHANGELOG.md | 6 ++++++ src/version.h | 4 ++-- src/workers/HashrateMonitor.cpp | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7568f468..8272bb8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# 1.9.5 +- Rebase from XMRig-amd 2.15.4 (Fixed compatibility with recent AMD drivers (19.7.2), thanks @psychocrypt) +- Added possibility to delete templates #257 +- Added embedded config parsing #256 +- OSX Hugepages fix #250 +- Fixed non-merged template assignment # 1.9.3 - Integrated UPX2 variant (algo: "cryptonight-extremelite", variant: "upx2" OR variant: "auto") - Integrated merged templates and replace of @WORKER-ID@ in template assignment diff --git a/src/version.h b/src/version.h index dc5c7a4a..98635d2c 100644 --- a/src/version.h +++ b/src/version.h @@ -29,14 +29,14 @@ #define APP_NAME "XMRigCC-AMD" #define APP_COPYRIGHT "Copyright (C) 2017- BenDr0id" #define APP_DESC "XMRigCC-AMD OpenCL miner" -#define APP_VERSION "1.9.3 (based on XMRig)" +#define APP_VERSION "1.9.5 (based on XMRig 2.14.5)" #define APP_SITE "https://github.com/Bendr0id/xmrigCC-amd" #define APP_DOMAIN "" #define APP_KIND "amd" #define APP_VER_MAJOR 1 #define APP_VER_MINOR 9 -#define APP_VER_PATCH 3 +#define APP_VER_PATCH 5 #define TYPE_AMD_GPU diff --git a/src/workers/HashrateMonitor.cpp b/src/workers/HashrateMonitor.cpp index befcf969..88b38bc3 100644 --- a/src/workers/HashrateMonitor.cpp +++ b/src/workers/HashrateMonitor.cpp @@ -100,7 +100,7 @@ void HashrateMonitor::onTick(uv_timer_t* handle) { if (m_self) { if (!m_self->m_hashrates.empty()) { - if (m_self->m_connectionTime > 30) { + if (m_self->m_connectionTime > 120) { bool error = false; double totalHashrate = 0; size_t threadId = 0; From 1b9937a829fccdb2182c7652a4ff6b01d6571cce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ben=20Gr=C3=A4f?= Date: Mon, 27 Dec 2021 10:40:10 +0100 Subject: [PATCH 97/97] Update README.md --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index 68aa6bf4..1f50f9a6 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,17 @@ # XMRigCC-amd (OpenCL) + + + +# :warning::warning: **This repo is outdated PLEASE use the latest unified miner available here: [XMRigCC](https://github.com/Bendr0id/xmrigCC)**:warning::warning: + + + + + + + + :warning: **Confused by all the forks? Check the [Coin Configuration](https://github.com/Bendr0id/xmrigCC/wiki/Coin-configurations) guide.** :bulb: **This is the AMD GPU (OpenCL) variant of XMRigCC, if you're looking for the CPU variant [click here](https://github.com/Bendr0id/xmrigCC/).**